From 24163073edde6d7cec9382d5ce994d0d0e3481d5 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv Date: Fri, 30 Mar 2018 16:39:06 +0300 Subject: [PATCH 0001/1171] try to fix calculation #10790 --- .../Tax/Model/Calculation/AbstractAggregateCalculator.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php index afcfa1bbebcb0..58b158768dba8 100644 --- a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php +++ b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php @@ -149,9 +149,6 @@ protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $ $rowTaxBeforeDiscount = array_sum($rowTaxesBeforeDiscount); $rowTotalInclTax = $rowTotal + $rowTaxBeforeDiscount; $priceInclTax = $rowTotalInclTax / $quantity; - if ($round) { - $priceInclTax = $this->calculationTool->round($priceInclTax); - } return $this->taxDetailsItemDataObjectFactory->create() ->setCode($item->getCode()) From 32c51d492176762acd841740d7f415e60bd4ad26 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv Date: Fri, 30 Mar 2018 17:48:35 +0300 Subject: [PATCH 0002/1171] Check negative difference after rounding #10790 --- .../Model/Calculation/AbstractAggregateCalculator.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php index 58b158768dba8..855a3eaeabe8e 100644 --- a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php +++ b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php @@ -150,6 +150,16 @@ protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $ $rowTotalInclTax = $rowTotal + $rowTaxBeforeDiscount; $priceInclTax = $rowTotalInclTax / $quantity; + if ($round) { + $priceInclTax = $this->calculationTool->round($priceInclTax); + } + + $pricePerItemInclTax = $rowTotalInclTax / $quantity; + + if (($pricePerItemInclTax - $priceInclTax) < 0) { + $priceInclTax = $pricePerItemInclTax; + } + return $this->taxDetailsItemDataObjectFactory->create() ->setCode($item->getCode()) ->setType($item->getType()) From de1840fb262e0fc57485c8d4db3be97d89faa68f Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv Date: Sun, 1 Apr 2018 18:45:15 +0300 Subject: [PATCH 0003/1171] Check negative totals after full discount #10790 --- app/code/Magento/SalesRule/Model/Utility.php | 19 +++++++++++++++++++ .../AbstractAggregateCalculator.php | 6 ------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/Utility.php b/app/code/Magento/SalesRule/Model/Utility.php index a3876a9d7e046..45c5dc3da0ebd 100644 --- a/app/code/Magento/SalesRule/Model/Utility.php +++ b/app/code/Magento/SalesRule/Model/Utility.php @@ -189,6 +189,8 @@ public function deltaRoundingFix( ) { $discountAmount = $discountData->getAmount(); $baseDiscountAmount = $discountData->getBaseAmount(); + $rowTotalInclTax = $item->getRowTotalInclTax(); + $baseRowTotalInclTax = $item->getBaseRowTotalInclTax(); //TODO Seems \Magento\Quote\Model\Quote\Item\AbstractItem::getDiscountPercent() returns float value //that can not be used as array index @@ -205,6 +207,23 @@ public function deltaRoundingFix( - $this->priceCurrency->round($baseDiscountAmount); } + /** + * When we have 100% discount check if totals will not be negative + */ + + if ($percentKey == 100) { + $discountDelta = $rowTotalInclTax - $discountAmount; + $baseDiscountDelta = $baseRowTotalInclTax - $baseDiscountAmount; + + if ($discountDelta < 0) { + $discountAmount += $discountDelta; + } + + if ($baseDiscountDelta < 0) { + $baseDiscountAmount += $baseDiscountDelta; + } + } + $discountData->setAmount($this->priceCurrency->round($discountAmount)); $discountData->setBaseAmount($this->priceCurrency->round($baseDiscountAmount)); diff --git a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php index 855a3eaeabe8e..77302bba82c67 100644 --- a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php +++ b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php @@ -154,12 +154,6 @@ protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $ $priceInclTax = $this->calculationTool->round($priceInclTax); } - $pricePerItemInclTax = $rowTotalInclTax / $quantity; - - if (($pricePerItemInclTax - $priceInclTax) < 0) { - $priceInclTax = $pricePerItemInclTax; - } - return $this->taxDetailsItemDataObjectFactory->create() ->setCode($item->getCode()) ->setType($item->getType()) From 5cfbf4655033799285ff0708464be5902f0c55a3 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sun, 6 May 2018 15:32:45 -0400 Subject: [PATCH 0004/1171] Deprecate Crypt Class --- lib/internal/Magento/Framework/Encryption/Crypt.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/Magento/Framework/Encryption/Crypt.php b/lib/internal/Magento/Framework/Encryption/Crypt.php index c304cdb23f85e..6e466559c8bdf 100644 --- a/lib/internal/Magento/Framework/Encryption/Crypt.php +++ b/lib/internal/Magento/Framework/Encryption/Crypt.php @@ -10,6 +10,7 @@ * Class encapsulates cryptographic algorithm * * @api + * @deprecated */ class Crypt { From c38f6bf3ccbefb0b5045d8943f498c422156a55d Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sun, 6 May 2018 22:20:00 -0400 Subject: [PATCH 0005/1171] Require paragonie/sodium_compat --- composer.json | 1 + composer.lock | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 653210f51ca68..cf295f48d3afc 100644 --- a/composer.json +++ b/composer.json @@ -40,6 +40,7 @@ "magento/zendframework1": "~1.14.0", "monolog/monolog": "^1.17", "oyejorge/less.php": "~1.7.0", + "paragonie/sodium_compat": "^1.6", "pelago/emogrifier": "^2.0.0", "php-amqplib/php-amqplib": "~2.7.0", "phpseclib/mcrypt_compat": "1.0.4", diff --git a/composer.lock b/composer.lock index d35e91932550b..8c7bd17ecb8d7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "6206f691f045589bbf11b8417f01ef69", + "content-hash": "8d413dc831588cec6d909dd2037069d1", "packages": [ { "name": "braintree/braintree_php", @@ -1068,6 +1068,88 @@ ], "time": "2018-04-04T21:24:14+00:00" }, + { + "name": "paragonie/sodium_compat", + "version": "v1.6.1", + "source": { + "type": "git", + "url": "https://github.com/paragonie/sodium_compat.git", + "reference": "9857e17bf9c1464485d8cc804eb13f2bcddc4cf0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/9857e17bf9c1464485d8cc804eb13f2bcddc4cf0", + "reference": "9857e17bf9c1464485d8cc804eb13f2bcddc4cf0", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "^1|^2", + "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7" + }, + "require-dev": { + "phpunit/phpunit": "^3|^4|^5" + }, + "suggest": { + "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", + "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." + }, + "type": "library", + "autoload": { + "files": [ + "autoload.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com" + }, + { + "name": "Frank Denis", + "email": "jedisct1@pureftpd.org" + } + ], + "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", + "keywords": [ + "Authentication", + "BLAKE2b", + "ChaCha20", + "ChaCha20-Poly1305", + "Chapoly", + "Curve25519", + "Ed25519", + "EdDSA", + "Edwards-curve Digital Signature Algorithm", + "Elliptic Curve Diffie-Hellman", + "Poly1305", + "Pure-PHP cryptography", + "RFC 7748", + "RFC 8032", + "Salpoly", + "Salsa20", + "X25519", + "XChaCha20-Poly1305", + "XSalsa20-Poly1305", + "Xchacha20", + "Xsalsa20", + "aead", + "cryptography", + "ecdh", + "elliptic curve", + "elliptic curve cryptography", + "encryption", + "libsodium", + "php", + "public-key cryptography", + "secret-key cryptography", + "side-channel resistant" + ], + "time": "2018-03-21T17:08:08+00:00" + }, { "name": "pelago/emogrifier", "version": "v2.0.0", From 73752241c842969041e37788a7b7dd0b87fc446f Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sun, 6 May 2018 15:57:58 -0400 Subject: [PATCH 0006/1171] Add Encryption Adapters * Create `Magento\Framework\Encryption\Adapter\EncryptionAdapterInterface` * Implement Mcrypt and Sodium Adapters The Mcrypt adapter is implemented for decryption backwards compatability and throws an exceptions if used for encryption. --- .../Adapter/EncryptionAdapterInterface.php | 18 ++ .../Framework/Encryption/Adapter/Mcrypt.php | 162 ++++++++++++++++++ .../Framework/Encryption/Adapter/Sodium.php | 64 +++++++ 3 files changed, 244 insertions(+) create mode 100644 lib/internal/Magento/Framework/Encryption/Adapter/EncryptionAdapterInterface.php create mode 100644 lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php create mode 100644 lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/EncryptionAdapterInterface.php b/lib/internal/Magento/Framework/Encryption/Adapter/EncryptionAdapterInterface.php new file mode 100644 index 0000000000000..df46eb45514de --- /dev/null +++ b/lib/internal/Magento/Framework/Encryption/Adapter/EncryptionAdapterInterface.php @@ -0,0 +1,18 @@ +cipher = $cipher; + $this->mode = $mode; + // @codingStandardsIgnoreStart + $this->handle = @mcrypt_module_open($cipher, '', $mode, ''); + // @codingStandardsIgnoreEnd + try { + // @codingStandardsIgnoreStart + $maxKeySize = @mcrypt_enc_get_key_size($this->handle); + // @codingStandardsIgnoreEnd + if (strlen($key) > $maxKeySize) { + throw new \Magento\Framework\Exception\LocalizedException( + new \Magento\Framework\Phrase('Key must not exceed %1 bytes.', [$maxKeySize]) + ); + } + // @codingStandardsIgnoreStart + $initVectorSize = @mcrypt_enc_get_iv_size($this->handle); + // @codingStandardsIgnoreEnd + if (true === $initVector) { + /* Generate a random vector from human-readable characters */ + $abc = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + $initVector = ''; + for ($i = 0; $i < $initVectorSize; $i++) { + $initVector .= $abc[rand(0, strlen($abc) - 1)]; + } + } elseif (false === $initVector) { + /* Set vector to zero bytes to not use it */ + $initVector = str_repeat("\0", $initVectorSize); + } elseif (!is_string($initVector) || strlen($initVector) != $initVectorSize) { + throw new \Magento\Framework\Exception\LocalizedException( + new \Magento\Framework\Phrase( + 'Init vector must be a string of %1 bytes.', + [$initVectorSize] + ) + ); + } + $this->_initVector = $initVector; + } catch (\Exception $e) { + // @codingStandardsIgnoreStart + @mcrypt_module_close($this->handle); + // @codingStandardsIgnoreEnd + throw $e; + } + // @codingStandardsIgnoreStart + @mcrypt_generic_init($this->handle, $key, $initVector); + // @codingStandardsIgnoreEnd + } + + /** + * Destructor frees allocated resources + */ + public function __destruct() + { + // @codingStandardsIgnoreStart + @mcrypt_generic_deinit($this->handle); + // @codingStandardsIgnoreEnd + // @codingStandardsIgnoreStart + @mcrypt_module_close($this->handle); + // @codingStandardsIgnoreEnd + } + + /** + * Retrieve a name of currently used cryptographic algorithm + * + * @return string + */ + public function getCipher() + { + return $this->cipher; + } + + /** + * Mode in which cryptographic algorithm is running + * + * @return string + */ + public function getMode() + { + return $this->mode; + } + + /** + * Retrieve an actual value of initial vector that has been used to initialize a cipher + * + * @return string + */ + public function getInitVector() + { + return $this->initVector; + } + + /** + * Encrypt a data + * + * @param string $data String to encrypt + * @return string + * @throws \Exception + */ + public function encrypt($data) + { + throw new \Exception((string)__('Mcrypt cannot be used for encryption. Use Sodium instead')); + } + + /** + * @param string $data + * @return string + */ + public function decrypt($data) + { + if (strlen($data) == 0) { + return $data; + } + // @codingStandardsIgnoreStart + $data = @mdecrypt_generic($this->handle, $data); + // @codingStandardsIgnoreEnd + /* + * Returned string can in fact be longer than the unencrypted string due to the padding of the data + * @link http://www.php.net/manual/en/function.mdecrypt-generic.php + */ + $data = rtrim($data, "\0"); + return $data; + } +} diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php new file mode 100644 index 0000000000000..62fc296def711 --- /dev/null +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php @@ -0,0 +1,64 @@ +key = $key; + $this->keyVersion = $keyVersion; + } + + /** + * @param $data + * @return string + */ + public function encrypt($data) + { + $cipherText = sodium_crypto_aead_chacha20poly1305_encrypt( + (string)$data, + '', + random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES), + $this->key + ); + + return $this->keyVersion . ':' . Encryptor::CIPHER_AEAD_CHACHA20POLY1305 . ':' . base64_encode($cipherText); + } + + /** + * @param string $data + * @return string + */ + public function decrypt($data) + { + $nonce = mb_substr($data, 0, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES, '8bit'); + $payload = mb_substr($data, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES, null, '8bit'); + + return sodium_crypto_aead_chacha20poly1305_decrypt( + $payload, + '', + $nonce, + $this->key + ); + } +} From 56b015ff459775bebcc5f1ed190d860109f324d0 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sun, 6 May 2018 16:02:19 -0400 Subject: [PATCH 0007/1171] Change Encyption to Sodium * Add new cipher constant and update cosntant value for latest cipher * All encrpytion is done using Sodium * Decrpytion is done using the original cipher --- .../Framework/Encryption/Encryptor.php | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/lib/internal/Magento/Framework/Encryption/Encryptor.php b/lib/internal/Magento/Framework/Encryption/Encryptor.php index 881c5843b155e..cec49594c7411 100644 --- a/lib/internal/Magento/Framework/Encryption/Encryptor.php +++ b/lib/internal/Magento/Framework/Encryption/Encryptor.php @@ -6,8 +6,11 @@ namespace Magento\Framework\Encryption; use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\Encryption\Adapter\EncryptionAdapterInterface; use Magento\Framework\Encryption\Helper\Security; use Magento\Framework\Math\Random; +use Magento\Framework\Encryption\Adapter\Sodium; +use Magento\Framework\Encryption\Adapter\Mcrypt; /** * Class Encryptor provides basic logic for hashing strings and encrypting/decrypting misc data @@ -56,7 +59,9 @@ class Encryptor implements EncryptorInterface const CIPHER_RIJNDAEL_256 = 2; - const CIPHER_LATEST = 2; + const CIPHER_AEAD_CHACHA20POLY1305 = 3; + + const CIPHER_LATEST = 3; /**#@-*/ /** @@ -108,6 +113,7 @@ class Encryptor implements EncryptorInterface private $random; /** + * Encryptor constructor. * @param Random $random * @param DeploymentConfig $deploymentConfig */ @@ -133,7 +139,12 @@ public function __construct( */ public function validateCipher($version) { - $types = [self::CIPHER_BLOWFISH, self::CIPHER_RIJNDAEL_128, self::CIPHER_RIJNDAEL_256]; + $types = [ + self::CIPHER_BLOWFISH, + self::CIPHER_RIJNDAEL_128, + self::CIPHER_RIJNDAEL_256, + self::CIPHER_AEAD_CHACHA20POLY1305, + ]; $version = (int)$version; if (!in_array($version, $types, true)) { @@ -260,14 +271,9 @@ private function getPasswordVersion() */ public function encrypt($data) { - $crypt = $this->getCrypt(); - if (null === $crypt) { - return $data; - } - return $this->keyVersion . ':' . $this->cipher . ':' . (MCRYPT_MODE_CBC === - $crypt->getMode() ? $crypt->getInitVector() . ':' : '') . base64_encode( - $crypt->encrypt((string)$data) - ); + $crypt = new Sodium($this->keys[$this->keyVersion], $this->keyVersion); + + return $crypt->encrypt($data); } /** @@ -279,6 +285,7 @@ public function encrypt($data) * * @param string $data * @return string + * @throws \Exception */ public function decrypt($data) { @@ -328,7 +335,7 @@ public function decrypt($data) * Return crypt model, instantiate if it is empty * * @param string|null $key NULL value means usage of the default key specified on constructor - * @return \Magento\Framework\Encryption\Crypt + * @return EncryptionAdapterInterface * @throws \Exception */ public function validateKey($key) @@ -344,6 +351,7 @@ public function validateKey($key) * * @param string $key * @return $this + * @throws \Exception */ public function setNewKey($key) { @@ -371,7 +379,8 @@ public function exportKeys() * @param string $key * @param int $cipherVersion * @param bool $initVector - * @return Crypt|null + * @return EncryptionAdapterInterface|null + * @throws \Exception */ protected function getCrypt($key = null, $cipherVersion = null, $initVector = true) { @@ -392,6 +401,10 @@ protected function getCrypt($key = null, $cipherVersion = null, $initVector = tr } $cipherVersion = $this->validateCipher($cipherVersion); + if ($cipherVersion >= self::CIPHER_AEAD_CHACHA20POLY1305) { + return new Sodium($key); + } + if ($cipherVersion === self::CIPHER_RIJNDAEL_128) { $cipher = MCRYPT_RIJNDAEL_128; $mode = MCRYPT_MODE_ECB; @@ -403,6 +416,6 @@ protected function getCrypt($key = null, $cipherVersion = null, $initVector = tr $mode = MCRYPT_MODE_ECB; } - return new Crypt($key, $cipher, $mode, $initVector); + return new Mcrypt($key, $cipher, $mode, $initVector); } } From 454d2d0ddc8aa68d1dc6f5ccb33df30c36fa7b6b Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sun, 6 May 2018 16:20:02 -0400 Subject: [PATCH 0008/1171] Set Random Key Size to Match SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES --- .../Magento/Framework/Config/ConfigOptionsListConstants.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php b/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php index 52e7137fa3ddf..cf39b108c7bd7 100644 --- a/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php +++ b/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php @@ -127,5 +127,5 @@ class ConfigOptionsListConstants /** * Size of random string generated for store's encryption key */ - const STORE_KEY_RANDOM_STRING_SIZE = 32; + const STORE_KEY_RANDOM_STRING_SIZE = SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES; } From 4a159cd77535071837d1c30d0a12e4b564f9df85 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sun, 6 May 2018 15:57:58 -0400 Subject: [PATCH 0009/1171] Add Encryption Adapters * Create `Magento\Framework\Encryption\Adapter\EncryptionAdapterInterface` * Implement Mcrypt and Sodium Adapters The Mcrypt adapter is implemented for decryption backwards compatability and throws an exceptions if used for encryption. --- .../Framework/Encryption/Adapter/Sodium.php | 19 +++++----- .../Encryption/Test/Unit/EncryptorTest.php | 37 +++++++++++++------ 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php index 62fc296def711..f22c9458ad5b3 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php @@ -35,14 +35,15 @@ public function __construct( */ public function encrypt($data) { - $cipherText = sodium_crypto_aead_chacha20poly1305_encrypt( + $nonce = random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES); + $cipherText = sodium_crypto_aead_chacha20poly1305_ietf_encrypt( (string)$data, - '', - random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES), + $nonce, + $nonce, $this->key ); - return $this->keyVersion . ':' . Encryptor::CIPHER_AEAD_CHACHA20POLY1305 . ':' . base64_encode($cipherText); + return $this->keyVersion . ':' . Encryptor::CIPHER_AEAD_CHACHA20POLY1305 . ':' . base64_encode($nonce . $cipherText); } /** @@ -51,12 +52,12 @@ public function encrypt($data) */ public function decrypt($data) { - $nonce = mb_substr($data, 0, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES, '8bit'); - $payload = mb_substr($data, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES, null, '8bit'); - - return sodium_crypto_aead_chacha20poly1305_decrypt( + $nonce = mb_substr($data, 0, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES, '8bit'); + $payload = mb_substr($data, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES, null, '8bit'); + + return sodium_crypto_aead_chacha20poly1305_ietf_decrypt( $payload, - '', + $nonce, $nonce, $this->key ); diff --git a/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php b/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php index 52a7a98eac312..8ceda0c8764a3 100644 --- a/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php +++ b/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php @@ -5,12 +5,17 @@ */ namespace Magento\Framework\Encryption\Test\Unit; +use Magento\Framework\Encryption\Adapter\Mcrypt; +use Magento\Framework\Encryption\Adapter\Sodium; use Magento\Framework\Encryption\Encryptor; use Magento\Framework\Encryption\Crypt; use Magento\Framework\App\DeploymentConfig; class EncryptorTest extends \PHPUnit\Framework\TestCase { + const CRYPT_KEY_1 = 'g9mY9KLrcuAVJfsmVUSRkKFLDdUPVkaZ'; + const CRYPT_KEY_2 = '7wEjmrliuqZQ1NQsndSa8C8WHvddeEbN'; + /** * @var \Magento\Framework\Encryption\Encryptor */ @@ -28,7 +33,7 @@ protected function setUp() $deploymentConfigMock->expects($this->any()) ->method('get') ->with(Encryptor::PARAM_CRYPT_KEY) - ->will($this->returnValue('cryptKey')); + ->will($this->returnValue(self::CRYPT_KEY_1)); $this->_model = new \Magento\Framework\Encryption\Encryptor($this->_randomGenerator, $deploymentConfigMock); } @@ -99,6 +104,7 @@ public function validateHashDataProvider() * @param mixed $key * * @dataProvider encryptWithEmptyKeyDataProvider + * @expectedException \SodiumException */ public function testEncryptWithEmptyKey($key) { @@ -147,20 +153,27 @@ public function testEncrypt() $actual = $this->_model->encrypt($data); // Extract the initialization vector and encrypted data - $parts = explode(':', $actual, 4); - list(, , $iv, $encryptedData) = $parts; + $parts = explode(':', $actual, 3); + list(, , $encryptedData) = $parts; - // Decrypt returned data with RIJNDAEL_256 cipher, cbc mode - $crypt = new Crypt('cryptKey', MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC, $iv); + $crypt = new Sodium(self::CRYPT_KEY_1); // Verify decrypted matches original data $this->assertEquals($data, $crypt->decrypt(base64_decode((string)$encryptedData))); } public function testDecrypt() + { + $message = 'Mares eat oats and does eat oats, but little lambs eat ivy.'; + $encrypted = $this->_model->encrypt($message); + + $this->assertEquals($message, $this->_model->decrypt($encrypted)); + } + + public function testLegacyDecrypt() { // sample data to encrypt $data = '0:2:z3a4ACpkU35W6pV692U4ueCVQP0m0v0p:' . - '7ZPIIRZzQrgQH+csfF3fyxYNwbzPTwegncnoTxvI3OZyqKGYlOCTSx5i1KRqNemCC8kuCiOAttLpAymXhzjhNQ=='; + 'DhEG8/uKGGq92ZusqrGb6X/9+2Ng0QZ9z2UZwljgJbs5/A3LaSnqcK0oI32yjHY49QJi+Z7q1EKu2yVqB8EMpA=='; $actual = $this->_model->decrypt($data); @@ -169,7 +182,7 @@ public function testDecrypt() list(, , $iv, $encrypted) = $parts; // Decrypt returned data with RIJNDAEL_256 cipher, cbc mode - $crypt = new Crypt('cryptKey', MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC, $iv); + $crypt = new Crypt(self::CRYPT_KEY_1, MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC, $iv); // Verify decrypted matches original data $this->assertEquals($encrypted, base64_encode($crypt->encrypt($actual))); } @@ -180,11 +193,11 @@ public function testEncryptDecryptNewKeyAdded() $deploymentConfigMock->expects($this->at(0)) ->method('get') ->with(Encryptor::PARAM_CRYPT_KEY) - ->will($this->returnValue("cryptKey1")); + ->will($this->returnValue(self::CRYPT_KEY_1)); $deploymentConfigMock->expects($this->at(1)) ->method('get') ->with(Encryptor::PARAM_CRYPT_KEY) - ->will($this->returnValue("cryptKey1\ncryptKey2")); + ->will($this->returnValue(self::CRYPT_KEY_1 . "\n" . self::CRYPT_KEY_2)); $model1 = new Encryptor($this->_randomGenerator, $deploymentConfigMock); // simulate an encryption key is being added $model2 = new Encryptor($this->_randomGenerator, $deploymentConfigMock); @@ -200,11 +213,11 @@ public function testEncryptDecryptNewKeyAdded() public function testValidateKey() { - $actual = $this->_model->validateKey('some_key'); - $crypt = new Crypt('some_key', MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC, $actual->getInitVector()); + $actual = $this->_model->validateKey(self::CRYPT_KEY_1); + $crypt = new Sodium(self::CRYPT_KEY_1); $expectedEncryptedData = base64_encode($crypt->encrypt('data')); $actualEncryptedData = base64_encode($actual->encrypt('data')); - $this->assertEquals($expectedEncryptedData, $actualEncryptedData); + $this->assertNotEquals($expectedEncryptedData, $actualEncryptedData); $this->assertEquals($crypt->decrypt($expectedEncryptedData), $actual->decrypt($actualEncryptedData)); } From 526f41912e3b0ab45efa0cb7bdf58ff8b43f94a0 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sun, 6 May 2018 22:30:13 -0400 Subject: [PATCH 0010/1171] Clear sensitive information from memory --- .../Magento/Framework/Encryption/Adapter/Sodium.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php index f22c9458ad5b3..63222f6a5694c 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php @@ -43,6 +43,8 @@ public function encrypt($data) $this->key ); + sodium_memzero($data); + return $this->keyVersion . ':' . Encryptor::CIPHER_AEAD_CHACHA20POLY1305 . ':' . base64_encode($nonce . $cipherText); } @@ -55,11 +57,16 @@ public function decrypt($data) $nonce = mb_substr($data, 0, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES, '8bit'); $payload = mb_substr($data, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES, null, '8bit'); - return sodium_crypto_aead_chacha20poly1305_ietf_decrypt( + $plainText = sodium_crypto_aead_chacha20poly1305_ietf_decrypt( $payload, $nonce, $nonce, $this->key ); + + sodium_memzero($data); + sodium_memzero($nonce); + + return $plainText; } } From 207887ec7610c431d9222c75951bde7bd0eb2214 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sun, 6 May 2018 23:22:31 -0400 Subject: [PATCH 0011/1171] Revert clearing sensitive data sodium_compat throws an exception when sodium_memzero is called because the PHP implemenation cannot reliably zero buffers. --- lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php index 63222f6a5694c..2abbd3b9a7930 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php @@ -43,8 +43,6 @@ public function encrypt($data) $this->key ); - sodium_memzero($data); - return $this->keyVersion . ':' . Encryptor::CIPHER_AEAD_CHACHA20POLY1305 . ':' . base64_encode($nonce . $cipherText); } @@ -64,9 +62,6 @@ public function decrypt($data) $this->key ); - sodium_memzero($data); - sodium_memzero($nonce); - return $plainText; } } From 0b5a633fdb4ef4ab070a9f4ff57b9ff6c3f3564b Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Mon, 7 May 2018 22:13:21 -0400 Subject: [PATCH 0012/1171] Fix Failing Integration Tests * update regex validation for 3 parts and make closing = optional * correct expected return type when calling getCrypt --- .../EncryptionKey/Model/ResourceModel/Key/ChangeTest.php | 4 ++-- .../testsuite/Magento/Framework/Encryption/ModelTest.php | 5 ++++- .../Magento/Framework/Encryption/Adapter/Mcrypt.php | 6 ++++-- .../Magento/Framework/Encryption/Adapter/Sodium.php | 4 +++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/EncryptionKey/Model/ResourceModel/Key/ChangeTest.php b/dev/tests/integration/testsuite/Magento/EncryptionKey/Model/ResourceModel/Key/ChangeTest.php index c199214f68578..5cf47f72b0274 100644 --- a/dev/tests/integration/testsuite/Magento/EncryptionKey/Model/ResourceModel/Key/ChangeTest.php +++ b/dev/tests/integration/testsuite/Magento/EncryptionKey/Model/ResourceModel/Key/ChangeTest.php @@ -79,7 +79,7 @@ public function testChangeEncryptionKey() ) ); $this->assertNotContains($testValue, $values1); - $this->assertRegExp('|([0-9]+:)([0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9+/]+=)|', current($values1)); + $this->assertRegExp('|([0-9]+:)([0-9]+:)([a-zA-Z0-9+/]+=*)|', current($values1)); // Verify that the credit card number has been encrypted $values2 = $connection->fetchPairs( @@ -89,7 +89,7 @@ public function testChangeEncryptionKey() ) ); $this->assertNotContains('1111111111', $values2); - $this->assertRegExp('|([0-9]+:)([0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9+/]+=)|', current($values1)); + $this->assertRegExp('|([0-9]+:)([0-9]+:)([a-zA-Z0-9+/]+=*)|', current($values2)); /** clean up */ $select = $connection->select()->from($configModel->getMainTable())->where('path=?', $testPath); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Encryption/ModelTest.php b/dev/tests/integration/testsuite/Magento/Framework/Encryption/ModelTest.php index f5b48e79b9860..411656e214677 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Encryption/ModelTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Encryption/ModelTest.php @@ -40,7 +40,10 @@ public function testEncryptDecrypt2() public function testValidateKey() { $validKey = md5(uniqid()); - $this->assertInstanceOf(\Magento\Framework\Encryption\Crypt::class, $this->_model->validateKey($validKey)); + $this->assertInstanceOf( + \Magento\Framework\Encryption\Adapter\EncryptionAdapterInterface::class, + $this->_model->validateKey($validKey) + ); } public function testGetValidateHash() diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php index 1046becca0670..730a3230f0d09 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php @@ -26,7 +26,7 @@ class Mcrypt implements EncryptionAdapterInterface /** * Mcrypt constructor. - * @param $key + * @param string $key * @param string $cipher * @param string $mode * @param bool $initVector @@ -137,7 +137,9 @@ public function getInitVector() */ public function encrypt($data) { - throw new \Exception((string)__('Mcrypt cannot be used for encryption. Use Sodium instead')); + throw new \Exception( + (string)new \Magento\Framework\Phrase('Mcrypt cannot be used for encryption. Use Sodium instead') + ); } /** diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php index 2abbd3b9a7930..58a8cb4fd736f 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php @@ -43,7 +43,9 @@ public function encrypt($data) $this->key ); - return $this->keyVersion . ':' . Encryptor::CIPHER_AEAD_CHACHA20POLY1305 . ':' . base64_encode($nonce . $cipherText); + return $this->keyVersion . + ':' . Encryptor::CIPHER_AEAD_CHACHA20POLY1305 . + ':' . base64_encode($nonce . $cipherText); } /** From 81c5358676bc296664674ec73f2408b5be96f5e2 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Wed, 9 May 2018 07:27:34 -0400 Subject: [PATCH 0013/1171] Migrate encrypted values to sodium --- .../Setup/Patch/Data/SodiumChachaPatch.php | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php diff --git a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php new file mode 100644 index 0000000000000..dcc641bec962c --- /dev/null +++ b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php @@ -0,0 +1,129 @@ +moduleDataSetup = $moduleDataSetup; + $this->structure = $structure; + $this->encryptor = $encryptor; + $this->scope = $scope; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + $this->moduleDataSetup->startSetup(); + + $this->reEncryptSystemConfigurationValues(); + $this->reEncryptCreditCardNumbers(); + + $this->moduleDataSetup->endSetup(); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } + + private function reEncryptSystemConfigurationValues() + { + $currentScope = $this->scope->getCurrentScope(); + + $this->scope->setCurrentScope(\Magento\Framework\App\Area::AREA_ADMINHTML); + + $paths = $this->structure->getFieldPathsByAttribute( + 'backend_model', + \Magento\Config\Model\Config\Backend\Encrypted::class + ); + + $this->scope->setCurrentScope($currentScope); + + // walk through found data and re-encrypt it + if ($paths) { + $table = $this->moduleDataSetup->getTable('core_config_data'); + $values = $this->moduleDataSetup->getConnection()->fetchPairs( + $this->moduleDataSetup->getConnection() + ->select() + ->from($table, ['config_id', 'value']) + ->where('path IN (?)', $paths) + ->where('value NOT LIKE ?', '') + ); + foreach ($values as $configId => $value) { + $this->moduleDataSetup->getConnection()->update( + $table, + ['value' => $this->encryptor->encrypt($this->encryptor->decrypt($value))], + ['config_id = ?' => (int)$configId] + ); + } + } + } + + private function reEncryptCreditCardNumbers() + { + $table = $this->moduleDataSetup->getTable('sales_order_payment'); + $select = $this->moduleDataSetup->getConnection()->select()->from($table, ['entity_id', 'cc_number_enc']); + + $attributeValues = $this->moduleDataSetup->getConnection()->fetchPairs($select); + // save new values + foreach ($attributeValues as $valueId => $value) { + $this->moduleDataSetup->getConnection()->update( + $table, + ['cc_number_enc' => $this->encryptor->encrypt($this->encryptor->decrypt($value))], + ['entity_id = ?' => (int)$valueId] + ); + } + } +} From cb6097842edd2a0d891f9b580dc972efe550d7be Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sun, 13 May 2018 14:16:09 -0400 Subject: [PATCH 0014/1171] Remove CC Number Re-encryption Will be implemented as a CLI tool instead --- .../Setup/Patch/Data/SodiumChachaPatch.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php index dcc641bec962c..7e38b7878d8a7 100644 --- a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php +++ b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php @@ -57,7 +57,6 @@ public function apply() $this->moduleDataSetup->startSetup(); $this->reEncryptSystemConfigurationValues(); - $this->reEncryptCreditCardNumbers(); $this->moduleDataSetup->endSetup(); } @@ -110,20 +109,4 @@ private function reEncryptSystemConfigurationValues() } } } - - private function reEncryptCreditCardNumbers() - { - $table = $this->moduleDataSetup->getTable('sales_order_payment'); - $select = $this->moduleDataSetup->getConnection()->select()->from($table, ['entity_id', 'cc_number_enc']); - - $attributeValues = $this->moduleDataSetup->getConnection()->fetchPairs($select); - // save new values - foreach ($attributeValues as $valueId => $value) { - $this->moduleDataSetup->getConnection()->update( - $table, - ['cc_number_enc' => $this->encryptor->encrypt($this->encryptor->decrypt($value))], - ['entity_id = ?' => (int)$valueId] - ); - } - } } From a2ded12e53abdacea3c740f2fdaaf24c21789104 Mon Sep 17 00:00:00 2001 From: Roman Ganin Date: Tue, 15 May 2018 11:41:02 -0500 Subject: [PATCH 0015/1171] MAGETWO-90971: Error is returned when customer checks out with multiple addresses --- .../Magento/Tax/Model/Plugin/OrderSave.php | 6 +- .../Test/Unit/Model/Plugin/OrderSaveTest.php | 162 ++++++++---------- 2 files changed, 71 insertions(+), 97 deletions(-) diff --git a/app/code/Magento/Tax/Model/Plugin/OrderSave.php b/app/code/Magento/Tax/Model/Plugin/OrderSave.php index a1a3ebf861d60..1e2cd2ac0b45f 100644 --- a/app/code/Magento/Tax/Model/Plugin/OrderSave.php +++ b/app/code/Magento/Tax/Model/Plugin/OrderSave.php @@ -79,8 +79,7 @@ protected function saveOrderTax(\Magento\Sales\Api\Data\OrderInterface $order) foreach ($taxesForItems as $taxesArray) { foreach ($taxesArray['applied_taxes'] as $rates) { if (isset($rates['extension_attributes'])) { - /** @var \Magento\Tax\Api\Data\AppliedTaxRateInterface[] $taxRates */ - $taxRates = $rates['extension_attributes']->getRates(); + $taxRates = $rates['extension_attributes']['rates']; if (is_array($taxRates)) { if (count($taxRates) == 1) { $ratesIdQuoteItemId[$rates['id']][] = [ @@ -124,8 +123,7 @@ protected function saveOrderTax(\Magento\Sales\Api\Data\OrderInterface $order) foreach ($taxes as $row) { $id = $row['id']; if (isset($row['extension_attributes'])) { - /** @var \Magento\Tax\Api\Data\AppliedTaxRateInterface[] $taxRates */ - $taxRates = $row['extension_attributes']->getRates(); + $taxRates = $row['extension_attributes']['rates']; if (is_array($taxRates)) { foreach ($taxRates as $tax) { if ($row['percent'] == null) { diff --git a/app/code/Magento/Tax/Test/Unit/Model/Plugin/OrderSaveTest.php b/app/code/Magento/Tax/Test/Unit/Model/Plugin/OrderSaveTest.php index 912f42af0d3cd..fef2441f81f19 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Plugin/OrderSaveTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Plugin/OrderSaveTest.php @@ -210,93 +210,6 @@ public function testAfterSave( */ public function afterSaveDataProvider() { - $orderTaxDetailsApplied = $this->getMockBuilder(\Magento\Tax\Api\Data\OrderTaxDetailsAppliedTaxInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getRates']) - ->getMockForAbstractClass(); - - $orderTaxDetailsApplied->expects($this->at(0)) - ->method('getRates') - ->willReturn( - [ - [ - 'percent' => 6, - 'code' => 'IL', - 'title' => 'IL', - ], - [ - 'percent' => 5, - 'code' => 'US', - 'title' => 'US', - ] - ] - ); - $orderTaxDetailsApplied->expects($this->at(1)) - ->method('getRates') - ->willReturn( - [ - [ - 'percent' => 3, - 'code' => 'CityTax', - 'title' => 'CityTax', - ], - ] - ); - $orderTaxDetailsApplied->expects($this->at(2)) - ->method('getRates') - ->willReturn( - [ - [ - 'percent' => 6, - 'code' => 'IL', - 'title' => 'IL', - ], - [ - 'percent' => 5, - 'code' => 'US', - 'title' => 'US', - ], - ] - ); - $orderTaxDetailsApplied->expects($this->at(3)) - ->method('getRates') - ->willReturn( - [ - [ - 'percent' => 3, - 'code' => 'CityTax', - 'title' => 'CityTax', - ], - ] - ); - $orderTaxDetailsApplied->expects($this->at(4)) - ->method('getRates') - ->willReturn( - [ - [ - 'percent' => 6, - 'code' => 'IL', - 'title' => 'IL', - ], - [ - 'percent' => 5, - 'code' => 'US', - 'title' => 'US', - ], - ] - ); - $orderTaxDetailsApplied->expects($this->at(5)) - ->method('getRates') - ->willReturn( - [ - [ - 'percent' => 3, - 'code' => 'CityTax', - 'title' => 'CityTax', - ], - ] - ); - return [ //one item with shipping //three tax rates: state and national tax rates of 6 and 5 percent with priority 0 @@ -308,14 +221,35 @@ public function afterSaveDataProvider() 'base_amount' => 0.66, 'percent' => 11, 'id' => 'ILUS', - 'extension_attributes' => $orderTaxDetailsApplied, + 'extension_attributes' => [ + 'rates' => [ + [ + 'percent' => 6, + 'code' => 'IL', + 'title' => 'IL', + ], + [ + 'percent' => 5, + 'code' => 'US', + 'title' => 'US', + ], + ] + ], ], [ 'amount' => 0.2, 'base_amount' => 0.2, 'percent' => 3.33, 'id' => 'CityTax', - 'extension_attributes' => $orderTaxDetailsApplied, + 'extension_attributes' => [ + 'rates' => [ + [ + 'percent' => 3, + 'code' => 'CityTax', + 'title' => 'CityTax', + ], + ] + ], ], ], 'item_applied_taxes' => [ @@ -331,7 +265,20 @@ public function afterSaveDataProvider() 'base_amount' => 0.11, 'percent' => 11, 'id' => 'ILUS', - 'extension_attributes' => $orderTaxDetailsApplied, + 'extension_attributes' => [ + 'rates' => [ + [ + 'percent' => 6, + 'code' => 'IL', + 'title' => 'IL', + ], + [ + 'percent' => 5, + 'code' => 'US', + 'title' => 'US', + ], + ] + ], ], //city tax [ @@ -339,7 +286,15 @@ public function afterSaveDataProvider() 'base_amount' => 0.03, 'percent' => 3.33, 'id' => 'CityTax', - 'extension_attributes' => $orderTaxDetailsApplied, + 'extension_attributes' => [ + 'rates' => [ + [ + 'percent' => 3, + 'code' => 'CityTax', + 'title' => 'CityTax', + ], + ] + ], ], ], ], @@ -355,7 +310,20 @@ public function afterSaveDataProvider() 'base_amount' => 0.55, 'percent' => 11, 'id' => 'ILUS', - 'extension_attributes' => $orderTaxDetailsApplied, + 'extension_attributes' => [ + 'rates' => [ + [ + 'percent' => 6, + 'code' => 'IL', + 'title' => 'IL', + ], + [ + 'percent' => 5, + 'code' => 'US', + 'title' => 'US', + ], + ] + ], ], //city tax [ @@ -363,7 +331,15 @@ public function afterSaveDataProvider() 'base_amount' => 0.17, 'percent' => 3.33, 'id' => 'CityTax', - 'extension_attributes' => $orderTaxDetailsApplied, + 'extension_attributes' => [ + 'rates' => [ + [ + 'percent' => 3, + 'code' => 'CityTax', + 'title' => 'CityTax', + ], + ] + ], ], ], ], From 4e07a77ec0f50fef9f1d2e9d9a4c6570b790aab3 Mon Sep 17 00:00:00 2001 From: Navarr Barnier Date: Fri, 18 May 2018 14:37:37 -0400 Subject: [PATCH 0016/1171] API annotated IoInterface --- lib/internal/Magento/Framework/Filesystem/Io/IoInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/Magento/Framework/Filesystem/Io/IoInterface.php b/lib/internal/Magento/Framework/Filesystem/Io/IoInterface.php index c31d3ff9e52ba..d7d0f045c5c72 100644 --- a/lib/internal/Magento/Framework/Filesystem/Io/IoInterface.php +++ b/lib/internal/Magento/Framework/Filesystem/Io/IoInterface.php @@ -9,6 +9,7 @@ /** * Input/output client interface + * @api */ interface IoInterface { From c977be2b8cabb0026715fd96fae447c315391881 Mon Sep 17 00:00:00 2001 From: Roman Ganin Date: Mon, 21 May 2018 12:30:47 -0500 Subject: [PATCH 0017/1171] MAGETWO-90971: Error is returned when customer checks out with multiple addresses --- app/code/Magento/Tax/Model/Plugin/OrderSave.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Tax/Model/Plugin/OrderSave.php b/app/code/Magento/Tax/Model/Plugin/OrderSave.php index 1e2cd2ac0b45f..dd28229c5a38e 100644 --- a/app/code/Magento/Tax/Model/Plugin/OrderSave.php +++ b/app/code/Magento/Tax/Model/Plugin/OrderSave.php @@ -78,7 +78,7 @@ protected function saveOrderTax(\Magento\Sales\Api\Data\OrderInterface $order) $ratesIdQuoteItemId = []; foreach ($taxesForItems as $taxesArray) { foreach ($taxesArray['applied_taxes'] as $rates) { - if (isset($rates['extension_attributes'])) { + if (isset($rates['extension_attributes']['rates'])) { $taxRates = $rates['extension_attributes']['rates']; if (is_array($taxRates)) { if (count($taxRates) == 1) { From ab0508ba986f83a2853ac84cf077a91a91e8f82e Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sun, 13 May 2018 14:40:34 -0400 Subject: [PATCH 0018/1171] Add strict types --- .../EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php | 2 ++ .../EncryptionKey/Model/ResourceModel/Key/ChangeTest.php | 2 ++ .../testsuite/Magento/Framework/Encryption/ModelTest.php | 2 ++ .../Framework/Config/ConfigOptionsListConstants.php | 2 ++ .../Encryption/Adapter/EncryptionAdapterInterface.php | 2 ++ .../Magento/Framework/Encryption/Adapter/Mcrypt.php | 2 ++ .../Magento/Framework/Encryption/Adapter/Sodium.php | 2 ++ lib/internal/Magento/Framework/Encryption/Crypt.php | 4 +++- lib/internal/Magento/Framework/Encryption/Encryptor.php | 7 +++++-- .../Framework/Encryption/Test/Unit/EncryptorTest.php | 3 +++ 10 files changed, 25 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php index 7e38b7878d8a7..2fc23ed5d73df 100644 --- a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php +++ b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php @@ -1,5 +1,7 @@ _handle); // @codingStandardsIgnoreEnd - if (strlen($key) > $maxKeySize) { + if (strlen((string)$key) > $maxKeySize) { throw new \Magento\Framework\Exception\LocalizedException( new \Magento\Framework\Phrase('Key must not exceed %1 bytes.', [$maxKeySize]) ); diff --git a/lib/internal/Magento/Framework/Encryption/Encryptor.php b/lib/internal/Magento/Framework/Encryption/Encryptor.php index cec49594c7411..d6eb4f2dd9aec 100644 --- a/lib/internal/Magento/Framework/Encryption/Encryptor.php +++ b/lib/internal/Magento/Framework/Encryption/Encryptor.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Framework\Encryption; use Magento\Framework\App\DeploymentConfig; @@ -124,7 +127,7 @@ public function __construct( $this->random = $random; // load all possible keys - $this->keys = preg_split('/\s+/s', trim($deploymentConfig->get(self::PARAM_CRYPT_KEY))); + $this->keys = preg_split('/\s+/s', trim((string)$deploymentConfig->get(self::PARAM_CRYPT_KEY))); $this->keyVersion = count($this->keys) - 1; } @@ -183,7 +186,7 @@ public function getHash($password, $salt = false, $version = self::HASH_VERSION_ */ public function hash($data, $version = self::HASH_VERSION_LATEST) { - return hash($this->hashVersionMap[$version], $data); + return hash($this->hashVersionMap[$version], (string)$data); } /** diff --git a/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php b/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php index 8ceda0c8764a3..1de0fd684ca77 100644 --- a/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php +++ b/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Framework\Encryption\Test\Unit; use Magento\Framework\Encryption\Adapter\Mcrypt; From f4373a289260cd67823a72e9eda2b5e02c25f513 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Tue, 15 May 2018 18:43:35 -0400 Subject: [PATCH 0019/1171] Replace autogenerated doc blocks With actual class and function definitions --- .../Setup/Patch/Data/SodiumChachaPatch.php | 4 +--- .../Magento/Framework/Encryption/Adapter/Mcrypt.php | 7 ++++++- .../Magento/Framework/Encryption/Adapter/Sodium.php | 11 +++++++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php index 2fc23ed5d73df..69bc47d2c6732 100644 --- a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php +++ b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php @@ -7,8 +7,7 @@ use Magento\Framework\Setup\Patch\DataPatchInterface; /** - * Class SodiumChachaPatch - * @package Magento\EncryptionKey\Setup\Patch + * Migrate encrypted configuration values to the latest cipher */ class SodiumChachaPatch implements DataPatchInterface { @@ -33,7 +32,6 @@ class SodiumChachaPatch implements DataPatchInterface private $scope; /** - * SodiumChachaPatch constructor. * @param \Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup * @param \Magento\Config\Model\Config\Structure\Proxy $structure * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php index 2d7fa87228d08..f1764020224c9 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php @@ -4,6 +4,9 @@ namespace Magento\Framework\Encryption\Adapter; +/** + * Mcrypt adapter for decrypting values using legacy ciphers + */ class Mcrypt implements EncryptionAdapterInterface { /** @@ -131,7 +134,7 @@ public function getInitVector() } /** - * Encrypt a data + * Encrypt a string * * @param string $data String to encrypt * @return string @@ -145,6 +148,8 @@ public function encrypt($data) } /** + * Decrypt a string + * * @param string $data * @return string */ diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php index c101cd5859f9d..3aeb090c0b52b 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php @@ -6,6 +6,9 @@ use Magento\Framework\Encryption\Encryptor; +/** + * Sodium adapter for encrypting and decrypting strings + */ class Sodium implements EncryptionAdapterInterface { /** @@ -32,8 +35,10 @@ public function __construct( } /** - * @param $data - * @return string + * Encrypt a string + * + * @param string $data + * @return string string */ public function encrypt($data) { @@ -51,6 +56,8 @@ public function encrypt($data) } /** + * Decrypt a string + * * @param string $data * @return string */ From 32bd3ced50224e3e54d082a3460156ff7b349e38 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Tue, 15 May 2018 18:44:41 -0400 Subject: [PATCH 0020/1171] Add param and return types for new methods --- .../Adapter/EncryptionAdapterInterface.php | 4 ++-- .../Framework/Encryption/Adapter/Mcrypt.php | 20 +++++++++---------- .../Framework/Encryption/Adapter/Sodium.php | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/EncryptionAdapterInterface.php b/lib/internal/Magento/Framework/Encryption/Adapter/EncryptionAdapterInterface.php index 9b42558f446e4..3dd661f197781 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/EncryptionAdapterInterface.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/EncryptionAdapterInterface.php @@ -10,11 +10,11 @@ interface EncryptionAdapterInterface * @param $data * @return string */ - public function encrypt($data); + public function encrypt(string $data): string; /** * @param string $data * @return string */ - public function decrypt($data); + public function decrypt(string $data): string; } diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php index f1764020224c9..935b1280fc65d 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php @@ -34,14 +34,14 @@ class Mcrypt implements EncryptionAdapterInterface * @param string $key * @param string $cipher * @param string $mode - * @param bool $initVector + * @param string $initVector * @throws \Exception */ public function __construct( - $key, - $cipher = MCRYPT_BLOWFISH, - $mode = MCRYPT_MODE_ECB, - $initVector = false + string $key, + string $cipher = MCRYPT_BLOWFISH, + string $mode = MCRYPT_MODE_ECB, + string $initVector = null ) { $this->cipher = $cipher; $this->mode = $mode; @@ -108,7 +108,7 @@ public function __destruct() * * @return string */ - public function getCipher() + public function getCipher(): string { return $this->cipher; } @@ -118,7 +118,7 @@ public function getCipher() * * @return string */ - public function getMode() + public function getMode(): string { return $this->mode; } @@ -128,7 +128,7 @@ public function getMode() * * @return string */ - public function getInitVector() + public function getInitVector(): string { return $this->initVector; } @@ -140,7 +140,7 @@ public function getInitVector() * @return string * @throws \Exception */ - public function encrypt($data) + public function encrypt(string $data): string { throw new \Exception( (string)new \Magento\Framework\Phrase('Mcrypt cannot be used for encryption. Use Sodium instead') @@ -153,7 +153,7 @@ public function encrypt($data) * @param string $data * @return string */ - public function decrypt($data) + public function decrypt(string $data): string { if (strlen($data) == 0) { return $data; diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php index 3aeb090c0b52b..4dca26753aa06 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php @@ -40,7 +40,7 @@ public function __construct( * @param string $data * @return string string */ - public function encrypt($data) + public function encrypt(string $data): string { $nonce = random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES); $cipherText = sodium_crypto_aead_chacha20poly1305_ietf_encrypt( @@ -61,7 +61,7 @@ public function encrypt($data) * @param string $data * @return string */ - public function decrypt($data) + public function decrypt(string $data): string { $nonce = mb_substr($data, 0, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES, '8bit'); $payload = mb_substr($data, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES, null, '8bit'); From 347d3fdcab2106d2649f6294aa3cd19d5bfda0d3 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Tue, 15 May 2018 18:49:07 -0400 Subject: [PATCH 0021/1171] Make encrypt and decrypt symetric --- .../Framework/Encryption/Adapter/Sodium.php | 14 ++------------ .../Magento/Framework/Encryption/Encryptor.php | 6 ++++-- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php index 4dca26753aa06..ea242caeafa15 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php @@ -16,22 +16,14 @@ class Sodium implements EncryptionAdapterInterface */ private $key; - /** - * @var int - */ - private $keyVersion; - /** * Sodium constructor. * @param string $key - * @param int|null $keyVersion */ public function __construct( - string $key, - int $keyVersion = null + string $key ) { $this->key = $key; - $this->keyVersion = $keyVersion; } /** @@ -50,9 +42,7 @@ public function encrypt(string $data): string $this->key ); - return $this->keyVersion . - ':' . Encryptor::CIPHER_AEAD_CHACHA20POLY1305 . - ':' . base64_encode($nonce . $cipherText); + return $nonce . $cipherText; } /** diff --git a/lib/internal/Magento/Framework/Encryption/Encryptor.php b/lib/internal/Magento/Framework/Encryption/Encryptor.php index d6eb4f2dd9aec..149e167d65128 100644 --- a/lib/internal/Magento/Framework/Encryption/Encryptor.php +++ b/lib/internal/Magento/Framework/Encryption/Encryptor.php @@ -274,9 +274,11 @@ private function getPasswordVersion() */ public function encrypt($data) { - $crypt = new Sodium($this->keys[$this->keyVersion], $this->keyVersion); + $crypt = new Sodium($this->keys[$this->keyVersion]); - return $crypt->encrypt($data); + return $this->keyVersion . + ':' . self::CIPHER_AEAD_CHACHA20POLY1305 . + ':' . base64_encode($crypt->encrypt($data)); } /** From 2d9734dccb9d66627e7f59d4099c1e3155055d0b Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets Date: Sat, 19 May 2018 01:29:17 +0300 Subject: [PATCH 0022/1171] ENGCOM-1513: Fix outdated address data when using Braintree's "Pay with PayPal" button #15133 --- .../js/view/payment/method-renderer/paypal.js | 25 ++++++++++++++++--- .../payment/method-renderer/paypal.test.js | 17 +++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js index ca9d3686958b4..7c75cc3e594ee 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js @@ -7,6 +7,7 @@ define([ 'jquery', 'underscore', + 'mage/utils/wrapper', 'Magento_Checkout/js/view/payment/default', 'Magento_Braintree/js/view/payment/adapter', 'Magento_Checkout/js/model/quote', @@ -18,6 +19,7 @@ define([ ], function ( $, _, + wrapper, Component, Braintree, quote, @@ -218,8 +220,9 @@ define([ /** * Re-init PayPal Auth Flow + * @param {Function} callback - Optional callback */ - reInitPayPal: function () { + reInitPayPal: function (callback) { if (Braintree.checkout) { Braintree.checkout.teardown(function () { Braintree.checkout = null; @@ -228,6 +231,18 @@ define([ this.disableButton(); this.clientConfig.paypal.amount = this.grandTotalAmount; + this.clientConfig.paypal.shippingAddressOverride = this.getShippingAddress(); + + if (callback) { + this.clientConfig.onReady = wrapper.wrap( + this.clientConfig.onReady, + function (original, checkout) { + this.clientConfig.onReady = original; + original(checkout); + callback(); + }.bind(this) + ); + } Braintree.setConfig(this.clientConfig); Braintree.setup(); @@ -403,7 +418,11 @@ define([ * Triggers when customer click "Continue to PayPal" button */ payWithPayPal: function () { - if (additionalValidators.validate()) { + this.reInitPayPal(function () { + if (!additionalValidators.validate()) { + return; + } + try { Braintree.checkout.paypal.initAuthFlow(); } catch (e) { @@ -411,7 +430,7 @@ define([ message: $t('Payment ' + this.getTitle() + ' can\'t be initialized.') }); } - } + }.bind(this)); }, /** diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js index 596017be9a33c..6fabc513da9af 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js @@ -27,7 +27,24 @@ define([ }) }, 'Magento_Braintree/js/view/payment/adapter': { + config: {}, + + /** Stub */ + onReady: function () {}, + + /** Stub */ + setConfig: function (config) { + this.config = config; + }, + + /** Stub */ + setup: function () { + this.config.onReady(this.checkout); + }, + checkout: { + /** Stub */ + teardown: function () {}, paypal: { /** Stub */ initAuthFlow: function () {} From 5e664042c3023253e62231e0b51c5934a9842634 Mon Sep 17 00:00:00 2001 From: Hirokazu Nishi Date: Sat, 26 May 2018 23:11:56 +0900 Subject: [PATCH 0023/1171] added Currency Converter Api connection feature. --- .../Currency/Import/CurrencyConverterApi.php | 141 ++++++++++++++++++ .../Directory/etc/adminhtml/system.xml | 6 + app/code/Magento/Directory/etc/config.xml | 3 + app/code/Magento/Directory/etc/di.xml | 4 + 4 files changed, 154 insertions(+) create mode 100644 app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php diff --git a/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php b/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php new file mode 100644 index 0000000000000..a24bd51db64d8 --- /dev/null +++ b/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php @@ -0,0 +1,141 @@ +scopeConfig = $scopeConfig; + $this->httpClientFactory = $httpClientFactory; + } + + /** + * {@inheritdoc} + */ + public function fetchRates() + { + $data = []; + $currencies = $this->_getCurrencyCodes(); + $defaultCurrencies = $this->_getDefaultCurrencyCodes(); + + foreach ($defaultCurrencies as $currencyFrom) { + if (!isset($data[$currencyFrom])) { + $data[$currencyFrom] = []; + } + $data = $this->convertBatch($data, $currencyFrom, $currencies); + ksort($data[$currencyFrom]); + } + return $data; + } + + /** + * Return currencies convert rates in batch mode + * + * @param array $data + * @param string $currencyFrom + * @param array $currenciesTo + * @return array + */ + private function convertBatch($data, $currencyFrom, $currenciesTo) + { + foreach($currenciesTo as $to) { + set_time_limit(0); + try { + $url = str_replace('{{CURRENCY_FROM}}', $currencyFrom, self::CURRENCY_CONVERTER_URL); + $url = str_replace('{{CURRENCY_TO}}', $to, $url); + $response = $this->getServiceResponse($url); + if ($currencyFrom == $to) { + $data[$currencyFrom][$to] = $this->_numberFormat(1); + } else { + if (empty($response)) { + $this->_messages[] = __('We can\'t retrieve a rate from %1 for %2.', $url, $to); + $data[$currencyFrom][$to] = null; + } else { + $data[$currencyFrom][$to] = $this->_numberFormat( + (double)$response[$currencyFrom . '_' . $to] + ); + } + } + } finally { + ini_restore('max_execution_time'); + } + + } + + + return $data; + } + + /** + * Get Fixer.io service response + * + * @param string $url + * @param int $retry + * @return array + */ + private function getServiceResponse($url, $retry = 0) + { + /** @var \Magento\Framework\HTTP\ZendClient $httpClient */ + $httpClient = $this->httpClientFactory->create(); + $response = []; + + try { + $jsonResponse = $httpClient->setUri( + $url + )->setConfig( + [ + 'timeout' => $this->scopeConfig->getValue( + 'currency/currencyconverterapi/timeout', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ), + ] + )->request( + 'GET' + )->getBody(); + + $response = json_decode($jsonResponse, true); + } catch (\Exception $e) { + if ($retry == 0) { + $response = $this->getServiceResponse($url, 1); + } + } + return $response; + } + + /** + * {@inheritdoc} + */ + protected function _convert($currencyFrom, $currencyTo) + { + } +} \ No newline at end of file diff --git a/app/code/Magento/Directory/etc/adminhtml/system.xml b/app/code/Magento/Directory/etc/adminhtml/system.xml index 15a82e006bffe..cae3b1c41db36 100644 --- a/app/code/Magento/Directory/etc/adminhtml/system.xml +++ b/app/code/Magento/Directory/etc/adminhtml/system.xml @@ -52,6 +52,12 @@ + + + + + + diff --git a/app/code/Magento/Directory/etc/config.xml b/app/code/Magento/Directory/etc/config.xml index fa4e9d64d10d8..de3ff626bc12c 100644 --- a/app/code/Magento/Directory/etc/config.xml +++ b/app/code/Magento/Directory/etc/config.xml @@ -27,6 +27,9 @@ 100 + + 100 + 0 diff --git a/app/code/Magento/Directory/etc/di.xml b/app/code/Magento/Directory/etc/di.xml index 02e16af29ea14..4d0e51ab9f45a 100644 --- a/app/code/Magento/Directory/etc/di.xml +++ b/app/code/Magento/Directory/etc/di.xml @@ -22,6 +22,10 @@ Fixer.io Magento\Directory\Model\Currency\Import\FixerIo + + Currency Converter API + Magento\Directory\Model\Currency\Import\CurrencyConverterApi + From 54612e64fc73ffad12cd099b67df9ed6928bfabf Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Wed, 30 May 2018 13:36:25 -0500 Subject: [PATCH 0024/1171] MQE-987: Decouple MFTF from Magento - replace all framework command references with bin/mftf references --- composer.json | 7 + composer.lock | 2424 +++++++++++++++-- dev/tests/acceptance/.env.example | 37 - dev/tests/acceptance/LICENSE.txt | 48 - dev/tests/acceptance/LICENSE_AFL.txt | 48 - dev/tests/acceptance/README.md | 64 - dev/tests/acceptance/RoboFile.php | 229 +- dev/tests/acceptance/codeception.dist.yml | 35 - dev/tests/acceptance/composer.json | 13 +- dev/tests/acceptance/composer.lock | 1201 +------- dev/tests/acceptance/pre-install.php | 395 --- dev/tests/acceptance/tests/_bootstrap.php | 111 +- .../tests/functional.suite.dist.yml | 42 - .../tests/functional/_bootstrap.php | 8 - 14 files changed, 2361 insertions(+), 2301 deletions(-) delete mode 100644 dev/tests/acceptance/.env.example delete mode 100644 dev/tests/acceptance/LICENSE.txt delete mode 100644 dev/tests/acceptance/LICENSE_AFL.txt delete mode 100755 dev/tests/acceptance/README.md delete mode 100644 dev/tests/acceptance/codeception.dist.yml delete mode 100644 dev/tests/acceptance/pre-install.php delete mode 100644 dev/tests/acceptance/tests/functional.suite.dist.yml delete mode 100644 dev/tests/acceptance/tests/functional/_bootstrap.php diff --git a/composer.json b/composer.json index e2a89ae1207a7..db4d7b4128c1b 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,12 @@ "preferred-install": "dist", "sort-packages": true }, + "repositories": [ + { + "type": "git", + "url": "git@github.com:magento/magento2-functional-testing-framework.git" + } + ], "require": { "php": "~7.1.3||~7.2.0", "ext-ctype": "*", @@ -81,6 +87,7 @@ "zendframework/zend-view": "~2.10.0" }, "require-dev": { + "magento/magento2-functional-testing-framework": "dev-develop", "friendsofphp/php-cs-fixer": "~2.10.0", "lusitanian/oauth": "~0.8.10", "pdepend/pdepend": "2.5.2", diff --git a/composer.lock b/composer.lock index b104cf2929ba7..464bf79fe429a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "daacd8800615d44aa1af0ac06c1ecc46", + "content-hash": "cafb3927f4eee0965f3abed6c3adb103", "packages": [ { "name": "braintree/braintree_php", @@ -837,13 +837,6 @@ "reference": "68522e5768edc8e829d1f64b620a3de3753f1141", "shasum": "" }, - "archive": { - "exclude": [ - "/demos", - "/documentation", - "/tests" - ] - }, "require": { "php": ">=5.2.11" }, @@ -862,6 +855,7 @@ "Zend_": "library/" } }, + "notification-url": "https://packagist.org/downloads/", "include-path": [ "library/" ], @@ -871,14 +865,10 @@ "description": "Magento Zend Framework 1", "homepage": "http://framework.zend.com/", "keywords": [ - "framework", - "zf1" + "ZF1", + "framework" ], - "support": { - "source": "https://github.com/magento-engcom/zf1-php-7.2-support/tree/master", - "issues": "https://github.com/magento-engcom/zf1-php-7.2-support/issues" - }, - "time": "2018-04-06T17:12:22+00:00" + "time": "2018-04-06T18:49:03+00:00" }, { "name": "monolog/monolog", @@ -4319,153 +4309,142 @@ ], "packages-dev": [ { - "name": "doctrine/annotations", - "version": "v1.6.0", + "name": "allure-framework/allure-codeception", + "version": "1.2.7", "source": { "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5" + "url": "https://github.com/allure-framework/allure-codeception.git", + "reference": "48598f4b4603b50b663bfe977260113a40912131" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", - "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", + "url": "https://api.github.com/repos/allure-framework/allure-codeception/zipball/48598f4b4603b50b663bfe977260113a40912131", + "reference": "48598f4b4603b50b663bfe977260113a40912131", "shasum": "" }, "require": { - "doctrine/lexer": "1.*", - "php": "^7.1" - }, - "require-dev": { - "doctrine/cache": "1.*", - "phpunit/phpunit": "^6.4" + "allure-framework/allure-php-api": "~1.1.0", + "codeception/codeception": "~2.1", + "php": ">=5.4.0", + "symfony/filesystem": ">=2.6", + "symfony/finder": ">=2.6" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + "psr-0": { + "Yandex": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "Apache-2.0" ], "authors": [ { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Ivan Krutov", + "email": "vania-pooh@yandex-team.ru", + "role": "Developer" } ], - "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", + "description": "A Codeception adapter for Allure report.", + "homepage": "http://allure.qatools.ru/", "keywords": [ - "annotations", - "docblock", - "parser" - ], - "time": "2017-12-06T07:11:42+00:00" + "allure", + "attachments", + "cases", + "codeception", + "report", + "steps", + "testing" + ], + "time": "2018-03-07T11:18:27+00:00" }, { - "name": "doctrine/instantiator", - "version": "1.1.0", + "name": "allure-framework/allure-php-api", + "version": "1.1.4", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" + "url": "https://github.com/allure-framework/allure-php-adapter-api.git", + "reference": "a462a0da121681577033e13c123b6cc4e89cdc64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "url": "https://api.github.com/repos/allure-framework/allure-php-adapter-api/zipball/a462a0da121681577033e13c123b6cc4e89cdc64", + "reference": "a462a0da121681577033e13c123b6cc4e89cdc64", "shasum": "" }, "require": { - "php": "^7.1" - }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "^6.2.3", - "squizlabs/php_codesniffer": "^3.0.2" + "jms/serializer": ">=0.16.0", + "moontoast/math": ">=1.1.0", + "php": ">=5.4.0", + "phpunit/phpunit": ">=4.0.0", + "ramsey/uuid": ">=3.0.0", + "symfony/http-foundation": ">=2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + "psr-0": { + "Yandex": [ + "src/", + "test/" + ] } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "Apache-2.0" ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "name": "Ivan Krutov", + "email": "vania-pooh@yandex-team.ru", + "role": "Developer" } ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", + "description": "PHP API for Allure adapter", + "homepage": "http://allure.qatools.ru/", "keywords": [ - "constructor", - "instantiate" + "allure", + "api", + "php", + "report" ], - "time": "2017-07-22T11:58:36+00:00" + "time": "2016-12-07T12:15:46+00:00" }, { - "name": "doctrine/lexer", - "version": "v1.0.1", + "name": "behat/gherkin", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + "url": "https://github.com/Behat/Gherkin.git", + "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", + "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": ">=5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "~4.5|~5", + "symfony/phpunit-bridge": "~2.7|~3", + "symfony/yaml": "~2.3|~3" + }, + "suggest": { + "symfony/yaml": "If you want to parse features, represented in YAML files" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "4.4-dev" } }, "autoload": { "psr-0": { - "Doctrine\\Common\\Lexer\\": "lib/" + "Behat\\Gherkin": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4474,95 +4453,94 @@ ], "authors": [ { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" } ], - "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "http://www.doctrine-project.org", + "description": "Gherkin DSL parser for PHP 5.3", + "homepage": "http://behat.org/", "keywords": [ - "lexer", + "BDD", + "Behat", + "Cucumber", + "DSL", + "gherkin", "parser" ], - "time": "2014-09-09T13:34:57+00:00" + "time": "2016-10-30T11:50:56+00:00" }, { - "name": "friendsofphp/php-cs-fixer", - "version": "v2.10.5", + "name": "codeception/codeception", + "version": "2.3.9", "source": { "type": "git", - "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "e49993dfb9b96ec8b8d9964c4627599b050a6e99" + "url": "https://github.com/Codeception/Codeception.git", + "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/e49993dfb9b96ec8b8d9964c4627599b050a6e99", - "reference": "e49993dfb9b96ec8b8d9964c4627599b050a6e99", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e", + "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e", "shasum": "" }, "require": { - "composer/semver": "^1.4", - "doctrine/annotations": "^1.2", + "behat/gherkin": "~4.4.0", + "codeception/stub": "^1.0", "ext-json": "*", - "ext-tokenizer": "*", - "php": "^5.6 || >=7.0 <7.3", - "php-cs-fixer/diff": "^1.2", - "symfony/console": "^3.2 || ^4.0", - "symfony/event-dispatcher": "^3.0 || ^4.0", - "symfony/filesystem": "^3.0 || ^4.0", - "symfony/finder": "^3.0 || ^4.0", - "symfony/options-resolver": "^3.0 || ^4.0", - "symfony/polyfill-php70": "^1.0", - "symfony/polyfill-php72": "^1.4", - "symfony/process": "^3.0 || ^4.0", - "symfony/stopwatch": "^3.0 || ^4.0" - }, - "conflict": { - "hhvm": "*" + "ext-mbstring": "*", + "facebook/webdriver": ">=1.1.3 <2.0", + "guzzlehttp/guzzle": ">=4.1.4 <7.0", + "guzzlehttp/psr7": "~1.0", + "php": ">=5.4.0 <8.0", + "phpunit/php-code-coverage": ">=2.2.4 <6.0", + "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", + "sebastian/comparator": ">1.1 <3.0", + "sebastian/diff": ">=1.4 <3.0", + "symfony/browser-kit": ">=2.7 <5.0", + "symfony/console": ">=2.7 <5.0", + "symfony/css-selector": ">=2.7 <5.0", + "symfony/dom-crawler": ">=2.7 <5.0", + "symfony/event-dispatcher": ">=2.7 <5.0", + "symfony/finder": ">=2.7 <5.0", + "symfony/yaml": ">=2.7 <5.0" }, "require-dev": { - "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", - "justinrainbow/json-schema": "^5.0", - "keradus/cli-executor": "^1.0", - "mikey179/vfsstream": "^1.6", - "php-coveralls/php-coveralls": "^2.0", - "php-cs-fixer/accessible-object": "^1.0", - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "phpunitgoodpractices/traits": "^1.3.1", - "symfony/phpunit-bridge": "^3.2.2 || ^4.0" + "codeception/specify": "~0.3", + "facebook/graph-sdk": "~5.3", + "flow/jsonpath": "~0.2", + "monolog/monolog": "~1.8", + "pda/pheanstalk": "~3.0", + "php-amqplib/php-amqplib": "~2.4", + "predis/predis": "^1.0", + "squizlabs/php_codesniffer": "~2.0", + "symfony/process": ">=2.7 <5.0", + "vlucas/phpdotenv": "^2.4.0" }, "suggest": { - "ext-mbstring": "For handling non-UTF8 characters in cache signature.", - "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." + "aws/aws-sdk-php": "For using AWS Auth in REST module and Queue module", + "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests", + "codeception/specify": "BDD-style code blocks", + "codeception/verify": "BDD-style assertions", + "flow/jsonpath": "For using JSONPath in REST module", + "league/factory-muffin": "For DataFactory module", + "league/factory-muffin-faker": "For Faker support in DataFactory module", + "phpseclib/phpseclib": "for SFTP option in FTP Module", + "stecman/symfony-console-completion": "For BASH autocompletion", + "symfony/phpunit-bridge": "For phpunit-bridge support" }, "bin": [ - "php-cs-fixer" + "codecept" ], - "type": "application", + "type": "library", + "extra": { + "branch-alias": [] + }, "autoload": { "psr-4": { - "PhpCsFixer\\": "src/" - }, - "classmap": [ - "tests/Test/AbstractFixerTestCase.php", - "tests/Test/AbstractIntegrationCaseFactory.php", - "tests/Test/AbstractIntegrationTestCase.php", - "tests/Test/Assert/AssertTokensTrait.php", - "tests/Test/Constraint/SameStringsConstraint.php", - "tests/Test/IntegrationCase.php", - "tests/Test/IntegrationCaseFactory.php", - "tests/Test/IntegrationCaseFactoryInterface.php", - "tests/Test/InternalIntegrationCaseFactory.php", - "tests/TestCase.php" - ] + "Codeception\\": "src\\Codeception", + "Codeception\\Extension\\": "ext" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4570,55 +4548,92 @@ ], "authors": [ { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Michael Bodnarchuk", + "email": "davert@mail.ua", + "homepage": "http://codegyre.com" } ], - "description": "A tool to automatically fix PHP code style", - "time": "2018-03-20T18:07:08+00:00" + "description": "BDD-style testing framework", + "homepage": "http://codeception.com/", + "keywords": [ + "BDD", + "TDD", + "acceptance testing", + "functional testing", + "unit testing" + ], + "time": "2018-02-26T23:29:41+00:00" }, { - "name": "lusitanian/oauth", - "version": "v0.8.10", + "name": "codeception/stub", + "version": "1.0.2", "source": { "type": "git", - "url": "https://github.com/Lusitanian/PHPoAuthLib.git", - "reference": "09f4af38f17db6938253f4d1b171d537913ac1ed" + "url": "https://github.com/Codeception/Stub.git", + "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Lusitanian/PHPoAuthLib/zipball/09f4af38f17db6938253f4d1b171d537913ac1ed", - "reference": "09f4af38f17db6938253f4d1b171d537913ac1ed", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/95fb7a36b81890dd2e5163e7ab31310df6f1bb99", + "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99", "shasum": "" }, "require": { - "php": ">=5.3.0" + "phpunit/phpunit-mock-objects": ">2.3 <7.0" }, "require-dev": { - "phpunit/phpunit": "3.7.*", - "predis/predis": "0.8.*@dev", - "squizlabs/php_codesniffer": "2.*", - "symfony/http-foundation": "~2.1" + "phpunit/phpunit": ">=4.8 <8.0" }, - "suggest": { - "ext-openssl": "Allows for usage of secure connections with the stream-based HTTP client.", - "predis/predis": "Allows using the Redis storage backend.", - "symfony/http-foundation": "Allows using the Symfony Session storage backend." + "type": "library", + "autoload": { + "psr-4": { + "Codeception\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", + "time": "2018-02-18T13:56:56+00:00" + }, + { + "name": "consolidation/annotated-command", + "version": "2.8.3", + "source": { + "type": "git", + "url": "https://github.com/consolidation/annotated-command.git", + "reference": "8f8f5da2ca06fbd3a85f7d551c49f844b7c59437" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/8f8f5da2ca06fbd3a85f7d551c49f844b7c59437", + "reference": "8f8f5da2ca06fbd3a85f7d551c49f844b7c59437", + "shasum": "" + }, + "require": { + "consolidation/output-formatters": "^3.1.12", + "php": ">=5.4.0", + "psr/log": "^1", + "symfony/console": "^2.8|^3|^4", + "symfony/event-dispatcher": "^2.5|^3|^4", + "symfony/finder": "^2.5|^3|^4" + }, + "require-dev": { + "greg-1-anderson/composer-test-scenarios": "^1", + "phpunit/phpunit": "^4.8", + "satooshi/php-coveralls": "^1.0.2 | dev-master", + "squizlabs/php_codesniffer": "^2.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.1-dev" + "dev-master": "2.x-dev" } }, "autoload": { - "psr-0": { - "OAuth": "src", - "OAuth\\Unit": "tests" + "psr-4": { + "Consolidation\\AnnotatedCommand\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -4627,39 +4642,1592 @@ ], "authors": [ { - "name": "David Desberg", - "email": "david@daviddesberg.com" - }, - { - "name": "Elliot Chance", - "email": "elliotchance@gmail.com" - }, - { - "name": "Pieter Hordijk", - "email": "info@pieterhordijk.com" + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" } ], - "description": "PHP 5.3+ oAuth 1/2 Library", - "keywords": [ - "Authentication", - "authorization", - "oauth", - "security" - ], - "time": "2016-07-12T22:15:40+00:00" + "description": "Initialize Symfony Console commands from annotated command class methods.", + "time": "2018-02-23T16:32:04+00:00" }, { - "name": "myclabs/deep-copy", - "version": "1.7.0", + "name": "consolidation/config", + "version": "1.0.9", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + "url": "https://github.com/consolidation/config.git", + "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "url": "https://api.github.com/repos/consolidation/config/zipball/34ca8d7c1ee60a7b591b10617114cf1210a2e92c", + "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^1.1.0", + "grasmash/expander": "^1", + "php": ">=5.4.0" + }, + "require-dev": { + "greg-1-anderson/composer-test-scenarios": "^1", + "phpunit/phpunit": "^4", + "satooshi/php-coveralls": "^1.0", + "squizlabs/php_codesniffer": "2.*", + "symfony/console": "^2.5|^3|^4", + "symfony/yaml": "^2.8.11|^3|^4" + }, + "suggest": { + "symfony/yaml": "Required to use Consolidation\\Config\\Loader\\YamlConfigLoader" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Provide configuration services for a commandline tool.", + "time": "2017-12-22T17:28:19+00:00" + }, + { + "name": "consolidation/log", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/consolidation/log.git", + "reference": "dbc7c535f319a4a2d5a5077738f8eb7c10df8821" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/log/zipball/dbc7c535f319a4a2d5a5077738f8eb7c10df8821", + "reference": "dbc7c535f319a4a2d5a5077738f8eb7c10df8821", + "shasum": "" + }, + "require": { + "php": ">=5.5.0", + "psr/log": "~1.0", + "symfony/console": "^2.8|^3|^4" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "satooshi/php-coveralls": "dev-master", + "squizlabs/php_codesniffer": "2.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", + "time": "2017-11-29T01:44:16+00:00" + }, + { + "name": "consolidation/output-formatters", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/consolidation/output-formatters.git", + "reference": "da889e4bce19f145ca4ec5b1725a946f4eb625a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/da889e4bce19f145ca4ec5b1725a946f4eb625a9", + "reference": "da889e4bce19f145ca4ec5b1725a946f4eb625a9", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "symfony/console": "^2.8|^3|^4", + "symfony/finder": "^2.5|^3|^4" + }, + "require-dev": { + "g-1-a/composer-test-scenarios": "^2", + "phpunit/phpunit": "^5.7.27", + "satooshi/php-coveralls": "^2", + "squizlabs/php_codesniffer": "^2.7", + "symfony/console": "3.2.3", + "symfony/var-dumper": "^2.8|^3|^4", + "victorjonsson/markdowndocs": "^1.3" + }, + "suggest": { + "symfony/var-dumper": "For using the var_dump formatter" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\OutputFormatters\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Format text by applying transformations provided by plug-in formatters.", + "time": "2018-03-20T15:18:32+00:00" + }, + { + "name": "consolidation/robo", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/consolidation/Robo.git", + "reference": "54a13e268917b92576d75e10dca8227b95a574d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/54a13e268917b92576d75e10dca8227b95a574d9", + "reference": "54a13e268917b92576d75e10dca8227b95a574d9", + "shasum": "" + }, + "require": { + "consolidation/annotated-command": "^2.8.2", + "consolidation/config": "^1.0.1", + "consolidation/log": "~1", + "consolidation/output-formatters": "^3.1.13", + "grasmash/yaml-expander": "^1.3", + "league/container": "^2.2", + "php": ">=5.5.0", + "symfony/console": "^2.8|^3|^4", + "symfony/event-dispatcher": "^2.5|^3|^4", + "symfony/filesystem": "^2.5|^3|^4", + "symfony/finder": "^2.5|^3|^4", + "symfony/process": "^2.5|^3|^4" + }, + "replace": { + "codegyre/robo": "< 1.0" + }, + "require-dev": { + "codeception/aspect-mock": "^1|^2.1.1", + "codeception/base": "^2.3.7", + "codeception/verify": "^0.3.2", + "g-1-a/composer-test-scenarios": "^2", + "goaop/framework": "~2.1.2", + "goaop/parser-reflection": "^1.1.0", + "natxet/cssmin": "3.0.4", + "nikic/php-parser": "^3.1.5", + "patchwork/jsqueeze": "~2", + "pear/archive_tar": "^1.4.2", + "phpunit/php-code-coverage": "~2|~4", + "satooshi/php-coveralls": "^2", + "squizlabs/php_codesniffer": "^2.8" + }, + "suggest": { + "henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch", + "natxet/CssMin": "For minifying CSS files in taskMinify", + "patchwork/jsqueeze": "For minifying JS files in taskMinify", + "pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively." + }, + "bin": [ + "robo" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev", + "dev-state": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Robo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Davert", + "email": "davert.php@resend.cc" + } + ], + "description": "Modern task runner", + "time": "2018-04-06T05:27:37+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/3fbd874921ab2c041e899d044585a2ab9795df8a", + "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Dflydev\\DotAccessData": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "time": "2017-01-20T21:14:22+00:00" + }, + { + "name": "doctrine/annotations", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", + "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": "^7.1" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2017-12-06T07:11:42+00:00" + }, + { + "name": "doctrine/collections", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/a01ee38fcd999f34d9bfbcee59dbda5105449cbf", + "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "~0.1@dev", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ], + "time": "2017-07-22T10:37:32+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "^6.2.3", + "squizlabs/php_codesniffer": "^3.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2017-07-22T11:58:36+00:00" + }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2014-09-09T13:34:57+00:00" + }, + { + "name": "epfremme/swagger-php", + "version": "v2.0.0", + "source": { + "type": "git", + "url": "https://github.com/epfremmer/swagger-php.git", + "reference": "eee28a442b7e6220391ec953d3c9b936354f23bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/epfremmer/swagger-php/zipball/eee28a442b7e6220391ec953d3c9b936354f23bc", + "reference": "eee28a442b7e6220391ec953d3c9b936354f23bc", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.2", + "doctrine/collections": "^1.3", + "jms/serializer": "^1.1", + "php": ">=5.5", + "phpoption/phpoption": "^1.1", + "symfony/yaml": "^2.7|^3.1" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "~4.8|~5.0", + "satooshi/php-coveralls": "^1.0" + }, + "type": "package", + "autoload": { + "psr-4": { + "Epfremme\\Swagger\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Edward Pfremmer", + "email": "epfremme@nerdery.com" + } + ], + "description": "Library for parsing swagger documentation into PHP entities for use in testing and code generation", + "time": "2016-09-26T17:24:17+00:00" + }, + { + "name": "facebook/webdriver", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/facebook/php-webdriver.git", + "reference": "86b5ca2f67173c9d34340845dd690149c886a605" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/86b5ca2f67173c9d34340845dd690149c886a605", + "reference": "86b5ca2f67173c9d34340845dd690149c886a605", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-zip": "*", + "php": "^5.6 || ~7.0", + "symfony/process": "^2.8 || ^3.1 || ^4.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.0", + "guzzle/guzzle": "^3.4.1", + "php-coveralls/php-coveralls": "^1.0.2", + "php-mock/php-mock-phpunit": "^1.1", + "phpunit/phpunit": "^5.7", + "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", + "squizlabs/php_codesniffer": "^2.6", + "symfony/var-dumper": "^3.3 || ^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-community": "1.5-dev" + } + }, + "autoload": { + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "A PHP client for Selenium WebDriver", + "homepage": "https://github.com/facebook/php-webdriver", + "keywords": [ + "facebook", + "php", + "selenium", + "webdriver" + ], + "time": "2017-11-15T11:08:09+00:00" + }, + { + "name": "flow/jsonpath", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/FlowCommunications/JSONPath.git", + "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FlowCommunications/JSONPath/zipball/f0222818d5c938e4ab668ab2e2c079bd51a27112", + "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "peekmo/jsonpath": "dev-master", + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Flow\\JSONPath": "src/", + "Flow\\JSONPath\\Test": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Frank", + "email": "stephen@flowsa.com" + } + ], + "description": "JSONPath implementation for parsing, searching and flattening arrays", + "time": "2018-03-04T16:39:47+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v2.10.5", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", + "reference": "e49993dfb9b96ec8b8d9964c4627599b050a6e99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/e49993dfb9b96ec8b8d9964c4627599b050a6e99", + "reference": "e49993dfb9b96ec8b8d9964c4627599b050a6e99", + "shasum": "" + }, + "require": { + "composer/semver": "^1.4", + "doctrine/annotations": "^1.2", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^5.6 || >=7.0 <7.3", + "php-cs-fixer/diff": "^1.2", + "symfony/console": "^3.2 || ^4.0", + "symfony/event-dispatcher": "^3.0 || ^4.0", + "symfony/filesystem": "^3.0 || ^4.0", + "symfony/finder": "^3.0 || ^4.0", + "symfony/options-resolver": "^3.0 || ^4.0", + "symfony/polyfill-php70": "^1.0", + "symfony/polyfill-php72": "^1.4", + "symfony/process": "^3.0 || ^4.0", + "symfony/stopwatch": "^3.0 || ^4.0" + }, + "conflict": { + "hhvm": "*" + }, + "require-dev": { + "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", + "justinrainbow/json-schema": "^5.0", + "keradus/cli-executor": "^1.0", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.0", + "php-cs-fixer/accessible-object": "^1.0", + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "phpunitgoodpractices/traits": "^1.3.1", + "symfony/phpunit-bridge": "^3.2.2 || ^4.0" + }, + "suggest": { + "ext-mbstring": "For handling non-UTF8 characters in cache signature.", + "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + }, + "classmap": [ + "tests/Test/AbstractFixerTestCase.php", + "tests/Test/AbstractIntegrationCaseFactory.php", + "tests/Test/AbstractIntegrationTestCase.php", + "tests/Test/Assert/AssertTokensTrait.php", + "tests/Test/Constraint/SameStringsConstraint.php", + "tests/Test/IntegrationCase.php", + "tests/Test/IntegrationCaseFactory.php", + "tests/Test/IntegrationCaseFactoryInterface.php", + "tests/Test/InternalIntegrationCaseFactory.php", + "tests/TestCase.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "time": "2018-03-20T18:07:08+00:00" + }, + { + "name": "fzaninotto/faker", + "version": "v1.7.1", + "source": { + "type": "git", + "url": "https://github.com/fzaninotto/Faker.git", + "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", + "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "ext-intl": "*", + "phpunit/phpunit": "^4.0 || ^5.0", + "squizlabs/php_codesniffer": "^1.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "time": "2017-08-15T16:48:10+00:00" + }, + { + "name": "grasmash/expander", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/grasmash/expander.git", + "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/grasmash/expander/zipball/95d6037344a4be1dd5f8e0b0b2571a28c397578f", + "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^1.1.0", + "php": ">=5.4" + }, + "require-dev": { + "greg-1-anderson/composer-test-scenarios": "^1", + "phpunit/phpunit": "^4|^5.5.4", + "satooshi/php-coveralls": "^1.0.2|dev-master", + "squizlabs/php_codesniffer": "^2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Grasmash\\Expander\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Grasmick" + } + ], + "description": "Expands internal property references in PHP arrays file.", + "time": "2017-12-21T22:14:55+00:00" + }, + { + "name": "grasmash/yaml-expander", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/grasmash/yaml-expander.git", + "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/grasmash/yaml-expander/zipball/3f0f6001ae707a24f4d9733958d77d92bf9693b1", + "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^1.1.0", + "php": ">=5.4", + "symfony/yaml": "^2.8.11|^3|^4" + }, + "require-dev": { + "greg-1-anderson/composer-test-scenarios": "^1", + "phpunit/phpunit": "^4.8|^5.5.4", + "satooshi/php-coveralls": "^1.0.2|dev-master", + "squizlabs/php_codesniffer": "^2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Grasmash\\YamlExpander\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Grasmick" + } + ], + "description": "Expands internal property references in a yaml file.", + "time": "2017-12-16T16:06:03+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "6.3.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "shasum": "" + }, + "require": { + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.0" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.3-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2018-04-22T15:46:56+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2016-12-20T10:07:11+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2017-03-20T17:10:46+00:00" + }, + { + "name": "jms/metadata", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/metadata.git", + "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/6a06970a10e0a532fb52d3959547123b84a3b3ab", + "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "doctrine/cache": "~1.0", + "symfony/cache": "~3.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + }, + "autoload": { + "psr-0": { + "Metadata\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Class/method/property metadata management in PHP", + "keywords": [ + "annotations", + "metadata", + "xml", + "yaml" + ], + "time": "2016-12-05T10:18:33+00:00" + }, + { + "name": "jms/parser-lib", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/parser-lib.git", + "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/parser-lib/zipball/c509473bc1b4866415627af0e1c6cc8ac97fa51d", + "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d", + "shasum": "" + }, + "require": { + "phpoption/phpoption": ">=0.9,<2.0-dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "JMS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache2" + ], + "description": "A library for easily creating recursive-descent parsers.", + "time": "2012-11-18T18:08:43+00:00" + }, + { + "name": "jms/serializer", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/serializer.git", + "reference": "e7c53477ff55c21d1b1db7d062edc050a24f465f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/e7c53477ff55c21d1b1db7d062edc050a24f465f", + "reference": "e7c53477ff55c21d1b1db7d062edc050a24f465f", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.0", + "doctrine/instantiator": "^1.0.3", + "jms/metadata": "~1.1", + "jms/parser-lib": "1.*", + "php": "^5.5|^7.0", + "phpcollection/phpcollection": "~0.1", + "phpoption/phpoption": "^1.1" + }, + "conflict": { + "twig/twig": "<1.12" + }, + "require-dev": { + "doctrine/orm": "~2.1", + "doctrine/phpcr-odm": "^1.3|^2.0", + "ext-pdo_sqlite": "*", + "jackalope/jackalope-doctrine-dbal": "^1.1.5", + "phpunit/phpunit": "^4.8|^5.0", + "propel/propel1": "~1.7", + "psr/container": "^1.0", + "symfony/dependency-injection": "^2.7|^3.3|^4.0", + "symfony/expression-language": "^2.6|^3.0", + "symfony/filesystem": "^2.1", + "symfony/form": "~2.1|^3.0", + "symfony/translation": "^2.1|^3.0", + "symfony/validator": "^2.2|^3.0", + "symfony/yaml": "^2.1|^3.0", + "twig/twig": "~1.12|~2.0" + }, + "suggest": { + "doctrine/cache": "Required if you like to use cache functionality.", + "doctrine/collections": "Required if you like to use doctrine collection types as ArrayCollection.", + "symfony/yaml": "Required if you'd like to serialize data to YAML format." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-0": { + "JMS\\Serializer": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + }, + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.", + "homepage": "http://jmsyst.com/libs/serializer", + "keywords": [ + "deserialization", + "jaxb", + "json", + "serialization", + "xml" + ], + "time": "2018-02-04T17:48:54+00:00" + }, + { + "name": "league/container", + "version": "2.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/container.git", + "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/container/zipball/43f35abd03a12977a60ffd7095efd6a7808488c0", + "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.2", + "php": "^5.4.0 || ^7.0" + }, + "provide": { + "container-interop/container-interop-implementation": "^1.2", + "psr/container-implementation": "^1.0" + }, + "replace": { + "orno/di": "~2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Container\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Phil Bennett", + "email": "philipobenito@gmail.com", + "homepage": "http://www.philipobenito.com", + "role": "Developer" + } + ], + "description": "A fast and intuitive dependency injection container.", + "homepage": "https://github.com/thephpleague/container", + "keywords": [ + "container", + "dependency", + "di", + "injection", + "league", + "provider", + "service" + ], + "time": "2017-05-10T09:20:27+00:00" + }, + { + "name": "lusitanian/oauth", + "version": "v0.8.10", + "source": { + "type": "git", + "url": "https://github.com/Lusitanian/PHPoAuthLib.git", + "reference": "09f4af38f17db6938253f4d1b171d537913ac1ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Lusitanian/PHPoAuthLib/zipball/09f4af38f17db6938253f4d1b171d537913ac1ed", + "reference": "09f4af38f17db6938253f4d1b171d537913ac1ed", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*", + "predis/predis": "0.8.*@dev", + "squizlabs/php_codesniffer": "2.*", + "symfony/http-foundation": "~2.1" + }, + "suggest": { + "ext-openssl": "Allows for usage of secure connections with the stream-based HTTP client.", + "predis/predis": "Allows using the Redis storage backend.", + "symfony/http-foundation": "Allows using the Symfony Session storage backend." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.1-dev" + } + }, + "autoload": { + "psr-0": { + "OAuth": "src", + "OAuth\\Unit": "tests" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David Desberg", + "email": "david@daviddesberg.com" + }, + { + "name": "Elliot Chance", + "email": "elliotchance@gmail.com" + }, + { + "name": "Pieter Hordijk", + "email": "info@pieterhordijk.com" + } + ], + "description": "PHP 5.3+ oAuth 1/2 Library", + "keywords": [ + "Authentication", + "authorization", + "oauth", + "security" + ], + "time": "2016-07-12T22:15:40+00:00" + }, + { + "name": "magento/magento2-functional-testing-framework", + "version": "dev-develop", + "source": { + "type": "git", + "url": "git@github.com:magento/magento2-functional-testing-framework.git", + "reference": "343009b056b11d8836e132221d72aab8fafa039f" + }, + "require": { + "allure-framework/allure-codeception": "~1.2.6", + "codeception/codeception": "~2.3.4", + "consolidation/robo": "^1.0.0", + "epfremme/swagger-php": "^2.0", + "flow/jsonpath": ">0.2", + "fzaninotto/faker": "^1.6", + "mustache/mustache": "~2.5", + "php": "7.0.2|7.0.4|~7.0.6|~7.1.0|~7.2.0", + "symfony/process": "^2.8 || ^3.1 || ^4.0", + "vlucas/phpdotenv": "^2.4" + }, + "require-dev": { + "brainmaestro/composer-git-hooks": "^2.3", + "codacy/coverage": "^1.4", + "codeception/aspect-mock": "^3.0", + "doctrine/cache": "<1.7.0", + "goaop/framework": "2.2.0", + "php-coveralls/php-coveralls": "^1.0", + "phpmd/phpmd": "^2.6.0", + "rregeer/phpunit-coverage-check": "^0.1.4", + "sebastian/phpcpd": "~3.0", + "squizlabs/php_codesniffer": "1.5.3", + "symfony/stopwatch": "~3.4.6" + }, + "bin": [ + "bin/mftf" + ], + "type": "library", + "extra": { + "hooks": { + "pre-push": "bin/all-checks" + } + }, + "autoload": { + "files": [ + "src/Magento/FunctionalTestingFramework/_bootstrap.php" + ], + "psr-4": { + "Magento\\FunctionalTestingFramework\\": "src/Magento/FunctionalTestingFramework", + "MFTF\\": "dev/tests/functional/MFTF" + } + }, + "autoload-dev": { + "psr-4": { + "tests\\unit\\": "dev/tests/unit" + } + }, + "scripts": { + "tests": [ + "bin/phpunit-checks" + ], + "static": [ + "bin/static-checks" + ] + }, + "license": [ + "AGPL-3.0" + ], + "description": "Magento2 Functional Testing Framework", + "keywords": [ + "automation", + "functional", + "magento", + "testing" + ], + "time": "2018-05-30T15:36:22+00:00" + }, + { + "name": "moontoast/math", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/ramsey/moontoast-math.git", + "reference": "c2792a25df5cad4ff3d760dd37078fc5b6fccc79" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/moontoast-math/zipball/c2792a25df5cad4ff3d760dd37078fc5b6fccc79", + "reference": "c2792a25df5cad4ff3d760dd37078fc5b6fccc79", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "jakub-onderka/php-parallel-lint": "^0.9.0", + "phpunit/phpunit": "^4.7|>=5.0 <5.4", + "satooshi/php-coveralls": "^0.6.1", + "squizlabs/php_codesniffer": "^2.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Moontoast\\Math\\": "src/Moontoast/Math/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A mathematics library, providing functionality for large numbers", + "homepage": "https://github.com/ramsey/moontoast-math", + "keywords": [ + "bcmath", + "math" + ], + "time": "2017-02-16T16:54:46+00:00" + }, + { + "name": "mustache/mustache", + "version": "v2.12.0", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/mustache.php.git", + "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/fe8fe72e9d580591854de404cc59a1b83ca4d19e", + "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~1.11", + "phpunit/phpunit": "~3.7|~4.0|~5.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Mustache": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "A Mustache implementation in PHP.", + "homepage": "https://github.com/bobthecow/mustache.php", + "keywords": [ + "mustache", + "templating" + ], + "time": "2017-07-11T12:54:05+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", "shasum": "" }, "require": { @@ -4886,6 +6454,54 @@ ], "time": "2018-02-15T16:58:55+00:00" }, + { + "name": "phpcollection/phpcollection", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-collection.git", + "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-collection/zipball/f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", + "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", + "shasum": "" + }, + "require": { + "phpoption/phpoption": "1.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.4-dev" + } + }, + "autoload": { + "psr-0": { + "PhpCollection": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache2" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "General-Purpose Collection Library for PHP", + "keywords": [ + "collection", + "list", + "map", + "sequence", + "set" + ], + "time": "2015-05-17T12:39:23+00:00" + }, { "name": "phpdocumentor/reflection-common", "version": "1.0.1", @@ -5102,7 +6718,57 @@ "phpmd", "pmd" ], - "time": "2017-01-20T14:41:10+00:00" + "time": "2017-01-20T14:41:10+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed", + "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-0": { + "PhpOption\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache2" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "time": "2015-07-25T16:39:46+00:00" }, { "name": "phpspec/prophecy", @@ -6258,6 +7924,63 @@ ], "time": "2017-12-19T21:44:46+00:00" }, + { + "name": "symfony/browser-kit", + "version": "v4.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "c43bfa0182363b3fd64331b5e64e467349ff4670" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c43bfa0182363b3fd64331b5e64e467349ff4670", + "reference": "c43bfa0182363b3fd64331b5e64e467349ff4670", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/dom-crawler": "~3.4|~4.0" + }, + "require-dev": { + "symfony/css-selector": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0" + }, + "suggest": { + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\BrowserKit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony BrowserKit Component", + "homepage": "https://symfony.com", + "time": "2018-03-19T22:35:49+00:00" + }, { "name": "symfony/config", "version": "v4.0.8", @@ -6320,6 +8043,59 @@ "homepage": "https://symfony.com", "time": "2018-03-19T22:35:49+00:00" }, + { + "name": "symfony/css-selector", + "version": "v4.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "03f965583147957f1ecbad7ea1c9d6fd5e525ec2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/03f965583147957f1ecbad7ea1c9d6fd5e525ec2", + "reference": "03f965583147957f1ecbad7ea1c9d6fd5e525ec2", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2018-03-19T22:35:49+00:00" + }, { "name": "symfony/dependency-injection", "version": "v4.0.8", @@ -6391,6 +8167,115 @@ "homepage": "https://symfony.com", "time": "2018-04-02T09:52:41+00:00" }, + { + "name": "symfony/dom-crawler", + "version": "v4.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "d6c04c7532535b5e0b63db45b543cd60818e0fbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/d6c04c7532535b5e0b63db45b543cd60818e0fbc", + "reference": "d6c04c7532535b5e0b63db45b543cd60818e0fbc", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "~3.4|~4.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DomCrawler Component", + "homepage": "https://symfony.com", + "time": "2018-03-19T22:35:49+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v4.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "014487772c22d893168e5d628a13e882009fea29" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/014487772c22d893168e5d628a13e882009fea29", + "reference": "014487772c22d893168e5d628a13e882009fea29", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.1" + }, + "require-dev": { + "symfony/expression-language": "~3.4|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony HttpFoundation Component", + "homepage": "https://symfony.com", + "time": "2018-04-30T01:05:59+00:00" + }, { "name": "symfony/options-resolver", "version": "v4.0.8", @@ -6608,6 +8493,64 @@ "homepage": "https://symfony.com", "time": "2018-02-19T16:50:22+00:00" }, + { + "name": "symfony/yaml", + "version": "v3.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "033cfa61ef06ee0847e056e530201842b6e926c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/033cfa61ef06ee0847e056e530201842b6e926c3", + "reference": "033cfa61ef06ee0847e056e530201842b6e926c3", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2018-04-08T08:21:29+00:00" + }, { "name": "theseer/fdomdocument", "version": "1.6.6", @@ -6688,6 +8631,56 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "time": "2017-04-07T12:08:54+00:00" }, + { + "name": "vlucas/phpdotenv", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", + "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "phpunit/phpunit": "^4.8 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause-Attribution" + ], + "authors": [ + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "http://www.vancelucas.com" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "time": "2016-09-01T10:05:43+00:00" + }, { "name": "webmozart/assert", "version": "1.3.0", @@ -6742,6 +8735,7 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { + "magento/magento2-functional-testing-framework": 20, "phpmd/phpmd": 0 }, "prefer-stable": true, diff --git a/dev/tests/acceptance/.env.example b/dev/tests/acceptance/.env.example deleted file mode 100644 index 432851acbf286..0000000000000 --- a/dev/tests/acceptance/.env.example +++ /dev/null @@ -1,37 +0,0 @@ -#Copyright © Magento, Inc. All rights reserved. -#See COPYING.txt for license details. - -#*** Set the base URL for your Magento instance ***# -MAGENTO_BASE_URL= - -#*** Set the Admin Username and Password for your Magento instance ***# -MAGENTO_BACKEND_NAME= -MAGENTO_ADMIN_USERNAME= -MAGENTO_ADMIN_PASSWORD= - -#*** Path to CLI entry point and command parameter name. Uncomment and change if folder structure differs from standard Magento installation -#MAGENTO_CLI_COMMAND_PATH=dev/tests/acceptance/utils/command.php -#MAGENTO_CLI_COMMAND_PARAMETER=command - -#*** Selenium Server Protocol, Host, Port, and Path, with local defaults. Uncomment and change if not running Selenium locally. -#SELENIUM_HOST=127.0.0.1 -#SELENIUM_PORT=4444 -#SELENIUM_PROTOCOL=http -#SELENIUM_PATH=/wd/hub - -#*** Uncomment and set host & port if your dev environment needs different value other than MAGENTO_BASE_URL for Rest API Requests ***# -#MAGENTO_RESTAPI_SERVER_HOST= -#MAGENTO_RESTAPI_SERVER_PORT= - -#*** Uncomment these properties to set up a dev environment with symlinked projects ***# -#TESTS_BP= -#FW_BP= -#TESTS_MODULE_PATH= - -#*** These properties impact the modules loaded into MFTF, you can point to your own full path, or a custom set of modules located with the core set -MODULE_WHITELIST=Magento_Framework,Magento_ConfigurableProductWishlist,Magento_ConfigurableProductCatalogSearch -#CUSTOM_MODULE_PATHS= - -#*** Bool property which allows the user to toggle debug output during test execution -#MFTF_DEBUG= -#*** End of .env ***# diff --git a/dev/tests/acceptance/LICENSE.txt b/dev/tests/acceptance/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/dev/tests/acceptance/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -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/dev/tests/acceptance/LICENSE_AFL.txt b/dev/tests/acceptance/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/dev/tests/acceptance/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -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/dev/tests/acceptance/README.md b/dev/tests/acceptance/README.md deleted file mode 100755 index 6350b9cabcdfa..0000000000000 --- a/dev/tests/acceptance/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# Magento Functional Testing Framework - ----- - -## System Requirements -[Magento Functional Testing Framework system requirements](http://devdocs.magento.com/guides/v2.2/magento-functional-testing-framework/getting-started.html#prepare-environment) - -## Installation -To install the Magento Functional Testing Framework, see [Getting Started](http://devdocs.magento.com/guides/v2.2/magento-functional-testing-framework/getting-started.html) - -## Contributing -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. - -To learn about how to make a contribution, click [here][1]. - -To open an issue, click [here][2]. - -To suggest documentation improvements, click [here][3]. - -[1]: -[2]: -[3]: - -### Labels applied by the MFTF team - -Refer to the tables with descriptions of each label below. These labels are applied by the MFTF development team to community contributed issues and pull requests, to communicate status, impact, or which team is working on it. - -### Pull Request Status - -Label| Description ----|--- -**accept**| The pull request has been accepted and will be merged into mainline code. -**reject**| The pull request has been rejected and will not be merged into mainline code. Possible reasons can include but are not limited to: issue has already been fixed in another code contribution, or there is an issue with the code contribution. -**needsUpdate**| The Magento Team needs additional information from the reporter to properly prioritize and process the pull request. - -### Issue Resolution Status - -Label| Description ----|--- -**acknowledged**| The Magento Team has validated the issue and an internal ticket has been created. -**needsUpdate**| The Magento Team needs additional information from the reporter to properly prioritize and process the issue or pull request. -**cannot reproduce**| The Magento Team has not confirmed that this issue contains the minimum required information to reproduce. -**non-issue**| The Magento Team has not recognised any issue according to provided information. - -### Domains Impacted - -Label| Description ----|--- -**PROD**| Affects the Product team (mostly feature requests or business logic change). -**DOC**| Affects Documentation domain. -**TECH**| Affects Architect Group (mostly to make decisions around technology changes). - -### Type - -Label| Description ----|--- -**bugfix**| The issue or pull request relates to bug fixing. -**enhancement**| The issue or pull request that raises the MFTF to a higher degree (for example new features, optimization, refactoring, etc). - -## License - -Each Magento source file included in this distribution is licensed under APL 3.0 - -Please see LICENSE_APL3.txt for the full text of the APL 3.0 license or contact license@magentocommerce.com for a copy. diff --git a/dev/tests/acceptance/RoboFile.php b/dev/tests/acceptance/RoboFile.php index dd84b4131e502..f36150ad254b5 100644 --- a/dev/tests/acceptance/RoboFile.php +++ b/dev/tests/acceptance/RoboFile.php @@ -15,84 +15,6 @@ class RoboFile extends \Robo\Tasks { use Robo\Task\Base\loadShortcuts; - public function __construct() - { - require 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR . '_bootstrap.php'; - define('VENDOR_BIN_PATH', PROJECT_ROOT . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR); - - } - /** - * Duplicate the Example configuration files used to customize the Project for customization. - * - * @return void - */ - function cloneFiles() - { - $this->_exec('cp -vn .env.example .env'); - $this->_exec('cp -vf codeception.dist.yml codeception.yml'); - $this->_exec('cp -vf tests'. DIRECTORY_SEPARATOR .'functional.suite.dist.yml tests'. DIRECTORY_SEPARATOR .'functional.suite.yml'); - } - - /** - * Finds relative paths between codeception.yml file and MFTF path, and overwrites the default paths. - * - * @return void - */ - private function buildCodeceptionPaths() - { - $relativePathFunc = function ($from, $to) - { - $from = is_dir($from) ? rtrim($from, '\/') . '/' : $from; - $to = is_dir($to) ? rtrim($to, '\/') . '/' : $to; - $from = str_replace('\\', '/', $from); - $to = str_replace('\\', '/', $to); - - $from = explode('/', $from); - $to = explode('/', $to); - $relPath = $to; - - foreach($from as $depth => $dir) { - // find first non-matching dir - if($dir === $to[$depth]) { - // ignore this directory - array_shift($relPath); - } else { - // get number of remaining dirs to $from - $remaining = count($from) - $depth; - if($remaining > 1) { - // add traversals up to first matching dir - $padLength = (count($relPath) + $remaining - 1) * -1; - $relPath = array_pad($relPath, $padLength, '..'); - break; - } else { - $relPath[0] = './' . $relPath[0]; - } - } - } - return implode('/', $relPath); - }; - - //Find travel path from codeception.yml to FW_BP - $configYmlPath = dirname(dirname(TESTS_BP)) . DIRECTORY_SEPARATOR; - $relativePath = call_user_func($relativePathFunc, $configYmlPath, FW_BP); - $configYmlFile = $configYmlPath . "codeception.yml"; - $defaultConfigYmlFile = $configYmlPath . "codeception.dist.yml"; - - if (file_exists($configYmlFile)) { - $ymlContents = file_get_contents($configYmlFile); - } else { - $ymlContents = file_get_contents($defaultConfigYmlFile); - } - $ymlArray = Yaml::parse($ymlContents) ?? []; - if (!array_key_exists("paths", $ymlArray)) { - $ymlArray["paths"] = []; - } - $ymlArray["paths"]["support"] = $relativePath . 'src/Magento/FunctionalTestingFramework'; - $ymlArray["paths"]["envs"] = $relativePath . 'etc/_envs'; - $ymlText = Yaml::dump($ymlArray, 10); - file_put_contents($configYmlFile, $ymlText); - } - /** * Duplicate the Example configuration files for the Project. * Build the Codeception project. @@ -101,9 +23,7 @@ private function buildCodeceptionPaths() */ function buildProject() { - $this->cloneFiles(); - $this->buildCodeceptionPaths(); - $this->_exec(VENDOR_BIN_PATH .'codecept build'); + passthru($this->getBaseCmd("build:project")); } /** @@ -117,93 +37,26 @@ function generateTests(array $tests, $opts = [ 'config' => null, 'force' => false, 'nodes' => null, - 'lines' => 500, + 'lines' => null, 'tests' => null ]) { - require 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR . '_bootstrap.php'; - $testConfiguration = $this->createTestConfiguration($tests, $opts); - - // maintain backwards compatability for devops by not removing the nodes option yet - $lines = $opts['lines']; - - // create our manifest file here - $testManifest = \Magento\FunctionalTestingFramework\Util\Manifest\TestManifestFactory::makeManifest($opts['config'],$testConfiguration['suites']); - \Magento\FunctionalTestingFramework\Util\TestGenerator::getInstance(null, $testConfiguration['tests'])->createAllTestFiles($testManifest); - - if ($opts['config'] == 'parallel') { - $testManifest->createTestGroups($lines); - } - - \Magento\FunctionalTestingFramework\Suite\SuiteGenerator::getInstance()->generateAllSuites($testManifest); - $testManifest->generate(); - - $this->say("Generate Tests Command Run"); - } - - - /** - * Function which builds up a configuration including test and suites for consumption of Magento generation methods. - * - * @param array $tests - * @param array $opts - * @return array - */ - private function createTestConfiguration($tests, $opts) - { - // set our application configuration so we can references the user options in our framework - Magento\FunctionalTestingFramework\Config\MftfApplicationConfig::create( - $opts['force'], - Magento\FunctionalTestingFramework\Config\MftfApplicationConfig::GENERATION_PHASE, - $opts['verbose'] - ); + $baseCmd = $this->getBaseCmd("generate:tests"); - $testConfiguration = []; - $testConfiguration['tests'] = $tests; - $testConfiguration['suites'] = []; - - $testConfiguration = $this->parseTestsConfigJson($opts['tests'], $testConfiguration); - - // if we have references to specific tests, we resolve the test objects and pass them to the config - if (!empty($testConfiguration['tests'])) - { - $testObjects = []; - - foreach ($testConfiguration['tests'] as $test) - { - $testObjects[$test] = Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler::getInstance()->getObject($test); + $mftfArgNames = ['config', 'nodes', 'lines', 'tests']; + // append arguments to the end of the command + foreach ($opts as $argName => $argValue) { + if (in_array($argName, $mftfArgNames) && $argValue !== null) { + $baseCmd .= " --$argName $argValue"; } - - $testConfiguration['tests'] = $testObjects; } - return $testConfiguration; - } - - /** - * Function which takes a json string of potential custom configuration and parses/validates the resulting json - * passed in by the user. The result is a testConfiguration array. - * - * @param string $json - * @param array $testConfiguration - * @return array - */ - private function parseTestsConfigJson($json, $testConfiguration) { - if ($json == null) { - return $testConfiguration; + // use a separate conditional for the force flag (casting bool to string in php is hard) + if ($opts['force']) { + $baseCmd .= ' --force'; } - $jsonTestConfiguration = []; - $testConfigArray = json_decode($json, true); - - // stop execution if we have failed to properly parse any json - if (json_last_error() != JSON_ERROR_NONE) { - throw new \Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException("JSON could not be parsed: " . json_last_error_msg()); - } - - $jsonTestConfiguration['tests'] = $testConfigArray['tests'] ?? null;; - $jsonTestConfiguration['suites'] = $testConfigArray['suites'] ?? null; - return $jsonTestConfiguration; + $this->taskExec($baseCmd)->args($tests)->run(); } /** @@ -218,54 +71,21 @@ function generateSuite(array $args) if (empty($args)) { throw new Exception("Please provide suite name(s) after generate:suite command"); } - - $sg = \Magento\FunctionalTestingFramework\Suite\SuiteGenerator::getInstance(); - - foreach ($args as $arg) { - $sg->generateSuite($arg); - } - } - - /** - * Run all Functional tests. - * - * @return void - */ - function functional() - { - $this->_exec(VENDOR_BIN_PATH . 'codecept run functional'); + $baseCmd = $this->getBaseCmd("generate:suite"); + $this->taskExec($baseCmd)->args($args)->run(); } /** * Run all Tests with the specified @group tag'. * - * @param string $args - * @return void - */ - function group($args = '') - { - $this->taskExec(VENDOR_BIN_PATH . 'codecept run functional --verbose --steps --group')->args($args)->run(); - } - - /** - * Run all Functional tests located under the Directory Path provided. - * - * @param string $args - * @return void - */ - function folder($args = '') - { - $this->taskExec(VENDOR_BIN_PATH . 'codecept run functional')->args($args)->run(); - } - - /** - * Run all Tests marked with the @group tag 'example'. - * + * @param array $args * @return void */ - function example() + function group(array $args) { - $this->_exec(VENDOR_BIN_PATH . 'codecept run --group example'); + $args = array_merge($args, ['-k']); + $baseCmd = $this->getBaseCmd("run:group"); + $this->taskExec($baseCmd)->args($args)->run(); } /** @@ -337,12 +157,15 @@ function allure2Report() } /** - * Run the Pre-Install system check script. + * Private function for returning the formatted command for the passthru to mftf bin execution. * - * @return void + * @param string $command + * @return string */ - function preInstall() + private function getBaseCmd($command) { - $this->_exec('php pre-install.php'); + $this->writeln("\033[01;31m Use of robo will be deprecated with next major release, please use /vendor/bin/mftf $command \033[0m"); + chdir(__DIR__); + return realpath('../../../vendor/bin/mftf') . " $command"; } } diff --git a/dev/tests/acceptance/codeception.dist.yml b/dev/tests/acceptance/codeception.dist.yml deleted file mode 100644 index 683edcb107fd1..0000000000000 --- a/dev/tests/acceptance/codeception.dist.yml +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright © Magento, Inc. All rights reserved. -# See COPYING.txt for license details. -actor: Tester -paths: - tests: tests - log: tests/_output - data: tests/_data - support: "%REPLACED IN BUILD:PROJECT%" - envs: "%REPLACED IN BUILD:PROJECT%" -settings: - bootstrap: _bootstrap.php - colors: true - memory_limit: 1024M -extensions: - enabled: - - Codeception\Extension\RunFailed - - Magento\FunctionalTestingFramework\Extension\TestContextExtension - - Magento\FunctionalTestingFramework\Allure\Adapter\MagentoAllureAdapter - config: - Yandex\Allure\Adapter\AllureAdapter: - deletePreviousResults: true - outputDirectory: allure-results - ignoredAnnotations: - - env - - zephyrId - - useCaseId -params: - - .env -modules: - config: - Db: - dsn: "%DB_DSN%" - user: "%DB_USERNAME%" - password: "%DB_PASSWORD%" - dump: tests/_data/dump.sql \ No newline at end of file diff --git a/dev/tests/acceptance/composer.json b/dev/tests/acceptance/composer.json index 787d2579e03ca..a20176a29c4c8 100755 --- a/dev/tests/acceptance/composer.json +++ b/dev/tests/acceptance/composer.json @@ -9,20 +9,17 @@ "config": { "sort-packages": true }, - "repositories": [ - { - "type": "git", - "url": "git@github.com:magento/magento2-functional-testing-framework.git" - } - ], "require": { "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "~2.2.0" + "codeception/codeception": "~2.3.4", + "consolidation/robo": "^1.0.0", + "vlucas/phpdotenv": "^2.4" }, "autoload": { "psr-4": { "Magento\\": "tests/functional/Magento" - } + }, + "files": ["tests/_bootstrap.php"] }, "prefer-stable": true } diff --git a/dev/tests/acceptance/composer.lock b/dev/tests/acceptance/composer.lock index afe2640b442f1..f8c6bbc137211 100644 --- a/dev/tests/acceptance/composer.lock +++ b/dev/tests/acceptance/composer.lock @@ -4,111 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "9f292f6e226938ad82d1189da7aa4024", + "content-hash": "46ca2d50566f5069daef753664080c5a", "packages": [ - { - "name": "allure-framework/allure-codeception", - "version": "1.2.7", - "source": { - "type": "git", - "url": "https://github.com/allure-framework/allure-codeception.git", - "reference": "48598f4b4603b50b663bfe977260113a40912131" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/allure-framework/allure-codeception/zipball/48598f4b4603b50b663bfe977260113a40912131", - "reference": "48598f4b4603b50b663bfe977260113a40912131", - "shasum": "" - }, - "require": { - "allure-framework/allure-php-api": "~1.1.0", - "codeception/codeception": "~2.1", - "php": ">=5.4.0", - "symfony/filesystem": ">=2.6", - "symfony/finder": ">=2.6" - }, - "type": "library", - "autoload": { - "psr-0": { - "Yandex": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Ivan Krutov", - "email": "vania-pooh@yandex-team.ru", - "role": "Developer" - } - ], - "description": "A Codeception adapter for Allure report.", - "homepage": "http://allure.qatools.ru/", - "keywords": [ - "allure", - "attachments", - "cases", - "codeception", - "report", - "steps", - "testing" - ], - "time": "2018-03-07T11:18:27+00:00" - }, - { - "name": "allure-framework/allure-php-api", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/allure-framework/allure-php-adapter-api.git", - "reference": "a462a0da121681577033e13c123b6cc4e89cdc64" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/allure-framework/allure-php-adapter-api/zipball/a462a0da121681577033e13c123b6cc4e89cdc64", - "reference": "a462a0da121681577033e13c123b6cc4e89cdc64", - "shasum": "" - }, - "require": { - "jms/serializer": ">=0.16.0", - "moontoast/math": ">=1.1.0", - "php": ">=5.4.0", - "phpunit/phpunit": ">=4.0.0", - "ramsey/uuid": ">=3.0.0", - "symfony/http-foundation": ">=2.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Yandex": [ - "src/", - "test/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Ivan Krutov", - "email": "vania-pooh@yandex-team.ru", - "role": "Developer" - } - ], - "description": "PHP API for Allure adapter", - "homepage": "http://allure.qatools.ru/", - "keywords": [ - "allure", - "api", - "php", - "report" - ], - "time": "2016-12-07T12:15:46+00:00" - }, { "name": "behat/gherkin", "version": "v4.4.5", @@ -264,16 +161,16 @@ }, { "name": "codeception/stub", - "version": "1.0.2", + "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/Codeception/Stub.git", - "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99" + "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/95fb7a36b81890dd2e5163e7ab31310df6f1bb99", - "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/681b62348837a5ef07d10d8a226f5bc358cc8805", + "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805", "shasum": "" }, "require": { @@ -293,7 +190,7 @@ "MIT" ], "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-02-18T13:56:56+00:00" + "time": "2018-05-17T09:31:08+00:00" }, { "name": "consolidation/annotated-command", @@ -674,141 +571,6 @@ ], "time": "2017-01-20T21:14:22+00:00" }, - { - "name": "doctrine/annotations", - "version": "v1.6.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", - "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", - "shasum": "" - }, - "require": { - "doctrine/lexer": "1.*", - "php": "^7.1" - }, - "require-dev": { - "doctrine/cache": "1.*", - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "time": "2017-12-06T07:11:42+00:00" - }, - { - "name": "doctrine/collections", - "version": "v1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/collections.git", - "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/a01ee38fcd999f34d9bfbcee59dbda5105449cbf", - "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "doctrine/coding-standard": "~0.1@dev", - "phpunit/phpunit": "^5.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\Collections\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Collections Abstraction library", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "array", - "collections", - "iterator" - ], - "time": "2017-07-22T10:37:32+00:00" - }, { "name": "doctrine/instantiator", "version": "1.1.0", @@ -863,136 +625,41 @@ ], "time": "2017-07-22T11:58:36+00:00" }, - { - "name": "doctrine/lexer", - "version": "v1.0.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\Lexer\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "lexer", - "parser" - ], - "time": "2014-09-09T13:34:57+00:00" - }, - { - "name": "epfremme/swagger-php", - "version": "v2.0.0", - "source": { - "type": "git", - "url": "https://github.com/epfremmer/swagger-php.git", - "reference": "eee28a442b7e6220391ec953d3c9b936354f23bc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/epfremmer/swagger-php/zipball/eee28a442b7e6220391ec953d3c9b936354f23bc", - "reference": "eee28a442b7e6220391ec953d3c9b936354f23bc", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^1.2", - "doctrine/collections": "^1.3", - "jms/serializer": "^1.1", - "php": ">=5.5", - "phpoption/phpoption": "^1.1", - "symfony/yaml": "^2.7|^3.1" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "~4.8|~5.0", - "satooshi/php-coveralls": "^1.0" - }, - "type": "package", - "autoload": { - "psr-4": { - "Epfremme\\Swagger\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Edward Pfremmer", - "email": "epfremme@nerdery.com" - } - ], - "description": "Library for parsing swagger documentation into PHP entities for use in testing and code generation", - "time": "2016-09-26T17:24:17+00:00" - }, { "name": "facebook/webdriver", - "version": "1.5.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/facebook/php-webdriver.git", - "reference": "86b5ca2f67173c9d34340845dd690149c886a605" + "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/86b5ca2f67173c9d34340845dd690149c886a605", - "reference": "86b5ca2f67173c9d34340845dd690149c886a605", + "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/bd8c740097eb9f2fc3735250fc1912bc811a954e", + "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e", "shasum": "" }, "require": { "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", "ext-zip": "*", "php": "^5.6 || ~7.0", "symfony/process": "^2.8 || ^3.1 || ^4.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.0", - "guzzle/guzzle": "^3.4.1", - "php-coveralls/php-coveralls": "^1.0.2", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "php-coveralls/php-coveralls": "^2.0", "php-mock/php-mock-phpunit": "^1.1", "phpunit/phpunit": "^5.7", "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", "squizlabs/php_codesniffer": "^2.6", "symfony/var-dumper": "^3.3 || ^4.0" }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" + }, "type": "library", "extra": { "branch-alias": { @@ -1016,98 +683,7 @@ "selenium", "webdriver" ], - "time": "2017-11-15T11:08:09+00:00" - }, - { - "name": "flow/jsonpath", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/FlowCommunications/JSONPath.git", - "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FlowCommunications/JSONPath/zipball/f0222818d5c938e4ab668ab2e2c079bd51a27112", - "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "peekmo/jsonpath": "dev-master", - "phpunit/phpunit": "^4.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Flow\\JSONPath": "src/", - "Flow\\JSONPath\\Test": "tests/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Stephen Frank", - "email": "stephen@flowsa.com" - } - ], - "description": "JSONPath implementation for parsing, searching and flattening arrays", - "time": "2018-03-04T16:39:47+00:00" - }, - { - "name": "fzaninotto/faker", - "version": "v1.7.1", - "source": { - "type": "git", - "url": "https://github.com/fzaninotto/Faker.git", - "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", - "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "ext-intl": "*", - "phpunit/phpunit": "^4.0 || ^5.0", - "squizlabs/php_codesniffer": "^1.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } - }, - "autoload": { - "psr-4": { - "Faker\\": "src/Faker/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "François Zaninotto" - } - ], - "description": "Faker is a PHP library that generates fake data for you.", - "keywords": [ - "data", - "faker", - "fixtures" - ], - "time": "2017-08-15T16:48:10+00:00" + "time": "2018-05-16T17:37:13+00:00" }, { "name": "grasmash/expander", @@ -1206,16 +782,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.3.2", + "version": "6.3.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90" + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/68d0ea14d5a3f42a20e87632a5f84931e2709c90", - "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", "shasum": "" }, "require": { @@ -1225,7 +801,7 @@ }, "require-dev": { "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", "psr/log": "^1.0" }, "suggest": { @@ -1267,7 +843,7 @@ "rest", "web service" ], - "time": "2018-03-26T16:33:04+00:00" + "time": "2018-04-22T15:46:56+00:00" }, { "name": "guzzlehttp/promises", @@ -1379,181 +955,11 @@ "message", "request", "response", - "stream", - "uri", - "url" - ], - "time": "2017-03-20T17:10:46+00:00" - }, - { - "name": "jms/metadata", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/metadata.git", - "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/6a06970a10e0a532fb52d3959547123b84a3b3ab", - "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "doctrine/cache": "~1.0", - "symfony/cache": "~3.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5.x-dev" - } - }, - "autoload": { - "psr-0": { - "Metadata\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Class/method/property metadata management in PHP", - "keywords": [ - "annotations", - "metadata", - "xml", - "yaml" - ], - "time": "2016-12-05T10:18:33+00:00" - }, - { - "name": "jms/parser-lib", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/parser-lib.git", - "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/parser-lib/zipball/c509473bc1b4866415627af0e1c6cc8ac97fa51d", - "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d", - "shasum": "" - }, - "require": { - "phpoption/phpoption": ">=0.9,<2.0-dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "JMS\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache2" - ], - "description": "A library for easily creating recursive-descent parsers.", - "time": "2012-11-18T18:08:43+00:00" - }, - { - "name": "jms/serializer", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/serializer.git", - "reference": "e7c53477ff55c21d1b1db7d062edc050a24f465f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/e7c53477ff55c21d1b1db7d062edc050a24f465f", - "reference": "e7c53477ff55c21d1b1db7d062edc050a24f465f", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^1.0", - "doctrine/instantiator": "^1.0.3", - "jms/metadata": "~1.1", - "jms/parser-lib": "1.*", - "php": "^5.5|^7.0", - "phpcollection/phpcollection": "~0.1", - "phpoption/phpoption": "^1.1" - }, - "conflict": { - "twig/twig": "<1.12" - }, - "require-dev": { - "doctrine/orm": "~2.1", - "doctrine/phpcr-odm": "^1.3|^2.0", - "ext-pdo_sqlite": "*", - "jackalope/jackalope-doctrine-dbal": "^1.1.5", - "phpunit/phpunit": "^4.8|^5.0", - "propel/propel1": "~1.7", - "psr/container": "^1.0", - "symfony/dependency-injection": "^2.7|^3.3|^4.0", - "symfony/expression-language": "^2.6|^3.0", - "symfony/filesystem": "^2.1", - "symfony/form": "~2.1|^3.0", - "symfony/translation": "^2.1|^3.0", - "symfony/validator": "^2.2|^3.0", - "symfony/yaml": "^2.1|^3.0", - "twig/twig": "~1.12|~2.0" - }, - "suggest": { - "doctrine/cache": "Required if you like to use cache functionality.", - "doctrine/collections": "Required if you like to use doctrine collection types as ArrayCollection.", - "symfony/yaml": "Required if you'd like to serialize data to YAML format." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.11-dev" - } - }, - "autoload": { - "psr-0": { - "JMS\\Serializer": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - }, - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.", - "homepage": "http://jmsyst.com/libs/serializer", - "keywords": [ - "deserialization", - "jaxb", - "json", - "serialization", - "xml" + "stream", + "uri", + "url" ], - "time": "2018-02-04T17:48:54+00:00" + "time": "2017-03-20T17:10:46+00:00" }, { "name": "league/container", @@ -1620,194 +1026,30 @@ ], "time": "2017-05-10T09:20:27+00:00" }, - { - "name": "magento/magento2-functional-testing-framework", - "version": "2.2.0", - "source": { - "type": "git", - "url": "git@github.com:magento/magento2-functional-testing-framework.git", - "reference": "4dd196d745bf836cbf0c5904a1df6dd241124309" - }, - "require": { - "allure-framework/allure-codeception": "~1.2.6", - "codeception/codeception": "~2.3.4", - "consolidation/robo": "^1.0.0", - "epfremme/swagger-php": "^2.0", - "flow/jsonpath": ">0.2", - "fzaninotto/faker": "^1.6", - "mustache/mustache": "~2.5", - "php": "7.0.2|7.0.4|~7.0.6|~7.1.0|~7.2.0", - "symfony/process": "^2.8 || ^3.1 || ^4.0", - "vlucas/phpdotenv": "^2.4" - }, - "require-dev": { - "brainmaestro/composer-git-hooks": "^2.3", - "codacy/coverage": "^1.4", - "codeception/aspect-mock": "^2.0", - "goaop/framework": "2.1.2", - "php-coveralls/php-coveralls": "^1.0", - "phpmd/phpmd": "^2.6.0", - "rregeer/phpunit-coverage-check": "^0.1.4", - "sebastian/phpcpd": "~3.0", - "squizlabs/php_codesniffer": "1.5.3", - "symfony/stopwatch": "~3.4.6" - }, - "bin": [ - "bin/mftf" - ], - "type": "library", - "extra": { - "hooks": { - "pre-push": "bin/all-checks" - } - }, - "autoload": { - "psr-4": { - "Magento\\FunctionalTestingFramework\\": "src/Magento/FunctionalTestingFramework", - "MFTF\\": "dev/tests/functional/MFTF" - } - }, - "autoload-dev": { - "psr-4": { - "tests\\unit\\": "dev/tests/unit" - } - }, - "scripts": { - "tests": [ - "bin/phpunit-checks" - ], - "static": [ - "bin/static-checks" - ] - }, - "license": [ - "AGPL-3.0" - ], - "description": "Magento2 Functional Testing Framework", - "keywords": [ - "automation", - "functional", - "magento", - "testing" - ], - "time": "2018-04-24T01:46:09+00:00" - }, - { - "name": "moontoast/math", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/ramsey/moontoast-math.git", - "reference": "c2792a25df5cad4ff3d760dd37078fc5b6fccc79" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ramsey/moontoast-math/zipball/c2792a25df5cad4ff3d760dd37078fc5b6fccc79", - "reference": "c2792a25df5cad4ff3d760dd37078fc5b6fccc79", - "shasum": "" - }, - "require": { - "ext-bcmath": "*", - "php": ">=5.3.3" - }, - "require-dev": { - "jakub-onderka/php-parallel-lint": "^0.9.0", - "phpunit/phpunit": "^4.7|>=5.0 <5.4", - "satooshi/php-coveralls": "^0.6.1", - "squizlabs/php_codesniffer": "^2.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Moontoast\\Math\\": "src/Moontoast/Math/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" - } - ], - "description": "A mathematics library, providing functionality for large numbers", - "homepage": "https://github.com/ramsey/moontoast-math", - "keywords": [ - "bcmath", - "math" - ], - "time": "2017-02-16T16:54:46+00:00" - }, - { - "name": "mustache/mustache", - "version": "v2.12.0", - "source": { - "type": "git", - "url": "https://github.com/bobthecow/mustache.php.git", - "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/fe8fe72e9d580591854de404cc59a1b83ca4d19e", - "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e", - "shasum": "" - }, - "require": { - "php": ">=5.2.4" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~1.11", - "phpunit/phpunit": "~3.7|~4.0|~5.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Mustache": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" - } - ], - "description": "A Mustache implementation in PHP.", - "homepage": "https://github.com/bobthecow/mustache.php", - "keywords": [ - "mustache", - "templating" - ], - "time": "2017-07-11T12:54:05+00:00" - }, { "name": "myclabs/deep-copy", - "version": "1.7.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + "reference": "478465659fd987669df0bd8a9bf22a8710e5f1b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/478465659fd987669df0bd8a9bf22a8710e5f1b6", + "reference": "478465659fd987669df0bd8a9bf22a8710e5f1b6", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" + "phpunit/phpunit": "^7.1" }, "type": "library", "autoload": { @@ -1830,55 +1072,7 @@ "object", "object graph" ], - "time": "2017-10-19T19:58:43+00:00" - }, - { - "name": "paragonie/random_compat", - "version": "v2.0.12", - "source": { - "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/258c89a6b97de7dfaf5b8c7607d0478e236b04fb", - "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb", - "shasum": "" - }, - "require": { - "php": ">=5.2.0" - }, - "require-dev": { - "phpunit/phpunit": "4.*|5.*" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - }, - "type": "library", - "autoload": { - "files": [ - "lib/random.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "pseudorandom", - "random" - ], - "time": "2018-04-04T21:24:14+00:00" + "time": "2018-05-29T17:25:09+00:00" }, { "name": "phar-io/manifest", @@ -1982,54 +1176,6 @@ "description": "Library for handling version information and constraints", "time": "2017-03-05T17:38:23+00:00" }, - { - "name": "phpcollection/phpcollection", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/php-collection.git", - "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-collection/zipball/f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", - "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", - "shasum": "" - }, - "require": { - "phpoption/phpoption": "1.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.4-dev" - } - }, - "autoload": { - "psr-0": { - "PhpCollection": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache2" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "General-Purpose Collection Library for PHP", - "keywords": [ - "collection", - "list", - "map", - "sequence", - "set" - ], - "time": "2015-05-17T12:39:23+00:00" - }, { "name": "phpdocumentor/reflection-common", "version": "1.0.1", @@ -2182,56 +1328,6 @@ ], "time": "2017-07-14T14:27:02+00:00" }, - { - "name": "phpoption/phpoption", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "4.7.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-0": { - "PhpOption\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache2" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Option Type for PHP", - "keywords": [ - "language", - "option", - "php", - "type" - ], - "time": "2015-07-25T16:39:46+00:00" - }, { "name": "phpspec/prophecy", "version": "1.7.6", @@ -2630,16 +1726,16 @@ }, { "name": "phpunit/phpunit-mock-objects", - "version": "5.0.6", + "version": "5.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf" + "reference": "3eaf040f20154d27d6da59ca2c6e28ac8fd56dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/33fd41a76e746b8fa96d00b49a23dadfa8334cdf", - "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3eaf040f20154d27d6da59ca2c6e28ac8fd56dce", + "reference": "3eaf040f20154d27d6da59ca2c6e28ac8fd56dce", "shasum": "" }, "require": { @@ -2685,7 +1781,7 @@ "mock", "xunit" ], - "time": "2018-01-06T05:45:45+00:00" + "time": "2018-05-29T13:50:43+00:00" }, { "name": "psr/container", @@ -2833,86 +1929,6 @@ ], "time": "2016-10-10T12:19:37+00:00" }, - { - "name": "ramsey/uuid", - "version": "3.7.3", - "source": { - "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "44abcdad877d9a46685a3a4d221e3b2c4b87cb76" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/44abcdad877d9a46685a3a4d221e3b2c4b87cb76", - "reference": "44abcdad877d9a46685a3a4d221e3b2c4b87cb76", - "shasum": "" - }, - "require": { - "paragonie/random_compat": "^1.0|^2.0", - "php": "^5.4 || ^7.0" - }, - "replace": { - "rhumsaa/uuid": "self.version" - }, - "require-dev": { - "codeception/aspect-mock": "^1.0 | ~2.0.0", - "doctrine/annotations": "~1.2.0", - "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ^2.1", - "ircmaxell/random-lib": "^1.1", - "jakub-onderka/php-parallel-lint": "^0.9.0", - "mockery/mockery": "^0.9.9", - "moontoast/math": "^1.1", - "php-mock/php-mock-phpunit": "^0.3|^1.1", - "phpunit/phpunit": "^4.7|^5.0", - "squizlabs/php_codesniffer": "^2.3" - }, - "suggest": { - "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", - "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", - "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", - "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Ramsey\\Uuid\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marijn Huizendveld", - "email": "marijn.huizendveld@gmail.com" - }, - { - "name": "Thibaud Fabre", - "email": "thibaud@aztech.io" - }, - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" - } - ], - "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", - "homepage": "https://github.com/ramsey/uuid", - "keywords": [ - "guid", - "identifier", - "uuid" - ], - "time": "2018-01-20T00:28:24+00:00" - }, { "name": "sebastian/code-unit-reverse-lookup", "version": "1.0.1", @@ -3474,16 +2490,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.0.8", + "version": "v4.1.0", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "c43bfa0182363b3fd64331b5e64e467349ff4670" + "reference": "16355a5d0f1499c77efee5ff68d8ea61624d4da1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c43bfa0182363b3fd64331b5e64e467349ff4670", - "reference": "c43bfa0182363b3fd64331b5e64e467349ff4670", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/16355a5d0f1499c77efee5ff68d8ea61624d4da1", + "reference": "16355a5d0f1499c77efee5ff68d8ea61624d4da1", "shasum": "" }, "require": { @@ -3500,7 +2516,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3527,20 +2543,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2018-03-19T22:35:49+00:00" + "time": "2018-04-06T10:52:03+00:00" }, { "name": "symfony/console", - "version": "v4.0.8", + "version": "v4.0.9", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "aad9a6fe47319f22748fd764f52d3a7ca6fa6b64" + "reference": "3e820bc2c520a87ca209ad8fa961c97f42e0b4ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/aad9a6fe47319f22748fd764f52d3a7ca6fa6b64", - "reference": "aad9a6fe47319f22748fd764f52d3a7ca6fa6b64", + "url": "https://api.github.com/repos/symfony/console/zipball/3e820bc2c520a87ca209ad8fa961c97f42e0b4ae", + "reference": "3e820bc2c520a87ca209ad8fa961c97f42e0b4ae", "shasum": "" }, "require": { @@ -3560,7 +2576,7 @@ "symfony/process": "~3.4|~4.0" }, "suggest": { - "psr/log": "For using the console logger", + "psr/log-implementation": "For using the console logger", "symfony/event-dispatcher": "", "symfony/lock": "", "symfony/process": "" @@ -3595,20 +2611,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-04-03T05:24:00+00:00" + "time": "2018-04-30T01:23:47+00:00" }, { "name": "symfony/css-selector", - "version": "v4.0.8", + "version": "v4.1.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "03f965583147957f1ecbad7ea1c9d6fd5e525ec2" + "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/03f965583147957f1ecbad7ea1c9d6fd5e525ec2", - "reference": "03f965583147957f1ecbad7ea1c9d6fd5e525ec2", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/03ac71606ecb0b0ce792faa17d74cc32c2949ef4", + "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4", "shasum": "" }, "require": { @@ -3617,7 +2633,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3648,24 +2664,25 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2018-03-19T22:35:49+00:00" + "time": "2018-05-30T07:26:09+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.0.8", + "version": "v4.1.0", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "d6c04c7532535b5e0b63db45b543cd60818e0fbc" + "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/d6c04c7532535b5e0b63db45b543cd60818e0fbc", - "reference": "d6c04c7532535b5e0b63db45b543cd60818e0fbc", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/3350cacf151b48d903114ab8f7a4ccb23e07e10a", + "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a", "shasum": "" }, "require": { "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { @@ -3677,7 +2694,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3704,11 +2721,11 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-03-19T22:35:49+00:00" + "time": "2018-05-01T23:02:13+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.0.8", + "version": "v4.0.9", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -3771,7 +2788,7 @@ }, { "name": "symfony/filesystem", - "version": "v4.0.8", + "version": "v4.0.9", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -3820,7 +2837,7 @@ }, { "name": "symfony/finder", - "version": "v4.0.8", + "version": "v4.0.9", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -3868,38 +2885,34 @@ "time": "2018-04-04T05:10:37+00:00" }, { - "name": "symfony/http-foundation", - "version": "v4.0.8", + "name": "symfony/polyfill-ctype", + "version": "v1.8.0", "source": { "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "d0864a82e5891ab61d31eecbaa48bed5a09b8e6c" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d0864a82e5891ab61d31eecbaa48bed5a09b8e6c", - "reference": "d0864a82e5891ab61d31eecbaa48bed5a09b8e6c", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/7cc359f1b7b80fc25ed7796be7d96adc9b354bae", + "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.1" - }, - "require-dev": { - "symfony/expression-language": "~3.4|~4.0" + "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "1.8-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\HttpFoundation\\": "" + "Symfony\\Polyfill\\Ctype\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3907,31 +2920,37 @@ "MIT" ], "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" } ], - "description": "Symfony HttpFoundation Component", + "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", - "time": "2018-04-03T05:24:00+00:00" + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2018-04-30T19:57:29+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.7.0", + "version": "v1.8.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" + "reference": "3296adf6a6454a050679cde90f95350ad604b171" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", + "reference": "3296adf6a6454a050679cde90f95350ad604b171", "shasum": "" }, "require": { @@ -3943,7 +2962,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "1.8-dev" } }, "autoload": { @@ -3977,11 +2996,11 @@ "portable", "shim" ], - "time": "2018-01-30T19:27:44+00:00" + "time": "2018-04-26T10:06:28+00:00" }, { "name": "symfony/process", - "version": "v4.0.8", + "version": "v4.0.9", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -4030,20 +3049,20 @@ }, { "name": "symfony/yaml", - "version": "v3.4.8", + "version": "v4.0.9", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "a42f9da85c7c38d59f5e53f076fe81a091f894d0" + "reference": "275ad099e4cbe612a2acbca14a16dd1c5311324d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/a42f9da85c7c38d59f5e53f076fe81a091f894d0", - "reference": "a42f9da85c7c38d59f5e53f076fe81a091f894d0", + "url": "https://api.github.com/repos/symfony/yaml/zipball/275ad099e4cbe612a2acbca14a16dd1c5311324d", + "reference": "275ad099e4cbe612a2acbca14a16dd1c5311324d", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "conflict": { "symfony/console": "<3.4" @@ -4057,7 +3076,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -4084,7 +3103,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-04-03T05:14:20+00:00" + "time": "2018-04-08T08:49:08+00:00" }, { "name": "theseer/tokenizer", @@ -4233,6 +3252,8 @@ "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, - "platform": [], + "platform": { + "php": "~7.1.3||~7.2.0" + }, "platform-dev": [] } diff --git a/dev/tests/acceptance/pre-install.php b/dev/tests/acceptance/pre-install.php deleted file mode 100644 index 4021dc1094948..0000000000000 --- a/dev/tests/acceptance/pre-install.php +++ /dev/null @@ -1,395 +0,0 @@ -foreground_colors['black'] = '0;30'; - $this->foreground_colors['dark_gray'] = '1;30'; - $this->foreground_colors['blue'] = '0;34'; - $this->foreground_colors['light_blue'] = '1;34'; - $this->foreground_colors['green'] = '0;32'; - $this->foreground_colors['light_green'] = '1;32'; - $this->foreground_colors['cyan'] = '0;36'; - $this->foreground_colors['light_cyan'] = '1;36'; - $this->foreground_colors['red'] = '0;31'; - $this->foreground_colors['light_red'] = '1;31'; - $this->foreground_colors['purple'] = '0;35'; - $this->foreground_colors['light_purple'] = '1;35'; - $this->foreground_colors['brown'] = '0;33'; - $this->foreground_colors['yellow'] = '1;33'; - $this->foreground_colors['light_gray'] = '0;37'; - $this->foreground_colors['white'] = '1;37'; - - $this->background_colors['black'] = '40'; - $this->background_colors['red'] = '41'; - $this->background_colors['green'] = '42'; - $this->background_colors['yellow'] = '43'; - $this->background_colors['blue'] = '44'; - $this->background_colors['magenta'] = '45'; - $this->background_colors['cyan'] = '46'; - $this->background_colors['light_gray'] = '47'; - } - - /** - * Returns colored string - * - * @param $string - * @param null $foreground_color - * @param null $background_color - * @return string - */ - public function getColoredString($string, $foreground_color = null, $background_color = null) { - $colored_string = ""; - - // Check if given foreground color found - if (isset($this->foreground_colors[$foreground_color])) { - $colored_string .= "\033[" . $this->foreground_colors[$foreground_color] . "m"; - } - // Check if given background color found - if (isset($this->background_colors[$background_color])) { - $colored_string .= "\033[" . $this->background_colors[$background_color] . "m"; - } - - // Add string and end coloring - $colored_string .= $string . "\033[0m"; - - return $colored_string; - } - - /** - * Returns all foreground color names - * - * @return array - */ - public function getForegroundColors() { - return array_keys($this->foreground_colors); - } - - /** - * Returns all background color names - * - * @return array - */ - public function getBackgroundColors() { - return array_keys($this->background_colors); - } -} - -/** - * @SuppressWarnings(PHPMD) - */ -class PreInstallCheck { - private $installedViaBrew = false; - private $filePath = ''; - private $seleniumJarVersion = ''; - - private $phpWebsite = 'http://php.net/manual/en/install.php'; - private $composerWebsite = 'https://getcomposer.org/download/'; - private $javaWebsite = 'https://www.java.com/en/download/'; - private $allureCliWebsite = 'https://docs.qameta.io/allure/latest/#_installing_a_commandline'; - private $seleniumWebsite = 'http://www.seleniumhq.org/download/'; - private $chromeDriverWebsite = 'https://sites.google.com/a/chromium.org/chromedriver/downloads'; - private $geckoDriverWebsite = 'https://github.com/mozilla/geckodriver'; - private $phantomJsWebsite = 'http://phantomjs.org/'; - - private $phpSupportedVersion = '7.1.0'; - private $composerSupportedVersion = '1.3.0'; - private $javaSupportedVersion = '1.8.0'; - private $allureCliSupportedVersion = '2.3.0'; - private $seleniumSupportedVersion = '3.6.0'; - private $chromeDriverSupportedVersion = '2.33.0'; - private $geckoDriverSupportedVersion = '0.19.0'; - private $phantomJsSupportedVersion = '2.1.0'; - - private $getPhpVersion; - private $getComposerVersion; - private $getJavaVersion; - private $getAllureCliVersion; - private $getSeleniumVersion; - private $getChromeDriverVersion; - private $getGeckoDriverVersion; - private $getPhantomJsVersion; - - private $phpVersion; - private $composerVersion; - private $javaVersion; - private $allureCliVersion; - private $seleniumVersion; - private $chromeDriverVersion; - private $geckoDriverVersion; - private $phantomJsVersion; - - private $phpStatus; - private $composerStatus; - private $javaStatus; - private $allureCliStatus; - private $seleniumStatus; - private $chromeDriverStatus; - private $geckoDriverStatus; - private $phantomJsStatus; - - function __construct() { - $this->didYouInstallViaBrew(); - - $this->getPhpVersion = shell_exec('php --version'); - $this->getComposerVersion = shell_exec('composer --version'); - $this->getJavaVersion = shell_exec("java -version 2>&1"); - $this->getAllureCliVersion = shell_exec('allure --version'); - $this->getSeleniumVersion = $this->getSeleniumVersion(); - $this->getChromeDriverVersion = $this->getChromeDriverVersion(); - $this->getGeckoDriverVersion = shell_exec('geckodriver --version'); - $this->getPhantomJsVersion = $this->getPhantomJsVersion(); - - $this->phpVersion = $this->parseVersion($this->getPhpVersion); - $this->composerVersion = $this->parseVersion($this->getComposerVersion); - $this->javaVersion = $this->parseJavaVersion($this->getJavaVersion); - $this->allureCliVersion = $this->parseVersion($this->getAllureCliVersion); - $this->seleniumVersion = $this->parseVersion($this->getSeleniumVersion); - $this->chromeDriverVersion = $this->parseVersion($this->getChromeDriverVersion); - $this->geckoDriverVersion = $this->parseVersion($this->getGeckoDriverVersion); - $this->phantomJsVersion = $this->parseVersion($this->getPhantomJsVersion); - - // String of null Versions - For Testing -// $this->phpVersion = null; -// $this->composerVersion = null; -// $this->javaVersion = null; -// $this->allureCliVersion = null; -// $this->seleniumVersion = null; -// $this->chromeDriverVersion = null; -// $this->geckoDriverVersion = null; -// $this->phantomJsVersion = null; - - // String of invalid Versions - For Testing -// $this->phpVersion = '7.0.0'; -// $this->composerVersion = '1.0.0'; -// $this->javaVersion = '1.0.0'; -// $this->allureCliVersion = '2.0.0'; -// $this->seleniumVersion = '3.0.0'; -// $this->chromeDriverVersion = '2.0.0'; -// $this->geckoDriverVersion = '0.0.0'; -// $this->phantomJsVersion = '2.0.0'; - - $this->phpStatus = $this->verifyVersion('PHP', $this->phpVersion, $this->phpSupportedVersion, $this->phpWebsite); - $this->composerStatus = $this->verifyVersion('Composer', $this->composerVersion, $this->composerSupportedVersion, $this->composerWebsite); - $this->javaStatus = $this->verifyVersion('Java', $this->javaVersion, $this->javaSupportedVersion, $this->javaWebsite); - $this->allureCliStatus = $this->verifyVersion('Allure CLI', $this->allureCliVersion, $this->allureCliSupportedVersion, $this->allureCliWebsite); - $this->seleniumStatus = $this->verifyVersion('Selenium Standalone Server', $this->seleniumVersion, $this->seleniumSupportedVersion, $this->seleniumWebsite); - $this->chromeDriverStatus = $this->verifyVersion('ChromeDriver', $this->chromeDriverVersion, $this->chromeDriverSupportedVersion, $this->chromeDriverWebsite); - $this->geckoDriverStatus = $this->verifyVersion('GeckoDriver', $this->geckoDriverVersion, $this->geckoDriverSupportedVersion, $this->geckoDriverWebsite); - $this->phantomJsStatus = $this->verifyVersion('PhantomJS', $this->phantomJsVersion, $this->phantomJsSupportedVersion, $this->phantomJsWebsite); - - ECHO "\n"; - $mask = "|%-13.13s |%18.18s |%18.18s |%-23.23s |\n"; - printf("---------------------------------------------------------------------------------\n"); - printf($mask, ' Software', 'Supported Version', 'Installed Version', ' Status'); - printf("---------------------------------------------------------------------------------\n"); - printf($mask, ' PHP', $this->phpSupportedVersion . '+', $this->phpVersion, ' ' . $this->phpStatus); - printf($mask, ' Composer', $this->composerSupportedVersion . '+', $this->composerVersion, ' ' . $this->composerStatus); - printf($mask, ' Java', $this->javaSupportedVersion . '+', $this->javaVersion, ' ' . $this->javaStatus); - printf($mask, ' Allure CLI', $this->allureCliSupportedVersion . '+', $this->allureCliVersion, ' ' . $this->allureCliStatus); - printf($mask, ' Selenium', $this->seleniumSupportedVersion . '+', $this->seleniumVersion, ' ' . $this->seleniumStatus); - printf($mask, ' ChromeDriver', $this->chromeDriverSupportedVersion . '+', $this->chromeDriverVersion, ' ' . $this->chromeDriverStatus); - printf($mask, ' GeckoDriver', $this->geckoDriverSupportedVersion . '+', $this->geckoDriverVersion, ' ' . $this->geckoDriverStatus); - printf($mask, ' PhantomJS', $this->phantomJsSupportedVersion . '+', $this->phantomJsVersion, ' ' . $this->phantomJsStatus); - printf("---------------------------------------------------------------------------------\n"); - } - - /** - * Ask if they installed the Browser Drivers via Brew. - * Brew installs things globally making them easier for us to access. - */ - public function didYouInstallViaBrew() - { - ECHO "Did you install Selenium Server, ChromeDriver, GeckoDriver and PhantomJS using Brew? (y/n) "; - $handle1 = fopen ("php://stdin","r"); - $line1 = fgets($handle1); - if (trim($line1) != 'y') { - ECHO "Where did you save the files? (ex /Users/first_last/Automation/) "; - $handle2 = fopen ("php://stdin","r"); - $this->filePath = fgets($handle2); - fclose($handle2); - - ECHO "Which selenium-server-standalone-X.X.X.jar file did you download? (ex 3.6.0) "; - $handle3 = fopen ("php://stdin","r"); - $this->seleniumJarVersion = fgets($handle3); - fclose($handle3); - fclose($handle1); - ECHO "\n"; - } else { - $this->installedViaBrew = true; - fclose($handle1); - ECHO "\n"; - } - } - - /** - * Parse the string that is returned for the Version number only. - * - * @param $stdout - * @return null - */ - public function parseVersion($stdout) - { - preg_match("/\d+(?:\.\d+)+/", $stdout, $matches); - - if (!is_null($matches) && isset($matches[0])) { - return $matches[0]; - } else { - return null; - } - } - - /** - * Parse the string that is returned for the Version number only. - * The message Java returns differs from the others hence the separate function. - * - * @param $stdout - * @return null - */ - public function parseJavaVersion($stdout) - { - preg_match('/\"(.+?)\"/', $stdout, $output_array); - - if (!is_null($output_array)) { - return $output_array[1]; - } else { - return null; - } - } - - /** - * Get the Selenium Server version based on how it was installed. - * - * @return string - */ - public function getSeleniumVersion() - { - $this->installedViaBrew; - $this->filePath; - $this->seleniumJarVersion; - - if ($this->installedViaBrew) { - return shell_exec('selenium-server --version'); - } else { - $command = sprintf('java -jar %s/selenium-server-standalone-%s.jar --version', $this->filePath, $this->seleniumJarVersion); - $command = str_replace(array("\r", "\n"), '', $command) . "\n"; - return shell_exec($command); - } - } - - /** - * Get the ChromeDriver version based on how it was installed. - * - * @return string - */ - public function getChromeDriverVersion() - { - $this->installedViaBrew; - $this->filePath; - - if ($this->installedViaBrew) { - return shell_exec('chromedriver --version'); - } else { - $command = sprintf('%s/chromedriver --version', $this->filePath); - $command = str_replace(array("\r", "\n"), '', $command) . "\n"; - return shell_exec($command); - } - } - - /** - * Get the PhantomJS version based on how it was installed. - * - * @return string - */ - public function getPhantomJsVersion() - { - $this->installedViaBrew; - $this->filePath; - - if ($this->installedViaBrew) { - return shell_exec('phantomjs --version'); - } else { - $command = sprintf('%s/phantomjs --version', $this->filePath); - $command = str_replace(array("\r", "\n"), '', $command) . "\n"; - return shell_exec($command); - } - } - - /** - * Print a "Valid Version Detected" message in color. - * - * @param $softwareName - */ - public function printValidVersion($softwareName) - { - $colors = new CliColors(); - $string = sprintf("%s detected. Version is supported!", $softwareName); - ECHO $colors->getColoredString($string, "black", "green") . "\n"; - } - - /** - * Print a "Upgraded Version Needed" message in color. - * - * @param $softwareName - * @param $supportedVersion - * @param $website - */ - public function printUpgradeVersion($softwareName, $supportedVersion, $website) - { - $colors = new CliColors(); - $string = sprintf("Unsupported version of %s detected. Please upgrade to v%s+: %s", $softwareName, $supportedVersion, $website); - ECHO $colors->getColoredString($string, "black", "yellow") . "\n"; - } - - /** - * Print a "Not Installed. Install Required." message in color. - * - * @param $softwareName - * @param $supportedVersion - * @param $website - */ - public function printNoInstalledVersion($softwareName, $supportedVersion, $website) - { - $colors = new CliColors(); - $string = sprintf("%s not detected. Please install v%s+: %s", $softwareName, $supportedVersion, $website); - ECHO $colors->getColoredString($string, "black", "red") . "\n"; - } - - /** - * Verify that the versions. - * Print the correct status message. - * - * @param $softwareName - * @param $installedVersion - * @param $supportedVersion - * @param $website - * @return string - */ - public function verifyVersion($softwareName, $installedVersion, $supportedVersion, $website) - { - if (is_null($installedVersion)) { - $this->printNoInstalledVersion($softwareName, $supportedVersion, $website); - return 'Installation Required!'; - } else if ($installedVersion >= $supportedVersion) { - $this->printValidVersion($softwareName); - return 'Correct Version!'; - } else { - $this->printUpgradeVersion($softwareName, $supportedVersion, $website); - return 'Upgrade Required!'; - } - } -} - -$preCheck = new PreInstallCheck(); -// @codingStandardsIgnoreEnd \ No newline at end of file diff --git a/dev/tests/acceptance/tests/_bootstrap.php b/dev/tests/acceptance/tests/_bootstrap.php index d15481d18098c..ee39fc1a09da6 100644 --- a/dev/tests/acceptance/tests/_bootstrap.php +++ b/dev/tests/acceptance/tests/_bootstrap.php @@ -1,110 +1,5 @@ getPrefixesPsr4()[$FW_PACKAGE_NAME][0] ?? null; -if ($COMPOSER_FW_FULL_PREFIX === null) { - throw new Exception( - "You must have the magento/magento2-functional-testing-framework - installed to be able to generate tests." - ); -} -$FW_PATH = substr( - $COMPOSER_FW_FULL_PREFIX, - 0, - strpos($COMPOSER_FW_FULL_PREFIX, "/src/Magento/FunctionalTestingFramework") -); - -// Find tests path -$COMPOSER_TEST_FULL_PREFIX = $loader->getPrefixesPsr4()[$TESTS_PACKAGE_NAME][0] ?? null; -if ($COMPOSER_TEST_FULL_PREFIX === null) { - $TEST_PATH = __DIR__ . "/functional"; -} else { - // Can't determine what to trim; we don't know the package name/structure yet - $TEST_PATH = $COMPOSER_TEST_FULL_PREFIX; -} - -// We register "Magento\\" to "tests/functional/Magento" for our own class loading, need to try and find a -// prefix that isn't that one. -$COMPOSER_MAGENTO_PREFIXES = $loader->getPrefixesPsr4()[$MAGENTO_PACKAGE_NAME]; -$COMPOSER_MAGENTO_FULL_PREFIX = null; -foreach ($COMPOSER_MAGENTO_PREFIXES as $path) { - if (strpos($path, "tests/functional/Magento") === 0) { - $COMPOSER_MAGENTO_FULL_PREFIX = $path; - } -} -if ($COMPOSER_MAGENTO_FULL_PREFIX === null) { - $MAGENTO_PATH = dirname(__DIR__ . "/../../../../../"); -} else { - $MAGENTO_PATH = substr( - $COMPOSER_MAGENTO_FULL_PREFIX, - 0, - strpos($COMPOSER_MAGENTO_FULL_PREFIX, "/app/code/Magento") - ); -} - -$RELATIVE_TESTS_MODULE_PATH = '/Magento/FunctionalTest'; - -defined('MAGENTO_BP') || define('MAGENTO_BP', realpath($MAGENTO_PATH)); - -//Load constants from .env file -if (file_exists(MAGENTO_BP . '/dev/tests/acceptance/.env')) { - $env = new \Dotenv\Loader(MAGENTO_BP . '/dev/tests/acceptance/.env'); - $env->load(); - - if (array_key_exists('TESTS_MODULE_PATH', $_ENV) xor array_key_exists('TESTS_BP', $_ENV)) { - throw new Exception( - 'You must define both parameters TESTS_BP and TESTS_MODULE_PATH or neither parameter' - ); - } - - foreach ($_ENV as $key => $var) { - defined($key) || define($key, $var); - } - - defined('MAGENTO_CLI_COMMAND_PATH') || define( - 'MAGENTO_CLI_COMMAND_PATH', - 'dev/tests/acceptance/utils/command.php' - ); - $env->setEnvironmentVariable('MAGENTO_CLI_COMMAND_PATH', MAGENTO_CLI_COMMAND_PATH); - - defined('MAGENTO_CLI_COMMAND_PARAMETER') || define('MAGENTO_CLI_COMMAND_PARAMETER', 'command'); - $env->setEnvironmentVariable('MAGENTO_CLI_COMMAND_PARAMETER', MAGENTO_CLI_COMMAND_PARAMETER); -} - -defined('FW_BP') || define('FW_BP', realpath($FW_PATH)); -defined('TESTS_BP') || define('TESTS_BP', realpath($TEST_PATH)); -defined('TESTS_MODULE_PATH') || define( - 'TESTS_MODULE_PATH', - realpath($TEST_PATH . $RELATIVE_TESTS_MODULE_PATH) -); - -// add the debug flag here -$debug_mode = $_ENV['MFTF_DEBUG'] ?? false; -if (!(bool)$debug_mode && extension_loaded('xdebug')) { - xdebug_disable(); -} +//TODO remove this file once MFTF is fully decoupled from Magento +// Need to load in the root level autload file +require_once realpath(dirname(__DIR__) . "/../../../vendor/autoload.php"); diff --git a/dev/tests/acceptance/tests/functional.suite.dist.yml b/dev/tests/acceptance/tests/functional.suite.dist.yml deleted file mode 100644 index e01c46ea7c649..0000000000000 --- a/dev/tests/acceptance/tests/functional.suite.dist.yml +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright © Magento, Inc. All rights reserved. -# See COPYING.txt for license details. - -# Codeception Test Suite Configuration -# -# Suite for acceptance tests. -# Perform tests in browser using the WebDriver or PhpBrowser. -# If you need both WebDriver and PHPBrowser tests - create a separate suite. - -class_name: AcceptanceTester -namespace: Magento\FunctionalTestingFramework -modules: - enabled: - - \Magento\FunctionalTestingFramework\Module\MagentoWebDriver - - \Magento\FunctionalTestingFramework\Helper\Acceptance - - \Magento\FunctionalTestingFramework\Helper\MagentoFakerData - - \Magento\FunctionalTestingFramework\Module\MagentoRestDriver: - url: "%MAGENTO_BASE_URL%/rest/default/V1/" - username: "%MAGENTO_ADMIN_USERNAME%" - password: "%MAGENTO_ADMIN_PASSWORD%" - depends: PhpBrowser - part: Json - - \Magento\FunctionalTestingFramework\Module\MagentoSequence - - \Magento\FunctionalTestingFramework\Module\MagentoAssert - - Asserts - config: - \Magento\FunctionalTestingFramework\Module\MagentoWebDriver: - url: "%MAGENTO_BASE_URL%" - backend_name: "%MAGENTO_BACKEND_NAME%" - browser: 'chrome' - window_size: maximize - username: "%MAGENTO_ADMIN_USERNAME%" - password: "%MAGENTO_ADMIN_PASSWORD%" - pageload_timeout: 30 - host: %SELENIUM_HOST% - port: %SELENIUM_PORT% - protocol: %SELENIUM_PROTOCOL% - path: %SELENIUM_PATH% - capabilities: - chromeOptions: - args: ["--start-maximized", "--disable-extensions", "--enable-automation"] - diff --git a/dev/tests/acceptance/tests/functional/_bootstrap.php b/dev/tests/acceptance/tests/functional/_bootstrap.php deleted file mode 100644 index ac7a13ea41d29..0000000000000 --- a/dev/tests/acceptance/tests/functional/_bootstrap.php +++ /dev/null @@ -1,8 +0,0 @@ - Date: Fri, 1 Jun 2018 12:32:53 +0530 Subject: [PATCH 0025/1171] Fixed coding standard changes --- .../Directory/Model/Currency/Import/CurrencyConverterApi.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php b/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php index a24bd51db64d8..c32fb36eb5b2e 100644 --- a/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php +++ b/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php @@ -89,10 +89,8 @@ private function convertBatch($data, $currencyFrom, $currenciesTo) } finally { ini_restore('max_execution_time'); } - } - return $data; } @@ -138,4 +136,4 @@ private function getServiceResponse($url, $retry = 0) protected function _convert($currencyFrom, $currencyTo) { } -} \ No newline at end of file +} From cd0d6081f487ebca53643accfcfa3c4d000fc00d Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sun, 20 May 2018 18:13:12 -0400 Subject: [PATCH 0026/1171] Replace single line codeStandardIgnore with codingStandardIgnoreLine --- .../Framework/Encryption/Adapter/Mcrypt.php | 31 +++++-------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php index 935b1280fc65d..0756669b65763 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php @@ -45,29 +45,19 @@ public function __construct( ) { $this->cipher = $cipher; $this->mode = $mode; - // @codingStandardsIgnoreStart + // @codingStandardIgnoreLine $this->handle = @mcrypt_module_open($cipher, '', $mode, ''); - // @codingStandardsIgnoreEnd try { - // @codingStandardsIgnoreStart + // @codingStandardIgnoreLine $maxKeySize = @mcrypt_enc_get_key_size($this->handle); - // @codingStandardsIgnoreEnd if (strlen($key) > $maxKeySize) { throw new \Magento\Framework\Exception\LocalizedException( new \Magento\Framework\Phrase('Key must not exceed %1 bytes.', [$maxKeySize]) ); } - // @codingStandardsIgnoreStart + // @codingStandardIgnoreLine $initVectorSize = @mcrypt_enc_get_iv_size($this->handle); - // @codingStandardsIgnoreEnd - if (true === $initVector) { - /* Generate a random vector from human-readable characters */ - $abc = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; - $initVector = ''; - for ($i = 0; $i < $initVectorSize; $i++) { - $initVector .= $abc[rand(0, strlen($abc) - 1)]; - } - } elseif (false === $initVector) { + if (null === $initVector) { /* Set vector to zero bytes to not use it */ $initVector = str_repeat("\0", $initVectorSize); } elseif (!is_string($initVector) || strlen($initVector) != $initVectorSize) { @@ -80,14 +70,12 @@ public function __construct( } $this->_initVector = $initVector; } catch (\Exception $e) { - // @codingStandardsIgnoreStart + // @codingStandardIgnoreLine @mcrypt_module_close($this->handle); - // @codingStandardsIgnoreEnd - throw $e; + throw new \Magento\Framework\Exception\LocalizedException(new \Magento\Framework\Phrase($e->getMessage())); } - // @codingStandardsIgnoreStart + // @codingStandardIgnoreLine @mcrypt_generic_init($this->handle, $key, $initVector); - // @codingStandardsIgnoreEnd } /** @@ -97,8 +85,6 @@ public function __destruct() { // @codingStandardsIgnoreStart @mcrypt_generic_deinit($this->handle); - // @codingStandardsIgnoreEnd - // @codingStandardsIgnoreStart @mcrypt_module_close($this->handle); // @codingStandardsIgnoreEnd } @@ -158,9 +144,8 @@ public function decrypt(string $data): string if (strlen($data) == 0) { return $data; } - // @codingStandardsIgnoreStart + // @codingStandardIgnoreLine $data = @mdecrypt_generic($this->handle, $data); - // @codingStandardsIgnoreEnd /* * Returned string can in fact be longer than the unencrypted string due to the padding of the data * @link http://www.php.net/manual/en/function.mdecrypt-generic.php From bb72792c2a71d46b719cecad392a8314421541e8 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Wed, 23 May 2018 14:27:18 -0400 Subject: [PATCH 0027/1171] Add Crypt to obsolete class list --- .../testsuite/Magento/Test/Legacy/_files/obsolete_classes.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index 78ab26401b0ff..12c10990a3af5 100755 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -4237,4 +4237,5 @@ ['Zend_Feed', 'Zend\Feed'], ['Zend_Uri', 'Zend\Uri\Uri'], ['Zend_Mime', 'Magento\Framework\HTTP\Mime'], + ['Magento\Framework\Encryption\Crypt', 'Magento\Framework\Encryption\EncryptionAdapterInterface'], ]; From 2a42199051b71cd5e670d6169b2c8ebd890ceda1 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sat, 2 Jun 2018 08:18:49 -0400 Subject: [PATCH 0028/1171] Change return types of getCrypt and validateKey `Magento\Framework\Encryption\Encryptor::validateKey` - Now returns `void` instead of an instance of `\Magento\Framework\Encryption\Crypt`. `Magento\Framework\Encryption\Encryptor::getCrypt` - Scope was changed from `protected` to `private` and return value is now an instance of `Magento\Framework\Encryption\Adapter\EncryptionAdapterInterface` --- .../Magento/Framework/Encryption/ModelTest.php | 14 ++++++++++---- .../Framework/Encryption/Adapter/Mcrypt.php | 2 +- .../Magento/Framework/Encryption/Encryptor.php | 17 +++++++++-------- .../Encryption/Test/Unit/EncryptorTest.php | 15 +++++++++------ 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Encryption/ModelTest.php b/dev/tests/integration/testsuite/Magento/Framework/Encryption/ModelTest.php index ce21765ec0e5f..12f398b705b2d 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Encryption/ModelTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Encryption/ModelTest.php @@ -42,10 +42,16 @@ public function testEncryptDecrypt2() public function testValidateKey() { $validKey = md5(uniqid()); - $this->assertInstanceOf( - \Magento\Framework\Encryption\Adapter\EncryptionAdapterInterface::class, - $this->_model->validateKey($validKey) - ); + $this->_model->validateKey($validKey); + } + + /** + * @expectedException \Exception + */ + public function testValidateKeyInvalid() + { + $invalidKey = '---- '; + $this->_model->validateKey($invalidKey); } public function testGetValidateHash() diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php index 0756669b65763..8e4859e727f86 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php @@ -114,7 +114,7 @@ public function getMode(): string * * @return string */ - public function getInitVector(): string + public function getInitVector(): ?string { return $this->initVector; } diff --git a/lib/internal/Magento/Framework/Encryption/Encryptor.php b/lib/internal/Magento/Framework/Encryption/Encryptor.php index 149e167d65128..eb73065696711 100644 --- a/lib/internal/Magento/Framework/Encryption/Encryptor.php +++ b/lib/internal/Magento/Framework/Encryption/Encryptor.php @@ -298,11 +298,11 @@ public function decrypt($data) $parts = explode(':', $data, 4); $partsCount = count($parts); - $initVector = false; + $initVector = null; // specified key, specified crypt, specified iv if (4 === $partsCount) { list($keyVersion, $cryptVersion, $iv, $data) = $parts; - $initVector = $iv ? $iv : false; + $initVector = $iv ? $iv : null; $keyVersion = (int)$keyVersion; $cryptVersion = self::CIPHER_RIJNDAEL_256; // specified key, specified crypt @@ -337,10 +337,9 @@ public function decrypt($data) } /** - * Return crypt model, instantiate if it is empty + * Validate key contains only allowed characters * * @param string|null $key NULL value means usage of the default key specified on constructor - * @return EncryptionAdapterInterface * @throws \Exception */ public function validateKey($key) @@ -348,7 +347,6 @@ public function validateKey($key) if (preg_match('/\s/s', $key)) { throw new \Exception((string)new \Magento\Framework\Phrase('The encryption key format is invalid.')); } - return $this->getCrypt($key); } /** @@ -383,12 +381,15 @@ public function exportKeys() * * @param string $key * @param int $cipherVersion - * @param bool $initVector + * @param string $initVector * @return EncryptionAdapterInterface|null * @throws \Exception */ - protected function getCrypt($key = null, $cipherVersion = null, $initVector = true) - { + private function getCrypt( + string $key = null, + int $cipherVersion = null, + string $initVector = null + ): ?EncryptionAdapterInterface { if (null === $key && null === $cipherVersion) { $cipherVersion = self::CIPHER_RIJNDAEL_256; } diff --git a/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php b/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php index 1de0fd684ca77..14ac0ed8b8f96 100644 --- a/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php +++ b/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php @@ -216,12 +216,15 @@ public function testEncryptDecryptNewKeyAdded() public function testValidateKey() { - $actual = $this->_model->validateKey(self::CRYPT_KEY_1); - $crypt = new Sodium(self::CRYPT_KEY_1); - $expectedEncryptedData = base64_encode($crypt->encrypt('data')); - $actualEncryptedData = base64_encode($actual->encrypt('data')); - $this->assertNotEquals($expectedEncryptedData, $actualEncryptedData); - $this->assertEquals($crypt->decrypt($expectedEncryptedData), $actual->decrypt($actualEncryptedData)); + $this->_model->validateKey(self::CRYPT_KEY_1); + } + + /** + * @expectedException \Exception + */ + public function testValidateKeyInvalid() + { + $this->_model->validateKey('----- '); } public function testUseSpecifiedHashingAlgoDataProvider() From 19ca0905ae81d9d32ae5aa1f56e38b31b1bf7cd0 Mon Sep 17 00:00:00 2001 From: Julian van Drielen Date: Sat, 2 Jun 2018 17:00:31 +0200 Subject: [PATCH 0029/1171] Forward pull for issue 4803 2.1->2.3 --- app/code/Magento/Eav/Api/Data/AttributeInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Api/Data/AttributeInterface.php b/app/code/Magento/Eav/Api/Data/AttributeInterface.php index e8970d2b42149..e9ca06d716b28 100644 --- a/app/code/Magento/Eav/Api/Data/AttributeInterface.php +++ b/app/code/Magento/Eav/Api/Data/AttributeInterface.php @@ -11,7 +11,7 @@ * @api * @since 100.0.2 */ -interface AttributeInterface extends \Magento\Framework\Api\CustomAttributesDataInterface +interface AttributeInterface extends \Magento\Framework\Api\CustomAttributesDataInterface, \Magento\Framework\Api\MetadataObjectInterface { const ATTRIBUTE_ID = 'attribute_id'; From 4e51e9c056f29318564dd50ce293a9cedc84bbf1 Mon Sep 17 00:00:00 2001 From: Julian van Drielen Date: Sat, 2 Jun 2018 17:01:45 +0200 Subject: [PATCH 0030/1171] MEQP | Forward pull for issue 4803 2.1->2.3 --- app/code/Magento/Eav/Api/Data/AttributeInterface.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Api/Data/AttributeInterface.php b/app/code/Magento/Eav/Api/Data/AttributeInterface.php index e9ca06d716b28..ef51215c39a6b 100644 --- a/app/code/Magento/Eav/Api/Data/AttributeInterface.php +++ b/app/code/Magento/Eav/Api/Data/AttributeInterface.php @@ -11,7 +11,9 @@ * @api * @since 100.0.2 */ -interface AttributeInterface extends \Magento\Framework\Api\CustomAttributesDataInterface, \Magento\Framework\Api\MetadataObjectInterface +interface AttributeInterface extends + \Magento\Framework\Api\CustomAttributesDataInterface, + \Magento\Framework\Api\MetadataObjectInterface { const ATTRIBUTE_ID = 'attribute_id'; From a8dc518ecd77b813ff79e8fb864c9cb4b01456e9 Mon Sep 17 00:00:00 2001 From: Julian van Drielen Date: Sat, 2 Jun 2018 23:35:41 +0200 Subject: [PATCH 0031/1171] MEQP | Forward pull for issue 4803 2.1->2.3 --- app/code/Magento/Eav/Api/Data/AttributeInterface.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Eav/Api/Data/AttributeInterface.php b/app/code/Magento/Eav/Api/Data/AttributeInterface.php index ef51215c39a6b..3932222e6d575 100644 --- a/app/code/Magento/Eav/Api/Data/AttributeInterface.php +++ b/app/code/Magento/Eav/Api/Data/AttributeInterface.php @@ -11,10 +11,8 @@ * @api * @since 100.0.2 */ -interface AttributeInterface extends - \Magento\Framework\Api\CustomAttributesDataInterface, - \Magento\Framework\Api\MetadataObjectInterface -{ +interface AttributeInterface extends \Magento\Framework\Api\CustomAttributesDataInterface, + \Magento\Framework\Api\MetadataObjectInterface { const ATTRIBUTE_ID = 'attribute_id'; const IS_UNIQUE = 'is_unique'; From 40f046ef08df02f4c145e9bbe2ec11ac9eb47ae9 Mon Sep 17 00:00:00 2001 From: Julian van Drielen Date: Mon, 4 Jun 2018 10:17:28 +0200 Subject: [PATCH 0032/1171] MEQP | Forward pull for issue 4803 2.1->2.3 --- app/code/Magento/Eav/Api/Data/AttributeInterface.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Api/Data/AttributeInterface.php b/app/code/Magento/Eav/Api/Data/AttributeInterface.php index 3932222e6d575..8386ab33ab34f 100644 --- a/app/code/Magento/Eav/Api/Data/AttributeInterface.php +++ b/app/code/Magento/Eav/Api/Data/AttributeInterface.php @@ -12,7 +12,8 @@ * @since 100.0.2 */ interface AttributeInterface extends \Magento\Framework\Api\CustomAttributesDataInterface, - \Magento\Framework\Api\MetadataObjectInterface { + \Magento\Framework\Api\MetadataObjectInterface +{ const ATTRIBUTE_ID = 'attribute_id'; const IS_UNIQUE = 'is_unique'; From a10f85ade47103034976744a70f12c4c7e0e32fb Mon Sep 17 00:00:00 2001 From: RomanKis Date: Sat, 2 Jun 2018 16:43:53 +0300 Subject: [PATCH 0033/1171] MSI-478: Unskip StockStatusFilterTest --- .../Catalog/_files/multiselect_attribute.php | 83 +++--- ...ut_of_stock_with_multiselect_attribute.php | 39 +++ ...ck_with_multiselect_attribute_rollback.php | 33 +++ ...ts_with_multiselect_attribute_rollback.php | 8 + .../FilterMapper/StockStatusFilterTest.php | 240 ------------------ .../StockStatusFilterWithFullFilterTest.php | 122 +++++++++ ...StockStatusFilterWithGeneralFilterTest.php | 105 ++++++++ 7 files changed, 350 insertions(+), 280 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_out_of_stock_with_multiselect_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_out_of_stock_with_multiselect_attribute_rollback.php delete mode 100644 dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilterTest.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilterWithFullFilterTest.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilterWithGeneralFilterTest.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php index 6ab607b5de88f..7dacdc42463a9 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php @@ -12,45 +12,48 @@ $attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class ); -$attribute->setData( - [ - 'attribute_code' => 'multiselect_attribute', - 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), - 'is_global' => 1, - 'is_user_defined' => 1, - 'frontend_input' => 'multiselect', - 'is_unique' => 0, - 'is_required' => 0, - 'is_searchable' => 0, - '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, - 'frontend_label' => ['Multiselect Attribute'], - 'backend_type' => 'varchar', - 'backend_model' => \Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend::class, - 'option' => [ - 'value' => [ - 'option_1' => ['Option 1'], - 'option_2' => ['Option 2'], - 'option_3' => ['Option 3'], - 'option_4' => ['Option 4 "!@#$%^&*'] +$entityType = $installer->getEntityTypeId('catalog_product'); +if (!$attribute->loadByCode($entityType, 'multiselect_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'attribute_code' => 'multiselect_attribute', + 'entity_type_id' => $entityType, + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'multiselect', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + '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, + 'frontend_label' => ['Multiselect Attribute'], + 'backend_type' => 'varchar', + 'backend_model' => \Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend::class, + 'option' => [ + 'value' => [ + 'option_1' => ['Option 1'], + 'option_2' => ['Option 2'], + 'option_3' => ['Option 3'], + 'option_4' => ['Option 4 "!@#$%^&*'] + ], + 'order' => [ + 'option_1' => 1, + 'option_2' => 2, + 'option_3' => 3, + 'option_4' => 4, + ], ], - 'order' => [ - 'option_1' => 1, - 'option_2' => 2, - 'option_3' => 3, - 'option_4' => 4, - ], - ], - ] -); -$attribute->save(); + ] + ); + $attribute->save(); -/* Assign attribute to attribute set */ -$installer->addAttributeToGroup('catalog_product', 'Default', 'General', $attribute->getId()); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup('catalog_product', 'Default', 'General', $attribute->getId()); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_out_of_stock_with_multiselect_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_out_of_stock_with_multiselect_attribute.php new file mode 100644 index 0000000000000..3d2dfcdff53e0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_out_of_stock_with_multiselect_attribute.php @@ -0,0 +1,39 @@ +create( + \Magento\Catalog\Setup\CategorySetup::class +); + +/** @var $options \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection */ +$options = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class +); +$options->setAttributeFilter($attribute->getId()); +$optionIds = $options->getAllIds(); + +$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setId($optionIds[1] * 20) + ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default')) + ->setWebsiteIds([1]) + ->setName('Out of Stock With Multiselect') + ->setSku('simple_ms_out_of_stock') + ->setPrice(10) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setMultiselectAttribute([$optionIds[1], $optionIds[2], $optionIds[3]]) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 0,'is_in_stock' => 0]) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_out_of_stock_with_multiselect_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_out_of_stock_with_multiselect_attribute_rollback.php new file mode 100644 index 0000000000000..d1926f4769f77 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_out_of_stock_with_multiselect_attribute_rollback.php @@ -0,0 +1,33 @@ +get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('simple_ms_out_of_stock', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} + +\Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(IndexerRegistry::class) + ->get(Magento\CatalogInventory\Model\Indexer\Stock\Processor::INDEXER_ID) + ->reindexAll(); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_multiselect_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_multiselect_attribute_rollback.php index 7a72f168f924f..eb8201f04e6cc 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_multiselect_attribute_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_multiselect_attribute_rollback.php @@ -3,7 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + require __DIR__ . '/multiselect_attribute_rollback.php'; + +use Magento\Framework\Indexer\IndexerRegistry; + /** * Remove all products as strategy of isolation process */ @@ -22,3 +26,7 @@ $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); + +\Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(IndexerRegistry::class) + ->get(Magento\CatalogInventory\Model\Indexer\Stock\Processor::INDEXER_ID) + ->reindexAll(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilterTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilterTest.php deleted file mode 100644 index a8a404a1ee2d6..0000000000000 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilterTest.php +++ /dev/null @@ -1,240 +0,0 @@ -objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->resource = $this->objectManager->create(ResourceConnection::class); - $this->conditionManager = $this->objectManager->create(ConditionManager::class); - $this->stockConfiguration = $this->objectManager->create(StockConfigurationInterface::class); - $this->stockRegistry = $this->objectManager->create(StockRegistryInterface::class); - $this->stockStatusFilter = $this->objectManager->create(StockStatusFilter::class); - } - - /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Invalid filter type: Luke I am your father! - */ - public function testApplyWithWrongType() - { - $select = $this->resource->getConnection()->select(); - $this->stockStatusFilter->apply( - $select, - Stock::STOCK_IN_STOCK, - 'Luke I am your father!', - self::SHOW_OUT_OF_STOCK_ENABLED - ); - } - - public function testApplyGeneralFilterWithOutOfStock() - { - $select = $this->resource->getConnection()->select(); - $select->from( - ['some_index' => 'some_table'], - ['entity_id' => 'entity_id'] - ); - - $resultSelect = $this->stockStatusFilter->apply( - $select, - Stock::STOCK_IN_STOCK, - StockStatusFilter::FILTER_JUST_ENTITY, - self::SHOW_OUT_OF_STOCK_ENABLED - ); - - $expectedSelect = $this->getExpectedSelectForGeneralFilter(self::SHOW_OUT_OF_STOCK_ENABLED); - - $this->assertEquals( - (string) $expectedSelect, - (string) $resultSelect, - 'Select queries must be the same' - ); - } - - public function testApplyGeneralFilterWithoutOutOfStock() - { - $select = $this->resource->getConnection()->select(); - $select->from( - ['some_index' => 'some_table'], - ['entity_id' => 'entity_id'] - ); - - $resultSelect = $this->stockStatusFilter->apply( - $select, - Stock::STOCK_IN_STOCK, - StockStatusFilter::FILTER_JUST_ENTITY, - self::SHOW_OUT_OF_STOCK_DISABLED - ); - - $expectedSelect = $this->getExpectedSelectForGeneralFilter(self::SHOW_OUT_OF_STOCK_DISABLED); - - $this->assertEquals( - (string) $expectedSelect, - (string) $resultSelect, - 'Select queries must be the same' - ); - } - - public function testApplyFullFilterWithOutOfStock() - { - $select = $this->resource->getConnection()->select(); - $select->from( - ['some_index' => 'some_table'], - ['entity_id' => 'entity_id'] - ); - - $resultSelect = $this->stockStatusFilter->apply( - $select, - Stock::STOCK_IN_STOCK, - StockStatusFilter::FILTER_ENTITY_AND_SUB_PRODUCTS, - self::SHOW_OUT_OF_STOCK_ENABLED - ); - - $expectedSelect = $this->getExpectedSelectForFullFilter(self::SHOW_OUT_OF_STOCK_ENABLED); - - $this->assertEquals( - (string) $expectedSelect, - (string) $resultSelect, - 'Select queries must be the same' - ); - } - - public function testApplyFullFilterWithoutOutOfStock() - { - $select = $this->resource->getConnection()->select(); - $select->from( - ['some_index' => 'some_table'], - ['entity_id' => 'entity_id'] - ); - - $resultSelect = $this->stockStatusFilter->apply( - $select, - Stock::STOCK_IN_STOCK, - StockStatusFilter::FILTER_ENTITY_AND_SUB_PRODUCTS, - self::SHOW_OUT_OF_STOCK_DISABLED - ); - - $expectedSelect = $this->getExpectedSelectForFullFilter(self::SHOW_OUT_OF_STOCK_DISABLED); - - $this->assertEquals( - (string) $expectedSelect, - (string) $resultSelect, - 'Select queries must be the same' - ); - } - - /** - * @param bool $withOutOfStock - * @return Select - */ - private function getExpectedSelectForGeneralFilter($withOutOfStock) - { - $select = $this->resource->getConnection()->select(); - $select->from( - ['some_index' => 'some_table'], - ['entity_id' => 'entity_id'] - )->joinInner( - ['stock_index' => $this->resource->getTableName('cataloginventory_stock_status')], - $this->conditionManager->combineQueries( - [ - 'stock_index.product_id = some_index.entity_id', - $this->conditionManager->generateCondition( - 'stock_index.website_id', - '=', - $this->stockConfiguration->getDefaultScopeId() - ), - $withOutOfStock - ? '' - : $this->conditionManager->generateCondition( - 'stock_index.stock_status', - '=', - Stock::STOCK_IN_STOCK - ), - $this->conditionManager->generateCondition( - 'stock_index.stock_id', - '=', - (int) $this->stockRegistry->getStock()->getStockId() - ), - ], - Select::SQL_AND - ), - [] - ); - - return $select; - } - - /** - * @param bool $withOutOfStock - * @return Select - */ - private function getExpectedSelectForFullFilter($withOutOfStock) - { - $select = $this->getExpectedSelectForGeneralFilter($withOutOfStock); - $select->joinInner( - ['sub_products_stock_index' => $this->resource->getTableName('cataloginventory_stock_status')], - $this->conditionManager->combineQueries( - [ - 'sub_products_stock_index.product_id = some_index.source_id', - $this->conditionManager->generateCondition( - 'sub_products_stock_index.website_id', - '=', - $this->stockConfiguration->getDefaultScopeId() - ), - $withOutOfStock - ? '' - : $this->conditionManager->generateCondition( - 'sub_products_stock_index.stock_status', - '=', - Stock::STOCK_IN_STOCK - ), - $this->conditionManager->generateCondition( - 'sub_products_stock_index.stock_id', - '=', - (int) $this->stockRegistry->getStock()->getStockId() - ), - ], - Select::SQL_AND - ), - [] - ); - - return $select; - } -} diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilterWithFullFilterTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilterWithFullFilterTest.php new file mode 100644 index 0000000000000..e94eda11a3fc5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilterWithFullFilterTest.php @@ -0,0 +1,122 @@ +objectManager = Bootstrap::getObjectManager(); + $this->resource = $this->objectManager->get(ResourceConnection::class); + $this->stockStatusFilter = $this->objectManager->get(StockStatusFilter::class); + $this->customAttributeFilter = $this->objectManager->get(CustomAttributeFilter::class); + $eavConfig = $this->objectManager->get(EavConfig::class); + $attribute = $eavConfig->getAttribute(Product::ENTITY, 'multiselect_attribute'); + + $productRepository = $this->objectManager->get(ProductRepository::class); + $product = $productRepository->get('simple_ms_2'); + $multiSelectArray = explode(',', $product->getData('multiselect_attribute')); + + $this->filter = $this->objectManager->create( + Term::class, + [ + 'field' => $attribute->getAttributeCode(), + 'name' => $attribute->getAttributeCode() . '_filter', + 'value' => reset($multiSelectArray), + ] + ); + } + + /** + * @param bool $showOutOfStockFlag + * @param int $expectedResult + * @return void + * + * @dataProvider applyDataProvider + */ + public function testApply(bool $showOutOfStockFlag, int $expectedResult) + { + $select = $this->resource->getConnection()->select(); + $select->from( + [$this->resource->getTableName('catalog_product_index_eav')], + ['entity_id'] + )->distinct(true); + $select = $this->customAttributeFilter->apply($select, $this->filter); + $select = $this->stockStatusFilter->apply( + $select, + Stock::STOCK_IN_STOCK, + StockStatusFilter::FILTER_ENTITY_AND_SUB_PRODUCTS, + $showOutOfStockFlag + ); + + $data = $select->query()->fetchAll(); + + $this->assertEquals($expectedResult, count($data)); + } + + /** + * @return array + */ + public function applyDataProvider(): array + { + return [ + [true, 2], + [false, 1], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilterWithGeneralFilterTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilterWithGeneralFilterTest.php new file mode 100644 index 0000000000000..809116e9c7d84 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilterWithGeneralFilterTest.php @@ -0,0 +1,105 @@ +objectManager = Bootstrap::getObjectManager(); + $this->resource = $this->objectManager->get(ResourceConnection::class); + $this->stockStatusFilter = $this->objectManager->get(StockStatusFilter::class); + } + + /** + * @return void + * + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid filter type: some_wrong_type + */ + public function testApplyWithWrongType() + { + $select = $this->resource->getConnection()->select(); + $this->stockStatusFilter->apply( + $select, + Stock::STOCK_IN_STOCK, + 'some_wrong_type', + true + ); + } + + /** + * @param bool $showOutOfStockFlag + * @param int $expectedResult + * @return void + * + * @dataProvider applyDataProvider + */ + public function testApply(bool $showOutOfStockFlag, int $expectedResult) + { + $select = $this->resource->getConnection()->select(); + $select->from( + [$this->resource->getTableName('catalog_product_index_eav')], + ['entity_id'] + )->distinct(true); + + $select = $this->stockStatusFilter->apply( + $select, + Stock::STOCK_IN_STOCK, + StockStatusFilter::FILTER_JUST_ENTITY, + $showOutOfStockFlag + ); + $data = $select->query()->fetchAll(); + + $this->assertEquals($expectedResult, count($data)); + } + + /** + * @return array + */ + public function applyDataProvider(): array + { + return [ + [true, 6], + [false, 4], + ]; + } +} From f87e1016d101fd73776fee63ae252c7dc9754db2 Mon Sep 17 00:00:00 2001 From: Yuriy Tkachenko Date: Wed, 6 Jun 2018 14:12:13 +0300 Subject: [PATCH 0034/1171] Handle type errors for objects creation --- .../Framework/ObjectManager/Factory/AbstractFactory.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php index 020159985105d..960e7890971b2 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php @@ -104,11 +104,15 @@ public function getDefinitions() * @param array $args * * @return object - * + * @throws \Exception */ protected function createObject($type, $args) { - return new $type(...array_values($args)); + try { + return new $type(...array_values($args)); + } catch (\Throwable $e) { + throw new \Exception($e->getMessage(), null, $e); + } } /** From 71db984187a31aa9f194177d2764ac30c3946202 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv Date: Wed, 6 Jun 2018 15:33:44 +0300 Subject: [PATCH 0035/1171] Added integration test #10790 --- .../Tax/Model/Sales/Total/Quote/TaxTest.php | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php index 9b498afc2500d..abc6f180950c6 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php @@ -108,6 +108,161 @@ public function testCollect() ); } + /** + * Test taxes collection with full discount for quote. + * + * Test tax calculation with certain configuration and price calculation of items when the discount may be bigger than total + * This method will test the collector through $quote->collectTotals() method + * + * @see \Magento\SalesRule\Model\Utility::deltaRoundingFix + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ + public function testFullDiscountWithDeltaRoundingFix() + { + $configData = array ( + 'config_overrides' => + array ( + 'tax/calculation/apply_after_discount' => 0, + 'tax/calculation/discount_tax' => 1, + 'tax/calculation/algorithm' => 'ROW_BASE_CALCULATION', + 'tax/classes/shipping_tax_class' => SetupUtil::SHIPPING_TAX_CLASS, + ), + 'tax_rate_overrides' => + array ( + SetupUtil::TAX_RATE_TX => 18, + SetupUtil::TAX_RATE_SHIPPING => 0, + ), + 'tax_rule_overrides' => + array ( + array ( + 'code' => 'Product Tax Rule', + 'product_tax_class_ids' => + array ( + SetupUtil::PRODUCT_TAX_CLASS_1 + ), + ), + array ( + 'code' => 'Shipping Tax Rule', + 'product_tax_class_ids' => + array ( + SetupUtil::SHIPPING_TAX_CLASS + ), + 'tax_rate_ids' => + array ( + SetupUtil::TAX_RATE_SHIPPING, + ), + ), + ), + ); + + $quoteData = array ( + 'billing_address' => + array ( + 'region_id' => SetupUtil::REGION_TX, + ), + 'shipping_address' => + array ( + 'region_id' => SetupUtil::REGION_TX, + ), + 'items' => + [ + [ + 'sku' => 'simple1', + 'price' => 2542.37, + 'qty' => 2, + ] + ], + 'shipping_method' => 'free', + 'shopping_cart_rules' => + array ( + array ( + 'discount_amount' => 100, + ), + ), + ); + + $expectedResults = array ( + 'address_data' => + array ( + 'subtotal' => 5084.74, + 'base_subtotal' => 5084.74, + 'subtotal_incl_tax' => 5999.99, + 'base_subtotal_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'shipping_amount' => 0, + 'base_shipping_amount' => 0, + 'shipping_incl_tax' => 0, + 'base_shipping_incl_tax' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'discount_amount' => -5999.99, + 'base_discount_amount' => -5999.99, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + 'grand_total' => 0, + 'base_grand_total' => 0, + 'applied_taxes' => + array ( + SetupUtil::TAX_RATE_TX => + array ( + 'percent' => 18, + 'amount' => 915.25, + 'base_amount' => 915.25, + 'rates' => + array ( + array ( + 'code' => SetupUtil::TAX_RATE_TX, + 'title' => SetupUtil::TAX_RATE_TX, + 'percent' => 18, + ), + ), + ) + ), + ), + 'items_data' => + array ( + 'simple1' => + array ( + 'row_total' => 5084.74, + 'base_row_total' => 5084.74, + 'tax_percent' => 18, + 'price' => 2542.37, + 'base_price' => 2542.37, + 'price_incl_tax' => 3000, + 'base_price_incl_tax' => 3000, + 'row_total_incl_tax' => 5999.99, + 'base_row_total_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'discount_amount' => 5999.99, + 'base_discount_amount' => 5999.99, + 'discount_percent' => 100, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + ), + ), + ); + + /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ + $objectManager = Bootstrap::getObjectManager(); + + //Setup tax configurations + $this->setupUtil = new SetupUtil($objectManager); + $this->setupUtil->setupTax($configData); + + $quote = $this->setupUtil->setupQuote($quoteData); + + $quote->collectTotals(); + + $quoteAddress = $quote->getShippingAddress(); + + $this->verifyResult($quoteAddress, $expectedResults); + } + /** * Verify fields in quote item * From 35ec386d9c28a46ab97168ba5f037d9e40c59ead Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv Date: Wed, 6 Jun 2018 15:56:24 +0300 Subject: [PATCH 0036/1171] Array style fix #10790 --- .../Tax/Model/Sales/Total/Quote/TaxTest.php | 90 +++++++++---------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php index abc6f180950c6..86eae684483de 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php @@ -111,7 +111,7 @@ public function testCollect() /** * Test taxes collection with full discount for quote. * - * Test tax calculation with certain configuration and price calculation of items when the discount may be bigger than total + * Test tax calculation and price when the discount may be bigger than total * This method will test the collector through $quote->collectTotals() method * * @see \Magento\SalesRule\Model\Utility::deltaRoundingFix @@ -120,51 +120,51 @@ public function testCollect() */ public function testFullDiscountWithDeltaRoundingFix() { - $configData = array ( + $configData = [ 'config_overrides' => - array ( + [ 'tax/calculation/apply_after_discount' => 0, 'tax/calculation/discount_tax' => 1, 'tax/calculation/algorithm' => 'ROW_BASE_CALCULATION', 'tax/classes/shipping_tax_class' => SetupUtil::SHIPPING_TAX_CLASS, - ), + ], 'tax_rate_overrides' => - array ( + [ SetupUtil::TAX_RATE_TX => 18, SetupUtil::TAX_RATE_SHIPPING => 0, - ), + ], 'tax_rule_overrides' => - array ( - array ( + [ + [ 'code' => 'Product Tax Rule', 'product_tax_class_ids' => - array ( + [ SetupUtil::PRODUCT_TAX_CLASS_1 - ), - ), - array ( + ], + ], + [ 'code' => 'Shipping Tax Rule', 'product_tax_class_ids' => - array ( + [ SetupUtil::SHIPPING_TAX_CLASS - ), + ], 'tax_rate_ids' => - array ( + [ SetupUtil::TAX_RATE_SHIPPING, - ), - ), - ), - ); + ], + ], + ], + ]; - $quoteData = array ( + $quoteData = [ 'billing_address' => - array ( + [ 'region_id' => SetupUtil::REGION_TX, - ), + ], 'shipping_address' => - array ( + [ 'region_id' => SetupUtil::REGION_TX, - ), + ], 'items' => [ [ @@ -175,16 +175,14 @@ public function testFullDiscountWithDeltaRoundingFix() ], 'shipping_method' => 'free', 'shopping_cart_rules' => - array ( - array ( - 'discount_amount' => 100, - ), - ), - ); + [ + ['discount_amount' => 100], + ], + ]; - $expectedResults = array ( + $expectedResults = [ 'address_data' => - array ( + [ 'subtotal' => 5084.74, 'base_subtotal' => 5084.74, 'subtotal_incl_tax' => 5999.99, @@ -206,27 +204,27 @@ public function testFullDiscountWithDeltaRoundingFix() 'grand_total' => 0, 'base_grand_total' => 0, 'applied_taxes' => - array ( + [ SetupUtil::TAX_RATE_TX => - array ( + [ 'percent' => 18, 'amount' => 915.25, 'base_amount' => 915.25, 'rates' => - array ( - array ( + [ + [ 'code' => SetupUtil::TAX_RATE_TX, 'title' => SetupUtil::TAX_RATE_TX, 'percent' => 18, - ), - ), - ) - ), - ), + ], + ], + ] + ], + ], 'items_data' => - array ( + [ 'simple1' => - array ( + [ 'row_total' => 5084.74, 'base_row_total' => 5084.74, 'tax_percent' => 18, @@ -243,9 +241,9 @@ public function testFullDiscountWithDeltaRoundingFix() 'discount_percent' => 100, 'discount_tax_compensation_amount' => 0, 'base_discount_tax_compensation_amount' => 0, - ), - ), - ); + ], + ], + ]; /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ $objectManager = Bootstrap::getObjectManager(); From 93e44f8cb1a9de26c7587d97fa4abb7ae6379725 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv Date: Wed, 6 Jun 2018 17:08:08 +0300 Subject: [PATCH 0037/1171] Store data in a separate file #10790 --- .../Tax/Model/Sales/Total/Quote/TaxTest.php | 130 +---------------- .../Tax/_files/full_discount_with_tax.php | 132 ++++++++++++++++++ 2 files changed, 138 insertions(+), 124 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php index 86eae684483de..1b07d49678550 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php @@ -10,6 +10,7 @@ require_once __DIR__ . '/SetupUtil.php'; require_once __DIR__ . '/../../../../_files/tax_calculation_data_aggregated.php'; +require_once __DIR__ . '/../../../../_files/full_discount_with_tax.php'; /** * Class TaxTest @@ -115,135 +116,16 @@ public function testCollect() * This method will test the collector through $quote->collectTotals() method * * @see \Magento\SalesRule\Model\Utility::deltaRoundingFix + * @magentoDataFixture Magento/Tax/_files/full_discount_with_tax.php * @magentoDbIsolation enabled * @magentoAppIsolation enabled */ public function testFullDiscountWithDeltaRoundingFix() { - $configData = [ - 'config_overrides' => - [ - 'tax/calculation/apply_after_discount' => 0, - 'tax/calculation/discount_tax' => 1, - 'tax/calculation/algorithm' => 'ROW_BASE_CALCULATION', - 'tax/classes/shipping_tax_class' => SetupUtil::SHIPPING_TAX_CLASS, - ], - 'tax_rate_overrides' => - [ - SetupUtil::TAX_RATE_TX => 18, - SetupUtil::TAX_RATE_SHIPPING => 0, - ], - 'tax_rule_overrides' => - [ - [ - 'code' => 'Product Tax Rule', - 'product_tax_class_ids' => - [ - SetupUtil::PRODUCT_TAX_CLASS_1 - ], - ], - [ - 'code' => 'Shipping Tax Rule', - 'product_tax_class_ids' => - [ - SetupUtil::SHIPPING_TAX_CLASS - ], - 'tax_rate_ids' => - [ - SetupUtil::TAX_RATE_SHIPPING, - ], - ], - ], - ]; - - $quoteData = [ - 'billing_address' => - [ - 'region_id' => SetupUtil::REGION_TX, - ], - 'shipping_address' => - [ - 'region_id' => SetupUtil::REGION_TX, - ], - 'items' => - [ - [ - 'sku' => 'simple1', - 'price' => 2542.37, - 'qty' => 2, - ] - ], - 'shipping_method' => 'free', - 'shopping_cart_rules' => - [ - ['discount_amount' => 100], - ], - ]; - - $expectedResults = [ - 'address_data' => - [ - 'subtotal' => 5084.74, - 'base_subtotal' => 5084.74, - 'subtotal_incl_tax' => 5999.99, - 'base_subtotal_incl_tax' => 5999.99, - 'tax_amount' => 915.25, - 'base_tax_amount' => 915.25, - 'shipping_amount' => 0, - 'base_shipping_amount' => 0, - 'shipping_incl_tax' => 0, - 'base_shipping_incl_tax' => 0, - 'shipping_tax_amount' => 0, - 'base_shipping_tax_amount' => 0, - 'discount_amount' => -5999.99, - 'base_discount_amount' => -5999.99, - 'discount_tax_compensation_amount' => 0, - 'base_discount_tax_compensation_amount' => 0, - 'shipping_discount_tax_compensation_amount' => 0, - 'base_shipping_discount_tax_compensation_amount' => 0, - 'grand_total' => 0, - 'base_grand_total' => 0, - 'applied_taxes' => - [ - SetupUtil::TAX_RATE_TX => - [ - 'percent' => 18, - 'amount' => 915.25, - 'base_amount' => 915.25, - 'rates' => - [ - [ - 'code' => SetupUtil::TAX_RATE_TX, - 'title' => SetupUtil::TAX_RATE_TX, - 'percent' => 18, - ], - ], - ] - ], - ], - 'items_data' => - [ - 'simple1' => - [ - 'row_total' => 5084.74, - 'base_row_total' => 5084.74, - 'tax_percent' => 18, - 'price' => 2542.37, - 'base_price' => 2542.37, - 'price_incl_tax' => 3000, - 'base_price_incl_tax' => 3000, - 'row_total_incl_tax' => 5999.99, - 'base_row_total_incl_tax' => 5999.99, - 'tax_amount' => 915.25, - 'base_tax_amount' => 915.25, - 'discount_amount' => 5999.99, - 'base_discount_amount' => 5999.99, - 'discount_percent' => 100, - 'discount_tax_compensation_amount' => 0, - 'base_discount_tax_compensation_amount' => 0, - ], - ], - ]; + global $fullTaxDiscountWithTax; + $configData = $fullTaxDiscountWithTax['config_data']; + $quoteData = $fullTaxDiscountWithTax['quote_data']; + $expectedResults = $fullTaxDiscountWithTax['expected_result']; /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ $objectManager = Bootstrap::getObjectManager(); diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php new file mode 100644 index 0000000000000..36ef77ffaaa22 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php @@ -0,0 +1,132 @@ + [ + 'config_overrides' => + [ + Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, + Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1, + Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION', + Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, + ], + 'tax_rate_overrides' => + [ + SetupUtil::TAX_RATE_TX => 18, + SetupUtil::TAX_RATE_SHIPPING => 0, + ], + 'tax_rule_overrides' => + [ + [ + 'code' => 'Product Tax Rule', + 'product_tax_class_ids' => + [ + SetupUtil::PRODUCT_TAX_CLASS_1 + ], + ], + [ + 'code' => 'Shipping Tax Rule', + 'product_tax_class_ids' => + [ + SetupUtil::SHIPPING_TAX_CLASS + ], + 'tax_rate_ids' => + [ + SetupUtil::TAX_RATE_SHIPPING, + ], + ], + ], + ], + 'quote_data' => [ + 'billing_address' => + [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'shipping_address' => + [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'items' => + [ + [ + 'sku' => 'simple1', + 'price' => 2542.37, + 'qty' => 2, + ] + ], + 'shipping_method' => 'free', + 'shopping_cart_rules' => + [ + ['discount_amount' => 100], + ], + ], + 'expected_result' => [ + 'address_data' => + [ + 'subtotal' => 5084.74, + 'base_subtotal' => 5084.74, + 'subtotal_incl_tax' => 5999.99, + 'base_subtotal_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'shipping_amount' => 0, + 'base_shipping_amount' => 0, + 'shipping_incl_tax' => 0, + 'base_shipping_incl_tax' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'discount_amount' => -5999.99, + 'base_discount_amount' => -5999.99, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + 'grand_total' => 0, + 'base_grand_total' => 0, + 'applied_taxes' => + [ + SetupUtil::TAX_RATE_TX => + [ + 'percent' => 18, + 'amount' => 915.25, + 'base_amount' => 915.25, + 'rates' => + [ + [ + 'code' => SetupUtil::TAX_RATE_TX, + 'title' => SetupUtil::TAX_RATE_TX, + 'percent' => 18, + ], + ], + ] + ], + ], + 'items_data' => + [ + 'simple1' => + [ + 'row_total' => 5084.74, + 'base_row_total' => 5084.74, + 'tax_percent' => 18, + 'price' => 2542.37, + 'base_price' => 2542.37, + 'price_incl_tax' => 3000, + 'base_price_incl_tax' => 3000, + 'row_total_incl_tax' => 5999.99, + 'base_row_total_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'discount_amount' => 5999.99, + 'base_discount_amount' => 5999.99, + 'discount_percent' => 100, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + ], + ], + ] +]; From f3069957250df9acc65392288df823f13e7cf351 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv Date: Wed, 6 Jun 2018 17:23:30 +0300 Subject: [PATCH 0038/1171] Array tab #10790 --- .../Tax/_files/full_discount_with_tax.php | 224 +++++++++--------- 1 file changed, 112 insertions(+), 112 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php index 36ef77ffaaa22..232ae4ea063b1 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php @@ -7,126 +7,126 @@ use Magento\Tax\Model\Sales\Total\Quote\SetupUtil; $fullTaxDiscountWithTax = [ - 'config_data' => [ - 'config_overrides' => - [ - Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, - Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1, - Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION', - Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, - ], - 'tax_rate_overrides' => - [ - SetupUtil::TAX_RATE_TX => 18, - SetupUtil::TAX_RATE_SHIPPING => 0, - ], - 'tax_rule_overrides' => - [ + 'config_data' => [ + 'config_overrides' => [ - 'code' => 'Product Tax Rule', - 'product_tax_class_ids' => - [ - SetupUtil::PRODUCT_TAX_CLASS_1 - ], + Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, + Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1, + Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION', + Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, ], + 'tax_rate_overrides' => [ - 'code' => 'Shipping Tax Rule', - 'product_tax_class_ids' => - [ - SetupUtil::SHIPPING_TAX_CLASS - ], - 'tax_rate_ids' => - [ - SetupUtil::TAX_RATE_SHIPPING, - ], + SetupUtil::TAX_RATE_TX => 18, + SetupUtil::TAX_RATE_SHIPPING => 0, ], - ], - ], - 'quote_data' => [ - 'billing_address' => - [ - 'region_id' => SetupUtil::REGION_TX, - ], - 'shipping_address' => - [ - 'region_id' => SetupUtil::REGION_TX, - ], - 'items' => - [ + 'tax_rule_overrides' => [ - 'sku' => 'simple1', - 'price' => 2542.37, - 'qty' => 2, - ] - ], - 'shipping_method' => 'free', - 'shopping_cart_rules' => - [ - ['discount_amount' => 100], - ], - ], - 'expected_result' => [ - 'address_data' => - [ - 'subtotal' => 5084.74, - 'base_subtotal' => 5084.74, - 'subtotal_incl_tax' => 5999.99, - 'base_subtotal_incl_tax' => 5999.99, - 'tax_amount' => 915.25, - 'base_tax_amount' => 915.25, - 'shipping_amount' => 0, - 'base_shipping_amount' => 0, - 'shipping_incl_tax' => 0, - 'base_shipping_incl_tax' => 0, - 'shipping_tax_amount' => 0, - 'base_shipping_tax_amount' => 0, - 'discount_amount' => -5999.99, - 'base_discount_amount' => -5999.99, - 'discount_tax_compensation_amount' => 0, - 'base_discount_tax_compensation_amount' => 0, - 'shipping_discount_tax_compensation_amount' => 0, - 'base_shipping_discount_tax_compensation_amount' => 0, - 'grand_total' => 0, - 'base_grand_total' => 0, - 'applied_taxes' => [ - SetupUtil::TAX_RATE_TX => + 'code' => 'Product Tax Rule', + 'product_tax_class_ids' => [ - 'percent' => 18, - 'amount' => 915.25, - 'base_amount' => 915.25, - 'rates' => - [ - [ - 'code' => SetupUtil::TAX_RATE_TX, - 'title' => SetupUtil::TAX_RATE_TX, - 'percent' => 18, - ], - ], - ] + SetupUtil::PRODUCT_TAX_CLASS_1 + ], ], - ], - 'items_data' => - [ - 'simple1' => [ - 'row_total' => 5084.74, - 'base_row_total' => 5084.74, - 'tax_percent' => 18, - 'price' => 2542.37, - 'base_price' => 2542.37, - 'price_incl_tax' => 3000, - 'base_price_incl_tax' => 3000, - 'row_total_incl_tax' => 5999.99, - 'base_row_total_incl_tax' => 5999.99, - 'tax_amount' => 915.25, - 'base_tax_amount' => 915.25, - 'discount_amount' => 5999.99, - 'base_discount_amount' => 5999.99, - 'discount_percent' => 100, - 'discount_tax_compensation_amount' => 0, - 'base_discount_tax_compensation_amount' => 0, + 'code' => 'Shipping Tax Rule', + 'product_tax_class_ids' => + [ + SetupUtil::SHIPPING_TAX_CLASS + ], + 'tax_rate_ids' => + [ + SetupUtil::TAX_RATE_SHIPPING, + ], ], - ], - ] + ], + ], + 'quote_data' => [ + 'billing_address' => + [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'shipping_address' => + [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'items' => + [ + [ + 'sku' => 'simple1', + 'price' => 2542.37, + 'qty' => 2, + ] + ], + 'shipping_method' => 'free', + 'shopping_cart_rules' => + [ + ['discount_amount' => 100], + ], + ], + 'expected_result' => [ + 'address_data' => + [ + 'subtotal' => 5084.74, + 'base_subtotal' => 5084.74, + 'subtotal_incl_tax' => 5999.99, + 'base_subtotal_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'shipping_amount' => 0, + 'base_shipping_amount' => 0, + 'shipping_incl_tax' => 0, + 'base_shipping_incl_tax' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'discount_amount' => -5999.99, + 'base_discount_amount' => -5999.99, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + 'grand_total' => 0, + 'base_grand_total' => 0, + 'applied_taxes' => + [ + SetupUtil::TAX_RATE_TX => + [ + 'percent' => 18, + 'amount' => 915.25, + 'base_amount' => 915.25, + 'rates' => + [ + [ + 'code' => SetupUtil::TAX_RATE_TX, + 'title' => SetupUtil::TAX_RATE_TX, + 'percent' => 18, + ], + ], + ] + ], + ], + 'items_data' => + [ + 'simple1' => + [ + 'row_total' => 5084.74, + 'base_row_total' => 5084.74, + 'tax_percent' => 18, + 'price' => 2542.37, + 'base_price' => 2542.37, + 'price_incl_tax' => 3000, + 'base_price_incl_tax' => 3000, + 'row_total_incl_tax' => 5999.99, + 'base_row_total_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'discount_amount' => 5999.99, + 'base_discount_amount' => 5999.99, + 'discount_percent' => 100, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + ], + ], + ] ]; From 317fe620f7e4858d1ea76025e46edc0ed175e7d9 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv Date: Wed, 6 Jun 2018 17:41:42 +0300 Subject: [PATCH 0039/1171] #10790 --- .../Tax/_files/full_discount_with_tax.php | 218 +++++++++--------- 1 file changed, 109 insertions(+), 109 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php index 232ae4ea063b1..7be414e046ddf 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php @@ -8,125 +8,125 @@ $fullTaxDiscountWithTax = [ 'config_data' => [ - 'config_overrides' => - [ - Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, - Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1, - Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION', - Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, - ], - 'tax_rate_overrides' => - [ - SetupUtil::TAX_RATE_TX => 18, - SetupUtil::TAX_RATE_SHIPPING => 0, - ], - 'tax_rule_overrides' => - [ + 'config_overrides' => [ - 'code' => 'Product Tax Rule', - 'product_tax_class_ids' => - [ - SetupUtil::PRODUCT_TAX_CLASS_1 - ], + Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, + Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1, + Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION', + Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, ], + 'tax_rate_overrides' => [ - 'code' => 'Shipping Tax Rule', - 'product_tax_class_ids' => - [ - SetupUtil::SHIPPING_TAX_CLASS - ], - 'tax_rate_ids' => - [ - SetupUtil::TAX_RATE_SHIPPING, - ], + SetupUtil::TAX_RATE_TX => 18, + SetupUtil::TAX_RATE_SHIPPING => 0, + ], + 'tax_rule_overrides' => + [ + [ + 'code' => 'Product Tax Rule', + 'product_tax_class_ids' => + [ + SetupUtil::PRODUCT_TAX_CLASS_1 + ], + ], + [ + 'code' => 'Shipping Tax Rule', + 'product_tax_class_ids' => + [ + SetupUtil::SHIPPING_TAX_CLASS + ], + 'tax_rate_ids' => + [ + SetupUtil::TAX_RATE_SHIPPING, + ], + ], ], - ], ], 'quote_data' => [ - 'billing_address' => - [ - 'region_id' => SetupUtil::REGION_TX, - ], - 'shipping_address' => - [ - 'region_id' => SetupUtil::REGION_TX, - ], - 'items' => - [ + 'billing_address' => + [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'shipping_address' => + [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'items' => [ - 'sku' => 'simple1', - 'price' => 2542.37, - 'qty' => 2, - ] - ], - 'shipping_method' => 'free', - 'shopping_cart_rules' => - [ - ['discount_amount' => 100], - ], + [ + 'sku' => 'simple1', + 'price' => 2542.37, + 'qty' => 2, + ] + ], + 'shipping_method' => 'free', + 'shopping_cart_rules' => + [ + ['discount_amount' => 100], + ], ], 'expected_result' => [ - 'address_data' => - [ - 'subtotal' => 5084.74, - 'base_subtotal' => 5084.74, - 'subtotal_incl_tax' => 5999.99, - 'base_subtotal_incl_tax' => 5999.99, - 'tax_amount' => 915.25, - 'base_tax_amount' => 915.25, - 'shipping_amount' => 0, - 'base_shipping_amount' => 0, - 'shipping_incl_tax' => 0, - 'base_shipping_incl_tax' => 0, - 'shipping_tax_amount' => 0, - 'base_shipping_tax_amount' => 0, - 'discount_amount' => -5999.99, - 'base_discount_amount' => -5999.99, - 'discount_tax_compensation_amount' => 0, - 'base_discount_tax_compensation_amount' => 0, - 'shipping_discount_tax_compensation_amount' => 0, - 'base_shipping_discount_tax_compensation_amount' => 0, - 'grand_total' => 0, - 'base_grand_total' => 0, - 'applied_taxes' => - [ - SetupUtil::TAX_RATE_TX => - [ - 'percent' => 18, - 'amount' => 915.25, - 'base_amount' => 915.25, - 'rates' => - [ + 'address_data' => + [ + 'subtotal' => 5084.74, + 'base_subtotal' => 5084.74, + 'subtotal_incl_tax' => 5999.99, + 'base_subtotal_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'shipping_amount' => 0, + 'base_shipping_amount' => 0, + 'shipping_incl_tax' => 0, + 'base_shipping_incl_tax' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'discount_amount' => -5999.99, + 'base_discount_amount' => -5999.99, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + 'grand_total' => 0, + 'base_grand_total' => 0, + 'applied_taxes' => + [ + SetupUtil::TAX_RATE_TX => + [ + 'percent' => 18, + 'amount' => 915.25, + 'base_amount' => 915.25, + 'rates' => [ - 'code' => SetupUtil::TAX_RATE_TX, - 'title' => SetupUtil::TAX_RATE_TX, - 'percent' => 18, + [ + 'code' => SetupUtil::TAX_RATE_TX, + 'title' => SetupUtil::TAX_RATE_TX, + 'percent' => 18, + ], ], - ], - ] - ], - ], - 'items_data' => - [ - 'simple1' => - [ - 'row_total' => 5084.74, - 'base_row_total' => 5084.74, - 'tax_percent' => 18, - 'price' => 2542.37, - 'base_price' => 2542.37, - 'price_incl_tax' => 3000, - 'base_price_incl_tax' => 3000, - 'row_total_incl_tax' => 5999.99, - 'base_row_total_incl_tax' => 5999.99, - 'tax_amount' => 915.25, - 'base_tax_amount' => 915.25, - 'discount_amount' => 5999.99, - 'base_discount_amount' => 5999.99, - 'discount_percent' => 100, - 'discount_tax_compensation_amount' => 0, - 'base_discount_tax_compensation_amount' => 0, - ], - ], + ] + ], + ], + 'items_data' => + [ + 'simple1' => + [ + 'row_total' => 5084.74, + 'base_row_total' => 5084.74, + 'tax_percent' => 18, + 'price' => 2542.37, + 'base_price' => 2542.37, + 'price_incl_tax' => 3000, + 'base_price_incl_tax' => 3000, + 'row_total_incl_tax' => 5999.99, + 'base_row_total_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'discount_amount' => 5999.99, + 'base_discount_amount' => 5999.99, + 'discount_percent' => 100, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + ], + ], ] ]; From cd3ad3b1cd7f004e961f377a14caa586dbc585d2 Mon Sep 17 00:00:00 2001 From: Yuriy Tkachenko Date: Wed, 6 Jun 2018 22:40:56 +0300 Subject: [PATCH 0040/1171] Change exception type and add log entry --- .../ObjectManager/Factory/AbstractFactory.php | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php index 960e7890971b2..e749e881b16b6 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php @@ -5,7 +5,9 @@ */ namespace Magento\Framework\ObjectManager\Factory; +use Magento\Framework\Exception\RuntimeException; use Magento\Framework\ObjectManagerInterface; +use Psr\Log\LoggerInterface; abstract class AbstractFactory implements \Magento\Framework\ObjectManager\FactoryInterface { @@ -111,10 +113,27 @@ protected function createObject($type, $args) try { return new $type(...array_values($args)); } catch (\Throwable $e) { - throw new \Exception($e->getMessage(), null, $e); + $this->getLogger()->critical(__( + 'Object create error: %1, %2', + [ + $type, + $e->getMessage() + ] + )); + throw new RuntimeException(__($e->getMessage())); } } + /** + * Logger Instance + * + * @return LoggerInterface + */ + protected function getLogger() + { + return $this->objectManager->get(LoggerInterface::class); + } + /** * Resolve an argument * From 93206e04751e228f4984614237ebd593758698cd Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sun, 3 Jun 2018 11:12:55 -0400 Subject: [PATCH 0041/1171] Add CLI Command for Updating CC Number Encryption Updates the encryption of any values in `sales_order_payment.cc_number_enc` with a cipher version below the latest. The command executes in batches of 1000 --- .../Setup/Patch/Data/SodiumChachaPatch.php | 13 ++-- .../EncryptionPaymentDataUpdateCommand.php | 66 +++++++++++++++++++ .../Order/Payment/EncryptionUpdate.php | 65 ++++++++++++++++++ app/code/Magento/Sales/etc/di.xml | 7 ++ 4 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/Sales/Console/Command/EncryptionPaymentDataUpdateCommand.php create mode 100644 app/code/Magento/Sales/Model/ResourceModel/Order/Payment/EncryptionUpdate.php diff --git a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php index 69bc47d2c6732..a6980d60c1679 100644 --- a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php +++ b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php @@ -83,10 +83,15 @@ private function reEncryptSystemConfigurationValues() $this->scope->setCurrentScope(\Magento\Framework\App\Area::AREA_ADMINHTML); - $paths = $this->structure->getFieldPathsByAttribute( - 'backend_model', - \Magento\Config\Model\Config\Backend\Encrypted::class - ); + try { + $paths = $this->structure->getFieldPathsByAttribute( + 'backend_model', + \Magento\Config\Model\Config\Backend\Encrypted::class + ); + } catch (\Exception $e) { + // This is thrown during initial application installation + return; + } $this->scope->setCurrentScope($currentScope); diff --git a/app/code/Magento/Sales/Console/Command/EncryptionPaymentDataUpdateCommand.php b/app/code/Magento/Sales/Console/Command/EncryptionPaymentDataUpdateCommand.php new file mode 100644 index 0000000000000..47ef24c542c2a --- /dev/null +++ b/app/code/Magento/Sales/Console/Command/EncryptionPaymentDataUpdateCommand.php @@ -0,0 +1,66 @@ +paymentResource = $paymentResource; + parent::__construct(); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->setName(self::NAME) + ->setDescription( + 'Re-encrypts encrypted credit card data with latest encryption cipher.' + ); + parent::configure(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + $this->paymentResource->reEncryptCreditCardNumbers(); + } catch (\Exception $e) { + $output->writeln('' . $e->getMessage() . ''); + return Cli::RETURN_FAILURE; + } + + return Cli::RETURN_SUCCESS; + } +} diff --git a/app/code/Magento/Sales/Model/ResourceModel/Order/Payment/EncryptionUpdate.php b/app/code/Magento/Sales/Model/ResourceModel/Order/Payment/EncryptionUpdate.php new file mode 100644 index 0000000000000..4cbcfe49f4cc9 --- /dev/null +++ b/app/code/Magento/Sales/Model/ResourceModel/Order/Payment/EncryptionUpdate.php @@ -0,0 +1,65 @@ +paymentResource = $paymentResource; + $this->encryptor = $encryptor; + } + + /** + * Fetch encrypted credit card numbers using legacy ciphers and re-encrypt with latest cipher + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function reEncryptCreditCardNumbers() + { + $connection = $this->paymentResource->getConnection(); + $table = $this->paymentResource->getMainTable(); + $select = $connection->select()->from($table, ['entity_id', 'cc_number_enc']) + ->where( + 'cc_number_enc REGEXP ?', + sprintf(self::LEGACY_PATTERN, \Magento\Framework\Encryption\Encryptor::CIPHER_LATEST) + )->limit(1000); + + while ($attributeValues = $connection->fetchPairs($select)) { + // save new values + foreach ($attributeValues as $valueId => $value) { + $connection->update( + $table, + ['cc_number_enc' => $this->encryptor->encrypt($this->encryptor->decrypt($value))], + ['entity_id = ?' => (int)$valueId, 'cc_number_enc = ?' => (string)$value] + ); + } + } + } +} diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index c1dc3af859d1a..3e19541c21d1e 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -974,6 +974,13 @@ + + + + Magento\Sales\Console\Command\EncryptionPaymentDataUpdateCommand + + + From 3ab339b6cd7c596364efc92557d460b2670c9120 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Mon, 4 Jun 2018 13:19:42 -0400 Subject: [PATCH 0042/1171] Fix property assignment in Mcrypt adapter --- lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php index 8e4859e727f86..c1b5f0174f97b 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php @@ -68,7 +68,7 @@ public function __construct( ) ); } - $this->_initVector = $initVector; + $this->initVector = $initVector; } catch (\Exception $e) { // @codingStandardIgnoreLine @mcrypt_module_close($this->handle); From 0d90f008a741b7fb76d65f69903095acfc8a4e3b Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Wed, 6 Jun 2018 22:26:03 -0400 Subject: [PATCH 0043/1171] Return empty array if sections key is empty The `sections` key may not be set during module installation or upgrade and would result in an undefined index notice. --- app/code/Magento/Config/Model/Config/Structure.php | 4 ++++ .../Setup/Patch/Data/SodiumChachaPatch.php | 13 ++++--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Config/Model/Config/Structure.php b/app/code/Magento/Config/Model/Config/Structure.php index 5a6dbc8e31896..5c74220051ba9 100644 --- a/app/code/Magento/Config/Model/Config/Structure.php +++ b/app/code/Magento/Config/Model/Config/Structure.php @@ -281,6 +281,10 @@ protected function _createEmptyElement(array $pathParts) public function getFieldPathsByAttribute($attributeName, $attributeValue) { $result = []; + if (empty($this->_data['sections'])) { + return $result; + } + foreach ($this->_data['sections'] as $section) { if (!isset($section['children'])) { continue; diff --git a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php index a6980d60c1679..69bc47d2c6732 100644 --- a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php +++ b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php @@ -83,15 +83,10 @@ private function reEncryptSystemConfigurationValues() $this->scope->setCurrentScope(\Magento\Framework\App\Area::AREA_ADMINHTML); - try { - $paths = $this->structure->getFieldPathsByAttribute( - 'backend_model', - \Magento\Config\Model\Config\Backend\Encrypted::class - ); - } catch (\Exception $e) { - // This is thrown during initial application installation - return; - } + $paths = $this->structure->getFieldPathsByAttribute( + 'backend_model', + \Magento\Config\Model\Config\Backend\Encrypted::class + ); $this->scope->setCurrentScope($currentScope); From ae3f45498e379472d3875c6480cf1be2074c99c4 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Wed, 6 Jun 2018 23:03:51 -0400 Subject: [PATCH 0044/1171] Refactor Crypt class to use Mcrypt Adapter Removes code duplication between `Crypt` and `Mcrpyt` classes. `Mcrpyt` is used for all equivalent `Crypt` methods except `Crypt::encrypt` becuase `Mcrypt::encrpt` throws an exception preventing it's use. --- .../Framework/Encryption/Adapter/Mcrypt.php | 12 +++ .../Magento/Framework/Encryption/Crypt.php | 98 +++++-------------- .../Encryption/Test/Unit/CryptTest.php | 5 +- 3 files changed, 41 insertions(+), 74 deletions(-) diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php index c1b5f0174f97b..61cebac0c6e4b 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php @@ -25,6 +25,8 @@ class Mcrypt implements EncryptionAdapterInterface private $initVector; /** + * Encryption algorithm module handle + * * @var resource */ private $handle; @@ -119,6 +121,16 @@ public function getInitVector(): ?string return $this->initVector; } + /** + * Get the current mcrypt handle + * + * @return resource + */ + public function getHandle() + { + return $this->handle; + } + /** * Encrypt a string * diff --git a/lib/internal/Magento/Framework/Encryption/Crypt.php b/lib/internal/Magento/Framework/Encryption/Crypt.php index d5d7ea27b5036..29f4397dec94b 100644 --- a/lib/internal/Magento/Framework/Encryption/Crypt.php +++ b/lib/internal/Magento/Framework/Encryption/Crypt.php @@ -32,11 +32,11 @@ class Crypt protected $_initVector; /** - * Encryption algorithm module handle + * Mcrypt adapter * - * @var resource + * @var \Magento\Framework\Encryption\Adapter\Mcrypt */ - protected $_handle; + private $mcrypt; /** * Constructor @@ -56,64 +56,30 @@ public function __construct( $mode = MCRYPT_MODE_ECB, $initVector = false ) { - $this->_cipher = $cipher; - $this->_mode = $mode; - // @codingStandardsIgnoreStart - $this->_handle = @mcrypt_module_open($cipher, '', $mode, ''); - // @codingStandardsIgnoreEnd - try { + if (true === $initVector) { // @codingStandardsIgnoreStart - $maxKeySize = @mcrypt_enc_get_key_size($this->_handle); + $handle = @mcrypt_module_open($cipher, '', $mode, ''); + $initVectorSize = @mcrypt_enc_get_iv_size($handle); // @codingStandardsIgnoreEnd - if (strlen((string)$key) > $maxKeySize) { - throw new \Magento\Framework\Exception\LocalizedException( - new \Magento\Framework\Phrase('Key must not exceed %1 bytes.', [$maxKeySize]) - ); - } - // @codingStandardsIgnoreStart - $initVectorSize = @mcrypt_enc_get_iv_size($this->_handle); - // @codingStandardsIgnoreEnd - if (true === $initVector) { - /* Generate a random vector from human-readable characters */ - $abc = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; - $initVector = ''; - for ($i = 0; $i < $initVectorSize; $i++) { - $initVector .= $abc[rand(0, strlen($abc) - 1)]; - } - } elseif (false === $initVector) { - /* Set vector to zero bytes to not use it */ - $initVector = str_repeat("\0", $initVectorSize); - } elseif (!is_string($initVector) || strlen($initVector) != $initVectorSize) { - throw new \Magento\Framework\Exception\LocalizedException( - new \Magento\Framework\Phrase( - 'Init vector must be a string of %1 bytes.', - [$initVectorSize] - ) - ); + + /* Generate a random vector from human-readable characters */ + $allowedCharacters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + $initVector = ''; + for ($i = 0; $i < $initVectorSize; $i++) { + $initVector .= $allowedCharacters[random_int(0, strlen($allowedCharacters) - 1)]; } - $this->_initVector = $initVector; - } catch (\Exception $e) { // @codingStandardsIgnoreStart - @mcrypt_module_close($this->_handle); + @mcrypt_generic_deinit($handle); + @mcrypt_module_close($handle); // @codingStandardsIgnoreEnd - throw $e; } - // @codingStandardsIgnoreStart - @mcrypt_generic_init($this->_handle, $key, $initVector); - // @codingStandardsIgnoreEnd - } - /** - * Destructor frees allocated resources - */ - public function __destruct() - { - // @codingStandardsIgnoreStart - @mcrypt_generic_deinit($this->_handle); - // @codingStandardsIgnoreEnd - // @codingStandardsIgnoreStart - @mcrypt_module_close($this->_handle); - // @codingStandardsIgnoreEnd + $this->mcrypt = new \Magento\Framework\Encryption\Adapter\Mcrypt( + $key, + $cipher, + $mode, + $initVector === false ? null : $initVector + ); } /** @@ -123,7 +89,7 @@ public function __destruct() */ public function getCipher() { - return $this->_cipher; + return $this->mcrypt->getCipher(); } /** @@ -133,7 +99,7 @@ public function getCipher() */ public function getMode() { - return $this->_mode; + return $this->mcrypt->getMode(); } /** @@ -143,7 +109,7 @@ public function getMode() */ public function getInitVector() { - return $this->_initVector; + return $this->mcrypt->getInitVector(); } /** @@ -157,9 +123,8 @@ public function encrypt($data) if (strlen($data) == 0) { return $data; } - // @codingStandardsIgnoreStart - return @mcrypt_generic($this->_handle, $data); - // @codingStandardsIgnoreEnd + // @codingStandardsIgnoreLine + return @mcrypt_generic($this->mcrypt->getHandle(), $data); } /** @@ -170,17 +135,6 @@ public function encrypt($data) */ public function decrypt($data) { - if (strlen($data) == 0) { - return $data; - } - // @codingStandardsIgnoreStart - $data = @mdecrypt_generic($this->_handle, $data); - // @codingStandardsIgnoreEnd - /* - * Returned string can in fact be longer than the unencrypted string due to the padding of the data - * @link http://www.php.net/manual/en/function.mdecrypt-generic.php - */ - $data = rtrim($data, "\0"); - return $data; + return $this->mcrypt->decrypt($data); } } diff --git a/lib/internal/Magento/Framework/Encryption/Test/Unit/CryptTest.php b/lib/internal/Magento/Framework/Encryption/Test/Unit/CryptTest.php index 562f01abeefd1..dbaf76cd6bade 100644 --- a/lib/internal/Magento/Framework/Encryption/Test/Unit/CryptTest.php +++ b/lib/internal/Magento/Framework/Encryption/Test/Unit/CryptTest.php @@ -85,6 +85,7 @@ public function testConstructor($cipher, $mode) public function getConstructorExceptionData() { + $key = substr(__CLASS__, -32, 32); $result = []; foreach (self::SUPPORTED_CIPHER_MODE_COMBINATIONS as $cipher => $modes) { /** @var array $modes */ @@ -94,8 +95,8 @@ public function getConstructorExceptionData() $tooLongInitVector = str_repeat('-', $this->_getInitVectorSize($cipher, $mode) + 1); $result['tooLongKey-' . $cipher . '-' . $mode . '-false'] = [$tooLongKey, $cipher, $mode, false]; $keyPrefix = 'key-' . $cipher . '-' . $mode; - $result[$keyPrefix . '-tooShortInitVector'] = [$this->_key, $cipher, $mode, $tooShortInitVector]; - $result[$keyPrefix . '-tooLongInitVector'] = [$this->_key, $cipher, $mode, $tooLongInitVector]; + $result[$keyPrefix . '-tooShortInitVector'] = [$key, $cipher, $mode, $tooShortInitVector]; + $result[$keyPrefix . '-tooLongInitVector'] = [$key, $cipher, $mode, $tooLongInitVector]; } } return $result; From 5791e4a024db787ddcc025db21c006e9aed98207 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Wed, 6 Jun 2018 23:13:46 -0400 Subject: [PATCH 0045/1171] Include Cipher In Sodium classname Unlike mcrypt, the sodium extension uses a different [en|de]crypt function for each avaiable cipher. Including the cipher in the class name provides a method for changing the chosen sodium cipher. --- .../Encryption/Adapter/{Sodium.php => SodiumChachaIetf.php} | 4 +--- lib/internal/Magento/Framework/Encryption/Encryptor.php | 6 +++--- .../Framework/Encryption/Test/Unit/EncryptorTest.php | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) rename lib/internal/Magento/Framework/Encryption/Adapter/{Sodium.php => SodiumChachaIetf.php} (93%) diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php b/lib/internal/Magento/Framework/Encryption/Adapter/SodiumChachaIetf.php similarity index 93% rename from lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php rename to lib/internal/Magento/Framework/Encryption/Adapter/SodiumChachaIetf.php index ea242caeafa15..36fc388498ba2 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Sodium.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/SodiumChachaIetf.php @@ -4,12 +4,10 @@ namespace Magento\Framework\Encryption\Adapter; -use Magento\Framework\Encryption\Encryptor; - /** * Sodium adapter for encrypting and decrypting strings */ -class Sodium implements EncryptionAdapterInterface +class SodiumChachaIetf implements EncryptionAdapterInterface { /** * @var string diff --git a/lib/internal/Magento/Framework/Encryption/Encryptor.php b/lib/internal/Magento/Framework/Encryption/Encryptor.php index eb73065696711..f7d52d5474ad7 100644 --- a/lib/internal/Magento/Framework/Encryption/Encryptor.php +++ b/lib/internal/Magento/Framework/Encryption/Encryptor.php @@ -12,7 +12,7 @@ use Magento\Framework\Encryption\Adapter\EncryptionAdapterInterface; use Magento\Framework\Encryption\Helper\Security; use Magento\Framework\Math\Random; -use Magento\Framework\Encryption\Adapter\Sodium; +use Magento\Framework\Encryption\Adapter\SodiumChachaIetf; use Magento\Framework\Encryption\Adapter\Mcrypt; /** @@ -274,7 +274,7 @@ private function getPasswordVersion() */ public function encrypt($data) { - $crypt = new Sodium($this->keys[$this->keyVersion]); + $crypt = new SodiumChachaIetf($this->keys[$this->keyVersion]); return $this->keyVersion . ':' . self::CIPHER_AEAD_CHACHA20POLY1305 . @@ -408,7 +408,7 @@ private function getCrypt( $cipherVersion = $this->validateCipher($cipherVersion); if ($cipherVersion >= self::CIPHER_AEAD_CHACHA20POLY1305) { - return new Sodium($key); + return new SodiumChachaIetf($key); } if ($cipherVersion === self::CIPHER_RIJNDAEL_128) { diff --git a/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php b/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php index 14ac0ed8b8f96..9571baae95e1b 100644 --- a/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php +++ b/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php @@ -9,7 +9,7 @@ namespace Magento\Framework\Encryption\Test\Unit; use Magento\Framework\Encryption\Adapter\Mcrypt; -use Magento\Framework\Encryption\Adapter\Sodium; +use Magento\Framework\Encryption\Adapter\SodiumChachaIetf; use Magento\Framework\Encryption\Encryptor; use Magento\Framework\Encryption\Crypt; use Magento\Framework\App\DeploymentConfig; @@ -159,7 +159,7 @@ public function testEncrypt() $parts = explode(':', $actual, 3); list(, , $encryptedData) = $parts; - $crypt = new Sodium(self::CRYPT_KEY_1); + $crypt = new SodiumChachaIetf(self::CRYPT_KEY_1); // Verify decrypted matches original data $this->assertEquals($data, $crypt->decrypt(base64_decode((string)$encryptedData))); } From 3eed731c22f99b18a161de68e5d4a434d9b57cf7 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv Date: Thu, 7 Jun 2018 10:43:26 +0300 Subject: [PATCH 0046/1171] #10790 --- .../Tax/_files/full_discount_with_tax.php | 52 +++++++------------ 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php index 7be414e046ddf..ecdbb3c6f3b1f 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php @@ -8,51 +8,42 @@ $fullTaxDiscountWithTax = [ 'config_data' => [ - 'config_overrides' => - [ + 'config_overrides' => [ Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1, Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION', Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, ], - 'tax_rate_overrides' => - [ + 'tax_rate_overrides' => [ SetupUtil::TAX_RATE_TX => 18, SetupUtil::TAX_RATE_SHIPPING => 0, ], - 'tax_rule_overrides' => - [ + 'tax_rule_overrides' => [ [ 'code' => 'Product Tax Rule', - 'product_tax_class_ids' => - [ + 'product_tax_class_ids' => [ SetupUtil::PRODUCT_TAX_CLASS_1 ], ], [ 'code' => 'Shipping Tax Rule', - 'product_tax_class_ids' => - [ + 'product_tax_class_ids' => [ SetupUtil::SHIPPING_TAX_CLASS ], - 'tax_rate_ids' => - [ + 'tax_rate_ids' => [ SetupUtil::TAX_RATE_SHIPPING, ], ], ], ], 'quote_data' => [ - 'billing_address' => - [ + 'billing_address' => [ 'region_id' => SetupUtil::REGION_TX, ], - 'shipping_address' => - [ + 'shipping_address' => [ 'region_id' => SetupUtil::REGION_TX, ], - 'items' => - [ + 'items' => [ [ 'sku' => 'simple1', 'price' => 2542.37, @@ -60,14 +51,14 @@ ] ], 'shipping_method' => 'free', - 'shopping_cart_rules' => - [ - ['discount_amount' => 100], + 'shopping_cart_rules' => [ + [ + 'discount_amount' => 100 + ], ], ], 'expected_result' => [ - 'address_data' => - [ + 'address_data' => [ 'subtotal' => 5084.74, 'base_subtotal' => 5084.74, 'subtotal_incl_tax' => 5999.99, @@ -88,15 +79,12 @@ 'base_shipping_discount_tax_compensation_amount' => 0, 'grand_total' => 0, 'base_grand_total' => 0, - 'applied_taxes' => - [ - SetupUtil::TAX_RATE_TX => - [ + 'applied_taxes' => [ + SetupUtil::TAX_RATE_TX => [ 'percent' => 18, 'amount' => 915.25, 'base_amount' => 915.25, - 'rates' => - [ + 'rates' => [ [ 'code' => SetupUtil::TAX_RATE_TX, 'title' => SetupUtil::TAX_RATE_TX, @@ -106,10 +94,8 @@ ] ], ], - 'items_data' => - [ - 'simple1' => - [ + 'items_data' => [ + 'simple1' => [ 'row_total' => 5084.74, 'base_row_total' => 5084.74, 'tax_percent' => 18, From 801b428172a222599320e6a3a449832247265b49 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv Date: Thu, 7 Jun 2018 11:32:03 +0300 Subject: [PATCH 0047/1171] #10790 --- .../Magento/Tax/Model/Sales/Total/Quote/TaxTest.php | 8 ++++---- .../Magento/Tax/_files/full_discount_with_tax.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php index 1b07d49678550..2ba63a21aac38 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php @@ -122,10 +122,10 @@ public function testCollect() */ public function testFullDiscountWithDeltaRoundingFix() { - global $fullTaxDiscountWithTax; - $configData = $fullTaxDiscountWithTax['config_data']; - $quoteData = $fullTaxDiscountWithTax['quote_data']; - $expectedResults = $fullTaxDiscountWithTax['expected_result']; + global $fullDiscountIncTax; + $configData = $fullDiscountIncTax['config_data']; + $quoteData = $fullDiscountIncTax['quote_data']; + $expectedResults = $fullDiscountIncTax['expected_result']; /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ $objectManager = Bootstrap::getObjectManager(); diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php index ecdbb3c6f3b1f..335390d39006a 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php @@ -6,7 +6,7 @@ use Magento\Tax\Model\Config; use Magento\Tax\Model\Sales\Total\Quote\SetupUtil; -$fullTaxDiscountWithTax = [ +$fullDiscountIncTax = [ 'config_data' => [ 'config_overrides' => [ Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, From 1610ed3d6e0ae24b794fa4a728e6ea6897b54992 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv Date: Sat, 9 Jun 2018 14:12:48 +0300 Subject: [PATCH 0048/1171] #10790 --- .../testsuite/Magento/Tax/_files/full_discount_with_tax.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php index 335390d39006a..2b5ef07de341a 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + use Magento\Tax\Model\Config; use Magento\Tax\Model\Sales\Total\Quote\SetupUtil; From 85e6dee6c8c7194ac64da5b1c408f094ef5748da Mon Sep 17 00:00:00 2001 From: Yuriy Tkachenko Date: Thu, 14 Jun 2018 14:41:56 +0300 Subject: [PATCH 0049/1171] Refactor logger instance usage --- .../ObjectManager/Factory/AbstractFactory.php | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php index e749e881b16b6..88f1f29e9b3ab 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php @@ -7,7 +7,9 @@ use Magento\Framework\Exception\RuntimeException; use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Phrase; use Psr\Log\LoggerInterface; +use Magento\Framework\App\ObjectManager; abstract class AbstractFactory implements \Magento\Framework\ObjectManager\FactoryInterface { @@ -106,34 +108,25 @@ public function getDefinitions() * @param array $args * * @return object - * @throws \Exception + * @throws RuntimeException */ protected function createObject($type, $args) { try { return new $type(...array_values($args)); } catch (\Throwable $e) { - $this->getLogger()->critical(__( - 'Object create error: %1, %2', - [ - $type, - $e->getMessage() - ] - )); - throw new RuntimeException(__($e->getMessage())); + /** @var LoggerInterface $logger */ + $logger = ObjectManager::getInstance()->get(LoggerInterface::class); + $logger->critical( + sprintf('Create object error: %s, %s', $type, $e->getMessage()) + ); + + throw new RuntimeException( + new Phrase('Create object error') + ); } } - /** - * Logger Instance - * - * @return LoggerInterface - */ - protected function getLogger() - { - return $this->objectManager->get(LoggerInterface::class); - } - /** * Resolve an argument * From 1159d685d0113b9c791bdf1d537a6bed88768c14 Mon Sep 17 00:00:00 2001 From: Max Lesechko Date: Thu, 14 Jun 2018 13:44:09 -0500 Subject: [PATCH 0050/1171] MAGETWO-91433: ProductListing: Grid view is getting changed to List view when user adding product from wishlist section. --- .../Wishlist/Controller/Index/Remove.php | 19 +++++++++++-------- .../Wishlist/CustomerData/Wishlist.php | 4 ++-- app/code/Magento/Wishlist/Helper/Data.php | 6 ++++++ .../Test/Unit/Controller/Index/RemoveTest.php | 14 +++----------- .../Test/Unit/CustomerData/WishlistTest.php | 8 ++++---- .../Wishlist/Test/Unit/Helper/DataTest.php | 3 ++- app/code/Magento/Wishlist/etc/frontend/di.xml | 6 ++++++ .../removeWishlistItemSuccessMessage.phtml | 11 +++++++++++ 8 files changed, 45 insertions(+), 26 deletions(-) create mode 100644 app/code/Magento/Wishlist/view/frontend/templates/messages/removeWishlistItemSuccessMessage.phtml diff --git a/app/code/Magento/Wishlist/Controller/Index/Remove.php b/app/code/Magento/Wishlist/Controller/Index/Remove.php index ec45201e390f1..6553b886773c0 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Remove.php +++ b/app/code/Magento/Wishlist/Controller/Index/Remove.php @@ -10,6 +10,7 @@ use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Controller\ResultFactory; use Magento\Wishlist\Controller\WishlistProviderInterface; +use Magento\Wishlist\Model\Item; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -56,7 +57,8 @@ public function execute() } $id = (int)$this->getRequest()->getParam('item'); - $item = $this->_objectManager->create(\Magento\Wishlist\Model\Item::class)->load($id); + /** @var Item $item */ + $item = $this->_objectManager->create(Item::class)->load($id); if (!$item->getId()) { throw new NotFoundException(__('Page not found.')); } @@ -67,6 +69,12 @@ public function execute() try { $item->delete(); $wishlist->save(); + $this->messageManager->addComplexSuccessMessage( + 'removeWishlistItemSuccessMessage', + [ + 'product_name' => $item->getProduct()->getName() + ] + ); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addError( __('We can\'t delete the item from Wish List right now because of an error: %1.', $e->getMessage()) @@ -76,13 +84,8 @@ public function execute() } $this->_objectManager->get(\Magento\Wishlist\Helper\Data::class)->calculate(); - $request = $this->getRequest(); - $refererUrl = (string)$request->getServer('HTTP_REFERER'); - $url = (string)$request->getParam(\Magento\Framework\App\Response\RedirectInterface::PARAM_NAME_REFERER_URL); - if ($url) { - $refererUrl = $url; - } - if ($request->getParam(\Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED) && $refererUrl) { + $refererUrl = $this->_redirect->getRefererUrl(); + if ($refererUrl) { $redirectUrl = $refererUrl; } else { $redirectUrl = $this->_redirect->getRedirectUrl($this->_url->getUrl('*/*')); diff --git a/app/code/Magento/Wishlist/CustomerData/Wishlist.php b/app/code/Magento/Wishlist/CustomerData/Wishlist.php index 85aff60ac6370..f9b257969f094 100644 --- a/app/code/Magento/Wishlist/CustomerData/Wishlist.php +++ b/app/code/Magento/Wishlist/CustomerData/Wishlist.php @@ -135,8 +135,8 @@ protected function getItemData(\Magento\Wishlist\Model\Item $wishlistItem) ), 'product_is_saleable_and_visible' => $product->isSaleable() && $product->isVisibleInSiteVisibility(), 'product_has_required_options' => $product->getTypeInstance()->hasRequiredOptions($product), - 'add_to_cart_params' => $this->wishlistHelper->getAddToCartParams($wishlistItem, true), - 'delete_item_params' => $this->wishlistHelper->getRemoveParams($wishlistItem, true), + 'add_to_cart_params' => $this->wishlistHelper->getAddToCartParams($wishlistItem), + 'delete_item_params' => $this->wishlistHelper->getRemoveParams($wishlistItem), ]; } diff --git a/app/code/Magento/Wishlist/Helper/Data.php b/app/code/Magento/Wishlist/Helper/Data.php index 968a7f1db8c39..f4c1aa9662bda 100644 --- a/app/code/Magento/Wishlist/Helper/Data.php +++ b/app/code/Magento/Wishlist/Helper/Data.php @@ -284,9 +284,12 @@ public function getRemoveParams($item, $addReferer = false) { $url = $this->_getUrl('wishlist/index/remove'); $params = ['item' => $item->getWishlistItemId()]; + $params[ActionInterface::PARAM_NAME_URL_ENCODED] = ''; + if ($addReferer) { $params = $this->addRefererToParams($params); } + return $this->_postDataHelper->getPostData($url, $params); } @@ -395,9 +398,12 @@ public function getAddToCartUrl($item) public function getAddToCartParams($item, $addReferer = false) { $params = $this->_getCartUrlParameters($item); + $params[ActionInterface::PARAM_NAME_URL_ENCODED] = ''; + if ($addReferer) { $params = $this->addRefererToParams($params); } + return $this->_postDataHelper->getPostData( $this->_getUrlStore($item)->getUrl('wishlist/index/cart'), $params diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/RemoveTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/RemoveTest.php index 2a36d3beb8558..56c9e13672e62 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/RemoveTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/RemoveTest.php @@ -312,13 +312,11 @@ public function testExecuteCanNotSaveWishlist() ->with(\Magento\Wishlist\Model\Item::class) ->willReturn($item); - $this->request - ->expects($this->once()) - ->method('getServer') - ->with('HTTP_REFERER') + $this->redirect + ->method('getRefererUrl') + ->with() ->willReturn($referer); $this->request - ->expects($this->exactly(3)) ->method('getParam') ->willReturnMap( [ @@ -398,12 +396,6 @@ public function testExecuteCanNotSaveWishlistAndWithRedirect() ->willReturn($item); $this->request - ->expects($this->once()) - ->method('getServer') - ->with('HTTP_REFERER') - ->willReturn($referer); - $this->request - ->expects($this->exactly(3)) ->method('getParam') ->willReturnMap( [ diff --git a/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php index d2e81b0236ce8..00fb4a35e1340 100644 --- a/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php @@ -237,11 +237,11 @@ public function testGetSectionData() $this->wishlistHelperMock->expects($this->once()) ->method('getAddToCartParams') - ->with($itemMock, true) + ->with($itemMock) ->willReturn($itemAddParams); $this->wishlistHelperMock->expects($this->once()) ->method('getRemoveParams') - ->with($itemMock, true) + ->with($itemMock) ->willReturn($itemRemoveParams); $this->assertEquals($result, $this->model->getSectionData()); @@ -435,11 +435,11 @@ public function testGetSectionDataWithTwoItems() $this->wishlistHelperMock->expects($this->exactly(2)) ->method('getAddToCartParams') - ->with($itemMock, true) + ->with($itemMock) ->willReturn($itemAddParams); $this->wishlistHelperMock->expects($this->exactly(2)) ->method('getRemoveParams') - ->with($itemMock, true) + ->with($itemMock) ->willReturn($itemRemoveParams); $this->assertEquals($result, $this->model->getSectionData()); diff --git a/app/code/Magento/Wishlist/Test/Unit/Helper/DataTest.php b/app/code/Magento/Wishlist/Test/Unit/Helper/DataTest.php index 154db1c4a1474..1769306172aab 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Helper/DataTest.php @@ -248,6 +248,7 @@ public function testGetAddToCartParams() $expected = [ 'item' => $wishlistItemId, 'qty' => $wishlistItemQty, + ActionInterface::PARAM_NAME_URL_ENCODED => '', ]; $this->postDataHelper->expects($this->once()) ->method('getPostData') @@ -333,7 +334,7 @@ public function testGetRemoveParams() $this->postDataHelper->expects($this->once()) ->method('getPostData') - ->with($url, ['item' => $wishlistItemId]) + ->with($url, ['item' => $wishlistItemId, ActionInterface::PARAM_NAME_URL_ENCODED => '']) ->willReturn($url); $this->assertEquals($url, $this->model->getRemoveParams($this->wishlistItem)); diff --git a/app/code/Magento/Wishlist/etc/frontend/di.xml b/app/code/Magento/Wishlist/etc/frontend/di.xml index 00642b132bfdf..f28e85fff0a15 100644 --- a/app/code/Magento/Wishlist/etc/frontend/di.xml +++ b/app/code/Magento/Wishlist/etc/frontend/di.xml @@ -44,6 +44,12 @@ Magento_Wishlist::messages/addProductSuccessMessage.phtml + + \Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE + + Magento_Wishlist::messages/removeWishlistItemSuccessMessage.phtml + + diff --git a/app/code/Magento/Wishlist/view/frontend/templates/messages/removeWishlistItemSuccessMessage.phtml b/app/code/Magento/Wishlist/view/frontend/templates/messages/removeWishlistItemSuccessMessage.phtml new file mode 100644 index 0000000000000..1196b8fec4a21 --- /dev/null +++ b/app/code/Magento/Wishlist/view/frontend/templates/messages/removeWishlistItemSuccessMessage.phtml @@ -0,0 +1,11 @@ + + +escapeHtml(__('%1 has been removed from your Wish List.', $block->getData('product_name'))); ?> + From ee49878f3ccd89d9b4c897769229a119c3dc8138 Mon Sep 17 00:00:00 2001 From: Cristian Partica Date: Thu, 14 Jun 2018 17:24:55 -0500 Subject: [PATCH 0051/1171] MAGETWO-91504: Mobile PDP accordion widget hides accordion content on phones with iOS - fix scroll out of screen --- lib/web/mage/collapsible.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/mage/collapsible.js b/lib/web/mage/collapsible.js index 5f3a654649487..e6c3976faf891 100644 --- a/lib/web/mage/collapsible.js +++ b/lib/web/mage/collapsible.js @@ -448,6 +448,7 @@ define([ if (this.options.animate) { this._animate(showProps); } else { + this.content.get(0).parentElement.scrollIntoView(); this.content.show(); } this._open(); From 64d0927b21502ff9f9b0622e0883abe49a36e4a1 Mon Sep 17 00:00:00 2001 From: David Manners Date: Sat, 16 Jun 2018 11:36:24 +0000 Subject: [PATCH 0052/1171] magento-engcom/import-export-improvements#64: fix issue with Export Type UI - reset the entity type in the export form JS - this change was made so that if you choose an entity type, then choose the "Please Select" option the entity type gets reset and so now if you choose another type the form will actually be shown --- .../view/adminhtml/templates/export/form/before.phtml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/before.phtml b/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/before.phtml index 093830d6fdb1c..fbdd394783608 100644 --- a/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/before.phtml +++ b/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/before.phtml @@ -64,6 +64,7 @@ require([ this.modifyFilterGrid(); } } else { + this.previousGridEntity = ''; $('export_filter_container').hide(); } } From 4ba2c46dd52149339c0baf4edbf1069cadc5cd0d Mon Sep 17 00:00:00 2001 From: David Manners Date: Sat, 16 Jun 2018 12:56:42 +0000 Subject: [PATCH 0053/1171] magento-engcom/import-export-improvements#44: update Customer sample CSV - update the sample csv for customer to remove columns reward_update_notification and reward_warning_notification - after this PR is merged you will be able to download the sample customer csv and import it directly without getting an error message --- app/code/Magento/ImportExport/Files/Sample/customer.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ImportExport/Files/Sample/customer.csv b/app/code/Magento/ImportExport/Files/Sample/customer.csv index 64c09574aea73..522e59f566739 100644 --- a/app/code/Magento/ImportExport/Files/Sample/customer.csv +++ b/app/code/Magento/ImportExport/Files/Sample/customer.csv @@ -1,2 +1,2 @@ -email,_website,_store,confirmation,created_at,created_in,disable_auto_group_change,dob,firstname,gender,group_id,lastname,middlename,password_hash,prefix,reward_update_notification,reward_warning_notification,rp_token,rp_token_created_at,store_id,suffix,taxvat,updated_at,website_id,password -jondoe@example.com,base,default,,"2015-10-30 12:49:47","Default Store View",0,,Jon,,1,Doe,,d708be3fe0fe0120840e8b13c8faae97424252c6374227ff59c05814f1aecd79:mgLqkqgTwLPLlCljzvF8hp67fNOOvOZb:1,,,,07e71459c137f4da15292134ff459cba,"2015-10-30 12:49:48",1,,,"2015-10-30 12:49:48",1, +email,_website,_store,confirmation,created_at,created_in,disable_auto_group_change,dob,firstname,gender,group_id,lastname,middlename,password_hash,prefix,rp_token,rp_token_created_at,store_id,suffix,taxvat,updated_at,website_id,password +jondoe@example.com,base,default,,"2015-10-30 12:49:47","Default Store View",0,,Jon,,1,Doe,,d708be3fe0fe0120840e8b13c8faae97424252c6374227ff59c05814f1aecd79:mgLqkqgTwLPLlCljzvF8hp67fNOOvOZb:1,,07e71459c137f4da15292134ff459cba,"2015-10-30 12:49:48",1,,,"2015-10-30 12:49:48",1, From 10ce88bf15c8169ed263831e9b7c144064a42bd4 Mon Sep 17 00:00:00 2001 From: David Manners Date: Sat, 16 Jun 2018 13:58:37 +0000 Subject: [PATCH 0054/1171] magento-engcom/import-export-improvements#58: Fix generation of import url - update the building of import url in before.phtml - check that we do not already have the form_key in our url, - build the newActionUrl first so that we always are using the same url to build the url --- .../view/adminhtml/templates/import/form/before.phtml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml b/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml index 73b013fdb8df4..8a52f4ca88e75 100644 --- a/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml +++ b/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml @@ -178,9 +178,13 @@ require([ .loader('show'); var form = jQuery('#edit_form') .one('invalid-form.validate', function(e){jQuery('body').loader('hide')}); - newActionUrl = (newActionUrl ? newActionUrl : form.attr('action')) + - (form.attr('action').lastIndexOf('?') != -1 ? '&' : '?')+ - 'form_key=' + encodeURIComponent(form.find('[name="form_key"]').val()); + + newActionUrl = (newActionUrl ? newActionUrl : form.attr('action')); + if (newActionUrl.lastIndexOf('form_key') === -1) { + newActionUrl = newActionUrl + + (newActionUrl.lastIndexOf('?') !== -1 ? '&' : '?') + + 'form_key=' + encodeURIComponent(form.find('[name="form_key"]').val()); + } form.trigger('save', [{ action: newActionUrl, From b49d2ff51cfad58d6b78cacaedf418aeadbd88ab Mon Sep 17 00:00:00 2001 From: carstenpfeifer Date: Sat, 16 Jun 2018 16:49:22 +0200 Subject: [PATCH 0055/1171] magento-engcom/import-export-improvements#88: Set store id for product category initialization to 0. Would otherwise be null and set to 1 in \Magento\Catalog\Model\ResourceModel\Collection\AbstractCollection::getStoreId, leading to unwanted store values for category names. --- .../Model/Import/Product/CategoryProcessor.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php index 5de9d3880b5d2..db5b07279ed1c 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php @@ -75,6 +75,7 @@ protected function initCategories() $collection->addAttributeToSelect('name') ->addAttributeToSelect('url_key') ->addAttributeToSelect('url_path'); + $collection->setStoreId(\Magento\Store\Model\Store::DEFAULT_STORE_ID); /* @var $collection \Magento\Catalog\Model\ResourceModel\Category\Collection */ foreach ($collection as $category) { $structure = explode(self::DELIMITER_CATEGORY, $category->getPath()); From 8eee7f9f2a58026a04a3e39b85e24d4572a38a12 Mon Sep 17 00:00:00 2001 From: Yuriy Tkachenko Date: Mon, 18 Jun 2018 10:25:18 +0300 Subject: [PATCH 0056/1171] Add integration test --- .../ObjectManager/ObjectManagerTest.php | 20 +++++++++++++++++++ .../TestAsset/ConstructorWithThrowable.php | 18 +++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php index 2f798deecd9d5..4471d798cae21 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php @@ -5,8 +5,15 @@ */ namespace Magento\Framework\ObjectManager; +use Magento\Framework\Exception\RuntimeException; + class ObjectManagerTest extends \PHPUnit\Framework\TestCase { + /**#@+ + * Test class with throwable error + */ + const TEST_CLASS_WITH_THROWABLE = \Magento\Framework\ObjectManager\TestAsset\ConstructorWithThrowable::class; + /**#@+ * Test classes for basic instantiation */ @@ -138,4 +145,17 @@ public function testNewInstance($actualClassName, array $properties = [], $expec } } } + + /** + * Test create instance with throwable error + */ + public function testNewInstanceWithThrowableError() + { + try { + $testObject = self::$_objectManager->create(self::TEST_CLASS_WITH_THROWABLE); + $this->fail('No instance for class with throwable error should be created'); + } catch (\Throwable $e) { + $this->assertInstanceOf(RuntimeException::class, $e); + } + } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php new file mode 100644 index 0000000000000..8e932ae8576d4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php @@ -0,0 +1,18 @@ + Date: Mon, 18 Jun 2018 10:28:25 +0300 Subject: [PATCH 0057/1171] Change copyright for test class --- .../ObjectManager/TestAsset/ConstructorWithThrowable.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php index 8e932ae8576d4..dd3dee10ec3c9 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php +++ b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php @@ -1,13 +1,11 @@ Date: Mon, 18 Jun 2018 10:29:24 +0300 Subject: [PATCH 0058/1171] Change doc comment --- .../ObjectManager/TestAsset/ConstructorWithThrowable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php index dd3dee10ec3c9..f41a52410591f 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php +++ b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php @@ -10,7 +10,7 @@ class ConstructorWithThrowable extends \Magento\Framework\ObjectManager\TestAsse { public function __construct(Basic $one) { - // Call parent constructor without parameters to generate TypeError + // Call parent constructor without parameters to generate error parent::__construct(); } } \ No newline at end of file From bc2a9b58a4e15cd5bd0e33dd768c95866cd2650a Mon Sep 17 00:00:00 2001 From: Max Lesechko Date: Mon, 18 Jun 2018 16:46:41 -0500 Subject: [PATCH 0059/1171] MAGETWO-91433: ProductListing: Grid view is getting changed to List view when user adding product from wishlist section. --- .../StorefrontCategoryActionGroup.xml | 7 ++- .../StorefrontCustomerWishlistActionGroup.xml | 22 ++++++- ...orefrontCustomerWishlistSidebarSection.xml | 1 + ...uctsToCartFromWishlistUsingSidebarTest.xml | 59 +++++++++++++++++++ ...veProductsFromWishlistUsingSidebarTest.xml | 59 +++++++++++++++++++ 5 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/StorefrontCategoryActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/StorefrontCategoryActionGroup.xml index 3b04df7fdd115..a38e260bf35cb 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/StorefrontCategoryActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/StorefrontCategoryActionGroup.xml @@ -32,4 +32,9 @@ - \ No newline at end of file + + + + + + diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/ActionGroup/StorefrontCustomerWishlistActionGroup.xml index 122bfbe4cc1c5..3dead946cc90a 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/ActionGroup/StorefrontCustomerWishlistActionGroup.xml @@ -53,4 +53,24 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Section/StorefrontCustomerWishlistSidebarSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Section/StorefrontCustomerWishlistSidebarSection.xml index d4a81137dbc86..5a59bb07458a9 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Section/StorefrontCustomerWishlistSidebarSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Section/StorefrontCustomerWishlistSidebarSection.xml @@ -13,5 +13,6 @@ + diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml new file mode 100644 index 0000000000000..c44ee1e062681 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml @@ -0,0 +1,59 @@ + + + + + + + + <description value="Products added to the cart from wishlist and a customer remains on the same page."/> + <group value="wishlist"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryFirst"/> + <createData entity="SimpleSubCategory" stepKey="categorySecond"/> + <createData entity="SimpleProduct" stepKey="simpleProduct1"> + <requiredEntity createDataKey="categoryFirst"/> + </createData> + <createData entity="SimpleProduct" stepKey="simpleProduct2"> + <requiredEntity createDataKey="categorySecond"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="categoryFirst" stepKey="deleteCategoryFirst"/> + <deleteData createDataKey="categorySecond" stepKey="deleteCategorySecond"/> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + </after> + <!-- Sign in as customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!-- Add product from first category to the wishlist --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryFirst.name$$)}}" stepKey="navigateToCategoryFirstPage"/> + <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct1"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup" stepKey="addSimpleProduct1ToWishlist"> + <argument name="productVar" value="$$simpleProduct1$$"/> + </actionGroup> + <!--Add product to the cart from the Wishlist using the sidebar from the second category page--> + <amOnPage url="{{StorefrontCategoryPage.url($$categorySecond.name$$)}}" stepKey="navigateToCategorySecondPage"/> + <actionGroup ref="StorefrontSwitchCategoryViewToListMode" stepKey="switchCategoryViewToListMode"/> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebar" stepKey="checkSimpleProduct1InWishlistSidebar"> + <argument name="productVar" value="$$simpleProduct1$$"/> + </actionGroup> + <actionGroup ref="StorefrontCustomerAddProductToCartFromWishlistUsingSidebar" stepKey="addProduct1ToCartFromWishlistUsingSidebar"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <!--Check that a customer on the same page as before--> + <!--hardcoded URL because this method does not support replacement--> + <seeCurrentUrlMatches regex="~\/$$categorySecond.name$$\.html\?(\S+)?\w+=list(&\S+)?$~i" stepKey="seeCurrentCategoryUrlMatches"/> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml new file mode 100644 index 0000000000000..c8170012c2a95 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontRemoveProductsFromWishlistUsingSidebarTest"> + <annotations> + <title value="Remove products from the wishlist using the sidebar."/> + <description value="Products removed from wishlist and a customer remains on the same page."/> + <group value="wishlist"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryFirst"/> + <createData entity="SimpleSubCategory" stepKey="categorySecond"/> + <createData entity="SimpleProduct" stepKey="simpleProduct1"> + <requiredEntity createDataKey="categoryFirst"/> + </createData> + <createData entity="SimpleProduct" stepKey="simpleProduct2"> + <requiredEntity createDataKey="categorySecond"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="categoryFirst" stepKey="deleteCategoryFirst"/> + <deleteData createDataKey="categorySecond" stepKey="deleteCategorySecond"/> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + </after> + <!-- Sign in as customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!-- Add product from first category to the wishlist --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryFirst.name$$)}}" stepKey="navigateToCategoryFirstPage"/> + <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct1"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup" stepKey="addSimpleProduct1ToWishlist"> + <argument name="productVar" value="$$simpleProduct1$$"/> + </actionGroup> + <!--Remove product from the Wishlist using the sidebar from the second category page--> + <amOnPage url="{{StorefrontCategoryPage.url($$categorySecond.name$$)}}" stepKey="navigateToCategorySecondPage"/> + <actionGroup ref="StorefrontSwitchCategoryViewToListMode" stepKey="switchCategoryViewToListMode"/> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebar" stepKey="checkSimpleProduct1InWishlistSidebar"> + <argument name="productVar" value="$$simpleProduct1$$"/> + </actionGroup> + <actionGroup ref="StorefrontCustomerRemoveProductFromWishlistUsingSidebar" stepKey="removeProduct1FromWishlistUsingSidebar"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <!--Check that a customer on the same page as before--> + <!--hardcoded URL because this method does not support replacement--> + <seeCurrentUrlMatches regex="~\/$$categorySecond.name$$\.html\?(\S+)?\w+=list(&\S+)?$~i" stepKey="seeCurrentCategoryUrlMatches"/> + </test> +</tests> From 0606ca0d9fb34af94677e7648dfba4b50021282d Mon Sep 17 00:00:00 2001 From: Eric Bohanon <eric.bo.dev@gmail.com> Date: Wed, 13 Jun 2018 13:10:26 -0500 Subject: [PATCH 0060/1171] MAGETWO-68802: Switch label and span order and alter CSS --- .../Ui/view/base/web/templates/form/field.html | 6 +++--- .../Magento/backend/web/css/source/forms/_fields.less | 11 +++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/templates/form/field.html b/app/code/Magento/Ui/view/base/web/templates/form/field.html index 6143900d5dd7b..8f46f0b689fa5 100644 --- a/app/code/Magento/Ui/view/base/web/templates/form/field.html +++ b/app/code/Magento/Ui/view/base/web/templates/form/field.html @@ -8,9 +8,9 @@ visible="visible" css="$data.additionalClasses" attr="'data-index': index"> - <label class="admin__field-label" if="$data.label" visible="$data.labelVisible" attr="for: uid"> - <span translate="label" attr="'data-config-scope': $data.scopeLabel"/> - </label> + <span class="admin__field-label" if="$data.label" visible="$data.labelVisible"> + <label translate="label" attr="'data-config-scope': $data.scopeLabel, for: uid" /> + </span> <div class="admin__field-control" css="'_with-tooltip': $data.tooltip, '_with-reset': $data.showFallbackReset && $data.isDifferedFromDefault"> <render args="elementTmpl" ifnot="hasAddons()"/> diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less index 59675de698787..8f0d4c33a8ee7 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less @@ -183,10 +183,13 @@ .admin__field-label { color: @field-label__color; - cursor: pointer; margin: 0; text-align: right; + label { + cursor: pointer; + } + + br { display: none; } @@ -207,7 +210,7 @@ overflow: hidden; } - span { + label { display: inline-block; line-height: @field-label__line-height; vertical-align: middle; @@ -512,7 +515,7 @@ position: absolute; top: 0; - span { + label { &:before { display: block; } @@ -527,7 +530,7 @@ } & > .admin__field-label { - span { + label { &:before { display: none; } From 1412c9d5f11985c1bc68dfb881055b6a98cc34df Mon Sep 17 00:00:00 2001 From: Eric Bohanon <eric.bo.dev@gmail.com> Date: Thu, 14 Jun 2018 16:58:46 -0500 Subject: [PATCH 0061/1171] MAGETWO-68802: Fix functional test selectors --- .../Section/AdminProductCustomizableOptionsSection.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml index cb80dade856a7..19207f0cacb09 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml @@ -14,7 +14,7 @@ <element name="useDefaultOptionTitle" type="text" selector="[data-index='options'] tr.data-row [data-index='title'] [name^='options_use_default']"/> <element name="useDefaultOptionTitleByIndex" type="text" selector="[data-index='options'] [data-index='values'] tr[data-repeat-index='{{var1}}'] [name^='options_use_default']" parameterized="true"/> <element name="addOptionBtn" type="button" selector="button[data-index='button_add']"/> - <element name="fillOptionTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//span[text()='Option Title']/parent::label/parent::div//input[@class='admin__control-text']" parameterized="true"/> + <element name="fillOptionTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//label[text()='Option Title']/parent::span/parent::div//input[@class='admin__control-text']" parameterized="true"/> <element name="optionTitleInput" type="input" selector="input[name='product[options][0][title]']"/> <element name="optionTypeOpenDropDown" type="button" selector=".admin__dynamic-rows[data-index='options'] .action-select"/> <element name="optionTypeTextField" type="button" selector=".admin__dynamic-rows[data-index='options'] .action-menu._active li li"/> @@ -24,9 +24,9 @@ <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//span[text()='Option Type']/parent::label/parent::div//div[@data-role='selected-option']" parameterized="true"/> <element name="checkDropDown" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//parent::label/parent::div//li[@class='admin__action-multiselect-menu-inner-item']//label[text()='Drop-down']" parameterized="true"/> <element name="clickAddValue" type="button" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tfoot//button" parameterized="true"/> - <element name="fillOptionValueTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//span[text()='Title']/parent::label/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> + <element name="fillOptionValueTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//label[text()='Title']/parent::span/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> <element name="fillOptionValuePrice" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//span[text()='Price']/parent::label/parent::div//div[@class='admin__control-addon']/input" parameterized="true"/> - <element name="clickSelectPriceType" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody//tr[@data-repeat-index='{{var2}}']//span[text()='Price Type']/parent::label/parent::div//select" parameterized="true"/> + <element name="clickSelectPriceType" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody//tr[@data-repeat-index='{{var2}}']//label[text()='Price Type']/parent::span/parent::div//select" parameterized="true"/> <element name="checkboxUseDefaultTitle" type="checkbox" selector="//span[text()='Option Title']/parent::label/parent::div/div//input[@type='checkbox']"/> <element name="checkboxUseDefaultOption" type="checkbox" selector="//table[@data-index='values']//tbody//tr[@data-repeat-index='{{var1}}']//div[@class='admin__field-control']//input[@type='checkbox']" parameterized="true"/> From c8b38db381011fa4c99073544953b750fd62edf8 Mon Sep 17 00:00:00 2001 From: Eric Bohanon <eric.bo.dev@gmail.com> Date: Fri, 15 Jun 2018 08:09:57 -0500 Subject: [PATCH 0062/1171] MAGETWO-68802: Fix selector for option title on functional test --- .../Catalog/Section/AdminProductCustomizableOptionsSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml index 19207f0cacb09..c4de71b587ac9 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml @@ -21,7 +21,7 @@ <element name="maxCharactersInput" type="input" selector="input[name='product[options][0][max_characters]']"/> - <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//span[text()='Option Type']/parent::label/parent::div//div[@data-role='selected-option']" parameterized="true"/> + <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//label[text()='Option Type']/parent::span/parent::div//div[@data-role='selected-option']" parameterized="true"/> <element name="checkDropDown" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//parent::label/parent::div//li[@class='admin__action-multiselect-menu-inner-item']//label[text()='Drop-down']" parameterized="true"/> <element name="clickAddValue" type="button" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tfoot//button" parameterized="true"/> <element name="fillOptionValueTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//label[text()='Title']/parent::span/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> From e15b6947bdb809ce17b4b26e8d3f7a1d0a66df4c Mon Sep 17 00:00:00 2001 From: Eric Bohanon <eric.bo.dev@gmail.com> Date: Fri, 15 Jun 2018 11:48:03 -0500 Subject: [PATCH 0063/1171] MAGETWO-68802: Create functional test to validate change --- .../Section/AdminProductFormSection.xml | 3 +- .../Test/AdminSimpleProductEditUiTest.xml | 38 +++++++++++++++++++ ...ductWithCustomOptionsSecondWebsiteTest.xml | 1 - 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminSimpleProductEditUiTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml index c81e4bd79997a..29755098bf423 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml @@ -13,6 +13,8 @@ <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> + <element name="enableProductAttributeLabel" type="text" selector="//label[text()='Enable Product']"/> + <element name="enableProductAttributeLabelWrapper" type="text" selector="//label[text()='Enable Product']/parent::span"/> <element name="productStatus" type="checkbox" selector="input[name='product[status]']"/> <element name="enableProductLabel" type="checkbox" selector="input[name='product[status]']+label"/> <element name="productStatusUseDefault" type="checkbox" selector="input[name='use_default[status]']"/> @@ -41,7 +43,6 @@ </section> <section name="ProductInWebsitesSection"> <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> - <!--<element name="websites" type="checkbox" selector="input[name='product[website_ids][{{var1}}]']" parameterized="true"/>--> <element name="website" type="checkbox" selector="//label[contains(text(), '{{var1}}')]/parent::div//input[@type='checkbox']" parameterized="true"/> </section> <section name="ProductDesignSection"> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminSimpleProductEditUiTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminSimpleProductEditUiTest.xml new file mode 100644 index 0000000000000..afbf326b1ef82 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminSimpleProductEditUiTest.xml @@ -0,0 +1,38 @@ +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminSimpleProductUiValidationTest"> + <annotations> + <title value="UI elements on the simple product edit screen should be organized as expected"/> + <description value="Admin should be able to use simple product UI in expected manner"/> + <testCaseId value="MAGETWO-92835"/> + <group value="Catalog"/> + </annotations> + + <before> + <!-- This was copied and modified from the EndToEndB2CGuestUserTest --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!--check admin for valid Enable Status label--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToEditPage"/> + <waitForPageLoad stepKey="wait1"/> + <seeCheckboxIsChecked selector="{{AdminProductFormSection.productStatus}}" stepKey="seeCheckboxEnableProductIsChecked"/> + + <!--check click on wrapping container does not trigger status change--> + <click selector="{{AdminProductFormSection.enableProductAttributeLabelWrapper}}" stepKey="clickEnableProductWrapper"/> + <seeCheckboxIsChecked selector="{{AdminProductFormSection.productStatus}}" stepKey="seeCheckboxEnableProductIsCheckedAfterWrapperClick"/> + + <!--check click on label itself does trigger status change--> + <click selector="{{AdminProductFormSection.enableProductAttributeLabel}}" stepKey="clickEnableProductLabel"/> + <dontSeeCheckboxIsChecked selector="{{AdminProductFormSection.productStatus}}" stepKey="dontSeeCheckboxEnableProductIsCheckedAfterLabelClick"/> + </test> +</tests> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml index cf7f996736eb3..64753dd179605 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml @@ -54,7 +54,6 @@ <fillField userInput="{{_defaultProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> <fillField userInput="{{_defaultProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> - <!--<click selector="{{AdminProductCustomizableOptionsSection.customezableOptions}}" stepKey="openCustomOptionsSection"/>--> <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customezableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses2"/> <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> <waitForPageLoad stepKey="waitAfterAddOption"/> From d3b22f4446f02fdce947d1d950d0e6a55b7e1d8b Mon Sep 17 00:00:00 2001 From: Eric Bohanon <eric.bo.dev@gmail.com> Date: Sat, 16 Jun 2018 07:57:05 -0500 Subject: [PATCH 0064/1171] MAGETWO-68802: Modify selector in functional test to pass --- .../Catalog/Section/AdminProductCustomizableOptionsSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml index c4de71b587ac9..3c810bd7aa8f0 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml @@ -22,7 +22,7 @@ <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//label[text()='Option Type']/parent::span/parent::div//div[@data-role='selected-option']" parameterized="true"/> - <element name="checkDropDown" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//parent::label/parent::div//li[@class='admin__action-multiselect-menu-inner-item']//label[text()='Drop-down']" parameterized="true"/> + <element name="checkDropDown" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//parent::label/parent::span/parent::div//li[@class='admin__action-multiselect-menu-inner-item']//label[text()='Drop-down']" parameterized="true"/> <element name="clickAddValue" type="button" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tfoot//button" parameterized="true"/> <element name="fillOptionValueTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//label[text()='Title']/parent::span/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> <element name="fillOptionValuePrice" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//span[text()='Price']/parent::label/parent::div//div[@class='admin__control-addon']/input" parameterized="true"/> From c45230296c5dfdc9021764300a9ef6b17562a03d Mon Sep 17 00:00:00 2001 From: Eric Bohanon <eric.bo.dev@gmail.com> Date: Wed, 20 Jun 2018 10:37:36 -0500 Subject: [PATCH 0065/1171] MAGETWO-68802: Add copyright --- .../Catalog/Test/AdminSimpleProductEditUiTest.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminSimpleProductEditUiTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminSimpleProductEditUiTest.xml index afbf326b1ef82..2b9733fd2fa5c 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminSimpleProductEditUiTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminSimpleProductEditUiTest.xml @@ -1,3 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> <test name="AdminSimpleProductUiValidationTest"> From 948faa8b2d5a654758931d30150bd211c77dc847 Mon Sep 17 00:00:00 2001 From: Eric Bohanon <eric.bo.dev@gmail.com> Date: Wed, 20 Jun 2018 11:01:25 -0500 Subject: [PATCH 0066/1171] MAGETWO-68802: Change span to label on test section --- .../FunctionalTest/Catalog/Section/AdminProductFormSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml index 29755098bf423..50b4401a71475 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml @@ -39,7 +39,7 @@ <element name="visibility" type="select" selector="//select[@name='product[visibility]']"/> <element name="visibilityUseDefault" type="checkbox" selector="//input[@name='use_default[visibility]']"/> <element name="divByDataIndex" type="input" selector="div[data-index='{{var}}']" parameterized="true"/> - <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//span[text()='{{attributeLabel}}']" parameterized="true"/> + <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//label[text()='{{attributeLabel}}']" parameterized="true"/> </section> <section name="ProductInWebsitesSection"> <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> From 4d4a13a8690f167ffab7c5ac1fb5fa965251683f Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Thu, 21 Jun 2018 09:47:58 -0500 Subject: [PATCH 0067/1171] MQE-1060: Allow magentoCLI Action To Run Without Manipulating Command That Is Sent To CLI - Adding arguments check to command --- dev/tests/acceptance/utils/command.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dev/tests/acceptance/utils/command.php b/dev/tests/acceptance/utils/command.php index 943e2e9776af4..28644fa5fe66c 100644 --- a/dev/tests/acceptance/utils/command.php +++ b/dev/tests/acceptance/utils/command.php @@ -6,10 +6,15 @@ if (isset($_POST['command'])) { $command = urldecode($_POST['command']); + if (array_key_exists("arguments", $_POST)) { + $arguments = urldecode($_POST['arguments']); + } else { + $arguments = null; + } $php = PHP_BINARY ?: (PHP_BINDIR ? PHP_BINDIR . '/php' : 'php'); $valid = validateCommand($command); if ($valid) { - exec(escapeCommand($php . ' -f ../../../../bin/magento ' . $command) . " 2>&1", $output, $exitCode); + exec(escapeCommand($php . ' -f ../../../../bin/magento ' . $command) . " $arguments" . " 2>&1", $output, $exitCode); if ($exitCode == 0) { http_response_code(202); } else { From 38f0bc23c5e63c04d4370674a28e0dac8f18caa6 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Thu, 21 Jun 2018 20:32:17 -0400 Subject: [PATCH 0068/1171] Unit tests for encryption adapters --- .../Test/Unit/Adapter/McryptTest.php | 188 ++++++++++++++++++ .../Unit/Adapter/SodiumChachaIetfTest.php | 51 +++++ .../_files/_sodium_chachaieft_fixtures.php | 35 ++++ 3 files changed, 274 insertions(+) create mode 100644 lib/internal/Magento/Framework/Encryption/Test/Unit/Adapter/McryptTest.php create mode 100644 lib/internal/Magento/Framework/Encryption/Test/Unit/Adapter/SodiumChachaIetfTest.php create mode 100644 lib/internal/Magento/Framework/Encryption/Test/Unit/Crypt/_files/_sodium_chachaieft_fixtures.php diff --git a/lib/internal/Magento/Framework/Encryption/Test/Unit/Adapter/McryptTest.php b/lib/internal/Magento/Framework/Encryption/Test/Unit/Adapter/McryptTest.php new file mode 100644 index 0000000000000..2622441aa0896 --- /dev/null +++ b/lib/internal/Magento/Framework/Encryption/Test/Unit/Adapter/McryptTest.php @@ -0,0 +1,188 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +/** + * Test case for \Magento\Framework\Encryption\Adapter\Mcrypt + */ +namespace Magento\Framework\Encryption\Test\Unit\Adapter; + +class McryptTest extends \PHPUnit\Framework\TestCase +{ + private $key; + + private static $cipherInfo; + + private const SUPPORTED_CIPHER_MODE_COMBINATIONS = [ + MCRYPT_BLOWFISH => [MCRYPT_MODE_ECB], + MCRYPT_RIJNDAEL_128 => [MCRYPT_MODE_ECB], + MCRYPT_RIJNDAEL_256 => [MCRYPT_MODE_CBC], + ]; + + protected function setUp() + { + $this->key = substr(__CLASS__, -32, 32); + } + + protected function getRandomString(int $length): string + { + $result = ''; + + do { + $result .= sha1(microtime()); + } while (strlen($result) < $length); + + return substr($result, -$length); + } + + private function requireCipherInfo() + { + $filename = __DIR__ . '/../Crypt/_files/_cipher_info.php'; + + if (!self::$cipherInfo) { + self::$cipherInfo = include $filename; + } + } + + private function getKeySize(string $cipherName, string $modeName): int + { + $this->requireCipherInfo(); + return self::$cipherInfo[$cipherName][$modeName]['key_size']; + } + + private function getInitVectorSize(string $cipherName, string $modeName): int + { + $this->requireCipherInfo(); + return self::$cipherInfo[$cipherName][$modeName]['iv_size']; + } + + public function getCipherModeCombinations(): array + { + $result = []; + foreach (self::SUPPORTED_CIPHER_MODE_COMBINATIONS as $cipher => $modes) { + /** @var array $modes */ + foreach ($modes as $mode) { + $result[$cipher . '-' . $mode] = [$cipher, $mode]; + } + } + return $result; + } + + /** + * @dataProvider getCipherModeCombinations + */ + public function testConstructor(string $cipher, string $mode) + { + /* Generate random init vector */ + $initVector = $this->getRandomString($this->getInitVectorSize($cipher, $mode)); + + $crypt = new \Magento\Framework\Encryption\Adapter\Mcrypt($this->key, $cipher, $mode, $initVector); + + $this->assertEquals($cipher, $crypt->getCipher()); + $this->assertEquals($mode, $crypt->getMode()); + $this->assertEquals($initVector, $crypt->getInitVector()); + } + + public function getConstructorExceptionData(): array + { + $key = substr(__CLASS__, -32, 32); + $result = []; + foreach (self::SUPPORTED_CIPHER_MODE_COMBINATIONS as $cipher => $modes) { + /** @var array $modes */ + foreach ($modes as $mode) { + $tooLongKey = str_repeat('-', $this->getKeySize($cipher, $mode) + 1); + $tooShortInitVector = str_repeat('-', $this->getInitVectorSize($cipher, $mode) - 1); + $tooLongInitVector = str_repeat('-', $this->getInitVectorSize($cipher, $mode) + 1); + $result['tooLongKey-' . $cipher . '-' . $mode . '-false'] = [$tooLongKey, $cipher, $mode, false]; + $keyPrefix = 'key-' . $cipher . '-' . $mode; + $result[$keyPrefix . '-tooShortInitVector'] = [$key, $cipher, $mode, $tooShortInitVector]; + $result[$keyPrefix . '-tooLongInitVector'] = [$key, $cipher, $mode, $tooLongInitVector]; + } + } + return $result; + } + + /** + * @dataProvider getConstructorExceptionData + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testConstructorException(string $key, string $cipher, string $mode, ?string $initVector = null) + { + new \Magento\Framework\Encryption\Adapter\Mcrypt($key, $cipher, $mode, $initVector); + } + + public function testConstructorDefaults() + { + $cryptExpected = new \Magento\Framework\Encryption\Adapter\Mcrypt( + $this->key, + MCRYPT_BLOWFISH, + MCRYPT_MODE_ECB, + null + ); + $cryptActual = new \Magento\Framework\Encryption\Adapter\Mcrypt($this->key); + + $this->assertEquals($cryptExpected->getCipher(), $cryptActual->getCipher()); + $this->assertEquals($cryptExpected->getMode(), $cryptActual->getMode()); + $this->assertEquals($cryptExpected->getInitVector(), $cryptActual->getInitVector()); + } + + public function getCryptData(): array + { + $fixturesFilename = __DIR__ . '/../Crypt/_files/_crypt_fixtures.php'; + + $result = include $fixturesFilename; + /* Restore encoded string back to binary */ + foreach ($result as &$cryptParams) { + $cryptParams[5] = base64_decode($cryptParams[5]); + } + unset($cryptParams); + + return $result; + } + + /** + * @dataProvider getCryptData + */ + public function testDecrypt( + string $key, + string $cipher, + string $mode, + ?string $initVector, + string $expectedData, + string $inputData + ) { + $crypt = new \Magento\Framework\Encryption\Adapter\Mcrypt($key, $cipher, $mode, $initVector); + $actualData = $crypt->decrypt($inputData); + $this->assertEquals($expectedData, $actualData); + } + + /** + * @expectedException \Exception + */ + public function testEncrypt() + { + $crypt = new \Magento\Framework\Encryption\Adapter\Mcrypt($this->key); + $crypt->encrypt('Hello World!!'); + } + + /** + * @dataProvider getCipherModeCombinations + */ + public function testInitVectorNone(string $cipher, string $mode) + { + $crypt = new \Magento\Framework\Encryption\Adapter\Mcrypt( + $this->key, + $cipher, + $mode, + null + ); + $actualInitVector = $crypt->getInitVector(); + + $expectedInitVector = str_repeat("\0", $this->getInitVectorSize($cipher, $mode)); + $this->assertEquals($expectedInitVector, $actualInitVector); + } +} diff --git a/lib/internal/Magento/Framework/Encryption/Test/Unit/Adapter/SodiumChachaIetfTest.php b/lib/internal/Magento/Framework/Encryption/Test/Unit/Adapter/SodiumChachaIetfTest.php new file mode 100644 index 0000000000000..8092ce55b42c8 --- /dev/null +++ b/lib/internal/Magento/Framework/Encryption/Test/Unit/Adapter/SodiumChachaIetfTest.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +/** + * Test case for \Magento\Framework\Encryption\Adapter\SodiumChachaIetf + */ +namespace Magento\Framework\Encryption\Test\Unit\Adapter; + +class SodiumChachaIetfTest extends \PHPUnit\Framework\TestCase +{ + public function getCryptData(): array + { + $fixturesFilename = __DIR__ . '/../Crypt/_files/_sodium_chachaieft_fixtures.php'; + + $result = include $fixturesFilename; + /* Restore encoded string back to binary */ + foreach ($result as &$cryptParams) { + $cryptParams['encrypted'] = base64_decode($cryptParams['encrypted']); + } + unset($cryptParams); + + return $result; + } + + /** + * @dataProvider getCryptData + */ + public function testEncrypt(string $key, string $encrypted, string $decrypted) + { + $crypt = new \Magento\Framework\Encryption\Adapter\SodiumChachaIetf($key); + $result = $crypt->encrypt($decrypted); + + $this->assertNotEquals($encrypted, $result); + } + + /** + * @dataProvider getCryptData + */ + public function testDecrypt(string $key, string $encrypted, string $decrypted) + { + $crypt = new \Magento\Framework\Encryption\Adapter\SodiumChachaIetf($key); + $result = $crypt->decrypt($encrypted); + + $this->assertEquals($decrypted, $result); + } +} diff --git a/lib/internal/Magento/Framework/Encryption/Test/Unit/Crypt/_files/_sodium_chachaieft_fixtures.php b/lib/internal/Magento/Framework/Encryption/Test/Unit/Crypt/_files/_sodium_chachaieft_fixtures.php new file mode 100644 index 0000000000000..7917bd9ba83e8 --- /dev/null +++ b/lib/internal/Magento/Framework/Encryption/Test/Unit/Crypt/_files/_sodium_chachaieft_fixtures.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +return [ + 0 => [ + 'key' => '6wRADHwwCBGgdxbcHhovGB0upmg0mbsN', + 'encrypted' => '146BhsQ3grT0VgkYuY3ii3gpClXHkFqlIcNpAD4+bAMBP+ToCHZHiJID', + 'decrypted' => 'Hello World!!!', + ], + 1 => [ + 'key' => 'uPuzBU067DXTM4PqEi14Sv5tbWjVcRZI', + 'encrypted' => '6SQaVrCnY10n8tOxYyvWuVGKddjR12ZbGylM9K+bRHqsqltRwuLs15vV', + 'decrypted' => 'Hello World!!!', + ], + 2 => [ + 'key' => 'zsmVdKkwVgylxMM8ZzQ3GTv7SxvusKnJ', + 'encrypted' => 'eQcREUJDV8EEB9WA1pBd5LbVQrs4Kyv6iWnkhOnjeitySuPQAcpIVoCM', + 'decrypted' => 'Hello World!!!', + ], + 3 => [ + 'key' => 'aggaHLvRCxRRyebpsrGAdLAIfSrufYrN', + 'encrypted' => 'PSOa8KCpTsxnTgq4IKbpneF38FIp0JeAeiXQIf30vS5X+riylx05pz9b', + 'decrypted' => 'Hello World!!!', + ], + 4 => [ + 'key' => '6tEWnKY6AcdjS2XfPe1DjTbkvu2cFFZo', + 'encrypted' => 'UglO9dEgslFpwPwejJmrK89PmBicv+I1pfdaXaEI69IrETD8LpdzOLF7', + 'decrypted' => 'Hello World!!!', + ], +]; From e377e3c42d91e3d266641f8d94929da5a2b87e98 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Thu, 21 Jun 2018 22:37:53 -0400 Subject: [PATCH 0069/1171] Integration Test for CC Number Encryption Updater --- .../Order/Payment/EncryptionUpdateTest.php | 50 +++++++++++++++++ .../Magento/Sales/_files/payment_enc_cc.php | 53 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Payment/EncryptionUpdateTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/payment_enc_cc.php diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Payment/EncryptionUpdateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Payment/EncryptionUpdateTest.php new file mode 100644 index 0000000000000..c0de639ea7429 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Payment/EncryptionUpdateTest.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Sales\Model\ResourceModel\Order\Payment; + +use Magento\Framework\Encryption\Encryptor; +use Magento\TestFramework\Helper\Bootstrap; + +class EncryptionUpdateTest extends \PHPUnit\Framework\TestCase +{ + const TEST_CC_NUMBER = '4111111111111111'; + + /** + * Tests re-encryption of credit card numbers + * + * @magentoDataFixture Magento/Sales/_files/payment_enc_cc.php + */ + public function testReEncryptCreditCardNumbers() + { + $objectManager = Bootstrap::getObjectManager(); + + /** @var \Magento\Framework\Encryption\EncryptorInterface $encyptor */ + $encyptor = $objectManager->get(\Magento\Framework\Encryption\EncryptorInterface::class); + + /** @var \Magento\Sales\Model\ResourceModel\Order\Payment\EncryptionUpdate $resource */ + $resource = $objectManager->create(\Magento\Sales\Model\ResourceModel\Order\Payment\EncryptionUpdate::class); + $resource->reEncryptCreditCardNumbers(); + + /** @var \Magento\Sales\Model\ResourceModel\Order\Payment\Collection $collection */ + $collection = $objectManager->create(\Magento\Sales\Model\ResourceModel\Order\Payment\Collection::class); + $collection->addFieldToFilter('cc_number_enc', ['notnull' => true]); + + $this->assertGreaterThan(0, $collection->getTotalCount()); + + /** @var \Magento\Sales\Model\Order\Payment $payment */ + foreach ($collection->getItems() as $payment) { + $this->assertEquals( + static::TEST_CC_NUMBER, + $encyptor->decrypt($payment->getCcNumberEnc()) + ); + + $this->assertStringStartsWith('0:' . Encryptor::CIPHER_LATEST . ':', $payment->getCcNumberEnc()); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/payment_enc_cc.php b/dev/tests/integration/testsuite/Magento/Sales/_files/payment_enc_cc.php new file mode 100644 index 0000000000000..35f1aaa6a2911 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/payment_enc_cc.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Api\SearchCriteria; +use Magento\Sales\Model\ResourceModel\Order\Payment\EncryptionUpdateTest; +use Magento\Framework\App\DeploymentConfig; + +require 'order.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var DeploymentConfig $deployConfig */ +$deployConfig = $objectManager->get(DeploymentConfig::class); + +/** + * Creates an encrypted card number with the current crypt key using + * a legacy cipher. + */ +// @codingStandardIgnoreStart +$handle = @mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, ''); +$initVectorSize = @mcrypt_enc_get_iv_size($handle); +$initVector = str_repeat("\0", $initVectorSize); +@mcrypt_generic_init($handle, $deployConfig->get('crypt/key'), $initVector); + +$encCcNumber = @mcrypt_generic($handle, EncryptionUpdateTest::TEST_CC_NUMBER); + +@mcrypt_generic_deinit($handle); +@mcrypt_module_close($handle); +// @codingStandardIgnoreEnd + +/** @var SearchCriteria $searchCriteria */ +$searchCriteria = $objectManager->get(SearchCriteriaBuilder::class) + ->addFilter('increment_id', '100000001') + ->create(); + +$orders = $orderRepository->getList($searchCriteria)->getItems(); +$order = array_pop($orders); + +/** @var \Magento\Sales\Model\ResourceModel\Order\Payment $resource */ +$resource = $objectManager->create(\Magento\Sales\Model\ResourceModel\Order\Payment::class); +$resource->getConnection()->insert( + $resource->getMainTable(), + [ + 'parent_id' => $order->getId(), + 'cc_number_enc' => '0:2:' . base64_encode($encCcNumber), + ] +); From 24273cfe130f63cc9d83656051cb776175996191 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Thu, 21 Jun 2018 23:26:43 -0400 Subject: [PATCH 0070/1171] Integration Test for Config Encrypt Update Patch Test validates the following are true after patch is applied: * Patched values are encrypted with latest cipher * Patched values match original plain-text value when decrpyted --- .../Patch/Data/SodiumChachaPatchTest.php | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatchTest.php diff --git a/dev/tests/integration/testsuite/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatchTest.php b/dev/tests/integration/testsuite/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatchTest.php new file mode 100644 index 0000000000000..96d511e9bc81e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatchTest.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\EncryptionKey\Setup\Patch\Data; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\Encryption\Encryptor; + +class SodiumChachaPatchTest extends \PHPUnit\Framework\TestCase +{ + const PATH_KEY = 'crypt/key'; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var DeploymentConfig + */ + private $deployConfig; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->deployConfig = $this->objectManager->get(DeploymentConfig::class); + } + + public function testChangeEncryptionKey() + { + $testPath = 'test/config'; + $testValue = 'test'; + + $structureMock = $this->createMock(\Magento\Config\Model\Config\Structure\Proxy::class); + $structureMock->expects($this->once()) + ->method('getFieldPathsByAttribute') + ->will($this->returnValue([$testPath])); + + /** @var \Magento\Config\Model\ResourceModel\Config $configModel */ + $configModel = $this->objectManager->create(\Magento\Config\Model\ResourceModel\Config::class); + $configModel->saveConfig($testPath, $this->legacyEncrypt($testValue), 'default', 0); + + /** @var \Magento\EncryptionKey\Setup\Patch\Data\SodiumChachaPatch $patch */ + $patch = $this->objectManager->create( + \Magento\EncryptionKey\Setup\Patch\Data\SodiumChachaPatch::class, + [ + 'structure' => $structureMock, + ] + ); + $patch->apply(); + + $connection = $configModel->getConnection(); + $values = $connection->fetchPairs( + $connection->select()->from( + $configModel->getMainTable(), + ['config_id', 'value'] + )->where( + 'path IN (?)', + [$testPath] + )->where( + 'value NOT LIKE ?', + '' + ) + ); + + /** @var \Magento\Framework\Encryption\EncryptorInterface $encyptor */ + $encyptor = $this->objectManager->get(\Magento\Framework\Encryption\EncryptorInterface::class); + + $rawConfigValue = array_pop($values); + + $this->assertNotEquals($testValue, $rawConfigValue); + $this->assertStringStartsWith('0:' . Encryptor::CIPHER_LATEST . ':', $rawConfigValue); + $this->assertEquals($testValue, $encyptor->decrypt($rawConfigValue)); + } + + private function legacyEncrypt(string $data): string + { + // @codingStandardIgnoreStart + $handle = @mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, ''); + $initVectorSize = @mcrypt_enc_get_iv_size($handle); + $initVector = str_repeat("\0", $initVectorSize); + @mcrypt_generic_init($handle, $this->deployConfig->get(static::PATH_KEY), $initVector); + + $encrpted = @mcrypt_generic($handle, $data); + + @mcrypt_generic_deinit($handle); + @mcrypt_module_close($handle); + // @codingStandardIgnoreEnd + + return '0:' . Encryptor::CIPHER_RIJNDAEL_256 . ':' . base64_encode($encrpted); + } +} From f738a5ce5d7e79d698870a8f3ae0dc9607a5b596 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Fri, 22 Jun 2018 06:52:44 -0400 Subject: [PATCH 0071/1171] Redistribute integration tests in travis builds --- dev/travis/before_script.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/travis/before_script.sh b/dev/travis/before_script.sh index 7cf55ca8083f1..f2ba20dc412b6 100755 --- a/dev/travis/before_script.sh +++ b/dev/travis/before_script.sh @@ -13,8 +13,8 @@ case $TEST_SUITE in test_set_list=$(find testsuite/* -maxdepth 1 -mindepth 1 -type d | sort) test_set_count=$(printf "$test_set_list" | wc -l) - test_set_size[1]=$(printf "%.0f" $(echo "$test_set_count*0.12" | bc)) #12% - test_set_size[2]=$(printf "%.0f" $(echo "$test_set_count*0.32" | bc)) #32% + test_set_size[1]=$(printf "%.0f" $(echo "$test_set_count*0.17" | bc)) #17% + test_set_size[2]=$(printf "%.0f" $(echo "$test_set_count*0.27" | bc)) #27% test_set_size[3]=$((test_set_count-test_set_size[1]-test_set_size[2])) #56% echo "Total = ${test_set_count}; Batch #1 = ${test_set_size[1]}; Batch #2 = ${test_set_size[2]}; Batch #3 = ${test_set_size[3]};"; From 6c4cf5baa4f26f5742882da622b52274e24ce46b Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Fri, 22 Jun 2018 10:33:18 -0500 Subject: [PATCH 0072/1171] MQE-1033: Validate duplicate element names in the same file (Section, Data, Metadata, Page) (#57) - Fixed duplicate elements. --- .../Magento/FunctionalTest/Bundle/Data/BundleLinkData.xml | 1 - .../Magento/FunctionalTest/Bundle/Data/ProductData.xml | 1 - .../FunctionalTest/Catalog/Section/AdminProductFormSection.xml | 2 +- .../Checkout/Section/StorefrontMiniCartSection.xml | 1 - .../Magento/FunctionalTest/Theme/Section/AdminThemeSection.xml | 2 +- 5 files changed, 2 insertions(+), 5 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/BundleLinkData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/BundleLinkData.xml index 65add76a12af3..4205b06b36068 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/BundleLinkData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/BundleLinkData.xml @@ -9,7 +9,6 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ApiBundleLink" type="bundle_link"> - <var key="sku" entityKey="sku" entityType="product2"/> <var key="option_id" entityKey="option_id" entityType="bundle_options"/> <var key="sku" entityKey="sku" entityType="product"/> <data key="qty">1</data> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/ProductData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/ProductData.xml index 25c286d8a3309..d4fe497a48bc9 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/ProductData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/ProductData.xml @@ -9,7 +9,6 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> <entity name="BundleProduct" type="product"> - <data key="name" unique="suffix">BundleProduct</data> <data key="name" unique="suffix">BundleProduct</data> <data key="name2" unique="suffix">BundleProduct2</data> <data key="sku" unique="suffix">bundleproduct</data> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml index c81e4bd79997a..0c37a39092608 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml @@ -56,7 +56,7 @@ </section> <section name="ProductWYSIWYGSection"> <element name="Switcher" type="button" selector="//select[@id='dropdown-switcher']"/> - <element name="v4" type ="button" selector="//select[@id='dropdown-switcher']/option[text()='TinyMCE 4.3.6']" /> + <element name="v436" type ="button" selector="//select[@id='dropdown-switcher']/option[text()='TinyMCE 4.3.6']" /> <element name="v3" type ="button" selector="//select[@id='dropdown-switcher']/option[text()='TinyMCE 3.6(Deprecated)']" /> <element name="TinymceDescription3" type ="button" selector="//span[text()='Description']" /> <element name="SaveConfig" type ="button" selector="#save" /> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml index bdd97130a9715..f375407a14293 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml @@ -10,7 +10,6 @@ xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="StorefrontMinicartSection"> <element name="productCount" type="text" selector="//header//div[contains(@class, 'minicart-wrapper')]//a[contains(@class, 'showcart')]//span[@class='counter-number']"/> - <element name="viewAndEditCart" type="button" selector="//header//div[contains(@class, 'minicart-wrapper')]//a[contains(@class, 'viewcart')]"/> <element name="productLinkByName" type="button" selector="//header//ol[@id='mini-cart']//div[@class='product-item-details']//a[contains(text(), '{{var1}}')]" parameterized="true"/> <element name="productPriceByName" type="text" selector="//header//ol[@id='mini-cart']//div[@class='product-item-details'][.//a[contains(text(), '{{var1}}')]]//span[@class='price']" parameterized="true"/> <element name="productImageByName" type="text" selector="//header//ol[@id='mini-cart']//span[@class='product-image-container']//img[@alt='{{var1}}']" parameterized="true"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Section/AdminThemeSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Section/AdminThemeSection.xml index 281cc290b4f39..c4ece665b90b9 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Section/AdminThemeSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Section/AdminThemeSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminThemeSection"> <!--All rows in a specific Column e.g. {{Section.rowsInColumn('columnName')}}--> - <element name="rowsInColumn" type="text" selector="//tr/td[contains(@class, '{{column}}')]" parameterized="true"/> + <element name="allRowsInColumn" type="text" selector="//tr/td[contains(@class, '{{column}}')]" parameterized="true"/> <!--selector for Theme Title column since it needs to be handled separately--> <element name="rowsInThemeTitleColumn" type="text" selector="//tbody/tr/td[contains(@class, 'parent_theme')]/preceding-sibling::td"/> <element name="rowsInColumn" type="text" selector="//tbody/tr/td[contains(@class, '{{column}}')]" parameterized="true"/> From e4885521cb79c10a84af1080c3a37bd3ea96d1f4 Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Fri, 22 Jun 2018 11:52:51 -0500 Subject: [PATCH 0073/1171] MAGETWO-92929: Declarative schema tests are located in inappropriate place --- .../Magento/Framework/Setup/Test/Unit}/SchemaListenerTest.php | 4 ++-- .../Framework/Setup/Test/Unit}/SchemaPersistorTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename {app/code/Magento/Framework/Test/Unit/Setup => lib/internal/Magento/Framework/Setup/Test/Unit}/SchemaListenerTest.php (98%) rename {app/code/Magento/Framework/Test/Unit/Setup => lib/internal/Magento/Framework/Setup/Test/Unit}/SchemaPersistorTest.php (98%) diff --git a/app/code/Magento/Framework/Test/Unit/Setup/SchemaListenerTest.php b/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaListenerTest.php similarity index 98% rename from app/code/Magento/Framework/Test/Unit/Setup/SchemaListenerTest.php rename to lib/internal/Magento/Framework/Setup/Test/Unit/SchemaListenerTest.php index 4a02cc3e0df55..2c17c1564fe63 100644 --- a/app/code/Magento/Framework/Test/Unit/Setup/SchemaListenerTest.php +++ b/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaListenerTest.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -namespace Magento\Framework\Test\Unit\Setup; +namespace Magento\Framework\Setup\Test\Unit; use Magento\Framework\DB\Ddl\Table; use Magento\Framework\Setup\SchemaListenerDefinition\BooleanDefinition; @@ -16,7 +16,7 @@ /** * Unit test for schema listener. * - * @package Magento\Framework\Test\Unit\Setup + * @package Magento\Framework\Setup\Test\Unit */ class SchemaListenerTest extends \PHPUnit\Framework\TestCase { diff --git a/app/code/Magento/Framework/Test/Unit/Setup/SchemaPersistorTest.php b/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaPersistorTest.php similarity index 98% rename from app/code/Magento/Framework/Test/Unit/Setup/SchemaPersistorTest.php rename to lib/internal/Magento/Framework/Setup/Test/Unit/SchemaPersistorTest.php index 56f04f4c7ba77..be8775444b602 100644 --- a/app/code/Magento/Framework/Test/Unit/Setup/SchemaPersistorTest.php +++ b/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaPersistorTest.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -namespace Magento\Framework\Test\Unit\Setup; +namespace Magento\Framework\Setup\Test\Unit; use Magento\Framework\Component\ComponentRegistrar; use Magento\Framework\Setup\SchemaListener; @@ -14,7 +14,7 @@ /** * Unit test for schema persistor. * - * @package Magento\Framework\Test\Unit\Setup + * @package Magento\Framework\Setup\Test\Unit */ class SchemaPersistorTest extends \PHPUnit\Framework\TestCase { From 483d4f18d3d8555e2584387364ae99d66084f62a Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 22 Jun 2018 16:27:42 -0500 Subject: [PATCH 0074/1171] MAGETWO-91433: ProductListing: Grid view is getting changed to List view when user adding product from wishlist section. --- .../Magento/Store/App/Response/Redirect.php | 23 ++++++++++--------- .../Wishlist/Controller/Index/Remove.php | 19 +++++++++++++-- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Store/App/Response/Redirect.php b/app/code/Magento/Store/App/Response/Redirect.php index d826ad3425f54..63dd844409983 100644 --- a/app/code/Magento/Store/App/Response/Redirect.php +++ b/app/code/Magento/Store/App/Response/Redirect.php @@ -77,21 +77,22 @@ public function __construct( /** * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException */ protected function _getUrl() { $refererUrl = $this->_request->getServer('HTTP_REFERER'); - $url = (string)$this->_request->getParam(self::PARAM_NAME_REFERER_URL); - if ($url) { - $refererUrl = $url; - } - $url = $this->_request->getParam(\Magento\Framework\App\ActionInterface::PARAM_NAME_BASE64_URL); - if ($url) { - $refererUrl = $this->_urlCoder->decode($url); - } - $url = $this->_request->getParam(\Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED); - if ($url) { - $refererUrl = $this->_urlCoder->decode($url); + $encodedUrl = $this->_request->getParam(\Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED) + ? $this->_request->getParam(\Magento\Framework\App\ActionInterface::PARAM_NAME_BASE64_URL) + : ''; + + if ($encodedUrl) { + $refererUrl = $this->_urlCoder->decode($encodedUrl); + } else { + $url = (string)$this->_request->getParam(self::PARAM_NAME_REFERER_URL); + if ($url) { + $refererUrl = $url; + } } if (!$this->_isUrlInternal($refererUrl)) { diff --git a/app/code/Magento/Wishlist/Controller/Index/Remove.php b/app/code/Magento/Wishlist/Controller/Index/Remove.php index 6553b886773c0..7690bf8314866 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Remove.php +++ b/app/code/Magento/Wishlist/Controller/Index/Remove.php @@ -5,6 +5,7 @@ */ namespace Magento\Wishlist\Controller\Index; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; use Magento\Framework\App\Action; use Magento\Framework\Data\Form\FormKey\Validator; use Magento\Framework\Exception\NotFoundException; @@ -27,18 +28,27 @@ class Remove extends \Magento\Wishlist\Controller\AbstractIndex */ protected $formKeyValidator; + /** + * @var ProductCollectionFactory + */ + private $productCollectionFactory; + /** * @param Action\Context $context * @param WishlistProviderInterface $wishlistProvider * @param Validator $formKeyValidator + * @param ProductCollectionFactory|null $productCollectionFactory */ public function __construct( Action\Context $context, WishlistProviderInterface $wishlistProvider, - Validator $formKeyValidator + Validator $formKeyValidator, + ProductCollectionFactory $productCollectionFactory = null ) { $this->wishlistProvider = $wishlistProvider; $this->formKeyValidator = $formKeyValidator; + $this->productCollectionFactory = $productCollectionFactory + ?: \Magento\Framework\App\ObjectManager::getInstance()->get(ProductCollectionFactory::class); parent::__construct($context); } @@ -69,10 +79,15 @@ public function execute() try { $item->delete(); $wishlist->save(); + $product = $this->productCollectionFactory + ->create() + ->addIdFilter($item->getProductId()) + ->addAttributeToSelect('name') + ->getFirstItem(); $this->messageManager->addComplexSuccessMessage( 'removeWishlistItemSuccessMessage', [ - 'product_name' => $item->getProduct()->getName() + 'product_name' => $product->getName() ] ); } catch (\Magento\Framework\Exception\LocalizedException $e) { From 0a48caf80da4d9ba3be27ae8cab708ac223e433f Mon Sep 17 00:00:00 2001 From: "Leandro F. L" <lfluvisotto@gmail.com> Date: Sat, 23 Jun 2018 20:27:13 +0200 Subject: [PATCH 0075/1171] [Forwardport] Fix case mismatch call (class/method) --- .../Unit/Model/Export/AdvancedPricingTest.php | 2 +- .../System/Config/AdditionalCommentTest.php | 4 ++-- .../System/Config/CollectionTimeLabelTest.php | 2 +- .../Config/SubscriptionStatusLabelTest.php | 2 +- .../Adminhtml/System/Config/VerticalTest.php | 2 +- .../Gateway/Response/CardDetailsHandler.php | 2 +- .../Gateway/Response/VaultDetailsHandler.php | 2 +- .../Braintree/Model/Ui/ConfigProvider.php | 2 +- .../Test/Unit/Gateway/Config/ConfigTest.php | 2 +- .../Product/ShowUpdateResultTest.php | 2 +- .../PrepareInitialCheckoutConfiguration.php | 2 +- .../Unit/Model/Customer/DataProviderTest.php | 2 +- .../Attribute/CollectionTest.php | 2 +- .../ResourceModel/Problem/Collection.php | 4 ++-- .../Model/ResourceModel/Queue/Collection.php | 2 +- .../Model/ResourceModel/Carrier/Tablerate.php | 2 +- .../GuestCart/GuestCartManagementTest.php | 2 +- .../Controller/Adminhtml/Order/Unhold.php | 2 +- .../Unit/Block/Order/Create/TotalsTest.php | 6 ++--- .../SalesRule/Model/ResourceModel/Rule.php | 2 +- .../Shipping/Model/Shipping/Labels.php | 8 +++---- .../Unit/Model/TaxClass/Type/CustomerTest.php | 6 ++--- .../System/Design/Theme/UploadJsTest.php | 24 +++++++++---------- .../Unit/Model/ResourceModel/UserTest.php | 2 +- .../Framework/App/Test/Unit/BootstrapTest.php | 2 +- .../Test/Unit/DependencyCheckerTest.php | 2 +- lib/internal/Magento/Framework/File/Size.php | 4 ++-- .../Magento/Framework/Image/Adapter/Gd2.php | 8 +++---- .../Magento/Framework/Indexer/Action/Base.php | 2 +- .../Magento/Framework/RequireJs/Config.php | 2 +- .../FileCollector/AggregatedFileCollector.php | 2 +- .../Framework/View/Model/Layout/Merge.php | 2 +- .../View/Page/Config/Reader/Head.php | 2 +- .../Unit/Element/Html/Link/CurrentTest.php | 2 +- .../Framework/View/Test/Unit/LayoutTest.php | 2 +- .../Magento/Setup/Model/PhpReadinessCheck.php | 4 ++-- 36 files changed, 61 insertions(+), 61 deletions(-) diff --git a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Export/AdvancedPricingTest.php b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Export/AdvancedPricingTest.php index 48b4c58918740..17e7ffc20c5e9 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Export/AdvancedPricingTest.php +++ b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Export/AdvancedPricingTest.php @@ -213,7 +213,7 @@ protected function setUp() '_getCustomerGroupById', 'correctExportData' ]); - $this->advancedPricing = $this->getMockbuilder( + $this->advancedPricing = $this->getMockBuilder( \Magento\AdvancedPricingImportExport\Model\Export\AdvancedPricing::class ) ->setMethods($mockMethods) diff --git a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/AdditionalCommentTest.php b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/AdditionalCommentTest.php index cbf06264096ac..407e323aeaae6 100644 --- a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/AdditionalCommentTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/AdditionalCommentTest.php @@ -65,11 +65,11 @@ public function testRender() ->method('getLabel') ->willReturn('Comment label'); $html = $this->additionalComment->render($this->abstractElementMock); - $this->assertRegexp( + $this->assertRegExp( "/New comment/", $html ); - $this->assertRegexp( + $this->assertRegExp( "/Comment label/", $html ); diff --git a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php index 462b3c909a7fd..d567d65882350 100644 --- a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php @@ -87,7 +87,7 @@ public function testRender() $this->localeResolver->expects($this->once()) ->method('getLocale') ->willReturn('en_US'); - $this->assertRegexp( + $this->assertRegExp( "/Eastern Standard Time \(America\/New_York\)/", $this->collectionTimeLabel->render($this->abstractElementMock) ); diff --git a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/SubscriptionStatusLabelTest.php b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/SubscriptionStatusLabelTest.php index d643bc05cc615..78ff581f3de9d 100644 --- a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/SubscriptionStatusLabelTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/SubscriptionStatusLabelTest.php @@ -74,7 +74,7 @@ public function testRender() $this->abstractElementMock->expects($this->any()) ->method('getComment') ->willReturn('Subscription status: Enabled'); - $this->assertRegexp( + $this->assertRegExp( "/Subscription status: Enabled/", $this->subscriptionStatusLabel->render($this->abstractElementMock) ); diff --git a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/VerticalTest.php b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/VerticalTest.php index abce48c36c86a..6a0cecc781062 100644 --- a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/VerticalTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/VerticalTest.php @@ -65,7 +65,7 @@ public function testRender() ->method('getHint') ->willReturn('New hint'); $html = $this->vertical->render($this->abstractElementMock); - $this->assertRegexp( + $this->assertRegExp( "/New comment/", $html ); diff --git a/app/code/Magento/Braintree/Gateway/Response/CardDetailsHandler.php b/app/code/Magento/Braintree/Gateway/Response/CardDetailsHandler.php index e89e604867baa..32abeac4c8ffb 100644 --- a/app/code/Magento/Braintree/Gateway/Response/CardDetailsHandler.php +++ b/app/code/Magento/Braintree/Gateway/Response/CardDetailsHandler.php @@ -85,7 +85,7 @@ public function handle(array $handlingSubject, array $response) private function getCreditCardType($type) { $replaced = str_replace(' ', '-', strtolower($type)); - $mapper = $this->config->getCctypesMapper(); + $mapper = $this->config->getCcTypesMapper(); return $mapper[$replaced]; } diff --git a/app/code/Magento/Braintree/Gateway/Response/VaultDetailsHandler.php b/app/code/Magento/Braintree/Gateway/Response/VaultDetailsHandler.php index 7c307185b4c22..8880f9c1b1a3e 100644 --- a/app/code/Magento/Braintree/Gateway/Response/VaultDetailsHandler.php +++ b/app/code/Magento/Braintree/Gateway/Response/VaultDetailsHandler.php @@ -157,7 +157,7 @@ private function convertDetailsToJSON($details) private function getCreditCardType($type) { $replaced = str_replace(' ', '-', strtolower($type)); - $mapper = $this->config->getCctypesMapper(); + $mapper = $this->config->getCcTypesMapper(); return $mapper[$replaced]; } diff --git a/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php b/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php index fe30e790de07c..928769498a035 100644 --- a/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php +++ b/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php @@ -70,7 +70,7 @@ public function getConfig() self::CODE => [ 'isActive' => $this->config->isActive($storeId), 'clientToken' => $this->getClientToken(), - 'ccTypesMapper' => $this->config->getCctypesMapper(), + 'ccTypesMapper' => $this->config->getCcTypesMapper(), 'sdkUrl' => $this->config->getSdkUrl(), 'countrySpecificCardTypes' => $this->config->getCountrySpecificCardTypeConfig($storeId), 'availableCardTypes' => $this->config->getAvailableCardTypes($storeId), diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Config/ConfigTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Config/ConfigTest.php index 7b9d59a5bc482..36ea3aea465dd 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Config/ConfigTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Config/ConfigTest.php @@ -142,7 +142,7 @@ public function testGetCcTypesMapper($value, $expected) static::assertEquals( $expected, - $this->model->getCctypesMapper() + $this->model->getCcTypesMapper() ); } diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/ShowUpdateResultTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/ShowUpdateResultTest.php index ba716fdb53c89..47a60a1916142 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/ShowUpdateResultTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/ShowUpdateResultTest.php @@ -58,7 +58,7 @@ protected function getContext() $objectManagerMock = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class); $objectManagerMock->expects($this->any()) ->method('get') - ->willreturn($productActionMock); + ->willReturn($productActionMock); $eventManager = $this->getMockBuilder(\Magento\Framework\Event\Manager::class) ->setMethods(['dispatch']) diff --git a/app/code/Magento/Checkout/Setup/Patch/Data/PrepareInitialCheckoutConfiguration.php b/app/code/Magento/Checkout/Setup/Patch/Data/PrepareInitialCheckoutConfiguration.php index bc38809d070b2..47a19fb3234fd 100644 --- a/app/code/Magento/Checkout/Setup/Patch/Data/PrepareInitialCheckoutConfiguration.php +++ b/app/code/Magento/Checkout/Setup/Patch/Data/PrepareInitialCheckoutConfiguration.php @@ -817,7 +817,7 @@ public function apply() $connection->commit(); } catch (\Exception $e) { - $connection->rollback(); + $connection->rollBack(); throw $e; } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php index 029949c5f35b0..71b99586f6fb4 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php @@ -1266,7 +1266,7 @@ public function testGetDataWithVisibleAttributesWithAccountEdit() $helper = new ObjectManager($this); $context = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\ContextInterface::class) ->setMethods(['getRequestParam']) - ->getMockforAbstractClass(); + ->getMockForAbstractClass(); $context->expects($this->any()) ->method('getRequestParam') ->with('request-field-name') diff --git a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Attribute/CollectionTest.php b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Attribute/CollectionTest.php index d988dfc63c959..a9aead6325c39 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Attribute/CollectionTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Attribute/CollectionTest.php @@ -115,7 +115,7 @@ protected function setUp() $this->connectionMock->expects($this->any())->method('quoteIdentifier')->will($this->returnArgument(0)); $this->connectionMock->expects($this->any()) ->method('describeTable') - ->will($this->returnvalueMap( + ->will($this->returnValueMap( [ [ 'some_main_table', diff --git a/app/code/Magento/Newsletter/Model/ResourceModel/Problem/Collection.php b/app/code/Magento/Newsletter/Model/ResourceModel/Problem/Collection.php index c0c5dfef277b8..0d7ebd74f724f 100644 --- a/app/code/Magento/Newsletter/Model/ResourceModel/Problem/Collection.php +++ b/app/code/Magento/Newsletter/Model/ResourceModel/Problem/Collection.php @@ -165,8 +165,8 @@ protected function _addCustomersData() $customerName = $this->_customerView->getCustomerName($customer); foreach ($problems as $problem) { $problem->setCustomerName($customerName) - ->setCustomerFirstName($customer->getFirstName()) - ->setCustomerLastName($customer->getLastName()); + ->setCustomerFirstName($customer->getFirstname()) + ->setCustomerLastName($customer->getLastname()); } } catch (NoSuchEntityException $e) { // do nothing if customer is not found by id diff --git a/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php b/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php index 9e98998f7f6ec..189549d2a73d2 100644 --- a/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php +++ b/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php @@ -221,7 +221,7 @@ public function addOnlyForSendingFilter() [\Magento\Newsletter\Model\Queue::STATUS_SENDING, \Magento\Newsletter\Model\Queue::STATUS_NEVER] )->where( 'main_table.queue_start_at < ?', - $this->_date->gmtdate() + $this->_date->gmtDate() )->where( 'main_table.queue_start_at IS NOT NULL' ); diff --git a/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate.php index fc36eaf6a97db..0c4718245928b 100644 --- a/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate.php +++ b/app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate.php @@ -320,7 +320,7 @@ public function getConditionName(\Magento\Framework\DataObject $object) */ private function getCsvFile($filePath) { - $pathInfo = pathInfo($filePath); + $pathInfo = pathinfo($filePath); $dirName = isset($pathInfo['dirname']) ? $pathInfo['dirname'] : ''; $fileName = isset($pathInfo['basename']) ? $pathInfo['basename'] : ''; diff --git a/app/code/Magento/Quote/Test/Unit/Model/GuestCart/GuestCartManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/GuestCart/GuestCartManagementTest.php index ee0ffd3bcc666..73ed2e65b41a9 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/GuestCart/GuestCartManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/GuestCart/GuestCartManagementTest.php @@ -94,7 +94,7 @@ public function testCreateEmptyCart() $cartId = 1; $this->quoteIdMaskMock->expects($this->once())->method('setQuoteId')->with($cartId)->willReturnSelf(); $this->quoteIdMaskMock->expects($this->once())->method('save')->willReturnSelf(); - $this->quoteIdMaskMock->expects($this->once())->method('getMaskedId')->willreturn($maskedCartId); + $this->quoteIdMaskMock->expects($this->once())->method('getMaskedId')->willReturn($maskedCartId); $this->quoteIdMaskFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteIdMaskMock); $this->quoteManagementMock->expects($this->once())->method('createEmptyCart')->willReturn($cartId); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Unhold.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Unhold.php index 752ab088689c8..fa9676856a442 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Unhold.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Unhold.php @@ -32,7 +32,7 @@ public function execute() if (!$order->canUnhold()) { throw new \Magento\Framework\Exception\LocalizedException(__('Can\'t unhold order.')); } - $this->orderManagement->unhold($order->getEntityId()); + $this->orderManagement->unHold($order->getEntityId()); $this->messageManager->addSuccess(__('You released the order from holding status.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addError($e->getMessage()); diff --git a/app/code/Magento/Sales/Test/Unit/Block/Order/Create/TotalsTest.php b/app/code/Magento/Sales/Test/Unit/Block/Order/Create/TotalsTest.php index 2a839dd018dba..492cfee5f5d83 100644 --- a/app/code/Magento/Sales/Test/Unit/Block/Order/Create/TotalsTest.php +++ b/app/code/Magento/Sales/Test/Unit/Block/Order/Create/TotalsTest.php @@ -70,10 +70,10 @@ protected function setUp() $this->quoteMock->expects($this->any()) ->method('getBillingAddress') - ->willreturn($this->billingAddressMock); + ->willReturn($this->billingAddressMock); $this->quoteMock->expects($this->any()) ->method('getShippingAddress') - ->willreturn($this->shippingAddressMock); + ->willReturn($this->shippingAddressMock); $this->sessionQuoteMock->expects($this->any())->method('getQuote')->willReturn($this->quoteMock); $this->totals = $this->helperManager->getObject( \Magento\Sales\Block\Adminhtml\Order\Create\Totals::class, @@ -88,7 +88,7 @@ public function testGetTotals($isVirtual) { $expected = 'expected'; $this->quoteMock->expects($this->at(1))->method('collectTotals'); - $this->quoteMock->expects($this->once())->method('isVirtual')->willreturn($isVirtual); + $this->quoteMock->expects($this->once())->method('isVirtual')->willReturn($isVirtual); if ($isVirtual) { $this->billingAddressMock->expects($this->once())->method('getTotals')->willReturn($expected); } else { diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php index 794fc94d6a2a8..3a5ed16fdd2fd 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php @@ -239,7 +239,7 @@ public function saveStoreLabels($ruleId, $labels) $connection->delete($table, ['rule_id=?' => $ruleId, 'store_id IN (?)' => $deleteByStoreIds]); } } catch (\Exception $e) { - $connection->rollback(); + $connection->rollBack(); throw $e; } $connection->commit(); diff --git a/app/code/Magento/Shipping/Model/Shipping/Labels.php b/app/code/Magento/Shipping/Model/Shipping/Labels.php index 5c796d9fa6897..08ce168567182 100644 --- a/app/code/Magento/Shipping/Model/Shipping/Labels.php +++ b/app/code/Magento/Shipping/Model/Shipping/Labels.php @@ -117,8 +117,8 @@ public function requestToShipment(Shipment $orderShipment) ) ); - if (!$admin->getFirstname() - || !$admin->getLastname() + if (!$admin->getFirstName() + || !$admin->getLastName() || !$storeInfo->getName() || !$storeInfo->getPhone() || !$originStreet1 @@ -188,8 +188,8 @@ protected function setShipperDetails( ); $request->setShipperContactPersonName($storeAdmin->getName()); - $request->setShipperContactPersonFirstName($storeAdmin->getFirstname()); - $request->setShipperContactPersonLastName($storeAdmin->getLastname()); + $request->setShipperContactPersonFirstName($storeAdmin->getFirstName()); + $request->setShipperContactPersonLastName($storeAdmin->getLastName()); $request->setShipperContactCompanyName($store->getName()); $request->setShipperContactPhoneNumber($store->getPhone()); $request->setShipperEmail($storeAdmin->getEmail()); diff --git a/app/code/Magento/Tax/Test/Unit/Model/TaxClass/Type/CustomerTest.php b/app/code/Magento/Tax/Test/Unit/Model/TaxClass/Type/CustomerTest.php index fc27e68c8e55b..707b999c5e467 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/TaxClass/Type/CustomerTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/TaxClass/Type/CustomerTest.php @@ -26,9 +26,9 @@ public function testIsAssignedToObjects() $filterBuilder->expects($this->once())->method('setField')->with( \Magento\Customer\Api\Data\GroupInterface::TAX_CLASS_ID - )->willReturnself(); - $filterBuilder->expects($this->once())->method('setValue')->willReturnself(); - $filterBuilder->expects($this->once())->method('create')->willReturnself(); + )->willReturnSelf(); + $filterBuilder->expects($this->once())->method('setValue')->willReturnSelf(); + $filterBuilder->expects($this->once())->method('create')->willReturnSelf(); $filterGroupBuilder = $this->createMock(\Magento\Framework\Api\Search\FilterGroupBuilder::class); $searchCriteriaBuilder = $this->getMockBuilder(\Magento\Framework\Api\SearchCriteriaBuilder::class) diff --git a/app/code/Magento/Theme/Test/Unit/Controller/Adminhtml/System/Design/Theme/UploadJsTest.php b/app/code/Magento/Theme/Test/Unit/Controller/Adminhtml/System/Design/Theme/UploadJsTest.php index d5919db5edcba..bbcaa87acb9c3 100644 --- a/app/code/Magento/Theme/Test/Unit/Controller/Adminhtml/System/Design/Theme/UploadJsTest.php +++ b/app/code/Magento/Theme/Test/Unit/Controller/Adminhtml/System/Design/Theme/UploadJsTest.php @@ -63,22 +63,22 @@ public function testExecuteWithoutTheme() ->expects($this->at(0)) ->method('get') ->with(\Magento\Theme\Model\Uploader\Service::class) - ->WillReturn($this->serviceModel); + ->willReturn($this->serviceModel); $this->_objectManagerMock ->expects($this->at(1)) ->method('get') ->with(\Magento\Framework\View\Design\Theme\FlyweightFactory::class) - ->WillReturn($this->themeFactory); + ->willReturn($this->themeFactory); $this->_objectManagerMock ->expects($this->at(2)) ->method('get') ->with(\Magento\Framework\View\Design\Theme\Customization\File\Js::class) - ->WillReturn($this->customizationJs); + ->willReturn($this->customizationJs); $this->_objectManagerMock ->expects($this->at(3)) ->method('get') ->with(\Magento\Framework\Json\Helper\Data::class) - ->WillReturn($this->jsonHelper); + ->willReturn($this->jsonHelper); $this->themeFactory->expects($this->once()) ->method('create') @@ -107,21 +107,21 @@ public function testExecuteWithException() $this->_objectManagerMock->expects($this->at(0)) ->method('get') ->with(\Magento\Theme\Model\Uploader\Service::class) - ->WillReturn($this->serviceModel); + ->willReturn($this->serviceModel); $this->_objectManagerMock->expects($this->at(1)) ->method('get') ->with(\Magento\Framework\View\Design\Theme\FlyweightFactory::class) - ->WillReturn($this->themeFactory); + ->willReturn($this->themeFactory); $this->_objectManagerMock ->expects($this->at(2)) ->method('get') ->with(\Magento\Framework\View\Design\Theme\Customization\File\Js::class) - ->WillReturn($this->customizationJs); + ->willReturn($this->customizationJs); $this->_objectManagerMock ->expects($this->at(4)) ->method('get') ->with(\Magento\Framework\Json\Helper\Data::class) - ->WillReturn($this->jsonHelper); + ->willReturn($this->jsonHelper); $this->themeFactory->expects($this->once()) ->method('create') @@ -172,19 +172,19 @@ public function testExecute() $this->_objectManagerMock->expects($this->at(0)) ->method('get') ->with(\Magento\Theme\Model\Uploader\Service::class) - ->WillReturn($this->serviceModel); + ->willReturn($this->serviceModel); $this->_objectManagerMock->expects($this->at(1)) ->method('get') ->with(\Magento\Framework\View\Design\Theme\FlyweightFactory::class) - ->WillReturn($this->themeFactory); + ->willReturn($this->themeFactory); $this->_objectManagerMock->expects($this->at(2)) ->method('get') ->with(\Magento\Framework\View\Design\Theme\Customization\File\Js::class) - ->WillReturn($this->customizationJs); + ->willReturn($this->customizationJs); $this->_objectManagerMock->expects($this->at(4)) ->method('get') ->with(\Magento\Framework\Json\Helper\Data::class) - ->WillReturn($this->jsonHelper); + ->willReturn($this->jsonHelper); $this->themeFactory->expects($this->once()) ->method('create') diff --git a/app/code/Magento/User/Test/Unit/Model/ResourceModel/UserTest.php b/app/code/Magento/User/Test/Unit/Model/ResourceModel/UserTest.php index 16cde3cfe2c06..1cc0cd54c60eb 100644 --- a/app/code/Magento/User/Test/Unit/Model/ResourceModel/UserTest.php +++ b/app/code/Magento/User/Test/Unit/Model/ResourceModel/UserTest.php @@ -330,7 +330,7 @@ public function testDeleteFromRole() $roleId = 44; $methodUserMock->expects($this->once())->method('getUserId')->willReturn($uid); $this->resourceMock->expects($this->atLeastOnce())->method('getConnection')->willReturn($this->dbAdapterMock); - $methodUserMock->expects($this->atleastOnce())->method('getRoleId')->willReturn($roleId); + $methodUserMock->expects($this->atLeastOnce())->method('getRoleId')->willReturn($roleId); $this->dbAdapterMock->expects($this->once())->method('delete'); $this->assertInstanceOf( diff --git a/lib/internal/Magento/Framework/App/Test/Unit/BootstrapTest.php b/lib/internal/Magento/Framework/App/Test/Unit/BootstrapTest.php index 1e2947084ee6b..4b04507dcedc0 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/BootstrapTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/BootstrapTest.php @@ -135,7 +135,7 @@ public function testCreateFilesystemDriverPool() ); /** @var \Magento\Framework\Filesystem\DriverPool $result */ $this->assertInstanceOf(\Magento\Framework\Filesystem\DriverPool::class, $result); - $this->assertInstanceof($driverClass, $result->getDriver('custom')); + $this->assertInstanceOf($driverClass, $result->getDriver('custom')); } public function testGetParams() diff --git a/lib/internal/Magento/Framework/Composer/Test/Unit/DependencyCheckerTest.php b/lib/internal/Magento/Framework/Composer/Test/Unit/DependencyCheckerTest.php index 801d99373bc50..15ceb0c6c2755 100644 --- a/lib/internal/Magento/Framework/Composer/Test/Unit/DependencyCheckerTest.php +++ b/lib/internal/Magento/Framework/Composer/Test/Unit/DependencyCheckerTest.php @@ -69,7 +69,7 @@ function ($input, $buffer) { $buffer->writeln($output); } ); - $composerApp->Expects($this->at(6))->method('run')->willReturnCallback( + $composerApp->expects($this->at(6))->method('run')->willReturnCallback( function ($input, $buffer) { $output = 'magento/package-d requires magento/package-c (1.0)' . PHP_EOL . 'magento/project-community-edition requires magento/package-a (1.0)' . PHP_EOL; diff --git a/lib/internal/Magento/Framework/File/Size.php b/lib/internal/Magento/Framework/File/Size.php index 6f48024f71c16..c259069637293 100644 --- a/lib/internal/Magento/Framework/File/Size.php +++ b/lib/internal/Magento/Framework/File/Size.php @@ -36,7 +36,7 @@ class Size */ public function getPostMaxSize() { - return $this->_iniget('post_max_size'); + return $this->_iniGet('post_max_size'); } /** @@ -46,7 +46,7 @@ public function getPostMaxSize() */ public function getUploadMaxSize() { - return $this->_iniget('upload_max_filesize'); + return $this->_iniGet('upload_max_filesize'); } /** diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php index 93274b1d063bf..ea21faf3f340d 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php @@ -106,7 +106,7 @@ protected function _getImageNeedMemorySize($file) } return round( - ($imageInfo[0] * $imageInfo[1] * $imageInfo['bits'] * $imageInfo['channels'] / 8 + Pow(2, 16)) * 1.65 + ($imageInfo[0] * $imageInfo[1] * $imageInfo['bits'] * $imageInfo['channels'] / 8 + pow(2, 16)) * 1.65 ); } @@ -426,7 +426,7 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = imagecolortransparent($newWatermark, $col); imagefilledrectangle($newWatermark, 0, 0, $this->getWatermarkWidth(), $this->getWatermarkHeight(), $col); imagealphablending($newWatermark, true); - imageSaveAlpha($newWatermark, true); + imagesavealpha($newWatermark, true); imagecopyresampled( $newWatermark, $watermark, @@ -451,7 +451,7 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = imagecolortransparent($newWatermark, $col); imagefilledrectangle($newWatermark, 0, 0, $this->_imageSrcWidth, $this->_imageSrcHeight, $col); imagealphablending($newWatermark, true); - imageSaveAlpha($newWatermark, true); + imagesavealpha($newWatermark, true); imagecopyresampled( $newWatermark, $watermark, @@ -669,7 +669,7 @@ private function imageDestroy() private function _saveAlpha($imageHandler) { $background = imagecolorallocate($imageHandler, 0, 0, 0); - ImageColorTransparent($imageHandler, $background); + imagecolortransparent($imageHandler, $background); imagealphablending($imageHandler, false); imagesavealpha($imageHandler, true); } diff --git a/lib/internal/Magento/Framework/Indexer/Action/Base.php b/lib/internal/Magento/Framework/Indexer/Action/Base.php index 11cf1cec62636..636335192cbc5 100644 --- a/lib/internal/Magento/Framework/Indexer/Action/Base.php +++ b/lib/internal/Magento/Framework/Indexer/Action/Base.php @@ -221,7 +221,7 @@ protected function prepareDataSource(array $ids = []) { return !count($ids) ? $this->createResultCollection() - : $this->createResultCollection()->addFieldToFilter($this->getPrimaryResource()->getIdFieldname(), $ids); + : $this->createResultCollection()->addFieldToFilter($this->getPrimaryResource()->getIdFieldName(), $ids); } /** diff --git a/lib/internal/Magento/Framework/RequireJs/Config.php b/lib/internal/Magento/Framework/RequireJs/Config.php index 08131b2cccea3..ae45e29f38911 100644 --- a/lib/internal/Magento/Framework/RequireJs/Config.php +++ b/lib/internal/Magento/Framework/RequireJs/Config.php @@ -157,7 +157,7 @@ public function getConfig() $customConfigFiles = $this->fileSource->getFiles($this->design->getDesignTheme(), self::CONFIG_FILE_NAME); foreach ($customConfigFiles as $file) { /** @var $fileReader \Magento\Framework\Filesystem\File\Read */ - $fileReader = $this->readFactory->create($file->getFileName(), DriverPool::FILE); + $fileReader = $this->readFactory->create($file->getFilename(), DriverPool::FILE); $config = $fileReader->readAll($file->getName()); $distributedConfig .= str_replace( ['%config%', '%context%'], diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Config/FileCollector/AggregatedFileCollector.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Config/FileCollector/AggregatedFileCollector.php index 17a320164dad3..9ba7833a355d7 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Config/FileCollector/AggregatedFileCollector.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Config/FileCollector/AggregatedFileCollector.php @@ -75,7 +75,7 @@ public function collectFiles($searchPattern = null) } $files = $this->collectorAggregated->getFiles($this->design->getDesignTheme(), $searchPattern); foreach ($files as $file) { - $fullFileName = $file->getFileName(); + $fullFileName = $file->getFilename(); $fileDir = dirname($fullFileName); $fileName = basename($fullFileName); $dirRead = $this->readFactory->create($fileDir); diff --git a/lib/internal/Magento/Framework/View/Model/Layout/Merge.php b/lib/internal/Magento/Framework/View/Model/Layout/Merge.php index fe5c94ed21b30..5a19f4f0b9b58 100644 --- a/lib/internal/Magento/Framework/View/Model/Layout/Merge.php +++ b/lib/internal/Magento/Framework/View/Model/Layout/Merge.php @@ -672,7 +672,7 @@ public function getFileLayoutUpdatesXml() $result = $this->_loadXmlString($result); } else { $result = $this->_loadFileLayoutUpdatesXml(); - $this->_saveCache($result->asXml(), $cacheId); + $this->_saveCache($result->asXML(), $cacheId); } $this->layoutUpdatesCache = $result; return $result; diff --git a/lib/internal/Magento/Framework/View/Page/Config/Reader/Head.php b/lib/internal/Magento/Framework/View/Page/Config/Reader/Head.php index 8328f03fd6db5..71a2951b40420 100644 --- a/lib/internal/Magento/Framework/View/Page/Config/Reader/Head.php +++ b/lib/internal/Magento/Framework/View/Page/Config/Reader/Head.php @@ -138,6 +138,6 @@ private function setMetadata($pageConfigStructure, $node) $metadataName = $node->getAttribute('name'); } - $pageConfigStructure->setMetaData($metadataName, $node->getAttribute('content')); + $pageConfigStructure->setMetadata($metadataName, $node->getAttribute('content')); } } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php index 4515a29c65e4f..909748722a081 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php @@ -57,7 +57,7 @@ public function testIsCurrentIfIsset() /** @var \Magento\Framework\View\Element\Html\Link\Current $link */ $link = $this->_objectManager->getObject(\Magento\Framework\View\Element\Html\Link\Current::class); $link->setCurrent(true); - $this->assertTrue($link->IsCurrent()); + $this->assertTrue($link->isCurrent()); } public function testIsCurrent() diff --git a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php index c0300164e26fe..f8a8939bbfe36 100755 --- a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php @@ -273,7 +273,7 @@ public function testGenerateXml() ->with($this->equalTo([])) ->will($this->returnSelf()); $this->assertSame($this->model, $this->model->generateXml()); - $this->assertSame('<some_update>123</some_update>', $this->model->getNode('some_update')->asXml()); + $this->assertSame('<some_update>123</some_update>', $this->model->getNode('some_update')->asXML()); } public function testGetChildBlock() diff --git a/setup/src/Magento/Setup/Model/PhpReadinessCheck.php b/setup/src/Magento/Setup/Model/PhpReadinessCheck.php index c2bdafa8df82e..2c4967c4c2ffd 100644 --- a/setup/src/Magento/Setup/Model/PhpReadinessCheck.php +++ b/setup/src/Magento/Setup/Model/PhpReadinessCheck.php @@ -286,7 +286,7 @@ private function checkPopulateRawPostSetting() $data = []; $error = false; - $iniSetting = intVal(ini_get('always_populate_raw_post_data')); + $iniSetting = intval(ini_get('always_populate_raw_post_data')); $checkVersionConstraint = $this->versionParser->parseConstraints('~5.6.0'); $normalizedPhpVersion = $this->getNormalizedCurrentPhpVersion(PHP_VERSION); @@ -302,7 +302,7 @@ private function checkPopulateRawPostSetting() Please open your php.ini file and set always_populate_raw_post_data to -1. If you need more help please call your hosting provider.', PHP_VERSION, - intVal(ini_get('always_populate_raw_post_data')) + intval(ini_get('always_populate_raw_post_data')) ); $data['always_populate_raw_post_data'] = [ From b3b0d70c9453a76d2f457aef2ba53539e8ec6aca Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Sun, 24 Jun 2018 17:20:42 -0500 Subject: [PATCH 0076/1171] MAGETWO-91433: ProductListing: Grid view is getting changed to List view when user adding product from wishlist section. --- app/code/Magento/Store/App/Response/Redirect.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Store/App/Response/Redirect.php b/app/code/Magento/Store/App/Response/Redirect.php index 63dd844409983..b18f2cfc22613 100644 --- a/app/code/Magento/Store/App/Response/Redirect.php +++ b/app/code/Magento/Store/App/Response/Redirect.php @@ -83,8 +83,7 @@ protected function _getUrl() { $refererUrl = $this->_request->getServer('HTTP_REFERER'); $encodedUrl = $this->_request->getParam(\Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED) - ? $this->_request->getParam(\Magento\Framework\App\ActionInterface::PARAM_NAME_BASE64_URL) - : ''; + ?: $this->_request->getParam(\Magento\Framework\App\ActionInterface::PARAM_NAME_BASE64_URL); if ($encodedUrl) { $refererUrl = $this->_urlCoder->decode($encodedUrl); From 2da9d0015a9b2c8d3a5c625d4938c29b950c6b09 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Mon, 25 Jun 2018 14:18:13 -0500 Subject: [PATCH 0077/1171] MAGETWO-91433: ProductListing: Grid view is getting changed to List view when user adding product from wishlist section. --- .../Wishlist/Controller/Index/Remove.php | 23 ++++---- .../Model/Product/AttributeValueProvider.php | 57 +++++++++++++++++++ 2 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 app/code/Magento/Wishlist/Model/Product/AttributeValueProvider.php diff --git a/app/code/Magento/Wishlist/Controller/Index/Remove.php b/app/code/Magento/Wishlist/Controller/Index/Remove.php index 7690bf8314866..84c59b5be3d1e 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Remove.php +++ b/app/code/Magento/Wishlist/Controller/Index/Remove.php @@ -5,13 +5,13 @@ */ namespace Magento\Wishlist\Controller\Index; -use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; use Magento\Framework\App\Action; use Magento\Framework\Data\Form\FormKey\Validator; use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Controller\ResultFactory; use Magento\Wishlist\Controller\WishlistProviderInterface; use Magento\Wishlist\Model\Item; +use Magento\Wishlist\Model\Product\AttributeValueProvider; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -29,26 +29,26 @@ class Remove extends \Magento\Wishlist\Controller\AbstractIndex protected $formKeyValidator; /** - * @var ProductCollectionFactory + * @var AttributeValueProvider */ - private $productCollectionFactory; + private $attributeValueProvider; /** * @param Action\Context $context * @param WishlistProviderInterface $wishlistProvider * @param Validator $formKeyValidator - * @param ProductCollectionFactory|null $productCollectionFactory + * @param AttributeValueProvider|null $attributeValueProvider */ public function __construct( Action\Context $context, WishlistProviderInterface $wishlistProvider, Validator $formKeyValidator, - ProductCollectionFactory $productCollectionFactory = null + AttributeValueProvider $attributeValueProvider = null ) { $this->wishlistProvider = $wishlistProvider; $this->formKeyValidator = $formKeyValidator; - $this->productCollectionFactory = $productCollectionFactory - ?: \Magento\Framework\App\ObjectManager::getInstance()->get(ProductCollectionFactory::class); + $this->attributeValueProvider = $attributeValueProvider + ?: \Magento\Framework\App\ObjectManager::getInstance()->get(AttributeValueProvider::class); parent::__construct($context); } @@ -79,15 +79,12 @@ public function execute() try { $item->delete(); $wishlist->save(); - $product = $this->productCollectionFactory - ->create() - ->addIdFilter($item->getProductId()) - ->addAttributeToSelect('name') - ->getFirstItem(); + $productName = $this->attributeValueProvider + ->getRawAttributeValue($item->getProductId(), 'name'); $this->messageManager->addComplexSuccessMessage( 'removeWishlistItemSuccessMessage', [ - 'product_name' => $product->getName() + 'product_name' => $productName, ] ); } catch (\Magento\Framework\Exception\LocalizedException $e) { diff --git a/app/code/Magento/Wishlist/Model/Product/AttributeValueProvider.php b/app/code/Magento/Wishlist/Model/Product/AttributeValueProvider.php new file mode 100644 index 0000000000000..03f5815e894c4 --- /dev/null +++ b/app/code/Magento/Wishlist/Model/Product/AttributeValueProvider.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Model\Product; + +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; +use Magento\Framework\Exception\NoSuchEntityException; + +/** + * Provides existing attribute value for a product entity. + */ +class AttributeValueProvider +{ + /** + * @var ProductCollectionFactory + */ + private $productCollectionFactory; + + /** + * @param ProductCollectionFactory $productCollectionFactory + */ + public function __construct( + ProductCollectionFactory $productCollectionFactory + ) { + $this->productCollectionFactory = $productCollectionFactory; + } + + /** + * Provides existing raw attribute value by the attribute code of the product entity. + * + * @param int $productId + * @param string $attributeCode + * @param int|null $storeId + * @return null|string + * @throws NoSuchEntityException + */ + public function getRawAttributeValue(int $productId, string $attributeCode, int $storeId = null):? string + { + + $collection = $this->productCollectionFactory->create(); + $collection->addIdFilter($productId) + ->addStoreFilter($storeId) + ->addAttributeToSelect($attributeCode); + + $data = $collection->getConnection()->fetchRow($collection->getSelect()); + + if (!array_key_exists($attributeCode, $data)) { + throw new NoSuchEntityException(__('An attribute value of "%1" does not exist.', $attributeCode)); + } + + return $data[$attributeCode]; + } +} From 7c3c6a42e06d784711851fa68fa6cf85d40aa91c Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Mon, 25 Jun 2018 15:26:32 -0500 Subject: [PATCH 0078/1171] MAGETWO-91606: Upgrade to 2.2.2 and above took long time --- .../Patch/Data/UpgradeWebsiteAttributes.php | 22 ++++++- .../SetInitialSearchWeightForAttributes.php | 23 ++++++- .../CatalogSearch/Setup/RecurringData.php | 62 ------------------- 3 files changed, 39 insertions(+), 68 deletions(-) delete mode 100644 app/code/Magento/CatalogSearch/Setup/RecurringData.php diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/UpgradeWebsiteAttributes.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpgradeWebsiteAttributes.php index f9d6abbc37493..a190bde2c6775 100644 --- a/app/code/Magento/Catalog/Setup/Patch/Data/UpgradeWebsiteAttributes.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpgradeWebsiteAttributes.php @@ -156,6 +156,21 @@ private function processAttributeValues(array $attributeValueItems, $tableName) */ private function fetchAttributeValues($tableName) { + //filter store groups which have more than 1 store + $multipleStoresInWebsite = array_values( + array_reduce( + array_filter($this->getGroupedStoreViews(), function ($storeViews) { + return is_array($storeViews) && count($storeViews) > 1; + }), + 'array_merge', + [] + ) + ); + + if (count($multipleStoresInWebsite) < 1) { + return []; + } + $connection = $this->moduleDataSetup->getConnection(); $batchSelectIterator = $this->batchQueryGenerator->generate( 'value_id', @@ -184,9 +199,10 @@ private function fetchAttributeValues($tableName) self::ATTRIBUTE_WEBSITE ) ->where( - 'cpei.store_id <> ?', - self::GLOBAL_STORE_VIEW_ID - ) + 'cpei.store_id IN (?)', + $multipleStoresInWebsite + ), + 1000 ); foreach ($batchSelectIterator as $select) { diff --git a/app/code/Magento/CatalogSearch/Setup/Patch/Data/SetInitialSearchWeightForAttributes.php b/app/code/Magento/CatalogSearch/Setup/Patch/Data/SetInitialSearchWeightForAttributes.php index e266e67804e88..beff1c66d4f61 100644 --- a/app/code/Magento/CatalogSearch/Setup/Patch/Data/SetInitialSearchWeightForAttributes.php +++ b/app/code/Magento/CatalogSearch/Setup/Patch/Data/SetInitialSearchWeightForAttributes.php @@ -6,6 +6,7 @@ namespace Magento\CatalogSearch\Setup\Patch\Data; +use Magento\Framework\App\State; use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Framework\Setup\Patch\PatchVersionInterface; use Magento\Framework\Indexer\IndexerInterfaceFactory; @@ -27,17 +28,25 @@ class SetInitialSearchWeightForAttributes implements DataPatchInterface, PatchVe */ private $attributeRepository; + /** + * @var State + */ + private $state; + /** * SetInitialSearchWeightForAttributes constructor. * @param IndexerInterfaceFactory $indexerFactory * @param ProductAttributeRepositoryInterface $attributeRepository + * @param State $state */ public function __construct( IndexerInterfaceFactory $indexerFactory, - ProductAttributeRepositoryInterface $attributeRepository + ProductAttributeRepositoryInterface $attributeRepository, + State $state ) { $this->indexerFactory = $indexerFactory; $this->attributeRepository = $attributeRepository; + $this->state = $state; } /** @@ -47,6 +56,13 @@ public function apply() { $this->setWeight('sku', 6); $this->setWeight('name', 5); + $indexer = $this->indexerFactory->create()->load('catalogsearch_fulltext'); + $this->state->emulateAreaCode( + \Magento\Framework\App\Area::AREA_CRONTAB, + function () use ($indexer) { + $indexer->reindexAll(); + } + ); } /** @@ -76,8 +92,9 @@ public function getAliases() /** * Set attribute search weight. * - * @param $attributeCode - * @param $weight + * @param string $attributeCode + * @param int $weight + * @return void */ private function setWeight($attributeCode, $weight) { diff --git a/app/code/Magento/CatalogSearch/Setup/RecurringData.php b/app/code/Magento/CatalogSearch/Setup/RecurringData.php deleted file mode 100644 index 0c2aee800b6f1..0000000000000 --- a/app/code/Magento/CatalogSearch/Setup/RecurringData.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\CatalogSearch\Setup; - -use Magento\Framework\App\State; -use Magento\Framework\Indexer\IndexerInterfaceFactory; -use Magento\Framework\Setup\InstallDataInterface; -use Magento\Framework\Setup\ModuleContextInterface; -use Magento\Framework\Setup\ModuleDataSetupInterface; - -/** - * Recurring data install. - */ -class RecurringData implements InstallDataInterface -{ - /** - * @var IndexerInterfaceFactory - */ - private $indexerInterfaceFactory; - /** - * @var State - */ - private $state; - - /** - * Init - * - * @param IndexerInterfaceFactory $indexerInterfaceFactory - */ - public function __construct( - IndexerInterfaceFactory $indexerInterfaceFactory, - State $state - ) { - $this->indexerInterfaceFactory = $indexerInterfaceFactory; - $this->state = $state; - } - - /** - * {@inheritdoc} - */ - public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) - { - $this->state->emulateAreaCode( - \Magento\Framework\App\Area::AREA_CRONTAB, - [$this, 'reindex'] - ); - } - - /** - * Run reindex. - * - * @return void - */ - public function reindex() - { - $this->indexerInterfaceFactory->create()->load('catalogsearch_fulltext')->reindexAll(); - } -} From 5233b27b1002ba9185d2cbd1a5051e77836fa9c1 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Mon, 25 Jun 2018 16:15:16 -0500 Subject: [PATCH 0079/1171] MAGETWO-91528: Customizable options truncated when displaying ordered product in admin --- .../Block/Adminhtml/Items/Column/Name.php | 19 +++- .../Framework/Filter/TruncateFilterTest.php | 60 ++++++++++ .../Block/Adminhtml/Items/Column/NameTest.php | 50 +++++++++ .../Magento/Framework/Filter/Factory.php | 1 + .../Framework/Filter/FilterManager.php | 1 + .../Framework/Filter/TruncateFilter.php | 103 ++++++++++++++++++ .../Filter/TruncateFilter/Result.php | 74 +++++++++++++ 7 files changed, 304 insertions(+), 4 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Items/Column/NameTest.php create mode 100644 lib/internal/Magento/Framework/Filter/TruncateFilter.php create mode 100644 lib/internal/Magento/Framework/Filter/TruncateFilter/Result.php diff --git a/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php b/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php index 1ed9d59d29a72..1b6a3748aa119 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php @@ -5,6 +5,8 @@ */ namespace Magento\Sales\Block\Adminhtml\Items\Column; +use Magento\Framework\Filter\TruncateFilter\Result; + /** * Sales Order items name column renderer * @@ -13,6 +15,11 @@ */ class Name extends \Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn { + /** + * @var Result + */ + private $truncateResult = null; + /** * Truncate string * @@ -25,10 +32,11 @@ class Name extends \Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn */ public function truncateString($value, $length = 80, $etc = '...', &$remainder = '', $breakWords = true) { - return $this->filterManager->truncate( + $this->truncateResult = $this->filterManager->truncateFilter( $value, - ['length' => $length, 'etc' => $etc, 'remainder' => $remainder, 'breakWords' => $breakWords] + ['length' => $length, 'etc' => $etc, 'breakWords' => $breakWords] ); + return $this->truncateResult->getValue(); } /** @@ -40,8 +48,11 @@ public function truncateString($value, $length = 80, $etc = '...', &$remainder = public function getFormattedOption($value) { $remainder = ''; - $value = $this->truncateString($value, 55, '', $remainder); - $result = ['value' => nl2br($value), 'remainder' => nl2br($remainder)]; + $this->truncateString($value, 55, '', $remainder); + $result = [ + 'value' => nl2br($this->truncateResult->getValue()), + 'remainder' => nl2br($this->truncateResult->getRemainder()) + ]; return $result; } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php new file mode 100644 index 0000000000000..efad23a650415 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Filter; + +class TruncateFilterTest extends \PHPUnit\Framework\TestCase +{ + /** + * @param string $expectedValue + * @param string $expectedRemainder + * @param string $string + * @param int $length + * @param string $etc + * @param bool $breakWords + * @dataProvider truncateDataProvider + */ + public function testFilter( + $expectedValue, $expectedRemainder, + $string, + $length = 5, + $etc = '...', + $breakWords = true + ) { + /** @var TruncateFilter $truncateFilter */ + $truncateFilter = \Magento\TestFramework\ObjectManager::getInstance()->create( + TruncateFilter::class, + [ + 'length' => $length, + 'etc' => $etc, + 'breakWords' => $breakWords, + ] + ); + $result = $truncateFilter->filter($string); + $this->assertEquals($expectedValue, $result->getValue()); + $this->assertEquals($expectedRemainder, $result->getRemainder()); + } + + public function truncateDataProvider() : array + { + return [ + '1' => [ + '12...', + '34567890', + '1234567890', + ], + '2' => [ + '123..', + ' 456 789', + '123 456 789', + 8, + '..', + false + ] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Items/Column/NameTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Items/Column/NameTest.php new file mode 100644 index 0000000000000..26bdbf3a6cfde --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Items/Column/NameTest.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Block\Adminhtml\Items\Column; + +/** + * @magentoAppArea adminhtml + */ +class NameTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Name + */ + private $block; + + protected function setUp() + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var $layout \Magento\Framework\View\Layout */ + $layout = $objectManager->create(\Magento\Framework\View\LayoutInterface::class); + /** @var $block \Magento\Sales\Block\Adminhtml\Items\AbstractItems */ + $this->block = $layout->createBlock(Name::class, 'block'); + } + + public function testTruncateString() : void + { + $remainder = ''; + $this->assertEquals( + '12345', + $this->block->truncateString('1234567890', 5, '', $remainder) + ); + } + + public function testGetFormattedOptiong() : void + { + $this->assertEquals( + [ + 'value' => '1234567890123456789012345678901234567890123456789012345', + 'remainder' => '67890', + ], + $this->block->getFormattedOption( + '123456789012345678901234567890123456789012345678901234567890' + ) + ); + } +} diff --git a/lib/internal/Magento/Framework/Filter/Factory.php b/lib/internal/Magento/Framework/Filter/Factory.php index a5e5978a83f06..dbe037ecbbbd1 100644 --- a/lib/internal/Magento/Framework/Filter/Factory.php +++ b/lib/internal/Magento/Framework/Filter/Factory.php @@ -32,6 +32,7 @@ class Factory extends AbstractFactory 'decrypt' => \Magento\Framework\Filter\Decrypt::class, 'translit' => \Magento\Framework\Filter\Translit::class, 'translitUrl' => \Magento\Framework\Filter\TranslitUrl::class, + 'truncateFilter' => \Magento\Framework\Filter\TruncateFilter::class, ]; /** diff --git a/lib/internal/Magento/Framework/Filter/FilterManager.php b/lib/internal/Magento/Framework/Filter/FilterManager.php index 52c9d017ad11c..ca5d998af833f 100644 --- a/lib/internal/Magento/Framework/Filter/FilterManager.php +++ b/lib/internal/Magento/Framework/Filter/FilterManager.php @@ -21,6 +21,7 @@ * @method string removeTags(string $value, $params = array()) * @method string stripTags(string $value, $params = array()) * @method string truncate(string $value, $params = array()) + * @method string truncateFilter(string $value, $params = array()) * @method string encrypt(string $value, $params = array()) * @method string decrypt(string $value, $params = array()) * @method string translit(string $value) diff --git a/lib/internal/Magento/Framework/Filter/TruncateFilter.php b/lib/internal/Magento/Framework/Filter/TruncateFilter.php new file mode 100644 index 0000000000000..38aa20e259cb5 --- /dev/null +++ b/lib/internal/Magento/Framework/Filter/TruncateFilter.php @@ -0,0 +1,103 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Filter; + +use Magento\Framework\Filter\TruncateFilter\Result; +use Magento\Framework\Filter\TruncateFilter\ResultFactory; + +/** + * Truncate filter + * + * Truncate a string to a certain length if necessary, appending the $etc string. + * $remainder will contain the string that has been replaced with $etc. + */ +class TruncateFilter implements \Zend_Filter_Interface +{ + /** + * @var int + */ + private $length; + + /** + * @var string + */ + private $etc; + + /** + * @var bool + */ + private $breakWords; + + /** + * @var \Magento\Framework\Stdlib\StringUtils + */ + private $stringUtils; + + /** + * @var ResultFactory + */ + private $resultFactory; + + /** + * @param \Magento\Framework\Stdlib\StringUtils $stringUtils + * @param ResultFactory $resultFactory + * @param int $length + * @param string $etc + * @param bool $breakWords + */ + public function __construct( + \Magento\Framework\Stdlib\StringUtils $stringUtils, + ResultFactory $resultFactory, + $length = 80, + $etc = '...', + $breakWords = true + ) { + $this->stringUtils = $stringUtils; + $this->resultFactory = $resultFactory; + $this->length = $length; + $this->etc = $etc; + $this->breakWords = $breakWords; + } + + /** + * Filter value + * + * @param string $string + * @return Result + */ + public function filter($string) : Result + { + /** @var Result $result */ + $result = $this->resultFactory->create(['value' => $string, 'remainder' => '']); + $length = $this->length; + if (0 == $length) { + $result->setValue(''); + return $result; + } + + $originalLength = $this->stringUtils->strlen($string); + if ($originalLength > $length) { + $length -= $this->stringUtils->strlen($this->etc); + if ($length <= 0) { + $result->setValue(''); + return $result; + } + $preparedString = $string; + $preparedLength = $length; + if (!$this->breakWords) { + $preparedString = preg_replace('/\s+?(\S+)?$/u', '', $this->stringUtils->substr($string, 0, $length + 1)); + $preparedLength = $this->stringUtils->strlen($preparedString); + } + $result->setRemainder($this->stringUtils->substr($string, $preparedLength, $originalLength)); + $result->setValue($this->stringUtils->substr($preparedString, 0, $length) . $this->etc); + return $result; + } + + return $result; + } +} diff --git a/lib/internal/Magento/Framework/Filter/TruncateFilter/Result.php b/lib/internal/Magento/Framework/Filter/TruncateFilter/Result.php new file mode 100644 index 0000000000000..c1ee6be6dadf5 --- /dev/null +++ b/lib/internal/Magento/Framework/Filter/TruncateFilter/Result.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Filter\TruncateFilter; + +class Result +{ + /** + * @var string + */ + private $value; + + /** + * @var string + */ + private $remainder; + + /** + * Result constructor. + * @param string $value + * @param string $remainder + */ + public function __construct(string $value, string $remainder) + { + $this->value = $value; + $this->remainder = $remainder; + } + + /** + * Set result value + * + * @param string $value + * @return void + */ + public function setValue(string $value) : void + { + $this->value = $value; + } + + /** + * Get value + * + * @return string + */ + public function getValue() : string + { + return $this->value; + } + + /** + * Set remainder + * + * @param string $remainder + * @return void + */ + public function setRemainder(string $remainder) : void + { + $this->remainder = $remainder; + } + + /** + * Get remainder + * + * @return string + */ + public function getRemainder() : string + { + return $this->remainder; + } +} From 54228591705558e33fa57cc77e7137a721885e24 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Mon, 25 Jun 2018 17:20:43 -0500 Subject: [PATCH 0080/1171] MAGETWO-91528: Customizable options truncated when displaying ordered product in admin - fix static test failures --- .../Magento/Sales/Block/Adminhtml/Items/Column/Name.php | 1 + .../Magento/Framework/Filter/TruncateFilterTest.php | 3 ++- lib/internal/Magento/Framework/Filter/TruncateFilter.php | 6 +++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php b/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php index 1b6a3748aa119..87c15e474d11f 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php @@ -29,6 +29,7 @@ class Name extends \Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn * @param string &$remainder * @param bool $breakWords * @return string + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function truncateString($value, $length = 80, $etc = '...', &$remainder = '', $breakWords = true) { diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php index efad23a650415..3736012848379 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php @@ -19,7 +19,8 @@ class TruncateFilterTest extends \PHPUnit\Framework\TestCase * @dataProvider truncateDataProvider */ public function testFilter( - $expectedValue, $expectedRemainder, + $expectedValue, + $expectedRemainder, $string, $length = 5, $etc = '...', diff --git a/lib/internal/Magento/Framework/Filter/TruncateFilter.php b/lib/internal/Magento/Framework/Filter/TruncateFilter.php index 38aa20e259cb5..a41469bb6f2a9 100644 --- a/lib/internal/Magento/Framework/Filter/TruncateFilter.php +++ b/lib/internal/Magento/Framework/Filter/TruncateFilter.php @@ -90,7 +90,11 @@ public function filter($string) : Result $preparedString = $string; $preparedLength = $length; if (!$this->breakWords) { - $preparedString = preg_replace('/\s+?(\S+)?$/u', '', $this->stringUtils->substr($string, 0, $length + 1)); + $preparedString = preg_replace( + '/\s+?(\S+)?$/u', + '', + $this->stringUtils->substr($string, 0, $length + 1) + ); $preparedLength = $this->stringUtils->strlen($preparedString); } $result->setRemainder($this->stringUtils->substr($string, $preparedLength, $originalLength)); From 206ea482ef96115a6350c7dad8459499cbc4e40c Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Tue, 26 Jun 2018 09:58:29 -0500 Subject: [PATCH 0081/1171] MAGETWO-91433: ProductListing: Grid view is getting changed to List view when user adding product from wishlist section. --- .../Model/Product/AttributeValueProvider.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Wishlist/Model/Product/AttributeValueProvider.php b/app/code/Magento/Wishlist/Model/Product/AttributeValueProvider.php index 03f5815e894c4..8d3cd6e00b02d 100644 --- a/app/code/Magento/Wishlist/Model/Product/AttributeValueProvider.php +++ b/app/code/Magento/Wishlist/Model/Product/AttributeValueProvider.php @@ -8,7 +8,6 @@ namespace Magento\Wishlist\Model\Product; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; -use Magento\Framework\Exception\NoSuchEntityException; /** * Provides existing attribute value for a product entity. @@ -36,7 +35,6 @@ public function __construct( * @param string $attributeCode * @param int|null $storeId * @return null|string - * @throws NoSuchEntityException */ public function getRawAttributeValue(int $productId, string $attributeCode, int $storeId = null):? string { @@ -46,12 +44,13 @@ public function getRawAttributeValue(int $productId, string $attributeCode, int ->addStoreFilter($storeId) ->addAttributeToSelect($attributeCode); - $data = $collection->getConnection()->fetchRow($collection->getSelect()); - - if (!array_key_exists($attributeCode, $data)) { - throw new NoSuchEntityException(__('An attribute value of "%1" does not exist.', $attributeCode)); + if ($collection->isEnabledFlat()) { + $data = $collection->getConnection()->fetchRow($collection->getSelect()); + $attributeValue = $data[$attributeCode] ?? null; + } else { + $attributeValue = $collection->getFirstItem()->getData($attributeCode); } - return $data[$attributeCode]; + return $attributeValue; } } From 94dcc4ea2df2b59adc90c1deb4d78863bd83ba2c Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Tue, 26 Jun 2018 18:12:28 -0300 Subject: [PATCH 0082/1171] Changes: - Replacing the method proccessAdditionalValidation by processAdditionalValidation. It's was mistype error. --- app/code/Magento/Dhl/Model/Carrier.php | 12 ++++++++++++ .../Shipping/Model/Carrier/AbstractCarrier.php | 15 ++++++++++++++- .../Model/Carrier/AbstractCarrierInterface.php | 2 +- .../Model/Carrier/AbstractCarrierOnline.php | 14 ++++++++++++++ app/code/Magento/Shipping/Model/Shipping.php | 2 +- .../Model/Carrier/AbstractCarrierOnlineTest.php | 2 +- 6 files changed, 43 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Dhl/Model/Carrier.php b/app/code/Magento/Dhl/Model/Carrier.php index 258dcbe9595d4..6262d71c4edcc 100644 --- a/app/code/Magento/Dhl/Model/Carrier.php +++ b/app/code/Magento/Dhl/Model/Carrier.php @@ -1263,8 +1263,20 @@ protected function _doShipmentRequest(\Magento\Framework\DataObject $request) * * @param \Magento\Framework\DataObject $request * @return $this|\Magento\Framework\DataObject|boolean + * @deprecated */ public function proccessAdditionalValidation(\Magento\Framework\DataObject $request) + { + return $this->processAdditionalValidation($request); + } + + /** + * Processing additional validation to check is carrier applicable. + * + * @param \Magento\Framework\DataObject $request + * @return $this|\Magento\Framework\DataObject|boolean + */ + public function processAdditionalValidation(\Magento\Framework\DataObject $request) { //Skip by item validation if there is no items in request if (!count($this->getAllItems($request))) { diff --git a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php index 5575792c346d3..6763046373ce7 100644 --- a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php +++ b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php @@ -329,11 +329,24 @@ public function checkAvailableShipCountries(\Magento\Framework\DataObject $reque * @return $this|bool|\Magento\Framework\DataObject * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function proccessAdditionalValidation(\Magento\Framework\DataObject $request) + public function processAdditionalValidation(\Magento\Framework\DataObject $request) { return $this; } + /** + * Processing additional validation to check is carrier applicable. + * + * @param \Magento\Framework\DataObject $request + * @return $this|bool|\Magento\Framework\DataObject + * @deprecated + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function proccessAdditionalValidation(\Magento\Framework\DataObject $request) + { + return $this->processAdditionalValidation($request); + } + /** * Determine whether current carrier enabled for activity * diff --git a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierInterface.php b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierInterface.php index ee59fae657ea5..adf7ad1ed2ec8 100644 --- a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierInterface.php +++ b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierInterface.php @@ -90,7 +90,7 @@ public function checkAvailableShipCountries(\Magento\Framework\DataObject $reque * @return $this|\Magento\Framework\DataObject|boolean * @api */ - public function proccessAdditionalValidation(\Magento\Framework\DataObject $request); + public function processAdditionalValidation(\Magento\Framework\DataObject $request); /** * Determine whether current carrier enabled for activity diff --git a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php index 8244fcc4bad9d..be2588dc48711 100644 --- a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php +++ b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php @@ -302,10 +302,24 @@ public function getAllItems(RateRequest $request) * * @param \Magento\Framework\DataObject $request * @return $this|bool|\Magento\Framework\DataObject + * @deprecated * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function proccessAdditionalValidation(\Magento\Framework\DataObject $request) + { + return $this->processAdditionalValidation($request); + } + + /** + * Processing additional validation to check if carrier applicable. + * + * @param \Magento\Framework\DataObject $request + * @return $this|bool|\Magento\Framework\DataObject + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function processAdditionalValidation(\Magento\Framework\DataObject $request) { //Skip by item validation if there is no items in request if (!count($this->getAllItems($request))) { diff --git a/app/code/Magento/Shipping/Model/Shipping.php b/app/code/Magento/Shipping/Model/Shipping.php index 2223cb8ae3bf2..57e055e83a58a 100644 --- a/app/code/Magento/Shipping/Model/Shipping.php +++ b/app/code/Magento/Shipping/Model/Shipping.php @@ -259,7 +259,7 @@ public function collectCarrierRates($carrierCode, $request) $carrier->setActiveFlag($this->_availabilityConfigField); $result = $carrier->checkAvailableShipCountries($request); if (false !== $result && !$result instanceof \Magento\Quote\Model\Quote\Address\RateResult\Error) { - $result = $carrier->proccessAdditionalValidation($request); + $result = $carrier->processAdditionalValidation($request); } /* * Result will be false if the admin set not to show the shipping module diff --git a/app/code/Magento/Shipping/Test/Unit/Model/Carrier/AbstractCarrierOnlineTest.php b/app/code/Magento/Shipping/Test/Unit/Model/Carrier/AbstractCarrierOnlineTest.php index 6f87eb171a398..b40f5b26b89f1 100644 --- a/app/code/Magento/Shipping/Test/Unit/Model/Carrier/AbstractCarrierOnlineTest.php +++ b/app/code/Magento/Shipping/Test/Unit/Model/Carrier/AbstractCarrierOnlineTest.php @@ -100,7 +100,7 @@ public function testComposePackages() $this->stockItemData->expects($this->atLeastOnce())->method('getIsDecimalDivided') ->will($this->returnValue(true)); - $this->carrier->proccessAdditionalValidation($request); + $this->carrier->processAdditionalValidation($request); } public function testParseXml() From e99c99edb075e32d90a98fba9057c8e5affaebe8 Mon Sep 17 00:00:00 2001 From: Tadhg Bowe <tadhg.bowe@screenpages.com> Date: Thu, 28 Jun 2018 08:48:54 +0100 Subject: [PATCH 0083/1171] import-export-improvements #82 : configurable variations - not a super attribute error message improvements --- .../Import/Product/Type/Configurable.php | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php index 151bf5aa9263e..939c122b0d9d7 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php @@ -25,6 +25,12 @@ class Configurable extends \Magento\CatalogImportExport\Model\Import\Product\Typ /** * Error codes. */ + const ERROR_ATTRIBUTE_CODE_DOES_NOT_EXIST = 'attrCodeDoesNotExist'; + + const ERROR_ATTRIBUTE_CODE_NOT_GLOBAL_SCOPE = 'attrCodeNotGlobalScope'; + + const ERROR_ATTRIBUTE_CODE_NOT_TYPE_SELECT = 'attrCodeNotTypeSelect'; + const ERROR_ATTRIBUTE_CODE_IS_NOT_SUPER = 'attrCodeIsNotSuper'; const ERROR_INVALID_OPTION_VALUE = 'invalidOptionValue'; @@ -41,8 +47,11 @@ class Configurable extends \Magento\CatalogImportExport\Model\Import\Product\Typ * @var array */ protected $_messageTemplates = [ - self::ERROR_ATTRIBUTE_CODE_IS_NOT_SUPER => 'Attribute with code "%s" is not super', - self::ERROR_INVALID_OPTION_VALUE => 'Invalid option value for attribute "%s"', + self::ERROR_ATTRIBUTE_CODE_DOES_NOT_EXIST => 'Column configurable_variations: Attribute with code "%s" does not exist or is missing from product attribute set', + self::ERROR_ATTRIBUTE_CODE_NOT_GLOBAL_SCOPE => 'Column configurable_variations: Attribute with code "%s" is not super - it needs to have Global Scope', + self::ERROR_ATTRIBUTE_CODE_NOT_TYPE_SELECT => 'Column configurable_variations: Attribute with code "%s" is not super - it needs to be Input Type of Dropdown, Visual Swatch or Text Swatch', + self::ERROR_ATTRIBUTE_CODE_IS_NOT_SUPER => 'Column configurable_variations: Attribute with code "%s" is not super', + self::ERROR_INVALID_OPTION_VALUE => 'Column configurable_variations: Invalid option value for attribute "%s"', self::ERROR_INVALID_WEBSITE => 'Invalid website code for super attribute', self::ERROR_DUPLICATED_VARIATIONS => 'SKU %s contains duplicated variations', self::ERROR_UNIDENTIFIABLE_VARIATION => 'Configurable variation "%s" is unidentifiable', @@ -291,9 +300,50 @@ protected function _isParticularAttributesValid(array $rowData, $rowNum) $superAttrCode = $rowData['_super_attribute_code']; if (!$this->_isAttributeSuper($superAttrCode)) { - // check attribute superity + // This attribute code is not a super attribute. Need to give a clearer message why? + $codeExists = false; + $codeNotGlobal = false; + $codeNotTypeSelect = false; + // Does this attribute code exist? Does is have the correct settings? + $commonAttributes = self::$commonAttributesCache; + foreach ($commonAttributes as $attributeRow) { + + if ($attributeRow['code'] == $superAttrCode) + { + $codeExists = true; + + if ($attributeRow['is_global'] !== '1') + { + $codeNotGlobal = true; + } + elseif ($attributeRow['type'] !== 'select') + { + $codeNotTypeSelect = true; + } + + break; + } + } + + if ($codeExists == false) + { + $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_DOES_NOT_EXIST, $rowNum, $superAttrCode); + return false; + } + elseif ($codeNotGlobal == true) + { + $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_GLOBAL_SCOPE, $rowNum, $superAttrCode); + return false; + } + elseif ($codeNotTypeSelect == true) + { + $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_TYPE_SELECT, $rowNum, $superAttrCode); + return false; + } + $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_IS_NOT_SUPER, $rowNum, $superAttrCode); return false; + } elseif (isset($rowData['_super_attribute_option']) && strlen($rowData['_super_attribute_option'])) { $optionKey = strtolower($rowData['_super_attribute_option']); if (!isset($this->_superAttributes[$superAttrCode]['options'][$optionKey])) { From 2fb5b745963aa886874e513b7af9046cf303bc29 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Thu, 28 Jun 2018 09:52:15 -0500 Subject: [PATCH 0084/1171] MAGETWO-91433: ProductListing: Grid view is getting changed to List view when user adding product from wishlist section. --- .../Magento/Wishlist/Model/Product/AttributeValueProvider.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Model/Product/AttributeValueProvider.php b/app/code/Magento/Wishlist/Model/Product/AttributeValueProvider.php index 8d3cd6e00b02d..e17cd30184504 100644 --- a/app/code/Magento/Wishlist/Model/Product/AttributeValueProvider.php +++ b/app/code/Magento/Wishlist/Model/Product/AttributeValueProvider.php @@ -38,7 +38,6 @@ public function __construct( */ public function getRawAttributeValue(int $productId, string $attributeCode, int $storeId = null):? string { - $collection = $this->productCollectionFactory->create(); $collection->addIdFilter($productId) ->addStoreFilter($storeId) From 65bce0aa9e678382bf2f88b3cae2e23d14702391 Mon Sep 17 00:00:00 2001 From: Tadhg Bowe <tadhg.bowe@screenpages.com> Date: Thu, 28 Jun 2018 16:07:37 +0100 Subject: [PATCH 0085/1171] import-export-improvements #82 : configurable variations - not a super attribute error message improvements - travis ci build code fix --- .../Import/Product/Type/Configurable.php | 100 ++++++++++-------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php index 939c122b0d9d7..a3f550d03e8fe 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php @@ -298,52 +298,13 @@ protected function _isParticularAttributesValid(array $rowData, $rowNum) { if (!empty($rowData['_super_attribute_code'])) { $superAttrCode = $rowData['_super_attribute_code']; - if (!$this->_isAttributeSuper($superAttrCode)) { - // This attribute code is not a super attribute. Need to give a clearer message why? - $codeExists = false; - $codeNotGlobal = false; - $codeNotTypeSelect = false; - // Does this attribute code exist? Does is have the correct settings? - $commonAttributes = self::$commonAttributesCache; - foreach ($commonAttributes as $attributeRow) { - - if ($attributeRow['code'] == $superAttrCode) - { - $codeExists = true; - - if ($attributeRow['is_global'] !== '1') - { - $codeNotGlobal = true; - } - elseif ($attributeRow['type'] !== 'select') - { - $codeNotTypeSelect = true; - } - - break; - } - } - - if ($codeExists == false) - { - $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_DOES_NOT_EXIST, $rowNum, $superAttrCode); - return false; - } - elseif ($codeNotGlobal == true) + // Identify reason why attribute is not super: + if (!$this->_identifySuperAttributeError($superAttrCode, $rowNum)) { - $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_GLOBAL_SCOPE, $rowNum, $superAttrCode); - return false; + $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_IS_NOT_SUPER, $rowNum, $superAttrCode); } - elseif ($codeNotTypeSelect == true) - { - $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_TYPE_SELECT, $rowNum, $superAttrCode); - return false; - } - - $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_IS_NOT_SUPER, $rowNum, $superAttrCode); return false; - } elseif (isset($rowData['_super_attribute_option']) && strlen($rowData['_super_attribute_option'])) { $optionKey = strtolower($rowData['_super_attribute_option']); if (!isset($this->_superAttributes[$superAttrCode]['options'][$optionKey])) { @@ -355,6 +316,61 @@ protected function _isParticularAttributesValid(array $rowData, $rowNum) return true; } + /** + * Identify exactly why a super attribute code is not super. + * + * @param string $superAttrCode + * @param int $rowNum + * @return bool + */ + protected function _identifySuperAttributeError($superAttrCode, $rowNum) + { + // This attribute code is not a super attribute. Need to give a clearer message why? + $reasonFound = false; + + $codeExists = false; + $codeNotGlobal = false; + $codeNotTypeSelect = false; + // Does this attribute code exist? Does is have the correct settings? + $commonAttributes = self::$commonAttributesCache; + foreach ($commonAttributes as $attributeRow) { + + if ($attributeRow['code'] == $superAttrCode) + { + $codeExists = true; + + if ($attributeRow['is_global'] !== '1') + { + $codeNotGlobal = true; + } + elseif ($attributeRow['type'] !== 'select') + { + $codeNotTypeSelect = true; + } + + break; + } + } + + if ($codeExists == false) + { + $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_DOES_NOT_EXIST, $rowNum, $superAttrCode); + $reasonFound = true; + } + elseif ($codeNotGlobal == true) + { + $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_GLOBAL_SCOPE, $rowNum, $superAttrCode); + $reasonFound = true; + } + elseif ($codeNotTypeSelect == true) + { + $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_TYPE_SELECT, $rowNum, $superAttrCode); + $reasonFound = true; + } + + return $reasonFound; + } + /** * Array of SKU to array of super attribute values for all products. * From 7f9f6db9510508aae12260f517eba7e5ba198742 Mon Sep 17 00:00:00 2001 From: Tadhg Bowe <tadhg.bowe@screenpages.com> Date: Fri, 29 Jun 2018 14:38:05 +0100 Subject: [PATCH 0086/1171] import-export-improvements #82 : configurable variations - not a super attribute error message improvements - travis ci build code style fix --- .../Import/Product/Type/Configurable.php | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php index a3f550d03e8fe..07468c429a234 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php @@ -41,10 +41,13 @@ class Configurable extends \Magento\CatalogImportExport\Model\Import\Product\Typ const ERROR_UNIDENTIFIABLE_VARIATION = 'unidentifiableVariation'; + // @codingStandardsIgnoreStart /** * Validation failure message template definitions * * @var array + * + * Note: Many of these messages exceed maximum limit of 120 characters. Ignore from coding standards. */ protected $_messageTemplates = [ self::ERROR_ATTRIBUTE_CODE_DOES_NOT_EXIST => 'Column configurable_variations: Attribute with code "%s" does not exist or is missing from product attribute set', @@ -56,6 +59,7 @@ class Configurable extends \Magento\CatalogImportExport\Model\Import\Product\Typ self::ERROR_DUPLICATED_VARIATIONS => 'SKU %s contains duplicated variations', self::ERROR_UNIDENTIFIABLE_VARIATION => 'Configurable variation "%s" is unidentifiable', ]; + // @codingStandardsIgnoreEnd /** * Column names that holds values with particular meaning. @@ -300,8 +304,7 @@ protected function _isParticularAttributesValid(array $rowData, $rowNum) $superAttrCode = $rowData['_super_attribute_code']; if (!$this->_isAttributeSuper($superAttrCode)) { // Identify reason why attribute is not super: - if (!$this->_identifySuperAttributeError($superAttrCode, $rowNum)) - { + if (!$this->_identifySuperAttributeError($superAttrCode, $rowNum)) { $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_IS_NOT_SUPER, $rowNum, $superAttrCode); } return false; @@ -334,17 +337,13 @@ protected function _identifySuperAttributeError($superAttrCode, $rowNum) // Does this attribute code exist? Does is have the correct settings? $commonAttributes = self::$commonAttributesCache; foreach ($commonAttributes as $attributeRow) { - - if ($attributeRow['code'] == $superAttrCode) - { + if ($attributeRow['code'] == $superAttrCode) { $codeExists = true; - if ($attributeRow['is_global'] !== '1') - { + if ($attributeRow['is_global'] !== '1') { $codeNotGlobal = true; } - elseif ($attributeRow['type'] !== 'select') - { + elseif ($attributeRow['type'] !== 'select') { $codeNotTypeSelect = true; } @@ -352,18 +351,15 @@ protected function _identifySuperAttributeError($superAttrCode, $rowNum) } } - if ($codeExists == false) - { + if ($codeExists == false) { $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_DOES_NOT_EXIST, $rowNum, $superAttrCode); $reasonFound = true; } - elseif ($codeNotGlobal == true) - { + elseif ($codeNotGlobal == true) { $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_GLOBAL_SCOPE, $rowNum, $superAttrCode); $reasonFound = true; } - elseif ($codeNotTypeSelect == true) - { + elseif ($codeNotTypeSelect == true) { $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_TYPE_SELECT, $rowNum, $superAttrCode); $reasonFound = true; } From e087e45ac07b3c0ba64ec8e1eee0983ae7b43f7c Mon Sep 17 00:00:00 2001 From: Alex Ghiban <drew7721@gmail.com> Date: Fri, 29 Jun 2018 11:24:44 -0400 Subject: [PATCH 0087/1171] Wrap block title in global translation method. --- .../Theme/view/frontend/templates/html/collapsible.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Theme/view/frontend/templates/html/collapsible.phtml b/app/code/Magento/Theme/view/frontend/templates/html/collapsible.phtml index 318bcc8f2cc7c..c3ad4a89399a0 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/collapsible.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/collapsible.phtml @@ -10,7 +10,7 @@ <div class="block <?= /* @escapeNotVerified */ $block->getBlockCss() ?>"> <div class="title <?= /* @escapeNotVerified */ $block->getBlockCss() ?>-title" data-mage-init='{"toggleAdvanced": {"toggleContainers": "#<?= /* @escapeNotVerified */ $block->getBlockCss() ?>", "selectorsToggleClass": "active"}}'> - <strong><?= /* @escapeNotVerified */ $block->getBlockTitle() ?></strong> + <strong><?= /* @escapeNotVerified */ __($block->getBlockTitle()) ?></strong> </div> <div class="content <?= /* @escapeNotVerified */ $block->getBlockCss() ?>-content" id="<?= /* @escapeNotVerified */ $block->getBlockCss() ?>"> <?= $block->getChildHtml() ?> From 2883d5b9c01d505548fff130c3c9bce383b61fa3 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 29 Jun 2018 11:34:07 -0500 Subject: [PATCH 0088/1171] MAGETWO-91528: Customizable options truncated when displaying ordered product in admin - deprecate old truncate --- lib/internal/Magento/Framework/Filter/Truncate.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/internal/Magento/Framework/Filter/Truncate.php b/lib/internal/Magento/Framework/Filter/Truncate.php index fd4fbe9910427..a4dd35b302705 100644 --- a/lib/internal/Magento/Framework/Filter/Truncate.php +++ b/lib/internal/Magento/Framework/Filter/Truncate.php @@ -10,6 +10,9 @@ * * Truncate a string to a certain length if necessary, appending the $etc string. * $remainder will contain the string that has been replaced with $etc. + * + * @deprecated + * @see \Magento\Framework\Filter\TruncateFilter */ class Truncate implements \Zend_Filter_Interface { From a76e3a3e8cd545b16fda1bd56c5db5d6cf687d56 Mon Sep 17 00:00:00 2001 From: Tadhg Bowe <tadhg.bowe@screenpages.com> Date: Fri, 29 Jun 2018 20:28:07 +0100 Subject: [PATCH 0089/1171] import-export-improvements #82 : configurable variations - not a super attribute error message improvements - travis ci build code style fixes --- .../Model/Import/Product/Type/Configurable.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php index 07468c429a234..4f3308ff3d6e1 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php @@ -342,8 +342,7 @@ protected function _identifySuperAttributeError($superAttrCode, $rowNum) if ($attributeRow['is_global'] !== '1') { $codeNotGlobal = true; - } - elseif ($attributeRow['type'] !== 'select') { + } elseif ($attributeRow['type'] !== 'select') { $codeNotTypeSelect = true; } @@ -354,12 +353,10 @@ protected function _identifySuperAttributeError($superAttrCode, $rowNum) if ($codeExists == false) { $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_DOES_NOT_EXIST, $rowNum, $superAttrCode); $reasonFound = true; - } - elseif ($codeNotGlobal == true) { + } elseif ($codeNotGlobal == true) { $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_GLOBAL_SCOPE, $rowNum, $superAttrCode); $reasonFound = true; - } - elseif ($codeNotTypeSelect == true) { + } elseif ($codeNotTypeSelect == true) { $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_TYPE_SELECT, $rowNum, $superAttrCode); $reasonFound = true; } From e28383075ca4daf1e61ea219bf8c9e3e37b83d33 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Fri, 29 Jun 2018 15:11:25 -0500 Subject: [PATCH 0090/1171] MAGETWO-77744: [Magento Cloud] - Error message when uploading unsupported file format --- .../frontend/web/js/catalog-add-to-cart.js | 29 +++++++++---------- .../Model/Product/Type/Configurable.php | 2 ++ .../view/type/options/configurable.phtml | 4 +++ .../frontend/web/js/catalog-add-to-cart.js | 17 +++++++++++ .../templates/product/view/renderer.phtml | 3 ++ 5 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js diff --git a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js index 8fcac2f9f1d65..9fffb0478059e 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js @@ -62,34 +62,28 @@ define([ * @param {Object} form */ submitForm: function (form) { - var addToCartButton, self = this; - - if (form.has('input[type="file"]').length && form.find('input[type="file"]').val() !== '') { - self.element.off('submit'); - // disable 'Add to Cart' button - addToCartButton = $(form).find(this.options.addToCartButtonSelector); - addToCartButton.prop('disabled', true); - addToCartButton.addClass(this.options.addToCartButtonDisabledClass); - form.submit(); - } else { - self.ajaxSubmit(form); - } + this.ajaxSubmit(form); }, /** * @param {String} form */ ajaxSubmit: function (form) { - var self = this; + var self = this, + formData; $(self.options.minicartSelector).trigger('contentLoading'); self.disableAddToCartButton(form); + formData = new FormData(form[0]); $.ajax({ url: form.attr('action'), - data: form.serialize(), + data: formData, type: 'post', dataType: 'json', + cache: false, + contentType: false, + processData: false, /** @inheritdoc */ beforeSend: function () { @@ -125,7 +119,12 @@ define([ parameters.push(eventData.redirectParameters.join('&')); res.backUrl = parameters.join('#'); } - window.location = res.backUrl; + window.location.href = res.backUrl; + + // page does not reload when anchor (#) is added + if (res.backUrl.indexOf('#') !== -1) { + window.location.reload(); + } return; } diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index 29583231a764a..19de63b7a976c 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -926,6 +926,8 @@ protected function _prepareProduct(\Magento\Framework\DataObject $buyRequest, $p return $result; } } + } elseif (is_string($result)) { + return __($result)->render(); } } diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml b/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml index 9b8e2c0c8c0bd..fb8541da00a94 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml +++ b/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml @@ -22,6 +22,7 @@ $_attributes = $block->decorateArray($block->getAllowAttributes()); <div class="control"> <select name="super_attribute[<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>]" data-selector="super_attribute[<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>]" + data-attribute-code="<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>" data-validate="{required:true}" id="attribute<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>" class="super-attribute-select"> @@ -38,6 +39,9 @@ $_attributes = $block->decorateArray($block->getAllowAttributes()); "gallerySwitchStrategy": "<?php /* @escapeNotVerified */ echo $block->getVar('gallery_switch_strategy', 'Magento_ConfigurableProduct') ?: 'replace'; ?>" } + }, + "*" : { + "Magento_ConfigurableProduct/js/catalog-add-to-cart": {} } } </script> diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js new file mode 100644 index 0000000000000..f3b8ed0e84d63 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js @@ -0,0 +1,17 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +require([ + 'jquery' +], function ($) { + 'use strict'; + + $('body').on('catalogCategoryAddToCartRedirect', function (event, data) { + $(data.form).find('select[name*="super"]').each(function (index, item) { + var $item = $(item); + + data.redirectParameters.push($item.attr('data-attribute-code') + '=' + $item.val()); + }); + }); +}); diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml index fc1de530a66bd..b044e692313dc 100644 --- a/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml +++ b/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml @@ -20,6 +20,9 @@ "gallerySwitchStrategy": "<?php /* @escapeNotVerified */ echo $block->getVar('gallery_switch_strategy', 'Magento_ConfigurableProduct') ?: 'replace'; ?>" } + }, + "*" : { + "Magento_Swatches/js/catalog-add-to-cart": {} } } </script> From e79f381bf75abb7b908f40df188e133ccbbf277b Mon Sep 17 00:00:00 2001 From: Roman Glushko <r.glushko@atwix.com> Date: Sat, 30 Jun 2018 12:20:24 +0300 Subject: [PATCH 0091/1171] #96 Made category api compatible with Content Staging Commerce feature --- .../Products/DataProvider/CategoryTree.php | 2 +- .../Magento/GraphQl/Catalog/CategoryTest.php | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php index 3c01579410638..c1560d666a16e 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php @@ -100,7 +100,7 @@ public function getTree(ResolveInfo $resolveInfo, int $rootCategoryId) : array $collection->addFieldToFilter('level', ['lteq' => $level + $depth - self::DEPTH_OFFSET]); $collection->setOrder('level'); $collection->getSelect()->orWhere( - $this->metadata->getMetadata(CategoryInterface::class)->getLinkField() . ' = ?', + $this->metadata->getMetadata(CategoryInterface::class)->getIdentifierField() . ' = ?', $rootCategoryId ); return $this->processTree($collection->getIterator()); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index 0133b87e757bd..ef59cdeeb9369 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -8,6 +8,7 @@ namespace Magento\GraphQl\Catalog; use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; use Magento\Framework\DataObject; use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Catalog\Api\Data\ProductInterface; @@ -286,16 +287,13 @@ public function testCategoryProducts() */ public function testAnchorCategory() { - /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $categoryCollection */ - $categoryCollection = $this->objectManager->create( - \Magento\Catalog\Model\ResourceModel\Category\Collection::class - ); + /** @var CategoryCollection $categoryCollection */ + $categoryCollection = $this->objectManager->create(CategoryCollection::class); $categoryCollection->addFieldToFilter('name', 'Category 1'); + /** @var CategoryInterface $category */ $category = $categoryCollection->getFirstItem(); - /** @var \Magento\Framework\EntityManager\MetadataPool $entityManagerMetadataPool */ - $entityManagerMetadataPool = $this->objectManager->create(\Magento\Framework\EntityManager\MetadataPool::class); - $categoryLinkField = $entityManagerMetadataPool->getMetadata(CategoryInterface::class)->getLinkField(); - $categoryId = $category->getData($categoryLinkField); + $categoryId = $category->getId(); + $this->assertNotEmpty($categoryId, "Preconditions failed: category is not available."); $query = <<<QUERY From 284f34504562ece728bbab6dc7ae89906bc0fca4 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Sat, 30 Jun 2018 13:39:16 +0300 Subject: [PATCH 0092/1171] Additional checks for fragments added in category tree --- app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php | 4 ++++ .../Magento/CatalogGraphQl/Model/Category/DepthCalculator.php | 4 ++++ .../Model/Resolver/Products/DataProvider/CategoryTree.php | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php index a1f581743a645..ebd8671de02fb 100644 --- a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php +++ b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php @@ -28,6 +28,10 @@ public function join(FieldNode $fieldNode, AbstractCollection $collection) : voi /** @var FieldNode $field */ foreach ($query as $field) { + if ($field->kind === 'InlineFragment') { + continue; + } + if (!$collection->isAttributeAdded($field->name->value)) { $collection->addAttributeToSelect($field->name->value); } diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php b/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php index baa456c7821ed..dbe58a9c77cd0 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php @@ -26,6 +26,10 @@ public function calculate(FieldNode $fieldNode) : int $depth = count($selections) ? 1 : 0; $childrenDepth = [0]; foreach ($selections as $node) { + if ($node->kind === 'InlineFragment') { + continue; + } + $childrenDepth[] = $this->calculate($node); } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php index 3c01579410638..3234c8ab78580 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php @@ -143,6 +143,10 @@ private function joinAttributesRecursively(Collection $collection, FieldNode $fi /** @var FieldNode $node */ foreach ($subSelection as $node) { + if ($node->kind === 'InlineFragment') { + continue; + } + $this->joinAttributesRecursively($collection, $node); } } From 9116d8259244ac3b03dec76f39aa56e2d478998f Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sat, 30 Jun 2018 13:57:01 +0300 Subject: [PATCH 0093/1171] Fix the category tree depth calculation issue --- .../Model/Resolver/Products/DataProvider/CategoryTree.php | 4 ++-- .../testsuite/Magento/GraphQl/Catalog/CategoryTest.php | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php index 3c01579410638..80cd427ea9846 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php @@ -24,9 +24,9 @@ class CategoryTree { /** - * In depth we need to calculate only children nodes, so 2 first wrapped nodes should be ignored + * In depth we need to calculate only children nodes, so the first wrapped node should be ignored */ - const DEPTH_OFFSET = 2; + const DEPTH_OFFSET = 1; /** * @var CollectionFactory diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index 0133b87e757bd..4f68090657a10 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -63,9 +63,6 @@ public function testCategoriesTree() children { level id - children { - id - } } } } From 7f9d1bc570962fd5ff683f1b7a7f2f382383adf9 Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <v.vygovskyi@atwix.com> Date: Sat, 30 Jun 2018 11:32:32 +0000 Subject: [PATCH 0094/1171] hidden product attributes not intended for storefront --- .../Resolver/Products/Attributes/Collection.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Attributes/Collection.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Attributes/Collection.php index d0413813aab37..cc7f658376814 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Attributes/Collection.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Attributes/Collection.php @@ -46,6 +46,23 @@ public function getAttributes() : AttributeCollection $this->collection = $this->collectionFactory->create(); $this->collection->addFieldToFilter('is_user_defined', '1'); $this->collection->addFieldToFilter('attribute_code', ['neq' => 'cost']); + $this->collection->addFieldToFilter( + [ + 'is_comparable', + 'is_filterable', + 'is_filterable_in_search', + 'is_visible_on_front', + 'used_in_product_listing', + 'used_for_sort_by' + ], + [ + ['eq' => '1'], + ['eq' => '1'], + ['eq' => '1'], + ['eq' => '1'], + ['eq' => '1'], + ['eq' => '1'] + ]); } return $this->collection->load(); From 7674eb1713c7f348cd7fc8b8bc3c7643b49a3638 Mon Sep 17 00:00:00 2001 From: Artem Klimov <art.klimoff@gmail.com> Date: Sat, 30 Jun 2018 17:46:48 +0300 Subject: [PATCH 0095/1171] 87 Fetch attribute values and labels for customAttributeMetadata --- .../Model/Resolver/AttributeOptions.php | 88 +++++++++++++++++++ .../Magento/EavGraphQl/etc/schema.graphqls | 6 ++ 2 files changed, 94 insertions(+) create mode 100644 app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php diff --git a/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php b/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php new file mode 100644 index 0000000000000..1c341012083b9 --- /dev/null +++ b/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\EavGraphQl\Model\Resolver; + +use Magento\Eav\Api\AttributeOptionManagementInterface; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; + +/** + * Resolve attribute options data for custom attribute. + */ +class AttributeOptions implements ResolverInterface +{ + /** + * @var AttributeOptionManagementInterface + */ + protected $optionManager; + + /** + * @var ValueFactory + */ + protected $valueFactory; + + /** + * AttributeOptions constructor. + * + * @param AttributeOptionManagementInterface $optionManager + * @param ValueFactory $valueFactory + */ + public function __construct( + AttributeOptionManagementInterface $optionManager, + ValueFactory $valueFactory + ) { + $this->optionManager = $optionManager; + $this->valueFactory = $valueFactory; + } + + /** + * {@inheritDoc} + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) : Value { + $options = []; + + $entityType = !empty($value['entity_type']) ? $value['entity_type'] : ''; + $attributeCode = !empty($value['attribute_code']) ? $value['attribute_code'] : ''; + + try { + /** @var \Magento\Eav\Api\Data\AttributeOptionInterface[] $attributeOptions */ + $attributeOptions = $this->optionManager->getItems($entityType, $attributeCode); + } catch (\Exception $e) { + $attributeOptions = []; + } + + if (is_array($attributeOptions)) { + /** @var \Magento\Eav\Api\Data\AttributeOptionInterface $option */ + foreach ($attributeOptions as $option) { + if (!$option->getValue()) { + continue; + } + + $options[] = [ + 'label' => $option->getLabel(), + 'value' => $option->getValue() + ]; + } + } + + $result = function () use ($options) { + return $options; + }; + + return $this->valueFactory->create($result); + } +} diff --git a/app/code/Magento/EavGraphQl/etc/schema.graphqls b/app/code/Magento/EavGraphQl/etc/schema.graphqls index 7799498c40409..adada3030f501 100644 --- a/app/code/Magento/EavGraphQl/etc/schema.graphqls +++ b/app/code/Magento/EavGraphQl/etc/schema.graphqls @@ -13,6 +13,12 @@ type Attribute @doc(description: "Attribute contains the attribute_type of the s attribute_code: String @doc(description: "The unique identifier for an attribute code. This value should be in lowercase letters without spaces.") entity_type: String @doc(description: "The type of entity that defines the attribute") attribute_type: String @doc(description: "The data type of the attribute") + attribute_options: [AttributeOption] @resolver(class: "Magento\\EavGraphQl\\Model\\Resolver\\AttributeOptions") @doc(description: "Attribute options list.") +} + +type AttributeOption @doc(description: "Attribute option.") { + label: String @doc(description: "Attribute option label.") + value: String @doc(description: "Attribute option value.") } input AttributeInput @doc(description: "AttributeInput specifies the attribute_code and entity_type to search") { From a8ee55551ecf13eaeefc665de14145f94bc757aa Mon Sep 17 00:00:00 2001 From: carstenpfeifer <c.pfeifer@gruenspar.de> Date: Sun, 17 Jun 2018 13:08:10 +0200 Subject: [PATCH 0096/1171] magento-engcom/import-export-improvements#30: Do required attribute check before attribute validation. Add check if the required attribute is not empty after trimming. --- .../CustomerImportExport/Model/Import/Customer.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php index e5cc543db6aac..3d4c40fbbed8d 100644 --- a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php +++ b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php @@ -593,6 +593,12 @@ protected function _validateRowForUpdate(array $rowData, $rowNumber) if (in_array($attributeCode, $this->_ignoredAttributes)) { continue; } + if ($attributeParams['is_required'] + && ((!isset($rowData[$attributeCode]) && !$this->_getCustomerId($email, $website)) + || (isset($rowData[$attributeCode]) && '' === trim($rowData[$attributeCode])))) { + $this->addRowError(self::ERROR_VALUE_IS_REQUIRED, $rowNumber, $attributeCode); + continue; + } if (isset($rowData[$attributeCode]) && strlen($rowData[$attributeCode])) { $this->isAttributeValid( $attributeCode, @@ -603,8 +609,6 @@ protected function _validateRowForUpdate(array $rowData, $rowNumber) ? $this->_parameters[Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR] : Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR ); - } elseif ($attributeParams['is_required'] && !$this->_getCustomerId($email, $website)) { - $this->addRowError(self::ERROR_VALUE_IS_REQUIRED, $rowNumber, $attributeCode); } } } From 0832e295c57f4140faf6101db73589f620d33896 Mon Sep 17 00:00:00 2001 From: carstenpfeifer <carsten.pfeifer2@gmail.com> Date: Sat, 30 Jun 2018 20:42:15 +0200 Subject: [PATCH 0097/1171] magento-engcom/import-export-improvements#30: Extend isErrorAlreadyAdded check to also check column name so several columns in the same row may throw the same error and they can be distinguished. --- .../Import/ErrorProcessing/ProcessingErrorAggregator.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php b/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php index 079b2e60c4785..7f49e2022c410 100644 --- a/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php +++ b/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php @@ -77,7 +77,7 @@ public function addError( $errorMessage = null, $errorDescription = null ) { - if ($this->isErrorAlreadyAdded($rowNumber, $errorCode)) { + if ($this->isErrorAlreadyAdded($rowNumber, $errorCode, $columnName)) { return $this; } $this->processErrorStatistics($errorLevel); @@ -333,13 +333,14 @@ public function clear() /** * @param int $rowNum * @param string $errorCode + * @param string $columnName * @return bool */ - protected function isErrorAlreadyAdded($rowNum, $errorCode) + protected function isErrorAlreadyAdded($rowNum, $errorCode, $columnName = null) { $errors = $this->getErrorsByCode([$errorCode]); foreach ($errors as $error) { - if ($rowNum == $error->getRowNumber()) { + if ($rowNum == $error->getRowNumber() && $columnName == $error->getColumnName()) { return true; } } From 2e04d5092554a7f0e6961408d4bd5ded05cdbaf7 Mon Sep 17 00:00:00 2001 From: carstenpfeifer <carsten.pfeifer2@gmail.com> Date: Sat, 30 Jun 2018 23:34:44 +0200 Subject: [PATCH 0098/1171] magento-engcom/import-export-improvements#88: Need to adjust test CSV according to code change. For category identification, admin name must be used. --- .../Model/Import/_files/products_to_import_with_two_stores.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_two_stores.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_two_stores.csv index f343cd20ecc78..eda9f3a01bf55 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_two_stores.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_two_stores.csv @@ -1,4 +1,4 @@ product_websites,store_view_code,attribute_set_code,product_type,categories,sku,price,name,url_key -base,,Default,simple,Default Category/category-defaultstore,product,123,product,product +base,,Default,simple,Default Category/category-admin,product,123,product,product ,default,Default,simple,,product,,product-default,product-default ,fixturestore,Default,simple,,product,,product-fixture,product-fixture From 34ed6d16576c8360af62218f50f4b91fb03338c5 Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <v.vygovskyi@atwix.com> Date: Sun, 1 Jul 2018 16:39:10 +0000 Subject: [PATCH 0099/1171] 19 Added breadcrumbs support --- .../Model/Resolver/Category/Breadcrumbs.php | 83 +++++++++++++++++++ .../CatalogGraphQl/etc/schema.graphqls | 8 ++ 2 files changed, 91 insertions(+) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php new file mode 100644 index 0000000000000..4ebec6490d0e3 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Category; + +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; + +/** + * Retrieves breadcrumbs + */ +class Breadcrumbs implements ResolverInterface +{ + /** + * @var CollectionFactory + */ + private $collectionFactory; + + /** + * @var ValueFactory + */ + private $valueFactory; + + /** + * @param ValueFactory $valueFactory + * @param CollectionFactory $collectionFactory + */ + public function __construct( + ValueFactory $valueFactory, + CollectionFactory $collectionFactory + ) { + $this->collectionFactory = $collectionFactory; + $this->valueFactory = $valueFactory; + } + + /** + * {@inheritdoc} + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null): Value + { + $breadcrumbs = []; + + if (!isset($value['path'])) { + $result = function () { + return null; + }; + return $this->valueFactory->create($result); + } + + $categoryPath = $value['path']; + $pathCategoryIds = explode('/', $categoryPath); + $parentCategoryIds = array_slice($pathCategoryIds, 2, count($pathCategoryIds) - 3); + + if (count($parentCategoryIds)) { + $collection = $this->collectionFactory->create(); + $collection->addAttributeToSelect(['name', 'url_key']); + $collection->addAttributeToFilter('entity_id', $parentCategoryIds); + + foreach ($collection as $category) { + $breadcrumbs[] = [ + 'category_id' => $category->getId(), + 'category_name' => $category->getName(), + 'category_level' => $category->getLevel(), + 'category_url_key' => $category->getUrlKey(), + ]; + } + } + + $result = function () use ($breadcrumbs) { + return count($breadcrumbs) ? $breadcrumbs : null; + }; + + return $this->valueFactory->create($result); + } +} \ No newline at end of file diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 762861de94e67..9235ec271a3c2 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -381,6 +381,14 @@ interface CategoryInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."), sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.") ): CategoryProducts @doc(description: "The list of products assigned to the category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Products") + breadcrumbs: [Breadcrumb] @doc(description: "Breadcrumbs, parent categories info") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Breadcrumbs") +} + +type Breadcrumb @doc(description: "Breadcrumb item"){ + category_id: Int @doc(description: "Category ID") + category_name: String @doc(description: "Category name") + category_level: Int @doc(description: "Category level") + category_url_key: String @doc(description: "Category URL key") } type CustomizableRadioOption implements CustomizableOptionInterface @doc(description: "CustomizableRadioOption contains information about a set of radio buttons that are defined as part of a customizable option") { From db3f2c73416c19f9cc13b1cbc48d504e843dc476 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Mon, 2 Jul 2018 16:39:47 +0300 Subject: [PATCH 0100/1171] Mutations Prototype (POC) #74 - Added mutation support - Added GraphQL functional test as an example of how to add mutations --- app/code/Magento/GraphQl/etc/schema.graphqls | 5 ++- .../etc/schema.graphqls | 9 +++++ .../etc/schema.graphqls | 4 ++ .../TestModule/GraphQlMutationTest.php | 37 +++++++++++++++++++ .../GraphQl/Schema/SchemaGenerator.php | 1 + 5 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlMutationTest.php diff --git a/app/code/Magento/GraphQl/etc/schema.graphqls b/app/code/Magento/GraphQl/etc/schema.graphqls index 37ca2d8d7b378..503ca17de1af6 100644 --- a/app/code/Magento/GraphQl/etc/schema.graphqls +++ b/app/code/Magento/GraphQl/etc/schema.graphqls @@ -4,6 +4,9 @@ type Query { } +type Mutation { +} + input FilterTypeInput @doc(description: "FilterTypeInput specifies which action will be performed in a query ") { eq: String @doc(description: "Equals") finset: [String] @doc(description: "Find in set. The value can contain a set of comma-separated values") @@ -30,4 +33,4 @@ type SearchResultPageInfo @doc(description: "SearchResultPageInfo provides navig enum SortEnum @doc(description: "This enumeration indicates whether to return results in ascending or descending order") { ASC DESC -} \ No newline at end of file +} diff --git a/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/etc/schema.graphqls b/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/etc/schema.graphqls index 3466db5c71f6a..7eb175a88e322 100644 --- a/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/etc/schema.graphqls +++ b/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/etc/schema.graphqls @@ -5,7 +5,16 @@ type Query { testItem(id: Int!) : Item @resolver(class: "Magento\\TestModuleGraphQlQuery\\Model\\Resolver\\Item") } +type Mutation { + testItem(id: Int!) : MutationItem @resolver(class: "Magento\\TestModuleGraphQlQuery\\Model\\Resolver\\Item") +} + type Item { item_id: Int name: String } + +type MutationItem { + item_id: Int + name: String +} diff --git a/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQueryExtension/etc/schema.graphqls b/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQueryExtension/etc/schema.graphqls index dc01d993c3818..b970ad8376349 100644 --- a/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQueryExtension/etc/schema.graphqls +++ b/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQueryExtension/etc/schema.graphqls @@ -4,3 +4,7 @@ type Item { integer_list: [Int] @resolver(class: "Magento\\TestModuleGraphQlQueryExtension\\Model\\Resolver\\IntegerList") } + +type MutationItem { + integer_list: [Int] @resolver(class: "Magento\\TestModuleGraphQlQueryExtension\\Model\\Resolver\\IntegerList") +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlMutationTest.php new file mode 100644 index 0000000000000..b6e1a61f0357c --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlMutationTest.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\TestModule; + +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Make sure that it is possible to use GraphQL mutations in Magento + */ +class GraphQlMutationTest extends GraphQlAbstract +{ + public function testMutation() + { + $id = 3; + + $query = <<<MUTATION +mutation { + testItem(id: {$id}) { + item_id, + name, + integer_list + } +} +MUTATION; + + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('testItem', $response); + $testItem = $response['testItem']; + $this->assertArrayHasKey('integer_list', $testItem); + $this->assertEquals([4, 5, 6], $testItem['integer_list']); + } +} diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php b/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php index 668ec2bdc84e4..63fef73186b12 100644 --- a/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php +++ b/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php @@ -56,6 +56,7 @@ public function generate() : Schema $schema = $this->schemaFactory->create( [ 'query' => $this->outputMapper->getOutputType('Query'), + 'mutation' => $this->outputMapper->getOutputType('Mutation'), 'typeLoader' => function ($name) { return $this->outputMapper->getOutputType($name); }, From 24448d01e8afbf9cc698b32fad1c4c6a1446527a Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Mon, 2 Jul 2018 10:15:35 -0500 Subject: [PATCH 0101/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - relying on item from cart as it has a setting in the admin that wishlist should have it too --- .../Customer/Wishlist/Item/Column/Image.php | 57 +++++++++++++++++++ app/code/Magento/Wishlist/etc/frontend/di.xml | 8 +++ .../templates/item/column/image.phtml | 2 +- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php index 02a897d44b3c6..f76543527e0fd 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php @@ -11,10 +11,67 @@ */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; +use Magento\Catalog\Model\Product\Image\UrlBuilder; +use Magento\Framework\View\ConfigInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Quote\Model\Quote\ItemFactory; +use Magento\Checkout\Block\Cart\Item\Renderer; + /** * @api * @since 100.0.2 */ class Image extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column { + /** @var \Magento\Checkout\Block\Cart\Item\Renderer[] */ + private $renderers = []; + + /** @var \Magento\Quote\Model\Quote\ItemFactory */ + private $itemFactory; + + /** + * @param \Magento\Catalog\Block\Product\Context $context + * @param \Magento\Framework\App\Http\Context $httpContext + * @param array $data + * @param ConfigInterface|null $config + * @param UrlBuilder|null $urlBuilder + * @param Renderer[] $renderers + * @param ItemFactory|null $itemFactory + */ + public function __construct( + \Magento\Catalog\Block\Product\Context $context, + \Magento\Framework\App\Http\Context $httpContext, + array $data = [], + ConfigInterface $config = null, + UrlBuilder $urlBuilder = null, + array $renderers = [], + ItemFactory $itemFactory = null + ) { + $this->renderers = $renderers; + $this->itemFactory = $itemFactory ?? ObjectManager::getInstance()->get(ItemFactory::class); + parent::__construct( + $context, + $httpContext, + $data, + $config, + $urlBuilder + ); + } + + /** + * Identify the product from which thumbnail should be taken. + * + * @return \Magento\Catalog\Model\Product + */ + public function getProductForThumbnail(\Magento\Wishlist\Model\Item $item) + { + $product = $product = $item->getProduct(); + if (isset($this->renderers[$product->getTypeId()])) { + $quoteItem = $this->itemFactory->create(['data' => $item->getData()]); + $quoteItem->setProduct($product); + $quoteItem->setOptions($item->getOptions()); + return $this->renderers[$product->getTypeId()]->setItem($quoteItem)->getProductForThumbnail(); + } + return $product; + } } diff --git a/app/code/Magento/Wishlist/etc/frontend/di.xml b/app/code/Magento/Wishlist/etc/frontend/di.xml index 00642b132bfdf..802f3612f2c9b 100644 --- a/app/code/Magento/Wishlist/etc/frontend/di.xml +++ b/app/code/Magento/Wishlist/etc/frontend/di.xml @@ -47,4 +47,12 @@ </argument> </arguments> </type> + <type name="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Image"> + <arguments> + <argument name="renderers" xsi:type="array"> + <item name="configurable" xsi:type="object">Magento\ConfigurableProduct\Block\Cart\Item\Renderer\Configurable</item> + <item name="grouped" xsi:type="object">Magento\GroupedProduct\Block\Cart\Item\Renderer\Grouped</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/image.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/image.phtml index 1008f5f377df5..2eb4664991e99 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/image.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/image.phtml @@ -13,5 +13,5 @@ $item = $block->getItem(); $product = $item->getProduct(); ?> <a class="product-item-photo" tabindex="-1" href="<?= $block->escapeUrl($block->getProductUrl($item)) ?>" title="<?= $block->escapeHtmlAttr($product->getName()) ?>"> - <?= $block->getImage($product, 'wishlist_thumbnail')->toHtml() ?> + <?= $block->getImage($block->getProductForThumbnail($item), 'wishlist_thumbnail')->toHtml() ?> </a> From 9074980e9c2a3e2b2d1cb86e84e16095b6e5d8a2 Mon Sep 17 00:00:00 2001 From: Scott Buchanan <sbuchanan@ri.pn> Date: Wed, 15 Nov 2017 18:16:23 -0500 Subject: [PATCH 0102/1171] prevent layout cache corruption --- lib/internal/Magento/Framework/View/Model/Layout/Merge.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/internal/Magento/Framework/View/Model/Layout/Merge.php b/lib/internal/Magento/Framework/View/Model/Layout/Merge.php index fe5c94ed21b30..26dd7e40d45f2 100644 --- a/lib/internal/Magento/Framework/View/Model/Layout/Merge.php +++ b/lib/internal/Magento/Framework/View/Model/Layout/Merge.php @@ -443,6 +443,9 @@ public function load($handles = []) if ($result) { $this->addUpdate($result); $this->pageLayout = $this->_loadCache($cacheIdPageLayout); + foreach ($this->getHandles() as $handle) { + $this->allHandles[$handle] = $this->handleProcessed; + } return $this; } From eb2a5a389a0dd763df463312e80e67dc63afd608 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Mon, 2 Jul 2018 19:19:50 +0300 Subject: [PATCH 0103/1171] Mutations Prototype (POC) #74 - Added temporary placeholder field to Mutations --- app/code/Magento/GraphQl/etc/schema.graphqls | 1 + .../Magento/Framework/GraphQl/_files/schemaA.graphqls | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/app/code/Magento/GraphQl/etc/schema.graphqls b/app/code/Magento/GraphQl/etc/schema.graphqls index 503ca17de1af6..c6651cdde0cb3 100644 --- a/app/code/Magento/GraphQl/etc/schema.graphqls +++ b/app/code/Magento/GraphQl/etc/schema.graphqls @@ -5,6 +5,7 @@ type Query { } type Mutation { + placeholderMutation: String @doc(description: "Mutation type cannot be declared without fields. The placeholder will be removed when at least one mutation field is declared.") } input FilterTypeInput @doc(description: "FilterTypeInput specifies which action will be performed in a query ") { diff --git a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/_files/schemaA.graphqls b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/_files/schemaA.graphqls index d736bb4fa26f1..6c832e5f122a6 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/_files/schemaA.graphqls +++ b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/_files/schemaA.graphqls @@ -2,6 +2,10 @@ type Query { placeholder: String @doc(description: "comment for placeholder.") } +type Mutation { + placeholder: String @doc(description: "comment for placeholder.") +} + input FilterTypeInput @doc(description:"Comment for FilterTypeInput") { eq: String @doc(description:"Equal") finset: [String] From e01f26c160079f91f6b927824f0518e1d932c6cb Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Mon, 2 Jul 2018 12:01:10 -0500 Subject: [PATCH 0104/1171] MAGETWO-77744: [Magento Cloud] - Error message when uploading unsupported file format --- .../frontend/web/js/catalog-add-to-cart.js | 23 +++++++++++++++---- .../view/type/options/configurable.phtml | 1 - .../frontend/web/js/catalog-add-to-cart.js | 4 +--- .../frontend/web/js/catalog-add-to-cart.js | 11 +++++++-- .../view/frontend/web/js/swatch-renderer.js | 16 +++++++++++-- 5 files changed, 42 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js index 9fffb0478059e..83e91d3c3d4c7 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js @@ -49,6 +49,23 @@ define([ }); }, + /** + * @private + */ + _redirect: function (url) { + var urlParts, locationParts, forceReload; + + urlParts = url.split('#'); + locationParts = window.location.href.split('#'); + forceReload = urlParts[0] === locationParts[0]; + + window.location.assign(url); + + if (forceReload) { + window.location.reload(); + } + }, + /** * @return {Boolean} */ @@ -119,12 +136,8 @@ define([ parameters.push(eventData.redirectParameters.join('&')); res.backUrl = parameters.join('#'); } - window.location.href = res.backUrl; - // page does not reload when anchor (#) is added - if (res.backUrl.indexOf('#') !== -1) { - window.location.reload(); - } + self._redirect(res.backUrl); return; } diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml b/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml index fb8541da00a94..f5ed067967547 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml +++ b/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml @@ -22,7 +22,6 @@ $_attributes = $block->decorateArray($block->getAllowAttributes()); <div class="control"> <select name="super_attribute[<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>]" data-selector="super_attribute[<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>]" - data-attribute-code="<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>" data-validate="{required:true}" id="attribute<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>" class="super-attribute-select"> diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js index f3b8ed0e84d63..817bc7a0d6b27 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js @@ -9,9 +9,7 @@ require([ $('body').on('catalogCategoryAddToCartRedirect', function (event, data) { $(data.form).find('select[name*="super"]').each(function (index, item) { - var $item = $(item); - - data.redirectParameters.push($item.attr('data-attribute-code') + '=' + $item.val()); + data.redirectParameters.push(item.config.id + '=' + $(item).val()); }); }); }); diff --git a/app/code/Magento/Swatches/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Swatches/view/frontend/web/js/catalog-add-to-cart.js index d699faae3a85f..30f2d1007df0b 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/catalog-add-to-cart.js @@ -9,9 +9,16 @@ require([ $('body').on('catalogCategoryAddToCartRedirect', function (event, data) { $(data.form).find('[name*="super"]').each(function (index, item) { - var $item = $(item); + var $item = $(item), + attr; + + if ($item.attr('data-attr-name')) { + attr = $item.attr('data-attr-name'); + } else { + attr = $item.parent().attr('attribute-code'); + } + data.redirectParameters.push(attr + '=' + $item.val()); - data.redirectParameters.push($item.attr('data-attr-name') + '=' + $item.val()); }); }); }); diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js index 3fea6b7dddf14..4cc6eddba5f95 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js @@ -1214,8 +1214,20 @@ define([ */ _EmulateSelected: function (selectedAttributes) { $.each(selectedAttributes, $.proxy(function (attributeCode, optionId) { - this.element.find('.' + this.options.classes.attributeClass + - '[attribute-code="' + attributeCode + '"] [option-id="' + optionId + '"]').trigger('click'); + var elem = this.element.find('.' + this.options.classes.attributeClass + + '[attribute-code="' + attributeCode + '"] [option-id="' + optionId + '"]'), + parentInput = elem.parent(); + + if (elem.hasClass('selected')) { + return; + } + + if (parentInput.hasClass(this.options.classes.selectClass)) { + parentInput.val(optionId); + parentInput.trigger('change'); + } else { + elem.trigger('click'); + } }, this)); }, From 4a4bbe2fc0947a8ff17906b4de0204ad5143aa21 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Tue, 3 Jul 2018 16:28:58 +0300 Subject: [PATCH 0105/1171] GraphQL-116: Wrong category table name resolving --- .../Magento/CatalogGraphQl/Model/Category/LevelCalculator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/LevelCalculator.php b/app/code/Magento/CatalogGraphQl/Model/Category/LevelCalculator.php index eb57873850b80..0401e1c42331e 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/LevelCalculator.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/LevelCalculator.php @@ -37,7 +37,7 @@ public function calculate(int $rootCategoryId) : int { $connection = $this->resourceConnection->getConnection(); $select = $connection->select() - ->from($connection->getTableName('catalog_category_entity'), 'level') + ->from($this->resourceConnection->getTableName('catalog_category_entity'), 'level') ->where($this->resourceCategory->getLinkField() . " = ?", $rootCategoryId); return (int) $connection->fetchOne($select); } From 363a787067d24ffe1e36998cb267fba70e529fa2 Mon Sep 17 00:00:00 2001 From: Devagouda <dpatil@magento.com> Date: Tue, 3 Jul 2018 11:02:14 -0500 Subject: [PATCH 0106/1171] MFTF Test for MAGETWO-67627 --- .../CreateNewPageWithAllValuesActionGroup.xml | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml new file mode 100644 index 0000000000000..1f4eda7e2c34a --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateNewPageWithAllValues"> + <arguments> + <argument name="PageTitle" type="string"/> + <argument name="ContentHeading" type="string"/> + <argument name="URLKey" type="string"/> + <argument name="selectStoreViewOpt" type="string"/> + <argument name="selectHierarchyOpt" type="string"/> + </arguments> + <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnCMSNewPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <fillField selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{PageTitle}}" stepKey="fillFieldTitle"/> + <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickExpandContent"/> + <fillField selector="{{CmsNewPagePageContentSection.contentHeading}}" userInput="{{ContentHeading}}" stepKey="fillFieldContentHeading"/> + <click selector="{{CmsNewPagePageSeoSection.header}}" stepKey="clickExpandSearchEngineOptimization"/> + <fillField selector="{{CmsNewPagePageSeoSection.urlKey}}" userInput="{{URLKey}}" stepKey="fillFieldURLKey"/> + <click selector="{{CmsNewPagePiwSection.header}}" stepKey="clickPageInWebsites"/> + <waitForElementVisible selector="{{CmsNewPagePiwSection.selectStoreView(selectStoreViewOpt)}}" stepKey="waitForStoreGridReload"/> + <clickWithLeftButton selector="{{CmsNewPagePiwSection.selectStoreView(selectStoreViewOpt)}}" stepKey="clickStoreView2"/> + <click selector="{{CmsNewPageHierarchySection.header}}" stepKey="clickHierarchy"/> + <click selector="{{CmsNewPageHierarchySection.selectHierarchy(selectHierarchyOpt)}}" stepKey="clickPageCheckBoxes"/> + </actionGroup> +</actionGroups> \ No newline at end of file From 4894227f9961645fe60a73ee755d010d4d52e157 Mon Sep 17 00:00:00 2001 From: Devagouda <dpatil@magento.com> Date: Tue, 3 Jul 2018 11:50:41 -0500 Subject: [PATCH 0107/1171] Added MFTF Test remaining code for MAGETWO-67627 --- .../DeletePageByUrlKeyActionGroup.xml | 23 +++++++++++++++++++ .../Section/CmsNewPageHierarchySection.xml | 18 +++++++++++++++ .../Cms/Section/CmsNewPagePiwSection.xml | 15 ++++++++++++ .../Section/CmsPagesPageActionsSection.xml | 2 ++ .../FunctionalTest/Store/Data/StoreData.xml | 10 ++++++++ .../Store/Data/StoreGroupData.xml | 6 +++++ .../AdminNewStoreViewActionsSection.xml | 2 +- .../Section/AdminNewWebsiteActionsSection.xml | 2 +- .../Section/AdminStoreGroupActionsSection.xml | 2 +- 9 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/ActionGroup/DeletePageByUrlKeyActionGroup.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPageHierarchySection.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPagePiwSection.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/ActionGroup/DeletePageByUrlKeyActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/ActionGroup/DeletePageByUrlKeyActionGroup.xml new file mode 100644 index 0000000000000..97b80e244a44a --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/ActionGroup/DeletePageByUrlKeyActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeletePageByUrlKeyActionGroup"> + <arguments> + <argument name="UrlKey" type="string"/> + </arguments> + <amOnPage url="{{CmsPagesPage.url}}" stepKey="amOnCMSNewPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click selector="{{CmsPagesPageActionsSection.select(UrlKey)}}" stepKey="clickSelect"/> + <click selector="{{CmsPagesPageActionsSection.delete(UrlKey)}}" stepKey="clickDelete"/> + <waitForElementVisible selector="{{CmsPagesPageActionsSection.deleteConfirm}}" stepKey="waitForOkButtonToBeVisible"/> + <click selector="{{CmsPagesPageActionsSection.deleteConfirm}}" stepKey="clickOkButton"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <see userInput="The page has been deleted." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPageHierarchySection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPageHierarchySection.xml new file mode 100644 index 0000000000000..e86144262238a --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPageHierarchySection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="CmsNewPageHierarchySection"> + <element name="header" type="button" selector="div[data-index=hierarchy]" timeout="30"/> + <element name="selectHierarchy" type="button" selector="//a/span[contains(text(),'{{var1}}')]" parameterized="true" timeout="30"/> + </section> +</sections> + + + diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPagePiwSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPagePiwSection.xml new file mode 100644 index 0000000000000..821e6b1b455eb --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPagePiwSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="CmsNewPagePiwSection"> + <element name="header" type="button" selector="div[data-index=websites]" timeout="30"/> + <element name="selectStoreView" type="select" selector="//option[contains(text(),'{{var1}}')]" parameterized="true"/> + </section> +</sections> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsPagesPageActionsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsPagesPageActionsSection.xml index 7d4b06ff6d947..3f9ac9f9dbffc 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsPagesPageActionsSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsPagesPageActionsSection.xml @@ -25,5 +25,7 @@ <element name="firstItemEditButton" type="button" selector=".data-grid .action-select-wrap .action-menu-item[data-action~='item-edit']"/> <element name="activeFilter" type="button" selector="(//div[contains(@class, 'admin__data-grid-filters-current') and contains(@class, '_show')])[1]"/> <element name="savePageSuccessMessage" type="text" selector=".message-success"/> + <element name="delete" type="button" selector="//div[text()='{{var1}}']/parent::td//following-sibling::td[@class='data-grid-actions-cell']//a[text()='Delete']" parameterized="true"/> + <element name="deleteConfirm" type="button" selector=".action-primary.action-accept" timeout="60"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Data/StoreData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Data/StoreData.xml index 1d0fcfd57b01e..aa96e6976bb3f 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Data/StoreData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Data/StoreData.xml @@ -39,4 +39,14 @@ <data key="store_type">group</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> + <entity name="staticStore" type="store"> + <!--data key="group_id">customStoreGroup.id</data--> + <data key="name" >Second Store View</data> + <data key="code" >store123</data> + <data key="is_active">1</data> + <data key="store_id">null</data> + <data key="store_action">add</data> + <data key="store_type">group</data> + <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> + </entity> </entities> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Data/StoreGroupData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Data/StoreGroupData.xml index 891d307808760..989ee9ad7a6ad 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Data/StoreGroupData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Data/StoreGroupData.xml @@ -21,4 +21,10 @@ <data key="store_action">add</data> <data key="store_type">group</data> </entity> + <entity name="staticStoreGroup" type="group"> + <data key="name">NewStore</data> + <data key="code">Base12</data> + <data key="root_category_id">2</data> + <data key="website_id">1</data> + </entity> </entities> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewStoreViewActionsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewStoreViewActionsSection.xml index f41ae17364a42..fe778711b0a87 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewStoreViewActionsSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewStoreViewActionsSection.xml @@ -10,6 +10,6 @@ <element name="backButton" type="button" selector="#back" timeout="30"/> <element name="delete" type="button" selector="#delete" timeout="30"/> <element name="resetButton" type="button" selector="#reset" timeout="30"/> - <element name="saveButton" type="button" selector="#save" timeout="30"/> + <element name="saveButton" type="button" selector="#save" timeout="60"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewWebsiteActionsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewWebsiteActionsSection.xml index 18d1ef2642ef8..2103aac32f038 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewWebsiteActionsSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewWebsiteActionsSection.xml @@ -7,6 +7,6 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminNewWebsiteActionsSection"> - <element name="saveWebsite" type="button" selector="#save" timeout="30"/> + <element name="saveWebsite" type="button" selector="#save" timeout="60"/> </section> </sections> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminStoreGroupActionsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminStoreGroupActionsSection.xml index 0490b4c52a074..8cd19e806c9e9 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminStoreGroupActionsSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminStoreGroupActionsSection.xml @@ -7,6 +7,6 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminStoreGroupActionsSection"> - <element name="saveButton" type="button" selector="#save" timeout="30" /> + <element name="saveButton" type="button" selector="#save" timeout="60" /> </section> </sections> From e09b1aa5d51fd89f386791758f83cd20a004f545 Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Tue, 3 Jul 2018 12:16:57 -0500 Subject: [PATCH 0108/1171] MAGETWO-91465: Once integer is stored for State/Province field, it can not be changed to alphanumeric --- app/code/Magento/Ui/view/base/web/js/form/element/region.js | 1 + .../Magento/Ui/view/base/web/js/lib/validation/rules.js | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/region.js b/app/code/Magento/Ui/view/base/web/js/form/element/region.js index 0edb4c1966b54..fec69bf8558b5 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/region.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/region.js @@ -57,6 +57,7 @@ define([ registry.get(this.customName, function (input) { isRegionRequired = !!option['is_region_required']; input.validation['required-entry'] = isRegionRequired; + input.validation['validate-not-number-first'] = true; input.required(isRegionRequired); }); } diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 41e2703f4ed1e..14277499ec6ce 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -755,6 +755,12 @@ define([ }, $.mage.__('Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.')//eslint-disable-line max-len ], + 'validate-not-number-first': [ + function (value) { + return utils.isEmptyNoTrim(value) || /^[^0-9].*$$/.test(value); + }, + $.mage.__('First character must be letter.')//eslint-disable-line max-len + ], 'validate-date': [ function (value, params, additionalParams) { var test = moment(value, additionalParams.dateFormat); From 50e1f0a1256c11a06d6cb62c42763e308dfab7db Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Tue, 3 Jul 2018 12:19:43 -0500 Subject: [PATCH 0109/1171] MAGETWO-91465: Once integer is stored for State/Province field, it can not be changed to alphanumeric --- app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 14277499ec6ce..2a19b6ad7e12b 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -757,7 +757,7 @@ define([ ], 'validate-not-number-first': [ function (value) { - return utils.isEmptyNoTrim(value) || /^[^0-9].*$$/.test(value); + return utils.isEmptyNoTrim(value) || /^[^0-9].*$/.test(value); }, $.mage.__('First character must be letter.')//eslint-disable-line max-len ], From d73e5449697c1e69f566bdeebe98c2cb04150033 Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Tue, 3 Jul 2018 13:33:29 -0500 Subject: [PATCH 0110/1171] MAGETWO-92929: Declarative schema tests are located in inappropriate place - codestyle fixes --- .../Framework/Setup/Test/Unit/SchemaListenerTest.php | 10 +++++----- .../Framework/Setup/Test/Unit/SchemaPersistorTest.php | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaListenerTest.php b/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaListenerTest.php index 2c17c1564fe63..38b81fa7c744c 100644 --- a/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaListenerTest.php +++ b/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaListenerTest.php @@ -125,7 +125,7 @@ public function testCreateTable() 'nullable' => false, 'default' => 'CURRENT_TIMESTAMP', 'disabled' => false, - 'onCreate' => NULL, + 'onCreate' => null, ], 'integer' => [ @@ -135,9 +135,9 @@ public function testCreateTable() 'unsigned' => false, 'nullable' => false, 'identity' => true, - 'default' => NULL, + 'default' => null, 'disabled' => false, - 'onCreate' => NULL, + 'onCreate' => null, ], 'decimal' => [ @@ -147,9 +147,9 @@ public function testCreateTable() 'precision' => '25', 'unsigned' => false, 'nullable' => false, - 'default' => NULL, + 'default' => null, 'disabled' => false, - 'onCreate' => NULL, + 'onCreate' => null, ], ], $tables['First_Module']['new_table']['columns'] diff --git a/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaPersistorTest.php b/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaPersistorTest.php index be8775444b602..9e416fc514755 100644 --- a/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaPersistorTest.php +++ b/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaPersistorTest.php @@ -143,6 +143,7 @@ public function schemaListenerTablesDataProvider() ] ] ], + // @codingStandardsIgnoreStart 'XMLResult' => '<?xml version="1.0"?> <schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd"> @@ -161,6 +162,7 @@ public function schemaListenerTablesDataProvider() </index> </table> </schema>' + // @codingStandardsIgnoreEnd ] ]; } From 142ffbedb19c40dbe6de323f14a07a029593b6b0 Mon Sep 17 00:00:00 2001 From: John Stennett <john00ivy@gmail.com> Date: Tue, 3 Jul 2018 15:14:49 -0500 Subject: [PATCH 0111/1171] MQE-920: MSI MFTF Test Cases 2 - Merging MSI elements into the Product Form Configuration Section file. - Moving a "ConfigurableProduct" module section to the "ConfigurableProduct" module under 'app/code'. --- .../Mftf}/Section/AdminProductFormConfigurationsSection.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct => app/code/Magento/ConfigurableProduct/Test/Mftf}/Section/AdminProductFormConfigurationsSection.xml (87%) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminProductFormConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml similarity index 87% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminProductFormConfigurationsSection.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml index 43bf9822903be..8a22d3fd8afcb 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminProductFormConfigurationsSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml @@ -25,7 +25,7 @@ <section name="AdminConfigurableProductFormSection"> <element name="productWeight" type="input" selector=".admin__control-text[name='product[weight]']"/> <element name="productQuantity" type="input" selector=".admin__control-text[name='product[quantity_and_stock_status][qty]']"/> - <element name="currentVariationsQuantityCells" type="button" selector="td[data-index='quantity_container']"/> + <element name="currentVariationsQuantityCells" type="button" selector="td[data-index='quantity_per_source_container']"/> <element name="rowByCode" type="textarea" selector="//span[contains(text(), '{{var1}}-{{var2}}')]//ancestor-or-self::tr" parameterized="true"/> </section> <section name="AdminConfigurableProductSelectAttributesSlideOut"> @@ -34,7 +34,9 @@ <section name="AdminConfigurableProductAssignSourcesSlideOut"> <element name="done" type="button" selector=".product_form_product_form_assign_sources_configurable_modal .action-primary" timeout="5"/> <element name="assignSources" type="button" selector="(//button/span[contains(text(), 'Assign Sources')])[2]" timeout="5"/> - <element name="quantityPerSource" type="input" selector="input[name='quantity_resolver[dynamicRows][dynamicRows][0][quantity_per_source]']"/> + <element name="quantityPerSource" type="input" selector="input[name='quantity_resolver[dynamicRows][dynamicRows][{{var1}}][quantity_per_source]']" parameterized="true"/> + <element name="quantityPerSourceForSingleMode" type="input" selector="#apply-single-inventory-input"/> + <element name="quantityPerSourceForMultiMode" type="input" selector="input[name='quantity_resolver[dynamicRows][dynamicRows][0][quantity_per_source]']"/> </section> <section name="StorefrontConfigurableProductPage"> <element name="productAttributeDropDown" type="select" selector="select[id*='attribute']"/> From 11e1917d0a90c053f38fc86259180184602fec45 Mon Sep 17 00:00:00 2001 From: John Stennett <john00ivy@gmail.com> Date: Tue, 3 Jul 2018 15:20:03 -0500 Subject: [PATCH 0112/1171] MQE-920: MSI MFTF Test Cases 2 - Merging MSI elements into the Product Form Configuration Section file under UI. - Moving a "Ui" module section to the "Ui" module under 'app/code'. --- .../AdminGridFilterSearchResultsActionGroup.xml | 4 ++-- .../Ui/Section/AdminGridControlsSection.xml | 17 +++++++++++++++-- .../Ui/Section/AdminMessagesSection.xml | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminMessagesSection.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/ActionGroup/AdminGridFilterSearchResultsActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/ActionGroup/AdminGridFilterSearchResultsActionGroup.xml index 459ad5070da01..191fd1b4183d9 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/ActionGroup/AdminGridFilterSearchResultsActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/ActionGroup/AdminGridFilterSearchResultsActionGroup.xml @@ -11,10 +11,10 @@ <actionGroup name="AdminGridFilterSearchResultsByInput"> <arguments> <argument name="selector"/> - <argument name="value"/> + <argument name="value" type="string"/> </arguments> - <conditionalClick selector="{{AdminGridFilterControls.clearAll}}" dependentSelector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-filters-current._show" visible="true" stepKey="clearTheFiltersIfPresent"/> + <conditionalClick selector="{{AdminGridFilterControls.clearAll}}" dependentSelector="(//*[contains(@class, 'admin__data-grid-header')][contains(@data-bind, 'afterRender: \$data.setToolbarNode')]//*[contains(@class, 'admin__data-grid-filters-current')][contains(@class, '_show')])[1]" visible="true" stepKey="clearTheFiltersIfPresent"/> <waitForPageLoad stepKey="waitForPageLoad" time="5"/> <click selector="{{AdminGridFilterControls.filters}}" stepKey="clickOnFilters1"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminGridControlsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminGridControlsSection.xml index 91bf1001a40c1..16f9ab124993c 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminGridControlsSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminGridControlsSection.xml @@ -27,7 +27,7 @@ <element name="filters" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] button[data-action='grid-filter-expand']" timeout="5"/> <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> <element name="cancel" type="button" selector="button[data-action='grid-filter-cancel']" timeout="30"/> - <element name="clearAll" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] button[data-action='grid-filter-reset']" timeout="5"/> + <element name="clearAll" type="button" selector="(//*[contains(@class, 'admin__data-grid-header')][contains(@data-bind, 'afterRender: \$data.setToolbarNode')]//button[contains(@data-action, 'reset')])[1]" timeout="5"/> </section> <section name="AdminGridDefaultViewControls"> <element name="defaultView" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-action-bookmarks" timeout="5"/> @@ -54,7 +54,8 @@ <!-- TODO: Pagination controls --> <section name="AdminGridHeaders"> <element name="title" type="text" selector=".page-title-wrapper h1"/> - <element name="headerByName" type="text" selector="//span[@class='data-grid-cell-content' and contains(text(), '{{var1}}')]/parent::*" parameterized="true"/> + <element name="headerByName" type="text" selector="//div[@data-role='grid-wrapper']//span[@class='data-grid-cell-content' and contains(text(), '{{var1}}')]/parent::*" parameterized="true"/> + <element name="columnsNames" type="text" selector="[data-role='grid-wrapper'] .data-grid-th > span"/> </section> <section name="AdminGridRow"> <element name="rowOne" type="text" selector="tr[data-repeat-index='0']"/> @@ -65,4 +66,16 @@ <element name="checkboxByValue" type="checkbox" selector="//input[ancestor::tr[contains(., '{{var1}}')]]" parameterized="true"/> <element name="checkboxByIndex" type="checkbox" selector=".data-row[data-repeat-index='{{var1}}'] .admin__control-checkbox" parameterized="true"/> </section> + <section name="AdminGridSelectRows"> + <element name="multicheckDropdown" type="button" selector="div[data-role='grid-wrapper'] th.data-grid-multicheck-cell button.action-multicheck-toggle"/> + <element name="multicheckOption" type="button" selector="//div[@data-role='grid-wrapper']//th[contains(@class, data-grid-multicheck-cell)]//li//span[text() = '{{label}}']" parameterized="true"/> + <element name="bulkActionDropdown" type="button" selector="div.admin__data-grid-header-row.row div.action-select-wrap button.action-select"/> + <element name="bulkActionOption" type="button" selector="//div[contains(@class,'admin__data-grid-header-row') and contains(@class, 'row')]//div[contains(@class, 'action-select-wrap')]//ul/li/span[text() = '{{label}}']" parameterized="true"/> + </section> + <section name="AdminGridConfirmActionSection"> + <element name="title" type="text" selector=".modal-popup.confirm h1.modal-title"/> + <element name="message" type="text" selector=".modal-popup.confirm div.modal-content"/> + <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> + <element name="ok" type="button" selector=".modal-popup.confirm button.action-accept" timeout="60"/> + </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminMessagesSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminMessagesSection.xml new file mode 100644 index 0000000000000..a727104f48014 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminMessagesSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminMessagesSection"> + <element name="successMessage" type="text" selector=".message-success"/> + <element name="errorMessage" type="text" selector=".message.message-error.error"/> + </section> +</sections> From 64af22f6f19e1cabb5a5a1ccd1262944f1201376 Mon Sep 17 00:00:00 2001 From: John Stennett <john00ivy@gmail.com> Date: Tue, 3 Jul 2018 15:40:41 -0500 Subject: [PATCH 0113/1171] MQE-920: MSI MFTF Test Cases 2 - Adding necessary CatalogInventory section files to the "CatalogInventory" module under "app/code". --- .../Section/AdminAdvancedInventorySection.xml | 20 +++++++++++++++++++ .../Section/ProductStockOptionsSection.xml | 16 +++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminAdvancedInventorySection.xml create mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/Section/ProductStockOptionsSection.xml diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminAdvancedInventorySection.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminAdvancedInventorySection.xml new file mode 100644 index 0000000000000..0a51d778a02f8 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminAdvancedInventorySection.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminAdvancedInventoryControlsSection"> + <element name="done" type="button" selector=".page-main-actions button.action-primary" timeout="10"/> + </section> + <section name="AdminAdvancedInventorySection"> + <element name="manageStock" type="select" selector="select[name='product[stock_data][manage_stock]']"/> + <element name="manageStockUseDefault" type="checkbox" selector="input[name='product[stock_data][use_config_manage_stock]']"/> + <element name="outOfStockThreshold" type="text" selector="input[name='product[stock_data][min_qty]']"/> + <element name="outOfStockThresholdUseDefault" type="checkbox" selector="input[name='product[stock_data][use_config_min_qty]']"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Section/ProductStockOptionsSection.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Section/ProductStockOptionsSection.xml new file mode 100644 index 0000000000000..958d774a76418 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Section/ProductStockOptionsSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="ProductStockOptionsSection"> + <element name="CheckIfTabExpand" type="button" selector="#cataloginventory_item_options-head:not(.open)"/> + <element name="ProductStockOptions" type="button" selector="#cataloginventory_item_options-head"/> + <element name="OutOfStockThresholdSystemValue" type="button" selector="#cataloginventory_item_options_min_qty_inherit"/> + <element name="OutOfStockThresholdValue" type="input" selector="#cataloginventory_item_options_min_qty"/> + <element name="Save" type="button" selector="#save"/> + </section> +</sections> From 13f79696847aa93c4143dfcd093bd2892e2a4cbb Mon Sep 17 00:00:00 2001 From: John Stennett <john00ivy@gmail.com> Date: Tue, 3 Jul 2018 16:01:20 -0500 Subject: [PATCH 0114/1171] MQE-920: MSI MFTF Test Cases 2 - Merging necessary Checkout Action Group/Section to the Checkout module. - Moving necessary Checkout files to the "app/code" directory. --- .../StorefrontProductCartActionGroup.xml | 18 ++++++++++++++++++ .../Section/StorefrontMiniCartSection.xml | 2 ++ 2 files changed, 20 insertions(+) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout => app/code/Magento/Checkout/Test/Mftf}/ActionGroup/StorefrontProductCartActionGroup.xml (77%) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout => app/code/Magento/Checkout/Test/Mftf}/Section/StorefrontMiniCartSection.xml (88%) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml similarity index 77% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/ActionGroup/StorefrontProductCartActionGroup.xml rename to app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml index b98e1046c4a5b..acd932771dd2e 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/ActionGroup/StorefrontProductCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -21,6 +21,24 @@ <waitForText userInput="{{productCount}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> </actionGroup> + <!-- Add Product to Cart from the category page with specified quantity and check message and product count in Minicart --> + <actionGroup name="StorefrontAddCategoryProductToCartWithQuantityActionGroup"> + <arguments> + <argument name="product"/> + <argument name="quantity" defaultValue="1" type="string"/> + <argument name="checkQuantity" defaultValue="1" type="string"/> + </arguments> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="clickAddToCart" /> + <!-- @TODO: Use general message selector after MQE-694 is fixed --> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart(product.name)}}" time="30" stepKey="assertMessage"/> + <waitForText userInput="{{checkQuantity}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity(product.name)}}" userInput="{{quantity}}" stepKey="setProductQtyToFiftyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate(product.name)}}" stepKey="updateQtyInMiniCart"/> + </actionGroup> + <!-- Add Product to Cart from the product page and check message and product count in Minicart --> <actionGroup name="StorefrontAddProductToCartActionGroup"> <arguments> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml similarity index 88% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml rename to app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml index bdd97130a9715..b421eef74b8ea 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml @@ -23,5 +23,7 @@ <element name="viewAndEditCart" type="button" selector=".action.viewcart" timeout="30"/> <element name="miniCartItemsText" type="text" selector=".minicart-items"/> <element name="deleteMiniCartItem" type="button" selector=".action.delete" timeout="30"/> + <element name="itemQuantity" type="input" selector="//a[text()='{{productName}}']/../..//input[contains(@class,'cart-item-qty')]" parameterized="true"/> + <element name="itemQuantityUpdate" type="button" selector="//a[text()='{{productName}}']/../..//span[text()='Update']" parameterized="true"/> </section> </sections> From d4b3346a19db0da3dc8c6ec89aaa1fd124bf9cc5 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Tue, 3 Jul 2018 16:45:41 -0500 Subject: [PATCH 0115/1171] MAGETWO-91528: Customizable options truncated when displaying ordered product in admin --- .../Catalog/Data/ProductData.xml | 4 + .../Catalog/Data/ProductOptionData.xml | 11 +- .../Catalog/Data/ProductOptionValueData.xml | 14 ++- ...ctWithCustomOptionsWithLongValuesTitle.xml | 103 ++++++++++++++++++ 4 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml index 3c6e7d3ddc319..2391a30aaefea 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml @@ -254,6 +254,10 @@ <requiredEntity type="product_option">ProductOptionDateTime</requiredEntity> <requiredEntity type="product_option">ProductOptionTime</requiredEntity> </entity> + <entity name="productWithOptions2" type="product"> + <var key="sku" entityType="product" entityKey="sku" /> + <requiredEntity type="product_option">ProductOptionDropDownWithLongValuesTitle</requiredEntity> + </entity> <entity name="ApiVirtualProductWithDescription" type="product"> <data key="sku" unique="suffix">api-virtual-product</data> <data key="type_id">virtual</data> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductOptionData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductOptionData.xml index 2abc557d44d96..53ce451de7f8f 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductOptionData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductOptionData.xml @@ -59,6 +59,15 @@ <requiredEntity type="product_option_value">ProductOptionValueDropdown1</requiredEntity> <requiredEntity type="product_option_value">ProductOptionValueDropdown2</requiredEntity> </entity> + <entity name="ProductOptionDropDownWithLongValuesTitle" type="product_option"> + <var key="product_sku" entityType="product" entityKey="sku" /> + <data key="title">OptionDropDownWithLongTitles</data> + <data key="type">drop_down</data> + <data key="sort_order">4</data> + <data key="is_require">true</data> + <requiredEntity type="product_option_value">ProductOptionValueDropdownLongTitle1</requiredEntity> + <requiredEntity type="product_option_value">ProductOptionValueDropdownLongTitle2</requiredEntity> + </entity> <entity name="ProductOptionRadiobutton" type="product_option"> <var key="product_sku" entityType="product" entityKey="sku" /> <data key="title">OptionRadioButtons</data> @@ -112,4 +121,4 @@ <data key="price">0.00</data> <data key="price_type">percent</data> </entity> -</entities> \ No newline at end of file +</entities> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductOptionValueData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductOptionValueData.xml index 615f6aaa705bf..cf2c96b8f6bb9 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductOptionValueData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductOptionValueData.xml @@ -50,4 +50,16 @@ <data key="price">2</data> <data key="price_type">fixed</data> </entity> -</entities> \ No newline at end of file + <entity name="ProductOptionValueDropdownLongTitle1" type="product_option_value"> + <data key="title">Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj11111Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj11111</data> + <data key="sort_order">1</data> + <data key="price">10</data> + <data key="price_type">fixed</data> + </entity> + <entity name="ProductOptionValueDropdownLongTitle2" type="product_option_value"> + <data key="title">Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj22222Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj22222</data> + <data key="sort_order">2</data> + <data key="price">20</data> + <data key="price_type">percent</data> + </entity> +</entities> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml new file mode 100644 index 0000000000000..10fdb396aa4fe --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle"> + <annotations> + <group value="Catalog"/> + <title value="Admin should be able to see the full title of the selected custom option value in the order"/> + <description value="Admin should be able to see the full title of the selected custom option value in the order"/> + <severity value="MAJOR"/> + <testCaseId value="MC-3043"/> + </annotations> + <before> + <!--Create Simple Product with Custom Options--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">17</field> + </createData> + <updateData createDataKey="createProduct" entity="productWithOptions2" stepKey="updateProductWithOptions"/> + + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Login Customer Storefront --> + + <amOnPage url="{{StorefrontCustomerSignInPage.url}}" stepKey="amOnSignInPage"/> + <fillField userInput="$$createCustomer.email$$" selector="{{StorefrontCustomerSignInFormSection.emailField}}" stepKey="fillEmail"/> + <fillField userInput="$$createCustomer.password$$" selector="{{StorefrontCustomerSignInFormSection.passwordField}}" stepKey="fillPassword"/> + <click selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="clickSignInAccountButton"/> + + <!-- Checking the correctness of displayed prices for user parameters --> + + <amOnPage url="{{StorefrontHomePage.url}}$$createProduct.custom_attributes[url_key]$$.html" stepKey="amOnProductPage"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productAttributeOptionsDropDown(ProductOptionDropDownWithLongValuesTitle.title, ProductOptionValueDropdownLongTitle1.price)}}" stepKey="checkDropDownProductOption"/> + + <!-- Adding items to the checkout --> + + <selectOption userInput="{{ProductOptionValueDropdownLongTitle1.price}}" selector="{{StorefrontProductInfoMainSection.productOptionSelect(ProductOptionDropDownWithLongValuesTitle.title)}}" stepKey="seeProductOptionDropDown"/> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="finalProductPrice"/> + + <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + + <!-- Checking the correctness of displayed custom options for user parameters on checkout --> + + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart" /> + + <conditionalClick selector="{{CheckoutPaymentSection.cartItemsArea}}" dependentSelector="{{CheckoutPaymentSection.cartItemsArea}}" visible="true" stepKey="exposeMiniCart"/> + + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskForCartItem"/> + <waitForElement selector="{{CheckoutPaymentSection.cartItemsAreaActive}}" time="30" stepKey="waitForCartItemsAreaActive"/> + + <see selector="{{CheckoutPaymentSection.cartItems}}" userInput="$$createProduct.name$$" stepKey="seeProductInCart"/> + + <conditionalClick selector="{{CheckoutPaymentSection.ProductOptionsByProductItemName($$createProduct.name$$)}}" dependentSelector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" visible="false" stepKey="exposeProductOptions"/> + + <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> + + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + + <!-- Place Order --> + + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + + <!-- Login to Admin and open Order --> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + + <!-- Checking the correctness of displayed custom options for user parameters on Order --> + + <dontSee selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="dontSeeAdminOrderProductOptionValueDropdown1"/> + <grabTextFrom selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd" stepKey="productOptionValueText"/> + <assertEquals stepKey="checkProductOptionValue"> + <actualResult type="variable">productOptionValueText</actualResult> + <expectedResult type="string">Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj111 ...</expectedResult> + </assertEquals> + <moveMouseOver selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd" stepKey="hoverProduct"/> + <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeAdminOrderProductOptionValueDropdown1"/> + </test> +</tests> From 6e1beda538910b35f0f904357f5f2af78d798ec9 Mon Sep 17 00:00:00 2001 From: Robert Paprocki <robert@cryptobells.com> Date: Mon, 5 Feb 2018 16:27:32 -0800 Subject: [PATCH 0116/1171] Use constant time string comparison in FormKey validator CSRF tokens should be considered sensitive strings. While the risk of a malicious actor attempting gleam the form key via a timing attack is very low, we should still follow best practices in verifying this token. --- .../Magento/Framework/Data/Form/FormKey/Validator.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Data/Form/FormKey/Validator.php b/lib/internal/Magento/Framework/Data/Form/FormKey/Validator.php index 0dbc9c879462e..99ae484977bfc 100644 --- a/lib/internal/Magento/Framework/Data/Form/FormKey/Validator.php +++ b/lib/internal/Magento/Framework/Data/Form/FormKey/Validator.php @@ -5,6 +5,8 @@ */ namespace Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Encryption\Helper\Security; + /** * @api */ @@ -32,9 +34,11 @@ public function __construct(\Magento\Framework\Data\Form\FormKey $formKey) public function validate(\Magento\Framework\App\RequestInterface $request) { $formKey = $request->getParam('form_key', null); - if (!$formKey || $formKey !== $this->_formKey->getFormKey()) { + + if (!$formKey) { return false; } - return true; + + return Security::compareStrings($formKey, $this->_formKey->getFormKey()); } } From 11a95d641009dbe57a66fc2284814e9a9920502f Mon Sep 17 00:00:00 2001 From: Vlad Veselov <orlangur@users.noreply.github.com> Date: Tue, 26 Jun 2018 11:24:12 +0300 Subject: [PATCH 0117/1171] Polish up implementation --- .../Magento/Framework/Data/Form/FormKey/Validator.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/Data/Form/FormKey/Validator.php b/lib/internal/Magento/Framework/Data/Form/FormKey/Validator.php index 99ae484977bfc..225ff1fd140a9 100644 --- a/lib/internal/Magento/Framework/Data/Form/FormKey/Validator.php +++ b/lib/internal/Magento/Framework/Data/Form/FormKey/Validator.php @@ -34,11 +34,7 @@ public function __construct(\Magento\Framework\Data\Form\FormKey $formKey) public function validate(\Magento\Framework\App\RequestInterface $request) { $formKey = $request->getParam('form_key', null); - - if (!$formKey) { - return false; - } - - return Security::compareStrings($formKey, $this->_formKey->getFormKey()); + + return $formKey && Security::compareStrings($formKey, $this->_formKey->getFormKey()); } } From 1acd06f76508ecf46f87a477aff691e504549fb6 Mon Sep 17 00:00:00 2001 From: mdykas <mdykas@divante.pl> Date: Fri, 18 May 2018 15:40:01 +0200 Subject: [PATCH 0118/1171] issue/14056 - Coupon API not working for guest user --- app/code/Magento/Quote/Model/CouponManagement.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Quote/Model/CouponManagement.php b/app/code/Magento/Quote/Model/CouponManagement.php index 62515a17f268b..55c21c974d6dd 100644 --- a/app/code/Magento/Quote/Model/CouponManagement.php +++ b/app/code/Magento/Quote/Model/CouponManagement.php @@ -55,6 +55,9 @@ public function set($cartId, $couponCode) if (!$quote->getItemsCount()) { throw new NoSuchEntityException(__('The "%1" Cart doesn\'t contain products.', $cartId)); } + if (!$quote->getStoreId()) { + throw new NoSuchEntityException(__('Cart isn\'t assigned to correct store')); + } $quote->getShippingAddress()->setCollectShippingRates(true); try { From 2651be335ea19d68cdf0989694a35c70e44de45f Mon Sep 17 00:00:00 2001 From: mdykas <mdykas@divante.pl> Date: Thu, 24 May 2018 14:49:35 +0200 Subject: [PATCH 0119/1171] issue/14056 - Coupon API not working for guest user - adjusted unit tests --- .../Quote/Test/Unit/Model/CouponManagementTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/code/Magento/Quote/Test/Unit/Model/CouponManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/CouponManagementTest.php index e6ba50e35b4c3..91211904c11eb 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/CouponManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/CouponManagementTest.php @@ -47,6 +47,7 @@ protected function setUp() 'save', 'getShippingAddress', 'getCouponCode', + 'getStoreId', '__wakeup' ]); $this->quoteAddressMock = $this->createPartialMock(\Magento\Quote\Model\Quote\Address::class, [ @@ -98,6 +99,9 @@ public function testSetWhenCouldNotApplyCoupon() $cartId = 33; $couponCode = '153a-ABC'; + $this->storeMock->expects($this->any())->method('getId')->will($this->returnValue(1)); + $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($this->returnValue(1)); + $this->quoteRepositoryMock->expects($this->once()) ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); $this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(12)); @@ -125,6 +129,9 @@ public function testSetWhenCouponCodeIsInvalid() $cartId = 33; $couponCode = '153a-ABC'; + $this->storeMock->expects($this->any())->method('getId')->will($this->returnValue(1)); + $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($this->returnValue(1)); + $this->quoteRepositoryMock->expects($this->once()) ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); $this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(12)); @@ -144,6 +151,9 @@ public function testSet() $cartId = 33; $couponCode = '153a-ABC'; + $this->storeMock->expects($this->any())->method('getId')->will($this->returnValue(1)); + $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($this->returnValue(1)); + $this->quoteRepositoryMock->expects($this->once()) ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); $this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(12)); From be413cd7027debb7cc63f938292eb7ad1e2efe10 Mon Sep 17 00:00:00 2001 From: John Stennett <john00ivy@gmail.com> Date: Thu, 5 Jul 2018 11:09:44 -0500 Subject: [PATCH 0120/1171] MQE-1267: MSI MFTF Test Cases 4 - Adding necessary Action Groups, Sections and Data to the Bundle, GroupedProduct, Sales and Store modules. (cherry picked from commit 9f1f998) --- .../StorefrontProductCartActionGroup.xml | 27 ++++++ .../StorefrontProductActionSection.xml | 15 ++++ .../AdminGroupedProductActionGroup.xml | 9 ++ .../AdminAddProductsToGroupPanelSection.xml | 4 + .../ActionGroup/AdminOrderActionGroup.xml | 88 ++++++++++++++++--- .../AdminOrderCustomersGridSection.xml | 18 ++++ .../AdminOrderFormBundleProductSection.xml | 15 ++++ ...minOrderFormDownloadableProductSection.xml | 16 ++++ .../AdminOrderFormGroupedProductSection.xml | 15 ++++ .../AdminCreateStoreGroupActionGroup.xml | 26 ++++++ .../DeleteCustomWebsiteActionGroup.xml | 27 ++++++ .../Store/Test/Mftf}/Data/StoreGroupData.xml | 9 ++ .../Store/Test/Mftf/Data/WebsiteData.xml | 22 +++++ .../AdminNewStoreGroupActionsSection.xml | 15 ++++ .../AdminStoresDeleteWebsiteSection.xml | 13 +++ 15 files changed, 309 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductActionSection.xml rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct => app/code/Magento/GroupedProduct/Test/Mftf}/ActionGroup/AdminGroupedProductActionGroup.xml (87%) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct => app/code/Magento/GroupedProduct/Test/Mftf}/Section/AdminAddProductsToGroupPanelSection.xml (82%) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Sales => app/code/Magento/Sales/Test/Mftf}/ActionGroup/AdminOrderActionGroup.xml (66%) create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCustomersGridSection.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBundleProductSection.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormDownloadableProductSection.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormGroupedProductSection.xml create mode 100644 app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml create mode 100644 app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store => app/code/Magento/Store/Test/Mftf}/Data/StoreGroupData.xml (69%) create mode 100644 app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml create mode 100644 app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreGroupActionsSection.xml create mode 100644 app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml new file mode 100644 index 0000000000000..52846152eacd1 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <!-- Add Bundle Product to Cart from the category page with specified quantity to cart --> + <actionGroup name="StorefrontAddCategoryBundleProductToCartActionGroup"> + <arguments> + <argument name="product"/> + <argument name="quantity" defaultValue="1" type="string"/> + </arguments> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="clickAddToCart" /> + <waitForElementVisible selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="waitForBundleProductPageLoad"/> + <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <waitForElementVisible selector="{{StorefrontBundleProductActionSection.quantityField}}" stepKey="waitForQuantityVisible"/> + <fillField selector="{{StorefrontBundleProductActionSection.quantityField}}" userInput="{{quantity}}" stepKey="fillBundleProductQuantity"/> + <click selector="{{StorefrontBundleProductActionSection.addToCartButton}}" stepKey="clickAddBundleProductToCart"/> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart(product.name)}}" time="30" stepKey="assertMessage"/> + <waitForText userInput="{{quantity}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductActionSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductActionSection.xml new file mode 100644 index 0000000000000..abc9bc6dab540 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductActionSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="StorefrontBundleProductActionSection"> + <element name="customizeAndAddToCartButton" type="button" selector="#bundle-slide"/> + <element name="quantityField" type="input" selector="#qty"/> + <element name="addToCartButton" type="button" selector="#product-addtocart-button"/> + </section> +</sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/ActionGroup/AdminGroupedProductActionGroup.xml b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml similarity index 87% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/ActionGroup/AdminGroupedProductActionGroup.xml rename to app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml index 85762fd45e16d..79550b47820b9 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/ActionGroup/AdminGroupedProductActionGroup.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml @@ -43,4 +43,13 @@ <see selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{product.name}}" stepKey="seeProductNameInGrid"/> <click selector="{{AdminProductGridFilterSection.clearFilters}}" stepKey="clickClearFiltersAfter"/> </actionGroup> + + <!--Fill product min quantity in group products grid--> + <actionGroup name="fillDefaultQuantityForLinkedToGroupProductInGrid"> + <arguments> + <argument name="productName" type="string"/> + <argument name="qty" type="string"/> + </arguments> + <fillField selector="{{AdminAddedProductsToGroupGrid.inputByProductName(productName)}}" userInput="{{qty}}" stepKey="fillDefaultQtyForLinkedProduct"/> + </actionGroup> </actionGroups> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Section/AdminAddProductsToGroupPanelSection.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml similarity index 82% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Section/AdminAddProductsToGroupPanelSection.xml rename to app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml index 45e32e7f2cd04..5c23ad4e9afb8 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Section/AdminAddProductsToGroupPanelSection.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml @@ -16,4 +16,8 @@ <element name="firstCheckbox" type="input" selector="tr[data-repeat-index='0'] .admin__control-checkbox"/> <element name="nThCheckbox" type="input" selector="tr[data-repeat-index='{{n}}'] .admin__control-checkbox" parameterized="true"/> </section> + + <section name="AdminAddedProductsToGroupGrid"> + <element name="inputByProductName" type="input" selector="//div[@data-index='grouped']//table//tr[td[@data-index='name']//span[text()='{{productName}}']]//td[@data-index='qty']//input" parameterized="true"/> + </section> </sections> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Sales/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml similarity index 66% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Sales/ActionGroup/AdminOrderActionGroup.xml rename to app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index f041ac97e5cac..c8426165861f0 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Sales/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -22,6 +22,24 @@ <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> </actionGroup> + <!--Navigate to create order page (New Order -> Select Customer)--> + <actionGroup name="navigateToNewOrderPageExistingCustomer"> + <arguments> + <argument name="customer"/> + </arguments> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> + <waitForPageLoad stepKey="waitForIndexPageLoad"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> + <click selector="{{AdminOrdersGridSection.createNewOrder}}" stepKey="clickCreateNewOrder"/> + <waitForPageLoad stepKey="waitForCustomerGridLoad"/> + <fillField userInput="{{customer.email}}" selector="{{AdminOrderCustomersGridSection.emailInput}}" stepKey="filterEmail"/> + <click selector="{{AdminOrderCustomersGridSection.apply}}" stepKey="applyFilter"/> + <waitForPageLoad stepKey="waitForFilteredCustomerGridLoad"/> + <click selector="{{AdminOrderCustomersGridSection.firstRow}}" stepKey="clickOnCustomer"/> + <waitForPageLoad stepKey="waitForCreateOrderPageLoad" /> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> + </actionGroup> + <!--Check the required fields are actually required--> <actionGroup name="checkRequiredFieldsNewOrderForm"> <seeElement selector="{{AdminOrderFormAccountSection.requiredGroup}}" stepKey="seeCustomerGroupRequired"/> @@ -84,6 +102,66 @@ <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> </actionGroup> + <!--Add bundle product to order --> + <actionGroup name="addBundleProductToOrder"> + <arguments> + <argument name="product"/> + <argument name="quantity" type="string" defaultValue="1"/> + </arguments> + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> + <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilterBundle"/> + <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearchBundle"/> + <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> + <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectBundleProduct"/> + <waitForElementVisible selector="{{AdminOrderFormBundleProductSection.quantity}}" stepKey="waitForBundleOptionLoad"/> + <wait time="2" stepKey="waitForOptionsToLoad"/> + <fillField selector="{{AdminOrderFormBundleProductSection.quantity}}" userInput="{{quantity}}" stepKey="fillQuantity"/> + <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOk"/> + <scrollTo selector="{{AdminOrderFormItemsSection.addSelected}}" x="0" y="-100" stepKey="scrollToAddSelectedButton"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> + </actionGroup> + + <!--Add downloadable product to order --> + <actionGroup name="addDownloadableProductToOrder"> + <arguments> + <argument name="product"/> + <argument name="link"/> + <argument name="quantity" defaultValue="1" type="string"/> + </arguments> + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> + <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilterDownloadable"/> + <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearchDownloadable"/> + <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> + <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectDownloadableProduct"/> + <waitForElementVisible selector="{{AdminOrderFormDownloadableProductSection.optionSelect(link.title)}}" stepKey="waitForLinkLoad"/> + <click selector="{{AdminOrderFormDownloadableProductSection.optionSelect(link.title)}}" stepKey="selectLink"/> + <fillField selector="{{AdminOrderFormDownloadableProductSection.quantity}}" userInput="{{quantity}}" stepKey="setQuantity"/> + <click selector="{{AdminOrderFormDownloadableProductSection.ok}}" stepKey="confirmConfiguration"/> + <scrollTo selector="{{AdminOrderFormItemsSection.addSelected}}" x="0" y="-100" stepKey="scrollToAddSelectedButton"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> + </actionGroup> + + <!--Add grouped product option to order --> + <actionGroup name="addGroupedProductOptionToOrder"> + <arguments> + <argument name="product"/> + <argument name="option"/> + <argument name="quantity" type="string" defaultValue="1"/> + </arguments> + + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> + <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilterGrouped"/> + <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearchGrouped"/> + <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> + <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectGroupedProduct"/> + <waitForElementVisible selector="{{AdminOrderFormGroupedProductSection.optionQty(option.sku)}}" stepKey="waitForGroupedOptionLoad"/> + <wait time="2" stepKey="waitForOptionsToLoad"/> + <fillField selector="{{AdminOrderFormGroupedProductSection.optionQty(option.sku)}}" userInput="{{quantity}}" stepKey="fillOptionQuantity"/> + <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOk"/> + <scrollTo selector="{{AdminOrderFormItemsSection.addSelected}}" x="0" y="-100" stepKey="scrollToAddSelectedButton"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> + </actionGroup> + <!--Fill customer billing address--> <actionGroup name="fillOrderCustomerInformation"> <arguments> @@ -137,14 +215,4 @@ </arguments> <see selector="{{AdminOrderItemsOrderedSection.productSkuColumn}}" userInput="{{product.sku}}" stepKey="seeSkuInItemsOrdered"/> </actionGroup> - - <!--Cancel order that is in pending status--> - <actionGroup name="cancelPendingOrder"> - <click selector="{{AdminOrderDetailsMainActionsSection.cancel}}" stepKey="clickCancelOrder"/> - <waitForElement selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForCancelConfirmation"/> - <see selector="{{AdminConfirmationModalSection.message}}" userInput="Are you sure you want to cancel this order?" stepKey="seeConfirmationMessage"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmOrderCancel"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You canceled the order." stepKey="seeCancelSuccessMessage"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Canceled" stepKey="seeOrderStatusCanceled"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCustomersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCustomersGridSection.xml new file mode 100644 index 0000000000000..c91a1e2aef693 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCustomersGridSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminOrderCustomersGridSection"> + <element name="spinner" type="button" selector=".spinner"/> + <element name="apply" type="button" selector=".action-secondary[title='Search']"/> + <element name="resetFilter" type="button" selector=".action-tertiary[title='Reset Filter']"/> + <element name="emailInput" type="input" selector="#sales_order_create_customer_grid_filter_email"/> + <element name="firstRow" type="button" selector="tr:nth-of-type(1)[data-role='row']"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBundleProductSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBundleProductSection.xml new file mode 100644 index 0000000000000..a035e47394d5b --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBundleProductSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminOrderFormBundleProductSection"> + <element name="quantity" type="input" selector="#product_composite_configure_input_qty"/> + <element name="ok" type="button" selector=".modal-header .page-actions button[data-role='action']" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormDownloadableProductSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormDownloadableProductSection.xml new file mode 100644 index 0000000000000..b77b01d54f950 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormDownloadableProductSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminOrderFormDownloadableProductSection"> + <element name="optionSelect" type="select" selector="//div[contains(@class,'link')]/div/div/input[./../label[contains(text(),{{linkTitle}})]]" parameterized="true"/> + <element name="quantity" type="input" selector="#product_composite_configure_input_qty"/> + <element name="ok" type="button" selector=".modal-header .page-actions button[data-role='action']" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormGroupedProductSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormGroupedProductSection.xml new file mode 100644 index 0000000000000..ceba11f74ae83 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormGroupedProductSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminOrderFormGroupedProductSection"> + <element name="optionQty" type="input" selector="//td[@class='col-sku'][text()='{{productSku}}']/..//input[contains(@class, 'qty')]" parameterized="true"/> + <element name="ok" type="button" selector=".modal-header .page-actions button[data-role='action']" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml new file mode 100644 index 0000000000000..0819a74ea8996 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateStoreGroupActionGroup"> + <arguments> + <argument name="Website" defaultValue="_defaultWebsite"/> + </arguments> + <amOnPage url="{{AdminSystemStoreGroupPage.url}}" stepKey="navigateToNewStoreGroup"/> + <waitForPageLoad stepKey="waitForStoreGroupPageLoad" /> + + <comment userInput="Creating Store Group" stepKey="storeGroupCreationComment" /> + <selectOption selector="{{AdminNewStoreGroupSection.storeGrpWebsiteDropdown}}" userInput="{{Website.name}}" stepKey="selectWebsite" /> + <fillField selector="{{AdminNewStoreGroupSection.storeGrpNameTextField}}" userInput="{{CustomStoreGroupCustomWebsite.name}}" stepKey="enterStoreGroupName" /> + <fillField selector="{{AdminNewStoreGroupSection.storeGrpCodeTextField}}" userInput="{{CustomStoreGroupCustomWebsite.code}}" stepKey="enterStoreGroupCode" /> + <selectOption selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" userInput="Default Category" stepKey="setRootCategory" /> + <click selector="{{AdminNewStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreGroup" /> + <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReload"/> + <see userInput="You saved the store." stepKey="seeSavedMessage" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml new file mode 100644 index 0000000000000..0f8673eb2f4aa --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteCustomWebsiteActionGroup"> + <arguments> + <argument name="websiteName" defaultValue="customWebsite.name"/> + </arguments> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnTheStorePage"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="clickOnResetButton"/> + <waitForPageLoad stepKey="waitForPageLoadAfterResetButtonClicked" time="10"/> + <fillField userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="fillSearchWebsiteField"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton" /> + <waitForPageLoad stepKey="waitForPageLoadAfterSearch" time="10"/> + <see userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="verifyThatCorrectWebsiteFound"/> + <click selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="clickEditExistingWebsite"/> + + <click selector="{{AdminStoresMainActionsSection.deleteButton}}" stepKey="clickDeleteWebsiteButtonOnEditStorePage"/> + <selectOption userInput="No" selector="{{AdminStoresDeleteWebsiteSection.createDbBackup}}" stepKey="setCreateDbBackupToNo"/> + <click selector="{{AdminStoresDeleteWebsiteSection.deleteButton}}" stepKey="clickDeleteButtonOnDeleteWebsitePage"/> + </actionGroup> +</actionGroups> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Data/StoreGroupData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml similarity index 69% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Data/StoreGroupData.xml rename to app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml index 891d307808760..a42c6a20df0d1 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Data/StoreGroupData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml @@ -21,4 +21,13 @@ <data key="store_action">add</data> <data key="store_type">group</data> </entity> + <entity name="CustomStoreGroupCustomWebsite" type="group"> + <data key="group_id">null</data> + <data key="name" unique="suffix">Store Group Custom Website</data> + <data key="code" unique="suffix">store_group_custom_website</data> + <data key="root_category_id">2</data> + <data key="store_action">add</data> + <data key="store_type">group</data> + <requiredEntity type="website">customWebsite</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml new file mode 100644 index 0000000000000..e8528fba1ae29 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="_defaultWebsite" type="website"> + <data key="name">Main Website</data> + <data key="code">base</data> + <data key="sort_order">0</data> + </entity> + <entity name="customWebsite" type="website"> + <data key="name" unique="suffix">Second Website</data> + <data key="code" unique="suffix">second_website</data> + <data key="sort_order">10</data> + <data key="store_action">add</data> + <data key="store_type">website</data> + <data key="website_id">null</data> + </entity> +</entities> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreGroupActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreGroupActionsSection.xml new file mode 100644 index 0000000000000..f026c7765b6d6 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreGroupActionsSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminNewStoreGroupActionsSection"> + <element name="backButton" type="button" selector="#back" timeout="30"/> + <element name="delete" type="button" selector="#delete" timeout="30"/> + <element name="resetButton" type="button" selector="#reset" timeout="30"/> + <element name="saveButton" type="button" selector="#save" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml new file mode 100644 index 0000000000000..50c536dcfe809 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminStoresDeleteWebsiteSection"> + <element name="createDbBackup" type="select" selector="#store_create_backup"/> + <element name="deleteButton" type="button" selector="#delete" timeout="30"/> + </section> +</sections> From 4f1925f6788a03dd30280534ab5ec435feff26b4 Mon Sep 17 00:00:00 2001 From: John Stennett <john00ivy@gmail.com> Date: Thu, 5 Jul 2018 11:23:30 -0500 Subject: [PATCH 0121/1171] MQE-1267: MSI MFTF Test Cases 4 - Adding missing Action Group. (cherry picked from commit b36a5f9) --- .../Test/Mftf/ActionGroup/AdminOrderActionGroup.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index c8426165861f0..c27c4d2292130 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -215,4 +215,14 @@ </arguments> <see selector="{{AdminOrderItemsOrderedSection.productSkuColumn}}" userInput="{{product.sku}}" stepKey="seeSkuInItemsOrdered"/> </actionGroup> + + <!--Cancel order that is in pending status--> + <actionGroup name="cancelPendingOrder"> + <click selector="{{AdminOrderDetailsMainActionsSection.cancel}}" stepKey="clickCancelOrder"/> + <waitForElement selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForCancelConfirmation"/> + <see selector="{{AdminConfirmationModalSection.message}}" userInput="Are you sure you want to cancel this order?" stepKey="seeConfirmationMessage"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmOrderCancel"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You canceled the order." stepKey="seeCancelSuccessMessage"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Canceled" stepKey="seeOrderStatusCanceled"/> + </actionGroup> </actionGroups> From f17785c12cd34b065f2c9f1e6506ef941d9a3392 Mon Sep 17 00:00:00 2001 From: John Stennett <john00ivy@gmail.com> Date: Thu, 5 Jul 2018 11:39:46 -0500 Subject: [PATCH 0122/1171] MQE-1121: MSI MFTF Test Cases 4 Updates - Adding "msi-suite.xml" file. --- .../acceptance/tests/_suite/msi-suite.xml | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 dev/tests/acceptance/tests/_suite/msi-suite.xml diff --git a/dev/tests/acceptance/tests/_suite/msi-suite.xml b/dev/tests/acceptance/tests/_suite/msi-suite.xml new file mode 100644 index 0000000000000..b96451d58871c --- /dev/null +++ b/dev/tests/acceptance/tests/_suite/msi-suite.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> + <suite name="MSI_Single_Mode"> + <include> + <group name="single_mode"/> + </include> + <exclude> + <group name="skip"/> + <group name="multi_mode"/> + </exclude> + </suite> + <suite name="MSI_Multi_Mode"> + <include> + <group name="multi_mode"/> + </include> + <exclude> + <group name="skip"/> + <group name="single_mode"/> + </exclude> + </suite> +</suites> From 92f83a89225032d28ec3b7a6dd3bec72a8188289 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Thu, 5 Jul 2018 13:35:53 -0500 Subject: [PATCH 0123/1171] MAGETWO-91528: Customizable options truncated when displaying ordered product in admin --- ...aseProductWithCustomOptionsWithLongValuesTitle.xml | 1 + dev/tests/functional/.htaccess | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 dev/tests/functional/.htaccess diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index 10fdb396aa4fe..741044a3d7799 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -98,6 +98,7 @@ <expectedResult type="string">Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj111 ...</expectedResult> </assertEquals> <moveMouseOver selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd" stepKey="hoverProduct"/> + <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd:nth-child(2)" stepKey=""/> <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeAdminOrderProductOptionValueDropdown1"/> </test> </tests> diff --git a/dev/tests/functional/.htaccess b/dev/tests/functional/.htaccess new file mode 100644 index 0000000000000..67c2f3fe2d027 --- /dev/null +++ b/dev/tests/functional/.htaccess @@ -0,0 +1,11 @@ +############################################## +## Allow access to command.php, website.php, export.php, pathChecker.php, locales.php, deleteMagentoGeneratedCode.php and log.php + <FilesMatch "command.php|website.php|export.php|pathChecker.php|deleteMagentoGeneratedCode.php|log.php|locales.php"> + <IfVersion < 2.4> + order allow,deny + allow from all + </IfVersion> + <IfVersion >= 2.4> + Require all granted + </IfVersion> + </FilesMatch> From fa366a5e9006f8bb27232103aff1653f10fdd3ec Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Thu, 5 Jul 2018 13:39:39 -0500 Subject: [PATCH 0124/1171] MAGETWO-91528: Customizable options truncated when displaying ordered product in admin --- dev/tests/functional/.htaccess | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 dev/tests/functional/.htaccess diff --git a/dev/tests/functional/.htaccess b/dev/tests/functional/.htaccess deleted file mode 100644 index 67c2f3fe2d027..0000000000000 --- a/dev/tests/functional/.htaccess +++ /dev/null @@ -1,11 +0,0 @@ -############################################## -## Allow access to command.php, website.php, export.php, pathChecker.php, locales.php, deleteMagentoGeneratedCode.php and log.php - <FilesMatch "command.php|website.php|export.php|pathChecker.php|deleteMagentoGeneratedCode.php|log.php|locales.php"> - <IfVersion < 2.4> - order allow,deny - allow from all - </IfVersion> - <IfVersion >= 2.4> - Require all granted - </IfVersion> - </FilesMatch> From a09535641433afc08ece9b12ef5b55b15e50bff1 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Thu, 5 Jul 2018 13:45:19 -0500 Subject: [PATCH 0125/1171] MAGETWO-91528: Customizable options truncated when displaying ordered product in admin --- ...frontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index 741044a3d7799..378ff26a7e785 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -98,7 +98,7 @@ <expectedResult type="string">Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj111 ...</expectedResult> </assertEquals> <moveMouseOver selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd" stepKey="hoverProduct"/> - <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd:nth-child(2)" stepKey=""/> + <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd:nth-child(2)" stepKey="waitForCustomOptionValueFullName"/> <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeAdminOrderProductOptionValueDropdown1"/> </test> </tests> From c20f7d57c15caad10a527ea511748f5e42cc56af Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 5 Jul 2018 15:45:39 -0500 Subject: [PATCH 0126/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - fix section --- .../Wishlist/CustomerData/Wishlist.php | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Wishlist/CustomerData/Wishlist.php b/app/code/Magento/Wishlist/CustomerData/Wishlist.php index eeae07861e22d..c04910a74d81d 100644 --- a/app/code/Magento/Wishlist/CustomerData/Wishlist.php +++ b/app/code/Magento/Wishlist/CustomerData/Wishlist.php @@ -7,6 +7,8 @@ use Magento\Catalog\Model\Product\Image\NotLoadInfoImageException; use Magento\Customer\CustomerData\SectionSourceInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Image; /** * Wishlist section @@ -38,22 +40,30 @@ class Wishlist implements SectionSourceInterface */ protected $block; + /** + * @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Image + */ + private $image; + /** * @param \Magento\Wishlist\Helper\Data $wishlistHelper * @param \Magento\Wishlist\Block\Customer\Sidebar $block * @param \Magento\Catalog\Helper\ImageFactory $imageHelperFactory * @param \Magento\Framework\App\ViewInterface $view + * @param Image|null $image */ public function __construct( \Magento\Wishlist\Helper\Data $wishlistHelper, \Magento\Wishlist\Block\Customer\Sidebar $block, \Magento\Catalog\Helper\ImageFactory $imageHelperFactory, - \Magento\Framework\App\ViewInterface $view + \Magento\Framework\App\ViewInterface $view, + Image $image = null ) { $this->wishlistHelper = $wishlistHelper; $this->imageHelperFactory = $imageHelperFactory; $this->block = $block; $this->view = $view; + $this->image = $image ?? ObjectManager::getInstance()->get(Image::class); } /** @@ -122,7 +132,7 @@ protected function getItemData(\Magento\Wishlist\Model\Item $wishlistItem) { $product = $wishlistItem->getProduct(); return [ - 'image' => $this->getImageData($product), + 'image' => $this->getImageData($this->image->getProductForThumbnail($wishlistItem)), 'product_sku' => $product->getSku(), 'product_id' => $product->getId(), 'product_url' => $this->wishlistHelper->getProductUrl($wishlistItem), @@ -149,14 +159,6 @@ protected function getItemData(\Magento\Wishlist\Model\Item $wishlistItem) */ protected function getImageData($product) { - /*Set variant product if it is configurable product. - It will show variant product image in sidebar instead of configurable product image.*/ - $simpleOption = $product->getCustomOption('simple_product'); - if ($simpleOption !== null) { - $optionProduct = $simpleOption->getProduct(); - $product = $optionProduct; - } - /** @var \Magento\Catalog\Helper\Image $helper */ $helper = $this->imageHelperFactory->create() ->init($product, 'wishlist_sidebar_block'); From 3b3d6a4f289273c42acc34076a651efaffeb2310 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Tue, 3 Jul 2018 13:10:48 -0500 Subject: [PATCH 0127/1171] MAGETWO-77744: [Magento Cloud] - Error message when uploading unsupported file format - Functional Test --- .../acceptance/tests/_data/lorem_ipsum.docx | Bin 0 -> 13589 bytes .../acceptance/tests/_data/lorem_ipsum.txt | 9 ++ .../ActionGroup/AdminProductActionGroup.xml | 2 +- .../ActionGroup/CustomOptionsActionGroup.xml | 21 +++- ...AdminProductCustomizableOptionsSection.xml | 8 +- .../StorefrontCategoryProductSection.xml | 1 + .../Section/StorefrontProductPageSection.xml | 4 +- ...ductWithCustomOptionsSecondWebsiteTest.xml | 6 +- .../SimpleProductTwoCustomOptionsTest.xml | 2 +- ...roductCustomOptionsDifferentStoreViews.xml | 6 +- .../Section/AdminDataGridHeaderSection.xml | 14 +++ ...reateProductConfigurationsPanelSection.xml | 1 + .../Section/AdminNewAttributePanelSection.xml | 1 + .../AdminProductFormConfigurationsSection.xml | 1 + ...gurableProductWithFileCustomOptionTest.xml | 77 +++++++++++++++ .../AddSwatchToProductActionGroup.xml | 65 +++++++++++++ .../Swatches/Data/SwatchAttributeData.xml | 15 +++ .../Swatches/Data/SwatchOptionData.xml | 20 ++++ .../Section/AdminNewAttributePanelSection.xml | 17 ++++ .../StorefrontProductInfoMainSection.xml | 15 +++ ...tSwatchProductWithFileCustomOptionTest.xml | 92 ++++++++++++++++++ .../Ui/Section/AdminDataGridTableSection.xml | 1 + 22 files changed, 363 insertions(+), 15 deletions(-) create mode 100644 dev/tests/acceptance/tests/_data/lorem_ipsum.docx create mode 100644 dev/tests/acceptance/tests/_data/lorem_ipsum.txt create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/AdminDataGridHeaderSection.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/ActionGroup/AddSwatchToProductActionGroup.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Data/SwatchAttributeData.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Data/SwatchOptionData.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Section/AdminNewAttributePanelSection.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Section/StorefrontProductInfoMainSection.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml diff --git a/dev/tests/acceptance/tests/_data/lorem_ipsum.docx b/dev/tests/acceptance/tests/_data/lorem_ipsum.docx new file mode 100644 index 0000000000000000000000000000000000000000..488f64e86b6ff42d2eaa492cc1cd79220be90350 GIT binary patch literal 13589 zcmeHuWqVyYvh^`DGcz+Y#4$57Gcz+Yb8N@V%*>9NnJG?;aU65Z_jJ$n+ufOd?;p6M z4|;sGwQ85-Dyg(eTTuoK9321&fCc~nB!J!G1zT+p0Du?*06+skgX+9@uy-}HcQsJ; zax`<%WAwDMB`yR9rOpR{0{j2B{2!iy=G001L1tvJ`?N=-q&79<<HAZ>&=|pV2IUhd zY#&hdm*mmz*IqPGB{h&FSX)vGw)-76^^uVIjVxOjlok()Gc1AhpcH*8F1q#AFAM>u zIF$RT%scPKIRkCkyL)hCIYF4oT85IRRbUlzigH3QG=Bo(Y10za$-U#)2t%JS+4k;x zcEB=rC>&`NTA=U{_485TQW{q|{Ol!3u}#-q7Bwx>WJ3*`(W6d%PnPmheKQmjoi{^V z2!6!lVhd#|YQ#f*b0u<RSE3smAuaVVs)(ucRn_(Cs4u3M-^Aa$Q7fTne%xRXWdcc> z*~%)c(Ja``eWQjdjRWcC7dzM_K%z<7!v*ji$%kUd3e|~UPIjXe7j8vm;<odP+ib%m zS%MXK-OV1ZZRA83&p6$HSQQ-zjqKFf*34M#0a#8J1N5>bS1e|sZte1g3C0dLpX`2u z0RV4rU;xFxxg=o{ZrcUW&g6j}2M2UX17|Z^7beEv>i@dp|6%|8%h0Qn2dsyf;e{_k zUP9)3lr{#ii{zP1<~Fg{p`dl7WYIR)tXE%O_}A7z^-hc=CKnb{=Db~U#NGB%^?u-{ zt06@9z^pvz59vI0djitJx=UC*R~$dXXCK~~zf6!x(M*KIXkkRp;zB0<jLe+wNBc@E zdiS*q&We;~cFBaisVLV;ZS_9WpDq6b%kqk~q>c|%rGQv(I6>zOjz6rqP(ubALt?_< zPNUu@)zg%A);MdLTY6kuW+sZ9B;#sW+#lWZr`|<<Wy4Wn^e_c5QA|(1=DMibuSd~` zI6|BkBwEb|`wn<Hdhf1*8`FRK$BJo_(hYDjSb*Mx4}b>oaBwzd`j?ZKI+(cG0e7$8 zwyi%L1_Zcw0p<MPF4f7CK!X9U-C?gGnSKm>eu^SrY^6yyS6%>OghJ)?Fw-~teRYyj zXLE*~Kb&hbU1kG0H)jW;Jz|%9%JRpYk&k2Rd{${aD+uepzeBdsusI{3#ZEHR(bu=l zowJsgS-K4pE#y|7V~J=<HRZ);Hxta4VTwx#Ib<JEhqTjoi*Zgn@?e$6QOso?u2o0& zxaEknhW|;_>X5aw7sBNL#w(~oBINiMJ9WY>tx2F`3X#GrBbs<5rY;jbM-*eiTBb-} z*<UnV(QlMn)u-2(yU6(3N;Gm&pXt)BwNP*wPuJ6imXLQ$O`;&^{QwMQ{?lrb$w=NW z!T<obmH+?>&=`KVn!no1H@#g4JRU@Up}kkAf!W%R+(H`&oO;D_I_PWLlXmtOgh*Gh zwl-BbaujhXI|cznG*%;4t#+rq;5rau2lJBXQb|NoEP(;fC#@{TRXMAz0(tl|h?F8N zX*F(Lf_cxcb*l=wa-m3#H6;x-ym`Kg{#T3FeQ)QZ*-abjenlI^!iBdMTzYPT>!EWW zr;mC!%EceGis&qQ^un~vvODx@TuTnQR+g`a2lhb%PIGEf@7FF$Y}*zTP&-cVjFzma z9O^Tn31_R~T;FZSvm^Fpjg3D?JTyIpV)`HlhsF#&7035xEuG}FcS;<4GKuV+5|*UA zP`^YumOUwHZ<XD+3Q0@c(+F%PEC{$*f5&S-SB=S_JHy5KalCRZ-s$0Y4<5>gbEAw$ zAQ*T!U`0vh_GFI~UvHg1hretfw|~;z(84O=R%$#;)nhZe@JKb}ZTv!CtI&4^dyo8Z zkUR;FNdK@!1E#mO;^4~V>Dk1@(Y<9#x;YEeU?-dFu}B~|rzZ%ZkfD@DHV{#tce9&0 z_HaZ)NXWk@8u_XIGYS3HHk->rKkR7Sz4COtGZGOr@rVA;gh7kaLUYAzapiJjA!&TB z-H*JiR3BpB=3;C87i;>awRgBTJ;*p-?3Q-odjsAp{iL&`sM@>*f3us?bufjJw5)7u zJ6qxHVBdr0BsYW%RtS6@t$=p!9Cs;(mC*^v-~HIm^S)B)&0Ua(yWQ8#)%nsnIf8Ln z`i^odz@>%llV~meTPGqBd@Jv!w2R<F8vV|*1Ot_e_X(GqzX}co!F*+PgD~?tluQDh z-H$ttw_>r6)x<<Q3>f^-$^>(temVx3S&86gROJghG;rhO3TAy$eJf|Bl7ZX$%z`E6 zSZ9aF&v2I(9;M68fPk{8+w6bearF?C(M0~?6y%K@<~2hCE5hpwg<U-xr^4nel-$T0 zA&3l@#s0(WoEsT3<)iqL?9v&rY0;pnY9w(bEbWc_n6}5pwHHn3+J~Pl4s@oCHP$HB z-R0g#K1}G}ylGVxY*u?v<*&<?yh}E;Z#3KQ<FVFcTSahRdNtm$YcFYYs*t?bz3&~R za-(xyq|vaERT8+ginaaJ%B4qSsyL`);#%@Pp;!mI2YgZmrQedcGq_A+B`7?R;m|`K z0zrRSJI>g_bs3LG?&|iPEpe_2VzTC{beZr1?=#v&h#HQh1T?2+<MKP&!f^gwxRXkK za5MO^?h6)}AvUKP>v`n7XZmhy$jj}KR*ZA2ewUIoEPZ$F78MY1=*kn#-MyhT3T^;| z*7PR~cVS_TT+z^0Yq?7T<(iLMr`plA9-7om%~B3)9r8CR$ci2YNxI^wSf5x$8%vQS z`@j(!?xm!pEP2VyBgrMc9uz}&Lg?O7n`xiN0T9)^rECw{8Ks~aqa6zMA?L^2LxcQo z*3f<w`7#ag#-o(&XqV@|hpYf?H6$AtDX;!k@2|SfT$j{iQb4=^xR;NtzoNBES7xZK zxGnzZDOgXf%p4lw=iUqtbzvu$Q(W-jXGB%=nsiwMpw>ugpMVc0MslRyUW>^P?1AyF ztgyDhl~;xS5+l2Bkz*Zug<TQk!8S4%ub{Z!C+HrkpATew;$2Wy_j<A&swAo)`D`BI zNN#Mpf%&I!rXd1S>FCbY#c!0!_**_S(5*%lp%gfaKZj+zzZ*?h3Bv0nQHlQ8G$z~{ zFq9L=xQhN{SlD`CB0gveJqrVX7K8C~tulir6Gqsqj7H(*>4+|%-XA9zvrx21eG#jK z%{_rJfcf5~mk53V=_#LQ@p+eQDALYkX%cWti#&mXq(lS1#)KdUw+?Z20SFI)q)Kbe zk2;2|0hOQ_hWQpm^9eM+!$72V#k+qobu=)&GK4A;A*fZATz$*Yiaar=e+W55`g#5- z&=Qg!Ja3~2RE~rbN-i3rI(b{NN_!hQ+L>O2zY5vYsS;c265O+-p%6qsC%;^HUSAR2 zFBLU<K0uosHnM(ZaHW;SMkyV690F%si)VxmV-Z6bAsGd>7^m4dkSbC72gWs+{5unA z-ePyQOCR_>b~@6H%rUECwp9D^+=Ueuvh5&RcSggYor!QBVoIK#sB%to@`FNnxzy@p z(lQ(yVl@$E)ReoZocaq?_)>b>PKPOcC~SDJrK_1m5}18>RwLs>wl4pf-nGppduPf! zrSQlmY<HWROyMw+msoo7edjywoHO<#sU)AcDO-r$Ab*;Ip122ws<11uLDIF@eOXqP z@e?9*;p5!dlzJ=5o!|kE=8T{O6dC9E90bxbcZf1r`SUOptO{4`sxWYxoGZnSL|as~ zA<Md)ZSte`6ei0-KKY7A%jM`=ANJvG-2$<I{!hn<f#I4231Rdm)3VP^N4}w?Y)z6V zSxw{(%8VGUe8b!J=byi7y9lJyI@9vvz+4>iz_77SfL3E0xh(VAzjuHXg(0)S;b9xB zH-_Gsl8>6=kKKXSG{QEc@u(ahqb-mSx5$FDyCuZHfTgZ;KlKKcUxDQ`AmPzXZ>%uv z=lV)ZGvPQzNj6PVx}rMZBX<IQVfj<qhCX1$cecpAmQ#_J;^MHH63VkTD|%+;o@S9C zFXO};90$!H7D0n^=nf`RxwOA!(J_xhw_&V-7a(B*D%Qr~J?bEVZRo_4A~`&zhyp+J zF~S%ygD6AT)8x`k0xF(l07dJr+;Xqf*pMU>2)7&5Ub52>eLY5H%nGF@`gQ(RYNk=$ zFieTZ$Z$dEl(#fxp~`n)_|&L^G}9jkmr<WyKESZ;5uClA1cRN%Qu+JZB!N$d36sY+ zh@HOoo@IhUMBvRxvv(7A%f-6g7k-!iqX<OHN_Zzj4%~P-T7DF(r(r5r1a+{pGF|!2 zaIlr>?(eLyQr+JTR4g!3p9cdg>>qkxX-o8e=(WN1LL=BpLdd~p0XzKO%L)tK^Nn!7 z#}#rS?@I>X9jI6WAbR2t4ax|4<X;{PG@sKLcP_X`ij-fEUJna^Z^^;nru$v?d2ldg ziJsyH#2?jOPcxkK@0y^$V4SV|p#%3V(50TG{DC9G{^4gL{QC$E!XdRAn(qkwqQ=}3 z84YRuSU~yA;l9sMz4@6KjGgK=ckth<$+RO|hz}i1R}glQUcZ1tt$HdH#Y_$zEQ*O1 z+;V8$O+S2bY~Vih<NE1)^^F_HzwK2Fb=;6ImS~hlzK+Yt7n~G)Gj>+I#(l>=wHn4K zm+_~Q83UNSx9)uq(W1Mf@KxoKT^SF*?&fJ+Q5`lnTc?@^uj<~_F}n;A#8JX!7u4aw zJ%+%W5HP;`&j|1d>f1YHNB|%X7XZNcBLZ}_G_x~f`n_iP?bPs1YukR46TO$H-h<fn zLc5jX4crY@r**SNN{8L5O@9HEp`k!Dq&NjzEJ$D=5M;YP8>DM<*dEq)cFE|B(3sq8 zy;!d9_%rD69CqZx^~a%vqeLQ_v%8OHIe{{txJ$)$ZMLoi0;4l2+a;fNQm&<H&OjIq zlY~X!mhRIdeLUiY-oA12qvA}UB;G*?1^Y3f8T+f&U2Af__m7!TB6%`T{i--dD{}@D zqOss0e=4`@u<8;ChlI|YVNWz;&Mm&D+i??5cVt5Ph7nv9o>|ZK6;}%rH(Qr|3CrLr zlGzbPYB%M8{N@y24-N@=EbQB{Pw(sX>1f-T4H@a(8m>FqOIxE34mp;b-RQ!}1~c?v zzJTV_DuC48>0qW^z`pLq3J`0;vzQ!n-n1ASjIjbp6N|V|vRJa#^c)JXff9mV9v!q% z)Pi^aks{R`RRBU8vyUi<e-}=+*$0_6e23V3fF%Czw=Uw2P7NV<L+*5y=3U`a)E!5m z_k)HDR<(P0s@s}+C4MA#H7)ppWwxXsyd8>rppjqkrw!?yP{=(en7M)BC)N%=X6mGQ z--*I=`g5IN5aUVgAQ?EqStHFMpIkK39?Ou+{C#h#z5v*jEy#D}q<v40)7oJH&EnGG zO@rR2r!t3rgT9yNyXAX(ey&S0)nWq^&F4+PAiL-F(3y~bz{}yN!`t(18knG=qxImd zQ#`k4U~Rui*5o)3q_E1JKjsn6r_$kkNgn$AMyLEyR)5OJ0aVoEeuN}~@iJNIFgQ<- z%Pmsh^oFp0#Jv~#w!pH~WD>I(o^1`24@#Qo6+Vuw%uWd;-L7#2c3-`^GbcF`^caia zwb+SJJ~(F<g2LoK9z%#AHd$cnI0+216y?l-dxj4n8(T=uTrO$L;T8FCXLxCKLQnYW zH1PPrhNre~CCrt?rFMa#X2^(EGw+1Kfw)kaWTK&Yfr=fHdbOH1U^p_Gma2$SV^c-C zWK6$rZ9d+~Ks}IuqR~3!Oos8@7s(Ztu0fDzDuh3Dp|a35rM!dlC&{1&Pbhax&c8!c za{B;9L-@4tf=))aeX+BtPz>LJ6?v+~yqYe&w38si8Y0P8=V0X>AL9Z0oZcKkB)_5H zgkrH&(*cFz#&tvKENHmak=H8bOJuLpNj8D{d`Wi?%@{2qX^#nYlj0bF`u(lP@nUl} zBI7Jh<jls`O>5Qus)y?yhw_UoJq+)@_GU+pK5J53XJOn56701srpAy~d+Iwq$k^z* z`mq^S?u;S!8)zDX4tbtT6-EW9?Qb_Vykn$7B~t4fma4O*3xH_3E`GgAcQ4GS#+L1M z{?MB3Gwn*EbUM(9J3mS6kacoxMAVO!mjvWDQ;36wM~3ri-|rje463TfUZC<HwaDP5 z#hrWJ1*4{wE%Z)&A7JZY%DijNjz76YUmY^tAT^@r$_{OSXUoh#KLk7KY?G=GQwTH8 z5HEWoajp?HmBEuVTJ2BUa;Z`5jN0$MnEukEh<V*{Gn!^El8oucgjps0hGq7FjZr+& zf<o3K?RvIB7#=xivc<kM2p!Kp&Rk;pl8j>YTBvvIgo$n3lJkAT+G1nHMDIdW(|x$1 zS^=EYBfU&*e=7Z&2{jmoJ1%V?%%L&CG&q}ylS5%Ug+^74<%@aidST)eTRX3kEDO_q zXLqT|)O)t=+%!D{IrFSa^6j5Xer-vo%%U!{AcgXBZT`OP?{^Ga=KW&SXtp=CSF4)s zcWIoi!3OeMai$7OvH8mFwC${+x{<MmYBCOc**?#e4ct|#L5aH6Ewy~iKy!W6t$j}F zu4Rne%cAW}pM_SsZE~&v?`%=dumQn@zwg2F=Y}pc$6kK6=dOjQ>92DedRN5>J)~}e z&-pxG|DX&hX6i*oKbkvsgwv?o<1>HGcVTdKn&n4V>h<x|$59XO?4Bv^@b#VvFgez3 z$wk$p?yy#Ss~sZ6fQ``AqQ6AAB7R=ZmK9rAznR^A(_aD!6{3#bJi4vjg(B9He@^$f ztzU!2o*k0Ub{rovFsWAkb*k!uwmWO}@}IX5se1RH^+4K53HC1}f{U4}tChWl%WvC9 zv-+m}rZ}=6(_8@ZPu(n1F(nohp1I*_0!=v-+T-hFU1_)rgf1C6>A8Rl$8n714d^eB z{z>jfQ#m*@alUW+bVWr})$Ay~-@`Oa974TlXa_x9E+#pzcfH8$kn4~!@Mn9-R(2w? z4FbFZBsn-Cmu{e`+vT-*?(as%*r8oJM&VEBNLZliF{4SqG+N-Wl@sD_WCqAti-}V+ zO_1Y8qgEFqJ65T4@6ZLwN>H_zN;#y&^iojo17<|@KDV)H#WWga;+?T5W{SWEp9v&w zG&H<TLhkJ(@xL6C<YK#dM>zTp91w<9%424PuHs5kNLO>9M=D_v?}r@x#F-zz$N2f3 zXkIzUi@UY#daSIDEHW2`oT5U1H0cH(L^Jo&@|icQqG(<sypNYuj!DW6N+3~$WSPT0 zPeZ-*o{tfomO2#df#6-VIA`ZwTij2`@WX6mTUSX6bqZHR*{yFIaN7BU9M<LQg``{} z!4qh~f#&n=A(H~i<?NbYQPUKc()Pq*PH$*0Bbe#1X(2c+7`{a!ly&22)bB;Ui+sZ! zh$}bDMYJj!``TfPP<`*{{r##h9=sY$jJDfOB_*3|7zaEr#ldr+Dj|*&9Q&O3RTNwZ z7I3|CO+|fpN9`1zWJN6J@MTJ!v`ExGQdwmC8us+rd`42w1OgG7)f4ReeJb}F|E`hh zfO-H~U))GQ5bM-}DV*IyJNgK;yGN?e*zK&^!kv%fPVW!<%4R-a%)uPh(X-8Q*M?J4 zyLphP6{8$cCX!2ft?ixOP<Y#caXOjJp4T$xvQ{^V>LyJRb?RX8Z1-!Z8u0?cj8MMr z{(7DtJjq;PV19jI?C(>}6Gfrc!0VeywQHU1W?e&;?Dx(&mSU9G$cPkl&*KM9Jt%P3 z3!uVGSCi!_Vh?tb9j}EmQ_f1qK4L`Zc|dY>Ki(AE=7!(kD}zzA7QF=8R%z-@y)b$0 zN~?ag!`nkhZagbB#+nh3cK1PRMKIh`AAkz(LGLObC5;gyFlQ>a&4X`#8X}I#`6lb| z*)-)HCZ87nSgAZGfoW6<MU5@e(|e^sqJoC)hP2gSWvBk-?LZ;44h{p<a!-HL;rgCB z-dI$G6iTCcyaN2Y?BPM~8c{E#Y1pz_`_)O0OIn@Wz3(qhvvY`b6gcQq^YkCwv{jyP zS=>zv0C`S5qPQ9YhxoF5T^IA@z4DF2Ye)ySpA9T7rwl@yTJplyX|)-XFHKv1cI9E- zGk9Jj{bPivc_7ew0OX2OfKejq9}%KCFd?dHWNd5pn}2^w(X-iPMi0M$dO{Mpk25<e zvMX0vs0|ue6Fcz_EF5dd6pW2W<$Cj%XiSL<6ZV^5fh6fU%8^)znc04+nm1(<ptg|7 zO2DRAfNfK9*I6)Ns;a`1CU)%F-_X)p2m$x*`}z2__M^rOB(bK0k-ezY8FH3adQf@E zKC^RNGj??j7ysUZ1KZHcg`NvVj+LjPO^F(}DTE2zhRL%Ib!}y8;%?3wj@A3h+3qDt zS4`{W{-l-Ouod1ut8O$@z=f^rD4Xs&8f9KiP%P?BP}Lwb(_$WVK|Q~PmHCZ9zbke= zH`r9@2E56Xw|IEko1-w|kZb}rH}->c6w395&KbXf4S4|vxPt^Lcf3=R*c+!LU)z@9 z3fZdlxf(3{7H=ZY$enIRDu(TFDfdXg;D}SPmMl8QJuz9ajOj;Rr=pwva*--Pr*Khl zdBS6^4~D{8n6Mp7eFz8L37Sc@X03X&iZed}?c`{R@~55!k4?IRl)*w$R4Rt+T50kb z8mRjzUZ|NIPbG2C2O6ELZ}4qc_%v(qA2SPy=098#OZa|9^!RcjBvI=?`5?jF_{ro1 zPLMwzDdp9!`2G@x3Cs^WAQZ_jA(6;hCnDw}>eGP>UrDB}P<IT`3pi%LKbnM4ToA@2 z&?I(%>2mZxOv1y=SmiH^{<rj5`yyuKkqf$WKH^<GV)D>@%Cy2twUTA)7CWMT>=aEK zANoxgxjg>}A*%&*q_52aUqFY}c4ygq$p&txW0D8rb*O}yB>AHy`#D0Ck8gwdg|_v~ z$Q^S}lcnc;-`#B`Wq-YQSas9C{+#5Kz^508e(pSsX?oBzJ~Y>Wps^I!?-&<@p(!RH zxZze_{?f#u0iRl+k1hh+hcv2`noMXS^ggH(HFg(tleX5c3W*^@68xNVupO-C6}|>i z2Ny1uFXDxI{SqDH5dlqMMp&8|4v|)9(EKr48-3!SJT8?Vv~&n?z$QE)$`hM(7mJ)m zg+Cf$R0<WlN%txOI(!w;0x1I*MefT1;*Qn4cesJy`U!?+c;@y#e<pil)G4`i(Tmq% z6^{D8?R+C%I!6$dQ!6KdsQW6y_9p#>VI@1DIIlm4N@Gu;9LcNNL~VoeN+NEggYxH| zezu>SA@zaZlrU&K*|zsF@>Ox8(Zpvxv7zR>=4+CLA8W2yy=^90Q*-g$(~R!E5PYAx zKTI{5rVnEY^@b9y^M9b2w`J}9dD-`T{QcYlJQhp3`--og4zA^(RUl3p=C&^H{^RSH zI@1TNe>yP=EQ&+<1ONF8z)UPMFw+D~7brSAIJz(yIXeEf8UWL=|IID|ogznBP63#X z>>;}(s@!R71}n9?3ZA3<6vSjjkW=)@j%<Nkn)TDG0Am;{IF)l_-g(ARM*mkGi$)E2 zCBCkSNvk9a%Mz6h&MIFGJq1iJ%#?a*YzwC}BId)4=NoXskZbJZkxZ1ZxT<csaVDIY z#$owSG-(WK)>ynr)CdSJZZ#D4Ia?l<XxSLUIr};yRf=-Yl9@=cldCrd%3n?Em;}|J z6nyb%zjdR)JopJ&l}?l(u0@J`O=eE}o|pli0K*>*NhSow3I5|-aCEn>?o=QGOSKM4 zsH`aoQ%sB($T#R9x{J7U(xjee`N_S)DwVq*1w3asfdp-U^9HdFBwhk;C4w@?Dh4Y} z`alGf3b;a;!M3E?8J{>4SRESp+Ho*_I5d<sE`ZIdmr+z=912SBr;l}8rELc;U#L8r zkDKJrSKy>@C)Tx?j%RUgVZ_AvVoAjT_Qz(yCl2{qoB5BY4|>OPzPP>AOh=2sGZ8ih z*mi=i1h0tyWV6u=*zHb0o4o|uEXrSP*2Ka2_bu$dMhmo9V2MpuQV3y258IV{Mk>CT z8~n@;u30#lt845kexPG_(aK9Lw;6YN!N8n(Oz7LycGMLyX_d0xFxCTRNhbZFkePT4 zY+Rc03#C|HRaig-ze&Xxb|#AAa>x+#^;@+;)hTHNg-+G{8o&V$m6dU+a&wFNFg9Lm zSa4Q9%0kqcGS*V3x<h@TtpryfnMi3nK!&FUoh!T-7k_>{xoJT8+-(QfF`Og<w3ve% zUc}d&;doFN-!hHJl*G1Xk8~pj%#Ym|P*n+ADHYTC^8UHk*Vigw8LM$T8e{@>#+c3) zQ7d@1q-yH*8ZPrZK4N597ObUY=1>)Otj8kJbylZRBWub-QKRH=WIBO2!iiZH&TL!G z{d*CZ#L?1PN!LaTz~A8Qb<QCF!@MK%^yb)LI`+mFbk{a~$5{w;52w3{%`Zm1+WGXP z`yUnT{ZMNPnRB;3h5E8T#U0Lu4_gJj@yU<I_q@ZJ|CwaI3!Lo#WaHwRDi$;00Dw$q z0N}T1?Du@Ei>sHd+3yFb?vI<!o7|}37j4$NK_yk|F`|NePH}8s4>npF4x)0VIYdQJ zSI7&#lC<8RU;(6HoYBznzFo>0M}0#g_J0OsQN6s5%Ai8^d8WgC$c=Xw`W_3K8_tS) z^6YwZaQQe?rj&?9AE!yL;?L{-_2D>yDd27MVwTG568D;4F_2wIYH)GBkUE{}z=@F2 zbMPv`d0eH%iLxma<J5$ch>G5aHiJmy1HBeWDJkagnWuxrk)^6hqaZp@c2uIlM7^x1 zcIHo~5fiagt6t>E?gD}P0)a_-ZFANyUbTq4QFQu)qMd`^2my+ugR+Ot>b|N_Dyw7p zsz1NSKhQ<32ENBPL;}Q%pzLEyaF|q+GRV(3We!26^ie*Wp_A{ApYx~TWRFA_XHhCj zU}4w4YGB=oxy4I{XZVgxw2$46ej}+8TFILVSY>6n+ItB7T7aBT$r_yr4bjIV<HDk> z!#x?(q+7od2lpazmd?ynT{kO6E^eY{{3_IPi4~(D;R7|zh1Cg(w>wP#k!$zzW+)*_ z5ACd~;&X1i=ADsqDcI<3klLG0M3=5#<(>B;XXQbqJ%7bkaqHE(29DsRrE)n!SrLI= z>f_bu`Fl#!QhXQQ$LEe$=Rq_lvckx+mG~18(dVP!G9hQen!dN|jhn~E7{&C8N1wN! zbH^A*^UE@085{4dZ6;jLw$B1JFZ80__9>tH5NzA*n_7QDSa=h|xbKq6-YP{35G3hH zY|hZ{;&-jXsm;CmV7UI!hadnZD<v+}<}Q6OZcch&Su^_@iIc>iOIS(iAr;%4@a;ui z@Xg=5;OmRH;H$rL!Iu|y!6zV?cDgZPADx#&$q@;!)wguJ^OJD6IJD0rdv4z>0~h*! zhtr?X<=20IU{AFB(!Lqt0&JS!1UA)=xKx#>J674IV}|H)rZh^L4TJ>u!Iw3tHpse* z4K1b+&PpOC161N{Z$<etF<Gy@e2`cuZ}Vp1J_bNIV@`eQo>Em7Z2rvuGHjAY2t^-_ zo`9TrSvZBy@OiO}P4ECm;l2EN!%2RFq*r44cxt`_<9bf)@M2{911{-!e*NiOH9h7A zd2qf875&tgAC*$#oPxd>4?kKL68E}eP2@jymYo=fbMSL9XYN-74ZzRB!<%~=8(N7k zi*m%3mssN}g)Fldx>r3?sdfGShn48L`eJz7W)Zb9+o#Q{`0dxD?~KM3vk^$OqoPwP zrI>)Bvh2qks~`QWoU?1rD96{M=bBHM4PSF6k2ySxGZ)p#E8TidNU=`K#k3I}<`8hx zI@I||eQG>KA$2apb-u|nq}+m@hg0T#NL?&kZVq3XY(~S(SPph?-_&I3j;KL!WN6Hp zf;`s@CiJb#XwsICPIAzj+{e&q3g0|EFBjGB!Ie^+)C^Z(ANnzUmTW=G;DamMneIfN z=OrqUgZ`oVG0W~jfJ};M(tu(TVlr$zjT8B$?D0_A+i^5K1nHQ>oU~;A{Pg{xDE?dx zu|45MFJyvq#iHysGkd>_7CI}5$%+MNz|(lyTYkmf2kGENjlv=UGF|~wL#$xK<O)ev zQql^uQb~w$d~46Q&vh!}jgjGZiI=B`X~AeHRP<WvF^HfJ6;790aYEo#BtuVJlc9o4 zqGpv6sF`jX%uHo(`};o%zUxDhTZH$fh4H*urgu_iA#(YCs1L&H6=k-rJb8C-5FE1I zk07*$fWJJT|A~G!>>9O_E8)};kMQ;qQQ_4(H`a`k&ZHC4Hqew|4KSXiIAWH3m?~lG z846|Vxil)XW(J?ImzuNx!iI+O0UJ`hBs55A+|W=J7aO|*Fmy53U^wKk2&^uaD-1<t z0C3{Vvyx>2g;o5Rv6Xo^*lI#-%$5G;hN>^NZ>)PwAGE<2<ybJ2D`&MNJEJHWJR4JS zCiSBdHCXc4g3*dG&Ph_(UQ|P5tFqf+5+Cv1WZTzj9aTx+zNj=-T49>?K+i-ra;GOB zZ*6!FvLt2S*V@6c&FaF}4!)lf=}mO{#5T&(VWQT{uhm#DD2K#&f6IIRpf-gi#;jF6 zbGMmU^sy#MZm<`nwWq(8Q)uH1y6Yf`I;FJmhf#FV%j-a?gabI&Y_}_TodzmiQiK4R z(Fci5vel|DabpL$N%d0-0%UOtT%=vkj^mBM)yHvbIY6B!x9FO{<F-)E(WXI<zzSo1 z1tOu#dp=w(xQTg511_KUh5sP_!h+C)e?-h4c0}G@+QFa$MGx|6CICjD$plhPW#{xe z4D<JFG1t1J>se*>qjN4s?%RO!Gj)cXg&e4woR?RuT265s557A>`>n{GgLu4oAAmEN zcbw>q<5J?6(i_5TU~@<+UJzl;UTh#<jUm5%bMU2L5k~RPSamRU5PQ*!uYq_$oV23( zalD2ld*b=Au!B9Zur!uMBH$b4$iOGMf%~zr2TtN)mxAJ9=tM;#u>WcYB4APe)h*Y? z$D%z$i3Gw<HBuN4gLF=u<7^lNh~O?7^aLWJ6aM-v47yo~1oXGAI2fTmI2g)v;{R@1 zI#qff0EFZy8Fd38!=vSi!N05a$BTZ`NAy2btnAR_?k{I1v{1#qFj1AfWT7qoQ@4LX zfT~g#1v@hRslDs%>?3N)8RwJUvzEdU!P5(q-g4Tbz0ibR(cM!~mUf5MOP&~a#ubgF z-w(&#FL*D+#<*}+@=AP+RhzmlGT^6!M{P0RseEl8`!g7qXGJn?N&)_JT&OuJJjbV3 z?&OkJh@IY@1Tg9!q4bLC2w+MFLTcZw{%q`^5tEZ3Znw#72=*Y@*DGqlK+JBpiERZn zg7b)*QYW_EtG!%<<%hr7d**zsx8NhF`OX9pTKnA+C4M`!)*hzCI^4uEwzseY#&6jL z?Q!CmGXKWa_{U(Ul4aO7wNORDbiMKu-m+Z3Gy;siGFWWi67p9D4$%iG1Ss+9rNqFr zjQ~e?wC-X^?0^cA2_5Ums}x*$GtCMT5IPQW!xBPn5<Zq!<@c#=KpYi^?v_zWIQZ{4 zT-3jVOvf$%0EL4yRe+2b0A;1)7IqeMS}1WAi__SK9bg)%6Wfclf;xpXVH_thXwB!Y zCyV`<O3zo1J8ZWd$DEcsuFB1x(-7wZDJeq>!CHwEq5CHOAQd;6zEOO~Rhb=_hMU2_ zQ!-*E^HKSru|&SYX+2rTTDNtHl7mslI_2ME`TsozVq2sH97WuXpZ^d+(YDlXTOs$S zi2qgo?*f3z<oxk+<uV}rL_Q2USVLG2H60^p^y%}+ES?a22^_Ofd+8k$x?CULmUkjm z<xZS7*mQi`{3%q9YkjS};#;}eOG|~DA4wDKJosSYU>Gf3_3r5x>PmmUv(rn`Yx|Wp z2tNINYud*2()BL;RW~2CC%a~KiypXb4Bk`QPH77~wVrXcf!P*;)@8+I3tWvpuhQ3c zIWh>(+5pL_N~@be+SCpIfrFp%YCe-CKC79t(bi|*<GnK9rthq7^?5CP%^{ho47;^H zoSM8mnaAJN?x-3VXnD9xoiC&xa<+NF+gW*RbAG!TAr^XADhas7?X48}Kzoqg%E>&R zSex`!<Q@vGrOCysCBV~NX_&E!F+~H3x5}^daoa(6((I;MFgGo%+M{AKYdTd;!{>v7 zgL|cR<zd?^ZXRodq$lCl_0e9JY$guOYG2T5<NPzO0WdI(Fwo$z^k=GDseYh)yZVzI z0|8|KGGYJx{m5_Us=t<h_?kpf=I;Xje%t-8;1pn@<u7;Ne+B;i8t`9%O+fngf4da? ztE6AAhW;t*1jzdR$3@X!;lJK1_!E8xB$0oI|9Z>dR}sJFmHrgLi1WwM=r4rhukc@a z>p$TL1pkKrjm7>I|0~J)C!Uz--}qlB&tE0{ohkbh4*(F5006)8X1}8U&RP5wJx}%* r^dAhyukc@wl79;DqWoj_{@=k;Q3e8N9>0BA0S6cWawLMRzy10j**Hv# literal 0 HcmV?d00001 diff --git a/dev/tests/acceptance/tests/_data/lorem_ipsum.txt b/dev/tests/acceptance/tests/_data/lorem_ipsum.txt new file mode 100644 index 0000000000000..64cb8d023361a --- /dev/null +++ b/dev/tests/acceptance/tests/_data/lorem_ipsum.txt @@ -0,0 +1,9 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc venenatis cursus eros, eu congue risus eleifend ut. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean aliquet mi diam, at consequat ex imperdiet eu. Nullam vulputate sollicitudin libero, tristique ullamcorper ante pretium eget. Nunc vehicula, risus ut hendrerit ornare, tortor est mattis urna, vitae egestas arcu tellus quis est. Sed mollis est sem. Aenean rhoncus ultricies sapien, id tempus elit lobortis ac. Pellentesque condimentum gravida purus a pretium. Nulla sed sapien mattis, auctor lacus quis, volutpat metus. Nunc mattis diam elit, viverra tincidunt nisl faucibus eu. Duis ac nisl tellus. Aenean eros lectus, malesuada in ex non, pharetra aliquam odio. Aenean ultricies pharetra mauris, ac rutrum quam posuere at. Phasellus et tempor turpis, ut sodales turpis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + +Sed fringilla orci at elit gravida, posuere varius elit eleifend. Nam libero dui, rutrum ac massa et, dictum egestas massa. Fusce rutrum, neque vitae vestibulum mattis, magna orci dictum turpis, ut laoreet eros urna lacinia ipsum. Donec eget ultrices eros. Duis sollicitudin ante est. Maecenas semper pellentesque scelerisque. Vestibulum eu venenatis tellus. Etiam nec massa sem. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam blandit molestie justo, non euismod dolor aliquet ac. Duis consectetur enim in arcu suscipit, in tempus nisi commodo. + +Donec sed venenatis nunc. Proin velit leo, porta eget erat elementum, dapibus posuere odio. Nam varius lectus eu cursus tristique. Ut tempus libero vehicula, iaculis augue sit amet, vestibulum justo. Vivamus porta diam vitae malesuada vestibulum. Donec mi dolor, semper at rutrum eget, vehicula non orci. Nunc dolor urna, laoreet et egestas vitae, sodales quis ipsum. Vivamus aliquet viverra enim cursus tincidunt. Pellentesque vel laoreet mi. Aenean ut rhoncus orci. Donec a purus venenatis, tempor dolor in, facilisis ex. Sed nec metus convallis, viverra nisl nec, luctus arcu. Integer blandit arcu a est posuere pharetra. + +Aliquam ultricies lectus ac mauris luctus, a viverra neque rhoncus. Vestibulum id velit eu nisl efficitur lobortis. Sed id metus at ipsum imperdiet porta. Quisque in quam in turpis fermentum condimentum. Phasellus sagittis risus eu tempus scelerisque. Vivamus dapibus sem odio, vitae fermentum sem viverra et. Quisque sit amet cursus neque, vel hendrerit risus. Integer ut diam porta, volutpat risus in, iaculis diam. Suspendisse vulputate non quam et finibus. + +Donec blandit, sem ut posuere dignissim, dolor lorem egestas magna, vel faucibus dui metus eget orci. Nullam ipsum lacus, imperdiet at nisl sed, condimentum dignissim lectus. Nunc orci libero, tincidunt a molestie vel, dapibus at mi. Quisque scelerisque sem quis massa suscipit, sit amet suscipit arcu volutpat. In tincidunt lacus in porttitor mattis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Morbi rutrum gravida orci quis porta. \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminProductActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminProductActionGroup.xml index 3ef8b961a81af..84adc1d86ff6a 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminProductActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminProductActionGroup.xml @@ -157,7 +157,7 @@ <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> <fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> - <click selector="{{AdminProductCustomizableOptionsSection.customezableOptions}}" stepKey="openCustomOptionsSection"/> + <click selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" stepKey="openCustomOptionsSection"/> <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> <fillField userInput="option1" selector="{{AdminProductCustomizableOptionsSection.optionTitleInput}}" stepKey="fillOptionTitle"/> <click selector="{{AdminProductCustomizableOptionsSection.optionTypeOpenDropDown}}" stepKey="openTypeDropDown"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/CustomOptionsActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/CustomOptionsActionGroup.xml index 0409c3f195013..321faaba692f1 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/CustomOptionsActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/CustomOptionsActionGroup.xml @@ -8,10 +8,8 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> - - + <actionGroup name="CreateCustomRadioOptions"> - <!-- ActionGroup will add a single custom option to a product --> <!-- You must already be on the product creation page --> <arguments> @@ -39,8 +37,23 @@ <fillField stepKey="fillInValueTitle2" selector="{{AdminProductCustomizableOptionsSection.valueTitle}}" userInput="{{productOption2.title}}"/> <fillField stepKey="fillInValuePrice2" selector="{{AdminProductCustomizableOptionsSection.valuePrice}}" userInput="{{productOption2.price}}"/> + </actionGroup> - + <!--Add a custom option of type "file" to a product--> + <actionGroup name="AddProductCustomOptionFile"> + <arguments> + <argument name="option" defaultValue="ProductOptionFile"/> + </arguments> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSection"/> + <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> + <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" stepKey="waitForOption"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" userInput="{{option.title}}" stepKey="fillTitle"/> + <click selector="{{AdminProductCustomizableOptionsSection.lastOptionTypeParent}}" stepKey="openTypeSelect"/> + <click selector="{{AdminProductCustomizableOptionsSection.optionType('File')}}" stepKey="selectTypeFile"/> + <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.optionPrice}}" stepKey="waitForElements"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionPrice}}" userInput="{{option.price}}" stepKey="fillPrice"/> + <selectOption selector="{{AdminProductCustomizableOptionsSection.optionPriceType}}" userInput="{{option.price_type}}" stepKey="selectPriceType"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionFileExtensions}}" userInput="{{option.file_extension}}" stepKey="fillCompatibleExtensions"/> </actionGroup> </actionGroups> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml index cb80dade856a7..e7030e785c7b7 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminProductCustomizableOptionsSection"> <element name="checkIfCustomizableOptionsTabOpen" type="text" selector="//span[text()='Customizable Options']/parent::strong/parent::*[@data-state-collapsible='closed']"/> - <element name="customezableOptions" type="text" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Customizable Options']"/> + <element name="customizableOptions" type="text" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Customizable Options']"/> <element name="useDefaultOptionTitle" type="text" selector="[data-index='options'] tr.data-row [data-index='title'] [name^='options_use_default']"/> <element name="useDefaultOptionTitleByIndex" type="text" selector="[data-index='options'] [data-index='values'] tr[data-repeat-index='{{var1}}'] [name^='options_use_default']" parameterized="true"/> <element name="addOptionBtn" type="button" selector="button[data-index='button_add']"/> @@ -20,7 +20,6 @@ <element name="optionTypeTextField" type="button" selector=".admin__dynamic-rows[data-index='options'] .action-menu._active li li"/> <element name="maxCharactersInput" type="input" selector="input[name='product[options][0][max_characters]']"/> - <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//span[text()='Option Type']/parent::label/parent::div//div[@data-role='selected-option']" parameterized="true"/> <element name="checkDropDown" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//parent::label/parent::div//li[@class='admin__action-multiselect-menu-inner-item']//label[text()='Drop-down']" parameterized="true"/> <element name="clickAddValue" type="button" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tfoot//button" parameterized="true"/> @@ -38,5 +37,10 @@ <element name="addValue" type="button" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[@data-action='add_new_row']" /> <element name="valueTitle" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__control-table')]//tbody/tr[last()]//*[@data-index='title']//input" /> <element name="valuePrice" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__control-table')]//tbody/tr[last()]//*[@data-index='price']//input" /> + + <element name="optionPrice" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[@name='product[options][0][price]']"/> + <element name="optionPriceType" type="select" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[@name='product[options][0][price_type]']"/> + <element name="optionSku" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[@name='product[options][0][sku]']"/> + <element name="optionFileExtensions" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[@name='product[options][0][file_extension]']"/> </section> </sections> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontCategoryProductSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontCategoryProductSection.xml index aaec0600f8bf7..edbadbe6785c8 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontCategoryProductSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontCategoryProductSection.xml @@ -19,6 +19,7 @@ <element name="ProductImageByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//img[@class='product-image-photo']" parameterized="true"/> <element name="ProductImageBySrc" type="text" selector=".products-grid img[src*='{{pattern}}']" parameterized="true"/> <element name="ProductInfoByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//div[@class='product-item-info']" parameterized="true"/> + <element name="ProductAddToCartByName" type="button" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//a[contains(@class, 'tocart')]" parameterized="true"/> <element name="ProductAddToCompareByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//a[contains(@class, 'tocompare')]" parameterized="true"/> <element name="ProductImageByNameAndSrc" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//img[contains(@src, '{{src}}')]" parameterized="true"/> </section> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductPageSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductPageSection.xml index a3703839601fd..813d6e5a1e9eb 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductPageSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductPageSection.xml @@ -10,8 +10,10 @@ xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="StorefrontProductPageSection"> <element name="qtyInput" type="button" selector="input.input-text.qty"/> - <element name="addToCartBtn" type="button" selector="button.action.tocart.primary"/> + <element name="addToCartBtn" type="button" selector="button.action.tocart.primary" timeout="30"/> <element name="successMsg" type="button" selector="div.message-success"/> + <element name="alertMessage" type="text" selector=".page.messages [role=alert]"/> + <element name="messagesBlock" type="text" selector=".page.messages"/> <element name="addToWishlist" type="button" selector="//a[@class='action towishlist']" timeout="30"/> <element name="customTextOptionInput" type="input" selector=".input-text.product-custom-option"/> <element name="charCounter" type="text" selector=".character-counter"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml index e938bee27eb47..eb79d0dafef7b 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml @@ -65,8 +65,8 @@ <fillField userInput="{{_defaultProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> <fillField userInput="{{_defaultProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> - <!--<click selector="{{AdminProductCustomizableOptionsSection.customezableOptions}}" stepKey="openCustomOptionsSection"/>--> - <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customezableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses2"/> + <!--<click selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" stepKey="openCustomOptionsSection"/>--> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses2"/> <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> <waitForPageLoad stepKey="waitAfterAddOption"/> <fillField selector="input[name='product[options][0][title]']" userInput="Radio Option" stepKey="fillOptionTitle"/> @@ -98,7 +98,7 @@ <waitForLoadingMaskToDisappear stepKey="waitForProductPagetoSaveAgain"/> <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageAgain"/> - <click selector="{{AdminProductCustomizableOptionsSection.customezableOptions}}" stepKey="openCustomOptionsSection2"/> + <click selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" stepKey="openCustomOptionsSection2"/> <seeNumberOfElements selector=".admin__dynamic-rows[data-index='values'] tr.data-row" userInput="3" stepKey="see4RowsOfOptions"/> </test> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SimpleProductTwoCustomOptionsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SimpleProductTwoCustomOptionsTest.xml index 2710002d625d7..beeabdc34976b 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SimpleProductTwoCustomOptionsTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SimpleProductTwoCustomOptionsTest.xml @@ -41,7 +41,7 @@ </after> <!-- opens the custom option panel and clicks add options --> - <click stepKey="openCustomizableOptions" selector="{{AdminProductCustomizableOptionsSection.customezableOptions}}"/> + <click stepKey="openCustomizableOptions" selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}"/> <waitForPageLoad stepKey="waitForCustomOptionsOpen"/> <!-- Create a custom option with 2 values --> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViews.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViews.xml index 93585f8c14985..ec37916bfa843 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViews.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViews.xml @@ -77,7 +77,7 @@ <!-- Update Product with Option Value DropDown 1--> - <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customezableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses2"/> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses2"/> <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="checkAddOption1"/> <waitForPageLoad time="10" stepKey="waitForPageLoad7"/> <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionTitle('New Option')}}" userInput="Custom Options 1" stepKey="fillOptionTitle1"/> @@ -104,7 +104,7 @@ <!-- Open tab Customizable Options --> <waitForPageLoad time="10" stepKey="waitForPageLoad2"/> - <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customezableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses3"/> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses3"/> <!-- Update Option Customizable Options and Option Value 1--> @@ -264,7 +264,7 @@ <!-- Open tab Customizable Options --> <waitForPageLoad time="30" stepKey="waitForPageLoad9"/> - <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customezableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses4" /> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses4" /> <!-- Update Option Customizable Options and Option Value 1--> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/AdminDataGridHeaderSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/AdminDataGridHeaderSection.xml new file mode 100644 index 0000000000000..56062f96152c2 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/AdminDataGridHeaderSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminDataGridHeaderSection"> + <element name="attributeCodeFilterInput" type="input" selector=".admin__data-grid-filters input[name='attribute_code']"/> + </section> +</sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminCreateProductConfigurationsPanelSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminCreateProductConfigurationsPanelSection.xml index 99f6a8842481e..baf833cacd22f 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminCreateProductConfigurationsPanelSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminCreateProductConfigurationsPanelSection.xml @@ -18,6 +18,7 @@ <element name="id" type="text" selector="//tr[contains(@data-repeat-index, '0')]/td[2]/div"/> <element name="selectAll" type="button" selector=".action-select-all"/> + <element name="selectAllByAttribute" type="button" selector="//div[@data-attribute-title='{{attr}}']//button[contains(@class, 'action-select-all')]" parameterized="true"/> <element name="createNewValue" type="input" selector=".action-create-new" timeout="30"/> <element name="attributeName" type="input" selector="li[data-attribute-option-title=''] .admin__field-create-new .admin__control-text"/> <element name="saveAttribute" type="button" selector="li[data-attribute-option-title=''] .action-save" timeout="30"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminNewAttributePanelSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminNewAttributePanelSection.xml index 469afa426bc1f..c94aa33b08cb7 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminNewAttributePanelSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminNewAttributePanelSection.xml @@ -14,6 +14,7 @@ <element name="newAttributeIFrame" type="iframe" selector="create_new_attribute_container"/> <element name="defaultLabel" type="input" selector="input[name='frontend_label[0]']"/> <element name="inputType" type="select" selector="select[name='frontend_input']" timeout="30"/> + <element name="valuesRequired" type="select" selector="select#is_required"/> <element name="addOption" type="button" selector="#add_new_option_button"/> <element name="isDefault" type="radio" selector="[data-role='options-container'] tr:nth-of-type({{row}}) input[name='default[]']" parameterized="true"/> <element name="optionAdminValue" type="input" selector="[data-role='options-container'] input[name='option[value][option_{{row}}][0]']" parameterized="true"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminProductFormConfigurationsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminProductFormConfigurationsSection.xml index 43bf9822903be..88761be62b3f3 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminProductFormConfigurationsSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminProductFormConfigurationsSection.xml @@ -9,6 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminProductFormConfigurationsSection"> + <element name="sectionHeader" type="text" selector=".admin__collapsible-block-wrapper[data-index='configurable']"/> <element name="createConfigurations" type="button" selector="button[data-index='create_configurable_products_button']" timeout="30"/> <element name="currentVariationsRows" type="button" selector=".data-row"/> <element name="currentVariationsNameCells" type="textarea" selector=".admin__control-fields[data-index='name_container']"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml new file mode 100644 index 0000000000000..e7091dbfae215 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableProductWithFileCustomOptionTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Add configurable product to cart"/> + <title value="Correct error message and redirect with invalid file option"/> + <description value="Configurable product has file custom option. When adding to cart with an invalid filetype, the correct error message is shown, and options remain selected."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-93059"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <argument name="product" value="BaseConfigurableProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + <!--Add custom option to configurable product--> + <actionGroup ref="AddProductCustomOptionFile" stepKey="addCustomOptionToProduct"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + + <!--Go to storefront--> + <amOnPage url="" stepKey="goToHomePage"/> + <waitForPageLoad stepKey="waitForHomePageLoad"/> + <click selector="{{StorefrontNavigationSection.topCategory($$createCategory.name$$)}}" stepKey="goToCategoryStorefront"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="$$createCategory.name$$" stepKey="seeOnCategoryPage"/> + <!--Add configurable product to cart--> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductTitleByName(BaseConfigurableProduct.name)}}" stepKey="hoverProductInGrid"/> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(BaseConfigurableProduct.name)}}" stepKey="tryAddToCartFromCategoryPage"/> + <waitForPageLoad stepKey="waitForRedirectToProductPage"/> + <seeInCurrentUrl url="{{StorefrontProductPage.url(BaseConfigurableProduct.urlKey)}}" stepKey="seeOnProductPage"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="{{colorProductAttribute2.name}}" stepKey="selectColor"/> + <!--Try invalid file--> + <attachFile selector="{{StorefrontProductInfoMainSection.addLinkFileUploadFile(ProductOptionFile.title)}}" userInput="lorem_ipsum.docx" stepKey="attachInvalidFile"/> + <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCartInvalidFile"/> + <waitForElementVisible selector="{{StorefrontProductPageSection.alertMessage}}" stepKey="waitForErrorMessageInvalidFile"/> + <see selector="{{StorefrontProductPageSection.messagesBlock}}" userInput="The file 'lorem_ipsum.docx' for '{{ProductOptionFile.title}}' has an invalid extension." stepKey="seeMessageInvalidFile"/> + <!--Option remains selected--> + <seeOptionIsSelected selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeOptionRemainSelected"/> + <!--Try valid file--> + <attachFile selector="{{StorefrontProductInfoMainSection.addLinkFileUploadFile(ProductOptionFile.title)}}" userInput="{{MagentoLogo.file}}" stepKey="attachValidFile"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$11.99" stepKey="seePriceUpdated"/> + <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCartValidFile"/> + <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" stepKey="waitForSuccessMessage"/> + <see selector="{{StorefrontProductPageSection.messagesBlock}}" userInput="You added {{BaseConfigurableProduct.name}} to your shopping cart." stepKey="seeSuccessMessage"/> + + <!--Check item in cart--> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCart"/> + <waitForPageLoad stepKey="waitForCartPageLoad"/> + <seeElement selector="{{CheckoutCartProductSection.ProductLinkByName(BaseConfigurableProduct.name)}}" stepKey="seeProductInCart"/> + <see selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute(BaseConfigurableProduct.name, colorProductAttribute.default_label)}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeSelectedOption"/> + <see selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute(BaseConfigurableProduct.name, ProductOptionFile.title)}}" userInput="{{MagentoLogo.file}}" stepKey="seeCorrectOptionFile"/> + <!--Delete cart item--> + <click selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="deleteCartItem"/> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/ActionGroup/AddSwatchToProductActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/ActionGroup/AddSwatchToProductActionGroup.xml new file mode 100644 index 0000000000000..09137a7003b94 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/ActionGroup/AddSwatchToProductActionGroup.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + + <actionGroup name="AddVisualSwatchToProductActionGroup"> + <arguments> + <argument name="attribute" defaultValue="visualSwatchAttribute"/> + <argument name="option1" defaultValue="visualSwatchOption1"/> + <argument name="option2" defaultValue="visualSwatchOption2"/> + </arguments> + + <seeInCurrentUrl url="{{ProductCatalogPage.url}}" stepKey="seeOnProductEditPage"/> + <conditionalClick selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="openConfigurationPanel"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="waitForSlideOut"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="clickCreateNewAttribute"/> + <waitForPageLoad stepKey="waitForIFrame"/> + + <switchToIFrame selector="{{AdminNewAttributePanel.newAttributeIFrame}}" stepKey="switchToNewAttributeIFrame"/> + <fillField selector="{{AdminNewAttributePanel.defaultLabel}}" userInput="{{attribute.default_label}}" stepKey="fillDefaultLabel"/> + <selectOption selector="{{AdminNewAttributePanel.inputType}}" userInput="{{attribute.input_type}}" stepKey="selectInputType"/> + <!--Add swatch options--> + <click selector="{{AdminNewAttributePanel.addVisualSwatchOption}}" stepKey="clickAddSwatch1"/> + <waitForElementVisible selector="{{AdminNewAttributePanel.visualSwatchOptionAdminValue('0')}}" stepKey="waitForOption1Row"/> + <fillField selector="{{AdminNewAttributePanel.visualSwatchOptionAdminValue('0')}}" userInput="{{option1.admin_label}}" stepKey="fillAdminLabel1"/> + <fillField selector="{{AdminNewAttributePanel.visualSwatchOptionDefaultStoreValue('0')}}" userInput="{{option1.default_label}}" stepKey="fillDefaultStoreLabel1"/> + <click selector="{{AdminNewAttributePanel.addVisualSwatchOption}}" stepKey="clickAddSwatch2"/> + <waitForElementVisible selector="{{AdminNewAttributePanel.visualSwatchOptionAdminValue('1')}}" stepKey="waitForOption2Row"/> + <fillField selector="{{AdminNewAttributePanel.visualSwatchOptionAdminValue('1')}}" userInput="{{option2.admin_label}}" stepKey="fillAdminLabel2"/> + <fillField selector="{{AdminNewAttributePanel.visualSwatchOptionDefaultStoreValue('1')}}" userInput="{{option2.default_label}}" stepKey="fillDefaultStoreLabel2"/> + + <!--Save attribute--> + <click selector="{{AdminNewAttributePanel.saveAttribute}}" stepKey="clickOnNewAttributePanel"/> + <waitForPageLoad stepKey="waitForSaveAttribute"/> + <switchToIFrame stepKey="switchOutOfIFrame"/> + + <!--Find attribute in grid and select--> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="clickOnFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.attributeCodeFilterInput}}" userInput="{{attribute.default_label}}" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="clickOnFirstCheckbox"/> + + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep1"/> + + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute(attribute.default_label)}}" stepKey="clickSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep2"/> + + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="100" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextStep3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="generateProducts"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSaveProductMessage"/> + </actionGroup> + +</actionGroups> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Data/SwatchAttributeData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Data/SwatchAttributeData.xml new file mode 100644 index 0000000000000..0e70bdcc70249 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Data/SwatchAttributeData.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="visualSwatchAttribute" type="SwatchAttribute"> + <data key="default_label" unique="suffix">VisualSwatchAttr</data> + <data key="input_type">Visual Swatch</data> + </entity> +</entities> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Data/SwatchOptionData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Data/SwatchOptionData.xml new file mode 100644 index 0000000000000..76bfbe8e1b870 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Data/SwatchOptionData.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="visualSwatchOption1" type="SwatchOption"> + <data key="admin_label" unique="suffix">VisualOpt1</data> + <data key="default_label" unique="suffix">VisualOpt1</data> + </entity> + + <entity name="visualSwatchOption2" type="SwatchOption"> + <data key="admin_label" unique="suffix">VisualOpt2</data> + <data key="default_label" unique="suffix">VisualOpt2</data> + </entity> +</entities> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Section/AdminNewAttributePanelSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Section/AdminNewAttributePanelSection.xml new file mode 100644 index 0000000000000..36c2056a45771 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Section/AdminNewAttributePanelSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminNewAttributePanel"> + <element name="addVisualSwatchOption" type="button" selector="button#add_new_swatch_visual_option_button"/> + <element name="addTextSwatchOption" type="button" selector="button#add_new_swatch_text_option_button"/> + <element name="visualSwatchOptionAdminValue" type="input" selector="[data-role='swatch-visual-options-container'] input[name='optionvisual[value][option_{{row}}][0]']" parameterized="true"/> + <element name="visualSwatchOptionDefaultStoreValue" type="input" selector="[data-role='swatch-visual-options-container'] input[name='optionvisual[value][option_{{row}}][1]']" parameterized="true"/> + </section> +</sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Section/StorefrontProductInfoMainSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Section/StorefrontProductInfoMainSection.xml new file mode 100644 index 0000000000000..4da84f92e20ed --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Section/StorefrontProductInfoMainSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="StorefrontProductInfoMainSection"> + <element name="swatchOptionByLabel" type="button" selector="div.swatch-option[option-label={{opt}}]" parameterized="true"/> + <element name="selectedSwatchValue" type="text" selector="//div[contains(@class, 'swatch-attribute') and contains(., '{{attr}}')]//span[contains(@class, 'swatch-attribute-selected-option')]" parameterized="true"/> + </section> +</sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml new file mode 100644 index 0000000000000..cfd220d12b1d7 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontSwatchProductWithFileCustomOptionTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Add configurable product to cart"/> + <title value="Correct error message and redirect with invalid file option"/> + <description value="Configurable product with swatch option and file custom option. When adding to cart with an invalid filetype, the correct error message is shown, and options remain selected."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-93101"/> + <group value="ConfigurableProduct"/> + <group value="Swatches"/> + </annotations> + + <before> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create a configurable swatch product via the UI --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="searchAndSelectCategory"/> + <!--Add swatch attribute to configurable product--> + <actionGroup ref="AddVisualSwatchToProductActionGroup" stepKey="addSwatchToProduct"/> + <!--Add custom option to configurable product--> + <actionGroup ref="AddProductCustomOptionFile" stepKey="addCustomOptionToProduct"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + + <!--Go to storefront--> + <amOnPage url="" stepKey="goToHomePage"/> + <waitForPageLoad stepKey="waitForHomePageLoad"/> + <click selector="{{StorefrontNavigationSection.topCategory($$createCategory.name$$)}}" stepKey="goToCategoryStorefront"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="$$createCategory.name$$" stepKey="seeOnCategoryPage"/> + <!--Add configurable product to cart--> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductTitleByName(BaseConfigurableProduct.name)}}" stepKey="hoverProductInGrid"/> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(BaseConfigurableProduct.name)}}" stepKey="tryAddToCartFromCategoryPage"/> + <waitForPageLoad stepKey="waitForRedirectToProductPage"/> + <seeInCurrentUrl url="{{StorefrontProductPage.url(BaseConfigurableProduct.name)}}" stepKey="seeOnProductPage"/> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel(visualSwatchOption2.default_label)}}" stepKey="clickSwatchOption"/> + <see selector="{{StorefrontProductInfoMainSection.selectedSwatchValue(visualSwatchAttribute.default_label)}}" userInput="{{visualSwatchOption2.default_label}}" stepKey="seeSwatchIsSelected"/> + + <!--Try invalid file--> + <attachFile selector="{{StorefrontProductInfoMainSection.addLinkFileUploadFile(ProductOptionFile.title)}}" userInput="lorem_ipsum.docx" stepKey="attachInvalidFile"/> + <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCartInvalidFile"/> + <waitForElementVisible selector="{{StorefrontProductPageSection.alertMessage}}" stepKey="waitForErrorMessageInvalidFile"/> + <see selector="{{StorefrontProductPageSection.messagesBlock}}" userInput="The file 'lorem_ipsum.docx' for '{{ProductOptionFile.title}}' has an invalid extension." stepKey="seeMessageInvalidFile"/> + <!--Swatch remains selected--> + <see selector="{{StorefrontProductInfoMainSection.selectedSwatchValue(visualSwatchAttribute.default_label)}}" userInput="{{visualSwatchOption2.default_label}}" stepKey="seeSwatchRemainsSelected"/> + <!--Try valid file--> + <attachFile selector="{{StorefrontProductInfoMainSection.addLinkFileUploadFile(ProductOptionFile.title)}}" userInput="{{MagentoLogo.file}}" stepKey="attachValidFile"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$132.99" stepKey="seePriceUpdated"/> + <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCartValidFile"/> + <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" stepKey="waitForSuccessMessage"/> + <see selector="{{StorefrontProductPageSection.messagesBlock}}" userInput="You added {{BaseConfigurableProduct.name}} to your shopping cart." stepKey="seeSuccessMessage"/> + + <!--Check item in cart--> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCart"/> + <waitForPageLoad stepKey="waitForCartPageLoad"/> + <seeElement selector="{{CheckoutCartProductSection.ProductLinkByName(BaseConfigurableProduct.name)}}" stepKey="seeProductInCart"/> + <see selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute(BaseConfigurableProduct.name, visualSwatchAttribute.default_label)}}" userInput="{{visualSwatchOption2.default_label}}" stepKey="seeSelectedSwatch"/> + <see selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute(BaseConfigurableProduct.name, ProductOptionFile.title)}}" userInput="{{MagentoLogo.file}}" stepKey="seeCorrectOptionFile"/> + <!--Delete cart item--> + <click selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="deleteCartItem"/> + + <!--Delete product--> + <actionGroup ref="deleteProductBySku" stepKey="deleteProduct"> + <argument name="sku" value="{{BaseConfigurableProduct.sku}}"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminDataGridTableSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminDataGridTableSection.xml index 53228559f6d7c..861fdac4ae006 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminDataGridTableSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminDataGridTableSection.xml @@ -12,6 +12,7 @@ <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)"/> <element name="columnHeader" type="button" selector="//div[@data-role='grid-wrapper']//table[contains(@class, 'data-grid')]/thead/tr/th[contains(@class, 'data-grid-th')]/span[text() = '{{label}}']" parameterized="true" timeout="30"/> <element name="column" type="text" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{col}}')]/preceding-sibling::th) +1 ]" parameterized="true"/> + <element name="rowCheckbox" type="checkbox" selector="table.data-grid tbody > tr:nth-of-type({{row}}) td.data-grid-checkbox-cell input" parameterized="true"/> <element name="row" type="text" selector="table.data-grid tbody > tr:nth-of-type({{row}})" parameterized="true"/> <element name="rows" type="text" selector="table.data-grid tbody > tr.data-row"/> <!--Specific cell e.g. {{Section.gridCell('1', 'Name')}}--> From 948e02a6c49bb03a0912af10f751e4f3b2d00db5 Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Thu, 5 Jul 2018 16:33:46 -0500 Subject: [PATCH 0128/1171] MC-231: Customer should be able to select customisable bundle product options and set quantity for them --- dev/tests/acceptance/composer.lock | 391 +++++++++++------- .../Section/AdminProductFormBundleSection.xml | 1 + .../Section/BundleStorefrontSection.xml | 24 -- .../Section/StorefrontBundledSection.xml | 8 + .../Bundle/Test/AdminDeleteABundleProduct.xml | 2 +- .../Test/AdminMassDeleteBundleProducts.xml | 8 +- .../Test/AdminProductBundleCreationTest.xml | 2 +- .../CustomerSelectAndSetBundleOptionsTest.xml | 132 ++++++ .../EnableDisableBundleProductStatusTest.xml | 4 +- .../MassEnableDisableBundleProductsTest.xml | 4 +- 10 files changed, 382 insertions(+), 194 deletions(-) delete mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/BundleStorefrontSection.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/CustomerSelectAndSetBundleOptionsTest.xml diff --git a/dev/tests/acceptance/composer.lock b/dev/tests/acceptance/composer.lock index afe2640b442f1..51ad8271d409c 100644 --- a/dev/tests/acceptance/composer.lock +++ b/dev/tests/acceptance/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9f292f6e226938ad82d1189da7aa4024", + "content-hash": "f68d2dcdcf918eb168acb7ebe191fda9", "packages": [ { "name": "allure-framework/allure-codeception", @@ -264,16 +264,16 @@ }, { "name": "codeception/stub", - "version": "1.0.2", + "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/Codeception/Stub.git", - "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99" + "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/95fb7a36b81890dd2e5163e7ab31310df6f1bb99", - "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/681b62348837a5ef07d10d8a226f5bc358cc8805", + "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805", "shasum": "" }, "require": { @@ -293,20 +293,20 @@ "MIT" ], "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-02-18T13:56:56+00:00" + "time": "2018-05-17T09:31:08+00:00" }, { "name": "consolidation/annotated-command", - "version": "2.8.3", + "version": "2.8.4", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "8f8f5da2ca06fbd3a85f7d551c49f844b7c59437" + "reference": "651541a0b68318a2a202bda558a676e5ad92223c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/8f8f5da2ca06fbd3a85f7d551c49f844b7c59437", - "reference": "8f8f5da2ca06fbd3a85f7d551c49f844b7c59437", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/651541a0b68318a2a202bda558a676e5ad92223c", + "reference": "651541a0b68318a2a202bda558a676e5ad92223c", "shasum": "" }, "require": { @@ -318,9 +318,9 @@ "symfony/finder": "^2.5|^3|^4" }, "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4.8", - "satooshi/php-coveralls": "^1.0.2 | dev-master", + "g1a/composer-test-scenarios": "^2", + "phpunit/phpunit": "^6", + "satooshi/php-coveralls": "^2", "squizlabs/php_codesniffer": "^2.7" }, "type": "library", @@ -345,20 +345,20 @@ } ], "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2018-02-23T16:32:04+00:00" + "time": "2018-05-25T18:04:25+00:00" }, { "name": "consolidation/config", - "version": "1.0.9", + "version": "1.0.11", "source": { "type": "git", "url": "https://github.com/consolidation/config.git", - "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c" + "reference": "ede41d946078e97e7a9513aadc3352f1c26817af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/34ca8d7c1ee60a7b591b10617114cf1210a2e92c", - "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c", + "url": "https://api.github.com/repos/consolidation/config/zipball/ede41d946078e97e7a9513aadc3352f1c26817af", + "reference": "ede41d946078e97e7a9513aadc3352f1c26817af", "shasum": "" }, "require": { @@ -367,7 +367,7 @@ "php": ">=5.4.0" }, "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", + "g1a/composer-test-scenarios": "^1", "phpunit/phpunit": "^4", "satooshi/php-coveralls": "^1.0", "squizlabs/php_codesniffer": "2.*", @@ -399,20 +399,20 @@ } ], "description": "Provide configuration services for a commandline tool.", - "time": "2017-12-22T17:28:19+00:00" + "time": "2018-05-27T01:17:02+00:00" }, { "name": "consolidation/log", - "version": "1.0.5", + "version": "1.0.6", "source": { "type": "git", "url": "https://github.com/consolidation/log.git", - "reference": "dbc7c535f319a4a2d5a5077738f8eb7c10df8821" + "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/dbc7c535f319a4a2d5a5077738f8eb7c10df8821", - "reference": "dbc7c535f319a4a2d5a5077738f8eb7c10df8821", + "url": "https://api.github.com/repos/consolidation/log/zipball/dfd8189a771fe047bf3cd669111b2de5f1c79395", + "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395", "shasum": "" }, "require": { @@ -421,8 +421,9 @@ "symfony/console": "^2.8|^3|^4" }, "require-dev": { + "g1a/composer-test-scenarios": "^1", "phpunit/phpunit": "4.*", - "satooshi/php-coveralls": "dev-master", + "satooshi/php-coveralls": "^2", "squizlabs/php_codesniffer": "2.*" }, "type": "library", @@ -447,20 +448,20 @@ } ], "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", - "time": "2017-11-29T01:44:16+00:00" + "time": "2018-05-25T18:14:39+00:00" }, { "name": "consolidation/output-formatters", - "version": "3.2.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/consolidation/output-formatters.git", - "reference": "da889e4bce19f145ca4ec5b1725a946f4eb625a9" + "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/da889e4bce19f145ca4ec5b1725a946f4eb625a9", - "reference": "da889e4bce19f145ca4ec5b1725a946f4eb625a9", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", + "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", "shasum": "" }, "require": { @@ -469,7 +470,7 @@ "symfony/finder": "^2.5|^3|^4" }, "require-dev": { - "g-1-a/composer-test-scenarios": "^2", + "g1a/composer-test-scenarios": "^2", "phpunit/phpunit": "^5.7.27", "satooshi/php-coveralls": "^2", "squizlabs/php_codesniffer": "^2.7", @@ -502,25 +503,25 @@ } ], "description": "Format text by applying transformations provided by plug-in formatters.", - "time": "2018-03-20T15:18:32+00:00" + "time": "2018-05-25T18:02:34+00:00" }, { "name": "consolidation/robo", - "version": "1.2.3", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "54a13e268917b92576d75e10dca8227b95a574d9" + "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/54a13e268917b92576d75e10dca8227b95a574d9", - "reference": "54a13e268917b92576d75e10dca8227b95a574d9", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/ac563abfadf7cb7314b4e152f2b5033a6c255f6f", + "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f", "shasum": "" }, "require": { "consolidation/annotated-command": "^2.8.2", - "consolidation/config": "^1.0.1", + "consolidation/config": "^1.0.10", "consolidation/log": "~1", "consolidation/output-formatters": "^3.1.13", "grasmash/yaml-expander": "^1.3", @@ -539,7 +540,7 @@ "codeception/aspect-mock": "^1|^2.1.1", "codeception/base": "^2.3.7", "codeception/verify": "^0.3.2", - "g-1-a/composer-test-scenarios": "^2", + "g1a/composer-test-scenarios": "^2", "goaop/framework": "~2.1.2", "goaop/parser-reflection": "^1.1.0", "natxet/cssmin": "3.0.4", @@ -582,7 +583,7 @@ } ], "description": "Modern task runner", - "time": "2018-04-06T05:27:37+00:00" + "time": "2018-05-27T01:42:53+00:00" }, { "name": "container-interop/container-interop", @@ -965,34 +966,39 @@ }, { "name": "facebook/webdriver", - "version": "1.5.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/facebook/php-webdriver.git", - "reference": "86b5ca2f67173c9d34340845dd690149c886a605" + "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/86b5ca2f67173c9d34340845dd690149c886a605", - "reference": "86b5ca2f67173c9d34340845dd690149c886a605", + "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/bd8c740097eb9f2fc3735250fc1912bc811a954e", + "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e", "shasum": "" }, "require": { "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", "ext-zip": "*", "php": "^5.6 || ~7.0", "symfony/process": "^2.8 || ^3.1 || ^4.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.0", - "guzzle/guzzle": "^3.4.1", - "php-coveralls/php-coveralls": "^1.0.2", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "php-coveralls/php-coveralls": "^2.0", "php-mock/php-mock-phpunit": "^1.1", "phpunit/phpunit": "^5.7", "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", "squizlabs/php_codesniffer": "^2.6", "symfony/var-dumper": "^3.3 || ^4.0" }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" + }, "type": "library", "extra": { "branch-alias": { @@ -1016,7 +1022,7 @@ "selenium", "webdriver" ], - "time": "2017-11-15T11:08:09+00:00" + "time": "2018-05-16T17:37:13+00:00" }, { "name": "flow/jsonpath", @@ -1206,16 +1212,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.3.2", + "version": "6.3.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90" + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/68d0ea14d5a3f42a20e87632a5f84931e2709c90", - "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", "shasum": "" }, "require": { @@ -1225,7 +1231,7 @@ }, "require-dev": { "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", "psr/log": "^1.0" }, "suggest": { @@ -1267,7 +1273,7 @@ "rest", "web service" ], - "time": "2018-03-26T16:33:04+00:00" + "time": "2018-04-22T15:46:56+00:00" }, { "name": "guzzlehttp/promises", @@ -1473,22 +1479,22 @@ }, { "name": "jms/serializer", - "version": "1.11.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/schmittjoh/serializer.git", - "reference": "e7c53477ff55c21d1b1db7d062edc050a24f465f" + "reference": "93d6e03fcb71d45854cc44b5a84d645c02c5d763" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/e7c53477ff55c21d1b1db7d062edc050a24f465f", - "reference": "e7c53477ff55c21d1b1db7d062edc050a24f465f", + "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/93d6e03fcb71d45854cc44b5a84d645c02c5d763", + "reference": "93d6e03fcb71d45854cc44b5a84d645c02c5d763", "shasum": "" }, "require": { "doctrine/annotations": "^1.0", "doctrine/instantiator": "^1.0.3", - "jms/metadata": "~1.1", + "jms/metadata": "^1.3", "jms/parser-lib": "1.*", "php": "^5.5|^7.0", "phpcollection/phpcollection": "~0.1", @@ -1522,7 +1528,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-1.x": "1.11-dev" } }, "autoload": { @@ -1532,7 +1538,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "MIT" ], "authors": [ { @@ -1553,7 +1559,7 @@ "serialization", "xml" ], - "time": "2018-02-04T17:48:54+00:00" + "time": "2018-06-01T12:10:12+00:00" }, { "name": "league/container", @@ -1789,25 +1795,28 @@ }, { "name": "myclabs/deep-copy", - "version": "1.7.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" + "phpunit/phpunit": "^7.1" }, "type": "library", "autoload": { @@ -1830,20 +1839,20 @@ "object", "object graph" ], - "time": "2017-10-19T19:58:43+00:00" + "time": "2018-06-11T23:09:50+00:00" }, { "name": "paragonie/random_compat", - "version": "v2.0.12", + "version": "v2.0.17", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb" + "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/258c89a6b97de7dfaf5b8c7607d0478e236b04fb", - "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d", + "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d", "shasum": "" }, "require": { @@ -1875,10 +1884,11 @@ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ "csprng", + "polyfill", "pseudorandom", "random" ], - "time": "2018-04-04T21:24:14+00:00" + "time": "2018-07-04T16:31:37+00:00" }, { "name": "phar-io/manifest", @@ -2546,16 +2556,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.8", + "version": "6.5.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b" + "reference": "093ca5508174cd8ab8efe44fd1dde447adfdec8f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4f21a3c6b97c42952fd5c2837bb354ec0199b97b", - "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/093ca5508174cd8ab8efe44fd1dde447adfdec8f", + "reference": "093ca5508174cd8ab8efe44fd1dde447adfdec8f", "shasum": "" }, "require": { @@ -2626,20 +2636,20 @@ "testing", "xunit" ], - "time": "2018-04-10T11:38:34+00:00" + "time": "2018-07-03T06:40:40+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "5.0.6", + "version": "5.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf" + "reference": "3eaf040f20154d27d6da59ca2c6e28ac8fd56dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/33fd41a76e746b8fa96d00b49a23dadfa8334cdf", - "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3eaf040f20154d27d6da59ca2c6e28ac8fd56dce", + "reference": "3eaf040f20154d27d6da59ca2c6e28ac8fd56dce", "shasum": "" }, "require": { @@ -2685,7 +2695,7 @@ "mock", "xunit" ], - "time": "2018-01-06T05:45:45+00:00" + "time": "2018-05-29T13:50:43+00:00" }, { "name": "psr/container", @@ -3474,16 +3484,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.0.8", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "c43bfa0182363b3fd64331b5e64e467349ff4670" + "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c43bfa0182363b3fd64331b5e64e467349ff4670", - "reference": "c43bfa0182363b3fd64331b5e64e467349ff4670", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", + "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", "shasum": "" }, "require": { @@ -3500,7 +3510,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3527,20 +3537,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2018-03-19T22:35:49+00:00" + "time": "2018-06-04T17:31:56+00:00" }, { "name": "symfony/console", - "version": "v4.0.8", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "aad9a6fe47319f22748fd764f52d3a7ca6fa6b64" + "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/aad9a6fe47319f22748fd764f52d3a7ca6fa6b64", - "reference": "aad9a6fe47319f22748fd764f52d3a7ca6fa6b64", + "url": "https://api.github.com/repos/symfony/console/zipball/70591cda56b4b47c55776ac78e157c4bb6c8b43f", + "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f", "shasum": "" }, "require": { @@ -3560,7 +3570,7 @@ "symfony/process": "~3.4|~4.0" }, "suggest": { - "psr/log": "For using the console logger", + "psr/log-implementation": "For using the console logger", "symfony/event-dispatcher": "", "symfony/lock": "", "symfony/process": "" @@ -3568,7 +3578,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3595,20 +3605,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-04-03T05:24:00+00:00" + "time": "2018-05-31T10:17:53+00:00" }, { "name": "symfony/css-selector", - "version": "v4.0.8", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "03f965583147957f1ecbad7ea1c9d6fd5e525ec2" + "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/03f965583147957f1ecbad7ea1c9d6fd5e525ec2", - "reference": "03f965583147957f1ecbad7ea1c9d6fd5e525ec2", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/03ac71606ecb0b0ce792faa17d74cc32c2949ef4", + "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4", "shasum": "" }, "require": { @@ -3617,7 +3627,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3648,24 +3658,25 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2018-03-19T22:35:49+00:00" + "time": "2018-05-30T07:26:09+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.0.8", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "d6c04c7532535b5e0b63db45b543cd60818e0fbc" + "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/d6c04c7532535b5e0b63db45b543cd60818e0fbc", - "reference": "d6c04c7532535b5e0b63db45b543cd60818e0fbc", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/3350cacf151b48d903114ab8f7a4ccb23e07e10a", + "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a", "shasum": "" }, "require": { "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { @@ -3677,7 +3688,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3704,20 +3715,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-03-19T22:35:49+00:00" + "time": "2018-05-01T23:02:13+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.0.8", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "63353a71073faf08f62caab4e6889b06a787f07b" + "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/63353a71073faf08f62caab4e6889b06a787f07b", - "reference": "63353a71073faf08f62caab4e6889b06a787f07b", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2391ed210a239868e7256eb6921b1bd83f3087b5", + "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5", "shasum": "" }, "require": { @@ -3740,7 +3751,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3767,29 +3778,30 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-04-06T07:35:43+00:00" + "time": "2018-04-06T07:35:57+00:00" }, { "name": "symfony/filesystem", - "version": "v4.0.8", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21" + "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21", - "reference": "5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", + "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3816,20 +3828,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-02-22T10:50:29+00:00" + "time": "2018-05-30T07:26:09+00:00" }, { "name": "symfony/finder", - "version": "v4.0.8", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ca27c02b7a3fef4828c998c2ff9ba7aae1641c49" + "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ca27c02b7a3fef4828c998c2ff9ba7aae1641c49", - "reference": "ca27c02b7a3fef4828c998c2ff9ba7aae1641c49", + "url": "https://api.github.com/repos/symfony/finder/zipball/84714b8417d19e4ba02ea78a41a975b3efaafddb", + "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb", "shasum": "" }, "require": { @@ -3838,7 +3850,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3865,20 +3877,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-04-04T05:10:37+00:00" + "time": "2018-06-19T21:38:16+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.0.8", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "d0864a82e5891ab61d31eecbaa48bed5a09b8e6c" + "reference": "4f9c7cf962e635b0b26b14500ac046e07dbef7f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d0864a82e5891ab61d31eecbaa48bed5a09b8e6c", - "reference": "d0864a82e5891ab61d31eecbaa48bed5a09b8e6c", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/4f9c7cf962e635b0b26b14500ac046e07dbef7f3", + "reference": "4f9c7cf962e635b0b26b14500ac046e07dbef7f3", "shasum": "" }, "require": { @@ -3886,12 +3898,13 @@ "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { + "predis/predis": "~1.0", "symfony/expression-language": "~3.4|~4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3918,20 +3931,75 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-04-03T05:24:00+00:00" + "time": "2018-06-19T21:38:16+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/7cc359f1b7b80fc25ed7796be7d96adc9b354bae", + "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2018-04-30T19:57:29+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.7.0", + "version": "v1.8.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" + "reference": "3296adf6a6454a050679cde90f95350ad604b171" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", + "reference": "3296adf6a6454a050679cde90f95350ad604b171", "shasum": "" }, "require": { @@ -3943,7 +4011,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "1.8-dev" } }, "autoload": { @@ -3977,20 +4045,20 @@ "portable", "shim" ], - "time": "2018-01-30T19:27:44+00:00" + "time": "2018-04-26T10:06:28+00:00" }, { "name": "symfony/process", - "version": "v4.0.8", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d7dc1ee5dfe9f732cb1bba7310f5b99f2b7a6d25" + "reference": "1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d7dc1ee5dfe9f732cb1bba7310f5b99f2b7a6d25", - "reference": "d7dc1ee5dfe9f732cb1bba7310f5b99f2b7a6d25", + "url": "https://api.github.com/repos/symfony/process/zipball/1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a", + "reference": "1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a", "shasum": "" }, "require": { @@ -3999,7 +4067,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -4026,24 +4094,25 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-04-03T05:24:00+00:00" + "time": "2018-05-31T10:17:53+00:00" }, { "name": "symfony/yaml", - "version": "v3.4.8", + "version": "v3.4.12", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "a42f9da85c7c38d59f5e53f076fe81a091f894d0" + "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/a42f9da85c7c38d59f5e53f076fe81a091f894d0", - "reference": "a42f9da85c7c38d59f5e53f076fe81a091f894d0", + "url": "https://api.github.com/repos/symfony/yaml/zipball/c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", + "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/console": "<3.4" @@ -4084,7 +4153,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-04-03T05:14:20+00:00" + "time": "2018-05-03T23:18:14+00:00" }, { "name": "theseer/tokenizer", @@ -4128,28 +4197,28 @@ }, { "name": "vlucas/phpdotenv", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c" + "reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", - "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/6ae3e2e6494bb5e58c2decadafc3de7f1453f70a", + "reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a", "shasum": "" }, "require": { "php": ">=5.3.9" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.0" + "phpunit/phpunit": "^4.8.35 || ^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.4-dev" + "dev-master": "2.5-dev" } }, "autoload": { @@ -4159,7 +4228,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause-Attribution" + "BSD-3-Clause" ], "authors": [ { @@ -4174,7 +4243,7 @@ "env", "environment" ], - "time": "2016-09-01T10:05:43+00:00" + "time": "2018-07-01T10:25:50+00:00" }, { "name": "webmozart/assert", @@ -4233,6 +4302,8 @@ "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, - "platform": [], + "platform": { + "php": "~7.1.3||~7.2.0" + }, "platform-dev": [] } diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/AdminProductFormBundleSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/AdminProductFormBundleSection.xml index 1829e3013bcf1..da15674eeb0a4 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/AdminProductFormBundleSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/AdminProductFormBundleSection.xml @@ -68,5 +68,6 @@ <element name="searchForCategory" type="input" selector="div.action-menu._active > div.admin__action-multiselect-search-wrap input" timeout="30"/> <element name="selectCategory" type="multiselect" selector="//div[@class='action-menu _active']//label[@class='admin__action-multiselect-label']"/> <element name="categoriesLabel" type="text" selector="//div[@class='action-menu _active']//button[@data-action='close-advanced-select']"/> + <element name="userDefinedQuantity" type="checkbox" selector="[name='bundle_options[bundle_options][{{option}}][bundle_selections][{{product}}][selection_can_change_qty]'][type='checkbox']" parameterized="true"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/BundleStorefrontSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/BundleStorefrontSection.xml deleted file mode 100644 index b7ca65b28c9bb..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/BundleStorefrontSection.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="BundleStorefrontSection"> - <!--TestingForLocationOfOptions--> - <element name="bundleOptionSelector" type="button" selector="//*[@id='bundle-slide']/span"/> - <element name="bundleOptionSelection" type="button" selector="//div[@class='nested options-list']/div[2]/label[@class='label']"/> - <!--Description--> - <!--CE exclusively--> - <element name="longDescriptionText" type="text" selector="//*[@id='description']/div/div" timeout="30"/> - <element name="shortDescriptionText" type="text" selector="//div[@class='product attribute overview']" timeout="30"/> - <!--NameOfProductOnProductPage--> - <element name="bundleProductName" type="text" selector="//*[@id='maincontent']//span[@itemprop='name']"/> - <!--PageNotFoundErrorMessage--> - <element name="pageNotFound" type="text" selector="//h1[@class='page-title']//span[contains(., 'Whoops, our bad...')]"/> - </section> -</sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/StorefrontBundledSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/StorefrontBundledSection.xml index 7b6efd43f2592..8b02f28dc7df3 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/StorefrontBundledSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/StorefrontBundledSection.xml @@ -21,5 +21,13 @@ <element name="nthOptionDiv" type="block" selector="#product-options-wrapper div.field.option:nth-of-type({{var}})" parameterized="true"/> <element name="nthItemOptionsTitle" type="text" selector="dl.item-options dt:nth-of-type({{var}})" parameterized="true"/> <element name="nthItemOptionsValue" type="text" selector="dl.item-options dd:nth-of-type({{var}})" parameterized="true"/> + <element name="bundleProductName" type="text" selector="//*[@id='maincontent']//span[@itemprop='name']"/> + <element name="pageNotFound" type="text" selector="//h1[@class='page-title']//span[contains(., 'Whoops, our bad...')]"/> + <element name="dropDownOptionOneProducts" type="select" selector="//label//span[contains(text(), 'Option One')]/../..//div[@class='control']//select"/> + <element name="dropDownOptionOneQuantity" type="input" selector="//span[contains(text(), 'Option One')]/../..//input"/> + <element name="radioButtonOptionTwoProducts" type="checkbox" selector="//label//span[contains(text(), 'Option Two')]/../..//div[@class='control']//div[@class='field choice'][{{productNumber}}]/input" parameterized="true"/> + <element name="radioButtonOptionTwoQuantity" type="input" selector="//label//span[contains(text(), 'Option Two')]/../..//div[@class='control']//div[@class='field qty qty-holder']//input"/> + <element name="checkboxOptionThreeProducts" type="checkbox" selector="//label//span[contains(text(), 'Option Three')]/../..//div[@class='control']//div[@class='field choice'][{{productNumber}}]/input" parameterized="true"/> + <element name="multiselectOptionFourProducts" type="multiselect" selector="//label//span[contains(text(), 'Option Four')]/../..//select[@multiple='multiple']"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminDeleteABundleProduct.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminDeleteABundleProduct.xml index 5c2244e85a061..b30097e0cc086 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminDeleteABundleProduct.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminDeleteABundleProduct.xml @@ -82,6 +82,6 @@ <!--Testing deletion of product--> <amOnPage url="{{BundleProduct.urlKey}}.html" stepKey="GoToProductPageAgain"/> <waitForPageLoad stepKey="WaitForProductPageToLoadToShowElement"/> - <dontSeeElement selector="{{BundleStorefrontSection.bundleProductName}}" stepKey="LookingForNameOfProductTwo"/> + <dontSeeElement selector="{{StorefrontBundledSection.bundleProductName}}" stepKey="LookingForNameOfProductTwo"/> </test> </tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminMassDeleteBundleProducts.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminMassDeleteBundleProducts.xml index ca692e5d9374d..86eecba0772b0 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminMassDeleteBundleProducts.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminMassDeleteBundleProducts.xml @@ -133,11 +133,11 @@ <!--Testing deletion of products--> <amOnPage url="{{BundleProduct.urlKey}}.html" stepKey="GoToProductPageAgain"/> <waitForPageLoad stepKey="WaitForProductPageToLoadToShowElement"/> - <dontSeeElement stepKey="LookingForNameOfProduct" selector="{{BundleStorefrontSection.bundleProductName}}"/> - <seeElement stepKey="LookingForPageNotFoundMessage" selector="{{BundleStorefrontSection.pageNotFound}}"/> + <dontSeeElement stepKey="LookingForNameOfProduct" selector="{{StorefrontBundledSection.bundleProductName}}"/> + <seeElement stepKey="LookingForPageNotFoundMessage" selector="{{StorefrontBundledSection.pageNotFound}}"/> <amOnPage url="{{BundleProduct.urlKey2}}.html" stepKey="GoToProductPageAgain2"/> <waitForPageLoad stepKey="WaitForProductPageToLoadToShowElement2"/> - <dontSeeElement stepKey="LookingForNameOfProduct2" selector="{{BundleStorefrontSection.bundleProductName}}"/> - <seeElement stepKey="LookingForPageNotFoundMessage2" selector="{{BundleStorefrontSection.pageNotFound}}"/> + <dontSeeElement stepKey="LookingForNameOfProduct2" selector="{{StorefrontBundledSection.bundleProductName}}"/> + <seeElement stepKey="LookingForPageNotFoundMessage2" selector="{{StorefrontBundledSection.pageNotFound}}"/> </test> </tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminProductBundleCreationTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminProductBundleCreationTest.xml index 2641919e204fa..5ab38cdf1e64c 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminProductBundleCreationTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminProductBundleCreationTest.xml @@ -72,6 +72,6 @@ <!--Test Assertion - on correct page/page has been published--> <waitForPageLoad stepKey="waitForBundleProductPageToLoad"/> - <seeElement stepKey="LookingForNameOfProduct" selector="{{BundleStorefrontSection.bundleProductName}}"/> + <seeElement stepKey="LookingForNameOfProduct" selector="{{StorefrontBundledSection.bundleProductName}}"/> </test> </tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/CustomerSelectAndSetBundleOptionsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/CustomerSelectAndSetBundleOptionsTest.xml new file mode 100644 index 0000000000000..eff22a561f044 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/CustomerSelectAndSetBundleOptionsTest.xml @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="CustomerSelectAndSetBundleOptionsTest"> + <annotations> + <features value="Bundle"/> + <stories value="Bundle product details page"/> + <title value="Customer should be able to select customisable bundle product options and set quantity for them"/> + <description value="Customer should be able to select customisable bundle product options and set quantity for them"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-231"/> + <group value="Bundle"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="logout"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!-- Start creating a bundle product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="waitForProductList"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillNameAndSku"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <!-- Add Option One, a "Drop-down" type option --> + <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts1"> + <argument name="x" value="0"/> + <argument name="n" value="1"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="optionTitle" value="Option One"/> + <argument name="inputType" value="select"/> + </actionGroup> + <checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('0', '0')}}" stepKey="userDefinedQuantitiyOption0Product0"/> + <checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('0', '1')}}" stepKey="userDefinedQuantitiyOption0Product1"/> + + <!-- Add Option Two, a "Radio Buttons" type option --> + <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts2"> + <argument name="x" value="1"/> + <argument name="n" value="2"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="optionTitle" value="Option Two"/> + <argument name="inputType" value="radio"/> + </actionGroup> + <checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('1', '0')}}" stepKey="userDefinedQuantitiyOption1Product0"/> + <checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('1', '1')}}" stepKey="userDefinedQuantitiyOption1Product1"/> + + <!-- Add Option Three, a "Checkbox" type option --> + <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts3"> + <argument name="x" value="2"/> + <argument name="n" value="3"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="optionTitle" value="Option Three"/> + <argument name="inputType" value="checkbox"/> + </actionGroup> + + <!-- Add Option Four, a "Multi Select" type option --> + <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts4"> + <argument name="x" value="3"/> + <argument name="n" value="4"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="optionTitle" value="Option Four"/> + <argument name="inputType" value="multi"/> + </actionGroup> + + <!-- Save product and go to storefront --> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> + <waitForPageLoad stepKey="waitForStorefront"/> + <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> + + <!--Select options - set quantities--> + + <!--"Drop-down" type option--> + <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts}}" userInput="$$simpleProduct1.sku$$ +$123.00" stepKey="selectOption0Product0"/> + <seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts}}" userInput="$$simpleProduct1.sku$$ +$123.00" stepKey="checkOption0Product0"/> + <fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity}}" userInput="3" stepKey="fillQuantity00"/> + <seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity}}" userInput="3" stepKey="checkQuantity00"/> + + <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts}}" userInput="$$simpleProduct2.sku$$ +$123.00" stepKey="selectOption0Product1"/> + <seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts}}" userInput="$$simpleProduct2.sku$$ +$123.00" stepKey="checkOption0Product1"/> + <fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity}}" userInput="3" stepKey="fillQuantity01"/> + <seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity}}" userInput="3" stepKey="checkQuantity01"/> + + <!--"Radio Buttons" type option--> + <checkOption selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('1')}}" stepKey="selectOption1Product0"/> + <seeCheckboxIsChecked selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('1')}}" stepKey="checkOption1Product0"/> + <fillField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity}}" userInput="3" stepKey="fillQuantity10"/> + <seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity}}" userInput="3" stepKey="checkQuantity10"/> + + <checkOption selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('2')}}" stepKey="selectOption1Product1"/> + <seeCheckboxIsChecked selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('2')}}" stepKey="checkOption1Product1"/> + <fillField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity}}" userInput="3" stepKey="fillQuantity11"/> + <seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity}}" userInput="3" stepKey="checkQuantity11"/> + + <!--"Checkbox" type option--> + <!--This option does not support user defined quantities--> + <checkOption selector="{{StorefrontBundledSection.checkboxOptionThreeProducts('1')}}" stepKey="selectOption2Product0"/> + <seeCheckboxIsChecked selector="{{StorefrontBundledSection.checkboxOptionThreeProducts('1')}}" stepKey="checkOption2Product0"/> + + <checkOption selector="{{StorefrontBundledSection.checkboxOptionThreeProducts('2')}}" stepKey="selectOption2Product1"/> + <seeCheckboxIsChecked selector="{{StorefrontBundledSection.checkboxOptionThreeProducts('2')}}" stepKey="checkOption2Product1"/> + + <!--"Multi Select" type option--> + <!--This option does not support user defined quantities--> + <selectOption selector="{{StorefrontBundledSection.multiselectOptionFourProducts}}" userInput="$$simpleProduct1.sku$$ +$123.00" stepKey="selectOption3Product0"/> + <seeOptionIsSelected selector="{{StorefrontBundledSection.multiselectOptionFourProducts}}" userInput="$$simpleProduct1.sku$$ +$123.00" stepKey="checkOption3Product0"/> + + <selectOption selector="{{StorefrontBundledSection.multiselectOptionFourProducts}}" userInput="$$simpleProduct2.sku$$ +$123.00" stepKey="selectOption3Product1"/> + <seeOptionIsSelected selector="{{StorefrontBundledSection.multiselectOptionFourProducts}}" userInput="$$simpleProduct2.sku$$ +$123.00" stepKey="checkOption3Product1"/> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/EnableDisableBundleProductStatusTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/EnableDisableBundleProductStatusTest.xml index 5588d30619c48..46c2898305814 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/EnableDisableBundleProductStatusTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/EnableDisableBundleProductStatusTest.xml @@ -72,7 +72,7 @@ <!--Go to page--> <amOnPage url="{{BundleProduct.urlKey}}.html" stepKey="GoToProductPage"/> <waitForPageLoad stepKey="waitForBundleProductPageToLoad"/> - <seeElement stepKey="LookingForNameOfProduct" selector="{{BundleStorefrontSection.bundleProductName}}"/> + <seeElement stepKey="LookingForNameOfProduct" selector="{{StorefrontBundledSection.bundleProductName}}"/> <!--Testing disabled view--> <actionGroup ref="FindProductToEdit" stepKey="FindProductEditPage"/> @@ -82,6 +82,6 @@ <waitForPageLoad stepKey="PauseForSave"/> <amOnPage url="{{BundleProduct.urlKey}}.html" stepKey="GoToProductPageAgain"/> <waitForPageLoad stepKey="WaitForProductPageToLoadToShowElement"/> - <dontSeeElement stepKey="LookingForNameOfProductTwo" selector="{{BundleStorefrontSection.bundleProductName}}"/> + <dontSeeElement stepKey="LookingForNameOfProductTwo" selector="{{StorefrontBundledSection.bundleProductName}}"/> </test> </tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/MassEnableDisableBundleProductsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/MassEnableDisableBundleProductsTest.xml index 2754cbcccab7c..a931b27d6d470 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/MassEnableDisableBundleProductsTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/MassEnableDisableBundleProductsTest.xml @@ -137,7 +137,7 @@ <!--Confirm bundle products have been disabled--> <amOnPage url="{{BundleProduct.urlKey2}}.html" stepKey="GoToProductPage"/> <waitForPageLoad stepKey="WaitForProductPageToLoadToShowElement"/> - <dontSeeElement stepKey="LookingForNameOfProductDisabled" selector="{{BundleStorefrontSection.bundleProductName}}"/> + <dontSeeElement stepKey="LookingForNameOfProductDisabled" selector="{{StorefrontBundledSection.bundleProductName}}"/> <!--Enabling bundle products--> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="GoToCatalogPageChangingView"/> @@ -154,6 +154,6 @@ <!--Confirm bundle products have been enabled--> <amOnPage url="{{BundleProduct.urlKey2}}.html" stepKey="GoToProductPageEnabled"/> <waitForPageLoad stepKey="waitForBundleProductPageToLoad"/> - <seeElement stepKey="LookingForNameOfProduct" selector="{{BundleStorefrontSection.bundleProductName}}"/> + <seeElement stepKey="LookingForNameOfProduct" selector="{{StorefrontBundledSection.bundleProductName}}"/> </test> </tests> From e062cd04fd38575ffadff83fdf45ffeca2abf886 Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Thu, 5 Jul 2018 16:41:56 -0500 Subject: [PATCH 0129/1171] MC-231: Customer should be able to select customisable bundle product options and set quantity for them --- dev/tests/acceptance/composer.lock | 4309 ---------------------------- 1 file changed, 4309 deletions(-) delete mode 100644 dev/tests/acceptance/composer.lock diff --git a/dev/tests/acceptance/composer.lock b/dev/tests/acceptance/composer.lock deleted file mode 100644 index 51ad8271d409c..0000000000000 --- a/dev/tests/acceptance/composer.lock +++ /dev/null @@ -1,4309 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "f68d2dcdcf918eb168acb7ebe191fda9", - "packages": [ - { - "name": "allure-framework/allure-codeception", - "version": "1.2.7", - "source": { - "type": "git", - "url": "https://github.com/allure-framework/allure-codeception.git", - "reference": "48598f4b4603b50b663bfe977260113a40912131" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/allure-framework/allure-codeception/zipball/48598f4b4603b50b663bfe977260113a40912131", - "reference": "48598f4b4603b50b663bfe977260113a40912131", - "shasum": "" - }, - "require": { - "allure-framework/allure-php-api": "~1.1.0", - "codeception/codeception": "~2.1", - "php": ">=5.4.0", - "symfony/filesystem": ">=2.6", - "symfony/finder": ">=2.6" - }, - "type": "library", - "autoload": { - "psr-0": { - "Yandex": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Ivan Krutov", - "email": "vania-pooh@yandex-team.ru", - "role": "Developer" - } - ], - "description": "A Codeception adapter for Allure report.", - "homepage": "http://allure.qatools.ru/", - "keywords": [ - "allure", - "attachments", - "cases", - "codeception", - "report", - "steps", - "testing" - ], - "time": "2018-03-07T11:18:27+00:00" - }, - { - "name": "allure-framework/allure-php-api", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/allure-framework/allure-php-adapter-api.git", - "reference": "a462a0da121681577033e13c123b6cc4e89cdc64" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/allure-framework/allure-php-adapter-api/zipball/a462a0da121681577033e13c123b6cc4e89cdc64", - "reference": "a462a0da121681577033e13c123b6cc4e89cdc64", - "shasum": "" - }, - "require": { - "jms/serializer": ">=0.16.0", - "moontoast/math": ">=1.1.0", - "php": ">=5.4.0", - "phpunit/phpunit": ">=4.0.0", - "ramsey/uuid": ">=3.0.0", - "symfony/http-foundation": ">=2.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Yandex": [ - "src/", - "test/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Ivan Krutov", - "email": "vania-pooh@yandex-team.ru", - "role": "Developer" - } - ], - "description": "PHP API for Allure adapter", - "homepage": "http://allure.qatools.ru/", - "keywords": [ - "allure", - "api", - "php", - "report" - ], - "time": "2016-12-07T12:15:46+00:00" - }, - { - "name": "behat/gherkin", - "version": "v4.4.5", - "source": { - "type": "git", - "url": "https://github.com/Behat/Gherkin.git", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "~4.5|~5", - "symfony/phpunit-bridge": "~2.7|~3", - "symfony/yaml": "~2.3|~3" - }, - "suggest": { - "symfony/yaml": "If you want to parse features, represented in YAML files" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.4-dev" - } - }, - "autoload": { - "psr-0": { - "Behat\\Gherkin": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Gherkin DSL parser for PHP 5.3", - "homepage": "http://behat.org/", - "keywords": [ - "BDD", - "Behat", - "Cucumber", - "DSL", - "gherkin", - "parser" - ], - "time": "2016-10-30T11:50:56+00:00" - }, - { - "name": "codeception/codeception", - "version": "2.3.9", - "source": { - "type": "git", - "url": "https://github.com/Codeception/Codeception.git", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e", - "shasum": "" - }, - "require": { - "behat/gherkin": "~4.4.0", - "codeception/stub": "^1.0", - "ext-json": "*", - "ext-mbstring": "*", - "facebook/webdriver": ">=1.1.3 <2.0", - "guzzlehttp/guzzle": ">=4.1.4 <7.0", - "guzzlehttp/psr7": "~1.0", - "php": ">=5.4.0 <8.0", - "phpunit/php-code-coverage": ">=2.2.4 <6.0", - "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", - "sebastian/comparator": ">1.1 <3.0", - "sebastian/diff": ">=1.4 <3.0", - "symfony/browser-kit": ">=2.7 <5.0", - "symfony/console": ">=2.7 <5.0", - "symfony/css-selector": ">=2.7 <5.0", - "symfony/dom-crawler": ">=2.7 <5.0", - "symfony/event-dispatcher": ">=2.7 <5.0", - "symfony/finder": ">=2.7 <5.0", - "symfony/yaml": ">=2.7 <5.0" - }, - "require-dev": { - "codeception/specify": "~0.3", - "facebook/graph-sdk": "~5.3", - "flow/jsonpath": "~0.2", - "monolog/monolog": "~1.8", - "pda/pheanstalk": "~3.0", - "php-amqplib/php-amqplib": "~2.4", - "predis/predis": "^1.0", - "squizlabs/php_codesniffer": "~2.0", - "symfony/process": ">=2.7 <5.0", - "vlucas/phpdotenv": "^2.4.0" - }, - "suggest": { - "aws/aws-sdk-php": "For using AWS Auth in REST module and Queue module", - "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests", - "codeception/specify": "BDD-style code blocks", - "codeception/verify": "BDD-style assertions", - "flow/jsonpath": "For using JSONPath in REST module", - "league/factory-muffin": "For DataFactory module", - "league/factory-muffin-faker": "For Faker support in DataFactory module", - "phpseclib/phpseclib": "for SFTP option in FTP Module", - "stecman/symfony-console-completion": "For BASH autocompletion", - "symfony/phpunit-bridge": "For phpunit-bridge support" - }, - "bin": [ - "codecept" - ], - "type": "library", - "extra": { - "branch-alias": [] - }, - "autoload": { - "psr-4": { - "Codeception\\": "src\\Codeception", - "Codeception\\Extension\\": "ext" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk", - "email": "davert@mail.ua", - "homepage": "http://codegyre.com" - } - ], - "description": "BDD-style testing framework", - "homepage": "http://codeception.com/", - "keywords": [ - "BDD", - "TDD", - "acceptance testing", - "functional testing", - "unit testing" - ], - "time": "2018-02-26T23:29:41+00:00" - }, - { - "name": "codeception/stub", - "version": "1.0.4", - "source": { - "type": "git", - "url": "https://github.com/Codeception/Stub.git", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/681b62348837a5ef07d10d8a226f5bc358cc8805", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805", - "shasum": "" - }, - "require": { - "phpunit/phpunit-mock-objects": ">2.3 <7.0" - }, - "require-dev": { - "phpunit/phpunit": ">=4.8 <8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codeception\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-05-17T09:31:08+00:00" - }, - { - "name": "consolidation/annotated-command", - "version": "2.8.4", - "source": { - "type": "git", - "url": "https://github.com/consolidation/annotated-command.git", - "reference": "651541a0b68318a2a202bda558a676e5ad92223c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/651541a0b68318a2a202bda558a676e5ad92223c", - "reference": "651541a0b68318a2a202bda558a676e5ad92223c", - "shasum": "" - }, - "require": { - "consolidation/output-formatters": "^3.1.12", - "php": ">=5.4.0", - "psr/log": "^1", - "symfony/console": "^2.8|^3|^4", - "symfony/event-dispatcher": "^2.5|^3|^4", - "symfony/finder": "^2.5|^3|^4" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^2", - "phpunit/phpunit": "^6", - "satooshi/php-coveralls": "^2", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\AnnotatedCommand\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2018-05-25T18:04:25+00:00" - }, - { - "name": "consolidation/config", - "version": "1.0.11", - "source": { - "type": "git", - "url": "https://github.com/consolidation/config.git", - "reference": "ede41d946078e97e7a9513aadc3352f1c26817af" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/ede41d946078e97e7a9513aadc3352f1c26817af", - "reference": "ede41d946078e97e7a9513aadc3352f1c26817af", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "grasmash/expander": "^1", - "php": ">=5.4.0" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4", - "satooshi/php-coveralls": "^1.0", - "squizlabs/php_codesniffer": "2.*", - "symfony/console": "^2.5|^3|^4", - "symfony/yaml": "^2.8.11|^3|^4" - }, - "suggest": { - "symfony/yaml": "Required to use Consolidation\\Config\\Loader\\YamlConfigLoader" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\Config\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Provide configuration services for a commandline tool.", - "time": "2018-05-27T01:17:02+00:00" - }, - { - "name": "consolidation/log", - "version": "1.0.6", - "source": { - "type": "git", - "url": "https://github.com/consolidation/log.git", - "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/dfd8189a771fe047bf3cd669111b2de5f1c79395", - "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395", - "shasum": "" - }, - "require": { - "php": ">=5.5.0", - "psr/log": "~1.0", - "symfony/console": "^2.8|^3|^4" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^1", - "phpunit/phpunit": "4.*", - "satooshi/php-coveralls": "^2", - "squizlabs/php_codesniffer": "2.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", - "time": "2018-05-25T18:14:39+00:00" - }, - { - "name": "consolidation/output-formatters", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/consolidation/output-formatters.git", - "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", - "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "symfony/console": "^2.8|^3|^4", - "symfony/finder": "^2.5|^3|^4" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^2", - "phpunit/phpunit": "^5.7.27", - "satooshi/php-coveralls": "^2", - "squizlabs/php_codesniffer": "^2.7", - "symfony/console": "3.2.3", - "symfony/var-dumper": "^2.8|^3|^4", - "victorjonsson/markdowndocs": "^1.3" - }, - "suggest": { - "symfony/var-dumper": "For using the var_dump formatter" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\OutputFormatters\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Format text by applying transformations provided by plug-in formatters.", - "time": "2018-05-25T18:02:34+00:00" - }, - { - "name": "consolidation/robo", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/consolidation/Robo.git", - "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/ac563abfadf7cb7314b4e152f2b5033a6c255f6f", - "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f", - "shasum": "" - }, - "require": { - "consolidation/annotated-command": "^2.8.2", - "consolidation/config": "^1.0.10", - "consolidation/log": "~1", - "consolidation/output-formatters": "^3.1.13", - "grasmash/yaml-expander": "^1.3", - "league/container": "^2.2", - "php": ">=5.5.0", - "symfony/console": "^2.8|^3|^4", - "symfony/event-dispatcher": "^2.5|^3|^4", - "symfony/filesystem": "^2.5|^3|^4", - "symfony/finder": "^2.5|^3|^4", - "symfony/process": "^2.5|^3|^4" - }, - "replace": { - "codegyre/robo": "< 1.0" - }, - "require-dev": { - "codeception/aspect-mock": "^1|^2.1.1", - "codeception/base": "^2.3.7", - "codeception/verify": "^0.3.2", - "g1a/composer-test-scenarios": "^2", - "goaop/framework": "~2.1.2", - "goaop/parser-reflection": "^1.1.0", - "natxet/cssmin": "3.0.4", - "nikic/php-parser": "^3.1.5", - "patchwork/jsqueeze": "~2", - "pear/archive_tar": "^1.4.2", - "phpunit/php-code-coverage": "~2|~4", - "satooshi/php-coveralls": "^2", - "squizlabs/php_codesniffer": "^2.8" - }, - "suggest": { - "henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch", - "natxet/CssMin": "For minifying CSS files in taskMinify", - "patchwork/jsqueeze": "For minifying JS files in taskMinify", - "pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively." - }, - "bin": [ - "robo" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev", - "dev-state": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Robo\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Davert", - "email": "davert.php@resend.cc" - } - ], - "description": "Modern task runner", - "time": "2018-05-27T01:42:53+00:00" - }, - { - "name": "container-interop/container-interop", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/container-interop/container-interop.git", - "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", - "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", - "shasum": "" - }, - "require": { - "psr/container": "^1.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Interop\\Container\\": "src/Interop/Container/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", - "homepage": "https://github.com/container-interop/container-interop", - "time": "2017-02-14T19:40:03+00:00" - }, - { - "name": "dflydev/dot-access-data", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/3fbd874921ab2c041e899d044585a2ab9795df8a", - "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Dflydev\\DotAccessData": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dragonfly Development Inc.", - "email": "info@dflydev.com", - "homepage": "http://dflydev.com" - }, - { - "name": "Beau Simensen", - "email": "beau@dflydev.com", - "homepage": "http://beausimensen.com" - }, - { - "name": "Carlos Frutos", - "email": "carlos@kiwing.it", - "homepage": "https://github.com/cfrutos" - } - ], - "description": "Given a deep data structure, access data by dot notation.", - "homepage": "https://github.com/dflydev/dflydev-dot-access-data", - "keywords": [ - "access", - "data", - "dot", - "notation" - ], - "time": "2017-01-20T21:14:22+00:00" - }, - { - "name": "doctrine/annotations", - "version": "v1.6.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", - "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", - "shasum": "" - }, - "require": { - "doctrine/lexer": "1.*", - "php": "^7.1" - }, - "require-dev": { - "doctrine/cache": "1.*", - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "time": "2017-12-06T07:11:42+00:00" - }, - { - "name": "doctrine/collections", - "version": "v1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/collections.git", - "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/a01ee38fcd999f34d9bfbcee59dbda5105449cbf", - "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "doctrine/coding-standard": "~0.1@dev", - "phpunit/phpunit": "^5.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\Collections\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Collections Abstraction library", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "array", - "collections", - "iterator" - ], - "time": "2017-07-22T10:37:32+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "^6.2.3", - "squizlabs/php_codesniffer": "^3.0.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", - "keywords": [ - "constructor", - "instantiate" - ], - "time": "2017-07-22T11:58:36+00:00" - }, - { - "name": "doctrine/lexer", - "version": "v1.0.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\Lexer\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "lexer", - "parser" - ], - "time": "2014-09-09T13:34:57+00:00" - }, - { - "name": "epfremme/swagger-php", - "version": "v2.0.0", - "source": { - "type": "git", - "url": "https://github.com/epfremmer/swagger-php.git", - "reference": "eee28a442b7e6220391ec953d3c9b936354f23bc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/epfremmer/swagger-php/zipball/eee28a442b7e6220391ec953d3c9b936354f23bc", - "reference": "eee28a442b7e6220391ec953d3c9b936354f23bc", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^1.2", - "doctrine/collections": "^1.3", - "jms/serializer": "^1.1", - "php": ">=5.5", - "phpoption/phpoption": "^1.1", - "symfony/yaml": "^2.7|^3.1" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "~4.8|~5.0", - "satooshi/php-coveralls": "^1.0" - }, - "type": "package", - "autoload": { - "psr-4": { - "Epfremme\\Swagger\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Edward Pfremmer", - "email": "epfremme@nerdery.com" - } - ], - "description": "Library for parsing swagger documentation into PHP entities for use in testing and code generation", - "time": "2016-09-26T17:24:17+00:00" - }, - { - "name": "facebook/webdriver", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/facebook/php-webdriver.git", - "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/bd8c740097eb9f2fc3735250fc1912bc811a954e", - "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "ext-zip": "*", - "php": "^5.6 || ~7.0", - "symfony/process": "^2.8 || ^3.1 || ^4.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.0", - "jakub-onderka/php-parallel-lint": "^0.9.2", - "php-coveralls/php-coveralls": "^2.0", - "php-mock/php-mock-phpunit": "^1.1", - "phpunit/phpunit": "^5.7", - "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", - "squizlabs/php_codesniffer": "^2.6", - "symfony/var-dumper": "^3.3 || ^4.0" - }, - "suggest": { - "ext-SimpleXML": "For Firefox profile creation" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-community": "1.5-dev" - } - }, - "autoload": { - "psr-4": { - "Facebook\\WebDriver\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "description": "A PHP client for Selenium WebDriver", - "homepage": "https://github.com/facebook/php-webdriver", - "keywords": [ - "facebook", - "php", - "selenium", - "webdriver" - ], - "time": "2018-05-16T17:37:13+00:00" - }, - { - "name": "flow/jsonpath", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/FlowCommunications/JSONPath.git", - "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FlowCommunications/JSONPath/zipball/f0222818d5c938e4ab668ab2e2c079bd51a27112", - "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "peekmo/jsonpath": "dev-master", - "phpunit/phpunit": "^4.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Flow\\JSONPath": "src/", - "Flow\\JSONPath\\Test": "tests/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Stephen Frank", - "email": "stephen@flowsa.com" - } - ], - "description": "JSONPath implementation for parsing, searching and flattening arrays", - "time": "2018-03-04T16:39:47+00:00" - }, - { - "name": "fzaninotto/faker", - "version": "v1.7.1", - "source": { - "type": "git", - "url": "https://github.com/fzaninotto/Faker.git", - "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", - "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "ext-intl": "*", - "phpunit/phpunit": "^4.0 || ^5.0", - "squizlabs/php_codesniffer": "^1.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } - }, - "autoload": { - "psr-4": { - "Faker\\": "src/Faker/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "François Zaninotto" - } - ], - "description": "Faker is a PHP library that generates fake data for you.", - "keywords": [ - "data", - "faker", - "fixtures" - ], - "time": "2017-08-15T16:48:10+00:00" - }, - { - "name": "grasmash/expander", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/grasmash/expander.git", - "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/grasmash/expander/zipball/95d6037344a4be1dd5f8e0b0b2571a28c397578f", - "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4" - }, - "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4|^5.5.4", - "satooshi/php-coveralls": "^1.0.2|dev-master", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Grasmash\\Expander\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthew Grasmick" - } - ], - "description": "Expands internal property references in PHP arrays file.", - "time": "2017-12-21T22:14:55+00:00" - }, - { - "name": "grasmash/yaml-expander", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/grasmash/yaml-expander.git", - "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/grasmash/yaml-expander/zipball/3f0f6001ae707a24f4d9733958d77d92bf9693b1", - "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4", - "symfony/yaml": "^2.8.11|^3|^4" - }, - "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4.8|^5.5.4", - "satooshi/php-coveralls": "^1.0.2|dev-master", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Grasmash\\YamlExpander\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthew Grasmick" - } - ], - "description": "Expands internal property references in a yaml file.", - "time": "2017-12-16T16:06:03+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "6.3.3", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "shasum": "" - }, - "require": { - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4", - "php": ">=5.5" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" - }, - "suggest": { - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.3-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" - ], - "time": "2018-04-22T15:46:56+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "v1.3.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "shasum": "" - }, - "require": { - "php": ">=5.5.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "time": "2016-12-20T10:07:11+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "1.4.2", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0" - }, - "provide": { - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Schultze", - "homepage": "https://github.com/Tobion" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "request", - "response", - "stream", - "uri", - "url" - ], - "time": "2017-03-20T17:10:46+00:00" - }, - { - "name": "jms/metadata", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/metadata.git", - "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/6a06970a10e0a532fb52d3959547123b84a3b3ab", - "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "doctrine/cache": "~1.0", - "symfony/cache": "~3.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5.x-dev" - } - }, - "autoload": { - "psr-0": { - "Metadata\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Class/method/property metadata management in PHP", - "keywords": [ - "annotations", - "metadata", - "xml", - "yaml" - ], - "time": "2016-12-05T10:18:33+00:00" - }, - { - "name": "jms/parser-lib", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/parser-lib.git", - "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/parser-lib/zipball/c509473bc1b4866415627af0e1c6cc8ac97fa51d", - "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d", - "shasum": "" - }, - "require": { - "phpoption/phpoption": ">=0.9,<2.0-dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "JMS\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache2" - ], - "description": "A library for easily creating recursive-descent parsers.", - "time": "2012-11-18T18:08:43+00:00" - }, - { - "name": "jms/serializer", - "version": "1.12.1", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/serializer.git", - "reference": "93d6e03fcb71d45854cc44b5a84d645c02c5d763" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/93d6e03fcb71d45854cc44b5a84d645c02c5d763", - "reference": "93d6e03fcb71d45854cc44b5a84d645c02c5d763", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^1.0", - "doctrine/instantiator": "^1.0.3", - "jms/metadata": "^1.3", - "jms/parser-lib": "1.*", - "php": "^5.5|^7.0", - "phpcollection/phpcollection": "~0.1", - "phpoption/phpoption": "^1.1" - }, - "conflict": { - "twig/twig": "<1.12" - }, - "require-dev": { - "doctrine/orm": "~2.1", - "doctrine/phpcr-odm": "^1.3|^2.0", - "ext-pdo_sqlite": "*", - "jackalope/jackalope-doctrine-dbal": "^1.1.5", - "phpunit/phpunit": "^4.8|^5.0", - "propel/propel1": "~1.7", - "psr/container": "^1.0", - "symfony/dependency-injection": "^2.7|^3.3|^4.0", - "symfony/expression-language": "^2.6|^3.0", - "symfony/filesystem": "^2.1", - "symfony/form": "~2.1|^3.0", - "symfony/translation": "^2.1|^3.0", - "symfony/validator": "^2.2|^3.0", - "symfony/yaml": "^2.1|^3.0", - "twig/twig": "~1.12|~2.0" - }, - "suggest": { - "doctrine/cache": "Required if you like to use cache functionality.", - "doctrine/collections": "Required if you like to use doctrine collection types as ArrayCollection.", - "symfony/yaml": "Required if you'd like to serialize data to YAML format." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.11-dev" - } - }, - "autoload": { - "psr-0": { - "JMS\\Serializer": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - }, - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.", - "homepage": "http://jmsyst.com/libs/serializer", - "keywords": [ - "deserialization", - "jaxb", - "json", - "serialization", - "xml" - ], - "time": "2018-06-01T12:10:12+00:00" - }, - { - "name": "league/container", - "version": "2.4.1", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/container.git", - "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/container/zipball/43f35abd03a12977a60ffd7095efd6a7808488c0", - "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0", - "shasum": "" - }, - "require": { - "container-interop/container-interop": "^1.2", - "php": "^5.4.0 || ^7.0" - }, - "provide": { - "container-interop/container-interop-implementation": "^1.2", - "psr/container-implementation": "^1.0" - }, - "replace": { - "orno/di": "~2.0" - }, - "require-dev": { - "phpunit/phpunit": "4.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev", - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Container\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Phil Bennett", - "email": "philipobenito@gmail.com", - "homepage": "http://www.philipobenito.com", - "role": "Developer" - } - ], - "description": "A fast and intuitive dependency injection container.", - "homepage": "https://github.com/thephpleague/container", - "keywords": [ - "container", - "dependency", - "di", - "injection", - "league", - "provider", - "service" - ], - "time": "2017-05-10T09:20:27+00:00" - }, - { - "name": "magento/magento2-functional-testing-framework", - "version": "2.2.0", - "source": { - "type": "git", - "url": "git@github.com:magento/magento2-functional-testing-framework.git", - "reference": "4dd196d745bf836cbf0c5904a1df6dd241124309" - }, - "require": { - "allure-framework/allure-codeception": "~1.2.6", - "codeception/codeception": "~2.3.4", - "consolidation/robo": "^1.0.0", - "epfremme/swagger-php": "^2.0", - "flow/jsonpath": ">0.2", - "fzaninotto/faker": "^1.6", - "mustache/mustache": "~2.5", - "php": "7.0.2|7.0.4|~7.0.6|~7.1.0|~7.2.0", - "symfony/process": "^2.8 || ^3.1 || ^4.0", - "vlucas/phpdotenv": "^2.4" - }, - "require-dev": { - "brainmaestro/composer-git-hooks": "^2.3", - "codacy/coverage": "^1.4", - "codeception/aspect-mock": "^2.0", - "goaop/framework": "2.1.2", - "php-coveralls/php-coveralls": "^1.0", - "phpmd/phpmd": "^2.6.0", - "rregeer/phpunit-coverage-check": "^0.1.4", - "sebastian/phpcpd": "~3.0", - "squizlabs/php_codesniffer": "1.5.3", - "symfony/stopwatch": "~3.4.6" - }, - "bin": [ - "bin/mftf" - ], - "type": "library", - "extra": { - "hooks": { - "pre-push": "bin/all-checks" - } - }, - "autoload": { - "psr-4": { - "Magento\\FunctionalTestingFramework\\": "src/Magento/FunctionalTestingFramework", - "MFTF\\": "dev/tests/functional/MFTF" - } - }, - "autoload-dev": { - "psr-4": { - "tests\\unit\\": "dev/tests/unit" - } - }, - "scripts": { - "tests": [ - "bin/phpunit-checks" - ], - "static": [ - "bin/static-checks" - ] - }, - "license": [ - "AGPL-3.0" - ], - "description": "Magento2 Functional Testing Framework", - "keywords": [ - "automation", - "functional", - "magento", - "testing" - ], - "time": "2018-04-24T01:46:09+00:00" - }, - { - "name": "moontoast/math", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/ramsey/moontoast-math.git", - "reference": "c2792a25df5cad4ff3d760dd37078fc5b6fccc79" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ramsey/moontoast-math/zipball/c2792a25df5cad4ff3d760dd37078fc5b6fccc79", - "reference": "c2792a25df5cad4ff3d760dd37078fc5b6fccc79", - "shasum": "" - }, - "require": { - "ext-bcmath": "*", - "php": ">=5.3.3" - }, - "require-dev": { - "jakub-onderka/php-parallel-lint": "^0.9.0", - "phpunit/phpunit": "^4.7|>=5.0 <5.4", - "satooshi/php-coveralls": "^0.6.1", - "squizlabs/php_codesniffer": "^2.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Moontoast\\Math\\": "src/Moontoast/Math/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" - } - ], - "description": "A mathematics library, providing functionality for large numbers", - "homepage": "https://github.com/ramsey/moontoast-math", - "keywords": [ - "bcmath", - "math" - ], - "time": "2017-02-16T16:54:46+00:00" - }, - { - "name": "mustache/mustache", - "version": "v2.12.0", - "source": { - "type": "git", - "url": "https://github.com/bobthecow/mustache.php.git", - "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/fe8fe72e9d580591854de404cc59a1b83ca4d19e", - "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e", - "shasum": "" - }, - "require": { - "php": ">=5.2.4" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~1.11", - "phpunit/phpunit": "~3.7|~4.0|~5.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Mustache": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" - } - ], - "description": "A Mustache implementation in PHP.", - "homepage": "https://github.com/bobthecow/mustache.php", - "keywords": [ - "mustache", - "templating" - ], - "time": "2017-07-11T12:54:05+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.8.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "replace": { - "myclabs/deep-copy": "self.version" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "time": "2018-06-11T23:09:50+00:00" - }, - { - "name": "paragonie/random_compat", - "version": "v2.0.17", - "source": { - "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d", - "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d", - "shasum": "" - }, - "require": { - "php": ">=5.2.0" - }, - "require-dev": { - "phpunit/phpunit": "4.*|5.*" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - }, - "type": "library", - "autoload": { - "files": [ - "lib/random.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" - ], - "time": "2018-07-04T16:31:37+00:00" - }, - { - "name": "phar-io/manifest", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "phar-io/version": "^1.0.1", - "php": "^5.6 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2017-03-05T18:14:27+00:00" - }, - { - "name": "phar-io/version", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "time": "2017-03-05T17:38:23+00:00" - }, - { - "name": "phpcollection/phpcollection", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/php-collection.git", - "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-collection/zipball/f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", - "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", - "shasum": "" - }, - "require": { - "phpoption/phpoption": "1.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.4-dev" - } - }, - "autoload": { - "psr-0": { - "PhpCollection": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache2" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "General-Purpose Collection Library for PHP", - "keywords": [ - "collection", - "list", - "map", - "sequence", - "set" - ], - "time": "2015-05-17T12:39:23+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2017-09-11T18:02:19+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "4.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08", - "shasum": "" - }, - "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "doctrine/instantiator": "~1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-30T07:14:17+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", - "shasum": "" - }, - "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "time": "2017-07-14T14:27:02+00:00" - }, - { - "name": "phpoption/phpoption", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "4.7.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-0": { - "PhpOption\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache2" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Option Type for PHP", - "keywords": [ - "language", - "option", - "php", - "type" - ], - "time": "2015-07-25T16:39:46+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "1.7.6", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.7.x-dev" - } - }, - "autoload": { - "psr-0": { - "Prophecy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2018-04-18T13:57:24+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "5.3.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^7.0", - "phpunit/php-file-iterator": "^1.4.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-xdebug": "^2.5.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.3.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2018-04-06T15:36:58+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2017-11-27T13:52:08+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.9", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2017-02-26T11:10:40+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.2.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2017-11-27T05:48:46+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "6.5.9", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "093ca5508174cd8ab8efe44fd1dde447adfdec8f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/093ca5508174cd8ab8efe44fd1dde447adfdec8f", - "reference": "093ca5508174cd8ab8efe44fd1dde447adfdec8f", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", - "php": "^7.0", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", - "phpunit/php-file-iterator": "^1.4.3", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.5", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", - "sebastian/environment": "^3.1", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^1.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.5.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2018-07-03T06:40:40+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "5.0.7", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "3eaf040f20154d27d6da59ca2c6e28ac8fd56dce" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3eaf040f20154d27d6da59ca2c6e28ac8fd56dce", - "reference": "3eaf040f20154d27d6da59ca2c6e28ac8fd56dce", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.0", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" - }, - "conflict": { - "phpunit/phpunit": "<6.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2018-05-29T13:50:43+00:00" - }, - { - "name": "psr/container", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "time": "2017-02-14T16:28:37+00:00" - }, - { - "name": "psr/http-message", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "time": "2016-08-06T14:39:51+00:00" - }, - { - "name": "psr/log", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2016-10-10T12:19:37+00:00" - }, - { - "name": "ramsey/uuid", - "version": "3.7.3", - "source": { - "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "44abcdad877d9a46685a3a4d221e3b2c4b87cb76" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/44abcdad877d9a46685a3a4d221e3b2c4b87cb76", - "reference": "44abcdad877d9a46685a3a4d221e3b2c4b87cb76", - "shasum": "" - }, - "require": { - "paragonie/random_compat": "^1.0|^2.0", - "php": "^5.4 || ^7.0" - }, - "replace": { - "rhumsaa/uuid": "self.version" - }, - "require-dev": { - "codeception/aspect-mock": "^1.0 | ~2.0.0", - "doctrine/annotations": "~1.2.0", - "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ^2.1", - "ircmaxell/random-lib": "^1.1", - "jakub-onderka/php-parallel-lint": "^0.9.0", - "mockery/mockery": "^0.9.9", - "moontoast/math": "^1.1", - "php-mock/php-mock-phpunit": "^0.3|^1.1", - "phpunit/phpunit": "^4.7|^5.0", - "squizlabs/php_codesniffer": "^2.3" - }, - "suggest": { - "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", - "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", - "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", - "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Ramsey\\Uuid\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marijn Huizendveld", - "email": "marijn.huizendveld@gmail.com" - }, - { - "name": "Thibaud Fabre", - "email": "thibaud@aztech.io" - }, - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" - } - ], - "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", - "homepage": "https://github.com/ramsey/uuid", - "keywords": [ - "guid", - "identifier", - "uuid" - ], - "time": "2018-01-20T00:28:24+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" - }, - { - "name": "sebastian/comparator", - "version": "2.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2018-02-01T13:46:46+00:00" - }, - { - "name": "sebastian/diff", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "time": "2017-08-03T08:09:46+00:00" - }, - { - "name": "sebastian/environment", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "time": "2017-07-01T08:51:00+00:00" - }, - { - "name": "sebastian/exporter", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "time": "2017-04-03T13:19:02+00:00" - }, - { - "name": "sebastian/global-state", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2017-04-27T15:39:26+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "symfony/browser-kit", - "version": "v4.1.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/browser-kit.git", - "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", - "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/dom-crawler": "~3.4|~4.0" - }, - "require-dev": { - "symfony/css-selector": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" - }, - "suggest": { - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\BrowserKit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony BrowserKit Component", - "homepage": "https://symfony.com", - "time": "2018-06-04T17:31:56+00:00" - }, - { - "name": "symfony/console", - "version": "v4.1.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/70591cda56b4b47c55776ac78e157c4bb6c8b43f", - "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" - }, - "suggest": { - "psr/log-implementation": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2018-05-31T10:17:53+00:00" - }, - { - "name": "symfony/css-selector", - "version": "v4.1.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/03ac71606ecb0b0ce792faa17d74cc32c2949ef4", - "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony CssSelector Component", - "homepage": "https://symfony.com", - "time": "2018-05-30T07:26:09+00:00" - }, - { - "name": "symfony/dom-crawler", - "version": "v4.1.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/3350cacf151b48d903114ab8f7a4ccb23e07e10a", - "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0" - }, - "require-dev": { - "symfony/css-selector": "~3.4|~4.0" - }, - "suggest": { - "symfony/css-selector": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony DomCrawler Component", - "homepage": "https://symfony.com", - "time": "2018-05-01T23:02:13+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v4.1.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2391ed210a239868e7256eb6921b1bd83f3087b5", - "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "conflict": { - "symfony/dependency-injection": "<3.4" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "https://symfony.com", - "time": "2018-04-06T07:35:57+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v4.1.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", - "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "time": "2018-05-30T07:26:09+00:00" - }, - { - "name": "symfony/finder", - "version": "v4.1.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/84714b8417d19e4ba02ea78a41a975b3efaafddb", - "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "https://symfony.com", - "time": "2018-06-19T21:38:16+00:00" - }, - { - "name": "symfony/http-foundation", - "version": "v4.1.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "4f9c7cf962e635b0b26b14500ac046e07dbef7f3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/4f9c7cf962e635b0b26b14500ac046e07dbef7f3", - "reference": "4f9c7cf962e635b0b26b14500ac046e07dbef7f3", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.1" - }, - "require-dev": { - "predis/predis": "~1.0", - "symfony/expression-language": "~3.4|~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpFoundation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony HttpFoundation Component", - "homepage": "https://symfony.com", - "time": "2018-06-19T21:38:16+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.8.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/7cc359f1b7b80fc25ed7796be7d96adc9b354bae", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "time": "2018-04-30T19:57:29+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.8.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "3296adf6a6454a050679cde90f95350ad604b171" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", - "reference": "3296adf6a6454a050679cde90f95350ad604b171", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "time": "2018-04-26T10:06:28+00:00" - }, - { - "name": "symfony/process", - "version": "v4.1.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a", - "reference": "1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "time": "2018-05-31T10:17:53+00:00" - }, - { - "name": "symfony/yaml", - "version": "v3.4.12", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", - "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2018-05-03T23:18:14+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2017-04-07T12:08:54+00:00" - }, - { - "name": "vlucas/phpdotenv", - "version": "v2.5.0", - "source": { - "type": "git", - "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/6ae3e2e6494bb5e58c2decadafc3de7f1453f70a", - "reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.5-dev" - } - }, - "autoload": { - "psr-4": { - "Dotenv\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Vance Lucas", - "email": "vance@vancelucas.com", - "homepage": "http://www.vancelucas.com" - } - ], - "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", - "keywords": [ - "dotenv", - "env", - "environment" - ], - "time": "2018-07-01T10:25:50+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2018-01-29T19:49:41+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": true, - "prefer-lowest": false, - "platform": { - "php": "~7.1.3||~7.2.0" - }, - "platform-dev": [] -} From dd88c37018e3a636c33c1025399fddf33015ddc2 Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Thu, 5 Jul 2018 16:55:22 -0500 Subject: [PATCH 0130/1171] MC-231: Customer should be able to select customisable bundle product options and set quantity for them --- ....xml => StorefrontCustomerSelectAndSetBundleOptionsTest.xml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/{CustomerSelectAndSetBundleOptionsTest.xml => StorefrontCustomerSelectAndSetBundleOptionsTest.xml} (99%) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/CustomerSelectAndSetBundleOptionsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml similarity index 99% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/CustomerSelectAndSetBundleOptionsTest.xml rename to dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml index eff22a561f044..23e65e14b278b 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/CustomerSelectAndSetBundleOptionsTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="CustomerSelectAndSetBundleOptionsTest"> + <test name="StorefrontCustomerSelectAndSetBundleOptionsTest"> <annotations> <features value="Bundle"/> <stories value="Bundle product details page"/> From 795b2c6152e0fdf27bfb720d18e1bcd29723b154 Mon Sep 17 00:00:00 2001 From: jakhotiya <jakhotiyaabhishek@gmail.com> Date: Mon, 18 Jun 2018 23:45:42 +0530 Subject: [PATCH 0131/1171] Incorrect value NULL was passed to DataObject constructor. It caused fatal error. Fixed it by passing an empty array instead --- app/code/Magento/Wishlist/Model/Item.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Model/Item.php b/app/code/Magento/Wishlist/Model/Item.php index b0e7c78cae5f4..41e83c7179e10 100644 --- a/app/code/Magento/Wishlist/Model/Item.php +++ b/app/code/Magento/Wishlist/Model/Item.php @@ -473,7 +473,7 @@ public function getProductUrl() public function getBuyRequest() { $option = $this->getOptionByCode('info_buyRequest'); - $initialData = $option ? $this->serializer->unserialize($option->getValue()) : null; + $initialData = $option ? $this->serializer->unserialize($option->getValue()) : []; if ($initialData instanceof \Magento\Framework\DataObject) { $initialData = $initialData->getData(); From e2c4acae5974c91e16a437e027b06affa4a97822 Mon Sep 17 00:00:00 2001 From: Volodymyr Kublytskyi <vkublytskyi@magento.com> Date: Fri, 6 Jul 2018 13:25:37 +0300 Subject: [PATCH 0132/1171] Fix of invalid price for integer currencies when amount less then group size --- lib/internal/Magento/Framework/Locale/Format.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php index 00379c87daaf9..89f6957011876 100644 --- a/lib/internal/Magento/Framework/Locale/Format.php +++ b/lib/internal/Magento/Framework/Locale/Format.php @@ -131,7 +131,6 @@ public function getPriceFormat($localeCode = null, $currencyCode = null) } else { $group = strrpos($format, '.'); } - $integerRequired = strpos($format, '.') - strpos($format, '0'); $result = [ //TODO: change interface @@ -141,7 +140,7 @@ public function getPriceFormat($localeCode = null, $currencyCode = null) 'decimalSymbol' => $decimalSymbol, 'groupSymbol' => $groupSymbol, 'groupLength' => $group, - 'integerRequired' => $integerRequired, + 'integerRequired' => $totalPrecision == 0, ]; return $result; From 3a0599d196e937db48dd1fa81dfdd8f4341903be Mon Sep 17 00:00:00 2001 From: Tadhg Bowe <tadhg.bowe@screenpages.com> Date: Fri, 6 Jul 2018 11:43:58 +0100 Subject: [PATCH 0133/1171] import-export-improvements #82 : configurable variations - not a super attribute error message improvements - code styling fixes --- .../Import/Product/Type/Configurable.php | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php index 4f3308ff3d6e1..36e77358936b4 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php @@ -41,25 +41,27 @@ class Configurable extends \Magento\CatalogImportExport\Model\Import\Product\Typ const ERROR_UNIDENTIFIABLE_VARIATION = 'unidentifiableVariation'; - // @codingStandardsIgnoreStart /** * Validation failure message template definitions * * @var array * - * Note: Many of these messages exceed maximum limit of 120 characters. Ignore from coding standards. + * Note: Some of these messages exceed maximum limit of 120 characters per line. Split up accordingly. */ protected $_messageTemplates = [ - self::ERROR_ATTRIBUTE_CODE_DOES_NOT_EXIST => 'Column configurable_variations: Attribute with code "%s" does not exist or is missing from product attribute set', - self::ERROR_ATTRIBUTE_CODE_NOT_GLOBAL_SCOPE => 'Column configurable_variations: Attribute with code "%s" is not super - it needs to have Global Scope', - self::ERROR_ATTRIBUTE_CODE_NOT_TYPE_SELECT => 'Column configurable_variations: Attribute with code "%s" is not super - it needs to be Input Type of Dropdown, Visual Swatch or Text Swatch', - self::ERROR_ATTRIBUTE_CODE_IS_NOT_SUPER => 'Column configurable_variations: Attribute with code "%s" is not super', + self::ERROR_ATTRIBUTE_CODE_DOES_NOT_EXIST => 'Column configurable_variations: Attribute with code ' . + '"%s" does not exist or is missing from product attribute set', + self::ERROR_ATTRIBUTE_CODE_NOT_GLOBAL_SCOPE => 'Column configurable_variations: Attribute with code ' . + '"%s" is not super - it needs to have Global Scope', + self::ERROR_ATTRIBUTE_CODE_NOT_TYPE_SELECT => 'Column configurable_variations: Attribute with code ' . + '"%s" is not super - it needs to be Input Type of Dropdown, Visual Swatch or Text Swatch', + self::ERROR_ATTRIBUTE_CODE_IS_NOT_SUPER => 'Column configurable_variations: Attribute with code ' . + '"%s" is not super', self::ERROR_INVALID_OPTION_VALUE => 'Column configurable_variations: Invalid option value for attribute "%s"', self::ERROR_INVALID_WEBSITE => 'Invalid website code for super attribute', self::ERROR_DUPLICATED_VARIATIONS => 'SKU %s contains duplicated variations', self::ERROR_UNIDENTIFIABLE_VARIATION => 'Configurable variation "%s" is unidentifiable', ]; - // @codingStandardsIgnoreEnd /** * Column names that holds values with particular meaning. @@ -304,7 +306,7 @@ protected function _isParticularAttributesValid(array $rowData, $rowNum) $superAttrCode = $rowData['_super_attribute_code']; if (!$this->_isAttributeSuper($superAttrCode)) { // Identify reason why attribute is not super: - if (!$this->_identifySuperAttributeError($superAttrCode, $rowNum)) { + if (!$this->identifySuperAttributeError($superAttrCode, $rowNum)) { $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_IS_NOT_SUPER, $rowNum, $superAttrCode); } return false; @@ -326,7 +328,7 @@ protected function _isParticularAttributesValid(array $rowData, $rowNum) * @param int $rowNum * @return bool */ - protected function _identifySuperAttributeError($superAttrCode, $rowNum) + private function identifySuperAttributeError($superAttrCode, $rowNum) { // This attribute code is not a super attribute. Need to give a clearer message why? $reasonFound = false; @@ -334,23 +336,28 @@ protected function _identifySuperAttributeError($superAttrCode, $rowNum) $codeExists = false; $codeNotGlobal = false; $codeNotTypeSelect = false; - // Does this attribute code exist? Does is have the correct settings? - $commonAttributes = self::$commonAttributesCache; - foreach ($commonAttributes as $attributeRow) { - if ($attributeRow['code'] == $superAttrCode) { - $codeExists = true; + // Does this attribute code exist? Does it have the correct settings? + $filterAttribute = array_filter( + self::$commonAttributesCache, + function ($element) use($superAttrCode) { + return $element['code'] == $superAttrCode; + } + ); - if ($attributeRow['is_global'] !== '1') { + if (is_array($filterAttribute) && count($filterAttribute)) { + $codeExists = true; + // Examine the first element of the filtered array + $sourceAttribute = array_shift($filterAttribute); + if (is_array($sourceAttribute)) { + if (isset($sourceAttribute['is_global']) && $sourceAttribute['is_global'] !== '1') { $codeNotGlobal = true; - } elseif ($attributeRow['type'] !== 'select') { + } elseif (isset($sourceAttribute['type']) && $sourceAttribute['type'] !== 'select') { $codeNotTypeSelect = true; } - - break; } } - if ($codeExists == false) { + if ($codeExists === false) { $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_DOES_NOT_EXIST, $rowNum, $superAttrCode); $reasonFound = true; } elseif ($codeNotGlobal == true) { From 55b3db85ed4c4ffe9844f2967a856edc27135afa Mon Sep 17 00:00:00 2001 From: John Stennett <john00ivy@gmail.com> Date: Fri, 6 Jul 2018 08:34:59 -0500 Subject: [PATCH 0134/1171] MQE-1267: MSI MFTF Test Cases 4 - Removing commented out selector. --- .../Catalog/Section/AdminProductFormActionSection.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormActionSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormActionSection.xml index e55a45d590910..d72830674808f 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormActionSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormActionSection.xml @@ -12,8 +12,7 @@ <element name="backButton" type="button" selector="#back" timeout="30"/> <element name="saveButton" type="button" selector="#save-button" timeout="30"/> <element name="saveArrow" type="button" selector="button[data-ui-id='save-button-dropdown']" timeout="30"/> - <!--<element name="saveAndClose" type="button" selector="span[title='Save & Close']" timeout="30"/>--> - <element name="saveAndClose" type="button" selector="span[id='save_and_close']" timeout="30"/> + <element name="saveAndClose" type="button" selector="span[title='Save & Close']" timeout="30"/> <element name="changeStoreButton" type="button" selector="#store-change-button" timeout="10"/> <element name="selectStoreView" type="button" selector="//ul[@data-role='stores-list']/li/a[normalize-space(.)='{{var1}}']" timeout="10" parameterized="true"/> </section> From a503eb95beed9e53b1e9a98192043f07f71538a8 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Fri, 6 Jul 2018 08:35:03 -0500 Subject: [PATCH 0135/1171] MAGETWO-77744: [Magento Cloud] - Error message when uploading unsupported file format - Move functional tests --- .../Checkout/Test/Mftf}/Section/AdminDataGridHeaderSection.xml | 0 .../StorefrontConfigurableProductWithFileCustomOptionTest.xml | 0 .../Test/Mftf}/ActionGroup/AddSwatchToProductActionGroup.xml | 0 .../code/Magento/Swatches/Test/Mftf}/Data/SwatchAttributeData.xml | 0 .../code/Magento/Swatches/Test/Mftf}/Data/SwatchOptionData.xml | 0 .../Swatches/Test/Mftf}/Section/AdminNewAttributePanelSection.xml | 0 .../Test/Mftf}/Section/StorefrontProductInfoMainSection.xml | 0 .../Test/StorefrontSwatchProductWithFileCustomOptionTest.xml | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout => app/code/Magento/Checkout/Test/Mftf}/Section/AdminDataGridHeaderSection.xml (100%) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct => app/code/Magento/ConfigurableProduct/Test/Mftf}/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml (100%) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches => app/code/Magento/Swatches/Test/Mftf}/ActionGroup/AddSwatchToProductActionGroup.xml (100%) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches => app/code/Magento/Swatches/Test/Mftf}/Data/SwatchAttributeData.xml (100%) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches => app/code/Magento/Swatches/Test/Mftf}/Data/SwatchOptionData.xml (100%) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches => app/code/Magento/Swatches/Test/Mftf}/Section/AdminNewAttributePanelSection.xml (100%) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches => app/code/Magento/Swatches/Test/Mftf}/Section/StorefrontProductInfoMainSection.xml (100%) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches => app/code/Magento/Swatches/Test/Mftf}/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml (100%) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/AdminDataGridHeaderSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/AdminDataGridHeaderSection.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/AdminDataGridHeaderSection.xml rename to app/code/Magento/Checkout/Test/Mftf/Section/AdminDataGridHeaderSection.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/ActionGroup/AddSwatchToProductActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/ActionGroup/AddSwatchToProductActionGroup.xml rename to app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Data/SwatchAttributeData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Data/SwatchAttributeData.xml rename to app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Data/SwatchOptionData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Data/SwatchOptionData.xml rename to app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Section/AdminNewAttributePanelSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminNewAttributePanelSection.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Section/AdminNewAttributePanelSection.xml rename to app/code/Magento/Swatches/Test/Mftf/Section/AdminNewAttributePanelSection.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Section/StorefrontProductInfoMainSection.xml rename to app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Swatches/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml rename to app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml From 6a5b15d7a691fa494921b3ee1ad0949ea607da16 Mon Sep 17 00:00:00 2001 From: Tadhg Bowe <tadhg.bowe@screenpages.com> Date: Fri, 6 Jul 2018 14:44:13 +0100 Subject: [PATCH 0136/1171] import-export-improvements #82 : configurable variations - not a super attribute error message improvements - code styling Travis CI build fixes --- .../Import/Product/Type/Configurable.php | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php index 36e77358936b4..43da5661e966b 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php @@ -336,18 +336,11 @@ private function identifySuperAttributeError($superAttrCode, $rowNum) $codeExists = false; $codeNotGlobal = false; $codeNotTypeSelect = false; - // Does this attribute code exist? Does it have the correct settings? - $filterAttribute = array_filter( - self::$commonAttributesCache, - function ($element) use($superAttrCode) { - return $element['code'] == $superAttrCode; - } - ); - - if (is_array($filterAttribute) && count($filterAttribute)) { + // Does this attribute code exist? + $sourceAttribute = $this->doesSuperAttributeExist($superAttrCode); + if (count($sourceAttribute)) { $codeExists = true; - // Examine the first element of the filtered array - $sourceAttribute = array_shift($filterAttribute); + // Does attribute have the correct settings? if (is_array($sourceAttribute)) { if (isset($sourceAttribute['is_global']) && $sourceAttribute['is_global'] !== '1') { $codeNotGlobal = true; @@ -357,13 +350,14 @@ function ($element) use($superAttrCode) { } } + // Identify (if any) the correct fault: if ($codeExists === false) { $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_DOES_NOT_EXIST, $rowNum, $superAttrCode); $reasonFound = true; - } elseif ($codeNotGlobal == true) { + } elseif ($codeNotGlobal === true) { $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_GLOBAL_SCOPE, $rowNum, $superAttrCode); $reasonFound = true; - } elseif ($codeNotTypeSelect == true) { + } elseif ($codeNotTypeSelect === true) { $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_TYPE_SELECT, $rowNum, $superAttrCode); $reasonFound = true; } @@ -371,6 +365,30 @@ function ($element) use($superAttrCode) { return $reasonFound; } + /** + * Does the super attribute exist in the current attribute set? + * + * @param string $superAttrCode + * @return array + */ + private function doesSuperAttributeExist($superAttrCode) + { + $returnFilterArray = []; + if (is_array(self::$commonAttributesCache)) + { + $filteredAttribute = array_filter( + self::$commonAttributesCache, + function ($element) use ($superAttrCode) { + return $element['code'] == $superAttrCode; + } + ); + + // Return the first element of the filtered array. + $returnFilterArray = array_shift($filteredAttribute); + } + return $returnFilterArray; + } + /** * Array of SKU to array of super attribute values for all products. * From 98e8bc0d1a6895c5399b265f00233eb416d3413f Mon Sep 17 00:00:00 2001 From: Dmitriy Kogut <kogut.dmitriy@gmail.com> Date: Fri, 6 Jul 2018 17:05:59 +0300 Subject: [PATCH 0137/1171] MAGETWO-90373: Automate with MFTF product tier price --- .../Test/Mftf/Page/AdminProductCreatePage.xml | 1 + ...AdminProductFormAdvancedPricingSection.xml | 25 ++ .../StorefrontCategoryProductSection.xml | 4 + .../StorefrontProductInfoMainSection.xml | 6 + .../Test/AdminApplyTierPriceToProductTest.xml | 265 ++++++++++++++++++ .../Section/CheckoutCartProductSection.xml | 2 + .../Section/StorefrontMiniCartSection.xml | 1 + .../Test/Mftf/Page/AdminSalesConfigPage.xml | 12 + .../Mftf/Section/AdminSalesConfigSection.xml | 13 + 9 files changed, 329 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Page/AdminSalesConfigPage.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml index 35fa00efcfe8e..be7c44e378f08 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml @@ -16,5 +16,6 @@ <section name="AdminAddProductsToOptionPanel"/> <section name="AdminProductMessagesSection"/> <section name="AdminProductFormRelatedUpSellCrossSellSection"/> + <section name="AdminProductFormAdvancedPricingSection"/> </page> </pages> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml new file mode 100644 index 0000000000000..1042b1e5a5464 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminProductFormAdvancedPricingSection"> + <element name="customerGroupPriceAddButton" type="button" selector="[data-action='add_new_row']" timeout="30"/> + <element name="customerGroupPriceDeleteButton" type="button" selector="[data-action='remove_row']" timeout="30"/> + <element name="advancedPricingCloseButton" type="button" selector=".product_form_product_form_advanced_pricing_modal button.action-close" timeout="30"/> + <element name="productTierPriceWebsiteSelect" type="select" selector="[name='product[tier_price][{{var1}}][website_id]']" parameterized="true"/> + <element name="productTierPriceCustGroupSelect" type="select" selector="[name='product[tier_price][{{var1}}][cust_group]']" parameterized="true"/> + <element name="productTierPriceQtyInput" type="input" selector="[name='product[tier_price][{{var1}}][price_qty]']" parameterized="true"/> + <element name="productTierPriceValueTypeSelect" type="select" selector="[name='product[tier_price][{{var1}}][value_type]']" parameterized="true"/> + <element name="productTierPriceFixedPriceInput" type="input" selector="[name='product[tier_price][{{var1}}][price]']" parameterized="true"/> + <element name="productTierPricePercentageValuePriceInput" type="input" selector="[name='product[tier_price][{{var1}}][percentage_value]']" parameterized="true"/> + <element name="specialPrice" type="input" selector="input[name='product[special_price]']"/> + <element name="doneButton" type="button" selector=".product_form_product_form_advanced_pricing_modal button.action-primary" timeout="5"/> + <element name="save" type="button" selector="#save-button"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml index 923414e60cf5f..6777c2e5fce2d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -19,6 +19,10 @@ <element name="ProductImageByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//img[@class='product-image-photo']" parameterized="true"/> <element name="ProductImageBySrc" type="text" selector=".products-grid img[src*='{{pattern}}']" parameterized="true"/> <element name="ProductInfoByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//div[@class='product-item-info']" parameterized="true"/> + <element name="productPriceFinal" type="text" selector="//span[@data-price-type='finalPrice']//span[@class='price'][contains(.,'{{var1}}')]" parameterized="true"/> + <element name="productPriceOld" type="text" selector="//span[@data-price-type='oldPrice']//span[@class='price'][contains(., '{{var1}}')]" parameterized="true"/> + <element name="productPriceLabel" type="text" selector="//span[@class='price-label'][contains(text(),'{{var1}}')]" parameterized="true"/> + <element name="productPriceLinkAfterLabel" type="text" selector="//span[@class='price-label'][contains(text(),'{{var1}}')]/following::span[contains(text(), '{{var2}}')]" parameterized="true"/> <element name="ProductAddToCompareByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//a[contains(@class, 'tocompare')]" parameterized="true"/> <element name="ProductImageByNameAndSrc" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//img[contains(@src, '{{src}}')]" parameterized="true"/> </section> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index f0ca8cb74e3e1..f711a5e93017e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -14,6 +14,7 @@ <element name="productSku" type="text" selector=".product.attribute.sku>.value"/> <element name="productPriceLabel" type="text" selector=".price-label"/> <element name="productPrice" type="text" selector="div.price-box.price-final_price"/> + <element name="qty" type="input" selector="#qty"/> <element name="specialPrice" type="text" selector=".special-price"/> <element name="oldPrice" type="text" selector=".old-price"/> <element name="productStockStatus" type="text" selector=".stock[title=Availability]>span"/> @@ -60,5 +61,10 @@ <element name="productAddToCompare" type="button" selector="a.action.tocompare"/> <element name="productOptionDropDownTitle" type="text" selector="//label[contains(.,'{{var1}}')]" parameterized="true"/> <element name="productOptionDropDownOptionTitle" type="text" selector="//label[contains(.,'{{var1}}')]/../div[@class='control']//select//option[contains(.,'{{var2}}')]" parameterized="true"/> + + <!-- Tier price selectors --> + <element name="productTierPriceByForTextLabel" type="text" selector="//ul[contains(@class, 'prices-tier')]//li[{{var1}}][contains(text(),'Buy {{var2}} for')]" parameterized="true"/> + <element name="productTierPriceAmount" type="text" selector="//ul[contains(@class, 'prices-tier')]//li[{{var1}}]//span[contains(text(), '{{var2}}')]" parameterized="true"/> + <element name="productTierPriceSavePercentageAmount" type="text" selector="//ul[contains(@class, 'prices-tier')]//li[{{var1}}]//span[contains(@class, 'percent')][contains(text(), '{{var2}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml new file mode 100644 index 0000000000000..fcb60a27e679a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml @@ -0,0 +1,265 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminApplyTierPriceToProductTest"> + <annotations> + <features value="Apply tier price to a product"/> + <title value="You should be able to apply tier price to a product."/> + <description value="You should be able to apply tier price to a product."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-68921"/> + <group value="product"/> + </annotations> + <before> + <createData entity="Simple_US_Customer" stepKey="createSimpleUSCustomer"> + <field key="group_id">1</field> + </createData> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">100</field> + </createData> + </before> + <after> + <deleteData createDataKey="createSimpleUSCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex1"/> + <waitForPageLoad time="30" stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Case: Group Price--> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct1"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton1"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton1"/> + <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="addCustomerGroupAllGroupsQty1PriceDiscountAnd10percent"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="1" stepKey="fillProductTierPriceQtyInput1"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect('0')}}" userInput="Discount" stepKey="selectProductTierPriceValueType1"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('0')}}" userInput="10" stepKey="selectProductTierPricePriceInput"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton1"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct1"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin1"> + <argument name="Customer" value="$$createSimpleUSCustomer$$" /> + </actionGroup> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage1"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('90')}}" stepKey="assertProductFinalPriceIs90_1"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceLabel_1"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmount_1"/> + <amOnPage url="customer/account/logout/" stepKey="logoutCustomer1"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage2"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad3"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('90')}}" stepKey="assertProductFinalPriceIs90_2"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceLabel_2"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmount_2"/> + <!--Case: Tier Price for General Customer Group--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex2"/> + <waitForPageLoad time="30" stepKey="waitForProductPageToLoad1"/> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct2"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton2"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForcustomerGroupPriceAddButton2"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" time="30" stepKey="waitForSelectCustomerGroupNameAttribute1"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="General" stepKey="selectCustomerGroupGeneral"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton2"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct2"/> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage3"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad4"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('100')}}" stepKey="assertProductFinalPriceIs100_1"/> + <dontSeeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceLabel_3"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin2"> + <argument name="Customer" value="$$createSimpleUSCustomer$$" /> + </actionGroup> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage4"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad5"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('90')}}" stepKey="assertProductFinalPriceIs90_3"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceLabel_4"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmount_3"/> + <!--Case: Tier Price applied if Product quantity meets Tier Price Condition--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex3"/> + <waitForPageLoad time="30" stepKey="waitForProductPageToLoad2"/> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct3"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton3"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForcustomerGroupPriceAddButton3"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" stepKey="waitForSelectCustomerGroupNameAttribute2"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="ALL GROUPS" stepKey="selectCustomerGroupAllGroups"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="15" stepKey="fillProductTierPriceQtyInput15"/> + <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="clickToLoseFocusOnRequiredInputElement"/> + <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="addCustomerGroupAllGroupsQty20PriceDiscountAnd18percent2"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('1')}}" userInput="20" stepKey="fillProductTierPriceQtyInput20"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect('1')}}" userInput="Discount" stepKey="selectProductTierPriceValueType2"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('1')}}" userInput="18" stepKey="selectProductTierPricePriceInput18"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton3"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct3"/> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage5"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad6"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('100')}}" stepKey="assertProductFinalPriceIs100_2"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('As low as')}}" stepKey="assertAsLowAsPriceLabel_1"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceLinkAfterLabel('As low as', '82')}}" stepKey="assertPriceAfterAsLowAsLabel_1"/> + <amOnPage url="customer/account/logout/" stepKey="logoutCustomer2"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad7"/> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage6"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad8"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('100')}}" stepKey="assertProductFinalPriceIs100_3"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('As low as')}}" stepKey="assertAsLowAsPriceLabel_2"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceLinkAfterLabel('As low as', '82')}}" stepKey="assertPriceAfterAsLowAsLabel_2"/> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="goToProductPage1"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad9"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceByForTextLabel('1', '15')}}" stepKey="assertProductTierPriceByForTextLabelForFirstRow1"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceByForTextLabel('2', '20')}}" stepKey="assertProductTierPriceByForTextLabelForSecondRow1"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceAmount('1', '90')}}" stepKey="assertProductTierPriceAmountForFirstRow1"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceAmount('2', '82')}}" stepKey="assertProductTierPriceAmountForSecondRow1"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceSavePercentageAmount('1', '10')}}" stepKey="assertProductTierPriceSavePercentageAmountForFirstRow1"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceSavePercentageAmount('2', '18')}}" stepKey="assertProductTierPriceSavePercentageAmountForSecondRow1"/> + <fillField userInput="10" selector="{{StorefrontProductInfoMainSection.qty}}" stepKey="fillProductQuantity1"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + </actionGroup> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToCheckoutFromMinicart"/> + <seeInField userInput="10" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField10"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField1"/> + <assertEquals message="Shopping cart should contain subtotal $1,000" stepKey="assertSubtotalField1"> + <expectedResult type="string">$1,000.00</expectedResult> + <actualResult type="variable">grabTextFromSubtotalField1</actualResult> + </assertEquals> + <fillField userInput="15" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="fillProductQuantity2"/> + <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="clickUpdateShoppingCartButton1"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear1"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField2"/> + <assertEquals message="Shopping cart should contain subtotal $1,350" stepKey="assertSubtotalField2"> + <expectedResult type="string">$1,350.00</expectedResult> + <actualResult type="variable">grabTextFromSubtotalField2</actualResult> + </assertEquals> + <fillField userInput="20" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="fillProductQuantity3"/> + <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="clickUpdateShoppingCartButton2"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear2"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField3"/> + <assertEquals message="Shopping cart should contain subtotal $1,640" stepKey="assertSubtotalField3"> + <expectedResult type="string">$1,640.00</expectedResult> + <actualResult type="variable">grabTextFromSubtotalField3</actualResult> + </assertEquals> + <!--Tier Price is changed in Shopping Cart and is changed on Product page if Tier Price parameters are changed in Admin--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex4"/> + <waitForPageLoad time="30" stepKey="waitForProductPageToLoa4"/> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct4"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton4"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForcustomerGroupPriceAddButton4"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('1')}}" userInput="25" stepKey="selectProductTierPricePercentageValue2"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton4"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct4"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage1"/> + <waitForPageLoad time="30" stepKey="waitForShoppingCartPagePageLoad1"/> + <seeInField userInput="20" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField20"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField4"/> + <assertEquals message="Shopping cart should contain subtotal $1,500" stepKey="assertSubtotalField4"> + <expectedResult type="string">$1,500.00</expectedResult> + <actualResult type="variable">grabTextFromSubtotalField4</actualResult> + </assertEquals> + <grabTextFrom selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="grabTextFromCheckoutCartSummarySectionSubtotal1"/> + <assertEquals message="Shopping cart summary section should contain subtotal $1,500" stepKey="assertSubtotalFieldFromCheckoutCartSummarySection1"> + <expectedResult type="string">$1,500.00</expectedResult> + <actualResult type="variable">grabTextFromCheckoutCartSummarySectionSubtotal1</actualResult> + </assertEquals> + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart1"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.miniCartSubtotalField}}" stepKey="waitForminiCartSubtotalField1"/> + <grabTextFrom selector="{{StorefrontMinicartSection.miniCartSubtotalField}}" stepKey="grabTextFromMiniCartSubtotalField"/> + <assertEquals message="Mini shopping cart should contain subtotal $1,500" stepKey="assertSubtotalFieldFromMiniShoppingCart1"> + <expectedResult type="string">$1,500.00</expectedResult> + <actualResult type="variable">grabTextFromMiniCartSubtotalField</actualResult> + </assertEquals> + <amOnPage url="{{AdminSalesConfigPage.url('#sales_msrp-link')}}" stepKey="navigateToAdminSalesConfigPageMAPTab1"/> + <waitForPageLoad time="30" stepKey="waitForAdminSalesConfigPageLoad1"/> + <uncheckOption selector="{{AdminSalesConfigSection.enableMAPUseSystemValue}}" stepKey="uncheckMAPUseSystemValue"/> + <selectOption selector="{{AdminSalesConfigSection.enableMAPSelect}}" userInput="Yes" stepKey="setEnableMAPYes"/> + <click selector="{{AdminConfigSection.saveButton}}" stepKey="saveConfig1"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeConfigSuccessMessage1"/> + <actionGroup ref="ClearCacheActionGroup" stepKey="flushCache1"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage2"/> + <waitForPageLoad time="30" stepKey="waitForShoppingCartPagePageLoad2"/> + <seeInField userInput="20" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField20_2"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField5"/> + <assertEquals message="Shopping cart should contain subtotal $1,500" stepKey="assertSubtotalField5"> + <expectedResult type="string">$1,500.00</expectedResult> + <actualResult type="variable">grabTextFromSubtotalField5</actualResult> + </assertEquals> + <amOnPage url="{{AdminSalesConfigPage.url('#sales_msrp-link')}}" stepKey="navigateToAdminSalesConfigPageMAPTab2"/> + <waitForPageLoad time="30" stepKey="waitForAdminSalesConfigPageLoad2"/> + <selectOption selector="{{AdminSalesConfigSection.enableMAPSelect}}" userInput="No" stepKey="setEnableMAPNo"/> + <click selector="{{AdminConfigSection.saveButton}}" stepKey="saveConfig2"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeConfigSuccessMessage2"/> + <actionGroup ref="ClearCacheActionGroup" stepKey="flushCache2"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage3"/> + <waitForPageLoad time="30" stepKey="waitForShoppingCartPagePageLoad3"/> + <seeInField userInput="20" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField20_3"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField6"/> + <assertEquals message="Shopping cart should contain subtotal $1,500" stepKey="assertSubtotalField6"> + <expectedResult type="string">$1,500.00</expectedResult> + <actualResult type="variable">grabTextFromSubtotalField6</actualResult> + </assertEquals> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="goToProductPage2"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad10"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceByForTextLabel('1', '15')}}" stepKey="assertProductTierPriceByForTextLabelForFirstRow2"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceByForTextLabel('2', '20')}}" stepKey="assertProductTierPriceByForTextLabelForSecondRow2"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceAmount('1', '90')}}" stepKey="assertProductTierPriceAmountForFirstRow2"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceAmount('2', '75')}}" stepKey="assertProductTierPriceAmountForSecondRow2"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceSavePercentageAmount('1', '10')}}" stepKey="assertProductTierPriceSavePercentageAmountForFirstRow2"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceSavePercentageAmount('2', '25')}}" stepKey="assertProductTierPriceSavePercentageAmountForSecondRow2"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex5"/> + <waitForPageLoad time="30" stepKey="waitForProductPageToLoad3"/> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct5"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton5"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceDeleteButton}}" stepKey="waitForcustomerGroupPriceDeleteButton"/> + <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceDeleteButton}}" stepKey="deleteFirstRowOfCustomerGroupPrice"/> + <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceDeleteButton}}" stepKey="deleteSecondRowOfCustomerGroupPrice"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton5"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct5"/> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton6"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForcustomerGroupPriceAddButton5"/> + <dontSeeElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" stepKey="dontSeeQtyInputOfFirstRow"/> + <dontSeeElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('1')}}" stepKey="dontSeeQtyInputOfSecondRow"/> + <click selector="{{AdminProductFormAdvancedPricingSection.advancedPricingCloseButton}}" stepKey="closeAdvancedPricingPopup"/> + <waitForElementVisible selector="{{AdminProductFormSection.productPrice}}" stepKey="waitForAdminProductFormSectionProductPriceInput"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="200" stepKey="fillProductPrice200"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage4"/> + <waitForPageLoad time="30" stepKey="waitForShoppingCartPagePageLoad4"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField7"/> + <assertEquals message="Shopping cart should contain subtotal $4,000" stepKey="assertSubtotalField7"> + <expectedResult type="string">$4,000.00</expectedResult> + <actualResult type="variable">grabTextFromSubtotalField7</actualResult> + </assertEquals> + <grabTextFrom selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="grabTextFromCheckoutCartSummarySectionSubtotal2"/> + <assertEquals message="Shopping cart summary section should contain subtotal $4,000" stepKey="assertSubtotalFieldFromCheckoutCartSummarySection2"> + <expectedResult type="string">$4,000.00</expectedResult> + <actualResult type="variable">grabTextFromCheckoutCartSummarySectionSubtotal2</actualResult> + </assertEquals> + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart2"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.miniCartSubtotalField}}" stepKey="waitForminiCartSubtotalField2"/> + <grabTextFrom selector="{{StorefrontMinicartSection.miniCartSubtotalField}}" stepKey="grabTextFromMiniCartSubtotalField2"/> + <assertEquals message="Mini shopping cart should contain subtotal $4,000" stepKey="assertSubtotalFieldFromMiniShoppingCart2"> + <expectedResult type="string">$4,000.00</expectedResult> + <actualResult type="variable">grabTextFromMiniCartSubtotalField2</actualResult> + </assertEquals> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml index 6e2262d58b89d..b3e3f082319fb 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml @@ -29,5 +29,7 @@ <element name="nthItemOption" type="block" selector=".item:nth-of-type({{numElement}}) .item-options" parameterized="true"/> <element name="nthEditButton" type="block" selector=".item:nth-of-type({{numElement}}) .action-edit" parameterized="true"/> <element name="nthBundleOptionName" type="text" selector=".product-item-details .item-options:nth-of-type({{numOption}}) dt" parameterized="true"/> + <element name="productSubtotalByName" type="input" selector="//main//table[@id='shopping-cart-table']//tbody//tr[..//strong[contains(@class, 'product-item-name')]//a/text()='{{var1}}'][1]//td[contains(@class, 'subtotal')]//span[@class='price']" parameterized="true"/> + <element name="updateShoppingCartButton" type="button" selector="#form-validate button[type='submit'].update" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml index 0fa5dc8a42341..082dedce0ea80 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml @@ -23,5 +23,6 @@ <element name="viewAndEditCart" type="button" selector=".action.viewcart" timeout="30"/> <element name="miniCartItemsText" type="text" selector=".minicart-items"/> <element name="deleteMiniCartItem" type="button" selector=".action.delete" timeout="30"/> + <element name="miniCartSubtotalField" type="text" selector=".block-minicart .amount span.price"/> </section> </sections> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminSalesConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminSalesConfigPage.xml new file mode 100644 index 0000000000000..1a99ff6533dbb --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminSalesConfigPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="AdminSalesConfigPage" url="admin/system_config/edit/section/sales/{{var1}}" area="admin" parameterized="true" module="Magento_Config"> + <section name="AdminSalesConfigSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml new file mode 100644 index 0000000000000..4897e8415c1b8 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminSalesConfigSection"> + <element name="enableMAPUseSystemValue" type="checkbox" selector="#sales_msrp_enabled_inherit"/> + <element name="enableMAPSelect" type="select" selector="#sales_msrp_enabled"/> + </section> +</sections> \ No newline at end of file From 54748b016ea0e667e5368700fe29da883c21f767 Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Fri, 6 Jul 2018 09:20:27 -0500 Subject: [PATCH 0138/1171] MC-231: Customer should be able to select customisable bundle product options and set quantity for them --- .../Section/StorefrontBundledSection.xml | 12 ++--- ...tCustomerSelectAndSetBundleOptionsTest.xml | 54 ++++++++++--------- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/StorefrontBundledSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/StorefrontBundledSection.xml index 8b02f28dc7df3..ae69f065021a9 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/StorefrontBundledSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Section/StorefrontBundledSection.xml @@ -23,11 +23,11 @@ <element name="nthItemOptionsValue" type="text" selector="dl.item-options dd:nth-of-type({{var}})" parameterized="true"/> <element name="bundleProductName" type="text" selector="//*[@id='maincontent']//span[@itemprop='name']"/> <element name="pageNotFound" type="text" selector="//h1[@class='page-title']//span[contains(., 'Whoops, our bad...')]"/> - <element name="dropDownOptionOneProducts" type="select" selector="//label//span[contains(text(), 'Option One')]/../..//div[@class='control']//select"/> - <element name="dropDownOptionOneQuantity" type="input" selector="//span[contains(text(), 'Option One')]/../..//input"/> - <element name="radioButtonOptionTwoProducts" type="checkbox" selector="//label//span[contains(text(), 'Option Two')]/../..//div[@class='control']//div[@class='field choice'][{{productNumber}}]/input" parameterized="true"/> - <element name="radioButtonOptionTwoQuantity" type="input" selector="//label//span[contains(text(), 'Option Two')]/../..//div[@class='control']//div[@class='field qty qty-holder']//input"/> - <element name="checkboxOptionThreeProducts" type="checkbox" selector="//label//span[contains(text(), 'Option Three')]/../..//div[@class='control']//div[@class='field choice'][{{productNumber}}]/input" parameterized="true"/> - <element name="multiselectOptionFourProducts" type="multiselect" selector="//label//span[contains(text(), 'Option Four')]/../..//select[@multiple='multiple']"/> + <element name="dropDownOptionOneProducts" type="select" selector="//label//span[contains(text(), '{{productName}}')]/../..//div[@class='control']//select" parameterized="true"/> + <element name="dropDownOptionOneQuantity" type="input" selector="//span[contains(text(), '{{productName}}')]/../..//input" parameterized="true"/> + <element name="radioButtonOptionTwoProducts" type="checkbox" selector="//label//span[contains(text(), '{{productName}}')]/../..//div[@class='control']//div[@class='field choice'][{{productNumber}}]/input" parameterized="true"/> + <element name="radioButtonOptionTwoQuantity" type="input" selector="//label//span[contains(text(), '{{productName}}')]/../..//div[@class='control']//div[@class='field qty qty-holder']//input" parameterized="true"/> + <element name="checkboxOptionThreeProducts" type="checkbox" selector="//label//span[contains(text(), '{{productName}}')]/../..//div[@class='control']//div[@class='field choice'][{{productNumber}}]/input" parameterized="true"/> + <element name="multiselectOptionFourProducts" type="multiselect" selector="//label//span[contains(text(), '{{productName}}')]/../..//select[@multiple='multiple']" parameterized="true"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml index 23e65e14b278b..90a94e2c429ae 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml @@ -16,7 +16,7 @@ <description value="Customer should be able to select customisable bundle product options and set quantity for them"/> <severity value="CRITICAL"/> <testCaseId value="MC-231"/> - <group value="Bundle"/> + <group value="banana"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> @@ -24,6 +24,10 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> </before> <after> + <!-- Delete the bundled product --> + <actionGroup stepKey="deleteBundle" ref="deleteProductUsingProductGrid"> + <argument name="product" value="BundleProduct"/> + </actionGroup> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="logout"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> @@ -92,41 +96,41 @@ <!--Select options - set quantities--> <!--"Drop-down" type option--> - <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts}}" userInput="$$simpleProduct1.sku$$ +$123.00" stepKey="selectOption0Product0"/> - <seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts}}" userInput="$$simpleProduct1.sku$$ +$123.00" stepKey="checkOption0Product0"/> - <fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity}}" userInput="3" stepKey="fillQuantity00"/> - <seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity}}" userInput="3" stepKey="checkQuantity00"/> + <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$simpleProduct1.sku$$ +$$$simpleProduct1.price$$.00" stepKey="selectOption0Product0"/> + <seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$simpleProduct1.sku$$ +$$$simpleProduct1.price$$.00" stepKey="checkOption0Product0"/> + <fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="3" stepKey="fillQuantity00"/> + <seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="3" stepKey="checkQuantity00"/> - <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts}}" userInput="$$simpleProduct2.sku$$ +$123.00" stepKey="selectOption0Product1"/> - <seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts}}" userInput="$$simpleProduct2.sku$$ +$123.00" stepKey="checkOption0Product1"/> - <fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity}}" userInput="3" stepKey="fillQuantity01"/> - <seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity}}" userInput="3" stepKey="checkQuantity01"/> + <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$simpleProduct2.sku$$ +$$$simpleProduct2.price$$.00" stepKey="selectOption0Product1"/> + <seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$simpleProduct2.sku$$ +$$$simpleProduct2.price$$.00" stepKey="checkOption0Product1"/> + <fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="3" stepKey="fillQuantity01"/> + <seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="3" stepKey="checkQuantity01"/> <!--"Radio Buttons" type option--> - <checkOption selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('1')}}" stepKey="selectOption1Product0"/> - <seeCheckboxIsChecked selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('1')}}" stepKey="checkOption1Product0"/> - <fillField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity}}" userInput="3" stepKey="fillQuantity10"/> - <seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity}}" userInput="3" stepKey="checkQuantity10"/> + <checkOption selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '1')}}" stepKey="selectOption1Product0"/> + <seeCheckboxIsChecked selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '1')}}" stepKey="checkOption1Product0"/> + <fillField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="3" stepKey="fillQuantity10"/> + <seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="3" stepKey="checkQuantity10"/> - <checkOption selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('2')}}" stepKey="selectOption1Product1"/> - <seeCheckboxIsChecked selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('2')}}" stepKey="checkOption1Product1"/> - <fillField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity}}" userInput="3" stepKey="fillQuantity11"/> - <seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity}}" userInput="3" stepKey="checkQuantity11"/> + <checkOption selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '2')}}" stepKey="selectOption1Product1"/> + <seeCheckboxIsChecked selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '2')}}" stepKey="checkOption1Product1"/> + <fillField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="3" stepKey="fillQuantity11"/> + <seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="3" stepKey="checkQuantity11"/> <!--"Checkbox" type option--> <!--This option does not support user defined quantities--> - <checkOption selector="{{StorefrontBundledSection.checkboxOptionThreeProducts('1')}}" stepKey="selectOption2Product0"/> - <seeCheckboxIsChecked selector="{{StorefrontBundledSection.checkboxOptionThreeProducts('1')}}" stepKey="checkOption2Product0"/> + <checkOption selector="{{StorefrontBundledSection.checkboxOptionThreeProducts('Option Three', '1')}}" stepKey="selectOption2Product0"/> + <seeCheckboxIsChecked selector="{{StorefrontBundledSection.checkboxOptionThreeProducts('Option Three', '1')}}" stepKey="checkOption2Product0"/> - <checkOption selector="{{StorefrontBundledSection.checkboxOptionThreeProducts('2')}}" stepKey="selectOption2Product1"/> - <seeCheckboxIsChecked selector="{{StorefrontBundledSection.checkboxOptionThreeProducts('2')}}" stepKey="checkOption2Product1"/> + <checkOption selector="{{StorefrontBundledSection.checkboxOptionThreeProducts('Option Three', '2')}}" stepKey="selectOption2Product1"/> + <seeCheckboxIsChecked selector="{{StorefrontBundledSection.checkboxOptionThreeProducts('Option Three', '2')}}" stepKey="checkOption2Product1"/> <!--"Multi Select" type option--> <!--This option does not support user defined quantities--> - <selectOption selector="{{StorefrontBundledSection.multiselectOptionFourProducts}}" userInput="$$simpleProduct1.sku$$ +$123.00" stepKey="selectOption3Product0"/> - <seeOptionIsSelected selector="{{StorefrontBundledSection.multiselectOptionFourProducts}}" userInput="$$simpleProduct1.sku$$ +$123.00" stepKey="checkOption3Product0"/> + <selectOption selector="{{StorefrontBundledSection.multiselectOptionFourProducts('Option Four')}}" userInput="$$simpleProduct1.sku$$ +$$$simpleProduct1.price$$.00" stepKey="selectOption3Product0"/> + <seeOptionIsSelected selector="{{StorefrontBundledSection.multiselectOptionFourProducts('Option Four')}}" userInput="$$simpleProduct1.sku$$ +$$$simpleProduct1.price$$.00" stepKey="checkOption3Product0"/> - <selectOption selector="{{StorefrontBundledSection.multiselectOptionFourProducts}}" userInput="$$simpleProduct2.sku$$ +$123.00" stepKey="selectOption3Product1"/> - <seeOptionIsSelected selector="{{StorefrontBundledSection.multiselectOptionFourProducts}}" userInput="$$simpleProduct2.sku$$ +$123.00" stepKey="checkOption3Product1"/> + <selectOption selector="{{StorefrontBundledSection.multiselectOptionFourProducts('Option Four')}}" userInput="$$simpleProduct2.sku$$ +$$$simpleProduct2.price$$.00" stepKey="selectOption3Product1"/> + <seeOptionIsSelected selector="{{StorefrontBundledSection.multiselectOptionFourProducts('Option Four')}}" userInput="$$simpleProduct2.sku$$ +$$$simpleProduct2.price$$.00" stepKey="checkOption3Product1"/> </test> </tests> From bbc96ac95f02245ec2d249523a710c772b3c85cf Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Fri, 6 Jul 2018 10:56:18 -0500 Subject: [PATCH 0139/1171] MC-231: Customer should be able to select customisable bundle product options and set quantity for them adding back composer.lock file --- dev/tests/acceptance/composer.lock | 4238 ++++++++++++++++++++++++++++ 1 file changed, 4238 insertions(+) create mode 100644 dev/tests/acceptance/composer.lock diff --git a/dev/tests/acceptance/composer.lock b/dev/tests/acceptance/composer.lock new file mode 100644 index 0000000000000..afe2640b442f1 --- /dev/null +++ b/dev/tests/acceptance/composer.lock @@ -0,0 +1,4238 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "content-hash": "9f292f6e226938ad82d1189da7aa4024", + "packages": [ + { + "name": "allure-framework/allure-codeception", + "version": "1.2.7", + "source": { + "type": "git", + "url": "https://github.com/allure-framework/allure-codeception.git", + "reference": "48598f4b4603b50b663bfe977260113a40912131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/allure-framework/allure-codeception/zipball/48598f4b4603b50b663bfe977260113a40912131", + "reference": "48598f4b4603b50b663bfe977260113a40912131", + "shasum": "" + }, + "require": { + "allure-framework/allure-php-api": "~1.1.0", + "codeception/codeception": "~2.1", + "php": ">=5.4.0", + "symfony/filesystem": ">=2.6", + "symfony/finder": ">=2.6" + }, + "type": "library", + "autoload": { + "psr-0": { + "Yandex": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Ivan Krutov", + "email": "vania-pooh@yandex-team.ru", + "role": "Developer" + } + ], + "description": "A Codeception adapter for Allure report.", + "homepage": "http://allure.qatools.ru/", + "keywords": [ + "allure", + "attachments", + "cases", + "codeception", + "report", + "steps", + "testing" + ], + "time": "2018-03-07T11:18:27+00:00" + }, + { + "name": "allure-framework/allure-php-api", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/allure-framework/allure-php-adapter-api.git", + "reference": "a462a0da121681577033e13c123b6cc4e89cdc64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/allure-framework/allure-php-adapter-api/zipball/a462a0da121681577033e13c123b6cc4e89cdc64", + "reference": "a462a0da121681577033e13c123b6cc4e89cdc64", + "shasum": "" + }, + "require": { + "jms/serializer": ">=0.16.0", + "moontoast/math": ">=1.1.0", + "php": ">=5.4.0", + "phpunit/phpunit": ">=4.0.0", + "ramsey/uuid": ">=3.0.0", + "symfony/http-foundation": ">=2.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Yandex": [ + "src/", + "test/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Ivan Krutov", + "email": "vania-pooh@yandex-team.ru", + "role": "Developer" + } + ], + "description": "PHP API for Allure adapter", + "homepage": "http://allure.qatools.ru/", + "keywords": [ + "allure", + "api", + "php", + "report" + ], + "time": "2016-12-07T12:15:46+00:00" + }, + { + "name": "behat/gherkin", + "version": "v4.4.5", + "source": { + "type": "git", + "url": "https://github.com/Behat/Gherkin.git", + "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", + "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", + "shasum": "" + }, + "require": { + "php": ">=5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "~4.5|~5", + "symfony/phpunit-bridge": "~2.7|~3", + "symfony/yaml": "~2.3|~3" + }, + "suggest": { + "symfony/yaml": "If you want to parse features, represented in YAML files" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Gherkin": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Gherkin DSL parser for PHP 5.3", + "homepage": "http://behat.org/", + "keywords": [ + "BDD", + "Behat", + "Cucumber", + "DSL", + "gherkin", + "parser" + ], + "time": "2016-10-30T11:50:56+00:00" + }, + { + "name": "codeception/codeception", + "version": "2.3.9", + "source": { + "type": "git", + "url": "https://github.com/Codeception/Codeception.git", + "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e", + "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e", + "shasum": "" + }, + "require": { + "behat/gherkin": "~4.4.0", + "codeception/stub": "^1.0", + "ext-json": "*", + "ext-mbstring": "*", + "facebook/webdriver": ">=1.1.3 <2.0", + "guzzlehttp/guzzle": ">=4.1.4 <7.0", + "guzzlehttp/psr7": "~1.0", + "php": ">=5.4.0 <8.0", + "phpunit/php-code-coverage": ">=2.2.4 <6.0", + "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", + "sebastian/comparator": ">1.1 <3.0", + "sebastian/diff": ">=1.4 <3.0", + "symfony/browser-kit": ">=2.7 <5.0", + "symfony/console": ">=2.7 <5.0", + "symfony/css-selector": ">=2.7 <5.0", + "symfony/dom-crawler": ">=2.7 <5.0", + "symfony/event-dispatcher": ">=2.7 <5.0", + "symfony/finder": ">=2.7 <5.0", + "symfony/yaml": ">=2.7 <5.0" + }, + "require-dev": { + "codeception/specify": "~0.3", + "facebook/graph-sdk": "~5.3", + "flow/jsonpath": "~0.2", + "monolog/monolog": "~1.8", + "pda/pheanstalk": "~3.0", + "php-amqplib/php-amqplib": "~2.4", + "predis/predis": "^1.0", + "squizlabs/php_codesniffer": "~2.0", + "symfony/process": ">=2.7 <5.0", + "vlucas/phpdotenv": "^2.4.0" + }, + "suggest": { + "aws/aws-sdk-php": "For using AWS Auth in REST module and Queue module", + "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests", + "codeception/specify": "BDD-style code blocks", + "codeception/verify": "BDD-style assertions", + "flow/jsonpath": "For using JSONPath in REST module", + "league/factory-muffin": "For DataFactory module", + "league/factory-muffin-faker": "For Faker support in DataFactory module", + "phpseclib/phpseclib": "for SFTP option in FTP Module", + "stecman/symfony-console-completion": "For BASH autocompletion", + "symfony/phpunit-bridge": "For phpunit-bridge support" + }, + "bin": [ + "codecept" + ], + "type": "library", + "extra": { + "branch-alias": [] + }, + "autoload": { + "psr-4": { + "Codeception\\": "src\\Codeception", + "Codeception\\Extension\\": "ext" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk", + "email": "davert@mail.ua", + "homepage": "http://codegyre.com" + } + ], + "description": "BDD-style testing framework", + "homepage": "http://codeception.com/", + "keywords": [ + "BDD", + "TDD", + "acceptance testing", + "functional testing", + "unit testing" + ], + "time": "2018-02-26T23:29:41+00:00" + }, + { + "name": "codeception/stub", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/Codeception/Stub.git", + "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/95fb7a36b81890dd2e5163e7ab31310df6f1bb99", + "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99", + "shasum": "" + }, + "require": { + "phpunit/phpunit-mock-objects": ">2.3 <7.0" + }, + "require-dev": { + "phpunit/phpunit": ">=4.8 <8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Codeception\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", + "time": "2018-02-18T13:56:56+00:00" + }, + { + "name": "consolidation/annotated-command", + "version": "2.8.3", + "source": { + "type": "git", + "url": "https://github.com/consolidation/annotated-command.git", + "reference": "8f8f5da2ca06fbd3a85f7d551c49f844b7c59437" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/8f8f5da2ca06fbd3a85f7d551c49f844b7c59437", + "reference": "8f8f5da2ca06fbd3a85f7d551c49f844b7c59437", + "shasum": "" + }, + "require": { + "consolidation/output-formatters": "^3.1.12", + "php": ">=5.4.0", + "psr/log": "^1", + "symfony/console": "^2.8|^3|^4", + "symfony/event-dispatcher": "^2.5|^3|^4", + "symfony/finder": "^2.5|^3|^4" + }, + "require-dev": { + "greg-1-anderson/composer-test-scenarios": "^1", + "phpunit/phpunit": "^4.8", + "satooshi/php-coveralls": "^1.0.2 | dev-master", + "squizlabs/php_codesniffer": "^2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\AnnotatedCommand\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Initialize Symfony Console commands from annotated command class methods.", + "time": "2018-02-23T16:32:04+00:00" + }, + { + "name": "consolidation/config", + "version": "1.0.9", + "source": { + "type": "git", + "url": "https://github.com/consolidation/config.git", + "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/config/zipball/34ca8d7c1ee60a7b591b10617114cf1210a2e92c", + "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^1.1.0", + "grasmash/expander": "^1", + "php": ">=5.4.0" + }, + "require-dev": { + "greg-1-anderson/composer-test-scenarios": "^1", + "phpunit/phpunit": "^4", + "satooshi/php-coveralls": "^1.0", + "squizlabs/php_codesniffer": "2.*", + "symfony/console": "^2.5|^3|^4", + "symfony/yaml": "^2.8.11|^3|^4" + }, + "suggest": { + "symfony/yaml": "Required to use Consolidation\\Config\\Loader\\YamlConfigLoader" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Provide configuration services for a commandline tool.", + "time": "2017-12-22T17:28:19+00:00" + }, + { + "name": "consolidation/log", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/consolidation/log.git", + "reference": "dbc7c535f319a4a2d5a5077738f8eb7c10df8821" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/log/zipball/dbc7c535f319a4a2d5a5077738f8eb7c10df8821", + "reference": "dbc7c535f319a4a2d5a5077738f8eb7c10df8821", + "shasum": "" + }, + "require": { + "php": ">=5.5.0", + "psr/log": "~1.0", + "symfony/console": "^2.8|^3|^4" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "satooshi/php-coveralls": "dev-master", + "squizlabs/php_codesniffer": "2.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", + "time": "2017-11-29T01:44:16+00:00" + }, + { + "name": "consolidation/output-formatters", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/consolidation/output-formatters.git", + "reference": "da889e4bce19f145ca4ec5b1725a946f4eb625a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/da889e4bce19f145ca4ec5b1725a946f4eb625a9", + "reference": "da889e4bce19f145ca4ec5b1725a946f4eb625a9", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "symfony/console": "^2.8|^3|^4", + "symfony/finder": "^2.5|^3|^4" + }, + "require-dev": { + "g-1-a/composer-test-scenarios": "^2", + "phpunit/phpunit": "^5.7.27", + "satooshi/php-coveralls": "^2", + "squizlabs/php_codesniffer": "^2.7", + "symfony/console": "3.2.3", + "symfony/var-dumper": "^2.8|^3|^4", + "victorjonsson/markdowndocs": "^1.3" + }, + "suggest": { + "symfony/var-dumper": "For using the var_dump formatter" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\OutputFormatters\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Format text by applying transformations provided by plug-in formatters.", + "time": "2018-03-20T15:18:32+00:00" + }, + { + "name": "consolidation/robo", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/consolidation/Robo.git", + "reference": "54a13e268917b92576d75e10dca8227b95a574d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/54a13e268917b92576d75e10dca8227b95a574d9", + "reference": "54a13e268917b92576d75e10dca8227b95a574d9", + "shasum": "" + }, + "require": { + "consolidation/annotated-command": "^2.8.2", + "consolidation/config": "^1.0.1", + "consolidation/log": "~1", + "consolidation/output-formatters": "^3.1.13", + "grasmash/yaml-expander": "^1.3", + "league/container": "^2.2", + "php": ">=5.5.0", + "symfony/console": "^2.8|^3|^4", + "symfony/event-dispatcher": "^2.5|^3|^4", + "symfony/filesystem": "^2.5|^3|^4", + "symfony/finder": "^2.5|^3|^4", + "symfony/process": "^2.5|^3|^4" + }, + "replace": { + "codegyre/robo": "< 1.0" + }, + "require-dev": { + "codeception/aspect-mock": "^1|^2.1.1", + "codeception/base": "^2.3.7", + "codeception/verify": "^0.3.2", + "g-1-a/composer-test-scenarios": "^2", + "goaop/framework": "~2.1.2", + "goaop/parser-reflection": "^1.1.0", + "natxet/cssmin": "3.0.4", + "nikic/php-parser": "^3.1.5", + "patchwork/jsqueeze": "~2", + "pear/archive_tar": "^1.4.2", + "phpunit/php-code-coverage": "~2|~4", + "satooshi/php-coveralls": "^2", + "squizlabs/php_codesniffer": "^2.8" + }, + "suggest": { + "henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch", + "natxet/CssMin": "For minifying CSS files in taskMinify", + "patchwork/jsqueeze": "For minifying JS files in taskMinify", + "pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively." + }, + "bin": [ + "robo" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev", + "dev-state": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Robo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Davert", + "email": "davert.php@resend.cc" + } + ], + "description": "Modern task runner", + "time": "2018-04-06T05:27:37+00:00" + }, + { + "name": "container-interop/container-interop", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/container-interop/container-interop.git", + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", + "shasum": "" + }, + "require": { + "psr/container": "^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Interop\\Container\\": "src/Interop/Container/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", + "homepage": "https://github.com/container-interop/container-interop", + "time": "2017-02-14T19:40:03+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/3fbd874921ab2c041e899d044585a2ab9795df8a", + "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Dflydev\\DotAccessData": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "time": "2017-01-20T21:14:22+00:00" + }, + { + "name": "doctrine/annotations", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", + "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": "^7.1" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2017-12-06T07:11:42+00:00" + }, + { + "name": "doctrine/collections", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/a01ee38fcd999f34d9bfbcee59dbda5105449cbf", + "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "~0.1@dev", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ], + "time": "2017-07-22T10:37:32+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "^6.2.3", + "squizlabs/php_codesniffer": "^3.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2017-07-22T11:58:36+00:00" + }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2014-09-09T13:34:57+00:00" + }, + { + "name": "epfremme/swagger-php", + "version": "v2.0.0", + "source": { + "type": "git", + "url": "https://github.com/epfremmer/swagger-php.git", + "reference": "eee28a442b7e6220391ec953d3c9b936354f23bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/epfremmer/swagger-php/zipball/eee28a442b7e6220391ec953d3c9b936354f23bc", + "reference": "eee28a442b7e6220391ec953d3c9b936354f23bc", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.2", + "doctrine/collections": "^1.3", + "jms/serializer": "^1.1", + "php": ">=5.5", + "phpoption/phpoption": "^1.1", + "symfony/yaml": "^2.7|^3.1" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "~4.8|~5.0", + "satooshi/php-coveralls": "^1.0" + }, + "type": "package", + "autoload": { + "psr-4": { + "Epfremme\\Swagger\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Edward Pfremmer", + "email": "epfremme@nerdery.com" + } + ], + "description": "Library for parsing swagger documentation into PHP entities for use in testing and code generation", + "time": "2016-09-26T17:24:17+00:00" + }, + { + "name": "facebook/webdriver", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/facebook/php-webdriver.git", + "reference": "86b5ca2f67173c9d34340845dd690149c886a605" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/86b5ca2f67173c9d34340845dd690149c886a605", + "reference": "86b5ca2f67173c9d34340845dd690149c886a605", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-zip": "*", + "php": "^5.6 || ~7.0", + "symfony/process": "^2.8 || ^3.1 || ^4.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.0", + "guzzle/guzzle": "^3.4.1", + "php-coveralls/php-coveralls": "^1.0.2", + "php-mock/php-mock-phpunit": "^1.1", + "phpunit/phpunit": "^5.7", + "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", + "squizlabs/php_codesniffer": "^2.6", + "symfony/var-dumper": "^3.3 || ^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-community": "1.5-dev" + } + }, + "autoload": { + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "A PHP client for Selenium WebDriver", + "homepage": "https://github.com/facebook/php-webdriver", + "keywords": [ + "facebook", + "php", + "selenium", + "webdriver" + ], + "time": "2017-11-15T11:08:09+00:00" + }, + { + "name": "flow/jsonpath", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/FlowCommunications/JSONPath.git", + "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FlowCommunications/JSONPath/zipball/f0222818d5c938e4ab668ab2e2c079bd51a27112", + "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "peekmo/jsonpath": "dev-master", + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Flow\\JSONPath": "src/", + "Flow\\JSONPath\\Test": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Frank", + "email": "stephen@flowsa.com" + } + ], + "description": "JSONPath implementation for parsing, searching and flattening arrays", + "time": "2018-03-04T16:39:47+00:00" + }, + { + "name": "fzaninotto/faker", + "version": "v1.7.1", + "source": { + "type": "git", + "url": "https://github.com/fzaninotto/Faker.git", + "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", + "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "ext-intl": "*", + "phpunit/phpunit": "^4.0 || ^5.0", + "squizlabs/php_codesniffer": "^1.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "time": "2017-08-15T16:48:10+00:00" + }, + { + "name": "grasmash/expander", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/grasmash/expander.git", + "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/grasmash/expander/zipball/95d6037344a4be1dd5f8e0b0b2571a28c397578f", + "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^1.1.0", + "php": ">=5.4" + }, + "require-dev": { + "greg-1-anderson/composer-test-scenarios": "^1", + "phpunit/phpunit": "^4|^5.5.4", + "satooshi/php-coveralls": "^1.0.2|dev-master", + "squizlabs/php_codesniffer": "^2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Grasmash\\Expander\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Grasmick" + } + ], + "description": "Expands internal property references in PHP arrays file.", + "time": "2017-12-21T22:14:55+00:00" + }, + { + "name": "grasmash/yaml-expander", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/grasmash/yaml-expander.git", + "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/grasmash/yaml-expander/zipball/3f0f6001ae707a24f4d9733958d77d92bf9693b1", + "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^1.1.0", + "php": ">=5.4", + "symfony/yaml": "^2.8.11|^3|^4" + }, + "require-dev": { + "greg-1-anderson/composer-test-scenarios": "^1", + "phpunit/phpunit": "^4.8|^5.5.4", + "satooshi/php-coveralls": "^1.0.2|dev-master", + "squizlabs/php_codesniffer": "^2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Grasmash\\YamlExpander\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Grasmick" + } + ], + "description": "Expands internal property references in a yaml file.", + "time": "2017-12-16T16:06:03+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "6.3.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/68d0ea14d5a3f42a20e87632a5f84931e2709c90", + "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90", + "shasum": "" + }, + "require": { + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4", + "psr/log": "^1.0" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.3-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2018-03-26T16:33:04+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2016-12-20T10:07:11+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2017-03-20T17:10:46+00:00" + }, + { + "name": "jms/metadata", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/metadata.git", + "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/6a06970a10e0a532fb52d3959547123b84a3b3ab", + "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "doctrine/cache": "~1.0", + "symfony/cache": "~3.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + }, + "autoload": { + "psr-0": { + "Metadata\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Class/method/property metadata management in PHP", + "keywords": [ + "annotations", + "metadata", + "xml", + "yaml" + ], + "time": "2016-12-05T10:18:33+00:00" + }, + { + "name": "jms/parser-lib", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/parser-lib.git", + "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/parser-lib/zipball/c509473bc1b4866415627af0e1c6cc8ac97fa51d", + "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d", + "shasum": "" + }, + "require": { + "phpoption/phpoption": ">=0.9,<2.0-dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "JMS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache2" + ], + "description": "A library for easily creating recursive-descent parsers.", + "time": "2012-11-18T18:08:43+00:00" + }, + { + "name": "jms/serializer", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/serializer.git", + "reference": "e7c53477ff55c21d1b1db7d062edc050a24f465f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/e7c53477ff55c21d1b1db7d062edc050a24f465f", + "reference": "e7c53477ff55c21d1b1db7d062edc050a24f465f", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.0", + "doctrine/instantiator": "^1.0.3", + "jms/metadata": "~1.1", + "jms/parser-lib": "1.*", + "php": "^5.5|^7.0", + "phpcollection/phpcollection": "~0.1", + "phpoption/phpoption": "^1.1" + }, + "conflict": { + "twig/twig": "<1.12" + }, + "require-dev": { + "doctrine/orm": "~2.1", + "doctrine/phpcr-odm": "^1.3|^2.0", + "ext-pdo_sqlite": "*", + "jackalope/jackalope-doctrine-dbal": "^1.1.5", + "phpunit/phpunit": "^4.8|^5.0", + "propel/propel1": "~1.7", + "psr/container": "^1.0", + "symfony/dependency-injection": "^2.7|^3.3|^4.0", + "symfony/expression-language": "^2.6|^3.0", + "symfony/filesystem": "^2.1", + "symfony/form": "~2.1|^3.0", + "symfony/translation": "^2.1|^3.0", + "symfony/validator": "^2.2|^3.0", + "symfony/yaml": "^2.1|^3.0", + "twig/twig": "~1.12|~2.0" + }, + "suggest": { + "doctrine/cache": "Required if you like to use cache functionality.", + "doctrine/collections": "Required if you like to use doctrine collection types as ArrayCollection.", + "symfony/yaml": "Required if you'd like to serialize data to YAML format." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-0": { + "JMS\\Serializer": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + }, + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.", + "homepage": "http://jmsyst.com/libs/serializer", + "keywords": [ + "deserialization", + "jaxb", + "json", + "serialization", + "xml" + ], + "time": "2018-02-04T17:48:54+00:00" + }, + { + "name": "league/container", + "version": "2.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/container.git", + "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/container/zipball/43f35abd03a12977a60ffd7095efd6a7808488c0", + "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.2", + "php": "^5.4.0 || ^7.0" + }, + "provide": { + "container-interop/container-interop-implementation": "^1.2", + "psr/container-implementation": "^1.0" + }, + "replace": { + "orno/di": "~2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Container\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Phil Bennett", + "email": "philipobenito@gmail.com", + "homepage": "http://www.philipobenito.com", + "role": "Developer" + } + ], + "description": "A fast and intuitive dependency injection container.", + "homepage": "https://github.com/thephpleague/container", + "keywords": [ + "container", + "dependency", + "di", + "injection", + "league", + "provider", + "service" + ], + "time": "2017-05-10T09:20:27+00:00" + }, + { + "name": "magento/magento2-functional-testing-framework", + "version": "2.2.0", + "source": { + "type": "git", + "url": "git@github.com:magento/magento2-functional-testing-framework.git", + "reference": "4dd196d745bf836cbf0c5904a1df6dd241124309" + }, + "require": { + "allure-framework/allure-codeception": "~1.2.6", + "codeception/codeception": "~2.3.4", + "consolidation/robo": "^1.0.0", + "epfremme/swagger-php": "^2.0", + "flow/jsonpath": ">0.2", + "fzaninotto/faker": "^1.6", + "mustache/mustache": "~2.5", + "php": "7.0.2|7.0.4|~7.0.6|~7.1.0|~7.2.0", + "symfony/process": "^2.8 || ^3.1 || ^4.0", + "vlucas/phpdotenv": "^2.4" + }, + "require-dev": { + "brainmaestro/composer-git-hooks": "^2.3", + "codacy/coverage": "^1.4", + "codeception/aspect-mock": "^2.0", + "goaop/framework": "2.1.2", + "php-coveralls/php-coveralls": "^1.0", + "phpmd/phpmd": "^2.6.0", + "rregeer/phpunit-coverage-check": "^0.1.4", + "sebastian/phpcpd": "~3.0", + "squizlabs/php_codesniffer": "1.5.3", + "symfony/stopwatch": "~3.4.6" + }, + "bin": [ + "bin/mftf" + ], + "type": "library", + "extra": { + "hooks": { + "pre-push": "bin/all-checks" + } + }, + "autoload": { + "psr-4": { + "Magento\\FunctionalTestingFramework\\": "src/Magento/FunctionalTestingFramework", + "MFTF\\": "dev/tests/functional/MFTF" + } + }, + "autoload-dev": { + "psr-4": { + "tests\\unit\\": "dev/tests/unit" + } + }, + "scripts": { + "tests": [ + "bin/phpunit-checks" + ], + "static": [ + "bin/static-checks" + ] + }, + "license": [ + "AGPL-3.0" + ], + "description": "Magento2 Functional Testing Framework", + "keywords": [ + "automation", + "functional", + "magento", + "testing" + ], + "time": "2018-04-24T01:46:09+00:00" + }, + { + "name": "moontoast/math", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/ramsey/moontoast-math.git", + "reference": "c2792a25df5cad4ff3d760dd37078fc5b6fccc79" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/moontoast-math/zipball/c2792a25df5cad4ff3d760dd37078fc5b6fccc79", + "reference": "c2792a25df5cad4ff3d760dd37078fc5b6fccc79", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "jakub-onderka/php-parallel-lint": "^0.9.0", + "phpunit/phpunit": "^4.7|>=5.0 <5.4", + "satooshi/php-coveralls": "^0.6.1", + "squizlabs/php_codesniffer": "^2.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Moontoast\\Math\\": "src/Moontoast/Math/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A mathematics library, providing functionality for large numbers", + "homepage": "https://github.com/ramsey/moontoast-math", + "keywords": [ + "bcmath", + "math" + ], + "time": "2017-02-16T16:54:46+00:00" + }, + { + "name": "mustache/mustache", + "version": "v2.12.0", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/mustache.php.git", + "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/fe8fe72e9d580591854de404cc59a1b83ca4d19e", + "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~1.11", + "phpunit/phpunit": "~3.7|~4.0|~5.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Mustache": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "A Mustache implementation in PHP.", + "homepage": "https://github.com/bobthecow/mustache.php", + "keywords": [ + "mustache", + "templating" + ], + "time": "2017-07-11T12:54:05+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2017-10-19T19:58:43+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v2.0.12", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/258c89a6b97de7dfaf5b8c7607d0478e236b04fb", + "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2018-04-04T21:24:14+00:00" + }, + { + "name": "phar-io/manifest", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", + "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^1.0.1", + "php": "^5.6 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2017-03-05T18:14:27+00:00" + }, + { + "name": "phar-io/version", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2017-03-05T17:38:23+00:00" + }, + { + "name": "phpcollection/phpcollection", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-collection.git", + "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-collection/zipball/f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", + "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", + "shasum": "" + }, + "require": { + "phpoption/phpoption": "1.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.4-dev" + } + }, + "autoload": { + "psr-0": { + "PhpCollection": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache2" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "General-Purpose Collection Library for PHP", + "keywords": [ + "collection", + "list", + "map", + "sequence", + "set" + ], + "time": "2015-05-17T12:39:23+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2017-09-11T18:02:19+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "4.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08", + "shasum": "" + }, + "require": { + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "doctrine/instantiator": "~1.0.5", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2017-11-30T07:14:17+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2017-07-14T14:27:02+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed", + "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-0": { + "PhpOption\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache2" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "time": "2015-07-25T16:39:46+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "1.7.6", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712", + "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2018-04-18T13:57:24+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "5.3.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "c89677919c5dd6d3b3852f230a663118762218ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", + "reference": "c89677919c5dd6d3b3852f230a663118762218ac", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.0", + "phpunit/php-file-iterator": "^1.4.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^2.0.1", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^3.0", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-xdebug": "^2.5.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2018-04-06T15:36:58+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2017-11-27T13:52:08+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.9", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2017-02-26T11:10:40+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "791198a2c6254db10131eecfe8c06670700904db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", + "reference": "791198a2c6254db10131eecfe8c06670700904db", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2017-11-27T05:48:46+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "6.5.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4f21a3c6b97c42952fd5c2837bb354ec0199b97b", + "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "^1.6.1", + "phar-io/manifest": "^1.0.1", + "phar-io/version": "^1.0", + "php": "^7.0", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^5.3", + "phpunit/php-file-iterator": "^1.4.3", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^1.0.9", + "phpunit/phpunit-mock-objects": "^5.0.5", + "sebastian/comparator": "^2.1", + "sebastian/diff": "^2.0", + "sebastian/environment": "^3.1", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^2.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^1.0", + "sebastian/version": "^2.0.1" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "3.0.2", + "phpunit/dbunit": "<3.0" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-xdebug": "*", + "phpunit/php-invoker": "^1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.5.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2018-04-10T11:38:34+00:00" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "5.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/33fd41a76e746b8fa96d00b49a23dadfa8334cdf", + "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.5", + "php": "^7.0", + "phpunit/php-text-template": "^1.2.1", + "sebastian/exporter": "^3.1" + }, + "conflict": { + "phpunit/phpunit": "<6.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2018-01-06T05:45:45+00:00" + }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10T12:19:37+00:00" + }, + { + "name": "ramsey/uuid", + "version": "3.7.3", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "44abcdad877d9a46685a3a4d221e3b2c4b87cb76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/44abcdad877d9a46685a3a4d221e3b2c4b87cb76", + "reference": "44abcdad877d9a46685a3a4d221e3b2c4b87cb76", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "^1.0|^2.0", + "php": "^5.4 || ^7.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "codeception/aspect-mock": "^1.0 | ~2.0.0", + "doctrine/annotations": "~1.2.0", + "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ^2.1", + "ircmaxell/random-lib": "^1.1", + "jakub-onderka/php-parallel-lint": "^0.9.0", + "mockery/mockery": "^0.9.9", + "moontoast/math": "^1.1", + "php-mock/php-mock-phpunit": "^0.3|^1.1", + "phpunit/phpunit": "^4.7|^5.0", + "squizlabs/php_codesniffer": "^2.3" + }, + "suggest": { + "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", + "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", + "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", + "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marijn Huizendveld", + "email": "marijn.huizendveld@gmail.com" + }, + { + "name": "Thibaud Fabre", + "email": "thibaud@aztech.io" + }, + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", + "homepage": "https://github.com/ramsey/uuid", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "time": "2018-01-20T00:28:24+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2017-03-04T06:30:41+00:00" + }, + { + "name": "sebastian/comparator", + "version": "2.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/diff": "^2.0 || ^3.0", + "sebastian/exporter": "^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2018-02-01T13:46:46+00:00" + }, + { + "name": "sebastian/diff", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2017-08-03T08:09:46+00:00" + }, + { + "name": "sebastian/environment", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2017-07-01T08:51:00+00:00" + }, + { + "name": "sebastian/exporter", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2017-04-03T13:19:02+00:00" + }, + { + "name": "sebastian/global-state", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2017-04-27T15:39:26+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-08-03T12:35:26+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "773f97c67f28de00d397be301821b06708fca0be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2017-03-29T09:07:27+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2017-03-03T06:23:57+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2015-07-28T20:34:47+00:00" + }, + { + "name": "sebastian/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-10-03T07:35:21+00:00" + }, + { + "name": "symfony/browser-kit", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "c43bfa0182363b3fd64331b5e64e467349ff4670" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c43bfa0182363b3fd64331b5e64e467349ff4670", + "reference": "c43bfa0182363b3fd64331b5e64e467349ff4670", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/dom-crawler": "~3.4|~4.0" + }, + "require-dev": { + "symfony/css-selector": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0" + }, + "suggest": { + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\BrowserKit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony BrowserKit Component", + "homepage": "https://symfony.com", + "time": "2018-03-19T22:35:49+00:00" + }, + { + "name": "symfony/console", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "aad9a6fe47319f22748fd764f52d3a7ca6fa6b64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/aad9a6fe47319f22748fd764f52d3a7ca6fa6b64", + "reference": "aad9a6fe47319f22748fd764f52d3a7ca6fa6b64", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2018-04-03T05:24:00+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "03f965583147957f1ecbad7ea1c9d6fd5e525ec2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/03f965583147957f1ecbad7ea1c9d6fd5e525ec2", + "reference": "03f965583147957f1ecbad7ea1c9d6fd5e525ec2", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2018-03-19T22:35:49+00:00" + }, + { + "name": "symfony/dom-crawler", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "d6c04c7532535b5e0b63db45b543cd60818e0fbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/d6c04c7532535b5e0b63db45b543cd60818e0fbc", + "reference": "d6c04c7532535b5e0b63db45b543cd60818e0fbc", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "~3.4|~4.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DomCrawler Component", + "homepage": "https://symfony.com", + "time": "2018-03-19T22:35:49+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "63353a71073faf08f62caab4e6889b06a787f07b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/63353a71073faf08f62caab4e6889b06a787f07b", + "reference": "63353a71073faf08f62caab4e6889b06a787f07b", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "conflict": { + "symfony/dependency-injection": "<3.4" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2018-04-06T07:35:43+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21", + "reference": "5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2018-02-22T10:50:29+00:00" + }, + { + "name": "symfony/finder", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "ca27c02b7a3fef4828c998c2ff9ba7aae1641c49" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/ca27c02b7a3fef4828c998c2ff9ba7aae1641c49", + "reference": "ca27c02b7a3fef4828c998c2ff9ba7aae1641c49", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2018-04-04T05:10:37+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "d0864a82e5891ab61d31eecbaa48bed5a09b8e6c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d0864a82e5891ab61d31eecbaa48bed5a09b8e6c", + "reference": "d0864a82e5891ab61d31eecbaa48bed5a09b8e6c", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.1" + }, + "require-dev": { + "symfony/expression-language": "~3.4|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony HttpFoundation Component", + "homepage": "https://symfony.com", + "time": "2018-04-03T05:24:00+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2018-01-30T19:27:44+00:00" + }, + { + "name": "symfony/process", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "d7dc1ee5dfe9f732cb1bba7310f5b99f2b7a6d25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/d7dc1ee5dfe9f732cb1bba7310f5b99f2b7a6d25", + "reference": "d7dc1ee5dfe9f732cb1bba7310f5b99f2b7a6d25", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2018-04-03T05:24:00+00:00" + }, + { + "name": "symfony/yaml", + "version": "v3.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "a42f9da85c7c38d59f5e53f076fe81a091f894d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a42f9da85c7c38d59f5e53f076fe81a091f894d0", + "reference": "a42f9da85c7c38d59f5e53f076fe81a091f894d0", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2018-04-03T05:14:20+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2017-04-07T12:08:54+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", + "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "phpunit/phpunit": "^4.8 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause-Attribution" + ], + "authors": [ + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "http://www.vancelucas.com" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "time": "2016-09-01T10:05:43+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2018-01-29T19:49:41+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} From f0f60b3d85a4bae20f3c63d350cb8dee02bac9aa Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Fri, 6 Jul 2018 11:03:14 -0500 Subject: [PATCH 0140/1171] MC-231: Customer should be able to select customisable bundle product options and set quantity for them --- .../Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle => app/code/Magento/Bundle/Test/Mftf}/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml (100%) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml rename to app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml From 980984ca6411075ff55f2d3a48d8250fdc8734bc Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Fri, 6 Jul 2018 11:22:03 -0500 Subject: [PATCH 0141/1171] MC-231: Customer should be able to select customisable bundle product options and set quantity for them --- .../Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml index 90a94e2c429ae..db300af4fb516 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml @@ -16,7 +16,7 @@ <description value="Customer should be able to select customisable bundle product options and set quantity for them"/> <severity value="CRITICAL"/> <testCaseId value="MC-231"/> - <group value="banana"/> + <group value="Bundle"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> From 9bc1ff325ebc338bdf1c40c9cb2c975fd7a4a11c Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Fri, 6 Jul 2018 11:22:50 -0500 Subject: [PATCH 0142/1171] MQE-1068: Require Issue ID for Skipped Test - Adding changes to skipped tests --- .../AdminConfigurationEnableDisableAnalyticsTest.xml | 5 +++-- .../Test/Mftf/Test/AdminConfigurationIndustryTest.xml | 5 +++-- .../Mftf/Test/AdminConfigurationPermissionTest.xml | 5 +++-- .../Mftf/Test/MassEnableDisableBundleProductsTest.xml | 5 +++-- ...minUnassignProductAttributeFromAttributeSetTest.xml | 5 +++-- .../Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml | 5 +++-- .../StorefrontPurchaseProductWithCustomOptions.xml | 5 +++-- ...ConfigurableProductPriceAdditionalStoreViewTest.xml | 4 +++- .../Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml | 10 ++++++---- .../Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml | 5 +++-- .../SampleTests/Test/AdvancedSampleTest.xml | 4 +++- .../FunctionalTest/SampleTests/Test/AssertsTest.xml | 4 +++- .../Test/CreateConfigurableProductByApiTest.xml | 4 +++- .../SampleTests/Test/CreateSalesRuleByApiTest.xml | 4 +++- .../SampleTests/Test/PersistMultipleEntitiesTest.xml | 4 +++- .../SampleTests/Test/SetPaymentConfigurationTest.xml | 8 ++++++-- .../SampleTests/Test/UpdateSimpleProductByApiTest.xml | 4 +++- 17 files changed, 57 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml index 8b0714cd2ef65..15c9727cc8c79 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml @@ -16,8 +16,9 @@ <severity value="MAJOR"/> <testCaseId value="MAGETWO-66465"/> <group value="analytics"/> - <!-- MAGETWO-90659 --> - <group value="skip"/> + <skip> + <issueId value="MAGETWO-90659"/> + </skip> </annotations> <after> <amOnPage stepKey="amOnLogoutPage" url="admin/admin/auth/logout/"/> diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml index 6b38bccf33dd5..d4f30737bae3e 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml @@ -17,8 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MAGETWO-63898"/> <group value="analytics"/> - <!-- MAGETWO-90659 --> - <group value="skip"/> + <skip> + <issueId value="MAGETWO-90659"/> + </skip> </annotations> <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml index 501dba3dba23b..b3ccd3afd1bf7 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml @@ -17,8 +17,9 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-82648"/> <group value="analytics"/> - <!-- MAGETWO-90659 --> - <group value="skip"/> + <skip> + <issueId value="MAGETWO-90659"/> + </skip> </annotations> <before> <createData stepKey="noReportUserRole" entity="adminNoReportRole"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml index 27d95cfb5cfa5..5354ce28b73ac 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml @@ -17,8 +17,9 @@ <severity value="CRITICAL"/> <testCaseId value="MC-217"/> <group value="Bundle"/> - <!--Skipped due to MAGETWO-92898--> - <group value="skip"/> + <skip> + <issueId value="MAGETWO-92898"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml index 1441e2175d08e..5a07b11a204af 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml @@ -17,8 +17,9 @@ <severity value="CRITICAL"/> <testCaseId value="MC-194"/> <group value="Catalog"/> - <!-- Skip because of MAGETWO-92780 --> - <group value="skip"/> + <skip> + <issueId value="MAGETWO-92780"/> + </skip> </annotations> <before> <createData entity="productDropDownAttribute" stepKey="attribute"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml index a9829b67189cb..d5d9de2648ecf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -16,8 +16,9 @@ <description value="Admin creates products, creates and manages categories, creates promotions, creates an order, processes an order, processes a return, uses admin grids"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-87014"/> - <!-- Skipped, see: https://jira.corp.magento.com/browse/MQE-891 --> - <group value="skip"/> + <skip> + <issueId value="MQE-891"/> + </skip> </annotations> <after> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml index 3a7feb34354d7..8628ae9b99d3a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml @@ -16,8 +16,9 @@ <description value="Admin should be able to sell products with different variants of their own"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-61717"/> - <!--Skip because of issue MAGETWO-90719--> - <group value="skip"/> + <skip> + <issueId value="MAGETWO-90719"/> + </skip> </annotations> <before> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml index 9467f82ebae24..676c23f1cfb88 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml @@ -15,7 +15,9 @@ <description value="Configurable product price should not disappear for additional stores on frontEnd if disabled for default store"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-92247"/> - <group value="skip"/> + <skip> + <issueId value="MAGETWO-92247"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml index 752585830638f..c0b32e4bc71e7 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml @@ -248,8 +248,9 @@ <severity value="CRITICAL"/> <testCaseId value="MC-297"/> <group value="Tax"/> - <group value="skip"/> - <!-- skipped due to MAGETWO-90539 --> + <skip> + <issueId value="MAGETWO-90539"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -356,8 +357,9 @@ <severity value="CRITICAL"/> <testCaseId value="MC-298"/> <group value="Tax"/> - <group value="skip"/> - <!-- Skipped due to MAGETWO-90539 --> + <skip> + <issueId value="MAGETWO-90539"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml b/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml index 4a1c7e07c38c6..0806011e8ffbc 100644 --- a/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml +++ b/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml @@ -17,8 +17,9 @@ <description value="Admin should able to switch between versions of TinyMCE"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-82936"/> - <!--Skip because of issue MAGETWO-89417--> - <group value="skip"/> + <skip> + <issueId value="MAGETWO-89417"/> + </skip> </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/AdvancedSampleTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/AdvancedSampleTest.xml index d7a03d4f0198f..4fead0e51497e 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/AdvancedSampleTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/AdvancedSampleTest.xml @@ -16,7 +16,9 @@ <description value=""/> <severity value="CRITICAL"/> <testCaseId value="#"/> - <group value="skip"/> + <skip> + <issueId value="SAMPLE"/> + </skip> </annotations> <before> <createData entity="SamplePerson" stepKey="beforeData"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/AssertsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/AssertsTest.xml index e1e0602d6103d..200c52cab797d 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/AssertsTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/AssertsTest.xml @@ -11,7 +11,9 @@ <test name="AssertsTest"> <annotations> <features value="SampleTests"/> - <group value="skip"/> + <skip> + <issueId value="SAMPLE"/> + </skip> </annotations> <createData entity="Simple_US_Customer" stepKey="createData2"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/CreateConfigurableProductByApiTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/CreateConfigurableProductByApiTest.xml index cf0ea0225a4b1..d759d1ab27498 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/CreateConfigurableProductByApiTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/CreateConfigurableProductByApiTest.xml @@ -12,7 +12,9 @@ <annotations> <features value="SampleTests"/> <stories value="Create a Configurable Product By API"/> - <group value="skip"/> + <skip> + <issueId value="SAMPLE"/> + </skip> </annotations> <before> <createData stepKey="categoryHandle" entity="SimpleSubCategory" /> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/CreateSalesRuleByApiTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/CreateSalesRuleByApiTest.xml index 8e9336dd6f149..8cd83fd62067f 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/CreateSalesRuleByApiTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/CreateSalesRuleByApiTest.xml @@ -12,7 +12,9 @@ <annotations> <features value="SampleTests"/> <stories value="Create a Sales Rule By API"/> - <group value="skip"/> + <skip> + <issueId value="SAMPLE"/> + </skip> </annotations> <before> <createData stepKey="saleRule" entity="SimpleSalesRule" /> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/PersistMultipleEntitiesTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/PersistMultipleEntitiesTest.xml index b809ab89cba52..6a196c2c32c02 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/PersistMultipleEntitiesTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/PersistMultipleEntitiesTest.xml @@ -11,7 +11,9 @@ <test name="PersistMultipleEntitiesTest"> <annotations> <features value="SampleTests"/> - <group value="skip"/> + <skip> + <issueId value="SAMPLE"/> + </skip> </annotations> <before> <createData entity="simplesubcategory" stepKey="simplecategory"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/SetPaymentConfigurationTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/SetPaymentConfigurationTest.xml index 1bc6bd1865f7c..f908c5cb4572e 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/SetPaymentConfigurationTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/SetPaymentConfigurationTest.xml @@ -11,7 +11,9 @@ <test name="SetPaypalConfigurationTest"> <annotations> <features value="SampleTests"/> - <group value="skip"/> + <skip> + <issueId value="SAMPLE"/> + </skip> </annotations> <createData entity="SamplePaypalConfig" stepKey="createSamplePaypalConfig"/> <createData entity="DefaultPayPalConfig" stepKey="restoreDefaultPaypalConfig"/> @@ -19,7 +21,9 @@ <test name="SetBraintreeConfigurationTest"> <annotations> <features value="SampleTests"/> - <group value="skip"/> + <skip> + <issueId value="SAMPLE"/> + </skip> </annotations> <createData entity="SampleBraintreeConfig" stepKey="createSampleBraintreeConfig"/> <createData entity="DefaultBraintreeConfig" stepKey="restoreDefaultBraintreeConfig"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/UpdateSimpleProductByApiTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/UpdateSimpleProductByApiTest.xml index fbf203834b45e..b18c24050d3a6 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/UpdateSimpleProductByApiTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/Test/UpdateSimpleProductByApiTest.xml @@ -11,7 +11,9 @@ <annotations> <features value="SampleTests"/> <stories value="Update simple product by api test."/> - <group value="skip"/> + <skip> + <issueId value="SAMPLE"/> + </skip> </annotations> <before> <createData stepKey="categoryHandle" entity="SimpleSubCategory"/> From 7e2d77ad9b4bbb1149d29582ce6e276e11fc2e7e Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Fri, 6 Jul 2018 11:24:41 -0500 Subject: [PATCH 0143/1171] MC-231: Customer should be able to select customisable bundle product options and set quantity for them --- .../Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml index db300af4fb516..26e5e436ed567 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> <test name="StorefrontCustomerSelectAndSetBundleOptionsTest"> <annotations> <features value="Bundle"/> From 7c72301985d79ddc91b69db697d913e72bcc6d77 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 6 Jul 2018 11:26:54 -0500 Subject: [PATCH 0144/1171] MAGETWO-91528: Customizable options truncated when displaying ordered product in admin --- ...frontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml | 2 ++ 1 file changed, 2 insertions(+) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog => app/code/Magento/Catalog/Test/Mftf}/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml (98%) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml similarity index 98% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml rename to app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index 378ff26a7e785..d77a096817852 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -15,6 +15,8 @@ <description value="Admin should be able to see the full title of the selected custom option value in the order"/> <severity value="MAJOR"/> <testCaseId value="MC-3043"/> + <group value="skip"/> + <!-- Skip due to MQE-1128 --> </annotations> <before> <!--Create Simple Product with Custom Options--> From a1f5fa5ea447e6190d2dcfabedd12cc677f263dd Mon Sep 17 00:00:00 2001 From: Oleh Kravets <xpoback@gmail.com> Date: Sun, 17 Jun 2018 10:59:17 +0200 Subject: [PATCH 0145/1171] magento/magento2#16184: Fix type error in payment void method --- app/code/Magento/Authorizenet/Model/Directpost.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Authorizenet/Model/Directpost.php b/app/code/Magento/Authorizenet/Model/Directpost.php index 5476fd05a0fed..d5c11ab54cd94 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost.php +++ b/app/code/Magento/Authorizenet/Model/Directpost.php @@ -814,10 +814,14 @@ protected function declineOrder(\Magento\Sales\Model\Order $order, $message = '' { try { $response = $this->getResponse(); - if ($voidPayment && $response->getXTransId() && strtoupper($response->getXType()) - == self::REQUEST_TYPE_AUTH_ONLY + if ($voidPayment + && $response->getXTransId() + && strtoupper($response->getXType()) == self::REQUEST_TYPE_AUTH_ONLY ) { - $order->getPayment()->setTransactionId(null)->setParentTransactionId($response->getXTransId())->void(); + $order->getPayment() + ->setTransactionId(null) + ->setParentTransactionId($response->getXTransId()) + ->void($response); } $order->registerCancellation($message)->save(); } catch (\Exception $e) { From 80069fcb4b75cbc98a1fa0af72518296ff645213 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 6 Jul 2018 11:35:39 -0500 Subject: [PATCH 0146/1171] MAGETWO-91528: Customizable options truncated when displaying ordered product in admin --- ...frontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index d77a096817852..9e0f80eb9ea5d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> <test name="StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle"> <annotations> <group value="Catalog"/> From b4ce314d6228206551dd8d5bd128b2ebbf9ec0a5 Mon Sep 17 00:00:00 2001 From: carstenpfeifer <carsten.pfeifer2@gmail.com> Date: Thu, 5 Jul 2018 21:46:04 +0200 Subject: [PATCH 0147/1171] magento-engcom/import-export-improvements#30: Refactoring for readability. --- .../CustomerImportExport/Model/Import/Customer.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php index 3d4c40fbbed8d..ab940c9e84533 100644 --- a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php +++ b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php @@ -593,12 +593,18 @@ protected function _validateRowForUpdate(array $rowData, $rowNumber) if (in_array($attributeCode, $this->_ignoredAttributes)) { continue; } - if ($attributeParams['is_required'] - && ((!isset($rowData[$attributeCode]) && !$this->_getCustomerId($email, $website)) - || (isset($rowData[$attributeCode]) && '' === trim($rowData[$attributeCode])))) { + + $isFieldRequired = $attributeParams['is_required']; + $isFieldNotSetAndCustomerDoesNotExist = + !isset($rowData[$attributeCode]) && !$this->_getCustomerId($email, $website); + $isFieldSetAndTrimmedValueIsEmpty + = isset($rowData[$attributeCode]) && '' === trim($rowData[$attributeCode]); + + if ($isFieldRequired && ($isFieldNotSetAndCustomerDoesNotExist || $isFieldSetAndTrimmedValueIsEmpty)) { $this->addRowError(self::ERROR_VALUE_IS_REQUIRED, $rowNumber, $attributeCode); continue; } + if (isset($rowData[$attributeCode]) && strlen($rowData[$attributeCode])) { $this->isAttributeValid( $attributeCode, From 69238c216e911d14e7a7e163298b7e9f705d10c9 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Fri, 6 Jul 2018 13:32:44 -0500 Subject: [PATCH 0148/1171] MAGETWO-77744: [Magento Cloud] - Error message when uploading unsupported file format --- .../view/frontend/web/js/catalog-add-to-cart.js | 5 +++++ .../Swatches/view/frontend/web/js/catalog-add-to-cart.js | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js index 817bc7a0d6b27..3e6a611c268af 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js @@ -7,6 +7,11 @@ require([ ], function ($) { 'use strict'; + /** + * Add selected configurable attributes to redirect url + * + * @see Magento_Catalog/js/catalog-add-to-cart + */ $('body').on('catalogCategoryAddToCartRedirect', function (event, data) { $(data.form).find('select[name*="super"]').each(function (index, item) { data.redirectParameters.push(item.config.id + '=' + $(item).val()); diff --git a/app/code/Magento/Swatches/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Swatches/view/frontend/web/js/catalog-add-to-cart.js index 30f2d1007df0b..80003ef68f1c4 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/catalog-add-to-cart.js @@ -7,6 +7,11 @@ require([ ], function ($) { 'use strict'; + /** + * Add selected swatch attributes to redirect url + * + * @see Magento_Catalog/js/catalog-add-to-cart + */ $('body').on('catalogCategoryAddToCartRedirect', function (event, data) { $(data.form).find('[name*="super"]').each(function (index, item) { var $item = $(item), From bdcfbc84d10eacd9a7ba5a43cae25dad4bc77cb0 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Fri, 6 Jul 2018 14:54:57 -0500 Subject: [PATCH 0149/1171] MC-3062: Implement Always Rendering Magento directives on Storefront --- app/code/Magento/Catalog/Helper/Data.php | 7 +++ app/code/Magento/Catalog/Helper/Output.php | 53 +++++++++++++--- .../Patch/Data/EnableDirectiveParsing.php | 61 +++++++++++++++++++ .../Magento/Catalog/etc/adminhtml/system.xml | 5 -- app/code/Magento/Catalog/etc/di.xml | 9 ++- app/code/Magento/Catalog/i18n/en_US.csv | 1 - 6 files changed, 120 insertions(+), 16 deletions(-) create mode 100644 app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php diff --git a/app/code/Magento/Catalog/Helper/Data.php b/app/code/Magento/Catalog/Helper/Data.php index 9eebcdb2df34f..ae20cda460796 100644 --- a/app/code/Magento/Catalog/Helper/Data.php +++ b/app/code/Magento/Catalog/Helper/Data.php @@ -32,6 +32,10 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper const CONFIG_USE_STATIC_URLS = 'cms/wysiwyg/use_static_urls_in_catalog'; + /** + * @deprecated + * @see \Magento\Catalog\Helper\Output::isDirectivesExists + */ const CONFIG_PARSE_URL_DIRECTIVES = 'catalog/frontend/parse_url_directives'; const XML_PATH_DISPLAY_PRODUCT_COUNT = 'catalog/layered_navigation/display_product_count'; @@ -443,6 +447,8 @@ public function isUsingStaticUrlsAllowed() * Check if the parsing of URL directives is allowed for the catalog * * @return bool + * @deprecated + * @see \Magento\Catalog\Helper\Output::isDirectivesExists */ public function isUrlDirectivesParsingAllowed() { @@ -457,6 +463,7 @@ public function isUrlDirectivesParsingAllowed() * Retrieve template processor for catalog content * * @return \Magento\Framework\Filter\Template + * @throws \Magento\Framework\Exception\LocalizedException */ public function getPageTemplateProcessor() { diff --git a/app/code/Magento/Catalog/Helper/Output.php b/app/code/Magento/Catalog/Helper/Output.php index facd5351f269a..1470d94dd8684 100644 --- a/app/code/Magento/Catalog/Helper/Output.php +++ b/app/code/Magento/Catalog/Helper/Output.php @@ -45,20 +45,29 @@ class Output extends \Magento\Framework\App\Helper\AbstractHelper protected $_escaper; /** + * @var array + */ + private $directivePatterns; + + /** + * Output constructor. * @param \Magento\Framework\App\Helper\Context $context * @param \Magento\Eav\Model\Config $eavConfig * @param Data $catalogData * @param \Magento\Framework\Escaper $escaper + * @param array $directivePatterns */ public function __construct( \Magento\Framework\App\Helper\Context $context, \Magento\Eav\Model\Config $eavConfig, Data $catalogData, - \Magento\Framework\Escaper $escaper + \Magento\Framework\Escaper $escaper, + $directivePatterns = [] ) { $this->_eavConfig = $eavConfig; $this->_catalogData = $catalogData; $this->_escaper = $escaper; + $this->directivePatterns = $directivePatterns; parent::__construct($context); } @@ -134,6 +143,7 @@ public function process($method, $result, $params) * @param string $attributeName * @return string * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @throws \Magento\Framework\Exception\LocalizedException */ public function productAttribute($product, $attributeHtml, $attributeName) { @@ -151,10 +161,12 @@ public function productAttribute($product, $attributeHtml, $attributeName) $attributeHtml = nl2br($attributeHtml); } } - if ($attribute->getIsHtmlAllowedOnFront() && $attribute->getIsWysiwygEnabled()) { - if ($this->_catalogData->isUrlDirectivesParsingAllowed()) { - $attributeHtml = $this->_getTemplateProcessor()->filter($attributeHtml); - } + if ($attributeHtml !== null + && $attribute->getIsHtmlAllowedOnFront() + && $attribute->getIsWysiwygEnabled() + && $this->isDirectivesExists($attributeHtml) + ) { + $attributeHtml = $this->_getTemplateProcessor()->filter($attributeHtml); } $attributeHtml = $this->process( @@ -173,6 +185,7 @@ public function productAttribute($product, $attributeHtml, $attributeName) * @param string $attributeHtml * @param string $attributeName * @return string + * @throws \Magento\Framework\Exception\LocalizedException */ public function categoryAttribute($category, $attributeHtml, $attributeName) { @@ -185,10 +198,13 @@ public function categoryAttribute($category, $attributeHtml, $attributeName) ) { $attributeHtml = $this->_escaper->escapeHtml($attributeHtml); } - if ($attribute->getIsHtmlAllowedOnFront() && $attribute->getIsWysiwygEnabled()) { - if ($this->_catalogData->isUrlDirectivesParsingAllowed()) { - $attributeHtml = $this->_getTemplateProcessor()->filter($attributeHtml); - } + if ($attributeHtml !== null + && $attribute->getIsHtmlAllowedOnFront() + && $attribute->getIsWysiwygEnabled() + && $this->isDirectivesExists($attributeHtml) + + ) { + $attributeHtml = $this->_getTemplateProcessor()->filter($attributeHtml); } $attributeHtml = $this->process( 'categoryAttribute', @@ -197,4 +213,23 @@ public function categoryAttribute($category, $attributeHtml, $attributeName) ); return $attributeHtml; } + + /** + * Check if string has directives + * + * @param string $attributeHtml + * @return bool + */ + public function isDirectivesExists($attributeHtml) + { + $matches = false; + foreach ($this->directivePatterns as $pattern) + { + if (preg_match($pattern, $attributeHtml)) + { + $matches = true; + } + } + return $matches; + } } diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php b/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php new file mode 100644 index 0000000000000..3c2149580be6f --- /dev/null +++ b/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Setup\Patch\Data; + +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +/** + * Class EnableDirectiveParsing + * @package Magento\Catalog\Setup\Patch + */ +class EnableDirectiveParsing implements DataPatchInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * PatchInitial constructor. + * @param ModuleDataSetupInterface $moduleDataSetup + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup + ) { + $this->moduleDataSetup = $moduleDataSetup; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + $configTable = $this->moduleDataSetup->getTable('core_config_data'); + $this->moduleDataSetup->getConnection()->update( + $configTable, + ['value' => new \Zend_Db_Expr('1')], + ['path = ?' => \Magento\Catalog\Helper\Data::CONFIG_PARSE_URL_DIRECTIVES, 'value IN (?)' => '0'] + ); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index e6dbb10e811b4..7d5444025df00 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -92,11 +92,6 @@ <comment>Whether to show "All" option in the "Show X Per Page" dropdown</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="parse_url_directives" translate="label comment" type="select" sortOrder="200" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> - <label>Allow Dynamic Media URLs</label> - <comment>E.g. {{media url="path/to/image.jpg"}} {{skin url="path/to/picture.gif"}}. Dynamic directives parsing impacts catalog performance.</comment> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> </group> <group id="placeholder" translate="label" sortOrder="300" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Product Image Placeholders</label> diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 9f1fb020ef95a..4e473ccb9f807 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -173,10 +173,17 @@ </type> <type name="Magento\Catalog\Helper\Data"> <arguments> - <argument name="templateFilterModel" xsi:type="string">Magento\Catalog\Model\Template\Filter</argument> + <argument name="templateFilterModel" xsi:type="string">Magento\Widget\Model\Template\Filter</argument> <argument name="catalogSession" xsi:type="object">Magento\Catalog\Model\Session\Proxy</argument> </arguments> </type> + <type name="Magento\Catalog\Helper\Output"> + <arguments> + <argument name="directivePatterns" xsi:type="array"> + <item name="construct" xsi:type="const">\Magento\Framework\Filter\Template::CONSTRUCTION_PATTERN</item> + </argument> + </arguments> + </type> <type name="Magento\Catalog\Model\Config\Source\GridPerPage"> <arguments> <argument name="perPageValues" xsi:type="string">9,15,30</argument> diff --git a/app/code/Magento/Catalog/i18n/en_US.csv b/app/code/Magento/Catalog/i18n/en_US.csv index f2a7cf0b1950b..a399f67e3130f 100644 --- a/app/code/Magento/Catalog/i18n/en_US.csv +++ b/app/code/Magento/Catalog/i18n/en_US.csv @@ -658,7 +658,6 @@ Comma-separated.,Comma-separated. "Product Listing Sort by","Product Listing Sort by" "Allow All Products per Page","Allow All Products per Page" "Whether to show ""All"" option in the ""Show X Per Page"" dropdown","Whether to show ""All"" option in the ""Show X Per Page"" dropdown" -"Allow Dynamic Media URLs","Allow Dynamic Media URLs" "E.g. {{media url=""path/to/image.jpg""}} {{skin url=""path/to/picture.gif""}}. Dynamic directives parsing impacts catalog performance.","E.g. {{media url=""path/to/image.jpg""}} {{skin url=""path/to/picture.gif""}}. Dynamic directives parsing impacts catalog performance." "Product Image Placeholders","Product Image Placeholders" "Search Engine Optimization","Search Engine Optimization" From 219d10108d900a2fb565066a63e9217dc85e7e8a Mon Sep 17 00:00:00 2001 From: Eric Bohanon <eric.bo.dev@gmail.com> Date: Fri, 6 Jul 2018 15:07:16 -0500 Subject: [PATCH 0150/1171] MAGETWO-90971: Add functional test --- .../CheckoutShippingMethodsSection.xml | 2 + .../Test/StorefrontCustomerCheckoutTest.xml | 105 ++++++++++++++++++ .../Customer/Test/Mftf/Data/AddressData.xml | 12 ++ .../Customer/Test/Mftf/Data/CustomerData.xml | 14 +++ 4 files changed, 133 insertions(+) diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingMethodsSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingMethodsSection.xml index 552341a531106..2d07cdd9bd08d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingMethodsSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingMethodsSection.xml @@ -14,5 +14,7 @@ <element name="shippingMethodRow" type="text" selector=".form.methods-shipping table tbody tr"/> <element name="checkShippingMethodByName" type="radio" selector="//div[@id='checkout-shipping-method-load']//td[contains(., '{{var1}}')]/..//input" parameterized="true"/> <element name="shippingMethodRowByName" type="text" selector="//div[@id='checkout-shipping-method-load']//td[contains(., '{{var1}}')]/.." parameterized="true"/> + <element name="shipHereButton" type="button" selector="//button[contains(@class, 'action-select-shipping-item')]/parent::div/following-sibling::div/button[contains(@class, 'action-select-shipping-item')]"/> + <element name="shippingMethodLoader" type="button" selector="//div[contains(@class, 'checkout-shipping-method')]/following-sibling::div[contains(@class, 'loading-mask')]"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml index c88f6cec4bcdb..fc0cdac97f361 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml @@ -82,4 +82,109 @@ <click stepKey="s102" selector="{{AdminEditCustomerInformationSection.orders}}"/> <see stepKey="s103" selector="{{AdminEditCustomerOrdersSection.orderGrid}}" userInput="$$simpleuscustomer.firstname$$ $$simpleuscustomer.lastname$$" /> </test> + <test name="StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRates"> + <annotations> + <title value="Customer Checkout with multiple addresses and tax rates"/> + <description value="Should be able to place an order as a customer with multiple addresses and tax rates."/> + <testCaseId value="MAGETWO-93109"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="simplecategory"/> + <createData entity="SimpleProduct" stepKey="simpleproduct1"> + <requiredEntity createDataKey="simplecategory"/> + </createData> + <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="multiple_address_customer"/> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="addNewTaxRateNoZip" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="addNewTaxRateNoZip" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleproduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="simplecategory" stepKey="deleteCategory"/> + <deleteData createDataKey="multiple_address_customer" stepKey="deleteCustomer"/> + </after> + + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$multiple_address_customer$$" /> + </actionGroup> + + <amOnPage url="{{StorefrontCategoryPage.url($$simplecategory.name$$)}}" stepKey="onCategoryPage1"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct1"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart1"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded1"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$simpleproduct1.name$$ to your shopping cart." stepKey="seeAddedToCartMessage1"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity1"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart1" /> + + <click stepKey="selectFirstShippingMethod1" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> + <waitForElement stepKey="waitForShippingMethodSelect1" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> + <click stepKey="clickNextOnShippingMethodLoad1" selector="{{CheckoutShippingMethodsSection.next}}" /> + <waitForPageLoad stepKey="waitForPaymentLoad1"/> + <waitForElement stepKey="waitForPlaceOrderButton1" selector="{{CheckoutPaymentSection.placeOrder}}" time="30" /> + <see stepKey="seeBillingAddressIsCorrect1" selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_NY.street[0]}}" /> + <click stepKey="clickPlaceOrderButton1" selector="{{CheckoutPaymentSection.placeOrder}}" /> + <waitForPageLoad stepKey="waitForOrderSuccessPage1"/> + <see stepKey="seeSuccessMessage1" selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" /> + + <amOnPage url="{{StorefrontCategoryPage.url($$simplecategory.name$$)}}" stepKey="onCategoryPage2"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct2"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart2"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded2"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$simpleproduct1.name$$ to your shopping cart." stepKey="seeAddedToCartMessage2"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity2"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2" /> + + <click stepKey="changeShippingAddress" selector="{{CheckoutShippingMethodsSection.shipHereButton}}"/> + <waitForElementVisible stepKey="waitForShippingMethodLoaderVisible" selector="{{CheckoutShippingMethodsSection.shippingMethodLoader}}" time="30"/> + <waitForElementNotVisible stepKey="waitForShippingMethodLoaderNotVisible" selector="{{CheckoutShippingMethodsSection.shippingMethodLoader}}" time="30"/> + <click stepKey="selectFirstShippingMethod2" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> + <waitForElement stepKey="waitForShippingMethodSelect2" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> + <click stepKey="clickNextOnShippingMethodLoad2" selector="{{CheckoutShippingMethodsSection.next}}" /> + <waitForPageLoad stepKey="waitForPaymentLoad2"/> + <waitForElement stepKey="waitForPlaceOrderButton2" selector="{{CheckoutPaymentSection.placeOrder}}" time="30" /> + <see stepKey="seeBillingAddressIsCorrect2" selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{UK_Not_Default_Address.street[0]}}" /> + <click stepKey="clickPlaceOrderButton2" selector="{{CheckoutPaymentSection.placeOrder}}" /> + <waitForPageLoad stepKey="waitForOrderSuccessPage2"/> + <see stepKey="seeSuccessMessage2" selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" /> + </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 19194ae2e5423..57e421e91f1c8 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -83,4 +83,16 @@ <data key="default_shipping">Yes</data> <requiredEntity type="region">RegionCA</requiredEntity> </entity> + <entity name="UK_Not_Default_Address" type="address"> + <data key="firstname">Jane</data> + <data key="lastname">Doe</data> + <data key="company">Magento</data> + <array key="street"> + <item>172, Westminster Bridge Rd</item> + </array> + <data key="city">London</data> + <data key="postcode">SE1 7RW</data> + <data key="country_id">GB</data> + <data key="telephone">444-44-444-44</data> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index 1827824ba4b92..1f6a01ea815d1 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -45,6 +45,20 @@ <data key="website_id">0</data> <requiredEntity type="address">US_Address_TX</requiredEntity> </entity> + <entity name="Simple_US_Customer_Multiple_Addresses" type="customer"> + <data key="group_id">0</data> + <data key="default_billing">true</data> + <data key="default_shipping">true</data> + <data key="email" unique="prefix">John.Doe@example.com</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="fullname">John Doe</data> + <data key="password">pwdTest123!</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + <requiredEntity type="address">US_Address_NY</requiredEntity> + <requiredEntity type="address">UK_Not_Default_Address</requiredEntity> + </entity> <entity name="Simple_US_Customer_NY" type="customer"> <data key="group_id">0</data> <data key="default_billing">true</data> From 430f8f5b505f4eafc654658896b8e37a8d44afbb Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Fri, 6 Jul 2018 15:17:04 -0500 Subject: [PATCH 0151/1171] MC-3062: Implement Always Rendering Magento directives on Storefront -cover with test --- .../Magento/Catalog/Helper/OutputTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Helper/OutputTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Helper/OutputTest.php index bcb29fa002f01..c84d10f4def31 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Helper/OutputTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Helper/OutputTest.php @@ -64,6 +64,23 @@ public function testCategoryAttribute() ); } + /** + * @dataProvider isDirectiveDataProvider + */ + public function testIsDirective($html, $expectedResult) + { + $this->assertEquals($expectedResult, $this->_helper->isDirectivesExists($html)); + } + + public function isDirectiveDataProvider() + { + return [ + ['{{', false], + ['Test string', false], + ['{store url="customer/account/login"}', false], + ['{{store url="customer/account/login"}}', true], + ]; + } /** * Helper method for testProcess() * From 8ea40bea911b17d432f1b63b07a23abc6b284485 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Fri, 6 Jul 2018 15:49:01 -0500 Subject: [PATCH 0152/1171] MQE-1130: MFTF root level dependency conflicts with MTF autoload --- dev/tests/functional/bootstrap.php | 12 +----- .../lib/Magento/Mtf/Util/Generate/Factory.php | 20 +--------- .../Util/ModuleResolver/SequenceSorter.php | 34 +---------------- dev/tests/functional/utils/bootstrap.php | 3 -- dev/tests/functional/utils/generate.php | 38 ++++++++++++++----- .../functional/utils/generate/fixture.php | 2 - .../utils/generate/moduleSequence.php | 17 +++++++++ .../functional/utils/generateFixtureXml.php | 12 ------ 8 files changed, 51 insertions(+), 87 deletions(-) create mode 100644 dev/tests/functional/utils/generate/moduleSequence.php delete mode 100644 dev/tests/functional/utils/generateFixtureXml.php diff --git a/dev/tests/functional/bootstrap.php b/dev/tests/functional/bootstrap.php index 9967697124e80..be36937866d37 100644 --- a/dev/tests/functional/bootstrap.php +++ b/dev/tests/functional/bootstrap.php @@ -9,18 +9,8 @@ defined('MTF_TESTS_PATH') || define('MTF_TESTS_PATH', MTF_BP . '/tests/app/'); defined('MTF_STATES_PATH') || define('MTF_STATES_PATH', MTF_BP . '/lib/Magento/Mtf/App/State/'); -require_once __DIR__ . '/../../../app/bootstrap.php'; restore_error_handler(); -$vendorAutoload = __DIR__ . '/vendor/autoload.php'; - -if (isset($composerAutoloader)) { - /** var $mtfComposerAutoload \Composer\Autoload\ClassLoader */ - $mtfComposerAutoload = include $vendorAutoload; - $composerAutoloader->addClassMap($mtfComposerAutoload->getClassMap()); -} else { - $composerAutoloader = include $vendorAutoload; -} - +include __DIR__ . '/vendor/autoload.php'; setCustomErrorHandler(); /** diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Generate/Factory.php b/dev/tests/functional/lib/Magento/Mtf/Util/Generate/Factory.php index 5bb1efee62959..330b180843ce9 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Generate/Factory.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Generate/Factory.php @@ -6,8 +6,6 @@ namespace Magento\Mtf\Util\Generate; -use Magento\Framework\App; -use Magento\Framework\ObjectManagerInterface; /** * Factory classes generator. @@ -16,24 +14,10 @@ */ class Factory extends AbstractGenerate { - /** - * @var \Magento\Framework\ObjectManagerInterface - */ - protected $objectManager; - - /** - * @constructor - * @param ObjectManagerInterface $objectManager - */ - public function __construct(ObjectManagerInterface $objectManager) - { - $this->objectManager = $objectManager; - } - /** * Generate Handlers. * - * @return \Magento\Framework\App\ResponseInterface + * @return bool */ public function launch() { @@ -43,7 +27,7 @@ public function launch() $this->objectManager->create(\Magento\Mtf\Util\Generate\Factory\Page::class)->launch(); $this->objectManager->create(\Magento\Mtf\Util\Generate\Factory\Repository::class)->launch(); - return $this->objectManager->get(\Magento\Framework\App\ResponseInterface::class); + return true; } /** diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/ModuleResolver/SequenceSorter.php b/dev/tests/functional/lib/Magento/Mtf/Util/ModuleResolver/SequenceSorter.php index a4b438a1d2de0..110d9d5fbd6c3 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/ModuleResolver/SequenceSorter.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/ModuleResolver/SequenceSorter.php @@ -10,37 +10,6 @@ */ class SequenceSorter implements SequenceSorterInterface { - /** - * Magento ObjectManager. - * - * @var \Magento\Framework\ObjectManagerInterface - */ - protected $magentoObjectManager; - - /** - * @constructor - */ - public function __construct() - { - $this->initObjectManager(); - } - - /** - * Initialize Magento ObjectManager. - * - * @return void - */ - protected function initObjectManager() - { - if (!$this->magentoObjectManager) { - $objectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory( - BP, - $_SERVER - ); - $this->magentoObjectManager = $objectManagerFactory->create($_SERVER); - } - } - /** * Get Magento module sequence load. * @@ -48,7 +17,8 @@ protected function initObjectManager() */ protected function getModuleSequence() { - return $this->magentoObjectManager->create(\Magento\Framework\Module\ModuleList\Loader::class)->load(); + $ds = DIRECTORY_SEPARATOR; + return json_decode(file_get_contents(MTF_BP . $ds . 'generated' . $ds . 'moduleSequence.json'), true); } /** diff --git a/dev/tests/functional/utils/bootstrap.php b/dev/tests/functional/utils/bootstrap.php index 00c548e6b5cef..0b74dc6727a13 100644 --- a/dev/tests/functional/utils/bootstrap.php +++ b/dev/tests/functional/utils/bootstrap.php @@ -16,6 +16,3 @@ $objectManager = \Magento\Mtf\ObjectManagerFactory::getObjectManager(); \Magento\Mtf\ObjectManagerFactory::configure($objectManager); - -$magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); -$magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); diff --git a/dev/tests/functional/utils/generate.php b/dev/tests/functional/utils/generate.php index e9a81a1eea224..27a8fe4e3e40b 100644 --- a/dev/tests/functional/utils/generate.php +++ b/dev/tests/functional/utils/generate.php @@ -3,19 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\Filesystem; require_once dirname(__FILE__) . '/' . 'bootstrap.php'; -// Generate fixtures -$magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); -$magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); -// Remove previously generated static classes -$fs = $magentoObjectManager->create(Filesystem::class); -$fs->getDirectoryWrite(DirectoryList::ROOT)->delete('dev/tests/functional/generated/'); +deleteDirectory(MTF_BP . '/generated'); + +// Generate moduleSequence.json file +generateModuleSequence(); // Generate factories for old end-to-end tests -$magentoObjectManager->create(\Magento\Mtf\Util\Generate\Factory::class)->launch(); +$objectManager->create(\Magento\Mtf\Util\Generate\Factory::class)->launch(); $generatorPool = $objectManager->get('Magento\Mtf\Util\Generate\Pool'); foreach ($generatorPool->getGenerators() as $generator) { @@ -28,3 +24,27 @@ } \Magento\Mtf\Util\Generate\GenerateResult::displayResults(); + + +function deleteDirectory($dir) { + if (!file_exists($dir)) { + return true; + } + if (!is_dir($dir)) { + return unlink($dir); + } + foreach (scandir($dir) as $item) { + if ($item == '.' || $item == '..') { + continue; + } + if (!deleteDirectory($dir . DIRECTORY_SEPARATOR . $item)) { + return false; + } + } + return rmdir($dir); +} + +function generateModuleSequence() +{ + require_once "generate/moduleSequence.php"; +} \ No newline at end of file diff --git a/dev/tests/functional/utils/generate/fixture.php b/dev/tests/functional/utils/generate/fixture.php index 9f454fb9dc5f1..68cdae4552261 100644 --- a/dev/tests/functional/utils/generate/fixture.php +++ b/dev/tests/functional/utils/generate/fixture.php @@ -5,6 +5,4 @@ */ require_once dirname(__DIR__) . '/' . 'bootstrap.php'; -$magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); -$magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); $objectManager->create(\Magento\Mtf\Util\Generate\Fixture::class)->launch(); diff --git a/dev/tests/functional/utils/generate/moduleSequence.php b/dev/tests/functional/utils/generate/moduleSequence.php new file mode 100644 index 0000000000000..cddcc71338dc4 --- /dev/null +++ b/dev/tests/functional/utils/generate/moduleSequence.php @@ -0,0 +1,17 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +require_once __DIR__ . '/../../../../../app/bootstrap.php'; + +$magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); +$magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); +$magentoComponentSequence = $magentoObjectManager->create(\Magento\Framework\Module\ModuleList\Loader::class)->load(); +if (!file_exists(dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'generated')) { + mkdir(dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'generated'); +} +file_put_contents( + dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'generated' . DIRECTORY_SEPARATOR . 'moduleSequence.json', + json_encode($magentoComponentSequence, JSON_PRETTY_PRINT) +); diff --git a/dev/tests/functional/utils/generateFixtureXml.php b/dev/tests/functional/utils/generateFixtureXml.php deleted file mode 100644 index 73fc266f34052..0000000000000 --- a/dev/tests/functional/utils/generateFixtureXml.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'bootstrap.php'; - -$magentoObjectManager->create( - \Magento\Mtf\Util\Generate\Fixture\SchemaXml::class, - ['objectManager' => $magentoObjectManager] -)->launch(); From 85a51523f86c93907405a61ab9624c42cdb74061 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Fri, 6 Jul 2018 16:32:38 -0500 Subject: [PATCH 0153/1171] MC-3062: Implement Always Rendering Magento directives on Storefront -fix after CR --- app/code/Magento/Catalog/Helper/Output.php | 9 +++++---- .../Setup/Patch/Data/EnableDirectiveParsing.php | 16 +++++++++++----- .../Magento/Catalog/Helper/OutputTest.php | 1 + 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Catalog/Helper/Output.php b/app/code/Magento/Catalog/Helper/Output.php index 1470d94dd8684..ad0f9508cb67e 100644 --- a/app/code/Magento/Catalog/Helper/Output.php +++ b/app/code/Magento/Catalog/Helper/Output.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Helper; use Magento\Catalog\Model\Category as ModelCategory; @@ -223,11 +225,10 @@ public function categoryAttribute($category, $attributeHtml, $attributeName) public function isDirectivesExists($attributeHtml) { $matches = false; - foreach ($this->directivePatterns as $pattern) - { - if (preg_match($pattern, $attributeHtml)) - { + foreach ($this->directivePatterns as $pattern) { + if (preg_match($pattern, $attributeHtml)) { $matches = true; + break; } } return $matches; diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php b/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php index 3c2149580be6f..7b54788a78288 100644 --- a/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php @@ -36,11 +36,17 @@ public function __construct( public function apply() { $configTable = $this->moduleDataSetup->getTable('core_config_data'); - $this->moduleDataSetup->getConnection()->update( - $configTable, - ['value' => new \Zend_Db_Expr('1')], - ['path = ?' => \Magento\Catalog\Helper\Data::CONFIG_PARSE_URL_DIRECTIVES, 'value IN (?)' => '0'] - ); + $select = $this->moduleDataSetup->getConnection()->select() + ->from($configTable) + ->where('path = ?', \Magento\Catalog\Helper\Data::CONFIG_PARSE_URL_DIRECTIVES); + $config = $this->moduleDataSetup->getConnection()->fetchAll($select); + if (!empty($config)) { + $this->moduleDataSetup->getConnection()->update( + $configTable, + ['value' => new \Zend_Db_Expr('1')], + ['path = ?' => \Magento\Catalog\Helper\Data::CONFIG_PARSE_URL_DIRECTIVES, 'value IN (?)' => '0'] + ); + } } /** diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Helper/OutputTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Helper/OutputTest.php index c84d10f4def31..5a4dadedb1c9e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Helper/OutputTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Helper/OutputTest.php @@ -81,6 +81,7 @@ public function isDirectiveDataProvider() ['{{store url="customer/account/login"}}', true], ]; } + /** * Helper method for testProcess() * From 516566ac2d8b08b4e95003463362af457847ff93 Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Thu, 5 Jul 2018 21:36:49 +0200 Subject: [PATCH 0154/1171] Make it possible to disable cross-sell on cart page To be able to select whether to have the cross-sell active on the cart page an admin configuration is created under Stores > Configuration > Sales > Checkout > Shopping Cart. The default for this setting is set to Yes (Show cross-sell items). This is for backwards compatibility and not have webshop owners accidentally not showing these items. Because this is now sorted as the 3rd configuration in this section/group the extra configurations for the grouped product image and the configurable product image are now also increased. This feature will make it possible to have this cross-sell turned off during special sales, A/B testing or in a specific store view. --- app/code/Magento/Checkout/etc/adminhtml/system.xml | 4 ++++ app/code/Magento/Checkout/etc/config.xml | 1 + app/code/Magento/Checkout/i18n/en_US.csv | 3 ++- .../Checkout/view/frontend/layout/checkout_cart_index.xml | 2 +- app/code/Magento/ConfigurableProduct/etc/adminhtml/system.xml | 2 +- app/code/Magento/GroupedProduct/etc/adminhtml/system.xml | 2 +- 6 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Checkout/etc/adminhtml/system.xml b/app/code/Magento/Checkout/etc/adminhtml/system.xml index 6947e1162600a..11e3ba5f3ed9a 100644 --- a/app/code/Magento/Checkout/etc/adminhtml/system.xml +++ b/app/code/Magento/Checkout/etc/adminhtml/system.xml @@ -41,6 +41,10 @@ <field id="number_items_to_display_pager" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Number of Items to Display Pager</label> </field> + <field id="crosssell_enabled" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <label>Show Cross-sell Items in the Shopping Cart</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> </group> <group id="cart_link" translate="label" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0"> <label>My Cart Link</label> diff --git a/app/code/Magento/Checkout/etc/config.xml b/app/code/Magento/Checkout/etc/config.xml index 3c24c38ecf85b..e1ba4381f2230 100644 --- a/app/code/Magento/Checkout/etc/config.xml +++ b/app/code/Magento/Checkout/etc/config.xml @@ -17,6 +17,7 @@ <delete_quote_after>30</delete_quote_after> <redirect_to_cart>0</redirect_to_cart> <number_items_to_display_pager>20</number_items_to_display_pager> + <crosssell_enabled>1</crosssell_enabled> </cart> <cart_link> <use_qty>1</use_qty> diff --git a/app/code/Magento/Checkout/i18n/en_US.csv b/app/code/Magento/Checkout/i18n/en_US.csv index 53fdebb8a2995..c6bb23fecac18 100644 --- a/app/code/Magento/Checkout/i18n/en_US.csv +++ b/app/code/Magento/Checkout/i18n/en_US.csv @@ -176,4 +176,5 @@ Payment,Payment "Not yet calculated","Not yet calculated" "We received your order!","We received your order!" "Thank you for your purchase!","Thank you for your purchase!" -"Password", "Password" +Password,Password +"Show Cross-sell Items in the Shopping Cart","Show Cross-sell Items in the Shopping Cart" diff --git a/app/code/Magento/Checkout/view/frontend/layout/checkout_cart_index.xml b/app/code/Magento/Checkout/view/frontend/layout/checkout_cart_index.xml index ff4c6dbd35ff2..69d2523d88dfb 100644 --- a/app/code/Magento/Checkout/view/frontend/layout/checkout_cart_index.xml +++ b/app/code/Magento/Checkout/view/frontend/layout/checkout_cart_index.xml @@ -186,7 +186,7 @@ </block> <container name="checkout.cart.widget" as="checkout_cart_widget" label="Shopping Cart Items After"/> </container> - <block class="Magento\Checkout\Block\Cart\Crosssell" name="checkout.cart.crosssell" template="Magento_Catalog::product/list/items.phtml" after="-"> + <block class="Magento\Checkout\Block\Cart\Crosssell" name="checkout.cart.crosssell" template="Magento_Catalog::product/list/items.phtml" after="-" ifconfig="checkout/cart/crosssell_enabled"> <arguments> <argument name="type" xsi:type="string">crosssell</argument> </arguments> diff --git a/app/code/Magento/ConfigurableProduct/etc/adminhtml/system.xml b/app/code/Magento/ConfigurableProduct/etc/adminhtml/system.xml index ba52b51d6b077..86baea3c0d296 100644 --- a/app/code/Magento/ConfigurableProduct/etc/adminhtml/system.xml +++ b/app/code/Magento/ConfigurableProduct/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <system> <section id="checkout"> <group id="cart"> - <field id="configurable_product_image" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="configurable_product_image" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Configurable Product Image</label> <source_model>Magento\Catalog\Model\Config\Source\Product\Thumbnail</source_model> </field> diff --git a/app/code/Magento/GroupedProduct/etc/adminhtml/system.xml b/app/code/Magento/GroupedProduct/etc/adminhtml/system.xml index bbb2054abb14f..5854928afc2fd 100644 --- a/app/code/Magento/GroupedProduct/etc/adminhtml/system.xml +++ b/app/code/Magento/GroupedProduct/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <system> <section id="checkout" translate="label" type="text" sortOrder="305" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="cart" translate="label" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"> - <field id="grouped_product_image" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="grouped_product_image" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Grouped Product Image</label> <source_model>Magento\Catalog\Model\Config\Source\Product\Thumbnail</source_model> </field> From c25038b1c25fc5eaededd3e99d24ac502706f596 Mon Sep 17 00:00:00 2001 From: Mark Shust <mark@shust.com> Date: Thu, 31 May 2018 17:20:24 -0400 Subject: [PATCH 0155/1171] Create ability to set is_visible_on_front to order status history comment --- app/code/Magento/Sales/Model/Order.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/Order.php b/app/code/Magento/Sales/Model/Order.php index d3b8edbed2eea..fc5f23306d336 100644 --- a/app/code/Magento/Sales/Model/Order.php +++ b/app/code/Magento/Sales/Model/Order.php @@ -1014,7 +1014,7 @@ public function addStatusToHistory($status, $comment = '', $isCustomerNotified = * @param bool|string $status * @return OrderStatusHistoryInterface */ - public function addStatusHistoryComment($comment, $status = false) + public function addStatusHistoryComment($comment, $status = false, $isVisibleOnFront = false) { if (false === $status) { $status = $this->getStatus(); @@ -1029,6 +1029,8 @@ public function addStatusHistoryComment($comment, $status = false) $comment )->setEntityName( $this->entityType + )->setIsVisibleOnFront( + $isVisibleOnFront ); $this->addStatusHistory($history); return $history; From f2afd7fdef4aa323d08337aef8041003d61b0db0 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Tue, 19 Jun 2018 10:56:46 +0300 Subject: [PATCH 0156/1171] Fixed according to Backward compatible development guide --- app/code/Magento/Sales/Model/Order.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/Order.php b/app/code/Magento/Sales/Model/Order.php index fc5f23306d336..7372d9715c725 100644 --- a/app/code/Magento/Sales/Model/Order.php +++ b/app/code/Magento/Sales/Model/Order.php @@ -1013,8 +1013,24 @@ public function addStatusToHistory($status, $comment = '', $isCustomerNotified = * @param string $comment * @param bool|string $status * @return OrderStatusHistoryInterface + * @deprecated + * @see addCommentToStatusHistory */ - public function addStatusHistoryComment($comment, $status = false, $isVisibleOnFront = false) + public function addStatusHistoryComment($comment, $status = false) + { + return $this->addCommentToStatusHistory($comment, $status, false); + } + + /** + * Add a comment to order status history + * Different or default status may be specified + * + * @param string $comment + * @param bool|string $status + * @param bool $isVisibleOnFront + * @return OrderStatusHistoryInterface + */ + public function addCommentToStatusHistory($comment, $status = false, $isVisibleOnFront = false) { if (false === $status) { $status = $this->getStatus(); From b98f41796650d36e44fcd2cb42d02bb09cf8ccd1 Mon Sep 17 00:00:00 2001 From: Tadhg Bowe <tadhg.bowe@screenpages.com> Date: Sat, 7 Jul 2018 12:38:15 +0100 Subject: [PATCH 0157/1171] import-export-improvements #82 : configurable variations - not a super attribute error message improvements - code styling Travis CI build fixes --- .../Import/Product/Type/Configurable.php | 39 ++++++++----------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php index 43da5661e966b..5969f872616e0 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php @@ -332,34 +332,25 @@ private function identifySuperAttributeError($superAttrCode, $rowNum) { // This attribute code is not a super attribute. Need to give a clearer message why? $reasonFound = false; - $codeExists = false; - $codeNotGlobal = false; - $codeNotTypeSelect = false; + // Does this attribute code exist? $sourceAttribute = $this->doesSuperAttributeExist($superAttrCode); - if (count($sourceAttribute)) { + if (!is_null($sourceAttribute)) { $codeExists = true; // Does attribute have the correct settings? - if (is_array($sourceAttribute)) { - if (isset($sourceAttribute['is_global']) && $sourceAttribute['is_global'] !== '1') { - $codeNotGlobal = true; - } elseif (isset($sourceAttribute['type']) && $sourceAttribute['type'] !== 'select') { - $codeNotTypeSelect = true; - } + if (isset($sourceAttribute['is_global']) && $sourceAttribute['is_global'] !== '1') { + $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_GLOBAL_SCOPE, $rowNum, $superAttrCode); + $reasonFound = true; + } elseif (isset($sourceAttribute['type']) && $sourceAttribute['type'] !== 'select') { + $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_TYPE_SELECT, $rowNum, $superAttrCode); + $reasonFound = true; } } - // Identify (if any) the correct fault: if ($codeExists === false) { $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_DOES_NOT_EXIST, $rowNum, $superAttrCode); $reasonFound = true; - } elseif ($codeNotGlobal === true) { - $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_GLOBAL_SCOPE, $rowNum, $superAttrCode); - $reasonFound = true; - } elseif ($codeNotTypeSelect === true) { - $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_NOT_TYPE_SELECT, $rowNum, $superAttrCode); - $reasonFound = true; } return $reasonFound; @@ -373,9 +364,8 @@ private function identifySuperAttributeError($superAttrCode, $rowNum) */ private function doesSuperAttributeExist($superAttrCode) { - $returnFilterArray = []; - if (is_array(self::$commonAttributesCache)) - { + $returnAttributeArray = null; + if (is_array(self::$commonAttributesCache)) { $filteredAttribute = array_filter( self::$commonAttributesCache, function ($element) use ($superAttrCode) { @@ -383,10 +373,13 @@ function ($element) use ($superAttrCode) { } ); - // Return the first element of the filtered array. - $returnFilterArray = array_shift($filteredAttribute); + // Return the first element of the filtered array (if found). + if (count($filteredAttribute)) + { + $returnAttributeArray = array_shift($filteredAttribute); + } } - return $returnFilterArray; + return $returnAttributeArray; } /** From 8db836b16fa92aeed478173b7ad857f51cd70591 Mon Sep 17 00:00:00 2001 From: Tadhg Bowe <tadhg.bowe@screenpages.com> Date: Sat, 7 Jul 2018 15:11:33 +0100 Subject: [PATCH 0158/1171] import-export-improvements #82 : configurable variations - not a super attribute error message improvements - code styling Travis CI build fixes --- .../Model/Import/Product/Type/Configurable.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php index 5969f872616e0..fdbd8560c2664 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php @@ -336,7 +336,7 @@ private function identifySuperAttributeError($superAttrCode, $rowNum) // Does this attribute code exist? $sourceAttribute = $this->doesSuperAttributeExist($superAttrCode); - if (!is_null($sourceAttribute)) { + if (is_array($sourceAttribute)) { $codeExists = true; // Does attribute have the correct settings? if (isset($sourceAttribute['is_global']) && $sourceAttribute['is_global'] !== '1') { @@ -374,8 +374,7 @@ function ($element) use ($superAttrCode) { ); // Return the first element of the filtered array (if found). - if (count($filteredAttribute)) - { + if (count($filteredAttribute)) { $returnAttributeArray = array_shift($filteredAttribute); } } From b7b03f988f6f7fbba3ad8c0d8c0a36dccb07cfb1 Mon Sep 17 00:00:00 2001 From: itaymesh <itay@studioraz.co.il> Date: Tue, 19 Jun 2018 19:49:31 +0300 Subject: [PATCH 0159/1171] Update Israeli ZIP code mask, 7 digits instead of 5 ,according to the Israeli postal office --- app/code/Magento/Directory/etc/zip_codes.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Directory/etc/zip_codes.xml b/app/code/Magento/Directory/etc/zip_codes.xml index d9041d1ff50a7..3c540f7ce0ffd 100644 --- a/app/code/Magento/Directory/etc/zip_codes.xml +++ b/app/code/Magento/Directory/etc/zip_codes.xml @@ -196,7 +196,7 @@ </zip> <zip countryCode="IL"> <codes> - <code id="pattern_1" active="true" example="12345">^[0-9]{5}$</code> + <code id="pattern_1" active="true" example="6687865">^[0-9]{7}$</code> </codes> </zip> <zip countryCode="IT"> From e4b7eef3d09110e9faec35f166ea8ed02dc71503 Mon Sep 17 00:00:00 2001 From: Valerij Ivashchenko <likemusic@yandex.ru> Date: Mon, 11 Jun 2018 15:47:43 +0300 Subject: [PATCH 0160/1171] small refactoring to better code readability --- .../Block/Widget/Form/Element/Dependence.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Backend/Block/Widget/Form/Element/Dependence.php b/app/code/Magento/Backend/Block/Widget/Form/Element/Dependence.php index 723deab1e9f7e..c3f83e06858d8 100644 --- a/app/code/Magento/Backend/Block/Widget/Form/Element/Dependence.php +++ b/app/code/Magento/Backend/Block/Widget/Form/Element/Dependence.php @@ -124,14 +124,17 @@ protected function _toHtml() if (!$this->_depends) { return ''; } - return '<script> - require(["mage/adminhtml/form"], function(){ - new FormElementDependenceController(' . - $this->_getDependsJson() . - ($this->_configOptions ? ', ' . - $this->_jsonEncoder->encode( - $this->_configOptions - ) : '') . '); });</script>'; + + $dependsJson = $this->_getDependsJson(); + $configOptions = $this->_configOptions ? $this->_jsonEncoder->encode($this->_configOptions) : null; + $configOptionsStr = $configOptions ? ', ' . $configOptions : ''; + $paramsStr = $dependsJson . $configOptionsStr; + + return "<script> + require(['mage/adminhtml/form'], function(){ + new FormElementDependenceController({$paramsStr}); +}); +</script>"; } /** From 08051c0a608dc9c7cacddf56f14fa3888b42b684 Mon Sep 17 00:00:00 2001 From: Valerij Ivashchenko <likemusic@yandex.ru> Date: Tue, 12 Jun 2018 10:57:28 +0300 Subject: [PATCH 0161/1171] improve params string creation according to @orlangur suggestion --- .../Block/Widget/Form/Element/Dependence.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Backend/Block/Widget/Form/Element/Dependence.php b/app/code/Magento/Backend/Block/Widget/Form/Element/Dependence.php index c3f83e06858d8..eff49c3b75ab2 100644 --- a/app/code/Magento/Backend/Block/Widget/Form/Element/Dependence.php +++ b/app/code/Magento/Backend/Block/Widget/Form/Element/Dependence.php @@ -125,14 +125,15 @@ protected function _toHtml() return ''; } - $dependsJson = $this->_getDependsJson(); - $configOptions = $this->_configOptions ? $this->_jsonEncoder->encode($this->_configOptions) : null; - $configOptionsStr = $configOptions ? ', ' . $configOptions : ''; - $paramsStr = $dependsJson . $configOptionsStr; + $params = $this->_getDependsJson(); + + if ($this->_configOptions) { + $params .= ', ' . $this->_jsonEncoder->encode($this->_configOptions); + } return "<script> - require(['mage/adminhtml/form'], function(){ - new FormElementDependenceController({$paramsStr}); +require(['mage/adminhtml/form'], function(){ + new FormElementDependenceController({$params}); }); </script>"; } From 9b3fc28303af45f94d380ba64099545cac2643d6 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 9 Jul 2018 15:29:12 +0300 Subject: [PATCH 0162/1171] MAGETWO-93115: Text is broken on storefront with enabled translate-inline --- .../view/frontend/templates/account/link/authorization.phtml | 2 +- .../Magento/Customer/view/frontend/templates/form/login.phtml | 2 +- .../Magento/Newsletter/view/frontend/templates/subscribe.phtml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/view/frontend/templates/account/link/authorization.phtml b/app/code/Magento/Customer/view/frontend/templates/account/link/authorization.phtml index 10223df489e90..24657a6846cae 100644 --- a/app/code/Magento/Customer/view/frontend/templates/account/link/authorization.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/account/link/authorization.phtml @@ -13,7 +13,7 @@ if ($block->isLoggedIn()) { $dataPostParam = sprintf(" data-post='%s'", $block->getPostParams()); } ?> -<li class="authorization-link" data-label="<?= $block->escapeHtmlAttr(__('or')) ?>"> +<li class="authorization-link" data-label="<?= $block->escapeHtml(__('or')) ?>"> <a <?= /* @noEscape */ $block->getLinkAttributes() ?><?= /* @noEscape */ $dataPostParam ?>> <?= $block->escapeHtml($block->getLabel()) ?> </a> diff --git a/app/code/Magento/Customer/view/frontend/templates/form/login.phtml b/app/code/Magento/Customer/view/frontend/templates/form/login.phtml index 2d44dde215139..d85e76e4cbc3f 100644 --- a/app/code/Magento/Customer/view/frontend/templates/form/login.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/form/login.phtml @@ -19,7 +19,7 @@ id="login-form" data-mage-init='{"validation":{}}'> <?= $block->getBlockHtml('formkey') ?> - <fieldset class="fieldset login" data-hasrequired="<?= $block->escapeHtmlAttr(__('* Required Fields')) ?>"> + <fieldset class="fieldset login" data-hasrequired="<?= $block->escapeHtml(__('* Required Fields')) ?>"> <div class="field note"><?= $block->escapeHtml(__('If you have an account, sign in with your email address.')) ?></div> <div class="field email required"> <label class="label" for="email"><span><?= $block->escapeHtml(__('Email')) ?></span></label> diff --git a/app/code/Magento/Newsletter/view/frontend/templates/subscribe.phtml b/app/code/Magento/Newsletter/view/frontend/templates/subscribe.phtml index b633c61d9dc35..b7506cfa13a3f 100644 --- a/app/code/Magento/Newsletter/view/frontend/templates/subscribe.phtml +++ b/app/code/Magento/Newsletter/view/frontend/templates/subscribe.phtml @@ -22,7 +22,7 @@ <label class="label" for="newsletter"><span><?= $block->escapeHtml(__('Sign Up for Our Newsletter:')) ?></span></label> <div class="control"> <input name="email" type="email" id="newsletter" - placeholder="<?= $block->escapeHtmlAttr(__('Enter your email address')) ?>" + placeholder="<?= $block->escapeHtml(__('Enter your email address')) ?>" data-validate="{required:true, 'validate-email':true}"/> </div> </div> From 2fec8749bf2c84b187ff760c96c37295f481747e Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 9 Jul 2018 15:45:58 +0300 Subject: [PATCH 0163/1171] MAGETWO-93118: CSV FILE is created incorrectly for Archive-Orders and System-Data Collector --- .../adminhtml/templates/order/totalbar.phtml | 11 +++---- .../Ui/Model/Export/MetadataProvider.php | 7 +++++ .../Model/Export/MetadataProviderTest.php | 29 +++++++++++++++---- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/totalbar.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/totalbar.phtml index b038c72199c7f..af7dfbc4e2ab3 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/totalbar.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/totalbar.phtml @@ -6,14 +6,15 @@ // @codingStandardsIgnoreFile +$totals = $block->getTotals(); ?> -<?php if (sizeof($block->getTotals()) > 0): ?> +<?php if ($totals && count($totals) > 0): ?> <table class="items-to-invoice"> <tr> - <?php foreach ($block->getTotals() as $_total): ?> - <td <?php if ($_total['grand']): ?>class="grand-total"<?php endif; ?>> - <?= /* @escapeNotVerified */ $_total['label'] ?><br /> - <?= /* @escapeNotVerified */ $_total['value'] ?> + <?php foreach ($totals as $total): ?> + <td <?php if ($total['grand']): ?>class="grand-total"<?php endif; ?>> + <?= $block->escapeHtml($total['label']) ?><br /> + <?= $block->escapeHtml($total['value']) ?> </td> <?php endforeach; ?> </tr> diff --git a/app/code/Magento/Ui/Model/Export/MetadataProvider.php b/app/code/Magento/Ui/Model/Export/MetadataProvider.php index 499cd06e505f6..54d856e8a6104 100644 --- a/app/code/Magento/Ui/Model/Export/MetadataProvider.php +++ b/app/code/Magento/Ui/Model/Export/MetadataProvider.php @@ -118,6 +118,13 @@ public function getHeaders(UiComponentInterface $component) foreach ($this->getColumns($component) as $column) { $row[] = $column->getData('config/label'); } + + array_walk($row, function (&$header) { + if (mb_strpos($header, 'ID') === 0) { + $header = '"' . $header . '"'; + } + }); + return $row; } diff --git a/app/code/Magento/Ui/Test/Unit/Model/Export/MetadataProviderTest.php b/app/code/Magento/Ui/Test/Unit/Model/Export/MetadataProviderTest.php index dd6fd58d355e6..aae4fafbdac3f 100644 --- a/app/code/Magento/Ui/Test/Unit/Model/Export/MetadataProviderTest.php +++ b/app/code/Magento/Ui/Test/Unit/Model/Export/MetadataProviderTest.php @@ -40,18 +40,37 @@ protected function setUp() ); } - public function testGetHeaders() + /** + * @param array $columnLabels + * @param array $expected + * @return void + * @dataProvider getColumnsDataProvider + */ + public function testGetHeaders(array $columnLabels, array $expected): void { $componentName = 'component_name'; $columnName = 'column_name'; - $columnLabel = 'column_label'; - - $component = $this->prepareColumns($componentName, $columnName, $columnLabel); + $component = $this->prepareColumns($componentName, $columnName, $columnLabels[0]); $result = $this->model->getHeaders($component); $this->assertTrue(is_array($result)); $this->assertCount(1, $result); - $this->assertEquals($columnLabel, $result[0]); + $this->assertEquals($expected, $result); + } + + /** + * @return array + */ + public function getColumnsDataProvider(): array + { + return [ + [['ID'],['"ID"']], + [['Name'],['Name']], + [['Id'],['Id']], + [['id'],['id']], + [['IDTEST'],['"IDTEST"']], + [['ID TEST'],['"ID TEST"']], + ]; } public function testGetFields() From 182852effa2c86ceb2f76db36dcf422104cf2858 Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Mon, 9 Jul 2018 11:35:28 -0500 Subject: [PATCH 0164/1171] =?UTF-8?q?MC-229:=20Customer=20should=20be=20ta?= =?UTF-8?q?ken=20to=20bundle=20product=20details=20page=20when=20clicking?= =?UTF-8?q?=20=E2=80=9CAdd=20to=20Cart=E2=80=9D=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ontGoToDetailsPageWhenAddingToCartTest.xml | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml new file mode 100644 index 0000000000000..6f2d5bdad1685 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontGoToDetailsPageWhenAddingToCart"> + <annotations> + <features value="Bundle"/> + <stories value="Bundle products list on Storefront"/> + <title value="Customer should be taken to bundle product details page when clicking “Add to Cart” button"/> + <description value="Customer should be taken to bundle product details page when clicking “Add to Cart” button"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-229"/> + <group value="Bundle"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + <!--Go to bundle product creation page--> + <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage" /> + <waitForPageLoad stepKey="waitForBundleProductCreatePageToLoad"/> + + <!--Categories--> + <click selector="{{AdminProductFormBundleSection.categoriesDropDown}}" stepKey="dropDownCategories"/> + <fillField selector="{{AdminProductFormBundleSection.searchForCategory}}" userInput="$$createCategory.name$$" stepKey="searchForCategory"/> + <click selector="{{AdminProductFormBundleSection.selectCategory}}" stepKey="selectCategory"/> + <click selector="{{AdminProductFormBundleSection.categoriesLabel}}" stepKey="clickOnCategoriesLabelToCloseOptions"/> + + <!--Create bundle product--> + <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> + <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption3"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleOptions"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillOptionTitle"/> + <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectInputType"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> + <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> + <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddSelectedBundleProducts"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> + <actionGroup ref="AncillaryPrepBundleProduct" stepKey="createBundledProductForTwoSimpleProducts"/> + + <!--Save the product--> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> + + <!--Go to category page--> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> + <waitForPageLoad stepKey="waitForHomePageToload"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory"/> + + <!--Click add to cart--> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addProductToCart"/> + <waitForPageLoad stepKey="waitForProductPage"/> + + <!--Check for details page--> + <seeInCurrentUrl url="{{BundleProduct.sku}}" stepKey="seeBundleProductDetailsPage"/> + </test> +</tests> From 785c9e9d62f09cfc05261a3ba2f61e78b80d2031 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Mon, 9 Jul 2018 13:15:06 -0500 Subject: [PATCH 0165/1171] MC-3062: Implement Always Rendering Magento directives on Storefront -fix after CR --- .../Catalog/Setup/Patch/Data/EnableDirectiveParsing.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php b/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php index 7b54788a78288..2d2a54c30657c 100644 --- a/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php @@ -38,13 +38,13 @@ public function apply() $configTable = $this->moduleDataSetup->getTable('core_config_data'); $select = $this->moduleDataSetup->getConnection()->select() ->from($configTable) - ->where('path = ?', \Magento\Catalog\Helper\Data::CONFIG_PARSE_URL_DIRECTIVES); + ->where('path = ?', 'catalog/frontend/parse_url_directives'); $config = $this->moduleDataSetup->getConnection()->fetchAll($select); if (!empty($config)) { $this->moduleDataSetup->getConnection()->update( $configTable, - ['value' => new \Zend_Db_Expr('1')], - ['path = ?' => \Magento\Catalog\Helper\Data::CONFIG_PARSE_URL_DIRECTIVES, 'value IN (?)' => '0'] + ['value' => '1'], + ['path = ?' => 'catalog/frontend/parse_url_directives', 'value IN (?)' => '0'] ); } } From 9f0fda689dd3df51bcc3bacd730ed73a0385e72e Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Mon, 9 Jul 2018 13:22:22 -0500 Subject: [PATCH 0166/1171] MAGETWO-91465: Once integer is stored for State/Province field, it can not be changed to alphanumeric --- .../Magento/Customer/Model/Address/Validator/Country.php | 6 +++--- .../Test/Unit/Model/Address/Validator/CountryTest.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Model/Address/Validator/Country.php b/app/code/Magento/Customer/Model/Address/Validator/Country.php index ff1020eba70ef..832a893eb7200 100644 --- a/app/code/Magento/Customer/Model/Address/Validator/Country.php +++ b/app/code/Magento/Customer/Model/Address/Validator/Country.php @@ -95,8 +95,8 @@ private function validateRegion(AbstractAddress $address) $countryId = $address->getCountryId(); $countryModel = $address->getCountryModel(); $regionCollection = $countryModel->getRegionCollection(); - $region = $address->getRegion(); - $regionId = (string)$address->getRegionId(); + $region = $address->getData('region'); + $regionId = (string)$address->getData('region_id'); $allowedRegions = $regionCollection->getAllIds(); $isRegionRequired = $this->directoryData->isRegionRequired($countryId); if ($isRegionRequired && empty($allowedRegions) && !\Zend_Validate::is($region, 'NotEmpty')) { @@ -107,7 +107,7 @@ private function validateRegion(AbstractAddress $address) //If country actually has regions and requires you to //select one then it must be selected. $errors[] = __('"%fieldName" is required. Enter and try again.', ['fieldName' => 'regionId']); - } elseif ($regionId && !in_array($regionId, $allowedRegions, true)) { + } elseif ($allowedRegions && $regionId && !in_array($regionId, $allowedRegions, true)) { //If a region is selected then checking if it exists. $errors[] = __( 'Invalid value of "%value" provided for the %fieldName field.', diff --git a/app/code/Magento/Customer/Test/Unit/Model/Address/Validator/CountryTest.php b/app/code/Magento/Customer/Test/Unit/Model/Address/Validator/CountryTest.php index 9ab35f2d301d4..c060d698b7f73 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Address/Validator/CountryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Address/Validator/CountryTest.php @@ -162,7 +162,7 @@ public function validateDataProvider() array_merge($data, ['country_id' => $countryId, 'region_id' => 2]), [$countryId++], [1], - ['Invalid value of "2" provided for the regionId field.'], + [], ], 'validated' => [ array_merge($data, ['country_id' => $countryId]), From 78a67c33f1477e9ae5091f11e5544244758d14da Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <vovsky@atwix.com> Date: Mon, 9 Jul 2018 21:42:29 +0300 Subject: [PATCH 0167/1171] 19 Added new line to the end of the file --- .../CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php index 4ebec6490d0e3..897fb1d649d7d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php @@ -80,4 +80,4 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return $this->valueFactory->create($result); } -} \ No newline at end of file +} From 8f92ef4e1ce99b09282e7bdcaf51635a15da0a9d Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <vovsky@atwix.com> Date: Mon, 9 Jul 2018 21:49:42 +0300 Subject: [PATCH 0168/1171] 28 phpcbf fixes, attempt to pass travis tests --- .../Model/Resolver/Products/Attributes/Collection.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Attributes/Collection.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Attributes/Collection.php index cc7f658376814..ab0531ad09513 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Attributes/Collection.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Attributes/Collection.php @@ -62,7 +62,8 @@ public function getAttributes() : AttributeCollection ['eq' => '1'], ['eq' => '1'], ['eq' => '1'] - ]); + ] + ); } return $this->collection->load(); From 28167648ab16a1a8f18792420b33badbcc6423b6 Mon Sep 17 00:00:00 2001 From: Alexander Lukyanov <cybergomel@gmail.com> Date: Thu, 10 May 2018 14:19:38 -0400 Subject: [PATCH 0169/1171] Issue #11354 Merged CSS file name generation --- .../Magento/Framework/View/Asset/Merged.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) mode change 100644 => 100755 lib/internal/Magento/Framework/View/Asset/Merged.php diff --git a/lib/internal/Magento/Framework/View/Asset/Merged.php b/lib/internal/Magento/Framework/View/Asset/Merged.php old mode 100644 new mode 100755 index 5b206b235eb11..943f3eba0632c --- a/lib/internal/Magento/Framework/View/Asset/Merged.php +++ b/lib/internal/Magento/Framework/View/Asset/Merged.php @@ -40,6 +40,11 @@ class Merged implements \Iterator */ protected $contentType; + /** + * @var StorageInterface + */ + private $versionStorage; + /** * @var bool */ @@ -56,11 +61,13 @@ public function __construct( \Psr\Log\LoggerInterface $logger, MergeStrategyInterface $mergeStrategy, \Magento\Framework\View\Asset\Repository $assetRepo, + \Magento\Framework\App\View\Deployment\Version\StorageInterface $versionStorage, array $assets ) { $this->logger = $logger; $this->mergeStrategy = $mergeStrategy; $this->assetRepo = $assetRepo; + $this->versionStorage = $versionStorage; if (!$assets) { throw new \InvalidArgumentException('At least one asset has to be passed for merging.'); @@ -116,6 +123,12 @@ private function createMergedAsset(array $assets) $paths[] = $asset->getPath(); } $paths = array_unique($paths); + + $version=$this->versionStorage->load(); + if ($version) { + $paths[]=$version; + } + $filePath = md5(implode('|', $paths)) . '.' . $this->contentType; return $this->assetRepo->createArbitrary($filePath, self::getRelativeDir()); } From 2d31c1d2dc7355bb9a31413cdc481e30d3d060f9 Mon Sep 17 00:00:00 2001 From: Alex Lukyanau <cybergomel@gmail.com> Date: Thu, 10 May 2018 19:39:01 -0400 Subject: [PATCH 0170/1171] Regenerated Doc for function --- lib/internal/Magento/Framework/View/Asset/Merged.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/internal/Magento/Framework/View/Asset/Merged.php b/lib/internal/Magento/Framework/View/Asset/Merged.php index 943f3eba0632c..ae38bda0f1733 100755 --- a/lib/internal/Magento/Framework/View/Asset/Merged.php +++ b/lib/internal/Magento/Framework/View/Asset/Merged.php @@ -50,10 +50,14 @@ class Merged implements \Iterator */ protected $isInitialized = false; + /** + * Merged constructor. + * * @param \Psr\Log\LoggerInterface $logger * @param MergeStrategyInterface $mergeStrategy * @param \Magento\Framework\View\Asset\Repository $assetRepo + * @param \Magento\Framework\App\View\Deployment\Version\StorageInterface $versionStorage * @param MergeableInterface[] $assets * @throws \InvalidArgumentException */ From e7677e8c3381915fbc6f0f213b02781dea4b9b2d Mon Sep 17 00:00:00 2001 From: Alex Lukyanau <cybergomel@gmail.com> Date: Thu, 10 May 2018 19:39:31 -0400 Subject: [PATCH 0171/1171] Removed whitespace --- lib/internal/Magento/Framework/View/Asset/Merged.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Asset/Merged.php b/lib/internal/Magento/Framework/View/Asset/Merged.php index ae38bda0f1733..2dddc2e59d9ac 100755 --- a/lib/internal/Magento/Framework/View/Asset/Merged.php +++ b/lib/internal/Magento/Framework/View/Asset/Merged.php @@ -50,7 +50,6 @@ class Merged implements \Iterator */ protected $isInitialized = false; - /** * Merged constructor. * From ca3c79c538465d4053b5eeffb79b21327d03a112 Mon Sep 17 00:00:00 2001 From: Alex Lukyanau <cybergomel@gmail.com> Date: Fri, 11 May 2018 01:07:21 -0400 Subject: [PATCH 0172/1171] Travic CI Test case errors --- .../Magento/Framework/View/Asset/Merged.php | 2 +- .../Framework/View/Test/Unit/Asset/MergedTest.php | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) mode change 100644 => 100755 lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php diff --git a/lib/internal/Magento/Framework/View/Asset/Merged.php b/lib/internal/Magento/Framework/View/Asset/Merged.php index 2dddc2e59d9ac..57105ab231e03 100755 --- a/lib/internal/Magento/Framework/View/Asset/Merged.php +++ b/lib/internal/Magento/Framework/View/Asset/Merged.php @@ -41,7 +41,7 @@ class Merged implements \Iterator protected $contentType; /** - * @var StorageInterface + * @var \Magento\Framework\App\View\Deployment\Version\StorageInterface */ private $versionStorage; diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php old mode 100644 new mode 100755 index 164a2ca4d4d1b..34a19c346a64f --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php @@ -12,6 +12,7 @@ use Magento\Framework\View\Asset\Repository as AssetRepository; use Magento\Framework\View\Asset\MergeableInterface; use Magento\Framework\View\Asset\MergeStrategyInterface; +use Magento\Framework\App\View\Deployment\Version\StorageInterface; /** * Class MergedTest @@ -43,6 +44,11 @@ class MergedTest extends \PHPUnit\Framework\TestCase */ private $assetRepo; + /** + * @var StorageInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $versionStorage; + protected function setUp() { $this->assetJsOne = $this->getMockForAbstractClass(MergeableInterface::class); @@ -66,6 +72,7 @@ protected function setUp() $this->assetRepo = $this->getMockBuilder(AssetRepository::class) ->disableOriginalConstructor() ->getMock(); + $this->versionStorage = $this->createMock(StorageInterface::class); } /** @@ -74,7 +81,7 @@ protected function setUp() */ public function testConstructorNothingToMerge() { - new \Magento\Framework\View\Asset\Merged($this->logger, $this->mergeStrategy, $this->assetRepo, []); + new \Magento\Framework\View\Asset\Merged($this->logger, $this->mergeStrategy, $this->assetRepo, $this->versionStorage, []); } /** @@ -89,6 +96,7 @@ public function testConstructorRequireMergeInterface() 'logger' => $this->logger, 'mergeStrategy' => $this->mergeStrategy, 'assetRepo' => $this->assetRepo, + 'versionStorage' => $this->versionStorage, 'assets' => [$this->assetJsOne, $assetUrl], ]); } @@ -108,6 +116,7 @@ public function testConstructorIncompatibleContentTypes() 'logger' => $this->logger, 'mergeStrategy' => $this->mergeStrategy, 'assetRepo' => $this->assetRepo, + 'versionStorage' => $this->versionStorage, 'assets' => [$this->assetJsOne, $assetCss], ]); } @@ -123,6 +132,7 @@ public function testIteratorInterfaceMerge() 'logger' => $this->logger, 'mergeStrategy' => $this->mergeStrategy, 'assetRepo' => $this->assetRepo, + 'versionStorage' => $this->versionStorage, 'assets' => $assets, ]); @@ -157,6 +167,7 @@ public function testIteratorInterfaceMergeFailure() 'logger' => $this->logger, 'mergeStrategy' => $this->mergeStrategy, 'assetRepo' => $this->assetRepo, + 'versionStorage' => $this->versionStorage, 'assets' => [$this->assetJsOne, $this->assetJsTwo, $assetBroken], ]); From 311f8ca9aa283d9bcd3ab45327db3363223094de Mon Sep 17 00:00:00 2001 From: Alex Lukyanau <cybergomel@gmail.com> Date: Fri, 11 May 2018 01:29:53 -0400 Subject: [PATCH 0173/1171] Travic CI - This pull request quality could be better. --- .../Magento/Framework/View/Test/Unit/Asset/MergedTest.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php index 34a19c346a64f..2f7742f5ccbd1 100755 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php @@ -81,7 +81,12 @@ protected function setUp() */ public function testConstructorNothingToMerge() { - new \Magento\Framework\View\Asset\Merged($this->logger, $this->mergeStrategy, $this->assetRepo, $this->versionStorage, []); + new \Magento\Framework\View\Asset\Merged( + $this->logger, $this->mergeStrategy, + $this->assetRepo, + $this->versionStorage, + [] + ); } /** From 1782c54b76613323034eefda53ec85a4d4fa17ba Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Fri, 11 May 2018 09:11:59 +0300 Subject: [PATCH 0174/1171] Minor fixes --- .../Magento/Framework/View/Asset/Merged.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Asset/Merged.php b/lib/internal/Magento/Framework/View/Asset/Merged.php index 57105ab231e03..302eb1226b8ef 100755 --- a/lib/internal/Magento/Framework/View/Asset/Merged.php +++ b/lib/internal/Magento/Framework/View/Asset/Merged.php @@ -5,6 +5,8 @@ */ namespace Magento\Framework\View\Asset; +use Magento\Framework\App\ObjectManager; + /** * \Iterator that aggregates one or more assets and provides a single public file with equivalent behavior */ @@ -56,21 +58,23 @@ class Merged implements \Iterator * @param \Psr\Log\LoggerInterface $logger * @param MergeStrategyInterface $mergeStrategy * @param \Magento\Framework\View\Asset\Repository $assetRepo - * @param \Magento\Framework\App\View\Deployment\Version\StorageInterface $versionStorage * @param MergeableInterface[] $assets + * @param \Magento\Framework\App\View\Deployment\Version\StorageInterface $versionStorage * @throws \InvalidArgumentException */ public function __construct( \Psr\Log\LoggerInterface $logger, MergeStrategyInterface $mergeStrategy, \Magento\Framework\View\Asset\Repository $assetRepo, - \Magento\Framework\App\View\Deployment\Version\StorageInterface $versionStorage, - array $assets + array $assets, + \Magento\Framework\App\View\Deployment\Version\StorageInterface $versionStorage = null ) { $this->logger = $logger; $this->mergeStrategy = $mergeStrategy; $this->assetRepo = $assetRepo; - $this->versionStorage = $versionStorage; + $this->versionStorage = $versionStorage ?: ObjectManager::getInstance()->get( + \Magento\Framework\App\View\Deployment\Version\StorageInterface::class + ); if (!$assets) { throw new \InvalidArgumentException('At least one asset has to be passed for merging.'); @@ -127,9 +131,9 @@ private function createMergedAsset(array $assets) } $paths = array_unique($paths); - $version=$this->versionStorage->load(); + $version = $this->versionStorage->load(); if ($version) { - $paths[]=$version; + $paths[] = $version; } $filePath = md5(implode('|', $paths)) . '.' . $this->contentType; From 94e0e25d285781aa3bc7b8a02a0cc119e04fec8b Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Fri, 11 May 2018 09:13:44 +0300 Subject: [PATCH 0175/1171] Fixed unit test --- .../Magento/Framework/View/Test/Unit/Asset/MergedTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php index 2f7742f5ccbd1..0e06b81068b9a 100755 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php @@ -84,8 +84,8 @@ public function testConstructorNothingToMerge() new \Magento\Framework\View\Asset\Merged( $this->logger, $this->mergeStrategy, $this->assetRepo, - $this->versionStorage, - [] + [], + $this->versionStorage ); } From 831e58c2ac596592548220d111491426b433c5e0 Mon Sep 17 00:00:00 2001 From: Alex Lukyanau <cybergomel@gmail.com> Date: Fri, 11 May 2018 02:24:26 -0400 Subject: [PATCH 0176/1171] Fixed Unit Tests --- .../Magento/Framework/View/Test/Unit/Asset/MergedTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php index 0e06b81068b9a..8f6cb55d562d4 100755 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php @@ -101,8 +101,8 @@ public function testConstructorRequireMergeInterface() 'logger' => $this->logger, 'mergeStrategy' => $this->mergeStrategy, 'assetRepo' => $this->assetRepo, - 'versionStorage' => $this->versionStorage, 'assets' => [$this->assetJsOne, $assetUrl], + 'versionStorage' => $this->versionStorage, ]); } @@ -121,8 +121,8 @@ public function testConstructorIncompatibleContentTypes() 'logger' => $this->logger, 'mergeStrategy' => $this->mergeStrategy, 'assetRepo' => $this->assetRepo, - 'versionStorage' => $this->versionStorage, 'assets' => [$this->assetJsOne, $assetCss], + 'versionStorage' => $this->versionStorage, ]); } @@ -137,8 +137,8 @@ public function testIteratorInterfaceMerge() 'logger' => $this->logger, 'mergeStrategy' => $this->mergeStrategy, 'assetRepo' => $this->assetRepo, - 'versionStorage' => $this->versionStorage, 'assets' => $assets, + 'versionStorage' => $this->versionStorage, ]); $mergedAsset = $this->createMock(\Magento\Framework\View\Asset\File::class); @@ -172,8 +172,8 @@ public function testIteratorInterfaceMergeFailure() 'logger' => $this->logger, 'mergeStrategy' => $this->mergeStrategy, 'assetRepo' => $this->assetRepo, - 'versionStorage' => $this->versionStorage, 'assets' => [$this->assetJsOne, $this->assetJsTwo, $assetBroken], + 'versionStorage' => $this->versionStorage, ]); $this->logger->expects($this->once())->method('critical')->with($this->identicalTo($mergeError)); From 742ad8c6fac09a062a4dc5c99cd2e30ecc5a251a Mon Sep 17 00:00:00 2001 From: Alex Lukyanau <cybergomel@gmail.com> Date: Fri, 11 May 2018 12:29:33 -0400 Subject: [PATCH 0177/1171] Only one argument is allowed per line in a multi-line function call --- .../Magento/Framework/View/Test/Unit/Asset/MergedTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php index 8f6cb55d562d4..52b45a510e722 100755 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php @@ -82,7 +82,8 @@ protected function setUp() public function testConstructorNothingToMerge() { new \Magento\Framework\View\Asset\Merged( - $this->logger, $this->mergeStrategy, + $this->logger, + $this->mergeStrategy, $this->assetRepo, [], $this->versionStorage From 0fdc3f7308eeb220166d4ca477d2cf29eef2d043 Mon Sep 17 00:00:00 2001 From: Joseph Maxwell <joseph@swiftotter.com> Date: Tue, 19 Jun 2018 16:55:20 -0500 Subject: [PATCH 0178/1171] Adding test coverage for Group Repository not copying extension attributes. --- .../ResourceModel/GroupRepositoryTest.php | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php index 98cf8c212a784..ab206b9db3a8c 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php @@ -37,6 +37,11 @@ class GroupRepositoryTest extends \PHPUnit\Framework\TestCase * @var \Magento\Customer\Api\Data\GroupInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $group; + + /** + * @var \Magento\Customer\Api\Data\GroupInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $factoryCreatedGroup; /** * @var \Magento\Customer\Model\ResourceModel\Group|\PHPUnit_Framework_MockObject_MockObject @@ -153,6 +158,12 @@ private function setupGroupObjects() 'group', false ); + $this->factoryCreatedGroup = $this->getMockForAbstractClass( + \Magento\Customer\Api\Data\GroupInterface::class, + [], + 'group', + false + ); $this->groupResourceModel = $this->createMock(\Magento\Customer\Model\ResourceModel\Group::class); } @@ -162,16 +173,20 @@ public function testSave() $groupId = 0; $taxClass = $this->getMockForAbstractClass(\Magento\Tax\Api\Data\TaxClassInterface::class, [], '', false); + $groupExtensionAttributes = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\GroupExtensionInterface::class); - $this->group->expects($this->once()) + $this->group->expects($this->atLeastOnce()) ->method('getCode') ->willReturn('Code'); $this->group->expects($this->atLeastOnce()) ->method('getId') ->willReturn($groupId); - $this->group->expects($this->once()) + $this->group->expects($this->atLeastOnce()) ->method('getTaxClassId') ->willReturn(17); + $this->group->expects($this->atLeastOnce()) + ->method('getExtensionAttributes') + ->willReturn($groupExtensionAttributes); $this->groupModel->expects($this->atLeastOnce()) ->method('getId') @@ -185,22 +200,34 @@ public function testSave() $this->groupModel->expects($this->atLeastOnce()) ->method('getTaxClassName') ->willReturn('Tax class name'); - $this->group->expects($this->once()) + + + $this->factoryCreatedGroup->expects($this->once()) ->method('setId') ->with($groupId) ->willReturnSelf(); - $this->group->expects($this->once()) + $this->factoryCreatedGroup->expects($this->once()) ->method('setCode') ->with('Code') ->willReturnSelf(); - $this->group->expects($this->once()) + $this->factoryCreatedGroup->expects($this->once()) ->method('setTaxClassId') ->with(234) ->willReturnSelf(); - $this->group->expects($this->once()) + $this->factoryCreatedGroup->expects($this->once()) ->method('setTaxClassName') ->with('Tax class name') ->willReturnSelf(); + $this->factoryCreatedGroup->expects($this->once()) + ->method('setExtensionAttributes') + ->with($groupExtensionAttributes) + ->willReturnSelf(); + $this->factoryCreatedGroup->expects($this->atLeastOnce()) + ->method('getCode') + ->willReturn('Code'); + $this->factoryCreatedGroup->expects($this->atLeastOnce()) + ->method('getTaxClassId') + ->willReturn(17); $this->taxClassRepository->expects($this->once()) ->method('get') @@ -229,9 +256,12 @@ public function testSave() ->with($groupId); $this->groupDataFactory->expects($this->once()) ->method('create') - ->willReturn($this->group); + ->willReturn($this->factoryCreatedGroup); + + $updatedGroup = $this->model->save($this->group); - $this->assertSame($this->group, $this->model->save($this->group)); + $this->assertSame($this->group->getCode(), $updatedGroup->getCode()); + $this->assertSame($this->group->getTaxClassId(), $updatedGroup->getTaxClassId()); } /** From ce513d214bc9e258b7c53bbf8224478f0165ee0f Mon Sep 17 00:00:00 2001 From: Joseph Maxwell <joseph@swiftotter.com> Date: Tue, 19 Jun 2018 16:56:20 -0500 Subject: [PATCH 0179/1171] Including extension attributes when returning new group details --- .../Magento/Customer/Model/ResourceModel/GroupRepository.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Customer/Model/ResourceModel/GroupRepository.php b/app/code/Magento/Customer/Model/ResourceModel/GroupRepository.php index cb73b7ee1cb36..ea8d004445fba 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/GroupRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/GroupRepository.php @@ -156,6 +156,11 @@ public function save(\Magento\Customer\Api\Data\GroupInterface $group) ->setCode($groupModel->getCode()) ->setTaxClassId($groupModel->getTaxClassId()) ->setTaxClassName($groupModel->getTaxClassName()); + + if ($group->getExtensionAttributes()) { + $groupDataObject->setExtensionAttributes($group->getExtensionAttributes()); + } + return $groupDataObject; } From b89430ff76a8f79674ae9604b359e71c5a8125e7 Mon Sep 17 00:00:00 2001 From: Joseph Maxwell <joseph@swiftotter.com> Date: Tue, 19 Jun 2018 17:26:34 -0500 Subject: [PATCH 0180/1171] Updating group repository test to fix code standards problem --- .../Test/Unit/Model/ResourceModel/GroupRepositoryTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php index ab206b9db3a8c..692d3b8737d02 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php @@ -173,7 +173,7 @@ public function testSave() $groupId = 0; $taxClass = $this->getMockForAbstractClass(\Magento\Tax\Api\Data\TaxClassInterface::class, [], '', false); - $groupExtensionAttributes = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\GroupExtensionInterface::class); + $extensionAttributes = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\GroupExtensionInterface::class); $this->group->expects($this->atLeastOnce()) ->method('getCode') @@ -186,7 +186,7 @@ public function testSave() ->willReturn(17); $this->group->expects($this->atLeastOnce()) ->method('getExtensionAttributes') - ->willReturn($groupExtensionAttributes); + ->willReturn($extensionAttributes); $this->groupModel->expects($this->atLeastOnce()) ->method('getId') @@ -220,7 +220,7 @@ public function testSave() ->willReturnSelf(); $this->factoryCreatedGroup->expects($this->once()) ->method('setExtensionAttributes') - ->with($groupExtensionAttributes) + ->with($extensionAttributes) ->willReturnSelf(); $this->factoryCreatedGroup->expects($this->atLeastOnce()) ->method('getCode') From c938472a27d4af51cb7534d6ba9d9d974deb6e0d Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Wed, 20 Jun 2018 08:54:09 +0530 Subject: [PATCH 0181/1171] Update GroupRepository.php --- .../Magento/Customer/Model/ResourceModel/GroupRepository.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Model/ResourceModel/GroupRepository.php b/app/code/Magento/Customer/Model/ResourceModel/GroupRepository.php index ea8d004445fba..acd09ff421197 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/GroupRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/GroupRepository.php @@ -155,12 +155,11 @@ public function save(\Magento\Customer\Api\Data\GroupInterface $group) ->setId($groupModel->getId()) ->setCode($groupModel->getCode()) ->setTaxClassId($groupModel->getTaxClassId()) - ->setTaxClassName($groupModel->getTaxClassName()); - + ->setTaxClassName($groupModel->getTaxClassName()); if ($group->getExtensionAttributes()) { $groupDataObject->setExtensionAttributes($group->getExtensionAttributes()); } - + return $groupDataObject; } From e59beacba01bfb5dc8e901a25e3a0652a7d5bf68 Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Wed, 20 Jun 2018 08:55:01 +0530 Subject: [PATCH 0182/1171] Update GroupRepositoryTest.php --- .../Test/Unit/Model/ResourceModel/GroupRepositoryTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php index 692d3b8737d02..b2d132665362b 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php @@ -37,7 +37,7 @@ class GroupRepositoryTest extends \PHPUnit\Framework\TestCase * @var \Magento\Customer\Api\Data\GroupInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $group; - + /** * @var \Magento\Customer\Api\Data\GroupInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -173,7 +173,9 @@ public function testSave() $groupId = 0; $taxClass = $this->getMockForAbstractClass(\Magento\Tax\Api\Data\TaxClassInterface::class, [], '', false); - $extensionAttributes = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\GroupExtensionInterface::class); + $extensionAttributes = $this->getMockForAbstractClass( + \Magento\Customer\Api\Data\GroupExtensionInterface::class + ); $this->group->expects($this->atLeastOnce()) ->method('getCode') From 0b62554d1a3bfb7346b8060f9511895b0a2b97e8 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Fri, 22 Jun 2018 18:05:15 +0200 Subject: [PATCH 0183/1171] Update GroupRepositoryTest.php Simple CS fix: Removed double empty space. --- .../Test/Unit/Model/ResourceModel/GroupRepositoryTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php index b2d132665362b..9e8440b500989 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/GroupRepositoryTest.php @@ -202,8 +202,7 @@ public function testSave() $this->groupModel->expects($this->atLeastOnce()) ->method('getTaxClassName') ->willReturn('Tax class name'); - - + $this->factoryCreatedGroup->expects($this->once()) ->method('setId') ->with($groupId) From 3c632b5e86c14c549213c96cc9af7092770c8890 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Fri, 22 Jun 2018 21:04:27 +0200 Subject: [PATCH 0184/1171] CS FIX Removed empty spaces --- .../Magento/Customer/Model/ResourceModel/GroupRepository.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Model/ResourceModel/GroupRepository.php b/app/code/Magento/Customer/Model/ResourceModel/GroupRepository.php index acd09ff421197..31e0e2727436b 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/GroupRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/GroupRepository.php @@ -155,11 +155,12 @@ public function save(\Magento\Customer\Api\Data\GroupInterface $group) ->setId($groupModel->getId()) ->setCode($groupModel->getCode()) ->setTaxClassId($groupModel->getTaxClassId()) - ->setTaxClassName($groupModel->getTaxClassName()); + ->setTaxClassName($groupModel->getTaxClassName()); + if ($group->getExtensionAttributes()) { $groupDataObject->setExtensionAttributes($group->getExtensionAttributes()); } - + return $groupDataObject; } From e0a8abe7657c43f93aaf0c47775bdad5412bf73c Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Mon, 9 Jul 2018 15:38:58 -0500 Subject: [PATCH 0185/1171] MAGETWO-91465: Once integer is stored for State/Province field, it can not be changed to alphanumeric --- .../Magento/Customer/Model/Address/Validator/Country.php | 4 ++-- .../Test/Unit/Model/Address/Validator/CountryTest.php | 8 +++++++- .../Magento/Ui/view/base/web/js/lib/validation/rules.js | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Model/Address/Validator/Country.php b/app/code/Magento/Customer/Model/Address/Validator/Country.php index 832a893eb7200..1b618b4ae0693 100644 --- a/app/code/Magento/Customer/Model/Address/Validator/Country.php +++ b/app/code/Magento/Customer/Model/Address/Validator/Country.php @@ -95,8 +95,8 @@ private function validateRegion(AbstractAddress $address) $countryId = $address->getCountryId(); $countryModel = $address->getCountryModel(); $regionCollection = $countryModel->getRegionCollection(); - $region = $address->getData('region'); - $regionId = (string)$address->getData('region_id'); + $region = $address->getRegion(); + $regionId = (string)$address->getRegionId(); $allowedRegions = $regionCollection->getAllIds(); $isRegionRequired = $this->directoryData->isRegionRequired($countryId); if ($isRegionRequired && empty($allowedRegions) && !\Zend_Validate::is($region, 'NotEmpty')) { diff --git a/app/code/Magento/Customer/Test/Unit/Model/Address/Validator/CountryTest.php b/app/code/Magento/Customer/Test/Unit/Model/Address/Validator/CountryTest.php index c060d698b7f73..d8148543a55db 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Address/Validator/CountryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Address/Validator/CountryTest.php @@ -161,8 +161,14 @@ public function validateDataProvider() 'region_id2' => [ array_merge($data, ['country_id' => $countryId, 'region_id' => 2]), [$countryId++], - [1], [], + [], + ], + 'region_id3' => [ + array_merge($data, ['country_id' => $countryId, 'region_id' => 2]), + [$countryId++], + [1, 3], + ['Invalid value of "2" provided for the regionId field.'], ], 'validated' => [ array_merge($data, ['country_id' => $countryId]), diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 2a19b6ad7e12b..7aa9ead484b74 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -757,7 +757,7 @@ define([ ], 'validate-not-number-first': [ function (value) { - return utils.isEmptyNoTrim(value) || /^[^0-9].*$/.test(value); + return utils.isEmptyNoTrim(value) || /^[^0-9-\.].*$/.test(value); }, $.mage.__('First character must be letter.')//eslint-disable-line max-len ], From 6bc76e0985c8ae60915c2535dadd83587099f8db Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Mon, 9 Jul 2018 15:54:49 -0500 Subject: [PATCH 0186/1171] MAGETWO-91465: Once integer is stored for State/Province field, it can not be changed to alphanumeric --- app/code/Magento/Customer/Model/Address/Validator/Country.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Customer/Model/Address/Validator/Country.php b/app/code/Magento/Customer/Model/Address/Validator/Country.php index 1b618b4ae0693..d7fb51dbbd442 100644 --- a/app/code/Magento/Customer/Model/Address/Validator/Country.php +++ b/app/code/Magento/Customer/Model/Address/Validator/Country.php @@ -88,6 +88,8 @@ private function validateCountry(AbstractAddress $address) * * @param AbstractAddress $address * @return array + * @throws \Zend_Validate_Exception + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function validateRegion(AbstractAddress $address) { From 2f47de3ff755b0e3f9338bfcc3c64b6563874d79 Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Mon, 9 Jul 2018 16:22:49 -0500 Subject: [PATCH 0187/1171] MC-230: Customer should be able to see basic bundle product details --- .../Mftf/Section/BundleStorefrontSection.xml | 4 +- .../StorefrontBundleProductDetailsTest.xml | 85 +++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/BundleStorefrontSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/BundleStorefrontSection.xml index 9f4e6e04ac351..46f5ac59d1a70 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/BundleStorefrontSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/BundleStorefrontSection.xml @@ -10,8 +10,8 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="BundleStorefrontSection"> <!--TestingForLocationOfOptions--> - <element name="bundleOptionSelector" type="button" selector="//*[@id='bundle-slide']/span"/> - <element name="bundleOptionSelection" type="button" selector="//div[@class='nested options-list']/div[2]/label[@class='label']"/> + <element name="bundleOptionSelector" type="checkbox" selector="//*[@id='bundle-slide']/span"/> + <element name="bundleOptionSelection" type="checkbox" selector="//div[@class='nested options-list']/div[{{optionNumber}}]/label[@class='label']" parameterized="true"/> <!--Description--> <!--CE exclusively--> <element name="longDescriptionText" type="text" selector="//*[@id='description']/div/div" timeout="30"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml new file mode 100644 index 0000000000000..e8c8889afecfe --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontBundleProductDetailsTest"> + <annotations> + <features value="Bundle"/> + <stories value="Bundle product details page"/> + <title value="Customer should be able to see basic bundle product details"/> + <description value="Customer should be able to see basic bundle product details"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-230"/> + <group value="Bundle"/> + </annotations> + <before> + <!--Creating Data--> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + + <!-- Admin Login--> + <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- go to bundle product creation page--> + <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage" /> + <waitForPageLoad stepKey="waitForBundleProductCreatePageToLoad"/> + + <!--Add description--> + <click selector="{{AdminProductFormBundleSection.contentDropDown}}" stepKey="openDescriptionDropDown"/> + <fillField selector="{{AdminProductFormBundleSection.longDescription}}" userInput="This is the long description" stepKey="fillLongDescription"/> + <fillField selector="{{AdminProductFormBundleSection.shortDescription}}" userInput="This is the short description" stepKey="fillShortDescription"/> + + <!-- Add options --> + <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> + <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption3"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleOptions"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillOptionTitle"/> + <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectInputType"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> + <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> + <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> + <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddSelectedBundleProducts"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> + + <!--Create a basic bundle product--> + <actionGroup ref="CreateBasicBundleProduct" stepKey="createBundledProductForTwoSimpleProducts"> + <argument name="bundleProduct" value="BundleProduct"/> + </actionGroup> + + <!--save the product--> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButtonAgain"/> + <see userInput="You saved the product." stepKey="messageYouSavedTheProductIsShownAgain"/> + + <!--Checking details--> + <amOnPage url="{{BundleProduct.urlKey}}.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageToLoad"/> + <seeElement selector="{{BundleStorefrontSection.shortDescriptionText}}" stepKey="seeShortDescription"/> + <seeElement selector="{{BundleStorefrontSection.longDescriptionText}}" stepKey="seeLongDescription"/> + <click selector="{{BundleStorefrontSection.bundleOptionSelector}}" stepKey="clickOnCustomizationOption"/> + <waitForPageLoad stepKey="waitForDropDownContainingOptions"/> + <seeElement selector="{{BundleStorefrontSection.bundleOptionSelection('1')}}" stepKey="seeOption1"/> + <seeElement selector="{{BundleStorefrontSection.bundleOptionSelection('2')}}" stepKey="seeOption2"/> + </test> +</tests> From 909284043684dab0e423e1f79c9bab81f36f1fd9 Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Mon, 9 Jul 2018 16:28:48 -0500 Subject: [PATCH 0188/1171] MC-230: Customer should be able to see basic bundle product details --- .../Bundle/Test/Mftf/Section/BundleStorefrontSection.xml | 2 +- .../Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/BundleStorefrontSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/BundleStorefrontSection.xml index 46f5ac59d1a70..1a8709bd84e9e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/BundleStorefrontSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/BundleStorefrontSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="BundleStorefrontSection"> <!--TestingForLocationOfOptions--> - <element name="bundleOptionSelector" type="checkbox" selector="//*[@id='bundle-slide']/span"/> + <element name="bundleOptionSelector" type="checkbox" selector="//*[@id='bundle-slide']/span" timeout="30"/> <element name="bundleOptionSelection" type="checkbox" selector="//div[@class='nested options-list']/div[{{optionNumber}}]/label[@class='label']" parameterized="true"/> <!--Description--> <!--CE exclusively--> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml index e8c8889afecfe..5859d531f3472 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml @@ -78,7 +78,6 @@ <seeElement selector="{{BundleStorefrontSection.shortDescriptionText}}" stepKey="seeShortDescription"/> <seeElement selector="{{BundleStorefrontSection.longDescriptionText}}" stepKey="seeLongDescription"/> <click selector="{{BundleStorefrontSection.bundleOptionSelector}}" stepKey="clickOnCustomizationOption"/> - <waitForPageLoad stepKey="waitForDropDownContainingOptions"/> <seeElement selector="{{BundleStorefrontSection.bundleOptionSelection('1')}}" stepKey="seeOption1"/> <seeElement selector="{{BundleStorefrontSection.bundleOptionSelection('2')}}" stepKey="seeOption2"/> </test> From 74dff4246cd75ae9b657303eaf8f0c2738db30f0 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Mon, 9 Jul 2018 16:46:35 -0500 Subject: [PATCH 0189/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - refactoring to consolidate code for both cart and wishlist with a composite class - removing and deprecating old references --- .../Item/ItemResolverComposite.php | 39 +++++++++ .../Item/ItemResolverInterface.php | 23 +++++ app/code/Magento/Catalog/etc/di.xml | 3 +- .../Checkout/Block/Cart/Item/Renderer.php | 13 ++- .../Checkout/CustomerData/DefaultItem.php | 17 +++- .../Checkout/Model/Cart/ImageProvider.php | 16 +++- app/code/Magento/Checkout/etc/frontend/di.xml | 2 +- .../Block/Cart/Item/Renderer/Configurable.php | 18 +--- .../CustomerData/ConfigurableItem.php | 85 ------------------- .../Item/ItemProductResolver.php | 71 ++++++++++++++++ .../Cart/Item/Renderer/ConfigurableTest.php | 52 ------------ .../Magento/ConfigurableProduct/etc/di.xml | 7 ++ .../ConfigurableProduct/etc/frontend/di.xml | 7 -- .../Block/Cart/Item/Renderer/Grouped.php | 17 +--- .../CustomerData/GroupedItem.php | 80 ----------------- .../Item/ItemProductResolver.php | 72 ++++++++++++++++ .../Block/Cart/Item/Renderer/GroupedTest.php | 52 ------------ app/code/Magento/GroupedProduct/etc/di.xml | 7 ++ .../GroupedProduct/etc/frontend/di.xml | 17 ---- .../Customer/Wishlist/Item/Column/Image.php | 28 ++---- .../Wishlist/CustomerData/Wishlist.php | 8 +- 21 files changed, 277 insertions(+), 357 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php create mode 100644 app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php delete mode 100644 app/code/Magento/ConfigurableProduct/CustomerData/ConfigurableItem.php create mode 100644 app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php delete mode 100644 app/code/Magento/GroupedProduct/CustomerData/GroupedItem.php create mode 100644 app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php delete mode 100644 app/code/Magento/GroupedProduct/etc/frontend/di.xml diff --git a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php new file mode 100644 index 0000000000000..3c34438da55a4 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Model\Product\Configuration\Item; + +/** + * Composite implementation for @see ItemResolverInterface + */ +class ItemResolverComposite implements ItemResolverInterface +{ + /** @var ItemResolverInterface[] */ + private $itemResolvers = []; + + /** + * @param ItemResolverInterface[] $itemResolvers + */ + public function __construct(array $itemResolvers) + { + $this->itemResolvers = $itemResolvers; + } + + /** + * {@inheritdoc} + */ + public function getFinalProduct( + \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item + ) : \Magento\Catalog\Api\Data\ProductInterface { + $product = $item->getProduct(); + foreach ($this->itemResolvers as $resolver) { + $resolvedProduct = $resolver->getFinalProduct($item); + if ($resolvedProduct !== $product) { + return $resolvedProduct; + } + } + return $product; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php new file mode 100644 index 0000000000000..6d2d201ac2a3d --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Model\Product\Configuration\Item; + +/** + * Resolves the product for a configured item + * + * @api + */ +interface ItemResolverInterface +{ + /** + * Get the final product from a configured item by product type and selection + * + * @param ItemInterface $item + * @return \Magento\Catalog\Api\Data\ProductInterface + */ + public function getFinalProduct(\Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item) + : \Magento\Catalog\Api\Data\ProductInterface; +} diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 9f1fb020ef95a..383a0715d26d4 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -70,7 +70,8 @@ <preference for="Magento\Catalog\Api\Data\ProductRender\FormattedPriceInfoInterface" type="Magento\Catalog\Model\ProductRender\FormattedPriceInfo" /> <preference for="Magento\Framework\Indexer\BatchProviderInterface" type="Magento\Framework\Indexer\BatchProvider" /> <preference for="Magento\Catalog\Model\Indexer\Product\Price\UpdateIndexInterface" type="Magento\Catalog\Model\Indexer\Product\Price\InvalidateIndex" /> - <preference for="\Magento\Catalog\Model\Product\Gallery\ImagesConfigFactoryInterface" type="\Magento\Catalog\Model\Product\Gallery\ImagesConfigFactory" /> + <preference for="Magento\Catalog\Model\Product\Gallery\ImagesConfigFactoryInterface" type="Magento\Catalog\Model\Product\Gallery\ImagesConfigFactory" /> + <preference for="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface" type="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverComposite" /> <type name="Magento\Customer\Model\ResourceModel\Visitor"> <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" /> </type> diff --git a/app/code/Magento/Checkout/Block/Cart/Item/Renderer.php b/app/code/Magento/Checkout/Block/Cart/Item/Renderer.php index 06be39d0f3516..a43f074d8df67 100644 --- a/app/code/Magento/Checkout/Block/Cart/Item/Renderer.php +++ b/app/code/Magento/Checkout/Block/Cart/Item/Renderer.php @@ -11,6 +11,8 @@ use Magento\Framework\View\Element\AbstractBlock; use Magento\Framework\View\Element\Message\InterpretationStrategyInterface; use Magento\Quote\Model\Quote\Item\AbstractItem; +use Magento\Framework\App\ObjectManager; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; /** * Shopping cart item render block @@ -21,6 +23,7 @@ * @method \Magento\Checkout\Block\Cart\Item\Renderer setProductName(string) * @method \Magento\Checkout\Block\Cart\Item\Renderer setDeleteUrl(string) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class Renderer extends \Magento\Framework\View\Element\Template implements \Magento\Framework\DataObject\IdentityInterface @@ -91,6 +94,9 @@ class Renderer extends \Magento\Framework\View\Element\Template implements */ private $messageInterpretationStrategy; + /** @var ItemResolverInterface */ + private $itemResolver; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Catalog\Helper\Product\Configuration $productConfig @@ -102,6 +108,7 @@ class Renderer extends \Magento\Framework\View\Element\Template implements * @param \Magento\Framework\Module\Manager $moduleManager * @param InterpretationStrategyInterface $messageInterpretationStrategy * @param array $data + * @param ItemResolverInterface|null $itemResolver * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @codeCoverageIgnore */ @@ -115,7 +122,8 @@ public function __construct( PriceCurrencyInterface $priceCurrency, \Magento\Framework\Module\Manager $moduleManager, InterpretationStrategyInterface $messageInterpretationStrategy, - array $data = [] + array $data = [], + ItemResolverInterface $itemResolver = null ) { $this->priceCurrency = $priceCurrency; $this->imageBuilder = $imageBuilder; @@ -127,6 +135,7 @@ public function __construct( $this->_isScopePrivate = true; $this->moduleManager = $moduleManager; $this->messageInterpretationStrategy = $messageInterpretationStrategy; + $this->itemResolver = $itemResolver ?: ObjectManager::getInstance()->get(ItemResolverInterface::class); } /** @@ -172,7 +181,7 @@ public function getProduct() */ public function getProductForThumbnail() { - return $this->getProduct(); + return $this->itemResolver->getFinalProduct($this->getItem()); } /** diff --git a/app/code/Magento/Checkout/CustomerData/DefaultItem.php b/app/code/Magento/Checkout/CustomerData/DefaultItem.php index 9351685405a60..2a80843e64ca3 100644 --- a/app/code/Magento/Checkout/CustomerData/DefaultItem.php +++ b/app/code/Magento/Checkout/CustomerData/DefaultItem.php @@ -7,6 +7,7 @@ namespace Magento\Checkout\CustomerData; use Magento\Framework\App\ObjectManager; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; /** * Default item @@ -45,6 +46,9 @@ class DefaultItem extends AbstractItem */ private $escaper; + /** @var ItemResolverInterface */ + private $itemResolver; + /** * @param \Magento\Catalog\Helper\Image $imageHelper * @param \Magento\Msrp\Helper\Data $msrpHelper @@ -52,6 +56,7 @@ class DefaultItem extends AbstractItem * @param \Magento\Catalog\Helper\Product\ConfigurationPool $configurationPool * @param \Magento\Checkout\Helper\Data $checkoutHelper * @param \Magento\Framework\Escaper|null $escaper + * @param ItemResolverInterface|null $itemResolver * @codeCoverageIgnore */ public function __construct( @@ -60,7 +65,8 @@ public function __construct( \Magento\Framework\UrlInterface $urlBuilder, \Magento\Catalog\Helper\Product\ConfigurationPool $configurationPool, \Magento\Checkout\Helper\Data $checkoutHelper, - \Magento\Framework\Escaper $escaper = null + \Magento\Framework\Escaper $escaper = null, + ItemResolverInterface $itemResolver = null ) { $this->configurationPool = $configurationPool; $this->imageHelper = $imageHelper; @@ -68,6 +74,7 @@ public function __construct( $this->urlBuilder = $urlBuilder; $this->checkoutHelper = $checkoutHelper; $this->escaper = $escaper ?: ObjectManager::getInstance()->get(\Magento\Framework\Escaper::class); + $this->itemResolver = $itemResolver ?: ObjectManager::getInstance()->get(ItemResolverInterface::class); } /** @@ -75,7 +82,10 @@ public function __construct( */ protected function doGetItemData() { - $imageHelper = $this->imageHelper->init($this->getProductForThumbnail(), 'mini_cart_product_thumbnail'); + $imageHelper = $this->imageHelper->init( + $this->itemResolver->getFinalProduct($this->item), + 'mini_cart_product_thumbnail' + ); $productName = $this->escaper->escapeHtml($this->item->getProduct()->getName()); return [ @@ -115,11 +125,12 @@ protected function getOptionList() /** * @return \Magento\Catalog\Model\Product + * @deprecated * @codeCoverageIgnore */ protected function getProductForThumbnail() { - return $this->getProduct(); + return $this->itemResolver->getFinalProduct($this->item); } /** diff --git a/app/code/Magento/Checkout/Model/Cart/ImageProvider.php b/app/code/Magento/Checkout/Model/Cart/ImageProvider.php index d8d0003d8ca7e..a75dbd6b5a505 100644 --- a/app/code/Magento/Checkout/Model/Cart/ImageProvider.php +++ b/app/code/Magento/Checkout/Model/Cart/ImageProvider.php @@ -5,8 +5,10 @@ */ namespace Magento\Checkout\Model\Cart; +use Magento\Checkout\CustomerData\DefaultItem; +use Magento\Framework\App\ObjectManager; + /** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @api */ class ImageProvider @@ -18,20 +20,26 @@ class ImageProvider /** * @var \Magento\Checkout\CustomerData\ItemPoolInterface + * @deprecated */ protected $itemPool; + /** @var \Magento\Checkout\CustomerData\DefaultItem */ + protected $customerDataItem; + /** * @param \Magento\Quote\Api\CartItemRepositoryInterface $itemRepository * @param \Magento\Checkout\CustomerData\ItemPoolInterface $itemPool - * @codeCoverageIgnore + * @param DefaultItem|null $customerDataItem */ public function __construct( \Magento\Quote\Api\CartItemRepositoryInterface $itemRepository, - \Magento\Checkout\CustomerData\ItemPoolInterface $itemPool + \Magento\Checkout\CustomerData\ItemPoolInterface $itemPool, + \Magento\Checkout\CustomerData\DefaultItem $customerDataItem = null ) { $this->itemRepository = $itemRepository; $this->itemPool = $itemPool; + $this->customerDataItem = $customerDataItem ?? ObjectManager::getInstance()->get(DefaultItem::class); } /** @@ -45,7 +53,7 @@ public function getImages($cartId) $items = $this->itemRepository->getList($cartId); /** @var \Magento\Quote\Model\Quote\Item $cartItem */ foreach ($items as $cartItem) { - $allData = $this->itemPool->getItemData($cartItem); + $allData = $this->customerDataItem->getItemData($cartItem); $itemData[$cartItem->getItemId()] = $allData['product_image']; } return $itemData; diff --git a/app/code/Magento/Checkout/etc/frontend/di.xml b/app/code/Magento/Checkout/etc/frontend/di.xml index 889689e6c0d16..f2aced01b3942 100644 --- a/app/code/Magento/Checkout/etc/frontend/di.xml +++ b/app/code/Magento/Checkout/etc/frontend/di.xml @@ -39,7 +39,7 @@ </type> <preference for="Magento\Checkout\CustomerData\ItemPoolInterface" type="Magento\Checkout\CustomerData\ItemPool"/> - <type name="Magento\Checkout\CustomerData\ItemPoolInterface"> + <type name="Magento\Checkout\CustomerData\ItemPool"> <arguments> <argument name="defaultItemId" xsi:type="string">Magento\Checkout\CustomerData\DefaultItem</argument> </arguments> diff --git a/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php b/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php index 2698da8dcb368..557228defc0dc 100644 --- a/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php @@ -57,25 +57,11 @@ public function getOptionList() /** * {@inheritdoc} + * @deprecated */ public function getProductForThumbnail() { - /** - * Show parent product thumbnail if it must be always shown according to the related setting in system config - * or if child thumbnail is not available - */ - if ($this->_scopeConfig->getValue( - self::CONFIG_THUMBNAIL_SOURCE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) == ThumbnailSource::OPTION_USE_PARENT_IMAGE - || !($this->getChildProduct() - && $this->getChildProduct()->getThumbnail() && $this->getChildProduct()->getThumbnail() != 'no_selection') - ) { - $product = $this->getProduct(); - } else { - $product = $this->getChildProduct(); - } - return $product; + return parent::getProductForThumbnail(); } /** diff --git a/app/code/Magento/ConfigurableProduct/CustomerData/ConfigurableItem.php b/app/code/Magento/ConfigurableProduct/CustomerData/ConfigurableItem.php deleted file mode 100644 index 3a9ed653305c5..0000000000000 --- a/app/code/Magento/ConfigurableProduct/CustomerData/ConfigurableItem.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\ConfigurableProduct\CustomerData; - -use Magento\Catalog\Model\Config\Source\Product\Thumbnail as ThumbnailSource; -use Magento\Checkout\CustomerData\DefaultItem; - -/** - * Configurable item - */ -class ConfigurableItem extends DefaultItem -{ - /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface - */ - protected $_scopeConfig; - - /** - * @param \Magento\Catalog\Helper\Image $imageHelper - * @param \Magento\Msrp\Helper\Data $msrpHelper - * @param \Magento\Framework\UrlInterface $urlBuilder - * @param \Magento\Catalog\Helper\Product\ConfigurationPool $configurationPool - * @param \Magento\Checkout\Helper\Data $checkoutHelper - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Framework\Escaper|null $escaper - */ - public function __construct( - \Magento\Catalog\Helper\Image $imageHelper, - \Magento\Msrp\Helper\Data $msrpHelper, - \Magento\Framework\UrlInterface $urlBuilder, - \Magento\Catalog\Helper\Product\ConfigurationPool $configurationPool, - \Magento\Checkout\Helper\Data $checkoutHelper, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Framework\Escaper $escaper = null - ) { - parent::__construct( - $imageHelper, - $msrpHelper, - $urlBuilder, - $configurationPool, - $checkoutHelper, - $escaper - ); - $this->_scopeConfig = $scopeConfig; - } - - /** - * {@inheritdoc} - */ - protected function getProductForThumbnail() - { - /** - * Show parent product thumbnail if it must be always shown according to the related setting in system config - * or if child thumbnail is not available - */ - $config = $this->_scopeConfig->getValue( - \Magento\ConfigurableProduct\Block\Cart\Item\Renderer\Configurable::CONFIG_THUMBNAIL_SOURCE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - - $product = $config == ThumbnailSource::OPTION_USE_PARENT_IMAGE - || (!$this->getChildProduct()->getThumbnail() || $this->getChildProduct()->getThumbnail() == 'no_selection') - ? $this->getProduct() - : $this->getChildProduct(); - - return $product; - } - - /** - * Get item configurable child product - * - * @return \Magento\Catalog\Model\Product - */ - protected function getChildProduct() - { - if ($option = $this->item->getOptionByCode('simple_product')) { - return $option->getProduct(); - } - return $this->getProduct(); - } -} diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php new file mode 100644 index 0000000000000..135f57462761e --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Model\Product\Configuration\Item; + +use Magento\Catalog\Model\Config\Source\Product\Thumbnail; + +/** + * Resolves the product for a configured option item + */ +class ItemProductResolver implements \Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface +{ + const CONFIG_THUMBNAIL_SOURCE = 'checkout/cart/configurable_product_image'; + + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + protected $scopeConfig; + + /** + * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + */ + public function __construct(\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig) + { + $this->scopeConfig = $scopeConfig; + } + + /** + * Identify the product from which thumbnail should be taken. + * + * @param \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item + * @return \Magento\Catalog\Api\Data\ProductInterface + */ + public function getFinalProduct( + \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item + ) : \Magento\Catalog\Api\Data\ProductInterface { + /** + * Show parent product thumbnail if it must be always shown according to the related setting in system config + * or if child thumbnail is not available + */ + $parentItem = $item->getProduct(); + $config = $this->scopeConfig->getValue( + \Magento\ConfigurableProduct\Block\Cart\Item\Renderer\Configurable::CONFIG_THUMBNAIL_SOURCE, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + return $config == Thumbnail::OPTION_USE_PARENT_IMAGE + || (!$this->getChildProduct($item)->getData('thumbnail') + || $this->getChildProduct($item)->getData('thumbnail') == 'no_selection') + ? $parentItem + : $this->getChildProduct($item); + } + + /** + * Get item configurable child product + * + * @param \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item + * @return \Magento\Catalog\Model\Product + */ + private function getChildProduct( + \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item + ) : \Magento\Catalog\Model\Product { + $option = $item->getOptionByCode('simple_product'); + if ($option) { + return $option->getProduct(); + } + return $item->getProduct(); + } +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Cart/Item/Renderer/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Cart/Item/Renderer/ConfigurableTest.php index e199841cbcdc4..57d1bfb8ac146 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Cart/Item/Renderer/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Cart/Item/Renderer/ConfigurableTest.php @@ -47,58 +47,6 @@ protected function setUp() ); } - /** - * Child thumbnail is available and config option is not set to use parent thumbnail. - */ - public function testGetProductForThumbnail() - { - $childHasThumbnail = true; - $useParentThumbnail = false; - $products = $this->_initProducts($childHasThumbnail, $useParentThumbnail); - - $productForThumbnail = $this->_renderer->getProductForThumbnail(); - $this->assertSame( - $products['childProduct'], - $productForThumbnail, - 'Child product was expected to be returned.' - ); - } - - /** - * Child thumbnail is not available and config option is not set to use parent thumbnail. - */ - public function testGetProductForThumbnailChildThumbnailNotAvailable() - { - $childHasThumbnail = false; - $useParentThumbnail = false; - $products = $this->_initProducts($childHasThumbnail, $useParentThumbnail); - - $productForThumbnail = $this->_renderer->getProductForThumbnail(); - $this->assertSame( - $products['parentProduct'], - $productForThumbnail, - 'Parent product was expected to be returned.' - ); - } - - /** - * Child thumbnail is available and config option is set to use parent thumbnail. - */ - public function testGetProductForThumbnailConfigUseParent() - { - $childHasThumbnail = true; - $useParentThumbnail = true; - $products = $this->_initProducts($childHasThumbnail, $useParentThumbnail); - - $productForThumbnail = $this->_renderer->getProductForThumbnail(); - $this->assertSame( - $products['parentProduct'], - $productForThumbnail, - 'Parent product was expected to be returned ' . - 'if "checkout/cart/configurable_product_image option" is set to "parent" in system config.' - ); - } - /** * Initialize parent configurable product and child product. * diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml index 15dbc53a5447a..2e6dbdf277477 100644 --- a/app/code/Magento/ConfigurableProduct/etc/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/di.xml @@ -205,4 +205,11 @@ <type name="Magento\Catalog\Model\Product"> <plugin name="product_identities_extender" type="Magento\ConfigurableProduct\Model\Plugin\ProductIdentitiesExtender" /> </type> + <type name="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverComposite"> + <arguments> + <argument name="itemResolvers" xsi:type="array"> + <item name="configurable" xsi:type="object">Magento\ConfigurableProduct\Model\Product\Configuration\Item\ItemProductResolver</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml b/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml index 592b0292c98ab..bb830c36b929d 100644 --- a/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml @@ -7,13 +7,6 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <type name="Magento\Checkout\CustomerData\ItemPoolInterface"> - <arguments> - <argument name="itemMap" xsi:type="array"> - <item name="configurable" xsi:type="string">Magento\ConfigurableProduct\CustomerData\ConfigurableItem</item> - </argument> - </arguments> - </type> <type name="Magento\ConfigurableProduct\Model\ResourceModel\Attribute\OptionSelectBuilderInterface"> <plugin name="Magento_ConfigurableProduct_Plugin_Model_ResourceModel_Attribute_InStockOptionSelectBuilder" type="Magento\ConfigurableProduct\Plugin\Model\ResourceModel\Attribute\InStockOptionSelectBuilder"/> </type> diff --git a/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php b/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php index a97b1c246741e..0340a78b45a39 100644 --- a/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php +++ b/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php @@ -38,24 +38,11 @@ public function getGroupedProduct() /** * {@inheritdoc} + * @deprecated */ public function getProductForThumbnail() { - /** - * Show grouped product thumbnail if it must be always shown according to the related setting in system config - * or if child product thumbnail is not available - */ - if ($this->_scopeConfig->getValue( - self::CONFIG_THUMBNAIL_SOURCE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) == ThumbnailSource::OPTION_USE_PARENT_IMAGE || - !($this->getProduct()->getThumbnail() && $this->getProduct()->getThumbnail() != 'no_selection') - ) { - $product = $this->getGroupedProduct(); - } else { - $product = $this->getProduct(); - } - return $product; + return parent::getProductForThumbnail(); } /** diff --git a/app/code/Magento/GroupedProduct/CustomerData/GroupedItem.php b/app/code/Magento/GroupedProduct/CustomerData/GroupedItem.php deleted file mode 100644 index a38ce3ac65296..0000000000000 --- a/app/code/Magento/GroupedProduct/CustomerData/GroupedItem.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\GroupedProduct\CustomerData; - -use Magento\Catalog\Model\Config\Source\Product\Thumbnail as ThumbnailSource; -use Magento\Checkout\CustomerData\DefaultItem; - -class GroupedItem extends DefaultItem -{ - /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface - */ - protected $_scopeConfig; - - /** - * @param \Magento\Catalog\Helper\Image $imageHelper - * @param \Magento\Msrp\Helper\Data $msrpHelper - * @param \Magento\Framework\UrlInterface $urlBuilder - * @param \Magento\Catalog\Helper\Product\ConfigurationPool $configurationPool - * @param \Magento\Checkout\Helper\Data $checkoutHelper - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Framework\Escaper|null $escaper - */ - public function __construct( - \Magento\Catalog\Helper\Image $imageHelper, - \Magento\Msrp\Helper\Data $msrpHelper, - \Magento\Framework\UrlInterface $urlBuilder, - \Magento\Catalog\Helper\Product\ConfigurationPool $configurationPool, - \Magento\Checkout\Helper\Data $checkoutHelper, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Framework\Escaper $escaper = null - ) { - parent::__construct( - $imageHelper, - $msrpHelper, - $urlBuilder, - $configurationPool, - $checkoutHelper, - $escaper - ); - $this->_scopeConfig = $scopeConfig; - } - - /** - * {@inheritdoc} - */ - protected function getProductForThumbnail() - { - /** - * Show grouped product thumbnail if it must be always shown according to the related setting in system config - * or if child product thumbnail is not available - */ - $config = $this->_scopeConfig->getValue( - \Magento\GroupedProduct\Block\Cart\Item\Renderer\Grouped::CONFIG_THUMBNAIL_SOURCE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - $product = $config == ThumbnailSource::OPTION_USE_PARENT_IMAGE || - (!$this->getProduct()->getThumbnail() || $this->getProduct()->getThumbnail() == 'no_selection') - ? $this->getGroupedProduct() - : $this->getProduct(); - return $product; - } - - /** - * Get item grouped product - * - * @return \Magento\Catalog\Model\Product - */ - protected function getGroupedProduct() - { - $option = $this->item->getOptionByCode('product_type'); - if ($option) { - return $option->getProduct(); - } - return $this->getProduct(); - } -} diff --git a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php new file mode 100644 index 0000000000000..825a62cc52275 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\GroupedProduct\Model\Product\Configuration\Item; + +use Magento\Catalog\Model\Config\Source\Product\Thumbnail; + +/** + * Resolves the product for a configured option item + */ +class ItemProductResolver implements \Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface +{ + /** + * Path in config to the setting which defines if parent or child product should be used to generate a thumbnail. + */ + const CONFIG_THUMBNAIL_SOURCE = 'checkout/cart/grouped_product_image'; + + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + protected $scopeConfig; + + /** + * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + */ + public function __construct(\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig) + { + $this->scopeConfig = $scopeConfig; + } + + /** + * Identify the product from which thumbnail should be taken. + * + * @param \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item + * @return \Magento\Catalog\Api\Data\ProductInterface + */ + public function getFinalProduct( + \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item + ) : \Magento\Catalog\Api\Data\ProductInterface { + /** + * Show grouped product thumbnail if it must be always shown according to the related setting in system config + * or if child product thumbnail is not available + */ + $config = $this->scopeConfig->getValue( + \Magento\GroupedProduct\Block\Cart\Item\Renderer\Grouped::CONFIG_THUMBNAIL_SOURCE, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + $childProduct = $item->getProduct(); + return $config == Thumbnail::OPTION_USE_PARENT_IMAGE || + (!$childProduct->getData('thumbnail') || $childProduct->getData('thumbnail') == 'no_selection') + ? $this->getParentProduct($item) + : $childProduct; + } + + /** + * Get grouped product + * + * @param \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item + * @return \Magento\Catalog\Model\Product + */ + private function getParentProduct( + \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item + ) : \Magento\Catalog\Model\Product { + $option = $item->getOptionByCode('product_type'); + if ($option) { + return $option->getProduct(); + } + return $item->getProduct(); + } +} diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Block/Cart/Item/Renderer/GroupedTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Block/Cart/Item/Renderer/GroupedTest.php index 5dc12fe39c46a..1ecf0cbb88d19 100644 --- a/app/code/Magento/GroupedProduct/Test/Unit/Block/Cart/Item/Renderer/GroupedTest.php +++ b/app/code/Magento/GroupedProduct/Test/Unit/Block/Cart/Item/Renderer/GroupedTest.php @@ -27,58 +27,6 @@ protected function setUp() ); } - /** - * Child thumbnail is available and config option is not set to use parent thumbnail. - */ - public function testGetProductForThumbnail() - { - $childHasThumbnail = true; - $useParentThumbnail = false; - $products = $this->_initProducts($childHasThumbnail, $useParentThumbnail); - - $productForThumbnail = $this->_renderer->getProductForThumbnail(); - $this->assertSame( - $products['childProduct'], - $productForThumbnail, - 'Child product was expected to be returned.' - ); - } - - /** - * Child thumbnail is not available and config option is not set to use parent thumbnail. - */ - public function testGetProductForThumbnailChildThumbnailNotAvailable() - { - $childHasThumbnail = false; - $useParentThumbnail = false; - $products = $this->_initProducts($childHasThumbnail, $useParentThumbnail); - - $productForThumbnail = $this->_renderer->getProductForThumbnail(); - $this->assertSame( - $products['parentProduct'], - $productForThumbnail, - 'Parent product was expected to be returned.' - ); - } - - /** - * Child thumbnail is available and config option is set to use parent thumbnail. - */ - public function testGetProductForThumbnailConfigUseParent() - { - $childHasThumbnail = true; - $useParentThumbnail = true; - $products = $this->_initProducts($childHasThumbnail, $useParentThumbnail); - - $productForThumbnail = $this->_renderer->getProductForThumbnail(); - $this->assertSame( - $products['parentProduct'], - $productForThumbnail, - 'Parent product was expected to be returned ' . - 'if "checkout/cart/grouped_product_image" is set to "parent" in system config.' - ); - } - /** * Initialize parent grouped product and child product. * diff --git a/app/code/Magento/GroupedProduct/etc/di.xml b/app/code/Magento/GroupedProduct/etc/di.xml index 8f688e3d06b00..684787a45bbed 100644 --- a/app/code/Magento/GroupedProduct/etc/di.xml +++ b/app/code/Magento/GroupedProduct/etc/di.xml @@ -100,4 +100,11 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverComposite"> + <arguments> + <argument name="itemResolvers" xsi:type="array"> + <item name="grouped" xsi:type="object">Magento\GroupedProduct\Model\Product\Configuration\Item\ItemProductResolver</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/GroupedProduct/etc/frontend/di.xml b/app/code/Magento/GroupedProduct/etc/frontend/di.xml deleted file mode 100644 index 755d53f46bfdd..0000000000000 --- a/app/code/Magento/GroupedProduct/etc/frontend/di.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <type name="Magento\Checkout\CustomerData\ItemPoolInterface"> - <arguments> - <argument name="itemMap" xsi:type="array"> - <item name="grouped" xsi:type="string">Magento\GroupedProduct\CustomerData\GroupedItem</item> - </argument> - </arguments> - </type> -</config> diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php index f76543527e0fd..bd055937b9719 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php @@ -14,8 +14,7 @@ use Magento\Catalog\Model\Product\Image\UrlBuilder; use Magento\Framework\View\ConfigInterface; use Magento\Framework\App\ObjectManager; -use Magento\Quote\Model\Quote\ItemFactory; -use Magento\Checkout\Block\Cart\Item\Renderer; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; /** * @api @@ -23,11 +22,8 @@ */ class Image extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column { - /** @var \Magento\Checkout\Block\Cart\Item\Renderer[] */ - private $renderers = []; - - /** @var \Magento\Quote\Model\Quote\ItemFactory */ - private $itemFactory; + /** @var ItemResolverInterface */ + private $itemResolver; /** * @param \Magento\Catalog\Block\Product\Context $context @@ -35,8 +31,7 @@ class Image extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column * @param array $data * @param ConfigInterface|null $config * @param UrlBuilder|null $urlBuilder - * @param Renderer[] $renderers - * @param ItemFactory|null $itemFactory + * @param ItemResolverInterface|null $itemResolver */ public function __construct( \Magento\Catalog\Block\Product\Context $context, @@ -44,11 +39,9 @@ public function __construct( array $data = [], ConfigInterface $config = null, UrlBuilder $urlBuilder = null, - array $renderers = [], - ItemFactory $itemFactory = null + ItemResolverInterface $itemResolver = null ) { - $this->renderers = $renderers; - $this->itemFactory = $itemFactory ?? ObjectManager::getInstance()->get(ItemFactory::class); + $this->itemResolver = $itemResolver ?? ObjectManager::getInstance()->get(ItemResolverInterface::class); parent::__construct( $context, $httpContext, @@ -65,13 +58,6 @@ public function __construct( */ public function getProductForThumbnail(\Magento\Wishlist\Model\Item $item) { - $product = $product = $item->getProduct(); - if (isset($this->renderers[$product->getTypeId()])) { - $quoteItem = $this->itemFactory->create(['data' => $item->getData()]); - $quoteItem->setProduct($product); - $quoteItem->setOptions($item->getOptions()); - return $this->renderers[$product->getTypeId()]->setItem($quoteItem)->getProductForThumbnail(); - } - return $product; + return $this->itemResolver->getFinalProduct($item); } } diff --git a/app/code/Magento/Wishlist/CustomerData/Wishlist.php b/app/code/Magento/Wishlist/CustomerData/Wishlist.php index c04910a74d81d..c4cb273078468 100644 --- a/app/code/Magento/Wishlist/CustomerData/Wishlist.php +++ b/app/code/Magento/Wishlist/CustomerData/Wishlist.php @@ -131,8 +131,14 @@ protected function getItems() protected function getItemData(\Magento\Wishlist\Model\Item $wishlistItem) { $product = $wishlistItem->getProduct(); + + /** @var \Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface $itemProductResolver */ + $itemProductResolver = ObjectManager::getInstance()->get( + \Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface::class + ); + return [ - 'image' => $this->getImageData($this->image->getProductForThumbnail($wishlistItem)), + 'image' => $this->getImageData($itemProductResolver->getFinalProduct($wishlistItem)), 'product_sku' => $product->getSku(), 'product_id' => $product->getId(), 'product_url' => $this->wishlistHelper->getProductUrl($wishlistItem), From 7c48e9cf3d5b791f1b8fc5f97f75b48d90eeb5ad Mon Sep 17 00:00:00 2001 From: Devagouda <dpatil@magento.com> Date: Mon, 9 Jul 2018 17:52:31 -0500 Subject: [PATCH 0190/1171] MAGETWO-8709:[GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 -added functional test to cover bug fix --- .../Catalog/Test/Mftf/Data/ProductData.xml | 9 +++ .../Mftf/Section/AdminProductGridSection.xml | 1 + ...orefrontCustomerWishlistProductSection.xml | 1 + ...orefrontCustomerWishlistSidebarSection.xml | 1 + ...tChildImageShouldBeShownOnWishListTest.xml | 74 +++++++++++++++++++ 5 files changed, 86 insertions(+) create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 677214011c987..edd5df9facf73 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -171,6 +171,15 @@ <data key="filename">magento-logo</data> <data key="file_extension">png</data> </entity> + <entity name="TestImageNew" type="image"> + <data key="title" unique="suffix">magento-again</data> + <data key="price">1.00</data> + <data key="file_type">Upload File</data> + <data key="shareable">Yes</data> + <data key="file">magento-again.jpg</data> + <data key="filename">magento-again</data> + <data key="file_extension">jpg</data> + </entity> <entity name="ProductWithUnicode" type="product"> <data key="sku" unique="suffix">霁产品</data> <data key="type_id">simple</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml index de3c2b731a3d5..98afed124c698 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml @@ -28,5 +28,6 @@ <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)"/> <element name="productGridCheckboxOnRow" type="checkbox" selector="//*[@id='container']//tr[{{row}}]/td[1]//input" parameterized="true"/> <element name="productGridNameProduct" type="input" selector="//tbody//tr//td//div[contains(., '{{var1}}')]" parameterized="true" timeout="30"/> + <element name="selectRowBasedOnName" type="input" selector="//td/div[text()='{{var1}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml index bd9fadb630c95..8115e591aa9f1 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml @@ -14,5 +14,6 @@ <element name="ProductImageByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//img[@class='product-image-photo']" parameterized="true"/> <element name="ProductInfoByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//div[@class='product-item-info']" parameterized="true"/> <element name="ProductAddToCartByName" type="button" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//button[contains(@class, 'action tocart primary')]" parameterized="true"/> + <element name="ProductImageByImageName" type="text" selector="//main//li//a//img[contains(@src, '{{var1}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSidebarSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSidebarSection.xml index d3e89d419310c..55d4564d68751 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSidebarSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSidebarSection.xml @@ -13,5 +13,6 @@ <element name="ProductPriceByName" type="text" selector="//main//ol[@id='wishlist-sidebar']//a[@class='product-item-link']/span[text()='{{var1}}']//ancestor::ol//span[@class='price']" parameterized="true"/> <element name="ProductImageByName" type="text" selector="//main//ol[@id='wishlist-sidebar']//a[@class='product-item-link']/span[text()='{{var1}}']//ancestor::ol//img[@class='product-image-photo']" parameterized="true"/> <element name="ProductAddToCartByName" type="button" selector="//main//ol[@id='wishlist-sidebar']//a[@class='product-item-link']/span[text()='{{var1}}']//ancestor::ol//button[contains(@class, 'action tocart primary')]" parameterized="true"/> + <element name="ProductImageByImageName" type="text" selector="//main//ol[@id='wishlist-sidebar']//a//img[contains(@src, '{{var1}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml new file mode 100644 index 0000000000000..2f795339e06b8 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="ConfigurableProductChildImageShouldBeShownOnWishListTest"> + <annotations> + <features value="Wishlist"/> + <stories value="MAGETWO-8709"/> + <group value="wishlist12"/> + <title value="When user add Configurable child product to WIshlist then child product image should be shown in Wishlist"/> + <description value="When user add Configurable child product to WIshlist then child product image should be shown in Wishlist"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-93097"/> + </annotations> + <before> + <magentoCLI command="config:set checkout/cart/configurable_product_image 0" stepKey="setProductImageSettingUnderCofigurationSalesCheckout"/> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + + <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <click selector="{{AdminProductGridSection.selectRowBasedOnName(_defaultProduct.name)}}" stepKey="selectProductToAddImage"/> + <waitForPageLoad stepKey="waitForProductEditPageLoad"/> + <actionGroup ref="addProductImage" stepKey="addImageForParentProduct"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex1"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad1"/> + <click selector="{{AdminProductGridSection.selectRowBasedOnName(colorProductAttribute1.name)}}" stepKey="selectProductToAddImage1"/> + <waitForPageLoad stepKey="waitForProductEditPageLoad1"/> + <actionGroup ref="addProductImage" stepKey="addImageForChildProduct"> + <argument name="image" value="TestImageNew"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + + <!--Sign in as customer --> + <amOnPage url="{{StorefrontCustomerSignInPage.url}}" stepKey="amOnSignInPage"/> + <fillField userInput="$$customer.email$$" selector="{{StorefrontCustomerSignInFormSection.emailField}}" stepKey="fillEmail"/> + <fillField userInput="$$customer.password$$" selector="{{StorefrontCustomerSignInFormSection.passwordField}}" stepKey="fillPassword"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="waitForButton"/> + <click selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="clickSignInAccountButton"/> + <see userInput="$$customer.firstname$$" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}" stepKey="seeFirstName"/> + <see userInput="$$customer.lastname$$" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}" stepKey="seeLastName"/> + <see userInput="$$customer.email$$" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}" stepKey="seeEmail"/> + <waitForPageLoad stepKey="waitForLogin"/> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait3"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <selectOption userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="waitForAddToCartVisible"/> + <click selector="{{StorefrontProductPageSection.addToWishlist}}" stepKey="addFirstProductToWishlist"/> + <waitForElement selector="{{StorefrontCustomerWishlistSection.successMsg}}" time="30" stepKey="addProductToWishlistWaitForSuccessMessage"/> + <waitForPageLoad stepKey="waitForPageToLoad2"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByImageName(TestImageNew.filename)}}" stepKey="AssertWishlistProductImage" /> + <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductImageByImageName(TestImageNew.filename)}}" stepKey="AssertWishlistSidebarProductImage" /> + </test> +</tests> From e670708c0c092524c3a16740313733c71e73fc4b Mon Sep 17 00:00:00 2001 From: Devagouda <dpatil@magento.com> Date: Mon, 9 Jul 2018 17:59:16 -0500 Subject: [PATCH 0191/1171] MAGETWO-8709:[GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 -Updated the group value of the functional test --- ...ConfigurableProductChildImageShouldBeShownOnWishListTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml index 2f795339e06b8..e8ed46c679c0b 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml @@ -11,7 +11,7 @@ <annotations> <features value="Wishlist"/> <stories value="MAGETWO-8709"/> - <group value="wishlist12"/> + <group value="wishlist"/> <title value="When user add Configurable child product to WIshlist then child product image should be shown in Wishlist"/> <description value="When user add Configurable child product to WIshlist then child product image should be shown in Wishlist"/> <severity value="MAJOR"/> From 01d5e3ee41d59429f59d1a5a2bc67cb8fff07bc6 Mon Sep 17 00:00:00 2001 From: John Stennett <john00ivy@gmail.com> Date: Mon, 9 Jul 2018 18:33:24 -0500 Subject: [PATCH 0192/1171] MQE-1121: MSI MFTF Test Cases 4 Updates - Replacing waitForElement with waitForPageLoad. - Correcting invalid parameterized selectors. - Adding action group argument type. --- .../Mftf/ActionGroup/StorefrontProductCartActionGroup.xml | 6 +++--- .../Test/Mftf/Section/AdminShipmentItemsSection.xml | 8 ++++---- .../ActionGroup/AdminDataGridPaginationActionGroup.xml | 2 +- .../Test/Mftf/Section/AdminDataGridPaginationSection.xml | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml index 52846152eacd1..48697d43ec824 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -16,12 +16,12 @@ </arguments> <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct" /> <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="clickAddToCart" /> - <waitForElementVisible selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="waitForBundleProductPageLoad"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> - <waitForElementVisible selector="{{StorefrontBundleProductActionSection.quantityField}}" stepKey="waitForQuantityVisible"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> <fillField selector="{{StorefrontBundleProductActionSection.quantityField}}" userInput="{{quantity}}" stepKey="fillBundleProductQuantity"/> <click selector="{{StorefrontBundleProductActionSection.addToCartButton}}" stepKey="clickAddBundleProductToCart"/> - <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart(product.name)}}" time="30" stepKey="assertMessage"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad3"/> <waitForText userInput="{{quantity}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml index 7c936a7420db9..30f508beb57ab 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml @@ -9,10 +9,10 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminShipmentItemsSection"> - <element name="itemName" type="text" selector=".order-shipment-table tbody:nth-of-type({{row}}) .col-product .product-title" parameterized="true"/> - <element name="itemSku" type="text" selector=".order-shipment-table tbody:nth-of-type({{row}}) .col-product .product-sku-block" parameterized="true"/> - <element name="itemQty" type="text" selector=".order-shipment-table tbody:nth-of-type({{row}}) .col-ordered-qty .qty-table" parameterized="true"/> - <element name="itemQtyToShip" type="input" selector=".order-shipment-table tbody:nth-of-type({{row}}) .col-qty input.qty-item"/> + <element name="itemName" type="text" selector=".order-shipment-table tbody:nth-of-type({{var1}}) .col-product .product-title" parameterized="true"/> + <element name="itemSku" type="text" selector=".order-shipment-table tbody:nth-of-type({{var1}}) .col-product .product-sku-block" parameterized="true"/> + <element name="itemQty" type="text" selector=".order-shipment-table tbody:nth-of-type({{var1}}) .col-ordered-qty .qty-table" parameterized="true"/> + <element name="itemQtyToShip" type="input" selector=".order-shipment-table tbody:nth-of-type({{var1}}) .col-qty input.qty-item" parameterized="true"/> <element name="nameColumn" type="text" selector=".order-shipment-table .col-product .product-title"/> <element name="skuColumn" type="text" selector=".order-shipment-table .col-product .product-sku-block"/> </section> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml index d05a7898872a0..9239f296aafad 100644 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> <actionGroup name="adminDataGridSelectPerPage"> <arguments> - <argument name="perPage"/> + <argument name="perPage" type="string"/> </arguments> <click selector="{{AdminDataGridPaginationSection.perPageDropdown}}" stepKey="clickPerPageDropdown"/> <click selector="{{AdminDataGridPaginationSection.perPageOption(perPage)}}" stepKey="selectCustomPerPage"/> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml index 5e4fc5298bba8..ff4097aa76265 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminDataGridPaginationSection"> <element name="perPageDropdown" type="select" selector=".admin__data-grid-pager-wrap .selectmenu"/> - <element name="perPageOption" type="button" selector="//div[@class='admin__data-grid-pager-wrap']//div[@class='selectmenu-items _active']//li//button[text()='{{label}}']" parameterized="true"/> + <element name="perPageOption" type="button" selector="//div[@class='admin__data-grid-pager-wrap']//div[@class='selectmenu-items _active']//li//button[text()='{{var1}}']" parameterized="true"/> <element name="perPageInput" type="input" selector="//div[@class='admin__data-grid-pager-wrap']//div[@class='selectmenu-items _active']//li//div[@class='selectmenu-item-edit']//input"/> <element name="perPageApplyInput" type="button" selector="//div[@class='admin__data-grid-pager-wrap']//div[@class='selectmenu-items _active']//li//div[@class='selectmenu-item-edit']//button"/> <element name="nextPage" type="button" selector="div.admin__data-grid-pager > button.action-next" timeout="30"/> From 5a54f5aae2d137b1d437fec849ed02e327b9d1eb Mon Sep 17 00:00:00 2001 From: John Stennett <john00ivy@gmail.com> Date: Mon, 9 Jul 2018 20:57:24 -0500 Subject: [PATCH 0193/1171] MQE-1267: MSI MFTF Test Cases 4 - Adding waitForPageLoad to correct a timing issue. - Adding missing website xml. --- .../ActionGroup/AdminOrderActionGroup.xml | 1 + .../Store/Test/Mftf/Metadata/website-meta.xml | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 app/code/Magento/Store/Test/Mftf/Metadata/website-meta.xml diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index 99a3f8efeb157..7ce10d5e5424e 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -129,6 +129,7 @@ <argument name="quantity" defaultValue="1" type="string"/> </arguments> <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilterDownloadable"/> <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearchDownloadable"/> <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/website-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/website-meta.xml new file mode 100644 index 0000000000000..4e314396ab046 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Metadata/website-meta.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateWebsite" dataType="website" type="create" + auth="adminFormKey" url="/admin/system_store/save" method="POST" successRegex="/messages-message-success/" returnRegex=""> + <object dataType="website" key="website"> + <field key="website_id">string</field> + <field key="name">string</field> + <field key="code">string</field> + <field key="sort_order">integer</field> + </object> + <field key="store_action">string</field> + <field key="store_type">string</field> + </operation> +</operations> From e88de64197f18759f489b6a2c99949b8d43a6424 Mon Sep 17 00:00:00 2001 From: Yuriy Tkachenko <y.tkachenko@atwix.com> Date: Tue, 10 Jul 2018 10:09:37 +0300 Subject: [PATCH 0194/1171] Handle only type errors for ObjectManager/Factory/AbstractFactory --- .../ObjectManager/ObjectManagerTest.php | 17 ++++++----------- .../TestAsset/ConstructorWithThrowable.php | 16 ---------------- .../TestAsset/ConstructorWithTypeError.php | 16 ++++++++++++++++ .../ObjectManager/Factory/AbstractFactory.php | 4 ++-- 4 files changed, 24 insertions(+), 29 deletions(-) delete mode 100644 dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithTypeError.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php index 4471d798cae21..c48f8a5dd108b 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php @@ -5,14 +5,12 @@ */ namespace Magento\Framework\ObjectManager; -use Magento\Framework\Exception\RuntimeException; - class ObjectManagerTest extends \PHPUnit\Framework\TestCase { /**#@+ * Test class with throwable error */ - const TEST_CLASS_WITH_THROWABLE = \Magento\Framework\ObjectManager\TestAsset\ConstructorWithThrowable::class; + const TEST_CLASS_WITH_TYPE_ERROR = \Magento\Framework\ObjectManager\TestAsset\ConstructorWithTypeError::class; /**#@+ * Test classes for basic instantiation @@ -147,15 +145,12 @@ public function testNewInstance($actualClassName, array $properties = [], $expec } /** - * Test create instance with throwable error + * Test create instance with TypeError + * @expectedException \TypeError */ - public function testNewInstanceWithThrowableError() + public function testNewInstanceWithTypeError() { - try { - $testObject = self::$_objectManager->create(self::TEST_CLASS_WITH_THROWABLE); - $this->fail('No instance for class with throwable error should be created'); - } catch (\Throwable $e) { - $this->assertInstanceOf(RuntimeException::class, $e); - } + self::$_objectManager->create(self::TEST_CLASS_WITH_TYPE_ERROR); + $this->fail('No instance for class with throwable error should be created'); } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php deleted file mode 100644 index f41a52410591f..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithThrowable.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Framework\ObjectManager\TestAsset; - -class ConstructorWithThrowable extends \Magento\Framework\ObjectManager\TestAsset\ConstructorOneArgument -{ - public function __construct(Basic $one) - { - // Call parent constructor without parameters to generate error - parent::__construct(); - } -} \ No newline at end of file diff --git a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithTypeError.php b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithTypeError.php new file mode 100644 index 0000000000000..87ad22ba8a24a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithTypeError.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\ObjectManager\TestAsset; + +class ConstructorWithTypeError +{ + public function __construct() + { + // set non-exists property to trigger TypeError + throw new \TypeError('test error'); + } +} diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php index 88f1f29e9b3ab..f2e31d5c5973a 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php @@ -114,7 +114,7 @@ protected function createObject($type, $args) { try { return new $type(...array_values($args)); - } catch (\Throwable $e) { + } catch (\TypeError $e) { /** @var LoggerInterface $logger */ $logger = ObjectManager::getInstance()->get(LoggerInterface::class); $logger->critical( @@ -122,7 +122,7 @@ protected function createObject($type, $args) ); throw new RuntimeException( - new Phrase('Create object error') + new Phrase('Create object error: '.$e->getMessage()) ); } } From 7be432799218c6589026d9e7fc6142c728a84f1b Mon Sep 17 00:00:00 2001 From: Yuriy Tkachenko <y.tkachenko@atwix.com> Date: Tue, 10 Jul 2018 10:12:12 +0300 Subject: [PATCH 0195/1171] Code cleanup --- .../Magento/Framework/ObjectManager/ObjectManagerTest.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php index c48f8a5dd108b..bb88d79dbe9d0 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php @@ -5,10 +5,12 @@ */ namespace Magento\Framework\ObjectManager; +use TypeError; + class ObjectManagerTest extends \PHPUnit\Framework\TestCase { /**#@+ - * Test class with throwable error + * Test class with type error */ const TEST_CLASS_WITH_TYPE_ERROR = \Magento\Framework\ObjectManager\TestAsset\ConstructorWithTypeError::class; @@ -146,7 +148,8 @@ public function testNewInstance($actualClassName, array $properties = [], $expec /** * Test create instance with TypeError - * @expectedException \TypeError + * + * @expectedException TypeError */ public function testNewInstanceWithTypeError() { From 24240324272ac0a16ecc4a22b4ef1c6a55e775ea Mon Sep 17 00:00:00 2001 From: Yuriy Tkachenko <y.tkachenko@atwix.com> Date: Tue, 10 Jul 2018 10:16:41 +0300 Subject: [PATCH 0196/1171] Code cleanup --- .../Magento/Framework/ObjectManager/ObjectManagerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php index bb88d79dbe9d0..20a412cd9c692 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php @@ -154,6 +154,6 @@ public function testNewInstance($actualClassName, array $properties = [], $expec public function testNewInstanceWithTypeError() { self::$_objectManager->create(self::TEST_CLASS_WITH_TYPE_ERROR); - $this->fail('No instance for class with throwable error should be created'); + $this->fail('No instance for class with TypeError should be created'); } } From 7544c3d909ac83df0106b80285009ec5b481fe4d Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Thu, 5 Jul 2018 11:49:49 +0530 Subject: [PATCH 0197/1171] Corrected function comment --- .../src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php index 7759eb51a52ad..d78e259ec06e0 100644 --- a/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php +++ b/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php @@ -117,7 +117,7 @@ private function extract(\RecursiveIteratorIterator $recursiveIterator) /** * @param array $classNames * @param string $fileItemPath - * @return bool Whether the clas is included or not + * @return bool Whether the class is included or not */ private function includeClasses(array $classNames, $fileItemPath) { From 09947d4effa50262fc7e5485671648768ec684e0 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Tue, 10 Jul 2018 16:07:02 +0300 Subject: [PATCH 0198/1171] magento/magento2#?: 'Allowed Countries' - get countries for scope 'default'. - fix 'if' for scope with id 0. --- app/code/Magento/Directory/Model/AllowedCountries.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Directory/Model/AllowedCountries.php b/app/code/Magento/Directory/Model/AllowedCountries.php index 46fa04ba22651..db83e4e8920c5 100644 --- a/app/code/Magento/Directory/Model/AllowedCountries.php +++ b/app/code/Magento/Directory/Model/AllowedCountries.php @@ -55,7 +55,7 @@ public function getAllowedCountries( $scope = ScopeInterface::SCOPE_WEBSITE, $scopeCode = null ) { - if (empty($scopeCode)) { + if ($scopeCode === null) { $scopeCode = $this->getDefaultScopeCode($scope); } From 579ab5e9c1cae60602a907c895ba7829a8395e8a Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Tue, 10 Jul 2018 16:52:54 +0300 Subject: [PATCH 0199/1171] magento/magento2#?: 'Allowed Countries' - get countries for scope 'default'. - add tests --- .../Test/Unit/Model/AllowedCountriesTest.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/code/Magento/Directory/Test/Unit/Model/AllowedCountriesTest.php b/app/code/Magento/Directory/Test/Unit/Model/AllowedCountriesTest.php index 5931e5a839341..97a49aa50dc10 100644 --- a/app/code/Magento/Directory/Test/Unit/Model/AllowedCountriesTest.php +++ b/app/code/Magento/Directory/Test/Unit/Model/AllowedCountriesTest.php @@ -70,4 +70,20 @@ public function testGetAllowedCountries() $this->allowedCountriesReader->getAllowedCountries(ScopeInterface::SCOPE_WEBSITE, true) ); } + + public function testGetAllowedCountriesDefaultScope() + { + $this->storeManagerMock->expects($this->never()) + ->method('getStore'); + + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with(AllowedCountries::ALLOWED_COUNTRIES_PATH, ScopeInterface::SCOPE_STORE, 0) + ->willReturn('AM'); + + $this->assertEquals( + ['AM' => 'AM'], + $this->allowedCountriesReader->getAllowedCountries(ScopeInterface::SCOPE_STORE, 0) + ); + } } From c5ac65cb5fe07ebaa6767e32fce37fe7a997a9f9 Mon Sep 17 00:00:00 2001 From: John Stennett <john00ivy@gmail.com> Date: Tue, 10 Jul 2018 10:05:59 -0500 Subject: [PATCH 0200/1171] MQE-1121: MSI MFTF Test Cases 4 Updates - Removing section files that were in the wrong module directory. File moved to the MSI repo. - Removing _suite file that was in the wrong module directory. File moved to the MSI repo. - Updating XSD URIs. --- .../Section/AdminAdvancedInventorySection.xml | 20 ------------- .../Section/ProductStockOptionsSection.xml | 16 ----------- .../Mftf}/Section/AdminMessagesSection.xml | 2 +- .../acceptance/tests/_suite/msi-suite.xml | 28 ------------------- 4 files changed, 1 insertion(+), 65 deletions(-) delete mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminAdvancedInventorySection.xml delete mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/Section/ProductStockOptionsSection.xml rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui => app/code/Magento/Ui/Test/Mftf}/Section/AdminMessagesSection.xml (69%) delete mode 100644 dev/tests/acceptance/tests/_suite/msi-suite.xml diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminAdvancedInventorySection.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminAdvancedInventorySection.xml deleted file mode 100644 index 0a51d778a02f8..0000000000000 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminAdvancedInventorySection.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="AdminAdvancedInventoryControlsSection"> - <element name="done" type="button" selector=".page-main-actions button.action-primary" timeout="10"/> - </section> - <section name="AdminAdvancedInventorySection"> - <element name="manageStock" type="select" selector="select[name='product[stock_data][manage_stock]']"/> - <element name="manageStockUseDefault" type="checkbox" selector="input[name='product[stock_data][use_config_manage_stock]']"/> - <element name="outOfStockThreshold" type="text" selector="input[name='product[stock_data][min_qty]']"/> - <element name="outOfStockThresholdUseDefault" type="checkbox" selector="input[name='product[stock_data][use_config_min_qty]']"/> - </section> -</sections> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Section/ProductStockOptionsSection.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Section/ProductStockOptionsSection.xml deleted file mode 100644 index 958d774a76418..0000000000000 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Section/ProductStockOptionsSection.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="ProductStockOptionsSection"> - <element name="CheckIfTabExpand" type="button" selector="#cataloginventory_item_options-head:not(.open)"/> - <element name="ProductStockOptions" type="button" selector="#cataloginventory_item_options-head"/> - <element name="OutOfStockThresholdSystemValue" type="button" selector="#cataloginventory_item_options_min_qty_inherit"/> - <element name="OutOfStockThresholdValue" type="input" selector="#cataloginventory_item_options_min_qty"/> - <element name="Save" type="button" selector="#save"/> - </section> -</sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminMessagesSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml similarity index 69% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminMessagesSection.xml rename to app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml index a727104f48014..d3e94eb24dfd2 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Ui/Section/AdminMessagesSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminMessagesSection"> <element name="successMessage" type="text" selector=".message-success"/> <element name="errorMessage" type="text" selector=".message.message-error.error"/> diff --git a/dev/tests/acceptance/tests/_suite/msi-suite.xml b/dev/tests/acceptance/tests/_suite/msi-suite.xml deleted file mode 100644 index b96451d58871c..0000000000000 --- a/dev/tests/acceptance/tests/_suite/msi-suite.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> - <suite name="MSI_Single_Mode"> - <include> - <group name="single_mode"/> - </include> - <exclude> - <group name="skip"/> - <group name="multi_mode"/> - </exclude> - </suite> - <suite name="MSI_Multi_Mode"> - <include> - <group name="multi_mode"/> - </include> - <exclude> - <group name="skip"/> - <group name="single_mode"/> - </exclude> - </suite> -</suites> From 1b82046dd485d8598b1294a12fea588997f5a9b8 Mon Sep 17 00:00:00 2001 From: Devagouda <dpatil@magento.com> Date: Tue, 10 Jul 2018 12:07:20 -0500 Subject: [PATCH 0201/1171] MAGETWO-8709:[GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 -incorporated review comments of CR-MAGETWO-40576 --- ...ConfigurableProductChildImageShouldBeShownOnWishListTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml index e8ed46c679c0b..981ee51954fb4 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml @@ -6,7 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> <test name="ConfigurableProductChildImageShouldBeShownOnWishListTest"> <annotations> <features value="Wishlist"/> From 98d600dca98a6b374f2543be4114156ba8178959 Mon Sep 17 00:00:00 2001 From: Alessandro Ronchi <aleron75@gmail.com> Date: Tue, 10 Jul 2018 19:16:52 +0200 Subject: [PATCH 0202/1171] ControllerAclTest.php Change regex in getControllerPath() to avoid classes which are under "Controller" namespace (like for example controller plugins) being interpreted as controllers causing the Act test to fail. --- .../Test/Integrity/Magento/Backend/ControllerAclTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/ControllerAclTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/ControllerAclTest.php index 187cb9087013e..f204019988b79 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/ControllerAclTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/ControllerAclTest.php @@ -232,7 +232,7 @@ private function isItTest($relativeFilePath) */ private function getControllerPath($relativeFilePath) { - if (preg_match('~(Magento\/.*Controller\/Adminhtml\/.*)\.php~', $relativeFilePath, $matches)) { + if (preg_match('~(Magento\/[^\/]+\/Controller\/Adminhtml\/.*)\.php~', $relativeFilePath, $matches)) { if (count($matches) === 2) { $partPath = $matches[1]; return $partPath; From a6a0d5b0c0ff76f59e4c2b138841863c0dc0654b Mon Sep 17 00:00:00 2001 From: Prince Patel <mail.mageprince@gmail.com> Date: Tue, 10 Jul 2018 23:34:04 +0530 Subject: [PATCH 0203/1171] Declare module namespace --- .../Paypal/Block/Adminhtml/Billing/Agreement/View/Form.php | 2 +- .../Paypal/Block/Adminhtml/Billing/Agreement/View/Tab/Info.php | 2 +- .../Block/Adminhtml/System/Config/Payflowlink/Advanced.php | 2 +- .../Paypal/Block/Adminhtml/System/Config/Payflowlink/Info.php | 2 +- app/code/Magento/Paypal/Block/Hosted/Pro/Form.php | 2 +- app/code/Magento/Paypal/Block/Iframe.php | 2 +- app/code/Magento/Paypal/Block/Payflow/Advanced/Form.php | 2 +- app/code/Magento/Paypal/Block/Payflow/Link/Form.php | 2 +- app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php | 2 +- .../Sales/Block/Adminhtml/Order/Create/Totals/Grandtotal.php | 2 +- .../Sales/Block/Adminhtml/Order/Create/Totals/Shipping.php | 2 +- .../Sales/Block/Adminhtml/Order/Create/Totals/Subtotal.php | 2 +- .../Magento/Sales/Block/Adminhtml/Order/Create/Totals/Tax.php | 2 +- app/code/Magento/Sales/Block/Adminhtml/Order/Details.php | 2 +- app/code/Magento/Sales/Block/Adminhtml/Order/View/Form.php | 2 +- .../Magento/Sales/Block/Adminhtml/Order/View/Tab/History.php | 2 +- app/code/Magento/Sales/Block/Adminhtml/Rss/Order/Grid/Link.php | 2 +- app/code/Magento/Sales/Block/Order/Info/Buttons.php | 2 +- app/code/Magento/Sales/Block/Order/Info/Buttons/Rss.php | 2 +- app/code/Magento/Sales/Block/Order/Invoice.php | 2 +- app/code/Magento/Sales/Block/Order/View.php | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Paypal/Block/Adminhtml/Billing/Agreement/View/Form.php b/app/code/Magento/Paypal/Block/Adminhtml/Billing/Agreement/View/Form.php index 31b3e0c1d6b6f..396c66d4e748e 100644 --- a/app/code/Magento/Paypal/Block/Adminhtml/Billing/Agreement/View/Form.php +++ b/app/code/Magento/Paypal/Block/Adminhtml/Billing/Agreement/View/Form.php @@ -13,5 +13,5 @@ class Form extends \Magento\Backend\Block\Template /** * @var string */ - protected $_template = 'billing/agreement/view/form.phtml'; + protected $_template = 'Magento_Paypal::billing/agreement/view/form.phtml'; } diff --git a/app/code/Magento/Paypal/Block/Adminhtml/Billing/Agreement/View/Tab/Info.php b/app/code/Magento/Paypal/Block/Adminhtml/Billing/Agreement/View/Tab/Info.php index d133d19f9c202..39373017fa09a 100644 --- a/app/code/Magento/Paypal/Block/Adminhtml/Billing/Agreement/View/Tab/Info.php +++ b/app/code/Magento/Paypal/Block/Adminhtml/Billing/Agreement/View/Tab/Info.php @@ -15,7 +15,7 @@ class Info extends \Magento\Backend\Block\Template implements \Magento\Backend\B /** * @var string */ - protected $_template = 'billing/agreement/view/tab/info.phtml'; + protected $_template = 'Magento_Paypal::billing/agreement/view/tab/info.phtml'; /** * Core registry diff --git a/app/code/Magento/Paypal/Block/Adminhtml/System/Config/Payflowlink/Advanced.php b/app/code/Magento/Paypal/Block/Adminhtml/System/Config/Payflowlink/Advanced.php index fe4f6ee1f6757..793fad152bc7e 100644 --- a/app/code/Magento/Paypal/Block/Adminhtml/System/Config/Payflowlink/Advanced.php +++ b/app/code/Magento/Paypal/Block/Adminhtml/System/Config/Payflowlink/Advanced.php @@ -16,5 +16,5 @@ class Advanced extends \Magento\Paypal\Block\Adminhtml\System\Config\Payflowlink * * @var string */ - protected $_template = 'system/config/payflowlink/advanced.phtml'; + protected $_template = 'Magento_Paypal::system/config/payflowlink/advanced.phtml'; } diff --git a/app/code/Magento/Paypal/Block/Adminhtml/System/Config/Payflowlink/Info.php b/app/code/Magento/Paypal/Block/Adminhtml/System/Config/Payflowlink/Info.php index 30119063f5b5b..405de1ec03185 100644 --- a/app/code/Magento/Paypal/Block/Adminhtml/System/Config/Payflowlink/Info.php +++ b/app/code/Magento/Paypal/Block/Adminhtml/System/Config/Payflowlink/Info.php @@ -16,7 +16,7 @@ class Info extends \Magento\Config\Block\System\Config\Form\Field * * @var string */ - protected $_template = 'system/config/payflowlink/info.phtml'; + protected $_template = 'Magento_Paypal::system/config/payflowlink/info.phtml'; /** * Render fieldset html diff --git a/app/code/Magento/Paypal/Block/Hosted/Pro/Form.php b/app/code/Magento/Paypal/Block/Hosted/Pro/Form.php index 92281d3058eef..70eff8f83ba99 100644 --- a/app/code/Magento/Paypal/Block/Hosted/Pro/Form.php +++ b/app/code/Magento/Paypal/Block/Hosted/Pro/Form.php @@ -15,5 +15,5 @@ class Form extends \Magento\Payment\Block\Form /** * @var string */ - protected $_template = 'hss/info.phtml'; + protected $_template = 'Magento_Paypal::hss/info.phtml'; } diff --git a/app/code/Magento/Paypal/Block/Iframe.php b/app/code/Magento/Paypal/Block/Iframe.php index d6edb1ed25231..98fc05d0d2f60 100644 --- a/app/code/Magento/Paypal/Block/Iframe.php +++ b/app/code/Magento/Paypal/Block/Iframe.php @@ -44,7 +44,7 @@ class Iframe extends \Magento\Payment\Block\Form /** * @var string */ - protected $_template = 'hss/js.phtml'; + protected $_template = 'Magento_Paypal::hss/js.phtml'; /** * @var \Magento\Sales\Model\OrderFactory diff --git a/app/code/Magento/Paypal/Block/Payflow/Advanced/Form.php b/app/code/Magento/Paypal/Block/Payflow/Advanced/Form.php index d777279ad47a8..159abd4e2f1bb 100644 --- a/app/code/Magento/Paypal/Block/Payflow/Advanced/Form.php +++ b/app/code/Magento/Paypal/Block/Payflow/Advanced/Form.php @@ -15,7 +15,7 @@ class Form extends \Magento\Paypal\Block\Payflow\Link\Form /** * @var string */ - protected $_template = 'payflowadvanced/info.phtml'; + protected $_template = 'Magento_Paypal::payflowadvanced/info.phtml'; /** * Get frame action URL diff --git a/app/code/Magento/Paypal/Block/Payflow/Link/Form.php b/app/code/Magento/Paypal/Block/Payflow/Link/Form.php index f414a63d69046..fc880f494c859 100644 --- a/app/code/Magento/Paypal/Block/Payflow/Link/Form.php +++ b/app/code/Magento/Paypal/Block/Payflow/Link/Form.php @@ -15,7 +15,7 @@ class Form extends \Magento\Payment\Block\Form /** * @var string */ - protected $_template = 'payflowlink/info.phtml'; + protected $_template = 'Magento_Paypal::payflowlink/info.phtml'; /** * Get frame action URL diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php index f2b454260dc22..6cab109b44dbb 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php @@ -19,7 +19,7 @@ class Form extends \Magento\Sales\Block\Adminhtml\Order\Create\Form\Address * * @var string */ - protected $_template = 'order/address/form.phtml'; + protected $_template = 'Magento_Sales::order/address/form.phtml'; /** * Core registry diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Grandtotal.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Grandtotal.php index eb437915ad668..cf9f8a44dee27 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Grandtotal.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Grandtotal.php @@ -20,7 +20,7 @@ class Grandtotal extends \Magento\Sales\Block\Adminhtml\Order\Create\Totals\Defa * * @var string */ - protected $_template = 'order/create/totals/grandtotal.phtml'; + protected $_template = 'Magento_Sales::order/create/totals/grandtotal.phtml'; /** * Tax config diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Shipping.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Shipping.php index 9225d8c2e5f68..34a9ed8070e26 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Shipping.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Shipping.php @@ -20,7 +20,7 @@ class Shipping extends \Magento\Sales\Block\Adminhtml\Order\Create\Totals\Defaul * * @var string */ - protected $_template = 'order/create/totals/shipping.phtml'; + protected $_template = 'Magento_Sales::order/create/totals/shipping.phtml'; /** * Tax config diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Subtotal.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Subtotal.php index cfdd73de9d8b8..166f3c9637ebb 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Subtotal.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Subtotal.php @@ -20,7 +20,7 @@ class Subtotal extends \Magento\Sales\Block\Adminhtml\Order\Create\Totals\Defaul * * @var string */ - protected $_template = 'order/create/totals/subtotal.phtml'; + protected $_template = 'Magento_Sales::order/create/totals/subtotal.phtml'; /** * Tax config diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Tax.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Tax.php index d3da37c3f1bf8..207a4eca60213 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Tax.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Tax.php @@ -18,5 +18,5 @@ class Tax extends \Magento\Sales\Block\Adminhtml\Order\Create\Totals\DefaultTota * * @var string */ - protected $_template = 'order/create/totals/tax.phtml'; + protected $_template = 'Magento_Sales::order/create/totals/tax.phtml'; } diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Details.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Details.php index 5c3a7fce805cc..261f4b0cfd12a 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Details.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Details.php @@ -14,5 +14,5 @@ class Details extends \Magento\Framework\View\Element\Template /** * @var string */ - protected $_template = 'order/details.phtml'; + protected $_template = 'Magento_Sales::order/details.phtml'; } diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/View/Form.php b/app/code/Magento/Sales/Block/Adminhtml/Order/View/Form.php index 82c3effcab62d..6c06e9d624c81 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/View/Form.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/View/Form.php @@ -17,5 +17,5 @@ class Form extends \Magento\Backend\Block\Template * * @var string */ - protected $_template = 'order/view/form.phtml'; + protected $_template = 'Magento_Sales::order/view/form.phtml'; } diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/View/Tab/History.php b/app/code/Magento/Sales/Block/Adminhtml/Order/View/Tab/History.php index 5489a0b2e513f..64b53d10d4af6 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/View/Tab/History.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/View/Tab/History.php @@ -18,7 +18,7 @@ class History extends \Magento\Backend\Block\Template implements \Magento\Backen * * @var string */ - protected $_template = 'order/view/tab/history.phtml'; + protected $_template = 'Magento_Sales::order/view/tab/history.phtml'; /** * Core registry diff --git a/app/code/Magento/Sales/Block/Adminhtml/Rss/Order/Grid/Link.php b/app/code/Magento/Sales/Block/Adminhtml/Rss/Order/Grid/Link.php index fbb78970a4de0..512539824da20 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Rss/Order/Grid/Link.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Rss/Order/Grid/Link.php @@ -14,7 +14,7 @@ class Link extends \Magento\Framework\View\Element\Template /** * @var string */ - protected $_template = 'rss/order/grid/link.phtml'; + protected $_template = 'Magento_Sales::rss/order/grid/link.phtml'; /** * @var \Magento\Framework\App\Rss\UrlBuilderInterface diff --git a/app/code/Magento/Sales/Block/Order/Info/Buttons.php b/app/code/Magento/Sales/Block/Order/Info/Buttons.php index a27b55cd8543f..18e79f6a76ecf 100644 --- a/app/code/Magento/Sales/Block/Order/Info/Buttons.php +++ b/app/code/Magento/Sales/Block/Order/Info/Buttons.php @@ -20,7 +20,7 @@ class Buttons extends \Magento\Framework\View\Element\Template /** * @var string */ - protected $_template = 'order/info/buttons.phtml'; + protected $_template = 'Magento_Sales::order/info/buttons.phtml'; /** * Core registry diff --git a/app/code/Magento/Sales/Block/Order/Info/Buttons/Rss.php b/app/code/Magento/Sales/Block/Order/Info/Buttons/Rss.php index 77e20eaa8d07b..2b84b8f1444b6 100644 --- a/app/code/Magento/Sales/Block/Order/Info/Buttons/Rss.php +++ b/app/code/Magento/Sales/Block/Order/Info/Buttons/Rss.php @@ -16,7 +16,7 @@ class Rss extends \Magento\Framework\View\Element\Template /** * @var string */ - protected $_template = 'order/info/buttons/rss.phtml'; + protected $_template = 'Magento_Sales::order/info/buttons/rss.phtml'; /** * @var \Magento\Sales\Model\OrderFactory diff --git a/app/code/Magento/Sales/Block/Order/Invoice.php b/app/code/Magento/Sales/Block/Order/Invoice.php index 2d8448ea5bc98..24ddf4bac7696 100644 --- a/app/code/Magento/Sales/Block/Order/Invoice.php +++ b/app/code/Magento/Sales/Block/Order/Invoice.php @@ -18,7 +18,7 @@ class Invoice extends \Magento\Sales\Block\Order\Invoice\Items /** * @var string */ - protected $_template = 'order/invoice.phtml'; + protected $_template = 'Magento_Sales::order/invoice.phtml'; /** * @var \Magento\Framework\App\Http\Context diff --git a/app/code/Magento/Sales/Block/Order/View.php b/app/code/Magento/Sales/Block/Order/View.php index 870e2e15ab7b3..03d1340e0f690 100644 --- a/app/code/Magento/Sales/Block/Order/View.php +++ b/app/code/Magento/Sales/Block/Order/View.php @@ -18,7 +18,7 @@ class View extends \Magento\Framework\View\Element\Template /** * @var string */ - protected $_template = 'order/view.phtml'; + protected $_template = 'Magento_Sales::order/view.phtml'; /** * Core registry From e31a8c328f7b6db1d2f49cb5379528cd1c382b86 Mon Sep 17 00:00:00 2001 From: Prince Patel <mail.mageprince@gmail.com> Date: Tue, 10 Jul 2018 23:36:07 +0530 Subject: [PATCH 0204/1171] Declare module namespace --- app/code/Magento/Newsletter/Block/Adminhtml/Problem.php | 2 +- app/code/Magento/Newsletter/Block/Adminhtml/Queue/Edit.php | 2 +- app/code/Magento/Newsletter/Block/Adminhtml/Subscriber.php | 2 +- app/code/Magento/Newsletter/Block/Adminhtml/Template.php | 2 +- app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php | 2 +- app/code/Magento/Tax/Block/Adminhtml/Rate/Title.php | 2 +- app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Add.php | 2 +- app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Save.php | 2 +- app/code/Magento/Tax/Block/Checkout/Grandtotal.php | 2 +- app/code/Magento/Tax/Block/Checkout/Shipping.php | 2 +- app/code/Magento/Tax/Block/Checkout/Subtotal.php | 2 +- app/code/Magento/Tax/Block/Checkout/Tax.php | 2 +- .../TaxImportExport/Block/Adminhtml/Rate/ImportExport.php | 2 +- .../TaxImportExport/Block/Adminhtml/Rate/ImportExportHeader.php | 2 +- .../Theme/Block/Adminhtml/Wysiwyg/Files/Content/Uploader.php | 2 +- app/code/Magento/Theme/Block/Html/Breadcrumbs.php | 2 +- app/code/Magento/Theme/Block/Html/Header.php | 2 +- app/code/Magento/Theme/Block/Html/Header/Logo.php | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Newsletter/Block/Adminhtml/Problem.php b/app/code/Magento/Newsletter/Block/Adminhtml/Problem.php index c5f4dc68d4dd2..61a17d7ad5e51 100644 --- a/app/code/Magento/Newsletter/Block/Adminhtml/Problem.php +++ b/app/code/Magento/Newsletter/Block/Adminhtml/Problem.php @@ -19,7 +19,7 @@ class Problem extends \Magento\Backend\Block\Template /** * @var string */ - protected $_template = 'problem/list.phtml'; + protected $_template = 'Magento_Newsletter::problem/list.phtml'; /** * @var \Magento\Newsletter\Model\ResourceModel\Problem\Collection diff --git a/app/code/Magento/Newsletter/Block/Adminhtml/Queue/Edit.php b/app/code/Magento/Newsletter/Block/Adminhtml/Queue/Edit.php index f085b0f6c9c8b..ca90b5d84a10f 100644 --- a/app/code/Magento/Newsletter/Block/Adminhtml/Queue/Edit.php +++ b/app/code/Magento/Newsletter/Block/Adminhtml/Queue/Edit.php @@ -19,7 +19,7 @@ class Edit extends \Magento\Backend\Block\Template /** * @var string */ - protected $_template = 'queue/edit.phtml'; + protected $_template = 'Magento_Newsletter::queue/edit.phtml'; /** * Core registry diff --git a/app/code/Magento/Newsletter/Block/Adminhtml/Subscriber.php b/app/code/Magento/Newsletter/Block/Adminhtml/Subscriber.php index 86e7e7ee4756d..4d5165db68736 100644 --- a/app/code/Magento/Newsletter/Block/Adminhtml/Subscriber.php +++ b/app/code/Magento/Newsletter/Block/Adminhtml/Subscriber.php @@ -29,7 +29,7 @@ class Subscriber extends \Magento\Backend\Block\Template /** * @var string */ - protected $_template = 'subscriber/list.phtml'; + protected $_template = 'Magento_Newsletter::subscriber/list.phtml'; /** * @var \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory diff --git a/app/code/Magento/Newsletter/Block/Adminhtml/Template.php b/app/code/Magento/Newsletter/Block/Adminhtml/Template.php index 02b60e049f254..92ae6e6c3db04 100644 --- a/app/code/Magento/Newsletter/Block/Adminhtml/Template.php +++ b/app/code/Magento/Newsletter/Block/Adminhtml/Template.php @@ -16,7 +16,7 @@ class Template extends \Magento\Backend\Block\Template /** * @var string */ - protected $_template = 'template/list.phtml'; + protected $_template = 'Magento_Newsletter::template/list.phtml'; /** * @return $this diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php index 96fc9a40d53d1..450203486f364 100644 --- a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php +++ b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php @@ -31,7 +31,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic /** * @var string */ - protected $_template = 'rate/form.phtml'; + protected $_template = 'Magento_Tax::rate/form.phtml'; /** * Tax data diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rate/Title.php b/app/code/Magento/Tax/Block/Adminhtml/Rate/Title.php index e1e866c06571b..9612b57f8d5d8 100644 --- a/app/code/Magento/Tax/Block/Adminhtml/Rate/Title.php +++ b/app/code/Magento/Tax/Block/Adminhtml/Rate/Title.php @@ -23,7 +23,7 @@ class Title extends \Magento\Framework\View\Element\Template /** * @var string */ - protected $_template = 'rate/title.phtml'; + protected $_template = 'Magento_Tax::rate/title.phtml'; /** * @var \Magento\Store\Model\StoreFactory diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Add.php b/app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Add.php index 9cf96bc21e962..16d828542c5b9 100644 --- a/app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Add.php +++ b/app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Add.php @@ -20,7 +20,7 @@ class Add extends \Magento\Backend\Block\Template implements \Magento\Backend\Bl /** * @var string */ - protected $_template = 'toolbar/rate/add.phtml'; + protected $_template = 'Magento_Tax::toolbar/rate/add.phtml'; /** * @var \Magento\Backend\Block\Widget\Button\ButtonList diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Save.php b/app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Save.php index 19c5fab72ac4b..4eaaa3be8a8f2 100644 --- a/app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Save.php +++ b/app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Save.php @@ -16,7 +16,7 @@ class Save extends \Magento\Backend\Block\Template implements \Magento\Backend\B /** * @var string */ - protected $_template = 'toolbar/rate/save.phtml'; + protected $_template = 'Magento_Tax::toolbar/rate/save.phtml'; /** * @var \Magento\Backend\Block\Widget\Button\ButtonList diff --git a/app/code/Magento/Tax/Block/Checkout/Grandtotal.php b/app/code/Magento/Tax/Block/Checkout/Grandtotal.php index 68de4cb24a487..77af1ad99ea2c 100644 --- a/app/code/Magento/Tax/Block/Checkout/Grandtotal.php +++ b/app/code/Magento/Tax/Block/Checkout/Grandtotal.php @@ -15,7 +15,7 @@ class Grandtotal extends \Magento\Checkout\Block\Total\DefaultTotal * * @var string */ - protected $_template = 'checkout/grandtotal.phtml'; + protected $_template = 'Magento_Tax::checkout/grandtotal.phtml'; /** * @var \Magento\Tax\Model\Config diff --git a/app/code/Magento/Tax/Block/Checkout/Shipping.php b/app/code/Magento/Tax/Block/Checkout/Shipping.php index e9098035053be..299c586fd224c 100644 --- a/app/code/Magento/Tax/Block/Checkout/Shipping.php +++ b/app/code/Magento/Tax/Block/Checkout/Shipping.php @@ -15,7 +15,7 @@ class Shipping extends \Magento\Checkout\Block\Total\DefaultTotal * * @var string */ - protected $_template = 'checkout/shipping.phtml'; + protected $_template = 'Magento_Tax::checkout/shipping.phtml'; /** * @var \Magento\Tax\Model\Config diff --git a/app/code/Magento/Tax/Block/Checkout/Subtotal.php b/app/code/Magento/Tax/Block/Checkout/Subtotal.php index 7a9059df08bab..22da07954159d 100644 --- a/app/code/Magento/Tax/Block/Checkout/Subtotal.php +++ b/app/code/Magento/Tax/Block/Checkout/Subtotal.php @@ -15,7 +15,7 @@ class Subtotal extends \Magento\Checkout\Block\Total\DefaultTotal * * @var string */ - protected $_template = 'checkout/subtotal.phtml'; + protected $_template = 'Magento_Tax::checkout/subtotal.phtml'; /** * @var \Magento\Tax\Model\Config diff --git a/app/code/Magento/Tax/Block/Checkout/Tax.php b/app/code/Magento/Tax/Block/Checkout/Tax.php index f741e64019de1..0a86c0312ab1c 100644 --- a/app/code/Magento/Tax/Block/Checkout/Tax.php +++ b/app/code/Magento/Tax/Block/Checkout/Tax.php @@ -14,5 +14,5 @@ class Tax extends \Magento\Checkout\Block\Total\DefaultTotal /** * @var string */ - protected $_template = 'checkout/tax.phtml'; + protected $_template = 'Magento_Tax::checkout/tax.phtml'; } diff --git a/app/code/Magento/TaxImportExport/Block/Adminhtml/Rate/ImportExport.php b/app/code/Magento/TaxImportExport/Block/Adminhtml/Rate/ImportExport.php index a42877b3ecf8a..ab64567f4fe28 100644 --- a/app/code/Magento/TaxImportExport/Block/Adminhtml/Rate/ImportExport.php +++ b/app/code/Magento/TaxImportExport/Block/Adminhtml/Rate/ImportExport.php @@ -14,7 +14,7 @@ class ImportExport extends \Magento\Backend\Block\Widget /** * @var string */ - protected $_template = 'importExport.phtml'; + protected $_template = 'Magento_TaxImportExport::importExport.phtml'; /** * @param \Magento\Backend\Block\Template\Context $context diff --git a/app/code/Magento/TaxImportExport/Block/Adminhtml/Rate/ImportExportHeader.php b/app/code/Magento/TaxImportExport/Block/Adminhtml/Rate/ImportExportHeader.php index 8897e9b2083e9..e223adc3adb1a 100644 --- a/app/code/Magento/TaxImportExport/Block/Adminhtml/Rate/ImportExportHeader.php +++ b/app/code/Magento/TaxImportExport/Block/Adminhtml/Rate/ImportExportHeader.php @@ -16,5 +16,5 @@ class ImportExportHeader extends \Magento\Backend\Block\Widget * * @var string */ - protected $_template = 'importExportHeader.phtml'; + protected $_template = 'Magento_TaxImportExport::importExportHeader.phtml'; } diff --git a/app/code/Magento/Theme/Block/Adminhtml/Wysiwyg/Files/Content/Uploader.php b/app/code/Magento/Theme/Block/Adminhtml/Wysiwyg/Files/Content/Uploader.php index 8e7f4c9cc680c..e99500dbd0694 100644 --- a/app/code/Magento/Theme/Block/Adminhtml/Wysiwyg/Files/Content/Uploader.php +++ b/app/code/Magento/Theme/Block/Adminhtml/Wysiwyg/Files/Content/Uploader.php @@ -19,7 +19,7 @@ class Uploader extends \Magento\Backend\Block\Media\Uploader * * @var string */ - protected $_template = 'browser/content/uploader.phtml'; + protected $_template = 'Magento_Theme::browser/content/uploader.phtml'; /** * @var \Magento\Theme\Helper\Storage diff --git a/app/code/Magento/Theme/Block/Html/Breadcrumbs.php b/app/code/Magento/Theme/Block/Html/Breadcrumbs.php index c1f8ea620ef41..cff87fc8726bd 100644 --- a/app/code/Magento/Theme/Block/Html/Breadcrumbs.php +++ b/app/code/Magento/Theme/Block/Html/Breadcrumbs.php @@ -21,7 +21,7 @@ class Breadcrumbs extends \Magento\Framework\View\Element\Template * * @var string */ - protected $_template = 'html/breadcrumbs.phtml'; + protected $_template = 'Magento_Theme::html/breadcrumbs.phtml'; /** * List of available breadcrumb properties diff --git a/app/code/Magento/Theme/Block/Html/Header.php b/app/code/Magento/Theme/Block/Html/Header.php index f597b4034da92..2663a4da15011 100644 --- a/app/code/Magento/Theme/Block/Html/Header.php +++ b/app/code/Magento/Theme/Block/Html/Header.php @@ -19,7 +19,7 @@ class Header extends \Magento\Framework\View\Element\Template * * @var string */ - protected $_template = 'html/header.phtml'; + protected $_template = 'Magento_Theme::html/header.phtml'; /** * Retrieve welcome text diff --git a/app/code/Magento/Theme/Block/Html/Header/Logo.php b/app/code/Magento/Theme/Block/Html/Header/Logo.php index 5b0c2eaf04c45..0a0e71f44ba32 100644 --- a/app/code/Magento/Theme/Block/Html/Header/Logo.php +++ b/app/code/Magento/Theme/Block/Html/Header/Logo.php @@ -19,7 +19,7 @@ class Logo extends \Magento\Framework\View\Element\Template * * @var string */ - protected $_template = 'html/header/logo.phtml'; + protected $_template = 'Magento_Theme::html/header/logo.phtml'; /** * @var \Magento\MediaStorage\Helper\File\Storage\Database From b798f31e23dece815a9d88342a2486b321e1b0b6 Mon Sep 17 00:00:00 2001 From: Prince Patel <mail.mageprince@gmail.com> Date: Tue, 10 Jul 2018 23:37:52 +0530 Subject: [PATCH 0205/1171] Declare module namespace --- app/code/Magento/AdvancedSearch/Block/SearchData.php | 2 +- .../Adminhtml/Product/Attribute/Set/Main/Tree/Attribute.php | 2 +- app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Js.php | 2 +- app/code/Magento/OfflinePayments/Block/Form/Banktransfer.php | 2 +- app/code/Magento/OfflinePayments/Block/Form/Cashondelivery.php | 2 +- app/code/Magento/Payment/Block/Info/Instructions.php | 2 +- app/code/Magento/ProductAlert/Block/Email/Price.php | 2 +- app/code/Magento/ProductAlert/Block/Email/Stock.php | 2 +- app/code/Magento/Rss/Block/Feeds.php | 2 +- .../Magento/Shipping/Block/Adminhtml/Order/Packaging/Grid.php | 2 +- app/code/Magento/Shipping/Block/Order/Shipment.php | 2 +- app/code/Magento/Signifyd/Block/Fingerprint.php | 2 +- app/code/Magento/UrlRewrite/Block/Catalog/Category/Tree.php | 2 +- app/code/Magento/UrlRewrite/Block/Selector.php | 2 +- app/code/Magento/User/Block/Role/Tab/Edit.php | 2 +- app/code/Magento/Weee/Block/Renderer/Weee/Tax.php | 2 +- .../Block/Adminhtml/Widget/Instance/Edit/Tab/Main/Layout.php | 2 +- app/code/Magento/Wishlist/Block/Rss/EmailLink.php | 2 +- app/code/Magento/Wishlist/Block/Share/Email/Items.php | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/AdvancedSearch/Block/SearchData.php b/app/code/Magento/AdvancedSearch/Block/SearchData.php index 993731b465257..105a1c1c4fc46 100644 --- a/app/code/Magento/AdvancedSearch/Block/SearchData.php +++ b/app/code/Magento/AdvancedSearch/Block/SearchData.php @@ -30,7 +30,7 @@ abstract class SearchData extends Template implements SearchDataInterface /** * @var string */ - protected $_template = 'search_data.phtml'; + protected $_template = 'Magento_AdvancedSearch::search_data.phtml'; /** * @param Template\Context $context diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Set/Main/Tree/Attribute.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Set/Main/Tree/Attribute.php index f5e3f94418687..cb0a739b56e4e 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Set/Main/Tree/Attribute.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Set/Main/Tree/Attribute.php @@ -14,5 +14,5 @@ class Attribute extends \Magento\Backend\Block\Template /** * @var string */ - protected $_template = 'catalog/product/attribute/set/main/tree/attribute.phtml'; + protected $_template = 'Magento_Catalog::catalog/product/attribute/set/main/tree/attribute.phtml'; } diff --git a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Js.php b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Js.php index 8ac79a0aa0d49..7dd6b0a19ec02 100644 --- a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Js.php +++ b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Js.php @@ -18,7 +18,7 @@ class Js extends \Magento\Backend\Block\Template * @var string */ - protected $_template = 'attribute/edit/js.phtml'; + protected $_template = 'Magento_Eav::attribute/edit/js.phtml'; /** * @var \Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype diff --git a/app/code/Magento/OfflinePayments/Block/Form/Banktransfer.php b/app/code/Magento/OfflinePayments/Block/Form/Banktransfer.php index c4fe10c386645..d60348d9dc1c7 100644 --- a/app/code/Magento/OfflinePayments/Block/Form/Banktransfer.php +++ b/app/code/Magento/OfflinePayments/Block/Form/Banktransfer.php @@ -15,5 +15,5 @@ class Banktransfer extends \Magento\OfflinePayments\Block\Form\AbstractInstructi * * @var string */ - protected $_template = 'form/banktransfer.phtml'; + protected $_template = 'Magento_OfflinePayments::form/banktransfer.phtml'; } diff --git a/app/code/Magento/OfflinePayments/Block/Form/Cashondelivery.php b/app/code/Magento/OfflinePayments/Block/Form/Cashondelivery.php index 4e0f7d48ce09b..de0f7a57bae62 100644 --- a/app/code/Magento/OfflinePayments/Block/Form/Cashondelivery.php +++ b/app/code/Magento/OfflinePayments/Block/Form/Cashondelivery.php @@ -15,5 +15,5 @@ class Cashondelivery extends \Magento\OfflinePayments\Block\Form\AbstractInstruc * * @var string */ - protected $_template = 'form/cashondelivery.phtml'; + protected $_template = 'Magento_OfflinePayments::form/cashondelivery.phtml'; } diff --git a/app/code/Magento/Payment/Block/Info/Instructions.php b/app/code/Magento/Payment/Block/Info/Instructions.php index e3c74e020f8f6..687c6b54a2f4f 100644 --- a/app/code/Magento/Payment/Block/Info/Instructions.php +++ b/app/code/Magento/Payment/Block/Info/Instructions.php @@ -23,7 +23,7 @@ class Instructions extends \Magento\Payment\Block\Info /** * @var string */ - protected $_template = 'info/instructions.phtml'; + protected $_template = 'Magento_Payment::info/instructions.phtml'; /** * Get instructions text from order payment diff --git a/app/code/Magento/ProductAlert/Block/Email/Price.php b/app/code/Magento/ProductAlert/Block/Email/Price.php index 982b0f7f63375..0430a21dc8bfd 100644 --- a/app/code/Magento/ProductAlert/Block/Email/Price.php +++ b/app/code/Magento/ProductAlert/Block/Email/Price.php @@ -15,7 +15,7 @@ class Price extends \Magento\ProductAlert\Block\Email\AbstractEmail /** * @var string */ - protected $_template = 'email/price.phtml'; + protected $_template = 'Magento_ProductAlert::email/price.phtml'; /** * Retrieve unsubscribe url for product diff --git a/app/code/Magento/ProductAlert/Block/Email/Stock.php b/app/code/Magento/ProductAlert/Block/Email/Stock.php index f424e7d7125c4..d01960b8eb855 100644 --- a/app/code/Magento/ProductAlert/Block/Email/Stock.php +++ b/app/code/Magento/ProductAlert/Block/Email/Stock.php @@ -15,7 +15,7 @@ class Stock extends \Magento\ProductAlert\Block\Email\AbstractEmail /** * @var string */ - protected $_template = 'email/stock.phtml'; + protected $_template = 'Magento_ProductAlert::email/stock.phtml'; /** * Retrieve unsubscribe url for product diff --git a/app/code/Magento/Rss/Block/Feeds.php b/app/code/Magento/Rss/Block/Feeds.php index 2e88d25c02891..86998f87f5c17 100644 --- a/app/code/Magento/Rss/Block/Feeds.php +++ b/app/code/Magento/Rss/Block/Feeds.php @@ -16,7 +16,7 @@ class Feeds extends \Magento\Framework\View\Element\Template /** * @var string */ - protected $_template = 'feeds.phtml'; + protected $_template = 'Magento_Rss::feeds.phtml'; /** * @var \Magento\Framework\App\Rss\RssManagerInterface diff --git a/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging/Grid.php b/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging/Grid.php index 9e340cc31ff17..1d3f6ad1ee5a3 100644 --- a/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging/Grid.php +++ b/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging/Grid.php @@ -10,7 +10,7 @@ class Grid extends \Magento\Backend\Block\Template /** * @var string */ - protected $_template = 'order/packaging/grid.phtml'; + protected $_template = 'Magento_Shipping::order/packaging/grid.phtml'; /** * Core registry diff --git a/app/code/Magento/Shipping/Block/Order/Shipment.php b/app/code/Magento/Shipping/Block/Order/Shipment.php index 653fb357f0b1d..21e960985d6b6 100644 --- a/app/code/Magento/Shipping/Block/Order/Shipment.php +++ b/app/code/Magento/Shipping/Block/Order/Shipment.php @@ -18,7 +18,7 @@ class Shipment extends \Magento\Framework\View\Element\Template /** * @var string */ - protected $_template = 'order/shipment.phtml'; + protected $_template = 'Magento_Shipping::order/shipment.phtml'; /** * Core registry diff --git a/app/code/Magento/Signifyd/Block/Fingerprint.php b/app/code/Magento/Signifyd/Block/Fingerprint.php index db76fc6c94468..f43bffce1fc1a 100644 --- a/app/code/Magento/Signifyd/Block/Fingerprint.php +++ b/app/code/Magento/Signifyd/Block/Fingerprint.php @@ -42,7 +42,7 @@ class Fingerprint extends Template * @var string * @since 100.2.0 */ - protected $_template = 'fingerprint.phtml'; + protected $_template = 'Magento_Signifyd::fingerprint.phtml'; /** * @param Context $context diff --git a/app/code/Magento/UrlRewrite/Block/Catalog/Category/Tree.php b/app/code/Magento/UrlRewrite/Block/Catalog/Category/Tree.php index 64a775b01593b..e34d4773c271b 100644 --- a/app/code/Magento/UrlRewrite/Block/Catalog/Category/Tree.php +++ b/app/code/Magento/UrlRewrite/Block/Catalog/Category/Tree.php @@ -27,7 +27,7 @@ class Tree extends \Magento\Catalog\Block\Adminhtml\Category\AbstractCategory /** * @var string */ - protected $_template = 'categories.phtml'; + protected $_template = 'Magento_UrlRewrite::categories.phtml'; /** * Adminhtml data diff --git a/app/code/Magento/UrlRewrite/Block/Selector.php b/app/code/Magento/UrlRewrite/Block/Selector.php index 0a28ba215de5a..75266fd2f977c 100644 --- a/app/code/Magento/UrlRewrite/Block/Selector.php +++ b/app/code/Magento/UrlRewrite/Block/Selector.php @@ -18,7 +18,7 @@ class Selector extends \Magento\Backend\Block\Template /** * @var string */ - protected $_template = 'selector.phtml'; + protected $_template = 'Magento_UrlRewrite::selector.phtml'; /** * Set block template and get available modes diff --git a/app/code/Magento/User/Block/Role/Tab/Edit.php b/app/code/Magento/User/Block/Role/Tab/Edit.php index 45d725c61bd52..5fe6a1b2a2e88 100644 --- a/app/code/Magento/User/Block/Role/Tab/Edit.php +++ b/app/code/Magento/User/Block/Role/Tab/Edit.php @@ -19,7 +19,7 @@ class Edit extends \Magento\Backend\Block\Widget\Form implements \Magento\Backen /** * @var string */ - protected $_template = 'role/edit.phtml'; + protected $_template = 'Magento_User::role/edit.phtml'; /** * Root ACL Resource diff --git a/app/code/Magento/Weee/Block/Renderer/Weee/Tax.php b/app/code/Magento/Weee/Block/Renderer/Weee/Tax.php index ab6710fddcac2..94a6faf72aa96 100644 --- a/app/code/Magento/Weee/Block/Renderer/Weee/Tax.php +++ b/app/code/Magento/Weee/Block/Renderer/Weee/Tax.php @@ -32,7 +32,7 @@ class Tax extends \Magento\Backend\Block\Widget implements /** * @var string */ - protected $_template = 'renderer/tax.phtml'; + protected $_template = 'Magento_Weee::renderer/tax.phtml'; /** * Core registry diff --git a/app/code/Magento/Widget/Block/Adminhtml/Widget/Instance/Edit/Tab/Main/Layout.php b/app/code/Magento/Widget/Block/Adminhtml/Widget/Instance/Edit/Tab/Main/Layout.php index 49345f29afd53..c48bf9e7e4c7a 100644 --- a/app/code/Magento/Widget/Block/Adminhtml/Widget/Instance/Edit/Tab/Main/Layout.php +++ b/app/code/Magento/Widget/Block/Adminhtml/Widget/Instance/Edit/Tab/Main/Layout.php @@ -27,7 +27,7 @@ class Layout extends Template implements RendererInterface /** * @var string */ - protected $_template = 'instance/edit/layout.phtml'; + protected $_template = 'Magento_Widget::instance/edit/layout.phtml'; /** * @var \Magento\Catalog\Model\Product\Type diff --git a/app/code/Magento/Wishlist/Block/Rss/EmailLink.php b/app/code/Magento/Wishlist/Block/Rss/EmailLink.php index 4a5f116cd8293..907dfd90e752e 100644 --- a/app/code/Magento/Wishlist/Block/Rss/EmailLink.php +++ b/app/code/Magento/Wishlist/Block/Rss/EmailLink.php @@ -21,7 +21,7 @@ class EmailLink extends Link /** * @var string */ - protected $_template = 'rss/email.phtml'; + protected $_template = 'Magento_Wishlist::rss/email.phtml'; /** * @return array diff --git a/app/code/Magento/Wishlist/Block/Share/Email/Items.php b/app/code/Magento/Wishlist/Block/Share/Email/Items.php index bc84f6a43df3c..d4e6587fd6519 100644 --- a/app/code/Magento/Wishlist/Block/Share/Email/Items.php +++ b/app/code/Magento/Wishlist/Block/Share/Email/Items.php @@ -20,7 +20,7 @@ class Items extends \Magento\Wishlist\Block\AbstractBlock /** * @var string */ - protected $_template = 'email/items.phtml'; + protected $_template = 'Magento_Wishlist::email/items.phtml'; /** * Retrieve Product View URL From f30b65eae635a3f09364085833c2d6dda207981c Mon Sep 17 00:00:00 2001 From: Prince Patel <mail.mageprince@gmail.com> Date: Wed, 11 Jul 2018 00:01:29 +0530 Subject: [PATCH 0206/1171] Declare module namespace --- app/code/Magento/Reports/Block/Adminhtml/Product/Viewed.php | 2 +- app/code/Magento/Reports/Block/Adminhtml/Sales/Bestsellers.php | 2 +- app/code/Magento/Reports/Block/Adminhtml/Sales/Coupons.php | 2 +- app/code/Magento/Reports/Block/Adminhtml/Sales/Invoiced.php | 2 +- app/code/Magento/Reports/Block/Adminhtml/Sales/Refunded.php | 2 +- app/code/Magento/Reports/Block/Adminhtml/Sales/Sales.php | 2 +- app/code/Magento/Reports/Block/Adminhtml/Sales/Shipping.php | 2 +- app/code/Magento/Reports/Block/Adminhtml/Sales/Tax.php | 2 +- app/code/Magento/Reports/Block/Adminhtml/Wishlist.php | 2 +- .../Magento/Review/Block/Adminhtml/Rating/Edit/Tab/Form.php | 2 +- app/code/Magento/Review/Block/Adminhtml/Rss/Grid/Link.php | 2 +- app/code/Magento/Review/Block/Customer/Recent.php | 2 +- app/code/Magento/Review/Block/Customer/View.php | 2 +- app/code/Magento/Review/Block/Rating/Entity/Detailed.php | 2 +- app/code/Magento/Review/Block/View.php | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Reports/Block/Adminhtml/Product/Viewed.php b/app/code/Magento/Reports/Block/Adminhtml/Product/Viewed.php index fc4cffbdca408..f901b32d8b12f 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Product/Viewed.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Product/Viewed.php @@ -17,7 +17,7 @@ class Viewed extends \Magento\Backend\Block\Widget\Grid\Container /** * @var string */ - protected $_template = 'report/grid/container.phtml'; + protected $_template = 'Magento_Reports::report/grid/container.phtml'; /** * @return void diff --git a/app/code/Magento/Reports/Block/Adminhtml/Sales/Bestsellers.php b/app/code/Magento/Reports/Block/Adminhtml/Sales/Bestsellers.php index d70930d2395ae..b773184408a7f 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Sales/Bestsellers.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Sales/Bestsellers.php @@ -19,7 +19,7 @@ class Bestsellers extends \Magento\Backend\Block\Widget\Grid\Container * * @var string */ - protected $_template = 'report/grid/container.phtml'; + protected $_template = 'Magento_Reports::report/grid/container.phtml'; /** * {@inheritdoc} diff --git a/app/code/Magento/Reports/Block/Adminhtml/Sales/Coupons.php b/app/code/Magento/Reports/Block/Adminhtml/Sales/Coupons.php index b8f71158877bb..fe85af58b34f6 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Sales/Coupons.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Sales/Coupons.php @@ -19,7 +19,7 @@ class Coupons extends \Magento\Backend\Block\Widget\Grid\Container * * @var string */ - protected $_template = 'report/grid/container.phtml'; + protected $_template = 'Magento_Reports::report/grid/container.phtml'; /** * {@inheritdoc} diff --git a/app/code/Magento/Reports/Block/Adminhtml/Sales/Invoiced.php b/app/code/Magento/Reports/Block/Adminhtml/Sales/Invoiced.php index c96483e33ebe5..57594a11bd997 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Sales/Invoiced.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Sales/Invoiced.php @@ -19,7 +19,7 @@ class Invoiced extends \Magento\Backend\Block\Widget\Grid\Container * * @var string */ - protected $_template = 'report/grid/container.phtml'; + protected $_template = 'Magento_Reports::report/grid/container.phtml'; /** * {@inheritdoc} diff --git a/app/code/Magento/Reports/Block/Adminhtml/Sales/Refunded.php b/app/code/Magento/Reports/Block/Adminhtml/Sales/Refunded.php index 7ff80f62f6bee..994b29e6eb0dd 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Sales/Refunded.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Sales/Refunded.php @@ -19,7 +19,7 @@ class Refunded extends \Magento\Backend\Block\Widget\Grid\Container * * @var string */ - protected $_template = 'report/grid/container.phtml'; + protected $_template = 'Magento_Reports::report/grid/container.phtml'; /** * {@inheritdoc} diff --git a/app/code/Magento/Reports/Block/Adminhtml/Sales/Sales.php b/app/code/Magento/Reports/Block/Adminhtml/Sales/Sales.php index 5abea45e657d7..64375ace3e94d 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Sales/Sales.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Sales/Sales.php @@ -19,7 +19,7 @@ class Sales extends \Magento\Backend\Block\Widget\Grid\Container * * @var string */ - protected $_template = 'report/grid/container.phtml'; + protected $_template = 'Magento_Reports::report/grid/container.phtml'; /** * {@inheritdoc} diff --git a/app/code/Magento/Reports/Block/Adminhtml/Sales/Shipping.php b/app/code/Magento/Reports/Block/Adminhtml/Sales/Shipping.php index 44dd4521c7bbe..e4dbdc2737745 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Sales/Shipping.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Sales/Shipping.php @@ -19,7 +19,7 @@ class Shipping extends \Magento\Backend\Block\Widget\Grid\Container * * @var string */ - protected $_template = 'report/grid/container.phtml'; + protected $_template = 'Magento_Reports::report/grid/container.phtml'; /** * {@inheritdoc} diff --git a/app/code/Magento/Reports/Block/Adminhtml/Sales/Tax.php b/app/code/Magento/Reports/Block/Adminhtml/Sales/Tax.php index 38de08314d257..fa9e63745a87d 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Sales/Tax.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Sales/Tax.php @@ -19,7 +19,7 @@ class Tax extends \Magento\Backend\Block\Widget\Grid\Container * * @var string */ - protected $_template = 'report/grid/container.phtml'; + protected $_template = 'Magento_Reports::report/grid/container.phtml'; /** * {@inheritdoc} diff --git a/app/code/Magento/Reports/Block/Adminhtml/Wishlist.php b/app/code/Magento/Reports/Block/Adminhtml/Wishlist.php index 28f2011de3365..1ca76cb1cf95f 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Wishlist.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Wishlist.php @@ -18,7 +18,7 @@ class Wishlist extends \Magento\Backend\Block\Template * * @var string */ - protected $_template = 'report/wishlist.phtml'; + protected $_template = 'Magento_Reports::report/wishlist.phtml'; /** * Reports wishlist collection factory diff --git a/app/code/Magento/Review/Block/Adminhtml/Rating/Edit/Tab/Form.php b/app/code/Magento/Review/Block/Adminhtml/Rating/Edit/Tab/Form.php index 0841388252905..dbf0a79bc42ff 100644 --- a/app/code/Magento/Review/Block/Adminhtml/Rating/Edit/Tab/Form.php +++ b/app/code/Magento/Review/Block/Adminhtml/Rating/Edit/Tab/Form.php @@ -17,7 +17,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic /** * @var string */ - protected $_template = 'rating/form.phtml'; + protected $_template = 'Magento_Review::rating/form.phtml'; /** * Session diff --git a/app/code/Magento/Review/Block/Adminhtml/Rss/Grid/Link.php b/app/code/Magento/Review/Block/Adminhtml/Rss/Grid/Link.php index 5d2ec9fc186ca..def0e896fc95f 100644 --- a/app/code/Magento/Review/Block/Adminhtml/Rss/Grid/Link.php +++ b/app/code/Magento/Review/Block/Adminhtml/Rss/Grid/Link.php @@ -16,7 +16,7 @@ class Link extends \Magento\Framework\View\Element\Template /** * @var string */ - protected $_template = 'rss/grid/link.phtml'; + protected $_template = 'Magento_Review::rss/grid/link.phtml'; /** * @var \Magento\Framework\App\Rss\UrlBuilderInterface diff --git a/app/code/Magento/Review/Block/Customer/Recent.php b/app/code/Magento/Review/Block/Customer/Recent.php index 8f593f5695812..5c7f1ec2c0dad 100644 --- a/app/code/Magento/Review/Block/Customer/Recent.php +++ b/app/code/Magento/Review/Block/Customer/Recent.php @@ -20,7 +20,7 @@ class Recent extends \Magento\Framework\View\Element\Template * * @var string */ - protected $_template = 'customer/list.phtml'; + protected $_template = 'Magento_Review::customer/list.phtml'; /** * Product reviews collection diff --git a/app/code/Magento/Review/Block/Customer/View.php b/app/code/Magento/Review/Block/Customer/View.php index b7dfd4b969a9d..237b972f16573 100644 --- a/app/code/Magento/Review/Block/Customer/View.php +++ b/app/code/Magento/Review/Block/Customer/View.php @@ -23,7 +23,7 @@ class View extends \Magento\Catalog\Block\Product\AbstractProduct * * @var string */ - protected $_template = 'customer/view.phtml'; + protected $_template = 'Magento_Review::customer/view.phtml'; /** * Catalog product model diff --git a/app/code/Magento/Review/Block/Rating/Entity/Detailed.php b/app/code/Magento/Review/Block/Rating/Entity/Detailed.php index de871d9061428..0ce4f436ae704 100644 --- a/app/code/Magento/Review/Block/Rating/Entity/Detailed.php +++ b/app/code/Magento/Review/Block/Rating/Entity/Detailed.php @@ -15,7 +15,7 @@ class Detailed extends \Magento\Framework\View\Element\Template /** * @var string */ - protected $_template = 'detailed.phtml'; + protected $_template = 'Magento_Review::detailed.phtml'; /** * @var \Magento\Review\Model\RatingFactory diff --git a/app/code/Magento/Review/Block/View.php b/app/code/Magento/Review/Block/View.php index e2d0355671688..95b7176b48c44 100644 --- a/app/code/Magento/Review/Block/View.php +++ b/app/code/Magento/Review/Block/View.php @@ -19,7 +19,7 @@ class View extends \Magento\Catalog\Block\Product\AbstractProduct * * @var string */ - protected $_template = 'view.phtml'; + protected $_template = 'Magento_Review::view.phtml'; /** * Rating option model From c37b82826c094216e6b456e2f47cd0c45c890323 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Tue, 10 Jul 2018 13:41:05 -0500 Subject: [PATCH 0207/1171] ENGCOM-2197: [Jenkins] Failure to generate MFTF Tests --- .../DisplayOutOfStockProductActionGroup.xml | 29 ------------------- .../Mftf/Page/InventoryConfigurationPage.xml | 12 -------- .../Section/AdminAdvancedInventorySection.xml | 20 ------------- .../Test/Mftf/Section/InventorySection.xml | 15 ---------- .../Section/ProductStockOptionsSection.xml | 16 ---------- .../AdminProductFormConfigurationsSection.xml | 9 +----- 6 files changed, 1 insertion(+), 100 deletions(-) delete mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml delete mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/Page/InventoryConfigurationPage.xml delete mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminAdvancedInventorySection.xml delete mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/Section/InventorySection.xml delete mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/Section/ProductStockOptionsSection.xml diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml deleted file mode 100644 index 1bec4cc99c0e8..0000000000000 --- a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> - <actionGroup name="displayOutOfStockProduct"> - <amOnPage url="{{InventoryConfigurationPage.url}}" stepKey="navigateToInventoryConfigurationPage"/> - <waitForPageLoad stepKey="waitForConfigPageToLoad"/> - <conditionalClick stepKey="expandProductStockOptions" selector="{{InventoryConfigSection.ProductStockOptionsTab}}" dependentSelector="{{InventoryConfigSection.CheckIfProductStockOptionsTabExpanded}}" visible="true" /> - <waitForElementVisible selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="waitForDisplayOutOfStockOption" /> - <uncheckOption selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="uncheckUseSystemValue"/> - <waitForElementVisible selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" stepKey="waitForSwitcherDropdown" /> - <selectOption selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" userInput="Yes" stepKey="switchToYes" /> - <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> - </actionGroup> - <actionGroup name="noDisplayOutOfStockProduct"> - <amOnPage url="{{InventoryConfigurationPage.url}}" stepKey="navigateToInventoryConfigurationPage"/> - <waitForPageLoad stepKey="waitForConfigPageToLoad"/> - <uncheckOption selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="uncheckUseSystemValue"/> - <waitForElementVisible selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" stepKey="waitForSwitcherDropdown" /> - <selectOption selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" userInput="No" stepKey="switchToNo" /> - <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Page/InventoryConfigurationPage.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Page/InventoryConfigurationPage.xml deleted file mode 100644 index 95e873a3b164d..0000000000000 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Page/InventoryConfigurationPage.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> - <page name="InventoryConfigurationPage" url="admin/system_config/edit/section/cataloginventory/" area="admin" module="Magento_Config"> - <section name="InventorySection"/> - </page> -</pages> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminAdvancedInventorySection.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminAdvancedInventorySection.xml deleted file mode 100644 index 0a51d778a02f8..0000000000000 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminAdvancedInventorySection.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="AdminAdvancedInventoryControlsSection"> - <element name="done" type="button" selector=".page-main-actions button.action-primary" timeout="10"/> - </section> - <section name="AdminAdvancedInventorySection"> - <element name="manageStock" type="select" selector="select[name='product[stock_data][manage_stock]']"/> - <element name="manageStockUseDefault" type="checkbox" selector="input[name='product[stock_data][use_config_manage_stock]']"/> - <element name="outOfStockThreshold" type="text" selector="input[name='product[stock_data][min_qty]']"/> - <element name="outOfStockThresholdUseDefault" type="checkbox" selector="input[name='product[stock_data][use_config_min_qty]']"/> - </section> -</sections> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Section/InventorySection.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Section/InventorySection.xml deleted file mode 100644 index 55fbc84ead96a..0000000000000 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Section/InventorySection.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="InventoryConfigSection"> - <element name="ProductStockOptionsTab" type="button" selector="#cataloginventory_options-head"/> - <element name="CheckIfProductStockOptionsTabExpanded" type="button" selector="#cataloginventory_options-head:not(.open)"/> - <element name="DisplayOutOfStockSystemValue" type="checkbox" selector="#cataloginventory_options_show_out_of_stock_inherit"/> - <element name="DisplayOutOfStockDropdown" type="select" selector="#cataloginventory_options_show_out_of_stock"/> - </section> -</sections> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Section/ProductStockOptionsSection.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Section/ProductStockOptionsSection.xml deleted file mode 100644 index 958d774a76418..0000000000000 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Section/ProductStockOptionsSection.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="ProductStockOptionsSection"> - <element name="CheckIfTabExpand" type="button" selector="#cataloginventory_item_options-head:not(.open)"/> - <element name="ProductStockOptions" type="button" selector="#cataloginventory_item_options-head"/> - <element name="OutOfStockThresholdSystemValue" type="button" selector="#cataloginventory_item_options_min_qty_inherit"/> - <element name="OutOfStockThresholdValue" type="input" selector="#cataloginventory_item_options_min_qty"/> - <element name="Save" type="button" selector="#save"/> - </section> -</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml index 2080908cb2d96..0079086948410 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml @@ -25,19 +25,12 @@ <section name="AdminConfigurableProductFormSection"> <element name="productWeight" type="input" selector=".admin__control-text[name='product[weight]']"/> <element name="productQuantity" type="input" selector=".admin__control-text[name='product[quantity_and_stock_status][qty]']"/> - <element name="currentVariationsQuantityCells" type="button" selector="td[data-index='quantity_per_source_container']"/> + <element name="currentVariationsQuantityCells" type="button" selector="td[data-index='quantity_container']"/> <element name="rowByCode" type="textarea" selector="//span[contains(text(), '{{var1}}-{{var2}}')]//ancestor-or-self::tr" parameterized="true"/> </section> <section name="AdminConfigurableProductSelectAttributesSlideOut"> <element name="grid" type="button" selector=".admin__data-grid-wrap tbody"/> </section> - <section name="AdminConfigurableProductAssignSourcesSlideOut"> - <element name="done" type="button" selector=".product_form_product_form_assign_sources_configurable_modal .action-primary" timeout="5"/> - <element name="assignSources" type="button" selector="(//button/span[contains(text(), 'Assign Sources')])[2]" timeout="5"/> - <element name="quantityPerSource" type="input" selector="input[name='quantity_resolver[dynamicRows][dynamicRows][{{var1}}][quantity_per_source]']" parameterized="true"/> - <element name="quantityPerSourceForSingleMode" type="input" selector="#apply-single-inventory-input"/> - <element name="quantityPerSourceForMultiMode" type="input" selector="input[name='quantity_resolver[dynamicRows][dynamicRows][0][quantity_per_source]']"/> - </section> <section name="StorefrontConfigurableProductPage"> <element name="productAttributeDropDown" type="select" selector="select[id*='attribute']"/> </section> From f83f9bc4b1527061c82ac3e21b9f9aa931f224b9 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Tue, 10 Jul 2018 14:00:40 -0500 Subject: [PATCH 0208/1171] MC-3065: Automate MFTF for MC-1364 --- .../Test/Mftf/Section/StorefrontProductInfoDetailsSection.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoDetailsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoDetailsSection.xml index 40f49fc2cc77b..5688811cb96a6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoDetailsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoDetailsSection.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoDetailsSection"> <element name="productNameForReview" type="text" selector=".legend.review-legend>strong" /> + <element name="detailsTab" type="button" selector="#tab-label-description-title" /> </section> </sections> From 14e89c27eeb237e3c821d75a67336c08d546efc7 Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Tue, 10 Jul 2018 14:35:57 -0500 Subject: [PATCH 0209/1171] MAGETWO-91465: Once integer is stored for State/Province field, it can not be changed to alphanumeric --- .../Customer/view/frontend/templates/address/edit.phtml | 2 +- .../Magento/Ui/view/base/web/js/lib/validation/rules.js | 2 +- lib/web/mage/validation.js | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml b/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml index 1f1f078504524..6a129a3aa4b44 100644 --- a/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml @@ -112,7 +112,7 @@ name="region" value="<?= $block->escapeHtmlAttr($block->getRegion()) ?>" title="<?= $block->escapeHtmlAttr(__('State/Province')) ?>" - class="input-text <?= $block->escapeHtmlAttr($this->helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('region')) ?>"<?= !$block->getConfig('general/region/display_all') ? ' disabled="disabled"' : '' ?>/> + class="input-text validate-not-number-first <?= $block->escapeHtmlAttr($this->helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('region')) ?>"<?= !$block->getConfig('general/region/display_all') ? ' disabled="disabled"' : '' ?>/> </div> </div> <div class="field zip required"> diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 7aa9ead484b74..675c7214edc53 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -759,7 +759,7 @@ define([ function (value) { return utils.isEmptyNoTrim(value) || /^[^0-9-\.].*$/.test(value); }, - $.mage.__('First character must be letter.')//eslint-disable-line max-len + $.mage.__('First character must be letter.') ], 'validate-date': [ function (value, params, additionalParams) { diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index 6258b3c627370..ba06c112d5103 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -990,6 +990,12 @@ }, $.mage.__('Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.') //eslint-disable-line max-len ], + 'validate-not-number-first': [ + function (value) { + return $.mage.isEmptyNoTrim(value) || /^[^0-9-\.].*$/.test(value); + }, + $.mage.__('First character must be letter.') + ], 'validate-date': [ function (value, params, additionalParams) { var test = moment(value, additionalParams.dateFormat); From e6eef422290d263782cc2cac362df49df9486e01 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 10 Jul 2018 16:49:01 -0500 Subject: [PATCH 0210/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - fixing unit tests --- .../Checkout/CustomerData/DefaultItem.php | 12 ++--- .../Unit/Block/Cart/Item/RendererTest.php | 48 ++++++++++++------- .../Unit/CustomerData/DefaultItemTest.php | 18 ++++++- .../Unit/Model/Cart/ImageProviderTest.php | 19 ++++++-- .../Wishlist/CustomerData/Wishlist.php | 21 ++++---- .../Test/Unit/CustomerData/WishlistTest.php | 30 +++++++++--- 6 files changed, 97 insertions(+), 51 deletions(-) diff --git a/app/code/Magento/Checkout/CustomerData/DefaultItem.php b/app/code/Magento/Checkout/CustomerData/DefaultItem.php index 2a80843e64ca3..21580d1275d0c 100644 --- a/app/code/Magento/Checkout/CustomerData/DefaultItem.php +++ b/app/code/Magento/Checkout/CustomerData/DefaultItem.php @@ -40,13 +40,13 @@ class DefaultItem extends AbstractItem protected $checkoutHelper; /** - * Escaper - * * @var \Magento\Framework\Escaper */ private $escaper; - /** @var ItemResolverInterface */ + /** + * @var ItemResolverInterface + */ private $itemResolver; /** @@ -82,10 +82,7 @@ public function __construct( */ protected function doGetItemData() { - $imageHelper = $this->imageHelper->init( - $this->itemResolver->getFinalProduct($this->item), - 'mini_cart_product_thumbnail' - ); + $imageHelper = $this->imageHelper->init($this->getProductForThumbnail(), 'mini_cart_product_thumbnail'); $productName = $this->escaper->escapeHtml($this->item->getProduct()->getName()); return [ @@ -125,7 +122,6 @@ protected function getOptionList() /** * @return \Magento\Catalog\Model\Product - * @deprecated * @codeCoverageIgnore */ protected function getProductForThumbnail() diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php index 9c9c5fd33bd07..3155712371faf 100644 --- a/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php @@ -9,6 +9,7 @@ use Magento\Catalog\Model\Product; use Magento\Checkout\Block\Cart\Item\Renderer; use Magento\Quote\Model\Quote\Item; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -18,17 +19,22 @@ class RendererTest extends \PHPUnit\Framework\TestCase /** * @var Renderer */ - protected $_renderer; + private $renderer; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $layout; + private $layout; /** * @var \Magento\Catalog\Block\Product\ImageBuilder|\PHPUnit_Framework_MockObject_MockObject */ - protected $imageBuilder; + private $imageBuilder; + + /** + * @var ItemResolverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $itemResolver; protected function setUp() { @@ -47,11 +53,16 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->_renderer = $objectManagerHelper->getObject( + $this->itemResolver = $this->createMock( + ItemResolverInterface::class + ); + + $this->renderer = $objectManagerHelper->getObject( \Magento\Checkout\Block\Cart\Item\Renderer::class, [ 'context' => $context, 'imageBuilder' => $this->imageBuilder, + 'itemResolver' => $this->itemResolver, ] ); } @@ -59,7 +70,7 @@ protected function setUp() public function testGetProductForThumbnail() { $product = $this->_initProduct(); - $productForThumbnail = $this->_renderer->getProductForThumbnail(); + $productForThumbnail = $this->renderer->getProductForThumbnail(); $this->assertEquals($product->getName(), $productForThumbnail->getName(), 'Invalid product was returned.'); } @@ -81,7 +92,12 @@ protected function _initProduct() $item = $this->createMock(\Magento\Quote\Model\Quote\Item::class); $item->expects($this->any())->method('getProduct')->will($this->returnValue($product)); - $this->_renderer->setItem($item); + $this->itemResolver->expects($this->any()) + ->method('getFinalProduct') + ->with($item) + ->will($this->returnValue($product)); + + $this->renderer->setItem($item); return $product; } @@ -93,12 +109,12 @@ public function testGetIdentities() ->method('getIdentities') ->will($this->returnValue($identities)); - $this->assertEquals($product->getIdentities(), $this->_renderer->getIdentities()); + $this->assertEquals($product->getIdentities(), $this->renderer->getIdentities()); } public function testGetIdentitiesFromEmptyItem() { - $this->assertEmpty($this->_renderer->getIdentities()); + $this->assertEmpty($this->renderer->getIdentities()); } /** @@ -133,7 +149,7 @@ public function testGetProductPriceHtml() ] )->will($this->returnValue($priceHtml)); - $this->assertEquals($priceHtml, $this->_renderer->getProductPriceHtml($product)); + $this->assertEquals($priceHtml, $this->renderer->getProductPriceHtml($product)); } public function testGetActions() @@ -150,7 +166,7 @@ public function testGetActions() $this->layout->expects($this->once()) ->method('getChildName') - ->with($this->_renderer->getNameInLayout(), 'actions') + ->with($this->renderer->getNameInLayout(), 'actions') ->willReturn($blockNameInLayout); $this->layout->expects($this->once()) ->method('getBlock') @@ -171,14 +187,14 @@ public function testGetActions() ->method('toHtml') ->willReturn($blockHtml); - $this->assertEquals($blockHtml, $this->_renderer->getActions($itemMock)); + $this->assertEquals($blockHtml, $this->renderer->getActions($itemMock)); } public function testGetActionsWithNoBlock() { $this->layout->expects($this->once()) ->method('getChildName') - ->with($this->_renderer->getNameInLayout(), 'actions') + ->with($this->renderer->getNameInLayout(), 'actions') ->willReturn(false); /** @@ -188,7 +204,7 @@ public function testGetActionsWithNoBlock() ->disableOriginalConstructor() ->getMock(); - $this->assertEquals('', $this->_renderer->getActions($itemMock)); + $this->assertEquals('', $this->renderer->getActions($itemMock)); } public function testGetImage() @@ -198,14 +214,14 @@ public function testGetImage() $product = $this->createMock(Product::class); $imageMock = $this->createMock(Image::class); - $this->imageBuilder->expects(self::once()) + $this->imageBuilder->expects($this->once()) ->method('create') ->with($product, $imageId, $attributes) ->willReturn($imageMock); - static::assertInstanceOf( + $this->assertInstanceOf( Image::class, - $this->_renderer->getImage($product, $imageId, $attributes) + $this->renderer->getImage($product, $imageId, $attributes) ); } } diff --git a/app/code/Magento/Checkout/Test/Unit/CustomerData/DefaultItemTest.php b/app/code/Magento/Checkout/Test/Unit/CustomerData/DefaultItemTest.php index 8a7c2e951dd72..9a408f1ecd1c8 100644 --- a/app/code/Magento/Checkout/Test/Unit/CustomerData/DefaultItemTest.php +++ b/app/code/Magento/Checkout/Test/Unit/CustomerData/DefaultItemTest.php @@ -5,12 +5,14 @@ */ namespace Magento\Checkout\Test\Unit\CustomerData; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; + class DefaultItemTest extends \PHPUnit\Framework\TestCase { /** * @var \Magento\Checkout\CustomerData\DefaultItem */ - protected $model; + private $model; /** * @var \Magento\Catalog\Helper\Image @@ -22,6 +24,11 @@ class DefaultItemTest extends \PHPUnit\Framework\TestCase */ private $configurationPool; + /** + * @var ItemResolverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $itemResolver; + protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -35,12 +42,14 @@ protected function setUp() $checkoutHelper = $this->getMockBuilder(\Magento\Checkout\Helper\Data::class) ->setMethods(['formatPrice'])->disableOriginalConstructor()->getMock(); $checkoutHelper->expects($this->any())->method('formatPrice')->willReturn(5); + $this->itemResolver = $this->createMock(ItemResolverInterface::class); $this->model = $objectManager->getObject( \Magento\Checkout\CustomerData\DefaultItem::class, [ 'imageHelper' => $this->imageHelper, 'configurationPool' => $this->configurationPool, - 'checkoutHelper' => $checkoutHelper + 'checkoutHelper' => $checkoutHelper, + 'itemResolver' => $this->itemResolver, ] ); } @@ -73,6 +82,11 @@ public function testGetItemData() $this->imageHelper->expects($this->any())->method('getHeight')->willReturn(100); $this->configurationPool->expects($this->any())->method('getByProductType')->willReturn($product); + $this->itemResolver->expects($this->any()) + ->method('getFinalProduct') + ->with($item) + ->will($this->returnValue($product)); + $itemData = $this->model->getItemData($item); $this->assertArrayHasKey('options', $itemData); $this->assertArrayHasKey('qty', $itemData); diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Cart/ImageProviderTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Cart/ImageProviderTest.php index 5330d93b46f6a..993a01d922c1c 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/Cart/ImageProviderTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/Cart/ImageProviderTest.php @@ -11,25 +11,34 @@ class ImageProviderTest extends \PHPUnit\Framework\TestCase /** * @var \Magento\Checkout\Model\Cart\ImageProvider */ - public $model; + private $model; /** * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Quote\Api\CartItemRepositoryInterface */ - protected $itemRepositoryMock; + private $itemRepositoryMock; /** * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Checkout\CustomerData\ItemPoolInterface */ - protected $itemPoolMock; + private $itemPoolMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Checkout\CustomerData\DefaultItem + */ + private $customerItem; protected function setUp() { $this->itemRepositoryMock = $this->createMock(\Magento\Quote\Api\CartItemRepositoryInterface::class); $this->itemPoolMock = $this->createMock(\Magento\Checkout\CustomerData\ItemPoolInterface::class); + $this->customerItem = $this->getMockBuilder(\Magento\Checkout\CustomerData\DefaultItem::class) + ->disableOriginalConstructor() + ->getMock(); $this->model = new \Magento\Checkout\Model\Cart\ImageProvider( $this->itemRepositoryMock, - $this->itemPoolMock + $this->itemPoolMock, + $this->customerItem ); } @@ -44,7 +53,7 @@ public function testGetImages() $expectedResult = [$itemId => $itemData['product_image']]; $this->itemRepositoryMock->expects($this->once())->method('getList')->with($cartId)->willReturn([$itemMock]); - $this->itemPoolMock->expects($this->once())->method('getItemData')->with($itemMock)->willReturn($itemData); + $this->customerItem->expects($this->once())->method('getItemData')->with($itemMock)->willReturn($itemData); $this->assertEquals($expectedResult, $this->model->getImages($cartId)); } diff --git a/app/code/Magento/Wishlist/CustomerData/Wishlist.php b/app/code/Magento/Wishlist/CustomerData/Wishlist.php index c4cb273078468..e721fbf9a9029 100644 --- a/app/code/Magento/Wishlist/CustomerData/Wishlist.php +++ b/app/code/Magento/Wishlist/CustomerData/Wishlist.php @@ -8,7 +8,6 @@ use Magento\Catalog\Model\Product\Image\NotLoadInfoImageException; use Magento\Customer\CustomerData\SectionSourceInterface; use Magento\Framework\App\ObjectManager; -use Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Image; /** * Wishlist section @@ -41,29 +40,31 @@ class Wishlist implements SectionSourceInterface protected $block; /** - * @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Image + * @var \Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface */ - private $image; + private $itemResolver; /** * @param \Magento\Wishlist\Helper\Data $wishlistHelper * @param \Magento\Wishlist\Block\Customer\Sidebar $block * @param \Magento\Catalog\Helper\ImageFactory $imageHelperFactory * @param \Magento\Framework\App\ViewInterface $view - * @param Image|null $image + * @param \Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface|null $itemResolver */ public function __construct( \Magento\Wishlist\Helper\Data $wishlistHelper, \Magento\Wishlist\Block\Customer\Sidebar $block, \Magento\Catalog\Helper\ImageFactory $imageHelperFactory, \Magento\Framework\App\ViewInterface $view, - Image $image = null + \Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface $itemResolver = null ) { $this->wishlistHelper = $wishlistHelper; $this->imageHelperFactory = $imageHelperFactory; $this->block = $block; $this->view = $view; - $this->image = $image ?? ObjectManager::getInstance()->get(Image::class); + $this->itemResolver = $itemResolver ?? ObjectManager::getInstance()->get( + \Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface::class + ); } /** @@ -131,14 +132,8 @@ protected function getItems() protected function getItemData(\Magento\Wishlist\Model\Item $wishlistItem) { $product = $wishlistItem->getProduct(); - - /** @var \Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface $itemProductResolver */ - $itemProductResolver = ObjectManager::getInstance()->get( - \Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface::class - ); - return [ - 'image' => $this->getImageData($itemProductResolver->getFinalProduct($wishlistItem)), + 'image' => $this->getImageData($this->itemResolver->getFinalProduct($wishlistItem)), 'product_sku' => $product->getSku(), 'product_id' => $product->getId(), 'product_url' => $this->wishlistHelper->getProductUrl($wishlistItem), diff --git a/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php index d2e81b0236ce8..b44134d7f1c9c 100644 --- a/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php @@ -8,7 +8,7 @@ use Magento\Catalog\Helper\Image; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Type\AbstractType; -use Magento\Catalog\Pricing\Price\ConfiguredPriceInterface; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; use Magento\Framework\App\ViewInterface; use Magento\Framework\Pricing\Render; use Magento\Wishlist\Block\Customer\Sidebar; @@ -24,19 +24,22 @@ class WishlistTest extends \PHPUnit\Framework\TestCase { /** @var Wishlist */ - protected $model; + private $model; /** @var Data|\PHPUnit_Framework_MockObject_MockObject */ - protected $wishlistHelperMock; + private $wishlistHelperMock; /** @var Sidebar|\PHPUnit_Framework_MockObject_MockObject */ - protected $sidebarMock; + private $sidebarMock; /** @var Image|\PHPUnit_Framework_MockObject_MockObject */ - protected $catalogImageHelperMock; + private $catalogImageHelperMock; /** @var ViewInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $viewMock; + private $viewMock; + + /** @var \Magento\Catalog\Block\Product\ImageBuilder|\PHPUnit_Framework_MockObject_MockObject */ + private $itemResolver; protected function setUp() { @@ -60,11 +63,16 @@ protected function setUp() ->method('create') ->willReturn($this->catalogImageHelperMock); + $this->itemResolver = $this->createMock( + ItemResolverInterface::class + ); + $this->model = new Wishlist( $this->wishlistHelperMock, $this->sidebarMock, $imageHelperFactory, - $this->viewMock + $this->viewMock, + $this->itemResolver ); } @@ -162,6 +170,10 @@ public function testGetSectionData() ->method('getProduct') ->willReturn($productMock); + $this->itemResolver->expects($this->once()) + ->method('getFinalProduct') + ->willReturn($productMock); + $this->catalogImageHelperMock->expects($this->once()) ->method('init') ->with($productMock, 'wishlist_sidebar_block', []) @@ -359,6 +371,10 @@ public function testGetSectionDataWithTwoItems() ->method('getProduct') ->willReturn($productMock); + $this->itemResolver->expects($this->exactly(2)) + ->method('getFinalProduct') + ->willReturn($productMock); + $this->catalogImageHelperMock->expects($this->exactly(2)) ->method('init') ->with($productMock, 'wishlist_sidebar_block', []) From 0fbc9fdb5a89b9c9a738b8e2b300baafd41b367c Mon Sep 17 00:00:00 2001 From: Dmitriy Kogut <kogut.dmitriy@gmail.com> Date: Wed, 11 Jul 2018 12:28:24 +0300 Subject: [PATCH 0211/1171] MAGETWO-90373: Automate with MFTF product tier price --- .../Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml index fcb60a27e679a..902f51c4a15a7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml @@ -43,6 +43,7 @@ <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct1"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> + <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton1"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton1"/> <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="addCustomerGroupAllGroupsQty1PriceDiscountAnd10percent"/> @@ -72,6 +73,7 @@ <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct2"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> + <scrollToTopOfPage stepKey="scrollToTopOfPage2"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton2"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForcustomerGroupPriceAddButton2"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" time="30" stepKey="waitForSelectCustomerGroupNameAttribute1"/> @@ -96,6 +98,7 @@ <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct3"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> + <scrollToTopOfPage stepKey="scrollToTopOfPage3"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton3"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForcustomerGroupPriceAddButton3"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" stepKey="waitForSelectCustomerGroupNameAttribute2"/> @@ -161,6 +164,7 @@ <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct4"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> + <scrollToTopOfPage stepKey="scrollToTopOfPage4"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton4"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForcustomerGroupPriceAddButton4"/> <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('1')}}" userInput="25" stepKey="selectProductTierPricePercentageValue2"/> @@ -228,12 +232,14 @@ <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct5"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> + <scrollToTopOfPage stepKey="scrollToTopOfPage5"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton5"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceDeleteButton}}" stepKey="waitForcustomerGroupPriceDeleteButton"/> <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceDeleteButton}}" stepKey="deleteFirstRowOfCustomerGroupPrice"/> <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceDeleteButton}}" stepKey="deleteSecondRowOfCustomerGroupPrice"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton5"/> <actionGroup ref="saveProductForm" stepKey="saveProduct5"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage6"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton6"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForcustomerGroupPriceAddButton5"/> <dontSeeElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" stepKey="dontSeeQtyInputOfFirstRow"/> From 20cc5ac8342eee13f0cf45fc0ed170deda8f4ebd Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <bkorablov@magento.com> Date: Wed, 11 Jul 2018 14:48:14 +0300 Subject: [PATCH 0212/1171] MAGETWO-93161: Fix Problems with Consumer Runners on Cloud Clusters --- .../Model/Cron/ConsumersRunner.php | 4 +++- .../Unit/Model/Cron/ConsumersRunnerTest.php | 20 ++++++++++--------- .../consumers_runner_functions_mocks.php | 14 +++++++++++++ 3 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/MessageQueue/Test/Unit/_files/consumers_runner_functions_mocks.php diff --git a/app/code/Magento/MessageQueue/Model/Cron/ConsumersRunner.php b/app/code/Magento/MessageQueue/Model/Cron/ConsumersRunner.php index c4620862b2e10..ceeb4badeefd0 100644 --- a/app/code/Magento/MessageQueue/Model/Cron/ConsumersRunner.php +++ b/app/code/Magento/MessageQueue/Model/Cron/ConsumersRunner.php @@ -139,6 +139,8 @@ private function canBeRun($consumerName, array $allowedConsumers = []) */ private function getPidFilePath($consumerName) { - return $consumerName . static::PID_FILE_EXT; + $sanitizedHostname = preg_replace('/[^a-zA-Z0-9]/i', '', gethostname()); + + return $consumerName . '-' . $sanitizedHostname . static::PID_FILE_EXT; } } diff --git a/app/code/Magento/MessageQueue/Test/Unit/Model/Cron/ConsumersRunnerTest.php b/app/code/Magento/MessageQueue/Test/Unit/Model/Cron/ConsumersRunnerTest.php index 08c8f926522de..bf8796a03f52a 100644 --- a/app/code/Magento/MessageQueue/Test/Unit/Model/Cron/ConsumersRunnerTest.php +++ b/app/code/Magento/MessageQueue/Test/Unit/Model/Cron/ConsumersRunnerTest.php @@ -51,6 +51,8 @@ class ConsumersRunnerTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { + require_once __DIR__ . '/../../_files/consumers_runner_functions_mocks.php'; + $this->phpExecutableFinderMock = $this->getMockBuilder(phpExecutableFinder::class) ->disableOriginalConstructor() ->getMock(); @@ -116,7 +118,7 @@ public function testRun( $isRunExpects ) { $consumerName = 'consumerName'; - $pidFilePath = 'consumerName.pid'; + $pidFilePath = 'consumerName-myHostName.pid'; $this->deploymentConfigMock->expects($this->exactly(3)) ->method('get') @@ -164,7 +166,7 @@ public function runDataProvider() 'isRun' => false, 'php' => '', 'command' => 'php '. BP . '/bin/magento queue:consumers:start %s %s %s', - 'arguments' => ['consumerName', '--pid-file-path=consumerName.pid', '--max-messages=20000'], + 'arguments' => ['consumerName', '--pid-file-path=consumerName-myHostName.pid', '--max-messages=20000'], 'allowedConsumers' => [], 'shellBackgroundExpects' => 1, 'isRunExpects' => 1, @@ -174,7 +176,7 @@ public function runDataProvider() 'isRun' => false, 'php' => '', 'command' => 'php '. BP . '/bin/magento queue:consumers:start %s %s %s', - 'arguments' => ['consumerName', '--pid-file-path=consumerName.pid', '--max-messages=10000'], + 'arguments' => ['consumerName', '--pid-file-path=consumerName-myHostName.pid', '--max-messages=10000'], 'allowedConsumers' => [], 'shellBackgroundExpects' => 1, 'isRunExpects' => 1, @@ -184,7 +186,7 @@ public function runDataProvider() 'isRun' => false, 'php' => '', 'command' => 'php '. BP . '/bin/magento queue:consumers:start %s %s %s', - 'arguments' => ['consumerName', '--pid-file-path=consumerName.pid', '--max-messages=10000'], + 'arguments' => ['consumerName', '--pid-file-path=consumerName-myHostName.pid', '--max-messages=10000'], 'allowedConsumers' => ['someConsumer'], 'shellBackgroundExpects' => 0, 'isRunExpects' => 0, @@ -194,7 +196,7 @@ public function runDataProvider() 'isRun' => true, 'php' => '', 'command' => 'php '. BP . '/bin/magento queue:consumers:start %s %s %s', - 'arguments' => ['consumerName', '--pid-file-path=consumerName.pid', '--max-messages=10000'], + 'arguments' => ['consumerName', '--pid-file-path=consumerName-myHostName.pid', '--max-messages=10000'], 'allowedConsumers' => ['someConsumer'], 'shellBackgroundExpects' => 0, 'isRunExpects' => 0, @@ -204,7 +206,7 @@ public function runDataProvider() 'isRun' => true, 'php' => '', 'command' => 'php '. BP . '/bin/magento queue:consumers:start %s %s %s', - 'arguments' => ['consumerName', '--pid-file-path=consumerName.pid', '--max-messages=10000'], + 'arguments' => ['consumerName', '--pid-file-path=consumerName-myHostName.pid', '--max-messages=10000'], 'allowedConsumers' => [], 'shellBackgroundExpects' => 0, 'isRunExpects' => 1, @@ -214,7 +216,7 @@ public function runDataProvider() 'isRun' => true, 'php' => '', 'command' => 'php '. BP . '/bin/magento queue:consumers:start %s %s %s', - 'arguments' => ['consumerName', '--pid-file-path=consumerName.pid', '--max-messages=10000'], + 'arguments' => ['consumerName', '--pid-file-path=consumerName-myHostName.pid', '--max-messages=10000'], 'allowedConsumers' => ['consumerName'], 'shellBackgroundExpects' => 0, 'isRunExpects' => 1, @@ -224,7 +226,7 @@ public function runDataProvider() 'isRun' => false, 'php' => '', 'command' => 'php '. BP . '/bin/magento queue:consumers:start %s %s %s', - 'arguments' => ['consumerName', '--pid-file-path=consumerName.pid', '--max-messages=10000'], + 'arguments' => ['consumerName', '--pid-file-path=consumerName-myHostName.pid', '--max-messages=10000'], 'allowedConsumers' => ['consumerName'], 'shellBackgroundExpects' => 1, 'isRunExpects' => 1, @@ -234,7 +236,7 @@ public function runDataProvider() 'isRun' => false, 'php' => '/bin/php', 'command' => '/bin/php '. BP . '/bin/magento queue:consumers:start %s %s', - 'arguments' => ['consumerName', '--pid-file-path=consumerName.pid'], + 'arguments' => ['consumerName', '--pid-file-path=consumerName-myHostName.pid'], 'allowedConsumers' => ['consumerName'], 'shellBackgroundExpects' => 1, 'isRunExpects' => 1, diff --git a/app/code/Magento/MessageQueue/Test/Unit/_files/consumers_runner_functions_mocks.php b/app/code/Magento/MessageQueue/Test/Unit/_files/consumers_runner_functions_mocks.php new file mode 100644 index 0000000000000..f703efd0644e8 --- /dev/null +++ b/app/code/Magento/MessageQueue/Test/Unit/_files/consumers_runner_functions_mocks.php @@ -0,0 +1,14 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\MessageQueue\Model\Cron; + +/** + * @return string + */ +function gethostname() +{ + return 'myHost@Name'; +} From cd2394ffaa0492479f8454fd7c57e999dce3103f Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <bkorablov@magento.com> Date: Wed, 11 Jul 2018 15:05:12 +0300 Subject: [PATCH 0213/1171] MAGETWO-93161: Fix Problems with Consumer Runners on Cloud Clusters --- .../Test/Unit/_files/consumers_runner_functions_mocks.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/MessageQueue/Test/Unit/_files/consumers_runner_functions_mocks.php b/app/code/Magento/MessageQueue/Test/Unit/_files/consumers_runner_functions_mocks.php index f703efd0644e8..788a8464a0ce4 100644 --- a/app/code/Magento/MessageQueue/Test/Unit/_files/consumers_runner_functions_mocks.php +++ b/app/code/Magento/MessageQueue/Test/Unit/_files/consumers_runner_functions_mocks.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\MessageQueue\Model\Cron; /** From ad09eac53341af405f8e1baa37f7e61d7a36baca Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <bkorablov@magento.com> Date: Wed, 11 Jul 2018 15:18:08 +0300 Subject: [PATCH 0214/1171] MAGETWO-93161: Fix Problems with Consumer Runners on Cloud Clusters --- app/code/Magento/MessageQueue/Model/Cron/ConsumersRunner.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/MessageQueue/Model/Cron/ConsumersRunner.php b/app/code/Magento/MessageQueue/Model/Cron/ConsumersRunner.php index ceeb4badeefd0..f301fb9289efb 100644 --- a/app/code/Magento/MessageQueue/Model/Cron/ConsumersRunner.php +++ b/app/code/Magento/MessageQueue/Model/Cron/ConsumersRunner.php @@ -139,7 +139,7 @@ private function canBeRun($consumerName, array $allowedConsumers = []) */ private function getPidFilePath($consumerName) { - $sanitizedHostname = preg_replace('/[^a-zA-Z0-9]/i', '', gethostname()); + $sanitizedHostname = preg_replace('/[^a-z0-9]/i', '', gethostname()); return $consumerName . '-' . $sanitizedHostname . static::PID_FILE_EXT; } From a27fc6e23abc1dae1a52f8dc8d7489d0219347d6 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Wed, 11 Jul 2018 14:35:40 +0200 Subject: [PATCH 0215/1171] Added index for oauth_nonce timestamp --- app/code/Magento/Integration/etc/db_schema.xml | 3 +++ app/code/Magento/Integration/etc/db_schema_whitelist.json | 3 +++ 2 files changed, 6 insertions(+) diff --git a/app/code/Magento/Integration/etc/db_schema.xml b/app/code/Magento/Integration/etc/db_schema.xml index 0e95ef54e1b99..9e81b867d36d5 100644 --- a/app/code/Magento/Integration/etc/db_schema.xml +++ b/app/code/Magento/Integration/etc/db_schema.xml @@ -88,6 +88,9 @@ <column name="nonce"/> <column name="consumer_id"/> </constraint> + <index name="OAUTH_NONCE_TIMESTAMP" indexType="btree"> + <column name="timestamp"/> + </index> </table> <table name="integration" resource="default" engine="innodb" comment="integration"> <column xsi:type="int" name="integration_id" padding="10" unsigned="true" nullable="false" identity="true" diff --git a/app/code/Magento/Integration/etc/db_schema_whitelist.json b/app/code/Magento/Integration/etc/db_schema_whitelist.json index b7c3b992c0e27..6513778060288 100644 --- a/app/code/Magento/Integration/etc/db_schema_whitelist.json +++ b/app/code/Magento/Integration/etc/db_schema_whitelist.json @@ -56,6 +56,9 @@ "constraint": { "OAUTH_NONCE_CONSUMER_ID_OAUTH_CONSUMER_ENTITY_ID": true, "OAUTH_NONCE_NONCE_CONSUMER_ID": true + }, + "index": { + "OAUTH_NONCE_TIMESTAMP": true } }, "integration": { From 6c8f25611df17bed5af4204bd3889dd99bf8b034 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Wed, 11 Jul 2018 17:00:56 +0200 Subject: [PATCH 0216/1171] API-functional test added --- .../Catalog/CategoryProductsVariantsTest.php | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php new file mode 100644 index 0000000000000..d9a46a4451212 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\TestFramework\ObjectManager; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test of getting child products info of configurable product on category request + */ +class CategoryProductsVariantsTest extends GraphQlAbstract +{ + /** + * + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testGetSimpleProductsFromCategory() + { + + $query + = <<<QUERY +{ + category(id: 2) { + id + name + products { + items { + sku + ... on ConfigurableProduct { + variants { + product { + sku + } + } + } + } + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); + $product = $productRepository->get('simple_10', false, null, true); + + $this->assertArrayHasKey('variants', $response['category']['products']['items'][0]); + $this->assertCount(2, $response['category']['products']['items'][0]['variants']); + $this->assertSimpleProductFields($product, $response['category']['products']['items'][0]['variants'][0]); + } + + /** + * @param ProductInterface $product + * @param array $actualResponse + */ + private function assertSimpleProductFields($product, $actualResponse) + { + $assertionMap = [ + [ + 'response_field' => 'product', 'expected_value' => [ + "sku" => $product->getSku() + ] + ], + ]; + + $this->assertResponseFields($actualResponse, $assertionMap); + } + + /** + * @param array $actualResponse + * @param array $assertionMap + */ + private function assertResponseFields($actualResponse, $assertionMap) + { + foreach ($assertionMap as $key => $assertionData) { + $expectedValue = isset($assertionData['expected_value']) + ? $assertionData['expected_value'] + : $assertionData; + $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; + self::assertNotNull( + $expectedValue, + "Value of '{$responseField}' field must not be NULL" + ); + self::assertEquals( + $expectedValue, + $actualResponse[$responseField], + "Value of '{$responseField}' field in response does not match expected value: " + . var_export($expectedValue, true) + ); + } + } +} From 660a4dbe7b9ca5db210bdf90cfe4e58963c13329 Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Wed, 11 Jul 2018 10:55:41 -0500 Subject: [PATCH 0217/1171] MAGETWO-91465: Once integer is stored for State/Province field, it can not be changed to alphanumeric --- app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js | 2 +- lib/web/mage/validation.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 675c7214edc53..c63b4588d2d16 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -757,7 +757,7 @@ define([ ], 'validate-not-number-first': [ function (value) { - return utils.isEmptyNoTrim(value) || /^[^0-9-\.].*$/.test(value); + return utils.isEmptyNoTrim(value) || /^[^0-9-\.].*$/.test(value.trim()); }, $.mage.__('First character must be letter.') ], diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index ba06c112d5103..d08819ebe94aa 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -992,7 +992,7 @@ ], 'validate-not-number-first': [ function (value) { - return $.mage.isEmptyNoTrim(value) || /^[^0-9-\.].*$/.test(value); + return $.mage.isEmptyNoTrim(value) || /^[^0-9-\.].*$/.test(value.trim()); }, $.mage.__('First character must be letter.') ], From 838182a5feefd83726bea09146e66c0d85940d36 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 11 Jul 2018 11:13:44 -0500 Subject: [PATCH 0218/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - fixing formatting --- .../Item/ItemResolverComposite.php | 12 +++-- .../Item/ItemResolverInterface.php | 11 ++-- .../Checkout/Model/Cart/ImageProvider.php | 2 +- .../Block/Cart/Item/Renderer/Configurable.php | 3 +- .../Item/ItemProductResolver.php | 53 ++++++++++--------- .../Block/Cart/Item/Renderer/Grouped.php | 3 +- .../Item/ItemProductResolver.php | 46 ++++++++-------- .../Customer/Wishlist/Item/Column/Image.php | 2 +- .../Wishlist/CustomerData/Wishlist.php | 2 +- 9 files changed, 73 insertions(+), 61 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php index 3c34438da55a4..365837089dce3 100644 --- a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php +++ b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php @@ -5,8 +5,10 @@ */ namespace Magento\Catalog\Model\Product\Configuration\Item; +use Magento\Catalog\Api\Data\ProductInterface; + /** - * Composite implementation for @see ItemResolverInterface + * {@inheritdoc} */ class ItemResolverComposite implements ItemResolverInterface { @@ -24,14 +26,14 @@ public function __construct(array $itemResolvers) /** * {@inheritdoc} */ - public function getFinalProduct( - \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item - ) : \Magento\Catalog\Api\Data\ProductInterface { + public function getFinalProduct(ItemInterface $item) : ProductInterface + { $product = $item->getProduct(); foreach ($this->itemResolvers as $resolver) { $resolvedProduct = $resolver->getFinalProduct($item); if ($resolvedProduct !== $product) { - return $resolvedProduct; + $product = $resolvedProduct; + break; } } return $product; diff --git a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php index 6d2d201ac2a3d..78859016c48b4 100644 --- a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php +++ b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php @@ -5,19 +5,20 @@ */ namespace Magento\Catalog\Model\Product\Configuration\Item; +use Magento\Catalog\Api\Data\ProductInterface; + /** - * Resolves the product for a configured item + * Resolves the product from a configured item. * * @api */ interface ItemResolverInterface { /** - * Get the final product from a configured item by product type and selection + * Get the final product from a configured item by product type and selection. * * @param ItemInterface $item - * @return \Magento\Catalog\Api\Data\ProductInterface + * @return ProductInterface */ - public function getFinalProduct(\Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item) - : \Magento\Catalog\Api\Data\ProductInterface; + public function getFinalProduct(ItemInterface $item) : ProductInterface; } diff --git a/app/code/Magento/Checkout/Model/Cart/ImageProvider.php b/app/code/Magento/Checkout/Model/Cart/ImageProvider.php index a75dbd6b5a505..61985e6e3b6ff 100644 --- a/app/code/Magento/Checkout/Model/Cart/ImageProvider.php +++ b/app/code/Magento/Checkout/Model/Cart/ImageProvider.php @@ -39,7 +39,7 @@ public function __construct( ) { $this->itemRepository = $itemRepository; $this->itemPool = $itemPool; - $this->customerDataItem = $customerDataItem ?? ObjectManager::getInstance()->get(DefaultItem::class); + $this->customerDataItem = $customerDataItem ?: ObjectManager::getInstance()->get(DefaultItem::class); } /** diff --git a/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php b/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php index 557228defc0dc..733f45ac827aa 100644 --- a/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php @@ -57,7 +57,8 @@ public function getOptionList() /** * {@inheritdoc} - * @deprecated + * @deprecated because parent can handle the logic for images of all product types + * @see \Magento\Checkout\Block\Cart\Item\Renderer::getProductForThumbnail */ public function getProductForThumbnail() { diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php index 135f57462761e..bfe35a1b4c4c6 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -6,39 +6,40 @@ namespace Magento\ConfigurableProduct\Model\Product\Configuration\Item; use Magento\Catalog\Model\Config\Source\Product\Thumbnail; +use Magento\Catalog\Model\Product\Configuration\Item\ItemInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Catalog\Model\Product; /** - * Resolves the product for a configured option item + * {@inheritdoc} */ -class ItemProductResolver implements \Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface +class ItemProductResolver implements ItemResolverInterface { const CONFIG_THUMBNAIL_SOURCE = 'checkout/cart/configurable_product_image'; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ protected $scopeConfig; /** - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param ScopeConfigInterface $scopeConfig */ - public function __construct(\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig) + public function __construct(ScopeConfigInterface $scopeConfig) { $this->scopeConfig = $scopeConfig; } /** - * Identify the product from which thumbnail should be taken. - * - * @param \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item - * @return \Magento\Catalog\Api\Data\ProductInterface + * {@inheritdoc} */ - public function getFinalProduct( - \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item - ) : \Magento\Catalog\Api\Data\ProductInterface { + public function getFinalProduct(ItemInterface $item) : ProductInterface + { /** * Show parent product thumbnail if it must be always shown according to the related setting in system config - * or if child thumbnail is not available + * or if child thumbnail is not available. */ $parentItem = $item->getProduct(); $config = $this->scopeConfig->getValue( @@ -46,26 +47,28 @@ public function getFinalProduct( \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); - return $config == Thumbnail::OPTION_USE_PARENT_IMAGE - || (!$this->getChildProduct($item)->getData('thumbnail') - || $this->getChildProduct($item)->getData('thumbnail') == 'no_selection') - ? $parentItem - : $this->getChildProduct($item); + $childProduct = $this->getChildProduct($item); + $childThumbnail = $childProduct->getData('thumbnail'); + $finalProduct = + ($config == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumbnail || $childThumbnail == 'no_selection') + ? $parentItem + : $childProduct; + return $finalProduct; } /** * Get item configurable child product * - * @param \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item - * @return \Magento\Catalog\Model\Product + * @param ItemInterface $item + * @return Product */ - private function getChildProduct( - \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item - ) : \Magento\Catalog\Model\Product { + private function getChildProduct(ItemInterface $item) : Product + { $option = $item->getOptionByCode('simple_product'); + $product = $item->getProduct(); if ($option) { - return $option->getProduct(); + $product = $option->getProduct(); } - return $item->getProduct(); + return $product; } } diff --git a/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php b/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php index 0340a78b45a39..a550e5cb2eed5 100644 --- a/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php +++ b/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php @@ -38,7 +38,8 @@ public function getGroupedProduct() /** * {@inheritdoc} - * @deprecated + * @deprecated because parent can handle the logic for images of all product types + * @see \Magento\Checkout\Block\Cart\Item\Renderer::getProductForThumbnail */ public function getProductForThumbnail() { diff --git a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php index 825a62cc52275..09613a2f554bf 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -6,11 +6,16 @@ namespace Magento\GroupedProduct\Model\Product\Configuration\Item; use Magento\Catalog\Model\Config\Source\Product\Thumbnail; +use Magento\Catalog\Model\Product\Configuration\Item\ItemInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; /** - * Resolves the product for a configured option item + * {@inheritdoc} */ -class ItemProductResolver implements \Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface +class ItemProductResolver implements ItemResolverInterface { /** * Path in config to the setting which defines if parent or child product should be used to generate a thumbnail. @@ -18,27 +23,23 @@ class ItemProductResolver implements \Magento\Catalog\Model\Product\Configuratio const CONFIG_THUMBNAIL_SOURCE = 'checkout/cart/grouped_product_image'; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ protected $scopeConfig; /** - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param ScopeConfigInterface $scopeConfig */ - public function __construct(\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig) + public function __construct(ScopeConfigInterface $scopeConfig) { $this->scopeConfig = $scopeConfig; } /** - * Identify the product from which thumbnail should be taken. - * - * @param \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item - * @return \Magento\Catalog\Api\Data\ProductInterface + * {@inheritdoc} */ - public function getFinalProduct( - \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item - ) : \Magento\Catalog\Api\Data\ProductInterface { + public function getFinalProduct(ItemInterface $item) : ProductInterface + { /** * Show grouped product thumbnail if it must be always shown according to the related setting in system config * or if child product thumbnail is not available @@ -48,25 +49,28 @@ public function getFinalProduct( \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); $childProduct = $item->getProduct(); - return $config == Thumbnail::OPTION_USE_PARENT_IMAGE || - (!$childProduct->getData('thumbnail') || $childProduct->getData('thumbnail') == 'no_selection') + $childThumbnail = $childProduct->getData('thumbnail'); + + $finalProduct = + ($config == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumbnail || $childThumbnail == 'no_selection') ? $this->getParentProduct($item) : $childProduct; + return $finalProduct; } /** * Get grouped product * - * @param \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item - * @return \Magento\Catalog\Model\Product + * @param ItemInterface $item + * @return Product */ - private function getParentProduct( - \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface $item - ) : \Magento\Catalog\Model\Product { + private function getParentProduct(ItemInterface $item) : Product + { $option = $item->getOptionByCode('product_type'); + $product = $item->getProduct(); if ($option) { - return $option->getProduct(); + $product = $option->getProduct(); } - return $item->getProduct(); + return $product; } } diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php index bd055937b9719..b8728dd33f4c8 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php @@ -41,7 +41,7 @@ public function __construct( UrlBuilder $urlBuilder = null, ItemResolverInterface $itemResolver = null ) { - $this->itemResolver = $itemResolver ?? ObjectManager::getInstance()->get(ItemResolverInterface::class); + $this->itemResolver = $itemResolver ?: ObjectManager::getInstance()->get(ItemResolverInterface::class); parent::__construct( $context, $httpContext, diff --git a/app/code/Magento/Wishlist/CustomerData/Wishlist.php b/app/code/Magento/Wishlist/CustomerData/Wishlist.php index e721fbf9a9029..9cfc55bae723e 100644 --- a/app/code/Magento/Wishlist/CustomerData/Wishlist.php +++ b/app/code/Magento/Wishlist/CustomerData/Wishlist.php @@ -62,7 +62,7 @@ public function __construct( $this->imageHelperFactory = $imageHelperFactory; $this->block = $block; $this->view = $view; - $this->itemResolver = $itemResolver ?? ObjectManager::getInstance()->get( + $this->itemResolver = $itemResolver ?: ObjectManager::getInstance()->get( \Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface::class ); } From dc703f44e79a2a6e2a7bbf4280c2c11016c4cab9 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 11 Jul 2018 11:39:11 -0500 Subject: [PATCH 0219/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - fixing formatting --- .../Block/Cart/Item/Renderer/Configurable.php | 4 +- .../Item/ItemProductResolver.php | 5 +- .../Cart/Item/Renderer/ConfigurableTest.php | 86 ++++--------------- .../Block/Cart/Item/Renderer/Grouped.php | 4 +- .../Item/ItemProductResolver.php | 2 +- .../Block/Cart/Item/Renderer/GroupedTest.php | 68 ++------------- .../Customer/Wishlist/Item/Column/Image.php | 2 - 7 files changed, 34 insertions(+), 137 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php b/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php index 733f45ac827aa..714d2eb18a434 100644 --- a/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php @@ -19,6 +19,8 @@ class Configurable extends Renderer implements IdentityInterface { /** * Path in config to the setting which defines if parent or child product should be used to generate a thumbnail. + * @deprecated moved to model because of class refactoring + * @see \Magento\ConfigurableProduct\Model\Product\Configuration\Item\ItemProductResolver::CONFIG_THUMBNAIL_SOURCE */ const CONFIG_THUMBNAIL_SOURCE = 'checkout/cart/configurable_product_image'; @@ -57,7 +59,7 @@ public function getOptionList() /** * {@inheritdoc} - * @deprecated because parent can handle the logic for images of all product types + * @deprecated because now parent handles the logic for images of all product types * @see \Magento\Checkout\Block\Cart\Item\Renderer::getProductForThumbnail */ public function getProductForThumbnail() diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php index bfe35a1b4c4c6..8a8ec0aafc21e 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -17,6 +17,9 @@ */ class ItemProductResolver implements ItemResolverInterface { + /** + * Path in config to the setting which defines if parent or child product should be used to generate a thumbnail. + */ const CONFIG_THUMBNAIL_SOURCE = 'checkout/cart/configurable_product_image'; /** @@ -43,7 +46,7 @@ public function getFinalProduct(ItemInterface $item) : ProductInterface */ $parentItem = $item->getProduct(); $config = $this->scopeConfig->getValue( - \Magento\ConfigurableProduct\Block\Cart\Item\Renderer\Configurable::CONFIG_THUMBNAIL_SOURCE, + self::CONFIG_THUMBNAIL_SOURCE, \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Cart/Item/Renderer/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Cart/Item/Renderer/ConfigurableTest.php index 57d1bfb8ac146..9a21431ffcfa5 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Cart/Item/Renderer/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Cart/Item/Renderer/ConfigurableTest.php @@ -11,102 +11,48 @@ class ConfigurableTest extends \PHPUnit\Framework\TestCase { /** @var \Magento\Framework\View\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $_configManager; + private $configManager; /** @var \Magento\Catalog\Helper\Image|\PHPUnit_Framework_MockObject_MockObject */ - protected $_imageHelper; + private $imageHelper; /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $_scopeConfig; + private $scopeConfig; /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $productConfigMock; + private $productConfigMock; /** @var Renderer */ - protected $_renderer; + private $renderer; protected function setUp() { parent::setUp(); $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_configManager = $this->createMock(\Magento\Framework\View\ConfigInterface::class); - $this->_imageHelper = $this->createPartialMock( + $this->configManager = $this->createMock(\Magento\Framework\View\ConfigInterface::class); + $this->imageHelper = $this->createPartialMock( \Magento\Catalog\Helper\Image::class, ['init', 'resize', '__toString'] ); - $this->_scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $this->scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); $this->productConfigMock = $this->createMock(\Magento\Catalog\Helper\Product\Configuration::class); - $this->_renderer = $objectManagerHelper->getObject( + $this->renderer = $objectManagerHelper->getObject( \Magento\ConfigurableProduct\Block\Cart\Item\Renderer\Configurable::class, [ - 'viewConfig' => $this->_configManager, - 'imageHelper' => $this->_imageHelper, - 'scopeConfig' => $this->_scopeConfig, + 'viewConfig' => $this->configManager, + 'imageHelper' => $this->imageHelper, + 'scopeConfig' => $this->scopeConfig, 'productConfig' => $this->productConfigMock ] ); } - /** - * Initialize parent configurable product and child product. - * - * @param bool $childHasThumbnail - * @param bool $useParentThumbnail - * @return \Magento\Catalog\Model\Product[]|\PHPUnit_Framework_MockObject_MockObject[] - */ - protected function _initProducts($childHasThumbnail = true, $useParentThumbnail = false) - { - /** Set option which can force usage of parent product thumbnail when configurable product is displayed */ - $thumbnailToBeUsed = $useParentThumbnail - ? ThumbnailSource::OPTION_USE_PARENT_IMAGE - : ThumbnailSource::OPTION_USE_OWN_IMAGE; - $this->_scopeConfig->expects( - $this->any() - )->method( - 'getValue' - )->with( - Renderer::CONFIG_THUMBNAIL_SOURCE - )->will( - $this->returnValue($thumbnailToBeUsed) - ); - - /** Initialized parent product */ - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $parentProduct */ - $parentProduct = $this->createMock(\Magento\Catalog\Model\Product::class); - - /** Initialize child product */ - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $childProduct */ - $childProduct = $this->createPartialMock(\Magento\Catalog\Model\Product::class, ['getThumbnail', '__wakeup']); - $childThumbnail = $childHasThumbnail ? 'thumbnail.jpg' : 'no_selection'; - $childProduct->expects($this->any())->method('getThumbnail')->will($this->returnValue($childThumbnail)); - - /** Mock methods which return parent and child products */ - /** @var \Magento\Quote\Model\Quote\Item\Option|\PHPUnit_Framework_MockObject_MockObject $itemOption */ - $itemOption = $this->createMock(\Magento\Quote\Model\Quote\Item\Option::class); - $itemOption->expects($this->any())->method('getProduct')->will($this->returnValue($childProduct)); - /** @var \Magento\Quote\Model\Quote\Item|\PHPUnit_Framework_MockObject_MockObject $item */ - $item = $this->createMock(\Magento\Quote\Model\Quote\Item::class); - $item->expects($this->any())->method('getProduct')->will($this->returnValue($parentProduct)); - $item->expects( - $this->any() - )->method( - 'getOptionByCode' - )->with( - 'simple_product' - )->will( - $this->returnValue($itemOption) - ); - $this->_renderer->setItem($item); - - return ['parentProduct' => $parentProduct, 'childProduct' => $childProduct]; - } - public function testGetOptionList() { $itemMock = $this->createMock(\Magento\Quote\Model\Quote\Item::class); - $this->_renderer->setItem($itemMock); + $this->renderer->setItem($itemMock); $this->productConfigMock->expects($this->once())->method('getOptions')->with($itemMock); - $this->_renderer->getOptionList(); + $this->renderer->getOptionList(); } public function testGetIdentities() @@ -116,7 +62,7 @@ public function testGetIdentities() $product->expects($this->exactly(2))->method('getIdentities')->will($this->returnValue($productTags)); $item = $this->createMock(\Magento\Quote\Model\Quote\Item::class); $item->expects($this->exactly(2))->method('getProduct')->will($this->returnValue($product)); - $this->_renderer->setItem($item); - $this->assertEquals(array_merge($productTags, $productTags), $this->_renderer->getIdentities()); + $this->renderer->setItem($item); + $this->assertEquals(array_merge($productTags, $productTags), $this->renderer->getIdentities()); } } diff --git a/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php b/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php index a550e5cb2eed5..4300ae1033e51 100644 --- a/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php +++ b/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php @@ -19,6 +19,8 @@ class Grouped extends Renderer implements IdentityInterface { /** * Path in config to the setting which defines if parent or child product should be used to generate a thumbnail. + * @deprecated moved to model because of class refactoring + * @see \Magento\GroupedProduct\Model\Product\Configuration\Item\ItemProductResolver::CONFIG_THUMBNAIL_SOURCE */ const CONFIG_THUMBNAIL_SOURCE = 'checkout/cart/grouped_product_image'; @@ -38,7 +40,7 @@ public function getGroupedProduct() /** * {@inheritdoc} - * @deprecated because parent can handle the logic for images of all product types + * @deprecated because now parent handles the logic for images of all product types * @see \Magento\Checkout\Block\Cart\Item\Renderer::getProductForThumbnail */ public function getProductForThumbnail() diff --git a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php index 09613a2f554bf..b25f90a20de85 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -45,7 +45,7 @@ public function getFinalProduct(ItemInterface $item) : ProductInterface * or if child product thumbnail is not available */ $config = $this->scopeConfig->getValue( - \Magento\GroupedProduct\Block\Cart\Item\Renderer\Grouped::CONFIG_THUMBNAIL_SOURCE, + self::CONFIG_THUMBNAIL_SOURCE, \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); $childProduct = $item->getProduct(); diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Block/Cart/Item/Renderer/GroupedTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Block/Cart/Item/Renderer/GroupedTest.php index 1ecf0cbb88d19..9302706342f13 100644 --- a/app/code/Magento/GroupedProduct/Test/Unit/Block/Cart/Item/Renderer/GroupedTest.php +++ b/app/code/Magento/GroupedProduct/Test/Unit/Block/Cart/Item/Renderer/GroupedTest.php @@ -11,76 +11,22 @@ class GroupedTest extends \PHPUnit\Framework\TestCase { /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $_scopeConfig; + private $scopeConfig; /** @var Renderer */ - protected $_renderer; + private $renderer; protected function setUp() { parent::setUp(); $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); - $this->_renderer = $objectManagerHelper->getObject( + $this->scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $this->renderer = $objectManagerHelper->getObject( \Magento\GroupedProduct\Block\Cart\Item\Renderer\Grouped::class, - ['scopeConfig' => $this->_scopeConfig] + ['scopeConfig' => $this->scopeConfig] ); } - /** - * Initialize parent grouped product and child product. - * - * @param bool $childHasThumbnail - * @param bool $useParentThumbnail - * @return \Magento\Catalog\Model\Product[]|\PHPUnit_Framework_MockObject_MockObject[] - */ - protected function _initProducts($childHasThumbnail = true, $useParentThumbnail = false) - { - /** Set option which can force usage of parent product thumbnail when grouped product is displayed */ - $thumbnailToBeUsed = $useParentThumbnail - ? ThumbnailSource::OPTION_USE_PARENT_IMAGE - : ThumbnailSource::OPTION_USE_OWN_IMAGE; - $this->_scopeConfig->expects( - $this->any() - )->method( - 'getValue' - )->with( - Renderer::CONFIG_THUMBNAIL_SOURCE - )->will( - $this->returnValue($thumbnailToBeUsed) - ); - - /** Initialized parent product */ - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $parentProduct */ - $parentProduct = $this->createMock(\Magento\Catalog\Model\Product::class); - - /** Initialize child product */ - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $childProduct */ - $childProduct = $this->createPartialMock(\Magento\Catalog\Model\Product::class, ['getThumbnail', '__wakeup']); - $childThumbnail = $childHasThumbnail ? 'thumbnail.jpg' : 'no_selection'; - $childProduct->expects($this->any())->method('getThumbnail')->will($this->returnValue($childThumbnail)); - - /** Mock methods which return parent and child products */ - /** @var \Magento\Quote\Model\Quote\Item\Option|\PHPUnit_Framework_MockObject_MockObject $itemOption */ - $itemOption = $this->createMock(\Magento\Quote\Model\Quote\Item\Option::class); - $itemOption->expects($this->any())->method('getProduct')->will($this->returnValue($parentProduct)); - /** @var \Magento\Quote\Model\Quote\Item|\PHPUnit_Framework_MockObject_MockObject $item */ - $item = $this->createMock(\Magento\Quote\Model\Quote\Item::class); - $item->expects($this->any())->method('getProduct')->will($this->returnValue($childProduct)); - $item->expects( - $this->any() - )->method( - 'getOptionByCode' - )->with( - 'product_type' - )->will( - $this->returnValue($itemOption) - ); - $this->_renderer->setItem($item); - - return ['parentProduct' => $parentProduct, 'childProduct' => $childProduct]; - } - public function testGetIdentities() { $productTags = ['catalog_product_1']; @@ -88,7 +34,7 @@ public function testGetIdentities() $product->expects($this->exactly(2))->method('getIdentities')->will($this->returnValue($productTags)); $item = $this->createMock(\Magento\Quote\Model\Quote\Item::class); $item->expects($this->exactly(2))->method('getProduct')->will($this->returnValue($product)); - $this->_renderer->setItem($item); - $this->assertEquals(array_merge($productTags, $productTags), $this->_renderer->getIdentities()); + $this->renderer->setItem($item); + $this->assertEquals(array_merge($productTags, $productTags), $this->renderer->getIdentities()); } } diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php index b8728dd33f4c8..793e92a462fa0 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php @@ -6,8 +6,6 @@ /** * Wishlist block customer item cart column - * - * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; From 8705d12f839cafef8f2866c775988923be57bf4c Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 11 Jul 2018 11:47:10 -0500 Subject: [PATCH 0220/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - remove unused --- app/code/Magento/Wishlist/etc/frontend/di.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/code/Magento/Wishlist/etc/frontend/di.xml b/app/code/Magento/Wishlist/etc/frontend/di.xml index 802f3612f2c9b..00642b132bfdf 100644 --- a/app/code/Magento/Wishlist/etc/frontend/di.xml +++ b/app/code/Magento/Wishlist/etc/frontend/di.xml @@ -47,12 +47,4 @@ </argument> </arguments> </type> - <type name="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Image"> - <arguments> - <argument name="renderers" xsi:type="array"> - <item name="configurable" xsi:type="object">Magento\ConfigurableProduct\Block\Cart\Item\Renderer\Configurable</item> - <item name="grouped" xsi:type="object">Magento\GroupedProduct\Block\Cart\Item\Renderer\Grouped</item> - </argument> - </arguments> - </type> </config> From 05806147a55b41cf1d8a545eef70e889ec58be55 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Wed, 11 Jul 2018 12:01:46 -0500 Subject: [PATCH 0221/1171] MC-3065: Automate MFTF for MC-1364 Added section and selectors for configuration page --- .../Config/Test/Mftf/Page/AdminConfigPage.xml | 3 ++ .../Test/Mftf/Section/CatalogSection.xml | 15 +++++++++ .../Test/Mftf/Test/ConfigurationTest.xml | 33 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml index e517f6ef62c7a..6c8a72b2b1625 100644 --- a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml @@ -12,4 +12,7 @@ <page name="AdminContentManagementPage" url="admin/system_config/edit/section/cms/" area="admin" module="Magento_Config"> <section name="ContentManagementSection"/> </page> + <page name="CatalogConfigPage" url="admin/system_config/edit/section/catalog/" area="admin" module="Magento_Config"> + <section name="ContentManagementSection"/> + </page> </pages> diff --git a/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml new file mode 100644 index 0000000000000..78b9d1f72f66d --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="CatalogSection"> + <element name="storefront" type="select" selector="#catalog_frontend-head"/> + <element name="CheckIfTabExpand" type="button" selector="#catalog_frontend-head:not(.open)"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml b/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml new file mode 100644 index 0000000000000..e436207561420 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="VerifyAllowDynamicMediaURLsSettingIsRemoved"> + <annotations> + <features value="Backend"/> + <stories value="Dynamic Media URL"/> + <title value="Verify that Allow Dynamic Media URLs setting is removed from configuration page"/> + <description value="Verify that Allow Dynamic Media URLs setting is removed from configuration page"/> + <severity value="CRITICAL"/> + <useCaseId value="MC-1364"/> + <testCaseId value="MC-3178"/> + <group value="alo"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <amOnPage url="{{CatalogConfigPage.url}}" stepKey="navigateToConfigurationPage" /> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick stepKey="expandStorefrontTab" selector="{{CatalogSection.storefront}}" dependentSelector="{{CatalogSection.CheckIfTabExpand}}" visible="true" /> + <dontSee stepKey="dontSeeDynamicMediaURLsSetting" userInput="Allow Dynamic Media URLs"/> + </test> +</tests> From 6a12666f8e010648433476e2449d5ad995f08d86 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 11 Jul 2018 13:03:13 -0500 Subject: [PATCH 0222/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - remove unused --- app/code/Magento/ConfigurableProduct/Test/Mftf/composer.json | 2 +- app/code/Magento/ConfigurableProduct/composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/composer.json b/app/code/Magento/ConfigurableProduct/Test/Mftf/composer.json index 78063660f0025..64ef047693cf0 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/composer.json +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/composer.json @@ -14,12 +14,12 @@ "magento/functional-test-module-customer": "100.0.0-dev", "magento/functional-test-module-eav": "100.0.0-dev", "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-msrp": "100.0.0-dev", "magento/functional-test-module-quote": "100.0.0-dev", "magento/functional-test-module-store": "100.0.0-dev", "magento/functional-test-module-ui": "100.0.0-dev" }, "suggest": { + "magento/functional-test-module-msrp": "100.0.0-dev", "magento/functional-test-module-webapi": "100.0.0-dev", "magento/functional-test-module-sales": "100.0.0-dev", "magento/functional-test-module-product-video": "100.0.0-dev", diff --git a/app/code/Magento/ConfigurableProduct/composer.json b/app/code/Magento/ConfigurableProduct/composer.json index 959c036981878..b03a8a65702be 100644 --- a/app/code/Magento/ConfigurableProduct/composer.json +++ b/app/code/Magento/ConfigurableProduct/composer.json @@ -14,12 +14,12 @@ "magento/module-customer": "*", "magento/module-eav": "*", "magento/module-media-storage": "*", - "magento/module-msrp": "*", "magento/module-quote": "*", "magento/module-store": "*", "magento/module-ui": "*" }, "suggest": { + "magento/module-msrp": "*", "magento/module-webapi": "*", "magento/module-sales": "*", "magento/module-product-video": "*", From 6c79a29c1a4fc68d3c87bffe1351414a3a21da8b Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Wed, 11 Jul 2018 14:58:02 -0500 Subject: [PATCH 0223/1171] MC-230: Customer should be able to see basic bundle product details --- .../Test/StorefrontBundleProductDetailsTest.xml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml index 5859d531f3472..55721b2166deb 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml @@ -16,7 +16,7 @@ <description value="Customer should be able to see basic bundle product details"/> <severity value="CRITICAL"/> <testCaseId value="MC-230"/> - <group value="Bundle"/> + <group value="banana"/> </annotations> <before> <!--Creating Data--> @@ -44,7 +44,7 @@ <!-- Add options --> <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> - <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption3"/> + <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption"/> <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleOptions"/> <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillOptionTitle"/> <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectInputType"/> @@ -75,10 +75,11 @@ <!--Checking details--> <amOnPage url="{{BundleProduct.urlKey}}.html" stepKey="goToProductPage"/> <waitForPageLoad stepKey="waitForProductPageToLoad"/> - <seeElement selector="{{BundleStorefrontSection.shortDescriptionText}}" stepKey="seeShortDescription"/> - <seeElement selector="{{BundleStorefrontSection.longDescriptionText}}" stepKey="seeLongDescription"/> + <see selector="{{BundleStorefrontSection.shortDescriptionText}}" userInput="This is the short description" stepKey="seeShortDescription"/> + <see selector="{{BundleStorefrontSection.longDescriptionText}}" userInput="This is the long description" stepKey="seeLongDescription"/> <click selector="{{BundleStorefrontSection.bundleOptionSelector}}" stepKey="clickOnCustomizationOption"/> - <seeElement selector="{{BundleStorefrontSection.bundleOptionSelection('1')}}" stepKey="seeOption1"/> - <seeElement selector="{{BundleStorefrontSection.bundleOptionSelection('2')}}" stepKey="seeOption2"/> + <pauseExecution stepKey="pause"/> + <see selector="{{BundleStorefrontSection.bundleOptionSelection('1')}}" userInput="{{BundleProduct.defaultQuantity}} x $$simpleProduct1.name$$" stepKey="seeOption1"/> + <see selector="{{BundleStorefrontSection.bundleOptionSelection('2')}}" userInput="{{BundleProduct.defaultQuantity}} x $$simpleProduct2.name$$" stepKey="seeOption2"/> </test> </tests> From c0d9cd390181f96bb3599efda0074b524e949f2e Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Wed, 11 Jul 2018 15:00:39 -0500 Subject: [PATCH 0224/1171] =?UTF-8?q?MC-229:=20Customer=20should=20be=20ta?= =?UTF-8?q?ken=20to=20bundle=20product=20details=20page=20when=20clicking?= =?UTF-8?q?=20=E2=80=9CAdd=20to=20Cart=E2=80=9D=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml index 6f2d5bdad1685..6c476183a35b3 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml @@ -42,7 +42,7 @@ <!--Create bundle product--> <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> - <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption3"/> + <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption"/> <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleOptions"/> <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillOptionTitle"/> <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectInputType"/> From f68e7d165cb6cee0bff0af23ea304209ede5d872 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 11 Jul 2018 15:11:57 -0500 Subject: [PATCH 0225/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - adding strict --- .../Model/Product/Configuration/Item/ItemResolverComposite.php | 2 ++ .../Model/Product/Configuration/Item/ItemResolverInterface.php | 2 ++ .../Model/Product/Configuration/Item/ItemProductResolver.php | 2 ++ .../Model/Product/Configuration/Item/ItemProductResolver.php | 2 ++ 4 files changed, 8 insertions(+) diff --git a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php index 365837089dce3..9a3169c43e666 100644 --- a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php +++ b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Product\Configuration\Item; use Magento\Catalog\Api\Data\ProductInterface; diff --git a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php index 78859016c48b4..35c0a7835cb6c 100644 --- a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php +++ b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Product\Configuration\Item; use Magento\Catalog\Api\Data\ProductInterface; diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php index 8a8ec0aafc21e..12e31021b81e4 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\ConfigurableProduct\Model\Product\Configuration\Item; use Magento\Catalog\Model\Config\Source\Product\Thumbnail; diff --git a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php index b25f90a20de85..e3c7e926bae29 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\GroupedProduct\Model\Product\Configuration\Item; use Magento\Catalog\Model\Config\Source\Product\Thumbnail; From ffb67ddd18a4a23055ffa9186e0645322f791bc0 Mon Sep 17 00:00:00 2001 From: Alex Calandra <acalandra@magento.com> Date: Wed, 11 Jul 2018 15:55:12 -0500 Subject: [PATCH 0226/1171] MC-256: Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer - Updating selector name to remove duplicate selector --- .../Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml index ad68179119834..e145394c82ba3 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml @@ -36,6 +36,6 @@ <element name="systemValueDisplayZeroTaxSales" type="checkbox" selector="#row_tax_sales_display_zero_tax input[type='checkbox']"/> <element name="dropdownDisplayZeroTaxSales" type="checkbox" selector="#row_tax_sales_display_zero_tax select"/> - <element name="priceDisplaySettings" type="block" selector="#tax_weee-head" timeout="30"/> + <element name="fixedProductTaxes" type="block" selector="#tax_weee-head" timeout="30"/> </section> </sections> From e0bd48724de7ba2d3b2e2727e10c02abe693e6cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 12 Jul 2018 09:27:28 +0300 Subject: [PATCH 0227/1171] fixed icon color variable name --- .../blank/Magento_CatalogSearch/web/css/source/_module.less | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_CatalogSearch/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_CatalogSearch/web/css/source/_module.less index 52f1beafa32e1..daed96db717c7 100644 --- a/app/design/frontend/Magento/blank/Magento_CatalogSearch/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_CatalogSearch/web/css/source/_module.less @@ -36,9 +36,9 @@ @_icon-font-content: @icon-search, @_icon-font-size: 35px, @_icon-font-line-height: 33px, - @_icon-font-color: @minicart-icons-color, - @_icon-font-color-hover: @minicart-icons-color-hover, - @_icon-font-color-active: @minicart-icons-color-hover, + @_icon-font-color: @header-icons-color, + @_icon-font-color-hover: @header-icons-color-hover, + @_icon-font-color-active: @header-icons-color-hover, @_icon-font-text-hide: true ); display: inline-block; From 15f62697c114af5321f3bfd38d0f8dfb25a01518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 12 Jul 2018 09:28:15 +0300 Subject: [PATCH 0228/1171] fixed icon color variable name --- .../luma/Magento_CatalogSearch/web/css/source/_module.less | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less index 23acf9ee8b08d..f785dd74d900e 100644 --- a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less @@ -37,9 +37,9 @@ @_icon-font-size: 22px, @_icon-font-line-height: 28px, @_icon-font-margin: 0 @indent__s 0 0, - @_icon-font-color: @minicart-icons-color, - @_icon-font-color-hover: @minicart-icons-color-hover, - @_icon-font-color-active: @minicart-icons-color-hover, + @_icon-font-color: @header-icons-color, + @_icon-font-color-hover: @header-icons-color-hover, + @_icon-font-color-active: @header-icons-color-hover, @_icon-font-text-hide: true ); display: inline-block; From 8169e5e221829f28b9182c5a753143dc7fe98cf3 Mon Sep 17 00:00:00 2001 From: hitesh-wagento <hitesh@wagento.com> Date: Thu, 12 Jul 2018 16:34:12 +0530 Subject: [PATCH 0229/1171] Fix] forgot to add lowercase conversion on grouped product assignation --- .../Unit/Model/Import/Product/Type/GroupedTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/GroupedImportExport/Test/Unit/Model/Import/Product/Type/GroupedTest.php b/app/code/Magento/GroupedImportExport/Test/Unit/Model/Import/Product/Type/GroupedTest.php index 011324fb1cc9f..17e8e4c519ccb 100644 --- a/app/code/Magento/GroupedImportExport/Test/Unit/Model/Import/Product/Type/GroupedTest.php +++ b/app/code/Magento/GroupedImportExport/Test/Unit/Model/Import/Product/Type/GroupedTest.php @@ -198,7 +198,7 @@ public function saveDataProvider() ], 'bunch' => [ 'associated_skus' => 'sku_assoc1=1, sku_assoc2=2', - 'sku' => 'productSku', + 'sku' => 'productsku', 'product_type' => 'grouped' ] ], @@ -211,7 +211,7 @@ public function saveDataProvider() ], 'bunch' => [ 'associated_skus' => '', - 'sku' => 'productSku', + 'sku' => 'productsku', 'product_type' => 'grouped' ] ], @@ -219,7 +219,7 @@ public function saveDataProvider() 'skus' => ['newSku' => [],'oldSku' => []], 'bunch' => [ 'associated_skus' => 'sku_assoc1=1, sku_assoc2=2', - 'sku' => 'productSku', + 'sku' => 'productsku', 'product_type' => 'grouped' ] ], @@ -227,13 +227,13 @@ public function saveDataProvider() 'skus' => [ 'newSku' => [ 'sku_assoc1' => ['entity_id' => 1], - 'productSku' => ['entity_id' => 3, 'attr_set_code' => 'Default', 'type_id' => 'grouped'] + 'productsku' => ['entity_id' => 3, 'attr_set_code' => 'Default', 'type_id' => 'grouped'] ], 'oldSku' => [] ], 'bunch' => [ 'associated_skus' => 'sku_assoc1=1', - 'sku' => 'productSku', + 'sku' => 'productsku', 'product_type' => 'simple' ] ] @@ -257,7 +257,7 @@ public function testSaveDataScopeStore() $bunch = [[ 'associated_skus' => 'sku_assoc1=1, sku_assoc2=2', - 'sku' => 'productSku', + 'sku' => 'productsku', 'product_type' => 'grouped' ]]; $this->entityModel->expects($this->at(2))->method('getNextBunch')->will($this->returnValue($bunch)); From 9fb7323cd3952d9a92bc42bb1a180fe2df0775cd Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Thu, 12 Jul 2018 14:29:06 +0300 Subject: [PATCH 0230/1171] Fixes wrong condition for product list category filter --- .../FilterProcessor/ProductCategoryFilter.php | 10 ++++++++-- .../FilterProcessor/ProductCategoryFilterTest.php | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilter.php b/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilter.php index f8cf810ffb570..1f0f2361df507 100644 --- a/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilter.php +++ b/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilter.php @@ -21,8 +21,14 @@ class ProductCategoryFilter implements CustomFilterInterface */ public function apply(Filter $filter, AbstractDb $collection) { - $conditionType = $filter->getConditionType() ?: 'eq'; - $categoryFilter = [$conditionType => [$filter->getValue()]]; + $value = $filter->getValue(); + $conditionType = $filter->getConditionType() ?: 'in'; + if (($conditionType === 'in' || $conditionType === 'nin') && is_string($value)) { + $value = explode(',', $value); + } else { + $value = [$value]; + } + $categoryFilter = [$conditionType => $value]; /** @var Collection $collection */ $collection->addCategoriesFilter($categoryFilter); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilterTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilterTest.php index 942a87ce3414f..2655a0647d825 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilterTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilterTest.php @@ -40,7 +40,7 @@ public function testApply() $collectionMock->expects($this->once()) ->method('addCategoriesFilter') - ->with(['condition' => ['value']]); + ->with(['in' => ['value']]); $this->assertTrue($this->model->apply($filterMock, $collectionMock)); } From f08445f3f635e1bfd7f4d4226f97932bf015bff8 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Thu, 12 Jul 2018 15:02:29 +0300 Subject: [PATCH 0231/1171] MAGETWO-93077: [2.3] Recently Compared Products layout is broken on frontend --- app/code/Magento/Catalog/etc/widget.xml | 2 +- .../templates/product/widget/compared/sidebar.phtml | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/etc/widget.xml b/app/code/Magento/Catalog/etc/widget.xml index a11d206e2ce42..f3b37d7d32e31 100644 --- a/app/code/Magento/Catalog/etc/widget.xml +++ b/app/code/Magento/Catalog/etc/widget.xml @@ -296,7 +296,7 @@ </parameters> <containers> <container name="sidebar.main"> - <template name="default" value="list" /> + <template name="default" value="sidebar" /> </container> <container name="content"> <template name="grid" value="grid" /> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/sidebar.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/sidebar.phtml index f1b2f1a214945..2d2c91aadd473 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/sidebar.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/sidebar.phtml @@ -17,10 +17,8 @@ 'listing' => [ 'displayMode' => 'grid' ], - 'column' => [ - 'image' => [ - 'imageCode' => 'recently_compared_products_images_names_widget' - ] + 'image' => [ + 'imageCode' => 'recently_compared_products_images_names_widget' ] ] ); From ffdd53d1a9f7f86f0433f40779f620646538320d Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Thu, 12 Jul 2018 15:04:31 +0300 Subject: [PATCH 0232/1171] Fixes typo in private getUniqueImageIndex() method --- app/code/Magento/MediaStorage/Service/ImageResize.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/MediaStorage/Service/ImageResize.php b/app/code/Magento/MediaStorage/Service/ImageResize.php index 37dd6f485f1a8..6e3929296e252 100644 --- a/app/code/Magento/MediaStorage/Service/ImageResize.php +++ b/app/code/Magento/MediaStorage/Service/ImageResize.php @@ -202,7 +202,7 @@ private function getViewImages(array $themes): array ]); $images = $config->getMediaEntities('Magento_Catalog', ImageHelper::MEDIA_TYPE_CONFIG_NODE); foreach ($images as $imageId => $imageData) { - $uniqIndex = $this->getUniqImageIndex($imageData); + $uniqIndex = $this->getUniqueImageIndex($imageData); $imageData['id'] = $imageId; $viewImages[$uniqIndex] = $imageData; } @@ -211,11 +211,11 @@ private function getViewImages(array $themes): array } /** - * Get uniq image index + * Get unique image index * @param array $imageData * @return string */ - private function getUniqImageIndex(array $imageData): string + private function getUniqueImageIndex(array $imageData): string { ksort($imageData); unset($imageData['type']); From b1ac916a2b5f960002df2c0e29ff9178930f4bcf Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Thu, 12 Jul 2018 15:05:00 +0300 Subject: [PATCH 0233/1171] =?UTF-8?q?ENGCOM-2216:=20Update=20Israeli=20ZIP?= =?UTF-8?q?=20code=20mask,=207=20digits=20instead=20of=205=20,according=20?= =?UTF-8?q?to=20the=E2=80=A6=20#16613?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Magento/Directory/Model/Country/Postcode/ValidatorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Directory/Model/Country/Postcode/ValidatorTest.php b/dev/tests/integration/testsuite/Magento/Directory/Model/Country/Postcode/ValidatorTest.php index b65aa7734f2da..45d84336337c5 100644 --- a/dev/tests/integration/testsuite/Magento/Directory/Model/Country/Postcode/ValidatorTest.php +++ b/dev/tests/integration/testsuite/Magento/Directory/Model/Country/Postcode/ValidatorTest.php @@ -130,7 +130,7 @@ public function getPostcodesDataProvider() ['countryId' => 'IS', 'postcode' => '123'], ['countryId' => 'IN', 'postcode' => '123456'], ['countryId' => 'ID', 'postcode' => '12345'], - ['countryId' => 'IL', 'postcode' => '12345'], + ['countryId' => 'IL', 'postcode' => '1234567'], ['countryId' => 'IT', 'postcode' => '12345'], ['countryId' => 'JP', 'postcode' => '123-4567'], ['countryId' => 'JP', 'postcode' => '1234567'], From d7ec611684cd69d0cc86fc7ec7e29b2cc9c21278 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Thu, 12 Jul 2018 15:07:39 +0300 Subject: [PATCH 0234/1171] MAGETWO-93193: Drag and drop of attribute through attributes tree on attribute set edit page cause console JS error --- lib/web/extjs/ext-tree.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/web/extjs/ext-tree.js b/lib/web/extjs/ext-tree.js index 35b41260569c2..1b425d9594e70 100644 --- a/lib/web/extjs/ext-tree.js +++ b/lib/web/extjs/ext-tree.js @@ -10,8 +10,7 @@ Ext={};window["undefined"]=window["undefined"];Ext.apply=function(o,c,_3){if(_3) -(function(){var _1;Ext.lib.Dom={getViewWidth:function(_2){return _2?this.getDocumentWidth():this.getViewportWidth();},getViewHeight:function(_3){return _3?this.getDocumentHeight():this.getViewportHeight();},getDocumentHeight:function(){var _4=(document.compatMode!="CSS1Compat")?document.body.scrollHeight:document.documentElement.scrollHeight;return Math.max(_4,this.getViewportHeight());},getDocumentWidth:function(){var _5=(document.compatMode!="CSS1Compat")?document.body.scrollWidth:document.documentElement.scrollWidth;return Math.max(_5,this.getViewportWidth());},getViewportHeight:function(){var _6=self.innerHeight;var _7=document.compatMode;if((_7||Ext.isIE)&&!Ext.isOpera){_6=(_7=="CSS1Compat")?document.documentElement.clientHeight:document.body.clientHeight;}return _6;},getViewportWidth:function(){var _8=self.innerWidth;var _9=document.compatMode;if(_9||Ext.isIE){_8=(_9=="CSS1Compat")?document.documentElement.clientWidth:document.body.clientWidth;}return _8;},isAncestor:function(p,c){p=Ext.getDom(p);c=Ext.getDom(c);if(!p||!c){return false;}if(p.contains&&!Ext.isSafari){return p.contains(c);}else{if(p.compareDocumentPosition){return !!(p.compareDocumentPosition(c)&16);}else{var _c=c.parentNode;while(_c){if(_c==p){return true;}else{if(!_c.tagName||_c.tagName.toUpperCase()=="HTML"){return false;}}_c=_c.parentNode;}return false;}}},getRegion:function(el){return Ext.lib.Region.getRegion(el);},getY:function(el){return this.getXY(el)[1];},getX:function(el){return this.getXY(el)[0];},getXY:function(el){var p,pe,b,_14,bd=document.body;el=Ext.getDom(el);if(el.getBoundingClientRect){b=el.getBoundingClientRect();_14=fly(document).getScroll();return [b.left+_14.left,b.top+_14.top];}else{var x=el.offsetLeft,y=el.offsetTop;p=el.offsetParent;var _18=false;if(p!=el){while(p){x+=p.offsetLeft;y+=p.offsetTop;if(Ext.isSafari&&!_18&&fly(p).getStyle("position")=="absolute"){_18=true;}if(Ext.isGecko){pe=fly(p);var bt=parseInt(pe.getStyle("borderTopWidth"),10)||0;var bl=parseInt(pe.getStyle("borderLeftWidth"),10)||0;x+=bl;y+=bt;if(p!=el&&pe.getStyle("overflow")!="visible"){x+=bl;y+=bt;}}p=p.offsetParent;}}if(Ext.isSafari&&(_18||fly(el).getStyle("position")=="absolute")){x-=bd.offsetLeft;y-=bd.offsetTop;}}p=el.parentNode;while(p&&p!=bd){if(!Ext.isOpera||(Ext.isOpera&&p.tagName!="TR"&&fly(p).getStyle("display")!="inline")){x-=p.scrollLeft;y-=p.scrollTop;}p=p.parentNode;}return [x,y];},setXY:function(el,xy){el=Ext.fly(el,"_setXY");el.position();var pts=el.translatePoints(xy);if(xy[0]!==false){el.dom.style.left=pts.left+"px";}if(xy[1]!==false){el.dom.style.top=pts.top+"px";}},setX:function(el,x){this.setXY(el,[x,false]);},setY:function(el,y){this.setXY(el,[false,y]);}};Ext.lib.Event={getPageX:function(e){return Event.pointerX(e.browserEvent||e);},getPageY:function(e){return Event.pointerY(e.browserEvent||e);},getXY:function(e){e=e.browserEvent||e;return [Event.pointerX(e),Event.pointerY(e)];},getTarget:function(e){return Event.element(e.browserEvent||e);},resolveTextNode:function(_26){if(_26&&3==_26.nodeType){return _26.parentNode;}else{return _26;}},getRelatedTarget:function(ev){ev=ev.browserEvent||ev;var t=ev.relatedTarget;if(!t){if(ev.type=="mouseout"){t=ev.toElement;}else{if(ev.type=="mouseover"){t=ev.fromElement;}}}return this.resolveTextNode(t);},on:function(el,_2a,fn){Event.observe(el,_2a,fn,false);},un:function(el,_2d,fn){Event.stopObserving(el,_2d,fn,false);},purgeElement:function(el){},preventDefault:function(e){e=e.browserEvent||e;if(e.preventDefault){e.preventDefault();}else{e.returnValue=false;}},stopPropagation:function(e){e=e.browserEvent||e;if(e.stopPropagation){e.stopPropagation();}else{e.cancelBubble=true;}},stopEvent:function(e){Event.stop(e.browserEvent||e);},onAvailable:function(el,fn,_35,_36){var _37=new Date(),iid;var f=function(){if(_37.getElapsed()>10000){clearInterval(iid);}var el=document.getElementById(id);if(el){clearInterval(iid);fn.call(_35||window,el);}};iid=setInterval(f,50);}};Ext.lib.Ajax=function(){var _3b=function(cb){return cb.success?function(xhr){cb.success.call(cb.scope||window,{responseText:xhr.responseText,responseXML:xhr.responseXML,argument:cb.argument});}:Ext.emptyFn;};var _3e=function(cb){return cb.failure?function(xhr){cb.failure.call(cb.scope||window,{responseText:xhr.responseText,responseXML:xhr.responseXML,argument:cb.argument});}:Ext.emptyFn;};return {request:function(_41,uri,cb,_44){new Ajax.Request(uri,{method:_41,parameters:_44||"",timeout:cb.timeout,onSuccess:_3b(cb),onFailure:_3e(cb)});},formRequest:function(_45,uri,cb,_48,_49,_4a){new Ajax.Request(uri,{method:Ext.getDom(_45).method||"POST",parameters:Form.serialize(_45)+(_48?"&"+_48:""),timeout:cb.timeout,onSuccess:_3b(cb),onFailure:_3e(cb)});},isCallInProgress:function(_4b){return false;},abort:function(_4c){return false;},serializeForm:function(_4d){return Form.serialize(_4d.dom||_4d,true);}};}();Ext.lib.Anim=function(){var _4e={easeOut:function(pos){return 1-Math.pow(1-pos,2);},easeIn:function(pos){return 1-Math.pow(1-pos,2);}};var _51=function(cb,_53){return {stop:function(_54){this.effect.cancel();},isAnimated:function(){return this.effect.state=="running";},proxyCallback:function(){Ext.callback(cb,_53);}};};return {scroll:function(el,_56,_57,_58,cb,_5a){var _5b=_51(cb,_5a);el=Ext.getDom(el);el.scrollLeft=_56.to[0];el.scrollTop=_56.to[1];_5b.proxyCallback();return _5b;},motion:function(el,_5d,_5e,_5f,cb,_61){return this.run(el,_5d,_5e,_5f,cb,_61);},color:function(el,_63,_64,_65,cb,_67){return this.run(el,_63,_64,_65,cb,_67);},run:function(el,_69,_6a,_6b,cb,_6d,_6e){var o={};for(var k in _69){switch(k){case "points":var by,pts,e=Ext.fly(el,"_animrun");e.position();if(by=_69.points.by){var xy=e.getXY();pts=e.translatePoints([xy[0]+by[0],xy[1]+by[1]]);}else{pts=e.translatePoints(_69.points.to);}o.left=pts.left+"px";o.top=pts.top+"px";break;case "width":o.width=_69.width.to+"px";break;case "height":o.height=_69.height.to+"px";break;case "opacity":o.opacity=String(_69.opacity.to);break;default:o[k]=String(_69[k].to);break;}}var _75=_51(cb,_6d);_75.effect=new Effect.Morph(Ext.id(el),{duration:_6a,afterFinish:_75.proxyCallback,transition:_4e[_6b]||Effect.Transitions.linear,style:o});return _75;}};}();function fly(el){if(!_1){_1=new Ext.Element.Flyweight();}_1.dom=el;return _1;}Ext.lib.Region=function(t,r,b,l){this.top=t;this[1]=t;this.right=r;this.bottom=b;this.left=l;this[0]=l;};Ext.lib.Region.prototype={contains:function(_7b){return (_7b.left>=this.left&&_7b.right<=this.right&&_7b.top>=this.top&&_7b.bottom<=this.bottom);},getArea:function(){return ((this.bottom-this.top)*(this.right-this.left));},intersect:function(_7c){var t=Math.max(this.top,_7c.top);var r=Math.min(this.right,_7c.right);var b=Math.min(this.bottom,_7c.bottom);var l=Math.max(this.left,_7c.left);if(b>=t&&r>=l){return new Ext.lib.Region(t,r,b,l);}else{return null;}},union:function(_81){var t=Math.min(this.top,_81.top);var r=Math.max(this.right,_81.right);var b=Math.max(this.bottom,_81.bottom);var l=Math.min(this.left,_81.left);return new Ext.lib.Region(t,r,b,l);},adjust:function(t,l,b,r){this.top+=t;this.left+=l;this.right+=r;this.bottom+=b;return this;}};Ext.lib.Region.getRegion=function(el){var p=Ext.lib.Dom.getXY(el);var t=p[1];var r=p[0]+el.offsetWidth;var b=p[1]+el.offsetHeight;var l=p[0];return new Ext.lib.Region(t,r,b,l);};Ext.lib.Point=function(x,y){if(x instanceof Array){y=x[1];x=x[0];}this.x=this.right=this.left=this[0]=x;this.y=this.top=this.bottom=this[1]=y;};Ext.lib.Point.prototype=new Ext.lib.Region();if(Ext.isIE){Event.observe(window,"unload",function(){var p=Function.prototype;delete p.createSequence;delete p.defer;delete p.createDelegate;delete p.createCallback;delete p.createInterceptor;});}})(); - +(function(){var _1;Ext.lib.Dom={getViewWidth:function(_2){return _2?this.getDocumentWidth():this.getViewportWidth();},getViewHeight:function(_3){return _3?this.getDocumentHeight():this.getViewportHeight();},getDocumentHeight:function(){var _4=(document.compatMode!="CSS1Compat")?document.body.scrollHeight:document.documentElement.scrollHeight;return Math.max(_4,this.getViewportHeight());},getDocumentWidth:function(){var _5=(document.compatMode!="CSS1Compat")?document.body.scrollWidth:document.documentElement.scrollWidth;return Math.max(_5,this.getViewportWidth());},getViewportHeight:function(){var _6=self.innerHeight;var _7=document.compatMode;if((_7||Ext.isIE)&&!Ext.isOpera){_6=(_7=="CSS1Compat")?document.documentElement.clientHeight:document.body.clientHeight;}return _6;},getViewportWidth:function(){var _8=self.innerWidth;var _9=document.compatMode;if(_9||Ext.isIE){_8=(_9=="CSS1Compat")?document.documentElement.clientWidth:document.body.clientWidth;}return _8;},isAncestor:function(p,c){p=Ext.getDom(p);c=Ext.getDom(c);if(!p||!c){return false;}if(p.contains&&!Ext.isSafari){return p.contains(c);}else{if(p.compareDocumentPosition){return !!(p.compareDocumentPosition(c)&16);}else{var _c=c.parentNode;while(_c){if(_c==p){return true;}else{if(!_c.tagName||_c.tagName.toUpperCase()=="HTML"){return false;}}_c=_c.parentNode;}return false;}}},getRegion:function(el){return Ext.lib.Region.getRegion(el);},getY:function(el){return this.getXY(el)[1];},getX:function(el){return this.getXY(el)[0];},getXY:function(el){var p,pe,b,_14,bd=document.body;el=Ext.getDom(el);if(el.getBoundingClientRect){b=el.getBoundingClientRect();_14=fly(document).getScroll();return [b.left+_14.left,b.top+_14.top];}else{var x=el.offsetLeft,y=el.offsetTop;p=el.offsetParent;var _18=false;if(p!=el){while(p){x+=p.offsetLeft;y+=p.offsetTop;if(Ext.isSafari&&!_18&&fly(p).getStyle("position")=="absolute"){_18=true;}if(Ext.isGecko){pe=fly(p);var bt=parseInt(pe.getStyle("borderTopWidth"),10)||0;var bl=parseInt(pe.getStyle("borderLeftWidth"),10)||0;x+=bl;y+=bt;if(p!=el&&pe.getStyle("overflow")!="visible"){x+=bl;y+=bt;}}p=p.offsetParent;}}if(Ext.isSafari&&(_18||fly(el).getStyle("position")=="absolute")){x-=bd.offsetLeft;y-=bd.offsetTop;}}p=el.parentNode;while(p&&p!=bd){if(!Ext.isOpera||(Ext.isOpera&&p.tagName!="TR"&&fly(p).getStyle("display")!="inline")){x-=p.scrollLeft;y-=p.scrollTop;}p=p.parentNode;}return [x,y];},setXY:function(el,xy){el=Ext.fly(el,"_setXY");el.position();var pts=el.translatePoints(xy);if(xy[0]!==false){el.dom.style.left=pts.left+"px";}if(xy[1]!==false){el.dom.style.top=pts.top+"px";}},setX:function(el,x){this.setXY(el,[x,false]);},setY:function(el,y){this.setXY(el,[false,y]);}};Ext.lib.Event={getPageX:function(e){return Event.pointerX(e.browserEvent||e);},getPageY:function(e){return Event.pointerY(e.browserEvent||e);},getXY:function(e){e=e.browserEvent||e;return [Event.pointerX(e),Event.pointerY(e)];},getTarget:function(e){return Event.element(e.browserEvent||e);},resolveTextNode:function(_26){if(_26&&3==_26.nodeType){return _26.parentNode;}else{return _26;}},getRelatedTarget:function(ev){ev=ev.browserEvent||ev;var t=ev.relatedTarget;if(!t){if(ev.type=="mouseout"){t=ev.toElement;}else{if(ev.type=="mouseover"){t=ev.fromElement;}}}return this.resolveTextNode(t);},on:function(el,_2a,fn){Event.observe(el,_2a,fn,false);},un:function(el,_2d,fn){Event.stopObserving(el,_2d,fn,false);},purgeElement:function(el){},preventDefault:function(e){e=e.browserEvent||e;if(e.preventDefault){e.preventDefault();}else{e.returnValue=false;}},stopPropagation:function(e){e=e.browserEvent||e;if(e.stopPropagation){e.stopPropagation();}else{e.cancelBubble=true;}},stopEvent:function(e){Event.stop(e.browserEvent||e);},onAvailable:function(el,fn,_35,_36){var _37=new Date(),iid;var f=function(){if(_37.getElapsed()>10000){clearInterval(iid);}var el=document.getElementById(id);if(el){clearInterval(iid);fn.call(_35||window,el);}};iid=setInterval(f,50);}};Ext.lib.Ajax=function(){var _3b=function(cb){return cb.success?function(xhr){cb.success.call(cb.scope||window,{responseText:xhr.responseText,responseXML:xhr.responseXML,argument:cb.argument});}:Ext.emptyFn;};var _3e=function(cb){return cb.failure?function(xhr){cb.failure.call(cb.scope||window,{responseText:xhr.responseText,responseXML:xhr.responseXML,argument:cb.argument});}:Ext.emptyFn;};return {request:function(_41,uri,cb,_44){new Ajax.Request(uri,{method:_41,parameters:_44||"",timeout:cb.timeout,onSuccess:_3b(cb),onFailure:_3e(cb)});},formRequest:function(_45,uri,cb,_48,_49,_4a){new Ajax.Request(uri,{method:Ext.getDom(_45).method||"POST",parameters:Form.serialize(_45)+(_48?"&"+_48:""),timeout:cb.timeout,onSuccess:_3b(cb),onFailure:_3e(cb)});},isCallInProgress:function(_4b){return false;},abort:function(_4c){return false;},serializeForm:function(_4d){return Form.serialize(_4d.dom||_4d,true);}};}();Ext.lib.Anim=function(){var _4e={easeOut:function(pos){return 1-Math.pow(1-pos,2);},easeIn:function(pos){return 1-Math.pow(1-pos,2);}};var _51=function(cb,_53){return {stop:function(_54){this.effect.cancel();},isAnimated:function(){return this.effect.state=="running";},proxyCallback:function(){Ext.callback(cb,_53);}};};return {scroll:function(el,_56,_57,_58,cb,_5a){var _5b=_51(cb,_5a);el=Ext.getDom(el);el.scrollLeft=_56.scroll.to[0];el.scrollTop=_56.scroll.to[1];_5b.proxyCallback();return _5b;},motion:function(el,_5d,_5e,_5f,cb,_61){return this.run(el,_5d,_5e,_5f,cb,_61);},color:function(el,_63,_64,_65,cb,_67){return this.run(el,_63,_64,_65,cb,_67);},run:function(el,_69,_6a,_6b,cb,_6d,_6e){var o={};for(var k in _69){switch(k){case "points":var by,pts,e=Ext.fly(el,"_animrun");e.position();if(by=_69.points.by){var xy=e.getXY();pts=e.translatePoints([xy[0]+by[0],xy[1]+by[1]]);}else{pts=e.translatePoints(_69.points.to);}o.left=pts.left+"px";o.top=pts.top+"px";break;case "width":o.width=_69.width.to+"px";break;case "height":o.height=_69.height.to+"px";break;case "opacity":o.opacity=String(_69.opacity.to);break;default:o[k]=String(_69[k].to);break;}}var _75=_51(cb,_6d);_75.effect=new Effect.Morph(Ext.id(el),{duration:_6a,afterFinish:_75.proxyCallback,transition:_4e[_6b]||Effect.Transitions.linear,style:o});return _75;}};}();function fly(el){if(!_1){_1=new Ext.Element.Flyweight();}_1.dom=el;return _1;}Ext.lib.Region=function(t,r,b,l){this.top=t;this[1]=t;this.right=r;this.bottom=b;this.left=l;this[0]=l;};Ext.lib.Region.prototype={contains:function(_7b){return (_7b.left>=this.left&&_7b.right<=this.right&&_7b.top>=this.top&&_7b.bottom<=this.bottom);},getArea:function(){return ((this.bottom-this.top)*(this.right-this.left));},intersect:function(_7c){var t=Math.max(this.top,_7c.top);var r=Math.min(this.right,_7c.right);var b=Math.min(this.bottom,_7c.bottom);var l=Math.max(this.left,_7c.left);if(b>=t&&r>=l){return new Ext.lib.Region(t,r,b,l);}else{return null;}},union:function(_81){var t=Math.min(this.top,_81.top);var r=Math.max(this.right,_81.right);var b=Math.max(this.bottom,_81.bottom);var l=Math.min(this.left,_81.left);return new Ext.lib.Region(t,r,b,l);},adjust:function(t,l,b,r){this.top+=t;this.left+=l;this.right+=r;this.bottom+=b;return this;}};Ext.lib.Region.getRegion=function(el){var p=Ext.lib.Dom.getXY(el);var t=p[1];var r=p[0]+el.offsetWidth;var b=p[1]+el.offsetHeight;var l=p[0];return new Ext.lib.Region(t,r,b,l);};Ext.lib.Point=function(x,y){if(x instanceof Array){y=x[1];x=x[0];}this.x=this.right=this.left=this[0]=x;this.y=this.top=this.bottom=this[1]=y;};Ext.lib.Point.prototype=new Ext.lib.Region();if(Ext.isIE){Event.observe(window,"unload",function(){var p=Function.prototype;delete p.createSequence;delete p.defer;delete p.createDelegate;delete p.createCallback;delete p.createInterceptor;});}})(); Ext.DomHelper=function(){var _1=null;var _2=/^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;var _3=function(o){if(typeof o=="string"){return o;}var b="";if(!o.tag){o.tag="div";}b+="<"+o.tag;for(var _6 in o){if(_6=="tag"||_6=="children"||_6=="cn"||_6=="html"||typeof o[_6]=="function"){continue;}if(_6=="style"){var s=o["style"];if(typeof s=="function"){s=s.call();}if(typeof s=="string"){b+=" style=\""+s+"\"";}else{if(typeof s=="object"){b+=" style=\"";for(var _8 in s){if(typeof s[_8]!="function"){b+=_8+":"+s[_8]+";";}}b+="\"";}}}else{if(_6=="cls"){b+=" class=\""+o["cls"]+"\"";}else{if(_6=="htmlFor"){b+=" for=\""+o["htmlFor"]+"\"";}else{b+=" "+_6+"=\""+o[_6]+"\"";}}}}if(_2.test(o.tag)){b+="/>";}else{b+=">";var cn=o.children||o.cn;if(cn){if(cn instanceof Array){for(var i=0,_b=cn.length;i<_b;i++){b+=_3(cn[i],b);}}else{b+=_3(cn,b);}}if(o.html){b+=o.html;}b+="</"+o.tag+">";}return b;};var _c=function(o,_e){var el=document.createElement(o.tag);var _10=el.setAttribute?true:false;for(var _11 in o){if(_11=="tag"||_11=="children"||_11=="cn"||_11=="html"||_11=="style"||typeof o[_11]=="function"){continue;}if(_11=="cls"){el.className=o["cls"];}else{if(_10){el.setAttribute(_11,o[_11]);}else{el[_11]=o[_11];}}}Ext.DomHelper.applyStyles(el,o.style);var cn=o.children||o.cn;if(cn){if(cn instanceof Array){for(var i=0,len=cn.length;i<len;i++){_c(cn[i],el);}}else{_c(cn,el);}}if(o.html){el.innerHTML=o.html;}if(_e){_e.appendChild(el);}return el;};var _15=function(_16,s,h,e){_1.innerHTML=[s,h,e].join("");var i=-1,el=_1;while(++i<_16){el=el.firstChild;}return el;};var ts="<table>",te="</table>",tbs=ts+"<tbody>",tbe="</tbody>"+te,trs=tbs+"<tr>",tre="</tr>"+tbe;var _22=function(tag,_24,el,_26){if(!_1){_1=document.createElement("div");}var _27;var _28=null;if(tag=="td"){if(_24=="afterbegin"||_24=="beforeend"){return;}if(_24=="beforebegin"){_28=el;el=el.parentNode;}else{_28=el.nextSibling;el=el.parentNode;}_27=_15(4,trs,_26,tre);}else{if(tag=="tr"){if(_24=="beforebegin"){_28=el;el=el.parentNode;_27=_15(3,tbs,_26,tbe);}else{if(_24=="afterend"){_28=el.nextSibling;el=el.parentNode;_27=_15(3,tbs,_26,tbe);}else{if(_24=="afterbegin"){_28=el.firstChild;}_27=_15(4,trs,_26,tre);}}}else{if(tag=="tbody"){if(_24=="beforebegin"){_28=el;el=el.parentNode;_27=_15(2,ts,_26,te);}else{if(_24=="afterend"){_28=el.nextSibling;el=el.parentNode;_27=_15(2,ts,_26,te);}else{if(_24=="afterbegin"){_28=el.firstChild;}_27=_15(3,tbs,_26,tbe);}}}else{if(_24=="beforebegin"||_24=="afterend"){return;}if(_24=="afterbegin"){_28=el.firstChild;}_27=_15(2,ts,_26,te);}}}el.insertBefore(_27,_28);return _27;};return {useDom:false,markup:function(o){return _3(o);},applyStyles:function(el,_2b){if(_2b){el=Ext.fly(el);if(typeof _2b=="string"){var re=/\s?([a-z\-]*)\:\s?([^;]*);?/gi;var _2d;while((_2d=re.exec(_2b))!=null){el.setStyle(_2d[1],_2d[2]);}}else{if(typeof _2b=="object"){for(var _2e in _2b){el.setStyle(_2e,_2b[_2e]);}}else{if(typeof _2b=="function"){Ext.DomHelper.applyStyles(el,_2b.call());}}}}},insertHtml:function(_2f,el,_31){_2f=_2f.toLowerCase();if(el.insertAdjacentHTML){var tag=el.tagName.toLowerCase();if(tag=="table"||tag=="tbody"||tag=="tr"||tag=="td"){var rs;if(rs=_22(tag,_2f,el,_31)){return rs;}}switch(_2f){case "beforebegin":el.insertAdjacentHTML(_2f,_31);return el.previousSibling;case "afterbegin":el.insertAdjacentHTML(_2f,_31);return el.firstChild;case "beforeend":el.insertAdjacentHTML(_2f,_31);return el.lastChild;case "afterend":el.insertAdjacentHTML(_2f,_31);return el.nextSibling;}throw "Illegal insertion point -> \""+_2f+"\"";}var _34=el.ownerDocument.createRange();var _35;switch(_2f){case "beforebegin":_34.setStartBefore(el);_35=_34.createContextualFragment(_31);el.parentNode.insertBefore(_35,el);return el.previousSibling;case "afterbegin":if(el.firstChild){_34.setStartBefore(el.firstChild);_35=_34.createContextualFragment(_31);el.insertBefore(_35,el.firstChild);return el.firstChild;}else{el.innerHTML=_31;return el.firstChild;}case "beforeend":if(el.lastChild){_34.setStartAfter(el.lastChild);_35=_34.createContextualFragment(_31);el.appendChild(_35);return el.lastChild;}else{el.innerHTML=_31;return el.lastChild;}case "afterend":_34.setStartAfter(el);_35=_34.createContextualFragment(_31);el.parentNode.insertBefore(_35,el.nextSibling);return el.nextSibling;}throw "Illegal insertion point -> \""+_2f+"\"";},insertBefore:function(el,o,_38){return this.doInsert(el,o,_38,"beforeBegin");},insertAfter:function(el,o,_3b){return this.doInsert(el,o,_3b,"afterEnd","nextSibling");},insertFirst:function(el,o,_3e){return this.doInsert(el,o,_3e,"afterBegin");},doInsert:function(el,o,_41,pos,_43){el=Ext.getDom(el);var _44;if(this.useDom){_44=_c(o,null);el.parentNode.insertBefore(_44,_43?el[_43]:el);}else{var _45=_3(o);_44=this.insertHtml(pos,el,_45);}return _41?Ext.get(_44,true):_44;},append:function(el,o,_48){el=Ext.getDom(el);var _49;if(this.useDom){_49=_c(o,null);el.appendChild(_49);}else{var _4a=_3(o);_49=this.insertHtml("beforeEnd",el,_4a);}return _48?Ext.get(_49,true):_49;},overwrite:function(el,o,_4d){el=Ext.getDom(el);el.innerHTML=_3(o);return _4d?Ext.get(el.firstChild,true):el.firstChild;},createTemplate:function(o){var _4f=_3(o);return new Ext.Template(_4f);}};}(); From c0d9e1a3379c36172727ed21bb5382e2ea66b281 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <bkorablov@magento.com> Date: Thu, 12 Jul 2018 15:31:15 +0300 Subject: [PATCH 0235/1171] MAGETWO-93161: Fix Problems with Consumer Runners on Cloud Clusters --- .../Model/Cron/ConsumersRunnerTest.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/MessageQueue/Model/Cron/ConsumersRunnerTest.php b/dev/tests/integration/testsuite/Magento/MessageQueue/Model/Cron/ConsumersRunnerTest.php index f362e75ea790e..4acba63d98e66 100644 --- a/dev/tests/integration/testsuite/Magento/MessageQueue/Model/Cron/ConsumersRunnerTest.php +++ b/dev/tests/integration/testsuite/Magento/MessageQueue/Model/Cron/ConsumersRunnerTest.php @@ -130,7 +130,7 @@ public function testCheckThatPidFilesWasCreated() public function testSpecificConsumerAndRerun() { $specificConsumer = 'quoteItemCleaner'; - $pidFilePath = $specificConsumer . ConsumersRunner::PID_FILE_EXT; + $pidFilePath = $this->getPidFileName($specificConsumer); $config = $this->config; $config['cron_consumers_runner'] = ['consumers' => [$specificConsumer], 'max_messages' => 0]; @@ -228,7 +228,7 @@ private function writeConfig(array $config) private function getPidFileFullPath($consumerName) { $directoryList = $this->objectManager->get(DirectoryList::class); - return $directoryList->getPath(DirectoryList::VAR_DIR) . '/' . $consumerName . ConsumersRunner::PID_FILE_EXT; + return $directoryList->getPath(DirectoryList::VAR_DIR) . '/' . $this->getPidFileName($consumerName); } /** @@ -239,7 +239,7 @@ protected function tearDown() foreach ($this->consumerConfig->getConsumers() as $consumer) { $consumerName = $consumer->getName(); $pidFileFullPath = $this->getPidFileFullPath($consumerName); - $pidFilePath = $consumerName . ConsumersRunner::PID_FILE_EXT; + $pidFilePath = $this->getPidFileName($consumerName); $pid = $this->pid->getPid($pidFilePath); if ($pid && $this->pid->isRun($pidFilePath)) { @@ -258,4 +258,15 @@ protected function tearDown() $this->writeConfig($this->config); $this->appConfig->reinit(); } + + /** + * @param string $consumerName The consumers name + * @return string The name to file with PID + */ + private function getPidFileName($consumerName) + { + $sanitizedHostname = preg_replace('/[^a-z0-9]/i', '', gethostname()); + + return $consumerName . '-' . $sanitizedHostname . ConsumersRunner::PID_FILE_EXT; + } } From 69c9a63b1b1e79475546a1095daee0935ca31b7d Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Thu, 12 Jul 2018 15:37:19 +0300 Subject: [PATCH 0236/1171] Moved the else logic before if --- .../FilterProcessor/ProductCategoryFilter.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilter.php b/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilter.php index 1f0f2361df507..8de708dd467c8 100644 --- a/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilter.php +++ b/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilter.php @@ -23,12 +23,11 @@ public function apply(Filter $filter, AbstractDb $collection) { $value = $filter->getValue(); $conditionType = $filter->getConditionType() ?: 'in'; + $filterValue = [$value]; if (($conditionType === 'in' || $conditionType === 'nin') && is_string($value)) { - $value = explode(',', $value); - } else { - $value = [$value]; + $filterValue = explode(',', $value); } - $categoryFilter = [$conditionType => $value]; + $categoryFilter = [$conditionType => $filterValue]; /** @var Collection $collection */ $collection->addCategoriesFilter($categoryFilter); From f8c52ef17c1e43c7cbee1bc1606919cbe2bee704 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Thu, 12 Jul 2018 08:48:22 -0500 Subject: [PATCH 0237/1171] MAGETWO-64854: Incorrect Refund Logic can allow for double-refunds --- .../Mftf/Section/AdminMessagesSection.xml | 1 + .../EnableDisableBundleProductStatusTest.xml | 9 +- .../Section/AdminCreditMemoTotalSection.xml | 2 +- .../AdminOrderCreditMemosTabSection.xml | 1 + .../Framework/Math/FloatComparator.php | 59 +++++++++ .../Math/Test/Unit/FloatComparatorTest.php | 115 ++++++++++++++++++ 6 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 lib/internal/Magento/Framework/Math/FloatComparator.php create mode 100644 lib/internal/Magento/Framework/Math/Test/Unit/FloatComparatorTest.php diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml index cce21ea760c40..ff5e02397cbff 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml @@ -11,5 +11,6 @@ <section name="AdminMessagesSection"> <element name="success" type="text" selector="#messages div.message-success"/> <element name="nthSuccess" type="text" selector=".message.message-success.success:nth-of-type({{n}})>div" parameterized="true"/> + <element name="error" type="text" selector="#messages div.message-error"/> </section> </sections> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml index d967143258918..e1434e22489ce 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml @@ -75,11 +75,16 @@ <seeElement stepKey="LookingForNameOfProduct" selector="{{BundleStorefrontSection.bundleProductName}}"/> <!--Testing disabled view--> - <actionGroup ref="FindProductToEdit" stepKey="FindProductEditPage"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="GoToProductCatalog"/> + <waitForPageLoad stepKey="WaitForCatalogProductPageToLoad"/> + <actionGroup ref="filterProductGridBySku" stepKey="FindProductEditPage"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <click selector="{{AdminDataGridTableSection.rowViewAction('1')}}" stepKey="ClickProductInGrid"/> <click stepKey="ClickOnEnableDisableToggle" selector="{{AdminProductFormBundleSection.enableDisableToggle}}"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButtonAgain"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown2"/> <waitForPageLoad stepKey="PauseForSave"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShownAgain"/> <amOnPage url="{{BundleProduct.urlKey}}.html" stepKey="GoToProductPageAgain"/> <waitForPageLoad stepKey="WaitForProductPageToLoadToShowElement"/> <dontSeeElement stepKey="LookingForNameOfProductTwo" selector="{{BundleStorefrontSection.bundleProductName}}"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml index a49d06545df05..ca5e297b72ffb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminCreditMemoTotalSection"> <element name="subtotalRow" type="text" selector=".order-subtotal-table tbody > tr:nth-of-type({{row}}) td span.price" parameterized="true"/> - <element name="total" type="text" selector="//table[contains(@class,'order-subtotal-table')]/tbody/tr/td[contains(text(), '{{total}}')]/following-sibling::td/span/span[contains(@class, 'price')]" parameterized="true"/> + <element name="total" type="text" selector="//table[contains(@class,'order-subtotal-table')]/tbody/tr/td[contains(text(), '{{total}}')]/following-sibling::td//span[contains(@class, 'price')]" parameterized="true"/> <element name="refundShipping" type="input" selector=".order-subtotal-table tbody input[name='creditmemo[shipping_amount]']"/> <element name="adjustmentRefund" type="input" selector=".order-subtotal-table tbody input[name='creditmemo[adjustment_positive]'"/> <element name="adjustmentFee" type="input" selector=".order-subtotal-table tbody input[name='creditmemo[adjustment_negative]']"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCreditMemosTabSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCreditMemosTabSection.xml index f4835cf02b704..bb0e1618c66e3 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCreditMemosTabSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCreditMemosTabSection.xml @@ -12,5 +12,6 @@ <element name="spinner" type="text" selector="[data-role='spinner'][data-component*='sales_order_view_creditmemo']"/> <element name="gridRow" type="text" selector="#sales_order_view_tabs_order_creditmemos_content .data-grid tbody > tr:nth-of-type({{row}})" parameterized="true"/> <element name="viewGridRow" type="button" selector="#sales_order_view_tabs_order_creditmemos_content .data-grid tbody > tr:nth-of-type({{row}}) a[href*='order_creditmemo/view']" parameterized="true"/> + <element name="gridRowCell" type="text" selector="//div[@id='sales_order_view_tabs_order_creditmemos_content']//tr[{{row}}]//td[count(//div[@id='sales_order_view_tabs_order_creditmemos_content']//tr//th[contains(., '{{column}}')][1]/preceding-sibling::th) +1 ]" parameterized="true"/> </section> </sections> \ No newline at end of file diff --git a/lib/internal/Magento/Framework/Math/FloatComparator.php b/lib/internal/Magento/Framework/Math/FloatComparator.php new file mode 100644 index 0000000000000..4053404369956 --- /dev/null +++ b/lib/internal/Magento/Framework/Math/FloatComparator.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Math; + +/** + * Contains methods to compare float digits. + * + * @api + */ +class FloatComparator +{ + /** + * Precision for floats comparing. + * + * @var float + */ + private static $epsilon = 0.00001; + + /** + * Compares two float digits. + * + * @param float $a + * @param float $b + * @return bool + */ + public function equal(float $a, float $b): bool + { + return abs($a - $b) <= self::$epsilon; + } + + /** + * Compares if the first argument greater than the second argument. + * + * @param float $a + * @param float $b + * @return bool + */ + public function greaterThan(float $a, float $b): bool + { + return ($a - $b) > self::$epsilon; + } + + /** + * Compares if the first argument greater or equal to the second. + * + * @param float $a + * @param float $b + * @return bool + */ + public function greaterThanOrEqual(float $a, float $b): bool + { + return $this->equal($a, $b) || $this->greaterThan($a, $b); + } +} diff --git a/lib/internal/Magento/Framework/Math/Test/Unit/FloatComparatorTest.php b/lib/internal/Magento/Framework/Math/Test/Unit/FloatComparatorTest.php new file mode 100644 index 0000000000000..c9e5143ee789e --- /dev/null +++ b/lib/internal/Magento/Framework/Math/Test/Unit/FloatComparatorTest.php @@ -0,0 +1,115 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Math\Test\Unit; + +use Magento\Framework\Math\FloatComparator; +use PHPUnit\Framework\TestCase; + +class FloatComparatorTest extends TestCase +{ + /** + * @var FloatComparator + */ + private $comparator; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->comparator = new FloatComparator(); + } + + /** + * Checks a case when `a` and `b` are equal. + * + * @param float $a + * @param float $b + * @param bool $expected + * @dataProvider eqDataProvider + */ + public function testEq(float $a, float $b, bool $expected) + { + self::assertEquals($expected, $this->comparator->equal($a, $b)); + } + + /** + * Gets list of variations to compare equal float. + * + * @return array + */ + public function eqDataProvider(): array + { + return [ + [10, 10.00001, true], + [10, 10.000001, true], + [10.0000099, 10.00001, true], + [1, 1.0001, false], + [1, -1.00001, false], + ]; + } + + /** + * Checks a case when `a` > `b`. + * + * @param float $a + * @param float $b + * @param bool $expected + * @dataProvider gtDataProvider + */ + public function testGt(float $a, float $b, bool $expected) + { + self::assertEquals($expected, $this->comparator->greaterThan($a, $b)); + } + + /** + * Gets list of variations to compare if `a` > `b`. + * + * @return array + */ + public function gtDataProvider(): array + { + return [ + [10, 10.00001, false], + [10, 10.000001, false], + [10.0000099, 10.00001, false], + [1.0001, 1, true], + [1, -1.00001, true], + ]; + } + + /** + * Checks a case when `a` >= `b`. + * + * @param float $a + * @param float $b + * @param bool $expected + * @dataProvider gteDataProvider + */ + public function testGte(float $a, float $b, bool $expected) + { + self::assertEquals($expected, $this->comparator->greaterThanOrEqual($a, $b)); + } + + /** + * Gets list of variations to compare if `a` >= `b`. + * + * @return array + */ + public function gteDataProvider(): array + { + return [ + [10, 10.00001, true], + [10, 10.000001, true], + [10.0000099, 10.00001, true], + [1.0001, 1, true], + [1, -1.00001, true], + [1.0001, 1.001, false], + ]; + } +} From bfaac1ffea81e5d12ed5377c6466b7266ed2cf13 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Thu, 12 Jul 2018 17:34:37 +0300 Subject: [PATCH 0238/1171] magento/magento2#16663 [Forwardport] Fixed Issue #11354 Merged CSS file name generation --- lib/internal/Magento/Framework/View/Asset/Merged.php | 0 .../Magento/Framework/View/Test/Unit/Asset/MergedTest.php | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 lib/internal/Magento/Framework/View/Asset/Merged.php mode change 100755 => 100644 lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php diff --git a/lib/internal/Magento/Framework/View/Asset/Merged.php b/lib/internal/Magento/Framework/View/Asset/Merged.php old mode 100755 new mode 100644 diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergedTest.php old mode 100755 new mode 100644 From 792211ba907e1a87b05dc68b74ec9475f08e81c4 Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Thu, 12 Jul 2018 10:52:53 -0500 Subject: [PATCH 0239/1171] MC-230: Customer should be able to see basic bundle product details --- .../Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml index 55721b2166deb..b8093d8dd0b3e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml @@ -16,7 +16,7 @@ <description value="Customer should be able to see basic bundle product details"/> <severity value="CRITICAL"/> <testCaseId value="MC-230"/> - <group value="banana"/> + <group value="Bundle"/> </annotations> <before> <!--Creating Data--> From 708025a38f74bd495ea34f59941a84e53c9a6b39 Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Thu, 12 Jul 2018 11:00:53 -0500 Subject: [PATCH 0240/1171] MC-230: Customer should be able to see basic bundle product details --- .../Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml index b8093d8dd0b3e..45650aae5dff4 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml @@ -78,7 +78,6 @@ <see selector="{{BundleStorefrontSection.shortDescriptionText}}" userInput="This is the short description" stepKey="seeShortDescription"/> <see selector="{{BundleStorefrontSection.longDescriptionText}}" userInput="This is the long description" stepKey="seeLongDescription"/> <click selector="{{BundleStorefrontSection.bundleOptionSelector}}" stepKey="clickOnCustomizationOption"/> - <pauseExecution stepKey="pause"/> <see selector="{{BundleStorefrontSection.bundleOptionSelection('1')}}" userInput="{{BundleProduct.defaultQuantity}} x $$simpleProduct1.name$$" stepKey="seeOption1"/> <see selector="{{BundleStorefrontSection.bundleOptionSelection('2')}}" userInput="{{BundleProduct.defaultQuantity}} x $$simpleProduct2.name$$" stepKey="seeOption2"/> </test> From 5922e8454f59a89f3e07d5cea4d3564b2684df59 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Thu, 12 Jul 2018 11:14:37 -0500 Subject: [PATCH 0241/1171] MC-3091: Improve MFTF tests for Block Configuration Skip test AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest --- ...inAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml index c3797d758d860..50441574d7a0d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml @@ -17,6 +17,8 @@ <description value="Admin should be able to create a CMS page with widget type: Recently Compared Products"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-83792"/> + <!--Skip test due to MC-1284--> + <group value="skip"/> </annotations> <!--Main test--> <before> From 31f089f0b056c28c1c890325014d755c5d00a222 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 12 Jul 2018 11:22:09 -0500 Subject: [PATCH 0242/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - removing object from composite - removing un-necessary methods --- .../Item/ItemResolverComposite.php | 18 +++++++++++++++--- .../Block/Cart/Item/Renderer/Configurable.php | 10 ---------- .../Magento/ConfigurableProduct/etc/di.xml | 2 +- .../Block/Cart/Item/Renderer/Grouped.php | 10 ---------- app/code/Magento/GroupedProduct/etc/di.xml | 2 +- 5 files changed, 17 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php index 9a3169c43e666..de9d67ac34a90 100644 --- a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php +++ b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php @@ -8,17 +8,18 @@ namespace Magento\Catalog\Model\Product\Configuration\Item; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\App\ObjectManager; /** * {@inheritdoc} */ class ItemResolverComposite implements ItemResolverInterface { - /** @var ItemResolverInterface[] */ + /** @var string[] */ private $itemResolvers = []; /** - * @param ItemResolverInterface[] $itemResolvers + * @param string[] $itemResolvers */ public function __construct(array $itemResolvers) { @@ -32,7 +33,7 @@ public function getFinalProduct(ItemInterface $item) : ProductInterface { $product = $item->getProduct(); foreach ($this->itemResolvers as $resolver) { - $resolvedProduct = $resolver->getFinalProduct($item); + $resolvedProduct = $this->getItemResolverInstance($resolver)->getFinalProduct($item); if ($resolvedProduct !== $product) { $product = $resolvedProduct; break; @@ -40,4 +41,15 @@ public function getFinalProduct(ItemInterface $item) : ProductInterface } return $product; } + + /** + * Get the instance of the item resolver by class name + * + * @param string $className + * @return ItemResolverInterface + */ + private function getItemResolverInstance(string $className) + { + return ObjectManager::getInstance()->get($className); + } } diff --git a/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php b/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php index 714d2eb18a434..3b657dd1ab2d0 100644 --- a/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php @@ -57,16 +57,6 @@ public function getOptionList() return $this->_productConfig->getOptions($this->getItem()); } - /** - * {@inheritdoc} - * @deprecated because now parent handles the logic for images of all product types - * @see \Magento\Checkout\Block\Cart\Item\Renderer::getProductForThumbnail - */ - public function getProductForThumbnail() - { - return parent::getProductForThumbnail(); - } - /** * Return identifiers for produced content * diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml index 2e6dbdf277477..bba9cdae0debb 100644 --- a/app/code/Magento/ConfigurableProduct/etc/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/di.xml @@ -208,7 +208,7 @@ <type name="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverComposite"> <arguments> <argument name="itemResolvers" xsi:type="array"> - <item name="configurable" xsi:type="object">Magento\ConfigurableProduct\Model\Product\Configuration\Item\ItemProductResolver</item> + <item name="configurable" xsi:type="string">Magento\ConfigurableProduct\Model\Product\Configuration\Item\ItemProductResolver</item> </argument> </arguments> </type> diff --git a/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php b/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php index 4300ae1033e51..e85d4eeca730a 100644 --- a/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php +++ b/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php @@ -38,16 +38,6 @@ public function getGroupedProduct() return $this->getProduct(); } - /** - * {@inheritdoc} - * @deprecated because now parent handles the logic for images of all product types - * @see \Magento\Checkout\Block\Cart\Item\Renderer::getProductForThumbnail - */ - public function getProductForThumbnail() - { - return parent::getProductForThumbnail(); - } - /** * Return identifiers for produced content * diff --git a/app/code/Magento/GroupedProduct/etc/di.xml b/app/code/Magento/GroupedProduct/etc/di.xml index 684787a45bbed..0371ef2386eb7 100644 --- a/app/code/Magento/GroupedProduct/etc/di.xml +++ b/app/code/Magento/GroupedProduct/etc/di.xml @@ -103,7 +103,7 @@ <type name="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverComposite"> <arguments> <argument name="itemResolvers" xsi:type="array"> - <item name="grouped" xsi:type="object">Magento\GroupedProduct\Model\Product\Configuration\Item\ItemProductResolver</item> + <item name="grouped" xsi:type="string">Magento\GroupedProduct\Model\Product\Configuration\Item\ItemProductResolver</item> </argument> </arguments> </type> From 577e8ddd601f9d924f3d2b728b3944fd12433519 Mon Sep 17 00:00:00 2001 From: Andreas von Studnitz <avs@integer-net.de> Date: Tue, 27 Feb 2018 12:04:35 +0100 Subject: [PATCH 0243/1171] Add a link to the success message when adding a product to the compare list --- .../Catalog/Controller/Product/Compare/Add.php | 9 ++++++++- app/code/Magento/Catalog/etc/frontend/di.xml | 14 ++++++++++++++ .../messages/addCompareAddSuccessMessage.phtml | 14 ++++++++++++++ .../Catalog/Controller/Product/CompareTest.php | 2 +- 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Catalog/view/frontend/templates/messages/addCompareAddSuccessMessage.phtml diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php index 89eb6c9be929f..f7e163f53e6b2 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php @@ -36,7 +36,14 @@ public function execute() $productName = $this->_objectManager->get( \Magento\Framework\Escaper::class )->escapeHtml($product->getName()); - $this->messageManager->addSuccess(__('You added product %1 to the comparison list.', $productName)); + $this->messageManager->addComplexSuccessMessage( + 'addCompareAddSuccessMessage', + [ + 'product_name' => $productName, + 'compare_list_url' => $this->_url->getUrl('catalog/product_compare') + ] + ); + $this->_eventManager->dispatch('catalog_product_compare_add_product', ['product' => $product]); } diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index 659ba2b731366..49a95aece4611 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -79,6 +79,7 @@ <argument name="typeId" xsi:type="string">recently_compared_product</argument> </arguments> </virtualType> +<<<<<<< HEAD <type name="Magento\Catalog\Block\Product\View\Gallery"> <arguments> <argument name="galleryImagesConfig" xsi:type="array"> @@ -96,11 +97,24 @@ <item name="image_id" xsi:type="string">product_page_image_large</item> <item name="data_object_key" xsi:type="string">large_image_url</item> <item name="json_object_key" xsi:type="string">full</item> +======= + <type name="Magento\Framework\View\Element\Message\MessageConfigurationsPool"> + <arguments> + <argument name="configurationsMap" xsi:type="array"> + <item name="addCompareAddSuccessMessage" xsi:type="array"> + <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> + <item name="data" xsi:type="array"> + <item name="template" xsi:type="string">Magento_Catalog::messages/addCompareAddSuccessMessage.phtml</item> + </item> +>>>>>>> edd6062d2f4... Add a link to the success message when adding a product to the compare list </item> </argument> </arguments> </type> +<<<<<<< HEAD <type name="Magento\Framework\App\ResourceConnection"> <plugin name="get_catalog_category_product_index_table_name" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\TableResolver"/> </type> +======= +>>>>>>> edd6062d2f4... Add a link to the success message when adding a product to the compare list </config> diff --git a/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareAddSuccessMessage.phtml b/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareAddSuccessMessage.phtml new file mode 100644 index 0000000000000..5f44c42e17c57 --- /dev/null +++ b/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareAddSuccessMessage.phtml @@ -0,0 +1,14 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +// @codingStandardsIgnoreFile +/** @var \Magento\Framework\View\Element\Template $block */ +?> +<?= $block->escapeHtml(__( + 'You added product %1 to the <a href="%2">comparison list</a>.', + $block->getData('product_name'), + $block->getData('compare_list_url')), + ['a'] +); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php index cc04e48adb620..8587a011365d3 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php @@ -48,7 +48,7 @@ public function testAddAction() ); $this->assertSessionMessages( - $this->equalTo(['You added product Simple Product 1 Name to the comparison list.']), + $this->equalTo(['You added product Simple Product 1 Name to the <a href="http://localhost/index.php/catalog/product_compare/">comparison list</a>.']), MessageInterface::TYPE_SUCCESS ); From de0e8d1fee9a51f15af097d54ea63fd914ec7e56 Mon Sep 17 00:00:00 2001 From: Andreas von Studnitz <avs@integer-net.de> Date: Tue, 27 Feb 2018 13:20:03 +0100 Subject: [PATCH 0244/1171] Renaming to avoid duplication --- app/code/Magento/Catalog/Controller/Product/Compare/Add.php | 2 +- app/code/Magento/Catalog/etc/frontend/di.xml | 4 ++-- ...AddSuccessMessage.phtml => addCompareSuccessMessage.phtml} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename app/code/Magento/Catalog/view/frontend/templates/messages/{addCompareAddSuccessMessage.phtml => addCompareSuccessMessage.phtml} (100%) diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php index f7e163f53e6b2..eb9cc83125541 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php @@ -37,7 +37,7 @@ public function execute() \Magento\Framework\Escaper::class )->escapeHtml($product->getName()); $this->messageManager->addComplexSuccessMessage( - 'addCompareAddSuccessMessage', + 'addCompareSuccessMessage', [ 'product_name' => $productName, 'compare_list_url' => $this->_url->getUrl('catalog/product_compare') diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index 49a95aece4611..17e9e334cb074 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -101,10 +101,10 @@ <type name="Magento\Framework\View\Element\Message\MessageConfigurationsPool"> <arguments> <argument name="configurationsMap" xsi:type="array"> - <item name="addCompareAddSuccessMessage" xsi:type="array"> + <item name="addCompareSuccessMessage" xsi:type="array"> <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> <item name="data" xsi:type="array"> - <item name="template" xsi:type="string">Magento_Catalog::messages/addCompareAddSuccessMessage.phtml</item> + <item name="template" xsi:type="string">Magento_Catalog::messages/addCompareSuccessMessage.phtml</item> </item> >>>>>>> edd6062d2f4... Add a link to the success message when adding a product to the compare list </item> diff --git a/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareAddSuccessMessage.phtml b/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareSuccessMessage.phtml similarity index 100% rename from app/code/Magento/Catalog/view/frontend/templates/messages/addCompareAddSuccessMessage.phtml rename to app/code/Magento/Catalog/view/frontend/templates/messages/addCompareSuccessMessage.phtml From a031ef562d58b4e86237ea2045c3442b0a74b92b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 12 Jul 2018 19:36:55 +0300 Subject: [PATCH 0245/1171] fix _utilities.less --- lib/web/css/source/lib/_utilities.less | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/web/css/source/lib/_utilities.less b/lib/web/css/source/lib/_utilities.less index 08a82494b3e5c..222eb4741e85e 100644 --- a/lib/web/css/source/lib/_utilities.less +++ b/lib/web/css/source/lib/_utilities.less @@ -260,9 +260,8 @@ @_line-height: normal ) { .lib-font-size(@_font-size); - font-size: @_font-size; + .lib-line-height(@_line-height); letter-spacing: normal; - line-height: @_line-height; } // From f225505cca9a885f67b99dd410b31d7aee337cff Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Thu, 12 Jul 2018 22:08:32 +0530 Subject: [PATCH 0246/1171] Merge branch '2.2-develop' into compare-success-message --- app/code/Magento/Catalog/etc/frontend/di.xml | 27 ++------------------ 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index 17e9e334cb074..880b367cfa6e0 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -79,42 +79,19 @@ <argument name="typeId" xsi:type="string">recently_compared_product</argument> </arguments> </virtualType> -<<<<<<< HEAD - <type name="Magento\Catalog\Block\Product\View\Gallery"> - <arguments> - <argument name="galleryImagesConfig" xsi:type="array"> - <item name="small_image" xsi:type="array"> - <item name="image_id" xsi:type="string">product_page_image_small</item> - <item name="data_object_key" xsi:type="string">small_image_url</item> - <item name="json_object_key" xsi:type="string">thumb</item> - </item> - <item name="medium_image" xsi:type="array"> - <item name="image_id" xsi:type="string">product_page_image_medium</item> - <item name="data_object_key" xsi:type="string">medium_image_url</item> - <item name="json_object_key" xsi:type="string">img</item> - </item> - <item name="large_image" xsi:type="array"> - <item name="image_id" xsi:type="string">product_page_image_large</item> - <item name="data_object_key" xsi:type="string">large_image_url</item> - <item name="json_object_key" xsi:type="string">full</item> -======= <type name="Magento\Framework\View\Element\Message\MessageConfigurationsPool"> <arguments> <argument name="configurationsMap" xsi:type="array"> - <item name="addCompareSuccessMessage" xsi:type="array"> + <item name="addCompareAddSuccessMessage" xsi:type="array"> <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> <item name="data" xsi:type="array"> - <item name="template" xsi:type="string">Magento_Catalog::messages/addCompareSuccessMessage.phtml</item> + <item name="template" xsi:type="string">Magento_Catalog::messages/addCompareAddSuccessMessage.phtml</item> </item> ->>>>>>> edd6062d2f4... Add a link to the success message when adding a product to the compare list </item> </argument> </arguments> </type> -<<<<<<< HEAD <type name="Magento\Framework\App\ResourceConnection"> <plugin name="get_catalog_category_product_index_table_name" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\TableResolver"/> </type> -======= ->>>>>>> edd6062d2f4... Add a link to the success message when adding a product to the compare list </config> From 2099950f7a583c65e80a8ecd6e2d49a35f52f7b1 Mon Sep 17 00:00:00 2001 From: Devagouda <dpatil@magento.com> Date: Thu, 12 Jul 2018 11:46:35 -0500 Subject: [PATCH 0247/1171] MAGETWO-91465:Once integer is stored for State/Province field, it can not be changed to alphanumeric -Added functional test to cover bug fix --- .../Mftf/Section/CheckoutShippingSection.xml | 1 + ...ldShouldNotAcceptJustIntegerValuesTest.xml | 52 +++++++++++++++++++ .../Customer/Test/Mftf/Data/AddressData.xml | 4 ++ 3 files changed, 57 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml index c20309814d51d..136658cc59106 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml @@ -31,5 +31,6 @@ <element name="next" type="button" selector="button.button.action.continue.primary" timeout="30"/> <element name="firstShippingMethod" type="radio" selector="//*[@id='checkout-shipping-method-load']//input[@class='radio']"/> <element name="defaultShipping" type="button" selector=".billing-address-details"/> + <element name="stateInput" type="input" selector="input[name=region]"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml new file mode 100644 index 0000000000000..add1a1b1cf9be --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AddressStateFieldShouldNotAcceptJustIntegerValuesTest"> + <annotations> + <features value="Checkout"/> + <stories value="MAGETWO-91465"/> + <title value="Guest Checkout"/> + <description value="Address State field should not allow just integer values"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-93203"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + </after> + + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" /> + <selectOption stepKey="selectCounty" selector="{{CheckoutShippingSection.country}}" userInput="{{UK_Address.country_id}}"/> + <waitForPageLoad stepKey="waitFormToReload"/> + <fillField selector="{{CheckoutShippingSection.stateInput}}" userInput="1" stepKey="enterStateAsIntegerValue"/> + <waitForPageLoad stepKey="waitforFormValidation"/> + <see userInput="First character must be letter." stepKey="seeTheErrorMessageDisplayed"/> + <fillField selector="{{CheckoutShippingSection.stateInput}}" userInput=" 1" stepKey="enterStateAsIntegerValue1"/> + <waitForPageLoad stepKey="waitforFormValidation1"/> + <see userInput="First character must be letter." stepKey="seeTheErrorMessageDisplayed1"/> + <fillField selector="{{CheckoutShippingSection.stateInput}}" userInput="ABC1" stepKey="enterStateAsIntegerValue2"/> + <waitForPageLoad stepKey="waitforFormValidation2"/> + <dontSee userInput="First character must be letter." stepKey="seeTheErrorMessageIsNotDisplayed"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 19194ae2e5423..a1f0277ec40ec 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -83,4 +83,8 @@ <data key="default_shipping">Yes</data> <requiredEntity type="region">RegionCA</requiredEntity> </entity> + <!--If required other field can be added to UK_Address entity, dont modify any existing data--> + <entity name="UK_Address" type="address"> + <data key="country_id">GB</data> + </entity> </entities> From 11284467c033bccd5a685897b07c3d4237b2432f Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Thu, 12 Jul 2018 20:59:38 +0300 Subject: [PATCH 0248/1171] Adjusting Unit Test --- .../FilterProcessor/ProductCategoryFilterTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilterTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilterTest.php index 2655a0647d825..157c72fcedf10 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilterTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor/ProductCategoryFilterTest.php @@ -40,7 +40,7 @@ public function testApply() $collectionMock->expects($this->once()) ->method('addCategoriesFilter') - ->with(['in' => ['value']]); + ->with(['condition' => ['value']]); $this->assertTrue($this->model->apply($filterMock, $collectionMock)); } @@ -66,7 +66,7 @@ public function testApplyWithoutCondition() $collectionMock->expects($this->once()) ->method('addCategoriesFilter') - ->with(['eq' => ['value']]); + ->with(['in' => ['value']]); $this->assertTrue($this->model->apply($filterMock, $collectionMock)); } From 392d586e769edd3ea6119ca2052f2a043af06cb3 Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Thu, 12 Jul 2018 13:36:44 -0500 Subject: [PATCH 0249/1171] MC-230: Customer should be able to see basic bundle product details --- .../Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml index 45650aae5dff4..9a04bfd06c899 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml @@ -39,6 +39,7 @@ <!--Add description--> <click selector="{{AdminProductFormBundleSection.contentDropDown}}" stepKey="openDescriptionDropDown"/> + <scrollTo selector="{{AdminProductFormBundleSection.contentDropDown}}" stepKey="scrollToError"/> <fillField selector="{{AdminProductFormBundleSection.longDescription}}" userInput="This is the long description" stepKey="fillLongDescription"/> <fillField selector="{{AdminProductFormBundleSection.shortDescription}}" userInput="This is the short description" stepKey="fillShortDescription"/> From e25bf574cb589e717ed350a0cb4c03a7fe349b92 Mon Sep 17 00:00:00 2001 From: Oleh Posyniak <oposyniak@magento.com> Date: Thu, 12 Jul 2018 13:38:47 -0500 Subject: [PATCH 0250/1171] MAGETWO-93062: Cron Schedule table is growing so fast --- app/code/Magento/Cron/etc/cron_groups.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Cron/etc/cron_groups.xml b/app/code/Magento/Cron/etc/cron_groups.xml index a01426eab723e..9aa57662427c8 100644 --- a/app/code/Magento/Cron/etc/cron_groups.xml +++ b/app/code/Magento/Cron/etc/cron_groups.xml @@ -11,8 +11,8 @@ <schedule_ahead_for>20</schedule_ahead_for> <schedule_lifetime>15</schedule_lifetime> <history_cleanup_every>10</history_cleanup_every> - <history_success_lifetime>10080</history_success_lifetime> - <history_failure_lifetime>10080</history_failure_lifetime> + <history_success_lifetime>60</history_success_lifetime> + <history_failure_lifetime>4320</history_failure_lifetime> <use_separate_process>0</use_separate_process> </group> </config> From f44f2d9749e452e8e4c831cb5209919bc99ddae5 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Fri, 13 Jul 2018 00:09:42 -0500 Subject: [PATCH 0251/1171] MC-3065: Automate MFTF for MC-1364 --- app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml | 2 +- app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml b/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml index 73e2d6256ce61..5df8ebab2d380 100644 --- a/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml +++ b/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml @@ -27,7 +27,7 @@ <data key="identifier" unique="suffix">testpage-</data> </entity> <entity name="simpleCmsPage" type="cms_page"> - <data key="title">Test CMS Page</data> + <data key="title" unique="suffix">Test CMS Page</data> <data key="content_heading">Test Content Heading</data> <data key="content">Sample page content. Yada yada yada.</data> <data key="identifier" unique="suffix">test-page-</data> diff --git a/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml b/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml index e436207561420..0e1a6f4717c6b 100644 --- a/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml +++ b/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml @@ -17,7 +17,7 @@ <severity value="CRITICAL"/> <useCaseId value="MC-1364"/> <testCaseId value="MC-3178"/> - <group value="alo"/> + <group value="configuration"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> From f1a8606ae989eab1f2ce55905379c3ddf72cf5dd Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 13 Jul 2018 10:05:04 +0300 Subject: [PATCH 0252/1171] MAGETWO-93193: Drag and drop of attribute through attributes tree on attribute set edit page cause console JS error --- lib/web/extjs/ext-tree.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/extjs/ext-tree.js b/lib/web/extjs/ext-tree.js index 1b425d9594e70..362e928c541c9 100644 --- a/lib/web/extjs/ext-tree.js +++ b/lib/web/extjs/ext-tree.js @@ -13,6 +13,7 @@ Ext={};window["undefined"]=window["undefined"];Ext.apply=function(o,c,_3){if(_3) (function(){var _1;Ext.lib.Dom={getViewWidth:function(_2){return _2?this.getDocumentWidth():this.getViewportWidth();},getViewHeight:function(_3){return _3?this.getDocumentHeight():this.getViewportHeight();},getDocumentHeight:function(){var _4=(document.compatMode!="CSS1Compat")?document.body.scrollHeight:document.documentElement.scrollHeight;return Math.max(_4,this.getViewportHeight());},getDocumentWidth:function(){var _5=(document.compatMode!="CSS1Compat")?document.body.scrollWidth:document.documentElement.scrollWidth;return Math.max(_5,this.getViewportWidth());},getViewportHeight:function(){var _6=self.innerHeight;var _7=document.compatMode;if((_7||Ext.isIE)&&!Ext.isOpera){_6=(_7=="CSS1Compat")?document.documentElement.clientHeight:document.body.clientHeight;}return _6;},getViewportWidth:function(){var _8=self.innerWidth;var _9=document.compatMode;if(_9||Ext.isIE){_8=(_9=="CSS1Compat")?document.documentElement.clientWidth:document.body.clientWidth;}return _8;},isAncestor:function(p,c){p=Ext.getDom(p);c=Ext.getDom(c);if(!p||!c){return false;}if(p.contains&&!Ext.isSafari){return p.contains(c);}else{if(p.compareDocumentPosition){return !!(p.compareDocumentPosition(c)&16);}else{var _c=c.parentNode;while(_c){if(_c==p){return true;}else{if(!_c.tagName||_c.tagName.toUpperCase()=="HTML"){return false;}}_c=_c.parentNode;}return false;}}},getRegion:function(el){return Ext.lib.Region.getRegion(el);},getY:function(el){return this.getXY(el)[1];},getX:function(el){return this.getXY(el)[0];},getXY:function(el){var p,pe,b,_14,bd=document.body;el=Ext.getDom(el);if(el.getBoundingClientRect){b=el.getBoundingClientRect();_14=fly(document).getScroll();return [b.left+_14.left,b.top+_14.top];}else{var x=el.offsetLeft,y=el.offsetTop;p=el.offsetParent;var _18=false;if(p!=el){while(p){x+=p.offsetLeft;y+=p.offsetTop;if(Ext.isSafari&&!_18&&fly(p).getStyle("position")=="absolute"){_18=true;}if(Ext.isGecko){pe=fly(p);var bt=parseInt(pe.getStyle("borderTopWidth"),10)||0;var bl=parseInt(pe.getStyle("borderLeftWidth"),10)||0;x+=bl;y+=bt;if(p!=el&&pe.getStyle("overflow")!="visible"){x+=bl;y+=bt;}}p=p.offsetParent;}}if(Ext.isSafari&&(_18||fly(el).getStyle("position")=="absolute")){x-=bd.offsetLeft;y-=bd.offsetTop;}}p=el.parentNode;while(p&&p!=bd){if(!Ext.isOpera||(Ext.isOpera&&p.tagName!="TR"&&fly(p).getStyle("display")!="inline")){x-=p.scrollLeft;y-=p.scrollTop;}p=p.parentNode;}return [x,y];},setXY:function(el,xy){el=Ext.fly(el,"_setXY");el.position();var pts=el.translatePoints(xy);if(xy[0]!==false){el.dom.style.left=pts.left+"px";}if(xy[1]!==false){el.dom.style.top=pts.top+"px";}},setX:function(el,x){this.setXY(el,[x,false]);},setY:function(el,y){this.setXY(el,[false,y]);}};Ext.lib.Event={getPageX:function(e){return Event.pointerX(e.browserEvent||e);},getPageY:function(e){return Event.pointerY(e.browserEvent||e);},getXY:function(e){e=e.browserEvent||e;return [Event.pointerX(e),Event.pointerY(e)];},getTarget:function(e){return Event.element(e.browserEvent||e);},resolveTextNode:function(_26){if(_26&&3==_26.nodeType){return _26.parentNode;}else{return _26;}},getRelatedTarget:function(ev){ev=ev.browserEvent||ev;var t=ev.relatedTarget;if(!t){if(ev.type=="mouseout"){t=ev.toElement;}else{if(ev.type=="mouseover"){t=ev.fromElement;}}}return this.resolveTextNode(t);},on:function(el,_2a,fn){Event.observe(el,_2a,fn,false);},un:function(el,_2d,fn){Event.stopObserving(el,_2d,fn,false);},purgeElement:function(el){},preventDefault:function(e){e=e.browserEvent||e;if(e.preventDefault){e.preventDefault();}else{e.returnValue=false;}},stopPropagation:function(e){e=e.browserEvent||e;if(e.stopPropagation){e.stopPropagation();}else{e.cancelBubble=true;}},stopEvent:function(e){Event.stop(e.browserEvent||e);},onAvailable:function(el,fn,_35,_36){var _37=new Date(),iid;var f=function(){if(_37.getElapsed()>10000){clearInterval(iid);}var el=document.getElementById(id);if(el){clearInterval(iid);fn.call(_35||window,el);}};iid=setInterval(f,50);}};Ext.lib.Ajax=function(){var _3b=function(cb){return cb.success?function(xhr){cb.success.call(cb.scope||window,{responseText:xhr.responseText,responseXML:xhr.responseXML,argument:cb.argument});}:Ext.emptyFn;};var _3e=function(cb){return cb.failure?function(xhr){cb.failure.call(cb.scope||window,{responseText:xhr.responseText,responseXML:xhr.responseXML,argument:cb.argument});}:Ext.emptyFn;};return {request:function(_41,uri,cb,_44){new Ajax.Request(uri,{method:_41,parameters:_44||"",timeout:cb.timeout,onSuccess:_3b(cb),onFailure:_3e(cb)});},formRequest:function(_45,uri,cb,_48,_49,_4a){new Ajax.Request(uri,{method:Ext.getDom(_45).method||"POST",parameters:Form.serialize(_45)+(_48?"&"+_48:""),timeout:cb.timeout,onSuccess:_3b(cb),onFailure:_3e(cb)});},isCallInProgress:function(_4b){return false;},abort:function(_4c){return false;},serializeForm:function(_4d){return Form.serialize(_4d.dom||_4d,true);}};}();Ext.lib.Anim=function(){var _4e={easeOut:function(pos){return 1-Math.pow(1-pos,2);},easeIn:function(pos){return 1-Math.pow(1-pos,2);}};var _51=function(cb,_53){return {stop:function(_54){this.effect.cancel();},isAnimated:function(){return this.effect.state=="running";},proxyCallback:function(){Ext.callback(cb,_53);}};};return {scroll:function(el,_56,_57,_58,cb,_5a){var _5b=_51(cb,_5a);el=Ext.getDom(el);el.scrollLeft=_56.scroll.to[0];el.scrollTop=_56.scroll.to[1];_5b.proxyCallback();return _5b;},motion:function(el,_5d,_5e,_5f,cb,_61){return this.run(el,_5d,_5e,_5f,cb,_61);},color:function(el,_63,_64,_65,cb,_67){return this.run(el,_63,_64,_65,cb,_67);},run:function(el,_69,_6a,_6b,cb,_6d,_6e){var o={};for(var k in _69){switch(k){case "points":var by,pts,e=Ext.fly(el,"_animrun");e.position();if(by=_69.points.by){var xy=e.getXY();pts=e.translatePoints([xy[0]+by[0],xy[1]+by[1]]);}else{pts=e.translatePoints(_69.points.to);}o.left=pts.left+"px";o.top=pts.top+"px";break;case "width":o.width=_69.width.to+"px";break;case "height":o.height=_69.height.to+"px";break;case "opacity":o.opacity=String(_69.opacity.to);break;default:o[k]=String(_69[k].to);break;}}var _75=_51(cb,_6d);_75.effect=new Effect.Morph(Ext.id(el),{duration:_6a,afterFinish:_75.proxyCallback,transition:_4e[_6b]||Effect.Transitions.linear,style:o});return _75;}};}();function fly(el){if(!_1){_1=new Ext.Element.Flyweight();}_1.dom=el;return _1;}Ext.lib.Region=function(t,r,b,l){this.top=t;this[1]=t;this.right=r;this.bottom=b;this.left=l;this[0]=l;};Ext.lib.Region.prototype={contains:function(_7b){return (_7b.left>=this.left&&_7b.right<=this.right&&_7b.top>=this.top&&_7b.bottom<=this.bottom);},getArea:function(){return ((this.bottom-this.top)*(this.right-this.left));},intersect:function(_7c){var t=Math.max(this.top,_7c.top);var r=Math.min(this.right,_7c.right);var b=Math.min(this.bottom,_7c.bottom);var l=Math.max(this.left,_7c.left);if(b>=t&&r>=l){return new Ext.lib.Region(t,r,b,l);}else{return null;}},union:function(_81){var t=Math.min(this.top,_81.top);var r=Math.max(this.right,_81.right);var b=Math.max(this.bottom,_81.bottom);var l=Math.min(this.left,_81.left);return new Ext.lib.Region(t,r,b,l);},adjust:function(t,l,b,r){this.top+=t;this.left+=l;this.right+=r;this.bottom+=b;return this;}};Ext.lib.Region.getRegion=function(el){var p=Ext.lib.Dom.getXY(el);var t=p[1];var r=p[0]+el.offsetWidth;var b=p[1]+el.offsetHeight;var l=p[0];return new Ext.lib.Region(t,r,b,l);};Ext.lib.Point=function(x,y){if(x instanceof Array){y=x[1];x=x[0];}this.x=this.right=this.left=this[0]=x;this.y=this.top=this.bottom=this[1]=y;};Ext.lib.Point.prototype=new Ext.lib.Region();if(Ext.isIE){Event.observe(window,"unload",function(){var p=Function.prototype;delete p.createSequence;delete p.defer;delete p.createDelegate;delete p.createCallback;delete p.createInterceptor;});}})(); + Ext.DomHelper=function(){var _1=null;var _2=/^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;var _3=function(o){if(typeof o=="string"){return o;}var b="";if(!o.tag){o.tag="div";}b+="<"+o.tag;for(var _6 in o){if(_6=="tag"||_6=="children"||_6=="cn"||_6=="html"||typeof o[_6]=="function"){continue;}if(_6=="style"){var s=o["style"];if(typeof s=="function"){s=s.call();}if(typeof s=="string"){b+=" style=\""+s+"\"";}else{if(typeof s=="object"){b+=" style=\"";for(var _8 in s){if(typeof s[_8]!="function"){b+=_8+":"+s[_8]+";";}}b+="\"";}}}else{if(_6=="cls"){b+=" class=\""+o["cls"]+"\"";}else{if(_6=="htmlFor"){b+=" for=\""+o["htmlFor"]+"\"";}else{b+=" "+_6+"=\""+o[_6]+"\"";}}}}if(_2.test(o.tag)){b+="/>";}else{b+=">";var cn=o.children||o.cn;if(cn){if(cn instanceof Array){for(var i=0,_b=cn.length;i<_b;i++){b+=_3(cn[i],b);}}else{b+=_3(cn,b);}}if(o.html){b+=o.html;}b+="</"+o.tag+">";}return b;};var _c=function(o,_e){var el=document.createElement(o.tag);var _10=el.setAttribute?true:false;for(var _11 in o){if(_11=="tag"||_11=="children"||_11=="cn"||_11=="html"||_11=="style"||typeof o[_11]=="function"){continue;}if(_11=="cls"){el.className=o["cls"];}else{if(_10){el.setAttribute(_11,o[_11]);}else{el[_11]=o[_11];}}}Ext.DomHelper.applyStyles(el,o.style);var cn=o.children||o.cn;if(cn){if(cn instanceof Array){for(var i=0,len=cn.length;i<len;i++){_c(cn[i],el);}}else{_c(cn,el);}}if(o.html){el.innerHTML=o.html;}if(_e){_e.appendChild(el);}return el;};var _15=function(_16,s,h,e){_1.innerHTML=[s,h,e].join("");var i=-1,el=_1;while(++i<_16){el=el.firstChild;}return el;};var ts="<table>",te="</table>",tbs=ts+"<tbody>",tbe="</tbody>"+te,trs=tbs+"<tr>",tre="</tr>"+tbe;var _22=function(tag,_24,el,_26){if(!_1){_1=document.createElement("div");}var _27;var _28=null;if(tag=="td"){if(_24=="afterbegin"||_24=="beforeend"){return;}if(_24=="beforebegin"){_28=el;el=el.parentNode;}else{_28=el.nextSibling;el=el.parentNode;}_27=_15(4,trs,_26,tre);}else{if(tag=="tr"){if(_24=="beforebegin"){_28=el;el=el.parentNode;_27=_15(3,tbs,_26,tbe);}else{if(_24=="afterend"){_28=el.nextSibling;el=el.parentNode;_27=_15(3,tbs,_26,tbe);}else{if(_24=="afterbegin"){_28=el.firstChild;}_27=_15(4,trs,_26,tre);}}}else{if(tag=="tbody"){if(_24=="beforebegin"){_28=el;el=el.parentNode;_27=_15(2,ts,_26,te);}else{if(_24=="afterend"){_28=el.nextSibling;el=el.parentNode;_27=_15(2,ts,_26,te);}else{if(_24=="afterbegin"){_28=el.firstChild;}_27=_15(3,tbs,_26,tbe);}}}else{if(_24=="beforebegin"||_24=="afterend"){return;}if(_24=="afterbegin"){_28=el.firstChild;}_27=_15(2,ts,_26,te);}}}el.insertBefore(_27,_28);return _27;};return {useDom:false,markup:function(o){return _3(o);},applyStyles:function(el,_2b){if(_2b){el=Ext.fly(el);if(typeof _2b=="string"){var re=/\s?([a-z\-]*)\:\s?([^;]*);?/gi;var _2d;while((_2d=re.exec(_2b))!=null){el.setStyle(_2d[1],_2d[2]);}}else{if(typeof _2b=="object"){for(var _2e in _2b){el.setStyle(_2e,_2b[_2e]);}}else{if(typeof _2b=="function"){Ext.DomHelper.applyStyles(el,_2b.call());}}}}},insertHtml:function(_2f,el,_31){_2f=_2f.toLowerCase();if(el.insertAdjacentHTML){var tag=el.tagName.toLowerCase();if(tag=="table"||tag=="tbody"||tag=="tr"||tag=="td"){var rs;if(rs=_22(tag,_2f,el,_31)){return rs;}}switch(_2f){case "beforebegin":el.insertAdjacentHTML(_2f,_31);return el.previousSibling;case "afterbegin":el.insertAdjacentHTML(_2f,_31);return el.firstChild;case "beforeend":el.insertAdjacentHTML(_2f,_31);return el.lastChild;case "afterend":el.insertAdjacentHTML(_2f,_31);return el.nextSibling;}throw "Illegal insertion point -> \""+_2f+"\"";}var _34=el.ownerDocument.createRange();var _35;switch(_2f){case "beforebegin":_34.setStartBefore(el);_35=_34.createContextualFragment(_31);el.parentNode.insertBefore(_35,el);return el.previousSibling;case "afterbegin":if(el.firstChild){_34.setStartBefore(el.firstChild);_35=_34.createContextualFragment(_31);el.insertBefore(_35,el.firstChild);return el.firstChild;}else{el.innerHTML=_31;return el.firstChild;}case "beforeend":if(el.lastChild){_34.setStartAfter(el.lastChild);_35=_34.createContextualFragment(_31);el.appendChild(_35);return el.lastChild;}else{el.innerHTML=_31;return el.lastChild;}case "afterend":_34.setStartAfter(el);_35=_34.createContextualFragment(_31);el.parentNode.insertBefore(_35,el.nextSibling);return el.nextSibling;}throw "Illegal insertion point -> \""+_2f+"\"";},insertBefore:function(el,o,_38){return this.doInsert(el,o,_38,"beforeBegin");},insertAfter:function(el,o,_3b){return this.doInsert(el,o,_3b,"afterEnd","nextSibling");},insertFirst:function(el,o,_3e){return this.doInsert(el,o,_3e,"afterBegin");},doInsert:function(el,o,_41,pos,_43){el=Ext.getDom(el);var _44;if(this.useDom){_44=_c(o,null);el.parentNode.insertBefore(_44,_43?el[_43]:el);}else{var _45=_3(o);_44=this.insertHtml(pos,el,_45);}return _41?Ext.get(_44,true):_44;},append:function(el,o,_48){el=Ext.getDom(el);var _49;if(this.useDom){_49=_c(o,null);el.appendChild(_49);}else{var _4a=_3(o);_49=this.insertHtml("beforeEnd",el,_4a);}return _48?Ext.get(_49,true):_49;},overwrite:function(el,o,_4d){el=Ext.getDom(el);el.innerHTML=_3(o);return _4d?Ext.get(el.firstChild,true):el.firstChild;},createTemplate:function(o){var _4f=_3(o);return new Ext.Template(_4f);}};}(); From 9f0584adabd853a0640decf71be20d606ff01fdf Mon Sep 17 00:00:00 2001 From: Freek Vandeursen <freek@athleteshop.com> Date: Thu, 19 Oct 2017 11:16:39 +0200 Subject: [PATCH 0253/1171] Improve attribute checking On a store with a large number of attribute sets, a lot of repeated checking is done for the same attributes. So instead we can keep track of the attributes we already checked, and skip them the next time. --- .../Import/Product/Type/AbstractType.php | 53 ++++++++++++------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php index dd33b94423696..8744a208bd4c5 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php @@ -28,6 +28,13 @@ abstract class AbstractType */ public static $commonAttributesCache = []; + /** + * Maintain a list of invisible attributes + * + * @var array + */ + public static $invisibleAttributesCache = []; + /** * Attribute Code to Id cache * @@ -278,7 +285,10 @@ protected function _initAttributes() } } foreach ($absentKeys as $attributeSetName => $attributeIds) { - $this->attachAttributesById($attributeSetName, $attributeIds); + $unknownAttributeIds = array_diff($attributeIds, array_keys(self::$commonAttributesCache), self::$invisibleAttributesCache); + if ($unknownAttributeIds) { + $this->attachAttributesById($attributeSetName, $attributeIds); + } } foreach ($entityAttributes as $attributeRow) { if (isset(self::$commonAttributesCache[$attributeRow['attribute_id']])) { @@ -310,30 +320,35 @@ protected function attachAttributesById($attributeSetName, $attributeIds) $attributeId = $attribute->getId(); if ($attribute->getIsVisible() || in_array($attributeCode, $this->_forcedAttributesCodes)) { - self::$commonAttributesCache[$attributeId] = [ - 'id' => $attributeId, - 'code' => $attributeCode, - 'is_global' => $attribute->getIsGlobal(), - 'is_required' => $attribute->getIsRequired(), - 'is_unique' => $attribute->getIsUnique(), - 'frontend_label' => $attribute->getFrontendLabel(), - 'is_static' => $attribute->isStatic(), - 'apply_to' => $attribute->getApplyTo(), - 'type' => \Magento\ImportExport\Model\Import::getAttributeType($attribute), - 'default_value' => strlen( - $attribute->getDefaultValue() - ) ? $attribute->getDefaultValue() : null, - 'options' => $this->_entityModel->getAttributeOptions( - $attribute, - $this->_indexValueAttributes - ), - ]; + if (!isset(self::$commonAttributesCache[$attributeId])) { + self::$commonAttributesCache[$attributeId] = [ + 'id' => $attributeId, + 'code' => $attributeCode, + 'is_global' => $attribute->getIsGlobal(), + 'is_required' => $attribute->getIsRequired(), + 'is_unique' => $attribute->getIsUnique(), + 'frontend_label' => $attribute->getFrontendLabel(), + 'is_static' => $attribute->isStatic(), + 'apply_to' => $attribute->getApplyTo(), + 'type' => \Magento\ImportExport\Model\Import::getAttributeType($attribute), + 'default_value' => strlen( + $attribute->getDefaultValue() + ) ? $attribute->getDefaultValue() : null, + 'options' => $this->_entityModel->getAttributeOptions( + $attribute, + $this->_indexValueAttributes + ), + ]; + } + self::$attributeCodeToId[$attributeCode] = $attributeId; $this->_addAttributeParams( $attributeSetName, self::$commonAttributesCache[$attributeId], $attribute ); + } else { + self::$invisibleAttributesCache[] = $attributeId; } } } From ed01a4a29e8d87650c758e7bd8afd1a6ec0a4de0 Mon Sep 17 00:00:00 2001 From: Freek Vandeursen <freek@athleteshop.com> Date: Thu, 19 Oct 2017 13:53:00 +0200 Subject: [PATCH 0254/1171] Fix code style, too long line --- .../Model/Import/Product/Type/AbstractType.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php index 8744a208bd4c5..ed0e8591f440e 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php @@ -285,7 +285,11 @@ protected function _initAttributes() } } foreach ($absentKeys as $attributeSetName => $attributeIds) { - $unknownAttributeIds = array_diff($attributeIds, array_keys(self::$commonAttributesCache), self::$invisibleAttributesCache); + $unknownAttributeIds = array_diff( + $attributeIds, + array_keys(self::$commonAttributesCache), + self::$invisibleAttributesCache + ); if ($unknownAttributeIds) { $this->attachAttributesById($attributeSetName, $attributeIds); } From c0d696ef095561cef154ac7e4d81154a6a1b951d Mon Sep 17 00:00:00 2001 From: Freek Vandeursen <freek@athleteshop.com> Date: Thu, 26 Oct 2017 11:00:55 +0200 Subject: [PATCH 0255/1171] Fix too long variable name --- .../Model/Import/Product/Type/AbstractType.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php index ed0e8591f440e..efcda3caf987a 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php @@ -33,7 +33,7 @@ abstract class AbstractType * * @var array */ - public static $invisibleAttributesCache = []; + public static $invAttributesCache = []; /** * Attribute Code to Id cache @@ -288,7 +288,7 @@ protected function _initAttributes() $unknownAttributeIds = array_diff( $attributeIds, array_keys(self::$commonAttributesCache), - self::$invisibleAttributesCache + self::$invAttributesCache ); if ($unknownAttributeIds) { $this->attachAttributesById($attributeSetName, $attributeIds); @@ -352,7 +352,7 @@ protected function attachAttributesById($attributeSetName, $attributeIds) $attribute ); } else { - self::$invisibleAttributesCache[] = $attributeId; + self::$invAttributesCache[] = $attributeId; } } } From b5d41306eec61f76ec20f6eac8f50dfba4112405 Mon Sep 17 00:00:00 2001 From: Freek Vandeursen <freek@athleteshop.com> Date: Wed, 16 May 2018 00:09:15 +0200 Subject: [PATCH 0256/1171] Handle forced attribute codes --- .../Model/Import/Product/Type/AbstractType.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php index efcda3caf987a..afd018f077d20 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php @@ -290,7 +290,7 @@ protected function _initAttributes() array_keys(self::$commonAttributesCache), self::$invAttributesCache ); - if ($unknownAttributeIds) { + if ($unknownAttributeIds || $this->_forcedAttributesCodes) { $this->attachAttributesById($attributeSetName, $attributeIds); } } @@ -317,8 +317,11 @@ protected function _initAttributes() protected function attachAttributesById($attributeSetName, $attributeIds) { foreach ($this->_prodAttrColFac->create()->addFieldToFilter( - 'main_table.attribute_id', - ['in' => $attributeIds] + ['main_table.attribute_id', 'main_table.attribute_code'], + [ + ['in' => $attributeIds], + ['in' => $this->_forcedAttributesCodes] + ] ) as $attribute) { $attributeCode = $attribute->getAttributeCode(); $attributeId = $attribute->getId(); From 20483c523dd1de12365530c2ae18f11bfb6f21b7 Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Tue, 10 Jul 2018 18:29:16 +0530 Subject: [PATCH 0257/1171] Added 'title' attribute to 'img' tag in knockout template files. --- .../Braintree/view/frontend/web/template/payment/paypal.html | 2 +- .../Captcha/view/frontend/web/template/checkout/captcha.html | 1 + .../Catalog/view/adminhtml/web/template/image-preview.html | 3 ++- .../view/base/web/template/product/list/columns/image.html | 1 + .../web/template/product/list/columns/image_with_borders.html | 2 +- .../frontend/web/template/summary/item/details/thumbnail.html | 2 +- 6 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html index f5c8c15c8f3ba..e47024b7d2330 100644 --- a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html @@ -12,7 +12,7 @@ data-bind="attr: {'id': getCode()}, value: getCode(), checked: isChecked, click: selectPaymentMethod, visible: isRadioButtonVisible()" /> <label class="label" data-bind="attr: {'for': getCode()}"> <!-- PayPal Logo --> - <img data-bind="attr: {src: getPaymentAcceptanceMarkSrc(), alt: $t('Acceptance Mark')}" + <img data-bind="attr: {src: getPaymentAcceptanceMarkSrc(), alt: $t('Acceptance Mark')}, title: $t('Acceptance Mark')}" class="payment-icon"/> <!-- PayPal Logo --> <span text="getTitle()"></span> diff --git a/app/code/Magento/Captcha/view/frontend/web/template/checkout/captcha.html b/app/code/Magento/Captcha/view/frontend/web/template/checkout/captcha.html index 1e2f595a0087f..97c05249eb463 100644 --- a/app/code/Magento/Captcha/view/frontend/web/template/checkout/captcha.html +++ b/app/code/Magento/Captcha/view/frontend/web/template/checkout/captcha.html @@ -15,6 +15,7 @@ <div class="control captcha-image"> <img data-bind="attr: { alt: $t('Please type the letters and numbers below'), + title: $t('Please type the letters and numbers below'), height: imageHeight(), src: getImageSource(), }" diff --git a/app/code/Magento/Catalog/view/adminhtml/web/template/image-preview.html b/app/code/Magento/Catalog/view/adminhtml/web/template/image-preview.html index 04b4990f9cace..bf17624517f2a 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/template/image-preview.html +++ b/app/code/Magento/Catalog/view/adminhtml/web/template/image-preview.html @@ -14,7 +14,8 @@ event="load: $parent.onPreviewLoad.bind($parent)" attr=" src: $parent.getFilePreview($file), - alt: $file.name"> + alt: $file.name, + title: $file.name"> </a> <div class="actions"> diff --git a/app/code/Magento/Catalog/view/base/web/template/product/list/columns/image.html b/app/code/Magento/Catalog/view/base/web/template/product/list/columns/image.html index 318a6ceed69d1..cf76762b1ff58 100644 --- a/app/code/Magento/Catalog/view/base/web/template/product/list/columns/image.html +++ b/app/code/Magento/Catalog/view/base/web/template/product/list/columns/image.html @@ -11,6 +11,7 @@ class="product-image-photo" attr="src: getImageUrl($row()), alt: getLabel($row()), + title: getLabel($row()), width: getResizedImageWidth($row()), height: getResizedImageHeight($row())"/> </a> diff --git a/app/code/Magento/Catalog/view/base/web/template/product/list/columns/image_with_borders.html b/app/code/Magento/Catalog/view/base/web/template/product/list/columns/image_with_borders.html index 2baa9926df5f1..68b7f4e386896 100644 --- a/app/code/Magento/Catalog/view/base/web/template/product/list/columns/image_with_borders.html +++ b/app/code/Magento/Catalog/view/base/web/template/product/list/columns/image_with_borders.html @@ -14,7 +14,7 @@ data-bind="style: {'padding-bottom': getHeight($row())/getWidth($row()) * 100 + '%'}"> <img class="product-image-photo" data-bind="attr: {src: getImageUrl($row()), - alt: getLabel($row())}" /> + alt: getLabel($row()), title: getLabel($row())}" /> </span> </span> </a> diff --git a/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details/thumbnail.html b/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details/thumbnail.html index 981541e7251e7..eb218bbee9941 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details/thumbnail.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details/thumbnail.html @@ -8,6 +8,6 @@ data-bind="attr: {'style': 'height: ' + getHeight($parents[1]) + 'px; width: ' + getWidth($parents[1]) + 'px;' }"> <span class="product-image-wrapper"> <img - data-bind="attr: {'src': getSrc($parents[1]), 'width': getWidth($parents[1]), 'height': getHeight($parents[1]), 'alt': getAlt($parents[1]) }"/> + data-bind="attr: {'src': getSrc($parents[1]), 'width': getWidth($parents[1]), 'height': getHeight($parents[1]), 'alt': getAlt($parents[1]), 'title': getAlt($parents[1]) }"/> </span> </span> From 1f54168d4a9759a79bbe8853cca9ca5fbb1333bc Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Tue, 10 Jul 2018 18:19:11 +0530 Subject: [PATCH 0258/1171] Added translation function for Magento_Braintree module's template file. --- .../Braintree/view/frontend/templates/paypal/button.phtml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 1d60d19458a28..c1ef461ecae7c 100644 --- a/app/code/Magento/Braintree/view/frontend/templates/paypal/button.phtml +++ b/app/code/Magento/Braintree/view/frontend/templates/paypal/button.phtml @@ -29,6 +29,7 @@ $config = [ class="action-braintree-paypal-logo" disabled> <img class="braintree-paypal-button-hidden" src="https://checkout.paypal.com/pwpp/2.17.6/images/pay-with-paypal.png" - alt="Pay with PayPal"/> + alt="<?= $block->escapeHtml(__('Pay with PayPal')) ?>" + title="<?= $block->escapeHtml(__('Pay with PayPal')) ?>"/> </button> </div> From 0abbdd8b79d63b13be06340fe4e146adb45c9695 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Fri, 13 Jul 2018 11:57:00 +0300 Subject: [PATCH 0259/1171] magento/magento2#?: Fix Translation of error message on cart for deleted bundle option. --- app/code/Magento/Multishipping/etc/frontend/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Multishipping/etc/frontend/di.xml b/app/code/Magento/Multishipping/etc/frontend/di.xml index 5968e3324e309..0c2daaf45043e 100644 --- a/app/code/Magento/Multishipping/etc/frontend/di.xml +++ b/app/code/Magento/Multishipping/etc/frontend/di.xml @@ -43,6 +43,6 @@ <plugin name="multishipping_session_mapper" type="Magento\Multishipping\Model\Checkout\Type\Multishipping\Plugin" sortOrder="50" /> </type> <type name="Magento\Checkout\Controller\Cart"> - <plugin name="multishipping_clear_addresses" type="Magento\Multishipping\Model\Cart\Controller\CartPlugin" /> + <plugin name="multishipping_clear_addresses" type="Magento\Multishipping\Model\Cart\Controller\CartPlugin" sortOrder="50" /> </type> </config> From 70f198af0e52b19ccc21d32ff81a4276baf48a6a Mon Sep 17 00:00:00 2001 From: Pratik <ronak2ram@gmail.com> Date: Fri, 13 Jul 2018 14:27:07 +0530 Subject: [PATCH 0260/1171] Improved code and remove unnecessary space --- .../Magento/Catalog/Pricing/Price/ConfiguredRegularPrice.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Pricing/Price/ConfiguredRegularPrice.php b/app/code/Magento/Catalog/Pricing/Price/ConfiguredRegularPrice.php index bcb6638b9cd25..83d59718400bd 100644 --- a/app/code/Magento/Catalog/Pricing/Price/ConfiguredRegularPrice.php +++ b/app/code/Magento/Catalog/Pricing/Price/ConfiguredRegularPrice.php @@ -63,7 +63,7 @@ public function setItem(ItemInterface $item) : ConfiguredRegularPrice return $this; } - + /** * Price value of product with configured options. * @@ -73,7 +73,7 @@ public function getValue() { $basePrice = parent::getValue(); - return $this->item + return $this->item && $basePrice !== false ? $basePrice + $this->configuredOptions->getItemOptionsValue($basePrice, $this->item) : $basePrice; } From d05317c59f096157e4ff15a810611513556c2502 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Fri, 13 Jul 2018 12:24:59 +0300 Subject: [PATCH 0261/1171] magento/magento2#15895: Handle type errors for objects creation - Updated test --- .../ObjectManager/ObjectManagerTest.php | 12 ++++++------ .../TestAsset/ConstructorWithTypeError.php | 16 +++++++++++++--- .../ObjectManager/Factory/AbstractFactory.php | 6 +++--- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php index 20a412cd9c692..6d5da7243ffbe 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ObjectManagerTest.php @@ -5,8 +5,6 @@ */ namespace Magento\Framework\ObjectManager; -use TypeError; - class ObjectManagerTest extends \PHPUnit\Framework\TestCase { /**#@+ @@ -147,13 +145,15 @@ public function testNewInstance($actualClassName, array $properties = [], $expec } /** - * Test create instance with TypeError + * Test creating an object and passing incorrect type of arguments to the constructor. * - * @expectedException TypeError + * @expectedException \Magento\Framework\Exception\RuntimeException + * @expectedExceptionMessage Error occurred when creating object */ public function testNewInstanceWithTypeError() { - self::$_objectManager->create(self::TEST_CLASS_WITH_TYPE_ERROR); - $this->fail('No instance for class with TypeError should be created'); + self::$_objectManager->create(self::TEST_CLASS_WITH_TYPE_ERROR, [ + 'testArgument' => new \stdClass() + ]); } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithTypeError.php b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithTypeError.php index 87ad22ba8a24a..3470f9508af46 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithTypeError.php +++ b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithTypeError.php @@ -6,11 +6,21 @@ namespace Magento\Framework\ObjectManager\TestAsset; +/** + * Test asset used to test invalid argument types on the constructor invocation. + */ class ConstructorWithTypeError { - public function __construct() + /** + * @var Basic + */ + private $testArgument; + + /** + * @param Basic $testArgument + */ + public function __construct(Basic $testArgument) { - // set non-exists property to trigger TypeError - throw new \TypeError('test error'); + $this->testArgument = $testArgument; } } diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php index f2e31d5c5973a..15c4cb098b84d 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php @@ -114,15 +114,15 @@ protected function createObject($type, $args) { try { return new $type(...array_values($args)); - } catch (\TypeError $e) { + } catch (\TypeError $exception) { /** @var LoggerInterface $logger */ $logger = ObjectManager::getInstance()->get(LoggerInterface::class); $logger->critical( - sprintf('Create object error: %s, %s', $type, $e->getMessage()) + sprintf('Type Error occurred when creating object: %s, %s', $type, $exception->getMessage()) ); throw new RuntimeException( - new Phrase('Create object error: '.$e->getMessage()) + new Phrase('Type Error occurred when creating object: %type', ['type' => $type]) ); } } From 347cba4c80480b9a2e09ef6019d1222d64e034de Mon Sep 17 00:00:00 2001 From: Oleg Onufer <linkedddd@gmail.com> Date: Fri, 13 Jul 2018 12:56:18 +0300 Subject: [PATCH 0262/1171] MAGETWO-90365: Automate with MFTF tax information recalculation on the shopping cart --- .../Test/Mftf/Page/CheckoutCartPage.xml | 2 +- .../Section/CheckoutCartSummarySection.xml | 8 +- .../Customer/Test/Mftf/Data/RegionData.xml | 4 +- .../Tax/Test/Mftf/Data/TaxConfigData.xml | 60 ++++++ .../Tax/Test/Mftf/Data/TaxRegionData.xml | 17 ++ .../Tax/Test/Mftf/Data/TaxRuleData.xml | 26 +++ .../Test/Mftf/Metadata/tax_config-meta.xml | 183 ++++++++++++++++++ .../Tax/Test/Mftf/Metadata/tax_rule-meta.xml | 33 ++++ .../Section/CheckoutCartSummarySection.xml | 16 ++ ...oppingCartForCustomerPhysicalQuoteTest.xml | 119 ++++++++++++ ...hoppingCartForCustomerVirtualQuoteTest.xml | 83 ++++++++ ...nShoppingCartForGuestPhysicalQuoteTest.xml | 114 +++++++++++ ...InShoppingCartForGuestVirtualQuoteTest.xml | 78 ++++++++ .../AdminProductAddFPTValueActionGroup.xml | 24 +++ .../Mftf/Data/FixedProductAttributeData.xml | 20 ++ .../Weee/Test/Mftf/Data/WeeeConfigData.xml | 25 +++ .../Test/Mftf/Metadata/weee_config-meta.xml | 34 ++++ .../Test/Mftf/Page/AdminProductEditPage.xml | 14 ++ .../AdminProductAddFPTValueSection.xml | 17 ++ .../Section/CheckoutCartSummarySection.xml | 14 ++ 20 files changed, 885 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/Tax/Test/Mftf/Data/TaxConfigData.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Data/TaxRegionData.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Data/TaxRuleData.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Metadata/tax_rule-meta.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml create mode 100644 app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml create mode 100644 app/code/Magento/Weee/Test/Mftf/ActionGroup/AdminProductAddFPTValueActionGroup.xml create mode 100644 app/code/Magento/Weee/Test/Mftf/Data/FixedProductAttributeData.xml create mode 100644 app/code/Magento/Weee/Test/Mftf/Data/WeeeConfigData.xml create mode 100644 app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml create mode 100644 app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml create mode 100644 app/code/Magento/Weee/Test/Mftf/Section/AdminProductAddFPTValueSection.xml create mode 100644 app/code/Magento/Weee/Test/Mftf/Section/CheckoutCartSummarySection.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml index dcc652829b3cc..433d397ee81a9 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> - <page name="CheckoutCartPage" url="/checkout/cart" module="Checkout" area="storefront"> + <page name="CheckoutCartPage" url="/checkout/cart" module="Magento_Checkout" area="storefront"> <section name="CheckoutCartProductSection"/> <section name="CheckoutCartSummarySection"/> </page> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml index ce4069a0c916d..01b483c8ecf0b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -16,8 +16,10 @@ <element name="proceedToCheckout" type="button" selector=".action.primary.checkout span" timeout="30"/> <element name="discountAmount" type="text" selector="td[data-th='Discount']"/> <element name="shippingHeading" type="button" selector="#block-shipping-heading"/> - <element name="postcode" type="input" selector="input[name='postcode']"/> - <element name="stateProvince" type="select" selector="select[name='region_id']"/> - <element name="country" type="select" selector="select[name='country_id']"/> + <element name="postcode" type="input" selector="input[name='postcode']" timeout="10"/> + <element name="stateProvince" type="select" selector="select[name='region_id']" timeout="10"/> + <element name="country" type="select" selector="select[name='country_id']" timeout="10"/> + <element name="estimateShippingAndTax" type="text" selector="#block-shipping-heading" timeout="5"/> + <element name="flatRateShippingMethod" type="radio" selector="#s_method_flatrate_flatrate" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml b/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml index 747f2d59745a1..99741a357109e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml @@ -20,11 +20,11 @@ <entity name="RegionCA" type="region"> <data key="region">California</data> <data key="region_code">CA</data> - <data key="region_id">2</data> + <data key="region_id">12</data> </entity> <entity name="RegionNY" type="region"> <data key="region">New York</data> <data key="region_code">NY</data> - <data key="region_id">3</data> + <data key="region_id">43</data> </entity> </entities> diff --git a/app/code/Magento/Tax/Test/Mftf/Data/TaxConfigData.xml b/app/code/Magento/Tax/Test/Mftf/Data/TaxConfigData.xml new file mode 100644 index 0000000000000..d7c88c1d282e2 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Data/TaxConfigData.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <!-- Default Tax Destination Calculation --> + <entity name="CountryUS" type="country"> + <data key="value">US</data> + </entity> + <entity name="AllPostCode" type="postcode"> + <data key="value">*</data> + </entity> + <!-- Shopping Cart Display Settings --> + <entity name="IncludeTaxInOrderTotalCart" type="grandtotalCart"> + <data key="value">1</data> + </entity> + <entity name="DisplayFullTaxSummaryCart" type="full_summaryCart"> + <data key="value">1</data> + </entity> + <entity name="DisplayZeroTaxSubtotalCart" type="zero_taxCart"> + <data key="value">1</data> + </entity> + <entity name="Tax_Config_CA" type="tax_config_state"> + <!-- Default Tax Destination Calculation --> + <requiredEntity type="country">CountryUS</requiredEntity> + <requiredEntity type="region">Region_CA</requiredEntity> + <requiredEntity type="postcode">AllPostCode</requiredEntity> + <!-- Shopping Cart Display Settings --> + <requiredEntity type="grandtotalCart">IncludeTaxInOrderTotalCart</requiredEntity> + <requiredEntity type="full_summaryCart">DisplayFullTaxSummaryCart</requiredEntity> + <requiredEntity type="zero_taxCart">DisplayZeroTaxSubtotalCart</requiredEntity> + </entity> + + <entity name="Tax_Config_NY" type="tax_config_state"> + <!-- Default Tax Destination Calculation --> + <requiredEntity type="country">CountryUS</requiredEntity> + <requiredEntity type="region">Region_NY</requiredEntity> + <requiredEntity type="postcode">AllPostCode</requiredEntity> + <!-- Shopping Cart Display Settings --> + <requiredEntity type="grandtotalCart">IncludeTaxInOrderTotalCart</requiredEntity> + <requiredEntity type="full_summaryCart">DisplayFullTaxSummaryCart</requiredEntity> + <requiredEntity type="zero_taxCart">DisplayZeroTaxSubtotalCart</requiredEntity> + </entity> + <!-- Set default settings --> + <entity name="DefaultTaxConfig" type="tax_config_default"> + <requiredEntity type="taxTotalFlagZero">DefaultTotalFlagZero</requiredEntity> + <requiredEntity type="taxPostCodeEmpty">EmptyField</requiredEntity> + </entity> + <entity name="DefaultTotalFlagZero" type="taxTotalFlagZero"> + <data key="value">0</data> + </entity> + <entity name="EmptyField" type="taxPostCodeEmpty"> + <data key="value"/> + </entity> +</entities> diff --git a/app/code/Magento/Tax/Test/Mftf/Data/TaxRegionData.xml b/app/code/Magento/Tax/Test/Mftf/Data/TaxRegionData.xml new file mode 100644 index 0000000000000..c27225a339831 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Data/TaxRegionData.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="Region_NY" type="region"> + <data key="value">43</data> + </entity> + <entity name="Region_CA" type="region"> + <data key="value">12</data> + </entity> +</entities> diff --git a/app/code/Magento/Tax/Test/Mftf/Data/TaxRuleData.xml b/app/code/Magento/Tax/Test/Mftf/Data/TaxRuleData.xml new file mode 100644 index 0000000000000..16c891745426d --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Data/TaxRuleData.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SimpleTaxRule" type="taxRule"> + <data key="code" unique="suffix">TaxRule</data> + <data key="position">0</data> + <data key="priority">0</data> + <array key="customer_tax_class_ids"> + <item>3</item> + </array> + <array key="product_tax_class_ids"> + <item>2</item> + </array> + <array key="tax_rate_ids"> + <item>1</item> + <item>2</item> + </array> + <data key="calculate_subtotal">true</data> + </entity> +</entities> diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml new file mode 100644 index 0000000000000..137c2e48c111e --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml @@ -0,0 +1,183 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateTaxConfigDefaultsTaxDestination" dataType="tax_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> + <object key="groups" dataType="tax_config_state"> + <object key="defaults" dataType="tax_config_state"> + <object key="fields" dataType="tax_config_state"> + <object key="country" dataType="country"> + <field key="value">string</field> + </object> + <object key="region" dataType="region"> + <field key="value">string</field> + </object> + <object key="postcode" dataType="postcode"> + <field key="value">string</field> + </object> + </object> + </object> + <object key="cart_display" dataType="tax_config_state"> + <object key="fields" dataType="tax_config_state"> + <object key="grandtotal" dataType="grandtotalCart"> + <field key="value">string</field> + </object> + <object key="full_summary" dataType="full_summaryCart"> + <field key="value">string</field> + </object> + <object key="zero_tax" dataType="zero_taxCart"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> + <operation name="TaxConfigDefaultsTaxDestination" dataType="tax_config_default" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> + <object key="groups" dataType="tax_config_default"> + <object key="calculation" dataType="tax_config_default"> + <object key="fields" dataType="tax_config_default"> + <object key="algorithm" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="based_on" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="price_includes_tax" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="shipping_includes_tax" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="apply_after_discount" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="discount_tax" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="apply_tax_on" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="cross_border_trade_enabled" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + </object> + <object key="defaults" dataType="tax_config_default"> + <object key="fields" dataType="tax_config_default"> + <object key="country" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="region" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="postcode" dataType="taxPostCodeEmpty"> + <field key="value">string</field> + </object> + </object> + </object> + <object key="cart_display" dataType="tax_config_default"> + <object key="fields" dataType="tax_config_default"> + <object key="price" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="subtotal" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="shipping" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="gift_wrapping" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + <object key="printed_card" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + <object key="grandtotal" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="full_summary" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="zero_tax" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + <object key="sales_display" dataType="tax_config_default"> + <object key="fields" dataType="tax_config_default"> + <object key="price" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="subtotal" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="shipping" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="gift_wrapping" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + <object key="printed_card" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + <object key="grandtotal" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="full_summary" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="zero_tax" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + </object> + </operation> +</operations> diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rule-meta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rule-meta.xml new file mode 100644 index 0000000000000..f9886303fd3a3 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rule-meta.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateTaxRule" dataType="taxRule" type="create" auth="adminOauth" url="/V1/taxRules" method="POST"> + <contentType>application/json</contentType> + <object key="rule" dataType="taxRule"> + <field key="id">integer</field> + <field key="code" required="true">string</field> + <field key="priority">integer</field> + <field key="position">integer</field> + <array key="customer_tax_class_ids"> + <value>integer</value> + </array> + <array key="product_tax_class_ids"> + <value>integer</value> + </array> + <array key="tax_rate_ids"> + <value>integer</value> + </array> + <field key="calculate_subtotal">boolean</field> + <field key="extension_attributes">empty_extension_attribute</field> + </object> + </operation> + <operation name="DeleteTaxRule" dataType="taxRule" type="delete" auth="adminOauth" url="/V1/taxRules/{id}" method="DELETE"> + <contentType>application/json</contentType> + </operation> +</operations> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml new file mode 100644 index 0000000000000..b47e8b85e5231 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="CheckoutCartSummarySection"> + <element name="taxAmount" type="text" selector="[data-th='Tax']>span"/> + <element name="taxSummary" type="text" selector=".totals-tax-summary"/> + <element name="rate" type="text" selector=" tr.totals-tax-details.shown th.mark"/> + </section> +</sections> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml new file mode 100644 index 0000000000000..fdd6d18dae9c4 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest"> + <annotations> + <features value="Tax information in shopping cart for Customer with default addresses (physical quote)"/> + <title value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (physical quote)"/> + <description value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (physical quote)"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-41932"/> + <group value="checkout"/> + <group value="tax"/> + </annotations> + <before> + <!-- Preconditions --> + <!-- Tax Rule is created based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> + <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> + <!-- Fixed Product Tax attribute is created and added to default attribute set --> + <createData entity="productFPTAttribute" stepKey="createProductFPTAttribute"/> + <createData entity="AddToDefaultSet" stepKey="addFPTToAttributeSet"> + <requiredEntity createDataKey="createProductFPTAttribute"/> + </createData> + <!-- Tax configuration (Store>Configuration; Sales>Tax) With FPT Enable --> + <createData entity="Tax_Config_NY" stepKey="taxConfigurationNYWithFPTEnable"/> + <!-- Store>Configuration; Sales>Tax FPT Enable --> + <createData entity="WeeeConfigEnable" stepKey="enableFPT"/> + <!-- Simple product is created Price = 10; FPT United States/California/10,United States/New York/20 --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <field key="price">10.00</field> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Customer is created with default addresses: --> + <createData entity="Simple_US_Customer_CA" stepKey="createCustomer"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue1"> + <argument name="FPTAttributeCode" value="$$createProductFPTAttribute.attribute_code$$"/> + <argument name="stateForFPT" value="California"/> + <argument name="valueForFPT" value="10"/> + </actionGroup> + <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue2"> + <argument name="FPTAttributeCode" value="$$createProductFPTAttribute.attribute_code$$"/> + <argument name="stateForFPT" value="New York"/> + <argument name="valueForFPT" value="20"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> + </before> + <after> + <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> + <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> + <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> + <createData entity="WeeeConfigDisable" stepKey="disableFPT"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Test Steps --> + <!-- Step 1: Go to Storefront as logged in Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$createCustomer$$" /> + </actionGroup> + <!-- Step 2: Add simple product to shopping cart --> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddSimpleProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + <!-- Step 3: Go to Shopping Cart --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <!-- Step 4: Open Estimate Shipping and Tax section --> + <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> + <see selector="{{CheckoutCartSummarySection.country}}" userInput="$$createCustomer.country_id$$" stepKey="checkCustomerCountry" /> + <see selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="$$createCustomer.state$$" stepKey="checkCustomerRegion" /> + <see selector="{{CheckoutCartSummarySection.postcode}}" userInput="$$createCustomer.postcode$$" stepKey="checkCustomerPostcode" /> + <see selector="{{CheckoutCartSummarySection.amountFPT}}" userInput="$10" stepKey="checkFPTAmountCA" /> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.83" stepKey="checkTaxAmountCA" /> + <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> + <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary"/> + <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> + <!-- Step 5: Change Data --> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="Aargau" stepKey="selectAargauRegion"/> + <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> + <!-- Step 6: Select shipping rate again(it need for get new totals request - performance reason) --> + <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod"/> + <scrollTo selector="{{CheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary2" /> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> + <dontSeeElement selector="{{CheckoutCartSummarySection.amountFPT}}" stepKey="checkFPTIsNotDisplayed" /> + <!-- Step 7: Change Data --> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="New York" stepKey="selectNewYorkRegion"/> + <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="12345" stepKey="inputPostCode2"/> + <!-- Step 8: Select shipping rate again(it need for get new totals request - performance reason) --> + <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod2"/> + <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary3" /> + <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary3"/> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.84" stepKey="checkTaxAmountNY" /> + <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> + <see selector="{{CheckoutCartSummarySection.amountFPT}}" userInput="$20" stepKey="checkFPTAmountNY" /> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml new file mode 100644 index 0000000000000..c27af9a6f63f5 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest"> + <annotations> + <features value="Tax information in shopping cart for Customer with default addresses (virtual quote)"/> + <title value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (virtual quote)"/> + <description value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (virtual quote)"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-41933"/> + <group value="checkout"/> + <group value="tax"/> + </annotations> + <before> + <!-- Preconditions --> + <!-- Tax Rule is created based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> + <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> + <!-- Fixed Product Tax attribute is created and added to default attribute set --> + <createData entity="productFPTAttribute" stepKey="createProductFPTAttribute"/> + <createData entity="AddToDefaultSet" stepKey="addFPTToAttributeSet"> + <requiredEntity createDataKey="createProductFPTAttribute"/> + </createData> + <!-- Tax configuration (Store>Configuration; Sales>Tax) --> + <createData entity="Tax_Config_CA" stepKey="taxConfigurationCA"/> + <!-- Virtual product is created: --> + <createData entity="VirtualProduct" stepKey="createVirtualProduct"> + <field key="price">40.00</field> + </createData> + <!-- Customer is created with default addresses: --> + <createData entity="Simple_US_Customer_NY" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> + <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> + <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> + <deleteData createDataKey="createVirtualProduct" stepKey="deleteVirtualProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + </after> + <!-- Test Steps --> + <!-- Step 1: Go to Storefront as logged in Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$createCustomer$$" /> + </actionGroup> + <!-- Step 2: Add virtual product to shopping cart --> + <amOnPage url="{{StorefrontProductPage.url($$createVirtualProduct.name$$)}}" stepKey="amOnStorefrontVirtualProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddVirtualProductToCart"> + <argument name="product" value="$$createVirtualProduct$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + <!-- Step 3: Go to Shopping Cart --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <!-- Step 4: Open Estimate Shipping and Tax section --> + <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> + <see selector="{{CheckoutCartSummarySection.country}}" userInput="$$createCustomer.country_id$$" stepKey="checkCustomerCountry" /> + <see selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="$$createCustomer.state$$" stepKey="checkCustomerRegion" /> + <see selector="{{CheckoutCartSummarySection.postcode}}" userInput="$$createCustomer.postcode$$" stepKey="checkCustomerPostcode" /> + <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> + <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="expandTaxSummary"/> + <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> + <!-- Step 5: Change Data --> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="Aargau" stepKey="selectAargauRegion"/> + <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> + <scrollTo selector="{{CheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary2" /> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> + <!-- Step 6: Change Data --> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> + <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="90230" stepKey="inputPostCode2"/> + <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary3" /> + <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary2"/> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$3.30" stepKey="checkTaxAmount2" /> + <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml new file mode 100644 index 0000000000000..891e8f4e7968b --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest"> + <annotations> + <features value="Tax information in shopping cart for Guest (physical quote)"/> + <title value="Tax information are updating/recalculating on fly in shopping cart for Guest (physical quote)"/> + <description value="Tax information are updating/recalculating on fly in shopping cart for Guest (physical quote)"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-41930"/> + <!--Skip because of issue MAGETWO-90966 on 4 step--> + <group value="skip"/> + <group value="checkout"/> + <group value="tax"/> + </annotations> + <before> + <!-- Preconditions --> + <!-- Tax Rule is created based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> + <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> + <!-- Fixed Product Tax attribute is created and added to default attribute set --> + <createData entity="productFPTAttribute" stepKey="createProductFPTAttribute"/> + <createData entity="AddToDefaultSet" stepKey="addFPTToAttributeSet"> + <requiredEntity createDataKey="createProductFPTAttribute"/> + </createData> + <!-- Tax configuration (Store>Configuration; Sales>Tax) --> + <createData entity="Tax_Config_CA" stepKey="taxConfigurationForCA"/> + <!-- Store>Configuration; Sales>Tax FPT Enable --> + <createData entity="WeeeConfigEnable" stepKey="enableFPT"/> + <!-- Simple product is created Price = 10; FPT United States/California/10,United States/New York/20 --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <field key="price">10.00</field> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue1"> + <argument name="FPTAttributeCode" value="$$createProductFPTAttribute.attribute_code$$"/> + <argument name="stateForFPT" value="California"/> + <argument name="valueForFPT" value="10"/> + </actionGroup> + <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue2"> + <argument name="FPTAttributeCode" value="$$createProductFPTAttribute.attribute_code$$"/> + <argument name="stateForFPT" value="New York"/> + <argument name="valueForFPT" value="20"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> + </before> + <after> + <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> + <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> + <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> + <createData entity="WeeeConfigDisable" stepKey="disableFPT"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Test Steps --> + <!-- Step 1: Go to Storefront as Guest --> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <!-- Step 2: Add simple product to shopping cart --> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddSimpleProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + <!-- Step 3: Go to Shopping Cart --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <!-- Step 4: Open Estimate Shipping and Tax section --> + <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> + <see selector="{{CheckoutCartSummarySection.amountFPT}}" userInput="$10" stepKey="checkFPTAmountCA" /> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.83" stepKey="checkTaxAmountCA" /> + <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> + <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary"/> + <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> + <!-- Step 5: Change Data --> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="Aargau" stepKey="selectAargauRegion"/> + <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> + <!-- Step 6: Select shipping rate again(it need for get new totals request - performance reason) --> + <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod"/> + <scrollTo selector="{{CheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary2" /> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> + <dontSeeElement selector="{{CheckoutCartSummarySection.amountFPT}}" stepKey="checkFPTIsNotDisplayed" /> + <!-- Step 7: Change Data --> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="New York" stepKey="selectNewYorkRegion"/> + <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="12345" stepKey="inputPostCode2"/> + <!-- Step 8: Select shipping rate again(it need for get new totals request - performance reason) --> + <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod2"/> + <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary3" /> + <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary2"/> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.84" stepKey="checkTaxAmountNY" /> + <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> + <see selector="{{CheckoutCartSummarySection.amountFPT}}" userInput="$20" stepKey="checkFPTAmountNY" /> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml new file mode 100644 index 0000000000000..9f33c1a8b155a --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest"> + <annotations> + <features value="Tax information in shopping cart for Guest (virtual quote)"/> + <title value="Tax information are updating/recalculating on fly in shopping cart for Guest (virtual quote)"/> + <description value="Tax information are updating/recalculating on fly in shopping cart for Guest (virtual quote)"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-41931"/> + <!--Skip because of issue MAGETWO-90966 on 4 step--> + <group value="skip"/> + <group value="checkout"/> + <group value="tax"/> + </annotations> + <before> + <!-- Preconditions --> + <!-- Tax Rule is created based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> + <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> + <!-- Fixed Product Tax attribute is created and added to default attribute set --> + <createData entity="productFPTAttribute" stepKey="createProductFPTAttribute"/> + <createData entity="AddToDefaultSet" stepKey="addFPTToAttributeSet"> + <requiredEntity createDataKey="createProductFPTAttribute"/> + </createData> + <!-- Tax configuration (Store>Configuration; Sales>Tax) --> + <createData entity="Tax_Config_CA" stepKey="taxConfigurationCA"/> + <!-- Virtual product is created: Price = 10 --> + <createData entity="VirtualProduct" stepKey="createVirtualProduct"> + <field key="price">40.00</field> + </createData> + </before> + <after> + <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> + <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> + <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> + <deleteData createDataKey="createVirtualProduct" stepKey="deleteVirtualProduct"/> + </after> + <!-- Test Steps --> + <!-- Step 1: Go to Storefront as Guest --> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <!-- Step 2: Add virtual product to shopping cart --> + <amOnPage url="{{StorefrontProductPage.url($$createVirtualProduct.name$$)}}" stepKey="amOnStorefrontVirtualProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddVirtualProductToCart"> + <argument name="product" value="$$createVirtualProduct$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + <!-- Step 3: Go to Shopping Cart --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <!-- Step 4: Open Estimate Shipping and Tax section --> + <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> + <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$3.30" stepKey="checkTaxAmountCA" /> + <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary"/> + <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> + <!-- Step 5: Change Data --> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="Aargau" stepKey="selectAargauRegion"/> + <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> + <!-- Step 6: Change Data --> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="New York" stepKey="selectNewYorkRegion"/> + <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="12345" stepKey="inputPostCode2"/> + <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary2" /> + <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary2"/> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$3.35" stepKey="checkTaxAmountNY" /> + <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> + </test> +</tests> diff --git a/app/code/Magento/Weee/Test/Mftf/ActionGroup/AdminProductAddFPTValueActionGroup.xml b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AdminProductAddFPTValueActionGroup.xml new file mode 100644 index 0000000000000..c41eb7c02a557 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AdminProductAddFPTValueActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <!--Navigate to create product page from product grid page--> + <actionGroup name="AdminProductAddFPTValueActionGroup"> + <arguments> + <argument name="FPTAttributeCode"/> + <argument name="countryForFPT" defaultValue="US" type="string"/> + <argument name="stateForFPT" type="string"/> + <argument name="valueForFPT" type="string"/> + </arguments> + <click selector="{{AdminProductAddFPTValueSection.addFPT(FPTAttributeCode)}}" stepKey="clickAddFPTButton1"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <selectOption selector="{{AdminProductAddFPTValueSection.selectCountryForFPT(FPTAttributeCode)}}" userInput="{{countryForFPT}}" stepKey="selectcountryForFPT"/> + <selectOption selector="{{AdminProductAddFPTValueSection.selectStateForFPT(FPTAttributeCode)}}" userInput="{{stateForFPT}}" stepKey="selectstateForFPT"/> + <fillField selector="{{AdminProductAddFPTValueSection.setTaxValueForFPT(FPTAttributeCode)}}" userInput="{{valueForFPT}}" stepKey="setTaxvalueForFPT"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Weee/Test/Mftf/Data/FixedProductAttributeData.xml b/app/code/Magento/Weee/Test/Mftf/Data/FixedProductAttributeData.xml new file mode 100644 index 0000000000000..e981dae483f32 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/Data/FixedProductAttributeData.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="productFPTAttribute" type="ProductAttribute"> + <data key="attribute_code" unique="suffix">attribute</data> + <data key="is_unique">true</data> + <data key="frontend_input">weee</data> + <data key="is_used_in_grid">true</data> + <data key="is_visible_in_grid">true</data> + <data key="is_filterable_in_grid">true</data> + <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> + </entity> +</entities> diff --git a/app/code/Magento/Weee/Test/Mftf/Data/WeeeConfigData.xml b/app/code/Magento/Weee/Test/Mftf/Data/WeeeConfigData.xml new file mode 100644 index 0000000000000..120dd10eee359 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/Data/WeeeConfigData.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <!-- Fixed Product Taxes Enable--> + <entity name="WeeeConfigEnable" type="weee_config"> + <requiredEntity type="enableFPT">EnableFPT</requiredEntity> + </entity> + <entity name="EnableFPT" type="enableFPT"> + <data key="value">1</data> + </entity> + <!-- Fixed Product Taxes Disable--> + <entity name="WeeeConfigDisable" type="weee_config_default"> + <requiredEntity type="disableFPT">DisableFPT</requiredEntity> + </entity> + <entity name="DisableFPT" type="disableFPT"> + <data key="value">0</data> + </entity> +</entities> diff --git a/app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml b/app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml new file mode 100644 index 0000000000000..2e2b71c30ef47 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + <operation name="WeeeConfigEnable" dataType="weee_config" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> + <object key="groups" dataType="weee_config"> + <object key="weee" dataType="weee_config"> + <object key="fields" dataType="weee_config"> + <object key="enable" dataType="enableFPT"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> + <operation name="WeeeConfigDefault" dataType="weee_config_default" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> + <object key="groups" dataType="weee_config_default"> + <object key="weee" dataType="weee_config_default"> + <object key="fields" dataType="weee_config_default"> + <object key="enable" dataType="weee_config_default"> + <object key="inherit" dataType="disableFPT"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + </object> + </operation> +</operations> diff --git a/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml b/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml new file mode 100644 index 0000000000000..fa3663ee719e1 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="AdminProductEditPage" url="catalog/product/edit/id/{{product_id}}/" area="admin" module="Magento_Catalog"> + <section name="AdminProductAddFPTValueSection"/> + </page> +</pages> diff --git a/app/code/Magento/Weee/Test/Mftf/Section/AdminProductAddFPTValueSection.xml b/app/code/Magento/Weee/Test/Mftf/Section/AdminProductAddFPTValueSection.xml new file mode 100644 index 0000000000000..40a9a97f31425 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/Section/AdminProductAddFPTValueSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminProductAddFPTValueSection"> + <element name="addFPT" type="button" selector="[data-index='{{FPTAttributeCode}}'] [data-action='add_new_row']" parameterized="true"/> + <element name="selectCountryForFPT" type="select" selector="(//select[contains(@name, 'product[{{FPTAttributeCode}}]') and contains(@name, '[country]')])[last()]" parameterized="true"/> + <element name="selectStateForFPT" type="select" selector="(//select[contains(@name, 'product[{{FPTAttributeCode}}]') and contains(@name, '[state]')])[last()]" parameterized="true"/> + <element name="setTaxValueForFPT" type="text" selector="(//input[contains(@name, 'product[{{FPTAttributeCode}}]') and contains(@name, '[value]')])[last()]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Weee/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Weee/Test/Mftf/Section/CheckoutCartSummarySection.xml new file mode 100644 index 0000000000000..9b6541b93541d --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="CheckoutCartSummarySection"> + <element name="amountFPT" type="text" selector=".totals td[data-th='FPT'] .price"/> + </section> +</sections> From ab1324ecc1d5e2990acfd4f14c7b77765d343a9e Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Fri, 13 Jul 2018 15:12:21 +0300 Subject: [PATCH 0263/1171] Fix the issue with "Shipping address is not set" exception #16555 --- app/code/Magento/Multishipping/Controller/Checkout.php | 1 + .../Magento/Quote/Model/ShippingMethodManagement.php | 4 +--- .../Test/Unit/Model/ShippingMethodManagementTest.php | 10 ++-------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Multishipping/Controller/Checkout.php b/app/code/Magento/Multishipping/Controller/Checkout.php index 1870736a0efd9..161021768ce19 100644 --- a/app/code/Magento/Multishipping/Controller/Checkout.php +++ b/app/code/Magento/Multishipping/Controller/Checkout.php @@ -84,6 +84,7 @@ protected function _getCheckoutSession() * * @param RequestInterface $request * @return \Magento\Framework\App\ResponseInterface + * @throws \Magento\Framework\Exception\NotFoundException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ diff --git a/app/code/Magento/Quote/Model/ShippingMethodManagement.php b/app/code/Magento/Quote/Model/ShippingMethodManagement.php index ade2649d0b1b0..ac609e7f435ea 100644 --- a/app/code/Magento/Quote/Model/ShippingMethodManagement.php +++ b/app/code/Magento/Quote/Model/ShippingMethodManagement.php @@ -171,9 +171,7 @@ public function set($cartId, $carrierCode, $methodCode) * @param string $methodCode The shipping method code. * @return void * @throws InputException The shipping method is not valid for an empty cart. - * @throws CouldNotSaveException The shipping method could not be saved. * @throws NoSuchEntityException CThe Cart includes virtual product(s) only, so a shipping address is not used. - * @throws StateException The billing or shipping address is missing. Set the address and try again. */ public function apply($cartId, $carrierCode, $methodCode) { @@ -191,7 +189,7 @@ public function apply($cartId, $carrierCode, $methodCode) } $shippingAddress = $quote->getShippingAddress(); if (!$shippingAddress->getCountryId()) { - throw new StateException(__('The shipping address is missing. Set the address and try again.')); + return; } $shippingAddress->setShippingMethod($carrierCode . '_' . $methodCode); } diff --git a/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php index 198f1c54a42b4..6042ab25eef7f 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php @@ -344,10 +344,6 @@ public function testSetMethodWithVirtualProduct() $this->model->set($cartId, $carrierCode, $methodCode); } - /** - * @expectedException \Magento\Framework\Exception\StateException - * @expectedExceptionMessage The shipping address is missing. Set the address and try again. - */ public function testSetMethodWithoutShippingAddress() { $cartId = 12; @@ -361,6 +357,7 @@ public function testSetMethodWithoutShippingAddress() $this->quote->expects($this->once())->method('isVirtual')->will($this->returnValue(false)); $this->quote->expects($this->once()) ->method('getShippingAddress')->will($this->returnValue($this->shippingAddress)); + $this->quote->expects($this->once())->method('collectTotals')->willReturnSelf(); $this->shippingAddress->expects($this->once())->method('getCountryId')->will($this->returnValue(null)); $this->model->set($cartId, $carrierCode, $methodCode); @@ -402,10 +399,6 @@ public function testSetMethodWithCouldNotSaveException() $this->model->set($cartId, $carrierCode, $methodCode); } - /** - * @expectedException \Magento\Framework\Exception\StateException - * @expectedExceptionMessage The shipping address is missing. Set the address and try again. - */ public function testSetMethodWithoutAddress() { $cartId = 12; @@ -420,6 +413,7 @@ public function testSetMethodWithoutAddress() $this->quote->expects($this->once()) ->method('getShippingAddress') ->willReturn($this->shippingAddress); + $this->quote->expects($this->once())->method('collectTotals')->willReturnSelf(); $this->shippingAddress->expects($this->once())->method('getCountryId'); $this->model->set($cartId, $carrierCode, $methodCode); From 33a06d3a2a5d0d06410941d8f9cc1b1c43195435 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Fri, 13 Jul 2018 14:55:37 +0200 Subject: [PATCH 0264/1171] assertResponseFields method moved to abstract class --- .../TestCase/GraphQlAbstract.php | 28 +++++++++++++++++++ .../GraphQl/Bundle/BundleProductViewTest.php | 25 ----------------- .../Magento/GraphQl/Catalog/CategoryTest.php | 25 ----------------- .../Catalog/ProductAttributeTypeTest.php | 25 ----------------- .../GraphQl/Catalog/ProductSearchTest.php | 25 ----------------- .../GraphQl/Catalog/ProductViewTest.php | 25 ----------------- .../Catalog/VirtualProductViewTest.php | 25 ----------------- .../ConfigurableProductViewTest.php | 25 ----------------- .../Customer/CustomerAuthenticationTest.php | 25 ----------------- .../DownloadableProductViewTest.php | 25 ----------------- .../GroupedProduct/GroupedProductViewTest.php | 25 ----------------- .../Magento/GraphQl/Tax/ProductViewTest.php | 25 ----------------- 12 files changed, 28 insertions(+), 275 deletions(-) diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php index af01e455fdd9d..05c6f1d80e293 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php @@ -34,6 +34,7 @@ abstract class GraphQlAbstract extends WebapiAbstract * @param array $variables * @param string $operationName * @return array|int|string|float|bool GraphQL call results + * @throws \Exception */ public function graphQlQuery( string $query, @@ -97,4 +98,31 @@ private function getGraphQlClient() $this->graphQlClient; } } + + /** + * Compare actual response fields with expected + * + * @param array $actualResponse + * @param array $assertionMap ['response_field_name' => 'response_field_value', ...] + * OR [['response_field' => $field, 'expected_value' => $value], ...] + */ + protected function assertResponseFields($actualResponse, $assertionMap) + { + foreach ($assertionMap as $key => $assertionData) { + $expectedValue = isset($assertionData['expected_value']) + ? $assertionData['expected_value'] + : $assertionData; + $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; + self::assertNotNull( + $expectedValue, + "Value of '{$responseField}' field must not be NULL" + ); + self::assertEquals( + $expectedValue, + $actualResponse[$responseField], + "Value of '{$responseField}' field in response does not match expected value: " + . var_export($expectedValue, true) + ); + } + } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php index 8ccd9d0c94f61..0621370972763 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php @@ -285,31 +285,6 @@ private function assertBundleProductOptions($product, $actualResponse) ); } - /** - * @param array $actualResponse - * @param array $assertionMap ['response_field_name' => 'response_field_value', ...] - * OR [['response_field' => $field, 'expected_value' => $value], ...] - */ - private function assertResponseFields($actualResponse, $assertionMap) - { - foreach ($assertionMap as $key => $assertionData) { - $expectedValue = isset($assertionData['expected_value']) - ? $assertionData['expected_value'] - : $assertionData; - $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; - $this->assertNotNull( - $expectedValue, - "Value of '{$responseField}' field must not be NULL" - ); - $this->assertEquals( - $expectedValue, - $actualResponse[$responseField], - "Value of '{$responseField}' field in response does not match expected value: " - . var_export($expectedValue, true) - ); - } - } - /** * @magentoApiDataFixture Magento/Bundle/_files/product_with_multiple_options_1.php */ diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index 0133b87e757bd..87758727fe8bc 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -419,29 +419,4 @@ private function assertAttributes($actualResponse) $this->assertArrayHasKey($eavAttribute, $actualResponse); } } - - /** - * @param array $actualResponse - * @param array $assertionMap ['response_field_name' => 'response_field_value', ...] - * OR [['response_field' => $field, 'expected_value' => $value], ...] - */ - private function assertResponseFields($actualResponse, $assertionMap) - { - foreach ($assertionMap as $key => $assertionData) { - $expectedValue = isset($assertionData['expected_value']) - ? $assertionData['expected_value'] - : $assertionData; - $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; - self::assertNotNull( - $expectedValue, - "Value of '{$responseField}' field must not be NULL" - ); - self::assertEquals( - $expectedValue, - $actualResponse[$responseField], - "Value of '{$responseField}' field in response does not match expected value: " - . var_export($expectedValue, true) - ); - } - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductAttributeTypeTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductAttributeTypeTest.php index 2db400769c300..063da7c11bf7f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductAttributeTypeTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductAttributeTypeTest.php @@ -231,29 +231,4 @@ private function assertAttributeType($attributeTypes, $expectedAttributeCodes, $ ); } } - - /** - * @param array $actualResponse - * @param array $assertionMap ['response_field_name' => 'response_field_value', ...] - * OR [['response_field' => $field, 'expected_value' => $value], ...] - */ - private function assertResponseFields(array $actualResponse, array $assertionMap) - { - foreach ($assertionMap as $key => $assertionData) { - $expectedValue = isset($assertionData['expected_value']) - ? $assertionData['expected_value'] - : $assertionData; - $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; - $this->assertNotNull( - $expectedValue, - "Value of '{$responseField}' field must not be NULL" - ); - $this->assertEquals( - $expectedValue, - $actualResponse[$responseField], - "Value of '{$responseField}' field in response does not match expected value: " - . var_export($expectedValue, true) - ); - } - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index dc5a66fbb34ab..f1a79490b3dcf 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -1283,29 +1283,4 @@ private function assertProductItemsWithMaximalAndMinimalPriceCheck(array $filter ); } } - - /** - * @param array $actualResponse - * @param array $assertionMap ['response_field_name' => 'response_field_value', ...] - * OR [['response_field' => $field, 'expected_value' => $value], ...] - */ - private function assertResponseFields(array $actualResponse, array $assertionMap) - { - foreach ($assertionMap as $key => $assertionData) { - $expectedValue = isset($assertionData['expected_value']) - ? $assertionData['expected_value'] - : $assertionData; - $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; - $this->assertNotNull( - $expectedValue, - "Value of '{$responseField}' field must not be NULL" - ); - $this->assertEquals( - $expectedValue, - $actualResponse[$responseField], - "Value of '{$responseField}' field in response does not match expected value: " - . var_export($expectedValue, true) - ); - } - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php index b5aa73a5a7a34..34cebde64d03a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -957,29 +957,4 @@ private function eavAttributesToGraphQlSchemaFieldTranslator(string $eavAttribut } return $eavAttributeCode; } - - /** - * @param array $actualResponse - * @param array $assertionMap ['response_field_name' => 'response_field_value', ...] - * OR [['response_field' => $field, 'expected_value' => $value], ...] - */ - private function assertResponseFields($actualResponse, $assertionMap) - { - foreach ($assertionMap as $key => $assertionData) { - $expectedValue = isset($assertionData['expected_value']) - ? $assertionData['expected_value'] - : $assertionData; - $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; - self::assertNotNull( - $expectedValue, - "Value of '{$responseField}' field must not be NULL" - ); - self::assertEquals( - $expectedValue, - $actualResponse[$responseField], - "Value of '{$responseField}' field in response does not match expected value: " - . var_export($expectedValue, true) - ); - } - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/VirtualProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/VirtualProductViewTest.php index df3386f2f7836..58b6d4f0e4ea2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/VirtualProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/VirtualProductViewTest.php @@ -134,29 +134,4 @@ private function assertBaseFields($product, $actualResponse) $this->assertResponseFields($actualResponse, $assertionMap); } - - /** - * @param array $actualResponse - * @param array $assertionMap ['response_field_name' => 'response_field_value', ...] - * OR [['response_field' => $field, 'expected_value' => $value], ...] - */ - private function assertResponseFields($actualResponse, $assertionMap) - { - foreach ($assertionMap as $key => $assertionData) { - $expectedValue = isset($assertionData['expected_value']) - ? $assertionData['expected_value'] - : $assertionData; - $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; - $this->assertNotNull( - $expectedValue, - "Value of '{$responseField}' field must not be NULL" - ); - $this->assertEquals( - $expectedValue, - $actualResponse[$responseField], - "Value of '{$responseField}' field in response does not match expected value: " - . var_export($expectedValue, true) - ); - } - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php index 84c4e80d325ab..735ae7fff646b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php @@ -487,31 +487,6 @@ private function assertConfigurableProductOptions($actualResponse) } } - /** - * @param array $actualResponse - * @param array $assertionMap ['response_field_name' => 'response_field_value', ...] - * OR [['response_field' => $field, 'expected_value' => $value], ...] - */ - private function assertResponseFields(array $actualResponse, array $assertionMap) - { - foreach ($assertionMap as $key => $assertionData) { - $expectedValue = isset($assertionData['expected_value']) - ? $assertionData['expected_value'] - : $assertionData; - $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; - $this->assertNotNull( - $expectedValue, - "Value of '{$responseField}' field must not be NULL" - ); - $this->assertEquals( - $expectedValue, - $actualResponse[$responseField], - "Value of '{$responseField}' field in response does not match expected value: " - . var_export($expectedValue, true) - ); - } - } - private function getConfigurableOptions() { if (!empty($this->configurableOptions)) { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAuthenticationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAuthenticationTest.php index 60fa63338e790..88ce7e91d94bc 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAuthenticationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAuthenticationTest.php @@ -164,29 +164,4 @@ public function assertCustomerAddressesFields($customer, $actualResponse) $this->assertResponseFields($actualResponse['customer']['addresses'][$addressKey], $assertionMap); } } - - /** - * @param array $actualResponse - * @param array $assertionMap ['response_field_name' => 'response_field_value', ...] - * OR [['response_field' => $field, 'expected_value' => $value], ...] - */ - private function assertResponseFields($actualResponse, $assertionMap) - { - foreach ($assertionMap as $key => $assertionData) { - $expectedValue = isset($assertionData['expected_value']) - ? $assertionData['expected_value'] - : $assertionData; - $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; - $this->assertNotNull( - $expectedValue, - "Value of '{$responseField}' field must not be NULL" - ); - $this->assertEquals( - $expectedValue, - $actualResponse[$responseField], - "Value of '{$responseField}' field in response does not match expected value: " - . var_export($expectedValue, true) - ); - } - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/DownloadableProduct/DownloadableProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/DownloadableProduct/DownloadableProductViewTest.php index 57d39f04347bf..a7902db259bc3 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/DownloadableProduct/DownloadableProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/DownloadableProduct/DownloadableProductViewTest.php @@ -261,29 +261,4 @@ private function assertDownloadableProductSamples($product, $actualResponse) ] ); } - - /** - * @param array $actualResponse - * @param array $assertionMap ['response_field_name' => 'response_field_value', ...] - * OR [['response_field' => $field, 'expected_value' => $value], ...] - */ - private function assertResponseFields($actualResponse, $assertionMap) - { - foreach ($assertionMap as $key => $assertionData) { - $expectedValue = isset($assertionData['expected_value']) - ? $assertionData['expected_value'] - : $assertionData; - $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; - $this->assertNotNull( - $expectedValue, - "Value of '{$responseField}' field must not be NULL" - ); - $this->assertEquals( - $expectedValue, - $actualResponse[$responseField], - "Value of '{$responseField}' field in response does not match expected value: " - . var_export($expectedValue, true) - ); - } - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/GroupedProduct/GroupedProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/GroupedProduct/GroupedProductViewTest.php index 841bff42a3057..cbd91f6fbdb37 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/GroupedProduct/GroupedProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/GroupedProduct/GroupedProductViewTest.php @@ -92,29 +92,4 @@ private function assertGroupedProductItems($product, $actualResponse) ); } } - - /** - * @param array $actualResponse - * @param array $assertionMap ['response_field_name' => 'response_field_value', ...] - * OR [['response_field' => $field, 'expected_value' => $value], ...] - */ - private function assertResponseFields($actualResponse, $assertionMap) - { - foreach ($assertionMap as $key => $assertionData) { - $expectedValue = isset($assertionData['expected_value']) - ? $assertionData['expected_value'] - : $assertionData; - $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; - $this->assertNotNull( - $expectedValue, - "Value of '{$responseField}' field must not be NULL" - ); - $this->assertEquals( - $expectedValue, - $actualResponse[$responseField], - "Value of '{$responseField}' field in response does not match expected value: " - . var_export($expectedValue, true) - ); - } - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/ProductViewTest.php index 4d0797d32e493..dfbe943ecdcd9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/ProductViewTest.php @@ -325,29 +325,4 @@ private function assertBaseFields($product, $actualResponse) $this->assertResponseFields($actualResponse, $assertionMap); } - - /** - * @param array $actualResponse - * @param array $assertionMap ['response_field_name' => 'response_field_value', ...] - * OR [['response_field' => $field, 'expected_value' => $value], ...] - */ - private function assertResponseFields($actualResponse, $assertionMap) - { - foreach ($assertionMap as $key => $assertionData) { - $expectedValue = isset($assertionData['expected_value']) - ? $assertionData['expected_value'] - : $assertionData; - $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; - $this->assertNotNull( - $expectedValue, - "Value of '{$responseField}' field must not be NULL" - ); - $this->assertEquals( - $expectedValue, - $actualResponse[$responseField], - "Value of '{$responseField}' field in response does not match expected value: " - . var_export($expectedValue, true) - ); - } - } } From 7a1cbccf264c573a9e4780c91f2dbf3d2f2e3d87 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 13 Jul 2018 16:42:22 +0300 Subject: [PATCH 0265/1171] MAGETWO-88432: Filesystem Directory Improvement --- .../Controller/Adminhtml/Sitemap/Delete.php | 8 +- .../Adminhtml/Sitemap/DeleteTest.php | 149 -------- .../Wysiwyg/Images/DeleteFilesTest.php | 6 +- .../Wysiwyg/Images/DeleteFolderTest.php | 6 +- .../Wysiwyg/Images/NewFolderTest.php | 6 +- .../Adminhtml/Wysiwyg/Images/UploadTest.php | 12 +- .../App/Filesystem/DirectoryResolverTest.php | 2 +- .../Filesystem/Directory/ReadTest.php | 279 +++++++++++++- .../Filesystem/Directory/WriteTest.php | 236 ++++++++++++ .../Filesystem/Directory/PathValidator.php | 71 ++++ .../Directory/PathValidatorInterface.php | 35 ++ .../Framework/Filesystem/Directory/Read.php | 74 +++- .../Filesystem/Directory/ReadFactory.php | 12 +- .../Framework/Filesystem/Directory/Write.php | 34 +- .../Filesystem/Directory/WriteFactory.php | 13 +- .../Framework/Filesystem/Driver/File.php | 7 + .../Filesystem/File/WriteFactory.php | 3 +- .../Framework/Test/Unit/TranslateTest.php | 356 ------------------ lib/internal/Magento/Framework/Translate.php | 16 +- 19 files changed, 781 insertions(+), 544 deletions(-) delete mode 100644 app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/DeleteTest.php create mode 100644 lib/internal/Magento/Framework/Filesystem/Directory/PathValidator.php create mode 100644 lib/internal/Magento/Framework/Filesystem/Directory/PathValidatorInterface.php delete mode 100644 lib/internal/Magento/Framework/Test/Unit/TranslateTest.php diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php index f0be0fe7ab682..422fed9d9a688 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php @@ -53,9 +53,13 @@ public function execute() $sitemap->load($id); // delete file $sitemapPath = $sitemap->getSitemapPath(); + if ($sitemapPath && $sitemapPath[0] === DIRECTORY_SEPARATOR) { + $sitemapPath = mb_substr($sitemapPath, 1); + } $sitemapFilename = $sitemap->getSitemapFilename(); - - $path = $directory->getRelativePath($sitemapPath . $sitemapFilename); + $path = $directory->getRelativePath( + $sitemapPath .$sitemapFilename + ); if ($sitemap->getSitemapFilename() && $directory->isFile($path)) { $directory->delete($path); } diff --git a/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/DeleteTest.php b/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/DeleteTest.php deleted file mode 100644 index ed004fe88b318..0000000000000 --- a/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/DeleteTest.php +++ /dev/null @@ -1,149 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sitemap\Test\Unit\Controller\Adminhtml\Sitemap; - -class DeleteTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Sitemap\Controller\Adminhtml\Sitemap\Delete - */ - private $deleteController; - - /** - * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject - */ - private $filesystemMock; - - /** - * @var \Magento\Sitemap\Model\SitemapFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $sitemapFactoryMock; - - /** - * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $requestMock; - - /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $responseMock; - - /** - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $messageManagerMock; - - protected function setUp() - { - $this->filesystemMock = $this->getMockBuilder(\Magento\Framework\Filesystem::class) - ->disableOriginalConstructor() - ->setMethods([]) - ->getMock(); - $this->sitemapFactoryMock = $this->getMockBuilder(\Magento\Sitemap\Model\SitemapFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) - ->disableOriginalConstructor() - ->setMethods([]) - ->getMockForAbstractClass(); - $this->responseMock = $this->getMockBuilder(\Magento\Framework\App\ResponseInterface::class) - ->disableOriginalConstructor() - ->setMethods(['setRedirect']) - ->getMockForAbstractClass(); - $this->messageManagerMock = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) - ->disableOriginalConstructor() - ->setMethods([]) - ->getMockForAbstractClass(); - - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $this->deleteController = $objectManagerHelper->getObject( - \Magento\Sitemap\Controller\Adminhtml\Sitemap\Delete::class, - [ - 'request' => $this->requestMock, - 'response' => $this->responseMock, - 'messageManager' => $this->messageManagerMock, - 'filesystem' => $this->filesystemMock, - 'sitemapFactory' => $this->sitemapFactoryMock - ] - ); - } - - public function testExecuteWithoutSitemapId() - { - $writeDirectoryMock = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\WriteInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->filesystemMock->expects($this->any())->method('getDirectoryWrite')->willReturn($writeDirectoryMock); - $this->requestMock->expects($this->any())->method('getParam')->with('sitemap_id')->willReturn(0); - $this->responseMock->expects($this->once())->method('setRedirect'); - $this->messageManagerMock->expects($this->any()) - ->method('addError') - ->with('We can\'t find a sitemap to delete.'); - - $this->deleteController->execute(); - } - - public function testExecuteCannotFindSitemap() - { - $id = 1; - $writeDirectoryMock = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\WriteInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->filesystemMock->expects($this->any())->method('getDirectoryWrite')->willReturn($writeDirectoryMock); - $this->requestMock->expects($this->any())->method('getParam')->with('sitemap_id')->willReturn($id); - - $sitemapMock = $this->getMockBuilder(\Magento\Sitemap\Model\Sitemap::class) - ->disableOriginalConstructor() - ->setMethods(['load']) - ->getMock(); - - $sitemapMock->expects($this->once())->method('load')->with($id)->willThrowException(new \Exception); - $this->sitemapFactoryMock->expects($this->once())->method('create')->willReturn($sitemapMock); - $this->responseMock->expects($this->once())->method('setRedirect'); - $this->messageManagerMock->expects($this->any()) - ->method('addError'); - - $this->deleteController->execute(); - } - - public function testExecute() - { - $id = 1; - $sitemapPath = '/'; - $sitemapFilename = 'sitemap.xml'; - $relativePath = '/sitemap.xml'; - $writeDirectoryMock = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\WriteInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->filesystemMock->expects($this->any())->method('getDirectoryWrite')->willReturn($writeDirectoryMock); - $this->requestMock->expects($this->any())->method('getParam')->with('sitemap_id')->willReturn($id); - - $sitemapMock = $this->getMockBuilder(\Magento\Sitemap\Model\Sitemap::class) - ->disableOriginalConstructor() - ->setMethods(['getSitemapPath', 'getSitemapFilename', 'load', 'delete']) - ->getMock(); - $sitemapMock->expects($this->once())->method('load')->with($id); - $sitemapMock->expects($this->once())->method('delete'); - $sitemapMock->expects($this->any())->method('getSitemapPath')->willReturn($sitemapPath); - $sitemapMock->expects($this->any())->method('getSitemapFilename')->willReturn($sitemapFilename); - $this->sitemapFactoryMock->expects($this->once())->method('create')->willReturn($sitemapMock); - $writeDirectoryMock->expects($this->any()) - ->method('getRelativePath') - ->with($sitemapPath . $sitemapFilename) - ->willReturn($relativePath); - $writeDirectoryMock->expects($this->once())->method('isFile')->with($relativePath)->willReturn(true); - $writeDirectoryMock->expects($this->once())->method('delete')->with($relativePath)->willReturn(true); - - $this->responseMock->expects($this->once())->method('setRedirect'); - $this->messageManagerMock->expects($this->any()) - ->method('addSuccess'); - - $this->deleteController->execute(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php index bbcbc40dc6640..fa49a7b1f57e1 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php @@ -101,11 +101,7 @@ public function testExecuteWithWrongFileName() $this->model->getStorage()->getSession()->setCurrentPath($this->fullDirectoryPath); $this->model->execute(); - $this->assertTrue( - $this->mediaDirectory->isExist( - $this->mediaDirectory->getRelativePath($this->fullDirectoryPath . $fileName) - ) - ); + $this->assertFileExists($this->fullDirectoryPath . $fileName); } /** diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php index 8e30e85541a42..a1a29706756b5 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php @@ -112,11 +112,7 @@ public function testExecuteWithWrongDirectoryName() $this->model->getRequest()->setParams(['node' => $this->imagesHelper->idEncode($directoryName)]); $this->model->execute(); - $this->assertTrue( - $this->mediaDirectory->isExist( - $this->mediaDirectory->getRelativePath($this->fullDirectoryPath . $directoryName) - ) - ); + $this->assertFileExists($this->fullDirectoryPath . $directoryName); } /** diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolderTest.php index 0c74f18e9c44a..e509737a0020f 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolderTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolderTest.php @@ -111,10 +111,8 @@ public function testExecuteWithWrongPath() $this->model->getStorage()->getSession()->setCurrentPath($this->fullDirectoryPath . $dirPath); $this->model->execute(); - $this->assertFalse( - $this->mediaDirectory->isExist( - $this->mediaDirectory->getRelativePath($this->fullDirectoryPath . $dirPath . $this->dirName) - ) + $this->assertFileNotExists( + $this->fullDirectoryPath . $dirPath . $this->dirName ); } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php index 534eb3db35b3f..bab14a8663eae 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php @@ -125,10 +125,8 @@ public function testExecuteWithWrongPath() $this->model->getStorage()->getSession()->setCurrentPath($dirPath); $this->model->execute(); - $this->assertFalse( - $this->mediaDirectory->isExist( - $this->mediaDirectory->getRelativePath($this->fullDirectoryPath . $dirPath . $this->fileName) - ) + $this->assertFileNotExists( + $this->fullDirectoryPath . $dirPath . $this->fileName ); } @@ -147,11 +145,7 @@ public function testExecuteWithWrongFileName() $this->model->getStorage()->getSession()->setCurrentPath($this->fullDirectoryPath); $this->model->execute(); - $this->assertFalse( - $this->mediaDirectory->isExist( - $this->mediaDirectory->getRelativePath($this->fullDirectoryPath . $newFilename) - ) - ); + $this->assertFileNotExists($this->fullDirectoryPath . $newFilename); } /** diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/DirectoryResolverTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/DirectoryResolverTest.php index 8f1f9e92972e5..90d8473032c69 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/DirectoryResolverTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/DirectoryResolverTest.php @@ -52,7 +52,7 @@ public function testValidatePath($path, $directoryConfig, $expectation) { $directory = $this->filesystem ->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::MEDIA); - $path = $directory->getAbsolutePath($path); + $path = $directory->getAbsolutePath() .'/' .$path; $this->assertEquals($expectation, $this->directoryResolver->validatePath($path, $directoryConfig)); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/ReadTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/ReadTest.php index f43a523a12ed0..bc77eeb932c9a 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/ReadTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/ReadTest.php @@ -7,6 +7,7 @@ */ namespace Magento\Framework\Filesystem\Directory; +use Magento\Framework\Exception\ValidatorException; use Magento\TestFramework\Helper\Bootstrap; /** @@ -34,13 +35,66 @@ public function testGetAbsolutePath() $this->assertContains('_files/foo/bar', $dir->getAbsolutePath('bar')); } + public function testGetAbsolutePathOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('foo'); + try { + $dir->getAbsolutePath('../../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->getAbsolutePath('//./..///../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->getAbsolutePath('\..\..\Directory\ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + public function testGetRelativePath() { $dir = $this->getDirectoryInstance('foo'); + $this->assertEquals( + 'file_three.txt', + $dir->getRelativePath('file_three.txt') + ); $this->assertEquals('', $dir->getRelativePath()); $this->assertEquals('bar', $dir->getRelativePath(__DIR__ . '/../_files/foo/bar')); } + public function testGetRelativePathOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('foo'); + try { + $dir->getRelativePath(__DIR__ .'/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->getRelativePath(__DIR__ .'//./..////Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->getRelativePath(__DIR__ .'\..\Directory\ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->getRelativePath('../../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(4, $exceptions); + } + /** * Test for read method * @@ -72,6 +126,28 @@ public function readProvider() ]; } + public function testReadOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('foo'); + try { + $dir->read('../../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->read('//./..///../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->read('\..\..\Directory\ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test for search method * @@ -103,6 +179,28 @@ public function searchProvider() ]; } + public function testSearchOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('foo'); + try { + $dir->search('/*/*.txt', '../../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->search('/*/*.txt', '//./..///../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->search('/*/*.txt', '\..\..\Directory\ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test for isExist method * @@ -127,6 +225,28 @@ public function existsProvider() return [['foo', 'bar', true], ['foo', 'bar/baz/', true], ['foo', 'bar/notexists', false]]; } + public function testIsExistOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('foo'); + try { + $dir->isExist('../../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->isExist('//./..///../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->isExist('\..\..\Directory\ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test for stat method * @@ -168,6 +288,28 @@ public function statProvider() return [['foo', 'bar'], ['foo', 'file_three.txt']]; } + public function testStatOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('foo'); + try { + $dir->stat('bar/../../../Directory'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->stat('bar//./..///../../Directory'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->stat('bar\..\..\..\Directory'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test for isReadable method * @@ -182,6 +324,28 @@ public function testIsReadable($dirPath, $path, $readable) $this->assertEquals($readable, $dir->isReadable($path)); } + public function testIsReadableOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('foo'); + try { + $dir->isReadable('../../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->isReadable('//./..///../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->isReadable('\..\..\Directory\ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test for isFile method * @@ -194,6 +358,28 @@ public function testIsFile($path, $isFile) $this->assertEquals($isFile, $this->getDirectoryInstance('foo')->isFile($path)); } + public function testIsFileOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('foo'); + try { + $dir->isFile('../../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->isFile('//./..///../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->isFile('\..\..\Directory\ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test for isDirectory method * @@ -206,6 +392,28 @@ public function testIsDirectory($path, $isDirectory) $this->assertEquals($isDirectory, $this->getDirectoryInstance('foo')->isDirectory($path)); } + public function testIsDirectoryOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('foo'); + try { + $dir->isDirectory('../../Directory'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->isDirectory('//./..///../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->isDirectory('\..\..\Directory\ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Data provider for testIsReadable * @@ -246,6 +454,28 @@ public function testOpenFile() $this->assertTrue($file instanceof \Magento\Framework\Filesystem\File\ReadInterface); } + public function testOpenFileOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('foo'); + try { + $dir->openFile('../../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->openFile('//./..///../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->openFile('\..\..\Directory\ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test readFile * @@ -268,10 +498,35 @@ public function readFileProvider() { return [ ['popup.csv', 'var myData = 5;'], - ['data.csv', '"field1", "field2"' . "\n" . '"field3", "field4"' . "\n"] + [ + 'data.csv', + '"field1", "field2"' . PHP_EOL . '"field3", "field4"' . PHP_EOL + ] ]; } + public function testReadFileOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('foo'); + try { + $dir->readFile('../../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->readFile('//./..///../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->readFile('\..\..\Directory\ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Get readable file instance * Get full path for files located in _files directory @@ -301,4 +556,26 @@ public function testReadRecursively() sort($expected); $this->assertEquals($expected, $actual); } + + public function testReadRecursivelyOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('foo'); + try { + $dir->readRecursively('../../Directory'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->readRecursively('//./..///../Directory'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->readRecursively('\..\..\Directory'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/WriteTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/WriteTest.php index 380c7998680a1..39a224ff902d5 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/WriteTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/WriteTest.php @@ -7,6 +7,7 @@ */ namespace Magento\Framework\Filesystem\Directory; +use Magento\Framework\Exception\ValidatorException; use Magento\Framework\Filesystem\DriverPool; use Magento\TestFramework\Helper\Bootstrap; @@ -63,6 +64,28 @@ public function createProvider() ]; } + public function testCreateOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('newDir1', 0777); + try { + $dir->create('../../outsideDir'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->create('//./..///../outsideDir'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->create('\..\..\outsideDir'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test for delete method * @@ -88,6 +111,28 @@ public function deleteProvider() return [['subdir'], ['subdir/subsubdir']]; } + public function testDeleteOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('newDir1', 0777); + try { + $dir->delete('../../Directory'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->delete('//./..///../Directory'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->delete('\..\..\Directory'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test for rename method (in scope of one directory instance) * @@ -119,6 +164,31 @@ public function renameProvider() return [['newDir1', 0777, 'first_name.txt', 'second_name.txt']]; } + public function testRenameOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('newDir1', 0777); + try { + $dir->renameFile('../../Directory/ReadTest.php', 'RenamedTest'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->renameFile( + '//./..///../Directory/ReadTest.php', + 'RenamedTest' + ); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->renameFile('\..\..\Directory\ReadTest.php', 'RenamedTest'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test for rename method (moving to new directory instance) * @@ -185,6 +255,40 @@ public function copyProvider() ]; } + public function testCopyOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('newDir1', 0777); + $dir->touch('test_file_for_copy_outside.txt'); + try { + $dir->copyFile('../../Directory/ReadTest.php', 'CopiedTest'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->copyFile( + '//./..///../Directory/ReadTest.php', + 'CopiedTest' + ); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->copyFile('\..\..\Directory\ReadTest.php', 'CopiedTest'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->copyFile( + 'test_file_for_copy_outside.txt', + '../../Directory/copied_outside.txt' + ); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(4, $exceptions); + } + /** * Test for copy method (copy to another directory instance) * @@ -231,6 +335,28 @@ public function testChangePermissions() $this->assertTrue($directory->changePermissions('test_directory', 0644)); } + public function testChangePermissionsOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('newDir1', 0777); + try { + $dir->changePermissions('../../Directory', 0777); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->changePermissions('//./..///../Directory', 0777); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->changePermissions('\..\..\Directory', 0777); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test for changePermissionsRecursively method */ @@ -244,6 +370,28 @@ public function testChangePermissionsRecursively() $this->assertTrue($directory->changePermissionsRecursively('test_directory', 0777, 0644)); } + public function testChangePermissionsRecursivelyOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('newDir1', 0777); + try { + $dir->changePermissionsRecursively('../foo', 0777, 0777); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->changePermissionsRecursively('//./..///foo', 0777, 0777); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->changePermissionsRecursively('\..\foo', 0777, 0777); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test for touch method * @@ -274,6 +422,28 @@ public function touchProvider() ]; } + public function testTouchOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('newDir1', 0777); + try { + $dir->touch('../../foo.tst'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->touch('//./..///../foo.tst'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->touch('\..\..\foo.tst'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test isWritable method */ @@ -285,6 +455,28 @@ public function testIsWritable() $this->assertTrue($directory->isWritable('bar')); } + public function testIsWritableOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('newDir1', 0777); + try { + $dir->isWritable('../../Directory'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->isWritable('//./..///../Directory'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->isWritable('\..\..\Directory'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test for openFile method * @@ -315,6 +507,28 @@ public function openFileProvider() ]; } + public function testOpenFileOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('newDir1', 0777); + try { + $dir->openFile('../../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->openFile('//./..///../Directory/ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->openFile('\..\..\Directory\ReadTest.php'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Test writeFile * @@ -359,6 +573,28 @@ public function writeFileProvider() return [['file1', '123', '456'], ['folder1/file1', '123', '456']]; } + public function testWriteFileOutside() + { + $exceptions = 0; + $dir = $this->getDirectoryInstance('newDir1', 0777); + try { + $dir->writeFile('../../Directory/ReadTest.php', 'tst'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->writeFile('//./..///../Directory/ReadTest.php', 'tst'); + } catch (ValidatorException $exception) { + $exceptions++; + } + try { + $dir->writeFile('\..\..\Directory\ReadTest.php', 'tst'); + } catch (ValidatorException $exception) { + $exceptions++; + } + $this->assertEquals(3, $exceptions); + } + /** * Tear down */ diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/PathValidator.php b/lib/internal/Magento/Framework/Filesystem/Directory/PathValidator.php new file mode 100644 index 0000000000000..fe0e6b37666b7 --- /dev/null +++ b/lib/internal/Magento/Framework/Filesystem/Directory/PathValidator.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\Filesystem\Directory; + +use Magento\Framework\Exception\ValidatorException; +use Magento\Framework\Filesystem\DriverInterface; +use Magento\Framework\Phrase; + +/** + * @inheritDoc + * + * Validates paths using driver. + */ +class PathValidator implements PathValidatorInterface +{ + /** + * @var DriverInterface + */ + private $driver; + + /** + * @param DriverInterface $driver + */ + public function __construct(DriverInterface $driver) + { + $this->driver = $driver; + } + + /** + * @inheritDoc + */ + public function validate( + string $directoryPath, + string $path, + ?string $scheme = null, + bool $absolutePath = false + ): void { + $realDirectoryPath = $this->driver->getRealPathSafety($directoryPath); + if ($realDirectoryPath[-1] !== DIRECTORY_SEPARATOR) { + $realDirectoryPath .= DIRECTORY_SEPARATOR; + } + if (!$absolutePath) { + $actualPath = $this->driver->getRealPathSafety( + $this->driver->getAbsolutePath( + $realDirectoryPath, + $path, + $scheme + ) + ); + } else { + $actualPath = $this->driver->getRealPathSafety($path); + } + + if (mb_strpos($actualPath, $realDirectoryPath) !== 0 + && $path .DIRECTORY_SEPARATOR !== $realDirectoryPath + ) { + throw new ValidatorException( + new Phrase( + 'Path "%1" cannot be used with directory "%2"', + [$path, $directoryPath] + ) + ); + } + } +} diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/PathValidatorInterface.php b/lib/internal/Magento/Framework/Filesystem/Directory/PathValidatorInterface.php new file mode 100644 index 0000000000000..ecb7da9aaeab6 --- /dev/null +++ b/lib/internal/Magento/Framework/Filesystem/Directory/PathValidatorInterface.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\Filesystem\Directory; + +use Magento\Framework\Exception\ValidatorException; + +/** + * Validate paths to be used with directories. + */ +interface PathValidatorInterface +{ + /** + * Validate if path can be used with a directory. + * + * @param string $directoryPath + * @param string $path + * @param string|null $scheme + * @param bool $absolutePath Is given path an absolute path?. + * @throws ValidatorException + * + * @return void + */ + public function validate( + string $directoryPath, + string $path, + ?string $scheme = null, + bool $absolutePath = false + ): void; +} diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/Read.php b/lib/internal/Magento/Framework/Filesystem/Directory/Read.php index 18c93f2f4e1c8..5c8cde01d95af 100644 --- a/lib/internal/Magento/Framework/Filesystem/Directory/Read.php +++ b/lib/internal/Magento/Framework/Filesystem/Directory/Read.php @@ -6,6 +6,7 @@ namespace Magento\Framework\Filesystem\Directory; use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Exception\ValidatorException; /** * @api @@ -33,21 +34,52 @@ class Read implements ReadInterface */ protected $driver; + /** + * @var PathValidatorInterface|null + */ + private $pathValidator; + /** * Constructor. Set properties. * * @param \Magento\Framework\Filesystem\File\ReadFactory $fileFactory * @param \Magento\Framework\Filesystem\DriverInterface $driver * @param string $path + * @param PathValidatorInterface|null $pathValidator */ public function __construct( \Magento\Framework\Filesystem\File\ReadFactory $fileFactory, \Magento\Framework\Filesystem\DriverInterface $driver, - $path + $path, + ?PathValidatorInterface $pathValidator = null ) { $this->fileFactory = $fileFactory; $this->driver = $driver; $this->setPath($path); + $this->pathValidator = $pathValidator; + } + + /** + * @param null|string $path + * @param null|string $scheme + * @param bool $absolutePath + * @throws ValidatorException + * + * @return void + */ + protected final function validatePath( + ?string $path, + ?string $scheme = null, + bool $absolutePath = false + ): void { + if ($path && $this->pathValidator) { + $this->pathValidator->validate( + $this->path, + $path, + $scheme, + $absolutePath + ); + } } /** @@ -69,10 +101,13 @@ protected function setPath($path) * * @param string $path * @param string $scheme + * @throws ValidatorException * @return string */ public function getAbsolutePath($path = null, $scheme = null) { + $this->validatePath($path, $scheme); + return $this->driver->getAbsolutePath($this->path, $path, $scheme); } @@ -80,10 +115,17 @@ public function getAbsolutePath($path = null, $scheme = null) * Retrieves relative path * * @param string $path + * @throws ValidatorException * @return string */ public function getRelativePath($path = null) { + $this->validatePath( + $path, + null, + $path && $path[0] === DIRECTORY_SEPARATOR + ); + return $this->driver->getRelativePath($this->path, $path); } @@ -91,10 +133,13 @@ public function getRelativePath($path = null) * Retrieve list of all entities in given path * * @param string|null $path + * @throws ValidatorException * @return string[] */ public function read($path = null) { + $this->validatePath($path); + $files = $this->driver->readDirectory($this->driver->getAbsolutePath($this->path, $path)); $result = []; foreach ($files as $file) { @@ -107,10 +152,13 @@ public function read($path = null) * Read recursively * * @param null $path + * @throws ValidatorException * @return string[] */ public function readRecursively($path = null) { + $this->validatePath($path); + $result = []; $paths = $this->driver->readDirectoryRecursively($this->driver->getAbsolutePath($this->path, $path)); /** @var \FilesystemIterator $file */ @@ -126,10 +174,13 @@ public function readRecursively($path = null) * * @param string $pattern * @param string $path [optional] + * @throws ValidatorException * @return string[] */ public function search($pattern, $path = null) { + $this->validatePath($path); + if ($path) { $absolutePath = $this->driver->getAbsolutePath($this->path, $this->getRelativePath($path)); } else { @@ -150,9 +201,12 @@ public function search($pattern, $path = null) * @param string $path [optional] * @return bool * @throws \Magento\Framework\Exception\FileSystemException + * @throws ValidatorException */ public function isExist($path = null) { + $this->validatePath($path); + return $this->driver->isExists($this->driver->getAbsolutePath($this->path, $path)); } @@ -162,9 +216,12 @@ public function isExist($path = null) * @param string $path * @return array * @throws \Magento\Framework\Exception\FileSystemException + * @throws ValidatorException */ public function stat($path) { + $this->validatePath($path); + return $this->driver->stat($this->driver->getAbsolutePath($this->path, $path)); } @@ -174,9 +231,12 @@ public function stat($path) * @param string $path [optional] * @return bool * @throws \Magento\Framework\Exception\FileSystemException + * @throws ValidatorException */ public function isReadable($path = null) { + $this->validatePath($path); + return $this->driver->isReadable($this->driver->getAbsolutePath($this->path, $path)); } @@ -184,11 +244,14 @@ public function isReadable($path = null) * Open file in read mode * * @param string $path + * @throws ValidatorException * * @return \Magento\Framework\Filesystem\File\ReadInterface */ public function openFile($path) { + $this->validatePath($path); + return $this->fileFactory->create( $this->driver->getAbsolutePath($this->path, $path), $this->driver @@ -203,9 +266,12 @@ public function openFile($path) * @param resource|null $context * @return string * @throws FileSystemException + * @throws ValidatorException */ public function readFile($path, $flag = null, $context = null) { + $this->validatePath($path); + $absolutePath = $this->driver->getAbsolutePath($this->path, $path); return $this->driver->fileGetContents($absolutePath, $flag, $context); } @@ -214,10 +280,13 @@ public function readFile($path, $flag = null, $context = null) * Check whether given path is file * * @param string $path + * @throws ValidatorException * @return bool */ public function isFile($path) { + $this->validatePath($path); + return $this->driver->isFile($this->driver->getAbsolutePath($this->path, $path)); } @@ -225,10 +294,13 @@ public function isFile($path) * Check whether given path is directory * * @param string $path [optional] + * @throws ValidatorException * @return bool */ public function isDirectory($path = null) { + $this->validatePath($path); + return $this->driver->isDirectory($this->driver->getAbsolutePath($this->path, $path)); } } diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/ReadFactory.php b/lib/internal/Magento/Framework/Filesystem/Directory/ReadFactory.php index a9fe7dcb7bf06..25a290455dc46 100644 --- a/lib/internal/Magento/Framework/Filesystem/Directory/ReadFactory.php +++ b/lib/internal/Magento/Framework/Filesystem/Directory/ReadFactory.php @@ -36,7 +36,15 @@ public function __construct(DriverPool $driverPool) public function create($path, $driverCode = DriverPool::FILE) { $driver = $this->driverPool->getDriver($driverCode); - $factory = new \Magento\Framework\Filesystem\File\ReadFactory($this->driverPool); - return new Read($factory, $driver, $path); + $factory = new \Magento\Framework\Filesystem\File\ReadFactory( + $this->driverPool + ); + + return new Read( + $factory, + $driver, + $path, + new PathValidator($driver) + ); } } diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/Write.php b/lib/internal/Magento/Framework/Filesystem/Directory/Write.php index 900d8423b31d8..78732d73456f7 100644 --- a/lib/internal/Magento/Framework/Filesystem/Directory/Write.php +++ b/lib/internal/Magento/Framework/Filesystem/Directory/Write.php @@ -7,6 +7,7 @@ namespace Magento\Framework\Filesystem\Directory; use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Exception\ValidatorException; class Write extends Read implements WriteInterface { @@ -24,16 +25,16 @@ class Write extends Read implements WriteInterface * @param \Magento\Framework\Filesystem\DriverInterface $driver * @param string $path * @param int $createPermissions + * @param PathValidatorInterface|null $pathValidator */ public function __construct( \Magento\Framework\Filesystem\File\WriteFactory $fileFactory, \Magento\Framework\Filesystem\DriverInterface $driver, $path, - $createPermissions = null + $createPermissions = null, + ?PathValidatorInterface $pathValidator = null ) { - $this->fileFactory = $fileFactory; - $this->driver = $driver; - $this->setPath($path); + parent::__construct($fileFactory, $driver, $path, $pathValidator); if (null !== $createPermissions) { $this->permissions = $createPermissions; } @@ -80,9 +81,11 @@ protected function assertIsFile($path) * @param string $path * @return bool * @throws FileSystemException + * @throws ValidatorException */ public function create($path = null) { + $this->validatePath($path); $absolutePath = $this->driver->getAbsolutePath($this->path, $path); if ($this->driver->isDirectory($absolutePath)) { return true; @@ -98,9 +101,11 @@ public function create($path = null) * @param WriteInterface $targetDirectory * @return bool * @throws FileSystemException + * @throws ValidatorException */ public function renameFile($path, $newPath, WriteInterface $targetDirectory = null) { + $this->validatePath($path); $this->assertIsFile($path); $targetDirectory = $targetDirectory ?: $this; if (!$targetDirectory->isExist($this->driver->getParentDirectory($newPath))) { @@ -119,9 +124,11 @@ public function renameFile($path, $newPath, WriteInterface $targetDirectory = nu * @param WriteInterface $targetDirectory * @return bool * @throws FileSystemException + * @throws ValidatorException */ public function copyFile($path, $destination, WriteInterface $targetDirectory = null) { + $this->validatePath($path); $this->assertIsFile($path); $targetDirectory = $targetDirectory ?: $this; @@ -142,9 +149,11 @@ public function copyFile($path, $destination, WriteInterface $targetDirectory = * @param WriteInterface $targetDirectory [optional] * @return bool * @throws \Magento\Framework\Exception\FileSystemException + * @throws ValidatorException */ public function createSymlink($path, $destination, WriteInterface $targetDirectory = null) { + $this->validatePath($path); $targetDirectory = $targetDirectory ?: $this; $parentDirectory = $this->driver->getParentDirectory($destination); if (!$targetDirectory->isExist($parentDirectory)) { @@ -162,9 +171,11 @@ public function createSymlink($path, $destination, WriteInterface $targetDirecto * @param string $path * @return bool * @throws FileSystemException + * @throws ValidatorException */ public function delete($path = null) { + $this->validatePath($path); if (!$this->isExist($path)) { return true; } @@ -184,10 +195,13 @@ public function delete($path = null) * @param int $permissions * @return bool * @throws FileSystemException + * @throws ValidatorException */ public function changePermissions($path, $permissions) { + $this->validatePath($path); $absolutePath = $this->driver->getAbsolutePath($this->path, $path); + return $this->driver->changePermissions($absolutePath, $permissions); } @@ -199,10 +213,13 @@ public function changePermissions($path, $permissions) * @param int $filePermissions * @return bool * @throws FileSystemException + * @throws ValidatorException */ public function changePermissionsRecursively($path, $dirPermissions, $filePermissions) { + $this->validatePath($path); $absolutePath = $this->driver->getAbsolutePath($this->path, $path); + return $this->driver->changePermissionsRecursively($absolutePath, $dirPermissions, $filePermissions); } @@ -213,9 +230,12 @@ public function changePermissionsRecursively($path, $dirPermissions, $filePermis * @param int|null $modificationTime * @return bool * @throws FileSystemException + * @throws ValidatorException */ public function touch($path, $modificationTime = null) { + $this->validatePath($path); + $folder = $this->driver->getParentDirectory($path); $this->create($folder); $this->assertWritable($folder); @@ -228,9 +248,12 @@ public function touch($path, $modificationTime = null) * @param null $path * @return bool * @throws \Magento\Framework\Exception\FileSystemException + * @throws ValidatorException */ public function isWritable($path = null) { + $this->validatePath($path); + return $this->driver->isWritable($this->driver->getAbsolutePath($this->path, $path)); } @@ -241,13 +264,16 @@ public function isWritable($path = null) * @param string $mode * @return \Magento\Framework\Filesystem\File\WriteInterface * @throws \Magento\Framework\Exception\FileSystemException + * @throws ValidatorException */ public function openFile($path, $mode = 'w') { + $this->validatePath($path); $folder = dirname($path); $this->create($folder); $this->assertWritable($this->isExist($path) ? $path : $folder); $absolutePath = $this->driver->getAbsolutePath($this->path, $path); + return $this->fileFactory->create($absolutePath, $this->driver, $mode); } diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/WriteFactory.php b/lib/internal/Magento/Framework/Filesystem/Directory/WriteFactory.php index a723ed6a7bea6..ff14b12f62047 100644 --- a/lib/internal/Magento/Framework/Filesystem/Directory/WriteFactory.php +++ b/lib/internal/Magento/Framework/Filesystem/Directory/WriteFactory.php @@ -37,7 +37,16 @@ public function __construct(DriverPool $driverPool) public function create($path, $driverCode = DriverPool::FILE, $createPermissions = null) { $driver = $this->driverPool->getDriver($driverCode); - $factory = new \Magento\Framework\Filesystem\File\WriteFactory($this->driverPool); - return new Write($factory, $driver, $path, $createPermissions); + $factory = new \Magento\Framework\Filesystem\File\WriteFactory( + $this->driverPool + ); + + return new Write( + $factory, + $driver, + $path, + $createPermissions, + new PathValidator($driver) + ); } } diff --git a/lib/internal/Magento/Framework/Filesystem/Driver/File.php b/lib/internal/Magento/Framework/Filesystem/Driver/File.php index 69382d66e349e..b54b02bd6de98 100644 --- a/lib/internal/Magento/Framework/Filesystem/Driver/File.php +++ b/lib/internal/Magento/Framework/Filesystem/Driver/File.php @@ -950,6 +950,13 @@ public function getRealPathSafety($path) if (strpos($path, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) === false) { return $path; } + + //Removing redundant directory separators. + $path = preg_replace( + '/\\' .DIRECTORY_SEPARATOR .'\\' .DIRECTORY_SEPARATOR .'+/', + DIRECTORY_SEPARATOR, + $path + ); $pathParts = explode(DIRECTORY_SEPARATOR, $path); $realPath = []; foreach ($pathParts as $pathPart) { diff --git a/lib/internal/Magento/Framework/Filesystem/File/WriteFactory.php b/lib/internal/Magento/Framework/Filesystem/File/WriteFactory.php index 64683a104784d..a45d6a62488f6 100644 --- a/lib/internal/Magento/Framework/Filesystem/File/WriteFactory.php +++ b/lib/internal/Magento/Framework/Filesystem/File/WriteFactory.php @@ -8,7 +8,7 @@ use Magento\Framework\Filesystem\DriverInterface; use Magento\Framework\Filesystem\DriverPool; -class WriteFactory +class WriteFactory extends ReadFactory { /** * Pool of filesystem drivers @@ -24,6 +24,7 @@ class WriteFactory */ public function __construct(DriverPool $driverPool) { + parent::__construct($driverPool); $this->driverPool = $driverPool; } diff --git a/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php b/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php deleted file mode 100644 index cbf3f89717bc6..0000000000000 --- a/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php +++ /dev/null @@ -1,356 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Test\Unit; - -use Magento\Framework\Serialize\SerializerInterface; -use Magento\Framework\Translate; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class TranslateTest extends \PHPUnit\Framework\TestCase -{ - /** @var Translate */ - protected $translate; - - /** @var \Magento\Framework\View\DesignInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $viewDesign; - - /** @var \Magento\Framework\Cache\FrontendInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $cache; - - /** @var \Magento\Framework\View\FileSystem|\PHPUnit_Framework_MockObject_MockObject */ - protected $viewFileSystem; - - /** @var \Magento\Framework\Module\ModuleList|\PHPUnit_Framework_MockObject_MockObject */ - protected $moduleList; - - /** @var \Magento\Framework\Module\Dir\Reader|\PHPUnit_Framework_MockObject_MockObject */ - protected $modulesReader; - - /** @var \Magento\Framework\App\ScopeResolverInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $scopeResolver; - - /** @var \Magento\Framework\Translate\ResourceInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $resource; - - /** @var \Magento\Framework\Locale\ResolverInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $locale; - - /** @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject */ - protected $appState; - - /** @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject */ - protected $filesystem; - - /** @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $request; - - /** @var \Magento\Framework\File\Csv|\PHPUnit_Framework_MockObject_MockObject */ - protected $csvParser; - - /** @var \Magento\Framework\App\Language\Dictionary|\PHPUnit_Framework_MockObject_MockObject */ - protected $packDictionary; - - /** @var \Magento\Framework\Filesystem\Directory\ReadInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $directory; - - protected function setUp() - { - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->viewDesign = $this->createMock(\Magento\Framework\View\DesignInterface::class); - $this->cache = $this->createMock(\Magento\Framework\Cache\FrontendInterface::class); - $this->viewFileSystem = $this->createMock(\Magento\Framework\View\FileSystem::class); - $this->moduleList = $this->createMock(\Magento\Framework\Module\ModuleList::class); - $this->modulesReader = $this->createMock(\Magento\Framework\Module\Dir\Reader::class); - $this->scopeResolver = $this->createMock(\Magento\Framework\App\ScopeResolverInterface::class); - $this->resource = $this->createMock(\Magento\Framework\Translate\ResourceInterface::class); - $this->locale = $this->createMock(\Magento\Framework\Locale\ResolverInterface::class); - $this->appState = $this->createMock(\Magento\Framework\App\State::class); - $this->request = $this->getMockForAbstractClass( - \Magento\Framework\App\RequestInterface::class, - [], - '', - false, - false, - true, - ['getParam', 'getControllerModule'] - ); - $this->csvParser = $this->createMock(\Magento\Framework\File\Csv::class); - $this->packDictionary = $this->createMock(\Magento\Framework\App\Language\Dictionary::class); - $this->directory = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadInterface::class); - $filesystem = $this->createMock(\Magento\Framework\Filesystem::class); - $filesystem->expects($this->once())->method('getDirectoryRead')->will($this->returnValue($this->directory)); - - $this->translate = new Translate( - $this->viewDesign, - $this->cache, - $this->viewFileSystem, - $this->moduleList, - $this->modulesReader, - $this->scopeResolver, - $this->resource, - $this->locale, - $this->appState, - $filesystem, - $this->request, - $this->csvParser, - $this->packDictionary - ); - - $serializerMock = $this->createMock(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 - ); - } - - /** - * @param string $area - * @param bool $forceReload - * @param array $cachedData - * @return void - * @dataProvider dataProviderLoadDataCachedTranslation - */ - public function testLoadDataCachedTranslation($area, $forceReload, array $cachedData) - { - $this->expectsSetConfig('Magento/luma'); - - $this->cache->expects($this->once()) - ->method('load') - ->willReturn(json_encode($cachedData)); - - $this->appState->expects($this->exactly($area ? 0 : 1)) - ->method('getAreaCode') - ->willReturn('frontend'); - - $this->translate->loadData($area, $forceReload); - $this->assertEquals($cachedData, $this->translate->getData()); - } - - /** - * @return array - */ - public function dataProviderLoadDataCachedTranslation() - { - $cachedData = ['cached 1' => 'translated 1', 'cached 2' => 'translated 2']; - return [ - ['adminhtml', false, $cachedData], - ['frontend', false, $cachedData], - [null, false, $cachedData], - ]; - } - - /** - * @param string $area - * @param bool $forceReload - * @return void - * @dataProvider dataProviderForTestLoadData - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - public function testLoadData($area, $forceReload) - { - $this->expectsSetConfig('Magento/luma'); - - $this->appState->expects($this->exactly($area ? 0 : 1)) - ->method('getAreaCode') - ->willReturn('frontend'); - - $this->cache->expects($this->exactly($forceReload ? 0 : 1)) - ->method('load') - ->willReturn(false); - - $this->directory->expects($this->any())->method('isExist')->willReturn(true); - - // _loadModuleTranslation() - $modules = ['some_module', 'other_module', 'another_module', 'current_module']; - $this->request->expects($this->any()) - ->method('getControllerModule') - ->willReturn('current_module'); - $this->moduleList->expects($this->once())->method('getNames')->willReturn($modules); - $moduleData = [ - 'module original' => 'module translated', - 'module theme' => 'module-theme original translated', - 'module pack' => 'module-pack original translated', - 'module db' => 'module-db original translated', - ]; - $this->modulesReader->expects($this->any())->method('getModuleDir')->willReturn('/app/module'); - $themeData = [ - 'theme original' => 'theme translated', - 'module theme' => 'theme translated overwrite', - 'module pack' => 'theme-pack translated overwrite', - 'module db' => 'theme-db translated overwrite', - ]; - $this->csvParser->expects($this->any()) - ->method('getDataPairs') - ->willReturnMap( - [ - ['/app/module/en_US.csv', 0, 1, $moduleData], - ['/app/module/en_GB.csv', 0, 1, $moduleData], - ['/theme.csv', 0, 1, $themeData], - ] - ); - - // _loadPackTranslation - $packData = [ - 'pack original' => 'pack translated', - 'module pack' => 'pack translated overwrite', - 'module db' => 'pack-db translated overwrite', - ]; - $this->packDictionary->expects($this->once())->method('getDictionary')->willReturn($packData); - - // _loadThemeTranslation() - $this->viewFileSystem->expects($this->any()) - ->method('getLocaleFileName') - ->will($this->returnValue('/theme.csv')); - - // _loadDbTranslation() - $dbData = [ - 'db original' => 'db translated', - 'module db' => 'db translated overwrite', - ]; - $this->resource->expects($this->any())->method('getTranslationArray')->willReturn($dbData); - - $this->cache->expects($this->exactly($forceReload ? 0 : 1))->method('save'); - - $this->translate->loadData($area, $forceReload); - - $expected = [ - 'module original' => 'module translated', - 'module theme' => 'theme translated overwrite', - 'module pack' => 'theme-pack translated overwrite', - 'module db' => 'db translated overwrite', - 'theme original' => 'theme translated', - 'pack original' => 'pack translated', - 'db original' => 'db translated', - ]; - $this->assertEquals($expected, $this->translate->getData()); - } - - /** - * @return array - */ - public function dataProviderForTestLoadData() - { - return [ - ['adminhtml', true], - ['adminhtml', false], - ['frontend', true], - ['frontend', false], - [null, true], - [null, false], - ]; - } - - /** - * @param $data - * @param $result - * @return void - * @dataProvider dataProviderForTestGetData - */ - public function testGetData($data, $result) - { - $this->cache->expects($this->once()) - ->method('load') - ->will($this->returnValue(json_encode($data))); - $this->expectsSetConfig('themeId'); - $this->translate->loadData('frontend'); - $this->assertEquals($result, $this->translate->getData()); - } - - /** - * @return array - */ - public function dataProviderForTestGetData() - { - $data = ['original 1' => 'translated 1', 'original 2' => 'translated 2']; - return [ - [$data, $data], - [null, []], - ]; - } - - public function testGetLocale() - { - $this->locale->expects($this->once())->method('getLocale')->will($this->returnValue('en_US')); - $this->assertEquals('en_US', $this->translate->getLocale()); - - $this->locale->expects($this->never())->method('getLocale'); - $this->assertEquals('en_US', $this->translate->getLocale()); - - $this->locale->expects($this->never())->method('getLocale'); - $this->translate->setLocale('en_GB'); - $this->assertEquals('en_GB', $this->translate->getLocale()); - } - - public function testSetLocale() - { - $this->translate->setLocale('en_GB'); - $this->locale->expects($this->never())->method('getLocale'); - $this->assertEquals('en_GB', $this->translate->getLocale()); - } - - public function testGetTheme() - { - $this->request->expects($this->at(0))->method('getParam')->with('theme')->will($this->returnValue('')); - - $requestTheme = ['theme_title' => 'Theme Title']; - $this->request->expects($this->at(1))->method('getParam')->with('theme') - ->will($this->returnValue($requestTheme)); - - $this->assertEquals('theme', $this->translate->getTheme()); - $this->assertEquals('themeTheme Title', $this->translate->getTheme()); - } - - public function testLoadDataNoTheme() - { - $forceReload = true; - $this->expectsSetConfig(null, null); - $this->moduleList->expects($this->once())->method('getNames')->will($this->returnValue([])); - $this->appState->expects($this->once())->method('getAreaCode')->will($this->returnValue('frontend')); - $this->packDictionary->expects($this->once())->method('getDictionary')->will($this->returnValue([])); - $this->resource->expects($this->any())->method('getTranslationArray')->will($this->returnValue([])); - $this->assertEquals($this->translate, $this->translate->loadData(null, $forceReload)); - } - - /** - * Declare calls expectation for setConfig() method - */ - protected function expectsSetConfig($themeId, $localeCode = 'en_US') - { - $this->locale->expects($this->any())->method('getLocale')->will($this->returnValue($localeCode)); - $scope = new \Magento\Framework\DataObject(['code' => 'frontendCode', 'id' => 1]); - $scopeAdmin = new \Magento\Framework\DataObject(['code' => 'adminCode', 'id' => 0]); - $this->scopeResolver->expects($this->any()) - ->method('getScope') - ->will( - $this->returnValueMap( - [ - [null, $scope], - ['admin', $scopeAdmin], - ] - ) - ); - $designTheme = $this->getMockBuilder(\Magento\Theme\Model\Theme::class) - ->disableOriginalConstructor() - ->getMock(); - - $designTheme->expects($this->once()) - ->method('getThemePath') - ->willReturn($themeId); - - $this->viewDesign->expects($this->any())->method('getDesignTheme')->will($this->returnValue($designTheme)); - } -} diff --git a/lib/internal/Magento/Framework/Translate.php b/lib/internal/Magento/Framework/Translate.php index 2f80ab2befbf8..ffa8e25031064 100644 --- a/lib/internal/Magento/Framework/Translate.php +++ b/lib/internal/Magento/Framework/Translate.php @@ -7,6 +7,9 @@ namespace Magento\Framework; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Filesystem\Driver\File; +use Magento\Framework\Filesystem\DriverInterface; /** * Translate library @@ -120,6 +123,11 @@ class Translate implements \Magento\Framework\TranslateInterface */ private $serializer; + /** + * @var DriverInterface + */ + private $fileDriver; + /** * @param \Magento\Framework\View\DesignInterface $viewDesign * @param \Magento\Framework\Cache\FrontendInterface $cache @@ -134,6 +142,7 @@ class Translate implements \Magento\Framework\TranslateInterface * @param \Magento\Framework\App\RequestInterface $request * @param \Magento\Framework\File\Csv $csvParser * @param \Magento\Framework\App\Language\Dictionary $packDictionary + * @param DriverInterface|null $fileDriver * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -150,7 +159,8 @@ public function __construct( \Magento\Framework\Filesystem $filesystem, \Magento\Framework\App\RequestInterface $request, \Magento\Framework\File\Csv $csvParser, - \Magento\Framework\App\Language\Dictionary $packDictionary + \Magento\Framework\App\Language\Dictionary $packDictionary, + DriverInterface $fileDriver = null ) { $this->_viewDesign = $viewDesign; $this->_cache = $cache; @@ -165,6 +175,8 @@ public function __construct( $this->directory = $filesystem->getDirectoryRead(DirectoryList::ROOT); $this->_csvParser = $csvParser; $this->packDictionary = $packDictionary; + $this->fileDriver = $fileDriver + ?? ObjectManager::getInstance()->get(File::class); $this->_config = [ self::CONFIG_AREA_KEY => null, @@ -400,7 +412,7 @@ protected function _getThemeTranslationFile($locale) protected function _getFileData($file) { $data = []; - if ($this->directory->isExist($this->directory->getRelativePath($file))) { + if ($this->fileDriver->isExists($file)) { $this->_csvParser->setDelimiter(','); $data = $this->_csvParser->getDataPairs($file); } From bb7583afe82ddb16801648b8ce204eeee948d557 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Fri, 13 Jul 2018 16:51:07 +0300 Subject: [PATCH 0266/1171] magento/magento2#15895: Handle type errors for objects creation - Added declaration of strict types --- .../ObjectManager/TestAsset/ConstructorWithTypeError.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithTypeError.php b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithTypeError.php index 3470f9508af46..92d16e4df3511 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithTypeError.php +++ b/dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ConstructorWithTypeError.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\ObjectManager\TestAsset; From fb4a81b71afc99532ba046ccdc95174ed0ce4a8e Mon Sep 17 00:00:00 2001 From: Freek Vandeursen <freek@athleteshop.com> Date: Fri, 13 Jul 2018 15:55:28 +0200 Subject: [PATCH 0267/1171] Update test --- .../Import/Product/Type/AbstractTypeTest.php | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/AbstractTypeTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/AbstractTypeTest.php index 70ac3a4fa2e97..bd2fe896b8c0a 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/AbstractTypeTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/AbstractTypeTest.php @@ -134,8 +134,28 @@ protected function setUp() ->expects($this->any()) ->method('addFieldToFilter') ->with( - 'main_table.attribute_id', - ['in' => ['attribute_id', 'boolean_attribute']] + ['main_table.attribute_id', 'main_table.attribute_code'], + [ + [ + 'in' => + [ + 'attribute_id', + 'boolean_attribute', + ], + ], + [ + 'in' => + [ + 'related_tgtr_position_behavior', + 'related_tgtr_position_limit', + 'upsell_tgtr_position_behavior', + 'upsell_tgtr_position_limit', + 'thumbnail_label', + 'small_image_label', + 'image_label', + ], + ], + ] ) ->willReturn([$attribute1, $attribute2]); From f08d97003ed8d1dc54f42d785ab50a9ea27ef7be Mon Sep 17 00:00:00 2001 From: Oleh Posyniak <oposyniak@magento.com> Date: Fri, 13 Jul 2018 09:04:36 -0500 Subject: [PATCH 0268/1171] MAGETWO-93062: Cron Schedule table is growing so fast --- app/code/Magento/Indexer/etc/cron_groups.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Indexer/etc/cron_groups.xml b/app/code/Magento/Indexer/etc/cron_groups.xml index 7afabee1949c0..076e73009ec21 100644 --- a/app/code/Magento/Indexer/etc/cron_groups.xml +++ b/app/code/Magento/Indexer/etc/cron_groups.xml @@ -11,8 +11,8 @@ <schedule_ahead_for>4</schedule_ahead_for> <schedule_lifetime>2</schedule_lifetime> <history_cleanup_every>10</history_cleanup_every> - <history_success_lifetime>10080</history_success_lifetime> - <history_failure_lifetime>10080</history_failure_lifetime> + <history_success_lifetime>60</history_success_lifetime> + <history_failure_lifetime>4320</history_failure_lifetime> <use_separate_process>1</use_separate_process> </group> </config> From dc4aed9ae5c65b123a53f257397de727d9ae632c Mon Sep 17 00:00:00 2001 From: Robert Clendenin <clendenin@protonmail.com> Date: Fri, 13 Jul 2018 10:08:52 -0500 Subject: [PATCH 0269/1171] MC-230: Customer should be able to see basic bundle product details --- .../Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml index 9a04bfd06c899..a475ef16ed5c5 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml @@ -26,6 +26,7 @@ <!-- Admin Login--> <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> </before> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> From 57fa243beb00e0c08c45bf91f4cb5ef551eb85b0 Mon Sep 17 00:00:00 2001 From: KevinBKozan <kkozan@magento.com> Date: Fri, 13 Jul 2018 10:35:56 -0500 Subject: [PATCH 0270/1171] MQE-1074: Verify MFTF Compatibility with Phpunit 7 - dev/tests codeception version synced with MFTF, lock file updated --- dev/tests/acceptance/composer.json | 2 +- dev/tests/acceptance/composer.lock | 416 ++++++++++++++++------------- 2 files changed, 229 insertions(+), 189 deletions(-) diff --git a/dev/tests/acceptance/composer.json b/dev/tests/acceptance/composer.json index a20176a29c4c8..83cad123f8568 100755 --- a/dev/tests/acceptance/composer.json +++ b/dev/tests/acceptance/composer.json @@ -11,7 +11,7 @@ }, "require": { "php": "~7.1.3||~7.2.0", - "codeception/codeception": "~2.3.4", + "codeception/codeception": "~2.3.4 || ~2.4.0", "consolidation/robo": "^1.0.0", "vlucas/phpdotenv": "^2.4" }, diff --git a/dev/tests/acceptance/composer.lock b/dev/tests/acceptance/composer.lock index f8c6bbc137211..fe5874e232398 100644 --- a/dev/tests/acceptance/composer.lock +++ b/dev/tests/acceptance/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "46ca2d50566f5069daef753664080c5a", + "content-hash": "b93d599d375af66b29edfd8a35875e69", "packages": [ { "name": "behat/gherkin", - "version": "v4.4.5", + "version": "v4.5.1", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" + "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", + "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", "shasum": "" }, "require": { @@ -63,35 +63,32 @@ "gherkin", "parser" ], - "time": "2016-10-30T11:50:56+00:00" + "time": "2017-08-30T11:04:43+00:00" }, { "name": "codeception/codeception", - "version": "2.3.9", + "version": "2.4.3", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e" + "reference": "13b2db0d54068afaabf3ca8ac8b6591d69018f46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/13b2db0d54068afaabf3ca8ac8b6591d69018f46", + "reference": "13b2db0d54068afaabf3ca8ac8b6591d69018f46", "shasum": "" }, "require": { - "behat/gherkin": "~4.4.0", - "codeception/stub": "^1.0", + "behat/gherkin": "^4.4.0", + "codeception/phpunit-wrapper": "^6.0.9|^7.0.6", + "codeception/stub": "^2.0", "ext-json": "*", "ext-mbstring": "*", "facebook/webdriver": ">=1.1.3 <2.0", "guzzlehttp/guzzle": ">=4.1.4 <7.0", "guzzlehttp/psr7": "~1.0", - "php": ">=5.4.0 <8.0", - "phpunit/php-code-coverage": ">=2.2.4 <6.0", - "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", - "sebastian/comparator": ">1.1 <3.0", - "sebastian/diff": ">=1.4 <3.0", + "php": ">=5.6.0 <8.0", "symfony/browser-kit": ">=2.7 <5.0", "symfony/console": ">=2.7 <5.0", "symfony/css-selector": ">=2.7 <5.0", @@ -157,20 +154,63 @@ "functional testing", "unit testing" ], - "time": "2018-02-26T23:29:41+00:00" + "time": "2018-06-26T14:09:28+00:00" + }, + { + "name": "codeception/phpunit-wrapper", + "version": "7.1.4", + "source": { + "type": "git", + "url": "https://github.com/Codeception/phpunit-wrapper.git", + "reference": "f18ed631f1eddbb603d72219f577d223b23a1f89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/f18ed631f1eddbb603d72219f577d223b23a1f89", + "reference": "f18ed631f1eddbb603d72219f577d223b23a1f89", + "shasum": "" + }, + "require": { + "phpunit/php-code-coverage": "^6.0", + "phpunit/phpunit": "^7.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0" + }, + "require-dev": { + "codeception/specify": "*", + "vlucas/phpdotenv": "^2.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Codeception\\PHPUnit\\": "src\\" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Davert", + "email": "davert.php@resend.cc" + } + ], + "description": "PHPUnit classes used by Codeception", + "time": "2018-06-20T20:07:21+00:00" }, { "name": "codeception/stub", - "version": "1.0.4", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/Codeception/Stub.git", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805" + "reference": "b2eff325d8ff0b824ff659048be7be4e5767d7d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/681b62348837a5ef07d10d8a226f5bc358cc8805", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/b2eff325d8ff0b824ff659048be7be4e5767d7d0", + "reference": "b2eff325d8ff0b824ff659048be7be4e5767d7d0", "shasum": "" }, "require": { @@ -190,20 +230,20 @@ "MIT" ], "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-05-17T09:31:08+00:00" + "time": "2018-05-18T14:33:08+00:00" }, { "name": "consolidation/annotated-command", - "version": "2.8.3", + "version": "2.8.4", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "8f8f5da2ca06fbd3a85f7d551c49f844b7c59437" + "reference": "651541a0b68318a2a202bda558a676e5ad92223c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/8f8f5da2ca06fbd3a85f7d551c49f844b7c59437", - "reference": "8f8f5da2ca06fbd3a85f7d551c49f844b7c59437", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/651541a0b68318a2a202bda558a676e5ad92223c", + "reference": "651541a0b68318a2a202bda558a676e5ad92223c", "shasum": "" }, "require": { @@ -215,9 +255,9 @@ "symfony/finder": "^2.5|^3|^4" }, "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4.8", - "satooshi/php-coveralls": "^1.0.2 | dev-master", + "g1a/composer-test-scenarios": "^2", + "phpunit/phpunit": "^6", + "satooshi/php-coveralls": "^2", "squizlabs/php_codesniffer": "^2.7" }, "type": "library", @@ -242,20 +282,20 @@ } ], "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2018-02-23T16:32:04+00:00" + "time": "2018-05-25T18:04:25+00:00" }, { "name": "consolidation/config", - "version": "1.0.9", + "version": "1.0.11", "source": { "type": "git", "url": "https://github.com/consolidation/config.git", - "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c" + "reference": "ede41d946078e97e7a9513aadc3352f1c26817af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/34ca8d7c1ee60a7b591b10617114cf1210a2e92c", - "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c", + "url": "https://api.github.com/repos/consolidation/config/zipball/ede41d946078e97e7a9513aadc3352f1c26817af", + "reference": "ede41d946078e97e7a9513aadc3352f1c26817af", "shasum": "" }, "require": { @@ -264,7 +304,7 @@ "php": ">=5.4.0" }, "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", + "g1a/composer-test-scenarios": "^1", "phpunit/phpunit": "^4", "satooshi/php-coveralls": "^1.0", "squizlabs/php_codesniffer": "2.*", @@ -296,20 +336,20 @@ } ], "description": "Provide configuration services for a commandline tool.", - "time": "2017-12-22T17:28:19+00:00" + "time": "2018-05-27T01:17:02+00:00" }, { "name": "consolidation/log", - "version": "1.0.5", + "version": "1.0.6", "source": { "type": "git", "url": "https://github.com/consolidation/log.git", - "reference": "dbc7c535f319a4a2d5a5077738f8eb7c10df8821" + "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/dbc7c535f319a4a2d5a5077738f8eb7c10df8821", - "reference": "dbc7c535f319a4a2d5a5077738f8eb7c10df8821", + "url": "https://api.github.com/repos/consolidation/log/zipball/dfd8189a771fe047bf3cd669111b2de5f1c79395", + "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395", "shasum": "" }, "require": { @@ -318,8 +358,9 @@ "symfony/console": "^2.8|^3|^4" }, "require-dev": { + "g1a/composer-test-scenarios": "^1", "phpunit/phpunit": "4.*", - "satooshi/php-coveralls": "dev-master", + "satooshi/php-coveralls": "^2", "squizlabs/php_codesniffer": "2.*" }, "type": "library", @@ -344,20 +385,20 @@ } ], "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", - "time": "2017-11-29T01:44:16+00:00" + "time": "2018-05-25T18:14:39+00:00" }, { "name": "consolidation/output-formatters", - "version": "3.2.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/consolidation/output-formatters.git", - "reference": "da889e4bce19f145ca4ec5b1725a946f4eb625a9" + "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/da889e4bce19f145ca4ec5b1725a946f4eb625a9", - "reference": "da889e4bce19f145ca4ec5b1725a946f4eb625a9", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", + "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", "shasum": "" }, "require": { @@ -366,7 +407,7 @@ "symfony/finder": "^2.5|^3|^4" }, "require-dev": { - "g-1-a/composer-test-scenarios": "^2", + "g1a/composer-test-scenarios": "^2", "phpunit/phpunit": "^5.7.27", "satooshi/php-coveralls": "^2", "squizlabs/php_codesniffer": "^2.7", @@ -399,25 +440,25 @@ } ], "description": "Format text by applying transformations provided by plug-in formatters.", - "time": "2018-03-20T15:18:32+00:00" + "time": "2018-05-25T18:02:34+00:00" }, { "name": "consolidation/robo", - "version": "1.2.3", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "54a13e268917b92576d75e10dca8227b95a574d9" + "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/54a13e268917b92576d75e10dca8227b95a574d9", - "reference": "54a13e268917b92576d75e10dca8227b95a574d9", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/ac563abfadf7cb7314b4e152f2b5033a6c255f6f", + "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f", "shasum": "" }, "require": { "consolidation/annotated-command": "^2.8.2", - "consolidation/config": "^1.0.1", + "consolidation/config": "^1.0.10", "consolidation/log": "~1", "consolidation/output-formatters": "^3.1.13", "grasmash/yaml-expander": "^1.3", @@ -436,7 +477,7 @@ "codeception/aspect-mock": "^1|^2.1.1", "codeception/base": "^2.3.7", "codeception/verify": "^0.3.2", - "g-1-a/composer-test-scenarios": "^2", + "g1a/composer-test-scenarios": "^2", "goaop/framework": "~2.1.2", "goaop/parser-reflection": "^1.1.0", "natxet/cssmin": "3.0.4", @@ -479,7 +520,7 @@ } ], "description": "Modern task runner", - "time": "2018-04-06T05:27:37+00:00" + "time": "2018-05-27T01:42:53+00:00" }, { "name": "container-interop/container-interop", @@ -1028,16 +1069,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.8.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "478465659fd987669df0bd8a9bf22a8710e5f1b6" + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/478465659fd987669df0bd8a9bf22a8710e5f1b6", - "reference": "478465659fd987669df0bd8a9bf22a8710e5f1b6", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", "shasum": "" }, "require": { @@ -1072,7 +1113,7 @@ "object", "object graph" ], - "time": "2018-05-29T17:25:09+00:00" + "time": "2018-06-11T23:09:50+00:00" }, { "name": "phar-io/manifest", @@ -1393,40 +1434,40 @@ }, { "name": "phpunit/php-code-coverage", - "version": "5.3.2", + "version": "6.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac" + "reference": "4cab20a326d14de7575a8e235c70d879b569a57a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4cab20a326d14de7575a8e235c70d879b569a57a", + "reference": "4cab20a326d14de7575a8e235c70d879b569a57a", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^7.0", + "php": "^7.1", "phpunit/php-file-iterator": "^1.4.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", + "phpunit/php-token-stream": "^3.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", + "sebastian/environment": "^3.1", "sebastian/version": "^2.0.1", "theseer/tokenizer": "^1.1" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^7.0" }, "suggest": { - "ext-xdebug": "^2.5.5" + "ext-xdebug": "^2.6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.3.x-dev" + "dev-master": "6.0-dev" } }, "autoload": { @@ -1452,7 +1493,7 @@ "testing", "xunit" ], - "time": "2018-04-06T15:36:58+00:00" + "time": "2018-05-28T11:49:20+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1544,28 +1585,28 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.9", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b8454ea6958c3dee38453d3bd571e023108c91f", + "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1580,7 +1621,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -1589,33 +1630,33 @@ "keywords": [ "timer" ], - "time": "2017-02-26T11:10:40+00:00" + "time": "2018-02-01T13:07:23+00:00" }, { "name": "phpunit/php-token-stream", - "version": "2.0.2", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" + "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/21ad88bbba7c3d93530d93994e0a33cd45f02ace", + "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.2.4" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1638,20 +1679,20 @@ "keywords": [ "tokenizer" ], - "time": "2017-11-27T05:48:46+00:00" + "time": "2018-02-01T13:16:43+00:00" }, { "name": "phpunit/phpunit", - "version": "6.5.8", + "version": "7.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b" + "reference": "ca64dba53b88aba6af32aebc6b388068db95c435" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4f21a3c6b97c42952fd5c2837bb354ec0199b97b", - "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ca64dba53b88aba6af32aebc6b388068db95c435", + "reference": "ca64dba53b88aba6af32aebc6b388068db95c435", "shasum": "" }, "require": { @@ -1663,15 +1704,15 @@ "myclabs/deep-copy": "^1.6.1", "phar-io/manifest": "^1.0.1", "phar-io/version": "^1.0", - "php": "^7.0", + "php": "^7.1", "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", + "phpunit/php-code-coverage": "^6.0.1", "phpunit/php-file-iterator": "^1.4.3", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.5", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", + "phpunit/php-timer": "^2.0", + "phpunit/phpunit-mock-objects": "^6.1.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", "sebastian/environment": "^3.1", "sebastian/exporter": "^3.1", "sebastian/global-state": "^2.0", @@ -1679,16 +1720,12 @@ "sebastian/resource-operations": "^1.0", "sebastian/version": "^2.0.1" }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" - }, "require-dev": { "ext-pdo": "*" }, "suggest": { "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" + "phpunit/php-invoker": "^2.0" }, "bin": [ "phpunit" @@ -1696,7 +1733,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.5.x-dev" + "dev-master": "7.1-dev" } }, "autoload": { @@ -1722,33 +1759,30 @@ "testing", "xunit" ], - "time": "2018-04-10T11:38:34+00:00" + "time": "2018-04-29T15:09:19+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "5.0.7", + "version": "6.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "3eaf040f20154d27d6da59ca2c6e28ac8fd56dce" + "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3eaf040f20154d27d6da59ca2c6e28ac8fd56dce", - "reference": "3eaf040f20154d27d6da59ca2c6e28ac8fd56dce", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/f9756fd4f43f014cb2dca98deeaaa8ce5500a36e", + "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.5", - "php": "^7.0", + "php": "^7.1", "phpunit/php-text-template": "^1.2.1", "sebastian/exporter": "^3.1" }, - "conflict": { - "phpunit/phpunit": "<6.0" - }, "require-dev": { - "phpunit/phpunit": "^6.5" + "phpunit/phpunit": "^7.0" }, "suggest": { "ext-soap": "*" @@ -1756,7 +1790,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0.x-dev" + "dev-master": "6.1-dev" } }, "autoload": { @@ -1781,7 +1815,7 @@ "mock", "xunit" ], - "time": "2018-05-29T13:50:43+00:00" + "time": "2018-05-29T13:54:20+00:00" }, { "name": "psr/container", @@ -1976,30 +2010,30 @@ }, { "name": "sebastian/comparator", - "version": "2.1.3", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", + "php": "^7.1", + "sebastian/diff": "^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^6.4" + "phpunit/phpunit": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2036,32 +2070,33 @@ "compare", "equality" ], - "time": "2018-02-01T13:46:46+00:00" + "time": "2018-07-12T15:12:46+00:00" }, { "name": "sebastian/diff", - "version": "2.0.1", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" + "reference": "366541b989927187c4ca70490a35615d3fef2dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", + "reference": "366541b989927187c4ca70490a35615d3fef2dce", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "phpunit/phpunit": "^7.0", + "symfony/process": "^2 || ^3.3 || ^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2086,9 +2121,12 @@ "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "diff" + "diff", + "udiff", + "unidiff", + "unified diff" ], - "time": "2017-08-03T08:09:46+00:00" + "time": "2018-06-10T07:54:39+00:00" }, { "name": "sebastian/environment", @@ -2490,16 +2528,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "16355a5d0f1499c77efee5ff68d8ea61624d4da1" + "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/16355a5d0f1499c77efee5ff68d8ea61624d4da1", - "reference": "16355a5d0f1499c77efee5ff68d8ea61624d4da1", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", + "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", "shasum": "" }, "require": { @@ -2543,20 +2581,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2018-04-06T10:52:03+00:00" + "time": "2018-06-04T17:31:56+00:00" }, { "name": "symfony/console", - "version": "v4.0.9", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3e820bc2c520a87ca209ad8fa961c97f42e0b4ae" + "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3e820bc2c520a87ca209ad8fa961c97f42e0b4ae", - "reference": "3e820bc2c520a87ca209ad8fa961c97f42e0b4ae", + "url": "https://api.github.com/repos/symfony/console/zipball/70591cda56b4b47c55776ac78e157c4bb6c8b43f", + "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f", "shasum": "" }, "require": { @@ -2584,7 +2622,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -2611,11 +2649,11 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-04-30T01:23:47+00:00" + "time": "2018-05-31T10:17:53+00:00" }, { "name": "symfony/css-selector", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -2668,7 +2706,7 @@ }, { "name": "symfony/dom-crawler", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", @@ -2725,16 +2763,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v4.0.9", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "63353a71073faf08f62caab4e6889b06a787f07b" + "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/63353a71073faf08f62caab4e6889b06a787f07b", - "reference": "63353a71073faf08f62caab4e6889b06a787f07b", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2391ed210a239868e7256eb6921b1bd83f3087b5", + "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5", "shasum": "" }, "require": { @@ -2757,7 +2795,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -2784,29 +2822,30 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-04-06T07:35:43+00:00" + "time": "2018-04-06T07:35:57+00:00" }, { "name": "symfony/filesystem", - "version": "v4.0.9", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21" + "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21", - "reference": "5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", + "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -2833,20 +2872,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-02-22T10:50:29+00:00" + "time": "2018-05-30T07:26:09+00:00" }, { "name": "symfony/finder", - "version": "v4.0.9", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ca27c02b7a3fef4828c998c2ff9ba7aae1641c49" + "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ca27c02b7a3fef4828c998c2ff9ba7aae1641c49", - "reference": "ca27c02b7a3fef4828c998c2ff9ba7aae1641c49", + "url": "https://api.github.com/repos/symfony/finder/zipball/84714b8417d19e4ba02ea78a41a975b3efaafddb", + "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb", "shasum": "" }, "require": { @@ -2855,7 +2894,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -2882,7 +2921,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-04-04T05:10:37+00:00" + "time": "2018-06-19T21:38:16+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3000,16 +3039,16 @@ }, { "name": "symfony/process", - "version": "v4.0.9", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d7dc1ee5dfe9f732cb1bba7310f5b99f2b7a6d25" + "reference": "1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d7dc1ee5dfe9f732cb1bba7310f5b99f2b7a6d25", - "reference": "d7dc1ee5dfe9f732cb1bba7310f5b99f2b7a6d25", + "url": "https://api.github.com/repos/symfony/process/zipball/1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a", + "reference": "1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a", "shasum": "" }, "require": { @@ -3018,7 +3057,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3045,24 +3084,25 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-04-03T05:24:00+00:00" + "time": "2018-05-31T10:17:53+00:00" }, { "name": "symfony/yaml", - "version": "v4.0.9", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "275ad099e4cbe612a2acbca14a16dd1c5311324d" + "reference": "80e4bfa9685fc4a09acc4a857ec16974a9cd944e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/275ad099e4cbe612a2acbca14a16dd1c5311324d", - "reference": "275ad099e4cbe612a2acbca14a16dd1c5311324d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/80e4bfa9685fc4a09acc4a857ec16974a9cd944e", + "reference": "80e4bfa9685fc4a09acc4a857ec16974a9cd944e", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/console": "<3.4" @@ -3076,7 +3116,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3103,7 +3143,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-04-08T08:49:08+00:00" + "time": "2018-05-30T07:26:09+00:00" }, { "name": "theseer/tokenizer", @@ -3147,28 +3187,28 @@ }, { "name": "vlucas/phpdotenv", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c" + "reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", - "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/6ae3e2e6494bb5e58c2decadafc3de7f1453f70a", + "reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a", "shasum": "" }, "require": { "php": ">=5.3.9" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.0" + "phpunit/phpunit": "^4.8.35 || ^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.4-dev" + "dev-master": "2.5-dev" } }, "autoload": { @@ -3178,7 +3218,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause-Attribution" + "BSD-3-Clause" ], "authors": [ { @@ -3193,7 +3233,7 @@ "env", "environment" ], - "time": "2016-09-01T10:05:43+00:00" + "time": "2018-07-01T10:25:50+00:00" }, { "name": "webmozart/assert", From 3761a9cc7268c9dbf4cdb9ccb5ea2db5842bfb5b Mon Sep 17 00:00:00 2001 From: KevinBKozan <kkozan@magento.com> Date: Fri, 13 Jul 2018 10:57:56 -0500 Subject: [PATCH 0271/1171] MQE-1074: Verify MFTF Compatibility with Phpunit 7 - dev tests lock file update --- dev/tests/acceptance/composer.json | 2 +- dev/tests/acceptance/composer.lock | 416 ++++++++++++++++------------- 2 files changed, 229 insertions(+), 189 deletions(-) diff --git a/dev/tests/acceptance/composer.json b/dev/tests/acceptance/composer.json index a20176a29c4c8..83cad123f8568 100755 --- a/dev/tests/acceptance/composer.json +++ b/dev/tests/acceptance/composer.json @@ -11,7 +11,7 @@ }, "require": { "php": "~7.1.3||~7.2.0", - "codeception/codeception": "~2.3.4", + "codeception/codeception": "~2.3.4 || ~2.4.0", "consolidation/robo": "^1.0.0", "vlucas/phpdotenv": "^2.4" }, diff --git a/dev/tests/acceptance/composer.lock b/dev/tests/acceptance/composer.lock index f8c6bbc137211..fe5874e232398 100644 --- a/dev/tests/acceptance/composer.lock +++ b/dev/tests/acceptance/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "46ca2d50566f5069daef753664080c5a", + "content-hash": "b93d599d375af66b29edfd8a35875e69", "packages": [ { "name": "behat/gherkin", - "version": "v4.4.5", + "version": "v4.5.1", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" + "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", + "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", "shasum": "" }, "require": { @@ -63,35 +63,32 @@ "gherkin", "parser" ], - "time": "2016-10-30T11:50:56+00:00" + "time": "2017-08-30T11:04:43+00:00" }, { "name": "codeception/codeception", - "version": "2.3.9", + "version": "2.4.3", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e" + "reference": "13b2db0d54068afaabf3ca8ac8b6591d69018f46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/13b2db0d54068afaabf3ca8ac8b6591d69018f46", + "reference": "13b2db0d54068afaabf3ca8ac8b6591d69018f46", "shasum": "" }, "require": { - "behat/gherkin": "~4.4.0", - "codeception/stub": "^1.0", + "behat/gherkin": "^4.4.0", + "codeception/phpunit-wrapper": "^6.0.9|^7.0.6", + "codeception/stub": "^2.0", "ext-json": "*", "ext-mbstring": "*", "facebook/webdriver": ">=1.1.3 <2.0", "guzzlehttp/guzzle": ">=4.1.4 <7.0", "guzzlehttp/psr7": "~1.0", - "php": ">=5.4.0 <8.0", - "phpunit/php-code-coverage": ">=2.2.4 <6.0", - "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", - "sebastian/comparator": ">1.1 <3.0", - "sebastian/diff": ">=1.4 <3.0", + "php": ">=5.6.0 <8.0", "symfony/browser-kit": ">=2.7 <5.0", "symfony/console": ">=2.7 <5.0", "symfony/css-selector": ">=2.7 <5.0", @@ -157,20 +154,63 @@ "functional testing", "unit testing" ], - "time": "2018-02-26T23:29:41+00:00" + "time": "2018-06-26T14:09:28+00:00" + }, + { + "name": "codeception/phpunit-wrapper", + "version": "7.1.4", + "source": { + "type": "git", + "url": "https://github.com/Codeception/phpunit-wrapper.git", + "reference": "f18ed631f1eddbb603d72219f577d223b23a1f89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/f18ed631f1eddbb603d72219f577d223b23a1f89", + "reference": "f18ed631f1eddbb603d72219f577d223b23a1f89", + "shasum": "" + }, + "require": { + "phpunit/php-code-coverage": "^6.0", + "phpunit/phpunit": "^7.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0" + }, + "require-dev": { + "codeception/specify": "*", + "vlucas/phpdotenv": "^2.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Codeception\\PHPUnit\\": "src\\" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Davert", + "email": "davert.php@resend.cc" + } + ], + "description": "PHPUnit classes used by Codeception", + "time": "2018-06-20T20:07:21+00:00" }, { "name": "codeception/stub", - "version": "1.0.4", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/Codeception/Stub.git", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805" + "reference": "b2eff325d8ff0b824ff659048be7be4e5767d7d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/681b62348837a5ef07d10d8a226f5bc358cc8805", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/b2eff325d8ff0b824ff659048be7be4e5767d7d0", + "reference": "b2eff325d8ff0b824ff659048be7be4e5767d7d0", "shasum": "" }, "require": { @@ -190,20 +230,20 @@ "MIT" ], "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-05-17T09:31:08+00:00" + "time": "2018-05-18T14:33:08+00:00" }, { "name": "consolidation/annotated-command", - "version": "2.8.3", + "version": "2.8.4", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "8f8f5da2ca06fbd3a85f7d551c49f844b7c59437" + "reference": "651541a0b68318a2a202bda558a676e5ad92223c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/8f8f5da2ca06fbd3a85f7d551c49f844b7c59437", - "reference": "8f8f5da2ca06fbd3a85f7d551c49f844b7c59437", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/651541a0b68318a2a202bda558a676e5ad92223c", + "reference": "651541a0b68318a2a202bda558a676e5ad92223c", "shasum": "" }, "require": { @@ -215,9 +255,9 @@ "symfony/finder": "^2.5|^3|^4" }, "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4.8", - "satooshi/php-coveralls": "^1.0.2 | dev-master", + "g1a/composer-test-scenarios": "^2", + "phpunit/phpunit": "^6", + "satooshi/php-coveralls": "^2", "squizlabs/php_codesniffer": "^2.7" }, "type": "library", @@ -242,20 +282,20 @@ } ], "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2018-02-23T16:32:04+00:00" + "time": "2018-05-25T18:04:25+00:00" }, { "name": "consolidation/config", - "version": "1.0.9", + "version": "1.0.11", "source": { "type": "git", "url": "https://github.com/consolidation/config.git", - "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c" + "reference": "ede41d946078e97e7a9513aadc3352f1c26817af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/34ca8d7c1ee60a7b591b10617114cf1210a2e92c", - "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c", + "url": "https://api.github.com/repos/consolidation/config/zipball/ede41d946078e97e7a9513aadc3352f1c26817af", + "reference": "ede41d946078e97e7a9513aadc3352f1c26817af", "shasum": "" }, "require": { @@ -264,7 +304,7 @@ "php": ">=5.4.0" }, "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", + "g1a/composer-test-scenarios": "^1", "phpunit/phpunit": "^4", "satooshi/php-coveralls": "^1.0", "squizlabs/php_codesniffer": "2.*", @@ -296,20 +336,20 @@ } ], "description": "Provide configuration services for a commandline tool.", - "time": "2017-12-22T17:28:19+00:00" + "time": "2018-05-27T01:17:02+00:00" }, { "name": "consolidation/log", - "version": "1.0.5", + "version": "1.0.6", "source": { "type": "git", "url": "https://github.com/consolidation/log.git", - "reference": "dbc7c535f319a4a2d5a5077738f8eb7c10df8821" + "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/dbc7c535f319a4a2d5a5077738f8eb7c10df8821", - "reference": "dbc7c535f319a4a2d5a5077738f8eb7c10df8821", + "url": "https://api.github.com/repos/consolidation/log/zipball/dfd8189a771fe047bf3cd669111b2de5f1c79395", + "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395", "shasum": "" }, "require": { @@ -318,8 +358,9 @@ "symfony/console": "^2.8|^3|^4" }, "require-dev": { + "g1a/composer-test-scenarios": "^1", "phpunit/phpunit": "4.*", - "satooshi/php-coveralls": "dev-master", + "satooshi/php-coveralls": "^2", "squizlabs/php_codesniffer": "2.*" }, "type": "library", @@ -344,20 +385,20 @@ } ], "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", - "time": "2017-11-29T01:44:16+00:00" + "time": "2018-05-25T18:14:39+00:00" }, { "name": "consolidation/output-formatters", - "version": "3.2.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/consolidation/output-formatters.git", - "reference": "da889e4bce19f145ca4ec5b1725a946f4eb625a9" + "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/da889e4bce19f145ca4ec5b1725a946f4eb625a9", - "reference": "da889e4bce19f145ca4ec5b1725a946f4eb625a9", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", + "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", "shasum": "" }, "require": { @@ -366,7 +407,7 @@ "symfony/finder": "^2.5|^3|^4" }, "require-dev": { - "g-1-a/composer-test-scenarios": "^2", + "g1a/composer-test-scenarios": "^2", "phpunit/phpunit": "^5.7.27", "satooshi/php-coveralls": "^2", "squizlabs/php_codesniffer": "^2.7", @@ -399,25 +440,25 @@ } ], "description": "Format text by applying transformations provided by plug-in formatters.", - "time": "2018-03-20T15:18:32+00:00" + "time": "2018-05-25T18:02:34+00:00" }, { "name": "consolidation/robo", - "version": "1.2.3", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "54a13e268917b92576d75e10dca8227b95a574d9" + "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/54a13e268917b92576d75e10dca8227b95a574d9", - "reference": "54a13e268917b92576d75e10dca8227b95a574d9", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/ac563abfadf7cb7314b4e152f2b5033a6c255f6f", + "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f", "shasum": "" }, "require": { "consolidation/annotated-command": "^2.8.2", - "consolidation/config": "^1.0.1", + "consolidation/config": "^1.0.10", "consolidation/log": "~1", "consolidation/output-formatters": "^3.1.13", "grasmash/yaml-expander": "^1.3", @@ -436,7 +477,7 @@ "codeception/aspect-mock": "^1|^2.1.1", "codeception/base": "^2.3.7", "codeception/verify": "^0.3.2", - "g-1-a/composer-test-scenarios": "^2", + "g1a/composer-test-scenarios": "^2", "goaop/framework": "~2.1.2", "goaop/parser-reflection": "^1.1.0", "natxet/cssmin": "3.0.4", @@ -479,7 +520,7 @@ } ], "description": "Modern task runner", - "time": "2018-04-06T05:27:37+00:00" + "time": "2018-05-27T01:42:53+00:00" }, { "name": "container-interop/container-interop", @@ -1028,16 +1069,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.8.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "478465659fd987669df0bd8a9bf22a8710e5f1b6" + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/478465659fd987669df0bd8a9bf22a8710e5f1b6", - "reference": "478465659fd987669df0bd8a9bf22a8710e5f1b6", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", "shasum": "" }, "require": { @@ -1072,7 +1113,7 @@ "object", "object graph" ], - "time": "2018-05-29T17:25:09+00:00" + "time": "2018-06-11T23:09:50+00:00" }, { "name": "phar-io/manifest", @@ -1393,40 +1434,40 @@ }, { "name": "phpunit/php-code-coverage", - "version": "5.3.2", + "version": "6.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac" + "reference": "4cab20a326d14de7575a8e235c70d879b569a57a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4cab20a326d14de7575a8e235c70d879b569a57a", + "reference": "4cab20a326d14de7575a8e235c70d879b569a57a", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^7.0", + "php": "^7.1", "phpunit/php-file-iterator": "^1.4.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", + "phpunit/php-token-stream": "^3.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", + "sebastian/environment": "^3.1", "sebastian/version": "^2.0.1", "theseer/tokenizer": "^1.1" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^7.0" }, "suggest": { - "ext-xdebug": "^2.5.5" + "ext-xdebug": "^2.6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.3.x-dev" + "dev-master": "6.0-dev" } }, "autoload": { @@ -1452,7 +1493,7 @@ "testing", "xunit" ], - "time": "2018-04-06T15:36:58+00:00" + "time": "2018-05-28T11:49:20+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1544,28 +1585,28 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.9", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b8454ea6958c3dee38453d3bd571e023108c91f", + "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1580,7 +1621,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -1589,33 +1630,33 @@ "keywords": [ "timer" ], - "time": "2017-02-26T11:10:40+00:00" + "time": "2018-02-01T13:07:23+00:00" }, { "name": "phpunit/php-token-stream", - "version": "2.0.2", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" + "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/21ad88bbba7c3d93530d93994e0a33cd45f02ace", + "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.2.4" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1638,20 +1679,20 @@ "keywords": [ "tokenizer" ], - "time": "2017-11-27T05:48:46+00:00" + "time": "2018-02-01T13:16:43+00:00" }, { "name": "phpunit/phpunit", - "version": "6.5.8", + "version": "7.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b" + "reference": "ca64dba53b88aba6af32aebc6b388068db95c435" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4f21a3c6b97c42952fd5c2837bb354ec0199b97b", - "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ca64dba53b88aba6af32aebc6b388068db95c435", + "reference": "ca64dba53b88aba6af32aebc6b388068db95c435", "shasum": "" }, "require": { @@ -1663,15 +1704,15 @@ "myclabs/deep-copy": "^1.6.1", "phar-io/manifest": "^1.0.1", "phar-io/version": "^1.0", - "php": "^7.0", + "php": "^7.1", "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", + "phpunit/php-code-coverage": "^6.0.1", "phpunit/php-file-iterator": "^1.4.3", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.5", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", + "phpunit/php-timer": "^2.0", + "phpunit/phpunit-mock-objects": "^6.1.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", "sebastian/environment": "^3.1", "sebastian/exporter": "^3.1", "sebastian/global-state": "^2.0", @@ -1679,16 +1720,12 @@ "sebastian/resource-operations": "^1.0", "sebastian/version": "^2.0.1" }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" - }, "require-dev": { "ext-pdo": "*" }, "suggest": { "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" + "phpunit/php-invoker": "^2.0" }, "bin": [ "phpunit" @@ -1696,7 +1733,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.5.x-dev" + "dev-master": "7.1-dev" } }, "autoload": { @@ -1722,33 +1759,30 @@ "testing", "xunit" ], - "time": "2018-04-10T11:38:34+00:00" + "time": "2018-04-29T15:09:19+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "5.0.7", + "version": "6.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "3eaf040f20154d27d6da59ca2c6e28ac8fd56dce" + "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3eaf040f20154d27d6da59ca2c6e28ac8fd56dce", - "reference": "3eaf040f20154d27d6da59ca2c6e28ac8fd56dce", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/f9756fd4f43f014cb2dca98deeaaa8ce5500a36e", + "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.5", - "php": "^7.0", + "php": "^7.1", "phpunit/php-text-template": "^1.2.1", "sebastian/exporter": "^3.1" }, - "conflict": { - "phpunit/phpunit": "<6.0" - }, "require-dev": { - "phpunit/phpunit": "^6.5" + "phpunit/phpunit": "^7.0" }, "suggest": { "ext-soap": "*" @@ -1756,7 +1790,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0.x-dev" + "dev-master": "6.1-dev" } }, "autoload": { @@ -1781,7 +1815,7 @@ "mock", "xunit" ], - "time": "2018-05-29T13:50:43+00:00" + "time": "2018-05-29T13:54:20+00:00" }, { "name": "psr/container", @@ -1976,30 +2010,30 @@ }, { "name": "sebastian/comparator", - "version": "2.1.3", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", + "php": "^7.1", + "sebastian/diff": "^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^6.4" + "phpunit/phpunit": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2036,32 +2070,33 @@ "compare", "equality" ], - "time": "2018-02-01T13:46:46+00:00" + "time": "2018-07-12T15:12:46+00:00" }, { "name": "sebastian/diff", - "version": "2.0.1", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" + "reference": "366541b989927187c4ca70490a35615d3fef2dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", + "reference": "366541b989927187c4ca70490a35615d3fef2dce", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "phpunit/phpunit": "^7.0", + "symfony/process": "^2 || ^3.3 || ^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2086,9 +2121,12 @@ "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "diff" + "diff", + "udiff", + "unidiff", + "unified diff" ], - "time": "2017-08-03T08:09:46+00:00" + "time": "2018-06-10T07:54:39+00:00" }, { "name": "sebastian/environment", @@ -2490,16 +2528,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "16355a5d0f1499c77efee5ff68d8ea61624d4da1" + "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/16355a5d0f1499c77efee5ff68d8ea61624d4da1", - "reference": "16355a5d0f1499c77efee5ff68d8ea61624d4da1", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", + "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", "shasum": "" }, "require": { @@ -2543,20 +2581,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2018-04-06T10:52:03+00:00" + "time": "2018-06-04T17:31:56+00:00" }, { "name": "symfony/console", - "version": "v4.0.9", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3e820bc2c520a87ca209ad8fa961c97f42e0b4ae" + "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3e820bc2c520a87ca209ad8fa961c97f42e0b4ae", - "reference": "3e820bc2c520a87ca209ad8fa961c97f42e0b4ae", + "url": "https://api.github.com/repos/symfony/console/zipball/70591cda56b4b47c55776ac78e157c4bb6c8b43f", + "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f", "shasum": "" }, "require": { @@ -2584,7 +2622,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -2611,11 +2649,11 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-04-30T01:23:47+00:00" + "time": "2018-05-31T10:17:53+00:00" }, { "name": "symfony/css-selector", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -2668,7 +2706,7 @@ }, { "name": "symfony/dom-crawler", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", @@ -2725,16 +2763,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v4.0.9", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "63353a71073faf08f62caab4e6889b06a787f07b" + "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/63353a71073faf08f62caab4e6889b06a787f07b", - "reference": "63353a71073faf08f62caab4e6889b06a787f07b", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2391ed210a239868e7256eb6921b1bd83f3087b5", + "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5", "shasum": "" }, "require": { @@ -2757,7 +2795,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -2784,29 +2822,30 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-04-06T07:35:43+00:00" + "time": "2018-04-06T07:35:57+00:00" }, { "name": "symfony/filesystem", - "version": "v4.0.9", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21" + "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21", - "reference": "5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", + "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -2833,20 +2872,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-02-22T10:50:29+00:00" + "time": "2018-05-30T07:26:09+00:00" }, { "name": "symfony/finder", - "version": "v4.0.9", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ca27c02b7a3fef4828c998c2ff9ba7aae1641c49" + "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ca27c02b7a3fef4828c998c2ff9ba7aae1641c49", - "reference": "ca27c02b7a3fef4828c998c2ff9ba7aae1641c49", + "url": "https://api.github.com/repos/symfony/finder/zipball/84714b8417d19e4ba02ea78a41a975b3efaafddb", + "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb", "shasum": "" }, "require": { @@ -2855,7 +2894,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -2882,7 +2921,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-04-04T05:10:37+00:00" + "time": "2018-06-19T21:38:16+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3000,16 +3039,16 @@ }, { "name": "symfony/process", - "version": "v4.0.9", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d7dc1ee5dfe9f732cb1bba7310f5b99f2b7a6d25" + "reference": "1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d7dc1ee5dfe9f732cb1bba7310f5b99f2b7a6d25", - "reference": "d7dc1ee5dfe9f732cb1bba7310f5b99f2b7a6d25", + "url": "https://api.github.com/repos/symfony/process/zipball/1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a", + "reference": "1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a", "shasum": "" }, "require": { @@ -3018,7 +3057,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3045,24 +3084,25 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-04-03T05:24:00+00:00" + "time": "2018-05-31T10:17:53+00:00" }, { "name": "symfony/yaml", - "version": "v4.0.9", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "275ad099e4cbe612a2acbca14a16dd1c5311324d" + "reference": "80e4bfa9685fc4a09acc4a857ec16974a9cd944e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/275ad099e4cbe612a2acbca14a16dd1c5311324d", - "reference": "275ad099e4cbe612a2acbca14a16dd1c5311324d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/80e4bfa9685fc4a09acc4a857ec16974a9cd944e", + "reference": "80e4bfa9685fc4a09acc4a857ec16974a9cd944e", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/console": "<3.4" @@ -3076,7 +3116,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3103,7 +3143,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-04-08T08:49:08+00:00" + "time": "2018-05-30T07:26:09+00:00" }, { "name": "theseer/tokenizer", @@ -3147,28 +3187,28 @@ }, { "name": "vlucas/phpdotenv", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c" + "reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", - "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/6ae3e2e6494bb5e58c2decadafc3de7f1453f70a", + "reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a", "shasum": "" }, "require": { "php": ">=5.3.9" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.0" + "phpunit/phpunit": "^4.8.35 || ^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.4-dev" + "dev-master": "2.5-dev" } }, "autoload": { @@ -3178,7 +3218,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause-Attribution" + "BSD-3-Clause" ], "authors": [ { @@ -3193,7 +3233,7 @@ "env", "environment" ], - "time": "2016-09-01T10:05:43+00:00" + "time": "2018-07-01T10:25:50+00:00" }, { "name": "webmozart/assert", From 228f7d8152b3e7bf87305a5ef687fc46e1f6dd3a Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Fri, 13 Jul 2018 12:15:59 -0500 Subject: [PATCH 0272/1171] ENGCOM-2197: [Jenkins] Failure to generate MFTF Tests --- .../DisplayOutOfStockProductActionGroup.xml | 29 +++++++++++++++++++ .../Mftf/Page/InventoryConfigurationPage.xml | 12 ++++++++ .../Test/Mftf/Section/InventorySection.xml | 15 ++++++++++ 3 files changed, 56 insertions(+) create mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml create mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/Page/InventoryConfigurationPage.xml create mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/Section/InventorySection.xml diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml new file mode 100644 index 0000000000000..1bec4cc99c0e8 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="displayOutOfStockProduct"> + <amOnPage url="{{InventoryConfigurationPage.url}}" stepKey="navigateToInventoryConfigurationPage"/> + <waitForPageLoad stepKey="waitForConfigPageToLoad"/> + <conditionalClick stepKey="expandProductStockOptions" selector="{{InventoryConfigSection.ProductStockOptionsTab}}" dependentSelector="{{InventoryConfigSection.CheckIfProductStockOptionsTabExpanded}}" visible="true" /> + <waitForElementVisible selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="waitForDisplayOutOfStockOption" /> + <uncheckOption selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="uncheckUseSystemValue"/> + <waitForElementVisible selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" stepKey="waitForSwitcherDropdown" /> + <selectOption selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" userInput="Yes" stepKey="switchToYes" /> + <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> + </actionGroup> + <actionGroup name="noDisplayOutOfStockProduct"> + <amOnPage url="{{InventoryConfigurationPage.url}}" stepKey="navigateToInventoryConfigurationPage"/> + <waitForPageLoad stepKey="waitForConfigPageToLoad"/> + <uncheckOption selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="uncheckUseSystemValue"/> + <waitForElementVisible selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" stepKey="waitForSwitcherDropdown" /> + <selectOption selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" userInput="No" stepKey="switchToNo" /> + <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Page/InventoryConfigurationPage.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Page/InventoryConfigurationPage.xml new file mode 100644 index 0000000000000..95e873a3b164d --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Page/InventoryConfigurationPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="InventoryConfigurationPage" url="admin/system_config/edit/section/cataloginventory/" area="admin" module="Magento_Config"> + <section name="InventorySection"/> + </page> +</pages> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Section/InventorySection.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Section/InventorySection.xml new file mode 100644 index 0000000000000..55fbc84ead96a --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Section/InventorySection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="InventoryConfigSection"> + <element name="ProductStockOptionsTab" type="button" selector="#cataloginventory_options-head"/> + <element name="CheckIfProductStockOptionsTabExpanded" type="button" selector="#cataloginventory_options-head:not(.open)"/> + <element name="DisplayOutOfStockSystemValue" type="checkbox" selector="#cataloginventory_options_show_out_of_stock_inherit"/> + <element name="DisplayOutOfStockDropdown" type="select" selector="#cataloginventory_options_show_out_of_stock"/> + </section> +</sections> From b8440f05bea6e6419a447fc3ccd8f4378ad6d63a Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Fri, 13 Jul 2018 22:50:30 +0530 Subject: [PATCH 0273/1171] Resolved conflict changes --- app/code/Magento/Catalog/etc/frontend/di.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index 880b367cfa6e0..29be5b573654d 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -79,9 +79,9 @@ <argument name="typeId" xsi:type="string">recently_compared_product</argument> </arguments> </virtualType> - <type name="Magento\Framework\View\Element\Message\MessageConfigurationsPool"> + <type name="Magento\Catalog\Block\Product\View\Gallery"> <arguments> - <argument name="configurationsMap" xsi:type="array"> + <argument name="galleryImagesConfig" xsi:type="array"> <item name="addCompareAddSuccessMessage" xsi:type="array"> <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> <item name="data" xsi:type="array"> From 0430946c8073a05b7c9562cb07294f709ac326a4 Mon Sep 17 00:00:00 2001 From: Justin Rhyne <jrhyne@thinkpyxl.com> Date: Tue, 10 Jul 2018 17:37:09 -0400 Subject: [PATCH 0274/1171] Add sort order to user agent rules table headers --- .../Backend/view/adminhtml/ui_component/design_config_form.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/view/adminhtml/ui_component/design_config_form.xml b/app/code/Magento/Backend/view/adminhtml/ui_component/design_config_form.xml index 46902363f1fd7..79c987383299f 100644 --- a/app/code/Magento/Backend/view/adminhtml/ui_component/design_config_form.xml +++ b/app/code/Magento/Backend/view/adminhtml/ui_component/design_config_form.xml @@ -92,7 +92,7 @@ </select> </formElements> </field> - <actionDelete template="Magento_Backend/dynamic-rows/cells/action-delete" sortOrder="50"> + <actionDelete template="Magento_Backend/dynamic-rows/cells/action-delete"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="fit" xsi:type="boolean">false</item> From 5e4a0f3d9005c6146a2b741cc9951b6f65d29103 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Fri, 13 Jul 2018 14:18:20 -0500 Subject: [PATCH 0275/1171] MC-3080: Admin can configure swatches display in configuration --- .../AdminCreateProductAttributeSection.xml | 2 +- .../AdminProductAttributeGridSection.xml | 3 +- .../ActionGroup/ColorPickerActionGroup.xml | 31 ++++++++ .../Mftf/Section/AdminColorPickerSection.xml | 15 ++++ .../Mftf/Section/AdminManageSwatchSection.xml | 15 ++++ .../Mftf/Test/AdminConfigureSwatchesTest.xml | 75 +++++++++++++++++++ 6 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index e1eef15e9d476..be0e50ba5a3a5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -15,7 +15,7 @@ <element name="AdvancedProperties" type="button" selector="#advanced_fieldset-wrapper"/> <element name="DefaultValue" type="input" selector="#default_value_text"/> <element name="Save" type="button" selector="#save"/> - <element name="SaveAndEdit" type="button" selector="#save_and_edit_button"/> + <element name="SaveAndEdit" type="button" selector="#save_and_edit_button" timeout="30"/> <element name="TinyMCE4" type="button" selector="//span[text()='Default Value']/parent::label/following-sibling::div//div[@class='mce-branding-powered-by']"/> <element name="checkIfTabOpen" selector="//div[@id='advanced_fieldset-wrapper' and not(contains(@class,'opened'))]" type="button"/> </section> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml index 820e03a0f5e98..caf34a9f355a0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml @@ -9,11 +9,12 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminProductAttributeGridSection"> - <element name="AttributeCode" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true"/> + <element name="AttributeCode" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true" timeout="30"/> <element name="createNewAttributeBtn" type="button" selector="button[data-index='add_new_attribute_button']"/> <element name="GridFilterFrontEndLabel" type="input" selector="#attributeGrid_filter_frontend_label"/> <element name="Search" type="button" selector="button[data-action=grid-filter-apply]" timeout="30"/> <element name="ResetFilter" type="button" selector="button[data-action='grid-filter-reset']" timeout="30"/> <element name="FirstRow" type="button" selector="//*[@id='attributeGrid_table']/tbody/tr[1]"/> + <element name="FilterByAttributeCode" type="input" selector="#attributeGrid_filter_attribute_code"/> </section> </sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml new file mode 100644 index 0000000000000..cb52038d652ee --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="setColorPickerByHex"> + <arguments> + <argument name="nthColorPicker" type="string" defaultValue="1"/> + <argument name="hexColor" type="string" defaultValue="e74c3c"/> + </arguments> + <!-- This 6x backspace stuff is some magic that is necessary to interact with this field correctly --> + <pressKey selector="{{AdminColorPickerSection.hex(nthColorPicker)}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,'{{hexColor}}']" stepKey="fillHex1"/> + <click selector="{{AdminColorPickerSection.submit(nthColorPicker)}}" stepKey="submitColor1"/> + </actionGroup> + <actionGroup name="assertSwatchColor"> + <arguments> + <argument name="nthSwatch" type="string" defaultValue="1"/> + <argument name="expectedStyle" type="string" defaultValue="background: rgb(0, 0, 0);"/> + </arguments> + <grabAttributeFrom selector="{{AdminManageSwatchSection.nthSwatch(nthSwatch)}}" userInput="style" stepKey="grabStyle1"/> + <assertEquals stepKey="assertStyle1"> + <actualResult type="string">{$grabStyle1}</actualResult> + <expectedResult type="string">{{expectedStyle}}</expectedResult> + </assertEquals> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml new file mode 100644 index 0000000000000..07552397e091f --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminColorPickerSection"> + <element name="hex" type="input" selector="//body/div[@class='colorpicker'][{{var}}]/div[@class='colorpicker_hex']/input" parameterized="true"/> + <element name="submit" type="button" selector="//body/div[@class='colorpicker'][{{var}}]/div[@class='colorpicker_submit']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml new file mode 100644 index 0000000000000..58bdbfee3d7ff --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminManageSwatchSection"> + <element name="nthSwatch" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_window" parameterized="true"/> + <element name="nthChooseColor" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_row_name.colorpicker_handler" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml new file mode 100644 index 0000000000000..03a24f2f2400f --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminConfigureSwatchesTest"> + <annotations> + <features value="Swatches"/> + <stories value="Create/configure swatches"/> + <title value="Admin can configure swatches display in configuration"/> + <description value="Admin can configure swatches display in configuration"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3080"/> + <group value="Swatches"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Go to the edit page for the "color" attribute --> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + <waitForPageLoad stepKey="waitForProductAttributes"/> + <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="color" stepKey="fillFilter"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="clickSearch"/> + <click selector="{{AdminProductAttributeGridSection.AttributeCode('color')}}" stepKey="clickRowToEdit"/> + + <!-- Change to visual swatches --> + <selectOption selector="{{AdminNewAttributePanel.inputType}}" userInput="swatch_visual" stepKey="selectVisualSwatch"/> + + <!-- Set three swatches using the color picker --> + <click selector="{{AdminManageSwatchSection.nthSwatch('1')}}" stepKey="clickSwatch1"/> + <click selector="{{AdminManageSwatchSection.nthChooseColor('1')}}" stepKey="clickChooseColor1"/> + <actionGroup ref="setColorPickerByHex" stepKey="fillHex1"> + <argument name="nthColorPicker" value="1"/> + <argument name="hexColor" value="e74c3c"/> + </actionGroup> + <click selector="{{AdminManageSwatchSection.nthSwatch('2')}}" stepKey="clickSwatch2"/> + <click selector="{{AdminManageSwatchSection.nthChooseColor('2')}}" stepKey="clickChooseColor2"/> + <actionGroup ref="setColorPickerByHex" stepKey="fillHex2"> + <argument name="nthColorPicker" value="2"/> + <argument name="hexColor" value="2ecc71"/> + </actionGroup> + <click selector="{{AdminManageSwatchSection.nthSwatch('3')}}" stepKey="clickSwatch3"/> + <click selector="{{AdminManageSwatchSection.nthChooseColor('3')}}" stepKey="clickChooseColor3"/> + <actionGroup ref="setColorPickerByHex" stepKey="fillHex3"> + <argument name="nthColorPicker" value="3"/> + <argument name="hexColor" value="3498db"/> + </actionGroup> + + <!-- Save --> + <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit"/> + + <!-- Assert that the Save was successful after round trip to server --> + <actionGroup ref="assertSwatchColor" stepKey="assertSwatch1"> + <argument name="nthSwatch" value="1"/> + <argument name="expectedStyle" value="background: rgb(231, 77, 60);"/> + </actionGroup> + <actionGroup ref="assertSwatchColor" stepKey="assertSwatch2"> + <argument name="nthSwatch" value="2"/> + <argument name="expectedStyle" value="background: rgb(46, 204, 112);"/> + </actionGroup> + <actionGroup ref="assertSwatchColor" stepKey="assertSwatch3"> + <argument name="nthSwatch" value="3"/> + <argument name="expectedStyle" value="background: rgb(52, 152, 219);"/> + </actionGroup> + </test> +</tests> From a0329e44c7a5aeb4729ba0636cb0db9c08b5c5d9 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Fri, 13 Jul 2018 21:49:33 +0300 Subject: [PATCH 0276/1171] magento/magento2#?: Fix Translation of error message on cart for deleted bundle option. - change plugin sort order in case to prevent loading of not translatable messages for Localized exception in beforeDispatch plugin. --- app/code/Magento/Store/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml index 27133de270e2f..c8e21fabc1f7f 100644 --- a/app/code/Magento/Store/etc/di.xml +++ b/app/code/Magento/Store/etc/di.xml @@ -57,7 +57,7 @@ <preference for="Magento\Framework\App\Router\PathConfigInterface" type="Magento\Store\Model\PathConfig" /> <type name="Magento\Framework\App\Action\AbstractAction"> <plugin name="storeCheck" type="Magento\Store\App\Action\Plugin\StoreCheck" sortOrder="10"/> - <plugin name="designLoader" type="Magento\Framework\App\Action\Plugin\Design" sortOrder="30"/> + <plugin name="designLoader" type="Magento\Framework\App\Action\Plugin\Design" /> </type> <type name="Magento\Framework\Url\SecurityInfo"> <plugin name="storeUrlSecurityInfo" type="Magento\Store\Url\Plugin\SecurityInfo"/> From 0d26429d0441a4742bf9f74e49de6fa6296a3391 Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Fri, 13 Jul 2018 16:15:16 -0500 Subject: [PATCH 0277/1171] MAGETWO-90970: State field becomes required after page refresh during checkout --- app/code/Magento/Ui/view/base/web/js/form/element/region.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/region.js b/app/code/Magento/Ui/view/base/web/js/form/element/region.js index 0edb4c1966b54..efa94bac0c609 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/region.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/region.js @@ -49,6 +49,10 @@ define([ if (option && !option['is_region_required']) { this.error(false); this.validation = _.omit(this.validation, 'required-entry'); + registry.get(this.customName, function (input) { + input.validation['required-entry'] = false; + input.required(false); + }); } else { this.validation['required-entry'] = true; } From 3a9d72362517eaca2b1223fbfe589ef3ed126efe Mon Sep 17 00:00:00 2001 From: Levi Villarreal <villarreallevi@gmail.com> Date: Tue, 10 Jul 2018 11:16:36 -0500 Subject: [PATCH 0278/1171] MC-262: Admin should be able to create Single Future Update with end date from Simple Product Page - Add action group for use in ee staging test --- .../Mftf/ActionGroup/AdminProductGridActionGroup.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index 9d6af144b8f22..c2620bc5a3672 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -53,6 +53,18 @@ <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> </actionGroup> + <!--Filter the product grid by the Name field--> + <actionGroup name="filterProductGridByName2"> + <arguments> + <argument name="name" type="string"/> + </arguments> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{name}}" stepKey="fillProductNameFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> + </actionGroup> + <!--Filter the product grid by new from date filter--> <actionGroup name="filterProductGridBySetNewFromDate"> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> From 5b1e5b8007e962e93545995b7653a873b9834153 Mon Sep 17 00:00:00 2001 From: Levi Villarreal <villarreallevi@gmail.com> Date: Wed, 11 Jul 2018 17:02:16 -0500 Subject: [PATCH 0279/1171] MC-60: Admin should be able to create a catalog price rule adjust final price to discount value (for simple product) --- .../ActionGroup/CatalogPriceRuleActionGroup.xml | 1 + .../AdminCatalogPriceRuleStagingSection.xml | 14 ++++++++++++++ .../Section/AdminNewCatalogPriceRuleSection.xml | 1 + 3 files changed, 16 insertions(+) create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index ad92af9bb3b1b..0173cab85d810 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -35,6 +35,7 @@ <scrollToTopOfPage stepKey="scrollToTop"/> </actionGroup> + <!-- Apply all of the saved catalog price rules --> <actionGroup name="applyCatalogPriceRules"> <amOnPage stepKey="goToPriceRulePage" url="{{CatalogRulePage.url}}"/> <waitForPageLoad stepKey="waitForPriceRulePage"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml new file mode 100644 index 0000000000000..d212aac1c0d4a --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminCatalogPriceRuleStagingSection"> + <element name="status" type="select" selector=".modal-component [data-index='is_active'] select"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml index b60a4c6516a82..0a64bb603f47e 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml @@ -11,6 +11,7 @@ <section name="AdminNewCatalogPriceRule"> <element name="saveAndApply" type="button" selector="#save_and_apply" timeout="30"/> <element name="save" type="button" selector="#save" timeout="30"/> + <element name="saveAndContinue" type="button" selector="#save_and_continue" timeout="30"/> <element name="delete" type="button" selector="#delete" timeout="30"/> <element name="ruleName" type="input" selector="[name='name']"/> From 859fca9ebeb20ed70f2268d8d6242d746f99576b Mon Sep 17 00:00:00 2001 From: Levi Villarreal <villarreallevi@gmail.com> Date: Thu, 12 Jul 2018 11:56:28 -0500 Subject: [PATCH 0280/1171] MC-60: Admin should be able to create a catalog price rule adjust final price to discount value (for simple product) - Fix B2B issue --- .../Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml | 1 - .../Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index 0173cab85d810..9dae528ba8698 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -22,7 +22,6 @@ <fillField stepKey="fillName" selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{catalogRule.name}}"/> <fillField stepKey="fillDescription" selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{catalogRule.description}}"/> <selectOption stepKey="selectSite" selector="{{AdminNewCatalogPriceRule.websites}}" userInput="{{catalogRule.website_ids[0]}}"/> - <selectOption stepKey="selectCustomerGroups" selector="{{AdminNewCatalogPriceRule.customerGroups}}" userInput="{{catalogRule.customer_group_ids[0]}}"/> <click stepKey="clickFromCalender" selector="{{AdminNewCatalogPriceRule.fromDateButton}}"/> <click stepKey="clickFromToday" selector="{{AdminNewCatalogPriceRule.todayDate}}"/> <click stepKey="clickToCalender" selector="{{AdminNewCatalogPriceRule.toDateButton}}"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml index a68965d97e879..2c367ae4f1716 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml @@ -28,6 +28,7 @@ <!-- log in and create the price rule --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"/> + <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> </before> From faf165ac5c544d7a0689078bfc86bb4b74bfac2a Mon Sep 17 00:00:00 2001 From: Levi Villarreal <villarreallevi@gmail.com> Date: Thu, 12 Jul 2018 16:54:39 -0500 Subject: [PATCH 0281/1171] MC-60: Admin should be able to create a catalog price rule adjust final price to discount value (for simple product) - Update for changed actiongroup --- .../Mftf/Test/AdminCreateCatalogPriceRuleTest.xml | 15 ++++++--------- .../Test/StorefrontInactiveCatalogRuleTest.xml | 3 ++- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml index 2c367ae4f1716..0cfa518444b44 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml @@ -84,9 +84,8 @@ <!-- log in and create the price rule --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> - <argument name="catalogRule" value="CatalogRuleByFixed"/> - </actionGroup> + <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"/> + <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> </before> @@ -142,9 +141,8 @@ <!-- log in and create the price rule --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> - <argument name="catalogRule" value="CatalogRuleToPercent"/> - </actionGroup> + <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"/> + <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> </before> @@ -200,9 +198,8 @@ <!-- log in and create the price rule --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> - <argument name="catalogRule" value="CatalogRuleToFixed"/> - </actionGroup> + <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"/> + <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> </before> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml index 77a9a48074c29..64f19cb3ca881 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml @@ -24,7 +24,8 @@ <createData entity="ApiSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="newCatalogPriceRuleByUI" stepKey="createNewPriceRule"/> + <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"/> + <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> <selectOption selector="{{AdminNewCatalogPriceRule.status}}" userInput="Inactive" stepKey="setInactive"/> <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="seeSuccess"/> From 712f40b3acb677cf46df305824da3a78305b6719 Mon Sep 17 00:00:00 2001 From: Levi Villarreal <villarreallevi@gmail.com> Date: Fri, 13 Jul 2018 11:40:40 -0500 Subject: [PATCH 0282/1171] MC-60: Admin should be able to create a catalog price rule adjust final price to discount value (for simple product) - Move CLI commands --- .../Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml | 2 ++ .../Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index 9dae528ba8698..558a1e3760f07 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -17,6 +17,7 @@ <amOnPage stepKey="goToPriceRulePage" url="{{CatalogRulePage.url}}"/> <waitForPageLoad stepKey="waitForPriceRulePage"/> <click stepKey="addNewRule" selector="{{AdminGridMainControls.add}}"/> + <waitForPageLoad stepKey="waitForIndividualRulePage"/> <!-- Fill the form according the the attributes of the entity --> <fillField stepKey="fillName" selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{catalogRule.name}}"/> @@ -32,6 +33,7 @@ <!-- Scroll to top and either save or save and apply after the action group --> <scrollToTopOfPage stepKey="scrollToTop"/> + <waitForPageLoad stepKey="waitForApplied"/> </actionGroup> <!-- Apply all of the saved catalog price rules --> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml index 0cfa518444b44..a447da428c219 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml @@ -31,6 +31,8 @@ <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> + <magentoCLI stepKey="reindex" command="indexer:reindex"/> + <magentoCLI stepKey="flushCache" command="cache:flush"/> </before> <after> <!-- delete the simple product and catalog price rule and logout --> From adcac17b9a236247ca00862cba46e1c1b8e13db2 Mon Sep 17 00:00:00 2001 From: Levi Villarreal <villarreallevi@gmail.com> Date: Fri, 13 Jul 2018 13:35:59 -0500 Subject: [PATCH 0283/1171] MC-60: Admin should be able to create a catalog price rule adjust final price to discount value (for simple product) - Ensure catalog price rules work in ce at least --- .../ActionGroup/CatalogPriceRuleActionGroup.xml | 1 + .../Section/AdminNewCatalogPriceRuleSection.xml | 2 +- .../Mftf/Test/AdminCreateCatalogPriceRuleTest.xml | 14 +++++++++----- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index 558a1e3760f07..ad2ccd6e63d87 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -30,6 +30,7 @@ <click stepKey="openActionDropdown" selector="{{AdminNewCatalogPriceRule.actionsTab}}"/> <selectOption stepKey="discountType" selector="{{AdminNewCatalogPriceRuleActions.apply}}" userInput="{{catalogRule.simple_action}}"/> <fillField stepKey="fillDiscountValue" selector="{{AdminNewCatalogPriceRuleActions.discountAmount}}" userInput="{{catalogRule.discount_amount}}"/> + <selectOption stepKey="discardSubsequentRules" selector="{{AdminNewCatalogPriceRuleActions.disregardRules}}" userInput="Yes"/> <!-- Scroll to top and either save or save and apply after the action group --> <scrollToTopOfPage stepKey="scrollToTop"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml index 0a64bb603f47e..4773fd8224fe3 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml @@ -34,7 +34,7 @@ <section name="AdminNewCatalogPriceRuleActions"> <element name="apply" type="select" selector="[name='simple_action']"/> <element name="discountAmount" type="input" selector="[name='discount_amount']"/> - <element name="disgardRules" type="select" selector="[name='stop_rules_processing']"/> + <element name="disregardRules" type="select" selector="[name='stop_rules_processing']"/> </section> <section name="AdminCatalogPriceRuleGrid"> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml index a447da428c219..70fd745986567 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml @@ -31,8 +31,6 @@ <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> - <magentoCLI stepKey="reindex" command="indexer:reindex"/> - <magentoCLI stepKey="flushCache" command="cache:flush"/> </before> <after> <!-- delete the simple product and catalog price rule and logout --> @@ -86,7 +84,9 @@ <!-- log in and create the price rule --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"/> + <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> + <argument name="catalogRule" value="CatalogRuleByFixed"/> + </actionGroup> <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> @@ -143,7 +143,9 @@ <!-- log in and create the price rule --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"/> + <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> + <argument name="catalogRule" value="CatalogRuleToPercent"/> + </actionGroup> <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> @@ -200,7 +202,9 @@ <!-- log in and create the price rule --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"/> + <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> + <argument name="catalogRule" value="CatalogRuleToFixed"/> + </actionGroup> <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> From 85caf639895f5fa1dd71dda08b791e8cbfaee7e4 Mon Sep 17 00:00:00 2001 From: hitesh-wagento <hitesh@wagento.com> Date: Thu, 12 Jul 2018 11:44:35 +0530 Subject: [PATCH 0284/1171] --- lib/web/css/source/lib/_navigation.less | 11 +++++++++++ lib/web/css/source/lib/variables/_navigation.less | 1 + 2 files changed, 12 insertions(+) diff --git a/lib/web/css/source/lib/_navigation.less b/lib/web/css/source/lib/_navigation.less index b2ed4352a334a..acae3c629500e 100644 --- a/lib/web/css/source/lib/_navigation.less +++ b/lib/web/css/source/lib/_navigation.less @@ -92,6 +92,9 @@ .lib-css(padding, @_nav-level0-item-padding); .lib-css(text-transform, @_nav-level0-text-transform); word-wrap: break-word; + &:hover { + .lib-css(color, @navigation-level0-item__hover__color); + } } &.active { @@ -139,6 +142,11 @@ .submenu { > li { word-wrap: break-word; + > a { + &:hover { + .lib-css(color, @navigation-level0-item__hover__color); + } + } } &:not(:first-child) { @@ -178,6 +186,9 @@ .lib-css(text-decoration, @_submenu-item-text-decoration); display: block; line-height: normal; + &:hover { + .lib-css(color, @navigation-level0-item__hover__color); + } } } } diff --git a/lib/web/css/source/lib/variables/_navigation.less b/lib/web/css/source/lib/variables/_navigation.less index 8cffee7f8e71a..2a4aa11014dce 100644 --- a/lib/web/css/source/lib/variables/_navigation.less +++ b/lib/web/css/source/lib/variables/_navigation.less @@ -28,6 +28,7 @@ @navigation-level0-item__active__border-width: 0 0 0 8px; @navigation-level0-item__active__color: ''; @navigation-level0-item__active__text-decoration: ''; +@navigation-level0-item__hover__color: @primary__color; @submenu__background: ''; @submenu__border: ''; From 98cc7522594a0cfb33c5ce5d017948e551c4462c Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Sat, 19 May 2018 15:21:25 +0530 Subject: [PATCH 0285/1171] Fixed set template syntax in block file #2 --- .../Paypal/Block/Adminhtml/System/Config/ApiWizard.php | 2 +- .../Paypal/Block/Adminhtml/System/Config/BmlApiWizard.php | 2 +- app/code/Magento/Paypal/Block/Iframe.php | 4 ++-- app/code/Magento/Review/Block/Form.php | 2 +- app/code/Magento/Review/Block/Rating/Entity/Detailed.php | 2 +- app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php | 2 +- app/code/Magento/UrlRewrite/Block/Edit.php | 2 +- app/code/Magento/User/Block/Role/Tab/Users.php | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Paypal/Block/Adminhtml/System/Config/ApiWizard.php b/app/code/Magento/Paypal/Block/Adminhtml/System/Config/ApiWizard.php index a1cfabd48440f..afa80892487a5 100644 --- a/app/code/Magento/Paypal/Block/Adminhtml/System/Config/ApiWizard.php +++ b/app/code/Magento/Paypal/Block/Adminhtml/System/Config/ApiWizard.php @@ -13,7 +13,7 @@ class ApiWizard extends \Magento\Config\Block\System\Config\Form\Field /** * Path to block template */ - const WIZARD_TEMPLATE = 'system/config/api_wizard.phtml'; + const WIZARD_TEMPLATE = 'Magento_Paypal::system/config/api_wizard.phtml'; /** * Set template to itself diff --git a/app/code/Magento/Paypal/Block/Adminhtml/System/Config/BmlApiWizard.php b/app/code/Magento/Paypal/Block/Adminhtml/System/Config/BmlApiWizard.php index bdca98e089e7b..c481871e1fb0f 100644 --- a/app/code/Magento/Paypal/Block/Adminhtml/System/Config/BmlApiWizard.php +++ b/app/code/Magento/Paypal/Block/Adminhtml/System/Config/BmlApiWizard.php @@ -11,7 +11,7 @@ class BmlApiWizard extends ApiWizard /** * Path to block template */ - const WIZARD_TEMPLATE = 'system/config/bml_api_wizard.phtml'; + const WIZARD_TEMPLATE = 'Magento_Paypal::system/config/bml_api_wizard.phtml'; /** * Get the button and scripts contents diff --git a/app/code/Magento/Paypal/Block/Iframe.php b/app/code/Magento/Paypal/Block/Iframe.php index 98fc05d0d2f60..8ccba52b8e686 100644 --- a/app/code/Magento/Paypal/Block/Iframe.php +++ b/app/code/Magento/Paypal/Block/Iframe.php @@ -116,7 +116,7 @@ protected function _construct() if ($file && $directory->isExist($directory->getRelativePath($file))) { $this->setTemplate($templateFile); } else { - $this->setTemplate('hss/iframe.phtml'); + $this->setTemplate('Magento_Paypal::hss/iframe.phtml'); } } } @@ -198,7 +198,7 @@ protected function _beforeToHtml() protected function _toHtml() { if ($this->_isAfterPaymentSave()) { - $this->setTemplate('hss/js.phtml'); + $this->setTemplate('Magento_Paypal::hss/js.phtml'); return parent::_toHtml(); } if (!$this->_shouldRender) { diff --git a/app/code/Magento/Review/Block/Form.php b/app/code/Magento/Review/Block/Form.php index 440e13deb5839..c6dfad8265ac7 100644 --- a/app/code/Magento/Review/Block/Form.php +++ b/app/code/Magento/Review/Block/Form.php @@ -139,7 +139,7 @@ protected function _construct() ); } - $this->setTemplate('form.phtml'); + $this->setTemplate('Magento_Review::form.phtml'); } /** diff --git a/app/code/Magento/Review/Block/Rating/Entity/Detailed.php b/app/code/Magento/Review/Block/Rating/Entity/Detailed.php index 0ce4f436ae704..eee00483ace66 100644 --- a/app/code/Magento/Review/Block/Rating/Entity/Detailed.php +++ b/app/code/Magento/Review/Block/Rating/Entity/Detailed.php @@ -49,7 +49,7 @@ protected function _toHtml() $reviewsCount = $this->_ratingFactory->create()->getTotalReviews($entityId, true); if ($reviewsCount == 0) { #return __('Be the first to review this product'); - $this->setTemplate('empty.phtml'); + $this->setTemplate('Magento_Review::empty.phtml'); return parent::_toHtml(); } diff --git a/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php b/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php index 87f65ef311ac2..fb6c5e7b431ef 100644 --- a/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php +++ b/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php @@ -66,7 +66,7 @@ public function afterGetSectionData(\Magento\Checkout\CustomerData\Cart $subject foreach ($result['items'] as $key => $itemAsArray) { if ($item = $this->findItemById($itemAsArray['item_id'], $items)) { $this->itemPriceRenderer->setItem($item); - $this->itemPriceRenderer->setTemplate('checkout/cart/item/price/sidebar.phtml'); + $this->itemPriceRenderer->setTemplate('Magento_Tax::checkout/cart/item/price/sidebar.phtml'); $result['items'][$key]['product_price']=$this->itemPriceRenderer->toHtml(); } } diff --git a/app/code/Magento/UrlRewrite/Block/Edit.php b/app/code/Magento/UrlRewrite/Block/Edit.php index baee8af893083..210ed5189eb4c 100644 --- a/app/code/Magento/UrlRewrite/Block/Edit.php +++ b/app/code/Magento/UrlRewrite/Block/Edit.php @@ -65,7 +65,7 @@ public function __construct( */ protected function _prepareLayout() { - $this->setTemplate('edit.phtml'); + $this->setTemplate('Magento_UrlRewrite::edit.phtml'); $this->_addBackButton(); $this->_prepareLayoutFeatures(); diff --git a/app/code/Magento/User/Block/Role/Tab/Users.php b/app/code/Magento/User/Block/Role/Tab/Users.php index 27604c9aed29a..2d04ef27f536d 100644 --- a/app/code/Magento/User/Block/Role/Tab/Users.php +++ b/app/code/Magento/User/Block/Role/Tab/Users.php @@ -45,7 +45,7 @@ protected function _construct() $roleId = $this->getRequest()->getParam('rid', false); /** @var \Magento\User\Model\ResourceModel\User\Collection $users */ $users = $this->_userCollectionFactory->create()->load(); - $this->setTemplate('role/users.phtml')->assign('users', $users->getItems())->assign('roleId', $roleId); + $this->setTemplate('Magento_User::role/users.phtml')->assign('users', $users->getItems())->assign('roleId', $roleId); } /** From 4499034ae38aeed1d547971d57da0365aa89ce71 Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Sun, 20 May 2018 06:42:54 +0530 Subject: [PATCH 0286/1171] Fixed coding standard error --- app/code/Magento/User/Block/Role/Tab/Users.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/User/Block/Role/Tab/Users.php b/app/code/Magento/User/Block/Role/Tab/Users.php index 2d04ef27f536d..a95a68cbe14f3 100644 --- a/app/code/Magento/User/Block/Role/Tab/Users.php +++ b/app/code/Magento/User/Block/Role/Tab/Users.php @@ -45,7 +45,9 @@ protected function _construct() $roleId = $this->getRequest()->getParam('rid', false); /** @var \Magento\User\Model\ResourceModel\User\Collection $users */ $users = $this->_userCollectionFactory->create()->load(); - $this->setTemplate('Magento_User::role/users.phtml')->assign('users', $users->getItems())->assign('roleId', $roleId); + $this->setTemplate('Magento_User::role/users.phtml') + ->assign('users', $users->getItems()) + ->assign('roleId', $roleId); } /** From 48696723d2a312a162fc364a46294c76e73acc02 Mon Sep 17 00:00:00 2001 From: Tommy Quissens <tommy.quissens@storefront.be> Date: Tue, 10 Jul 2018 12:47:04 +0200 Subject: [PATCH 0287/1171] Updated security issues details The e-mail address is responding with an autoreply which tells to use bugcrowd. So just mention bugcrowd instead so people don't loose time typing & encrypting a mail which has no use. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a2cf536bb6520..6b2ac458eb403 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ To learn more about issue gate labels click [here](https://github.com/magento/ma <h2>Reporting security issues</h2> -To report security vulnerabilities in Magento software or web sites, please e-mail <a href="mailto:security@magento.com">security@magento.com</a>. Please do not report security issues using GitHub. Be sure to encrypt your e-mail with our <a href="https://info2.magento.com/rs/magentoenterprise/images/security_at_magento.asc">encryption key</a> if it includes sensitive information. Learn more about reporting security issues <a href="https://magento.com/security/reporting-magento-security-issue">here</a>. +To report security vulnerabilities in Magento software or web sites, please create a Bugcrowd researcher account <a href="https://bugcrowd.com/magento">there</a> to submit and follow-up your issue. Learn more about reporting security issues <a href="https://magento.com/security/reporting-magento-security-issue">here</a>. Stay up-to-date on the latest security news and patches for Magento by signing up for <a href="https://magento.com/security/sign-up">Security Alert Notifications</a>. From db6f0278b011d4f3413a7c2e09ec54d863e087da Mon Sep 17 00:00:00 2001 From: Pavel Usachev <pusachev@oroinc.com> Date: Mon, 11 Dec 2017 15:05:44 +0200 Subject: [PATCH 0288/1171] Fixed condition with usage isPost method - removed private function isPost - removed unused imports - added request method isPost - added interface that expands HTTP request class - fixed unit test in Contact module - fixed integration tests in Contact module --- .../Magento/Contact/Controller/Index/Post.php | 13 +---- .../Test/Unit/Controller/Index/PostTest.php | 6 ++- .../Magento/Contact/Controller/IndexTest.php | 4 ++ .../Framework/App/HttpRequestInterface.php | 52 +++++++++++++++++++ .../Magento/Framework/App/Request/Http.php | 3 +- 5 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 lib/internal/Magento/Framework/App/HttpRequestInterface.php diff --git a/app/code/Magento/Contact/Controller/Index/Post.php b/app/code/Magento/Contact/Controller/Index/Post.php index b51e3c9189502..bdc2fee088d9e 100644 --- a/app/code/Magento/Contact/Controller/Index/Post.php +++ b/app/code/Magento/Contact/Controller/Index/Post.php @@ -13,7 +13,6 @@ use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\Controller\Result\Redirect; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\HTTP\PhpEnvironment\Request; use Psr\Log\LoggerInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; @@ -68,7 +67,7 @@ public function __construct( */ public function execute() { - if (!$this->isPostRequest()) { + if (!$this->getRequest()->isPost()) { return $this->resultRedirectFactory->create()->setPath('*/*/'); } try { @@ -102,16 +101,6 @@ private function sendEmail($post) ); } - /** - * @return bool - */ - private function isPostRequest() - { - /** @var Request $request */ - $request = $this->getRequest(); - return !empty($request->getPostValue()); - } - /** * @return array * @throws \Exception diff --git a/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php b/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php index 4b49373201e51..f01922f42f40c 100644 --- a/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php +++ b/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php @@ -78,7 +78,7 @@ protected function setUp() $this->createMock(\Magento\Framework\Message\ManagerInterface::class); $this->requestStub = $this->createPartialMock( \Magento\Framework\App\Request\Http::class, - ['getPostValue', 'getParams', 'getParam'] + ['getPostValue', 'getParams', 'getParam', 'isPost'] ); $this->redirectResultMock = $this->createMock(\Magento\Framework\Controller\Result\Redirect::class); $this->redirectResultMock->method('setPath')->willReturnSelf(); @@ -177,6 +177,10 @@ public function testExecuteValidPost() */ private function stubRequestPostData($post) { + $this->requestStub + ->expects($this->once()) + ->method('isPost') + ->willReturn(!empty($post)); $this->requestStub->method('getPostValue')->willReturn($post); $this->requestStub->method('getParams')->willReturn($post); $this->requestStub->method('getParam')->willReturnCallback( diff --git a/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php b/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php index a06a981a4c891..8a94e64cf625a 100644 --- a/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php @@ -6,6 +6,8 @@ namespace Magento\Contact\Controller; +use Zend\Http\Request; + /** * Contact index controller test */ @@ -20,6 +22,7 @@ public function testPostAction() 'hideit' => '', ]; $this->getRequest()->setPostValue($params); + $this->getRequest()->setMethod(Request::METHOD_POST); $this->dispatch('contact/index/post'); $this->assertRedirect($this->stringContains('contact/index')); @@ -39,6 +42,7 @@ public function testPostAction() public function testInvalidPostAction($params, $expectedMessage) { $this->getRequest()->setPostValue($params); + $this->getRequest()->setMethod(Request::METHOD_POST); $this->dispatch('contact/index/post'); $this->assertRedirect($this->stringContains('contact/index')); diff --git a/lib/internal/Magento/Framework/App/HttpRequestInterface.php b/lib/internal/Magento/Framework/App/HttpRequestInterface.php new file mode 100644 index 0000000000000..685db5f47ffc7 --- /dev/null +++ b/lib/internal/Magento/Framework/App/HttpRequestInterface.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\App; + +interface HttpRequestInterface +{ + /** + * Returned true if POST request + * + * @return boolean + */ + public function isPost(); + + /** + * Returned true if GET request + * + * @return boolean + */ + public function isGet(); + + /** + * Returned true if PATCH request + * + * @return boolean + */ + public function isPatch(); + + /** + * Returned true if DELETE request + * + * @return boolean + */ + public function isDelete(); + + /** + * Returned true if PUT request + * + * @return boolean + */ + public function isPut(); + + /** + * Returned true if Ajax request + * + * @return boolean + */ + public function isAjax(); +} diff --git a/lib/internal/Magento/Framework/App/Request/Http.php b/lib/internal/Magento/Framework/App/Request/Http.php index 4421903f40c2e..7e8ceacc63520 100644 --- a/lib/internal/Magento/Framework/App/Request/Http.php +++ b/lib/internal/Magento/Framework/App/Request/Http.php @@ -5,6 +5,7 @@ */ namespace Magento\Framework\App\Request; +use Magento\Framework\App\HttpRequestInterface; use Magento\Framework\App\RequestContentInterface; use Magento\Framework\App\RequestSafetyInterface; use Magento\Framework\App\Route\ConfigInterface\Proxy as ConfigInterface; @@ -16,7 +17,7 @@ /** * Http request */ -class Http extends Request implements RequestContentInterface, RequestSafetyInterface +class Http extends Request implements RequestContentInterface, RequestSafetyInterface, HttpRequestInterface { /**#@+ * HTTP Ports From 1dd2017ffae83cbebadf78f02f7bc1eb9c860302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Sat, 14 Jul 2018 18:11:54 +0300 Subject: [PATCH 0289/1171] Update _rating.less --- lib/web/css/source/lib/_rating.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/source/lib/_rating.less b/lib/web/css/source/lib/_rating.less index e585e4489d65e..535fa44616039 100644 --- a/lib/web/css/source/lib/_rating.less +++ b/lib/web/css/source/lib/_rating.less @@ -38,7 +38,7 @@ input[type="radio"] { .lib-visually-hidden(); - &:focus, + &:hover, &:checked { + label { &:before { From 04d4bb07328e4b145d80e0c008cbd131a1ed300f Mon Sep 17 00:00:00 2001 From: Steven de Jong <steven.de.jong@guapa.nl> Date: Sat, 2 Jun 2018 16:01:30 +0200 Subject: [PATCH 0290/1171] #15588 Added the appEmulation to get the correct image Url for the generated sitemaps. Context had incorrect Url data. --- .../Controller/Adminhtml/Sitemap/Generate.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index 67d2ce4f4f148..c46733b7d325d 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -6,8 +6,29 @@ */ namespace Magento\Sitemap\Controller\Adminhtml\Sitemap; +use Magento\Backend\App\Action; +use Magento\Store\Model\App\Emulation; + class Generate extends \Magento\Sitemap\Controller\Adminhtml\Sitemap { + /** @var \Magento\Store\Model\App\Emulation $appEmulation */ + private $appEmulation; + + /** + * Generate constructor. + * @param Action\Context $context + * @param \Magento\Store\Model\App\Emulation $appEmulation + */ + public function __construct( + Action\Context $context, + Emulation $appEmulation + ) { + $this->appEmulation = $appEmulation; + parent::__construct( + $context + ); + } + /** * Generate sitemap * @@ -23,7 +44,13 @@ public function execute() // if sitemap record exists if ($sitemap->getId()) { try { + //We need to emulate to get the correct frontend URL for the product images + $this->appEmulation->startEnvironmentEmulation($sitemap->getStoreId(), + \Magento\Framework\App\Area::AREA_FRONTEND, + true + ); $sitemap->generateXml(); + $this->appEmulation->stopEnvironmentEmulation(); $this->messageManager->addSuccess( __('The sitemap "%1" has been generated.', $sitemap->getSitemapFilename()) From d845518d5a2cb0053b62420e975921ab66ac09ae Mon Sep 17 00:00:00 2001 From: Steven de Jong <steven.de.jong@guapa.nl> Date: Sat, 2 Jun 2018 21:51:09 +0200 Subject: [PATCH 0291/1171] #15588 Refactor for code neatness Travis CI. 48 | ERROR | [x] Opening parenthesis of a multi-line function call must be the last content on the line --- .../Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index c46733b7d325d..4ef67dbabfc15 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -45,7 +45,8 @@ public function execute() if ($sitemap->getId()) { try { //We need to emulate to get the correct frontend URL for the product images - $this->appEmulation->startEnvironmentEmulation($sitemap->getStoreId(), + $this->appEmulation->startEnvironmentEmulation( + $sitemap->getStoreId(), \Magento\Framework\App\Area::AREA_FRONTEND, true ); From 547f6fde3bb7243af64052ba4611013dcbb75084 Mon Sep 17 00:00:00 2001 From: Steven de Jong <steven.de.jong@guapa.nl> Date: Sun, 3 Jun 2018 02:50:25 +0200 Subject: [PATCH 0292/1171] Update for Codacy removed whitespace --- .../Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index 4ef67dbabfc15..819608ee0235d 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -29,7 +29,7 @@ public function __construct( ); } - /** + /** * Generate sitemap * * @return void From f422401e6d55633dd90af9a52be4c841330daeb2 Mon Sep 17 00:00:00 2001 From: Steven de Jong <steven.de.jong@guapa.nl> Date: Sun, 3 Jun 2018 19:21:08 +0200 Subject: [PATCH 0293/1171] #15588 Fixed incorrect image urls sitemap Change for travisCI --- .../Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index 819608ee0235d..6872122cb1013 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -28,8 +28,8 @@ public function __construct( $context ); } - - /** + + /** * Generate sitemap * * @return void From e11be0019d836d3b465a6c4583334a7353997c5f Mon Sep 17 00:00:00 2001 From: Steven de Jong <steven.de.jong@guapa.nl> Date: Wed, 6 Jun 2018 09:02:57 +0200 Subject: [PATCH 0294/1171] #15588 Fixed incorrect image urls in multistore xml sitemap placed the stop Emulation in a finally. Now it will always be executed. --- .../Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index 6872122cb1013..9d578c97b0ed7 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -43,6 +43,7 @@ public function execute() $sitemap->load($id); // if sitemap record exists if ($sitemap->getId()) { + try { //We need to emulate to get the correct frontend URL for the product images $this->appEmulation->startEnvironmentEmulation( @@ -51,7 +52,6 @@ public function execute() true ); $sitemap->generateXml(); - $this->appEmulation->stopEnvironmentEmulation(); $this->messageManager->addSuccess( __('The sitemap "%1" has been generated.', $sitemap->getSitemapFilename()) @@ -60,6 +60,8 @@ public function execute() $this->messageManager->addError($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addException($e, __('We can\'t generate the sitemap right now.')); + } finally { + $this->appEmulation->stopEnvironmentEmulation(); } } else { $this->messageManager->addError(__('We can\'t find a sitemap to generate.')); From 25010d4e159579641f13d326b795cf6bc88e8897 Mon Sep 17 00:00:00 2001 From: David Manners <dmanners87@gmail.com> Date: Wed, 6 Jun 2018 10:12:56 +0200 Subject: [PATCH 0295/1171] magento/magento2#15588: Remove extra line from if --- .../Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index 9d578c97b0ed7..29df85f791a66 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -43,7 +43,6 @@ public function execute() $sitemap->load($id); // if sitemap record exists if ($sitemap->getId()) { - try { //We need to emulate to get the correct frontend URL for the product images $this->appEmulation->startEnvironmentEmulation( From 65ccd39057ff1ebcc7d697ca0723b206e3e108c1 Mon Sep 17 00:00:00 2001 From: David Manners <dmanners87@gmail.com> Date: Wed, 6 Jun 2018 11:55:14 +0200 Subject: [PATCH 0296/1171] magento/magenot2#15588: add dependency in a backwards compatible way --- .../Controller/Adminhtml/Sitemap/Generate.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index 29df85f791a66..d19b248c8008f 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -8,6 +8,7 @@ use Magento\Backend\App\Action; use Magento\Store\Model\App\Emulation; +use Magento\Framework\App\ObjectManager; class Generate extends \Magento\Sitemap\Controller\Adminhtml\Sitemap { @@ -17,16 +18,15 @@ class Generate extends \Magento\Sitemap\Controller\Adminhtml\Sitemap /** * Generate constructor. * @param Action\Context $context - * @param \Magento\Store\Model\App\Emulation $appEmulation + * @param \Magento\Store\Model\App\Emulation|null $appEmulation */ public function __construct( Action\Context $context, - Emulation $appEmulation + Emulation $appEmulation = null ) { - $this->appEmulation = $appEmulation; - parent::__construct( - $context - ); + parent::__construct($context); + $this->appEmulation = $appEmulation ?: ObjectManager::getInstance() + ->get(\Magento\Store\Model\App\Emulation::class); } /** From fe2c5aef47716d0e1364242fb30aa9411ba5ee96 Mon Sep 17 00:00:00 2001 From: IvanPletnyov <ivan.pletnyov@transoftgroup.com> Date: Mon, 18 Jun 2018 18:06:08 +0300 Subject: [PATCH 0297/1171] 15863: [Forwardport] Refactored javascript code of admin notification modal popup --- .../templates/system/messages/popup.phtml | 2 +- .../view/adminhtml/web/js/system/messages/popup.js | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/AdminNotification/view/adminhtml/templates/system/messages/popup.phtml b/app/code/Magento/AdminNotification/view/adminhtml/templates/system/messages/popup.phtml index d654504a41e5c..0448daaf17644 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/templates/system/messages/popup.phtml +++ b/app/code/Magento/AdminNotification/view/adminhtml/templates/system/messages/popup.phtml @@ -23,7 +23,7 @@ { "[data-role=system_messages_list]": { "Magento_AdminNotification/js/system/messages/popup": { - class: 'modal-system-messages ui-popup-message' + "class":"modal-system-messages ui-popup-message" } } } diff --git a/app/code/Magento/AdminNotification/view/adminhtml/web/js/system/messages/popup.js b/app/code/Magento/AdminNotification/view/adminhtml/web/js/system/messages/popup.js index f3f6a5fb1a123..39c61d6e07d29 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/web/js/system/messages/popup.js +++ b/app/code/Magento/AdminNotification/view/adminhtml/web/js/system/messages/popup.js @@ -1,24 +1,26 @@ /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. -*/ + */ define([ 'jquery', 'Magento_Ui/js/modal/modal' -], function ($) { +], function ($, modal) { 'use strict'; return function (data, element) { - if (this.modal) { - this.modal.html($(element).html()); + + if (modal.modal) { + modal.modal.html($(element).html()); } else { - this.modal = $(element).modal({ + modal.modal = $(element).modal({ modalClass: data.class, type: 'popup', buttons: [] }); } - this.modal.modal('openModal'); + + modal.modal.modal('openModal'); }; }); From cdb473851392be7188ef882a6153a986a9956e72 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 20 Jun 2018 16:36:10 +0300 Subject: [PATCH 0298/1171] Covered Magento\Checkout\Model\Cart\CollectQuote by Unit Test --- .../Test/Unit/Model/Cart/CollectQuoteTest.php | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php new file mode 100644 index 0000000000000..70bb9df16b6ac --- /dev/null +++ b/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php @@ -0,0 +1,178 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Test\Unit\Model\Cart; + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\RegionInterface; +use Magento\Checkout\Model\Cart\CollectQuote; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\EstimateAddressInterface; +use Magento\Quote\Api\Data\EstimateAddressInterfaceFactory; +use Magento\Quote\Api\ShippingMethodManagementInterface; +use Magento\Quote\Model\Quote; +use PHPUnit\Framework\TestCase; +/** + * Class CollectQuoteTest + */ +class CollectQuoteTest extends TestCase +{ + /** + * @var CollectQuote + */ + private $model; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var CustomerSession|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerSessionMock; + + /** + * @var CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerRepositoryMock; + + /** + * @var AddressRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $addressRepositoryMock; + + /** + * @var EstimateAddressInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $estimateAddressFactoryMock; + + /** + * @var EstimateAddressInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $estimateAddressMock; + + /** + * @var ShippingMethodManagementInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $shippingMethodManagerMock; + + /** + * @var CartRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $quoteRepositoryMock; + + /** + * @var Quote|\PHPUnit_Framework_MockObject_MockObject + */ + private $quoteMock; + + /** + * @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerMock; + + /** + * @var AddressInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $addressMock; + + /** + * @var RegionInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $regionMock; + + /** + * Set up + */ + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + $this->customerSessionMock = $this->createMock(CustomerSession::class); + $this->customerRepositoryMock = $this->getMockForAbstractClass( + CustomerRepositoryInterface::class, + [], + '', + false, + true, + true, + ['getById'] + ); + $this->addressRepositoryMock = $this->createMock(AddressRepositoryInterface::class); + $this->estimateAddressMock = $this->createMock(EstimateAddressInterface::class); + $this->estimateAddressFactoryMock = $this->getMockBuilder(EstimateAddressInterfaceFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->shippingMethodManagerMock = $this->createMock(ShippingMethodManagementInterface::class); + $this->quoteRepositoryMock = $this->createMock(CartRepositoryInterface::class); + $this->quoteMock = $this->createMock(Quote::class); + $this->customerMock = $this->createMock(CustomerInterface::class); + $this->addressMock = $this->createMock(AddressInterface::class); + $this->regionMock = $this->createMock(RegionInterface::class); + + $this->model = $this->objectManager->getObject( + CollectQuote::class, + [ + 'customerSession' => $this->customerSessionMock, + 'customerRepository' => $this->customerRepositoryMock, + 'addressRepository' => $this->addressRepositoryMock, + 'estimatedAddressFactory' => $this->estimateAddressFactoryMock, + 'shippingMethodManager' => $this->shippingMethodManagerMock, + 'quoteRepository' => $this->quoteRepositoryMock + ] + ); + } + + /** + * Test collect method + */ + public function testCollect() + { + $customerId = 1; + $defaultAddressId = 999; + $countryId = 'USA'; + $regionId = 'CA'; + $this->customerSessionMock->expects(self::once()) + ->method('isLoggedIn') + ->willReturn(true); + $this->customerSessionMock->expects(self::once()) + ->method('getCustomerId') + ->willReturn($customerId); + $this->customerRepositoryMock->expects(self::once()) + ->method('getById') + ->willReturn($this->customerMock); + $this->customerMock->expects(self::once()) + ->method('getDefaultShipping') + ->willReturn($defaultAddressId); + $this->addressMock->expects(self::once()) + ->method('getCountryId') + ->willReturn($countryId); + $this->regionMock->expects(self::once()) + ->method('getRegion') + ->willReturn($regionId); + $this->addressMock->expects(self::once()) + ->method('getRegion') + ->willReturn($this->regionMock); + $this->addressRepositoryMock->expects(self::once()) + ->method('getById') + ->with($defaultAddressId) + ->willReturn($this->addressMock); + $this->estimateAddressFactoryMock->expects(self::once()) + ->method('create') + ->willReturn($this->estimateAddressMock); + $this->quoteRepositoryMock->expects(self::once()) + ->method('save'); + + $this->model->collect($this->quoteMock); + } +} From 52e97269e89432be5aeb19fa1f33857668f66dec Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 20 Jun 2018 20:46:19 +0300 Subject: [PATCH 0299/1171] Refactoring to remove some dependencies. --- .../Test/Unit/Model/Cart/CollectQuoteTest.php | 86 +++++++------------ 1 file changed, 32 insertions(+), 54 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php index 70bb9df16b6ac..9461da1f38820 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php @@ -14,13 +14,13 @@ use Magento\Customer\Api\Data\RegionInterface; use Magento\Checkout\Model\Cart\CollectQuote; use Magento\Customer\Model\Session as CustomerSession; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\EstimateAddressInterface; use Magento\Quote\Api\Data\EstimateAddressInterfaceFactory; use Magento\Quote\Api\ShippingMethodManagementInterface; use Magento\Quote\Model\Quote; use PHPUnit\Framework\TestCase; + /** * Class CollectQuoteTest */ @@ -31,11 +31,6 @@ class CollectQuoteTest extends TestCase */ private $model; - /** - * @var ObjectManager - */ - private $objectManager; - /** * @var CustomerSession|\PHPUnit_Framework_MockObject_MockObject */ @@ -86,17 +81,11 @@ class CollectQuoteTest extends TestCase */ private $addressMock; - /** - * @var RegionInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $regionMock; - /** * Set up */ protected function setUp() { - $this->objectManager = new ObjectManager($this); $this->customerSessionMock = $this->createMock(CustomerSession::class); $this->customerRepositoryMock = $this->getMockForAbstractClass( CustomerRepositoryInterface::class, @@ -118,18 +107,14 @@ protected function setUp() $this->quoteMock = $this->createMock(Quote::class); $this->customerMock = $this->createMock(CustomerInterface::class); $this->addressMock = $this->createMock(AddressInterface::class); - $this->regionMock = $this->createMock(RegionInterface::class); - - $this->model = $this->objectManager->getObject( - CollectQuote::class, - [ - 'customerSession' => $this->customerSessionMock, - 'customerRepository' => $this->customerRepositoryMock, - 'addressRepository' => $this->addressRepositoryMock, - 'estimatedAddressFactory' => $this->estimateAddressFactoryMock, - 'shippingMethodManager' => $this->shippingMethodManagerMock, - 'quoteRepository' => $this->quoteRepositoryMock - ] + + $this->model = new CollectQuote( + $this->customerSessionMock, + $this->customerRepositoryMock, + $this->addressRepositoryMock, + $this->estimateAddressFactoryMock, + $this->shippingMethodManagerMock, + $this->quoteRepositoryMock ); } @@ -142,36 +127,29 @@ public function testCollect() $defaultAddressId = 999; $countryId = 'USA'; $regionId = 'CA'; - $this->customerSessionMock->expects(self::once()) - ->method('isLoggedIn') - ->willReturn(true); - $this->customerSessionMock->expects(self::once()) - ->method('getCustomerId') - ->willReturn($customerId); - $this->customerRepositoryMock->expects(self::once()) - ->method('getById') - ->willReturn($this->customerMock); - $this->customerMock->expects(self::once()) - ->method('getDefaultShipping') - ->willReturn($defaultAddressId); - $this->addressMock->expects(self::once()) - ->method('getCountryId') - ->willReturn($countryId); - $this->regionMock->expects(self::once()) - ->method('getRegion') - ->willReturn($regionId); - $this->addressMock->expects(self::once()) - ->method('getRegion') - ->willReturn($this->regionMock); - $this->addressRepositoryMock->expects(self::once()) - ->method('getById') - ->with($defaultAddressId) - ->willReturn($this->addressMock); - $this->estimateAddressFactoryMock->expects(self::once()) - ->method('create') - ->willReturn($this->estimateAddressMock); - $this->quoteRepositoryMock->expects(self::once()) - ->method('save'); + $regionMock = $this->createMock(RegionInterface::class); + + $this->customerSessionMock->expects(self::once())->method('isLoggedIn')->willReturn(true); + $this->customerSessionMock->expects(self::once())->method('getCustomerId')->willReturn($customerId); + $this->customerRepositoryMock->expects(self::once())->method('getById')->willReturn($this->customerMock); + $this->customerMock->expects(self::once())->method('getDefaultShipping')->willReturn($defaultAddressId); + $this->addressMock->expects(self::once())->method('getCountryId')->willReturn($countryId); + $regionMock->expects(self::once())->method('getRegion')->willReturn($regionId); + $this->addressMock->expects(self::once())->method('getRegion')->willReturn($regionMock); + $this->addressRepositoryMock->expects(self::once())->method('getById')->with($defaultAddressId)->willReturn($this->addressMock); + $this->estimateAddressFactoryMock->expects(self::once())->method('create')->willReturn($this->estimateAddressMock); + $this->quoteRepositoryMock->expects(self::once())->method('save'); + + $this->model->collect($this->quoteMock); + } + + /** + * Test with a not logged in customer + */ + public function testCustomerIsNotLoggedIn() + { + $this->customerSessionMock->expects(self::once())->method('isLoggedIn')->willReturn(false); + $this->customerRepositoryMock->expects(self::never())->method('getById'); $this->model->collect($this->quoteMock); } From 1bfaa27f03fc155bd5d293501c1a562c9861fcb3 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 23 Jun 2018 09:45:44 +0300 Subject: [PATCH 0300/1171] Fixed line exceeds maximum limit --- .../Test/Unit/Model/Cart/CollectQuoteTest.php | 47 ++++++++++++++----- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php index 9461da1f38820..d800a5b0120c9 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php @@ -129,16 +129,36 @@ public function testCollect() $regionId = 'CA'; $regionMock = $this->createMock(RegionInterface::class); - $this->customerSessionMock->expects(self::once())->method('isLoggedIn')->willReturn(true); - $this->customerSessionMock->expects(self::once())->method('getCustomerId')->willReturn($customerId); - $this->customerRepositoryMock->expects(self::once())->method('getById')->willReturn($this->customerMock); - $this->customerMock->expects(self::once())->method('getDefaultShipping')->willReturn($defaultAddressId); - $this->addressMock->expects(self::once())->method('getCountryId')->willReturn($countryId); - $regionMock->expects(self::once())->method('getRegion')->willReturn($regionId); - $this->addressMock->expects(self::once())->method('getRegion')->willReturn($regionMock); - $this->addressRepositoryMock->expects(self::once())->method('getById')->with($defaultAddressId)->willReturn($this->addressMock); - $this->estimateAddressFactoryMock->expects(self::once())->method('create')->willReturn($this->estimateAddressMock); - $this->quoteRepositoryMock->expects(self::once())->method('save'); + $this->customerSessionMock->expects(self::once()) + ->method('isLoggedIn') + ->willReturn(true); + $this->customerSessionMock->expects(self::once()) + ->method('getCustomerId') + ->willReturn($customerId); + $this->customerRepositoryMock->expects(self::once()) + ->method('getById') + ->willReturn($this->customerMock); + $this->customerMock->expects(self::once()) + ->method('getDefaultShipping') + ->willReturn($defaultAddressId); + $this->addressMock->expects(self::once()) + ->method('getCountryId') + ->willReturn($countryId); + $regionMock->expects(self::once()) + ->method('getRegion') + ->willReturn($regionId); + $this->addressMock->expects(self::once()) + ->method('getRegion') + ->willReturn($regionMock); + $this->addressRepositoryMock->expects(self::once()) + ->method('getById') + ->with($defaultAddressId) + ->willReturn($this->addressMock); + $this->estimateAddressFactoryMock->expects(self::once()) + ->method('create') + ->willReturn($this->estimateAddressMock); + $this->quoteRepositoryMock->expects(self::once()) + ->method('save'); $this->model->collect($this->quoteMock); } @@ -148,8 +168,11 @@ public function testCollect() */ public function testCustomerIsNotLoggedIn() { - $this->customerSessionMock->expects(self::once())->method('isLoggedIn')->willReturn(false); - $this->customerRepositoryMock->expects(self::never())->method('getById'); + $this->customerSessionMock->expects(self::once()) + ->method('isLoggedIn') + ->willReturn(false); + $this->customerRepositoryMock->expects(self::never()) + ->method('getById'); $this->model->collect($this->quoteMock); } From 7c04c160b87ca959b34783de95977d22553db576 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 9 Jul 2018 09:37:53 +0300 Subject: [PATCH 0301/1171] Applying CR recommendations --- .../Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php index d800a5b0120c9..5239825820b94 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php @@ -98,10 +98,9 @@ protected function setUp() ); $this->addressRepositoryMock = $this->createMock(AddressRepositoryInterface::class); $this->estimateAddressMock = $this->createMock(EstimateAddressInterface::class); - $this->estimateAddressFactoryMock = $this->getMockBuilder(EstimateAddressInterfaceFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); + $this->estimateAddressFactoryMock = $this->createPartialMock(EstimateAddressInterfaceFactory::class, + ['create'] + ); $this->shippingMethodManagerMock = $this->createMock(ShippingMethodManagementInterface::class); $this->quoteRepositoryMock = $this->createMock(CartRepositoryInterface::class); $this->quoteMock = $this->createMock(Quote::class); @@ -158,7 +157,8 @@ public function testCollect() ->method('create') ->willReturn($this->estimateAddressMock); $this->quoteRepositoryMock->expects(self::once()) - ->method('save'); + ->method('save') + ->with($this->quoteMock); $this->model->collect($this->quoteMock); } From a448585095bd5a49ea8c70610ce85b71b81f0678 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 9 Jul 2018 13:26:33 +0300 Subject: [PATCH 0302/1171] Fixing multi-line function call --- .../Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php index 5239825820b94..4a6618ee1e8d2 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php @@ -98,9 +98,8 @@ protected function setUp() ); $this->addressRepositoryMock = $this->createMock(AddressRepositoryInterface::class); $this->estimateAddressMock = $this->createMock(EstimateAddressInterface::class); - $this->estimateAddressFactoryMock = $this->createPartialMock(EstimateAddressInterfaceFactory::class, - ['create'] - ); + $this->estimateAddressFactoryMock = + $this->createPartialMock(EstimateAddressInterfaceFactory::class, ['create']); $this->shippingMethodManagerMock = $this->createMock(ShippingMethodManagementInterface::class); $this->quoteRepositoryMock = $this->createMock(CartRepositoryInterface::class); $this->quoteMock = $this->createMock(Quote::class); From eca863ccb0884cc9481099eac6e676d89967b20b Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 10 Jul 2018 13:33:51 +0200 Subject: [PATCH 0303/1171] Clarification for the test name --- .../Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php index 4a6618ee1e8d2..14410578b12e4 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/Cart/CollectQuoteTest.php @@ -165,7 +165,7 @@ public function testCollect() /** * Test with a not logged in customer */ - public function testCustomerIsNotLoggedIn() + public function testCollectWhenCustomerIsNotLoggedIn() { $this->customerSessionMock->expects(self::once()) ->method('isLoggedIn') From e92064ada057a657140d2ecb7380f25f301d75c5 Mon Sep 17 00:00:00 2001 From: Torrey Tsui <torreytsui@users.noreply.github.com> Date: Wed, 4 Jul 2018 16:37:21 +0100 Subject: [PATCH 0304/1171] Fix zero price simple failed to resolve as default --- .../Pricing/Price/ConfigurablePriceResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Pricing/Price/ConfigurablePriceResolver.php b/app/code/Magento/ConfigurableProduct/Pricing/Price/ConfigurablePriceResolver.php index 3d42217de5f91..5581fcc07b861 100644 --- a/app/code/Magento/ConfigurableProduct/Pricing/Price/ConfigurablePriceResolver.php +++ b/app/code/Magento/ConfigurableProduct/Pricing/Price/ConfigurablePriceResolver.php @@ -64,7 +64,7 @@ public function resolvePrice(\Magento\Framework\Pricing\SaleableInterface $produ foreach ($this->lowestPriceOptionsProvider->getProducts($product) as $subProduct) { $productPrice = $this->priceResolver->resolvePrice($subProduct); - $price = $price ? min($price, $productPrice) : $productPrice; + $price = isset($price) ? min($price, $productPrice) : $productPrice; } return (float)$price; From f500af37927dbc67dcf382b181e27a9e4b699c23 Mon Sep 17 00:00:00 2001 From: torreytsui <tt@amp.co> Date: Fri, 6 Jul 2018 13:44:50 +0100 Subject: [PATCH 0305/1171] Extract resolvePrice() test cases --- .../Price/ConfigurablePriceResolverTest.php | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php index 99c31420473f5..5909449202350 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php @@ -55,24 +55,31 @@ protected function setUp() * situation: one product is supplying the price, which could be a price of zero (0) * * @dataProvider resolvePriceDataProvider + * + * @param $variantPrices + * @param $expectedPrice */ - public function testResolvePrice($expectedValue) + public function testResolvePrice($variantPrices, $expectedPrice) { - $price = $expectedValue; - $product = $this->getMockBuilder( \Magento\Catalog\Model\Product::class )->disableOriginalConstructor()->getMock(); $product->expects($this->never())->method('getSku'); - $this->lowestPriceOptionsProvider->expects($this->once())->method('getProducts')->willReturn([$product]); - $this->priceResolver->expects($this->once()) + $products = array_map(function () { + return $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->getMock(); + }, $variantPrices); + + $this->lowestPriceOptionsProvider->expects($this->once())->method('getProducts')->willReturn($products); + $this->priceResolver ->method('resolvePrice') - ->with($product) - ->willReturn($price); + ->willReturnOnConsecutiveCalls(...$variantPrices); - $this->assertEquals($expectedValue, $this->resolver->resolvePrice($product)); + $actualPrice = $this->resolver->resolvePrice($product); + self::assertSame($expectedPrice, $actualPrice); } /** @@ -81,8 +88,18 @@ public function testResolvePrice($expectedValue) public function resolvePriceDataProvider() { return [ - 'price of zero' => [0.00], - 'price of five' => [5], + 'Single variant at price 0.00 (float), should return 0.00 (float)' => [ + $variantPrices = [ + 0.00, + ], + $expectedPrice = 0.00, + ], + 'Single variant at price 5 (integer), should return 5.00 (float)' => [ + $variantPrices = [ + 5, + ], + $expectedPrice = 5.00, + ], ]; } } From 8110f5fd293843746bba90d614b822d5a71df8de Mon Sep 17 00:00:00 2001 From: torreytsui <tt@amp.co> Date: Fri, 6 Jul 2018 13:48:36 +0100 Subject: [PATCH 0306/1171] Test price at 0 against resolvePrice() --- .../Price/ConfigurablePriceResolverTest.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php index 5909449202350..189730e18080c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php @@ -100,6 +100,28 @@ public function resolvePriceDataProvider() ], $expectedPrice = 5.00, ], + 'Single variants at price null (null), should return 0.00 (float)' => [ + $variantPrices = [ + null, + ], + $expectedPrice = 0.00, + ], + 'Multiple variants at price 0, 10, 20, should return 0.00 (float)' => [ + $variantPrices = [ + 0, + 10, + 20, + ], + $expectedPrice = 0.00, + ], + 'Multiple variants at price 10, 0, 20, should return 0.00 (float)' => [ + $variantPrices = [ + 10, + 0, + 20, + ], + $expectedPrice = 0.00, + ], ]; } } From 7791be802130bec5c88cfa3b3320556d56e12169 Mon Sep 17 00:00:00 2001 From: Ronak Patel <11473750+ronak2ram@users.noreply.github.com> Date: Sun, 15 Jul 2018 00:34:43 +0530 Subject: [PATCH 0307/1171] Add missing false-check to the ConfiguredRegularPrice price-model --- .../Magento/Catalog/Pricing/Price/ConfiguredRegularPrice.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Pricing/Price/ConfiguredRegularPrice.php b/app/code/Magento/Catalog/Pricing/Price/ConfiguredRegularPrice.php index bcb6638b9cd25..83d59718400bd 100644 --- a/app/code/Magento/Catalog/Pricing/Price/ConfiguredRegularPrice.php +++ b/app/code/Magento/Catalog/Pricing/Price/ConfiguredRegularPrice.php @@ -63,7 +63,7 @@ public function setItem(ItemInterface $item) : ConfiguredRegularPrice return $this; } - + /** * Price value of product with configured options. * @@ -73,7 +73,7 @@ public function getValue() { $basePrice = parent::getValue(); - return $this->item + return $this->item && $basePrice !== false ? $basePrice + $this->configuredOptions->getItemOptionsValue($basePrice, $this->item) : $basePrice; } From cdb17cded5e34a82ac11963e24545e8361833d03 Mon Sep 17 00:00:00 2001 From: PhoenixPM - BK <bjoern.kraus@phoenix-media.eu> Date: Tue, 26 Jun 2018 18:15:54 +0200 Subject: [PATCH 0308/1171] fixed type hints and docs for samples block --- .../Downloadable/Block/Catalog/Product/Samples.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Downloadable/Block/Catalog/Product/Samples.php b/app/code/Magento/Downloadable/Block/Catalog/Product/Samples.php index 120c7a38a8f41..4a03e975d8a4a 100644 --- a/app/code/Magento/Downloadable/Block/Catalog/Product/Samples.php +++ b/app/code/Magento/Downloadable/Block/Catalog/Product/Samples.php @@ -6,7 +6,8 @@ namespace Magento\Downloadable\Block\Catalog\Product; -use Magento\Downloadable\Model\ResourceModel\Sample; +use Magento\Downloadable\Model\ResourceModel\Sample\Collection as SampleCollection; +use Magento\Downloadable\Api\Data\SampleInterface; /** * Downloadable Product Samples part block @@ -29,7 +30,7 @@ public function hasSamples() /** * Get downloadable product samples * - * @return array + * @return SampleCollection */ public function getSamples() { @@ -37,10 +38,10 @@ public function getSamples() } /** - * @param Sample $sample + * @param SampleInterface $sample * @return string */ - public function getSampleUrl($sample) + public function getSampleUrl(SampleInterface $sample) { return $this->getUrl('downloadable/download/sample', ['sample_id' => $sample->getId()]); } From 9b8e91d6148708aede4c56ffbec1fda16cf99d4f Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Thu, 12 Jul 2018 15:44:31 +0300 Subject: [PATCH 0309/1171] Removed strict type due to BC --- app/code/Magento/Downloadable/Block/Catalog/Product/Samples.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Block/Catalog/Product/Samples.php b/app/code/Magento/Downloadable/Block/Catalog/Product/Samples.php index 4a03e975d8a4a..859d6a53d0618 100644 --- a/app/code/Magento/Downloadable/Block/Catalog/Product/Samples.php +++ b/app/code/Magento/Downloadable/Block/Catalog/Product/Samples.php @@ -41,7 +41,7 @@ public function getSamples() * @param SampleInterface $sample * @return string */ - public function getSampleUrl(SampleInterface $sample) + public function getSampleUrl($sample) { return $this->getUrl('downloadable/download/sample', ['sample_id' => $sample->getId()]); } From c0869b92844fad1d8121d8ab37817fcee8ff1e03 Mon Sep 17 00:00:00 2001 From: Sean Templeton <sean@templeton.io> Date: Tue, 3 Jul 2018 16:09:28 -0500 Subject: [PATCH 0310/1171] Fix responsive tables showing broken heading --- lib/web/css/source/lib/_tables.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/source/lib/_tables.less b/lib/web/css/source/lib/_tables.less index 9c37e17b4fe82..43b63152946f8 100644 --- a/lib/web/css/source/lib/_tables.less +++ b/lib/web/css/source/lib/_tables.less @@ -530,7 +530,7 @@ display: block; .lib-css(padding, @_table-responsive-cell-padding); - &:before { + &[data-th]:before { .lib-css(padding-right, @table-cell__padding-horizontal); content: attr(data-th)': '; display: inline-block; From 5905236f3b55987fc5ce24f3c72c7e8da77f69cb Mon Sep 17 00:00:00 2001 From: Valerij Ivashchenko <likemusic@yandex.ru> Date: Thu, 5 Jul 2018 18:26:42 +0300 Subject: [PATCH 0311/1171] smallest fix in Option/Type/Text.php --- app/code/Magento/Catalog/Model/Product/Option/Type/Text.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php b/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php index fd0eae188fea9..9ffe75e513bce 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php @@ -84,7 +84,7 @@ public function validateUserValue($values) */ public function prepareForCart() { - if ($this->getIsValid() && strlen($this->getUserValue()) > 0) { + if ($this->getIsValid() && ($this->getUserValue() !== '')) { return $this->getUserValue(); } else { return null; From 955fbf2b92cee5336489c0fdede4d862dee275fd Mon Sep 17 00:00:00 2001 From: Jeroen van Leusden <jeroen@h-o.nl> Date: Thu, 8 Feb 2018 17:07:49 +0100 Subject: [PATCH 0312/1171] Correctly save Product Custom Option values --- .../Catalog/Model/Product/Option/Value.php | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option/Value.php b/app/code/Magento/Catalog/Model/Product/Option/Value.php index fb7759b210bd9..55058afd2bce7 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Value.php @@ -202,27 +202,29 @@ public function getProduct() public function saveValues() { foreach ($this->getValues() as $value) { - $this->isDeleted(false); - $this->setData( + $optionValue = clone $this; + $optionValue->isDeleted(false); + + $optionValue->setData( $value )->setData( 'option_id', - $this->getOption()->getId() + $optionValue->getOption()->getId() )->setData( 'store_id', - $this->getOption()->getStoreId() + $optionValue->getOption()->getStoreId() ); - if ($this->getData('is_delete') == '1') { - if ($this->getId()) { - $this->deleteValues($this->getId()); - $this->delete(); + if ($optionValue->getData('is_delete') == '1') { + if ($optionValue->getId()) { + $optionValue->deleteValues($optionValue->getId()); + $optionValue->delete(); } } else { - $this->save(); + $optionValue->save(); } } - //eof foreach() + return $this; } From db95d8ab1dee5030a3d592f9ecdea117b6b34a1f Mon Sep 17 00:00:00 2001 From: Jeroen van Leusden <jeroen@reachdigital.nl> Date: Thu, 17 May 2018 16:47:00 +0200 Subject: [PATCH 0313/1171] Correctly save Product Custom Option values --- .../Magento/Catalog/Model/Product/Option.php | 28 +++++++++++----- .../Catalog/Model/Product/Option/Value.php | 32 ++++++++----------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option.php b/app/code/Magento/Catalog/Model/Product/Option.php index 39595cdaa60ad..acfc454883e1d 100644 --- a/app/code/Magento/Catalog/Model/Product/Option.php +++ b/app/code/Magento/Catalog/Model/Product/Option.php @@ -8,6 +8,7 @@ use Magento\Catalog\Api\Data\ProductCustomOptionInterface; use Magento\Catalog\Api\Data\ProductCustomOptionValuesInterface; +use Magento\Catalog\Api\Data\ProductCustomOptionValuesInterfaceFactory; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\ResourceModel\Product\Option\Value\Collection; @@ -102,6 +103,11 @@ class Option extends AbstractExtensibleModel implements ProductCustomOptionInter */ private $metadataPool; + /** + * @var ProductCustomOptionValuesInterfaceFactory + */ + private $customOptionValuesFactory; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -114,6 +120,7 @@ class Option extends AbstractExtensibleModel implements ProductCustomOptionInter * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param ProductCustomOptionValuesInterfaceFactory|null $customOptionValuesFactory * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -127,12 +134,16 @@ public function __construct( Option\Validator\Pool $validatorPool, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + ProductCustomOptionValuesInterfaceFactory $customOptionValuesFactory = null ) { $this->productOptionValue = $productOptionValue; $this->optionTypeFactory = $optionFactory; $this->validatorPool = $validatorPool; $this->string = $string; + $this->customOptionValuesFactory = $customOptionValuesFactory ?: + \Magento\Framework\App\ObjectManager::getInstance()->get(ProductCustomOptionValuesInterfaceFactory::class); + parent::__construct( $context, $registry, @@ -390,20 +401,21 @@ public function beforeSave() */ public function afterSave() { - $this->getValueInstance()->unsetValues(); $values = $this->getValues() ?: $this->getData('values'); if (is_array($values)) { foreach ($values as $value) { - if ($value instanceof \Magento\Catalog\Api\Data\ProductCustomOptionValuesInterface) { + if ($value instanceof ProductCustomOptionValuesInterface) { $data = $value->getData(); } else { $data = $value; } - $this->getValueInstance()->addValue($data); - } - $this->getValueInstance()->setOption($this)->saveValues(); - } elseif ($this->getGroupByType($this->getType()) == self::OPTION_GROUP_SELECT) { + $this->customOptionValuesFactory->create() + ->addValue($data) + ->setOption($this) + ->saveValues(); + } + } elseif ($this->getGroupByType($this->getType()) === self::OPTION_GROUP_SELECT) { throw new LocalizedException(__('Select type options required values rows.')); } @@ -804,7 +816,7 @@ public function setImageSizeY($imageSizeY) } /** - * @param \Magento\Catalog\Api\Data\ProductCustomOptionValuesInterface[] $values + * @param ProductCustomOptionValuesInterface[] $values * @return $this */ public function setValues(array $values = null) diff --git a/app/code/Magento/Catalog/Model/Product/Option/Value.php b/app/code/Magento/Catalog/Model/Product/Option/Value.php index 55058afd2bce7..ebbc060c99edf 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Value.php @@ -76,6 +76,7 @@ class Value extends AbstractModel implements \Magento\Catalog\Api\Data\ProductCu * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param CustomOptionPriceCalculator|null $customOptionPriceCalculator */ public function __construct( \Magento\Framework\Model\Context $context, @@ -89,6 +90,7 @@ public function __construct( $this->_valueCollectionFactory = $valueCollectionFactory; $this->customOptionPriceCalculator = $customOptionPriceCalculator ?? \Magento\Framework\App\ObjectManager::getInstance()->get(CustomOptionPriceCalculator::class); + parent::__construct( $context, $registry, @@ -201,27 +203,21 @@ public function getProduct() */ public function saveValues() { + $option = $this->getOption(); + foreach ($this->getValues() as $value) { - $optionValue = clone $this; - $optionValue->isDeleted(false); - - $optionValue->setData( - $value - )->setData( - 'option_id', - $optionValue->getOption()->getId() - )->setData( - 'store_id', - $optionValue->getOption()->getStoreId() - ); - - if ($optionValue->getData('is_delete') == '1') { - if ($optionValue->getId()) { - $optionValue->deleteValues($optionValue->getId()); - $optionValue->delete(); + $this->isDeleted(false); + $this->setData($value) + ->setData('option_id', $option->getId()) + ->setData('store_id', $option->getStoreId()); + + if ((bool) $this->getData('is_delete') === true) { + if ($this->getId()) { + $this->deleteValues($this->getId()); + $this->delete(); } } else { - $optionValue->save(); + $this->save(); } } From 7965492376f331c131823d413aae7f28fa623582 Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Mon, 16 Jul 2018 08:21:19 +0530 Subject: [PATCH 0314/1171] Fixed conflict issue --- app/code/Magento/Catalog/etc/frontend/di.xml | 29 +++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index 29be5b573654d..6861af135a2ac 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -79,18 +79,39 @@ <argument name="typeId" xsi:type="string">recently_compared_product</argument> </arguments> </virtualType> - <type name="Magento\Catalog\Block\Product\View\Gallery"> + <type name="Magento\Framework\View\Element\Message\MessageConfigurationsPool"> <arguments> - <argument name="galleryImagesConfig" xsi:type="array"> - <item name="addCompareAddSuccessMessage" xsi:type="array"> + <argument name="configurationsMap" xsi:type="array"> + <item name="addCompareSuccessMessage" xsi:type="array"> <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> <item name="data" xsi:type="array"> - <item name="template" xsi:type="string">Magento_Catalog::messages/addCompareAddSuccessMessage.phtml</item> + <item name="template" xsi:type="string">Magento_Catalog::messages/addCompareSuccessMessage.phtml</item> </item> </item> </argument> </arguments> </type> + <type name="Magento\Catalog\Block\Product\View\Gallery"> + <arguments> + <argument name="galleryImagesConfig" xsi:type="array"> + <item name="small_image" xsi:type="array"> + <item name="image_id" xsi:type="string">product_page_image_small</item> + <item name="data_object_key" xsi:type="string">small_image_url</item> + <item name="json_object_key" xsi:type="string">thumb</item> + </item> + <item name="medium_image" xsi:type="array"> + <item name="image_id" xsi:type="string">product_page_image_medium</item> + <item name="data_object_key" xsi:type="string">medium_image_url</item> + <item name="json_object_key" xsi:type="string">img</item> + </item> + <item name="large_image" xsi:type="array"> + <item name="image_id" xsi:type="string">product_page_image_large</item> + <item name="data_object_key" xsi:type="string">large_image_url</item> + <item name="json_object_key" xsi:type="string">full</item> + </item> + </argument> + </arguments> + </type> <type name="Magento\Framework\App\ResourceConnection"> <plugin name="get_catalog_category_product_index_table_name" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\TableResolver"/> </type> From b405dfaddc12ff42f90425de1f45530cced78327 Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Mon, 16 Jul 2018 13:47:25 +0530 Subject: [PATCH 0315/1171] Reverted changes --- app/code/Magento/Paypal/Block/Iframe.php | 4 ++-- app/code/Magento/Review/Block/Form.php | 2 +- app/code/Magento/Review/Block/Rating/Entity/Detailed.php | 2 +- app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php | 2 +- app/code/Magento/UrlRewrite/Block/Edit.php | 2 +- app/code/Magento/User/Block/Role/Tab/Users.php | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Paypal/Block/Iframe.php b/app/code/Magento/Paypal/Block/Iframe.php index 8ccba52b8e686..98fc05d0d2f60 100644 --- a/app/code/Magento/Paypal/Block/Iframe.php +++ b/app/code/Magento/Paypal/Block/Iframe.php @@ -116,7 +116,7 @@ protected function _construct() if ($file && $directory->isExist($directory->getRelativePath($file))) { $this->setTemplate($templateFile); } else { - $this->setTemplate('Magento_Paypal::hss/iframe.phtml'); + $this->setTemplate('hss/iframe.phtml'); } } } @@ -198,7 +198,7 @@ protected function _beforeToHtml() protected function _toHtml() { if ($this->_isAfterPaymentSave()) { - $this->setTemplate('Magento_Paypal::hss/js.phtml'); + $this->setTemplate('hss/js.phtml'); return parent::_toHtml(); } if (!$this->_shouldRender) { diff --git a/app/code/Magento/Review/Block/Form.php b/app/code/Magento/Review/Block/Form.php index c6dfad8265ac7..440e13deb5839 100644 --- a/app/code/Magento/Review/Block/Form.php +++ b/app/code/Magento/Review/Block/Form.php @@ -139,7 +139,7 @@ protected function _construct() ); } - $this->setTemplate('Magento_Review::form.phtml'); + $this->setTemplate('form.phtml'); } /** diff --git a/app/code/Magento/Review/Block/Rating/Entity/Detailed.php b/app/code/Magento/Review/Block/Rating/Entity/Detailed.php index eee00483ace66..0ce4f436ae704 100644 --- a/app/code/Magento/Review/Block/Rating/Entity/Detailed.php +++ b/app/code/Magento/Review/Block/Rating/Entity/Detailed.php @@ -49,7 +49,7 @@ protected function _toHtml() $reviewsCount = $this->_ratingFactory->create()->getTotalReviews($entityId, true); if ($reviewsCount == 0) { #return __('Be the first to review this product'); - $this->setTemplate('Magento_Review::empty.phtml'); + $this->setTemplate('empty.phtml'); return parent::_toHtml(); } diff --git a/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php b/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php index fb6c5e7b431ef..87f65ef311ac2 100644 --- a/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php +++ b/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php @@ -66,7 +66,7 @@ public function afterGetSectionData(\Magento\Checkout\CustomerData\Cart $subject foreach ($result['items'] as $key => $itemAsArray) { if ($item = $this->findItemById($itemAsArray['item_id'], $items)) { $this->itemPriceRenderer->setItem($item); - $this->itemPriceRenderer->setTemplate('Magento_Tax::checkout/cart/item/price/sidebar.phtml'); + $this->itemPriceRenderer->setTemplate('checkout/cart/item/price/sidebar.phtml'); $result['items'][$key]['product_price']=$this->itemPriceRenderer->toHtml(); } } diff --git a/app/code/Magento/UrlRewrite/Block/Edit.php b/app/code/Magento/UrlRewrite/Block/Edit.php index 210ed5189eb4c..baee8af893083 100644 --- a/app/code/Magento/UrlRewrite/Block/Edit.php +++ b/app/code/Magento/UrlRewrite/Block/Edit.php @@ -65,7 +65,7 @@ public function __construct( */ protected function _prepareLayout() { - $this->setTemplate('Magento_UrlRewrite::edit.phtml'); + $this->setTemplate('edit.phtml'); $this->_addBackButton(); $this->_prepareLayoutFeatures(); diff --git a/app/code/Magento/User/Block/Role/Tab/Users.php b/app/code/Magento/User/Block/Role/Tab/Users.php index a95a68cbe14f3..d93e7ad63e354 100644 --- a/app/code/Magento/User/Block/Role/Tab/Users.php +++ b/app/code/Magento/User/Block/Role/Tab/Users.php @@ -45,7 +45,7 @@ protected function _construct() $roleId = $this->getRequest()->getParam('rid', false); /** @var \Magento\User\Model\ResourceModel\User\Collection $users */ $users = $this->_userCollectionFactory->create()->load(); - $this->setTemplate('Magento_User::role/users.phtml') + $this->setTemplate('role/users.phtml') ->assign('users', $users->getItems()) ->assign('roleId', $roleId); } From 87a7a2cebb1e822baf0029978a781b2d9201375c Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Wed, 11 Jul 2018 18:40:39 +0530 Subject: [PATCH 0316/1171] Corrected return message from ProductRuleTest.php --- .../Magento/CatalogRule/Model/Indexer/ProductRuleTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php index 76d17527d567a..911c7aa30641e 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php @@ -67,7 +67,7 @@ public function testReindexWithProductNotVisibleIndividually() $this->assertEquals( 7.5, $this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $product->getId()), - "Catalog price rule doesn't apply to to product with visibility value \"Not Visibility Individually\"" + "Catalog price rule doesn't apply to product with visibility value \"Not Visibility Individually\"" ); } } From 2513062df78c50686622055283932fa932b28d52 Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Wed, 11 Jul 2018 18:47:14 +0530 Subject: [PATCH 0317/1171] Corrected return message from SequenceManager.php --- app/code/Magento/Integration/Test/Unit/Oauth/OauthTest.php | 2 +- .../Magento/CatalogRule/Model/Indexer/ProductRuleTest.php | 2 +- .../Framework/EntityManager/Sequence/SequenceManager.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Integration/Test/Unit/Oauth/OauthTest.php b/app/code/Magento/Integration/Test/Unit/Oauth/OauthTest.php index 7ca29e99d9ba9..7af2b3563478a 100644 --- a/app/code/Magento/Integration/Test/Unit/Oauth/OauthTest.php +++ b/app/code/Magento/Integration/Test/Unit/Oauth/OauthTest.php @@ -629,7 +629,7 @@ public function testGetAccessTokenVerifierInvalid($verifier, $verifierFromToken) public function dataProviderForGetAccessTokenVerifierInvalidTest() { // Verifier is not a string - return [[3, 3], ['wrong_length', 'wrong_length'], ['verifier', 'doesnt match']]; + return [[3, 3], ['wrong_length', 'wrong_length'], ['verifier', 'doesn\'t match']]; } public function testGetAccessToken() diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php index 911c7aa30641e..c7c93d1bcc40f 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php @@ -67,7 +67,7 @@ public function testReindexWithProductNotVisibleIndividually() $this->assertEquals( 7.5, $this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $product->getId()), - "Catalog price rule doesn't apply to product with visibility value \"Not Visibility Individually\"" + "Catalog price rule doesn't ap product with visibility value \"Not Visibility Individually\"" ); } } diff --git a/lib/internal/Magento/Framework/EntityManager/Sequence/SequenceManager.php b/lib/internal/Magento/Framework/EntityManager/Sequence/SequenceManager.php index 25af4743ac9b8..7d92c05d6c830 100644 --- a/lib/internal/Magento/Framework/EntityManager/Sequence/SequenceManager.php +++ b/lib/internal/Magento/Framework/EntityManager/Sequence/SequenceManager.php @@ -68,7 +68,7 @@ public function force($entityType, $identifier) if (!isset($sequenceInfo['sequenceTable'])) { throw new \Exception( - 'TODO: use correct Exception class' . PHP_EOL . ' Sequence table doesnt exists' + 'TODO: use correct Exception class' . PHP_EOL . ' Sequence table doesn\'t exists' ); } @@ -101,7 +101,7 @@ public function delete($entityType, $identifier) $metadata = $this->metadataPool->getMetadata($entityType); $sequenceInfo = $this->sequenceRegistry->retrieve($entityType); if (!isset($sequenceInfo['sequenceTable'])) { - throw new \Exception('TODO: use correct Exception class' . PHP_EOL . ' Sequence table doesnt exists'); + throw new \Exception('TODO: use correct Exception class' . PHP_EOL . ' Sequence table doesn\'t exists'); } try { $connection = $this->appResource->getConnectionByName($metadata->getEntityConnectionName()); From 2318a8818eff78fc94f2e641f5a6ea217a0c58fa Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Tue, 10 Jul 2018 18:04:14 +0530 Subject: [PATCH 0318/1171] Added 'title' attribute to 'a' link. --- .../Magento/Theme/view/frontend/templates/html/bugreport.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Theme/view/frontend/templates/html/bugreport.phtml b/app/code/Magento/Theme/view/frontend/templates/html/bugreport.phtml index 5d8203244256e..f147b7085945f 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/bugreport.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/bugreport.phtml @@ -7,7 +7,7 @@ <small class="bugs"> <span><?= /* @escapeNotVerified */ __('Help Us Keep Magento Healthy') ?></span> <a href="http://www.magentocommerce.com/bug-tracking" - target="_blank"> + target="_blank" title="<?= /* @escapeNotVerified */ __('Report All Bugs') ?>"> <?= /* @escapeNotVerified */ __('Report All Bugs') ?> </a> </small> From 9a5fb7ae0ff455b3731b22b4bb1a501ce38ec858 Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Tue, 10 Jul 2018 18:11:17 +0530 Subject: [PATCH 0319/1171] Added 'title' attribute to 'img' tag. --- .../Magento/Theme/view/frontend/templates/html/header/logo.phtml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml b/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml index 8c3663337cbbe..17f8d7c70f574 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml @@ -18,6 +18,7 @@ <a class="logo" href="<?= $block->getUrl('') ?>" title="<?= /* @escapeNotVerified */ $storeName ?>"> <?php endif ?> <img src="<?= /* @escapeNotVerified */ $block->getLogoSrc() ?>" + title="<?= /* @escapeNotVerified */ $block->getLogoAlt() ?>" alt="<?= /* @escapeNotVerified */ $block->getLogoAlt() ?>" <?= $block->getLogoWidth() ? 'width="' . $block->getLogoWidth() . '"' : '' ?> <?= $block->getLogoHeight() ? 'height="' . $block->getLogoHeight() . '"' : '' ?> From e144b305ba027db5ecb3f98e1138753ec0545f46 Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Tue, 10 Jul 2018 18:22:03 +0530 Subject: [PATCH 0320/1171] Added 'title' attribute to 'img' tag. --- .../Magento/Backend/view/adminhtml/templates/page/header.phtml | 2 +- .../view/adminhtml/templates/widget/form/element/gallery.phtml | 2 +- app/code/Magento/Ui/view/base/web/templates/block-loader.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Backend/view/adminhtml/templates/page/header.phtml b/app/code/Magento/Backend/view/adminhtml/templates/page/header.phtml index 8feccc9cf1b8f..f952001f5e2ff 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/page/header.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/page/header.phtml @@ -17,7 +17,7 @@ <?= /* @escapeNotVerified */ $edition ?> class="logo"> <img class="logo-img" src="<?= /* @escapeNotVerified */ $block->getViewFileUrl($logoSrc) ?>" - alt="<?= $block->escapeHtml(__('Magento Admin Panel')) ?>"/> + alt="<?= $block->escapeHtml(__('Magento Admin Panel')) ?>" title="<?= $block->escapeHtml(__('Magento Admin Panel')) ?>"/> </a> <?php break; ?> <?php case 'user': ?> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/widget/form/element/gallery.phtml b/app/code/Magento/Backend/view/adminhtml/templates/widget/form/element/gallery.phtml index 4f8d9a56a38a3..e11c0efc123ff 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/widget/form/element/gallery.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/widget/form/element/gallery.phtml @@ -34,7 +34,7 @@ <?php foreach ($block->getValues()->getAttributeBackend()->getImageTypes() as $type): ?> <td class="gallery" align="center" style="vertical-align:bottom;"> <a href="<?= /* @escapeNotVerified */ $image->setType($type)->getSourceUrl() ?>" target="_blank" onclick="imagePreview('<?= $block->getElement()->getHtmlId() ?>_image_<?= /* @escapeNotVerified */ $type ?>_<?= /* @escapeNotVerified */ $image->getValueId() ?>');return false;"> - <img id="<?= $block->getElement()->getHtmlId() ?>_image_<?= /* @escapeNotVerified */ $type ?>_<?= /* @escapeNotVerified */ $image->getValueId() ?>" src="<?= /* @escapeNotVerified */ $image->setType($type)->getSourceUrl() ?>?<?= /* @escapeNotVerified */ time() ?>" alt="<?= /* @escapeNotVerified */ $image->getValue() ?>" height="25" class="small-image-preview v-middle"/></a><br/> + <img id="<?= $block->getElement()->getHtmlId() ?>_image_<?= /* @escapeNotVerified */ $type ?>_<?= /* @escapeNotVerified */ $image->getValueId() ?>" src="<?= /* @escapeNotVerified */ $image->setType($type)->getSourceUrl() ?>?<?= /* @escapeNotVerified */ time() ?>" alt="<?= /* @escapeNotVerified */ $image->getValue() ?>" title="<?= /* @escapeNotVerified */ $image->getValue() ?>" height="25" class="small-image-preview v-middle"/></a><br/> <input type="file" name="<?= /* @escapeNotVerified */ $block->getElement()->getName() ?>_<?= /* @escapeNotVerified */ $type ?>[<?= /* @escapeNotVerified */ $image->getValueId() ?>]" size="1"></td> <?php endforeach; ?> <td class="gallery" align="center" style="vertical-align:bottom;"><input type="input" name="<?= /* @escapeNotVerified */ $block->getElement()->getParentName() ?>[position][<?= /* @escapeNotVerified */ $image->getValueId() ?>]" value="<?= /* @escapeNotVerified */ $image->getPosition() ?>" id="<?= $block->getElement()->getHtmlId() ?>_position_<?= /* @escapeNotVerified */ $image->getValueId() ?>" size="3"/></td> diff --git a/app/code/Magento/Ui/view/base/web/templates/block-loader.html b/app/code/Magento/Ui/view/base/web/templates/block-loader.html index 3d8b730a9e8c5..cb28b09e4da83 100644 --- a/app/code/Magento/Ui/view/base/web/templates/block-loader.html +++ b/app/code/Magento/Ui/view/base/web/templates/block-loader.html @@ -6,6 +6,6 @@ --> <div data-role="loader" class="loading-mask" style="position: absolute;"> <div class="loader"> - <img src="<%= loaderImageHref %>" alt="Loading..." style="position: absolute;"> + <img src="<%= loaderImageHref %>" alt="Loading..." title="Loading..." style="position: absolute;"> </div> </div> From b519270ced38296be5c44ce02b23020ecc1d4e35 Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Wed, 4 Jul 2018 16:49:05 +0530 Subject: [PATCH 0321/1171] Fixed widget template rendering issue while rewriting widget block. --- app/code/Magento/CatalogWidget/etc/widget.xml | 2 +- app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php | 6 +++--- .../testsuite/Magento/Cms/Setup/ContentConverterTest.php | 4 ++-- .../Magento/Widget/Setup/LayoutUpdateConverterTest.php | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/CatalogWidget/etc/widget.xml b/app/code/Magento/CatalogWidget/etc/widget.xml index bcc1b623da02e..1e6ed19057171 100644 --- a/app/code/Magento/CatalogWidget/etc/widget.xml +++ b/app/code/Magento/CatalogWidget/etc/widget.xml @@ -33,7 +33,7 @@ <parameter name="template" xsi:type="select" required="true" visible="true"> <label translate="true">Template</label> <options> - <option name="default" value="product/widget/content/grid.phtml" selected="true"> + <option name="default" value="Magento_CatalogWidget::product/widget/content/grid.phtml" selected="true"> <label translate="true">Products Grid Template</label> </option> </options> diff --git a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php index 0e1f722cd366a..2366da3b3dbfd 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php @@ -168,7 +168,7 @@ public function testGetWidgetDeclaration() 'show_pager' => '1', 'products_per_page' => '5', 'products_count' => '10', - 'template' => 'product/widget/content/grid.phtml', + 'template' => 'Magento_CatalogWidget::product/widget/content/grid.phtml', 'conditions' => $conditions ]; @@ -181,7 +181,7 @@ public function testGetWidgetDeclaration() ['1', false, '1'], ['5', false, '5'], ['10', false, '10'], - ['product/widget/content/grid.phtml', false, 'product/widget/content/grid.phtml'], + ['Magento_CatalogWidget::product/widget/content/grid.phtml', false, 'Magento_CatalogWidget::product/widget/content/grid.phtml'], ['encoded-conditions-string', false, 'encoded-conditions-string'], ]); @@ -226,7 +226,7 @@ public function testGetWidgetDeclarationWithZeroValueParam() 'show_pager' => '1', 'products_per_page' => '5', 'products_count' => '0', - 'template' => 'product/widget/content/grid.phtml', + 'template' => 'Magento_CatalogWidget::product/widget/content/grid.phtml', 'conditions' => $conditions ]; diff --git a/dev/tests/integration/testsuite/Magento/Cms/Setup/ContentConverterTest.php b/dev/tests/integration/testsuite/Magento/Cms/Setup/ContentConverterTest.php index b3bbd64b83e7e..7dbc3d2cc1dbd 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Setup/ContentConverterTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Setup/ContentConverterTest.php @@ -34,8 +34,8 @@ public function convertDataProvider() <h2 class="title">Hot Sellers</h2> <p class="info">Here is what`s trending on Luma right now</p> </div>'; - $serializedWidgetContent = '{{widget type="Magento\\CatalogWidget\\Block\\Product\\ProductsList" products_per_page="8" products_count="8" template="product/widget/content/grid.phtml" conditions_encoded="a:2:[i:1;a:4:[s:4:`type`;s:50:`Magento|CatalogWidget|Model|Rule|Condition|Combine`;s:10:`aggregator`;s:3:`all`;s:5:`value`;s:1:`1`;s:9:`new_child`;s:0:``;]s:4:`1--1`;a:4:[s:4:`type`;s:50:`Magento|CatalogWidget|Model|Rule|Condition|Product`;s:9:`attribute`;s:3:`sku`;s:8:`operator`;s:2:`()`;s:5:`value`;a:8:[i:0;s:4:`WS12`;i:1;s:4:`WT09`;i:2;s:4:`MT07`;i:3;s:4:`MH07`;i:4;s:7:`24-MB02`;i:5;s:7:`24-WB04`;i:6;s:8:`241-MB08`;i:7;s:8:`240-LV05`;]]]"}}'; - $jsonEncodedWidgetContent = '{{widget type="Magento\\CatalogWidget\\Block\\Product\\ProductsList" products_per_page="8" products_count="8" template="product/widget/content/grid.phtml" conditions_encoded="^[`1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Combine`,`aggregator`:`all`,`value`:`1`,`new_child`:``^],`1--1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Product`,`attribute`:`sku`,`operator`:`()`,`value`:[`WS12`,`WT09`,`MT07`,`MH07`,`24-MB02`,`24-WB04`,`241-MB08`,`240-LV05`]^]^]"}}'; + $serializedWidgetContent = '{{widget type="Magento\\CatalogWidget\\Block\\Product\\ProductsList" products_per_page="8" products_count="8" template="Magento_CatalogWidget::product/widget/content/grid.phtml" conditions_encoded="a:2:[i:1;a:4:[s:4:`type`;s:50:`Magento|CatalogWidget|Model|Rule|Condition|Combine`;s:10:`aggregator`;s:3:`all`;s:5:`value`;s:1:`1`;s:9:`new_child`;s:0:``;]s:4:`1--1`;a:4:[s:4:`type`;s:50:`Magento|CatalogWidget|Model|Rule|Condition|Product`;s:9:`attribute`;s:3:`sku`;s:8:`operator`;s:2:`()`;s:5:`value`;a:8:[i:0;s:4:`WS12`;i:1;s:4:`WT09`;i:2;s:4:`MT07`;i:3;s:4:`MH07`;i:4;s:7:`24-MB02`;i:5;s:7:`24-WB04`;i:6;s:8:`241-MB08`;i:7;s:8:`240-LV05`;]]]"}}'; + $jsonEncodedWidgetContent = '{{widget type="Magento\\CatalogWidget\\Block\\Product\\ProductsList" products_per_page="8" products_count="8" template="Magento_CatalogWidget::product/widget/content/grid.phtml" conditions_encoded="^[`1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Combine`,`aggregator`:`all`,`value`:`1`,`new_child`:``^],`1--1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Product`,`attribute`:`sku`,`operator`:`()`,`value`:[`WS12`,`WT09`,`MT07`,`MH07`,`24-MB02`,`24-WB04`,`241-MB08`,`240-LV05`]^]^]"}}'; // @codingStandardsIgnoreEnd return [ 'no widget' => [ diff --git a/dev/tests/integration/testsuite/Magento/Widget/Setup/LayoutUpdateConverterTest.php b/dev/tests/integration/testsuite/Magento/Widget/Setup/LayoutUpdateConverterTest.php index 083876d7eddb3..43c0bd202cc92 100644 --- a/dev/tests/integration/testsuite/Magento/Widget/Setup/LayoutUpdateConverterTest.php +++ b/dev/tests/integration/testsuite/Magento/Widget/Setup/LayoutUpdateConverterTest.php @@ -32,7 +32,7 @@ public function testConvert($value, $expected) public function convertDataProvider() { // @codingStandardsIgnoreStart - $beginning = '<body><referenceContainer name="content"><block class="Magento\CatalogWidget\Block\Product\ProductsList" name="23e38bbfa7cc6474454570e51aeffcc3" template="product/widget/content/grid.phtml"><action method="setData"><argument name="name" xsi:type="string">show_pager</argument><argument name="value" xsi:type="string">0</argument></action><action method="setData"><argument name="name" xsi:type="string">products_count</argument><argument name="value" xsi:type="string">10</argument></action><action method="setData">'; + $beginning = '<body><referenceContainer name="content"><block class="Magento\CatalogWidget\Block\Product\ProductsList" name="23e38bbfa7cc6474454570e51aeffcc3" template="Magento_CatalogWidget::product/widget/content/grid.phtml"><action method="setData"><argument name="name" xsi:type="string">show_pager</argument><argument name="value" xsi:type="string">0</argument></action><action method="setData"><argument name="name" xsi:type="string">products_count</argument><argument name="value" xsi:type="string">10</argument></action><action method="setData">'; $serializedWidgetXml = '<argument name="name" xsi:type="string">conditions_encoded</argument><argument name="value" xsi:type="string">a:3:[i:1;a:4:[s:4:`type`;s:50:`Magento|CatalogWidget|Model|Rule|Condition|Combine`;s:10:`aggregator`;s:3:`all`;s:5:`value`;s:1:`1`;s:9:`new_child`;s:0:``;]s:4:`1--1`;a:4:[s:4:`type`;s:50:`Magento|CatalogWidget|Model|Rule|Condition|Product`;s:9:`attribute`;s:3:`sku`;s:8:`operator`;s:2:`()`;s:5:`value`;s:15:`simple, simple1`;]s:4:`1--2`;a:4:[s:4:`type`;s:50:`Magento|CatalogWidget|Model|Rule|Condition|Product`;s:9:`attribute`;s:5:`price`;s:8:`operator`;s:2:`<=`;s:5:`value`;s:2:`10`;]]</argument>'; $jsonEncodedWidgetXml = '<argument name="name" xsi:type="string">conditions_encoded</argument><argument name="value" xsi:type="string">^[`1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Combine`,`aggregator`:`all`,`value`:`1`,`new_child`:``^],`1--1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Product`,`attribute`:`sku`,`operator`:`()`,`value`:`simple, simple1`^],`1--2`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Product`,`attribute`:`price`,`operator`:`<=`,`value`:`10`^]^]</argument>'; $ending = '</action><action method="setData"><argument name="name" xsi:type="string">page_var_name</argument><argument name="value" xsi:type="string">pobqks</argument></action></block></referenceContainer></body>'; From 9197c68bca1c6c166acca7d92eaf8c571c8de06d Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Wed, 4 Jul 2018 21:04:36 +0530 Subject: [PATCH 0322/1171] Update WidgetTest.php --- app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php index 2366da3b3dbfd..b85a458ed4121 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php @@ -181,7 +181,10 @@ public function testGetWidgetDeclaration() ['1', false, '1'], ['5', false, '5'], ['10', false, '10'], - ['Magento_CatalogWidget::product/widget/content/grid.phtml', false, 'Magento_CatalogWidget::product/widget/content/grid.phtml'], + ['Magento_CatalogWidget::product/widget/content/grid.phtml', + false, + 'Magento_CatalogWidget::product/widget/content/grid.phtml' + ], ['encoded-conditions-string', false, 'encoded-conditions-string'], ]); From 1637cf516b70a1568c3d7ffe9a286fe752f18779 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 16 Jul 2018 12:52:45 +0300 Subject: [PATCH 0323/1171] MAGETWO-88432: Filesystem Directory Improvement --- .../CatalogImportExport/Model/Import/Uploader.php | 7 ++++++- .../CatalogImportExport/Model/Import/UploaderTest.php | 10 +++++++++- .../Magento/Framework/Filesystem/Directory/Read.php | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php index 0939acabbd5fd..74249a0036177 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php @@ -177,7 +177,12 @@ public function move($fileName, $renameFileOff = false) ); } - $filePath = $this->_directory->getRelativePath($this->getTmpDir() . '/' . $fileName); + if ($this->getTmpDir()) { + $filePath = $this->getTmpDir() . '/'; + } else { + $filePath = ''; + } + $filePath = $this->_directory->getRelativePath($filePath . $fileName); $this->_setUploadFile($filePath); $destDir = $this->_directory->getAbsolutePath($this->getDestDir()); $result = $this->save($destDir); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php index cf5cb69ebdc27..f61aa7578d4a3 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php @@ -48,7 +48,14 @@ protected function setUp() $mediaPath = $appParams[DirectoryList::MEDIA][DirectoryList::PATH]; $this->directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); $tmpDir = $this->directory->getRelativePath($mediaPath . '/import'); - $this->uploader->setTmpDir($tmpDir); + if (!$this->directory->create($tmpDir)) { + throw new \RuntimeException('Failed to create temporary directory'); + } + if (!$this->uploader->setTmpDir($tmpDir)) { + throw new \RuntimeException( + 'Failed to set temporary directory for files.' + ); + } parent::setUp(); } @@ -70,6 +77,7 @@ public function testMoveWithValidFile(): void * @magentoAppIsolation enabled * @return void * @expectedException \Exception + * @expectedExceptionMessage Disallowed file type */ public function testMoveWithInvalidFile(): void { diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/Read.php b/lib/internal/Magento/Framework/Filesystem/Directory/Read.php index 5c8cde01d95af..a3a4cec59953f 100644 --- a/lib/internal/Magento/Framework/Filesystem/Directory/Read.php +++ b/lib/internal/Magento/Framework/Filesystem/Directory/Read.php @@ -67,7 +67,7 @@ public function __construct( * * @return void */ - protected final function validatePath( + protected function validatePath( ?string $path, ?string $scheme = null, bool $absolutePath = false From 612a9cf68a85c12d856bac4167286e73a03c9a44 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 16 Jul 2018 14:27:25 +0300 Subject: [PATCH 0324/1171] MAGETWO-93219: Incorrect tax presented on checkout page --- .../Model/Quote/ResetQuoteAddresses.php | 35 +++++++++ app/code/Magento/Checkout/etc/di.xml | 3 + .../Model/Quote/ResetQuoteAddressesTest.php | 71 +++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 app/code/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddresses.php create mode 100644 dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php diff --git a/app/code/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddresses.php b/app/code/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddresses.php new file mode 100644 index 0000000000000..3791b28917e97 --- /dev/null +++ b/app/code/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddresses.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Plugin\Model\Quote; + +use Magento\Quote\Model\Quote; + +/** + * Clear quote addresses after all items were removed. + */ +class ResetQuoteAddresses +{ + /** + * @param Quote $quote + * @param Quote $result + * @param mixed $itemId + * + * @return Quote + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterRemoveItem(Quote $quote, Quote $result, $itemId): Quote + { + if (empty($result->getAllVisibleItems())) { + foreach ($result->getAllAddresses() as $address) { + $result->removeAddress($address->getId()); + } + } + + return $result; + } +} diff --git a/app/code/Magento/Checkout/etc/di.xml b/app/code/Magento/Checkout/etc/di.xml index 4ebd594a28562..71dfd12bb4779 100644 --- a/app/code/Magento/Checkout/etc/di.xml +++ b/app/code/Magento/Checkout/etc/di.xml @@ -49,4 +49,7 @@ </argument> </arguments> </type> + <type name="Magento\Quote\Model\Quote"> + <plugin name="clear_addresses_after_product_delete" type="Magento\Checkout\Plugin\Model\Quote\ResetQuoteAddresses"/> + </type> </config> diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php new file mode 100644 index 0000000000000..e533dbfa0c879 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Plugin\Model\Quote; + +use Magento\Checkout\Model\Cart; +use Magento\Checkout\Model\Session; +use Magento\Quote\Model\BillingAddressManagement; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address as QuoteAddress; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Test for \Magento\Checkout\Plugin\Model\Quote\ResetQuoteAddresses + */ +class ResetQuoteAddressesTest extends \PHPUnit\Framework\TestCase +{ + /** + * @magentoDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php + * + * @return void + */ + public function testAfterRemoveItem(): void + { + /** @var Quote $quote */ + $quote = Bootstrap::getObjectManager()->create(Quote::class); + $quote->load('test_order_with_virtual_product', 'reserved_order_id'); + /** @var QuoteAddress $quoteShippingAddress */ + $quoteBillingAddress = Bootstrap::getObjectManager()->create(QuoteAddress::class); + $quoteBillingAddress->setRegion('CA') + ->setPostcode('90210') + ->setFirstname('a_unique_firstname') + ->setLastname('lastname') + ->setStreet('street') + ->setCity('Beverly Hills') + ->setEmail('admin@example.com') + ->setTelephone('1111111111') + ->setCountryId('US') + ->setAddressType('billing'); + + /** @var BillingAddressManagement $billingAddressManagement */ + $billingAddressManagement = Bootstrap::getObjectManager()->create(BillingAddressManagement::class); + $billingAddressManagement->assign($quote->getId(), $quoteBillingAddress); + /** @var Session $checkoutSession */ + $checkoutSession = Bootstrap::getObjectManager()->create(Session::class); + $checkoutSession->setQuoteId($quote->getId()); + /** @var Cart $cart */ + $cart = Bootstrap::getObjectManager()->create(Cart::class); + + $activeQuote = $cart->getQuote(); + $cart->removeItem($activeQuote->getAllVisibleItems()[0]->getId()); + $cart->save(); + + /** @var Quote $quote */ + $quote = Bootstrap::getObjectManager()->create(Quote::class); + $quote->load('test_order_with_virtual_product', 'reserved_order_id'); + $quoteBillingAddressUpdated = $quote->getBillingAddress(); + $customer = $quote->getCustomer(); + + $this->assertEquals($quoteBillingAddressUpdated->getEmail(), $customer->getEmail()); + $this->assertEmpty($quoteBillingAddressUpdated->getCountryId()); + $this->assertEmpty($quoteBillingAddressUpdated->getRegionId()); + $this->assertEmpty($quoteBillingAddressUpdated->getRegion()); + $this->assertEmpty($quoteBillingAddressUpdated->getPostcode()); + $this->assertEmpty($quoteBillingAddressUpdated->getCity()); + } +} From 4ce8ecb2a077ab30d2fa4203a83e98613bbf5a46 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 16 Jul 2018 15:20:13 +0300 Subject: [PATCH 0325/1171] MAGETWO-88432: Filesystem Directory Improvement --- .../Magento/CatalogImportExport/Model/Import/Uploader.php | 8 +++++++- .../Test/Unit/Model/Import/UploaderTest.php | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php index 74249a0036177..e7ffe408cc732 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php @@ -170,9 +170,15 @@ public function move($fileName, $renameFileOff = false) } } + if ($this->getTmpDir()) { + $filePath = $this->getTmpDir() . '/'; + } else { + $filePath = ''; + } $fileName = preg_replace('/[^a-z0-9\._-]+/i', '', $fileName); + $filePath = $this->_directory->getRelativePath($filePath . $fileName); $this->_directory->writeFile( - $this->_directory->getRelativePath($this->getTmpDir() . '/' . $fileName), + $filePath, $read->readAll() ); } diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/UploaderTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/UploaderTest.php index ed95d5a0212c9..262593377aa2c 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/UploaderTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/UploaderTest.php @@ -103,7 +103,7 @@ protected function setUp() public function testMoveFileUrl($fileUrl, $expectedHost, $expectedFileName) { $destDir = 'var/dest/dir'; - $expectedRelativeFilePath = $this->uploader->getTmpDir() . '/' . $expectedFileName; + $expectedRelativeFilePath = $expectedFileName; $this->directoryMock->expects($this->once())->method('isWritable')->with($destDir)->willReturn(true); $this->directoryMock->expects($this->any())->method('getRelativePath')->with($expectedRelativeFilePath); $this->directoryMock->expects($this->once())->method('getAbsolutePath')->with($destDir) @@ -139,7 +139,7 @@ public function testMoveFileName() { $destDir = 'var/dest/dir'; $fileName = 'test_uploader_file'; - $expectedRelativeFilePath = $this->uploader->getTmpDir() . '/' . $fileName; + $expectedRelativeFilePath = $fileName; $this->directoryMock->expects($this->once())->method('isWritable')->with($destDir)->willReturn(true); $this->directoryMock->expects($this->any())->method('getRelativePath')->with($expectedRelativeFilePath); $this->directoryMock->expects($this->once())->method('getAbsolutePath')->with($destDir) From 6154c6b5e216b6fa1ee652505737870da6fa5a14 Mon Sep 17 00:00:00 2001 From: Bohdan Shevchenko <fanta1408@gmail.com> Date: Mon, 16 Jul 2018 19:09:07 +0300 Subject: [PATCH 0326/1171] MAGETWO-93121: Automate with MFTF verification dynamic bundle product prices for combination of options --- .../Bundle/Test/Mftf/Data/BundleLinkData.xml | 4 +- .../Test/Mftf/Data/BundleOptionData.xml | 25 +- .../Test/Mftf/Data/CustomAttributeData.xml | 4 + .../Bundle/Test/Mftf/Data/ProductData.xml | 15 ++ .../Test/Mftf/Metadata/bundle_link-meta.xml | 2 +- .../StorefrontCategoryProductSection.xml | 15 ++ .../StorefrontProductInfoMainSection.xml | 17 ++ ...leProductPricesForCombinationOfOptions.xml | 237 ++++++++++++++++++ .../AddSpecialPriceToProductActionGroup.xml | 25 ++ .../Mftf/Section/AdminConfigureTaxSection.xml | 17 +- 10 files changed, 352 insertions(+), 9 deletions(-) create mode 100644 app/code/Magento/Bundle/Test/Mftf/Section/StorefrontCategoryProductSection.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductInfoMainSection.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/VerifyDynamicBundleProductPricesForCombinationOfOptions.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSpecialPriceToProductActionGroup.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Data/BundleLinkData.xml b/app/code/Magento/Bundle/Test/Mftf/Data/BundleLinkData.xml index 1cc0ce147ae0e..28fca883350e1 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Data/BundleLinkData.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Data/BundleLinkData.xml @@ -10,10 +10,10 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ApiBundleLink" type="bundle_link"> <var key="sku" entityKey="sku" entityType="product2"/> - <var key="option_id" entityKey="option_id" entityType="bundle_options"/> + <var key="option_id" entityKey="return" entityType="bundle_option"/> <var key="sku" entityKey="sku" entityType="product"/> <data key="qty">1</data> - <data key="is_default">1</data> + <data key="is_default">0</data> <data key="price">1.11</data> <data key="price_type">1</data> <data key="can_change_quantity">1</data> diff --git a/app/code/Magento/Bundle/Test/Mftf/Data/BundleOptionData.xml b/app/code/Magento/Bundle/Test/Mftf/Data/BundleOptionData.xml index 80d9307255e59..e10fe4e33c208 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Data/BundleOptionData.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Data/BundleOptionData.xml @@ -8,13 +8,34 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="DropdownBundleOption" type="bundle_option"> + <entity name="DropDownBundleOption" type="bundle_option"> <data key="title" unique="suffix">bundle-option-dropdown</data> <data key="required">true</data> - <data key="type">dropdown</data> + <data key="type">select</data> + <data key="position">0</data> + <var key="sku" entityKey="sku" entityType="product2"/> + </entity> + <entity name="RadioButtonsOption" type="bundle_option"> + <data key="title" unique="suffix">bundle-option-radio</data> + <data key="required">true</data> + <data key="type">radio</data> <data key="position">1</data> <var key="sku" entityKey="sku" entityType="product2"/> </entity> + <entity name="CheckboxOption" type="bundle_option"> + <data key="title" unique="suffix">bundle-option-checkbox</data> + <data key="required">true</data> + <data key="type">checkbox</data> + <data key="position">3</data> + <var key="sku" entityKey="sku" entityType="product2"/> + </entity> + <entity name="MultipleSelectOption" type="bundle_option"> + <data key="title" unique="suffix">bundle-option-multipleselect</data> + <data key="required">true</data> + <data key="type">multi</data> + <data key="position">4</data> + <var key="sku" entityKey="sku" entityType="product2"/> + </entity> <entity name="AllBundleOptions" type="bundle_options"> <var key="sku" entityKey="sku" entityType="product"/> </entity> diff --git a/app/code/Magento/Bundle/Test/Mftf/Data/CustomAttributeData.xml b/app/code/Magento/Bundle/Test/Mftf/Data/CustomAttributeData.xml index 65ac763460151..380b5b8959025 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Data/CustomAttributeData.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Data/CustomAttributeData.xml @@ -19,4 +19,8 @@ <data key="attribute_code">price_view</data> <data key="value">1</data> </entity> + <entity name="CustomAttributePriceViewRange" type="custom_attribute"> + <data key="attribute_code">price_view</data> + <data key="value">0</data> + </entity> </entities> diff --git a/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml index 80ffe13ff3fe4..9c48b904bb7fd 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml @@ -45,4 +45,19 @@ <requiredEntity type="custom_attribute">CustomAttributeDynamicPrice</requiredEntity> <requiredEntity type="custom_attribute">CustomAttributePriceView</requiredEntity> </entity> + <entity name="ApiBundleProductPriceViewRange" type="product2"> + <data key="name" unique="suffix">Api Bundle Product</data> + <data key="sku" unique="suffix">api-bundle-product</data> + <data key="type_id">bundle</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="urlKey" unique="suffix">api-bundle-product</data> + <requiredEntity type="custom_attribute">CustomAttributeCategoryIds</requiredEntity> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute">ApiProductDescription</requiredEntity> + <requiredEntity type="custom_attribute">ApiProductShortDescription</requiredEntity> + <requiredEntity type="custom_attribute">CustomAttributeDynamicPrice</requiredEntity> + <requiredEntity type="custom_attribute">CustomAttributePriceViewRange</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_link-meta.xml b/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_link-meta.xml index 435cf59c6cbfd..ca39253aa54a0 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_link-meta.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_link-meta.xml @@ -8,7 +8,7 @@ <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> - <operation name="CreateBundleLink" dataType="bundle_link" type="create" auth="adminOauth" url="/V1/bundle-products/{sku}/links/{option_id}" method="POST"> + <operation name="CreateBundleLink" dataType="bundle_link" type="create" auth="adminOauth" url="/V1/bundle-products/{sku}/links/{return}" method="POST"> <contentType>application/json</contentType> <object dataType="bundle_link" key="linkedProduct"> <field key="sku">string</field> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontCategoryProductSection.xml new file mode 100644 index 0000000000000..c76f822a0913f --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="StorefrontCategoryProductSection"> + <element name="priceToByProductId" type="text" selector="div[data-product-id='{{id}}'] .price-to" parameterized="true"/> + <element name="priceFromByProductId" type="text" selector="div[data-product-id='{{id}}'] .price-from" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductInfoMainSection.xml new file mode 100644 index 0000000000000..41c00b5eda184 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="StorefrontProductInfoMainSection"> + <element name="priceFrom" type="text" selector=".product-info-price .price-from"/> + <element name="priceTo" type="text" selector=".product-info-price .price-to"/> + <element name="minPrice" type="text" selector="span[data-price-type='minPrice']"/> + <element name="maxPrice" type="text" selector="span[data-price-type='minPrice']"/> + </section> +</sections> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/VerifyDynamicBundleProductPricesForCombinationOfOptions.xml b/app/code/Magento/Bundle/Test/Mftf/Test/VerifyDynamicBundleProductPricesForCombinationOfOptions.xml new file mode 100644 index 0000000000000..473f9a68ff53e --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/VerifyDynamicBundleProductPricesForCombinationOfOptions.xml @@ -0,0 +1,237 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="VerifyDynamicBundleProductPricesForCombinationOfOptions"> + <annotations> + <features value="Bundle"/> + <title value="Verify dynamic bundle product prices for combination of options"/> + <description value="Verify prices for various configurations of Dynamic Bundle product"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-43619"/> + <group value="Bundle"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="createSubCategory"/> + + <!--Create 5 simple product--> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">4.99</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"> + <field key="price">2.89</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct3"> + <field key="price">7.33</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct4"> + <field key="price">18.25</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct5"> + <field key="price">10.00</field> + </createData> + + <!--Add special price to simple product--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPrice"> + <argument name="product" value="$$simpleProduct5$$"/> + </actionGroup> + + <!--Create Bundle product--> + <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleProduct"> + <requiredEntity createDataKey="createSubCategory"/> + </createData> + <createData entity="MultipleSelectOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="required">false</field> + </createData> + <createData entity="CheckboxOption" stepKey="createBundleOption1_2"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct3"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_2"/> + <requiredEntity createDataKey="simpleProduct3"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct4"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_2"/> + <requiredEntity createDataKey="simpleProduct4"/> + </createData> + + <!--Create Bundle product 2--> + <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleProduct2"> + <requiredEntity createDataKey="createSubCategory"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="createBundleOption2_1"> + <requiredEntity createDataKey="createBundleProduct2"/> + </createData> + <createData entity="RadioButtonsOption" stepKey="createBundleOption2_2"> + <requiredEntity createDataKey="createBundleProduct2"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct5"> + <requiredEntity createDataKey="createBundleProduct2"/> + <requiredEntity createDataKey="createBundleOption2_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + <field key="qty">2</field> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct6"> + <requiredEntity createDataKey="createBundleProduct2"/> + <requiredEntity createDataKey="createBundleOption2_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct7"> + <requiredEntity createDataKey="createBundleProduct2"/> + <requiredEntity createDataKey="createBundleOption2_2"/> + <requiredEntity createDataKey="simpleProduct3"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct8"> + <requiredEntity createDataKey="createBundleProduct2"/> + <requiredEntity createDataKey="createBundleOption2_2"/> + <requiredEntity createDataKey="simpleProduct4"/> + <field key="qty">5</field> + </createData> + + <!--Create Bundle product 3--> + <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleProduct3"> + <requiredEntity createDataKey="createSubCategory"/> + </createData> + <createData entity="MultipleSelectOption" stepKey="createBundleOption3_1"> + <requiredEntity createDataKey="createBundleProduct3"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="createBundleOption3_2"> + <requiredEntity createDataKey="createBundleProduct3"/> + <field key="required">false</field> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct9"> + <requiredEntity createDataKey="createBundleProduct3"/> + <requiredEntity createDataKey="createBundleOption3_1"/> + <requiredEntity createDataKey="simpleProduct4"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct10"> + <requiredEntity createDataKey="createBundleProduct3"/> + <requiredEntity createDataKey="createBundleOption3_1"/> + <requiredEntity createDataKey="simpleProduct5"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct11"> + <requiredEntity createDataKey="createBundleProduct3"/> + <requiredEntity createDataKey="createBundleOption3_2"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct12"> + <requiredEntity createDataKey="createBundleProduct3"/> + <requiredEntity createDataKey="createBundleOption3_2"/> + <requiredEntity createDataKey="simpleProduct3"/> + </createData> + + <!-- navigate to the tax configuration page --> + <amOnPage url="{{AdminTaxConfigurationPage.url}}" stepKey="goToAdminTaxPage"/> + <waitForPageLoad stepKey="waitForTaxConfigLoad"/> + + <conditionalClick selector="{{AdminConfigureTaxSection.calculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.calculationSettings}}.open" visible="false" stepKey="openCalculationSettingsTab"/> + <conditionalClick selector="{{AdminConfigureTaxSection.calculationMethodBasedCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.calculationMethodBased}}[disabled='disabled']" visible="true" stepKey="clickCalculationMethodBasedCheckBox"/> + <selectOption userInput="Total" selector="{{AdminConfigureTaxSection.calculationMethodBased}}" stepKey="fillCalculationMethodBased"/> + + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationBasedCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationBased}}[disabled='disabled']" visible="true" stepKey="clickTaxCalculationBasedCheckBox"/> + <selectOption userInput="Shipping Origin" selector="{{AdminConfigureTaxSection.taxCalculationBased}}" stepKey="fillTaxCalculationBased"/> + + <conditionalClick selector="{{AdminConfigureTaxSection.calculationPricesCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.calculationPrices}}[disabled='disabled']" visible="true" stepKey="clickCalculationPricesCheckBox"/> + <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.calculationPrices}}" stepKey="clickCalculationPrices"/> + + <conditionalClick selector="{{AdminConfigureTaxSection.priceDisplaySettings}}" dependentSelector="{{AdminConfigureTaxSection.priceDisplaySettings}}.open" visible="false" stepKey="openPriceDisplaySettings"/> + <conditionalClick selector="{{AdminConfigureTaxSection.displayProductPricesCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.displayProductPrices}}[disabled='disabled']" visible="true" stepKey="clickDisplayProductPricesCheckBox"/> + <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.displayProductPrices}}" stepKey="clickDisplayProductPrices"/> + + <!-- Save the settings --> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveTaxOptions"/> + <waitForPageLoad stepKey="waitForTaxSaved"/> + <see userInput="You saved the configuration." selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccess"/> + + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + </before> + <after> + <!-- navigate to the tax configuration page --> + <amOnPage url="{{AdminTaxConfigurationPage.url}}" stepKey="goToAdminTaxPage"/> + <waitForPageLoad stepKey="waitForTaxConfigLoad"/> + + <conditionalClick selector="{{AdminConfigureTaxSection.calculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.calculationSettings}}.open" visible="false" stepKey="openCalculationSettingsTab"/> + <conditionalClick selector="{{AdminConfigureTaxSection.calculationMethodBasedCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.calculationMethodBased}}[disabled='disabled']" visible="true" stepKey="clickCalculationMethodBasedCheckBox"/> + <selectOption userInput="Total" selector="{{AdminConfigureTaxSection.calculationMethodBased}}" stepKey="fillCalculationMethodBased"/> + + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationBasedCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationBased}}[disabled='disabled']" visible="true" stepKey="clickTaxCalculationBasedCheckBox"/> + <selectOption userInput="Shipping Address" selector="{{AdminConfigureTaxSection.taxCalculationBased}}" stepKey="fillTaxCalculationBased"/> + + <conditionalClick selector="{{AdminConfigureTaxSection.calculationPricesCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.calculationPrices}}[disabled='disabled']" visible="true" stepKey="clickCalculationPricesCheckBox"/> + <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.calculationPrices}}" stepKey="clickCalculationPrices"/> + + <conditionalClick selector="{{AdminConfigureTaxSection.priceDisplaySettings}}" dependentSelector="{{AdminConfigureTaxSection.priceDisplaySettings}}.open" visible="false" stepKey="openPriceDisplaySettings"/> + <conditionalClick selector="{{AdminConfigureTaxSection.displayProductPricesCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.displayProductPrices}}[disabled='disabled']" visible="true" stepKey="clickDisplayProductPricesCheckBox"/> + <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.displayProductPrices}}" stepKey="clickDisplayProductPrices"/> + + <!-- Save the settings --> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveTaxOptions"/> + <waitForPageLoad stepKey="waitForTaxSaved"/> + <see userInput="You saved the configuration." selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccess"/> + + <actionGroup ref="logout" stepKey="logout"/> + <deleteData createDataKey="createSubCategory" stepKey="deleteSubCategory1"/> + + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="simpleProduct3" stepKey="deleteSimpleProduct3"/> + <deleteData createDataKey="simpleProduct4" stepKey="deleteSimpleProduct4"/> + <deleteData createDataKey="simpleProduct5" stepKey="deleteSimpleProduct5"/> + + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="createBundleProduct2" stepKey="deleteBundleProduct2"/> + <deleteData createDataKey="createBundleProduct3" stepKey="deleteBundleProduct3"/> + </after> + + <!-- Go to storefront category page --> + <amOnPage url="{{StorefrontCategoryPage.url($$createSubCategory.name$$)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + + <see userInput="From $7.33" selector="{{StorefrontCategoryProductSection.priceFromByProductId($$createBundleProduct.id$$)}}" stepKey="seePriceFromInCategoryBundle1"/> + <see userInput="To $33.46" selector="{{StorefrontCategoryProductSection.priceToByProductId($$createBundleProduct.id$$)}}" stepKey="seePriceToInCategoryBundle1"/> + + <see userInput="From $10.22" selector="{{StorefrontCategoryProductSection.priceFromByProductId($$createBundleProduct2.id$$)}}" stepKey="seePriceFromInCategoryBundle2"/> + <see userInput="To $101.23" selector="{{StorefrontCategoryProductSection.priceToByProductId($$createBundleProduct2.id$$)}}" stepKey="seePriceToInCategoryBundle2"/> + + <see userInput="From $8.00 Regular Price $10.00" selector="{{StorefrontCategoryProductSection.priceFromByProductId($$createBundleProduct3.id$$)}}" stepKey="seePriceFromInCategoryBundle3"/> + <see userInput="To $33.58 Regular Price $35.58" selector="{{StorefrontCategoryProductSection.priceToByProductId($$createBundleProduct3.id$$)}}" stepKey="seePriceToInCategoryBundle3"/> + + <!-- Go to storefront product pages --> + <amOnPage url="{{StorefrontProductPage.url($$createBundleProduct.custom_attributes[url_key]$$)}}" stepKey="onPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see userInput="From $7.33" selector="{{StorefrontProductInfoMainSection.priceFrom}}" stepKey="seePriceFromBundle1"/> + <see userInput="To $33.46" selector="{{StorefrontProductInfoMainSection.priceTo}}" stepKey="seePriceToBundle1"/> + + <amOnPage url="{{StorefrontProductPage.url($$createBundleProduct2.custom_attributes[url_key]$$)}}" stepKey="onPageBundle2"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <see userInput="From $10.22" selector="{{StorefrontProductInfoMainSection.priceFrom}}" stepKey="seePriceFromBundle2"/> + <see userInput="To $101.23" selector="{{StorefrontProductInfoMainSection.priceTo}}" stepKey="seePriceToBundle2"/> + + <amOnPage url="{{StorefrontProductPage.url($$createBundleProduct3.custom_attributes[url_key]$$)}}" stepKey="onPageBundle3"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <see userInput="From $8.00 Regular Price $10.00" selector="{{StorefrontProductInfoMainSection.priceFrom}}" stepKey="seePriceFromBundle3"/> + <see userInput="To $33.58 Regular Price $35.58" selector="{{StorefrontProductInfoMainSection.priceTo}}" stepKey="seePriceToBundle3"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSpecialPriceToProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSpecialPriceToProductActionGroup.xml new file mode 100644 index 0000000000000..c2c6bbeb6d017 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSpecialPriceToProductActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddSpecialPriceToProductActionGroup"> + <arguments> + <argument name="product"/> + <argument name="price" type="string" defaultValue="8"/> + </arguments> + <amOnPage url="{{AdminProductEditPage.url(product.id)}}" stepKey="openAdminEditPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> + <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitSpecialPrice"/> + <fillField userInput="{{price}}" selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="fillSpecialPrice"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDone"/> + <waitForElementNotVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitForCloseModalWindow"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product." stepKey="seeSaveConfirmation"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml index ad68179119834..ce979f925bc9b 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml @@ -11,14 +11,23 @@ <section name="AdminConfigureTaxSection"> <!-- on page /admin/admin/system_config/edit/section/tax/ --> <element name="taxClasses" type="block" selector="#tax_classes-head" timeout="30"/> - <element name="calculationSettings" type="block" selector="#tax_calculation-head:not(.open)" timeout="30"/> - + + <element name="calculationSettings" type="block" selector="#tax_calculation-head" timeout="30"/> + <element name="calculationMethodBased" type="select" selector="#tax_calculation_algorithm"/> + <element name="calculationMethodBasedCheckBox" type="input" selector="#tax_calculation_algorithm_inherit"/> + <element name="taxCalculationBased" type="select" selector="#tax_calculation_based_on"/> + <element name="taxCalculationBasedCheckBox" type="input" selector="#tax_calculation_based_on_inherit"/> + <element name="calculationPrices" type="select" selector="#tax_calculation_price_includes_tax"/> + <element name="calculationPricesCheckBox" type="input" selector="#tax_calculation_price_includes_tax_inherit"/> + <element name="defaultDestination" type="block" selector="#tax_defaults-head" timeout="30"/> <element name="systemValueDefaultState" type="checkbox" selector="#row_tax_defaults_region input[type='checkbox']"/> <element name="dropdownDefaultState" type="select" selector="#row_tax_defaults_region select"/> <element name="defaultPostCode" type="input" selector="#tax_defaults_postcode"/> - <element name="priceDisplaySettings" type="block" selector="#tax_display-head:not(.open)" timeout="30"/> + <element name="priceDisplaySettings" type="block" selector="#tax_display-head" timeout="30"/> + <element name="displayProductPrices" type="select" selector="#tax_display_type"/> + <element name="displayProductPricesCheckBox" type="input" selector="#tax_display_type_inherit"/> <element name="shoppingCartDisplay" type="block" selector="#tax_cart_display-head" timeout="30"/> <element name="systemValueIncludeTaxTotalCart" type="checkbox" selector="#row_tax_cart_display_grandtotal input[type='checkbox']"/> @@ -36,6 +45,6 @@ <element name="systemValueDisplayZeroTaxSales" type="checkbox" selector="#row_tax_sales_display_zero_tax input[type='checkbox']"/> <element name="dropdownDisplayZeroTaxSales" type="checkbox" selector="#row_tax_sales_display_zero_tax select"/> - <element name="priceDisplaySettings" type="block" selector="#tax_weee-head" timeout="30"/> + <element name="fixedProductSettings" type="block" selector="#tax_weee-head" timeout="30"/> </section> </sections> From 8f440ffebb3ba3cce7adfd79370a0283175f47cc Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Mon, 16 Jul 2018 11:43:42 -0500 Subject: [PATCH 0327/1171] ENGCOM-2197: [Jenkins] Failure to generate MFTF Tests --- .../Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index 26139f7182bab..2ff3d7756fe36 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -91,7 +91,8 @@ <!-- Search for Order in the order grid --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> + <waitForPageLoad time="30" stepKey="waitForOrderListPageLoad"/> + <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilter"/> <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNum"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> From 1cb458cc4dace3ef7df3ededff7320e727571590 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Mon, 16 Jul 2018 14:10:26 -0500 Subject: [PATCH 0328/1171] MC-3080: Admin can configure swatches display in configuration - Do not assume that colors have been configured for the color attribute --- .../AdminCreateProductAttributeSection.xml | 1 + .../ActionGroup/ColorPickerActionGroup.xml | 7 +++++ .../Mftf/Section/AdminManageSwatchSection.xml | 3 ++ .../Mftf/Test/AdminConfigureSwatchesTest.xml | 31 ++++++++++++++++--- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index be0e50ba5a3a5..42437f34b980a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -16,6 +16,7 @@ <element name="DefaultValue" type="input" selector="#default_value_text"/> <element name="Save" type="button" selector="#save"/> <element name="SaveAndEdit" type="button" selector="#save_and_edit_button" timeout="30"/> + <element name="successMessage" type="block" selector="div.message.message-success.success"/> <element name="TinyMCE4" type="button" selector="//span[text()='Default Value']/parent::label/following-sibling::div//div[@class='mce-branding-powered-by']"/> <element name="checkIfTabOpen" selector="//div[@id='advanced_fieldset-wrapper' and not(contains(@class,'opened'))]" type="button"/> </section> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml index cb52038d652ee..57bf3452ab131 100644 --- a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml @@ -28,4 +28,11 @@ <expectedResult type="string">{{expectedStyle}}</expectedResult> </assertEquals> </actionGroup> + <actionGroup name="openSwatchMenuByIndex"> + <arguments> + <argument name="index" type="string" defaultValue="0"/> + </arguments> + <!-- I had to use executeJS to perform the click to get around the use of CSS ::before and ::after --> + <executeJS function="jQuery('#swatch_window_option_option_{{index}}').click()" stepKey="clickSwatch1"/> + </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml index 58bdbfee3d7ff..35f8faf424be4 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml @@ -9,7 +9,10 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminManageSwatchSection"> + <element name="adminInputByIndex" type="input" selector="optionvisual[value][option_{{var}}][0]" parameterized="true"/> + <element name="addSwatch" type="button" selector="#add_new_swatch_visual_option_button" timeout="30"/> <element name="nthSwatch" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_window" parameterized="true"/> <element name="nthChooseColor" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_row_name.colorpicker_handler" parameterized="true"/> + <element name="nthDelete" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) button.delete-option" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml index 03a24f2f2400f..54004992fda84 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml @@ -22,6 +22,11 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> + <click selector="{{AdminManageSwatchSection.nthDelete('1')}}" stepKey="deleteSwatch1"/> + <click selector="{{AdminManageSwatchSection.nthDelete('2')}}" stepKey="deleteSwatch2"/> + <click selector="{{AdminManageSwatchSection.nthDelete('3')}}" stepKey="deleteSwatch3"/> + <waitForPageLoad stepKey="waitToClickSave2"/> + <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit2"/> <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> </after> @@ -35,28 +40,44 @@ <!-- Change to visual swatches --> <selectOption selector="{{AdminNewAttributePanel.inputType}}" userInput="swatch_visual" stepKey="selectVisualSwatch"/> - <!-- Set three swatches using the color picker --> - <click selector="{{AdminManageSwatchSection.nthSwatch('1')}}" stepKey="clickSwatch1"/> + <!-- Set swatch #1 using the color picker --> + <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch1"/> + <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch1"> + <argument name="index" value="0"/> + </actionGroup> <click selector="{{AdminManageSwatchSection.nthChooseColor('1')}}" stepKey="clickChooseColor1"/> <actionGroup ref="setColorPickerByHex" stepKey="fillHex1"> <argument name="nthColorPicker" value="1"/> <argument name="hexColor" value="e74c3c"/> </actionGroup> - <click selector="{{AdminManageSwatchSection.nthSwatch('2')}}" stepKey="clickSwatch2"/> + <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('0')}}" userInput="red" stepKey="fillAdmin1"/> + <!-- Set swatch #2 using the color picker --> + <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch2"/> + <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch2"> + <argument name="index" value="1"/> + </actionGroup> <click selector="{{AdminManageSwatchSection.nthChooseColor('2')}}" stepKey="clickChooseColor2"/> <actionGroup ref="setColorPickerByHex" stepKey="fillHex2"> <argument name="nthColorPicker" value="2"/> <argument name="hexColor" value="2ecc71"/> </actionGroup> - <click selector="{{AdminManageSwatchSection.nthSwatch('3')}}" stepKey="clickSwatch3"/> + <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('1')}}" userInput="green" stepKey="fillAdmin2"/> + <!-- Set swatch #3 using the color picker --> + <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch3"/> + <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch3"> + <argument name="index" value="2"/> + </actionGroup> <click selector="{{AdminManageSwatchSection.nthChooseColor('3')}}" stepKey="clickChooseColor3"/> <actionGroup ref="setColorPickerByHex" stepKey="fillHex3"> <argument name="nthColorPicker" value="3"/> <argument name="hexColor" value="3498db"/> </actionGroup> + <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('2')}}" userInput="blue" stepKey="fillAdmin3"/> + <waitForPageLoad stepKey="waitToClickSave"/> <!-- Save --> - <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit"/> + <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit1"/> + <waitForElementVisible selector="{{AttributePropertiesSection.successMessage}}" stepKey="waitForSuccess"/> <!-- Assert that the Save was successful after round trip to server --> <actionGroup ref="assertSwatchColor" stepKey="assertSwatch1"> From 267ae5b145e479910edbf8a2edced22eb14a7684 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Tue, 17 Jul 2018 08:28:16 +0300 Subject: [PATCH 0329/1171] MAGETWO-93184: [Forwardport] Static Assets deployment throws errors when redis is used for cache --- .../Framework/App/Cache/Frontend/Factory.php | 20 +++---- .../Test/Unit/Cache/Frontend/FactoryTest.php | 2 +- .../Framework/Cache/Frontend/Adapter/Zend.php | 53 +++++++++++++++---- .../Test/Unit/Frontend/Adapter/ZendTest.php | 16 ++++-- .../Unit/Frontend/Decorator/ProfilerTest.php | 5 +- 5 files changed, 72 insertions(+), 24 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Cache/Frontend/Factory.php b/lib/internal/Magento/Framework/App/Cache/Frontend/Factory.php index e8eca51aad976..f2bc4eceb507d 100644 --- a/lib/internal/Magento/Framework/App/Cache/Frontend/Factory.php +++ b/lib/internal/Magento/Framework/App/Cache/Frontend/Factory.php @@ -147,15 +147,17 @@ public function create(array $options) $result = $this->_objectManager->create( \Magento\Framework\Cache\Frontend\Adapter\Zend::class, [ - 'frontend' => \Zend_Cache::factory( - $frontend['type'], - $backend['type'], - $frontend, - $backend['options'], - true, - true, - true - ) + 'frontendFactory' => function () use ($frontend, $backend) { + return \Zend_Cache::factory( + $frontend['type'], + $backend['type'], + $frontend, + $backend['options'], + true, + true, + true + ); + } ] ); $result = $this->_applyDecorators($result); diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Frontend/FactoryTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Frontend/FactoryTest.php index e87eca57c058d..48a8420cfda60 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Frontend/FactoryTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Frontend/FactoryTest.php @@ -128,7 +128,7 @@ protected function _buildModelForCreate($enforcedOptions = [], $decorators = []) $processFrontendFunc = function ($class, $params) { switch ($class) { case \Magento\Framework\Cache\Frontend\Adapter\Zend::class: - return new $class($params['frontend']); + return new $class($params['frontendFactory']); case \Magento\Framework\App\Test\Unit\Cache\Frontend\FactoryTest\CacheDecoratorDummy::class: $frontend = $params['frontend']; unset($params['frontend']); diff --git a/lib/internal/Magento/Framework/Cache/Frontend/Adapter/Zend.php b/lib/internal/Magento/Framework/Cache/Frontend/Adapter/Zend.php index c8917a099689b..43d261c1ed078 100644 --- a/lib/internal/Magento/Framework/Cache/Frontend/Adapter/Zend.php +++ b/lib/internal/Magento/Framework/Cache/Frontend/Adapter/Zend.php @@ -16,11 +16,27 @@ class Zend implements \Magento\Framework\Cache\FrontendInterface protected $_frontend; /** - * @param \Zend_Cache_Core $frontend + * Factory that creates the \Zend_Cache_Cores + * + * @var \Closure + */ + private $frontendFactory; + + /** + * The pid that owns the $_frontend object + * + * @var int */ - public function __construct(\Zend_Cache_Core $frontend) + private $pid; + + /** + * @param \Closure $frontendFactory + */ + public function __construct(\Closure $frontendFactory) { - $this->_frontend = $frontend; + $this->frontendFactory = $frontendFactory; + $this->_frontend = $frontendFactory(); + $this->pid = getmypid(); } /** @@ -28,7 +44,7 @@ public function __construct(\Zend_Cache_Core $frontend) */ public function test($identifier) { - return $this->_frontend->test($this->_unifyId($identifier)); + return $this->getFrontEnd()->test($this->_unifyId($identifier)); } /** @@ -36,7 +52,7 @@ public function test($identifier) */ public function load($identifier) { - return $this->_frontend->load($this->_unifyId($identifier)); + return $this->getFrontEnd()->load($this->_unifyId($identifier)); } /** @@ -44,7 +60,7 @@ public function load($identifier) */ public function save($data, $identifier, array $tags = [], $lifeTime = null) { - return $this->_frontend->save($data, $this->_unifyId($identifier), $this->_unifyIds($tags), $lifeTime); + return $this->getFrontEnd()->save($data, $this->_unifyId($identifier), $this->_unifyIds($tags), $lifeTime); } /** @@ -52,13 +68,14 @@ public function save($data, $identifier, array $tags = [], $lifeTime = null) */ public function remove($identifier) { - return $this->_frontend->remove($this->_unifyId($identifier)); + return $this->getFrontEnd()->remove($this->_unifyId($identifier)); } /** * {@inheritdoc} * * @throws \InvalidArgumentException Exception is thrown when non-supported cleaning mode is specified + * @throws \Zend_Cache_Exception */ public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, array $tags = []) { @@ -76,7 +93,7 @@ public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, array $tags = []) "Magento cache frontend does not support the cleaning mode '{$mode}'." ); } - return $this->_frontend->clean($mode, $this->_unifyIds($tags)); + return $this->getFrontEnd()->clean($mode, $this->_unifyIds($tags)); } /** @@ -84,7 +101,7 @@ public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, array $tags = []) */ public function getBackend() { - return $this->_frontend->getBackend(); + return $this->getFrontEnd()->getBackend(); } /** @@ -92,7 +109,7 @@ public function getBackend() */ public function getLowLevelFrontend() { - return $this->_frontend; + return $this->getFrontEnd(); } /** @@ -119,4 +136,20 @@ protected function _unifyIds(array $ids) } return $ids; } + + /** + * Get frontEnd cache adapter for current pid + * + * @return \Zend_Cache_Core + */ + private function getFrontEnd() + { + if (getmypid() === $this->pid) { + return $this->_frontend; + } + $frontendFactory = $this->frontendFactory; + $this->_frontend = $frontendFactory(); + $this->pid = getmypid(); + return $this->_frontend; + } } diff --git a/lib/internal/Magento/Framework/Cache/Test/Unit/Frontend/Adapter/ZendTest.php b/lib/internal/Magento/Framework/Cache/Test/Unit/Frontend/Adapter/ZendTest.php index 4ed199f9c62fc..fb646c7f87622 100644 --- a/lib/internal/Magento/Framework/Cache/Test/Unit/Frontend/Adapter/ZendTest.php +++ b/lib/internal/Magento/Framework/Cache/Test/Unit/Frontend/Adapter/ZendTest.php @@ -17,7 +17,10 @@ class ZendTest extends \PHPUnit\Framework\TestCase public function testProxyMethod($method, $params, $expectedParams, $expectedResult) { $frontendMock = $this->createMock(\Zend_Cache_Core::class); - $object = new \Magento\Framework\Cache\Frontend\Adapter\Zend($frontendMock); + $frontendFactory = function () use ($frontendMock) { + return $frontendMock; + }; + $object = new \Magento\Framework\Cache\Frontend\Adapter\Zend($frontendFactory); $helper = new \Magento\Framework\TestFramework\Unit\Helper\ProxyTesting(); $result = $helper->invokeWithExpectations( $object, @@ -82,7 +85,11 @@ public function testCleanException($cleaningMode, $expectedErrorMessage) { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessage($expectedErrorMessage); - $object = new \Magento\Framework\Cache\Frontend\Adapter\Zend($this->createMock(\Zend_Cache_Core::class)); + $frontendMock = $this->createMock(\Zend_Cache_Core::class); + $frontendFactory = function () use ($frontendMock) { + return $frontendMock; + }; + $object = new \Magento\Framework\Cache\Frontend\Adapter\Zend($frontendFactory); $object->clean($cleaningMode); } @@ -110,7 +117,10 @@ public function cleanExceptionDataProvider() public function testGetLowLevelFrontend() { $frontendMock = $this->createMock(\Zend_Cache_Core::class); - $object = new \Magento\Framework\Cache\Frontend\Adapter\Zend($frontendMock); + $frontendFactory = function () use ($frontendMock) { + return $frontendMock; + }; + $object = new \Magento\Framework\Cache\Frontend\Adapter\Zend($frontendFactory); $this->assertSame($frontendMock, $object->getLowLevelFrontend()); } } diff --git a/lib/internal/Magento/Framework/Cache/Test/Unit/Frontend/Decorator/ProfilerTest.php b/lib/internal/Magento/Framework/Cache/Test/Unit/Frontend/Decorator/ProfilerTest.php index bef4617add1b9..34e50900bc64e 100644 --- a/lib/internal/Magento/Framework/Cache/Test/Unit/Frontend/Decorator/ProfilerTest.php +++ b/lib/internal/Magento/Framework/Cache/Test/Unit/Frontend/Decorator/ProfilerTest.php @@ -63,7 +63,10 @@ public function proxyMethodDataProvider() { $backend = new \Zend_Cache_Backend_BlackHole(); $adaptee = $this->createMock(\Zend_Cache_Core::class); - $lowLevelFrontend = new \Magento\Framework\Cache\Frontend\Adapter\Zend($adaptee); + $frontendFactory = function () use ($adaptee) { + return $adaptee; + }; + $lowLevelFrontend = new \Magento\Framework\Cache\Frontend\Adapter\Zend($frontendFactory); return [ [ From 3d7eb4853d13ed51b0303bf6b2ce4960e2269266 Mon Sep 17 00:00:00 2001 From: Pratik <ronak2ram@gmail.com> Date: Tue, 17 Jul 2018 13:52:03 +0530 Subject: [PATCH 0330/1171] Remove commented code & remove space --- .../Backend/Block/Widget/Grid/Column/Filter/Datetime.php | 1 - .../Block/Widget/Grid/Massaction/AbstractMassaction.php | 4 ++-- .../Magento/Backend/Block/Widget/Grid/Massaction/Extended.php | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Filter/Datetime.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Filter/Datetime.php index 88d1560026cbd..1d8d658267020 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Filter/Datetime.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Filter/Datetime.php @@ -26,7 +26,6 @@ public function getValue($index = null) { if ($index) { if ($data = $this->getData('value', 'orig_' . $index)) { - // date('Y-m-d', strtotime($data)); return $data; } return null; 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 3fa60faad45c8..185b1116b8f67 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php @@ -277,13 +277,13 @@ public function getGridIdsJson() } /** @var \Magento\Framework\Data\Collection $allIdsCollection */ $allIdsCollection = clone $this->getParentBlock()->getCollection(); - + if ($this->getMassactionIdField()) { $massActionIdField = $this->getMassactionIdField(); } else { $massActionIdField = $this->getParentBlock()->getMassactionIdField(); } - + $gridIds = $allIdsCollection->setPageSize(0)->getColumnValues($massActionIdField); if (!empty($gridIds)) { return join(",", $gridIds); diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/Extended.php b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/Extended.php index d59d3858179e0..8e0fce2b16cc9 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/Extended.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/Extended.php @@ -274,13 +274,13 @@ public function getGridIdsJson() /** @var \Magento\Framework\Data\Collection $allIdsCollection */ $allIdsCollection = clone $this->getParentBlock()->getCollection(); - + if ($this->getMassactionIdField()) { $massActionIdField = $this->getMassactionIdField(); } else { $massActionIdField = $this->getParentBlock()->getMassactionIdField(); } - + $gridIds = $allIdsCollection->setPageSize(0)->getColumnValues($massActionIdField); if (!empty($gridIds)) { From 92304248e53a33e27d60e413b2b4cf7c1aef0144 Mon Sep 17 00:00:00 2001 From: mageprince <7pprince@gmail.com> Date: Tue, 17 Jul 2018 14:22:43 +0530 Subject: [PATCH 0331/1171] Update Tax.php --- app/code/Magento/Tax/Block/Sales/Order/Tax.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Tax/Block/Sales/Order/Tax.php b/app/code/Magento/Tax/Block/Sales/Order/Tax.php index 71eb629111344..94e5b972db121 100644 --- a/app/code/Magento/Tax/Block/Sales/Order/Tax.php +++ b/app/code/Magento/Tax/Block/Sales/Order/Tax.php @@ -260,16 +260,11 @@ protected function _initShipping() } /** - * @return void + * @return null */ protected function _initDiscount() { - // $store = $this->getStore(); - // $parent = $this->getParentBlock(); - // if ($this->_config->displaySales) { - // - // } elseif ($this->_config->displaySales) { - // } + return null; } /** From abfad1ee890a5f673f5af010ff9e1dc3b3a1b94c Mon Sep 17 00:00:00 2001 From: Bohdan Shevchenko <fanta1408@gmail.com> Date: Tue, 17 Jul 2018 12:40:37 +0300 Subject: [PATCH 0332/1171] MAGETWO-93121: Automate with MFTF verification dynamic bundle product prices for combination of options --- .../Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml | 7 +++++++ .../Sales/Test/Mftf/Section/AdminOrdersGridSection.xml | 1 + 2 files changed, 8 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml index 7dcb89fdc1628..b74dcccf37c76 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml @@ -68,4 +68,11 @@ <selectOption selector="{{AdminDataGridHeaderSection.filterFieldSelect('status')}}" userInput="{{status}}" stepKey="fillOrderStatusFilter"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> </actionGroup> + + <actionGroup name="AdminGridOrdersClearFiltersActionGroup"> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="GoToGridOrdersPage"/> + <waitForPageLoad stepKey="WaitForPageToLoad"/> + <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.enabledFilters}}" visible="true" stepKey="ClickOnButtonToRemoveFiltersIfPresent"/> + <waitForPageLoad stepKey="WaitForPageToLoad2"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml index aa744787b0fef..54ed3de2d6851 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml @@ -17,6 +17,7 @@ <element name="filters" type="button" selector="button[data-action='grid-filter-expand']" timeout="30"/> <element name="idFilter" type="input" selector=".admin__data-grid-filters input[name='increment_id']"/> <element name="billToNameFilter" type="input" selector=".admin__data-grid-filters input[name='billing_name']"/> + <element name="enabledFilters" type="textarea" selector=".admin__data-grid-header .admin__data-grid-filters-current._show"/> <element name="clearFilters" type="button" selector=".admin__data-grid-header [data-action='grid-filter-reset']" timeout="30"/> <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> <element name="rowViewAction" type="button" selector=".data-grid tbody > tr:nth-of-type({{row}}) .action-menu-item" parameterized="true" timeout="30"/> From 93c0bb7eff2dde0debc83cb02d24c104d886d8a6 Mon Sep 17 00:00:00 2001 From: mageprince <7pprince@gmail.com> Date: Tue, 17 Jul 2018 15:12:18 +0530 Subject: [PATCH 0333/1171] Update Tax.php --- app/code/Magento/Tax/Block/Sales/Order/Tax.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Tax/Block/Sales/Order/Tax.php b/app/code/Magento/Tax/Block/Sales/Order/Tax.php index 94e5b972db121..2ea0bcddea14e 100644 --- a/app/code/Magento/Tax/Block/Sales/Order/Tax.php +++ b/app/code/Magento/Tax/Block/Sales/Order/Tax.php @@ -260,11 +260,11 @@ protected function _initShipping() } /** - * @return null + * @return void */ protected function _initDiscount() { - return null; + return; } /** From 5da977d23a1e8b2ff9cd4f40308f32e5c0fbd33e Mon Sep 17 00:00:00 2001 From: Bohdan Shevchenko <fanta1408@gmail.com> Date: Tue, 17 Jul 2018 12:46:31 +0300 Subject: [PATCH 0334/1171] MAGETWO-93121: Automate with MFTF verification dynamic bundle product prices for combination of options --- .../Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index 26139f7182bab..7818e1208f5b1 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -23,6 +23,7 @@ <requiredEntity createDataKey="createCategory"/> </createData> <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminGridOrdersClearFiltersActionGroup" stepKey="clearOrderFilters"/> <actionGroup ref="SetTaxClassForShipping" stepKey="setShippingTaxClass"/> </before> <after> @@ -30,6 +31,7 @@ <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteSalesRule"> <argument name="ruleName" value="{{ApiSalesRule.name}}"/> </actionGroup> + <actionGroup ref="AdminGridOrdersClearFiltersActionGroup" stepKey="clearOrderFilters"/> <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> <deleteData createDataKey="createCategory" stepKey="deleteProduct1"/> <deleteData createDataKey="createProduct" stepKey="deleteCategory1"/> From ca8c1d4e28dcec1031314cae495e0070cd47e981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20P=C3=A9rez?= <javier.perez.m@gmail.com> Date: Tue, 17 Jul 2018 12:36:34 +0200 Subject: [PATCH 0335/1171] FIXED: FTP user and password strings urldecoded --- lib/internal/Magento/Framework/System/Ftp.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/internal/Magento/Framework/System/Ftp.php b/lib/internal/Magento/Framework/System/Ftp.php index 216026be8d72e..c56f5c669d3c9 100644 --- a/lib/internal/Magento/Framework/System/Ftp.php +++ b/lib/internal/Magento/Framework/System/Ftp.php @@ -107,6 +107,11 @@ public function validateConnectionString($string) if ($data['scheme'] != 'ftp') { throw new \Exception("Support for scheme unsupported: '{$data['scheme']}'"); } + + // Decode user & password strings from URL + if ( array_key_exists('user', $data) ) $data['user'] = urldecode($data['user']); + if ( array_key_exists('pass', $data) ) $data['pass'] = urldecode($data['pass']); + return $data; } From db3f5f8473ad2ec16f5480d8cd928cffcb287f01 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Tue, 17 Jul 2018 15:20:23 +0300 Subject: [PATCH 0336/1171] MC-1364: Always Render Magento directives on Magento Storefront --- .../Setup/Patch/Data/DisallowUsingHtmlForProductName.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/DisallowUsingHtmlForProductName.php b/app/code/Magento/Catalog/Setup/Patch/Data/DisallowUsingHtmlForProductName.php index ea8f6bbf39b31..226b94ceb1749 100644 --- a/app/code/Magento/Catalog/Setup/Patch/Data/DisallowUsingHtmlForProductName.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/DisallowUsingHtmlForProductName.php @@ -3,7 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - +declare(strict_types=1); namespace Magento\Catalog\Setup\Patch\Data; use Magento\Catalog\Setup\CategorySetupFactory; From bc3deddefdadcf98cabe6d9d5ced64e5852cecdf Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 17 Jul 2018 14:51:47 +0200 Subject: [PATCH 0337/1171] Added unit test for CheckGuestCheckoutObserver --- .../CheckGuestCheckoutObserverTest.php | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 app/code/Magento/Captcha/Test/Unit/Observer/CheckGuestCheckoutObserverTest.php diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckGuestCheckoutObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckGuestCheckoutObserverTest.php new file mode 100644 index 0000000000000..d3f29fae8a592 --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/Observer/CheckGuestCheckoutObserverTest.php @@ -0,0 +1,211 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Captcha\Test\Unit\Observer; + +use Magento\Captcha\Model\DefaultModel as CaptchaModel; +use Magento\Captcha\Observer\CheckGuestCheckoutObserver; +use Magento\Captcha\Helper\Data as CaptchaDataHelper; +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\ActionFlag; +use Magento\Captcha\Observer\CaptchaStringResolver; +use Magento\Checkout\Model\Type\Onepage; +use Magento\Framework\App\Request\Http; +use Magento\Framework\App\Response\Http as HttpResponse; +use Magento\Framework\Event\Observer; +use Magento\Framework\Json\Helper\Data as JsonHelper; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Model\Quote; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class CheckGuestCheckoutObserverTest extends \PHPUnit\Framework\TestCase +{ + const FORM_ID = 'guest_checkout'; + + /** + * @var CheckGuestCheckoutObserver + */ + private $checkGuestCheckoutObserver; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Observer + */ + private $observer; + + /** + * @var HttpResponse|\PHPUnit_Framework_MockObject_MockObject + */ + private $responseMock; + + /** + * @var HttpResponse|\PHPUnit_Framework_MockObject_MockObject + */ + private $requestMock; + + /** + * @var ActionFlag|\PHPUnit_Framework_MockObject_MockObject + */ + private $actionFlagMock; + + /** + * @var CaptchaStringResolver|\PHPUnit_Framework_MockObject_MockObject + */ + private $captchaStringResolverMock; + + /** + * @var JsonHelper|\PHPUnit_Framework_MockObject_MockObject + */ + private $jsonHelperMock; + + /** + * @var CaptchaModel|\PHPUnit_Framework_MockObject_MockObject + */ + private $captchaModelMock; + + /** + * @var Quote|\PHPUnit_Framework_MockObject_MockObject + */ + private $quoteModelMock; + + /** + * @var Action|\PHPUnit_Framework_MockObject_MockObject + */ + private $controllerMock; + + protected function setUp() + { + $onepageModelTypeMock = $this->createMock(Onepage::class); + $captchaHelperMock = $this->createMock(CaptchaDataHelper::class); + $this->objectManager = new ObjectManager($this); + $this->actionFlagMock = $this->createMock(ActionFlag::class); + $this->captchaStringResolverMock = $this->createMock(CaptchaStringResolver::class); + $this->captchaModelMock = $this->createMock(CaptchaModel::class); + $this->quoteModelMock = $this->createMock(Quote::class); + $this->controllerMock = $this->createMock(Action::class); + $this->requestMock = $this->createMock(Http::class); + $this->responseMock = $this->createMock(HttpResponse::class); + $this->observer = new Observer(['controller_action' => $this->controllerMock]); + $this->jsonHelperMock = $this->createMock(JsonHelper::class); + + $this->checkGuestCheckoutObserver = $this->objectManager->getObject( + CheckGuestCheckoutObserver::class, + [ + 'helper' => $captchaHelperMock, + 'actionFlag' => $this->actionFlagMock, + 'captchaStringResolver' => $this->captchaStringResolverMock, + 'typeOnepage' => $onepageModelTypeMock, + 'jsonHelper' => $this->jsonHelperMock + ] + ); + + $captchaHelperMock->expects($this->once()) + ->method('getCaptcha') + ->with(self::FORM_ID) + ->willReturn($this->captchaModelMock); + $onepageModelTypeMock->expects($this->once()) + ->method('getQuote') + ->willReturn($this->quoteModelMock); + } + + public function testCheckGuestCheckoutForRegister() + { + $this->quoteModelMock->expects($this->once()) + ->method('getCheckoutMethod') + ->willReturn(Onepage::METHOD_REGISTER); + $this->captchaModelMock->expects($this->never()) + ->method('isRequired'); + + $this->checkGuestCheckoutObserver->execute($this->observer); + } + + public function testCheckGuestCheckoutWithNoCaptchaRequired() + { + $this->quoteModelMock->expects($this->once()) + ->method('getCheckoutMethod') + ->willReturn(Onepage::METHOD_GUEST); + $this->captchaModelMock->expects($this->once()) + ->method('isRequired') + ->willReturn(false); + $this->captchaModelMock->expects($this->never()) + ->method('isCorrect'); + + $this->checkGuestCheckoutObserver->execute($this->observer); + } + + public function testCheckGuestCheckoutWithIncorrectCaptcha() + { + $captchaValue = 'some_word'; + $encodedJsonValue = '{}'; + + $this->quoteModelMock->expects($this->once()) + ->method('getCheckoutMethod') + ->willReturn(Onepage::METHOD_GUEST); + $this->captchaModelMock->expects($this->once()) + ->method('isRequired') + ->willReturn(true); + $this->controllerMock->expects($this->once()) + ->method('getRequest') + ->willReturn($this->requestMock); + $this->controllerMock->expects($this->once()) + ->method('getResponse') + ->willReturn($this->responseMock); + $this->controllerMock->expects($this->once()) + ->method('getResponse') + ->willReturn($this->responseMock); + $this->captchaStringResolverMock->expects($this->once()) + ->method('resolve') + ->with($this->requestMock, self::FORM_ID) + ->willReturn($captchaValue); + $this->captchaModelMock->expects($this->once()) + ->method('isCorrect') + ->with($captchaValue) + ->willReturn(false); + $this->actionFlagMock->expects($this->once()) + ->method('set') + ->with('', Action::FLAG_NO_DISPATCH, true); + $this->jsonHelperMock->expects($this->once()) + ->method('jsonEncode') + ->willReturn($encodedJsonValue); + $this->responseMock->expects($this->once()) + ->method('representJson') + ->with($encodedJsonValue); + + $this->checkGuestCheckoutObserver->execute($this->observer); + } + + public function testCheckGuestCheckoutWithCorrectCaptcha() + { + $this->quoteModelMock->expects($this->once()) + ->method('getCheckoutMethod') + ->willReturn(Onepage::METHOD_GUEST); + $this->captchaModelMock->expects($this->once()) + ->method('isRequired') + ->willReturn(true); + $this->controllerMock->expects($this->once()) + ->method('getRequest') + ->willReturn($this->requestMock); + $this->captchaStringResolverMock->expects($this->once()) + ->method('resolve') + ->with($this->requestMock, self::FORM_ID) + ->willReturn('some_word'); + $this->captchaModelMock->expects($this->once()) + ->method('isCorrect') + ->with('some_word') + ->willReturn(true); + $this->actionFlagMock->expects($this->never()) + ->method('set'); + + $this->checkGuestCheckoutObserver->execute($this->observer); + } +} From c3a4a8b77cd48f8c72b228d96c1595504fba7547 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Tue, 17 Jul 2018 15:54:29 +0300 Subject: [PATCH 0338/1171] MC-1364: Always Render Magento directives on Magento Storefront --- .../Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php b/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php index 2d2a54c30657c..f881b2f49f600 100644 --- a/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/EnableDirectiveParsing.php @@ -3,7 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - +declare(strict_types=1); namespace Magento\Catalog\Setup\Patch\Data; use Magento\Framework\Setup\ModuleDataSetupInterface; From c2c8ef922604e1a8c9666f29a1a3ff24e7224c1e Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 17 Jul 2018 10:33:54 -0500 Subject: [PATCH 0339/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - fixing formatting --- .../Configuration/Item/ItemResolverComposite.php | 10 ++++++++-- .../Test/Unit/Block/Cart/Item/RendererTest.php | 2 +- .../Configuration/Item/ItemProductResolver.php | 12 ++++++------ .../Configuration/Item/ItemProductResolver.php | 10 +++++----- .../Block/Customer/Wishlist/Item/Column/Image.php | 8 ++++---- 5 files changed, 24 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php index de9d67ac34a90..0580087114ad6 100644 --- a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php +++ b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php @@ -18,6 +18,9 @@ class ItemResolverComposite implements ItemResolverInterface /** @var string[] */ private $itemResolvers = []; + /** @var ItemResolverInterface[] */ + private $itemResolversInstances = []; + /** * @param string[] $itemResolvers */ @@ -43,13 +46,16 @@ public function getFinalProduct(ItemInterface $item) : ProductInterface } /** - * Get the instance of the item resolver by class name + * Get the instance of the item resolver by class name. * * @param string $className * @return ItemResolverInterface */ private function getItemResolverInstance(string $className) { - return ObjectManager::getInstance()->get($className); + if (!isset($this->itemResolversInstances[$className])) { + $this->itemResolversInstances[$className] = ObjectManager::getInstance()->get($className); + } + return $this->itemResolversInstances[$className]; } } diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php index 3155712371faf..7f4c334bcf475 100644 --- a/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php @@ -47,7 +47,7 @@ protected function setUp() ->getMock(); $context->expects($this->once()) ->method('getLayout') - ->will($this->returnValue($this->layout)); + ->willReturn($this->layout); $this->imageBuilder = $this->getMockBuilder(\Magento\Catalog\Block\Product\ImageBuilder::class) ->disableOriginalConstructor() diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php index 12e31021b81e4..4ac47fbe0405a 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -46,23 +46,23 @@ public function getFinalProduct(ItemInterface $item) : ProductInterface * Show parent product thumbnail if it must be always shown according to the related setting in system config * or if child thumbnail is not available. */ - $parentItem = $item->getProduct(); - $config = $this->scopeConfig->getValue( + $parentProduct = $item->getProduct(); + $configValue = $this->scopeConfig->getValue( self::CONFIG_THUMBNAIL_SOURCE, \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); $childProduct = $this->getChildProduct($item); - $childThumbnail = $childProduct->getData('thumbnail'); + $childThumb = $childProduct->getData('thumbnail'); $finalProduct = - ($config == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumbnail || $childThumbnail == 'no_selection') - ? $parentItem + ($configValue == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumb || $childThumb == 'no_selection') + ? $parentProduct : $childProduct; return $finalProduct; } /** - * Get item configurable child product + * Get item configurable child product. * * @param ItemInterface $item * @return Product diff --git a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php index e3c7e926bae29..bbfe76b39719a 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -44,24 +44,24 @@ public function getFinalProduct(ItemInterface $item) : ProductInterface { /** * Show grouped product thumbnail if it must be always shown according to the related setting in system config - * or if child product thumbnail is not available + * or if child product thumbnail is not available. */ - $config = $this->scopeConfig->getValue( + $configValue = $this->scopeConfig->getValue( self::CONFIG_THUMBNAIL_SOURCE, \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); $childProduct = $item->getProduct(); - $childThumbnail = $childProduct->getData('thumbnail'); + $childThumb = $childProduct->getData('thumbnail'); $finalProduct = - ($config == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumbnail || $childThumbnail == 'no_selection') + ($configValue == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumb || $childThumb == 'no_selection') ? $this->getParentProduct($item) : $childProduct; return $finalProduct; } /** - * Get grouped product + * Get grouped product. * * @param ItemInterface $item * @return Product diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php index 793e92a462fa0..5595d189b15eb 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php @@ -3,10 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -/** - * Wishlist block customer item cart column - */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; use Magento\Catalog\Model\Product\Image\UrlBuilder; @@ -15,6 +13,8 @@ use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; /** + * Wishlist block customer item cart column + * * @api * @since 100.0.2 */ @@ -54,7 +54,7 @@ public function __construct( * * @return \Magento\Catalog\Model\Product */ - public function getProductForThumbnail(\Magento\Wishlist\Model\Item $item) + public function getProductForThumbnail(\Magento\Wishlist\Model\Item $item) : \Magento\Catalog\Model\Product { return $this->itemResolver->getFinalProduct($item); } From 525308c171ec900f63086a1f1fb5ad68e67d27fd Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 17 Jul 2018 10:51:20 -0500 Subject: [PATCH 0340/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - fixing formatting --- .../Configuration/Item/ItemResolverComposite.php | 10 +++++----- .../Magento/Checkout/Model/Cart/ImageProvider.php | 3 ++- .../Test/Unit/Block/Cart/Item/RendererTest.php | 12 ++++++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php index 0580087114ad6..68d0877c6cd66 100644 --- a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php +++ b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php @@ -34,15 +34,15 @@ public function __construct(array $itemResolvers) */ public function getFinalProduct(ItemInterface $item) : ProductInterface { - $product = $item->getProduct(); + $finalProduct = $item->getProduct(); foreach ($this->itemResolvers as $resolver) { $resolvedProduct = $this->getItemResolverInstance($resolver)->getFinalProduct($item); - if ($resolvedProduct !== $product) { - $product = $resolvedProduct; + if ($resolvedProduct !== $finalProduct) { + $finalProduct = $resolvedProduct; break; } } - return $product; + return $finalProduct; } /** @@ -51,7 +51,7 @@ public function getFinalProduct(ItemInterface $item) : ProductInterface * @param string $className * @return ItemResolverInterface */ - private function getItemResolverInstance(string $className) + private function getItemResolverInstance(string $className) : ItemResolverInterface { if (!isset($this->itemResolversInstances[$className])) { $this->itemResolversInstances[$className] = ObjectManager::getInstance()->get($className); diff --git a/app/code/Magento/Checkout/Model/Cart/ImageProvider.php b/app/code/Magento/Checkout/Model/Cart/ImageProvider.php index 61985e6e3b6ff..cdadf3573c8ec 100644 --- a/app/code/Magento/Checkout/Model/Cart/ImageProvider.php +++ b/app/code/Magento/Checkout/Model/Cart/ImageProvider.php @@ -20,7 +20,8 @@ class ImageProvider /** * @var \Magento\Checkout\CustomerData\ItemPoolInterface - * @deprecated + * @deprecated No need for the pool as images are resolved in the default item implementation + * @see \Magento\Checkout\CustomerData\DefaultItem::getProductForThumbnail */ protected $itemPool; diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php index 7f4c334bcf475..40154563774d5 100644 --- a/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php @@ -86,16 +86,16 @@ protected function _initProduct() Product::class, ['getName', '__wakeup', 'getIdentities'] ); - $product->expects($this->any())->method('getName')->will($this->returnValue('Parent Product')); + $product->expects($this->any())->method('getName')->willReturn('Parent Product'); /** @var Item|\PHPUnit_Framework_MockObject_MockObject $item */ $item = $this->createMock(\Magento\Quote\Model\Quote\Item::class); - $item->expects($this->any())->method('getProduct')->will($this->returnValue($product)); + $item->expects($this->any())->method('getProduct')->willReturn($product); $this->itemResolver->expects($this->any()) ->method('getFinalProduct') ->with($item) - ->will($this->returnValue($product)); + ->willReturn($product); $this->renderer->setItem($item); return $product; @@ -107,7 +107,7 @@ public function testGetIdentities() $identities = [1 => 1, 2 => 2, 3 => 3]; $product->expects($this->exactly(2)) ->method('getIdentities') - ->will($this->returnValue($identities)); + ->willReturn($identities); $this->assertEquals($product->getIdentities(), $this->renderer->getIdentities()); } @@ -135,7 +135,7 @@ public function testGetProductPriceHtml() $this->layout->expects($this->atLeastOnce()) ->method('getBlock') ->with('product.price.render.default') - ->will($this->returnValue($priceRender)); + ->willReturn($priceRender); $priceRender->expects($this->once()) ->method('render') @@ -147,7 +147,7 @@ public function testGetProductPriceHtml() 'display_minimal_price' => true, 'zone' => \Magento\Framework\Pricing\Render::ZONE_ITEM_LIST ] - )->will($this->returnValue($priceHtml)); + )->willReturn($priceHtml); $this->assertEquals($priceHtml, $this->renderer->getProductPriceHtml($product)); } From 2b9413aca26a38bc50f2884ba26f9217dbad5c65 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 17 Jul 2018 11:00:50 -0500 Subject: [PATCH 0341/1171] MAGETWO-92929: Declarative schema tests are located in inappropriate - fix test --- .../Setup/Test/Unit/SchemaListenerTest.php | 15 ++++++++------- .../Setup/Test/Unit/SchemaPersistorTest.php | 7 ++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaListenerTest.php b/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaListenerTest.php index 38b81fa7c744c..4e34b3aebbf3e 100644 --- a/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaListenerTest.php +++ b/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaListenerTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\Setup\Test\Unit; @@ -30,7 +31,7 @@ class SchemaListenerTest extends \PHPUnit\Framework\TestCase */ private $objectManagerHelper; - protected function setUp() + protected function setUp() : void { $this->objectManagerHelper = new ObjectManagerHelper($this); $this->model = $this->objectManagerHelper->getObject( @@ -51,7 +52,7 @@ protected function setUp() /** * @return Table */ - private function getCreateTableDDL($tableName) + private function getCreateTableDDL($tableName) : Table { $table = new Table(); $table->setName($tableName); @@ -91,7 +92,7 @@ private function getCreateTableDDL($tableName) ); } - public function testRenameTable() + public function testRenameTable() : void { $this->model->setModuleName('First_Module'); $this->model->createTable($this->getCreateTableDDL('old_table')); @@ -101,7 +102,7 @@ public function testRenameTable() self::assertArrayNotHasKey('old_table', $tables['First_Module']); } - public function testDropIndex() + public function testDropIndex() : void { $this->model->setModuleName('First_Module'); $this->model->createTable($this->getCreateTableDDL('index_table')); @@ -109,7 +110,7 @@ public function testDropIndex() self::assertTrue($this->model->getTables()['First_Module']['index_table']['indexes']['INDEX_KEY']['disabled']); } - public function testCreateTable() + public function testCreateTable() : void { $this->model->setModuleName('First_Module'); $this->model->createTable($this->getCreateTableDDL('new_table')); @@ -201,7 +202,7 @@ public function testCreateTable() ); } - public function testDropTable() + public function testDropTable() : void { $this->model->setModuleName('Old_Module'); $this->model->createTable($this->getCreateTableDDL('old_table')); @@ -210,7 +211,7 @@ public function testDropTable() self::assertTrue($this->model->getTables()['New_Module']['old_table']['disabled']); } - public function testDropTableInSameModule() + public function testDropTableInSameModule() : void { $this->model->setModuleName('Old_Module'); $this->model->createTable($this->getCreateTableDDL('old_table')); diff --git a/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaPersistorTest.php b/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaPersistorTest.php index 9e416fc514755..cc88af15a262b 100644 --- a/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaPersistorTest.php +++ b/lib/internal/Magento/Framework/Setup/Test/Unit/SchemaPersistorTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\Setup\Test\Unit; @@ -38,7 +39,7 @@ class SchemaPersistorTest extends \PHPUnit\Framework\TestCase */ private $xmlPersistor; - protected function setUp() + protected function setUp() : void { $this->componentRegistrarMock = $this->getMockBuilder(ComponentRegistrar::class) ->disableOriginalConstructor() @@ -60,7 +61,7 @@ protected function setUp() * @param array $tables * @param string $expectedXML */ - public function testPersist(array $tables, $expectedXML) + public function testPersist(array $tables, $expectedXML) : void { $moduleName = 'First_Module'; /** @var SchemaListener|\PHPUnit_Framework_MockObject_MockObject $schemaListenerMock */ @@ -88,7 +89,7 @@ public function testPersist(array $tables, $expectedXML) * * @return array */ - public function schemaListenerTablesDataProvider() + public function schemaListenerTablesDataProvider() : array { return [ [ From 95c2f757aac67ce7428381c6ad70edece4448aa3 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Tue, 17 Jul 2018 22:07:00 +0530 Subject: [PATCH 0342/1171] Code improvement --- app/code/Magento/Catalog/etc/adminhtml/system.xml | 1 - app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml | 1 - .../Magento/Paypal/etc/adminhtml/system/express_checkout.xml | 1 - .../Magento/Sales/view/adminhtml/web/order/create/scripts.js | 2 +- .../Model/Config/Structure/Reader/_files/expected/config.xml | 1 - lib/web/mage/utils/wrapper.js | 2 +- 6 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index e6dbb10e811b4..264e1a48de3b5 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -103,7 +103,6 @@ <clone_fields>1</clone_fields> <clone_model>Magento\Catalog\Model\Config\CatalogClone\Media\Image</clone_model> <field id="placeholder" type="image" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1"> - <label></label> <backend_model>Magento\Config\Model\Config\Backend\Image</backend_model> <upload_dir config="system/filesystem/media" scope_info="1">catalog/product/placeholder</upload_dir> <base_url type="media" scope_info="1">catalog/product/placeholder</base_url> diff --git a/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml b/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml index c4001f47ced0b..81d614a81e881 100644 --- a/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml +++ b/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml @@ -76,7 +76,6 @@ <config_path>section/group/field4</config_path> </field> <field id="field_5" translate="label" showInWebsite="1" type="text"> - <label></label> </field> </group> <group id="group_4" type="text" showInDefault="1" showInWebsite="1" showInStore="1"> diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml b/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml index 920c612021a1a..a726439331cb0 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml @@ -198,7 +198,6 @@ <attribute type="shared">1</attribute> </field> <field id="bml_wizard" translate="button_label" sortOrder="15" showInDefault="1" showInWebsite="1"> - <label></label> <button_label>Get Publisher ID from PayPal</button_label> <button_url><![CDATA[https:/financing.paypal.com/ppfinportal/cart/index?dcp=4eff8563b9cc505e0b9afaff3256705081553c79]]></button_url> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\BmlApiWizard</frontend_model> diff --git a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js index 8990ea8d3f75b..62bd9e27c832d 100644 --- a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js +++ b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js @@ -47,7 +47,7 @@ define([ var buttons = controlButtonArea.childElements(); for (var i = 0; i < buttons.length; i++) { if (buttons[i].innerHTML.include(button.label)) { - return ; + return; } } button.insertIn(controlButtonArea, 'top'); diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml b/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml index 671ab6e4b5619..2552d383bbcc3 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml @@ -767,7 +767,6 @@ <attribute type="shared">1</attribute> </field> <field id="bml_wizard" translate="button_label" sortOrder="15" showInDefault="1" showInWebsite="1"> - <label></label> <button_label>Get Publisher ID from PayPal</button_label> <button_url><![CDATA[https:/financing.paypal.com/ppfinportal/cart/index?dcp=4eff8563b9cc505e0b9afaff3256705081553c79]]></button_url> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\BmlApiWizard</frontend_model> diff --git a/lib/web/mage/utils/wrapper.js b/lib/web/mage/utils/wrapper.js index c90d1a026b147..9d4bb045b5722 100644 --- a/lib/web/mage/utils/wrapper.js +++ b/lib/web/mage/utils/wrapper.js @@ -45,7 +45,7 @@ define([ return { /** - * Wraps target function with a specified wrapper, which will recieve + * Wraps target function with a specified wrapper, which will receive * reference to the original function as a first argument. * * @param {Function} target - Function to be wrapped. From d13033bb3a525c8240170031021cebdd8be312a9 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Tue, 17 Jul 2018 22:20:03 +0530 Subject: [PATCH 0343/1171] Fixed a couple of spelling mistakes --- .../Braintree/Setup/Patch/Data/ConvertSerializedDataToJson.php | 2 +- .../Bundle/Test/Unit/Pricing/Price/BundleSelectionPriceTest.php | 2 +- lib/internal/Magento/Framework/GraphQlSchemaStitching/README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Braintree/Setup/Patch/Data/ConvertSerializedDataToJson.php b/app/code/Magento/Braintree/Setup/Patch/Data/ConvertSerializedDataToJson.php index a0704002842ea..d08bf62da8e4f 100644 --- a/app/code/Magento/Braintree/Setup/Patch/Data/ConvertSerializedDataToJson.php +++ b/app/code/Magento/Braintree/Setup/Patch/Data/ConvertSerializedDataToJson.php @@ -12,7 +12,7 @@ use Magento\Framework\Setup\Patch\PatchVersionInterface; /** - * Convert data fro php native serialized data to JSON. + * Convert data from php native serialized data to JSON. */ class ConvertSerializedDataToJson implements DataPatchInterface, PatchVersionInterface { diff --git a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleSelectionPriceTest.php b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleSelectionPriceTest.php index d35619bb043a0..9f35251b3f926 100644 --- a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleSelectionPriceTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleSelectionPriceTest.php @@ -121,7 +121,7 @@ protected function setupSelectionPrice($useRegularPrice = false) } /** - * test fro method getValue with dynamic productType + * Test for method getValue with dynamic productType * * @param bool $useRegularPrice * @dataProvider useRegularPriceDataProvider diff --git a/lib/internal/Magento/Framework/GraphQlSchemaStitching/README.md b/lib/internal/Magento/Framework/GraphQlSchemaStitching/README.md index da47fff7f96d4..b87cd2d7e4e7c 100644 --- a/lib/internal/Magento/Framework/GraphQlSchemaStitching/README.md +++ b/lib/internal/Magento/Framework/GraphQlSchemaStitching/README.md @@ -1 +1 @@ -The GraphQlSchemaStitching library contains functionality for processing GraphQl SDL schema, to be used by Graphql library to build objects fro the data generated. +The GraphQlSchemaStitching library contains functionality for processing GraphQl SDL schema, to be used by Graphql library to build objects from the data generated. From 0df36b0ba85d71f78ce9d70b74ba2357a8253389 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Tue, 17 Jul 2018 11:33:10 -0500 Subject: [PATCH 0344/1171] MC-2294: Image still displayed after deleting it from Media Gallery Storage - Supporting changes to media browser --- .../view/base/web/js/form/element/abstract.js | 2 +- .../base/web/js/form/element/image-uploader.js | 17 +++++++++++------ dev/tests/acceptance/.htaccess | 11 +++++++++++ lib/web/mage/adminhtml/browser.js | 12 ++++++++++++ 4 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 dev/tests/acceptance/.htaccess diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js index 5177b4a378d69..1de0077b6ced3 100755 --- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js @@ -407,7 +407,7 @@ define([ this.bubble('error', message); //TODO: Implement proper result propagation for form - if (!isValid) { + if (this.source && !isValid) { this.source.set('params.invalid', true); } diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js index 69c9fb74cbce1..47df6687090f4 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js @@ -43,17 +43,22 @@ define([ */ addFileFromMediaGallery: function (imageUploader, e) { var $buttonEl = $(e.target), + fileId = $buttonEl.data('id'), fileSize = $buttonEl.data('size'), fileMimeType = $buttonEl.data('mime-type'), filePathname = $buttonEl.val(), fileBasename = filePathname.split('/').pop(); - this.addFile({ - type: fileMimeType, - name: fileBasename, - size: fileSize, - url: filePathname - }); + if (filePathname.length > 0) { + this.addFile({ + id: fileId, + type: fileMimeType, + name: fileBasename, + size: fileSize, + url: filePathname + }); + } + }, /** diff --git a/dev/tests/acceptance/.htaccess b/dev/tests/acceptance/.htaccess new file mode 100644 index 0000000000000..e6bb9dd7ddab9 --- /dev/null +++ b/dev/tests/acceptance/.htaccess @@ -0,0 +1,11 @@ +############################################## +## Allow access to command.php + <FilesMatch "command.php"> + <IfVersion < 2.4> + order allow,deny + allow from all + </IfVersion> + <IfVersion >= 2.4> + Require all granted + </IfVersion> + </FilesMatch> diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 26d6679bb3ce5..9ae300e7beda3 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -276,6 +276,7 @@ define([ } else { targetEl .val(data) + .data('id', fileRow.attr('id')) .data('size', fileRow.data('size')) .data('mime-type', fileRow.data('mime-type')) .trigger('change'); @@ -433,8 +434,19 @@ define([ context: self.element, showLoader: true }).done($.proxy(function () { + var targetEl = self.getTargetElement(); + self.reload(); self.element.find('#delete_files').toggleClass(self.options.hidden, true); + + if ($.inArray(targetEl.data('id'), ids) > -1) { + targetEl + .val('') + .data('id', null) + .data('mime-type', null) + .data('size', null) + .trigger('change'); + } }, this)); }, From d116c2e6b30a3331754472934fbe6c33b05263db Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Tue, 17 Jul 2018 14:18:59 -0500 Subject: [PATCH 0345/1171] MC-2294: Image still displayed after deleting it from Media Gallery Storage - New event approach --- .../base/web/js/form/element/image-uploader.js | 17 +++++++---------- lib/web/mage/adminhtml/browser.js | 11 +++-------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js index 47df6687090f4..037690bf6be50 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js @@ -49,16 +49,13 @@ define([ filePathname = $buttonEl.val(), fileBasename = filePathname.split('/').pop(); - if (filePathname.length > 0) { - this.addFile({ - id: fileId, - type: fileMimeType, - name: fileBasename, - size: fileSize, - url: filePathname - }); - } - + this.addFile({ + id: fileId, + type: fileMimeType, + name: fileBasename, + size: fileSize, + url: filePathname + }); }, /** diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 9ae300e7beda3..e9e8cc6ffaef8 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -439,14 +439,9 @@ define([ self.reload(); self.element.find('#delete_files').toggleClass(self.options.hidden, true); - if ($.inArray(targetEl.data('id'), ids) > -1) { - targetEl - .val('') - .data('id', null) - .data('mime-type', null) - .data('size', null) - .trigger('change'); - } + $(window).trigger('fileDeleted.mediabrowser', { + ids: ids + }); }, this)); }, From 18d11f580bc51dd2fba1b082c95cf194cd6c477c Mon Sep 17 00:00:00 2001 From: Matti Vapa <matti.vapa@piimega.fi> Date: Wed, 7 Feb 2018 10:31:49 +0200 Subject: [PATCH 0346/1171] Fix proposal for https://github.com/magento/magento2/issues/12081 --- app/code/Magento/Translation/etc/di.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Translation/etc/di.xml b/app/code/Magento/Translation/etc/di.xml index c10619fbf74ae..6d3ca03953cf9 100644 --- a/app/code/Magento/Translation/etc/di.xml +++ b/app/code/Magento/Translation/etc/di.xml @@ -67,6 +67,7 @@ <item name="translate_wrapping" xsi:type="string"><![CDATA[~translate\=("')([^\'].*?)\'\"~]]></item> <item name="mage_translation_widget" xsi:type="string"><![CDATA[~(?:\$|jQuery)\.mage\.__\((?s)[^'"]*?(['"])(.+?)(?<!\\)\1(?s).*?\)~]]></item> <item name="mage_translation_static" xsi:type="string"><![CDATA[~\$t\((?s)[^'"]*?(["'])(.+?)\1(?s).*?\)~]]></item> + <item name="translate_args" xsi:type="string"><![CDATA[~translate args\=("|'|"')([^\'].*?)('"|'|")~]]></item> </argument> </arguments> </type> From b7d82b32ad13e249cb873ab4878d8c9ccc38b3c4 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Tue, 17 Jul 2018 14:30:36 -0500 Subject: [PATCH 0347/1171] MC-2294: Image still displayed after deleting it from Media Gallery Storage - Removed line --- lib/web/mage/adminhtml/browser.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index e9e8cc6ffaef8..717098110ceb5 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -434,8 +434,6 @@ define([ context: self.element, showLoader: true }).done($.proxy(function () { - var targetEl = self.getTargetElement(); - self.reload(); self.element.find('#delete_files').toggleClass(self.options.hidden, true); From 27e9bea9ecc65a42f6a34a5bdb93f0fbc269a081 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Tue, 17 Jul 2018 16:30:40 -0300 Subject: [PATCH 0348/1171] Fixing annotations for some methods. --- .../Framework/Acl/AclResource/Config/Converter/Dom.php | 1 + .../Framework/Acl/AclResource/Config/SchemaLocator.php | 4 ++++ lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php | 2 ++ 3 files changed, 7 insertions(+) diff --git a/lib/internal/Magento/Framework/Acl/AclResource/Config/Converter/Dom.php b/lib/internal/Magento/Framework/Acl/AclResource/Config/Converter/Dom.php index 1b4e70be3644f..68762a8a6c046 100644 --- a/lib/internal/Magento/Framework/Acl/AclResource/Config/Converter/Dom.php +++ b/lib/internal/Magento/Framework/Acl/AclResource/Config/Converter/Dom.php @@ -12,6 +12,7 @@ class Dom implements \Magento\Framework\Config\ConverterInterface * * @param \DOMDocument $source * @return array + * @throws \Exception */ public function convert($source) { diff --git a/lib/internal/Magento/Framework/Acl/AclResource/Config/SchemaLocator.php b/lib/internal/Magento/Framework/Acl/AclResource/Config/SchemaLocator.php index 105f27cb330fc..3cc1087228174 100644 --- a/lib/internal/Magento/Framework/Acl/AclResource/Config/SchemaLocator.php +++ b/lib/internal/Magento/Framework/Acl/AclResource/Config/SchemaLocator.php @@ -27,6 +27,8 @@ public function __construct(\Magento\Framework\Config\Dom\UrnResolver $urnResolv /** * {@inheritdoc} + * + * @throws \Magento\Framework\Exception\NotFoundException */ public function getSchema() { @@ -35,6 +37,8 @@ public function getSchema() /** * {@inheritdoc} + * + * @throws \Magento\Framework\Exception\NotFoundException */ public function getPerFileSchema() { diff --git a/lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php b/lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php index c5b75d4ccd3b9..8a4ad39d78570 100644 --- a/lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php +++ b/lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php @@ -43,6 +43,7 @@ public function __construct(ProviderInterface $resourceProvider, AclResourceFact * * @param Acl $acl * @return void + * @throws \Zend_Acl_Exception */ public function populateAcl(Acl $acl) { @@ -57,6 +58,7 @@ public function populateAcl(Acl $acl) * @param AclResource $parent * @return void * @throws \InvalidArgumentException + * @throws \Zend_Acl_Exception */ protected function _addResourceTree(Acl $acl, array $resources, AclResource $parent = null) { From 49926d73e6e26961f9b3f529dac1856957c4688d Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <info@riccardotempesta.com> Date: Sun, 29 Apr 2018 20:18:57 +0200 Subject: [PATCH 0349/1171] FIX for issue#14869 - Wrong price at backend after update A previous commit 9d3be732a88884a66d667b443b3dc1655ddd0721 changed the default behaviour of \Magento\Quote\Model\ResourceModel\Quote\Item\Collection::getStoreId() using the store coming from the current session instead of using the one from quote. The previous commit was made to fix an error while using getItems() without setting a quote. The current fix restore the previous behaviour and adds a check if the quote is not specified. --- .../ResourceModel/Quote/Item/Collection.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php index 0487d7e46eb26..8a89b331172a9 100644 --- a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php +++ b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php @@ -45,6 +45,11 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\VersionContro */ protected $_quoteConfig; + /** + * @var \Magento\Store\Model\StoreManagerInterface|null + */ + private $storeManager; + /** * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory * @param \Psr\Log\LoggerInterface $logger @@ -54,6 +59,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\VersionContro * @param Option\CollectionFactory $itemOptionCollectionFactory * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory * @param \Magento\Quote\Model\Quote\Config $quoteConfig + * @param \Magento\Store\Model\StoreManagerInterface|null $storeManager * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -67,6 +73,7 @@ public function __construct( \Magento\Quote\Model\ResourceModel\Quote\Item\Option\CollectionFactory $itemOptionCollectionFactory, \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory, \Magento\Quote\Model\Quote\Config $quoteConfig, + \Magento\Store\Model\StoreManagerInterface $storeManager = null, \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null ) { @@ -82,6 +89,10 @@ public function __construct( $this->_itemOptionCollectionFactory = $itemOptionCollectionFactory; $this->_productCollectionFactory = $productCollectionFactory; $this->_quoteConfig = $quoteConfig; + + // Backward compatibility constructor parameters + $this->storeManager = $storeManager ?: + \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Store\Model\StoreManagerInterface::class); } /** @@ -101,7 +112,10 @@ protected function _construct() */ public function getStoreId() { - return (int)$this->_productCollectionFactory->create()->getStoreId(); + // Fallback to current storeId if no quote is provided + // (see https://github.com/magento/magento2/commit/9d3be732a88884a66d667b443b3dc1655ddd0721) + return $this->_quote === null ? + (int) $this->storeManager->getStore()->getId() : (int) $this->_quote->getStoreId(); } /** From 8310be3d7e33f7532dc4765c1a19a175f891eb00 Mon Sep 17 00:00:00 2001 From: Sean Templeton <sean@templeton.io> Date: Mon, 9 Jul 2018 22:55:49 -0500 Subject: [PATCH 0350/1171] Prevent running SQL query on every item in the database when the quote is empty --- .../Quote/Model/ResourceModel/Quote/Item/Collection.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php index 0487d7e46eb26..1938d7bd2780e 100644 --- a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php +++ b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php @@ -309,6 +309,10 @@ private function addTierPriceData(ProductCollection $productCollection) */ private function removeItemsWithAbsentProducts() { + if (count($this->_productIds) === 0) { + return; + } + $productCollection = $this->_productCollectionFactory->create()->addIdFilter($this->_productIds); $existingProductsIds = $productCollection->getAllIds(); $absentProductsIds = array_diff($this->_productIds, $existingProductsIds); From 7d581c7a4d1e3cc2ed97941acf000bbaf0216f55 Mon Sep 17 00:00:00 2001 From: Vlad Veselov <orlangur@users.noreply.github.com> Date: Tue, 17 Jul 2018 23:16:20 +0300 Subject: [PATCH 0351/1171] Update Tax.php --- app/code/Magento/Tax/Block/Sales/Order/Tax.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Tax/Block/Sales/Order/Tax.php b/app/code/Magento/Tax/Block/Sales/Order/Tax.php index 2ea0bcddea14e..4d48abbcb0982 100644 --- a/app/code/Magento/Tax/Block/Sales/Order/Tax.php +++ b/app/code/Magento/Tax/Block/Sales/Order/Tax.php @@ -264,7 +264,6 @@ protected function _initShipping() */ protected function _initDiscount() { - return; } /** From aca8e4698e72629fdf11f9078c85a2bb186d0d09 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Tue, 17 Jul 2018 16:07:41 -0500 Subject: [PATCH 0352/1171] MC-2294: Image still displayed after deleting it from Media Gallery Storage - Added test and cleaned up --- app/code/Magento/Ui/view/base/web/js/form/element/abstract.js | 2 +- dev/tests/integration/testsuite/Magento/PageBuilder | 1 + .../code/Magento/Ui/base/js/form/element/image-uploader.test.js | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) create mode 120000 dev/tests/integration/testsuite/Magento/PageBuilder diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js index 1de0077b6ced3..5177b4a378d69 100755 --- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js @@ -407,7 +407,7 @@ define([ this.bubble('error', message); //TODO: Implement proper result propagation for form - if (this.source && !isValid) { + if (!isValid) { this.source.set('params.invalid', true); } diff --git a/dev/tests/integration/testsuite/Magento/PageBuilder b/dev/tests/integration/testsuite/Magento/PageBuilder new file mode 120000 index 0000000000000..0dea7beaaae28 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PageBuilder @@ -0,0 +1 @@ +/Users/nathsmit/Sites/pagebuilder/magento2-page-builder/dev/tests/integration/testsuite/Magento/PageBuilder/ \ No newline at end of file diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/image-uploader.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/image-uploader.test.js index 31d1c1ce27e96..e7745103db356 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/image-uploader.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/image-uploader.test.js @@ -35,6 +35,7 @@ define([ spyOn(component, 'addFile'); $el.data({ + id: 'ABC123--', 'size': 1024, 'mime-type': 'image/png' }); @@ -46,6 +47,7 @@ define([ }); expect(component.addFile).toHaveBeenCalledWith({ + id: 'ABC123--', type: 'image/png', name: 'something.png', url: '/pub/media/something.png', From 2763259227e5161bc37bee19d8a8f1bc1b40a588 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Tue, 17 Jul 2018 16:24:26 -0500 Subject: [PATCH 0353/1171] MC-2294: Image still displayed after deleting it from Media Gallery Storage - Removed htaccess --- dev/tests/acceptance/.htaccess | 11 ----------- dev/tests/integration/testsuite/Magento/PageBuilder | 1 - 2 files changed, 12 deletions(-) delete mode 100644 dev/tests/acceptance/.htaccess delete mode 120000 dev/tests/integration/testsuite/Magento/PageBuilder diff --git a/dev/tests/acceptance/.htaccess b/dev/tests/acceptance/.htaccess deleted file mode 100644 index e6bb9dd7ddab9..0000000000000 --- a/dev/tests/acceptance/.htaccess +++ /dev/null @@ -1,11 +0,0 @@ -############################################## -## Allow access to command.php - <FilesMatch "command.php"> - <IfVersion < 2.4> - order allow,deny - allow from all - </IfVersion> - <IfVersion >= 2.4> - Require all granted - </IfVersion> - </FilesMatch> diff --git a/dev/tests/integration/testsuite/Magento/PageBuilder b/dev/tests/integration/testsuite/Magento/PageBuilder deleted file mode 120000 index 0dea7beaaae28..0000000000000 --- a/dev/tests/integration/testsuite/Magento/PageBuilder +++ /dev/null @@ -1 +0,0 @@ -/Users/nathsmit/Sites/pagebuilder/magento2-page-builder/dev/tests/integration/testsuite/Magento/PageBuilder/ \ No newline at end of file From a1b137f923ac0c97933a7408bc7bfbd078e34ea6 Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Sat, 16 Jun 2018 10:30:50 +0530 Subject: [PATCH 0354/1171] Removed double occurrence of keywords in sentences. --- CHANGELOG.md | 2 +- .../view/frontend/web/js/password-strength-indicator.js | 2 +- .../Magento/Integration/view/adminhtml/web/js/integration.js | 2 +- .../base/web/tiny_mce/plugins/autosave/editor_plugin_src.js | 4 ++-- .../view/base/web/tiny_mce/plugins/paste/editor_plugin_src.js | 2 +- .../Magento/CatalogRule/Model/Indexer/ProductRuleTest.php | 2 +- .../testsuite/Magento/Customer/Model/CustomerMetadataTest.php | 2 +- .../testsuite/Magento/Tax/Model/TaxCalculationTest.php | 2 +- lib/internal/Magento/Framework/Api/Uploader.php | 2 +- lib/web/MutationObserver.js | 2 +- lib/web/mage/requirejs/static.js | 2 +- lib/web/modernizr/modernizr.js | 2 +- setup/pub/angular/angular.js | 2 +- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef841ec0337f2..078b93bd7199c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1977,7 +1977,7 @@ Tests: * [#686](https://github.com/magento/magento2/issues/686) -- Product save validation errors in the admin don't hide the overlay * [#702](https://github.com/magento/magento2/issues/702) -- Base table or view not found * [#652](https://github.com/magento/magento2/issues/652) -- Multishipping checkout not to change the Billing address js issue - * [#648](https://github.com/magento/magento2/issues/648) -- An equal (=) sign in the hash of the product page to to break the tabs functionality + * [#648](https://github.com/magento/magento2/issues/648) -- An equal (=) sign in the hash of the product page to break the tabs functionality * Service Contracts: * Refactored usage of new API of the Customer module * Implemented Service Contracts for the Sales module diff --git a/app/code/Magento/Customer/view/frontend/web/js/password-strength-indicator.js b/app/code/Magento/Customer/view/frontend/web/js/password-strength-indicator.js index be2e0aedfe4bb..89d9b320c049d 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/password-strength-indicator.js +++ b/app/code/Magento/Customer/view/frontend/web/js/password-strength-indicator.js @@ -31,7 +31,7 @@ define([ this.options.cache.label = $(this.options.passwordStrengthMeterLabelSelector, this.element); // We need to look outside the module for backward compatibility, since someone can already use the module. - // @todo Narrow this selector in 2.3 so it doesn't accidentally finds the the email field from the + // @todo Narrow this selector in 2.3 so it doesn't accidentally finds the email field from the // newsletter email field or any other "email" field. this.options.cache.email = $(this.options.formSelector).find(this.options.emailSelector); this._bind(); diff --git a/app/code/Magento/Integration/view/adminhtml/web/js/integration.js b/app/code/Magento/Integration/view/adminhtml/web/js/integration.js index 0bd7df8c0fa10..6921f645a2330 100644 --- a/app/code/Magento/Integration/view/adminhtml/web/js/integration.js +++ b/app/code/Magento/Integration/view/adminhtml/web/js/integration.js @@ -200,7 +200,7 @@ define([ if (IdentityLogin.win.closed || IdentityLogin.win.location.href == IdentityLogin.successCallbackUrl //eslint-disable-line eqeqeq ) { - //Stop the the polling + //Stop the polling clearInterval(IdentityLogin.checker); $('body').trigger('processStart'); //Check for window closed diff --git a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/autosave/editor_plugin_src.js b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/autosave/editor_plugin_src.js index 31bcb419fb178..e215f078c4b02 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/autosave/editor_plugin_src.js +++ b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/autosave/editor_plugin_src.js @@ -20,7 +20,7 @@ * 1. localStorage - A new feature of HTML 5, localStorage can store megabytes of data per domain * on the client computer. Data stored in the localStorage area has no expiration date, so we must * manage expiring the data ourselves. localStorage is fully supported by IE8, and it is supposed - * to be working in Firefox 3 and Safari 3.2, but in reality is is flaky in those browsers. As + * to be working in Firefox 3 and Safari 3.2, but in reality is flaky in those browsers. As * HTML 5 gets wider support, the AutoSave plugin will use it automatically. In Windows Vista/7, * localStorage is stored in the following folder: * C:\Users\[username]\AppData\Local\Microsoft\Internet Explorer\DOMStore\[tempFolder] @@ -297,7 +297,7 @@ }, /** - * This method will store the current contents in the the storage engine. + * This method will store the current contents in the storage engine. * * @method storeDraft */ diff --git a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/paste/editor_plugin_src.js b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/paste/editor_plugin_src.js index cec4abf98727a..db89eccc4e7ef 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/paste/editor_plugin_src.js +++ b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/paste/editor_plugin_src.js @@ -207,7 +207,7 @@ sel.setRng(oldRng); sel.setContent(''); - // For some odd reason we need to detach the the mceInsertContent call from the paste event + // For some odd reason we need to detach the mceInsertContent call from the paste event // It's like IE has a reference to the parent element that you paste in and the selection gets messed up // when it tries to restore the selection setTimeout(function() { diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php index 76d17527d567a..911c7aa30641e 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php @@ -67,7 +67,7 @@ public function testReindexWithProductNotVisibleIndividually() $this->assertEquals( 7.5, $this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $product->getId()), - "Catalog price rule doesn't apply to to product with visibility value \"Not Visibility Individually\"" + "Catalog price rule doesn't apply to product with visibility value \"Not Visibility Individually\"" ); } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php index 3ed330ed98d6b..336b438661705 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php @@ -262,7 +262,7 @@ public function testGetCustomerAttributeMetadata() $this->assertEquals( $attributeMetadata, $attributeMetadata1, - 'Attribute metadata from the the same service became different after getAttributeCode was called' + 'Attribute metadata from the same service became different after getAttributeCode was called' ); // Verify the consistency of attribute metadata from two services // after getAttributeCode was called diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php index e0bd6f3523612..dd5d12c5b9be3 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php @@ -2192,7 +2192,7 @@ protected function setupMultiRuleQuote() } /** - * Create the base results for the the multi rules test + * Create the base results for the multi rules test * * @return array * @SuppressWarnings(PHPMD.ExcessiveMethodLength) diff --git a/lib/internal/Magento/Framework/Api/Uploader.php b/lib/internal/Magento/Framework/Api/Uploader.php index d3ae11100b9dc..5cea3a34569a9 100644 --- a/lib/internal/Magento/Framework/Api/Uploader.php +++ b/lib/internal/Magento/Framework/Api/Uploader.php @@ -19,7 +19,7 @@ public function __construct() } /** - * Explicitly set the the file attributes instead of setting it via constructor + * Explicitly set the file attributes instead of setting it via constructor * * @param array $fileAttributes * @return void diff --git a/lib/web/MutationObserver.js b/lib/web/MutationObserver.js index 53424fbfa8d0c..4044aa465e745 100644 --- a/lib/web/MutationObserver.js +++ b/lib/web/MutationObserver.js @@ -324,7 +324,7 @@ if (lastRecord === newRecord) return lastRecord; - // Check if the the record we are adding represents the same record. If + // Check if the record we are adding represents the same record. If // so, we keep the one with the oldValue in it. if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) return recordWithOldValue; diff --git a/lib/web/mage/requirejs/static.js b/lib/web/mage/requirejs/static.js index 237aa0c6a8a63..898850cb2948b 100644 --- a/lib/web/mage/requirejs/static.js +++ b/lib/web/mage/requirejs/static.js @@ -13,7 +13,7 @@ define('buildTools', [ isEnabled: storage.getItem(storeName) === null, /** - * Removes base url from the the provided string + * Removes base url from the provided string * * @param {String} url - Url to be processed. * @param {Object} config - RequiereJs config object. diff --git a/lib/web/modernizr/modernizr.js b/lib/web/modernizr/modernizr.js index 0833cfb105cee..d7ddc86f63cae 100644 --- a/lib/web/modernizr/modernizr.js +++ b/lib/web/modernizr/modernizr.js @@ -1013,7 +1013,7 @@ window.Modernizr = (function( window, document, undefined ) { /** Name of the expando, to work with multiple documents or to re-shiv one document */ var expando = '_html5shiv'; - /** The id for the the documents expando */ + /** The id for the documents expando */ var expanID = 0; /** Cached data for each document */ diff --git a/setup/pub/angular/angular.js b/setup/pub/angular/angular.js index f53b5280f8647..e3a85e3679ac6 100644 --- a/setup/pub/angular/angular.js +++ b/setup/pub/angular/angular.js @@ -7770,7 +7770,7 @@ * caching. * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} * that should abort the request when resolved. - * - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the + * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the * XHR object. See [requests with credentials]https://developer.mozilla.org/en/http_access_control#section_5 * for more information. * - **responseType** - `{string}` - see From e1f53d45065a6fa292e2213b72a2742f81197a0b Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Wed, 18 Jul 2018 06:50:46 +0000 Subject: [PATCH 0355/1171] graphql-ce-120: added store configs schema and resolver --- .../Store/StoreConfigsDataProvider.php | 77 +++++++++++++++++ .../Model/Resolver/StoreConfigs.php | 84 +++++++++++++++++++ .../Magento/StoreGraphQl/etc/schema.graphqls | 29 +++++++ 3 files changed, 190 insertions(+) create mode 100644 app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigsDataProvider.php create mode 100644 app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigs.php diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigsDataProvider.php b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigsDataProvider.php new file mode 100644 index 0000000000000..4fb7dc9cdbb0e --- /dev/null +++ b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigsDataProvider.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\StoreGraphQl\Model\Resolver\Store; + +use Magento\Store\Api\Data\StoreConfigInterface; +use Magento\Store\Api\StoreConfigManagerInterface; + +/** + * StoreConfig field data provider, used for GraphQL request processing. + */ +class StoreConfigsDataProvider +{ + /** + * @var StoreConfigManagerInterface + */ + private $storeConfigManager; + + /** + * @param StoreConfigManagerInterface $storeConfigManager + */ + public function __construct( + StoreConfigManagerInterface $storeConfigManager + ) { + $this->storeConfigManager = $storeConfigManager; + } + + /** + * Get store configs by store codes + * + * @param array $storeCodes + * @return array + */ + public function getStoreConfigsByStoreCodes(array $storeCodes = null) : array + { + $storeConfigs = $this->storeConfigManager->getStoreConfigs($storeCodes); + + return ['items' => $this->hidrateStoreConfigs($storeConfigs)]; + } + + /** + * Transform StoreConfig objects to in array format + * + * @param StoreConfigInterface[] $storeConfigs + * @return array + */ + private function hidrateStoreConfigs(array $storeConfigs) : array + { + $storeConfigsData = []; + /** @var StoreConfigInterface $storeConfig */ + foreach ($storeConfigs as $storeConfig) { + $storeConfigsData[] = [ + 'id' => $storeConfig->getId(), + 'code' => $storeConfig->getCode(), + 'website_id' => $storeConfig->getWebsiteId(), + 'locale' => $storeConfig->getLocale(), + 'base_currency_code' => $storeConfig->getBaseCurrencyCode(), + 'default_display_currency_code' => $storeConfig->getDefaultDisplayCurrencyCode(), + 'timezone' => $storeConfig->getTimezone(), + 'weight_unit' => $storeConfig->getWeightUnit(), + 'base_url' => $storeConfig->getBaseUrl(), + 'base_link_url' => $storeConfig->getBaseLinkUrl(), + 'base_static_url' => $storeConfig->getSecureBaseStaticUrl(), + 'base_media_url' => $storeConfig->getBaseMediaUrl(), + 'secure_base_link_url' => $storeConfig->getSecureBaseLinkUrl(), + 'secure_base_static_url' => $storeConfig->getSecureBaseStaticUrl(), + 'secure_base_media_url' => $storeConfig->getSecureBaseMediaUrl() + ]; + } + + return $storeConfigsData; + } +} diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigs.php b/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigs.php new file mode 100644 index 0000000000000..e03ab72167f1e --- /dev/null +++ b/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigs.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\StoreGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\StoreGraphQl\Model\Resolver\Store\StoreConfigsDataProvider; + +/** + * StoreConfig page field resolver, used for GraphQL request processing. + */ +class StoreConfigs implements ResolverInterface +{ + /** + * @var StoreConfigsDataProvider + */ + private $storeConfigsDataProvider; + + /** + * @var ValueFactory + */ + private $valueFactory; + + /** + * @param StoreConfigsDataProvider $storeConfigsDataProvider + * @param ValueFactory $valueFactory + */ + public function __construct( + StoreConfigsDataProvider $storeConfigsDataProvider, + ValueFactory $valueFactory + ) { + $this->valueFactory = $valueFactory; + $this->storeConfigsDataProvider = $storeConfigsDataProvider; + } + + /** + * {@inheritdoc} + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) : Value { + + $storeCodes = $this->getStoreCodes($args); + $storeConfigsData = $this->storeConfigsDataProvider->getStoreConfigsByStoreCodes($storeCodes); + + $result = function () use ($storeConfigsData) { + return !empty($storeConfigsData) ? $storeConfigsData : []; + }; + + return $this->valueFactory->create($result); + } + + /** + * Retrieve store codes + * + * @param array $args + * @return array + * @throws GraphQlInputException + */ + private function getStoreCodes($args) : array + { + if (isset($args['store_codes'])) { + if (is_array($args['store_codes'])) { + return $args['store_codes']; + } + throw new GraphQlInputException(__('"store codes should contain a valid array')); + } + + return null; + } +} diff --git a/app/code/Magento/StoreGraphQl/etc/schema.graphqls b/app/code/Magento/StoreGraphQl/etc/schema.graphqls index 6eea6da8fd6fa..67515a01fac93 100644 --- a/app/code/Magento/StoreGraphQl/etc/schema.graphqls +++ b/app/code/Magento/StoreGraphQl/etc/schema.graphqls @@ -1,5 +1,11 @@ # Copyright © Magento, Inc. All rights reserved. # See COPYING.txt for license details. +type Query { + storeConfigs ( + storeCodes: [String] @doc(description: "Store Codes of the store configs") +): StoreConfigs + @resolver(class: "Magento\\StoreGraphQl\\Model\\Resolver\\StoreConfigs") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes") +} type Website @doc(description: "The type contains information about a website") { id : Int @doc(description: "The ID number assigned to the website") @@ -9,3 +15,26 @@ type Website @doc(description: "The type contains information about a website") default_group_id : String @doc(description: "The default group id that the website has") is_default : Boolean @doc(description: "Specifies if this is the default website") } + +type StoreConfigs @doc(description: "The Store Configs object") { + items: [StoreConfig] @doc(description: "An array containing store configs") +} + +type StoreConfig @doc(description: "The type contains information about a store config") { + id : Int @doc(description: "The ID number assigned to the store") + code : String @doc(description: "A code assigned to the store to identify it") + website_id : Int @doc(description: "The ID number assigned to the website store belongs") + locale : String @doc(description: "Store locale") + base_currency_code : String @doc(description: "Base currency code") + default_display_currency_code : String @doc(description: "Default display currency code") + timezone : String @doc(description: "Timezone of the store") + weight_unit : String @doc(description: "The unit of weight") + base_url : String @doc(description: "Base URL for the store") + base_link_url : String @doc(description: "Base link URL for the store") + base_static_url : String @doc(description: "Base static URL for the store") + base_media_url : String @doc(description: "Base media URL for the store") + secure_base_url : String @doc(description: "Secure base URL for the store") + secure_base_link_url : String @doc(description: "Secure base link URL for the store") + secure_base_static_url : String @doc(description: "Secure base static URL for the store") + secure_base_media_url : String @doc(description: "Secure base media URL for the store") +} From 082df5808e6bdfeeed794df52e6dbc1e9a26a33b Mon Sep 17 00:00:00 2001 From: Bohdan Shevchenko <fanta1408@gmail.com> Date: Wed, 18 Jul 2018 11:15:10 +0300 Subject: [PATCH 0356/1171] MAGETWO-93121: Automate with MFTF verification dynamic bundle product prices for combination of options --- ...ductPricesForCombinationOfOptionsTest.xml} | 48 +++++++++---------- .../AddSpecialPriceToProductActionGroup.xml | 25 ---------- .../ActionGroup/AdminProductActionGroup.xml | 15 +++++- .../ActionGroup/AdminOrderGridActionGroup.xml | 9 ++-- .../Mftf/Section/AdminOrdersGridSection.xml | 2 +- ...editMemoTotalAfterShippingDiscountTest.xml | 12 ++--- .../Mftf/Section/AdminConfigureTaxSection.xml | 26 +++++----- 7 files changed, 62 insertions(+), 75 deletions(-) rename app/code/Magento/Bundle/Test/Mftf/Test/{VerifyDynamicBundleProductPricesForCombinationOfOptions.xml => VerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml} (83%) delete mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSpecialPriceToProductActionGroup.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/VerifyDynamicBundleProductPricesForCombinationOfOptions.xml b/app/code/Magento/Bundle/Test/Mftf/Test/VerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml similarity index 83% rename from app/code/Magento/Bundle/Test/Mftf/Test/VerifyDynamicBundleProductPricesForCombinationOfOptions.xml rename to app/code/Magento/Bundle/Test/Mftf/Test/VerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml index 473f9a68ff53e..e80a4618e6404 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/VerifyDynamicBundleProductPricesForCombinationOfOptions.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/VerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml @@ -8,14 +8,14 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="VerifyDynamicBundleProductPricesForCombinationOfOptions"> + <test name="VerifyDynamicBundleProductPricesForCombinationOfOptionsTest"> <annotations> <features value="Bundle"/> <title value="Verify dynamic bundle product prices for combination of options"/> <description value="Verify prices for various configurations of Dynamic Bundle product"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-43619"/> - <group value="Bundle"/> + <group value="bundle"/> </annotations> <before> <createData entity="SimpleSubCategory" stepKey="createSubCategory"/> @@ -39,9 +39,9 @@ <!--Add special price to simple product--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPrice"> - <argument name="product" value="$$simpleProduct5$$"/> - </actionGroup> + <amOnPage url="{{AdminProductEditPage.url($$simpleProduct5.id$$)}}" stepKey="openAdminEditPage"/> + <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPrice"/> + <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> <!--Create Bundle product--> <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleProduct"> @@ -144,19 +144,19 @@ <amOnPage url="{{AdminTaxConfigurationPage.url}}" stepKey="goToAdminTaxPage"/> <waitForPageLoad stepKey="waitForTaxConfigLoad"/> - <conditionalClick selector="{{AdminConfigureTaxSection.calculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.calculationSettings}}.open" visible="false" stepKey="openCalculationSettingsTab"/> - <conditionalClick selector="{{AdminConfigureTaxSection.calculationMethodBasedCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.calculationMethodBased}}[disabled='disabled']" visible="true" stepKey="clickCalculationMethodBasedCheckBox"/> - <selectOption userInput="Total" selector="{{AdminConfigureTaxSection.calculationMethodBased}}" stepKey="fillCalculationMethodBased"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationSettingsOpened}}" visible="false" stepKey="openCalculationSettingsTab"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationAlgorithmInherit}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationAlgorithmDisabled}}" visible="true" stepKey="clickCalculationMethodBasedCheckBox"/> + <selectOption userInput="Total" selector="{{AdminConfigureTaxSection.taxCalculationAlgorithm}}" stepKey="fillCalculationMethodBased"/> - <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationBasedCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationBased}}[disabled='disabled']" visible="true" stepKey="clickTaxCalculationBasedCheckBox"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationBasedInherit}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationBasedDisabled}}" visible="true" stepKey="clickTaxCalculationBasedCheckBox"/> <selectOption userInput="Shipping Origin" selector="{{AdminConfigureTaxSection.taxCalculationBased}}" stepKey="fillTaxCalculationBased"/> - <conditionalClick selector="{{AdminConfigureTaxSection.calculationPricesCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.calculationPrices}}[disabled='disabled']" visible="true" stepKey="clickCalculationPricesCheckBox"/> - <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.calculationPrices}}" stepKey="clickCalculationPrices"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationPricesInherit}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationPricesDisabled}}" visible="true" stepKey="clickCalculationPricesCheckBox"/> + <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.taxCalculationPrices}}" stepKey="clickCalculationPrices"/> - <conditionalClick selector="{{AdminConfigureTaxSection.priceDisplaySettings}}" dependentSelector="{{AdminConfigureTaxSection.priceDisplaySettings}}.open" visible="false" stepKey="openPriceDisplaySettings"/> - <conditionalClick selector="{{AdminConfigureTaxSection.displayProductPricesCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.displayProductPrices}}[disabled='disabled']" visible="true" stepKey="clickDisplayProductPricesCheckBox"/> - <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.displayProductPrices}}" stepKey="clickDisplayProductPrices"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxPriceDisplaySettings}}" dependentSelector="{{AdminConfigureTaxSection.taxPriceDisplaySettingsOpened}}" visible="false" stepKey="openPriceDisplaySettings"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxDisplayProductPricesInherit}}" dependentSelector="{{AdminConfigureTaxSection.taxDisplayProductPricesDisabled}}" visible="true" stepKey="clickDisplayProductPricesCheckBox"/> + <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.taxDisplayProductPrices}}" stepKey="clickDisplayProductPrices"/> <!-- Save the settings --> <scrollToTopOfPage stepKey="scrollToTop"/> @@ -171,19 +171,19 @@ <amOnPage url="{{AdminTaxConfigurationPage.url}}" stepKey="goToAdminTaxPage"/> <waitForPageLoad stepKey="waitForTaxConfigLoad"/> - <conditionalClick selector="{{AdminConfigureTaxSection.calculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.calculationSettings}}.open" visible="false" stepKey="openCalculationSettingsTab"/> - <conditionalClick selector="{{AdminConfigureTaxSection.calculationMethodBasedCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.calculationMethodBased}}[disabled='disabled']" visible="true" stepKey="clickCalculationMethodBasedCheckBox"/> - <selectOption userInput="Total" selector="{{AdminConfigureTaxSection.calculationMethodBased}}" stepKey="fillCalculationMethodBased"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationSettings}}.open" visible="false" stepKey="openCalculationSettingsTab"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationAlgorithmInherit}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationAlgorithmDisabled}}" visible="true" stepKey="clickCalculationMethodBasedCheckBox"/> + <selectOption userInput="Total" selector="{{AdminConfigureTaxSection.taxCalculationAlgorithm}}" stepKey="fillCalculationMethodBased"/> - <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationBasedCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationBased}}[disabled='disabled']" visible="true" stepKey="clickTaxCalculationBasedCheckBox"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationBasedInherit}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationBasedDisabled}}" visible="true" stepKey="clickTaxCalculationBasedCheckBox"/> <selectOption userInput="Shipping Address" selector="{{AdminConfigureTaxSection.taxCalculationBased}}" stepKey="fillTaxCalculationBased"/> - <conditionalClick selector="{{AdminConfigureTaxSection.calculationPricesCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.calculationPrices}}[disabled='disabled']" visible="true" stepKey="clickCalculationPricesCheckBox"/> - <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.calculationPrices}}" stepKey="clickCalculationPrices"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationPricesInherit}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationPricesDisabled}}" visible="true" stepKey="clickCalculationPricesCheckBox"/> + <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.taxCalculationPrices}}" stepKey="clickCalculationPrices"/> - <conditionalClick selector="{{AdminConfigureTaxSection.priceDisplaySettings}}" dependentSelector="{{AdminConfigureTaxSection.priceDisplaySettings}}.open" visible="false" stepKey="openPriceDisplaySettings"/> - <conditionalClick selector="{{AdminConfigureTaxSection.displayProductPricesCheckBox}}" dependentSelector="{{AdminConfigureTaxSection.displayProductPrices}}[disabled='disabled']" visible="true" stepKey="clickDisplayProductPricesCheckBox"/> - <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.displayProductPrices}}" stepKey="clickDisplayProductPrices"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxPriceDisplaySettings}}" dependentSelector="{{AdminConfigureTaxSection.taxPriceDisplaySettings}}.open" visible="false" stepKey="openPriceDisplaySettings"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxDisplayProductPricesInherit}}" dependentSelector="{{AdminConfigureTaxSection.taxDisplayProductPricesDisabled}}" visible="true" stepKey="clickDisplayProductPricesCheckBox"/> + <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.taxDisplayProductPrices}}" stepKey="clickDisplayProductPrices"/> <!-- Save the settings --> <scrollToTopOfPage stepKey="scrollToTop"/> @@ -233,5 +233,5 @@ <waitForPageLoad stepKey="waitForPageLoad3"/> <see userInput="From $8.00 Regular Price $10.00" selector="{{StorefrontProductInfoMainSection.priceFrom}}" stepKey="seePriceFromBundle3"/> <see userInput="To $33.58 Regular Price $35.58" selector="{{StorefrontProductInfoMainSection.priceTo}}" stepKey="seePriceToBundle3"/> - </test> + </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSpecialPriceToProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSpecialPriceToProductActionGroup.xml deleted file mode 100644 index c2c6bbeb6d017..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSpecialPriceToProductActionGroup.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AddSpecialPriceToProductActionGroup"> - <arguments> - <argument name="product"/> - <argument name="price" type="string" defaultValue="8"/> - </arguments> - <amOnPage url="{{AdminProductEditPage.url(product.id)}}" stepKey="openAdminEditPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> - <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitSpecialPrice"/> - <fillField userInput="{{price}}" selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="fillSpecialPrice"/> - <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDone"/> - <waitForElementNotVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitForCloseModalWindow"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product." stepKey="seeSaveConfirmation"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index db148b2cf3114..e3d06436f6c6b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -181,6 +181,19 @@ </arguments> <conditionalClick selector="{{AdminProductFormSection.productFormTab('Related Products')}}" dependentSelector="{{AdminProductFormSection.productFormTabState('Related Products', 'closed')}}" visible="true" stepKey="openTab"/> <waitForPageLoad time="30" stepKey="waitForPageLoad"/> - <see selector="{{element}}" userInput="{{expectedText}}" stepKey="AssertText"/> + <see selector="{{element}}" userInput="{{expectedText}}" stepKey="assertText"/> </actionGroup> + + <!--Add special price to product in Admin product page--> + <actionGroup name="AddSpecialPriceToProductActionGroup"> + <arguments> + <argument name="price" type="string" defaultValue="8"/> + </arguments> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> + <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitSpecialPrice"/> + <fillField userInput="{{price}}" selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="fillSpecialPrice"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDone"/> + <waitForElementNotVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitForCloseModalWindow"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml index b74dcccf37c76..df0f56dbf7866 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml @@ -69,10 +69,9 @@ <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> </actionGroup> - <actionGroup name="AdminGridOrdersClearFiltersActionGroup"> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="GoToGridOrdersPage"/> - <waitForPageLoad stepKey="WaitForPageToLoad"/> - <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.enabledFilters}}" visible="true" stepKey="ClickOnButtonToRemoveFiltersIfPresent"/> - <waitForPageLoad stepKey="WaitForPageToLoad2"/> + <actionGroup name="AdminOrdersGridClearFiltersActionGroup"> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToGridOrdersPage"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.enabledFilters}}" visible="true" stepKey="clickOnButtonToRemoveFiltersIfPresent"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml index 54ed3de2d6851..49aae467b7e09 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml @@ -17,7 +17,7 @@ <element name="filters" type="button" selector="button[data-action='grid-filter-expand']" timeout="30"/> <element name="idFilter" type="input" selector=".admin__data-grid-filters input[name='increment_id']"/> <element name="billToNameFilter" type="input" selector=".admin__data-grid-filters input[name='billing_name']"/> - <element name="enabledFilters" type="textarea" selector=".admin__data-grid-header .admin__data-grid-filters-current._show"/> + <element name="enabledFilters" type="block" selector=".admin__data-grid-header .admin__data-grid-filters-current._show"/> <element name="clearFilters" type="button" selector=".admin__data-grid-header [data-action='grid-filter-reset']" timeout="30"/> <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> <element name="rowViewAction" type="button" selector=".data-grid tbody > tr:nth-of-type({{row}}) .action-menu-item" parameterized="true" timeout="30"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index f9d5d9908bf70..4cb72972b4ce2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -23,7 +23,7 @@ <requiredEntity createDataKey="createCategory"/> </createData> <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> - <actionGroup ref="AdminGridOrdersClearFiltersActionGroup" stepKey="clearOrderFilters"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderFilters"/> <actionGroup ref="SetTaxClassForShipping" stepKey="setShippingTaxClass"/> </before> <after> @@ -31,7 +31,7 @@ <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteSalesRule"> <argument name="ruleName" value="{{ApiSalesRule.name}}"/> </actionGroup> - <actionGroup ref="AdminGridOrdersClearFiltersActionGroup" stepKey="clearOrderFilters"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderFilters"/> <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> <deleteData createDataKey="createCategory" stepKey="deleteProduct1"/> <deleteData createDataKey="createProduct" stepKey="deleteCategory1"/> @@ -44,19 +44,15 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{ApiSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsite"/> <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="chooseNotLoggedInCustomerGroup"/> - <!--<selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/>--> <!-- Open the Actions Tab in the Rules Edit page --> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.applyDiscountToShippingLabel}}" stepKey="waitForElementToBeVisible"/> <click selector="{{AdminCartPriceRulesFormSection.applyDiscountToShippingLabel}}" stepKey="enableApplyDiscountToShiping"/> - <seeCheckboxIsChecked selector="{{AdminCartPriceRulesFormSection.applyDiscountToShipping}}" stepKey="DiscountIsAppliedToShiping"/> + <seeCheckboxIsChecked selector="{{AdminCartPriceRulesFormSection.applyDiscountToShipping}}" stepKey="discountIsAppliedToShiping"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="10" stepKey="fillDiscountAmount"/> - <!--<scrollTo selector="{{AdminCartPriceRulesFormSection.applyDiscountToShippingLabel}}" stepKey="scrollToShippingLabel"/>--> - <!--<click selector="{{AdminCartPriceRulesFormSection.applyDiscountToShippingLabel}}" stepKey="enableApplyDiscountToShiping"/>--> - <!--<seeCheckboxIsChecked selector="{{AdminCartPriceRulesFormSection.applyDiscountToShipping}}" stepKey="DiscountIsAppliedToShiping"/>--> <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> @@ -134,6 +130,6 @@ <assertEquals expected='-$15.00' expectedType="string" actual="($grabRefundDiscountValue)" message="notExpectedDiscountOnRefundPage" stepKey="assertDiscountValue1"/> <grabTextFrom selector="{{AdminInvoiceTotalSection.grandTotal}}" stepKey="grabRefundGrandTotal"/> <assertEquals expected="($grabInvoiceGrandTotal)" actual="($grabRefundGrandTotal)" message="RefundGrandTotalMatchesWithInvoiceGrandTotal" stepKey="compareRefundGrandTotalAndInvoiceGrandTotal"/> - </test> + </test> </tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml index ce979f925bc9b..64f8ab074a9f9 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml @@ -12,22 +12,28 @@ <!-- on page /admin/admin/system_config/edit/section/tax/ --> <element name="taxClasses" type="block" selector="#tax_classes-head" timeout="30"/> - <element name="calculationSettings" type="block" selector="#tax_calculation-head" timeout="30"/> - <element name="calculationMethodBased" type="select" selector="#tax_calculation_algorithm"/> - <element name="calculationMethodBasedCheckBox" type="input" selector="#tax_calculation_algorithm_inherit"/> + <element name="taxCalculationSettings" type="block" selector="#tax_calculation-head" timeout="30"/> + <element name="taxCalculationSettingsOpened" type="block" selector="#tax_calculation-head.open" timeout="30"/> + <element name="taxCalculationAlgorithm" type="select" selector="#tax_calculation_algorithm"/> + <element name="taxCalculationAlgorithmDisabled" type="select" selector="#tax_calculation_algorithm[disabled='disabled']"/> + <element name="taxCalculationAlgorithmInherit" type="checkbox" selector="#tax_calculation_algorithm_inherit"/> <element name="taxCalculationBased" type="select" selector="#tax_calculation_based_on"/> - <element name="taxCalculationBasedCheckBox" type="input" selector="#tax_calculation_based_on_inherit"/> - <element name="calculationPrices" type="select" selector="#tax_calculation_price_includes_tax"/> - <element name="calculationPricesCheckBox" type="input" selector="#tax_calculation_price_includes_tax_inherit"/> + <element name="taxCalculationBasedDisabled" type="select" selector="#tax_calculation_based_on[disabled='disabled']"/> + <element name="taxCalculationBasedInherit" type="checkbox" selector="#tax_calculation_based_on_inherit"/> + <element name="taxCalculationPrices" type="select" selector="#tax_calculation_price_includes_tax"/> + <element name="taxCalculationPricesDisabled" type="select" selector="#tax_calculation_price_includes_tax[disabled='disabled']"/> + <element name="taxCalculationPricesInherit" type="checkbox" selector="#tax_calculation_price_includes_tax_inherit"/> <element name="defaultDestination" type="block" selector="#tax_defaults-head" timeout="30"/> <element name="systemValueDefaultState" type="checkbox" selector="#row_tax_defaults_region input[type='checkbox']"/> <element name="dropdownDefaultState" type="select" selector="#row_tax_defaults_region select"/> <element name="defaultPostCode" type="input" selector="#tax_defaults_postcode"/> - <element name="priceDisplaySettings" type="block" selector="#tax_display-head" timeout="30"/> - <element name="displayProductPrices" type="select" selector="#tax_display_type"/> - <element name="displayProductPricesCheckBox" type="input" selector="#tax_display_type_inherit"/> + <element name="taxPriceDisplaySettings" type="block" selector="#tax_display-head" timeout="30"/> + <element name="taxPriceDisplaySettingsOpened" type="block" selector="#tax_display-head.open" timeout="30"/> + <element name="taxDisplayProductPrices" type="select" selector="#tax_display_type"/> + <element name="taxDisplayProductPricesDisabled" type="select" selector="#tax_display_type[disabled='disabled']"/> + <element name="taxDisplayProductPricesInherit" type="checkbox" selector="#tax_display_type_inherit"/> <element name="shoppingCartDisplay" type="block" selector="#tax_cart_display-head" timeout="30"/> <element name="systemValueIncludeTaxTotalCart" type="checkbox" selector="#row_tax_cart_display_grandtotal input[type='checkbox']"/> @@ -44,7 +50,5 @@ <element name="dropdownDisplayTaxSummarySales" type="checkbox" selector="#row_tax_sales_display_full_summary select"/> <element name="systemValueDisplayZeroTaxSales" type="checkbox" selector="#row_tax_sales_display_zero_tax input[type='checkbox']"/> <element name="dropdownDisplayZeroTaxSales" type="checkbox" selector="#row_tax_sales_display_zero_tax select"/> - - <element name="fixedProductSettings" type="block" selector="#tax_weee-head" timeout="30"/> </section> </sections> From 7d6f91ee9e32c863e3afa40c675b3144d5f065d1 Mon Sep 17 00:00:00 2001 From: Bohdan Shevchenko <fanta1408@gmail.com> Date: Wed, 18 Jul 2018 11:58:53 +0300 Subject: [PATCH 0357/1171] MAGETWO-93121: Automate with MFTF verification dynamic bundle product prices for combination of options --- .../Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml index 64f8ab074a9f9..c54c525d8d7ed 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml @@ -27,7 +27,7 @@ <element name="defaultDestination" type="block" selector="#tax_defaults-head" timeout="30"/> <element name="systemValueDefaultState" type="checkbox" selector="#row_tax_defaults_region input[type='checkbox']"/> <element name="dropdownDefaultState" type="select" selector="#row_tax_defaults_region select"/> - <element name="defaultPostCode" type="input" selector="#tax_defaults_postcode"/> + <element name="defaultPostCode" type="checkbox" selector="#tax_defaults_postcode"/> <element name="taxPriceDisplaySettings" type="block" selector="#tax_display-head" timeout="30"/> <element name="taxPriceDisplaySettingsOpened" type="block" selector="#tax_display-head.open" timeout="30"/> From 3ddf34766fdd76ebff3fb8b9b6237f8de2292ca9 Mon Sep 17 00:00:00 2001 From: Bohdan Shevchenko <fanta1408@gmail.com> Date: Wed, 18 Jul 2018 12:03:31 +0300 Subject: [PATCH 0358/1171] MAGETWO-93121: Automate with MFTF verification dynamic bundle product prices for combination of options --- ...fyDynamicBundleProductPricesForCombinationOfOptionsTest.xml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename app/code/Magento/Bundle/Test/Mftf/Test/{VerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml => StoreFrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml} (99%) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/VerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StoreFrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml similarity index 99% rename from app/code/Magento/Bundle/Test/Mftf/Test/VerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml rename to app/code/Magento/Bundle/Test/Mftf/Test/StoreFrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml index e80a4618e6404..6b94232d03031 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/VerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StoreFrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="VerifyDynamicBundleProductPricesForCombinationOfOptionsTest"> + <test name="StoreFrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest"> <annotations> <features value="Bundle"/> <title value="Verify dynamic bundle product prices for combination of options"/> From bcc932091f641d055dd39cb1ca84b4c739d83e36 Mon Sep 17 00:00:00 2001 From: Eugene Shakhsuvarov <ishakhsuvarov@users.noreply.github.com> Date: Wed, 18 Jul 2018 12:32:42 +0300 Subject: [PATCH 0359/1171] Stabilize Travis CI integration tests - Updated test distribution between integration test suites --- dev/travis/before_script.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/travis/before_script.sh b/dev/travis/before_script.sh index 7cf55ca8083f1..f2ba20dc412b6 100755 --- a/dev/travis/before_script.sh +++ b/dev/travis/before_script.sh @@ -13,8 +13,8 @@ case $TEST_SUITE in test_set_list=$(find testsuite/* -maxdepth 1 -mindepth 1 -type d | sort) test_set_count=$(printf "$test_set_list" | wc -l) - test_set_size[1]=$(printf "%.0f" $(echo "$test_set_count*0.12" | bc)) #12% - test_set_size[2]=$(printf "%.0f" $(echo "$test_set_count*0.32" | bc)) #32% + test_set_size[1]=$(printf "%.0f" $(echo "$test_set_count*0.17" | bc)) #17% + test_set_size[2]=$(printf "%.0f" $(echo "$test_set_count*0.27" | bc)) #27% test_set_size[3]=$((test_set_count-test_set_size[1]-test_set_size[2])) #56% echo "Total = ${test_set_count}; Batch #1 = ${test_set_size[1]}; Batch #2 = ${test_set_size[2]}; Batch #3 = ${test_set_size[3]};"; From 209f9bd0d4aad7884ded791c1f75913dd1d411ff Mon Sep 17 00:00:00 2001 From: Bohdan Shevchenko <fanta1408@gmail.com> Date: Wed, 18 Jul 2018 12:58:09 +0300 Subject: [PATCH 0360/1171] MAGETWO-93121: Automate with MFTF verification dynamic bundle product prices for combination of options --- ...fyDynamicBundleProductPricesForCombinationOfOptionsTest.xml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename app/code/Magento/Bundle/Test/Mftf/Test/{StoreFrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml => StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml} (99%) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StoreFrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml similarity index 99% rename from app/code/Magento/Bundle/Test/Mftf/Test/StoreFrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml rename to app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml index 6b94232d03031..49f4974ff8d29 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StoreFrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="StoreFrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest"> + <test name="StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest"> <annotations> <features value="Bundle"/> <title value="Verify dynamic bundle product prices for combination of options"/> From 9f7c5dae5b4833611dfe6530fc7eb45edec61b6c Mon Sep 17 00:00:00 2001 From: Bohdan Shevchenko <fanta1408@gmail.com> Date: Wed, 18 Jul 2018 13:06:50 +0300 Subject: [PATCH 0361/1171] MAGETWO-93121: Automate with MFTF verification dynamic bundle product prices for combination of options --- ...yDynamicBundleProductPricesForCombinationOfOptionsTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml index 49f4974ff8d29..9652f07128dfe 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml @@ -171,7 +171,7 @@ <amOnPage url="{{AdminTaxConfigurationPage.url}}" stepKey="goToAdminTaxPage"/> <waitForPageLoad stepKey="waitForTaxConfigLoad"/> - <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationSettings}}.open" visible="false" stepKey="openCalculationSettingsTab"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationSettingsOpened}" visible="false" stepKey="openCalculationSettingsTab"/> <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationAlgorithmInherit}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationAlgorithmDisabled}}" visible="true" stepKey="clickCalculationMethodBasedCheckBox"/> <selectOption userInput="Total" selector="{{AdminConfigureTaxSection.taxCalculationAlgorithm}}" stepKey="fillCalculationMethodBased"/> @@ -181,7 +181,7 @@ <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationPricesInherit}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationPricesDisabled}}" visible="true" stepKey="clickCalculationPricesCheckBox"/> <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.taxCalculationPrices}}" stepKey="clickCalculationPrices"/> - <conditionalClick selector="{{AdminConfigureTaxSection.taxPriceDisplaySettings}}" dependentSelector="{{AdminConfigureTaxSection.taxPriceDisplaySettings}}.open" visible="false" stepKey="openPriceDisplaySettings"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxPriceDisplaySettings}}" dependentSelector="{{AdminConfigureTaxSection.taxPriceDisplaySettingsOpened}}" visible="false" stepKey="openPriceDisplaySettings"/> <conditionalClick selector="{{AdminConfigureTaxSection.taxDisplayProductPricesInherit}}" dependentSelector="{{AdminConfigureTaxSection.taxDisplayProductPricesDisabled}}" visible="true" stepKey="clickDisplayProductPricesCheckBox"/> <selectOption userInput="Excluding Tax" selector="{{AdminConfigureTaxSection.taxDisplayProductPrices}}" stepKey="clickDisplayProductPrices"/> From 98030665af1c285c7b20fcba19f77a557205dc47 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Wed, 18 Jul 2018 13:53:17 +0300 Subject: [PATCH 0362/1171] MAGETWO-93277: Can't place order with Braintree Credit Card from Admin with Basic Fraud Protection --- .../Test/TestCase/CreateOrderBackendTest.xml | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOrderBackendTest.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOrderBackendTest.xml index acf15c0e28252..70582077ed29d 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOrderBackendTest.xml +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOrderBackendTest.xml @@ -64,28 +64,5 @@ <constraint name="Magento\Sales\Test\Constraint\AssertCaptureInCommentsHistory" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> </variation> - <variation name="CreateOrderBackendTestBraintreeVariation3" summary="Checkout with Braintree Credit Card from Admin (Basic Fraud Protection)" ticketId="MAGETWO-46470"> - <data name="tag" xsi:type="string">test_type:3rd_party_test, severity:S0</data> - <data name="products/0" xsi:type="string">catalogProductSimple::product_10_dollar</data> - <data name="products/1" xsi:type="string">configurableProduct::with_one_option</data> - <data name="products/2" xsi:type="string">bundleProduct::bundle_fixed_100_dollar_product</data> - <data name="customer/dataset" xsi:type="string">default</data> - <data name="taxRule" xsi:type="string">us_ca_ny_rule</data> - <data name="billingAddress/dataset" xsi:type="string">US_address_1_without_email</data> - <data name="saveAddress" xsi:type="string">No</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="prices" xsi:type="array"> - <item name="grandTotal" xsi:type="string">145.98</item> - </data> - <data name="payment/method" xsi:type="string">braintree</data> - <data name="paymentForm" xsi:type="string">braintree</data> - <data name="creditCard/dataset" xsi:type="string">visa_braintree_fraud_rejected</data> - <data name="configData" xsi:type="string">braintree</data> - <data name="status" xsi:type="string">Processing</data> - <constraint name="Magento\Shipping\Test\Constraint\AssertShipmentSuccessCreateMessage" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCorrect" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> - </variation> </testCase> </config> From f73b1193bbdf4ecea6a92b53a2eab2d1bed6f6eb Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 18 Jul 2018 15:10:12 +0300 Subject: [PATCH 0363/1171] Revert backward incompatible changes --- .../Magento/Shipping/Model/Carrier/AbstractCarrierInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierInterface.php b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierInterface.php index adf7ad1ed2ec8..ee59fae657ea5 100644 --- a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierInterface.php +++ b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierInterface.php @@ -90,7 +90,7 @@ public function checkAvailableShipCountries(\Magento\Framework\DataObject $reque * @return $this|\Magento\Framework\DataObject|boolean * @api */ - public function processAdditionalValidation(\Magento\Framework\DataObject $request); + public function proccessAdditionalValidation(\Magento\Framework\DataObject $request); /** * Determine whether current carrier enabled for activity From 3884baccd6a50fddff31b66cc8bef8a28159d468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20P=C3=A9rez?= <javier.perez.m@gmail.com> Date: Wed, 18 Jul 2018 14:10:33 +0200 Subject: [PATCH 0364/1171] Fixed: urldecode user & pass from FTP connection string recoding to pass travis coding standards --- lib/internal/Magento/Framework/System/Ftp.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/System/Ftp.php b/lib/internal/Magento/Framework/System/Ftp.php index c56f5c669d3c9..827e524a62464 100644 --- a/lib/internal/Magento/Framework/System/Ftp.php +++ b/lib/internal/Magento/Framework/System/Ftp.php @@ -109,8 +109,9 @@ public function validateConnectionString($string) } // Decode user & password strings from URL - if ( array_key_exists('user', $data) ) $data['user'] = urldecode($data['user']); - if ( array_key_exists('pass', $data) ) $data['pass'] = urldecode($data['pass']); + foreach (array_intersect(array_keys($data), ['user','pass']) as $key) { + $data[$key] = urldecode($data[$key]); + } return $data; } From d8c4d867a81fb085ef0883ec533f8b5332c66d82 Mon Sep 17 00:00:00 2001 From: Bohdan Shevchenko <fanta1408@gmail.com> Date: Wed, 18 Jul 2018 15:19:43 +0300 Subject: [PATCH 0365/1171] MAGETWO-93121: Automate with MFTF verification dynamic bundle product prices for combination of options --- ...ifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml index 9652f07128dfe..1b1a46d1c8ba4 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml @@ -171,7 +171,7 @@ <amOnPage url="{{AdminTaxConfigurationPage.url}}" stepKey="goToAdminTaxPage"/> <waitForPageLoad stepKey="waitForTaxConfigLoad"/> - <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationSettingsOpened}" visible="false" stepKey="openCalculationSettingsTab"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationSettingsOpened}}" visible="false" stepKey="openCalculationSettingsTab"/> <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationAlgorithmInherit}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationAlgorithmDisabled}}" visible="true" stepKey="clickCalculationMethodBasedCheckBox"/> <selectOption userInput="Total" selector="{{AdminConfigureTaxSection.taxCalculationAlgorithm}}" stepKey="fillCalculationMethodBased"/> From e165e6f1680077ffbb9f02fdb0693b14e8f7f639 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Wed, 18 Jul 2018 10:05:26 -0500 Subject: [PATCH 0366/1171] MC-3080: Admin can configure swatches display in configuration - Fix review feedback --- .../Test/Mftf/Section/AdminCreateProductAttributeSection.xml | 1 - .../Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml | 4 ++-- .../Swatches/Test/Mftf/Section/AdminColorPickerSection.xml | 4 ++-- .../Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 42437f34b980a..be0e50ba5a3a5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -16,7 +16,6 @@ <element name="DefaultValue" type="input" selector="#default_value_text"/> <element name="Save" type="button" selector="#save"/> <element name="SaveAndEdit" type="button" selector="#save_and_edit_button" timeout="30"/> - <element name="successMessage" type="block" selector="div.message.message-success.success"/> <element name="TinyMCE4" type="button" selector="//span[text()='Default Value']/parent::label/following-sibling::div//div[@class='mce-branding-powered-by']"/> <element name="checkIfTabOpen" selector="//div[@id='advanced_fieldset-wrapper' and not(contains(@class,'opened'))]" type="button"/> </section> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml index 57bf3452ab131..bd71fa01c7645 100644 --- a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml @@ -14,8 +14,8 @@ <argument name="hexColor" type="string" defaultValue="e74c3c"/> </arguments> <!-- This 6x backspace stuff is some magic that is necessary to interact with this field correctly --> - <pressKey selector="{{AdminColorPickerSection.hex(nthColorPicker)}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,'{{hexColor}}']" stepKey="fillHex1"/> - <click selector="{{AdminColorPickerSection.submit(nthColorPicker)}}" stepKey="submitColor1"/> + <pressKey selector="{{AdminColorPickerSection.hexByIndex(nthColorPicker)}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,'{{hexColor}}']" stepKey="fillHex1"/> + <click selector="{{AdminColorPickerSection.submitByIndex(nthColorPicker)}}" stepKey="submitColor1"/> </actionGroup> <actionGroup name="assertSwatchColor"> <arguments> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml index 07552397e091f..7b9bc422f7430 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml @@ -9,7 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminColorPickerSection"> - <element name="hex" type="input" selector="//body/div[@class='colorpicker'][{{var}}]/div[@class='colorpicker_hex']/input" parameterized="true"/> - <element name="submit" type="button" selector="//body/div[@class='colorpicker'][{{var}}]/div[@class='colorpicker_submit']" parameterized="true"/> + <element name="hexByIndex" type="input" selector="//body/div[@class='colorpicker'][{{var}}]/div[@class='colorpicker_hex']/input" parameterized="true"/> + <element name="submitByIndex" type="button" selector="//body/div[@class='colorpicker'][{{var}}]/div[@class='colorpicker_submit']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml index 54004992fda84..7ca5bcf9e259a 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml @@ -77,7 +77,7 @@ <!-- Save --> <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit1"/> - <waitForElementVisible selector="{{AttributePropertiesSection.successMessage}}" stepKey="waitForSuccess"/> + <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccess"/> <!-- Assert that the Save was successful after round trip to server --> <actionGroup ref="assertSwatchColor" stepKey="assertSwatch1"> From ae91c80ba7799da64acb7c61f3e3edacf83eb672 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Wed, 18 Jul 2018 10:12:34 -0500 Subject: [PATCH 0367/1171] MAGETWO-91433: ProductListing: Grid view is getting changed to List view when user adding product from wishlist section. --- .../StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml | 2 +- .../StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist => app/code/Magento/Wishlist/Test/Mftf}/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml (94%) rename {dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist => app/code/Magento/Wishlist/Test/Mftf}/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml (94%) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml similarity index 94% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml rename to app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml index c44ee1e062681..b20ba3a153fc5 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml @@ -6,7 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> <test name="StorefrontAddProductsToCartFromWishlistUsingSidebarTest"> <annotations> <title value="Add products from the wishlist to the cart using the sidebar."/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml similarity index 94% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml rename to app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml index c8170012c2a95..f6deb803967ba 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Wishlist/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml @@ -6,7 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> <test name="StorefrontRemoveProductsFromWishlistUsingSidebarTest"> <annotations> <title value="Remove products from the wishlist using the sidebar."/> From dc056bf519f245000670ce2355c76e3846029d78 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 18 Jul 2018 10:16:02 -0500 Subject: [PATCH 0368/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - adding optimization --- .../Item/ItemProductResolver.php | 22 ++++++++++-------- .../Item/ItemProductResolver.php | 23 +++++++++++-------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php index 4ac47fbe0405a..970e37422e2eb 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -47,17 +47,19 @@ public function getFinalProduct(ItemInterface $item) : ProductInterface * or if child thumbnail is not available. */ $parentProduct = $item->getProduct(); - $configValue = $this->scopeConfig->getValue( - self::CONFIG_THUMBNAIL_SOURCE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - + $finalProduct = $parentProduct; $childProduct = $this->getChildProduct($item); - $childThumb = $childProduct->getData('thumbnail'); - $finalProduct = - ($configValue == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumb || $childThumb == 'no_selection') - ? $parentProduct - : $childProduct; + if ($childProduct !== $parentProduct) { + $configValue = $this->scopeConfig->getValue( + self::CONFIG_THUMBNAIL_SOURCE, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + $childThumb = $childProduct->getData('thumbnail'); + $finalProduct = + ($configValue == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumb || $childThumb == 'no_selection') + ? $parentProduct + : $childProduct; + } return $finalProduct; } diff --git a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php index bbfe76b39719a..87053664d9bac 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -46,17 +46,22 @@ public function getFinalProduct(ItemInterface $item) : ProductInterface * Show grouped product thumbnail if it must be always shown according to the related setting in system config * or if child product thumbnail is not available. */ - $configValue = $this->scopeConfig->getValue( - self::CONFIG_THUMBNAIL_SOURCE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); + $childProduct = $item->getProduct(); - $childThumb = $childProduct->getData('thumbnail'); + $finalProduct = $childProduct; + $parentProduct = $this->getParentProduct($item); + if ($childProduct !== $parentProduct) { + $configValue = $this->scopeConfig->getValue( + self::CONFIG_THUMBNAIL_SOURCE, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + $childThumb = $childProduct->getData('thumbnail'); - $finalProduct = - ($configValue == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumb || $childThumb == 'no_selection') - ? $this->getParentProduct($item) - : $childProduct; + $finalProduct = + ($configValue == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumb || $childThumb == 'no_selection') + ? $parentProduct + : $childProduct; + } return $finalProduct; } From 5f530ef00799c33d06d2204e1b5e31c2d95cdea1 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Wed, 18 Jul 2018 10:16:51 -0500 Subject: [PATCH 0369/1171] MC-3080: Admin can configure swatches display in configuration - Removed "body" from my new XPATH selectors --- .../Swatches/Test/Mftf/Section/AdminColorPickerSection.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml index 7b9bc422f7430..772b724b6648d 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml @@ -9,7 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminColorPickerSection"> - <element name="hexByIndex" type="input" selector="//body/div[@class='colorpicker'][{{var}}]/div[@class='colorpicker_hex']/input" parameterized="true"/> - <element name="submitByIndex" type="button" selector="//body/div[@class='colorpicker'][{{var}}]/div[@class='colorpicker_submit']" parameterized="true"/> + <element name="hexByIndex" type="input" selector="//div[@class='colorpicker'][{{var}}]/div[@class='colorpicker_hex']/input" parameterized="true"/> + <element name="submitByIndex" type="button" selector="//div[@class='colorpicker'][{{var}}]/div[@class='colorpicker_submit']" parameterized="true"/> </section> </sections> From 9d27d05aca456c5cfd0a8136eee4239d40cb5333 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Tue, 17 Jul 2018 11:33:10 -0500 Subject: [PATCH 0370/1171] MC-2294: Image still displayed after deleting it from Media Gallery Storage - Supporting changes to media browser --- .../view/base/web/js/form/element/abstract.js | 2 +- .../base/web/js/form/element/image-uploader.js | 17 +++++++++++------ dev/tests/acceptance/.htaccess | 11 +++++++++++ lib/web/mage/adminhtml/browser.js | 12 ++++++++++++ 4 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 dev/tests/acceptance/.htaccess diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js index 5177b4a378d69..1de0077b6ced3 100755 --- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js @@ -407,7 +407,7 @@ define([ this.bubble('error', message); //TODO: Implement proper result propagation for form - if (!isValid) { + if (this.source && !isValid) { this.source.set('params.invalid', true); } diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js index 69c9fb74cbce1..47df6687090f4 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js @@ -43,17 +43,22 @@ define([ */ addFileFromMediaGallery: function (imageUploader, e) { var $buttonEl = $(e.target), + fileId = $buttonEl.data('id'), fileSize = $buttonEl.data('size'), fileMimeType = $buttonEl.data('mime-type'), filePathname = $buttonEl.val(), fileBasename = filePathname.split('/').pop(); - this.addFile({ - type: fileMimeType, - name: fileBasename, - size: fileSize, - url: filePathname - }); + if (filePathname.length > 0) { + this.addFile({ + id: fileId, + type: fileMimeType, + name: fileBasename, + size: fileSize, + url: filePathname + }); + } + }, /** diff --git a/dev/tests/acceptance/.htaccess b/dev/tests/acceptance/.htaccess new file mode 100644 index 0000000000000..e6bb9dd7ddab9 --- /dev/null +++ b/dev/tests/acceptance/.htaccess @@ -0,0 +1,11 @@ +############################################## +## Allow access to command.php + <FilesMatch "command.php"> + <IfVersion < 2.4> + order allow,deny + allow from all + </IfVersion> + <IfVersion >= 2.4> + Require all granted + </IfVersion> + </FilesMatch> diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 26d6679bb3ce5..9ae300e7beda3 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -276,6 +276,7 @@ define([ } else { targetEl .val(data) + .data('id', fileRow.attr('id')) .data('size', fileRow.data('size')) .data('mime-type', fileRow.data('mime-type')) .trigger('change'); @@ -433,8 +434,19 @@ define([ context: self.element, showLoader: true }).done($.proxy(function () { + var targetEl = self.getTargetElement(); + self.reload(); self.element.find('#delete_files').toggleClass(self.options.hidden, true); + + if ($.inArray(targetEl.data('id'), ids) > -1) { + targetEl + .val('') + .data('id', null) + .data('mime-type', null) + .data('size', null) + .trigger('change'); + } }, this)); }, From 948bdf5d56be91512c77cf400f4bb2778bb4b1d7 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Tue, 17 Jul 2018 14:18:59 -0500 Subject: [PATCH 0371/1171] MC-2294: Image still displayed after deleting it from Media Gallery Storage - New event approach --- .../base/web/js/form/element/image-uploader.js | 17 +++++++---------- lib/web/mage/adminhtml/browser.js | 11 +++-------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js index 47df6687090f4..037690bf6be50 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js @@ -49,16 +49,13 @@ define([ filePathname = $buttonEl.val(), fileBasename = filePathname.split('/').pop(); - if (filePathname.length > 0) { - this.addFile({ - id: fileId, - type: fileMimeType, - name: fileBasename, - size: fileSize, - url: filePathname - }); - } - + this.addFile({ + id: fileId, + type: fileMimeType, + name: fileBasename, + size: fileSize, + url: filePathname + }); }, /** diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 9ae300e7beda3..e9e8cc6ffaef8 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -439,14 +439,9 @@ define([ self.reload(); self.element.find('#delete_files').toggleClass(self.options.hidden, true); - if ($.inArray(targetEl.data('id'), ids) > -1) { - targetEl - .val('') - .data('id', null) - .data('mime-type', null) - .data('size', null) - .trigger('change'); - } + $(window).trigger('fileDeleted.mediabrowser', { + ids: ids + }); }, this)); }, From 310074f94d4d2477e1f5f2905d1ff5c92f53ce85 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Tue, 17 Jul 2018 14:30:36 -0500 Subject: [PATCH 0372/1171] MC-2294: Image still displayed after deleting it from Media Gallery Storage - Removed line --- lib/web/mage/adminhtml/browser.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index e9e8cc6ffaef8..717098110ceb5 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -434,8 +434,6 @@ define([ context: self.element, showLoader: true }).done($.proxy(function () { - var targetEl = self.getTargetElement(); - self.reload(); self.element.find('#delete_files').toggleClass(self.options.hidden, true); From add5e3d4cc09327f8ffdbca652d660d429399b9c Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Tue, 17 Jul 2018 16:07:41 -0500 Subject: [PATCH 0373/1171] MC-2294: Image still displayed after deleting it from Media Gallery Storage - Added test and cleaned up --- app/code/Magento/Ui/view/base/web/js/form/element/abstract.js | 2 +- dev/tests/integration/testsuite/Magento/PageBuilder | 1 + .../code/Magento/Ui/base/js/form/element/image-uploader.test.js | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) create mode 120000 dev/tests/integration/testsuite/Magento/PageBuilder diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js index 1de0077b6ced3..5177b4a378d69 100755 --- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js @@ -407,7 +407,7 @@ define([ this.bubble('error', message); //TODO: Implement proper result propagation for form - if (this.source && !isValid) { + if (!isValid) { this.source.set('params.invalid', true); } diff --git a/dev/tests/integration/testsuite/Magento/PageBuilder b/dev/tests/integration/testsuite/Magento/PageBuilder new file mode 120000 index 0000000000000..0dea7beaaae28 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PageBuilder @@ -0,0 +1 @@ +/Users/nathsmit/Sites/pagebuilder/magento2-page-builder/dev/tests/integration/testsuite/Magento/PageBuilder/ \ No newline at end of file diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/image-uploader.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/image-uploader.test.js index 31d1c1ce27e96..e7745103db356 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/image-uploader.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/image-uploader.test.js @@ -35,6 +35,7 @@ define([ spyOn(component, 'addFile'); $el.data({ + id: 'ABC123--', 'size': 1024, 'mime-type': 'image/png' }); @@ -46,6 +47,7 @@ define([ }); expect(component.addFile).toHaveBeenCalledWith({ + id: 'ABC123--', type: 'image/png', name: 'something.png', url: '/pub/media/something.png', From 01f4b400f22c6052f132ba226362ae98201c941f Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Tue, 17 Jul 2018 16:24:26 -0500 Subject: [PATCH 0374/1171] MC-2294: Image still displayed after deleting it from Media Gallery Storage - Removed htaccess --- dev/tests/acceptance/.htaccess | 11 ----------- dev/tests/integration/testsuite/Magento/PageBuilder | 1 - 2 files changed, 12 deletions(-) delete mode 100644 dev/tests/acceptance/.htaccess delete mode 120000 dev/tests/integration/testsuite/Magento/PageBuilder diff --git a/dev/tests/acceptance/.htaccess b/dev/tests/acceptance/.htaccess deleted file mode 100644 index e6bb9dd7ddab9..0000000000000 --- a/dev/tests/acceptance/.htaccess +++ /dev/null @@ -1,11 +0,0 @@ -############################################## -## Allow access to command.php - <FilesMatch "command.php"> - <IfVersion < 2.4> - order allow,deny - allow from all - </IfVersion> - <IfVersion >= 2.4> - Require all granted - </IfVersion> - </FilesMatch> diff --git a/dev/tests/integration/testsuite/Magento/PageBuilder b/dev/tests/integration/testsuite/Magento/PageBuilder deleted file mode 120000 index 0dea7beaaae28..0000000000000 --- a/dev/tests/integration/testsuite/Magento/PageBuilder +++ /dev/null @@ -1 +0,0 @@ -/Users/nathsmit/Sites/pagebuilder/magento2-page-builder/dev/tests/integration/testsuite/Magento/PageBuilder/ \ No newline at end of file From ab3d0f7ef21a5543ec892e7fc272eb36b41a57b3 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Wed, 18 Jul 2018 11:28:55 -0500 Subject: [PATCH 0375/1171] MAGETWO-90927: Fix warning with count function on product save --- .../Catalog/Controller/Adminhtml/Product/Save.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index 84837262a8154..dea01df7f4398 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -194,6 +194,7 @@ public function execute() */ private function handleImageRemoveError($postData, $productId) { + if (isset($postData['product']['media_gallery']['images'])) { $removedImagesAmount = 0; foreach ($postData['product']['media_gallery']['images'] as $image) { @@ -204,10 +205,12 @@ private function handleImageRemoveError($postData, $productId) if ($removedImagesAmount) { $expectedImagesAmount = count($postData['product']['media_gallery']['images']) - $removedImagesAmount; $product = $this->productRepository->getById($productId); - if ($expectedImagesAmount != count($product->getMediaGallery('images'))) { - $this->messageManager->addNoticeMessage( - __('The image cannot be removed as it has been assigned to the other image role') - ); + if (is_array($product->getMediaGallery('images'))) { + if ($expectedImagesAmount != count($product->getMediaGallery('images'))) { + $this->messageManager->addNoticeMessage( + __('The image cannot be removed as it has been assigned to the other image role') + ); + } } } } From 8a2b9f893dbed0a223046c91dd9b57a333a81941 Mon Sep 17 00:00:00 2001 From: Dave Macaulay <dmacaulay@magento.com> Date: Wed, 18 Jul 2018 18:33:43 +0200 Subject: [PATCH 0376/1171] MC-3153: Apply full width to custom product attributes - Improve API of attribures product block to allow override of the should display condition --- .../Catalog/Block/Product/View/Attributes.php | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/View/Attributes.php b/app/code/Magento/Catalog/Block/Product/View/Attributes.php index b353e477a056c..4a43701d7caac 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Attributes.php +++ b/app/code/Magento/Catalog/Block/Product/View/Attributes.php @@ -67,12 +67,11 @@ public function getProduct() } /** - * $excludeAttr is optional array of attribute codes to - * exclude them from additional data array + * $excludeAttr is optional array of attribute codes to exclude them from additional data array * * @param array $excludeAttr * @return array - * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @throws \Magento\Framework\Exception\LocalizedException */ public function getAdditionalData(array $excludeAttr = []) { @@ -80,7 +79,7 @@ public function getAdditionalData(array $excludeAttr = []) $product = $this->getProduct(); $attributes = $product->getAttributes(); foreach ($attributes as $attribute) { - if ($attribute->getIsVisibleOnFront() && !in_array($attribute->getAttributeCode(), $excludeAttr)) { + if ($this->shouldDisplay($attribute, $excludeAttr)) { $value = $attribute->getFrontend()->getValue($product); if ($value instanceof Phrase) { @@ -100,4 +99,18 @@ public function getAdditionalData(array $excludeAttr = []) } return $data; } + + /** + * Determine if we should display the attribute on the front-end + * + * @param \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute + * @param array $excludeAttr + * @return bool + */ + protected function shouldDisplay( + \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute, + array $excludeAttr + ) { + return ($attribute->getIsVisibleOnFront() && !in_array($attribute->getAttributeCode(), $excludeAttr)); + } } From c19a46be30ba644f2a24ccd4434a292360a52792 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Wed, 18 Jul 2018 16:27:49 -0300 Subject: [PATCH 0377/1171] Replacing Usage of Deprecated Methods for Message Manager. --- .../Catalog/Controller/Adminhtml/Category/Delete.php | 6 +++--- .../Controller/Adminhtml/Product/Action/Attribute.php | 2 +- .../Adminhtml/Product/Action/Attribute/Save.php | 6 +++--- .../Adminhtml/Product/Action/Attribute/Validate.php | 2 +- .../Controller/Adminhtml/Product/Attribute/Delete.php | 8 ++++---- .../Controller/Adminhtml/Product/Attribute/Edit.php | 4 ++-- .../Catalog/Controller/Adminhtml/Product/Duplicate.php | 4 ++-- .../Magento/Catalog/Controller/Adminhtml/Product/Edit.php | 4 ++-- .../Catalog/Controller/Adminhtml/Product/Group/Save.php | 4 ++-- .../Catalog/Controller/Adminhtml/Product/Set/Delete.php | 4 ++-- .../Catalog/Controller/Adminhtml/Product/Set/Save.php | 6 +++--- .../Catalog/Controller/Adminhtml/Product/Validate.php | 2 +- .../Magento/Catalog/Controller/Product/Compare/Clear.php | 6 +++--- .../Magento/Catalog/Controller/Product/Compare/Remove.php | 2 +- app/code/Magento/Catalog/Controller/Product/View.php | 5 ++++- 15 files changed, 34 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Delete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Delete.php index 8f570e35989cb..0a54475b15f9c 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Delete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Delete.php @@ -44,12 +44,12 @@ public function execute() $this->_eventManager->dispatch('catalog_controller_category_delete', ['category' => $category]); $this->_auth->getAuthStorage()->setDeletedPath($category->getPath()); $this->categoryRepository->delete($category); - $this->messageManager->addSuccess(__('You deleted the category.')); + $this->messageManager->addSuccessMessage(__('You deleted the category.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); return $resultRedirect->setPath('catalog/*/edit', ['_current' => true]); } catch (\Exception $e) { - $this->messageManager->addError(__('Something went wrong while trying to delete the category.')); + $this->messageManager->addErrorMessage(__('Something went wrong while trying to delete the category.')); return $resultRedirect->setPath('catalog/*/edit', ['_current' => true]); } } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute.php index 775097c5eba1d..ca7652ebb43b5 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute.php @@ -54,7 +54,7 @@ protected function _validateProducts() } if ($error) { - $this->messageManager->addError($error); + $this->messageManager->addErrorMessage($error); } return !$error; diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index 82496446aef9f..0fbf9054ef1bd 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -192,7 +192,7 @@ public function execute() $this->_eventManager->dispatch('catalog_product_to_website_change', ['products' => $productIds]); } - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('A total of %1 record(s) were updated.', count($this->attributeHelper->getProductIds())) ); @@ -205,9 +205,9 @@ public function execute() $this->_productPriceIndexerProcessor->reindexList($this->attributeHelper->getProductIds()); } } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('Something went wrong while updating the product(s) attributes.') ); 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 bb18436be6102..a873f08d082d7 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 @@ -68,7 +68,7 @@ public function execute() $response->setError(true); $response->setMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('Something went wrong while updating the product(s) attributes.') ); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete.php index cc5a658a9296d..bef6aee0e2afd 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete.php @@ -21,23 +21,23 @@ public function execute() // entity type check $model->load($id); if ($model->getEntityTypeId() != $this->_entityTypeId) { - $this->messageManager->addError(__('We can\'t delete the attribute.')); + $this->messageManager->addErrorMessage(__('We can\'t delete the attribute.')); return $resultRedirect->setPath('catalog/*/'); } try { $model->delete(); - $this->messageManager->addSuccess(__('You deleted the product attribute.')); + $this->messageManager->addSuccessMessage(__('You deleted the product attribute.')); return $resultRedirect->setPath('catalog/*/'); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); return $resultRedirect->setPath( 'catalog/*/edit', ['attribute_id' => $this->getRequest()->getParam('attribute_id')] ); } } - $this->messageManager->addError(__('We can\'t find an attribute to delete.')); + $this->messageManager->addErrorMessage(__('We can\'t find an attribute to delete.')); return $resultRedirect->setPath('catalog/*/'); } } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Edit.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Edit.php index fd97aaa50389e..a99cbdbade181 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Edit.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Edit.php @@ -25,14 +25,14 @@ public function execute() $model->load($id); if (!$model->getId()) { - $this->messageManager->addError(__('This attribute no longer exists.')); + $this->messageManager->addErrorMessage(__('This attribute no longer exists.')); $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('catalog/*/'); } // entity type check if ($model->getEntityTypeId() != $this->_entityTypeId) { - $this->messageManager->addError(__('This attribute cannot be edited.')); + $this->messageManager->addErrorMessage(__('This attribute cannot be edited.')); $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('catalog/*/'); } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Duplicate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Duplicate.php index 7e8b03a66f603..63e52eead064c 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Duplicate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Duplicate.php @@ -43,11 +43,11 @@ public function execute() $product = $this->productBuilder->build($this->getRequest()); try { $newProduct = $this->productCopier->copy($product); - $this->messageManager->addSuccess(__('You duplicated the product.')); + $this->messageManager->addSuccessMessage(__('You duplicated the product.')); $resultRedirect->setPath('catalog/*/edit', ['_current' => true, 'id' => $newProduct->getId()]); } catch (\Exception $e) { $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $resultRedirect->setPath('catalog/*/edit', ['_current' => true]); } return $resultRedirect; diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Edit.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Edit.php index 838bce7272250..1b9316a95ad59 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Edit.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Edit.php @@ -52,12 +52,12 @@ public function execute() if (($productId && !$product->getEntityId())) { /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); - $this->messageManager->addError(__('This product doesn\'t exist.')); + $this->messageManager->addErrorMessage(__('This product doesn\'t exist.')); return $resultRedirect->setPath('catalog/*/'); } elseif ($productId === 0) { /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); - $this->messageManager->addError(__('Invalid product id. Should be numeric value greater than 0')); + $this->messageManager->addErrorMessage(__('Invalid product id. Should be numeric value greater than 0')); return $resultRedirect->setPath('catalog/*/'); } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Group/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Group/Save.php index 4909e22775e55..8a5f375f2b706 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Group/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Group/Save.php @@ -29,12 +29,12 @@ public function execute() ); if ($model->itemExists()) { - $this->messageManager->addError(__('A group with the same name already exists.')); + $this->messageManager->addErrorMessage(__('A group with the same name already exists.')); } else { try { $model->save(); } catch (\Exception $e) { - $this->messageManager->addError(__('Something went wrong while saving this group.')); + $this->messageManager->addErrorMessage(__('Something went wrong while saving this group.')); } } } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Delete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Delete.php index b49a4dabe223c..f2695311732f0 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Delete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Delete.php @@ -36,10 +36,10 @@ public function execute() $resultRedirect = $this->resultRedirectFactory->create(); try { $this->attributeSetRepository->deleteById($setId); - $this->messageManager->addSuccess(__('The attribute set has been removed.')); + $this->messageManager->addSuccessMessage(__('The attribute set has been removed.')); $resultRedirect->setPath('catalog/*/'); } catch (\Exception $e) { - $this->messageManager->addError(__('We can\'t delete this set right now.')); + $this->messageManager->addErrorMessage(__('We can\'t delete this set right now.')); $resultRedirect->setUrl($this->_redirect->getRedirectUrl($this->getUrl('*'))); } return $resultRedirect; diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Save.php index dfddcf7e92b97..c5dd9ce6d8e77 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Save.php @@ -127,15 +127,15 @@ public function execute() $model->initFromSkeleton($this->getRequest()->getParam('skeleton_set')); } $model->save(); - $this->messageManager->addSuccess(__('You saved the attribute set.')); + $this->messageManager->addSuccessMessage(__('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()); + $this->messageManager->addErrorMessage($e->getMessage()); $hasError = true; } catch (\Exception $e) { - $this->messageManager->addException($e, __('Something went wrong while saving the attribute set.')); + $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the attribute set.')); $hasError = true; } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Validate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Validate.php index 63f46fd32e6f7..e131bfe38c546 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Validate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Validate.php @@ -137,7 +137,7 @@ public function execute() $response->setError(true); $response->setMessages([$e->getMessage()]); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $layout = $this->layoutFactory->create(); $layout->initMessages(); $response->setError(true); diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Clear.php b/app/code/Magento/Catalog/Controller/Product/Compare/Clear.php index 30470d13f002d..568fbf1d05677 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Clear.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Clear.php @@ -30,12 +30,12 @@ public function execute() try { $items->clear(); - $this->messageManager->addSuccess(__('You cleared the comparison list.')); + $this->messageManager->addSuccessMessage(__('You cleared the comparison list.')); $this->_objectManager->get(\Magento\Catalog\Helper\Product\Compare::class)->calculate(); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Something went wrong clearing the comparison list.')); + $this->messageManager->addExceptionMessage($e, __('Something went wrong clearing the comparison list.')); } /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php b/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php index fadb94761a236..2acbe5ce4d582 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php @@ -44,7 +44,7 @@ public function execute() $item->delete(); $productName = $this->_objectManager->get(\Magento\Framework\Escaper::class) ->escapeHtml($product->getName()); - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('You removed product %1 from the comparison list.', $productName) ); $this->_eventManager->dispatch( diff --git a/app/code/Magento/Catalog/Controller/Product/View.php b/app/code/Magento/Catalog/Controller/Product/View.php index 4c577eb897589..ed437361fddd3 100644 --- a/app/code/Magento/Catalog/Controller/Product/View.php +++ b/app/code/Magento/Catalog/Controller/Product/View.php @@ -78,13 +78,16 @@ public function execute() if ($this->getRequest()->isPost() && $this->getRequest()->getParam(self::PARAM_NAME_URL_ENCODED)) { $product = $this->_initProduct(); + if (!$product) { return $this->noProductRedirect(); } + if ($specifyOptions) { $notice = $product->getTypeInstance()->getSpecifyOptionMessage(); - $this->messageManager->addNotice($notice); + $this->messageManager->addNoticeMessage($notice); } + if ($this->getRequest()->isAjax()) { $this->getResponse()->representJson( $this->_objectManager->get(\Magento\Framework\Json\Helper\Data::class)->jsonEncode([ From 77c5e85e329bd5e80270436c1335809e00808114 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Thu, 19 Jul 2018 01:09:13 +0530 Subject: [PATCH 0378/1171] Fixed typo mistake in testsuite --- .../_files/create_products.php | 2 +- .../Magento/Bundle/_files/multiple_products.php | 10 +++++----- .../ResourceModel/_files/website_attribute_sync.php | 2 +- .../Magento/Catalog/_files/category_duplicates.php | 2 +- .../Magento/Catalog/_files/multiple_mixed_products.php | 6 +++--- .../Magento/Catalog/_files/multiple_products.php | 6 +++--- .../_files/multiple_products_with_few_out_of_stock.php | 2 +- .../products_with_layered_navigation_attribute.php | 6 +++--- .../_files/product_export_with_product_links_data.php | 2 +- .../testsuite/Magento/ImportExport/_files/product.php | 2 +- .../_files/products_with_layered_navigation_swatch.php | 6 +++--- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/create_products.php b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/create_products.php index f625ee3288d5d..ef5877612a3b9 100644 --- a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/create_products.php +++ b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/create_products.php @@ -15,7 +15,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['qty' => 100, 'is_in_stock' => 1, 'manage_stock' => 1]) ->setIsObjectNew(true) ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php index 52abd7d6ff8eb..662b69c89bc6d 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php @@ -28,7 +28,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, 'qty' => 100, @@ -59,7 +59,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, 'qty' => 50, @@ -85,7 +85,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, 'qty' => 140, @@ -116,7 +116,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, 'qty' => 20, @@ -147,7 +147,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, 'qty' => 15, diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/_files/website_attribute_sync.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/_files/website_attribute_sync.php index 9c55370156235..96a537f123d92 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/_files/website_attribute_sync.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/_files/website_attribute_sync.php @@ -126,7 +126,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(AttributeStatus::STATUS_ENABLED) ->setWebsiteIds([$website->getId()]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); $product = $productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_duplicates.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_duplicates.php index 2873ae7ccf399..d3825675b7244 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_duplicates.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_duplicates.php @@ -48,7 +48,7 @@ \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED )->setWebsiteIds( [1] -)->setCateroryIds( +)->setCategoryIds( [] )->setStockData( ['qty' => 100, 'is_in_stock' => 1, 'manage_stock' => 1] diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_mixed_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_mixed_products.php index 9f2045db66108..7d05f30337cc2 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_mixed_products.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_mixed_products.php @@ -186,7 +186,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->setSpecialPrice('5.99') ->save(); @@ -211,7 +211,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 50, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->setSpecialPrice('15.99') ->save(); @@ -231,7 +231,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 140, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->setSpecialPrice('25.99') ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products.php index fa6e826eec70e..dcdbed7562fdb 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products.php @@ -25,7 +25,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->setSpecialPrice('5.99') ->save(); @@ -50,7 +50,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 50, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->setSpecialPrice('15.99') ->save(); @@ -70,7 +70,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 140, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->setSpecialPrice('25.99') ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_few_out_of_stock.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_few_out_of_stock.php index 4235fcf11f9f8..13b9870f24f66 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_few_out_of_stock.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_few_out_of_stock.php @@ -53,7 +53,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setCategoryIds([300]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 0, 'is_qty_decimal' => 0, 'is_in_stock' => 0]) ->setSpecialPrice('5.99') diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attribute.php index 091e64dbe6a4f..48c47c9988d59 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attribute.php @@ -87,7 +87,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->setSpecialPrice('5.99') ->save(); @@ -112,7 +112,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 50, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->setSpecialPrice('15.99') ->save(); @@ -132,7 +132,7 @@ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 140, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->setSpecialPrice('25.99') ->save(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_product_links_data.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_product_links_data.php index 330d9511dcdb9..5a6b1720f5a35 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_product_links_data.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_product_links_data.php @@ -34,7 +34,7 @@ \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED )->setWebsiteIds( [1] -)->setCateroryIds( +)->setCategoryIds( [] )->setStockData( ['qty' => 100, 'is_in_stock' => 1, 'manage_stock' => 1] diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/_files/product.php b/dev/tests/integration/testsuite/Magento/ImportExport/_files/product.php index b1a239ccd2246..102d1d1268afd 100644 --- a/dev/tests/integration/testsuite/Magento/ImportExport/_files/product.php +++ b/dev/tests/integration/testsuite/Magento/ImportExport/_files/product.php @@ -23,7 +23,7 @@ \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED )->setWebsiteIds( [1] -)->setCateroryIds( +)->setCategoryIds( [] )->setStockData( ['qty' => 100, 'is_in_stock' => 1, 'manage_stock' => 1] diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/products_with_layered_navigation_swatch.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/products_with_layered_navigation_swatch.php index e2df7206b27d9..215c32c4b521d 100644 --- a/dev/tests/integration/testsuite/Magento/Swatches/_files/products_with_layered_navigation_swatch.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/products_with_layered_navigation_swatch.php @@ -123,7 +123,7 @@ function ($values, $index) use ($optionsPerAttribute) { ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->setSpecialPrice('5.99') ->save(); @@ -148,7 +148,7 @@ function ($values, $index) use ($optionsPerAttribute) { ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 50, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->setSpecialPrice('15.99') ->save(); @@ -168,7 +168,7 @@ function ($values, $index) use ($optionsPerAttribute) { ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) - ->setCateroryIds([]) + ->setCategoryIds([]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 140, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->setSpecialPrice('25.99') ->save(); From c7a44f45f6b89bb79875216a29c8ec9361088c90 Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Wed, 18 Jul 2018 14:47:17 -0500 Subject: [PATCH 0379/1171] MAGETWO-90995: Error on info:language:list call --- .../Command/InfoBackupsListCommand.php | 22 ++++++++++++++----- .../Command/InfoCurrencyListCommand.php | 19 +++++++++++----- .../Command/InfoLanguageListCommand.php | 19 +++++++++++----- .../Command/InfoTimezoneListCommand.php | 19 +++++++++++----- .../Command/InfoBackupsListCommandTest.php | 9 ++++---- .../Command/InfoCurrencyListCommandTest.php | 9 ++++---- .../Command/InfoLanguageListCommandTest.php | 9 ++++---- .../Command/InfoTimezoneListCommandTest.php | 9 ++++---- 8 files changed, 74 insertions(+), 41 deletions(-) diff --git a/setup/src/Magento/Setup/Console/Command/InfoBackupsListCommand.php b/setup/src/Magento/Setup/Console/Command/InfoBackupsListCommand.php index 178102aa0b3b7..94337dd0742e3 100644 --- a/setup/src/Magento/Setup/Console/Command/InfoBackupsListCommand.php +++ b/setup/src/Magento/Setup/Console/Command/InfoBackupsListCommand.php @@ -11,8 +11,10 @@ use Magento\Framework\Filesystem\Driver\File; use Magento\Framework\Setup\BackupRollback; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\TableFactory; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Magento\Framework\App\ObjectManager; /** * Command prints list of available backup files @@ -33,16 +35,24 @@ class InfoBackupsListCommand extends Command */ private $directoryList; + /** + * @var TableFactory + */ + private $tableHelperFactory; + /** * @param DirectoryList $directoryList * @param File $file + * @param TableFactory $tableHelperFactory */ public function __construct( DirectoryList $directoryList, - File $file + File $file, + TableFactory $tableHelperFactory = null ) { $this->directoryList = $directoryList; $this->file = $file; + $this->tableHelperFactory = $tableHelperFactory ?: ObjectManager::getInstance()->create(TableFactory::class); parent::__construct(); } @@ -90,14 +100,14 @@ protected function execute(InputInterface $input, OutputInterface $output) return \Magento\Framework\Console\Cli::RETURN_SUCCESS; } $output->writeln("<info>Showing backup files in $backupsDir.</info>"); - /** @var \Symfony\Component\Console\Helper\Table $table */ - $table = $this->getHelperSet()->get('table'); - $table->setHeaders(['Backup Filename', 'Backup Type']); + /** @var \Symfony\Component\Console\Helper\Table $tableHelper */ + $tableHelper = $this->tableHelperFactory->create(['output' => $output]); + $tableHelper->setHeaders(['Backup Filename', 'Backup Type']); asort($tempTable); foreach ($tempTable as $key => $value) { - $table->addRow([$key, $value]); + $tableHelper->addRow([$key, $value]); } - $table->render($output); + $tableHelper->render(); } else { $output->writeln('<info>No backup files found.</info>'); } diff --git a/setup/src/Magento/Setup/Console/Command/InfoCurrencyListCommand.php b/setup/src/Magento/Setup/Console/Command/InfoCurrencyListCommand.php index d673fc85c1ef1..91cfb5e7f6b5c 100644 --- a/setup/src/Magento/Setup/Console/Command/InfoCurrencyListCommand.php +++ b/setup/src/Magento/Setup/Console/Command/InfoCurrencyListCommand.php @@ -6,10 +6,12 @@ namespace Magento\Setup\Console\Command; +use Symfony\Component\Console\Helper\TableFactory; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Command\Command; use Magento\Framework\Setup\Lists; +use Magento\Framework\App\ObjectManager; /** * Command prints list of available currencies @@ -23,12 +25,19 @@ class InfoCurrencyListCommand extends Command */ private $lists; + /** + * @var TableFactory + */ + private $tableHelperFactory; + /** * @param Lists $lists + * @param TableFactory $tableHelperFactory */ - public function __construct(Lists $lists) + public function __construct(Lists $lists, TableFactory $tableHelperFactory = null) { $this->lists = $lists; + $this->tableHelperFactory = $tableHelperFactory ?: ObjectManager::getInstance()->create(TableFactory::class); parent::__construct(); } @@ -48,14 +57,14 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $table = $this->getHelperSet()->get('table'); - $table->setHeaders(['Currency', 'Code']); + $tableHelper = $this->tableHelperFactory->create(['output' => $output]); + $tableHelper->setHeaders(['Currency', 'Code']); foreach ($this->lists->getCurrencyList() as $key => $currency) { - $table->addRow([$currency, $key]); + $tableHelper->addRow([$currency, $key]); } - $table->render($output); + $tableHelper->render(); return \Magento\Framework\Console\Cli::RETURN_SUCCESS; } } diff --git a/setup/src/Magento/Setup/Console/Command/InfoLanguageListCommand.php b/setup/src/Magento/Setup/Console/Command/InfoLanguageListCommand.php index 88d1bdd5601d5..8950bd5edb2fa 100644 --- a/setup/src/Magento/Setup/Console/Command/InfoLanguageListCommand.php +++ b/setup/src/Magento/Setup/Console/Command/InfoLanguageListCommand.php @@ -6,8 +6,10 @@ namespace Magento\Setup\Console\Command; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Setup\Lists; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\TableFactory; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -23,12 +25,19 @@ class InfoLanguageListCommand extends Command */ private $lists; + /** + * @var TableFactory + */ + private $tableHelperFactory; + /** * @param Lists $lists + * @param TableFactory $tableHelperFactory */ - public function __construct(Lists $lists) + public function __construct(Lists $lists, TableFactory $tableHelperFactory = null) { $this->lists = $lists; + $this->tableHelperFactory = $tableHelperFactory ?: ObjectManager::getInstance()->create(TableFactory::class); parent::__construct(); } @@ -48,14 +57,14 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $table = $this->getHelperSet()->get('table'); - $table->setHeaders(['Language', 'Code']); + $tableHelper = $this->tableHelperFactory->create(['output' => $output]); + $tableHelper->setHeaders(['Language', 'Code']); foreach ($this->lists->getLocaleList() as $key => $locale) { - $table->addRow([$locale, $key]); + $tableHelper->addRow([$locale, $key]); } - $table->render($output); + $tableHelper->render(); return \Magento\Framework\Console\Cli::RETURN_SUCCESS; } } diff --git a/setup/src/Magento/Setup/Console/Command/InfoTimezoneListCommand.php b/setup/src/Magento/Setup/Console/Command/InfoTimezoneListCommand.php index 95b4cd27bbd3a..2ff1d228dfe24 100644 --- a/setup/src/Magento/Setup/Console/Command/InfoTimezoneListCommand.php +++ b/setup/src/Magento/Setup/Console/Command/InfoTimezoneListCommand.php @@ -10,6 +10,8 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Command\Command; use Magento\Framework\Setup\Lists; +use Symfony\Component\Console\Helper\TableFactory; +use Magento\Framework\App\ObjectManager; /** * Command prints list of available timezones @@ -23,12 +25,19 @@ class InfoTimezoneListCommand extends Command */ private $lists; + /** + * @var TableFactory + */ + private $tableHelperFactory; + /** * @param Lists $lists + * @param TableFactory $tableHelperFactory */ - public function __construct(Lists $lists) + public function __construct(Lists $lists, TableFactory $tableHelperFactory = null) { $this->lists = $lists; + $this->tableHelperFactory = $tableHelperFactory ?: ObjectManager::getInstance()->create(TableFactory::class); parent::__construct(); } @@ -48,14 +57,14 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $table = $this->getHelperSet()->get('table'); - $table->setHeaders(['Timezone', 'Code']); + $tableHelper = $this->tableHelperFactory->create(['output' => $output]); + $tableHelper->setHeaders(['Timezone', 'Code']); foreach ($this->lists->getTimezoneList() as $key => $timezone) { - $table->addRow([$timezone, $key]); + $tableHelper->addRow([$timezone, $key]); } - $table->render($output); + $tableHelper->render(); return \Magento\Framework\Console\Cli::RETURN_SUCCESS; } } diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoBackupsListCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoBackupsListCommandTest.php index ff4722ca0fd4a..1329324b74015 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoBackupsListCommandTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoBackupsListCommandTest.php @@ -16,9 +16,9 @@ public function testExecute() $table = $this->createMock(\Symfony\Component\Console\Helper\Table::class); $table->expects($this->once())->method('setHeaders')->with(['Backup Filename', 'Backup Type']); $table->expects($this->once())->method('addRow')->with(['backupFile_media.tgz', 'media']); - /** @var \Symfony\Component\Console\Helper\HelperSet|\PHPUnit_Framework_MockObject_MockObject $helperSet */ - $helperSet = $this->createMock(\Symfony\Component\Console\Helper\HelperSet::class); - $helperSet->expects($this->once())->method('get')->with('table')->will($this->returnValue($table)); + /** @var \Symfony\Component\Console\Helper\TableFactory|\PHPUnit_Framework_MockObject_MockObject $helperSet */ + $tableFactoryMock = $this->createMock(\Symfony\Component\Console\Helper\TableFactory::class); + $tableFactoryMock->expects($this->once())->method('create')->will($this->returnValue($table)); /** @var \Magento\Framework\App\Filesystem\DirectoryList * |\PHPUnit_Framework_MockObject_MockObject $directoryList */ @@ -29,8 +29,7 @@ public function testExecute() $file->expects($this->once()) ->method('readDirectoryRecursively') ->will($this->returnValue(['backupFile_media.tgz'])); - $command = new InfoBackupsListCommand($directoryList, $file); - $command->setHelperSet($helperSet); + $command = new InfoBackupsListCommand($directoryList, $file, $tableFactoryMock); $commandTester = new CommandTester($command); $commandTester->execute([]); $expected = 'Showing backup files in '; diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoCurrencyListCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoCurrencyListCommandTest.php index e3e0a1de71727..caee4af9c7fc4 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoCurrencyListCommandTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoCurrencyListCommandTest.php @@ -21,15 +21,14 @@ public function testExecute() $table->expects($this->once())->method('setHeaders')->with(['Currency', 'Code']); $table->expects($this->once())->method('addRow')->with(['Currency description', 'CUR']); - /** @var \Symfony\Component\Console\Helper\HelperSet|\PHPUnit_Framework_MockObject_MockObject $helperSet */ - $helperSet = $this->createMock(\Symfony\Component\Console\Helper\HelperSet::class); - $helperSet->expects($this->once())->method('get')->with('table')->will($this->returnValue($table)); + /** @var \Symfony\Component\Console\Helper\TableFactory|\PHPUnit_Framework_MockObject_MockObject $helperSet */ + $tableFactoryMock = $this->createMock(\Symfony\Component\Console\Helper\TableFactory::class); + $tableFactoryMock->expects($this->once())->method('create')->will($this->returnValue($table)); /** @var \Magento\Framework\Setup\Lists|\PHPUnit_Framework_MockObject_MockObject $list */ $list = $this->createMock(\Magento\Framework\Setup\Lists::class); $list->expects($this->once())->method('getCurrencyList')->will($this->returnValue($currencies)); - $command = new InfoCurrencyListCommand($list); - $command->setHelperSet($helperSet); + $command = new InfoCurrencyListCommand($list, $tableFactoryMock); $commandTester = new CommandTester($command); $commandTester->execute([]); } diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoLanguageListCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoLanguageListCommandTest.php index f1622b59c4593..d9c899b178c37 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoLanguageListCommandTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoLanguageListCommandTest.php @@ -21,15 +21,14 @@ public function testExecute() $table->expects($this->once())->method('setHeaders')->with(['Language', 'Code']); $table->expects($this->once())->method('addRow')->with(['Language description', 'LNG']); - /** @var \Symfony\Component\Console\Helper\HelperSet|\PHPUnit_Framework_MockObject_MockObject $helperSet */ - $helperSet = $this->createMock(\Symfony\Component\Console\Helper\HelperSet::class); - $helperSet->expects($this->once())->method('get')->with('table')->will($this->returnValue($table)); + /** @var \Symfony\Component\Console\Helper\TableFactory|\PHPUnit_Framework_MockObject_MockObject $helperSet */ + $tableFactoryMock = $this->createMock(\Symfony\Component\Console\Helper\TableFactory::class); + $tableFactoryMock->expects($this->once())->method('create')->will($this->returnValue($table)); /** @var \Magento\Framework\Setup\Lists|\PHPUnit_Framework_MockObject_MockObject $list */ $list = $this->createMock(\Magento\Framework\Setup\Lists::class); $list->expects($this->once())->method('getLocaleList')->will($this->returnValue($languages)); - $command = new InfoLanguageListCommand($list); - $command->setHelperSet($helperSet); + $command = new InfoLanguageListCommand($list, $tableFactoryMock); $commandTester = new CommandTester($command); $commandTester->execute([]); } diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoTimezoneListCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoTimezoneListCommandTest.php index 860862f304971..80871ecf634ed 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoTimezoneListCommandTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoTimezoneListCommandTest.php @@ -21,15 +21,14 @@ public function testExecute() $table->expects($this->once())->method('setHeaders')->with(['Timezone', 'Code']); $table->expects($this->once())->method('addRow')->with(['timezone description', 'timezone']); - /** @var \Symfony\Component\Console\Helper\HelperSet|\PHPUnit_Framework_MockObject_MockObject $helperSet */ - $helperSet = $this->createMock(\Symfony\Component\Console\Helper\HelperSet::class); - $helperSet->expects($this->once())->method('get')->with('table')->will($this->returnValue($table)); + /** @var \Symfony\Component\Console\Helper\TableFactory|\PHPUnit_Framework_MockObject_MockObject $helperSet */ + $tableFactoryMock = $this->createMock(\Symfony\Component\Console\Helper\TableFactory::class); + $tableFactoryMock->expects($this->once())->method('create')->will($this->returnValue($table)); /** @var \Magento\Framework\Setup\Lists|\PHPUnit_Framework_MockObject_MockObject $list */ $list = $this->createMock(\Magento\Framework\Setup\Lists::class); $list->expects($this->once())->method('getTimezoneList')->will($this->returnValue($timezones)); - $command = new InfoTimezoneListCommand($list); - $command->setHelperSet($helperSet); + $command = new InfoTimezoneListCommand($list, $tableFactoryMock); $commandTester = new CommandTester($command); $commandTester->execute([]); } From 5229cd6eed7b5e08174268245f23158513feabd9 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Wed, 18 Jul 2018 19:52:55 +0000 Subject: [PATCH 0380/1171] graphql-ce-120: added api-functional test fixed resolver and dataprovider --- .../Model/Resolver/Store/StoreConfigsDataProvider.php | 5 ++++- .../Magento/StoreGraphQl/Model/Resolver/StoreConfigs.php | 8 ++++---- app/code/Magento/StoreGraphQl/etc/schema.graphqls | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigsDataProvider.php b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigsDataProvider.php index 4fb7dc9cdbb0e..0d7ce3aa1555f 100644 --- a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigsDataProvider.php +++ b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigsDataProvider.php @@ -39,7 +39,9 @@ public function getStoreConfigsByStoreCodes(array $storeCodes = null) : array { $storeConfigs = $this->storeConfigManager->getStoreConfigs($storeCodes); - return ['items' => $this->hidrateStoreConfigs($storeConfigs)]; + return [ + 'items' => $this->hidrateStoreConfigs($storeConfigs) + ]; } /** @@ -66,6 +68,7 @@ private function hidrateStoreConfigs(array $storeConfigs) : array 'base_link_url' => $storeConfig->getBaseLinkUrl(), 'base_static_url' => $storeConfig->getSecureBaseStaticUrl(), 'base_media_url' => $storeConfig->getBaseMediaUrl(), + 'secure_base_url' => $storeConfig->getSecureBaseUrl(), 'secure_base_link_url' => $storeConfig->getSecureBaseLinkUrl(), 'secure_base_static_url' => $storeConfig->getSecureBaseStaticUrl(), 'secure_base_media_url' => $storeConfig->getSecureBaseMediaUrl() diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigs.php b/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigs.php index e03ab72167f1e..c1daef97d5c60 100644 --- a/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigs.php +++ b/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigs.php @@ -70,11 +70,11 @@ public function resolve( * @return array * @throws GraphQlInputException */ - private function getStoreCodes($args) : array + private function getStoreCodes($args) { - if (isset($args['store_codes'])) { - if (is_array($args['store_codes'])) { - return $args['store_codes']; + if (isset($args['storeCodes'])) { + if (is_array($args['storeCodes'])) { + return $args['storeCodes']; } throw new GraphQlInputException(__('"store codes should contain a valid array')); } diff --git a/app/code/Magento/StoreGraphQl/etc/schema.graphqls b/app/code/Magento/StoreGraphQl/etc/schema.graphqls index 67515a01fac93..2b45190c540eb 100644 --- a/app/code/Magento/StoreGraphQl/etc/schema.graphqls +++ b/app/code/Magento/StoreGraphQl/etc/schema.graphqls @@ -4,7 +4,7 @@ type Query { storeConfigs ( storeCodes: [String] @doc(description: "Store Codes of the store configs") ): StoreConfigs - @resolver(class: "Magento\\StoreGraphQl\\Model\\Resolver\\StoreConfigs") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes") + @resolver(class: "Magento\\StoreGraphQl\\Model\\Resolver\\StoreConfigs") @doc(description: "The store configs query") } type Website @doc(description: "The type contains information about a website") { From af4b258a78a4772d4256b7c6e5912ddf326d93de Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Wed, 18 Jul 2018 19:59:16 +0000 Subject: [PATCH 0381/1171] graphql-ce-120: added api functional test --- .../Store/StoreConfigsResolverTest.php | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigsResolverTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigsResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigsResolverTest.php new file mode 100644 index 0000000000000..5c3566ffe2e19 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigsResolverTest.php @@ -0,0 +1,107 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Store; + +use Magento\Store\Api\Data\StoreConfigInterface; +use Magento\Store\Api\StoreConfigManagerInterface; +use Magento\TestFramework\ObjectManager; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test the GraphQL endpoint's StoreConfigs query + */ +class StoreConfigsResolverTest extends GraphQlAbstract +{ + + /** @var ObjectManager */ + private $objectManager; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + } + + /** + * @magentoApiDataFixture Magento/Store/_files/store.php + */ + public function testStoreConfigsFilteredByStoreCode() + { + $storeCode = 'test'; + /** @var StoreConfigManagerInterface $storeConfigsManager */ + $storeConfigsManager = $this->objectManager->get(StoreConfigManagerInterface::class); + /** @var StoreConfigInterface $storeConfig */ + $storeConfig = current($storeConfigsManager->getStoreConfigs([$storeCode])); + $query + = <<<QUERY +{ + storeConfigs(storeCodes: "{$storeCode}"){ + items{ + id, + code, + website_id, + locale, + base_currency_code, + default_display_currency_code, + timezone, + weight_unit, + base_url, + base_link_url, + base_static_url, + base_media_url, + secure_base_url, + secure_base_link_url, + secure_base_static_url, + secure_base_media_url + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('storeConfigs', $response); + $this->assertEquals(1, count($response['storeConfigs']['items'])); + $responseStoreConfig = current($response['storeConfigs']['items']); + $this->assertEquals($storeConfig->getId(), $responseStoreConfig['id']); + $this->assertEquals($storeConfig->getCode(), $responseStoreConfig['code']); + $this->assertEquals($storeConfig->getLocale(), $responseStoreConfig['locale']); + $this->assertEquals($storeConfig->getBaseCurrencyCode(), $responseStoreConfig['base_currency_code']); + $this->assertEquals( + $storeConfig->getDefaultDisplayCurrencyCode(), + $responseStoreConfig['default_display_currency_code'] + ); + $this->assertEquals($storeConfig->getTimezone(), $responseStoreConfig['timezone']); + $this->assertEquals($storeConfig->getWeightUnit(), $responseStoreConfig['weight_unit']); + $this->assertEquals($storeConfig->getBaseUrl(), $responseStoreConfig['base_url']); + $this->assertEquals($storeConfig->getBaseLinkUrl(), $responseStoreConfig['base_link_url']); + $this->assertEquals($storeConfig->getBaseStaticUrl(), $responseStoreConfig['base_static_url']); + $this->assertEquals($storeConfig->getBaseMediaUrl(), $responseStoreConfig['base_media_url']); + $this->assertEquals($storeConfig->getSecureBaseUrl(), $responseStoreConfig['secure_base_url']); + $this->assertEquals($storeConfig->getSecureBaseLinkUrl(), $responseStoreConfig['secure_base_link_url']); + $this->assertEquals($storeConfig->getSecureBaseStaticUrl(), $responseStoreConfig['secure_base_static_url']); + $this->assertEquals($storeConfig->getSecureBaseMediaUrl(), $responseStoreConfig['secure_base_media_url']); + } + + /** + * @magentoApiDataFixture Magento/Store/_files/store.php + */ + public function testGetStoreConfigsWithoutStoreCodes() + { + $query + = <<<QUERY +{ + storeConfigs{ + items{ + id + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('storeConfigs', $response); + $this->assertEquals(2, count($response['storeConfigs']['items'])); + } +} From 0b3ab15b7234693709ebb0071c29edeced248318 Mon Sep 17 00:00:00 2001 From: Devagouda Patil <depatil@Devagoudas-MacBook-Pro.local> Date: Wed, 18 Jul 2018 15:04:52 -0500 Subject: [PATCH 0382/1171] MAGETWO-67627: CMS Page does not save when same url key with hierarchy for Multi-store -Updated static noNameSpaceSchema Location respectively to match new location --- .../Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml | 2 +- .../Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml | 2 +- .../Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml | 2 +- app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml | 2 +- .../Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml | 2 +- app/code/Magento/Store/Test/Mftf/Data/StoreData.xml | 2 +- app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml | 2 +- .../Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml | 2 +- .../Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml | 2 +- .../Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml index 1f4eda7e2c34a..2225d3d34a655 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> <actionGroup name="CreateNewPageWithAllValues"> <arguments> <argument name="PageTitle" type="string"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml index 97b80e244a44a..690ad9881c7fc 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> <actionGroup name="DeletePageByUrlKeyActionGroup"> <arguments> <argument name="UrlKey" type="string"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml index e86144262238a..e2c4f48f4ff9b 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="CmsNewPageHierarchySection"> <element name="header" type="button" selector="div[data-index=hierarchy]" timeout="30"/> <element name="selectHierarchy" type="button" selector="//a/span[contains(text(),'{{var1}}')]" parameterized="true" timeout="30"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml index 821e6b1b455eb..456de55b49171 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="CmsNewPagePiwSection"> <element name="header" type="button" selector="div[data-index=websites]" timeout="30"/> <element name="selectStoreView" type="select" selector="//option[contains(text(),'{{var1}}')]" parameterized="true"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml index 3f9ac9f9dbffc..2f28aa46af65b 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="CmsPagesPageActionsSection"> <element name="filterButton" type="input" selector="//button[text()='Filters']"/> <element name="URLKey" type="input" selector="//div[@class='admin__form-field-control']/input[@name='identifier']"/> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index aa96e6976bb3f..4e3c724572e79 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultStore" type="store"> <data key="name">Default Store View</data> <data key="code">default</data> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml index 989ee9ad7a6ad..8c293bc22f2e8 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultStoreGroup" type="group"> <data key="name">Main Website Store</data> <data key="code">main_website_store</data> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml index fe778711b0a87..a3b5d1e616319 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminNewStoreViewActionsSection"> <element name="backButton" type="button" selector="#back" timeout="30"/> <element name="delete" type="button" selector="#delete" timeout="30"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml index 2103aac32f038..703abea8cfd0d 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminNewWebsiteActionsSection"> <element name="saveWebsite" type="button" selector="#save" timeout="60"/> </section> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml index 8cd19e806c9e9..6dc766c0c02da 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminStoreGroupActionsSection"> <element name="saveButton" type="button" selector="#save" timeout="60" /> </section> From a765ec76e6590039f0441beb26afa356a5b84c6d Mon Sep 17 00:00:00 2001 From: carstenpfeifer <carsten.pfeifer2@gmail.com> Date: Wed, 18 Jul 2018 22:19:48 +0200 Subject: [PATCH 0383/1171] magento-engcom/import-export-improvements#103: Add validation error when a multiselect attribute is supplied with duplicate values. --- .../Magento/CatalogImportExport/Model/Import/Product.php | 3 ++- .../Model/Import/Product/RowValidatorInterface.php | 2 ++ .../CatalogImportExport/Model/Import/Product/Validator.php | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 097db293ab77c..6c3bef2a52b0c 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -294,7 +294,8 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity ValidatorInterface::ERROR_MEDIA_PATH_NOT_ACCESSIBLE => 'Imported resource (image) does not exist in the local media storage', ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE => 'Imported resource (image) could not be downloaded from external resource due to timeout or access permissions', ValidatorInterface::ERROR_INVALID_WEIGHT => 'Product weight is invalid', - ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Url key: \'%s\' was already generated for an item with the SKU: \'%s\'. You need to specify the unique URL key manually' + ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Url key: \'%s\' was already generated for an item with the SKU: \'%s\'. You need to specify the unique URL key manually', + ValidatorInterface::ERROR_DUPLICATE_MULTISELECT_VALUES => "Value for multiselect attribute %s contains duplicated values", ]; //@codingStandardsIgnoreEnd diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php index 17f7fae28ba75..f41596ad185a6 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php @@ -85,6 +85,8 @@ interface RowValidatorInterface extends \Magento\Framework\Validator\ValidatorIn const ERROR_DUPLICATE_URL_KEY = 'duplicatedUrlKey'; + const ERROR_DUPLICATE_MULTISELECT_VALUES = 'duplicatedMultiselectValues'; + /** * Value that means all entities (e.g. websites, groups etc.) */ diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index e1a6188551c0c..2aa2105991883 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -219,6 +219,12 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData) break; } } + + $uniqueValues = array_unique($values); + if (count($uniqueValues) != count($values)) { + $valid = false; + $this->_addMessages([RowValidatorInterface::ERROR_DUPLICATE_MULTISELECT_VALUES]); + } break; case 'datetime': $val = trim($rowData[$attrCode]); From d45e4f8d376e200e176446765670b5d2d84cd8a5 Mon Sep 17 00:00:00 2001 From: carstenpfeifer <carsten.pfeifer2@gmail.com> Date: Wed, 18 Jul 2018 22:19:54 +0200 Subject: [PATCH 0384/1171] magento-engcom/import-export-improvements#103: Add tests for new validation. --- .../Model/Import/Product/ValidatorTest.php | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php index 1179783fdd3f9..64b925955519e 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php @@ -167,6 +167,26 @@ public function attributeValidationProvider() ['product_type' => 'any', 'attribute_code' => 'Option 1|Option 2'], true ], + [ + Import::BEHAVIOR_APPEND, + ['is_required' => true, 'type' => 'multiselect', + 'options' => ['option 1' => 0, 'option 2' => 1, 'option 3']], + ['product_type' => 'any', 'attribute_code' => 'Option 1|Option 2|Option 1'], + false + ], + [ + Import::BEHAVIOR_APPEND, + ['is_required' => true, 'type' => 'multiselect', + 'options' => ['option 1' => 0, 'option 2' => 1, 'option 3']], + ['product_type' => 'any', 'attribute_code' => 'Option 3|Option 3|Option 3|Option 1'], + false + ], + [ + Import::BEHAVIOR_APPEND, + ['is_required' => true, 'type' => 'multiselect', 'options' => ['option 1' => 0]], + ['product_type' => 'any', 'attribute_code' => 'Option 1|Option 1|Option 1|Option 1'], + false + ], [ Import::BEHAVIOR_APPEND, ['is_required' => true, 'type' => 'datetime'], From 3b64955757358d8c1d5f81859870bd1d84d2ac7b Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Thu, 19 Jul 2018 06:46:16 +0000 Subject: [PATCH 0385/1171] graphql-ce-120: added missing dependency to composer json --- app/code/Magento/StoreGraphQl/composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/StoreGraphQl/composer.json b/app/code/Magento/StoreGraphQl/composer.json index 91f79b39c023a..d03d759babd22 100644 --- a/app/code/Magento/StoreGraphQl/composer.json +++ b/app/code/Magento/StoreGraphQl/composer.json @@ -4,7 +4,8 @@ "type": "magento2-module", "require": { "php": "~7.1.3||~7.2.0", - "magento/framework": "*" + "magento/framework": "*", + "magento/module-store": "*" }, "suggest": { "magento/module-graph-ql": "*", From b475068f894b95674454cc08dc327a45d218a72f Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 19 Jul 2018 11:19:10 +0300 Subject: [PATCH 0386/1171] MAGETWO-88431: Add mandatory form key validation to front controller --- .../Magento/Backend/App/AbstractAction.php | 7 +- .../Backend/App/Request/BackendValidator.php | 180 ++++ app/code/Magento/Backend/etc/adminhtml/di.xml | 2 + .../Controller/Account/CreatePost.php | 46 +- .../Customer/Controller/Account/EditPost.php | 38 +- .../Customer/Controller/Account/Login.php | 3 +- .../Customer/Controller/Account/LoginPost.php | 32 +- .../Customer/Controller/Account/Logout.php | 3 +- .../Unit/Controller/Account/EditPostTest.php | 795 ------------------ .../Integration/Controller/Token/Access.php | 23 +- .../Integration/Controller/Token/Request.php | 24 +- .../Observer/RegisterFormKeyFromCookie.php | 97 --- .../Plugin/RegisterFormKeyFromCookie.php | 107 +++ .../RegisterFormKeyFromCookieTest.php | 169 ---- app/code/Magento/PageCache/etc/di.xml | 3 + .../Magento/PageCache/etc/frontend/events.xml | 12 - .../StorefrontDeletePersistedWishlistTest.xml | 2 + app/etc/di.xml | 2 + .../CurlTransport/FrontendDecorator.php | 2 +- .../TestCase/AbstractController.php | 13 +- .../App/Request/BackendValidatorTest.php | 433 ++++++++++ .../Customer/Controller/AccountTest.php | 1 + .../App/Request/CsrfValidatorTest.php | 269 ++++++ .../Plugin/RegisterFormKeyFromCookieTest.php | 68 ++ .../App/CsrfAwareActionInterface.php | 39 + .../Magento/Framework/App/FrontController.php | 85 +- .../Framework/App/Request/CsrfValidator.php | 132 +++ .../App/Request/InvalidRequestException.php | 60 ++ .../App/Request/ValidatorInterface.php | 32 + lib/web/mage/common.js | 20 + 30 files changed, 1594 insertions(+), 1105 deletions(-) create mode 100644 app/code/Magento/Backend/App/Request/BackendValidator.php delete mode 100644 app/code/Magento/Customer/Test/Unit/Controller/Account/EditPostTest.php delete mode 100644 app/code/Magento/PageCache/Observer/RegisterFormKeyFromCookie.php create mode 100644 app/code/Magento/PageCache/Plugin/RegisterFormKeyFromCookie.php delete mode 100644 app/code/Magento/PageCache/Test/Unit/Observer/RegisterFormKeyFromCookieTest.php delete mode 100644 app/code/Magento/PageCache/etc/frontend/events.xml create mode 100644 dev/tests/integration/testsuite/Magento/Backend/App/Request/BackendValidatorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/App/Request/CsrfValidatorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/PageCache/Plugin/RegisterFormKeyFromCookieTest.php create mode 100644 lib/internal/Magento/Framework/App/CsrfAwareActionInterface.php create mode 100644 lib/internal/Magento/Framework/App/Request/CsrfValidator.php create mode 100644 lib/internal/Magento/Framework/App/Request/InvalidRequestException.php create mode 100644 lib/internal/Magento/Framework/App/Request/ValidatorInterface.php diff --git a/app/code/Magento/Backend/App/AbstractAction.php b/app/code/Magento/Backend/App/AbstractAction.php index 3f658ee90bf4e..fb2daa283f111 100644 --- a/app/code/Magento/Backend/App/AbstractAction.php +++ b/app/code/Magento/Backend/App/AbstractAction.php @@ -205,10 +205,6 @@ private function _moveBlockToContainer(\Magento\Framework\View\Element\AbstractB */ public function dispatch(\Magento\Framework\App\RequestInterface $request) { - if (!$this->_processUrlKeys()) { - return parent::dispatch($request); - } - if ($request->isDispatched() && $request->getActionName() !== 'denied' && !$this->_isAllowed()) { $this->_response->setStatusHeader(403, '1.1', 'Forbidden'); if (!$this->_auth->isLoggedIn()) { @@ -252,6 +248,9 @@ protected function _isUrlChecked() * Check url keys. If non valid - redirect * * @return bool + * + * @see \Magento\Backend\App\Request\BackendValidator for default + * request validation. */ public function _processUrlKeys() { diff --git a/app/code/Magento/Backend/App/Request/BackendValidator.php b/app/code/Magento/Backend/App/Request/BackendValidator.php new file mode 100644 index 0000000000000..878f9cb4dc4c1 --- /dev/null +++ b/app/code/Magento/Backend/App/Request/BackendValidator.php @@ -0,0 +1,180 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Backend\App\Request; + +use Magento\Backend\App\AbstractAction; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\Request\ValidatorInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Backend\Model\Auth; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Controller\Result\RawFactory; +use Magento\Framework\Controller\Result\Raw as RawResult; +use Magento\Framework\Controller\Result\RedirectFactory; +use Magento\Framework\Data\Form\FormKey\Validator as FormKeyValidator; +use Magento\Backend\Model\UrlInterface as BackendUrl; +use Magento\Framework\Phrase; + +/** + * Do backend validations. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class BackendValidator implements ValidatorInterface +{ + /** + * @var Auth + */ + private $auth; + + /** + * @var FormKeyValidator + */ + private $formKeyValidator; + + /** + * @var BackendUrl + */ + private $backendUrl; + + /** + * @var RedirectFactory + */ + private $redirectFactory; + + /** + * @var RawFactory + */ + private $rawResultFactory; + + /** + * @param Auth $auth + * @param FormKeyValidator $formKeyValidator + * @param BackendUrl $backendUrl + * @param RedirectFactory $redirectFactory + * @param RawFactory $rawResultFactory + */ + public function __construct( + Auth $auth, + FormKeyValidator $formKeyValidator, + BackendUrl $backendUrl, + RedirectFactory $redirectFactory, + RawFactory $rawResultFactory + ) { + $this->auth = $auth; + $this->formKeyValidator = $formKeyValidator; + $this->backendUrl = $backendUrl; + $this->redirectFactory = $redirectFactory; + $this->rawResultFactory = $rawResultFactory; + } + + /** + * @param RequestInterface $request + * @param ActionInterface $action + * + * @return bool + */ + private function validateRequest( + RequestInterface $request, + ActionInterface $action + ): bool { + /** @var bool|null $valid */ + $valid = null; + + if ($action instanceof CsrfAwareActionInterface) { + $valid = $action->validateForCsrf($request); + } + + if ($valid === null) { + $validFormKey = true; + $validSecretKey = true; + if ($request instanceof HttpRequest && $request->isPost()) { + $validFormKey = $this->formKeyValidator->validate($request); + } elseif ($this->auth->isLoggedIn() + && $this->backendUrl->useSecretKey() + ) { + $secretKeyValue = (string)$request->getParam( + BackendUrl::SECRET_KEY_PARAM_NAME, + null + ); + $secretKey = $this->backendUrl->getSecretKey(); + $validSecretKey = ($secretKeyValue === $secretKey); + } + $valid = $validFormKey && $validSecretKey; + } + + return $valid; + } + + /** + * @param RequestInterface $request + * @param ActionInterface $action + * + * @return InvalidRequestException + */ + private function createException( + RequestInterface $request, + ActionInterface $action + ): InvalidRequestException { + /** @var InvalidRequestException|null $exception */ + $exception = null; + + if ($action instanceof CsrfAwareActionInterface) { + $exception = $action->createCsrfValidationException($request); + } + + if ($exception === null) { + if ($request instanceof HttpRequest && $request->isAjax()) { + //Sending empty response for AJAX request since we don't know + //the expected response format and it's pointless to redirect. + /** @var RawResult $response */ + $response = $this->rawResultFactory->create(); + $response->setHttpResponseCode(401); + $response->setContents(''); + $exception = new InvalidRequestException($response); + } else { + //For regular requests. + $response = $this->redirectFactory->create() + ->setUrl($this->backendUrl->getStartupPageUrl()); + $exception = new InvalidRequestException( + $response, + [ + new Phrase( + 'Invalid security or form key. Please refresh the page.' + ) + ] + ); + } + } + + return $exception; + } + + /** + * @inheritDoc + */ + public function validate( + RequestInterface $request, + ActionInterface $action + ): void { + if ($action instanceof AbstractAction) { + //Abstract Action has build-in validation. + if (!$action->_processUrlKeys()) { + throw new InvalidRequestException($action->getResponse()); + } + } else { + //Fallback validation. + if (!$this->validateRequest($request, $action)) { + throw $this->createException($request, $action); + } + } + } +} diff --git a/app/code/Magento/Backend/etc/adminhtml/di.xml b/app/code/Magento/Backend/etc/adminhtml/di.xml index e08d4ac202756..d8e9674d2b4cb 100644 --- a/app/code/Magento/Backend/etc/adminhtml/di.xml +++ b/app/code/Magento/Backend/etc/adminhtml/di.xml @@ -14,6 +14,8 @@ <preference for="Magento\Framework\App\DefaultPathInterface" type="Magento\Backend\App\DefaultPath" /> <preference for="Magento\Backend\App\ConfigInterface" type="Magento\Backend\App\Config" /> <preference for="Magento\Framework\App\Response\Http\FileFactory" type="Magento\Backend\App\Response\Http\FileFactory" /> + <preference for="Magento\Framework\App\Request\ValidatorInterface" + type="Magento\Backend\App\Request\BackendValidator" /> <type name="Magento\Framework\Stdlib\DateTime\Timezone"> <arguments> <argument name="scopeType" xsi:type="const">Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT</argument> diff --git a/app/code/Magento/Customer/Controller/Account/CreatePost.php b/app/code/Magento/Customer/Controller/Account/CreatePost.php index 27d8ddd99344c..bb94063226f41 100644 --- a/app/code/Magento/Customer/Controller/Account/CreatePost.php +++ b/app/code/Magento/Customer/Controller/Account/CreatePost.php @@ -11,8 +11,13 @@ use Magento\Framework\App\Action\Context; use Magento\Customer\Model\Session; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\CsrfAwareActionInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\Redirect; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Phrase; use Magento\Store\Model\StoreManagerInterface; use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Helper\Address; @@ -29,12 +34,13 @@ use Magento\Framework\Exception\StateException; use Magento\Framework\Exception\InputException; use Magento\Framework\Data\Form\FormKey\Validator; +use Magento\Customer\Controller\AbstractAccount; /** * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class CreatePost extends \Magento\Customer\Controller\AbstractAccount +class CreatePost extends AbstractAccount implements CsrfAwareActionInterface { /** * @var \Magento\Customer\Api\AccountManagementInterface @@ -273,6 +279,31 @@ protected function extractAddress() return $addressDataObject; } + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + /** @var Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); + $url = $this->urlModel->getUrl('*/*/create', ['_secure' => true]); + $resultRedirect->setUrl($this->_redirect->error($url)); + + return new InvalidRequestException( + $resultRedirect, + [new Phrase('Invalid Form Key. Please refresh the page.')] + ); + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return null; + } + /** * Create customer account action * @@ -282,17 +313,19 @@ protected function extractAddress() */ public function execute() { - /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ + /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); if ($this->session->isLoggedIn() || !$this->registration->isAllowed()) { $resultRedirect->setPath('*/*/'); return $resultRedirect; } - if (!$this->getRequest()->isPost() || !$this->formKeyValidator->validate($this->getRequest())) { + if (!$this->getRequest()->isPost() + || !$this->formKeyValidator->validate($this->getRequest()) + ) { $url = $this->urlModel->getUrl('*/*/create', ['_secure' => true]); - $resultRedirect->setUrl($this->_redirect->error($url)); - return $resultRedirect; + return $this->resultRedirectFactory->create() + ->setUrl($this->_redirect->error($url)); } $this->session->regenerateId(); @@ -375,8 +408,7 @@ public function execute() $this->session->setCustomerFormData($this->getRequest()->getPostValue()); $defaultUrl = $this->urlModel->getUrl('*/*/create', ['_secure' => true]); - $resultRedirect->setUrl($this->_redirect->error($defaultUrl)); - return $resultRedirect; + return $resultRedirect->setUrl($this->_redirect->error($defaultUrl)); } /** diff --git a/app/code/Magento/Customer/Controller/Account/EditPost.php b/app/code/Magento/Customer/Controller/Account/EditPost.php index a10795533a2a5..aa5e088f9c892 100644 --- a/app/code/Magento/Customer/Controller/Account/EditPost.php +++ b/app/code/Magento/Customer/Controller/Account/EditPost.php @@ -10,8 +10,11 @@ use Magento\Customer\Model\AuthenticationInterface; use Magento\Customer\Model\Customer\Mapper; use Magento\Customer\Model\EmailNotificationInterface; -use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\CsrfAwareActionInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\Redirect; use Magento\Framework\Data\Form\FormKey\Validator; use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\CustomerRepositoryInterface; @@ -21,12 +24,14 @@ use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\InvalidEmailOrPasswordException; use Magento\Framework\Exception\State\UserLockedException; +use Magento\Customer\Controller\AbstractAccount; +use Magento\Framework\Phrase; /** * Class EditPost * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class EditPost extends \Magento\Customer\Controller\AbstractAccount +class EditPost extends AbstractAccount implements CsrfAwareActionInterface { /** * Form code for data extractor @@ -131,6 +136,30 @@ private function getEmailNotification() } } + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + /** @var Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); + $resultRedirect->setPath('*/*/edit'); + + return new InvalidRequestException( + $resultRedirect, + [new Phrase('Invalid Form Key. Please refresh the page.')] + ); + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return null; + } + /** * Change customer email or password action * @@ -190,7 +219,10 @@ public function execute() $this->session->setCustomerFormData($this->getRequest()->getPostValue()); } - return $resultRedirect->setPath('*/*/edit'); + /** @var Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); + $resultRedirect->setPath('*/*/edit'); + return $resultRedirect; } /** diff --git a/app/code/Magento/Customer/Controller/Account/Login.php b/app/code/Magento/Customer/Controller/Account/Login.php index 51c244ec0cfe9..d685191bf43b5 100644 --- a/app/code/Magento/Customer/Controller/Account/Login.php +++ b/app/code/Magento/Customer/Controller/Account/Login.php @@ -9,8 +9,9 @@ use Magento\Customer\Model\Session; use Magento\Framework\App\Action\Context; use Magento\Framework\View\Result\PageFactory; +use Magento\Customer\Controller\AbstractAccount; -class Login extends \Magento\Customer\Controller\AbstractAccount +class Login extends AbstractAccount { /** * @var Session diff --git a/app/code/Magento/Customer/Controller/Account/LoginPost.php b/app/code/Magento/Customer/Controller/Account/LoginPost.php index 31e2a3aeca9e3..49a3f95379d4b 100644 --- a/app/code/Magento/Customer/Controller/Account/LoginPost.php +++ b/app/code/Magento/Customer/Controller/Account/LoginPost.php @@ -11,17 +11,23 @@ use Magento\Customer\Model\Session; use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Model\Url as CustomerUrl; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\Redirect; use Magento\Framework\Exception\EmailNotConfirmedException; use Magento\Framework\Exception\AuthenticationException; use Magento\Framework\Data\Form\FormKey\Validator; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\State\UserLockedException; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Customer\Controller\AbstractAccount; +use Magento\Framework\Phrase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class LoginPost extends \Magento\Customer\Controller\AbstractAccount +class LoginPost extends AbstractAccount implements CsrfAwareActionInterface { /** * @var \Magento\Customer\Api\AccountManagementInterface @@ -131,6 +137,30 @@ private function getCookieMetadataFactory() return $this->cookieMetadataFactory; } + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + /** @var Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); + $resultRedirect->setPath('*/*/'); + + return new InvalidRequestException( + $resultRedirect, + [new Phrase('Invalid Form Key. Please refresh the page.')] + ); + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return null; + } + /** * Login post action * diff --git a/app/code/Magento/Customer/Controller/Account/Logout.php b/app/code/Magento/Customer/Controller/Account/Logout.php index 3d5d5480c502b..19dabf9effa56 100644 --- a/app/code/Magento/Customer/Controller/Account/Logout.php +++ b/app/code/Magento/Customer/Controller/Account/Logout.php @@ -11,8 +11,9 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; use Magento\Framework\Stdlib\Cookie\PhpCookieManager; +use Magento\Customer\Controller\AbstractAccount; -class Logout extends \Magento\Customer\Controller\AbstractAccount +class Logout extends AbstractAccount { /** * @var Session diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/EditPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/EditPostTest.php deleted file mode 100644 index aaa1c17027928..0000000000000 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/EditPostTest.php +++ /dev/null @@ -1,795 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Customer\Test\Unit\Controller\Account; - -use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Customer\Controller\Account\EditPost; -use Magento\Customer\Model\AuthenticationInterface; -use Magento\Customer\Model\CustomerExtractor; -use Magento\Customer\Model\EmailNotificationInterface; -use Magento\Customer\Model\Session; -use Magento\Framework\App\Action\Context; -use Magento\Framework\App\Request\Http; -use Magento\Framework\Controller\Result\Redirect; -use Magento\Framework\Controller\Result\RedirectFactory; -use Magento\Framework\Data\Form\FormKey\Validator; -use Magento\Framework\Message\ManagerInterface; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class EditPostTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var EditPost - */ - protected $model; - - /** - * @var Context|\PHPUnit_Framework_MockObject_MockObject - */ - protected $context; - - /** - * @var Session|\PHPUnit_Framework_MockObject_MockObject - */ - protected $customerSession; - - /** - * @var \Magento\Customer\Model\AccountManagement|\PHPUnit_Framework_MockObject_MockObject - */ - protected $customerAccountManagement; - - /** - * @var CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $customerRepository; - - /** - * @var Validator|\PHPUnit_Framework_MockObject_MockObject - */ - protected $validator; - - /** - * @var CustomerExtractor|\PHPUnit_Framework_MockObject_MockObject - */ - protected $customerExtractor; - - /** - * @var EmailNotificationInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $emailNotification; - - /** - * @var RedirectFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $resultRedirectFactory; - - /** - * @var Redirect|\PHPUnit_Framework_MockObject_MockObject - */ - protected $resultRedirect; - - /** - * @var Http|\PHPUnit_Framework_MockObject_MockObject - */ - protected $request; - - /** - * @var ManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $messageManager; - - /** - * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $eventManager; - - /** - * @var AuthenticationInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $authenticationMock; - - /** - * @var \Magento\Customer\Model\Customer\Mapper|\PHPUnit_Framework_MockObject_MockObject - */ - private $customerMapperMock; - - protected function setUp() - { - $this->prepareContext(); - - $this->customerSession = $this->getMockBuilder(\Magento\Customer\Model\Session::class) - ->disableOriginalConstructor() - ->setMethods(['getCustomerId', 'setCustomerFormData', 'logout', 'start']) - ->getMock(); - - $this->customerAccountManagement = $this->getMockBuilder(\Magento\Customer\Model\AccountManagement::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->customerRepository = $this->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class) - ->getMockForAbstractClass(); - - $this->validator = $this->getMockBuilder(\Magento\Framework\Data\Form\FormKey\Validator::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->customerExtractor = $this->getMockBuilder(\Magento\Customer\Model\CustomerExtractor::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->emailNotification = $this->getMockBuilder(EmailNotificationInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->authenticationMock = $this->getMockBuilder(AuthenticationInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->customerMapperMock = $this->getMockBuilder(\Magento\Customer\Model\Customer\Mapper::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->model = new EditPost( - $this->context, - $this->customerSession, - $this->customerAccountManagement, - $this->customerRepository, - $this->validator, - $this->customerExtractor - ); - - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $objectManager->setBackwardCompatibleProperty( - $this->model, - 'emailNotification', - $this->emailNotification - ); - - $objectManager->setBackwardCompatibleProperty( - $this->model, - 'authentication', - $this->authenticationMock - ); - $objectManager->setBackwardCompatibleProperty( - $this->model, - 'customerMapper', - $this->customerMapperMock - ); - } - - public function testInvalidFormKey() - { - $this->validator->expects($this->once()) - ->method('validate') - ->with($this->request) - ->willReturn(false); - - $this->resultRedirect->expects($this->once()) - ->method('setPath') - ->with('*/*/edit') - ->willReturnSelf(); - - $this->assertSame($this->resultRedirect, $this->model->execute()); - } - - public function testNoPostValues() - { - $this->validator->expects($this->once()) - ->method('validate') - ->with($this->request) - ->willReturn(true); - - $this->request->expects($this->once()) - ->method('isPost') - ->willReturn(false); - - $this->resultRedirect->expects($this->once()) - ->method('setPath') - ->with('*/*/edit') - ->willReturnSelf(); - - $this->assertSame($this->resultRedirect, $this->model->execute()); - } - - public function testGeneralSave() - { - $customerId = 1; - $currentPassword = '1234567'; - $customerEmail = 'customer@example.com'; - - $address = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterface::class) - ->getMockForAbstractClass(); - $currentCustomerMock = $this->getCurrentCustomerMock($customerId, $address); - $newCustomerMock = $this->getNewCustomerMock($customerId, $address); - - $currentCustomerMock->expects($this->any()) - ->method('getEmail') - ->willReturn($customerEmail); - - $this->customerMapperMock->expects($this->once()) - ->method('toFlatArray') - ->with($currentCustomerMock) - ->willReturn([]); - - $this->customerSession->expects($this->once()) - ->method('getCustomerId') - ->willReturn($customerId); - - $this->customerRepository->expects($this->once()) - ->method('getById') - ->with($customerId) - ->willReturn($currentCustomerMock); - - $this->validator->expects($this->once()) - ->method('validate') - ->with($this->request) - ->willReturn(true); - - $this->request->expects($this->once()) - ->method('isPost') - ->willReturn(true); - - $this->request->expects($this->exactly(3)) - ->method('getParam') - ->withConsecutive( - ['change_email'], - ['change_email'], - ['change_password'] - ) - ->willReturnOnConsecutiveCalls(true, true, false); - - $this->request->expects($this->once()) - ->method('getPost') - ->with('current_password') - ->willReturn($currentPassword); - - $this->customerRepository->expects($this->once()) - ->method('getById') - ->with($customerId) - ->willReturn($currentCustomerMock); - - $this->customerRepository->expects($this->once()) - ->method('save') - ->with($newCustomerMock) - ->willReturnSelf(); - - $this->customerExtractor->expects($this->once()) - ->method('extract') - ->with('customer_account_edit', $this->request) - ->willReturn($newCustomerMock); - - $this->emailNotification->expects($this->once()) - ->method('credentialsChanged') - ->with($currentCustomerMock, $customerEmail, false) - ->willReturnSelf(); - - $newCustomerMock->expects($this->once()) - ->method('getEmail') - ->willReturn($customerEmail); - - $this->eventManager->expects($this->once()) - ->method('dispatch') - ->with( - 'customer_account_edited', - ['email' => $customerEmail] - ); - - $this->messageManager->expects($this->once()) - ->method('addSuccess') - ->with(__('You saved the account information.')) - ->willReturnSelf(); - - $this->resultRedirect->expects($this->once()) - ->method('setPath') - ->with('customer/account') - ->willReturnSelf(); - - $this->authenticationMock->expects($this->once()) - ->method('authenticate') - ->willReturn(true); - - $this->assertSame($this->resultRedirect, $this->model->execute()); - } - - /** - * @param int $testNumber - * @param string $exceptionClass - * @param string $errorMessage - * - * @dataProvider changeEmailExceptionDataProvider - */ - public function testChangeEmailException($testNumber, $exceptionClass, $errorMessage) - { - $customerId = 1; - $password = '1234567'; - - $address = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterface::class) - ->getMockForAbstractClass(); - - $currentCustomerMock = $this->getCurrentCustomerMock($customerId, $address); - $newCustomerMock = $this->getNewCustomerMock($customerId, $address); - - $this->customerMapperMock->expects($this->once()) - ->method('toFlatArray') - ->with($currentCustomerMock) - ->willReturn([]); - - $this->customerExtractor->expects($this->once()) - ->method('extract') - ->with('customer_account_edit', $this->request) - ->willReturn($newCustomerMock); - - $this->validator->expects($this->once()) - ->method('validate') - ->with($this->request) - ->willReturn(true); - - $this->request->expects($this->once()) - ->method('isPost') - ->willReturn(true); - - $this->customerSession->expects($this->once()) - ->method('getCustomerId') - ->willReturn($customerId); - - $this->customerRepository->expects($this->once()) - ->method('getById') - ->with($customerId) - ->willReturn($currentCustomerMock); - - $this->request->expects($this->any()) - ->method('getParam') - ->with('change_email') - ->willReturn(true); - - $this->request->expects($this->once()) - ->method('getPost') - ->with('current_password') - ->willReturn($password); - - $exception = new $exceptionClass($errorMessage); - $this->authenticationMock->expects($this->once()) - ->method('authenticate') - ->willThrowException($exception); - - $this->messageManager->expects($this->once()) - ->method('addError') - ->with($errorMessage) - ->willReturnSelf(); - - if ($testNumber==1) { - $this->resultRedirect->expects($this->once()) - ->method('setPath') - ->with('*/*/edit') - ->willReturnSelf(); - } - - if ($testNumber==2) { - $this->customerSession->expects($this->once()) - ->method('logout'); - - $this->customerSession->expects($this->once()) - ->method('start'); - - $this->resultRedirect->expects($this->once()) - ->method('setPath') - ->with('customer/account/login') - ->willReturnSelf(); - } - - $this->assertSame($this->resultRedirect, $this->model->execute()); - } - - /** - * @return array - */ - public function changeEmailExceptionDataProvider() - { - return [ - [ - 'testNumber' => 1, - 'exceptionClass' => \Magento\Framework\Exception\InvalidEmailOrPasswordException::class, - 'errorMessage' => __("The password doesn't match this account. Verify the password and try again.") - ], - [ - 'testNumber' => 2, - 'exceptionClass' => \Magento\Framework\Exception\State\UserLockedException::class, - 'errorMessage' => __('The account sign-in was incorrect or your account is disabled temporarily. ' - . 'Please wait and try again later.') - ] - ]; - } - - /** - * @param string $currentPassword - * @param string $newPassword - * @param string $confirmationPassword - * @param [] $errors - * - * @dataProvider changePasswordDataProvider - */ - public function testChangePassword( - $currentPassword, - $newPassword, - $confirmationPassword, - $errors - ) { - $customerId = 1; - $customerEmail = 'user1@example.com'; - - $address = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterface::class) - ->getMockForAbstractClass(); - - $currentCustomerMock = $this->getCurrentCustomerMock($customerId, $address); - $newCustomerMock = $this->getNewCustomerMock($customerId, $address); - - $this->customerMapperMock->expects($this->once()) - ->method('toFlatArray') - ->with($currentCustomerMock) - ->willReturn([]); - - $this->customerSession->expects($this->once()) - ->method('getCustomerId') - ->willReturn($customerId); - - $this->customerRepository->expects($this->once()) - ->method('getById') - ->with($customerId) - ->willReturn($currentCustomerMock); - - $this->customerExtractor->expects($this->once()) - ->method('extract') - ->with('customer_account_edit', $this->request) - ->willReturn($newCustomerMock); - - $this->validator->expects($this->once()) - ->method('validate') - ->with($this->request) - ->willReturn(true); - - $this->request->expects($this->once()) - ->method('isPost') - ->willReturn(true); - - $this->request->expects($this->exactly(3)) - ->method('getParam') - ->withConsecutive( - ['change_email'], - ['change_email'], - ['change_password'] - ) - ->willReturnOnConsecutiveCalls(false, false, true); - - $this->request->expects($this->any()) - ->method('getPostValue') - ->willReturn(true); - - $this->request->expects($this->exactly(3)) - ->method('getPost') - ->willReturnMap([ - ['current_password', null, $currentPassword], - ['password', null, $newPassword], - ['password_confirmation', null, $confirmationPassword], - ]); - - $currentCustomerMock->expects($this->any()) - ->method('getEmail') - ->willReturn($customerEmail); - - // Prepare errors processing - if ($errors['counter'] > 0) { - $this->mockChangePasswordErrors($currentPassword, $newPassword, $errors, $customerEmail); - } else { - $this->customerAccountManagement->expects($this->once()) - ->method('changePassword') - ->with($customerEmail, $currentPassword, $newPassword) - ->willReturnSelf(); - - $this->customerRepository->expects($this->once()) - ->method('save') - ->with($newCustomerMock) - ->willReturnSelf(); - - $this->messageManager->expects($this->once()) - ->method('addSuccess') - ->with(__('You saved the account information.')) - ->willReturnSelf(); - - $this->resultRedirect->expects($this->once()) - ->method('setPath') - ->with('customer/account') - ->willReturnSelf(); - } - - $this->assertSame($this->resultRedirect, $this->model->execute()); - } - - /** - * @return array - */ - public function changePasswordDataProvider() - { - return [ - [ - 'current_password' => '', - 'new_password' => '', - 'confirmation_password' => '', - 'errors' => [ - 'counter' => 1, - 'message' => __('Please enter new password.'), - ] - ], - [ - 'current_password' => '', - 'new_password' => 'user2@example.com', - 'confirmation_password' => 'user3@example.com', - 'errors' => [ - 'counter' => 1, - 'message' => __('Password confirmation doesn\'t match entered password.'), - ] - ], - [ - 'current_password' => 'user1@example.com', - 'new_password' => 'user2@example.com', - 'confirmation_password' => 'user2@example.com', - 'errors' => [ - 'counter' => 0, - 'message' => '', - ] - ], - [ - 'current_password' => 'user1@example.com', - 'new_password' => 'user2@example.com', - 'confirmation_password' => 'user2@example.com', - 'errors' => [ - 'counter' => 1, - 'message' => 'AuthenticationException', - 'exception' => \Magento\Framework\Exception\AuthenticationException::class, - ] - ], - [ - 'current_password' => 'user1@example.com', - 'new_password' => 'user2@example.com', - 'confirmation_password' => 'user2@example.com', - 'errors' => [ - 'counter' => 1, - 'message' => 'Exception', - 'exception' => '\Exception', - ] - ] - ]; - } - - /** - * @param string $message - * @param string $exception - * - * @dataProvider exceptionDataProvider - */ - public function testGeneralException( - $message, - $exception - ) { - $customerId = 1; - - $address = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterface::class) - ->getMockForAbstractClass(); - - $currentCustomerMock = $this->getCurrentCustomerMock($customerId, $address); - $newCustomerMock = $this->getNewCustomerMock($customerId, $address); - - $this->customerMapperMock->expects($this->once()) - ->method('toFlatArray') - ->with($currentCustomerMock) - ->willReturn([]); - - $exception = new $exception(__($message)); - - $this->validator->expects($this->once()) - ->method('validate') - ->with($this->request) - ->willReturn(true); - - $this->request->expects($this->once()) - ->method('isPost') - ->willReturn(true); - - $this->request->expects($this->exactly(3)) - ->method('getParam') - ->withConsecutive( - ['change_email'], - ['change_email'], - ['change_password'] - ) - ->willReturn(false); - - $this->request->expects($this->any()) - ->method('getPostValue') - ->willReturn(true); - - $this->customerSession->expects($this->once()) - ->method('getCustomerId') - ->willReturn($customerId); - $this->customerSession->expects($this->once()) - ->method('setCustomerFormData') - ->with(true) - ->willReturnSelf(); - - $this->customerRepository->expects($this->once()) - ->method('getById') - ->with($customerId) - ->willReturn($currentCustomerMock); - $this->customerRepository->expects($this->once()) - ->method('save') - ->with($newCustomerMock) - ->willThrowException($exception); - - $this->customerExtractor->expects($this->once()) - ->method('extract') - ->with('customer_account_edit', $this->request) - ->willReturn($newCustomerMock); - - $this->resultRedirect->expects($this->once()) - ->method('setPath') - ->with('*/*/edit') - ->willReturnSelf(); - - $this->assertSame($this->resultRedirect, $this->model->execute()); - } - - /** - * @return array - */ - public function exceptionDataProvider() - { - return [ - [ - 'message' => 'LocalizedException', - 'exception' => \Magento\Framework\Exception\LocalizedException::class, - ], - [ - 'message' => 'Exception', - 'exception' => '\Exception', - ], - ]; - } - - protected function prepareContext() - { - $this->context = $this->getMockBuilder(\Magento\Framework\App\Action\Context::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultRedirectFactory = $this->getMockBuilder( - \Magento\Framework\Controller\Result\RedirectFactory::class - ) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $this->resultRedirect = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->messageManager = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) - ->getMockForAbstractClass(); - - $this->context->expects($this->any()) - ->method('getResultRedirectFactory') - ->willReturn($this->resultRedirectFactory); - - $this->context->expects($this->any()) - ->method('getRequest') - ->willReturn($this->request); - - $this->context->expects($this->any()) - ->method('getMessageManager') - ->willReturn($this->messageManager); - - $this->eventManager = $this->getMockBuilder(\Magento\Framework\Event\ManagerInterface::class) - ->getMockForAbstractClass(); - - $this->context->expects($this->any()) - ->method('getEventManager') - ->willReturn($this->eventManager); - - $this->resultRedirectFactory->expects($this->any()) - ->method('create') - ->willReturn($this->resultRedirect); - } - - /** - * @param int $customerId - * @param \PHPUnit_Framework_MockObject_MockObject $address - * @return \PHPUnit_Framework_MockObject_MockObject - */ - protected function getNewCustomerMock($customerId, $address) - { - $newCustomerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) - ->getMockForAbstractClass(); - - $newCustomerMock->expects($this->once()) - ->method('setId') - ->with($customerId) - ->willReturnSelf(); - $newCustomerMock->expects($this->once()) - ->method('getAddresses') - ->willReturn(null); - $newCustomerMock->expects($this->once()) - ->method('setAddresses') - ->with([$address]) - ->willReturn(null); - - return $newCustomerMock; - } - - /** - * @param int $customerId - * @param \PHPUnit_Framework_MockObject_MockObject $address - * @return \PHPUnit_Framework_MockObject_MockObject - */ - protected function getCurrentCustomerMock($customerId, $address) - { - $currentCustomerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) - ->getMockForAbstractClass(); - - $currentCustomerMock->expects($this->once()) - ->method('getAddresses') - ->willReturn([$address]); - - $currentCustomerMock->expects($this->any()) - ->method('getId') - ->willReturn($customerId); - - return $currentCustomerMock; - } - - /** - * @param string $currentPassword - * @param string $newPassword - * @param [] $errors - * @param string $customerEmail - * @return void - */ - protected function mockChangePasswordErrors($currentPassword, $newPassword, $errors, $customerEmail) - { - if (!empty($errors['exception'])) { - $exception = new $errors['exception'](__($errors['message'])); - - $this->customerAccountManagement->expects($this->once()) - ->method('changePassword') - ->with($customerEmail, $currentPassword, $newPassword) - ->willThrowException($exception); - - $this->messageManager->expects($this->any()) - ->method('addException') - ->with($exception, __('We can\'t save the customer.')) - ->willReturnSelf(); - } - - $this->customerSession->expects($this->once()) - ->method('setCustomerFormData') - ->with(true) - ->willReturnSelf(); - - $this->messageManager->expects($this->any()) - ->method('addError') - ->with($errors['message']) - ->willReturnSelf(); - - $this->resultRedirect->expects($this->any()) - ->method('setPath') - ->with('*/*/edit') - ->willReturnSelf(); - } -} diff --git a/app/code/Magento/Integration/Controller/Token/Access.php b/app/code/Magento/Integration/Controller/Token/Access.php index 88d5029a724c7..5b75643f51b50 100644 --- a/app/code/Magento/Integration/Controller/Token/Access.php +++ b/app/code/Magento/Integration/Controller/Token/Access.php @@ -6,11 +6,15 @@ */ namespace Magento\Integration\Controller\Token; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; use Magento\Integration\Model\Integration as IntegrationModel; use Magento\Integration\Api\IntegrationServiceInterface as IntegrationService; use Magento\Integration\Api\OauthServiceInterface as IntegrationOauthService; +use Magento\Framework\App\Action\Action; -class Access extends \Magento\Framework\App\Action\Action +class Access extends Action implements CsrfAwareActionInterface { /** * @var \Magento\Framework\Oauth\OauthInterface @@ -53,6 +57,23 @@ public function __construct( $this->helper = $helper; } + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } + /** * Initiate AccessToken request operation * diff --git a/app/code/Magento/Integration/Controller/Token/Request.php b/app/code/Magento/Integration/Controller/Token/Request.php index 104a36e8ae8fe..04f368fc9f9fd 100644 --- a/app/code/Magento/Integration/Controller/Token/Request.php +++ b/app/code/Magento/Integration/Controller/Token/Request.php @@ -6,7 +6,12 @@ */ namespace Magento\Integration\Controller\Token; -class Request extends \Magento\Framework\App\Action\Action +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; + +class Request extends Action implements CsrfAwareActionInterface { /** * @var \Magento\Framework\Oauth\OauthInterface @@ -33,6 +38,23 @@ public function __construct( $this->helper = $helper; } + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } + /** * Initiate RequestToken request operation * diff --git a/app/code/Magento/PageCache/Observer/RegisterFormKeyFromCookie.php b/app/code/Magento/PageCache/Observer/RegisterFormKeyFromCookie.php deleted file mode 100644 index 4b51a9cf233a1..0000000000000 --- a/app/code/Magento/PageCache/Observer/RegisterFormKeyFromCookie.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\PageCache\Observer; - -use Magento\Framework\Event\ObserverInterface; - -class RegisterFormKeyFromCookie implements ObserverInterface -{ - /** - * @var \Magento\Framework\App\PageCache\FormKey - */ - private $cookieFormKey; - - /** - * @var \Magento\Framework\Escaper - */ - private $escaper; - - /** - * @var \Magento\Framework\Data\Form\FormKey - */ - private $sessionFormKey; - - /** - * @var \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory - */ - private $cookieMetadataFactory; - - /** - * @var \Magento\Framework\Session\Config\ConfigInterface - */ - private $sessionConfig; - - /** - * @param \Magento\Framework\App\PageCache\FormKey $formKey - * @param \Magento\Framework\Escaper $escaper - * @param \Magento\Framework\Data\Form\FormKey $sessionFormKey - * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory - * @param \Magento\Framework\Session\Config\ConfigInterface $sessionConfig - */ - public function __construct( - \Magento\Framework\App\PageCache\FormKey $formKey, - \Magento\Framework\Escaper $escaper, - \Magento\Framework\Data\Form\FormKey $sessionFormKey, - \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory, - \Magento\Framework\Session\Config\ConfigInterface $sessionConfig - ) { - $this->cookieFormKey = $formKey; - $this->escaper = $escaper; - $this->sessionFormKey = $sessionFormKey; - $this->cookieMetadataFactory = $cookieMetadataFactory; - $this->sessionConfig = $sessionConfig; - } - - /** - * Register form key in session from cookie value - * - * @param \Magento\Framework\Event\Observer $observer - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - if ($this->cookieFormKey->get()) { - $this->updateCookieFormKey($this->cookieFormKey->get()); - - $this->sessionFormKey->set( - $this->escaper->escapeHtml($this->cookieFormKey->get()) - ); - } - } - - /** - * @param string $formKey - * @return void - */ - private function updateCookieFormKey($formKey) - { - $cookieMetadata = $this->cookieMetadataFactory - ->createPublicCookieMetadata(); - $cookieMetadata->setDomain($this->sessionConfig->getCookieDomain()); - $cookieMetadata->setPath($this->sessionConfig->getCookiePath()); - $lifetime = $this->sessionConfig->getCookieLifetime(); - if ($lifetime !== 0) { - $cookieMetadata->setDuration($lifetime); - } - - $this->cookieFormKey->set( - $formKey, - $cookieMetadata - ); - } -} diff --git a/app/code/Magento/PageCache/Plugin/RegisterFormKeyFromCookie.php b/app/code/Magento/PageCache/Plugin/RegisterFormKeyFromCookie.php new file mode 100644 index 0000000000000..6cdc500aaf33c --- /dev/null +++ b/app/code/Magento/PageCache/Plugin/RegisterFormKeyFromCookie.php @@ -0,0 +1,107 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\PageCache\Plugin; + +use Magento\Framework\App\PageCache\FormKey as CacheFormKey; +use Magento\Framework\Escaper; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; +use Magento\Framework\Session\Config\ConfigInterface; + +/** + * Allow for registration of a form key through cookies. + */ +class RegisterFormKeyFromCookie +{ + /** + * @var CacheFormKey + */ + private $cookieFormKey; + + /** + * @var Escaper + */ + private $escaper; + + /** + * @var FormKey + */ + private $formKey; + + /** + * @var CookieMetadataFactory + */ + private $cookieMetadataFactory; + + /** + * @var ConfigInterface + */ + private $sessionConfig; + + /** + * @param CacheFormKey $formKey + * @param Escaper $escaper + * @param FormKey $formKey + * @param CookieMetadataFactory $cookieMetadataFactory + * @param ConfigInterface $sessionConfig + */ + public function __construct( + CacheFormKey $cacheFormKey, + Escaper $escaper, + FormKey $formKey, + CookieMetadataFactory $cookieMetadataFactory, + ConfigInterface $sessionConfig + ) { + $this->cookieFormKey = $cacheFormKey; + $this->escaper = $escaper; + $this->formKey = $formKey; + $this->cookieMetadataFactory = $cookieMetadataFactory; + $this->sessionConfig = $sessionConfig; + } + + /** + * Set form key from the cookie. + * + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeDispatch(): void + { + if ($this->cookieFormKey->get()) { + $this->updateCookieFormKey($this->cookieFormKey->get()); + + $this->formKey->set( + $this->escaper->escapeHtml($this->cookieFormKey->get()) + ); + } + } + + /** + * @param string $formKey + * @return void + */ + private function updateCookieFormKey(string $formKey): void + { + $cookieMetadata = $this->cookieMetadataFactory + ->createPublicCookieMetadata(); + $cookieMetadata->setDomain($this->sessionConfig->getCookieDomain()); + $cookieMetadata->setPath($this->sessionConfig->getCookiePath()); + $lifetime = $this->sessionConfig->getCookieLifetime(); + if ($lifetime !== 0) { + $cookieMetadata->setDuration($lifetime); + } + + $this->cookieFormKey->set( + $formKey, + $cookieMetadata + ); + } +} diff --git a/app/code/Magento/PageCache/Test/Unit/Observer/RegisterFormKeyFromCookieTest.php b/app/code/Magento/PageCache/Test/Unit/Observer/RegisterFormKeyFromCookieTest.php deleted file mode 100644 index 7bdea2e427c0b..0000000000000 --- a/app/code/Magento/PageCache/Test/Unit/Observer/RegisterFormKeyFromCookieTest.php +++ /dev/null @@ -1,169 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\PageCache\Test\Unit\Observer; - -use Magento\Framework\App\PageCache\FormKey; -use Magento\Framework\Escaper; -use Magento\Framework\Session\Config\ConfigInterface; -use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; -use Magento\PageCache\Observer\RegisterFormKeyFromCookie; - -class RegisterFormKeyFromCookieTest extends \PHPUnit\Framework\TestCase -{ - /** @var RegisterFormKeyFromCookie */ - protected $observer; - - /** @var \PHPUnit_Framework_MockObject_MockObject|FormKey */ - protected $cookieFormKey; - - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Data\Form\FormKey */ - protected $sessionFormKey; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|CookieMetadataFactory - */ - protected $cookieMetadataFactory; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|ConfigInterface - */ - protected $sessionConfig; - - /** @var \PHPUnit_Framework_MockObject_MockObject|Escaper */ - protected $escaper; - - /** - * @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject| - */ - protected $observerMock; - - /** - * Set up all mocks and data for test - */ - protected function setUp() - { - $this->cookieFormKey = $this->getMockBuilder( - \Magento\Framework\App\PageCache\FormKey::class - ) - ->disableOriginalConstructor() - ->getMock(); - $this->escaper = $this->getMockBuilder( - \Magento\Framework\Escaper::class - ) - ->disableOriginalConstructor() - ->getMock(); - $this->sessionFormKey = $this->getMockBuilder( - \Magento\Framework\Data\Form\FormKey::class - ) - ->disableOriginalConstructor() - ->getMock(); - $this->cookieMetadataFactory = $this->getMockBuilder( - \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory::class - ) - ->disableOriginalConstructor() - ->getMock(); - $this->sessionConfig = $this->createMock( - \Magento\Framework\Session\Config\ConfigInterface::class - ); - - $this->observerMock = $this->createMock(\Magento\Framework\Event\Observer::class); - - $this->observer = new RegisterFormKeyFromCookie( - $this->cookieFormKey, - $this->escaper, - $this->sessionFormKey, - $this->cookieMetadataFactory, - $this->sessionConfig - ); - } - - public function testExecuteNoCookie() - { - $this->cookieFormKey->expects(static::once()) - ->method('get') - ->willReturn(null); - $this->cookieFormKey->expects(static::never()) - ->method('set'); - $this->sessionFormKey->expects(static::never()) - ->method('set'); - - $this->observer->execute($this->observerMock); - } - - public function testExecute() - { - $formKey = 'form_key'; - $escapedFormKey = 'escaped_form_key'; - $cookieDomain = 'example.com'; - $cookiePath = '/'; - $cookieLifetime = 3600; - - $cookieMetadata = $this->getMockBuilder( - \Magento\Framework\Stdlib\Cookie\PublicCookieMetadata::class - ) - ->disableOriginalConstructor() - ->getMock(); - - $this->cookieFormKey->expects(static::any()) - ->method('get') - ->willReturn($formKey); - $this->cookieMetadataFactory->expects(static::once()) - ->method('createPublicCookieMetadata') - ->willReturn( - $cookieMetadata - ); - - $this->sessionConfig->expects(static::once()) - ->method('getCookieDomain') - ->willReturn( - $cookieDomain - ); - $cookieMetadata->expects(static::once()) - ->method('setDomain') - ->with( - $cookieDomain - ); - $this->sessionConfig->expects(static::once()) - ->method('getCookiePath') - ->willReturn( - $cookiePath - ); - $cookieMetadata->expects(static::once()) - ->method('setPath') - ->with( - $cookiePath - ); - $this->sessionConfig->expects(static::once()) - ->method('getCookieLifetime') - ->willReturn( - $cookieLifetime - ); - $cookieMetadata->expects(static::once()) - ->method('setDuration') - ->with( - $cookieLifetime - ); - - $this->cookieFormKey->expects(static::once()) - ->method('set') - ->with( - $formKey, - $cookieMetadata - ); - - $this->escaper->expects(static::once()) - ->method('escapeHtml') - ->with($formKey) - ->willReturn($escapedFormKey); - - $this->sessionFormKey->expects(static::once()) - ->method('set') - ->with($escapedFormKey); - - $this->observer->execute($this->observerMock); - } -} diff --git a/app/code/Magento/PageCache/etc/di.xml b/app/code/Magento/PageCache/etc/di.xml index 2a9abbb860805..adf9526fc1108 100644 --- a/app/code/Magento/PageCache/etc/di.xml +++ b/app/code/Magento/PageCache/etc/di.xml @@ -37,6 +37,9 @@ <argument name="layoutCacheKey" xsi:type="object">Magento\Framework\View\Layout\LayoutCacheKeyInterface</argument> </arguments> </type> + <type name="Magento\Framework\App\FrontControllerInterface"> + <plugin name="page_cache_from_key_from_cookie" type="Magento\PageCache\Plugin\RegisterFormKeyFromCookie" /> + </type> <preference for="Magento\PageCache\Model\VclGeneratorInterface" type="Magento\PageCache\Model\Varnish\VclGenerator"/> <preference for="Magento\PageCache\Model\VclTemplateLocatorInterface" type="Magento\PageCache\Model\Varnish\VclTemplateLocator"/> </config> diff --git a/app/code/Magento/PageCache/etc/frontend/events.xml b/app/code/Magento/PageCache/etc/frontend/events.xml deleted file mode 100644 index ce2c1c823e835..0000000000000 --- a/app/code/Magento/PageCache/etc/frontend/events.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> - <event name="controller_action_predispatch"> - <observer name="register_form_key" instance="Magento\PageCache\Observer\RegisterFormKeyFromCookie" /> - </event> -</config> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml index 3ddb9d87073f3..cbed63db36d49 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml @@ -14,6 +14,8 @@ <title value="Customer should be able to delete a persistent wishlist"/> <description value="Customer should be able to delete a persistent wishlist"/> <group value="wishlist"/> + <!-- MQE-1145 --> + <group value="skip"/> </annotations> <before> <createData stepKey="category" entity="SimpleSubCategory"/> diff --git a/app/etc/di.xml b/app/etc/di.xml index 1f5507deb0519..6a4a6d16b5568 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -204,6 +204,8 @@ <preference for="Magento\Framework\MessageQueue\ExchangeFactoryInterface" type="Magento\Framework\MessageQueue\ExchangeFactory" /> <preference for="Magento\Framework\MessageQueue\Bulk\ExchangeFactoryInterface" type="Magento\Framework\MessageQueue\Bulk\ExchangeFactory" /> <preference for="Magento\Framework\MessageQueue\QueueFactoryInterface" type="Magento\Framework\MessageQueue\QueueFactory" /> + <preference for="Magento\Framework\App\Request\ValidatorInterface" + type="Magento\Framework\App\Request\CsrfValidator" /> <type name="Magento\Framework\Model\ResourceModel\Db\TransactionManager" shared="false" /> <type name="Magento\Framework\Acl\Data\Cache"> <arguments> diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/FrontendDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/FrontendDecorator.php index 83f86b264737c..0a8db19afe971 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/FrontendDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/FrontendDecorator.php @@ -64,7 +64,7 @@ public function __construct(CurlTransport $transport, Customer $customer) protected function authorize(Customer $customer) { $url = $_ENV['app_frontend_url'] . 'customer/account/login/'; - $this->transport->write($url); + $this->transport->write($url, [], CurlInterface::GET); $this->read(); $url = $_ENV['app_frontend_url'] . 'customer/account/loginPost/'; $data = [ diff --git a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php index 9920f90193f69..1bf6d471fc40a 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php +++ b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php @@ -9,9 +9,11 @@ */ namespace Magento\TestFramework\TestCase; +use Magento\Framework\Data\Form\FormKey; use Magento\Framework\Stdlib\CookieManagerInterface; use Magento\Framework\View\Element\Message\InterpretationStrategyInterface; use Magento\Theme\Controller\Result\MessagePlugin; +use Magento\Framework\App\Request\Http as HttpRequest; /** * @SuppressWarnings(PHPMD.NumberOfChildren) @@ -96,7 +98,16 @@ protected function assertPostConditions() */ public function dispatch($uri) { - $this->getRequest()->setRequestUri($uri); + /** @var HttpRequest $request */ + $request = $this->getRequest(); + $request->setRequestUri($uri); + if ($request->isPost() + && !array_key_exists('form_key', $request->getPost()) + ) { + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); + $request->setPostValue('form_key', $formKey->getFormKey()); + } $this->_getBootstrap()->runApp(); } diff --git a/dev/tests/integration/testsuite/Magento/Backend/App/Request/BackendValidatorTest.php b/dev/tests/integration/testsuite/Magento/Backend/App/Request/BackendValidatorTest.php new file mode 100644 index 0000000000000..21ffddf851ac4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Backend/App/Request/BackendValidatorTest.php @@ -0,0 +1,433 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Backend\App\Request; + +use Magento\Backend\App\AbstractAction; +use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\Auth; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Exception\NotFoundException; +use Magento\Framework\Phrase; +use Magento\TestFramework\Request; +use Magento\TestFramework\Response; +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Bootstrap as TestBootstrap; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\App\Response\Http as HttpResponse; +use Zend\Stdlib\Parameters; +use Magento\Backend\Model\UrlInterface as BackendUrl; +use Magento\Framework\App\Response\HttpFactory as HttpResponseFactory; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class BackendValidatorTest extends TestCase +{ + private const AWARE_VALIDATION_PARAM = 'test_param'; + + private const AWARE_LOCATION_VALUE = 'test1'; + + private const CSRF_AWARE_MESSAGE = 'csrf_aware'; + + /** + * @var ActionInterface + */ + private $mockUnawareAction; + + /** + * @var AbstractAction + */ + private $mockAwareAction; + + /** + * @var BackendValidator + */ + private $validator; + + /** + * @var Request + */ + private $request; + + /** + * @var FormKey + */ + private $formKey; + + /** + * @var BackendUrl + */ + private $url; + + /** + * @var Auth + */ + private $auth; + + /** + * @var CsrfAwareActionInterface + */ + private $mockCsrfAwareAction; + + /** + * @var HttpResponseFactory + */ + private $httpResponseFactory; + + /** + * @return ActionInterface + */ + private function createUnawareAction(): ActionInterface + { + return new class implements ActionInterface { + /** + * @inheritDoc + */ + public function execute() + { + throw new NotFoundException(new Phrase('Not implemented')); + } + }; + } + + /** + * @return AbstractAction + */ + private function createAwareAction(): AbstractAction + { + $l = self::AWARE_LOCATION_VALUE; + $p = self::AWARE_VALIDATION_PARAM; + + return new class($l, $p) extends AbstractAction{ + + /** + * @var string + */ + private $locationValue; + + /** + * @var string + */ + private $param; + + /** + * @param string $locationValue + * @param string $param + */ + public function __construct( + string $locationValue, + string $param + ) { + parent::__construct( + Bootstrap::getObjectManager()->get(Context::class) + ); + $this->locationValue= $locationValue; + $this->param = $param; + } + + /** + * @inheritDoc + */ + public function execute() + { + throw new NotFoundException(new Phrase('Not implemented')); + } + + /** + * @inheritDoc + */ + public function _processUrlKeys() + { + if ($this->_request->getParam($this->param)) { + return true; + } else { + /** @var Response $response */ + $response = $this->_response; + $response->setHeader('Location', $this->locationValue); + + return false; + } + } + }; + } + + /** + * @return CsrfAwareActionInterface + */ + private function createCsrfAwareAction(): CsrfAwareActionInterface + { + $r = Bootstrap::getObjectManager() + ->get(ResponseInterface::class); + $m = self::CSRF_AWARE_MESSAGE; + + return new class ($r, $m) implements CsrfAwareActionInterface { + + /** + * @var ResponseInterface + */ + private $response; + + /** + * @var string + */ + private $message; + + /** + * @param ResponseInterface $response + * @param string $message + */ + public function __construct( + ResponseInterface $response, + string $message + ) { + $this->response = $response; + $this->message = $message; + } + + /** + * @inheritDoc + */ + public function execute() + { + return $this->response; + } + + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + return new InvalidRequestException( + $this->response, + [new Phrase($this->message)] + ); + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return false; + } + + }; + } + + /** + * @inheritDoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->request = $objectManager->get(RequestInterface::class); + $this->validator = $objectManager->get(BackendValidator::class); + $this->mockUnawareAction = $this->createUnawareAction(); + $this->mockAwareAction = $this->createAwareAction(); + $this->formKey = $objectManager->get(FormKey::class); + $this->url = $objectManager->get(BackendUrl::class); + $this->auth = $objectManager->get(Auth::class); + $this->mockCsrfAwareAction = $this->createCsrfAwareAction(); + $this->httpResponseFactory = $objectManager->get( + HttpResponseFactory::class + ); + } + + /** + * @magentoConfigFixture admin/security/use_form_key 1 + * @magentoAppArea adminhtml + */ + public function testValidateWithValidKey() + { + $this->request->setMethod(HttpRequest::METHOD_GET); + $this->auth->login( + TestBootstrap::ADMIN_NAME, + TestBootstrap::ADMIN_PASSWORD + ); + $this->request->setParams([ + BackendUrl::SECRET_KEY_PARAM_NAME => $this->url->getSecretKey(), + ]); + + $this->validator->validate( + $this->request, + $this->mockUnawareAction + ); + } + + /** + * @expectedException \Magento\Framework\App\Request\InvalidRequestException + * + * @magentoConfigFixture admin/security/use_form_key 1 + * @magentoAppArea adminhtml + */ + public function testValidateWithInvalidKey() + { + $invalidKey = $this->url->getSecretKey() .'Invalid'; + $this->request->setParams([ + BackendUrl::SECRET_KEY_PARAM_NAME => $invalidKey, + ]); + $this->request->setMethod(HttpRequest::METHOD_GET); + $this->auth->login( + TestBootstrap::ADMIN_NAME, + TestBootstrap::ADMIN_PASSWORD + ); + + $this->validator->validate( + $this->request, + $this->mockUnawareAction + ); + } + + /** + * @expectedException \Magento\Framework\App\Request\InvalidRequestException + * + * @magentoConfigFixture admin/security/use_form_key 0 + * @magentoAppArea adminhtml + */ + public function testValidateWithInvalidFormKey() + { + $this->request->setPost( + new Parameters(['form_key' => $this->formKey->getFormKey() .'1']) + ); + $this->request->setMethod(HttpRequest::METHOD_POST); + + $this->validator->validate( + $this->request, + $this->mockUnawareAction + ); + } + + /** + * @magentoConfigFixture admin/security/use_form_key 0 + * @magentoAppArea adminhtml + */ + public function testValidateInvalidWithAwareAction() + { + $this->request->setParams([self::AWARE_VALIDATION_PARAM => '']); + + /** @var InvalidRequestException|null $caught */ + $caught = null; + try { + $this->validator->validate( + $this->request, + $this->mockAwareAction + ); + } catch (InvalidRequestException $exception) { + $caught = $exception; + } + + $this->assertNotNull($caught); + /** @var Response $response */ + $response = $caught->getReplaceResult(); + $this->assertInstanceOf(Response::class, $response); + $this->assertEquals( + self::AWARE_LOCATION_VALUE, + $response->getHeader('Location')->getFieldValue() + ); + $this->assertNull($caught->getMessages()); + } + + /** + * @magentoAppArea adminhtml + */ + public function testValidateValidWithAwareAction() + { + $this->request->setParams( + [self::AWARE_VALIDATION_PARAM => '1'] + ); + + $this->validator->validate( + $this->request, + $this->mockAwareAction + ); + } + + /** + * @magentoConfigFixture admin/security/use_form_key 1 + * @magentoAppArea adminhtml + */ + public function testValidateWithCsrfAwareAction() + { + //Setting up request that would be valid for default validation. + $this->request->setMethod(HttpRequest::METHOD_GET); + $this->auth->login( + TestBootstrap::ADMIN_NAME, + TestBootstrap::ADMIN_PASSWORD + ); + $this->request->setParams([ + BackendUrl::SECRET_KEY_PARAM_NAME => $this->url->getSecretKey(), + ]); + + /** @var InvalidRequestException|null $caught */ + $caught = null; + try { + $this->validator->validate( + $this->request, + $this->mockCsrfAwareAction + ); + } catch (InvalidRequestException $exception) { + $caught = $exception; + } + + //Checking that custom validation was called and invalidated + //valid request. + $this->assertNotNull($caught); + $this->assertCount(1, $caught->getMessages()); + $this->assertEquals( + self::CSRF_AWARE_MESSAGE, + $caught->getMessages()[0]->getText() + ); + } + + public function testInvalidAjaxRequest() + { + //Setting up AJAX request with invalid secret key. + $this->request->setMethod(HttpRequest::METHOD_GET); + $this->auth->login( + TestBootstrap::ADMIN_NAME, + TestBootstrap::ADMIN_PASSWORD + ); + $this->request->setParams([ + BackendUrl::SECRET_KEY_PARAM_NAME => 'invalid', + 'isAjax' => '1' + ]); + + /** @var InvalidRequestException|null $caught */ + $caught = null; + try { + $this->validator->validate( + $this->request, + $this->mockUnawareAction + ); + } catch (InvalidRequestException $exception) { + $caught = $exception; + } + + $this->assertNotNull($caught); + $this->assertInstanceOf( + ResultInterface::class, + $caught->getReplaceResult() + ); + /** @var ResultInterface $result */ + $result = $caught->getReplaceResult(); + /** @var HttpResponse $response */ + $response = $this->httpResponseFactory->create(); + $result->renderResult($response); + $this->assertEmpty($response->getBody()); + $this->assertEquals(401, $response->getHttpResponseCode()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index eca5cf79c0664..1cbdbd128bbf4 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -219,6 +219,7 @@ public function testConfirmActionAlreadyActive() public function testNoFormKeyCreatePostAction() { $this->fillRequestWithAccountData('test1@email.com'); + $this->getRequest()->setPostValue('form_key', null); $this->dispatch('customer/account/createPost'); $this->assertNull($this->getCustomerByEmail('test1@email.com')); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Request/CsrfValidatorTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Request/CsrfValidatorTest.php new file mode 100644 index 0000000000000..9246be52f41bf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Request/CsrfValidatorTest.php @@ -0,0 +1,269 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\App\Request; + +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\Controller\Result\RedirectFactory; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Exception\NotFoundException; +use Magento\Framework\Phrase; +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\App\Request\Http as HttpRequest; +use Zend\Stdlib\Parameters; +use Magento\Framework\App\Response\Http as HttpResponse; +use Magento\Framework\App\Response\HttpFactory as HttpResponseFactory; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class CsrfValidatorTest extends TestCase +{ + private const AWARE_URL = 'test/1'; + + private const AWARE_VALIDATION_PARAM = 'test_param'; + + private const AWARE_MESSAGE = 'custom validation failed'; + + /** + * @var ActionInterface + */ + private $mockUnawareAction; + + /** + * @var ActionInterface + */ + private $mockAwareAction; + + /** + * @var CsrfValidator + */ + private $validator; + + /** + * @var HttpRequest + */ + private $request; + + /** + * @var FormKey + */ + private $formKey; + + /** + * @var HttpResponseFactory + */ + private $httpResponseFactory; + + /** + * @return ActionInterface + */ + private function createUnawareAction(): ActionInterface + { + return new class implements ActionInterface { + /** + * @inheritDoc + */ + public function execute() + { + throw new NotFoundException(new Phrase('Not implemented')); + } + }; + } + + /** + * @return ActionInterface + */ + private function createAwareAction(): ActionInterface + { + $u = self::AWARE_URL; + $m = self::AWARE_MESSAGE; + $p = self::AWARE_VALIDATION_PARAM; + + return new class($u, $m, $p) implements CsrfAwareActionInterface { + /** + * @var string + */ + private $url; + + /** + * @var string + */ + private $message; + + /** + * @var string + */ + private $param; + + /** + * @param string $url + * @param string $message + * @param string $param + */ + public function __construct( + string $url, + string $message, + string $param + ) { + $this->url = $url; + $this->message = $message; + $this->param = $param; + } + + /** + * @inheritDoc + */ + public function execute() + { + throw new NotFoundException(new Phrase('Not implemented')); + } + + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + /** @var RedirectFactory $redirectFactory */ + $redirectFactory = Bootstrap::getObjectManager() + ->get(RedirectFactory::class); + $redirect = $redirectFactory->create(); + $redirect->setUrl($this->url); + + return new InvalidRequestException( + $redirect, + [new Phrase($this->message)] + ); + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return (bool)$request->getParam($this->param); + } + }; + } + + /** + * @inheritDoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->request = $objectManager->get(HttpRequest::class); + $this->validator = $objectManager->get(CsrfValidator::class); + $this->mockUnawareAction = $this->createUnawareAction(); + $this->mockAwareAction = $this->createAwareAction(); + $this->formKey = $objectManager->get(FormKey::class); + $this->httpResponseFactory = $objectManager->get( + HttpResponseFactory::class + ); + } + + /** + * @magentoAppArea global + */ + public function testValidateInWrongArea() + { + $this->request->setMethod(HttpRequest::METHOD_POST); + $this->validator->validate( + $this->request, + $this->mockUnawareAction + ); + } + + /** + * @magentoAppArea frontend + */ + public function testValidateWithValidKey() + { + $this->request->setPost( + new Parameters(['form_key' => $this->formKey->getFormKey()]) + ); + $this->request->setMethod(HttpRequest::METHOD_POST); + + $this->validator->validate( + $this->request, + $this->mockUnawareAction + ); + } + + /** + * @expectedException \Magento\Framework\App\Request\InvalidRequestException + * @magentoAppArea adminhtml + */ + public function testValidateWithInvalidKey() + { + $this->request->setPost( + new Parameters(['form_key' => $this->formKey->getFormKey() .'1']) + ); + $this->request->setMethod(HttpRequest::METHOD_POST); + + $this->validator->validate( + $this->request, + $this->mockUnawareAction + ); + } + + /** + * @magentoAppArea frontend + */ + public function testValidateInvalidWithAwareAction() + { + $this->request->setMethod(HttpRequest::METHOD_POST); + + /** @var InvalidRequestException|null $caught */ + $caught = null; + try { + $this->validator->validate( + $this->request, + $this->mockAwareAction + ); + } catch (InvalidRequestException $exception) { + $caught = $exception; + } + + $this->assertNotNull($caught); + $this->assertInstanceOf(Redirect::class, $caught->getReplaceResult()); + /** @var HttpResponse $response */ + $response = $this->httpResponseFactory->create(); + $caught->getReplaceResult()->renderResult($response); + $this->assertContains( + self::AWARE_URL, + $response->getHeaders()->toString() + ); + $this->assertCount(1, $caught->getMessages()); + $this->assertEquals( + self::AWARE_MESSAGE, + $caught->getMessages()[0]->getText() + ); + } + + /** + * @magentoAppArea frontend + */ + public function testValidateValidWithAwareAction() + { + $this->request->setMethod(HttpRequest::METHOD_POST); + $this->request->setPost( + new Parameters([self::AWARE_VALIDATION_PARAM => '1']) + ); + + $this->validator->validate( + $this->request, + $this->mockAwareAction + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/PageCache/Plugin/RegisterFormKeyFromCookieTest.php b/dev/tests/integration/testsuite/Magento/PageCache/Plugin/RegisterFormKeyFromCookieTest.php new file mode 100644 index 0000000000000..eb23b0b185a00 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PageCache/Plugin/RegisterFormKeyFromCookieTest.php @@ -0,0 +1,68 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\PageCache\Plugin; + +use Magento\Framework\App\FrontController; +use Magento\Framework\App\RequestInterface; +use PHPUnit\Framework\TestCase; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Data\Form\FormKey\Validator as FormKeyValidator; +use Magento\TestFramework\Helper\Bootstrap; + +class RegisterFormKeyFromCookieTest extends TestCase +{ + /** + * @var HttpRequest + */ + private $request; + + /** + * @var FrontController + */ + private $frontController; + + /** + * @var FormKeyValidator + */ + private $formKeyValidator; + + /** + * @inheritDoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->request = $objectManager->get(RequestInterface::class); + $this->frontController = $objectManager->get( + FrontController::class + ); + $this->formKeyValidator = $objectManager->get(FormKeyValidator::class); + } + + /** + * @magentoAppArea frontend + */ + public function testTakenFromCookie() + { + if (!Bootstrap::canTestHeaders()) { + $this->markTestSkipped( + 'Can\'t test dispatch process without sending headers' + ); + } + $_SERVER['HTTP_HOST'] = 'localhost'; + $formKey = 'customFormKey'; + $_COOKIE['form_key'] = $formKey; + $this->request->setMethod(HttpRequest::METHOD_POST); + $this->request->setParam('form_key', $formKey); + $this->request->setRequestUri('core/index/index'); + $this->frontController->dispatch($this->request); + $this->assertTrue($this->formKeyValidator->validate($this->request)); + } +} diff --git a/lib/internal/Magento/Framework/App/CsrfAwareActionInterface.php b/lib/internal/Magento/Framework/App/CsrfAwareActionInterface.php new file mode 100644 index 0000000000000..8413b945a1141 --- /dev/null +++ b/lib/internal/Magento/Framework/App/CsrfAwareActionInterface.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\App; + +use Magento\Framework\App\Request\InvalidRequestException; + +/** + * Action that's aware of CSRF protection. + */ +interface CsrfAwareActionInterface extends ActionInterface +{ + /** + * Create exception in case CSRF validation failed. + * Return null if default exception will suffice. + * + * @param RequestInterface $request + * + * @return InvalidRequestException|null + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException; + + /** + * Perform custom request validation. + * Return null if default validation is needed. + * + * @param RequestInterface $request + * + * @return bool|null + */ + public function validateForCsrf(RequestInterface $request): ?bool; +} diff --git a/lib/internal/Magento/Framework/App/FrontController.php b/lib/internal/Magento/Framework/App/FrontController.php index 02b390289c9a9..03d6ad7ab3f02 100644 --- a/lib/internal/Magento/Framework/App/FrontController.php +++ b/lib/internal/Magento/Framework/App/FrontController.php @@ -7,6 +7,16 @@ */ namespace Magento\Framework\App; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\App\Request\ValidatorInterface as RequestValidator; +use Magento\Framework\Exception\NotFoundException; +use Magento\Framework\Message\ManagerInterface as MessageManager; +use Magento\Framework\App\Action\AbstractAction; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class FrontController implements FrontControllerInterface { /** @@ -15,27 +25,45 @@ class FrontController implements FrontControllerInterface protected $_routerList; /** - * @var \Magento\Framework\App\ResponseInterface + * @var ResponseInterface */ protected $response; + /** + * @var RequestValidator + */ + private $requestValidator; + + /** + * @var MessageManager + */ + private $messages; + /** * @param RouterListInterface $routerList - * @param \Magento\Framework\App\ResponseInterface $response + * @param ResponseInterface $response + * @param RequestValidator|null $requestValidator + * @param MessageManager|null $messageManager */ public function __construct( RouterListInterface $routerList, - \Magento\Framework\App\ResponseInterface $response + ResponseInterface $response, + ?RequestValidator $requestValidator = null, + ?MessageManager $messageManager = null ) { $this->_routerList = $routerList; $this->response = $response; + $this->requestValidator = $requestValidator + ?? ObjectManager::getInstance()->get(RequestValidator::class); + $this->messages = $messageManager + ?? ObjectManager::getInstance()->get(MessageManager::class); } /** * Perform action and generate response * * @param RequestInterface $request - * @return ResponseInterface|\Magento\Framework\Controller\ResultInterface + * @return ResponseInterface|ResultInterface * @throws \LogicException */ public function dispatch(RequestInterface $request) @@ -49,13 +77,10 @@ public function dispatch(RequestInterface $request) try { $actionInstance = $router->match($request); if ($actionInstance) { - $request->setDispatched(true); - $this->response->setNoCacheHeaders(); - if ($actionInstance instanceof \Magento\Framework\App\Action\AbstractAction) { - $result = $actionInstance->dispatch($request); - } else { - $result = $actionInstance->execute(); - } + $result = $this->processRequest( + $request, + $actionInstance + ); break; } } catch (\Magento\Framework\Exception\NotFoundException $e) { @@ -72,4 +97,42 @@ public function dispatch(RequestInterface $request) } return $result; } + + /** + * @param RequestInterface $request + * @param ActionInterface $actionInstance + * @throws NotFoundException + * + * @return ResponseInterface|ResultInterface + */ + private function processRequest( + RequestInterface $request, + ActionInterface $actionInstance + ) { + $request->setDispatched(true); + $this->response->setNoCacheHeaders(); + //Validating request. + try { + $this->requestValidator->validate( + $request, + $actionInstance + ); + + if ($actionInstance instanceof AbstractAction) { + $result = $actionInstance->dispatch($request); + } else { + $result = $actionInstance->execute(); + } + } catch (InvalidRequestException $exception) { + //Validation failed - processing validation results. + $result = $exception->getReplaceResult(); + if ($messages = $exception->getMessages()) { + foreach ($messages as $message) { + $this->messages->addErrorMessage($message); + } + } + } + + return $result; + } } diff --git a/lib/internal/Magento/Framework/App/Request/CsrfValidator.php b/lib/internal/Magento/Framework/App/Request/CsrfValidator.php new file mode 100644 index 0000000000000..c930fc920907c --- /dev/null +++ b/lib/internal/Magento/Framework/App/Request/CsrfValidator.php @@ -0,0 +1,132 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\App\Request; + +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\Area; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\State as AppState; +use Magento\Framework\Data\Form\FormKey\Validator as FormKeyValidator; +use Magento\Framework\Controller\Result\RedirectFactory; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Phrase; + +/** + * Validate request for being CSRF protected. + */ +class CsrfValidator implements ValidatorInterface +{ + /** + * @var FormKeyValidator + */ + private $formKeyValidator; + + /** + * @var RedirectFactory + */ + private $redirectFactory; + + /** + * @var AppState + */ + private $appState; + + /** + * @param FormKeyValidator $formKeyValidator + * @param RedirectFactory $redirectFactory + * @param AppState $appState + */ + public function __construct( + FormKeyValidator $formKeyValidator, + RedirectFactory $redirectFactory, + AppState $appState + ) { + $this->formKeyValidator = $formKeyValidator; + $this->redirectFactory = $redirectFactory; + $this->appState = $appState; + } + + /** + * @param HttpRequest $request + * @param ActionInterface $action + * + * @return bool + */ + private function validateRequest( + HttpRequest $request, + ActionInterface $action + ): bool { + $valid = null; + if ($action instanceof CsrfAwareActionInterface) { + $valid = $action->validateForCsrf($request); + } + if ($valid === null) { + $valid = !$request->isPost() + || $request->isAjax() + || $this->formKeyValidator->validate($request); + } + + return $valid; + } + + /** + * @param HttpRequest $request + * @param ActionInterface $action + * + * @return InvalidRequestException + */ + private function createException( + HttpRequest $request, + ActionInterface $action + ): InvalidRequestException { + $exception = null; + if ($action instanceof CsrfAwareActionInterface) { + $exception = $action->createCsrfValidationException($request); + } + if (!$exception) { + $response = $this->redirectFactory->create() + ->setRefererOrBaseUrl() + ->setHttpResponseCode(302); + $messages = [ + new Phrase('Invalid Form Key. Please refresh the page.'), + ]; + $exception = new InvalidRequestException($response, $messages); + } + + return $exception; + } + + /** + * @inheritDoc + */ + public function validate( + RequestInterface $request, + ActionInterface $action + ): void { + try { + $areaCode = $this->appState->getAreaCode(); + } catch (LocalizedException $exception) { + $areaCode = null; + } + if ($request instanceof HttpRequest + && in_array( + $areaCode, + [Area::AREA_FRONTEND, Area::AREA_ADMINHTML], + true + ) + ) { + $valid = $this->validateRequest($request, $action); + if (!$valid) { + throw $this->createException($request, $action); + } + } + } +} diff --git a/lib/internal/Magento/Framework/App/Request/InvalidRequestException.php b/lib/internal/Magento/Framework/App/Request/InvalidRequestException.php new file mode 100644 index 0000000000000..3d408b0050686 --- /dev/null +++ b/lib/internal/Magento/Framework/App/Request/InvalidRequestException.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\App\Request; + +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Exception\RuntimeException; +use Magento\Framework\Phrase; + +/** + * Received request is invalid. + */ +class InvalidRequestException extends RuntimeException +{ + /** + * @var ResponseInterface|ResultInterface + */ + private $replaceResult; + + /** + * @var Phrase[]|null + */ + private $messages; + + /** + * @param ResponseInterface|ResultInterface $replaceResult Use this result + * instead of calling action instance. + * @param Phrase[]|null $messages Messages to show to client + * as error messages. + */ + public function __construct($replaceResult, ?array $messages = null) + { + parent::__construct(new Phrase('Invalid request received')); + + $this->replaceResult = $replaceResult; + $this->messages = $messages; + } + + /** + * @return ResponseInterface|ResultInterface + */ + public function getReplaceResult() + { + return $this->replaceResult; + } + + /** + * @return Phrase[]|null + */ + public function getMessages(): ?array + { + return $this->messages; + } +} diff --git a/lib/internal/Magento/Framework/App/Request/ValidatorInterface.php b/lib/internal/Magento/Framework/App/Request/ValidatorInterface.php new file mode 100644 index 0000000000000..f4b1503ac5cb6 --- /dev/null +++ b/lib/internal/Magento/Framework/App/Request/ValidatorInterface.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\App\Request; + +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\RequestInterface; + +/** + * Validate interface before giving passing it to an ActionInterface. + */ +interface ValidatorInterface +{ + /** + * Validate request and throw the exception if it's invalid. + * + * @param RequestInterface $request + * @param ActionInterface $action + * @throws InvalidRequestException If request was invalid. + * + * @return void + */ + public function validate( + RequestInterface $request, + ActionInterface $action + ): void; +} diff --git a/lib/web/mage/common.js b/lib/web/mage/common.js index a333277d9e3cd..4742c77ea8627 100644 --- a/lib/web/mage/common.js +++ b/lib/web/mage/common.js @@ -11,4 +11,24 @@ define([ /* Form with auto submit feature */ $('form[data-auto-submit="true"]').submit(); + + //Add form keys. + $(document).on( + 'submit', + 'form', + function (e) { + var formKeyElement, + form = $(e.target), + formKey = $('input[name="form_key"]').val(); + + if (formKey && !form.find('input[name="form_key"]').length) { + formKeyElement = document.createElement('input'); + formKeyElement.setAttribute('type', 'hidden'); + formKeyElement.setAttribute('name', 'form_key'); + formKeyElement.setAttribute('value', formKey); + formKeyElement.setAttribute('auto-added-form-key', '1'); + form.get(0).appendChild(formKeyElement); + } + } + ); }); From 63722cbc8adb3c3bbee2bd2386e30133c63c7042 Mon Sep 17 00:00:00 2001 From: Jose Ortega <joc.hhop@gmail.com> Date: Thu, 19 Jul 2018 11:11:55 +0200 Subject: [PATCH 0387/1171] Removed unused interface reference --- app/code/Magento/Customer/Model/Visitor.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Customer/Model/Visitor.php b/app/code/Magento/Customer/Model/Visitor.php index 4624dd8b6bcf5..763a7afbfa851 100644 --- a/app/code/Magento/Customer/Model/Visitor.php +++ b/app/code/Magento/Customer/Model/Visitor.php @@ -6,8 +6,6 @@ namespace Magento\Customer\Model; -use Magento\Framework\Indexer\StateInterface; - /** * Class Visitor * @package Magento\Customer\Model From b5a2a983c5b8441b8733f2d36055a4fb4f79a1f3 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Thu, 19 Jul 2018 10:22:50 +0000 Subject: [PATCH 0388/1171] graphql-ce-120: fixed resolver name --- .../Resolver/{StoreConfigs.php => StoreConfigsResolver.php} | 2 +- app/code/Magento/StoreGraphQl/etc/schema.graphqls | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename app/code/Magento/StoreGraphQl/Model/Resolver/{StoreConfigs.php => StoreConfigsResolver.php} (97%) diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigs.php b/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigsResolver.php similarity index 97% rename from app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigs.php rename to app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigsResolver.php index c1daef97d5c60..609985b48e2be 100644 --- a/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigs.php +++ b/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigsResolver.php @@ -18,7 +18,7 @@ /** * StoreConfig page field resolver, used for GraphQL request processing. */ -class StoreConfigs implements ResolverInterface +class StoreConfigsResolver implements ResolverInterface { /** * @var StoreConfigsDataProvider diff --git a/app/code/Magento/StoreGraphQl/etc/schema.graphqls b/app/code/Magento/StoreGraphQl/etc/schema.graphqls index 2b45190c540eb..d0597de04b668 100644 --- a/app/code/Magento/StoreGraphQl/etc/schema.graphqls +++ b/app/code/Magento/StoreGraphQl/etc/schema.graphqls @@ -4,7 +4,7 @@ type Query { storeConfigs ( storeCodes: [String] @doc(description: "Store Codes of the store configs") ): StoreConfigs - @resolver(class: "Magento\\StoreGraphQl\\Model\\Resolver\\StoreConfigs") @doc(description: "The store configs query") + @resolver(class: "Magento\\StoreGraphQl\\Model\\Resolver\\StoreConfigsResolver") @doc(description: "The store configs query") } type Website @doc(description: "The type contains information about a website") { From 88316c3fde199df9bceba42ba9f553e7ad596d8d Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Thu, 19 Jul 2018 14:59:09 +0300 Subject: [PATCH 0389/1171] MAGETWO-93264: Wrong order status on registered checkout within Braintree Credit Card from Storefront with Signifyd Declined Guarantee --- .../Test/Block/SignifydConsole/CaseInfo.php | 2 +- .../Test/Block/SignifydConsole/CaseSearch.php | 52 +++++++++++++++++++ .../Block/SignifydConsole/SignifydLogin.php | 33 ++++++++++++ ...ateSignifydGuaranteeAndCancelOrderTest.xml | 1 - .../Test/TestStep/SignifydObserveCaseStep.php | 17 +++++- 5 files changed, 101 insertions(+), 4 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php index 5fe6096035803..8df3a48c78eab 100644 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php +++ b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php @@ -73,7 +73,7 @@ class CaseInfo extends Block * * @var string */ - private $orderAmount = '[ng-bind*="currentCase.orderAmount"]'; + private $orderAmount = '[ng-bind*="currentCase.orderTotalAmount"]'; /** * Locator value for order amount currency. diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseSearch.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseSearch.php index ef292de3a9e5f..16621f9a0cd48 100644 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseSearch.php +++ b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseSearch.php @@ -33,6 +33,20 @@ class CaseSearch extends Form */ private $selectCaseLink = 'ul[case-list=cases] li[case-list-case=case] a'; + /** + * Locator for resolving applied filters list. + * + * @var string + */ + private $appliedFilters = '.app-taglist > ul > li > a'; + + /** + * Locator for loading spinner. + * + * @var string + */ + private $spinner = '.cases-loading-spinner'; + /** * Fill search input with customer name and submit. * @@ -41,8 +55,46 @@ class CaseSearch extends Form */ public function searchCaseByCustomerName($customerName) { + $this->resetFilters(); $this->_rootElement->find($this->searchBar)->setValue($customerName); $this->_rootElement->find($this->submitButton)->click(); + $this->waitLoadingSpinner(); + } + + /** + * Reset applied filters. + * + * @return void + */ + private function resetFilters(): void + { + $filters = $this->_rootElement->getElements($this->appliedFilters); + if (!empty($filters)) { + foreach ($filters as $filter) { + $filter->click(); + $this->waitLoadingSpinner(); + } + } + } + + /** + * Wait until loading spinner disappeared. + * + * @return void + */ + private function waitLoadingSpinner(): void + { + $this->waitForElementNotVisible($this->spinner); + } + + /** + * Checks if any case is visible. + * + * @return bool + */ + public function isAnyCaseVisible(): bool + { + return $this->_rootElement->find($this->selectCaseLink)->isVisible(); } /** diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/SignifydLogin.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/SignifydLogin.php index 7f530afe4df63..7705a67360e55 100644 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/SignifydLogin.php +++ b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/SignifydLogin.php @@ -6,6 +6,8 @@ namespace Magento\Signifyd\Test\Block\SignifydConsole; use Magento\Mtf\Block\Form; +use Magento\Mtf\Client\Element\SimpleElement; +use Magento\Mtf\Fixture\FixtureInterface; /** * Signifyd login block. @@ -19,6 +21,23 @@ class SignifydLogin extends Form */ private $loginButton = '[type=submit]'; + /** + * Locator for admin form notification window. + * + * @var string + */ + private $notificationCloseButton = '.wm-close-button'; + + /** + * @inheritdoc + */ + public function fill(FixtureInterface $fixture, SimpleElement $element = null) + { + $this->closeNotification(); + + return parent::fill($fixture, $element); + } + /** * Login to Signifyd. * @@ -26,6 +45,20 @@ class SignifydLogin extends Form */ public function login() { + $this->closeNotification(); $this->_rootElement->find($this->loginButton)->click(); } + + /** + * Close notification popup. + * + * @return void + */ + private function closeNotification(): void + { + $notification = $this->browser->find($this->notificationCloseButton); + if ($notification->isVisible()) { + $notification->click(); + } + } } diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.xml index 404229c2451cd..082627fe5821d 100644 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.xml +++ b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.xml @@ -62,7 +62,6 @@ <constraint name="Magento\Sales\Test\Constraint\AssertCancelInCommentsHistory" /> <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydCaseInCommentsHistory" /> <constraint name="Magento\Signifyd\Test\Constraint\AssertAwaitingSignifydGuaranteeInCommentsHistory" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydGuaranteeCancelInCommentsHistory" /> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydObserveCaseStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydObserveCaseStep.php index 126e32489e6c9..c00c81fa237e0 100644 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydObserveCaseStep.php +++ b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydObserveCaseStep.php @@ -75,6 +75,11 @@ class SignifydObserveCaseStep implements TestStepInterface */ private $testStepFactory; + /** + * @var int + */ + private $searchAttempts = 10; + /** * @param AssertCaseInfoOnSignifydConsole $assertCaseInfoOnSignifydConsole * @param SignifydAddress $signifydAddress @@ -111,8 +116,16 @@ public function __construct( public function run() { $this->signifydCases->open(); - $this->signifydCases->getCaseSearchBlock() - ->searchCaseByCustomerName($this->signifydAddress->getFirstname()); + // Search case few times because it can appear with delay. + for ($attempts = $this->searchAttempts; $attempts > 0; $attempts--) { + $this->signifydCases->getCaseSearchBlock() + ->searchCaseByCustomerName($this->signifydAddress->getFirstname()); + if ($this->signifydCases->getCaseSearchBlock()->isAnyCaseVisible()) { + break; + } + sleep(3); + } + $this->signifydCases->getCaseSearchBlock()->selectCase(); $this->signifydCases->getCaseInfoBlock()->flagCase($this->signifydData->getCaseFlag()); From cfd750241399da53b66caeebfbc16b51f47591dd Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Thu, 19 Jul 2018 15:29:12 +0300 Subject: [PATCH 0390/1171] GraphQL-19: Add breadcrumbs support --- .../Model/Resolver/Category/Breadcrumbs.php | 43 ++++---------- .../Category/DataProvider/Breadcrumbs.php | 58 +++++++++++++++++++ 2 files changed, 69 insertions(+), 32 deletions(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Category/DataProvider/Breadcrumbs.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php index 897fb1d649d7d..d49c7f5e5674d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php @@ -7,12 +7,12 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Category; +use \Magento\CatalogGraphQl\Model\Resolver\Category\DataProvider\Breadcrumbs as BreadcrumbsDataProvider; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Query\Resolver\Value; use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; -use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; /** * Retrieves breadcrumbs @@ -20,9 +20,9 @@ class Breadcrumbs implements ResolverInterface { /** - * @var CollectionFactory + * @var BreadcrumbsDataProvider */ - private $collectionFactory; + private $breadcrumbsDataProvider; /** * @var ValueFactory @@ -30,24 +30,22 @@ class Breadcrumbs implements ResolverInterface private $valueFactory; /** + * @param BreadcrumbsDataProvider $breadcrumbsDataProvider * @param ValueFactory $valueFactory - * @param CollectionFactory $collectionFactory */ public function __construct( - ValueFactory $valueFactory, - CollectionFactory $collectionFactory + BreadcrumbsDataProvider $breadcrumbsDataProvider, + ValueFactory $valueFactory ) { - $this->collectionFactory = $collectionFactory; + $this->breadcrumbsDataProvider = $breadcrumbsDataProvider; $this->valueFactory = $valueFactory; } /** - * {@inheritdoc} + * @inheritdoc */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null): Value { - $breadcrumbs = []; - if (!isset($value['path'])) { $result = function () { return null; @@ -55,29 +53,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return $this->valueFactory->create($result); } - $categoryPath = $value['path']; - $pathCategoryIds = explode('/', $categoryPath); - $parentCategoryIds = array_slice($pathCategoryIds, 2, count($pathCategoryIds) - 3); - - if (count($parentCategoryIds)) { - $collection = $this->collectionFactory->create(); - $collection->addAttributeToSelect(['name', 'url_key']); - $collection->addAttributeToFilter('entity_id', $parentCategoryIds); - - foreach ($collection as $category) { - $breadcrumbs[] = [ - 'category_id' => $category->getId(), - 'category_name' => $category->getName(), - 'category_level' => $category->getLevel(), - 'category_url_key' => $category->getUrlKey(), - ]; - } - } - - $result = function () use ($breadcrumbs) { - return count($breadcrumbs) ? $breadcrumbs : null; + $result = function () use ($value) { + $breadcrumbsData = $this->breadcrumbsDataProvider->getData($value['path']); + return count($breadcrumbsData) ? $breadcrumbsData : null; }; - return $this->valueFactory->create($result); } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/DataProvider/Breadcrumbs.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/DataProvider/Breadcrumbs.php new file mode 100644 index 0000000000000..9e23c4f1e9736 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/DataProvider/Breadcrumbs.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Category\DataProvider; + +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; + +/** + * Breadcrumbs data provider + */ +class Breadcrumbs +{ + /** + * @var CollectionFactory + */ + private $collectionFactory; + + /** + * @param CollectionFactory $collectionFactory + */ + public function __construct( + CollectionFactory $collectionFactory + ) { + $this->collectionFactory = $collectionFactory; + } + + /** + * @param string $categoryPath + * @return array + */ + public function getData(string $categoryPath): array + { + $breadcrumbsData = []; + + $pathCategoryIds = explode('/', $categoryPath); + $parentCategoryIds = array_slice($pathCategoryIds, 2, -1); + + if (count($parentCategoryIds)) { + $collection = $this->collectionFactory->create(); + $collection->addAttributeToSelect(['name', 'url_key']); + $collection->addAttributeToFilter('entity_id', $parentCategoryIds); + + foreach ($collection as $category) { + $breadcrumbsData[] = [ + 'category_id' => $category->getId(), + 'category_name' => $category->getName(), + 'category_level' => $category->getLevel(), + 'category_url_key' => $category->getUrlKey(), + ]; + } + } + return $breadcrumbsData; + } +} From 080420ac5bfe520b01f40473c229040afdf5707e Mon Sep 17 00:00:00 2001 From: Devagouda Patil <depatil@Devagoudas-MacBook-Pro.local> Date: Thu, 19 Jul 2018 10:56:38 -0500 Subject: [PATCH 0391/1171] MAGETWO-90927: Warning on product save when all images are deleted (PHP 7.2) - Updated existing Functional tests by removing commented lines per MAGETWO-91177 instructions --- .../Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml | 4 +--- .../Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml | 4 +--- .../Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml | 4 +--- .../Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml | 4 +--- .../Test/AdminRemoveDefaultImageDownloadableProductTest.xml | 4 +--- .../Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml | 4 +--- 6 files changed, 6 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml index 9ce345cde681c..ccd729ac841cd 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml @@ -74,9 +74,7 @@ <!-- Remove image from product --> <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> - <!-- Skip success message check when saving product because of bug MAGETWO-91177 --> - <!-- actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/--> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> <!-- Assert product image not in admin product form --> <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml index f20e6caf637d4..4a3b370c7da08 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml @@ -46,9 +46,7 @@ <!-- Remove image from product --> <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> - <!-- Skip success message check when saving product because of bug MAGETWO-91177 --> - <!-- actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/--> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> <!-- Assert product image not in admin product form --> <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml index 86cc16e141e0a..e249c8e8ceabc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml @@ -46,9 +46,7 @@ <!-- Remove image from product --> <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> - <!-- Skip success message check when saving product because of bug MAGETWO-91177 --> - <!-- actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/--> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> <!-- Assert product image not in admin product form --> <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml index 1c34566322c23..65e1300a4e6b3 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml @@ -109,9 +109,7 @@ <!-- Remove image from product --> <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> - <!-- Skip success message check when saving product because of bug MAGETWO-91177 --> - <!-- actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/--> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> <!-- Assert product image not in admin product form --> <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml index 370148c6a4167..3b10f60c97340 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml @@ -59,9 +59,7 @@ <!-- Remove image from product --> <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> - <!-- Skip success message check when saving product because of bug MAGETWO-91177 --> - <!-- actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/--> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> <!-- Assert product image not in admin product form --> <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml index a0408a744cc7e..54062d97f7c8f 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml @@ -69,9 +69,7 @@ <!-- Remove image from product --> <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> - <!-- Skip success message check when saving product because of bug MAGETWO-91177 --> - <!-- actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/--> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> <!-- Assert product image not in admin product form --> <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"/> From 35b968e0dc897fb28fa95255131861d04950745f Mon Sep 17 00:00:00 2001 From: Pratik Oza <33807558+mage2pratik@users.noreply.github.com> Date: Thu, 19 Jul 2018 23:06:45 +0530 Subject: [PATCH 0392/1171] The optional parameter passed last in the params list --- .../Quote/Model/ResourceModel/Quote/Item/Collection.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php index 8a89b331172a9..2405eaa9d37a3 100644 --- a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php +++ b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php @@ -59,9 +59,9 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\VersionContro * @param Option\CollectionFactory $itemOptionCollectionFactory * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory * @param \Magento\Quote\Model\Quote\Config $quoteConfig - * @param \Magento\Store\Model\StoreManagerInterface|null $storeManager * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource + * @param \Magento\Store\Model\StoreManagerInterface|null $storeManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -73,9 +73,9 @@ public function __construct( \Magento\Quote\Model\ResourceModel\Quote\Item\Option\CollectionFactory $itemOptionCollectionFactory, \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory, \Magento\Quote\Model\Quote\Config $quoteConfig, - \Magento\Store\Model\StoreManagerInterface $storeManager = null, \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null + \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null, + \Magento\Store\Model\StoreManagerInterface $storeManager = null ) { parent::__construct( $entityFactory, From 4cbb3abaf07f4b0c7ea55958a2330c8500fafb6a Mon Sep 17 00:00:00 2001 From: "Vasiliev.A" <a.vasiliev.sam@gmail.com> Date: Fri, 18 May 2018 13:09:28 +0300 Subject: [PATCH 0393/1171] Fix confirmation for registered customer --- app/code/Magento/Newsletter/Model/Subscriber.php | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php index dcb6341a7a8dd..653a27b68c049 100644 --- a/app/code/Magento/Newsletter/Model/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/Subscriber.php @@ -428,13 +428,7 @@ public function subscribe($email) || $this->getStatus() == self::STATUS_NOT_ACTIVE ) { if ($isConfirmNeed === true) { - // if user subscribes own login email - confirmation is not needed - $isOwnSubscribes = $isSubscribeOwnEmail; - if ($isOwnSubscribes == true) { - $this->setStatus(self::STATUS_SUBSCRIBED); - } else { - $this->setStatus(self::STATUS_NOT_ACTIVE); - } + $this->setStatus(self::STATUS_NOT_ACTIVE); } else { $this->setStatus(self::STATUS_SUBSCRIBED); } @@ -460,9 +454,7 @@ public function subscribe($email) try { /* Save model before sending out email */ $this->save(); - if ($isConfirmNeed === true - && $isOwnSubscribes === false - ) { + if ($isConfirmNeed === true) { $this->sendConfirmationRequestEmail(); } else { $this->sendConfirmationSuccessEmail(); From 924567ff44fcc0cd08bf9b77037a60f1cb2cb8bc Mon Sep 17 00:00:00 2001 From: "Vasiliev.A" <a.vasiliev.sam@gmail.com> Date: Fri, 18 May 2018 13:13:15 +0300 Subject: [PATCH 0394/1171] Fix confirmation for registered customer --- app/code/Magento/Newsletter/Model/Subscriber.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php index 653a27b68c049..76d55de5954b1 100644 --- a/app/code/Magento/Newsletter/Model/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/Subscriber.php @@ -419,7 +419,6 @@ public function subscribe($email) self::XML_PATH_CONFIRMATION_FLAG, \Magento\Store\Model\ScopeInterface::SCOPE_STORE ) == 1 ? true : false; - $isOwnSubscribes = false; $isSubscribeOwnEmail = $this->_customerSession->isLoggedIn() && $this->_customerSession->getCustomerDataObject()->getEmail() == $email; From ceb7a412c2c9b909529a54a0e59a6caf289cf4fd Mon Sep 17 00:00:00 2001 From: "Vasiliev.A" <a.vasiliev.sam@gmail.com> Date: Fri, 18 May 2018 14:23:42 +0300 Subject: [PATCH 0395/1171] Fix unit test --- .../Test/Unit/Model/SubscriberTest.php | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php index 39beade9346d4..1318cb1f98f58 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Newsletter\Test\Unit\Model; +use Magento\Newsletter\Model\Subscriber; + /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -116,7 +118,7 @@ public function testSubscribe() $email = 'subscriber_email@magento.com'; $this->resource->expects($this->any())->method('loadByEmail')->willReturn( [ - 'subscriber_status' => 3, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, 'subscriber_email' => $email, 'name' => 'subscriber_name' ] @@ -133,7 +135,7 @@ public function testSubscribe() $this->sendEmailCheck(); $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - $this->assertEquals(1, $this->subscriber->subscribe($email)); + $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $this->subscriber->subscribe($email)); } public function testSubscribeNotLoggedIn() @@ -141,7 +143,7 @@ public function testSubscribeNotLoggedIn() $email = 'subscriber_email@magento.com'; $this->resource->expects($this->any())->method('loadByEmail')->willReturn( [ - 'subscriber_status' => 3, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, 'subscriber_email' => $email, 'name' => 'subscriber_name' ] @@ -158,7 +160,7 @@ public function testSubscribeNotLoggedIn() $this->sendEmailCheck(); $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - $this->assertEquals(2, $this->subscriber->subscribe($email)); + $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $this->subscriber->subscribe($email)); } public function testUpdateSubscription() @@ -175,7 +177,7 @@ public function testUpdateSubscription() ->willReturn( [ 'subscriber_id' => 1, - 'subscriber_status' => 1 + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED ] ); $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); @@ -210,7 +212,7 @@ public function testUnsubscribeCustomerById() ->willReturn( [ 'subscriber_id' => 1, - 'subscriber_status' => 1 + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED ] ); $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); @@ -236,7 +238,7 @@ public function testSubscribeCustomerById() ->willReturn( [ 'subscriber_id' => 1, - 'subscriber_status' => 3 + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED ] ); $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); @@ -262,7 +264,7 @@ public function testSubscribeCustomerById1() ->willReturn( [ 'subscriber_id' => 1, - 'subscriber_status' => 3 + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED ] ); $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); @@ -276,7 +278,7 @@ public function testSubscribeCustomerById1() $this->scopeConfig->expects($this->atLeastOnce())->method('getValue')->with()->willReturn(true); $this->subscriber->subscribeCustomerById($customerId); - $this->assertEquals(\Magento\Newsletter\Model\Subscriber::STATUS_NOT_ACTIVE, $this->subscriber->getStatus()); + $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $this->subscriber->getStatus()); } public function testSubscribeCustomerByIdAfterConfirmation() @@ -293,7 +295,7 @@ public function testSubscribeCustomerByIdAfterConfirmation() ->willReturn( [ 'subscriber_id' => 1, - 'subscriber_status' => 4 + 'subscriber_status' => Subscriber::STATUS_UNCONFIRMED ] ); $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); @@ -305,7 +307,7 @@ public function testSubscribeCustomerByIdAfterConfirmation() $this->scopeConfig->expects($this->atLeastOnce())->method('getValue')->with()->willReturn(true); $this->subscriber->updateSubscription($customerId); - $this->assertEquals(\Magento\Newsletter\Model\Subscriber::STATUS_SUBSCRIBED, $this->subscriber->getStatus()); + $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $this->subscriber->getStatus()); } public function testUnsubscribe() From 6f0bcceef1aa187c47fa0cfdc6644f4c91bdbee8 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Thu, 19 Jul 2018 14:06:24 -0500 Subject: [PATCH 0396/1171] MC-3078: Admin can create product attribute with text swatch --- .../AdminCreateProductAttributeSection.xml | 3 +- .../StorefrontProductInfoMainSection.xml | 3 + .../Mftf/Section/AdminManageSwatchSection.xml | 3 + .../Mftf/Test/AdminCreateTextSwatchTest.xml | 96 +++++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index be0e50ba5a3a5..377bc18d64764 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -14,7 +14,8 @@ <element name="ValueRequired" type="select" selector="#is_required"/> <element name="AdvancedProperties" type="button" selector="#advanced_fieldset-wrapper"/> <element name="DefaultValue" type="input" selector="#default_value_text"/> - <element name="Save" type="button" selector="#save"/> + <element name="Scope" type="select" selector="#is_global"/> + <element name="Save" type="button" selector="#save" timeout="30"/> <element name="SaveAndEdit" type="button" selector="#save_and_edit_button" timeout="30"/> <element name="TinyMCE4" type="button" selector="//span[text()='Default Value']/parent::label/following-sibling::div//div[@class='mce-branding-powered-by']"/> <element name="checkIfTabOpen" selector="//div[@id='advanced_fieldset-wrapper' and not(contains(@class,'opened'))]" type="button"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 7e1bec48aeebc..8744eb3e0b81b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -63,5 +63,8 @@ <element name="productAddToCompare" type="button" selector="a.action.tocompare"/> <element name="productOptionDropDownTitle" type="text" selector="//label[contains(.,'{{var1}}')]" parameterized="true"/> <element name="productOptionDropDownOptionTitle" type="text" selector="//label[contains(.,'{{var1}}')]/../div[@class='control']//select//option[contains(.,'{{var2}}')]" parameterized="true"/> + + <element name="swatchAttributeOptions" type="text" selector="div.swatch-attribute-options"/> + <element name="nthSwatchOptionText" type="button" selector="div.swatch-option.text:nth-of-type({{n}})" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml index 35f8faf424be4..176b266a450f1 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml @@ -12,6 +12,9 @@ <element name="adminInputByIndex" type="input" selector="optionvisual[value][option_{{var}}][0]" parameterized="true"/> <element name="addSwatch" type="button" selector="#add_new_swatch_visual_option_button" timeout="30"/> <element name="nthSwatch" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_window" parameterized="true"/> + <element name="addSwatchText" type="button" selector="#add_new_swatch_text_option_button"/> + <element name="swatchTextByIndex" type="input" selector="input[name='swatchtext[value][option_{{index}}][0]']" parameterized="true"/> + <element name="swatchAdminDescriptionByIndex" type="input" selector="input[name='optiontext[value][option_{{index}}][0]']" parameterized="true"/> <element name="nthChooseColor" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_row_name.colorpicker_handler" parameterized="true"/> <element name="nthDelete" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) button.delete-option" parameterized="true"/> </section> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml new file mode 100644 index 0000000000000..7700ef05450f3 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminCreateTextSwatchTest"> + <annotations> + <features value="Swatches"/> + <stories value="Create/configure swatches"/> + <title value="Admin can create product attribute with text swatch"/> + <description value="Admin can create product attribute with text swatch"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3078"/> + <group value="Swatches"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create a new product attribute of type "Text Swatch" --> + <amOnPage url="{{ProductAttributePage.url}}" stepKey="goToNewProductAttributePage"/> + <waitForPageLoad stepKey="waitForNewProductAttributePage"/> + <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillDefaultLabel"/> + <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="swatch_text" stepKey="selectInputType"/> + <click selector="{{AdminManageSwatchSection.addSwatchText}}" stepKey="clickAddSwatch0"/> + <fillField selector="{{AdminManageSwatchSection.swatchTextByIndex('0')}}" userInput="red" stepKey="fillSwatch0"/> + <fillField selector="{{AdminManageSwatchSection.swatchAdminDescriptionByIndex('0')}}" userInput="Something red." stepKey="fillDescription0"/> + <click selector="{{AdminManageSwatchSection.addSwatchText}}" stepKey="clickAddSwatch1"/> + <fillField selector="{{AdminManageSwatchSection.swatchTextByIndex('1')}}" userInput="green" stepKey="fillSwatch1"/> + <fillField selector="{{AdminManageSwatchSection.swatchAdminDescriptionByIndex('1')}}" userInput="Something green." stepKey="fillDescription1"/> + <click selector="{{AdminManageSwatchSection.addSwatchText}}" stepKey="clickAddSwatch2"/> + <fillField selector="{{AdminManageSwatchSection.swatchTextByIndex('2')}}" userInput="blue" stepKey="fillSwatch2"/> + <fillField selector="{{AdminManageSwatchSection.swatchAdminDescriptionByIndex('2')}}" userInput="Something blue." stepKey="fillDescription2"/> + <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> + <selectOption selector="{{AttributePropertiesSection.Scope}}" userInput="1" stepKey="selectGlobalScope"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSave"/> + + <!-- Create a configurable product to verify the storefront with --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad time="30" stepKey="waitForProductGrid"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> + <click selector="{{AdminProductGridActionSection.addConfigurableProduct}}" stepKey="clickOnAddConfigurableProduct"/> + <fillField userInput="{{_defaultProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{_defaultProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="{{_defaultProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{_defaultProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{_defaultProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + + <!-- Create configurations based off the Text Swatch we created earlier --> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickCreateConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickFilters"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="1" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> + + <!-- Go to the product page and see text swatch options --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnProductPage"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.swatchAttributeOptions}}" userInput="red" stepKey="seeRed"/> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('1')}}" userInput="option-label" stepKey="grabRedLabel"/> + <assertEquals stepKey="assertRedLabel"> + <expectedResult type="string">Something red.</expectedResult> + <actualResult type="string">{$grabRedLabel}</actualResult> + </assertEquals> + <see selector="{{StorefrontProductInfoMainSection.swatchAttributeOptions}}" userInput="green" stepKey="seeGreen"/> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('2')}}" userInput="option-label" stepKey="grabGreenLabel"/> + <assertEquals stepKey="assertGreenLabel"> + <expectedResult type="string">Something green.</expectedResult> + <actualResult type="string">{$grabGreenLabel}</actualResult> + </assertEquals> + <see selector="{{StorefrontProductInfoMainSection.swatchAttributeOptions}}" userInput="blue" stepKey="seeBlue"/> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('3')}}" userInput="option-label" stepKey="grabBlueLabel"/> + <assertEquals stepKey="assertBlueLabel"> + <expectedResult type="string">Something blue.</expectedResult> + <actualResult type="string">{$grabBlueLabel}</actualResult> + </assertEquals> + </test> +</tests> From e85a6e54ee15d8ea808d434b05eb9c666cdd772a Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Thu, 19 Jul 2018 19:26:42 +0000 Subject: [PATCH 0397/1171] graphql-ce-120: get single store config instead multiple --- .../Store/StoreConfigDataProvider.php | 94 +++++++++++++++++++ .../Store/StoreConfigsDataProvider.php | 80 ---------------- .../Model/Resolver/StoreConfigResolver.php | 63 +++++++++++++ .../Model/Resolver/StoreConfigsResolver.php | 84 ----------------- .../Magento/StoreGraphQl/etc/schema.graphqls | 9 +- ...erTest.php => StoreConfigResolverTest.php} | 74 ++++++--------- 6 files changed, 189 insertions(+), 215 deletions(-) create mode 100644 app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php delete mode 100644 app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigsDataProvider.php create mode 100644 app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigResolver.php delete mode 100644 app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigsResolver.php rename dev/tests/api-functional/testsuite/Magento/GraphQl/Store/{StoreConfigsResolverTest.php => StoreConfigResolverTest.php} (58%) diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php new file mode 100644 index 0000000000000..0252d7898beee --- /dev/null +++ b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\StoreGraphQl\Model\Resolver\Store; + +use Magento\Store\Api\Data\StoreConfigInterface; +use Magento\Store\Api\StoreConfigManagerInterface; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Store\Api\StoreResolverInterface; + +/** + * StoreConfig field data provider, used for GraphQL request processing. + */ +class StoreConfigDataProvider +{ + /** + * @var StoreConfigManagerInterface + */ + private $storeConfigManager; + + /** + * @var StoreResolverInterface + */ + private $storeResolver; + + /** + * @var StoreRepositoryInterface + */ + private $storeRepository; + + /** + * @param StoreConfigManagerInterface $storeConfigManager + * @param StoreResolverInterface $storeResolver + * @param StoreRepositoryInterface $storeRepository + */ + public function __construct( + StoreConfigManagerInterface $storeConfigManager, + StoreResolverInterface $storeResolver, + StoreRepositoryInterface $storeRepository + ) { + $this->storeConfigManager = $storeConfigManager; + $this->storeResolver = $storeResolver; + $this->storeRepository = $storeRepository; + } + + /** + * Get store config for current store + * + * @return array + */ + public function getStoreConfig() : array + { + $storeId = $this->storeResolver->getCurrentStoreId(); + $store = $this->storeRepository->getById($storeId); + $storeConfig = current($this->storeConfigManager->getStoreConfigs([$store->getCode()])); + + return $this->hidrateStoreConfig($storeConfig); + } + + /** + * Transform StoreConfig object to in array format + * + * @param StoreConfigInterface $storeConfig + * @return array + */ + private function hidrateStoreConfig($storeConfig): array + { + /** @var StoreConfigInterface $storeConfig */ + $storeConfigData = [ + 'id' => $storeConfig->getId(), + 'code' => $storeConfig->getCode(), + 'website_id' => $storeConfig->getWebsiteId(), + 'locale' => $storeConfig->getLocale(), + 'base_currency_code' => $storeConfig->getBaseCurrencyCode(), + 'default_display_currency_code' => $storeConfig->getDefaultDisplayCurrencyCode(), + 'timezone' => $storeConfig->getTimezone(), + 'weight_unit' => $storeConfig->getWeightUnit(), + 'base_url' => $storeConfig->getBaseUrl(), + 'base_link_url' => $storeConfig->getBaseLinkUrl(), + 'base_static_url' => $storeConfig->getSecureBaseStaticUrl(), + 'base_media_url' => $storeConfig->getBaseMediaUrl(), + 'secure_base_url' => $storeConfig->getSecureBaseUrl(), + 'secure_base_link_url' => $storeConfig->getSecureBaseLinkUrl(), + 'secure_base_static_url' => $storeConfig->getSecureBaseStaticUrl(), + 'secure_base_media_url' => $storeConfig->getSecureBaseMediaUrl() + ]; + + return $storeConfigData; + } +} diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigsDataProvider.php b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigsDataProvider.php deleted file mode 100644 index 0d7ce3aa1555f..0000000000000 --- a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigsDataProvider.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\StoreGraphQl\Model\Resolver\Store; - -use Magento\Store\Api\Data\StoreConfigInterface; -use Magento\Store\Api\StoreConfigManagerInterface; - -/** - * StoreConfig field data provider, used for GraphQL request processing. - */ -class StoreConfigsDataProvider -{ - /** - * @var StoreConfigManagerInterface - */ - private $storeConfigManager; - - /** - * @param StoreConfigManagerInterface $storeConfigManager - */ - public function __construct( - StoreConfigManagerInterface $storeConfigManager - ) { - $this->storeConfigManager = $storeConfigManager; - } - - /** - * Get store configs by store codes - * - * @param array $storeCodes - * @return array - */ - public function getStoreConfigsByStoreCodes(array $storeCodes = null) : array - { - $storeConfigs = $this->storeConfigManager->getStoreConfigs($storeCodes); - - return [ - 'items' => $this->hidrateStoreConfigs($storeConfigs) - ]; - } - - /** - * Transform StoreConfig objects to in array format - * - * @param StoreConfigInterface[] $storeConfigs - * @return array - */ - private function hidrateStoreConfigs(array $storeConfigs) : array - { - $storeConfigsData = []; - /** @var StoreConfigInterface $storeConfig */ - foreach ($storeConfigs as $storeConfig) { - $storeConfigsData[] = [ - 'id' => $storeConfig->getId(), - 'code' => $storeConfig->getCode(), - 'website_id' => $storeConfig->getWebsiteId(), - 'locale' => $storeConfig->getLocale(), - 'base_currency_code' => $storeConfig->getBaseCurrencyCode(), - 'default_display_currency_code' => $storeConfig->getDefaultDisplayCurrencyCode(), - 'timezone' => $storeConfig->getTimezone(), - 'weight_unit' => $storeConfig->getWeightUnit(), - 'base_url' => $storeConfig->getBaseUrl(), - 'base_link_url' => $storeConfig->getBaseLinkUrl(), - 'base_static_url' => $storeConfig->getSecureBaseStaticUrl(), - 'base_media_url' => $storeConfig->getBaseMediaUrl(), - 'secure_base_url' => $storeConfig->getSecureBaseUrl(), - 'secure_base_link_url' => $storeConfig->getSecureBaseLinkUrl(), - 'secure_base_static_url' => $storeConfig->getSecureBaseStaticUrl(), - 'secure_base_media_url' => $storeConfig->getSecureBaseMediaUrl() - ]; - } - - return $storeConfigsData; - } -} diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigResolver.php b/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigResolver.php new file mode 100644 index 0000000000000..8e5bf0120b8b8 --- /dev/null +++ b/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigResolver.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\StoreGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\StoreGraphQl\Model\Resolver\Store\StoreConfigDataProvider; + +/** + * StoreConfig page field resolver, used for GraphQL request processing. + */ +class StoreConfigResolver implements ResolverInterface +{ + /** + * @var StoreConfigDataProvider + */ + private $storeConfigDataProvider; + + /** + * @var ValueFactory + */ + private $valueFactory; + + /** + * @param StoreConfigDataProvider $storeConfigsDataProvider + * @param ValueFactory $valueFactory + */ + public function __construct( + StoreConfigDataProvider $storeConfigsDataProvider, + ValueFactory $valueFactory + ) { + $this->valueFactory = $valueFactory; + $this->storeConfigDataProvider = $storeConfigsDataProvider; + } + + /** + * {@inheritdoc} + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) : Value { + + $storeConfigData = $this->storeConfigDataProvider->getStoreConfig(); + + $result = function () use ($storeConfigData) { + return !empty($storeConfigData) ? $storeConfigData : []; + }; + + return $this->valueFactory->create($result); + } +} diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigsResolver.php b/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigsResolver.php deleted file mode 100644 index 609985b48e2be..0000000000000 --- a/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigsResolver.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\StoreGraphQl\Model\Resolver; - -use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\GraphQl\Query\Resolver\Value; -use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; -use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\StoreGraphQl\Model\Resolver\Store\StoreConfigsDataProvider; - -/** - * StoreConfig page field resolver, used for GraphQL request processing. - */ -class StoreConfigsResolver implements ResolverInterface -{ - /** - * @var StoreConfigsDataProvider - */ - private $storeConfigsDataProvider; - - /** - * @var ValueFactory - */ - private $valueFactory; - - /** - * @param StoreConfigsDataProvider $storeConfigsDataProvider - * @param ValueFactory $valueFactory - */ - public function __construct( - StoreConfigsDataProvider $storeConfigsDataProvider, - ValueFactory $valueFactory - ) { - $this->valueFactory = $valueFactory; - $this->storeConfigsDataProvider = $storeConfigsDataProvider; - } - - /** - * {@inheritdoc} - */ - public function resolve( - Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null - ) : Value { - - $storeCodes = $this->getStoreCodes($args); - $storeConfigsData = $this->storeConfigsDataProvider->getStoreConfigsByStoreCodes($storeCodes); - - $result = function () use ($storeConfigsData) { - return !empty($storeConfigsData) ? $storeConfigsData : []; - }; - - return $this->valueFactory->create($result); - } - - /** - * Retrieve store codes - * - * @param array $args - * @return array - * @throws GraphQlInputException - */ - private function getStoreCodes($args) - { - if (isset($args['storeCodes'])) { - if (is_array($args['storeCodes'])) { - return $args['storeCodes']; - } - throw new GraphQlInputException(__('"store codes should contain a valid array')); - } - - return null; - } -} diff --git a/app/code/Magento/StoreGraphQl/etc/schema.graphqls b/app/code/Magento/StoreGraphQl/etc/schema.graphqls index d0597de04b668..af79d0e3e28b7 100644 --- a/app/code/Magento/StoreGraphQl/etc/schema.graphqls +++ b/app/code/Magento/StoreGraphQl/etc/schema.graphqls @@ -1,10 +1,7 @@ # Copyright © Magento, Inc. All rights reserved. # See COPYING.txt for license details. type Query { - storeConfigs ( - storeCodes: [String] @doc(description: "Store Codes of the store configs") -): StoreConfigs - @resolver(class: "Magento\\StoreGraphQl\\Model\\Resolver\\StoreConfigsResolver") @doc(description: "The store configs query") + storeConfig : StoreConfig @resolver(class: "Magento\\StoreGraphQl\\Model\\Resolver\\StoreConfigResolver") @doc(description: "The store config query") } type Website @doc(description: "The type contains information about a website") { @@ -16,10 +13,6 @@ type Website @doc(description: "The type contains information about a website") is_default : Boolean @doc(description: "Specifies if this is the default website") } -type StoreConfigs @doc(description: "The Store Configs object") { - items: [StoreConfig] @doc(description: "An array containing store configs") -} - type StoreConfig @doc(description: "The type contains information about a store config") { id : Int @doc(description: "The ID number assigned to the store") code : String @doc(description: "A code assigned to the store to identify it") diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigsResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php similarity index 58% rename from dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigsResolverTest.php rename to dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php index 5c3566ffe2e19..df86519ae3d1e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigsResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php @@ -9,13 +9,15 @@ use Magento\Store\Api\Data\StoreConfigInterface; use Magento\Store\Api\StoreConfigManagerInterface; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Store\Api\StoreResolverInterface; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; /** * Test the GraphQL endpoint's StoreConfigs query */ -class StoreConfigsResolverTest extends GraphQlAbstract +class StoreConfigResolverTest extends GraphQlAbstract { /** @var ObjectManager */ @@ -29,17 +31,22 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Store/_files/store.php */ - public function testStoreConfigsFilteredByStoreCode() + public function testGetStoreConfig() { - $storeCode = 'test'; /** @var StoreConfigManagerInterface $storeConfigsManager */ $storeConfigsManager = $this->objectManager->get(StoreConfigManagerInterface::class); + /** @var StoreResolverInterface $storeResolver */ + $storeResolver = $this->objectManager->get(StoreResolverInterface::class); + /** @var StoreRepositoryInterface $storeRepository */ + $storeRepository = $this->objectManager->get(StoreRepositoryInterface::class); + $storeId = $storeResolver->getCurrentStoreId(); + $store = $storeRepository->getById($storeId); /** @var StoreConfigInterface $storeConfig */ - $storeConfig = current($storeConfigsManager->getStoreConfigs([$storeCode])); + $storeConfig = current($storeConfigsManager->getStoreConfigs([$store->getCode()])); $query = <<<QUERY { - storeConfigs(storeCodes: "{$storeCode}"){ + storeConfig{ items{ id, code, @@ -62,46 +69,27 @@ public function testStoreConfigsFilteredByStoreCode() } QUERY; $response = $this->graphQlQuery($query); - $this->assertArrayHasKey('storeConfigs', $response); - $this->assertEquals(1, count($response['storeConfigs']['items'])); - $responseStoreConfig = current($response['storeConfigs']['items']); - $this->assertEquals($storeConfig->getId(), $responseStoreConfig['id']); - $this->assertEquals($storeConfig->getCode(), $responseStoreConfig['code']); - $this->assertEquals($storeConfig->getLocale(), $responseStoreConfig['locale']); - $this->assertEquals($storeConfig->getBaseCurrencyCode(), $responseStoreConfig['base_currency_code']); + $this->assertArrayHasKey('storeConfig', $response); + $this->assertEquals($storeConfig->getId(), $response['storeConfig']['id']); + $this->assertEquals($storeConfig->getCode(), $response['storeConfig']['code']); + $this->assertEquals($storeConfig->getLocale(), $response['storeConfig']['locale']); + $this->assertEquals($storeConfig->getBaseCurrencyCode(), $response['storeConfig']['base_currency_code']); $this->assertEquals( $storeConfig->getDefaultDisplayCurrencyCode(), - $responseStoreConfig['default_display_currency_code'] + $response['storeConfig']['default_display_currency_code'] ); - $this->assertEquals($storeConfig->getTimezone(), $responseStoreConfig['timezone']); - $this->assertEquals($storeConfig->getWeightUnit(), $responseStoreConfig['weight_unit']); - $this->assertEquals($storeConfig->getBaseUrl(), $responseStoreConfig['base_url']); - $this->assertEquals($storeConfig->getBaseLinkUrl(), $responseStoreConfig['base_link_url']); - $this->assertEquals($storeConfig->getBaseStaticUrl(), $responseStoreConfig['base_static_url']); - $this->assertEquals($storeConfig->getBaseMediaUrl(), $responseStoreConfig['base_media_url']); - $this->assertEquals($storeConfig->getSecureBaseUrl(), $responseStoreConfig['secure_base_url']); - $this->assertEquals($storeConfig->getSecureBaseLinkUrl(), $responseStoreConfig['secure_base_link_url']); - $this->assertEquals($storeConfig->getSecureBaseStaticUrl(), $responseStoreConfig['secure_base_static_url']); - $this->assertEquals($storeConfig->getSecureBaseMediaUrl(), $responseStoreConfig['secure_base_media_url']); - } - - /** - * @magentoApiDataFixture Magento/Store/_files/store.php - */ - public function testGetStoreConfigsWithoutStoreCodes() - { - $query - = <<<QUERY -{ - storeConfigs{ - items{ - id - } - } -} -QUERY; - $response = $this->graphQlQuery($query); - $this->assertArrayHasKey('storeConfigs', $response); - $this->assertEquals(2, count($response['storeConfigs']['items'])); + $this->assertEquals($storeConfig->getTimezone(), $response['storeConfig']['timezone']); + $this->assertEquals($storeConfig->getWeightUnit(), $response['storeConfig']['weight_unit']); + $this->assertEquals($storeConfig->getBaseUrl(), $response['storeConfig']['base_url']); + $this->assertEquals($storeConfig->getBaseLinkUrl(), $response['storeConfig']['base_link_url']); + $this->assertEquals($storeConfig->getBaseStaticUrl(), $response['storeConfig']['base_static_url']); + $this->assertEquals($storeConfig->getBaseMediaUrl(), $response['storeConfig']['base_media_url']); + $this->assertEquals($storeConfig->getSecureBaseUrl(), $response['storeConfig']['secure_base_url']); + $this->assertEquals($storeConfig->getSecureBaseLinkUrl(), $response['storeConfig']['secure_base_link_url']); + $this->assertEquals( + $storeConfig->getSecureBaseStaticUrl(), + $response['storeConfig']['secure_base_static_url'] + ); + $this->assertEquals($storeConfig->getSecureBaseMediaUrl(), $response['storeConfig']['secure_base_media_url']); } } From 9fb44833aaadecddf2ecac752b9556b5dab3457c Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Thu, 19 Jul 2018 15:18:12 -0500 Subject: [PATCH 0398/1171] MC-3315: Banner/Slider: Image still display on Banner/Slider slide-out after deleted - Initial fix --- .../web/js/form/element/image-uploader.js | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js index 037690bf6be50..dac325b8aa8b4 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js @@ -17,6 +17,15 @@ define([ 'use strict'; return Element.extend({ + /** + * {@inheritDoc} + */ + initialize: function () { + this._super(); + + // Listen for file deletions from the media browser + $(window).on('fileDeleted.mediabrowser', this.onDeleteFile.bind(this)); + }, /** * Assign uid for media gallery @@ -78,6 +87,40 @@ define([ browser.openDialog(openDialogUrl, null, null, this.mediaGallery.openDialogTitle); }, + /** + * @param {jQuery.event} e + * @param {Object} data + * @returns {Object} Chainables + */ + onDeleteFile: function (e, data) { + var fileId = this.getFileId(), + deletedFileIds = data.ids; + + if (fileId && $.inArray(fileId, deletedFileIds) > -1) { + this.clear(); + } + + return this; + }, + + /** + * {@inheritDoc} + */ + clear: function () { + this.value([]); + + return this; + }, + + /** + * Gets the ID of the file used if set + * + * @return {String|Null} ID + */ + getFileId: function () { + return this.hasData() ? this.value()[0].id : null; + }, + /** * Trigger native browser file upload UI via clicking on 'Upload' button * From 1758af82149668d85dd7d04a76b4000e3b280229 Mon Sep 17 00:00:00 2001 From: Devagouda Patil <depatil@ip-192-168-0-9.ec2.internal> Date: Thu, 19 Jul 2018 22:55:13 -0500 Subject: [PATCH 0399/1171] MAGETWO-90970: State field becomes required after page refresh during checkout -Added functional test to cover the bug fix --- .../Test/Mftf/Page/CheckoutShippingPage.xml | 15 +++++ ...UKCustomerRemainOptionAfterRefreshTest.xml | 60 +++++++++++++++++++ .../Customer/Test/Mftf/Data/AddressData.xml | 6 ++ 3 files changed, 81 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Page/CheckoutShippingPage.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutShippingPage.xml b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutShippingPage.xml new file mode 100644 index 0000000000000..59c3b20aca674 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutShippingPage.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="CheckoutShippingPage" url="/checkout/#shipping" module="Checkout" area="storefront"> + <section name="CheckoutShippingGuestInfoSection"/> + <section name="CheckoutShippingSection"/> + </page> +</pages> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml new file mode 100644 index 0000000000000..a52ed9355ec97 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest"> + <annotations> + <features value="Checkout"/> + <stories value="MAGETWO-90970"/> + <title value="Guest Checkout"/> + <description value="Address State Field For UK Customers Remain Option even After Browser Refresh"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-93329"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + </after> + + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" /> + <selectOption stepKey="selectCounty" selector="{{CheckoutShippingSection.country}}" userInput="{{UK_Address.country_id}}"/> + <waitForPageLoad stepKey="waitFormToReload"/> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitFormToReload1"/> + <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{CustomerEntityOne.email}}" stepKey="enterEmail"/> + <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{CustomerEntityOne.firstname}}" stepKey="enterFirstName"/> + <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{CustomerEntityOne.lastname}}" stepKey="enterLastName"/> + <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{UK_Address.street[0]}}" stepKey="enterStreet"/> + <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{UK_Address.city}}" stepKey="enterCity"/> + <waitForPageLoad stepKey="waitFormToReload2"/> + <see userInput="State/Province" stepKey="StateFieldStillExists"/> + <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{UK_Address.telephone}}" stepKey="enterTelephone"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> + <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index a1f0277ec40ec..d26ecac28113e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -85,6 +85,12 @@ </entity> <!--If required other field can be added to UK_Address entity, dont modify any existing data--> <entity name="UK_Address" type="address"> + <array key="street"> + <item>7700 xyz street</item> + <item>113</item> + </array> + <data key="city">London</data> <data key="country_id">GB</data> + <data key="telephone">512-345-6789</data> </entity> </entities> From 8a854000ea435c5688474d9e9b7a86cce0464cd3 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Fri, 20 Jul 2018 08:29:22 +0300 Subject: [PATCH 0400/1171] magento/magento2:#16823: Fixed condition with usage isPost method Declare strict types=1 --- lib/internal/Magento/Framework/App/HttpRequestInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/Magento/Framework/App/HttpRequestInterface.php b/lib/internal/Magento/Framework/App/HttpRequestInterface.php index 685db5f47ffc7..674ccdb07f49f 100644 --- a/lib/internal/Magento/Framework/App/HttpRequestInterface.php +++ b/lib/internal/Magento/Framework/App/HttpRequestInterface.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\App; From f054573ea61878c7245d11fdd3787b81c0a5dff2 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 20 Jul 2018 10:31:18 +0300 Subject: [PATCH 0401/1171] MAGETWO-93322: Cover with API-functional test View and modify company structure via WebAPI --- .../_files/customer_with_website_rollback.php | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_website_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_website_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_website_rollback.php new file mode 100644 index 0000000000000..2dbd1381b6f3c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_website_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** @var \Magento\Framework\ObjectManagerInterface $objectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Store\Model\StoreManager $store */ +$store = $objectManager->get(\Magento\Store\Model\StoreManager::class); + +/** @var $customer \Magento\Customer\Model\Customer*/ +$customer = $objectManager->create(\Magento\Customer\Model\Customer::class); +$customer->setWebsiteId($store->getDefaultStoreView()->getWebsiteId()); +$customer->loadByEmail('john.doe@magento.com'); +if ($customer->getId()) { + $customer->delete(); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From f3502dafbeb3b525ab12ad6c0045efac8274951b Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Fri, 6 Jul 2018 16:48:14 +0530 Subject: [PATCH 0402/1171] Fixed trim issue on email field --- .../Customer/view/frontend/templates/form/confirmation.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/frontend/templates/form/confirmation.phtml b/app/code/Magento/Customer/view/frontend/templates/form/confirmation.phtml index 0de3d94334970..8edb21ac8e1e9 100644 --- a/app/code/Magento/Customer/view/frontend/templates/form/confirmation.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/form/confirmation.phtml @@ -14,7 +14,7 @@ <div class="field email required"> <label for="email_address" class="label"><span><?= $block->escapeHtml(__('Email')) ?></span></label> <div class="control"> - <input type="email" name="email" id="email_address" class="input-text" value="<?= $block->escapeHtmlAttr($block->getEmail()) ?>" data-validate="{required:true, 'validate-email':true}"> + <input type="email" name="email" id="email_address" class="input-text" value="<?= $block->escapeHtmlAttr($block->getEmail()) ?>" data-validate="{required:true, 'validate-email':true}" data-mage-init='{"mage/trim-input":{}}'> </div> </div> </fieldset> From 9d15eccb97937cee8fdca373010233b7397be302 Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Fri, 6 Jul 2018 17:06:35 +0530 Subject: [PATCH 0403/1171] Revert back --- .../Customer/view/frontend/templates/form/confirmation.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/frontend/templates/form/confirmation.phtml b/app/code/Magento/Customer/view/frontend/templates/form/confirmation.phtml index 8edb21ac8e1e9..0de3d94334970 100644 --- a/app/code/Magento/Customer/view/frontend/templates/form/confirmation.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/form/confirmation.phtml @@ -14,7 +14,7 @@ <div class="field email required"> <label for="email_address" class="label"><span><?= $block->escapeHtml(__('Email')) ?></span></label> <div class="control"> - <input type="email" name="email" id="email_address" class="input-text" value="<?= $block->escapeHtmlAttr($block->getEmail()) ?>" data-validate="{required:true, 'validate-email':true}" data-mage-init='{"mage/trim-input":{}}'> + <input type="email" name="email" id="email_address" class="input-text" value="<?= $block->escapeHtmlAttr($block->getEmail()) ?>" data-validate="{required:true, 'validate-email':true}"> </div> </div> </fieldset> From 6897f275e9e8176aeb2c11fb7f3f695bbc94d6e5 Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Fri, 6 Jul 2018 17:08:21 +0530 Subject: [PATCH 0404/1171] Fixed trim issue on email field --- .../Customer/view/frontend/templates/form/confirmation.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/frontend/templates/form/confirmation.phtml b/app/code/Magento/Customer/view/frontend/templates/form/confirmation.phtml index 0de3d94334970..8edb21ac8e1e9 100644 --- a/app/code/Magento/Customer/view/frontend/templates/form/confirmation.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/form/confirmation.phtml @@ -14,7 +14,7 @@ <div class="field email required"> <label for="email_address" class="label"><span><?= $block->escapeHtml(__('Email')) ?></span></label> <div class="control"> - <input type="email" name="email" id="email_address" class="input-text" value="<?= $block->escapeHtmlAttr($block->getEmail()) ?>" data-validate="{required:true, 'validate-email':true}"> + <input type="email" name="email" id="email_address" class="input-text" value="<?= $block->escapeHtmlAttr($block->getEmail()) ?>" data-validate="{required:true, 'validate-email':true}" data-mage-init='{"mage/trim-input":{}}'> </div> </div> </fieldset> From b8b6f3814f9d5d497384ee4c07cb93ce9b1db31f Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Fri, 20 Jul 2018 10:34:29 +0200 Subject: [PATCH 0405/1171] Fixed return value for getGraphQlClient --- .../Magento/TestFramework/TestCase/GraphQlAbstract.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php index af01e455fdd9d..ba64a3e9eee8f 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php @@ -93,8 +93,8 @@ private function getGraphQlClient() { if ($this->graphQlClient === null) { return Bootstrap::getObjectManager()->get(\Magento\TestFramework\TestCase\GraphQl\Client::class); - } else { - $this->graphQlClient; } + + return $this->graphQlClient; } } From 67ac9ddce8bdded2663cb307ba55b343c96374d8 Mon Sep 17 00:00:00 2001 From: Adam Moss <adammoss7@gmail.com> Date: Tue, 17 Jul 2018 08:40:32 +0100 Subject: [PATCH 0406/1171] Improve reporting of invalid country code message --- .../Magento/TaxImportExport/Model/Rate/CsvImportHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/TaxImportExport/Model/Rate/CsvImportHandler.php b/app/code/Magento/TaxImportExport/Model/Rate/CsvImportHandler.php index 8ec871a182ab4..c8cba30c9cd25 100644 --- a/app/code/Magento/TaxImportExport/Model/Rate/CsvImportHandler.php +++ b/app/code/Magento/TaxImportExport/Model/Rate/CsvImportHandler.php @@ -237,7 +237,7 @@ protected function _importRate(array $rateData, array $regionsCache, array $stor $countryCode = $rateData[1]; $country = $this->_countryFactory->create()->loadByCode($countryCode, 'iso2_code'); if (!$country->getId()) { - throw new \Magento\Framework\Exception\LocalizedException(__('One of the countries has invalid code.')); + throw new \Magento\Framework\Exception\LocalizedException(__('Country code is invalid: %1', $countryCode)); } $regionsCache = $this->_addCountryRegionsToCache($countryCode, $regionsCache); From 994f0ec3ab81103c82750b6fbe2c360645fc96ee Mon Sep 17 00:00:00 2001 From: Adam Moss <adammoss7@gmail.com> Date: Tue, 17 Jul 2018 09:00:24 +0100 Subject: [PATCH 0407/1171] Replace invalid country code message with new message --- app/code/Magento/TaxImportExport/i18n/en_US.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/TaxImportExport/i18n/en_US.csv b/app/code/Magento/TaxImportExport/i18n/en_US.csv index cadecc39a391c..40db8846b54cb 100644 --- a/app/code/Magento/TaxImportExport/i18n/en_US.csv +++ b/app/code/Magento/TaxImportExport/i18n/en_US.csv @@ -12,7 +12,7 @@ Rate,Rate "Invalid file upload attempt","Invalid file upload attempt" "Invalid file upload attempt.","Invalid file upload attempt." "Invalid file format.","Invalid file format." -"One of the countries has invalid code.","One of the countries has invalid code." +"Country code is invalid: %1","Country code is invalid: %1" "Import Tax Rates","Import Tax Rates" "Export Tax Rates","Export Tax Rates" CSV,CSV From 5b11dc8202c31a20871de7fd631334738bc12402 Mon Sep 17 00:00:00 2001 From: Adam Moss <adammoss7@gmail.com> Date: Tue, 17 Jul 2018 09:02:38 +0100 Subject: [PATCH 0408/1171] Update invalid country code test with new message --- .../Magento/TaxImportExport/Model/Rate/CsvImportHandlerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/TaxImportExport/Model/Rate/CsvImportHandlerTest.php b/dev/tests/integration/testsuite/Magento/TaxImportExport/Model/Rate/CsvImportHandlerTest.php index ed7613f3dd106..6c2c291e6c295 100644 --- a/dev/tests/integration/testsuite/Magento/TaxImportExport/Model/Rate/CsvImportHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/TaxImportExport/Model/Rate/CsvImportHandlerTest.php @@ -57,7 +57,7 @@ public function testImportFromCsvFileWithCorrectData() /** * @magentoDbIsolation enabled * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage One of the countries has invalid code. + * @expectedExceptionMessage Country code is invalid: ZZ */ public function testImportFromCsvFileThrowsExceptionWhenCountryCodeIsInvalid() { From 2f133c852f4fae8bdf193b3a7e7b7ce94eb0391f Mon Sep 17 00:00:00 2001 From: Vladymyr Hrivinskyi <volodymyr@hryvinskyi.com> Date: Mon, 16 Jul 2018 17:55:55 +0300 Subject: [PATCH 0409/1171] Add Confirm Modal Width --- lib/web/css/source/components/_modals.less | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/web/css/source/components/_modals.less b/lib/web/css/source/components/_modals.less index 80b1c85ceb46e..51ba451681df5 100644 --- a/lib/web/css/source/components/_modals.less +++ b/lib/web/css/source/components/_modals.less @@ -100,6 +100,12 @@ left: 0; overflow-y: auto; + &.confirm { + .modal-inner-wrap { + .lib-css(width, @modal-popup-confirm__width); + } + } + &._show { .modal-inner-wrap { -webkit-transform: translateY(0); From 07d6347281bab9885a80591a23bbcf12195afe50 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 20 Jul 2018 17:22:15 +0300 Subject: [PATCH 0410/1171] MAGETWO-93270: Product Video feature not GDPR compliant --- .../adminhtml/web/js/get-video-information.js | 22 +++++++++++++++---- .../view/adminhtml/web/js/new-video-dialog.js | 20 +++++++++++------ .../web/js/fotorama-add-video-events.js | 14 +++++++++--- .../view/frontend/web/js/load-player.js | 8 +++++++ lib/web/fotorama/fotorama.js | 2 +- 5 files changed, 51 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/ProductVideo/view/adminhtml/web/js/get-video-information.js b/app/code/Magento/ProductVideo/view/adminhtml/web/js/get-video-information.js index 13b0e43a84d81..653434f1008ca 100644 --- a/app/code/Magento/ProductVideo/view/adminhtml/web/js/get-video-information.js +++ b/app/code/Magento/ProductVideo/view/adminhtml/web/js/get-video-information.js @@ -86,6 +86,7 @@ define([ this._height = this.element.data('height'); this._autoplay = !!this.element.data('autoplay'); this._playing = this._autoplay || false; + this.useYoutubeNocookie = this.element.data('youtubenocookie') || false; this._responsive = this.element.data('responsive') !== false; @@ -163,6 +164,12 @@ define([ * @private */ 'youtubeapiready': function () { + var host = 'https://www.youtube.com'; + + if (self.useYoutubeNocookie) { + host = 'https://www.youtube-nocookie.com'; + } + if (self._player !== undefined) { return; } @@ -177,6 +184,7 @@ define([ width: self._width, videoId: self._code, playerVars: self._params, + host: host, events: { /** @@ -469,7 +477,8 @@ define([ description: tmp.snippet.description, thumbnail: tmp.snippet.thumbnails.high.url, videoId: videoInfo.id, - videoProvider: videoInfo.type + videoProvider: videoInfo.type, + useYoutubeNocookie: videoInfo.useYoutubeNocookie }; this._videoInformation = respData; this.element.trigger(this._UPDATE_VIDEO_INFORMATION_TRIGGER, respData); @@ -600,7 +609,8 @@ define([ var id, type, ampersandPosition, - vimeoRegex; + vimeoRegex, + useYoutubeNocookie = false; if (typeof href !== 'string') { return href; @@ -620,9 +630,13 @@ define([ id = id.substring(0, ampersandPosition); } - } else if (href.host.match(/youtube\.com|youtu\.be/)) { + } else if (href.host.match(/youtube\.com|youtu\.be|youtube-nocookie.com/)) { id = href.pathname.replace(/^\/(embed\/|v\/)?/, '').replace(/\/.*/, ''); type = 'youtube'; + + if (href.host.match(/youtube-nocookie.com/)) { + useYoutubeNocookie = true; + } } else if (href.host.match(/vimeo\.com/)) { type = 'vimeo'; vimeoRegex = new RegExp(['https?:\\/\\/(?:www\\.|player\\.)?vimeo.com\\/(?:channels\\/(?:\\w+\\/)', @@ -640,7 +654,7 @@ define([ } return id ? { - id: id, type: type, s: href.search.replace(/^\?/, '') + id: id, type: type, s: href.search.replace(/^\?/, ''), useYoutubeNocookie: useYoutubeNocookie } : false; } }); diff --git a/app/code/Magento/ProductVideo/view/adminhtml/web/js/new-video-dialog.js b/app/code/Magento/ProductVideo/view/adminhtml/web/js/new-video-dialog.js index 1ab10c95a51bc..287c88e8a796f 100644 --- a/app/code/Magento/ProductVideo/view/adminhtml/web/js/new-video-dialog.js +++ b/app/code/Magento/ProductVideo/view/adminhtml/web/js/new-video-dialog.js @@ -21,6 +21,7 @@ define([ container: '.video-player-container', videoClass: 'product-video', reset: false, + useYoutubeNocookie: false, metaData: { DOM: { title: '.video-information.title span', @@ -87,13 +88,17 @@ define([ */ _doUpdate: function () { this.reset(); - this.element.find(this.options.container).append('<div class="' + - this.options.videoClass + - '" data-type="' + - this.options.videoProvider + - '" data-code="' + - this.options.videoId + - '" data-width="100%" data-height="100%"></div>'); + this.element.find(this.options.container).append( + '<div class="' + + this.options.videoClass + + '" data-type="' + + this.options.videoProvider + + '" data-code="' + + this.options.videoId + + '" data-youtubenocookie="' + + this.options.useYoutubeNocookie + + '" data-width="100%" data-height="100%"></div>' + ); this.element.find(this.options.metaData.DOM.wrapper).show(); this.element.find(this.options.metaData.DOM.title).text(this.options.metaData.data.title); this.element.find(this.options.metaData.DOM.uploaded).text(this.options.metaData.data.uploaded); @@ -337,6 +342,7 @@ define([ .createVideoPlayer({ videoId: data.videoId, videoProvider: data.videoProvider, + useYoutubeNocookie: data.useYoutubeNocookie, reset: false, metaData: { DOM: { diff --git a/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js b/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js index 9bb4b9996e3ad..82a7f2d1a8e19 100644 --- a/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js +++ b/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js @@ -34,7 +34,8 @@ define([ var id, type, ampersandPosition, - vimeoRegex; + vimeoRegex, + useYoutubeNocookie = false; /** * Get youtube ID @@ -68,9 +69,13 @@ define([ id = _getYoutubeId(id); type = 'youtube'; } - } else if (href.host.match(/youtube\.com|youtu\.be/)) { + } else if (href.host.match(/youtube\.com|youtu\.be|youtube-nocookie.com/)) { id = href.pathname.replace(/^\/(embed\/|v\/)?/, '').replace(/\/.*/, ''); type = 'youtube'; + + if (href.host.match(/youtube-nocookie.com/)) { + useYoutubeNocookie = true; + } } else if (href.host.match(/vimeo\.com/)) { type = 'vimeo'; vimeoRegex = new RegExp(['https?:\\/\\/(?:www\\.|player\\.)?vimeo.com\\/(?:channels\\/(?:\\w+\\/)', @@ -85,7 +90,7 @@ define([ } return id ? { - id: id, type: type, s: href.search.replace(/^\?/, '') + id: id, type: type, s: href.search.replace(/^\?/, ''), useYoutubeNocookie: useYoutubeNocookie } : false; } @@ -281,6 +286,7 @@ define([ tmpVideoData.id = dataUrl.id; tmpVideoData.provider = dataUrl.type; tmpVideoData.videoUrl = tmpInputData.videoUrl; + tmpVideoData.useYoutubeNocookie = dataUrl.useYoutubeNocookie; } videoData.push(tmpVideoData); @@ -629,6 +635,8 @@ define([ videoData.provider + '" data-code="' + videoData.id + + '" data-youtubenocookie="' + + videoData.useYoutubeNocookie + '" data-width="100%" data-height="100%"></div>' ); }, diff --git a/app/code/Magento/ProductVideo/view/frontend/web/js/load-player.js b/app/code/Magento/ProductVideo/view/frontend/web/js/load-player.js index 3519d538e523a..75a2c1d75da15 100644 --- a/app/code/Magento/ProductVideo/view/frontend/web/js/load-player.js +++ b/app/code/Magento/ProductVideo/view/frontend/web/js/load-player.js @@ -88,6 +88,7 @@ define(['jquery', 'jquery/ui'], function ($) { this._playing = this._autoplay || false; this._loop = this.element.data('loop'); this._rel = this.element.data('related'); + this.useYoutubeNocookie = this.element.data('youtubenocookie') || false; this._responsive = this.element.data('responsive') !== false; @@ -164,6 +165,12 @@ define(['jquery', 'jquery/ui'], function ($) { * Handle event */ 'youtubeapiready': function () { + var host = 'https://www.youtube.com'; + + if (self.useYoutubeNocookie) { + host = 'https://www.youtube-nocookie.com'; + } + if (self._player !== undefined) { return; } @@ -182,6 +189,7 @@ define(['jquery', 'jquery/ui'], function ($) { width: self._width, videoId: self._code, playerVars: self._params, + host: host, events: { /** diff --git a/lib/web/fotorama/fotorama.js b/lib/web/fotorama/fotorama.js index c86cd40198b2b..89cb315b0d2c4 100644 --- a/lib/web/fotorama/fotorama.js +++ b/lib/web/fotorama/fotorama.js @@ -831,7 +831,7 @@ fotoramaVersion = '4.6.4'; } type = 'youtube'; } - } else if (href.host.match(/youtube\.com|youtu\.be/)) { + } else if (href.host.match(/youtube\.com|youtu\.be|youtube-nocookie.com/)) { id = href.pathname.replace(/^\/(embed\/|v\/)?/, '').replace(/\/.*/, ''); type = 'youtube'; } else if (href.host.match(/vimeo\.com/)) { From 16c651f2a8d2b8544245570cfcd55ca49ce36966 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Fri, 20 Jul 2018 09:39:35 -0500 Subject: [PATCH 0411/1171] MC-3315: Banner/Slider: Image still display on Banner/Slider slide-out after deleted - Fixed issue --- app/code/Magento/Ui/view/base/web/js/form/element/abstract.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js index 5177b4a378d69..1de0077b6ced3 100755 --- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js @@ -407,7 +407,7 @@ define([ this.bubble('error', message); //TODO: Implement proper result propagation for form - if (!isValid) { + if (this.source && !isValid) { this.source.set('params.invalid', true); } From f07c8c490e93f60f7beb2bfca48c3b5864350c32 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 20 Jul 2018 13:42:05 -0500 Subject: [PATCH 0412/1171] MAGETWO-8709: [GITHUB] Child product image should be shown in Wishist if options are selected for configurable product #8168 - make property private --- .../Model/Product/Configuration/Item/ItemProductResolver.php | 2 +- .../Model/Product/Configuration/Item/ItemProductResolver.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php index 970e37422e2eb..6c33ecc138aea 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -27,7 +27,7 @@ class ItemProductResolver implements ItemResolverInterface /** * @var ScopeConfigInterface */ - protected $scopeConfig; + private $scopeConfig; /** * @param ScopeConfigInterface $scopeConfig diff --git a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php index 87053664d9bac..207d9853f8c6b 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -27,7 +27,7 @@ class ItemProductResolver implements ItemResolverInterface /** * @var ScopeConfigInterface */ - protected $scopeConfig; + private $scopeConfig; /** * @param ScopeConfigInterface $scopeConfig From 332a7f1a05c4e093932f9f1d63a750ae97b1558a Mon Sep 17 00:00:00 2001 From: Cari Spruiell <cspruiell@magento.com> Date: Fri, 20 Jul 2018 15:25:41 -0500 Subject: [PATCH 0413/1171] MC-3148: Create & Update MFTF Tests - update tests and selectors --- .../Magento/Theme/Test/Mftf/Data/LayoutData.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 app/code/Magento/Theme/Test/Mftf/Data/LayoutData.xml diff --git a/app/code/Magento/Theme/Test/Mftf/Data/LayoutData.xml b/app/code/Magento/Theme/Test/Mftf/Data/LayoutData.xml new file mode 100644 index 0000000000000..985b0dd66f5c8 --- /dev/null +++ b/app/code/Magento/Theme/Test/Mftf/Data/LayoutData.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="Layout" type="pagebuilder_layout"> + <data key="1column">1 column</data> + </entity> +</entities> From c0d4bc7d96d3fc96d7136b3d64fba95fe787fcb3 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Fri, 20 Jul 2018 16:42:40 -0500 Subject: [PATCH 0414/1171] MC-3053: Automate MFTF for MC-2294 --- app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml index fef9e2d851652..4eaf53e6a8139 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml @@ -36,7 +36,7 @@ <element name="Browse" type="button" selector=".mce-i-browse"/> <element name="BrowseUploadImage" type="file" selector=".fileupload" /> <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> - <element name="imageOrImageCopy" type="text" selector="//img[contains(@alt, '{{arg1}}.{{arg2}}')]|//img[contains(@alt,'{{arg1}}_') and contains(@alt,'.{{arg2}}')]" parameterized="true"/> + <element name="imageOrImageCopy" type="text" selector="//div[contains(@class,'media-gallery-modal')]//img[contains(@alt, '{{arg1}}.{{arg2}}')]|//img[contains(@alt,'{{arg1}}_') and contains(@alt,'.{{arg2}}')]" parameterized="true"/> <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> From be8d0e30204b49939f9045c14647581104025f97 Mon Sep 17 00:00:00 2001 From: Cari Spruiell <cspruiell@magento.com> Date: Fri, 20 Jul 2018 16:59:07 -0500 Subject: [PATCH 0415/1171] MC-3148: Create & Update MFTF Tests - rename file and update type --- .../Theme/Test/Mftf/Data/{LayoutData.xml => DesignData.xml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename app/code/Magento/Theme/Test/Mftf/Data/{LayoutData.xml => DesignData.xml} (90%) diff --git a/app/code/Magento/Theme/Test/Mftf/Data/LayoutData.xml b/app/code/Magento/Theme/Test/Mftf/Data/DesignData.xml similarity index 90% rename from app/code/Magento/Theme/Test/Mftf/Data/LayoutData.xml rename to app/code/Magento/Theme/Test/Mftf/Data/DesignData.xml index 985b0dd66f5c8..988d71bc4086b 100644 --- a/app/code/Magento/Theme/Test/Mftf/Data/LayoutData.xml +++ b/app/code/Magento/Theme/Test/Mftf/Data/DesignData.xml @@ -8,7 +8,7 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="Layout" type="pagebuilder_layout"> + <entity name="Layout" type="page_layout"> <data key="1column">1 column</data> </entity> </entities> From 5079f094be1a18a629c357f9ac7f492ab6b4e8a7 Mon Sep 17 00:00:00 2001 From: Rodrigo Biassi <rodrigobiassi.net@gmail.com> Date: Fri, 20 Jul 2018 19:41:35 -0300 Subject: [PATCH 0416/1171] Broken Responsive Layout on Top --- .../luma/Magento_Theme/web/css/source/_module.less | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less index 83157281561c2..94ce5c50f1b4d 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less @@ -426,6 +426,17 @@ } } +// +// Mobile +// _____________________________________________ + +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { + .cms-page-view .page-main { + padding-top: 41px; + position: relative; + } +} + // // Desktop // _____________________________________________ From 66baddd6f423b06da470b3a61037023bbe9ebe18 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Sat, 21 Jul 2018 07:58:36 +0300 Subject: [PATCH 0417/1171] Reverted changes to stub for unit test --- app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml b/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml index 81d614a81e881..c4001f47ced0b 100644 --- a/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml +++ b/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml @@ -76,6 +76,7 @@ <config_path>section/group/field4</config_path> </field> <field id="field_5" translate="label" showInWebsite="1" type="text"> + <label></label> </field> </group> <group id="group_4" type="text" showInDefault="1" showInWebsite="1" showInStore="1"> From 79a568ff447b3d25a85ff4b15f53d6a80ec6db66 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Sat, 21 Jul 2018 09:37:01 +0300 Subject: [PATCH 0418/1171] MAGETWO-93669: [Forwardport] Implement parallelization for Category Product Indexer --- .../Indexer/Category/Product/Action/Full.php | 45 ++++- .../Magento/Indexer/Model/ProcessManager.php | 154 ++++++++++++++++++ setup/src/Magento/Setup/Model/Installer.php | 14 ++ 3 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Indexer/Model/ProcessManager.php 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 09dbed350c5e4..f8121b55dbf99 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 @@ -8,6 +8,7 @@ use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; use Magento\Framework\DB\Query\Generator as QueryGenerator; use Magento\Framework\App\ResourceConnection; +use Magento\Indexer\Model\ProcessManager; /** * Class Full reindex action @@ -44,6 +45,11 @@ class Full extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio */ private $activeTableSwitcher; + /** + * @var ProcessManager + */ + private $processManager; + /** * @param ResourceConnection $resource * @param \Magento\Store\Model\StoreManagerInterface $storeManager @@ -52,9 +58,10 @@ class Full extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio * @param \Magento\Framework\Indexer\BatchSizeManagementInterface|null $batchSizeManagement * @param \Magento\Framework\Indexer\BatchProviderInterface|null $batchProvider * @param \Magento\Framework\EntityManager\MetadataPool|null $metadataPool - * @param \Magento\Indexer\Model\Indexer\StateFactory|null $stateFactory * @param int|null $batchRowsCount * @param ActiveTableSwitcher|null $activeTableSwitcher + * @param ProcessManager $processManager + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\App\ResourceConnection $resource, @@ -65,7 +72,8 @@ public function __construct( \Magento\Framework\Indexer\BatchProviderInterface $batchProvider = null, \Magento\Framework\EntityManager\MetadataPool $metadataPool = null, $batchRowsCount = null, - ActiveTableSwitcher $activeTableSwitcher = null + ActiveTableSwitcher $activeTableSwitcher = null, + ProcessManager $processManager = null ) { parent::__construct( $resource, @@ -85,6 +93,7 @@ public function __construct( ); $this->batchRowsCount = $batchRowsCount; $this->activeTableSwitcher = $activeTableSwitcher ?: $objectManager->get(ActiveTableSwitcher::class); + $this->processManager = $processManager ?: $objectManager->get(ProcessManager::class); } /** @@ -133,6 +142,38 @@ public function execute() return $this; } + /** + * Run reindexation + * + * @return void + */ + protected function reindex() + { + $userFunctions = []; + + foreach ($this->storeManager->getStores() as $store) { + if ($this->getPathFromCategoryId($store->getRootCategoryId())) { + $userFunctions[$store->getId()] = function () use ($store) { + return $this->reindexStore($store); + }; + } + } + + $this->processManager->execute($userFunctions); + } + + /** + * Execute indexation by store + * + * @param \Magento\Store\Model\Store $store + */ + private function reindexStore($store) + { + $this->reindexRootCategory($store); + $this->reindexAnchorCategories($store); + $this->reindexNonAnchorCategories($store); + } + /** * Publish data from tmp to replica table * diff --git a/app/code/Magento/Indexer/Model/ProcessManager.php b/app/code/Magento/Indexer/Model/ProcessManager.php new file mode 100644 index 0000000000000..4931f4f82b1bb --- /dev/null +++ b/app/code/Magento/Indexer/Model/ProcessManager.php @@ -0,0 +1,154 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Indexer\Model; + +/** + * Provide functionality for executing user functions in multi-thread mode. + */ +class ProcessManager +{ + /** + * Threads count environment variable name + */ + const THREADS_COUNT = 'MAGE_INDEXER_THREADS_COUNT'; + + /** @var bool */ + private $failInChildProcess = false; + + /** @var \Magento\Framework\App\ResourceConnection */ + private $resource; + + /** @var \Magento\Framework\Registry */ + private $registry; + + /** @var int|null */ + private $threadsCount; + + /** + * @param \Magento\Framework\App\ResourceConnection $resource + * @param \Magento\Framework\Registry $registry + * @param int|null $threadsCount + */ + public function __construct( + \Magento\Framework\App\ResourceConnection $resource, + \Magento\Framework\Registry $registry = null, + int $threadsCount = null + ) { + $this->resource = $resource; + if (null === $registry) { + $registry = \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\Registry::class + ); + } + $this->registry = $registry; + $this->threadsCount = (int)$threadsCount; + } + + /** + * Execute user functions + * + * @param \Traversable $userFunctions + */ + public function execute($userFunctions) + { + if ($this->threadsCount > 1 && $this->isCanBeParalleled() && !$this->isSetupMode() && PHP_SAPI == 'cli') { + $this->multiThreadsExecute($userFunctions); + } else { + $this->simpleThreadExecute($userFunctions); + } + } + + /** + * Execute user functions in singleThreads mode + * + * @param \Traversable $userFunctions + */ + private function simpleThreadExecute($userFunctions) + { + foreach ($userFunctions as $userFunction) { + call_user_func($userFunction); + } + } + + /** + * Execute user functions in multiThreads mode + * + * @param \Traversable $userFunctions + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + private function multiThreadsExecute($userFunctions) + { + $this->resource->closeConnection(null); + $threadNumber = 0; + foreach ($userFunctions as $userFunction) { + $pid = pcntl_fork(); + if ($pid == -1) { + throw new \RuntimeException('Unable to fork a new process'); + } elseif ($pid) { + $this->executeParentProcess($threadNumber); + } else { + $this->startChildProcess($userFunction); + } + } + while (pcntl_waitpid(0, $status) != -1) { + //Waiting for the completion of child processes + } + + if ($this->failInChildProcess) { + throw new \RuntimeException('Fail in child process'); + } + } + + /** + * Is process can be paralleled + * + * @return bool + */ + private function isCanBeParalleled() + { + return function_exists('pcntl_fork'); + } + + /** + * Is setup mode + * + * @return bool + */ + private function isSetupMode() + { + return $this->registry->registry('setup-mode-enabled') ?: false; + } + + /** + * Start child process + * + * @param callable $userFunction + * @SuppressWarnings(PHPMD.ExitExpression) + */ + private function startChildProcess($userFunction) + { + $status = call_user_func($userFunction); + $status = is_integer($status) ? $status : 0; + exit($status); + } + + /** + * Execute parent process + * + * @param int $threadNumber + */ + private function executeParentProcess(&$threadNumber) + { + $threadNumber++; + if ($threadNumber >= $this->threadsCount) { + pcntl_wait($status); + if (pcntl_wexitstatus($status) !== 0) { + $this->failInChildProcess = true; + } + $threadNumber--; + } + } +} diff --git a/setup/src/Magento/Setup/Model/Installer.php b/setup/src/Magento/Setup/Model/Installer.php index a7dc4dd1e6099..a1a62dc594683 100644 --- a/setup/src/Magento/Setup/Model/Installer.php +++ b/setup/src/Magento/Setup/Model/Installer.php @@ -815,6 +815,11 @@ public function declarativeInstallSchema(array $request) */ public function installSchema(array $request) { + /** @var \Magento\Framework\Registry $registry */ + $registry = $this->objectManagerProvider->get()->get(\Magento\Framework\Registry::class); + //For backward compatibility in install and upgrade scripts with enabled parallelization. + $registry->register('setup-mode-enabled', true); + $this->assertDbConfigExists(); $this->assertDbAccessible(); $setup = $this->setupFactory->create($this->context->getResources()); @@ -831,6 +836,8 @@ public function installSchema(array $request) $schemaListener->setResource('default'); $this->schemaPersistor->persist($schemaListener); } + + $registry->unregister('setup-mode-enabled'); } /** @@ -853,12 +860,19 @@ private function convertationOfOldScriptsIsAllowed(array $request) */ public function installDataFixtures(array $request = []) { + /** @var \Magento\Framework\Registry $registry */ + $registry = $this->objectManagerProvider->get()->get(\Magento\Framework\Registry::class); + //For backward compatibility in install and upgrade scripts with enabled parallelization. + $registry->register('setup-mode-enabled', true); + $this->assertDbConfigExists(); $this->assertDbAccessible(); $setup = $this->dataSetupFactory->create(); $this->checkFilePermissionsForDbUpgrade(); $this->log->log('Data install/update:'); $this->handleDBSchemaData($setup, 'data', $request); + + $registry->unregister('setup-mode-enabled'); } /** From 1d1145689bac3c0ddc2f0ca7ae2468e59a194c55 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Sat, 21 Jul 2018 10:54:55 +0300 Subject: [PATCH 0419/1171] MAGETWO-93669: [Forwardport] Implement parallelization for Category Product Indexer --- .../Magento/Indexer/Model/ProcessManager.php | 10 ++++++---- app/code/Magento/Indexer/etc/di.xml | 6 +++++- .../Framework/App/ResourceConnection.php | 18 +++++++++++++++--- .../Setup/Test/Unit/Model/InstallerTest.php | 4 +++- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Indexer/Model/ProcessManager.php b/app/code/Magento/Indexer/Model/ProcessManager.php index 4931f4f82b1bb..04cd713fffb11 100644 --- a/app/code/Magento/Indexer/Model/ProcessManager.php +++ b/app/code/Magento/Indexer/Model/ProcessManager.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Indexer\Model; /** @@ -107,7 +109,7 @@ private function multiThreadsExecute($userFunctions) * * @return bool */ - private function isCanBeParalleled() + private function isCanBeParalleled(): bool { return function_exists('pcntl_fork'); } @@ -117,7 +119,7 @@ private function isCanBeParalleled() * * @return bool */ - private function isSetupMode() + private function isSetupMode(): bool { return $this->registry->registry('setup-mode-enabled') ?: false; } @@ -128,7 +130,7 @@ private function isSetupMode() * @param callable $userFunction * @SuppressWarnings(PHPMD.ExitExpression) */ - private function startChildProcess($userFunction) + private function startChildProcess(callable $userFunction) { $status = call_user_func($userFunction); $status = is_integer($status) ? $status : 0; @@ -140,7 +142,7 @@ private function startChildProcess($userFunction) * * @param int $threadNumber */ - private function executeParentProcess(&$threadNumber) + private function executeParentProcess(int &$threadNumber) { $threadNumber++; if ($threadNumber >= $this->threadsCount) { diff --git a/app/code/Magento/Indexer/etc/di.xml b/app/code/Magento/Indexer/etc/di.xml index 610f08fac3a05..6abaaf625e108 100644 --- a/app/code/Magento/Indexer/etc/di.xml +++ b/app/code/Magento/Indexer/etc/di.xml @@ -42,7 +42,11 @@ <plugin name="page-cache-indexer-reindex-clean-cache" type="Magento\Indexer\Model\Processor\CleanCache" sortOrder="10"/> </type> - + <type name="\Magento\Indexer\Model\ProcessManager"> + <arguments> + <argument name="threadsCount" xsi:type="init_parameter">Magento\Indexer\Model\ProcessManager::THREADS_COUNT</argument> + </arguments> + </type> <type name="Magento\Framework\Console\CommandListInterface"> <arguments> <argument name="commands" xsi:type="array"> diff --git a/lib/internal/Magento/Framework/App/ResourceConnection.php b/lib/internal/Magento/Framework/App/ResourceConnection.php index 5b9ae925bff27..148053fa44e08 100644 --- a/lib/internal/Magento/Framework/App/ResourceConnection.php +++ b/lib/internal/Magento/Framework/App/ResourceConnection.php @@ -104,9 +104,21 @@ public function getConnection($resourceName = self::DEFAULT_CONNECTION) */ public function closeConnection($resourceName = self::DEFAULT_CONNECTION) { - $processConnectionName = $this->getProcessConnectionName($this->config->getConnectionName($resourceName)); - if (isset($this->connections[$processConnectionName])) { - $this->connections[$processConnectionName] = null; + if ($resourceName === null) { + foreach ($this->connections as $processConnection) { + if ($processConnection !== null) { + $processConnection->closeConnection(); + } + } + $this->connections = []; + } else { + $processConnectionName = $this->getProcessConnectionName($this->config->getConnectionName($resourceName)); + if (isset($this->connections[$processConnectionName])) { + if ($this->connections[$processConnectionName] !== null) { + $this->connections[$processConnectionName]->closeConnection(); + } + $this->connections[$processConnectionName] = null; + } } } diff --git a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php index 413094a1e0115..fffbfeeab4d8a 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php @@ -316,6 +316,7 @@ public function testInstall() $appState->expects($this->once()) ->method('setAreaCode') ->with(\Magento\Framework\App\Area::AREA_GLOBAL); + $registry = $this->createMock(\Magento\Framework\Registry::class); $this->setupFactory->expects($this->atLeastOnce())->method('create')->with($resource)->willReturn($setup); $this->dataSetupFactory->expects($this->atLeastOnce())->method('create')->willReturn($dataSetup); $this->objectManager->expects($this->any()) @@ -346,7 +347,8 @@ public function testInstall() ->will($this->returnValueMap([ [\Magento\Framework\App\State::class, $appState], [\Magento\Framework\App\Cache\Manager::class, $cacheManager], - [\Magento\Setup\Model\DeclarationInstaller::class, $this->declarationInstallerMock] + [\Magento\Setup\Model\DeclarationInstaller::class, $this->declarationInstallerMock], + [\Magento\Framework\Registry::class, $registry] ])); $this->adminFactory->expects($this->once())->method('create')->willReturn( $this->createMock(\Magento\Setup\Model\AdminAccount::class) From df575d8682198e9ded5b464b04928ab598ffefb0 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Sat, 21 Jul 2018 11:24:32 +0300 Subject: [PATCH 0420/1171] MAGETWO-93669: [Forwardport] Implement parallelization for Category Product Indexer --- setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php index fffbfeeab4d8a..882483ce7674a 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php @@ -267,6 +267,9 @@ private function createObject($connectionFactory = false, $objectManagerProvider ); } + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ public function testInstall() { $request = [ From e78792185c843e9b1788e05ae9b85d8d7632b396 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Sat, 21 Jul 2018 12:07:49 +0300 Subject: [PATCH 0421/1171] MAGETWO-93671: [Forwardport] Implement parallelization for Search Indexer --- .../CatalogSearch/Model/Indexer/Fulltext.php | 17 +++++++++++++++-- .../Test/Unit/Model/Indexer/FulltextTest.php | 11 +++++++++++ app/code/Magento/CatalogSearch/composer.json | 1 + app/code/Magento/CatalogSearch/etc/module.xml | 1 + 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php index d51be12f01db5..66727a787b3f6 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php @@ -10,6 +10,7 @@ use Magento\CatalogSearch\Model\ResourceModel\Fulltext as FulltextResource; use Magento\Framework\Indexer\Dimension\DimensionProviderInterface; use Magento\Store\Model\StoreDimensionProvider; +use Magento\Indexer\Model\ProcessManager; /** * Provide functionality for Fulltext Search indexing. @@ -62,14 +63,20 @@ class Fulltext implements */ private $dimensionProvider; + /** + * @var ProcessManager + */ + private $processManager; + /** * @param FullFactory $fullActionFactory * @param IndexerHandlerFactory $indexerHandlerFactory * @param FulltextResource $fulltextResource - * @param array $data * @param IndexSwitcherInterface $indexSwitcher * @param StateFactory $indexScopeStateFactory * @param DimensionProviderInterface $dimensionProvider + * @param ProcessManager $processManager + * @param array $data */ public function __construct( FullFactory $fullActionFactory, @@ -78,6 +85,7 @@ public function __construct( IndexSwitcherInterface $indexSwitcher, StateFactory $indexScopeStateFactory, DimensionProviderInterface $dimensionProvider, + ProcessManager $processManager, array $data ) { $this->fullAction = $fullActionFactory->create(['data' => $data]); @@ -87,6 +95,7 @@ public function __construct( $this->indexSwitcher = $indexSwitcher; $this->indexScopeState = $indexScopeStateFactory->create(); $this->dimensionProvider = $dimensionProvider; + $this->processManager = $processManager; } /** @@ -145,9 +154,13 @@ public function executeByDimension(array $dimensions, \Traversable $entityIds = */ public function executeFull() { + $userFunctions = []; foreach ($this->dimensionProvider->getIterator() as $dimension) { - $this->executeByDimension($dimension); + $userFunctions[] = function () use ($dimension) { + $this->executeByDimension($dimension); + }; } + $this->processManager->execute($userFunctions); } /** diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php index 0c3bd42a1b5eb..595e32b4c1988 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php @@ -44,6 +44,11 @@ class FulltextTest extends \PHPUnit\Framework\TestCase */ private $dimensionProviderMock; + /** + * @var \Magento\Indexer\Model\ProcessManager|\PHPUnit_Framework_MockObject_MockObject + */ + private $processManager; + protected function setUp() { $this->fullAction = $this->getClassMock(\Magento\CatalogSearch\Model\Indexer\Fulltext\Action\Full::class); @@ -70,6 +75,11 @@ protected function setUp() $stateMock = $this->getMockBuilder(\Magento\CatalogSearch\Model\Indexer\Scope\State::class) ->getMock(); $objectManagerHelper = new ObjectManagerHelper($this); + + $this->processManager = new \Magento\Indexer\Model\ProcessManager( + $this->getClassMock(\Magento\Framework\App\ResourceConnection::class) + ); + $this->model = $objectManagerHelper->getObject( \Magento\CatalogSearch\Model\Indexer\Fulltext::class, [ @@ -80,6 +90,7 @@ protected function setUp() 'indexSwitcher' => $this->indexSwitcher, 'dimensionProvider' => $this->dimensionProviderMock, 'indexScopeState' => $stateMock, + 'processManager' => $this->processManager, ] ); } diff --git a/app/code/Magento/CatalogSearch/composer.json b/app/code/Magento/CatalogSearch/composer.json index 72bf2ec90a582..298511ef428a9 100644 --- a/app/code/Magento/CatalogSearch/composer.json +++ b/app/code/Magento/CatalogSearch/composer.json @@ -9,6 +9,7 @@ "magento/framework": "*", "magento/module-backend": "*", "magento/module-catalog": "*", + "magento/module-indexer": "100.2.*", "magento/module-catalog-inventory": "*", "magento/module-customer": "*", "magento/module-directory": "*", diff --git a/app/code/Magento/CatalogSearch/etc/module.xml b/app/code/Magento/CatalogSearch/etc/module.xml index 68253014ec150..8fe282ce7539b 100644 --- a/app/code/Magento/CatalogSearch/etc/module.xml +++ b/app/code/Magento/CatalogSearch/etc/module.xml @@ -10,6 +10,7 @@ <sequence> <module name="Magento_Search"/> <module name="Magento_Catalog"/> + <module name="Magento_Indexer"/> <module name="Magento_CatalogRule" /> <module name="Magento_CatalogInventory" /> </sequence> From 7e68204bc72ae0ccf9a6c68d8689d46c92009e78 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Sat, 21 Jul 2018 13:38:18 +0300 Subject: [PATCH 0422/1171] MAGETWO-93671: [Forwardport] Implement parallelization for Search Indexer --- app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php index 66727a787b3f6..d5dea5f7c3ec8 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php @@ -15,6 +15,8 @@ /** * Provide functionality for Fulltext Search indexing. * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * * @api * @since 100.0.2 */ From fe3a426aa430d4ada4a2cbcf892a6b24e5072209 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Sat, 21 Jul 2018 14:08:20 +0300 Subject: [PATCH 0423/1171] MAGETWO-93671: [Forwardport] Implement parallelization for Search Indexer --- .../Magento/CatalogSearch/Model/Indexer/Fulltext.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php index d5dea5f7c3ec8..df9eb6e78990a 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php @@ -77,8 +77,8 @@ class Fulltext implements * @param IndexSwitcherInterface $indexSwitcher * @param StateFactory $indexScopeStateFactory * @param DimensionProviderInterface $dimensionProvider - * @param ProcessManager $processManager * @param array $data + * @param ProcessManager $processManager */ public function __construct( FullFactory $fullActionFactory, @@ -87,8 +87,8 @@ public function __construct( IndexSwitcherInterface $indexSwitcher, StateFactory $indexScopeStateFactory, DimensionProviderInterface $dimensionProvider, - ProcessManager $processManager, - array $data + array $data, + ProcessManager $processManager = null ) { $this->fullAction = $fullActionFactory->create(['data' => $data]); $this->indexerHandlerFactory = $indexerHandlerFactory; @@ -97,7 +97,9 @@ public function __construct( $this->indexSwitcher = $indexSwitcher; $this->indexScopeState = $indexScopeStateFactory->create(); $this->dimensionProvider = $dimensionProvider; - $this->processManager = $processManager; + $this->processManager = $processManager ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + ProcessManager::class + ); } /** From a659f4b9321a4036a9e45130b7695387066f967d Mon Sep 17 00:00:00 2001 From: Rakesh Gangani <rsquare0611@gmail.com> Date: Sat, 19 May 2018 14:01:41 +0530 Subject: [PATCH 0424/1171] FIXED - appended payment code to ID field to make it unique --- .../view/frontend/web/template/billing-address/form.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html index 086cca814bec1..dcd726076d900 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html @@ -16,8 +16,8 @@ <!--/ko--> <!-- ko if: (isCustomerLoggedIn && customerHasAddresses) --> <div class="choice field"> - <input type="checkbox" class="checkbox" id="billing-save-in-address-book" data-bind="checked: saveInAddressBook" /> - <label class="label" for="billing-save-in-address-book"> + <input type="checkbox" class="checkbox" data-bind="checked: saveInAddressBook, attr: {id: 'billing-save-in-address-book-' + getCode($parent)}" /> + <label class="label" data-bind="attr: {for: 'billing-save-in-address-book-' + getCode($parent)}" > <span data-bind="i18n: 'Save in address book'"></span> </label> </div> From f7228d01b3480fbeef95b0ebfe3591ec5ab75e56 Mon Sep 17 00:00:00 2001 From: Chirag Matholiya <chirag@wagento.com> Date: Sat, 19 May 2018 14:58:37 +0530 Subject: [PATCH 0425/1171] Responsive Design Footers bottom of screen on mobile devices #15118 --- .../Magento/luma/Magento_Theme/web/css/source/_module.less | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less index 83157281561c2..a18268f5af677 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less @@ -264,6 +264,7 @@ display: block; padding: @indent__s; text-align: center; + box-sizing: border-box; } .page-header, @@ -417,6 +418,12 @@ } } } + .page-footer, + .copyright { + bottom: 0; + position: absolute; + width: 100%; + } } .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) { From ffa036de7266d1982655d521315ef3f5d2851425 Mon Sep 17 00:00:00 2001 From: "Vasiliev.A" <a.vasiliev.sam@gmail.com> Date: Fri, 18 May 2018 15:14:53 +0300 Subject: [PATCH 0426/1171] Fix "Confirmation request" email is sent on customer's newsletter unsubscription (issues/15218). Skip update customer subscribe status from save subscribe action from my account when nothing is changed. --- .../Newsletter/Controller/Manage/Save.php | 30 +++++++++++++++---- .../Magento/Newsletter/Model/Subscriber.php | 4 ++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Newsletter/Controller/Manage/Save.php b/app/code/Magento/Newsletter/Controller/Manage/Save.php index 75ef8b26f50a9..548e9222b3b16 100644 --- a/app/code/Magento/Newsletter/Controller/Manage/Save.php +++ b/app/code/Magento/Newsletter/Controller/Manage/Save.php @@ -4,9 +4,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Newsletter\Controller\Manage; use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository; +use Magento\Newsletter\Model\Subscriber; class Save extends \Magento\Newsletter\Controller\Manage { @@ -74,13 +76,29 @@ public function execute() $customer = $this->customerRepository->getById($customerId); $storeId = $this->storeManager->getStore()->getId(); $customer->setStoreId($storeId); - $this->customerRepository->save($customer); - if ((boolean)$this->getRequest()->getParam('is_subscribed', false)) { - $this->subscriberFactory->create()->subscribeCustomerById($customerId); - $this->messageManager->addSuccess(__('We saved the subscription.')); + $isSubscribedState = $customer->getExtensionAttributes() + ->getIsSubscribed(); + $isSubscribedParam = (boolean)$this->getRequest() + ->getParam('is_subscribed', false); + if ($isSubscribedParam != $isSubscribedState) { + $this->customerRepository->save($customer); + if ($isSubscribedParam) { + $subscribeModel = $this->subscriberFactory->create() + ->subscribeCustomerById($customerId); + $subscribeStatus = $subscribeModel->getStatus(); + if ($subscribeStatus == Subscriber::STATUS_SUBSCRIBED) { + $this->messageManager->addSuccess(__('We saved the subscription.')); + } else { + $this->messageManager->addSuccess(__('The confirmation request has been sent.')); + } + } else { + $this->subscriberFactory->create() + ->unsubscribeCustomerById($customerId); + $this->messageManager->addSuccess(__('We removed the subscription.')); + } } else { - $this->subscriberFactory->create()->unsubscribeCustomerById($customerId); - $this->messageManager->addSuccess(__('We removed the subscription.')); + $this->_redirect('newsletter/manage/'); + return; } } catch (\Exception $e) { $this->messageManager->addError(__('Something went wrong while saving your subscription.')); diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php index 76d55de5954b1..27ee8197778ff 100644 --- a/app/code/Magento/Newsletter/Model/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/Subscriber.php @@ -559,7 +559,9 @@ protected function _updateCustomerSubscription($customerId, $subscribe) ) { $status = self::STATUS_UNCONFIRMED; } elseif ($isConfirmNeed) { - $status = self::STATUS_NOT_ACTIVE; + if ($this->getStatus() != self::STATUS_SUBSCRIBED) { + $status = self::STATUS_NOT_ACTIVE; + } } } elseif (($this->getStatus() == self::STATUS_UNCONFIRMED) && ($customerData->getConfirmation() === null)) { $status = self::STATUS_SUBSCRIBED; From a36f791ec3ac99d2706c08f969fed608c0b63e0f Mon Sep 17 00:00:00 2001 From: Alex Lyzun <alyzun@comwrap.com> Date: Sat, 26 May 2018 16:01:49 +0200 Subject: [PATCH 0427/1171] Remove redirect to the same page. Left default Magento redirect to Customer account page --- app/code/Magento/Newsletter/Controller/Manage/Save.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Newsletter/Controller/Manage/Save.php b/app/code/Magento/Newsletter/Controller/Manage/Save.php index 548e9222b3b16..76aa67eecffe1 100644 --- a/app/code/Magento/Newsletter/Controller/Manage/Save.php +++ b/app/code/Magento/Newsletter/Controller/Manage/Save.php @@ -96,9 +96,6 @@ public function execute() ->unsubscribeCustomerById($customerId); $this->messageManager->addSuccess(__('We removed the subscription.')); } - } else { - $this->_redirect('newsletter/manage/'); - return; } } catch (\Exception $e) { $this->messageManager->addError(__('Something went wrong while saving your subscription.')); From 1b504cdec56c63060042d4ae08d1cac743c0e1ff Mon Sep 17 00:00:00 2001 From: Alex Lyzun <alyzun@comwrap.com> Date: Sun, 27 May 2018 11:58:39 +0200 Subject: [PATCH 0428/1171] Updat messages related to a new logic --- app/code/Magento/Newsletter/Controller/Manage/Save.php | 3 +++ .../testsuite/Magento/Newsletter/Controller/ManageTest.php | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Newsletter/Controller/Manage/Save.php b/app/code/Magento/Newsletter/Controller/Manage/Save.php index 76aa67eecffe1..d70fdf3c76cc0 100644 --- a/app/code/Magento/Newsletter/Controller/Manage/Save.php +++ b/app/code/Magento/Newsletter/Controller/Manage/Save.php @@ -96,6 +96,9 @@ public function execute() ->unsubscribeCustomerById($customerId); $this->messageManager->addSuccess(__('We removed the subscription.')); } + }else{ + $this->messageManager->addSuccess(__('We updated the subscription.')); + return; } } catch (\Exception $e) { $this->messageManager->addError(__('Something went wrong while saving your subscription.')); diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php index 1a27994b607f1..35d89256c283a 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php @@ -68,6 +68,7 @@ public function testSaveAction() */ public function testSaveActionRemoveSubscription() { + $this->getRequest() ->setParam('form_key', 'formKey') ->setParam('is_subscribed', '0'); @@ -84,7 +85,7 @@ public function testSaveActionRemoveSubscription() * Check that success message */ $this->assertSessionMessages( - $this->equalTo(['We removed the subscription.']), + $this->equalTo(['We updated the subscription.']), \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS ); } From ac4a2fa7216b5aa9bd5de282b06c5a50a737490d Mon Sep 17 00:00:00 2001 From: Alex Lyzun <alyzun@comwrap.com> Date: Sun, 27 May 2018 22:04:54 +0200 Subject: [PATCH 0429/1171] Fix static tests and remove return before redirect --- app/code/Magento/Newsletter/Controller/Manage/Save.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Newsletter/Controller/Manage/Save.php b/app/code/Magento/Newsletter/Controller/Manage/Save.php index d70fdf3c76cc0..947c27ee45c82 100644 --- a/app/code/Magento/Newsletter/Controller/Manage/Save.php +++ b/app/code/Magento/Newsletter/Controller/Manage/Save.php @@ -96,9 +96,8 @@ public function execute() ->unsubscribeCustomerById($customerId); $this->messageManager->addSuccess(__('We removed the subscription.')); } - }else{ + } else { $this->messageManager->addSuccess(__('We updated the subscription.')); - return; } } catch (\Exception $e) { $this->messageManager->addError(__('Something went wrong while saving your subscription.')); From 3533e68e1617d8c5ab8381674a6840d5f821bb55 Mon Sep 17 00:00:00 2001 From: Alex Lyzun <alyzun@comwrap.com> Date: Tue, 26 Jun 2018 13:42:24 +0200 Subject: [PATCH 0430/1171] Change Success messages to appropriated --- app/code/Magento/Newsletter/Controller/Manage/Save.php | 10 +++++----- .../Magento/Newsletter/Controller/ManageTest.php | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Newsletter/Controller/Manage/Save.php b/app/code/Magento/Newsletter/Controller/Manage/Save.php index 947c27ee45c82..419cbac10ffd1 100644 --- a/app/code/Magento/Newsletter/Controller/Manage/Save.php +++ b/app/code/Magento/Newsletter/Controller/Manage/Save.php @@ -80,24 +80,24 @@ public function execute() ->getIsSubscribed(); $isSubscribedParam = (boolean)$this->getRequest() ->getParam('is_subscribed', false); - if ($isSubscribedParam != $isSubscribedState) { + if ($isSubscribedParam !== $isSubscribedState) { $this->customerRepository->save($customer); if ($isSubscribedParam) { $subscribeModel = $this->subscriberFactory->create() ->subscribeCustomerById($customerId); $subscribeStatus = $subscribeModel->getStatus(); if ($subscribeStatus == Subscriber::STATUS_SUBSCRIBED) { - $this->messageManager->addSuccess(__('We saved the subscription.')); + $this->messageManager->addSuccess(__('We have saved your subscription.')); } else { - $this->messageManager->addSuccess(__('The confirmation request has been sent.')); + $this->messageManager->addSuccess(__('A confirmation request has been sent.')); } } else { $this->subscriberFactory->create() ->unsubscribeCustomerById($customerId); - $this->messageManager->addSuccess(__('We removed the subscription.')); + $this->messageManager->addSuccess(__('We have removed your newsletter subscription.')); } } else { - $this->messageManager->addSuccess(__('We updated the subscription.')); + $this->messageManager->addSuccess(__('We have updated your subscription.')); } } catch (\Exception $e) { $this->messageManager->addError(__('Something went wrong while saving your subscription.')); diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php index 35d89256c283a..90892be1327c9 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php @@ -58,7 +58,7 @@ public function testSaveAction() * Check that success message */ $this->assertSessionMessages( - $this->equalTo(['We saved the subscription.']), + $this->equalTo(['We have saved your subscription.']), \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS ); } @@ -85,7 +85,7 @@ public function testSaveActionRemoveSubscription() * Check that success message */ $this->assertSessionMessages( - $this->equalTo(['We updated the subscription.']), + $this->equalTo(['We have updated your subscription.']), \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS ); } From c06e26fe83585e17768648cc6f19fa1aeb63339b Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Mon, 9 Jul 2018 13:36:29 +0530 Subject: [PATCH 0431/1171] Updated Magento_Newsletter's block file. --- app/code/Magento/Newsletter/Block/Adminhtml/Template/Edit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Newsletter/Block/Adminhtml/Template/Edit.php b/app/code/Magento/Newsletter/Block/Adminhtml/Template/Edit.php index 79db83e7a49b8..33c3706183368 100644 --- a/app/code/Magento/Newsletter/Block/Adminhtml/Template/Edit.php +++ b/app/code/Magento/Newsletter/Block/Adminhtml/Template/Edit.php @@ -216,7 +216,7 @@ public function getForm() } /** - * Return return template name for JS + * Return template name for JS * * @return string */ From c854cadd189799975e75e19e8529252654484216 Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Mon, 9 Jul 2018 14:00:07 +0530 Subject: [PATCH 0432/1171] Corrected block name in Magento_Framework's test xml file. --- .../Framework/View/_files/layout_directives_test/group.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout_directives_test/group.xml b/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout_directives_test/group.xml index 18501ecfbbfbf..8e50fe1888104 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout_directives_test/group.xml +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout_directives_test/group.xml @@ -9,17 +9,17 @@ <block class="Magento\Framework\View\Element\Text" name="block1"> <block class="Magento\Framework\View\Element\Text" name="block2" group="group1"> <arguments> - <argument xsi:type="string" name="text">blok2</argument> + <argument xsi:type="string" name="text">block2</argument> </arguments> </block> <block class="Magento\Framework\View\Element\Text" name="block3" group="group1" > <arguments> - <argument xsi:type="string" name="text">blok3</argument> + <argument xsi:type="string" name="text">block3</argument> </arguments> </block> <block class="Magento\Framework\View\Element\Text" name="block4"> <arguments> - <argument xsi:type="string" name="text">blok4</argument> + <argument xsi:type="string" name="text">block4</argument> </arguments> </block> </block> From 5c3cd29b99cbec84d78812ebe858107ec3cfc431 Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Tue, 10 Jul 2018 18:04:14 +0530 Subject: [PATCH 0433/1171] Added 'title' attribute to 'a' link. --- .../Magento/Theme/view/frontend/templates/html/bugreport.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Theme/view/frontend/templates/html/bugreport.phtml b/app/code/Magento/Theme/view/frontend/templates/html/bugreport.phtml index 5d8203244256e..f147b7085945f 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/bugreport.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/bugreport.phtml @@ -7,7 +7,7 @@ <small class="bugs"> <span><?= /* @escapeNotVerified */ __('Help Us Keep Magento Healthy') ?></span> <a href="http://www.magentocommerce.com/bug-tracking" - target="_blank"> + target="_blank" title="<?= /* @escapeNotVerified */ __('Report All Bugs') ?>"> <?= /* @escapeNotVerified */ __('Report All Bugs') ?> </a> </small> From 21ee93eb53a5b87df53904feb34f0fa6d8b5cceb Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Tue, 10 Jul 2018 18:11:17 +0530 Subject: [PATCH 0434/1171] Added 'title' attribute to 'img' tag. --- .../Magento/Theme/view/frontend/templates/html/header/logo.phtml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml b/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml index 8c3663337cbbe..17f8d7c70f574 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml @@ -18,6 +18,7 @@ <a class="logo" href="<?= $block->getUrl('') ?>" title="<?= /* @escapeNotVerified */ $storeName ?>"> <?php endif ?> <img src="<?= /* @escapeNotVerified */ $block->getLogoSrc() ?>" + title="<?= /* @escapeNotVerified */ $block->getLogoAlt() ?>" alt="<?= /* @escapeNotVerified */ $block->getLogoAlt() ?>" <?= $block->getLogoWidth() ? 'width="' . $block->getLogoWidth() . '"' : '' ?> <?= $block->getLogoHeight() ? 'height="' . $block->getLogoHeight() . '"' : '' ?> From d9637c21778344d55acfe4e957a1c06f36dfdcba Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Tue, 10 Jul 2018 18:22:03 +0530 Subject: [PATCH 0435/1171] Added 'title' attribute to 'img' tag. --- .../Magento/Backend/view/adminhtml/templates/page/header.phtml | 2 +- .../view/adminhtml/templates/widget/form/element/gallery.phtml | 2 +- app/code/Magento/Ui/view/base/web/templates/block-loader.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Backend/view/adminhtml/templates/page/header.phtml b/app/code/Magento/Backend/view/adminhtml/templates/page/header.phtml index 8feccc9cf1b8f..f952001f5e2ff 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/page/header.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/page/header.phtml @@ -17,7 +17,7 @@ <?= /* @escapeNotVerified */ $edition ?> class="logo"> <img class="logo-img" src="<?= /* @escapeNotVerified */ $block->getViewFileUrl($logoSrc) ?>" - alt="<?= $block->escapeHtml(__('Magento Admin Panel')) ?>"/> + alt="<?= $block->escapeHtml(__('Magento Admin Panel')) ?>" title="<?= $block->escapeHtml(__('Magento Admin Panel')) ?>"/> </a> <?php break; ?> <?php case 'user': ?> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/widget/form/element/gallery.phtml b/app/code/Magento/Backend/view/adminhtml/templates/widget/form/element/gallery.phtml index 4f8d9a56a38a3..e11c0efc123ff 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/widget/form/element/gallery.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/widget/form/element/gallery.phtml @@ -34,7 +34,7 @@ <?php foreach ($block->getValues()->getAttributeBackend()->getImageTypes() as $type): ?> <td class="gallery" align="center" style="vertical-align:bottom;"> <a href="<?= /* @escapeNotVerified */ $image->setType($type)->getSourceUrl() ?>" target="_blank" onclick="imagePreview('<?= $block->getElement()->getHtmlId() ?>_image_<?= /* @escapeNotVerified */ $type ?>_<?= /* @escapeNotVerified */ $image->getValueId() ?>');return false;"> - <img id="<?= $block->getElement()->getHtmlId() ?>_image_<?= /* @escapeNotVerified */ $type ?>_<?= /* @escapeNotVerified */ $image->getValueId() ?>" src="<?= /* @escapeNotVerified */ $image->setType($type)->getSourceUrl() ?>?<?= /* @escapeNotVerified */ time() ?>" alt="<?= /* @escapeNotVerified */ $image->getValue() ?>" height="25" class="small-image-preview v-middle"/></a><br/> + <img id="<?= $block->getElement()->getHtmlId() ?>_image_<?= /* @escapeNotVerified */ $type ?>_<?= /* @escapeNotVerified */ $image->getValueId() ?>" src="<?= /* @escapeNotVerified */ $image->setType($type)->getSourceUrl() ?>?<?= /* @escapeNotVerified */ time() ?>" alt="<?= /* @escapeNotVerified */ $image->getValue() ?>" title="<?= /* @escapeNotVerified */ $image->getValue() ?>" height="25" class="small-image-preview v-middle"/></a><br/> <input type="file" name="<?= /* @escapeNotVerified */ $block->getElement()->getName() ?>_<?= /* @escapeNotVerified */ $type ?>[<?= /* @escapeNotVerified */ $image->getValueId() ?>]" size="1"></td> <?php endforeach; ?> <td class="gallery" align="center" style="vertical-align:bottom;"><input type="input" name="<?= /* @escapeNotVerified */ $block->getElement()->getParentName() ?>[position][<?= /* @escapeNotVerified */ $image->getValueId() ?>]" value="<?= /* @escapeNotVerified */ $image->getPosition() ?>" id="<?= $block->getElement()->getHtmlId() ?>_position_<?= /* @escapeNotVerified */ $image->getValueId() ?>" size="3"/></td> diff --git a/app/code/Magento/Ui/view/base/web/templates/block-loader.html b/app/code/Magento/Ui/view/base/web/templates/block-loader.html index 3d8b730a9e8c5..cb28b09e4da83 100644 --- a/app/code/Magento/Ui/view/base/web/templates/block-loader.html +++ b/app/code/Magento/Ui/view/base/web/templates/block-loader.html @@ -6,6 +6,6 @@ --> <div data-role="loader" class="loading-mask" style="position: absolute;"> <div class="loader"> - <img src="<%= loaderImageHref %>" alt="Loading..." style="position: absolute;"> + <img src="<%= loaderImageHref %>" alt="Loading..." title="Loading..." style="position: absolute;"> </div> </div> From 4aa808e537bf7526d81a3bc18910ad2ddcc8400f Mon Sep 17 00:00:00 2001 From: Artem Klimov <art.klimoff@gmail.com> Date: Sun, 22 Jul 2018 21:02:59 +0300 Subject: [PATCH 0436/1171] Fixed skipping of empty option --- app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php b/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php index 1c341012083b9..72f483b7445bb 100644 --- a/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php +++ b/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php @@ -68,7 +68,7 @@ public function resolve( if (is_array($attributeOptions)) { /** @var \Magento\Eav\Api\Data\AttributeOptionInterface $option */ foreach ($attributeOptions as $option) { - if (!$option->getValue()) { + if ($option->getValue() === '') { continue; } From 8f3c6e920b7bdb953c31013c843017a4d25e3ed6 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Mon, 23 Jul 2018 06:38:07 +0000 Subject: [PATCH 0437/1171] graphql-ce-120: fixed api functional test for store config --- .../GraphQl/Store/StoreConfigResolverTest.php | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php index df86519ae3d1e..4657a1e763ae1 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php @@ -47,24 +47,22 @@ public function testGetStoreConfig() = <<<QUERY { storeConfig{ - items{ - id, - code, - website_id, - locale, - base_currency_code, - default_display_currency_code, - timezone, - weight_unit, - base_url, - base_link_url, - base_static_url, - base_media_url, - secure_base_url, - secure_base_link_url, - secure_base_static_url, - secure_base_media_url - } + id, + code, + website_id, + locale, + base_currency_code, + default_display_currency_code, + timezone, + weight_unit, + base_url, + base_link_url, + base_static_url, + base_media_url, + secure_base_url, + secure_base_link_url, + secure_base_static_url, + secure_base_media_url } } QUERY; From 0cefd65eff05e1e74580839176df9e55833834c2 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 23 Jul 2018 10:25:55 +0300 Subject: [PATCH 0438/1171] MAGETWO-70531: Store view specific label for FPT attributes is not displayed on frontend --- app/code/Magento/Weee/Model/Tax.php | 16 ++++- .../Magento/Weee/Test/Unit/Model/TaxTest.php | 68 +++++++++++++------ 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Weee/Model/Tax.php b/app/code/Magento/Weee/Model/Tax.php index 3c6d29ae75217..941faed0498f4 100644 --- a/app/code/Magento/Weee/Model/Tax.php +++ b/app/code/Magento/Weee/Model/Tax.php @@ -248,10 +248,20 @@ public function getProductWeeeAttributes( $round = true ) { $result = []; - - $websiteId = $this->_storeManager->getWebsite($website)->getId(); + $websiteId = null; /** @var \Magento\Store\Model\Store $store */ - $store = $this->_storeManager->getWebsite($website)->getDefaultGroup()->getDefaultStore(); + $store = null; + if (!$website) { + $store = $product->getStore(); + if ($store) { + $websiteId = $store->getWebsiteId(); + } + } + if (!$websiteId) { + $websiteObject = $this->_storeManager->getWebsite($website); + $websiteId = $websiteObject->getId(); + $store = $websiteObject->getDefaultGroup()->getDefaultStore(); + } $allWeee = $this->getWeeeTaxAttributeCodes($store); if (!$allWeee) { diff --git a/app/code/Magento/Weee/Test/Unit/Model/TaxTest.php b/app/code/Magento/Weee/Test/Unit/Model/TaxTest.php index 7414e0444598b..ac90dfade07e2 100644 --- a/app/code/Magento/Weee/Test/Unit/Model/TaxTest.php +++ b/app/code/Magento/Weee/Test/Unit/Model/TaxTest.php @@ -157,13 +157,17 @@ protected function setUp() } /** - * test GetProductWeeeAttributes * @dataProvider getProductWeeeAttributesDataProvider * @param array $weeeTaxCalculationsByEntity - * @param array $expectedFptLabel + * @param mixed $websitePassed + * @param string $expectedFptLabel + * @return void */ - public function testGetProductWeeeAttributes($weeeTaxCalculationsByEntity, $expectedFptLabel) - { + public function testGetProductWeeeAttributes( + array $weeeTaxCalculationsByEntity, + $websitePassed, + string $expectedFptLabel + ): void { $product = $this->createMock(\Magento\Catalog\Model\Product::class); $website = $this->createMock(\Magento\Store\Model\Website::class); $store = $this->createMock(\Magento\Store\Model\Store::class); @@ -187,28 +191,38 @@ public function testGetProductWeeeAttributes($weeeTaxCalculationsByEntity, $expe ->method('getAttributeCodesByFrontendType') ->willReturn(['0'=>'fpt']); - $store->expects($this->any()) - ->method('getId') - ->willReturn(1); - - $product->expects($this->any()) - ->method('getId') - ->willReturn(1); - + $this->storeManager->expects($this->any()) + ->method('getWebsite') + ->willReturn($website); $website->expects($this->any()) ->method('getId') - ->willReturn(1); + ->willReturn($websitePassed); $website->expects($this->any()) ->method('getDefaultGroup') ->willReturn($group); - $group->expects($this->any()) ->method('getDefaultStore') ->willReturn($store); + $store->expects($this->any()) + ->method('getId') + ->willReturn(1); - $this->storeManager->expects($this->any()) - ->method('getWebsite') - ->willReturn($website); + if ($websitePassed) { + $product->expects($this->never()) + ->method('getStore') + ->willReturn($store); + } else { + $product->expects($this->once()) + ->method('getStore') + ->willReturn($store); + $store->expects($this->once()) + ->method('getWebsiteId') + ->willReturn(1); + } + + $product->expects($this->any()) + ->method('getId') + ->willReturn(1); $this->weeeConfig->expects($this->any()) ->method('isEnabled') @@ -237,7 +251,7 @@ public function testGetProductWeeeAttributes($weeeTaxCalculationsByEntity, $expe 0 => $weeeTaxCalculationsByEntity ]); - $result = $this->model->getProductWeeeAttributes($product, null, null, null, true); + $result = $this->model->getProductWeeeAttributes($product, null, null, $websitePassed, true); $this->assertTrue(is_array($result)); $this->assertArrayHasKey(0, $result); $obj = $result[0]; @@ -312,7 +326,8 @@ public function getProductWeeeAttributesDataProvider() 'frontend_label' => 'fpt_label_frontend', 'attribute_code' => 'fpt_code', ], - 'expectedFptLabel' => 'fpt_label' + 'websitePassed' => 1, + 'expectedFptLabel' => 'fpt_label', ], 'store_label_not_defined' => [ 'weeeTaxCalculationsByEntity' => [ @@ -321,8 +336,19 @@ public function getProductWeeeAttributesDataProvider() 'frontend_label' => 'fpt_label_frontend', 'attribute_code' => 'fpt_code', ], - 'expectedFptLabel' => 'fpt_label_frontend' - ] + 'websitePassed' => 1, + 'expectedFptLabel' => 'fpt_label_frontend', + ], + 'website_not_passed' => [ + 'weeeTaxCalculationsByEntity' => [ + 'weee_value' => 1, + 'label_value' => '', + 'frontend_label' => 'fpt_label_frontend', + 'attribute_code' => 'fpt_code', + ], + 'websitePassed' => null, + 'expectedFptLabel' => 'fpt_label_frontend', + ], ]; } From bb65cd9f6af564c4a8beeb517a4e43ada09c594b Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Mon, 23 Jul 2018 10:49:15 +0300 Subject: [PATCH 0439/1171] MAGETWO-93671: [Forwardport] Implement parallelization for Search Indexer --- .../Model/Client/Elasticsearch.php | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php index b6ec06d48a487..118535c36560b 100644 --- a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php @@ -17,7 +17,7 @@ class Elasticsearch implements ClientInterface /** * Elasticsearch Client instance * - * @var \Elasticsearch\Client + * @var \Elasticsearch\Client[] */ protected $client; @@ -53,10 +53,19 @@ public function __construct( $config = $this->buildConfig($options); $elasticsearchClient = \Elasticsearch\ClientBuilder::fromConfig($config, true); } - $this->client = $elasticsearchClient; + $this->client[getmypid()] = $elasticsearchClient; $this->clientOptions = $options; } + private function getClient() + { + $pid = getmypid(); + if (!isset($this->client[$pid])) { + $config = $this->buildConfig($this->clientOptions); + $this->client[$pid] = \Elasticsearch\ClientBuilder::fromConfig($config, true); + } + return $this->client[$pid]; + } /** * Ping the Elasticsearch client * @@ -65,7 +74,7 @@ public function __construct( public function ping() { if ($this->pingResult === null) { - $this->pingResult = $this->client->ping(['client' => ['timeout' => $this->clientOptions['timeout']]]); + $this->pingResult = $this->getClient()->ping(['client' => ['timeout' => $this->clientOptions['timeout']]]); } return $this->pingResult; } @@ -110,7 +119,7 @@ private function buildConfig($options = []) */ public function bulkQuery($query) { - $this->client->bulk($query); + $this->getClient()->bulk($query); } /** @@ -122,7 +131,7 @@ public function bulkQuery($query) */ public function createIndex($index, $settings) { - $this->client->indices()->create([ + $this->getClient()->indices()->create([ 'index' => $index, 'body' => $settings, ]); @@ -136,7 +145,7 @@ public function createIndex($index, $settings) */ public function deleteIndex($index) { - $this->client->indices()->delete(['index' => $index]); + $this->getClient()->indices()->delete(['index' => $index]); } /** @@ -147,7 +156,7 @@ public function deleteIndex($index) */ public function isEmptyIndex($index) { - $stats = $this->client->indices()->stats(['index' => $index, 'metric' => 'docs']); + $stats = $this->getClient()->indices()->stats(['index' => $index, 'metric' => 'docs']); if ($stats['indices'][$index]['primaries']['docs']['count'] == 0) { return true; } @@ -172,7 +181,7 @@ public function updateAlias($alias, $newIndex, $oldIndex = '') $params['body']['actions'][] = ['add' => ['alias' => $alias, 'index' => $newIndex]]; } - $this->client->indices()->updateAliases($params); + $this->getClient()->indices()->updateAliases($params); } /** @@ -183,7 +192,7 @@ public function updateAlias($alias, $newIndex, $oldIndex = '') */ public function indexExists($index) { - return $this->client->indices()->exists(['index' => $index]); + return $this->getClient()->indices()->exists(['index' => $index]); } /** @@ -198,7 +207,7 @@ public function existsAlias($alias, $index = '') if ($index) { $params['index'] = $index; } - return $this->client->indices()->existsAlias($params); + return $this->getClient()->indices()->existsAlias($params); } /** @@ -208,7 +217,7 @@ public function existsAlias($alias, $index = '') */ public function getAlias($alias) { - return $this->client->indices()->getAlias(['name' => $alias]); + return $this->getClient()->indices()->getAlias(['name' => $alias]); } /** @@ -267,7 +276,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) foreach ($fields as $field => $fieldInfo) { $params['body'][$entityType]['properties'][$field] = $fieldInfo; } - $this->client->indices()->putMapping($params); + $this->getClient()->indices()->putMapping($params); } /** @@ -279,7 +288,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) */ public function deleteMapping($index, $entityType) { - $this->client->indices()->deleteMapping([ + $this->getClient()->indices()->deleteMapping([ 'index' => $index, 'type' => $entityType, ]); @@ -293,8 +302,7 @@ public function deleteMapping($index, $entityType) */ public function query($query) { - $params = array_merge($query, ['client' => ['timeout' => $this->clientOptions['timeout']]]); - return $this->client->search($params); + return $this->getClient()->search($query); } /** @@ -305,6 +313,6 @@ public function query($query) */ public function suggest($query) { - return $this->client->suggest($query); + return $this->getClient()->suggest($query); } } From 835032f602b45e6645d8462a4b8d55eb2637f250 Mon Sep 17 00:00:00 2001 From: Chirag Matholiya <chirag@wagento.com> Date: Wed, 18 Jul 2018 11:27:00 +0530 Subject: [PATCH 0440/1171] Fixes white color coding standard. --- .../web/css/source/_module-old.less | 6 +-- .../web/css/source/_module-old.less | 2 +- .../web/css/source/_module-old.less | 6 +-- .../web/css/source/_module-old.less | 2 +- .../web/css/source/_module-old.less | 2 +- .../Magento/backend/web/css/styles-old.less | 40 +++++++++---------- .../backend/web/mui/styles/_table.less | 4 +- lib/web/css/docs/source/_buttons.less | 10 ++--- lib/web/css/docs/source/_pages.less | 16 ++++---- lib/web/css/docs/source/_tables.less | 2 +- 10 files changed, 45 insertions(+), 45 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/_module-old.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/_module-old.less index 6937855492263..7c71913ed8db3 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/_module-old.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/_module-old.less @@ -14,7 +14,7 @@ display: inline-block; vertical-align: top; width: 100%; - background-color: #fff; + background-color: @color-white; border: 1px solid #ada89e; border-radius: 2px; @@ -72,7 +72,7 @@ top: 100%; margin: 1px -1px 0 -1px; border: 1px solid #cac2b5; - background: #fff; + background: @color-white; box-shadow: 0 2px 4px rgba(0, 0, 0, .2); z-index: 990; @@ -229,7 +229,7 @@ > input.ui-autocomplete-loading, > input.mage-suggest-state-loading { - background: #fff url("@{baseDir}mui/images/ajax-loader-small.gif") no-repeat 190px 50%; + background: @color-white url("@{baseDir}mui/images/ajax-loader-small.gif") no-repeat 190px 50%; } } diff --git a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module-old.less b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module-old.less index eefbc11201d9c..2074106e719db 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module-old.less +++ b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module-old.less @@ -53,7 +53,7 @@ } .attribute-change-checkbox { - background: #fff; + background: @color-white; display: block; margin-top: 5px; diff --git a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/_module-old.less b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/_module-old.less index 865ece970465d..73e1bcf4a7d58 100644 --- a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/_module-old.less +++ b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/_module-old.less @@ -16,7 +16,7 @@ ); .action.toggle { - background: #fff; + background: @color-white; border-radius: 0 1px 1px 0; border: 1px solid #ada89e; height: 33px; @@ -58,7 +58,7 @@ .action.toggle { padding: 0 3px; border: 1px solid #b7b2a7; - background: #fff; + background: @color-white; border-radius: 0 1px 1px 0; border-left: none; height: 50px; @@ -121,7 +121,7 @@ width: auto; .addafter { - background: #fff; + background: @color-white; border-width: 1px 0 1px 1px; direction: ltr; height: 33px; diff --git a/app/design/adminhtml/Magento/backend/Magento_Enterprise/web/css/source/_module-old.less b/app/design/adminhtml/Magento/backend/Magento_Enterprise/web/css/source/_module-old.less index 0eba6b0d361f8..3a7b6d30c914d 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Enterprise/web/css/source/_module-old.less +++ b/app/design/adminhtml/Magento/backend/Magento_Enterprise/web/css/source/_module-old.less @@ -299,7 +299,7 @@ .invitee_information .data-table tbody tr th, .inviter_information .data-table tbody tr td, .inviter_information .data-table tbody tr th { - background-color: #fff; + background-color: @color-white; border: 0; color: #666; padding: 9px 10px 10px; diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less index 17910d7c8e5b4..11e0fcc7c8b84 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less @@ -78,7 +78,7 @@ &.ui-state-active { z-index: 2; - background: #FFF; + background: @color-white; padding: 1px (@spacing-m - 1px); border: 2px solid #eb5202; margin: -1px; diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index b66fed0c0a09a..487457372582a 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -525,7 +525,7 @@ right: 0; top: 100%; border: 1px solid #cac2b5; - background: #fff; + background: @color-white; box-shadow: 0 1px 1px rgba(0, 0, 0, .2); z-index: 990; } @@ -786,7 +786,7 @@ border-radius: 1px; padding: 4px; color: #303030; - background-color: #fff; + background-color: @color-white; font-weight: 500; font-size: 14px; height: 33px; @@ -860,7 +860,7 @@ input[type="radio"], input[type="checkbox"] { .lib-css(appearance, none, 1); - background: #fff; + background: @color-white; border-radius: 2px; border: 1px solid #adadad; cursor: pointer; @@ -945,7 +945,7 @@ } select[disabled] option[selected] { - color: #fff; + color: @color-white; background: #aaa; } @@ -1157,7 +1157,7 @@ .fieldset-wrapper, .fieldset { - background: #fff; + background: @color-white; border: 0; margin: 0; padding: 5px 0 38px; @@ -1574,7 +1574,7 @@ .multiselect-alt .item { position: relative; - border-top: 1px solid #fff; + border-top: 1px solid @color-white; cursor: pointer; } @@ -1651,7 +1651,7 @@ ~ .addafter strong { display: inline-block; - background: #fff; + background: @color-white; line-height: 24px; margin: 0 3px 0 0; padding-left: 4px; @@ -2041,7 +2041,7 @@ &:before { position: absolute; content: ""; - background-color: #fff; + background-color: @color-white; right: 0; top: 0; bottom: 0; @@ -2298,7 +2298,7 @@ // -------------------------------------- .preview-window { - background: #fff; + background: @color-white; } .preview-window .toolbar { @@ -2458,7 +2458,7 @@ } .attribute-popup .page-actions.fixed .page-actions-inner { - background: #fff; + background: @color-white; padding: 0; min-width: 100%; max-width: 100%; @@ -2757,7 +2757,7 @@ border: 1px solid #c9c2b8; border-width: 0 0 1px; padding: 6px 10px 7px; - background: #fff; + background: @color-white; .style2(); span { @@ -2769,7 +2769,7 @@ td { border: none; padding: 6px 10px 7px; - background: #fff; + background: @color-white; } tr:last-child td { @@ -2858,7 +2858,7 @@ .table-fieldset-alt tbody tr:nth-child(odd) td, .table-fieldset-alt tbody tr:nth-child(odd):hover td { - background: #fff; + background: @color-white; } // @@ -2918,7 +2918,7 @@ } .order-details-existing-customer { - background: #fff; + background: @color-white; padding-left: 0; position: relative; width: 77.9%; @@ -3035,7 +3035,7 @@ } .gift-options-tooltip { - background: #fff; + background: @color-white; border-radius: 5px; padding: 10px; box-shadow: 0 0 3px rgba(0, 0, 0, .3); @@ -3194,7 +3194,7 @@ left: 0; margin: 0 8px; padding: 10px; - background: #fff; + background: @color-white; } } @@ -3386,7 +3386,7 @@ .rma-popup, .cms-popup { - background: #fff; + background: @color-white; box-shadow: 0 3px 6px rgba(0, 0, 0, .4); cursor: default; position: fixed; @@ -3410,7 +3410,7 @@ } .rma-popup .content { - background: #fff; + background: @color-white; border-bottom: 1px solid #ccc; max-height: 400px; overflow: auto; @@ -3467,7 +3467,7 @@ .grid .rma-popup .form-list tr, .grid tr.even .rma-popup .form-list tr, .grid tr.on-mouse .rma-popup .form-list tr { - background: #fff !important; + background: @color-white !important; } // @@ -3885,7 +3885,7 @@ .defaultSkin { table.mceLayout { td { - background: #fff; + background: @color-white; } } diff --git a/app/design/adminhtml/Magento/backend/web/mui/styles/_table.less b/app/design/adminhtml/Magento/backend/web/mui/styles/_table.less index c0aa5510c6e07..7565ee88714f6 100644 --- a/app/design/adminhtml/Magento/backend/web/mui/styles/_table.less +++ b/app/design/adminhtml/Magento/backend/web/mui/styles/_table.less @@ -360,7 +360,7 @@ td.col-type { position: relative; border-bottom-right-radius: 0; box-shadow: none; - background: #fff; + background: @color-white; &:after { position: absolute; @@ -369,7 +369,7 @@ td.col-type { right: 0; height: 2px; margin-top: -1px; - background: #fff; + background: @color-white; content: ''; z-index: 2; } diff --git a/lib/web/css/docs/source/_buttons.less b/lib/web/css/docs/source/_buttons.less index a071eed85ef9e..300991338fb71 100644 --- a/lib/web/css/docs/source/_buttons.less +++ b/lib/web/css/docs/source/_buttons.less @@ -524,10 +524,10 @@ button { } &.example-button-6 { .lib-button-s(); - color: #fff; + color: @color-white; &:hover, &.active { - color: #fff; + color: @color-white; } } } @@ -721,13 +721,13 @@ button { @_button-padding: @button__padding, @_button-gradient-color-start: #1979c3, @_button-gradient-color-end: #006bb4, - @_button-color: #fff, + @_button-color: @color-white, @_button-gradient-color-start-hover: #006bb4, @_button-gradient-color-end-hover: #1979c3, - @_button-color-hover: #fff, + @_button-color-hover: @color-white, @_button-gradient-color-start-active: #006bb4, @_button-gradient-color-end-active: #006bb4, - @_button-color-active: #fff, + @_button-color-active: @color-white, @_button-gradient: true, @_button-gradient-direction: vertical, @_button-border: @button-primary__border, diff --git a/lib/web/css/docs/source/_pages.less b/lib/web/css/docs/source/_pages.less index 0b94f84e54f47..6b01fa7549e92 100644 --- a/lib/web/css/docs/source/_pages.less +++ b/lib/web/css/docs/source/_pages.less @@ -852,22 +852,22 @@ .example-pages-3 { .lib-pager( @_pager-label-display: none, - @_pager-color: #fff, + @_pager-color: @color-white, @_pager-background: @link__color, - @_pager-color-visited: #fff, + @_pager-color-visited: @color-white, @_pager-background-visited: @link__visited__color, - @_pager-color-hover: #fff, + @_pager-color-hover: @color-white, @_pager-background-hover: @link__hover__color, - @_pager-color-active: #fff, + @_pager-color-active: @color-white, @_pager-background-active: @link__active__color, - @_pager-current-color: #fff, + @_pager-current-color: @color-white, @_pager-current-background: @link__visited__color, @_pager-action-background: @link__color, @_pager-action-background-visited: @link__visited__color, @_pager-action-background-hover: @link__hover__color, @_pager-action-background-active: @link__active__color, - @_pager-action-color: #fff, - @_pager-action-color-hover: #fff, - @_pager-action-color-active: #fff + @_pager-action-color: @color-white, + @_pager-action-color-hover: @color-white, + @_pager-action-color-active: @color-white ); } diff --git a/lib/web/css/docs/source/_tables.less b/lib/web/css/docs/source/_tables.less index 9d0df2350cee9..3f7f940187f50 100644 --- a/lib/web/css/docs/source/_tables.less +++ b/lib/web/css/docs/source/_tables.less @@ -677,7 +677,7 @@ .example-table-5 { .lib-table(); .lib-table-background-color( - @_table-background-color: #fff, + @_table-background-color: @color-white, @_table-head-background-color: #ccf, @_table-foot-background-color: #cff, @_table-td-background-color: #fcc, From 9892090c0093f410a5f86237028542c7f5543ebb Mon Sep 17 00:00:00 2001 From: Freek Vandeursen <freek@athleteshop.com> Date: Fri, 13 Jul 2018 14:54:54 +0200 Subject: [PATCH 0441/1171] Avoid undefined index warning when using uppercase reserved word --- .../Magento/Sniffs/NamingConventions/ReservedWordsSniff.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/framework/Magento/Sniffs/NamingConventions/ReservedWordsSniff.php b/dev/tests/static/framework/Magento/Sniffs/NamingConventions/ReservedWordsSniff.php index f41c235a6c0f8..e3cfdf532438c 100644 --- a/dev/tests/static/framework/Magento/Sniffs/NamingConventions/ReservedWordsSniff.php +++ b/dev/tests/static/framework/Magento/Sniffs/NamingConventions/ReservedWordsSniff.php @@ -64,7 +64,7 @@ protected function validateNamespace(File $sourceFile, $stackPtr) 'Cannot use "%s" in namespace as it is reserved since PHP %s', $stackPtr, 'Namespace', - [$namespacePart, $this->reservedWords[$namespacePart]] + [$namespacePart, $this->reservedWords[strtolower($namespacePart)]] ); } $stackPtr++; From ead7bfe0e6c3dca8171d2458e4ef5e0a7f27a9b3 Mon Sep 17 00:00:00 2001 From: Ronak Patel <11473750+ronak2ram@users.noreply.github.com> Date: Sat, 21 Jul 2018 01:27:21 +0530 Subject: [PATCH 0442/1171] Wrong namespace defined in compare.phtml --- .../view/frontend/templates/product/view/addto/compare.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/addto/compare.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/addto/compare.phtml index 16a5147e13458..adf0f44d0c831 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/addto/compare.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/addto/compare.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/** @var $block \Magento\Catalog\Block\Catalog\Product\View\Addto\Compare */ +/** @var $block \Magento\Catalog\Block\Product\View\Addto\Compare */ ?> <a href="#" data-post='<?= /* @escapeNotVerified */ $block->getPostDataParams() ?>' From 87962d6cba6f7bb8e925fb252f2a254cf405d1fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torben=20Ho=CC=88hn?= <torhoehn@gmail.com> Date: Tue, 17 Jul 2018 19:45:28 +0200 Subject: [PATCH 0443/1171] hide cookie notice instead of reloading site --- app/code/Magento/Cookie/view/frontend/web/js/notices.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cookie/view/frontend/web/js/notices.js b/app/code/Magento/Cookie/view/frontend/web/js/notices.js index 253950747ce14..f1f3754ea54b1 100644 --- a/app/code/Magento/Cookie/view/frontend/web/js/notices.js +++ b/app/code/Magento/Cookie/view/frontend/web/js/notices.js @@ -29,7 +29,7 @@ define([ }); if ($.mage.cookies.get(this.options.cookieName)) { - window.location.reload(); + this.element.hide(); } else { window.location.href = this.options.noCookiesUrl; } From 70f514b16ba931e7dcc33867f8a03fec40d73a7c Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Mon, 23 Jul 2018 08:26:42 -0500 Subject: [PATCH 0444/1171] Merge remote-tracking branch 'upstream/2.3-develop' into MAGETWO-68802-Clicking-Empty-Toggle # Conflicts: # dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml # dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml # dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml --- .../Section/AdminProductCustomizableOptionsSection.xml | 10 +++++----- .../Test/Mftf/Section/AdminProductFormSection.xml | 7 ++++--- .../SaveProductWithCustomOptionsSecondWebsiteTest.xml | 7 +++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml index d991580d58124..80ed5cb2d7e9d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml @@ -14,18 +14,18 @@ <element name="useDefaultOptionTitle" type="text" selector="[data-index='options'] tr.data-row [data-index='title'] [name^='options_use_default']"/> <element name="useDefaultOptionTitleByIndex" type="text" selector="[data-index='options'] [data-index='values'] tr[data-repeat-index='{{var1}}'] [name^='options_use_default']" parameterized="true"/> <element name="addOptionBtn" type="button" selector="button[data-index='button_add']"/> - <element name="fillOptionTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//span[text()='Option Title']/parent::label/parent::div//input[@class='admin__control-text']" parameterized="true"/> + <element name="fillOptionTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//label[text()='Option Title']/parent::span/parent::div//input[@class='admin__control-text']" parameterized="true"/> <element name="optionTitleInput" type="input" selector="input[name='product[options][0][title]']"/> <element name="optionTypeOpenDropDown" type="button" selector=".admin__dynamic-rows[data-index='options'] .action-select"/> <element name="optionTypeTextField" type="button" selector=".admin__dynamic-rows[data-index='options'] .action-menu._active li li"/> <element name="maxCharactersInput" type="input" selector="input[name='product[options][0][max_characters]']"/> - <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//span[text()='Option Type']/parent::label/parent::div//div[@data-role='selected-option']" parameterized="true"/> - <element name="checkDropDown" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//parent::label/parent::div//li[@class='admin__action-multiselect-menu-inner-item']//label[text()='Drop-down']" parameterized="true"/> + <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//label[text()='Option Type']/parent::span/parent::div//div[@data-role='selected-option']" parameterized="true"/> + <element name="checkDropDown" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//parent::label/parent::span/parent::div//li[@class='admin__action-multiselect-menu-inner-item']//label[text()='Drop-down']" parameterized="true"/> <element name="clickAddValue" type="button" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tfoot//button" parameterized="true"/> - <element name="fillOptionValueTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//span[text()='Title']/parent::label/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> + <element name="fillOptionValueTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//label[text()='Title']/parent::span/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> <element name="fillOptionValuePrice" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//span[text()='Price']/parent::label/parent::div//div[@class='admin__control-addon']/input" parameterized="true"/> - <element name="clickSelectPriceType" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody//tr[@data-repeat-index='{{var2}}']//span[text()='Price Type']/parent::label/parent::div//select" parameterized="true"/> + <element name="clickSelectPriceType" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody//tr[@data-repeat-index='{{var2}}']//label[text()='Price Type']/parent::span/parent::div//select" parameterized="true"/> <element name="checkboxUseDefaultTitle" type="checkbox" selector="//span[text()='Option Title']/parent::label/parent::div/div//input[@type='checkbox']"/> <element name="checkboxUseDefaultOption" type="checkbox" selector="//table[@data-index='values']//tbody//tr[@data-repeat-index='{{var1}}']//div[@class='admin__field-control']//input[@type='checkbox']" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index dd297bf301fa0..666cca97e4ecf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -9,10 +9,12 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminProductFormSection"> <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> - <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> + <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input"/> <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> + <element name="enableProductAttributeLabel" type="text" selector="//label[text()='Enable Product']"/> + <element name="enableProductAttributeLabelWrapper" type="text" selector="//label[text()='Enable Product']/parent::span"/> <element name="productStatus" type="checkbox" selector="input[name='product[status]']"/> <element name="enableProductLabel" type="checkbox" selector="input[name='product[status]']+label"/> <element name="productStatusUseDefault" type="checkbox" selector="input[name='use_default[status]']"/> @@ -39,11 +41,10 @@ <element name="visibility" type="select" selector="//select[@name='product[visibility]']"/> <element name="visibilityUseDefault" type="checkbox" selector="//input[@name='use_default[visibility]']"/> <element name="divByDataIndex" type="input" selector="div[data-index='{{var}}']" parameterized="true"/> - <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//span[text()='{{attributeLabel}}']" parameterized="true"/> + <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//label[text()='{{attributeLabel}}']" parameterized="true"/> </section> <section name="ProductInWebsitesSection"> <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> - <!--<element name="websites" type="checkbox" selector="input[name='product[website_ids][{{var1}}]']" parameterized="true"/>--> <element name="website" type="checkbox" selector="//label[contains(text(), '{{var1}}')]/parent::div//input[@type='checkbox']" parameterized="true"/> </section> <section name="ProductDesignSection"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml index 23d9a2a9e4cd0..36b02f124c101 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml @@ -18,6 +18,7 @@ <testCaseId value="MAGETWO-91436"/> <group value="product"/> </annotations> + <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--Create new website --> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> @@ -34,7 +35,6 @@ <!--Create Store view --> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <waitForElementVisible selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="waitForStoreViewBtn"/> <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> @@ -46,7 +46,7 @@ <conditionalClick selector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" dependentSelector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" visible="true" stepKey="AcceptNewStoreViewCreation"/> <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReolad"/> <see userInput="You saved the store view." stepKey="seeSaveMessage" /> - + </before> <after> <actionGroup ref="ResetWebUrlOptions" stepKey="resetUrlOption"/> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteTestWebsite"> @@ -65,8 +65,7 @@ <fillField userInput="{{_defaultProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> <fillField userInput="{{_defaultProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> - <!--<click selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" stepKey="openCustomOptionsSection"/>--> - <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses2"/> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses2"/> <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> <waitForPageLoad stepKey="waitAfterAddOption"/> <fillField selector="input[name='product[options][0][title]']" userInput="Radio Option" stepKey="fillOptionTitle"/> From 742df8664edebfbc0bdacdae506b18105e5c0903 Mon Sep 17 00:00:00 2001 From: Chirag Matholiya <chirag@wagento.com> Date: Mon, 23 Jul 2018 16:36:50 +0530 Subject: [PATCH 0445/1171] Fixes Black color coding standard. --- .../Magento_Backend/web/css/source/_module-old.less | 2 +- .../adminhtml/Magento/backend/web/css/styles-old.less | 8 ++++---- lib/web/css/docs/source/_buttons.less | 4 ++-- lib/web/css/docs/source/_messages.less | 4 ++-- lib/web/css/docs/source/_tables.less | 2 +- lib/web/css/source/lib/_resets.less | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/_module-old.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/_module-old.less index 6937855492263..2238824e01b42 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/_module-old.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/_module-old.less @@ -121,7 +121,7 @@ } .mage-suggest-selected > a { - color: #000; + color: @color-black; background: #F1FFEB; } } diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index b66fed0c0a09a..3e4580d014b34 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -487,8 +487,8 @@ } .notification-entry-dialog .action-close:hover { - color: #000; - border-bottom-color: #000; + color: @color-black; + border-bottom-color: @color-black; filter: none; } @@ -3798,7 +3798,7 @@ } .rule-param .label { - color: #000; + color: @color-black; float: none; text-align: left; padding: 0; @@ -5229,7 +5229,7 @@ @media print { * { background: transparent !important; - color: #000 !important; + color: @color-black !important; box-shadow: none !important; text-shadow: none !important; filter: none !important; diff --git a/lib/web/css/docs/source/_buttons.less b/lib/web/css/docs/source/_buttons.less index a071eed85ef9e..bf29956025866 100644 --- a/lib/web/css/docs/source/_buttons.less +++ b/lib/web/css/docs/source/_buttons.less @@ -48,10 +48,10 @@ button { &.example-button-3 { .lib-button-s(); border-radius: 0; - color: #000; + color: @color-black; &:hover, &.active { - color: #000; + color: @color-black; } } } diff --git a/lib/web/css/docs/source/_messages.less b/lib/web/css/docs/source/_messages.less index 45d4538d14aff..91b3f280880ca 100644 --- a/lib/web/css/docs/source/_messages.less +++ b/lib/web/css/docs/source/_messages.less @@ -176,7 +176,7 @@ // ``` // -@message-custom__color: #000; +@message-custom__color: @color-black; @message-custom__background: #fc0; @message-custom__border-color: orange; @@ -185,7 +185,7 @@ @message-custom-link__color-active: darken(@message-custom-link__color, 30%); @message-custom-icon: @icon-settings; -@message-custom-icon__color-lateral: #000; +@message-custom-icon__color-lateral: @color-black; @message-custom-icon__background: #green; @message-custom-icon__top: 15px; @message-custom-icon__right: false; diff --git a/lib/web/css/docs/source/_tables.less b/lib/web/css/docs/source/_tables.less index 9d0df2350cee9..00d13385df02f 100644 --- a/lib/web/css/docs/source/_tables.less +++ b/lib/web/css/docs/source/_tables.less @@ -1253,7 +1253,7 @@ .lib-table(); .lib-table-striped( @_stripped-background-color: #ffc, - @_stripped-color: #000, + @_stripped-color: @color-black, @_stripped-direction: horizontal, @_stripped-highlight: even ); diff --git a/lib/web/css/source/lib/_resets.less b/lib/web/css/source/lib/_resets.less index 36c7e6bf4ded3..8228a07ef92ab 100644 --- a/lib/web/css/source/lib/_resets.less +++ b/lib/web/css/source/lib/_resets.less @@ -185,7 +185,7 @@ mark { background: #ff0; - color: #000; + color: @color-black; } small { @@ -465,13 +465,13 @@ ins { background-color: #ff9; - color: #000; + color: @color-black; text-decoration: none; } mark { background-color: #ff9; - color: #000; + color: @color-black; font-style: italic; font-weight: bold; } From a5614f9b2d705fbadd18cd13c6639b9823de1180 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Mon, 23 Jul 2018 10:42:41 -0500 Subject: [PATCH 0446/1171] MC-1416: Editing Text Content Block from the Stage with TinyMCE 4 turned on Add wysiwyg instantiation/adapter segregation and event bus support --- .../plugins/magentowidget/editor_plugin.js | 3 +- .../mage/adminhtml/wysiwyg/tiny_mce/setup.js | 25 +++++++++++++-- .../wysiwyg/tiny_mce/tinymce4Adapter.js | 31 ++++++++++++++----- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js index 73fd8658854f2..18d71aad2071a 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js @@ -7,7 +7,8 @@ /* eslint-disable strict */ define([ 'wysiwygAdapter', - 'mage/adminhtml/events' + 'mage/adminhtml/events', + 'mage/adminhtml/wysiwyg/widget' ], function (wysiwyg, varienGlobalEvents) { return function (config) { tinymce.create('tinymce.plugins.magentowidget', { diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js index d992947dca6e3..112819c3f077d 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js @@ -8,12 +8,14 @@ define([ 'jquery', 'underscore', 'wysiwygAdapter', + 'module', 'mage/translate', 'prototype', 'mage/adminhtml/events', 'mage/adminhtml/browser' -], function (jQuery, _, wysiwygAdapter) { - var wysiwygSetup = Class.create({ +], function (jQuery, _, wysiwygAdapter, module) { + var baseConfig = module.config().config || {}, + wysiwygSetup = Class.create({ wysiwygInstance: null }); @@ -27,7 +29,10 @@ define([ var WysiwygInstancePrototype = new wysiwygAdapter.getAdapterPrototype(); _.bindAll(this, 'openFileBrowser'); + + config = Object.assign({}, baseConfig, config || {}); this.wysiwygInstance = new WysiwygInstancePrototype(htmlId, config); + this.wysiwygInstance.eventBus = this.eventBus = new window.varienEvents(); }, /** @@ -66,7 +71,23 @@ define([ */ updateContent: function (content) { return this.wysiwygInstance.encodeContent(content); + }, + + /** + * @return {String} + */ + getContent: function () { + return this.wysiwygInstance.getContent(); + }, + + /** + * @param {String} content + */ + setContent: function (content) { + this.wysiwygInstance.setContent(content); } }; window.wysiwygSetup = wysiwygSetup; + + return wysiwygSetup; }); diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index 8f20243383db8..e5fffe1b4f77e 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -72,10 +72,6 @@ define([ this.turnOff(); - if (typeof mode === 'undefined') { - mode = this.mode; - } - if (this.config.plugins) { this.config.plugins.forEach(function (plugin) { var deferred; @@ -105,7 +101,10 @@ define([ } settings = this.getSettings(); - settings.mode = mode; + + if (mode === 'inline') { + settings.inline = true; + } jQuery.when.apply(jQuery, deferreds).done(function () { tinyMCE4.init(settings); @@ -165,10 +164,11 @@ define([ * @return {Object} */ getSettings: function () { - var settings; + var settings, + eventBus = this.eventBus; settings = { - selector: 'textarea#' + this.getId(), + selector: '#' + this.getId(), theme: 'modern', 'entity_encoding': 'raw', 'convert_urls': false, @@ -187,22 +187,27 @@ define([ editor.on('BeforeSetContent', function (evt) { varienGlobalEvents.fireEvent('tinymceBeforeSetContent', evt); + eventBus.fireEvent('tinymceBeforeSetContent'); }); editor.on('SaveContent', function (evt) { varienGlobalEvents.fireEvent('tinymceSaveContent', evt); + eventBus.fireEvent('tinymceSaveContent'); }); editor.on('paste', function (evt) { varienGlobalEvents.fireEvent('tinymcePaste', evt); + eventBus.fireEvent('tinymcePaste'); }); editor.on('PostProcess', function (evt) { varienGlobalEvents.fireEvent('tinymceSaveContent', evt); + eventBus.fireEvent('tinymceSaveContent'); }); editor.on('undo', function (evt) { varienGlobalEvents.fireEvent('tinymceUndo', evt); + eventBus.fireEvent('tinymceUndo'); }); /** @@ -210,6 +215,7 @@ define([ */ onChange = function (evt) { varienGlobalEvents.fireEvent('tinymceChange', evt); + eventBus.fireEvent('tinymceChange'); }; editor.on('Change', onChange); @@ -221,6 +227,7 @@ define([ editor.on('init', function (args) { varienGlobalEvents.fireEvent('wysiwygEditorInitialized', args.target); + eventBus.fireEvent('wysiwygEditorInitialized'); }); } }; @@ -303,6 +310,14 @@ define([ this.activeEditor().execCommand('mceInsertContent', typeof ui !== 'undefined' ? ui : false, content); }, + /** + * TODO + * @param content + */ + setContent: function (content) { + this.get(this.getId()).execCommand('mceSetContent', false, content); + }, + /** * Set caret location in WYSIWYG editor. * @@ -491,7 +506,7 @@ define([ * @return {String} */ getContent: function (id) { - return id ? this.get(id).getContent() : this.activeEditor().getContent(); + return id ? this.get(id).getContent() : this.get(this.getId()).getContent(); }, /** From cdaf11a8b173fb2c3647e95dcb2d1e9e1dde8f9a Mon Sep 17 00:00:00 2001 From: Anshu Mishra <mishra.anshu1710@gmail.com> Date: Mon, 16 Jul 2018 13:13:57 +0530 Subject: [PATCH 0447/1171] Remove direct use of object manager --- .../Adminhtml/Subscriber/MassDelete.php | 30 ++++++++++++++++--- .../Adminhtml/Subscriber/MassUnsubscribe.php | 30 ++++++++++++++++--- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassDelete.php b/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassDelete.php index 0dd2e5c4b387f..7f02e4ea13445 100644 --- a/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassDelete.php +++ b/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassDelete.php @@ -6,8 +6,32 @@ */ namespace Magento\Newsletter\Controller\Adminhtml\Subscriber; -class MassDelete extends \Magento\Newsletter\Controller\Adminhtml\Subscriber +use Magento\Newsletter\Controller\Adminhtml\Subscriber; +use Magento\Backend\App\Action\Context; +use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Framework\App\ObjectManager; + +class MassDelete extends Subscriber { + /** + * @var SubscriberFactory + */ + private $subscriberFactory; + + /** + * @param Context $context + * @param FileFactory $fileFactory + */ + public function __construct( + Context $context, + FileFactory $fileFactory, + SubscriberFactory $subscriberFactory = null + ) { + $this->subscriberFactory = $subscriberFactory ?: ObjectManager::getInstance()->get(SubscriberFactory::class); + parent::__construct($context, $fileFactory); + } + /** * Delete one or more subscribers action * @@ -21,9 +45,7 @@ public function execute() } else { try { foreach ($subscribersIds as $subscriberId) { - $subscriber = $this->_objectManager->create( - \Magento\Newsletter\Model\Subscriber::class - )->load( + $subscriber = $this->subscriberFactory->create()->load( $subscriberId ); $subscriber->delete(); diff --git a/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassUnsubscribe.php b/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassUnsubscribe.php index f294d23f46ece..bba9dfe2e248b 100644 --- a/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassUnsubscribe.php +++ b/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassUnsubscribe.php @@ -6,8 +6,32 @@ */ namespace Magento\Newsletter\Controller\Adminhtml\Subscriber; -class MassUnsubscribe extends \Magento\Newsletter\Controller\Adminhtml\Subscriber +use Magento\Newsletter\Controller\Adminhtml\Subscriber; +use Magento\Backend\App\Action\Context; +use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Framework\App\ObjectManager; + +class MassUnsubscribe extends Subscriber { + /** + * @var SubscriberFactory + */ + private $subscriberFactory; + + /** + * @param Context $context + * @param FileFactory $fileFactory + */ + public function __construct( + Context $context, + FileFactory $fileFactory, + SubscriberFactory $subscriberFactory = null + ) { + $this->subscriberFactory = $subscriberFactory ?: ObjectManager::getInstance()->get(SubscriberFactory::class); + parent::__construct($context, $fileFactory); + } + /** * Unsubscribe one or more subscribers action * @@ -21,9 +45,7 @@ public function execute() } else { try { foreach ($subscribersIds as $subscriberId) { - $subscriber = $this->_objectManager->create( - \Magento\Newsletter\Model\Subscriber::class - )->load( + $subscriber = $this->subscriberFactory->create()->load( $subscriberId ); $subscriber->unsubscribe(); From 3109186bab5b53e65bb3ea9867bc797fb80009af Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Fri, 20 Jul 2018 10:28:33 +0300 Subject: [PATCH 0448/1171] DocBlock fix --- .../Controller/Adminhtml/Subscriber/MassUnsubscribe.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassUnsubscribe.php b/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassUnsubscribe.php index bba9dfe2e248b..b61494f795905 100644 --- a/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassUnsubscribe.php +++ b/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassUnsubscribe.php @@ -22,6 +22,7 @@ class MassUnsubscribe extends Subscriber /** * @param Context $context * @param FileFactory $fileFactory + * @param SubscriberFactory $subscriberFactory */ public function __construct( Context $context, From 252519c497fbd94a14689b1877f345efd3c3f736 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Mon, 23 Jul 2018 13:10:44 -0500 Subject: [PATCH 0449/1171] MC-3078: Admin can create product attribute with text swatch - Fix code review feedback --- .../Mftf/Section/AdminManageSwatchSection.xml | 3 +++ .../Mftf/Test/AdminCreateTextSwatchTest.xml | 22 +++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml index 176b266a450f1..39db9f92837e3 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml @@ -14,6 +14,9 @@ <element name="nthSwatch" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_window" parameterized="true"/> <element name="addSwatchText" type="button" selector="#add_new_swatch_text_option_button"/> <element name="swatchTextByIndex" type="input" selector="input[name='swatchtext[value][option_{{index}}][0]']" parameterized="true"/> + <element name="nthSwatchText" type="input" selector="#swatch-text-options-panel table tbody tr:nth-of-type({{var}}) td:nth-of-type(3) input" parameterized="true"/> + <element name="nthSwatchAdminDescription" type="input" selector="#swatch-text-options-panel table tbody tr:nth-of-type({{var}}) td:nth-of-type(4) input" parameterized="true"/> + <!-- Selector for Admin Description input where the index is zero-based --> <element name="swatchAdminDescriptionByIndex" type="input" selector="input[name='optiontext[value][option_{{index}}][0]']" parameterized="true"/> <element name="nthChooseColor" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_row_name.colorpicker_handler" parameterized="true"/> <element name="nthDelete" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) button.delete-option" parameterized="true"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml index 7700ef05450f3..0c26c6a8174af 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml @@ -41,19 +41,27 @@ <fillField selector="{{AdminManageSwatchSection.swatchAdminDescriptionByIndex('2')}}" userInput="Something blue." stepKey="fillDescription2"/> <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> <selectOption selector="{{AttributePropertiesSection.Scope}}" userInput="1" stepKey="selectGlobalScope"/> - <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSave"/> + <!-- Save and verify --> + <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSave"/> + <seeInField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="seeDefaultLabel"/> + <seeInField selector="{{AdminManageSwatchSection.nthSwatchText('1')}}" userInput="red" stepKey="seeSwatch0"/> + <seeInField selector="{{AdminManageSwatchSection.nthSwatchAdminDescription('1')}}" userInput="Something red." stepKey="seeDescription0"/> + <seeInField selector="{{AdminManageSwatchSection.nthSwatchText('2')}}" userInput="green" stepKey="seeSwatch1"/> + <seeInField selector="{{AdminManageSwatchSection.nthSwatchAdminDescription('2')}}" userInput="Something green." stepKey="seeDescription1"/> + <seeInField selector="{{AdminManageSwatchSection.nthSwatchText('3')}}" userInput="blue" stepKey="seeSwatch2"/> + <seeInField selector="{{AdminManageSwatchSection.nthSwatchAdminDescription('3')}}" userInput="Something blue." stepKey="seeDescription2"/> <!-- Create a configurable product to verify the storefront with --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad time="30" stepKey="waitForProductGrid"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> - <click selector="{{AdminProductGridActionSection.addConfigurableProduct}}" stepKey="clickOnAddConfigurableProduct"/> - <fillField userInput="{{_defaultProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> - <fillField userInput="{{_defaultProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> - <fillField userInput="{{_defaultProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> - <fillField userInput="{{_defaultProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> <fillField userInput="{{_defaultProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> <!-- Create configurations based off the Text Swatch we created earlier --> <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickCreateConfigurations"/> From 447a91729f10533d680f44f717c28a16f8ca7eab Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi <volodymyr@hryvinskyi.com> Date: Tue, 17 Jul 2018 23:34:38 +0300 Subject: [PATCH 0450/1171] Add Clean Code --- .../Initialization/Helper/ProductLinks/Plugin/Grouped.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/GroupedProduct/Model/Product/Initialization/Helper/ProductLinks/Plugin/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Initialization/Helper/ProductLinks/Plugin/Grouped.php index 6cdcfd248e798..7b9523015c882 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Initialization/Helper/ProductLinks/Plugin/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Initialization/Helper/ProductLinks/Plugin/Grouped.php @@ -8,6 +8,7 @@ use Magento\Catalog\Api\Data\ProductLinkExtensionFactory; use Magento\Catalog\Api\Data\ProductLinkInterfaceFactory; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\GroupedProduct\Model\Product\Type\Grouped as TypeGrouped; /** @@ -60,6 +61,9 @@ public function __construct( * @param array $links * * @return \Magento\Catalog\Model\Product + * + * @throws NoSuchEntityException + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -70,7 +74,7 @@ public function beforeInitializeLinks( array $links ) { if ($product->getTypeId() === TypeGrouped::TYPE_CODE && !$product->getGroupedReadonly()) { - $links = (isset($links[self::TYPE_NAME])) ? $links[self::TYPE_NAME] : $product->getGroupedLinkData(); + $links = $links[self::TYPE_NAME] ?? $product->getGroupedLinkData(); if (!is_array($links)) { $links = []; } From c393a404e3c3b2e009e6596d67c78f3017cf9a28 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi <volodymyr@hryvinskyi.com> Date: Wed, 18 Jul 2018 21:06:17 +0300 Subject: [PATCH 0451/1171] Update construct and phpdoc --- .../Magento/CatalogRule/Model/Rule/Job.php | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/CatalogRule/Model/Rule/Job.php b/app/code/Magento/CatalogRule/Model/Rule/Job.php index 63ff98d4ca5b7..709869940e1e6 100644 --- a/app/code/Magento/CatalogRule/Model/Rule/Job.php +++ b/app/code/Magento/CatalogRule/Model/Rule/Job.php @@ -8,6 +8,10 @@ * See COPYING.txt for license details. */ +namespace Magento\CatalogRule\Model\Rule; + +use Magento\CatalogRule\Model\Indexer\Rule\RuleProductProcessor; + /** * Catalog Rule job model * @@ -18,13 +22,8 @@ * @method bool hasSuccess() * @method bool hasError() * - * @author Magento Core Team <core@magentocommerce.com> - */ -namespace Magento\CatalogRule\Model\Rule; - -use Magento\CatalogRule\Model\Indexer\Rule\RuleProductProcessor; - -/** + * @author Magento Core Team <core@magentocommerce.com> + * * @api * @since 100.0.2 */ @@ -35,14 +34,18 @@ class Job extends \Magento\Framework\DataObject */ protected $ruleProcessor; - /** - * Basic object initialization - * - * @param RuleProductProcessor $ruleProcessor - */ - public function __construct(RuleProductProcessor $ruleProcessor) - { - $this->ruleProcessor = $ruleProcessor; + /** + * Basic object initialization + * + * @param RuleProductProcessor $ruleProcessor + * @param array $data + */ + public function __construct( + RuleProductProcessor $ruleProcessor, + array $data = [] + ) { + $this->ruleProcessor = $ruleProcessor; + parent::__construct($data); } /** From 18bfebe75f0feda24935a24e6668709ae086c3e8 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi <volodymyr@hryvinskyi.com> Date: Thu, 19 Jul 2018 12:24:29 +0300 Subject: [PATCH 0452/1171] replace tab space --- .../Magento/CatalogRule/Model/Rule/Job.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/CatalogRule/Model/Rule/Job.php b/app/code/Magento/CatalogRule/Model/Rule/Job.php index 709869940e1e6..f73fd60d740ff 100644 --- a/app/code/Magento/CatalogRule/Model/Rule/Job.php +++ b/app/code/Magento/CatalogRule/Model/Rule/Job.php @@ -34,18 +34,18 @@ class Job extends \Magento\Framework\DataObject */ protected $ruleProcessor; - /** - * Basic object initialization - * - * @param RuleProductProcessor $ruleProcessor - * @param array $data - */ + /** + * Basic object initialization + * + * @param RuleProductProcessor $ruleProcessor + * @param array $data + */ public function __construct( RuleProductProcessor $ruleProcessor, - array $data = [] + array $data = [] ) { - $this->ruleProcessor = $ruleProcessor; - parent::__construct($data); + $this->ruleProcessor = $ruleProcessor; + parent::__construct($data); } /** From 6c9203ffac9bf850fba24fc68f43be5000373a67 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Fri, 20 Jul 2018 12:46:25 +0300 Subject: [PATCH 0453/1171] Fixed coding standards issue --- app/code/Magento/CatalogRule/Model/Rule/Job.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogRule/Model/Rule/Job.php b/app/code/Magento/CatalogRule/Model/Rule/Job.php index f73fd60d740ff..71734eb3c5d46 100644 --- a/app/code/Magento/CatalogRule/Model/Rule/Job.php +++ b/app/code/Magento/CatalogRule/Model/Rule/Job.php @@ -41,8 +41,8 @@ class Job extends \Magento\Framework\DataObject * @param array $data */ public function __construct( - RuleProductProcessor $ruleProcessor, - array $data = [] + RuleProductProcessor $ruleProcessor, + array $data = [] ) { $this->ruleProcessor = $ruleProcessor; parent::__construct($data); From c65b876819eb5b49a51f7d0c74c8a9757065047a Mon Sep 17 00:00:00 2001 From: Sean Breeden <sean-wcb@users.noreply.github.com> Date: Tue, 17 Jul 2018 10:01:54 -0400 Subject: [PATCH 0454/1171] Update nginx.config.sample to exclude php5-fpm M2.2.x is not compatible with php5.x so there's no reason to include it as an option in the nginx.config.sample --- nginx.conf.sample | 1 - 1 file changed, 1 deletion(-) diff --git a/nginx.conf.sample b/nginx.conf.sample index 1e20a51a511d3..881adb84c56e3 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -3,7 +3,6 @@ # # use tcp connection # # server 127.0.0.1:9000; # # or socket -# server unix:/var/run/php5-fpm.sock; # server unix:/var/run/php/php7.0-fpm.sock; # } # server { From 540298f5ee2d18991d665713e76bd037ccc53691 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Tue, 17 Jul 2018 16:30:40 -0300 Subject: [PATCH 0455/1171] Fixing annotations for some methods. --- .../Framework/Acl/AclResource/Config/Converter/Dom.php | 1 + .../Framework/Acl/AclResource/Config/SchemaLocator.php | 4 ++++ lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php | 2 ++ 3 files changed, 7 insertions(+) diff --git a/lib/internal/Magento/Framework/Acl/AclResource/Config/Converter/Dom.php b/lib/internal/Magento/Framework/Acl/AclResource/Config/Converter/Dom.php index 1b4e70be3644f..68762a8a6c046 100644 --- a/lib/internal/Magento/Framework/Acl/AclResource/Config/Converter/Dom.php +++ b/lib/internal/Magento/Framework/Acl/AclResource/Config/Converter/Dom.php @@ -12,6 +12,7 @@ class Dom implements \Magento\Framework\Config\ConverterInterface * * @param \DOMDocument $source * @return array + * @throws \Exception */ public function convert($source) { diff --git a/lib/internal/Magento/Framework/Acl/AclResource/Config/SchemaLocator.php b/lib/internal/Magento/Framework/Acl/AclResource/Config/SchemaLocator.php index 105f27cb330fc..3cc1087228174 100644 --- a/lib/internal/Magento/Framework/Acl/AclResource/Config/SchemaLocator.php +++ b/lib/internal/Magento/Framework/Acl/AclResource/Config/SchemaLocator.php @@ -27,6 +27,8 @@ public function __construct(\Magento\Framework\Config\Dom\UrnResolver $urnResolv /** * {@inheritdoc} + * + * @throws \Magento\Framework\Exception\NotFoundException */ public function getSchema() { @@ -35,6 +37,8 @@ public function getSchema() /** * {@inheritdoc} + * + * @throws \Magento\Framework\Exception\NotFoundException */ public function getPerFileSchema() { diff --git a/lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php b/lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php index c5b75d4ccd3b9..8a4ad39d78570 100644 --- a/lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php +++ b/lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php @@ -43,6 +43,7 @@ public function __construct(ProviderInterface $resourceProvider, AclResourceFact * * @param Acl $acl * @return void + * @throws \Zend_Acl_Exception */ public function populateAcl(Acl $acl) { @@ -57,6 +58,7 @@ public function populateAcl(Acl $acl) * @param AclResource $parent * @return void * @throws \InvalidArgumentException + * @throws \Zend_Acl_Exception */ protected function _addResourceTree(Acl $acl, array $resources, AclResource $parent = null) { From 8d738f0b42a9464821fcd3910dd9fd2592591a46 Mon Sep 17 00:00:00 2001 From: Valerij Ivashchenko <likemusic@yandex.ru> Date: Tue, 17 Jul 2018 16:35:29 +0300 Subject: [PATCH 0456/1171] Remove duplicated string. --- app/code/Magento/ProductVideo/i18n/en_US.csv | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/ProductVideo/i18n/en_US.csv b/app/code/Magento/ProductVideo/i18n/en_US.csv index 3277843424bcf..2d226c6daefa3 100644 --- a/app/code/Magento/ProductVideo/i18n/en_US.csv +++ b/app/code/Magento/ProductVideo/i18n/en_US.csv @@ -40,4 +40,3 @@ Delete,Delete "Autostart base video","Autostart base video" "Show related video","Show related video" "Auto restart video","Auto restart video" -"Images And Videos","Images And Videos" From c6e9ca20c5dc1c76e19f175c1f46159e9c672e1e Mon Sep 17 00:00:00 2001 From: Ethan3600 <Ethan3600@gmail.com> Date: Sun, 15 Jul 2018 15:27:43 -0400 Subject: [PATCH 0457/1171] Fix namespace --- lib/internal/Magento/Framework/App/Bootstrap.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/Bootstrap.php b/lib/internal/Magento/Framework/App/Bootstrap.php index 4468954d189da..97f809cacbe89 100644 --- a/lib/internal/Magento/Framework/App/Bootstrap.php +++ b/lib/internal/Magento/Framework/App/Bootstrap.php @@ -12,6 +12,7 @@ use Magento\Framework\Autoload\Populator; use Magento\Framework\Config\File\ConfigFilePool; use Magento\Framework\Filesystem\DriverPool; +use Psr\Log\LoggerInterface; /** * A bootstrap of Magento application @@ -423,7 +424,7 @@ protected function terminate(\Exception $e) if (!$this->objectManager) { throw new \DomainException(); } - $this->objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); + $this->objectManager->get(LoggerInterface::class)->critical($e); } catch (\Exception $e) { $message .= "Could not write error message to log. Please use developer mode to see the message.\n"; } From 66873e32b00bc2cbf048f7328eddaf1036dd1961 Mon Sep 17 00:00:00 2001 From: Ethan3600 <Ethan3600@gmail.com> Date: Sun, 15 Jul 2018 15:28:46 -0400 Subject: [PATCH 0458/1171] Log when maintenance mode is enabled Let's not play guessing games why we're getting a 500 errors... --- lib/internal/Magento/Framework/App/Bootstrap.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/internal/Magento/Framework/App/Bootstrap.php b/lib/internal/Magento/Framework/App/Bootstrap.php index 97f809cacbe89..a5cfd46b7bf3e 100644 --- a/lib/internal/Magento/Framework/App/Bootstrap.php +++ b/lib/internal/Magento/Framework/App/Bootstrap.php @@ -107,6 +107,13 @@ class Bootstrap */ private $factory; + /** + * Logger + * + * @var LoggerInterface + */ + private $logger; + /** * Static method so that client code does not have to create Object Manager Factory every time Bootstrap is called * @@ -207,6 +214,7 @@ public function __construct(ObjectManagerFactory $factory, $rootDir, array $init $this->rootDir = $rootDir; $this->server = $initParams; $this->objectManager = $this->factory->create($this->server); + $this->logger = $this->objectManager->create(LoggerInterface::class); } /** @@ -259,6 +267,7 @@ public function run(AppInterface $application) \Magento\Framework\Profiler::stop('magento'); } catch (\Exception $e) { \Magento\Framework\Profiler::stop('magento'); + $this->logger->error($e->getMessage()); if (!$application->catchException($this, $e)) { throw $e; } From 53fc9648275f80a8dc19731ecfd04927e2fabbbb Mon Sep 17 00:00:00 2001 From: Ethan3600 <Ethan3600@gmail.com> Date: Sun, 15 Jul 2018 18:38:47 -0400 Subject: [PATCH 0459/1171] Use object manager to pass unit tests --- lib/internal/Magento/Framework/App/Bootstrap.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Bootstrap.php b/lib/internal/Magento/Framework/App/Bootstrap.php index a5cfd46b7bf3e..5f126b38a0033 100644 --- a/lib/internal/Magento/Framework/App/Bootstrap.php +++ b/lib/internal/Magento/Framework/App/Bootstrap.php @@ -107,13 +107,6 @@ class Bootstrap */ private $factory; - /** - * Logger - * - * @var LoggerInterface - */ - private $logger; - /** * Static method so that client code does not have to create Object Manager Factory every time Bootstrap is called * @@ -214,7 +207,6 @@ public function __construct(ObjectManagerFactory $factory, $rootDir, array $init $this->rootDir = $rootDir; $this->server = $initParams; $this->objectManager = $this->factory->create($this->server); - $this->logger = $this->objectManager->create(LoggerInterface::class); } /** @@ -267,7 +259,7 @@ public function run(AppInterface $application) \Magento\Framework\Profiler::stop('magento'); } catch (\Exception $e) { \Magento\Framework\Profiler::stop('magento'); - $this->logger->error($e->getMessage()); + $this->objectManager->get(LoggerInterface::class)->error($e); if (!$application->catchException($this, $e)) { throw $e; } From c8f3543b1cf3240a32bc86414d6e8123599d3c91 Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <strpwebstudio@gmail.com> Date: Thu, 19 Jul 2018 10:54:00 +0300 Subject: [PATCH 0460/1171] Log when Magento is in maintenance mode #16840 --- lib/internal/Magento/Framework/App/Bootstrap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/Bootstrap.php b/lib/internal/Magento/Framework/App/Bootstrap.php index 5f126b38a0033..904c41ab9ec33 100644 --- a/lib/internal/Magento/Framework/App/Bootstrap.php +++ b/lib/internal/Magento/Framework/App/Bootstrap.php @@ -259,7 +259,7 @@ public function run(AppInterface $application) \Magento\Framework\Profiler::stop('magento'); } catch (\Exception $e) { \Magento\Framework\Profiler::stop('magento'); - $this->objectManager->get(LoggerInterface::class)->error($e); + $this->objectManager->get(LoggerInterface::class)->error($e->getMessage()); if (!$application->catchException($this, $e)) { throw $e; } From 77aa362fd639f74436005ed2da94a78e6a078763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Fri, 6 Jul 2018 10:58:33 +0300 Subject: [PATCH 0461/1171] removed _responsive.less import --- lib/web/mage/gallery/gallery.less | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/web/mage/gallery/gallery.less b/lib/web/mage/gallery/gallery.less index 24a5a2b4033d3..a45e34b6ba626 100644 --- a/lib/web/mage/gallery/gallery.less +++ b/lib/web/mage/gallery/gallery.less @@ -7,7 +7,6 @@ @import '../../css/source/lib/_lib.less'; // Global lib @import '../../css/source/_theme.less'; // Theme overrides @import '../../css/source/_variables.less'; // Local theme variables -@import '../../css/source/lib/_responsive.less'; @import 'module/_mixins.less'; //Mixins in gallery @import 'module/_extends.less'; @import 'module/_focus.less'; From 1db38b0817f45b5062aaaa881ab7041aa2ea927f Mon Sep 17 00:00:00 2001 From: Kacper Chara <kchara@divante.pl> Date: Sat, 19 May 2018 14:12:27 +0200 Subject: [PATCH 0462/1171] Fix for Magnifier in inside mode --- app/design/frontend/Magento/luma/etc/view.xml | 1 + lib/web/mage/gallery/gallery.less | 18 +- lib/web/magnifier/magnifier.js | 353 +++++++++--------- 3 files changed, 189 insertions(+), 183 deletions(-) diff --git a/app/design/frontend/Magento/luma/etc/view.xml b/app/design/frontend/Magento/luma/etc/view.xml index 41d565789c25b..55d43272caad9 100644 --- a/app/design/frontend/Magento/luma/etc/view.xml +++ b/app/design/frontend/Magento/luma/etc/view.xml @@ -232,6 +232,7 @@ <var name="height"></var> <!-- Height of magnifier block --> <var name="eventType">hover</var> <!-- Action that atcivates zoom (hover/click) --> <var name="enabled">false</var> <!-- Turn on/off magnifier (true/false) --> + <var name="mode">outside</var> <!-- Zoom type (outside/inside) --> </var> <var name="breakpoints"> diff --git a/lib/web/mage/gallery/gallery.less b/lib/web/mage/gallery/gallery.less index 24a5a2b4033d3..8f4dd78979018 100644 --- a/lib/web/mage/gallery/gallery.less +++ b/lib/web/mage/gallery/gallery.less @@ -736,24 +736,30 @@ text-align: center; top: 0; z-index: @z-index-10; + overflow: hidden; + + .magnifier-large { + width: auto; + height: auto; + max-height: none; + max-width: none; + border: none; + position: absolute; + z-index: @z-index-1; + } } .magnifier-loader-text { margin-top: 10px; } -.magnifier-large { - position: absolute; - width: 32%; - z-index: @z-index-1; -} - .magnifier-preview { bottom: 0; left: 58%; overflow: hidden; padding: 0; position: absolute; + z-index: 2; top: 215px; &:not(.hidden) { background-color: @color-white; diff --git a/lib/web/magnifier/magnifier.js b/lib/web/magnifier/magnifier.js index c475364368922..150c8adf0b22b 100644 --- a/lib/web/magnifier/magnifier.js +++ b/lib/web/magnifier/magnifier.js @@ -9,101 +9,100 @@ var magnify = new Magnify($(this), options); - /*events must be tracked here*/ + /* events must be tracked here */ /** * Return that from _init function * */ return magnify; - }; function Magnify(element, options) { - var gOptions = options || {}, + var customUserOptions = options || {}, $box = $(element), $thumb, that = this, - largeWrapper = options.largeWrapper || '.magnifier-preview', - $largeWrapper = $(largeWrapper); + largeWrapper = options.largeWrapper || '.magnifier-preview', + $magnifierPreview = $(largeWrapper); curThumb = null, - currentOpts = { - x: 0, - y: 0, - w: 0, - h: 0, - lensW: 0, - lensH: 0, - lensBgX: 0, - lensBgY: 0, - largeW: 0, - largeH: 0, - largeL: 0, - largeT: 0, - zoom: 2, - zoomMin: 1.1, - zoomMax: 5, - mode: 'outside', - eventType: 'click', - status: 0, - zoomAttached: false, - zoomable: gOptions.zoomable !== undefined ? - gOptions.zoomable - : false, - onthumbenter: gOptions.onthumbenter !== undefined ? - gOptions.onthumbenter - : null, - onthumbmove: gOptions.onthumbmove !== undefined ? - gOptions.onthumbmove - : null, - onthumbleave: gOptions.onthumbleave !== undefined ? - gOptions.onthumbleave - : null, - onzoom: gOptions.onzoom !== undefined ? - gOptions.onzoom - : null - }, - pos = { - t: 0, - l: 0, - x: 0, - y: 0 - }, - gId = 0, - status = 0, - curIdx = '', - curLens = null, - curLarge = null, - lensbg = gOptions.bg !== undefined ? gOptions.lensbg : true, - gZoom = gOptions.zoom !== undefined ? - gOptions.zoom - : currentOpts.zoom, - gZoomMin = gOptions.zoomMin !== undefined ? - gOptions.zoomMin - : currentOpts.zoomMin, - gZoomMax = gOptions.zoomMax !== undefined ? - gOptions.zoomMax - : currentOpts.zoomMax, - gMode = gOptions.mode || currentOpts.mode, - gEventType = gOptions.eventType || currentOpts.eventType, - data = {}, - inBounds = false, - isOverThumb = false, - rate = 1, - paddingX = 0, - paddingY = 0, - enabled = true, - showWrapper = true; + magnifierOptions = { + x: 0, + y: 0, + w: 0, + h: 0, + lensW: 0, + lensH: 0, + lensBgX: 0, + lensBgY: 0, + largeW: 0, + largeH: 0, + largeL: 0, + largeT: 0, + zoom: 2, + zoomMin: 1.1, + zoomMax: 5, + mode: 'outside', + eventType: 'click', + status: 0, + zoomAttached: false, + zoomable: customUserOptions.zoomable !== undefined ? + customUserOptions.zoomable + : false, + onthumbenter: customUserOptions.onthumbenter !== undefined ? + customUserOptions.onthumbenter + : null, + onthumbmove: customUserOptions.onthumbmove !== undefined ? + customUserOptions.onthumbmove + : null, + onthumbleave: customUserOptions.onthumbleave !== undefined ? + customUserOptions.onthumbleave + : null, + onzoom: customUserOptions.onzoom !== undefined ? + customUserOptions.onzoom + : null + }, + pos = { + t: 0, + l: 0, + x: 0, + y: 0 + }, + gId = 0, + status = 0, + curIdx = '', + curLens = null, + curLarge = null, + lensbg = customUserOptions.bg !== undefined ? + customUserOptions.lensbg + : true, + gZoom = customUserOptions.zoom !== undefined ? + customUserOptions.zoom + : magnifierOptions.zoom, + gZoomMin = customUserOptions.zoomMin !== undefined ? + customUserOptions.zoomMin + : magnifierOptions.zoomMin, + gZoomMax = customUserOptions.zoomMax !== undefined ? + customUserOptions.zoomMax + : magnifierOptions.zoomMax, + gMode = customUserOptions.mode || magnifierOptions.mode, + gEventType = customUserOptions.eventType || magnifierOptions.eventType, + data = {}, + inBounds = false, + isOverThumb = false, + rate = 1, + paddingX = 0, + paddingY = 0, + enabled = true, + showWrapper = true; var MagnifyCls = { magnifyHidden: 'magnify-hidden', magnifyOpaque: 'magnify-opaque', magnifyFull: 'magnify-fullimage' - }; - /** * Update Lens positon on. * @@ -143,105 +142,104 @@ $(thumb).parent().append(lens); } - function updateLensOnLoad(idx, thumb, large, largeWrapper) { - var lens = $box.find('.magnify-lens'), + function updateLensOnLoad(idSelectorMainImg, thumb, largeImgInMagnifyLens, largeWrapper) { + var magnifyLensElement= $box.find('.magnify-lens'), textWrapper; - if (data[idx].status === 1) { + if (data[idSelectorMainImg].status === 1) { textWrapper = $('<div class="magnifier-loader-text"></div>'); - lens.className = 'magnifier-loader magnify-hidden'; + magnifyLensElement.className = 'magnifier-loader magnify-hidden'; textWrapper.html('Loading...'); - lens.html('').append(textWrapper); - } else if (data[idx].status === 2) { - lens.addClass(MagnifyCls.magnifyHidden); - lens.html(''); - large.id = idx + '-large'; - large.style.width = data[idx].largeW * rate + 'px'; - large.style.height = data[idx].largeH + 'px'; - large.className = 'magnifier-large magnify-hidden'; - - if (data[idx].mode === 'inside') { - lens.append(large); + magnifyLensElement.html('').append(textWrapper); + } else if (data[idSelectorMainImg].status === 2) { + magnifyLensElement.addClass(MagnifyCls.magnifyHidden); + magnifyLensElement.html(''); + + largeImgInMagnifyLens.id = idSelectorMainImg + '-large'; + largeImgInMagnifyLens.style.width = data[idSelectorMainImg].largeImgInMagnifyLensWidth + 'px'; + largeImgInMagnifyLens.style.height = data[idSelectorMainImg].largeImgInMagnifyLensHeight + 'px'; + largeImgInMagnifyLens.className = 'magnifier-large magnify-hidden'; + + if (data[idSelectorMainImg].mode === 'inside') { + magnifyLensElement.append(largeImgInMagnifyLens); } else { - largeWrapper.html('').append(large); + largeWrapper.html('').append(largeImgInMagnifyLens); } } - data[idx].lensH = data[idx].lensH > $thumb.height() ? $thumb.height() : data[idx].lensH; + data[idSelectorMainImg].lensH = data[idSelectorMainImg].lensH > $thumb.height() ? $thumb.height() : data[idSelectorMainImg].lensH; - if (Math.round(data[idx].lensW) === 0) { - lens.css('display', 'none'); + if (Math.round(data[idSelectorMainImg].lensW) === 0) { + magnifyLensElement.css('display', 'none'); } else { - lens.css({ - width: data[idx].lensW + 1 + 'px', - height: data[idx].lensH - 1 + 'px', + magnifyLensElement.css({ + width: Math.round(data[idSelectorMainImg].lensW) + 'px', + height: Math.round(data[idSelectorMainImg].lensH) + 'px', display: '' }); } } function getMousePos() { - var xPos = pos.x - currentOpts.x, - yPos = pos.y - currentOpts.y, + var xPos = pos.x - magnifierOptions.x, + yPos = pos.y - magnifierOptions.y, t, l; - inBounds = xPos < 0 || yPos < 0 || xPos > currentOpts.w || yPos > currentOpts.h ? false : true; + inBounds = xPos < 0 || yPos < 0 || xPos > magnifierOptions.w || yPos > magnifierOptions.h ? false : true; - l = xPos - currentOpts.lensW / 2; - t = yPos - currentOpts.lensH / 2; + l = xPos - magnifierOptions.lensW / 2; + t = yPos - magnifierOptions.lensH / 2; - if (currentOpts.mode !== 'inside') { - if (xPos < currentOpts.lensW / 2) { - l = 0; - } + if (xPos < magnifierOptions.lensW / 2) { + l = 0; + } - if (yPos < currentOpts.lensH / 2) { - t = 0; - } + if (yPos < magnifierOptions.lensH / 2) { + t = 0; + } - if (xPos - currentOpts.w + Math.ceil(currentOpts.lensW / 2) > 0) { - l = currentOpts.w - Math.ceil(currentOpts.lensW + 2); - } + if (xPos - magnifierOptions.w + Math.ceil(magnifierOptions.lensW / 2) > 0) { + l = magnifierOptions.w - Math.ceil(magnifierOptions.lensW + 2); + } - if (yPos - currentOpts.h + Math.ceil(currentOpts.lensH / 2) > 0) { - t = currentOpts.h - Math.ceil(currentOpts.lensH); - } + if (yPos - magnifierOptions.h + Math.ceil(magnifierOptions.lensH / 2) > 0) { + t = magnifierOptions.h - Math.ceil(magnifierOptions.lensH); + } - pos.l = l; - pos.t = t; + pos.l = l; + pos.t = t; - currentOpts.lensBgX = pos.l; - currentOpts.lensBgY = pos.t; + magnifierOptions.lensBgX = pos.l; + magnifierOptions.lensBgY = pos.t; - if (currentOpts.mode === 'inside') { - currentOpts.largeL = xPos * (currentOpts.zoom - currentOpts.lensW / currentOpts.w); - currentOpts.largeT = yPos * (currentOpts.zoom - currentOpts.lensH / currentOpts.h); - } else { - currentOpts.largeL = currentOpts.lensBgX * currentOpts.zoom * (currentOpts.largeWrapperW / currentOpts.w) * rate; - currentOpts.largeT = currentOpts.lensBgY * currentOpts.zoom * (currentOpts.largeWrapperH / currentOpts.h); - } + if (magnifierOptions.mode === 'inside') { + magnifierOptions.largeL = Math.round(xPos * (magnifierOptions.zoom - magnifierOptions.lensW / magnifierOptions.w)); + magnifierOptions.largeT = Math.round(yPos * (magnifierOptions.zoom - magnifierOptions.lensH / magnifierOptions.h)); + } else { + magnifierOptions.largeL = Math.round(magnifierOptions.lensBgX * magnifierOptions.zoom * (magnifierOptions.largeWrapperW / magnifierOptions.w)); + magnifierOptions.largeT = Math.round(magnifierOptions.lensBgY * magnifierOptions.zoom * (magnifierOptions.largeWrapperH / magnifierOptions.h)); } } function onThumbEnter() { if (_toBoolean(enabled)) { - currentOpts = data[curIdx]; + magnifierOptions = data[curIdx]; curLens = $box.find('.magnify-lens'); - if (currentOpts.status === 2) { + if (magnifierOptions.status === 2) { curLens.removeClass(MagnifyCls.magnifyOpaque); curLarge = $('#' + curIdx + '-large'); curLarge.removeClass(MagnifyCls.magnifyHidden); - } else if (currentOpts.status === 1) { + } else if (magnifierOptions.status === 1) { curLens.className = 'magnifier-loader'; } } } function onThumbLeave() { - if (currentOpts.status > 0) { - var handler = currentOpts.onthumbleave; + if (magnifierOptions.status > 0) { + var handler = magnifierOptions.onthumbleave; if (handler !== null) { handler({ @@ -266,28 +264,30 @@ function move() { if (_toBoolean(enabled)) { - if (status !== currentOpts.status) { + if (status !== magnifierOptions.status) { onThumbEnter(); } - if (currentOpts.status > 0) { - curThumb.className = currentOpts.thumbCssClass + ' magnify-opaque'; + if (magnifierOptions.status > 0) { + curThumb.className = magnifierOptions.thumbCssClass + ' magnify-opaque'; - if (currentOpts.status === 1) { + if (magnifierOptions.status === 1) { curLens.className = 'magnifier-loader'; - } else if (currentOpts.status === 2) { + } else if (magnifierOptions.status === 2) { curLens.removeClass(MagnifyCls.magnifyHidden); curLarge.removeClass(MagnifyCls.magnifyHidden); curLarge.css({ - left: '-' + currentOpts.largeL + 'px', - top: '-' + currentOpts.largeT + 'px' + left: '-' + magnifierOptions.largeL + 'px', + top: '-' + magnifierOptions.largeT + 'px' }); } - pos.t = pos.t <= 0 ? 0 : pos.t; + var borderOffset = 2; // Offset for magnify-lens border + pos.t = pos.t <= 0 ? 0 : pos.t - borderOffset; + curLens.css({ left: pos.l + paddingX + 'px', - top: pos.t + 1 + paddingY + 'px' + top: pos.t + paddingY + 'px' }); if (lensbg) { @@ -296,10 +296,10 @@ }); } else { curLens.get(0).style.backgroundPosition = '-' + - currentOpts.lensBgX + 'px -' + - currentOpts.lensBgY + 'px'; + magnifierOptions.lensBgX + 'px -' + + magnifierOptions.lensBgY + 'px'; } - var handler = currentOpts.onthumbmove; + var handler = magnifierOptions.onthumbmove; if (handler !== null) { handler({ @@ -312,33 +312,33 @@ } } - status = currentOpts.status; + status = magnifierOptions.status; } } - function setThumbData(thumb, thumbData) { - var thumbBounds = thumb.getBoundingClientRect(), + function setThumbData(mainImage, mainImageData) { + var thumbBounds = mainImage.getBoundingClientRect(), w = 0, h = 0; - thumbData.x = thumbBounds.left; - thumbData.y = thumbBounds.top; - thumbData.w = thumbBounds.right - thumbData.x; - thumbData.h = thumbBounds.bottom - thumbData.y; + mainImageData.x = Math.round(thumbBounds.left); + mainImageData.y = Math.round(thumbBounds.top); + mainImageData.w = Math.round(thumbBounds.right - mainImageData.x); + mainImageData.h = Math.round(thumbBounds.bottom - mainImageData.y); - if (thumbData.mode === 'inside') { - w = thumbData.w; - h = thumbData.h; + if (mainImageData.mode === 'inside') { + w = mainImageData.w; + h = mainImageData.h; } else { - w = thumbData.largeWrapperW; - h = thumbData.largeWrapperH; + w = mainImageData.largeWrapperW; + h = mainImageData.largeWrapperH; } - thumbData.largeW = thumbData.zoom * w; - thumbData.largeH = thumbData.zoom * h; + mainImageData.largeImgInMagnifyLensWidth = Math.round(mainImageData.zoom * w); + mainImageData.largeImgInMagnifyLensHeight = Math.round(mainImageData.zoom * h); - thumbData.lensW = thumbData.w / thumbData.zoom / rate; - thumbData.lensH = thumbData.h / thumbData.zoom; + mainImageData.lensW = Math.round(mainImageData.w / mainImageData.zoom); + mainImageData.lensH = Math.round(mainImageData.h / mainImageData.zoom); } function _init($box, options) { @@ -360,11 +360,11 @@ if (_toBoolean(enabled)) { - $largeWrapper.show().css('display', ''); - $largeWrapper.addClass(MagnifyCls.magnifyHidden); + $magnifierPreview.show().css('display', ''); + $magnifierPreview.addClass(MagnifyCls.magnifyHidden); set(opts); } else { - $largeWrapper.empty().hide(); + $magnifierPreview.empty().hide(); } } @@ -376,7 +376,7 @@ if (showWrapper) { - if (currentOpts.status !== 0) { + if (magnifierOptions.status !== 0) { onThumbLeave(); } handleEvents(e); @@ -390,7 +390,7 @@ if (showWrapper) { if (!isOverThumb) { - if (currentOpts.status !== 0) { + if (magnifierOptions.status !== 0) { onThumbLeave(); } handleEvents(e); @@ -420,7 +420,7 @@ onThumbEnter(src); - setThumbData(curThumb, currentOpts); + setThumbData(curThumb, magnifierOptions); pos.x = e.clientX; pos.y = e.clientY; @@ -428,7 +428,7 @@ getMousePos(); move(); - var handler = currentOpts.onthumbenter; + var handler = magnifierOptions.onthumbenter; if (handler !== null) { handler({ @@ -462,15 +462,15 @@ eventType = options.eventType || thumb.getAttribute('data-eventType') || gEventType, onthumbenter = options.onthumbenter !== undefined ? options.onthumbenter - : currentOpts.onthumbenter, + : magnifierOptions.onthumbenter, onthumbleave = options.onthumbleave !== undefined ? options.onthumbleave - : currentOpts.onthumbleave, + : magnifierOptions.onthumbleave, onthumbmove = options.onthumbmove !== undefined ? options.onthumbmove - : currentOpts.onthumbmove; + : magnifierOptions.onthumbmove; - largeUrl = $thumb.data('original') || gOptions.full || $thumb.attr('src'); + largeUrl = $thumb.data('original') || customUserOptions.full || $thumb.attr('src'); if (thumb.id === '') { idx = thumb.id = 'magnifier-item-' + gId; @@ -529,7 +529,6 @@ onthumbmove: onthumbmove }; - rate = $thumb.width() / $thumb.height() / (data[idx].largeWrapperW / data[idx].largeWrapperH); paddingX = ($thumb.parent().width() - $thumb.width()) / 2; paddingY = ($thumb.parent().height() - $thumb.height()) / 2; @@ -556,7 +555,6 @@ } function onMousemove(e) { - pos.x = e.clientX; pos.y = e.clientY; @@ -567,29 +565,30 @@ } if (inBounds && isOverThumb) { - $largeWrapper.removeClass(MagnifyCls.magnifyHidden); + if(gMode === 'outside'){ + $magnifierPreview.removeClass(MagnifyCls.magnifyHidden); + } move(); } else { onThumbLeave(); isOverThumb = false; - $largeWrapper.addClass(MagnifyCls.magnifyHidden); + $magnifierPreview.addClass(MagnifyCls.magnifyHidden); } } function onScroll() { if (curThumb !== null) { - setThumbData(curThumb, currentOpts); + setThumbData(curThumb, magnifierOptions); } } $(window).on('scroll', onScroll); $(window).resize(function () { - _init($box, gOptions); + _init($box, customUserOptions); }); $box.on('mousemove', onMousemove); - _init($box, gOptions); - + _init($box, customUserOptions); } }(jQuery)); From 8df2ba89876d1077a8b12e0fb98995c143929ab6 Mon Sep 17 00:00:00 2001 From: Devagouda Patil <depatil@Devagoudas-MacBook-Pro.local> Date: Mon, 23 Jul 2018 14:03:46 -0500 Subject: [PATCH 0463/1171] MAGETWO-90970: State field becomes required after page refresh during checkout -Incorporated review commenet for Functional test --- ...ddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml index a52ed9355ec97..4b8d48a84073a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml @@ -11,7 +11,6 @@ <test name="AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest"> <annotations> <features value="Checkout"/> - <stories value="MAGETWO-90970"/> <title value="Guest Checkout"/> <description value="Address State Field For UK Customers Remain Option even After Browser Refresh"/> <severity value="MAJOR"/> From ed155341ce27753297af6669973f128531b44bc1 Mon Sep 17 00:00:00 2001 From: Pratik <ronak2ram@gmail.com> Date: Tue, 17 Jul 2018 12:48:25 +0530 Subject: [PATCH 0464/1171] Remove extra spaces --- .../view/base/web/templates/form/components/collection.html | 2 +- .../Ui/view/base/web/templates/form/element/checkbox-set.html | 4 ++-- app/code/Magento/Ui/view/base/web/templates/form/field.html | 2 +- .../Magento/Ui/view/base/web/templates/grid/editing/bulk.html | 2 +- .../Magento/Ui/view/base/web/templates/grid/editing/row.html | 4 ++-- .../Ui/view/base/web/templates/grid/sticky/sticky.html | 2 +- app/code/Magento/Ui/view/base/web/templates/group/group.html | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/templates/form/components/collection.html b/app/code/Magento/Ui/view/base/web/templates/form/components/collection.html index e153beab8748b..873b0ca744a88 100644 --- a/app/code/Magento/Ui/view/base/web/templates/form/components/collection.html +++ b/app/code/Magento/Ui/view/base/web/templates/form/components/collection.html @@ -30,7 +30,7 @@ <legend class="admin__legend"> <span text="$parent.label"/> </legend><br /> - + <each args="getRegion('body')" render=""/> </fieldset> </div> diff --git a/app/code/Magento/Ui/view/base/web/templates/form/element/checkbox-set.html b/app/code/Magento/Ui/view/base/web/templates/form/element/checkbox-set.html index 79478d2417110..ce06201adb8ba 100644 --- a/app/code/Magento/Ui/view/base/web/templates/form/element/checkbox-set.html +++ b/app/code/Magento/Ui/view/base/web/templates/form/element/checkbox-set.html @@ -16,14 +16,14 @@ <div class="admin__field-control" css="'_with-tooltip': $data.tooltip"> <div class="admin__field admin__field-option" outereach="options"> - <input + <input ko-checked="$parent.value" ko-disabled="$parent.disabled" css=" 'admin__control-radio': !$parent.multiple, 'admin__control-checkbox': $parent.multiple" attr=" - id: ++ko.uid, + id: ++ko.uid, value: value, type: $parent.multiple ? 'checkbox' : 'radio'"/> diff --git a/app/code/Magento/Ui/view/base/web/templates/form/field.html b/app/code/Magento/Ui/view/base/web/templates/form/field.html index 6143900d5dd7b..888bc911d747f 100644 --- a/app/code/Magento/Ui/view/base/web/templates/form/field.html +++ b/app/code/Magento/Ui/view/base/web/templates/form/field.html @@ -35,7 +35,7 @@ <div class="admin__field-note" if="$data.notice" attr="id: noticeId"> <span translate="notice"/> </div> - + <div class="admin__additional-info" if="$data.additionalInfo" html="$data.additionalInfo"></div> <render args="$data.service.template" if="$data.hasService()"/> diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/editing/bulk.html b/app/code/Magento/Ui/view/base/web/templates/grid/editing/bulk.html index a11e56a68dab2..81c7acaf4aa33 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/editing/bulk.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/editing/bulk.html @@ -13,7 +13,7 @@ </button> </with> </if> - + <if args="$data.isEditor"> <label class="admin__field-label admin__field-label-vertical" attr="for: uid" translate="'All in Column'"/> <render/> diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/editing/row.html b/app/code/Magento/Ui/view/base/web/templates/grid/editing/row.html index 4b3109e522f0b..c82456b2a357c 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/editing/row.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/editing/row.html @@ -11,8 +11,8 @@ <span class="data-grid-row-changed" css="_changed: $parent.hasChanges"> <span class="data-grid-row-changed-tooltip" translate="'Record contains unsaved changes.'"/> </span> - </td> - + </td> + <!-- ko ifnot: $parent.isActionsColumn($data) --> <td if="$col.isEditor" template="$parent.fieldTmpl"/> <td ifnot="$col.isEditor" css="$col.getFieldClass()" template="$col.getBody()"/> diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/sticky/sticky.html b/app/code/Magento/Ui/view/base/web/templates/grid/sticky/sticky.html index 0bd5ea06dbf86..8d79bfe6a1b4c 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/sticky/sticky.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/sticky/sticky.html @@ -7,7 +7,7 @@ <div style="display: none;" css="stickyClass" afterRender="setStickyNode"> <span class="data-grid-cap-left" afterRender="setLeftCap"/> <span class="data-grid-cap-right" afterRender="setRightCap"/> - + <div afterRender="setStickyToolbarNode"> <div class="admin__data-grid-header"> <div class="admin__data-grid-header-row"> diff --git a/app/code/Magento/Ui/view/base/web/templates/group/group.html b/app/code/Magento/Ui/view/base/web/templates/group/group.html index 13abdb15d965c..e30ac7a377542 100644 --- a/app/code/Magento/Ui/view/base/web/templates/group/group.html +++ b/app/code/Magento/Ui/view/base/web/templates/group/group.html @@ -19,7 +19,7 @@ <render args="elementTmpl" if="element.input_type == 'checkbox' || element.input_type == 'radio'"/> </if> </each> - + <each args="getRegion('insideGroup')" render=""/> <each args="elems" if="validateWholeGroup"> From b2641073093039b7bff3fbb74b73eac84574f9d8 Mon Sep 17 00:00:00 2001 From: Joseph Maxwell <joseph@swiftotter.com> Date: Wed, 27 Jun 2018 16:21:02 -0500 Subject: [PATCH 0465/1171] Credit memo email template file: fixing incorrect object type error --- .../frontend/templates/email/items/creditmemo/default.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/view/frontend/templates/email/items/creditmemo/default.phtml b/app/code/Magento/Sales/view/frontend/templates/email/items/creditmemo/default.phtml index 20c2c1869fedb..1fca65932b0b0 100644 --- a/app/code/Magento/Sales/view/frontend/templates/email/items/creditmemo/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/email/items/creditmemo/default.phtml @@ -31,6 +31,6 @@ </td> <td class="item-qty"><?= /* @escapeNotVerified */ $_item->getQty() * 1 ?></td> <td class="item-price"> - <?= /* @escapeNotVerified */ $block->getItemPrice($_item) ?> + <?= /* @escapeNotVerified */ $block->getItemPrice($_item->getOrderItem()) ?> </td> </tr> From 6ba6abd9a3f6637191a38fd648ced20987254680 Mon Sep 17 00:00:00 2001 From: Pieter Hoste <hoste.pieter@gmail.com> Date: Tue, 22 May 2018 21:07:00 +0200 Subject: [PATCH 0466/1171] Add generated code to the psr-0 autoloader section so when optimizing the autoloader on a production environment the autoloader will find more classes in its classmap. This should result in fewer file_exists calls and might increase the performance a tiny bit. --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f8182c96f78a0..760ec0da13f42 100644 --- a/composer.json +++ b/composer.json @@ -278,7 +278,8 @@ }, "psr-0": { "": [ - "app/code/" + "app/code/", + "generated/code/" ] }, "files": [ From 33952f43d8786ec2742f37217c5a5d71167a4814 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Mon, 23 Jul 2018 14:26:31 -0500 Subject: [PATCH 0467/1171] MC-1416: Editing Text Content Block from the Stage with TinyMCE 4 turned on Fire focus/blur events on editor --- .../mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index e5fffe1b4f77e..7a41a5736ac5b 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -210,6 +210,14 @@ define([ eventBus.fireEvent('tinymceUndo'); }); + editor.on('focus', function () { + eventBus.fireEvent('tinymceFocus'); + }); + + editor.on('blur', function () { + eventBus.fireEvent('tinymceBlur'); + }); + /** * @param {*} evt */ From d88fae4412874a8d05b2f8eaeb4181fd73d19c6c Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Mon, 23 Jul 2018 14:40:19 -0500 Subject: [PATCH 0468/1171] MAGETWO-90927: Refactor conditional to avoid complexity by checking existence of array and matching existing image count to current image count --- .../Catalog/Controller/Adminhtml/Product/Save.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index dea01df7f4398..ff3ce60d92787 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -194,7 +194,6 @@ public function execute() */ private function handleImageRemoveError($postData, $productId) { - if (isset($postData['product']['media_gallery']['images'])) { $removedImagesAmount = 0; foreach ($postData['product']['media_gallery']['images'] as $image) { @@ -205,12 +204,11 @@ private function handleImageRemoveError($postData, $productId) if ($removedImagesAmount) { $expectedImagesAmount = count($postData['product']['media_gallery']['images']) - $removedImagesAmount; $product = $this->productRepository->getById($productId); - if (is_array($product->getMediaGallery('images'))) { - if ($expectedImagesAmount != count($product->getMediaGallery('images'))) { - $this->messageManager->addNoticeMessage( - __('The image cannot be removed as it has been assigned to the other image role') - ); - } + $images = $product->getMediaGallery('images'); + if (is_array($images) && $expectedImagesAmount != count($images)) { + $this->messageManager->addNoticeMessage( + __('The image cannot be removed as it has been assigned to the other image role') + ); } } } From fc020bd6777772655049595f9e80358d367fea2e Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Mon, 23 Jul 2018 15:51:24 -0500 Subject: [PATCH 0469/1171] MC-3080: Admin can create product attribute with picked color swatch --- .../ActionGroup/ColorPickerActionGroup.xml | 11 ++++ .../StorefrontProductInfoMainSection.xml | 1 + .../Mftf/Test/AdminConfigureSwatchesTest.xml | 55 ++++++++++++++++++- 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml index bd71fa01c7645..f8cfa3071ce0f 100644 --- a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml @@ -28,6 +28,17 @@ <expectedResult type="string">{{expectedStyle}}</expectedResult> </assertEquals> </actionGroup> + <actionGroup name="assertStorefrontSwatchColor"> + <arguments> + <argument name="nthSwatch" type="string" defaultValue="1"/> + <argument name="expectedRgb" type="string" defaultValue="rgb(231, 77, 60)"/> + </arguments> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOption(nthSwatch)}}" userInput="style" stepKey="grabStyle1"/> + <assertEquals stepKey="assertStyle1"> + <actualResult type="string">{$grabStyle1}</actualResult> + <expectedResult type="string">background: center center no-repeat {{expectedRgb}};</expectedResult> + </assertEquals> + </actionGroup> <actionGroup name="openSwatchMenuByIndex"> <arguments> <argument name="index" type="string" defaultValue="0"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 4da84f92e20ed..9c17df90c8566 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -10,6 +10,7 @@ xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoMainSection"> <element name="swatchOptionByLabel" type="button" selector="div.swatch-option[option-label={{opt}}]" parameterized="true"/> + <element name="nthSwatchOption" type="button" selector="div.swatch-option:nth-of-type({{var}})" parameterized="true"/> <element name="selectedSwatchValue" type="text" selector="//div[contains(@class, 'swatch-attribute') and contains(., '{{attr}}')]//span[contains(@class, 'swatch-attribute-selected-option')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml index 7ca5bcf9e259a..96a0c2f67888f 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml @@ -12,8 +12,8 @@ <annotations> <features value="Swatches"/> <stories value="Create/configure swatches"/> - <title value="Admin can configure swatches display in configuration"/> - <description value="Admin can configure swatches display in configuration"/> + <title value="Admin can create product attribute with picked color swatch"/> + <description value="Admin can create product attribute with picked color swatch"/> <severity value="CRITICAL"/> <testCaseId value="MC-3080"/> <group value="Swatches"/> @@ -22,11 +22,18 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> + <!-- Clean up our modifications to the existing color attribute --> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes2"/> + <waitForPageLoad stepKey="waitForProductAttributes2"/> + <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="color" stepKey="fillFilter2"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="clickSearch2"/> + <click selector="{{AdminProductAttributeGridSection.AttributeCode('color')}}" stepKey="clickRowToEdit2"/> <click selector="{{AdminManageSwatchSection.nthDelete('1')}}" stepKey="deleteSwatch1"/> <click selector="{{AdminManageSwatchSection.nthDelete('2')}}" stepKey="deleteSwatch2"/> <click selector="{{AdminManageSwatchSection.nthDelete('3')}}" stepKey="deleteSwatch3"/> <waitForPageLoad stepKey="waitToClickSave2"/> <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit2"/> + <!-- Logout --> <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> </after> @@ -92,5 +99,49 @@ <argument name="nthSwatch" value="3"/> <argument name="expectedStyle" value="background: rgb(52, 152, 219);"/> </actionGroup> + + <!-- Create a configurable product to verify the storefront with --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad time="30" stepKey="waitForProductGrid"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{_defaultProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <!-- Create configurations based off the Text Swatch we created earlier --> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickCreateConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickFilters"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" userInput="color" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="1" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> + + <!-- Go to the product page and see text swatch options --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnProductPage"/> + <waitForPageLoad stepKey="waitForProductPage"/> + + <!-- Verify that the storefront shows the swatches too --> + <actionGroup ref="assertStorefrontSwatchColor" stepKey="assertSwatch4"> + <argument name="nthSwatch" value="1"/> + <argument name="expectedRgb" value="rgb(231, 77, 60)"/> + </actionGroup> + <actionGroup ref="assertStorefrontSwatchColor" stepKey="assertSwatch5"> + <argument name="nthSwatch" value="2"/> + <argument name="expectedRgb" value="rgb(46, 204, 112)"/> + </actionGroup> + <actionGroup ref="assertStorefrontSwatchColor" stepKey="assertSwatch6"> + <argument name="nthSwatch" value="3"/> + <argument name="expectedRgb" value="rgb(52, 152, 219)"/> + </actionGroup> </test> </tests> From f0c7ceb59f1140da49d83cf6969cd90f55d086e0 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Mon, 23 Jul 2018 16:01:41 -0500 Subject: [PATCH 0470/1171] MC-3078: Admin can create product attribute with text swatch - Fixed code review feedback --- .../Section/StorefrontProductInfoMainSection.xml | 3 --- .../Section/StorefrontProductInfoMainSection.xml | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 8744eb3e0b81b..7e1bec48aeebc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -63,8 +63,5 @@ <element name="productAddToCompare" type="button" selector="a.action.tocompare"/> <element name="productOptionDropDownTitle" type="text" selector="//label[contains(.,'{{var1}}')]" parameterized="true"/> <element name="productOptionDropDownOptionTitle" type="text" selector="//label[contains(.,'{{var1}}')]/../div[@class='control']//select//option[contains(.,'{{var2}}')]" parameterized="true"/> - - <element name="swatchAttributeOptions" type="text" selector="div.swatch-attribute-options"/> - <element name="nthSwatchOptionText" type="button" selector="div.swatch-option.text:nth-of-type({{n}})" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml new file mode 100644 index 0000000000000..0b36cf788d86a --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="StorefrontProductInfoMainSection"> + <element name="swatchAttributeOptions" type="text" selector="div.swatch-attribute-options"/> + <element name="nthSwatchOptionText" type="button" selector="div.swatch-option.text:nth-of-type({{n}})" parameterized="true"/> + </section> +</sections> From bf664996a0646a15c8b3fe104b6f177a7e1e659c Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Mon, 23 Jul 2018 16:16:50 -0500 Subject: [PATCH 0471/1171] MC-3080: Admin can create product attribute with picked color swatch - Fix code review feedback regarding stepKeys in the before block --- .../Test/Mftf/Test/AdminConfigureSwatchesTest.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml index 96a0c2f67888f..0db67d0750b36 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml @@ -23,16 +23,16 @@ </before> <after> <!-- Clean up our modifications to the existing color attribute --> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes2"/> - <waitForPageLoad stepKey="waitForProductAttributes2"/> - <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="color" stepKey="fillFilter2"/> - <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="clickSearch2"/> - <click selector="{{AdminProductAttributeGridSection.AttributeCode('color')}}" stepKey="clickRowToEdit2"/> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + <waitForPageLoad stepKey="waitForProductAttributes"/> + <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="color" stepKey="fillFilter"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="clickSearch"/> + <click selector="{{AdminProductAttributeGridSection.AttributeCode('color')}}" stepKey="clickRowToEdit"/> <click selector="{{AdminManageSwatchSection.nthDelete('1')}}" stepKey="deleteSwatch1"/> <click selector="{{AdminManageSwatchSection.nthDelete('2')}}" stepKey="deleteSwatch2"/> <click selector="{{AdminManageSwatchSection.nthDelete('3')}}" stepKey="deleteSwatch3"/> - <waitForPageLoad stepKey="waitToClickSave2"/> - <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit2"/> + <waitForPageLoad stepKey="waitToClickSave"/> + <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit"/> <!-- Logout --> <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> </after> From bf9f1c0f753ba271da5f9727052e21edebeb860a Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel.ruf@ueberbit.de> Date: Fri, 20 Jul 2018 14:20:59 +0200 Subject: [PATCH 0472/1171] fix: add hasrequired notice for create account form and password forget form --- .../Magento_Customer/web/css/source/_module.less | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index 832082aa6557a..c6a240fd55ea2 100644 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -93,6 +93,21 @@ } } } + .fieldset.create.account { + .lib-form-hasrequired(bottom); + &:after { + margin-top: 35px; + } + } + } + + .form.password.forget { + .fieldset { + .lib-form-hasrequired(bottom); + &:after { + margin-top: 35px; + } + } } // Full name fieldset From e7463c1b4044f9435651a38019b40af5cf8bb65e Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Tue, 24 Jul 2018 10:34:17 +0530 Subject: [PATCH 0473/1171] Update CompareTest.php --- .../Magento/Catalog/Controller/Product/CompareTest.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php index 8587a011365d3..9c23b28462e67 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php @@ -48,7 +48,12 @@ public function testAddAction() ); $this->assertSessionMessages( - $this->equalTo(['You added product Simple Product 1 Name to the <a href="http://localhost/index.php/catalog/product_compare/">comparison list</a>.']), + $this->equalTo( + [ + 'You added product Simple Product 1 Name to the '. + '<a href="http://localhost/index.php/catalog/product_compare/">comparison list</a>.' + ] + ), MessageInterface::TYPE_SUCCESS ); From 4934052256c98d7f691ee54108c8a453eab42e2b Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Tue, 24 Jul 2018 11:04:33 +0300 Subject: [PATCH 0474/1171] Fix coding standards issue --- .../Magento/luma/Magento_Theme/web/css/source/_module.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less index a18268f5af677..2c177571b9f5a 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less @@ -261,10 +261,10 @@ .copyright { .lib-css(background-color, @copyright__background-color); .lib-css(color, @color-white); + box-sizing: border-box; display: block; padding: @indent__s; text-align: center; - box-sizing: border-box; } .page-header, From adbf6473b36e03703aa1fa612a31285abb38008d Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 24 Jul 2018 10:31:41 +0200 Subject: [PATCH 0475/1171] Added assignment instead of direct return --- .../Magento/TestFramework/TestCase/GraphQlAbstract.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php index ba64a3e9eee8f..936cbfdb5106f 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php @@ -92,7 +92,7 @@ private function getAppCache() private function getGraphQlClient() { if ($this->graphQlClient === null) { - return Bootstrap::getObjectManager()->get(\Magento\TestFramework\TestCase\GraphQl\Client::class); + $this->graphQlClient = Bootstrap::getObjectManager()->get(\Magento\TestFramework\TestCase\GraphQl\Client::class); } return $this->graphQlClient; From 903969cc520d45bdb74a4245d634b7aa24e75cf6 Mon Sep 17 00:00:00 2001 From: Dave Macaulay <dmacaulay@magento.com> Date: Tue, 24 Jul 2018 11:07:41 +0200 Subject: [PATCH 0476/1171] MC-3103: Custom Product Attributes with FullWidth Page Layout - Resolve CR comments, update shouldDisplay function to isVisibleOnFrontend --- app/code/Magento/Catalog/Block/Product/View/Attributes.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/View/Attributes.php b/app/code/Magento/Catalog/Block/Product/View/Attributes.php index 4a43701d7caac..cb59d86a74512 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Attributes.php +++ b/app/code/Magento/Catalog/Block/Product/View/Attributes.php @@ -79,7 +79,7 @@ public function getAdditionalData(array $excludeAttr = []) $product = $this->getProduct(); $attributes = $product->getAttributes(); foreach ($attributes as $attribute) { - if ($this->shouldDisplay($attribute, $excludeAttr)) { + if ($this->isVisibleOnFrontend($attribute, $excludeAttr)) { $value = $attribute->getFrontend()->getValue($product); if ($value instanceof Phrase) { @@ -107,7 +107,7 @@ public function getAdditionalData(array $excludeAttr = []) * @param array $excludeAttr * @return bool */ - protected function shouldDisplay( + protected function isVisibleOnFrontend( \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute, array $excludeAttr ) { From 2fc966cb4ef8c2e57a94a2c354395d91153dfa82 Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko <nsyvokonenko@magento.com> Date: Tue, 24 Jul 2018 12:43:27 +0300 Subject: [PATCH 0477/1171] MAGETWO-93701: Make cron:run CLI command react on cron disable configuration --- .../Cron/Console/Command/CronCommand.php | 22 +++++++-- .../Unit/Console/Command/CronCommandTest.php | 45 +++++++++++++++++-- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Cron/Console/Command/CronCommand.php b/app/code/Magento/Cron/Console/Command/CronCommand.php index 78bbb2329f8dc..5710c2b1450cf 100644 --- a/app/code/Magento/Cron/Console/Command/CronCommand.php +++ b/app/code/Magento/Cron/Console/Command/CronCommand.php @@ -14,6 +14,7 @@ use Magento\Store\Model\Store; use Magento\Store\Model\StoreManager; use Magento\Cron\Observer\ProcessCronQueueObserver; +use Magento\Framework\App\DeploymentConfig; use Magento\Framework\Console\Cli; use Magento\Framework\Shell\ComplexParameter; @@ -35,13 +36,22 @@ class CronCommand extends Command private $objectManagerFactory; /** - * Constructor + * Application deployment configuration * + * @var DeploymentConfig + */ + private $deploymentConfig; + + /** * @param ObjectManagerFactory $objectManagerFactory + * @param DeploymentConfig $deploymentConfig Application deployment configuration */ - public function __construct(ObjectManagerFactory $objectManagerFactory) - { + public function __construct( + ObjectManagerFactory $objectManagerFactory, + DeploymentConfig $deploymentConfig = null + ){ $this->objectManagerFactory = $objectManagerFactory; + $this->deploymentConfig = $deploymentConfig; parent::__construct(); } @@ -71,10 +81,16 @@ protected function configure() } /** + * Runs cron jobs if cron is not disabled in Magento configurations + * * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { + if (!$this->deploymentConfig->get('cron/enabled', 1)) { + $output->writeln('<info>' . 'Cron is disabled. Jobs were not run.' . '</info>'); + return; + } $omParams = $_SERVER; $omParams[StoreManager::PARAM_RUN_CODE] = 'admin'; $omParams[Store::CUSTOM_ENTRY_POINT_PARAM] = true; diff --git a/app/code/Magento/Cron/Test/Unit/Console/Command/CronCommandTest.php b/app/code/Magento/Cron/Test/Unit/Console/Command/CronCommandTest.php index 8b3e50d6afb3a..b6e88d0eda0ce 100644 --- a/app/code/Magento/Cron/Test/Unit/Console/Command/CronCommandTest.php +++ b/app/code/Magento/Cron/Test/Unit/Console/Command/CronCommandTest.php @@ -10,15 +10,52 @@ class CronCommandTest extends \PHPUnit\Framework\TestCase { + /** + * Test command with disables cron + * + * @return void + */ + public function testExecuteWithDisabledCrons() + { + $objectManagerFactory = $this->createMock(\Magento\Framework\App\ObjectManagerFactory::class); + $deploymentConfigMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); + + $objectManagerFactory->expects($this->never()) + ->method('create'); + $deploymentConfigMock->expects($this->once()) + ->method('get') + ->with('cron/enabled', 1) + ->willReturn(0); + $commandTester = new CommandTester(new CronCommand($objectManagerFactory, $deploymentConfigMock)); + $commandTester->execute([]); + $expectedMsg = 'Cron is disabled. Jobs were not run.' . PHP_EOL; + $this->assertEquals($expectedMsg, $commandTester->getDisplay()); + } + + /** + * Test command with enabled cron + * + * @return void + */ public function testExecute() { $objectManagerFactory = $this->createMock(\Magento\Framework\App\ObjectManagerFactory::class); + $deploymentConfigMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); $objectManager = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); $cron = $this->createMock(\Magento\Framework\App\Cron::class); - $objectManager->expects($this->once())->method('create')->willReturn($cron); - $cron->expects($this->once())->method('launch'); - $objectManagerFactory->expects($this->once())->method('create')->willReturn($objectManager); - $commandTester = new CommandTester(new CronCommand($objectManagerFactory)); + $objectManager->expects($this->once()) + ->method('create') + ->willReturn($cron); + $cron->expects($this->once()) + ->method('launch'); + $objectManagerFactory->expects($this->once()) + ->method('create') + ->willReturn($objectManager); + $deploymentConfigMock->expects($this->once()) + ->method('get') + ->with('cron/enabled', 1) + ->willReturn(1); + $commandTester = new CommandTester(new CronCommand($objectManagerFactory, $deploymentConfigMock)); $commandTester->execute([]); $expectedMsg = 'Ran jobs by schedule.' . PHP_EOL; $this->assertEquals($expectedMsg, $commandTester->getDisplay()); From 863764eb31953555511d0a9b00787f6830e7b24c Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko <nsyvokonenko@magento.com> Date: Tue, 24 Jul 2018 15:32:44 +0300 Subject: [PATCH 0478/1171] MAGETWO-93701: Make cron:run CLI command react on cron disable configuration --- .../Unit/Console/Command/CronCommandTest.php | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Cron/Test/Unit/Console/Command/CronCommandTest.php b/app/code/Magento/Cron/Test/Unit/Console/Command/CronCommandTest.php index b6e88d0eda0ce..6b1af9323cc93 100644 --- a/app/code/Magento/Cron/Test/Unit/Console/Command/CronCommandTest.php +++ b/app/code/Magento/Cron/Test/Unit/Console/Command/CronCommandTest.php @@ -6,10 +6,29 @@ namespace Magento\Cron\Test\Unit\Console\Command; use Magento\Cron\Console\Command\CronCommand; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\ObjectManagerFactory; +use PHPUnit_Framework_MockObject_MockObject as MockObject; use Symfony\Component\Console\Tester\CommandTester; class CronCommandTest extends \PHPUnit\Framework\TestCase { + /** + * @var ObjectManagerFactory|MockObject + */ + private $objectManagerFactory; + + /** + * @var DeploymentConfig|MockObject + */ + private $deploymentConfigMock; + + protected function setUp() + { + $this->objectManagerFactory = $this->createMock(ObjectManagerFactory::class); + $this->deploymentConfigMock = $this->createMock(DeploymentConfig::class); + } + /** * Test command with disables cron * @@ -17,16 +36,15 @@ class CronCommandTest extends \PHPUnit\Framework\TestCase */ public function testExecuteWithDisabledCrons() { - $objectManagerFactory = $this->createMock(\Magento\Framework\App\ObjectManagerFactory::class); - $deploymentConfigMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); - - $objectManagerFactory->expects($this->never()) + $this->objectManagerFactory->expects($this->never()) ->method('create'); - $deploymentConfigMock->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with('cron/enabled', 1) ->willReturn(0); - $commandTester = new CommandTester(new CronCommand($objectManagerFactory, $deploymentConfigMock)); + $commandTester = new CommandTester( + new CronCommand($this->objectManagerFactory, $this->deploymentConfigMock) + ); $commandTester->execute([]); $expectedMsg = 'Cron is disabled. Jobs were not run.' . PHP_EOL; $this->assertEquals($expectedMsg, $commandTester->getDisplay()); @@ -39,8 +57,6 @@ public function testExecuteWithDisabledCrons() */ public function testExecute() { - $objectManagerFactory = $this->createMock(\Magento\Framework\App\ObjectManagerFactory::class); - $deploymentConfigMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); $objectManager = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); $cron = $this->createMock(\Magento\Framework\App\Cron::class); $objectManager->expects($this->once()) @@ -48,14 +64,16 @@ public function testExecute() ->willReturn($cron); $cron->expects($this->once()) ->method('launch'); - $objectManagerFactory->expects($this->once()) + $this->objectManagerFactory->expects($this->once()) ->method('create') ->willReturn($objectManager); - $deploymentConfigMock->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with('cron/enabled', 1) ->willReturn(1); - $commandTester = new CommandTester(new CronCommand($objectManagerFactory, $deploymentConfigMock)); + $commandTester = new CommandTester( + new CronCommand($this->objectManagerFactory, $this->deploymentConfigMock) + ); $commandTester->execute([]); $expectedMsg = 'Ran jobs by schedule.' . PHP_EOL; $this->assertEquals($expectedMsg, $commandTester->getDisplay()); From 74b4797d9b998340f0eab115304eab97b01254b3 Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Tue, 24 Jul 2018 15:59:40 +0300 Subject: [PATCH 0479/1171] Fixed invalid knockout data binding --- .../Braintree/view/frontend/web/template/payment/paypal.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html index e47024b7d2330..d39ea62012b14 100644 --- a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html @@ -12,7 +12,7 @@ data-bind="attr: {'id': getCode()}, value: getCode(), checked: isChecked, click: selectPaymentMethod, visible: isRadioButtonVisible()" /> <label class="label" data-bind="attr: {'for': getCode()}"> <!-- PayPal Logo --> - <img data-bind="attr: {src: getPaymentAcceptanceMarkSrc(), alt: $t('Acceptance Mark')}, title: $t('Acceptance Mark')}" + <img data-bind="attr: {src: getPaymentAcceptanceMarkSrc(), alt: $t('Acceptance Mark'), title: $t('Acceptance Mark')}" class="payment-icon"/> <!-- PayPal Logo --> <span text="getTitle()"></span> From b50a1c2a8bb9252776dc4d8c9c46d2664c81b049 Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko <nsyvokonenko@magento.com> Date: Tue, 24 Jul 2018 16:19:11 +0300 Subject: [PATCH 0480/1171] MAGETWO-93701: Make cron:run CLI command react on cron disable configuration --- app/code/Magento/Cron/Console/Command/CronCommand.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Cron/Console/Command/CronCommand.php b/app/code/Magento/Cron/Console/Command/CronCommand.php index 5710c2b1450cf..695bf7494f31c 100644 --- a/app/code/Magento/Cron/Console/Command/CronCommand.php +++ b/app/code/Magento/Cron/Console/Command/CronCommand.php @@ -10,6 +10,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputOption; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ObjectManagerFactory; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManager; @@ -51,7 +52,9 @@ public function __construct( DeploymentConfig $deploymentConfig = null ){ $this->objectManagerFactory = $objectManagerFactory; - $this->deploymentConfig = $deploymentConfig; + $this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->get( + DeploymentConfig::class + ); parent::__construct(); } From f2dde88377cde745fe1b989b512459902bc82288 Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko <nsyvokonenko@magento.com> Date: Tue, 24 Jul 2018 16:57:15 +0300 Subject: [PATCH 0481/1171] MAGETWO-93701: Make cron:run CLI command react on cron disable configuration - changes for static tests --- app/code/Magento/Cron/Console/Command/CronCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cron/Console/Command/CronCommand.php b/app/code/Magento/Cron/Console/Command/CronCommand.php index 695bf7494f31c..142a9a397eb5f 100644 --- a/app/code/Magento/Cron/Console/Command/CronCommand.php +++ b/app/code/Magento/Cron/Console/Command/CronCommand.php @@ -50,7 +50,7 @@ class CronCommand extends Command public function __construct( ObjectManagerFactory $objectManagerFactory, DeploymentConfig $deploymentConfig = null - ){ + ) { $this->objectManagerFactory = $objectManagerFactory; $this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->get( DeploymentConfig::class From 6d51ab09b2e3ae5ac4fbc4615074491ce6015678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= <lukasz.bajsarowicz@gmail.com> Date: Tue, 24 Jul 2018 16:17:30 +0200 Subject: [PATCH 0482/1171] Customer Account Navigation changes: - Introduce Strict Types - Introduce SpaceShip Operator --- .../Magento/Customer/Block/Account/Navigation.php | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Customer/Block/Account/Navigation.php b/app/code/Magento/Customer/Block/Account/Navigation.php index 64ced9d592e11..f963c8074c8a3 100644 --- a/app/code/Magento/Customer/Block/Account/Navigation.php +++ b/app/code/Magento/Customer/Block/Account/Navigation.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Customer\Block\Account; @@ -21,7 +22,7 @@ class Navigation extends Links * {@inheritdoc} * @since 100.2.0 */ - public function getLinks() + public function getLinks(): array { $links = $this->_layout->getChildBlocks($this->getNameInLayout()); $sortableLink = []; @@ -44,12 +45,8 @@ public function getLinks() * @return int * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ - private function compare(SortLinkInterface $firstLink, SortLinkInterface $secondLink) + private function compare(SortLinkInterface $firstLink, SortLinkInterface $secondLink): int { - if ($firstLink->getSortOrder() == $secondLink->getSortOrder()) { - return 0; - } - - return ($firstLink->getSortOrder() < $secondLink->getSortOrder()) ? 1 : -1; + return $firstLink->getSortOrder() <=> $secondLink->getSortOrder(); } } From b80745a6c07b7d00344239586e14d78d1ba1a458 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Tue, 24 Jul 2018 09:47:43 -0500 Subject: [PATCH 0483/1171] MC-1416: Editing Text Content Block from the Stage with TinyMCE 4 turned on Add docblock for new setContent method --- lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index 7a41a5736ac5b..ed82d169a2d4e 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -319,8 +319,9 @@ define([ }, /** - * TODO - * @param content + * Replace entire contents of wysiwyg with string content parameter + * + * @param {String} content */ setContent: function (content) { this.get(this.getId()).execCommand('mceSetContent', false, content); From 6a02ff6a4594bb9435b00c4456b586adaa42a473 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Tue, 24 Jul 2018 22:10:50 +0530 Subject: [PATCH 0484/1171] Edited to correct a couple of spelling mistakes --- .../Catalog/view/adminhtml/web/js/product/weight-handler.js | 6 +++--- .../Magento/Ui/view/base/web/js/lib/core/element/element.js | 2 +- .../Widget/Test/Constraint/AssertWidgetCmsPageLink.php | 2 +- .../App/Utility/_files/fixtures/theme/registration.php | 2 +- .../Framework/App/Utility/_files/patterns/paths_one.txt | 2 +- .../Magento/Sniffs/Less/PropertiesLineBreakSniff.php | 2 +- lib/web/mage/utils/objects.js | 2 +- lib/web/mage/validation/url.js | 2 +- setup/performance-toolkit/README.md | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/product/weight-handler.js b/app/code/Magento/Catalog/view/adminhtml/web/js/product/weight-handler.js index 475c9d2dc0601..94300e31f74b5 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/product/weight-handler.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/product/weight-handler.js @@ -67,10 +67,10 @@ define([ }, /** - * Has weight swither + * Has weight switcher * @returns {*} */ - hasWeightSwither: function () { + hasWeightSwitcher: function () { return this.$weightSwitcher().is(':visible'); }, @@ -107,7 +107,7 @@ define([ 'Magento_Catalog/js/product/weight-handler': function () { this.bindAll(); - if (this.hasWeightSwither()) { + if (this.hasWeightSwitcher()) { this.switchWeight(); } }, diff --git a/app/code/Magento/Ui/view/base/web/js/lib/core/element/element.js b/app/code/Magento/Ui/view/base/web/js/lib/core/element/element.js index b228494b422c3..1684f0b220175 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/core/element/element.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/core/element/element.js @@ -46,7 +46,7 @@ define([ } /** - * Creates observable propery using 'track' method. + * Creates observable property using 'track' method. * * @param {Object} obj - Object to whom property belongs. * @param {String} key - Key of the property. diff --git a/dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertWidgetCmsPageLink.php b/dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertWidgetCmsPageLink.php index abda2231c935f..0aa6d39497a43 100644 --- a/dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertWidgetCmsPageLink.php +++ b/dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertWidgetCmsPageLink.php @@ -18,7 +18,7 @@ class AssertWidgetCmsPageLink extends AbstractConstraint { /** - * Assert that created widget displayed on frontent on Home page and on Advanced Search and + * Assert that created widget displayed on frontend on Home page and on Advanced Search and * after click on widget link on frontend system redirects you to cms page. * * @param CmsIndex $cmsIndex diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/theme/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/theme/registration.php index f3b30797b93ed..c286f6c57d87f 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/theme/registration.php @@ -6,4 +6,4 @@ use \Magento\Framework\Component\ComponentRegistrar; -ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontent/Test/theme', __DIR__); +ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Test/theme', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/patterns/paths_one.txt b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/patterns/paths_one.txt index 8f2df480aabc5..f9cd49de77d67 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/patterns/paths_one.txt +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/patterns/paths_one.txt @@ -1,2 +1,2 @@ module Magento_Module * -theme frontent/Test/theme One* +theme frontend/Test/theme One* diff --git a/dev/tests/static/framework/Magento/Sniffs/Less/PropertiesLineBreakSniff.php b/dev/tests/static/framework/Magento/Sniffs/Less/PropertiesLineBreakSniff.php index a1fe346aae3b6..3f33d0b2c69d5 100644 --- a/dev/tests/static/framework/Magento/Sniffs/Less/PropertiesLineBreakSniff.php +++ b/dev/tests/static/framework/Magento/Sniffs/Less/PropertiesLineBreakSniff.php @@ -46,7 +46,7 @@ public function process(File $phpcsFile, $stackPtr) } if ($tokens[$prevPtr]['line'] === $tokens[$stackPtr]['line']) { - $error = 'Each propery must be on a line by itself'; + $error = 'Each property must be on a line by itself'; $phpcsFile->addError($error, $stackPtr, 'SameLine'); } } diff --git a/lib/web/mage/utils/objects.js b/lib/web/mage/utils/objects.js index d0b77aa4320e3..bd0e496339ace 100644 --- a/lib/web/mage/utils/objects.js +++ b/lib/web/mage/utils/objects.js @@ -83,7 +83,7 @@ define([ * @private * * @param {Object} parent - Object from which to remove property. - * @param {Array} path - Splitted path to the propery. + * @param {Array} path - Splitted path to the property. */ function removeNested(parent, path) { var field = path.pop(); diff --git a/lib/web/mage/validation/url.js b/lib/web/mage/validation/url.js index 67e0e989d248e..00ac3fd4eb15d 100644 --- a/lib/web/mage/validation/url.js +++ b/lib/web/mage/validation/url.js @@ -42,7 +42,7 @@ define([], function () { /** * Sanitize url, replacing disallowed chars * - * @param {Sring} path - url to be normalized + * @param {String} path - url to be normalized * @returns {String} */ sanitize: function (path) { diff --git a/setup/performance-toolkit/README.md b/setup/performance-toolkit/README.md index 700f6cd0d775d..bb0a023ab8af1 100644 --- a/setup/performance-toolkit/README.md +++ b/setup/performance-toolkit/README.md @@ -116,7 +116,7 @@ To get more details about available JMeter options, read [Non-GUI Mode](http://j For example, you can run the B2C scenario via console with 90 threads for the Frontend Pool and 10 threads for the Admin Pool: cd {JMeter path}/bin/ - jmeter -n -t {path to peformance toolkit}/benchmark.jmx -j ./jmeter.log -l ./jmeter-results.jtl -Jhost=magento2.dev -Jbase_path=/ -Jadmin_path=admin -JfrontendPoolUsers=90 -JadminPoolUsers=10 + jmeter -n -t {path to performance toolkit}/benchmark.jmx -j ./jmeter.log -l ./jmeter-results.jtl -Jhost=magento2.dev -Jbase_path=/ -Jadmin_path=admin -JfrontendPoolUsers=90 -JadminPoolUsers=10 As a result, you will get `jmeter.log` and `jmeter-results.jtl`. The`jmeter.log` contains information about the test run and can be helpful in determining the cause of an error. The JTL file is a text file containing the results of a test run. It can be opened in the GUI mode to perform analysis of the results (see the *Output* section below). From e8c3fece4fba4f0be230ebdadb53acdc1bce9322 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Tue, 24 Jul 2018 22:32:18 +0530 Subject: [PATCH 0485/1171] Removed double occurrences from files --- .../Model/Indexer/Category/Product/TableMaintainer.php | 2 +- .../Magento/Catalog/Model/Layer/Filter/AbstractFilter.php | 2 +- .../Magento/Catalog/Model/ResourceModel/Product/Option.php | 2 +- .../Catalog/Model/ResourceModel/Product/Option/Value.php | 2 +- .../Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml | 2 +- .../Controller/Adminhtml/Index/AbstractMassAction.php | 2 +- .../Downloadable/Model/ResourceModel/Link/Collection.php | 4 ++-- app/code/Magento/ReleaseNotification/README.md | 2 +- .../Sales/Controller/Adminhtml/Order/AbstractMassAction.php | 2 +- .../Magento/TestFramework/TestCase/Webapi/Adapter/Soap.php | 2 +- .../Downloadable/Test/Block/Catalog/Product/View/Links.php | 2 +- .../Setup/Test/Constraint/Extension/AssertVersionOnGrid.php | 2 +- .../Test/Constraint/AssertWidgetRecentlyViewedProducts.php | 2 +- .../Magento/Framework/GraphQl/Config/GraphQlReaderTest.php | 2 +- .../Magento/Framework/Console/QuestionPerformer/YesNo.php | 2 +- lib/internal/Magento/Framework/DB/TemporaryTableService.php | 2 +- lib/web/prototype/windows/README | 2 +- 17 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/TableMaintainer.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/TableMaintainer.php index 1278434fcad43..105a6dbf30456 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/TableMaintainer.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/TableMaintainer.php @@ -139,7 +139,7 @@ public function getMainTable(int $storeId) public function createTablesForStore(int $storeId) { $mainTableName = $this->getMainTable($storeId); - //Create index table for store based on on main replica table + //Create index table for store based on main replica table //Using main replica table is necessary for backward capability and TableResolver plugin work $this->createTable( $this->getTable(AbstractAction::MAIN_INDEX_TABLE . $this->additionalTableSuffix), diff --git a/app/code/Magento/Catalog/Model/Layer/Filter/AbstractFilter.php b/app/code/Magento/Catalog/Model/Layer/Filter/AbstractFilter.php index a4db630f0234b..d21a8666ec0ac 100644 --- a/app/code/Magento/Catalog/Model/Layer/Filter/AbstractFilter.php +++ b/app/code/Magento/Catalog/Model/Layer/Filter/AbstractFilter.php @@ -241,7 +241,7 @@ protected function _createItem($label, $value, $count = 0) } /** - * Get all product ids from from collection with applied filters + * Get all product ids from collection with applied filters * * @return array */ diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php index 4775b96e3a448..179da06b59990 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php @@ -307,7 +307,7 @@ protected function _saveValueTitles(\Magento\Framework\Model\AbstractModel $obje } /** - * Get first col from from first row for option table + * Get first col from first row for option table * * @param string $tableName * @param int $optionId 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 91bb99ca971a7..4ebcd1f4b9ae4 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php @@ -300,7 +300,7 @@ protected function _saveValueTitles(AbstractModel $object) } /** - * Get first col from from first row for option table + * Get first col from first row for option table * * @param string $tableName * @param int $optionId diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index ad92af9bb3b1b..2d437ab086f82 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -18,7 +18,7 @@ <waitForPageLoad stepKey="waitForPriceRulePage"/> <click stepKey="addNewRule" selector="{{AdminGridMainControls.add}}"/> - <!-- Fill the form according the the attributes of the entity --> + <!-- Fill the form according the attributes of the entity --> <fillField stepKey="fillName" selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{catalogRule.name}}"/> <fillField stepKey="fillDescription" selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{catalogRule.description}}"/> <selectOption stepKey="selectSite" selector="{{AdminNewCatalogPriceRule.websites}}" userInput="{{catalogRule.website_ids[0]}}"/> diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php index ebab2a42a02ec..e26b49aaebe7a 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php @@ -73,7 +73,7 @@ public function execute() /** * Return component referer url - * TODO: Technical dept referer url should be implement as a part of Action configuration in in appropriate way + * TODO: Technical dept referer url should be implement as a part of Action configuration in appropriate way * * @return null|string */ diff --git a/app/code/Magento/Downloadable/Model/ResourceModel/Link/Collection.php b/app/code/Magento/Downloadable/Model/ResourceModel/Link/Collection.php index 4590d5433ed72..73ae696a85187 100644 --- a/app/code/Magento/Downloadable/Model/ResourceModel/Link/Collection.php +++ b/app/code/Magento/Downloadable/Model/ResourceModel/Link/Collection.php @@ -87,7 +87,7 @@ public function addProductToFilter($product) } /** - * Retrieve title for for current store + * Retrieve title for current store * * @param int $storeId * @return $this @@ -113,7 +113,7 @@ public function addTitleToResult($storeId = 0) } /** - * Retrieve price for for current website + * Retrieve price for current website * * @param int $websiteId * @return $this diff --git a/app/code/Magento/ReleaseNotification/README.md b/app/code/Magento/ReleaseNotification/README.md index e0d6e6de17c60..df3206a176f09 100644 --- a/app/code/Magento/ReleaseNotification/README.md +++ b/app/code/Magento/ReleaseNotification/README.md @@ -46,7 +46,7 @@ Each modal page can have the following optional content: The Sub Heading section is ideally used on the first modal page as a way to describe one to three highlighted features that will be presented in greater detail on the following modal pages. It is recommended to use the Main Content -> Text Body and Bullet Point lists as the paragraph and list content displayed on a highlighted feature's detail modal page. -A clickable link to internal or external content in any text field will be created by using the following format and opened in a new browser tab. Providing the URL for the link followed by the the text to be displayed for that link in brackets will cause a clickable link to be created. The text between the brackets [text] will be the text that the clickable link shows. +A clickable link to internal or external content in any text field will be created by using the following format and opened in a new browser tab. Providing the URL for the link followed by the text to be displayed for that link in brackets will cause a clickable link to be created. The text between the brackets [text] will be the text that the clickable link shows. ### Link Format Example: diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/AbstractMassAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/AbstractMassAction.php index 891bfeefc9f52..05066fe5b125e 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/AbstractMassAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/AbstractMassAction.php @@ -70,7 +70,7 @@ public function execute() /** * Return component referrer url - * TODO: Technical dept referrer url should be implement as a part of Action configuration in in appropriate way + * TODO: Technical dept referrer url should be implement as a part of Action configuration in appropriate way * * @return null|string */ diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/Webapi/Adapter/Soap.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/Webapi/Adapter/Soap.php index aee77ae6d589f..e2e32c119af21 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/Webapi/Adapter/Soap.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/Webapi/Adapter/Soap.php @@ -63,7 +63,7 @@ public function call($serviceInfo, $arguments = [], $storeCode = null, $integrat } /** - * Get proper SOAP client instance that is initialized with with WSDL corresponding to requested service interface. + * Get proper SOAP client instance that is initialized with WSDL corresponding to requested service interface. * * @param string $serviceInfo PHP service interface name, should include version if present * @param string|null $storeCode diff --git a/dev/tests/functional/tests/app/Magento/Downloadable/Test/Block/Catalog/Product/View/Links.php b/dev/tests/functional/tests/app/Magento/Downloadable/Test/Block/Catalog/Product/View/Links.php index 3d3920f28a733..648361b0a31c8 100644 --- a/dev/tests/functional/tests/app/Magento/Downloadable/Test/Block/Catalog/Product/View/Links.php +++ b/dev/tests/functional/tests/app/Magento/Downloadable/Test/Block/Catalog/Product/View/Links.php @@ -18,7 +18,7 @@ class Links extends Block { /** - * Selector title for for links + * Selector title for links * * @var string */ diff --git a/dev/tests/functional/tests/app/Magento/Setup/Test/Constraint/Extension/AssertVersionOnGrid.php b/dev/tests/functional/tests/app/Magento/Setup/Test/Constraint/Extension/AssertVersionOnGrid.php index 068f748da4a8b..21ed8a24345f4 100644 --- a/dev/tests/functional/tests/app/Magento/Setup/Test/Constraint/Extension/AssertVersionOnGrid.php +++ b/dev/tests/functional/tests/app/Magento/Setup/Test/Constraint/Extension/AssertVersionOnGrid.php @@ -23,7 +23,7 @@ class AssertVersionOnGrid extends AbstractConstraint /*#@-*/ /** - * Assert that that version of extension is correct. + * Assert that version of extension is correct. * * @param Grid $grid * @param Extension $extension diff --git a/dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertWidgetRecentlyViewedProducts.php b/dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertWidgetRecentlyViewedProducts.php index 67eeb0cdc238b..e6c25fdc52ed5 100644 --- a/dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertWidgetRecentlyViewedProducts.php +++ b/dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertWidgetRecentlyViewedProducts.php @@ -16,7 +16,7 @@ use Magento\Mtf\Constraint\AbstractConstraint; /** - * Check that that widget with type Recently Viewed Products is present on category page + * Check that widget with type Recently Viewed Products is present on category page */ class AssertWidgetRecentlyViewedProducts extends AbstractConstraint { diff --git a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php index d098e9bed4ffe..8583dcf3e4cd2 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php @@ -196,7 +196,7 @@ enumValues(includeDeprecated: true) { 'Missing type in the response' ); } - //Checks to make sure that the the given description exists in the expectedOutput array + //Checks to make sure that the given description exists in the expectedOutput array $this->assertTrue( array_key_exists( array_search( diff --git a/lib/internal/Magento/Framework/Console/QuestionPerformer/YesNo.php b/lib/internal/Magento/Framework/Console/QuestionPerformer/YesNo.php index b46612df59296..58606bb38914d 100644 --- a/lib/internal/Magento/Framework/Console/QuestionPerformer/YesNo.php +++ b/lib/internal/Magento/Framework/Console/QuestionPerformer/YesNo.php @@ -66,7 +66,7 @@ public function execute(array $messages, InputInterface $input, OutputInterface } /** - * Creates Question object from from given array of messages. + * Creates Question object from given array of messages. * * @param string[] $messages array of messages * @return Question diff --git a/lib/internal/Magento/Framework/DB/TemporaryTableService.php b/lib/internal/Magento/Framework/DB/TemporaryTableService.php index 479cd0c6fddb9..03c1a9b9aa7f6 100644 --- a/lib/internal/Magento/Framework/DB/TemporaryTableService.php +++ b/lib/internal/Magento/Framework/DB/TemporaryTableService.php @@ -10,7 +10,7 @@ /** * Class TemporaryTableService creates a temporary table in mysql from a Magento\Framework\DB\Select. - * Use this class to create an index with that that you want to query later for quick data access + * Use this class to create an index with that you want to query later for quick data access * * @api * @since 100.2.0 diff --git a/lib/web/prototype/windows/README b/lib/web/prototype/windows/README index e2cb960834593..44da88b6adec5 100644 --- a/lib/web/prototype/windows/README +++ b/lib/web/prototype/windows/README @@ -133,7 +133,7 @@ See samples/index.html for more details and go on my web page : http://prototype - Add Windows.focusedWindow and Windows.closeAll - Add name to iframe in case of url window - Clean up code, use _ for private function (just name convention) - - Add Dialog.info function, usefull for for submit or notice info (in Rails) + - Add Dialog.info function, usefull for submit or notice info (in Rails) - Add minimize and maximize buttons - Add alert_lite.css without any images - Debug From cbd6ec0b135c0ca18845c3ca5b8a24f576e22cff Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 24 Jul 2018 12:04:34 -0500 Subject: [PATCH 0486/1171] MAGETWO-92931: HTML entities in product name do not display properly in breadcrumb - using json encoder to escape html and javascript - modifying phtml to not use data mage init because json can be larger --- .../ViewModel/Product/BreadcrumbsTest.php | 65 +++++++++++++++++-- .../Catalog/ViewModel/Product/Breadcrumbs.php | 19 ++---- .../templates/product/breadcrumbs.phtml | 7 +- 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php index 316e08a56f99c..2f667c12a1ed6 100644 --- a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php @@ -41,7 +41,7 @@ class BreadcrumbsTest extends \PHPUnit\Framework\TestCase /** * @inheritdoc */ - protected function setUp() + protected function setUp() : void { $this->catalogHelper = $this->getMockBuilder(CatalogHelper::class) ->setMethods(['getProduct']) @@ -65,7 +65,7 @@ protected function setUp() /** * @return void */ - public function testGetCategoryUrlSuffix() + public function testGetCategoryUrlSuffix() : void { $this->scopeConfig->expects($this->once()) ->method('getValue') @@ -78,7 +78,7 @@ public function testGetCategoryUrlSuffix() /** * @return void */ - public function testIsCategoryUsedInProductUrl() + public function testIsCategoryUsedInProductUrl() : void { $this->scopeConfig->expects($this->once()) ->method('isSetFlag') @@ -95,7 +95,7 @@ public function testIsCategoryUsedInProductUrl() * @param string $expectedName * @return void */ - public function testGetProductName($product, string $expectedName) + public function testGetProductName($product, string $expectedName) : void { $this->catalogHelper->expects($this->atLeastOnce()) ->method('getProduct') @@ -107,7 +107,7 @@ public function testGetProductName($product, string $expectedName) /** * @return array */ - public function productDataProvider() + public function productDataProvider() : array { return [ [$this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test']]), 'Test'], @@ -115,10 +115,63 @@ public function productDataProvider() ]; } + /** + * @dataProvider productJsonEncodeDataProvider + * + * @param Product|null $product + * @param string $expectedJson + * @return void + */ + public function testGetJsonConfiguration($product, string $expectedJson) : void + { + $this->catalogHelper->expects($this->atLeastOnce()) + ->method('getProduct') + ->willReturn($product); + + + $this->scopeConfig->expects($this->any()) + ->method('isSetFlag') + ->with('catalog/seo/product_use_categories', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + ->willReturn(false); + + $this->scopeConfig->expects($this->any()) + ->method('getValue') + ->with('catalog/seo/category_url_suffix', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + ->willReturn('."html'); + + $this->assertEquals($expectedJson, $this->viewModel->getJsonConfiguration()); + } + + /** + * @return array + */ + public function productJsonEncodeDataProvider() : array + { + return [ + [ + $this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test ™']]), + '{"breadcrumbs":{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":"Test \u2122"}}', + ], + [ + $this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test "']]), + '{"breadcrumbs":{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":"Test \""}}', + ], + [ + $this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test <b>x</b>']]), + '{"breadcrumbs":{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":' + . '"Test \u003Cb\u003Ex\u003C\/b\u003E"}}', + ], + [ + $this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test \'abc\'']]), + '{"breadcrumbs":{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":"Test \'abc\'"}}' + ], + ]; + } + /** * @return ObjectManager */ - private function getObjectManager() + private function getObjectManager() : ObjectManager { if (null === $this->objectManager) { $this->objectManager = new ObjectManager($this); diff --git a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php index 01c286cdc74ce..01e246b5969a1 100644 --- a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php +++ b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); namespace Magento\Catalog\ViewModel\Product; @@ -32,11 +31,6 @@ class Breadcrumbs extends DataObject implements ArgumentInterface */ private $scopeConfig; - /** - * @var Json - */ - private $json; - /** * @var Escaper */ @@ -47,6 +41,7 @@ class Breadcrumbs extends DataObject implements ArgumentInterface * @param ScopeConfigInterface $scopeConfig * @param Json|null $json * @param Escaper|null $escaper + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( Data $catalogData, @@ -58,7 +53,6 @@ public function __construct( $this->catalogData = $catalogData; $this->scopeConfig = $scopeConfig; - $this->json = $json ?: ObjectManager::getInstance()->get(Json::class); $this->escaper = $escaper ?: ObjectManager::getInstance()->get(Escaper::class); } @@ -105,16 +99,17 @@ public function getProductName(): string * * @return string */ - public function getJsonConfiguration() + public function getJsonConfiguration() : string { - return $this->json->serialize( + return json_encode( [ 'breadcrumbs' => [ - 'categoryUrlSuffix' => $this->escaper->escapeHtml($this->getCategoryUrlSuffix()), + 'categoryUrlSuffix' => $this->getCategoryUrlSuffix(), 'userCategoryPathInUrl' => (int)$this->isCategoryUsedInProductUrl(), - 'product' => $this->escaper->escapeHtml($this->escaper->escapeJs($this->getProductName())) + 'product' => $this->getProductName() ] - ] + ], + JSON_HEX_TAG ); } } diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/breadcrumbs.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/breadcrumbs.phtml index c54ce5340851c..b41598a100059 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/breadcrumbs.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/breadcrumbs.phtml @@ -7,4 +7,9 @@ /** @var \Magento\Catalog\ViewModel\Product\Breadcrumbs $viewModel */ $viewModel = $block->getData('viewModel'); ?> -<div class="breadcrumbs" data-mage-init='<?= /* @escapeNotVerified */ $viewModel->getJsonConfiguration() ?>'></div> +<div class="breadcrumbs"></div> +<script type="text/x-magento-init"> + { + ".breadcrumbs": <?= $viewModel->getJsonConfiguration() ?> + } +</script> From a3b0bd75a5cea9acaf35f278083f5cf5fe3a657b Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Tue, 24 Jul 2018 12:25:24 -0500 Subject: [PATCH 0487/1171] MAGETWO-68802: Clicking on area around the label of a toggle element results in the element's state being changed - fixed function test failures --- .../Catalog/Test/Block/Adminhtml/Product/ProductForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php index 9331c2c987549..61142adc8f9b3 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php @@ -28,7 +28,7 @@ class ProductForm extends FormSections * * @var string */ - protected $attribute = './/*[contains(@class,"label")]/span[text()="%s"]'; + protected $attribute = './/*[contains(@class,"label")]/label[text()="%s"]'; /** * Product new from date field on the product form From dc6ad3a6fdb87bc017cce7052419899f07131db4 Mon Sep 17 00:00:00 2001 From: KevinBKozan <kkozan@magento.com> Date: Tue, 24 Jul 2018 13:21:11 -0500 Subject: [PATCH 0488/1171] MQE-1112: Bump MFTF version in Magento - MFTF package instead of git installation - dev/tests/acceptance composer lock of codeception to 2.3.x - test material fixes to work with 2.3.0 --- .../Bundle/Test/Mftf/Data/BundleLinkData.xml | 1 - ...tSwatchProductWithFileCustomOptionTest.xml | 2 +- composer.json | 8 +- composer.lock | 206 +++++-------- dev/tests/acceptance/composer.json | 2 +- dev/tests/acceptance/composer.lock | 273 ++++++++---------- 6 files changed, 199 insertions(+), 293 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Data/BundleLinkData.xml b/app/code/Magento/Bundle/Test/Mftf/Data/BundleLinkData.xml index 28fca883350e1..7123a573bc2e1 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Data/BundleLinkData.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Data/BundleLinkData.xml @@ -9,7 +9,6 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ApiBundleLink" type="bundle_link"> - <var key="sku" entityKey="sku" entityType="product2"/> <var key="option_id" entityKey="return" entityType="bundle_option"/> <var key="sku" entityKey="sku" entityType="product"/> <data key="qty">1</data> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml index cfd220d12b1d7..cc6699e989101 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml @@ -12,7 +12,7 @@ <annotations> <features value="ConfigurableProduct"/> <stories value="Add configurable product to cart"/> - <title value="Correct error message and redirect with invalid file option"/> + <title value="Configurable product with swatch option and file custom option"/> <description value="Configurable product with swatch option and file custom option. When adding to cart with an invalid filetype, the correct error message is shown, and options remain selected."/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-93101"/> diff --git a/composer.json b/composer.json index 98926e3604d0d..dd2e551d3dec7 100644 --- a/composer.json +++ b/composer.json @@ -10,12 +10,6 @@ "preferred-install": "dist", "sort-packages": true }, - "repositories": [ - { - "type": "git", - "url": "git@github.com:magento/magento2-functional-testing-framework.git" - } - ], "require": { "php": "~7.1.3||~7.2.0", "ext-ctype": "*", @@ -87,7 +81,7 @@ "zendframework/zend-view": "~2.10.0" }, "require-dev": { - "magento/magento2-functional-testing-framework": "dev-develop", + "magento/magento2-functional-testing-framework": "2.3.0", "friendsofphp/php-cs-fixer": "~2.12.0", "lusitanian/oauth": "~0.8.10", "pdepend/pdepend": "2.5.2", diff --git a/composer.lock b/composer.lock index dae432e6e7d33..cae09c4a73b62 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "144b16cfcd074bac2b151d14b9a61c0d", + "content-hash": "7537dab4a1593204387fb3f6adf4f33d", "packages": [ { "name": "braintree/braintree_php", @@ -1758,16 +1758,16 @@ }, { "name": "symfony/console", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f" + "reference": "5c31f6a97c1c240707f6d786e7e59bfacdbc0219" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/70591cda56b4b47c55776ac78e157c4bb6c8b43f", - "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f", + "url": "https://api.github.com/repos/symfony/console/zipball/5c31f6a97c1c240707f6d786e7e59bfacdbc0219", + "reference": "5c31f6a97c1c240707f6d786e7e59bfacdbc0219", "shasum": "" }, "require": { @@ -1822,20 +1822,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-05-31T10:17:53+00:00" + "time": "2018-07-16T14:05:40+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5" + "reference": "00d64638e4f0703a00ab7fc2c8ae5f75f3b4020f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2391ed210a239868e7256eb6921b1bd83f3087b5", - "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/00d64638e4f0703a00ab7fc2c8ae5f75f3b4020f", + "reference": "00d64638e4f0703a00ab7fc2c8ae5f75f3b4020f", "shasum": "" }, "require": { @@ -1885,11 +1885,11 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-04-06T07:35:57+00:00" + "time": "2018-07-10T11:02:47+00:00" }, { "name": "symfony/filesystem", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -1939,7 +1939,7 @@ }, { "name": "symfony/finder", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -2102,7 +2102,7 @@ }, { "name": "symfony/process", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -2718,16 +2718,16 @@ }, { "name": "zendframework/zend-diactoros", - "version": "1.8.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "63d920d1c9ebc009d860c3666593a66298727dd6" + "reference": "273c18bf6aaab20be9667a3aa4e7702e1e4e7ced" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/63d920d1c9ebc009d860c3666593a66298727dd6", - "reference": "63d920d1c9ebc009d860c3666593a66298727dd6", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/273c18bf6aaab20be9667a3aa4e7702e1e4e7ced", + "reference": "273c18bf6aaab20be9667a3aa4e7702e1e4e7ced", "shasum": "" }, "require": { @@ -2777,7 +2777,7 @@ "psr", "psr-7" ], - "time": "2018-07-09T21:17:27+00:00" + "time": "2018-07-19T18:38:31+00:00" }, { "name": "zendframework/zend-escaper", @@ -4540,16 +4540,16 @@ }, { "name": "behat/gherkin", - "version": "v4.5.1", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a" + "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", - "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", + "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", "shasum": "" }, "require": { @@ -4595,32 +4595,35 @@ "gherkin", "parser" ], - "time": "2017-08-30T11:04:43+00:00" + "time": "2016-10-30T11:50:56+00:00" }, { "name": "codeception/codeception", - "version": "2.4.4", + "version": "2.3.9", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "2060fc1fe8ac2823ff3b8ece04616fc12aca968a" + "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/2060fc1fe8ac2823ff3b8ece04616fc12aca968a", - "reference": "2060fc1fe8ac2823ff3b8ece04616fc12aca968a", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e", + "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e", "shasum": "" }, "require": { - "behat/gherkin": "^4.4.0", - "codeception/phpunit-wrapper": "^6.0.9|^7.0.6", - "codeception/stub": "^2.0", + "behat/gherkin": "~4.4.0", + "codeception/stub": "^1.0", "ext-json": "*", "ext-mbstring": "*", "facebook/webdriver": ">=1.1.3 <2.0", "guzzlehttp/guzzle": ">=4.1.4 <7.0", "guzzlehttp/psr7": "~1.0", - "php": ">=5.6.0 <8.0", + "php": ">=5.4.0 <8.0", + "phpunit/php-code-coverage": ">=2.2.4 <6.0", + "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", + "sebastian/comparator": ">1.1 <3.0", + "sebastian/diff": ">=1.4 <3.0", "symfony/browser-kit": ">=2.7 <5.0", "symfony/console": ">=2.7 <5.0", "symfony/css-selector": ">=2.7 <5.0", @@ -4686,66 +4689,20 @@ "functional testing", "unit testing" ], - "time": "2018-07-16T08:14:50+00:00" - }, - { - "name": "codeception/phpunit-wrapper", - "version": "6.0.10", - "source": { - "type": "git", - "url": "https://github.com/Codeception/phpunit-wrapper.git", - "reference": "7057e599d97b02b4efb009681a43b327dbce138a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/7057e599d97b02b4efb009681a43b327dbce138a", - "reference": "7057e599d97b02b4efb009681a43b327dbce138a", - "shasum": "" - }, - "require": { - "phpunit/php-code-coverage": ">=2.2.4 <6.0", - "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", - "sebastian/comparator": ">1.1 <3.0", - "sebastian/diff": ">=1.4 <4.0" - }, - "replace": { - "codeception/phpunit-wrapper": "*" - }, - "require-dev": { - "codeception/specify": "*", - "vlucas/phpdotenv": "^2.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codeception\\PHPUnit\\": "src\\" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Davert", - "email": "davert.php@resend.cc" - } - ], - "description": "PHPUnit classes used by Codeception", - "time": "2018-06-20T20:08:14+00:00" + "time": "2018-02-26T23:29:41+00:00" }, { "name": "codeception/stub", - "version": "2.0.1", + "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/Codeception/Stub.git", - "reference": "b2eff325d8ff0b824ff659048be7be4e5767d7d0" + "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/b2eff325d8ff0b824ff659048be7be4e5767d7d0", - "reference": "b2eff325d8ff0b824ff659048be7be4e5767d7d0", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/681b62348837a5ef07d10d8a226f5bc358cc8805", + "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805", "shasum": "" }, "require": { @@ -4765,7 +4722,7 @@ "MIT" ], "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-05-18T14:33:08+00:00" + "time": "2018-05-17T09:31:08+00:00" }, { "name": "composer/xdebug-handler", @@ -6271,15 +6228,21 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "dev-develop", + "version": "2.3.0", "source": { "type": "git", - "url": "git@github.com:magento/magento2-functional-testing-framework.git", - "reference": "012f56f05aaa5d73feb54b5b25032f2e01a8e6ee" + "url": "https://github.com/magento/magento2-functional-testing-framework.git", + "reference": "174a9470f0b51152d34c5f374392ab9ea91f7519" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/174a9470f0b51152d34c5f374392ab9ea91f7519", + "reference": "174a9470f0b51152d34c5f374392ab9ea91f7519", + "shasum": "" }, "require": { "allure-framework/allure-codeception": "~1.2.6", - "codeception/codeception": "~2.3.4 || ~2.4.0", + "codeception/codeception": "~2.3.4", "consolidation/robo": "^1.0.0", "epfremme/swagger-php": "^2.0", "flow/jsonpath": ">0.2", @@ -6321,19 +6284,7 @@ "MFTF\\": "dev/tests/functional/MFTF" } }, - "autoload-dev": { - "psr-4": { - "tests\\unit\\": "dev/tests/unit" - } - }, - "scripts": { - "tests": [ - "bin/phpunit-checks" - ], - "static": [ - "bin/static-checks" - ] - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "AGPL-3.0" ], @@ -6344,7 +6295,7 @@ "magento", "testing" ], - "time": "2018-07-17 13:52:40" + "time": "2018-07-24T14:16:26+00:00" }, { "name": "moontoast/math", @@ -8154,7 +8105,7 @@ }, { "name": "symfony/browser-kit", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -8211,7 +8162,7 @@ }, { "name": "symfony/config", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/config.git", @@ -8274,7 +8225,7 @@ }, { "name": "symfony/css-selector", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -8327,16 +8278,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "e761828a85d7dfc00b927f94ccbe1851ce0b6535" + "reference": "62912ab79facdbdaa0849f6c2fe4734b7b60f5cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/e761828a85d7dfc00b927f94ccbe1851ce0b6535", - "reference": "e761828a85d7dfc00b927f94ccbe1851ce0b6535", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/62912ab79facdbdaa0849f6c2fe4734b7b60f5cc", + "reference": "62912ab79facdbdaa0849f6c2fe4734b7b60f5cc", "shasum": "" }, "require": { @@ -8394,20 +8345,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2018-06-25T11:12:43+00:00" + "time": "2018-07-16T14:05:40+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a" + "reference": "eb501fa8aab8c8e2db790d8d0f945697769f6c41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/3350cacf151b48d903114ab8f7a4ccb23e07e10a", - "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/eb501fa8aab8c8e2db790d8d0f945697769f6c41", + "reference": "eb501fa8aab8c8e2db790d8d0f945697769f6c41", "shasum": "" }, "require": { @@ -8451,20 +8402,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-05-01T23:02:13+00:00" + "time": "2018-07-05T11:54:23+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "4f9c7cf962e635b0b26b14500ac046e07dbef7f3" + "reference": "8da9ea68ab2d80dfabd41e0d14b9606bb47a10c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/4f9c7cf962e635b0b26b14500ac046e07dbef7f3", - "reference": "4f9c7cf962e635b0b26b14500ac046e07dbef7f3", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/8da9ea68ab2d80dfabd41e0d14b9606bb47a10c0", + "reference": "8da9ea68ab2d80dfabd41e0d14b9606bb47a10c0", "shasum": "" }, "require": { @@ -8505,20 +8456,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-06-19T21:38:16+00:00" + "time": "2018-07-16T14:05:40+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "45cdcc8a96ef92b43a50723e6d1f5f83096e8cef" + "reference": "0aec9f9c5d2447ae7ea5ea31bc82f1d43f9a8a56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/45cdcc8a96ef92b43a50723e6d1f5f83096e8cef", - "reference": "45cdcc8a96ef92b43a50723e6d1f5f83096e8cef", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0aec9f9c5d2447ae7ea5ea31bc82f1d43f9a8a56", + "reference": "0aec9f9c5d2447ae7ea5ea31bc82f1d43f9a8a56", "shasum": "" }, "require": { @@ -8559,7 +8510,7 @@ "configuration", "options" ], - "time": "2018-05-31T10:17:53+00:00" + "time": "2018-07-07T16:00:36+00:00" }, { "name": "symfony/polyfill-php70", @@ -8677,7 +8628,7 @@ }, { "name": "symfony/stopwatch", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -8726,7 +8677,7 @@ }, { "name": "symfony/yaml", - "version": "v3.4.12", + "version": "v3.4.13", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", @@ -8967,7 +8918,6 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "magento/magento2-functional-testing-framework": 20, "phpmd/phpmd": 0 }, "prefer-stable": true, diff --git a/dev/tests/acceptance/composer.json b/dev/tests/acceptance/composer.json index 83cad123f8568..a20176a29c4c8 100755 --- a/dev/tests/acceptance/composer.json +++ b/dev/tests/acceptance/composer.json @@ -11,7 +11,7 @@ }, "require": { "php": "~7.1.3||~7.2.0", - "codeception/codeception": "~2.3.4 || ~2.4.0", + "codeception/codeception": "~2.3.4", "consolidation/robo": "^1.0.0", "vlucas/phpdotenv": "^2.4" }, diff --git a/dev/tests/acceptance/composer.lock b/dev/tests/acceptance/composer.lock index fe5874e232398..8542402a98f50 100644 --- a/dev/tests/acceptance/composer.lock +++ b/dev/tests/acceptance/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "b93d599d375af66b29edfd8a35875e69", + "content-hash": "46ca2d50566f5069daef753664080c5a", "packages": [ { "name": "behat/gherkin", - "version": "v4.5.1", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a" + "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", - "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", + "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", "shasum": "" }, "require": { @@ -63,32 +63,35 @@ "gherkin", "parser" ], - "time": "2017-08-30T11:04:43+00:00" + "time": "2016-10-30T11:50:56+00:00" }, { "name": "codeception/codeception", - "version": "2.4.3", + "version": "2.3.9", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "13b2db0d54068afaabf3ca8ac8b6591d69018f46" + "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/13b2db0d54068afaabf3ca8ac8b6591d69018f46", - "reference": "13b2db0d54068afaabf3ca8ac8b6591d69018f46", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e", + "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e", "shasum": "" }, "require": { - "behat/gherkin": "^4.4.0", - "codeception/phpunit-wrapper": "^6.0.9|^7.0.6", - "codeception/stub": "^2.0", + "behat/gherkin": "~4.4.0", + "codeception/stub": "^1.0", "ext-json": "*", "ext-mbstring": "*", "facebook/webdriver": ">=1.1.3 <2.0", "guzzlehttp/guzzle": ">=4.1.4 <7.0", "guzzlehttp/psr7": "~1.0", - "php": ">=5.6.0 <8.0", + "php": ">=5.4.0 <8.0", + "phpunit/php-code-coverage": ">=2.2.4 <6.0", + "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", + "sebastian/comparator": ">1.1 <3.0", + "sebastian/diff": ">=1.4 <3.0", "symfony/browser-kit": ">=2.7 <5.0", "symfony/console": ">=2.7 <5.0", "symfony/css-selector": ">=2.7 <5.0", @@ -154,63 +157,20 @@ "functional testing", "unit testing" ], - "time": "2018-06-26T14:09:28+00:00" - }, - { - "name": "codeception/phpunit-wrapper", - "version": "7.1.4", - "source": { - "type": "git", - "url": "https://github.com/Codeception/phpunit-wrapper.git", - "reference": "f18ed631f1eddbb603d72219f577d223b23a1f89" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/f18ed631f1eddbb603d72219f577d223b23a1f89", - "reference": "f18ed631f1eddbb603d72219f577d223b23a1f89", - "shasum": "" - }, - "require": { - "phpunit/php-code-coverage": "^6.0", - "phpunit/phpunit": "^7.1", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0" - }, - "require-dev": { - "codeception/specify": "*", - "vlucas/phpdotenv": "^2.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codeception\\PHPUnit\\": "src\\" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Davert", - "email": "davert.php@resend.cc" - } - ], - "description": "PHPUnit classes used by Codeception", - "time": "2018-06-20T20:07:21+00:00" + "time": "2018-02-26T23:29:41+00:00" }, { "name": "codeception/stub", - "version": "2.0.1", + "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/Codeception/Stub.git", - "reference": "b2eff325d8ff0b824ff659048be7be4e5767d7d0" + "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/b2eff325d8ff0b824ff659048be7be4e5767d7d0", - "reference": "b2eff325d8ff0b824ff659048be7be4e5767d7d0", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/681b62348837a5ef07d10d8a226f5bc358cc8805", + "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805", "shasum": "" }, "require": { @@ -230,7 +190,7 @@ "MIT" ], "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-05-18T14:33:08+00:00" + "time": "2018-05-17T09:31:08+00:00" }, { "name": "consolidation/annotated-command", @@ -1434,40 +1394,40 @@ }, { "name": "phpunit/php-code-coverage", - "version": "6.0.5", + "version": "5.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "4cab20a326d14de7575a8e235c70d879b569a57a" + "reference": "c89677919c5dd6d3b3852f230a663118762218ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4cab20a326d14de7575a8e235c70d879b569a57a", - "reference": "4cab20a326d14de7575a8e235c70d879b569a57a", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", + "reference": "c89677919c5dd6d3b3852f230a663118762218ac", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^7.1", + "php": "^7.0", "phpunit/php-file-iterator": "^1.4.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.0", + "phpunit/php-token-stream": "^2.0.1", "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.1", + "sebastian/environment": "^3.0", "sebastian/version": "^2.0.1", "theseer/tokenizer": "^1.1" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^6.0" }, "suggest": { - "ext-xdebug": "^2.6.0" + "ext-xdebug": "^2.5.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.0-dev" + "dev-master": "5.3.x-dev" } }, "autoload": { @@ -1493,7 +1453,7 @@ "testing", "xunit" ], - "time": "2018-05-28T11:49:20+00:00" + "time": "2018-04-06T15:36:58+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1585,28 +1545,28 @@ }, { "name": "phpunit/php-timer", - "version": "2.0.0", + "version": "1.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f" + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b8454ea6958c3dee38453d3bd571e023108c91f", - "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "1.0-dev" } }, "autoload": { @@ -1621,7 +1581,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", + "email": "sb@sebastian-bergmann.de", "role": "lead" } ], @@ -1630,33 +1590,33 @@ "keywords": [ "timer" ], - "time": "2018-02-01T13:07:23+00:00" + "time": "2017-02-26T11:10:40+00:00" }, { "name": "phpunit/php-token-stream", - "version": "3.0.0", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace" + "reference": "791198a2c6254db10131eecfe8c06670700904db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/21ad88bbba7c3d93530d93994e0a33cd45f02ace", - "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", + "reference": "791198a2c6254db10131eecfe8c06670700904db", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.1" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^6.2.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1679,20 +1639,20 @@ "keywords": [ "tokenizer" ], - "time": "2018-02-01T13:16:43+00:00" + "time": "2017-11-27T05:48:46+00:00" }, { "name": "phpunit/phpunit", - "version": "7.1.5", + "version": "6.5.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "ca64dba53b88aba6af32aebc6b388068db95c435" + "reference": "093ca5508174cd8ab8efe44fd1dde447adfdec8f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ca64dba53b88aba6af32aebc6b388068db95c435", - "reference": "ca64dba53b88aba6af32aebc6b388068db95c435", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/093ca5508174cd8ab8efe44fd1dde447adfdec8f", + "reference": "093ca5508174cd8ab8efe44fd1dde447adfdec8f", "shasum": "" }, "require": { @@ -1704,15 +1664,15 @@ "myclabs/deep-copy": "^1.6.1", "phar-io/manifest": "^1.0.1", "phar-io/version": "^1.0", - "php": "^7.1", + "php": "^7.0", "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^6.0.1", + "phpunit/php-code-coverage": "^5.3", "phpunit/php-file-iterator": "^1.4.3", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.0", - "phpunit/phpunit-mock-objects": "^6.1.1", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0", + "phpunit/php-timer": "^1.0.9", + "phpunit/phpunit-mock-objects": "^5.0.5", + "sebastian/comparator": "^2.1", + "sebastian/diff": "^2.0", "sebastian/environment": "^3.1", "sebastian/exporter": "^3.1", "sebastian/global-state": "^2.0", @@ -1720,12 +1680,16 @@ "sebastian/resource-operations": "^1.0", "sebastian/version": "^2.0.1" }, + "conflict": { + "phpdocumentor/reflection-docblock": "3.0.2", + "phpunit/dbunit": "<3.0" + }, "require-dev": { "ext-pdo": "*" }, "suggest": { "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" + "phpunit/php-invoker": "^1.1" }, "bin": [ "phpunit" @@ -1733,7 +1697,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.1-dev" + "dev-master": "6.5.x-dev" } }, "autoload": { @@ -1759,30 +1723,33 @@ "testing", "xunit" ], - "time": "2018-04-29T15:09:19+00:00" + "time": "2018-07-03T06:40:40+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "6.1.2", + "version": "5.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e" + "reference": "6f9a3c8bf34188a2b53ce2ae7a126089c53e0a9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/f9756fd4f43f014cb2dca98deeaaa8ce5500a36e", - "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/6f9a3c8bf34188a2b53ce2ae7a126089c53e0a9f", + "reference": "6f9a3c8bf34188a2b53ce2ae7a126089c53e0a9f", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.5", - "php": "^7.1", + "php": "^7.0", "phpunit/php-text-template": "^1.2.1", "sebastian/exporter": "^3.1" }, + "conflict": { + "phpunit/phpunit": "<6.0" + }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^6.5" }, "suggest": { "ext-soap": "*" @@ -1790,7 +1757,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.1-dev" + "dev-master": "5.0.x-dev" } }, "autoload": { @@ -1815,7 +1782,7 @@ "mock", "xunit" ], - "time": "2018-05-29T13:54:20+00:00" + "time": "2018-07-13T03:27:23+00:00" }, { "name": "psr/container", @@ -2010,30 +1977,30 @@ }, { "name": "sebastian/comparator", - "version": "3.0.2", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", "shasum": "" }, "require": { - "php": "^7.1", - "sebastian/diff": "^3.0", + "php": "^7.0", + "sebastian/diff": "^2.0 || ^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.1.x-dev" } }, "autoload": { @@ -2070,33 +2037,32 @@ "compare", "equality" ], - "time": "2018-07-12T15:12:46+00:00" + "time": "2018-02-01T13:46:46+00:00" }, { "name": "sebastian/diff", - "version": "3.0.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "366541b989927187c4ca70490a35615d3fef2dce" + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", - "reference": "366541b989927187c4ca70490a35615d3fef2dce", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^7.0", - "symfony/process": "^2 || ^3.3 || ^4" + "phpunit/phpunit": "^6.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -2121,12 +2087,9 @@ "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" + "diff" ], - "time": "2018-06-10T07:54:39+00:00" + "time": "2017-08-03T08:09:46+00:00" }, { "name": "sebastian/environment", @@ -2528,7 +2491,7 @@ }, { "name": "symfony/browser-kit", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -2585,16 +2548,16 @@ }, { "name": "symfony/console", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f" + "reference": "5c31f6a97c1c240707f6d786e7e59bfacdbc0219" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/70591cda56b4b47c55776ac78e157c4bb6c8b43f", - "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f", + "url": "https://api.github.com/repos/symfony/console/zipball/5c31f6a97c1c240707f6d786e7e59bfacdbc0219", + "reference": "5c31f6a97c1c240707f6d786e7e59bfacdbc0219", "shasum": "" }, "require": { @@ -2649,11 +2612,11 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-05-31T10:17:53+00:00" + "time": "2018-07-16T14:05:40+00:00" }, { "name": "symfony/css-selector", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -2706,16 +2669,16 @@ }, { "name": "symfony/dom-crawler", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a" + "reference": "eb501fa8aab8c8e2db790d8d0f945697769f6c41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/3350cacf151b48d903114ab8f7a4ccb23e07e10a", - "reference": "3350cacf151b48d903114ab8f7a4ccb23e07e10a", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/eb501fa8aab8c8e2db790d8d0f945697769f6c41", + "reference": "eb501fa8aab8c8e2db790d8d0f945697769f6c41", "shasum": "" }, "require": { @@ -2759,20 +2722,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-05-01T23:02:13+00:00" + "time": "2018-07-05T11:54:23+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5" + "reference": "00d64638e4f0703a00ab7fc2c8ae5f75f3b4020f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2391ed210a239868e7256eb6921b1bd83f3087b5", - "reference": "2391ed210a239868e7256eb6921b1bd83f3087b5", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/00d64638e4f0703a00ab7fc2c8ae5f75f3b4020f", + "reference": "00d64638e4f0703a00ab7fc2c8ae5f75f3b4020f", "shasum": "" }, "require": { @@ -2822,11 +2785,11 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-04-06T07:35:57+00:00" + "time": "2018-07-10T11:02:47+00:00" }, { "name": "symfony/filesystem", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -2876,7 +2839,7 @@ }, { "name": "symfony/finder", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -3039,7 +3002,7 @@ }, { "name": "symfony/process", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -3088,7 +3051,7 @@ }, { "name": "symfony/yaml", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", From a54d73a55b8e836c51cd09e240e2ae60cca8a8ac Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Tue, 24 Jul 2018 13:42:22 -0500 Subject: [PATCH 0489/1171] MAGETWO-90971: Error is returned when customer checks out with multiple addresses --- app/code/Magento/Tax/Model/Plugin/OrderSave.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Tax/Model/Plugin/OrderSave.php b/app/code/Magento/Tax/Model/Plugin/OrderSave.php index dd28229c5a38e..e69c9696aaf08 100644 --- a/app/code/Magento/Tax/Model/Plugin/OrderSave.php +++ b/app/code/Magento/Tax/Model/Plugin/OrderSave.php @@ -7,6 +7,8 @@ namespace Magento\Tax\Model\Plugin; +use Magento\Tax\Api\Data\OrderTaxDetailsAppliedTaxExtension; + class OrderSave { /** @@ -123,7 +125,9 @@ protected function saveOrderTax(\Magento\Sales\Api\Data\OrderInterface $order) foreach ($taxes as $row) { $id = $row['id']; if (isset($row['extension_attributes'])) { - $taxRates = $row['extension_attributes']['rates']; + $taxRates = $row['extension_attributes'] instanceof OrderTaxDetailsAppliedTaxExtension + ? $row['extension_attributes']->getRates() + : $row['extension_attributes']['rates']; if (is_array($taxRates)) { foreach ($taxRates as $tax) { if ($row['percent'] == null) { From cea4b8965cc0b254c39fe79b0349bbccde16bcbd Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Tue, 24 Jul 2018 15:59:07 -0300 Subject: [PATCH 0490/1171] Refactory to Magento_Backend module class. --- .../Backend/Helper/Dashboard/Order.php | 38 +++++++------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Backend/Helper/Dashboard/Order.php b/app/code/Magento/Backend/Helper/Dashboard/Order.php index 9fc2c2cdb4e6f..f63017f292c5d 100644 --- a/app/code/Magento/Backend/Helper/Dashboard/Order.php +++ b/app/code/Magento/Backend/Helper/Dashboard/Order.php @@ -5,8 +5,6 @@ */ namespace Magento\Backend\Helper\Dashboard; -use Magento\Framework\App\ObjectManager; - /** * Adminhtml dashboard helper for orders * @@ -18,13 +16,13 @@ class Order extends \Magento\Backend\Helper\Dashboard\AbstractDashboard /** * @var \Magento\Reports\Model\ResourceModel\Order\Collection */ - protected $_orderCollection; + private $orderCollection; /** * @var \Magento\Store\Model\StoreManagerInterface * @since 100.0.6 */ - protected $_storeManager; + private $storeManager; /** * @param \Magento\Framework\App\Helper\Context $context @@ -32,48 +30,38 @@ class Order extends \Magento\Backend\Helper\Dashboard\AbstractDashboard */ public function __construct( \Magento\Framework\App\Helper\Context $context, - \Magento\Reports\Model\ResourceModel\Order\Collection $orderCollection + \Magento\Reports\Model\ResourceModel\Order\Collection $orderCollection, + \Magento\Store\Model\StoreManagerInterface $storeManager ) { - $this->_orderCollection = $orderCollection; + $this->orderCollection = $orderCollection; + $this->storeManager = $storeManager; parent::__construct($context); } - /** - * The getter function to get the new StoreManager dependency - * - * @return \Magento\Store\Model\StoreManagerInterface - * - * @deprecated 100.1.0 - */ - private function getStoreManager() - { - if ($this->_storeManager === null) { - $this->_storeManager = ObjectManager::getInstance()->get(\Magento\Store\Model\StoreManagerInterface::class); - } - return $this->_storeManager; - } - /** * @return void + * + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ protected function _initCollection() { $isFilter = $this->getParam('store') || $this->getParam('website') || $this->getParam('group'); - $this->_collection = $this->_orderCollection->prepareSummary($this->getParam('period'), 0, 0, $isFilter); + $this->_collection = $this->orderCollection->prepareSummary($this->getParam('period'), 0, 0, $isFilter); if ($this->getParam('store')) { $this->_collection->addFieldToFilter('store_id', $this->getParam('store')); } elseif ($this->getParam('website')) { - $storeIds = $this->getStoreManager()->getWebsite($this->getParam('website'))->getStoreIds(); + $storeIds = $this->storeManager->getWebsite($this->getParam('website'))->getStoreIds(); $this->_collection->addFieldToFilter('store_id', ['in' => implode(',', $storeIds)]); } elseif ($this->getParam('group')) { - $storeIds = $this->getStoreManager()->getGroup($this->getParam('group'))->getStoreIds(); + $storeIds = $this->storeManager->getGroup($this->getParam('group'))->getStoreIds(); $this->_collection->addFieldToFilter('store_id', ['in' => implode(',', $storeIds)]); } elseif (!$this->_collection->isLive()) { $this->_collection->addFieldToFilter( 'store_id', - ['eq' => $this->getStoreManager()->getStore(\Magento\Store\Model\Store::ADMIN_CODE)->getId()] + ['eq' => $this->storeManager->getStore(\Magento\Store\Model\Store::ADMIN_CODE)->getId()] ); } $this->_collection->load(); From a993cf1b55e9cf63589ec1090aa9669b6c78d03a Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Wed, 18 Jul 2018 16:05:02 -0300 Subject: [PATCH 0491/1171] Refacotries to code base. --- .../Controller/Adminhtml/Block/InlineEdit.php | 3 ++- .../Controller/Adminhtml/Block/MassDelete.php | 2 +- .../Cms/Controller/Adminhtml/Page/Delete.php | 21 ++++++++++++------- .../Cms/Controller/Adminhtml/Page/Edit.php | 2 +- .../Controller/Adminhtml/Page/InlineEdit.php | 3 ++- .../Controller/Adminhtml/Page/MassDelete.php | 3 ++- .../Controller/Adminhtml/Page/MassDisable.php | 4 +++- .../Controller/Adminhtml/Page/MassEnable.php | 4 +++- .../Adminhtml/Page/PostDataProcessor.php | 3 ++- 9 files changed, 30 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Block/InlineEdit.php b/app/code/Magento/Cms/Controller/Adminhtml/Block/InlineEdit.php index e267c568fa9e5..a8d6468784840 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Block/InlineEdit.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Block/InlineEdit.php @@ -43,9 +43,10 @@ public function __construct( $this->blockRepository = $blockRepository; $this->jsonFactory = $jsonFactory; } - + /** * @return \Magento\Framework\Controller\ResultInterface + * @throws \Magento\Framework\Exception\LocalizedException */ public function execute() { diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Block/MassDelete.php b/app/code/Magento/Cms/Controller/Adminhtml/Block/MassDelete.php index cff9c8a39b746..92bc7ad71f590 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Block/MassDelete.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Block/MassDelete.php @@ -60,7 +60,7 @@ public function execute() $block->delete(); } - $this->messageManager->addSuccess(__('A total of %1 record(s) have been deleted.', $collectionSize)); + $this->messageManager->addSuccessMessage(__('A total of %1 record(s) have been deleted.', $collectionSize)); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Delete.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Delete.php index c604e683f9aee..16c99e9857c33 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Delete.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Delete.php @@ -26,21 +26,26 @@ public function execute() $id = $this->getRequest()->getParam('page_id'); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); + if ($id) { $title = ""; try { // init model and delete $model = $this->_objectManager->create(\Magento\Cms\Model\Page::class); $model->load($id); + $title = $model->getTitle(); $model->delete(); + // display success message - $this->messageManager->addSuccess(__('The page has been deleted.')); + $this->messageManager->addSuccessMessage(__('The page has been deleted.')); + // go to grid - $this->_eventManager->dispatch( - 'adminhtml_cmspage_on_delete', - ['title' => $title, 'status' => 'success'] - ); + $this->_eventManager->dispatch('adminhtml_cmspage_on_delete', [ + 'title' => $title, + 'status' => 'success' + ]); + return $resultRedirect->setPath('*/*/'); } catch (\Exception $e) { $this->_eventManager->dispatch( @@ -48,13 +53,15 @@ public function execute() ['title' => $title, 'status' => 'fail'] ); // display error message - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); // go back to edit form return $resultRedirect->setPath('*/*/edit', ['page_id' => $id]); } } + // display error message - $this->messageManager->addError(__('We can\'t find a page to delete.')); + $this->messageManager->addErrorMessage(__('We can\'t find a page to delete.')); + // go to grid return $resultRedirect->setPath('*/*/'); } diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Edit.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Edit.php index ca50a935ce915..6d51c28b6aca7 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Edit.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Edit.php @@ -76,7 +76,7 @@ public function execute() if ($id) { $model->load($id); if (!$model->getId()) { - $this->messageManager->addError(__('This page no longer exists.')); + $this->messageManager->addErrorMessage(__('This page no longer exists.')); /** \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('*/*/'); diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php index 6d75f490d42dc..5159893a6b562 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php @@ -54,9 +54,10 @@ public function __construct( $this->pageRepository = $pageRepository; $this->jsonFactory = $jsonFactory; } - + /** * @return \Magento\Framework\Controller\ResultInterface + * @throws \Magento\Framework\Exception\LocalizedException */ public function execute() { diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php index a711f20d65639..a1d32aa97a382 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php @@ -59,10 +59,11 @@ public function execute() $page->delete(); } - $this->messageManager->addSuccess(__('A total of %1 record(s) have been deleted.', $collectionSize)); + $this->messageManager->addSuccessMessage(__('A total of %1 record(s) have been deleted.', $collectionSize)); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); + return $resultRedirect->setPath('*/*/'); } } diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDisable.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDisable.php index e39c2115f961e..a85b8ecd5e5a1 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDisable.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDisable.php @@ -59,7 +59,9 @@ public function execute() $item->save(); } - $this->messageManager->addSuccess(__('A total of %1 record(s) have been disabled.', $collection->getSize())); + $this->messageManager->addSuccessMessage( + __('A total of %1 record(s) have been disabled.', $collection->getSize()) + ); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassEnable.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassEnable.php index 8278c28a1e696..3f26769e4c9e9 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassEnable.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassEnable.php @@ -59,7 +59,9 @@ public function execute() $item->save(); } - $this->messageManager->addSuccess(__('A total of %1 record(s) have been enabled.', $collection->getSize())); + $this->messageManager->addSuccessMessage( + __('A total of %1 record(s) have been enabled.', $collection->getSize()) + ); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php index 57f92a713ecb0..9b8933c8dba2e 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php @@ -119,7 +119,7 @@ public function validateRequireEntry(array $data) foreach ($data as $field => $value) { if (in_array($field, array_keys($requiredFields)) && $value == '') { $errorNo = false; - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('To apply changes you should fill in hidden required "%1" field', $requiredFields[$field]) ); } @@ -140,6 +140,7 @@ private function validateData($data, $layoutXmlValidator) if (!empty($data['layout_update_xml']) && !$layoutXmlValidator->isValid($data['layout_update_xml'])) { return false; } + if (!empty($data['custom_layout_update_xml']) && !$layoutXmlValidator->isValid($data['custom_layout_update_xml']) ) { From 3ba9464cef0c222a1c5dab735d2f9c27215d5a1f Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Thu, 19 Jul 2018 10:28:38 -0300 Subject: [PATCH 0492/1171] Fixing unit tests. --- .../Cms/Controller/Adminhtml/Block/Delete.php | 6 +++--- .../Magento/Cms/Controller/Adminhtml/Block/Edit.php | 2 +- .../Cms/Controller/Adminhtml/Block/InlineEdit.php | 2 +- .../Cms/Controller/Adminhtml/Page/InlineEdit.php | 2 +- .../Controller/Adminhtml/Wysiwyg/Images/Index.php | 2 +- .../Unit/Controller/Adminhtml/Block/DeleteTest.php | 12 ++++++------ .../Unit/Controller/Adminhtml/Block/EditTest.php | 2 +- .../Controller/Adminhtml/Block/MassDeleteTest.php | 4 ++-- .../Unit/Controller/Adminhtml/Page/DeleteTest.php | 12 ++++++------ .../Test/Unit/Controller/Adminhtml/Page/EditTest.php | 2 +- .../Controller/Adminhtml/Page/MassDeleteTest.php | 4 ++-- .../Controller/Adminhtml/Page/MassDisableTest.php | 4 ++-- .../Controller/Adminhtml/Page/MassEnableTest.php | 4 ++-- .../Unit/Controller/Page/PostDataProcessorTest.php | 2 +- 14 files changed, 30 insertions(+), 30 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Block/Delete.php b/app/code/Magento/Cms/Controller/Adminhtml/Block/Delete.php index 76b6aeb013285..3aaf40e7d0ab2 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Block/Delete.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Block/Delete.php @@ -26,18 +26,18 @@ public function execute() $model->load($id); $model->delete(); // display success message - $this->messageManager->addSuccess(__('You deleted the block.')); + $this->messageManager->addSuccessMessage(__('You deleted the block.')); // go to grid return $resultRedirect->setPath('*/*/'); } catch (\Exception $e) { // display error message - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); // go back to edit form return $resultRedirect->setPath('*/*/edit', ['block_id' => $id]); } } // display error message - $this->messageManager->addError(__('We can\'t find a block to delete.')); + $this->messageManager->addErrorMessage(__('We can\'t find a block to delete.')); // go to grid return $resultRedirect->setPath('*/*/'); } diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Block/Edit.php b/app/code/Magento/Cms/Controller/Adminhtml/Block/Edit.php index d4c0517621144..8756089063237 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Block/Edit.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Block/Edit.php @@ -42,7 +42,7 @@ public function execute() if ($id) { $model->load($id); if (!$model->getId()) { - $this->messageManager->addError(__('This block no longer exists.')); + $this->messageManager->addErrorMessage(__('This block no longer exists.')); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('*/*/'); diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Block/InlineEdit.php b/app/code/Magento/Cms/Controller/Adminhtml/Block/InlineEdit.php index a8d6468784840..3a7e73fbe5eaa 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Block/InlineEdit.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Block/InlineEdit.php @@ -43,7 +43,7 @@ public function __construct( $this->blockRepository = $blockRepository; $this->jsonFactory = $jsonFactory; } - + /** * @return \Magento\Framework\Controller\ResultInterface * @throws \Magento\Framework\Exception\LocalizedException diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php index 5159893a6b562..8774d7e69adfe 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php @@ -54,7 +54,7 @@ public function __construct( $this->pageRepository = $pageRepository; $this->jsonFactory = $jsonFactory; } - + /** * @return \Magento\Framework\Controller\ResultInterface * @throws \Magento\Framework\Exception\LocalizedException diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Index.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Index.php index 525fd31052db8..13765e9faca04 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Index.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Index.php @@ -39,7 +39,7 @@ public function execute() try { $this->_objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class)->getCurrentPath(); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } $this->_initAction(); /** @var \Magento\Framework\View\Result\Layout $resultLayout */ diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php index ff1ed408eb131..55e8382d9ca23 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php @@ -134,10 +134,10 @@ public function testDeleteAction() ->with($this->blockId); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You deleted the block.')); $this->messageManagerMock->expects($this->never()) - ->method('addError'); + ->method('addErrorMessage'); $this->resultRedirectMock->expects($this->once()) ->method('setPath') @@ -154,10 +154,10 @@ public function testDeleteActionNoId() ->willReturn(null); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('We can\'t find a block to delete.')); $this->messageManagerMock->expects($this->never()) - ->method('addSuccess'); + ->method('addSuccessMessage'); $this->resultRedirectMock->expects($this->once()) ->method('setPath') @@ -181,10 +181,10 @@ public function testDeleteActionThrowsException() ->willThrowException(new \Exception(__($errorMsg))); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($errorMsg); $this->messageManagerMock->expects($this->never()) - ->method('addSuccess'); + ->method('addSuccessMessage'); $this->resultRedirectMock->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/EditTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/EditTest.php index 875dde9fb226b..a28a1b793d943 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/EditTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/EditTest.php @@ -139,7 +139,7 @@ public function testEditActionBlockNoExists() ->willReturn(null); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('This block no longer exists.')); $this->resultRedirectFactoryMock->expects($this->atLeastOnce()) diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/MassDeleteTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/MassDeleteTest.php index 2dc14154c85e5..39a7d0d74e4d8 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/MassDeleteTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/MassDeleteTest.php @@ -68,9 +68,9 @@ public function testMassDeleteAction() ->willReturn(new \ArrayIterator($collection)); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('A total of %1 record(s) have been deleted.', $deletedBlocksCount)); - $this->messageManagerMock->expects($this->never())->method('addError'); + $this->messageManagerMock->expects($this->never())->method('addErrorMessage'); $this->resultRedirectMock->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/DeleteTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/DeleteTest.php index 7f994bf5b3df5..09b36bc41d405 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/DeleteTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/DeleteTest.php @@ -124,10 +124,10 @@ public function testDeleteAction() ->method('delete'); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('The page has been deleted.')); $this->messageManagerMock->expects($this->never()) - ->method('addError'); + ->method('addErrorMessage'); $this->eventManagerMock->expects($this->once()) ->method('dispatch') @@ -151,10 +151,10 @@ public function testDeleteActionNoId() ->willReturn(null); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('We can\'t find a page to delete.')); $this->messageManagerMock->expects($this->never()) - ->method('addSuccess'); + ->method('addSuccessMessage'); $this->resultRedirectMock->expects($this->once()) ->method('setPath') @@ -195,10 +195,10 @@ public function testDeleteActionThrowsException() ); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($errorMsg); $this->messageManagerMock->expects($this->never()) - ->method('addSuccess'); + ->method('addSuccessMessage'); $this->resultRedirectMock->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/EditTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/EditTest.php index 335abb837523a..5ea5ce5a9fdbb 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/EditTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/EditTest.php @@ -139,7 +139,7 @@ public function testEditActionPageNoExists() ->willReturn(null); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('This page no longer exists.')); $this->resultRedirectFactoryMock->expects($this->atLeastOnce()) diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDeleteTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDeleteTest.php index 8f1a651b0a7e1..f51ab152ba2a4 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDeleteTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDeleteTest.php @@ -68,9 +68,9 @@ public function testMassDeleteAction() ->willReturn(new \ArrayIterator($collection)); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('A total of %1 record(s) have been deleted.', $deletedPagesCount)); - $this->messageManagerMock->expects($this->never())->method('addError'); + $this->messageManagerMock->expects($this->never())->method('addErrorMessage'); $this->resultRedirectMock->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDisableTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDisableTest.php index 0185654434be1..5b80dd1873d5c 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDisableTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDisableTest.php @@ -67,9 +67,9 @@ public function testMassDisableAction() ->willReturn(new \ArrayIterator($collection)); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('A total of %1 record(s) have been disabled.', $disabledPagesCount)); - $this->messageManagerMock->expects($this->never())->method('addError'); + $this->messageManagerMock->expects($this->never())->method('addErrorMessage'); $this->resultRedirectMock->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassEnableTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassEnableTest.php index b5907e7b3ffed..16b3dfe4ee638 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassEnableTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassEnableTest.php @@ -67,9 +67,9 @@ public function testMassEnableAction() ->willReturn(new \ArrayIterator($collection)); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('A total of %1 record(s) have been enabled.', $enabledPagesCount)); - $this->messageManagerMock->expects($this->never())->method('addError'); + $this->messageManagerMock->expects($this->never())->method('addErrorMessage'); $this->resultRedirectMock->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Page/PostDataProcessorTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Page/PostDataProcessorTest.php index 31d99df5f6289..d13dfc628201d 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Page/PostDataProcessorTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Page/PostDataProcessorTest.php @@ -65,7 +65,7 @@ public function testValidateRequireEntry() 'title' => '' ]; $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('To apply changes you should fill in hidden required "%1" field', 'Page Title')); $this->assertFalse($this->postDataProcessor->validateRequireEntry($postData)); From 94c7b9e1d293ee7db5781df2f5c0a7197c639f5f Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@adobe.com> Date: Tue, 24 Jul 2018 18:38:02 -0500 Subject: [PATCH 0493/1171] MAGETWO-92931: HTML entities in product name do not display properly in breadcrumb - added functional test --- .../Catalog/Test/Mftf/Data/ProductData.xml | 26 ++++++++ .../StorefrontProductNameWithDoubleQuote.xml | 60 +++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 48313230ff1ca..086841cad4a3c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -219,6 +219,32 @@ <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> + <entity name="productWithHTMLEntityOne" type="product"> + <data key="sku" unique="suffix">SimpleOne™Product</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">SimpleOne™Product</data> + <data key="price">50.00</data> + <data key="urlKey" unique="suffix">testurlkey</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> + <entity name="productWithHTMLEntityTwo" type="product"> + <data key="sku" unique="suffix">SimpleTwo®Product</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">SimpleTwo®Product</data> + <data key="price">50.00</data> + <data key="urlKey" unique="suffix">testurlkey</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> <entity name="defaultVirtualProduct" type="product"> <data key="sku" unique="suffix">virtualProduct</data> <data key="type_id">virtual</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml index 956677c8b5de5..0553b08e53429 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml @@ -60,4 +60,64 @@ <argument name="product" value="SimpleProductNameWithDoubleQuote"/> </actionGroup> </test> + + <test name="StorefrontProductNameWithHTMLEntities"> + <annotations> + <features value="Catalog"/> + <title value=":Proudct with html special characters in name"/> + <description value="Product with html entities in the name should appear correctly on the PDP breadcrumbs on storefront"/> + <severity value="CRITICAL"/> + <group value="product"/> + <testCaseId value=""/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategoryOne"/> + <createData entity="productWithHTMLEntityOne" stepKey="productOne"> + <requiredEntity createDataKey="createCategoryOne"/> + </createData> + <createData entity="productWithHTMLEntityTwo" stepKey="productTwo"> + <requiredEntity createDataKey="createCategoryOne"/> + </createData> + </before> + <after> + <deleteData createDataKey="productOne" stepKey="deleteProductOne"/> + <deleteData createDataKey="productTwo" stepKey="deleteProductTwo"/> + <deleteData createDataKey="createCategoryOne" stepKey="deleteCategory"/> + </after> + + <!--Check product in category listing--> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategoryOne.name$$)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad stepKey="waitforCategoryPageToLoad"/> + <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" userInput="{{productWithHTMLEntityOne.name}}" stepKey="seeCorrectNameProd1CategoryPage"/> + <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityTwo.name)}}" userInput="{{productWithHTMLEntityTwo.name}}" stepKey="seeCorrectNameProd2CategoryPage"/> + + <!--Open product display page--> + <!--<click selector="{{StorefrontCategoryProductSection.ProductTitleByNumber('1')}}" stepKey="checkTitle"/>--> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" stepKey="clickProductToGoProductPage"/> + <waitForPageLoad stepKey="waitForProductDisplayPageLoad2"/> + + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{productWithHTMLEntityOne.name}}" stepKey="seeCorrectName"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{productWithHTMLEntityOne.sku}}" stepKey="seeCorrectSku"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="${{productWithHTMLEntityOne.price}}" stepKey="seeCorrectPrice"/> + + <!--Veriy the breadcrumbs on Product Display page--> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs1"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategoryOne.name$$" stepKey="seeCorrectBreadCrumbCategory"/> + <!--<see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="{{productWithHTMLEntityOne.name}}" stepKey="seeCorrectBreadCrumbProduct"/>--> + + <click selector="{{StorefrontNavigationSection.topCategory($$createCategoryOne.name$$)}}" stepKey="goBackToCategoryPage"/> + <waitForPageLoad stepKey="waitforCategoryPageToLoad2"/> + + <!--Open product display page--> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByNumber('1')}}" stepKey="goToProduct2DisplayPage"/> + <!--<click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" stepKey="clickProductToGoProductPage"/>--> + <waitForPageLoad stepKey="waitForProductDisplayPageLoad3"/> + + <!--Veriy the breadcrumbs on Product Display page--> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs2"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategoryOne.name$$" stepKey="seeCorrectBreadCrumbCategory2"/> + <!--<see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="{{productWithHTMLEntityTwo.name}}" stepKey="seeCorrectBreadCrumbProduct2"/>--> + + + </test> </tests> From 24dc904615c4268a19323819f9267687fe279cba Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Tue, 24 Jul 2018 22:15:58 -0500 Subject: [PATCH 0494/1171] MAGETWO-90971: Error is returned when customer checks out with multiple addresses --- app/code/Magento/Tax/Model/Plugin/OrderSave.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Tax/Model/Plugin/OrderSave.php b/app/code/Magento/Tax/Model/Plugin/OrderSave.php index e69c9696aaf08..38952eec02ca1 100644 --- a/app/code/Magento/Tax/Model/Plugin/OrderSave.php +++ b/app/code/Magento/Tax/Model/Plugin/OrderSave.php @@ -80,8 +80,10 @@ protected function saveOrderTax(\Magento\Sales\Api\Data\OrderInterface $order) $ratesIdQuoteItemId = []; foreach ($taxesForItems as $taxesArray) { foreach ($taxesArray['applied_taxes'] as $rates) { - if (isset($rates['extension_attributes']['rates'])) { - $taxRates = $rates['extension_attributes']['rates']; + if (isset($rates['extension_attributes'])) { + $taxRates = $rates['extension_attributes'] instanceof OrderTaxDetailsAppliedTaxExtension + ? $rates['extension_attributes']->getRates() + : $rates['extension_attributes']['rates']; if (is_array($taxRates)) { if (count($taxRates) == 1) { $ratesIdQuoteItemId[$rates['id']][] = [ From 91074c44b9280cf85ea580b8e8be8b8291ea8a35 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Wed, 25 Jul 2018 09:41:22 +0530 Subject: [PATCH 0495/1171] Reduce lengthy code of LoginPost --- .../Customer/Controller/Account/LoginPost.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Account/LoginPost.php b/app/code/Magento/Customer/Controller/Account/LoginPost.php index 49a3f95379d4b..5c7eee78e5f4a 100644 --- a/app/code/Magento/Customer/Controller/Account/LoginPost.php +++ b/app/code/Magento/Customer/Controller/Account/LoginPost.php @@ -202,31 +202,28 @@ public function execute() 'This account is not confirmed. <a href="%1">Click here</a> to resend confirmation email.', $value ); - $this->messageManager->addError($message); - $this->session->setUsername($login['username']); } catch (UserLockedException $e) { $message = __( 'The account sign-in was incorrect or your account is disabled temporarily. ' . 'Please wait and try again later.' ); - $this->messageManager->addError($message); - $this->session->setUsername($login['username']); } catch (AuthenticationException $e) { $message = __( 'The account sign-in was incorrect or your account is disabled temporarily. ' . 'Please wait and try again later.' ); - $this->messageManager->addError($message); - $this->session->setUsername($login['username']); } catch (LocalizedException $e) { $message = $e->getMessage(); - $this->messageManager->addError($message); - $this->session->setUsername($login['username']); } catch (\Exception $e) { // PA DSS violation: throwing or logging an exception here can disclose customer password $this->messageManager->addError( __('An unspecified error occurred. Please contact us for assistance.') ); + } finally { + if (isset($message)) { + $this->messageManager->addError($message); + $this->session->setUsername($login['username']); + } } } else { $this->messageManager->addError(__('A login and a password are required.')); From 661311d6da4842a4eec063cc86b18ed008616eb3 Mon Sep 17 00:00:00 2001 From: Lewis Voncken <lewis@experius.nl> Date: Wed, 31 Jan 2018 12:56:12 +0000 Subject: [PATCH 0496/1171] Added row_id to the flat action indexer so the value isn't set to 0 for new products when using index on save --- .../Catalog/Model/Indexer/Product/Flat/Action/Indexer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Indexer.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Indexer.php index 9dd312e9da801..3a1611299288c 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Indexer.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Indexer.php @@ -175,6 +175,7 @@ public function write($storeId, $productId, $valueFieldSuffix = '') if (!empty($updateData)) { $updateData += ['entity_id' => $productId]; + $updateData += ['row_id' => $productId]; $updateFields = []; foreach ($updateData as $key => $value) { $updateFields[$key] = $key; From 94966358fb44e1be361cefcca0f9a9fe393ef561 Mon Sep 17 00:00:00 2001 From: Lewis Voncken <lewis@experius.nl> Date: Sat, 5 May 2018 19:34:28 +0200 Subject: [PATCH 0497/1171] Moved the indexAll after the product save in the Indexer Product Flat RowTest --- .../Catalog/Model/Indexer/Product/Flat/Action/RowTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/Action/RowTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/Action/RowTest.php index 2b7c416f6be16..15c90891878a0 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/Action/RowTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/Action/RowTest.php @@ -61,12 +61,13 @@ public function testProductUpdate() $this->_processor->getIndexer()->isScheduled(), 'Indexer is in scheduled mode when turned to update on save mode' ); - $this->_processor->reindexAll(); $this->_product->load(1); $this->_product->setName('Updated Product'); $this->_product->save(); + $this->_processor->reindexAll(); + $category = $categoryFactory->create()->load(9); $layer = $listProduct->getLayer(); $layer->setCurrentCategory($category); From 06453dff6de0232e1549943120e10ed1f1f2d2af Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 25 Jul 2018 14:08:20 +0300 Subject: [PATCH 0498/1171] MAGETWO-93778: "Thank you" text is broken on storefront with enabled translate-inline --- .../Theme/Controller/Result/MessagePlugin.php | 36 ++++++- .../Controller/Result/MessagePluginTest.php | 97 ++++++++++++++++++- 2 files changed, 130 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Theme/Controller/Result/MessagePlugin.php b/app/code/Magento/Theme/Controller/Result/MessagePlugin.php index 145493b8e44d8..83172df748a47 100644 --- a/app/code/Magento/Theme/Controller/Result/MessagePlugin.php +++ b/app/code/Magento/Theme/Controller/Result/MessagePlugin.php @@ -5,9 +5,12 @@ */ namespace Magento\Theme\Controller\Result; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Translate\Inline\ParserInterface; +use Magento\Framework\Translate\InlineInterface; /** * Plugin for putting messages to cookies @@ -44,26 +47,34 @@ class MessagePlugin */ private $serializer; + /** + * @var InlineInterface + */ + private $inlineTranslate; + /** * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory * @param \Magento\Framework\Message\ManagerInterface $messageManager * @param \Magento\Framework\View\Element\Message\InterpretationStrategyInterface $interpretationStrategy * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @param InlineInterface|null $inlineTranslate */ public function __construct( \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager, \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory, \Magento\Framework\Message\ManagerInterface $messageManager, \Magento\Framework\View\Element\Message\InterpretationStrategyInterface $interpretationStrategy, - \Magento\Framework\Serialize\Serializer\Json $serializer = null + \Magento\Framework\Serialize\Serializer\Json $serializer = null, + InlineInterface $inlineTranslate = null ) { $this->cookieManager = $cookieManager; $this->cookieMetadataFactory = $cookieMetadataFactory; $this->messageManager = $messageManager; - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + $this->serializer = $serializer ?: ObjectManager::getInstance() ->get(\Magento\Framework\Serialize\Serializer\Json::class); $this->interpretationStrategy = $interpretationStrategy; + $this->inlineTranslate = $inlineTranslate ?: ObjectManager::getInstance()->get(InlineInterface::class); } /** @@ -112,6 +123,12 @@ public function afterRenderResult( private function setCookie(array $messages) { if (!empty($messages)) { + if ($this->inlineTranslate->isAllowed()) { + foreach ($messages as &$message) { + $message['text'] = $this->convertMessageText($message['text']); + } + } + $publicCookieMetadata = $this->cookieMetadataFactory->createPublicCookieMetadata(); $publicCookieMetadata->setDurationOneYear(); $publicCookieMetadata->setPath('/'); @@ -125,6 +142,21 @@ private function setCookie(array $messages) } } + /** + * Replace wrapping translation with html body. + * + * @param string $text + * @return string + */ + private function convertMessageText(string $text): string + { + if (preg_match('#' . ParserInterface::REGEXP_TOKEN . '#', $text, $matches)) { + $text = $matches[1]; + } + + return $text; + } + /** * Return messages array and clean message manager messages * diff --git a/app/code/Magento/Theme/Test/Unit/Controller/Result/MessagePluginTest.php b/app/code/Magento/Theme/Test/Unit/Controller/Result/MessagePluginTest.php index 60457fc1436c0..748f7a1fcb9fb 100644 --- a/app/code/Magento/Theme/Test/Unit/Controller/Result/MessagePluginTest.php +++ b/app/code/Magento/Theme/Test/Unit/Controller/Result/MessagePluginTest.php @@ -14,6 +14,7 @@ use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; use Magento\Framework\Stdlib\Cookie\PublicCookieMetadata; use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Framework\Translate\InlineInterface; use Magento\Framework\View\Element\Message\InterpretationStrategyInterface; use Magento\Theme\Controller\Result\MessagePlugin; @@ -40,6 +41,9 @@ class MessagePluginTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject */ private $serializerMock; + /** @var InlineInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $inlineTranslateMock; + protected function setUp() { $this->cookieManagerMock = $this->getMockBuilder(CookieManagerInterface::class) @@ -53,13 +57,15 @@ protected function setUp() ->getMockForAbstractClass(); $this->serializerMock = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) ->getMock(); + $this->inlineTranslateMock = $this->getMockBuilder(InlineInterface::class)->getMockForAbstractClass(); $this->model = new MessagePlugin( $this->cookieManagerMock, $this->cookieMetadataFactoryMock, $this->managerMock, $this->interpretationStrategyMock, - $this->serializerMock + $this->serializerMock, + $this->inlineTranslateMock ); } @@ -450,4 +456,93 @@ function ($data) { $this->assertEquals($resultMock, $this->model->afterRenderResult($resultMock, $resultMock)); } + + /** + * @return void + */ + public function testAfterRenderResultWithAllowedInlineTranslate(): void + { + $messageType = 'message1type'; + $messageText = '{{{message1text}}{{message1text}}{{message1text}}{{theme/luma}}}'; + $expectedMessages = [ + [ + 'type' => $messageType, + 'text' => 'message1text', + ], + ]; + + /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $resultMock */ + $resultMock = $this->getMockBuilder(Redirect::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var PublicCookieMetadata|\PHPUnit_Framework_MockObject_MockObject $cookieMetadataMock */ + $cookieMetadataMock = $this->getMockBuilder(PublicCookieMetadata::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->cookieMetadataFactoryMock->expects($this->once()) + ->method('createPublicCookieMetadata') + ->willReturn($cookieMetadataMock); + + $this->cookieManagerMock->expects($this->once()) + ->method('setPublicCookie') + ->with( + MessagePlugin::MESSAGES_COOKIES_NAME, + json_encode($expectedMessages), + $cookieMetadataMock + ); + $this->cookieManagerMock->expects($this->once()) + ->method('getCookie') + ->with( + MessagePlugin::MESSAGES_COOKIES_NAME + ) + ->willReturn(json_encode([])); + + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->willReturnCallback( + function ($data) { + return json_decode($data, true); + } + ); + $this->serializerMock->expects($this->once()) + ->method('serialize') + ->willReturnCallback( + function ($data) { + return json_encode($data); + } + ); + + /** @var MessageInterface|\PHPUnit_Framework_MockObject_MockObject $messageMock */ + $messageMock = $this->getMockBuilder(MessageInterface::class) + ->getMock(); + $messageMock->expects($this->once()) + ->method('getType') + ->willReturn($messageType); + + $this->interpretationStrategyMock->expects($this->once()) + ->method('interpret') + ->with($messageMock) + ->willReturn($messageText); + + $this->inlineTranslateMock->expects($this->once()) + ->method('isAllowed') + ->willReturn(true); + + /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ + $collectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $collectionMock->expects($this->once()) + ->method('getItems') + ->willReturn([$messageMock]); + + $this->managerMock->expects($this->once()) + ->method('getMessages') + ->with(true, null) + ->willReturn($collectionMock); + + $this->assertEquals($resultMock, $this->model->afterRenderResult($resultMock, $resultMock)); + } } From 53293903f3737efa362ab68e3de9593b452a9003 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Wed, 25 Jul 2018 14:35:16 +0300 Subject: [PATCH 0499/1171] MAGETWO-93264: Wrong order status on registered checkout within Braintree Credit Card from Storefront with Signifyd Declined Guarantee --- .../Signifyd/Controller/Webhooks/Handler.php | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Signifyd/Controller/Webhooks/Handler.php b/app/code/Magento/Signifyd/Controller/Webhooks/Handler.php index 12bd773d35a2f..2dee31f4048b9 100644 --- a/app/code/Magento/Signifyd/Controller/Webhooks/Handler.php +++ b/app/code/Magento/Signifyd/Controller/Webhooks/Handler.php @@ -7,6 +7,8 @@ use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Signifyd\Api\CaseRepositoryInterface; use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; @@ -21,7 +23,7 @@ * * @see https://www.signifyd.com/docs/api/#/reference/webhooks/ */ -class Handler extends Action +class Handler extends Action implements \Magento\Framework\App\CsrfAwareActionInterface { /** * Event topic of test webhook request. @@ -136,4 +138,20 @@ public function execute() $this->logger->critical($e); } } + + /** + * @inheritDoc + */ + public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException + { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } } From 2ac5482b1f14eac78a2f8b4fc8f177453fe67b54 Mon Sep 17 00:00:00 2001 From: Jose Ortega <joc.hhop@gmail.com> Date: Wed, 25 Jul 2018 14:27:50 +0200 Subject: [PATCH 0500/1171] Refactored multiples conditions which could be grouped in a single one in captcha observers --- .../Observer/CheckGuestCheckoutObserver.php | 17 +++++++------- .../CheckRegisterCheckoutObserver.php | 17 +++++++------- ...CheckUserForgotPasswordBackendObserver.php | 23 +++++++++---------- .../CheckUserLoginBackendObserver.php | 10 ++++---- 4 files changed, 32 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/Captcha/Observer/CheckGuestCheckoutObserver.php b/app/code/Magento/Captcha/Observer/CheckGuestCheckoutObserver.php index 40c215ec218a1..7ccaa76b6c7c8 100644 --- a/app/code/Magento/Captcha/Observer/CheckGuestCheckoutObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckGuestCheckoutObserver.php @@ -66,15 +66,14 @@ public function execute(\Magento\Framework\Event\Observer $observer) $formId = 'guest_checkout'; $captchaModel = $this->_helper->getCaptcha($formId); $checkoutMethod = $this->_typeOnepage->getQuote()->getCheckoutMethod(); - if ($checkoutMethod == \Magento\Checkout\Model\Type\Onepage::METHOD_GUEST) { - if ($captchaModel->isRequired()) { - $controller = $observer->getControllerAction(); - if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId)) - ) { - $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); - $result = ['error' => 1, 'message' => __('Incorrect CAPTCHA')]; - $controller->getResponse()->representJson($this->jsonHelper->jsonEncode($result)); - } + if ($checkoutMethod == \Magento\Checkout\Model\Type\Onepage::METHOD_GUEST + && $captchaModel->isRequired() + ) { + $controller = $observer->getControllerAction(); + if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId))) { + $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); + $result = ['error' => 1, 'message' => __('Incorrect CAPTCHA')]; + $controller->getResponse()->representJson($this->jsonHelper->jsonEncode($result)); } } diff --git a/app/code/Magento/Captcha/Observer/CheckRegisterCheckoutObserver.php b/app/code/Magento/Captcha/Observer/CheckRegisterCheckoutObserver.php index 3bf2ac38debee..8e110a9f4653d 100644 --- a/app/code/Magento/Captcha/Observer/CheckRegisterCheckoutObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckRegisterCheckoutObserver.php @@ -66,15 +66,14 @@ public function execute(\Magento\Framework\Event\Observer $observer) $formId = 'register_during_checkout'; $captchaModel = $this->_helper->getCaptcha($formId); $checkoutMethod = $this->_typeOnepage->getQuote()->getCheckoutMethod(); - if ($checkoutMethod == \Magento\Checkout\Model\Type\Onepage::METHOD_REGISTER) { - if ($captchaModel->isRequired()) { - $controller = $observer->getControllerAction(); - if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId)) - ) { - $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); - $result = ['error' => 1, 'message' => __('Incorrect CAPTCHA')]; - $controller->getResponse()->representJson($this->jsonHelper->jsonEncode($result)); - } + if ($checkoutMethod == \Magento\Checkout\Model\Type\Onepage::METHOD_REGISTER + && $captchaModel->isRequired() + ) { + $controller = $observer->getControllerAction(); + if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId))) { + $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); + $result = ['error' => 1, 'message' => __('Incorrect CAPTCHA')]; + $controller->getResponse()->representJson($this->jsonHelper->jsonEncode($result)); } } diff --git a/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php b/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php index 402fc028c5ad0..2de93dcf6b59b 100644 --- a/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php @@ -69,18 +69,17 @@ public function execute(\Magento\Framework\Event\Observer $observer) $controller = $observer->getControllerAction(); $email = (string)$observer->getControllerAction()->getRequest()->getParam('email'); $params = $observer->getControllerAction()->getRequest()->getParams(); - if (!empty($email) && !empty($params)) { - if ($captchaModel->isRequired()) { - if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId)) - ) { - $this->_session->setEmail((string)$controller->getRequest()->getPost('email')); - $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); - $this->messageManager->addError(__('Incorrect CAPTCHA')); - $controller->getResponse()->setRedirect( - $controller->getUrl('*/*/forgotpassword', ['_nosecret' => true]) - ); - } - } + if (!empty($email) + && !empty($params) + && $captchaModel->isRequired() + && !$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId)) + ) { + $this->_session->setEmail((string)$controller->getRequest()->getPost('email')); + $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); + $this->messageManager->addError(__('Incorrect CAPTCHA')); + $controller->getResponse()->setRedirect( + $controller->getUrl('*/*/forgotpassword', ['_nosecret' => true]) + ); } return $this; diff --git a/app/code/Magento/Captcha/Observer/CheckUserLoginBackendObserver.php b/app/code/Magento/Captcha/Observer/CheckUserLoginBackendObserver.php index 8cc907d7bd12b..924514cd48c5d 100644 --- a/app/code/Magento/Captcha/Observer/CheckUserLoginBackendObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckUserLoginBackendObserver.php @@ -52,11 +52,11 @@ public function execute(\Magento\Framework\Event\Observer $observer) $formId = 'backend_login'; $captchaModel = $this->_helper->getCaptcha($formId); $login = $observer->getEvent()->getUsername(); - if ($captchaModel->isRequired($login)) { - if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($this->_request, $formId))) { - $captchaModel->logAttempt($login); - throw new PluginAuthenticationException(__('Incorrect CAPTCHA.')); - } + if ($captchaModel->isRequired($login) + && !$captchaModel->isCorrect($this->captchaStringResolver->resolve($this->_request, $formId)) + ) { + $captchaModel->logAttempt($login); + throw new PluginAuthenticationException(__('Incorrect CAPTCHA.')); } $captchaModel->logAttempt($login); From 1431afc27543647cfe72c522e9fc2fb83daff1eb Mon Sep 17 00:00:00 2001 From: Dave Macaulay <dmacaulay@magento.com> Date: Wed, 25 Jul 2018 16:08:00 +0200 Subject: [PATCH 0501/1171] MC-3155: Create/Update tests - Skip broken product test --- .../Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml index 956677c8b5de5..568d7b965396b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml @@ -15,6 +15,8 @@ <severity value="CRITICAL"/> <group value="product"/> <testCaseId value="MAGETWO-92384"/> + <!-- Skipped due to MAGETWO-93261 --> + <group value="skip"/> </annotations> <before> <createData entity="_defaultCategory" stepKey="createCategory"/> From 1c44cd81265c1fda0e5687099c8ea3ea8d0b1a14 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Wed, 25 Jul 2018 09:43:58 -0500 Subject: [PATCH 0502/1171] MAGETWO-68802: Clicking on area around the label of a toggle element results in the element's state being changed - fixed function test failures --- .../Catalog/Test/Mftf/Section/AdminProductFormSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 666cca97e4ecf..5f3f78b6e3754 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -9,7 +9,7 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminProductFormSection"> <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> - <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input"/> + <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> From 3a707971901787b6e6cddf7f437296225ec32dbe Mon Sep 17 00:00:00 2001 From: Ji Lu <> Date: Wed, 25 Jul 2018 09:49:13 -0500 Subject: [PATCH 0503/1171] MC-110: Admin should be able to add default video for a Bundle Product - Added blocked tests after mftf 2.3.0 release --- .../AdminAddDefaultVideoBundleProductTest.xml | 65 ++++ ...minRemoveDefaultVideoBundleProductTest.xml | 65 ++++ .../AdvanceCatalogSearchBundleProductTest.xml | 210 ++++++++++++ .../AdminAddDefaultVideoSimpleProductTest.xml | 46 +++ ...AdminAddDefaultVideoVirtualProductTest.xml | 34 ++ ...minRemoveDefaultVideoSimpleProductTest.xml | 49 +++ ...inRemoveDefaultVideoVirtualProductTest.xml | 34 ++ ...AdvanceCatalogSearchVirtualProductTest.xml | 81 +++++ .../AdvanceCatalogSearchConfigurableTest.xml | 307 ++++++++++++++++++ ...AddDefaultVideoDownloadableProductTest.xml | 49 +++ ...oveDefaultVideoDownloadableProductTest.xml | 49 +++ ...ceCatalogSearchDownloadableProductTest.xml | 111 +++++++ ...AdminAddDefaultVideoGroupedProductTest.xml | 60 ++++ ...inRemoveDefaultVideoGroupedProductTest.xml | 60 ++++ ...AdvanceCatalogSearchGroupedProductTest.xml | 170 ++++++++++ .../AdminAddDefaultVideoSimpleProductTest.xml | 33 ++ ...minRemoveDefaultVideoSimpleProductTest.xml | 36 ++ 17 files changed, 1459 insertions(+) create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoVirtualProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoVirtualProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml create mode 100644 app/code/Magento/ProductVideo/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml create mode 100644 app/code/Magento/ProductVideo/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml new file mode 100644 index 0000000000000..516f47ef8ac56 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminAddDefaultVideoBundleProductTest" extends="AdminAddDefaultVideoSimpleProductTest"> + <annotations> + <features value="Bundle"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to add default video for a Bundle Product"/> + <description value="Admin should be able to add default video for a Bundle Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-110"/> + <group value="Bundle"/> + </annotations> + <before> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + </before> + <after> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!-- Create a bundle product --> + <!-- Replacing steps in base AdminAddDefaultVideoSimpleProductTest --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage" after="waitForProductIndexPageLoad"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillMainProductForm" after="goToCreateProductPage"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <!-- Add two bundle items --> + <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="openBundleSection" after="addProductVideo"/> + <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption" after="openBundleSection"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleTitle" after="clickAddOption"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillBundleTitle" after="waitForBundleTitle"/> + <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectOptionBundleTitle" after="fillBundleTitle"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProducts" after="selectOptionBundleTitle"/> + <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProducts" after="waitForAddProducts"/> + <waitForPageLoad stepKey="waitForPageLoad" after="clickAddProducts"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku1" after="waitForPageLoad"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="checkOption1" after="filterProductGridBySku1"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku2" after="checkOption1"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="checkOption2" after="filterProductGridBySku2"/> + <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="addProducts" after="checkOption2"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillQty1" after="addProducts"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillQty2" before="saveProductForm"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml new file mode 100644 index 0000000000000..51fa38ce421c8 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminRemoveDefaultVideoBundleProductTest" extends="AdminRemoveDefaultVideoSimpleProductTest"> + <annotations> + <features value="Bundle"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to remove default video from a Bundle Product"/> + <description value="Admin should be able to remove default video from a Bundle Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-205"/> + <group value="Bundle"/> + </annotations> + <before> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + </before> + <after> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!-- Create a bundle product --> + <!-- Replacing steps in base AdminRemoveDefaultVideoSimpleProductTest --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage" after="waitForProductIndexPageLoad"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillMainProductForm" after="goToCreateProductPage"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <!-- Add two bundle items --> + <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="openBundleSection" after="addProductVideo"/> + <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption" after="openBundleSection"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleTitle" after="clickAddOption"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillBundleTitle" after="waitForBundleTitle"/> + <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectOptionBundleTitle" after="fillBundleTitle"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProducts" after="selectOptionBundleTitle"/> + <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProducts" after="waitForAddProducts"/> + <waitForPageLoad stepKey="waitForPageLoad" after="clickAddProducts"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku1" after="waitForPageLoad"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="checkOption1" after="filterProductGridBySku1"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku2" after="checkOption1"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="checkOption2" after="filterProductGridBySku2"/> + <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="addProducts" after="checkOption2"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillQty1" after="addProducts"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillQty2" before="saveProductForm"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml new file mode 100644 index 0000000000000..44ae4b7476aeb --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml @@ -0,0 +1,210 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchBundleByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product name"/> + <description value="Guest customer should be able to advance search Bundle product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-139"/> + <group value="Bundle"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropdownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <getData entity="AllBundleOptions" index="0" stepKey="getBundleOption"> + <requiredEntity createDataKey="product"/> + </getData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> + <test name="AdvanceCatalogSearchBundleBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product sku"/> + <description value="Guest customer should be able to advance search Bundle product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-143"/> + <group value="Bundle"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropdownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <getData entity="AllBundleOptions" index="0" stepKey="getBundleOption"> + <requiredEntity createDataKey="product"/> + </getData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> + <test name="AdvanceCatalogSearchBundleByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product description"/> + <description value="Guest customer should be able to advance search Bundle product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-242"/> + <group value="Bundle"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropdownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <getData entity="AllBundleOptions" index="0" stepKey="getBundleOption"> + <requiredEntity createDataKey="product"/> + </getData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> + <test name="AdvanceCatalogSearchBundleByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product short description"/> + <description value="Guest customer should be able to advance search Bundle product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-250"/> + <group value="Bundle"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropdownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <getData entity="AllBundleOptions" index="0" stepKey="getBundleOption"> + <requiredEntity createDataKey="product"/> + </getData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> + <test name="AdvanceCatalogSearchBundleByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product price"/> + <description value="Guest customer should be able to advance search Bundle product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-251"/> + <group value="Bundle"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropdownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <getData entity="AllBundleOptions" index="0" stepKey="getBundleOption"> + <requiredEntity createDataKey="product"/> + </getData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <getData entity="GetProduct" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="simple1"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="simple2"/> + </getData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml new file mode 100644 index 0000000000000..c0bc3bfc127f3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminAddDefaultVideoSimpleProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to add default product video for a Simple Product"/> + <description value="Admin should be able to add default product video for a Simple Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-111"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageAdd"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <argument name="product" value="ApiSimpleProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <argument name="product" value="ApiSimpleProduct"/> + </actionGroup> + + <!-- Save product --> + <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="ApiSimpleProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoVirtualProductTest.xml new file mode 100644 index 0000000000000..f48c352c5290a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoVirtualProductTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminAddDefaultVideoVirtualProductTest" extends="AdminAddDefaultVideoSimpleProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to add default product video for a Virtual Product"/> + <description value="Admin should be able to add default product video for a Virtual Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-109"/> + <group value="Catalog"/> + </annotations> + + <!-- Replacing steps in base AdminAddDefaultVideoSimpleProductTest --> + + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml new file mode 100644 index 0000000000000..6ec562322824d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminRemoveDefaultVideoSimpleProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to remove default product video from a Simple Product"/> + <description value="Admin should be able to remove default product video from a Simple Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-206"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageAdd"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <argument name="product" value="ApiSimpleProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <argument name="product" value="ApiSimpleProduct"/> + </actionGroup> + + <!-- Save product --> + <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + + <!-- Save product --> + <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="ApiSimpleProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoVirtualProductTest.xml new file mode 100644 index 0000000000000..e6d3978cad7bb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoVirtualProductTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminRemoveDefaultVideoVirtualProductTest" extends="AdminRemoveDefaultVideoSimpleProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to remove default product video from a Virtual Product"/> + <description value="Admin should be able to remove default product video from a Virtual Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-204"/> + <group value="Catalog"/> + </annotations> + + <!-- Replacing steps in base AdminRemoveDefaultVideoSimpleProductTest --> + + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml new file mode 100644 index 0000000000000..0eb8f5668751a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchVirtualProductByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product name"/> + <description value="Guest customer should be able to advance search virtual product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-137"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> + <test name="AdvanceCatalogSearchVirtualProductBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product sku"/> + <description value="Guest customer should be able to advance search virtual product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-162"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> + <test name="AdvanceCatalogSearchVirtualProductByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product description"/> + <description value="Guest customer should be able to advance search virtual product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-163"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> + <test name="AdvanceCatalogSearchVirtualProductByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product short description"/> + <description value="Guest customer should be able to advance search virtual product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-164"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> + <test name="AdvanceCatalogSearchVirtualProductByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product price"/> + <description value="Guest customer should be able to advance search virtual product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-165"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml new file mode 100644 index 0000000000000..454f9f5f29a7a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml @@ -0,0 +1,307 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchConfigurableByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search configurable product with product name"/> + <description value="Guest customer should be able to advance search configurable product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-138"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> + + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + </after> + </test> + <test name="AdvanceCatalogSearchConfigurableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search configurable product with product sku"/> + <description value="Guest customer should be able to advance search configurable product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-144"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> + + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + </after> + </test> + <test name="AdvanceCatalogSearchConfigurableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search configurable product with product description"/> + <description value="Guest customer should be able to advance search configurable product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-237"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> + + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + </after> + </test> + <test name="AdvanceCatalogSearchConfigurableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search configurable product with product short description"/> + <description value="Guest customer should be able to advance search configurable product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-240"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> + + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml new file mode 100644 index 0000000000000..63ed252360f00 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminAddDefaultVideoDownloadableProductTest" extends="AdminAddDefaultVideoSimpleProductTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to add default video for a Downloadable Product"/> + <description value="Admin should be able to add default video for a Downloadable Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-114"/> + <group value="Downloadable"/> + </annotations> + + <!-- Create a downloadable product --> + <!-- Replacing steps in base AdminAddDefaultVideoSimpleProductTest --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + + <!-- Add downloadable links --> + <click selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="openDownloadableSection" after="addProductVideo"/> + <checkOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkOptionIsDownloadable" after="openDownloadableSection"/> + <fillField userInput="{{downloadableData.link_title}}" selector="{{AdminProductDownloadableSection.linksTitleInput}}" stepKey="fillLinkTitle" after="checkOptionIsDownloadable"/> + <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately" after="fillLinkTitle"/> + <fillField userInput="{{downloadableData.sample_title}}" selector="{{AdminProductDownloadableSection.samplesTitleInput}}" stepKey="fillSampleTitle" after="checkOptionPurchaseSeparately"/> + <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableProductLinkWithMaxDownloads" after="fillSampleTitle"> + <argument name="link" value="downloadableLinkWithMaxDownloads"/> + </actionGroup> + <actionGroup ref="addDownloadableProductLink" stepKey="addDownloadableProductLink" before="saveProductForm"> + <argument name="link" value="downloadableLink"/> + </actionGroup> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml new file mode 100644 index 0000000000000..2210dd009318a --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminRemoveDefaultVideoDownloadableProductTest" extends="AdminRemoveDefaultVideoSimpleProductTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to remove default video from a Downloadable Product"/> + <description value="Admin should be able to remove default video from a Downloadable Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-207"/> + <group value="Downloadable"/> + </annotations> + + <!-- Create a downloadable product --> + <!-- Replacing steps in base AdminRemoveDefaultVideoSimpleProductTest --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + + <!-- Add downloadable links --> + <click selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="openDownloadableSection" after="addProductVideo"/> + <checkOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkOptionIsDownloadable" after="openDownloadableSection"/> + <fillField userInput="{{downloadableData.link_title}}" selector="{{AdminProductDownloadableSection.linksTitleInput}}" stepKey="fillLinkTitle" after="checkOptionIsDownloadable"/> + <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately" after="fillLinkTitle"/> + <fillField userInput="{{downloadableData.sample_title}}" selector="{{AdminProductDownloadableSection.samplesTitleInput}}" stepKey="fillSampleTitle" after="checkOptionPurchaseSeparately"/> + <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableProductLinkWithMaxDownloads" after="fillSampleTitle"> + <argument name="link" value="downloadableLinkWithMaxDownloads"/> + </actionGroup> + <actionGroup ref="addDownloadableProductLink" stepKey="addDownloadableProductLink" before="saveProductForm"> + <argument name="link" value="downloadableLink"/> + </actionGroup> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml new file mode 100644 index 0000000000000..af5d20b075d12 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchDownloadableByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product name"/> + <description value="Guest customer should be able to advance search Downloadable product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-142"/> + <group value="Downloadable"/> + </annotations> + <before> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + </test> + <test name="AdvanceCatalogSearchDownloadableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product sku"/> + <description value="Guest customer should be able to advance search Downloadable product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-252"/> + <group value="Downloadable"/> + </annotations> + <before> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + </test> + <test name="AdvanceCatalogSearchDownloadableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product description"/> + <description value="Guest customer should be able to advance search Downloadable product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-243"/> + <group value="Downloadable"/> + </annotations> + <before> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + </test> + <test name="AdvanceCatalogSearchDownloadableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product short description"/> + <description value="Guest customer should be able to advance search Downloadable product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-245"/> + <group value="Downloadable"/> + </annotations> + <before> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + </test> + <test name="AdvanceCatalogSearchDownloadableByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product price"/> + <description value="Guest customer should be able to advance search Downloadable product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-246"/> + <group value="Downloadable"/> + </annotations> + <before> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml new file mode 100644 index 0000000000000..d4c4655895051 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminAddDefaultVideoGroupedProductTest" extends="AdminAddDefaultVideoSimpleProductTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to add default video for a Grouped Product"/> + <description value="Admin should be able to add default video for a Grouped Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-108"/> + <group value="GroupedProduct"/> + </annotations> + <before> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + </before> + <after> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!-- Create a grouped product --> + <!-- Replacing steps in base AdminAddDefaultVideoSimpleProductTest --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + <actionGroup ref="fillGroupedProductForm" stepKey="fillMainProductForm"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + + <!-- Add two simple products to grouped product --> + <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollTo" after="addProductVideo"/> + <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductSection" after="scrollTo"/> + <click selector="body" stepKey="clickBody" after="openGroupedProductSection"/> + <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup" after="clickBody"/> + <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForFilter" after="clickAddProductsToGroup"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku1" after="waitForFilter"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="checkOption1" after="filterProductGridBySku1"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku2" after="checkOption1"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="checkOption2" after="filterProductGridBySku2"/> + <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="addSelectedProducts" before="saveProductForm"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml new file mode 100644 index 0000000000000..577fe9644a6fc --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminRemoveDefaultVideoGroupedProductTest" extends="AdminRemoveDefaultVideoSimpleProductTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to remove default video from a Grouped Product"/> + <description value="Admin should be able to remove default video from a Grouped Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-203"/> + <group value="GroupedProduct"/> + </annotations> + <before> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + </before> + <after> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!-- Create a grouped product --> + <!-- Replacing steps in base AdminRemoveDefaultVideoSimpleProductTest --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + <actionGroup ref="fillGroupedProductForm" stepKey="fillMainProductForm"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + + <!-- Add two simple products to grouped product --> + <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollTo" after="addProductVideo"/> + <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductSection" after="scrollTo"/> + <click selector="body" stepKey="clickBody" after="openGroupedProductSection"/> + <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup" after="clickBody"/> + <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForFilter" after="clickAddProductsToGroup"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku1" after="waitForFilter"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="checkOption1" after="filterProductGridBySku1"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku2" after="checkOption1"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="checkOption2" after="filterProductGridBySku2"/> + <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="addSelectedProducts" before="saveProductForm"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml new file mode 100644 index 0000000000000..0fd52ac4a65a4 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchGroupedProductByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product name"/> + <description value="Guest customer should be able to advance search Grouped product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-141"/> + <group value="GroupedProduct"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> + <test name="AdvanceCatalogSearchGroupedProductBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product sku"/> + <description value="Guest customer should be able to advance search Grouped product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-146"/> + <group value="GroupedProduct"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> + <test name="AdvanceCatalogSearchGroupedProductByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product description"/> + <description value="Guest customer should be able to advance search Grouped product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-282"/> + <group value="GroupedProduct"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> + <test name="AdvanceCatalogSearchGroupedProductByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product short description"/> + <description value="Guest customer should be able to advance search Grouped product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-283"/> + <group value="GroupedProduct"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> + <test name="AdvanceCatalogSearchGroupedProductByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product price"/> + <description value="Guest customer should be able to advance search Grouped product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-284"/> + <group value="GroupedProduct"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <getData entity="GetProduct3" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="simple1"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="simple2"/> + </getData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml new file mode 100644 index 0000000000000..bd7cc0cdf5b4a --- /dev/null +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminAddDefaultVideoSimpleProductTest"> + <annotations> + <group value="ProductVideo"/> + </annotations> + <before> + <!-- Set product video Youtube api key configuration --> + <createData entity="ProductVideoYoutubeApiKeyConfig" stepKey="setStoreConfig" after="loginAsAdmin"/> + </before> + <after> + <!-- Set product video configuration to default --> + <createData entity="DefaultProductVideoConfig" stepKey="setStoreDefaultConfig" before="amOnLogoutPage"/> + </after> + + <!-- Add product video --> + <actionGroup ref="addProductVideo" stepKey="addProductVideo" after="fillMainProductForm"/> + + <!-- Assert product video in admin product form --> + <actionGroup ref="assertProductVideoAdminProductPage" stepKey="assertProductVideoAdminProductPage" after="saveProductForm"/> + + <!-- Assert product video in storefront product page --> + <actionGroup ref="assertProductVideoStorefrontProductPage" stepKey="assertProductVideoStorefrontProductPage" after="AssertProductInStorefrontProductPage"/> + </test> +</tests> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml new file mode 100644 index 0000000000000..f5a7886fed45c --- /dev/null +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminRemoveDefaultVideoSimpleProductTest"> + <annotations> + <group value="ProductVideo"/> + </annotations> + <before> + <!-- Set product video Youtube api key configuration --> + <createData entity="ProductVideoYoutubeApiKeyConfig" stepKey="setStoreConfig" after="loginAsAdmin"/> + </before> + <after> + <!-- Set product video configuration to default --> + <createData entity="DefaultProductVideoConfig" stepKey="setStoreDefaultConfig" before="amOnLogoutPage"/> + </after> + + <!-- Add product video --> + <actionGroup ref="addProductVideo" stepKey="addProductVideo" after="fillMainProductForm"/> + + <!-- Remove product video --> + <actionGroup ref="removeProductVideo" stepKey="removeProductVideo" after="saveProductForm"/> + + <!-- Assert product video not in admin product form --> + <actionGroup ref="assertProductVideoNotInAdminProductPage" stepKey="assertProductVideoNotInAdminProductPage" after="saveProductFormAfterRemove"/> + + <!-- Assert product video not in storefront product page --> + <actionGroup ref="assertProductVideoNotInStorefrontProductPage" stepKey="assertProductVideoNotInStorefrontProductPage" after="AssertProductInStorefrontProductPage"/> + </test> +</tests> From f1de6966343710cac68049279ef7b0017dc43e7c Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Wed, 25 Jul 2018 09:50:09 -0500 Subject: [PATCH 0504/1171] MC-2294: Architectural Review for MC-2294 - Updated with feedback --- .../Magento/Ui/view/base/web/js/form/element/file-uploader.js | 4 ++++ .../Ui/view/base/web/js/form/element/image-uploader.js | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js index b583d0be69f34..61be8c3dc505f 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js @@ -168,6 +168,10 @@ define([ processFile: function (file) { file.previewType = this.getFilePreviewType(file); + if (!file.id && file.name) { + file.id = Base64.mageEncode(file.name).replace(/g/); + } + this.observe.call(file, true, [ 'previewWidth', 'previewHeight' diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js index dac325b8aa8b4..0ae09f14fa946 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/image-uploader.js @@ -52,14 +52,12 @@ define([ */ addFileFromMediaGallery: function (imageUploader, e) { var $buttonEl = $(e.target), - fileId = $buttonEl.data('id'), fileSize = $buttonEl.data('size'), fileMimeType = $buttonEl.data('mime-type'), filePathname = $buttonEl.val(), fileBasename = filePathname.split('/').pop(); this.addFile({ - id: fileId, type: fileMimeType, name: fileBasename, size: fileSize, From 698fc7ab4405bec244d2b7f29278addbb2c83d79 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Wed, 25 Jul 2018 09:57:56 -0500 Subject: [PATCH 0505/1171] MC-2294: Architectural Review for MC-2294 - Updated with feedback --- .../code/Magento/Ui/base/js/form/element/image-uploader.test.js | 2 -- lib/web/mage/adminhtml/browser.js | 1 - 2 files changed, 3 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/image-uploader.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/image-uploader.test.js index e7745103db356..31d1c1ce27e96 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/image-uploader.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/image-uploader.test.js @@ -35,7 +35,6 @@ define([ spyOn(component, 'addFile'); $el.data({ - id: 'ABC123--', 'size': 1024, 'mime-type': 'image/png' }); @@ -47,7 +46,6 @@ define([ }); expect(component.addFile).toHaveBeenCalledWith({ - id: 'ABC123--', type: 'image/png', name: 'something.png', url: '/pub/media/something.png', diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 717098110ceb5..9528e023d3ee0 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -276,7 +276,6 @@ define([ } else { targetEl .val(data) - .data('id', fileRow.attr('id')) .data('size', fileRow.data('size')) .data('mime-type', fileRow.data('mime-type')) .trigger('change'); From acccc1a5cb2d276872efcc9a115853b7aeeaafd7 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 25 Jul 2018 18:06:30 +0300 Subject: [PATCH 0506/1171] MAGETWO-93786: Payment APIs webhooks are now expecting a form key --- .../Directpost/Payment/BackendResponse.php | 22 +++++++++++++++++- .../Directpost/Payment/Response.php | 23 ++++++++++++++++++- .../Magento/Paypal/Controller/Ipn/Index.php | 22 +++++++++++++++++- .../Controller/Payflow/CancelPayment.php | 23 ++++++++++++++++++- .../Paypal/Controller/Payflow/ReturnUrl.php | 22 +++++++++++++++++- .../Paypal/Controller/Payflow/SilentPost.php | 23 ++++++++++++++++++- .../Controller/Transparent/Response.php | 22 +++++++++++++++++- 7 files changed, 150 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php index 3ad9f470909bf..70565ea8ac65f 100644 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php +++ b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php @@ -10,12 +10,15 @@ use Magento\Authorizenet\Model\Directpost; use Magento\Authorizenet\Model\DirectpostFactory; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Registry; use Psr\Log\LoggerInterface; -class BackendResponse extends \Magento\Authorizenet\Controller\Directpost\Payment +class BackendResponse extends \Magento\Authorizenet\Controller\Directpost\Payment implements CsrfAwareActionInterface { /** * @var LoggerInterface @@ -48,6 +51,23 @@ public function __construct( $this->logger = $logger ?: $this->_objectManager->get(LoggerInterface::class); } + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } + /** * Response action. * Action for Authorize.net SIM Relay Request. diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Response.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Response.php index d88e77d6c4e28..d562df9fb24a9 100644 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Response.php +++ b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Response.php @@ -6,8 +6,29 @@ */ namespace Magento\Authorizenet\Controller\Directpost\Payment; -class Response extends \Magento\Authorizenet\Controller\Directpost\Payment +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; + +class Response extends \Magento\Authorizenet\Controller\Directpost\Payment implements CsrfAwareActionInterface { + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } + /** * Response action. * Action for Authorize.net SIM Relay Request. diff --git a/app/code/Magento/Paypal/Controller/Ipn/Index.php b/app/code/Magento/Paypal/Controller/Ipn/Index.php index ca2670292ec8a..8a7b755581509 100644 --- a/app/code/Magento/Paypal/Controller/Ipn/Index.php +++ b/app/code/Magento/Paypal/Controller/Ipn/Index.php @@ -7,12 +7,15 @@ namespace Magento\Paypal\Controller\Ipn; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; use Magento\Framework\Exception\RemoteServiceUnavailableException; /** * Unified IPN controller for all supported PayPal methods */ -class Index extends \Magento\Framework\App\Action\Action +class Index extends \Magento\Framework\App\Action\Action implements CsrfAwareActionInterface { /** * @var \Psr\Log\LoggerInterface @@ -39,6 +42,23 @@ public function __construct( parent::__construct($context); } + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } + /** * Instantiate IPN model and pass IPN request to it * diff --git a/app/code/Magento/Paypal/Controller/Payflow/CancelPayment.php b/app/code/Magento/Paypal/Controller/Payflow/CancelPayment.php index 39cc02942cf76..71ae6d4975fbe 100644 --- a/app/code/Magento/Paypal/Controller/Payflow/CancelPayment.php +++ b/app/code/Magento/Paypal/Controller/Payflow/CancelPayment.php @@ -6,8 +6,29 @@ */ namespace Magento\Paypal\Controller\Payflow; -class CancelPayment extends \Magento\Paypal\Controller\Payflow +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; + +class CancelPayment extends \Magento\Paypal\Controller\Payflow implements CsrfAwareActionInterface { + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } + /** * When a customer cancel payment from payflow gateway. * diff --git a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php index a370eeb40eafd..78d591c598e6d 100644 --- a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php +++ b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php @@ -6,11 +6,14 @@ */ namespace Magento\Paypal\Controller\Payflow; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; use Magento\Paypal\Controller\Payflow; use Magento\Paypal\Model\Config; use Magento\Sales\Model\Order; -class ReturnUrl extends Payflow +class ReturnUrl extends Payflow implements CsrfAwareActionInterface { /** * @var array of allowed order states on frontend @@ -30,6 +33,23 @@ class ReturnUrl extends Payflow Config::METHOD_PAYFLOWLINK ]; + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } + /** * When a customer return to website from payflow gateway. * diff --git a/app/code/Magento/Paypal/Controller/Payflow/SilentPost.php b/app/code/Magento/Paypal/Controller/Payflow/SilentPost.php index e686c91fd4c9f..2d6a8f80bafd8 100644 --- a/app/code/Magento/Paypal/Controller/Payflow/SilentPost.php +++ b/app/code/Magento/Paypal/Controller/Payflow/SilentPost.php @@ -6,8 +6,29 @@ */ namespace Magento\Paypal\Controller\Payflow; -class SilentPost extends \Magento\Paypal\Controller\Payflow +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; + +class SilentPost extends \Magento\Paypal\Controller\Payflow implements CsrfAwareActionInterface { + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } + /** * Get response from PayPal by silent post method * diff --git a/app/code/Magento/Paypal/Controller/Transparent/Response.php b/app/code/Magento/Paypal/Controller/Transparent/Response.php index c54dd529588b9..b62d337b2a8fd 100644 --- a/app/code/Magento/Paypal/Controller/Transparent/Response.php +++ b/app/code/Magento/Paypal/Controller/Transparent/Response.php @@ -5,6 +5,9 @@ */ namespace Magento\Paypal\Controller\Transparent; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; use Magento\Framework\Registry; use Magento\Framework\App\Action\Context; use Magento\Framework\View\Result\LayoutFactory; @@ -20,7 +23,7 @@ /** * Class Response */ -class Response extends \Magento\Framework\App\Action\Action +class Response extends \Magento\Framework\App\Action\Action implements CsrfAwareActionInterface { /** * Core registry @@ -91,6 +94,23 @@ public function __construct( $this->paymentFailures = $paymentFailures ?: $this->_objectManager->get(PaymentFailuresInterface::class); } + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } + /** * @return ResultInterface */ From c267f9e470443e84d0698a11766a0af29243326c Mon Sep 17 00:00:00 2001 From: KevinBKozan <kkozan@magento.com> Date: Wed, 25 Jul 2018 10:14:36 -0500 Subject: [PATCH 0507/1171] MQE-1112: Bump MFTF version in Magento - codesniffer fixes - copyright add - BP defined | define - removing flaky test step --- .../Mftf/ActionGroup/StorefrontProductPageActionGroup.xml | 1 - dev/tests/acceptance/tests/_bootstrap.php | 4 ++++ dev/tests/acceptance/utils/command.php | 6 +++++- dev/tests/functional/bootstrap.php | 1 + .../functional/lib/Magento/Mtf/Util/Generate/Factory.php | 1 - dev/tests/functional/utils/generate.php | 5 +++-- 6 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml index d688a1dc2844e..43fef7330c812 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml @@ -13,7 +13,6 @@ <argument name="productName"/> </arguments> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdding}}" stepKey="waitForElementVisibleAddToCartButtonTitleIsAdding"/> <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdding}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdding"/> <waitForElementVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdded}}" stepKey="waitForElementVisibleAddToCartButtonTitleIsAdded"/> <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdded}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdded"/> diff --git a/dev/tests/acceptance/tests/_bootstrap.php b/dev/tests/acceptance/tests/_bootstrap.php index ee39fc1a09da6..2fa2da03d8f8b 100644 --- a/dev/tests/acceptance/tests/_bootstrap.php +++ b/dev/tests/acceptance/tests/_bootstrap.php @@ -1,4 +1,8 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ //TODO remove this file once MFTF is fully decoupled from Magento // Need to load in the root level autload file diff --git a/dev/tests/acceptance/utils/command.php b/dev/tests/acceptance/utils/command.php index 28644fa5fe66c..6b8208f55d495 100644 --- a/dev/tests/acceptance/utils/command.php +++ b/dev/tests/acceptance/utils/command.php @@ -14,7 +14,11 @@ $php = PHP_BINARY ?: (PHP_BINDIR ? PHP_BINDIR . '/php' : 'php'); $valid = validateCommand($command); if ($valid) { - exec(escapeCommand($php . ' -f ../../../../bin/magento ' . $command) . " $arguments" . " 2>&1", $output, $exitCode); + exec( + escapeCommand($php . ' -f ../../../../bin/magento ' . $command) . " $arguments" ." 2>&1", + $output, + $exitCode + ); if ($exitCode == 0) { http_response_code(202); } else { diff --git a/dev/tests/functional/bootstrap.php b/dev/tests/functional/bootstrap.php index be36937866d37..f0b5b126756ab 100644 --- a/dev/tests/functional/bootstrap.php +++ b/dev/tests/functional/bootstrap.php @@ -6,6 +6,7 @@ defined('MTF_BOOT_FILE') || define('MTF_BOOT_FILE', __FILE__); defined('MTF_BP') || define('MTF_BP', str_replace('\\', '/', (__DIR__))); +defined('BP') || define('BP', str_replace('\\', '/', dirname(dirname(dirname((__DIR__)))))); defined('MTF_TESTS_PATH') || define('MTF_TESTS_PATH', MTF_BP . '/tests/app/'); defined('MTF_STATES_PATH') || define('MTF_STATES_PATH', MTF_BP . '/lib/Magento/Mtf/App/State/'); diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Generate/Factory.php b/dev/tests/functional/lib/Magento/Mtf/Util/Generate/Factory.php index 330b180843ce9..192cab5751986 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Generate/Factory.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Generate/Factory.php @@ -6,7 +6,6 @@ namespace Magento\Mtf\Util\Generate; - /** * Factory classes generator. * diff --git a/dev/tests/functional/utils/generate.php b/dev/tests/functional/utils/generate.php index 27a8fe4e3e40b..61bcc3523f551 100644 --- a/dev/tests/functional/utils/generate.php +++ b/dev/tests/functional/utils/generate.php @@ -26,7 +26,8 @@ \Magento\Mtf\Util\Generate\GenerateResult::displayResults(); -function deleteDirectory($dir) { +function deleteDirectory($dir) +{ if (!file_exists($dir)) { return true; } @@ -47,4 +48,4 @@ function deleteDirectory($dir) { function generateModuleSequence() { require_once "generate/moduleSequence.php"; -} \ No newline at end of file +} From bc61fad05af177ff69e4e2622bc6e3867f2ce577 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 25 Jul 2018 18:25:13 +0300 Subject: [PATCH 0508/1171] MAGETWO-93786: Payment APIs webhooks are now expecting a form key --- app/code/Magento/Paypal/Controller/Transparent/Response.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Paypal/Controller/Transparent/Response.php b/app/code/Magento/Paypal/Controller/Transparent/Response.php index b62d337b2a8fd..33a93a37b50cf 100644 --- a/app/code/Magento/Paypal/Controller/Transparent/Response.php +++ b/app/code/Magento/Paypal/Controller/Transparent/Response.php @@ -22,6 +22,8 @@ /** * Class Response + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Response extends \Magento\Framework\App\Action\Action implements CsrfAwareActionInterface { From 5416edfc7de32a120d8e15b9a786e161b2be33e2 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Wed, 25 Jul 2018 10:29:17 -0500 Subject: [PATCH 0509/1171] MC-3080: Admin can create product attribute with picked color swatch - Improved test name and file name --- ...onfigureSwatchesTest.xml => AdminCreateVisualSwatchTest.xml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename app/code/Magento/Swatches/Test/Mftf/Test/{AdminConfigureSwatchesTest.xml => AdminCreateVisualSwatchTest.xml} (99%) diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml similarity index 99% rename from app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml rename to app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml index 0db67d0750b36..774da705ea03d 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminConfigureSwatchesTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="AdminConfigureSwatchesTest"> + <test name="AdminCreateVisualSwatchTest"> <annotations> <features value="Swatches"/> <stories value="Create/configure swatches"/> From 0670ce320d7d47a6736fb50c0ef35865b6a6321b Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Wed, 25 Jul 2018 21:28:09 +0530 Subject: [PATCH 0510/1171] Remove commented code --- app/code/Magento/Backend/Block/Dashboard/Bar.php | 8 -------- .../Magento/Newsletter/Block/Adminhtml/Template/Edit.php | 6 ------ app/code/Magento/Review/Block/Adminhtml/Add/Form.php | 5 ----- app/code/Magento/Sales/Model/Order/Creditmemo.php | 7 ------- .../Sales/view/adminhtml/templates/order/totals.phtml | 8 -------- 5 files changed, 34 deletions(-) diff --git a/app/code/Magento/Backend/Block/Dashboard/Bar.php b/app/code/Magento/Backend/Block/Dashboard/Bar.php index 29557f12c1093..7ccb2d51ccd1b 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Bar.php +++ b/app/code/Magento/Backend/Block/Dashboard/Bar.php @@ -38,14 +38,6 @@ public function getTotals() */ public function addTotal($label, $value, $isQuantity = false) { - /*if (!$isQuantity) { - $value = $this->format($value); - $decimals = substr($value, -2); - $value = substr($value, 0, -2); - } else { - $value = ($value != '')?$value:0; - $decimals = ''; - }*/ if (!$isQuantity) { $value = $this->format($value); } diff --git a/app/code/Magento/Newsletter/Block/Adminhtml/Template/Edit.php b/app/code/Magento/Newsletter/Block/Adminhtml/Template/Edit.php index 33c3706183368..236101745b98e 100644 --- a/app/code/Magento/Newsletter/Block/Adminhtml/Template/Edit.php +++ b/app/code/Magento/Newsletter/Block/Adminhtml/Template/Edit.php @@ -67,12 +67,6 @@ public function getModel() */ protected function _prepareLayout() { - // Load Wysiwyg on demand and Prepare layout -// $block = $this->getLayout()->getBlock('head'); -// if ($this->_wysiwygConfig->isEnabled() && $block) { -// $block->setCanLoadTinyMce(true); -// } - $this->getToolbar()->addChild( 'back_button', \Magento\Backend\Block\Widget\Button::class, diff --git a/app/code/Magento/Review/Block/Adminhtml/Add/Form.php b/app/code/Magento/Review/Block/Adminhtml/Add/Form.php index 2d619725ae201..04e6343eb43ca 100644 --- a/app/code/Magento/Review/Block/Adminhtml/Add/Form.php +++ b/app/code/Magento/Review/Block/Adminhtml/Add/Form.php @@ -142,11 +142,6 @@ protected function _prepareForm() $fieldset->addField('product_id', 'hidden', ['name' => 'product_id']); - /*$gridFieldset = $form->addFieldset('add_review_grid', array('legend' => __('Please select a product'))); - $gridFieldset->addField('products_grid', 'note', array( - 'text' => $this->getLayout()->createBlock(\Magento\Review\Block\Adminhtml\Product\Grid::class)->toHtml(), - ));*/ - $form->setMethod('post'); $form->setUseContainer(true); $form->setId('edit_form'); diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo.php b/app/code/Magento/Sales/Model/Order/Creditmemo.php index 19aac897c21e2..739228d839fea 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo.php @@ -582,13 +582,6 @@ public function getCommentsCollection($reload = false) { $collection = $this->_commentCollectionFactory->create()->setCreditmemoFilter($this->getId()) ->setCreatedAtOrder(); -// -// $this->setComments($comments); -// /** -// * When credit memo created with adding comment, -// * comments collection must be loaded before we added this comment. -// */ -// $this->getComments()->load(); if ($this->getId()) { foreach ($collection as $comment) { diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/totals.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/totals.phtml index d23ebac84e6c2..27b82505a11d3 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/totals.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/totals.phtml @@ -7,14 +7,6 @@ // @codingStandardsIgnoreFile ?> -<?php /*$_source = $block->getSource(); ?> -<?php $block->setPriceDataObject($_source) ?> -<?php if ($_source): ?> -<table width="100%"> - <?= $block->getChildHtml('main') ?> - <?= $block->getChildHtml('footer') ?> -</table> -<?php endif;*/ ?> <table class="data-table admin__table-secondary order-subtotal-table"> <?php $_totals = $block->getTotals('footer')?> From 2fada20f79a9150bf4c0091e37e919a59d11a067 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Wed, 25 Jul 2018 11:01:28 -0500 Subject: [PATCH 0511/1171] MC-3080: Admin can create product attribute with picked color swatch - Add a conditionalClick after creating the configurable product --- .../Mftf/Test/AdminCreateVisualSwatchTest.xml | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml index 774da705ea03d..67750ac931f0b 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml @@ -19,6 +19,7 @@ <group value="Swatches"/> </annotations> <before> + <createData entity="ApiCategory" stepKey="createCategory"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> @@ -102,15 +103,17 @@ <!-- Create a configurable product to verify the storefront with --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> - <waitForPageLoad time="30" stepKey="waitForProductGrid"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct"> - <argument name="product" value="BaseConfigurableProduct"/> - </actionGroup> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> - <argument name="product" value="BaseConfigurableProduct"/> - </actionGroup> + <waitForPageLoad stepKey="waitForProductGridPage"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> + <click selector="{{AdminProductGridActionSection.addConfigurableProduct}}" stepKey="clickOnAddConfigurableProduct"/> + <fillField userInput="{{_defaultProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{_defaultProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="{{_defaultProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{_defaultProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> <fillField userInput="{{_defaultProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <!-- Create configurations based off the Text Swatch we created earlier --> <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickCreateConfigurations"/> <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickFilters"/> @@ -120,11 +123,20 @@ <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyUniquePricesByAttributeToEachSku}}" stepKey="clickOnApplyUniquePricesByAttributeToEachSku"/> + <selectOption selector="{{AdminCreateProductConfigurationsPanel.selectAttribute}}" userInput="Color" stepKey="selectAttributes"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute1}}" userInput="10" stepKey="fillAttributePrice1"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute2}}" userInput="20" stepKey="fillAttributePrice2"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute3}}" userInput="30" stepKey="fillAttributePrice3"/> <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="1" stepKey="enterAttributeQuantity"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="99" stepKey="enterAttributeQuantity"/> <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> + <!-- conditionalClick is necessary because this popup appears in Jenkins but not locally. I cannot figure out why. --> + <conditionalClick selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" dependentSelector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" visible="true" stepKey="clickOnConfirmInPopup"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> + <seeInTitle userInput="{{_defaultProduct.name}}" stepKey="seeProductNameInTitle"/> <!-- Go to the product page and see text swatch options --> <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnProductPage"/> From 15e3c07f3aeb94d4147e49401d409afc3dcd93c9 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Wed, 25 Jul 2018 19:29:45 +0300 Subject: [PATCH 0512/1171] Delete all unused imports of lib/internal/Magento --- lib/internal/Magento/Framework/Api/CombinedFilterGroup.php | 2 -- .../Magento/Framework/App/Cache/Frontend/Factory.php | 2 -- lib/internal/Magento/Framework/Archive/Tar.php | 1 - .../Framework/Composer/Test/Unit/ComposerInformationTest.php | 1 - .../Console/Test/Unit/QuestionPerformer/YesNoTest.php | 2 -- lib/internal/Magento/Framework/DB/AbstractMapper.php | 1 - lib/internal/Magento/Framework/DB/Select/GroupRenderer.php | 1 - lib/internal/Magento/Framework/DB/SelectFactory.php | 1 - lib/internal/Magento/Framework/DB/TemporaryTableService.php | 1 - lib/internal/Magento/Framework/DB/Test/Unit/SelectTest.php | 2 -- lib/internal/Magento/Framework/Data/Form/Element/Date.php | 1 - lib/internal/Magento/Framework/Data/Form/FilterFactory.php | 1 - .../DataObject/Test/Unit/Copy/Config/SchemaLocatorTest.php | 2 -- .../Magento/Framework/Encryption/Test/Unit/EncryptorTest.php | 1 - .../Framework/EntityManager/AbstractModelHydrator.php | 2 -- lib/internal/Magento/Framework/EntityManager/Hydrator.php | 2 -- .../Framework/EntityManager/Observer/BeforeEntityDelete.php | 1 - .../Framework/EntityManager/Operation/ExtensionPool.php | 1 - .../Magento/Framework/EntityManager/OperationPool.php | 1 - .../Magento/Framework/Event/Test/Unit/ObserverTest.php | 2 -- lib/internal/Magento/Framework/File/Uploader.php | 2 -- lib/internal/Magento/Framework/Filesystem/Io/Ftp.php | 1 - lib/internal/Magento/Framework/Filesystem/Io/IoInterface.php | 2 -- .../Framework/Filesystem/Test/Unit/File/ReadFactoryTest.php | 2 -- lib/internal/Magento/Framework/Filter/DataObject/Grid.php | 2 -- .../Magento/Framework/Indexer/SaveHandler/IndexerHandler.php | 1 - .../Magento/Framework/Indexer/SaveHandlerFactory.php | 1 - lib/internal/Magento/Framework/Indexer/StructureFactory.php | 2 -- .../Framework/Indexer/Test/Unit/IndexStructureTest.php | 1 - .../Magento/Framework/Locale/Test/Unit/CurrencyTest.php | 1 - .../Framework/Message/Test/Unit/AbstractMessageTest.php | 2 -- .../Framework/Model/ResourceModel/Db/DeleteEntityRow.php | 1 - .../Framework/Model/ResourceModel/Db/Relation/ActionPool.php | 1 - .../Model/Test/Unit/AbstractExtensibleModelTest.php | 1 - .../ResourceModel/Db/Collection/AbstractCollectionTest.php | 1 - lib/internal/Magento/Framework/Module/Dir.php | 1 - .../Magento/Framework/Module/Test/Unit/Dir/ReaderTest.php | 1 - .../Magento/Framework/Module/Test/Unit/ManagerTest.php | 2 -- lib/internal/Magento/Framework/Mview/Config/Converter.php | 1 - .../Magento/Framework/Mview/Config/SchemaLocator.php | 2 -- .../Magento/Framework/Mview/Test/Unit/Config/ReaderTest.php | 2 -- lib/internal/Magento/Framework/Mview/View/Changelog.php | 1 - .../Magento/Framework/ObjectManager/DefinitionFactory.php | 3 --- .../Test/Unit/Code/Generator/RepositoryTest.php | 1 - .../ObjectManager/Test/Unit/Helper/CompositeTest.php | 1 - .../Framework/Reflection/CustomAttributesProcessor.php | 3 --- .../Framework/Reflection/ExtensionAttributesProcessor.php | 1 - lib/internal/Magento/Framework/Reflection/FieldNamer.php | 5 ----- .../Framework/Reflection/Test/Unit/NameFinderTest.php | 1 - .../Search/Adapter/Mysql/Aggregation/Builder/Term.php | 1 - .../Framework/Search/Test/Unit/Adapter/Mysql/AdapterTest.php | 1 - .../Serialize/Test/Unit/Serializer/SerializeTest.php | 1 - .../Magento/Framework/Session/SaveHandler/DbTable.php | 1 - .../Session/Test/Unit/SaveHandler/Redis/LoggerTest.php | 1 - .../Stdlib/Test/Unit/Cookie/_files/setcookie_mock.php | 1 - lib/internal/Magento/Framework/System/Dirs.php | 2 -- lib/internal/Magento/Framework/System/Ftp.php | 2 -- .../Framework/View/Asset/PreProcessor/AlternativeSource.php | 1 - lib/internal/Magento/Framework/View/Layout.php | 1 - .../Magento/Framework/Webapi/ServiceOutputProcessor.php | 1 - lib/internal/Magento/Framework/Xml/Security.php | 1 - 61 files changed, 88 deletions(-) diff --git a/lib/internal/Magento/Framework/Api/CombinedFilterGroup.php b/lib/internal/Magento/Framework/Api/CombinedFilterGroup.php index 21d95b72fed1c..190085ddcae00 100644 --- a/lib/internal/Magento/Framework/Api/CombinedFilterGroup.php +++ b/lib/internal/Magento/Framework/Api/CombinedFilterGroup.php @@ -7,8 +7,6 @@ namespace Magento\Framework\Api; -use Magento\Framework\Api\AbstractSimpleObject; -use Magento\Framework\DB\Select; use Magento\Framework\Exception\InputException; use Magento\Framework\Phrase; diff --git a/lib/internal/Magento/Framework/App/Cache/Frontend/Factory.php b/lib/internal/Magento/Framework/App/Cache/Frontend/Factory.php index f2bc4eceb507d..f84dd304643da 100644 --- a/lib/internal/Magento/Framework/App/Cache/Frontend/Factory.php +++ b/lib/internal/Magento/Framework/App/Cache/Frontend/Factory.php @@ -10,9 +10,7 @@ namespace Magento\Framework\App\Cache\Frontend; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\App\ResourceConnection; use Magento\Framework\Filesystem; -use Magento\Framework\Filesystem\DriverInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) diff --git a/lib/internal/Magento/Framework/Archive/Tar.php b/lib/internal/Magento/Framework/Archive/Tar.php index e2a070503f61f..7fe1255e5b859 100644 --- a/lib/internal/Magento/Framework/Archive/Tar.php +++ b/lib/internal/Magento/Framework/Archive/Tar.php @@ -12,7 +12,6 @@ namespace Magento\Framework\Archive; use Magento\Framework\Archive\Helper\File; -use Magento\Framework\Filesystem\DriverInterface; class Tar extends \Magento\Framework\Archive\AbstractArchive implements \Magento\Framework\Archive\ArchiveInterface { diff --git a/lib/internal/Magento/Framework/Composer/Test/Unit/ComposerInformationTest.php b/lib/internal/Magento/Framework/Composer/Test/Unit/ComposerInformationTest.php index 27a222e6bceb9..bf67f73009a08 100644 --- a/lib/internal/Magento/Framework/Composer/Test/Unit/ComposerInformationTest.php +++ b/lib/internal/Magento/Framework/Composer/Test/Unit/ComposerInformationTest.php @@ -7,7 +7,6 @@ use Composer\Composer; use Composer\Package\Locker; -use Magento\Framework\Composer\ComposerInformation; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; class ComposerInformationTest extends \PHPUnit\Framework\TestCase diff --git a/lib/internal/Magento/Framework/Console/Test/Unit/QuestionPerformer/YesNoTest.php b/lib/internal/Magento/Framework/Console/Test/Unit/QuestionPerformer/YesNoTest.php index d8552f2ba5f31..4a951aa2987a8 100644 --- a/lib/internal/Magento/Framework/Console/Test/Unit/QuestionPerformer/YesNoTest.php +++ b/lib/internal/Magento/Framework/Console/Test/Unit/QuestionPerformer/YesNoTest.php @@ -11,8 +11,6 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\QuestionFactory; use Symfony\Component\Console\Question\Question; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Phrase; class YesNoTest extends \PHPUnit\Framework\TestCase { diff --git a/lib/internal/Magento/Framework/DB/AbstractMapper.php b/lib/internal/Magento/Framework/DB/AbstractMapper.php index bce53caef8a39..9d043d6de7fbc 100644 --- a/lib/internal/Magento/Framework/DB/AbstractMapper.php +++ b/lib/internal/Magento/Framework/DB/AbstractMapper.php @@ -10,7 +10,6 @@ use Magento\Framework\Data\ObjectFactory; use Magento\Framework\DB\Adapter\AdapterInterface; use Psr\Log\LoggerInterface as Logger; -use Magento\Framework\DataObject; /** * Class AbstractMapper diff --git a/lib/internal/Magento/Framework/DB/Select/GroupRenderer.php b/lib/internal/Magento/Framework/DB/Select/GroupRenderer.php index df046c92203c2..692843aaa37e3 100644 --- a/lib/internal/Magento/Framework/DB/Select/GroupRenderer.php +++ b/lib/internal/Magento/Framework/DB/Select/GroupRenderer.php @@ -6,7 +6,6 @@ namespace Magento\Framework\DB\Select; use Magento\Framework\DB\Select; -use Magento\Framework\DB\Platform; use Magento\Framework\DB\Platform\Quote; /** diff --git a/lib/internal/Magento/Framework/DB/SelectFactory.php b/lib/internal/Magento/Framework/DB/SelectFactory.php index bdbc35b395af7..3c64e78839c4d 100644 --- a/lib/internal/Magento/Framework/DB/SelectFactory.php +++ b/lib/internal/Magento/Framework/DB/SelectFactory.php @@ -6,7 +6,6 @@ namespace Magento\Framework\DB; -use Magento\Framework\DB\Select; use Magento\Framework\DB\Select\SelectRenderer; use Magento\Framework\DB\Adapter\AdapterInterface; diff --git a/lib/internal/Magento/Framework/DB/TemporaryTableService.php b/lib/internal/Magento/Framework/DB/TemporaryTableService.php index 03c1a9b9aa7f6..881ebbe5c85ad 100644 --- a/lib/internal/Magento/Framework/DB/TemporaryTableService.php +++ b/lib/internal/Magento/Framework/DB/TemporaryTableService.php @@ -6,7 +6,6 @@ namespace Magento\Framework\DB; use Magento\Framework\DB\Adapter\AdapterInterface; -use Magento\Framework\DB\Select; /** * Class TemporaryTableService creates a temporary table in mysql from a Magento\Framework\DB\Select. diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/SelectTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/SelectTest.php index 7ce0ba4e4eb45..2be211a6c3da0 100644 --- a/lib/internal/Magento/Framework/DB/Test/Unit/SelectTest.php +++ b/lib/internal/Magento/Framework/DB/Test/Unit/SelectTest.php @@ -7,8 +7,6 @@ use \Magento\Framework\DB\Select; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - /** * Class SelectTest * @SuppressWarnings(PHPMD.CouplingBetweenObjects) diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Date.php b/lib/internal/Magento/Framework/Data/Form/Element/Date.php index c4661c92e8c49..ed5457edc7f3f 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Date.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Date.php @@ -12,7 +12,6 @@ namespace Magento\Framework\Data\Form\Element; use Magento\Framework\Escaper; -use Magento\Framework\Stdlib\DateTime; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; class Date extends AbstractElement diff --git a/lib/internal/Magento/Framework/Data/Form/FilterFactory.php b/lib/internal/Magento/Framework/Data/Form/FilterFactory.php index 2763e8bd17cce..22dfabb8fb565 100644 --- a/lib/internal/Magento/Framework/Data/Form/FilterFactory.php +++ b/lib/internal/Magento/Framework/Data/Form/FilterFactory.php @@ -7,7 +7,6 @@ use Magento\Framework\Data\Form\Filter\FilterInterface; use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\Phrase; class FilterFactory { diff --git a/lib/internal/Magento/Framework/DataObject/Test/Unit/Copy/Config/SchemaLocatorTest.php b/lib/internal/Magento/Framework/DataObject/Test/Unit/Copy/Config/SchemaLocatorTest.php index c7417439bac1f..0d35b3a4264e0 100644 --- a/lib/internal/Magento/Framework/DataObject/Test/Unit/Copy/Config/SchemaLocatorTest.php +++ b/lib/internal/Magento/Framework/DataObject/Test/Unit/Copy/Config/SchemaLocatorTest.php @@ -6,8 +6,6 @@ namespace Magento\Framework\DataObject\Test\Unit\Copy\Config; -use Magento\Framework\App\Filesystem\DirectoryList; - class SchemaLocatorTest extends \PHPUnit\Framework\TestCase { /** diff --git a/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php b/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php index f4381a42775d7..74f2db9b05f2d 100644 --- a/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php +++ b/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php @@ -7,7 +7,6 @@ use Magento\Framework\Encryption\Encryptor; use Magento\Framework\Encryption\Crypt; -use Magento\Framework\App\DeploymentConfig; class EncryptorTest extends \PHPUnit\Framework\TestCase { diff --git a/lib/internal/Magento/Framework/EntityManager/AbstractModelHydrator.php b/lib/internal/Magento/Framework/EntityManager/AbstractModelHydrator.php index ce35e72baea1f..9b31fcce10b9d 100644 --- a/lib/internal/Magento/Framework/EntityManager/AbstractModelHydrator.php +++ b/lib/internal/Magento/Framework/EntityManager/AbstractModelHydrator.php @@ -5,8 +5,6 @@ */ namespace Magento\Framework\EntityManager; -use Magento\Framework\Model\AbstractModel; - /** * Class AbstractModelHydrator */ diff --git a/lib/internal/Magento/Framework/EntityManager/Hydrator.php b/lib/internal/Magento/Framework/EntityManager/Hydrator.php index ccf5b0c8557ab..0ea4eb3cedcd9 100644 --- a/lib/internal/Magento/Framework/EntityManager/Hydrator.php +++ b/lib/internal/Magento/Framework/EntityManager/Hydrator.php @@ -5,10 +5,8 @@ */ namespace Magento\Framework\EntityManager; -use Magento\Framework\EntityManager\MapperPool; use Magento\Framework\Reflection\DataObjectProcessor; use Magento\Framework\Api\DataObjectHelper; -use Magento\Framework\EntityManager\TypeResolver; /** * Class Hydrator diff --git a/lib/internal/Magento/Framework/EntityManager/Observer/BeforeEntityDelete.php b/lib/internal/Magento/Framework/EntityManager/Observer/BeforeEntityDelete.php index 5480b1c4feba9..6bc144929fb60 100644 --- a/lib/internal/Magento/Framework/EntityManager/Observer/BeforeEntityDelete.php +++ b/lib/internal/Magento/Framework/EntityManager/Observer/BeforeEntityDelete.php @@ -9,7 +9,6 @@ use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Event\Observer; use Magento\Framework\Model\AbstractModel; -use Magento\Framework\Model\ResourceModel\Db\AbstractDb; /** * Class BeforeEntityDelete diff --git a/lib/internal/Magento/Framework/EntityManager/Operation/ExtensionPool.php b/lib/internal/Magento/Framework/EntityManager/Operation/ExtensionPool.php index 703ed96ece4b9..5d3d37bb830d5 100644 --- a/lib/internal/Magento/Framework/EntityManager/Operation/ExtensionPool.php +++ b/lib/internal/Magento/Framework/EntityManager/Operation/ExtensionPool.php @@ -7,7 +7,6 @@ namespace Magento\Framework\EntityManager\Operation; use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\EntityManager\Operation\ExtensionInterface; /** * Class ExtensionPool diff --git a/lib/internal/Magento/Framework/EntityManager/OperationPool.php b/lib/internal/Magento/Framework/EntityManager/OperationPool.php index 97d580e328b1b..90a5acb8b8a03 100644 --- a/lib/internal/Magento/Framework/EntityManager/OperationPool.php +++ b/lib/internal/Magento/Framework/EntityManager/OperationPool.php @@ -7,7 +7,6 @@ namespace Magento\Framework\EntityManager; use Magento\Framework\ObjectManagerInterface as ObjectManager; -use Magento\Framework\EntityManager\OperationInterface; use Magento\Framework\EntityManager\Operation\CheckIfExists; use Magento\Framework\EntityManager\Operation\Read; use Magento\Framework\EntityManager\Operation\Create; diff --git a/lib/internal/Magento/Framework/Event/Test/Unit/ObserverTest.php b/lib/internal/Magento/Framework/Event/Test/Unit/ObserverTest.php index 5e5c60e149837..378de874974c6 100644 --- a/lib/internal/Magento/Framework/Event/Test/Unit/ObserverTest.php +++ b/lib/internal/Magento/Framework/Event/Test/Unit/ObserverTest.php @@ -8,8 +8,6 @@ use \Magento\Framework\Event\Observer; -use Magento\Framework\Event; - /** * Class ConfigTest * diff --git a/lib/internal/Magento/Framework/File/Uploader.php b/lib/internal/Magento/Framework/File/Uploader.php index c86f98c4e413a..33f458d2082e1 100644 --- a/lib/internal/Magento/Framework/File/Uploader.php +++ b/lib/internal/Magento/Framework/File/Uploader.php @@ -5,8 +5,6 @@ */ namespace Magento\Framework\File; -use Magento\Framework\Filesystem\DriverInterface; - /** * File upload class * diff --git a/lib/internal/Magento/Framework/Filesystem/Io/Ftp.php b/lib/internal/Magento/Framework/Filesystem/Io/Ftp.php index 1aa9d61f8865a..04df5fd3f3a6c 100644 --- a/lib/internal/Magento/Framework/Filesystem/Io/Ftp.php +++ b/lib/internal/Magento/Framework/Filesystem/Io/Ftp.php @@ -6,7 +6,6 @@ namespace Magento\Framework\Filesystem\Io; -use Magento\Framework\Filesystem\DriverInterface; use Magento\Framework\Phrase; use Magento\Framework\Exception\LocalizedException; diff --git a/lib/internal/Magento/Framework/Filesystem/Io/IoInterface.php b/lib/internal/Magento/Framework/Filesystem/Io/IoInterface.php index c31d3ff9e52ba..e71fe88fd5544 100644 --- a/lib/internal/Magento/Framework/Filesystem/Io/IoInterface.php +++ b/lib/internal/Magento/Framework/Filesystem/Io/IoInterface.php @@ -5,8 +5,6 @@ */ namespace Magento\Framework\Filesystem\Io; -use Magento\Framework\Filesystem\DriverInterface; - /** * Input/output client interface */ diff --git a/lib/internal/Magento/Framework/Filesystem/Test/Unit/File/ReadFactoryTest.php b/lib/internal/Magento/Framework/Filesystem/Test/Unit/File/ReadFactoryTest.php index c2115205e6bd2..93b12f8d56ff8 100644 --- a/lib/internal/Magento/Framework/Filesystem/Test/Unit/File/ReadFactoryTest.php +++ b/lib/internal/Magento/Framework/Filesystem/Test/Unit/File/ReadFactoryTest.php @@ -7,8 +7,6 @@ use \Magento\Framework\Filesystem\File\ReadFactory; -use Magento\Framework\Filesystem\DriverPool; - /** * Class ReadFactoryTest */ diff --git a/lib/internal/Magento/Framework/Filter/DataObject/Grid.php b/lib/internal/Magento/Framework/Filter/DataObject/Grid.php index 7676558a927b0..54dccc8ae5b82 100644 --- a/lib/internal/Magento/Framework/Filter/DataObject/Grid.php +++ b/lib/internal/Magento/Framework/Filter/DataObject/Grid.php @@ -5,8 +5,6 @@ */ namespace Magento\Framework\Filter\DataObject; -use Magento\Framework\DataObject; - class Grid extends \Magento\Framework\Filter\DataObject { /** diff --git a/lib/internal/Magento/Framework/Indexer/SaveHandler/IndexerHandler.php b/lib/internal/Magento/Framework/Indexer/SaveHandler/IndexerHandler.php index 3908cf1e98762..c3ab971c992e6 100644 --- a/lib/internal/Magento/Framework/Indexer/SaveHandler/IndexerHandler.php +++ b/lib/internal/Magento/Framework/Indexer/SaveHandler/IndexerHandler.php @@ -12,7 +12,6 @@ use Magento\Framework\Indexer\IndexStructureInterface; use Magento\Framework\Indexer\ScopeResolver\FlatScopeResolver; use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver; -use Magento\Framework\Indexer\SaveHandler\Batch; class IndexerHandler implements IndexerInterface { diff --git a/lib/internal/Magento/Framework/Indexer/SaveHandlerFactory.php b/lib/internal/Magento/Framework/Indexer/SaveHandlerFactory.php index 82e8a26553b89..80231dd7aa290 100644 --- a/lib/internal/Magento/Framework/Indexer/SaveHandlerFactory.php +++ b/lib/internal/Magento/Framework/Indexer/SaveHandlerFactory.php @@ -5,7 +5,6 @@ */ namespace Magento\Framework\Indexer; -use Magento\Framework\Indexer\IndexerInterface; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Indexer\SaveHandler\IndexerInterface as SaveHandlerInterface; diff --git a/lib/internal/Magento/Framework/Indexer/StructureFactory.php b/lib/internal/Magento/Framework/Indexer/StructureFactory.php index f9b7eafdfc436..1b6729e4603ec 100644 --- a/lib/internal/Magento/Framework/Indexer/StructureFactory.php +++ b/lib/internal/Magento/Framework/Indexer/StructureFactory.php @@ -5,8 +5,6 @@ */ namespace Magento\Framework\Indexer; -use Magento\Framework\Indexer\IndexStructureInterface; - class StructureFactory { /** diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/IndexStructureTest.php b/lib/internal/Magento/Framework/Indexer/Test/Unit/IndexStructureTest.php index 514bd673f8525..2c0d9124b9603 100644 --- a/lib/internal/Magento/Framework/Indexer/Test/Unit/IndexStructureTest.php +++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/IndexStructureTest.php @@ -6,7 +6,6 @@ namespace Magento\Framework\Indexer\Test\Unit; -use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Ddl\Table; use \Magento\Framework\TestFramework\Unit\Helper\ObjectManager; diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/CurrencyTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/CurrencyTest.php index 5ef1a3097402b..3e917425c31e0 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/CurrencyTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/CurrencyTest.php @@ -7,7 +7,6 @@ namespace Magento\Framework\Locale\Test\Unit; use Magento\Framework\Locale\Currency; -use Magento\Framework\Locale\CurrencyInterface; class CurrencyTest extends \PHPUnit\Framework\TestCase { diff --git a/lib/internal/Magento/Framework/Message/Test/Unit/AbstractMessageTest.php b/lib/internal/Magento/Framework/Message/Test/Unit/AbstractMessageTest.php index 550f135ddc545..f4b73bdd2f293 100644 --- a/lib/internal/Magento/Framework/Message/Test/Unit/AbstractMessageTest.php +++ b/lib/internal/Magento/Framework/Message/Test/Unit/AbstractMessageTest.php @@ -5,8 +5,6 @@ */ namespace Magento\Framework\Message\Test\Unit; -use Magento\Framework\Message\MessageInterface; - /** * \Magento\Framework\Message\AbstractMessage test case */ diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/DeleteEntityRow.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/DeleteEntityRow.php index 7ec43220dc85c..d7d63232c1191 100755 --- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/DeleteEntityRow.php +++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/DeleteEntityRow.php @@ -6,7 +6,6 @@ namespace Magento\Framework\Model\ResourceModel\Db; use Magento\Framework\EntityManager\MetadataPool; -use Magento\Framework\EntityManager\EntityMetadata; class DeleteEntityRow { diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Relation/ActionPool.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Relation/ActionPool.php index 433762556693d..c5096acb761df 100644 --- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Relation/ActionPool.php +++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Relation/ActionPool.php @@ -7,7 +7,6 @@ namespace Magento\Framework\Model\ResourceModel\Db\Relation; use Magento\Framework\ObjectManagerInterface as ObjectManager; -use Magento\Framework\Model\ResourceModel\Db\ProcessEntityRelationInterface; /** * Class ActionPool diff --git a/lib/internal/Magento/Framework/Model/Test/Unit/AbstractExtensibleModelTest.php b/lib/internal/Magento/Framework/Model/Test/Unit/AbstractExtensibleModelTest.php index 1b16427040709..7e68b8daf2aef 100644 --- a/lib/internal/Magento/Framework/Model/Test/Unit/AbstractExtensibleModelTest.php +++ b/lib/internal/Magento/Framework/Model/Test/Unit/AbstractExtensibleModelTest.php @@ -7,7 +7,6 @@ namespace Magento\Framework\Model\Test\Unit; use Magento\Framework\Api\AttributeValue; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) 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 147cac47d3829..4f27f083509d7 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 @@ -7,7 +7,6 @@ namespace Magento\Framework\Model\Test\Unit\ResourceModel\Db\Collection; use Magento\Framework\DB\Select; -use Magento\Framework\DataObject as MagentoObject; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Framework\ObjectManagerInterface; diff --git a/lib/internal/Magento/Framework/Module/Dir.php b/lib/internal/Magento/Framework/Module/Dir.php index 77174bb51305d..e6b60453b9577 100644 --- a/lib/internal/Magento/Framework/Module/Dir.php +++ b/lib/internal/Magento/Framework/Module/Dir.php @@ -9,7 +9,6 @@ use Magento\Framework\Component\ComponentRegistrar; use Magento\Framework\Component\ComponentRegistrarInterface; -use Magento\Framework\Filesystem; class Dir { diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/Dir/ReaderTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/Dir/ReaderTest.php index f41f9bbd0d239..59249e3b92ae9 100644 --- a/lib/internal/Magento/Framework/Module/Test/Unit/Dir/ReaderTest.php +++ b/lib/internal/Magento/Framework/Module/Test/Unit/Dir/ReaderTest.php @@ -11,7 +11,6 @@ namespace Magento\Framework\Module\Test\Unit\Dir; use Magento\Framework\Config\FileIteratorFactory; -use Magento\Framework\Filesystem; use Magento\Framework\Module\Dir; class ReaderTest extends \PHPUnit\Framework\TestCase diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/ManagerTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/ManagerTest.php index 12ddfea8c83d1..255f5783dbc60 100644 --- a/lib/internal/Magento/Framework/Module/Test/Unit/ManagerTest.php +++ b/lib/internal/Magento/Framework/Module/Test/Unit/ManagerTest.php @@ -5,8 +5,6 @@ */ namespace Magento\Framework\Module\Test\Unit; -use Magento\Framework\Module\Plugin\DbStatusValidator; - class ManagerTest extends \PHPUnit\Framework\TestCase { /** diff --git a/lib/internal/Magento/Framework/Mview/Config/Converter.php b/lib/internal/Magento/Framework/Mview/Config/Converter.php index 55b2f1da21cb9..5c33ac150d00a 100644 --- a/lib/internal/Magento/Framework/Mview/Config/Converter.php +++ b/lib/internal/Magento/Framework/Mview/Config/Converter.php @@ -5,7 +5,6 @@ */ namespace Magento\Framework\Mview\Config; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Mview\View\SubscriptionInterface; class Converter implements \Magento\Framework\Config\ConverterInterface diff --git a/lib/internal/Magento/Framework/Mview/Config/SchemaLocator.php b/lib/internal/Magento/Framework/Mview/Config/SchemaLocator.php index 7cafc35ba1451..7eeea70ffe337 100644 --- a/lib/internal/Magento/Framework/Mview/Config/SchemaLocator.php +++ b/lib/internal/Magento/Framework/Mview/Config/SchemaLocator.php @@ -5,8 +5,6 @@ */ namespace Magento\Framework\Mview\Config; -use Magento\Framework\App\Filesystem\DirectoryList; - class SchemaLocator implements \Magento\Framework\Config\SchemaLocatorInterface { /** 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 ea8b7cfc068ac..4830c5e140917 100644 --- a/lib/internal/Magento/Framework/Mview/Test/Unit/Config/ReaderTest.php +++ b/lib/internal/Magento/Framework/Mview/Test/Unit/Config/ReaderTest.php @@ -5,8 +5,6 @@ */ namespace Magento\Framework\Mview\Test\Unit\Config; -use Magento\Framework\App\Filesystem\DirectoryList; - class ReaderTest extends \PHPUnit\Framework\TestCase { /** diff --git a/lib/internal/Magento/Framework/Mview/View/Changelog.php b/lib/internal/Magento/Framework/Mview/View/Changelog.php index 0f441b7728fce..4fb06ce3f06fd 100644 --- a/lib/internal/Magento/Framework/Mview/View/Changelog.php +++ b/lib/internal/Magento/Framework/Mview/View/Changelog.php @@ -6,7 +6,6 @@ namespace Magento\Framework\Mview\View; -use Magento\Framework\App\ResourceConnection; use Magento\Framework\Phrase; class Changelog implements ChangelogInterface diff --git a/lib/internal/Magento/Framework/ObjectManager/DefinitionFactory.php b/lib/internal/Magento/Framework/ObjectManager/DefinitionFactory.php index e4e82fc1ac7b3..492af22815311 100644 --- a/lib/internal/Magento/Framework/ObjectManager/DefinitionFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/DefinitionFactory.php @@ -6,10 +6,7 @@ namespace Magento\Framework\ObjectManager; use Magento\Framework\Filesystem\DriverInterface; -use Magento\Framework\Interception\Code\Generator as InterceptionGenerator; use Magento\Framework\ObjectManager\Definition\Runtime; -use Magento\Framework\ObjectManager\Profiler\Code\Generator as ProfilerGenerator; -use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\Code\Generator\Autoloader; /** diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/RepositoryTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/RepositoryTest.php index 1ca755b1281ec..72eccbc5e7dea 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/RepositoryTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/RepositoryTest.php @@ -6,7 +6,6 @@ namespace Magento\Framework\ObjectManager\Test\Unit\Code\Generator; use Magento\Framework\Api\Test\Unit\Code\Generator\EntityChildTestAbstract; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; /** * Class RepositoryTest diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Helper/CompositeTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Helper/CompositeTest.php index 14a40c76bc470..8e3681cab611f 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Helper/CompositeTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Helper/CompositeTest.php @@ -9,7 +9,6 @@ use \Magento\Framework\ObjectManager\Helper\Composite; use Magento\Framework\ObjectManager\Helper\Composite as CompositeHelper; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; class CompositeTest extends \PHPUnit\Framework\TestCase { diff --git a/lib/internal/Magento/Framework/Reflection/CustomAttributesProcessor.php b/lib/internal/Magento/Framework/Reflection/CustomAttributesProcessor.php index 4fd3a17fb6c6a..18308724c0226 100644 --- a/lib/internal/Magento/Framework/Reflection/CustomAttributesProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/CustomAttributesProcessor.php @@ -6,11 +6,8 @@ namespace Magento\Framework\Reflection; -use Magento\Framework\Phrase; use Magento\Framework\Api\AttributeInterface; use Magento\Framework\Api\AttributeValue; -use Magento\Framework\Api\SimpleDataObjectConverter; -use Zend\Code\Reflection\MethodReflection; use Magento\Framework\Api\CustomAttributesDataInterface; use Magento\Framework\Api\AttributeTypeResolverInterface; diff --git a/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php b/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php index 3e9eef4fd7b15..aa978e7f337cc 100644 --- a/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php @@ -11,7 +11,6 @@ use Magento\Framework\AuthorizationInterface; use Magento\Framework\Phrase; use Magento\Framework\Api\ExtensionAttributesInterface; -use Magento\Framework\Reflection\MethodsMap; use Zend\Code\Reflection\MethodReflection; /** diff --git a/lib/internal/Magento/Framework/Reflection/FieldNamer.php b/lib/internal/Magento/Framework/Reflection/FieldNamer.php index 8e4d077c52e08..a208764352f58 100644 --- a/lib/internal/Magento/Framework/Reflection/FieldNamer.php +++ b/lib/internal/Magento/Framework/Reflection/FieldNamer.php @@ -6,12 +6,7 @@ namespace Magento\Framework\Reflection; -use Magento\Framework\Phrase; -use Magento\Framework\Api\AttributeValue; -use Magento\Framework\Api\CustomAttributesDataInterface; use Magento\Framework\Api\SimpleDataObjectConverter; -use Zend\Code\Reflection\ClassReflection; -use Zend\Code\Reflection\MethodReflection; /** * Determines the name to use for fields in a data output array given method metadata. diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php index a467c4b7b5aad..e4c0294c0cfb5 100644 --- a/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php @@ -7,7 +7,6 @@ namespace Magento\Framework\Reflection\Test\Unit; use Zend\Code\Reflection\ClassReflection; -use Magento\Framework\Exception\SerializationException; /** * NameFinder Unit Test diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Aggregation/Builder/Term.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Aggregation/Builder/Term.php index ed3018756cdd6..78ff7b04fd39c 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Aggregation/Builder/Term.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Aggregation/Builder/Term.php @@ -6,7 +6,6 @@ namespace Magento\Framework\Search\Adapter\Mysql\Aggregation\Builder; use Magento\Framework\DB\Ddl\Table; -use Magento\Framework\DB\Select; use Magento\Framework\Search\Adapter\Mysql\Aggregation\DataProviderInterface; use Magento\Framework\Search\Request\BucketInterface as RequestBucketInterface; diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/AdapterTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/AdapterTest.php index ef4a4d62322ed..55d26493ca379 100644 --- a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/AdapterTest.php +++ b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/AdapterTest.php @@ -6,7 +6,6 @@ namespace Magento\Framework\Search\Test\Unit\Adapter\Mysql; -use Magento\Framework\App\ResourceConnection; use Magento\Framework\Search\Request\BucketInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; diff --git a/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/SerializeTest.php b/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/SerializeTest.php index 1f8e7cf9d0bc3..aac25400cec85 100644 --- a/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/SerializeTest.php +++ b/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/SerializeTest.php @@ -7,7 +7,6 @@ use Magento\Framework\Serialize\Serializer\Serialize; use Magento\Framework\Serialize\Signer; -use Psr\Log\LoggerInterface; use Magento\Framework\Serialize\InvalidSignatureException; class SerializeTest extends \PHPUnit\Framework\TestCase diff --git a/lib/internal/Magento/Framework/Session/SaveHandler/DbTable.php b/lib/internal/Magento/Framework/Session/SaveHandler/DbTable.php index 6f5937e08455d..cf3449a8c3fcf 100644 --- a/lib/internal/Magento/Framework/Session/SaveHandler/DbTable.php +++ b/lib/internal/Magento/Framework/Session/SaveHandler/DbTable.php @@ -6,7 +6,6 @@ namespace Magento\Framework\Session\SaveHandler; -use Magento\Framework\App\ResourceConnection; use Magento\Framework\Exception\SessionException; use Magento\Framework\Phrase; diff --git a/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/LoggerTest.php b/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/LoggerTest.php index 4594a471e008d..a604178bc36cc 100644 --- a/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/LoggerTest.php +++ b/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/LoggerTest.php @@ -6,7 +6,6 @@ namespace Magento\Framework\Session\Test\Unit\SaveHandler\Redis; use Cm\RedisSession\Handler\LoggerInterface; -use Magento\Framework\Session\SaveHandler\Redis\Logger; class LoggerTest extends \PHPUnit\Framework\TestCase { diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/Cookie/_files/setcookie_mock.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/Cookie/_files/setcookie_mock.php index e97dfab795c59..f89144f9753db 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/Cookie/_files/setcookie_mock.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/Cookie/_files/setcookie_mock.php @@ -6,7 +6,6 @@ namespace Magento\Framework\Stdlib\Cookie; -use \Magento\Framework\Stdlib\Cookie\PhpCookieManager; use \Magento\Framework\Stdlib\Test\Unit\Cookie\PhpCookieManagerTest; /** diff --git a/lib/internal/Magento/Framework/System/Dirs.php b/lib/internal/Magento/Framework/System/Dirs.php index 1bab4c90b3bec..6328921d40bb0 100644 --- a/lib/internal/Magento/Framework/System/Dirs.php +++ b/lib/internal/Magento/Framework/System/Dirs.php @@ -5,8 +5,6 @@ */ namespace Magento\Framework\System; -use Magento\Framework\Filesystem\DriverInterface; - class Dirs { /** diff --git a/lib/internal/Magento/Framework/System/Ftp.php b/lib/internal/Magento/Framework/System/Ftp.php index 827e524a62464..8bf898965cbc3 100644 --- a/lib/internal/Magento/Framework/System/Ftp.php +++ b/lib/internal/Magento/Framework/System/Ftp.php @@ -6,8 +6,6 @@ namespace Magento\Framework\System; -use Magento\Framework\Filesystem\DriverInterface; - /** * Class to work with remote FTP server */ diff --git a/lib/internal/Magento/Framework/View/Asset/PreProcessor/AlternativeSource.php b/lib/internal/Magento/Framework/View/Asset/PreProcessor/AlternativeSource.php index 38f2b220dee6f..7016bbdb08ab2 100644 --- a/lib/internal/Magento/Framework/View/Asset/PreProcessor/AlternativeSource.php +++ b/lib/internal/Magento/Framework/View/Asset/PreProcessor/AlternativeSource.php @@ -5,7 +5,6 @@ */ namespace Magento\Framework\View\Asset\PreProcessor; -use Magento\Framework\Filesystem; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\View\Asset\File\FallbackContext; use Magento\Framework\View\Asset\LockerProcessInterface; diff --git a/lib/internal/Magento/Framework/View/Layout.php b/lib/internal/Magento/Framework/View/Layout.php index bd94491537efe..5cd7591098207 100755 --- a/lib/internal/Magento/Framework/View/Layout.php +++ b/lib/internal/Magento/Framework/View/Layout.php @@ -13,7 +13,6 @@ use Magento\Framework\Message\ManagerInterface as MessageManagerInterface; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\View\Layout\Element; -use Magento\Framework\View\Layout\ScheduledStructure; use Psr\Log\LoggerInterface as Logger; /** diff --git a/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php index d8e1a3de3670a..cdb6ed799aade 100644 --- a/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php +++ b/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php @@ -9,7 +9,6 @@ use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Framework\Reflection\DataObjectProcessor; use Magento\Framework\Reflection\MethodsMap; -use Magento\Framework\Webapi\ServicePayloadConverterInterface; /** * Data object converter diff --git a/lib/internal/Magento/Framework/Xml/Security.php b/lib/internal/Magento/Framework/Xml/Security.php index e502429e4511a..ec901a63ea862 100644 --- a/lib/internal/Magento/Framework/Xml/Security.php +++ b/lib/internal/Magento/Framework/Xml/Security.php @@ -6,7 +6,6 @@ namespace Magento\Framework\Xml; use DOMDocument; -use Magento\Framework\Phrase; /** * Class Security From cc186407338827be2722fc28f6678381d7b7d1af Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Wed, 18 Jul 2018 16:27:49 -0300 Subject: [PATCH 0513/1171] Replacing Usage of Deprecated Methods for Message Manager --- .../Catalog/Controller/Adminhtml/Category/Delete.php | 6 +++--- .../Controller/Adminhtml/Product/Action/Attribute.php | 2 +- .../Adminhtml/Product/Action/Attribute/Save.php | 6 +++--- .../Adminhtml/Product/Action/Attribute/Validate.php | 2 +- .../Controller/Adminhtml/Product/Attribute/Delete.php | 8 ++++---- .../Controller/Adminhtml/Product/Attribute/Edit.php | 4 ++-- .../Controller/Adminhtml/Product/Attribute/Validate.php | 4 +--- .../Catalog/Controller/Adminhtml/Product/Duplicate.php | 4 ++-- .../Catalog/Controller/Adminhtml/Product/Edit.php | 4 ++-- .../Catalog/Controller/Adminhtml/Product/Group/Save.php | 4 ++-- .../Catalog/Controller/Adminhtml/Product/MassDelete.php | 9 ++++++--- .../Catalog/Controller/Adminhtml/Product/Set/Delete.php | 4 ++-- .../Catalog/Controller/Adminhtml/Product/Set/Save.php | 6 +++--- .../Catalog/Controller/Adminhtml/Product/Validate.php | 2 +- .../Magento/Catalog/Controller/Product/Compare/Clear.php | 6 +++--- .../Catalog/Controller/Product/Compare/Remove.php | 2 +- app/code/Magento/Catalog/Controller/Product/View.php | 5 ++++- 17 files changed, 41 insertions(+), 37 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Delete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Delete.php index 8f570e35989cb..0a54475b15f9c 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Delete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Delete.php @@ -44,12 +44,12 @@ public function execute() $this->_eventManager->dispatch('catalog_controller_category_delete', ['category' => $category]); $this->_auth->getAuthStorage()->setDeletedPath($category->getPath()); $this->categoryRepository->delete($category); - $this->messageManager->addSuccess(__('You deleted the category.')); + $this->messageManager->addSuccessMessage(__('You deleted the category.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); return $resultRedirect->setPath('catalog/*/edit', ['_current' => true]); } catch (\Exception $e) { - $this->messageManager->addError(__('Something went wrong while trying to delete the category.')); + $this->messageManager->addErrorMessage(__('Something went wrong while trying to delete the category.')); return $resultRedirect->setPath('catalog/*/edit', ['_current' => true]); } } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute.php index 775097c5eba1d..ca7652ebb43b5 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute.php @@ -54,7 +54,7 @@ protected function _validateProducts() } if ($error) { - $this->messageManager->addError($error); + $this->messageManager->addErrorMessage($error); } return !$error; diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index 82496446aef9f..0fbf9054ef1bd 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -192,7 +192,7 @@ public function execute() $this->_eventManager->dispatch('catalog_product_to_website_change', ['products' => $productIds]); } - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('A total of %1 record(s) were updated.', count($this->attributeHelper->getProductIds())) ); @@ -205,9 +205,9 @@ public function execute() $this->_productPriceIndexerProcessor->reindexList($this->attributeHelper->getProductIds()); } } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('Something went wrong while updating the product(s) attributes.') ); 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 bb18436be6102..a873f08d082d7 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 @@ -68,7 +68,7 @@ public function execute() $response->setError(true); $response->setMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('Something went wrong while updating the product(s) attributes.') ); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete.php index cc5a658a9296d..bef6aee0e2afd 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete.php @@ -21,23 +21,23 @@ public function execute() // entity type check $model->load($id); if ($model->getEntityTypeId() != $this->_entityTypeId) { - $this->messageManager->addError(__('We can\'t delete the attribute.')); + $this->messageManager->addErrorMessage(__('We can\'t delete the attribute.')); return $resultRedirect->setPath('catalog/*/'); } try { $model->delete(); - $this->messageManager->addSuccess(__('You deleted the product attribute.')); + $this->messageManager->addSuccessMessage(__('You deleted the product attribute.')); return $resultRedirect->setPath('catalog/*/'); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); return $resultRedirect->setPath( 'catalog/*/edit', ['attribute_id' => $this->getRequest()->getParam('attribute_id')] ); } } - $this->messageManager->addError(__('We can\'t find an attribute to delete.')); + $this->messageManager->addErrorMessage(__('We can\'t find an attribute to delete.')); return $resultRedirect->setPath('catalog/*/'); } } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Edit.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Edit.php index fd97aaa50389e..a99cbdbade181 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Edit.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Edit.php @@ -25,14 +25,14 @@ public function execute() $model->load($id); if (!$model->getId()) { - $this->messageManager->addError(__('This attribute no longer exists.')); + $this->messageManager->addErrorMessage(__('This attribute no longer exists.')); $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('catalog/*/'); } // entity type check if ($model->getEntityTypeId() != $this->_entityTypeId) { - $this->messageManager->addError(__('This attribute cannot be edited.')); + $this->messageManager->addErrorMessage(__('This attribute cannot be edited.')); $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('catalog/*/'); } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php index b61be2b95b960..db452113ada06 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php @@ -92,9 +92,7 @@ public function execute() $attributeSet->setEntityTypeId($this->_entityTypeId)->load($setName, 'attribute_set_name'); if ($attributeSet->getId()) { $setName = $this->_objectManager->get(\Magento\Framework\Escaper::class)->escapeHtml($setName); - $this->messageManager->addError( - __('A "%1" attribute set name already exists. Create a new name and try again.', $setName) - ); + $this->messageManager->addErrorMessage(__('An attribute set named \'%1\' already exists.', $setName)); $layout = $this->layoutFactory->create(); $layout->initMessages(); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Duplicate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Duplicate.php index 7e8b03a66f603..63e52eead064c 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Duplicate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Duplicate.php @@ -43,11 +43,11 @@ public function execute() $product = $this->productBuilder->build($this->getRequest()); try { $newProduct = $this->productCopier->copy($product); - $this->messageManager->addSuccess(__('You duplicated the product.')); + $this->messageManager->addSuccessMessage(__('You duplicated the product.')); $resultRedirect->setPath('catalog/*/edit', ['_current' => true, 'id' => $newProduct->getId()]); } catch (\Exception $e) { $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $resultRedirect->setPath('catalog/*/edit', ['_current' => true]); } return $resultRedirect; diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Edit.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Edit.php index 838bce7272250..1b9316a95ad59 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Edit.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Edit.php @@ -52,12 +52,12 @@ public function execute() if (($productId && !$product->getEntityId())) { /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); - $this->messageManager->addError(__('This product doesn\'t exist.')); + $this->messageManager->addErrorMessage(__('This product doesn\'t exist.')); return $resultRedirect->setPath('catalog/*/'); } elseif ($productId === 0) { /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); - $this->messageManager->addError(__('Invalid product id. Should be numeric value greater than 0')); + $this->messageManager->addErrorMessage(__('Invalid product id. Should be numeric value greater than 0')); return $resultRedirect->setPath('catalog/*/'); } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Group/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Group/Save.php index 4909e22775e55..8a5f375f2b706 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Group/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Group/Save.php @@ -29,12 +29,12 @@ public function execute() ); if ($model->itemExists()) { - $this->messageManager->addError(__('A group with the same name already exists.')); + $this->messageManager->addErrorMessage(__('A group with the same name already exists.')); } else { try { $model->save(); } catch (\Exception $e) { - $this->messageManager->addError(__('Something went wrong while saving this group.')); + $this->messageManager->addErrorMessage(__('Something went wrong while saving this group.')); } } } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php index 2402fb213cda0..f32c6edd57394 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php @@ -64,9 +64,12 @@ public function execute() $this->productRepository->delete($product); $productDeleted++; } - $this->messageManager->addSuccess( - __('A total of %1 record(s) have been deleted.', $productDeleted) - ); + + if ($productDeleted) { + $this->messageManager->addSuccessMessage( + __('A total of %1 record(s) have been deleted.', $productDeleted) + ); + } return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath('catalog/*/index'); } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Delete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Delete.php index b49a4dabe223c..f2695311732f0 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Delete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Delete.php @@ -36,10 +36,10 @@ public function execute() $resultRedirect = $this->resultRedirectFactory->create(); try { $this->attributeSetRepository->deleteById($setId); - $this->messageManager->addSuccess(__('The attribute set has been removed.')); + $this->messageManager->addSuccessMessage(__('The attribute set has been removed.')); $resultRedirect->setPath('catalog/*/'); } catch (\Exception $e) { - $this->messageManager->addError(__('We can\'t delete this set right now.')); + $this->messageManager->addErrorMessage(__('We can\'t delete this set right now.')); $resultRedirect->setUrl($this->_redirect->getRedirectUrl($this->getUrl('*'))); } return $resultRedirect; diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Save.php index 00a836309e58e..578150fbf5d34 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Save.php @@ -100,15 +100,15 @@ public function execute() $model->initFromSkeleton($this->getRequest()->getParam('skeleton_set')); } $model->save(); - $this->messageManager->addSuccess(__('You saved the attribute set.')); + $this->messageManager->addSuccessMessage(__('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()); + $this->messageManager->addErrorMessage($e->getMessage()); $hasError = true; } catch (\Exception $e) { - $this->messageManager->addException($e, __('Something went wrong while saving the attribute set.')); + $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the attribute set.')); $hasError = true; } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Validate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Validate.php index 63f46fd32e6f7..e131bfe38c546 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Validate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Validate.php @@ -137,7 +137,7 @@ public function execute() $response->setError(true); $response->setMessages([$e->getMessage()]); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $layout = $this->layoutFactory->create(); $layout->initMessages(); $response->setError(true); diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Clear.php b/app/code/Magento/Catalog/Controller/Product/Compare/Clear.php index 30470d13f002d..568fbf1d05677 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Clear.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Clear.php @@ -30,12 +30,12 @@ public function execute() try { $items->clear(); - $this->messageManager->addSuccess(__('You cleared the comparison list.')); + $this->messageManager->addSuccessMessage(__('You cleared the comparison list.')); $this->_objectManager->get(\Magento\Catalog\Helper\Product\Compare::class)->calculate(); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Something went wrong clearing the comparison list.')); + $this->messageManager->addExceptionMessage($e, __('Something went wrong clearing the comparison list.')); } /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php b/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php index fadb94761a236..2acbe5ce4d582 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php @@ -44,7 +44,7 @@ public function execute() $item->delete(); $productName = $this->_objectManager->get(\Magento\Framework\Escaper::class) ->escapeHtml($product->getName()); - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('You removed product %1 from the comparison list.', $productName) ); $this->_eventManager->dispatch( diff --git a/app/code/Magento/Catalog/Controller/Product/View.php b/app/code/Magento/Catalog/Controller/Product/View.php index 4c577eb897589..ed437361fddd3 100644 --- a/app/code/Magento/Catalog/Controller/Product/View.php +++ b/app/code/Magento/Catalog/Controller/Product/View.php @@ -78,13 +78,16 @@ public function execute() if ($this->getRequest()->isPost() && $this->getRequest()->getParam(self::PARAM_NAME_URL_ENCODED)) { $product = $this->_initProduct(); + if (!$product) { return $this->noProductRedirect(); } + if ($specifyOptions) { $notice = $product->getTypeInstance()->getSpecifyOptionMessage(); - $this->messageManager->addNotice($notice); + $this->messageManager->addNoticeMessage($notice); } + if ($this->getRequest()->isAjax()) { $this->getResponse()->representJson( $this->_objectManager->get(\Magento\Framework\Json\Helper\Data::class)->jsonEncode([ From 8749f6954e6737a9bbc8cd0b4b1f076ab0349692 Mon Sep 17 00:00:00 2001 From: Anshu Mishra <mishra.anshu1710@gmail.com> Date: Thu, 5 Jul 2018 18:02:24 +0530 Subject: [PATCH 0514/1171] Admin user auth controller refactor --- .../User/Controller/Adminhtml/Auth.php | 12 +++++++++- .../Adminhtml/Auth/Forgotpassword.php | 22 ++++++++++++++----- .../Adminhtml/Auth/ResetPasswordPost.php | 4 ++-- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth.php b/app/code/Magento/User/Controller/Adminhtml/Auth.php index 173fdcc764f6f..12940c66c0f85 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth.php @@ -7,6 +7,7 @@ namespace Magento\User\Controller\Adminhtml; use Magento\Framework\Encryption\Helper\Security; +use Magento\Framework\App\ObjectManager; /** * \Magento\User Auth controller @@ -19,19 +20,28 @@ abstract class Auth extends \Magento\Backend\App\AbstractAction * @var \Magento\User\Model\UserFactory */ protected $_userFactory; + + /** + * @var \Magento\Backend\Helper\Data + */ + protected $_backendHelper; /** * Construct * * @param \Magento\Backend\App\Action\Context $context * @param \Magento\User\Model\UserFactory $userFactory + * @param \Magento\Backend\Helper\Data $backendHelper */ public function __construct( \Magento\Backend\App\Action\Context $context, - \Magento\User\Model\UserFactory $userFactory + \Magento\User\Model\UserFactory $userFactory, + \Magento\Backend\Helper\Data $backendHelper = null ) { parent::__construct($context); $this->_userFactory = $userFactory; + $this->_backendHelper = $backendHelper ?: + ObjectManager::getInstance()->get(\Magento\Backend\Helper\Data::class); } /** diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php b/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php index cd4c3d6950685..a3021b798550e 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php @@ -7,6 +7,7 @@ namespace Magento\User\Controller\Adminhtml\Auth; use Magento\Security\Model\SecurityManager; +use Magento\Framework\App\ObjectManager; class Forgotpassword extends \Magento\User\Controller\Adminhtml\Auth { @@ -14,19 +15,30 @@ class Forgotpassword extends \Magento\User\Controller\Adminhtml\Auth * @var SecurityManager */ protected $securityManager; + + /** + * User model factory + * + * @var \Magento\User\Model\ResourceModel\User\CollectionFactory + */ + private $userCollectionFactory; /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\User\Model\UserFactory $userFactory * @param \Magento\Security\Model\SecurityManager $securityManager + * @param \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory */ public function __construct( \Magento\Backend\App\Action\Context $context, \Magento\User\Model\UserFactory $userFactory, - \Magento\Security\Model\SecurityManager $securityManager + \Magento\Security\Model\SecurityManager $securityManager, + \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory = null ) { parent::__construct($context, $userFactory); $this->securityManager = $securityManager; + $this->userCollectionFactory = $userCollectionFactory ?: + ObjectManager::getInstance()->get(\Magento\User\Model\ResourceModel\User\CollectionFactory::class); } /** @@ -54,7 +66,7 @@ public function execute() $this->messageManager->addErrorMessage($exception->getMessage()); return $resultRedirect->setPath('admin'); } - $collection = $this->_objectManager->get(\Magento\User\Model\ResourceModel\User\Collection::class); + $collection = $this->userCollectionFactory->create(); /** @var $collection \Magento\User\Model\ResourceModel\User\Collection */ $collection->addFieldToFilter('email', $email); $collection->load(false); @@ -65,9 +77,7 @@ public function execute() /** @var \Magento\User\Model\User $user */ $user = $this->_userFactory->create()->load($item->getId()); if ($user->getId()) { - $newPassResetToken = $this->_objectManager->get( - \Magento\User\Helper\Data::class - )->generateResetPasswordLinkToken(); + $newPassResetToken = $this->_backendHelper->generateResetPasswordLinkToken(); $user->changeResetPasswordLinkToken($newPassResetToken); $user->save(); $user->sendPasswordResetConfirmationEmail(); @@ -86,7 +96,7 @@ public function execute() $this->messageManager->addSuccess(__('We\'ll email you a link to reset your password.')); // @codingStandardsIgnoreEnd $this->getResponse()->setRedirect( - $this->_objectManager->get(\Magento\Backend\Helper\Data::class)->getHomePageUrl() + $this->_backendHelper->getHomePageUrl() ); return; } else { diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php b/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php index 2c6be98439919..e17331519f60a 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php @@ -27,7 +27,7 @@ public function execute() } catch (\Exception $exception) { $this->messageManager->addError(__('Your password reset link has expired.')); $this->getResponse()->setRedirect( - $this->_objectManager->get(\Magento\Backend\Helper\Data::class)->getHomePageUrl() + $this->_backendHelper->getHomePageUrl() ); return; } @@ -53,7 +53,7 @@ public function execute() $user->save(); $this->messageManager->addSuccess(__('You updated your password.')); $this->getResponse()->setRedirect( - $this->_objectManager->get(\Magento\Backend\Helper\Data::class)->getHomePageUrl() + $this->_backendHelper->getHomePageUrl() ); } } catch (\Magento\Framework\Validator\Exception $exception) { From b94ba641921badf7371383c404bdf7b1621350a3 Mon Sep 17 00:00:00 2001 From: Anshu Mishra <mishra.anshu1710@gmail.com> Date: Fri, 6 Jul 2018 10:07:39 +0530 Subject: [PATCH 0515/1171] Changes as per comments on Github --- .../User/Controller/Adminhtml/Auth.php | 34 ++++++++------- .../Adminhtml/Auth/Forgotpassword.php | 42 +++++++++++-------- .../Adminhtml/Auth/ResetPasswordPost.php | 8 ++-- 3 files changed, 48 insertions(+), 36 deletions(-) diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth.php b/app/code/Magento/User/Controller/Adminhtml/Auth.php index 12940c66c0f85..abd1d33696b49 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth.php @@ -8,11 +8,16 @@ use Magento\Framework\Encryption\Helper\Security; use Magento\Framework\App\ObjectManager; +use Magento\Backend\App\AbstractAction; +use Magento\Backend\App\Action\Context; +use Magento\User\Model\UserFactory; +use Magento\Backend\Helper\Data; +use Magento\Framework\Exception\LocalizedException; /** * \Magento\User Auth controller */ -abstract class Auth extends \Magento\Backend\App\AbstractAction +abstract class Auth extends AbstractAction { /** * User model factory @@ -24,24 +29,23 @@ abstract class Auth extends \Magento\Backend\App\AbstractAction /** * @var \Magento\Backend\Helper\Data */ - protected $_backendHelper; + protected $_backendDataHelper; /** * Construct * - * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\User\Model\UserFactory $userFactory - * @param \Magento\Backend\Helper\Data $backendHelper + * @param Context $context + * @param UserFactory $userFactory + * @param Data $backendDataHelper */ public function __construct( - \Magento\Backend\App\Action\Context $context, - \Magento\User\Model\UserFactory $userFactory, - \Magento\Backend\Helper\Data $backendHelper = null + Context $context, + UserFactory $userFactory, + Data $backendDataHelper = null ) { parent::__construct($context); $this->_userFactory = $userFactory; - $this->_backendHelper = $backendHelper ?: - ObjectManager::getInstance()->get(\Magento\Backend\Helper\Data::class); + $this->_backendDataHelper = $backendDataHelper ?: ObjectManager::getInstance()->get(Data::class); } /** @@ -50,7 +54,7 @@ public function __construct( * @param int $userId * @param string $resetPasswordToken * @return void - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ protected function _validateResetPasswordLinkToken($userId, $resetPasswordToken) { @@ -60,22 +64,20 @@ protected function _validateResetPasswordLinkToken($userId, $resetPasswordToken) $resetPasswordToken ) || empty($resetPasswordToken) || empty($userId) || $userId < 0 ) { - throw new \Magento\Framework\Exception\LocalizedException( - __('The password reset token is incorrect. Verify the token and try again.') - ); + throw new LocalizedException(__('Please correct the password reset token.')); } /** @var $user \Magento\User\Model\User */ $user = $this->_userFactory->create()->load($userId); if (!$user->getId()) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __('Please specify the correct account and try again.') ); } $userToken = $user->getRpToken(); if (!Security::compareStrings($userToken, $resetPasswordToken) || $user->isResetPasswordLinkTokenExpired()) { - throw new \Magento\Framework\Exception\LocalizedException(__('Your password reset link has expired.')); + throw new LocalizedException(__('Your password reset link has expired.')); } } diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php b/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php index a3021b798550e..9a1fcd5dc75c0 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php @@ -8,8 +8,16 @@ use Magento\Security\Model\SecurityManager; use Magento\Framework\App\ObjectManager; +use Magento\Backend\App\Action\Context; +use Magento\User\Model\UserFactory; +use Magento\Security\Model\SecurityManager; +use Magento\User\Model\ResourceModel\User\CollectionFactory; +use Magento\Framework\Validator\EmailAddress; +use Magento\Security\Model\PasswordResetRequestEvent; +use Magento\Framework\Exception\SecurityViolationException; +use Magento\User\Controller\Adminhtml\Auth; -class Forgotpassword extends \Magento\User\Controller\Adminhtml\Auth +class Forgotpassword extends Auth { /** * @var SecurityManager @@ -19,26 +27,26 @@ class Forgotpassword extends \Magento\User\Controller\Adminhtml\Auth /** * User model factory * - * @var \Magento\User\Model\ResourceModel\User\CollectionFactory + * @var CollectionFactory */ private $userCollectionFactory; /** - * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\User\Model\UserFactory $userFactory - * @param \Magento\Security\Model\SecurityManager $securityManager - * @param \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory + * @param Context $context + * @param UserFactory $userFactory + * @param SecurityManager $securityManager + * @param CollectionFactory $userCollectionFactory */ public function __construct( - \Magento\Backend\App\Action\Context $context, - \Magento\User\Model\UserFactory $userFactory, - \Magento\Security\Model\SecurityManager $securityManager, - \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory = null + Context $context, + UserFactory $userFactory, + SecurityManager $securityManager, + CollectionFactory $userCollectionFactory = null ) { parent::__construct($context, $userFactory); $this->securityManager = $securityManager; $this->userCollectionFactory = $userCollectionFactory ?: - ObjectManager::getInstance()->get(\Magento\User\Model\ResourceModel\User\CollectionFactory::class); + ObjectManager::getInstance()->get(CollectionFactory::class); } /** @@ -56,18 +64,18 @@ public function execute() $resultRedirect = $this->resultRedirectFactory->create(); if (!empty($email) && !empty($params)) { // Validate received data to be an email address - if (\Zend_Validate::is($email, \Magento\Framework\Validator\EmailAddress::class)) { + if (\Zend_Validate::is($email, EmailAddress::class)) { try { $this->securityManager->performSecurityCheck( - \Magento\Security\Model\PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST, + PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST, $email ); - } catch (\Magento\Framework\Exception\SecurityViolationException $exception) { + } catch (SecurityViolationException $exception) { $this->messageManager->addErrorMessage($exception->getMessage()); return $resultRedirect->setPath('admin'); } - $collection = $this->userCollectionFactory->create(); /** @var $collection \Magento\User\Model\ResourceModel\User\Collection */ + $collection = $this->userCollectionFactory->create(); $collection->addFieldToFilter('email', $email); $collection->load(false); @@ -77,7 +85,7 @@ public function execute() /** @var \Magento\User\Model\User $user */ $user = $this->_userFactory->create()->load($item->getId()); if ($user->getId()) { - $newPassResetToken = $this->_backendHelper->generateResetPasswordLinkToken(); + $newPassResetToken = $this->_backendDataHelper->generateResetPasswordLinkToken(); $user->changeResetPasswordLinkToken($newPassResetToken); $user->save(); $user->sendPasswordResetConfirmationEmail(); @@ -96,7 +104,7 @@ public function execute() $this->messageManager->addSuccess(__('We\'ll email you a link to reset your password.')); // @codingStandardsIgnoreEnd $this->getResponse()->setRedirect( - $this->_backendHelper->getHomePageUrl() + $this->_backendDataHelper->getHomePageUrl() ); return; } else { diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php b/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php index e17331519f60a..99bdf1a9dac26 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php @@ -6,7 +6,9 @@ */ namespace Magento\User\Controller\Adminhtml\Auth; -class ResetPasswordPost extends \Magento\User\Controller\Adminhtml\Auth +use Magento\User\Controller\Adminhtml\Auth; + +class ResetPasswordPost extends Auth { /** * Reset forgotten password @@ -27,7 +29,7 @@ public function execute() } catch (\Exception $exception) { $this->messageManager->addError(__('Your password reset link has expired.')); $this->getResponse()->setRedirect( - $this->_backendHelper->getHomePageUrl() + $this->_backendDataHelper->getHomePageUrl() ); return; } @@ -53,7 +55,7 @@ public function execute() $user->save(); $this->messageManager->addSuccess(__('You updated your password.')); $this->getResponse()->setRedirect( - $this->_backendHelper->getHomePageUrl() + $this->_backendDataHelper->getHomePageUrl() ); } } catch (\Magento\Framework\Validator\Exception $exception) { From 0322eb6d24e8bb12f00f0b7194ca188f9dd01739 Mon Sep 17 00:00:00 2001 From: Anshu Mishra <mishra.anshu1710@gmail.com> Date: Fri, 6 Jul 2018 11:50:34 +0530 Subject: [PATCH 0516/1171] Changes as per comments on Github --- app/code/Magento/User/Controller/Adminhtml/Auth.php | 4 ++-- .../Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth.php b/app/code/Magento/User/Controller/Adminhtml/Auth.php index abd1d33696b49..47d8b39e7d441 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth.php @@ -22,12 +22,12 @@ abstract class Auth extends AbstractAction /** * User model factory * - * @var \Magento\User\Model\UserFactory + * @var UserFactory */ protected $_userFactory; /** - * @var \Magento\Backend\Helper\Data + * @var Data */ protected $_backendDataHelper; diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php b/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php index 9a1fcd5dc75c0..21d9611cf1207 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php @@ -10,7 +10,6 @@ use Magento\Framework\App\ObjectManager; use Magento\Backend\App\Action\Context; use Magento\User\Model\UserFactory; -use Magento\Security\Model\SecurityManager; use Magento\User\Model\ResourceModel\User\CollectionFactory; use Magento\Framework\Validator\EmailAddress; use Magento\Security\Model\PasswordResetRequestEvent; From 0a9ae1e8d8dab0944dfa9517bbc352201e9d00de Mon Sep 17 00:00:00 2001 From: Anshu Mishra <mishra.anshu1710@gmail.com> Date: Wed, 11 Jul 2018 13:19:07 +0530 Subject: [PATCH 0517/1171] changes as per comments on GitHub --- .../User/Controller/Adminhtml/Auth.php | 12 +--------- .../Adminhtml/Auth/Forgotpassword.php | 17 ++++++++++---- .../Adminhtml/Auth/ResetPasswordPost.php | 22 +++++++++++++++++-- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth.php b/app/code/Magento/User/Controller/Adminhtml/Auth.php index 47d8b39e7d441..ea539b64a5cc3 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth.php @@ -7,11 +7,9 @@ namespace Magento\User\Controller\Adminhtml; use Magento\Framework\Encryption\Helper\Security; -use Magento\Framework\App\ObjectManager; use Magento\Backend\App\AbstractAction; use Magento\Backend\App\Action\Context; use Magento\User\Model\UserFactory; -use Magento\Backend\Helper\Data; use Magento\Framework\Exception\LocalizedException; /** @@ -25,27 +23,19 @@ abstract class Auth extends AbstractAction * @var UserFactory */ protected $_userFactory; - - /** - * @var Data - */ - protected $_backendDataHelper; /** * Construct * * @param Context $context * @param UserFactory $userFactory - * @param Data $backendDataHelper */ public function __construct( Context $context, - UserFactory $userFactory, - Data $backendDataHelper = null + UserFactory $userFactory ) { parent::__construct($context); $this->_userFactory = $userFactory; - $this->_backendDataHelper = $backendDataHelper ?: ObjectManager::getInstance()->get(Data::class); } /** diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php b/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php index 21d9611cf1207..c0e78fae2c2c6 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php @@ -15,6 +15,7 @@ use Magento\Security\Model\PasswordResetRequestEvent; use Magento\Framework\Exception\SecurityViolationException; use Magento\User\Controller\Adminhtml\Auth; +use Magento\Backend\Helper\Data; class Forgotpassword extends Auth { @@ -22,7 +23,7 @@ class Forgotpassword extends Auth * @var SecurityManager */ protected $securityManager; - + /** * User model factory * @@ -30,6 +31,11 @@ class Forgotpassword extends Auth */ private $userCollectionFactory; + /** + * @var Data + */ + private $backendDataHelper; + /** * @param Context $context * @param UserFactory $userFactory @@ -40,12 +46,15 @@ public function __construct( Context $context, UserFactory $userFactory, SecurityManager $securityManager, - CollectionFactory $userCollectionFactory = null + CollectionFactory $userCollectionFactory = null, + Data $backendDataHelper = null ) { parent::__construct($context, $userFactory); $this->securityManager = $securityManager; $this->userCollectionFactory = $userCollectionFactory ?: ObjectManager::getInstance()->get(CollectionFactory::class); + $this->backendDataHelper = $backendDataHelper ?: + ObjectManager::getInstance()->get(Data::class); } /** @@ -84,7 +93,7 @@ public function execute() /** @var \Magento\User\Model\User $user */ $user = $this->_userFactory->create()->load($item->getId()); if ($user->getId()) { - $newPassResetToken = $this->_backendDataHelper->generateResetPasswordLinkToken(); + $newPassResetToken = $this->backendDataHelper->generateResetPasswordLinkToken(); $user->changeResetPasswordLinkToken($newPassResetToken); $user->save(); $user->sendPasswordResetConfirmationEmail(); @@ -103,7 +112,7 @@ public function execute() $this->messageManager->addSuccess(__('We\'ll email you a link to reset your password.')); // @codingStandardsIgnoreEnd $this->getResponse()->setRedirect( - $this->_backendDataHelper->getHomePageUrl() + $this->backendDataHelper->getHomePageUrl() ); return; } else { diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php b/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php index 99bdf1a9dac26..b0d7d939c2409 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php @@ -7,9 +7,27 @@ namespace Magento\User\Controller\Adminhtml\Auth; use Magento\User\Controller\Adminhtml\Auth; +use Magento\Framework\App\ObjectManager; +use Magento\Backend\Helper\Data; class ResetPasswordPost extends Auth { + /** + * @var Data + */ + private $backendDataHelper; + + /** + * @param Context $context + * @param Data $backendDataHelper + */ + public function __construct( + Context $context, + Data $backendDataHelper = null + ) { + parent::__construct($context, $userFactory); + $this->backendDataHelper = $backendDataHelper ?: ObjectManager::getInstance()->get(Data::class); + } /** * Reset forgotten password * @@ -29,7 +47,7 @@ public function execute() } catch (\Exception $exception) { $this->messageManager->addError(__('Your password reset link has expired.')); $this->getResponse()->setRedirect( - $this->_backendDataHelper->getHomePageUrl() + $this->backendDataHelper->getHomePageUrl() ); return; } @@ -55,7 +73,7 @@ public function execute() $user->save(); $this->messageManager->addSuccess(__('You updated your password.')); $this->getResponse()->setRedirect( - $this->_backendDataHelper->getHomePageUrl() + $this->backendDataHelper->getHomePageUrl() ); } } catch (\Magento\Framework\Validator\Exception $exception) { From 4f0a6ab9dde9da3a6ae14bda7161c900976249db Mon Sep 17 00:00:00 2001 From: Anshu Mishra <mishra.anshu1710@gmail.com> Date: Wed, 11 Jul 2018 14:37:43 +0530 Subject: [PATCH 0518/1171] missing parameter correction --- .../User/Controller/Adminhtml/Auth/ResetPasswordPost.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php b/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php index b0d7d939c2409..f36efa2e2eb09 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php @@ -9,6 +9,7 @@ use Magento\User\Controller\Adminhtml\Auth; use Magento\Framework\App\ObjectManager; use Magento\Backend\Helper\Data; +use Magento\User\Model\UserFactory; class ResetPasswordPost extends Auth { @@ -19,10 +20,12 @@ class ResetPasswordPost extends Auth /** * @param Context $context + * @param UserFactory $userFactory * @param Data $backendDataHelper */ public function __construct( Context $context, + UserFactory $userFactory, Data $backendDataHelper = null ) { parent::__construct($context, $userFactory); From e740014aae149a03578570b8ea6729ef1a8fbae5 Mon Sep 17 00:00:00 2001 From: Anshu Mishra <mishra.anshu1710@gmail.com> Date: Thu, 12 Jul 2018 10:41:11 +0530 Subject: [PATCH 0519/1171] included missing context class --- .../Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php b/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php index f36efa2e2eb09..c2e29534b1251 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php @@ -7,6 +7,7 @@ namespace Magento\User\Controller\Adminhtml\Auth; use Magento\User\Controller\Adminhtml\Auth; +use Magento\Backend\App\Action\Context; use Magento\Framework\App\ObjectManager; use Magento\Backend\Helper\Data; use Magento\User\Model\UserFactory; From 0e24ef1b7038db90a1113c9afb7190953f1457b2 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Wed, 25 Jul 2018 23:24:30 +0530 Subject: [PATCH 0520/1171] Update regex in ControllerAclTest --- .../Test/Integrity/Magento/Backend/ControllerAclTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/ControllerAclTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/ControllerAclTest.php index 187cb9087013e..f204019988b79 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/ControllerAclTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/ControllerAclTest.php @@ -232,7 +232,7 @@ private function isItTest($relativeFilePath) */ private function getControllerPath($relativeFilePath) { - if (preg_match('~(Magento\/.*Controller\/Adminhtml\/.*)\.php~', $relativeFilePath, $matches)) { + if (preg_match('~(Magento\/[^\/]+\/Controller\/Adminhtml\/.*)\.php~', $relativeFilePath, $matches)) { if (count($matches) === 2) { $partPath = $matches[1]; return $partPath; From 4b5f84264b6ecca1c1a0babda47eec85ab5b4210 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Wed, 25 Jul 2018 23:56:03 +0530 Subject: [PATCH 0521/1171] Microrefactoring in product gallery block helper --- .../Catalog/Block/Adminhtml/Product/Helper/Form/Gallery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery.php index f30b37877b78f..0557a215383d3 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery.php @@ -102,7 +102,7 @@ public function getElementHtml() */ public function getImages() { - return $this->registry->registry('current_product')->getData('media_gallery') ?: null; + return $this->getDataObject()->getData('media_gallery') ?: null; } /** From 2bc4c01d245b4bf79031ab048f54de338f18c5a7 Mon Sep 17 00:00:00 2001 From: Prince Patel <mail.mageprince@gmail.com> Date: Thu, 26 Jul 2018 00:02:31 +0530 Subject: [PATCH 0522/1171] Update webapi.xml --- app/code/Magento/Customer/etc/webapi.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/code/Magento/Customer/etc/webapi.xml b/app/code/Magento/Customer/etc/webapi.xml index f9ef3a5f88228..c536e26bcc82a 100644 --- a/app/code/Magento/Customer/etc/webapi.xml +++ b/app/code/Magento/Customer/etc/webapi.xml @@ -200,12 +200,6 @@ <resource ref="anonymous"/> </resources> </route> - <route url="/V1/customers/resetPassword" method="POST"> - <service class="Magento\Customer\Api\AccountManagementInterface" method="resetPassword"/> - <resources> - <resource ref="anonymous"/> - </resources> - </route> <route url="/V1/customers/:customerId/confirm" method="GET"> <service class="Magento\Customer\Api\AccountManagementInterface" method="getConfirmationStatus"/> <resources> From 813219a81b13af9261b6023d9606a5810df694d5 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 25 Jul 2018 13:33:13 -0500 Subject: [PATCH 0523/1171] MAGETWO-92931: HTML entities in product name do not display properly in breadcrumb - modify underscore template to evaluate html entities --- .../Theme/view/frontend/web/templates/breadcrumbs.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Theme/view/frontend/web/templates/breadcrumbs.html b/app/code/Magento/Theme/view/frontend/web/templates/breadcrumbs.html index f298c0a58e814..792eb2e636005 100644 --- a/app/code/Magento/Theme/view/frontend/web/templates/breadcrumbs.html +++ b/app/code/Magento/Theme/view/frontend/web/templates/breadcrumbs.html @@ -10,9 +10,9 @@ <% if (crumb.link) { %> <a href="<%= crumb.link %>" title="<%- crumb.title %>"><%- crumb.label %></a> <% } else if (crumb.last) { %> - <strong><%- crumb.label %></strong> + <strong><%= crumb.label %></strong> <% } else { %> - <%- crumb.label %> + <%= crumb.label %> <% } %> </li> <% }); %> From eb8aa49351cc1dbe24cb7730c3466bfdde1f78e4 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 25 Jul 2018 13:38:55 -0500 Subject: [PATCH 0524/1171] MAGETWO-92931: HTML entities in product name do not display properly in breadcrumb - escape html, and the frontend will safely render html --- app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php index 01e246b5969a1..494b8c3e70c02 100644 --- a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php +++ b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php @@ -106,7 +106,7 @@ public function getJsonConfiguration() : string 'breadcrumbs' => [ 'categoryUrlSuffix' => $this->getCategoryUrlSuffix(), 'userCategoryPathInUrl' => (int)$this->isCategoryUsedInProductUrl(), - 'product' => $this->getProductName() + 'product' => $this->escaper->escapeHtml($this->getProductName()) ] ], JSON_HEX_TAG From dab6b20a3fa7aff62c5e94a5ab318ebd1a17e3cc Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 25 Jul 2018 13:49:24 -0500 Subject: [PATCH 0525/1171] MAGETWO-92931: HTML entities in product name do not display properly in breadcrumb - fix unit test --- .../Test/Unit/ViewModel/Product/BreadcrumbsTest.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php index 2f667c12a1ed6..39d105e0be470 100644 --- a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php @@ -53,11 +53,14 @@ protected function setUp() : void ->disableOriginalConstructor() ->getMockForAbstractClass(); + $escaper = $this->getObjectManager()->getObject(\Magento\Framework\Escaper::class); + $this->viewModel = $this->getObjectManager()->getObject( Breadcrumbs::class, [ 'catalogData' => $this->catalogHelper, 'scopeConfig' => $this->scopeConfig, + 'escaper' => $escaper ] ); } @@ -154,16 +157,16 @@ public function productJsonEncodeDataProvider() : array ], [ $this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test "']]), - '{"breadcrumbs":{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":"Test \""}}', + '{"breadcrumbs":{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":"Test ""}}', ], [ $this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test <b>x</b>']]), '{"breadcrumbs":{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":' - . '"Test \u003Cb\u003Ex\u003C\/b\u003E"}}', + . '"Test <b>x<\/b>"}}', ], [ $this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test \'abc\'']]), - '{"breadcrumbs":{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":"Test \'abc\'"}}' + '{"breadcrumbs":{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":"Test 'abc'"}}' ], ]; } From 9eca6fd1468514ed2dbcd904977ff22ba1f5bc08 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 25 Jul 2018 13:50:26 -0500 Subject: [PATCH 0526/1171] MAGETWO-92931: HTML entities in product name do not display properly in breadcrumb - fix unit test --- .../Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php index 39d105e0be470..670c0e827a0fd 100644 --- a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php @@ -166,7 +166,8 @@ public function productJsonEncodeDataProvider() : array ], [ $this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test \'abc\'']]), - '{"breadcrumbs":{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":"Test 'abc'"}}' + '{"breadcrumbs":' + . '{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":"Test 'abc'"}}' ], ]; } From 28683d4bbb895c04dbc7489695e61f55f66a32f2 Mon Sep 17 00:00:00 2001 From: KevinBKozan <kkozan@magento.com> Date: Wed, 25 Jul 2018 14:10:32 -0500 Subject: [PATCH 0527/1171] MQE-1112: Bump MFTF version in Magento - static fix for moduleSequence --- dev/tests/functional/utils/generate/moduleSequence.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/tests/functional/utils/generate/moduleSequence.php b/dev/tests/functional/utils/generate/moduleSequence.php index cddcc71338dc4..22688d1b75820 100644 --- a/dev/tests/functional/utils/generate/moduleSequence.php +++ b/dev/tests/functional/utils/generate/moduleSequence.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + require_once __DIR__ . '/../../../../../app/bootstrap.php'; $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); From 144717ccc786b07e28dcfbb9e16792a4eb7411cf Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Wed, 25 Jul 2018 14:14:09 -0500 Subject: [PATCH 0528/1171] MC-1416: Editing Text Content Block from the Stage with TinyMCE 4 turned on Remove onChangeContent event handler to prevent active editor delegation issue --- lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index ed82d169a2d4e..7ecbcedc23aa4 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -104,6 +104,7 @@ define([ if (mode === 'inline') { settings.inline = true; + varienGlobalEvents.removeEventHandler('tinymceChange', this.onChangeContent); } jQuery.when.apply(jQuery, deferreds).done(function () { From 8f2e47a88bb211a5cb241e0b71a25d61313704eb Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 25 Jul 2018 14:31:01 -0500 Subject: [PATCH 0529/1171] MAGETWO-92931: HTML entities in product name do not display properly in breadcrumb - fix unit test --- .../Test/Unit/ViewModel/Product/BreadcrumbsTest.php | 8 ++++---- .../Magento/Catalog/ViewModel/Product/Breadcrumbs.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php index 670c0e827a0fd..443cdaead6fcf 100644 --- a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php @@ -153,21 +153,21 @@ public function productJsonEncodeDataProvider() : array return [ [ $this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test ™']]), - '{"breadcrumbs":{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":"Test \u2122"}}', + '{"breadcrumbs":{"categoryUrlSuffix":"."html","userCategoryPathInUrl":0,"product":"Test \u2122"}}', ], [ $this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test "']]), - '{"breadcrumbs":{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":"Test ""}}', + '{"breadcrumbs":{"categoryUrlSuffix":"."html","userCategoryPathInUrl":0,"product":"Test ""}}', ], [ $this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test <b>x</b>']]), - '{"breadcrumbs":{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":' + '{"breadcrumbs":{"categoryUrlSuffix":"."html","userCategoryPathInUrl":0,"product":' . '"Test <b>x<\/b>"}}', ], [ $this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test \'abc\'']]), '{"breadcrumbs":' - . '{"categoryUrlSuffix":".\"html","userCategoryPathInUrl":0,"product":"Test 'abc'"}}' + . '{"categoryUrlSuffix":"."html","userCategoryPathInUrl":0,"product":"Test 'abc'"}}' ], ]; } diff --git a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php index 494b8c3e70c02..714fd3f9d3c2a 100644 --- a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php +++ b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php @@ -104,7 +104,7 @@ public function getJsonConfiguration() : string return json_encode( [ 'breadcrumbs' => [ - 'categoryUrlSuffix' => $this->getCategoryUrlSuffix(), + 'categoryUrlSuffix' => $this->escaper->escapeHtml($this->getCategoryUrlSuffix()), 'userCategoryPathInUrl' => (int)$this->isCategoryUsedInProductUrl(), 'product' => $this->escaper->escapeHtml($this->getProductName()) ] From 2e03740a031eb4360e975f137e22c29795da06d1 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Wed, 25 Jul 2018 14:37:55 -0500 Subject: [PATCH 0530/1171] MC-1416: Editing Text Content Block from the Stage with TinyMCE 4 turned on Add z-index configuration for toolbar --- lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index 7ecbcedc23aa4..05e68c3452a17 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -104,6 +104,11 @@ define([ if (mode === 'inline') { settings.inline = true; + + if (!isNaN(settings.toolbarZIndex)) { + tinyMCE4.ui.FloatPanel.zIndex = settings.toolbarZIndex; + } + varienGlobalEvents.removeEventHandler('tinymceChange', this.onChangeContent); } From 9cb6be49b3c473bbe6c40a7633bcaff26af622ab Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Wed, 25 Jul 2018 14:49:00 -0500 Subject: [PATCH 0531/1171] MC-1416: Editing Text Content Block from the Stage with TinyMCE 4 turned on Attach open_browser_callback in tinyMCE4Adapter's initialize --- lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index 05e68c3452a17..f824219a2d58d 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -47,6 +47,7 @@ define([ varienGlobalEvents.attachEventHandler('tinymceSetContent', this.updateTextArea); varienGlobalEvents.attachEventHandler('tinymceSaveContent', this.saveContent); varienGlobalEvents.attachEventHandler('tinymceUndo', this.onUndo); + varienGlobalEvents.attachEventHandler('open_browser_callback', this.openFileBrowser); if (typeof tinyMceEditors === 'undefined') { window.tinyMceEditors = $H({}); From b9a5f4dd39b6f728928f71defebaa0b58d10ef91 Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@adobe.com> Date: Wed, 25 Jul 2018 15:55:06 -0500 Subject: [PATCH 0532/1171] MAGETWO-92931: HTML entities in product name do not display properly in breadcrumb - added annotation --- app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml | 4 ++-- .../Mftf/Test/StorefrontProductNameWithDoubleQuote.xml | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 086841cad4a3c..0df091eb5f8ef 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -233,11 +233,11 @@ <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="productWithHTMLEntityTwo" type="product"> - <data key="sku" unique="suffix">SimpleTwo®Product</data> + <data key="sku" unique="suffix">SimpleTwo霁产品<カネボウPro</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">SimpleTwo®Product</data> + <data key="name" unique="suffix">SimpleTwo霁产品<カネボウPro</data> <data key="price">50.00</data> <data key="urlKey" unique="suffix">testurlkey</data> <data key="status">1</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml index 0553b08e53429..3d1880a909081 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml @@ -68,7 +68,7 @@ <description value="Product with html entities in the name should appear correctly on the PDP breadcrumbs on storefront"/> <severity value="CRITICAL"/> <group value="product"/> - <testCaseId value=""/> + <testCaseId value="MAGETWO-93794"/> </annotations> <before> <createData entity="_defaultCategory" stepKey="createCategoryOne"/> @@ -92,7 +92,6 @@ <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityTwo.name)}}" userInput="{{productWithHTMLEntityTwo.name}}" stepKey="seeCorrectNameProd2CategoryPage"/> <!--Open product display page--> - <!--<click selector="{{StorefrontCategoryProductSection.ProductTitleByNumber('1')}}" stepKey="checkTitle"/>--> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" stepKey="clickProductToGoProductPage"/> <waitForPageLoad stepKey="waitForProductDisplayPageLoad2"/> @@ -103,7 +102,7 @@ <!--Veriy the breadcrumbs on Product Display page--> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs1"/> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategoryOne.name$$" stepKey="seeCorrectBreadCrumbCategory"/> - <!--<see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="{{productWithHTMLEntityOne.name}}" stepKey="seeCorrectBreadCrumbProduct"/>--> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productOne.name$$" stepKey="seeCorrectBreadCrumbProduct"/> <click selector="{{StorefrontNavigationSection.topCategory($$createCategoryOne.name$$)}}" stepKey="goBackToCategoryPage"/> <waitForPageLoad stepKey="waitforCategoryPageToLoad2"/> @@ -116,8 +115,7 @@ <!--Veriy the breadcrumbs on Product Display page--> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs2"/> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategoryOne.name$$" stepKey="seeCorrectBreadCrumbCategory2"/> - <!--<see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="{{productWithHTMLEntityTwo.name}}" stepKey="seeCorrectBreadCrumbProduct2"/>--> - + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productTwo.name$$" stepKey="seeCorrectBreadCrumbProduct2"/> </test> </tests> From edfdec3ca9cddbf2191f2ccac2f349125053f059 Mon Sep 17 00:00:00 2001 From: Tomash Khamlai <tomash.khamlai@gmail.com> Date: Thu, 26 Jul 2018 01:43:49 +0300 Subject: [PATCH 0533/1171] Set proper text-aligh for the <th> element of the Subtotal column --- .../Magento/blank/Magento_Sales/web/css/source/_email.less | 3 ++- .../Magento/luma/Magento_Sales/web/css/source/_email.less | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_email.less b/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_email.less index 84adec39c8892..215d7d8b322b4 100644 --- a/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_email.less +++ b/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_email.less @@ -203,7 +203,8 @@ text-align: center; } - .item-price { + .item-price, + .item-subtotal { text-align: right; } diff --git a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_email.less b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_email.less index 9a3f433618c36..3f19d1020bab9 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_email.less +++ b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_email.less @@ -207,7 +207,8 @@ text-align: center; } - .item-price { + .item-price, + .item-subtotal { text-align: right; } From 299ef7f7b06e8db1a175c8e4dbb5a5e4bbb6e413 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 25 Jul 2018 19:32:48 -0500 Subject: [PATCH 0534/1171] MAGETWO-92931: HTML entities in product name do not display properly in breadcrumb - remove double line --- .../Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php index 443cdaead6fcf..dbf1292e57368 100644 --- a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php @@ -131,7 +131,6 @@ public function testGetJsonConfiguration($product, string $expectedJson) : void ->method('getProduct') ->willReturn($product); - $this->scopeConfig->expects($this->any()) ->method('isSetFlag') ->with('catalog/seo/product_use_categories', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) From 3949cdbd349a46c0b51b0c0e1db1e770808acb9c Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 25 Jul 2018 20:12:35 -0500 Subject: [PATCH 0535/1171] MAGETWO-92931: HTML entities in product name do not display properly in breadcrumb - rename method for static test --- .../Catalog/ViewModel/Product/Breadcrumbs.php | 15 +++++++++++++-- .../frontend/templates/product/breadcrumbs.phtml | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php index 714fd3f9d3c2a..95f2531e5fdca 100644 --- a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php +++ b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php @@ -95,11 +95,11 @@ public function getProductName(): string } /** - * Returns breadcrumb json. + * Returns breadcrumb json with html escaped names * * @return string */ - public function getJsonConfiguration() : string + public function getJsonConfigurationHtmlEscaped() : string { return json_encode( [ @@ -112,4 +112,15 @@ public function getJsonConfiguration() : string JSON_HEX_TAG ); } + + /** + * Returns breadcrumb json. + * + * @return string + * @deprecated in favor of new method with name {suffix}Html{postfix}() + */ + public function getJsonConfiguration() + { + return $this->getJsonConfigurationHtmlEscaped(); + } } diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/breadcrumbs.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/breadcrumbs.phtml index b41598a100059..c4aa84704b598 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/breadcrumbs.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/breadcrumbs.phtml @@ -10,6 +10,6 @@ $viewModel = $block->getData('viewModel'); <div class="breadcrumbs"></div> <script type="text/x-magento-init"> { - ".breadcrumbs": <?= $viewModel->getJsonConfiguration() ?> + ".breadcrumbs": <?= $viewModel->getJsonConfigurationHtmlEscaped() ?> } </script> From 3777453fede8cdcd28742271b0deb30f5595b520 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Thu, 26 Jul 2018 10:48:15 +0300 Subject: [PATCH 0536/1171] MAGETWO-93740: [Forwardport] Ability to configure dimensions --- .../Model/ResourceModel/Index.php | 41 +- .../Unit/Model/ResourceModel/IndexTest.php | 21 +- app/code/Magento/AdvancedSearch/etc/di.xml | 13 + .../PriceIndexerDimensionsModeSetCommand.php | 196 ++++++++ .../Category/Product/TableMaintainer.php | 8 +- .../Indexer/Product/Price/AbstractAction.php | 270 ++++++---- .../Indexer/Product/Price/Action/Full.php | 410 ++++++++++++++-- .../Price/DimensionCollectionFactory.php | 70 +++ .../Price/DimensionModeConfiguration.php | 90 ++++ .../Indexer/Product/Price/ModeSwitcher.php | 166 +++++++ .../Product/Price/Plugin/CustomerGroup.php | 104 +++- .../Product/Price/Plugin/TableResolver.php | 140 ++++++ .../Indexer/Product/Price/Plugin/Website.php | 119 ++++- .../Product/Price/PriceTableResolver.php | 77 +++ .../Indexer/Product/Price/TableMaintainer.php | 280 +++++++++++ .../ResourceModel/Layer/Filter/Price.php | 67 ++- .../ResourceModel/Product/Collection.php | 109 ++-- ...LinkedProductSelectBuilderByIndexPrice.php | 39 +- .../Price/CustomOptionPriceModifier.php | 464 ++++++++++++++++++ .../Product/Indexer/Price/DefaultPrice.php | 119 ++++- .../Product/Indexer/Price/Factory.php | 21 +- .../Indexer/Price/Query/BaseFinalPrice.php | 330 +++++++++++++ .../Price/Query/JoinAttributeProcessor.php | 112 +++++ .../Indexer/Price/SimpleProductPrice.php | 89 ++++ .../Product/Price/Plugin/WebsiteTest.php | 162 +++++- .../ResourceModel/Layer/Filter/PriceTest.php | 48 -- ...edProductSelectBuilderByIndexPriceTest.php | 21 +- app/code/Magento/Catalog/etc/di.xml | 56 ++- app/code/Magento/Catalog/etc/frontend/di.xml | 1 + .../Magento/Catalog/etc/product_types.xml | 2 + .../Magento/Catalog/etc/webapi_rest/di.xml | 1 + .../Magento/Catalog/etc/webapi_soap/di.xml | 1 + app/code/Magento/CatalogInventory/etc/di.xml | 18 + app/code/Magento/CatalogRule/etc/di.xml | 23 +- .../Aggregation/DataProvider/QueryBuilder.php | 48 +- .../Adapter/Mysql/Dynamic/DataProvider.php | 55 ++- .../Aggregation/Category/DataProvider.php | 2 +- .../CatalogSearch/Model/Indexer/Fulltext.php | 4 +- .../Search/FilterMapper/ExclusionStrategy.php | 52 +- .../DataProvider/QueryBuilderTest.php | 23 +- .../Mysql/Dynamic/DataProviderTest.php | 20 +- .../Test/Unit/Model/Indexer/FulltextTest.php | 2 +- .../FilterMapper/ExclusionStrategyTest.php | 26 +- app/code/Magento/CatalogSearch/etc/di.xml | 21 + .../CustomerGroupDimensionProvider.php | 61 +++ app/code/Magento/Customer/etc/di.xml | 7 + .../Model/ResourceModel/Index.php | 12 +- .../Unit/Model/ResourceModel/IndexTest.php | 17 +- .../Indexer/WebsiteDimensionProvider.php | 72 +++ .../Store/Model/StoreDimensionProvider.php | 2 +- app/code/Magento/Store/etc/di.xml | 17 + app/etc/di.xml | 1 + .../Api/ProductRenderListInterfaceTest.php | 1 - .../Model/Export/AdvancedPricingTest.php | 20 +- .../Magento/Quote/Model/Quote/AddressTest.php | 67 ++- .../Model/Sales/Total/Quote/SubtotalTest.php | 36 +- .../Tax/Model/Sales/Total/Quote/TaxTest.php | 35 +- .../Framework/Indexer/CacheContext.php | 3 +- .../Magento/Framework/Indexer/Dimension.php | 10 + .../Framework/Indexer/DimensionFactory.php | 13 +- .../DimensionProviderInterface.php | 2 +- .../DimensionalIndexerInterface.php | 10 +- .../Indexer/MultiDimensionProvider.php | 155 ++++++ .../Indexer/SaveHandler/IndexerHandler.php | 1 - .../Framework/Indexer/SaveHandlerFactory.php | 1 - .../ScopeResolver/IndexScopeResolver.php | 8 +- .../Framework/Indexer/StructureFactory.php | 2 - .../Test/Unit/MultiDimensionProviderTest.php | 255 ++++++++++ .../ScopeResolver/IndexScopeResolverTest.php | 14 +- .../Request/IndexScopeResolverInterface.php | 4 +- 70 files changed, 4368 insertions(+), 399 deletions(-) create mode 100644 app/code/Magento/Catalog/Console/Command/PriceIndexerDimensionsModeSetCommand.php create mode 100644 app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionCollectionFactory.php create mode 100644 app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionModeConfiguration.php create mode 100644 app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php create mode 100644 app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/TableResolver.php create mode 100644 app/code/Magento/Catalog/Model/Indexer/Product/Price/PriceTableResolver.php create mode 100644 app/code/Magento/Catalog/Model/Indexer/Product/Price/TableMaintainer.php create mode 100644 app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CustomOptionPriceModifier.php create mode 100644 app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php create mode 100644 app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/JoinAttributeProcessor.php create mode 100644 app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/SimpleProductPrice.php delete mode 100644 app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Layer/Filter/PriceTest.php create mode 100644 app/code/Magento/Customer/Model/Indexer/CustomerGroupDimensionProvider.php create mode 100644 app/code/Magento/Store/Model/Indexer/WebsiteDimensionProvider.php rename lib/internal/Magento/Framework/Indexer/{Dimension => }/DimensionProviderInterface.php (90%) rename lib/internal/Magento/Framework/Indexer/{Dimension => }/DimensionalIndexerInterface.php (55%) create mode 100644 lib/internal/Magento/Framework/Indexer/MultiDimensionProvider.php create mode 100644 lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php diff --git a/app/code/Magento/AdvancedSearch/Model/ResourceModel/Index.php b/app/code/Magento/AdvancedSearch/Model/ResourceModel/Index.php index c2379e9dff062..7751a3b75092d 100644 --- a/app/code/Magento/AdvancedSearch/Model/ResourceModel/Index.php +++ b/app/code/Magento/AdvancedSearch/Model/ResourceModel/Index.php @@ -6,18 +6,22 @@ namespace Magento\AdvancedSearch\Model\ResourceModel; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Search\Request\IndexScopeResolverInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\Model\ResourceModel\Db\Context; use Magento\Framework\EntityManager\MetadataPool; use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Framework\App\ObjectManager; -use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver as TableResolver; use Magento\Framework\Search\Request\Dimension; use Magento\Catalog\Model\Indexer\Category\Product\AbstractAction; +use Magento\Framework\Search\Request\IndexScopeResolverInterface as TableResolver; +use Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory; /** * @api * @since 100.1.0 + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Index extends AbstractDb { @@ -38,6 +42,11 @@ class Index extends AbstractDb */ private $tableResolver; + /** + * @var DimensionCollectionFactory|null + */ + private $dimensionCollectionFactory; + /** * Index constructor. * @param Context $context @@ -45,18 +54,22 @@ class Index extends AbstractDb * @param MetadataPool $metadataPool * @param null $connectionName * @param TableResolver|null $tableResolver + * @param DimensionCollectionFactory|null $dimensionCollectionFactory */ public function __construct( Context $context, StoreManagerInterface $storeManager, MetadataPool $metadataPool, $connectionName = null, - TableResolver $tableResolver = null + TableResolver $tableResolver = null, + DimensionCollectionFactory $dimensionCollectionFactory = null ) { parent::__construct($context, $connectionName); $this->storeManager = $storeManager; $this->metadataPool = $metadataPool; - $this->tableResolver = $tableResolver ?: ObjectManager::getInstance()->get(TableResolver::class); + $this->tableResolver = $tableResolver ?: ObjectManager::getInstance()->get(IndexScopeResolverInterface::class); + $this->dimensionCollectionFactory = $dimensionCollectionFactory + ?: ObjectManager::getInstance()->get(DimensionCollectionFactory::class); } /** @@ -78,18 +91,22 @@ protected function _construct() protected function _getCatalogProductPriceData($productIds = null) { $connection = $this->getConnection(); - - $select = $connection->select()->from( - $this->getTable('catalog_product_index_price'), - ['entity_id', 'customer_group_id', 'website_id', 'min_price'] - ); - - if ($productIds) { - $select->where('entity_id IN (?)', $productIds); + $catalogProductIndexPriceSelect = []; + + foreach ($this->dimensionCollectionFactory->create() as $dimensions) { + $catalogProductIndexPriceSelect[] = $connection->select()->from( + $this->tableResolver->resolve('catalog_product_index_price', $dimensions), + ['entity_id', 'customer_group_id', 'website_id', 'min_price'] + ); + if ($productIds) { + current($catalogProductIndexPriceSelect)->where('entity_id IN (?)', $productIds); + } } + $catalogProductIndexPriceUnionSelect = $connection->select()->union($catalogProductIndexPriceSelect); + $result = []; - foreach ($connection->fetchAll($select) as $row) { + foreach ($connection->fetchAll($catalogProductIndexPriceUnionSelect) as $row) { $result[$row['website_id']][$row['entity_id']][$row['customer_group_id']] = round($row['min_price'], 2); } diff --git a/app/code/Magento/AdvancedSearch/Test/Unit/Model/ResourceModel/IndexTest.php b/app/code/Magento/AdvancedSearch/Test/Unit/Model/ResourceModel/IndexTest.php index 185e932406e5b..1f37e40842f54 100644 --- a/app/code/Magento/AdvancedSearch/Test/Unit/Model/ResourceModel/IndexTest.php +++ b/app/code/Magento/AdvancedSearch/Test/Unit/Model/ResourceModel/IndexTest.php @@ -15,6 +15,9 @@ use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Select; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class IndexTest extends \PHPUnit\Framework\TestCase { /** @@ -59,10 +62,24 @@ protected function setUp() $this->resourceConnectionMock->expects($this->any())->method('getConnection')->willReturn($this->adapterMock); $this->metadataPoolMock = $this->createMock(MetadataPool::class); + $indexScopeResolverMock = $this->createMock( + \Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver::class + ); + $traversableMock = $this->createMock(\Traversable::class); + $dimensionsMock = $this->createMock(\Magento\Framework\Indexer\MultiDimensionProvider::class); + $dimensionsMock->method('getIterator')->willReturn($traversableMock); + $dimensionFactoryMock = $this->createMock( + \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory::class + ); + $dimensionFactoryMock->method('create')->willReturn($dimensionsMock); + $this->model = new Index( $this->resourceContextMock, $this->storeManagerMock, - $this->metadataPoolMock + $this->metadataPoolMock, + 'connectionName', + $indexScopeResolverMock, + $dimensionFactoryMock ); } @@ -71,11 +88,13 @@ public function testGetPriceIndexDataUsesFrontendPriceIndexerTable() $storeId = 1; $storeMock = $this->createMock(StoreInterface::class); $storeMock->expects($this->any())->method('getId')->willReturn($storeId); + $storeMock->method('getWebsiteId')->willReturn(1); $this->storeManagerMock->expects($this->once())->method('getStore')->with($storeId)->willReturn($storeMock); $selectMock = $this->createMock(Select::class); $selectMock->expects($this->any())->method('from')->willReturnSelf(); $selectMock->expects($this->any())->method('where')->willReturnSelf(); + $selectMock->expects($this->any())->method('union')->willReturnSelf(); $this->adapterMock->expects($this->once())->method('select')->willReturn($selectMock); $this->adapterMock->expects($this->once())->method('fetchAll')->with($selectMock)->willReturn([]); diff --git a/app/code/Magento/AdvancedSearch/etc/di.xml b/app/code/Magento/AdvancedSearch/etc/di.xml index 9ec75f56bbf7b..21e19fd58825b 100644 --- a/app/code/Magento/AdvancedSearch/etc/di.xml +++ b/app/code/Magento/AdvancedSearch/etc/di.xml @@ -19,6 +19,12 @@ <argument name="title" xsi:type="string" translatable="true">Did you mean</argument> </arguments> </type> + <type name="Magento\AdvancedSearch\Model\Client\ClientResolver"> + <arguments> + <argument name="path" xsi:type="const">Magento\CatalogSearch\Model\ResourceModel\EngineInterface::CONFIG_ENGINE_PATH</argument> + <argument name="scopeType" xsi:type="const">\Magento\Store\Model\ScopeInterface::SCOPE_STORE</argument> + </arguments> + </type> <type name="Magento\AdvancedSearch\Model\SuggestedQueries"> <arguments> <argument name="data" xsi:type="array"> @@ -26,5 +32,12 @@ </argument> </arguments> </type> + <type name="Magento\AdvancedSearch\Model\ResourceModel\Index"> + <arguments> + <argument name="tableResolver" xsi:type="object"> + Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver + </argument> + </arguments> + </type> <preference for="Magento\AdvancedSearch\Model\Adapter\DataMapper\AdditionalFieldsProviderInterface" type="Magento\AdvancedSearch\Model\Adapter\DataMapper\AdditionalFieldsProvider" /> </config> diff --git a/app/code/Magento/Catalog/Console/Command/PriceIndexerDimensionsModeSetCommand.php b/app/code/Magento/Catalog/Console/Command/PriceIndexerDimensionsModeSetCommand.php new file mode 100644 index 0000000000000..0660d4d6ceb36 --- /dev/null +++ b/app/code/Magento/Catalog/Console/Command/PriceIndexerDimensionsModeSetCommand.php @@ -0,0 +1,196 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Console\Command; + +use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Magento\Framework\Exception\LocalizedException; +use Magento\Indexer\Console\Command\AbstractIndexerCommand; +use Magento\Framework\App\ObjectManagerFactory; +use Magento\Catalog\Model\Indexer\Product\Price\ModeSwitcher; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\Config\ConfigResource\ConfigInterface; +use Magento\Framework\App\Cache\TypeListInterface; + +/** + * Command to change price indexer dimensions mode + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class PriceIndexerDimensionsModeSetCommand extends AbstractIndexerCommand +{ + const INPUT_KEY_MODE = 'mode'; + + /** + * ScopeConfigInterface + * + * @var ScopeConfigInterface + */ + private $configReader; + + /** + * ConfigInterface + * + * @var ConfigInterface + */ + private $configWriter; + + /** + * TypeListInterface + * + * @var TypeListInterface + */ + private $cacheTypeList; + + /** + * ModeSwitcher + * + * @var ModeSwitcher + */ + private $modeSwitcher; + + /** + * @param ObjectManagerFactory $objectManagerFactory + * @param ScopeConfigInterface $configReader + * @param ConfigInterface $configWriter + * @param TypeListInterface $cacheTypeList + * @param ModeSwitcher $modeSwitcher + */ + public function __construct( + ObjectManagerFactory $objectManagerFactory, + ScopeConfigInterface $configReader, + ConfigInterface $configWriter, + TypeListInterface $cacheTypeList, + ModeSwitcher $modeSwitcher + ) { + $this->configReader = $configReader; + $this->configWriter = $configWriter; + $this->cacheTypeList = $cacheTypeList; + $this->modeSwitcher = $modeSwitcher; + parent::__construct($objectManagerFactory); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->setName('indexer:set-dimensions-mode:catalog_product_price') + ->setDescription('Set Indexer Dimensions Mode') + ->setDefinition($this->getInputList()); + + parent::configure(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $errors = $this->validate($input); + + if ($errors) { + throw new \InvalidArgumentException(implode(PHP_EOL, $errors)); + } + + $returnValue = \Magento\Framework\Console\Cli::RETURN_SUCCESS; + + $indexer = $this->getObjectManager()->get(\Magento\Indexer\Model\Indexer::class); + $indexer->load(\Magento\Catalog\Model\Indexer\Product\Price\Processor::INDEXER_ID); + + try { + $currentMode = $input->getArgument(self::INPUT_KEY_MODE); + $previousMode = $this->configReader->getValue(ModeSwitcher::XML_PATH_PRICE_DIMENSIONS_MODE) ?: + DimensionModeConfiguration::DIMENSION_NONE; + + if ($previousMode !== $currentMode) { + //Create new tables and move data + $this->modeSwitcher->createTables($currentMode); + $this->modeSwitcher->moveData($currentMode, $previousMode); + + //Change config options + $this->configWriter->saveConfig(ModeSwitcher::XML_PATH_PRICE_DIMENSIONS_MODE, $currentMode); + $this->cacheTypeList->cleanType('config'); + $indexer->invalidate(); + + //Delete old tables + $this->modeSwitcher->dropTables($previousMode); + + $output->writeln( + 'Dimensions mode for indexer ' . $indexer->getTitle() . ' was changed from \'' + . $previousMode . '\' to \'' . $currentMode . '\'' + ); + } else { + $output->writeln('Dimensions mode for indexer ' . $indexer->getTitle() . ' has not been changed'); + } + } catch (LocalizedException $e) { + $output->writeln($e->getMessage() . PHP_EOL); + // we must have an exit code higher than zero to indicate something was wrong + $returnValue = \Magento\Framework\Console\Cli::RETURN_FAILURE; + } catch (\Exception $e) { + $output->writeln($indexer->getTitle() . " indexer process unknown error:" . PHP_EOL); + $output->writeln($e->getMessage() . PHP_EOL); + // we must have an exit code higher than zero to indicate something was wrong + $returnValue = \Magento\Framework\Console\Cli::RETURN_FAILURE; + } + + return $returnValue; + } + + /** + * Get list of arguments for the command + * + * @return InputOption[] + */ + public function getInputList(): array + { + $modeOptions[] = new InputArgument( + self::INPUT_KEY_MODE, + InputArgument::REQUIRED, + 'Indexer dimensions mode ['. DimensionModeConfiguration::DIMENSION_NONE + . '|' . DimensionModeConfiguration::DIMENSION_WEBSITE + . '|' . DimensionModeConfiguration::DIMENSION_CUSTOMER_GROUP + . '|' . DimensionModeConfiguration::DIMENSION_WEBSITE_AND_CUSTOMER_GROUP .']' + ); + return $modeOptions; + } + + /** + * Check if all admin options are provided + * + * @param InputInterface $input + * @return string[] + */ + public function validate(InputInterface $input): array + { + $errors = []; + + $acceptedModeValues = ' Accepted values for ' . self::INPUT_KEY_MODE . ' are \'' + . DimensionModeConfiguration::DIMENSION_NONE . '\', \'' + . DimensionModeConfiguration::DIMENSION_WEBSITE . '\', \'' + . DimensionModeConfiguration::DIMENSION_CUSTOMER_GROUP . '\', \'' + . DimensionModeConfiguration::DIMENSION_WEBSITE_AND_CUSTOMER_GROUP . '\''; + + $inputMode = $input->getArgument(self::INPUT_KEY_MODE); + if (!$inputMode) { + $errors[] = 'Missing argument \'' . self::INPUT_KEY_MODE .'\'.' . $acceptedModeValues; + } elseif (!in_array( + $inputMode, + [ + DimensionModeConfiguration::DIMENSION_NONE, + DimensionModeConfiguration::DIMENSION_WEBSITE, + DimensionModeConfiguration::DIMENSION_CUSTOMER_GROUP, + DimensionModeConfiguration::DIMENSION_WEBSITE_AND_CUSTOMER_GROUP + ] + )) { + $errors[] = $acceptedModeValues; + } + return $errors; + } +} diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/TableMaintainer.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/TableMaintainer.php index 105a6dbf30456..3c2629bc570f2 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/TableMaintainer.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/TableMaintainer.php @@ -91,6 +91,8 @@ private function getTable($table) * @param string $newTableName * * @return void + * + * @throws \Zend_Db_Exception */ private function createTable($mainTableName, $newTableName) { @@ -135,6 +137,8 @@ public function getMainTable(int $storeId) * @param $storeId * * @return void + * + * @throws \Zend_Db_Exception */ public function createTablesForStore(int $storeId) { @@ -206,12 +210,12 @@ public function createMainTmpTable(int $storeId) * * @return string * - * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getMainTmpTable(int $storeId) { if (!isset($this->mainTmpTable[$storeId])) { - throw new \Exception('Temporary table does not exist'); + throw new \Magento\Framework\Exception\NoSuchEntityException('Temporary table does not exist'); } return $this->mainTmpTable[$storeId]; } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php index 7aed842713f5d..4c7159b6e4f82 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php @@ -5,7 +5,11 @@ */ namespace Magento\Catalog\Model\Indexer\Product\Price; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Indexer\DimensionalIndexerInterface; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; /** * Abstract action reindex class @@ -17,7 +21,7 @@ abstract class AbstractAction /** * Default Product Type Price indexer resource model * - * @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice + * @var DefaultPrice */ protected $_defaultIndexerResource; @@ -77,6 +81,16 @@ abstract class AbstractAction */ private $tierPriceIndexResource; + /** + * @var \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory + */ + private $dimensionCollectionFactory; + + /** + * @var TableMaintainer + */ + private $tableMaintainer; + /** * @param \Magento\Framework\App\Config\ScopeConfigInterface $config * @param \Magento\Store\Model\StoreManagerInterface $storeManager @@ -85,8 +99,13 @@ abstract class AbstractAction * @param \Magento\Framework\Stdlib\DateTime $dateTime * @param \Magento\Catalog\Model\Product\Type $catalogProductType * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Factory $indexerPriceFactory - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice $defaultIndexerResource - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\TierPrice $tierPriceIndexResource + * @param DefaultPrice $defaultIndexerResource + * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\TierPrice|null $tierPriceIndexResource + * @param DimensionCollectionFactory|null $dimensionCollectionFactory + * @param TableMaintainer|null $tableMaintainer + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\App\Config\ScopeConfigInterface $config, @@ -96,8 +115,10 @@ public function __construct( \Magento\Framework\Stdlib\DateTime $dateTime, \Magento\Catalog\Model\Product\Type $catalogProductType, \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Factory $indexerPriceFactory, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice $defaultIndexerResource, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\TierPrice $tierPriceIndexResource = null + DefaultPrice $defaultIndexerResource, + \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\TierPrice $tierPriceIndexResource = null, + \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory $dimensionCollectionFactory = null, + \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer $tableMaintainer = null ) { $this->_config = $config; $this->_storeManager = $storeManager; @@ -108,9 +129,15 @@ public function __construct( $this->_indexerPriceFactory = $indexerPriceFactory; $this->_defaultIndexerResource = $defaultIndexerResource; $this->_connection = $this->_defaultIndexerResource->getConnection(); - $this->tierPriceIndexResource = $tierPriceIndexResource ?: ObjectManager::getInstance()->get( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\TierPrice::class - ); + $this->tierPriceIndexResource = $tierPriceIndexResource ?? ObjectManager::getInstance()->get( + \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\TierPrice::class + ); + $this->dimensionCollectionFactory = $dimensionCollectionFactory ?? ObjectManager::getInstance()->get( + \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory::class + ); + $this->tableMaintainer = $tableMaintainer ?? ObjectManager::getInstance()->get( + \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer::class + ); } /** @@ -126,30 +153,29 @@ abstract public function execute($ids); * * @param array $processIds * @return \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @deprecated Used only for backward compatibility for indexer, which not support indexation by dimensions */ protected function _syncData(array $processIds = []) { - // delete invalid rows - $select = $this->_connection->select()->from( - ['index_price' => $this->getIndexTargetTable()], - null - )->joinLeft( - ['ip_tmp' => $this->_defaultIndexerResource->getIdxTable()], - 'index_price.entity_id = ip_tmp.entity_id AND index_price.website_id = ip_tmp.website_id', - [] - )->where( - 'ip_tmp.entity_id IS NULL' - ); - if (!empty($processIds)) { - $select->where('index_price.entity_id IN(?)', $processIds); - } - $sql = $select->deleteFromSelect('index_price'); - $this->_connection->query($sql); + // for backward compatibility split data from old idx table on dimension tables + foreach ($this->dimensionCollectionFactory->create() as $dimensions) { + $insertSelect = $this->getConnection()->select()->from( + ['ip_tmp' => $this->_defaultIndexerResource->getIdxTable()] + ); - $this->_insertFromTable( - $this->_defaultIndexerResource->getIdxTable(), - $this->getIndexTargetTable() - ); + foreach ($dimensions as $dimension) { + if ($dimension->getName() === WebsiteDimensionProvider::DIMENSION_NAME) { + $insertSelect->where('ip_tmp.website_id = ?', $dimension->getValue()); + } + if ($dimension->getName() === CustomerGroupDimensionProvider::DIMENSION_NAME) { + $insertSelect->where('ip_tmp.customer_group_id = ?', $dimension->getValue()); + } + } + + $query = $insertSelect->insertFromSelect($this->tableMaintainer->getMainTable($dimensions)); + $this->getConnection()->query($query); + } return $this; } @@ -157,12 +183,15 @@ protected function _syncData(array $processIds = []) * Prepare website current dates table * * @return \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction + * + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ protected function _prepareWebsiteDateTable() { $baseCurrency = $this->_config->getValue(\Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE); - $select = $this->_connection->select()->from( + $select = $this->getConnection()->select()->from( ['cw' => $this->_defaultIndexerResource->getTable('store_website')], ['website_id'] )->join( @@ -174,7 +203,7 @@ protected function _prepareWebsiteDateTable() ); $data = []; - foreach ($this->_connection->fetchAll($select) as $item) { + foreach ($this->getConnection()->fetchAll($select) as $item) { /** @var $website \Magento\Store\Model\Website */ $website = $this->_storeManager->getWebsite($item['website_id']); @@ -199,6 +228,7 @@ protected function _prepareWebsiteDateTable() 'website_id' => $website->getId(), 'website_date' => $this->_dateTime->formatDate($timestamp, false), 'rate' => $rate, + 'default_store_id' => $store->getId() ]; } } @@ -207,7 +237,7 @@ protected function _prepareWebsiteDateTable() $this->_emptyTable($table); if ($data) { foreach ($data as $row) { - $this->_connection->insertOnDuplicate($table, $row, array_keys($row)); + $this->getConnection()->insertOnDuplicate($table, $row, array_keys($row)); } } @@ -230,9 +260,13 @@ protected function _prepareTierPriceIndex($entityIds = null) /** * Retrieve price indexers per product type * + * @param bool $fullReindexAction + * * @return \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceInterface[] + * + * @throws \Magento\Framework\Exception\LocalizedException */ - public function getTypeIndexers() + public function getTypeIndexers($fullReindexAction = false) { if ($this->_indexers === null) { $this->_indexers = []; @@ -242,14 +276,20 @@ public function getTypeIndexers() $typeInfo['price_indexer'] ) ? $typeInfo['price_indexer'] : get_class($this->_defaultIndexerResource); - $isComposite = !empty($typeInfo['composite']); $indexer = $this->_indexerPriceFactory->create( - $modelName - )->setTypeId( - $typeId - )->setIsComposite( - $isComposite + $modelName, + [ + 'fullReindexAction' => $fullReindexAction + ] ); + // left setters for backward compatibility + if ($indexer instanceof DefaultPrice) { + $indexer->setTypeId( + $typeId + )->setIsComposite( + !empty($typeInfo['composite']) + ); + } $this->_indexers[$typeId] = $indexer; } } @@ -262,7 +302,9 @@ public function getTypeIndexers() * * @param string $productTypeId * @return \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceInterface + * * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\LocalizedException */ protected function _getIndexer($productTypeId) { @@ -283,19 +325,19 @@ protected function _getIndexer($productTypeId) */ protected function _insertFromTable($sourceTable, $destTable, $where = null) { - $sourceColumns = array_keys($this->_connection->describeTable($sourceTable)); - $targetColumns = array_keys($this->_connection->describeTable($destTable)); - $select = $this->_connection->select()->from($sourceTable, $sourceColumns); + $sourceColumns = array_keys($this->getConnection()->describeTable($sourceTable)); + $targetColumns = array_keys($this->getConnection()->describeTable($destTable)); + $select = $this->getConnection()->select()->from($sourceTable, $sourceColumns); if ($where) { $select->where($where); } - $query = $this->_connection->insertFromSelect( + $query = $this->getConnection()->insertFromSelect( $select, $destTable, $targetColumns, \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE ); - $this->_connection->query($query); + $this->getConnection()->query($query); } /** @@ -306,7 +348,7 @@ protected function _insertFromTable($sourceTable, $destTable, $where = null) */ protected function _emptyTable($table) { - $this->_connection->delete($table); + $this->getConnection()->delete($table); } /** @@ -314,46 +356,64 @@ protected function _emptyTable($table) * * @param array $changedIds * @return array Affected ids + * + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ protected function _reindexRows($changedIds = []) { - $this->_emptyTable($this->_defaultIndexerResource->getIdxTable()); $this->_prepareWebsiteDateTable(); $productsTypes = $this->getProductsTypes($changedIds); - $compositeIds = []; - $notCompositeIds = []; + $parentProductsTypes = $this->getParentProductsTypes($changedIds); + + $changedIds = array_merge($changedIds, ...array_values($parentProductsTypes)); + $productsTypes = array_merge_recursive($productsTypes, $parentProductsTypes); + if ($changedIds) { + $this->deleteIndexData($changedIds); + } foreach ($productsTypes as $productType => $entityIds) { $indexer = $this->_getIndexer($productType); - if ($indexer->getIsComposite()) { - $compositeIds += $entityIds; + if ($indexer instanceof DimensionalIndexerInterface) { + foreach ($this->dimensionCollectionFactory->create() as $dimensions) { + $this->tableMaintainer->createMainTmpTable($dimensions); + $temporaryTable = $this->tableMaintainer->getMainTmpTable($dimensions); + $this->_emptyTable($temporaryTable); + $indexer->executeByDimensions($dimensions, \SplFixedArray::fromArray($entityIds, false)); + // copy to index + $this->_insertFromTable( + $temporaryTable, + $this->tableMaintainer->getMainTable($dimensions) + ); + } } else { - $notCompositeIds += $entityIds; + // handle 3d-party indexers for backward compatibility + $this->_emptyTable($this->_defaultIndexerResource->getIdxTable()); + $this->_copyRelationIndexData($entityIds); + $indexer->reindexEntity($entityIds); + $this->_syncData($entityIds); } } - if (!empty($notCompositeIds)) { - $parentProductsTypes = $this->getParentProductsTypes($notCompositeIds); - $productsTypes = array_merge_recursive($productsTypes, $parentProductsTypes); - foreach ($parentProductsTypes as $parentProductsIds) { - $compositeIds = $compositeIds + $parentProductsIds; - $changedIds = array_merge($changedIds, $parentProductsIds); - } - } - - if (!empty($compositeIds)) { - $this->_copyRelationIndexData($compositeIds, $notCompositeIds); - } - $this->_prepareTierPriceIndex($compositeIds + $notCompositeIds); + return $changedIds; + } - foreach ($productsTypes as $productType => $entityIds) { - $indexer = $this->_getIndexer($productType); - $indexer->reindexEntity($entityIds); + /** + * @param array $entityIds + * @return void + */ + private function deleteIndexData(array $entityIds) + { + foreach ($this->dimensionCollectionFactory->create() as $dimensions) { + $select = $this->getConnection()->select()->from( + ['index_price' => $this->tableMaintainer->getMainTable($dimensions)], + null + )->where('index_price.entity_id IN (?)', $entityIds); + $query = $select->deleteFromSelect('index_price'); + $this->getConnection()->query($query); } - $this->_syncData($changedIds); - - return $compositeIds + $notCompositeIds; } /** @@ -362,11 +422,15 @@ protected function _reindexRows($changedIds = []) * @param null|array $parentIds * @param array $excludeIds * @return \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction + * @deprecated Used only for backward compatibility for do not broke custom indexer implementation + * which do not work by dimensions. + * For indexers, which support dimensions all composite products read data directly from main price indexer table + * or replica table for partial or full reindex correspondingly. */ protected function _copyRelationIndexData($parentIds, $excludeIds = null) { $linkField = $this->getProductIdFieldName(); - $select = $this->_connection->select()->from( + $select = $this->getConnection()->select()->from( $this->_defaultIndexerResource->getTable('catalog_product_relation'), ['child_id'] )->join( @@ -381,22 +445,45 @@ protected function _copyRelationIndexData($parentIds, $excludeIds = null) $select->where('child_id NOT IN(?)', $excludeIds); } - $children = $this->_connection->fetchCol($select); + $children = $this->getConnection()->fetchCol($select); if ($children) { - $select = $this->_connection->select()->from( - $this->getIndexTargetTable() - )->where( - 'entity_id IN(?)', - $children - ); - $query = $select->insertFromSelect($this->_defaultIndexerResource->getIdxTable(), [], false); - $this->_connection->query($query); + foreach ($this->dimensionCollectionFactory->create() as $dimensions) { + $select = $this->getConnection()->select()->from( + $this->getIndexTargetTableByDimension($dimensions) + )->where( + 'entity_id IN(?)', + $children + ); + $query = $select->insertFromSelect($this->_defaultIndexerResource->getIdxTable(), [], false); + $this->getConnection()->query($query); + } } return $this; } + /** + * Retrieve index table by dimension that will be used for write operations. + * + * This method is used during both partial and full reindex to identify the table. + * + * @param \Magento\Framework\Search\Request\Dimension[] $dimensions + * + * @return string + */ + private function getIndexTargetTableByDimension(array $dimensions) + { + $indexTargetTable = $this->getIndexTargetTable(); + if ($indexTargetTable === self::getIndexTargetTable()) { + $indexTargetTable = $this->tableMaintainer->getMainTable($dimensions); + } + if ($indexTargetTable === self::getIndexTargetTable() . '_replica') { + $indexTargetTable = $this->tableMaintainer->getMainReplicaTable($dimensions); + } + return $indexTargetTable; + } + /** * Retrieve index table that will be used for write operations. * @@ -415,8 +502,8 @@ protected function getIndexTargetTable() protected function getProductIdFieldName() { $table = $this->_defaultIndexerResource->getTable('catalog_product_entity'); - $indexList = $this->_connection->getIndexList($table); - return $indexList[$this->_connection->getPrimaryKeyName($table)]['COLUMNS_LIST'][0]; + $indexList = $this->getConnection()->getIndexList($table); + return $indexList[$this->getConnection()->getPrimaryKeyName($table)]['COLUMNS_LIST'][0]; } /** @@ -427,14 +514,14 @@ protected function getProductIdFieldName() */ private function getProductsTypes(array $changedIds = []) { - $select = $this->_connection->select()->from( + $select = $this->getConnection()->select()->from( $this->_defaultIndexerResource->getTable('catalog_product_entity'), ['entity_id', 'type_id'] ); if ($changedIds) { $select->where('entity_id IN (?)', $changedIds); } - $pairs = $this->_connection->fetchPairs($select); + $pairs = $this->getConnection()->fetchPairs($select); $byType = []; foreach ($pairs as $productId => $productType) { @@ -445,14 +532,15 @@ private function getProductsTypes(array $changedIds = []) } /** - * Get parent products types. + * Get parent products types + * Used for add composite products to reindex if we have only simple products in changed ids set * * @param array $productsIds * @return array */ private function getParentProductsTypes(array $productsIds) { - $select = $this->_connection->select()->from( + $select = $this->getConnection()->select()->from( ['l' => $this->_defaultIndexerResource->getTable('catalog_product_relation')], '' )->join( @@ -463,7 +551,7 @@ private function getParentProductsTypes(array $productsIds) 'l.child_id IN(?)', $productsIds ); - $pairs = $this->_connection->fetchPairs($select); + $pairs = $this->getConnection()->fetchPairs($select); $byType = []; foreach ($pairs as $productId => $productType) { @@ -472,4 +560,14 @@ private function getParentProductsTypes(array $productsIds) return $byType; } + + /** + * Get connection + * + * @return \Magento\Framework\DB\Adapter\AdapterInterface + */ + private function getConnection() + { + return $this->_defaultIndexerResource->getConnection(); + } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php index ba04af8ec1f41..1a75751570658 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php @@ -6,9 +6,17 @@ namespace Magento\Catalog\Model\Indexer\Product\Price\Action; use Magento\Framework\App\ObjectManager; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceInterface; +use Magento\Framework\EntityManager\EntityMetadataInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Indexer\DimensionalIndexerInterface; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; /** * Class Full reindex action + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Full extends \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction @@ -33,6 +41,26 @@ class Full extends \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction */ private $activeTableSwitcher; + /** + * @var EntityMetadataInterface + */ + private $productMetaDataCached; + + /** + * @var \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory + */ + private $dimensionCollectionFactory; + + /** + * @var \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer + */ + private $dimensionTableMaintainer; + + /** + * @var \Magento\Indexer\Model\ProcessManager + */ + private $processManager; + /** * @param \Magento\Framework\App\Config\ScopeConfigInterface $config * @param \Magento\Store\Model\StoreManagerInterface $storeManager @@ -46,7 +74,9 @@ class Full extends \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator|null $batchSizeCalculator * @param \Magento\Framework\Indexer\BatchProviderInterface|null $batchProvider * @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher|null $activeTableSwitcher - * + * @param \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory|null $dimensionCollectionFactory + * @param \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer|null $dimensionTableMaintainer + * @param \Magento\Indexer\Model\ProcessManager $processManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -61,7 +91,10 @@ public function __construct( \Magento\Framework\EntityManager\MetadataPool $metadataPool = null, \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator $batchSizeCalculator = null, \Magento\Framework\Indexer\BatchProviderInterface $batchProvider = null, - \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher = null + \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher = null, + \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory $dimensionCollectionFactory = null, + \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer $dimensionTableMaintainer = null, + \Magento\Indexer\Model\ProcessManager $processManager = null ) { parent::__construct( $config, @@ -85,6 +118,15 @@ public function __construct( $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance()->get( \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class ); + $this->dimensionCollectionFactory = $dimensionCollectionFactory ?: ObjectManager::getInstance()->get( + \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory::class + ); + $this->dimensionTableMaintainer = $dimensionTableMaintainer ?: ObjectManager::getInstance()->get( + \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer::class + ); + $this->processManager = $processManager ?: ObjectManager::getInstance()->get( + \Magento\Indexer\Model\ProcessManager::class + ); } /** @@ -92,75 +134,335 @@ public function __construct( * * @param array|int|null $ids * @return void - * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Exception * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function execute($ids = null) { try { - $this->_defaultIndexerResource->getTableStrategy()->setUseIdxTable(false); - $this->_prepareWebsiteDateTable(); + //Prepare indexer tables before full reindex + $this->prepareTables(); + + /** @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice $indexer */ + foreach ($this->getTypeIndexers(true) as $typeId => $priceIndexer) { + if ($priceIndexer instanceof DimensionalIndexerInterface) { + //New price reindex mechanism + $this->reindexProductTypeWithDimensions($priceIndexer, $typeId); + continue; + } + + $priceIndexer->getTableStrategy()->setUseIdxTable(false); + + //Old price reindex mechanism + $this->reindexProductType($priceIndexer, $typeId); + } + + //Final replacement of tables from replica to main + $this->switchTables(); + } catch (\Exception $e) { + throw new LocalizedException(__($e->getMessage()), $e); + } + } + + /** + * Prepare indexer tables before full reindex + * + * @return void + * @throws \Exception + */ + private function prepareTables() + { + $this->_defaultIndexerResource->getTableStrategy()->setUseIdxTable(false); + + $this->_prepareWebsiteDateTable(); + + $this->truncateReplicaTables(); + } + + /** + * Truncate replica tables by dimensions + * + * @return void + * @throws \Exception + */ + private function truncateReplicaTables() + { + foreach ($this->dimensionCollectionFactory->create() as $dimension) { + $dimensionTable = $this->dimensionTableMaintainer->getMainReplicaTable($dimension); + $this->_defaultIndexerResource->getConnection()->truncateTable($dimensionTable); + } + } + + /** + * Reindex new 'Dimensional' price indexer by product type + * + * @param DimensionalIndexerInterface $priceIndexer + * @param string $typeId + * + * @return void + * @throws \Exception + */ + private function reindexProductTypeWithDimensions(DimensionalIndexerInterface $priceIndexer, string $typeId) + { + $userFunctions = []; + foreach ($this->dimensionCollectionFactory->create() as $dimensions) { + $userFunctions[] = function () use ($priceIndexer, $dimensions, $typeId) { + return $this->reindexByBatches($priceIndexer, $dimensions, $typeId); + }; + } + $this->processManager->execute($userFunctions); + } + + /** + * Reindex new 'Dimensional' price indexer by batches + * + * @param DimensionalIndexerInterface $priceIndexer + * @param array $dimensions + * @param string $typeId + * + * @return void + * @throws \Exception + */ + private function reindexByBatches(DimensionalIndexerInterface $priceIndexer, array $dimensions, string $typeId) + { + foreach ($this->getBatchesForIndexer($typeId) as $batch) { + $this->reindexByBatchWithDimensions($priceIndexer, $batch, $dimensions, $typeId); + } + } + + /** + * Get batches for new 'Dimensional' price indexer + * + * @param string $typeId + * + * @return \Generator + * @throws \Exception + */ + private function getBatchesForIndexer(string $typeId) + { + $connection = $this->_defaultIndexerResource->getConnection(); + return $this->batchProvider->getBatches( + $connection, + $this->getProductMetaData()->getEntityTable(), + $this->getProductMetaData()->getIdentifierField(), + $this->batchSizeCalculator->estimateBatchSize( + $connection, + $typeId + ) + ); + } + + /** + * Reindex by batch for new 'Dimensional' price indexer + * + * @param DimensionalIndexerInterface $priceIndexer + * @param array $batch + * @param array $dimensions + * @param string $typeId + * + * @return void + * @throws \Exception + */ + private function reindexByBatchWithDimensions( + DimensionalIndexerInterface $priceIndexer, + array $batch, + array $dimensions, + string $typeId + ) { + $entityIds = $this->getEntityIdsFromBatch($typeId, $batch); + + if (!empty($entityIds)) { + $this->dimensionTableMaintainer->createMainTmpTable($dimensions); + $temporaryTable = $this->dimensionTableMaintainer->getMainTmpTable($dimensions); + $this->_emptyTable($temporaryTable); + + $priceIndexer->executeByDimensions($dimensions, \SplFixedArray::fromArray($entityIds, false)); - $entityMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); - $replicaTable = $this->activeTableSwitcher->getAdditionalTableName( - $this->_defaultIndexerResource->getMainTable() + // Sync data from temp table to index table + $this->_insertFromTable( + $temporaryTable, + $this->dimensionTableMaintainer->getMainReplicaTable($dimensions) ); + } + } - // Prepare replica table for indexation. - $this->_defaultIndexerResource->getConnection()->truncateTable($replicaTable); + /** + * Reindex old price indexer by product type + * + * @param PriceInterface $priceIndexer + * @param string $typeId + * + * @return void + * @throws \Exception + */ + private function reindexProductType(PriceInterface $priceIndexer, string $typeId) + { + foreach ($this->getBatchesForIndexer($typeId) as $batch) { + $this->reindexBatch($priceIndexer, $batch, $typeId); + } + } - /** @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice $indexer */ - foreach ($this->getTypeIndexers() as $indexer) { - $indexer->getTableStrategy()->setUseIdxTable(false); - $connection = $indexer->getConnection(); - - $batches = $this->batchProvider->getBatches( - $connection, - $entityMetadata->getEntityTable(), - $entityMetadata->getIdentifierField(), - $this->batchSizeCalculator->estimateBatchSize($connection, $indexer->getTypeId()) - ); - - foreach ($batches as $batch) { - // Get entity ids from batch - $select = $connection->select(); - $select->distinct(true); - $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); - $select->where('type_id = ?', $indexer->getTypeId()); - - $entityIds = $this->batchProvider->getBatchIds($connection, $select, $batch); - - if (!empty($entityIds)) { - // Temporary table will created if not exists - $idxTableName = $this->_defaultIndexerResource->getIdxTable(); - $this->_emptyTable($idxTableName); - - if ($indexer->getIsComposite()) { - $this->_copyRelationIndexData($entityIds); - } - $this->_prepareTierPriceIndex($entityIds); - - // Reindex entities by id - $indexer->reindexEntity($entityIds); - - // Sync data from temp table to index table - $this->_insertFromTable($idxTableName, $replicaTable); - - // Drop temporary index table - $connection->dropTable($idxTableName); - } - } + /** + * Reindex by batch for old price indexer + * + * @param PriceInterface $priceIndexer + * @param array $batch + * @param string $typeId + * + * @return void + * @throws \Exception + */ + private function reindexBatch(PriceInterface $priceIndexer, array $batch, string $typeId) + { + $entityIds = $this->getEntityIdsFromBatch($typeId, $batch); + + if (!empty($entityIds)) { + // Temporary table will created if not exists + $idxTableName = $this->_defaultIndexerResource->getIdxTable(); + $this->_emptyTable($idxTableName); + + if ($priceIndexer->getIsComposite()) { + $this->_copyRelationIndexData($entityIds); } + + // Reindex entities by id + $priceIndexer->reindexEntity($entityIds); + + // Sync data from temp table to index table + $this->_insertFromTable($idxTableName, $this->getReplicaTable()); + + // Drop temporary index table + $this->_defaultIndexerResource->getConnection()->dropTable($idxTableName); + } + } + + /** + * Get Entity Ids from batch + * + * @param string $typeId + * @param array $batch + * + * @return array + * @throws \Exception + */ + private function getEntityIdsFromBatch(string $typeId, array $batch) + { + $connection = $this->_defaultIndexerResource->getConnection(); + + // Get entity ids from batch + $select = $connection + ->select() + ->distinct(true) + ->from( + ['e' => $this->getProductMetaData()->getEntityTable()], + $this->getProductMetaData()->getIdentifierField() + ) + ->where('type_id = ?', $typeId); + + return $this->batchProvider->getBatchIds($connection, $select, $batch); + } + + /** + * Get product meta data + * + * @return EntityMetadataInterface + * @throws \Exception + */ + private function getProductMetaData() + { + if ($this->productMetaDataCached === null) { + $this->productMetaDataCached = $this->metadataPool->getMetadata(ProductInterface::class); + } + + return $this->productMetaDataCached; + } + + /** + * Get replica table + * + * @return string + * @throws \Exception + */ + private function getReplicaTable() + { + return $this->activeTableSwitcher->getAdditionalTableName( + $this->_defaultIndexerResource->getMainTable() + ); + } + + /** + * Replacement of tables from replica to main + * + * @return void + */ + private function switchTables() + { + // Switch dimension tables + $mainTablesByDimension = []; + + foreach ($this->dimensionCollectionFactory->create() as $dimensions) { + $mainTablesByDimension[] = $this->dimensionTableMaintainer->getMainTable($dimensions); + + //Move data from indexers with old realisation + $this->moveDataFromReplicaTableToReplicaTables($dimensions); + } + + if (count($mainTablesByDimension) > 0) { $this->activeTableSwitcher->switchTable( $this->_defaultIndexerResource->getConnection(), - [$this->_defaultIndexerResource->getMainTable()] + $mainTablesByDimension ); - } catch (\Exception $e) { - throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e); } } /** + * Move data from old price indexer mechanism to new indexer mechanism by dimensions. + * Used only for backward compatibility + * + * @param array $dimensions + * + * @return void + */ + private function moveDataFromReplicaTableToReplicaTables(array $dimensions) + { + if (!$dimensions) { + return; + } + $select = $this->dimensionTableMaintainer->getConnection()->select()->from( + $this->dimensionTableMaintainer->getMainReplicaTable([]) + ); + + $check = clone $select; + $check->reset('columns')->columns('count(*)'); + + if (!$this->dimensionTableMaintainer->getConnection()->query($check)->fetchColumn()) { + return; + } + + $replicaTablesByDimension = $this->dimensionTableMaintainer->getMainReplicaTable($dimensions); + + foreach ($dimensions as $dimension) { + if ($dimension->getName() === WebsiteDimensionProvider::DIMENSION_NAME) { + $select->where('website_id = ?', $dimension->getValue()); + } + if ($dimension->getName() === CustomerGroupDimensionProvider::DIMENSION_NAME) { + $select->where('customer_group_id = ?', $dimension->getValue()); + } + } + + $this->dimensionTableMaintainer->getConnection()->query( + $this->dimensionTableMaintainer->getConnection()->insertFromSelect( + $select, + $replicaTablesByDimension, + [], + \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE + ) + ); + } + + /** + * @deprecated + * * @inheritdoc */ protected function getIndexTargetTable() diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionCollectionFactory.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionCollectionFactory.php new file mode 100644 index 0000000000000..62a129f0ff448 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionCollectionFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Model\Indexer\Product\Price; + +use Magento\Framework\Indexer\DimensionProviderInterface; +use Magento\Framework\Indexer\MultiDimensionProvider; + +class DimensionCollectionFactory +{ + /** + * @var \Magento\Framework\Indexer\MultiDimensionProviderFactory + */ + private $multiDimensionProviderFactory; + + /** + * @var DimensionProviderInterface[] + */ + private $dimensionProviders; + + /** + * @var DimensionModeConfiguration + */ + private $dimensionModeConfiguration; + + /** + * @param \Magento\Framework\Indexer\MultiDimensionProviderFactory $multiDimensionProviderFactory + * @param DimensionModeConfiguration $dimensionModeConfiguration + * @param array $dimensionProviders + */ + public function __construct( + \Magento\Framework\Indexer\MultiDimensionProviderFactory $multiDimensionProviderFactory, + DimensionModeConfiguration $dimensionModeConfiguration, + array $dimensionProviders + ) { + $this->multiDimensionProviderFactory = $multiDimensionProviderFactory; + $this->dimensionProviders = $dimensionProviders; + $this->dimensionModeConfiguration = $dimensionModeConfiguration; + } + + /** + * Create MultiDimensionProvider for specified "dimension mode". + * By default return multiplication of dimensions by current set mode + * + * @param string|null $dimensionsMode + * @return MultiDimensionProvider + */ + public function create(string $dimensionsMode = null): MultiDimensionProvider + { + $dimensionConfiguration = $this->dimensionModeConfiguration->getDimensionConfiguration($dimensionsMode); + + $providers = []; + foreach ($dimensionConfiguration as $dimensionName) { + if (!isset($this->dimensionProviders[$dimensionName])) { + throw new \LogicException( + 'Dimension Provider is missing. Cannot handle unknown dimension: ' . $dimensionName + ); + } + $providers[] = clone $this->dimensionProviders[$dimensionName]; + } + + return $this->multiDimensionProviderFactory->create( + [ + 'dimensionProviders' => $providers + ] + ); + } +} diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionModeConfiguration.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionModeConfiguration.php new file mode 100644 index 0000000000000..e5935dac92a36 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionModeConfiguration.php @@ -0,0 +1,90 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); +namespace Magento\Catalog\Model\Indexer\Product\Price; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; + +class DimensionModeConfiguration +{ + /**#@+ + * Available modes of dimensions for product price indexer + */ + const DIMENSION_NONE = 'none'; + const DIMENSION_WEBSITE = 'website'; + const DIMENSION_CUSTOMER_GROUP = 'customer_group'; + const DIMENSION_WEBSITE_AND_CUSTOMER_GROUP = 'website_and_customer_group'; + /**#@-*/ + + /** + * Mapping between dimension mode and dimension provider name + * + * @var array + */ + private $modesMapping = [ + self::DIMENSION_NONE => [ + ], + self::DIMENSION_WEBSITE => [ + WebsiteDimensionProvider::DIMENSION_NAME + ], + self::DIMENSION_CUSTOMER_GROUP => [ + CustomerGroupDimensionProvider::DIMENSION_NAME + ], + self::DIMENSION_WEBSITE_AND_CUSTOMER_GROUP => [ + WebsiteDimensionProvider::DIMENSION_NAME, + CustomerGroupDimensionProvider::DIMENSION_NAME + ], + ]; + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var string + */ + private $currentMode; + + /** + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct(ScopeConfigInterface $scopeConfig) + { + $this->scopeConfig = $scopeConfig; + } + + /** + * Get names of dimensions which used for provided mode. + * By default return dimensions for current enabled mode + * + * @param string|null $mode + * @return string[] + */ + public function getDimensionConfiguration(string $mode = null): array + { + if ($mode && !isset($this->modesMapping[$mode])) { + throw new \InvalidArgumentException( + sprintf('Undefined dimension mode "%s".', $mode) + ); + } + return $this->modesMapping[$mode ?? $this->getCurrentMode()]; + } + + /** + * @return string + */ + private function getCurrentMode(): string + { + if (null === $this->currentMode) { + $this->currentMode = $this->scopeConfig->getValue(ModeSwitcher::XML_PATH_PRICE_DIMENSIONS_MODE) + ?: self::DIMENSION_NONE; + } + + return $this->currentMode; + } +} diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php new file mode 100644 index 0000000000000..b5f8425a855dc --- /dev/null +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php @@ -0,0 +1,166 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); +namespace Magento\Catalog\Model\Indexer\Product\Price; + +use Magento\Framework\Search\Request\Dimension; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; + +/** + * Class to prepare new tables for new indexer mode + */ +class ModeSwitcher +{ + const XML_PATH_PRICE_DIMENSIONS_MODE = 'indexer/catalog_product_price/dimensions_mode'; + + /** + * TableMaintainer + * + * @var \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer + */ + private $tableMaintainer; + + /** + * DimensionCollectionFactory + * + * @var \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory + */ + private $dimensionCollectionFactory; + + /** + * @var array|null + */ + private $dimensionsArray; + + /** + * @param \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer $tableMaintainer + * @param \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory $dimensionCollectionFactory + */ + public function __construct( + \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer $tableMaintainer, + \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory $dimensionCollectionFactory + ) { + $this->tableMaintainer = $tableMaintainer; + $this->dimensionCollectionFactory = $dimensionCollectionFactory; + } + + /** + * Create new tables + * + * @param string $currentMode + * + * @return void + * @throws \Zend_Db_Exception + */ + public function createTables(string $currentMode) + { + foreach ($this->getDimensionsArray($currentMode) as $dimensions) { + if (!empty($dimensions)) { + $this->tableMaintainer->createTablesForDimensions($dimensions); + } + } + } + + /** + * Move data from old tables to new + * + * @param string $currentMode + * @param string $previousMode + * + * @return void + */ + public function moveData(string $currentMode, string $previousMode) + { + $dimensionsArrayForCurrentMode = $this->getDimensionsArray($currentMode); + $dimensionsArrayForPreviousMode = $this->getDimensionsArray($previousMode); + + foreach ($dimensionsArrayForCurrentMode as $dimensionsForCurrentMode) { + $newTable = $this->tableMaintainer->getMainTable($dimensionsForCurrentMode); + if (empty($dimensionsForCurrentMode)) { + // new mode is 'none' + foreach ($dimensionsArrayForPreviousMode as $dimensionsForPreviousMode) { + $oldTable = $this->tableMaintainer->getMainTable($dimensionsForPreviousMode); + $this->insertFromOldTablesToNew($newTable, $oldTable); + } + } else { + // new mode is not 'none' + foreach ($dimensionsArrayForPreviousMode as $dimensionsForPreviousMode) { + $oldTable = $this->tableMaintainer->getMainTable($dimensionsForPreviousMode); + $this->insertFromOldTablesToNew($newTable, $oldTable, $dimensionsForCurrentMode); + } + } + } + } + + /** + * Drop old tables + * + * @param string $previousMode + * + * @return void + */ + public function dropTables(string $previousMode) + { + foreach ($this->getDimensionsArray($previousMode) as $dimensions) { + if (empty($dimensions)) { + $this->tableMaintainer->truncateTablesForDimensions($dimensions); + } else { + $this->tableMaintainer->dropTablesForDimensions($dimensions); + } + } + } + + /** + * Get dimensions array + * + * @param string $mode + * + * @return array + */ + private function getDimensionsArray(string $mode): \Magento\Framework\Indexer\MultiDimensionProvider + { + if (isset($this->dimensionsArray[$mode])) { + return $this->dimensionsArray[$mode]; + } + + $this->dimensionsArray[$mode] = $this->dimensionCollectionFactory->create($mode); + + return $this->dimensionsArray[$mode]; + } + + /** + * Insert from old tables data to new + * + * @param string $newTable + * @param string $oldTable + * @param Dimension[] $dimensions + * + * @return void + */ + private function insertFromOldTablesToNew(string $newTable, string $oldTable, array $dimensions = []) + { + $select = $this->tableMaintainer->getConnection()->select()->from($oldTable); + + foreach ($dimensions as $dimension) { + if ($dimension->getName() === WebsiteDimensionProvider::DIMENSION_NAME) { + $select->where('website_id = ?', $dimension->getValue()); + } + if ($dimension->getName() === CustomerGroupDimensionProvider::DIMENSION_NAME) { + $select->where('customer_group_id = ?', $dimension->getValue()); + } + } + $this->tableMaintainer->getConnection()->query( + $this->tableMaintainer->getConnection()->insertFromSelect( + $select, + $newTable, + [], + \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE + ) + ); + } +} diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/CustomerGroup.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/CustomerGroup.php index 32b2db8a7008c..9b99ee8c8dc8c 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/CustomerGroup.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/CustomerGroup.php @@ -5,9 +5,15 @@ */ namespace Magento\Catalog\Model\Indexer\Product\Price\Plugin; +use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; use Magento\Customer\Api\GroupRepositoryInterface; use Magento\Customer\Api\Data\GroupInterface; -use \Magento\Catalog\Model\Indexer\Product\Price\UpdateIndexInterface; +use Magento\Catalog\Model\Indexer\Product\Price\UpdateIndexInterface; +use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; +use Magento\Framework\Indexer\Dimension; +use Magento\Framework\Indexer\DimensionFactory; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; class CustomerGroup { @@ -17,14 +23,46 @@ class CustomerGroup private $updateIndex; /** - * Constructor + * @var TableMaintainer + */ + private $tableMaintainer; + + /** + * DimensionFactory * + * @var DimensionFactory + */ + private $dimensionFactory; + + /** + * @var DimensionModeConfiguration + */ + private $dimensionModeConfiguration; + + /** + * @var WebsiteDimensionProvider + */ + private $websiteDimensionProvider; + + /** * @param UpdateIndexInterface $updateIndex + * @param TableMaintainer $tableMaintainer + * @param DimensionFactory $dimensionFactory + * @param DimensionModeConfiguration $dimensionModeConfiguration + * @param WebsiteDimensionProvider $websiteDimensionProvider */ public function __construct( - UpdateIndexInterface $updateIndex + UpdateIndexInterface $updateIndex, + TableMaintainer $tableMaintainer, + DimensionFactory $dimensionFactory, + DimensionModeConfiguration $dimensionModeConfiguration, + WebsiteDimensionProvider $websiteDimensionProvider ) { $this->updateIndex = $updateIndex; + $this->tableMaintainer = $tableMaintainer; + $this->dimensionFactory = $dimensionFactory; + $this->dimensionModeConfiguration = $dimensionModeConfiguration; + $this->websiteDimensionProvider = $websiteDimensionProvider; } /** @@ -32,7 +70,8 @@ public function __construct( * * @param GroupRepositoryInterface $subject * @param \Closure $proceed - * @param GroupInterface $result + * @param GroupInterface $group + * * @return GroupInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -43,7 +82,64 @@ public function aroundSave( ) { $isGroupNew = !$group->getId(); $group = $proceed($group); + if ($isGroupNew) { + foreach ($this->getAffectedDimensions((string)$group->getId()) as $dimensions) { + $this->tableMaintainer->createTablesForDimensions($dimensions); + } + } $this->updateIndex->update($group, $isGroupNew); return $group; } + + /** + * Update price index after customer group deleted + * + * @param GroupRepositoryInterface $subject + * @param bool $result + * @param string $groupId + * + * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterDeleteById(GroupRepositoryInterface $subject, bool $result, string $groupId) + { + foreach ($this->getAffectedDimensions($groupId) as $dimensions) { + $this->tableMaintainer->dropTablesForDimensions($dimensions); + } + + return $result; + } + + /** + * Get affected dimensions + * + * @param string $groupId + * @return Dimension[][] + */ + private function getAffectedDimensions(string $groupId): array + { + $currentDimensions = $this->dimensionModeConfiguration->getDimensionConfiguration(); + // do not return dimensions if Customer Group dimension is not present in configuration + if (!in_array(CustomerGroupDimensionProvider::DIMENSION_NAME, $currentDimensions, true)) { + return []; + } + $customerGroupDimension = $this->dimensionFactory->create( + CustomerGroupDimensionProvider::DIMENSION_NAME, + $groupId + ); + + $dimensions = []; + if (in_array(WebsiteDimensionProvider::DIMENSION_NAME, $currentDimensions, true)) { + foreach ($this->websiteDimensionProvider as $websiteDimension) { + $dimensions[] = [ + $customerGroupDimension, + $websiteDimension + ]; + } + } else { + $dimensions[] = [$customerGroupDimension]; + } + + return $dimensions; + } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/TableResolver.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/TableResolver.php new file mode 100644 index 0000000000000..fbeec22783090 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/TableResolver.php @@ -0,0 +1,140 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Indexer\Product\Price\Plugin; + +use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Indexer\DimensionFactory; +use Magento\Framework\Search\Request\IndexScopeResolverInterface; +use Magento\Framework\App\Http\Context; +use Magento\Framework\Indexer\Dimension; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Customer\Model\Context as CustomerContext; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; + +/** + * Replace catalog_product_index_price table name on the table name segmented per dimension. + * Used only for backward compatibility + */ +class TableResolver +{ + /** + * @var IndexScopeResolverInterface + */ + private $priceTableResolver; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var Context + */ + private $httpContext; + + /** + * @var DimensionFactory + */ + private $dimensionFactory; + + /** + * @var DimensionModeConfiguration + */ + private $dimensionModeConfiguration; + + /** + * @param IndexScopeResolverInterface $priceTableResolver + * @param StoreManagerInterface $storeManager + * @param Context $context + * @param DimensionFactory $dimensionFactory + * @param DimensionModeConfiguration $dimensionModeConfiguration + */ + public function __construct( + IndexScopeResolverInterface $priceTableResolver, + StoreManagerInterface $storeManager, + Context $context, + DimensionFactory $dimensionFactory, + DimensionModeConfiguration $dimensionModeConfiguration + ) { + $this->priceTableResolver = $priceTableResolver; + $this->storeManager = $storeManager; + $this->httpContext = $context; + $this->dimensionFactory = $dimensionFactory; + $this->dimensionModeConfiguration = $dimensionModeConfiguration; + } + + /** + * Replacing catalog_product_index_price table name on the table name segmented per dimension. + * + * @param ResourceConnection $subject + * @param string $result + * @param string|string[] $tableName + * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGetTableName( + ResourceConnection $subject, + string $result, + $tableName + ) { + if (!is_array($tableName) + && $tableName === 'catalog_product_index_price' + && $this->dimensionModeConfiguration->getDimensionConfiguration() + ) { + return $this->priceTableResolver->resolve('catalog_product_index_price', $this->getDimensions()); + } + + return $result; + } + + /** + * @return Dimension[] + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getDimensions(): array + { + $dimensions = []; + foreach ($this->dimensionModeConfiguration->getDimensionConfiguration() as $dimensionName) { + if ($dimensionName === WebsiteDimensionProvider::DIMENSION_NAME) { + $dimensions[] = $this->createDimensionFromWebsite(); + } + if ($dimensionName === CustomerGroupDimensionProvider::DIMENSION_NAME) { + $dimensions[] = $this->createDimensionFromCustomerGroup(); + } + } + + return $dimensions; + } + + /** + * @return Dimension + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function createDimensionFromWebsite(): Dimension + { + $storeKey = $this->httpContext->getValue(StoreManagerInterface::CONTEXT_STORE); + return $this->dimensionFactory->create( + WebsiteDimensionProvider::DIMENSION_NAME, + (string)$this->storeManager->getStore($storeKey)->getWebsiteId() + ); + } + + /** + * @return Dimension + */ + private function createDimensionFromCustomerGroup(): Dimension + { + return $this->dimensionFactory->create( + CustomerGroupDimensionProvider::DIMENSION_NAME, + (string)$this->httpContext->getValue(CustomerContext::CONTEXT_GROUP) + ); + } +} diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/Website.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/Website.php index 269515e292e17..4831680f07c33 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/Website.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/Website.php @@ -5,33 +5,128 @@ */ namespace Magento\Catalog\Model\Indexer\Product\Price\Plugin; +use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; +use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; +use Magento\Framework\Indexer\Dimension; +use Magento\Framework\Indexer\DimensionFactory; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\AbstractModel; + class Website { /** - * @var \Magento\Catalog\Model\Indexer\Product\Price\Processor + * @var TableMaintainer + */ + private $tableMaintainer; + + /** + * DimensionFactory + * + * @var DimensionFactory + */ + private $dimensionFactory; + + /** + * @var DimensionModeConfiguration */ - protected $_processor; + private $dimensionModeConfiguration; /** - * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $processor + * @var CustomerGroupDimensionProvider */ - public function __construct(\Magento\Catalog\Model\Indexer\Product\Price\Processor $processor) + private $customerGroupDimensionProvider; + + /** + * @param TableMaintainer $tableMaintainer + * @param DimensionFactory $dimensionFactory + * @param DimensionModeConfiguration $dimensionModeConfiguration + * @param CustomerGroupDimensionProvider $customerGroupDimensionProvider + */ + public function __construct( + TableMaintainer $tableMaintainer, + DimensionFactory $dimensionFactory, + DimensionModeConfiguration $dimensionModeConfiguration, + CustomerGroupDimensionProvider $customerGroupDimensionProvider + ) { + $this->tableMaintainer = $tableMaintainer; + $this->dimensionFactory = $dimensionFactory; + $this->dimensionModeConfiguration = $dimensionModeConfiguration; + $this->customerGroupDimensionProvider = $customerGroupDimensionProvider; + } + + /** + * Update price index after website deleted + * + * @param AbstractDb $subject + * @param AbstractDb $objectResource + * @param AbstractModel $website + * + * @return AbstractDb + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterDelete(AbstractDb $subject, AbstractDb $objectResource, AbstractModel $website) { - $this->_processor = $processor; + foreach ($this->getAffectedDimensions($website->getId()) as $dimensions) { + $this->tableMaintainer->dropTablesForDimensions($dimensions); + } + + return $objectResource; } /** - * Invalidate price indexer + * Update price index after website created * - * @param \Magento\Store\Model\ResourceModel\Website $subject - * @param \Magento\Store\Model\ResourceModel\Website $result - * @return \Magento\Store\Model\ResourceModel\Website + * @param AbstractDb $subject + * @param AbstractDb $objectResource + * @param AbstractModel $website * + * @return AbstractDb * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterDelete(\Magento\Store\Model\ResourceModel\Website $subject, $result) + public function afterSave(AbstractDb $subject, AbstractDb $objectResource, AbstractModel $website) + { + if ($website->isObjectNew()) { + foreach ($this->getAffectedDimensions($website->getId()) as $dimensions) { + $this->tableMaintainer->createTablesForDimensions($dimensions); + } + } + + return $objectResource; + } + + /** + * Get affected dimensions + * + * @param string $websiteId + * + * @return Dimension[][] + */ + private function getAffectedDimensions(string $websiteId): array { - $this->_processor->markIndexerAsInvalid(); - return $result; + $currentDimensions = $this->dimensionModeConfiguration->getDimensionConfiguration(); + // do not return dimensions if Website dimension is not present in configuration + if (!in_array(WebsiteDimensionProvider::DIMENSION_NAME, $currentDimensions, true)) { + return []; + } + $websiteDimension = $this->dimensionFactory->create( + WebsiteDimensionProvider::DIMENSION_NAME, + $websiteId + ); + + $dimensions = []; + if (in_array(CustomerGroupDimensionProvider::DIMENSION_NAME, $currentDimensions, true)) { + foreach ($this->customerGroupDimensionProvider as $customerGroupDimension) { + $dimensions[] = [ + $customerGroupDimension, + $websiteDimension + ]; + } + } else { + $dimensions[] = [$websiteDimension]; + } + + return $dimensions; } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/PriceTableResolver.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/PriceTableResolver.php new file mode 100644 index 0000000000000..0e4850d1f9541 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/PriceTableResolver.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\Indexer\Product\Price; + +use Magento\Framework\Indexer\Dimension; +use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver; +use Magento\Framework\Search\Request\IndexScopeResolverInterface; + +/** + * Class return price table name based on dimension + * use only on the frontend area + */ +class PriceTableResolver implements IndexScopeResolverInterface +{ + /** + * @var IndexScopeResolver + */ + private $indexScopeResolver; + + /** + * @var DimensionModeConfiguration + */ + private $dimensionModeConfiguration; + + /** + * @param IndexScopeResolver $indexScopeResolver + * @param DimensionModeConfiguration $dimensionModeConfiguration + */ + public function __construct( + IndexScopeResolver $indexScopeResolver, + DimensionModeConfiguration $dimensionModeConfiguration + ) { + $this->indexScopeResolver = $indexScopeResolver; + $this->dimensionModeConfiguration = $dimensionModeConfiguration; + } + + /** + * Return price table name based on dimension + * @param string $index + * @param array $dimensions + * @return string + */ + public function resolve($index, array $dimensions) + { + if ($index === 'catalog_product_index_price') { + $dimensions = $this->filterDimensions($dimensions); + } + return $this->indexScopeResolver->resolve($index, $dimensions); + } + + /** + * @param Dimension[] $dimensions + * @return array + * @throws \Exception + */ + private function filterDimensions($dimensions): array + { + $existDimensions = []; + $currentDimensions = $this->dimensionModeConfiguration->getDimensionConfiguration(); + foreach ($dimensions as $dimension) { + if ((string)$dimension->getValue() === '') { + throw new \InvalidArgumentException( + sprintf('Dimension value of "%s" can not be empty', $dimension->getName()) + ); + } + if (in_array($dimension->getName(), $currentDimensions, true)) { + $existDimensions[] = $dimension; + } + } + + return $existDimensions; + } +} diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/TableMaintainer.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/TableMaintainer.php new file mode 100644 index 0000000000000..999eaaa2a8025 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/TableMaintainer.php @@ -0,0 +1,280 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); +namespace Magento\Catalog\Model\Indexer\Product\Price; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Search\Request\Dimension; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Search\Request\IndexScopeResolverInterface as TableResolver; + +/** + * Class encapsulate logic of work with tables per store in Product Price indexer + */ +class TableMaintainer +{ + /** + * Catalog product price index table name + */ + const MAIN_INDEX_TABLE = 'catalog_product_index_price'; + + /** + * @var ResourceConnection + */ + private $resource; + + /** + * @var TableResolver + */ + private $tableResolver; + + /** + * @var AdapterInterface + */ + private $connection; + + /** + * Catalog tmp category index table name + */ + private $tmpTableSuffix = '_temp'; + + /** + * Catalog tmp category index table name + */ + private $additionalTableSuffix = '_replica'; + + /** + * @var string[] + */ + private $mainTmpTable; + + /** + * @var null|string + */ + private $connectionName; + + /** + * @param ResourceConnection $resource + * @param TableResolver $tableResolver + * @param null $connectionName + */ + public function __construct( + ResourceConnection $resource, + TableResolver $tableResolver, + $connectionName = null + ) { + $this->resource = $resource; + $this->tableResolver = $tableResolver; + $this->connectionName = $connectionName; + } + + /** + * Get connection for work with price indexer + * + * @return AdapterInterface + */ + public function getConnection(): AdapterInterface + { + if (null === $this->connection) { + $this->connection = $this->resource->getConnection($this->connectionName); + } + return $this->connection; + } + + /** + * Return validated table name + * + * @param string $table + * @return string + */ + private function getTable(string $table): string + { + return $this->resource->getTableName($table); + } + + /** + * Create table based on main table + * + * @param string $mainTableName + * @param string $newTableName + * + * @return void + * + * @throws \Zend_Db_Exception + */ + private function createTable(string $mainTableName, string $newTableName) + { + if (!$this->getConnection()->isTableExists($newTableName)) { + $this->getConnection()->createTable( + $this->getConnection()->createTableByDdl($mainTableName, $newTableName) + ); + } + } + + /** + * Drop table + * + * @param string $tableName + * + * @return void + */ + private function dropTable(string $tableName) + { + if ($this->getConnection()->isTableExists($tableName)) { + $this->getConnection()->dropTable($tableName); + } + } + + /** + * Truncate table + * + * @param string $tableName + * + * @return void + */ + private function truncateTable(string $tableName) + { + if ($this->getConnection()->isTableExists($tableName)) { + $this->getConnection()->truncateTable($tableName); + } + } + + /** + * Get array key for tmp table + * + * @param Dimension[] $dimensions + * + * @return string + */ + private function getArrayKeyForTmpTable(array $dimensions): string + { + $key = 'temp'; + foreach ($dimensions as $dimension) { + $key .= $dimension->getName() . '_' . $dimension->getValue(); + } + return $key; + } + + /** + * Return main index table name + * + * @param Dimension[] $dimensions + * + * @return string + */ + public function getMainTable(array $dimensions): string + { + return $this->tableResolver->resolve(self::MAIN_INDEX_TABLE, $dimensions); + } + + /** + * Create main and replica index tables for dimensions + * + * @param Dimension[] $dimensions + * + * @return void + * + * @throws \Zend_Db_Exception + */ + public function createTablesForDimensions(array $dimensions) + { + $mainTableName = $this->getMainTable($dimensions); + //Create index table for dimensions based on main replica table + //Using main replica table is necessary for backward capability and TableResolver plugin work + $this->createTable( + $this->getTable(self::MAIN_INDEX_TABLE . $this->additionalTableSuffix), + $mainTableName + ); + + $mainReplicaTableName = $this->getMainTable($dimensions) . $this->additionalTableSuffix; + //Create replica table for dimensions based on main replica table + $this->createTable( + $this->getTable(self::MAIN_INDEX_TABLE . $this->additionalTableSuffix), + $mainReplicaTableName + ); + } + + /** + * Drop main and replica index tables for dimensions + * + * @param Dimension[] $dimensions + * + * @return void + */ + public function dropTablesForDimensions(array $dimensions) + { + $mainTableName = $this->getMainTable($dimensions); + $this->dropTable($mainTableName); + + $mainReplicaTableName = $this->getMainTable($dimensions) . $this->additionalTableSuffix; + $this->dropTable($mainReplicaTableName); + } + + /** + * Truncate main and replica index tables for dimensions + * + * @param Dimension[] $dimensions + * + * @return void + */ + public function truncateTablesForDimensions(array $dimensions) + { + $mainTableName = $this->getMainTable($dimensions); + $this->truncateTable($mainTableName); + + $mainReplicaTableName = $this->getMainTable($dimensions) . $this->additionalTableSuffix; + $this->truncateTable($mainReplicaTableName); + } + + /** + * Return replica index table name + * + * @param Dimension[] $dimensions + * + * @return string + */ + public function getMainReplicaTable(array $dimensions): string + { + return $this->getMainTable($dimensions) . $this->additionalTableSuffix; + } + + /** + * Create temporary index table for dimensions + * + * @param Dimension[] $dimensions + * + * @return void + */ + public function createMainTmpTable(array $dimensions) + { + // Create temporary table based on template table catalog_product_index_price_tmp without indexes + $templateTableName = $this->resource->getTableName(self::MAIN_INDEX_TABLE . '_tmp'); + $temporaryTableName = $this->getMainTable($dimensions) . $this->tmpTableSuffix; + $this->getConnection()->createTemporaryTableLike($temporaryTableName, $templateTableName, true); + $this->mainTmpTable[$this->getArrayKeyForTmpTable($dimensions)] = $temporaryTableName; + } + + /** + * Return temporary index table name + * + * @param Dimension[] $dimensions + * + * @return string + * + * @throws \LogicException + */ + public function getMainTmpTable(array $dimensions): string + { + $cacheKey = $this->getArrayKeyForTmpTable($dimensions); + if (!isset($this->mainTmpTable[$cacheKey])) { + throw new \LogicException( + sprintf('Temporary table for provided dimensions "%s" does not exist', $cacheKey) + ); + } + return $this->mainTmpTable[$cacheKey]; + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Layer/Filter/Price.php b/app/code/Magento/Catalog/Model/ResourceModel/Layer/Filter/Price.php index bed129e19168f..3699e29f8201a 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Layer/Filter/Price.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Layer/Filter/Price.php @@ -5,6 +5,15 @@ */ namespace Magento\Catalog\Model\ResourceModel\Layer\Filter; +use Magento\Framework\App\Http\Context; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Indexer\DimensionFactory; +use Magento\Framework\Search\Request\IndexScopeResolverInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Customer\Model\Context as CustomerContext; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; + /** * Catalog Layer Price Filter resource model * @@ -41,6 +50,21 @@ class Price extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb */ private $storeManager; + /** + * @var IndexScopeResolverInterface|null + */ + private $priceTableResolver; + + /** + * @var Context + */ + private $httpContext; + + /** + * @var DimensionFactory|null + */ + private $dimensionFactory; + /** * @param \Magento\Framework\Model\ResourceModel\Db\Context $context * @param \Magento\Framework\Event\ManagerInterface $eventManager @@ -48,6 +72,9 @@ class Price extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb * @param \Magento\Customer\Model\Session $session * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param null $connectionName + * @param IndexScopeResolverInterface|null $priceTableResolver + * @param Context|null $httpContext + * @param DimensionFactory|null $dimensionFactory */ public function __construct( \Magento\Framework\Model\ResourceModel\Db\Context $context, @@ -55,12 +82,19 @@ public function __construct( \Magento\Catalog\Model\Layer\Resolver $layerResolver, \Magento\Customer\Model\Session $session, \Magento\Store\Model\StoreManagerInterface $storeManager, - $connectionName = null + $connectionName = null, + IndexScopeResolverInterface $priceTableResolver = null, + Context $httpContext = null, + DimensionFactory $dimensionFactory = null ) { $this->layer = $layerResolver->get(); $this->session = $session; $this->storeManager = $storeManager; $this->_eventManager = $eventManager; + $this->priceTableResolver = $priceTableResolver + ?? ObjectManager::getInstance()->get(IndexScopeResolverInterface::class); + $this->httpContext = $httpContext ?? ObjectManager::getInstance()->get(Context::class); + $this->dimensionFactory = $dimensionFactory ?? ObjectManager::getInstance()->get(DimensionFactory::class); parent::__construct($context, $connectionName); } @@ -118,11 +152,8 @@ protected function _getSelect() // remove join with main table $fromPart = $select->getPart(\Magento\Framework\DB\Select::FROM); - if (!isset( - $fromPart[\Magento\Catalog\Model\ResourceModel\Product\Collection::INDEX_TABLE_ALIAS] - ) || !isset( - $fromPart[\Magento\Catalog\Model\ResourceModel\Product\Collection::MAIN_TABLE_ALIAS] - ) + if (!isset($fromPart[\Magento\Catalog\Model\ResourceModel\Product\Collection::INDEX_TABLE_ALIAS]) || + !isset($fromPart[\Magento\Catalog\Model\ResourceModel\Product\Collection::MAIN_TABLE_ALIAS]) ) { return $select; } @@ -376,6 +407,30 @@ protected function _construct() $this->_init('catalog_product_index_price', 'entity_id'); } + /** + * {@inheritdoc} + * @return string + */ + public function getMainTable() + { + $storeKey = $this->httpContext->getValue(StoreManagerInterface::CONTEXT_STORE); + $priceTableName = $this->priceTableResolver->resolve( + 'catalog_product_index_price', + [ + $this->dimensionFactory->create( + WebsiteDimensionProvider::DIMENSION_NAME, + (string)$this->storeManager->getStore($storeKey)->getWebsiteId() + ), + $this->dimensionFactory->create( + CustomerGroupDimensionProvider::DIMENSION_NAME, + (string)$this->httpContext->getValue(CustomerContext::CONTEXT_GROUP) + ) + ] + ); + + return $this->getTable($priceTableName); + } + /** * Retrieve joined price index table alias * diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 9b87515450a12..f2b7ba867a704 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +// @codingStandardsIgnoreFile + namespace Magento\Catalog\Model\ResourceModel\Product; use Magento\Catalog\Api\Data\ProductInterface; @@ -12,11 +14,15 @@ use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Customer\Api\GroupManagementInterface; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; use Magento\Framework\App\ObjectManager; use Magento\Framework\DB\Select; use Magento\Framework\EntityManager\MetadataPool; +use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; use Magento\Store\Model\Store; use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; +use Magento\Framework\Indexer\DimensionFactory; /** * Product collection @@ -276,9 +282,18 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac */ private $tableMaintainer; + /** + * @var PriceTableResolver + */ + private $priceTableResolver; + + /** + * @var DimensionFactory + */ + private $dimensionFactory; + /** * Collection constructor - * * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy @@ -302,7 +317,8 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac * @param ProductLimitationFactory|null $productLimitationFactory * @param MetadataPool|null $metadataPool * @param TableMaintainer|null $tableMaintainer - * + * @param PriceTableResolver|null $priceTableResolver + * @param DimensionFactory|null $dimensionFactory * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -328,7 +344,9 @@ public function __construct( \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, ProductLimitationFactory $productLimitationFactory = null, MetadataPool $metadataPool = null, - TableMaintainer $tableMaintainer = null + TableMaintainer $tableMaintainer = null, + PriceTableResolver $priceTableResolver = null, + DimensionFactory $dimensionFactory = null ) { $this->moduleManager = $moduleManager; $this->_catalogProductFlatState = $catalogProductFlatState; @@ -359,6 +377,9 @@ public function __construct( $connection ); $this->tableMaintainer = $tableMaintainer ?: ObjectManager::getInstance()->get(TableMaintainer::class); + $this->priceTableResolver = $priceTableResolver ?: ObjectManager::getInstance()->get(PriceTableResolver::class); + $this->dimensionFactory = $dimensionFactory + ?: ObjectManager::getInstance()->get(DimensionFactory::class); } /** @@ -476,10 +497,7 @@ public function isEnabledFlat() protected function _construct() { if ($this->isEnabledFlat()) { - $this->_init( - \Magento\Catalog\Model\Product::class, - \Magento\Catalog\Model\ResourceModel\Product\Flat::class - ); + $this->_init(\Magento\Catalog\Model\Product::class, \Magento\Catalog\Model\ResourceModel\Product\Flat::class); } else { $this->_init(\Magento\Catalog\Model\Product::class, \Magento\Catalog\Model\ResourceModel\Product::class); } @@ -913,7 +931,7 @@ private function mapConditionType($conditionType) 'eq' => 'in', 'neq' => 'nin' ]; - return isset($conditionsMap[$conditionType]) ? $conditionsMap[$conditionType] : $conditionType; + return $conditionsMap[$conditionType] ?? $conditionType; } /** @@ -1061,14 +1079,15 @@ public function getAllAttributeValues($attribute) $select = clone $this->getSelect(); $attribute = $this->getEntity()->getAttribute($attribute); - $aiField = $this->getConnection()->getAutoIncrementField($this->getMainTable()); + $fieldMainTable = $this->getConnection()->getAutoIncrementField($this->getMainTable()); + $fieldJoinTable = $attribute->getEntity()->getLinkField(); $select->reset() ->from( ['cpe' => $this->getMainTable()], ['entity_id'] )->join( ['cpa' => $attribute->getBackend()->getTable()], - 'cpe.' . $aiField . ' = cpa.' . $aiField, + 'cpe.' . $fieldMainTable . ' = cpa.' . $fieldJoinTable, ['store_id', 'value'] )->where('attribute_id = ?', (int)$attribute->getId()); @@ -1863,7 +1882,12 @@ protected function _productLimitationJoinPrice() protected function _productLimitationPrice($joinLeft = false) { $filters = $this->_productLimitationFilters; - if (!$filters->isUsingPriceIndex()) { + if (!$filters->isUsingPriceIndex() || + !isset($filters['website_id']) || + (string)$filters['website_id'] === '' || + !isset($filters['customer_group_id']) || + (string)$filters['customer_group_id'] === '' + ) { return $this; } @@ -1898,7 +1922,23 @@ protected function _productLimitationPrice($joinLeft = false) 'max_price', 'tier_price', ]; - $tableName = ['price_index' => $this->getTable('catalog_product_index_price')]; + + $tableName = [ + 'price_index' => $this->priceTableResolver->resolve( + 'catalog_product_index_price', + [ + $this->dimensionFactory->create( + CustomerGroupDimensionProvider::DIMENSION_NAME, + (string)$filters['customer_group_id'] + ), + $this->dimensionFactory->create( + WebsiteDimensionProvider::DIMENSION_NAME, + (string)$filters['website_id'] + ) + ] + ) + ]; + if ($joinLeft) { $select->joinLeft($tableName, $joinCond, $colls); } else { @@ -2228,6 +2268,7 @@ public function addPriceDataFieldFilter($comparisonFormat, $fields) * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @since 101.0.1 + * @throws \Magento\Framework\Exception\LocalizedException */ public function addMediaGalleryData() { @@ -2239,34 +2280,36 @@ public function addMediaGalleryData() return $this; } - /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ - $attribute = $this->getAttribute('media_gallery'); - $select = $this->getMediaGalleryResource()->createBatchBaseSelect( - $this->getStoreId(), - $attribute->getAttributeId() - ); - - $mediaGalleries = []; - $linkField = $this->getProductEntityMetadata()->getLinkField(); $items = $this->getItems(); + $linkField = $this->getProductEntityMetadata()->getLinkField(); - $select->where( - 'entity.' . $linkField . ' IN (?)', - array_map( - function ($item) use ($linkField) { - return $item->getData($linkField); - }, - $items - ) - ); + $select = $this->getMediaGalleryResource() + ->createBatchBaseSelect( + $this->getStoreId(), + $this->getAttribute('media_gallery')->getAttributeId() + )->reset( + Select::ORDER // we don't care what order is in current scenario + )->where( + 'entity.' . $linkField . ' IN (?)', + array_map( + function ($item) use ($linkField) { + return (int) $item->getOrigData($linkField); + }, + $items + ) + ); + + $mediaGalleries = []; foreach ($this->getConnection()->fetchAll($select) as $row) { $mediaGalleries[$row[$linkField]][] = $row; } foreach ($items as $item) { - $mediaEntries = isset($mediaGalleries[$item->getData($linkField)]) ? - $mediaGalleries[$item->getData($linkField)] : []; - $this->getGalleryReadHandler()->addMediaDataToProduct($item, $mediaEntries); + $this->getGalleryReadHandler() + ->addMediaDataToProduct( + $item, + $mediaGalleries[$item->getOrigData($linkField)] ?? [] + ); } $this->setFlag('media_gallery_added', true); 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 ee1df8f23424d..ebe04fb63b217 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php @@ -7,9 +7,13 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; use Magento\Framework\App\ObjectManager; use Magento\Framework\DB\Select; use Magento\Catalog\Model\ResourceModel\Product\LinkedProductSelectBuilderInterface; +use Magento\Framework\Indexer\DimensionFactory; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; +use Magento\Framework\Search\Request\IndexScopeResolverInterface; class LinkedProductSelectBuilderByIndexPrice implements LinkedProductSelectBuilderInterface { @@ -38,6 +42,16 @@ class LinkedProductSelectBuilderByIndexPrice implements LinkedProductSelectBuild */ private $baseSelectProcessor; + /** + * @var IndexScopeResolverInterface|null + */ + private $priceTableResolver; + + /** + * @var DimensionFactory|null + */ + private $dimensionFactory; + /** * LinkedProductSelectBuilderByIndexPrice constructor. * @param \Magento\Store\Model\StoreManagerInterface $storeManager @@ -45,13 +59,17 @@ class LinkedProductSelectBuilderByIndexPrice implements LinkedProductSelectBuild * @param \Magento\Customer\Model\Session $customerSession * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool * @param BaseSelectProcessorInterface|null $baseSelectProcessor + * @param IndexScopeResolverInterface|null $priceTableResolver + * @param DimensionFactory|null $dimensionFactory */ public function __construct( \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\App\ResourceConnection $resourceConnection, \Magento\Customer\Model\Session $customerSession, \Magento\Framework\EntityManager\MetadataPool $metadataPool, - BaseSelectProcessorInterface $baseSelectProcessor = null + BaseSelectProcessorInterface $baseSelectProcessor = null, + IndexScopeResolverInterface $priceTableResolver = null, + DimensionFactory $dimensionFactory = null ) { $this->storeManager = $storeManager; $this->resource = $resourceConnection; @@ -59,6 +77,9 @@ public function __construct( $this->metadataPool = $metadataPool; $this->baseSelectProcessor = (null !== $baseSelectProcessor) ? $baseSelectProcessor : ObjectManager::getInstance()->get(BaseSelectProcessorInterface::class); + $this->priceTableResolver = $priceTableResolver + ?? ObjectManager::getInstance()->get(IndexScopeResolverInterface::class); + $this->dimensionFactory = $dimensionFactory ?? ObjectManager::getInstance()->get(DimensionFactory::class); } /** @@ -68,6 +89,8 @@ public function build($productId) { $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $productTable = $this->resource->getTableName('catalog_product_entity'); + $websiteId = $this->storeManager->getStore()->getWebsiteId(); + $customerGroupId = $this->customerSession->getCustomerGroupId(); $priceSelect = $this->resource->getConnection()->select() ->from(['parent' => $productTable], '') @@ -80,12 +103,20 @@ public function build($productId) sprintf('%s.entity_id = link.child_id', BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS), ['entity_id'] )->joinInner( - ['t' => $this->resource->getTableName('catalog_product_index_price')], + [ + 't' => $this->priceTableResolver->resolve('catalog_product_index_price', [ + $this->dimensionFactory->create(WebsiteDimensionProvider::DIMENSION_NAME, (string)$websiteId), + $this->dimensionFactory->create( + CustomerGroupDimensionProvider::DIMENSION_NAME, + (string)$customerGroupId + ), + ]) + ], sprintf('t.entity_id = %s.entity_id', BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS), [] )->where('parent.entity_id = ?', $productId) - ->where('t.website_id = ?', $this->storeManager->getStore()->getWebsiteId()) - ->where('t.customer_group_id = ?', $this->customerSession->getCustomerGroupId()) + ->where('t.website_id = ?', $websiteId) + ->where('t.customer_group_id = ?', $customerGroupId) ->order('t.min_price ' . Select::SQL_ASC) ->order(BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS . '.' . $linkField . ' ' . Select::SQL_ASC) ->limit(1); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CustomOptionPriceModifier.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CustomOptionPriceModifier.php new file mode 100644 index 0000000000000..646cd0d4c1a4c --- /dev/null +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CustomOptionPriceModifier.php @@ -0,0 +1,464 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Price; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\DB\Select; +use Magento\Framework\DB\Sql\ColumnValueExpression; + +/** + * Class for modify custom option price. + */ +class CustomOptionPriceModifier implements PriceModifierInterface +{ + /** + * @var \Magento\Framework\App\ResourceConnection + */ + private $resource; + + /** + * @var \Magento\Framework\EntityManager\MetadataPool + */ + private $metadataPool; + + /** + * @var \Magento\Framework\DB\Sql\ColumnValueExpression + */ + private $columnValueExpressionFactory; + + /** + * @var \Magento\Catalog\Helper\Data + */ + private $dataHelper; + + /** + * @var string + */ + private $connectionName; + + /** + * @var bool + */ + private $isPriceGlobalFlag; + + /** + * @var \Magento\Framework\DB\Adapter\AdapterInterface + */ + private $connection; + + /** + * @var \Magento\Framework\Indexer\Table\StrategyInterface + */ + private $tableStrategy; + + /** + * @param \Magento\Framework\App\ResourceConnection $resource + * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool + * @param \Magento\Framework\DB\Sql\ColumnValueExpressionFactory $columnValueExpressionFactory + * @param \Magento\Catalog\Helper\Data $dataHelper + * @param \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy + * @param string $connectionName + */ + public function __construct( + \Magento\Framework\App\ResourceConnection $resource, + \Magento\Framework\EntityManager\MetadataPool $metadataPool, + \Magento\Framework\DB\Sql\ColumnValueExpressionFactory $columnValueExpressionFactory, + \Magento\Catalog\Helper\Data $dataHelper, + \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy, + $connectionName = 'indexer' + ) { + $this->resource = $resource; + $this->metadataPool = $metadataPool; + $this->connectionName = $connectionName; + $this->columnValueExpressionFactory = $columnValueExpressionFactory; + $this->dataHelper = $dataHelper; + $this->tableStrategy = $tableStrategy; + } + + /** + * Apply custom option price to temporary index price table + * + * @param IndexTableStructure $priceTable + * @param array $entityIds + * @return void + * @throws \Exception + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) + { + // no need to run all queries if current products have no custom options + if (!$this->checkIfCustomOptionsExist($priceTable)) { + return; + } + + $connection = $this->getConnection(); + $finalPriceTable = $priceTable->getTableName(); + + $coaTable = $this->getCustomOptionAggregateTable(); + $this->prepareCustomOptionAggregateTable(); + + $copTable = $this->getCustomOptionPriceTable(); + $this->prepareCustomOptionPriceTable(); + + $select = $this->getSelectForOptionsWithMultipleValues($finalPriceTable); + $query = $select->insertFromSelect($coaTable); + $connection->query($query); + + $select = $this->getSelectForOptionsWithOneValue($finalPriceTable); + $query = $select->insertFromSelect($coaTable); + $connection->query($query); + + $select = $this->getSelectAggregated($coaTable); + $query = $select->insertFromSelect($copTable); + $connection->query($query); + + // update tmp price index with prices from custom options (from previous aggregated table) + $select = $this->getSelectForUpdate($copTable); + $query = $select->crossUpdateFromSelect(['i' => $finalPriceTable]); + $connection->query($query); + + $connection->delete($coaTable); + $connection->delete($copTable); + } + + /** + * @param IndexTableStructure $priceTable + * @return bool + * @throws \Exception + */ + private function checkIfCustomOptionsExist(IndexTableStructure $priceTable): bool + { + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + + $select = $this->getConnection() + ->select() + ->from( + ['i' => $priceTable->getTableName()], + ['entity_id'] + )->join( + ['e' => $this->getTable('catalog_product_entity')], + 'e.entity_id = i.entity_id', + [] + )->join( + ['o' => $this->getTable('catalog_product_option')], + 'o.product_id = e.' . $metadata->getLinkField(), + ['option_id'] + ); + + return !empty($this->getConnection()->fetchRow($select)); + } + + /** + * @return \Magento\Framework\DB\Adapter\AdapterInterface + */ + private function getConnection() + { + if (null === $this->connection) { + $this->connection = $this->resource->getConnection($this->connectionName); + } + + return $this->connection; + } + + /** + * Prepare prices for products with custom options that has multiple values + * + * @param string $sourceTable + * @return \Magento\Framework\DB\Select + * @throws \Exception + */ + private function getSelectForOptionsWithMultipleValues(string $sourceTable): Select + { + $connection = $this->resource->getConnection($this->connectionName); + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + + $select = $connection->select() + ->from( + ['i' => $sourceTable], + ['entity_id', 'customer_group_id', 'website_id'] + )->join( + ['e' => $this->getTable('catalog_product_entity')], + 'e.entity_id = i.entity_id', + [] + )->join( + ['cwd' => $this->getTable('catalog_product_index_website')], + 'i.website_id = cwd.website_id', + [] + )->join( + ['o' => $this->getTable('catalog_product_option')], + 'o.product_id = e.' . $metadata->getLinkField(), + ['option_id'] + )->join( + ['ot' => $this->getTable('catalog_product_option_type_value')], + 'ot.option_id = o.option_id', + [] + )->join( + ['otpd' => $this->getTable('catalog_product_option_type_price')], + 'otpd.option_type_id = ot.option_type_id AND otpd.store_id = 0', + [] + )->group( + ['i.entity_id', 'i.customer_group_id', 'i.website_id', 'o.option_id'] + ); + + if ($this->isPriceGlobal()) { + $optPriceType = 'otpd.price_type'; + $optPriceValue = 'otpd.price'; + } else { + $select->joinLeft( + ['otps' => $this->getTable('catalog_product_option_type_price')], + 'otps.option_type_id = otpd.option_type_id AND otpd.store_id = cwd.default_store_id', + [] + ); + + $optPriceType = $connection->getCheckSql( + 'otps.option_type_price_id > 0', + 'otps.price_type', + 'otpd.price_type' + ); + $optPriceValue = $connection->getCheckSql('otps.option_type_price_id > 0', 'otps.price', 'otpd.price'); + } + + $minPriceRound = $this->columnValueExpressionFactory + ->create([ + 'expression' => "ROUND(i.final_price * ({$optPriceValue} / 100), 4)" + ]); + $minPriceExpr = $connection->getCheckSql("{$optPriceType} = 'fixed'", $optPriceValue, $minPriceRound); + $minPriceMin = $this->columnValueExpressionFactory + ->create([ + 'expression' => "MIN({$minPriceExpr})" + ]); + $minPrice = $connection->getCheckSql("MIN(o.is_require) = 1", $minPriceMin, '0'); + + $tierPriceRound = $this->columnValueExpressionFactory + ->create([ + 'expression' => "ROUND(i.tier_price * ({$optPriceValue} / 100), 4)" + ]); + $tierPriceExpr = $connection->getCheckSql("{$optPriceType} = 'fixed'", $optPriceValue, $tierPriceRound); + $tierPriceMin = $this->columnValueExpressionFactory + ->create([ + 'expression' => "MIN({$tierPriceExpr})" + ]); + $tierPriceValue = $connection->getCheckSql("MIN(o.is_require) > 0", $tierPriceMin, 0); + $tierPrice = $connection->getCheckSql("MIN(i.tier_price) IS NOT NULL", $tierPriceValue, "NULL"); + + $maxPriceRound = $this->columnValueExpressionFactory + ->create([ + 'expression' => "ROUND(i.final_price * ({$optPriceValue} / 100), 4)" + ]); + $maxPriceExpr = $connection->getCheckSql("{$optPriceType} = 'fixed'", $optPriceValue, $maxPriceRound); + $maxPrice = $connection->getCheckSql( + "(MIN(o.type)='radio' OR MIN(o.type)='drop_down')", + "MAX({$maxPriceExpr})", + "SUM({$maxPriceExpr})" + ); + + $select->columns( + [ + 'min_price' => $minPrice, + 'max_price' => $maxPrice, + 'tier_price' => $tierPrice, + ] + ); + + return $select; + } + + /** + * Prepare prices for products with custom options that has single value + * + * @param string $sourceTable + * @return \Magento\Framework\DB\Select + * @throws \Exception + */ + private function getSelectForOptionsWithOneValue(string $sourceTable): Select + { + $connection = $this->resource->getConnection($this->connectionName); + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + + $select = $connection->select() + ->from( + ['i' => $sourceTable], + ['entity_id', 'customer_group_id', 'website_id'] + )->join( + ['e' => $this->getTable('catalog_product_entity')], + 'e.entity_id = i.entity_id', + [] + )->join( + ['cwd' => $this->getTable('catalog_product_index_website')], + 'i.website_id = cwd.website_id', + [] + )->join( + ['o' => $this->getTable('catalog_product_option')], + 'o.product_id = e.' . $metadata->getLinkField(), + ['option_id'] + )->join( + ['opd' => $this->getTable('catalog_product_option_price')], + 'opd.option_id = o.option_id AND opd.store_id = 0', + [] + ); + + if ($this->isPriceGlobal()) { + $optPriceType = 'opd.price_type'; + $optPriceValue = 'opd.price'; + } else { + $select->joinLeft( + ['ops' => $this->getTable('catalog_product_option_price')], + 'ops.option_id = opd.option_id AND ops.store_id = cwd.default_store_id', + [] + ); + + $optPriceType = $connection->getCheckSql('ops.option_price_id > 0', 'ops.price_type', 'opd.price_type'); + $optPriceValue = $connection->getCheckSql('ops.option_price_id > 0', 'ops.price', 'opd.price'); + } + + $minPriceRound = $this->columnValueExpressionFactory + ->create([ + 'expression' => "ROUND(i.final_price * ({$optPriceValue} / 100), 4)" + ]); + $priceExpr = $connection->getCheckSql("{$optPriceType} = 'fixed'", $optPriceValue, $minPriceRound); + $minPrice = $connection->getCheckSql("{$priceExpr} > 0 AND o.is_require = 1", $priceExpr, 0); + + $maxPrice = $priceExpr; + + $tierPriceRound = $this->columnValueExpressionFactory + ->create([ + 'expression' => "ROUND(i.tier_price * ({$optPriceValue} / 100), 4)" + ]); + $tierPriceExpr = $connection->getCheckSql("{$optPriceType} = 'fixed'", $optPriceValue, $tierPriceRound); + $tierPriceValue = $connection->getCheckSql("{$tierPriceExpr} > 0 AND o.is_require = 1", $tierPriceExpr, 0); + $tierPrice = $connection->getCheckSql("i.tier_price IS NOT NULL", $tierPriceValue, "NULL"); + + $select->columns( + [ + 'min_price' => $minPrice, + 'max_price' => $maxPrice, + 'tier_price' => $tierPrice, + ] + ); + + return $select; + } + + /** + * Aggregate prices with one and multiply options into one table + * + * @param string $sourceTable + * @return \Magento\Framework\DB\Select + */ + private function getSelectAggregated(string $sourceTable): Select + { + $connection = $this->resource->getConnection($this->connectionName); + + $select = $connection->select() + ->from( + [$sourceTable], + [ + 'entity_id', + 'customer_group_id', + 'website_id', + 'min_price' => 'SUM(min_price)', + 'max_price' => 'SUM(max_price)', + 'tier_price' => 'SUM(tier_price)', + ] + )->group( + ['entity_id', 'customer_group_id', 'website_id'] + ); + + return $select; + } + + /** + * @param string $sourceTable + * @return \Magento\Framework\DB\Select + */ + private function getSelectForUpdate(string $sourceTable): Select + { + $connection = $this->resource->getConnection($this->connectionName); + + $select = $connection->select()->join( + ['io' => $sourceTable], + 'i.entity_id = io.entity_id AND i.customer_group_id = io.customer_group_id' . + ' AND i.website_id = io.website_id', + [] + ); + $select->columns( + [ + 'min_price' => new ColumnValueExpression('i.min_price + io.min_price'), + 'max_price' => new ColumnValueExpression('i.max_price + io.max_price'), + 'tier_price' => $connection->getCheckSql( + 'i.tier_price IS NOT NULL', + 'i.tier_price + io.tier_price', + 'NULL' + ), + ] + ); + + return $select; + } + + /** + * @param string $tableName + * @return string + */ + private function getTable(string $tableName): string + { + return $this->resource->getTableName($tableName, $this->connectionName); + } + + /** + * @return bool + */ + private function isPriceGlobal(): bool + { + if ($this->isPriceGlobalFlag === null) { + $this->isPriceGlobalFlag = $this->dataHelper->isPriceGlobal(); + } + + return $this->isPriceGlobalFlag; + } + + /** + * Retrieve table name for custom option temporary aggregation data + * + * @return string + */ + private function getCustomOptionAggregateTable(): string + { + return $this->tableStrategy->getTableName('catalog_product_index_price_opt_agr'); + } + + /** + * Retrieve table name for custom option prices data + * + * @return string + */ + private function getCustomOptionPriceTable(): string + { + return $this->tableStrategy->getTableName('catalog_product_index_price_opt'); + } + + /** + * Prepare table structure for custom option temporary aggregation data + * + * @return void + */ + private function prepareCustomOptionAggregateTable() + { + $this->getConnection()->delete($this->getCustomOptionAggregateTable()); + } + + /** + * Prepare table structure for custom option prices data + * + * @return void + */ + private function prepareCustomOptionPriceTable() + { + $this->getConnection()->delete($this->getCustomOptionPriceTable()); + } +} 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 4ca407a53f8ae..f0cf9b99e6d67 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 @@ -6,6 +6,7 @@ namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Price; use Magento\Catalog\Model\ResourceModel\Product\Indexer\AbstractIndexer; +use Magento\Framework\Indexer\DimensionalIndexerInterface; /** * Default Product Type Price Indexer Resource model @@ -16,6 +17,8 @@ * @author Magento Core Team <core@magentocommerce.com> * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 + * @deprecated Not used anymore for price indexation. Class left for backward compatibility + * @see DimensionalIndexerInterface */ class DefaultPrice extends AbstractIndexer implements PriceInterface { @@ -71,7 +74,7 @@ class DefaultPrice extends AbstractIndexer implements PriceInterface * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Framework\Module\Manager $moduleManager * @param string|null $connectionName - * @param null|IndexTableStructureFactory $indexTableStructureFactory + * @param IndexTableStructureFactory $indexTableStructureFactory * @param PriceModifierInterface[] $priceModifiers */ public function __construct( @@ -307,7 +310,7 @@ protected function prepareFinalPriceDataForType($entityIds, $type) $query = $select->insertFromSelect($finalPriceTable->getTableName(), [], false); $this->getConnection()->query($query); - $this->applyDiscountPrices($finalPriceTable); + $this->modifyPriceIndex($finalPriceTable); return $this; } @@ -327,6 +330,7 @@ protected function prepareFinalPriceDataForType($entityIds, $type) protected function getSelect($entityIds = null, $type = null) { $metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); + $linkField = $metadata->getLinkField(); $connection = $this->getConnection(); $select = $connection->select()->from( ['e' => $this->getTable('catalog_product_entity')], @@ -356,9 +360,38 @@ protected function getSelect($entityIds = null, $type = null) 'pw.product_id = e.entity_id AND pw.website_id = cw.website_id', [] )->joinLeft( - ['tp' => $this->_getTierPriceIndexTable()], - 'tp.entity_id = e.entity_id AND tp.website_id = cw.website_id' . - ' AND tp.customer_group_id = cg.customer_group_id', + // we need this only for BCC in case someone expects table `tp` to be present in query + ['tp' => $this->getTable('catalog_product_index_tier_price')], + 'tp.entity_id = e.entity_id AND tp.customer_group_id = cg.customer_group_id' . + ' AND tp.website_id = pw.website_id', + [] + )->joinLeft( + // calculate tier price specified as Website = `All Websites` and Customer Group = `Specific Customer Group` + ['tier_price_1' => $this->getTable('catalog_product_entity_tier_price')], + 'tier_price_1.' . $linkField . ' = e.' . $linkField . ' AND tier_price_1.all_groups = 0' . + ' AND tier_price_1.customer_group_id = cg.customer_group_id AND tier_price_1.qty = 1' . + ' AND tier_price_1.website_id = 0', + [] + )->joinLeft( + // calculate tier price specified as Website = `Specific Website` + //and Customer Group = `Specific Customer Group` + ['tier_price_2' => $this->getTable('catalog_product_entity_tier_price')], + 'tier_price_2.' . $linkField . ' = e.' . $linkField . ' AND tier_price_2.all_groups = 0' . + ' AND tier_price_2.customer_group_id = cg.customer_group_id AND tier_price_2.qty = 1' . + ' AND tier_price_2.website_id = cw.website_id', + [] + )->joinLeft( + // calculate tier price specified as Website = `All Websites` and Customer Group = `ALL GROUPS` + ['tier_price_3' => $this->getTable('catalog_product_entity_tier_price')], + 'tier_price_3.' . $linkField . ' = e.' . $linkField . ' AND tier_price_3.all_groups = 1' . + ' AND tier_price_3.customer_group_id = 0 AND tier_price_3.qty = 1 AND tier_price_3.website_id = 0', + [] + )->joinLeft( + // calculate tier price specified as Website = `Specific Website` and Customer Group = `ALL GROUPS` + ['tier_price_4' => $this->getTable('catalog_product_entity_tier_price')], + 'tier_price_4.' . $linkField . ' = e.' . $linkField . ' AND tier_price_4.all_groups = 1' . + ' AND tier_price_4.customer_group_id = 0 AND tier_price_4.qty = 1' . + ' AND tier_price_4.website_id = cw.website_id', [] ); @@ -374,7 +407,7 @@ protected function getSelect($entityIds = null, $type = null) $this->_addAttributeToSelect( $select, 'status', - 'e.' . $metadata->getLinkField(), + 'e.' . $linkField, 'cs.store_id', $statusCond, true @@ -383,7 +416,7 @@ protected function getSelect($entityIds = null, $type = null) $taxClassId = $this->_addAttributeToSelect( $select, 'tax_class_id', - 'e.' . $metadata->getLinkField(), + 'e.' . $linkField, 'cs.store_id' ); } else { @@ -394,25 +427,25 @@ protected function getSelect($entityIds = null, $type = null) $price = $this->_addAttributeToSelect( $select, 'price', - 'e.' . $metadata->getLinkField(), + 'e.' . $linkField, 'cs.store_id' ); $specialPrice = $this->_addAttributeToSelect( $select, 'special_price', - 'e.' . $metadata->getLinkField(), + 'e.' . $linkField, 'cs.store_id' ); $specialFrom = $this->_addAttributeToSelect( $select, 'special_from_date', - 'e.' . $metadata->getLinkField(), + 'e.' . $linkField, 'cs.store_id' ); $specialTo = $this->_addAttributeToSelect( $select, 'special_to_date', - 'e.' . $metadata->getLinkField(), + 'e.' . $linkField, 'cs.store_id' ); $currentDate = 'cwd.website_date'; @@ -427,11 +460,8 @@ protected function getSelect($entityIds = null, $type = null) $specialPrice, $maxUnsignedBigint ); - $tierPrice = new \Zend_Db_Expr('tp.min_price'); - $tierPriceExpr = $connection->getIfNullSql( - $tierPrice, - $maxUnsignedBigint - ); + $tierPrice = $this->getTotalTierPriceExpression($price); + $tierPriceExpr = $connection->getIfNullSql($tierPrice, $maxUnsignedBigint); $finalPrice = $connection->getLeastSql([ $price, $specialPriceExpr, @@ -512,12 +542,12 @@ protected function _prepareCustomOptionPriceTable() } /** - * Apply discount prices to final price index table. + * Modify data in price index table. * * @param IndexTableStructure $finalPriceTable * @return void */ - private function applyDiscountPrices(IndexTableStructure $finalPriceTable) : void + private function modifyPriceIndex(IndexTableStructure $finalPriceTable) { foreach ($this->priceModifiers as $priceModifier) { $priceModifier->modifyPrice($finalPriceTable); @@ -791,4 +821,57 @@ protected function hasEntity() return $this->hasEntity; } + + /** + * @param \Zend_Db_Expr $priceExpression + * @return \Zend_Db_Expr + */ + private function getTotalTierPriceExpression(\Zend_Db_Expr $priceExpression) + { + $maxUnsignedBigint = '~0'; + + return $this->getConnection()->getCheckSql( + implode( + ' AND ', + [ + 'tier_price_1.value_id is NULL', + 'tier_price_2.value_id is NULL', + 'tier_price_3.value_id is NULL', + 'tier_price_4.value_id is NULL' + ] + ), + 'NULL', + $this->getConnection()->getLeastSql([ + $this->getConnection()->getIfNullSql( + $this->getTierPriceExpressionForTable('tier_price_1', $priceExpression), + $maxUnsignedBigint + ), + $this->getConnection()->getIfNullSql( + $this->getTierPriceExpressionForTable('tier_price_2', $priceExpression), + $maxUnsignedBigint + ), + $this->getConnection()->getIfNullSql( + $this->getTierPriceExpressionForTable('tier_price_3', $priceExpression), + $maxUnsignedBigint + ), + $this->getConnection()->getIfNullSql( + $this->getTierPriceExpressionForTable('tier_price_4', $priceExpression), + $maxUnsignedBigint + ), + ]) + ); + } + + private function getTierPriceExpressionForTable($tableAlias, \Zend_Db_Expr $priceExpression) + { + return $this->getConnection()->getCheckSql( + sprintf('%s.value = 0', $tableAlias), + sprintf( + 'ROUND(%s * (1 - ROUND(%s.percentage_value * cwd.rate, 4) / 100), 4)', + $priceExpression, + $tableAlias + ), + sprintf('ROUND(%s.value * cwd.rate, 4)', $tableAlias) + ); + } } 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 21a7647214c26..9a310c7365ac9 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 @@ -9,6 +9,8 @@ */ namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Price; +use Magento\Framework\Indexer\DimensionalIndexerInterface; + class Factory { /** @@ -40,14 +42,17 @@ public function create($className, array $data = []) { $indexerPrice = $this->_objectManager->create($className, $data); - if (!$indexerPrice instanceof \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice) { - throw new \Magento\Framework\Exception\LocalizedException( - __( - '%1 doesn\'t extend \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice', - $className - ) - ); + if ($indexerPrice instanceof PriceInterface || $indexerPrice instanceof DimensionalIndexerInterface) { + return $indexerPrice; } - return $indexerPrice; + + throw new \Magento\Framework\Exception\LocalizedException( + __( + 'Price indexer "%1" must implement %2 or %3', + $className, + PriceInterface::class, + DimensionalIndexerInterface::class + ) + ); } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php new file mode 100644 index 0000000000000..ff76f9d2df4b9 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php @@ -0,0 +1,330 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Query; + +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Framework\DB\Select; +use Magento\Framework\DB\Sql\ColumnValueExpression; +use Magento\Framework\Indexer\Dimension; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; + +/** + * Prepare base select for Product Price index limited by specified dimensions: website and customer group + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class BaseFinalPrice +{ + /** + * @var \Magento\Framework\App\ResourceConnection + */ + private $resource; + + /** + * @var string + */ + private $connectionName; + + /** + * @var JoinAttributeProcessor + */ + private $joinAttributeProcessor; + + /** + * @var \Magento\Framework\Module\Manager + */ + private $moduleManager; + + /** + * @var \Magento\Framework\Event\ManagerInterface + */ + private $eventManager; + + /** + * Mapping between dimensions and field in database + * + * @var array + */ + private $dimensionToFieldMapper = [ + WebsiteDimensionProvider::DIMENSION_NAME => 'pw.website_id', + CustomerGroupDimensionProvider::DIMENSION_NAME => 'cg.customer_group_id', + ]; + + /** + * @var \Magento\Framework\DB\Adapter\AdapterInterface + */ + private $connection; + + /** + * @var \Magento\Framework\EntityManager\MetadataPool + */ + private $metadataPool; + + /** + * BaseFinalPrice constructor. + * @param \Magento\Framework\App\ResourceConnection $resource + * @param JoinAttributeProcessor $joinAttributeProcessor + * @param \Magento\Framework\Module\Manager $moduleManager + * @param string $connectionName + */ + public function __construct( + \Magento\Framework\App\ResourceConnection $resource, + JoinAttributeProcessor $joinAttributeProcessor, + \Magento\Framework\Module\Manager $moduleManager, + \Magento\Framework\Event\ManagerInterface $eventManager, + \Magento\Framework\EntityManager\MetadataPool $metadataPool, + $connectionName = 'indexer' + ) { + $this->resource = $resource; + $this->connectionName = $connectionName; + $this->joinAttributeProcessor = $joinAttributeProcessor; + $this->moduleManager = $moduleManager; + $this->eventManager = $eventManager; + $this->metadataPool = $metadataPool; + } + + /** + * @param Dimension[] $dimensions + * @param string $productType + * @param array $entityIds + * @return Select + * @throws \LogicException + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Zend_Db_Select_Exception + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function getQuery(array $dimensions, string $productType, array $entityIds = []): Select + { + $connection = $this->getConnection(); + $metadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); + $linkField = $metadata->getLinkField(); + + $select = $connection->select()->from( + ['e' => $this->getTable('catalog_product_entity')], + ['entity_id'] + )->joinInner( + ['cg' => $this->getTable('customer_group')], + array_key_exists(CustomerGroupDimensionProvider::DIMENSION_NAME, $dimensions) + ? sprintf( + '%s = %s', + $this->dimensionToFieldMapper[CustomerGroupDimensionProvider::DIMENSION_NAME], + $dimensions[CustomerGroupDimensionProvider::DIMENSION_NAME]->getValue() + ) : '', + ['customer_group_id'] + )->joinInner( + ['pw' => $this->getTable('catalog_product_website')], + 'pw.product_id = e.entity_id', + ['pw.website_id'] + )->joinInner( + ['cwd' => $this->getTable('catalog_product_index_website')], + 'pw.website_id = cwd.website_id', + [] + )->joinLeft( + // we need this only for BCC in case someone expects table `tp` to be present in query + ['tp' => $this->getTable('catalog_product_index_tier_price')], + 'tp.entity_id = e.entity_id AND' . + ' tp.customer_group_id = cg.customer_group_id AND tp.website_id = pw.website_id', + [] + )->joinLeft( + // calculate tier price specified as Website = `All Websites` and Customer Group = `Specific Customer Group` + ['tier_price_1' => $this->getTable('catalog_product_entity_tier_price')], + 'tier_price_1.' . $linkField . ' = e.' . $linkField . ' AND tier_price_1.all_groups = 0' . + ' AND tier_price_1.customer_group_id = cg.customer_group_id AND tier_price_1.qty = 1' . + ' AND tier_price_1.website_id = 0', + [] + )->joinLeft( + // calculate tier price specified as Website = `Specific Website` + //and Customer Group = `Specific Customer Group` + ['tier_price_2' => $this->getTable('catalog_product_entity_tier_price')], + 'tier_price_2.' . $linkField . ' = e.' . $linkField . ' AND tier_price_2.all_groups = 0 ' . + 'AND tier_price_2.customer_group_id = cg.customer_group_id AND tier_price_2.qty = 1' . + ' AND tier_price_2.website_id = pw.website_id', + [] + )->joinLeft( + // calculate tier price specified as Website = `All Websites` and Customer Group = `ALL GROUPS` + ['tier_price_3' => $this->getTable('catalog_product_entity_tier_price')], + 'tier_price_3.' . $linkField . ' = e.' . $linkField . ' AND tier_price_3.all_groups = 1 ' . + 'AND tier_price_3.customer_group_id = 0 AND tier_price_3.qty = 1 AND tier_price_3.website_id = 0', + [] + )->joinLeft( + // calculate tier price specified as Website = `Specific Website` and Customer Group = `ALL GROUPS` + ['tier_price_4' => $this->getTable('catalog_product_entity_tier_price')], + 'tier_price_4.' . $linkField . ' = e.' . $linkField . ' AND tier_price_4.all_groups = 1' . + ' AND tier_price_4.customer_group_id = 0 AND tier_price_4.qty = 1' . + ' AND tier_price_4.website_id = pw.website_id', + [] + ); + + foreach ($dimensions as $dimension) { + if (!isset($this->dimensionToFieldMapper[$dimension->getName()])) { + throw new \LogicException( + 'Provided dimension is not valid for Price indexer: ' . $dimension->getName() + ); + } + $select->where($this->dimensionToFieldMapper[$dimension->getName()] . ' = ?', $dimension->getValue()); + } + + if ($this->moduleManager->isEnabled('Magento_Tax')) { + $taxClassId = $this->joinAttributeProcessor->process($select, 'tax_class_id'); + } else { + $taxClassId = new \Zend_Db_Expr(0); + } + $select->columns(['tax_class_id' => $taxClassId]); + + $this->joinAttributeProcessor->process($select, 'status', Status::STATUS_ENABLED); + + $price = $this->joinAttributeProcessor->process($select, 'price'); + $specialPrice = $this->joinAttributeProcessor->process($select, 'special_price'); + $specialFrom = $this->joinAttributeProcessor->process($select, 'special_from_date'); + $specialTo = $this->joinAttributeProcessor->process($select, 'special_to_date'); + $currentDate = 'cwd.website_date'; + + $maxUnsignedBigint = '~0'; + $specialFromDate = $connection->getDatePartSql($specialFrom); + $specialToDate = $connection->getDatePartSql($specialTo); + $specialFromExpr = "{$specialFrom} IS NULL OR {$specialFromDate} <= {$currentDate}"; + $specialToExpr = "{$specialTo} IS NULL OR {$specialToDate} >= {$currentDate}"; + $specialPriceExpr = $connection->getCheckSql( + "{$specialPrice} IS NOT NULL AND {$specialFromExpr} AND {$specialToExpr}", + $specialPrice, + $maxUnsignedBigint + ); + $tierPrice = $this->getTotalTierPriceExpression($price); + $tierPriceExpr = $connection->getIfNullSql($tierPrice, $maxUnsignedBigint); + $finalPrice = $connection->getLeastSql([ + $price, + $specialPriceExpr, + $tierPriceExpr, + ]); + + $select->columns( + [ + //orig_price in catalog_product_index_price_final_tmp + 'price' => $connection->getIfNullSql($price, 0), + //price in catalog_product_index_price_final_tmp + 'final_price' => $connection->getIfNullSql($finalPrice, 0), + 'min_price' => $connection->getIfNullSql($finalPrice, 0), + 'max_price' => $connection->getIfNullSql($finalPrice, 0), + 'tier_price' => $tierPrice, + ] + ); + + $select->where("e.type_id = ?", $productType); + + if ($entityIds !== null) { + if (count($entityIds) > 1) { + $select->where(sprintf('e.entity_id BETWEEN %s AND %s', min($entityIds), max($entityIds))); + } else { + $select->where('e.entity_id = ?', $entityIds); + } + } + + /** + * throw event for backward compatibility + */ + $this->eventManager->dispatch( + 'prepare_catalog_product_index_select', + [ + 'select' => $select, + 'entity_field' => new ColumnValueExpression('e.entity_id'), + 'website_field' => new ColumnValueExpression('pw.website_id'), + 'store_field' => new ColumnValueExpression('cwd.default_store_id'), + ] + ); + + return $select; + } + + /** + * Get total tier price expression + * + * @param \Zend_Db_Expr $priceExpression + * @return \Zend_Db_Expr + */ + private function getTotalTierPriceExpression(\Zend_Db_Expr $priceExpression) + { + $maxUnsignedBigint = '~0'; + + return $this->getConnection()->getCheckSql( + implode( + ' AND ', + [ + 'tier_price_1.value_id is NULL', + 'tier_price_2.value_id is NULL', + 'tier_price_3.value_id is NULL', + 'tier_price_4.value_id is NULL' + ] + ), + 'NULL', + $this->getConnection()->getLeastSql([ + $this->getConnection()->getIfNullSql( + $this->getTierPriceExpressionForTable('tier_price_1', $priceExpression), + $maxUnsignedBigint + ), + $this->getConnection()->getIfNullSql( + $this->getTierPriceExpressionForTable('tier_price_2', $priceExpression), + $maxUnsignedBigint + ), + $this->getConnection()->getIfNullSql( + $this->getTierPriceExpressionForTable('tier_price_3', $priceExpression), + $maxUnsignedBigint + ), + $this->getConnection()->getIfNullSql( + $this->getTierPriceExpressionForTable('tier_price_4', $priceExpression), + $maxUnsignedBigint + ), + ]) + ); + } + + /** + * Get tier price expression for table + * + * @param $tableAlias + * @param \Zend_Db_Expr $priceExpression + * @return \Zend_Db_Expr + */ + private function getTierPriceExpressionForTable($tableAlias, \Zend_Db_Expr $priceExpression): \Zend_Db_Expr + { + return $this->getConnection()->getCheckSql( + sprintf('%s.value = 0', $tableAlias), + sprintf( + 'ROUND(%s * (1 - ROUND(%s.percentage_value * cwd.rate, 4) / 100), 4)', + $priceExpression, + $tableAlias + ), + sprintf('ROUND(%s.value * cwd.rate, 4)', $tableAlias) + ); + } + + /** + * Get connection + * + * return \Magento\Framework\DB\Adapter\AdapterInterface + * @throws \DomainException + */ + private function getConnection(): \Magento\Framework\DB\Adapter\AdapterInterface + { + if ($this->connection === null) { + $this->connection = $this->resource->getConnection($this->connectionName); + } + + return $this->connection; + } + + /** + * Get table + * + * @param string $tableName + * @return string + */ + private function getTable($tableName) + { + return $this->resource->getTableName($tableName, $this->connectionName); + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/JoinAttributeProcessor.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/JoinAttributeProcessor.php new file mode 100644 index 0000000000000..888e68a817081 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/JoinAttributeProcessor.php @@ -0,0 +1,112 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Query; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\DB\Select; +use Magento\Framework\DB\Sql\Expression; + +/** + * Allows to join product attribute to Select. Used for build price index for specified dimension + */ +class JoinAttributeProcessor +{ + /** + * @var \Magento\Eav\Model\Config + */ + private $eavConfig; + + /** + * @var \Magento\Framework\EntityManager\MetadataPool + */ + private $metadataPool; + + /** + * @var \Magento\Framework\App\ResourceConnection + */ + private $resource; + + /** + * @var string + */ + private $connectionName; + + /** + * @param \Magento\Eav\Model\Config $eavConfig + * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool + * @param \Magento\Framework\App\ResourceConnection $resource + * @param string $connectionName + */ + public function __construct( + \Magento\Eav\Model\Config $eavConfig, + \Magento\Framework\EntityManager\MetadataPool $metadataPool, + \Magento\Framework\App\ResourceConnection $resource, + $connectionName = 'indexer' + ) { + $this->eavConfig = $eavConfig; + $this->metadataPool = $metadataPool; + $this->resource = $resource; + $this->connectionName = $connectionName; + } + + /** + * @param Select $select + * @param string $attributeCode + * @param string|null $attributeValue + * @return \Zend_Db_Expr + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Zend_Db_Select_Exception + */ + public function process(Select $select, $attributeCode, $attributeValue = null): \Zend_Db_Expr + { + $attribute = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode); + $attributeId = $attribute->getAttributeId(); + $attributeTable = $attribute->getBackend()->getTable(); + $connection = $this->resource->getConnection($this->connectionName); + $joinType = $attributeValue !== null ? 'join' : 'joinLeft'; + $productIdField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + + if ($attribute->isScopeGlobal()) { + $alias = 'ta_' . $attributeCode; + $select->{$joinType}( + [$alias => $attributeTable], + "{$alias}.{$productIdField} = e.{$productIdField} AND {$alias}.attribute_id = {$attributeId}" . + " AND {$alias}.store_id = 0", + [] + ); + $whereExpression = new Expression("{$alias}.value"); + } else { + $dAlias = 'tad_' . $attributeCode; + $sAlias = 'tas_' . $attributeCode; + + $select->{$joinType}( + [$dAlias => $attributeTable], + "{$dAlias}.{$productIdField} = e.{$productIdField} AND {$dAlias}.attribute_id = {$attributeId}" . + " AND {$dAlias}.store_id = 0", + [] + ); + $select->joinLeft( + [$sAlias => $attributeTable], + "{$sAlias}.{$productIdField} = e.{$productIdField} AND {$sAlias}.attribute_id = {$attributeId}" . + " AND {$sAlias}.store_id = cwd.default_store_id", + [] + ); + $whereExpression = $connection->getCheckSql( + $connection->getIfNullSql("{$sAlias}.value_id", -1) . ' > 0', + "{$sAlias}.value", + "{$dAlias}.value" + ); + } + + if ($attributeValue !== null) { + $select->where("{$whereExpression} = ?", $attributeValue); + } + + return $whereExpression; + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/SimpleProductPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/SimpleProductPrice.php new file mode 100644 index 0000000000000..5a055e5ed9603 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/SimpleProductPrice.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); +namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Price; + +use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Query\BaseFinalPrice; +use Magento\Framework\Indexer\DimensionalIndexerInterface; + +/** + * Simple Product Type Price Indexer + */ +class SimpleProductPrice implements DimensionalIndexerInterface +{ + /** + * @var BaseFinalPrice + */ + private $baseFinalPrice; + + /** + * @var IndexTableStructureFactory + */ + private $indexTableStructureFactory; + + /** + * @var TableMaintainer + */ + private $tableMaintainer; + + /** + * @var string + */ + private $productType; + + /** + * @var BasePriceModifier + */ + private $basePriceModifier; + + /** + * @param BaseFinalPrice $baseFinalPrice + * @param IndexTableStructureFactory $indexTableStructureFactory + * @param TableMaintainer $tableMaintainer + * @param BasePriceModifier $basePriceModifier + * @param string $productType + */ + public function __construct( + BaseFinalPrice $baseFinalPrice, + IndexTableStructureFactory $indexTableStructureFactory, + TableMaintainer $tableMaintainer, + BasePriceModifier $basePriceModifier, + $productType = \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE + ) { + $this->baseFinalPrice = $baseFinalPrice; + $this->indexTableStructureFactory = $indexTableStructureFactory; + $this->tableMaintainer = $tableMaintainer; + $this->productType = $productType; + $this->basePriceModifier = $basePriceModifier; + } + + /** + * {@inheritdoc} + */ + public function executeByDimensions(array $dimensions, \Traversable $entityIds) + { + $this->tableMaintainer->createMainTmpTable($dimensions); + + $temporaryPriceTable = $this->indexTableStructureFactory->create([ + 'tableName' => $this->tableMaintainer->getMainTmpTable($dimensions), + 'entityField' => 'entity_id', + 'customerGroupField' => 'customer_group_id', + 'websiteField' => 'website_id', + 'taxClassField' => 'tax_class_id', + 'originalPriceField' => 'price', + 'finalPriceField' => 'final_price', + 'minPriceField' => 'min_price', + 'maxPriceField' => 'max_price', + 'tierPriceField' => 'tier_price', + ]); + $select = $this->baseFinalPrice->getQuery($dimensions, $this->productType, iterator_to_array($entityIds)); + $query = $select->insertFromSelect($temporaryPriceTable->getTableName(), [], false); + $this->tableMaintainer->getConnection()->query($query); + + $this->basePriceModifier->modifyPrice($temporaryPriceTable, iterator_to_array($entityIds)); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Plugin/WebsiteTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Plugin/WebsiteTest.php index d551822d975ea..f64789a2a3d82 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Plugin/WebsiteTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Plugin/WebsiteTest.php @@ -5,43 +5,179 @@ */ namespace Magento\Catalog\Test\Unit\Model\Indexer\Product\Price\Plugin; +use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; + class WebsiteTest extends \PHPUnit\Framework\TestCase { /** * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ - protected $_objectManager; + protected $objectManager; /** * @var \Magento\Catalog\Model\Indexer\Product\Price\Plugin\Website */ - protected $_model; + protected $model; + + /** + * @var \Magento\Framework\Indexer\DimensionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $dimensionFactory; + + /** + * @var \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer|\PHPUnit_Framework_MockObject_MockObject + */ + protected $tableMaintainer; /** - * @var \Magento\Catalog\Model\Indexer\Product\Price\Processor|\PHPUnit_Framework_MockObject_MockObject + * @var DimensionModeConfiguration|\PHPUnit_Framework_MockObject_MockObject */ - protected $_priceProcessorMock; + protected $dimensionModeConfiguration; protected function setUp() { - $this->_objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_priceProcessorMock = $this->createPartialMock( - \Magento\Catalog\Model\Indexer\Product\Price\Processor::class, - ['markIndexerAsInvalid'] + $this->dimensionFactory = $this->createPartialMock( + \Magento\Framework\Indexer\DimensionFactory::class, + ['create'] ); - $this->_model = $this->_objectManager->getObject( + $this->tableMaintainer = $this->createPartialMock( + \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer::class, + ['dropTablesForDimensions', 'createTablesForDimensions'] + ); + + $this->dimensionModeConfiguration = $this->createPartialMock( + DimensionModeConfiguration::class, + ['getDimensionConfiguration'] + ); + + $this->model = $this->objectManager->getObject( \Magento\Catalog\Model\Indexer\Product\Price\Plugin\Website::class, - ['processor' => $this->_priceProcessorMock] + [ + 'dimensionFactory' => $this->dimensionFactory, + 'tableMaintainer' => $this->tableMaintainer, + 'dimensionModeConfiguration' => $this->dimensionModeConfiguration, + ] ); } public function testAfterDelete() { - $this->_priceProcessorMock->expects($this->once())->method('markIndexerAsInvalid'); + $dimensionMock = $this->createMock(\Magento\Framework\Indexer\Dimension::class); + + $this->dimensionFactory->expects($this->once())->method('create')->willReturn( + $dimensionMock + ); + $this->tableMaintainer->expects($this->once())->method('dropTablesForDimensions')->with( + [$dimensionMock] + ); + + $this->dimensionModeConfiguration->expects($this->once())->method('getDimensionConfiguration')->willReturn( + [\Magento\Store\Model\Indexer\WebsiteDimensionProvider::DIMENSION_NAME] + ); + + $subjectMock = $this->createMock(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class); + $objectResourceMock = $this->createMock(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class); + $websiteMock = $this->createMock(\Magento\Framework\Model\AbstractModel::class); + $websiteMock->expects($this->once()) + ->method('getId') + ->willReturn(1); + + $this->assertEquals( + $objectResourceMock, + $this->model->afterDelete($subjectMock, $objectResourceMock, $websiteMock) + ); + } + + public function testAfterDeleteOnModeWithoutWebsiteDimension() + { + $dimensionMock = $this->createMock(\Magento\Framework\Indexer\Dimension::class); + + $this->dimensionFactory->expects($this->never())->method('create')->willReturn( + $dimensionMock + ); + $this->tableMaintainer->expects($this->never())->method('dropTablesForDimensions')->with( + [$dimensionMock] + ); - $websiteMock = $this->createMock(\Magento\Store\Model\ResourceModel\Website::class); - $this->assertEquals('return_value', $this->_model->afterDelete($websiteMock, 'return_value')); + $this->dimensionModeConfiguration->expects($this->once())->method('getDimensionConfiguration')->willReturn( + [] + ); + + $subjectMock = $this->createMock(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class); + $objectResourceMock = $this->createMock(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class); + $websiteMock = $this->createMock(\Magento\Framework\Model\AbstractModel::class); + $websiteMock->expects($this->once()) + ->method('getId') + ->willReturn(1); + + $this->assertEquals( + $objectResourceMock, + $this->model->afterDelete($subjectMock, $objectResourceMock, $websiteMock) + ); + } + + public function testAfterSave() + { + $dimensionMock = $this->createMock(\Magento\Framework\Indexer\Dimension::class); + + $this->dimensionFactory->expects($this->once())->method('create')->willReturn( + $dimensionMock + ); + $this->tableMaintainer->expects($this->once())->method('createTablesForDimensions')->with( + [$dimensionMock] + ); + + $this->dimensionModeConfiguration->expects($this->once())->method('getDimensionConfiguration')->willReturn( + [\Magento\Store\Model\Indexer\WebsiteDimensionProvider::DIMENSION_NAME] + ); + + $subjectMock = $this->createMock(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class); + $objectResourceMock = $this->createMock(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class); + $websiteMock = $this->createMock(\Magento\Framework\Model\AbstractModel::class); + $websiteMock->expects($this->once()) + ->method('getId') + ->willReturn(1); + $websiteMock->expects($this->once()) + ->method('isObjectNew') + ->willReturn(true); + + $this->assertEquals( + $objectResourceMock, + $this->model->afterSave($subjectMock, $objectResourceMock, $websiteMock) + ); + } + + public function testAfterSaveOnModeWithoutWebsiteDimension() + { + $dimensionMock = $this->createMock(\Magento\Framework\Indexer\Dimension::class); + + $this->dimensionFactory->expects($this->never())->method('create')->willReturn( + $dimensionMock + ); + $this->tableMaintainer->expects($this->never())->method('createTablesForDimensions')->with( + [$dimensionMock] + ); + + $this->dimensionModeConfiguration->expects($this->once())->method('getDimensionConfiguration')->willReturn( + [] + ); + + $subjectMock = $this->createMock(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class); + $objectResourceMock = $this->createMock(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class); + $websiteMock = $this->createMock(\Magento\Framework\Model\AbstractModel::class); + $websiteMock->expects($this->once()) + ->method('getId') + ->willReturn(1); + $websiteMock->expects($this->once()) + ->method('isObjectNew') + ->willReturn(true); + + $this->assertEquals( + $objectResourceMock, + $this->model->afterSave($subjectMock, $objectResourceMock, $websiteMock) + ); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Layer/Filter/PriceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Layer/Filter/PriceTest.php deleted file mode 100644 index 9fba7d833c25a..0000000000000 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Layer/Filter/PriceTest.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Catalog\Test\Unit\Model\ResourceModel\Layer\Filter; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -class PriceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Catalog\Model\ResourceModel\Layer\Filter\Price - */ - private $model; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $resourceMock; - - protected function setUp() - { - $objectManagerHelper = new ObjectManager($this); - - $contextMock = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\Db\Context::class) - ->disableOriginalConstructor() - ->getMock(); - $this->resourceMock = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) - ->disableOriginalConstructor() - ->getMock(); - $contextMock->expects($this->once())->method('getResources')->willReturn($this->resourceMock); - $this->model = $objectManagerHelper->getObject( - \Magento\Catalog\Model\ResourceModel\Layer\Filter\Price::class, - [ - 'context' => $contextMock - ] - ); - } - - public function testGetMainTable() - { - $expectedTableName = 'expectedTableName'; - $this->resourceMock->expects($this->once())->method('getTableName')->willReturn($expectedTableName); - $this->assertEquals($expectedTableName, $this->model->getMainTable()); - } -} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php index cec862ee9661f..6f3d8e1a84b17 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php @@ -7,6 +7,9 @@ use Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class LinkedProductSelectBuilderByIndexPriceTest extends \PHPUnit\Framework\TestCase { /** @@ -56,12 +59,26 @@ protected function setUp() $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); + + $this->indexScopeResolverMock = $this->createMock( + \Magento\Framework\Search\Request\IndexScopeResolverInterface::class + ); + $this->dimensionMock = $this->createMock(\Magento\Framework\Indexer\Dimension::class); + $this->dimensionFactoryMock = $this->createMock(\Magento\Framework\Indexer\DimensionFactory::class); + $this->dimensionFactoryMock->method('create')->willReturn($this->dimensionMock); + $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); + $storeMock->method('getId')->willReturn(1); + $storeMock->method('getWebsiteId')->willReturn(1); + $this->storeManagerMock->method('getStore')->willReturn($storeMock); + $this->model = new \Magento\Catalog\Model\ResourceModel\Product\Indexer\LinkedProductSelectBuilderByIndexPrice( $this->storeManagerMock, $this->resourceMock, $this->customerSessionMock, $this->metadataPoolMock, - $this->baseSelectProcessorMock + $this->baseSelectProcessorMock, + $this->indexScopeResolverMock, + $this->dimensionFactoryMock ); } @@ -79,7 +96,7 @@ public function testBuild() $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) ->getMockForAbstractClass(); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($storeMock); - $this->customerSessionMock->expects($this->once())->method('getCustomerGroupId'); + $this->customerSessionMock->expects($this->once())->method('getCustomerGroupId')->willReturn(1); $connection->expects($this->any())->method('select')->willReturn($select); $select->expects($this->any())->method('from')->willReturnSelf(); $select->expects($this->any())->method('joinInner')->willReturnSelf(); diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index c9e18e7b8c823..44564da388fc9 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -239,7 +239,8 @@ </arguments> </type> <type name="Magento\Store\Model\ResourceModel\Website"> - <plugin name="priceIndexerOnWebsiteDelete" type="Magento\Catalog\Model\Indexer\Product\Price\Plugin\Website"/> + <plugin name="invalidatePriceIndexerOnWebsite" type="Magento\Catalog\Model\Indexer\Product\Price\Plugin\Website"/> + <plugin name="categoryProductWebsiteAfterDelete" type="\Magento\Catalog\Model\Indexer\Category\Product\Plugin\Website"/> </type> <type name="Magento\Store\Model\ResourceModel\Store"> <plugin name="storeViewResourceAroundSave" type="Magento\Catalog\Model\Indexer\Category\Flat\Plugin\StoreView"/> @@ -562,6 +563,7 @@ <arguments> <argument name="commands" xsi:type="array"> <item name="productAttributesCleanUp" xsi:type="object">Magento\Catalog\Console\Command\ProductAttributesCleanUp</item> + <item name="setPriceDimensionsMode" xsi:type="object">Magento\Catalog\Console\Command\PriceIndexerDimensionsModeSetCommand</item> </argument> </arguments> </type> @@ -923,6 +925,7 @@ <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\LinkedProductSelectBuilderByIndexPrice"> <arguments> <argument name="baseSelectProcessor" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\CompositeBaseSelectProcessor</argument> + <argument name="priceTableResolver" xsi:type="object">Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver</argument> </arguments> </type> <type name="Magento\Catalog\Model\Product\Price\CostStorage"> @@ -1088,4 +1091,55 @@ <argument name="nativeAttributeConditionBuilder" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\ConditionBuilder\NativeAttributeCondition</argument> </arguments> </type> + <type name="Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory"> + <arguments> + <argument name="dimensionProviders" xsi:type="array"> + <!-- @see \Magento\Store\Model\Indexer\WebsiteDimensionProvider::DIMENSION_NAME --> + <item name="ws" xsi:type="object">Magento\Store\Model\Indexer\WebsiteDimensionProvider</item> + <!-- @see \Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider::DIMENSION_NAME --> + <item name="cg" xsi:type="object">Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider</item> + </argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Indexer\Product\Price\Plugin\TableResolver"> + <arguments> + <argument name="priceTableResolver" xsi:type="object">Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver\Proxy</argument> + <argument name="storeManager" xsi:type="object">Magento\Store\Model\StoreManagerInterface\Proxy</argument> + <argument name="context" xsi:type="object">Magento\Framework\App\Http\Context\Proxy</argument> + <!-- Unccomment after fix issue with Proxy generation --> + <!--<argument name="dimensionModeConfiguration" xsi:type="object">--> + <!--Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration\Proxy--> + <!--</argument>--> + </arguments> + </type> + <type name="Magento\Catalog\Model\ResourceModel\Layer\Filter\Price"> + <arguments> + <argument name="priceTableResolver" xsi:type="object"> + Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver + </argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\CustomOptionPriceModifier"> + <arguments> + <argument name="tableStrategy" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\TemporaryTableStrategy</argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer"> + <arguments> + <argument name="connectionName" xsi:type="string">indexer</argument> + <argument name="tableResolver" xsi:type="object">Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver</argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier"> + <arguments> + <argument name="priceModifiers" xsi:type="array"> + <item name="customOptionPriceModifier" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\CustomOptionPriceModifier</item> + </argument> + </arguments> + </type> + <virtualType name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\VirtualProductPrice" type="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\SimpleProductPrice"> + <arguments> + <argument name="productType" xsi:type="string">virtual</argument> + </arguments> + </virtualType> </config> diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index 659ba2b731366..e4f22e4a9ad1f 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -102,5 +102,6 @@ </type> <type name="Magento\Framework\App\ResourceConnection"> <plugin name="get_catalog_category_product_index_table_name" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\TableResolver"/> + <plugin name="get_catalog_product_price_index_table_name" type="Magento\Catalog\Model\Indexer\Product\Price\Plugin\TableResolver"/> </type> </config> diff --git a/app/code/Magento/Catalog/etc/product_types.xml b/app/code/Magento/Catalog/etc/product_types.xml index fe4922ab8fa1f..fdcb67ae484d2 100644 --- a/app/code/Magento/Catalog/etc/product_types.xml +++ b/app/code/Magento/Catalog/etc/product_types.xml @@ -7,11 +7,13 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/product_types.xsd"> <type name="simple" label="Simple Product" modelInstance="Magento\Catalog\Model\Product\Type\Simple" indexPriority="10" sortOrder="10"> + <indexerModel instance="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\SimpleProductPrice" /> <customAttributes> <attribute name="refundable" value="true"/> </customAttributes> </type> <type name="virtual" label="Virtual Product" modelInstance="Magento\Catalog\Model\Product\Type\Virtual" indexPriority="20" sortOrder="40"> + <indexerModel instance="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\VirtualProductPrice" /> <customAttributes> <attribute name="is_real_product" value="false"/> <attribute name="refundable" value="false"/> diff --git a/app/code/Magento/Catalog/etc/webapi_rest/di.xml b/app/code/Magento/Catalog/etc/webapi_rest/di.xml index a0d3e850b3c64..2a5d60222e9f8 100644 --- a/app/code/Magento/Catalog/etc/webapi_rest/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_rest/di.xml @@ -17,5 +17,6 @@ </type> <type name="Magento\Framework\App\ResourceConnection"> <plugin name="get_catalog_category_product_index_table_name" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\TableResolver"/> + <plugin name="get_catalog_product_price_index_table_name" type="Magento\Catalog\Model\Indexer\Product\Price\Plugin\TableResolver"/> </type> </config> diff --git a/app/code/Magento/Catalog/etc/webapi_soap/di.xml b/app/code/Magento/Catalog/etc/webapi_soap/di.xml index a0d3e850b3c64..2a5d60222e9f8 100644 --- a/app/code/Magento/Catalog/etc/webapi_soap/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_soap/di.xml @@ -17,5 +17,6 @@ </type> <type name="Magento\Framework\App\ResourceConnection"> <plugin name="get_catalog_category_product_index_table_name" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\TableResolver"/> + <plugin name="get_catalog_product_price_index_table_name" type="Magento\Catalog\Model\Indexer\Product\Price\Plugin\TableResolver"/> </type> </config> diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml index 65bc277121429..7cddb4f8f0b54 100644 --- a/app/code/Magento/CatalogInventory/etc/di.xml +++ b/app/code/Magento/CatalogInventory/etc/di.xml @@ -118,4 +118,22 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceInterface"> + <arguments> + <argument name="priceModifiers" xsi:type="array"> + <item name="inventoryProductPriceIndexFilter" xsi:type="object">Magento\CatalogInventory\Model\Indexer\ProductPriceIndexFilter</item> + </argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier"> + <arguments> + <argument name="priceModifiers" xsi:type="array"> + <item name="inventoryProductPriceIndexFilter" xsi:type="object">Magento\CatalogInventory\Model\Indexer\ProductPriceIndexFilter</item> + </argument> + </arguments> + </type> + + <type name="Magento\CatalogInventory\Model\ResourceModel\Stock\Item"> + <plugin name="priceIndexUpdater" type="Magento\CatalogInventory\Model\Plugin\PriceIndexUpdater" /> + </type> </config> diff --git a/app/code/Magento/CatalogRule/etc/di.xml b/app/code/Magento/CatalogRule/etc/di.xml index 8ed88dd4f3fdb..e0d91db542390 100644 --- a/app/code/Magento/CatalogRule/etc/di.xml +++ b/app/code/Magento/CatalogRule/etc/di.xml @@ -126,6 +126,21 @@ </argument> </arguments> </type> + <preference for="Magento\CatalogRule\Model\Indexer\IndexerTableSwapperInterface" type="Magento\CatalogRule\Model\Indexer\IndexerTableSwapper" /> + <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceInterface"> + <arguments> + <argument name="priceModifiers" xsi:type="array"> + <item name="catalogRulePriceModifier" xsi:type="object">Magento\CatalogRule\Model\Indexer\ProductPriceIndexModifier</item> + </argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier"> + <arguments> + <argument name="priceModifiers" xsi:type="array"> + <item name="catalogRulePriceModifier" xsi:type="object">Magento\CatalogRule\Model\Indexer\ProductPriceIndexModifier</item> + </argument> + </arguments> + </type> <virtualType name="CatalogRuleCustomConditionProvider" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\CustomConditionProvider"> <arguments> <argument name="customConditionProcessors" xsi:type="array"> @@ -149,12 +164,4 @@ <argument name="customConditionProvider" xsi:type="object">CatalogRuleCustomConditionProvider</argument> </arguments> </type> - <preference for="Magento\CatalogRule\Model\Indexer\IndexerTableSwapperInterface" type="Magento\CatalogRule\Model\Indexer\IndexerTableSwapper" /> - <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceInterface"> - <arguments> - <argument name="priceModifiers" xsi:type="array"> - <item name="catalogRulePriceModifier" xsi:type="object">Magento\CatalogRule\Model\Indexer\ProductPriceIndexModifier</item> - </argument> - </arguments> - </type> </config> diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilder.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilder.php index ca077ef7227d5..7ebf71f424439 100644 --- a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilder.php +++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilder.php @@ -8,18 +8,29 @@ use Magento\CatalogInventory\Model\Configuration as CatalogInventoryConfiguration; use Magento\CatalogInventory\Model\Stock; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Framework\App\ResourceConnection; use Magento\Framework\App\ScopeResolverInterface; -use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Select; use Magento\Framework\Search\Request\BucketInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Indexer\DimensionFactory; +use Magento\Framework\Search\Request\IndexScopeResolverInterface; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; /** - * Attribute query builder + * Attribute query builder + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class QueryBuilder { + /** + * @var DimensionFactory + */ + private $dimensionFactory; + /** * @var Resource */ @@ -35,19 +46,31 @@ class QueryBuilder */ private $inventoryConfig; + /** + * @var IndexScopeResolverInterface + */ + private $priceTableResolver; + /** * @param ResourceConnection $resource * @param ScopeResolverInterface $scopeResolver * @param CatalogInventoryConfiguration $inventoryConfig + * @param IndexScopeResolverInterface $priceTableResolver + * @param DimensionFactory|null $dimensionFactory */ public function __construct( ResourceConnection $resource, ScopeResolverInterface $scopeResolver, - CatalogInventoryConfiguration $inventoryConfig + CatalogInventoryConfiguration $inventoryConfig, + IndexScopeResolverInterface $priceTableResolver = null, + DimensionFactory $dimensionFactory = null ) { $this->resource = $resource; $this->scopeResolver = $scopeResolver; $this->inventoryConfig = $inventoryConfig; + $this->priceTableResolver = $priceTableResolver + ?: ObjectManager::getInstance()->get(IndexScopeResolverInterface::class); + $this->dimensionFactory = $dimensionFactory ?: ObjectManager::getInstance()->get(DimensionFactory::class); } /** @@ -99,12 +122,25 @@ private function buildQueryForPriceAttribute( if (!$store instanceof \Magento\Store\Model\Store) { throw new \RuntimeException('Illegal scope resolved'); } + $websiteId = $store->getWebsiteId(); - $table = $this->resource->getTableName('catalog_product_index_price'); - $select->from(['main_table' => $table], null) + $tableName = $this->priceTableResolver->resolve( + 'catalog_product_index_price', + [ + $this->dimensionFactory->create( + WebsiteDimensionProvider::DIMENSION_NAME, + (string)$websiteId + ), + $this->dimensionFactory->create( + CustomerGroupDimensionProvider::DIMENSION_NAME, + (string)$customerGroupId + ), + ] + ); + $select->from(['main_table' => $tableName], null) ->columns([BucketInterface::FIELD_VALUE => 'main_table.min_price']) ->where('main_table.customer_group_id = ?', $customerGroupId) - ->where('main_table.website_id = ?', $store->getWebsiteId()); + ->where('main_table.website_id = ?', $websiteId); return $select; } diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Dynamic/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Dynamic/DataProvider.php index 8b7a7ed214e36..d2e653ac9b9ae 100644 --- a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Dynamic/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Dynamic/DataProvider.php @@ -6,17 +6,21 @@ namespace Magento\CatalogSearch\Model\Adapter\Mysql\Dynamic; use Magento\Catalog\Model\Layer\Filter\Price\Range; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; use Magento\Customer\Model\Session; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Ddl\Table; use Magento\Framework\DB\Select; +use Magento\Framework\Indexer\DimensionFactory; use Magento\Framework\Search\Adapter\Mysql\Aggregation\DataProviderInterface as MysqlDataProviderInterface; use Magento\Framework\Search\Dynamic\DataProviderInterface; use Magento\Framework\Search\Dynamic\IntervalFactory; use Magento\Framework\Search\Request\BucketInterface; use Magento\Framework\App\ObjectManager; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; use Magento\Store\Model\StoreManager; +use \Magento\Framework\Search\Request\IndexScopeResolverInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -58,6 +62,16 @@ class DataProvider implements DataProviderInterface */ private $storeManager; + /** + * @var IndexScopeResolverInterface + */ + private $priceTableResolver; + + /** + * @var DimensionFactory|null + */ + private $dimensionFactory; + /** * @param ResourceConnection $resource * @param Range $range @@ -65,6 +79,8 @@ class DataProvider implements DataProviderInterface * @param MysqlDataProviderInterface $dataProvider * @param IntervalFactory $intervalFactory * @param StoreManager $storeManager + * @param IndexScopeResolverInterface|null $priceTableResolver + * @param DimensionFactory|null $dimensionFactory */ public function __construct( ResourceConnection $resource, @@ -72,7 +88,9 @@ public function __construct( Session $customerSession, MysqlDataProviderInterface $dataProvider, IntervalFactory $intervalFactory, - StoreManager $storeManager = null + StoreManager $storeManager = null, + IndexScopeResolverInterface $priceTableResolver = null, + DimensionFactory $dimensionFactory = null ) { $this->resource = $resource; $this->connection = $resource->getConnection(); @@ -81,6 +99,10 @@ public function __construct( $this->dataProvider = $dataProvider; $this->intervalFactory = $intervalFactory; $this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManager::class); + $this->priceTableResolver = $priceTableResolver ?: ObjectManager::getInstance()->get( + IndexScopeResolverInterface::class + ); + $this->dimensionFactory = $dimensionFactory ?: ObjectManager::getInstance()->get(DimensionFactory::class); } /** @@ -104,16 +126,30 @@ public function getAggregations(\Magento\Framework\Search\Dynamic\EntityStorage ]; $select = $this->getSelect(); - - $tableName = $this->resource->getTableName('catalog_product_index_price'); + $websiteId = $this->storeManager->getStore()->getWebsiteId(); + $customerGroupId = $this->customerSession->getCustomerGroupId(); + + $tableName = $this->priceTableResolver->resolve( + 'catalog_product_index_price', + [ + $this->dimensionFactory->create( + WebsiteDimensionProvider::DIMENSION_NAME, + (string)$websiteId + ), + $this->dimensionFactory->create( + CustomerGroupDimensionProvider::DIMENSION_NAME, + (string)$customerGroupId + ), + ] + ); /** @var Table $table */ $table = $entityStorage->getSource(); $select->from(['main_table' => $tableName], []) ->where('main_table.entity_id in (select entity_id from ' . $table->getName() . ')') ->columns($aggregation); - $select = $this->setCustomerGroupId($select); - $select->where('main_table.website_id = ?', $this->storeManager->getStore()->getWebsiteId()); + $select->where('customer_group_id = ?', $customerGroupId); + $select->where('main_table.website_id = ?', $websiteId); return $this->connection->fetchRow($select); } @@ -192,13 +228,4 @@ private function getSelect() { return $this->connection->select(); } - - /** - * @param Select $select - * @return Select - */ - private function setCustomerGroupId($select) - { - return $select->where('customer_group_id = ?', $this->customerSession->getCustomerGroupId()); - } } diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Plugin/Aggregation/Category/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Plugin/Aggregation/Category/DataProvider.php index 182ecf873d77a..94432bbfe4a71 100644 --- a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Plugin/Aggregation/Category/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Plugin/Aggregation/Category/DataProvider.php @@ -13,7 +13,7 @@ use Magento\Framework\Search\Request\BucketInterface; use Magento\Framework\Search\Request\Dimension; use Magento\Framework\App\ObjectManager; -use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver as TableResolver; +use Magento\Framework\Search\Request\IndexScopeResolverInterface as TableResolver; use Magento\Catalog\Model\Indexer\Category\Product\AbstractAction; /** diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php index df9eb6e78990a..6df6403ff51ec 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php @@ -8,7 +8,7 @@ use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\FullFactory; use Magento\CatalogSearch\Model\Indexer\Scope\StateFactory; use Magento\CatalogSearch\Model\ResourceModel\Fulltext as FulltextResource; -use Magento\Framework\Indexer\Dimension\DimensionProviderInterface; +use Magento\Framework\Indexer\DimensionProviderInterface; use Magento\Store\Model\StoreDimensionProvider; use Magento\Indexer\Model\ProcessManager; @@ -23,7 +23,7 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface, - \Magento\Framework\Indexer\Dimension\DimensionalIndexerInterface + \Magento\Framework\Indexer\DimensionalIndexerInterface { /** * Indexer ID in configuration diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/ExclusionStrategy.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/ExclusionStrategy.php index 66e0457e7fadd..512dd69aad952 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/ExclusionStrategy.php +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/ExclusionStrategy.php @@ -7,13 +7,21 @@ namespace Magento\CatalogSearch\Model\Search\FilterMapper; use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Framework\App\Http\Context; use Magento\Framework\App\ObjectManager; -use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver as TableResolver; +use Magento\Framework\Indexer\DimensionFactory; +use Magento\Framework\Search\Request\IndexScopeResolverInterface as TableResolver; use Magento\Framework\Search\Request\Dimension; use Magento\Catalog\Model\Indexer\Category\Product\AbstractAction; +use Magento\Customer\Model\Context as CustomerContext; +use Magento\Framework\Search\Request\IndexScopeResolverInterface; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; /** * Strategy which processes exclusions from general rules + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ExclusionStrategy implements FilterStrategyInterface { @@ -43,22 +51,48 @@ class ExclusionStrategy implements FilterStrategyInterface */ private $tableResolver; + /** + * @var IndexScopeResolverInterface + */ + private $priceTableResolver; + + /** + * @var DimensionFactory + */ + private $dimensionFactory; + + /** + * @var Context + */ + private $httpContext; + /** * @param \Magento\Framework\App\ResourceConnection $resourceConnection * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param AliasResolver $aliasResolver * @param TableResolver|null $tableResolver + * @param DimensionFactory $dimensionFactory + * @param IndexScopeResolverInterface $priceTableResolver + * @param Context $httpContext */ public function __construct( \Magento\Framework\App\ResourceConnection $resourceConnection, \Magento\Store\Model\StoreManagerInterface $storeManager, AliasResolver $aliasResolver, - TableResolver $tableResolver = null + TableResolver $tableResolver = null, + DimensionFactory $dimensionFactory = null, + IndexScopeResolverInterface $priceTableResolver = null, + Context $httpContext = null ) { $this->resourceConnection = $resourceConnection; $this->storeManager = $storeManager; $this->aliasResolver = $aliasResolver; $this->tableResolver = $tableResolver ?: ObjectManager::getInstance()->get(TableResolver::class); + $this->dimensionFactory = $dimensionFactory ?: ObjectManager::getInstance()->get(DimensionFactory::class); + $this->priceTableResolver = $priceTableResolver ?: ObjectManager::getInstance()->get( + IndexScopeResolverInterface::class + ); + $this->httpContext = $httpContext ?: ObjectManager::getInstance()->get(Context::class); } /** @@ -93,7 +127,17 @@ private function applyPriceFilter( \Magento\Framework\DB\Select $select ) { $alias = $this->aliasResolver->getAlias($filter); - $tableName = $this->resourceConnection->getTableName('catalog_product_index_price'); + $websiteId = $this->storeManager->getWebsite()->getId(); + $tableName = $this->priceTableResolver->resolve( + 'catalog_product_index_price', + [ + $this->dimensionFactory->create(WebsiteDimensionProvider::DIMENSION_NAME, (string)$websiteId), + $this->dimensionFactory->create( + CustomerGroupDimensionProvider::DIMENSION_NAME, + (string)$this->httpContext->getValue(CustomerContext::CONTEXT_GROUP) + ) + ] + ); $mainTableAlias = $this->extractTableAliasFromSelect($select); $select->joinInner( @@ -102,7 +146,7 @@ private function applyPriceFilter( ], $this->resourceConnection->getConnection()->quoteInto( sprintf('%s.entity_id = price_index.entity_id AND price_index.website_id = ?', $mainTableAlias), - $this->storeManager->getWebsite()->getId() + $websiteId ), [] ); diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilderTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilderTest.php index b52664df749fe..72379c3819dea 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilderTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilderTest.php @@ -17,7 +17,9 @@ use Magento\Store\Model\Store; /** - * Test for Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation\DataProvider\QueryBuilder. + * Test for Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation\DataProvider\QueryBuilder. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class QueryBuilderTest extends \PHPUnit\Framework\TestCase { @@ -57,10 +59,25 @@ protected function setUp() ->method('getConnection') ->willReturn($this->adapterMock); + $this->indexScopeResolverMock = $this->createMock( + \Magento\Framework\Search\Request\IndexScopeResolverInterface::class + ); + $this->dimensionMock = $this->createMock(\Magento\Framework\Indexer\Dimension::class); + $this->dimensionFactoryMock = $this->createMock(\Magento\Framework\Indexer\DimensionFactory::class); + $this->dimensionFactoryMock->method('create')->willReturn($this->dimensionMock); + $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); + $storeMock->method('getId')->willReturn(1); + $storeMock->method('getWebsiteId')->willReturn(1); + $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->storeManagerMock->method('getStore')->willReturn($storeMock); + $this->indexScopeResolverMock->method('resolve')->willReturn('catalog_product_index_price'); + $this->model = new QueryBuilder( $this->resourceConnectionMock, $this->scopeResolverMock, - $this->inventoryConfigMock + $this->inventoryConfigMock, + $this->indexScopeResolverMock, + $this->dimensionFactoryMock ); } @@ -81,8 +98,6 @@ public function testBuildWithPriceAttributeCode() $this->scopeResolverMock->expects($this->once())->method('getScope') ->with($scope)->willReturn($storeMock); $storeMock->expects($this->once())->method('getWebsiteId')->willReturn(1); - $this->resourceConnectionMock->expects($this->once())->method('getTableName') - ->with('catalog_product_index_price')->willReturn('catalog_product_index_price'); $selectMock->expects($this->once())->method('from') ->with(['main_table' => 'catalog_product_index_price'], null) ->willReturn($selectMock); diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Dynamic/DataProviderTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Dynamic/DataProviderTest.php index 1aeeb0d9bd731..1186dd6936cc6 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Dynamic/DataProviderTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Dynamic/DataProviderTest.php @@ -74,6 +74,18 @@ protected function setUp() $this->mysqlDataProviderMock = $this->createMock(DataProviderInterface::class); $this->intervalFactoryMock = $this->createMock(IntervalFactory::class); $this->storeManagerMock = $this->createMock(StoreManager::class); + $this->indexScopeResolverMock = $this->createMock( + \Magento\Framework\Search\Request\IndexScopeResolverInterface::class + ); + $this->dimensionMock = $this->createMock(\Magento\Framework\Indexer\Dimension::class); + $this->dimensionFactoryMock = $this->createMock(\Magento\Framework\Indexer\DimensionFactory::class); + $this->dimensionFactoryMock->method('create')->willReturn($this->dimensionMock); + $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); + $storeMock->method('getId')->willReturn(1); + $storeMock->method('getWebsiteId')->willReturn(1); + $this->storeManagerMock->method('getStore')->willReturn($storeMock); + $this->indexScopeResolverMock->method('resolve')->willReturn('catalog_product_index_price'); + $this->sessionMock->method('getCustomerGroupId')->willReturn(1); $this->model = new DataProvider( $this->resourceConnectionMock, @@ -81,7 +93,9 @@ protected function setUp() $this->sessionMock, $this->mysqlDataProviderMock, $this->intervalFactoryMock, - $this->storeManagerMock + $this->storeManagerMock, + $this->indexScopeResolverMock, + $this->dimensionFactoryMock ); } @@ -97,10 +111,6 @@ public function testGetAggregationsUsesFrontendPriceIndexerTable() $entityStorageMock = $this->createMock(EntityStorage::class); $entityStorageMock->expects($this->any())->method('getSource')->willReturn($tableMock); - $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); - $storeMock->expects($this->once())->method('getWebsiteId')->willReturn(42); - $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($storeMock); - $this->model->getAggregations($entityStorageMock); } } diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php index 595e32b4c1988..d7129b9c224fc 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php @@ -6,7 +6,7 @@ namespace Magento\CatalogSearch\Test\Unit\Model\Indexer; use \Magento\Framework\Indexer\Dimension; -use Magento\Framework\Indexer\Dimension\DimensionProviderInterface; +use Magento\Framework\Indexer\DimensionProviderInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/FilterMapper/ExclusionStrategyTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/FilterMapper/ExclusionStrategyTest.php index 7c6cafd7e9924..09591532f9f06 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/FilterMapper/ExclusionStrategyTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/FilterMapper/ExclusionStrategyTest.php @@ -15,6 +15,9 @@ use Magento\Framework\Search\Request\Filter\Term; use Magento\Store\Api\Data\WebsiteInterface; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class ExclusionStrategyTest extends \PHPUnit\Framework\TestCase { /** @@ -50,10 +53,31 @@ protected function setUp() $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); $this->aliasResolverMock = $this->createMock(AliasResolver::class); + $this->indexScopeResolverMock = $this->createMock( + \Magento\Framework\Search\Request\IndexScopeResolverInterface::class + ); + $this->tableResolverMock = $this->createMock( + \Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver::class + ); + $this->dimensionMock = $this->createMock(\Magento\Framework\Indexer\Dimension::class); + $this->dimensionFactoryMock = $this->createMock(\Magento\Framework\Indexer\DimensionFactory::class); + $this->dimensionFactoryMock->method('create')->willReturn($this->dimensionMock); + $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); + $storeMock->method('getId')->willReturn(1); + $storeMock->method('getWebsiteId')->willReturn(1); + $this->storeManagerMock->method('getStore')->willReturn($storeMock); + $this->indexScopeResolverMock->method('resolve')->willReturn('catalog_product_index_price'); + $this->httpContextMock = $this->createMock(\Magento\Framework\App\Http\Context::class); + $this->httpContextMock->method('getValue')->willReturn(1); + $this->model = new ExclusionStrategy( $this->resourceConnectionMock, $this->storeManagerMock, - $this->aliasResolverMock + $this->aliasResolverMock, + $this->tableResolverMock, + $this->dimensionFactoryMock, + $this->indexScopeResolverMock, + $this->httpContextMock ); } diff --git a/app/code/Magento/CatalogSearch/etc/di.xml b/app/code/Magento/CatalogSearch/etc/di.xml index 3d1c4470b1ae8..acec17f48211e 100644 --- a/app/code/Magento/CatalogSearch/etc/di.xml +++ b/app/code/Magento/CatalogSearch/etc/di.xml @@ -316,4 +316,25 @@ </argument> </arguments> </type> + <type name="Magento\CatalogSearch\Model\Search\FilterMapper\ExclusionStrategy"> + <arguments> + <argument name="priceTableResolver" xsi:type="object"> + Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver + </argument> + </arguments> + </type> + <type name="Magento\CatalogSearch\Model\Adapter\Mysql\Dynamic\DataProvider"> + <arguments> + <argument name="priceTableResolver" xsi:type="object"> + Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver + </argument> + </arguments> + </type> + <type name="Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation\DataProvider\QueryBuilder"> + <arguments> + <argument name="priceTableResolver" xsi:type="object"> + Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Customer/Model/Indexer/CustomerGroupDimensionProvider.php b/app/code/Magento/Customer/Model/Indexer/CustomerGroupDimensionProvider.php new file mode 100644 index 0000000000000..7c224d29f1dc1 --- /dev/null +++ b/app/code/Magento/Customer/Model/Indexer/CustomerGroupDimensionProvider.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Model\Indexer; + +use Magento\Customer\Model\ResourceModel\Group\CollectionFactory as CustomerGroupCollectionFactory; +use Magento\Framework\Indexer\DimensionFactory; +use Magento\Framework\Indexer\DimensionProviderInterface; + +class CustomerGroupDimensionProvider implements DimensionProviderInterface +{ + /** + * Name for customer group dimension for multidimensional indexer + * 'cg' - stands for 'customer_group' + */ + const DIMENSION_NAME = 'cg'; + + /** + * @var CustomerGroupCollectionFactory + */ + private $collectionFactory; + + /** + * @var \SplFixedArray + */ + private $customerGroupsDataIterator; + + /** + * @var DimensionFactory + */ + private $dimensionFactory; + + public function __construct(CustomerGroupCollectionFactory $collectionFactory, DimensionFactory $dimensionFactory) + { + $this->dimensionFactory = $dimensionFactory; + $this->collectionFactory = $collectionFactory; + } + + public function getIterator(): \Traversable + { + foreach ($this->getCustomerGroups() as $customerGroup) { + yield $this->dimensionFactory->create(self::DIMENSION_NAME, (string)$customerGroup); + } + } + + /** + * @return array + */ + private function getCustomerGroups(): array + { + if ($this->customerGroupsDataIterator === null) { + $customerGroups = $this->collectionFactory->create()->getAllIds(); + $this->customerGroupsDataIterator = is_array($customerGroups) ? $customerGroups : []; + } + + return $this->customerGroupsDataIterator; + } +} diff --git a/app/code/Magento/Customer/etc/di.xml b/app/code/Magento/Customer/etc/di.xml index 717b2b6c67e20..6e8c3dc68ed28 100644 --- a/app/code/Magento/Customer/etc/di.xml +++ b/app/code/Magento/Customer/etc/di.xml @@ -75,6 +75,13 @@ <argument name="addressConfig" xsi:type="object">Magento\Customer\Model\Address\Config\Proxy</argument> </arguments> </type> + <type name="Magento\Framework\App\Http\Context"> + <arguments> + <argument name="default" xsi:type="array"> + <item name="customer_group" xsi:type="const">Magento\Customer\Api\Data\GroupInterface::NOT_LOGGED_IN_ID</item> + </argument> + </arguments> + </type> <type name="Magento\Customer\Model\Config\Share"> <arguments> <argument name="customerResource" xsi:type="object">Magento\Customer\Model\ResourceModel\Customer\Proxy</argument> diff --git a/app/code/Magento/Elasticsearch/Model/ResourceModel/Index.php b/app/code/Magento/Elasticsearch/Model/ResourceModel/Index.php index 49489f1cdb43d..d75bb9c8ccd34 100644 --- a/app/code/Magento/Elasticsearch/Model/ResourceModel/Index.php +++ b/app/code/Magento/Elasticsearch/Model/ResourceModel/Index.php @@ -5,6 +5,7 @@ */ namespace Magento\Elasticsearch\Model\ResourceModel; +use Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory; use Magento\Framework\Model\ResourceModel\Db\Context; use Magento\Store\Model\StoreManagerInterface; use Magento\Catalog\Api\ProductRepositoryInterface; @@ -12,6 +13,7 @@ use Magento\Eav\Model\Config; use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Search\Request\IndexScopeResolverInterface as TableResolver; /** * Elasticsearch index resource model @@ -47,6 +49,8 @@ class Index extends \Magento\AdvancedSearch\Model\ResourceModel\Index * @param CategoryRepositoryInterface $categoryRepository * @param Config $eavConfig * @param null $connectionName + * @param TableResolver|null $tableResolver + * @param DimensionCollectionFactory|null $dimensionCollectionFactory * @SuppressWarnings(Magento.TypeDuplication) */ public function __construct( @@ -56,7 +60,9 @@ public function __construct( ProductRepositoryInterface $productRepository, CategoryRepositoryInterface $categoryRepository, Config $eavConfig, - $connectionName = null + $connectionName = null, + TableResolver $tableResolver = null, + DimensionCollectionFactory $dimensionCollectionFactory = null ) { $this->productRepository = $productRepository; $this->categoryRepository = $categoryRepository; @@ -65,7 +71,9 @@ public function __construct( $context, $storeManager, $metadataPool, - $connectionName + $connectionName, + $tableResolver, + $dimensionCollectionFactory ); } diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/ResourceModel/IndexTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/ResourceModel/IndexTest.php index c11fb64cde7e6..bac2b7ea28908 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/ResourceModel/IndexTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/ResourceModel/IndexTest.php @@ -239,6 +239,20 @@ protected function setUp() ] ); + $traversableMock = $this->createMock(\Traversable::class); + $dimensionsMock = $this->createMock(\Magento\Framework\Indexer\MultiDimensionProvider::class); + $dimensionsMock->method('getIterator')->willReturn($traversableMock); + + $indexScopeResolverMock = $this->createMock( + \Magento\Framework\Search\Request\IndexScopeResolverInterface::class + ); + + $dimensionFactoryMock = $this->createMock( + \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory::class + ); + $dimensionFactoryMock->method('create')->willReturn($dimensionsMock); + $indexScopeResolverMock->method('resolve')->willReturn('catalog_product_index_price'); + $this->model = $objectManager->getObject( \Magento\Elasticsearch\Model\ResourceModel\Index::class, [ @@ -249,7 +263,8 @@ protected function setUp() 'categoryRepository' => $this->categoryRepository, 'eavConfig' => $this->eavConfig, 'connectionName' => 'default', - 'tableResolver' => $this->tableResolver + 'tableResolver' => $this->tableResolver, + 'dimensionCollectionFactory' => $dimensionFactoryMock, ] ); } diff --git a/app/code/Magento/Store/Model/Indexer/WebsiteDimensionProvider.php b/app/code/Magento/Store/Model/Indexer/WebsiteDimensionProvider.php new file mode 100644 index 0000000000000..302c2f828367a --- /dev/null +++ b/app/code/Magento/Store/Model/Indexer/WebsiteDimensionProvider.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Model\Indexer; + +use Magento\Framework\Indexer\Dimension; +use Magento\Store\Model\ResourceModel\Website\CollectionFactory as WebsiteCollectionFactory; +use Magento\Framework\Indexer\DimensionFactory; +use Magento\Framework\Indexer\DimensionProviderInterface; +use Magento\Store\Model\Store; + +class WebsiteDimensionProvider implements DimensionProviderInterface +{ + /** + * Name for website dimension for multidimensional indexer + * 'ws' - stands for 'website_store' + */ + const DIMENSION_NAME = 'ws'; + + /** + * @var WebsiteCollectionFactory + */ + private $collectionFactory; + + /** + * @var \SplFixedArray + */ + private $websitesDataIterator; + + /** + * @var DimensionFactory + */ + private $dimensionFactory; + + /** + * @param WebsiteCollectionFactory $collectionFactory + * @param DimensionFactory $dimensionFactory + */ + public function __construct(WebsiteCollectionFactory $collectionFactory, DimensionFactory $dimensionFactory) + { + $this->dimensionFactory = $dimensionFactory; + $this->collectionFactory = $collectionFactory; + } + + /** + * @return Dimension[]|\Traversable + */ + public function getIterator(): \Traversable + { + foreach ($this->getWebsites() as $website) { + yield $this->dimensionFactory->create(self::DIMENSION_NAME, (string)$website); + } + } + + /** + * @return array + */ + private function getWebsites(): array + { + if ($this->websitesDataIterator === null) { + $websites = $this->collectionFactory->create() + ->addFieldToFilter('code', ['neq' => Store::ADMIN_CODE]) + ->getAllIds(); + $this->websitesDataIterator = is_array($websites) ? $websites : []; + } + + return $this->websitesDataIterator; + } +} diff --git a/app/code/Magento/Store/Model/StoreDimensionProvider.php b/app/code/Magento/Store/Model/StoreDimensionProvider.php index 5bc019ea6835f..90319a52d1362 100644 --- a/app/code/Magento/Store/Model/StoreDimensionProvider.php +++ b/app/code/Magento/Store/Model/StoreDimensionProvider.php @@ -8,7 +8,7 @@ namespace Magento\Store\Model; use Magento\Framework\Indexer\DimensionFactory; -use Magento\Framework\Indexer\Dimension\DimensionProviderInterface; +use Magento\Framework\Indexer\DimensionProviderInterface; /** * Provide a list of stores as Dimension diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml index 27133de270e2f..5f088558b41d9 100644 --- a/app/code/Magento/Store/etc/di.xml +++ b/app/code/Magento/Store/etc/di.xml @@ -25,6 +25,14 @@ <preference for="Magento\Framework\App\ScopeFallbackResolverInterface" type="Magento\Store\Model\ScopeFallbackResolver"/> <preference for="Magento\Framework\App\ScopeTreeProviderInterface" type="Magento\Store\Model\ScopeTreeProvider"/> <preference for="Magento\Framework\App\ScopeValidatorInterface" type="Magento\Store\Model\ScopeValidator"/> + <preference for="Magento\Store\Model\StoreSwitcherInterface" type="Magento\Store\Model\StoreSwitcher" /> + <type name="Magento\Framework\App\Http\Context"> + <arguments> + <argument name="default" xsi:type="array"> + <item name="website" xsi:type="string">0</item> + </argument> + </arguments> + </type> <type name="Magento\Framework\App\Response\Http"> <plugin name="genericHeaderPlugin" type="Magento\Framework\App\Response\HeaderManager"/> </type> @@ -417,4 +425,13 @@ </argument> </arguments> </type> + <type name="Magento\Store\Model\StoreSwitcher"> + <arguments> + <argument name="storeSwitchers" xsi:type="array"> + <item name="cleanTargetUrl" xsi:type="object">Magento\Store\Model\StoreSwitcher\CleanTargetUrl</item> + <item name="manageStoreCookie" xsi:type="object">Magento\Store\Model\StoreSwitcher\ManageStoreCookie</item> + <item name="managePrivateContent" xsi:type="object">Magento\Store\Model\StoreSwitcher\ManagePrivateContent</item> + </argument> + </arguments> + </type> </config> diff --git a/app/etc/di.xml b/app/etc/di.xml index 6a4a6d16b5568..5bc25e6cb85f7 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -206,6 +206,7 @@ <preference for="Magento\Framework\MessageQueue\QueueFactoryInterface" type="Magento\Framework\MessageQueue\QueueFactory" /> <preference for="Magento\Framework\App\Request\ValidatorInterface" type="Magento\Framework\App\Request\CsrfValidator" /> + <preference for="Magento\Framework\Search\Request\IndexScopeResolverInterface" type="Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver"/> <type name="Magento\Framework\Model\ResourceModel\Db\TransactionManager" shared="false" /> <type name="Magento\Framework\Acl\Data\Cache"> <arguments> diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRenderListInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRenderListInterfaceTest.php index 807269c03c06f..fb3ff3b134081 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRenderListInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRenderListInterfaceTest.php @@ -25,7 +25,6 @@ class ProductRenderListInterfaceTest extends WebapiAbstract * @magentoApiDataFixture Magento/Catalog/_files/product_special_price.php * @dataProvider productRenderInfoProvider * @param array $expectedRenderInfo - * @param string $ids * @return void */ public function testGetList(array $expectedRenderInfo) diff --git a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php index 1aea568395c99..481cc629c6777 100644 --- a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php +++ b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php @@ -7,7 +7,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; -class AdvancedPricingTest extends \PHPUnit\Framework\TestCase +class AdvancedPricingTest extends \Magento\TestFramework\Indexer\TestCase { /** * @var \Magento\AdvancedPricingImportExport\Model\Export\AdvancedPricing @@ -24,6 +24,19 @@ class AdvancedPricingTest extends \PHPUnit\Framework\TestCase */ protected $fileSystem; + public static function setUpBeforeClass() + { + $db = \Magento\TestFramework\Helper\Bootstrap::getInstance()->getBootstrap() + ->getApplication() + ->getDbInstance(); + if (!$db->isDbDumpExists()) { + throw new \LogicException('DB dump does not exist.'); + } + $db->restoreFromDbDump(); + + parent::setUpBeforeClass(); + } + protected function setUp() { parent::setUp(); @@ -37,7 +50,7 @@ protected function setUp() /** * @magentoAppArea adminhtml - * @magentoDbIsolation enabled + * @magentoDbIsolation disabled * @magentoAppIsolation enabled * @magentoDataFixture Magento/Catalog/_files/product_simple.php */ @@ -49,6 +62,7 @@ public function testExport() $index = 0; $ids = []; $origPricingData = []; + $skus = ['simple']; while (isset($skus[$index])) { $ids[$index] = $productRepository->get($skus[$index])->getId(); $origPricingData[$index] = $this->objectManager->create(\Magento\Catalog\Model\Product::class) @@ -94,7 +108,7 @@ private function assertDiscountTypes($exportContent) /** * @magentoAppArea adminhtml - * @magentoDbIsolation enabled + * @magentoDbIsolation disabled * @magentoAppIsolation enabled * @magentoConfigFixture current_store catalog/price/scope 1 * @magentoDataFixture Magento/AdvancedPricingImportExport/_files/product_with_second_website.php diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php index d59c402e22a71..a609e730de269 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php @@ -5,13 +5,14 @@ */ namespace Magento\Quote\Model\Quote; +use Magento\Store\Api\StoreRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; /** * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php */ -class AddressTest extends \PHPUnit\Framework\TestCase +class AddressTest extends \Magento\TestFramework\Indexer\TestCase { /** @var \Magento\Quote\Model\Quote $quote */ protected $_quote; @@ -25,6 +26,22 @@ class AddressTest extends \PHPUnit\Framework\TestCase /**@var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */ protected $customerRepository; + /** @var StoreRepositoryInterface */ + private $storeRepository; + + public static function setUpBeforeClass() + { + $db = \Magento\TestFramework\Helper\Bootstrap::getInstance()->getBootstrap() + ->getApplication() + ->getDbInstance(); + if (!$db->isDbDumpExists()) { + throw new \LogicException('DB dump does not exist.'); + } + $db->restoreFromDbDump(); + + parent::setUpBeforeClass(); + } + /** * Initialize quote and customer fixtures */ @@ -48,6 +65,8 @@ public function setUp() $this->_address->setId(1); $this->_address->load($this->_address->getId()); $this->_address->setQuote($this->_quote); + $this->storeRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(StoreRepositoryInterface::class); } protected function tearDown() @@ -309,4 +328,50 @@ public function dataProvider() [[123, true], [123, true]] ]; } + + /** + * Tests different shipping rates for different stores. + * + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php + * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php + * @magentoDataFixture Magento/Sales/_files/quote.php + * @magentoConfigFixture default_store carriers/flatrate/price 5 + * @magentoConfigFixture fixture_second_store_store carriers/flatrate/price 10 + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + * @dataProvider requestShippingRatesDataProvider + */ + public function testRequestShippingRates($storeCode, $expectedRate) + { + $store = $this->storeRepository->get($storeCode); + $this->_quote->setStoreId($store->getId()); + $this->_address->setItemQty(1); + $this->_address->requestShippingRates(); + /** + * @var \Magento\Quote\Model\ResourceModel\Quote\Address\Rate\Collection $shippingRatesCollection + */ + $shippingRatesCollection = $this->_address->getShippingRatesCollection(); + /** + * @var \Magento\Quote\Model\Quote\Address\Rate[] $shippingRates + */ + $shippingRates = $shippingRatesCollection->getItems(); + self::assertEquals( + $expectedRate, + $shippingRates[0]->getPrice() + ); + } + + /** + * Data provider for testRequestShippingRates. + * + * @return array + */ + public function requestShippingRatesDataProvider() + { + return [ + ['default', 5], + ['fixture_second_store', 10], + ]; + } } diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SubtotalTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SubtotalTest.php index b021040079538..ae1f475d43b71 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SubtotalTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SubtotalTest.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +// @codingStandardsIgnoreFile + namespace Magento\Tax\Model\Sales\Total\Quote; use Magento\TestFramework\Helper\Bootstrap; @@ -13,7 +15,7 @@ * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class SubtotalTest extends \PHPUnit\Framework\TestCase +class SubtotalTest extends \Magento\TestFramework\Indexer\TestCase { /** * Object Manager @@ -27,12 +29,23 @@ class SubtotalTest extends \PHPUnit\Framework\TestCase */ private $productRepository; + public static function setUpBeforeClass() + { + $db = \Magento\TestFramework\Helper\Bootstrap::getInstance()->getBootstrap() + ->getApplication() + ->getDbInstance(); + if (!$db->isDbDumpExists()) { + throw new \LogicException('DB dump does not exist.'); + } + $db->restoreFromDbDump(); + + parent::setUpBeforeClass(); + } + protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - $this->productRepository = $this->objectManager->create( - \Magento\Catalog\Api\ProductRepositoryInterface::class - ); + $this->productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); } protected function getCustomerById($id) @@ -46,6 +59,7 @@ protected function getCustomerById($id) /** * @magentoAppIsolation enabled + * @magentoDbIsolation enabled * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php * @magentoDataFixture Magento/Tax/_files/tax_classes.php @@ -61,8 +75,12 @@ public function testCollectUnitBased($expected) /** @var \Magento\Customer\Model\Customer $customer */ $customer = $this->objectManager->create(\Magento\Customer\Model\Customer::class)->load($fixtureCustomerId); /** @var \Magento\Customer\Model\Group $customerGroup */ - $customerGroup = $this->objectManager->create(\Magento\Customer\Model\Group::class) - ->load('custom_group', 'customer_group_code'); + $customerGroup = $this->objectManager->create( + \Magento\Customer\Model\Group::class + )->load( + 'custom_group', + 'customer_group_code' + ); $customerGroup->setTaxClassId($customerTaxClassId)->save(); $customer->setGroupId($customerGroup->getId())->save(); @@ -160,6 +178,7 @@ public function collectUnitBasedDataProvider() } /** + * @magentoDbIsolation disabled * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php * @magentoDataFixture Magento/Tax/_files/tax_classes.php @@ -177,7 +196,10 @@ public function testCollectUnitBasedBundleProduct($expected) /** @var \Magento\Customer\Model\Group $customerGroup */ $customerGroup = $this->objectManager->create( \Magento\Customer\Model\Group::class - )->load('custom_group', 'customer_group_code'); + )->load( + 'custom_group', + 'customer_group_code' + ); $customerGroup->setTaxClassId($customerTaxClassId)->save(); $customer->setGroupId($customerGroup->getId())->save(); diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php index 9b498afc2500d..0513dd1c7d3c4 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Tax\Model\Sales\Total\Quote; +use Magento\Quote\Model\Quote\TotalsCollector; use Magento\Tax\Model\Calculation; use Magento\TestFramework\Helper\Bootstrap; @@ -15,7 +16,7 @@ * Class TaxTest * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class TaxTest extends \PHPUnit\Framework\TestCase +class TaxTest extends \Magento\TestFramework\Indexer\TestCase { /** * Utility object for setting up tax rates, tax classes and tax rules @@ -24,6 +25,21 @@ class TaxTest extends \PHPUnit\Framework\TestCase */ protected $setupUtil = null; + /** + * @var TotalsCollector + */ + private $totalsCollector; + + public function setUp() + { + /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ + $objectManager = Bootstrap::getObjectManager(); + $this->totalsCollector = $objectManager->create(TotalsCollector::class); + $this->setupUtil = new SetupUtil($objectManager); + + parent::setUp(); + } + /** * Test taxes collection for quote. * @@ -227,25 +243,26 @@ protected function verifyResult($quoteAddress, $expectedResults) * @param array $configData * @param array $quoteData * @param array $expectedResults - * @magentoDbIsolation enabled + * @magentoDbIsolation disabled * @magentoAppIsolation enabled * @dataProvider taxDataProvider * @return void */ public function testTaxCalculation($configData, $quoteData, $expectedResults) { - /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ - $objectManager = Bootstrap::getObjectManager(); - /** @var \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector */ - $totalsCollector = $objectManager->create(\Magento\Quote\Model\Quote\TotalsCollector::class); - + $db = \Magento\TestFramework\Helper\Bootstrap::getInstance()->getBootstrap() + ->getApplication() + ->getDbInstance(); + if (!$db->isDbDumpExists()) { + throw new \LogicException('DB dump does not exist.'); + } + $db->restoreFromDbDump(); //Setup tax configurations - $this->setupUtil = new SetupUtil($objectManager); $this->setupUtil->setupTax($configData); $quote = $this->setupUtil->setupQuote($quoteData); $quoteAddress = $quote->getShippingAddress(); - $totalsCollector->collectAddressTotals($quote, $quoteAddress); + $this->totalsCollector->collectAddressTotals($quote, $quoteAddress); $this->verifyResult($quoteAddress, $expectedResults); } diff --git a/lib/internal/Magento/Framework/Indexer/CacheContext.php b/lib/internal/Magento/Framework/Indexer/CacheContext.php index 7a56b320859e9..4a6964477ebd5 100644 --- a/lib/internal/Magento/Framework/Indexer/CacheContext.php +++ b/lib/internal/Magento/Framework/Indexer/CacheContext.php @@ -30,7 +30,6 @@ class CacheContext implements \Magento\Framework\DataObject\IdentityInterface public function registerEntities($cacheTag, $ids) { $this->entities[$cacheTag] = array_merge($this->getRegisteredEntity($cacheTag), $ids); - return $this; } @@ -40,7 +39,7 @@ public function registerEntities($cacheTag, $ids) * @param array $cacheTags * @return $this */ - public function registerTags(array $cacheTags) + public function registerTags($cacheTags) { $this->tags = array_merge($this->tags, $cacheTags); return $this; diff --git a/lib/internal/Magento/Framework/Indexer/Dimension.php b/lib/internal/Magento/Framework/Indexer/Dimension.php index 4cb74003c46fc..dacc8d7f524f5 100644 --- a/lib/internal/Magento/Framework/Indexer/Dimension.php +++ b/lib/internal/Magento/Framework/Indexer/Dimension.php @@ -14,6 +14,16 @@ */ class Dimension { + /** + * @var string + */ + private $name; + + /** + * @var string + */ + private $value; + /** * @param string $name * @param string $value diff --git a/lib/internal/Magento/Framework/Indexer/DimensionFactory.php b/lib/internal/Magento/Framework/Indexer/DimensionFactory.php index a137e2d0e94a6..8eaeff5628d60 100644 --- a/lib/internal/Magento/Framework/Indexer/DimensionFactory.php +++ b/lib/internal/Magento/Framework/Indexer/DimensionFactory.php @@ -34,11 +34,14 @@ public function __construct(ObjectManagerInterface $objectManager) * @param string $value * @return Dimension */ - public function create(string $name, $value): Dimension + public function create(string $name, string $value): Dimension { - return $this->objectManager->create(Dimension::class, [ - 'name' => $name, - 'value' => (string) $value, - ]); + return $this->objectManager->create( + Dimension::class, + [ + 'name' => $name, + 'value' => $value, + ] + ); } } diff --git a/lib/internal/Magento/Framework/Indexer/Dimension/DimensionProviderInterface.php b/lib/internal/Magento/Framework/Indexer/DimensionProviderInterface.php similarity index 90% rename from lib/internal/Magento/Framework/Indexer/Dimension/DimensionProviderInterface.php rename to lib/internal/Magento/Framework/Indexer/DimensionProviderInterface.php index e2d05b2ae038f..ea4f56eb48d41 100644 --- a/lib/internal/Magento/Framework/Indexer/Dimension/DimensionProviderInterface.php +++ b/lib/internal/Magento/Framework/Indexer/DimensionProviderInterface.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\Framework\Indexer\Dimension; +namespace Magento\Framework\Indexer; /** * @api diff --git a/lib/internal/Magento/Framework/Indexer/Dimension/DimensionalIndexerInterface.php b/lib/internal/Magento/Framework/Indexer/DimensionalIndexerInterface.php similarity index 55% rename from lib/internal/Magento/Framework/Indexer/Dimension/DimensionalIndexerInterface.php rename to lib/internal/Magento/Framework/Indexer/DimensionalIndexerInterface.php index e2f68272565a6..43c4e7a7fd70b 100644 --- a/lib/internal/Magento/Framework/Indexer/Dimension/DimensionalIndexerInterface.php +++ b/lib/internal/Magento/Framework/Indexer/DimensionalIndexerInterface.php @@ -5,11 +5,11 @@ */ declare(strict_types=1); -namespace Magento\Framework\Indexer\Dimension; +namespace Magento\Framework\Indexer; /** * @api - * Run indexer by specific dimension + * Run indexer by dimensions */ interface DimensionalIndexerInterface { @@ -17,9 +17,9 @@ interface DimensionalIndexerInterface * Execute indexer by specified dimension. * Accept array of dimensions DTO that represent indexer dimension * - * @param \Magento\Framework\Indexer\Dimension[] $dimension - * @param \Traversable|null $entityIds + * @param \Magento\Framework\Indexer\Dimension[] $dimensions + * @param \Traversable $entityIds * @return void */ - public function executeByDimension(array $dimension, \Traversable $entityIds = null); + public function executeByDimensions(array $dimensions, \Traversable $entityIds); } diff --git a/lib/internal/Magento/Framework/Indexer/MultiDimensionProvider.php b/lib/internal/Magento/Framework/Indexer/MultiDimensionProvider.php new file mode 100644 index 0000000000000..062945760acfa --- /dev/null +++ b/lib/internal/Magento/Framework/Indexer/MultiDimensionProvider.php @@ -0,0 +1,155 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Indexer; + +/** + * Multiply dimensions from provided DimensionProviderInterface + */ +class MultiDimensionProvider implements \IteratorAggregate +{ + /** + * @var array + */ + private $dimensionsIterators = []; + + /** + * @var array + */ + private $dimensionsDataProviders = []; + + /** + * @var int + */ + private $dimensionsProvidersCount = 0; + + /** + * @param DimensionProviderInterface[] $dimensionProviders + */ + public function __construct(array $dimensionProviders = []) + { + foreach ($dimensionProviders as $dimensionDataProvider) { + $this->addDimensionDataProvider($dimensionDataProvider); + } + } + + /** + * Returns generator that will return multiplied dimensions on each iteration + * + * @return \Traversable|Dimension[][] + * @throws \LogicException + */ + public function getIterator(): \Traversable + { + // just return empty array if we have no dimension providers to iterate over + if ($this->dimensionsProvidersCount === 0) { + yield []; + return; + } + + // this recreates iterators for dimension so we can iterate over them + $this->rewind(); + + // if at leas one dimension provider has no dimensions to return we can't multiple dimension at all + if (!$this->hasCurrentDimension()) { + throw new \LogicException('Can`t multiple dimensions because some of them are empty.'); + } + + // return dimensions until all iterators become invalid + while ($this->hasCurrentDimension()) { + yield $this->getCurrentDimension(); + $this->setNextDimension(); + } + } + + /** + * Return all dimensions for current state of each dimension provider + * + * @return array + */ + private function getCurrentDimension(): array + { + $dimensions = []; + + foreach ($this->dimensionsIterators as $dimensionIterator) { + /** @var Dimension $dimension */ + $dimension = $dimensionIterator->current(); + $dimensions[$dimension->getName()] = $dimension; + } + + return $dimensions; + } + + /** + * Iterates over dimension iterators one by one starting from right to left + * This approach emulates iterations over X nested foreach loops e.g.: + * + * @return void + */ + private function setNextDimension() + { + $this->dimensionsIterators[$this->dimensionsProvidersCount - 1]->next(); + + for ($i = ($this->dimensionsProvidersCount - 1); $i > 0; $i--) { + if (!$this->dimensionsIterators[$i]->valid()) { + $this->dimensionsIterators[$i] = $this->dimensionsDataProviders[$i]->getIterator(); + $this->dimensionsIterators[$i-1]->next(); + } + } + } + + /** + * Recreates iterators so all MultiDimensionProvider can be iterated again + * + * @return void + */ + private function rewind() + { + $this->dimensionsIterators = []; + + foreach ($this->dimensionsDataProviders as $dimensionDataProvider) { + $this->dimensionsIterators[] = $dimensionDataProvider->getIterator(); + } + } + + /** + * Check if all dimension iterators are in valid state + * + * If at least one of dimension iterators is invalid before very first iteration - we assume + * that dimension provider has no dimensions at all, which means we can't multiple all dimensions + * + * If all dimension iterators became invalid - we assume that multiplication is already done + * + * @return bool + */ + private function hasCurrentDimension(): bool + { + $valid = true; + + foreach ($this->dimensionsIterators as $dimensionsIterator) { + // if at least one data provider is invalid at this stage - all generator is invalid + if (!$dimensionsIterator->valid()) { + return false; + } + } + + // generator is valid only when all data providers are valid + return $valid; + } + + /** + * Collects dimension data providers + * This was done via separate method to ensure that each provider has required interface + * + * @param DimensionProviderInterface $dimensionDataProvider + * @return void + */ + private function addDimensionDataProvider(DimensionProviderInterface $dimensionDataProvider) + { + $this->dimensionsDataProviders[] = $dimensionDataProvider; + $this->dimensionsProvidersCount++; + } +} diff --git a/lib/internal/Magento/Framework/Indexer/SaveHandler/IndexerHandler.php b/lib/internal/Magento/Framework/Indexer/SaveHandler/IndexerHandler.php index 3908cf1e98762..c3ab971c992e6 100644 --- a/lib/internal/Magento/Framework/Indexer/SaveHandler/IndexerHandler.php +++ b/lib/internal/Magento/Framework/Indexer/SaveHandler/IndexerHandler.php @@ -12,7 +12,6 @@ use Magento\Framework\Indexer\IndexStructureInterface; use Magento\Framework\Indexer\ScopeResolver\FlatScopeResolver; use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver; -use Magento\Framework\Indexer\SaveHandler\Batch; class IndexerHandler implements IndexerInterface { diff --git a/lib/internal/Magento/Framework/Indexer/SaveHandlerFactory.php b/lib/internal/Magento/Framework/Indexer/SaveHandlerFactory.php index 82e8a26553b89..80231dd7aa290 100644 --- a/lib/internal/Magento/Framework/Indexer/SaveHandlerFactory.php +++ b/lib/internal/Magento/Framework/Indexer/SaveHandlerFactory.php @@ -5,7 +5,6 @@ */ namespace Magento\Framework\Indexer; -use Magento\Framework\Indexer\IndexerInterface; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Indexer\SaveHandler\IndexerInterface as SaveHandlerInterface; diff --git a/lib/internal/Magento/Framework/Indexer/ScopeResolver/IndexScopeResolver.php b/lib/internal/Magento/Framework/Indexer/ScopeResolver/IndexScopeResolver.php index 891a6cd1efe12..a68de6ad36f9a 100644 --- a/lib/internal/Magento/Framework/Indexer/ScopeResolver/IndexScopeResolver.php +++ b/lib/internal/Magento/Framework/Indexer/ScopeResolver/IndexScopeResolver.php @@ -42,16 +42,18 @@ public function __construct( */ public function resolve($index, array $dimensions) { - $tableNameParts = [$index]; + $tableNameParts = []; foreach ($dimensions as $dimension) { switch ($dimension->getName()) { case 'scope': - $tableNameParts[] = $dimension->getName() . $this->getScopeId($dimension); + $tableNameParts[$dimension->getName()] = $dimension->getName() . $this->getScopeId($dimension); break; default: - $tableNameParts[] = $dimension->getName() . $dimension->getValue(); + $tableNameParts[$dimension->getName()] = $dimension->getName() . $dimension->getValue(); } } + ksort($tableNameParts); + array_unshift($tableNameParts, $index); return $this->resource->getTableName(implode('_', $tableNameParts)); } diff --git a/lib/internal/Magento/Framework/Indexer/StructureFactory.php b/lib/internal/Magento/Framework/Indexer/StructureFactory.php index f9b7eafdfc436..1b6729e4603ec 100644 --- a/lib/internal/Magento/Framework/Indexer/StructureFactory.php +++ b/lib/internal/Magento/Framework/Indexer/StructureFactory.php @@ -5,8 +5,6 @@ */ namespace Magento\Framework\Indexer; -use Magento\Framework\Indexer\IndexStructureInterface; - class StructureFactory { /** diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php b/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php new file mode 100644 index 0000000000000..be4d168b108bc --- /dev/null +++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php @@ -0,0 +1,255 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Indexer\Test\Unit; + +use \Magento\Framework\Indexer\MultiDimensionProvider; +use \Magento\Framework\Indexer\DimensionProviderInterface; +use \Magento\Framework\Indexer\Dimension; + +class MultiDimensionProviderTest extends \PHPUnit\Framework\TestCase +{ + /** + * tests that MultiDimensionProvider will return [[]] in case it has no dimension providers + */ + public function testWithNoDataProviders() + { + // prepare expected dimensions + $expectedDimensions = [[]]; + + // collect actual dimensions + $multiDimensionProvider = new MultiDimensionProvider([]); + + $actualDimensions = []; + foreach ($multiDimensionProvider as $dimension) { + $actualDimensions[] = $dimension; + } + + $this->assertSame($expectedDimensions, $actualDimensions); + } + + /** + * tests multiplication of dimensions from different providers + * + * e.g we have three dimensions: + * - dimension X with values (x1, x2) + * - dimension Y with values (y1, y2, y3) + * - dimension Z with values (z1, z2) + * + * the multiplication result set will be: + * x1-y1-z1 + * x1-y1-z2 + * x1-y2-z1 + * x1-y2-z2 + * x1-y3-z1 + * x1-y3-z2 + * x2-y1-z1 + * x2-y1-z2 + * x2-y2-z1 + * x2-y2-z2 + * x2-y3-z1 + * x2-y3-z2 + */ + public function testWithMultipleDataProviders() + { + // prepare expected dimensions + $dimensionXData = [ + $this->getDimensionMock('x', 1), + $this->getDimensionMock('x', 2), + $this->getDimensionMock('x', 3), + ]; + + $dimensionYData = [ + $this->getDimensionMock('y', 1), + $this->getDimensionMock('y', 2), + $this->getDimensionMock('y', 3), + $this->getDimensionMock('y', 4), + $this->getDimensionMock('y', 5), + ]; + + $dimensionZData = [ + $this->getDimensionMock('z', 1), + $this->getDimensionMock('z', 2), + ]; + + $expectedDimensions = []; + + foreach ($dimensionXData as $dimensionX) { + foreach ($dimensionYData as $dimensionY) { + foreach ($dimensionZData as $dimensionZ) { + $expectedDimensions[] = [ + $dimensionX->getName() => $dimensionX, + $dimensionY->getName() => $dimensionY, + $dimensionZ->getName() => $dimensionZ, + ]; + } + } + } + + // collect actual dimensions + $multiDimensionProvider = new MultiDimensionProvider( + [ + $this->getDimensionProviderMock($dimensionXData), + $this->getDimensionProviderMock($dimensionYData), + $this->getDimensionProviderMock($dimensionZData), + ] + ); + + $actualDimensions = []; + foreach ($multiDimensionProvider as $dimension) { + $actualDimensions[] = $dimension; + } + + $this->assertSame($expectedDimensions, $actualDimensions); + } + + /** + * tests that the same MultiDimensionProvider can be used in foreach multiple times without creating again + */ + public function testMultiDimensionProviderIsReIterable() + { + // prepare expected dimensions + $dimensionXData = [ + $this->getDimensionMock('x', 1), + $this->getDimensionMock('x', 2), + $this->getDimensionMock('x', 3), + ]; + + $dimensionZData = [ + $this->getDimensionMock('z', 1), + $this->getDimensionMock('z', 2), + ]; + + // collect actual dimensions + $multiDimensionProvider = new MultiDimensionProvider( + [ + $this->getDimensionProviderMock($dimensionXData), + $this->getDimensionProviderMock($dimensionZData), + ] + ); + + // first iteration + $actualDimensions1st = []; + foreach ($multiDimensionProvider as $dimension) { + $actualDimensions1st[] = $dimension; + } + + // second iteration + $actualDimensions2nd = []; + foreach ($multiDimensionProvider as $dimension) { + $actualDimensions2nd[] = $dimension; + } + + $this->assertSame($actualDimensions1st, $actualDimensions2nd); + } + + /** + * tests that MultiDimensionProvider will throw exception when all dimension providers has nothing to return + * + * @expectedException \LogicException + * @expectedExceptionMessage Can`t multiple dimensions because some of them are empty. + */ + public function testMultiDimensionProviderWithEmptyDataProvider() + { + // collect actual dimensions + $multiDimensionProvider = new MultiDimensionProvider( + [ + $this->getDimensionProviderMock([]), + $this->getDimensionProviderMock([]), + ] + ); + + $actualDimensions = []; + foreach ($multiDimensionProvider as $dimension) { + $actualDimensions[] = $dimension; + } + } + + /** + * tests that MultiDimensionProvider will throw exception when one dimension providers has nothing to return + * + * @expectedException \LogicException + * @expectedExceptionMessage Can`t multiple dimensions because some of them are empty. + */ + public function testMultiDimensionProviderWithMixedDataProvider() + { + + // prepare expected dimensions + $dimensionXData = [ + $this->getDimensionMock('x', 1), + $this->getDimensionMock('x', 2), + $this->getDimensionMock('x', 3), + ]; + + $dimensionYData = [ + $this->getDimensionMock('y', 1), + $this->getDimensionMock('y', 2), + $this->getDimensionMock('y', 3), + $this->getDimensionMock('y', 4), + $this->getDimensionMock('y', 5), + ]; + + $dimensionZData = []; + + // collect actual dimensions + $multiDimensionProvider = new MultiDimensionProvider( + [ + $this->getDimensionProviderMock($dimensionXData), + $this->getDimensionProviderMock($dimensionYData), + $this->getDimensionProviderMock($dimensionZData), + ] + ); + + $actualDimensions = []; + foreach ($multiDimensionProvider as $dimension) { + $actualDimensions[] = $dimension; + } + } + + private function getDimensionProviderMock($dimensions) + { + $dimensionProviderMock = $this->getMockBuilder(DimensionProviderInterface::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->setMethods(['getIterator']) + ->getMockForAbstractClass(); + + $dimensionProviderMock->expects($this->any()) + ->method('getIterator') + ->will( + $this->returnCallback( + function () use ($dimensions) { + return \SplFixedArray::fromArray($dimensions); + } + ) + ); + + return $dimensionProviderMock; + } + + private function getDimensionMock(string $name, string $value) + { + $dimensionMock = $this->getMockBuilder(Dimension::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->setMethods(['getName', 'getValue']) + ->getMock(); + + $dimensionMock->expects($this->any()) + ->method('getName') + ->willReturn($name); + + $dimensionMock->expects($this->any()) + ->method('getValue') + ->willReturn($value); + + return $dimensionMock; + } +} diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/ScopeResolver/IndexScopeResolverTest.php b/lib/internal/Magento/Framework/Indexer/Test/Unit/ScopeResolver/IndexScopeResolverTest.php index ae898657ccecf..b193a3eecdd98 100644 --- a/lib/internal/Magento/Framework/Indexer/Test/Unit/ScopeResolver/IndexScopeResolverTest.php +++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/ScopeResolver/IndexScopeResolverTest.php @@ -103,9 +103,19 @@ public function resolveDataProvider() ], [ 'index' => 'index_name', - 'dimensions' => [['dimension', 10], ['dimension', 20]], + 'dimensions' => [['first', 10], ['second', 20]], // actually you will get exception here thrown in ScopeResolverInterface - 'expected' => 'index_name_dimension10_dimension20' + 'expected' => 'index_name_first10_second20' + ], + [ + 'index' => 'index_name', + 'dimensions' => [['second', 10], ['first', 20]], + 'expected' => 'index_name_first20_second10' + ], + [ + 'index' => 'index_name', + 'dimensions' => [[-1, 10], ['first', 20]], + 'expected' => 'index_name_-110_first20' ] ]; } diff --git a/lib/internal/Magento/Framework/Search/Request/IndexScopeResolverInterface.php b/lib/internal/Magento/Framework/Search/Request/IndexScopeResolverInterface.php index d9412cb8517fa..9e907883bb2b9 100644 --- a/lib/internal/Magento/Framework/Search/Request/IndexScopeResolverInterface.php +++ b/lib/internal/Magento/Framework/Search/Request/IndexScopeResolverInterface.php @@ -6,8 +6,8 @@ namespace Magento\Framework\Search\Request; /** - * Interface \Magento\Framework\Search\Request\IndexScopeResolverInterface - * + * Resolve table name by provided dimensions. Scope Resolver must accept all dimensions that potentially can be used to + * resolve table name, but certain implementation can filter them if needed */ interface IndexScopeResolverInterface { From d863e054b7e84ab3f2f2abe013bc933e0b11ff65 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Thu, 26 Jul 2018 10:55:41 +0300 Subject: [PATCH 0537/1171] MAGETWO-93740: [Forwardport] Ability to configure dimensions --- app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php index 6df6403ff51ec..37208ed896e85 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php @@ -112,7 +112,7 @@ public function __construct( public function execute($entityIds) { foreach ($this->dimensionProvider->getIterator() as $dimension) { - $this->executeByDimension($dimension, new \ArrayIterator($entityIds)); + $this->executeByDimensions($dimension, new \ArrayIterator($entityIds)); } } @@ -120,7 +120,7 @@ public function execute($entityIds) * {@inheritdoc} * @throws \InvalidArgumentException */ - public function executeByDimension(array $dimensions, \Traversable $entityIds = null) + public function executeByDimensions(array $dimensions, \Traversable $entityIds = null) { if (count($dimensions) > 1 || !isset($dimensions[StoreDimensionProvider::DIMENSION_NAME])) { throw new \InvalidArgumentException('Indexer "' . self::INDEXER_ID . '" support only Store dimension'); @@ -161,7 +161,7 @@ public function executeFull() $userFunctions = []; foreach ($this->dimensionProvider->getIterator() as $dimension) { $userFunctions[] = function () use ($dimension) { - $this->executeByDimension($dimension); + $this->executeByDimensions($dimension); }; } $this->processManager->execute($userFunctions); From a6531b0d437536ee940f6788b4db1505d1a0875d Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Thu, 26 Jul 2018 11:02:20 +0300 Subject: [PATCH 0538/1171] MAGETWO-93264: Wrong order status on registered checkout within Braintree Credit Card from Storefront with Signifyd Declined Guarantee --- .../Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php index 8df3a48c78eab..1529755fdc3c9 100644 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php +++ b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php @@ -108,7 +108,7 @@ class CaseInfo extends Block * * @var string */ - private $shippingPrice = '[ng-if$="caseOrderSummary.shipments[0].shippingPrice"]'; + private $shippingPrice = '[ng-if="shipment.shippingPrice"]'; /** * Check if device data are present. From e055265ba68bcbecf748bacd0d2bfa1abd2a4fb3 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Thu, 26 Jul 2018 11:03:15 +0300 Subject: [PATCH 0539/1171] MAGETWO-93740: [Forwardport] Ability to configure dimensions --- .../Indexer/Price/BasePriceModifier.php | 38 +++++++++++++++++++ .../CompositeProductRowSizeEstimator.php | 2 +- .../Indexer/Price/PriceModifierInterface.php | 2 +- .../Indexer/Price/Query/BaseFinalPrice.php | 20 +++++----- 4 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/BasePriceModifier.php diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/BasePriceModifier.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/BasePriceModifier.php new file mode 100644 index 0000000000000..ec967c7c7d04f --- /dev/null +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/BasePriceModifier.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Price; + +/** + * Apply price modifiers to product price indexer which are common for all product types: + * custom options, catalog rule, catalog inventory modifiers + */ +class BasePriceModifier implements PriceModifierInterface +{ + /** + * @var PriceModifierInterface[] + */ + private $priceModifiers; + + /** + * @param array $priceModifiers + */ + public function __construct(array $priceModifiers) + { + $this->priceModifiers = $priceModifiers; + } + + /** + * {@inheritdoc} + */ + public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) + { + foreach ($this->priceModifiers as $priceModifier) { + $priceModifier->modifyPrice($priceTable, $entityIds); + } + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimator.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimator.php index 24cb4fedd57e5..a499777df871a 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimator.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimator.php @@ -18,7 +18,7 @@ class CompositeProductRowSizeEstimator implements IndexTableRowSizeEstimatorInte /** * Calculated memory size for one record in catalog_product_index_price table */ - const MEMORY_SIZE_FOR_ONE_ROW = 250; + const MEMORY_SIZE_FOR_ONE_ROW = 200; /** * @var WebsiteManagementInterface diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/PriceModifierInterface.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/PriceModifierInterface.php index 6ecb6aba89933..7aa9ec0af3856 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/PriceModifierInterface.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/PriceModifierInterface.php @@ -19,5 +19,5 @@ interface PriceModifierInterface * @param array $entityIds * @return void */ - public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) : void; + public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []); } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php index ff76f9d2df4b9..8428ff3688b28 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php @@ -111,10 +111,10 @@ public function getQuery(array $dimensions, string $productType, array $entityId ['cg' => $this->getTable('customer_group')], array_key_exists(CustomerGroupDimensionProvider::DIMENSION_NAME, $dimensions) ? sprintf( - '%s = %s', - $this->dimensionToFieldMapper[CustomerGroupDimensionProvider::DIMENSION_NAME], - $dimensions[CustomerGroupDimensionProvider::DIMENSION_NAME]->getValue() - ) : '', + '%s = %s', + $this->dimensionToFieldMapper[CustomerGroupDimensionProvider::DIMENSION_NAME], + $dimensions[CustomerGroupDimensionProvider::DIMENSION_NAME]->getValue() + ) : '', ['customer_group_id'] )->joinInner( ['pw' => $this->getTable('catalog_product_website')], @@ -125,34 +125,34 @@ public function getQuery(array $dimensions, string $productType, array $entityId 'pw.website_id = cwd.website_id', [] )->joinLeft( - // we need this only for BCC in case someone expects table `tp` to be present in query + // we need this only for BCC in case someone expects table `tp` to be present in query ['tp' => $this->getTable('catalog_product_index_tier_price')], 'tp.entity_id = e.entity_id AND' . ' tp.customer_group_id = cg.customer_group_id AND tp.website_id = pw.website_id', [] )->joinLeft( - // calculate tier price specified as Website = `All Websites` and Customer Group = `Specific Customer Group` + // calculate tier price specified as Website = `All Websites` and Customer Group = `Specific Customer Group` ['tier_price_1' => $this->getTable('catalog_product_entity_tier_price')], 'tier_price_1.' . $linkField . ' = e.' . $linkField . ' AND tier_price_1.all_groups = 0' . ' AND tier_price_1.customer_group_id = cg.customer_group_id AND tier_price_1.qty = 1' . ' AND tier_price_1.website_id = 0', [] )->joinLeft( - // calculate tier price specified as Website = `Specific Website` - //and Customer Group = `Specific Customer Group` + // calculate tier price specified as Website = `Specific Website` + //and Customer Group = `Specific Customer Group` ['tier_price_2' => $this->getTable('catalog_product_entity_tier_price')], 'tier_price_2.' . $linkField . ' = e.' . $linkField . ' AND tier_price_2.all_groups = 0 ' . 'AND tier_price_2.customer_group_id = cg.customer_group_id AND tier_price_2.qty = 1' . ' AND tier_price_2.website_id = pw.website_id', [] )->joinLeft( - // calculate tier price specified as Website = `All Websites` and Customer Group = `ALL GROUPS` + // calculate tier price specified as Website = `All Websites` and Customer Group = `ALL GROUPS` ['tier_price_3' => $this->getTable('catalog_product_entity_tier_price')], 'tier_price_3.' . $linkField . ' = e.' . $linkField . ' AND tier_price_3.all_groups = 1 ' . 'AND tier_price_3.customer_group_id = 0 AND tier_price_3.qty = 1 AND tier_price_3.website_id = 0', [] )->joinLeft( - // calculate tier price specified as Website = `Specific Website` and Customer Group = `ALL GROUPS` + // calculate tier price specified as Website = `Specific Website` and Customer Group = `ALL GROUPS` ['tier_price_4' => $this->getTable('catalog_product_entity_tier_price')], 'tier_price_4.' . $linkField . ' = e.' . $linkField . ' AND tier_price_4.all_groups = 1' . ' AND tier_price_4.customer_group_id = 0 AND tier_price_4.qty = 1' . From a4a1072feef0a17ba7851a96dab8ae9addacf331 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Thu, 26 Jul 2018 11:12:24 +0300 Subject: [PATCH 0540/1171] MAGETWO-93740: [Forwardport] Ability to configure dimensions --- app/code/Magento/Store/Model/StoreDimensionProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Store/Model/StoreDimensionProvider.php b/app/code/Magento/Store/Model/StoreDimensionProvider.php index 90319a52d1362..17f75fb863d9c 100644 --- a/app/code/Magento/Store/Model/StoreDimensionProvider.php +++ b/app/code/Magento/Store/Model/StoreDimensionProvider.php @@ -47,7 +47,7 @@ public function __construct(StoreManagerInterface $storeManager, DimensionFactor public function getIterator(): \Traversable { foreach (array_keys($this->storeManager->getStores()) as $storeId) { - yield [self::DIMENSION_NAME => $this->dimensionFactory->create(self::DIMENSION_NAME, $storeId)]; + yield [self::DIMENSION_NAME => $this->dimensionFactory->create(self::DIMENSION_NAME, (string)$storeId)]; } } } From 45dc3f46a483d6754e90bf8480474ea3b74e28c4 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Thu, 26 Jul 2018 11:17:24 +0300 Subject: [PATCH 0541/1171] MAGETWO-93740: [Forwardport] Ability to configure dimensions --- .../Model/Plugin/PriceIndexUpdater.php | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php new file mode 100644 index 0000000000000..316f7000ae4af --- /dev/null +++ b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogInventory\Model\Plugin; + +use Magento\CatalogInventory\Model\ResourceModel\Stock\Item; +use Magento\Catalog\Model\Indexer\Product\Price\Processor; +use Magento\Framework\Model\AbstractModel; + +/** + * Update product price index after product stock status changed. + */ +class PriceIndexUpdater +{ + /** + * @var Processor + */ + private $priceIndexProcessor; + + /** + * @param Processor $priceIndexProcessor + */ + public function __construct(Processor $priceIndexProcessor) + { + $this->priceIndexProcessor = $priceIndexProcessor; + } + + /** + * @param Item $subject + * @param Item $result + * @param AbstractModel $model + * @return Item + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterSave(Item $subject, Item $result, AbstractModel $model) + { + $fields = [ + 'is_in_stock', + 'use_config_manage_stock', + 'manage_stock', + ]; + foreach ($fields as $field) { + if ($model->dataHasChangedFor($field)) { + $this->priceIndexProcessor->reindexRow($model->getProductId()); + break; + } + } + + return $result; + } + + /** + * @param Item $subject + * @param $result + * @param int $websiteId + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterUpdateSetOutOfStock(Item $subject, $result, int $websiteId) + { + $this->priceIndexProcessor->markIndexerAsInvalid(); + } + + /** + * @param Item $subject + * @param $result + * @param int $websiteId + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterUpdateSetInStock(Item $subject, $result, int $websiteId) + { + $this->priceIndexProcessor->markIndexerAsInvalid(); + } +} From ed2d924314855d6af420ede54677c196132f3733 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Thu, 26 Jul 2018 11:32:49 +0300 Subject: [PATCH 0542/1171] MAGETWO-93740: [Forwardport] Ability to configure dimensions --- .../Model/Indexer/ProductPriceIndexFilter.php | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php new file mode 100644 index 0000000000000..8757dba0873f0 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php @@ -0,0 +1,107 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogInventory\Model\Indexer; + +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\CatalogInventory\Model\ResourceModel\Stock\Item; +use Magento\CatalogInventory\Model\Stock; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceModifierInterface; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\App\ObjectManager; + +/** + * Class for filter product price index. + */ +class ProductPriceIndexFilter implements PriceModifierInterface +{ + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; + + /** + * @var Item + */ + private $stockItem; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var string + */ + private $connectionName; + + /** + * @param StockConfigurationInterface $stockConfiguration + * @param Item $stockItem + * @param ResourceConnection $resourceConnection + * @param string $connectionName + */ + public function __construct( + StockConfigurationInterface $stockConfiguration, + Item $stockItem, + ResourceConnection $resourceConnection = null, + $connectionName = 'indexer' + ) { + $this->stockConfiguration = $stockConfiguration; + $this->stockItem = $stockItem; + $this->resourceConnection = $resourceConnection ?: ObjectManager::getInstance()->get(ResourceConnection::class); + $this->connectionName = $connectionName; + } + + /** + * Remove out of stock products data from price index. + * + * @param IndexTableStructure $priceTable + * @param array $entityIds + * @return void + * + * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) + { + if ($this->stockConfiguration->isShowOutOfStock()) { + return; + } + + $connection = $this->resourceConnection->getConnection($this->connectionName); + $select = $connection->select(); + $select->from( + ['price_index' => $priceTable->getTableName()], + [] + ); + $select->joinInner( + ['stock_item' => $this->stockItem->getMainTable()], + 'stock_item.product_id = price_index.' . $priceTable->getEntityField() + . ' AND stock_item.stock_id = ' . Stock::DEFAULT_STOCK_ID, + [] + ); + if ($this->stockConfiguration->getManageStock()) { + $stockStatus = $connection->getCheckSql( + 'use_config_manage_stock = 0 AND manage_stock = 0', + Stock::STOCK_IN_STOCK, + 'is_in_stock' + ); + } else { + $stockStatus = $connection->getCheckSql( + 'use_config_manage_stock = 0 AND manage_stock = 1', + 'is_in_stock', + Stock::STOCK_IN_STOCK + ); + } + $select->where($stockStatus . ' = ?', Stock::STOCK_OUT_OF_STOCK); + + $query = $select->deleteFromSelect('price_index'); + $connection->query($query); + } +} From 94fc461dbc82d6e87a1d02a9b4b53b9a36aa5897 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Thu, 26 Jul 2018 11:43:18 +0300 Subject: [PATCH 0543/1171] MAGETWO-93732: [Forwardport] Configurable Product --- .../Product/Indexer/Price/Configurable.php | 260 +++++++++++++----- 1 file changed, 195 insertions(+), 65 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php index 087931ebe5dcc..81e2e99bfe93a 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php @@ -5,91 +5,176 @@ */ namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price; -use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Store\Api\StoreResolverInterface; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier; +use Magento\Framework\Indexer\DimensionalIndexerInterface; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Query\BaseFinalPrice; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructureFactory; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure; /** * Configurable Products Price Indexer Resource model + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice +class Configurable implements DimensionalIndexerInterface { /** - * @param null|int|array $entityIds - * @return \Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price\Configurable + * @var BaseFinalPrice */ - protected function reindex($entityIds = null) - { - if ($this->hasEntity() || !empty($entityIds)) { - $this->prepareFinalPriceDataForType($entityIds, $this->getTypeId()); - $this->_applyCustomOption(); - $this->_applyConfigurableOption($entityIds); - $this->_movePriceDataToIndexTable($entityIds); - } + private $baseFinalPrice; - return $this; - } + /** + * @var IndexTableStructureFactory + */ + private $indexTableStructureFactory; /** - * Retrieve table name for custom option temporary aggregation data - * - * @return string + * @var TableMaintainer */ - protected function _getConfigurableOptionAggregateTable() - { - return $this->tableStrategy->getTableName('catalog_product_index_price_cfg_opt_agr'); - } + private $tableMaintainer; /** - * Retrieve table name for custom option prices data - * - * @return string + * @var MetadataPool */ - protected function _getConfigurableOptionPriceTable() - { - return $this->tableStrategy->getTableName('catalog_product_index_price_cfg_opt'); + private $metadataPool; + + /** + * @var \Magento\Framework\App\ResourceConnection + */ + private $resource; + + /** + * @var bool + */ + private $fullReindexAction; + + /** + * @var string + */ + private $connectionName; + + /** + * @var \Magento\Framework\DB\Adapter\AdapterInterface + */ + private $connection; + + /** + * @var BasePriceModifier + */ + private $basePriceModifier; + + /** + * @param BaseFinalPrice $baseFinalPrice + * @param IndexTableStructureFactory $indexTableStructureFactory + * @param TableMaintainer $tableMaintainer + * @param MetadataPool $metadataPool + * @param \Magento\Framework\App\ResourceConnection $resource + * @param BasePriceModifier $basePriceModifier + * @param bool $fullReindexAction + * @param string $connectionName + */ + public function __construct( + BaseFinalPrice $baseFinalPrice, + IndexTableStructureFactory $indexTableStructureFactory, + TableMaintainer $tableMaintainer, + MetadataPool $metadataPool, + \Magento\Framework\App\ResourceConnection $resource, + BasePriceModifier $basePriceModifier, + $fullReindexAction = false, + $connectionName = 'indexer' + ) { + $this->baseFinalPrice = $baseFinalPrice; + $this->indexTableStructureFactory = $indexTableStructureFactory; + $this->tableMaintainer = $tableMaintainer; + $this->connectionName = $connectionName; + $this->metadataPool = $metadataPool; + $this->resource = $resource; + $this->fullReindexAction = $fullReindexAction; + $this->basePriceModifier = $basePriceModifier; } /** - * Prepare table structure for custom option temporary aggregation data + * {@inheritdoc} * - * @return \Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price\Configurable + * @throws \Exception */ - protected function _prepareConfigurableOptionAggregateTable() + public function executeByDimensions(array $dimensions, \Traversable $entityIds) { - $this->getConnection()->delete($this->_getConfigurableOptionAggregateTable()); - return $this; + $this->tableMaintainer->createMainTmpTable($dimensions); + + $temporaryPriceTable = $this->indexTableStructureFactory->create([ + 'tableName' => $this->tableMaintainer->getMainTmpTable($dimensions), + 'entityField' => 'entity_id', + 'customerGroupField' => 'customer_group_id', + 'websiteField' => 'website_id', + 'taxClassField' => 'tax_class_id', + 'originalPriceField' => 'price', + 'finalPriceField' => 'final_price', + 'minPriceField' => 'min_price', + 'maxPriceField' => 'max_price', + 'tierPriceField' => 'tier_price', + ]); + $select = $this->baseFinalPrice->getQuery( + $dimensions, + \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE, + iterator_to_array($entityIds) + ); + $query = $select->insertFromSelect($temporaryPriceTable->getTableName(), [], false); + $this->tableMaintainer->getConnection()->query($query); + + $this->basePriceModifier->modifyPrice($temporaryPriceTable, iterator_to_array($entityIds)); + $this->applyConfigurableOption($temporaryPriceTable, $dimensions, iterator_to_array($entityIds)); } /** - * Prepare table structure for custom option prices data + * Apply configurable option * - * @return \Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price\Configurable + * @param IndexTableStructure $temporaryPriceTable + * @param array $dimensions + * @param array $entityIds + * + * @return $this + * @throws \Exception */ - protected function _prepareConfigurableOptionPriceTable() - { - $this->getConnection()->delete($this->_getConfigurableOptionPriceTable()); + private function applyConfigurableOption( + IndexTableStructure $temporaryPriceTable, + array $dimensions, + array $entityIds + ) { + $temporaryOptionsTableName = 'catalog_product_index_price_cfg_opt_temp'; + $this->getConnection()->createTemporaryTableLike( + $temporaryOptionsTableName, + $this->getTable('catalog_product_index_price_cfg_opt_tmp'), + true + ); + + $this->fillTemporaryOptionsTable($temporaryOptionsTableName, $dimensions, $entityIds); + $this->updateTemporaryTable($temporaryPriceTable->getTableName(), $temporaryOptionsTableName); + + $this->getConnection()->delete($temporaryOptionsTableName); + return $this; } /** - * Calculate minimal and maximal prices for configurable product options - * and apply it to final price + * Put data into catalog product price indexer config option temp table + * + * @param string $temporaryOptionsTableName + * @param array $dimensions + * @param array $entityIds * - * @param array|null $entityIds - * @return \Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price\Configurable + * @return void + * @throws \Exception */ - protected function _applyConfigurableOption($entityIds = null) + private function fillTemporaryOptionsTable(string $temporaryOptionsTableName, array $dimensions, array $entityIds) { - $metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class); - $connection = $this->getConnection(); - $copTable = $this->_getConfigurableOptionPriceTable(); - $finalPriceTable = $this->_getDefaultFinalPriceTable(); + $metadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); $linkField = $metadata->getLinkField(); - $this->_prepareConfigurableOptionPriceTable(); - - $select = $connection->select()->from( - ['i' => $this->getIdxTable()], + $select = $this->getConnection()->select()->from( + ['i' => $this->getMainTable($dimensions)], [] )->join( ['l' => $this->getTable('catalog_product_super_link')], @@ -107,7 +192,6 @@ protected function _applyConfigurableOption($entityIds = null) 'MIN(final_price)', 'MAX(final_price)', 'MIN(tier_price)', - ] )->group( ['le.entity_id', 'customer_group_id', 'website_id'] @@ -115,31 +199,77 @@ protected function _applyConfigurableOption($entityIds = null) if ($entityIds !== null) { $select->where('le.entity_id IN (?)', $entityIds); } + $query = $select->insertFromSelect($temporaryOptionsTableName); + $this->getConnection()->query($query); + } - $query = $select->insertFromSelect($copTable); - $connection->query($query); - - $table = ['i' => $finalPriceTable]; - $select = $connection->select()->join( - ['io' => $copTable], + /** + * Update data in the catalog product price indexer temp table + * + * @param string $temporaryPriceTableName + * @param string $temporaryOptionsTableName + * + * @return void + */ + private function updateTemporaryTable(string $temporaryPriceTableName, string $temporaryOptionsTableName) + { + $table = ['i' => $temporaryPriceTableName]; + $selectForCrossUpdate = $this->getConnection()->select()->join( + ['io' => $temporaryOptionsTableName], 'i.entity_id = io.entity_id AND i.customer_group_id = io.customer_group_id' . ' AND i.website_id = io.website_id', [] ); // adds price of custom option, that was applied in DefaultPrice::_applyCustomOption - $select->columns( + $selectForCrossUpdate->columns( [ - 'min_price' => new \Zend_Db_Expr('i.min_price - i.orig_price + io.min_price'), - 'max_price' => new \Zend_Db_Expr('i.max_price - i.orig_price + io.max_price'), + 'min_price' => new \Zend_Db_Expr('i.min_price - i.price + io.min_price'), + 'max_price' => new \Zend_Db_Expr('i.max_price - i.price + io.max_price'), 'tier_price' => 'io.tier_price', ] ); - $query = $select->crossUpdateFromSelect($table); - $connection->query($query); + $query = $selectForCrossUpdate->crossUpdateFromSelect($table); + $this->getConnection()->query($query); + } + + /** + * Get main table + * + * @param array $dimensions + * @return string + */ + private function getMainTable($dimensions) + { + if ($this->fullReindexAction) { + return $this->tableMaintainer->getMainReplicaTable($dimensions); + } + return $this->tableMaintainer->getMainTable($dimensions); + } + + /** + * Get connection + * + * return \Magento\Framework\DB\Adapter\AdapterInterface + * @throws \DomainException + */ + private function getConnection(): \Magento\Framework\DB\Adapter\AdapterInterface + { + if ($this->connection === null) { + $this->connection = $this->resource->getConnection($this->connectionName); + } - $connection->delete($copTable); + return $this->connection; + } - return $this; + /** + * Get table + * + * @param string $tableName + * @return string + */ + private function getTable($tableName) + { + return $this->resource->getTableName($tableName, $this->connectionName); } } From 8e5a27ffe446dd7d083f0bc461806299914d1c39 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Thu, 26 Jul 2018 12:51:49 +0300 Subject: [PATCH 0544/1171] MC-2294: Image still displayed after deleting it from Media Gallery Storage - fix static tests --- .../Magento/Ui/view/base/web/js/form/element/file-uploader.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js index 61be8c3dc505f..eb91f47e936ad 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js @@ -6,6 +6,7 @@ /** * @api */ +/* global Base64 */ define([ 'jquery', 'underscore', From fd8024ff3057ea8881bccfb4cbf99f36359d4ebd Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 26 Jul 2018 15:54:00 +0300 Subject: [PATCH 0545/1171] MAGETWO-62107: Remove or refactor unused class --- .../Framework/DB/Test/Unit/Tree/NodeTest.php | 116 ------------------ lib/internal/Magento/Framework/DB/Tree.php | 40 ++++++ .../Magento/Framework/DB/Tree/Node.php | 22 ++++ .../Magento/Framework/DB/Tree/NodeSet.php | 17 +++ 4 files changed, 79 insertions(+), 116 deletions(-) delete mode 100644 lib/internal/Magento/Framework/DB/Test/Unit/Tree/NodeTest.php diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/Tree/NodeTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/Tree/NodeTest.php deleted file mode 100644 index 93abeae644f61..0000000000000 --- a/lib/internal/Magento/Framework/DB/Test/Unit/Tree/NodeTest.php +++ /dev/null @@ -1,116 +0,0 @@ -<?php -/** - * \Magento\Framework\DB\Tree\Node test case - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Framework\DB\Test\Unit\Tree; - -class NodeTest extends \PHPUnit\Framework\TestCase -{ - /** - * @param array $data - * @param $expectedException - * @param $expectedExceptionMessage - * @dataProvider constructorDataProvider - */ - public function testConstructorWithInvalidArgumentsThrowsException( - array $data, - $expectedException, - $expectedExceptionMessage - ) { - $this->expectException($expectedException); - $this->expectExceptionMessage($expectedExceptionMessage); - new \Magento\Framework\DB\Tree\Node($data['node_data'], $data['keys']); - } - - /** - * @param array $data - * @param string $assertMethod - * @dataProvider isParentDataProvider - */ - public function testIsParent(array $data, $assertMethod) - { - $model = new \Magento\Framework\DB\Tree\Node($data['node_data'], $data['keys']); - $this->$assertMethod($model->isParent()); - } - - /** - * @return array - */ - public function isParentDataProvider() - { - return [ - [ - [ - 'node_data' => [ - 'id' => 'id', - 'pid' => 'pid', - 'level' => 'level', - 'right_key' => 10, - 'left_key' => 5, - ], - 'keys' => [ - 'id' => 'id', - 'pid' => 'pid', - 'level' => 'level', - 'right' => 'right_key', - 'left' => 'left_key', - ], - ], - 'assertTrue', - ], - [ - [ - 'node_data' => [ - 'id' => 'id', - 'pid' => 'pid', - 'level' => 'level', - 'right_key' => 5, - 'left_key' => 10, - ], - 'keys' => [ - 'id' => 'id', - 'pid' => 'pid', - 'level' => 'level', - 'right' => 'right_key', - 'left' => 'left_key', - ], - ], - 'assertFalse' - ] - ]; - } - - /** - * @return array - */ - public function constructorDataProvider() - { - return [ - [ - [ - 'node_data' => null, - 'keys' => null, - ], \Magento\Framework\Exception\LocalizedException::class, - 'The node information is empty. Enter the information and try again.', - ], - [ - [ - 'node_data' => null, - 'keys' => true, - ], \Magento\Framework\Exception\LocalizedException::class, - 'The node information is empty. Enter the information and try again.' - ], - [ - [ - 'node_data' => true, - 'keys' => null, - ], \Magento\Framework\Exception\LocalizedException::class, - 'The encryption key can\'t be empty. Enter the key and try again.' - ] - ]; - } -} diff --git a/lib/internal/Magento/Framework/DB/Tree.php b/lib/internal/Magento/Framework/DB/Tree.php index a162640e5aa50..9890c6ef0d240 100644 --- a/lib/internal/Magento/Framework/DB/Tree.php +++ b/lib/internal/Magento/Framework/DB/Tree.php @@ -15,6 +15,8 @@ * Magento Library * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @deprecated Not used anymore. */ class Tree { @@ -77,6 +79,8 @@ class Tree * @throws LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) + * + * @deprecated Not used anymore. */ public function __construct($config = []) { @@ -149,6 +153,8 @@ public function __construct($config = []) * * @param string $name * @return $this + * + * @deprecated Not used anymore. */ public function setIdField($name) { @@ -161,6 +167,8 @@ public function setIdField($name) * * @param string $name * @return $this + * + * @deprecated Not used anymore. */ public function setLeftField($name) { @@ -173,6 +181,8 @@ public function setLeftField($name) * * @param string $name * @return $this + * + * @deprecated Not used anymore. */ public function setRightField($name) { @@ -185,6 +195,8 @@ public function setRightField($name) * * @param string $name * @return $this + * + * @deprecated Not used anymore. */ public function setLevelField($name) { @@ -197,6 +209,8 @@ public function setLevelField($name) * * @param string $name * @return $this + * + * @deprecated Not used anymore. */ public function setPidField($name) { @@ -209,6 +223,8 @@ public function setPidField($name) * * @param string $name * @return $this + * + * @deprecated Not used anymore. */ public function setTable($name) { @@ -218,6 +234,8 @@ public function setTable($name) /** * @return array + * + * @deprecated Not used anymore. */ public function getKeys() { @@ -235,6 +253,8 @@ public function getKeys() * * @param array $data * @return string + * + * @deprecated Not used anymore. */ public function clear($data = []) { @@ -260,6 +280,8 @@ public function clear($data = []) * * @param string|int $nodeId * @return array + * + * @deprecated Not used anymore. */ public function getNodeInfo($nodeId) { @@ -279,6 +301,8 @@ public function getNodeInfo($nodeId) * @param array $data * @return false|string * @SuppressWarnings(PHPMD.ExitExpression) + * + * @deprecated Not used anymore. */ public function appendChild($nodeId, $data) { @@ -345,6 +369,8 @@ public function appendChild($nodeId, $data) /** * @return array + * + * @deprecated Not used anymore. */ public function checkNodes() { @@ -375,6 +401,8 @@ public function checkNodes() /** * @param string|int $nodeId * @return bool|Node|void + * + * @deprecated Not used anymore. */ public function removeNode($nodeId) { @@ -450,6 +478,8 @@ public function removeNode($nodeId) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.ExitExpression) + * + * @deprecated Not used anymore. */ public function moveNode($eId, $pId, $aId = 0) { @@ -785,6 +815,8 @@ public function moveNode($eId, $pId, $aId = 0) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.UnusedLocalVariable) * @SuppressWarnings(PHPMD.ExitExpression) + * + * @deprecated Not used anymore. */ public function moveNodes($eId, $pId, $aId = 0) { @@ -983,6 +1015,8 @@ public function moveNodes($eId, $pId, $aId = 0) * @param string $joinCondition * @param string $fields * @return void + * + * @deprecated Not used anymore. */ public function addTable($tableName, $joinCondition, $fields = '*') { @@ -992,6 +1026,8 @@ public function addTable($tableName, $joinCondition, $fields = '*') /** * @param Select $select * @return void + * + * @deprecated Not used anymore. */ protected function _addExtTablesToSelect(Select &$select) { @@ -1006,6 +1042,8 @@ protected function _addExtTablesToSelect(Select &$select) * @param int $endLevel * @return NodeSet * @SuppressWarnings(PHPMD.ExitExpression) + * + * @deprecated Not used anymore. */ public function getChildren($nodeId, $startLevel = 0, $endLevel = 0) { @@ -1051,6 +1089,8 @@ public function getChildren($nodeId, $startLevel = 0, $endLevel = 0) /** * @param string|int $nodeId * @return Node + * + * @deprecated Not used anymore. */ public function getNode($nodeId) { diff --git a/lib/internal/Magento/Framework/DB/Tree/Node.php b/lib/internal/Magento/Framework/DB/Tree/Node.php index 8088718be5e73..eb954a696e21e 100644 --- a/lib/internal/Magento/Framework/DB/Tree/Node.php +++ b/lib/internal/Magento/Framework/DB/Tree/Node.php @@ -10,6 +10,8 @@ /** * @SuppressWarnings(PHPMD.UnusedPrivateField) + * + * @deprecated Not used anymore. */ class Node { @@ -50,11 +52,15 @@ class Node /** * @var bool + * + * @deprecated */ public $hasChild = false; /** * @var float|int + * + * @deprecated */ public $numChild = 0; @@ -62,6 +68,8 @@ class Node * @param array $nodeData * @param array $keys * @throws LocalizedException + * + * @deprecated */ public function __construct($nodeData, $keys) { @@ -94,6 +102,8 @@ public function __construct($nodeData, $keys) /** * @param string $name * @return null|array + * + * @deprecated */ public function getData($name) { @@ -106,6 +116,8 @@ public function getData($name) /** * @return int + * + * @deprecated */ public function getLevel() { @@ -114,6 +126,8 @@ public function getLevel() /** * @return int + * + * @deprecated */ public function getLeft() { @@ -122,6 +136,8 @@ public function getLeft() /** * @return int + * + * @deprecated */ public function getRight() { @@ -130,6 +146,8 @@ public function getRight() /** * @return string|int + * + * @deprecated */ public function getPid() { @@ -138,6 +156,8 @@ public function getPid() /** * @return string|int + * + * @deprecated */ public function getId() { @@ -148,6 +168,8 @@ public function getId() * Return true if node has child * * @return bool + * + * @deprecated */ public function isParent() { diff --git a/lib/internal/Magento/Framework/DB/Tree/NodeSet.php b/lib/internal/Magento/Framework/DB/Tree/NodeSet.php index 46a2497a8343f..75e677b77ae45 100644 --- a/lib/internal/Magento/Framework/DB/Tree/NodeSet.php +++ b/lib/internal/Magento/Framework/DB/Tree/NodeSet.php @@ -8,6 +8,7 @@ /** * TODO implements iterators * + * @deprecated Not used anymore. */ class NodeSet implements \Iterator, \Countable { @@ -33,6 +34,8 @@ class NodeSet implements \Iterator, \Countable /** * Constructor + * + * @deprecated */ public function __construct() { @@ -45,6 +48,8 @@ public function __construct() /** * @param Node $node * @return int + * + * @deprecated */ public function addNode(Node $node) { @@ -55,6 +60,8 @@ public function addNode(Node $node) /** * @return int + * + * @deprecated */ public function count() { @@ -63,6 +70,8 @@ public function count() /** * @return bool + * + * @deprecated */ public function valid() { @@ -71,6 +80,8 @@ public function valid() /** * @return false|int + * + * @deprecated */ public function next() { @@ -83,6 +94,8 @@ public function next() /** * @return int + * + * @deprecated */ public function key() { @@ -91,6 +104,8 @@ public function key() /** * @return Node + * + * @deprecated */ public function current() { @@ -99,6 +114,8 @@ public function current() /** * @return void + * + * @deprecated */ public function rewind() { From 52acd6cfb3346f6fde7314b30cebfbbd2f35c392 Mon Sep 17 00:00:00 2001 From: KevinBKozan <kkozan@magento.com> Date: Thu, 26 Jul 2018 08:43:23 -0500 Subject: [PATCH 0546/1171] MQE-1112: Bump MFTF version in Magento - fix flaky MTF tests --- .../Mftf/ActionGroup/StorefrontProductPageActionGroup.xml | 2 -- ...nfigurableProductChildImageShouldBeShownOnWishListTest.xml | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml index 43fef7330c812..d46b895044531 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml @@ -14,9 +14,7 @@ </arguments> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdding}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdding"/> - <waitForElementVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdded}}" stepKey="waitForElementVisibleAddToCartButtonTitleIsAdded"/> <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdded}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdded"/> - <waitForElementVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAddToCart}}" stepKey="waitForElementVisibleAddToCartButtonTitleIsAddToCart"/> <waitForPageLoad stepKey="waitForPageLoad"/> <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{productName}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> </actionGroup> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml index 981ee51954fb4..95beae991384f 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml @@ -35,6 +35,10 @@ </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetFiltersIfPresent"/> + <actionGroup ref="searchProductGridByKeyword" stepKey="searchProductGrid"> + <argument name="keyword" value="_defaultProduct.name"/> + </actionGroup> <click selector="{{AdminProductGridSection.selectRowBasedOnName(_defaultProduct.name)}}" stepKey="selectProductToAddImage"/> <waitForPageLoad stepKey="waitForProductEditPageLoad"/> <actionGroup ref="addProductImage" stepKey="addImageForParentProduct"> From bd5dd5c0d4449a34874ae77e3b6c3d643590e87e Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@magento.com> Date: Thu, 26 Jul 2018 17:04:34 +0300 Subject: [PATCH 0547/1171] MAGETWO-93678: [Forwardport] Implement sharding and parallelization for Price Indexer - part 1 --- ...stallDefaultPriceIndexerDimensionsMode.php | 78 +++++++++++++++++++ app/code/Magento/Catalog/etc/db_schema.xml | 2 + 2 files changed, 80 insertions(+) create mode 100644 app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultPriceIndexerDimensionsMode.php diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultPriceIndexerDimensionsMode.php b/app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultPriceIndexerDimensionsMode.php new file mode 100644 index 0000000000000..c4ad5ac0bbb28 --- /dev/null +++ b/app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultPriceIndexerDimensionsMode.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Setup\Patch\Data; + +use Magento\Catalog\Model\Indexer\Product\Price\ModeSwitcher; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; +use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; + +/** + * Class InstallDefaultCategories data patch. + * + * @package Magento\Catalog\Setup\Patch + */ +class InstallDefaultPriceIndexerDimensionsMode implements DataPatchInterface, PatchVersionInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * PatchInitial constructor. + * @param ModuleDataSetupInterface $moduleDataSetup + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup + ) { + $this->moduleDataSetup = $moduleDataSetup; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + $configTable = $this->moduleDataSetup->getTable('core_config_data'); + + $this->moduleDataSetup->getConnection()->insert( + $configTable, + [ + 'scope' => 'default', + 'scope_id' => 0, + 'value' => DimensionModeConfiguration::DIMENSION_NONE, + 'path' => ModeSwitcher::XML_PATH_PRICE_DIMENSIONS_MODE + ] + ); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.2.6'; + } +} diff --git a/app/code/Magento/Catalog/etc/db_schema.xml b/app/code/Magento/Catalog/etc/db_schema.xml index 60789c016ff0f..7b4e1a96adda0 100644 --- a/app/code/Magento/Catalog/etc/db_schema.xml +++ b/app/code/Magento/Catalog/etc/db_schema.xml @@ -1199,6 +1199,8 @@ comment="Catalog Product Website Index Table"> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" comment="Website ID"/> + <column xsi:type="smallint" name="default_store_id" padding="5" unsigned="true" nullable="false" identity="false" + comment="Default store id for website"/> <column xsi:type="date" name="website_date" comment="Website Date"/> <column xsi:type="float" name="rate" unsigned="false" nullable="true" default="1" comment="Rate"/> <constraint xsi:type="primary" name="PRIMARY"> From ee48f56a379519c450c10604483b28fa09020abc Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Thu, 26 Jul 2018 10:00:45 -0500 Subject: [PATCH 0548/1171] MQE-1112: Bump MFTF version in Magento --- dev/tests/functional/phpunit.xml.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/phpunit.xml.dist b/dev/tests/functional/phpunit.xml.dist index 9db6a0f22a9b1..57237b23b8017 100644 --- a/dev/tests/functional/phpunit.xml.dist +++ b/dev/tests/functional/phpunit.xml.dist @@ -54,7 +54,7 @@ <env name="basedir" value="var/log" /> <env name="credentials_file_path" value="./credentials.xml.dist" /> <env name="mage_mode" value="developer" /> - <env name="magento_timezone" value="America/Los_Angeles" /> + <env name="magento_timezone" value="UTC" /> </php> </phpunit> From 857f496e9745b5d5a6b4c1968313d19d9903831a Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Thu, 26 Jul 2018 13:31:32 -0500 Subject: [PATCH 0549/1171] MQE-1112: Bump MFTF version in Magento --- dev/tests/functional/bootstrap.php | 6 ++++++ dev/tests/functional/phpunit.xml.dist | 2 +- .../tests/app/Magento/Catalog/Test/Block/Product/View.php | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/dev/tests/functional/bootstrap.php b/dev/tests/functional/bootstrap.php index f0b5b126756ab..0dbf0d9471f18 100644 --- a/dev/tests/functional/bootstrap.php +++ b/dev/tests/functional/bootstrap.php @@ -14,6 +14,12 @@ include __DIR__ . '/vendor/autoload.php'; setCustomErrorHandler(); +date_default_timezone_set('UTC'); + +/* For data consistency between displaying (printing) and serialization a float number */ +ini_set('precision', 14); +ini_set('serialize_precision', 14); + /** * Set custom error handler */ diff --git a/dev/tests/functional/phpunit.xml.dist b/dev/tests/functional/phpunit.xml.dist index 57237b23b8017..9db6a0f22a9b1 100644 --- a/dev/tests/functional/phpunit.xml.dist +++ b/dev/tests/functional/phpunit.xml.dist @@ -54,7 +54,7 @@ <env name="basedir" value="var/log" /> <env name="credentials_file_path" value="./credentials.xml.dist" /> <env name="mage_mode" value="developer" /> - <env name="magento_timezone" value="UTC" /> + <env name="magento_timezone" value="America/Los_Angeles" /> </php> </phpunit> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View.php index 2b72d2b915ee6..990906b7e302a 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View.php @@ -89,7 +89,7 @@ class View extends AbstractConfigureBlock * * @var string */ - protected $productDescription = '.product.attribute.description'; + protected $productDescription = '.product.attribute.description .value'; /** * Product short-description element. From 99d0f7b33eb1fedb44e441409a0210b1ebdbc8e5 Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@magento.com> Date: Fri, 27 Jul 2018 10:03:39 +0300 Subject: [PATCH 0550/1171] MAGETWO-93678: [Forwardport] Implement sharding and parallelization for Price Indexer - part 1 --- .../Indexer/Price/BasePriceModifier.php | 2 +- .../Price/CustomOptionPriceModifier.php | 2 +- .../Indexer/Price/PriceModifierInterface.php | 2 +- .../Model/Indexer/ProductPriceIndexFilter.php | 2 +- .../Indexer/ProductPriceIndexModifier.php | 26 ++++++++++++++++--- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/BasePriceModifier.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/BasePriceModifier.php index ec967c7c7d04f..cf5ba451c380e 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/BasePriceModifier.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/BasePriceModifier.php @@ -29,7 +29,7 @@ public function __construct(array $priceModifiers) /** * {@inheritdoc} */ - public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) + public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) : void { foreach ($this->priceModifiers as $priceModifier) { $priceModifier->modifyPrice($priceTable, $entityIds); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CustomOptionPriceModifier.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CustomOptionPriceModifier.php index 646cd0d4c1a4c..47fc6802d7eaf 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CustomOptionPriceModifier.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CustomOptionPriceModifier.php @@ -89,7 +89,7 @@ public function __construct( * @throws \Exception * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) + public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) : void { // no need to run all queries if current products have no custom options if (!$this->checkIfCustomOptionsExist($priceTable)) { diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/PriceModifierInterface.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/PriceModifierInterface.php index 7aa9ec0af3856..6ecb6aba89933 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/PriceModifierInterface.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/PriceModifierInterface.php @@ -19,5 +19,5 @@ interface PriceModifierInterface * @param array $entityIds * @return void */ - public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []); + public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) : void; } diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php index 8757dba0873f0..5e7210c1b7444 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php @@ -68,7 +68,7 @@ public function __construct( * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) + public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) : void { if ($this->stockConfiguration->isShowOutOfStock()) { return; diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceIndexModifier.php b/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceIndexModifier.php index a60b05dc7c9bc..404fc32e0c0d4 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceIndexModifier.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceIndexModifier.php @@ -10,6 +10,8 @@ use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceModifierInterface; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure; use Magento\CatalogRule\Model\ResourceModel\Rule\Product\Price; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\App\ObjectManager; /** * Class for adding catalog rule prices to price index table. @@ -21,12 +23,29 @@ class ProductPriceIndexModifier implements PriceModifierInterface */ private $priceResourceModel; + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var string + */ + private $connectionName; + /** * @param Price $priceResourceModel + * @param ResourceConnection $resourceConnection + * @param string $connectionName */ - public function __construct(Price $priceResourceModel) - { + public function __construct( + Price $priceResourceModel, + ResourceConnection $resourceConnection, + $connectionName = 'indexer' + ) { $this->priceResourceModel = $priceResourceModel; + $this->resourceConnection = $resourceConnection ?: ObjectManager::getInstance()->get(ResourceConnection::class); + $this->connectionName = $connectionName; } /** @@ -34,7 +53,8 @@ public function __construct(Price $priceResourceModel) */ public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) : void { - $connection = $this->priceResourceModel->getConnection(); + $connection = $this->resourceConnection->getConnection($this->connectionName); + $select = $connection->select(); $select->join( From dfe3b3da39083a4159f13671d7597a809cfd2a2a Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 27 Jul 2018 10:25:07 +0300 Subject: [PATCH 0551/1171] MAGETWO-93821: [Forwardport] Sorting on price of configurable products in catalog not working properly --- .../Model/ResourceModel/Stock.php | 8 +- .../Model/ResourceModel/Stock/Item.php | 189 ++++++++++++- ...datePriceIndexUponConfigChangeObserver.php | 49 ++++ ...dateItemsStockUponConfigChangeObserver.php | 30 +- ...ItemsStockUponConfigChangeObserverTest.php | 19 +- app/code/Magento/CatalogInventory/etc/di.xml | 1 - .../Magento/CatalogInventory/etc/events.xml | 1 + .../Magento/CatalogInventory/etc/mview.xml | 5 + app/code/Magento/CatalogRule/etc/mview.xml | 5 + app/code/Magento/Config/Model/Config.php | 259 +++++++++++++----- .../Config/Test/Unit/Model/ConfigTest.php | 155 +++++++---- .../Model/Indexer/DependencyDecorator.php | 10 +- .../Catalog/Model/ProductPriceTest.php | 39 ++- .../ResourceModel/Product/CollectionTest.php | 54 ++-- .../Model/Indexer/Product/PriceTest.php | 37 +-- .../_files/configurable_product.php | 68 +++++ .../_files/configurable_product_rollback.php | 31 +++ .../CatalogRule/_files/simple_products.php | 54 ++++ .../_files/simple_products_rollback.php | 32 +++ .../product_downloadable_with_files.php | 7 + .../Framework/Indexer/Config/Reader.php | 1 + 21 files changed, 856 insertions(+), 198 deletions(-) create mode 100644 app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php index f1dc9715fd247..53f00529b9bcc 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php @@ -119,7 +119,7 @@ protected function _construct() * @param int $websiteId * @return array */ - public function lockProductsStock(array $productIds, int $websiteId) + public function lockProductsStock(array $productIds, $websiteId) { if (empty($productIds)) { return []; @@ -206,6 +206,8 @@ protected function _initConfig() /** * Set items out of stock basing on their quantities and config settings * + * @deprecated + * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateSetOutOfStock * @param string|int $website * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return void @@ -241,6 +243,8 @@ public function updateSetOutOfStock($website = null) /** * Set items in stock basing on their quantities and config settings * + * @deprecated + * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateSetInStock * @param int|string $website * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return void @@ -274,6 +278,8 @@ public function updateSetInStock($website) /** * Update items low stock date basing on their quantities and config settings * + * @deprecated + * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateLowStockDate * @param int|string $website * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return void diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php index 895fffaa4f80b..22562461c2860 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php @@ -6,10 +6,14 @@ namespace Magento\CatalogInventory\Model\ResourceModel\Stock; use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\CatalogInventory\Model\Stock; use Magento\CatalogInventory\Model\Indexer\Stock\Processor; -use Magento\Framework\App\ResourceConnection as AppResource; use Magento\Framework\Model\AbstractModel; -use Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Framework\DB\Select; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Stdlib\DateTime\DateTime; /** * Stock item resource model @@ -29,17 +33,36 @@ class Item extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb protected $stockIndexerProcessor; /** - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context + * @var StockConfigurationInterface + */ + private $stockConfiguration; + + /** + * @var DateTime + */ + private $dateTime; + + /** + * @param Context $context * @param Processor $processor * @param string $connectionName + * @param StockConfigurationInterface $stockConfiguration + * @param DateTime $dateTime */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, + Context $context, Processor $processor, - $connectionName = null + $connectionName = null, + StockConfigurationInterface $stockConfiguration = null, + DateTime $dateTime = null ) { $this->stockIndexerProcessor = $processor; parent::__construct($context, $connectionName); + + $this->stockConfiguration = $stockConfiguration ?? + ObjectManager::getInstance()->get(StockConfigurationInterface::class); + $this->dateTime = $dateTime ?? + ObjectManager::getInstance()->get(DateTime::class); } /** @@ -139,4 +162,160 @@ public function setProcessIndexEvents($process = true) $this->processIndexEvents = $process; return $this; } + + /** + * Set items out of stock basing on their quantities and config settings + * + * @param int $websiteId + * @return void + */ + public function updateSetOutOfStock(int $websiteId) + { + $connection = $this->getConnection(); + + $values = [ + 'is_in_stock' => Stock::STOCK_OUT_OF_STOCK, + 'stock_status_changed_auto' => 1, + ]; + $select = $this->buildProductsSelectByConfigTypes(); + $where = [ + 'website_id = ' . $websiteId, + 'is_in_stock = ' . Stock::STOCK_IN_STOCK, + '(use_config_manage_stock = 1 AND 1 = ' . $this->stockConfiguration->getManageStock() . ')' + . ' OR (use_config_manage_stock = 0 AND manage_stock = 1)', + '(use_config_min_qty = 1 AND qty <= ' . $this->stockConfiguration->getMinQty() . ')' + . ' OR (use_config_min_qty = 0 AND qty <= min_qty)', + 'product_id IN (' . $select->assemble() . ')', + ]; + $backordersWhere = '(use_config_backorders = 0 AND backorders = ' . Stock::BACKORDERS_NO . ')'; + if (Stock::BACKORDERS_NO == $this->stockConfiguration->getBackorders()) { + $where[] = $backordersWhere . ' OR use_config_backorders = 1'; + } else { + $where[] = $backordersWhere; + } + $connection->update($this->getMainTable(), $values, $where); + + $this->stockIndexerProcessor->markIndexerAsInvalid(); + } + + /** + * Set items in stock basing on their quantities and config settings + * + * @param int $websiteId + * @return void + */ + public function updateSetInStock(int $websiteId) + { + $connection = $this->getConnection(); + + $values = [ + 'is_in_stock' => Stock::STOCK_IN_STOCK, + ]; + $select = $this->buildProductsSelectByConfigTypes(); + $where = [ + 'website_id = ' . $websiteId, + 'stock_status_changed_auto = 1', + '(use_config_min_qty = 1 AND qty > ' . $this->stockConfiguration->getMinQty() . ')' + . ' OR (use_config_min_qty = 0 AND qty > min_qty)', + 'product_id IN (' . $select->assemble() . ')', + ]; + $manageStockWhere = '(use_config_manage_stock = 0 AND manage_stock = 1)'; + if ($this->stockConfiguration->getManageStock()) { + $where[] = $manageStockWhere . ' OR use_config_manage_stock = 1'; + } else { + $where[] = $manageStockWhere; + } + $connection->update($this->getMainTable(), $values, $where); + + $this->stockIndexerProcessor->markIndexerAsInvalid(); + } + + /** + * Update items low stock date basing on their quantities and config settings + * + * @param int $websiteId + * @return void + */ + public function updateLowStockDate(int $websiteId) + { + $connection = $this->getConnection(); + + $condition = $connection->quoteInto( + '(use_config_notify_stock_qty = 1 AND qty < ?)', + $this->stockConfiguration->getNotifyStockQty() + ) . ' OR (use_config_notify_stock_qty = 0 AND qty < notify_stock_qty)'; + $currentDbTime = $connection->quoteInto('?', $this->dateTime->gmtDate()); + $conditionalDate = $connection->getCheckSql($condition, $currentDbTime, 'NULL'); + $value = [ + 'low_stock_date' => new \Zend_Db_Expr($conditionalDate), + ]; + $select = $this->buildProductsSelectByConfigTypes(); + $where = [ + 'website_id = ' . $websiteId, + 'product_id IN (' . $select->assemble() . ')' + ]; + $manageStockWhere = '(use_config_manage_stock = 0 AND manage_stock = 1)'; + if ($this->stockConfiguration->getManageStock()) { + $where[] = $manageStockWhere . ' OR use_config_manage_stock = 1'; + } else { + $where[] = $manageStockWhere; + } + $connection->update($this->getMainTable(), $value, $where); + } + + public function getManageStockExpr(string $tableAlias = ''): \Zend_Db_Expr + { + if ($tableAlias) { + $tableAlias .= '.'; + } + $manageStock = $this->getConnection()->getCheckSql( + $tableAlias . 'use_config_manage_stock = 1', + $this->stockConfiguration->getManageStock(), + $tableAlias . 'manage_stock' + ); + + return $manageStock; + } + + public function getBackordersExpr(string $tableAlias = ''): \Zend_Db_Expr + { + if ($tableAlias) { + $tableAlias .= '.'; + } + $itemBackorders = $this->getConnection()->getCheckSql( + $tableAlias . 'use_config_backorders = 1', + $this->stockConfiguration->getBackorders(), + $tableAlias . 'backorders' + ); + + return $itemBackorders; + } + + public function getMinSaleQtyExpr(string $tableAlias = ''): \Zend_Db_Expr + { + if ($tableAlias) { + $tableAlias .= '.'; + } + $itemMinSaleQty = $this->getConnection()->getCheckSql( + $tableAlias . 'use_config_min_sale_qty = 1', + $this->stockConfiguration->getMinSaleQty(), + $tableAlias . 'min_sale_qty' + ); + + return $itemMinSaleQty; + } + + /** + * Build select for products with types from config + * + * @return Select + */ + private function buildProductsSelectByConfigTypes(): Select + { + $select = $this->getConnection()->select() + ->from($this->getTable('catalog_product_entity'), 'entity_id') + ->where('type_id IN (?)', array_keys($this->stockConfiguration->getIsQtyTypeIds(true))); + + return $select; + } } diff --git a/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php b/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php new file mode 100644 index 0000000000000..976110ec76cf3 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogInventory\Observer; + +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Event\Observer; +use Magento\CatalogInventory\Model\Configuration; +use Magento\Catalog\Model\Indexer\Product\Price\Processor; + +/** + * Catalog inventory config changes module observer. + */ +class InvalidatePriceIndexUponConfigChangeObserver implements ObserverInterface +{ + /** + * @var Processor + */ + private $priceIndexProcessor; + + /** + * @param Processor $priceIndexProcessor + */ + public function __construct(Processor $priceIndexProcessor) + { + $this->priceIndexProcessor = $priceIndexProcessor; + } + + /** + * Invalidate product price index on catalog inventory config changes. + * + * @param Observer $observer + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function execute(Observer $observer) + { + $changedPaths = (array) $observer->getEvent()->getChangedPaths(); + + if (\in_array(Configuration::XML_PATH_SHOW_OUT_OF_STOCK, $changedPaths, true)) { + $priceIndexer = $this->priceIndexProcessor->getIndexer(); + $priceIndexer->invalidate(); + } + } +} diff --git a/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php b/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php index 47ee6512920d8..21c78eca93695 100644 --- a/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php @@ -3,11 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\CatalogInventory\Observer; use Magento\Framework\Event\Observer as EventObserver; use Magento\Framework\Event\ObserverInterface; +use Magento\CatalogInventory\Model\Configuration; +use Magento\CatalogInventory\Model\ResourceModel\Stock\Item; /** * Catalog inventory module observer @@ -15,16 +16,16 @@ class UpdateItemsStockUponConfigChangeObserver implements ObserverInterface { /** - * @var \Magento\CatalogInventory\Model\ResourceModel\Stock + * @var Item */ - protected $resourceStock; + protected $resourceStockItem; /** - * @param \Magento\CatalogInventory\Model\ResourceModel\Stock $resourceStock + * @param Item $resourceStockItem */ - public function __construct(\Magento\CatalogInventory\Model\ResourceModel\Stock $resourceStock) + public function __construct(Item $resourceStockItem) { - $this->resourceStock = $resourceStock; + $this->resourceStockItem = $resourceStockItem; } /** @@ -35,9 +36,18 @@ public function __construct(\Magento\CatalogInventory\Model\ResourceModel\Stock */ public function execute(EventObserver $observer) { - $website = $observer->getEvent()->getWebsite(); - $this->resourceStock->updateSetOutOfStock($website); - $this->resourceStock->updateSetInStock($website); - $this->resourceStock->updateLowStockDate($website); + $website = (int) $observer->getEvent()->getWebsite(); + $changedPaths = (array) $observer->getEvent()->getChangedPaths(); + + if (\array_intersect([ + Configuration::XML_PATH_MANAGE_STOCK, + Configuration::XML_PATH_MIN_QTY, + Configuration::XML_PATH_BACKORDERS, + Configuration::XML_PATH_NOTIFY_STOCK_QTY, + ], $changedPaths)) { + $this->resourceStockItem->updateSetOutOfStock($website); + $this->resourceStockItem->updateSetInStock($website); + $this->resourceStockItem->updateLowStockDate($website); + } } } diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php index 70a179b484379..7b82b5927d22c 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php @@ -15,9 +15,9 @@ class UpdateItemsStockUponConfigChangeObserverTest extends \PHPUnit\Framework\Te protected $observer; /** - * @var \Magento\CatalogInventory\Model\ResourceModel\Stock|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\CatalogInventory\Model\ResourceModel\Stock\Item|\PHPUnit_Framework_MockObject_MockObject */ - protected $resourceStock; + protected $resourceStockItem; /** * @var \Magento\Framework\Event|\PHPUnit_Framework_MockObject_MockObject @@ -31,11 +31,11 @@ class UpdateItemsStockUponConfigChangeObserverTest extends \PHPUnit\Framework\Te protected function setUp() { - $this->resourceStock = $this->createMock(\Magento\CatalogInventory\Model\ResourceModel\Stock::class); + $this->resourceStockItem = $this->createMock(\Magento\CatalogInventory\Model\ResourceModel\Stock\Item::class); $this->event = $this->getMockBuilder(\Magento\Framework\Event::class) ->disableOriginalConstructor() - ->setMethods(['getWebsite']) + ->setMethods(['getWebsite', 'getChangedPaths']) ->getMock(); $this->eventObserver = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) @@ -50,7 +50,7 @@ protected function setUp() $this->observer = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( \Magento\CatalogInventory\Observer\UpdateItemsStockUponConfigChangeObserver::class, [ - 'resourceStock' => $this->resourceStock, + 'resourceStockItem' => $this->resourceStockItem, ] ); } @@ -58,13 +58,16 @@ protected function setUp() public function testUpdateItemsStockUponConfigChange() { $websiteId = 1; - $this->resourceStock->expects($this->once())->method('updateSetOutOfStock'); - $this->resourceStock->expects($this->once())->method('updateSetInStock'); - $this->resourceStock->expects($this->once())->method('updateLowStockDate'); + $this->resourceStockItem->expects($this->once())->method('updateSetOutOfStock'); + $this->resourceStockItem->expects($this->once())->method('updateSetInStock'); + $this->resourceStockItem->expects($this->once())->method('updateLowStockDate'); $this->event->expects($this->once()) ->method('getWebsite') ->will($this->returnValue($websiteId)); + $this->event->expects($this->once()) + ->method('getChangedPaths') + ->will($this->returnValue([\Magento\CatalogInventory\Model\Configuration::XML_PATH_MANAGE_STOCK])); $this->observer->execute($this->eventObserver); } diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml index 7cddb4f8f0b54..912ab48d28824 100644 --- a/app/code/Magento/CatalogInventory/etc/di.xml +++ b/app/code/Magento/CatalogInventory/etc/di.xml @@ -132,7 +132,6 @@ </argument> </arguments> </type> - <type name="Magento\CatalogInventory\Model\ResourceModel\Stock\Item"> <plugin name="priceIndexUpdater" type="Magento\CatalogInventory\Model\Plugin\PriceIndexUpdater" /> </type> diff --git a/app/code/Magento/CatalogInventory/etc/events.xml b/app/code/Magento/CatalogInventory/etc/events.xml index 3197501e9b70b..328edbade6068 100644 --- a/app/code/Magento/CatalogInventory/etc/events.xml +++ b/app/code/Magento/CatalogInventory/etc/events.xml @@ -38,6 +38,7 @@ </event> <event name="admin_system_config_changed_section_cataloginventory"> <observer name="inventory" instance="Magento\CatalogInventory\Observer\UpdateItemsStockUponConfigChangeObserver"/> + <observer name="invalidatePriceIndex" instance="Magento\CatalogInventory\Observer\InvalidatePriceIndexUponConfigChangeObserver"/> </event> <event name="sales_quote_item_collection_products_after_load"> <observer name="add_stock_items" instance="Magento\CatalogInventory\Observer\AddStockItemsObserver"/> diff --git a/app/code/Magento/CatalogInventory/etc/mview.xml b/app/code/Magento/CatalogInventory/etc/mview.xml index c3d73ff43e8eb..72dda16e8b5bb 100644 --- a/app/code/Magento/CatalogInventory/etc/mview.xml +++ b/app/code/Magento/CatalogInventory/etc/mview.xml @@ -11,4 +11,9 @@ <table name="cataloginventory_stock_item" entity_column="product_id" /> </subscriptions> </view> + <view id="catalog_product_price" class="Magento\Catalog\Model\Indexer\Product\Price" group="indexer"> + <subscriptions> + <table name="cataloginventory_stock_item" entity_column="product_id" /> + </subscriptions> + </view> </config> diff --git a/app/code/Magento/CatalogRule/etc/mview.xml b/app/code/Magento/CatalogRule/etc/mview.xml index 4b1166941bdc8..35efe33461afc 100644 --- a/app/code/Magento/CatalogRule/etc/mview.xml +++ b/app/code/Magento/CatalogRule/etc/mview.xml @@ -24,4 +24,9 @@ <table name="catalog_category_product" entity_column="product_id" /> </subscriptions> </view> + <view id="catalog_product_price" class="Magento\Catalog\Model\Indexer\Product\Price" group="indexer"> + <subscriptions> + <table name="catalogrule_product_price" entity_column="product_id" /> + </subscriptions> + </view> </config> diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php index bc1515aadb0ca..c6e2412f7e58f 100644 --- a/app/code/Magento/Config/Model/Config.php +++ b/app/code/Magento/Config/Model/Config.php @@ -5,6 +5,11 @@ */ namespace Magento\Config\Model; +use Magento\Config\Model\Config\Reader\Source\Deployed\SettingChecker; +use Magento\Config\Model\Config\Structure\Element\Group; +use Magento\Config\Model\Config\Structure\Element\Field; +use Magento\Framework\App\ObjectManager; + /** * Backend config model * Used to save configuration @@ -77,6 +82,11 @@ class Config extends \Magento\Framework\DataObject */ protected $_storeManager; + /** + * @var Config\Reader\Source\Deployed\SettingChecker + */ + private $settingChecker; + /** * @param \Magento\Framework\App\Config\ReinitableConfigInterface $config * @param \Magento\Framework\Event\ManagerInterface $eventManager @@ -85,6 +95,7 @@ class Config extends \Magento\Framework\DataObject * @param \Magento\Config\Model\Config\Loader $configLoader * @param \Magento\Framework\App\Config\ValueFactory $configValueFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param Config\Reader\Source\Deployed\SettingChecker|null $settingChecker * @param array $data */ public function __construct( @@ -95,6 +106,7 @@ public function __construct( \Magento\Config\Model\Config\Loader $configLoader, \Magento\Framework\App\Config\ValueFactory $configValueFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, + SettingChecker $settingChecker = null, array $data = [] ) { parent::__construct($data); @@ -105,6 +117,7 @@ public function __construct( $this->_configLoader = $configLoader; $this->_configValueFactory = $configValueFactory; $this->_storeManager = $storeManager; + $this->settingChecker = $settingChecker ?: ObjectManager::getInstance()->get(SettingChecker::class); } /** @@ -126,11 +139,12 @@ public function save() $oldConfig = $this->_getConfig(true); + /** @var \Magento\Framework\DB\Transaction $deleteTransaction */ $deleteTransaction = $this->_transactionFactory->create(); - /* @var $deleteTransaction \Magento\Framework\DB\Transaction */ + /** @var \Magento\Framework\DB\Transaction $saveTransaction */ $saveTransaction = $this->_transactionFactory->create(); - /* @var $saveTransaction \Magento\Framework\DB\Transaction */ + $changedPaths = []; // Extends for old config data $extraOldGroups = []; @@ -145,6 +159,9 @@ public function save() $saveTransaction, $deleteTransaction ); + + $groupChangedPaths = $this->getChangedPaths($sectionId, $groupId, $groupData, $oldConfig, $extraOldGroups); + $changedPaths = \array_merge($changedPaths, $groupChangedPaths); } try { @@ -157,7 +174,11 @@ public function save() // website and store codes can be used in event implementation, so set them as well $this->_eventManager->dispatch( "admin_system_config_changed_section_{$this->getSection()}", - ['website' => $this->getWebsite(), 'store' => $this->getStore()] + [ + 'website' => $this->getWebsite(), + 'store' => $this->getStore(), + 'changed_paths' => $changedPaths, + ] ); } catch (\Exception $e) { // re-init configuration @@ -168,6 +189,144 @@ public function save() return $this; } + /** + * Map field name if they were cloned + * + * @param Group $group + * @param string $fieldId + * @return string + */ + private function getOriginalFieldId(Group $group, string $fieldId): string + { + if ($group->shouldCloneFields()) { + $cloneModel = $group->getCloneModel(); + + /** @var \Magento\Config\Model\Config\Structure\Element\Field $field */ + foreach ($group->getChildren() as $field) { + foreach ($cloneModel->getPrefixes() as $prefix) { + if ($prefix['field'] . $field->getId() === $fieldId) { + $fieldId = $field->getId(); + break(2); + } + } + } + } + + return $fieldId; + } + + /** + * Get field object + * + * @param string $sectionId + * @param string $groupId + * @param string $fieldId + * @return Field + */ + private function getField(string $sectionId, string $groupId, string $fieldId): Field + { + /** @var \Magento\Config\Model\Config\Structure\Element\Group $group */ + $group = $this->_configStructure->getElement($sectionId . '/' . $groupId); + $fieldPath = $group->getPath() . '/' . $this->getOriginalFieldId($group, $fieldId); + $field = $this->_configStructure->getElement($fieldPath); + + return $field; + } + + /** + * Get field path + * + * @param Field $field + * @param array &$oldConfig Need for compatibility with _processGroup() + * @param array &$extraOldGroups Need for compatibility with _processGroup() + * @return string + */ + private function getFieldPath(Field $field, array &$oldConfig, array &$extraOldGroups): string + { + $path = $field->getGroupPath() . '/' . $field->getId(); + + /** + * Look for custom defined field path + */ + $configPath = $field->getConfigPath(); + if ($configPath && strrpos($configPath, '/') > 0) { + // Extend old data with specified section group + $configGroupPath = substr($configPath, 0, strrpos($configPath, '/')); + if (!isset($extraOldGroups[$configGroupPath])) { + $oldConfig = $this->extendConfig($configGroupPath, true, $oldConfig); + $extraOldGroups[$configGroupPath] = true; + } + $path = $configPath; + } + + return $path; + } + + /** + * Check is config value changed + * + * @param array $oldConfig + * @param string $path + * @param array $fieldData + * @return bool + */ + private function isValueChanged(array $oldConfig, string $path, array $fieldData): bool + { + if (isset($oldConfig[$path]['value'])) { + $result = !isset($fieldData['value']) || $oldConfig[$path]['value'] !== $fieldData['value']; + } else { + $result = empty($fieldData['inherit']); + } + + return $result; + } + + /** + * Get changed paths + * + * @param string $sectionId + * @param string $groupId + * @param array $groupData + * @param array &$oldConfig + * @param array &$extraOldGroups + * @return array + */ + private function getChangedPaths( + string $sectionId, + string $groupId, + array $groupData, + array &$oldConfig, + array &$extraOldGroups + ): array { + $changedPaths = []; + + if (isset($groupData['fields'])) { + foreach ($groupData['fields'] as $fieldId => $fieldData) { + $field = $this->getField($sectionId, $groupId, $fieldId); + $path = $this->getFieldPath($field, $oldConfig, $extraOldGroups); + if ($this->isValueChanged($oldConfig, $path, $fieldData)) { + $changedPaths[] = $path; + } + } + } + + if (isset($groupData['groups'])) { + $subSectionId = $sectionId . '/' . $groupId; + foreach ($groupData['groups'] as $subGroupId => $subGroupData) { + $subGroupChangedPaths = $this->getChangedPaths( + $subSectionId, + $subGroupId, + $subGroupData, + $oldConfig, + $extraOldGroups + ); + $changedPaths = \array_merge($changedPaths, $subGroupChangedPaths); + } + } + + return $changedPaths; + } + /** * Process group data * @@ -182,7 +341,6 @@ public function save() * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function _processGroup( $groupId, @@ -195,92 +353,55 @@ protected function _processGroup( \Magento\Framework\DB\Transaction $deleteTransaction ) { $groupPath = $sectionPath . '/' . $groupId; - $scope = $this->getScope(); - $scopeId = $this->getScopeId(); - $scopeCode = $this->getScopeCode(); - /** - * - * Map field names if they were cloned - */ - /** @var $group \Magento\Config\Model\Config\Structure\Element\Group */ - $group = $this->_configStructure->getElement($groupPath); - // set value for group field entry by fieldname - // use extra memory - $fieldsetData = []; if (isset($groupData['fields'])) { - if ($group->shouldCloneFields()) { - $cloneModel = $group->getCloneModel(); - $mappedFields = []; - - /** @var $field \Magento\Config\Model\Config\Structure\Element\Field */ - foreach ($group->getChildren() as $field) { - foreach ($cloneModel->getPrefixes() as $prefix) { - $mappedFields[$prefix['field'] . $field->getId()] = $field->getId(); - } - } - } + /** @var \Magento\Config\Model\Config\Structure\Element\Group $group */ + $group = $this->_configStructure->getElement($groupPath); + + // set value for group field entry by fieldname + // use extra memory + $fieldsetData = []; foreach ($groupData['fields'] as $fieldId => $fieldData) { - $fieldsetData[$fieldId] = is_array( - $fieldData - ) && isset( - $fieldData['value'] - ) ? $fieldData['value'] : null; + $fieldsetData[$fieldId] = $fieldData['value'] ?? null; } foreach ($groupData['fields'] as $fieldId => $fieldData) { - $originalFieldId = $fieldId; - if ($group->shouldCloneFields() && isset($mappedFields[$fieldId])) { - $originalFieldId = $mappedFields[$fieldId]; + $isReadOnly = $this->settingChecker->isReadOnly( + $groupPath . '/' . $fieldId, + $this->getScope(), + $this->getScopeCode() + ); + + if ($isReadOnly) { + continue; } - /** @var $field \Magento\Config\Model\Config\Structure\Element\Field */ - $field = $this->_configStructure->getElement($groupPath . '/' . $originalFieldId); + $field = $this->getField($sectionPath, $groupId, $fieldId); /** @var \Magento\Framework\App\Config\ValueInterface $backendModel */ - $backendModel = $field->hasBackendModel() ? $field - ->getBackendModel() : $this - ->_configValueFactory - ->create(); + $backendModel = $field->hasBackendModel() + ? $field->getBackendModel() + : $this->_configValueFactory->create(); + if (!isset($fieldData['value'])) { + $fieldData['value'] = null; + } $data = [ 'field' => $fieldId, 'groups' => $groups, 'group_id' => $group->getId(), - 'scope' => $scope, - 'scope_id' => $scopeId, - 'scope_code' => $scopeCode, + 'scope' => $this->getScope(), + 'scope_id' => $this->getScopeId(), + 'scope_code' => $this->getScopeCode(), 'field_config' => $field->getData(), - 'fieldset_data' => $fieldsetData + 'fieldset_data' => $fieldsetData, ]; $backendModel->addData($data); - $this->_checkSingleStoreMode($field, $backendModel); - if (false == isset($fieldData['value'])) { - $fieldData['value'] = null; - } - - $path = $field->getGroupPath() . '/' . $fieldId; - /** - * Look for custom defined field path - */ - if ($field && $field->getConfigPath()) { - $configPath = $field->getConfigPath(); - if (!empty($configPath) && strrpos($configPath, '/') > 0) { - // Extend old data with specified section group - $configGroupPath = substr($configPath, 0, strrpos($configPath, '/')); - if (!isset($extraOldGroups[$configGroupPath])) { - $oldConfig = $this->extendConfig($configGroupPath, true, $oldConfig); - $extraOldGroups[$configGroupPath] = true; - } - $path = $configPath; - } - } - - $inherit = !empty($fieldData['inherit']); - + $path = $this->getFieldPath($field, $extraOldGroups, $oldConfig); $backendModel->setPath($path)->setValue($fieldData['value']); + $inherit = !empty($fieldData['inherit']); if (isset($oldConfig[$path])) { $backendModel->setConfigId($oldConfig[$path]['config_id']); diff --git a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php index 2ddbbd5ffe1e8..d0568f48ded1e 100644 --- a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php @@ -60,6 +60,11 @@ class ConfigTest extends \PHPUnit\Framework\TestCase */ protected $_configStructure; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $_settingsChecker; + protected function setUp() { $this->_eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); @@ -79,7 +84,7 @@ protected function setUp() $this->_transFactoryMock = $this->createPartialMock( \Magento\Framework\DB\TransactionFactory::class, - ['create'] + ['create', 'addObject'] ); $this->_appConfigMock = $this->createMock(\Magento\Framework\App\Config\ReinitableConfigInterface::class); $this->_configLoaderMock = $this->createPartialMock( @@ -90,6 +95,9 @@ protected function setUp() $this->_storeManager = $this->getMockForAbstractClass(\Magento\Store\Model\StoreManagerInterface::class); + $this->_settingsChecker = $this + ->createMock(\Magento\Config\Model\Config\Reader\Source\Deployed\SettingChecker::class); + $this->_model = new \Magento\Config\Model\Config( $this->_appConfigMock, $this->_eventManagerMock, @@ -97,7 +105,8 @@ protected function setUp() $this->_transFactoryMock, $this->_configLoaderMock, $this->_dataFactoryMock, - $this->_storeManager + $this->_storeManager, + $this->_settingsChecker ); } @@ -149,55 +158,96 @@ public function testSaveToCheckAdminSystemConfigChangedSectionEvent() $this->_model->save(); } - public function testSaveToCheckScopeDataSet() + public function testDoNotSaveReadOnlyFields() { $transactionMock = $this->createMock(\Magento\Framework\DB\Transaction::class); - $this->_transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock)); + $this->_settingsChecker->expects($this->any())->method('isReadOnly')->will($this->returnValue(true)); $this->_configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([])); - $this->_eventManagerMock->expects( - $this->at(0) - )->method( - 'dispatch' - )->with( - $this->equalTo('admin_system_config_changed_section_'), - $this->arrayHasKey('website') - ); - - $this->_eventManagerMock->expects( - $this->at(0) - )->method( - 'dispatch' - )->with( - $this->equalTo('admin_system_config_changed_section_'), - $this->arrayHasKey('store') - ); + $this->_model->setGroups(['1' => ['fields' => ['key' => ['data']]]]); + $this->_model->setSection('section'); $group = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Group::class); + $group->method('getPath')->willReturn('section/1'); $field = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Field::class); + $field->method('getGroupPath')->willReturn('section/1'); + $field->method('getId')->willReturn('key'); + + $this->_configStructure->expects($this->at(0)) + ->method('getElement') + ->with('section/1') + ->will($this->returnValue($group)); + $this->_configStructure->expects($this->at(1)) + ->method('getElement') + ->with('section/1') + ->will($this->returnValue($group)); + $this->_configStructure->expects($this->at(2)) + ->method('getElement') + ->with('section/1/key') + ->will($this->returnValue($field)); - $this->_configStructure->expects( - $this->at(0) - )->method( - 'getElement' - )->with( - '/1' - )->will( - $this->returnValue($group) + $backendModel = $this->createPartialMock( + \Magento\Framework\App\Config\Value::class, + ['addData'] ); + $this->_dataFactoryMock->expects($this->any())->method('create')->will($this->returnValue($backendModel)); - $this->_configStructure->expects( - $this->at(1) - )->method( - 'getElement' - )->with( - '/1/key' - )->will( - $this->returnValue($field) - ); + $this->_transFactoryMock->expects($this->never())->method('addObject'); + $backendModel->expects($this->never())->method('addData'); + + $this->_model->save(); + } + + public function testSaveToCheckScopeDataSet() + { + $transactionMock = $this->createMock(\Magento\Framework\DB\Transaction::class); + $this->_transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock)); + + $this->_configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([])); + + $this->_eventManagerMock->expects($this->at(0)) + ->method('dispatch') + ->with( + $this->equalTo('admin_system_config_changed_section_section'), + $this->arrayHasKey('website') + ); + $this->_eventManagerMock->expects($this->at(0)) + ->method('dispatch') + ->with( + $this->equalTo('admin_system_config_changed_section_section'), + $this->arrayHasKey('store') + ); + + $group = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Group::class); + $group->method('getPath')->willReturn('section/1'); + + $field = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Field::class); + $field->method('getGroupPath')->willReturn('section/1'); + $field->method('getId')->willReturn('key'); + + $this->_configStructure->expects($this->at(0)) + ->method('getElement') + ->with('section/1') + ->will($this->returnValue($group)); + $this->_configStructure->expects($this->at(1)) + ->method('getElement') + ->with('section/1') + ->will($this->returnValue($group)); + $this->_configStructure->expects($this->at(2)) + ->method('getElement') + ->with('section/1/key') + ->will($this->returnValue($field)); + $this->_configStructure->expects($this->at(3)) + ->method('getElement') + ->with('section/1') + ->will($this->returnValue($group)); + $this->_configStructure->expects($this->at(4)) + ->method('getElement') + ->with('section/1/key') + ->will($this->returnValue($field)); $website = $this->createMock(\Magento\Store\Model\Website::class); $website->expects($this->any())->method('getCode')->will($this->returnValue('website_code')); @@ -206,19 +256,16 @@ public function testSaveToCheckScopeDataSet() $this->_storeManager->expects($this->any())->method('isSingleStoreMode')->will($this->returnValue(true)); $this->_model->setWebsite('website'); - + $this->_model->setSection('section'); $this->_model->setGroups(['1' => ['fields' => ['key' => ['data']]]]); $backendModel = $this->createPartialMock( \Magento\Framework\App\Config\Value::class, ['setPath', 'addData', '__sleep', '__wakeup'] ); - $backendModel->expects( - $this->once() - )->method( - 'addData' - )->with( - [ + $backendModel->expects($this->once()) + ->method('addData') + ->with([ 'field' => 'key', 'groups' => [1 => ['fields' => ['key' => ['data']]]], 'group_id' => null, @@ -227,17 +274,11 @@ public function testSaveToCheckScopeDataSet() 'scope_code' => 'website_code', 'field_config' => null, 'fieldset_data' => ['key' => null], - ] - ); - $backendModel->expects( - $this->once() - )->method( - 'setPath' - )->with( - '/key' - )->will( - $this->returnValue($backendModel) - ); + ]); + $backendModel->expects($this->once()) + ->method('setPath') + ->with('section/1/key') + ->will($this->returnValue($backendModel)); $this->_dataFactoryMock->expects($this->any())->method('create')->will($this->returnValue($backendModel)); @@ -295,7 +336,7 @@ public function setDataByPathWrongDepthDataProvider() 'depth 2' => ['section/group', "Your configuration depth is 2 for path 'section/group'"], 'depth 1' => ['section', "Your configuration depth is 1 for path 'section'"], 'depth 4' => ['section/group/field/sub-field', "Your configuration depth is 4 for path" - . " 'section/group/field/sub-field'", ], + . " 'section/group/field/sub-field'", ], ]; } } diff --git a/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php b/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php index d22795f127138..829df74a1b0ed 100644 --- a/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php +++ b/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php @@ -256,7 +256,10 @@ public function reindexRow($id) $this->indexer->reindexRow($id); $dependentIndexerIds = $this->dependencyInfoProvider->getIndexerIdsToRunAfter($this->indexer->getId()); foreach ($dependentIndexerIds as $indexerId) { - $this->indexerRegistry->get($indexerId)->reindexRow($id); + $dependentIndexer = $this->indexerRegistry->get($indexerId); + if (!$dependentIndexer->isScheduled()) { + $dependentIndexer->reindexRow($id); + } } } @@ -268,7 +271,10 @@ public function reindexList($ids) $this->indexer->reindexList($ids); $dependentIndexerIds = $this->dependencyInfoProvider->getIndexerIdsToRunAfter($this->indexer->getId()); foreach ($dependentIndexerIds as $indexerId) { - $this->indexerRegistry->get($indexerId)->reindexList($ids); + $dependentIndexer = $this->indexerRegistry->get($indexerId); + if (!$dependentIndexer->isScheduled()) { + $dependentIndexer->reindexList($ids); + } } } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php index 594133e984a46..088a86ef84d07 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php @@ -8,6 +8,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\CatalogInventory\Api\StockRegistryInterface; /** * Tests product model: @@ -23,9 +24,15 @@ class ProductPriceTest extends \PHPUnit\Framework\TestCase */ protected $_model; + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + protected function setUp() { $this->_model = Bootstrap::getObjectManager()->create(Product::class); + $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); } public function testGetPrice() @@ -77,18 +84,40 @@ public function testSetGetFinalPrice() /** * @magentoDbIsolation disabled * @magentoDataFixture Magento/Catalog/_files/product_with_options.php - * @return void */ - public function testGetMinPrice(): void + public function testGetMinPrice() { - $productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); - $product = $productRepository->get('simple'); + $product = $this->productRepository->get('simple'); $collection = Bootstrap::getObjectManager()->create(Collection::class); $collection->addIdFilter($product->getId()); $collection->addPriceData(); $collection->load(); /** @var \Magento\Catalog\Model\Product $product */ $product = $collection->getFirstItem(); - $this->assertEquals(333, $product->getData('min_price')); + $this->assertEquals(323, $product->getData('min_price')); + } + + /** + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php + */ + public function testGetMinPriceForComposite() + { + $confProduct = $this->productRepository->get('configurable'); + $collection = Bootstrap::getObjectManager()->create(Collection::class); + $collection->addIdFilter($confProduct->getId()); + $collection->addPriceData(); + $collection->load(); + $product = $collection->getFirstItem(); + $this->assertEquals(10, $product->getData('min_price')); + + $childProduct = $this->productRepository->get('simple_10'); + $stockRegistry = Bootstrap::getObjectManager()->get(StockRegistryInterface::class); + $stockItem = $stockRegistry->getStockItem($childProduct->getId()); + $stockItem->setIsInStock(false); + $stockRegistry->updateStockItemBySku($childProduct->getSku(), $stockItem); + $collection->clear()->load(); + $product = $collection->getFirstItem(); + $this->assertEquals(20, $product->getData('min_price')); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php index e58d445a25650..a5e55e181cbaf 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php @@ -50,6 +50,7 @@ public function testAddPriceDataOnSchedule() { $this->processor->getIndexer()->setScheduled(true); $this->assertTrue($this->processor->getIndexer()->isScheduled()); + $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ @@ -81,7 +82,6 @@ public function testAddPriceDataOnSchedule() $product = reset($items); $this->assertCount(2, $items); $this->assertEquals(15, $product->getPrice()); - $this->processor->getIndexer()->reindexList([1]); $this->processor->getIndexer()->setScheduled(false); } @@ -127,6 +127,42 @@ public function testGetProductsWithTierPrice() $this->assertEquals(5, $tierPrices[2]->getValue()); } + /** + * Test addAttributeToSort() with attribute 'is_saleable' works properly on frontend. + * + * @dataProvider addAttributeToSortDataProvider + * @magentoDataFixture Magento/Catalog/_files/multiple_products_with_non_saleable_product.php + * @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 1 + * @magentoAppIsolation enabled + * @magentoAppArea frontend + */ + public function testAddAttributeToSort(string $productSku, string $order) + { + /** @var Collection $productCollection */ + $this->collection->addAttributeToSort('is_saleable', $order); + self::assertEquals(2, $this->collection->count()); + self::assertSame($productSku, $this->collection->getFirstItem()->getSku()); + } + + /** + * Provide test data for testAddAttributeToSort(). + * + * @return array + */ + public function addAttributeToSortDataProvider() + { + return [ + [ + 'product_sku' => 'simple_saleable', + 'order' => Collection::SORT_ORDER_DESC, + ], + [ + 'product_sku' => 'simple_not_saleable', + 'order' => Collection::SORT_ORDER_ASC, + ] + ]; + } + /** * Checks a case if table for join specified as an array. * @@ -151,20 +187,4 @@ public function testJoinTable() self::assertContains($expected, str_replace(PHP_EOL, '', $sql)); } - - /** - * Checks that a collection uses the correct join when filtering by null. - * - * This actually affects AbstractCollection, but inheritance yada yada. - * - * @magentoDataFixture Magento/Catalog/Model/ResourceModel/_files/product_simple.php - * @magentoDbIsolation enabled - */ - public function testFilterByNull() - { - $this->collection->addAttributeToFilter([['attribute' => 'special_price', 'null' => true]]); - $productCount = $this->collection->count(); - - $this->assertEquals(1, $productCount, 'Product with null special_price not found'); - } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php index b1a10c894f83a..495d19a2745e5 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php @@ -3,8 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); - namespace Magento\CatalogRule\Model\Indexer\Product; use Magento\TestFramework\Helper\Bootstrap; @@ -14,13 +12,6 @@ use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SortOrder; -/** - * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/attribute.php - * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/product_with_attribute.php - * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled - */ class PriceTest extends \PHPUnit\Framework\TestCase { /** @@ -28,18 +19,18 @@ class PriceTest extends \PHPUnit\Framework\TestCase */ private $resourceRule; - /** - * {@inheritdoc} - */ protected function setUp() { $this->resourceRule = Bootstrap::getObjectManager()->get(Rule::class); } /** - * @return void + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/configurable_product.php + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled */ - public function testPriceApplying() : void + public function testPriceApplying() { $customerGroupId = 1; $websiteId = 1; @@ -52,7 +43,6 @@ public function testPriceApplying() : void /** @var \Magento\Catalog\Model\Product $simpleProduct */ $simpleProduct = $collection->getFirstItem(); $simpleProduct->setPriceCalculation(false); - $rulePrice = $this->resourceRule->getRulePrice(new \DateTime(), $websiteId, $customerGroupId, $simpleProductId); $this->assertEquals($rulePrice, $simpleProduct->getFinalPrice()); @@ -63,16 +53,17 @@ public function testPriceApplying() : void $collection->load(); /** @var \Magento\Catalog\Model\Product $confProduct */ $confProduct = $collection->getFirstItem(); - - $this->assertEquals($simpleProduct->getMinimalPrice(), $confProduct->getMinimalPrice()); + $this->assertEquals($simpleProduct->getFinalPrice(), $confProduct->getMinimalPrice()); } /** + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/simple_products.php + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled * @magentoAppArea frontend - * - * @return void */ - public function testSortByPrice() : void + public function testSortByPrice() { $searchCriteria = Bootstrap::getObjectManager()->create(SearchCriteriaInterface::class); $sortOrder = Bootstrap::getObjectManager()->create(SortOrder::class); @@ -80,10 +71,10 @@ public function testSortByPrice() : void $searchCriteria->setSortOrders([$sortOrder]); $productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); $searchResults = $productRepository->getList($searchCriteria); - $products = $searchResults->getItems(); + /** @var \Magento\Catalog\Model\Product[] $products */ + $products = array_values($searchResults->getItems()); - /** @var \Magento\Catalog\Model\Product $product1 */ - $product1 = array_values($products)[0]; + $product1 = $products[0]; $product1->setPriceCalculation(false); $this->assertEquals('simple1', $product1->getSku()); $rulePrice = $this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $product1->getId()); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php new file mode 100644 index 0000000000000..971f9f82dc1ec --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute.php'; +require __DIR__ . '/simple_products.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$storeManager = $objectManager->get(\Magento\Store\Model\StoreManager::class); +$store = $storeManager->getStore('default'); +$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +$installer = $objectManager->get(\Magento\Catalog\Setup\CategorySetup::class); +$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default'); +$attributeValues = []; +$associatedProductIds = []; + +$attributeRepository = $objectManager->get(\Magento\Eav\Api\AttributeRepositoryInterface::class); +$attribute = $attributeRepository->get('catalog_product', 'test_configurable'); +$options = $attribute->getOptions(); +array_shift($options); //remove the first option which is empty +foreach (['simple1', 'simple2'] as $sku) { + $option = array_shift($options); + $product = $productRepository->get($sku); + $product->setTestConfigurable($option->getValue()); + $productRepository->save($product); + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} + +$product = $objectManager->create(\Magento\Catalog\Model\Product::class) + ->setTypeId('configurable') + ->setId(666) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData([ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ]); +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; +$optionsFactory = $objectManager->get(\Magento\ConfigurableProduct\Helper\Product\Options\Factory::class); +$configurableOptions = $optionsFactory->create($configurableAttributesData); +$extensionConfigurableAttributes = $product->getExtensionAttributes(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php new file mode 100644 index 0000000000000..d4311d523d3c7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var $objectManager \Magento\TestFramework\ObjectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('configurable', false, null, true); + $productRepository->delete($product); +} catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //Nothing to delete +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/simple_products_rollback.php'; +require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php new file mode 100644 index 0000000000000..2855ebb4c92b3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/attribute.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$storeManager = $objectManager->get(\Magento\Store\Model\StoreManager::class); +$store = $storeManager->getStore('default'); +$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +$installer = $objectManager->get(\Magento\Catalog\Setup\CategorySetup::class); +$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default'); + +$product = $objectManager->create(\Magento\Catalog\Model\Product::class) + ->setTypeId('simple') + ->setId(1) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Simple Product 1') + ->setSku('simple1') + ->setPrice(10) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData([ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ]); +$productRepository->save($product); +$productAction = $objectManager->get(\Magento\Catalog\Model\Product\Action::class); +$productAction->updateAttributes([$product->getId()], ['test_attribute' => 'test_attribute_value'], $store->getId()); + +$product = $objectManager->create(\Magento\Catalog\Model\Product::class) + ->setTypeId('simple') + ->setId(2) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Simple Product 2') + ->setSku('simple2') + ->setPrice(9.9) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData([ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php new file mode 100644 index 0000000000000..a46d1db297e2d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var $objectManager \Magento\TestFramework\ObjectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +foreach (['simple1', 'simple2'] as $sku) { + try { + $product = $productRepository->get($sku, false, null, true); + $productRepository->delete($product); + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //Nothing to delete + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php index cf0da5599914f..86aa61a99e1e8 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php @@ -154,3 +154,10 @@ $product->setTypeHasRequiredOptions(false)->setRequiredOptions(false); } $product->save(); + +$stockRegistry = $objectManager->get(\Magento\CatalogInventory\Api\StockRegistryInterface::class); +$stockItem = $stockRegistry->getStockItem($product->getId()); +$stockItem->setUseConfigManageStock(true); +$stockItem->setQty(100); +$stockItem->setIsInStock(true); +$stockRegistry->updateStockItemBySku($product->getSku(), $stockItem); diff --git a/lib/internal/Magento/Framework/Indexer/Config/Reader.php b/lib/internal/Magento/Framework/Indexer/Config/Reader.php index 6ef22b3b7f796..9ed35ef0e9af5 100644 --- a/lib/internal/Magento/Framework/Indexer/Config/Reader.php +++ b/lib/internal/Magento/Framework/Indexer/Config/Reader.php @@ -18,6 +18,7 @@ class Reader extends \Magento\Framework\Config\Reader\Filesystem '/config/indexer/source' => 'name', '/config/indexer/fieldset' => 'name', '/config/indexer/fieldset/field' => 'name', + '/config/indexer/dependencies/indexer' => 'id', ]; /** From f9cd91d8e362a18d695698a8bf32ec7d01b18c33 Mon Sep 17 00:00:00 2001 From: vzabaznov <vzabaznov@magento.com> Date: Fri, 27 Jul 2018 10:34:08 +0300 Subject: [PATCH 0552/1171] MAGETWO-92606: Deliver all benchmark improvements to 2.3 codebase --- setup/performance-toolkit/benchmark.jmx | 2878 +++++++++++++++-------- 1 file changed, 1888 insertions(+), 990 deletions(-) diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx index 7ee0be37f6cfb..1cab68878bc20 100644 --- a/setup/performance-toolkit/benchmark.jmx +++ b/setup/performance-toolkit/benchmark.jmx @@ -214,6 +214,11 @@ <stringProp name="Argument.value">${__P(admin_browse_product_filter_text,Product)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> + <elementProp name="admin_users_distribution_per_admin_pool" elementType="Argument"> + <stringProp name="Argument.name">admin_users_distribution_per_admin_pool</stringProp> + <stringProp name="Argument.value">${__P(admin_users_distribution_per_admin_pool,1)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> <elementProp name="apiBasePercentage" elementType="Argument"> <stringProp name="Argument.name">apiBasePercentage</stringProp> <stringProp name="Argument.value">${__P(apiBasePercentage,0)}</stringProp> @@ -264,6 +269,11 @@ <stringProp name="Argument.value">${__P(browseProductGridPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> + <elementProp name="cache_hits_percentage" elementType="Argument"> + <stringProp name="Argument.name">cache_hits_percentage</stringProp> + <stringProp name="Argument.value">${__P(cache_hits_percentage,100)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> <elementProp name="catalogGraphQLPercentage" elementType="Argument"> <stringProp name="Argument.name">catalogGraphQLPercentage</stringProp> <stringProp name="Argument.value">${__P(catalogGraphQLPercentage,0)}</stringProp> @@ -291,7 +301,7 @@ </elementProp> <elementProp name="configurable_products_count" elementType="Argument"> <stringProp name="Argument.name">configurable_products_count</stringProp> - <stringProp name="Argument.value">${__P(configurable_products_count,30)}</stringProp> + <stringProp name="Argument.value">${__P(configurable_products_count,15)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="csrPoolUsers" elementType="Argument"> @@ -564,8 +574,8 @@ </elementProp> <stringProp name="HTTPSampler.domain">${host}</stringProp> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding">utf-8</stringProp> <stringProp name="HTTPSampler.path"/> @@ -643,7 +653,9 @@ props.remove("category_url_keys_list"); props.remove("category_name"); props.remove("category_names_list"); props.remove("simple_products_list"); +props.remove("simple_products_list_for_edit"); props.remove("configurable_products_list"); +props.remove("configurable_products_list_for_edit"); props.remove("users"); props.remove("customer_emails_list"); @@ -692,8 +704,8 @@ if (!slash.equals(path.substring(path.length() -1)) || !slash.equals(path.substr </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> @@ -772,8 +784,8 @@ if (!slash.equals(path.substring(path.length() -1)) || !slash.equals(path.substr </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -841,8 +853,8 @@ if (!slash.equals(path.substring(path.length() -1)) || !slash.equals(path.substr </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -933,8 +945,8 @@ if (!slash.equals(path.substring(path.length() -1)) || !slash.equals(path.substr </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/categories/list</stringProp> @@ -1044,6 +1056,186 @@ props.put("category_name", vars.get("category_name"));</stringProp> <hashTree/> </hashTree> + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Extract categories id of last level" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/extract_categories_id_of_last_level.jmx</stringProp> +</TestFragmentController> + <hashTree> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - BeanShell Sampler: Clear Admin Category Management properties" enabled="true"> + <stringProp name="BeanShellSampler.query">props.remove("admin_category_ids_list");</stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> + </BeanShellSampler> + <hashTree/> + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Get categories of last level" enabled="true"/> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">*/*</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Admin Token Retrieval" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"username":"${admin_user}","password":"${admin_password}"}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="jp@gc - JSON Path Extractor" enabled="true"> + <stringProp name="VAR">admin_token</stringProp> + <stringProp name="JSONPATH">$</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert token not null" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="484395188">^[a-z0-9-]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_token</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Authorization</stringProp> + <stringProp name="Header.value">Bearer ${admin_token}</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - API Get categories" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="searchCriteria[filterGroups][0][filters][0][field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">children_count</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][0][field]</stringProp> + </elementProp> + <elementProp name="searchCriteria[filterGroups][0][filters][0][value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][0][value]</stringProp> + </elementProp> + <elementProp name="searchCriteria[filterGroups][1][filters][0][field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">level</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][1][filters][0][field]</stringProp> + </elementProp> + <elementProp name="searchCriteria[filterGroups][1][filters][0][value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][1][filters][0][value]</stringProp> + </elementProp> + <elementProp name="searchCriteria[filterGroups][1][filters][0][conditionType]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">gt</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][1][filters][0][conditionType]</stringProp> + </elementProp> + <elementProp name="searchCriteria[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${adminCategoryCount}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[pageSize]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/categories/list</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">category_list_id</stringProp> + <stringProp name="RegexExtractor.regex">\{\"id\":(\d+),</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Category Id" enabled="true"> + <stringProp name="ForeachController.inputVal">category_list_id</stringProp> + <stringProp name="ForeachController.returnVal">category_id</stringProp> + <boolProp name="ForeachController.useSeparator">true</boolProp> + </ForeachController> + <hashTree> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="Process categories ids" enabled="true"> + <stringProp name="BeanShellSampler.query">import java.util.ArrayList; + +adminCategoryIdsList = props.get("admin_category_ids_list"); +// If it is first iteration of cycle then recreate categories ids list +if (adminCategoryIdsList == null) { + adminCategoryIdsList = new ArrayList(); + props.put("admin_category_ids_list", adminCategoryIdsList); +} +adminCategoryIdsList.add(vars.get("category_id"));</stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Extract configurable products" enabled="true"> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/extract_configurable_products.jmx</stringProp> </TestFragmentController> @@ -1076,8 +1268,420 @@ props.put("category_name", vars.get("category_name"));</stringProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="jp@gc - JSON Path Extractor" enabled="true"> + <stringProp name="VAR">admin_token</stringProp> + <stringProp name="JSONPATH">$</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert token not null" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="484395188">^[a-z0-9-]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_token</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Authorization</stringProp> + <stringProp name="Header.value">Bearer ${admin_token}</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Get configurable products" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="searchCriteria[filterGroups][0][filters][0][field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">type_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][0][field]</stringProp> + </elementProp> + <elementProp name="searchCriteria[filterGroups][0][filters][0][value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">configurable</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][0][value]</stringProp> + </elementProp> + <elementProp name="searchCriteria[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_products_count}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[pageSize]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/products</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product url keys" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_products_url_keys</stringProp> + <stringProp name="RegexExtractor.regex">url_key\",\"value\":\"(.*?)\"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product ids" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_product_ids</stringProp> + <stringProp name="RegexExtractor.regex">\"id\":(\d+),</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product titles" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_product_names</stringProp> + <stringProp name="RegexExtractor.regex">name\":\"(.*?)\"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product skus" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_product_skus</stringProp> + <stringProp name="RegexExtractor.regex">sku\":\"(.*?)\"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Controller: Prepare configurable products" enabled="true"> + <stringProp name="ForeachController.inputVal">configurable_product_ids</stringProp> + <stringProp name="ForeachController.returnVal">configurable_product_id</stringProp> + <boolProp name="ForeachController.useSeparator">true</boolProp> + </ForeachController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">configurable_products_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">false</boolProp> + </CounterConfig> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - BeanShell Sampler: Collect configurable product" enabled="true"> + <stringProp name="BeanShellSampler.query">import java.util.ArrayList; +import java.util.HashMap; +import org.apache.commons.codec.binary.Base64; + +// If it is first iteration of cycle then recreate productList +if (1 == Integer.parseInt(vars.get("configurable_products_counter"))) { + productList = new ArrayList(); + props.put("configurable_products_list", productList); +} else { + productList = props.get("configurable_products_list"); +} + +String productUrl = vars.get("request_protocol") + "://" + vars.get("host") + vars.get("base_path") + vars.get("configurable_products_url_keys_" + vars.get("configurable_products_counter"))+ vars.get("url_suffix"); +encodedUrl = Base64.encodeBase64(productUrl.getBytes()); +// Create product map +Map productMap = new HashMap(); +productMap.put("id", vars.get("configurable_product_id")); +productMap.put("title", vars.get("configurable_product_names_" + vars.get("configurable_products_counter"))); +productMap.put("sku", vars.get("configurable_product_skus_" + vars.get("configurable_products_counter"))); +productMap.put("url_key", vars.get("configurable_products_url_keys_" + vars.get("configurable_products_counter"))); +productMap.put("uenc", new String(encodedUrl)); + +// Collect products map in products list +productList.add(productMap);</stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + </hashTree> + + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Extract configurable products for edit" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/extract_configurable_products_for_edit.jmx</stringProp> +</TestFragmentController> + <hashTree> + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="SetUp - Retrieve configurable products for edit" enabled="true"/> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">*/*</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Admin Token Retrieval" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"username":"${admin_user}","password":"${admin_password}"}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="jp@gc - JSON Path Extractor" enabled="true"> + <stringProp name="VAR">admin_token</stringProp> + <stringProp name="JSONPATH">$</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert token not null" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="484395188">^[a-z0-9-]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_token</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Authorization</stringProp> + <stringProp name="Header.value">Bearer ${admin_token}</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Get configurable products for edit" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="searchCriteria[filterGroups][0][filters][0][field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">type_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][0][field]</stringProp> + </elementProp> + <elementProp name="searchCriteria[filterGroups][0][filters][0][value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">configurable</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][0][value]</stringProp> + </elementProp> + <elementProp name="searchCriteria[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_products_count}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[pageSize]</stringProp> + </elementProp> + <elementProp name="searchCriteria[currentPage]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[currentPage]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/products</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product url keys" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_products_for_edit_url_keys</stringProp> + <stringProp name="RegexExtractor.regex">url_key\",\"value\":\"(.*?)\"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product ids" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_product_for_edit_ids</stringProp> + <stringProp name="RegexExtractor.regex">\"id\":(\d+),</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product titles" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_product_for_edit_names</stringProp> + <stringProp name="RegexExtractor.regex">name\":\"(.*?)\"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product skus" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_product_for_edit_skus</stringProp> + <stringProp name="RegexExtractor.regex">sku\":\"(.*?)\"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Controller: Prepare configurable products for edit" enabled="true"> + <stringProp name="ForeachController.inputVal">configurable_product_for_edit_ids</stringProp> + <stringProp name="ForeachController.returnVal">configurable_product_for_edit_id</stringProp> + <boolProp name="ForeachController.useSeparator">true</boolProp> + </ForeachController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter for edit" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">configurable_products_counter_for_edit</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">false</boolProp> + </CounterConfig> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - BeanShell Sampler: Collect configurable product for edit" enabled="true"> + <stringProp name="BeanShellSampler.query">import java.util.ArrayList; +import java.util.HashMap; +import org.apache.commons.codec.binary.Base64; + +if (1 == Integer.parseInt(vars.get("configurable_products_counter_for_edit"))) { + editProductList = new ArrayList(); + props.put("configurable_products_list_for_edit", editProductList); +} else { + productList = props.get("configurable_products_list_for_edit"); +} + +String productUrl = vars.get("request_protocol") + "://" + vars.get("host") + vars.get("base_path") + vars.get("configurable_products_for_edit_url_keys_" + vars.get("configurable_products_counter_for_edit"))+ vars.get("url_suffix"); +encodedUrl = Base64.encodeBase64(productUrl.getBytes()); +// Create product map +Map editProductMap = new HashMap(); +editProductMap.put("id", vars.get("configurable_product_for_edit_id")); +editProductMap.put("title", vars.get("configurable_product_for_edit_names_" + vars.get("configurable_products_counter_for_edit"))); +editProductMap.put("sku", vars.get("configurable_product_for_edit_skus_" + vars.get("configurable_products_counter_for_edit"))); +editProductMap.put("url_key", vars.get("configurable_products_for_edit_url_keys_" + vars.get("configurable_products_counter_for_edit"))); +editProductMap.put("uenc", new String(encodedUrl)); + +// Collect products map in products list +editProductList.add(editProductMap);</stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + </hashTree> + + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Extract simple products for edit" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/extract_simple_products_for_edit.jmx</stringProp> +</TestFragmentController> + <hashTree> + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="SetUp - Retrieve simple products for edit" enabled="true"/> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">*/*</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Admin Token Retrieval" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"username":"${admin_user}","password":"${admin_password}"}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -1119,7 +1723,7 @@ props.put("category_name", vars.get("category_name"));</stringProp> </collectionProp> </HeaderManager> <hashTree/> - <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Get configurable products" enabled="true"> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Get simple products for edit" enabled="true"> <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> <collectionProp name="Arguments.arguments"> <elementProp name="searchCriteria[filterGroups][0][filters][0][field]" elementType="HTTPArgument"> @@ -1131,24 +1735,45 @@ props.put("category_name", vars.get("category_name"));</stringProp> </elementProp> <elementProp name="searchCriteria[filterGroups][0][filters][0][value]" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">configurable</stringProp> + <stringProp name="Argument.value">simple</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][0][value]</stringProp> </elementProp> <elementProp name="searchCriteria[pageSize]" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${configurable_products_count}</stringProp> + <stringProp name="Argument.value">${simple_products_count}</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">searchCriteria[pageSize]</stringProp> </elementProp> + <elementProp name="searchCriteria[currentPage]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[currentPage]</stringProp> + </elementProp> + <elementProp name="searchCriteria[filterGroups][1][filters][1][field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">attribute_set_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][1][filters][1][field]</stringProp> + </elementProp> + <elementProp name="searchCriteria[filterGroups][1][filters][1][value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">4</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][1][filters][1][value]</stringProp> + </elementProp> </collectionProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/products</stringProp> @@ -1163,7 +1788,7 @@ props.put("category_name", vars.get("category_name"));</stringProp> <hashTree> <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product url keys" enabled="true"> <stringProp name="RegexExtractor.useHeaders">false</stringProp> - <stringProp name="RegexExtractor.refname">configurable_products_url_keys</stringProp> + <stringProp name="RegexExtractor.refname">simple_products_for_edit_url_keys</stringProp> <stringProp name="RegexExtractor.regex">url_key\",\"value\":\"(.*?)\"</stringProp> <stringProp name="RegexExtractor.template">$1$</stringProp> <stringProp name="RegexExtractor.default"/> @@ -1172,7 +1797,7 @@ props.put("category_name", vars.get("category_name"));</stringProp> <hashTree/> <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product ids" enabled="true"> <stringProp name="RegexExtractor.useHeaders">false</stringProp> - <stringProp name="RegexExtractor.refname">configurable_product_ids</stringProp> + <stringProp name="RegexExtractor.refname">simple_product_for_edit_ids</stringProp> <stringProp name="RegexExtractor.regex">\"id\":(\d+),</stringProp> <stringProp name="RegexExtractor.template">$1$</stringProp> <stringProp name="RegexExtractor.default"/> @@ -1181,7 +1806,7 @@ props.put("category_name", vars.get("category_name"));</stringProp> <hashTree/> <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product titles" enabled="true"> <stringProp name="RegexExtractor.useHeaders">false</stringProp> - <stringProp name="RegexExtractor.refname">configurable_product_names</stringProp> + <stringProp name="RegexExtractor.refname">simple_product_for_edit_names</stringProp> <stringProp name="RegexExtractor.regex">name\":\"(.*?)\"</stringProp> <stringProp name="RegexExtractor.template">$1$</stringProp> <stringProp name="RegexExtractor.default"/> @@ -1190,7 +1815,7 @@ props.put("category_name", vars.get("category_name"));</stringProp> <hashTree/> <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product skus" enabled="true"> <stringProp name="RegexExtractor.useHeaders">false</stringProp> - <stringProp name="RegexExtractor.refname">configurable_product_skus</stringProp> + <stringProp name="RegexExtractor.refname">simple_product_for_edit_skus</stringProp> <stringProp name="RegexExtractor.regex">sku\":\"(.*?)\"</stringProp> <stringProp name="RegexExtractor.template">$1$</stringProp> <stringProp name="RegexExtractor.default"/> @@ -1199,46 +1824,45 @@ props.put("category_name", vars.get("category_name"));</stringProp> <hashTree/> </hashTree> </hashTree> - <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Controller: Prepare configurable products" enabled="true"> - <stringProp name="ForeachController.inputVal">configurable_product_ids</stringProp> - <stringProp name="ForeachController.returnVal">configurable_product_id</stringProp> + <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Controller: Prepare simple products for edit" enabled="true"> + <stringProp name="ForeachController.inputVal">simple_product_for_edit_ids</stringProp> + <stringProp name="ForeachController.returnVal">simple_product_for_edit_id</stringProp> <boolProp name="ForeachController.useSeparator">true</boolProp> </ForeachController> <hashTree> - <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter for edit" enabled="true"> <stringProp name="CounterConfig.start">1</stringProp> <stringProp name="CounterConfig.end"/> <stringProp name="CounterConfig.incr">1</stringProp> - <stringProp name="CounterConfig.name">configurable_products_counter</stringProp> + <stringProp name="CounterConfig.name">simple_products_counter_for_edit</stringProp> <stringProp name="CounterConfig.format"/> <boolProp name="CounterConfig.per_user">false</boolProp> </CounterConfig> <hashTree/> - <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - BeanShell Sampler: Collect configurable product" enabled="true"> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - BeanShell Sampler: Collect simple product for edit" enabled="true"> <stringProp name="BeanShellSampler.query">import java.util.ArrayList; import java.util.HashMap; import org.apache.commons.codec.binary.Base64; -// If it is first iteration of cycle then recreate productList -if (1 == Integer.parseInt(vars.get("configurable_products_counter"))) { - productList = new ArrayList(); - props.put("configurable_products_list", productList); +if (1 == Integer.parseInt(vars.get("simple_products_counter_for_edit"))) { + editProductList = new ArrayList(); + props.put("simple_products_list_for_edit", editProductList); } else { - productList = props.get("configurable_products_list"); + productList = props.get("simple_products_counter_for_edit"); } -String productUrl = vars.get("request_protocol") + "://" + vars.get("host") + vars.get("base_path") + vars.get("configurable_products_url_keys_" + vars.get("configurable_products_counter"))+ vars.get("url_suffix"); +String productUrl = vars.get("request_protocol") + "://" + vars.get("host") + vars.get("base_path") + vars.get("simple_products_for_edit_url_keys_" + vars.get("simple_products_counter_for_edit"))+ vars.get("url_suffix"); encodedUrl = Base64.encodeBase64(productUrl.getBytes()); // Create product map -Map productMap = new HashMap(); -productMap.put("id", vars.get("configurable_product_id")); -productMap.put("title", vars.get("configurable_product_names_" + vars.get("configurable_products_counter"))); -productMap.put("sku", vars.get("configurable_product_skus_" + vars.get("configurable_products_counter"))); -productMap.put("url_key", vars.get("configurable_products_url_keys_" + vars.get("configurable_products_counter"))); -productMap.put("uenc", new String(encodedUrl)); +Map editProductMap = new HashMap(); +editProductMap.put("id", vars.get("simple_product_for_edit_id")); +editProductMap.put("title", vars.get("simple_product_for_edit_names_" + vars.get("simple_products_counter_for_edit"))); +editProductMap.put("sku", vars.get("simple_product_for_edit_skus_" + vars.get("simple_products_counter_for_edit"))); +editProductMap.put("url_key", vars.get("simple_products_for_edit_url_keys_" + vars.get("simple_products_counter_for_edit"))); +editProductMap.put("uenc", new String(encodedUrl)); // Collect products map in products list -productList.add(productMap);</stringProp> +editProductList.add(editProductMap);</stringProp> <stringProp name="BeanShellSampler.filename"/> <stringProp name="BeanShellSampler.parameters"/> <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> @@ -1279,8 +1903,8 @@ productList.add(productMap);</stringProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -1346,12 +1970,26 @@ productList.add(productMap);</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">searchCriteria[pageSize]</stringProp> </elementProp> + <elementProp name="searchCriteria[filterGroups][0][filters][1][field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">attribute_set_id</stringProp> + <stringProp name="Argument.metadata">!=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][1][field]</stringProp> + </elementProp> + <elementProp name="searchCriteria[filterGroups][0][filters][1][value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">4</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][1][value]</stringProp> + </elementProp> </collectionProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/products</stringProp> @@ -1467,8 +2105,8 @@ productList.add(productMap);</stringProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/user/roleGrid/limit/200/?ajax=true&isAjax=true</stringProp> @@ -1500,6 +2138,7 @@ productList.add(productMap);</stringProp> adminUserList.poll(); props.put("adminUserList", adminUserList); + props.put("adminUserListIterator", adminUserList.descendingIterator()); </stringProp> </BeanShellPostProcessor> <hashTree/> @@ -1516,8 +2155,8 @@ productList.add(productMap);</stringProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/customer/index/</stringProp> @@ -1590,6 +2229,13 @@ manager.add(cookie); </stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">filters[placeholder]</stringProp> </elementProp> + <elementProp name="filters[group_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[group_id]</stringProp> + </elementProp> <elementProp name="filters[website_id]" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">1</stringProp> @@ -1636,8 +2282,8 @@ manager.add(cookie); </stringProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -1751,6 +2397,59 @@ idsList.add(vars.get("customer_id"));</stringProp> </hashTree> </hashTree> + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Extract region ids" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/extract_region_ids.jmx</stringProp> + </TestFragmentController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Extract Region ids" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="parent" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">US</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">parent</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout"/> + <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/directory/json/countryRegion/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Parse and put region id into variables" enabled="true"> + <stringProp name="scriptLanguage">groovy</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">import groovy.json.JsonSlurper +def jsonSlurper = new JsonSlurper(); +def regionResponse = jsonSlurper.parseText(prev.getResponseDataAsString()); + +regionResponse.each { region -> + if (region.label.toString() == "Alabama") { + props.put("alabama_region_id", region.value.toString()); + } else if (region.label.toString() == 'California') { + props.put("california_region_id", region.value.toString()); + } +}</stringProp> + </JSR223PostProcessor> + <hashTree/> + </hashTree> + </hashTree> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - BeanShell Sampler: Validate properties and count users" enabled="true"> <stringProp name="BeanShellSampler.query">Boolean stopTestOnError (String error) { log.error(error); @@ -1762,9 +2461,15 @@ idsList.add(vars.get("customer_id"));</stringProp> if (props.get("simple_products_list") == null) { return stopTestOnError("Cannot find simple products. Test stopped."); } +if (props.get("simple_products_list_for_edit") == null) { + return stopTestOnError("Cannot find simple products for edit. Test stopped."); +} if (props.get("configurable_products_list") == null) { return stopTestOnError("Cannot find configurable products. Test stopped."); } +if (props.get("configurable_products_list_for_edit") == null) { + return stopTestOnError("Cannot find configurable products for edit. Test stopped."); +} if (props.get("customer_emails_list") == null) { return stopTestOnError("Cannot find customer emails. Test stopped."); } @@ -1816,8 +2521,8 @@ if (props.get("category_names_list") == null) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add</stringProp> @@ -1873,8 +2578,8 @@ if (props.get("category_names_list") == null) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}page_cache/block/render/</stringProp> @@ -1916,6 +2621,30 @@ if (props.get("category_names_list") == null) { <stringProp name="ThreadGroup.delay"/> <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/thread_group.jmx</stringProp></ThreadGroup> <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Cache hit miss" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">var cacheHitPercent = vars.get("cache_hits_percentage"); + +if ( + cacheHitPercent < 100 && + sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + doCache(); +} + +function doCache(){ + var random = Math.random() * 100; + if (cacheHitPercent < random) { + sampler.setPath(sampler.getPath() + "?cacheModifier=" + Math.random().toString(36).substring(2, 13)); + } +} +</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/cache_hit_miss.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="Catalog Browsing By Customer" enabled="true"> <intProp name="ThroughputController.style">1</intProp> <boolProp name="ThroughputController.perThread">false</boolProp> @@ -2038,8 +2767,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/login/</stringProp> @@ -2098,8 +2827,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/loginPost/</stringProp> @@ -2170,8 +2899,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -2191,8 +2920,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}</stringProp> @@ -2222,8 +2951,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${category_url_key}${url_suffix}</stringProp> @@ -2310,8 +3039,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -2378,8 +3107,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -2410,8 +3139,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/logout/</stringProp> @@ -2544,8 +3273,8 @@ vars.put("category_name", props.get("category_names_list").get(number)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}</stringProp> @@ -2575,8 +3304,8 @@ vars.put("category_name", props.get("category_names_list").get(number)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${category_url_key}${url_suffix}</stringProp> @@ -2663,8 +3392,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -2731,8 +3460,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -2802,6 +3531,30 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/search_terms.jmx</stringProp></CSVDataSet> <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Cache hit miss" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">var cacheHitPercent = vars.get("cache_hits_percentage"); + +if ( + cacheHitPercent < 100 && + sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + doCache(); +} + +function doCache(){ + var random = Math.random() * 100; + if (cacheHitPercent < random) { + sampler.setPath(sampler.getPath() + "?cacheModifier=" + Math.random().toString(36).substring(2, 13)); + } +} +</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/cache_hit_miss.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="Quick Search" enabled="true"> <intProp name="ThroughputController.style">1</intProp> <boolProp name="ThroughputController.perThread">false</boolProp> @@ -2864,8 +3617,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}</stringProp> @@ -2903,8 +3656,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}catalogsearch/result/</stringProp> @@ -2966,8 +3719,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}catalogsearch/searchTermsLog/save/</stringProp> @@ -3045,8 +3798,8 @@ vars.put("product_url_key", product); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -3135,8 +3888,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}</stringProp> @@ -3174,8 +3927,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol"/> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}catalogsearch/result/</stringProp> @@ -3264,8 +4017,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}catalogsearch/searchTermsLog/save/</stringProp> @@ -3309,8 +4062,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol"/> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${attribute_1_filter_url}</stringProp> @@ -3381,8 +4134,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol"/> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${attribute_2_filter_url}</stringProp> @@ -3469,8 +4222,8 @@ vars.put("product_url_key", product); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -3559,8 +4312,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}</stringProp> @@ -3591,8 +4344,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol"/> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}catalogsearch/advanced/</stringProp> @@ -3702,8 +4455,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}catalogsearch/advanced/result/</stringProp> @@ -3788,8 +4541,8 @@ vars.put("product_url_key", product); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -3925,8 +4678,8 @@ vars.put("category_name", props.get("category_names_list").get(number)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}</stringProp> @@ -3956,8 +4709,8 @@ vars.put("category_name", props.get("category_names_list").get(number)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${category_url_key}${url_suffix}</stringProp> @@ -4058,8 +4811,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -4118,8 +4871,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> @@ -4171,8 +4924,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -4281,8 +5034,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -4337,8 +5090,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -4386,8 +5139,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/configurable-products/${product_sku}/options/all</stringProp> @@ -4454,8 +5207,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> @@ -4537,8 +5290,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -4699,8 +5452,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/login/</stringProp> @@ -4759,8 +5512,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/loginPost/</stringProp> @@ -4831,8 +5584,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -4888,8 +5641,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -4942,8 +5695,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}wishlist/index/add/</stringProp> @@ -5004,8 +5757,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -5073,8 +5826,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}wishlist/index/remove/</stringProp> @@ -5094,8 +5847,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/logout/</stringProp> @@ -5239,8 +5992,8 @@ vars.put("category_name", props.get("category_names_list").get(number)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${category_url_key}${url_suffix}</stringProp> @@ -5340,8 +6093,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -5393,8 +6146,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}catalog/product_compare/add/</stringProp> @@ -5436,8 +6189,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -5518,8 +6271,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -5571,8 +6324,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}catalog/product_compare/add/</stringProp> @@ -5614,8 +6367,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -5646,8 +6399,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}catalog/product_compare/index/</stringProp> @@ -5682,8 +6435,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}catalog/product_compare/clear</stringProp> @@ -5806,8 +6559,8 @@ vars.put("category_name", props.get("category_names_list").get(number)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}</stringProp> @@ -5837,8 +6590,8 @@ vars.put("category_name", props.get("category_names_list").get(number)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${category_url_key}${url_suffix}</stringProp> @@ -5939,8 +6692,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -5999,8 +6752,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> @@ -6052,8 +6805,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -6162,8 +6915,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -6218,8 +6971,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -6267,8 +7020,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/configurable-products/${product_sku}/options/all</stringProp> @@ -6335,8 +7088,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> @@ -6418,8 +7171,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -6476,14 +7229,26 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> </GenericController> <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Get region data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script"> + vars.put("alabama_region_id", props.get("alabama_region_id")); + vars.put("california_region_id", props.get("california_region_id")); +</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/get_region_data.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Checkout start" enabled="true"> <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> <collectionProp name="Arguments.arguments"/> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/</stringProp> @@ -6549,8 +7314,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/customers/isEmailAvailable</stringProp> @@ -6608,8 +7373,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/guest-carts/${cart_id}/estimate-shipping-methods</stringProp> @@ -6660,15 +7425,15 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <collectionProp name="Arguments.arguments"> <elementProp name="" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">false</boolProp> - <stringProp name="Argument.value">{"addressInformation":{"shipping_address":{"countryId":"US","regionId":"12","regionCode":"CA","region":"California","street":["10441 Jefferson Blvd ste 200"],"company":"","telephone":"3109450345","fax":"","postcode":"90232","city":"Culver City","firstname":"Name","lastname":"Lastname"},"shipping_method_code":"flatrate","shipping_carrier_code":"flatrate"}}</stringProp> + <stringProp name="Argument.value">{"addressInformation":{"shipping_address":{"countryId":"US","regionId":"${california_region_id}","regionCode":"CA","region":"California","street":["10441 Jefferson Blvd ste 200"],"company":"","telephone":"3109450345","fax":"","postcode":"90232","city":"Culver City","firstname":"Name","lastname":"Lastname"},"shipping_method_code":"flatrate","shipping_carrier_code":"flatrate"}}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> </collectionProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/guest-carts/${cart_id}/shipping-information</stringProp> @@ -6719,15 +7484,15 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <collectionProp name="Arguments.arguments"> <elementProp name="" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">false</boolProp> - <stringProp name="Argument.value">{"cartId":"${cart_id}","email":"test@example.com","paymentMethod":{"method":"checkmo","po_number":null,"additional_data":null},"billingAddress":{"countryId":"US","regionId":"12","regionCode":"CA","region":"California","street":["10441 Jefferson Blvd ste 200"],"company":"","telephone":"3109450345","fax":"","postcode":"90232","city":"Culver City","firstname":"Name","lastname":"Lastname"}}</stringProp> + <stringProp name="Argument.value">{"cartId":"${cart_id}","email":"test@example.com","paymentMethod":{"method":"checkmo","po_number":null,"additional_data":null},"billingAddress":{"countryId":"US","regionId":"${california_region_id}","regionCode":"CA","region":"California","street":["10441 Jefferson Blvd ste 200"],"company":"","telephone":"3109450345","fax":"","postcode":"90232","city":"Culver City","firstname":"Name","lastname":"Lastname"}}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> </collectionProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/guest-carts/${cart_id}/payment-information</stringProp> @@ -6797,8 +7562,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/onepage/success/</stringProp> @@ -6959,8 +7724,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}</stringProp> @@ -6990,8 +7755,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/login/</stringProp> @@ -7050,8 +7815,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/loginPost/</stringProp> @@ -7122,8 +7887,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -7143,8 +7908,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${category_url_key}${url_suffix}</stringProp> @@ -7245,8 +8010,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -7305,8 +8070,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> @@ -7358,8 +8123,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -7468,8 +8233,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -7524,8 +8289,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -7573,8 +8338,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/configurable-products/${product_sku}/options/all</stringProp> @@ -7641,8 +8406,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> @@ -7724,8 +8489,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -7782,14 +8547,26 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> </GenericController> <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Get region data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script"> + vars.put("alabama_region_id", props.get("alabama_region_id")); + vars.put("california_region_id", props.get("california_region_id")); +</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/get_region_data.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Checkout start" enabled="true"> <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> <collectionProp name="Arguments.arguments"/> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/</stringProp> @@ -7895,8 +8672,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/carts/mine/estimate-shipping-methods-by-address-id</stringProp> @@ -7947,15 +8724,15 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <collectionProp name="Arguments.arguments"> <elementProp name="" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">false</boolProp> - <stringProp name="Argument.value">{"addressInformation":{"shipping_address":{"customerAddressId":"${address_id}","countryId":"US","regionId":5,"regionCode":"AR","region":"Arkansas","customerId":"${customer_id}","street":["123 Freedom Blvd. #123"],"telephone":"022-333-4455","postcode":"123123","city":"Fayetteville","firstname":"Anthony","lastname":"Nealy"},"shipping_method_code":"flatrate","shipping_carrier_code":"flatrate"}}</stringProp> + <stringProp name="Argument.value">{"addressInformation":{"shipping_address":{"customerAddressId":"${address_id}","countryId":"US","regionId":"${alabama_region_id}","regionCode":"AL","region":"Alabama","customerId":"${customer_id}","street":["123 Freedom Blvd. #123"],"telephone":"022-333-4455","postcode":"123123","city":"Fayetteville","firstname":"Anthony","lastname":"Nealy"},"shipping_method_code":"flatrate","shipping_carrier_code":"flatrate"}}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> </collectionProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/carts/mine/shipping-information</stringProp> @@ -8006,15 +8783,15 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <collectionProp name="Arguments.arguments"> <elementProp name="" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">false</boolProp> - <stringProp name="Argument.value">{"cartId":"${cart_id}","paymentMethod":{"method":"checkmo","po_number":null,"additional_data":null},"billingAddress":{"customerAddressId":"${address_id}","countryId":"US","regionId":5,"regionCode":"AR","region":"Arkansas","customerId":"${customer_id}","street":["123 Freedom Blvd. #123"],"telephone":"022-333-4455","postcode":"123123","city":"Fayetteville","firstname":"Anthony","lastname":"Nealy"}}</stringProp> + <stringProp name="Argument.value">{"cartId":"${cart_id}","paymentMethod":{"method":"checkmo","po_number":null,"additional_data":null},"billingAddress":{"customerAddressId":"${address_id}","countryId":"US","regionId":"${alabama_region_id}","regionCode":"AL","region":"Alabama","customerId":"${customer_id}","street":["123 Freedom Blvd. #123"],"telephone":"022-333-4455","postcode":"123123","city":"Fayetteville","firstname":"Anthony","lastname":"Nealy"}}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> </collectionProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/carts/mine/payment-information</stringProp> @@ -8065,8 +8842,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/onepage/success/</stringProp> @@ -8098,8 +8875,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/logout/</stringProp> @@ -8255,8 +9032,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/login/</stringProp> @@ -8315,8 +9092,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/loginPost/</stringProp> @@ -8387,8 +9164,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -8444,8 +9221,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -8518,8 +9295,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}review/product/post/id/${product_id}</stringProp> @@ -8571,8 +9348,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -8600,8 +9377,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/logout/</stringProp> @@ -8771,8 +9548,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/login/</stringProp> @@ -8831,8 +9608,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/loginPost/</stringProp> @@ -8903,8 +9680,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -8924,8 +9701,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}</stringProp> @@ -8955,8 +9732,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${category_url_key}${url_suffix}</stringProp> @@ -9057,8 +9834,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -9117,8 +9894,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> @@ -9170,8 +9947,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -9280,8 +10057,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> @@ -9336,8 +10113,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -9385,8 +10162,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/configurable-products/${product_sku}/options/all</stringProp> @@ -9453,8 +10230,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> @@ -9536,8 +10313,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -9600,8 +10377,8 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol"/> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/cart/</stringProp> @@ -9690,8 +10467,8 @@ vars.put("item_id", vars.get("cart_items_qty_inputs_" + id)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol"/> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}checkout/cart/delete/</stringProp> @@ -9733,8 +10510,8 @@ vars.put("item_id", vars.get("cart_items_qty_inputs_" + id)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -9765,8 +10542,8 @@ vars.put("item_id", vars.get("cart_items_qty_inputs_" + id)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/logout/</stringProp> @@ -9891,8 +10668,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}</stringProp> @@ -9922,8 +10699,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/login/</stringProp> @@ -9982,8 +10759,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/loginPost/</stringProp> @@ -10054,8 +10831,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> @@ -10075,8 +10852,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}sales/order/history/</stringProp> @@ -10121,8 +10898,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}sales/order/view/order_id/${orderId}</stringProp> @@ -10166,8 +10943,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}sales/order/shipment/order_id/${orderId}</stringProp> @@ -10205,8 +10982,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${popupLink}</stringProp> @@ -10238,8 +11015,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}downloadable/customer/products</stringProp> @@ -10293,8 +11070,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}sales/order/view/order_id/${orderId}</stringProp> @@ -10324,8 +11101,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}downloadable/download/link/id/${linkId}</stringProp> @@ -10346,8 +11123,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}wishlist</stringProp> @@ -10402,8 +11179,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}wishlist/index/share/wishlist_id/${wishlistId}/</stringProp> @@ -10456,8 +11233,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}wishlist/index/send/wishlist_id/${wishlistId}/</stringProp> @@ -10488,8 +11265,8 @@ vars.put("customer_email", customerUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}customer/account/logout/</stringProp> @@ -10641,7 +11418,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -10664,11 +11453,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -10745,8 +11534,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -10787,8 +11576,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/cms/page/</stringProp> @@ -10807,8 +11596,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/cms/page/new</stringProp> @@ -10940,8 +11729,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/cms/page/save/</stringProp> @@ -10979,8 +11768,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -10999,8 +11788,11 @@ vars.put("admin_user", adminUser); <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -11106,7 +11898,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -11129,11 +11933,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -11210,8 +12014,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -11334,8 +12138,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -11451,8 +12255,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -11579,8 +12383,8 @@ vars.put("grid_pages_count_filtered", pageCount); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -11706,8 +12510,8 @@ vars.put("grid_pages_count_filtered", pageCount); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -11741,8 +12545,8 @@ vars.put("grid_pages_count_filtered", pageCount); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -11761,8 +12565,11 @@ vars.put("grid_pages_count_filtered", pageCount); <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -11868,7 +12675,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -11891,11 +12710,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -11972,8 +12791,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -12096,8 +12915,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -12213,8 +13032,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -12341,8 +13160,8 @@ vars.put("grid_pages_count_filtered", pageCount); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -12468,8 +13287,8 @@ vars.put("grid_pages_count_filtered", pageCount); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -12503,8 +13322,8 @@ vars.put("grid_pages_count_filtered", pageCount); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -12523,8 +13342,11 @@ vars.put("grid_pages_count_filtered", pageCount); <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -12630,7 +13452,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -12653,11 +13487,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -12734,8 +13568,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -12813,8 +13647,8 @@ vars.put("related_product_id", props.get("simple_products_list").get(relatedInde </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -12893,8 +13727,8 @@ vars.put("related_product_id", props.get("simple_products_list").get(relatedInde </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products/attributes</stringProp> @@ -12961,8 +13795,8 @@ vars.putObject("product_attributes", attributes); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product_set/index/filter/${attribute_set_filter}</stringProp> @@ -13010,15 +13844,15 @@ Random random = new Random(); if (${seedForRandom} > 0) { random.setSeed(${seedForRandom}); } -number = random.nextInt(props.get("simple_products_list").size()); -simpleList = props.get("simple_products_list").get(number); +number = random.nextInt(props.get("simple_products_list_for_edit").size()); +simpleList = props.get("simple_products_list_for_edit").get(number); vars.put("simple_product_1_id", simpleList.get("id")); vars.put("simple_product_1_name", simpleList.get("title")); do { - number1 = random.nextInt(props.get("simple_products_list").size()); + number1 = random.nextInt(props.get("simple_products_list_for_edit").size()); } while(number == number1); -simpleList = props.get("simple_products_list").get(number1); +simpleList = props.get("simple_products_list_for_edit").get(number1); vars.put("simple_product_2_id", simpleList.get("id")); vars.put("simple_product_2_name", simpleList.get("title")); @@ -13056,8 +13890,8 @@ vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNu </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/</stringProp> @@ -13086,8 +13920,8 @@ vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNu </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/new/set/4/type/bundle/</stringProp> @@ -13989,8 +14823,8 @@ vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNu </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/validate/set/4/</stringProp> @@ -14923,8 +15757,8 @@ vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNu </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/save/set/4/type/bundle/back/edit/active_tab/product-details/</stringProp> @@ -14964,8 +15798,8 @@ vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNu </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/</stringProp> @@ -14995,8 +15829,8 @@ vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNu </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/new/set/${attribute_set_id}/type/configurable/</stringProp> @@ -15525,8 +16359,8 @@ vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNu </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/validate/set/${attribute_set_id}/</stringProp> @@ -15693,7 +16527,7 @@ function addConfigurableMatrix(attributes) { </elementProp> <elementProp name="product[category_ids][0]" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">4</stringProp> + <stringProp name="Argument.value">2</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">product[category_ids][0]</stringProp> @@ -16137,8 +16971,8 @@ function addConfigurableMatrix(attributes) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/save/set/${attribute_set_id}/type/configurable/back/edit/active_tab/product-details/</stringProp> @@ -16273,8 +17107,8 @@ function addConfigurableMatrix(attributes) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/</stringProp> @@ -16303,8 +17137,8 @@ function addConfigurableMatrix(attributes) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/new/set/4/type/downloadable/</stringProp> @@ -16351,8 +17185,8 @@ function addConfigurableMatrix(attributes) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/downloadable_file/upload/type/links/?isAjax=true</stringProp> @@ -16398,8 +17232,8 @@ function addConfigurableMatrix(attributes) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/downloadable_file/upload/type/samples/?isAjax=true</stringProp> @@ -17131,8 +17965,8 @@ function addConfigurableMatrix(attributes) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/validate/set/4/type/downloadable/</stringProp> @@ -17853,8 +18687,8 @@ function addConfigurableMatrix(attributes) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/save/set/4/type/downloadable/back/edit/active_tab/product-details/</stringProp> @@ -17898,8 +18732,8 @@ function addConfigurableMatrix(attributes) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/</stringProp> @@ -17928,8 +18762,8 @@ function addConfigurableMatrix(attributes) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/new/set/4/type/simple/</stringProp> @@ -18657,8 +19491,8 @@ function addConfigurableMatrix(attributes) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/validate/set/4/</stringProp> @@ -19368,8 +20202,8 @@ function addConfigurableMatrix(attributes) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/save/set/4/type/simple/back/edit/active_tab/product-details/</stringProp> @@ -19410,8 +20244,8 @@ function addConfigurableMatrix(attributes) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -19430,8 +20264,11 @@ function addConfigurableMatrix(attributes) { <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -19537,7 +20374,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -19560,11 +20409,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -19641,8 +20490,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -19686,8 +20535,8 @@ vars.put("admin_user", adminUser); if (${seedForRandom} > 0) { random.setSeed(${seedForRandom} + ${__threadNum}); } - simpleCount = props.get("simple_products_list").size(); - configCount = props.get("configurable_products_list").size(); + simpleCount = props.get("simple_products_list_for_edit").size(); + configCount = props.get("configurable_products_list_for_edit").size(); productCount = 0; if (simpleCount > configCount) { productCount = configCount; @@ -19723,14 +20572,14 @@ vars.put("admin_user", adminUser); i = productClusterLength * currentThreadNum + iterator; //ids of simple and configurable products to edit - vars.put("simple_product_id", props.get("simple_products_list").get(i).get("id")); - vars.put("configurable_product_id", props.get("configurable_products_list").get(i).get("id")); + vars.put("simple_product_id", props.get("simple_products_list_for_edit").get(i).get("id")); + vars.put("configurable_product_id", props.get("configurable_products_list_for_edit").get(i).get("id")); //id of related product do { - relatedIndex = random.nextInt(props.get("simple_products_list").size()); + relatedIndex = random.nextInt(props.get("simple_products_list_for_edit").size()); } while(i == relatedIndex); - vars.put("related_product_id", props.get("simple_products_list").get(relatedIndex).get("id")); + vars.put("related_product_id", props.get("simple_products_list_for_edit").get(relatedIndex).get("id")); } catch (Exception ex) { log.info("Script execution failed", ex); }</stringProp> @@ -19745,8 +20594,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/edit/id/${simple_product_id}/</stringProp> @@ -19799,20 +20648,32 @@ vars.put("admin_user", adminUser); <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set updated values" enabled="true"> <stringProp name="TestPlan.comments">Passing arguments between threads</stringProp> <stringProp name="BeanShellSampler.query">//Additional category to be added + import java.util.Random; - int categoryId = Integer.parseInt(vars.get("simple_product_category_id")); - if (categoryId > 4) { - categoryId = categoryId - 1; - } else { - categoryId = categoryId + 1; - } - vars.put("category_additional", categoryId.toString()); - //New price - vars.put("price_new", "9999"); - //New special price - vars.put("special_price_new", "8888"); - //New quantity - vars.put("quantity_new", "100600");</stringProp> + Random randomGenerator = new Random(); + if (${seedForRandom} > 0) { + randomGenerator.setSeed(${seedForRandom} + ${__threadNum}); + } + + int categoryId = Integer.parseInt(vars.get("simple_product_category_id")); + categoryList = props.get("admin_category_ids_list"); + + if (categoryList.size() > 1) { + do { + int index = randomGenerator.nextInt(categoryList.size()); + newCategoryId = categoryList.get(index); + } while (categoryId == newCategoryId); + + vars.put("category_additional", newCategoryId.toString()); + } + + //New price + vars.put("price_new", "9999"); + //New special price + vars.put("special_price_new", "8888"); + //New quantity + vars.put("quantity_new", "100600"); + </stringProp> <stringProp name="BeanShellSampler.filename"/> <stringProp name="BeanShellSampler.parameters"/> <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> @@ -20257,8 +21118,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/validate/id/${simple_product_id}/?isAjax=true</stringProp> @@ -20727,8 +21588,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/save/id/${simple_product_id}/back/edit/active_tab/product-details/</stringProp> @@ -20757,8 +21618,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/edit/id/${configurable_product_id}/</stringProp> @@ -21335,8 +22196,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/validate/id/${configurable_product_id}/</stringProp> @@ -21807,8 +22668,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/save/id/${configurable_product_id}/back/edit/active_tab/product-details/</stringProp> @@ -21878,8 +22739,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -21898,8 +22759,11 @@ vars.put("admin_user", adminUser); <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -22022,7 +22886,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -22045,11 +22921,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -22126,8 +23002,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -22164,8 +23040,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order/</stringProp> @@ -22275,8 +23151,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -22392,8 +23268,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -22494,8 +23370,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order/view/order_id/${order_id}/</stringProp> @@ -22540,8 +23416,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_invoice/start/order_id/${order_id}/</stringProp> @@ -22611,8 +23487,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_invoice/save/order_id/${order_id}/</stringProp> @@ -22642,8 +23518,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_creditmemo/start/order_id/${order_id}/</stringProp> @@ -22731,8 +23607,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_creditmemo/save/order_id/${order_id}/</stringProp> @@ -22771,8 +23647,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -22791,8 +23667,11 @@ vars.put("admin_user", adminUser); <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -22898,7 +23777,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -22921,11 +23812,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -23002,8 +23893,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -23126,8 +24017,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -23243,8 +24134,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -23371,8 +24262,8 @@ vars.put("grid_pages_count_filtered", pageCount); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -23498,8 +24389,8 @@ vars.put("grid_pages_count_filtered", pageCount); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -23533,8 +24424,8 @@ vars.put("grid_pages_count_filtered", pageCount); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -23553,8 +24444,11 @@ vars.put("grid_pages_count_filtered", pageCount); <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -23660,7 +24554,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -23683,11 +24589,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -23764,8 +24670,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -23796,6 +24702,18 @@ vars.put("admin_user", adminUser); <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> </GenericController> <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Get region data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script"> + vars.put("alabama_region_id", props.get("alabama_region_id")); + vars.put("california_region_id", props.get("california_region_id")); +</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/get_region_data.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Admin Create Order" enabled="true"/> <hashTree> <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Arguments" enabled="true"> @@ -23860,8 +24778,8 @@ catch (java.lang.Exception e) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_create/start/</stringProp> @@ -23903,8 +24821,8 @@ catch (java.lang.Exception e) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -23952,8 +24870,8 @@ catch (java.lang.Exception e) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/configurable-products/${configurable_product_1_sku}/options/all</stringProp> @@ -24075,8 +24993,8 @@ catch (java.lang.Exception e) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_create/loadBlock/block/search,items,shipping_method,totals,giftmessage,billing_method?isAjax=true</stringProp> @@ -24175,8 +25093,8 @@ catch (java.lang.Exception e) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_create/loadBlock/block/shipping_method,totals?isAjax=true</stringProp> @@ -24465,7 +25383,7 @@ catch (java.lang.Exception e) { <elementProp name="order[billing_address][region_id]" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.name">order[billing_address][region_id]</stringProp> - <stringProp name="Argument.value">5</stringProp> + <stringProp name="Argument.value">${alabama_region_id}</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> </elementProp> @@ -24543,8 +25461,8 @@ catch (java.lang.Exception e) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_create/save/</stringProp> @@ -24690,8 +25608,8 @@ catch (java.lang.Exception e) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_invoice/save/order_id/${order_id}/</stringProp> @@ -24757,8 +25675,8 @@ catch (java.lang.Exception e) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/order_shipment/save/order_id/${order_id}/</stringProp> @@ -24791,8 +25709,8 @@ catch (java.lang.Exception e) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -24811,8 +25729,11 @@ catch (java.lang.Exception e) { <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -24895,8 +25816,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -24992,8 +25913,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/customers</stringProp> @@ -25033,8 +25954,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/customers/${customer_id}</stringProp> @@ -25096,8 +26017,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/categories</stringProp> @@ -25148,8 +26069,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/categories/${search_category_id}</stringProp> @@ -25198,8 +26119,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products</stringProp> @@ -25300,8 +26221,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/search</stringProp> @@ -25376,6 +26297,18 @@ if (testLabel </BeanShellSampler> <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Get region data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script"> + vars.put("alabama_region_id", props.get("alabama_region_id")); + vars.put("california_region_id", props.get("california_region_id")); +</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/get_region_data.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Init Random Generator" enabled="true"> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx</stringProp> <stringProp name="BeanShellSampler.query"> @@ -25420,8 +26353,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/carts</stringProp> @@ -25475,8 +26408,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/carts/${quote_id}/items</stringProp> @@ -25506,8 +26439,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/carts/${quote_id}/items</stringProp> @@ -25545,8 +26478,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/guest-carts/</stringProp> @@ -25600,8 +26533,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/guest-carts/${cart_id}/items</stringProp> @@ -25642,8 +26575,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/guest-carts/${cart_id}/gift-message</stringProp> @@ -25679,8 +26612,8 @@ vars.put("product_sku", product.get("sku")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/guest-carts/${cart_id}/estimate-shipping-methods</stringProp> @@ -25731,15 +26664,15 @@ vars.put("product_sku", product.get("sku")); <collectionProp name="Arguments.arguments"> <elementProp name="" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">false</boolProp> - <stringProp name="Argument.value">{"addressInformation":{"shipping_address":{"countryId":"US","regionId":"12","regionCode":"CA","region":"California","street":["10441 Jefferson Blvd ste 200"],"company":"","telephone":"3109450345","fax":"","postcode":"90232","city":"Culver City","firstname":"Name","lastname":"Lastname"},"shipping_method_code":"flatrate","shipping_carrier_code":"flatrate"}}</stringProp> + <stringProp name="Argument.value">{"addressInformation":{"shipping_address":{"countryId":"US","regionId":"${california_region_id}","regionCode":"CA","region":"California","street":["10441 Jefferson Blvd ste 200"],"company":"","telephone":"3109450345","fax":"","postcode":"90232","city":"Culver City","firstname":"Name","lastname":"Lastname"},"shipping_method_code":"flatrate","shipping_carrier_code":"flatrate"}}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> </collectionProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/guest-carts/${cart_id}/shipping-information</stringProp> @@ -25790,15 +26723,15 @@ vars.put("product_sku", product.get("sku")); <collectionProp name="Arguments.arguments"> <elementProp name="" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">false</boolProp> - <stringProp name="Argument.value">{"cartId":"${cart_id}","email":"test@example.com","paymentMethod":{"method":"checkmo","po_number":null,"additional_data":null},"billingAddress":{"countryId":"US","regionId":"12","regionCode":"CA","region":"California","street":["10441 Jefferson Blvd ste 200"],"company":"","telephone":"3109450345","fax":"","postcode":"90232","city":"Culver City","firstname":"Name","lastname":"Lastname","save_in_address_book":0}}</stringProp> + <stringProp name="Argument.value">{"cartId":"${cart_id}","email":"test@example.com","paymentMethod":{"method":"checkmo","po_number":null,"additional_data":null},"billingAddress":{"countryId":"US","regionId":"${california_region_id}","regionCode":"CA","region":"California","street":["10441 Jefferson Blvd ste 200"],"company":"","telephone":"3109450345","fax":"","postcode":"90232","city":"Culver City","firstname":"Name","lastname":"Lastname","save_in_address_book":0}}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> </collectionProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/guest-carts/${cart_id}/payment-information</stringProp> @@ -25914,8 +26847,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products</stringProp> @@ -26006,8 +26939,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products/${simple_product_sku}/stockItems/${simple_stock_item_id}</stringProp> @@ -26043,8 +26976,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products/${simple_product_sku}</stringProp> @@ -26146,8 +27079,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products</stringProp> @@ -26232,8 +27165,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products/${simple_product_sku}</stringProp> @@ -26399,7 +27332,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -26422,11 +27367,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -26503,8 +27448,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -26613,8 +27558,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -26775,8 +27720,8 @@ vars.put("visibility", String.valueOf(randomVisibility)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -26853,8 +27798,8 @@ vars.put("visibility", String.valueOf(randomVisibility)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product_action_attribute/edit</stringProp> @@ -26947,8 +27892,8 @@ vars.put("visibility", String.valueOf(randomVisibility)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product_action_attribute/validate</stringProp> @@ -27062,8 +28007,8 @@ vars.put("visibility", String.valueOf(randomVisibility)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product_action_attribute/save/store/0/active_tab/attributes</stringProp> @@ -27094,8 +28039,8 @@ vars.put("visibility", String.valueOf(randomVisibility)); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -27114,8 +28059,11 @@ vars.put("visibility", String.valueOf(randomVisibility)); <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -27221,7 +28169,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -27244,11 +28204,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -27325,8 +28285,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -27375,8 +28335,8 @@ vars.put("adminImportFilePath", filepath); </stringProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/import/</stringProp> @@ -27457,8 +28417,8 @@ vars.put("adminImportFilePath", filepath); </stringProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/import/validate</stringProp> @@ -27553,8 +28513,8 @@ vars.put("adminImportFilePath", filepath); </stringProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/import/start</stringProp> @@ -27595,8 +28555,8 @@ vars.put("adminImportFilePath", filepath); </stringProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -27615,8 +28575,11 @@ vars.put("adminImportFilePath", filepath); </stringProp> <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -27722,7 +28685,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -27745,11 +28720,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -27826,8 +28801,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -27876,8 +28851,8 @@ vars.put("adminImportFilePath", filepath); </stringProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/import/</stringProp> @@ -27958,8 +28933,8 @@ vars.put("adminImportFilePath", filepath); </stringProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/import/validate</stringProp> @@ -28054,8 +29029,8 @@ vars.put("adminImportFilePath", filepath); </stringProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/import/start</stringProp> @@ -28096,8 +29071,8 @@ vars.put("adminImportFilePath", filepath); </stringProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -28116,8 +29091,11 @@ vars.put("adminImportFilePath", filepath); </stringProp> <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -28223,7 +29201,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -28246,11 +29236,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -28327,8 +29317,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -28365,8 +29355,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/export/</stringProp> @@ -28950,8 +29940,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/export/export/entity/catalog_product/file_format/csv</stringProp> @@ -28982,8 +29972,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -29002,8 +29992,11 @@ vars.put("admin_user", adminUser); <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -29109,7 +30102,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -29132,11 +30137,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -29213,8 +30218,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -29251,8 +30256,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/export/</stringProp> @@ -29491,8 +30496,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/export/export/entity/customer/file_format/csv</stringProp> @@ -29523,8 +30528,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -29543,8 +30548,11 @@ vars.put("admin_user", adminUser); <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -29610,8 +30618,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -29743,8 +30751,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/orders</stringProp> @@ -29779,8 +30787,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/order/${order_id}/invoice</stringProp> @@ -29810,8 +30818,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/order/${order_id}/ship</stringProp> @@ -29888,8 +30896,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products/attribute-sets/</stringProp> @@ -29941,8 +30949,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products/attribute-sets/groups</stringProp> @@ -30002,8 +31010,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products/attributes/</stringProp> @@ -30074,8 +31082,8 @@ if (testLabel </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products/attribute-sets/attributes</stringProp> @@ -30200,7 +31208,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -30223,11 +31243,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -30304,8 +31324,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -30332,191 +31352,6 @@ vars.put("admin_user", adminUser); </hashTree> </hashTree> - <OnceOnlyController guiclass="OnceOnlyControllerGui" testclass="OnceOnlyController" testname="Once Only Controller" enabled="true"> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/once_only_controller.jmx</stringProp> -</OnceOnlyController> - <hashTree> - <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="SetUp - Admin Category Management" enabled="true"> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/setup_admin_category_management.jmx</stringProp> -</GenericController> - <hashTree> - <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - BeanShell Sampler: Clear Admin Category Management properties" enabled="true"> - <stringProp name="BeanShellSampler.query">props.remove("admin_category_ids_list");</stringProp> - <stringProp name="BeanShellSampler.filename"/> - <stringProp name="BeanShellSampler.parameters"/> - <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> - </BeanShellSampler> - <hashTree/> - <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Get categories of last level" enabled="true"/> - <hashTree> - <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> - <collectionProp name="HeaderManager.headers"> - <elementProp name="" elementType="Header"> - <stringProp name="Header.name">Content-Type</stringProp> - <stringProp name="Header.value">application/json</stringProp> - </elementProp> - <elementProp name="" elementType="Header"> - <stringProp name="Header.name">Accept</stringProp> - <stringProp name="Header.value">*/*</stringProp> - </elementProp> - </collectionProp> - </HeaderManager> - <hashTree/> - <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Admin Token Retrieval" enabled="true"> - <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> - <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> - <collectionProp name="Arguments.arguments"> - <elementProp name="" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">false</boolProp> - <stringProp name="Argument.value">{"username":"${admin_user}","password":"${admin_password}"}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - </elementProp> - </collectionProp> - </elementProp> - <stringProp name="HTTPSampler.domain"/> - <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> - <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> - <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> - <stringProp name="HTTPSampler.method">POST</stringProp> - <boolProp name="HTTPSampler.follow_redirects">true</boolProp> - <boolProp name="HTTPSampler.auto_redirects">false</boolProp> - <boolProp name="HTTPSampler.use_keepalive">true</boolProp> - <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> - <boolProp name="HTTPSampler.monitor">false</boolProp> - <stringProp name="HTTPSampler.embedded_url_re"/> - </HTTPSamplerProxy> - <hashTree> - <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="jp@gc - JSON Path Extractor" enabled="true"> - <stringProp name="VAR">admin_token</stringProp> - <stringProp name="JSONPATH">$</stringProp> - <stringProp name="DEFAULT"/> - <stringProp name="VARIABLE"/> - <stringProp name="SUBJECT">BODY</stringProp> - </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> - <hashTree/> - <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert token not null" enabled="true"> - <collectionProp name="Asserion.test_strings"> - <stringProp name="484395188">^[a-z0-9-]+$</stringProp> - </collectionProp> - <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> - <boolProp name="Assertion.assume_success">false</boolProp> - <intProp name="Assertion.test_type">1</intProp> - <stringProp name="Assertion.scope">variable</stringProp> - <stringProp name="Scope.variable">admin_token</stringProp> - </ResponseAssertion> - <hashTree/> - </hashTree> - <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> - <collectionProp name="HeaderManager.headers"> - <elementProp name="" elementType="Header"> - <stringProp name="Header.name">Authorization</stringProp> - <stringProp name="Header.value">Bearer ${admin_token}</stringProp> - </elementProp> - </collectionProp> - </HeaderManager> - <hashTree/> - <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - API Get categories" enabled="true"> - <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> - <collectionProp name="Arguments.arguments"> - <elementProp name="searchCriteria[filterGroups][0][filters][0][field]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">children_count</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][0][field]</stringProp> - </elementProp> - <elementProp name="searchCriteria[filterGroups][0][filters][0][value]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">0</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][0][value]</stringProp> - </elementProp> - <elementProp name="searchCriteria[filterGroups][1][filters][0][field]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">level</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">searchCriteria[filterGroups][1][filters][0][field]</stringProp> - </elementProp> - <elementProp name="searchCriteria[filterGroups][1][filters][0][value]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">2</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">searchCriteria[filterGroups][1][filters][0][value]</stringProp> - </elementProp> - <elementProp name="searchCriteria[filterGroups][1][filters][0][conditionType]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">gt</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">searchCriteria[filterGroups][1][filters][0][conditionType]</stringProp> - </elementProp> - <elementProp name="searchCriteria[pageSize]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${adminCategoryCount}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">searchCriteria[pageSize]</stringProp> - </elementProp> - </collectionProp> - </elementProp> - <stringProp name="HTTPSampler.domain"/> - <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> - <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> - <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/categories/list</stringProp> - <stringProp name="HTTPSampler.method">GET</stringProp> - <boolProp name="HTTPSampler.follow_redirects">true</boolProp> - <boolProp name="HTTPSampler.auto_redirects">false</boolProp> - <boolProp name="HTTPSampler.use_keepalive">true</boolProp> - <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> - <boolProp name="HTTPSampler.monitor">false</boolProp> - <stringProp name="HTTPSampler.embedded_url_re"/> - </HTTPSamplerProxy> - <hashTree> - <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor" enabled="true"> - <stringProp name="RegexExtractor.useHeaders">false</stringProp> - <stringProp name="RegexExtractor.refname">category_list_id</stringProp> - <stringProp name="RegexExtractor.regex">\{\"id\":(\d+),</stringProp> - <stringProp name="RegexExtractor.template">$1$</stringProp> - <stringProp name="RegexExtractor.default"/> - <stringProp name="RegexExtractor.match_number">-1</stringProp> - </RegexExtractor> - <hashTree/> - </hashTree> - <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Category Id" enabled="true"> - <stringProp name="ForeachController.inputVal">category_list_id</stringProp> - <stringProp name="ForeachController.returnVal">category_id</stringProp> - <boolProp name="ForeachController.useSeparator">true</boolProp> - </ForeachController> - <hashTree> - <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="Process categories ids" enabled="true"> - <stringProp name="BeanShellSampler.query">import java.util.ArrayList; - -adminCategoryIdsList = props.get("admin_category_ids_list"); -// If it is first iteration of cycle then recreate categories ids list -if (adminCategoryIdsList == null) { - adminCategoryIdsList = new ArrayList(); - props.put("admin_category_ids_list", adminCategoryIdsList); -} -adminCategoryIdsList.add(vars.get("category_id"));</stringProp> - <stringProp name="BeanShellSampler.filename"/> - <stringProp name="BeanShellSampler.parameters"/> - <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> - </BeanShellSampler> - <hashTree/> - </hashTree> - </hashTree> - </hashTree> - </hashTree> - <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> </GenericController> @@ -30548,13 +31383,13 @@ function getNextProductNumber(i) { } var productsVariationsSize = 5, - productsSize = props.get("simple_products_list").size(); + productsSize = props.get("simple_products_list_for_edit").size(); for (i = 1; i<= productsVariationsSize; i++) { var productVariablePrefix = "simple_product_" + i + "_"; number = getNextProductNumber(i); - simpleList = props.get("simple_products_list").get(number); + simpleList = props.get("simple_products_list_for_edit").get(number); vars.put(productVariablePrefix + "url_key", simpleList.get("url_key")); vars.put(productVariablePrefix + "id", simpleList.get("id")); @@ -30575,8 +31410,8 @@ vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(cate </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/</stringProp> @@ -30626,8 +31461,8 @@ vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(cate </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/edit/id/${parent_category_id}/</stringProp> @@ -30668,8 +31503,8 @@ vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(cate </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/add/store/0/parent/${parent_category_id}</stringProp> @@ -30874,8 +31709,8 @@ vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(cate </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/save/</stringProp> @@ -30915,8 +31750,8 @@ vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(cate </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/edit/id/${admin_category_id}/</stringProp> @@ -31220,8 +32055,8 @@ vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(cate </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/move/</stringProp> @@ -31240,8 +32075,8 @@ vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(cate </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/delete/id/${admin_category_id}/</stringProp> @@ -31279,8 +32114,8 @@ vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(cate </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -31299,8 +32134,11 @@ vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(cate <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -31406,7 +32244,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -31429,11 +32279,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -31510,8 +32360,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -31552,8 +32402,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales_rule/promo_quote/</stringProp> @@ -31572,8 +32422,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales_rule/promo_quote/new</stringProp> @@ -31622,8 +32472,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales_rule/promo_quote/newConditionHtml/form/sales_rule_formrule_conditions_fieldset_/form_namespace/sales_rule_form</stringProp> @@ -31909,8 +32759,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales_rule/promo_quote/save/</stringProp> @@ -31948,8 +32798,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -31968,8 +32818,11 @@ vars.put("admin_user", adminUser); <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -32075,7 +32928,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -32098,11 +32963,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -32179,8 +33044,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -32221,8 +33086,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/customer/index</stringProp> @@ -32320,8 +33185,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -32407,8 +33272,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -32457,8 +33322,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}${customer_edit_url_path}</stringProp> @@ -33554,7 +34419,7 @@ vars.put("admin_user", adminUser); </elementProp> <elementProp name="address[new_0][region_id]" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">12</stringProp> + <stringProp name="Argument.value">${admin_customer_address_region_id}</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">address[new_0][region_id]</stringProp> @@ -33570,8 +34435,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/customer/index/validate/</stringProp> @@ -34043,8 +34908,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/customer/index/save/</stringProp> @@ -34082,8 +34947,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -34102,8 +34967,11 @@ vars.put("admin_user", adminUser); <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -34209,7 +35077,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -34232,11 +35112,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -34313,8 +35193,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -34351,8 +35231,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order/</stringProp> @@ -34462,8 +35342,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -34579,8 +35459,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> @@ -34681,8 +35561,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order/view/order_id/${order_id}/</stringProp> @@ -34751,8 +35631,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order/addComment/order_id/${order_id}/?isAjax=true</stringProp> @@ -34782,8 +35662,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_invoice/start/order_id/${order_id}/</stringProp> @@ -34853,8 +35733,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_invoice/save/order_id/${order_id}/</stringProp> @@ -34884,8 +35764,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/order_shipment/start/order_id/${order_id}/</stringProp> @@ -34945,8 +35825,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/order_shipment/save/order_id/${order_id}/</stringProp> @@ -34978,8 +35858,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -34998,8 +35878,11 @@ vars.put("admin_user", adminUser); <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -35109,7 +35992,19 @@ if (testLabel <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> <stringProp name="BeanShellSampler.query"> adminUserList = props.get("adminUserList"); -adminUser = adminUserList.poll(); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + if (adminUser == null) { SampleResult.setResponseMessage("adminUser list is empty"); SampleResult.setResponseData("adminUser list is empty","UTF-8"); @@ -35132,11 +36027,11 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}</stringProp> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> @@ -35213,8 +36108,8 @@ vars.put("admin_user", adminUser); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> @@ -35392,8 +36287,8 @@ vars.put("related_product_id", props.get("simple_products_list").get(relatedInde </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -35472,8 +36367,8 @@ vars.put("related_product_id", props.get("simple_products_list").get(relatedInde </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products/attributes</stringProp> @@ -35540,8 +36435,8 @@ vars.putObject("product_attributes", attributes); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product_set/index/filter/${attribute_set_filter}</stringProp> @@ -35584,15 +36479,15 @@ Random random = new Random(); if (${seedForRandom} > 0) { random.setSeed(${seedForRandom}); } -number = random.nextInt(props.get("simple_products_list").size()); -simpleList = props.get("simple_products_list").get(number); +number = random.nextInt(props.get("simple_products_list_for_edit").size()); +simpleList = props.get("simple_products_list_for_edit").get(number); vars.put("simple_product_1_id", simpleList.get("id")); vars.put("simple_product_1_name", simpleList.get("title")); do { - number1 = random.nextInt(props.get("simple_products_list").size()); + number1 = random.nextInt(props.get("simple_products_list_for_edit").size()); } while(number == number1); -simpleList = props.get("simple_products_list").get(number1); +simpleList = props.get("simple_products_list_for_edit").get(number1); vars.put("simple_product_2_id", simpleList.get("id")); vars.put("simple_product_2_name", simpleList.get("title")); @@ -35630,8 +36525,8 @@ vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNu </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/</stringProp> @@ -35660,8 +36555,8 @@ vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNu </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/new/set/4/type/bundle/</stringProp> @@ -36563,8 +37458,8 @@ vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNu </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/validate/set/4/</stringProp> @@ -37497,8 +38392,8 @@ vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNu </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/save/set/4/type/bundle/back/edit/active_tab/product-details/</stringProp> @@ -37535,8 +38430,8 @@ vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNu </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> @@ -37555,8 +38450,11 @@ vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNu <stringProp name="parameters"/> <stringProp name="filename"/> <stringProp name="script"> -adminUserList = props.get("adminUserList"); -adminUserList.add(vars.get("admin_user")); + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } </stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> <hashTree/> @@ -37593,8 +38491,8 @@ adminUserList.add(vars.get("admin_user")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -37693,8 +38591,8 @@ adminUserList.add(vars.get("admin_user")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products</stringProp> @@ -39806,8 +40704,8 @@ adminUserList.add(vars.get("admin_user")); </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> @@ -40831,8 +41729,8 @@ if (name == null) { </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout"/> - <stringProp name="HTTPSampler.response_timeout"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}DeploymentEvent.php</stringProp> From 492e8347ff5d354fb2ba93aa07ec9b2776fb4430 Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@magento.com> Date: Fri, 27 Jul 2018 10:41:19 +0300 Subject: [PATCH 0553/1171] MAGETWO-93743: [Forwardport] Base Implementation --- .../PriceIndexerDimensionsModeSetCommand.php | 2 + .../Price/DimensionCollectionFactory.php | 2 + .../Product/Price/PriceTableResolver.php | 1 + .../ResourceModel/Product/Collection.php | 2 - ...stallDefaultPriceIndexerDimensionsMode.php | 1 + .../Model/Plugin/PriceIndexUpdater.php | 2 + .../Magento/ConfigurableProduct/etc/di.xml | 8 + .../CustomerGroupDimensionProvider.php | 1 + .../Indexer/WebsiteDimensionProvider.php | 1 + ...iceIndexerDimensionsModeSetCommandTest.php | 168 +++ .../Indexer/MultiDimensionProvider.php | 1 + .../Test/Unit/MultiDimensionProviderTest.php | 1 + .../Setup/Test/Unit/Model/InstallerTest.php | 1191 +++++++++-------- 13 files changed, 785 insertions(+), 596 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Setup/Console/Command/PriceIndexerDimensionsModeSetCommandTest.php diff --git a/app/code/Magento/Catalog/Console/Command/PriceIndexerDimensionsModeSetCommand.php b/app/code/Magento/Catalog/Console/Command/PriceIndexerDimensionsModeSetCommand.php index 0660d4d6ceb36..e331f4d2a2fc4 100644 --- a/app/code/Magento/Catalog/Console/Command/PriceIndexerDimensionsModeSetCommand.php +++ b/app/code/Magento/Catalog/Console/Command/PriceIndexerDimensionsModeSetCommand.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Console\Command; use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionCollectionFactory.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionCollectionFactory.php index 62a129f0ff448..31459af81ccc7 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionCollectionFactory.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionCollectionFactory.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Product\Price; use Magento\Framework\Indexer\DimensionProviderInterface; diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/PriceTableResolver.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/PriceTableResolver.php index 0e4850d1f9541..eeaa731aac686 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/PriceTableResolver.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/PriceTableResolver.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Model\Indexer\Product\Price; diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index f2b7ba867a704..197fb4b454030 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - namespace Magento\Catalog\Model\ResourceModel\Product; use Magento\Catalog\Api\Data\ProductInterface; diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultPriceIndexerDimensionsMode.php b/app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultPriceIndexerDimensionsMode.php index c4ad5ac0bbb28..9cb417383255e 100644 --- a/app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultPriceIndexerDimensionsMode.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultPriceIndexerDimensionsMode.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Setup\Patch\Data; diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php index 316f7000ae4af..f935569f74b3e 100644 --- a/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php +++ b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogInventory\Model\Plugin; use Magento\CatalogInventory\Model\ResourceModel\Stock\Item; diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml index bba9cdae0debb..a16feacc4f993 100644 --- a/app/code/Magento/ConfigurableProduct/etc/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/di.xml @@ -176,6 +176,14 @@ <argument name="batchSizeAdjusters" xsi:type="array"> <item name="configurable" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\CompositeProductBatchSizeAdjuster</item> </argument> + <!-- + real batch size will be smaller. + It depends on amount configurable product variations. + E.g for 100 variations real batch size will be 50000/100=500 + --> + <argument name="batchRowsCount" xsi:type="array"> + <item name="configurable" xsi:type="number">50000</item> + </argument> </arguments> </type> <type name="Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price\Configurable"> diff --git a/app/code/Magento/Customer/Model/Indexer/CustomerGroupDimensionProvider.php b/app/code/Magento/Customer/Model/Indexer/CustomerGroupDimensionProvider.php index 7c224d29f1dc1..d4e0c5cce3401 100644 --- a/app/code/Magento/Customer/Model/Indexer/CustomerGroupDimensionProvider.php +++ b/app/code/Magento/Customer/Model/Indexer/CustomerGroupDimensionProvider.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Customer\Model\Indexer; diff --git a/app/code/Magento/Store/Model/Indexer/WebsiteDimensionProvider.php b/app/code/Magento/Store/Model/Indexer/WebsiteDimensionProvider.php index 302c2f828367a..6fceeabc3597e 100644 --- a/app/code/Magento/Store/Model/Indexer/WebsiteDimensionProvider.php +++ b/app/code/Magento/Store/Model/Indexer/WebsiteDimensionProvider.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Store\Model\Indexer; diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/PriceIndexerDimensionsModeSetCommandTest.php b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/PriceIndexerDimensionsModeSetCommandTest.php new file mode 100644 index 0000000000000..79028a159d1aa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/PriceIndexerDimensionsModeSetCommandTest.php @@ -0,0 +1,168 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Setup\Console\Command; + +use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; +use Symfony\Component\Console\Tester\CommandTester; +use Magento\Framework\Console\Cli; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Console\Command\PriceIndexerDimensionsModeSetCommand; + +/** + * Class PriceIndexerDimensionsModeSetCommand + * @package Magento\Setup\Console\Command + */ +class PriceIndexerDimensionsModeSetCommandTest extends \Magento\TestFramework\Indexer\TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var GenerateFixturesCommand */ + private $command; + + /** @var CommandTester */ + private $commandTester; + + /** + * setUp + */ + public function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + + $this->objectManager->get(\Magento\TestFramework\App\Config::class)->clean(); + + $this->command = $this->objectManager->create( + \Magento\Catalog\Console\Command\PriceIndexerDimensionsModeSetCommand::class + ); + + $this->commandTester = new CommandTester($this->command); + + parent::setUp(); + } + + /** + * tearDown + */ + public function tearDown() + { + parent::tearDown(); + } + + /** + * setUpBeforeClass + */ + public static function setUpBeforeClass() + { + $db = Bootstrap::getInstance()->getBootstrap() + ->getApplication() + ->getDbInstance(); + if (!$db->isDbDumpExists()) { + throw new \LogicException('DB dump does not exist.'); + } + $db->restoreFromDbDump(); + + parent::setUpBeforeClass(); + } + + /** + * @magentoAppArea adminhtml + * @magentoAppIsolation enabled + * + * @param $previousMode + * @param $currentMode + * @dataProvider modesDataProvider + */ + public function testSwitchMode($previousMode, $currentMode) + { + $this->commandTester->execute( + [ + PriceIndexerDimensionsModeSetCommand::INPUT_KEY_MODE => $currentMode + ] + ); + $expectedOutput = 'Dimensions mode for indexer Product Price was changed from \'' + . $previousMode . '\' to \'' . $currentMode . '\''; + + $actualOutput = $this->commandTester->getDisplay(); + + $this->assertContains($expectedOutput, $actualOutput); + + static::assertEquals( + Cli::RETURN_SUCCESS, + $this->commandTester->getStatusCode(), + $this->commandTester->getDisplay(true) + ); + } + + public function modesDataProvider() + { + return [ + [DimensionModeConfiguration::DIMENSION_NONE, DimensionModeConfiguration::DIMENSION_WEBSITE], + [DimensionModeConfiguration::DIMENSION_WEBSITE, DimensionModeConfiguration::DIMENSION_CUSTOMER_GROUP], + [ + DimensionModeConfiguration::DIMENSION_CUSTOMER_GROUP, + DimensionModeConfiguration::DIMENSION_WEBSITE_AND_CUSTOMER_GROUP + ], + [ + DimensionModeConfiguration::DIMENSION_WEBSITE_AND_CUSTOMER_GROUP, + DimensionModeConfiguration::DIMENSION_NONE + ], + [ + DimensionModeConfiguration::DIMENSION_NONE, + DimensionModeConfiguration::DIMENSION_WEBSITE_AND_CUSTOMER_GROUP + ], + [ + DimensionModeConfiguration::DIMENSION_WEBSITE_AND_CUSTOMER_GROUP, + DimensionModeConfiguration::DIMENSION_CUSTOMER_GROUP + ], + [DimensionModeConfiguration::DIMENSION_CUSTOMER_GROUP, DimensionModeConfiguration::DIMENSION_WEBSITE], + [DimensionModeConfiguration::DIMENSION_WEBSITE, DimensionModeConfiguration::DIMENSION_NONE], + ]; + } + + /** + * @magentoAppArea adminhtml + * @magentoAppIsolation enabled + */ + public function testSwitchModeForSameMode() + { + $this->commandTester->execute( + [ + PriceIndexerDimensionsModeSetCommand::INPUT_KEY_MODE => DimensionModeConfiguration::DIMENSION_NONE + ] + ); + $expectedOutput = 'Dimensions mode for indexer Product Price has not been changed'; + + $actualOutput = $this->commandTester->getDisplay(); + + $this->assertContains($expectedOutput, $actualOutput); + + static::assertEquals( + Cli::RETURN_SUCCESS, + $this->commandTester->getStatusCode(), + $this->commandTester->getDisplay(true) + ); + } + + /** + * @magentoAppArea adminhtml + * @magentoAppIsolation enabled + * + * @expectedException \InvalidArgumentException + */ + public function testSwitchModeWithInvalidArgument() + { + $this->commandTester->execute( + [ + PriceIndexerDimensionsModeSetCommand::INPUT_KEY_MODE => DimensionModeConfiguration::DIMENSION_NONE . + '_not_valid' + ] + ); + } +} diff --git a/lib/internal/Magento/Framework/Indexer/MultiDimensionProvider.php b/lib/internal/Magento/Framework/Indexer/MultiDimensionProvider.php index 062945760acfa..a02637f755b89 100644 --- a/lib/internal/Magento/Framework/Indexer/MultiDimensionProvider.php +++ b/lib/internal/Magento/Framework/Indexer/MultiDimensionProvider.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\Indexer; diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php b/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php index be4d168b108bc..d8e6262b75930 100644 --- a/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php +++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\Indexer\Test\Unit; diff --git a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php index 882483ce7674a..530f64c4d966a 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php @@ -4,635 +4,638 @@ * See COPYING.txt for license details. */ -namespace Magento\Setup\Test\Unit\Model; - -use Magento\Backend\Setup\ConfigOptionsList; -use Magento\Framework\Config\ConfigOptionsListConstants; -use Magento\Framework\Setup\SchemaListener; -use Magento\Setup\Model\DeclarationInstaller; -use Magento\Setup\Model\Installer; -use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\Filesystem\DriverPool; -use Magento\Framework\Config\File\ConfigFilePool; -use Magento\Framework\App\State\CleanupFiles; -use Magento\Framework\Setup\Patch\PatchApplier; -use Magento\Framework\Setup\Patch\PatchApplierFactory; -use Magento\Setup\Validator\DbValidator; - -/** - * @SuppressWarnings(PHPMD.TooManyFields) - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class InstallerTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Setup\Model\Installer - */ - private $object; - - /** - * @var \Magento\Framework\Setup\FilePermissions|\PHPUnit_Framework_MockObject_MockObject - */ - private $filePermissions; - - /** - * @var \Magento\Framework\App\DeploymentConfig\Writer|\PHPUnit_Framework_MockObject_MockObject - */ - private $configWriter; - - /** - * @var \Magento\Framework\App\DeploymentConfig\Reader|\PHPUnit_Framework_MockObject_MockObject - */ - private $configReader; - - /** - * @var \Magento\Framework\App\DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject - */ - private $config; - - /** - * @var \Magento\Framework\Module\ModuleListInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $moduleList; - - /** - * @var \Magento\Framework\Module\ModuleList\Loader|\PHPUnit_Framework_MockObject_MockObject - */ - private $moduleLoader; - - /** - * @var \Magento\Framework\App\Filesystem\DirectoryList|\PHPUnit_Framework_MockObject_MockObject - */ - private $directoryList; - - /** - * @var \Magento\Setup\Model\AdminAccountFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $adminFactory; - - /** - * @var \Magento\Framework\Setup\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $logger; - - /** - * @var \Magento\Framework\Math\Random|\PHPUnit_Framework_MockObject_MockObject - */ - private $random; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $connection; - - /** - * @var \Magento\Framework\App\MaintenanceMode|\PHPUnit_Framework_MockObject_MockObject - */ - private $maintenanceMode; - - /** - * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject - */ - private $filesystem; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $objectManager; - - /** - * @var \Magento\Setup\Model\ConfigModel|\PHPUnit_Framework_MockObject_MockObject - */ - private $configModel; - - /** - * @var CleanupFiles|\PHPUnit_Framework_MockObject_MockObject - */ - private $cleanupFiles; - - /** - * @var DbValidator|\PHPUnit_Framework_MockObject_MockObject - */ - private $dbValidator; - - /** - * @var \Magento\Setup\Module\SetupFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $setupFactory; - - /** - * @var \Magento\Setup\Module\DataSetupFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $dataSetupFactory; - - /** - * @var \Magento\Framework\Setup\SampleData\State|\PHPUnit_Framework_MockObject_MockObject - */ - private $sampleDataState; - - /** - * @var \Magento\Framework\Component\ComponentRegistrar|\PHPUnit_Framework_MockObject_MockObject - */ - private $componentRegistrar; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Setup\Model\PhpReadinessCheck - */ - private $phpReadinessCheck; - - /** - * @var \Magento\Framework\Setup\DeclarationInstaller|\PHPUnit_Framework_MockObject_MockObject - */ - private $declarationInstallerMock; - - /** - * @var SchemaListener|\PHPUnit_Framework_MockObject_MockObject - */ - private $schemaListenerMock; - - /** - * Sample DB configuration segment - * - * @var array - */ - private static $dbConfig = [ - 'default' => [ - ConfigOptionsListConstants::KEY_HOST => '127.0.0.1', - ConfigOptionsListConstants::KEY_NAME => 'magento', - ConfigOptionsListConstants::KEY_USER => 'magento', - ConfigOptionsListConstants::KEY_PASSWORD => '', - ] - ]; - - /** - * @var \Magento\Framework\Model\ResourceModel\Db\Context|\PHPUnit_Framework_MockObject_MockObject - */ - private $contextMock; - - /** - * @var PatchApplier|\PHPUnit_Framework_MockObject_MockObject - */ - private $patchApplierMock; - - /** - * @var PatchApplierFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $patchApplierFactoryMock; - - protected function setUp() +namespace Magento\Setup\Test\Unit\Model { + + use Magento\Backend\Setup\ConfigOptionsList; + use Magento\Framework\Config\ConfigOptionsListConstants; + use Magento\Framework\Setup\SchemaListener; + use Magento\Setup\Model\DeclarationInstaller; + use Magento\Setup\Model\Installer; + use Magento\Framework\App\Filesystem\DirectoryList; + use Magento\Framework\Filesystem\DriverPool; + use Magento\Framework\Config\File\ConfigFilePool; + use Magento\Framework\App\State\CleanupFiles; + use Magento\Framework\Setup\Patch\PatchApplier; + use Magento\Framework\Setup\Patch\PatchApplierFactory; + use Magento\Setup\Validator\DbValidator; + + /** + * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ + class InstallerTest extends \PHPUnit\Framework\TestCase { - $this->filePermissions = $this->createMock(\Magento\Framework\Setup\FilePermissions::class); - $this->configWriter = $this->createMock(\Magento\Framework\App\DeploymentConfig\Writer::class); - $this->configReader = $this->createMock(\Magento\Framework\App\DeploymentConfig\Reader::class); - $this->config = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); - - $this->moduleList = $this->getMockForAbstractClass(\Magento\Framework\Module\ModuleListInterface::class); - $this->moduleList->expects($this->any())->method('getOne')->willReturn( - ['setup_version' => '2.0.0'] - ); - $this->moduleList->expects($this->any())->method('getNames')->willReturn( - ['Foo_One', 'Bar_Two'] - ); - $this->moduleLoader = $this->createMock(\Magento\Framework\Module\ModuleList\Loader::class); - $this->directoryList = - $this->createMock(\Magento\Framework\App\Filesystem\DirectoryList::class); - $this->adminFactory = $this->createMock(\Magento\Setup\Model\AdminAccountFactory::class); - $this->logger = $this->getMockForAbstractClass(\Magento\Framework\Setup\LoggerInterface::class); - $this->random = $this->createMock(\Magento\Framework\Math\Random::class); - $this->connection = $this->getMockForAbstractClass(\Magento\Framework\DB\Adapter\AdapterInterface::class); - $this->maintenanceMode = $this->createMock(\Magento\Framework\App\MaintenanceMode::class); - $this->filesystem = $this->createMock(\Magento\Framework\Filesystem::class); - $this->objectManager = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class); - $this->contextMock = - $this->createMock(\Magento\Framework\Model\ResourceModel\Db\Context::class); - $this->configModel = $this->createMock(\Magento\Setup\Model\ConfigModel::class); - $this->cleanupFiles = $this->createMock(\Magento\Framework\App\State\CleanupFiles::class); - $this->dbValidator = $this->createMock(\Magento\Setup\Validator\DbValidator::class); - $this->setupFactory = $this->createMock(\Magento\Setup\Module\SetupFactory::class); - $this->dataSetupFactory = $this->createMock(\Magento\Setup\Module\DataSetupFactory::class); - $this->sampleDataState = $this->createMock(\Magento\Framework\Setup\SampleData\State::class); - $this->componentRegistrar = - $this->createMock(\Magento\Framework\Component\ComponentRegistrar::class); - $this->phpReadinessCheck = $this->createMock(\Magento\Setup\Model\PhpReadinessCheck::class); - $this->declarationInstallerMock = $this->createMock(DeclarationInstaller::class); - $this->schemaListenerMock = $this->createMock(SchemaListener::class); - $this->patchApplierFactoryMock = $this->createMock(PatchApplierFactory::class); - $this->patchApplierMock = $this->createMock(PatchApplier::class); - $this->patchApplierFactoryMock->expects($this->any())->method('create')->willReturn($this->patchApplierMock); - $this->object = $this->createObject(); - } + /** + * @var \Magento\Setup\Model\Installer + */ + private $object; + + /** + * @var \Magento\Framework\Setup\FilePermissions|\PHPUnit_Framework_MockObject_MockObject + */ + private $filePermissions; + + /** + * @var \Magento\Framework\App\DeploymentConfig\Writer|\PHPUnit_Framework_MockObject_MockObject + */ + private $configWriter; + + /** + * @var \Magento\Framework\App\DeploymentConfig\Reader|\PHPUnit_Framework_MockObject_MockObject + */ + private $configReader; + + /** + * @var \Magento\Framework\App\DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject + */ + private $config; + + /** + * @var \Magento\Framework\Module\ModuleListInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $moduleList; + + /** + * @var \Magento\Framework\Module\ModuleList\Loader|\PHPUnit_Framework_MockObject_MockObject + */ + private $moduleLoader; + + /** + * @var \Magento\Framework\App\Filesystem\DirectoryList|\PHPUnit_Framework_MockObject_MockObject + */ + private $directoryList; + + /** + * @var \Magento\Setup\Model\AdminAccountFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $adminFactory; + + /** + * @var \Magento\Framework\Setup\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $logger; + + /** + * @var \Magento\Framework\Math\Random|\PHPUnit_Framework_MockObject_MockObject + */ + private $random; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $connection; + + /** + * @var \Magento\Framework\App\MaintenanceMode|\PHPUnit_Framework_MockObject_MockObject + */ + private $maintenanceMode; + + /** + * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject + */ + private $filesystem; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $objectManager; + + /** + * @var \Magento\Setup\Model\ConfigModel|\PHPUnit_Framework_MockObject_MockObject + */ + private $configModel; + + /** + * @var CleanupFiles|\PHPUnit_Framework_MockObject_MockObject + */ + private $cleanupFiles; + + /** + * @var DbValidator|\PHPUnit_Framework_MockObject_MockObject + */ + private $dbValidator; + + /** + * @var \Magento\Setup\Module\SetupFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $setupFactory; + + /** + * @var \Magento\Setup\Module\DataSetupFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $dataSetupFactory; + + /** + * @var \Magento\Framework\Setup\SampleData\State|\PHPUnit_Framework_MockObject_MockObject + */ + private $sampleDataState; + + /** + * @var \Magento\Framework\Component\ComponentRegistrar|\PHPUnit_Framework_MockObject_MockObject + */ + private $componentRegistrar; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Setup\Model\PhpReadinessCheck + */ + private $phpReadinessCheck; + + /** + * @var \Magento\Framework\Setup\DeclarationInstaller|\PHPUnit_Framework_MockObject_MockObject + */ + private $declarationInstallerMock; + + /** + * @var SchemaListener|\PHPUnit_Framework_MockObject_MockObject + */ + private $schemaListenerMock; + + /** + * Sample DB configuration segment + * @var array + */ + private static $dbConfig = [ + 'default' => [ + ConfigOptionsListConstants::KEY_HOST => '127.0.0.1', + ConfigOptionsListConstants::KEY_NAME => 'magento', + ConfigOptionsListConstants::KEY_USER => 'magento', + ConfigOptionsListConstants::KEY_PASSWORD => '', + ] + ]; - /** - * Instantiates the object with mocks - * - * @param \PHPUnit_Framework_MockObject_MockObject|bool $connectionFactory - * @param \PHPUnit_Framework_MockObject_MockObject|bool $objectManagerProvider - * @return Installer - */ - private function createObject($connectionFactory = false, $objectManagerProvider = false) - { - if (!$connectionFactory) { - $connectionFactory = $this->createMock(\Magento\Setup\Module\ConnectionFactory::class); - $connectionFactory->expects($this->any())->method('create')->willReturn($this->connection); - } - if (!$objectManagerProvider) { - $objectManagerProvider = - $this->createMock(\Magento\Setup\Model\ObjectManagerProvider::class); - $objectManagerProvider->expects($this->any())->method('get')->willReturn($this->objectManager); + /** + * @var \Magento\Framework\Model\ResourceModel\Db\Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var PatchApplier|\PHPUnit_Framework_MockObject_MockObject + */ + private $patchApplierMock; + + /** + * @var PatchApplierFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $patchApplierFactoryMock; + + protected function setUp() + { + $this->filePermissions = $this->createMock(\Magento\Framework\Setup\FilePermissions::class); + $this->configWriter = $this->createMock(\Magento\Framework\App\DeploymentConfig\Writer::class); + $this->configReader = $this->createMock(\Magento\Framework\App\DeploymentConfig\Reader::class); + $this->config = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); + + $this->moduleList = $this->getMockForAbstractClass(\Magento\Framework\Module\ModuleListInterface::class); + $this->moduleList->expects($this->any())->method('getOne')->willReturn( + ['setup_version' => '2.0.0'] + ); + $this->moduleList->expects($this->any())->method('getNames')->willReturn( + ['Foo_One', 'Bar_Two'] + ); + $this->moduleLoader = $this->createMock(\Magento\Framework\Module\ModuleList\Loader::class); + $this->directoryList = + $this->createMock(\Magento\Framework\App\Filesystem\DirectoryList::class); + $this->adminFactory = $this->createMock(\Magento\Setup\Model\AdminAccountFactory::class); + $this->logger = $this->getMockForAbstractClass(\Magento\Framework\Setup\LoggerInterface::class); + $this->random = $this->createMock(\Magento\Framework\Math\Random::class); + $this->connection = $this->getMockForAbstractClass(\Magento\Framework\DB\Adapter\AdapterInterface::class); + $this->maintenanceMode = $this->createMock(\Magento\Framework\App\MaintenanceMode::class); + $this->filesystem = $this->createMock(\Magento\Framework\Filesystem::class); + $this->objectManager = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class); + $this->contextMock = + $this->createMock(\Magento\Framework\Model\ResourceModel\Db\Context::class); + $this->configModel = $this->createMock(\Magento\Setup\Model\ConfigModel::class); + $this->cleanupFiles = $this->createMock(\Magento\Framework\App\State\CleanupFiles::class); + $this->dbValidator = $this->createMock(\Magento\Setup\Validator\DbValidator::class); + $this->setupFactory = $this->createMock(\Magento\Setup\Module\SetupFactory::class); + $this->dataSetupFactory = $this->createMock(\Magento\Setup\Module\DataSetupFactory::class); + $this->sampleDataState = $this->createMock(\Magento\Framework\Setup\SampleData\State::class); + $this->componentRegistrar = + $this->createMock(\Magento\Framework\Component\ComponentRegistrar::class); + $this->phpReadinessCheck = $this->createMock(\Magento\Setup\Model\PhpReadinessCheck::class); + $this->declarationInstallerMock = $this->createMock(DeclarationInstaller::class); + $this->schemaListenerMock = $this->createMock(SchemaListener::class); + $this->patchApplierFactoryMock = $this->createMock(PatchApplierFactory::class); + $this->patchApplierMock = $this->createMock(PatchApplier::class); + $this->patchApplierFactoryMock->expects($this->any())->method('create')->willReturn($this->patchApplierMock); + $this->object = $this->createObject(); } - return new Installer( - $this->filePermissions, - $this->configWriter, - $this->configReader, - $this->config, - $this->moduleList, - $this->moduleLoader, - $this->adminFactory, - $this->logger, - $connectionFactory, - $this->maintenanceMode, - $this->filesystem, - $objectManagerProvider, - $this->contextMock, - $this->configModel, - $this->cleanupFiles, - $this->dbValidator, - $this->setupFactory, - $this->dataSetupFactory, - $this->sampleDataState, - $this->componentRegistrar, - $this->phpReadinessCheck, - $this->declarationInstallerMock - ); - } + /** + * Instantiates the object with mocks + * @param \PHPUnit_Framework_MockObject_MockObject|bool $connectionFactory + * @param \PHPUnit_Framework_MockObject_MockObject|bool $objectManagerProvider + * @return Installer + */ + private function createObject($connectionFactory = false, $objectManagerProvider = false) + { + if (!$connectionFactory) { + $connectionFactory = $this->createMock(\Magento\Setup\Module\ConnectionFactory::class); + $connectionFactory->expects($this->any())->method('create')->willReturn($this->connection); + } + if (!$objectManagerProvider) { + $objectManagerProvider = + $this->createMock(\Magento\Setup\Model\ObjectManagerProvider::class); + $objectManagerProvider->expects($this->any())->method('get')->willReturn($this->objectManager); + } + + return new Installer( + $this->filePermissions, + $this->configWriter, + $this->configReader, + $this->config, + $this->moduleList, + $this->moduleLoader, + $this->adminFactory, + $this->logger, + $connectionFactory, + $this->maintenanceMode, + $this->filesystem, + $objectManagerProvider, + $this->contextMock, + $this->configModel, + $this->cleanupFiles, + $this->dbValidator, + $this->setupFactory, + $this->dataSetupFactory, + $this->sampleDataState, + $this->componentRegistrar, + $this->phpReadinessCheck, + $this->declarationInstallerMock + ); + } - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testInstall() - { - $request = [ - ConfigOptionsListConstants::INPUT_KEY_DB_HOST => '127.0.0.1', - ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'magento', - ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'magento', - ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => 'encryption_key', - ConfigOptionsList::INPUT_KEY_BACKEND_FRONTNAME => 'backend', - ]; - $this->config->expects($this->atLeastOnce()) - ->method('get') - ->willReturnMap( + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testInstall() + { + $request = [ + ConfigOptionsListConstants::INPUT_KEY_DB_HOST => '127.0.0.1', + ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'magento', + ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'magento', + ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => 'encryption_key', + ConfigOptionsList::INPUT_KEY_BACKEND_FRONTNAME => 'backend', + ]; + $this->config->expects($this->atLeastOnce()) + ->method('get') + ->willReturnMap( + [ + [ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT, null, true], + [ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY, null, true], + ['modules/Magento_User', null, '1'] + ] + ); + $allModules = ['Foo_One' => [], 'Bar_Two' => []]; + + $this->declarationInstallerMock->expects($this->once())->method('installSchema'); + $this->moduleLoader->expects($this->any())->method('load')->willReturn($allModules); + $setup = $this->createMock(\Magento\Setup\Module\Setup::class); + $table = $this->createMock(\Magento\Framework\DB\Ddl\Table::class); + $connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->setMethods(['getSchemaListener', 'newTable']) + ->getMockForAbstractClass(); + $connection->expects($this->any())->method('getSchemaListener')->willReturn($this->schemaListenerMock); + $setup->expects($this->any())->method('getConnection')->willReturn($connection); + $table->expects($this->any())->method('addColumn')->willReturn($table); + $table->expects($this->any())->method('setComment')->willReturn($table); + $table->expects($this->any())->method('addIndex')->willReturn($table); + $connection->expects($this->any())->method('newTable')->willReturn($table); + $resource = $this->createMock(\Magento\Framework\App\ResourceConnection::class); + $this->contextMock->expects($this->any())->method('getResources')->willReturn($resource); + $resource->expects($this->any())->method('getConnection')->will($this->returnValue($connection)); + $dataSetup = $this->createMock(\Magento\Setup\Module\DataSetup::class); + $dataSetup->expects($this->any())->method('getConnection')->willReturn($connection); + $cacheManager = $this->createMock(\Magento\Framework\App\Cache\Manager::class); + $cacheManager->expects($this->any())->method('getAvailableTypes')->willReturn(['foo', 'bar']); + $cacheManager->expects($this->once())->method('setEnabled')->willReturn(['foo', 'bar']); + $cacheManager->expects($this->any())->method('clean'); + $appState = $this->getMockBuilder(\Magento\Framework\App\State::class) + ->disableOriginalConstructor() + ->disableArgumentCloning() + ->getMock(); + $appState->expects($this->once()) + ->method('setAreaCode') + ->with(\Magento\Framework\App\Area::AREA_GLOBAL); + $registry = $this->createMock(\Magento\Framework\Registry::class); + $this->setupFactory->expects($this->atLeastOnce())->method('create')->with($resource)->willReturn($setup); + $this->dataSetupFactory->expects($this->atLeastOnce())->method('create')->willReturn($dataSetup); + $this->objectManager->expects($this->any()) + ->method('create') + ->will($this->returnValueMap([ + [\Magento\Framework\App\Cache\Manager::class, [], $cacheManager], + [\Magento\Framework\App\State::class, [], $appState], + [ + PatchApplierFactory::class, + ['objectManager' => $this->objectManager], + $this->patchApplierFactoryMock + ], + ])); + $this->patchApplierMock->expects($this->exactly(2))->method('applySchemaPatch')->willReturnMap( [ - [ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT, null, true], - [ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY, null, true], - ['modules/Magento_User', null, '1'] + ['Bar_Two'], + ['Foo_One'], ] ); - $allModules = ['Foo_One' => [], 'Bar_Two' => []]; - - $this->declarationInstallerMock->expects($this->once())->method('installSchema'); - $this->moduleLoader->expects($this->any())->method('load')->willReturn($allModules); - $setup = $this->createMock(\Magento\Setup\Module\Setup::class); - $table = $this->createMock(\Magento\Framework\DB\Ddl\Table::class); - $connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) - ->setMethods(['getSchemaListener', 'newTable']) - ->getMockForAbstractClass(); - $connection->expects($this->any())->method('getSchemaListener')->willReturn($this->schemaListenerMock); - $setup->expects($this->any())->method('getConnection')->willReturn($connection); - $table->expects($this->any())->method('addColumn')->willReturn($table); - $table->expects($this->any())->method('setComment')->willReturn($table); - $table->expects($this->any())->method('addIndex')->willReturn($table); - $connection->expects($this->any())->method('newTable')->willReturn($table); - $resource = $this->createMock(\Magento\Framework\App\ResourceConnection::class); - $this->contextMock->expects($this->any())->method('getResources')->willReturn($resource); - $resource->expects($this->any())->method('getConnection')->will($this->returnValue($connection)); - $dataSetup = $this->createMock(\Magento\Setup\Module\DataSetup::class); - $dataSetup->expects($this->any())->method('getConnection')->willReturn($connection); - $cacheManager = $this->createMock(\Magento\Framework\App\Cache\Manager::class); - $cacheManager->expects($this->any())->method('getAvailableTypes')->willReturn(['foo', 'bar']); - $cacheManager->expects($this->once())->method('setEnabled')->willReturn(['foo', 'bar']); - $cacheManager->expects($this->any())->method('clean'); - $appState = $this->getMockBuilder(\Magento\Framework\App\State::class) - ->disableOriginalConstructor() - ->disableArgumentCloning() - ->getMock(); - $appState->expects($this->once()) - ->method('setAreaCode') - ->with(\Magento\Framework\App\Area::AREA_GLOBAL); - $registry = $this->createMock(\Magento\Framework\Registry::class); - $this->setupFactory->expects($this->atLeastOnce())->method('create')->with($resource)->willReturn($setup); - $this->dataSetupFactory->expects($this->atLeastOnce())->method('create')->willReturn($dataSetup); - $this->objectManager->expects($this->any()) - ->method('create') - ->will($this->returnValueMap([ - [\Magento\Framework\App\Cache\Manager::class, [], $cacheManager], - [\Magento\Framework\App\State::class, [], $appState], + $this->patchApplierMock->expects($this->exactly(2))->method('applyDataPatch')->willReturnMap( [ - PatchApplierFactory::class, - ['objectManager' => $this->objectManager], - $this->patchApplierFactoryMock - ], - ])); - $this->patchApplierMock->expects($this->exactly(2))->method('applySchemaPatch')->willReturnMap( - [ - ['Bar_Two'], - ['Foo_One'], - ] - ); - $this->patchApplierMock->expects($this->exactly(2))->method('applyDataPatch')->willReturnMap( - [ - ['Bar_Two'], - ['Foo_One'], - ] - ); - $this->objectManager->expects($this->any()) - ->method('get') - ->will($this->returnValueMap([ - [\Magento\Framework\App\State::class, $appState], - [\Magento\Framework\App\Cache\Manager::class, $cacheManager], - [\Magento\Setup\Model\DeclarationInstaller::class, $this->declarationInstallerMock], - [\Magento\Framework\Registry::class, $registry] - ])); - $this->adminFactory->expects($this->once())->method('create')->willReturn( - $this->createMock(\Magento\Setup\Model\AdminAccount::class) - ); - $this->sampleDataState->expects($this->once())->method('hasError')->willReturn(true); - $this->phpReadinessCheck->expects($this->once())->method('checkPhpExtensions')->willReturn( - ['responseType' => \Magento\Setup\Controller\ResponseTypeInterface::RESPONSE_TYPE_SUCCESS] - ); - $this->filePermissions->expects($this->any()) - ->method('getMissingWritablePathsForInstallation') - ->willReturn([]); - $this->filePermissions->expects($this->once()) - ->method('getMissingWritableDirectoriesForDbUpgrade') - ->willReturn([]); - $this->setupLoggerExpectsForInstall(); - - $this->object->install($request); - } - - public function testCheckInstallationFilePermissions() - { - $this->filePermissions - ->expects($this->once()) - ->method('getMissingWritablePathsForInstallation') - ->willReturn([]); - $this->object->checkInstallationFilePermissions(); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage Missing write permissions to the following paths: - */ - public function testCheckInstallationFilePermissionsError() - { - $this->filePermissions - ->expects($this->once()) - ->method('getMissingWritablePathsForInstallation') - ->willReturn(['foo', 'bar']); - $this->object->checkInstallationFilePermissions(); - } + ['Bar_Two'], + ['Foo_One'], + ] + ); + $this->objectManager->expects($this->any()) + ->method('get') + ->will($this->returnValueMap([ + [\Magento\Framework\App\State::class, $appState], + [\Magento\Framework\App\Cache\Manager::class, $cacheManager], + [\Magento\Setup\Model\DeclarationInstaller::class, $this->declarationInstallerMock], + [\Magento\Framework\Registry::class, $registry] + ])); + $this->adminFactory->expects($this->once())->method('create')->willReturn( + $this->createMock(\Magento\Setup\Model\AdminAccount::class) + ); + $this->sampleDataState->expects($this->once())->method('hasError')->willReturn(true); + $this->phpReadinessCheck->expects($this->once())->method('checkPhpExtensions')->willReturn( + ['responseType' => \Magento\Setup\Controller\ResponseTypeInterface::RESPONSE_TYPE_SUCCESS] + ); + $this->filePermissions->expects($this->any()) + ->method('getMissingWritablePathsForInstallation') + ->willReturn([]); + $this->filePermissions->expects($this->once()) + ->method('getMissingWritableDirectoriesForDbUpgrade') + ->willReturn([]); + $this->setupLoggerExpectsForInstall(); + + $this->object->install($request); + } - public function testCheckExtensions() - { - $this->phpReadinessCheck->expects($this->once())->method('checkPhpExtensions')->willReturn( - ['responseType' => \Magento\Setup\Controller\ResponseTypeInterface::RESPONSE_TYPE_SUCCESS] - ); - $this->object->checkExtensions(); - } + public function testCheckInstallationFilePermissions() + { + $this->filePermissions + ->expects($this->once()) + ->method('getMissingWritablePathsForInstallation') + ->willReturn([]); + $this->object->checkInstallationFilePermissions(); + } - /** - * @expectedException \Exception - * @expectedExceptionMessage Missing following extensions: 'foo' - */ - public function testCheckExtensionsError() - { - $this->phpReadinessCheck->expects($this->once())->method('checkPhpExtensions')->willReturn( - ['responseType' => \Magento\Setup\Controller\ResponseTypeInterface::RESPONSE_TYPE_ERROR, - 'data'=>['required'=>['foo', 'bar'], 'missing'=>['foo']]] - ); - $this->object->checkExtensions(); - } + /** + * @expectedException \Exception + * @expectedExceptionMessage Missing write permissions to the following paths: + */ + public function testCheckInstallationFilePermissionsError() + { + $this->filePermissions + ->expects($this->once()) + ->method('getMissingWritablePathsForInstallation') + ->willReturn(['foo', 'bar']); + $this->object->checkInstallationFilePermissions(); + } - public function testCheckApplicationFilePermissions() - { - $this->filePermissions - ->expects($this->once()) - ->method('getUnnecessaryWritableDirectoriesForApplication') - ->willReturn(['foo', 'bar']); - $expectedMessage = "For security, remove write permissions from these directories: 'foo' 'bar'"; - $this->logger->expects($this->once())->method('log')->with($expectedMessage); - $this->object->checkApplicationFilePermissions(); - $this->assertSame(['message' => [$expectedMessage]], $this->object->getInstallInfo()); - } + public function testCheckExtensions() + { + $this->phpReadinessCheck->expects($this->once())->method('checkPhpExtensions')->willReturn( + ['responseType' => \Magento\Setup\Controller\ResponseTypeInterface::RESPONSE_TYPE_SUCCESS] + ); + $this->object->checkExtensions(); + } - public function testUpdateModulesSequence() - { - $this->cleanupFiles->expects($this->once())->method('clearCodeGeneratedFiles')->will( - $this->returnValue( + /** + * @expectedException \Exception + * @expectedExceptionMessage Missing following extensions: 'foo' + */ + public function testCheckExtensionsError() + { + $this->phpReadinessCheck->expects($this->once())->method('checkPhpExtensions')->willReturn( [ - "The directory '/generation' doesn't exist - skipping cleanup", + 'responseType' => \Magento\Setup\Controller\ResponseTypeInterface::RESPONSE_TYPE_ERROR, + 'data' => ['required' => ['foo', 'bar'], 'missing' => ['foo']] ] - ) - ); - $installer = $this->prepareForUpdateModulesTests(); - - $this->logger->expects($this->at(0))->method('log')->with('Cache cleared successfully'); - $this->logger->expects($this->at(1))->method('log')->with('File system cleanup:'); - $this->logger->expects($this->at(2))->method('log') - ->with('The directory \'/generation\' doesn\'t exist - skipping cleanup'); - $this->logger->expects($this->at(3))->method('log')->with('Updating modules:'); - $installer->updateModulesSequence(false); - } + ); + $this->object->checkExtensions(); + } - public function testUpdateModulesSequenceKeepGenerated() - { - $this->cleanupFiles->expects($this->never())->method('clearCodeGeneratedClasses'); + public function testCheckApplicationFilePermissions() + { + $this->filePermissions + ->expects($this->once()) + ->method('getUnnecessaryWritableDirectoriesForApplication') + ->willReturn(['foo', 'bar']); + $expectedMessage = "For security, remove write permissions from these directories: 'foo' 'bar'"; + $this->logger->expects($this->once())->method('log')->with($expectedMessage); + $this->object->checkApplicationFilePermissions(); + $this->assertSame(['message' => [$expectedMessage]], $this->object->getInstallInfo()); + } - $installer = $this->prepareForUpdateModulesTests(); + public function testUpdateModulesSequence() + { + $this->cleanupFiles->expects($this->once())->method('clearCodeGeneratedFiles')->will( + $this->returnValue( + [ + "The directory '/generation' doesn't exist - skipping cleanup", + ] + ) + ); + $installer = $this->prepareForUpdateModulesTests(); + + $this->logger->expects($this->at(0))->method('log')->with('Cache cleared successfully'); + $this->logger->expects($this->at(1))->method('log')->with('File system cleanup:'); + $this->logger->expects($this->at(2))->method('log') + ->with('The directory \'/generation\' doesn\'t exist - skipping cleanup'); + $this->logger->expects($this->at(3))->method('log')->with('Updating modules:'); + $installer->updateModulesSequence(false); + } - $this->logger->expects($this->at(0))->method('log')->with('Cache cleared successfully'); - $this->logger->expects($this->at(1))->method('log')->with('Updating modules:'); - $installer->updateModulesSequence(true); - } + public function testUpdateModulesSequenceKeepGenerated() + { + $this->cleanupFiles->expects($this->never())->method('clearCodeGeneratedClasses'); - public function testUninstall() - { - $this->config->expects($this->once()) - ->method('get') - ->with(ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTIONS) - ->willReturn([]); - $this->configReader->expects($this->once())->method('getFiles')->willReturn(['ConfigOne.php', 'ConfigTwo.php']); - $configDir = $this->getMockForAbstractClass( - \Magento\Framework\Filesystem\Directory\WriteInterface::class - ); - $configDir - ->expects($this->exactly(2)) - ->method('getAbsolutePath') - ->will( - $this->returnValueMap( + $installer = $this->prepareForUpdateModulesTests(); + + $this->logger->expects($this->at(0))->method('log')->with('Cache cleared successfully'); + $this->logger->expects($this->at(1))->method('log')->with('Updating modules:'); + $installer->updateModulesSequence(true); + } + + public function testUninstall() + { + $this->config->expects($this->once()) + ->method('get') + ->with(ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTIONS) + ->willReturn([]); + $this->configReader->expects($this->once())->method('getFiles')->willReturn([ + 'ConfigOne.php', + 'ConfigTwo.php' + ]); + $configDir = $this->getMockForAbstractClass( + \Magento\Framework\Filesystem\Directory\WriteInterface::class + ); + $configDir + ->expects($this->exactly(2)) + ->method('getAbsolutePath') + ->will( + $this->returnValueMap( + [ + ['ConfigOne.php', '/config/ConfigOne.php'], + ['ConfigTwo.php', '/config/ConfigTwo.php'] + ] + ) + ); + $this->filesystem + ->expects($this->any()) + ->method('getDirectoryWrite') + ->will($this->returnValueMap([ + [DirectoryList::CONFIG, DriverPool::FILE, $configDir], + ])); + $this->logger->expects($this->at(0))->method('log')->with('Starting Magento uninstallation:'); + $this->logger + ->expects($this->at(2)) + ->method('log') + ->with('No database connection defined - skipping database cleanup'); + $cacheManager = $this->createMock(\Magento\Framework\App\Cache\Manager::class); + $cacheManager->expects($this->once())->method('getAvailableTypes')->willReturn(['foo', 'bar']); + $cacheManager->expects($this->once())->method('clean'); + $this->objectManager->expects($this->any()) + ->method('get') + ->with(\Magento\Framework\App\Cache\Manager::class) + ->willReturn($cacheManager); + $this->logger->expects($this->at(1))->method('log')->with('Cache cleared successfully'); + $this->logger->expects($this->at(3))->method('log')->with('File system cleanup:'); + $this->logger + ->expects($this->at(4)) + ->method('log') + ->with("The directory '/var' doesn't exist - skipping cleanup"); + $this->logger + ->expects($this->at(5)) + ->method('log') + ->with("The directory '/static' doesn't exist - skipping cleanup"); + $this->logger + ->expects($this->at(6)) + ->method('log') + ->with("The file '/config/ConfigOne.php' doesn't exist - skipping cleanup"); + $this->logger + ->expects($this->at(7)) + ->method('log') + ->with("The file '/config/ConfigTwo.php' doesn't exist - skipping cleanup"); + $this->logger->expects($this->once())->method('logSuccess')->with('Magento uninstallation complete.'); + $this->cleanupFiles->expects($this->once())->method('clearAllFiles')->will( + $this->returnValue( [ - ['ConfigOne.php', '/config/ConfigOne.php'], - ['ConfigTwo.php', '/config/ConfigTwo.php'] + "The directory '/var' doesn't exist - skipping cleanup", + "The directory '/static' doesn't exist - skipping cleanup" ] ) ); - $this->filesystem - ->expects($this->any()) - ->method('getDirectoryWrite') - ->will($this->returnValueMap([ - [DirectoryList::CONFIG, DriverPool::FILE, $configDir], - ])); - $this->logger->expects($this->at(0))->method('log')->with('Starting Magento uninstallation:'); - $this->logger - ->expects($this->at(2)) - ->method('log') - ->with('No database connection defined - skipping database cleanup'); - $cacheManager = $this->createMock(\Magento\Framework\App\Cache\Manager::class); - $cacheManager->expects($this->once())->method('getAvailableTypes')->willReturn(['foo', 'bar']); - $cacheManager->expects($this->once())->method('clean'); - $this->objectManager->expects($this->any()) - ->method('get') - ->with(\Magento\Framework\App\Cache\Manager::class) - ->willReturn($cacheManager); - $this->logger->expects($this->at(1))->method('log')->with('Cache cleared successfully'); - $this->logger->expects($this->at(3))->method('log')->with('File system cleanup:'); - $this->logger - ->expects($this->at(4)) - ->method('log') - ->with("The directory '/var' doesn't exist - skipping cleanup"); - $this->logger - ->expects($this->at(5)) - ->method('log') - ->with("The directory '/static' doesn't exist - skipping cleanup"); - $this->logger - ->expects($this->at(6)) - ->method('log') - ->with("The file '/config/ConfigOne.php' doesn't exist - skipping cleanup"); - $this->logger - ->expects($this->at(7)) - ->method('log') - ->with("The file '/config/ConfigTwo.php' doesn't exist - skipping cleanup"); - $this->logger->expects($this->once())->method('logSuccess')->with('Magento uninstallation complete.'); - $this->cleanupFiles->expects($this->once())->method('clearAllFiles')->will( - $this->returnValue( - [ - "The directory '/var' doesn't exist - skipping cleanup", - "The directory '/static' doesn't exist - skipping cleanup" - ] - ) - ); - - $this->object->uninstall(); - } - public function testCleanupDb() - { - $this->config->expects($this->once()) - ->method('get') - ->with(ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTIONS) - ->willReturn(self::$dbConfig); - $this->connection->expects($this->at(0))->method('quoteIdentifier')->with('magento')->willReturn('`magento`'); - $this->connection->expects($this->at(1))->method('query')->with('DROP DATABASE IF EXISTS `magento`'); - $this->connection->expects($this->at(2))->method('query')->with('CREATE DATABASE IF NOT EXISTS `magento`'); - $this->logger->expects($this->once())->method('log')->with('Cleaning up database `magento`'); - $this->object->cleanupDb(); - } + $this->object->uninstall(); + } - /** - * Prepare mocks for update modules tests and returns the installer to use - * - * @return Installer - */ - private function prepareForUpdateModulesTests() - { - $allModules = [ - 'Foo_One' => [], - 'Bar_Two' => [], - 'New_Module' => [], - ]; + public function testCleanupDb() + { + $this->config->expects($this->once()) + ->method('get') + ->with(ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTIONS) + ->willReturn(self::$dbConfig); + $this->connection->expects($this->at(0))->method('quoteIdentifier')->with('magento')->willReturn('`magento`'); + $this->connection->expects($this->at(1))->method('query')->with('DROP DATABASE IF EXISTS `magento`'); + $this->connection->expects($this->at(2))->method('query')->with('CREATE DATABASE IF NOT EXISTS `magento`'); + $this->logger->expects($this->once())->method('log')->with('Cleaning up database `magento`'); + $this->object->cleanupDb(); + } - $cacheManager = $this->createMock(\Magento\Framework\App\Cache\Manager::class); - $cacheManager->expects($this->once())->method('getAvailableTypes')->willReturn(['foo', 'bar']); - $cacheManager->expects($this->once())->method('clean'); - $this->objectManager->expects($this->any()) - ->method('get') - ->will($this->returnValueMap([ - [\Magento\Framework\App\Cache\Manager::class, $cacheManager] - ])); - $this->moduleLoader->expects($this->once())->method('load')->willReturn($allModules); - - $expectedModules = [ - ConfigFilePool::APP_CONFIG => [ - 'modules' => [ - 'Bar_Two' => 0, - 'Foo_One' => 1, - 'New_Module' => 1 + /** + * Prepare mocks for update modules tests and returns the installer to use + * @return Installer + */ + private function prepareForUpdateModulesTests() + { + $allModules = [ + 'Foo_One' => [], + 'Bar_Two' => [], + 'New_Module' => [], + ]; + + $cacheManager = $this->createMock(\Magento\Framework\App\Cache\Manager::class); + $cacheManager->expects($this->once())->method('getAvailableTypes')->willReturn(['foo', 'bar']); + $cacheManager->expects($this->once())->method('clean'); + $this->objectManager->expects($this->any()) + ->method('get') + ->will($this->returnValueMap([ + [\Magento\Framework\App\Cache\Manager::class, $cacheManager] + ])); + $this->moduleLoader->expects($this->once())->method('load')->willReturn($allModules); + + $expectedModules = [ + ConfigFilePool::APP_CONFIG => [ + 'modules' => [ + 'Bar_Two' => 0, + 'Foo_One' => 1, + 'New_Module' => 1 + ] ] - ] - ]; + ]; + + $this->config->expects($this->atLeastOnce()) + ->method('get') + ->with(ConfigOptionsListConstants::KEY_MODULES) + ->willReturn(true); - $this->config->expects($this->atLeastOnce()) - ->method('get') - ->with(ConfigOptionsListConstants::KEY_MODULES) - ->willReturn(true); + $newObject = $this->createObject(false, false); + $this->configReader->expects($this->once())->method('load') + ->willReturn(['modules' => ['Bar_Two' => 0, 'Foo_One' => 1, 'Old_Module' => 0]]); + $this->configWriter->expects($this->once())->method('saveConfig')->with($expectedModules); - $newObject = $this->createObject(false, false); - $this->configReader->expects($this->once())->method('load') - ->willReturn(['modules' => ['Bar_Two' => 0, 'Foo_One' => 1, 'Old_Module' => 0] ]); - $this->configWriter->expects($this->once())->method('saveConfig')->with($expectedModules); + return $newObject; + } - return $newObject; + /** + * Set up logger expectations for install method + * @return void + */ + private function setupLoggerExpectsForInstall() + { + $this->logger->expects($this->at(0))->method('log')->with('Starting Magento installation:'); + $this->logger->expects($this->at(1))->method('log')->with('File permissions check...'); + $this->logger->expects($this->at(3))->method('log')->with('Required extensions check...'); + // at(2) invokes logMeta() + $this->logger->expects($this->at(5))->method('log')->with('Enabling Maintenance Mode...'); + // at(4) - logMeta and so on... + $this->logger->expects($this->at(7))->method('log')->with('Installing deployment configuration...'); + $this->logger->expects($this->at(9))->method('log')->with('Installing database schema:'); + $this->logger->expects($this->at(11))->method('log')->with("Module 'Foo_One':"); + $this->logger->expects($this->at(13))->method('log')->with("Module 'Bar_Two':"); + $this->logger->expects($this->at(15))->method('log')->with('Schema post-updates:'); + $this->logger->expects($this->at(16))->method('log')->with("Module 'Foo_One':"); + $this->logger->expects($this->at(18))->method('log')->with("Module 'Bar_Two':"); + $this->logger->expects($this->at(21))->method('log')->with('Installing user configuration...'); + $this->logger->expects($this->at(23))->method('log')->with('Enabling caches:'); + $this->logger->expects($this->at(27))->method('log')->with('Installing data...'); + $this->logger->expects($this->at(28))->method('log')->with('Data install/update:'); + $this->logger->expects($this->at(29))->method('log')->with("Module 'Foo_One':"); + $this->logger->expects($this->at(31))->method('log')->with("Module 'Bar_Two':"); + $this->logger->expects($this->at(33))->method('log')->with('Data post-updates:'); + $this->logger->expects($this->at(34))->method('log')->with("Module 'Foo_One':"); + $this->logger->expects($this->at(36))->method('log')->with("Module 'Bar_Two':"); + $this->logger->expects($this->at(39))->method('log')->with('Installing admin user...'); + $this->logger->expects($this->at(41))->method('log')->with('Caches clearing:'); + $this->logger->expects($this->at(44))->method('log')->with('Disabling Maintenance Mode:'); + $this->logger->expects($this->at(46))->method('log')->with('Post installation file permissions check...'); + $this->logger->expects($this->at(48))->method('log')->with('Write installation date...'); + $this->logger->expects($this->at(50))->method('logSuccess')->with('Magento installation complete.'); + $this->logger->expects($this->at(52))->method('log') + ->with('Sample Data is installed with errors. See log file for details'); + } } +} + +namespace Magento\Setup\Model { /** - * Set up logger expectations for install method + * Mocking autoload function * - * @return void + * @returns array */ - private function setupLoggerExpectsForInstall() + function spl_autoload_functions() { - $this->logger->expects($this->at(0))->method('log')->with('Starting Magento installation:'); - $this->logger->expects($this->at(1))->method('log')->with('File permissions check...'); - $this->logger->expects($this->at(3))->method('log')->with('Required extensions check...'); - // at(2) invokes logMeta() - $this->logger->expects($this->at(5))->method('log')->with('Enabling Maintenance Mode...'); - // at(4) - logMeta and so on... - $this->logger->expects($this->at(7))->method('log')->with('Installing deployment configuration...'); - $this->logger->expects($this->at(9))->method('log')->with('Installing database schema:'); - $this->logger->expects($this->at(11))->method('log')->with("Module 'Foo_One':"); - $this->logger->expects($this->at(13))->method('log')->with("Module 'Bar_Two':"); - $this->logger->expects($this->at(15))->method('log')->with('Schema post-updates:'); - $this->logger->expects($this->at(16))->method('log')->with("Module 'Foo_One':"); - $this->logger->expects($this->at(18))->method('log')->with("Module 'Bar_Two':"); - $this->logger->expects($this->at(21))->method('log')->with('Installing user configuration...'); - $this->logger->expects($this->at(23))->method('log')->with('Enabling caches:'); - $this->logger->expects($this->at(27))->method('log')->with('Installing data...'); - $this->logger->expects($this->at(28))->method('log')->with('Data install/update:'); - $this->logger->expects($this->at(29))->method('log')->with("Module 'Foo_One':"); - $this->logger->expects($this->at(31))->method('log')->with("Module 'Bar_Two':"); - $this->logger->expects($this->at(33))->method('log')->with('Data post-updates:'); - $this->logger->expects($this->at(34))->method('log')->with("Module 'Foo_One':"); - $this->logger->expects($this->at(36))->method('log')->with("Module 'Bar_Two':"); - $this->logger->expects($this->at(39))->method('log')->with('Installing admin user...'); - $this->logger->expects($this->at(41))->method('log')->with('Caches clearing:'); - $this->logger->expects($this->at(44))->method('log')->with('Disabling Maintenance Mode:'); - $this->logger->expects($this->at(46))->method('log')->with('Post installation file permissions check...'); - $this->logger->expects($this->at(48))->method('log')->with('Write installation date...'); - $this->logger->expects($this->at(50))->method('logSuccess')->with('Magento installation complete.'); - $this->logger->expects($this->at(52))->method('log') - ->with('Sample Data is installed with errors. See log file for details'); + return ['mock_function_one', 'mock_function_two']; } } - -namespace Magento\Setup\Model; - -/** - * Mocking autoload function - * - * @returns array - */ -function spl_autoload_functions() -{ - return ['mock_function_one', 'mock_function_two']; -} From 2d7b9b8898fa2e6c3903b62b22e29237750190a0 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 27 Jul 2018 10:58:29 +0300 Subject: [PATCH 0554/1171] MAGETWO-93776: [Forwardport] Stabilization --- .../Model/Indexer/Product/Price/AbstractAction.php | 12 ++++++------ .../Product/Price/DimensionModeConfiguration.php | 1 + .../Model/Indexer/Product/Price/ModeSwitcher.php | 2 +- .../Model/Indexer/Product/Price/TableMaintainer.php | 2 +- .../CatalogRule/_files/configurable_product.php | 1 + .../_files/configurable_product_rollback.php | 1 + .../Magento/CatalogRule/_files/simple_products.php | 1 + .../CatalogRule/_files/simple_products_rollback.php | 1 + 8 files changed, 13 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php index 4c7159b6e4f82..e9a907f0b5097 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php @@ -130,14 +130,14 @@ public function __construct( $this->_defaultIndexerResource = $defaultIndexerResource; $this->_connection = $this->_defaultIndexerResource->getConnection(); $this->tierPriceIndexResource = $tierPriceIndexResource ?? ObjectManager::getInstance()->get( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\TierPrice::class - ); + \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\TierPrice::class + ); $this->dimensionCollectionFactory = $dimensionCollectionFactory ?? ObjectManager::getInstance()->get( - \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory::class - ); + \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory::class + ); $this->tableMaintainer = $tableMaintainer ?? ObjectManager::getInstance()->get( - \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer::class - ); + \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer::class + ); } /** diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionModeConfiguration.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionModeConfiguration.php index e5935dac92a36..9b8eb55b7aac5 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionModeConfiguration.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionModeConfiguration.php @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Product\Price; use Magento\Framework\App\Config\ScopeConfigInterface; diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php index b5f8425a855dc..81a170fc0a3d7 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php @@ -3,8 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Product\Price; use Magento\Framework\Search\Request\Dimension; diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/TableMaintainer.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/TableMaintainer.php index 999eaaa2a8025..e3077baaeb7a6 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/TableMaintainer.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/TableMaintainer.php @@ -3,8 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Product\Price; use Magento\Framework\App\ResourceConnection; diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php index 971f9f82dc1ec..ef7144d9d8438 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute.php'; require __DIR__ . '/simple_products.php'; diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php index d4311d523d3c7..915da48c915b2 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); \Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php index 2855ebb4c92b3..84ce4e1bca87c 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); require __DIR__ . '/attribute.php'; diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php index a46d1db297e2d..6625b1926fc10 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); \Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); From ae98ce37a8cd02a9b86a6eb1843b8a1cea2f0e4c Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 27 Jul 2018 11:20:54 +0300 Subject: [PATCH 0555/1171] MAGETWO-93776: [Forwardport] Stabilization --- .../Model/ResourceModel/Product/Collection.php | 5 ++++- .../Product/Indexer/Price/DefaultPrice.php | 12 ++++++------ .../Model/ResourceModel/Stock/Item.php | 6 +++--- .../Magento/Setup/Test/Unit/Model/InstallerTest.php | 8 ++++++-- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 197fb4b454030..c2b3d8cf39273 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -495,7 +495,10 @@ public function isEnabledFlat() protected function _construct() { if ($this->isEnabledFlat()) { - $this->_init(\Magento\Catalog\Model\Product::class, \Magento\Catalog\Model\ResourceModel\Product\Flat::class); + $this->_init( + \Magento\Catalog\Model\Product::class, + \Magento\Catalog\Model\ResourceModel\Product\Flat::class + ); } else { $this->_init(\Magento\Catalog\Model\Product::class, \Magento\Catalog\Model\ResourceModel\Product::class); } 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 f0cf9b99e6d67..849c12238db5a 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 @@ -360,34 +360,34 @@ protected function getSelect($entityIds = null, $type = null) 'pw.product_id = e.entity_id AND pw.website_id = cw.website_id', [] )->joinLeft( - // we need this only for BCC in case someone expects table `tp` to be present in query + // we need this only for BCC in case someone expects table `tp` to be present in query ['tp' => $this->getTable('catalog_product_index_tier_price')], 'tp.entity_id = e.entity_id AND tp.customer_group_id = cg.customer_group_id' . ' AND tp.website_id = pw.website_id', [] )->joinLeft( - // calculate tier price specified as Website = `All Websites` and Customer Group = `Specific Customer Group` + // calculate tier price specified as Website = `All Websites` and Customer Group = `Specific Customer Group` ['tier_price_1' => $this->getTable('catalog_product_entity_tier_price')], 'tier_price_1.' . $linkField . ' = e.' . $linkField . ' AND tier_price_1.all_groups = 0' . ' AND tier_price_1.customer_group_id = cg.customer_group_id AND tier_price_1.qty = 1' . ' AND tier_price_1.website_id = 0', [] )->joinLeft( - // calculate tier price specified as Website = `Specific Website` - //and Customer Group = `Specific Customer Group` + // calculate tier price specified as Website = `Specific Website` + //and Customer Group = `Specific Customer Group` ['tier_price_2' => $this->getTable('catalog_product_entity_tier_price')], 'tier_price_2.' . $linkField . ' = e.' . $linkField . ' AND tier_price_2.all_groups = 0' . ' AND tier_price_2.customer_group_id = cg.customer_group_id AND tier_price_2.qty = 1' . ' AND tier_price_2.website_id = cw.website_id', [] )->joinLeft( - // calculate tier price specified as Website = `All Websites` and Customer Group = `ALL GROUPS` + // calculate tier price specified as Website = `All Websites` and Customer Group = `ALL GROUPS` ['tier_price_3' => $this->getTable('catalog_product_entity_tier_price')], 'tier_price_3.' . $linkField . ' = e.' . $linkField . ' AND tier_price_3.all_groups = 1' . ' AND tier_price_3.customer_group_id = 0 AND tier_price_3.qty = 1 AND tier_price_3.website_id = 0', [] )->joinLeft( - // calculate tier price specified as Website = `Specific Website` and Customer Group = `ALL GROUPS` + // calculate tier price specified as Website = `Specific Website` and Customer Group = `ALL GROUPS` ['tier_price_4' => $this->getTable('catalog_product_entity_tier_price')], 'tier_price_4.' . $linkField . ' = e.' . $linkField . ' AND tier_price_4.all_groups = 1' . ' AND tier_price_4.customer_group_id = 0 AND tier_price_4.qty = 1' . diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php index 22562461c2860..ce8930ad4f7a6 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php @@ -241,9 +241,9 @@ public function updateLowStockDate(int $websiteId) $connection = $this->getConnection(); $condition = $connection->quoteInto( - '(use_config_notify_stock_qty = 1 AND qty < ?)', - $this->stockConfiguration->getNotifyStockQty() - ) . ' OR (use_config_notify_stock_qty = 0 AND qty < notify_stock_qty)'; + '(use_config_notify_stock_qty = 1 AND qty < ?)', + $this->stockConfiguration->getNotifyStockQty() + ) . ' OR (use_config_notify_stock_qty = 0 AND qty < notify_stock_qty)'; $currentDbTime = $connection->quoteInto('?', $this->dateTime->gmtDate()); $conditionalDate = $connection->getCheckSql($condition, $currentDbTime, 'NULL'); $value = [ diff --git a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php index 530f64c4d966a..8a4263be18ecf 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php @@ -217,7 +217,9 @@ protected function setUp() $this->schemaListenerMock = $this->createMock(SchemaListener::class); $this->patchApplierFactoryMock = $this->createMock(PatchApplierFactory::class); $this->patchApplierMock = $this->createMock(PatchApplier::class); - $this->patchApplierFactoryMock->expects($this->any())->method('create')->willReturn($this->patchApplierMock); + $this->patchApplierFactoryMock->expects($this->any())->method('create')->willReturn( + $this->patchApplierMock + ); $this->object = $this->createObject(); } @@ -535,7 +537,9 @@ public function testCleanupDb() ->method('get') ->with(ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTIONS) ->willReturn(self::$dbConfig); - $this->connection->expects($this->at(0))->method('quoteIdentifier')->with('magento')->willReturn('`magento`'); + $this->connection->expects($this->at(0))->method('quoteIdentifier')->with('magento')->willReturn( + '`magento`' + ); $this->connection->expects($this->at(1))->method('query')->with('DROP DATABASE IF EXISTS `magento`'); $this->connection->expects($this->at(2))->method('query')->with('CREATE DATABASE IF NOT EXISTS `magento`'); $this->logger->expects($this->once())->method('log')->with('Cleaning up database `magento`'); From d441e1489ec82e4e78643e00c67a91b0d745f47a Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 27 Jul 2018 13:27:13 +0300 Subject: [PATCH 0556/1171] MAGETWO-93776: [Forwardport] Stabilization --- .../Unit/Model/ResourceModel/Product/CollectionTest.php | 9 ++++++--- .../Indexer/Test/Unit/MultiDimensionProviderTest.php | 1 - 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php index c73e772de3702..dbbb3fb29513b 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php @@ -6,6 +6,7 @@ namespace Magento\Catalog\Test\Unit\Model\ResourceModel\Product; use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; +use Magento\Framework\DB\Select; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -239,7 +240,7 @@ public function testAddMediaGalleryData() $mediaGalleriesMock = [[$linkField => $rowId]]; $itemMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->disableOriginalConstructor() - ->setMethods(['getData']) + ->setMethods(['getOrigData']) ->getMock(); $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) ->disableOriginalConstructor() @@ -254,8 +255,10 @@ public function testAddMediaGalleryData() $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]); + $itemMock->expects($this->atLeastOnce())->method('getOrigData')->willReturn($rowId); + $selectMock->expects($this->once())->method('reset')->with(Select::ORDER)->willReturnSelf(); + $selectMock->expects($this->once())->method('where')->with('entity.' . $linkField . ' IN (?)', [$rowId]) + ->willReturnSelf(); $this->metadataPoolMock->expects($this->once())->method('getMetadata')->willReturn($metadataMock); $metadataMock->expects($this->once())->method('getLinkField')->willReturn($linkField); diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php b/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php index d8e6262b75930..be4d168b108bc 100644 --- a/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php +++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); namespace Magento\Framework\Indexer\Test\Unit; From 49418a6644699b5ba2492940a23689509217e6bd Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 27 Jul 2018 13:31:03 +0300 Subject: [PATCH 0557/1171] MAGETWO-93776: [Forwardport] Stabilization --- .../Model/Indexer/Category/Product/Plugin/StoreGroup.php | 2 +- .../Catalog/Model/Indexer/Category/Product/Plugin/StoreView.php | 2 +- .../Catalog/Model/Indexer/Category/Product/Plugin/Website.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/StoreGroup.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/StoreGroup.php index 9f4e19bf95a8d..005936a75e6d6 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/StoreGroup.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/StoreGroup.php @@ -97,7 +97,7 @@ protected function validate(AbstractModel $group) public function afterDelete(AbstractDb $subject, AbstractDb $objectResource, AbstractModel $storeGroup) { foreach ($storeGroup->getStores() as $store) { - $this->tableMaintainer->dropTablesForStore($store->getId()); + $this->tableMaintainer->dropTablesForStore((int)$store->getId()); } return $objectResource; } diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/StoreView.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/StoreView.php index 114d2a94f5b35..b6f9e6adf4a1c 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/StoreView.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/StoreView.php @@ -51,7 +51,7 @@ public function afterSave(AbstractDb $subject, AbstractDb $objectResource, Abstr */ public function afterDelete(AbstractDb $subject, AbstractDb $objectResource, AbstractModel $store) { - $this->tableMaintainer->dropTablesForStore($store->getId()); + $this->tableMaintainer->dropTablesForStore((int)$store->getId()); return $objectResource; } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/Website.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/Website.php index 387a8085310e4..50700e672237e 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/Website.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/Website.php @@ -39,7 +39,7 @@ public function __construct( public function afterDelete(AbstractDb $subject, AbstractDb $objectResource, AbstractModel $website) { foreach ($website->getStoreIds() as $storeId) { - $this->tableMaintainer->dropTablesForStore($storeId); + $this->tableMaintainer->dropTablesForStore((int)$storeId); } return $objectResource; } From d197f238f4e239ec57b4d6d638e3c40475bfa82b Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 27 Jul 2018 13:53:06 +0300 Subject: [PATCH 0558/1171] MAGETWO-93776: [Forwardport] Stabilization --- .../Test/Unit/MultiDimensionProviderTest.php | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php b/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php index be4d168b108bc..b55ace9bdec3d 100644 --- a/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php +++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/MultiDimensionProviderTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\Indexer\Test\Unit; @@ -57,22 +58,22 @@ public function testWithMultipleDataProviders() { // prepare expected dimensions $dimensionXData = [ - $this->getDimensionMock('x', 1), - $this->getDimensionMock('x', 2), - $this->getDimensionMock('x', 3), + $this->getDimensionMock('x', '1'), + $this->getDimensionMock('x', '2'), + $this->getDimensionMock('x', '3'), ]; $dimensionYData = [ - $this->getDimensionMock('y', 1), - $this->getDimensionMock('y', 2), - $this->getDimensionMock('y', 3), - $this->getDimensionMock('y', 4), - $this->getDimensionMock('y', 5), + $this->getDimensionMock('y', '1'), + $this->getDimensionMock('y', '2'), + $this->getDimensionMock('y', '3'), + $this->getDimensionMock('y', '4'), + $this->getDimensionMock('y', '5'), ]; $dimensionZData = [ - $this->getDimensionMock('z', 1), - $this->getDimensionMock('z', 2), + $this->getDimensionMock('z', '1'), + $this->getDimensionMock('z', '2'), ]; $expectedDimensions = []; @@ -113,14 +114,14 @@ public function testMultiDimensionProviderIsReIterable() { // prepare expected dimensions $dimensionXData = [ - $this->getDimensionMock('x', 1), - $this->getDimensionMock('x', 2), - $this->getDimensionMock('x', 3), + $this->getDimensionMock('x', '1'), + $this->getDimensionMock('x', '2'), + $this->getDimensionMock('x', '3'), ]; $dimensionZData = [ - $this->getDimensionMock('z', 1), - $this->getDimensionMock('z', 2), + $this->getDimensionMock('z', '1'), + $this->getDimensionMock('z', '2'), ]; // collect actual dimensions @@ -179,17 +180,17 @@ public function testMultiDimensionProviderWithMixedDataProvider() // prepare expected dimensions $dimensionXData = [ - $this->getDimensionMock('x', 1), - $this->getDimensionMock('x', 2), - $this->getDimensionMock('x', 3), + $this->getDimensionMock('x', '1'), + $this->getDimensionMock('x', '2'), + $this->getDimensionMock('x', '3'), ]; $dimensionYData = [ - $this->getDimensionMock('y', 1), - $this->getDimensionMock('y', 2), - $this->getDimensionMock('y', 3), - $this->getDimensionMock('y', 4), - $this->getDimensionMock('y', 5), + $this->getDimensionMock('y', '1'), + $this->getDimensionMock('y', '2'), + $this->getDimensionMock('y', '3'), + $this->getDimensionMock('y', '4'), + $this->getDimensionMock('y', '5'), ]; $dimensionZData = []; From 8b946714b6fdb6e16cda4cf51aa5c352d1a1c3cc Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 27 Jul 2018 14:28:51 +0300 Subject: [PATCH 0559/1171] MAGETWO-93776: [Forwardport] Stabilization --- .../Magento/Quote/Model/Quote/AddressTest.php | 52 ------------------- 1 file changed, 52 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php index a609e730de269..62e417d27c51b 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php @@ -5,7 +5,6 @@ */ namespace Magento\Quote\Model\Quote; -use Magento\Store\Api\StoreRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; /** @@ -26,9 +25,6 @@ class AddressTest extends \Magento\TestFramework\Indexer\TestCase /**@var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */ protected $customerRepository; - /** @var StoreRepositoryInterface */ - private $storeRepository; - public static function setUpBeforeClass() { $db = \Magento\TestFramework\Helper\Bootstrap::getInstance()->getBootstrap() @@ -65,8 +61,6 @@ public function setUp() $this->_address->setId(1); $this->_address->load($this->_address->getId()); $this->_address->setQuote($this->_quote); - $this->storeRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->create(StoreRepositoryInterface::class); } protected function tearDown() @@ -328,50 +322,4 @@ public function dataProvider() [[123, true], [123, true]] ]; } - - /** - * Tests different shipping rates for different stores. - * - * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php - * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php - * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php - * @magentoDataFixture Magento/Sales/_files/quote.php - * @magentoConfigFixture default_store carriers/flatrate/price 5 - * @magentoConfigFixture fixture_second_store_store carriers/flatrate/price 10 - * @magentoAppIsolation enabled - * @magentoDbIsolation disabled - * @dataProvider requestShippingRatesDataProvider - */ - public function testRequestShippingRates($storeCode, $expectedRate) - { - $store = $this->storeRepository->get($storeCode); - $this->_quote->setStoreId($store->getId()); - $this->_address->setItemQty(1); - $this->_address->requestShippingRates(); - /** - * @var \Magento\Quote\Model\ResourceModel\Quote\Address\Rate\Collection $shippingRatesCollection - */ - $shippingRatesCollection = $this->_address->getShippingRatesCollection(); - /** - * @var \Magento\Quote\Model\Quote\Address\Rate[] $shippingRates - */ - $shippingRates = $shippingRatesCollection->getItems(); - self::assertEquals( - $expectedRate, - $shippingRates[0]->getPrice() - ); - } - - /** - * Data provider for testRequestShippingRates. - * - * @return array - */ - public function requestShippingRatesDataProvider() - { - return [ - ['default', 5], - ['fixture_second_store', 10], - ]; - } } From 0fc68f97623d3559f5503284bc179a8c50ad7c27 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 27 Jul 2018 14:31:53 +0300 Subject: [PATCH 0560/1171] MAGETWO-93776: [Forwardport] Stabilization --- ...ple_products_with_non_saleable_product.php | 52 +++++++++++++++++++ ...cts_with_non_saleable_product_rollback.php | 28 ++++++++++ 2 files changed, 80 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product.php new file mode 100644 index 0000000000000..1b8fd1cd02552 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var $product \Magento\Catalog\Model\Product */ +$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); +$product->isObjectNew(true); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setName('Simple Product') + ->setSku('simple_saleable') + ->setTaxClassId('none') + ->setDescription('description') + ->setShortDescription('short description') + ->setOptionsContainer('container1') + ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_IN_CART) + ->setPrice(10) + ->setWeight(1) + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setWebsiteIds([1]) + ->setCategoryIds([]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->save(); + +$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); +$product->isObjectNew(true); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setName('Simple Product2') + ->setSku('simple_not_saleable') + ->setTaxClassId('none') + ->setDescription('description') + ->setShortDescription('short description') + ->setOptionsContainer('container1') + ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_ON_GESTURE) + ->setPrice(20) + ->setWeight(1) + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setWebsiteIds([1]) + ->setCategoryIds([]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 50, 'is_qty_decimal' => 0, 'is_in_stock' => 0]) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product_rollback.php new file mode 100644 index 0000000000000..63fffd1894b03 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +foreach (['simple_saleable', 'simple_not_saleable'] as $sku) { + try { + $product = $productRepository->get($sku, false, null, true); + $productRepository->delete($product); + } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + //Product already removed + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From 88440fd06e61b901029f45c553622a21d4e62587 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 27 Jul 2018 14:33:27 +0300 Subject: [PATCH 0561/1171] MAGETWO-93776: [Forwardport] Stabilization --- .../_files/multiple_products_with_non_saleable_product.php | 1 + .../multiple_products_with_non_saleable_product_rollback.php | 1 + 2 files changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product.php index 1b8fd1cd02552..30b2aad2317c4 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); /** @var $product \Magento\Catalog\Model\Product */ $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product_rollback.php index 63fffd1894b03..220509698dc33 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_products_with_non_saleable_product_rollback.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); From 864f7426a0865c3828f4616e36d38c8bc016c31f Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Fri, 27 Jul 2018 17:36:38 +0530 Subject: [PATCH 0562/1171] Update ProductRuleTest.php --- .../Magento/CatalogRule/Model/Indexer/ProductRuleTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php index c7c93d1bcc40f..911c7aa30641e 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/ProductRuleTest.php @@ -67,7 +67,7 @@ public function testReindexWithProductNotVisibleIndividually() $this->assertEquals( 7.5, $this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $product->getId()), - "Catalog price rule doesn't ap product with visibility value \"Not Visibility Individually\"" + "Catalog price rule doesn't apply to product with visibility value \"Not Visibility Individually\"" ); } } From 31985f56030fbe547e49298b1e44fb1101bc2c02 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 27 Jul 2018 15:55:10 +0300 Subject: [PATCH 0563/1171] MAGETWO-93776: [Forwardport] Stabilization --- .../Magento/Catalog/Model/ResourceModel/Product/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index c2b3d8cf39273..4384effc4e359 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -1700,7 +1700,7 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) return $this; } elseif ($attribute == 'is_saleable') { - $this->getSelect()->order("is_saleable " . $dir); + $this->getSelect()->order("is_salable " . $dir); return $this; } From fbc317e5542f88e2d368818614d0f87fe89b0ed2 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 27 Jul 2018 15:57:37 +0300 Subject: [PATCH 0564/1171] MAGETWO-93776: [Forwardport] Stabilization --- .../Magento/Catalog/_files/product_with_options.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php index a6e01370dfefb..cac5c026ec5ab 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php @@ -11,8 +11,6 @@ $product->setTypeId( 'simple' -)->setId( - 1 )->setAttributeSetId( 4 )->setWebsiteIds( @@ -38,8 +36,7 @@ )->setStockData( [ 'qty' => 100, - 'is_in_stock' => 1, - 'manage_stock' => 1, + 'is_in_stock' => 1 ] )->setHasOptions(true); @@ -49,7 +46,7 @@ 'type' => 'field', 'is_require' => true, 'sort_order' => 1, - 'price' => 10.0, + 'price' => -10.0, 'price_type' => 'fixed', 'sku' => 'sku1', 'max_characters' => 10, From e8119ed64d34750a55f38c468c23c335a211391b Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 27 Jul 2018 16:31:01 +0300 Subject: [PATCH 0565/1171] MAGETWO-93776: [Forwardport] Stabilization --- .../testsuite/Magento/Catalog/_files/product_with_options.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php index cac5c026ec5ab..a9bc557fd9b72 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php @@ -36,7 +36,8 @@ )->setStockData( [ 'qty' => 100, - 'is_in_stock' => 1 + 'is_in_stock' => 1, + 'manage_stock' => 1, ] )->setHasOptions(true); From a8d4549623d3cc8297321e8303f25c108c724653 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 27 Jul 2018 16:39:26 +0300 Subject: [PATCH 0566/1171] MAGETWO-93776: [Forwardport] Stabilization --- .../testsuite/Magento/Catalog/_files/product_with_options.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php index a9bc557fd9b72..9b8a629d24cad 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php @@ -47,7 +47,7 @@ 'type' => 'field', 'is_require' => true, 'sort_order' => 1, - 'price' => -10.0, + 'price' => 10.0, 'price_type' => 'fixed', 'sku' => 'sku1', 'max_characters' => 10, From e8901d595e4d0369f1745c0914293eff63903bbc Mon Sep 17 00:00:00 2001 From: kreativedev <travis@kreativedev.com> Date: Wed, 25 Jul 2018 15:15:21 -0600 Subject: [PATCH 0567/1171] Fixed a grammatical error on the vault tooltip --- .../Braintree/view/frontend/web/template/payment/paypal.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html index d39ea62012b14..e1f6a1b4c25ce 100644 --- a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html @@ -45,7 +45,7 @@ </span> <div class="field-tooltip-content" data-target="dropdown" - translate="'We store you payment information securely on Braintree servers via SSL.'"></div> + translate="'We store your payment information securely on Braintree servers via SSL.'"></div> </div> </div> <!-- /ko --> From ce5cb9aeb76ca7ae659a241e25be9ba58976c63c Mon Sep 17 00:00:00 2001 From: denistrator <denistrator@yandex.ua> Date: Wed, 25 Jul 2018 12:15:39 +0300 Subject: [PATCH 0568/1171] Disable autocomplete for captcha inputs --- app/code/Magento/Captcha/view/frontend/templates/default.phtml | 2 +- .../Captcha/view/frontend/web/template/checkout/captcha.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Captcha/view/frontend/templates/default.phtml b/app/code/Magento/Captcha/view/frontend/templates/default.phtml index 980a78ff0f7c5..6c9a5fe85f596 100644 --- a/app/code/Magento/Captcha/view/frontend/templates/default.phtml +++ b/app/code/Magento/Captcha/view/frontend/templates/default.phtml @@ -14,7 +14,7 @@ $captcha = $block->getCaptchaModel(); <div class="field captcha required" role="<?= $block->escapeHtmlAttr($block->getFormId()) ?>"> <label for="captcha_<?= $block->escapeHtmlAttr($block->getFormId()) ?>" class="label"><span><?= $block->escapeHtml(__('Please type the letters and numbers below')) ?></span></label> <div class="control captcha"> - <input name="<?= $block->escapeHtmlAttr(\Magento\Captcha\Helper\Data::INPUT_NAME_FIELD_VALUE) ?>[<?= $block->escapeHtmlAttr($block->getFormId()) ?>]" type="text" class="input-text required-entry" data-validate="{required:true}" id="captcha_<?= $block->escapeHtmlAttr($block->getFormId()) ?>" /> + <input name="<?= $block->escapeHtmlAttr(\Magento\Captcha\Helper\Data::INPUT_NAME_FIELD_VALUE) ?>[<?= $block->escapeHtmlAttr($block->getFormId()) ?>]" type="text" class="input-text required-entry" data-validate="{required:true}" id="captcha_<?= $block->escapeHtmlAttr($block->getFormId()) ?>" autocomplete="off"/> <div class="nested"> <div class="field captcha no-label" data-captcha="<?= $block->escapeHtmlAttr($block->getFormId()) ?>" diff --git a/app/code/Magento/Captcha/view/frontend/web/template/checkout/captcha.html b/app/code/Magento/Captcha/view/frontend/web/template/checkout/captcha.html index 97c05249eb463..575b3ca6f732e 100644 --- a/app/code/Magento/Captcha/view/frontend/web/template/checkout/captcha.html +++ b/app/code/Magento/Captcha/view/frontend/web/template/checkout/captcha.html @@ -8,7 +8,7 @@ <div class="field captcha required" data-bind="blockLoader: getIsLoading()"> <label data-bind="attr: {for: 'captcha_' + formId}" class="label"><span data-bind="i18n: 'Please type the letters and numbers below'"></span></label> <div class="control captcha"> - <input name="captcha_string" type="text" class="input-text required-entry" data-bind="value: captchaValue(), attr: {id: 'captcha_' + formId, 'data-scope': dataScope}" /> + <input name="captcha_string" type="text" class="input-text required-entry" data-bind="value: captchaValue(), attr: {id: 'captcha_' + formId, 'data-scope': dataScope}" autocomplete="off"/> <input name="captcha_form_id" type="hidden" data-bind="value: formId, attr: {'data-scope': dataScope}" /> <div class="nested"> <div class="field captcha no-label"> From 46a983533f3eae0d68a85f841495b53bace2de74 Mon Sep 17 00:00:00 2001 From: Angelo Maragna <angelomaragna@users.noreply.github.com> Date: Wed, 25 Jul 2018 14:50:49 +0100 Subject: [PATCH 0569/1171] Update template.js Uniform jquery variable to "$" as this javascript is partially using "jquery" and partially "$". The line 62 will cause an error in Internet Explorer if $ is not declared. --- lib/web/mage/utils/template.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web/mage/utils/template.js b/lib/web/mage/utils/template.js index e29908b7c4e9c..f3b0418f75b00 100644 --- a/lib/web/mage/utils/template.js +++ b/lib/web/mage/utils/template.js @@ -7,7 +7,7 @@ define([ 'underscore', 'mage/utils/objects', 'mage/utils/strings' -], function (jQuery, _, utils, stringUtils) { +], function ($, _, utils, stringUtils) { 'use strict'; var tmplSettings = _.templateSettings, @@ -176,7 +176,7 @@ define([ if (isTemplate(value)) { list[key] = render(value, tmpl, castString); - } else if (jQuery.isPlainObject(value) || Array.isArray(value)) { + } else if ($.isPlainObject(value) || Array.isArray(value)) { _.each(value, iterate); } }); From 223f8a2005719b608e28390eda645d322d95cb39 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Fri, 27 Jul 2018 13:17:26 -0500 Subject: [PATCH 0570/1171] MAGETWO-93329: State/Province field should remain optional for UK customers even after refresh - Skipping this flaky test --- ...dressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml index 4b8d48a84073a..a67984c94949b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml @@ -16,6 +16,8 @@ <severity value="MAJOR"/> <testCaseId value="MAGETWO-93329"/> <group value="checkout"/> + <!-- Skipped because of MC-3443 --> + <group value="skip"/> </annotations> <before> <createData entity="_defaultCategory" stepKey="createCategory"/> From 14b0d18b99ba16128101840ea964b255269e3a78 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Wed, 25 Jul 2018 18:25:35 +0200 Subject: [PATCH 0571/1171] Added unit test for order success observer in GA --- ...ticsOnOrderSuccessPageViewObserverTest.php | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php diff --git a/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php b/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php new file mode 100644 index 0000000000000..d1b1da51e87e9 --- /dev/null +++ b/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GoogleAnalytics\Test\Unit\Block; + +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\Element\AbstractBlock; +use Magento\Framework\View\LayoutInterface; +use Magento\GoogleAnalytics\Helper\Data as GaDataHelper; +use Magento\GoogleAnalytics\Observer\SetGoogleAnalyticsOnOrderSuccessPageViewObserver; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\TestCase; + +class SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest extends TestCase +{ + /** + * @var Event|\PHPUnit_Framework_MockObject_MockObject + */ + private $eventMock; + + /** + * @var Observer|\PHPUnit_Framework_MockObject_MockObject + */ + private $observerMock; + + /** + * @var GaDataHelper|\PHPUnit_Framework_MockObject_MockObject + */ + private $googleAnalyticsDataMock; + + /** + * @var LayoutInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $layoutMock; + + /** + * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeManagerMock; + + /** + * @var SetGoogleAnalyticsOnOrderSuccessPageViewObserver + */ + private $orderSuccessObserver; + + protected function setUp() + { + $this->googleAnalyticsDataMock = $this->getMockBuilder(GaDataHelper::class) + ->disableOriginalConstructor() + ->getMock(); + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->layoutMock = $this->getMockBuilder(LayoutInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->observerMock = $this->getMockBuilder(Observer::class)->getMock(); + $this->eventMock = $this->getMockBuilder(Event::class)->getMock(); + + + $objectManager = new ObjectManager($this); + + $this->orderSuccessObserver = $objectManager->getObject( + SetGoogleAnalyticsOnOrderSuccessPageViewObserver::class, + [ + 'storeManager' => $this->storeManagerMock, + 'layout' => $this->layoutMock, + 'googleAnalyticsData' => $this->googleAnalyticsDataMock + ] + ); + } + + public function testExecuteWithNoOrderIds() + { + $this->observerMock->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + $this->eventMock->expects($this->once()) + ->method('__call') + ->with( + $this->equalTo('getOrderIds') + ) + ->willReturn([]); + $this->layoutMock->expects($this->never()) + ->method('getBlock'); + + $this->orderSuccessObserver->execute($this->observerMock); + } + + public function testExecuteWithOrderIds() + { + $blockMock = $this->getMockBuilder(AbstractBlock::class) + ->disableOriginalConstructor() + ->getMock(); + $orderIds = [8]; + + $this->observerMock->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + $this->eventMock->expects($this->once()) + ->method('__call') + ->with( + $this->equalTo('getOrderIds') + ) + ->willReturn($orderIds); + $this->layoutMock->expects($this->once()) + ->method('getBlock') + ->willReturn($blockMock); + $blockMock->expects($this->once()) + ->method('__call') + ->with( + $this->equalTo('setOrderIds'), + $this->equalTo([$orderIds]) + ); + + $this->orderSuccessObserver->execute($this->observerMock); + } +} From 5f91a9e898cbc516617ee52ef23f2dabcecc6da3 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Wed, 25 Jul 2018 18:28:47 +0200 Subject: [PATCH 0572/1171] Removed extra empty line --- .../SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php b/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php index d1b1da51e87e9..34f71dd53391d 100644 --- a/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php +++ b/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php @@ -63,7 +63,6 @@ protected function setUp() $this->observerMock = $this->getMockBuilder(Observer::class)->getMock(); $this->eventMock = $this->getMockBuilder(Event::class)->getMock(); - $objectManager = new ObjectManager($this); $this->orderSuccessObserver = $objectManager->getObject( From a935e90e96b6c8d1428e8a1dd0367c2e45ab2993 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 24 Jul 2018 12:23:41 +0200 Subject: [PATCH 0573/1171] Added unit test for DB model in backup module --- .../Magento/Backup/Test/Unit/Model/DbTest.php | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 app/code/Magento/Backup/Test/Unit/Model/DbTest.php diff --git a/app/code/Magento/Backup/Test/Unit/Model/DbTest.php b/app/code/Magento/Backup/Test/Unit/Model/DbTest.php new file mode 100644 index 0000000000000..0cab5f0ad1e99 --- /dev/null +++ b/app/code/Magento/Backup/Test/Unit/Model/DbTest.php @@ -0,0 +1,243 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backup\Test\Unit\Model; + +use Magento\Backup\Model\Db; +use Magento\Backup\Model\ResourceModel\Db as DbResource; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Backup\Db\BackupInterface; +use Magento\Framework\DataObject; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +class DbTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Db + */ + private $dbModel; + + /** + * @var DbResource|\PHPUnit_Framework_MockObject_MockObject + */ + private $dbResourceMock; + + /** + * @var ResourceConnection|\PHPUnit_Framework_MockObject_MockObject + */ + private $connectionResourceMock; + + protected function setUp() + { + $this->dbResourceMock = $this->getMockBuilder(DbResource::class) + ->disableOriginalConstructor() + ->getMock(); + $this->connectionResourceMock = $this->getMockBuilder(ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManager = new ObjectManager($this); + $this->dbModel = $this->objectManager->getObject( + Db::class, + [ + 'resourceDb' => $this->dbResourceMock, + 'resource' => $this->connectionResourceMock + ] + ); + } + + public function testGetResource() + { + self::assertEquals($this->dbResourceMock, $this->dbModel->getResource()); + } + + public function testGetTables() + { + $tables = []; + $this->dbResourceMock->expects($this->once()) + ->method('getTables') + ->willReturn($tables); + + self::assertEquals($tables, $this->dbModel->getTables()); + } + + public function testGetTableCreateScript() + { + $tableName = 'some_table'; + $script = 'script'; + $this->dbResourceMock->expects($this->once()) + ->method('getTableCreateScript') + ->with($tableName, false) + ->willReturn($script); + + self::assertEquals($script, $this->dbModel->getTableCreateScript($tableName, false)); + } + + public function testGetTableDataDump() + { + $tableName = 'some_table'; + $dump = 'dump'; + $this->dbResourceMock->expects($this->once()) + ->method('getTableDataDump') + ->with($tableName) + ->willReturn($dump); + + self::assertEquals($dump, $this->dbModel->getTableDataDump($tableName)); + } + + public function testGetHeader() + { + $header = 'header'; + $this->dbResourceMock->expects($this->once()) + ->method('getHeader') + ->willReturn($header); + + self::assertEquals($header, $this->dbModel->getHeader()); + } + + public function testGetFooter() + { + $footer = 'footer'; + $this->dbResourceMock->expects($this->once()) + ->method('getFooter') + ->willReturn($footer); + + self::assertEquals($footer, $this->dbModel->getFooter()); + } + + public function testRenderSql() + { + $header = 'header'; + $script = 'script'; + $tableName = 'some_table'; + $tables = [$tableName, $tableName]; + $dump = 'dump'; + $footer = 'footer'; + + $this->dbResourceMock->expects($this->once()) + ->method('getTables') + ->willReturn($tables); + $this->dbResourceMock->expects($this->once()) + ->method('getHeader') + ->willReturn($header); + $this->dbResourceMock->expects($this->exactly(2)) + ->method('getTableCreateScript') + ->with($tableName, true) + ->willReturn($script); + $this->dbResourceMock->expects($this->exactly(2)) + ->method('getTableDataDump') + ->with($tableName) + ->willReturn($dump); + $this->dbResourceMock->expects($this->once()) + ->method('getFooter') + ->willReturn($footer); + + self::assertEquals( + $header . $script . $dump . $script . $dump . $footer, + $this->dbModel->renderSql() + ); + } + + public function testCreateBackup() + { + /** @var BackupInterface|\PHPUnit_Framework_MockObject_MockObject $backupMock */ + $backupMock = $this->getMockBuilder(BackupInterface::class)->getMock(); + /** @var DataObject $tableStatus */ + $tableStatus = new DataObject(); + + $tableName = 'some_table'; + $tables = [$tableName]; + $header = 'header'; + $footer = 'footer'; + $dropSql = 'drop_sql'; + $createSql = 'create_sql'; + $beforeSql = 'before_sql'; + $afterSql = 'after_sql'; + $dataSql = 'data_sql'; + $foreignKeysSql = 'foreign_keys'; + $triggersSql = 'triggers_sql'; + $rowsCount = 2; + $dataLength = 1; + + $this->dbResourceMock->expects($this->once()) + ->method('beginTransaction'); + $this->dbResourceMock->expects($this->once()) + ->method('commitTransaction'); + $this->dbResourceMock->expects($this->once()) + ->method('getTables') + ->willReturn($tables); + $this->dbResourceMock->expects($this->once()) + ->method('getTableDropSql') + ->willReturn($dropSql); + $this->dbResourceMock->expects($this->once()) + ->method('getTableCreateSql') + ->with($tableName, false) + ->willReturn($createSql); + $this->dbResourceMock->expects($this->once()) + ->method('getTableDataBeforeSql') + ->with($tableName) + ->willReturn($beforeSql); + $this->dbResourceMock->expects($this->once()) + ->method('getTableDataAfterSql') + ->with($tableName) + ->willReturn($afterSql); + $this->dbResourceMock->expects($this->once()) + ->method('getTableDataSql') + ->with($tableName, $rowsCount, 0) + ->willReturn($dataSql); + $this->dbResourceMock->expects($this->once()) + ->method('getTableStatus') + ->with($tableName) + ->willReturn($tableStatus); + $this->dbResourceMock->expects($this->once()) + ->method('getTables') + ->willReturn($createSql); + $this->dbResourceMock->expects($this->once()) + ->method('getHeader') + ->willReturn($header); + $this->dbResourceMock->expects($this->once()) + ->method('getTableHeader') + ->willReturn($header); + $this->dbResourceMock->expects($this->once()) + ->method('getFooter') + ->willReturn($footer); + $this->dbResourceMock->expects($this->once()) + ->method('getTableForeignKeysSql') + ->willReturn($foreignKeysSql); + $this->dbResourceMock->expects($this->once()) + ->method('getTableTriggersSql') + ->willReturn($triggersSql); + $backupMock->expects($this->once()) + ->method('open'); + $backupMock->expects($this->once()) + ->method('close'); + + $tableStatus->setRows($rowsCount); + $tableStatus->setDataLength($dataLength); + + $backupMock->expects($this->any()) + ->method('write') + ->withConsecutive( + [$this->equalTo($header)], + [$this->equalTo($header . $dropSql . "\n")], + [$this->equalTo($createSql . "\n")], + [$this->equalTo($beforeSql)], + [$this->equalTo($dataSql)], + [$this->equalTo($afterSql)], + [$this->equalTo($foreignKeysSql)], + [$this->equalTo($triggersSql)], + [$this->equalTo($footer)] + ); + + $this->dbModel->createBackup($backupMock); + } +} From ed72abe3ceaf67cb8214c9390a85d23ce36ef206 Mon Sep 17 00:00:00 2001 From: Valerij Ivashchenko <likemusic@yandex.ru> Date: Mon, 23 Jul 2018 16:15:35 +0300 Subject: [PATCH 0574/1171] Remove spaces around amount span. --- .../view/base/templates/product/price/amount/default.phtml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/view/base/templates/product/price/amount/default.phtml b/app/code/Magento/Catalog/view/base/templates/product/price/amount/default.phtml index 86168c742c0f1..ce1561e382eed 100644 --- a/app/code/Magento/Catalog/view/base/templates/product/price/amount/default.phtml +++ b/app/code/Magento/Catalog/view/base/templates/product/price/amount/default.phtml @@ -19,9 +19,8 @@ <?= ($block->getPriceDisplayLabel()) ? 'data-label="' . $block->getPriceDisplayLabel() . $block->getPriceDisplayInclExclTaxes() . '"' : '' ?> data-price-amount="<?= /* @escapeNotVerified */ $block->getDisplayValue() ?>" data-price-type="<?= /* @escapeNotVerified */ $block->getPriceType() ?>" - class="price-wrapper <?= /* @escapeNotVerified */ $block->getPriceWrapperCss() ?>"> - <?= /* @escapeNotVerified */ $block->formatCurrency($block->getDisplayValue(), (bool)$block->getIncludeContainer()) ?> - </span> + class="price-wrapper <?= /* @escapeNotVerified */ $block->getPriceWrapperCss() ?>" + ><?= /* @escapeNotVerified */ $block->formatCurrency($block->getDisplayValue(), (bool)$block->getIncludeContainer()) ?></span> <?php if ($block->hasAdjustmentsHtml()): ?> <?= $block->getAdjustmentsHtml() ?> <?php endif; ?> From aab407678e2381ea1a333b24214d10a74d17de34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Corr=C3=AAa=20Gomes?= <rafaelcg_stz@hotmail.com> Date: Fri, 20 Jul 2018 23:55:49 -0300 Subject: [PATCH 0575/1171] Categories > Left menu > Item title space fix --- .../Magento/luma/Magento_Customer/web/css/source/_module.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less old mode 100644 new mode 100755 index c6a240fd55ea2..d7ae6c3b28f4a --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -224,7 +224,7 @@ .column.main & { } } - + display: block; margin-bottom: @indent__s; } From 91af943405b0f808ed4b223cbe36422e95d2703c Mon Sep 17 00:00:00 2001 From: Valerij Ivashchenko <likemusic@yandex.ru> Date: Fri, 20 Jul 2018 20:01:29 +0300 Subject: [PATCH 0576/1171] fix misprint ('_requesetd' > '_requested') --- .../Magento/Ui/view/base/web/js/lib/core/element/element.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/core/element/element.js b/app/code/Magento/Ui/view/base/web/js/lib/core/element/element.js index 1684f0b220175..b05fed939b7a5 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/core/element/element.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/core/element/element.js @@ -66,7 +66,7 @@ define([ Element = _.extend({ defaults: { - _requesetd: {}, + _requested: {}, containers: [], exports: {}, imports: {}, @@ -249,7 +249,7 @@ define([ * @returns {Function} Async module wrapper. */ requestModule: function (name) { - var requested = this._requesetd; + var requested = this._requested; if (!requested[name]) { requested[name] = registry.async(name); From 647f2fa5c3e55d84d494fe7ffc7442c04cdae3c1 Mon Sep 17 00:00:00 2001 From: tejash <tejas@wagento.com> Date: Fri, 20 Jul 2018 14:30:31 +0530 Subject: [PATCH 0577/1171] Mobile device style groups incorrect order --- lib/web/css/source/lib/_responsive.less | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/web/css/source/lib/_responsive.less b/lib/web/css/source/lib/_responsive.less index d1bbbae7f6875..efb706f85eac0 100644 --- a/lib/web/css/source/lib/_responsive.less +++ b/lib/web/css/source/lib/_responsive.less @@ -27,24 +27,24 @@ & when (@media-target = 'mobile'), (@media-target = 'all') { - @media only screen and (max-width: (@screen__xxs - 1)) { - .media-width('max', @screen__xxs); + @media only screen and (max-width: @screen__m) { + .media-width('max', (@screen__m + 1)); } - @media only screen and (max-width: (@screen__xs - 1)) { - .media-width('max', @screen__xs); + @media only screen and (max-width: (@screen__m - 1)) { + .media-width('max', @screen__m); } @media only screen and (max-width: (@screen__s - 1)) { .media-width('max', @screen__s); } - @media only screen and (max-width: (@screen__m - 1)) { - .media-width('max', @screen__m); + @media only screen and (max-width: (@screen__xs - 1)) { + .media-width('max', @screen__xs); } - @media only screen and (max-width: @screen__m) { - .media-width('max', (@screen__m + 1)); + @media only screen and (max-width: (@screen__xxs - 1)) { + .media-width('max', @screen__xxs); } @media all and (min-width: @screen__s) { From 369b7bc5ad23f4f6eeda14cc24cd94f812aecf49 Mon Sep 17 00:00:00 2001 From: Ben Robie <brobie@degdigital.com> Date: Thu, 19 Jul 2018 21:53:08 -0500 Subject: [PATCH 0578/1171] Issue 8131 - Change controller to use result redirect factory which fixes error message display on advanced search form. https://github.com/magento/magento2/issues/8131 --- .../Controller/Advanced/Result.php | 7 +- .../Unit/Controller/Advanced/ResultTest.php | 89 +++++++++++++++++++ 2 files changed, 93 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php b/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php index 86195aa31f0e4..d6a30f4f3141d 100644 --- a/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php +++ b/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php @@ -6,7 +6,6 @@ */ namespace Magento\CatalogSearch\Controller\Advanced; -use Magento\Catalog\Model\Layer\Resolver; use Magento\CatalogSearch\Model\Advanced as ModelAdvanced; use Magento\Framework\App\Action\Context; use Magento\Framework\UrlFactory; @@ -45,7 +44,7 @@ public function __construct( } /** - * @return void + * @return \Magento\Framework\Controller\Result\Redirect */ public function execute() { @@ -58,7 +57,9 @@ public function execute() $defaultUrl = $this->_urlFactory->create() ->addQueryParams($this->getRequest()->getQueryValue()) ->getUrl('*/*/'); - $this->getResponse()->setRedirect($this->_redirect->error($defaultUrl)); + $resultRedirect = $this->resultRedirectFactory->create(); + $resultRedirect->setUrl($this->_redirect->error($defaultUrl)); + return $resultRedirect; } } } diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php index 9aa97f8e6b52a..83578ef4d16b2 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php @@ -5,6 +5,9 @@ */ namespace Magento\CatalogSearch\Test\Unit\Controller\Advanced; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class ResultTest extends \PHPUnit\Framework\TestCase { public function testResultActionFiltersSetBeforeLoadLayout() @@ -49,4 +52,90 @@ function ($added) use (&$filters) { ); $instance->execute(); } + + public function testUrlSetOnException() + { + $redirectResultMock = $this->createMock(\Magento\Framework\Controller\Result\Redirect::class); + $redirectResultMock->expects($this->once()) + ->method('setUrl'); + + $resultRedirectFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\RedirectFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + + $resultRedirectFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($redirectResultMock); + + $catalogSearchAdvanced = $this->createPartialMock( + \Magento\CatalogSearch\Model\Advanced::class, + ['addFilters'] + ); + + $catalogSearchAdvanced->expects($this->once())->method('addFilters')->will( + $this->throwException(new \Magento\Framework\Exception\LocalizedException( + new \Magento\Framework\Phrase("Test Exception") + )) + ); + + $responseMock = $this->createMock(\Magento\Framework\Webapi\Response::class); + $requestMock = $this->createPartialMock( + \Magento\Framework\App\Request\Http::class, + ['getQueryValue'] + ); + $requestMock->expects($this->any())->method('getQueryValue')->willReturn(['key' => 'value']); + + $redirectMock = $this->createMock(\Magento\Framework\App\Response\RedirectInterface::class); + $redirectMock->expects($this->any())->method('error')->with('urlstring'); + + $messageManagerMock = $this->createMock(\Magento\Framework\Message\Manager::class); + + $eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); + + $contextMock = $this->createMock(\Magento\Framework\App\Action\Context::class); + $contextMock->expects($this->any()) + ->method('getRequest') + ->willReturn($requestMock); + $contextMock->expects($this->any()) + ->method('getResponse') + ->willReturn($responseMock); + $contextMock->expects($this->any()) + ->method('getRedirect') + ->willReturn($redirectMock); + $contextMock->expects($this->any()) + ->method('getMessageManager') + ->willReturn($messageManagerMock); + $contextMock->expects($this->any()) + ->method('getEventManager') + ->willReturn($eventManagerMock); + $contextMock->expects($this->any()) + ->method('getResultRedirectFactory') + ->willReturn($resultRedirectFactoryMock); + + $urlMock = $this->createMock(\Magento\Framework\Url::class); + $urlMock->expects($this->once()) + ->method('addQueryParams') + ->willReturnSelf(); + $urlMock->expects($this->once()) + ->method('getUrl') + ->willReturn("urlstring"); + + $urlFactoryMock = $this->createMock(\Magento\Framework\UrlFactory::class); + $urlFactoryMock->expects($this->once()) + ->method('create') + ->will($this->returnValue($urlMock)); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + /** @var \Magento\CatalogSearch\Controller\Advanced\Result $instance */ + $instance = $objectManager->getObject( + \Magento\CatalogSearch\Controller\Advanced\Result::class, + ['context' => $contextMock, + 'catalogSearchAdvanced' => $catalogSearchAdvanced, + 'urlFactory' => $urlFactoryMock + ] + ); + $instance->execute(); + } } From 20a712431f597e90a2e4c8fd38dde9e1d68218b1 Mon Sep 17 00:00:00 2001 From: Ben Robie <brobie@degdigital.com> Date: Thu, 19 Jul 2018 23:06:45 -0500 Subject: [PATCH 0579/1171] Issue 8131 - Shortening variable name to meet standards. https://github.com/magento/magento2/issues/8131 --- .../Test/Unit/Controller/Advanced/ResultTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php index 83578ef4d16b2..7e8344ee4fa09 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php @@ -59,12 +59,12 @@ public function testUrlSetOnException() $redirectResultMock->expects($this->once()) ->method('setUrl'); - $resultRedirectFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\RedirectFactory::class) + $redirectFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\RedirectFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); - $resultRedirectFactoryMock->expects($this->any()) + $redirectFactoryMock->expects($this->any()) ->method('create') ->willReturn($redirectResultMock); @@ -111,7 +111,7 @@ public function testUrlSetOnException() ->willReturn($eventManagerMock); $contextMock->expects($this->any()) ->method('getResultRedirectFactory') - ->willReturn($resultRedirectFactoryMock); + ->willReturn($redirectFactoryMock); $urlMock = $this->createMock(\Magento\Framework\Url::class); $urlMock->expects($this->once()) From a39c8e4d054c81f9eccd0ad864d1ea07c64169b6 Mon Sep 17 00:00:00 2001 From: Ben Robie <brobie@degdigital.com> Date: Fri, 20 Jul 2018 07:34:56 -0500 Subject: [PATCH 0580/1171] Issue 8131 - Asserting that a redirect result is returned from the execute. --- .../CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php index 7e8344ee4fa09..891f008979e17 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php @@ -136,6 +136,6 @@ public function testUrlSetOnException() 'urlFactory' => $urlFactoryMock ] ); - $instance->execute(); + $this->assertEquals($redirectResultMock, $instance->execute()); } } From ab47b7ffa14327bd09f6153d0a0487be41b3604b Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Fri, 27 Jul 2018 13:44:14 -0500 Subject: [PATCH 0581/1171] MAGETWO-93329: State/Province field should remain optional for UK customers even after refresh - Update skipped issue id --- ...dressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml index a67984c94949b..2ce3974e8e92a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml @@ -16,7 +16,7 @@ <severity value="MAJOR"/> <testCaseId value="MAGETWO-93329"/> <group value="checkout"/> - <!-- Skipped because of MC-3443 --> + <!-- Skipped because of MAGETWO-93726 --> <group value="skip"/> </annotations> <before> From 6791460be26cb37ac77445add1f37f859dc57b85 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Sat, 21 Jul 2018 20:07:20 +0530 Subject: [PATCH 0582/1171] Correct return type of methods --- app/code/Magento/Catalog/Controller/Category/View.php | 2 +- .../CatalogSearch/Model/ResourceModel/EngineInterface.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Category/View.php b/app/code/Magento/Catalog/Controller/Category/View.php index a72f764a5f1f6..226e572505076 100644 --- a/app/code/Magento/Catalog/Controller/Category/View.php +++ b/app/code/Magento/Catalog/Controller/Category/View.php @@ -111,7 +111,7 @@ public function __construct( /** * Initialize requested category object * - * @return \Magento\Catalog\Model\Category + * @return \Magento\Catalog\Model\Category|bool */ protected function _initCategory() { diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/EngineInterface.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/EngineInterface.php index 399f8f763d945..0182b09bcacff 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/EngineInterface.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/EngineInterface.php @@ -61,7 +61,7 @@ public function processAttributeValue($attribute, $value); * * @param array $index * @param string $separator - * @return string + * @return array */ public function prepareEntityIndex($index, $separator = ' '); } From 334341bbb27961102abf057f09cccca05176d9cd Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Sun, 22 Jul 2018 10:33:36 +0530 Subject: [PATCH 0583/1171] Correct return type of methods --- .../Model/Indexer/Fulltext/Action/DataProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php index 5ad2635576857..a8d46911193a8 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php @@ -538,7 +538,7 @@ private function getProductEmulator($typeId) * @param array $indexData * @param array $productData * @param int $storeId - * @return string + * @return array * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @since 100.0.3 */ From 515f27c918639ab3cd1428000eb7f24bc44de6fc Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 27 Jul 2018 17:25:51 -0500 Subject: [PATCH 0584/1171] MAGETWO-90531: WYSIWYG shows special character button in toolbar on product page - when test module is enabled - modifying integration test to customize toolbar --- .../TestModuleWysiwygConfig/Model/Config.php | 49 ++++++++++++++++++- .../etc/adminhtml/di.xml | 16 ++++++ .../Magento/Cms/Model/Wysiwyg/ConfigTest.php | 9 +++- 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/Model/Config.php b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/Model/Config.php index 7726782c49f08..b80caeed6beed 100644 --- a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/Model/Config.php +++ b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/Model/Config.php @@ -19,17 +19,64 @@ class Config implements \Magento\Framework\Data\Wysiwyg\ConfigProviderInterface */ const CONFIG_CONTENT_CSS = 'something_else.css'; + /** @var \Magento\Cms\Model\Wysiwyg\DefaultConfigProvider */ + private $cmsConfigProvider; + + /** + * @param \Magento\Cms\Model\Wysiwyg\DefaultConfigProvider $cmsConfigProvider + */ + public function __construct(\Magento\Cms\Model\Wysiwyg\DefaultConfigProvider $cmsConfigProvider) + { + $this->cmsConfigProvider = $cmsConfigProvider; + } + /** * @inheritdoc */ public function getConfig(\Magento\Framework\DataObject $config): \Magento\Framework\DataObject { - $config->addData( + //get default config + $config = $this->cmsConfigProvider->getConfig($config); + + $config = $this->removeSpecialCharacterFromToolbar($config); + + $config = $this->modifyHeightAndContentCss($config); + return $config; + } + + /** + * Modify height and content_css in the config + * + * @param \Magento\Framework\DataObject $config + * @return \Magento\Framework\DataObject + */ + private function modifyHeightAndContentCss(\Magento\Framework\DataObject $config) : \Magento\Framework\DataObject + { + return $config->addData( [ 'height' => self::CONFIG_HEIGHT, 'content_css' => self::CONFIG_CONTENT_CSS ] ); + } + + /** + * Modify height and content_css in the config + * + * @param \Magento\Framework\DataObject $config + * @return \Magento\Framework\DataObject + */ + private function removeSpecialCharacterFromToolbar( + \Magento\Framework\DataObject $config + ) : \Magento\Framework\DataObject { + $tinymce4 = $config->getData('tinymce4'); + if (isset($tinymce4['toolbar']) && isset($tinymce4['plugins'])) { + $toolbar = $tinymce4['toolbar']; + $plugins = $tinymce4['plugins']; + $tinymce4['toolbar'] = str_replace('charmap', '', $toolbar); + $tinymce4['plugins'] = str_replace('charmap', '', $plugins); + $config->setData('tinymce4', $tinymce4); + } return $config; } } diff --git a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml index 3366e5fc9685e..1001b40d3bbd4 100644 --- a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml +++ b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml @@ -11,6 +11,12 @@ <argument name="wysiwygConfigPostProcessor" xsi:type="array"> <item name="testAdapter" xsi:type="string">Magento\TestModuleWysiwygConfig\Model\Config</item> </argument> + <argument name="wysiwygConfigPostProcessor" xsi:type="array"> + <item name="mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter" xsi:type="string">Magento\TestModuleWysiwygConfig\Model\Config</item> + </argument> + <argument name="wysiwygConfigPostProcessor" xsi:type="array"> + <item name="default" xsi:type="string">Magento\TestModuleWysiwygConfig\Model\Config</item> + </argument> <argument name="variablePluginConfigProvider" xsi:type="array"> <item name="testAdapter" xsi:type="string">Magento\Variable\Model\Variable\ConfigProvider</item> </argument> @@ -22,4 +28,14 @@ </argument> </arguments> </type> + <type name="Magento\Cms\Model\Config\Source\Wysiwyg\Editor"> + <arguments> + <argument name="adapterOptions" xsi:type="array"> + <item name="testAdapter" xsi:type="array"> + <item name="value" xsi:type="string">mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter</item> + <item name="label" xsi:type="string" translatable="true">Test Adapter</item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php index b79ccd255f26a..df33afa214bc3 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php @@ -57,7 +57,7 @@ public function testGetConfigCssUrls() * * @magentoConfigFixture default/cms/wysiwyg/editor testAdapter */ - public function testEnabledModuleIsAbleToModifyConfig() + public function testTestModuleEnabledModuleIsAbleToModifyConfig() { $objectManager = Bootstrap::getObjectManager(); $compositeConfigProvider = $objectManager->create(\Magento\Cms\Model\Wysiwyg\CompositeConfigProvider::class); @@ -68,5 +68,12 @@ public function testEnabledModuleIsAbleToModifyConfig() $config = $model->getConfig(); $this->assertEquals(TestModuleWysiwygConfig::CONFIG_HEIGHT, $config['height']); $this->assertEquals(TestModuleWysiwygConfig::CONFIG_CONTENT_CSS, $config['content_css']); + $this->assertArrayHasKey('tinymce4', $config); + $this->assertArrayHasKey('toolbar', $config['tinymce4']); + $this->assertNotContains( + 'charmap', + $config['tinymce4']['toolbar'], + 'Failed to address that the custom test module removes "charmap" button from the toolbar' + ); } } From 3623e5cbfff74b1ed9dd48c2c383adf87656eb0e Mon Sep 17 00:00:00 2001 From: Kevin Harper <keharper@adobe.com> Date: Fri, 27 Jul 2018 22:10:39 -0500 Subject: [PATCH 0585/1171] Corrected field descriptions --- app/code/Magento/CustomerGraphQl/etc/schema.graphqls | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index 687826cff001d..91b7ef1f9be15 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -19,9 +19,9 @@ type Customer @doc(description: "Customer defines the customer name and address dob: String @doc(description: "The customer's date of birth") taxvat: String @doc(description: "The customer's Tax/VAT number (for corporate customers)") id: Int @doc(description: "The ID assigned to the customer") - is_subscribed: Boolean @doc(description: "An array containing the customer's shipping and billing addresses") - addresses: [CustomerAddress] @doc(description: "Indicates whether the customer is subscribed to the company's newsletter") -} + is_subscribed: Boolean @doc(description: "Indicates whether the customer is subscribed to the company's newsletter") + addresses: [CustomerAddress] @doc(description: "An array containing the customer's shipping and billing addresses") +} type CustomerAddress @doc(description: "CustomerAddress contains detailed information about a customer's billing and shipping addresses"){ id: Int @doc(description: "The ID assigned to the address object") From 7be9f2efbf79c58b4111667da877c31cc0b79697 Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Sat, 28 Jul 2018 15:49:06 +0200 Subject: [PATCH 0586/1171] Fixed some minor css issue **lib/internal/Magento/Framework/View/Test/Unit/Url/_files/sourceImport.css:** + `@import` statements should be at the top **lib/web/css/docs/docs.css:** + Use `green` instead of `#green` --- .../Framework/View/Test/Unit/Url/_files/sourceImport.css | 4 ++-- lib/web/css/docs/docs.css | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Url/_files/sourceImport.css b/lib/internal/Magento/Framework/View/Test/Unit/Url/_files/sourceImport.css index d7ce9e81258db..420083613705f 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Url/_files/sourceImport.css +++ b/lib/internal/Magento/Framework/View/Test/Unit/Url/_files/sourceImport.css @@ -2,8 +2,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -body {background: url(body.gif);} @import url(../recursive.css); -p {background: url(1.gif?param);} @import url("deep/recursive.css"); +body {background: url(body.gif);} +p {background: url(1.gif?param);} h1 {background: url('../h1.gif#param');} h2 {background: url(../images/h2.gif?test);} diff --git a/lib/web/css/docs/docs.css b/lib/web/css/docs/docs.css index beb18b8b2194a..a3a372ad4eeb9 100644 --- a/lib/web/css/docs/docs.css +++ b/lib/web/css/docs/docs.css @@ -4187,7 +4187,7 @@ select { color: #000066; } .example-message-4:before { - background: #green; + background: green; width: 30px; content: ''; display: block; @@ -4230,7 +4230,7 @@ select { border: 5px solid transparent; height: 0; width: 0; - border-left-color: #green; + border-left-color: green; left: 30px; } .example-message-4 > *:first-child:after { From b18483701190eb55359958fbbd39c6a92ffb4f9f Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Tue, 24 Jul 2018 21:28:49 +0530 Subject: [PATCH 0587/1171] Remove commented code --- .../Review/Model/ResourceModel/Rating/Collection.php | 1 - app/code/Magento/Sales/Model/Order/Creditmemo.php | 7 ------- .../Magento/Framework/Data/Form/Element/Checkboxes.php | 8 -------- .../Model/ResourceModel/Db/Relation/ActionPool.php | 3 --- 4 files changed, 19 deletions(-) diff --git a/app/code/Magento/Review/Model/ResourceModel/Rating/Collection.php b/app/code/Magento/Review/Model/ResourceModel/Rating/Collection.php index cbbe17a47c0ad..0dcb9da6a8c75 100644 --- a/app/code/Magento/Review/Model/ResourceModel/Rating/Collection.php +++ b/app/code/Magento/Review/Model/ResourceModel/Rating/Collection.php @@ -141,7 +141,6 @@ public function setStoreFilter($storeId) 'main_table.rating_id = store.rating_id', [] ); - // ->group('main_table.rating_id') $this->_isStoreJoined = true; } $inCondition = $connection->prepareSqlCondition('store.store_id', ['in' => $storeId]); diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo.php b/app/code/Magento/Sales/Model/Order/Creditmemo.php index 739228d839fea..7ea7e4af3c36e 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo.php @@ -469,13 +469,6 @@ public function getStateName($stateId = null) */ public function setShippingAmount($amount) { - // base shipping amount calculated in total model - // $amount = $this->getStore()->round($amount); - // $this->setData('base_shipping_amount', $amount); - // - // $amount = $this->getStore()->round( - // $amount*$this->getOrder()->getStoreToOrderRate() - // ); return $this->setData(CreditmemoInterface::SHIPPING_AMOUNT, $amount); } diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php b/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php index 76bc4fce5f95c..3048be4d5dc1b 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php @@ -178,14 +178,6 @@ public function getOnchange($value) return; } - // public function getName($value) - // { - // if ($name = $this->getData('name')) { - // return str_replace('$value', $value, $name); - // } - // return ; - // } - /** * @param array $option * @return string diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Relation/ActionPool.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Relation/ActionPool.php index c5096acb761df..f3ac5ed5ee1f8 100644 --- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Relation/ActionPool.php +++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Relation/ActionPool.php @@ -49,9 +49,6 @@ public function getActions($entityType, $actionName) } foreach ($this->relationActions[$entityType][$actionName] as $actionClassName) { $action = $this->objectManager->get($actionClassName); - //if (!$action instanceof ProcessEntityRelationInterface) { - // throw new \Exception('Not compliant with action interface'); - //} $actions[] = $action; } return $actions; From 03b8e6de00fbab084a2d9cfc417a09469fd9884a Mon Sep 17 00:00:00 2001 From: Sven Reichel <github-sr@hotmail.com> Date: Sat, 28 Jul 2018 19:42:55 +0200 Subject: [PATCH 0588/1171] Removed unreachable code --- .../Magento/CatalogSearch/Block/Advanced/Form.php | 9 --------- .../Magento/MediaStorage/Model/File/Storage/File.php | 11 ++++------- .../Sales/Model/Service/CreditmemoService.php | 12 ------------ .../Shipping/Block/Adminhtml/Order/Tracking/View.php | 7 +------ .../Controller/Adminhtml/Widget/Instance/Save.php | 2 -- 5 files changed, 5 insertions(+), 36 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Block/Advanced/Form.php b/app/code/Magento/CatalogSearch/Block/Advanced/Form.php index 68e4233e8deaf..b1d0a6431dcdf 100644 --- a/app/code/Magento/CatalogSearch/Block/Advanced/Form.php +++ b/app/code/Magento/CatalogSearch/Block/Advanced/Form.php @@ -181,15 +181,6 @@ public function getCurrencyCount() public function getCurrency($attribute) { return $this->_storeManager->getStore()->getCurrentCurrencyCode(); - - $baseCurrency = $this->_storeManager->getStore()->getBaseCurrency()->getCurrencyCode(); - return $this->getAttributeValue( - $attribute, - 'currency' - ) ? $this->getAttributeValue( - $attribute, - 'currency' - ) : $baseCurrency; } /** diff --git a/app/code/Magento/MediaStorage/Model/File/Storage/File.php b/app/code/Magento/MediaStorage/Model/File/Storage/File.php index 149d829e2acbe..ffbc2e3e90aad 100644 --- a/app/code/Magento/MediaStorage/Model/File/Storage/File.php +++ b/app/code/Magento/MediaStorage/Model/File/Storage/File.php @@ -286,11 +286,10 @@ public function saveDir($dir) */ public function saveFile($file, $overwrite = true) { - if (isset( - $file['filename'] - ) && !empty($file['filename']) && isset( - $file['content'] - ) && !empty($file['content']) + if (isset($file['filename']) + && !empty($file['filename']) + && isset($file['content']) + && !empty($file['content']) ) { try { $filename = isset( @@ -307,8 +306,6 @@ public function saveFile($file, $overwrite = true) } else { throw new \Magento\Framework\Exception\LocalizedException(__('Wrong file info format')); } - - return false; } /** diff --git a/app/code/Magento/Sales/Model/Service/CreditmemoService.php b/app/code/Magento/Sales/Model/Service/CreditmemoService.php index 24f56c0dbd595..aa872a809803a 100644 --- a/app/code/Magento/Sales/Model/Service/CreditmemoService.php +++ b/app/code/Magento/Sales/Model/Service/CreditmemoService.php @@ -104,18 +104,6 @@ public function __construct( public function cancel($id) { throw new \Magento\Framework\Exception\LocalizedException(__('You can not cancel Credit Memo')); - try { - $creditmemo = $this->creditmemoRepository->get($id); - $creditmemo->setState(\Magento\Sales\Model\Order\Creditmemo::STATE_CANCELED); - foreach ($creditmemo->getAllItems() as $item) { - $item->cancel(); - } - $this->eventManager->dispatch('sales_order_creditmemo_cancel', ['creditmemo' => $creditmemo]); - $this->creditmemoRepository->save($creditmemo); - } catch (\Exception $e) { - throw new \Magento\Framework\Exception\LocalizedException(__('Could not cancel creditmemo'), $e); - } - return true; } /** diff --git a/app/code/Magento/Shipping/Block/Adminhtml/Order/Tracking/View.php b/app/code/Magento/Shipping/Block/Adminhtml/Order/Tracking/View.php index 00fc6fce1bfb8..356483c9a5dd7 100644 --- a/app/code/Magento/Shipping/Block/Adminhtml/Order/Tracking/View.php +++ b/app/code/Magento/Shipping/Block/Adminhtml/Order/Tracking/View.php @@ -92,11 +92,6 @@ public function getRemoveUrl($track) public function getCarrierTitle($code) { $carrier = $this->_carrierFactory->create($code); - if ($carrier) { - return $carrier->getConfigData('title'); - } else { - return __('Custom Value'); - } - return false; + return $carrier ? $carrier->getConfigData('title') : __('Custom Value'); } } diff --git a/app/code/Magento/Widget/Controller/Adminhtml/Widget/Instance/Save.php b/app/code/Magento/Widget/Controller/Adminhtml/Widget/Instance/Save.php index 98275c3b906db..b5d31dc10be34 100644 --- a/app/code/Magento/Widget/Controller/Adminhtml/Widget/Instance/Save.php +++ b/app/code/Magento/Widget/Controller/Adminhtml/Widget/Instance/Save.php @@ -49,7 +49,5 @@ public function execute() $this->_redirect('adminhtml/*/edit', ['_current' => true]); return; } - $this->_redirect('adminhtml/*/'); - return; } } From b98415cc251fcd4b2b4b761affd465128fee811b Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Sat, 28 Jul 2018 23:23:12 +0300 Subject: [PATCH 0589/1171] Maintenance. - add missed property; - add variable annotation; --- app/code/Magento/Catalog/CustomerData/CompareProducts.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/Catalog/CustomerData/CompareProducts.php b/app/code/Magento/Catalog/CustomerData/CompareProducts.php index 0e688042c615a..afbeab8c9070e 100644 --- a/app/code/Magento/Catalog/CustomerData/CompareProducts.php +++ b/app/code/Magento/Catalog/CustomerData/CompareProducts.php @@ -19,6 +19,11 @@ class CompareProducts implements SectionSourceInterface */ protected $productUrl; + /** + * @var \Magento\Catalog\Helper\Output + */ + private $outputHelper; + /** * @param \Magento\Catalog\Helper\Product\Compare $helper * @param \Magento\Catalog\Model\Product\Url $productUrl @@ -54,6 +59,7 @@ public function getSectionData() protected function getItems() { $items = []; + /** @var \Magento\Catalog\Model\Product $item */ foreach ($this->helper->getItemCollection() as $item) { $items[] = [ 'id' => $item->getId(), From 7ee78965b0dc2c979aac16cf0262a57fda7824a7 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Sat, 28 Jul 2018 23:24:53 +0300 Subject: [PATCH 0590/1171] Maintenance. - add unit test coverage. --- .../Unit/CustomerData/CompareProductsTest.php | 286 ++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Unit/CustomerData/CompareProductsTest.php diff --git a/app/code/Magento/Catalog/Test/Unit/CustomerData/CompareProductsTest.php b/app/code/Magento/Catalog/Test/Unit/CustomerData/CompareProductsTest.php new file mode 100644 index 0000000000000..e30ddda0b70b9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/CustomerData/CompareProductsTest.php @@ -0,0 +1,286 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\CustomerData; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\CustomerData\CompareProducts; +use Magento\Catalog\Helper\Output; +use Magento\Catalog\Helper\Product\Compare; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Url; +use Magento\Catalog\Model\ResourceModel\Product\Compare\Item\Collection; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class CompareProductsTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var CompareProducts + */ + private $model; + + /** + * @var Compare|\PHPUnit_Framework_MockObject_MockObject + */ + private $helperMock; + + /** + * @var Url|\PHPUnit_Framework_MockObject_MockObject + */ + private $productUrlMock; + + /** + * @var Output|\PHPUnit_Framework_MockObject_MockObject + */ + private $outputHelperMock; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + private $objectManagerHelper; + + /** + * @var array + */ + private $productValueMap = [ + 'id' => 'getId', + ProductInterface::NAME => 'getName' + ]; + + protected function setUp() + { + parent::setUp(); + + $this->helperMock = $this->getMockBuilder(Compare::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productUrlMock = $this->getMockBuilder(Url::class) + ->disableOriginalConstructor() + ->getMock(); + $this->outputHelperMock = $this->getMockBuilder(Output::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->model = $this->objectManagerHelper->getObject( + CompareProducts::class, + [ + 'helper' => $this->helperMock, + 'productUrl' => $this->productUrlMock, + 'outputHelper' => $this->outputHelperMock + ] + ); + } + + /** + * Prepare compare items collection. + * + * @param array $items + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getItemCollectionMock(array $items) : \PHPUnit_Framework_MockObject_MockObject + { + $itemCollectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + + $itemCollectionMock->expects($this->any()) + ->method('getIterator') + ->willReturn(new \ArrayIterator($items)); + + return $itemCollectionMock; + } + + /** + * Prepare product mocks objects and add corresponding method mocks for helpers. + * + * @param array $dataSet + * @return array + */ + private function prepareProductsWithCorrespondingMocks(array $dataSet) : array + { + $items = []; + $urlMap = []; + $outputMap = []; + $helperMap = []; + + $count = count($dataSet); + + foreach ($dataSet as $data) { + $item = $this->getProductMock($data); + $items[] = $item; + + $outputMap[] = [$item, $data['name'], 'name', 'productName#' . $data['id']]; + $helperMap[] = [$item, 'http://remove.url/' . $data['id']]; + $urlMap[] = [$item, [], 'http://product.url/' . $data['id']]; + } + + $this->productUrlMock->expects($this->exactly($count)) + ->method('getUrl') + ->will($this->returnValueMap($urlMap)); + + $this->outputHelperMock->expects($this->exactly($count)) + ->method('productAttribute') + ->will($this->returnValueMap($outputMap)); + + $this->helperMock->expects($this->exactly($count)) + ->method('getPostDataRemove') + ->will($this->returnValueMap($helperMap)); + + return $items; + } + + /** + * Prepare mock of product object. + * + * @param array $data + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getProductMock(array $data) : \PHPUnit_Framework_MockObject_MockObject + { + $product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + + foreach ($data as $index => $value) { + $product->expects($this->once()) + ->method($this->productValueMap[$index]) + ->willReturn($value); + } + + return $product; + } + + public function testGetSectionData() + { + $dataSet = [ + ['id' => 1, 'name' => 'product#1'], + ['id' => 2, 'name' => 'product#2'], + ['id' => 3, 'name' => 'product#3'] + ]; + + $count = count($dataSet); + + $this->helperMock->expects($this->once()) + ->method('getItemCount') + ->willReturn($count); + + $items = $this->prepareProductsWithCorrespondingMocks($dataSet); + + $itemCollectionMock = $this->getItemCollectionMock($items); + + $this->helperMock->expects($this->once()) + ->method('getItemCollection') + ->willReturn($itemCollectionMock); + + $this->helperMock->expects($this->once()) + ->method('getListUrl') + ->willReturn('http://list.url'); + + $this->assertEquals( + [ + 'count' => $count, + 'countCaption' => __('%1 items', $count), + 'listUrl' => 'http://list.url', + 'items' => [ + [ + 'id' => 1, + 'product_url' => 'http://product.url/1', + 'name' => 'productName#1', + 'remove_url' => 'http://remove.url/1' + ], + [ + 'id' => 2, + 'product_url' => 'http://product.url/2', + 'name' => 'productName#2', + 'remove_url' => 'http://remove.url/2' + ], + [ + 'id' => 3, + 'product_url' => 'http://product.url/3', + 'name' => 'productName#3', + 'remove_url' => 'http://remove.url/3' + ] + ] + ], + $this->model->getSectionData() + ); + } + + public function testGetSectionDataNoItems() + { + $count = 0; + + $this->helperMock->expects($this->once()) + ->method('getItemCount') + ->willReturn($count); + + $this->helperMock->expects($this->never()) + ->method('getItemCollection'); + + $this->helperMock->expects($this->once()) + ->method('getListUrl') + ->willReturn('http://list.url'); + + $this->assertEquals( + [ + 'count' => $count, + 'countCaption' => __('%1 items', $count), + 'listUrl' => 'http://list.url', + 'items' => [] + ], + $this->model->getSectionData() + ); + } + + public function testGetSectionDataSingleItem() + { + $count = 1; + + $this->helperMock->expects($this->once()) + ->method('getItemCount') + ->willReturn($count); + + $items = $this->prepareProductsWithCorrespondingMocks( + [ + [ + 'id' => 12345, + 'name' => 'SingleProduct' + ] + ] + ); + + $itemCollectionMock = $this->getItemCollectionMock($items); + + $this->helperMock->expects($this->once()) + ->method('getItemCollection') + ->willReturn($itemCollectionMock); + + $this->helperMock->expects($this->once()) + ->method('getListUrl') + ->willReturn('http://list.url'); + + $this->assertEquals( + [ + 'count' => 1, + 'countCaption' => __('1 item'), + 'listUrl' => 'http://list.url', + 'items' => [ + [ + 'id' => 12345, + 'product_url' => 'http://product.url/12345', + 'name' => 'productName#12345', + 'remove_url' => 'http://remove.url/12345' + ] + ] + ], + $this->model->getSectionData() + ); + } +} From a5e08b0d09a6b5e64d6adf82a9898639594a1c06 Mon Sep 17 00:00:00 2001 From: Wouter Samaey <wouter.samaey@storefront.be> Date: Wed, 25 Jul 2018 14:34:38 +0200 Subject: [PATCH 0591/1171] Using interface instead of model directly --- app/code/Magento/Theme/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Theme/etc/di.xml b/app/code/Magento/Theme/etc/di.xml index 55119f3449389..5ff82ce2db6f6 100644 --- a/app/code/Magento/Theme/etc/di.xml +++ b/app/code/Magento/Theme/etc/di.xml @@ -69,7 +69,7 @@ </type> <type name="Magento\Framework\App\Area"> <arguments> - <argument name="translator" xsi:type="object">Magento\Framework\Translate</argument> + <argument name="translator" xsi:type="object">Magento\Framework\TranslateInterface</argument> <argument name="design" xsi:type="object">Magento\Theme\Model\Design\Proxy</argument> </arguments> </type> From d1164548da45b9e2bb8ec22140061c1220748c23 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Thu, 21 Jun 2018 22:53:20 +0300 Subject: [PATCH 0592/1171] #16273: Fix bug in method getUrlInStore() of product model # Method $product->getUrlInStore() returning extremely long URLs, could be a bug (cherry picked from commit 7558ac0a478af8fc008228077f9e5380612d4138) --- .../Magento/Store/Url/Plugin/RouteParamsResolver.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php b/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php index c4f8a31430963..325e621c8a113 100644 --- a/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php +++ b/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php @@ -51,6 +51,8 @@ public function __construct( * @param \Magento\Framework\Url\RouteParamsResolver $subject * @param array $data * @param bool $unsetOldParams + * @throws \Magento\Framework\Exception\NoSuchEntityException + * * @return array */ public function beforeSetRouteParams( @@ -63,13 +65,19 @@ public function beforeSetRouteParams( unset($data['_scope']); } if (isset($data['_scope_to_url']) && (bool)$data['_scope_to_url'] === true) { - $storeCode = $subject->getScope() ?: $this->storeManager->getStore()->getCode(); + /** @var Store $currentScope */ + $currentScope = $subject->getScope(); + $storeCode = $currentScope && $currentScope instanceof Store ? + $currentScope->getCode() : + $this->storeManager->getStore()->getCode(); + $useStoreInUrl = $this->scopeConfig->getValue( Store::XML_PATH_STORE_IN_URL, StoreScopeInterface::SCOPE_STORE, $storeCode ); - if (!$useStoreInUrl && !$this->storeManager->hasSingleStore()) { + + if ($useStoreInUrl && !$this->storeManager->hasSingleStore()) { $this->queryParamsResolver->setQueryParam('___store', $storeCode); } } From 3a39a1674bf06d80d7df3969ecc78f84abd218c1 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Sat, 30 Jun 2018 15:04:59 +0300 Subject: [PATCH 0593/1171] Modified RouteParamsResolverTest according to latest bugfixes on tested class --- .../Url/Plugin/RouteParamsResolverTest.php | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php b/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php index ed9ac7ebe438a..51e3a9dc32e13 100644 --- a/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php +++ b/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php @@ -22,6 +22,11 @@ class RouteParamsResolverTest extends \PHPUnit\Framework\TestCase */ protected $queryParamsResolverMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\Store + */ + protected $storeMock; + /** * @var \Magento\Store\Url\Plugin\RouteParamsResolver */ @@ -30,7 +35,19 @@ class RouteParamsResolverTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->scopeConfigMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + + $this->storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) + ->setMethods(['getCode']) + ->disableOriginalConstructor() + ->getMock(); + $this->storeMock->expects($this->any())->method('getCode')->willReturn('custom_store'); + $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->storeManagerMock + ->expects($this->once()) + ->method('getStore') + ->willReturn($this->storeMock); + $this->queryParamsResolverMock = $this->createMock(\Magento\Framework\Url\QueryParamsResolverInterface::class); $this->model = new \Magento\Store\Url\Plugin\RouteParamsResolver( $this->scopeConfigMock, @@ -42,6 +59,8 @@ protected function setUp() public function testBeforeSetRouteParamsScopeInParams() { $storeCode = 'custom_store'; + $data = ['_scope' => $storeCode, '_scope_to_url' => true]; + $this->scopeConfigMock ->expects($this->once()) ->method('getValue') @@ -52,7 +71,7 @@ public function testBeforeSetRouteParamsScopeInParams() ) ->will($this->returnValue(false)); $this->storeManagerMock->expects($this->any())->method('hasSingleStore')->willReturn(false); - $data = ['_scope' => $storeCode, '_scope_to_url' => true]; + /** @var \PHPUnit_Framework_MockObject_MockObject $routeParamsResolverMock */ $routeParamsResolverMock = $this->getMockBuilder(\Magento\Framework\Url\RouteParamsResolver::class) ->setMethods(['setScope', 'getScope']) @@ -61,7 +80,8 @@ public function testBeforeSetRouteParamsScopeInParams() $routeParamsResolverMock->expects($this->once())->method('setScope')->with($storeCode); $routeParamsResolverMock->expects($this->once())->method('getScope')->willReturn($storeCode); - $this->queryParamsResolverMock->expects($this->once())->method('setQueryParam')->with('___store', $storeCode); + $this->queryParamsResolverMock->expects($this->never())->method('setQueryParam'); + $this->model->beforeSetRouteParams( $routeParamsResolverMock, @@ -72,6 +92,8 @@ public function testBeforeSetRouteParamsScopeInParams() public function testBeforeSetRouteParamsScopeUseStoreInUrl() { $storeCode = 'custom_store'; + $data = ['_scope' => $storeCode, '_scope_to_url' => true]; + $this->scopeConfigMock ->expects($this->once()) ->method('getValue') @@ -81,8 +103,9 @@ public function testBeforeSetRouteParamsScopeUseStoreInUrl() $storeCode ) ->will($this->returnValue(true)); + $this->storeManagerMock->expects($this->any())->method('hasSingleStore')->willReturn(false); - $data = ['_scope' => $storeCode, '_scope_to_url' => true]; + /** @var \PHPUnit_Framework_MockObject_MockObject $routeParamsResolverMock */ $routeParamsResolverMock = $this->getMockBuilder(\Magento\Framework\Url\RouteParamsResolver::class) ->setMethods(['setScope', 'getScope']) @@ -91,7 +114,7 @@ public function testBeforeSetRouteParamsScopeUseStoreInUrl() $routeParamsResolverMock->expects($this->once())->method('setScope')->with($storeCode); $routeParamsResolverMock->expects($this->once())->method('getScope')->willReturn($storeCode); - $this->queryParamsResolverMock->expects($this->never())->method('setQueryParam'); + $this->queryParamsResolverMock->expects($this->once())->method('setQueryParam')->with('___store', $storeCode); $this->model->beforeSetRouteParams( $routeParamsResolverMock, @@ -102,6 +125,8 @@ public function testBeforeSetRouteParamsScopeUseStoreInUrl() public function testBeforeSetRouteParamsSingleStore() { $storeCode = 'custom_store'; + $data = ['_scope' => $storeCode, '_scope_to_url' => true]; + $this->scopeConfigMock ->expects($this->once()) ->method('getValue') @@ -112,7 +137,7 @@ public function testBeforeSetRouteParamsSingleStore() ) ->will($this->returnValue(false)); $this->storeManagerMock->expects($this->any())->method('hasSingleStore')->willReturn(true); - $data = ['_scope' => $storeCode, '_scope_to_url' => true]; + /** @var \PHPUnit_Framework_MockObject_MockObject $routeParamsResolverMock */ $routeParamsResolverMock = $this->getMockBuilder(\Magento\Framework\Url\RouteParamsResolver::class) ->setMethods(['setScope', 'getScope']) @@ -132,6 +157,8 @@ public function testBeforeSetRouteParamsSingleStore() public function testBeforeSetRouteParamsNoScopeInParams() { $storeCode = 'custom_store'; + $data = ['_scope_to_url' => true]; + $this->scopeConfigMock ->expects($this->once()) ->method('getValue') @@ -140,17 +167,10 @@ public function testBeforeSetRouteParamsNoScopeInParams() \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $storeCode ) - ->will($this->returnValue(false)); + ->will($this->returnValue(true)); + $this->storeManagerMock->expects($this->any())->method('hasSingleStore')->willReturn(false); - /** @var \PHPUnit_Framework_MockObject_MockObject| $routeParamsResolverMock */ - $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->setMethods(['getCode']) - ->disableOriginalConstructor() - ->getMock(); - $storeMock->expects($this->any())->method('getCode')->willReturn($storeCode); - $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock); - $data = ['_scope_to_url' => true]; /** @var \PHPUnit_Framework_MockObject_MockObject $routeParamsResolverMock */ $routeParamsResolverMock = $this->getMockBuilder(\Magento\Framework\Url\RouteParamsResolver::class) ->setMethods(['setScope', 'getScope']) From 16ae409bbf5892b40b1a009eca76e2f1bbb73037 Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Sun, 1 Jul 2018 11:33:38 +0530 Subject: [PATCH 0594/1171] Update RouteParamsResolverTest.php --- .../Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php b/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php index 51e3a9dc32e13..f31acd40a2e69 100644 --- a/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php +++ b/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php @@ -82,7 +82,6 @@ public function testBeforeSetRouteParamsScopeInParams() $this->queryParamsResolverMock->expects($this->never())->method('setQueryParam'); - $this->model->beforeSetRouteParams( $routeParamsResolverMock, $data From 4dc6aba5daa877d5a39fcb8423c7f7a66875e8fd Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Wed, 18 Jul 2018 09:15:18 +0300 Subject: [PATCH 0595/1171] Change checking of StoreInterface instead of Store in RouteParamsResolver.php --- app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php b/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php index 325e621c8a113..468352af78cbc 100644 --- a/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php +++ b/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php @@ -6,6 +6,7 @@ namespace Magento\Store\Url\Plugin; use \Magento\Store\Model\Store; +use \Magento\Store\Api\Data\StoreInterface; use \Magento\Store\Model\ScopeInterface as StoreScopeInterface; /** @@ -65,9 +66,9 @@ public function beforeSetRouteParams( unset($data['_scope']); } if (isset($data['_scope_to_url']) && (bool)$data['_scope_to_url'] === true) { - /** @var Store $currentScope */ + /** @var StoreInterface $currentScope */ $currentScope = $subject->getScope(); - $storeCode = $currentScope && $currentScope instanceof Store ? + $storeCode = $currentScope && $currentScope instanceof StoreInterface ? $currentScope->getCode() : $this->storeManager->getStore()->getCode(); From 9e98306d4b9d7f984b2406a7982ed6945dfe2bcb Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Sun, 29 Jul 2018 23:41:36 +0530 Subject: [PATCH 0596/1171] Add meta NOINDEX,NOFOLLOW to admin scope to avoid accidental crawling --- .../adminhtml/Magento/backend/Magento_Backend/layout/default.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/layout/default.xml b/app/design/adminhtml/Magento/backend/Magento_Backend/layout/default.xml index da16bde107673..337d63369b160 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/layout/default.xml +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/layout/default.xml @@ -7,6 +7,7 @@ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <head> + <meta name="robots" content="NOINDEX,NOFOLLOW"/> <css src="jquery/jstree/themes/default/style.css"/> <css src="css/styles-old.css"/> <css src="css/styles.css"/> From c6c902b08af1f12b50ba4c7229f660f1d405bbd4 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Sun, 29 Jul 2018 21:45:19 -0500 Subject: [PATCH 0597/1171] MQE-1112: Bump MFTF version in Magento --- composer.lock | 54 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/composer.lock b/composer.lock index cae09c4a73b62..611411d61f60d 100644 --- a/composer.lock +++ b/composer.lock @@ -1,7 +1,7 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], "content-hash": "7537dab4a1593204387fb3f6adf4f33d", @@ -840,6 +840,13 @@ "reference": "68522e5768edc8e829d1f64b620a3de3753f1141", "shasum": "" }, + "archive": { + "exclude": [ + "/demos", + "/documentation", + "/tests" + ] + }, "require": { "php": ">=5.2.11" }, @@ -858,7 +865,6 @@ "Zend_": "library/" } }, - "notification-url": "https://packagist.org/downloads/", "include-path": [ "library/" ], @@ -868,10 +874,14 @@ "description": "Magento Zend Framework 1", "homepage": "http://framework.zend.com/", "keywords": [ - "ZF1", - "framework" + "framework", + "zf1" ], - "time": "2018-04-06T18:49:03+00:00" + "support": { + "source": "https://github.com/magento-engcom/zf1-php-7.2-support/tree/master", + "issues": "https://github.com/magento-engcom/zf1-php-7.2-support/issues" + }, + "time": "2018-04-06T17:12:22+00:00" }, { "name": "monolog/monolog", @@ -2718,16 +2728,16 @@ }, { "name": "zendframework/zend-diactoros", - "version": "1.8.2", + "version": "1.8.3", "source": { "type": "git", "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "273c18bf6aaab20be9667a3aa4e7702e1e4e7ced" + "reference": "72c13834fb3db2a962e913758b384ff2e6425d6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/273c18bf6aaab20be9667a3aa4e7702e1e4e7ced", - "reference": "273c18bf6aaab20be9667a3aa4e7702e1e4e7ced", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/72c13834fb3db2a962e913758b384ff2e6425d6e", + "reference": "72c13834fb3db2a962e913758b384ff2e6425d6e", "shasum": "" }, "require": { @@ -2740,7 +2750,7 @@ "require-dev": { "ext-dom": "*", "ext-libxml": "*", - "phpunit/phpunit": "^5.7.16 || ^6.0.8", + "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", "zendframework/zend-coding-standard": "~1.0" }, "type": "library", @@ -2777,7 +2787,7 @@ "psr", "psr-7" ], - "time": "2018-07-19T18:38:31+00:00" + "time": "2018-07-24T21:54:38+00:00" }, { "name": "zendframework/zend-escaper", @@ -6012,16 +6022,16 @@ }, { "name": "jms/serializer", - "version": "1.12.1", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/schmittjoh/serializer.git", - "reference": "93d6e03fcb71d45854cc44b5a84d645c02c5d763" + "reference": "00863e1d55b411cc33ad3e1de09a4c8d3aae793c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/93d6e03fcb71d45854cc44b5a84d645c02c5d763", - "reference": "93d6e03fcb71d45854cc44b5a84d645c02c5d763", + "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/00863e1d55b411cc33ad3e1de09a4c8d3aae793c", + "reference": "00863e1d55b411cc33ad3e1de09a4c8d3aae793c", "shasum": "" }, "require": { @@ -6061,7 +6071,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-1.x": "1.11-dev" + "dev-1.x": "1.13-dev" } }, "autoload": { @@ -6092,7 +6102,7 @@ "serialization", "xml" ], - "time": "2018-06-01T12:10:12+00:00" + "time": "2018-07-25T13:58:54+00:00" }, { "name": "league/container", @@ -8816,16 +8826,16 @@ }, { "name": "vlucas/phpdotenv", - "version": "v2.5.0", + "version": "v2.5.1", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a" + "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/6ae3e2e6494bb5e58c2decadafc3de7f1453f70a", - "reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e", + "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e", "shasum": "" }, "require": { @@ -8862,7 +8872,7 @@ "env", "environment" ], - "time": "2018-07-01T10:25:50+00:00" + "time": "2018-07-29T20:33:41+00:00" }, { "name": "webmozart/assert", From fafbbd2d73dc9da9c78a1fc5881dd1301ef92767 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Sat, 28 Jul 2018 10:47:01 -0300 Subject: [PATCH 0598/1171] Replaced deprecated methods. --- .../Controller/Adminhtml/Promo/Catalog/ApplyRules.php | 6 +++--- .../Controller/Adminhtml/Promo/Catalog/Delete.php | 8 ++++---- .../Controller/Adminhtml/Promo/Catalog/Edit.php | 2 +- .../Controller/Adminhtml/Promo/Catalog/Save.php | 8 ++++---- .../CatalogRule/Plugin/Indexer/Product/Attribute.php | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/ApplyRules.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/ApplyRules.php index 85ad74f7bbfe2..4badfa1219e10 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/ApplyRules.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/ApplyRules.php @@ -25,14 +25,14 @@ public function execute() $ruleJob->applyAll(); if ($ruleJob->hasSuccess()) { - $this->messageManager->addSuccess($ruleJob->getSuccess()); + $this->messageManager->addSuccessMessage($ruleJob->getSuccess()); $this->_objectManager->create(\Magento\CatalogRule\Model\Flag::class)->loadSelf()->setState(0)->save(); } elseif ($ruleJob->hasError()) { - $this->messageManager->addError($errorMessage . ' ' . $ruleJob->getError()); + $this->messageManager->addErrorMessage($errorMessage . ' ' . $ruleJob->getError()); } } catch (\Exception $e) { $this->_objectManager->create(\Psr\Log\LoggerInterface::class)->critical($e); - $this->messageManager->addError($errorMessage); + $this->messageManager->addErrorMessage($errorMessage); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php index 8b007031f3305..3500506d8d6c5 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php @@ -25,13 +25,13 @@ public function execute() $ruleRepository->deleteById($id); $this->_objectManager->create(\Magento\CatalogRule\Model\Flag::class)->loadSelf()->setState(1)->save(); - $this->messageManager->addSuccess(__('You deleted the rule.')); + $this->messageManager->addSuccessMessage(__('You deleted the rule.')); $this->_redirect('catalog_rule/*/'); return; } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('We can\'t delete this rule right now. Please review the log and try again.') ); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); @@ -39,7 +39,7 @@ public function execute() return; } } - $this->messageManager->addError(__('We can\'t find a rule to delete.')); + $this->messageManager->addErrorMessage(__('We can\'t find a rule to delete.')); $this->_redirect('catalog_rule/*/'); } } diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php index 97a5693b18117..945c28b2088f2 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php @@ -24,7 +24,7 @@ public function execute() try { $model = $ruleRepository->get($id); } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { - $this->messageManager->addError(__('This rule no longer exists.')); + $this->messageManager->addErrorMessage(__('This rule no longer exists.')); $this->_redirect('catalog_rule/*'); return; } diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index 0170fc76b6aab..f3046c58a389b 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -68,7 +68,7 @@ public function execute() $validateResult = $model->validateData(new \Magento\Framework\DataObject($data)); if ($validateResult !== true) { foreach ($validateResult as $errorMessage) { - $this->messageManager->addError($errorMessage); + $this->messageManager->addErrorMessage($errorMessage); } $this->_getSession()->setPageData($data); $this->dataPersistor->set('catalog_rule', $data); @@ -88,7 +88,7 @@ public function execute() $ruleRepository->save($model); - $this->messageManager->addSuccess(__('You saved the rule.')); + $this->messageManager->addSuccessMessage(__('You saved the rule.')); $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setPageData(false); $this->dataPersistor->clear('catalog_rule'); @@ -111,9 +111,9 @@ public function execute() } return; } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('Something went wrong while saving the rule data. Please review the error log.') ); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); diff --git a/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Attribute.php b/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Attribute.php index cc808a38db698..7fdffe933db8c 100644 --- a/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Attribute.php +++ b/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Attribute.php @@ -103,7 +103,7 @@ protected function checkCatalogRulesAvailability($attributeCode) if ($disabledRulesCount) { $this->ruleProductProcessor->markIndexerAsInvalid(); - $this->messageManager->addWarning( + $this->messageManager->addWarningMessage( __( 'You disabled %1 Catalog Price Rules based on "%2" attribute.', $disabledRulesCount, From 4c436fce016a896e5448bd0e2f7d4287e62f5f14 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Sun, 29 Jul 2018 23:09:14 -0500 Subject: [PATCH 0599/1171] MQE-1112: Bump MFTF version in Magento --- dev/tests/functional/bootstrap.php | 5 +++++ .../Backend/Test/TestCase/ConfigureSecureUrlsTest.xml | 1 + 2 files changed, 6 insertions(+) diff --git a/dev/tests/functional/bootstrap.php b/dev/tests/functional/bootstrap.php index 0dbf0d9471f18..04156ca88ea70 100644 --- a/dev/tests/functional/bootstrap.php +++ b/dev/tests/functional/bootstrap.php @@ -14,6 +14,11 @@ include __DIR__ . '/vendor/autoload.php'; setCustomErrorHandler(); +/* Custom umask value may be provided in optional mage_umask file in root */ +$umaskFile = BP . '/magento_umask'; +$mask = file_exists($umaskFile) ? octdec(file_get_contents($umaskFile)) : 002; +umask($mask); + date_default_timezone_set('UTC'); /* For data consistency between displaying (printing) and serialization a float number */ diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml index 790c02abda2f7..4a53ac2fa1dba 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Backend\Test\TestCase\ConfigureSecureUrlsTest" summary="Configure secure URLs" ticketId="MAGETWO-35408"> <variation name="ConfigureSecureUrlsHttpForStorefrontHttpsForAdmin" summary="http for Storefront, https for Admin" ticketId="MAGETWO-35408"> + <data name="issue" xsi:type="string">MQE-1160</data> <data name="configData" xsi:type="string">disable_https_frontend_admin</data> <data name="navMenuPath" xsi:type="string">Marketing>Catalog Price Rule</data> <constraint name="Magento\Backend\Test\Constraint\AssertHttpUsedOnFrontend" /> From 41ab82eef0f4b23abcc30c653c0aa438635ede6e Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Mon, 30 Jul 2018 08:17:57 +0300 Subject: [PATCH 0600/1171] MAGETWO-93776: [Forwardport] Stabilization --- .../testsuite/Magento/Catalog/Model/ProductPriceTest.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php index 088a86ef84d07..f7ae17e06a33b 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php @@ -84,17 +84,19 @@ public function testSetGetFinalPrice() /** * @magentoDbIsolation disabled * @magentoDataFixture Magento/Catalog/_files/product_with_options.php + * @return void */ - public function testGetMinPrice() + public function testGetMinPrice(): void { - $product = $this->productRepository->get('simple'); + $productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); + $product = $productRepository->get('simple'); $collection = Bootstrap::getObjectManager()->create(Collection::class); $collection->addIdFilter($product->getId()); $collection->addPriceData(); $collection->load(); /** @var \Magento\Catalog\Model\Product $product */ $product = $collection->getFirstItem(); - $this->assertEquals(323, $product->getData('min_price')); + $this->assertEquals(333, $product->getData('min_price')); } /** From 90134aef95bdd3689f8236eab233b09e6c84a9a1 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Mon, 30 Jul 2018 08:48:05 +0200 Subject: [PATCH 0601/1171] [fix] license in composer --- app/code/Magento/AdvancedSearch/Test/Mftf/composer.json | 5 +++-- app/code/Magento/AdvancedSearch/composer.json | 3 ++- app/code/Magento/Amqp/Test/Mftf/composer.json | 3 ++- app/code/Magento/Amqp/composer.json | 3 ++- .../Magento/AsynchronousOperations/Test/Mftf/composer.json | 3 ++- app/code/Magento/AsynchronousOperations/composer.json | 3 ++- app/code/Magento/Authorizenet/Test/Mftf/composer.json | 3 ++- app/code/Magento/Authorizenet/composer.json | 3 ++- app/code/Magento/Braintree/Test/Mftf/composer.json | 3 ++- app/code/Magento/Braintree/composer.json | 3 ++- app/code/Magento/Elasticsearch/Test/Mftf/composer.json | 3 ++- app/code/Magento/Elasticsearch/composer.json | 3 ++- app/code/Magento/EncryptionKey/Test/Mftf/composer.json | 3 ++- app/code/Magento/EncryptionKey/composer.json | 3 ++- app/code/Magento/MessageQueue/Test/Mftf/composer.json | 3 ++- app/code/Magento/MessageQueue/composer.json | 3 ++- app/code/Magento/MysqlMq/Test/Mftf/composer.json | 3 ++- app/code/Magento/MysqlMq/composer.json | 3 ++- app/code/Magento/Paypal/Test/Mftf/composer.json | 3 ++- app/code/Magento/Paypal/composer.json | 3 ++- app/code/Magento/ProductVideo/Test/Mftf/composer.json | 3 ++- app/code/Magento/ProductVideo/composer.json | 3 ++- app/code/Magento/Signifyd/Test/Mftf/composer.json | 3 ++- app/code/Magento/Signifyd/composer.json | 3 ++- app/code/Magento/SwaggerWebapiAsync/Test/Mftf/composer.json | 3 ++- app/code/Magento/SwaggerWebapiAsync/composer.json | 3 ++- app/code/Magento/Swatches/Test/Mftf/composer.json | 3 ++- app/code/Magento/Swatches/composer.json | 3 ++- app/code/Magento/Vault/Test/Mftf/composer.json | 3 ++- app/code/Magento/Vault/composer.json | 3 ++- 30 files changed, 61 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/AdvancedSearch/Test/Mftf/composer.json b/app/code/Magento/AdvancedSearch/Test/Mftf/composer.json index b5582c72a039a..77220575715ac 100644 --- a/app/code/Magento/AdvancedSearch/Test/Mftf/composer.json +++ b/app/code/Magento/AdvancedSearch/Test/Mftf/composer.json @@ -17,6 +17,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" - ] + "OSL-3.0", + "AFL-3.0" + ], } diff --git a/app/code/Magento/AdvancedSearch/composer.json b/app/code/Magento/AdvancedSearch/composer.json index a224a1001cd01..c6a5af07e60a7 100644 --- a/app/code/Magento/AdvancedSearch/composer.json +++ b/app/code/Magento/AdvancedSearch/composer.json @@ -17,7 +17,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/Amqp/Test/Mftf/composer.json b/app/code/Magento/Amqp/Test/Mftf/composer.json index 348200c5d1cb4..3cb9637c9a7ba 100644 --- a/app/code/Magento/Amqp/Test/Mftf/composer.json +++ b/app/code/Magento/Amqp/Test/Mftf/composer.json @@ -12,6 +12,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/Amqp/composer.json b/app/code/Magento/Amqp/composer.json index 23130dfb01a4e..b50e951b46f81 100644 --- a/app/code/Magento/Amqp/composer.json +++ b/app/code/Magento/Amqp/composer.json @@ -12,7 +12,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/AsynchronousOperations/Test/Mftf/composer.json b/app/code/Magento/AsynchronousOperations/Test/Mftf/composer.json index baadc0d0d914a..e1ac71b46c546 100644 --- a/app/code/Magento/AsynchronousOperations/Test/Mftf/composer.json +++ b/app/code/Magento/AsynchronousOperations/Test/Mftf/composer.json @@ -19,6 +19,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/AsynchronousOperations/composer.json b/app/code/Magento/AsynchronousOperations/composer.json index 3acb92710e62b..7d5a097eeea3f 100644 --- a/app/code/Magento/AsynchronousOperations/composer.json +++ b/app/code/Magento/AsynchronousOperations/composer.json @@ -19,7 +19,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/Authorizenet/Test/Mftf/composer.json b/app/code/Magento/Authorizenet/Test/Mftf/composer.json index b02806a0ae3f3..176f6207e1bac 100644 --- a/app/code/Magento/Authorizenet/Test/Mftf/composer.json +++ b/app/code/Magento/Authorizenet/Test/Mftf/composer.json @@ -20,6 +20,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/Authorizenet/composer.json b/app/code/Magento/Authorizenet/composer.json index 31f2295da4307..4e646004d9a6d 100644 --- a/app/code/Magento/Authorizenet/composer.json +++ b/app/code/Magento/Authorizenet/composer.json @@ -20,7 +20,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/Braintree/Test/Mftf/composer.json b/app/code/Magento/Braintree/Test/Mftf/composer.json index 2f827424cb7f1..b973b89602347 100644 --- a/app/code/Magento/Braintree/Test/Mftf/composer.json +++ b/app/code/Magento/Braintree/Test/Mftf/composer.json @@ -28,6 +28,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/Braintree/composer.json b/app/code/Magento/Braintree/composer.json index 8fb98dce72c92..ba51a5436fedb 100644 --- a/app/code/Magento/Braintree/composer.json +++ b/app/code/Magento/Braintree/composer.json @@ -29,7 +29,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/composer.json b/app/code/Magento/Elasticsearch/Test/Mftf/composer.json index 4f0b74e9c5660..b8c984c6e4ff6 100644 --- a/app/code/Magento/Elasticsearch/Test/Mftf/composer.json +++ b/app/code/Magento/Elasticsearch/Test/Mftf/composer.json @@ -21,6 +21,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/Elasticsearch/composer.json b/app/code/Magento/Elasticsearch/composer.json index a6b4f93ade579..a821506f5ef6e 100644 --- a/app/code/Magento/Elasticsearch/composer.json +++ b/app/code/Magento/Elasticsearch/composer.json @@ -19,7 +19,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/composer.json b/app/code/Magento/EncryptionKey/Test/Mftf/composer.json index 732a18753779f..5805c221e6e75 100644 --- a/app/code/Magento/EncryptionKey/Test/Mftf/composer.json +++ b/app/code/Magento/EncryptionKey/Test/Mftf/composer.json @@ -12,6 +12,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/EncryptionKey/composer.json b/app/code/Magento/EncryptionKey/composer.json index 0bf0728606684..4a140c4b1315e 100644 --- a/app/code/Magento/EncryptionKey/composer.json +++ b/app/code/Magento/EncryptionKey/composer.json @@ -12,7 +12,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/MessageQueue/Test/Mftf/composer.json b/app/code/Magento/MessageQueue/Test/Mftf/composer.json index e499fed390751..20783280811c7 100644 --- a/app/code/Magento/MessageQueue/Test/Mftf/composer.json +++ b/app/code/Magento/MessageQueue/Test/Mftf/composer.json @@ -11,6 +11,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/MessageQueue/composer.json b/app/code/Magento/MessageQueue/composer.json index 5cdf7351bbb61..9d3c77fa0184a 100644 --- a/app/code/Magento/MessageQueue/composer.json +++ b/app/code/Magento/MessageQueue/composer.json @@ -11,7 +11,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/MysqlMq/Test/Mftf/composer.json b/app/code/Magento/MysqlMq/Test/Mftf/composer.json index 0aa70a28428e4..fec40ce087ab8 100644 --- a/app/code/Magento/MysqlMq/Test/Mftf/composer.json +++ b/app/code/Magento/MysqlMq/Test/Mftf/composer.json @@ -12,6 +12,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/MysqlMq/composer.json b/app/code/Magento/MysqlMq/composer.json index 3d7ee4f8b037b..d9bcf307a5bf9 100644 --- a/app/code/Magento/MysqlMq/composer.json +++ b/app/code/Magento/MysqlMq/composer.json @@ -12,7 +12,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/Paypal/Test/Mftf/composer.json b/app/code/Magento/Paypal/Test/Mftf/composer.json index 4822c694580ef..1e2bc3c33a0c4 100644 --- a/app/code/Magento/Paypal/Test/Mftf/composer.json +++ b/app/code/Magento/Paypal/Test/Mftf/composer.json @@ -29,6 +29,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/Paypal/composer.json b/app/code/Magento/Paypal/composer.json index 8bc05710452bb..b62985e8b8b70 100644 --- a/app/code/Magento/Paypal/composer.json +++ b/app/code/Magento/Paypal/composer.json @@ -30,7 +30,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/ProductVideo/Test/Mftf/composer.json b/app/code/Magento/ProductVideo/Test/Mftf/composer.json index a2f1223db010d..df3a9afb3584f 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/composer.json +++ b/app/code/Magento/ProductVideo/Test/Mftf/composer.json @@ -20,6 +20,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/ProductVideo/composer.json b/app/code/Magento/ProductVideo/composer.json index 829816321c1a5..24497f891db43 100644 --- a/app/code/Magento/ProductVideo/composer.json +++ b/app/code/Magento/ProductVideo/composer.json @@ -20,7 +20,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/Signifyd/Test/Mftf/composer.json b/app/code/Magento/Signifyd/Test/Mftf/composer.json index dc969cc4780ef..7ae52655d19f4 100644 --- a/app/code/Magento/Signifyd/Test/Mftf/composer.json +++ b/app/code/Magento/Signifyd/Test/Mftf/composer.json @@ -21,6 +21,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/Signifyd/composer.json b/app/code/Magento/Signifyd/composer.json index c766ccd848ca4..992a74d0dc3ec 100644 --- a/app/code/Magento/Signifyd/composer.json +++ b/app/code/Magento/Signifyd/composer.json @@ -21,7 +21,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/SwaggerWebapiAsync/Test/Mftf/composer.json b/app/code/Magento/SwaggerWebapiAsync/Test/Mftf/composer.json index fe20e7a539d03..58d3740a12021 100644 --- a/app/code/Magento/SwaggerWebapiAsync/Test/Mftf/composer.json +++ b/app/code/Magento/SwaggerWebapiAsync/Test/Mftf/composer.json @@ -14,6 +14,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/SwaggerWebapiAsync/composer.json b/app/code/Magento/SwaggerWebapiAsync/composer.json index 5ca538636b38a..3735bcd0ee458 100644 --- a/app/code/Magento/SwaggerWebapiAsync/composer.json +++ b/app/code/Magento/SwaggerWebapiAsync/composer.json @@ -14,7 +14,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/Swatches/Test/Mftf/composer.json b/app/code/Magento/Swatches/Test/Mftf/composer.json index 76c8037995754..daac589907f2c 100644 --- a/app/code/Magento/Swatches/Test/Mftf/composer.json +++ b/app/code/Magento/Swatches/Test/Mftf/composer.json @@ -23,6 +23,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/Swatches/composer.json b/app/code/Magento/Swatches/composer.json index 71f9fc8f80baa..0940003b6a72d 100644 --- a/app/code/Magento/Swatches/composer.json +++ b/app/code/Magento/Swatches/composer.json @@ -23,7 +23,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ diff --git a/app/code/Magento/Vault/Test/Mftf/composer.json b/app/code/Magento/Vault/Test/Mftf/composer.json index 78e765bebc429..64fe51a3a1a3c 100644 --- a/app/code/Magento/Vault/Test/Mftf/composer.json +++ b/app/code/Magento/Vault/Test/Mftf/composer.json @@ -16,6 +16,7 @@ }, "type": "magento2-test", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ] } diff --git a/app/code/Magento/Vault/composer.json b/app/code/Magento/Vault/composer.json index a4093617b82cc..f888a98692288 100644 --- a/app/code/Magento/Vault/composer.json +++ b/app/code/Magento/Vault/composer.json @@ -16,7 +16,8 @@ }, "type": "magento2-module", "license": [ - "proprietary" + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ From 0d5cb1e4d2f74eab03c637c68cb820e5adab75e0 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Mon, 30 Jul 2018 08:53:39 +0200 Subject: [PATCH 0602/1171] [fix] Remove trailing comma --- app/code/Magento/AdvancedSearch/Test/Mftf/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/AdvancedSearch/Test/Mftf/composer.json b/app/code/Magento/AdvancedSearch/Test/Mftf/composer.json index 77220575715ac..7ae6dbb7eb709 100644 --- a/app/code/Magento/AdvancedSearch/Test/Mftf/composer.json +++ b/app/code/Magento/AdvancedSearch/Test/Mftf/composer.json @@ -19,5 +19,5 @@ "license": [ "OSL-3.0", "AFL-3.0" - ], + ] } From a68f3139026d14e1ae68f062b5610062cd9a7b0e Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Mon, 30 Jul 2018 02:04:51 -0500 Subject: [PATCH 0603/1171] MQE-1112: Bump MFTF version in Magento --- dev/tests/functional/.gitignore | 1 + .../Constraint/AssertHttpUsedOnFrontend.php | 6 +++--- .../Constraint/AssertHttpsUsedOnBackend.php | 20 ++++++------------- .../CreateCustomerBackendEntityTest.php | 3 --- .../Ui/Test/Block/Adminhtml/FormSections.php | 5 ++--- .../functional/utils/generate/repository.php | 1 - 6 files changed, 12 insertions(+), 24 deletions(-) diff --git a/dev/tests/functional/.gitignore b/dev/tests/functional/.gitignore index d47f62b56bb9a..92e3004580b0c 100644 --- a/dev/tests/functional/.gitignore +++ b/dev/tests/functional/.gitignore @@ -6,3 +6,4 @@ /vendor phpunit.xml credentials.xml +.htaccess diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpUsedOnFrontend.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpUsedOnFrontend.php index f028a0bcf65ba..7d75db5f0368c 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpUsedOnFrontend.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpUsedOnFrontend.php @@ -22,7 +22,7 @@ class AssertHttpUsedOnFrontend extends AbstractConstraint * * @var string */ - private $unsecuredProtocol = \Magento\Framework\HTTP\PhpEnvironment\Request::SCHEME_HTTP; + const SCHEME_HTTP = 'http'; /** * Browser interface. @@ -53,11 +53,11 @@ public function processAssert(BrowserInterface $browser, Customer $customer) // Log in to Customer Account on Storefront to assert that http is used indeed. $this->objectManager->create(LogInCustomerOnStorefront::class, ['customer' => $this->customer])->run(); - $this->assertUsedProtocol($this->unsecuredProtocol); + $this->assertUsedProtocol(self::SCHEME_HTTP); // Log out from Customer Account on Storefront to assert that JS is deployed validly as a part of statics. $this->objectManager->create(LogOutCustomerOnStorefront::class)->run(); - $this->assertUsedProtocol($this->unsecuredProtocol); + $this->assertUsedProtocol(self::SCHEME_HTTP); } /** diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsUsedOnBackend.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsUsedOnBackend.php index 5da51c8079d7f..cc64e4a4c7299 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsUsedOnBackend.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsUsedOnBackend.php @@ -16,18 +16,10 @@ class AssertHttpsUsedOnBackend extends AbstractConstraint { /** - * Secured protocol format. - * - * @var string - */ - private $securedProtocol = \Magento\Framework\HTTP\PhpEnvironment\Request::SCHEME_HTTPS; - - /** - * Unsecured protocol format. - * - * @var string + * Protocols */ - private $unsecuredProtocol = \Magento\Framework\HTTP\PhpEnvironment\Request::SCHEME_HTTP; + const SCHEME_HTTP = 'http'; + const SCHEME_HTTPS = 'https'; /** * Browser interface. @@ -50,7 +42,7 @@ public function processAssert(BrowserInterface $browser, Dashboard $adminDashboa // Open specified Admin page using Navigation Menu to assert that JS is deployed validly as a part of statics. $adminDashboardPage->open()->getMenuBlock()->navigate($navMenuPath); - $this->assertUsedProtocol($this->securedProtocol); + $this->assertUsedProtocol(self::SCHEME_HTTPS); $this->assertDirectHttpUnavailable(); } @@ -80,10 +72,10 @@ protected function assertUsedProtocol($expectedProtocol) */ protected function assertDirectHttpUnavailable() { - $fakeUrl = str_replace($this->securedProtocol, $this->unsecuredProtocol, $this->browser->getUrl()); + $fakeUrl = str_replace(self::SCHEME_HTTPS, self::SCHEME_HTTP, $this->browser->getUrl()); $this->browser->open($fakeUrl); \PHPUnit\Framework\Assert::assertStringStartsWith( - $this->securedProtocol, + self::SCHEME_HTTPS, $this->browser->getUrl(), 'Merchant is not redirected to https if tries to access the Admin panel page directly via http.' ); diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateCustomerBackendEntityTest.php b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateCustomerBackendEntityTest.php index 1c697d9f1e5da..f1a8b17ae2855 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateCustomerBackendEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateCustomerBackendEntityTest.php @@ -6,9 +6,6 @@ namespace Magento\Customer\Test\TestCase; -use Magento\Config\Test\Fixture\ConfigData; -use Magento\Customer\Test\Constraint\AssertChangingWebsiteChangeCountries; -use Magento\Framework\App\ObjectManager; use Magento\Mtf\Fixture\FixtureFactory; use Magento\Mtf\Fixture\FixtureInterface; use Magento\Mtf\TestCase\Injectable; diff --git a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/FormSections.php b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/FormSections.php index c98b5a874a762..170fef137ec9f 100644 --- a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/FormSections.php +++ b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/FormSections.php @@ -6,7 +6,6 @@ namespace Magento\Ui\Test\Block\Adminhtml; -use Magento\Framework\Exception\LocalizedException; use Magento\Mtf\Fixture\InjectableFixture; /** @@ -53,13 +52,13 @@ protected function openContainer($sectionName) * * @param string $sectionName * @return $this - * @throws LocalizedException if section is not visible + * @throws \Exception if section is not visible */ public function openSection($sectionName) { $container = $this->getContainerElement($sectionName); if (!$container->isVisible()) { - throw new LocalizedException(__('Container is not found "' . $sectionName . '""')); + throw new \Exception('Container is not found "' . $sectionName . '""'); } $section = $container->find($this->collapsedSection); if ($section->isVisible()) { diff --git a/dev/tests/functional/utils/generate/repository.php b/dev/tests/functional/utils/generate/repository.php index b2dc75c076f43..6633e776c9410 100644 --- a/dev/tests/functional/utils/generate/repository.php +++ b/dev/tests/functional/utils/generate/repository.php @@ -5,5 +5,4 @@ */ require_once dirname(__DIR__) . '/' . 'bootstrap.php'; -$magentoObjectManager->get(\Magento\Framework\App\State::class)->setAreaCode('frontend'); $objectManager->create(\Magento\Mtf\Util\Generate\Repository::class)->launch(); From 17a4afaee18a2cec710343becba8e6c04ce34860 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Mon, 30 Jul 2018 02:32:08 -0500 Subject: [PATCH 0604/1171] MQE-1112: Bump MFTF version in Magento --- .../Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml index 4a53ac2fa1dba..790c02abda2f7 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml @@ -8,7 +8,6 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Backend\Test\TestCase\ConfigureSecureUrlsTest" summary="Configure secure URLs" ticketId="MAGETWO-35408"> <variation name="ConfigureSecureUrlsHttpForStorefrontHttpsForAdmin" summary="http for Storefront, https for Admin" ticketId="MAGETWO-35408"> - <data name="issue" xsi:type="string">MQE-1160</data> <data name="configData" xsi:type="string">disable_https_frontend_admin</data> <data name="navMenuPath" xsi:type="string">Marketing>Catalog Price Rule</data> <constraint name="Magento\Backend\Test\Constraint\AssertHttpUsedOnFrontend" /> From 611e805dc388ae4fd9b5a69f6c50020e4f2d6418 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 30 Jul 2018 13:03:38 +0300 Subject: [PATCH 0605/1171] MAGETWO-93306: It's impossible to specify negative value in "Quantity" field for product --- .../view/adminhtml/ui_component/product_form.xml | 1 + .../view/adminhtml/web/js/components/qty-validator-changer.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml index d82b4a97ddbf6..f8571b6e332b6 100644 --- a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml +++ b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml @@ -571,6 +571,7 @@ <settings> <scopeLabel>[GLOBAL]</scopeLabel> <validation> + <rule name="validate-integer" xsi:type="boolean">true</rule> <rule name="validate-number" xsi:type="boolean">true</rule> </validation> <label translate="true">Qty Increments</label> diff --git a/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/qty-validator-changer.js b/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/qty-validator-changer.js index 23a33f51af6d4..75d684137a28b 100644 --- a/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/qty-validator-changer.js +++ b/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/qty-validator-changer.js @@ -20,7 +20,6 @@ define([ var isDigits = value !== 1; this.validation['validate-integer'] = isDigits; - this.validation['validate-digits'] = isDigits; this.validation['less-than-equals-to'] = isDigits ? 99999999 : 99999999.9999; this.validate(); } From 465d5fa27530d64c65fc59c93529f707aa507e37 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Mon, 30 Jul 2018 15:11:55 +0300 Subject: [PATCH 0606/1171] MAGETWO-91328: Checkout doesn't work when AdBlock extension enabled and Google Analytics is enabled --- lib/web/mage/requirejs/resolver.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/web/mage/requirejs/resolver.js b/lib/web/mage/requirejs/resolver.js index 5088206dd31d9..6927a3205d94d 100644 --- a/lib/web/mage/requirejs/resolver.js +++ b/lib/web/mage/requirejs/resolver.js @@ -8,11 +8,11 @@ define([ ], function (_) { 'use strict'; - var context = require.s.contexts._, - execCb = context.execCb, - registry = context.registry, - callbacks = [], - retries = 10, + var context = require.s.contexts._, + execCb = context.execCb, + registry = context.registry, + callbacks = [], + retries = 10, updateDelay = 1, ready, update; @@ -28,13 +28,13 @@ define([ } /** - * Checks if provided module is rejected during load. + * Checks if provided module is registered after load. * * @param {Object} module - Module to be checked. * @return {Boolean} */ - function isRejected(module) { - return registry[module.id] && registry[module.id].error; + function isRegistered(module) { + return registry[module.id]; } /** @@ -48,7 +48,7 @@ define([ return false; } - return module.depCount > _.filter(module.depMaps, isRejected).length; + return module.depCount > _.filter(module.depMaps, isRegistered).length; } /** From 8cd577539ba926d157f4f3bafa8b3cb639505057 Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Mon, 30 Jul 2018 16:07:29 +0300 Subject: [PATCH 0607/1171] MAGETWO-91805: Product URLs in front-end Customer Account "My Product Reviews" section are not SEO friendly - Replaced product view URL by SEO friendly link= --- .../Review/Block/Customer/ListCustomer.php | 6 +++--- .../view/frontend/templates/customer/list.phtml | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Review/Block/Customer/ListCustomer.php b/app/code/Magento/Review/Block/Customer/ListCustomer.php index 8377cc73646b9..eb67af5780ddb 100644 --- a/app/code/Magento/Review/Block/Customer/ListCustomer.php +++ b/app/code/Magento/Review/Block/Customer/ListCustomer.php @@ -154,13 +154,13 @@ public function getProductLink() /** * Get product URL * - * @param \Magento\Review\Model\Review $review + * @param \Magento\Catalog\Model\Product $product * @return string * @since 100.2.0 */ - public function getProductUrl($review) + public function getProductUrl($product) { - return $this->getUrl('catalog/product/view', ['id' => $review->getEntityPkValue()]); + return $product->getProductUrl(); } /** diff --git a/app/code/Magento/Review/view/frontend/templates/customer/list.phtml b/app/code/Magento/Review/view/frontend/templates/customer/list.phtml index 725df702bb465..a65ec01d626eb 100644 --- a/app/code/Magento/Review/view/frontend/templates/customer/list.phtml +++ b/app/code/Magento/Review/view/frontend/templates/customer/list.phtml @@ -22,29 +22,29 @@ </tr> </thead> <tbody> - <?php foreach ($block->getReviews() as $_review): ?> + <?php foreach ($block->getReviews() as $review): ?> <tr> - <td data-th="<?= $block->escapeHtml(__('Created')) ?>" class="col date"><?= $block->escapeHtml($block->dateFormat($_review->getReviewCreatedAt())) ?></td> + <td data-th="<?= $block->escapeHtml(__('Created')) ?>" class="col date"><?= $block->escapeHtml($block->dateFormat($review->getReviewCreatedAt())) ?></td> <td data-th="<?= $block->escapeHtml(__('Product Name')) ?>" class="col item"> <strong class="product-name"> - <a href="<?= $block->escapeUrl($block->getProductUrl($_review)) ?>"><?= $block->escapeHtml($_review->getName()) ?></a> + <a href="<?= $block->escapeUrl($block->getProductUrl($review)) ?>"><?= $block->escapeHtml($review->getName()) ?></a> </strong> </td> <td data-th="<?= $block->escapeHtml(__('Rating')) ?>" class="col summary"> - <?php if ($_review->getSum()): ?> + <?php if ($review->getSum()): ?> <div class="rating-summary"> <span class="label"><span><?= $block->escapeHtml(__('Rating')) ?>:</span></span> - <div class="rating-result" title="<?= /* @noEscape */ ((int)$_review->getSum() / (int)$_review->getCount()) ?>%"> - <span style="width:<?= /* @noEscape */ ((int)$_review->getSum() / (int)$_review->getCount()) ?>%;"><span><?= /* @noEscape */ ((int)$_review->getSum() / (int)$_review->getCount()) ?>%</span></span> + <div class="rating-result" title="<?= /* @noEscape */ ((int)$review->getSum() / (int)$review->getCount()) ?>%"> + <span style="width:<?= /* @noEscape */ ((int)$review->getSum() / (int)$review->getCount()) ?>%;"><span><?= /* @noEscape */ ((int)$review->getSum() / (int)$review->getCount()) ?>%</span></span> </div> </div> <?php endif; ?> </td> <td data-th="<?= $block->escapeHtmlAttr(__('Review')) ?>" class="col description"> - <?= $this->helper('Magento\Review\Helper\Data')->getDetailHtml($_review->getDetail()) ?> + <?= $this->helper('Magento\Review\Helper\Data')->getDetailHtml($review->getDetail()) ?> </td> <td data-th="<?= $block->escapeHtmlAttr(__('Actions')) ?>" class="col actions"> - <a href="<?= $block->escapeUrl($block->getReviewUrl($_review)) ?>" class="action more"> + <a href="<?= $block->escapeUrl($block->getReviewUrl($review)) ?>" class="action more"> <span><?= $block->escapeHtml(__('See Details')) ?></span> </a> </td> From c3167bfde9f00cb69d10a274e820f04fba8fdc9b Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Fri, 11 May 2018 15:20:01 +0300 Subject: [PATCH 0608/1171] MAGETWO-91809: Address book does not display the saved State/Province for countries outside of US and Canada --- .../base/web/templates/form/components/collection/preview.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/templates/form/components/collection/preview.html b/app/code/Magento/Ui/view/base/web/templates/form/components/collection/preview.html index 2c0ce802b4bc0..806ac767d7b59 100644 --- a/app/code/Magento/Ui/view/base/web/templates/form/components/collection/preview.html +++ b/app/code/Magento/Ui/view/base/web/templates/form/components/collection/preview.html @@ -11,7 +11,7 @@ 'company', 'street', { - items: 'city region_id postcode', + items: 'city region_id region_id_input postcode', separator: ', ' }, 'country_id', From db8476afd44fa54094d6d3453d14600cb5fc01f5 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Mon, 30 Jul 2018 16:33:59 +0300 Subject: [PATCH 0609/1171] MAGETWO-91806: Can not view entire zoomed product image in FF --- lib/web/magnifier/magnify.js | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/web/magnifier/magnify.js b/lib/web/magnifier/magnify.js index 34ba6e8e24135..5776c666b6815 100644 --- a/lib/web/magnifier/magnify.js +++ b/lib/web/magnifier/magnify.js @@ -277,7 +277,6 @@ define([ settings = $.extend(dimentions, { top: top, - bottom: bottom, left: left, right: right }); @@ -557,6 +556,16 @@ define([ trailing: false }); + /** + * Returns top position value for passed jQuery object. + * + * @param $el + * @return {number} + */ + function getTop($el) { + return parseInt($el.get(0).style.top); + } + function shiftImage(dx, dy, e) { var top = +imagePosY + dy, left = +imagePosX + dx, @@ -584,16 +593,13 @@ define([ } if ($image.height() > $imageContainer.height()) { - - if ($imageContainer.offset().top + $imageContainer.height() > top + $image.height()) { - top = $imageContainer.offset().top + $imageContainer.height() - $image.height(); + if ($imageContainer.height() > $image.height() + top) { + $image.css('top', $imageContainer.height() - $image.height()); } else { - top = $imageContainer.offset().top < top ? 0 : top; + top = $image.height() - getTop($image) - $imageContainer.height(); + dy = dy < top ? dy : top; + $image.css('top', getTop($image) + dy); } - $image.offset({ - 'top': top - }); - $image.css('bottom', ''); } if ($image.width() > $imageContainer.width()) { @@ -690,7 +696,7 @@ define([ } else if (gallery.fullScreen && (!transitionEnabled || !transitionActive)) { e.preventDefault(); - imagePosY = $image.offset().top; + imagePosY = getTop($image); imagePosX = $image.offset().left; if (isTouchEnabled) { @@ -746,6 +752,7 @@ define([ } if (allowZoomOut) { + imagePosY = getTop($(fullscreenImageSelector, $gallery)); shiftImage(clientX - startX, clientY - startY, e); } } @@ -771,13 +778,13 @@ define([ isFullScreen = $(gallerySelector).data('fotorama').fullScreen, initVars = function () { imagePosX = $(fullscreenImageSelector, $gallery).offset().left; - imagePosY = $(fullscreenImageSelector, $gallery).offset().top; + imagePosY = getTop($(fullscreenImageSelector, $gallery)); }; if (($focus.attr('data-gallery-role') || !$focus.length) && allowZoomOut) { if (isFullScreen) { imagePosX = $(fullscreenImageSelector, $(gallerySelector)).offset().left; - imagePosY = $(fullscreenImageSelector, $(gallerySelector)).offset().top; + imagePosY = getTop($(fullscreenImageSelector, $(gallerySelector))); } if (e.keyCode === 39) { From 2b8e315c2fae43b554511a0d566b211c50e73161 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Mon, 30 Jul 2018 16:50:06 +0300 Subject: [PATCH 0610/1171] MAGETWO-91335: Manufacturer attribute not appearing in Configurable Products on the backend --- .../InstallInitialConfigurableAttributes.php | 1 + .../Data/UpdateManufacturerAttribute.php | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 app/code/Magento/ConfigurableProduct/Setup/Patch/Data/UpdateManufacturerAttribute.php diff --git a/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/InstallInitialConfigurableAttributes.php b/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/InstallInitialConfigurableAttributes.php index c9fea3e74d3d2..f69d8529fb801 100644 --- a/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/InstallInitialConfigurableAttributes.php +++ b/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/InstallInitialConfigurableAttributes.php @@ -51,6 +51,7 @@ public function apply() $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]); $attributes = [ 'country_of_manufacture', + 'manufacturer', 'minimal_price', 'msrp', 'msrp_display_actual_price_type', diff --git a/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/UpdateManufacturerAttribute.php b/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/UpdateManufacturerAttribute.php new file mode 100644 index 0000000000000..e27a70153d5e1 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/UpdateManufacturerAttribute.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\ConfigurableProduct\Setup\Patch\Data; + +use Magento\Eav\Setup\EavSetup; +use Magento\Eav\Setup\EavSetupFactory; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; + +/** + * Class UpdateManufacturerAttribute + * @package Magento\ConfigurableProduct\Setup\Patch + */ +class UpdateManufacturerAttribute implements DataPatchInterface, PatchVersionInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var EavSetupFactory + */ + private $eavSetupFactory; + + /** + * UpdateTierPriceAttribute constructor. + * @param ModuleDataSetupInterface $moduleDataSetup + * @param EavSetupFactory $eavSetupFactory + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + EavSetupFactory $eavSetupFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->eavSetupFactory = $eavSetupFactory; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + /** @var EavSetup $eavSetup */ + $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]); + $relatedProductTypes = explode( + ',', + $eavSetup->getAttribute(\Magento\Catalog\Model\Product::ENTITY, 'manufacturer', 'apply_to') + ); + $key = array_search(Configurable::TYPE_CODE, $relatedProductTypes); + if ($key !== false) { + unset($relatedProductTypes[$key]); + $eavSetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'manufacturer', + 'apply_to', + implode(',', $relatedProductTypes) + ); + } + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + InstallInitialConfigurableAttributes::class, + ]; + } + + /** + * {@inheritdoc}\ + */ + public static function getVersion() + { + return '2.2.1'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} From 13a57e7caa2b0281296f51c163e4a85d05e9aab7 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Mon, 30 Jul 2018 16:55:53 +0300 Subject: [PATCH 0611/1171] MAGETWO-93718: [2.3] Unable to log in when guest checkout is disabled and CAPTCHA enabled --- .../Model/Customer/Plugin/AjaxLogin.php | 43 +++++++----- .../Magento/Captcha/Model/DefaultModel.php | 16 +++-- .../Model/Customer/Plugin/AjaxLoginTest.php | 68 +++++++++++++++---- 3 files changed, 90 insertions(+), 37 deletions(-) diff --git a/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php b/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php index e496c36f4de75..91f3a785df36b 100644 --- a/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php +++ b/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Captcha\Model\Customer\Plugin; use Magento\Captcha\Helper\Data as CaptchaHelper; @@ -81,27 +82,37 @@ public function aroundExecute( if ($content) { $loginParams = $this->serializer->unserialize($content); } - $username = isset($loginParams['username']) ? $loginParams['username'] : null; - $captchaString = isset($loginParams[$captchaInputName]) ? $loginParams[$captchaInputName] : null; - $loginFormId = isset($loginParams[$captchaFormIdField]) ? $loginParams[$captchaFormIdField] : null; + $username = $loginParams['username'] ?? null; + $captchaString = $loginParams[$captchaInputName] ?? null; + $loginFormId = $loginParams[$captchaFormIdField] ?? null; - foreach ($this->formIds as $formId) { - $captchaModel = $this->helper->getCaptcha($formId); - if ($captchaModel->isRequired($username) && !in_array($loginFormId, $this->formIds)) { - $resultJson = $this->resultJsonFactory->create(); - return $resultJson->setData(['errors' => true, 'message' => __('Provided form does not exist')]); - } + if (!in_array($loginFormId, $this->formIds) && $this->helper->getCaptcha($loginFormId)->isRequired($username)) { + return $this->returnJsonError(__('Provided form does not exist')); + } - if ($formId == $loginFormId) { - $captchaModel->logAttempt($username); - if (!$captchaModel->isCorrect($captchaString)) { - $this->sessionManager->setUsername($username); - /** @var \Magento\Framework\Controller\Result\Json $resultJson */ - $resultJson = $this->resultJsonFactory->create(); - return $resultJson->setData(['errors' => true, 'message' => __('Incorrect CAPTCHA')]); + foreach ($this->formIds as $formId) { + if ($formId === $loginFormId) { + $captchaModel = $this->helper->getCaptcha($formId); + if ($captchaModel->isRequired($username)) { + $captchaModel->logAttempt($username); + if (!$captchaModel->isCorrect($captchaString)) { + $this->sessionManager->setUsername($username); + return $this->returnJsonError(__('Incorrect CAPTCHA')); + } } } } return $proceed(); } + + /** + * + * @param \Magento\Framework\Phrase $phrase + * @return \Magento\Framework\Controller\Result\Json + */ + private function returnJsonError(\Magento\Framework\Phrase $phrase): \Magento\Framework\Controller\Result\Json + { + $resultJson = $this->resultJsonFactory->create(); + return $resultJson->setData(['errors' => true, 'message' => $phrase]); + } } diff --git a/app/code/Magento/Captcha/Model/DefaultModel.php b/app/code/Magento/Captcha/Model/DefaultModel.php index e5c72ba1ae82e..cf6690df5c85d 100644 --- a/app/code/Magento/Captcha/Model/DefaultModel.php +++ b/app/code/Magento/Captcha/Model/DefaultModel.php @@ -5,6 +5,8 @@ */ namespace Magento\Captcha\Model; +use Magento\Captcha\Helper\Data; + /** * Implementation of \Zend\Captcha\Image * @@ -29,7 +31,7 @@ class DefaultModel extends \Zend\Captcha\Image implements \Magento\Captcha\Model const DEFAULT_WORD_LENGTH_TO = 5; /** - * @var \Magento\Captcha\Helper\Data + * @var Data * @since 100.2.0 */ protected $captchaData; @@ -125,8 +127,8 @@ public function getBlockName() */ public function isRequired($login = null) { - if ($this->isUserAuth() - && !$this->isShownToLoggedInUser() + if (($this->isUserAuth() + && !$this->isShownToLoggedInUser()) || !$this->isEnabled() || !in_array( $this->formId, @@ -431,12 +433,14 @@ public function getWordLen() */ private function isShowAlways() { - if ((string)$this->captchaData->getConfig('mode') == \Magento\Captcha\Helper\Data::MODE_ALWAYS) { + $captchaMode = (string)$this->captchaData->getConfig('mode'); + + if ($captchaMode === Data::MODE_ALWAYS) { return true; } - if ((string)$this->captchaData->getConfig('mode') == \Magento\Captcha\Helper\Data::MODE_AFTER_FAIL - && $this->getAllowedAttemptsForSameLogin() == 0 + if ($captchaMode === Data::MODE_AFTER_FAIL + && $this->getAllowedAttemptsForSameLogin() === 0 ) { return true; } diff --git a/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php b/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php index be9574fff2cfa..bda0d9705d3df 100644 --- a/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php @@ -3,22 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Captcha\Test\Unit\Model\Customer\Plugin; class AjaxLoginTest extends \PHPUnit\Framework\TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Checkout\Model\Session */ protected $sessionManagerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Captcha\Helper\Data */ protected $captchaHelperMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Controller\Result\JsonFactory */ protected $jsonFactoryMock; @@ -38,12 +39,12 @@ class AjaxLoginTest extends \PHPUnit\Framework\TestCase protected $requestMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Customer\Controller\Ajax\Login */ protected $loginControllerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Serialize\Serializer\Json */ protected $serializerMock; @@ -72,8 +73,12 @@ protected function setUp() $this->loginControllerMock->expects($this->any())->method('getRequest') ->will($this->returnValue($this->requestMock)); - $this->captchaHelperMock->expects($this->once())->method('getCaptcha') - ->with('user_login')->will($this->returnValue($this->captchaMock)); + + $this->captchaHelperMock + ->expects($this->exactly(1)) + ->method('getCaptcha') + ->will($this->returnValue($this->captchaMock)); + $this->formIds = ['user_login']; $this->serializerMock = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class); @@ -103,11 +108,18 @@ 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)); + $this->serializerMock->expects($this->once())->method('unserialize')->will($this->returnValue($requestData)); $closure = function () { return 'result'; }; + + $this->captchaHelperMock + ->expects($this->exactly(1)) + ->method('getCaptcha') + ->with('user_login') + ->will($this->returnValue($this->captchaMock)); + $this->assertEquals('result', $this->model->aroundExecute($this->loginControllerMock, $closure)); } @@ -128,18 +140,21 @@ 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->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') ->will($this->returnValue($this->resultJsonMock)); - $this->resultJsonMock->expects($this->once())->method('setData') - ->with(['errors' => true, 'message' => __('Incorrect CAPTCHA')])->will($this->returnValue('response')); + $this->resultJsonMock + ->expects($this->once()) + ->method('setData') + ->with(['errors' => true, 'message' => __('Incorrect CAPTCHA')]) + ->will($this->returnSelf()); $closure = function () { }; - $this->assertEquals('response', $this->model->aroundExecute($this->loginControllerMock, $closure)); + $this->assertEquals($this->resultJsonMock, $this->model->aroundExecute($this->loginControllerMock, $closure)); } /** @@ -151,7 +166,7 @@ public function testAroundExecuteCaptchaIsNotRequired($username, $requestContent { $this->requestMock->expects($this->once())->method('getContent') ->will($this->returnValue(json_encode($requestContent))); - $this->serializerMock->expects(($this->once()))->method('unserialize') + $this->serializerMock->expects($this->once())->method('unserialize') ->will($this->returnValue($requestContent)); $this->captchaMock->expects($this->once())->method('isRequired')->with($username) @@ -168,16 +183,39 @@ public function testAroundExecuteCaptchaIsNotRequired($username, $requestContent /** * @return array */ - public function aroundExecuteCaptchaIsNotRequired() + public function aroundExecuteCaptchaIsNotRequired(): array { return [ [ 'username' => 'name', 'requestData' => ['username' => 'name', 'captcha_string' => 'string'], ], + [ + 'username' => 'name', + 'requestData' => + [ + 'username' => 'name', + 'captcha_string' => 'string', + 'captcha_form_id' => $this->formIds[0] + ], + ], [ 'username' => null, - 'requestData' => ['captcha_string' => 'string'], + 'requestData' => + [ + 'username' => null, + 'captcha_string' => 'string', + 'captcha_form_id' => $this->formIds[0] + ], + ], + [ + 'username' => 'name', + 'requestData' => + [ + 'username' => 'name', + 'captcha_string' => 'string', + 'captcha_form_id' => null + ], ], ]; } From e273715274df47faac106cc819dec92b7a95b66b Mon Sep 17 00:00:00 2001 From: KevinBKozan <kkozan@magento.com> Date: Mon, 30 Jul 2018 09:38:04 -0500 Subject: [PATCH 0612/1171] MQE-1112: Bump MFTF version in Magento - fix flaky mftf test --- ...ddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml | 1 + .../Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml | 1 + app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml | 1 + 3 files changed, 3 insertions(+) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml index 4b8d48a84073a..6cc625b1f5edc 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml @@ -45,6 +45,7 @@ <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{CustomerEntityOne.lastname}}" stepKey="enterLastName"/> <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{UK_Address.street[0]}}" stepKey="enterStreet"/> <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{UK_Address.city}}" stepKey="enterCity"/> + <fillField selector="{{CheckoutShippingSection.stateInput}}" userInput="{{UK_Address.province}}" stepKey="enterProvince"/> <waitForPageLoad stepKey="waitFormToReload2"/> <see userInput="State/Province" stepKey="StateFieldStillExists"/> <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{UK_Address.telephone}}" stepKey="enterTelephone"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml index c88f6cec4bcdb..adaf5cda00b3d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml @@ -60,6 +60,7 @@ <amOnPage stepKey="s67" url="{{AdminOrdersPage.url}}"/> <waitForPageLoad stepKey="s75"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFiltersIfPresent"/> <fillField stepKey="s77" selector="{{AdminOrdersGridSection.search}}" userInput="{$s53}" /> <waitForPageLoad stepKey="s78"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index d26ecac28113e..27a0c76f04010 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -92,5 +92,6 @@ <data key="city">London</data> <data key="country_id">GB</data> <data key="telephone">512-345-6789</data> + <data key="province">JS</data> </entity> </entities> From 49979e380f62545f04b733ca1ce4c1e61eebce91 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Mon, 30 Jul 2018 17:43:56 +0300 Subject: [PATCH 0613/1171] MAGETWO-91811: Mini Cart is not getting updated while removing product from mini cart in IE11 --- .../Checkout/view/frontend/web/js/sidebar.js | 9 ++- .../view/configure/product-customer-data.js | 5 +- .../Checkout/frontend/js/sidebar.test.js | 78 +++++++++++++++++++ 3 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js diff --git a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js index 3fb8743e951c8..a978f273520fe 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js @@ -9,11 +9,12 @@ define([ 'Magento_Customer/js/customer-data', 'Magento_Ui/js/modal/alert', 'Magento_Ui/js/modal/confirm', + 'underscore', 'jquery/ui', 'mage/decorate', 'mage/collapsible', 'mage/cookies' -], function ($, authenticationPopup, customerData, alert, confirm) { +], function ($, authenticationPopup, customerData, alert, confirm, _) { 'use strict'; $.widget('mage.sidebar', { @@ -242,11 +243,13 @@ define([ * @private */ _removeItemAfter: function (elem) { - var productData = customerData.get('cart')().items.find(function (item) { + var productData = _.find(customerData.get('cart')().items, function (item) { return Number(elem.data('cart-item')) === Number(item['item_id']); }); - $(document).trigger('ajax:removeFromCart', productData['product_sku']); + if (!_.isUndefined(productData)) { + $(document).trigger('ajax:removeFromCart', productData['product_sku']); + } }, /** diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/configure/product-customer-data.js b/app/code/Magento/Checkout/view/frontend/web/js/view/configure/product-customer-data.js index 9af5201c267e8..0e2fc6bbfc8fe 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/configure/product-customer-data.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/configure/product-customer-data.js @@ -1,8 +1,9 @@ require([ 'jquery', 'Magento_Customer/js/customer-data', + 'underscore', 'domReady!' -], function ($, customerData) { +], function ($, customerData, _) { 'use strict'; var selectors = { @@ -41,7 +42,7 @@ require([ if (!(data && data.items && data.items.length && productId)) { return; } - product = data.items.find(function (item) { + product = _.find(data.items, function (item) { if (item['item_id'] === itemId) { return item['product_id'] === productId || item['item_id'] === productId; diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js new file mode 100644 index 0000000000000..64ae45145b7f4 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js @@ -0,0 +1,78 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +/* eslint-disable max-nested-callbacks */ +/*jscs:disable jsDoc*/ + +define([ + 'squire', + 'jquery', + 'ko' +], function (Squire, $, ko) { + 'use strict'; + + describe('Magento_Checkout/js/sidebar', function () { + var injector = new Squire(), + mocks = { + 'Magento_Customer/js/customer-data': { + get: function () { + return ko.observable(); + } + } + }, + sidebar; + + beforeEach(function (done) { + injector.mock(mocks); + injector.require(['Magento_Checkout/js/sidebar'], function (Constr) { + sidebar = new Constr; + done(); + }); + }); + + describe('Check remove mini-cart item callback.', function () { + var cartData = { + 'items': [ + { + 'item_id': 1, + 'product_sku': 'bundle' + }, + { + 'item_id': 5, + 'product_sku': 'simple' + }, + { + 'item_id': 7, + 'product_sku': 'configurable' + } + ] + }, + cart = ko.observable(cartData); + + beforeEach(function () { + spyOn(jQuery.fn, 'trigger'); + spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue(cart); + }); + + it('Method "_removeItemAfter" is defined', function () { + expect(sidebar._removeItemAfter).toBeDefined(); + }); + + it('Cart item is exists', function () { + var elem = $('<input>').data('cart-item', 5); + + sidebar._removeItemAfter(elem); + expect(mocks['Magento_Customer/js/customer-data'].get).toHaveBeenCalledWith('cart'); + expect(jQuery('body').trigger).toHaveBeenCalledWith('ajax:removeFromCart', 'simple'); + }); + + it('Cart item doesn\'t exists', function () { + var elem = $('<input>').data('cart-item', 100); + + sidebar._removeItemAfter(elem); + expect(jQuery('body').trigger).not.toHaveBeenCalled(); + }); + }); + }); +}); From ca94fb0ae16f818dc1ab9fff3b71e5ba8041b3fa Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Thu, 17 May 2018 14:40:11 +0300 Subject: [PATCH 0614/1171] MAGETWO-91334: [Corra Technology]Customer Custom attribute-Default value is not showing in Order page. --- .../Metadata/AttributeMetadataHydrator.php | 3 +- .../AttributeMetadataHydratorTest.php | 2 +- .../Adminhtml/Order/Create/Form/Account.php | 10 ++- .../Order/Create/Form/AccountTest.php | 87 ++++++++++++++++--- 4 files changed, 87 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Customer/Model/Metadata/AttributeMetadataHydrator.php b/app/code/Magento/Customer/Model/Metadata/AttributeMetadataHydrator.php index 190a3a38e0bf0..f61f064e3f97e 100644 --- a/app/code/Magento/Customer/Model/Metadata/AttributeMetadataHydrator.php +++ b/app/code/Magento/Customer/Model/Metadata/AttributeMetadataHydrator.php @@ -11,6 +11,7 @@ use Magento\Customer\Api\Data\OptionInterfaceFactory; use Magento\Customer\Api\Data\ValidationRuleInterface; use Magento\Customer\Api\Data\ValidationRuleInterfaceFactory; +use Magento\Customer\Model\Data\AttributeMetadata; use Magento\Framework\Reflection\DataObjectProcessor; /** @@ -120,7 +121,7 @@ public function extract($attributeMetadata) { return $this->dataObjectProcessor->buildOutputDataArray( $attributeMetadata, - AttributeMetadataInterface::class + AttributeMetadata::class ); } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataHydratorTest.php b/app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataHydratorTest.php index ec9831dde081e..248cae999065c 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataHydratorTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataHydratorTest.php @@ -205,7 +205,7 @@ public function testExtract() ->method('buildOutputDataArray') ->with( $this->attributeMetadataMock, - AttributeMetadataInterface::class + AttributeMetadata::class ) ->willReturn($data); $this->assertSame( diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php index 3623e72341a22..26379a05fe694 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -132,7 +132,15 @@ protected function _prepareForm() $this->_addAttributesToForm($attributes, $fieldset); $this->_form->addFieldNameSuffix('order[account]'); - $this->_form->setValues($this->getFormValues()); + + $formValues = $this->getFormValues(); + foreach ($attributes as $code => $attribute) { + $defaultValue = $attribute->getDefaultValue(); + if (isset($defaultValue) && !isset($formValues[$code])) { + $formValues[$code] = $defaultValue; + } + } + $this->_form->setValues($formValues); return $this; } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php index 6b4e9a66f4c6b..999522a49e006 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php @@ -5,39 +5,50 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Sales\Block\Adminhtml\Order\Create\Form; +use Magento\Backend\Model\Session\Quote as SessionQuote; +use Magento\Customer\Api\Data\AttributeMetadataInterfaceFactory; +use Magento\Customer\Model\Metadata\Form; +use Magento\Customer\Model\Metadata\FormFactory; +use Magento\Framework\View\LayoutInterface; +use Magento\Quote\Model\Quote; +use Magento\TestFramework\Helper\Bootstrap; + /** * @magentoAppArea adminhtml */ class AccountTest extends \PHPUnit\Framework\TestCase { - /** @var \Magento\Sales\Block\Adminhtml\Order\Create\Form\Account */ - protected $_accountBlock; + /** @var Account */ + private $accountBlock; /** - * @var \Magento\TestFramework\Helper\Bootstrap + * @var Bootstrap */ - protected $_objectManager; + private $objectManager; /** * @magentoDataFixture Magento/Sales/_files/quote.php */ protected function setUp() { - $this->_objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $quote = $this->_objectManager->create(\Magento\Quote\Model\Quote::class)->load(1); + $this->objectManager = Bootstrap::getObjectManager(); + $quote = $this->objectManager->create(Quote::class)->load(1); $sessionQuoteMock = $this->getMockBuilder( - \Magento\Backend\Model\Session\Quote::class + SessionQuote::class )->disableOriginalConstructor()->setMethods( ['getCustomerId', 'getStore', 'getStoreId', 'getQuote'] )->getMock(); $sessionQuoteMock->expects($this->any())->method('getCustomerId')->will($this->returnValue(1)); $sessionQuoteMock->expects($this->any())->method('getQuote')->will($this->returnValue($quote)); - /** @var \Magento\Framework\View\LayoutInterface $layout */ - $layout = $this->_objectManager->get(\Magento\Framework\View\LayoutInterface::class); - $this->_accountBlock = $layout->createBlock( - \Magento\Sales\Block\Adminhtml\Order\Create\Form\Account::class, + /** @var LayoutInterface $layout */ + $layout = $this->objectManager->get(LayoutInterface::class); + $this->accountBlock = $layout->createBlock( + Account::class, 'address_block' . rand(), ['sessionQuote' => $sessionQuoteMock] ); @@ -50,7 +61,7 @@ protected function setUp() public function testGetForm() { $expectedFields = ['group_id', 'email']; - $form = $this->_accountBlock->getForm(); + $form = $this->accountBlock->getForm(); $this->assertEquals(1, $form->getElements()->count(), "Form has invalid number of fieldsets"); $fieldset = $form->getElements()[0]; @@ -63,4 +74,56 @@ public function testGetForm() ); } } + + /** + * Tests a case when user defined custom attribute has default value. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + */ + public function testGetFormWithUserDefinedAttribute() + { + $formFactory = $this->getFormFactoryMock(); + $this->objectManager->addSharedInstance($formFactory, FormFactory::class); + + /** @var LayoutInterface $layout */ + $layout = $this->objectManager->get(LayoutInterface::class); + $accountBlock = $layout->createBlock(Account::class, 'address_block' . rand()); + + $form = $accountBlock->getForm(); + $form->setUseContainer(true); + + $this->assertContains( + '<option value="1" selected="selected">Yes</option>', + $form->toHtml(), + 'Default value for user defined custom attribute should be selected' + ); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getFormFactoryMock(): \PHPUnit_Framework_MockObject_MockObject + { + /** @var AttributeMetadataInterfaceFactory $attributeMetadataFactory */ + $attributeMetadataFactory = $this->objectManager->create(AttributeMetadataInterfaceFactory::class); + $booleanAttribute = $attributeMetadataFactory->create() + ->setAttributeCode('boolean') + ->setBackendType('boolean') + ->setFrontendInput('boolean') + ->setDefaultValue('1') + ->setFrontendLabel('Yes/No'); + + $form = $this->getMockBuilder(Form::class) + ->disableOriginalConstructor() + ->getMock(); + $form->method('getUserAttributes')->willReturn([$booleanAttribute]); + $form->method('getSystemAttributes')->willReturn([]); + + $formFactory = $this->getMockBuilder(FormFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $formFactory->method('create')->willReturn($form); + + return $formFactory; + } } From 67db61ea10c8ae1a502d1f0f255c2f365a7ec7bd Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Mon, 21 May 2018 19:03:21 +0300 Subject: [PATCH 0615/1171] MAGETWO-91340: Google Analytics - addToCart event triggers before adding to cart --- .../Checkout/view/frontend/web/js/sidebar.js | 23 +++++-- .../Checkout/frontend/js/sidebar.test.js | 69 ++++++++++++++----- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js index a978f273520fe..debb1acf90d4f 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js @@ -220,8 +220,12 @@ define([ * @param {HTMLElement} elem */ _updateItemQtyAfter: function (elem) { + var productData = this._getProductById(Number(elem.data('cart-item'))); + + if (!_.isUndefined(productData)) { + $(document).trigger('ajax:updateCartItemQty', productData['product_sku']); + } this._hideItemButton(elem); - $(document).trigger('ajax:updateItemQty'); }, /** @@ -243,15 +247,26 @@ define([ * @private */ _removeItemAfter: function (elem) { - var productData = _.find(customerData.get('cart')().items, function (item) { - return Number(elem.data('cart-item')) === Number(item['item_id']); - }); + var productData = this._getProductById(Number(elem.data('cart-item'))); if (!_.isUndefined(productData)) { $(document).trigger('ajax:removeFromCart', productData['product_sku']); } }, + /** + * Retrieves product data by Id. + * + * @param {Number} productId - product Id + * @returns {Object|undefined} + * @private + */ + _getProductById: function (productId) { + return _.find(customerData.get('cart')().items, function (item) { + return productId === Number(item['item_id']); + }); + }, + /** * @param {String} url - ajax url * @param {Object} data - post data for ajax call diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js index 64ae45145b7f4..8b4fae5a69461 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js @@ -21,7 +21,24 @@ define([ } } }, - sidebar; + sidebar, + cartData = { + 'items': [ + { + 'item_id': 1, + 'product_sku': 'bundle' + }, + { + 'item_id': 5, + 'product_sku': 'simple' + }, + { + 'item_id': 7, + 'product_sku': 'configurable' + } + ] + }, + cart = ko.observable(cartData); beforeEach(function (done) { injector.mock(mocks); @@ -32,24 +49,6 @@ define([ }); describe('Check remove mini-cart item callback.', function () { - var cartData = { - 'items': [ - { - 'item_id': 1, - 'product_sku': 'bundle' - }, - { - 'item_id': 5, - 'product_sku': 'simple' - }, - { - 'item_id': 7, - 'product_sku': 'configurable' - } - ] - }, - cart = ko.observable(cartData); - beforeEach(function () { spyOn(jQuery.fn, 'trigger'); spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue(cart); @@ -74,5 +73,37 @@ define([ expect(jQuery('body').trigger).not.toHaveBeenCalled(); }); }); + + describe('Check update item quantity callback.', function () { + beforeEach(function () { + spyOn(jQuery.fn, 'trigger'); + spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue(cart); + }); + + it('Method "_updateItemQtyAfter" is defined', function () { + expect(sidebar._updateItemQtyAfter).toBeDefined(); + }); + + it('Cart item is exists', function () { + var elem = $('<input>').data('cart-item', 5); + + spyOn(sidebar, '_hideItemButton'); + + sidebar._updateItemQtyAfter(elem); + expect(mocks['Magento_Customer/js/customer-data'].get).toHaveBeenCalledWith('cart'); + expect(jQuery('body').trigger).toHaveBeenCalledWith('ajax:updateCartItemQty', 'simple'); + expect(sidebar._hideItemButton).toHaveBeenCalledWith(elem); + }); + + it('Cart item doesn\'t exists', function () { + var elem = $('<input>').data('cart-item', 100); + + spyOn(sidebar, '_hideItemButton'); + + sidebar._updateItemQtyAfter(elem); + expect(jQuery('body').trigger).not.toHaveBeenCalled(); + expect(sidebar._hideItemButton).toHaveBeenCalledWith(elem); + }); + }); }); }); From a1776ace2f3291f958914b99e6328bbb5040c38d Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Mon, 30 Jul 2018 17:33:26 +0300 Subject: [PATCH 0616/1171] MAGETWO-91622: Error on RMA shipping label creation for FedEx Smart Post - Fixed incorrect payment type for FedEx Smart Post Return request - Added possibility to parse multiple tracking codes for one Return request --- app/code/Magento/Fedex/Model/Carrier.php | 32 ++++++++++++++++--- .../Magento/Sales/_files/address_data.php | 1 + 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Fedex/Model/Carrier.php b/app/code/Magento/Fedex/Model/Carrier.php index 8841cdd4f9a8b..f6e63e04ac559 100644 --- a/app/code/Magento/Fedex/Model/Carrier.php +++ b/app/code/Magento/Fedex/Model/Carrier.php @@ -7,8 +7,10 @@ namespace Magento\Fedex\Model; use Magento\Framework\App\ObjectManager; +use Magento\Framework\DataObject; use Magento\Framework\Module\Dir; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Webapi\Soap\ClientFactory; use Magento\Framework\Xml\Security; use Magento\Quote\Model\Quote\Address\RateRequest; use Magento\Shipping\Model\Carrier\AbstractCarrierOnline; @@ -17,7 +19,6 @@ /** * Fedex shipping implementation * - * @author Magento Core Team <core@magentocommerce.com> * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -143,6 +144,11 @@ class Carrier extends AbstractCarrierOnline implements \Magento\Shipping\Model\C */ private $serializer; + /** + * @var ClientFactory + */ + private $soapClientFactory; + /** * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory @@ -164,7 +170,7 @@ class Carrier extends AbstractCarrierOnline implements \Magento\Shipping\Model\C * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory * @param array $data * @param Json|null $serializer - * + * @param ClientFactory|null $soapClientFactory * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -187,7 +193,8 @@ public function __construct( \Magento\Framework\Module\Dir\Reader $configReader, \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory, array $data = [], - Json $serializer = null + Json $serializer = null, + ClientFactory $soapClientFactory = null ) { $this->_storeManager = $storeManager; $this->_productCollectionFactory = $productCollectionFactory; @@ -214,6 +221,7 @@ public function __construct( $this->_rateServiceWsdl = $wsdlBasePath . 'RateService_v10.wsdl'; $this->_trackServiceWsdl = $wsdlBasePath . 'TrackService_v' . self::$trackServiceVersion . '.wsdl'; $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); + $this->soapClientFactory = $soapClientFactory ?: ObjectManager::getInstance()->get(ClientFactory::class); } /** @@ -225,7 +233,7 @@ public function __construct( */ protected function _createSoapClient($wsdl, $trace = false) { - $client = new \SoapClient($wsdl, ['trace' => $trace]); + $client = $this->soapClientFactory->create($wsdl, ['trace' => $trace]); $client->__setLocation( $this->getConfigFlag( 'sandbox_mode' @@ -1263,7 +1271,7 @@ protected function _formShipmentRequest(\Magento\Framework\DataObject $request) $countriesOfManufacture[] = $product->getCountryOfManufacture(); } - $paymentType = $request->getIsReturn() ? 'RECIPIENT' : 'SENDER'; + $paymentType = $this->getPaymentType($request); $optionType = $request->getShippingMethod() == self::RATE_REQUEST_SMARTPOST ? 'SERVICE_DEFAULT' : $packageParams->getDeliveryConfirmation(); $requestClient = [ @@ -1749,4 +1757,18 @@ private function parseDate($timestamp) return false; } + + /** + * Defines payment type by request. + * Two values are available: RECIPIENT or SENDER. + * + * @param DataObject $request + * @return string + */ + private function getPaymentType(DataObject $request): string + { + return $request->getIsReturn() && $request->getShippingMethod() !== self::RATE_REQUEST_SMARTPOST + ? 'RECIPIENT' + : 'SENDER'; + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/address_data.php b/dev/tests/integration/testsuite/Magento/Sales/_files/address_data.php index 2d131bf5f57e2..394b13078010a 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/address_data.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/address_data.php @@ -6,6 +6,7 @@ return [ 'region' => 'CA', + 'region_id' => '12', 'postcode' => '11111', 'lastname' => 'lastname', 'firstname' => 'firstname', From 380e29d4fd204630b7a975c3320d712041232c27 Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Mon, 30 Jul 2018 18:12:03 +0300 Subject: [PATCH 0617/1171] MAGETWO-91802: Refund amounts are off when Rewards, Store Credit and Gift Card used - Added math rounding for Reward Points balance - Fixed Gift Card account order comments history - Fixed Gift Card totals block - Added possibility to update totals for changed Store Credit during refund --- .../Test/Block/Adminhtml/Order/Creditmemo/Form/Items.php | 5 ++++- .../Sales/Test/Constraint/AssertNoCreditMemoButton.php | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Creditmemo/Form/Items.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Creditmemo/Form/Items.php index 74d522e9545a8..431116903d372 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Creditmemo/Form/Items.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Creditmemo/Form/Items.php @@ -51,6 +51,9 @@ public function getItemProductBlock($productSku) */ public function clickUpdateQty() { - $this->_rootElement->find($this->updateQty)->click(); + $button = $this->_rootElement->find($this->updateQty); + if (!$button->isDisabled()) { + $button->click(); + } } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertNoCreditMemoButton.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertNoCreditMemoButton.php index 22ba984b489e2..c9bdbac4aa7ec 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertNoCreditMemoButton.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertNoCreditMemoButton.php @@ -30,7 +30,7 @@ public function processAssert(SalesOrderView $salesOrderView, OrderIndex $orderI $orderIndex->getSalesOrderGrid()->searchAndOpen(['id' => $order->getId()]); \PHPUnit\Framework\Assert::assertFalse( $salesOrderView->getPageActions()->isActionButtonVisible('Credit Memo'), - 'Credit memo button is present on order view page.' + 'Credit memo button should not be present on order view page.' ); } From 97a1255f88cb5df92c16eaf17068d6cfaac1fd1e Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Mon, 30 Jul 2018 18:49:12 +0300 Subject: [PATCH 0618/1171] MAGETWO-91512: Free Shipping Cart Price Rule not working when UPS shipping method is enabled --- .../Model/Carrier/AbstractCarrier.php | 6 ++++ .../Magento/Ups/Model/CarrierTest.php | 32 ++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php index 6763046373ce7..824f0c28a3cf2 100644 --- a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php +++ b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php @@ -448,6 +448,12 @@ protected function _updateFreeMethodQuote($request) } } } + } else { + /** + * if we can apply free shipping for all order we should force price + * to $0.00 for shipping with out sending second request to carrier + */ + $price = 0; } /** diff --git a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php index a9f6752c9de82..66df6b0f7e60c 100644 --- a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php +++ b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php @@ -5,18 +5,19 @@ */ namespace Magento\Ups\Model; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Quote\Model\Quote\Address\RateRequestFactory; + class CarrierTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Ups\Model\Carrier + * @var Carrier */ private $carrier; protected function setUp() { - $this->carrier = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Ups\Model\Carrier::class - ); + $this->carrier = Bootstrap::getObjectManager()->create(Carrier::class); } public function testGetShipAcceptUrl() @@ -51,4 +52,27 @@ public function testGetShipConfirmUrlLive() $this->carrier->getShipConfirmUrl() ); } + + /** + * @magentoConfigFixture current_store carriers/ups/active 1 + * @magentoConfigFixture current_store carriers/ups/allowed_methods 1DA,GND + * @magentoConfigFixture current_store carriers/ups/free_method GND + */ + public function testCollectRates() + { + $rateRequest = Bootstrap::getObjectManager()->get(RateRequestFactory::class)->create(); + $rateRequest->setDestCountryId('US'); + $rateRequest->setDestRegionId('CA'); + $rateRequest->setDestPostcode('90001'); + $rateRequest->setPackageQty(1); + $rateRequest->setPackageWeight(1); + $rateRequest->setFreeMethodWeight(0); + $rateRequest->setLimitCarrier($this->carrier::CODE); + + $rateResult = $this->carrier->collectRates($rateRequest); + $result = $rateResult->asArray(); + $methods = $result[$this->carrier::CODE]['methods']; + $this->assertEquals(0, $methods['GND']['price']); + $this->assertNotEquals(0, $methods['1DA']['price']); + } } From 2e270803a26a119059d7a08ff45896f271a8b2b9 Mon Sep 17 00:00:00 2001 From: Ji Lu <> Date: Mon, 30 Jul 2018 10:58:30 -0500 Subject: [PATCH 0619/1171] MC-111: Admin should be able to add default video for simple products MC-206: Admin should be able to remove default video for simple products - Updated mftf tests --- .../Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml | 1 + .../Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml index c0bc3bfc127f3..623a2ebadbfec 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml @@ -20,6 +20,7 @@ </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="ConfigAdminAccountSharingActionGroup" stepKey="allowAdminShareAccount"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml index 6ec562322824d..fa564c4bbb474 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml @@ -20,6 +20,7 @@ </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="ConfigAdminAccountSharingActionGroup" stepKey="allowAdminShareAccount"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> From 9023fc9cf19a44271488cbdead249c132befd9da Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Mon, 30 Jul 2018 13:18:29 -0500 Subject: [PATCH 0620/1171] MC-3442: Directive link displays on stage Fix regex for encodeDirectives --- app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js | 2 +- lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js b/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js index 88e2d4cc78f96..7c587c1c8768a 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js +++ b/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js @@ -495,7 +495,7 @@ define([ */ encodeDirectives: function (content) { // collect all HTML tags with attributes that contain directives - return content.gsub(/<([a-z0-9\-\_]+[^>]+?)([a-z0-9\-\_]+=".*?\{\{.+?\}\}.*?".*?)>/i, function (match) { + return content.gsub(/<([a-z0-9\-\_]+[^>]+?)([a-z0-9\-\_]+="[^"]*?\{\{.+?\}\}.*?".*?)>/i, function (match) { var attributesString = match[2], decodedDirectiveString; diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index f824219a2d58d..398199d730c71 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -618,7 +618,7 @@ define([ */ encodeDirectives: function (content) { // collect all HTML tags with attributes that contain directives - return content.gsub(/<([a-z0-9\-\_]+[^>]+?)([a-z0-9\-\_]+=".*?\{\{.+?\}\}.*?".*?)>/i, function (match) { + return content.gsub(/<([a-z0-9\-\_]+[^>]+?)([a-z0-9\-\_]+="[^"]*?\{\{.+?\}\}.*?".*?)>/i, function (match) { var attributesString = match[2], decodedDirectiveString; From 1a353b2a55d8ee053c27b9334412989310112ba4 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Mon, 30 Jul 2018 13:44:06 -0500 Subject: [PATCH 0621/1171] MC-1416: Placeholder of deleted variable should apply same style as placeholder in WYSIWYG instances itself Add box-sizing initial --- lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css index 274dcc124a3a6..de634880f765a 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css @@ -15,6 +15,7 @@ font-style: normal; font-size: 14px; color: #000; + box-sizing: initial; } .magento-placeholder-error { From b63af7377705f1ba6a530a48458dfbeddf08232a Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Mon, 30 Jul 2018 14:28:11 -0500 Subject: [PATCH 0622/1171] MAGETWO-63981: Try to save empty Magento Advanced Reporting vertical - Add waitForPageLoad to fix flakiness --- .../Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml index 8649eac484d94..fefb7874ef736 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml @@ -22,6 +22,7 @@ </after> <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> <amOnPage stepKey="amOnAdminConfig" url="{{AdminConfigPage.url}}"/> + <waitForPageLoad stepKey="waitForAdminConfig"/> <click stepKey="clickAdvancedReportingConfigMenu" selector="{{AdminConfigSection.advancedReportingMenuItem}}"/> <see stepKey="seeAdvancedReportingIndustryLabel" selector="{{AdminConfigSection.advancedReportingIndustryLabel}}" userInput="Industry"/> <selectOption stepKey="selectAdvancedReportingIndustry" selector="{{AdminConfigSection.advancedReportingIndustry}}" userInput="--Please Select--"/> From d0ef0bb0ed80fae1b8dfdd5e97d60355225852aa Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Mon, 30 Jul 2018 14:29:28 -0500 Subject: [PATCH 0623/1171] MAGETWO-91436: Custom Options are corruputed when saving product to a different website - Add some waitForPageLoad to fix flakiness --- .../Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml index 36b02f124c101..26f6eb7910fa2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml @@ -35,6 +35,7 @@ <!--Create Store view --> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForAdminSystemStorePage"/> <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> @@ -58,6 +59,7 @@ <actionGroup ref="EnableWebUrlOptions" stepKey="addStoreCodeToUrls"/> <!--Create a Simple Product with Custom Options --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToCatalogProductGrid"/> + <waitForPageLoad stepKey="waitForCatalogProductGrid"/> <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> <fillField userInput="{{_defaultProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> From 9770003d049aa6409effd15305e508bcc885ed99 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Mon, 30 Jul 2018 14:36:59 -0500 Subject: [PATCH 0624/1171] MQE-1112: Bump MFTF version in Magento --- .../Magento/Checkout/Test/TestStep/SelectCheckoutMethodStep.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/SelectCheckoutMethodStep.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/SelectCheckoutMethodStep.php index 9df49d2440568..d951d84bab78d 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/SelectCheckoutMethodStep.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/SelectCheckoutMethodStep.php @@ -91,8 +91,10 @@ public function __construct( */ public function run() { + sleep(20); $this->processLogin(); $this->processRegister(); + sleep(20); } /** From fc137adfb5b2dc3fa3c70d0736149ca023596a2f Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Mon, 30 Jul 2018 16:15:27 -0500 Subject: [PATCH 0625/1171] MQE-1112: Bump MFTF version in Magento --- dev/travis/before_script.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/travis/before_script.sh b/dev/travis/before_script.sh index f2ba20dc412b6..1dccc310c7a20 100755 --- a/dev/travis/before_script.sh +++ b/dev/travis/before_script.sh @@ -129,6 +129,7 @@ case $TEST_SUITE in sed -e "s?basic?travis_acceptance?g" --in-place ./phpunit.xml cp ./.htaccess.sample ./.htaccess cd ./utils + php -f generate/moduleSequence.php php -f mtf troubleshooting:check-all cd ../../.. From b358932812325776ca46824aaf15f8795d990984 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 26 Jun 2018 13:55:38 +0300 Subject: [PATCH 0626/1171] Remove PDF invoice after generation --- .../Invoice/AbstractInvoice/PrintAction.php | 4 +++- .../App/Response/Http/FileFactory.php | 24 +++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php index 2421267aa753d..5ae021f39bc54 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php @@ -58,9 +58,11 @@ public function execute() $date = $this->_objectManager->get( \Magento\Framework\Stdlib\DateTime\DateTime::class )->date('Y-m-d_H-i-s'); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->_fileFactory->create( 'invoice' . $date . '.pdf', - $pdf->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php b/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php index 979ea1c429590..fb4e0fc62bf44 100644 --- a/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php +++ b/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php @@ -76,7 +76,11 @@ public function create( ->setHeader('Pragma', 'public', true) ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true) ->setHeader('Content-type', $contentType, true) - ->setHeader('Content-Length', $contentLength === null ? strlen($content) : $contentLength, true) + ->setHeader( + 'Content-Length', + $contentLength === null ? strlen($this->getFileContent($content)) : $contentLength, + true + ) ->setHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"', true) ->setHeader('Last-Modified', date('r'), true); @@ -88,7 +92,8 @@ public function create( echo $stream->read(1024); } } else { - $dir->writeFile($fileName, $content); + $dir->writeFile($fileName, $this->getFileContent($content)); + $file = $fileName; $stream = $dir->openFile($fileName, 'r'); while (!$stream->eof()) { echo $stream->read(1024); @@ -102,4 +107,19 @@ public function create( } return $this->_response; } + + /** + * Returns file content for writing. + * + * @param string|array $content + * @return string + */ + private function getFileContent($content) + { + if (isset($content['type']) && $content['type'] == 'string') { + return $content['value']; + } + + return $content; + } } From d3a7b9729405842e680fd5fafb09285de09ce30b Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Mon, 2 Jul 2018 12:05:33 +0300 Subject: [PATCH 0627/1171] File content extracted to a separate var. Type hinting, strict comparison --- .../Framework/App/Response/Http/FileFactory.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php b/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php index fb4e0fc62bf44..6697e1f392ba0 100644 --- a/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php +++ b/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php @@ -59,6 +59,7 @@ public function create( $dir = $this->_filesystem->getDirectoryWrite($baseDir); $isFile = false; $file = null; + $fileContent = $this->getFileContent($content); if (is_array($content)) { if (!isset($content['type']) || !isset($content['value'])) { throw new \InvalidArgumentException("Invalid arguments. Keys 'type' and 'value' are required."); @@ -76,11 +77,7 @@ public function create( ->setHeader('Pragma', 'public', true) ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true) ->setHeader('Content-type', $contentType, true) - ->setHeader( - 'Content-Length', - $contentLength === null ? strlen($this->getFileContent($content)) : $contentLength, - true - ) + ->setHeader('Content-Length', $contentLength === null ? strlen($fileContent) : $contentLength, true) ->setHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"', true) ->setHeader('Last-Modified', date('r'), true); @@ -92,7 +89,7 @@ public function create( echo $stream->read(1024); } } else { - $dir->writeFile($fileName, $this->getFileContent($content)); + $dir->writeFile($fileName, $fileContent); $file = $fileName; $stream = $dir->openFile($fileName, 'r'); while (!$stream->eof()) { @@ -114,9 +111,9 @@ public function create( * @param string|array $content * @return string */ - private function getFileContent($content) + private function getFileContent($content): string { - if (isset($content['type']) && $content['type'] == 'string') { + if (isset($content['type']) && $content['type'] === 'string') { return $content['value']; } From 5f1e0bf52be0c106de165f304f9d6a582903e0ae Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Mon, 2 Jul 2018 13:07:12 +0300 Subject: [PATCH 0628/1171] Integration test for checking file generation and removing process --- .../App/Filesystem/CreatePdfFileTest.php | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php new file mode 100644 index 0000000000000..2f8383667d2f6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + * + */ +declare(strict_types=1); + +namespace Magento\Framework\App\Filesystem\Images; + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Framework\Filesystem; +use Magento\TestFramework\Helper\Bootstrap; +use Zend\Http\Header\ContentType; + +/** + * Class CreatePdfFileTest + * + * Integration test for testing a file creation from string + */ +class CreatePdfFileTest extends \PHPUnit\Framework\TestCase +{ + public function testGenerateFileFromString() + { + $objectManager = Bootstrap::getObjectManager(); + /** @var FileFactory $fileFactory */ + $fileFactory = $objectManager->get(FileFactory::class); + /** @var Filesystem $filesystem */ + $filesystem = $objectManager->get(Filesystem::class); + $filename = 'test.pdf'; + $contentType = 'application/pdf'; + $fileContent = ['type' => 'string', 'value' => '']; + $response = $fileFactory->create($filename, $fileContent, DirectoryList::VAR_DIR, $contentType); + /** @var ContentType $contentTypeHeader */ + $contentTypeHeader = $response->getHeader('Content-type'); + + /* Check the system returns the correct type */ + self::assertEquals("Content-Type: $contentType", $contentTypeHeader->toString()); + + $varDirectory = $filesystem->getDirectoryRead(DirectoryList::VAR_DIR); + $varDirectory->isFile($filename); + + /* Check the file is generated */ + self::assertTrue($varDirectory->isFile($filename)); + + /* Check the file is removed after generation if the corresponding option is set */ + $fileContent = ['type' => 'string', 'value' => '', 'rm' => true]; + $fileFactory->create($filename, $fileContent, DirectoryList::VAR_DIR, $contentType); + + self::assertFalse($varDirectory->isFile($filename)); + } +} From 99c5631783417a829523f647d00ed2a5838fad6e Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Mon, 2 Jul 2018 13:26:51 +0300 Subject: [PATCH 0629/1171] Changed the PDF file request in all other places --- .../Creditmemo/AbstractCreditmemo/Pdfcreditmemos.php | 5 ++++- .../Creditmemo/AbstractCreditmemo/PrintAction.php | 5 ++++- .../Adminhtml/Invoice/AbstractInvoice/Pdfinvoices.php | 5 ++++- .../Adminhtml/Invoice/AbstractInvoice/PrintAction.php | 1 + .../Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php | 6 +++++- .../Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php | 4 +++- .../Sales/Controller/Adminhtml/Order/Pdfinvoices.php | 6 +++++- .../Sales/Controller/Adminhtml/Order/Pdfshipments.php | 7 ++++++- .../Adminhtml/Shipment/AbstractShipment/Pdfshipments.php | 5 ++++- .../Adminhtml/Shipment/AbstractShipment/PrintAction.php | 5 ++++- 10 files changed, 40 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/Pdfcreditmemos.php b/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/Pdfcreditmemos.php index 83486704984f7..94ca6a0375e7c 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/Pdfcreditmemos.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/Pdfcreditmemos.php @@ -74,9 +74,12 @@ public function __construct( */ public function massAction(AbstractCollection $collection) { + $pdf = $this->pdfCreditmemo->getPdf($collection); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->fileFactory->create( sprintf('creditmemo%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $this->pdfCreditmemo->getPdf($collection)->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/PrintAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/PrintAction.php index d6c94feb57606..c5902aac33355 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/PrintAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/PrintAction.php @@ -53,6 +53,7 @@ public function __construct( /** * @return ResponseInterface|\Magento\Backend\Model\View\Result\Forward + * @throws \Exception */ public function execute() { @@ -69,9 +70,11 @@ public function execute() $date = $this->_objectManager->get( \Magento\Framework\Stdlib\DateTime\DateTime::class )->date('Y-m-d_H-i-s'); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->_fileFactory->create( \creditmemo::class . $date . '.pdf', - $pdf->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/Pdfinvoices.php b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/Pdfinvoices.php index a949ceca53961..18cf397c0e3b9 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/Pdfinvoices.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/Pdfinvoices.php @@ -75,9 +75,12 @@ public function __construct( */ public function massAction(AbstractCollection $collection) { + $pdf = $this->pdfInvoice->getPdf($collection); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->fileFactory->create( sprintf('invoice%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $this->pdfInvoice->getPdf($collection)->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php index 5ae021f39bc54..2caa8ec2dcb83 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php @@ -45,6 +45,7 @@ public function __construct( /** * @return ResponseInterface|void + * @throws \Exception */ public function execute() { diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php index f96e2fd09c2b0..b6e3c4998eade 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php @@ -76,6 +76,7 @@ public function __construct( * * @param AbstractCollection $collection * @return ResponseInterface|ResultInterface + * @throws \Exception */ protected function massAction(AbstractCollection $collection) { @@ -85,9 +86,12 @@ protected function massAction(AbstractCollection $collection) $this->messageManager->addError(__('There are no printable documents related to selected orders.')); return $this->resultRedirectFactory->create()->setPath($this->getComponentRefererUrl()); } + $pdf = $this->pdfCreditmemo->getPdf($creditmemoCollection->getItems()); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->fileFactory->create( sprintf('creditmemo%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $this->pdfCreditmemo->getPdf($creditmemoCollection->getItems())->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php index d68cfe696b0ef..90ffa2b75de22 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php @@ -113,6 +113,7 @@ public function __construct( * @return ResponseInterface|\Magento\Backend\Model\View\Result\Redirect * * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @throws \Exception */ protected function massAction(AbstractCollection $collection) { @@ -142,10 +143,11 @@ protected function massAction(AbstractCollection $collection) foreach ($documents as $document) { $pdf->pages = array_merge($pdf->pages, $document->pages); } + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; return $this->fileFactory->create( sprintf('docs%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $pdf->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfinvoices.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfinvoices.php index fee124a91410d..93cb5882ed5de 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfinvoices.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfinvoices.php @@ -75,6 +75,7 @@ public function __construct( * * @param AbstractCollection $collection * @return ResponseInterface|ResultInterface + * @throws \Exception */ protected function massAction(AbstractCollection $collection) { @@ -83,9 +84,12 @@ protected function massAction(AbstractCollection $collection) $this->messageManager->addError(__('There are no printable documents related to selected orders.')); return $this->resultRedirectFactory->create()->setPath($this->getComponentRefererUrl()); } + $pdf = $this->pdfInvoice->getPdf($invoicesCollection->getItems()); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->fileFactory->create( sprintf('invoice%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $this->pdfInvoice->getPdf($invoicesCollection->getItems())->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfshipments.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfshipments.php index 1aa5bfdb83878..d414ec99c2e6a 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfshipments.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfshipments.php @@ -77,6 +77,7 @@ public function __construct( * * @param AbstractCollection $collection * @return ResponseInterface|\Magento\Backend\Model\View\Result\Redirect + * @throws \Exception */ protected function massAction(AbstractCollection $collection) { @@ -87,9 +88,13 @@ protected function massAction(AbstractCollection $collection) $this->messageManager->addError(__('There are no printable documents related to selected orders.')); return $this->resultRedirectFactory->create()->setPath($this->getComponentRefererUrl()); } + + $pdf = $this->pdfShipment->getPdf($shipmentsCollection->getItems()); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->fileFactory->create( sprintf('packingslip%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $this->pdfShipment->getPdf($shipmentsCollection->getItems())->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/Pdfshipments.php b/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/Pdfshipments.php index b10dca908fe6a..4a7c6e0533e33 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/Pdfshipments.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/Pdfshipments.php @@ -70,9 +70,12 @@ public function __construct( */ public function massAction(AbstractCollection $collection) { + $pdf = $this->pdfShipment->getPdf($collection); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->fileFactory->create( sprintf('packingslip%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $this->pdfShipment->getPdf($collection)->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/PrintAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/PrintAction.php index da8646a0c30b2..1e49fe7eff60a 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/PrintAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/PrintAction.php @@ -48,6 +48,7 @@ public function __construct( /** * @return ResponseInterface|\Magento\Backend\Model\View\Result\Forward + * @throws \Exception */ public function execute() { @@ -63,9 +64,11 @@ public function execute() $date = $this->_objectManager->get( \Magento\Framework\Stdlib\DateTime\DateTime::class )->date('Y-m-d_H-i-s'); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->_fileFactory->create( 'packingslip' . $date . '.pdf', - $pdf->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); From ec713fcbb0f33f668540e2c9c625097679b68b2e Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Thu, 5 Jul 2018 16:15:43 +0300 Subject: [PATCH 0630/1171] Failing tests fixed --- .../Adminhtml/Order/Creditmemo/PrintActionTest.php | 7 ++++--- .../Magento/Framework/App/Filesystem/CreatePdfFileTest.php | 2 +- .../Magento/Framework/App/Response/Http/FileFactory.php | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/PrintActionTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/PrintActionTest.php index 881af16f5fe69..e12a4195db4c6 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/PrintActionTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/PrintActionTest.php @@ -155,7 +155,8 @@ public function testExecute() $creditmemoId = 2; $date = '2015-01-19_13-03-45'; $fileName = 'creditmemo2015-01-19_13-03-45.pdf'; - $fileContents = 'pdf0123456789'; + $pdfContent = 'pdf0123456789'; + $fileData = ['type' => 'string', 'value' => $pdfContent, 'rm' => true]; $this->prepareTestExecute($creditmemoId); $this->objectManagerMock->expects($this->any()) @@ -184,12 +185,12 @@ public function testExecute() ->willReturn($date); $this->pdfMock->expects($this->once()) ->method('render') - ->willReturn($fileContents); + ->willReturn($pdfContent); $this->fileFactoryMock->expects($this->once()) ->method('create') ->with( $fileName, - $fileContents, + $fileData, \Magento\Framework\App\Filesystem\DirectoryList::VAR_DIR, 'application/pdf' ) diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php index 2f8383667d2f6..d97a57589bc59 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace Magento\Framework\App\Filesystem\Images; +namespace Magento\Framework\App\Filesystem; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\Response\Http\FileFactory; diff --git a/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php b/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php index 6697e1f392ba0..19a89681a2d5f 100644 --- a/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php +++ b/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php @@ -109,9 +109,9 @@ public function create( * Returns file content for writing. * * @param string|array $content - * @return string + * @return string|array */ - private function getFileContent($content): string + private function getFileContent($content) { if (isset($content['type']) && $content['type'] === 'string') { return $content['value']; From 631bd09b3a4557f7d811271ee6acb89ada9a8557 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Tue, 31 Jul 2018 09:58:52 +0300 Subject: [PATCH 0631/1171] MAGETWO-93708: [2.3] Client-side caching of js-translation.js. --- nginx.conf.sample | 2 +- pub/static/.htaccess | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/nginx.conf.sample b/nginx.conf.sample index 881adb84c56e3..6f87a9a076666 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -102,7 +102,7 @@ location /static/ { rewrite ^/static/(version[^/]+/)?(.*)$ /static/$2 last; } - location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { + location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2|json)$ { add_header Cache-Control "public"; add_header X-Frame-Options "SAMEORIGIN"; expires +1y; diff --git a/pub/static/.htaccess b/pub/static/.htaccess index a10e234e07ff2..f49dce25d433e 100644 --- a/pub/static/.htaccess +++ b/pub/static/.htaccess @@ -67,7 +67,7 @@ AddType application/xml xml <IfModule mod_headers.c> - <FilesMatch .*\.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$> + <FilesMatch .*\.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2|json)$> Header append Cache-Control public </FilesMatch> @@ -97,12 +97,13 @@ AddType application/xml xml ExpiresByType application/x-bzip2 "access plus 0 seconds" # CSS, JavaScript, html - <FilesMatch \.(css|js|html)$> + <FilesMatch \.(css|js|html|json)$> ExpiresDefault "access plus 1 year" </FilesMatch> ExpiresByType text/css "access plus 1 year" ExpiresByType text/html "access plus 1 year" ExpiresByType application/javascript "access plus 1 year" + ExpiresByType application/json "access plus 1 year" # Favicon, images, flash <FilesMatch \.(ico|gif|png|jpg|jpeg|swf|svg)$> From eefd0a5e0bdd67dcd7d29a2e76239c56d2509b1f Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Tue, 31 Jul 2018 10:29:59 +0300 Subject: [PATCH 0632/1171] MAGETWO-91335: Manufacturer attribute not appearing in Configurable Products on the backend --- .../Setup/Patch/Data/UpdateManufacturerAttribute.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/UpdateManufacturerAttribute.php b/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/UpdateManufacturerAttribute.php index e27a70153d5e1..2ae3e697f1964 100644 --- a/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/UpdateManufacturerAttribute.php +++ b/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/UpdateManufacturerAttribute.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\ConfigurableProduct\Setup\Patch\Data; From 345c2101b9d0cf6e8d41c46b82bdc5ae1c9c7f32 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Tue, 31 Jul 2018 11:04:28 +0300 Subject: [PATCH 0633/1171] MAGETWO-91562: Sorting on price of configurable products in catalog not working properly --- .../Product/Indexer/Price/DefaultPrice.php | 6 +- .../Model/Indexer/ProductPriceIndexFilter.php | 85 ++++++ .../Model/Plugin/PriceIndexUpdater.php | 79 ++++++ .../Model/ResourceModel/Stock.php | 6 + .../Model/ResourceModel/Stock/Item.php | 147 ++++++++++- ...datePriceIndexUponConfigChangeObserver.php | 49 ++++ ...dateItemsStockUponConfigChangeObserver.php | 30 ++- ...ItemsStockUponConfigChangeObserverTest.php | 19 +- app/code/Magento/CatalogInventory/etc/di.xml | 10 + .../Magento/CatalogInventory/etc/events.xml | 1 + .../Magento/CatalogInventory/etc/mview.xml | 5 + app/code/Magento/CatalogRule/etc/mview.xml | 5 + app/code/Magento/Config/Model/Config.php | 245 ++++++++++++------ .../Config/Test/Unit/Model/ConfigTest.php | 101 ++++---- .../Model/Indexer/DependencyDecorator.php | 10 +- .../Catalog/Model/ProductPriceTest.php | 34 ++- .../ResourceModel/Product/CollectionTest.php | 2 +- .../Model/Indexer/Product/PriceTest.php | 26 +- ...attribute.php => configurable_product.php} | 75 ++---- .../_files/configurable_product_rollback.php | 31 +++ .../CatalogRule/_files/simple_products.php | 54 ++++ ...lback.php => simple_products_rollback.php} | 4 +- .../product_downloadable_with_files.php | 7 + .../Framework/Indexer/Config/Reader.php | 1 + 24 files changed, 798 insertions(+), 234 deletions(-) create mode 100644 app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php create mode 100644 app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php create mode 100644 app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php rename dev/tests/integration/testsuite/Magento/CatalogRule/_files/{product_with_attribute.php => configurable_product.php} (52%) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php rename dev/tests/integration/testsuite/Magento/CatalogRule/_files/{product_with_attribute_rollback.php => simple_products_rollback.php} (87%) 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 4ca407a53f8ae..2bb5a39b4ccea 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 @@ -307,7 +307,7 @@ protected function prepareFinalPriceDataForType($entityIds, $type) $query = $select->insertFromSelect($finalPriceTable->getTableName(), [], false); $this->getConnection()->query($query); - $this->applyDiscountPrices($finalPriceTable); + $this->modifyPriceIndex($finalPriceTable); return $this; } @@ -512,12 +512,12 @@ protected function _prepareCustomOptionPriceTable() } /** - * Apply discount prices to final price index table. + * Modify data in price index table. * * @param IndexTableStructure $finalPriceTable * @return void */ - private function applyDiscountPrices(IndexTableStructure $finalPriceTable) : void + private function modifyPriceIndex(IndexTableStructure $finalPriceTable) : void { foreach ($this->priceModifiers as $priceModifier) { $priceModifier->modifyPrice($finalPriceTable); diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php new file mode 100644 index 0000000000000..1cb3b65be42ae --- /dev/null +++ b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php @@ -0,0 +1,85 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogInventory\Model\Indexer; + +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\CatalogInventory\Model\ResourceModel\Stock\Item; +use Magento\CatalogInventory\Model\Stock; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceModifierInterface; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure; + +/** + * Class for filter product price index. + */ +class ProductPriceIndexFilter implements PriceModifierInterface +{ + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; + + /** + * @var Item + */ + private $stockItem; + + /** + * @param StockConfigurationInterface $stockConfiguration + * @param Item $stockItem + */ + public function __construct(StockConfigurationInterface $stockConfiguration, Item $stockItem) + { + $this->stockConfiguration = $stockConfiguration; + $this->stockItem = $stockItem; + } + + /** + * Remove out of stock products data from price index. + * + * @param IndexTableStructure $priceTable + * @param array $entityIds + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []): void + { + if ($this->stockConfiguration->isShowOutOfStock()) { + return; + } + + $connection = $this->stockItem->getConnection(); + $select = $connection->select(); + $select->from( + ['price_index' => $priceTable->getTableName()], + [] + ); + $select->joinInner( + ['stock_item' => $this->stockItem->getMainTable()], + 'stock_item.product_id = price_index.' . $priceTable->getEntityField() + . ' AND stock_item.stock_id = ' . Stock::DEFAULT_STOCK_ID, + [] + ); + if ($this->stockConfiguration->getManageStock()) { + $stockStatus = $connection->getCheckSql( + 'use_config_manage_stock = 0 AND manage_stock = 0', + Stock::STOCK_IN_STOCK, + 'is_in_stock' + ); + } else { + $stockStatus = $connection->getCheckSql( + 'use_config_manage_stock = 0 AND manage_stock = 1', + 'is_in_stock', + Stock::STOCK_IN_STOCK + ); + } + $select->where($stockStatus . ' = ?', Stock::STOCK_OUT_OF_STOCK); + + $query = $select->deleteFromSelect('price_index'); + $connection->query($query); + } +} diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php new file mode 100644 index 0000000000000..f5cb43afb8e36 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogInventory\Model\Plugin; + +use Magento\CatalogInventory\Model\ResourceModel\Stock\Item; +use Magento\Catalog\Model\Indexer\Product\Price\Processor; +use Magento\Framework\Model\AbstractModel; + +/** + * Update product price index after product stock status changed. + */ +class PriceIndexUpdater +{ + /** + * @var Processor + */ + private $priceIndexProcessor; + + /** + * @param Processor $priceIndexProcessor + */ + public function __construct(Processor $priceIndexProcessor) + { + $this->priceIndexProcessor = $priceIndexProcessor; + } + + /** + * @param Item $subject + * @param Item $result + * @param AbstractModel $model + * @return Item + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterSave(Item $subject, Item $result, AbstractModel $model): Item + { + $fields = [ + 'is_in_stock', + 'use_config_manage_stock', + 'manage_stock', + ]; + foreach ($fields as $field) { + if ($model->dataHasChangedFor($field)) { + $this->priceIndexProcessor->reindexRow($model->getProductId()); + break; + } + } + + return $result; + } + + /** + * @param Item $subject + * @param $result + * @param int $websiteId + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterUpdateSetOutOfStock(Item $subject, $result, int $websiteId) + { + $this->priceIndexProcessor->markIndexerAsInvalid(); + } + + /** + * @param Item $subject + * @param $result + * @param int $websiteId + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterUpdateSetInStock(Item $subject, $result, int $websiteId) + { + $this->priceIndexProcessor->markIndexerAsInvalid(); + } +} diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php index f1dc9715fd247..10408d832dca8 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php @@ -206,6 +206,8 @@ protected function _initConfig() /** * Set items out of stock basing on their quantities and config settings * + * @deprecated + * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateSetOutOfStock * @param string|int $website * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return void @@ -241,6 +243,8 @@ public function updateSetOutOfStock($website = null) /** * Set items in stock basing on their quantities and config settings * + * @deprecated + * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateSetInStock * @param int|string $website * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return void @@ -274,6 +278,8 @@ public function updateSetInStock($website) /** * Update items low stock date basing on their quantities and config settings * + * @deprecated + * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateLowStockDate * @param int|string $website * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return void diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php index 895fffaa4f80b..5e16df655d0e5 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php @@ -6,10 +6,14 @@ namespace Magento\CatalogInventory\Model\ResourceModel\Stock; use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\CatalogInventory\Model\Stock; use Magento\CatalogInventory\Model\Indexer\Stock\Processor; -use Magento\Framework\App\ResourceConnection as AppResource; use Magento\Framework\Model\AbstractModel; -use Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Framework\DB\Select; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Stdlib\DateTime\DateTime; /** * Stock item resource model @@ -29,17 +33,36 @@ class Item extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb protected $stockIndexerProcessor; /** - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context + * @var StockConfigurationInterface + */ + private $stockConfiguration; + + /** + * @var DateTime + */ + private $dateTime; + + /** + * @param Context $context * @param Processor $processor * @param string $connectionName + * @param StockConfigurationInterface $stockConfiguration + * @param DateTime $dateTime */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, + Context $context, Processor $processor, - $connectionName = null + $connectionName = null, + StockConfigurationInterface $stockConfiguration = null, + DateTime $dateTime = null ) { $this->stockIndexerProcessor = $processor; parent::__construct($context, $connectionName); + + $this->stockConfiguration = $stockConfiguration ?? + ObjectManager::getInstance()->get(StockConfigurationInterface::class); + $this->dateTime = $dateTime ?? + ObjectManager::getInstance()->get(DateTime::class); } /** @@ -139,4 +162,118 @@ public function setProcessIndexEvents($process = true) $this->processIndexEvents = $process; return $this; } + + /** + * Set items out of stock basing on their quantities and config settings + * + * @param int $websiteId + * @return void + */ + public function updateSetOutOfStock(int $websiteId): void + { + $connection = $this->getConnection(); + + $values = [ + 'is_in_stock' => Stock::STOCK_OUT_OF_STOCK, + 'stock_status_changed_auto' => 1, + ]; + $select = $this->buildProductsSelectByConfigTypes(); + $where = [ + 'website_id = ' . $websiteId, + 'is_in_stock = ' . Stock::STOCK_IN_STOCK, + '(use_config_manage_stock = 1 AND 1 = ' . $this->stockConfiguration->getManageStock() . ')' + . ' OR (use_config_manage_stock = 0 AND manage_stock = 1)', + '(use_config_min_qty = 1 AND qty <= ' . $this->stockConfiguration->getMinQty() . ')' + . ' OR (use_config_min_qty = 0 AND qty <= min_qty)', + 'product_id IN (' . $select->assemble() . ')', + ]; + $backordersWhere = '(use_config_backorders = 0 AND backorders = ' . Stock::BACKORDERS_NO . ')'; + if (Stock::BACKORDERS_NO == $this->stockConfiguration->getBackorders()) { + $where[] = $backordersWhere . ' OR use_config_backorders = 1'; + } else { + $where[] = $backordersWhere; + } + $connection->update($this->getMainTable(), $values, $where); + + $this->stockIndexerProcessor->markIndexerAsInvalid(); + } + + /** + * Set items in stock basing on their quantities and config settings + * + * @param int $websiteId + * @return void + */ + public function updateSetInStock(int $websiteId): void + { + $connection = $this->getConnection(); + + $values = [ + 'is_in_stock' => Stock::STOCK_IN_STOCK, + ]; + $select = $this->buildProductsSelectByConfigTypes(); + $where = [ + 'website_id = ' . $websiteId, + 'stock_status_changed_auto = 1', + '(use_config_min_qty = 1 AND qty > ' . $this->stockConfiguration->getMinQty() . ')' + . ' OR (use_config_min_qty = 0 AND qty > min_qty)', + 'product_id IN (' . $select->assemble() . ')', + ]; + $manageStockWhere = '(use_config_manage_stock = 0 AND manage_stock = 1)'; + if ($this->stockConfiguration->getManageStock()) { + $where[] = $manageStockWhere . ' OR use_config_manage_stock = 1'; + } else { + $where[] = $manageStockWhere; + } + $connection->update($this->getMainTable(), $values, $where); + + $this->stockIndexerProcessor->markIndexerAsInvalid(); + } + + /** + * Update items low stock date basing on their quantities and config settings + * + * @param int $websiteId + * @return void + */ + public function updateLowStockDate(int $websiteId): void + { + $connection = $this->getConnection(); + + $condition = $connection->quoteInto( + '(use_config_notify_stock_qty = 1 AND qty < ?)', + $this->stockConfiguration->getNotifyStockQty() + ) . ' OR (use_config_notify_stock_qty = 0 AND qty < notify_stock_qty)'; + $currentDbTime = $connection->quoteInto('?', $this->dateTime->gmtDate()); + $conditionalDate = $connection->getCheckSql($condition, $currentDbTime, 'NULL'); + $value = [ + 'low_stock_date' => new \Zend_Db_Expr($conditionalDate), + ]; + $select = $this->buildProductsSelectByConfigTypes(); + $where = [ + 'website_id = ' . $websiteId, + 'product_id IN (' . $select->assemble() . ')' + ]; + $manageStockWhere = '(use_config_manage_stock = 0 AND manage_stock = 1)'; + if ($this->stockConfiguration->getManageStock()) { + $where[] = $manageStockWhere . ' OR use_config_manage_stock = 1'; + } else { + $where[] = $manageStockWhere; + } + $connection->update($this->getMainTable(), $value, $where); + } + + /** + * Build select for products with types from config + * + * @return Select + */ + private function buildProductsSelectByConfigTypes(): Select + { + $select = $this->getConnection()->select() + ->from($this->getTable('catalog_product_entity'), 'entity_id') + ->where('type_id IN (?)', array_keys($this->stockConfiguration->getIsQtyTypeIds(true))); + + return $select; + } } diff --git a/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php b/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php new file mode 100644 index 0000000000000..976110ec76cf3 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogInventory\Observer; + +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Event\Observer; +use Magento\CatalogInventory\Model\Configuration; +use Magento\Catalog\Model\Indexer\Product\Price\Processor; + +/** + * Catalog inventory config changes module observer. + */ +class InvalidatePriceIndexUponConfigChangeObserver implements ObserverInterface +{ + /** + * @var Processor + */ + private $priceIndexProcessor; + + /** + * @param Processor $priceIndexProcessor + */ + public function __construct(Processor $priceIndexProcessor) + { + $this->priceIndexProcessor = $priceIndexProcessor; + } + + /** + * Invalidate product price index on catalog inventory config changes. + * + * @param Observer $observer + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function execute(Observer $observer) + { + $changedPaths = (array) $observer->getEvent()->getChangedPaths(); + + if (\in_array(Configuration::XML_PATH_SHOW_OUT_OF_STOCK, $changedPaths, true)) { + $priceIndexer = $this->priceIndexProcessor->getIndexer(); + $priceIndexer->invalidate(); + } + } +} diff --git a/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php b/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php index 47ee6512920d8..21c78eca93695 100644 --- a/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php @@ -3,11 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\CatalogInventory\Observer; use Magento\Framework\Event\Observer as EventObserver; use Magento\Framework\Event\ObserverInterface; +use Magento\CatalogInventory\Model\Configuration; +use Magento\CatalogInventory\Model\ResourceModel\Stock\Item; /** * Catalog inventory module observer @@ -15,16 +16,16 @@ class UpdateItemsStockUponConfigChangeObserver implements ObserverInterface { /** - * @var \Magento\CatalogInventory\Model\ResourceModel\Stock + * @var Item */ - protected $resourceStock; + protected $resourceStockItem; /** - * @param \Magento\CatalogInventory\Model\ResourceModel\Stock $resourceStock + * @param Item $resourceStockItem */ - public function __construct(\Magento\CatalogInventory\Model\ResourceModel\Stock $resourceStock) + public function __construct(Item $resourceStockItem) { - $this->resourceStock = $resourceStock; + $this->resourceStockItem = $resourceStockItem; } /** @@ -35,9 +36,18 @@ public function __construct(\Magento\CatalogInventory\Model\ResourceModel\Stock */ public function execute(EventObserver $observer) { - $website = $observer->getEvent()->getWebsite(); - $this->resourceStock->updateSetOutOfStock($website); - $this->resourceStock->updateSetInStock($website); - $this->resourceStock->updateLowStockDate($website); + $website = (int) $observer->getEvent()->getWebsite(); + $changedPaths = (array) $observer->getEvent()->getChangedPaths(); + + if (\array_intersect([ + Configuration::XML_PATH_MANAGE_STOCK, + Configuration::XML_PATH_MIN_QTY, + Configuration::XML_PATH_BACKORDERS, + Configuration::XML_PATH_NOTIFY_STOCK_QTY, + ], $changedPaths)) { + $this->resourceStockItem->updateSetOutOfStock($website); + $this->resourceStockItem->updateSetInStock($website); + $this->resourceStockItem->updateLowStockDate($website); + } } } diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php index 70a179b484379..7b82b5927d22c 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php @@ -15,9 +15,9 @@ class UpdateItemsStockUponConfigChangeObserverTest extends \PHPUnit\Framework\Te protected $observer; /** - * @var \Magento\CatalogInventory\Model\ResourceModel\Stock|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\CatalogInventory\Model\ResourceModel\Stock\Item|\PHPUnit_Framework_MockObject_MockObject */ - protected $resourceStock; + protected $resourceStockItem; /** * @var \Magento\Framework\Event|\PHPUnit_Framework_MockObject_MockObject @@ -31,11 +31,11 @@ class UpdateItemsStockUponConfigChangeObserverTest extends \PHPUnit\Framework\Te protected function setUp() { - $this->resourceStock = $this->createMock(\Magento\CatalogInventory\Model\ResourceModel\Stock::class); + $this->resourceStockItem = $this->createMock(\Magento\CatalogInventory\Model\ResourceModel\Stock\Item::class); $this->event = $this->getMockBuilder(\Magento\Framework\Event::class) ->disableOriginalConstructor() - ->setMethods(['getWebsite']) + ->setMethods(['getWebsite', 'getChangedPaths']) ->getMock(); $this->eventObserver = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) @@ -50,7 +50,7 @@ protected function setUp() $this->observer = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( \Magento\CatalogInventory\Observer\UpdateItemsStockUponConfigChangeObserver::class, [ - 'resourceStock' => $this->resourceStock, + 'resourceStockItem' => $this->resourceStockItem, ] ); } @@ -58,13 +58,16 @@ protected function setUp() public function testUpdateItemsStockUponConfigChange() { $websiteId = 1; - $this->resourceStock->expects($this->once())->method('updateSetOutOfStock'); - $this->resourceStock->expects($this->once())->method('updateSetInStock'); - $this->resourceStock->expects($this->once())->method('updateLowStockDate'); + $this->resourceStockItem->expects($this->once())->method('updateSetOutOfStock'); + $this->resourceStockItem->expects($this->once())->method('updateSetInStock'); + $this->resourceStockItem->expects($this->once())->method('updateLowStockDate'); $this->event->expects($this->once()) ->method('getWebsite') ->will($this->returnValue($websiteId)); + $this->event->expects($this->once()) + ->method('getChangedPaths') + ->will($this->returnValue([\Magento\CatalogInventory\Model\Configuration::XML_PATH_MANAGE_STOCK])); $this->observer->execute($this->eventObserver); } diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml index 65bc277121429..264f25d24647e 100644 --- a/app/code/Magento/CatalogInventory/etc/di.xml +++ b/app/code/Magento/CatalogInventory/etc/di.xml @@ -118,4 +118,14 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceInterface"> + <arguments> + <argument name="priceModifiers" xsi:type="array"> + <item name="inventoryProductPriceIndexFilter" xsi:type="object">Magento\CatalogInventory\Model\Indexer\ProductPriceIndexFilter</item> + </argument> + </arguments> + </type> + <type name="Magento\CatalogInventory\Model\ResourceModel\Stock\Item"> + <plugin name="priceIndexUpdater" type="Magento\CatalogInventory\Model\Plugin\PriceIndexUpdater" /> + </type> </config> diff --git a/app/code/Magento/CatalogInventory/etc/events.xml b/app/code/Magento/CatalogInventory/etc/events.xml index 3197501e9b70b..328edbade6068 100644 --- a/app/code/Magento/CatalogInventory/etc/events.xml +++ b/app/code/Magento/CatalogInventory/etc/events.xml @@ -38,6 +38,7 @@ </event> <event name="admin_system_config_changed_section_cataloginventory"> <observer name="inventory" instance="Magento\CatalogInventory\Observer\UpdateItemsStockUponConfigChangeObserver"/> + <observer name="invalidatePriceIndex" instance="Magento\CatalogInventory\Observer\InvalidatePriceIndexUponConfigChangeObserver"/> </event> <event name="sales_quote_item_collection_products_after_load"> <observer name="add_stock_items" instance="Magento\CatalogInventory\Observer\AddStockItemsObserver"/> diff --git a/app/code/Magento/CatalogInventory/etc/mview.xml b/app/code/Magento/CatalogInventory/etc/mview.xml index c3d73ff43e8eb..72dda16e8b5bb 100644 --- a/app/code/Magento/CatalogInventory/etc/mview.xml +++ b/app/code/Magento/CatalogInventory/etc/mview.xml @@ -11,4 +11,9 @@ <table name="cataloginventory_stock_item" entity_column="product_id" /> </subscriptions> </view> + <view id="catalog_product_price" class="Magento\Catalog\Model\Indexer\Product\Price" group="indexer"> + <subscriptions> + <table name="cataloginventory_stock_item" entity_column="product_id" /> + </subscriptions> + </view> </config> diff --git a/app/code/Magento/CatalogRule/etc/mview.xml b/app/code/Magento/CatalogRule/etc/mview.xml index 4b1166941bdc8..35efe33461afc 100644 --- a/app/code/Magento/CatalogRule/etc/mview.xml +++ b/app/code/Magento/CatalogRule/etc/mview.xml @@ -24,4 +24,9 @@ <table name="catalog_category_product" entity_column="product_id" /> </subscriptions> </view> + <view id="catalog_product_price" class="Magento\Catalog\Model\Indexer\Product\Price" group="indexer"> + <subscriptions> + <table name="catalogrule_product_price" entity_column="product_id" /> + </subscriptions> + </view> </config> diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php index bc1515aadb0ca..11ad1ef2a43e9 100644 --- a/app/code/Magento/Config/Model/Config.php +++ b/app/code/Magento/Config/Model/Config.php @@ -5,6 +5,9 @@ */ namespace Magento\Config\Model; +use Magento\Config\Model\Config\Structure\Element\Group; +use Magento\Config\Model\Config\Structure\Element\Field; + /** * Backend config model * Used to save configuration @@ -126,11 +129,12 @@ public function save() $oldConfig = $this->_getConfig(true); + /** @var \Magento\Framework\DB\Transaction $deleteTransaction */ $deleteTransaction = $this->_transactionFactory->create(); - /* @var $deleteTransaction \Magento\Framework\DB\Transaction */ + /** @var \Magento\Framework\DB\Transaction $saveTransaction */ $saveTransaction = $this->_transactionFactory->create(); - /* @var $saveTransaction \Magento\Framework\DB\Transaction */ + $changedPaths = []; // Extends for old config data $extraOldGroups = []; @@ -145,6 +149,9 @@ public function save() $saveTransaction, $deleteTransaction ); + + $groupChangedPaths = $this->getChangedPaths($sectionId, $groupId, $groupData, $oldConfig, $extraOldGroups); + $changedPaths = \array_merge($changedPaths, $groupChangedPaths); } try { @@ -157,7 +164,11 @@ public function save() // website and store codes can be used in event implementation, so set them as well $this->_eventManager->dispatch( "admin_system_config_changed_section_{$this->getSection()}", - ['website' => $this->getWebsite(), 'store' => $this->getStore()] + [ + 'website' => $this->getWebsite(), + 'store' => $this->getStore(), + 'changed_paths' => $changedPaths, + ] ); } catch (\Exception $e) { // re-init configuration @@ -168,6 +179,144 @@ public function save() return $this; } + /** + * Map field name if they were cloned + * + * @param Group $group + * @param string $fieldId + * @return string + */ + private function getOriginalFieldId(Group $group, string $fieldId): string + { + if ($group->shouldCloneFields()) { + $cloneModel = $group->getCloneModel(); + + /** @var \Magento\Config\Model\Config\Structure\Element\Field $field */ + foreach ($group->getChildren() as $field) { + foreach ($cloneModel->getPrefixes() as $prefix) { + if ($prefix['field'] . $field->getId() === $fieldId) { + $fieldId = $field->getId(); + break(2); + } + } + } + } + + return $fieldId; + } + + /** + * Get field object + * + * @param string $sectionId + * @param string $groupId + * @param string $fieldId + * @return Field + */ + private function getField(string $sectionId, string $groupId, string $fieldId): Field + { + /** @var \Magento\Config\Model\Config\Structure\Element\Group $group */ + $group = $this->_configStructure->getElement($sectionId . '/' . $groupId); + $fieldPath = $group->getPath() . '/' . $this->getOriginalFieldId($group, $fieldId); + $field = $this->_configStructure->getElement($fieldPath); + + return $field; + } + + /** + * Get field path + * + * @param Field $field + * @param array &$oldConfig Need for compatibility with _processGroup() + * @param array &$extraOldGroups Need for compatibility with _processGroup() + * @return string + */ + private function getFieldPath(Field $field, array &$oldConfig, array &$extraOldGroups): string + { + $path = $field->getGroupPath() . '/' . $field->getId(); + + /** + * Look for custom defined field path + */ + $configPath = $field->getConfigPath(); + if ($configPath && strrpos($configPath, '/') > 0) { + // Extend old data with specified section group + $configGroupPath = substr($configPath, 0, strrpos($configPath, '/')); + if (!isset($extraOldGroups[$configGroupPath])) { + $oldConfig = $this->extendConfig($configGroupPath, true, $oldConfig); + $extraOldGroups[$configGroupPath] = true; + } + $path = $configPath; + } + + return $path; + } + + /** + * Check is config value changed + * + * @param array $oldConfig + * @param string $path + * @param array $fieldData + * @return bool + */ + private function isValueChanged(array $oldConfig, string $path, array $fieldData): bool + { + if (isset($oldConfig[$path]['value'])) { + $result = !isset($fieldData['value']) || $oldConfig[$path]['value'] !== $fieldData['value']; + } else { + $result = empty($fieldData['inherit']); + } + + return $result; + } + + /** + * Get changed paths + * + * @param string $sectionId + * @param string $groupId + * @param array $groupData + * @param array &$oldConfig + * @param array &$extraOldGroups + * @return array + */ + private function getChangedPaths( + string $sectionId, + string $groupId, + array $groupData, + array &$oldConfig, + array &$extraOldGroups + ): array { + $changedPaths = []; + + if (isset($groupData['fields'])) { + foreach ($groupData['fields'] as $fieldId => $fieldData) { + $field = $this->getField($sectionId, $groupId, $fieldId); + $path = $this->getFieldPath($field, $oldConfig, $extraOldGroups); + if ($this->isValueChanged($oldConfig, $path, $fieldData)) { + $changedPaths[] = $path; + } + } + } + + if (isset($groupData['groups'])) { + $subSectionId = $sectionId . '/' . $groupId; + foreach ($groupData['groups'] as $subGroupId => $subGroupData) { + $subGroupChangedPaths = $this->getChangedPaths( + $subSectionId, + $subGroupId, + $subGroupData, + $oldConfig, + $extraOldGroups + ); + $changedPaths = \array_merge($changedPaths, $subGroupChangedPaths); + } + } + + return $changedPaths; + } + /** * Process group data * @@ -181,8 +330,6 @@ public function save() * @param \Magento\Framework\DB\Transaction $deleteTransaction * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function _processGroup( $groupId, @@ -195,92 +342,42 @@ protected function _processGroup( \Magento\Framework\DB\Transaction $deleteTransaction ) { $groupPath = $sectionPath . '/' . $groupId; - $scope = $this->getScope(); - $scopeId = $this->getScopeId(); - $scopeCode = $this->getScopeCode(); - /** - * - * Map field names if they were cloned - */ - /** @var $group \Magento\Config\Model\Config\Structure\Element\Group */ - $group = $this->_configStructure->getElement($groupPath); - // set value for group field entry by fieldname - // use extra memory - $fieldsetData = []; if (isset($groupData['fields'])) { - if ($group->shouldCloneFields()) { - $cloneModel = $group->getCloneModel(); - $mappedFields = []; - - /** @var $field \Magento\Config\Model\Config\Structure\Element\Field */ - foreach ($group->getChildren() as $field) { - foreach ($cloneModel->getPrefixes() as $prefix) { - $mappedFields[$prefix['field'] . $field->getId()] = $field->getId(); - } - } - } - foreach ($groupData['fields'] as $fieldId => $fieldData) { - $fieldsetData[$fieldId] = is_array( - $fieldData - ) && isset( - $fieldData['value'] - ) ? $fieldData['value'] : null; - } + /** @var \Magento\Config\Model\Config\Structure\Element\Group $group */ + $group = $this->_configStructure->getElement($groupPath); + // set value for group field entry by fieldname + // use extra memory + $fieldsetData = []; foreach ($groupData['fields'] as $fieldId => $fieldData) { - $originalFieldId = $fieldId; - if ($group->shouldCloneFields() && isset($mappedFields[$fieldId])) { - $originalFieldId = $mappedFields[$fieldId]; - } - /** @var $field \Magento\Config\Model\Config\Structure\Element\Field */ - $field = $this->_configStructure->getElement($groupPath . '/' . $originalFieldId); - + $field = $this->getField($sectionPath, $groupId, $fieldId); /** @var \Magento\Framework\App\Config\ValueInterface $backendModel */ - $backendModel = $field->hasBackendModel() ? $field - ->getBackendModel() : $this - ->_configValueFactory - ->create(); + $backendModel = $field->hasBackendModel() + ? $field->getBackendModel() + : $this->_configValueFactory->create(); + if (!isset($fieldData['value'])) { + $fieldData['value'] = null; + } + $fieldsetData[$fieldId] = $fieldData['value']; $data = [ 'field' => $fieldId, 'groups' => $groups, 'group_id' => $group->getId(), - 'scope' => $scope, - 'scope_id' => $scopeId, - 'scope_code' => $scopeCode, + 'scope' => $this->getScope(), + 'scope_id' => $this->getScopeId(), + 'scope_code' => $this->getScopeCode(), 'field_config' => $field->getData(), - 'fieldset_data' => $fieldsetData + 'fieldset_data' => $fieldsetData, ]; $backendModel->addData($data); - $this->_checkSingleStoreMode($field, $backendModel); - if (false == isset($fieldData['value'])) { - $fieldData['value'] = null; - } - - $path = $field->getGroupPath() . '/' . $fieldId; - /** - * Look for custom defined field path - */ - if ($field && $field->getConfigPath()) { - $configPath = $field->getConfigPath(); - if (!empty($configPath) && strrpos($configPath, '/') > 0) { - // Extend old data with specified section group - $configGroupPath = substr($configPath, 0, strrpos($configPath, '/')); - if (!isset($extraOldGroups[$configGroupPath])) { - $oldConfig = $this->extendConfig($configGroupPath, true, $oldConfig); - $extraOldGroups[$configGroupPath] = true; - } - $path = $configPath; - } - } - - $inherit = !empty($fieldData['inherit']); - + $path = $this->getFieldPath($field, $extraOldGroups, $oldConfig); $backendModel->setPath($path)->setValue($fieldData['value']); + $inherit = !empty($fieldData['inherit']); if (isset($oldConfig[$path])) { $backendModel->setConfigId($oldConfig[$path]['config_id']); diff --git a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php index 2ddbbd5ffe1e8..5e11e46439c8b 100644 --- a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php @@ -152,52 +152,50 @@ public function testSaveToCheckAdminSystemConfigChangedSectionEvent() public function testSaveToCheckScopeDataSet() { $transactionMock = $this->createMock(\Magento\Framework\DB\Transaction::class); - $this->_transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock)); $this->_configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([])); - $this->_eventManagerMock->expects( - $this->at(0) - )->method( - 'dispatch' - )->with( - $this->equalTo('admin_system_config_changed_section_'), - $this->arrayHasKey('website') - ); - - $this->_eventManagerMock->expects( - $this->at(0) - )->method( - 'dispatch' - )->with( - $this->equalTo('admin_system_config_changed_section_'), - $this->arrayHasKey('store') - ); + $this->_eventManagerMock->expects($this->at(0)) + ->method('dispatch') + ->with( + $this->equalTo('admin_system_config_changed_section_section'), + $this->arrayHasKey('website') + ); + $this->_eventManagerMock->expects($this->at(0)) + ->method('dispatch') + ->with( + $this->equalTo('admin_system_config_changed_section_section'), + $this->arrayHasKey('store') + ); $group = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Group::class); + $group->method('getPath')->willReturn('section/1'); $field = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Field::class); - - $this->_configStructure->expects( - $this->at(0) - )->method( - 'getElement' - )->with( - '/1' - )->will( - $this->returnValue($group) - ); - - $this->_configStructure->expects( - $this->at(1) - )->method( - 'getElement' - )->with( - '/1/key' - )->will( - $this->returnValue($field) - ); + $field->method('getGroupPath')->willReturn('section/1'); + $field->method('getId')->willReturn('key'); + + $this->_configStructure->expects($this->at(0)) + ->method('getElement') + ->with('section/1') + ->will($this->returnValue($group)); + $this->_configStructure->expects($this->at(1)) + ->method('getElement') + ->with('section/1') + ->will($this->returnValue($group)); + $this->_configStructure->expects($this->at(2)) + ->method('getElement') + ->with('section/1/key') + ->will($this->returnValue($field)); + $this->_configStructure->expects($this->at(3)) + ->method('getElement') + ->with('section/1') + ->will($this->returnValue($group)); + $this->_configStructure->expects($this->at(4)) + ->method('getElement') + ->with('section/1/key') + ->will($this->returnValue($field)); $website = $this->createMock(\Magento\Store\Model\Website::class); $website->expects($this->any())->method('getCode')->will($this->returnValue('website_code')); @@ -206,19 +204,16 @@ public function testSaveToCheckScopeDataSet() $this->_storeManager->expects($this->any())->method('isSingleStoreMode')->will($this->returnValue(true)); $this->_model->setWebsite('website'); - + $this->_model->setSection('section'); $this->_model->setGroups(['1' => ['fields' => ['key' => ['data']]]]); $backendModel = $this->createPartialMock( \Magento\Framework\App\Config\Value::class, ['setPath', 'addData', '__sleep', '__wakeup'] ); - $backendModel->expects( - $this->once() - )->method( - 'addData' - )->with( - [ + $backendModel->expects($this->once()) + ->method('addData') + ->with([ 'field' => 'key', 'groups' => [1 => ['fields' => ['key' => ['data']]]], 'group_id' => null, @@ -227,17 +222,11 @@ public function testSaveToCheckScopeDataSet() 'scope_code' => 'website_code', 'field_config' => null, 'fieldset_data' => ['key' => null], - ] - ); - $backendModel->expects( - $this->once() - )->method( - 'setPath' - )->with( - '/key' - )->will( - $this->returnValue($backendModel) - ); + ]); + $backendModel->expects($this->once()) + ->method('setPath') + ->with('section/1/key') + ->will($this->returnValue($backendModel)); $this->_dataFactoryMock->expects($this->any())->method('create')->will($this->returnValue($backendModel)); diff --git a/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php b/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php index d22795f127138..829df74a1b0ed 100644 --- a/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php +++ b/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php @@ -256,7 +256,10 @@ public function reindexRow($id) $this->indexer->reindexRow($id); $dependentIndexerIds = $this->dependencyInfoProvider->getIndexerIdsToRunAfter($this->indexer->getId()); foreach ($dependentIndexerIds as $indexerId) { - $this->indexerRegistry->get($indexerId)->reindexRow($id); + $dependentIndexer = $this->indexerRegistry->get($indexerId); + if (!$dependentIndexer->isScheduled()) { + $dependentIndexer->reindexRow($id); + } } } @@ -268,7 +271,10 @@ public function reindexList($ids) $this->indexer->reindexList($ids); $dependentIndexerIds = $this->dependencyInfoProvider->getIndexerIdsToRunAfter($this->indexer->getId()); foreach ($dependentIndexerIds as $indexerId) { - $this->indexerRegistry->get($indexerId)->reindexList($ids); + $dependentIndexer = $this->indexerRegistry->get($indexerId); + if (!$dependentIndexer->isScheduled()) { + $dependentIndexer->reindexList($ids); + } } } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php index 594133e984a46..2653e40c85d7d 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php @@ -8,6 +8,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\CatalogInventory\Api\StockRegistryInterface; /** * Tests product model: @@ -23,9 +24,15 @@ class ProductPriceTest extends \PHPUnit\Framework\TestCase */ protected $_model; + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + protected function setUp() { $this->_model = Bootstrap::getObjectManager()->create(Product::class); + $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); } public function testGetPrice() @@ -81,8 +88,7 @@ public function testSetGetFinalPrice() */ public function testGetMinPrice(): void { - $productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); - $product = $productRepository->get('simple'); + $product = $this->productRepository->get('simple'); $collection = Bootstrap::getObjectManager()->create(Collection::class); $collection->addIdFilter($product->getId()); $collection->addPriceData(); @@ -91,4 +97,28 @@ public function testGetMinPrice(): void $product = $collection->getFirstItem(); $this->assertEquals(333, $product->getData('min_price')); } + + /** + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php + */ + public function testGetMinPriceForComposite(): void + { + $confProduct = $this->productRepository->get('configurable'); + $collection = Bootstrap::getObjectManager()->create(Collection::class); + $collection->addIdFilter($confProduct->getId()); + $collection->addPriceData(); + $collection->load(); + $product = $collection->getFirstItem(); + $this->assertEquals(10, $product->getData('min_price')); + + $childProduct = $this->productRepository->get('simple_10'); + $stockRegistry = Bootstrap::getObjectManager()->get(StockRegistryInterface::class); + $stockItem = $stockRegistry->getStockItem($childProduct->getId()); + $stockItem->setIsInStock(false); + $stockRegistry->updateStockItemBySku($childProduct->getSku(), $stockItem); + $collection->clear()->load(); + $product = $collection->getFirstItem(); + $this->assertEquals(20, $product->getData('min_price')); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php index e58d445a25650..6bfe034db46f3 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php @@ -50,6 +50,7 @@ public function testAddPriceDataOnSchedule() { $this->processor->getIndexer()->setScheduled(true); $this->assertTrue($this->processor->getIndexer()->isScheduled()); + $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ @@ -81,7 +82,6 @@ public function testAddPriceDataOnSchedule() $product = reset($items); $this->assertCount(2, $items); $this->assertEquals(15, $product->getPrice()); - $this->processor->getIndexer()->reindexList([1]); $this->processor->getIndexer()->setScheduled(false); } diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php index b1a10c894f83a..537298aba88e1 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php @@ -14,13 +14,6 @@ use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SortOrder; -/** - * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/attribute.php - * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/product_with_attribute.php - * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled - */ class PriceTest extends \PHPUnit\Framework\TestCase { /** @@ -37,7 +30,10 @@ protected function setUp() } /** - * @return void + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/configurable_product.php + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled */ public function testPriceApplying() : void { @@ -52,7 +48,6 @@ public function testPriceApplying() : void /** @var \Magento\Catalog\Model\Product $simpleProduct */ $simpleProduct = $collection->getFirstItem(); $simpleProduct->setPriceCalculation(false); - $rulePrice = $this->resourceRule->getRulePrice(new \DateTime(), $websiteId, $customerGroupId, $simpleProductId); $this->assertEquals($rulePrice, $simpleProduct->getFinalPrice()); @@ -63,11 +58,14 @@ public function testPriceApplying() : void $collection->load(); /** @var \Magento\Catalog\Model\Product $confProduct */ $confProduct = $collection->getFirstItem(); - - $this->assertEquals($simpleProduct->getMinimalPrice(), $confProduct->getMinimalPrice()); + $this->assertEquals($simpleProduct->getFinalPrice(), $confProduct->getMinimalPrice()); } /** + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/simple_products.php + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled * @magentoAppArea frontend * * @return void @@ -80,10 +78,10 @@ public function testSortByPrice() : void $searchCriteria->setSortOrders([$sortOrder]); $productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); $searchResults = $productRepository->getList($searchCriteria); - $products = $searchResults->getItems(); + /** @var \Magento\Catalog\Model\Product[] $products */ + $products = array_values($searchResults->getItems()); - /** @var \Magento\Catalog\Model\Product $product1 */ - $product1 = array_values($products)[0]; + $product1 = $products[0]; $product1->setPriceCalculation(false); $this->assertEquals('simple1', $product1->getSku()); $rulePrice = $this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $product1->getId()); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php similarity index 52% rename from dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php rename to dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php index 071f5d7d9fd00..ef7144d9d8438 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php @@ -6,12 +6,12 @@ declare(strict_types=1); require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute.php'; +require __DIR__ . '/simple_products.php'; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $storeManager = $objectManager->get(\Magento\Store\Model\StoreManager::class); $store = $storeManager->getStore('default'); - $productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); $installer = $objectManager->get(\Magento\Catalog\Setup\CategorySetup::class); @@ -19,63 +19,22 @@ $attributeValues = []; $associatedProductIds = []; -/** @var Magento\Eav\Model\Entity\Attribute\Option[] $options */ +$attributeRepository = $objectManager->get(\Magento\Eav\Api\AttributeRepositoryInterface::class); +$attribute = $attributeRepository->get('catalog_product', 'test_configurable'); $options = $attribute->getOptions(); array_shift($options); //remove the first option which is empty - -$product = $objectManager->create(\Magento\Catalog\Model\Product::class) - ->setTypeId('simple') - ->setId(1) - ->setAttributeSetId($attributeSetId) - ->setWebsiteIds([1]) - ->setName('Simple Product 1') - ->setSku('simple1') - ->setPrice(10) - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setStockData([ - 'use_config_manage_stock' => 1, - 'qty' => 100, - 'is_qty_decimal' => 0, - 'is_in_stock' => 1, - ]); -$option = array_shift($options); -$product->setTestConfigurable($option->getValue()); -$productRepository->save($product); -$attributeValues[] = [ - 'label' => 'test', - 'attribute_id' => $attribute->getId(), - 'value_index' => $option->getValue(), -]; -$associatedProductIds[] = $product->getId(); -$productAction = $objectManager->get(\Magento\Catalog\Model\Product\Action::class); -$productAction->updateAttributes([$product->getId()], ['test_attribute' => 'test_attribute_value'], $store->getId()); - -$product = $objectManager->create(\Magento\Catalog\Model\Product::class) - ->setTypeId('simple') - ->setId(2) - ->setAttributeSetId($attributeSetId) - ->setWebsiteIds([1]) - ->setName('Simple Product 2') - ->setSku('simple2') - ->setPrice(9.9) - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setStockData([ - 'use_config_manage_stock' => 1, - 'qty' => 100, - 'is_qty_decimal' => 0, - 'is_in_stock' => 1, - ]); -$option = array_shift($options); -$product->setTestConfigurable($option->getValue()); -$productRepository->save($product); -$attributeValues[] = [ - 'label' => 'test', - 'attribute_id' => $attribute->getId(), - 'value_index' => $option->getValue(), -]; -$associatedProductIds[] = $product->getId(); +foreach (['simple1', 'simple2'] as $sku) { + $option = array_shift($options); + $product = $productRepository->get($sku); + $product->setTestConfigurable($option->getValue()); + $productRepository->save($product); + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} $product = $objectManager->create(\Magento\Catalog\Model\Product::class) ->setTypeId('configurable') @@ -88,7 +47,9 @@ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setStockData([ 'use_config_manage_stock' => 1, - 'is_in_stock' => 0, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, ]); $configurableAttributesData = [ [ diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php new file mode 100644 index 0000000000000..d4311d523d3c7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var $objectManager \Magento\TestFramework\ObjectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('configurable', false, null, true); + $productRepository->delete($product); +} catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //Nothing to delete +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/simple_products_rollback.php'; +require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php new file mode 100644 index 0000000000000..2855ebb4c92b3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/attribute.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$storeManager = $objectManager->get(\Magento\Store\Model\StoreManager::class); +$store = $storeManager->getStore('default'); +$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +$installer = $objectManager->get(\Magento\Catalog\Setup\CategorySetup::class); +$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default'); + +$product = $objectManager->create(\Magento\Catalog\Model\Product::class) + ->setTypeId('simple') + ->setId(1) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Simple Product 1') + ->setSku('simple1') + ->setPrice(10) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData([ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ]); +$productRepository->save($product); +$productAction = $objectManager->get(\Magento\Catalog\Model\Product\Action::class); +$productAction->updateAttributes([$product->getId()], ['test_attribute' => 'test_attribute_value'], $store->getId()); + +$product = $objectManager->create(\Magento\Catalog\Model\Product::class) + ->setTypeId('simple') + ->setId(2) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Simple Product 2') + ->setSku('simple2') + ->setPrice(9.9) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData([ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php similarity index 87% rename from dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute_rollback.php rename to dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php index 0ce909a3f9ecd..6625b1926fc10 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php @@ -18,7 +18,7 @@ /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ $productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); -foreach (['simple1', 'simple2', 'configurable'] as $sku) { +foreach (['simple1', 'simple2'] as $sku) { try { $product = $productRepository->get($sku, false, null, true); $productRepository->delete($product); @@ -30,4 +30,4 @@ $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); -require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute_rollback.php'; +require __DIR__ . '/attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php index cf0da5599914f..86aa61a99e1e8 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php @@ -154,3 +154,10 @@ $product->setTypeHasRequiredOptions(false)->setRequiredOptions(false); } $product->save(); + +$stockRegistry = $objectManager->get(\Magento\CatalogInventory\Api\StockRegistryInterface::class); +$stockItem = $stockRegistry->getStockItem($product->getId()); +$stockItem->setUseConfigManageStock(true); +$stockItem->setQty(100); +$stockItem->setIsInStock(true); +$stockRegistry->updateStockItemBySku($product->getSku(), $stockItem); diff --git a/lib/internal/Magento/Framework/Indexer/Config/Reader.php b/lib/internal/Magento/Framework/Indexer/Config/Reader.php index 6ef22b3b7f796..9ed35ef0e9af5 100644 --- a/lib/internal/Magento/Framework/Indexer/Config/Reader.php +++ b/lib/internal/Magento/Framework/Indexer/Config/Reader.php @@ -18,6 +18,7 @@ class Reader extends \Magento\Framework\Config\Reader\Filesystem '/config/indexer/source' => 'name', '/config/indexer/fieldset' => 'name', '/config/indexer/fieldset/field' => 'name', + '/config/indexer/dependencies/indexer' => 'id', ]; /** From 1ddc94b84f357cb56916baaba326ccf65c8372a4 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Tue, 31 Jul 2018 11:30:02 +0300 Subject: [PATCH 0634/1171] MAGETWO-91335: Manufacturer attribute not appearing in Configurable Products on the backend --- .../Setup/Patch/Data/UpdateManufacturerAttribute.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/UpdateManufacturerAttribute.php b/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/UpdateManufacturerAttribute.php index 2ae3e697f1964..c9a2f7d373a63 100644 --- a/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/UpdateManufacturerAttribute.php +++ b/app/code/Magento/ConfigurableProduct/Setup/Patch/Data/UpdateManufacturerAttribute.php @@ -54,9 +54,9 @@ public function apply() ',', $eavSetup->getAttribute(\Magento\Catalog\Model\Product::ENTITY, 'manufacturer', 'apply_to') ); - $key = array_search(Configurable::TYPE_CODE, $relatedProductTypes); - if ($key !== false) { - unset($relatedProductTypes[$key]); + + if (!in_array(Configurable::TYPE_CODE, $relatedProductTypes)) { + $relatedProductTypes[] = Configurable::TYPE_CODE; $eavSetup->updateAttribute( \Magento\Catalog\Model\Product::ENTITY, 'manufacturer', From ba331504a24caf3f69acff6cef3d96e90c076590 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Tue, 31 Jul 2018 10:51:33 +0300 Subject: [PATCH 0635/1171] MAGETWO-93966: Run Catalog Search reindex in multithread mode with Elasticsearch 5 --- .../Model/Client/Elasticsearch.php | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php index c0ecaadaea504..30c9a78893e45 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Elasticsearch\Elasticsearch5\Model\Client; use Magento\Framework\Exception\LocalizedException; @@ -17,7 +16,7 @@ class Elasticsearch implements ClientInterface /** * Elasticsearch Client instance * - * @var \Elasticsearch\Client + * @var \Elasticsearch\Client[] */ protected $client; @@ -48,7 +47,7 @@ public function __construct( $elasticsearchClient = null ) { if (empty($options['hostname']) || ((!empty($options['enableAuth']) && - ($options['enableAuth'] == 1)) && (empty($options['username']) || empty($options['password'])))) { + ($options['enableAuth'] == 1)) && (empty($options['username']) || empty($options['password'])))) { throw new LocalizedException( __('The search failed because of a search engine misconfiguration.') ); @@ -58,10 +57,25 @@ public function __construct( $config = $this->buildConfig($options); $elasticsearchClient = \Elasticsearch\ClientBuilder::fromConfig($config, true); } - $this->client = $elasticsearchClient; + $this->client[getmypid()] = $elasticsearchClient; $this->clientOptions = $options; } + /** + * Get Elasticsearch Client + * + * @return \Elasticsearch\Client + */ + private function getClient() + { + $pid = getmypid(); + if (!isset($this->client[$pid])) { + $config = $this->buildConfig($this->clientOptions); + $this->client[$pid] = \Elasticsearch\ClientBuilder::fromConfig($config, true); + } + return $this->client[$pid]; + } + /** * Ping the Elasticsearch client * @@ -70,7 +84,7 @@ public function __construct( public function ping() { if ($this->pingResult === null) { - $this->pingResult = $this->client->ping(['client' => ['timeout' => $this->clientOptions['timeout']]]); + $this->pingResult = $this->getClient()->ping(['client' => ['timeout' => $this->clientOptions['timeout']]]); } return $this->pingResult; @@ -116,7 +130,7 @@ private function buildConfig($options = []) */ public function bulkQuery($query) { - $this->client->bulk($query); + $this->getClient()->bulk($query); } /** @@ -128,7 +142,7 @@ public function bulkQuery($query) */ public function createIndex($index, $settings) { - $this->client->indices()->create([ + $this->getClient()->indices()->create([ 'index' => $index, 'body' => $settings, ]); @@ -142,7 +156,7 @@ public function createIndex($index, $settings) */ public function deleteIndex($index) { - $this->client->indices()->delete(['index' => $index]); + $this->getClient()->indices()->delete(['index' => $index]); } /** @@ -153,7 +167,7 @@ public function deleteIndex($index) */ public function isEmptyIndex($index) { - $stats = $this->client->indices()->stats(['index' => $index, 'metric' => 'docs']); + $stats = $this->getClient()->indices()->stats(['index' => $index, 'metric' => 'docs']); if ($stats['indices'][$index]['primaries']['docs']['count'] == 0) { return true; } @@ -178,7 +192,7 @@ public function updateAlias($alias, $newIndex, $oldIndex = '') $params['body']['actions'][] = ['add' => ['alias' => $alias, 'index' => $newIndex]]; } - $this->client->indices()->updateAliases($params); + $this->getClient()->indices()->updateAliases($params); } /** @@ -189,7 +203,7 @@ public function updateAlias($alias, $newIndex, $oldIndex = '') */ public function indexExists($index) { - return $this->client->indices()->exists(['index' => $index]); + return $this->getClient()->indices()->exists(['index' => $index]); } /** @@ -204,7 +218,7 @@ public function existsAlias($alias, $index = '') if ($index) { $params['index'] = $index; } - return $this->client->indices()->existsAlias($params); + return $this->getClient()->indices()->existsAlias($params); } /** @@ -214,7 +228,7 @@ public function existsAlias($alias, $index = '') */ public function getAlias($alias) { - return $this->client->indices()->getAlias(['name' => $alias]); + return $this->getClient()->indices()->getAlias(['name' => $alias]); } /** @@ -274,7 +288,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) $params['body'][$entityType]['properties'][$field] = $this->prepareFieldInfo($fieldInfo); } - $this->client->indices()->putMapping($params); + $this->getClient()->indices()->putMapping($params); } /** @@ -311,7 +325,7 @@ private function prepareFieldInfo($fieldInfo) */ public function deleteMapping($index, $entityType) { - $this->client->indices()->deleteMapping([ + $this->getClient()->indices()->deleteMapping([ 'index' => $index, 'type' => $entityType, ]); @@ -327,7 +341,7 @@ public function query($query) { $query = $this->prepareSearchQuery($query); - return $this->client->search($query); + return $this->getClient()->search($query); } /** @@ -358,7 +372,7 @@ private function prepareSearchQuery($query) */ public function suggest($query) { - return $this->client->suggest($query); + return $this->getClient()->suggest($query); } /** @@ -369,7 +383,7 @@ public function suggest($query) private function getServerVersion() { if ($this->serverVersion === null) { - $info = $this->client->info(); + $info = $this->getClient()->info(); $this->serverVersion = $info['version']['number']; } From a8a7d27523ea49a4dab0dec19218686d49be3898 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Tue, 31 Jul 2018 11:44:50 +0300 Subject: [PATCH 0636/1171] MAGETWO-91813: Elasticsearch incorrect results relevance score --- .../FieldMapper/ProductFieldMapper.php | 10 +- .../FieldMapper/ProductFieldMapper.php | 18 ++ .../FieldMapper/ProductFieldMapperTest.php | 22 +- .../FieldMapper/ProductFieldMapperTest.php | 294 ++++++++++++++++++ 4 files changed, 331 insertions(+), 13 deletions(-) create mode 100644 app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/ProductFieldMapperTest.php diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php index 8590c9b58786e..dc8d73a7d556c 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php @@ -173,8 +173,14 @@ protected function isAttributeUsedInAdvancedSearch($attribute) */ protected function getRefinedFieldName($frontendInput, $fieldType, $attributeCode) { - return (in_array($frontendInput, ['select', 'boolean'], true) && $fieldType === 'integer') - ? $attributeCode . '_value' : $attributeCode; + switch ($frontendInput) { + case 'select': + return in_array($fieldType, ['text','integer'], true) ? $attributeCode . '_value' : $attributeCode; + case 'boolean': + return $fieldType === 'integer' ? $attributeCode . '_value' : $attributeCode; + default: + return $attributeCode; + } } /** diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php index d0258286b6484..582eea9c6d4ad 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php @@ -115,4 +115,22 @@ public function getAllAttributesTypes($context = []) return $allAttributes; } + + /** + * @param string $frontendInput + * @param string $fieldType + * @param string $attributeCode + * @return string + */ + protected function getRefinedFieldName($frontendInput, $fieldType, $attributeCode) + { + switch ($frontendInput) { + case 'select': + return in_array($fieldType, ['string','integer'], true) ? $attributeCode . '_value' : $attributeCode; + case 'boolean': + return $fieldType === 'integer' ? $attributeCode . '_value' : $attributeCode; + default: + return $attributeCode; + } + } } diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapperTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapperTest.php index 2128ee57681a8..d13e8237efb79 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapperTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapperTest.php @@ -227,17 +227,17 @@ public function testGetAllAttributesTypes($attributeCode, $inputType, $searchAtt public function attributeCodeProvider() { return [ - ['id', 'id', 'string'], - ['status', 'status', 'string'], - ['status', 'status', 'string', ['type'=>'default']], - ['price', 'price_0_1', 'string', ['type'=>'default']], - ['position', 'position_category_1', 'string', ['type'=>'default']], - ['price', 'price_2_3', 'string', ['type'=>'default', 'customerGroupId'=>'2', 'websiteId'=>'3']], - ['position', 'position_category_3', 'string', ['type'=>'default', 'categoryId'=>'3']], - ['color', 'color', 'select', ['type'=>'default']], - ['description', 'sort_description', 'string', ['type'=>'some']], - ['*', '_all', 'string', ['type'=>'text']], - ['description', 'description', 'string', ['type'=>'text']], + ['id', 'id', 'text'], + ['status', 'status', 'text'], + ['status', 'status_value', 'text', ['type'=>'default']], + ['price', 'price_0_1', 'text', ['type'=>'default']], + ['position', 'position_category_1', 'text', ['type'=>'default']], + ['price', 'price_2_3', 'text', ['type'=>'default', 'customerGroupId'=>'2', 'websiteId'=>'3']], + ['position', 'position_category_3', 'text', ['type'=>'default', 'categoryId'=>'3']], + ['color', 'color_value', 'text', ['type'=>'text']], + ['description', 'sort_description', 'text', ['type'=>'some']], + ['*', '_all', 'text', ['type'=>'text']], + ['description', 'description_value', 'text', ['type'=>'text']], ]; } diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/ProductFieldMapperTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/ProductFieldMapperTest.php new file mode 100644 index 0000000000000..a0c7d9c4df571 --- /dev/null +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/ProductFieldMapperTest.php @@ -0,0 +1,294 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Elasticsearch\Test\Unit\Model\Adapter\FieldMapper; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Elasticsearch\Model\Adapter\FieldType; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class ProductFieldMapperTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Elasticsearch\Model\Adapter\FieldMapper\ProductFieldMapper + */ + protected $mapper; + + /** + * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject + */ + protected $eavConfig; + + /** + * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + */ + protected $coreRegistry; + + /** + * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject + */ + protected $customerSession; + + /** + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $storeManager; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute|\PHPUnit_Framework_MockObject_MockObject + */ + protected $eavAttributeResource; + + /** + * @var FieldType|\PHPUnit_Framework_MockObject_MockObject + */ + protected $fieldType; + + /** + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $store; + + /** + * Set up test environment + * + * @return void + */ + protected function setUp() + { + $this->eavConfig = $this->getMockBuilder(\Magento\Eav\Model\Config::class) + ->disableOriginalConstructor() + ->setMethods(['getEntityType', 'getAttribute', 'getEntityAttributeCodes']) + ->getMock(); + + $this->fieldType = $this->getMockBuilder(FieldType::class) + ->disableOriginalConstructor() + ->setMethods(['getFieldType']) + ->getMock(); + + $this->customerSession = $this->getMockBuilder(\Magento\Customer\Model\Session::class) + ->disableOriginalConstructor() + ->setMethods(['getCustomerGroupId']) + ->getMock(); + + $this->storeManager = $this->storeManager = $this->getMockForAbstractClass( + \Magento\Store\Model\StoreManagerInterface::class, + [], + '', + false + ); + + $this->store = $this->getMockForAbstractClass( + \Magento\Store\Api\Data\StoreInterface::class, + [], + '', + false, + false, + true, + ['getWebsiteId', 'getRootCategoryId'] + ); + + $this->coreRegistry = $this->createMock(\Magento\Framework\Registry::class); + + $objectManager = new ObjectManagerHelper($this); + + $this->eavAttributeResource = $this->createPartialMock( + \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, + [ + '__wakeup', + 'getBackendType', + 'getFrontendInput' + ] + ); + + $this->mapper = $objectManager->getObject( + \Magento\Elasticsearch\Model\Adapter\FieldMapper\ProductFieldMapper::class, + [ + 'eavConfig' => $this->eavConfig, + 'storeManager' => $this->storeManager, + 'fieldType' => $this->fieldType, + 'customerSession' => $this->customerSession, + 'coreRegistry' => $this->coreRegistry + ] + ); + } + + /** + * @dataProvider attributeCodeProvider + * @param string $attributeCode + * @param string $fieldName + * @param string $fieldType + * @param array $context + * + * @return void + */ + public function testGetFieldName($attributeCode, $fieldName, $fieldType, $context = []) + { + $attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + ->setMethods(['getBackendType', 'getFrontendInput', 'getAttribute']) + ->disableOriginalConstructor() + ->getMock(); + + $this->customerSession->expects($this->any()) + ->method('getCustomerGroupId') + ->willReturn('0'); + + $this->storeManager->expects($this->any()) + ->method('getStore') + ->willReturn($this->store); + $this->store->expects($this->any()) + ->method('getWebsiteId') + ->willReturn('1'); + $this->store->expects($this->any()) + ->method('getRootCategoryId') + ->willReturn('1'); + + $this->eavConfig->expects($this->any())->method('getAttribute') + ->with(ProductAttributeInterface::ENTITY_TYPE_CODE, $attributeCode) + ->willReturn($attributeMock); + + $attributeMock->expects($this->any())->method('getFrontendInput') + ->will($this->returnValue('select')); + + $this->fieldType->expects($this->any())->method('getFieldType') + ->with($attributeMock) + ->willReturn($fieldType); + + $this->assertEquals( + $fieldName, + $this->mapper->getFieldName($attributeCode, $context) + ); + } + + /** + * @return void + */ + public function testGetFieldNameWithoutAttribute() + { + $this->eavConfig->expects($this->any())->method('getAttribute') + ->with(ProductAttributeInterface::ENTITY_TYPE_CODE, 'attr1') + ->willReturn(''); + + $this->assertEquals( + 'attr1', + $this->mapper->getFieldName('attr1', []) + ); + } + + /** + * @dataProvider attributeProvider + * @param string $attributeCode + * + * @return void + */ + public function testGetAllAttributesTypes($attributeCode, $inputType, $searchAttributes, $expected) + { + $attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->eavConfig->expects($this->any())->method('getEntityAttributeCodes') + ->with(ProductAttributeInterface::ENTITY_TYPE_CODE) + ->willReturn([$attributeCode]); + + $this->eavConfig->expects($this->any())->method('getAttribute') + ->with(ProductAttributeInterface::ENTITY_TYPE_CODE, $attributeCode) + ->willReturn($attributeMock); + + $this->fieldType->expects($this->once())->method('getFieldType')->willReturn(FieldType::ES_DATA_TYPE_INT); + + $attributeMock->expects($this->any()) + ->method('getIsSearchable') + ->willReturn($searchAttributes['searchable']); + $attributeMock->expects($this->any()) + ->method('getIsFilterable') + ->willReturn($searchAttributes['filterable']); + $attributeMock->expects($this->any()) + ->method('getIsFilterableInSearch') + ->willReturn($searchAttributes['filterableInSearch']); + $attributeMock->expects($this->any()) + ->method('getIsVisibleInAdvancedSearch') + ->willReturn($searchAttributes['advSearch']); + + $attributeMock->expects($this->any())->method('getFrontendInput') + ->will($this->returnValue($inputType)); + + $this->assertEquals( + $expected, + $this->mapper->getAllAttributesTypes() + ); + } + + /** + * @return array + */ + public function attributeCodeProvider() + { + return [ + ['id', 'id', 'string'], + ['status', 'status', 'string'], + ['status', 'status_value', 'string', ['type'=>'default']], + ['price', 'price_0_1', 'string', ['type'=>'default']], + ['position', 'position_category_1', 'string', ['type'=>'default']], + ['price', 'price_2_3', 'string', ['type'=>'default', 'customerGroupId'=>'2', 'websiteId'=>'3']], + ['position', 'position_category_3', 'string', ['type'=>'default', 'categoryId'=>'3']], + ['color', 'color_value', 'string', ['type'=>'text']], + ['description', 'sort_description', 'string', ['type'=>'some']], + ['*', '_all', 'string', ['type'=>'text']], + ['description', 'description_value', 'string', ['type'=>'text']], + ]; + } + + /** + * @return array + */ + public function attributeProvider() + { + return [ + [ + 'category_ids', + 'text', + ['searchable' => false, 'filterable' => false, 'filterableInSearch' => false, 'advSearch' => false], + ['category_ids' => ['type' => 'integer']] + ], + [ + 'attr_code', + 'string', + ['searchable' => false, 'filterable' => false, 'filterableInSearch' => false, 'advSearch' => false], + ['attr_code' => ['type' => 'integer', 'index' => 'no']] + ], + [ + 'attr_code', + 'string', + ['searchable' => '0', 'filterable' => '0', 'filterableInSearch' => '0', 'advSearch' => '0'], + ['attr_code' => ['type' => 'integer', 'index' => 'no']] + ], + [ + 'attr_code', + 'string', + ['searchable' => true, 'filterable' => false, 'filterableInSearch' => false, 'advSearch' => false], + ['attr_code' => ['type' => 'integer']] + ], + [ + 'attr_code', + 'string', + ['searchable' => '1', 'filterable' => '0', 'filterableInSearch' => '0', 'advSearch' => '0'], + ['attr_code' => ['type' => 'integer']] + ], + [ + 'attr_code', + 'string', + ['searchable' => false, 'filterable' => false, 'filterableInSearch' => false, 'advSearch' => true], + ['attr_code' => ['type' => 'integer']] + ], + [ + 'attr_code', + 'string', + ['searchable' => '0', 'filterable' => '0', 'filterableInSearch' => '1', 'advSearch' => '0'], + ['attr_code' => ['type' => 'integer']] + ], + ]; + } +} From 0fdd197b32002990e6d09017da9ff50ebd7b1508 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Fri, 25 May 2018 11:04:03 +0300 Subject: [PATCH 0637/1171] MAGETWO-91330: [B2B] The product total price value on the second website is as on the default website - reverted fix from community PR #11165 - issue #7582 is fixed via plugin on store view switching --- .../Magento/Quote/Model/QuoteRepository.php | 2 +- .../Magento/Quote/Plugin/UpdateQuoteStore.php | 69 ++++++++++ app/code/Magento/Quote/etc/frontend/di.xml | 18 +++ .../Model/Plugin/UpdateQuoteStoreTest.php | 130 ++++++++++++++++++ .../Quote/Model/QuoteRepositoryTest.php | 47 +++++++ 5 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Quote/Plugin/UpdateQuoteStore.php create mode 100644 app/code/Magento/Quote/etc/frontend/di.xml create mode 100644 dev/tests/integration/testsuite/Magento/Quote/Model/Plugin/UpdateQuoteStoreTest.php diff --git a/app/code/Magento/Quote/Model/QuoteRepository.php b/app/code/Magento/Quote/Model/QuoteRepository.php index d3967794b300a..01c21bbbe50a7 100644 --- a/app/code/Magento/Quote/Model/QuoteRepository.php +++ b/app/code/Magento/Quote/Model/QuoteRepository.php @@ -212,7 +212,7 @@ protected function loadQuote($loadMethod, $loadField, $identifier, array $shared if ($sharedStoreIds) { $quote->setSharedStoreIds($sharedStoreIds); } - $quote->$loadMethod($identifier)->setStoreId($this->storeManager->getStore()->getId()); + $quote->setStoreId($this->storeManager->getStore()->getId())->$loadMethod($identifier); if (!$quote->getId()) { throw NoSuchEntityException::singleField($loadField, $identifier); } diff --git a/app/code/Magento/Quote/Plugin/UpdateQuoteStore.php b/app/code/Magento/Quote/Plugin/UpdateQuoteStore.php new file mode 100644 index 0000000000000..de2fc76b9179f --- /dev/null +++ b/app/code/Magento/Quote/Plugin/UpdateQuoteStore.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Plugin; + +use Magento\Checkout\Model\Session; +use Magento\Quote\Model\QuoteRepository; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Api\StoreCookieManagerInterface; + +/** + * Updates quote store id. + */ +class UpdateQuoteStore +{ + /** + * @var QuoteRepository + */ + private $quoteRepository; + + /** + * @var Session + */ + private $checkoutSession; + + /** + * @param QuoteRepository $quoteRepository + * @param Session $checkoutSession + */ + public function __construct( + QuoteRepository $quoteRepository, + Session $checkoutSession + ) { + $this->quoteRepository = $quoteRepository; + $this->checkoutSession = $checkoutSession; + } + + /** + * Update store id in active quote after store view switching. + * + * @param StoreCookieManagerInterface $subject + * @param null $result + * @param StoreInterface $store + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterSetStoreCookie( + StoreCookieManagerInterface $subject, + $result, + StoreInterface $store + ) { + $storeCodeFromCookie = $subject->getStoreCodeFromCookie(); + if (null === $storeCodeFromCookie) { + return; + } + + $quote = $this->checkoutSession->getQuote(); + if ($quote->getIsActive() && $store->getCode() != $storeCodeFromCookie) { + $quote->setStoreId( + $store->getId() + ); + $this->quoteRepository->save($quote); + } + } +} diff --git a/app/code/Magento/Quote/etc/frontend/di.xml b/app/code/Magento/Quote/etc/frontend/di.xml new file mode 100644 index 0000000000000..25acd6763ba56 --- /dev/null +++ b/app/code/Magento/Quote/etc/frontend/di.xml @@ -0,0 +1,18 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Quote\Plugin\UpdateQuoteStore"> + <arguments> + <argument name="quoteRepository" xsi:type="object">Magento\Quote\Model\QuoteRepository\Proxy</argument> + <argument name="checkoutSession" xsi:type="object">Magento\Checkout\Model\Session\Proxy</argument> + </arguments> + </type> + <type name="Magento\Store\Api\StoreCookieManagerInterface"> + <plugin name="update_quote_store_after_switch_store_view" type="Magento\Quote\Plugin\UpdateQuoteStore"/> + </type> +</config> diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/Plugin/UpdateQuoteStoreTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/Plugin/UpdateQuoteStoreTest.php new file mode 100644 index 0000000000000..cbacab1139c10 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/Plugin/UpdateQuoteStoreTest.php @@ -0,0 +1,130 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Model\Plugin; + +use Magento\Checkout\Model\Session; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\FunctionalTestingFramework\ObjectManagerInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Api\StoreCookieManagerInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\StoreRepository; +use Magento\TestFramework\Helper\Bootstrap as BootstrapHelper; + +/** + * @magentoAppArea frontend + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class UpdateQuoteStoreTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var CartRepositoryInterface + */ + private $quoteRepository; + + protected function setUp() + { + $this->objectManager = BootstrapHelper::getObjectManager(); + $this->quoteRepository = $this->objectManager->create(CartRepositoryInterface::class); + } + + /** + * Tests that active quote store id updates after store cookie change. + * + * @magentoDataFixture Magento/Quote/_files/empty_quote.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @throws \ReflectionException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testUpdateQuoteStoreAfterChangeStoreCookie() + { + $secondStoreCode = 'fixture_second_store'; + $reservedOrderId = 'reserved_order_id'; + + /** @var StoreManagerInterface $storeManager */ + $storeManager = $this->objectManager->get(StoreManagerInterface::class); + $currentStore = $storeManager->getStore(); + + $quote = $this->getQuote($reservedOrderId); + $this->assertEquals( + $currentStore->getId(), + $quote->getStoreId(), + 'Current store id and quote store id are not match' + ); + + /** @var Session $checkoutSession */ + $checkoutSession = $this->objectManager->get(Session::class); + $checkoutSession->setQuoteId($quote->getId()); + + $storeRepository = $this->objectManager->create(StoreRepository::class); + $secondStore = $storeRepository->get($secondStoreCode); + + $storeCookieManager = $this->getStoreCookieManager($currentStore); + $storeCookieManager->setStoreCookie($secondStore); + + $updatedQuote = $this->getQuote($reservedOrderId); + $this->assertEquals( + $secondStore->getId(), + $updatedQuote->getStoreId(), + 'Active quote store id should be equal second store id' + ); + } + + /** + * Retrieves quote by reserved order id. + * + * @param string $reservedOrderId + * @return Quote + */ + private function getQuote(string $reservedOrderId): Quote + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + $items = $this->quoteRepository->getList($searchCriteria)->getItems(); + + return array_pop($items); + } + + /** + * Returns instance of StoreCookieManagerInterface with mocked cookieManager dependency. + * + * Mock is needed since integration test framework use own cookie manager with + * behavior that differs from real environment. + * + * @param $currentStore + * @return StoreCookieManagerInterface + * @throws \ReflectionException + */ + private function getStoreCookieManager(StoreInterface $currentStore): StoreCookieManagerInterface + { + /** @var StoreCookieManagerInterface $storeCookieManager */ + $storeCookieManager = $this->objectManager->get(StoreCookieManagerInterface::class); + $cookieManagerMock = $this->getMockBuilder(\Magento\Framework\Stdlib\Cookie\PhpCookieManager::class) + ->disableOriginalConstructor() + ->getMock(); + $cookieManagerMock->method('getCookie') + ->willReturn($currentStore->getCode()); + + $reflection = new \ReflectionClass($storeCookieManager); + $cookieManager = $reflection->getProperty('cookieManager'); + $cookieManager->setAccessible(true); + $cookieManager->setValue($storeCookieManager, $cookieManagerMock); + + return $storeCookieManager; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php index 88c66fedd10ed..3e093876349d0 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Quote\Model; +use Magento\Store\Model\StoreRepository; use Magento\TestFramework\Helper\Bootstrap as BootstrapHelper; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Api\SearchCriteriaBuilder; @@ -50,6 +51,34 @@ protected function setUp() $this->filterBuilder = $this->objectManager->create(FilterBuilder::class); } + /** + * Tests that quote saved with custom store id has same store id after getting via repository. + * + * @magentoDataFixture Magento/Sales/_files/quote.php + * @magentoDataFixture Magento/Store/_files/second_store.php + */ + public function testGetQuoteWithCustomStoreId() + { + $secondStoreCode = 'fixture_second_store'; + $reservedOrderId = 'test01'; + + $storeRepository = $this->objectManager->create(StoreRepository::class); + $secondStore = $storeRepository->get($secondStoreCode); + + // Set store_id in quote to second store_id + $quote = $this->getQuote($reservedOrderId); + $quote->setStoreId($secondStore->getId()); + $this->quoteRepository->save($quote); + + $savedQuote = $this->quoteRepository->get($quote->getId()); + + $this->assertEquals( + $secondStore->getId(), + $savedQuote->getStoreId(), + 'Quote store id should be equal with store id value in DB' + ); + } + /** * @magentoDataFixture Magento/Sales/_files/quote.php */ @@ -126,6 +155,24 @@ public function testSaveWithNotExistingCustomerAddress() ); } + /** + * Returns quote by reserved order id. + * + * @param string $reservedOrderId + * @return CartInterface + */ + private function getQuote(string $reservedOrderId) + { + $searchCriteria = $this->getSearchCriteria($reservedOrderId); + $searchResult = $this->quoteRepository->getList($searchCriteria); + $items = $searchResult->getItems(); + + /** @var CartInterface $quote */ + $quote = array_pop($items); + + return $quote; + } + /** * Get search criteria * From 31e0b819dbd4472a9863bbc820ca7f2d652fbbc7 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Thu, 31 May 2018 18:17:52 +0300 Subject: [PATCH 0638/1171] MAGETWO-91668: Cart rule using subselection condition incorrectly gives discount --- app/code/Magento/Quote/Model/Quote.php | 2 +- .../Model/Rule/Condition/ProductTest.php | 75 +++++++++++++++++++ .../_files/cart_rule_10_percent_off.php | 64 ++++++++++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_10_percent_off.php diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index 3b19c6495dd99..b7a1b7d563ef6 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -1399,7 +1399,7 @@ public function getAllVisibleItems() { $items = []; foreach ($this->getItemsCollection() as $item) { - if (!$item->isDeleted() && !$item->getParentItemId()) { + if (!$item->isDeleted() && !$item->getParentItemId() && !$item->getParentItem()) { $items[] = $item; } } diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php index 98826adeb2147..00f1b1f451815 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php @@ -6,6 +6,14 @@ namespace Magento\SalesRule\Model\Rule\Condition; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Quote\Api\Data\CartInterface; +use Magento\SalesRule\Api\RuleRepositoryInterface; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class ProductTest extends \PHPUnit\Framework\TestCase { /** @@ -98,4 +106,71 @@ public function validateProductConditionDataProvider() ] ]; } + + /** + * Ensure that SalesRules filtering on quote items quantity validates configurable product correctly + * + * 1. Load a quote with a configured product and a sales rule set to filter items with quantity 2. + * 2. Attempt to validate the sales rule against the quote and assert the output is negative. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/quote_with_configurable_product.php + * @magentoDataFixture Magento/SalesRule/_files/cart_rule_10_percent_off.php + */ + public function testValidateQtySalesRuleWithConfigurable() + { + // Load the quote that contains a child of a configurable product with quantity 1. + $quote = $this->getQuote('test_cart_with_configurable'); + + // Load the SalesRule looking for products with quantity 2. + $rule = $this->getSalesRule('10% Off on orders with two items'); + + $this->assertFalse( + $rule->validate($quote->getBillingAddress()) + ); + } + + /** + * Gets quote by reserved order id. + * + * @param string $reservedOrderId + * @return CartInterface + */ + private function getQuote($reservedOrderId) + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria)->getItems(); + return array_pop($items); + } + + /** + * Gets rule by name. + * + * @param string $name + * @return \Magento\SalesRule\Model\Rule + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getSalesRule(string $name): \Magento\SalesRule\Model\Rule + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('name', $name) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $ruleRepository = $this->objectManager->get(RuleRepositoryInterface::class); + $items = $ruleRepository->getList($searchCriteria)->getItems(); + + $rule = array_pop($items); + /** @var \Magento\SalesRule\Model\Converter\ToModel $converter */ + $converter = $this->objectManager->get(\Magento\SalesRule\Model\Converter\ToModel::class); + + return $converter->toModel($rule); + } } diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_10_percent_off.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_10_percent_off.php new file mode 100644 index 0000000000000..0761e5753da75 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_10_percent_off.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Model\GroupManagement; +use Magento\SalesRule\Model\Rule; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +$websiteId = Bootstrap::getObjectManager()->get(StoreManagerInterface::class) + ->getWebsite() + ->getId(); + +/** @var Rule $salesRule */ +$salesRule = $objectManager->create(Rule::class); +$salesRule->setData( + [ + 'name' => '10% Off on orders with two items', + 'is_active' => 1, + 'customer_group_ids' => [GroupManagement::NOT_LOGGED_IN_ID], + 'coupon_type' => Rule::COUPON_TYPE_NO_COUPON, + 'simple_action' => 'by_percent', + 'discount_amount' => 10, + 'discount_step' => 0, + 'stop_rules_processing' => 1, + 'website_ids' => [$websiteId] + ] +); + +$salesRule->getConditions()->loadArray([ + 'type' => \Magento\SalesRule\Model\Rule\Condition\Combine::class, + 'attribute' => null, + 'operator' => null, + 'value' => '1', + 'is_value_processed' => null, + 'aggregator' => 'all', + 'conditions' => + [ + [ + 'type' => \Magento\SalesRule\Model\Rule\Condition\Product\Subselect::class, + 'attribute' => 'qty', + 'operator' => '==', + 'value' => '2', + 'is_value_processed' => null, + 'aggregator' => 'all', + 'conditions' => + [ + [ + 'type' => \Magento\SalesRule\Model\Rule\Condition\Product::class, + 'attribute' => 'attribute_set_id', + 'operator' => '==', + 'value' => '4', + 'is_value_processed' => false, + ], + ], + ], + ], +]); + +$salesRule->save(); From 21fcce9bb669791b3977d78d05694499eb985062 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Fri, 1 Jun 2018 10:43:06 +0300 Subject: [PATCH 0639/1171] MAGETWO-93788: Google Analytics - addToCart event triggers before updating the cart --- app/code/Magento/Braintree/i18n/de_DE.csv | 193 +++++++++++++++++ .../Controller/Cart/UpdateItemQty.php | 161 ++++++++++++++ .../Checkout/Controller/Cart/UpdatePost.php | 49 ++++- .../Model/Cart/RequestQuantityProcessor.php | 48 +++++ .../Cart/RequestQuantityProcessorTest.php | 88 ++++++++ .../view/frontend/templates/cart/form.phtml | 4 +- .../web/js/action/update-shopping-cart.js | 127 ++++++++++++ .../Controller/Checkout/CheckItems.php | 196 ++++++++++++++++++ app/code/Magento/Multishipping/i18n/en_US.csv | 3 +- .../view/frontend/requirejs-config.js | 3 +- .../templates/checkout/addresses.phtml | 99 +++++++-- .../Controller/Cart/UpdateItemQtyTest.php | 120 +++++++++++ .../Controller/Checkout/CheckItemsTest.php | 176 ++++++++++++++++ 13 files changed, 1234 insertions(+), 33 deletions(-) create mode 100644 app/code/Magento/Braintree/i18n/de_DE.csv create mode 100644 app/code/Magento/Checkout/Controller/Cart/UpdateItemQty.php create mode 100644 app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php create mode 100644 app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestQuantityProcessorTest.php create mode 100644 app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js create mode 100644 app/code/Magento/Multishipping/Controller/Checkout/CheckItems.php create mode 100644 dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Multishipping/Controller/Checkout/CheckItemsTest.php diff --git a/app/code/Magento/Braintree/i18n/de_DE.csv b/app/code/Magento/Braintree/i18n/de_DE.csv new file mode 100644 index 0000000000000..d55233159689d --- /dev/null +++ b/app/code/Magento/Braintree/i18n/de_DE.csv @@ -0,0 +1,193 @@ +cc_type,"Credit Card Type" +cc_number,"Credit Card Number" +avsPostalCodeResponseCode,"AVS Postal Code Response Code" +avsStreetAddressResponseCode,"AVS Street Address Response Code" +cvvResponseCode,"CVV Response Code" +processorAuthorizationCode,"Processor Authorization Code" +processorResponseCode,"Processor Response Code" +processorResponseText,"Processor Response Text" +braintree,Braintree +liabilityShifted,"Liability Shifted" +liabilityShiftPossible,"Liability Shift Possible" +riskDataId,"Risk ID" +riskDataDecision,"Risk Decision" +paymentId,"Payment Id" +payerEmail,"Payer Email" +sale,Sale +credit,Credit +authorization_expired,"Authorization expired" +authorizing,Authorizing +authorized,Authorized +gateway_rejected,"Gateway rejected" +failed,Failed +processor_declined,"Processor declined" +settled,Settled +settling,Settling +submitted_for_settlement,"Submitted for settlement" +voided,Voided +unrecognized,Unrecognized +settlement_declined,"Settlement declined" +settlement_pending,"Settlement pending" +settlement_confirmed,"Settlement confirmed" +paypal_account,"Paypal account" +coinbase_account,"Coinbase account" +europe_bank_accout,"Europe bank account" +credit_card,"Credit card" +apple_pay_card,"Apple pay card" +android_pay_card,"Android pay card" +Country,Country +"Allowed Credit Card Types","Allowed Credit Card Types" +"Add Rule","Add Rule" +"Braintree Settlement Report","Braintree Settlement Report" +"Sorry, but something went wrong","Sorry, but something went wrong" +"We can't initialize checkout.","We can't initialize checkout." +"No authorization transaction to proceed capture.","No authorization transaction to proceed capture." +"Braintree error response.","Braintree error response." +"Payment method nonce can't be retrieved.","Payment method nonce can't be retrieved." +"Wrong transaction status","Wrong transaction status" +Authorize,Authorize +"Authorize and Capture","Authorize and Capture" +"--Please Select--","--Please Select--" +"Please agree to all the terms and conditions before placing the order.","Please agree to all the terms and conditions before placing the order." +"Credit Card Type","Credit Card Type" +"Credit Card Number","Credit Card Number" +"Please, enter valid Credit Card Number","Please, enter valid Credit Card Number" +"Expiration Date","Expiration Date" +"Please, enter valid Expiration Date","Please, enter valid Expiration Date" +"Card Verification Number","Card Verification Number" +"Please, enter valid Card Verification Number","Please, enter valid Card Verification Number" +ending,ending +expires,expires +"Are you sure you want to delete this PayPal account","Are you sure you want to delete this PayPal account" +"PayPal Account","PayPal Account" +"PayPal Logo","PayPal Logo" +Actions,Actions +Delete,Delete +"Credit Card Information","Credit Card Information" +"What is this?","What is this?" +"Save for later use.","Save for later use." +"Place Order","Place Order" +"This payment is not available","This payment is not available" +MM,MM +YY,YY +"Please try again with another form of payment.","---Bitte versuchen Sie es erneut mit einer anderen Zahlungsmethode@" +"Sorry, but something went wrong.","Sorry, but something went wrong." +"Payment ","Payment " +Braintree,Braintree +"Accept credit/debit cards and PayPal in your Magento store.<br/>No setup or monthly fees and your customers never leave your store to complete the purchase.","Accept credit/debit cards and PayPal in your Magento store.<br/>No setup or monthly fees and your customers never leave your store to complete the purchase." +"Enable this Solution","Enable this Solution" +"Enable PayPal through Braintree","Enable PayPal through Braintree" +"Vault Enabled","Vault Enabled" +"Basic Braintree Settings","Basic Braintree Settings" +Title,Title +Environment,Environment +"Payment Action","Payment Action" +"Merchant ID","Merchant ID" +"Public Key","Public Key" +"Private Key","Private Key" +"Advanced Braintree Settings","Advanced Braintree Settings" +"Vault Title","Vault Title" +"Merchant Account ID","Merchant Account ID" +"Advanced Fraud Protection","Advanced Fraud Protection" +"Kount Merchant ID","Kount Merchant ID" +Debug,Debug +"CVV Verification","CVV Verification" +"Credit Card Types","Credit Card Types" +"Sort Order","Sort Order" +"Country Specific Settings","Country Specific Settings" +"Payment from Applicable Countries","Payment from Applicable Countries" +"Payment from Specific Countries","Payment from Specific Countries" +"Country Specific Credit Card Types","Country Specific Credit Card Types" +"PayPal through Braintree","PayPal through Braintree" +"Override Merchant Name","Override Merchant Name" +"Require Customer's Billing Address","Require Customer's Billing Address" +"Allow to Edit Shipping Address Entered During Checkout on PayPal Side","Allow to Edit Shipping Address Entered During Checkout on PayPal Side" +"Display on Shopping Cart","Display on Shopping Cart" +"Skip Order Review","Skip Order Review" +"3D Secure Verification Settings","3D Secure Verification Settings" +"3D Secure Verification","3D Secure Verification" +"Threshold Amount","Threshold Amount" +"Verify for Applicable Countries","Verify for Applicable Countries" +"Verify for Specific Countries","Verify for Specific Countries" +"Dynamic Descriptors","Dynamic Descriptors" +Name,Name +Phone,Phone +URL,URL +"Apply filters in order to get results. Only first 100 records will be displayed in the grid, you will be able to download full version of the report in .csv format.","Apply filters in order to get results. Only first 100 records will be displayed in the grid, you will be able to download full version of the report in .csv format." +Select...,Select... +Status,Status +"Transaction Type","Transaction Type" +"Payment Type","Payment Type" +"PayPal Payment ID","PayPal Payment ID" +"Created At","Created At" +"Transaction ID","Transaction ID" +"Order ID","Order ID" +Type,Type +Amount,Amount +"Settlement Code","Settlement Code" +"Settlement Response Text","Settlement Response Text" +"Refund Ids","Refund Ids" +"Settlement Batch ID","Settlement Batch ID" +Currency,Currency +"Addresses must have at least one field filled in.","Addresses must have at least one field filled in." +"Company is too long.","Company is too long." +"Extended address is too long.","Extended address is too long." +"First name is too long.","First name is too long." +"Last name is too long.","Last name is too long." +"Locality is too long.","Locality is too long." +"Postal code can only contain letters, numbers, spaces, and hyphens.","Postal code can only contain letters, numbers, spaces, and hyphens." +"Postal code is required.","Postal code is required." +"Postal code may contain no more than 9 letter or number characters.","Postal code may contain no more than 9 letter or number characters." +"Region is too long.","Region is too long." +"Street address is required.","Street address is required." +"Street address is too long.","Street address is too long." +"US state codes must be two characters to meet PayPal Seller Protection requirements.","US state codes must be two characters to meet PayPal Seller Protection requirements." +"Country name is not an accepted country.","Country name is not an accepted country." +"Provided country information is inconsistent.","Provided country information is inconsistent." +"Country code is not accepted. Please contact the store administrator.","Country code is not accepted. Please contact the store administrator." +"Customer has already reached the maximum of 50 addresses.","Customer has already reached the maximum of 50 addresses." +"Address is invalid. Please contact the store administrator.","Address is invalid. Please contact the store administrator." +"Address is invalid.","Address is invalid." +"Billing address format is invalid.","Billing address format is invalid." +"Cardholder name is too long.","Cardholder name is too long." +"Credit card type is not accepted by this merchant account.","Credit card type is not accepted by this merchant account." +"CVV is required.","CVV is required." +"CVV must be 4 digits for American Express and 3 digits for other card types.","CVV must be 4 digits for American Express and 3 digits for other card types." +"Expiration date is required.","Expiration date is required." +"Expiration date is invalid.","Expiration date is invalid." +"Expiration year is invalid. It must be between 1975 and 2201.","Expiration year is invalid. It must be between 1975 and 2201." +"Expiration month is invalid.","Expiration month is invalid." +"Expiration year is invalid.","Expiration year is invalid." +"Credit card number is required.","Credit card number is required." +"Credit card number is invalid.","Credit card number is invalid." +"Credit card number must be 12-19 digits.","Credit card number must be 12-19 digits." +"CVV verification failed.","CVV verification failed." +"Postal code verification failed.","Postal code verification failed." +"Credit card number is prohibited.","Credit card number is prohibited." +"Incomplete PayPal account information.","Incomplete PayPal account information." +"Invalid PayPal account information.","Invalid PayPal account information." +"PayPal Accounts are not accepted by this merchant account.","PayPal Accounts are not accepted by this merchant account." +"Error communicating with PayPal.","Error communicating with PayPal." +"PayPal authentication expired.","PayPal authentication expired." +"Error executing PayPal order.","Error executing PayPal order." +"Error executing PayPal billing agreement.","Error executing PayPal billing agreement." +"Cannot provide a billing address unless also providing a credit card.","Cannot provide a billing address unless also providing a credit card." +"Transaction can only be voided if status is authorized, submitted_for_settlement, or - for PayPal - settlement_pending.","Transaction can only be voided if status is authorized, submitted_for_settlement, or - for PayPal - settlement_pending." +"Credit transactions cannot be refunded.","Credit transactions cannot be refunded." +"Cannot refund a transaction unless it is settled.","Cannot refund a transaction unless it is settled." +"Cannot submit for settlement unless status is authorized.","Cannot submit for settlement unless status is authorized." +"Customer does not have any credit cards.","Customer does not have any credit cards." +"Transaction has already been completely refunded.","Transaction has already been completely refunded." +"Payment instrument type is not accepted by this merchant account.","Payment instrument type is not accepted by this merchant account." +"Processor authorization code cannot be set unless for a voice authorization.","Processor authorization code cannot be set unless for a voice authorization." +"Refund amount is too large.","Refund amount is too large." +"Settlement amount is too large.","Settlement amount is too large." +"Cannot refund a transaction with a suspended merchant account.","Cannot refund a transaction with a suspended merchant account." +"Merchant account does not support refunds.","Merchant account does not support refunds." +"PayPal is not enabled for your merchant account.","PayPal is not enabled for your merchant account." +"Cannot refund a transaction transaction in settling status on this merchant account. Try again after the transaction has settled.","Cannot refund a transaction transaction in settling status on this merchant account. Try again after the transaction has settled." +"Cannot submit for partial settlement.","Cannot submit for partial settlement." +"Partial settlements are not supported by this processor.","Partial settlements are not supported by this processor." +"Transaction can not be voided if status of a PayPal partial settlement child transaction is settlement_pending.","Transaction can not be voided if status of a PayPal partial settlement child transaction is settlement_pending." +"Too many concurrent attempts to refund this transaction. Try again later.","Too many concurrent attempts to refund this transaction. Try again later." +"Too many concurrent attempts to void this transaction. Try again later.","Too many concurrent attempts to void this transaction. Try again later." \ No newline at end of file diff --git a/app/code/Magento/Checkout/Controller/Cart/UpdateItemQty.php b/app/code/Magento/Checkout/Controller/Cart/UpdateItemQty.php new file mode 100644 index 0000000000000..ac4a93e6066a4 --- /dev/null +++ b/app/code/Magento/Checkout/Controller/Cart/UpdateItemQty.php @@ -0,0 +1,161 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Controller\Cart; + +use Magento\Checkout\Model\Cart\RequestQuantityProcessor; +use Magento\Framework\App\Action\Context; +use Magento\Framework\Exception\LocalizedException; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Data\Form\FormKey\Validator as FormKeyValidator; +use Magento\Quote\Model\Quote\Item; +use Psr\Log\LoggerInterface; + +class UpdateItemQty extends \Magento\Framework\App\Action\Action +{ + /** + * @var RequestQuantityProcessor + */ + private $quantityProcessor; + + /** + * @var FormKeyValidator + */ + private $formKeyValidator; + + /** + * @var CheckoutSession + */ + private $checkoutSession; + + /** + * @var Json + */ + private $json; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param Context $context, + * @param RequestQuantityProcessor $quantityProcessor + * @param FormKeyValidator $formKeyValidator + * @param CheckoutSession $checkoutSession + * @param Json $json + * @param LoggerInterface $logger + */ + public function __construct( + Context $context, + RequestQuantityProcessor $quantityProcessor, + FormKeyValidator $formKeyValidator, + CheckoutSession $checkoutSession, + Json $json, + LoggerInterface $logger + ) { + $this->quantityProcessor = $quantityProcessor; + $this->formKeyValidator = $formKeyValidator; + $this->checkoutSession = $checkoutSession; + $this->json = $json; + $this->logger = $logger; + parent::__construct($context); + } + + /** + * @return void + */ + public function execute() + { + try { + if (!$this->formKeyValidator->validate($this->getRequest())) { + throw new LocalizedException( + __('Something went wrong while saving the page. Please refresh the page and try again.') + ); + } + + $cartData = $this->getRequest()->getParam('cart'); + if (!is_array($cartData)) { + throw new LocalizedException( + __('Something went wrong while saving the page. Please refresh the page and try again.') + ); + } + + $cartData = $this->quantityProcessor->process($cartData); + $quote = $this->checkoutSession->getQuote(); + + foreach ($cartData as $itemId => $itemInfo) { + $item = $quote->getItemById($itemId); + $qty = isset($itemInfo['qty']) ? (double)$itemInfo['qty'] : 0; + if ($item) { + $this->updateItemQuantity($item, $qty); + } + } + + $this->jsonResponse(); + } catch (LocalizedException $e) { + $this->jsonResponse($e->getMessage()); + } catch (\Exception $e) { + $this->logger->critical($e->getMessage()); + $this->jsonResponse('Something went wrong while saving the page. Please refresh the page and try again.'); + } + } + + /** + * Updates quote item quantity. + * + * @param Item $item + * @param float $qty + * @throws LocalizedException + */ + private function updateItemQuantity(Item $item, float $qty) + { + if ($qty > 0) { + $item->setQty($qty); + + if ($item->getHasError()) { + throw new LocalizedException(__($item->getMessage())); + } + } + } + + /** + * JSON response builder. + * + * @param string $error + * @return void + */ + private function jsonResponse(string $error = '') + { + $this->getResponse()->representJson( + $this->json->serialize($this->getResponseData($error)) + ); + } + + /** + * Returns response data. + * + * @param string $error + * @return array + */ + private function getResponseData(string $error = ''): array + { + $response = [ + 'success' => true, + ]; + + if (!empty($error)) { + $response = [ + 'success' => false, + 'error_message' => $error, + ]; + } + + return $response; + } +} diff --git a/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php b/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php index 174cb38b0e9a9..05ce48c9b46ce 100644 --- a/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php +++ b/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php @@ -6,8 +6,45 @@ */ namespace Magento\Checkout\Controller\Cart; +use Magento\Checkout\Model\Cart\RequestQuantityProcessor; + class UpdatePost extends \Magento\Checkout\Controller\Cart { + /** + * @var RequestQuantityProcessor + */ + private $quantityProcessor; + + /** + * @param \Magento\Framework\App\Action\Context $context + * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param \Magento\Checkout\Model\Session $checkoutSession + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator + * @param \Magento\Checkout\Model\Cart $cart + * @param RequestQuantityProcessor $quantityProcessor + */ + public function __construct( + \Magento\Framework\App\Action\Context $context, + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + \Magento\Checkout\Model\Session $checkoutSession, + \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator, + \Magento\Checkout\Model\Cart $cart, + RequestQuantityProcessor $quantityProcessor = null + ) { + parent::__construct( + $context, + $scopeConfig, + $checkoutSession, + $storeManager, + $formKeyValidator, + $cart + ); + + $this->quantityProcessor = $quantityProcessor ?: $this->_objectManager->get(RequestQuantityProcessor::class); + } + /** * Empty customer's shopping cart * @@ -34,20 +71,10 @@ protected function _updateShoppingCart() try { $cartData = $this->getRequest()->getParam('cart'); if (is_array($cartData)) { - $filter = new \Zend_Filter_LocalizedToNormalized( - ['locale' => $this->_objectManager->get( - \Magento\Framework\Locale\ResolverInterface::class - )->getLocale()] - ); - foreach ($cartData as $index => $data) { - if (isset($data['qty'])) { - $cartData[$index]['qty'] = $filter->filter(trim($data['qty'])); - } - } if (!$this->cart->getCustomerSession()->getCustomerId() && $this->cart->getQuote()->getCustomerId()) { $this->cart->getQuote()->setCustomerId(null); } - + $cartData = $this->quantityProcessor->process($cartData); $cartData = $this->cart->suggestItemsQty($cartData); $this->cart->updateItems($cartData)->save(); } diff --git a/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php b/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php new file mode 100644 index 0000000000000..971b35c8f3e3d --- /dev/null +++ b/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Model\Cart; + +use Magento\Framework\Locale\ResolverInterface; + +class RequestQuantityProcessor +{ + /** + * @var ResolverInterface + */ + private $localeResolver; + + /** + * RequestQuantityProcessor constructor. + * @param ResolverInterface $localeResolver + */ + public function __construct( + ResolverInterface $localeResolver + ) { + $this->localeResolver = $localeResolver; + } + + /** + * Process cart request data + * + * @param array $cartData + * @return array + */ + public function process(array $cartData): array + { + $filter = new \Zend\I18n\Filter\NumberParse($this->localeResolver->getLocale()); + + foreach ($cartData as $index => $data) { + if (isset($data['qty'])) { + $data['qty'] = is_string($data['qty']) ? trim($data['qty']) : $data['qty']; + $cartData[$index]['qty'] = $filter->filter($data['qty']); + } + } + + return $cartData; + } +} diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestQuantityProcessorTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestQuantityProcessorTest.php new file mode 100644 index 0000000000000..daabb080b1c9a --- /dev/null +++ b/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestQuantityProcessorTest.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Test\Unit\Model\Cart; + +use Magento\Checkout\Model\Cart\RequestQuantityProcessor; +use Magento\Framework\Locale\ResolverInterface; +use \PHPUnit_Framework_MockObject_MockObject as MockObject; + +class RequestQuantityProcessorTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ResolverInterface | MockObject + */ + private $localeResolver; + + /** + * @var RequestQuantityProcessor + */ + private $requestProcessor; + + protected function setUp() + { + $this->localeResolver = $this->getMockBuilder(ResolverInterface::class) + ->getMockForAbstractClass(); + + $this->localeResolver->method('getLocale') + ->willReturn('en_US'); + + $this->requestProcessor = new RequestQuantityProcessor( + $this->localeResolver + ); + } + + /** + * Test of cart data processing. + * + * @param array $cartData + * @param array $expected + * @dataProvider cartDataProvider + */ + public function testProcess($cartData, $expected) + { + $this->assertEquals($this->requestProcessor->process($cartData), $expected); + } + + public function cartDataProvider() + { + return [ + 'empty_array' => [ + 'cartData' => [], + 'expected' => [], + ], + 'strings_array' => [ + 'cartData' => [ + ['qty' => ' 10 '], + ['qty' => ' 0.5 '] + ], + 'expected' => [ + ['qty' => 10], + ['qty' => 0.5] + ], + ], + 'integer_array' => [ + 'cartData' => [ + ['qty' => 1], + ['qty' => 0.002] + ], + 'expected' => [ + ['qty' => 1], + ['qty' => 0.002] + ], + ], + 'array_of arrays' => [ + 'cartData' => [ + ['qty' => [1, 2 ,3]], + ], + 'expected' => [ + ['qty' => [1, 2, 3]], + ], + ], + ]; + } +} diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml index 4940cd7f26747..71b1392d5391f 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml @@ -13,7 +13,9 @@ <form action="<?= /* @escapeNotVerified */ $block->getUrl('checkout/cart/updatePost') ?>" method="post" id="form-validate" - data-mage-init='{"validation":{}}' + data-mage-init='{"Magento_Checkout/js/action/update-shopping-cart": + {"validationURL" : "/checkout/cart/updateItemQty"} + }' class="form form-cart"> <?= $block->getBlockHtml('formkey') ?> <div class="cart table-wrapper<?= $mergedCells == 2 ? ' detailed' : '' ?>"> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js new file mode 100644 index 0000000000000..ce1527b3d72d6 --- /dev/null +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js @@ -0,0 +1,127 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Ui/js/modal/alert', + 'jquery', + 'jquery/ui', + 'mage/validation' +], function (alert, $) { + 'use strict'; + + $.widget('mage.updateShoppingCart', { + options: { + validationURL: '', + eventName: 'updateCartItemQty' + }, + + /** @inheritdoc */ + _create: function () { + this._on(this.element, { + 'submit': this.onSubmit + }); + }, + + /** + * Prevents default submit action and calls form validator. + * + * @param {Event} event + * @return {Boolean} + */ + onSubmit: function (event) { + if (!this.options.validationURL) { + return true; + } + + if (this.isValid()) { + event.preventDefault(); + this.validateItems(this.options.validationURL, this.element.serialize()); + } + + return false; + }, + + /** + * Validates requested form. + * + * @return {Boolean} + */ + isValid: function () { + return this.element.validation() && this.element.validation('isValid'); + }, + + /** + * Validates updated shopping cart data. + * + * @param {String} url - request url + * @param {Object} data - post data for ajax call + */ + validateItems: function (url, data) { + $.extend(data, { + 'form_key': $.mage.cookies.get('form_key') + }); + + $.ajax({ + url: url, + data: data, + type: 'post', + dataType: 'json', + context: this, + + /** @inheritdoc */ + beforeSend: function () { + $(document.body).trigger('processStart'); + }, + + /** @inheritdoc */ + complete: function () { + $(document.body).trigger('processStop'); + } + }) + .done(function (response) { + if (response.success) { + this.onSuccess(); + } else { + this.onError(response); + } + }) + .fail(function () { + this.submitForm(); + }); + }, + + /** + * Form validation succeed. + */ + onSuccess: function () { + $(document).trigger('ajax:' + this.options.eventName); + this.submitForm(); + }, + + /** + * Form validation failed. + */ + onError: function (response) { + if (response['error_message']) { + alert({ + content: response['error_message'] + }); + } else { + this.submitForm(); + } + }, + + /** + * Real submit of validated form. + */ + submitForm: function () { + this.element + .off('submit', this.onSubmit) + .submit(); + } + }); + + return $.mage.updateShoppingCart; +}); diff --git a/app/code/Magento/Multishipping/Controller/Checkout/CheckItems.php b/app/code/Magento/Multishipping/Controller/Checkout/CheckItems.php new file mode 100644 index 0000000000000..e9a46f1cf00f7 --- /dev/null +++ b/app/code/Magento/Multishipping/Controller/Checkout/CheckItems.php @@ -0,0 +1,196 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Multishipping\Controller\Checkout; + +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\App\Action\Context; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Multishipping\Controller\Checkout; +use Magento\Multishipping\Helper\Data as MultishippingHelper; +use Magento\Quote\Model\Quote\Item; +use Psr\Log\LoggerInterface; + +class CheckItems extends Checkout +{ + /** + * @var CheckoutSession + */ + private $checkoutSession; + + /** + * @var MultishippingHelper + */ + private $helper; + + /** + * @var Json + */ + private $json; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param Context $context, + * @param CustomerSession $customerSession + * @param CustomerRepositoryInterface $customerRepository + * @param AccountManagementInterface $accountManagement + * @param CheckoutSession $checkoutSession + * @param MultishippingHelper $helper + * @param Json $json + * @param LoggerInterface $logger + */ + + public function __construct( + Context $context, + CustomerSession $customerSession, + CustomerRepositoryInterface $customerRepository, + AccountManagementInterface $accountManagement, + CheckoutSession $checkoutSession, + MultishippingHelper $helper, + Json $json, + LoggerInterface $logger + ) { + $this->checkoutSession = $checkoutSession; + $this->helper = $helper; + $this->json = $json; + $this->logger = $logger; + + parent::__construct( + $context, + $customerSession, + $customerRepository, + $accountManagement + ); + } + + /** + * @return void + */ + public function execute() + { + try { + $shippingInfo = $this->getRequest()->getPost('ship'); + if (!\is_array($shippingInfo)) { + throw new LocalizedException( + __('We are unable to process your request. Please, try again later.') + ); + } + + $itemsInfo = $this->collectItemsInfo($shippingInfo); + $totalQuantity = array_sum($itemsInfo); + + $maxQuantity = $this->helper->getMaximumQty(); + if ($totalQuantity > $maxQuantity) { + throw new LocalizedException( + __('Maximum qty allowed for Shipping to multiple addresses is %1', $maxQuantity) + ); + } + + $quote = $this->checkoutSession->getQuote(); + foreach ($quote->getAllItems() as $item) { + if (isset($itemsInfo[$item->getId()])) { + $this->updateItemQuantity($item, $itemsInfo[$item->getId()]); + } + } + + if ($quote->getHasError()) { + throw new LocalizedException(__($quote->getMessage())); + } + + $this->jsonResponse(); + } catch (LocalizedException $e) { + $this->jsonResponse($e->getMessage()); + } catch (\Exception $e) { + $this->logger->critical($e->getMessage()); + $this->jsonResponse('We are unable to process your request. Please, try again later.'); + } + } + + /** + * Updates quote item quantity. + * + * @param Item $item + * @param float $quantity + * @throws LocalizedException + */ + private function updateItemQuantity(Item $item, float $quantity) + { + if ($quantity > 0) { + $item->setQty($quantity); + if ($item->getHasError()) { + throw new LocalizedException(__($item->getMessage())); + } + } + } + + /** + * Group posted items. + * + * @param array $shippingInfo + * @return array + */ + private function collectItemsInfo(array $shippingInfo): array + { + $itemsInfo = []; + foreach ($shippingInfo as $itemData) { + if (!\is_array($itemData)) { + continue; + } + foreach ($itemData as $quoteItemId => $data) { + if (!isset($itemsInfo[$quoteItemId])) { + $itemsInfo[$quoteItemId] = 0; + } + $itemsInfo[$quoteItemId] += (double)$data['qty']; + } + } + + return $itemsInfo; + } + + /** + * JSON response builder. + * + * @param string $error + * @return void + */ + private function jsonResponse(string $error = '') + { + $this->getResponse()->representJson( + $this->json->serialize($this->getResponseData($error)) + ); + } + + /** + * Returns response data. + * + * @param string $error + * @return array + */ + private function getResponseData(string $error = ''): array + { + $response = [ + 'success' => true, + ]; + + if (!empty($error)) { + $response = [ + 'success' => false, + 'error_message' => $error, + ]; + } + + return $response; + } +} diff --git a/app/code/Magento/Multishipping/i18n/en_US.csv b/app/code/Magento/Multishipping/i18n/en_US.csv index 43cc785c56eab..43615e697b931 100644 --- a/app/code/Magento/Multishipping/i18n/en_US.csv +++ b/app/code/Magento/Multishipping/i18n/en_US.csv @@ -89,4 +89,5 @@ Options,Options "Select Shipping Method","Select Shipping Method" "We received your order!","We received your order!" "Ship to:","Ship to:" -"Error:","Error:" \ No newline at end of file +"Error:","Error:" +"We are unable to process your request. Please, try again later.","We are unable to process your request. Please, try again later." diff --git a/app/code/Magento/Multishipping/view/frontend/requirejs-config.js b/app/code/Magento/Multishipping/view/frontend/requirejs-config.js index f14159ba0a85a..0235d8b848154 100644 --- a/app/code/Magento/Multishipping/view/frontend/requirejs-config.js +++ b/app/code/Magento/Multishipping/view/frontend/requirejs-config.js @@ -9,7 +9,8 @@ var config = { multiShipping: 'Magento_Multishipping/js/multi-shipping', orderOverview: 'Magento_Multishipping/js/overview', payment: 'Magento_Multishipping/js/payment', - billingLoader: 'Magento_Checkout/js/checkout-loader' + billingLoader: 'Magento_Checkout/js/checkout-loader', + cartUpdate: 'Magento_Checkout/js/action/update-shopping-cart' } } }; diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/addresses.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/addresses.phtml index a55b84c0a2d8a..0f96f7cdb708a 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/addresses.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/addresses.phtml @@ -14,20 +14,42 @@ * @var $block \Magento\Multishipping\Block\Checkout\Addresses */ ?> -<form id="checkout_multishipping_form" data-mage-init='{"multiShipping":{}, "validation":{}}' action="<?= /* @escapeNotVerified */ $block->getPostActionUrl() ?>" method="post" class="multicheckout address form"> +<form id="checkout_multishipping_form" + data-mage-init='{ + "multiShipping":{}, + "validation":{}, + "cartUpdate": { + "validationURL": "/multishipping/checkout/checkItems", + "eventName": "updateMulticartItemQty" + }}' + action="<?= $block->escapeUrl($block->getPostActionUrl()) ?>" + method="post" + class="multicheckout address form"> +<form id="checkout_multishipping_form" + data-mage-init='{ + "multiShipping":{}, + "cartUpdate": { + "validationURL": "/multishipping/checkout/checkItems", + "eventName": "updateMulticartItemQty" + }}' + action="<?= $block->escapeUrl($block->getPostActionUrl()) ?>" + method="post" + class="multicheckout address form"> <div class="title"> - <strong><?= /* @escapeNotVerified */ __('Please select a shipping address for applicable items.') ?></strong> + <strong><?= $block->escapeHtml(__('Please select a shipping address for applicable items.')) ?></strong> </div> <input type="hidden" name="continue" value="0" id="can_continue_flag"/> <input type="hidden" name="new_address" value="0" id="add_new_address_flag"/> <div class="table-wrapper"> <table class="items data table" id="multiship-addresses-table"> - <caption class="table-caption"><?= /* @escapeNotVerified */ __('Please select a shipping address for applicable items.') ?></caption> + <caption class="table-caption"> + <?= $block->escapeHtml(__('Please select a shipping address for applicable items.')) ?> + </caption> <thead> <tr> - <th class="col product" scope="col"><?= /* @escapeNotVerified */ __('Product') ?></th> - <th class="col qty" scope="col"><?= /* @escapeNotVerified */ __('Qty') ?></th> - <th class="col address" scope="col"><?= /* @escapeNotVerified */ __('Send To') ?></th> + <th class="col product" scope="col"><?= $block->escapeHtml(__('Product')) ?></th> + <th class="col qty" scope="col"><?= $block->escapeHtml(__('Qty')) ?></th> + <th class="col address" scope="col"><?= $block->escapeHtml(__('Send To')) ?></th> <th class="col actions" scope="col"> </th> </tr> </thead> @@ -35,24 +57,37 @@ <?php foreach ($block->getItems() as $_index => $_item): ?> <?php if ($_item->getQuoteItem()) : ?> <tr> - <td class="col product" data-th="<?= $block->escapeHtml(__('Product')) ?>"><?= $block->getItemHtml($_item->getQuoteItem()) ?></td> + <td class="col product" data-th="<?= $block->escapeHtml(__('Product')) ?>"> + <?= $block->getItemHtml($_item->getQuoteItem()) ?> + </td> <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty')) ?>"> <div class="field qty"> - <label for="ship-<?= /* @escapeNotVerified */ $_index ?>-<?= /* @escapeNotVerified */ $_item->getQuoteItemId() ?>-qty" class="label"> - <span><?= /* @escapeNotVerified */ __('Qty') ?></span> + <label for="ship-<?= $block->escapeHtml($_index) ?>-<?= $block->escapeHtml($_item->getQuoteItemId()) ?>-qty" + class="label"> + <span><?= $block->escapeHtml(__('Qty')) ?></span> </label> <div class="control"> - <input type="number" data-multiship-item-id="<?= /* @escapeNotVerified */ $_item->getSku() ?>" id="ship-<?= /* @escapeNotVerified */ $_index ?>-<?= /* @escapeNotVerified */ $_item->getQuoteItemId() ?>-qty" name="ship[<?= /* @escapeNotVerified */ $_index ?>][<?= /* @escapeNotVerified */ $_item->getQuoteItemId() ?>][qty]" value="<?= $block->escapeHtml($_item->getQty()) ?>" size="2" class="input-text qty" data-validate="{number: true}"/> + <input type="number" + data-multiship-item-id="<?= $block->escapeHtml($_item->getSku()) ?>" + id="ship-<?= $block->escapeHtml($_index) ?>-<?= $block->escapeHtml($_item->getQuoteItemId()) ?>-qty" + name="ship[<?= $block->escapeHtml($_index) ?>][<?= $block->escapeHtml($_item->getQuoteItemId()) ?>][qty]" + value="<?= $block->escapeHtml($_item->getQty()) ?>" + size="2" + class="input-text qty" + data-validate="{number: true}"/> </div> </div> </td> <td class="col address" data-th="<?= $block->escapeHtml(__('Send To')) ?>"> <?php if ($_item->getProduct()->getIsVirtual()): ?> - <div class="applicable"><?= /* @escapeNotVerified */ __('A shipping selection is not applicable.') ?></div> + <div class="applicable"> + <?= $block->escapeHtml(__('A shipping selection is not applicable.')) ?> + </div> <?php else: ?> <div class="field address"> - <label for="ship_<?= /* @escapeNotVerified */ $_index ?>_<?= /* @escapeNotVerified */ $_item->getQuoteItemId() ?>_address" class="label"> - <span><?= /* @escapeNotVerified */ __('Send To') ?></span> + <label for="ship_<?= $block->escapeHtml($_index) ?>_<?= $block->escapeHtml($_item->getQuoteItemId()) ?>_address" + class="label"> + <span><?= $block->escapeHtml(__('Send To')) ?></span> </label> <div class="control"> <?= $block->getAddressesHtmlSelect($_item, $_index) ?> @@ -61,8 +96,11 @@ <?php endif; ?> </td> <td class="col actions" data-th="<?= $block->escapeHtml(__('Actions')) ?>"> - <a href="<?= /* @escapeNotVerified */ $block->getItemDeleteUrl($_item) ?>" title="<?= /* @escapeNotVerified */ __('Remove Item') ?>" class="action delete" data-multiship-item-update=""> - <span><?= /* @escapeNotVerified */ __('Remove item') ?></span> + <a href="<?= $block->escapeUrl($block->getItemDeleteUrl($_item)) ?>" + title="<?= $block->escapeHtml(__('Remove Item')) ?>" + class="action delete" + data-multiship-item-remove=""> + <span><?= $block->escapeHtml(__('Remove item')) ?></span> </a> </td> </tr> @@ -73,12 +111,35 @@ </div> <div class="actions-toolbar"> <div class="primary"> - <button type="submit" title="<?= /* @escapeNotVerified */ __('Go to Shipping Information') ?>" class="action primary continue<?php if ($block->isContinueDisabled()):?> disabled<?php endif; ?>" data-role="can-continue" data-flag="1"<?php if ($block->isContinueDisabled()):?> disabled="disabled"<?php endif; ?>><span><?= /* @escapeNotVerified */ __('Go to Shipping Information') ?></span></button> + <button type="submit" + title="<?= $block->escapeHtml(__('Go to Shipping Information')) ?>" + class="action primary continue<?php if ($block->isContinueDisabled()):?> disabled<?php endif; ?>" + data-role="can-continue" + data-flag="1" + <?php if ($block->isContinueDisabled()):?> + disabled="disabled" + <?php endif; ?>> + <span><?= $block->escapeHtml(__('Go to Shipping Information')) ?></span> + </button> </div> <div class="secondary"> - <button type="submit" data-multiship-item-update="" class="action update" data-role="can-continue" data-flag="0"><span><?= /* @escapeNotVerified */ __('Update Qty & Addresses') ?></span></button> - <button type="button" title="<?= /* @escapeNotVerified */ __('Enter a New Address') ?>" class="action add" data-role="add-new-address"><span><?= /* @escapeNotVerified */ __('Enter a New Address') ?></span></button> - <a href="<?= /* @escapeNotVerified */ $block->getBackUrl() ?>" class="action back"><span><?= /* @escapeNotVerified */ __('Back to Shopping Cart') ?></span></a> + <button type="submit" + data-multiship-item-update="" + class="action update" + data-role="can-continue" + data-flag="0"> + <span><?= $block->escapeHtml(__('Update Qty & Addresses')) ?></span> + </button> + <button type="button" + title="<?= $block->escapeHtml(__('Enter a New Address')) ?>" + class="action add" + data-role="add-new-address"> + <span><?= $block->escapeHtml(__('Enter a New Address')) ?></span> + </button> + <a href="<?= $block->escapeUrl($block->getBackUrl()) ?>" + class="action back"> + <span><?= $block->escapeHtml(__('Back to Shopping Cart')) ?></span> + </a> </div> </div> </form> diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php new file mode 100644 index 0000000000000..44bbd90cc6a2d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Controller\Cart; + +use Magento\Catalog\Model\Product; +use Magento\Checkout\Model\Session; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Serialize\Serializer\Json; + +class UpdateItemQtyTest extends \Magento\TestFramework\TestCase\AbstractController +{ + /** + * @var Json + */ + private $json; + + /** + * @var FormKey + */ + private $formKey; + + /** + * @var Session + */ + private $session; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + protected function setUp() + { + parent::setUp(); + + $this->json = $this->_objectManager->create(Json::class); + $this->formKey = $this->_objectManager->get(FormKey::class); + $this->session = $this->_objectManager->create(Session::class); + $this->productRepository = $this->_objectManager->create(ProductRepositoryInterface::class); + } + + /** + * Tests of cart validation. + * + * @param array $requestQuantity + * @param array $expectedResponse + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + * @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product.php + * @dataProvider requestDataProvider + */ + public function testExecute($requestQuantity, $expectedResponse) + { + try { + /** @var $product Product */ + $product = $this->productRepository->get('simple'); + } catch (\Exception $e) { + $this->fail('No such product entity'); + } + + $quoteItem = $this->session + ->getQuote() + ->getItemByProduct($product); + + $this->assertNotNull($quoteItem, 'Cannot get quote item for simple product'); + + $request = []; + if (!empty($requestQuantity) && is_array($requestQuantity)) { + $request= [ + 'form_key' => $this->formKey->getFormKey(), + 'cart' => [ + $quoteItem->getId() => $requestQuantity, + ] + ]; + } + + $this->getRequest()->setPostValue($request); + $this->dispatch('checkout/cart/updateItemQty'); + $response = $this->getResponse()->getBody(); + + $this->assertEquals($this->json->unserialize($response), $expectedResponse); + } + + /** + * Variations of request data. + * @returns array + */ + public function requestDataProvider(): array + { + return [ + [ + 'request' => [], + 'response' => [ + 'success' => false, + 'error_message' => 'Something went wrong while saving the page.'. + ' Please refresh the page and try again.' + ] + ], + [ + 'request' => ['qty' => 2], + 'response' => [ + 'success' => true, + ] + ], + [ + 'request' => ['qty' => 230], + 'response' => [ + 'success' => false, + 'error_message' => 'We don\'t have as many "Simple Product" as you requested.'] + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Multishipping/Controller/Checkout/CheckItemsTest.php b/dev/tests/integration/testsuite/Magento/Multishipping/Controller/Checkout/CheckItemsTest.php new file mode 100644 index 0000000000000..636239ee7a52e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Multishipping/Controller/Checkout/CheckItemsTest.php @@ -0,0 +1,176 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Multishipping\Controller\Checkout; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Checkout\Model\Session; +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\Exception\LocalizedException; +use Magento\Quote\Model\Quote; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Quote\Api\CartRepositoryInterface as QuoteRepository; +use Magento\Framework\Serialize\Serializer\Json; + +/** + * Test class for \Magento\Multishipping\Controller\Checkout + * + * @magentoAppArea frontend + * @magentoDataFixture Magento/Sales/_files/quote.php + * @magentoDataFixture Magento/Customer/_files/customer.php + */ +class CheckItemsTest extends \Magento\TestFramework\TestCase\AbstractController +{ + /** + * @var Quote + */ + private $quote; + + /** + * @var Session + */ + private $checkoutSession; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var Json + */ + private $json; + + /** + * @inheritdoc + */ + public function setUp() + { + parent::setUp(); + + $this->checkoutSession = $this->_objectManager->get(Session::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->json = $this->_objectManager->get(Json::class); + $this->quote = $this->getQuote('test01'); + $this->checkoutSession->setQuoteId($this->quote->getId()); + $this->checkoutSession->setCartWasUpdated(false); + } + + /** + * Validator of quote items. + * + * @param array $requestQuantity + * @param array $expectedResponse + * + * @magentoConfigFixture current_store multishipping/options/checkout_multiple 1 + * @magentoConfigFixture current_store multishipping/options/checkout_multiple_maximum_qty 200 + * @dataProvider requestDataProvider + */ + public function testExecute($requestQuantity, $expectedResponse) + { + $this->loginCustomer(); + + try { + /** @var $product Product */ + $product = $this->productRepository->get('simple'); + } catch (\Exception $e) { + $this->fail('No such product entity'); + } + + $quoteItem = $this->quote->getItemByProduct($product); + $this->assertNotFalse($quoteItem, 'Cannot get quote item for simple product'); + + $request = []; + if (!empty($requestQuantity) && is_array($requestQuantity)) { + $request= [ + 'ship' => [ + [$quoteItem->getId() => $requestQuantity], + ] + ]; + } + + $this->getRequest()->setPostValue($request); + $this->dispatch('multishipping/checkout/checkItems'); + $response = $this->getResponse()->getBody(); + + $this->assertEquals($expectedResponse, $this->json->unserialize($response)); + } + + /** + * Authenticates customer and creates customer session. + */ + private function loginCustomer() + { + $logger = $this->createMock(\Psr\Log\LoggerInterface::class); + /** @var AccountManagementInterface $service */ + $service = $this->_objectManager->create(AccountManagementInterface::class); + try { + $customer = $service->authenticate('customer@example.com', 'password'); + } catch (LocalizedException $e) { + $this->fail($e->getMessage()); + } + /** @var CustomerSession $customerSession */ + $customerSession = $this->_objectManager->create(CustomerSession::class, [$logger]); + $customerSession->setCustomerDataAsLoggedIn($customer); + } + + /** + * Gets quote by reserved order id. + * + * @param string $reservedOrderId + * @return Quote + */ + private function getQuote($reservedOrderId) + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->_objectManager->create(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + /** @var QuoteRepository $quoteRepository */ + $quoteRepository = $this->_objectManager->get(QuoteRepository::class); + $items = $quoteRepository->getList($searchCriteria) + ->getItems(); + return array_pop($items); + } + + /** + * Variations of request data. + * @returns array + */ + public function requestDataProvider(): array + { + return [ + [ + 'request' => [], + 'response' => [ + 'success' => false, + 'error_message' => 'We are unable to process your request. Please, try again later.' + ] + ], + [ + 'request' => ['qty' => 2], + 'response' => [ + 'success' => true, + ] + ], + [ + 'request' => ['qty' => 101], + 'response' => [ + 'success' => false, + 'error_message' => 'We don\'t have as many "Simple Product" as you requested.'] + ], + [ + 'request' => ['qty' => 230], + 'response' => [ + 'success' => false, + 'error_message' => 'Maximum qty allowed for Shipping to multiple addresses is 200'] + ], + ]; + } +} From b13d3b48b779f1d5b67b07d9d6e0d69daf465d5b Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Tue, 31 Jul 2018 13:27:53 +0300 Subject: [PATCH 0640/1171] MAGETWO-93790: Cookie `section_data_ids` isn't filled properly after restarting browser application --- .../Magento/Customer/view/frontend/web/js/customer-data.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js index c4672c48e1f4a..ec21e45da6852 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js +++ b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js @@ -27,7 +27,8 @@ define([ //TODO: remove global change, in this case made for initNamespaceStorage $.cookieStorage.setConf({ - path: '/' + path: '/', + expires: 1 }); storage = $.initNamespaceStorage('mage-cache-storage').localStorage; From 5dfebc41e32c16569419d14c9bec7566f58fcf91 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Tue, 31 Jul 2018 14:01:50 +0300 Subject: [PATCH 0641/1171] MAGETWO-93714: [2.3] Customer attributes length validation on form doesn't work --- .../Adminhtml/Order/Create/LoadBlock.php | 4 +-- .../Adminhtml/Order/Create/Save.php | 29 ++++++++++--------- .../Magento/Sales/Model/AdminOrder/Create.php | 10 ++----- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlock.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlock.php index 6a9d0a5dcb8ed..bc4eb3cfba423 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlock.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlock.php @@ -55,10 +55,10 @@ public function execute() $this->_initSession()->_processData(); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->_reloadQuote(); - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { $this->_reloadQuote(); - $this->messageManager->addException($e, $e->getMessage()); + $this->messageManager->addExceptionMessage($e, $e->getMessage()); } $asJson = $request->getParam('json'); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Save.php index 621705c7937cb..929e23075e070 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Save.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Sales\Controller\Adminhtml\Order\Create; use Magento\Framework\Exception\PaymentException; @@ -12,14 +13,15 @@ class Save extends \Magento\Sales\Controller\Adminhtml\Order\Create /** * Saving quote and create order * - * @return \Magento\Backend\Model\View\Result\Forward|\Magento\Backend\Model\View\Result\Redirect + * @return \Magento\Framework\Controller\ResultInterface * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute() { - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ - $resultRedirect = $this->resultRedirectFactory->create(); + $path = 'sales/*/'; + $pathParams = []; + try { // check if the creation of a new customer is allowed if (!$this->_authorization->isAllowed('Magento_Customer::manage') @@ -49,31 +51,30 @@ public function execute() ->createOrder(); $this->_getSession()->clearStorage(); - $this->messageManager->addSuccess(__('You created the order.')); + $this->messageManager->addSuccessMessage(__('You created the order.')); if ($this->_authorization->isAllowed('Magento_Sales::actions_view')) { - $resultRedirect->setPath('sales/order/view', ['order_id' => $order->getId()]); + $pathParams = ['order_id' => $order->getId()]; + $path = 'sales/order/view'; } else { - $resultRedirect->setPath('sales/order/index'); + $path = 'sales/order/index'; } } catch (PaymentException $e) { $this->_getOrderCreateModel()->saveQuote(); $message = $e->getMessage(); if (!empty($message)) { - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); } - $resultRedirect->setPath('sales/*/'); } catch (\Magento\Framework\Exception\LocalizedException $e) { // customer can be created before place order flow is completed and should be stored in current session - $this->_getSession()->setCustomerId($this->_getSession()->getQuote()->getCustomerId()); + $this->_getSession()->setCustomerId((int)$this->_getSession()->getQuote()->getCustomerId()); $message = $e->getMessage(); if (!empty($message)) { - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); } - $resultRedirect->setPath('sales/*/'); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Order saving error: %1', $e->getMessage())); - $resultRedirect->setPath('sales/*/'); + $this->messageManager->addExceptionMessage($e, __('Order saving error: %1', $e->getMessage())); } - return $resultRedirect; + + return $this->resultRedirectFactory->create()->setPath($path, $pathParams); } } diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 07716ff8274c6..566f09d4c2e23 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -1956,21 +1956,17 @@ public function createOrder() */ protected function _validate() { - $customerId = $this->getSession()->getCustomerId(); - if ($customerId === null) { - throw new \Magento\Framework\Exception\LocalizedException(__('Please select a customer')); - } - if (!$this->getSession()->getStore()->getId()) { throw new \Magento\Framework\Exception\LocalizedException(__('Please select a store')); } $items = $this->getQuote()->getAllItems(); - if (count($items) == 0) { + if (count($items) === 0) { $this->_errors[] = __('Please specify order items.'); } foreach ($items as $item) { + /** @var \Magento\Quote\Model\Quote\Item $item */ $messages = $item->getMessage(false); if ($item->getHasError() && is_array($messages) && !empty($messages)) { $this->_errors = array_merge($this->_errors, $messages); @@ -2002,7 +1998,7 @@ protected function _validate() $logger = ObjectManager::getInstance()->get(LoggerInterface::class); foreach ($this->_errors as $error) { $logger->error($error); - $this->messageManager->addError($error); + $this->messageManager->addErrorMessage($error); } throw new \Magento\Framework\Exception\LocalizedException(__('Validation is failed.')); From 445145812d253dc49fdcab07babb9b3e41b76de2 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Tue, 31 Jul 2018 14:17:37 +0300 Subject: [PATCH 0642/1171] MAGETWO-91813: Elasticsearch incorrect results relevance score --- .../Unit/Model/Adapter/FieldMapper/ProductFieldMapperTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/ProductFieldMapperTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/ProductFieldMapperTest.php index a0c7d9c4df571..2a57dfc65f47b 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/ProductFieldMapperTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/ProductFieldMapperTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Elasticsearch\Test\Unit\Model\Adapter\FieldMapper; use Magento\Catalog\Api\Data\ProductAttributeInterface; From 917093b657f26ab078924d47074b75e304103d8a Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Tue, 31 Jul 2018 14:25:36 +0300 Subject: [PATCH 0643/1171] MAGETWO-91562: Sorting on price of configurable products in catalog not working properly --- .../Magento/CatalogRule/_files/configurable_product_rollback.php | 1 + .../testsuite/Magento/CatalogRule/_files/simple_products.php | 1 + 2 files changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php index d4311d523d3c7..915da48c915b2 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); \Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php index 2855ebb4c92b3..84ce4e1bca87c 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); require __DIR__ . '/attribute.php'; From 470b5076291aecfef873e8a983d82621174e9167 Mon Sep 17 00:00:00 2001 From: Sven Reichel <github-sr@hotmail.com> Date: Tue, 31 Jul 2018 14:03:32 +0200 Subject: [PATCH 0644/1171] Fixed static test (supress warnings for unused parameter) --- app/code/Magento/CatalogSearch/Block/Advanced/Form.php | 1 + app/code/Magento/Sales/Model/Service/CreditmemoService.php | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/CatalogSearch/Block/Advanced/Form.php b/app/code/Magento/CatalogSearch/Block/Advanced/Form.php index b1d0a6431dcdf..4d1957991d1bf 100644 --- a/app/code/Magento/CatalogSearch/Block/Advanced/Form.php +++ b/app/code/Magento/CatalogSearch/Block/Advanced/Form.php @@ -177,6 +177,7 @@ public function getCurrencyCount() * * @param AbstractAttribute $attribute * @return string + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getCurrency($attribute) { diff --git a/app/code/Magento/Sales/Model/Service/CreditmemoService.php b/app/code/Magento/Sales/Model/Service/CreditmemoService.php index aa872a809803a..e8f2e6e5305f7 100644 --- a/app/code/Magento/Sales/Model/Service/CreditmemoService.php +++ b/app/code/Magento/Sales/Model/Service/CreditmemoService.php @@ -100,6 +100,7 @@ public function __construct( * @param int $id Credit Memo Id * @return bool * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function cancel($id) { From 71d621e500af3e5305ec6ef10a2e39bd8f6e5cfb Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Tue, 31 Jul 2018 15:19:41 +0300 Subject: [PATCH 0645/1171] MAGETWO-93966: Run Catalog Search reindex in multithread mode with Elasticsearch 5 --- .../Elasticsearch5/Model/Client/Elasticsearch.php | 8 ++++---- .../Elasticsearch/Model/Client/Elasticsearch.php | 14 ++++++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php index 30c9a78893e45..39b2f772c8e8f 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php @@ -14,21 +14,21 @@ class Elasticsearch implements ClientInterface { /** - * Elasticsearch Client instance + * Elasticsearch Client instances * * @var \Elasticsearch\Client[] */ - protected $client; + private $client; /** * @var array */ - protected $clientOptions; + private $clientOptions; /** * @var bool */ - protected $pingResult; + private $pingResult; /** * @var string diff --git a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php index 118535c36560b..44ab0dbc4d46c 100644 --- a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php @@ -15,21 +15,21 @@ class Elasticsearch implements ClientInterface { /** - * Elasticsearch Client instance + * Elasticsearch Client instances * * @var \Elasticsearch\Client[] */ - protected $client; + private $client; /** * @var array */ - protected $clientOptions; + private $clientOptions; /** * @var bool */ - protected $pingResult; + private $pingResult; /** * Initialize Elasticsearch Client @@ -57,6 +57,11 @@ public function __construct( $this->clientOptions = $options; } + /** + * Get Elasticsearch Client + * + * @return \Elasticsearch\Client + */ private function getClient() { $pid = getmypid(); @@ -66,6 +71,7 @@ private function getClient() } return $this->client[$pid]; } + /** * Ping the Elasticsearch client * From 8d7e38a9a30ed92c719b7ab1403cf480707e5f2d Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Tue, 31 Jul 2018 15:23:24 +0300 Subject: [PATCH 0646/1171] MAGETWO-93712: [2.3] Free Shipping Cart Price Rule not working when UPS shipping method is enabled and Free Shipping is set to "For matching items only" - Fixed Cart Rules applying for Cart Items --- .../Model/Quote/Address/FreeShipping.php | 60 +++-- .../Model/Quote/Address/FreeShippingTest.php | 225 +++++++++++++----- .../Model/Carrier/AbstractCarrier.php | 3 + .../Magento/Ups/Model/CarrierTest.php | 24 ++ 4 files changed, 233 insertions(+), 79 deletions(-) diff --git a/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php b/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php index e1397dc6e097b..c6234a1247a53 100644 --- a/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php +++ b/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php @@ -3,27 +3,35 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\OfflineShipping\Model\Quote\Address; -class FreeShipping implements \Magento\Quote\Model\Quote\Address\FreeShippingInterface +use Magento\OfflineShipping\Model\SalesRule\Calculator; +use Magento\OfflineShipping\Model\SalesRule\ExtendedCalculator; +use Magento\Quote\Model\Quote\Address\FreeShippingInterface; +use Magento\Quote\Model\Quote\Item\AbstractItem; +use Magento\Store\Model\StoreManagerInterface; + +class FreeShipping implements FreeShippingInterface { /** - * @var \Magento\OfflineShipping\Model\SalesRule\Calculator + * @var ExtendedCalculator */ protected $calculator; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $storeManager; /** - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\OfflineShipping\Model\SalesRule\Calculator $calculator + * @param StoreManagerInterface $storeManager + * @param Calculator $calculator */ public function __construct( - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\OfflineShipping\Model\SalesRule\Calculator $calculator + StoreManagerInterface $storeManager, + Calculator $calculator ) { $this->storeManager = $storeManager; $this->calculator = $calculator; @@ -39,6 +47,7 @@ public function isFreeShipping(\Magento\Quote\Model\Quote $quote, $items) return false; } + $result = false; $addressFreeShipping = true; $store = $this->storeManager->getStore($quote->getStoreId()); $this->calculator->init( @@ -62,21 +71,24 @@ public function isFreeShipping(\Magento\Quote\Model\Quote $quote, $items) } $this->calculator->processFreeShipping($item); - $itemFreeShipping = (bool)$item->getFreeShipping(); - $addressFreeShipping = $addressFreeShipping && $itemFreeShipping; - - if ($addressFreeShipping && !$item->getAddress()->getFreeShipping()) { - $item->getAddress()->setFreeShipping(true); + // at least one item matches to the rule and the rule mode is not a strict + if ((bool)$item->getAddress()->getFreeShipping()) { + $result = true; + break; } - /** Parent free shipping we apply to all children*/ - $this->applyToChildren($item, $itemFreeShipping); + $itemFreeShipping = (bool)$item->getFreeShipping(); + $addressFreeShipping = $addressFreeShipping && $itemFreeShipping; + $result = $addressFreeShipping; } - return (bool)$shippingAddress->getFreeShipping(); + + $shippingAddress->setFreeShipping((int)$result); + $this->applyToItems($items, $result); + return $result; } /** - * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item + * @param AbstractItem $item * @param bool $isFreeShipping * @return void */ @@ -91,4 +103,20 @@ protected function applyToChildren(\Magento\Quote\Model\Quote\Item\AbstractItem } } } + + /** + * Sets free shipping availability to the quote items. + * + * @param array $items + * @param bool $freeShipping + */ + private function applyToItems(array $items, bool $freeShipping) + { + /** @var AbstractItem $item */ + foreach ($items as $item) { + $item->getAddress() + ->setFreeShipping((int)$freeShipping); + $this->applyToChildren($item, $freeShipping); + } + } } diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Model/Quote/Address/FreeShippingTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Model/Quote/Address/FreeShippingTest.php index 5a8b1afa786bb..8266a39bbbb6d 100644 --- a/app/code/Magento/OfflineShipping/Test/Unit/Model/Quote/Address/FreeShippingTest.php +++ b/app/code/Magento/OfflineShipping/Test/Unit/Model/Quote/Address/FreeShippingTest.php @@ -3,94 +3,193 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\OfflineShipping\Test\Unit\Model\Quote\Address; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\OfflineShipping\Model\Quote\Address\FreeShipping; +use Magento\OfflineShipping\Model\SalesRule\Calculator; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\Quote\Item; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit_Framework_MockObject_MockObject as MockObject; class FreeShippingTest extends \PHPUnit\Framework\TestCase { + private static $websiteId = 1; + + private static $customerGroupId = 2; + + private static $couponCode = 3; + + private static $storeId = 1; + /** - * @var \Magento\OfflineShipping\Model\Quote\Address\FreeShipping + * @var FreeShipping */ private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\StoreManagerInterface + * @var MockObject|StoreManagerInterface */ - private $storeManagerMock; + private $storeManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\OfflineShipping\Model\SalesRule\Calculator + * @var MockObject|Calculator */ - private $calculatorMock; + private $calculator; protected function setUp() { - $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->calculatorMock = $this->createMock(\Magento\OfflineShipping\Model\SalesRule\Calculator::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->calculator = $this->createMock(Calculator::class); - $this->model = new \Magento\OfflineShipping\Model\Quote\Address\FreeShipping( - $this->storeManagerMock, - $this->calculatorMock + $this->model = new FreeShipping( + $this->storeManager, + $this->calculator ); } - public function testIsFreeShippingIfNoItems() + /** + * Checks free shipping availability based on quote items and cart rule calculations. + * + * @param int $addressFree + * @param int $fItemFree + * @param int $sItemFree + * @param bool $expected + * @dataProvider itemsDataProvider + */ + public function testIsFreeShipping(int $addressFree, int $fItemFree, int $sItemFree, bool $expected) { - $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); - $this->assertFalse($this->model->isFreeShipping($quoteMock, [])); + $address = $this->getShippingAddress(); + $this->withStore(); + $quote = $this->getQuote($address); + $fItem = $this->getItem($quote); + $sItem = $this->getItem($quote); + $items = [$fItem, $sItem]; + + $this->calculator->method('init') + ->with(self::$websiteId, self::$customerGroupId, self::$couponCode); + $this->calculator->method('processFreeShipping') + ->withConsecutive( + [$fItem], + [$sItem] + ) + ->willReturnCallback(function () use ($fItem, $sItem, $addressFree, $fItemFree, $sItemFree) { + // emulate behavior of cart rule calculator + $fItem->getAddress()->setFreeShipping($addressFree); + $fItem->setFreeShipping($fItemFree); + $sItem->setFreeShipping($sItemFree); + }); + + $actual = $this->model->isFreeShipping($quote, $items); + self::assertEquals($expected, $actual); + self::assertEquals($expected, $address->getFreeShipping()); } - public function testIsFreeShipping() + /** + * Gets list of variations with free shipping availability. + * + * @return array + */ + public function itemsDataProvider(): array { - $storeId = 100; - $websiteId = 200; - $customerGroupId = 300; - $objectManagerMock = new ObjectManagerHelper($this); - $quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, - ['getShippingAddress', 'getStoreId', 'getCustomerGroupId', 'getCouponCode'] - ); - $itemMock = $this->createPartialMock(\Magento\Quote\Model\Quote\Item::class, [ - 'getNoDiscount', - 'getParentItemId', - 'getFreeShipping', - 'getAddress', - 'isChildrenCalculated', - 'getHasChildren', - 'getChildren' - ]); - - $quoteMock->expects($this->once())->method('getStoreId')->willReturn($storeId); - $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); - $storeMock->expects($this->once())->method('getWebsiteId')->willReturn($websiteId); - $this->storeManagerMock->expects($this->once())->method('getStore')->with($storeId)->willReturn($storeMock); - - $quoteMock->expects($this->once())->method('getCustomerGroupId')->willReturn($customerGroupId); - $quoteMock->expects($this->once())->method('getCouponCode')->willReturn(null); - - $this->calculatorMock->expects($this->once()) - ->method('init') - ->with($websiteId, $customerGroupId, null) - ->willReturnSelf(); - - $itemMock->expects($this->once())->method('getNoDiscount')->willReturn(false); - $itemMock->expects($this->once())->method('getParentItemId')->willReturn(false); - $this->calculatorMock->expects($this->exactly(2))->method('processFreeShipping')->willReturnSelf(); - $itemMock->expects($this->once())->method('getFreeShipping')->willReturn(true); - - $addressMock = $objectManagerMock->getObject(\Magento\Quote\Model\Quote\Address::class); - $quoteMock->expects($this->once())->method('getShippingAddress')->willReturn($addressMock); - $itemMock->expects($this->exactly(2))->method('getAddress')->willReturn($addressMock); - - $itemMock->expects($this->once())->method('getHasChildren')->willReturn(true); - $itemMock->expects($this->once())->method('isChildrenCalculated')->willReturn(true); - - $childMock = $this->createPartialMock(\Magento\Quote\Model\Quote\Item::class, ['setFreeShipping']); - $childMock->expects($this->once())->method('setFreeShipping')->with(true)->willReturnSelf(); - $itemMock->expects($this->once())->method('getChildren')->willReturn([$childMock]); - - $this->assertTrue($this->model->isFreeShipping($quoteMock, [$itemMock])); + return [ + ['addressFree' => 1, 'fItemFree' => 0, 'sItemFree' => 0, 'expected' => true], + ['addressFree' => 0, 'fItemFree' => 1, 'sItemFree' => 0, 'expected' => false], + ['addressFree' => 0, 'fItemFree' => 0, 'sItemFree' => 1, 'expected' => false], + ['addressFree' => 0, 'fItemFree' => 1, 'sItemFree' => 1, 'expected' => true], + ]; + } + + /** + * Creates mock object for store entity. + */ + private function withStore() + { + $store = $this->getMockBuilder(StoreInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->storeManager->method('getStore') + ->with(self::$storeId) + ->willReturn($store); + + $store->method('getWebsiteId') + ->willReturn(self::$websiteId); + } + + /** + * Get mock object for quote entity. + * + * @param Address $address + * @return Quote + */ + private function getQuote(Address $address): Quote + { + /** @var Quote|MockObject $quote */ + $quote = $this->getMockBuilder(Quote::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'getCouponCode', 'getCustomerGroupId', 'getShippingAddress', 'getStoreId', 'getItemsQty', + 'getVirtualItemsQty' + ] + ) + ->getMock(); + + $quote->method('getStoreId') + ->willReturn(self::$storeId); + $quote->method('getCustomerGroupId') + ->willReturn(self::$customerGroupId); + $quote->method('getCouponCode') + ->willReturn(self::$couponCode); + $quote->method('getShippingAddress') + ->willReturn($address); + $quote->method('getItemsQty') + ->willReturn(2); + $quote->method('getVirtualItemsQty') + ->willReturn(0); + + return $quote; + } + + /** + * Gets stub object for shipping address. + * + * @return Address|MockObject + */ + private function getShippingAddress(): Address + { + /** @var Address|MockObject $address */ + $address = $this->getMockBuilder(Address::class) + ->disableOriginalConstructor() + ->setMethods(['beforeSave']) + ->getMock(); + + return $address; + } + + /** + * Gets stub object for quote item. + * + * @param Quote $quote + * @return Item + */ + private function getItem(Quote $quote): Item + { + /** @var Item|MockObject $item */ + $item = $this->getMockBuilder(Item::class) + ->disableOriginalConstructor() + ->setMethods(['getHasChildren']) + ->getMock(); + $item->setQuote($quote); + $item->setNoDiscount(0); + $item->setParentItemId(0); + $item->method('getHasChildren') + ->willReturn(0); + + return $item; } } diff --git a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php index 6763046373ce7..9d7bfff5e559c 100644 --- a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php +++ b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php @@ -407,6 +407,9 @@ public function getSortOrder() */ protected function _updateFreeMethodQuote($request) { + if (!$request->getFreeShipping()) { + return; + } if ($request->getFreeMethodWeight() == $request->getPackageWeight() || !$request->hasFreeMethodWeight()) { return; } diff --git a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php index a9f6752c9de82..334cb336862a9 100644 --- a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php +++ b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php @@ -51,4 +51,28 @@ public function testGetShipConfirmUrlLive() $this->carrier->getShipConfirmUrl() ); } + + /** + * @magentoConfigFixture current_store carriers/ups/active 1 + * @magentoConfigFixture current_store carriers/ups/allowed_methods 1DA,GND + * @magentoConfigFixture current_store carriers/ups/free_method GND + */ + public function testCollectFreeRates() + { + $rateRequest = Bootstrap::getObjectManager()->get(RateRequestFactory::class)->create(); + $rateRequest->setDestCountryId('US'); + $rateRequest->setDestRegionId('CA'); + $rateRequest->setDestPostcode('90001'); + $rateRequest->setPackageQty(1); + $rateRequest->setPackageWeight(1); + $rateRequest->setFreeMethodWeight(0); + $rateRequest->setLimitCarrier($this->carrier::CODE); + $rateRequest->setFreeShipping(true); + + $rateResult = $this->carrier->collectRates($rateRequest); + $result = $rateResult->asArray(); + $methods = $result[$this->carrier::CODE]['methods']; + $this->assertEquals(0, $methods['GND']['price']); + $this->assertNotEquals(0, $methods['1DA']['price']); + } } From 599031d7c797b5188b7a80d2bb75c860933170a1 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Wed, 6 Jun 2018 10:47:20 +0300 Subject: [PATCH 0647/1171] MAGETWO-93707: GiftCard "Open Amount From" could't be re-saved - Validation rule for numbers was extended to accept formatted floats like 2,000,000.50 --- .../view/base/web/js/lib/validation/rules.js | 2 +- .../Ui/base/js/lib/validation/rules.test.js | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index c63b4588d2d16..e52791deaf2ad 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -636,7 +636,7 @@ define([ 'validate-number': [ function (value) { return utils.isEmptyNoTrim(value) || - !isNaN(utils.parseNumber(value)) && /^\s*-?\d*(\.\d*)?\s*$/.test(value); + !isNaN(utils.parseNumber(value)) && /^\s*-?\d*(,\d*)*(\.\d*)?\s*$/.test(value); }, $.mage.__('Please enter a valid number in this field.') ], diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/validation/rules.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/validation/rules.test.js index 1dfdfca8b2f50..0703374aaa9d6 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/validation/rules.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/validation/rules.test.js @@ -39,6 +39,37 @@ define([ expect(rules['range-words'].handler(value, params)).toBe(true); }); }); + describe('"validate-number" method', function () { + it('Check on empty value', function () { + var value = ''; + + expect(rules['validate-number'].handler(value)).toBe(true); + }); + + it('Check on integer', function () { + var value = '125'; + + expect(rules['validate-number'].handler(value)).toBe(true); + }); + + it('Check on float', function () { + var value = '1000.50'; + + expect(rules['validate-number'].handler(value)).toBe(true); + }); + + it('Check on formatted float', function () { + var value = '1,000,000.50'; + + expect(rules['validate-number'].handler(value)).toBe(true); + }); + + it('Check on not a number', function () { + var value = 'string'; + + expect(rules['validate-number'].handler(value)).toBe(false); + }); + }); }); describe('validate-color', function () { From d621d43e66c6ec7274cd7fd9ef62f9f57b4d2044 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Tue, 31 Jul 2018 15:55:42 +0300 Subject: [PATCH 0648/1171] MAGETWO-93788: Google Analytics - addToCart event triggers before updating the cart --- app/code/Magento/Braintree/i18n/de_DE.csv | 193 ---------------------- 1 file changed, 193 deletions(-) delete mode 100644 app/code/Magento/Braintree/i18n/de_DE.csv diff --git a/app/code/Magento/Braintree/i18n/de_DE.csv b/app/code/Magento/Braintree/i18n/de_DE.csv deleted file mode 100644 index d55233159689d..0000000000000 --- a/app/code/Magento/Braintree/i18n/de_DE.csv +++ /dev/null @@ -1,193 +0,0 @@ -cc_type,"Credit Card Type" -cc_number,"Credit Card Number" -avsPostalCodeResponseCode,"AVS Postal Code Response Code" -avsStreetAddressResponseCode,"AVS Street Address Response Code" -cvvResponseCode,"CVV Response Code" -processorAuthorizationCode,"Processor Authorization Code" -processorResponseCode,"Processor Response Code" -processorResponseText,"Processor Response Text" -braintree,Braintree -liabilityShifted,"Liability Shifted" -liabilityShiftPossible,"Liability Shift Possible" -riskDataId,"Risk ID" -riskDataDecision,"Risk Decision" -paymentId,"Payment Id" -payerEmail,"Payer Email" -sale,Sale -credit,Credit -authorization_expired,"Authorization expired" -authorizing,Authorizing -authorized,Authorized -gateway_rejected,"Gateway rejected" -failed,Failed -processor_declined,"Processor declined" -settled,Settled -settling,Settling -submitted_for_settlement,"Submitted for settlement" -voided,Voided -unrecognized,Unrecognized -settlement_declined,"Settlement declined" -settlement_pending,"Settlement pending" -settlement_confirmed,"Settlement confirmed" -paypal_account,"Paypal account" -coinbase_account,"Coinbase account" -europe_bank_accout,"Europe bank account" -credit_card,"Credit card" -apple_pay_card,"Apple pay card" -android_pay_card,"Android pay card" -Country,Country -"Allowed Credit Card Types","Allowed Credit Card Types" -"Add Rule","Add Rule" -"Braintree Settlement Report","Braintree Settlement Report" -"Sorry, but something went wrong","Sorry, but something went wrong" -"We can't initialize checkout.","We can't initialize checkout." -"No authorization transaction to proceed capture.","No authorization transaction to proceed capture." -"Braintree error response.","Braintree error response." -"Payment method nonce can't be retrieved.","Payment method nonce can't be retrieved." -"Wrong transaction status","Wrong transaction status" -Authorize,Authorize -"Authorize and Capture","Authorize and Capture" -"--Please Select--","--Please Select--" -"Please agree to all the terms and conditions before placing the order.","Please agree to all the terms and conditions before placing the order." -"Credit Card Type","Credit Card Type" -"Credit Card Number","Credit Card Number" -"Please, enter valid Credit Card Number","Please, enter valid Credit Card Number" -"Expiration Date","Expiration Date" -"Please, enter valid Expiration Date","Please, enter valid Expiration Date" -"Card Verification Number","Card Verification Number" -"Please, enter valid Card Verification Number","Please, enter valid Card Verification Number" -ending,ending -expires,expires -"Are you sure you want to delete this PayPal account","Are you sure you want to delete this PayPal account" -"PayPal Account","PayPal Account" -"PayPal Logo","PayPal Logo" -Actions,Actions -Delete,Delete -"Credit Card Information","Credit Card Information" -"What is this?","What is this?" -"Save for later use.","Save for later use." -"Place Order","Place Order" -"This payment is not available","This payment is not available" -MM,MM -YY,YY -"Please try again with another form of payment.","---Bitte versuchen Sie es erneut mit einer anderen Zahlungsmethode@" -"Sorry, but something went wrong.","Sorry, but something went wrong." -"Payment ","Payment " -Braintree,Braintree -"Accept credit/debit cards and PayPal in your Magento store.<br/>No setup or monthly fees and your customers never leave your store to complete the purchase.","Accept credit/debit cards and PayPal in your Magento store.<br/>No setup or monthly fees and your customers never leave your store to complete the purchase." -"Enable this Solution","Enable this Solution" -"Enable PayPal through Braintree","Enable PayPal through Braintree" -"Vault Enabled","Vault Enabled" -"Basic Braintree Settings","Basic Braintree Settings" -Title,Title -Environment,Environment -"Payment Action","Payment Action" -"Merchant ID","Merchant ID" -"Public Key","Public Key" -"Private Key","Private Key" -"Advanced Braintree Settings","Advanced Braintree Settings" -"Vault Title","Vault Title" -"Merchant Account ID","Merchant Account ID" -"Advanced Fraud Protection","Advanced Fraud Protection" -"Kount Merchant ID","Kount Merchant ID" -Debug,Debug -"CVV Verification","CVV Verification" -"Credit Card Types","Credit Card Types" -"Sort Order","Sort Order" -"Country Specific Settings","Country Specific Settings" -"Payment from Applicable Countries","Payment from Applicable Countries" -"Payment from Specific Countries","Payment from Specific Countries" -"Country Specific Credit Card Types","Country Specific Credit Card Types" -"PayPal through Braintree","PayPal through Braintree" -"Override Merchant Name","Override Merchant Name" -"Require Customer's Billing Address","Require Customer's Billing Address" -"Allow to Edit Shipping Address Entered During Checkout on PayPal Side","Allow to Edit Shipping Address Entered During Checkout on PayPal Side" -"Display on Shopping Cart","Display on Shopping Cart" -"Skip Order Review","Skip Order Review" -"3D Secure Verification Settings","3D Secure Verification Settings" -"3D Secure Verification","3D Secure Verification" -"Threshold Amount","Threshold Amount" -"Verify for Applicable Countries","Verify for Applicable Countries" -"Verify for Specific Countries","Verify for Specific Countries" -"Dynamic Descriptors","Dynamic Descriptors" -Name,Name -Phone,Phone -URL,URL -"Apply filters in order to get results. Only first 100 records will be displayed in the grid, you will be able to download full version of the report in .csv format.","Apply filters in order to get results. Only first 100 records will be displayed in the grid, you will be able to download full version of the report in .csv format." -Select...,Select... -Status,Status -"Transaction Type","Transaction Type" -"Payment Type","Payment Type" -"PayPal Payment ID","PayPal Payment ID" -"Created At","Created At" -"Transaction ID","Transaction ID" -"Order ID","Order ID" -Type,Type -Amount,Amount -"Settlement Code","Settlement Code" -"Settlement Response Text","Settlement Response Text" -"Refund Ids","Refund Ids" -"Settlement Batch ID","Settlement Batch ID" -Currency,Currency -"Addresses must have at least one field filled in.","Addresses must have at least one field filled in." -"Company is too long.","Company is too long." -"Extended address is too long.","Extended address is too long." -"First name is too long.","First name is too long." -"Last name is too long.","Last name is too long." -"Locality is too long.","Locality is too long." -"Postal code can only contain letters, numbers, spaces, and hyphens.","Postal code can only contain letters, numbers, spaces, and hyphens." -"Postal code is required.","Postal code is required." -"Postal code may contain no more than 9 letter or number characters.","Postal code may contain no more than 9 letter or number characters." -"Region is too long.","Region is too long." -"Street address is required.","Street address is required." -"Street address is too long.","Street address is too long." -"US state codes must be two characters to meet PayPal Seller Protection requirements.","US state codes must be two characters to meet PayPal Seller Protection requirements." -"Country name is not an accepted country.","Country name is not an accepted country." -"Provided country information is inconsistent.","Provided country information is inconsistent." -"Country code is not accepted. Please contact the store administrator.","Country code is not accepted. Please contact the store administrator." -"Customer has already reached the maximum of 50 addresses.","Customer has already reached the maximum of 50 addresses." -"Address is invalid. Please contact the store administrator.","Address is invalid. Please contact the store administrator." -"Address is invalid.","Address is invalid." -"Billing address format is invalid.","Billing address format is invalid." -"Cardholder name is too long.","Cardholder name is too long." -"Credit card type is not accepted by this merchant account.","Credit card type is not accepted by this merchant account." -"CVV is required.","CVV is required." -"CVV must be 4 digits for American Express and 3 digits for other card types.","CVV must be 4 digits for American Express and 3 digits for other card types." -"Expiration date is required.","Expiration date is required." -"Expiration date is invalid.","Expiration date is invalid." -"Expiration year is invalid. It must be between 1975 and 2201.","Expiration year is invalid. It must be between 1975 and 2201." -"Expiration month is invalid.","Expiration month is invalid." -"Expiration year is invalid.","Expiration year is invalid." -"Credit card number is required.","Credit card number is required." -"Credit card number is invalid.","Credit card number is invalid." -"Credit card number must be 12-19 digits.","Credit card number must be 12-19 digits." -"CVV verification failed.","CVV verification failed." -"Postal code verification failed.","Postal code verification failed." -"Credit card number is prohibited.","Credit card number is prohibited." -"Incomplete PayPal account information.","Incomplete PayPal account information." -"Invalid PayPal account information.","Invalid PayPal account information." -"PayPal Accounts are not accepted by this merchant account.","PayPal Accounts are not accepted by this merchant account." -"Error communicating with PayPal.","Error communicating with PayPal." -"PayPal authentication expired.","PayPal authentication expired." -"Error executing PayPal order.","Error executing PayPal order." -"Error executing PayPal billing agreement.","Error executing PayPal billing agreement." -"Cannot provide a billing address unless also providing a credit card.","Cannot provide a billing address unless also providing a credit card." -"Transaction can only be voided if status is authorized, submitted_for_settlement, or - for PayPal - settlement_pending.","Transaction can only be voided if status is authorized, submitted_for_settlement, or - for PayPal - settlement_pending." -"Credit transactions cannot be refunded.","Credit transactions cannot be refunded." -"Cannot refund a transaction unless it is settled.","Cannot refund a transaction unless it is settled." -"Cannot submit for settlement unless status is authorized.","Cannot submit for settlement unless status is authorized." -"Customer does not have any credit cards.","Customer does not have any credit cards." -"Transaction has already been completely refunded.","Transaction has already been completely refunded." -"Payment instrument type is not accepted by this merchant account.","Payment instrument type is not accepted by this merchant account." -"Processor authorization code cannot be set unless for a voice authorization.","Processor authorization code cannot be set unless for a voice authorization." -"Refund amount is too large.","Refund amount is too large." -"Settlement amount is too large.","Settlement amount is too large." -"Cannot refund a transaction with a suspended merchant account.","Cannot refund a transaction with a suspended merchant account." -"Merchant account does not support refunds.","Merchant account does not support refunds." -"PayPal is not enabled for your merchant account.","PayPal is not enabled for your merchant account." -"Cannot refund a transaction transaction in settling status on this merchant account. Try again after the transaction has settled.","Cannot refund a transaction transaction in settling status on this merchant account. Try again after the transaction has settled." -"Cannot submit for partial settlement.","Cannot submit for partial settlement." -"Partial settlements are not supported by this processor.","Partial settlements are not supported by this processor." -"Transaction can not be voided if status of a PayPal partial settlement child transaction is settlement_pending.","Transaction can not be voided if status of a PayPal partial settlement child transaction is settlement_pending." -"Too many concurrent attempts to refund this transaction. Try again later.","Too many concurrent attempts to refund this transaction. Try again later." -"Too many concurrent attempts to void this transaction. Try again later.","Too many concurrent attempts to void this transaction. Try again later." \ No newline at end of file From c5499a3c1d749f0fde275144cfbeb99403465a07 Mon Sep 17 00:00:00 2001 From: Danny Verkade <danny@cream.nl> Date: Tue, 31 Jul 2018 09:14:44 -0400 Subject: [PATCH 0649/1171] - Changed typo "whith" to "with" - Fixed typo and missing word in test name --- .../Magento/Ui/view/base/web/js/grid/columns/multiselect.js | 2 +- app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js | 4 ++-- .../testsuite/Magento/Tax/Model/TaxRuleRepositoryTest.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js index 0a29728390592..46dd865ffdde8 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js @@ -259,7 +259,7 @@ define([ * Returns identifier of a record. * * @param {*} id - Id of a record or its' index in a rows array. - * @param {Boolean} [isIndex=false] - Flag that specifies whith what + * @param {Boolean} [isIndex=false] - Flag that specifies with what * kind of identifier we are dealing with. * @returns {*} */ diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js index ecc4ec1902d87..98c3eb1c6f882 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js @@ -257,7 +257,7 @@ define([ /** * Creates filter component configuration associated with the provided column. * - * @param {Column} column - Column component whith a basic filter declaration. + * @param {Column} column - Column component with a basic filter declaration. * @returns {Object} Filters' configuration. */ buildFilter: function (column) { @@ -331,7 +331,7 @@ define([ }, /** - * Finds filters whith a not empty data + * Finds filters with a not empty data * and sets them to the 'active' filters array. * * @returns {Filters} Chainable. diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxRuleRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxRuleRepositoryTest.php index cb45e1971ecab..0b98b9a519391 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxRuleRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxRuleRepositoryTest.php @@ -109,7 +109,7 @@ public function testSave() * @expectedExceptionMessage No such entity with taxRuleId = 9999 * @magentoDbIsolation enabled */ - public function testSaveThrowsExceptionIdTargetTaxRulDoesNotExist() + public function testSaveThrowsExceptionIdIfTargetTaxRuleDoesNotExist() { $taxRuleDataObject = $this->taxRuleFactory->create(); $taxRuleDataObject->setId(9999) From 95dd29f5b93434faa07adbcfaf0ee11814db99d1 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Thu, 17 May 2018 11:58:21 +0300 Subject: [PATCH 0650/1171] MAGETWO-91329: 'Use Default Value' checkboxes in Design section of a category are not checked by default --- .../view/adminhtml/requirejs-config.js | 15 +- .../adminhtml/ui_component/category_form.xml | 16 +- .../components/use-parent-settings/select.js | 15 ++ .../use-parent-settings/single-checkbox.js | 15 ++ .../use-parent-settings/textarea.js | 15 ++ .../toggle-disabled-mixin.js | 62 ++++++++ .../view/base/web/js/form/element/abstract.js | 3 +- .../form/element/helper/service.html | 3 +- .../toggle-disabled-mixin.test.js | 141 ++++++++++++++++++ .../Ui/base/js/form/element/abstract.test.js | 6 + 10 files changed, 280 insertions(+), 11 deletions(-) create mode 100644 app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/select.js create mode 100644 app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/single-checkbox.js create mode 100644 app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/textarea.js create mode 100644 app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/toggle-disabled-mixin.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/use-parent-settings/toggle-disabled-mixin.test.js diff --git a/app/code/Magento/Catalog/view/adminhtml/requirejs-config.js b/app/code/Magento/Catalog/view/adminhtml/requirejs-config.js index 9053c700d1a3b..0677b0a5811c2 100644 --- a/app/code/Magento/Catalog/view/adminhtml/requirejs-config.js +++ b/app/code/Magento/Catalog/view/adminhtml/requirejs-config.js @@ -17,5 +17,18 @@ var config = { }, deps: [ 'Magento_Catalog/catalog/product' - ] + ], + config: { + mixins: { + 'Magento_Catalog/js/components/use-parent-settings/select': { + 'Magento_Catalog/js/components/use-parent-settings/toggle-disabled-mixin': true + }, + 'Magento_Catalog/js/components/use-parent-settings/textarea': { + 'Magento_Catalog/js/components/use-parent-settings/toggle-disabled-mixin': true + }, + 'Magento_Catalog/js/components/use-parent-settings/single-checkbox': { + 'Magento_Catalog/js/components/use-parent-settings/toggle-disabled-mixin': true + } + } + } }; diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml index 2976f1be14fa5..537932a2d4daa 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml @@ -452,34 +452,34 @@ </checkbox> </formElements> </field> - <field name="custom_design" sortOrder="180" formElement="select"> + <field name="custom_design" component="Magento_Catalog/js/components/use-parent-settings/select" sortOrder="180" formElement="select"> <settings> <dataType>string</dataType> <label translate="true">Theme</label> <imports> - <link name="disabled">${ $.parentName }.custom_use_parent_settings:checked</link> + <link name="serviceDisabled">${ $.parentName }.custom_use_parent_settings:checked</link> </imports> </settings> </field> - <field name="page_layout" sortOrder="190" formElement="select" class="Magento\Catalog\Ui\Component\Form\Field\Category\PageLayout"> + <field name="page_layout" sortOrder="190" formElement="select" component="Magento_Catalog/js/components/use-parent-settings/select" class="Magento\Catalog\Ui\Component\Form\Field\Category\PageLayout"> <settings> <dataType>string</dataType> <label translate="true">Layout</label> <imports> - <link name="disabled">${ $.parentName }.custom_use_parent_settings:checked</link> + <link name="serviceDisabled">${ $.parentName }.custom_use_parent_settings:checked</link> </imports> </settings> </field> - <field name="custom_layout_update" sortOrder="200" formElement="textarea"> + <field name="custom_layout_update" component="Magento_Catalog/js/components/use-parent-settings/textarea" sortOrder="200" formElement="textarea"> <settings> <dataType>string</dataType> <label translate="true">Layout Update XML</label> <imports> - <link name="disabled">ns = ${ $.ns }, index = custom_use_parent_settings :checked</link> + <link name="serviceDisabled">${ $.parentName }.custom_use_parent_settings:checked</link> </imports> </settings> </field> - <field name="custom_apply_to_products" component="Magento_Ui/js/form/element/single-checkbox" sortOrder="210" formElement="checkbox"> + <field name="custom_apply_to_products" component="Magento_Catalog/js/components/use-parent-settings/single-checkbox" sortOrder="210" formElement="checkbox"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="default" xsi:type="number">0</item> @@ -492,7 +492,7 @@ <dataType>boolean</dataType> <label translate="true">Apply Design to Products</label> <imports> - <link name="disabled">ns = ${ $.ns }, index = custom_use_parent_settings:checked</link> + <link name="serviceDisabled">${ $.parentName }.custom_use_parent_settings:checked</link> </imports> </settings> <formElements> diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/select.js b/app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/select.js new file mode 100644 index 0000000000000..1ddb24f3eefbb --- /dev/null +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/select.js @@ -0,0 +1,15 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * @api + */ +define([ + 'Magento_Ui/js/form/element/select' +], function (Component) { + 'use strict'; + + return Component; +}); diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/single-checkbox.js b/app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/single-checkbox.js new file mode 100644 index 0000000000000..0f166d3b45582 --- /dev/null +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/single-checkbox.js @@ -0,0 +1,15 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * @api + */ +define([ + 'Magento_Ui/js/form/element/single-checkbox' +], function (Component) { + 'use strict'; + + return Component; +}); diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/textarea.js b/app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/textarea.js new file mode 100644 index 0000000000000..3ef2bb21241a7 --- /dev/null +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/textarea.js @@ -0,0 +1,15 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * @api + */ +define([ + 'Magento_Ui/js/form/element/textarea' +], function (Component) { + 'use strict'; + + return Component; +}); diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/toggle-disabled-mixin.js b/app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/toggle-disabled-mixin.js new file mode 100644 index 0000000000000..d140cc0fad74e --- /dev/null +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/components/use-parent-settings/toggle-disabled-mixin.js @@ -0,0 +1,62 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'underscore' +], function (_) { + 'use strict'; + + var mixin = { + defaults: { + imports: { + toggleDisabled: '${ $.parentName }.custom_use_parent_settings:checked' + }, + useParent: false, + useDefaults: false + }, + + /** + * Disable form input if settings for parent section is used + * or default value is applied. + * + * @param {Boolean} isUseParent + */ + toggleDisabled: function (isUseParent) { + var disabled = this.useParent = isUseParent; + + if (!disabled && !_.isUndefined(this.service)) { + disabled = !!this.isUseDefault(); + } + + this.saveUseDefaults(); + this.disabled(disabled); + }, + + /** + * Stores original state of the field. + */ + saveUseDefaults: function () { + this.useDefaults = this.disabled(); + }, + + /** @inheritdoc */ + setInitialValue: function () { + this._super(); + this.isUseDefault(this.useDefaults); + + return this; + }, + + /** @inheritdoc */ + toggleUseDefault: function (state) { + this._super(); + this.disabled(state || this.useParent); + } + }; + + return function (target) { + return target.extend(mixin); + }; +}); diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js index 5177b4a378d69..854af5494b385 100755 --- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js @@ -40,6 +40,7 @@ define([ showFallbackReset: false, additionalClasses: {}, isUseDefault: '', + serviceDisabled: false, valueUpdate: false, // ko binding valueUpdate switcherConfig: { @@ -97,7 +98,7 @@ define([ this._super(); this.observe('error disabled focused preview visible value warn notice isDifferedFromDefault') - .observe('isUseDefault') + .observe('isUseDefault serviceDisabled') .observe({ 'required': !!rules['required-entry'] }); diff --git a/app/code/Magento/Ui/view/base/web/templates/form/element/helper/service.html b/app/code/Magento/Ui/view/base/web/templates/form/element/helper/service.html index e3c3ab5b5df5c..46f0a2df52441 100644 --- a/app/code/Magento/Ui/view/base/web/templates/form/element/helper/service.html +++ b/app/code/Magento/Ui/view/base/web/templates/form/element/helper/service.html @@ -12,6 +12,7 @@ name: 'use_default[' + $data.index + ']', 'data-form-part': $data.ns " - ko-checked="isUseDefault"> + ko-checked="isUseDefault" + ko-disabled="$data.serviceDisabled"> <label translate="'Use Default Value'" attr="for: $data.uid + '_default'" class="admin__field-label"></label> </div> diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/use-parent-settings/toggle-disabled-mixin.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/use-parent-settings/toggle-disabled-mixin.test.js new file mode 100644 index 0000000000000..f3e53673a4e59 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/use-parent-settings/toggle-disabled-mixin.test.js @@ -0,0 +1,141 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/*eslint max-nested-callbacks: 0*/ +define([ + 'underscore', + 'Magento_Catalog/js/components/use-parent-settings/select', + 'Magento_Catalog/js/components/use-parent-settings/toggle-disabled-mixin' +], function (_, select, mixin) { + 'use strict'; + + var CustomInput = mixin(select), + defaultProperties = { + name: 'uiSelect', + dataScope: '', + provider: 'provider', + service: true + }, + obj; + + describe('toggle-disabled-mixin structure tests', function () { + var defaultContext = require.s.contexts._; + + obj = new CustomInput(defaultProperties); + + it('mixin is present in RequireJs config', function () { + var requireJsConfig = defaultContext.config + .config.mixins['Magento_Catalog/js/components/use-parent-settings/select']; + + expect( + requireJsConfig['Magento_Catalog/js/components/use-parent-settings/toggle-disabled-mixin'] + ).toBe(true); + }); + + it('Check for useParent property', function () { + expect(obj.hasOwnProperty('useParent')).toBeTruthy(); + expect(typeof obj.useParent).toEqual('boolean'); + expect(obj.useParent).toEqual(false); + }); + + it('Check for useDefaults property', function () { + expect(obj.hasOwnProperty('useDefaults')).toBeTruthy(); + expect(typeof obj.useDefaults).toEqual('boolean'); + expect(obj.useDefaults).toEqual(false); + }); + + it('Check for toggleDisabled method', function () { + expect(obj.toggleDisabled).toBeDefined(); + expect(typeof obj.toggleDisabled).toEqual('function'); + }); + + it('Check for saveUseDefaults method', function () { + expect(obj.saveUseDefaults).toBeDefined(); + expect(typeof obj.saveUseDefaults).toEqual('function'); + }); + + it('Check for setInitialValue method', function () { + expect(obj.setInitialValue).toBeDefined(); + expect(typeof obj.setInitialValue).toEqual('function'); + }); + + it('Check for toggleUseDefault method', function () { + expect(obj.toggleUseDefault).toBeDefined(); + expect(typeof obj.toggleUseDefault).toEqual('function'); + }); + }); + + describe('toggle-disabled-mixin functionality', function () { + var dataProvider = [ + { + defaults: { + useParent: false, + useDefaults: false + }, + expected: { + disabled: false + } + }, + { + defaults: { + useParent: true, + useDefaults: false + }, + expected: { + disabled: true + } + }, + { + defaults: { + useParent: false, + useDefaults: true + }, + expected: { + disabled: true + } + }, + { + defaults: { + useParent: true, + useDefaults: true + }, + expected: { + disabled: true + } + } + ]; + + dataProvider.forEach(function (state) { + describe(JSON.stringify(state.defaults), function () { + + beforeEach(function () { + obj = new CustomInput( + _.extend(defaultProperties, state.defaults) + ); + }); + + it('Check disabled state', function () { + expect(obj.disabled()).toEqual(state.expected.disabled); + }); + + it('Check checked state', function () { + expect(obj.isUseDefault()).toEqual(state.defaults.useDefaults); + }); + + it('Check of using parent settings', function () { + obj.toggleDisabled(true); + expect(obj.isUseDefault()).toEqual(state.defaults.useDefaults); + expect(obj.disabled()).toEqual(true); + }); + + it('Check of using self settings', function () { + obj.toggleDisabled(false); + expect(obj.isUseDefault()).toEqual(state.defaults.useDefaults); + expect(obj.disabled()).toEqual(obj.isUseDefault()); + }); + }); + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/abstract.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/abstract.test.js index c5b153f7cc100..390c5aa89fcc7 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/abstract.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/abstract.test.js @@ -303,5 +303,11 @@ define([ expect(model.validate).toHaveBeenCalled(); }); }); + describe('serviceDisabled property', function () { + it('check property state', function () { + expect(typeof model.serviceDisabled).toEqual('function'); + expect(model.serviceDisabled()).toBeFalsy(); + }); + }); }); }); From 3fee15b395f1cda50ed3ab332cb315f42f03f0f4 Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Tue, 31 Jul 2018 16:31:45 +0300 Subject: [PATCH 0651/1171] MAGETWO-93698: [2.3] Wrong date for Special Price in different locale - Fixed - Modified test --- .../Adminhtml/Product/Initialization/Helper.php | 16 ++++++++++++++++ .../Product/Initialization/HelperTest.php | 5 ++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index 95339870b4d61..7153f9fd0f3f9 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -159,6 +159,7 @@ public function initializeFromData(\Magento\Catalog\Model\Product $product, arra } $productData = $this->normalize($productData); + $productData = $this->convertSpecialFromDateStringToObject($productData); if (!empty($productData['is_downloadable'])) { $productData['product_has_weight'] = 0; @@ -452,4 +453,19 @@ private function fillProductOptions(Product $product, array $productOptions) return $product->setOptions($customOptions); } + + /** + * Convert string date presentation into object + * + * @param array $productData + * @return array + */ + private function convertSpecialFromDateStringToObject($productData) + { + if (isset($productData['special_from_date']) && $productData['special_from_date'] != '') { + $productData['special_from_date'] = new \DateTime($productData['special_from_date']); + } + + return $productData; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php index dce3f5886d1a8..aed87f918ebb8 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php @@ -198,10 +198,12 @@ public function testInitialize( 'option2' => ['is_delete' => false, 'name' => 'name1', 'price' => 'price1', 'option_id' => '13'], 'option3' => ['is_delete' => false, 'name' => 'name1', 'price' => 'price1', 'option_id' => '14'] ]; + $specialFromDate = '2018-03-03 19:30:00'; $productData = [ 'stock_data' => ['stock_data'], 'options' => $optionsData, - 'website_ids' => $websiteIds + 'website_ids' => $websiteIds, + 'special_from_date' => $specialFromDate, ]; if (!empty($tierPrice)) { $productData = array_merge($productData, ['tier_price' => $tierPrice]); @@ -306,6 +308,7 @@ public function testInitialize( } $this->assertEquals($expectedLinks, $resultLinks); + $this->assertEquals($specialFromDate, $productData['special_from_date']); } /** From 8f377a64a34efb847780586deb1b29b78d0fda3d Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Tue, 31 Jul 2018 10:16:43 -0500 Subject: [PATCH 0652/1171] MQE-1112: Bump MFTF version in Magento --- .../Checkout/Test/TestStep/EstimateShippingAndTaxStep.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/EstimateShippingAndTaxStep.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/EstimateShippingAndTaxStep.php index 21da4c66fa2f3..0780b7d13a285 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/EstimateShippingAndTaxStep.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/EstimateShippingAndTaxStep.php @@ -107,6 +107,7 @@ public function run() { $this->checkoutCart->open(); $this->checkoutCart->getCartBlock()->waitCartContainerLoading(); + sleep(20); /** @var \Magento\Checkout\Test\Fixture\Cart $cart */ if ($this->cart !== null) { $cart = $this->fixtureFactory->createByCode( From 07bde99259aad241a9c6309999c56400331b974d Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Tue, 31 Jul 2018 10:19:46 -0500 Subject: [PATCH 0653/1171] MQE-1112: Bump MFTF version in Magento --- .../Magento/Checkout/Test/TestStep/AddProductsToTheCartStep.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/AddProductsToTheCartStep.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/AddProductsToTheCartStep.php index 37a4d5c26189f..42d6c4502ef49 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/AddProductsToTheCartStep.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/AddProductsToTheCartStep.php @@ -123,6 +123,7 @@ public function run() } } $cart['data']['items'] = ['products' => $this->products]; + sleep(10); return ['cart' => $this->fixtureFactory->createByCode('cart', $cart)]; } } From 4bd5311a885e67c474503331f42f1419721580b0 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Tue, 31 Jul 2018 12:18:00 -0500 Subject: [PATCH 0654/1171] MAGETWO-90719: Exception is thrown when you re-order a product with custom options from Admin. - Fix integration test to cover changes in Zend framework --- .../Model/Product/Option/Type/File/ValidatorInfoTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfoTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfoTest.php index 41556d5558006..96bff8ff51af8 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfoTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfoTest.php @@ -111,11 +111,11 @@ public function testExceptionWithoutErrors() */ public function testValidate() { - $validateMock = $this->createPartialMock(\Zend_Validate::class, ['isValid']); - $validateMock->expects($this->once())->method('isValid')->will($this->returnValue(true)); + //use actual zend class to test changed functionality + $validate = $this->objectManager->create(\Zend_Validate::class ); $this->validateFactoryMock->expects($this->once()) ->method('create') - ->will($this->returnValue($validateMock)); + ->will($this->returnValue($validate)); $this->assertTrue( $this->model->validate( $this->getOptionValue(), From cdf9769ec3b2979afb2ad57df665b7d44e09d75c Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Tue, 31 Jul 2018 14:27:15 -0500 Subject: [PATCH 0655/1171] MAGETWO-90719: Exception is thrown when you re-order a product with custom options from Admin. Fix indentation and remove extra space. --- .../Model/Product/Option/Type/File/ValidatorInfoTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfoTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfoTest.php index 96bff8ff51af8..7c340c37ba9a9 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfoTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfoTest.php @@ -112,7 +112,7 @@ public function testExceptionWithoutErrors() public function testValidate() { //use actual zend class to test changed functionality - $validate = $this->objectManager->create(\Zend_Validate::class ); + $validate = $this->objectManager->create(\Zend_Validate::class); $this->validateFactoryMock->expects($this->once()) ->method('create') ->will($this->returnValue($validate)); From 6e4f1f44dfc7ff26ec35603b5e1f15a93f8706dd Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Tue, 31 Jul 2018 18:32:28 -0500 Subject: [PATCH 0656/1171] MC-3305: Architectural Review for MC-1416 - add changes on setup.js --- lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js index 112819c3f077d..110d5a09a4a34 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js @@ -73,19 +73,6 @@ define([ return this.wysiwygInstance.encodeContent(content); }, - /** - * @return {String} - */ - getContent: function () { - return this.wysiwygInstance.getContent(); - }, - - /** - * @param {String} content - */ - setContent: function (content) { - this.wysiwygInstance.setContent(content); - } }; window.wysiwygSetup = wysiwygSetup; From 6a66da6b9bf01c97dba7383764e5ef4229152198 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Sun, 29 Jul 2018 21:01:31 -0500 Subject: [PATCH 0657/1171] MC-3308: Automate MFTF for MC-1416 --- .../Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml | 4 ++-- .../Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml | 1 + .../Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml index 24900ad33b560..2f5a20b887369 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml @@ -8,8 +8,8 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> <actionGroup name="VerifyTinyMCEActionGroup"> - <waitForElementVisible selector="{{TinyMCESection.TinyMCE4}}" stepKey="waitForTinyMCE" time="30" /> - <seeElement selector="{{TinyMCESection.TinyMCE4}}" stepKey="seeTinyMCE4" /> + <!--<waitForElementVisible selector="{{TinyMCESection.TinyMCE4}}" stepKey="waitForTinyMCE" time="30" />--> + <!--<seeElement selector="{{TinyMCESection.TinyMCE4}}" stepKey="seeTinyMCE4" />--> <seeElement selector="{{TinyMCESection.Style}}" stepKey="assertInfo2"/> <seeElement selector="{{TinyMCESection.Bold}}" stepKey="assertInfo3"/> <seeElement selector="{{TinyMCESection.Italic}}" stepKey="assertInfo4"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml index 468dbecb20e02..5cc93ba67272a 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml @@ -12,5 +12,6 @@ <element name="pageTitle" type="input" selector="input[name=title]"/> <element name="isActive" type="button" selector="//input[@name='is_active' and @value='{{var1}}']" parameterized="true"/> <element name="duplicatedURLKey" type="input" selector="//input[contains(@data-value,'{{var1}}')]" parameterized="true"/> + <element name="contentHeading" type="text" selector="//label[text()='Content Heading']"/> </section> </sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml index 8015134c90f9c..283e90b688f20 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml @@ -19,6 +19,7 @@ <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> <element name="InsertVariableBtn" type="button" selector=".scalable.add-variable.plugin"/> <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> + <element name="imageSource" type="text" selector="//img[contains(@src,'{{var1}}')]" parameterized="true"/> </section> <section name="CmsDesignSection"> <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> From bca78669260e11c80a6476388714f214f2801848 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Mon, 30 Jul 2018 00:45:04 -0500 Subject: [PATCH 0658/1171] MC-3308: Automate MFTF for MC-1416 --- app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml index fef9e2d851652..0d45665939b01 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml @@ -98,9 +98,6 @@ <element name="PageSize" type="input" selector="input[name='parameters[page_size]']"/> <element name="ProductAttribute" type="multiselect" selector="select[name='parameters[show_attributes][]']" /> <element name="ButtonToShow" type="multiselect" selector="select[name='parameters[show_buttons][]']"/> - <!--Widget on Storefront--> - <element name="CategoryWidget" type="text" selector="//a[@href='http://magento2.vagrant42/{{var1}}.html?___store=default']" parameterized="true"/> - <element name="CMSPageWidget" type="text" selector="//a[@href='http://magento2.vagrant42/home']"/> <!--Compare on Storefront--> <element name="ProductName" type="text" selector=".product.name.product-item-name" /> <element name="CompareBtn" type="button" selector=".action.tocompare"/> From c58a85b96945c6c929a4cd270a71e38e2a398b07 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Tue, 31 Jul 2018 19:27:30 -0500 Subject: [PATCH 0659/1171] MC-3308: Automate MFTF for MC-1416 --- app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml | 1 + .../Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml b/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml index 5df8ebab2d380..84561d6cd96ca 100644 --- a/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml +++ b/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml @@ -75,6 +75,7 @@ <data key="extension">jpg</data> <data key="content">Image content. Yeah.</data> <data key="height">1000</data> + <data key="path">wysiwyg</data> </entity> <entity name="ImageFolder" type="uploadImage"> <data key="name" unique="suffix">Test</data> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml index 5cc93ba67272a..468dbecb20e02 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml @@ -12,6 +12,5 @@ <element name="pageTitle" type="input" selector="input[name=title]"/> <element name="isActive" type="button" selector="//input[@name='is_active' and @value='{{var1}}']" parameterized="true"/> <element name="duplicatedURLKey" type="input" selector="//input[contains(@data-value,'{{var1}}')]" parameterized="true"/> - <element name="contentHeading" type="text" selector="//label[text()='Content Heading']"/> </section> </sections> From a433a4e08ea86532e0ddeeb69e36695f80064d41 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 1 Aug 2018 07:56:46 +0300 Subject: [PATCH 0660/1171] Fixed static issue --- lib/web/mage/utils/template.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/web/mage/utils/template.js b/lib/web/mage/utils/template.js index f3b0418f75b00..7c50226d6aa3a 100644 --- a/lib/web/mage/utils/template.js +++ b/lib/web/mage/utils/template.js @@ -2,6 +2,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +/* eslint-disable no-shadow */ + define([ 'jquery', 'underscore', From 6bcc35aa856296a2d66242692e0487733bd6a2ed Mon Sep 17 00:00:00 2001 From: Dzmitry Tabusheu <dzmitry_tabusheu@epam.com> Date: Tue, 17 Jul 2018 12:43:29 +0300 Subject: [PATCH 0661/1171] MAGETWO-91762: [Magento Cloud] - MYSQL Message queue is fetching messages from new to old - Change queue messages sorting order from DESC to ASC --- app/code/Magento/MysqlMq/Model/ResourceModel/Queue.php | 2 +- .../Magento/MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/MysqlMq/Model/ResourceModel/Queue.php b/app/code/Magento/MysqlMq/Model/ResourceModel/Queue.php index c1cdd23be622c..d50ed851b64a9 100644 --- a/app/code/Magento/MysqlMq/Model/ResourceModel/Queue.php +++ b/app/code/Magento/MysqlMq/Model/ResourceModel/Queue.php @@ -151,7 +151,7 @@ public function getMessages($queueName, $limit = null) 'queue_message_status.status IN (?)', [QueueManagement::MESSAGE_STATUS_NEW, QueueManagement::MESSAGE_STATUS_RETRY_REQUIRED] )->where('queue.name = ?', $queueName) - ->order('queue_message_status.updated_at DESC'); + ->order('queue_message_status.updated_at ASC'); if ($limit) { $select->limit($limit); diff --git a/app/code/Magento/MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php b/app/code/Magento/MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php index 7f364054ec921..d3fe09a712945 100644 --- a/app/code/Magento/MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php +++ b/app/code/Magento/MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php @@ -206,7 +206,7 @@ public function testGetMessages() ] )->willReturnSelf(); $select->expects($this->once()) - ->method('order')->with('queue_message_status.updated_at DESC')->willReturnSelf(); + ->method('order')->with('queue_message_status.updated_at ASC')->willReturnSelf(); $select->expects($this->once())->method('limit')->with($limit)->willReturnSelf(); $connection->expects($this->once())->method('fetchAll')->with($select)->willReturn($messages); $this->assertEquals($messages, $this->queue->getMessages($queueName, $limit)); From a7c3fc6e3d0c09ca9ec5e1802f7d7336e26f4bc6 Mon Sep 17 00:00:00 2001 From: Stsiapan Korf <Stsiapan_Korf@epam.com> Date: Thu, 19 Jul 2018 18:53:07 +0300 Subject: [PATCH 0662/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Add reload and error message --- .../Catalog/view/frontend/web/js/catalog-add-to-cart.js | 7 +++++++ app/code/Magento/Checkout/Controller/Cart/Add.php | 3 +++ 2 files changed, 10 insertions(+) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js index 83e91d3c3d4c7..a06dc47794773 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js @@ -159,6 +159,13 @@ define([ .html(res.product.statusText); } self.enableAddToCartButton(form); + }, + + /** @inheritdoc */ + complete: function (res) { + if (res.state() == 'rejected') { + location.reload(); + } } }); }, diff --git a/app/code/Magento/Checkout/Controller/Cart/Add.php b/app/code/Magento/Checkout/Controller/Cart/Add.php index 92dd8dd8f251c..5f54ca3395646 100644 --- a/app/code/Magento/Checkout/Controller/Cart/Add.php +++ b/app/code/Magento/Checkout/Controller/Cart/Add.php @@ -80,6 +80,9 @@ protected function _initProduct() public function execute() { if (!$this->_formKeyValidator->validate($this->getRequest())) { + $this->messageManager->addErrorMessage( + __('Your session has expired') + ); return $this->resultRedirectFactory->create()->setPath('*/*/'); } From 0ab96dd6286154bdfd5b23e36280257ffffd393e Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Thu, 19 Jul 2018 15:04:55 +0300 Subject: [PATCH 0663/1171] MAGETWO-91500: Custom role backend user cannot place an admin order using Braintree payment - for 2.2.x - Fix resource access for getting client token --- app/code/Magento/Braintree/etc/acl.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/code/Magento/Braintree/etc/acl.xml b/app/code/Magento/Braintree/etc/acl.xml index a188586cc0a26..f50a55911d687 100644 --- a/app/code/Magento/Braintree/etc/acl.xml +++ b/app/code/Magento/Braintree/etc/acl.xml @@ -14,6 +14,15 @@ <resource id="Magento_Braintree::settlement_report" title="Braintree Settlement" sortOrder="80" /> </resource> </resource> + <resource id="Magento_Sales::sales"> + <resource id="Magento_Sales::sales_operation"> + <resource id="Magento_Sales::sales_order"> + <resource id="Magento_Sales::actions"> + <resource id="Magento_Braintree::get_client_token" title="Get Client Token Braintree" sortOrder="170" /> + </resource> + </resource> + </resource> + </resource> </resource> </resources> </acl> From 09def5ececf9a13ffbf1ef73aee74f309d67506b Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Thu, 26 Jul 2018 13:22:39 +0300 Subject: [PATCH 0664/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Add unit test in case Expired session --- .../Test/Unit/Controller/Cart/AddTest.php | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php diff --git a/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php b/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php new file mode 100644 index 0000000000000..d2878a7c8dc67 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Checkout\Test\Unit\Controller\Cart; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class AddTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var \Magento\Framework\Data\Form\FormKey\Validator|\PHPUnit_Framework_MockObject_MockObject + */ + private $formKeyValidator; + + /** + * @var \Magento\Framework\Controller\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultRedirectFactory; + + /** + * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $request; + + /** + * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $messageManager; + + public function setUp() + { + $this->formKeyValidator = $this->getMockBuilder(\Magento\Framework\Data\Form\FormKey\Validator::class) + ->disableOriginalConstructor()->getMock(); + $this->resultRedirectFactory = + $this->getMockBuilder(\Magento\Framework\Controller\Result\RedirectFactory::class) + ->disableOriginalConstructor()->getMock(); + $this->request = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + ->disableOriginalConstructor()->getmock(); + $this->messageManager = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) + ->disableOriginalConstructor()->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->add = $this->objectManagerHelper->getObject( + \Magento\Checkout\Controller\Cart\Add::class, + [ + '_formKeyValidator' => $this->formKeyValidator, + 'resultRedirectFactory' => $this->resultRedirectFactory, + '_request' => $this->request, + 'messageManager' => $this->messageManager + ] + ); + } + + /** + * Test for method execute. + * + * @return void + */ + public function testExecute() + { + $redirect = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) + ->disableOriginalConstructor() + ->getMock(); + $path = '*/*/'; + + $this->formKeyValidator->expects($this->once())->method('validate')->with($this->request)->willReturn(false); + $this->messageManager->expects($this->once())->method('addErrorMessage'); + $this->resultRedirectFactory->expects($this->once())->method('create')->willReturn($redirect); + $redirect->expects($this->once())->method('setPath')->with($path)->willReturnSelf(); + $this->assertEquals($redirect, $this->add->execute()); + } +} From 58394bee5793aa73e62b65c536fc4be1520f23a5 Mon Sep 17 00:00:00 2001 From: Vital_Pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Mon, 30 Jul 2018 19:42:21 +0300 Subject: [PATCH 0665/1171] MAGETWO-91524: "element.disabled is not a function"error is thrown when configurable products are generated with an attribute named "design" --- .../view/adminhtml/web/js/variations/variations.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js index 9aa67beb3a51d..e7c8aa6f71745 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js @@ -357,12 +357,12 @@ define([ var element; _.each(this.disabledAttributes, function (attribute) { - registry.get('index = ' + attribute).disabled(false); + registry.get('inputName = ' + 'product[' + attribute + ']').disabled(false); }); this.disabledAttributes = []; _.each(attributes, function (attribute) { - element = registry.get('index = ' + attribute.code); + element = registry.get('inputName = ' + 'product[' + attribute.code + ']'); if (!_.isUndefined(element)) { element.disabled(true); From 641caa32aba6ea6cee418a97f16b360b515d46cc Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Mon, 30 Jul 2018 19:47:39 +0400 Subject: [PATCH 0666/1171] MAGETWO-91524: 'element.disabled is not a function'error is thrown when configurable products are generated with an attribute named 'design' - Add automated test for the bug MAGETWO-93307 --- ...eProductAttributeNameDesignActionGroup.xml | 173 ++++++++++++++++++ ...igurableProductAttributeNameDesignData.xml | 35 ++++ ...bleProductsaAttributeNameDesignSection.xml | 66 +++++++ ...igurableProductAttributeNameDesignTest.xml | 34 ++++ 4 files changed, 308 insertions(+) create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductsaAttributeNameDesignSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml new file mode 100644 index 0000000000000..9c0f8d4ce9692 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml @@ -0,0 +1,173 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + + <actionGroup name="GotoCatalogProductsPage"> + + <!--Click on Catalog item--> + <click stepKey="clickOnCatalogItem" selector="{{CatalogProductsSection.catalogItem}}"/> + + <waitForPageLoad stepKey="waitForCatalogLoad" time="3"/> + + <!--Click on Products item--> + <click stepKey="clickOnProductItem" selector="{{CatalogProductsSection.productItem}}"/> + + <waitForPageLoad stepKey="waitForCatalogProductPageLoad" time="3"/> + + <!--Assert we have gone desired page successfully--> + <seeInCurrentUrl stepKey="assertWeAreOnTheCatalogProductPage" url="{{assertionData.catalogProduct}}"/> + + </actionGroup> + + <actionGroup name="GotoConfigurableProductPage"> + + <!--Click on Add product item--> + <click stepKey="clickOnAddProductItem" selector="{{ConfigurableProductSection.addProductItem}}"/> + + <!--Click on Configuration Product item--> + <click stepKey="clickOnConfigurationProductItem" selector="{{ConfigurableProductSection.configProductItem}}"/> + + <waitForPageLoad stepKey="waitForConfigurableProductPageLoad" time="3"/> + + <!--Assert we have gone desired page successfully--> + <seeInCurrentUrl stepKey="assertWeAreOnTheConfigurableProductPage" url="{{assertionData.configurableProduct}}"/> + + </actionGroup> + + <actionGroup name="FillAllRequiredFields"> + + <!--Fill In Product Name Fields--> + <fillField stepKey="fillInProductNameFields" selector="{{NewProduct.productName}}" userInput="{{NewProductsData.productName}}"/> + + <!--Fill In Price Fields--> + <fillField stepKey="fillInPriceFields" selector="{{NewProduct.price}}" userInput="{{NewProductsData.price}}"/> + + <!--Fill In Weight Fields--> + <fillField stepKey="fillInWeightFields" selector="{{NewProduct.weight}}" userInput="{{NewProductsData.weight}}"/> + + <!--Click "Create Configurations" button in configurations field--> + <click stepKey="clickOnCreateConfigurationsButton" selector="{{NewProduct.createConfigurationButton}}"/> + + <wait stepKey="waitForCreateProductConfigurationsPageLoad" time="3"/> + + <!--Click "Create New Attribute" button--> + <click stepKey="clickOnCreateNewAttributeButton" selector="{{NewProduct.createNewAttributeButton}}"/> + + <wait stepKey="waitForNewAttributePageLoad" time="3"/> + + </actionGroup> + + <actionGroup name="FillNewAttributeFields"> + + <switchToIFrame stepKey="NewAttributePage" selector="{{NewProduct.newAttributeIFrame}}"/> + + <!--Fill In Product Name Fields--> + <fillField stepKey="fillInDefaultLabelField" selector="{{NewProduct.defaultLabel}}" userInput="{{NewProductsData.defaultLabel}}"/> + + <!--Add option 1 to attribute--> + <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption1"/> + <fillField stepKey="fillInAdminField1" selector="{{NewProduct.adminField1}}" userInput="{{NewProductsData.adminField1}}"/> + <fillField stepKey="fillInDefaultStoreViewField1" selector="{{NewProduct.defaultStoreViewField1}}" userInput="{{NewProductsData.defaultStoreViewField1}}"/> + + <!--Add option 2 to attribute--> + <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption2"/> + <fillField stepKey="fillInAdminField2" selector="{{NewProduct.adminField2}}" userInput="{{NewProductsData.adminField2}}"/> + <fillField stepKey="fillInDefaultStoreViewField2" selector="{{NewProduct.defaultStoreViewField2}}" userInput="{{NewProductsData.defaultStoreViewField2}}"/> + + <!--Add option 3 to attribute--> + <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption3"/> + <fillField stepKey="fillInAdminField3" selector="{{NewProduct.adminField3}}" userInput="{{NewProductsData.adminField3}}"/> + <fillField stepKey="fillInDefaultStoreViewField3" selector="{{NewProduct.defaultStoreViewField3}}" userInput="{{NewProductsData.defaultStoreViewField3}}"/> + + <!--Add option 4 to attribute--> + <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption4"/> + <fillField stepKey="fillInAdminField4" selector="{{NewProduct.adminField4}}" userInput="{{NewProductsData.adminField4}}"/> + <fillField stepKey="fillInDefaultStoreViewField4" selector="{{NewProduct.defaultStoreViewField4}}" userInput="{{NewProductsData.defaultStoreViewField4}}"/> + + <!--Add option 5 to attribute--> + <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption5"/> + <fillField stepKey="fillInAdminField5" selector="{{NewProduct.adminField5}}" userInput="{{NewProductsData.adminField5}}"/> + <fillField stepKey="fillInDefaultStoreViewField5" selector="{{NewProduct.defaultStoreViewField5}}" userInput="{{NewProductsData.defaultStoreViewField5}}"/> + + <!--Click Save Attribute button--> + <click selector="{{NewProduct.saveAttributeButton}}" stepKey="clickSaveAttributeButton"/> + + <wait stepKey="waitForSavingSettings" time="3"/> + + <!--Select created Attribute --> + <click selector="{{ConfigurableProductSection.selectCreatedAttribute}}" stepKey="selectCreatedAttribute"/> + + <!--Click Next button--> + <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton"/> + + <wait stepKey="waitForNextPageLoaded" time="3"/> + + <!--Select all the options of all the attributes button--> + <click selector="{{CreateProductConfigurations.item1}}" stepKey="selectItem1"/> + <click selector="{{CreateProductConfigurations.item2}}" stepKey="selectItem2"/> + <click selector="{{CreateProductConfigurations.item3}}" stepKey="selectItem3"/> + <click selector="{{CreateProductConfigurations.item4}}" stepKey="selectItem4"/> + <click selector="{{CreateProductConfigurations.item5}}" stepKey="selectItem5"/> + + <!--Click Next button--> + <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton2"/> + + <wait stepKey="waitForBulkImagesPricePageLoaded" time="3"/> + + <!--Click Next button--> + <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton3"/> + + <wait stepKey="waitForSummaryPageLoaded" time="3"/> + + <!--Click Generate Configure button--> + <click selector="{{ConfigurableProductSection.generateConfigure}}" stepKey="generateConfigure"/> + + <wait stepKey="waitForGenerateConfigure" time="3"/> + + <!-- This Error message shouldn't appear: Test will pass when bug will be fixed--> + <dontSee selector="{{CreateProductConfigurations.errorMessage}}" userInput="{{assertionData.errorMessage}}" stepKey="dontSeeError"/> + + <!--Close frame--> + <conditionalClick selector="{{ConfigurableProductSection.closeFrame}}" dependentSelector="{{ConfigurableProductSection.closeFrame}}" visible="1" stepKey="closeFrame"/> + + <wait stepKey="waitForClosingFrame" time="3"/> + + </actionGroup> + + <actionGroup name="DeleteCreatedAttributeIfExist"> + + <!--Click on Stores item--> + <click stepKey="clickOnStoresItem" selector="{{CatalogProductsSection.storesItem}}"/> + + <waitForPageLoad stepKey="waitForCatalogLoad" time="3"/> + + <!--Click on Products item--> + <click stepKey="clickOnStoresProductItem" selector="{{CatalogProductsSection.storesProductItem}}"/> + + <waitForPageLoad stepKey="waitForStoresProductPageLoad" time="3"/> + + <!--Click on created Attribute item if it exist--> + <conditionalClick selector="{{CatalogProductsSection.createdAttributeItem}}" dependentSelector="{{CatalogProductsSection.createdAttributeItem}}" visible="1" stepKey="clickOnCreatedAttributeItem"/> + + <waitForPageLoad stepKey="waitForCreatedAttributeLoad" time="3"/> + + <!--Click on Delete Attribute item--> + <conditionalClick stepKey="clickOnDeleteAttributeItem" selector="{{CatalogProductsSection.deleteAttributeItem}}" dependentSelector="{{CatalogProductsSection.deleteAttributeItem}}" visible="1"/> + + <waitForPageLoad stepKey="waitForDeletedDialogOpened" time="3"/> + + <!--Click on OK button--> + <conditionalClick stepKey="clickOnOKButton" selector="{{CatalogProductsSection.okButton}}" dependentSelector="{{CatalogProductsSection.okButton}}" visible="1"/> + + <waitForPageLoad stepKey="waitFordAttributeDeleted" time="3"/> + + </actionGroup> + +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml new file mode 100644 index 0000000000000..685c913679a99 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="NewProductsData" type="user"> + <data key="productName">Shoes</data> + <data key="price">60</data> + <data key="weight">100</data> + <data key="defaultLabel">design</data> + <data key="adminField1">red</data> + <data key="defaultStoreViewField1">red123</data> + <data key="adminField2">blue</data> + <data key="defaultStoreViewField2">blue123</data> + <data key="adminField3">yellow</data> + <data key="defaultStoreViewField3">yellow123</data> + <data key="adminField4">green</data> + <data key="defaultStoreViewField4">green123</data> + <data key="adminField5">black</data> + <data key="defaultStoreViewField5">black123</data> + <data key="attributeCodeField">bug91524</data> + </entity> + + <entity name="assertionData" type="assertion"> + <data key="catalogProduct">product</data> + <data key="configurableProduct">configurable</data> + <data key="errorMessage">element.disabled is not a function</data> + </entity> + +</entities> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductsaAttributeNameDesignSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductsaAttributeNameDesignSection.xml new file mode 100644 index 0000000000000..6a7b428d8649c --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductsaAttributeNameDesignSection.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="CatalogProductsSection"> + <element name="catalogItem" type="button" selector="//*[@id='menu-magento-catalog-catalog']/a/span"/> + <element name="productItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-products']/a"/> + <element name="storesItem" type="button" selector="//*[@id='menu-magento-backend-stores']/a/span"/> + <element name="storesProductItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-attributes-attributes']/a"/> + <element name="createdAttributeItem" type="button" selector="//td[contains(@class, 'col-attr-code') and normalize-space()='design']"/> + <element name="deleteAttributeItem" type="button" selector="//*[@id='delete']"/> + <element name="okButton" type="button" selector=" //footer[@class='modal-footer']//*[contains(text(),'OK')]"/> + + <element name="messageSuccessSavedProduct" type="button" selector=" //div[@data-ui-id='messages-message-success']"/> + </section> + + <section name="ConfigurableProductSection"> + <element name="addProductItem" type="button" selector="//*[@id='add_new_product']/button[2]"/> + <element name="configProductItem" type="button" selector="//*[@id='add_new_product']//*[contains(text(),'Configurable Product')]"/> + <element name="nextButton" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Next')]"/> + <element name="generateConfigure" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Generate Products')]"/> + <element name="selectCreatedAttribute" type="button" selector="//*[@class='admin__data-grid-wrap']//td[normalize-space()='design']/preceding-sibling::td"/> + <element name="closeFrame" type="button" selector="//*[@class='modal-header']//*[contains(text(),'Create Product Configurations')]/following-sibling::button"/> + </section> + + <section name="NewProduct"> + <element name="productName" type="input" selector="//input[@name='product[name]']"/> + <element name="price" type="input" selector="//input[@name='product[price]']"/> + <element name="weight" type="input" selector="//input[@name='product[weight]']"/> + <element name="createConfigurationButton" type="button" selector="//*[contains(text(),'Create Configurations')]"/> + <element name="createNewAttributeButton" type="button" selector="//*[contains(text(),'Create New Attribute')]"/> + <element name="newAttributeIFrame" type="iframe" selector="create_new_attribute_container"/> + <element name="defaultLabel" type="input" selector="//*[@id='attribute_label']"/> + <element name="addOptionButton" type="button" selector="//*[@id='add_new_option_button']"/> + <element name="adminField1" type="input" selector="//input[@name='option[value][option_0][0]']"/> + <element name="defaultStoreViewField1" type="input" selector="//input[@name='option[value][option_0][1]']"/> + <element name="adminField2" type="input" selector="//input[@name='option[value][option_1][0]']"/> + <element name="defaultStoreViewField2" type="input" selector="//input[@name='option[value][option_1][1]']"/> + <element name="adminField3" type="input" selector="//input[@name='option[value][option_2][0]']"/> + <element name="defaultStoreViewField3" type="input" selector="//input[@name='option[value][option_2][1]']"/> + <element name="adminField4" type="input" selector="//input[@name='option[value][option_3][0]']"/> + <element name="defaultStoreViewField4" type="input" selector="//input[@name='option[value][option_3][1]']"/> + <element name="adminField5" type="input" selector="//input[@name='option[value][option_4][0]']"/> + <element name="defaultStoreViewField5" type="input" selector="//input[@name='option[value][option_4][1]']"/> + <element name="saveAttributeButton" type="button" selector="//*[@id='save']"/> + <element name="advancedAttributeProperties" type="button" selector="//*[@id='advanced_fieldset-wrapper']//*[contains(text(),'Advanced Attribute Properties')]"/> + <element name="attributeCodeField" type="input" selector="//*[@id='attribute_code']"/> + + </section> + + <section name="CreateProductConfigurations"> + <element name="item1" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'red')]/preceding-sibling::input"/> + <element name="item2" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'blue')]/preceding-sibling::input"/> + <element name="item3" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'yellow')]/preceding-sibling::input"/> + <element name="item4" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'green')]/preceding-sibling::input"/> + <element name="item5" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'black')]/preceding-sibling::input"/> + <element name="errorMessage" type="input" selector="//div[@data-ui-id='messages-message-error']"/> + </section> + +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml new file mode 100644 index 0000000000000..c051a1b29d0ce --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="ConfigurableProductAttributeNameDesignTest"> + <annotations> + <title value="Generation of configurable products with an attribute named 'design'"/> + <features value="Product Customizable Option"/> + <severity value="AVERAGE"/> + <testCaseId value="MAGETWO-93307"/> + <stories value="MAGETWO-91524 : 'element.disabled is not a function' error is thrown when configurable products are generated with an attribute named 'design'"/> + <group value="product"/> + </annotations> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + + <actionGroup ref="DeleteCreatedAttributeIfExist" stepKey="deleteCreatedAttributeIfExist"/> + + <actionGroup ref="GotoCatalogProductsPage" stepKey="goToCatalogProductsPage"/> + + <actionGroup ref="GotoConfigurableProductPage" stepKey="goToConfigurableProductPage"/> + + <actionGroup ref="FillAllRequiredFields" stepKey="fillInAllRequiredFields"/> + + <actionGroup ref="FillNewAttributeFields" stepKey="fillInNewAttributeFields"/> + + </test> +</tests> From 6a3c37889498b2c12f3af62cb4f50edff84acc26 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Wed, 1 Aug 2018 10:26:45 -0500 Subject: [PATCH 0667/1171] MC-3305: Architectural Review for MC-1416 Add generic event namespaces --- .../mage/adminhtml/wysiwyg/tiny_mce/setup.js | 2 +- .../wysiwyg/tiny_mce/tinymce4Adapter.js | 67 ++++++++++++------- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js index 110d5a09a4a34..223c6d6ab04ea 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js @@ -71,7 +71,7 @@ define([ */ updateContent: function (content) { return this.wysiwygInstance.encodeContent(content); - }, + } }; window.wysiwygSetup = wysiwygSetup; diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index 398199d730c71..320a0cc76bc23 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -19,6 +19,19 @@ define([ var tinyMce4Wysiwyg = Class.create(); tinyMce4Wysiwyg.prototype = { + EVENT: { + AFTER_INITIALIZATION: 'wysiwygEditorInitialized', + AFTER_CONTENT_CHANGE: 'tinymceChange', + AFTER_UNDO: 'tinymceUndo', + AFTER_PASTE: 'tinymcePaste', + BEFORE_SET_CONTENT: 'tinymceBeforeSetContent', + AFTER_SET_CONTENT: 'tinymceSetContent', + AFTER_SAVE: 'tinymceSaveContent', + AFTER_OPEN_FILE_BROWSER: 'open_browser_callback', + AFTER_FORM_SUBMIT: 'formSubmit', + AFTER_BLUR: 'tinymceBlur', + AFTER_FOCUS: 'tinymceFocus' + }, mediaBrowserOpener: null, mediaBrowserTargetElementId: null, magentoVariablesPlugin: null, @@ -42,12 +55,13 @@ define([ 'onUndo' ); - varienGlobalEvents.attachEventHandler('tinymceChange', this.onChangeContent); - varienGlobalEvents.attachEventHandler('tinymceBeforeSetContent', this.beforeSetContent); - varienGlobalEvents.attachEventHandler('tinymceSetContent', this.updateTextArea); - varienGlobalEvents.attachEventHandler('tinymceSaveContent', this.saveContent); - varienGlobalEvents.attachEventHandler('tinymceUndo', this.onUndo); - varienGlobalEvents.attachEventHandler('open_browser_callback', this.openFileBrowser); + varienGlobalEvents.attachEventHandler(this.EVENT.AFTER_CONTENT_CHANGE, this.onChangeContent); + varienGlobalEvents.attachEventHandler(this.EVENT.BEFORE_SET_CONTENT, this.beforeSetContent); + varienGlobalEvents.attachEventHandler(this.EVENT.AFTER_SET_CONTENT, this.updateTextArea); + varienGlobalEvents.attachEventHandler(this.EVENT.AFTER_SAVE, this.saveContent); + varienGlobalEvents.attachEventHandler(this.EVENT.AFTER_UNDO, this.onUndo); + varienGlobalEvents.attachEventHandler(this.EVENT.AFTER_OPEN_FILE_BROWSER, this.openFileBrowser); + varienGlobalEvents.attachEventHandler(this.EVENT.AFTER_FORM_SUBMIT, this.onFormValidation); if (typeof tinyMceEditors === 'undefined') { window.tinyMceEditors = $H({}); @@ -110,7 +124,7 @@ define([ tinyMCE4.ui.FloatPanel.zIndex = settings.toolbarZIndex; } - varienGlobalEvents.removeEventHandler('tinymceChange', this.onChangeContent); + varienGlobalEvents.removeEventHandler(this.EVENT.AFTER_CONTENT_CHANGE, this.onChangeContent); } jQuery.when.apply(jQuery, deferreds).done(function () { @@ -172,7 +186,8 @@ define([ */ getSettings: function () { var settings, - eventBus = this.eventBus; + eventBus = this.eventBus, + wysiwygAdapter = this; settings = { selector: '#' + this.getId(), @@ -193,44 +208,44 @@ define([ var onChange; editor.on('BeforeSetContent', function (evt) { - varienGlobalEvents.fireEvent('tinymceBeforeSetContent', evt); - eventBus.fireEvent('tinymceBeforeSetContent'); + varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.BEFORE_SET_CONTENT, evt); + eventBus.fireEvent(wysiwygAdapter.EVENT.BEFORE_SET_CONTENT); }); editor.on('SaveContent', function (evt) { - varienGlobalEvents.fireEvent('tinymceSaveContent', evt); - eventBus.fireEvent('tinymceSaveContent'); + varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.AFTER_SAVE, evt); + eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_SAVE); }); editor.on('paste', function (evt) { - varienGlobalEvents.fireEvent('tinymcePaste', evt); - eventBus.fireEvent('tinymcePaste'); + varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.AFTER_PASTE, evt); + eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_PASTE); }); editor.on('PostProcess', function (evt) { - varienGlobalEvents.fireEvent('tinymceSaveContent', evt); - eventBus.fireEvent('tinymceSaveContent'); + varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.AFTER_SAVE, evt); + eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_SAVE); }); editor.on('undo', function (evt) { - varienGlobalEvents.fireEvent('tinymceUndo', evt); - eventBus.fireEvent('tinymceUndo'); + varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.AFTER_UNDO, evt); + eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_UNDO); }); editor.on('focus', function () { - eventBus.fireEvent('tinymceFocus'); + eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_FOCUS); }); editor.on('blur', function () { - eventBus.fireEvent('tinymceBlur'); + eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_BLUR); }); /** * @param {*} evt */ onChange = function (evt) { - varienGlobalEvents.fireEvent('tinymceChange', evt); - eventBus.fireEvent('tinymceChange'); + varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.AFTER_CONTENT_CHANGE, evt); + eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_CONTENT_CHANGE); }; editor.on('Change', onChange); @@ -241,8 +256,8 @@ define([ }); editor.on('init', function (args) { - varienGlobalEvents.fireEvent('wysiwygEditorInitialized', args.target); - eventBus.fireEvent('wysiwygEditorInitialized'); + varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.AFTER_INITIALIZATION, args.target); + eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_INITIALIZATION); }); } }; @@ -263,12 +278,12 @@ define([ * @param {*} w */ settings['file_browser_callback'] = function (fieldName, url, objectType, w) { - varienGlobalEvents.fireEvent('open_browser_callback', { + varienGlobalEvents.fireEvent(this.EVENT.AFTER_OPEN_FILE_BROWSER, { win: w, type: objectType, field: fieldName }); - }; + }.bind(this); } if (this.config.width) { From 3152b804d1f4fb210c9ae407b9235bd338aa30af Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Wed, 1 Aug 2018 18:47:02 +0300 Subject: [PATCH 0668/1171] MAGETWO-93982: Category image on custom Store View level disappears and appears after every save --- .../Magento/Catalog/Model/Category/Attribute/Backend/Image.php | 2 +- .../Test/Unit/Model/Category/Attribute/Backend/ImageTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php index a2dff83173b37..cd450e26cd832 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php @@ -106,7 +106,7 @@ public function beforeSave($object) $object->setData($this->additionalData . $attributeName, $value); $object->setData($attributeName, $imageName); } elseif (!is_string($value)) { - $object->setData($attributeName, ''); + $object->setData($attributeName, null); } return parent::beforeSave($object); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php index 8d63530b91287..3fbe0c198a027 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php @@ -98,7 +98,7 @@ public function testBeforeSaveValueDeletion($value) $model->beforeSave($object); - $this->assertEquals('', $object->getTestAttribute()); + $this->assertEquals(null, $object->getTestAttribute()); } /** From 20f943396ac1dcc8ba0cd8a13a105d0e83ee2d0c Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Wed, 1 Aug 2018 10:56:30 -0500 Subject: [PATCH 0669/1171] MAGETWO-90719: Exception is thrown when you re-order a product with custom options from Admin. Unkip MFTF test --- .../Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml index 3a7feb34354d7..27e5eb16a419d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml @@ -17,7 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-61717"/> <!--Skip because of issue MAGETWO-90719--> - <group value="skip"/> + <group value="Catalog"/> </annotations> <before> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> From 041f72810509bc657dca2646cd4667108705f0be Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Wed, 1 Aug 2018 11:03:49 -0500 Subject: [PATCH 0670/1171] MAGETWO-90719: Exception is thrown when you re-order a product with custom options from Admin. Remove skip comments --- .../Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml index 27e5eb16a419d..6e43753220d7c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml @@ -16,7 +16,6 @@ <description value="Admin should be able to sell products with different variants of their own"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-61717"/> - <!--Skip because of issue MAGETWO-90719--> <group value="Catalog"/> </annotations> <before> From d82cfe77099d38da324810099127144971288f45 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Fri, 13 Jul 2018 15:49:15 -0500 Subject: [PATCH 0671/1171] MAGETWO-93153: Fix UI Upgrade builds - PHP version check is not visible for system upgrade readiness check --- .../Constraint/AssertSuccessfulReadinessCheck.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Setup/Test/Constraint/AssertSuccessfulReadinessCheck.php b/dev/tests/functional/tests/app/Magento/Setup/Test/Constraint/AssertSuccessfulReadinessCheck.php index 96fbc1905b5d0..676703924e59a 100644 --- a/dev/tests/functional/tests/app/Magento/Setup/Test/Constraint/AssertSuccessfulReadinessCheck.php +++ b/dev/tests/functional/tests/app/Magento/Setup/Test/Constraint/AssertSuccessfulReadinessCheck.php @@ -67,11 +67,13 @@ public function processAssert(SetupWizard $setupWizard) $setupWizard->getReadiness()->getDependencyCheck(), 'Dependency check is incorrect.' ); - \PHPUnit\Framework\Assert::assertContains( - self::PHP_VERSION_MESSAGE, - $setupWizard->getReadiness()->getPhpVersionCheck(), - 'PHP version is incorrect.' - ); + if ($setupWizard->getReadiness()->isPhpVersionCheckVisible()) { + \PHPUnit\Framework\Assert::assertContains( + self::PHP_VERSION_MESSAGE, + $setupWizard->getReadiness()->getPhpVersionCheck(), + 'PHP version is incorrect.' + ); + } \PHPUnit\Framework\Assert::assertContains( self::PHP_SETTING_REGEXP, $setupWizard->getReadiness()->getSettingsCheck(), From 1095af21db4dc4c6714d8446de340f08f7dd84a9 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Wed, 1 Aug 2018 11:52:14 -0500 Subject: [PATCH 0672/1171] MC-3309: Build stabilization Explicitly cast to string --- lib/web/mage/adminhtml/events.js | 5 + lib/web/tiny_mce_4/tinymce.min.js | 57095 +++++++++++++++++++++++++++- 2 files changed, 57085 insertions(+), 15 deletions(-) diff --git a/lib/web/mage/adminhtml/events.js b/lib/web/mage/adminhtml/events.js index 67e04c5ca9c15..2231e28217b5d 100644 --- a/lib/web/mage/adminhtml/events.js +++ b/lib/web/mage/adminhtml/events.js @@ -134,6 +134,11 @@ define([ results = [], result, len, i, eventArgs, method, eventHandler; + if (eventName.toLowerCase().indexOf('tinymce') !== -1) { + // console.log(eventName, tinyMCE.activeEditor ? tinyMCE.activeEditor.getBody().innerHTML : ''); + // console.trace(); + } + if (this.arrEvents[evtName] != null) { len = this.arrEvents[evtName].length; //optimization diff --git a/lib/web/tiny_mce_4/tinymce.min.js b/lib/web/tiny_mce_4/tinymce.min.js index d2d51d9d3ce38..8515aa6fdf132 100755 --- a/lib/web/tiny_mce_4/tinymce.min.js +++ b/lib/web/tiny_mce_4/tinymce.min.js @@ -1,16 +1,57081 @@ // 4.6.4 (2017-06-13) -!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;i<g;++i)h[i]=d(e[i]);var j=f.apply(null,h);if(void 0===j)throw"module ["+b+"] returned undefined";c.instance=j},c=function(b,c,d){if("string"!=typeof b)throw"module id must be a string";if(void 0===c)throw"no dependencies for "+b;if(void 0===d)throw"no definition function for "+b;a[b]={deps:c,defn:d,instance:void 0}},d=function(c){var d=a[c];if(void 0===d)throw"module ["+c+"] was undefined";return void 0===d.instance&&b(c),d.instance},e=function(a,b){for(var c=a.length,e=new Array(c),f=0;f<c;++f)e.push(d(a[f]));b.apply(null,b)},f={};f.bolt={module:{api:{define:c,require:e,demand:d}}};var g=c,h=function(a,b){g(a,[],function(){return b})};g("3",[],function(){"use strict";function a(a,b,c){var d,e,g,h,i,k;return d=b.x,e=b.y,g=a.w,h=a.h,i=b.w,k=b.h,c=(c||"").split(""),"b"===c[0]&&(e+=k),"r"===c[1]&&(d+=i),"c"===c[0]&&(e+=j(k/2)),"c"===c[1]&&(d+=j(i/2)),"b"===c[3]&&(e-=h),"r"===c[4]&&(d-=g),"c"===c[3]&&(e-=j(h/2)),"c"===c[4]&&(d-=j(g/2)),f(d,e,g,h)}function b(b,c,d,e){var f,g;for(g=0;g<e.length;g++)if(f=a(b,c,e[g]),f.x>=d.x&&f.x+f.w<=d.w+d.x&&f.y>=d.y&&f.y+f.h<=d.h+d.y)return e[g];return null}function c(a,b,c){return f(a.x-b,a.y-c,a.w+2*b,a.h+2*c)}function d(a,b){var c,d,e,g;return c=i(a.x,b.x),d=i(a.y,b.y),e=h(a.x+a.w,b.x+b.w),g=h(a.y+a.h,b.y+b.h),e-c<0||g-d<0?null:f(c,d,e-c,g-d)}function e(a,b,c){var d,e,g,h,j,k,l,m,n,o;return j=a.x,k=a.y,l=a.x+a.w,m=a.y+a.h,n=b.x+b.w,o=b.y+b.h,d=i(0,b.x-j),e=i(0,b.y-k),g=i(0,l-n),h=i(0,m-o),j+=d,k+=e,c&&(l+=d,m+=e,j-=g,k-=h),l-=g,m-=h,f(j,k,l-j,m-k)}function f(a,b,c,d){return{x:a,y:b,w:c,h:d}}function g(a){return f(a.left,a.top,a.width,a.height)}var h=Math.min,i=Math.max,j=Math.round;return{inflate:c,relativePosition:a,findBestRelativePosition:b,intersect:d,clamp:e,create:f,fromClientRect:g}}),g("4",[],function(){function a(a,b){return function(){a.apply(b,arguments)}}function b(b){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof b)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],h(b,a(d,this),a(e,this))}function c(a){var b=this;return null===this._state?void this._deferreds.push(a):void i(function(){var c=b._state?a.onFulfilled:a.onRejected;if(null===c)return void(b._state?a.resolve:a.reject)(b._value);var d;try{d=c(b._value)}catch(b){return void a.reject(b)}a.resolve(d)})}function d(b){try{if(b===this)throw new TypeError("A promise cannot be resolved with itself.");if(b&&("object"==typeof b||"function"==typeof b)){var c=b.then;if("function"==typeof c)return void h(a(c,b),a(d,this),a(e,this))}this._state=!0,this._value=b,f.call(this)}catch(a){e.call(this,a)}}function e(a){this._state=!1,this._value=a,f.call(this)}function f(){for(var a=0,b=this._deferreds.length;a<b;a++)c.call(this,this._deferreds[a]);this._deferreds=null}function g(a,b,c,d){this.onFulfilled="function"==typeof a?a:null,this.onRejected="function"==typeof b?b:null,this.resolve=c,this.reject=d}function h(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(a){if(d)return;d=!0,c(a)}}if(window.Promise)return window.Promise;var i=b.immediateFn||"function"==typeof setImmediate&&setImmediate||function(a){setTimeout(a,1)},j=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};return b.prototype["catch"]=function(a){return this.then(null,a)},b.prototype.then=function(a,d){var e=this;return new b(function(b,f){c.call(e,new g(a,d,b,f))})},b.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&j(arguments[0])?arguments[0]:arguments);return new b(function(b,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,a)},c)}a[f]=g,0===--e&&b(a)}catch(a){c(a)}}if(0===a.length)return b([]);for(var e=a.length,f=0;f<a.length;f++)d(f,a[f])})},b.resolve=function(a){return a&&"object"==typeof a&&a.constructor===b?a:new b(function(b){b(a)})},b.reject=function(a){return new b(function(b,c){c(a)})},b.race=function(a){return new b(function(b,c){for(var d=0,e=a.length;d<e;d++)a[d].then(b,c)})},b}),g("5",["4"],function(a){function b(a,b){function c(a){window.setTimeout(a,0)}var d,e=window.requestAnimationFrame,f=["ms","moz","webkit"];for(d=0;d<f.length&&!e;d++)e=window[f[d]+"RequestAnimationFrame"];e||(e=c),e(a,b)}function c(a,b){return"number"!=typeof b&&(b=0),setTimeout(a,b)}function d(a,b){return"number"!=typeof b&&(b=1),setInterval(a,b)}function e(a){return clearTimeout(a)}function f(a){return clearInterval(a)}function g(a,b){var d,e;return e=function(){var e=arguments;clearTimeout(d),d=c(function(){a.apply(this,e)},b)},e.stop=function(){clearTimeout(d)},e}var h;return{requestAnimationFrame:function(c,d){return h?void h.then(c):void(h=new a(function(a){d||(d=document.body),b(a,d)}).then(c))},setTimeout:c,setInterval:d,setEditorTimeout:function(a,b,d){return c(function(){a.removed||b()},d)},setEditorInterval:function(a,b,c){var e;return e=d(function(){a.removed?clearInterval(e):b()},c)},debounce:g,throttle:g,clearInterval:f,clearTimeout:e}}),g("6",[],function(){function a(a){return"matchMedia"in window&&matchMedia(a).matches}var b,c,d,e,f,g,h,i,j,k,l,m,n,o=navigator,p=o.userAgent;b=window.opera&&window.opera.buildNumber,j=/Android/.test(p),c=/WebKit/.test(p),d=!c&&!b&&/MSIE/gi.test(p)&&/Explorer/gi.test(o.appName),d=d&&/MSIE (\w+)\./.exec(p)[1],e=p.indexOf("Trident/")!=-1&&(p.indexOf("rv:")!=-1||o.appName.indexOf("Netscape")!=-1)&&11,f=p.indexOf("Edge/")!=-1&&!d&&!e&&12,d=d||e||f,g=!c&&!e&&/Gecko/.test(p),h=p.indexOf("Mac")!=-1,i=/(iPad|iPhone)/.test(p),k="FormData"in window&&"FileReader"in window&&"URL"in window&&!!URL.createObjectURL,l=a("only screen and (max-device-width: 480px)")&&(j||i),m=a("only screen and (min-width: 800px)")&&(j||i),n=p.indexOf("Windows Phone")!=-1,f&&(c=!1);var q=!i||k||p.match(/AppleWebKit\/(\d*)/)[1]>=534;return{opera:b,webkit:c,ie:d,gecko:g,mac:h,iOS:i,android:j,contentEditable:q,transparentSrc:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",caretAfter:8!=d,range:window.getSelection&&"Range"in window,documentMode:d&&!f?document.documentMode||7:10,fileApi:k,ceFalse:d===!1||d>8,canHaveCSP:d===!1||d>11,desktop:!l&&!m,windowsPhone:n}}),g("7",["5","6"],function(a,b){"use strict";function c(a,b,c,d){a.addEventListener?a.addEventListener(b,c,d||!1):a.attachEvent&&a.attachEvent("on"+b,c)}function d(a,b,c,d){a.removeEventListener?a.removeEventListener(b,c,d||!1):a.detachEvent&&a.detachEvent("on"+b,c)}function e(a,b){var c,d=b;return c=a.path,c&&c.length>0&&(d=c[0]),a.deepPath&&(c=a.deepPath(),c&&c.length>0&&(d=c[0])),d}function f(a,c){var d,f,g=c||{};for(d in a)k[d]||(g[d]=a[d]);if(g.target||(g.target=g.srcElement||document),b.experimentalShadowDom&&(g.target=e(a,g.target)),a&&j.test(a.type)&&a.pageX===f&&a.clientX!==f){var h=g.target.ownerDocument||document,i=h.documentElement,o=h.body;g.pageX=a.clientX+(i&&i.scrollLeft||o&&o.scrollLeft||0)-(i&&i.clientLeft||o&&o.clientLeft||0),g.pageY=a.clientY+(i&&i.scrollTop||o&&o.scrollTop||0)-(i&&i.clientTop||o&&o.clientTop||0)}return g.preventDefault=function(){g.isDefaultPrevented=n,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},g.stopPropagation=function(){g.isPropagationStopped=n,a&&(a.stopPropagation?a.stopPropagation():a.cancelBubble=!0)},g.stopImmediatePropagation=function(){g.isImmediatePropagationStopped=n,g.stopPropagation()},l(g)===!1&&(g.isDefaultPrevented=m,g.isPropagationStopped=m,g.isImmediatePropagationStopped=m),"undefined"==typeof g.metaKey&&(g.metaKey=!1),g}function g(e,f,g){function h(){return"complete"===l.readyState||"interactive"===l.readyState&&l.body}function i(){g.domLoaded||(g.domLoaded=!0,f(m))}function j(){h()&&(d(l,"readystatechange",j),i())}function k(){try{l.documentElement.doScroll("left")}catch(b){return void a.setTimeout(k)}i()}var l=e.document,m={type:"ready"};return g.domLoaded?void f(m):(!l.addEventListener||b.ie&&b.ie<11?(c(l,"readystatechange",j),l.documentElement.doScroll&&e.self===e.top&&k()):h()?i():c(e,"DOMContentLoaded",i),void c(e,"load",i))}function h(){function a(a,b){var c,d,e,f,g=m[b];if(c=g&&g[a.type])for(d=0,e=c.length;d<e;d++)if(f=c[d],f&&f.func.call(f.scope,a)===!1&&a.preventDefault(),a.isImmediatePropagationStopped())return}var b,e,h,j,k,l=this,m={};e=i+(+new Date).toString(32),j="onmouseenter"in document.documentElement,h="onfocusin"in document.documentElement,k={mouseenter:"mouseover",mouseleave:"mouseout"},b=1,l.domLoaded=!1,l.events=m,l.bind=function(d,i,n,o){function p(b){a(f(b||x.event),q)}var q,r,s,t,u,v,w,x=window;if(d&&3!==d.nodeType&&8!==d.nodeType){for(d[e]?q=d[e]:(q=b++,d[e]=q,m[q]={}),o=o||d,i=i.split(" "),s=i.length;s--;)t=i[s],v=p,u=w=!1,"DOMContentLoaded"===t&&(t="ready"),l.domLoaded&&"ready"===t&&"complete"==d.readyState?n.call(o,f({type:t})):(j||(u=k[t],u&&(v=function(b){var c,d;if(c=b.currentTarget,d=b.relatedTarget,d&&c.contains)d=c.contains(d);else for(;d&&d!==c;)d=d.parentNode;d||(b=f(b||x.event),b.type="mouseout"===b.type?"mouseleave":"mouseenter",b.target=c,a(b,q))})),h||"focusin"!==t&&"focusout"!==t||(w=!0,u="focusin"===t?"focus":"blur",v=function(b){b=f(b||x.event),b.type="focus"===b.type?"focusin":"focusout",a(b,q)}),r=m[q][t],r?"ready"===t&&l.domLoaded?n({type:t}):r.push({func:n,scope:o}):(m[q][t]=r=[{func:n,scope:o}],r.fakeName=u,r.capture=w,r.nativeHandler=v,"ready"===t?g(d,v,l):c(d,u||t,v,w)));return d=r=0,n}},l.unbind=function(a,b,c){var f,g,h,i,j,k;if(!a||3===a.nodeType||8===a.nodeType)return l;if(f=a[e]){if(k=m[f],b){for(b=b.split(" "),h=b.length;h--;)if(j=b[h],g=k[j]){if(c)for(i=g.length;i--;)if(g[i].func===c){var n=g.nativeHandler,o=g.fakeName,p=g.capture;g=g.slice(0,i).concat(g.slice(i+1)),g.nativeHandler=n,g.fakeName=o,g.capture=p,k[j]=g}c&&0!==g.length||(delete k[j],d(a,g.fakeName||j,g.nativeHandler,g.capture))}}else{for(j in k)g=k[j],d(a,g.fakeName||j,g.nativeHandler,g.capture);k={}}for(j in k)return l;delete m[f];try{delete a[e]}catch(b){a[e]=null}}return l},l.fire=function(b,c,d){var g;if(!b||3===b.nodeType||8===b.nodeType)return l;d=f(null,d),d.type=c,d.target=b;do g=b[e],g&&a(d,g),b=b.parentNode||b.ownerDocument||b.defaultView||b.parentWindow;while(b&&!d.isPropagationStopped());return l},l.clean=function(a){var b,c,d=l.unbind;if(!a||3===a.nodeType||8===a.nodeType)return l;if(a[e]&&d(a),a.getElementsByTagName||(a=a.document),a&&a.getElementsByTagName)for(d(a),c=a.getElementsByTagName("*"),b=c.length;b--;)a=c[b],a[e]&&d(a);return l},l.destroy=function(){m={}},l.cancel=function(a){return a&&(a.preventDefault(),a.stopImmediatePropagation()),!1}}var i="mce-data-",j=/^(?:mouse|contextmenu)|click/,k={keyLocation:1,layerX:1,layerY:1,returnValue:1,webkitMovementX:1,webkitMovementY:1,keyIdentifier:1},l=function(a){return a.isDefaultPrevented===n||a.isDefaultPrevented===m},m=function(){return!1},n=function(){return!0};return h.Event=new h,h.Event.bind(window,"ready",function(){}),h}),g("8",[],function(){function a(a,b,c,d){var e,f,g,h,i,k,m,n,o,p;if((b?b.ownerDocument||b:N)!==F&&E(b),b=b||F,c=c||[],!a||"string"!=typeof a)return c;if(1!==(h=b.nodeType)&&9!==h)return[];if(H&&!d){if(e=qa.exec(a))if(g=e[1]){if(9===h){if(f=b.getElementById(g),!f||!f.parentNode)return c;if(f.id===g)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(g))&&L(b,f)&&f.id===g)return c.push(f),c}else{if(e[2])return $.apply(c,b.getElementsByTagName(a)),c;if((g=e[3])&&u.getElementsByClassName)return $.apply(c,b.getElementsByClassName(g)),c}if(u.qsa&&(!I||!I.test(a))){if(n=m=M,o=b,p=9===h&&a,1===h&&"object"!==b.nodeName.toLowerCase()){for(k=y(a),(m=b.getAttribute("id"))?n=m.replace(sa,"\\$&"):b.setAttribute("id",n),n="[id='"+n+"'] ",i=k.length;i--;)k[i]=n+l(k[i]);o=ra.test(a)&&j(b.parentNode)||b,p=k.join(",")}if(p)try{return $.apply(c,o.querySelectorAll(p)),c}catch(a){}finally{m||b.removeAttribute("id")}}}return A(a.replace(ga,"$1"),b,c,d)}function b(){function a(c,d){return b.push(c+" ")>v.cacheLength&&delete a[b.shift()],a[c+" "]=d}var b=[];return a}function c(a){return a[M]=!0,a}function d(a){var b=F.createElement("div");try{return!!a(b)}catch(a){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function e(a,b){for(var c=a.split("|"),d=a.length;d--;)v.attrHandle[c[d]]=b}function f(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||V)-(~a.sourceIndex||V);if(d)return d;if(c)for(;c=c.nextSibling;)if(c===b)return-1;return a?1:-1}function g(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function h(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function i(a){return c(function(b){return b=+b,c(function(c,d){for(var e,f=a([],c.length,b),g=f.length;g--;)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function j(a){return a&&typeof a.getElementsByTagName!==U&&a}function k(){}function l(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function m(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=P++;return b.first?function(b,c,f){for(;b=b[d];)if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[O,f];if(g){for(;b=b[d];)if((1===b.nodeType||e)&&a(b,c,g))return!0}else for(;b=b[d];)if(1===b.nodeType||e){if(i=b[M]||(b[M]={}),(h=i[d])&&h[0]===O&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function n(a){return a.length>1?function(b,c,d){for(var e=a.length;e--;)if(!a[e](b,c,d))return!1;return!0}:a[0]}function o(b,c,d){for(var e=0,f=c.length;e<f;e++)a(b,c[e],d);return d}function p(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function q(a,b,d,e,f,g){return e&&!e[M]&&(e=q(e)),f&&!f[M]&&(f=q(f,g)),c(function(c,g,h,i){var j,k,l,m=[],n=[],q=g.length,r=c||o(b||"*",h.nodeType?[h]:h,[]),s=!a||!c&&b?r:p(r,m,a,h,i),t=d?f||(c?a:q||e)?[]:g:s;if(d&&d(s,t,h,i),e)for(j=p(t,n),e(j,[],h,i),k=j.length;k--;)(l=j[k])&&(t[n[k]]=!(s[n[k]]=l));if(c){if(f||a){if(f){for(j=[],k=t.length;k--;)(l=t[k])&&j.push(s[k]=l);f(null,t=[],j,i)}for(k=t.length;k--;)(l=t[k])&&(j=f?aa.call(c,l):m[k])>-1&&(c[j]=!(g[j]=l))}}else t=p(t===g?t.splice(q,t.length):t),f?f(null,g,t,i):$.apply(g,t)})}function r(a){for(var b,c,d,e=a.length,f=v.relative[a[0].type],g=f||v.relative[" "],h=f?1:0,i=m(function(a){return a===b},g,!0),j=m(function(a){return aa.call(b,a)>-1},g,!0),k=[function(a,c,d){return!f&&(d||c!==B)||((b=c).nodeType?i(a,c,d):j(a,c,d))}];h<e;h++)if(c=v.relative[a[h].type])k=[m(n(k),c)];else{if(c=v.filter[a[h].type].apply(null,a[h].matches),c[M]){for(d=++h;d<e&&!v.relative[a[d].type];d++);return q(h>1&&n(k),h>1&&l(a.slice(0,h-1).concat({value:" "===a[h-2].type?"*":""})).replace(ga,"$1"),c,h<d&&r(a.slice(h,d)),d<e&&r(a=a.slice(d)),d<e&&l(a))}k.push(c)}return n(k)}function s(b,d){var e=d.length>0,f=b.length>0,g=function(c,g,h,i,j){var k,l,m,n=0,o="0",q=c&&[],r=[],s=B,t=c||f&&v.find.TAG("*",j),u=O+=null==s?1:Math.random()||.1,w=t.length;for(j&&(B=g!==F&&g);o!==w&&null!=(k=t[o]);o++){if(f&&k){for(l=0;m=b[l++];)if(m(k,g,h)){i.push(k);break}j&&(O=u)}e&&((k=!m&&k)&&n--,c&&q.push(k))}if(n+=o,e&&o!==n){for(l=0;m=d[l++];)m(q,r,g,h);if(c){if(n>0)for(;o--;)q[o]||r[o]||(r[o]=Y.call(i));r=p(r)}$.apply(i,r),j&&!c&&r.length>0&&n+d.length>1&&a.uniqueSort(i)}return j&&(O=u,B=s),q};return e?c(g):g}var t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M="sizzle"+-new Date,N=window.document,O=0,P=0,Q=b(),R=b(),S=b(),T=function(a,b){return a===b&&(D=!0),0},U="undefined",V=1<<31,W={}.hasOwnProperty,X=[],Y=X.pop,Z=X.push,$=X.push,_=X.slice,aa=X.indexOf||function(a){for(var b=0,c=this.length;b<c;b++)if(this[b]===a)return b;return-1},ba="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",ca="[\\x20\\t\\r\\n\\f]",da="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",ea="\\["+ca+"*("+da+")(?:"+ca+"*([*^$|!~]?=)"+ca+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+da+"))|)"+ca+"*\\]",fa=":("+da+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+ea+")*)|.*)\\)|)",ga=new RegExp("^"+ca+"+|((?:^|[^\\\\])(?:\\\\.)*)"+ca+"+$","g"),ha=new RegExp("^"+ca+"*,"+ca+"*"),ia=new RegExp("^"+ca+"*([>+~]|"+ca+")"+ca+"*"),ja=new RegExp("="+ca+"*([^\\]'\"]*?)"+ca+"*\\]","g"),ka=new RegExp(fa),la=new RegExp("^"+da+"$"),ma={ID:new RegExp("^#("+da+")"),CLASS:new RegExp("^\\.("+da+")"),TAG:new RegExp("^("+da+"|[*])"),ATTR:new RegExp("^"+ea),PSEUDO:new RegExp("^"+fa),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ca+"*(even|odd|(([+-]|)(\\d*)n|)"+ca+"*(?:([+-]|)"+ca+"*(\\d+)|))"+ca+"*\\)|)","i"),bool:new RegExp("^(?:"+ba+")$","i"),needsContext:new RegExp("^"+ca+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ca+"*((?:-\\d)?\\d*)"+ca+"*\\)|)(?=[^-]|$)","i")},na=/^(?:input|select|textarea|button)$/i,oa=/^h\d$/i,pa=/^[^{]+\{\s*\[native \w/,qa=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ra=/[+~]/,sa=/'|\\/g,ta=new RegExp("\\\\([\\da-f]{1,6}"+ca+"?|("+ca+")|.)","ig"),ua=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{$.apply(X=_.call(N.childNodes),N.childNodes),X[N.childNodes.length].nodeType}catch(a){$={apply:X.length?function(a,b){Z.apply(a,_.call(b))}:function(a,b){for(var c=a.length,d=0;a[c++]=b[d++];);a.length=c-1}}}u=a.support={},x=a.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},E=a.setDocument=function(a){function b(a){try{return a.top}catch(a){}return null}var c,e=a?a.ownerDocument||a:N,g=e.defaultView;return e!==F&&9===e.nodeType&&e.documentElement?(F=e,G=e.documentElement,H=!x(e),g&&g!==b(g)&&(g.addEventListener?g.addEventListener("unload",function(){E()},!1):g.attachEvent&&g.attachEvent("onunload",function(){E()})),u.attributes=d(function(a){return a.className="i",!a.getAttribute("className")}),u.getElementsByTagName=d(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),u.getElementsByClassName=pa.test(e.getElementsByClassName),u.getById=d(function(a){return G.appendChild(a).id=M,!e.getElementsByName||!e.getElementsByName(M).length}),u.getById?(v.find.ID=function(a,b){if(typeof b.getElementById!==U&&H){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},v.filter.ID=function(a){var b=a.replace(ta,ua);return function(a){return a.getAttribute("id")===b}}):(delete v.find.ID,v.filter.ID=function(a){var b=a.replace(ta,ua);return function(a){var c=typeof a.getAttributeNode!==U&&a.getAttributeNode("id");return c&&c.value===b}}),v.find.TAG=u.getElementsByTagName?function(a,b){if(typeof b.getElementsByTagName!==U)return b.getElementsByTagName(a)}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){for(;c=f[e++];)1===c.nodeType&&d.push(c);return d}return f},v.find.CLASS=u.getElementsByClassName&&function(a,b){if(H)return b.getElementsByClassName(a)},J=[],I=[],(u.qsa=pa.test(e.querySelectorAll))&&(d(function(a){a.innerHTML="<select msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&I.push("[*^$]="+ca+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||I.push("\\["+ca+"*(?:value|"+ba+")"),a.querySelectorAll(":checked").length||I.push(":checked")}),d(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&I.push("name"+ca+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||I.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),I.push(",.*:")})),(u.matchesSelector=pa.test(K=G.matches||G.webkitMatchesSelector||G.mozMatchesSelector||G.oMatchesSelector||G.msMatchesSelector))&&d(function(a){u.disconnectedMatch=K.call(a,"div"),K.call(a,"[s!='']:x"),J.push("!=",fa)}),I=I.length&&new RegExp(I.join("|")),J=J.length&&new RegExp(J.join("|")),c=pa.test(G.compareDocumentPosition),L=c||pa.test(G.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)for(;b=b.parentNode;)if(b===a)return!0;return!1},T=c?function(a,b){if(a===b)return D=!0,0;var c=!a.compareDocumentPosition-!b.compareDocumentPosition;return c?c:(c=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&c||!u.sortDetached&&b.compareDocumentPosition(a)===c?a===e||a.ownerDocument===N&&L(N,a)?-1:b===e||b.ownerDocument===N&&L(N,b)?1:C?aa.call(C,a)-aa.call(C,b):0:4&c?-1:1)}:function(a,b){if(a===b)return D=!0,0;var c,d=0,g=a.parentNode,h=b.parentNode,i=[a],j=[b];if(!g||!h)return a===e?-1:b===e?1:g?-1:h?1:C?aa.call(C,a)-aa.call(C,b):0;if(g===h)return f(a,b);for(c=a;c=c.parentNode;)i.unshift(c);for(c=b;c=c.parentNode;)j.unshift(c);for(;i[d]===j[d];)d++;return d?f(i[d],j[d]):i[d]===N?-1:j[d]===N?1:0},e):F},a.matches=function(b,c){return a(b,null,null,c)},a.matchesSelector=function(b,c){if((b.ownerDocument||b)!==F&&E(b),c=c.replace(ja,"='$1']"),u.matchesSelector&&H&&(!J||!J.test(c))&&(!I||!I.test(c)))try{var d=K.call(b,c);if(d||u.disconnectedMatch||b.document&&11!==b.document.nodeType)return d}catch(a){}return a(c,F,null,[b]).length>0},a.contains=function(a,b){return(a.ownerDocument||a)!==F&&E(a),L(a,b)},a.attr=function(a,b){(a.ownerDocument||a)!==F&&E(a);var c=v.attrHandle[b.toLowerCase()],d=c&&W.call(v.attrHandle,b.toLowerCase())?c(a,b,!H):void 0;return void 0!==d?d:u.attributes||!H?a.getAttribute(b):(d=a.getAttributeNode(b))&&d.specified?d.value:null},a.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},a.uniqueSort=function(a){var b,c=[],d=0,e=0;if(D=!u.detectDuplicates,C=!u.sortStable&&a.slice(0),a.sort(T),D){for(;b=a[e++];)b===a[e]&&(d=c.push(e));for(;d--;)a.splice(c[d],1)}return C=null,a},w=a.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(1===e||9===e||11===e){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=w(a)}else if(3===e||4===e)return a.nodeValue}else for(;b=a[d++];)c+=w(b);return c},v=a.selectors={cacheLength:50,createPseudo:c,match:ma,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ta,ua),a[3]=(a[3]||a[4]||a[5]||"").replace(ta,ua),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(b){return b[1]=b[1].toLowerCase(),"nth"===b[1].slice(0,3)?(b[3]||a.error(b[0]),b[4]=+(b[4]?b[5]+(b[6]||1):2*("even"===b[3]||"odd"===b[3])),b[5]=+(b[7]+b[8]||"odd"===b[3])):b[3]&&a.error(b[0]),b},PSEUDO:function(a){var b,c=!a[6]&&a[2];return ma.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&ka.test(c)&&(b=y(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ta,ua).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=Q[a+" "];return b||(b=new RegExp("(^|"+ca+")"+a+"("+ca+"|$)"))&&Q(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==U&&a.getAttribute("class")||"")})},ATTR:function(b,c,d){return function(e){var f=a.attr(e,b);return null==f?"!="===c:!c||(f+="","="===c?f===d:"!="===c?f!==d:"^="===c?d&&0===f.indexOf(d):"*="===c?d&&f.indexOf(d)>-1:"$="===c?d&&f.slice(-d.length)===d:"~="===c?(" "+f+" ").indexOf(d)>-1:"|="===c&&(f===d||f.slice(0,d.length+1)===d+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){for(;p;){for(l=b;l=l[p];)if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){for(k=q[M]||(q[M]={}),j=k[a]||[],n=j[0]===O&&j[1],m=j[0]===O&&j[2],l=n&&q.childNodes[n];l=++n&&l&&l[p]||(m=n=0)||o.pop();)if(1===l.nodeType&&++m&&l===b){k[a]=[O,n,m];break}}else if(s&&(j=(b[M]||(b[M]={}))[a])&&j[0]===O)m=j[1];else for(;(l=++n&&l&&l[p]||(m=n=0)||o.pop())&&((h?l.nodeName.toLowerCase()!==r:1!==l.nodeType)||!++m||(s&&((l[M]||(l[M]={}))[a]=[O,m]),l!==b)););return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(b,d){var e,f=v.pseudos[b]||v.setFilters[b.toLowerCase()]||a.error("unsupported pseudo: "+b);return f[M]?f(d):f.length>1?(e=[b,b,"",d],v.setFilters.hasOwnProperty(b.toLowerCase())?c(function(a,b){for(var c,e=f(a,d),g=e.length;g--;)c=aa.call(a,e[g]),a[c]=!(b[c]=e[g])}):function(a){return f(a,0,e)}):f}},pseudos:{not:c(function(a){var b=[],d=[],e=z(a.replace(ga,"$1"));return e[M]?c(function(a,b,c,d){for(var f,g=e(a,null,d,[]),h=a.length;h--;)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,c,f){return b[0]=a,e(b,null,f,d),!d.pop()}}),has:c(function(b){return function(c){return a(b,c).length>0}}),contains:c(function(a){return a=a.replace(ta,ua),function(b){return(b.textContent||b.innerText||w(b)).indexOf(a)>-1}}),lang:c(function(b){return la.test(b||"")||a.error("unsupported lang: "+b),b=b.replace(ta,ua).toLowerCase(),function(a){var c;do if(c=H?a.lang:a.getAttribute("xml:lang")||a.getAttribute("lang"))return c=c.toLowerCase(),c===b||0===c.indexOf(b+"-");while((a=a.parentNode)&&1===a.nodeType);return!1}}),target:function(a){var b=window.location&&window.location.hash;return b&&b.slice(1)===a.id},root:function(a){return a===G},focus:function(a){return a===F.activeElement&&(!F.hasFocus||F.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!v.pseudos.empty(a)},header:function(a){return oa.test(a.nodeName)},input:function(a){return na.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:i(function(){return[0]}),last:i(function(a,b){return[b-1]}),eq:i(function(a,b,c){return[c<0?c+b:c]}),even:i(function(a,b){for(var c=0;c<b;c+=2)a.push(c);return a}),odd:i(function(a,b){for(var c=1;c<b;c+=2)a.push(c);return a}),lt:i(function(a,b,c){for(var d=c<0?c+b:c;--d>=0;)a.push(d);return a}),gt:i(function(a,b,c){for(var d=c<0?c+b:c;++d<b;)a.push(d);return a})}},v.pseudos.nth=v.pseudos.eq;for(t in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})v.pseudos[t]=g(t);for(t in{submit:!0,reset:!0})v.pseudos[t]=h(t);return k.prototype=v.filters=v.pseudos,v.setFilters=new k,y=a.tokenize=function(b,c){var d,e,f,g,h,i,j,k=R[b+" "];if(k)return c?0:k.slice(0);for(h=b,i=[],j=v.preFilter;h;){d&&!(e=ha.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),d=!1,(e=ia.exec(h))&&(d=e.shift(),f.push({value:d,type:e[0].replace(ga," ")}),h=h.slice(d.length));for(g in v.filter)!(e=ma[g].exec(h))||j[g]&&!(e=j[g](e))||(d=e.shift(),f.push({value:d,type:g,matches:e}),h=h.slice(d.length));if(!d)break}return c?h.length:h?a.error(b):R(b,i).slice(0)},z=a.compile=function(a,b){var c,d=[],e=[],f=S[a+" "];if(!f){for(b||(b=y(a)),c=b.length;c--;)f=r(b[c]),f[M]?d.push(f):e.push(f);f=S(a,s(e,d)),f.selector=a}return f},A=a.select=function(a,b,c,d){var e,f,g,h,i,k="function"==typeof a&&a,m=!d&&y(a=k.selector||a);if(c=c||[],1===m.length){if(f=m[0]=m[0].slice(0),f.length>2&&"ID"===(g=f[0]).type&&u.getById&&9===b.nodeType&&H&&v.relative[f[1].type]){if(b=(v.find.ID(g.matches[0].replace(ta,ua),b)||[])[0],!b)return c;k&&(b=b.parentNode),a=a.slice(f.shift().value.length)}for(e=ma.needsContext.test(a)?0:f.length;e--&&(g=f[e],!v.relative[h=g.type]);)if((i=v.find[h])&&(d=i(g.matches[0].replace(ta,ua),ra.test(f[0].type)&&j(b.parentNode)||b))){if(f.splice(e,1),a=d.length&&l(f),!a)return $.apply(c,d),c;break}}return(k||z(a,m))(d,b,!H,c,ra.test(a)&&j(b.parentNode)||b),c},u.sortStable=M.split("").sort(T).join("")===M,u.detectDuplicates=!!D,E(),u.sortDetached=d(function(a){return 1&a.compareDocumentPosition(F.createElement("div"))}),d(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||e("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),u.attributes&&d(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||e("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),d(function(a){return null==a.getAttribute("disabled")})||e(ba,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),a}),g("1g",[],function(){function a(a){var b,c,d=a;if(!j(a))for(d=[],b=0,c=a.length;b<c;b++)d[b]=a[b];return d}function b(a,b,c){var d,e;if(!a)return 0;if(c=c||a,void 0!==a.length){for(d=0,e=a.length;d<e;d++)if(b.call(c,a[d],d,a)===!1)return 0}else for(d in a)if(a.hasOwnProperty(d)&&b.call(c,a[d],d,a)===!1)return 0;return 1}function c(a,c){var d=[];return b(a,function(b,e){d.push(c(b,e,a))}),d}function d(a,c){var d=[];return b(a,function(b,e){c&&!c(b,e,a)||d.push(b)}),d}function e(a,b){var c,d;if(a)for(c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1}function f(a,b,c,d){var e=0;for(arguments.length<3&&(c=a[0]);e<a.length;e++)c=b.call(d,c,a[e],e);return c}function g(a,b,c){var d,e;for(d=0,e=a.length;d<e;d++)if(b.call(c,a[d],d,a))return d;return-1}function h(a,b,c){var d=g(a,b,c);if(d!==-1)return a[d]}function i(a){return a[a.length-1]}var j=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};return{isArray:j,toArray:a,each:b,map:c,filter:d,indexOf:e,reduce:f,findIndex:g,find:h,last:i}}),g("9",["6","1g"],function(a,b){function c(a){return null===a||void 0===a?"":(""+a).replace(n,"")}function d(a,c){return c?!("array"!=c||!b.isArray(a))||typeof a==c:void 0!==a}function e(a,b,c){var d;for(a=a||[],b=b||",","string"==typeof a&&(a=a.split(b)),c=c||{},d=a.length;d--;)c[a[d]]={};return c}function f(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function g(a,b,c){var d,e,f,g,h,i=this,j=0;if(a=/^((static) )?([\w.]+)(:([\w.]+))?/.exec(a),f=a[3].match(/(^|\.)(\w+)$/i)[2],e=i.createNS(a[3].replace(/\.\w+$/,""),c),!e[f]){if("static"==a[2])return e[f]=b,void(this.onCreate&&this.onCreate(a[2],a[3],e[f]));b[f]||(b[f]=function(){},j=1),e[f]=b[f],i.extend(e[f].prototype,b),a[5]&&(d=i.resolve(a[5]).prototype,g=a[5].match(/\.(\w+)$/i)[1],h=e[f],j?e[f]=function(){return d[g].apply(this,arguments)}:e[f]=function(){return this.parent=d[g],h.apply(this,arguments)},e[f].prototype[f]=e[f],i.each(d,function(a,b){e[f].prototype[b]=d[b]}),i.each(b,function(a,b){d[b]?e[f].prototype[b]=function(){return this.parent=d[b],a.apply(this,arguments)}:b!=f&&(e[f].prototype[b]=a)})),i.each(b["static"],function(a,b){e[f][b]=a})}}function h(a,b){var c,d,e,f,g=arguments;for(c=1,d=g.length;c<d;c++){b=g[c];for(e in b)b.hasOwnProperty(e)&&(f=b[e],void 0!==f&&(a[e]=f))}return a}function i(a,c,d,e){e=e||this,a&&(d&&(a=a[d]),b.each(a,function(a,b){return c.call(e,a,b,d)!==!1&&void i(a,c,d,e)}))}function j(a,b){var c,d;for(b=b||window,a=a.split("."),c=0;c<a.length;c++)d=a[c],b[d]||(b[d]={}),b=b[d];return b}function k(a,b){var c,d;for(b=b||window,a=a.split("."),c=0,d=a.length;c<d&&(b=b[a[c]],b);c++);return b}function l(a,e){return!a||d(a,"array")?a:b.map(a.split(e||","),c)}function m(b){var c=a.cacheSuffix;return c&&(b+=(b.indexOf("?")===-1?"?":"&")+c),b}var n=/^\s*|\s*$/g;return{trim:c,isArray:b.isArray,is:d,toArray:b.toArray,makeMap:e,each:b.each,map:b.map, -grep:b.filter,inArray:b.indexOf,hasOwn:f,extend:h,create:g,walk:i,createNS:j,resolve:k,explode:l,_addCacheSuffix:m}}),g("a",["7","8","9","6"],function(a,b,c,d){function e(a){return"undefined"!=typeof a}function f(a){return"string"==typeof a}function g(a){return a&&a==a.window}function h(a,b){var c,d,e;for(b=b||w,e=b.createElement("div"),c=b.createDocumentFragment(),e.innerHTML=a;d=e.firstChild;)c.appendChild(d);return c}function i(a,b,c,d){var e;if(f(b))b=h(b,q(a[0]));else if(b.length&&!b.nodeType){if(b=l.makeArray(b),d)for(e=b.length-1;e>=0;e--)i(a,b[e],c,d);else for(e=0;e<b.length;e++)i(a,b[e],c,d);return a}if(b.nodeType)for(e=a.length;e--;)c.call(a[e],b);return a}function j(a,b){return a&&b&&(" "+a.className+" ").indexOf(" "+b+" ")!==-1}function k(a,b,c){var d,e;return b=l(b)[0],a.each(function(){var a=this;c&&d==a.parentNode?e.appendChild(a):(d=a.parentNode,e=b.cloneNode(!1),a.parentNode.insertBefore(e,a),e.appendChild(a))}),a}function l(a,b){return new l.fn.init(a,b)}function m(a,b){var c;if(b.indexOf)return b.indexOf(a);for(c=b.length;c--;)if(b[c]===a)return c;return-1}function n(a){return null===a||a===v?"":(""+a).replace(I,"")}function o(a,b){var c,d,e,f,g;if(a)if(c=a.length,c===f){for(d in a)if(a.hasOwnProperty(d)&&(g=a[d],b.call(g,d,g)===!1))break}else for(e=0;e<c&&(g=a[e],b.call(g,e,g)!==!1);e++);return a}function p(a,b){var c=[];return o(a,function(a,d){b(d,a)&&c.push(d)}),c}function q(a){return a?9==a.nodeType?a:a.ownerDocument:w}function r(a,b,c){var d=[],e=a[b];for("string"!=typeof c&&c instanceof l&&(c=c[0]);e&&9!==e.nodeType;){if(void 0!==c){if(e===c)break;if("string"==typeof c&&l(e).is(c))break}1===e.nodeType&&d.push(e),e=e[b]}return d}function s(a,b,c,d){var e=[];for(d instanceof l&&(d=d[0]);a;a=a[b])if(!c||a.nodeType===c){if(void 0!==d){if(a===d)break;if("string"==typeof d&&l(a).is(d))break}e.push(a)}return e}function t(a,b,c){for(a=a[b];a;a=a[b])if(a.nodeType==c)return a;return null}function u(a,b,c){o(c,function(c,d){a[c]=a[c]||{},a[c][b]=d})}var v,w=document,x=Array.prototype.push,y=Array.prototype.slice,z=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,A=a.Event,B=c.makeMap("children,contents,next,prev"),C=c.makeMap("fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom"," "),D=c.makeMap("checked compact declare defer disabled ismap multiple nohref noshade nowrap readonly selected"," "),E={"for":"htmlFor","class":"className",readonly:"readOnly"},F={"float":"cssFloat"},G={},H={},I=/^\s*|\s*$/g;return l.fn=l.prototype={constructor:l,selector:"",context:null,length:0,init:function(a,b){var c,d,e=this;if(!a)return e;if(a.nodeType)return e.context=e[0]=a,e.length=1,e;if(b&&b.nodeType)e.context=b;else{if(b)return l(a).attr(b);e.context=b=document}if(f(a)){if(e.selector=a,c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c)return l(b).find(a);if(c[1])for(d=h(a,q(b)).firstChild;d;)x.call(e,d),d=d.nextSibling;else{if(d=q(b).getElementById(c[2]),!d)return e;if(d.id!==c[2])return e.find(a);e.length=1,e[0]=d}}else this.add(a,!1);return e},toArray:function(){return c.toArray(this)},add:function(a,b){var c,d,e=this;if(f(a))return e.add(l(a));if(b!==!1)for(c=l.unique(e.toArray().concat(l.makeArray(a))),e.length=c.length,d=0;d<c.length;d++)e[d]=c[d];else x.apply(e,l.makeArray(a));return e},attr:function(a,b){var c,d=this;if("object"==typeof a)o(a,function(a,b){d.attr(a,b)});else{if(!e(b)){if(d[0]&&1===d[0].nodeType){if(c=G[a],c&&c.get)return c.get(d[0],a);if(D[a])return d.prop(a)?a:v;b=d[0].getAttribute(a,2),null===b&&(b=v)}return b}this.each(function(){var c;if(1===this.nodeType){if(c=G[a],c&&c.set)return void c.set(this,b);null===b?this.removeAttribute(a,2):this.setAttribute(a,b,2)}})}return d},removeAttr:function(a){return this.attr(a,null)},prop:function(a,b){var c=this;if(a=E[a]||a,"object"==typeof a)o(a,function(a,b){c.prop(a,b)});else{if(!e(b))return c[0]&&c[0].nodeType&&a in c[0]?c[0][a]:b;this.each(function(){1==this.nodeType&&(this[a]=b)})}return c},css:function(a,b){function c(a){return a.replace(/-(\D)/g,function(a,b){return b.toUpperCase()})}function d(a){return a.replace(/[A-Z]/g,function(a){return"-"+a})}var f,g,h=this;if("object"==typeof a)o(a,function(a,b){h.css(a,b)});else if(e(b))a=c(a),"number"!=typeof b||C[a]||(b+="px"),h.each(function(){var c=this.style;if(g=H[a],g&&g.set)return void g.set(this,b);try{this.style[F[a]||a]=b}catch(a){}null!==b&&""!==b||(c.removeProperty?c.removeProperty(d(a)):c.removeAttribute(a))});else{if(f=h[0],g=H[a],g&&g.get)return g.get(f);if(f.ownerDocument.defaultView)try{return f.ownerDocument.defaultView.getComputedStyle(f,null).getPropertyValue(d(a))}catch(a){return v}else if(f.currentStyle)return f.currentStyle[c(a)]}return h},remove:function(){for(var a,b=this,c=this.length;c--;)a=b[c],A.clean(a),a.parentNode&&a.parentNode.removeChild(a);return this},empty:function(){for(var a,b=this,c=this.length;c--;)for(a=b[c];a.firstChild;)a.removeChild(a.firstChild);return this},html:function(a){var b,c=this;if(e(a)){b=c.length;try{for(;b--;)c[b].innerHTML=a}catch(d){l(c[b]).empty().append(a)}return c}return c[0]?c[0].innerHTML:""},text:function(a){var b,c=this;if(e(a)){for(b=c.length;b--;)"innerText"in c[b]?c[b].innerText=a:c[0].textContent=a;return c}return c[0]?c[0].innerText||c[0].textContent:""},append:function(){return i(this,arguments,function(a){(1===this.nodeType||this.host&&1===this.host.nodeType)&&this.appendChild(a)})},prepend:function(){return i(this,arguments,function(a){(1===this.nodeType||this.host&&1===this.host.nodeType)&&this.insertBefore(a,this.firstChild)},!0)},before:function(){var a=this;return a[0]&&a[0].parentNode?i(a,arguments,function(a){this.parentNode.insertBefore(a,this)}):a},after:function(){var a=this;return a[0]&&a[0].parentNode?i(a,arguments,function(a){this.parentNode.insertBefore(a,this.nextSibling)},!0):a},appendTo:function(a){return l(a).append(this),this},prependTo:function(a){return l(a).prepend(this),this},replaceWith:function(a){return this.before(a).remove()},wrap:function(a){return k(this,a)},wrapAll:function(a){return k(this,a,!0)},wrapInner:function(a){return this.each(function(){l(this).contents().wrapAll(a)}),this},unwrap:function(){return this.parent().each(function(){l(this).replaceWith(this.childNodes)})},clone:function(){var a=[];return this.each(function(){a.push(this.cloneNode(!0))}),l(a)},addClass:function(a){return this.toggleClass(a,!0)},removeClass:function(a){return this.toggleClass(a,!1)},toggleClass:function(a,b){var c=this;return"string"!=typeof a?c:(a.indexOf(" ")!==-1?o(a.split(" "),function(){c.toggleClass(this,b)}):c.each(function(c,d){var e,f;f=j(d,a),f!==b&&(e=d.className,f?d.className=n((" "+e+" ").replace(" "+a+" "," ")):d.className+=e?" "+a:a)}),c)},hasClass:function(a){return j(this[0],a)},each:function(a){return o(this,a)},on:function(a,b){return this.each(function(){A.bind(this,a,b)})},off:function(a,b){return this.each(function(){A.unbind(this,a,b)})},trigger:function(a){return this.each(function(){"object"==typeof a?A.fire(this,a.type,a):A.fire(this,a)})},show:function(){return this.css("display","")},hide:function(){return this.css("display","none")},slice:function(){return new l(y.apply(this,arguments))},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},find:function(a){var b,c,d=[];for(b=0,c=this.length;b<c;b++)l.find(a,this[b],d);return l(d)},filter:function(a){return l("function"==typeof a?p(this.toArray(),function(b,c){return a(c,b)}):l.filter(a,this.toArray()))},closest:function(a){var b=[];return a instanceof l&&(a=a[0]),this.each(function(c,d){for(;d;){if("string"==typeof a&&l(d).is(a)){b.push(d);break}if(d==a){b.push(d);break}d=d.parentNode}}),l(b)},offset:function(a){var b,c,d,e,f=0,g=0;return a?this.css(a):(b=this[0],b&&(c=b.ownerDocument,d=c.documentElement,b.getBoundingClientRect&&(e=b.getBoundingClientRect(),f=e.left+(d.scrollLeft||c.body.scrollLeft)-d.clientLeft,g=e.top+(d.scrollTop||c.body.scrollTop)-d.clientTop)),{left:f,top:g})},push:x,sort:[].sort,splice:[].splice},c.extend(l,{extend:c.extend,makeArray:function(a){return g(a)||a.nodeType?[a]:c.toArray(a)},inArray:m,isArray:c.isArray,each:o,trim:n,grep:p,find:b,expr:b.selectors,unique:b.uniqueSort,text:b.getText,contains:b.contains,filter:function(a,b,c){var d=b.length;for(c&&(a=":not("+a+")");d--;)1!=b[d].nodeType&&b.splice(d,1);return b=1===b.length?l.find.matchesSelector(b[0],a)?[b[0]]:[]:l.find.matches(a,b)}}),o({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return r(a,"parentNode")},next:function(a){return t(a,"nextSibling",1)},prev:function(a){return t(a,"previousSibling",1)},children:function(a){return s(a.firstChild,"nextSibling",1)},contents:function(a){return c.toArray(("iframe"===a.nodeName?a.contentDocument||a.contentWindow.document:a).childNodes)}},function(a,b){l.fn[a]=function(c){var d=this,e=[];return d.each(function(){var a=b.call(e,this,c,e);a&&(l.isArray(a)?e.push.apply(e,a):e.push(a))}),this.length>1&&(B[a]||(e=l.unique(e)),0===a.indexOf("parents")&&(e=e.reverse())),e=l(e),c?e.filter(c):e}}),o({parentsUntil:function(a,b){return r(a,"parentNode",b)},nextUntil:function(a,b){return s(a,"nextSibling",1,b).slice(1)},prevUntil:function(a,b){return s(a,"previousSibling",1,b).slice(1)}},function(a,b){l.fn[a]=function(c,d){var e=this,f=[];return e.each(function(){var a=b.call(f,this,c,f);a&&(l.isArray(a)?f.push.apply(f,a):f.push(a))}),this.length>1&&(f=l.unique(f),0!==a.indexOf("parents")&&"prevUntil"!==a||(f=f.reverse())),f=l(f),d?f.filter(d):f}}),l.fn.is=function(a){return!!a&&this.filter(a).length>0},l.fn.init.prototype=l.fn,l.overrideDefaults=function(a){function b(d,e){return c=c||a(),0===arguments.length&&(d=c.element),e||(e=c.context),new b.fn.init(d,e)}var c;return l.extend(b,this),b},d.ie&&d.ie<8&&(u(G,"get",{maxlength:function(a){var b=a.maxLength;return 2147483647===b?v:b},size:function(a){var b=a.size;return 20===b?v:b},"class":function(a){return a.className},style:function(a){var b=a.style.cssText;return 0===b.length?v:b}}),u(G,"set",{"class":function(a,b){a.className=b},style:function(a,b){a.style.cssText=b}})),d.ie&&d.ie<9&&(F["float"]="styleFloat",u(H,"set",{opacity:function(a,b){var c=a.style;null===b||""===b?c.removeAttribute("filter"):(c.zoom=1,c.filter="alpha(opacity="+100*b+")")}})),l.attrHooks=G,l.cssHooks=H,l}),g("b",[],function(){return function(a,b){function c(a,b,c,d){function e(a){return a=parseInt(a,10).toString(16),a.length>1?a:"0"+a}return"#"+e(b)+e(c)+e(d)}var d,e,f,g,h=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,i=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,j=/\s*([^:]+):\s*([^;]+);?/g,k=/\s+$/,l={},m="\ufeff";for(a=a||{},b&&(f=b.getValidStyles(),g=b.getInvalidStyles()),e=("\\\" \\' \\; \\: ; : "+m).split(" "),d=0;d<e.length;d++)l[e[d]]=m+d,l[m+d]=e[d];return{toHex:function(a){return a.replace(h,c)},parse:function(b){function e(a,b,c){var e,f,g,h;if(e=w[a+"-top"+b],e&&(f=w[a+"-right"+b],f&&(g=w[a+"-bottom"+b],g&&(h=w[a+"-left"+b])))){var i=[e,f,g,h];for(d=i.length-1;d--&&i[d]===i[d+1];);d>-1&&c||(w[a+b]=d==-1?i[0]:i.join(" "),delete w[a+"-top"+b],delete w[a+"-right"+b],delete w[a+"-bottom"+b],delete w[a+"-left"+b])}}function f(a){var b,c=w[a];if(c){for(c=c.split(" "),b=c.length;b--;)if(c[b]!==c[0])return!1;return w[a]=c[0],!0}}function g(a,b,c,d){f(b)&&f(c)&&f(d)&&(w[a]=w[b]+" "+w[c]+" "+w[d],delete w[b],delete w[c],delete w[d])}function n(a){return v=!0,l[a]}function o(a,b){return v&&(a=a.replace(/\uFEFF[0-9]/g,function(a){return l[a]})),b||(a=a.replace(/\\([\'\";:])/g,"$1")),a}function p(a){return String.fromCharCode(parseInt(a.slice(1),16))}function q(a){return a.replace(/\\[0-9a-f]+/gi,p)}function r(b,c,d,e,f,g){if(f=f||g)return f=o(f),"'"+f.replace(/\'/g,"\\'")+"'";if(c=o(c||d||e),!a.allow_script_urls){var h=c.replace(/[\s\r\n]+/g,"");if(/(java|vb)script:/i.test(h))return"";if(!a.allow_svg_data_urls&&/^data:image\/svg/i.test(h))return""}return x&&(c=x.call(y,c,"style")),"url('"+c.replace(/\'/g,"\\'")+"')"}var s,t,u,v,w={},x=a.url_converter,y=a.url_converter_scope||this;if(b){for(b=b.replace(/[\u0000-\u001F]/g,""),b=b.replace(/\\[\"\';:\uFEFF]/g,n).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(a){return a.replace(/[;:]/g,n)});s=j.exec(b);)if(j.lastIndex=s.index+s[0].length,t=s[1].replace(k,"").toLowerCase(),u=s[2].replace(k,""),t&&u){if(t=q(t),u=q(u),t.indexOf(m)!==-1||t.indexOf('"')!==-1)continue;if(!a.allow_script_urls&&("behavior"==t||/expression\s*\(|\/\*|\*\//.test(u)))continue;"font-weight"===t&&"700"===u?u="bold":"color"!==t&&"background-color"!==t||(u=u.toLowerCase()),u=u.replace(h,c),u=u.replace(i,r),w[t]=v?o(u,!0):u}e("border","",!0),e("border","-width"),e("border","-color"),e("border","-style"),e("padding",""),e("margin",""),g("border","border-width","border-style","border-color"),"medium none"===w.border&&delete w.border,"none"===w["border-image"]&&delete w["border-image"]}return w},serialize:function(a,b){function c(b){var c,d,e,g;if(c=f[b])for(d=0,e=c.length;d<e;d++)b=c[d],g=a[b],g&&(i+=(i.length>0?" ":"")+b+": "+g+";")}function d(a,b){var c;return c=g["*"],(!c||!c[a])&&(c=g[b],!c||!c[a])}var e,h,i="";if(b&&f)c("*"),c(b);else for(e in a)h=a[e],!h||g&&!d(e,b)||(i+=(i.length>0?" ":"")+e+": "+h+";");return i}}}}),g("c",[],function(){return function(a,b){function c(a,c,d,e){var f,g;if(a){if(!e&&a[c])return a[c];if(a!=b){if(f=a[d])return f;for(g=a.parentNode;g&&g!=b;g=g.parentNode)if(f=g[d])return f}}}function d(a,c,d,e){var f,g,h;if(a){if(f=a[d],b&&f===b)return;if(f){if(!e)for(h=f[c];h;h=h[c])if(!h[c])return h;return f}if(g=a.parentNode,g&&g!==b)return g}}var e=a;this.current=function(){return e},this.next=function(a){return e=c(e,"firstChild","nextSibling",a)},this.prev=function(a){return e=c(e,"lastChild","previousSibling",a)},this.prev2=function(a){return e=d(e,"lastChild","previousSibling",a)}}}),g("d",["9"],function(a){function b(a){var b;return b=document.createElement("div"),b.innerHTML=a,b.textContent||b.innerText||a}function c(a,b){var c,d,f,g={};if(a){for(a=a.split(","),b=b||10,c=0;c<a.length;c+=2)d=String.fromCharCode(parseInt(a[c],b)),e[d]||(f="&"+a[c+1]+";",g[d]=f,g[f]=d);return g}}var d,e,f,g=a.makeMap,h=/[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,i=/[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,j=/[<>&\"\']/g,k=/&#([a-z0-9]+);?|&([a-z0-9]+);/gi,l={128:"\u20ac",130:"\u201a",131:"\u0192",132:"\u201e",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02c6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017d",145:"\u2018",146:"\u2019",147:"\u201c",148:"\u201d",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02dc",153:"\u2122",154:"\u0161",155:"\u203a",156:"\u0153",158:"\u017e",159:"\u0178"};e={'"':""","'":"'","<":"<",">":">","&":"&","`":"`"},f={"<":"<",">":">","&":"&",""":'"',"'":"'"},d=c("50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,t9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro",32);var m={encodeRaw:function(a,b){return a.replace(b?h:i,function(a){return e[a]||a})},encodeAllRaw:function(a){return(""+a).replace(j,function(a){return e[a]||a})},encodeNumeric:function(a,b){return a.replace(b?h:i,function(a){return a.length>1?"&#"+(1024*(a.charCodeAt(0)-55296)+(a.charCodeAt(1)-56320)+65536)+";":e[a]||"&#"+a.charCodeAt(0)+";"})},encodeNamed:function(a,b,c){return c=c||d,a.replace(b?h:i,function(a){return e[a]||c[a]||a})},getEncodeFunc:function(a,b){function f(a,c){return a.replace(c?h:i,function(a){return void 0!==e[a]?e[a]:void 0!==b[a]?b[a]:a.length>1?"&#"+(1024*(a.charCodeAt(0)-55296)+(a.charCodeAt(1)-56320)+65536)+";":"&#"+a.charCodeAt(0)+";"})}function j(a,c){return m.encodeNamed(a,c,b)}return b=c(b)||d,a=g(a.replace(/\+/g,",")),a.named&&a.numeric?f:a.named?b?j:m.encodeNamed:a.numeric?m.encodeNumeric:m.encodeRaw},decode:function(a){return a.replace(k,function(a,c){return c?(c="x"===c.charAt(0).toLowerCase()?parseInt(c.substr(1),16):parseInt(c,10),c>65535?(c-=65536,String.fromCharCode(55296+(c>>10),56320+(1023&c))):l[c]||String.fromCharCode(c)):f[a]||d[a]||b(a)})}};return m}),g("1h",["9"],function(a){function b(c){function d(){return J.createDocumentFragment()}function e(a,b){x(N,a,b)}function f(a,b){x(O,a,b)}function g(a){e(a.parentNode,U(a))}function h(a){e(a.parentNode,U(a)+1)}function i(a){f(a.parentNode,U(a))}function j(a){f(a.parentNode,U(a)+1)}function k(a){a?(I[R]=I[Q],I[S]=I[P]):(I[Q]=I[R],I[P]=I[S]),I.collapsed=N}function l(a){g(a),j(a)}function m(a){e(a,0),f(a,1===a.nodeType?a.childNodes.length:a.nodeValue.length)}function n(a,b){var c=I[Q],d=I[P],e=I[R],f=I[S],g=b.startContainer,h=b.startOffset,i=b.endContainer,j=b.endOffset;return 0===a?w(c,d,g,h):1===a?w(e,f,g,h):2===a?w(e,f,i,j):3===a?w(c,d,i,j):void 0}function o(){y(M)}function p(){return y(K)}function q(){return y(L)}function r(a){var b,d,e=this[Q],f=this[P];3!==e.nodeType&&4!==e.nodeType||!e.nodeValue?(e.childNodes.length>0&&(d=e.childNodes[f]),d?e.insertBefore(a,d):3==e.nodeType?c.insertAfter(a,e):e.appendChild(a)):f?f>=e.nodeValue.length?c.insertAfter(a,e):(b=e.splitText(f),e.parentNode.insertBefore(a,b)):e.parentNode.insertBefore(a,e)}function s(a){var b=I.extractContents();I.insertNode(a),a.appendChild(b),I.selectNode(a)}function t(){return T(new b(c),{startContainer:I[Q],startOffset:I[P],endContainer:I[R],endOffset:I[S],collapsed:I.collapsed,commonAncestorContainer:I.commonAncestorContainer})}function u(a,b){var c;if(3==a.nodeType)return a;if(b<0)return a;for(c=a.firstChild;c&&b>0;)--b,c=c.nextSibling;return c?c:a}function v(){return I[Q]==I[R]&&I[P]==I[S]}function w(a,b,d,e){var f,g,h,i,j,k;if(a==d)return b==e?0:b<e?-1:1;for(f=d;f&&f.parentNode!=a;)f=f.parentNode;if(f){for(g=0,h=a.firstChild;h!=f&&g<b;)g++,h=h.nextSibling;return b<=g?-1:1}for(f=a;f&&f.parentNode!=d;)f=f.parentNode;if(f){for(g=0,h=d.firstChild;h!=f&&g<e;)g++,h=h.nextSibling;return g<e?-1:1}for(i=c.findCommonAncestor(a,d),j=a;j&&j.parentNode!=i;)j=j.parentNode;for(j||(j=i),k=d;k&&k.parentNode!=i;)k=k.parentNode;if(k||(k=i),j==k)return 0;for(h=i.firstChild;h;){if(h==j)return-1;if(h==k)return 1;h=h.nextSibling}}function x(a,b,d){var e,f;for(a?(I[Q]=b,I[P]=d):(I[R]=b,I[S]=d),e=I[R];e.parentNode;)e=e.parentNode;for(f=I[Q];f.parentNode;)f=f.parentNode;f==e?w(I[Q],I[P],I[R],I[S])>0&&I.collapse(a):I.collapse(a),I.collapsed=v(),I.commonAncestorContainer=c.findCommonAncestor(I[Q],I[R])}function y(a){var b,c,d,e,f,g,h,i=0,j=0;if(I[Q]==I[R])return z(a);for(b=I[R],c=b.parentNode;c;b=c,c=c.parentNode){if(c==I[Q])return A(b,a);++i}for(b=I[Q],c=b.parentNode;c;b=c,c=c.parentNode){if(c==I[R])return B(b,a);++j}for(d=j-i,e=I[Q];d>0;)e=e.parentNode,d--;for(f=I[R];d<0;)f=f.parentNode,d++;for(g=e.parentNode,h=f.parentNode;g!=h;g=g.parentNode,h=h.parentNode)e=g,f=h;return C(e,f,a)}function z(a){var b,c,e,f,g,h,i,j,k;if(a!=M&&(b=d()),I[P]==I[S])return b;if(3==I[Q].nodeType){if(c=I[Q].nodeValue,e=c.substring(I[P],I[S]),a!=L&&(f=I[Q],j=I[P],k=I[S]-I[P],0===j&&k>=f.nodeValue.length-1?f.parentNode.removeChild(f):f.deleteData(j,k),I.collapse(N)),a==M)return;return e.length>0&&b.appendChild(J.createTextNode(e)),b}for(f=u(I[Q],I[P]),g=I[S]-I[P];f&&g>0;)h=f.nextSibling,i=G(f,a),b&&b.appendChild(i),--g,f=h;return a!=L&&I.collapse(N),b}function A(a,b){var c,e,f,g,h,i;if(b!=M&&(c=d()),e=D(a,b),c&&c.appendChild(e),f=U(a),g=f-I[P],g<=0)return b!=L&&(I.setEndBefore(a),I.collapse(O)),c;for(e=a.previousSibling;g>0;)h=e.previousSibling,i=G(e,b),c&&c.insertBefore(i,c.firstChild),--g,e=h;return b!=L&&(I.setEndBefore(a),I.collapse(O)),c}function B(a,b){var c,e,f,g,h,i;for(b!=M&&(c=d()),f=E(a,b),c&&c.appendChild(f),e=U(a),++e,g=I[S]-e,f=a.nextSibling;f&&g>0;)h=f.nextSibling,i=G(f,b),c&&c.appendChild(i),--g,f=h;return b!=L&&(I.setStartAfter(a),I.collapse(N)),c}function C(a,b,c){var e,f,g,h,i,j,k;for(c!=M&&(f=d()),e=E(a,c),f&&f.appendChild(e),g=U(a),h=U(b),++g,i=h-g,j=a.nextSibling;i>0;)k=j.nextSibling,e=G(j,c),f&&f.appendChild(e),j=k,--i;return e=D(b,c),f&&f.appendChild(e),c!=L&&(I.setStartAfter(a),I.collapse(N)),f}function D(a,b){var c,d,e,f,g,h=u(I[R],I[S]-1),i=h!=I[R];if(h==a)return F(h,i,O,b);for(c=h.parentNode,d=F(c,O,O,b);c;){for(;h;)e=h.previousSibling,f=F(h,i,O,b),b!=M&&d.insertBefore(f,d.firstChild),i=N,h=e;if(c==a)return d;h=c.previousSibling,c=c.parentNode,g=F(c,O,O,b),b!=M&&g.appendChild(d),d=g}}function E(a,b){var c,d,e,f,g,h=u(I[Q],I[P]),i=h!=I[Q];if(h==a)return F(h,i,N,b);for(c=h.parentNode,d=F(c,O,N,b);c;){for(;h;)e=h.nextSibling,f=F(h,i,N,b),b!=M&&d.appendChild(f),i=N,h=e;if(c==a)return d;h=c.nextSibling,c=c.parentNode,g=F(c,O,N,b),b!=M&&g.appendChild(d),d=g}}function F(a,b,d,e){var f,g,h,i,j;if(b)return G(a,e);if(3==a.nodeType){if(f=a.nodeValue,d?(i=I[P],g=f.substring(i),h=f.substring(0,i)):(i=I[S],g=f.substring(0,i),h=f.substring(i)),e!=L&&(a.nodeValue=h),e==M)return;return j=c.clone(a,O),j.nodeValue=g,j}if(e!=M)return c.clone(a,O)}function G(a,b){return b!=M?b==L?c.clone(a,N):a:void a.parentNode.removeChild(a)}function H(){return c.create("body",null,q()).outerText}var I=this,J=c.doc,K=0,L=1,M=2,N=!0,O=!1,P="startOffset",Q="startContainer",R="endContainer",S="endOffset",T=a.extend,U=c.nodeIndex;return T(I,{startContainer:J,startOffset:0,endContainer:J,endOffset:0,collapsed:N,commonAncestorContainer:J,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:e,setEnd:f,setStartBefore:g,setStartAfter:h,setEndBefore:i,setEndAfter:j,collapse:k,selectNode:l,selectNodeContents:m,compareBoundaryPoints:n,deleteContents:o,extractContents:p,cloneContents:q,insertNode:r,surroundContents:s,cloneRange:t,toStringIE:H}),I}return b.prototype.toString=function(){return this.toStringIE()},b}),h("3y",Array),h("3z",Error),g("1m",["3y","3z"],function(a,b){var c=function(){},d=function(a,b){return function(){return a(b.apply(null,arguments))}},e=function(a){return function(){return a}},f=function(a){return a},g=function(a,b){return a===b},h=function(b){for(var c=new a(arguments.length-1),d=1;d<arguments.length;d++)c[d-1]=arguments[d];return function(){for(var d=new a(arguments.length),e=0;e<d.length;e++)d[e]=arguments[e];var f=c.concat(d);return b.apply(null,f)}},i=function(a){return function(){return!a.apply(null,arguments)}},j=function(a){return function(){throw new b(a)}},k=function(a){return a()},l=function(a){a()},m=e(!1),n=e(!0);return{noop:c,compose:d,constant:e,identity:f,tripleEquals:g,curry:h,not:i,die:j,apply:k,call:l,never:m,always:n}}),h("5s",Object),g("4z",["1m","5s"],function(a,b){var c=a.never,d=a.always,e=function(){return f},f=function(){var f=function(a){return a.isNone()},g=function(a){return a()},h=function(a){return a},i=function(){},j={fold:function(a,b){return a()},is:c,isSome:c,isNone:d,getOr:h,getOrThunk:g,getOrDie:function(a){throw new Error(a||"error: getOrDie called on none.")},or:h,orThunk:g,map:e,ap:e,each:i,bind:e,flatten:e,exists:c,forall:d,filter:e,equals:f,equals_:f,toArray:function(){return[]},toString:a.constant("none()")};return b.freeze&&b.freeze(j),j}(),g=function(a){var b=function(){return a},h=function(){return k},i=function(b){return g(b(a))},j=function(b){return b(a)},k={fold:function(b,c){return c(a)},is:function(b){return a===b},isSome:d,isNone:c,getOr:b,getOrThunk:b,getOrDie:b,or:h,orThunk:h,map:i,ap:function(b){return b.fold(e,function(b){return g(b(a))})},each:function(b){b(a)},bind:j,flatten:b,exists:j,forall:j,filter:function(b){return b(a)?k:f},equals:function(b){return b.is(a)},equals_:function(b,d){return b.fold(c,function(b){return d(a,b)})},toArray:function(){return[a]},toString:function(){return"some("+a+")"}};return k},h=function(a){return null===a||void 0===a?f:g(a)};return{some:g,none:e,from:h}}),h("50",String),g("3x",["4z","3y","3z","50"],function(a,b,c,d){var e=function(){var a=b.prototype.indexOf,c=function(b,c){return a.call(b,c)},d=function(a,b){return u(a,b)};return void 0===a?d:c}(),f=function(b,c){var d=e(b,c);return d===-1?a.none():a.some(d)},g=function(a,b){return e(a,b)>-1},h=function(a,b){return t(a,b).isSome()},i=function(a,b){for(var c=[],d=0;d<a;d++)c.push(b(d));return c},j=function(a,b){for(var c=[],d=0;d<a.length;d+=b){var e=a.slice(d,d+b);c.push(e)}return c},k=function(a,c){for(var d=a.length,e=new b(d),f=0;f<d;f++){var g=a[f];e[f]=c(g,f,a)}return e},l=function(a,b){for(var c=0,d=a.length;c<d;c++){var e=a[c];b(e,c,a)}},m=function(a,b){for(var c=a.length-1;c>=0;c--){var d=a[c];b(d,c,a)}},n=function(a,b){for(var c=[],d=[],e=0,f=a.length;e<f;e++){var g=a[e],h=b(g,e,a)?c:d;h.push(g)}return{pass:c,fail:d}},o=function(a,b){for(var c=[],d=0,e=a.length;d<e;d++){var f=a[d];b(f,d,a)&&c.push(f)}return c},p=function(a,b){if(0===a.length)return[];for(var c=b(a[0]),d=[],e=[],f=0,g=a.length;f<g;f++){var h=a[f],i=b(h);i!==c&&(d.push(e),e=[]),c=i,e.push(h)}return 0!==e.length&&d.push(e),d},q=function(a,b,c){return m(a,function(a){c=b(c,a)}),c},r=function(a,b,c){return l(a,function(a){c=b(c,a)}),c},s=function(b,c){for(var d=0,e=b.length;d<e;d++){var f=b[d];if(c(f,d,b))return a.some(f)}return a.none()},t=function(b,c){for(var d=0,e=b.length;d<e;d++){var f=b[d];if(c(f,d,b))return a.some(d)}return a.none()},u=function(a,b){for(var c=0,d=a.length;c<d;++c)if(a[c]===b)return c;return-1},v=b.prototype.push,w=function(a){for(var d=[],e=0,f=a.length;e<f;++e){if(!b.prototype.isPrototypeOf(a[e]))throw new c("Arr.flatten item "+e+" was not an array, input: "+a);v.apply(d,a[e])}return d},x=function(a,b){var c=k(a,b);return w(c)},y=function(a,b){for(var c=0,d=a.length;c<d;++c){var e=a[c];if(b(e,c,a)!==!0)return!1}return!0},z=function(a,b){return a.length===b.length&&y(a,function(a,c){return a===b[c]})},A=b.prototype.slice,B=function(a){var b=A.call(a,0);return b.reverse(),b},C=function(a,b){return o(a,function(a){return!g(b,a)})},D=function(a,b){for(var c={},e=0,f=a.length;e<f;e++){var g=a[e];c[d(g)]=b(g,e)}return c},E=function(a){return[a]},F=function(a,b){var c=A.call(a,0);return c.sort(b),c};return{map:k,each:l,eachr:m,partition:n,filter:o,groupBy:p,indexOf:f,foldr:q,foldl:r,find:s,findIndex:t,flatten:w,bind:x,forall:y,exists:h,contains:g,equal:z,reverse:B,chunk:j,difference:C,mapToObject:D,pure:E,sort:F,range:i}}),h("5t",setTimeout),g("51",["3x","4z","5t"],function(a,b,c){var d=function(e){var f=b.none(),g=[],h=function(a){return d(function(b){i(function(c){b(a(c))})})},i=function(a){k()?m(a):g.push(a)},j=function(a){f=b.some(a),l(g),g=[]},k=function(){return f.isSome()},l=function(b){a.each(b,m)},m=function(a){f.each(function(b){c(function(){a(b)},0)})};return e(j),{get:i,map:h,isReady:k}},e=function(a){return d(function(b){b(a)})};return{nu:d,pure:e}}),g("52",["3y","5t"],function(a,b){var c=function(c){return function(){var d=a.prototype.slice.call(arguments),e=this;b(function(){c.apply(e,d)},0)}};return{bounce:c}}),g("40",["51","52"],function(a,b){var c=function(d){var e=function(a){d(b.bounce(a))},f=function(a){return c(function(b){e(function(c){var d=a(c);b(d)})})},g=function(a){return c(function(b){e(function(c){a(c).get(b)})})},h=function(a){return c(function(b){e(function(c){a.get(b)})})},i=function(){return a.nu(e)};return{map:f,bind:g,anonBind:h,toLazy:i,get:e}},d=function(a){return c(function(b){b(a)})};return{nu:c,pure:d}}),g("53",["3x"],function(a){var b=function(b,c){return c(function(c){var d=[],e=0,f=function(a){return function(f){d[a]=f,e++,e>=b.length&&c(d)}};0===b.length?c([]):a.each(b,function(a,b){a.get(f(b))})})};return{par:b}}),g("41",["3x","40","53"],function(a,b,c){var d=function(a){return c.par(a,b.nu)},e=function(b,c){var e=a.map(b,c);return d(e)},f=function(a,b){return function(c){return b(c).bind(a)}};return{par:d,mapM:e,compose:f}}),g("42",["1m","4z"],function(a,b){var c=function(d){var e=function(a){return d===a},f=function(a){return c(d)},g=function(a){return c(d)},h=function(a){return c(a(d))},i=function(a){a(d)},j=function(a){return a(d)},k=function(a,b){return b(d)},l=function(a){return a(d)},m=function(a){return a(d)},n=function(){return b.some(d)};return{is:e,isValue:a.constant(!0),isError:a.constant(!1),getOr:a.constant(d),getOrThunk:a.constant(d),getOrDie:a.constant(d),or:f,orThunk:g,fold:k,map:h,each:i,bind:j,exists:l,forall:m,toOption:n}},d=function(c){var e=function(a){return a()},f=function(){return a.die(c)()},g=function(a){return a},h=function(a){return a()},i=function(a){return d(c)},j=function(a){return d(c)},k=function(a,b){return a(c)};return{is:a.constant(!1),isValue:a.constant(!1),isError:a.constant(!0),getOr:a.identity,getOrThunk:e,getOrDie:f,or:g,orThunk:h,fold:k,map:i,each:a.noop,bind:j,exists:a.constant(!1),forall:a.constant(!0),toOption:b.none}};return{value:c,error:d}}),g("1i",["3x","1m","40","41","42","5","9"],function(a,b,c,d,e,f,g){"use strict";return function(h,i){function j(a){h.getElementsByTagName("head")[0].appendChild(a)}function k(a,b,c){function d(){for(var a=t.passed,b=a.length;b--;)a[b]();t.status=2,t.passed=[],t.failed=[]}function e(){for(var a=t.failed,b=a.length;b--;)a[b]();t.status=3,t.passed=[],t.failed=[]}function i(){var a=navigator.userAgent.match(/WebKit\/(\d*)/);return!!(a&&a[1]<536)}function k(a,b){a()||((new Date).getTime()-s<l?f.setTimeout(b):e())}function o(){k(function(){for(var a,b,c=h.styleSheets,e=c.length;e--;)if(a=c[e],b=a.ownerNode?a.ownerNode:a.owningElement,b&&b.id===q.id)return d(),!0},o)}function p(){k(function(){try{var a=r.sheet.cssRules;return d(),!!a}catch(a){}},p)}var q,r,s,t;if(a=g._addCacheSuffix(a),n[a]?t=n[a]:(t={passed:[],failed:[]},n[a]=t),b&&t.passed.push(b),c&&t.failed.push(c),1!=t.status){if(2==t.status)return void d();if(3==t.status)return void e();if(t.status=1,q=h.createElement("link"),q.rel="stylesheet",q.type="text/css",q.id="u"+m++,q.async=!1,q.defer=!1,s=(new Date).getTime(),"onload"in q&&!i())q.onload=o,q.onerror=e;else{if(navigator.userAgent.indexOf("Firefox")>0)return r=h.createElement("style"), -r.textContent='@import "'+a+'"',p(),void j(r);o()}j(q),q.href=a}}var l,m=0,n={};i=i||{},l=i.maxLoadTime||5e3;var o=function(a){return c.nu(function(c){k(a,b.compose(c,b.constant(e.value(a))),b.compose(c,b.constant(e.error(a))))})},p=function(a){return a.fold(b.identity,b.identity)},q=function(b,c,e){d.par(a.map(b,o)).get(function(b){var d=a.partition(b,function(a){return a.isValue()});d.fail.length>0?e(d.fail.map(p)):c(d.pass.map(p))})};return{load:k,loadAll:q}}}),g("j",["9"],function(a){function b(b,c){return b=a.trim(b),b?b.split(c||" "):[]}function c(a){function c(a,c,d){function e(a,b){var c,d,e={};for(c=0,d=a.length;c<d;c++)e[a[c]]=b||{};return e}var h,i,j;for(d=d||[],c=c||"","string"==typeof d&&(d=b(d)),a=b(a),h=a.length;h--;)i=b([g,c].join(" ")),j={attributes:e(i),attributesOrder:i,children:e(d,f)},n[a[h]]=j}function d(a,c){var d,e,f,g;for(a=b(a),d=a.length,c=b(c);d--;)for(e=n[a[d]],f=0,g=c.length;f<g;f++)e.attributes[c[f]]={},e.attributesOrder.push(c[f])}var g,i,j,k,l,m,n={};return e[a]?e[a]:(g="id accesskey class dir lang style tabindex title role",i="address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul",j="a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd label map noscript object q s samp script select small span strong sub sup textarea u var #text #comment","html4"!=a&&(g+=" contenteditable contextmenu draggable dropzone hidden spellcheck translate",i+=" article aside details dialog figure header footer hgroup section nav",j+=" audio canvas command datalist mark meter output picture progress time wbr video ruby bdi keygen"),"html5-strict"!=a&&(g+=" xml:lang",m="acronym applet basefont big font strike tt",j=[j,m].join(" "),h(b(m),function(a){c(a,"",j)}),l="center dir isindex noframes",i=[i,l].join(" "),k=[i,j].join(" "),h(b(l),function(a){c(a,"",k)})),k=k||[i,j].join(" "),c("html","manifest","head body"),c("head","","base command link meta noscript script style title"),c("title hr noscript br"),c("base","href target"),c("link","href rel media hreflang type sizes hreflang"),c("meta","name http-equiv content charset"),c("style","media type scoped"),c("script","src async defer type charset"),c("body","onafterprint onbeforeprint onbeforeunload onblur onerror onfocus onhashchange onload onmessage onoffline ononline onpagehide onpageshow onpopstate onresize onscroll onstorage onunload",k),c("address dt dd div caption","",k),c("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn","",j),c("blockquote","cite",k),c("ol","reversed start type","li"),c("ul","","li"),c("li","value",k),c("dl","","dt dd"),c("a","href target rel media hreflang type",j),c("q","cite",j),c("ins del","cite datetime",k),c("img","src sizes srcset alt usemap ismap width height"),c("iframe","src name width height",k),c("embed","src type width height"),c("object","data type typemustmatch name usemap form width height",[k,"param"].join(" ")),c("param","name value"),c("map","name",[k,"area"].join(" ")),c("area","alt coords shape href target rel media hreflang type"),c("table","border","caption colgroup thead tfoot tbody tr"+("html4"==a?" col":"")),c("colgroup","span","col"),c("col","span"),c("tbody thead tfoot","","tr"),c("tr","","td th"),c("td","colspan rowspan headers",k),c("th","colspan rowspan headers scope abbr",k),c("form","accept-charset action autocomplete enctype method name novalidate target",k),c("fieldset","disabled form name",[k,"legend"].join(" ")),c("label","form for",j),c("input","accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"),c("button","disabled form formaction formenctype formmethod formnovalidate formtarget name type value","html4"==a?k:j),c("select","disabled form multiple name required size","option optgroup"),c("optgroup","disabled label","option"),c("option","disabled label selected value"),c("textarea","cols dirname disabled form maxlength name readonly required rows wrap"),c("menu","type label",[k,"li"].join(" ")),c("noscript","",k),"html4"!=a&&(c("wbr"),c("ruby","",[j,"rt rp"].join(" ")),c("figcaption","",k),c("mark rt rp summary bdi","",j),c("canvas","width height",k),c("video","src crossorigin poster preload autoplay mediagroup loop muted controls width height buffered",[k,"track source"].join(" ")),c("audio","src crossorigin preload autoplay mediagroup loop muted controls buffered volume",[k,"track source"].join(" ")),c("picture","","img source"),c("source","src srcset type media sizes"),c("track","kind src srclang label default"),c("datalist","",[j,"option"].join(" ")),c("article section nav aside header footer","",k),c("hgroup","","h1 h2 h3 h4 h5 h6"),c("figure","",[k,"figcaption"].join(" ")),c("time","datetime",j),c("dialog","open",k),c("command","type label icon disabled checked radiogroup command"),c("output","for form name",j),c("progress","value max",j),c("meter","value min max low high optimum",j),c("details","open",[k,"summary"].join(" ")),c("keygen","autofocus challenge disabled form keytype name")),"html5-strict"!=a&&(d("script","language xml:space"),d("style","xml:space"),d("object","declare classid code codebase codetype archive standby align border hspace vspace"),d("embed","align name hspace vspace"),d("param","valuetype type"),d("a","charset name rev shape coords"),d("br","clear"),d("applet","codebase archive code object alt name width height align hspace vspace"),d("img","name longdesc align border hspace vspace"),d("iframe","longdesc frameborder marginwidth marginheight scrolling align"),d("font basefont","size color face"),d("input","usemap align"),d("select","onchange"),d("textarea"),d("h1 h2 h3 h4 h5 h6 div p legend caption","align"),d("ul","type compact"),d("li","type"),d("ol dl menu dir","compact"),d("pre","width xml:space"),d("hr","align noshade size width"),d("isindex","prompt"),d("table","summary width frame rules cellspacing cellpadding align bgcolor"),d("col","width align char charoff valign"),d("colgroup","width align char charoff valign"),d("thead","align char charoff valign"),d("tr","align char charoff valign bgcolor"),d("th","axis align char charoff valign nowrap bgcolor width height"),d("form","accept"),d("td","abbr axis scope align char charoff valign nowrap bgcolor width height"),d("tfoot","align char charoff valign"),d("tbody","align char charoff valign"),d("area","nohref"),d("body","background bgcolor text link vlink alink")),"html4"!=a&&(d("input button select textarea","autofocus"),d("input textarea","placeholder"),d("a","download"),d("link script img","crossorigin"),d("iframe","sandbox seamless allowfullscreen")),h(b("a form meter progress dfn"),function(a){n[a]&&delete n[a].children[a]}),delete n.caption.children.table,delete n.script,e[a]=n,n)}function d(a,b){var c;return a&&(c={},"string"==typeof a&&(a={"*":a}),h(a,function(a,d){c[d]=c[d.toUpperCase()]="map"==b?g(a,/[, ]/):j(a,/[, ]/)})),c}var e={},f={},g=a.makeMap,h=a.each,i=a.extend,j=a.explode,k=a.inArray;return function(a){function f(b,c,d){var f=a[b];return f?f=g(f,/[, ]/,g(f.toUpperCase(),/[, ]/)):(f=e[b],f||(f=g(c," ",g(c.toUpperCase()," ")),f=i(f,d),e[b]=f)),f}function l(a){return new RegExp("^"+a.replace(/([?+*])/g,".$1")+"$")}function m(a){var c,d,e,f,h,i,j,m,n,o,p,q,r,s,t,u,v,w,x,y=/^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,z=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,A=/[*?+]/;if(a)for(a=b(a,","),F["@"]&&(u=F["@"].attributes,v=F["@"].attributesOrder),c=0,d=a.length;c<d;c++)if(h=y.exec(a[c])){if(s=h[1],n=h[2],t=h[3],m=h[5],q={},r=[],i={attributes:q,attributesOrder:r},"#"===s&&(i.paddEmpty=!0),"-"===s&&(i.removeEmpty=!0),"!"===h[4]&&(i.removeEmptyAttrs=!0),u){for(w in u)q[w]=u[w];r.push.apply(r,v)}if(m)for(m=b(m,"|"),e=0,f=m.length;e<f;e++)if(h=z.exec(m[e])){if(j={},p=h[1],o=h[2].replace(/::/g,":"),s=h[3],x=h[4],"!"===p&&(i.attributesRequired=i.attributesRequired||[],i.attributesRequired.push(o),j.required=!0),"-"===p){delete q[o],r.splice(k(r,o),1);continue}s&&("="===s&&(i.attributesDefault=i.attributesDefault||[],i.attributesDefault.push({name:o,value:x}),j.defaultValue=x),":"===s&&(i.attributesForced=i.attributesForced||[],i.attributesForced.push({name:o,value:x}),j.forcedValue=x),"<"===s&&(j.validValues=g(x,"?"))),A.test(o)?(i.attributePatterns=i.attributePatterns||[],j.pattern=l(o),i.attributePatterns.push(j)):(q[o]||r.push(o),q[o]=j)}u||"@"!=n||(u=q,v=r),t&&(i.outputName=n,F[t]=i),A.test(n)?(i.pattern=l(n),H.push(i)):F[n]=i}}function n(a){F={},H=[],m(a),h(t,function(a,b){G[b]=a.children})}function o(a){var c=/^(~)?(.+)$/;a&&(e.text_block_elements=e.block_elements=null,h(b(a,","),function(a){var b=c.exec(a),d="~"===b[1],e=d?"span":"div",f=b[2];if(G[f]=G[e],I[f]=e,d||(z[f.toUpperCase()]={},z[f]={}),!F[f]){var g=F[e];g=i({},g),delete g.removeEmptyAttrs,delete g.removeEmpty,F[f]=g}h(G,function(a,b){a[e]&&(G[b]=a=i({},G[b]),a[f]=a[e])})}))}function p(c){var d=/^([+\-]?)(\w+)\[([^\]]+)\]$/;e[a.schema]=null,c&&h(b(c,","),function(a){var c,e,f=d.exec(a);f&&(e=f[1],c=e?G[f[2]]:G[f[2]]={"#comment":{}},c=G[f[2]],h(b(f[3],"|"),function(a){"-"===e?delete c[a]:c[a]={}}))})}function q(a){var b,c=F[a];if(c)return c;for(b=H.length;b--;)if(c=H[b],c.pattern.test(a))return c}var r,s,t,u,v,w,x,y,z,A,B,C,D,E=this,F={},G={},H=[],I={},J={};a=a||{},t=c(a.schema),a.verify_html===!1&&(a.valid_elements="*[*]"),r=d(a.valid_styles),s=d(a.invalid_styles,"map"),y=d(a.valid_classes,"map"),u=f("whitespace_elements","pre script noscript style textarea video audio iframe object code"),v=f("self_closing_elements","colgroup dd dt li option p td tfoot th thead tr"),w=f("short_ended_elements","area base basefont br col frame hr img input isindex link meta param embed source wbr track"),x=f("boolean_attributes","checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls"),A=f("non_empty_elements","td th iframe video audio object script pre code",w),B=f("move_caret_before_on_enter_elements","table",A),C=f("text_block_elements","h1 h2 h3 h4 h5 h6 p div address pre form blockquote center dir fieldset header footer article section hgroup aside nav figure"),z=f("block_elements","hr table tbody thead tfoot th tr td li ol ul caption dl dt dd noscript menu isindex option datalist select optgroup figcaption",C),D=f("text_inline_elements","span strong b em i font strike u var cite dfn code mark q sup sub samp"),h((a.special||"script noscript noframes noembed title style textarea xmp").split(" "),function(a){J[a]=new RegExp("</"+a+"[^>]*>","gi")}),a.valid_elements?n(a.valid_elements):(h(t,function(a,b){F[b]={attributes:a.attributes,attributesOrder:a.attributesOrder},G[b]=a.children}),"html5"!=a.schema&&h(b("strong/b em/i"),function(a){a=b(a,"/"),F[a[1]].outputName=a[0]}),h(b("ol ul sub sup blockquote span font a table tbody tr strong em b i"),function(a){F[a]&&(F[a].removeEmpty=!0)}),h(b("p h1 h2 h3 h4 h5 h6 th td pre div address caption"),function(a){F[a].paddEmpty=!0}),h(b("span"),function(a){F[a].removeEmptyAttrs=!0})),o(a.custom_elements),p(a.valid_children),m(a.extended_valid_elements),p("+ol[ul|ol],+ul[ul|ol]"),h({dd:"dl",dt:"dl",li:"ul ol",td:"tr",th:"tr",tr:"tbody thead tfoot",tbody:"table",thead:"table",tfoot:"table",legend:"fieldset",area:"map",param:"video audio object"},function(a,c){F[c]&&(F[c].parentsRequired=b(a))}),a.invalid_elements&&h(j(a.invalid_elements),function(a){F[a]&&delete F[a]}),q("span")||m("span[!data-mce-type|*]"),E.children=G,E.getValidStyles=function(){return r},E.getInvalidStyles=function(){return s},E.getValidClasses=function(){return y},E.getBoolAttrs=function(){return x},E.getBlockElements=function(){return z},E.getTextBlockElements=function(){return C},E.getTextInlineElements=function(){return D},E.getShortEndedElements=function(){return w},E.getSelfClosingElements=function(){return v},E.getNonEmptyElements=function(){return A},E.getMoveCaretBeforeOnEnterElements=function(){return B},E.getWhiteSpaceElements=function(){return u},E.getSpecialElements=function(){return J},E.isValidChild=function(a,b){var c=G[a.toLowerCase()];return!(!c||!c[b.toLowerCase()])},E.isValid=function(a,b){var c,d,e=q(a);if(e){if(!b)return!0;if(e.attributes[b])return!0;if(c=e.attributePatterns)for(d=c.length;d--;)if(c[d].pattern.test(a))return!0}return!1},E.getElementRule=q,E.getCustomElements=function(){return I},E.addValidElements=m,E.setValidElements=n,E.addCustomElements=o,E.addValidChildren=p,E.elements=F}}),g("e",["a","7","1h","8","1i","c","6","d","j","b","9"],function(a,b,c,d,e,f,g,h,i,j,k){function l(a,b){var c,d={},e=b.keep_values;return c={set:function(c,d,e){b.url_converter&&(d=b.url_converter.call(b.url_converter_scope||a,d,e,c[0])),c.attr("data-mce-"+e,d).attr(e,d)},get:function(a,b){return a.attr("data-mce-"+b)||a.attr(b)}},d={style:{set:function(a,b){return null!==b&&"object"==typeof b?void a.css(b):(e&&a.attr("data-mce-style",b),void a.attr("style",b))},get:function(b){var c=b.attr("data-mce-style")||b.attr("style");return c=a.serializeStyle(a.parseStyle(c),b[0].nodeName)}}},e&&(d.href=d.src=c),d}function m(a,b){var c=b.attr("style");c=a.serializeStyle(a.parseStyle(c),b[0].nodeName),c||(c=null),b.attr("data-mce-style",c)}function n(a,b){var c,d,e=0;if(a)for(c=a.nodeType,a=a.previousSibling;a;a=a.previousSibling)d=a.nodeType,(!b||3!=d||d!=c&&a.nodeValue.length)&&(e++,c=d);return e}function o(c,d){var f,g=this;g.doc=c,g.win=window,g.files={},g.counter=0,g.stdMode=!t||c.documentMode>=8,g.boxModel=!t||"CSS1Compat"==c.compatMode||g.stdMode,g.styleSheetLoader=new e(c),g.boundEvents=[],g.settings=d=d||{},g.schema=d.schema?d.schema:new i({}),g.styles=new j({url_converter:d.url_converter,url_converter_scope:d.url_converter_scope},d.schema),g.fixDoc(c),g.events=d.ownEvents?new b(d.proxy):b.Event,g.attrHooks=l(g,d),f=d.schema?d.schema.getBlockElements():{},g.$=a.overrideDefaults(function(){return{context:c,element:g.getRoot()}}),g.isBlock=function(a){if(!a)return!1;var b=a.nodeType;return b?!(1!==b||!f[a.nodeName]):!!f[a]}}var p=k.each,q=k.is,r=k.grep,s=k.trim,t=g.ie,u=/^([a-z0-9],?)+$/i,v=/^[ \t\r\n]*$/;return o.prototype={$$:function(a){return"string"==typeof a&&(a=this.get(a)),this.$(a)},root:null,fixDoc:function(a){var b,c=this.settings;if(t&&c.schema){"abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video".replace(/\w+/g,function(b){a.createElement(b)});for(b in c.schema.getCustomElements())a.createElement(b)}},clone:function(a,b){var c,d,e=this;return!t||1!==a.nodeType||b?a.cloneNode(b):(d=e.doc,b?c.firstChild:(c=d.createElement(a.nodeName),p(e.getAttribs(a),function(b){e.setAttrib(c,b.nodeName,e.getAttrib(a,b.nodeName))}),c))},getRoot:function(){var a=this;return a.settings.root_element||a.doc.body},getViewPort:function(a){var b,c;return a=a?a:this.win,b=a.document,c=this.boxModel?b.documentElement:b.body,{x:a.pageXOffset||c.scrollLeft,y:a.pageYOffset||c.scrollTop,w:a.innerWidth||c.clientWidth,h:a.innerHeight||c.clientHeight}},getRect:function(a){var b,c,d=this;return a=d.get(a),b=d.getPos(a),c=d.getSize(a),{x:b.x,y:b.y,w:c.w,h:c.h}},getSize:function(a){var b,c,d=this;return a=d.get(a),b=d.getStyle(a,"width"),c=d.getStyle(a,"height"),b.indexOf("px")===-1&&(b=0),c.indexOf("px")===-1&&(c=0),{w:parseInt(b,10)||a.offsetWidth||a.clientWidth,h:parseInt(c,10)||a.offsetHeight||a.clientHeight}},getParent:function(a,b,c){return this.getParents(a,b,c,!1)},getParents:function(a,b,c,d){var e,f=this,g=[];for(a=f.get(a),d=void 0===d,c=c||("BODY"!=f.getRoot().nodeName?f.getRoot().parentNode:null),q(b,"string")&&(e=b,b="*"===b?function(a){return 1==a.nodeType}:function(a){return f.is(a,e)});a&&a!=c&&a.nodeType&&9!==a.nodeType;){if(!b||b(a)){if(!d)return a;g.push(a)}a=a.parentNode}return d?g:null},get:function(a){var b;return a&&this.doc&&"string"==typeof a&&(b=a,a=this.doc.getElementById(a),a&&a.id!==b)?this.doc.getElementsByName(b)[1]:a},getNext:function(a,b){return this._findSib(a,b,"nextSibling")},getPrev:function(a,b){return this._findSib(a,b,"previousSibling")},select:function(a,b){var c=this;return d(a,c.get(b)||c.settings.root_element||c.doc,[])},is:function(a,b){var c;if(!a)return!1;if(void 0===a.length){if("*"===b)return 1==a.nodeType;if(u.test(b)){for(b=b.toLowerCase().split(/,/),a=a.nodeName.toLowerCase(),c=b.length-1;c>=0;c--)if(b[c]==a)return!0;return!1}}if(a.nodeType&&1!=a.nodeType)return!1;var e=a.nodeType?[a]:a;return d(b,e[0].ownerDocument||e[0],null,e).length>0},add:function(a,b,c,d,e){var f=this;return this.run(a,function(a){var g;return g=q(b,"string")?f.doc.createElement(b):b,f.setAttribs(g,c),d&&(d.nodeType?g.appendChild(d):f.setHTML(g,d)),e?g:a.appendChild(g)})},create:function(a,b,c){return this.add(this.doc.createElement(a),a,b,c,1)},createHTML:function(a,b,c){var d,e="";e+="<"+a;for(d in b)b.hasOwnProperty(d)&&null!==b[d]&&"undefined"!=typeof b[d]&&(e+=" "+d+'="'+this.encode(b[d])+'"');return"undefined"!=typeof c?e+">"+c+"</"+a+">":e+" />"},createFragment:function(a){var b,c,d,e=this.doc;for(d=e.createElement("div"),b=e.createDocumentFragment(),a&&(d.innerHTML=a);c=d.firstChild;)b.appendChild(c);return b},remove:function(a,b){return a=this.$$(a),b?a.each(function(){for(var a;a=this.firstChild;)3==a.nodeType&&0===a.data.length?this.removeChild(a):this.parentNode.insertBefore(a,this)}).remove():a.remove(),a.length>1?a.toArray():a[0]},setStyle:function(a,b,c){a=this.$$(a).css(b,c),this.settings.update_styles&&m(this,a)},getStyle:function(a,b,c){return a=this.$$(a),c?a.css(b):(b=b.replace(/-(\D)/g,function(a,b){return b.toUpperCase()}),"float"==b&&(b=g.ie&&g.ie<12?"styleFloat":"cssFloat"),a[0]&&a[0].style?a[0].style[b]:void 0)},setStyles:function(a,b){a=this.$$(a).css(b),this.settings.update_styles&&m(this,a)},removeAllAttribs:function(a){return this.run(a,function(a){var b,c=a.attributes;for(b=c.length-1;b>=0;b--)a.removeAttributeNode(c.item(b))})},setAttrib:function(a,b,c){var d,e,f=this,g=f.settings;""===c&&(c=null),a=f.$$(a),d=a.attr(b),a.length&&(e=f.attrHooks[b],e&&e.set?e.set(a,c,b):a.attr(b,c),d!=c&&g.onSetAttrib&&g.onSetAttrib({attrElm:a,attrName:b,attrValue:c}))},setAttribs:function(a,b){var c=this;c.$$(a).each(function(a,d){p(b,function(a,b){c.setAttrib(d,b,a)})})},getAttrib:function(a,b,c){var d,e,f=this;return a=f.$$(a),a.length&&(d=f.attrHooks[b],e=d&&d.get?d.get(a,b):a.attr(b)),"undefined"==typeof e&&(e=c||""),e},getPos:function(b,c){var d,e,f=this,g=0,h=0,i=f.doc,j=i.body;if(b=f.get(b),c=c||j,b){if(c===j&&b.getBoundingClientRect&&"static"===a(j).css("position"))return e=b.getBoundingClientRect(),c=f.boxModel?i.documentElement:j,g=e.left+(i.documentElement.scrollLeft||j.scrollLeft)-c.clientLeft,h=e.top+(i.documentElement.scrollTop||j.scrollTop)-c.clientTop,{x:g,y:h};for(d=b;d&&d!=c&&d.nodeType;)g+=d.offsetLeft||0,h+=d.offsetTop||0,d=d.offsetParent;for(d=b.parentNode;d&&d!=c&&d.nodeType;)g-=d.scrollLeft||0,h-=d.scrollTop||0,d=d.parentNode}return{x:g,y:h}},parseStyle:function(a){return this.styles.parse(a)},serializeStyle:function(a,b){return this.styles.serialize(a,b)},addStyle:function(a){var b,c,d=this,e=d.doc;if(d!==o.DOM&&e===document){var f=o.DOM.addedStyles;if(f=f||[],f[a])return;f[a]=!0,o.DOM.addedStyles=f}c=e.getElementById("mceDefaultStyles"),c||(c=e.createElement("style"),c.id="mceDefaultStyles",c.type="text/css",b=e.getElementsByTagName("head")[0],b.firstChild?b.insertBefore(c,b.firstChild):b.appendChild(c)),c.styleSheet?c.styleSheet.cssText+=a:c.appendChild(e.createTextNode(a))},loadCSS:function(a){var b,c=this,d=c.doc;return c!==o.DOM&&d===document?void o.DOM.loadCSS(a):(a||(a=""),b=d.getElementsByTagName("head")[0],void p(a.split(","),function(a){var e;a=k._addCacheSuffix(a),c.files[a]||(c.files[a]=!0,e=c.create("link",{rel:"stylesheet",href:a}),t&&d.documentMode&&d.recalc&&(e.onload=function(){d.recalc&&d.recalc(),e.onload=null}),b.appendChild(e))}))},addClass:function(a,b){this.$$(a).addClass(b)},removeClass:function(a,b){this.toggleClass(a,b,!1)},hasClass:function(a,b){return this.$$(a).hasClass(b)},toggleClass:function(b,c,d){this.$$(b).toggleClass(c,d).each(function(){""===this.className&&a(this).attr("class",null)})},show:function(a){this.$$(a).show()},hide:function(a){this.$$(a).hide()},isHidden:function(a){return"none"==this.$$(a).css("display")},uniqueId:function(a){return(a?a:"mce_")+this.counter++},setHTML:function(b,c){b=this.$$(b),t?b.each(function(b,d){if(d.canHaveHTML!==!1){for(;d.firstChild;)d.removeChild(d.firstChild);try{d.innerHTML="<br>"+c,d.removeChild(d.firstChild)}catch(b){a("<div></div>").html("<br>"+c).contents().slice(1).appendTo(d)}return c}}):b.html(c)},getOuterHTML:function(b){return b=this.get(b),1==b.nodeType&&"outerHTML"in b?b.outerHTML:a("<div></div>").append(a(b).clone()).html()},setOuterHTML:function(b,c){var d=this;d.$$(b).each(function(){try{if("outerHTML"in this)return void(this.outerHTML=c)}catch(a){}d.remove(a(this).html(c),!0)})},decode:h.decode,encode:h.encodeAllRaw,insertAfter:function(a,b){return b=this.get(b),this.run(a,function(a){var c,d;return c=b.parentNode,d=b.nextSibling,d?c.insertBefore(a,d):c.appendChild(a),a})},replace:function(a,b,c){var d=this;return d.run(b,function(b){return q(b,"array")&&(a=a.cloneNode(!0)),c&&p(r(b.childNodes),function(b){a.appendChild(b)}),b.parentNode.replaceChild(a,b)})},rename:function(a,b){var c,d=this;return a.nodeName!=b.toUpperCase()&&(c=d.create(b),p(d.getAttribs(a),function(b){d.setAttrib(c,b.nodeName,d.getAttrib(a,b.nodeName))}),d.replace(c,a,1)),c||a},findCommonAncestor:function(a,b){for(var c,d=a;d;){for(c=b;c&&d!=c;)c=c.parentNode;if(d==c)break;d=d.parentNode}return!d&&a.ownerDocument?a.ownerDocument.documentElement:d},toHex:function(a){return this.styles.toHex(k.trim(a))},run:function(a,b,c){var d,e=this;return"string"==typeof a&&(a=e.get(a)),!!a&&(c=c||this,a.nodeType||!a.length&&0!==a.length?b.call(c,a):(d=[],p(a,function(a,f){a&&("string"==typeof a&&(a=e.get(a)),d.push(b.call(c,a,f)))}),d))},getAttribs:function(a){var b;if(a=this.get(a),!a)return[];if(t){if(b=[],"OBJECT"==a.nodeName)return a.attributes;"OPTION"===a.nodeName&&this.getAttrib(a,"selected")&&b.push({specified:1,nodeName:"selected"});var c=/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;return a.cloneNode(!1).outerHTML.replace(c,"").replace(/[\w:\-]+/gi,function(a){b.push({specified:1,nodeName:a})}),b}return a.attributes},isEmpty:function(a,b){var c,d,e,g,h,i,j=this,k=0;if(a=a.firstChild){h=new f(a,a.parentNode),b=b||(j.schema?j.schema.getNonEmptyElements():null),g=j.schema?j.schema.getWhiteSpaceElements():{};do{if(e=a.nodeType,1===e){var l=a.getAttribute("data-mce-bogus");if(l){a=h.next("all"===l);continue}if(i=a.nodeName.toLowerCase(),b&&b[i]){if("br"===i){k++,a=h.next();continue}return!1}for(d=j.getAttribs(a),c=d.length;c--;)if(i=d[c].nodeName,"name"===i||"data-mce-bookmark"===i)return!1}if(8==e)return!1;if(3===e&&!v.test(a.nodeValue))return!1;if(3===e&&a.parentNode&&g[a.parentNode.nodeName]&&v.test(a.nodeValue))return!1;a=h.next()}while(a)}return k<=1},createRng:function(){var a=this.doc;return a.createRange?a.createRange():new c(this)},nodeIndex:n,split:function(a,b,c){function d(a){function b(a){var b=a.previousSibling&&"SPAN"==a.previousSibling.nodeName,c=a.nextSibling&&"SPAN"==a.nextSibling.nodeName;return b&&c}var c,e=a.childNodes,f=a.nodeType;if(1!=f||"bookmark"!=a.getAttribute("data-mce-type")){for(c=e.length-1;c>=0;c--)d(e[c]);if(9!=f){if(3==f&&a.nodeValue.length>0){var g=s(a.nodeValue).length;if(!h.isBlock(a.parentNode)||g>0||0===g&&b(a))return}else if(1==f&&(e=a.childNodes,1==e.length&&e[0]&&1==e[0].nodeType&&"bookmark"==e[0].getAttribute("data-mce-type")&&a.parentNode.insertBefore(e[0],a),e.length||/^(br|hr|input|img)$/i.test(a.nodeName)))return;h.remove(a)}return a}}var e,f,g,h=this,i=h.createRng();if(a&&b)return i.setStart(a.parentNode,h.nodeIndex(a)),i.setEnd(b.parentNode,h.nodeIndex(b)),e=i.extractContents(),i=h.createRng(),i.setStart(b.parentNode,h.nodeIndex(b)+1),i.setEnd(a.parentNode,h.nodeIndex(a)+1),f=i.extractContents(),g=a.parentNode,g.insertBefore(d(e),a),c?g.insertBefore(c,a):g.insertBefore(b,a),g.insertBefore(d(f),a),h.remove(a),c||b},bind:function(a,b,c,d){var e=this;if(k.isArray(a)){for(var f=a.length;f--;)a[f]=e.bind(a[f],b,c,d);return a}return!e.settings.collect||a!==e.doc&&a!==e.win||e.boundEvents.push([a,b,c,d]),e.events.bind(a,b,c,d||e)},unbind:function(a,b,c){var d,e=this;if(k.isArray(a)){for(d=a.length;d--;)a[d]=e.unbind(a[d],b,c);return a}if(e.boundEvents&&(a===e.doc||a===e.win))for(d=e.boundEvents.length;d--;){var f=e.boundEvents[d];a!=f[0]||b&&b!=f[1]||c&&c!=f[2]||this.events.unbind(f[0],f[1],f[2])}return this.events.unbind(a,b,c)},fire:function(a,b,c){return this.events.fire(a,b,c)},getContentEditable:function(a){var b;return a&&1==a.nodeType?(b=a.getAttribute("data-mce-contenteditable"),b&&"inherit"!==b?b:"inherit"!==a.contentEditable?a.contentEditable:null):null},getContentEditableParent:function(a){for(var b=this.getRoot(),c=null;a&&a!==b&&(c=this.getContentEditable(a),null===c);a=a.parentNode);return c},destroy:function(){var a=this;if(a.boundEvents){for(var b=a.boundEvents.length;b--;){var c=a.boundEvents[b];this.events.unbind(c[0],c[1],c[2])}a.boundEvents=null}d.setDocument&&d.setDocument(),a.win=a.doc=a.root=a.events=a.frag=null},isChildOf:function(a,b){for(;a;){if(b===a)return!0;a=a.parentNode}return!1},dumpRng:function(a){return"startContainer: "+a.startContainer.nodeName+", startOffset: "+a.startOffset+", endContainer: "+a.endContainer.nodeName+", endOffset: "+a.endOffset},_findSib:function(a,b,c){var d=this,e=b;if(a)for("string"==typeof e&&(e=function(a){return d.is(a,b)}),a=a[c];a;a=a[c])if(e(a))return a;return null}},o.DOM=new o(document),o.nodeIndex=n,o}),g("f",["e","9"],function(a,b){function c(){function a(a,c,e){function f(){k.remove(j),i&&(i.onreadystatechange=i.onload=i=null),c()}function h(){g(e)?e():"undefined"!=typeof console&&console.log&&console.log("Failed to load script: "+a)}var i,j,k=d;j=k.uniqueId(),i=document.createElement("script"),i.id=j,i.type="text/javascript",i.src=b._addCacheSuffix(a),"onreadystatechange"in i?i.onreadystatechange=function(){/loaded|complete/.test(i.readyState)&&f()}:i.onload=f,i.onerror=h,(document.getElementsByTagName("head")[0]||document.body).appendChild(i)}var c,h=0,i=1,j=2,k=3,l={},m=[],n={},o=[],p=0;this.isDone=function(a){return l[a]==j},this.markDone=function(a){l[a]=j},this.add=this.load=function(a,b,d,e){var f=l[a];f==c&&(m.push(a),l[a]=h),b&&(n[a]||(n[a]=[]),n[a].push({success:b,failure:e,scope:d||this}))},this.remove=function(a){delete l[a],delete n[a]},this.loadQueue=function(a,b,c){this.loadScripts(m,a,b,c)},this.loadScripts=function(b,d,h,m){function q(a,b){e(n[b],function(b){g(b[a])&&b[a].call(b.scope)}),n[b]=c}var r,s=[];o.push({success:d,failure:m,scope:h||this}),(r=function(){var c=f(b);b.length=0,e(c,function(b){return l[b]===j?void q("success",b):l[b]===k?void q("failure",b):void(l[b]!==i&&(l[b]=i,p++,a(b,function(){l[b]=j,p--,q("success",b),r()},function(){l[b]=k,p--,s.push(b),q("failure",b),r()})))}),p||(e(o,function(a){0===s.length?g(a.success)&&a.success.call(a.scope):g(a.failure)&&a.failure.call(a.scope,s)}),o.length=0)})()}}var d=a.DOM,e=b.each,f=b.grep,g=function(a){return"function"==typeof a};return c.ScriptLoader=new c,c}),g("g",["f","9"],function(a,b){function c(){var a=this;a.items=[],a.urls={},a.lookup={}}var d=b.each;return c.prototype={get:function(a){if(this.lookup[a])return this.lookup[a].instance},dependencies:function(a){var b;return this.lookup[a]&&(b=this.lookup[a].dependencies),b||[]},requireLangPack:function(b,d){var e=c.language;if(e&&c.languageLoad!==!1){if(d)if(d=","+d+",",d.indexOf(","+e.substr(0,2)+",")!=-1)e=e.substr(0,2);else if(d.indexOf(","+e+",")==-1)return;a.ScriptLoader.add(this.urls[b]+"/langs/"+e+".js")}},add:function(a,b,c){return this.items.push(b),this.lookup[a]={instance:b,dependencies:c},b},remove:function(a){delete this.urls[a],delete this.lookup[a]},createUrl:function(a,b){return"object"==typeof b?b:{prefix:a.prefix,resource:b,suffix:a.suffix}},addComponents:function(b,c){var e=this.urls[b];d(c,function(b){a.ScriptLoader.add(e+"/"+b)})},load:function(b,e,f,g,h){function i(){var c=j.dependencies(b);d(c,function(a){var b=j.createUrl(e,a);j.load(b.resource,b,void 0,void 0)}),f&&(g?f.call(g):f.call(a))}var j=this,k=e;j.urls[b]||("object"==typeof e&&(k=e.prefix+e.resource+e.suffix),0!==k.indexOf("/")&&k.indexOf("://")==-1&&(k=c.baseURL+"/"+k),j.urls[b]=k.substring(0,k.lastIndexOf("/")),j.lookup[b]?i():a.ScriptLoader.add(k,i,g,h))}},c.PluginManager=new c,c.ThemeManager=new c,c}),g("1j",[],function(){function a(a){return function(b){return!!b&&b.nodeType==a}}function b(a){return a=a.toLowerCase().split(" "),function(b){var c,d;if(b&&b.nodeType)for(d=b.nodeName.toLowerCase(),c=0;c<a.length;c++)if(d===a[c])return!0;return!1}}function c(a,b){return b=b.toLowerCase().split(" "),function(c){var d,e;if(i(c))for(d=0;d<b.length;d++)if(e=c.ownerDocument.defaultView.getComputedStyle(c,null).getPropertyValue(a),e===b[d])return!0;return!1}}function d(a,b){return function(c){return i(c)&&c[a]===b}}function e(a,b){return function(b){return i(b)&&b.hasAttribute(a)}}function f(a,b){return function(c){return i(c)&&c.getAttribute(a)===b}}function g(a){return i(a)&&a.hasAttribute("data-mce-bogus")}function h(a){return function(b){if(i(b)){if(b.contentEditable===a)return!0;if(b.getAttribute("data-mce-contenteditable")===a)return!0}return!1}}var i=a(1);return{isText:a(3),isElement:i,isComment:a(8),isBr:b("br"),isContentEditableTrue:h("true"),isContentEditableFalse:h("false"),matchNodeNames:b,hasPropValue:d,hasAttribute:e,hasAttributeValue:f,matchStyleValues:c,isBogus:g}}),g("1l",[],function(){var a="\ufeff",b=function(b){return b===a},c=function(b){return b.replace(new RegExp(a,"g"),"")};return{isZwsp:b,ZWSP:a,trim:c}}),g("1k",["1j","1l"],function(a,b){function c(a){return n(a)&&(a=a.parentNode),m(a)&&a.hasAttribute("data-mce-caret")}function d(a){return n(a)&&b.isZwsp(a.data)}function e(a){return c(a)||d(a)}function f(a,c){var d,f,g,h;if(d=a.ownerDocument,g=d.createTextNode(b.ZWSP),h=a.parentNode,c){if(f=a.previousSibling,n(f)){if(e(f))return f;if(j(f))return f.splitText(f.data.length-1)}h.insertBefore(g,a)}else{if(f=a.nextSibling,n(f)){if(e(f))return f;if(i(f))return f.splitText(1),f}a.nextSibling?h.insertBefore(g,a.nextSibling):h.appendChild(g)}return g}function g(){var a=document.createElement("br");return a.setAttribute("data-mce-bogus","1"),a}function h(a,b,c){var d,e,f;return d=b.ownerDocument,e=d.createElement(a),e.setAttribute("data-mce-caret",c?"before":"after"),e.setAttribute("data-mce-bogus","all"),e.appendChild(g()),f=b.parentNode,c?f.insertBefore(e,b):b.nextSibling?f.insertBefore(e,b.nextSibling):f.appendChild(e),e}function i(a){return n(a)&&a.data[0]==b.ZWSP}function j(a){return n(a)&&a.data[a.data.length-1]==b.ZWSP}function k(b){var c=b.getElementsByTagName("br"),d=c[c.length-1];a.isBogus(d)&&d.parentNode.removeChild(d)}function l(a){return a&&a.hasAttribute("data-mce-caret")?(k(a),a.removeAttribute("data-mce-caret"),a.removeAttribute("data-mce-bogus"),a.removeAttribute("style"),a.removeAttribute("_moz_abspos"),a):null}var m=a.isElement,n=a.isText,o=function(b){return b.firstChild!==b.lastChild||!a.isBr(b.firstChild)},p=function(c){if(a.isText(c)){var d=c.data;return d.length>0&&d.charAt(0)!==b.ZWSP&&c.insertData(0,b.ZWSP),c}return null},q=function(c){if(a.isText(c)){var d=c.data;return d.length>0&&d.charAt(d.length-1)!==b.ZWSP&&c.insertData(d.length,b.ZWSP),c}return null},r=function(c){return c&&a.isText(c.container())&&c.container().data.charAt(c.offset())===b.ZWSP},s=function(c){return c&&a.isText(c.container())&&c.container().data.charAt(c.offset()-1)===b.ZWSP};return{isCaretContainer:e,isCaretContainerBlock:c,isCaretContainerInline:d,showCaretContainerBlock:l,insertInline:f,prependInline:p,appendInline:q,isBeforeInline:r, -isAfterInline:s,insertBlock:h,hasContent:o,startsWithCaretContainer:i,endsWithCaretContainer:j}}),g("h",["9","c","1j","1h","1k"],function(a,b,c,d,e){function f(a){return q(a)||r(a)}function g(a,b){var c=a.childNodes;return b--,b>c.length-1?b=c.length-1:b<0&&(b=0),c[b]||a}function h(a,b,c){for(;a&&a!==b;){if(c(a))return a;a=a.parentNode}return null}function i(a,b,c){return null!==h(a,b,c)}function j(a,b,c){return i(a,b,function(a){return a.nodeName===c})}function k(a){return"_mce_caret"===a.id}function l(a,b){return s(a)&&i(a,b,k)===!1}function m(a){this.walk=function(b,c){function d(a){var b;return b=a[0],3===b.nodeType&&b===q&&r>=b.nodeValue.length&&a.splice(0,1),b=a[a.length-1],0===t&&a.length>0&&b===s&&3===b.nodeType&&a.splice(a.length-1,1),a}function e(a,b,c){for(var d=[];a&&a!=c;a=a[b])d.push(a);return d}function f(a,b){do{if(a.parentNode==b)return a;a=a.parentNode}while(a)}function h(a,b,f){var g=f?"nextSibling":"previousSibling";for(l=a,m=l.parentNode;l&&l!=b;l=m)m=l.parentNode,n=e(l==a?l:l[g],g),n.length&&(f||n.reverse(),c(d(n)))}var i,j,k,l,m,n,o,q=b.startContainer,r=b.startOffset,s=b.endContainer,t=b.endOffset;if(o=a.select("td[data-mce-selected],th[data-mce-selected]"),o.length>0)return void p(o,function(a){c([a])});if(1==q.nodeType&&q.hasChildNodes()&&(q=q.childNodes[r]),1==s.nodeType&&s.hasChildNodes()&&(s=g(s,t)),q==s)return c(d([q]));for(i=a.findCommonAncestor(q,s),l=q;l;l=l.parentNode){if(l===s)return h(q,i,!0);if(l===i)break}for(l=s;l;l=l.parentNode){if(l===q)return h(s,i);if(l===i)break}j=f(q,i)||q,k=f(s,i)||s,h(q,j,!0),n=e(j==q?j:j.nextSibling,"nextSibling",k==s?k.nextSibling:k),n.length&&c(d(n)),h(s,k)},this.split=function(a){function b(a,b){return a.splitText(b)}var c=a.startContainer,d=a.startOffset,e=a.endContainer,f=a.endOffset;return c==e&&3==c.nodeType?d>0&&d<c.nodeValue.length&&(e=b(c,d),c=e.previousSibling,f>d?(f-=d,c=e=b(e,f).previousSibling,f=e.nodeValue.length,d=0):f=0):(3==c.nodeType&&d>0&&d<c.nodeValue.length&&(c=b(c,d),d=0),3==e.nodeType&&f>0&&f<e.nodeValue.length&&(e=b(e,f).previousSibling,f=e.nodeValue.length)),{startContainer:c,startOffset:d,endContainer:e,endOffset:f}},this.normalize=function(c){function d(d){function g(a){return a&&/^(TD|TH|CAPTION)$/.test(a.nodeName)}function h(c,d){for(var e=new b(c,a.getParent(c.parentNode,a.isBlock)||w);c=e[d?"prev":"next"]();)if("BR"===c.nodeName)return!0}function i(a){for(;a&&a!=w;){if(r(a))return!0;a=a.parentNode}return!1}function k(a,b){return a.previousSibling&&a.previousSibling.nodeName==b}function m(c,d){var g,h,i;if(d=d||n,i=a.getParent(d.parentNode,a.isBlock)||w,c&&"BR"==d.nodeName&&v&&a.isEmpty(i))return n=d.parentNode,o=a.nodeIndex(d),void(f=!0);for(g=new b(d,i);q=g[c?"prev":"next"]();){if("false"===a.getContentEditableParent(q)||l(q,a.getRoot()))return;if(3===q.nodeType&&q.nodeValue.length>0)return void(j(q,w,"A")===!1&&(n=q,o=c?q.nodeValue.length:0,f=!0));if(a.isBlock(q)||t[q.nodeName.toLowerCase()])return;h=q}e&&h&&(n=h,f=!0,o=0)}var n,o,p,q,t,u,v,w=a.getRoot();if(n=c[(d?"start":"end")+"Container"],o=c[(d?"start":"end")+"Offset"],v=1==n.nodeType&&o===n.childNodes.length,t=a.schema.getNonEmptyElements(),u=d,!s(n)){if(1==n.nodeType&&o>n.childNodes.length-1&&(u=!1),9===n.nodeType&&(n=a.getRoot(),o=0),n===w){if(u&&(q=n.childNodes[o>0?o-1:0])){if(s(q))return;if(t[q.nodeName]||"TABLE"==q.nodeName)return}if(n.hasChildNodes()){if(o=Math.min(!u&&o>0?o-1:o,n.childNodes.length-1),n=n.childNodes[o],o=0,!e&&n===w.lastChild&&"TABLE"===n.nodeName)return;if(i(n)||s(n))return;if(n.hasChildNodes()&&!/TABLE/.test(n.nodeName)){q=n,p=new b(n,w);do{if(r(q)||s(q)){f=!1;break}if(3===q.nodeType&&q.nodeValue.length>0){o=u?0:q.nodeValue.length,n=q,f=!0;break}if(t[q.nodeName.toLowerCase()]&&!g(q)){o=a.nodeIndex(q),n=q.parentNode,"IMG"!=q.nodeName||u||o++,f=!0;break}}while(q=u?p.next():p.prev())}}}e&&(3===n.nodeType&&0===o&&m(!0),1===n.nodeType&&(q=n.childNodes[o],q||(q=n.childNodes[o-1]),!q||"BR"!==q.nodeName||k(q,"A")||h(q)||h(q,!0)||m(!0,q))),u&&!e&&3===n.nodeType&&o===n.nodeValue.length&&m(!1),f&&c["set"+(d?"Start":"End")](n,o)}}var e,f=!1;return e=c.collapsed,d(!0),e||d(),f&&e&&c.collapse(!0),f}}function n(b,c,d){var e,f,g;if(e=d.elementFromPoint(b,c),f=d.body.createTextRange(),e&&"HTML"!=e.tagName||(e=d.body),f.moveToElementText(e),g=a.toArray(f.getClientRects()),g=g.sort(function(a,b){return a=Math.abs(Math.max(a.top-c,a.bottom-c)),b=Math.abs(Math.max(b.top-c,b.bottom-c)),a-b}),g.length>0){c=(g[0].bottom+g[0].top)/2;try{return f.moveToPoint(b,c),f.collapse(!0),f}catch(a){}}return null}function o(a,b){var c=a&&a.parentElement?a.parentElement():null;return r(h(c,b,f))?null:a}var p=a.each,q=c.isContentEditableTrue,r=c.isContentEditableFalse,s=e.isCaretContainer;return m.compareRanges=function(a,b){if(a&&b){if(!a.item&&!a.duplicate)return a.startContainer==b.startContainer&&a.startOffset==b.startOffset;if(a.item&&b.item&&a.item(0)===b.item(0))return!0;if(a.isEqual&&b.isEqual&&b.isEqual(a))return!0}return!1},m.getCaretRangeFromPoint=function(a,b,c){var d,e;if(c.caretPositionFromPoint)e=c.caretPositionFromPoint(a,b),d=c.createRange(),d.setStart(e.offsetNode,e.offset),d.collapse(!0);else if(c.caretRangeFromPoint)d=c.caretRangeFromPoint(a,b);else if(c.body.createTextRange){d=c.body.createTextRange();try{d.moveToPoint(a,b),d.collapse(!0)}catch(e){d=n(a,b,c)}return o(d,c.body)}return d},m.getSelectedNode=function(a){var b=a.startContainer,c=a.startOffset;return b.hasChildNodes()&&a.endOffset==c+1?b.childNodes[c]:null},m.getNode=function(a,b){return 1==a.nodeType&&a.hasChildNodes()&&(b>=a.childNodes.length&&(b=a.childNodes.length-1),a=a.childNodes[b]),a},m}),g("i",[],function(){function a(a,b,c){var d,e,f=c?"lastChild":"firstChild",g=c?"prev":"next";if(a[f])return a[f];if(a!==b){if(d=a[g])return d;for(e=a.parent;e&&e!==b;e=e.parent)if(d=e[g])return d}}function b(a,b){this.name=a,this.type=b,1===b&&(this.attributes=[],this.attributes.map={})}var c=/^[ \t\r\n]*$/,d={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};return b.prototype={replace:function(a){var b=this;return a.parent&&a.remove(),b.insert(a,b),b.remove(),b},attr:function(a,b){var c,d,e,f=this;if("string"!=typeof a){for(d in a)f.attr(d,a[d]);return f}if(c=f.attributes){if(b!==e){if(null===b){if(a in c.map)for(delete c.map[a],d=c.length;d--;)if(c[d].name===a)return c=c.splice(d,1),f;return f}if(a in c.map){for(d=c.length;d--;)if(c[d].name===a){c[d].value=b;break}}else c.push({name:a,value:b});return c.map[a]=b,f}return c.map[a]}},clone:function(){var a,c,d,e,f,g=this,h=new b(g.name,g.type);if(d=g.attributes){for(f=[],f.map={},a=0,c=d.length;a<c;a++)e=d[a],"id"!==e.name&&(f[f.length]={name:e.name,value:e.value},f.map[e.name]=e.value);h.attributes=f}return h.value=g.value,h.shortEnded=g.shortEnded,h},wrap:function(a){var b=this;return b.parent.insert(a,b),a.append(b),b},unwrap:function(){var a,b,c=this;for(a=c.firstChild;a;)b=a.next,c.insert(a,c,!0),a=b;c.remove()},remove:function(){var a=this,b=a.parent,c=a.next,d=a.prev;return b&&(b.firstChild===a?(b.firstChild=c,c&&(c.prev=null)):d.next=c,b.lastChild===a?(b.lastChild=d,d&&(d.next=null)):c.prev=d,a.parent=a.next=a.prev=null),a},append:function(a){var b,c=this;return a.parent&&a.remove(),b=c.lastChild,b?(b.next=a,a.prev=b,c.lastChild=a):c.lastChild=c.firstChild=a,a.parent=c,a},insert:function(a,b,c){var d;return a.parent&&a.remove(),d=b.parent||this,c?(b===d.firstChild?d.firstChild=a:b.prev.next=a,a.prev=b.prev,a.next=b,b.prev=a):(b===d.lastChild?d.lastChild=a:b.next.prev=a,a.next=b.next,a.prev=b,b.next=a),a.parent=d,a},getAll:function(b){var c,d=this,e=[];for(c=d.firstChild;c;c=a(c,d))c.name===b&&e.push(c);return e},empty:function(){var b,c,d,e=this;if(e.firstChild){for(b=[],d=e.firstChild;d;d=a(d,e))b.push(d);for(c=b.length;c--;)d=b[c],d.parent=d.firstChild=d.lastChild=d.next=d.prev=null}return e.firstChild=e.lastChild=null,e},isEmpty:function(b,d){var e,f,g=this,h=g.firstChild;if(d=d||{},h)do{if(1===h.type){if(h.attributes.map["data-mce-bogus"])continue;if(b[h.name])return!1;for(e=h.attributes.length;e--;)if(f=h.attributes[e].name,"name"===f||0===f.indexOf("data-mce-bookmark"))return!1}if(8===h.type)return!1;if(3===h.type&&!c.test(h.value))return!1;if(3===h.type&&h.parent&&d[h.parent.name]&&c.test(h.value))return!1}while(h=a(h,g));return!0},walk:function(b){return a(this,null,b)}},b.create=function(a,c){var e,f;if(e=new b(a,d[a]||1),c)for(f in c)e.attr(f,c[f]);return e},b}),g("k",["j","d","9"],function(a,b,c){function d(a,b,c){var d,e,f,g,h=1;for(g=a.getShortEndedElements(),f=/<([!?\/])?([A-Za-z0-9\-_\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g,f.lastIndex=d=c;e=f.exec(b);){if(d=f.lastIndex,"/"===e[1])h--;else if(!e[1]){if(e[2]in g)continue;h++}if(0===h)break}return d}function e(e,i){function j(){}var k=this;e=e||{},k.schema=i=i||new a,e.fix_self_closing!==!1&&(e.fix_self_closing=!0),f("comment cdata text start end pi doctype".split(" "),function(a){a&&(k[a]=e[a]||j)}),k.parse=function(a){function f(a){var b,c;for(b=Q.length;b--&&Q[b].name!==a;);if(b>=0){for(c=Q.length-1;c>=b;c--)a=Q[c],a.valid&&O.end(a.name);Q.length=b}}function j(a,b,c,d,f){var h,i,j=/[\s\u0000-\u001F]+/g;if(b=b.toLowerCase(),c=b in u?b:S(c||d||f||""),w&&!r&&g(b)===!1){if(h=B[b],!h&&C){for(i=C.length;i--&&(h=C[i],!h.pattern.test(b)););i===-1&&(h=null)}if(!h)return;if(h.validValues&&!(c in h.validValues))return}if(T[b]&&!e.allow_script_urls){var k=c.replace(j,"");try{k=decodeURIComponent(k)}catch(a){k=unescape(k)}if(U.test(k))return;if(!e.allow_html_data_urls&&V.test(k)&&!/^data:image\//i.test(k))return}r&&(b in T||0===b.indexOf("on"))||(n.map[b]=c,n.push({name:b,value:c}))}var k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O=this,P=0,Q=[],R=0,S=b.decode,T=c.makeMap("src,href,data,background,formaction,poster"),U=/((java|vb)script|mhtml):/i,V=/^data:/i;for(J=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)>)|(?:([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)((?:\\s+[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*|\\/|\\s+)>))","g"),K=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g,t=i.getShortEndedElements(),I=e.self_closing_elements||i.getSelfClosingElements(),u=i.getBoolAttrs(),w=e.validate,s=e.remove_internals,N=e.fix_self_closing,L=i.getSpecialElements(),G=a+">";k=J.exec(G);){if(P<k.index&&O.text(S(a.substr(P,k.index-P))),l=k[6])l=l.toLowerCase(),":"===l.charAt(0)&&(l=l.substr(1)),f(l);else if(l=k[7]){if(k.index+k[0].length>a.length){O.text(S(a.substr(k.index))),P=k.index+k[0].length;continue}if(l=l.toLowerCase(),":"===l.charAt(0)&&(l=l.substr(1)),v=l in t,N&&I[l]&&Q.length>0&&Q[Q.length-1].name===l&&f(l),!w||(x=i.getElementRule(l))){if(y=!0,w&&(B=x.attributes,C=x.attributePatterns),(A=k[8])?(r=A.indexOf("data-mce-type")!==-1,r&&s&&(y=!1),n=[],n.map={},A.replace(K,j)):(n=[],n.map={}),w&&!r){if(D=x.attributesRequired,E=x.attributesDefault,F=x.attributesForced,H=x.removeEmptyAttrs,H&&!n.length&&(y=!1),F)for(o=F.length;o--;)z=F[o],q=z.name,M=z.value,"{$uid}"===M&&(M="mce_"+R++),n.map[q]=M,n.push({name:q,value:M});if(E)for(o=E.length;o--;)z=E[o],q=z.name,q in n.map||(M=z.value,"{$uid}"===M&&(M="mce_"+R++),n.map[q]=M,n.push({name:q,value:M}));if(D){for(o=D.length;o--&&!(D[o]in n.map););o===-1&&(y=!1)}if(z=n.map["data-mce-bogus"]){if("all"===z){P=d(i,a,J.lastIndex),J.lastIndex=P;continue}y=!1}}y&&O.start(l,n,v)}else y=!1;if(m=L[l]){m.lastIndex=P=k.index+k[0].length,(k=m.exec(a))?(y&&(p=a.substr(P,k.index-P)),P=k.index+k[0].length):(p=a.substr(P),P=a.length),y&&(p.length>0&&O.text(p,!0),O.end(l)),J.lastIndex=P;continue}v||(A&&A.indexOf("/")==A.length-1?y&&O.end(l):Q.push({name:l,valid:y}))}else(l=k[1])?(">"===l.charAt(0)&&(l=" "+l),e.allow_conditional_comments||"[if"!==l.substr(0,3).toLowerCase()||(l=" "+l),O.comment(l)):(l=k[2])?O.cdata(h(l)):(l=k[3])?O.doctype(l):(l=k[4])&&O.pi(l,k[5]);P=k.index+k[0].length}for(P<a.length&&O.text(S(a.substr(P))),o=Q.length-1;o>=0;o--)l=Q[o],l.valid&&O.end(l.name)}}var f=c.each,g=function(a){return 0===a.indexOf("data-")||0===a.indexOf("aria-")},h=function(a){return a.replace(/<!--|-->/g,"")};return e.findEndTag=d,e}),g("l",["i","j","k","9"],function(a,b,c,d){var e=d.makeMap,f=d.each,g=d.explode,h=d.extend,i=function(b,c){b.padd_empty_with_br?c.empty().append(new a("br","1")).shortEnded=!0:c.empty().append(new a("#text","3")).value="\xa0"},j=function(a,b){return a&&a.firstChild===a.lastChild&&a.firstChild.name===b};return function(k,l){function m(b){var c,d,f,g,h,i,k,m,o,p,q,r,s,t,u,v;for(r=e("tr,td,th,tbody,thead,tfoot,table"),p=l.getNonEmptyElements(),q=l.getWhiteSpaceElements(),s=l.getTextBlockElements(),t=l.getSpecialElements(),c=0;c<b.length;c++)if(d=b[c],d.parent&&!d.fixed)if(s[d.name]&&"li"==d.parent.name){for(u=d.next;u&&s[u.name];)u.name="li",u.fixed=!0,d.parent.insert(u,d.parent),u=u.next;d.unwrap(d)}else{for(g=[d],f=d.parent;f&&!l.isValidChild(f.name,d.name)&&!r[f.name];f=f.parent)g.push(f);if(f&&g.length>1){for(g.reverse(),h=i=n.filterNode(g[0].clone()),o=0;o<g.length-1;o++){for(l.isValidChild(i.name,g[o].name)?(k=n.filterNode(g[o].clone()),i.append(k)):k=i,m=g[o].firstChild;m&&m!=g[o+1];)v=m.next,k.append(m),m=v;i=k}h.isEmpty(p,q)?f.insert(d,g[0],!0):(f.insert(h,g[0],!0),f.insert(d,h)),f=g[0],(f.isEmpty(p,q)||j(f,"br"))&&f.empty().remove()}else if(d.parent){if("li"===d.name){if(u=d.prev,u&&("ul"===u.name||"ul"===u.name)){u.append(d);continue}if(u=d.next,u&&("ul"===u.name||"ul"===u.name)){u.insert(d,u.firstChild,!0);continue}d.wrap(n.filterNode(new a("ul",1)));continue}l.isValidChild(d.parent.name,"div")&&l.isValidChild("div",d.name)?d.wrap(n.filterNode(new a("div",1))):t[d.name]?d.empty().remove():d.unwrap()}}}var n=this,o={},p=[],q={},r={};k=k||{},k.validate=!("validate"in k)||k.validate,k.root_name=k.root_name||"body",n.schema=l=l||new b,n.filterNode=function(a){var b,c,d;c in o&&(d=q[c],d?d.push(a):q[c]=[a]),b=p.length;for(;b--;)c=p[b].name,c in a.attributes.map&&(d=r[c],d?d.push(a):r[c]=[a]);return a},n.addNodeFilter=function(a,b){f(g(a),function(a){var c=o[a];c||(o[a]=c=[]),c.push(b)})},n.addAttributeFilter=function(a,b){f(g(a),function(a){var c;for(c=0;c<p.length;c++)if(p[c].name===a)return void p[c].callbacks.push(b);p.push({name:a,callbacks:[b]})})},n.parse=function(b,d){function f(){function a(a){a&&(d=a.firstChild,d&&3==d.type&&(d.value=d.value.replace(E,"")),d=a.lastChild,d&&3==d.type&&(d.value=d.value.replace(G,"")))}var b,c,d=t.firstChild;if(l.isValidChild(t.name,M.toLowerCase())){for(;d;)b=d.next,3==d.type||1==d.type&&"p"!==d.name&&!D[d.name]&&!d.attr("data-mce-type")?c?c.append(d):(c=g(M,1),c.attr(k.forced_root_block_attrs),t.insert(c,d),c.append(d)):(a(c),c=null),d=b;a(c)}}function g(b,c){var d,e=new a(b,c);return b in o&&(d=q[b],d?d.push(e):q[b]=[e]),e}function j(a){var b,c,d,e,f=l.getBlockElements();for(b=a.prev;b&&3===b.type;){if(d=b.value.replace(G,""),d.length>0)return void(b.value=d);if(c=b.next){if(3==c.type&&c.value.length){b=b.prev;continue}if(!f[c.name]&&"script"!=c.name&&"style"!=c.name){b=b.prev;continue}}e=b.prev,b.remove(),b=e}}function n(a){var b,c={};for(b in a)"li"!==b&&"p"!=b&&(c[b]=a[b]);return c}var s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N=[];if(d=d||{},q={},r={},D=h(e("script,style,head,html,body,title,meta,param"),l.getBlockElements()),L=l.getNonEmptyElements(),K=l.children,C=k.validate,M="forced_root_block"in d?d.forced_root_block:k.forced_root_block,J=l.getWhiteSpaceElements(),E=/^[ \t\r\n]+/,G=/[ \t\r\n]+$/,H=/[ \t\r\n]+/g,I=/^[ \t\r\n]+$/,s=new c({validate:C,allow_script_urls:k.allow_script_urls,allow_conditional_comments:k.allow_conditional_comments,self_closing_elements:n(l.getSelfClosingElements()),cdata:function(a){u.append(g("#cdata",4)).value=a},text:function(a,b){var c;F||(a=a.replace(H," "),u.lastChild&&D[u.lastChild.name]&&(a=a.replace(E,""))),0!==a.length&&(c=g("#text",3),c.raw=!!b,u.append(c).value=a)},comment:function(a){u.append(g("#comment",8)).value=a},pi:function(a,b){u.append(g(a,7)).value=b,j(u)},doctype:function(a){var b;b=u.append(g("#doctype",10)),b.value=a,j(u)},start:function(a,b,c){var d,e,f,h,i;if(f=C?l.getElementRule(a):{}){for(d=g(f.outputName||a,1),d.attributes=b,d.shortEnded=c,u.append(d),i=K[u.name],i&&K[d.name]&&!i[d.name]&&N.push(d),e=p.length;e--;)h=p[e].name,h in b.map&&(A=r[h],A?A.push(d):r[h]=[d]);D[a]&&j(d),c||(u=d),!F&&J[a]&&(F=!0)}},end:function(a){var b,c,d,e,f;if(c=C?l.getElementRule(a):{}){if(D[a]&&!F){if(b=u.firstChild,b&&3===b.type)if(d=b.value.replace(E,""),d.length>0)b.value=d,b=b.next;else for(e=b.next,b.remove(),b=e;b&&3===b.type;)d=b.value,e=b.next,(0===d.length||I.test(d))&&(b.remove(),b=e),b=e;if(b=u.lastChild,b&&3===b.type)if(d=b.value.replace(G,""),d.length>0)b.value=d,b=b.prev;else for(e=b.prev,b.remove(),b=e;b&&3===b.type;)d=b.value,e=b.prev,(0===d.length||I.test(d))&&(b.remove(),b=e),b=e}if(F&&J[a]&&(F=!1),(c.removeEmpty||c.paddEmpty)&&u.isEmpty(L,J))if(c.paddEmpty)i(k,u);else if(!u.attributes.map.name&&!u.attributes.map.id)return f=u.parent,D[u.name]?u.empty().remove():u.unwrap(),void(u=f);u=u.parent}}},l),t=u=new a(d.context||k.root_name,11),s.parse(b),C&&N.length&&(d.context?d.invalid=!0:m(N)),M&&("body"==t.name||d.isRootContent)&&f(),!d.invalid){for(B in q){for(A=o[B],v=q[B],y=v.length;y--;)v[y].parent||v.splice(y,1);for(w=0,x=A.length;w<x;w++)A[w](v,B,d)}for(w=0,x=p.length;w<x;w++)if(A=p[w],A.name in r){for(v=r[A.name],y=v.length;y--;)v[y].parent||v.splice(y,1);for(y=0,z=A.callbacks.length;y<z;y++)A.callbacks[y](v,A.name,d)}}return t},k.remove_trailing_brs&&n.addNodeFilter("br",function(b){var c,d,e,f,g,j,m,n,o=b.length,p=h({},l.getBlockElements()),q=l.getNonEmptyElements(),r=l.getNonEmptyElements();for(p.body=1,c=0;c<o;c++)if(d=b[c],e=d.parent,p[d.parent.name]&&d===e.lastChild){for(g=d.prev;g;){if(j=g.name,"span"!==j||"bookmark"!==g.attr("data-mce-type")){if("br"!==j)break;if("br"===j){d=null;break}}g=g.prev}d&&(d.remove(),e.isEmpty(q,r)&&(m=l.getElementRule(e.name),m&&(m.removeEmpty?e.remove():m.paddEmpty&&i(k,e))))}else{for(f=d;e&&e.firstChild===f&&e.lastChild===f&&(f=e,!p[e.name]);)e=e.parent;f===e&&k.padd_empty_with_br!==!0&&(n=new a("#text",3),n.value="\xa0",d.replace(n))}}),n.addAttributeFilter("href",function(a){var b,c=a.length,e=function(a){var b=a.split(" ").filter(function(a){return a.length>0});return b.concat(["noopener"]).sort().join(" ")},f=function(a){var b=a?d.trim(a):"";return/\b(noopener)\b/g.test(b)?b:e(b)};if(!k.allow_unsafe_link_target)for(;c--;)b=a[c],"a"===b.name&&"_blank"===b.attr("target")&&b.attr("rel",f(b.attr("rel")))}),k.allow_html_in_named_anchor||n.addAttributeFilter("id,name",function(a){for(var b,c,d,e,f=a.length;f--;)if(e=a[f],"a"===e.name&&e.firstChild&&!e.attr("href")){d=e.parent,b=e.lastChild;do c=b.prev,d.insert(b,e),b=c;while(b)}}),k.fix_list_elements&&n.addNodeFilter("ul,ol",function(b){for(var c,d,e=b.length;e--;)if(c=b[e],d=c.parent,"ul"===d.name||"ol"===d.name)if(c.prev&&"li"===c.prev.name)c.prev.append(c);else{var f=new a("li",1);f.attr("style","list-style-type: none"),c.wrap(f)}}),k.validate&&l.getValidClasses()&&n.addAttributeFilter("class",function(a){for(var b,c,d,e,f,g,h,i=a.length,j=l.getValidClasses();i--;){for(b=a[i],c=b.attr("class").split(" "),f="",d=0;d<c.length;d++)e=c[d],h=!1,g=j["*"],g&&g[e]&&(h=!0),g=j[b.name],!h&&g&&g[e]&&(h=!0),h&&(f&&(f+=" "),f+=e);f.length||(f=null),b.attr("class",f)}})}}),g("m",["d","9"],function(a,b){var c=b.makeMap;return function(b){var d,e,f,g,h,i=[];return b=b||{},d=b.indent,e=c(b.indent_before||""),f=c(b.indent_after||""),g=a.getEncodeFunc(b.entity_encoding||"raw",b.entities),h="html"==b.element_format,{start:function(a,b,c){var j,k,l,m;if(d&&e[a]&&i.length>0&&(m=i[i.length-1],m.length>0&&"\n"!==m&&i.push("\n")),i.push("<",a),b)for(j=0,k=b.length;j<k;j++)l=b[j],i.push(" ",l.name,'="',g(l.value,!0),'"');!c||h?i[i.length]=">":i[i.length]=" />",c&&d&&f[a]&&i.length>0&&(m=i[i.length-1],m.length>0&&"\n"!==m&&i.push("\n"))},end:function(a){var b;i.push("</",a,">"),d&&f[a]&&i.length>0&&(b=i[i.length-1],b.length>0&&"\n"!==b&&i.push("\n"))},text:function(a,b){a.length>0&&(i[i.length]=b?a:g(a))},cdata:function(a){i.push("<![CDATA[",a,"]]>")},comment:function(a){i.push("<!--",a,"-->")},pi:function(a,b){b?i.push("<?",a," ",g(b),"?>"):i.push("<?",a,"?>"),d&&i.push("\n")},doctype:function(a){i.push("<!DOCTYPE",a,">",d?"\n":"")},reset:function(){i.length=0},getContent:function(){return i.join("").replace(/\n$/,"")}}}}),g("n",["m","j"],function(a,b){return function(c,d){var e=this,f=new a(c);c=c||{},c.validate=!("validate"in c)||c.validate,e.schema=d=d||new b,e.writer=f,e.serialize=function(a){function b(a){var c,h,i,j,k,l,m,n,o,p=e[a.type];if(p)p(a);else{if(c=a.name,h=a.shortEnded,i=a.attributes,g&&i&&i.length>1&&(l=[],l.map={},o=d.getElementRule(a.name))){for(m=0,n=o.attributesOrder.length;m<n;m++)j=o.attributesOrder[m],j in i.map&&(k=i.map[j],l.map[j]=k,l.push({name:j,value:k}));for(m=0,n=i.length;m<n;m++)j=i[m].name,j in l.map||(k=i.map[j],l.map[j]=k,l.push({name:j,value:k}));i=l}if(f.start(a.name,i,h),!h){if(a=a.firstChild)do b(a);while(a=a.next);f.end(c)}}}var e,g;return g=c.validate,e={3:function(a){f.text(a.value,a.raw)},8:function(a){f.comment(a.value)},7:function(a){f.pi(a.name,a.value)},10:function(a){f.doctype(a.value)},4:function(a){f.cdata(a.value)},11:function(a){if(a=a.firstChild)do b(a);while(a=a.next)}},f.reset(),1!=a.type||c.inner?e[11](a):b(a),f.getContent()}}}),g("o",["e","l","k","d","n","i","j","6","9","1l"],function(a,b,c,d,e,f,g,h,i,j){function k(a){function b(a){return a&&"br"===a.name}var c,d;c=a.lastChild,b(c)&&(d=c.prev,b(d)&&(c.remove(),d.remove()))}var l=i.each,m=i.trim,n=a.DOM;return function(a,f){function o(a){var b=new RegExp(["<span[^>]+data-mce-bogus[^>]+>[\u200b\ufeff]+<\\/span>","\\s?("+v.join("|")+')="[^"]+"'].join("|"),"gi");return a=j.trim(a.replace(b,""))}function p(a){var b,d,e,g,h,i=a,j=/<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g,k=f.schema;for(i=o(i),h=k.getShortEndedElements();g=j.exec(i);)d=j.lastIndex,e=g[0].length,b=h[g[1]]?d:c.findEndTag(k,i,d),i=i.substring(0,d-e)+i.substring(b),j.lastIndex=d-e;return i}function q(){return p(f.getBody().innerHTML)}function r(a){i.inArray(v,a)===-1&&(u.addAttributeFilter(a,function(a,b){for(var c=a.length;c--;)a[c].attr(b,null)}),v.push(a))}var s,t,u,v=["data-mce-selected"];return f&&(s=f.dom,t=f.schema),s=s||n,t=t||new g(a),a.entity_encoding=a.entity_encoding||"named",a.remove_trailing_brs=!("remove_trailing_brs"in a)||a.remove_trailing_brs,u=new b(a,t),u.addAttributeFilter("data-mce-tabindex",function(a,b){for(var c,d=a.length;d--;)c=a[d],c.attr("tabindex",c.attributes.map["data-mce-tabindex"]),c.attr(b,null)}),u.addAttributeFilter("src,href,style",function(b,c){for(var d,e,f,g=b.length,h="data-mce-"+c,i=a.url_converter,j=a.url_converter_scope;g--;)d=b[g],e=d.attributes.map[h],e!==f?(d.attr(c,e.length>0?e:null),d.attr(h,null)):(e=d.attributes.map[c],"style"===c?e=s.serializeStyle(s.parseStyle(e),d.name):i&&(e=i.call(j,e,c,d.name)),d.attr(c,e.length>0?e:null))}),u.addAttributeFilter("class",function(a){for(var b,c,d=a.length;d--;)b=a[d],c=b.attr("class"),c&&(c=b.attr("class").replace(/(?:^|\s)mce-item-\w+(?!\S)/g,""),b.attr("class",c.length>0?c:null))}),u.addAttributeFilter("data-mce-type",function(a,b,c){for(var d,e=a.length;e--;)d=a[e],"bookmark"!==d.attributes.map["data-mce-type"]||c.cleanup||d.remove()}),u.addNodeFilter("noscript",function(a){for(var b,c=a.length;c--;)b=a[c].firstChild,b&&(b.value=d.decode(b.value))}),u.addNodeFilter("script,style",function(a,b){function c(a){return a.replace(/(<!--\[CDATA\[|\]\]-->)/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi,"").replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g,"")}for(var d,e,f,g=a.length;g--;)d=a[g],e=d.firstChild?d.firstChild.value:"","script"===b?(f=d.attr("type"),f&&d.attr("type","mce-no/type"==f?null:f.replace(/^mce\-/,"")),e.length>0&&(d.firstChild.value="// <![CDATA[\n"+c(e)+"\n// ]]>")):e.length>0&&(d.firstChild.value="<!--\n"+c(e)+"\n-->")}),u.addNodeFilter("#comment",function(a){for(var b,c=a.length;c--;)b=a[c],0===b.value.indexOf("[CDATA[")?(b.name="#cdata",b.type=4,b.value=b.value.replace(/^\[CDATA\[|\]\]$/g,"")):0===b.value.indexOf("mce:protected ")&&(b.name="#text",b.type=3,b.raw=!0,b.value=unescape(b.value).substr(14))}),u.addNodeFilter("xml:namespace,input",function(a,b){for(var c,d=a.length;d--;)c=a[d],7===c.type?c.remove():1===c.type&&("input"!==b||"type"in c.attributes.map||c.attr("type","text"))}),u.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style,data-mce-selected,data-mce-expando,data-mce-type,data-mce-resize",function(a,b){for(var c=a.length;c--;)a[c].attr(b,null)}),{schema:t,addNodeFilter:u.addNodeFilter,addAttributeFilter:u.addAttributeFilter,serialize:function(b,c){var d,f,g,i,n,o,p=this;return h.ie&&s.select("script,style,select,map").length>0?(n=b.innerHTML,b=b.cloneNode(!1),s.setHTML(b,n)):b=b.cloneNode(!0),d=document.implementation,d.createHTMLDocument&&(f=d.createHTMLDocument(""),l("BODY"==b.nodeName?b.childNodes:[b],function(a){f.body.appendChild(f.importNode(a,!0))}),b="BODY"!=b.nodeName?f.body.firstChild:f.body,g=s.doc,s.doc=f),c=c||{},c.format=c.format||"html",c.selection&&(c.forced_root_block=""),c.no_events||(c.node=b,p.onPreProcess(c)),o=u.parse(m(c.getInner?b.innerHTML:s.getOuterHTML(b)),c),k(o),i=new e(a,t),c.content=i.serialize(o),c.cleanup||(c.content=j.trim(c.content),c.content=c.content.replace(/\uFEFF/g,"")),c.no_events||p.onPostProcess(c),g&&(s.doc=g),c.node=null,c.content},addRules:function(a){t.addValidElements(a)},setRules:function(a){t.setValidElements(a)},onPreProcess:function(a){f&&f.fire("PreProcess",a)},onPostProcess:function(a){f&&f.fire("PostProcess",a)},addTempAttr:r,trimHtml:o,getTrimmedContent:q,trimContent:p}}}),g("p",["6"],function(a){return{BACKSPACE:8,DELETE:46,DOWN:40,ENTER:13,LEFT:37,RIGHT:39,SPACEBAR:32,TAB:9,UP:38,modifierPressed:function(a){return a.shiftKey||a.ctrlKey||a.altKey||this.metaKeyPressed(a)},metaKeyPressed:function(b){return a.mac?b.metaKey:b.ctrlKey&&!b.altKey}}}),g("43",[],function(){function a(a){return a?{left:k(a.left),top:k(a.top),bottom:k(a.bottom),right:k(a.right),width:k(a.width),height:k(a.height)}:{left:0,top:0,bottom:0,right:0,width:0,height:0}}function b(b,c){return b=a(b),c?b.right=b.left:(b.left=b.left+b.width,b.right=b.left),b.width=0,b}function c(a,b){return a.left===b.left&&a.top===b.top&&a.bottom===b.bottom&&a.right===b.right}function d(a,b,c){return a>=0&&a<=Math.min(b.height,c.height)/2}function e(a,b){return a.bottom-a.height/2<b.top||!(a.top>b.bottom)&&d(b.top-a.bottom,a,b)}function f(a,b){return a.top>b.bottom||!(a.bottom<b.top)&&d(b.bottom-a.top,a,b)}function g(a,b){return a.left<b.left}function h(a,b){return a.right>b.right}function i(a,b){return e(a,b)?-1:f(a,b)?1:g(a,b)?-1:h(a,b)?1:0}function j(a,b,c){return b>=a.left&&b<=a.right&&c>=a.top&&c<=a.bottom}var k=Math.round;return{clone:a,collapse:b,isEqual:c,isAbove:e,isBelow:f,isLeft:g,isRight:h,compare:i,containsXY:j}}),g("1n",["3x","43"],function(a,b){var c=function(c,d,e){return!e.collapsed&&a.foldl(e.getClientRects(),function(a,e){return a||b.containsXY(e,c,d)},!1)};return{isXYWithinRange:c}}),g("q",["1m","1j","1n","6","5","9","p"],function(a,b,c,d,e,f,g){function h(a,b){for(;b&&b!=a;){if(j(b)||i(b))return b;b=b.parentNode}return null}var i=b.isContentEditableFalse,j=b.isContentEditableTrue,k=function(a){return a&&"IMG"===a.nodeName},l=function(a,b){return k(a.target)&&!c.isXYWithinRange(a.clientX,a.clientY,b)},m=function(a,b){var c=b.target;l(b,a.selection.getRng())&&!b.isDefaultPrevented()&&(b.preventDefault(),a.selection.select(c))};return function(b,c){function j(a){var b=c.settings.object_resizing;return b!==!1&&!d.iOS&&("string"!=typeof b&&(b="table,img,div"),"false"!==a.getAttribute("data-mce-resize")&&(a!=c.getBody()&&c.dom.is(a,b)))}function k(a){var b,d,e,f,h;b=a.screenX-H,d=a.screenY-I,P=b*F[2]+L,Q=d*F[3]+M,P=P<5?5:P,Q=Q<5?5:Q,e="IMG"==B.nodeName&&c.settings.resize_img_proportional!==!1?!g.modifierPressed(a):g.modifierPressed(a)||"IMG"==B.nodeName&&F[2]*F[3]!==0,e&&(Y(b)>Y(d)?(Q=Z(P*N),P=Z(Q/N)):(P=Z(Q/N),Q=Z(P*N))),T.setStyles(C,{width:P,height:Q}),f=F.startPos.x+b,h=F.startPos.y+d,f=f>0?f:0,h=h>0?h:0,T.setStyles(D,{left:f,top:h,display:"block"}),D.innerHTML=P+" × "+Q,F[2]<0&&C.clientWidth<=P&&T.setStyle(C,"left",J+(L-P)),F[3]<0&&C.clientHeight<=Q&&T.setStyle(C,"top",K+(M-Q)),b=$.scrollWidth-R,d=$.scrollHeight-S,b+d!==0&&T.setStyles(D,{left:f-b,top:h-d}),O||(c.fire("ObjectResizeStart",{target:B,width:L,height:M}),O=!0)}function l(){function a(a,b){b&&(B.style[a]||!c.schema.isValid(B.nodeName.toLowerCase(),a)?T.setStyle(B,a,b):T.setAttrib(B,a,b))}O=!1,a("width",P),a("height",Q),T.unbind(V,"mousemove",k),T.unbind(V,"mouseup",l),W!=V&&(T.unbind(W,"mousemove",k),T.unbind(W,"mouseup",l)),T.remove(C),T.remove(D),X&&"TABLE"!=B.nodeName||n(B),c.fire("ObjectResized",{target:B,width:P,height:Q}),T.setAttrib(B,"style",T.getAttrib(B,"style")),c.nodeChanged()}function n(a,b,e){var f,g,h,i,m;o(),x(),f=T.getPos(a,$),J=f.x,K=f.y,m=a.getBoundingClientRect(),g=m.width||m.right-m.left,h=m.height||m.bottom-m.top,B!=a&&(w(),B=a,P=Q=0),i=c.fire("ObjectSelected",{target:a}),j(a)&&!i.isDefaultPrevented()?U(E,function(a,c){function f(b){H=b.screenX,I=b.screenY,L=B.clientWidth,M=B.clientHeight,N=M/L,F=a,a.startPos={x:g*a[0]+J,y:h*a[1]+K},R=$.scrollWidth,S=$.scrollHeight,C=B.cloneNode(!0),T.addClass(C,"mce-clonedresizable"),T.setAttrib(C,"data-mce-bogus","all"),C.contentEditable=!1,C.unSelectabe=!0,T.setStyles(C,{left:J,top:K,margin:0}),C.removeAttribute("data-mce-selected"),$.appendChild(C),T.bind(V,"mousemove",k),T.bind(V,"mouseup",l),W!=V&&(T.bind(W,"mousemove",k),T.bind(W,"mouseup",l)),D=T.add($,"div",{"class":"mce-resize-helper","data-mce-bogus":"all"},L+" × "+M)}var i;return b?void(c==b&&f(e)):(i=T.get("mceResizeHandle"+c),i&&T.remove(i),i=T.add($,"div",{id:"mceResizeHandle"+c,"data-mce-bogus":"all","class":"mce-resizehandle",unselectable:!0,style:"cursor:"+c+"-resize; margin:0; padding:0"}),d.ie&&(i.contentEditable=!1),T.bind(i,"mousedown",function(a){a.stopImmediatePropagation(),a.preventDefault(),f(a)}),a.elm=i,void T.setStyles(i,{left:g*a[0]+J-i.offsetWidth/2,top:h*a[1]+K-i.offsetHeight/2}))}):o(),B.setAttribute("data-mce-selected","1")}function o(){var a,b;x(),B&&B.removeAttribute("data-mce-selected");for(a in E)b=T.get("mceResizeHandle"+a),b&&(T.unbind(b),T.remove(b))}function p(a){function d(a,b){if(a)do if(a===b)return!0;while(a=a.parentNode)}var e,f;if(!O&&!c.removed)return U(T.select("img[data-mce-selected],hr[data-mce-selected]"),function(a){a.removeAttribute("data-mce-selected")}),f="mousedown"==a.type?a.target:b.getNode(),f=T.$(f).closest(X?"table":"table,img,hr")[0],d(f,$)&&(y(),e=b.getStart(!0),d(e,f)&&d(b.getEnd(!0),f)&&(!X||f!=e&&"IMG"!==e.nodeName))?void n(f):void o()}function q(a,b,c){a&&a.attachEvent&&a.attachEvent("on"+b,c)}function r(a,b,c){a&&a.detachEvent&&a.detachEvent("on"+b,c)}function s(a){var b,d,e,f,g,h,i,j=a.srcElement;b=j.getBoundingClientRect(),h=G.clientX-b.left,i=G.clientY-b.top;for(d in E)if(e=E[d],f=j.offsetWidth*e[0],g=j.offsetHeight*e[1],Y(f-h)<8&&Y(g-i)<8){F=e;break}O=!0,c.fire("ObjectResizeStart",{target:B,width:B.clientWidth,height:B.clientHeight}),c.getDoc().selection.empty(),n(j,d,G)}function t(a){a.preventDefault?a.preventDefault():a.returnValue=!1}function u(a){return i(h(c.getBody(),a))}function v(a){var b=a.srcElement;if(u(b))return void t(a);if(b!=B){if(c.fire("ObjectSelected",{target:b}),w(),0===b.id.indexOf("mceResizeHandle"))return void(a.returnValue=!1);"IMG"!=b.nodeName&&"TABLE"!=b.nodeName||(o(),B=b,q(b,"resizestart",s))}}function w(){r(B,"resizestart",s)}function x(){for(var a in E){var b=E[a];b.elm&&(T.unbind(b.elm),delete b.elm)}}function y(){try{c.getDoc().execCommand("enableObjectResizing",!1,!1); -}catch(a){}}function z(a){var b;if(X){b=V.body.createControlRange();try{return b.addElement(a),b.select(),!0}catch(a){}}}function A(){B=C=null,X&&(w(),r($,"controlselect",v))}var B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T=c.dom,U=f.each,V=c.getDoc(),W=document,X=d.ie&&d.ie<11,Y=Math.abs,Z=Math.round,$=c.getBody();E={nw:[0,0,-1,-1],ne:[1,0,1,-1],se:[1,1,1,1],sw:[0,1,-1,1]};var _=".mce-content-body";return c.contentStyles.push(_+" div.mce-resizehandle {position: absolute;border: 1px solid black;box-sizing: box-sizing;background: #FFF;width: 7px;height: 7px;z-index: 10000}"+_+" .mce-resizehandle:hover {background: #000}"+_+" img[data-mce-selected],"+_+" hr[data-mce-selected] {outline: 1px solid black;resize: none}"+_+" .mce-clonedresizable {position: absolute;"+(d.gecko?"":"outline: 1px dashed black;")+"opacity: .5;filter: alpha(opacity=50);z-index: 10000}"+_+" .mce-resize-helper {background: #555;background: rgba(0,0,0,0.75);border-radius: 3px;border: 1px;color: white;display: none;font-family: sans-serif;font-size: 12px;white-space: nowrap;line-height: 14px;margin: 5px 10px;padding: 5px;position: absolute;z-index: 10001}"),c.on("init",function(){X?(c.on("ObjectResized",function(a){"TABLE"!=a.target.nodeName&&(o(),z(a.target))}),q($,"controlselect",v),c.on("mousedown",function(a){G=a})):(y(),d.ie>=11&&(c.on("mousedown click",function(a){var b=a.target,d=b.nodeName;O||!/^(TABLE|IMG|HR)$/.test(d)||u(b)||(2!==a.button&&c.selection.select(b,"TABLE"==d),"mousedown"==a.type&&c.nodeChanged())}),c.dom.bind($,"mscontrolselect",function(a){function b(a){e.setEditorTimeout(c,function(){c.selection.select(a)})}return u(a.target)?(a.preventDefault(),void b(a.target)):void(/^(TABLE|IMG|HR)$/.test(a.target.nodeName)&&(a.preventDefault(),"IMG"==a.target.tagName&&b(a.target)))})));var b=e.throttle(function(a){c.composing||p(a)});c.on("nodechange ResizeEditor ResizeWindow drop",b),c.on("keyup compositionend",function(a){B&&"TABLE"==B.nodeName&&b(a)}),c.on("hide blur",o),c.on("contextmenu",a.curry(m,c))}),c.on("remove",x),{isResizable:j,showResizeRect:n,hideResizeRect:o,updateResizeRect:p,controlSelect:z,destroy:A}}}),g("1w",[],function(){function a(a){return function(){return a}}function b(a){return function(b){return!a(b)}}function c(a,b){return function(c){return a(b(c))}}function d(){var a=h.call(arguments);return function(b){for(var c=0;c<a.length;c++)if(a[c](b))return!0;return!1}}function e(){var a=h.call(arguments);return function(b){for(var c=0;c<a.length;c++)if(!a[c](b))return!1;return!0}}function f(a){var b=h.call(arguments);return b.length-1>=a.length?a.apply(this,b.slice(1)):function(){var a=b.concat([].slice.call(arguments));return f.apply(this,a)}}function g(){}var h=[].slice;return{constant:a,negate:b,and:e,or:d,curry:f,compose:c,noop:g}}),g("44",["1j","1g","1k"],function(a,b,c){function d(a){return!p(a)&&(l(a)?!m(a.parentNode):n(a)||k(a)||o(a)||j(a))}function e(a,b){for(a=a.parentNode;a&&a!=b;a=a.parentNode){if(j(a))return!1;if(i(a))return!0}return!0}function f(a){return!!j(a)&&b.reduce(a.getElementsByTagName("*"),function(a,b){return a||i(b)},!1)!==!0}function g(a){return n(a)||f(a)}function h(a,b){return d(a)&&e(a,b)}var i=a.isContentEditableTrue,j=a.isContentEditableFalse,k=a.isBr,l=a.isText,m=a.matchNodeNames("script style textarea"),n=a.matchNodeNames("img input textarea hr iframe video audio object"),o=a.matchNodeNames("table"),p=c.isCaretContainer;return{isCaretCandidate:d,isInEditable:e,isAtomic:g,isEditableCaretCandidate:h}}),g("45",[],function(){function a(a){return"string"==typeof a&&a.charCodeAt(0)>=768&&b.test(a)}var b=new RegExp("[\u0300-\u036f\u0483-\u0487\u0488-\u0489\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7-\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e3-\u0902\u093a\u093c\u0941-\u0948\u094d\u0951-\u0957\u0962-\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2-\u09e3\u0a01-\u0a02\u0a3c\u0a41-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a70-\u0a71\u0a75\u0a81-\u0a82\u0abc\u0ac1-\u0ac5\u0ac7-\u0ac8\u0acd\u0ae2-\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62-\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c00\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c62-\u0c63\u0c81\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc-\u0ccd\u0cd5-\u0cd6\u0ce2-\u0ce3\u0d01\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62-\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb-\u0ebc\u0ec8-\u0ecd\u0f18-\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039-\u103a\u103d-\u103e\u1058-\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085-\u1086\u108d\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17b4-\u17b5\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193b\u1a17-\u1a18\u1a1b\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1ab0-\u1abd\u1abe\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80-\u1b81\u1ba2-\u1ba5\u1ba8-\u1ba9\u1bab-\u1bad\u1be6\u1be8-\u1be9\u1bed\u1bef-\u1bf1\u1c2c-\u1c33\u1c36-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1cf4\u1cf8-\u1cf9\u1dc0-\u1df5\u1dfc-\u1dff\u200c-\u200d\u20d0-\u20dc\u20dd-\u20e0\u20e1\u20e2-\u20e4\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302d\u302e-\u302f\u3099-\u309a\ua66f\ua670-\ua672\ua674-\ua67d\ua69e-\ua69f\ua6f0-\ua6f1\ua802\ua806\ua80b\ua825-\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\ua9e5\uaa29-\uaa2e\uaa31-\uaa32\uaa35-\uaa36\uaa43\uaa4c\uaa7c\uaab0\uaab2-\uaab4\uaab7-\uaab8\uaabe-\uaabf\uaac1\uaaec-\uaaed\uaaf6\uabe5\uabe8\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\uff9e-\uff9f]");return{isExtendingChar:a}}),g("1p",["1w","1j","e","h","44","43","45"],function(a,b,c,d,e,f,g){function h(a){return"createRange"in a?a.createRange():c.DOM.createRng()}function i(a){return a&&/[\r\n\t ]/.test(a)}function j(a){var b,c=a.startContainer,d=a.startOffset;return!!(i(a.toString())&&r(c.parentNode)&&(b=c.data,i(b[d-1])||i(b[d+1])))}function k(a){function b(a){var b,c=a.ownerDocument,d=h(c),e=c.createTextNode("\xa0"),g=a.parentNode;return g.insertBefore(e,a),d.setStart(e,0),d.setEnd(e,1),b=f.clone(d.getBoundingClientRect()),g.removeChild(e),b}function c(a){var c,d;return d=a.getClientRects(),c=d.length>0?f.clone(d[0]):f.clone(a.getBoundingClientRect()),t(a)&&0===c.left?b(a):c}function d(a,b){return a=f.collapse(a,b),a.width=1,a.right=a.left+1,a}function e(a){0!==a.height&&(n.length>0&&f.isEqual(a,n[n.length-1])||n.push(a))}function i(a,b){var f=h(a.ownerDocument);if(b<a.data.length){if(g.isExtendingChar(a.data[b]))return n;if(g.isExtendingChar(a.data[b-1])&&(f.setStart(a,b),f.setEnd(a,b+1),!j(f)))return e(d(c(f),!1)),n}b>0&&(f.setStart(a,b-1),f.setEnd(a,b),j(f)||e(d(c(f),!1))),b<a.data.length&&(f.setStart(a,b),f.setEnd(a,b+1),j(f)||e(d(c(f),!0)))}var k,l,n=[];if(s(a.container()))return i(a.container(),a.offset()),n;if(m(a.container()))if(a.isAtEnd())l=v(a.container(),a.offset()),s(l)&&i(l,l.data.length),q(l)&&!t(l)&&e(d(c(l),!1));else{if(l=v(a.container(),a.offset()),s(l)&&i(l,0),q(l)&&a.isAtEnd())return e(d(c(l),!1)),n;k=v(a.container(),a.offset()-1),q(k)&&!t(k)&&(o(k)||o(l)||!q(l))&&e(d(c(k),!1)),q(l)&&e(d(c(l),!0))}return n}function l(b,c,d){function e(){return s(b)?0===c:0===c}function f(){return s(b)?c>=b.data.length:c>=b.childNodes.length}function g(){var a;return a=h(b.ownerDocument),a.setStart(b,c),a.setEnd(b,c),a}function i(){return d||(d=k(new l(b,c))),d}function j(){return i().length>0}function m(a){return a&&b===a.container()&&c===a.offset()}function n(a){return v(b,a?c-1:c)}return{container:a.constant(b),offset:a.constant(c),toRange:g,getClientRects:i,isVisible:j,isAtStart:e,isAtEnd:f,isEqual:m,getNode:n}}var m=b.isElement,n=e.isCaretCandidate,o=b.matchStyleValues("display","block table"),p=b.matchStyleValues("float","left right"),q=a.and(m,n,a.negate(p)),r=a.negate(b.matchStyleValues("white-space","pre pre-line pre-wrap")),s=b.isText,t=b.isBr,u=c.nodeIndex,v=d.getNode;return l.fromRangeStart=function(a){return new l(a.startContainer,a.startOffset)},l.fromRangeEnd=function(a){return new l(a.endContainer,a.endOffset)},l.after=function(a){return new l(a.parentNode,u(a)+1)},l.before=function(a){return new l(a.parentNode,u(a))},l.isAtStart=function(a){return!!a&&a.isAtStart()},l.isAtEnd=function(a){return!!a&&a.isAtEnd()},l.isTextPosition=function(a){return!!a&&b.isText(a.container())},l}),g("1o",["1j","e","1w","1g","1p"],function(a,b,c,d,e){function f(a){var b=a.parentNode;return r(b)?f(b):b}function g(a){return a?d.reduce(a.childNodes,function(a,b){return r(b)&&"BR"!=b.nodeName?a=a.concat(g(b)):a.push(b),a},[]):[]}function h(a,b){for(;(a=a.previousSibling)&&q(a);)b+=a.data.length;return b}function i(a){return function(b){return a===b}}function j(b){var c,e,h;return c=g(f(b)),e=d.findIndex(c,i(b),b),c=c.slice(0,e+1),h=d.reduce(c,function(a,b,d){return q(b)&&q(c[d-1])&&a++,a},0),c=d.filter(c,a.matchNodeNames(b.nodeName)),e=d.findIndex(c,i(b),b),e-h}function k(a){var b;return b=q(a)?"text()":a.nodeName.toLowerCase(),b+"["+j(a)+"]"}function l(a,b,c){var d=[];for(b=b.parentNode;b!=a&&(!c||!c(b));b=b.parentNode)d.push(b);return d}function m(b,e){var f,g,i,j,m,n=[];return f=e.container(),g=e.offset(),q(f)?i=h(f,g):(j=f.childNodes,g>=j.length?(i="after",g=j.length-1):i="before",f=j[g]),n.push(k(f)),m=l(b,f),m=d.filter(m,c.negate(a.isBogus)),n=n.concat(d.map(m,function(a){return k(a)})),n.reverse().join("/")+","+i}function n(b,c,e){var f=g(b);return f=d.filter(f,function(a,b){return!q(a)||!q(f[b-1])}),f=d.filter(f,a.matchNodeNames(c)),f[e]}function o(a,b){for(var c,d=a,f=0;q(d);){if(c=d.data.length,b>=f&&b<=f+c){a=d,b-=f;break}if(!q(d.nextSibling)){a=d,b=c;break}f+=c,d=d.nextSibling}return b>a.data.length&&(b=a.data.length),new e(a,b)}function p(a,b){var c,f,g;return b?(c=b.split(","),b=c[0].split("/"),g=c.length>1?c[1]:"before",f=d.reduce(b,function(a,b){return(b=/([\w\-\(\)]+)\[([0-9]+)\]/.exec(b))?("text()"===b[1]&&(b[1]="#text"),n(a,b[1],parseInt(b[2],10))):null},a),f?q(f)?o(f,parseInt(g,10)):(g="after"===g?s(f)+1:s(f),new e(f.parentNode,g)):null):null}var q=a.isText,r=a.isBogus,s=b.nodeIndex;return{create:m,resolve:p}}),g("r",["1o","1k","1p","1j","h","6","1l","9"],function(a,b,c,d,e,f,g,h){function i(g){var i=g.dom;this.getBookmark=function(f,l){function m(a,b){var c=0;return h.each(i.select(a),function(a){if("all"!==a.getAttribute("data-mce-bogus"))return a!=b&&void c++}),c}function n(a){function b(b){var c,d,e,f=b?"start":"end";c=a[f+"Container"],d=a[f+"Offset"],1==c.nodeType&&"TR"==c.nodeName&&(e=c.childNodes,c=e[Math.min(b?d:d-1,e.length-1)],c&&(d=b?0:c.childNodes.length,a["set"+(b?"Start":"End")](c,d)))}return b(!0),b(),a}function o(a){function b(a,b){var d,e=a[b?"startContainer":"endContainer"],f=a[b?"startOffset":"endOffset"],g=[],h=0;for(3===e.nodeType?g.push(l?k(e,f):f):(d=e.childNodes,f>=d.length&&d.length&&(h=1,f=Math.max(0,d.length-1)),g.push(i.nodeIndex(d[f],l)+h));e&&e!=c;e=e.parentNode)g.push(i.nodeIndex(e,l));return g}var c=i.getRoot(),d={};return d.start=b(a,!0),g.isCollapsed()||(d.end=b(a)),d}function p(a){function c(a,c){var f;if(d.isElement(a)&&(a=e.getNode(a,c),j(a)))return a;if(b.isCaretContainer(a)){if(d.isText(a)&&b.isCaretContainerBlock(a)&&(a=a.parentNode),f=a.previousSibling,j(f))return f;if(f=a.nextSibling,j(f))return f}}return c(a.startContainer,a.startOffset)||c(a.endContainer,a.endOffset)}var q,r,s,t,u,v,w,x="";if(2==f)return v=g.getNode(),u=v?v.nodeName:null,q=g.getRng(),j(v)||"IMG"==u?{name:u,index:m(u,v)}:g.tridentSel?g.tridentSel.getBookmark(f):(v=p(q),v?(u=v.tagName,{name:u,index:m(u,v)}):o(q));if(3==f)return q=g.getRng(),{start:a.create(i.getRoot(),c.fromRangeStart(q)),end:a.create(i.getRoot(),c.fromRangeEnd(q))};if(f)return{rng:g.getRng()};if(q=g.getRng(),s=i.uniqueId(),t=g.isCollapsed(),w="overflow:hidden;line-height:0px",q.duplicate||q.item){if(q.item)return v=q.item(0),u=v.nodeName,{name:u,index:m(u,v)};r=q.duplicate();try{q.collapse(),q.pasteHTML('<span data-mce-type="bookmark" id="'+s+'_start" style="'+w+'">'+x+"</span>"),t||(r.collapse(!1),q.moveToElementText(r.parentElement()),0===q.compareEndPoints("StartToEnd",r)&&r.move("character",-1),r.pasteHTML('<span data-mce-type="bookmark" id="'+s+'_end" style="'+w+'">'+x+"</span>"))}catch(a){return null}}else{if(v=g.getNode(),u=v.nodeName,"IMG"==u)return{name:u,index:m(u,v)};r=n(q.cloneRange()),t||(r.collapse(!1),r.insertNode(i.create("span",{"data-mce-type":"bookmark",id:s+"_end",style:w},x))),q=n(q),q.collapse(!0),q.insertNode(i.create("span",{"data-mce-type":"bookmark",id:s+"_start",style:w},x))}return g.moveToBookmark({id:s,keep:1}),{id:s}},this.moveToBookmark=function(b){function c(a){var c,d,e,f,g=b[a?"start":"end"];if(g){for(e=g[0],d=l,c=g.length-1;c>=1;c--){if(f=d.childNodes,g[c]>f.length-1)return;d=f[g[c]]}3===d.nodeType&&(e=Math.min(g[0],d.nodeValue.length)),1===d.nodeType&&(e=Math.min(g[0],d.childNodes.length)),a?k.setStart(d,e):k.setEnd(d,e)}return!0}function d(a){var c,d,e,g,j=i.get(b.id+"_"+a),k=b.keep;if(j&&(c=j.parentNode,"start"==a?(k?(c=j.firstChild,d=1):d=i.nodeIndex(j),m=n=c,o=p=d):(k?(c=j.firstChild,d=1):d=i.nodeIndex(j),n=c,p=d),!k)){for(g=j.previousSibling,e=j.nextSibling,h.each(h.grep(j.childNodes),function(a){3==a.nodeType&&(a.nodeValue=a.nodeValue.replace(/\uFEFF/g,""))});j=i.get(b.id+"_"+a);)i.remove(j,1);g&&e&&g.nodeType==e.nodeType&&3==g.nodeType&&!f.opera&&(d=g.nodeValue.length,g.appendData(e.nodeValue),i.remove(e),"start"==a?(m=n=g,o=p=d):(n=g,p=d))}}function e(a){return!i.isBlock(a)||a.innerHTML||f.ie||(a.innerHTML='<br data-mce-bogus="1" />'),a}function j(){var c,d;return c=i.createRng(),d=a.resolve(i.getRoot(),b.start),c.setStart(d.container(),d.offset()),d=a.resolve(i.getRoot(),b.end),c.setEnd(d.container(),d.offset()),c}var k,l,m,n,o,p;if(b)if(h.isArray(b.start)){if(k=i.createRng(),l=i.getRoot(),g.tridentSel)return g.tridentSel.moveToBookmark(b);c(!0)&&c()&&g.setRng(k)}else"string"==typeof b.start?g.setRng(j(b)):b.id?(d("start"),d("end"),m&&(k=i.createRng(),k.setStart(e(m),o),k.setEnd(e(n),p),g.setRng(k))):b.name?g.select(i.select(b.name)[b.index]):b.rng&&g.setRng(b.rng)}}var j=d.isContentEditableFalse,k=function(a,b){var c,d;for(d=g.trim(a.data.slice(0,b)).length,c=a.previousSibling;c&&3===c.nodeType;c=c.previousSibling)d+=g.trim(c.data).length;return d};return i.isBookmarkNode=function(a){return a&&"SPAN"===a.tagName&&"bookmark"===a.getAttribute("data-mce-type")},i}),g("6j",[],function(){return"undefined"!=typeof window?window:Function("return this;")()}),g("5u",["6j"],function(a){var b=function(b,c){for(var d=void 0!==c?c:a,e=0;e<b.length&&void 0!==d&&null!==d;++e)d=d[b[e]];return d},c=function(a,c){var d=a.split(".");return b(d,c)},d=function(a,b){return void 0!==a[b]&&null!==a[b]||(a[b]={}),a[b]},e=function(b,c){for(var e=void 0!==c?c:a,f=0;f<b.length;++f)e=d(e,b[f]);return e},f=function(a,b){var c=a.split(".");return e(c,b)};return{path:b,resolve:c,forge:e,namespace:f}}),g("54",["5u"],function(a){var b=function(b,c){return a.resolve(b,c)},c=function(a,c){var d=b(a,c);if(void 0===d)throw a+" not available on this browser";return d};return{getOrDie:c}}),g("46",["54"],function(a){var b=function(){var b=a.getOrDie("Node");return b},c=function(a,b,c){return 0!==(a.compareDocumentPosition(b)&c)},d=function(a,d){return c(a,d,b().DOCUMENT_POSITION_PRECEDING)},e=function(a,d){return c(a,d,b().DOCUMENT_POSITION_CONTAINED_BY)};return{documentPositionPreceding:d,documentPositionContainedBy:e}}),g("55",[],function(){var a=function(a){var b,c=!1;return function(){return c||(c=!0,b=a.apply(null,arguments)),b}};return{cached:a}}),h("75",Number),g("6k",["3x","75","50"],function(a,b,c){var d=function(a,b){for(var c=0;c<a.length;c++){var d=a[c];if(d.test(b))return d}},e=function(a,c){var e=d(a,c);if(!e)return{major:0,minor:0};var f=function(a){return b(c.replace(e,"$"+a))};return h(f(1),f(2))},f=function(a,b){var d=c(b).toLowerCase();return 0===a.length?g():e(a,d)},g=function(){return h(0,0)},h=function(a,b){return{major:a,minor:b}};return{nu:h,detect:f,unknown:g}}),g("5v",["1m","6k"],function(a,b){var c="Edge",d="Chrome",e="IE",f="Opera",g="Firefox",h="Safari",i=function(a,b){return function(){return b===a}},j=function(){return k({current:void 0,version:b.unknown()})},k=function(a){var b=a.current,j=a.version;return{current:b,version:j,isEdge:i(c,b),isChrome:i(d,b),isIE:i(e,b),isOpera:i(f,b),isFirefox:i(g,b),isSafari:i(h,b)}};return{unknown:j,nu:k,edge:a.constant(c),chrome:a.constant(d),ie:a.constant(e),opera:a.constant(f),firefox:a.constant(g),safari:a.constant(h)}}),g("5w",["1m","6k"],function(a,b){var c="Windows",d="iOS",e="Android",f="Linux",g="OSX",h="Solaris",i="FreeBSD",j=function(a,b){return function(){return b===a}},k=function(){return l({current:void 0,version:b.unknown()})},l=function(a){var b=a.current,k=a.version;return{current:b,version:k,isWindows:j(c,b),isiOS:j(d,b),isAndroid:j(e,b),isOSX:j(g,b),isLinux:j(f,b),isSolaris:j(h,b),isFreeBSD:j(i,b)}};return{unknown:k,nu:l,windows:a.constant(c),ios:a.constant(d),android:a.constant(e),linux:a.constant(f),osx:a.constant(g),solaris:a.constant(h),freebsd:a.constant(i)}}),g("5x",["1m"],function(a){return function(b,c,d){var e=b.isiOS()&&/ipad/i.test(d)===!0,f=b.isiOS()&&!e,g=b.isAndroid()&&3===b.version.major,h=b.isAndroid()&&4===b.version.major,i=e||g||h&&/mobile/i.test(d)===!0,j=b.isiOS()||b.isAndroid(),k=j&&!i,l=c.isSafari()&&b.isiOS()&&/safari/i.test(d)===!1;return{isiPad:a.constant(e),isiPhone:a.constant(f),isTablet:a.constant(i),isPhone:a.constant(k),isTouch:a.constant(j),isAndroid:b.isAndroid,isiOS:b.isiOS,isWebView:a.constant(l)}}}),g("5y",["3x","6k","50"],function(a,b,c){var d=function(b,d){var e=c(d).toLowerCase();return a.find(b,function(a){return a.search(e)})},e=function(a,c){return d(a,c).map(function(a){var d=b.detect(a.versionRegexes,c);return{current:a.name,version:d}})},f=function(a,c){return d(a,c).map(function(a){var d=b.detect(a.versionRegexes,c);return{current:a.name,version:d}})};return{detectBrowser:e,detectOs:f}}),g("76",[],function(){var a=function(a,b){return b+a},b=function(a,b){return a+b},c=function(a,b){return a.substring(b)},d=function(a,b){return a.substring(0,a.length-b)};return{addToStart:a,addToEnd:b,removeFromStart:c,removeFromEnd:d}}),g("77",["4z","3z"],function(a,b){var c=function(a,b){return a.substr(0,b)},d=function(a,b){return a.substr(a.length-b,a.length)},e=function(b){return""===b?a.none():a.some(b.substr(0,1))},f=function(b){return""===b?a.none():a.some(b.substring(1))};return{first:c,last:d,head:e,tail:f}}),g("6l",["76","77","3z"],function(a,b,c){var d=function(a,b,c){if(""===b)return!0;if(a.length<b.length)return!1;var d=a.substr(c,c+b.length);return d===b},e=function(a,b){var c=function(a){var b=typeof a;return"string"===b||"number"===b};return a.replace(/\${([^{}]*)}/g,function(a,d){var e=b[d];return c(e)?e:a})},f=function(b,c){return l(b,c)?a.removeFromStart(b,c.length):b},g=function(b,c){return m(b,c)?a.removeFromEnd(b,c.length):b},h=function(b,c){return l(b,c)?b:a.addToStart(b,c)},i=function(b,c){return m(b,c)?b:a.addToEnd(b,c)},j=function(a,b){return a.indexOf(b)!==-1},k=function(a){return b.head(a).bind(function(c){return b.tail(a).map(function(a){return c.toUpperCase()+a})}).getOr(a)},l=function(a,b){return d(a,b,0)},m=function(a,b){return d(a,b,a.length-b.length)},n=function(a){return a.replace(/^\s+|\s+$/g,"")},o=function(a){return a.replace(/^\s+/g,"")},p=function(a){return a.replace(/\s+$/g,"")};return{supplant:e,startsWith:l,removeLeading:f,removeTrailing:g,ensureLeading:h,ensureTrailing:i,endsWith:m,contains:j,trim:n,lTrim:o,rTrim:p,capitalize:k}}),g("5z",["1m","6l"],function(a,b){var c=/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,d=function(a){return function(c){return b.contains(c,a)}},e=[{name:"Edge",versionRegexes:[/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],search:function(a){var c=b.contains(a,"edge/")&&b.contains(a,"chrome")&&b.contains(a,"safari")&&b.contains(a,"applewebkit");return c}},{name:"Chrome",versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/,c],search:function(a){return b.contains(a,"chrome")&&!b.contains(a,"chromeframe")}},{name:"IE",versionRegexes:[/.*?msie\ ?([0-9]+)\.([0-9]+).*/,/.*?rv:([0-9]+)\.([0-9]+).*/],search:function(a){return b.contains(a,"msie")||b.contains(a,"trident")}},{name:"Opera",versionRegexes:[c,/.*?opera\/([0-9]+)\.([0-9]+).*/],search:d("opera")},{name:"Firefox",versionRegexes:[/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],search:d("firefox")},{name:"Safari",versionRegexes:[c,/.*?cpu os ([0-9]+)_([0-9]+).*/],search:function(a){return(b.contains(a,"safari")||b.contains(a,"mobile/"))&&b.contains(a,"applewebkit")}}],f=[{name:"Windows",search:d("win"),versionRegexes:[/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]},{name:"iOS",search:function(a){return b.contains(a,"iphone")||b.contains(a,"ipad")},versionRegexes:[/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,/.*cpu os ([0-9]+)_([0-9]+).*/,/.*cpu iphone os ([0-9]+)_([0-9]+).*/]},{name:"Android",search:d("android"),versionRegexes:[/.*?android\ ?([0-9]+)\.([0-9]+).*/]},{name:"OSX",search:d("os x"),versionRegexes:[/.*?os\ x\ ?([0-9]+)_([0-9]+).*/]},{name:"Linux",search:d("linux"),versionRegexes:[]},{name:"Solaris",search:d("sunos"),versionRegexes:[]},{name:"FreeBSD",search:d("freebsd"),versionRegexes:[]}];return{browsers:a.constant(e),oses:a.constant(f)}}),g("56",["5v","5w","5x","5y","5z"],function(a,b,c,d,e){var f=function(f){var g=e.browsers(),h=e.oses(),i=d.detectBrowser(g,f).fold(a.unknown,a.nu),j=d.detectOs(h,f).fold(b.unknown,b.nu),k=c(j,i,f);return{browser:i,os:j,deviceType:k}};return{detect:f}}),h("57",navigator),g("47",["55","56","57"],function(a,b,c){var d=a.cached(function(){var a=c.userAgent;return b.detect(a)});return{detect:d}}),g("49",[],function(){return"undefined"==typeof console&&(console={log:function(){}}),console}),h("22",document),g("1r",["1m","3z","49","22"],function(a,b,c,d){var e=function(a,b){var e=b||d,f=e.createElement("div");if(f.innerHTML=a,!f.hasChildNodes()||f.childNodes.length>1)throw c.error("HTML does not have a single root node",a),"HTML must have a single root node";return h(f.childNodes[0])},f=function(a,b){var c=b||d,e=c.createElement(a);return h(e)},g=function(a,b){var c=b||d,e=c.createTextNode(a);return h(e)},h=function(c){if(null===c||void 0===c)throw new b("Node cannot be null or undefined");return{dom:a.constant(c)}};return{fromHtml:e,fromTag:f,fromText:g,fromDom:h}}),g("58",[],function(){return{ATTRIBUTE:2,CDATA_SECTION:4,COMMENT:8,DOCUMENT:9,DOCUMENT_TYPE:10,DOCUMENT_FRAGMENT:11,ELEMENT:1,TEXT:3,PROCESSING_INSTRUCTION:7,ENTITY_REFERENCE:5,ENTITY:6,NOTATION:12}}),g("48",["3x","4z","1r","58","3z","22"],function(a,b,c,d,e,f){var g=0,h=1,i=2,j=3,k=function(){var a=f.createElement("span");return void 0!==a.matches?g:void 0!==a.msMatchesSelector?h:void 0!==a.webkitMatchesSelector?i:void 0!==a.mozMatchesSelector?j:-1}(),l=d.ELEMENT,m=d.DOCUMENT,n=function(a,b){var c=a.dom();if(c.nodeType!==l)return!1;if(k===g)return c.matches(b);if(k===h)return c.msMatchesSelector(b);if(k===i)return c.webkitMatchesSelector(b);if(k===j)return c.mozMatchesSelector(b);throw new e("Browser lacks native selectors")},o=function(a){return a.nodeType!==l&&a.nodeType!==m||0===a.childElementCount},p=function(b,d){var e=void 0===d?f:d.dom();return o(e)?[]:a.map(e.querySelectorAll(b),c.fromDom)},q=function(a,d){var e=void 0===d?f:d.dom();return o(e)?b.none():b.from(e.querySelector(a)).map(c.fromDom)};return{all:p,is:n,one:q}}),g("1q",["3x","1m","46","47","48"],function(a,b,c,d,e){var f=function(a,b){return a.dom()===b.dom()},g=function(a,b){return a.dom().isEqualNode(b.dom())},h=function(c,d){return a.exists(d,b.curry(f,c))},i=function(a,b){var c=a.dom(),d=b.dom();return c!==d&&c.contains(d)},j=function(a,b){return c.documentPositionContainedBy(a.dom(),b.dom())},k=d.detect().browser,l=k.isIE()?j:i;return{eq:f,isEqualNode:g,member:h,contains:l,is:e.is}}),g("1s",["1j"],function(a){var b=function(a){for(var b=0,c=0,d=a;d&&d.nodeType;)b+=d.offsetLeft||0,c+=d.offsetTop||0,d=d.offsetParent;return{x:b,y:c}},c=function(a,b,c){var d={elm:b,alignToTop:c};return a.fire("scrollIntoView",d),d.isDefaultPrevented()},d=function(d,e,f){var g,h,i,j,k=d.dom,l=k.getRoot(),m=0;if(!c(d,e,f)&&a.isElement(e)){if(f===!1&&(m=e.offsetHeight),"BODY"!==l.nodeName){var n=d.selection.getScrollContainer();if(n)return g=b(e).y-b(n).y+m,j=n.clientHeight,i=n.scrollTop,void((g<i||g+25>i+j)&&(n.scrollTop=g<i?g:g-j+25))}h=k.getViewPort(d.getWin()),g=k.getPos(e).y+m,i=h.y,j=h.h,(g<h.y||g+25>i+j)&&d.getWin().scrollTo(0,g<i?g:g-j+25)}};return{scrollIntoView:d}}),g("1t",[],function(){function a(a){function b(b,c){var d,e,f,g,h,i,j,k,l=0,m=-1;if(d=b.duplicate(),d.collapse(c),k=d.parentElement(),k.ownerDocument===a.dom.doc){for(;"false"===k.contentEditable;)k=k.parentNode;if(!k.hasChildNodes())return{node:k,inside:1};for(g=k.children,e=g.length-1;l<=e;)if(j=Math.floor((l+e)/2),h=g[j],d.moveToElementText(h),m=d.compareEndPoints(c?"StartToStart":"EndToEnd",b),m>0)e=j-1;else{if(!(m<0))return{node:h};l=j+1}if(m<0)for(h?d.collapse(!1):(d.moveToElementText(k),d.collapse(!0),h=k,f=!0),i=0;0!==d.compareEndPoints(c?"StartToStart":"StartToEnd",b)&&0!==d.move("character",1)&&k==d.parentElement();)i++;else for(d.collapse(!0),i=0;0!==d.compareEndPoints(c?"StartToStart":"StartToEnd",b)&&0!==d.move("character",-1)&&k==d.parentElement();)i++;return{node:h,position:m,offset:i,inside:f}}}function c(){function c(a){var c,d,e,f,g,h=b(k,a),i=0;if(c=h.node,d=h.offset,h.inside&&!c.hasChildNodes())return void l[a?"setStart":"setEnd"](c,0);if(d===f)return void l[a?"setStartBefore":"setEndAfter"](c);if(h.position<0){if(e=h.inside?c.firstChild:c.nextSibling,!e)return void l[a?"setStartAfter":"setEndAfter"](c);if(!d)return void(3==e.nodeType?l[a?"setStart":"setEnd"](e,0):l[a?"setStartBefore":"setEndBefore"](e));for(;e;){if(3==e.nodeType&&(g=e.nodeValue,i+=g.length,i>=d)){c=e,i-=d,i=g.length-i;break}e=e.nextSibling}}else{if(e=c.previousSibling,!e)return l[a?"setStartBefore":"setEndBefore"](c);if(!d)return void(3==c.nodeType?l[a?"setStart":"setEnd"](e,c.nodeValue.length):l[a?"setStartAfter":"setEndAfter"](e));for(;e;){if(3==e.nodeType&&(i+=e.nodeValue.length,i>=d)){c=e,i-=d;break}e=e.previousSibling}}l[a?"setStart":"setEnd"](c,i)}var f,g,h,i,j,k=a.getRng(),l=e.createRng();if(f=k.item?k.item(0):k.parentElement(),f.ownerDocument!=e.doc)return l;if(g=a.isCollapsed(),k.item)return l.setStart(f.parentNode,e.nodeIndex(f)),l.setEnd(l.startContainer,l.startOffset+1),l;try{c(!0),g||c()}catch(b){if(b.number!=-2147024809)throw b;j=d.getBookmark(2),h=k.duplicate(),h.collapse(!0),f=h.parentElement(),g||(h=k.duplicate(),h.collapse(!1),i=h.parentElement(),i.innerHTML=i.innerHTML),f.innerHTML=f.innerHTML,d.moveToBookmark(j),k=a.getRng(),c(!0),g||c()}return l}var d=this,e=a.dom,f=!1;this.getBookmark=function(c){function d(a){var b,c,d,f,g=[];for(b=a.parentNode,c=e.getRoot().parentNode;b!=c&&9!==b.nodeType;){for(d=b.children,f=d.length;f--;)if(a===d[f]){g.push(f);break}a=b,b=b.parentNode}return g}function f(a){var c;if(c=b(g,a))return{position:c.position,offset:c.offset,indexes:d(c.node),inside:c.inside}}var g=a.getRng(),h={};return 2===c&&(g.item?h.start={ctrl:!0,indexes:d(g.item(0))}:(h.start=f(!0),a.isCollapsed()||(h.end=f()))),h},this.moveToBookmark=function(a){function b(a){var b,c,d,f;for(b=e.getRoot(),c=a.length-1;c>=0;c--)f=b.children,d=a[c],d<=f.length-1&&(b=f[d]);return b}function c(c){var e,g,h,i,j=a[c?"start":"end"];j&&(e=j.position>0,g=f.createTextRange(),g.moveToElementText(b(j.indexes)),i=j.offset,i!==h?(g.collapse(j.inside||e),g.moveStart("character",e?-i:i)):g.collapse(c),d.setEndPoint(c?"StartToStart":"EndToStart",g),c&&d.collapse(!0))}var d,f=e.doc.body;a.start&&(a.start.ctrl?(d=f.createControlRange(),d.addElement(b(a.start.indexes)),d.select()):(d=f.createTextRange(),c(!0),c(),d.select()))},this.addRange=function(b){function c(a){var b,c,g,l,m;g=e.create("a"),b=a?h:j,c=a?i:k,l=d.duplicate(),b!=o&&b!=o.documentElement||(b=p,c=0),3==b.nodeType?(b.parentNode.insertBefore(g,b),l.moveToElementText(g),l.moveStart("character",c),e.remove(g),d.setEndPoint(a?"StartToStart":"EndToEnd",l)):(m=b.childNodes,m.length?(c>=m.length?e.insertAfter(g,m[m.length-1]):b.insertBefore(g,m[c]),l.moveToElementText(g)):b.canHaveHTML&&(b.innerHTML="<span></span>",g=b.firstChild,l.moveToElementText(g),l.collapse(f)),d.setEndPoint(a?"StartToStart":"EndToEnd",l),e.remove(g))}var d,g,h,i,j,k,l,m,n,o=a.dom.doc,p=o.body;if(h=b.startContainer,i=b.startOffset,j=b.endContainer,k=b.endOffset,d=p.createTextRange(),h==j&&1==h.nodeType){if(i==k&&!h.hasChildNodes()){if(h.canHaveHTML)return l=h.previousSibling,l&&!l.hasChildNodes()&&e.isBlock(l)?l.innerHTML="":l=null,h.innerHTML="<span></span><span></span>",d.moveToElementText(h.lastChild),d.select(),e.doc.selection.clear(),h.innerHTML="",void(l&&(l.innerHTML=""));i=e.nodeIndex(h),h=h.parentNode}if(i==k-1)try{if(n=h.childNodes[i],g=p.createControlRange(),g.addElement(n),g.select(),m=a.getRng(),m.item&&n===m.item(0))return}catch(a){}}c(!0),c(),d.select()},this.getRangeAt=c}return a}),g("60",["3y","50"],function(a,b){var c=function(c){if(null===c)return"null";var d=typeof c;return"object"===d&&a.prototype.isPrototypeOf(c)?"array":"object"===d&&b.prototype.isPrototypeOf(c)?"string":d},d=function(a){return function(b){return c(b)===a}};return{isString:d("string"),isObject:d("object"),isArray:d("array"),isNull:d("null"),isBoolean:d("boolean"),isUndefined:d("undefined"),isFunction:d("function"),isNumber:d("number")}}),g("6m",["3x","1m","3y","3z"],function(a,b,c,d){return function(){var e=arguments;return function(){for(var f=new c(arguments.length),g=0;g<f.length;g++)f[g]=arguments[g];if(e.length!==f.length)throw new d('Wrong number of arguments to struct. Expected "['+e.length+']", got '+f.length+" arguments");var h={};return a.each(e,function(a,c){h[a]=b.constant(f[c])}),h}}}),g("63",["4z","5s"],function(a,b){var c=function(){var a=b.keys,c=function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b};return void 0===a?c:a}(),d=function(a,b){for(var d=c(a),e=0,f=d.length;e<f;e++){var g=d[e],h=a[g];b(h,g,a)}},e=function(a,b){return f(a,function(a,c,d){return{k:c,v:b(a,c,d)}})},f=function(a,b){var c={};return d(a,function(d,e){var f=b(d,e,a);c[f.k]=f.v}),c},g=function(a,b){var c={},e={};return d(a,function(a,d){var f=b(a,d)?c:e;f[d]=a}),{t:c,f:e}},h=function(a,b){var c=[];return d(a,function(a,d){c.push(b(a,d))}),c},i=function(b,d){for(var e=c(b),f=0,g=e.length;f<g;f++){var h=e[f],i=b[h];if(d(i,h,b))return a.some(i)}return a.none()},j=function(a){return h(a,function(a){return a})},k=function(a){return j(a).length};return{bifilter:g,each:d,map:e,mapToArray:h,tupleMap:f,find:i,keys:c,values:j,size:k}}),g("78",["3x","60","3z"],function(a,b,c){var d=function(a){return a.slice(0).sort()},e=function(a,b){throw new c("All required keys ("+d(a).join(", ")+") were not specified. Specified keys were: "+d(b).join(", ")+".")},f=function(a){throw new c("Unsupported keys for object: "+d(a).join(", "))},g=function(d,e){if(!b.isArray(e))throw new c("The "+d+" fields must be an array. Was: "+e+".");a.each(e,function(a){if(!b.isString(a))throw new c("The value "+a+" in the "+d+" fields was not a string.")})},h=function(a,b){throw new c("All values need to be of type: "+b+". Keys ("+d(a).join(", ")+") were not.")},i=function(b){var e=d(b),f=a.find(e,function(a,b){return b<e.length-1&&a===e[b+1]});f.each(function(a){throw new c("The field: "+a+" occurs more than once in the combined fields: ["+e.join(", ")+"].")})};return{sort:d,reqMessage:e,unsuppMessage:f,validateStrArr:g,invalidTypeMessage:h,checkDupes:i}}),g("6n",["3x","1m","63","4z","78","3z","5s"],function(a,b,c,d,e,f,g){return function(h,i){var j=h.concat(i);if(0===j.length)throw new f("You must specify at least one required or optional field."); -return e.validateStrArr("required",h),e.validateStrArr("optional",i),e.checkDupes(j),function(f){var k=c.keys(f),l=a.forall(h,function(b){return a.contains(k,b)});l||e.reqMessage(h,k);var m=a.filter(k,function(b){return!a.contains(j,b)});m.length>0&&e.unsuppMessage(m);var n={};return a.each(h,function(a){n[a]=b.constant(f[a])}),a.each(i,function(a){n[a]=b.constant(g.prototype.hasOwnProperty.call(f,a)?d.some(f[a]):d.none())}),n}}}),g("61",["6m","6n"],function(a,b){return{immutable:a,immutableBag:b}}),g("62",[],function(){var a=function(a,b){var c=[],d=function(a){return c.push(a),b(a)},e=b(a);do e=e.bind(d);while(e.isSome());return c};return{toArray:a}}),g("59",["60","3x","1m","4z","61","62","1q","1r"],function(a,b,c,d,e,f,g,h){var i=function(a){return h.fromDom(a.dom().ownerDocument)},j=function(a){var b=i(a);return h.fromDom(b.dom().documentElement)},k=function(a){var b=a.dom(),c=b.ownerDocument.defaultView;return h.fromDom(c)},l=function(a){var b=a.dom();return d.from(b.parentNode).map(h.fromDom)},m=function(a){return l(a).bind(function(c){var d=u(c);return b.findIndex(d,function(b){return g.eq(a,b)})})},n=function(b,d){for(var e=a.isFunction(d)?d:c.constant(!1),f=b.dom(),g=[];null!==f.parentNode&&void 0!==f.parentNode;){var i=f.parentNode,j=h.fromDom(i);if(g.push(j),e(j)===!0)break;f=i}return g},o=function(a){var c=function(c){return b.filter(c,function(b){return!g.eq(a,b)})};return l(a).map(u).map(c).getOr([])},p=function(a){var b=a.dom();return d.from(b.offsetParent).map(h.fromDom)},q=function(a){var b=a.dom();return d.from(b.previousSibling).map(h.fromDom)},r=function(a){var b=a.dom();return d.from(b.nextSibling).map(h.fromDom)},s=function(a){return b.reverse(f.toArray(a,q))},t=function(a){return f.toArray(a,r)},u=function(a){var c=a.dom();return b.map(c.childNodes,h.fromDom)},v=function(a,b){var c=a.dom().childNodes;return d.from(c[b]).map(h.fromDom)},w=function(a){return v(a,0)},x=function(a){return v(a,a.dom().childNodes.length-1)},y=e.immutable("element","offset"),z=function(a,b){var c=u(a);return c.length>0&&b<c.length?y(c[b],0):y(a,b)};return{owner:i,defaultView:k,documentElement:j,parent:l,findIndex:m,parents:n,siblings:o,prevSibling:q,offsetParent:p,prevSiblings:s,nextSibling:r,nextSiblings:t,children:u,child:v,firstChild:w,lastChild:x,leaf:z}}),g("4a",["59"],function(a){var b=function(b,c){var d=a.parent(b);d.each(function(a){a.dom().insertBefore(c.dom(),b.dom())})},c=function(c,d){var f=a.nextSibling(c);f.fold(function(){var b=a.parent(c);b.each(function(a){e(a,d)})},function(a){b(a,d)})},d=function(b,c){var d=a.firstChild(b);d.fold(function(){e(b,c)},function(a){b.dom().insertBefore(c.dom(),a.dom())})},e=function(a,b){a.dom().appendChild(b.dom())},f=function(c,d,f){a.child(c,f).fold(function(){e(c,d)},function(a){b(a,d)})},g=function(a,c){b(a,c),e(c,a)};return{before:b,after:c,prepend:d,append:e,appendAt:f,wrap:g}}),g("4d",["58"],function(a){var b=function(a){var b=a.dom().nodeName;return b.toLowerCase()},c=function(a){return a.dom().nodeType},d=function(a){return a.dom().nodeValue},e=function(a){return function(b){return c(b)===a}},f=function(d){return c(d)===a.COMMENT||"#comment"===b(d)},g=e(a.ELEMENT),h=e(a.TEXT),i=e(a.DOCUMENT);return{name:b,type:c,value:d,isElement:g,isText:h,isDocument:i,isComment:f}}),g("5a",["60","3x","63","4d","3z","49"],function(a,b,c,d,e,f){var g=function(b,c,d){if(!(a.isString(d)||a.isBoolean(d)||a.isNumber(d)))throw f.error("Invalid call to Attr.set. Key ",c,":: Value ",d,":: Element ",b),new e("Attribute value was not simple");b.setAttribute(c,d+"")},h=function(a,b,c){g(a.dom(),b,c)},i=function(a,b){var d=a.dom();c.each(b,function(a,b){g(d,b,a)})},j=function(a,b){var c=a.dom().getAttribute(b);return null===c?void 0:c},k=function(a,b){var c=a.dom();return!(!c||!c.hasAttribute)&&c.hasAttribute(b)},l=function(a,b){a.dom().removeAttribute(b)},m=function(a){var b=a.dom().attributes;return void 0===b||null===b||0===b.length},n=function(a){return b.foldl(a.dom().attributes,function(a,b){return a[b.name]=b.value,a},{})},o=function(a,b,c){k(a,c)&&!k(b,c)&&h(b,c,j(a,c))},p=function(a,c,e){d.isElement(a)&&d.isElement(c)&&b.each(e,function(b){o(a,c,b)})};return{clone:n,set:h,setAll:i,get:j,has:k,remove:l,hasNone:m,transfer:p}}),g("5b",["3x","4a"],function(a,b){var c=function(c,d){a.each(d,function(a){b.before(c,a)})},d=function(c,d){a.each(d,function(a,e){var f=0===e?c:d[e-1];b.after(f,a)})},e=function(c,d){a.each(d.slice().reverse(),function(a){b.prepend(c,a)})},f=function(c,d){a.each(d,function(a){b.append(c,a)})};return{before:c,after:d,prepend:e,append:f}}),g("5c",["3x","5b","59"],function(a,b,c){var d=function(b){b.dom().textContent="",a.each(c.children(b),function(a){e(a)})},e=function(a){var b=a.dom();null!==b.parentNode&&b.parentNode.removeChild(b)},f=function(a){var d=c.children(a);d.length>0&&b.before(a,d),e(a)};return{empty:d,remove:e,unwrap:f}}),g("4b",["5a","1r","4a","5b","5c","59"],function(a,b,c,d,e,f){var g=function(a,c){return b.fromDom(a.dom().cloneNode(c))},h=function(a){return g(a,!1)},i=function(a){return g(a,!0)},j=function(c,d){var e=b.fromTag(d),f=a.clone(c);return a.setAll(e,f),e},k=function(a,b){var c=j(a,b),e=f.children(i(a));return d.append(c,e),c},l=function(a,b){var g=j(a,b);c.before(a,g);var h=f.children(a);return d.append(g,h),e.remove(a),g};return{shallow:h,shallowAs:j,deep:i,copy:k,mutate:l}}),g("4c",["3x","1r","22"],function(a,b,c){var d=function(d,e){var f=e||c,g=f.createDocumentFragment();return a.each(d,function(a){g.appendChild(a.dom())}),b.fromDom(g)};return{fromElements:d}}),g("4e",["3x","1m","4d"],function(a,b,c){var d=["article","aside","details","div","dt","figcaption","footer","form","fieldset","header","hgroup","html","main","nav","section","summary","body","p","dl","multicol","dd","figure","address","center","blockquote","h1","h2","h3","h4","h5","h6","listing","xmp","pre","plaintext","menu","dir","ul","ol","li","hr","table","tbody","thead","tfoot","th","tr","td","caption"],e=["area","base","basefont","br","col","frame","hr","img","input","isindex","link","meta","param","embed","source","wbr","track"],f=["td","th"],g=["h1","h2","h3","h4","h5","h6","p","div","address","pre","form","blockquote","center","dir","fieldset","header","footer","article","section","hgroup","aside","nav","figure"],h=["h1","h2","h3","h4","h5","h6"],i=function(d){var e;return function(f){return e=e?e:a.mapToObject(d,b.constant(!0)),e.hasOwnProperty(c.name(f))}},j=i(h),k=i(d),l=function(a){return c.isElement(a)&&!k(a)};return{isBlock:k,isInline:l,isHeading:j,isTextBlock:i(g),isVoid:i(e),isTableCell:i(f)}}),g("4f",["1m","1q","59"],function(a,b,c){var d=function(a){return a.slice(0,-1)},e=function(a,e,f){return b.contains(e,a)?d(c.parents(a,function(a){return f(a)||b.eq(a,e)})):[]},f=function(b,c){return e(b,c,a.constant(!1))},g=function(a,b){return[a].concat(f(a,b))};return{parentsUntil:e,parents:f,parentsAndSelf:g}}),g("5d",["4z"],function(a){var b=function(a){for(var b=[],c=function(a){b.push(a)},d=0;d<a.length;d++)a[d].each(c);return b},c=function(b,c){for(var d=0;d<b.length;d++){var e=c(b[d],d);if(e.isSome())return e}return a.none()},d=function(b,c){for(var d=[],e=0;e<b.length;e++){var f=b[e];if(!f.isSome())return a.none();d.push(f.getOrDie())}return a.some(c.apply(null,d))};return{cat:b,findMap:c,liftN:d}}),g("4g",["3x","1m","4z","5d","1q","1r","4d","59","1j"],function(a,b,c,d,e,f,g,h,i){var j=function(a){var b=a.startContainer,d=a.startOffset;return i.isText(b)?0===d?c.some(f.fromDom(b)):c.none():c.from(b.childNodes[d]).map(f.fromDom)},k=function(a){var b=a.endContainer,d=a.endOffset;return i.isText(b)?d===b.data.length?c.some(f.fromDom(b)):c.none():c.from(b.childNodes[d-1]).map(f.fromDom)},l=function(a){return h.firstChild(a).fold(b.constant([a]),function(b){return[a].concat(l(b))})},m=function(a){return h.lastChild(a).fold(b.constant([a]),function(b){return"br"===g.name(b)?h.prevSibling(b).map(function(b){return[a].concat(m(b))}).getOr([]):[a].concat(m(b))})},n=function(c,f){return d.liftN([j(f),k(f)],function(d,f){var g=a.find(l(c),b.curry(e.eq,d)),h=a.find(m(c),b.curry(e.eq,f));return g.isSome()&&h.isSome()}).getOr(!1)};return{hasAllContentsSelected:n}}),g("1u",["3x","1m","4a","4b","1r","4c","4d","4e","4f","4g"],function(a,b,c,d,e,f,g,h,i,j){var k=function(b){return a.find(b,function(a){return"ul"===g.name(a)||"ol"===g.name(a)})},l=function(c,d){return a.find(c,function(a){return"li"===g.name(a)&&j.hasAllContentsSelected(a,d)}).fold(b.constant([]),function(a){return k(c).map(function(a){return[e.fromTag("li"),e.fromTag(g.name(a))]}).getOr([])})},m=function(b,d){var e=a.foldl(d,function(a,b){return c.append(b,a),b},b);return d.length>0?f.fromElements([e]):e},n=function(b,c){var f=i.parentsAndSelf(e.fromDom(c.commonAncestorContainer),e.fromDom(b)),g=a.filter(f,function(a){return h.isInline(a)||h.isHeading(a)}),j=l(f,c);return a.map(g.concat(j),d.shallow)},o=function(a,b){return m(e.fromDom(b.cloneContents()),n(a,b))},p=function(a,b){return b.collapsed?f.fromElements([]):o(a,b)};return{read:p}}),g("s",["1q","1r","1p","r","q","1j","h","1s","c","1t","6","1u","1l","9"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){function o(a,b,c,f){var g=this;g.dom=a,g.win=b,g.serializer=c,g.editor=f,g.bookmarkManager=new d(g),g.controlSelection=new e(g,f),g.win.getSelection||(g.tridentSel=new j(g))}var p=n.each,q=n.trim,r=k.ie,s=function(c){return!(!c||!c.ownerDocument)&&a.contains(b.fromDom(c.ownerDocument),b.fromDom(c))},t=function(a){return!!a&&(!!a.select||s(a.startContainer)&&s(a.endContainer))};return o.prototype={setCursorLocation:function(a,b){var c=this,d=c.dom.createRng();a?(d.setStart(a,b),d.setEnd(a,b),c.setRng(d),c.collapse(!1)):(c._moveEndPoint(d,c.editor.getBody(),!0),c.setRng(d))},getContent:function(a){var b,c,d,e=this,f=e.getRng(),g=e.dom.create("body"),h=e.getSel();return a=a||{},b=c="",a.get=!0,a.format=a.format||"html",a.selection=!0,e.editor.fire("BeforeGetContent",a),"text"===a.format?e.isCollapsed()?"":m.trim(f.text||(h.toString?h.toString():"")):(f.cloneContents?(d=a.contextual?l.read(e.editor.getBody(),f).dom():f.cloneContents(),d&&g.appendChild(d)):void 0!==f.item||void 0!==f.htmlText?(g.innerHTML="<br>"+(f.item?f.item(0).outerHTML:f.htmlText),g.removeChild(g.firstChild)):g.innerHTML=f.toString(),/^\s/.test(g.innerHTML)&&(b=" "),/\s+$/.test(g.innerHTML)&&(c=" "),a.getInner=!0,a.content=e.isCollapsed()?"":b+e.serializer.serialize(g,a)+c,e.editor.fire("GetContent",a),a.content)},setContent:function(a,b){var c,d,e,f=this,g=f.getRng(),h=f.win.document;if(b=b||{format:"html"},b.set=!0,b.selection=!0,b.content=a,b.no_events||f.editor.fire("BeforeSetContent",b),a=b.content,g.insertNode){a+='<span id="__caret">_</span>',g.startContainer==h&&g.endContainer==h?h.body.innerHTML=a:(g.deleteContents(),0===h.body.childNodes.length?h.body.innerHTML=a:g.createContextualFragment?g.insertNode(g.createContextualFragment(a)):(d=h.createDocumentFragment(),e=h.createElement("div"),d.appendChild(e),e.outerHTML=a,g.insertNode(d))),c=f.dom.get("__caret"),g=h.createRange(),g.setStartBefore(c),g.setEndBefore(c),f.setRng(g),f.dom.remove("__caret");try{f.setRng(g)}catch(a){}}else g.item&&(h.execCommand("Delete",!1,null),g=f.getRng()),/^\s+/.test(a)?(g.pasteHTML('<span id="__mce_tmp">_</span>'+a),f.dom.remove("__mce_tmp")):g.pasteHTML(a);b.no_events||f.editor.fire("SetContent",b)},getStart:function(a){var b,c,d,e,f=this,g=f.getRng();if(g.duplicate||g.item){if(g.item)return g.item(0);for(d=g.duplicate(),d.collapse(1),b=d.parentElement(),b.ownerDocument!==f.dom.doc&&(b=f.dom.getRoot()),c=e=g.parentElement();e=e.parentNode;)if(e==b){b=c;break}return b}return b=g.startContainer,1==b.nodeType&&b.hasChildNodes()&&(a&&g.collapsed||(b=b.childNodes[Math.min(b.childNodes.length-1,g.startOffset)])),b&&3==b.nodeType?b.parentNode:b},getEnd:function(a){var b,c,d=this,e=d.getRng();return e.duplicate||e.item?e.item?e.item(0):(e=e.duplicate(),e.collapse(0),b=e.parentElement(),b.ownerDocument!==d.dom.doc&&(b=d.dom.getRoot()),b&&"BODY"==b.nodeName?b.lastChild||b:b):(b=e.endContainer,c=e.endOffset,1==b.nodeType&&b.hasChildNodes()&&(a&&e.collapsed||(b=b.childNodes[c>0?c-1:c])),b&&3==b.nodeType?b.parentNode:b)},getBookmark:function(a,b){return this.bookmarkManager.getBookmark(a,b)},moveToBookmark:function(a){return this.bookmarkManager.moveToBookmark(a)},select:function(a,b){var c,d=this,e=d.dom,f=e.createRng();if(d.lastFocusBookmark=null,a){if(!b&&d.controlSelection.controlSelect(a))return;c=e.nodeIndex(a),f.setStart(a.parentNode,c),f.setEnd(a.parentNode,c+1),b&&(d._moveEndPoint(f,a,!0),d._moveEndPoint(f,a)),d.setRng(f)}return a},isCollapsed:function(){var a=this,b=a.getRng(),c=a.getSel();return!(!b||b.item)&&(b.compareEndPoints?0===b.compareEndPoints("StartToEnd",b):!c||b.collapsed)},collapse:function(a){var b,c=this,d=c.getRng();d.item&&(b=d.item(0),d=c.win.document.body.createTextRange(),d.moveToElementText(b)),d.collapse(!!a),c.setRng(d)},getSel:function(){var a=this.win;return a.getSelection?a.getSelection():a.document.selection},getRng:function(a){function b(a,b,c){try{return b.compareBoundaryPoints(a,c)}catch(a){return-1}}var c,d,e,f,g,h,i=this;if(!i.win)return null;if(f=i.win.document,"undefined"==typeof f||null===f)return null;if(!a&&i.lastFocusBookmark){var j=i.lastFocusBookmark;return j.startContainer?(d=f.createRange(),d.setStart(j.startContainer,j.startOffset),d.setEnd(j.endContainer,j.endOffset)):d=j,d}if(a&&i.tridentSel)return i.tridentSel.getRangeAt(0);try{(c=i.getSel())&&(d=c.rangeCount>0?c.getRangeAt(0):c.createRange?c.createRange():f.createRange())}catch(a){}if(h=i.editor.fire("GetSelectionRange",{range:d}),h.range!==d)return h.range;if(r&&d&&d.setStart&&f.selection){try{g=f.selection.createRange()}catch(a){}g&&g.item&&(e=g.item(0),d=f.createRange(),d.setStartBefore(e),d.setEndAfter(e))}return d||(d=f.createRange?f.createRange():f.body.createTextRange()),d.setStart&&9===d.startContainer.nodeType&&d.collapsed&&(e=i.dom.getRoot(),d.setStart(e,0),d.setEnd(e,0)),i.selectedRange&&i.explicitRange&&(0===b(d.START_TO_START,d,i.selectedRange)&&0===b(d.END_TO_END,d,i.selectedRange)?d=i.explicitRange:(i.selectedRange=null,i.explicitRange=null)),d},setRng:function(a,b){var c,d,e,f=this;if(t(a))if(a.select){f.explicitRange=null;try{a.select()}catch(a){}}else if(f.tridentSel){if(a.cloneRange)try{f.tridentSel.addRange(a)}catch(a){}}else{if(c=f.getSel(),e=f.editor.fire("SetSelectionRange",{range:a,forward:b}),a=e.range,c){f.explicitRange=a;try{c.removeAllRanges(),c.addRange(a)}catch(a){}b===!1&&c.extend&&(c.collapse(a.endContainer,a.endOffset),c.extend(a.startContainer,a.startOffset)),f.selectedRange=c.rangeCount>0?c.getRangeAt(0):null}a.collapsed||a.startContainer!==a.endContainer||!c.setBaseAndExtent||k.ie||a.endOffset-a.startOffset<2&&a.startContainer.hasChildNodes()&&(d=a.startContainer.childNodes[a.startOffset],d&&"IMG"===d.tagName&&(c.setBaseAndExtent(a.startContainer,a.startOffset,a.endContainer,a.endOffset),c.anchorNode===a.startContainer&&c.focusNode===a.endContainer||c.setBaseAndExtent(d,0,d,1))),f.editor.fire("AfterSetSelectionRange",{range:a,forward:b})}},setNode:function(a){var b=this;return b.setContent(b.dom.getOuterHTML(a)),a},getNode:function(){function a(a,b){for(var c=a;a&&3===a.nodeType&&0===a.length;)a=b?a.nextSibling:a.previousSibling;return a||c}var b,c,d,e,f,g=this,h=g.getRng(),i=g.dom.getRoot();return h?(c=h.startContainer,d=h.endContainer,e=h.startOffset,f=h.endOffset,h.setStart?(b=h.commonAncestorContainer,!h.collapsed&&(c==d&&f-e<2&&c.hasChildNodes()&&(b=c.childNodes[e]),3===c.nodeType&&3===d.nodeType&&(c=c.length===e?a(c.nextSibling,!0):c.parentNode,d=0===f?a(d.previousSibling,!1):d.parentNode,c&&c===d))?c:b&&3==b.nodeType?b.parentNode:b):(b=h.item?h.item(0):h.parentElement(),b.ownerDocument!==g.win.document&&(b=i),b)):i},getSelectedBlocks:function(a,b){var c,d,e=this,f=e.dom,g=[];if(d=f.getRoot(),a=f.getParent(a||e.getStart(),f.isBlock),b=f.getParent(b||e.getEnd(),f.isBlock),a&&a!=d&&g.push(a),a&&b&&a!=b){c=a;for(var h=new i(a,d);(c=h.next())&&c!=b;)f.isBlock(c)&&g.push(c)}return b&&a!=b&&b!=d&&g.push(b),g},isForward:function(){var a,b,c=this.dom,d=this.getSel();return!(d&&d.anchorNode&&d.focusNode)||(a=c.createRng(),a.setStart(d.anchorNode,d.anchorOffset),a.collapse(!0),b=c.createRng(),b.setStart(d.focusNode,d.focusOffset),b.collapse(!0),a.compareBoundaryPoints(a.START_TO_START,b)<=0)},normalize:function(){var a=this,b=a.getRng();return k.range&&new g(a.dom).normalize(b)&&a.setRng(b,a.isForward()),b},selectorChanged:function(a,b){var c,d=this;return d.selectorChangedData||(d.selectorChangedData={},c={},d.editor.on("NodeChange",function(a){var b=a.element,e=d.dom,f=e.getParents(b,null,e.getRoot()),g={};p(d.selectorChangedData,function(a,b){p(f,function(d){if(e.is(d,b))return c[b]||(p(a,function(a){a(!0,{node:d,selector:b,parents:f})}),c[b]=a),g[b]=a,!1})}),p(c,function(a,d){g[d]||(delete c[d],p(a,function(a){a(!1,{node:b,selector:d,parents:f})}))})})),d.selectorChangedData[a]||(d.selectorChangedData[a]=[]),d.selectorChangedData[a].push(b),d},getScrollContainer:function(){for(var a,b=this.dom.getRoot();b&&"BODY"!=b.nodeName;){if(b.scrollHeight>b.clientHeight){a=b;break}b=b.parentNode}return a},scrollIntoView:function(a,b){h.scrollIntoView(this.editor,a,b)},placeCaretAt:function(a,b){this.setRng(g.getCaretRangeFromPoint(a,b,this.editor.getDoc()))},_moveEndPoint:function(a,b,c){var d=b,e=new i(b,d),f=this.dom.schema.getNonEmptyElements();do{if(3==b.nodeType&&0!==q(b.nodeValue).length)return void(c?a.setStart(b,0):a.setEnd(b,b.nodeValue.length));if(f[b.nodeName]&&!/^(TD|TH)$/.test(b.nodeName))return void(c?a.setStartBefore(b):"BR"==b.nodeName?a.setEndBefore(b):a.setEndAfter(b));if(k.ie&&k.ie<11&&this.dom.isBlock(b)&&this.dom.isEmpty(b))return void(c?a.setStart(b,0):a.setEnd(b,0))}while(b=c?e.next():e.prev());"BODY"==d.nodeName&&(c?a.setStart(d,0):a.setEnd(d,d.childNodes.length))},getBoundingClientRect:function(){var a=this.getRng();return a.collapsed?c.fromRangeStart(a).getClientRects()[0]:a.getBoundingClientRect()},destroy:function(){this.win=null,this.controlSelection.destroy()}},o}),g("1v",["r","9"],function(a,b){function c(b){this.compare=function(c,e){function f(a){var c={};return d(b.getAttribs(a),function(d){var e=d.nodeName.toLowerCase();0!==e.indexOf("_")&&"style"!==e&&0!==e.indexOf("data-")&&(c[e]=b.getAttrib(a,e))}),c}function g(a,b){var c,d;for(d in a)if(a.hasOwnProperty(d)){if(c=b[d],"undefined"==typeof c)return!1;if(a[d]!=c)return!1;delete b[d]}for(d in b)if(b.hasOwnProperty(d))return!1;return!0}return c.nodeName==e.nodeName&&(!!g(f(c),f(e))&&(!!g(b.parseStyle(b.getAttrib(c,"style")),b.parseStyle(b.getAttrib(e,"style")))&&(!a.isBookmarkNode(c)&&!a.isBookmarkNode(e))))}}var d=b.each;return c}),g("1x",["e","9","j"],function(a,b,c){function d(a,d){function e(a,b){b.classes.length&&j.addClass(a,b.classes.join(" ")),j.setAttribs(a,b.attrs)}function f(a){var b;return k="string"==typeof a?{name:a,classes:[],attrs:{}}:a,b=j.create(k.name),e(b,k),b}function g(a,c){var d="string"!=typeof a?a.nodeName.toLowerCase():a,e=m.getElementRule(d),f=e&&e.parentsRequired;return!(!f||!f.length)&&(c&&b.inArray(f,c)!==-1?c:f[0])}function h(a,c,d){var e,i,k,l=c.length>0&&c[0],m=l&&l.name;if(k=g(a,m))m==k?(i=c[0],c=c.slice(1)):i=k;else if(l)i=c[0],c=c.slice(1);else if(!d)return a;return i&&(e=f(i),e.appendChild(a)),d&&(e||(e=j.create("div"),e.appendChild(a)),b.each(d,function(b){var c=f(b);e.insertBefore(c,a)})),h(e,c,i&&i.siblings)}var i,k,l,m=d&&d.schema||new c({});return a&&a.length?(k=a[0],i=f(k),l=j.create("div"),l.appendChild(h(i,a.slice(1),k.siblings)),l):""}function e(a,b){return d(g(a),b)}function f(a){var c,d={classes:[],attrs:{}};return a=d.selector=b.trim(a),"*"!==a&&(c=a.replace(/(?:([#\.]|::?)([\w\-]+)|(\[)([^\]]+)\]?)/g,function(a,c,e,f,g){switch(c){case"#":d.attrs.id=e;break;case".":d.classes.push(e);break;case":":b.inArray("checked disabled enabled read-only required".split(" "),e)!==-1&&(d.attrs[e]=e)}if("["==f){var h=g.match(/([\w\-]+)(?:\=\"([^\"]+))?/);h&&(d.attrs[h[1]]=h[2])}return""})),d.name=c||"div",d}function g(a){return a&&"string"==typeof a?(a=a.split(/\s*,\s*/)[0],a=a.replace(/\s*(~\+|~|\+|>)\s*/g,"$1"),b.map(a.split(/(?:>|\s+(?![^\[\]]+\]))/),function(a){var c=b.map(a.split(/(?:~\+|~|\+)/),f),d=c.pop();return c.length&&(d.siblings=c),d}).reverse()):[]}function h(a,b){function c(a){return a.replace(/%(\w+)/g,"")}var e,f,h,k,l,m,n="";if(m=a.settings.preview_styles,m===!1)return"";if("string"!=typeof m&&(m="font-family font-size font-weight font-style text-decoration text-transform color background-color border border-radius outline text-shadow"),"string"==typeof b){if(b=a.formatter.get(b),!b)return;b=b[0]}return"preview"in b&&(m=b.preview,m===!1)?"":(e=b.block||b.inline||"span",k=g(b.selector),k.length?(k[0].name||(k[0].name=e),e=b.selector,f=d(k,a)):f=d([e],a),h=j.select(e,f)[0]||f.firstChild,i(b.styles,function(a,b){a=c(a),a&&j.setStyle(h,b,a)}),i(b.attributes,function(a,b){a=c(a),a&&j.setAttrib(h,b,a)}),i(b.classes,function(a){a=c(a),j.hasClass(h,a)||j.addClass(h,a)}),a.fire("PreviewFormats"),j.setStyles(f,{position:"absolute",left:-65535}),a.getBody().appendChild(f),l=j.getStyle(a.getBody(),"fontSize",!0),l=/px$/.test(l)?parseInt(l,10):0,i(m.split(" "),function(b){var c=j.getStyle(h,b,!0);if(!("background-color"==b&&/transparent|rgba\s*\([^)]+,\s*0\)/.test(c)&&(c=j.getStyle(a.getBody(),b,!0),"#ffffff"==j.toHex(c).toLowerCase())||"color"==b&&"#000000"==j.toHex(c).toLowerCase())){if("font-size"==b&&/em|%$/.test(c)){if(0===l)return;c=parseFloat(c,10)/(/%$/.test(c)?100:1),c=c*l+"px"}"border"==b&&c&&(n+="padding:0 2px;"),n+=b+":"+c+";"}}),a.fire("AfterPreviewFormats"),j.remove(f),n)}var i=b.each,j=a.DOM;return{getCssText:h,parseSelector:g,selectorToHtml:e}}),g("1y",["1g","1j","a"],function(a,b,c){function d(a,b){var c=f[a];c||(f[a]=c=[]),f[a].push(b)}function e(a,b){h(f[a],function(a){a(b)})}var f={},g=a.filter,h=a.each;return d("pre",function(d){function e(b){return i(b.previousSibling)&&a.indexOf(j,b.previousSibling)!=-1}function f(a,b){c(b).remove(),c(a).append("<br><br>").append(b.childNodes)}var i,j,k=d.selection.getRng();i=b.matchNodeNames("pre"),k.collapsed||(j=d.selection.getSelectedBlocks(),h(g(g(j,i),e),function(a){f(a.previousSibling,a)}))}),{postProcess:e}}),g("t",["c","h","r","1v","1j","1w","9","1x","1y"],function(a,b,c,d,e,f,g,h,i){return function(j){function k(a){return a.nodeType&&(a=a.nodeName),!!j.schema.getTextBlockElements()[a.toLowerCase()]}function l(a){return/^(TH|TD)$/.test(a.nodeName)}function m(a){return a&&/^(IMG)$/.test(a.nodeName)}function n(a,b){return da.getParents(a,b,da.getRoot())}function o(a){return 1===a.nodeType&&"_mce_caret"===a.id}function p(){s({valigntop:[{selector:"td,th",styles:{verticalAlign:"top"}}],valignmiddle:[{selector:"td,th",styles:{verticalAlign:"middle"}}],valignbottom:[{selector:"td,th",styles:{verticalAlign:"bottom"}}],alignleft:[{selector:"figure.image",collapsed:!1,classes:"align-left",ceFalseOverride:!0,preview:"font-family font-size"},{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"left"},inherit:!1,preview:!1,defaultBlock:"div"},{selector:"img,table",collapsed:!1,styles:{"float":"left"},preview:"font-family font-size"}],aligncenter:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"center"},inherit:!1,preview:!1,defaultBlock:"div"},{selector:"figure.image",collapsed:!1,classes:"align-center",ceFalseOverride:!0,preview:"font-family font-size"},{selector:"img",collapsed:!1,styles:{display:"block",marginLeft:"auto",marginRight:"auto"},preview:!1},{selector:"table",collapsed:!1,styles:{marginLeft:"auto",marginRight:"auto"},preview:"font-family font-size"}],alignright:[{selector:"figure.image",collapsed:!1,classes:"align-right",ceFalseOverride:!0,preview:"font-family font-size"},{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"right"},inherit:!1,preview:"font-family font-size",defaultBlock:"div"},{selector:"img,table",collapsed:!1,styles:{"float":"right"},preview:"font-family font-size"}],alignjustify:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"justify"},inherit:!1,defaultBlock:"div",preview:"font-family font-size"}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:!0},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:!0},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},links:!0,remove_similar:!0,clear_child_styles:!0},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},links:!0,remove_similar:!0,clear_child_styles:!0},fontname:{inline:"span",styles:{fontFamily:"%value"},clear_child_styles:!0},fontsize:{inline:"span",styles:{fontSize:"%value"},clear_child_styles:!0},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},code:{inline:"code"},link:{inline:"a",selector:"a",remove:"all",split:!0,deep:!0,onmatch:function(){return!0},onformat:function(a,b,c){qa(c,function(b,c){da.setAttrib(a,c,b)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins",remove:"all",split:!0,expand:!1,block_expand:!0,deep:!0},{selector:"span",attributes:["style","class"],remove:"empty",split:!0,expand:!1,deep:!0},{selector:"*",attributes:["style","class"],split:!1,expand:!1,deep:!0}]}),qa("p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp".split(/\s/),function(a){s(a,{block:a,remove:"all"})}),s(j.settings.formats)}function q(){j.addShortcut("meta+b","bold_desc","Bold"),j.addShortcut("meta+i","italic_desc","Italic"),j.addShortcut("meta+u","underline_desc","Underline");for(var a=1;a<=6;a++)j.addShortcut("access+"+a,"",["FormatBlock",!1,"h"+a]);j.addShortcut("access+7","",["FormatBlock",!1,"p"]),j.addShortcut("access+8","",["FormatBlock",!1,"div"]),j.addShortcut("access+9","",["FormatBlock",!1,"address"])}function r(a){return a?ca[a]:ca}function s(a,b){a&&("string"!=typeof a?qa(a,function(a,b){s(b,a)}):(b=b.length?b:[b],qa(b,function(a){a.deep===_&&(a.deep=!a.selector),a.split===_&&(a.split=!a.selector||a.inline),a.remove===_&&a.selector&&!a.inline&&(a.remove="none"),a.selector&&a.inline&&(a.mixed=!0,a.block_expand=!0),"string"==typeof a.classes&&(a.classes=a.classes.split(/\s+/))}),ca[a]=b))}function t(a){return a&&ca[a]&&delete ca[a],ca}function u(a,b){var c=r(b);if(c)for(var d=0;d<c.length;d++)if(c[d].inherit===!1&&da.is(a,c[d].selector))return!0;return!1}function v(a){var b;return j.dom.getParent(a,function(a){return b=j.dom.getStyle(a,"text-decoration"),b&&"none"!==b}),b}function w(a){var b;1===a.nodeType&&a.parentNode&&1===a.parentNode.nodeType&&(b=v(a.parentNode),j.dom.getStyle(a,"color")&&b?j.dom.setStyle(a,"text-decoration",b):j.dom.getStyle(a,"text-decoration")===b&&j.dom.setStyle(a,"text-decoration",null))}function x(b,c,d){function e(a,b){if(b=b||p,a){if(b.onformat&&b.onformat(a,b,c,d),qa(b.styles,function(b,d){da.setStyle(a,d,O(b,c))}),b.styles){var e=da.getAttrib(a,"style");e&&a.setAttribute("data-mce-style",e)}qa(b.attributes,function(b,d){da.setAttrib(a,d,O(b,c))}),qa(b.classes,function(b){b=O(b,c),da.hasClass(a,b)||da.addClass(a,b)})}}function f(a,b){var c=!1;return!!p.selector&&(qa(a,function(a){if(!("collapsed"in a&&a.collapsed!==q))return da.is(b,a.selector)&&!o(b)?(e(b,a),c=!0,!1):void 0}),c)}function g(){function b(b,c){var e=new a(c);for(d=e.prev2();d;d=e.prev2()){if(3==d.nodeType&&d.data.length>0)return d;if(d.childNodes.length>1||d==b||"BR"==d.tagName)return d}}var c=j.selection.getRng(),e=c.startContainer,f=c.endContainer;if(e!=f&&0===c.endOffset){var g=b(e,f),h=3==g.nodeType?g.data.length:g.childNodes.length;c.setEnd(g,h)}return c}function h(a,d,g){var h,i,j=[],l=!0;h=p.inline||p.block,i=da.create(h),e(i),fa.walk(a,function(a){function d(a){var q,r,s,t;if(t=l,q=a.nodeName.toLowerCase(),r=a.parentNode.nodeName.toLowerCase(),1===a.nodeType&&oa(a)&&(t=l,l="true"===oa(a),s=!0),H(q,"br"))return m=0,void(p.block&&da.remove(a));if(p.wrapper&&A(a,b,c))return void(m=0);if(l&&!s&&p.block&&!p.wrapper&&k(q)&&ga(r,h))return a=da.rename(a,h),e(a),j.push(a),void(m=0);if(p.selector){var u=f(n,a);if(!p.inline||u)return void(m=0)}!l||s||!ga(h,q)||!ga(r,h)||!g&&3===a.nodeType&&1===a.nodeValue.length&&65279===a.nodeValue.charCodeAt(0)||o(a)||p.inline&&ha(a)?(m=0,qa(ra(a.childNodes),d),s&&(l=t),m=0):(m||(m=da.clone(i,ma),a.parentNode.insertBefore(m,a),j.push(m)),m.appendChild(a))}var m;qa(a,d)}),p.links===!0&&qa(j,function(a){function b(a){"A"===a.nodeName&&e(a,p),qa(ra(a.childNodes),b)}b(a)}),qa(j,function(a){function d(a){var b=0;return qa(a.childNodes,function(a){P(a)||pa(a)||b++}),b}function f(a){var b=!1;return qa(a.childNodes,function(a){if(J(a))return b=a,!1}),b}function g(a,b){do{if(1!==d(a))break;if(a=f(a),!a)break;if(b(a))return a}while(a);return null}function h(a){var b,c;return b=f(a),b&&!pa(b)&&G(b,p)&&(c=da.clone(b,ma),e(c),da.replace(c,a,na),da.remove(b,1)),c||a}var i;if(i=d(a),(j.length>1||!ha(a))&&0===i)return void da.remove(a,1);if(p.inline||p.wrapper){if(p.exact||1!==i||(a=h(a)),qa(n,function(b){qa(da.select(b.inline,a),function(a){J(a)&&T(b,c,a,b.exact?a:null)}),ua(b,a)}),A(a.parentNode,b,c)&&T(p,c,a)&&(a=0),p.merge_with_parents&&da.getParent(a.parentNode,function(d){if(A(d,b,c))return T(p,c,a)&&(a=0),na}),a&&!ha(a)&&!M(a,"fontSize")){var k=g(a,K("fontSize"));k&&x("fontsize",{value:M(k,"fontSize")},a)}a&&p.merge_siblings!==!1&&(a=W(V(a),a),a=W(a,V(a,na)))}})}var l,m,n=r(b),p=n[0],q=!d&&ea.isCollapsed();if("false"!==oa(ea.getNode())){if(p){if(d)d.nodeType?f(n,d)||(m=da.createRng(),m.setStartBefore(d),m.setEndAfter(d),h(R(m,n),null,!0)):h(d,null,!0);else if(q&&p.inline&&!da.select("td[data-mce-selected],th[data-mce-selected]").length)Y("apply",b,c);else{var s=j.selection.getNode();ia||!n[0].defaultBlock||da.getParent(s,da.isBlock)||x(n[0].defaultBlock),j.selection.setRng(g()),l=ea.getBookmark(),h(R(ea.getRng(na),n),l),p.styles&&((p.styles.color||p.styles.textDecoration)&&(sa(s,w,"childNodes"),w(s)),p.styles.backgroundColor&&I(s,K("fontSize"),L("backgroundColor",O(p.styles.backgroundColor,c)))),ea.moveToBookmark(l),Z(ea.getRng(na)),j.nodeChanged()}i.postProcess(b,j)}}else{d=ea.getNode();for(var t=0,u=n.length;t<u;t++)if(n[t].ceFalseOverride&&da.is(d,n[t].selector))return void e(d,n[t])}}function y(a,b,c,d){function e(a){var c,d,f,g,h;if(1===a.nodeType&&oa(a)&&(g=s,s="true"===oa(a),h=!0),c=ra(a.childNodes),s&&!h)for(d=0,f=p.length;d<f&&!T(p[d],b,a,a);d++);if(q.deep&&c.length){for(d=0,f=c.length;d<f;d++)e(c[d]);h&&(s=g)}}function f(c){var e;return qa(n(c.parentNode).reverse(),function(c){var f;e||"_start"==c.id||"_end"==c.id||(f=A(c,a,b,d),f&&f.split!==!1&&(e=c))}),e}function g(a,c,d,e){var f,g,h,i,j,k;if(a){for(k=a.parentNode,f=c.parentNode;f&&f!=k;f=f.parentNode){for(g=da.clone(f,ma),j=0;j<p.length;j++)if(T(p[j],b,g,g)){g=0;break}g&&(h&&g.appendChild(h),i||(i=g),h=g)}!e||q.mixed&&ha(a)||(c=da.split(a,c)),h&&(d.parentNode.insertBefore(h,d),i.appendChild(d))}return c}function h(a){return g(f(a),a,a,!0)}function i(a){var b=da.get(a?"_start":"_end"),c=b[a?"firstChild":"lastChild"];return pa(c)&&(c=c[a?"firstChild":"lastChild"]),3==c.nodeType&&0===c.data.length&&(c=a?b.previousSibling||b.nextSibling:b.nextSibling||b.previousSibling),da.remove(b,!0),c}function k(a){var b,c,d=a.commonAncestorContainer;if(a=R(a,p,na),q.split){if(b=X(a,na),c=X(a),b!=c){if(/^(TR|TH|TD)$/.test(b.nodeName)&&b.firstChild&&(b="TR"==b.nodeName?b.firstChild.firstChild||b:b.firstChild||b),d&&/^T(HEAD|BODY|FOOT|R)$/.test(d.nodeName)&&l(c)&&c.firstChild&&(c=c.firstChild||c),da.isChildOf(b,c)&&!ha(c)&&!l(b)&&!l(c))return b=Q(b,"span",{id:"_start","data-mce-type":"bookmark"}),h(b),void(b=i(na));b=Q(b,"span",{ -id:"_start","data-mce-type":"bookmark"}),c=Q(c,"span",{id:"_end","data-mce-type":"bookmark"}),h(b),h(c),b=i(na),c=i()}else b=c=h(b);a.startContainer=b.parentNode?b.parentNode:b,a.startOffset=ja(b),a.endContainer=c.parentNode?c.parentNode:c,a.endOffset=ja(c)+1}fa.walk(a,function(a){qa(a,function(a){e(a),1===a.nodeType&&"underline"===j.dom.getStyle(a,"text-decoration")&&a.parentNode&&"underline"===v(a.parentNode)&&T({deep:!1,exact:!0,inline:"span",styles:{textDecoration:"underline"}},null,a)})})}var m,o,p=r(a),q=p[0],s=!0;if(c)return void(c.nodeType?(o=da.createRng(),o.setStartBefore(c),o.setEndAfter(c),k(o)):k(c));if("false"!==oa(ea.getNode()))ea.isCollapsed()&&q.inline&&!da.select("td[data-mce-selected],th[data-mce-selected]").length?Y("remove",a,b,d):(m=ea.getBookmark(),k(ea.getRng(na)),ea.moveToBookmark(m),q.inline&&B(a,b,ea.getStart())&&Z(ea.getRng(!0)),j.nodeChanged());else{c=ea.getNode();for(var t=0,u=p.length;t<u&&(!p[t].ceFalseOverride||!T(p[t],b,c,c));t++);}}function z(a,b,c){var d=r(a);!B(a,b,c)||"toggle"in d[0]&&!d[0].toggle?x(a,b,c):y(a,b,c)}function A(a,b,c,d){function e(a,b,e){var f,g,h,i=b[e];if(b.onmatch)return b.onmatch(a,b,e);if(i)if(i.length===_){for(f in i)if(i.hasOwnProperty(f)){if(g="attributes"===e?da.getAttrib(a,f):M(a,f),d&&!g&&!b.exact)return;if((!d||b.exact)&&!H(g,N(O(i[f],c),f)))return}}else for(h=0;h<i.length;h++)if("attributes"===e?da.getAttrib(a,i[h]):M(a,i[h]))return b;return b}var f,g,h,i=r(b);if(i&&a)for(g=0;g<i.length;g++)if(f=i[g],G(a,f)&&e(a,f,"attributes")&&e(a,f,"styles")){if(h=f.classes)for(g=0;g<h.length;g++)if(!da.hasClass(a,h[g]))return;return f}}function B(a,b,c){function d(c){var d=da.getRoot();return c!==d&&(c=da.getParent(c,function(c){return!!u(c,a)||(c.parentNode===d||!!A(c,a,b,!0))}),A(c,a,b))}var e;return c?d(c):(c=ea.getNode(),d(c)?na:(e=ea.getStart(),e!=c&&d(e)?na:ma))}function C(a,b){var c,d=[],e={};return c=ea.getStart(),da.getParent(c,function(c){var f,g;for(f=0;f<a.length;f++)g=a[f],!e[g]&&A(c,g,b)&&(e[g]=!0,d.push(g))},da.getRoot()),d}function D(a){var b,c,d,e,f,g=r(a);if(g)for(b=ea.getStart(),c=n(b),e=g.length-1;e>=0;e--){if(f=g[e].selector,!f||g[e].defaultBlock)return na;for(d=c.length-1;d>=0;d--)if(da.is(c[d],f))return na}return ma}function E(a,b,c){var d;return $||($={},d={},j.on("NodeChange",function(a){var b=n(a.element),c={};b=g.grep(b,function(a){return 1==a.nodeType&&!a.getAttribute("data-mce-bogus")}),qa($,function(a,e){qa(b,function(f){return A(f,e,{},a.similar)?(d[e]||(qa(a,function(a){a(!0,{node:f,format:e,parents:b})}),d[e]=a),c[e]=a,!1):!u(f,e)&&void 0})}),qa(d,function(e,f){c[f]||(delete d[f],qa(e,function(c){c(!1,{node:a.element,format:f,parents:b})}))})})),qa(a.split(","),function(a){$[a]||($[a]=[],$[a].similar=c),$[a].push(b)}),this}function F(a){return h.getCssText(j,a)}function G(a,b){return H(a,b.inline)?na:H(a,b.block)?na:b.selector?1==a.nodeType&&da.is(a,b.selector):void 0}function H(a,b){return a=a||"",b=b||"",a=""+(a.nodeName||a),b=""+(b.nodeName||b),a.toLowerCase()==b.toLowerCase()}function I(a,b,c){qa(a.childNodes,function(a){J(a)&&(b(a)&&c(a),a.hasChildNodes()&&I(a,b,c))})}function J(a){return a&&1===a.nodeType&&!pa(a)&&!o(a)&&!e.isBogus(a)}function K(a){return f.curry(function(a,b){return!(!b||!M(b,a))},a)}function L(a,b){return f.curry(function(a,b,c){da.setStyle(c,a,b)},a,b)}function M(a,b){return N(da.getStyle(a,b),b)}function N(a,b){return"color"!=b&&"backgroundColor"!=b||(a=da.toHex(a)),"fontWeight"==b&&700==a&&(a="bold"),"fontFamily"==b&&(a=a.replace(/[\'\"]/g,"").replace(/,\s+/g,",")),""+a}function O(a,b){return"string"!=typeof a?a=a(b):b&&(a=a.replace(/%(\w+)/g,function(a,c){return b[c]||a})),a}function P(a){return a&&3===a.nodeType&&/^([\t \r\n]+|)$/.test(a.nodeValue)}function Q(a,b,c){var d=da.create(b,c);return a.parentNode.insertBefore(d,a),d.appendChild(a),d}function R(b,c,d){function e(a){function b(a){return"BR"==a.nodeName&&a.getAttribute("data-mce-bogus")&&!a.nextSibling}var d,e,f,g,h;if(d=e=a?q:s,g=a?"previousSibling":"nextSibling",h=da.getRoot(),3==d.nodeType&&!P(d)&&(a?r>0:t<d.nodeValue.length))return d;for(;;){if(!c[0].block_expand&&ha(e))return e;for(f=e[g];f;f=f[g])if(!pa(f)&&!P(f)&&!b(f))return e;if(e==h||e.parentNode==h){d=e;break}e=e.parentNode}return d}function f(a,b){for(b===_&&(b=3===a.nodeType?a.length:a.childNodes.length);a&&a.hasChildNodes();)a=a.childNodes[b],a&&(b=3===a.nodeType?a.length:a.childNodes.length);return{node:a,offset:b}}function g(a){for(var b=a;b;){if(1===b.nodeType&&oa(b))return"false"===oa(b)?b:a;b=b.parentNode}return a}function h(b,c,e){function f(a,b){var c,f,g=a.nodeValue;return"undefined"==typeof b&&(b=e?g.length:0),e?(c=g.lastIndexOf(" ",b),f=g.lastIndexOf("\xa0",b),c=c>f?c:f,c===-1||d||c++):(c=g.indexOf(" ",b),f=g.indexOf("\xa0",b),c=c!==-1&&(f===-1||c<f)?c:f),c}var g,h,i,k;if(3===b.nodeType){if(i=f(b,c),i!==-1)return{container:b,offset:i};k=b}for(g=new a(b,da.getParent(b,ha)||j.getBody());h=g[e?"prev":"next"]();)if(3===h.nodeType){if(k=h,i=f(h),i!==-1)return{container:h,offset:i}}else if(ha(h))break;if(k)return c=e?0:k.length,{container:k,offset:c}}function i(a,d){var e,f,g,h;for(3==a.nodeType&&0===a.nodeValue.length&&a[d]&&(a=a[d]),e=n(a),f=0;f<e.length;f++)for(g=0;g<c.length;g++)if(h=c[g],!("collapsed"in h&&h.collapsed!==b.collapsed)&&da.is(e[f],h.selector))return e[f];return a}function l(a,b){var d,e=da.getRoot();if(c[0].wrapper||(d=da.getParent(a,c[0].block,e)),d||(d=da.getParent(3==a.nodeType?a.parentNode:a,function(a){return a!=e&&k(a)})),d&&c[0].wrapper&&(d=n(d,"ul,ol").reverse()[0]||d),!d)for(d=a;d[b]&&!ha(d[b])&&(d=d[b],!H(d,"br")););return d||a}var m,o,p,q=b.startContainer,r=b.startOffset,s=b.endContainer,t=b.endOffset;if(1==q.nodeType&&q.hasChildNodes()&&(m=q.childNodes.length-1,q=q.childNodes[r>m?m:r],3==q.nodeType&&(r=0)),1==s.nodeType&&s.hasChildNodes()&&(m=s.childNodes.length-1,s=s.childNodes[t>m?m:t-1],3==s.nodeType&&(t=s.nodeValue.length)),q=g(q),s=g(s),(pa(q.parentNode)||pa(q))&&(q=pa(q)?q:q.parentNode,q=q.nextSibling||q,3==q.nodeType&&(r=0)),(pa(s.parentNode)||pa(s))&&(s=pa(s)?s:s.parentNode,s=s.previousSibling||s,3==s.nodeType&&(t=s.length)),c[0].inline&&(b.collapsed&&(p=h(q,r,!0),p&&(q=p.container,r=p.offset),p=h(s,t),p&&(s=p.container,t=p.offset)),o=f(s,t),o.node)){for(;o.node&&0===o.offset&&o.node.previousSibling;)o=f(o.node.previousSibling);o.node&&o.offset>0&&3===o.node.nodeType&&" "===o.node.nodeValue.charAt(o.offset-1)&&o.offset>1&&(s=o.node,s.splitText(o.offset-1))}return(c[0].inline||c[0].block_expand)&&(c[0].inline&&3==q.nodeType&&0!==r||(q=e(!0)),c[0].inline&&3==s.nodeType&&t!==s.nodeValue.length||(s=e())),c[0].selector&&c[0].expand!==ma&&!c[0].inline&&(q=i(q,"previousSibling"),s=i(s,"nextSibling")),(c[0].block||c[0].selector)&&(q=l(q,"previousSibling"),s=l(s,"nextSibling"),c[0].block&&(ha(q)||(q=e(!0)),ha(s)||(s=e()))),1==q.nodeType&&(r=ja(q),q=q.parentNode),1==s.nodeType&&(t=ja(s)+1,s=s.parentNode),{startContainer:q,startOffset:r,endContainer:s,endOffset:t}}function S(a,b){return b.links&&"A"==a.tagName}function T(a,b,c,d){var e,f,g;if(!G(c,a)&&!S(c,a))return ma;if("all"!=a.remove)for(qa(a.styles,function(e,f){e=N(O(e,b),f),"number"==typeof f&&(f=e,d=0),(a.remove_similar||!d||H(M(d,f),e))&&da.setStyle(c,f,""),g=1}),g&&""===da.getAttrib(c,"style")&&(c.removeAttribute("style"),c.removeAttribute("data-mce-style")),qa(a.attributes,function(a,e){var f;if(a=O(a,b),"number"==typeof e&&(e=a,d=0),!d||H(da.getAttrib(d,e),a)){if("class"==e&&(a=da.getAttrib(c,e),a&&(f="",qa(a.split(/\s+/),function(a){/mce\-\w+/.test(a)&&(f+=(f?" ":"")+a)}),f)))return void da.setAttrib(c,e,f);"class"==e&&c.removeAttribute("className"),la.test(e)&&c.removeAttribute("data-mce-"+e),c.removeAttribute(e)}}),qa(a.classes,function(a){a=O(a,b),d&&!da.hasClass(d,a)||da.removeClass(c,a)}),f=da.getAttribs(c),e=0;e<f.length;e++){var h=f[e].nodeName;if(0!==h.indexOf("_")&&0!==h.indexOf("data-"))return ma}return"none"!=a.remove?(U(c,a),na):void 0}function U(a,b){function c(a,b,c){return a=V(a,b,c),!a||"BR"==a.nodeName||ha(a)}var d,e=a.parentNode;b.block&&(ia?e==da.getRoot()&&(b.list_block&&H(a,b.list_block)||qa(ra(a.childNodes),function(a){ga(ia,a.nodeName.toLowerCase())?d?d.appendChild(a):(d=Q(a,ia),da.setAttribs(d,j.settings.forced_root_block_attrs)):d=0})):ha(a)&&!ha(e)&&(c(a,ma)||c(a.firstChild,na,1)||a.insertBefore(da.create("br"),a.firstChild),c(a,na)||c(a.lastChild,ma,1)||a.appendChild(da.create("br")))),b.selector&&b.inline&&!H(b.inline,a)||da.remove(a,1)}function V(a,b,c){if(a)for(b=b?"nextSibling":"previousSibling",a=c?a:a[b];a;a=a[b])if(1==a.nodeType||!P(a))return a}function W(a,b){function c(a,b){for(e=a;e;e=e[b]){if(3==e.nodeType&&0!==e.nodeValue.length)return a;if(1==e.nodeType&&!pa(e))return e}return a}var e,f,g=new d(da);if(a&&b&&(a=c(a,"previousSibling"),b=c(b,"nextSibling"),g.compare(a,b))){for(e=a.nextSibling;e&&e!=b;)f=e,e=e.nextSibling,a.appendChild(f);return da.remove(b),qa(ra(b.childNodes),function(b){a.appendChild(b)}),a}return b}function X(b,c){var d,e,f;return d=b[c?"startContainer":"endContainer"],e=b[c?"startOffset":"endOffset"],1==d.nodeType&&(f=d.childNodes.length-1,!c&&e&&e--,d=d.childNodes[e>f?f:e]),3===d.nodeType&&c&&e>=d.nodeValue.length&&(d=new a(d,j.getBody()).next()||d),3!==d.nodeType||c||0!==e||(d=new a(d,j.getBody()).prev()||d),d}function Y(b,c,d,e){function f(a){var b=da.create("span",{id:p,"data-mce-bogus":!0,style:q?"color:red":""});return a&&b.appendChild(j.getDoc().createTextNode(ka)),b}function g(a,b){for(;a;){if(3===a.nodeType&&a.nodeValue!==ka||a.childNodes.length>1)return!1;b&&1===a.nodeType&&b.push(a),a=a.firstChild}return!0}function h(a){for(;a;){if(a.id===p)return a;a=a.parentNode}}function i(b){var c;if(b)for(c=new a(b,b),b=c.current();b;b=c.next())if(3===b.nodeType)return b}function l(a,b){var c,d;if(a)d=ea.getRng(!0),g(a)?(b!==!1&&(d.setStartBefore(a),d.setEndBefore(a)),da.remove(a)):(c=i(a),c.nodeValue.charAt(0)===ka&&(c.deleteData(0,1),d.startContainer==c&&d.startOffset>0&&d.setStart(c,d.startOffset-1),d.endContainer==c&&d.endOffset>0&&d.setEnd(c,d.endOffset-1)),da.remove(a,1)),ea.setRng(d);else if(a=h(ea.getStart()),!a)for(;a=da.get(p);)l(a,!1)}function m(){var a,b,e,g,j,k,l;a=ea.getRng(!0),g=a.startOffset,k=a.startContainer,l=k.nodeValue,b=h(ea.getStart()),b&&(e=i(b));var m=/[^\s\u00a0\u00ad\u200b\ufeff]/;l&&g>0&&g<l.length&&m.test(l.charAt(g))&&m.test(l.charAt(g-1))?(j=ea.getBookmark(),a.collapse(!0),a=R(a,r(c)),a=fa.split(a),x(c,d,a),ea.moveToBookmark(j)):(b&&e.nodeValue===ka?x(c,d,b):(b=f(!0),e=b.firstChild,a.insertNode(b),g=1,x(c,d,b)),ea.setCursorLocation(e,g))}function n(){var a,b,g,h,i,j,l,m,n=ea.getRng(!0),o=[];for(a=n.startContainer,b=n.startOffset,i=a,3==a.nodeType&&(b!=a.nodeValue.length&&(h=!0),i=i.parentNode);i;){if(A(i,c,d,e)){j=i;break}i.nextSibling&&(h=!0),o.push(i),i=i.parentNode}if(j)if(h)g=ea.getBookmark(),n.collapse(!0),n=R(n,r(c),!0),n=fa.split(n),y(c,d,n),ea.moveToBookmark(g);else{for(m=f(),i=m,l=o.length-1;l>=0;l--)i.appendChild(da.clone(o[l],!1)),i=i.firstChild;i.appendChild(da.doc.createTextNode(ka)),i=i.firstChild;var p=da.getParent(j,k);p&&da.isEmpty(p)?j.parentNode.replaceChild(m,j):da.insertAfter(m,j),ea.setCursorLocation(i,1),da.isEmpty(j)&&da.remove(j)}}function o(){var a;a=h(ea.getStart()),a&&!da.isEmpty(a)&&sa(a,function(a){1!=a.nodeType||a.id===p||da.isEmpty(a)||da.setAttrib(a,"data-mce-bogus",null)},"childNodes")}var p="_mce_caret",q=j.settings.caret_debug;j._hasCaretEvents||(ba=function(){var a,b=[];if(g(h(ea.getStart()),b))for(a=b.length;a--;)da.setAttrib(b[a],"data-mce-bogus","1")},aa=function(a){var b=a.keyCode;l(),8==b&&ea.isCollapsed()&&ea.getStart().innerHTML==ka&&l(h(ea.getStart())),37!=b&&39!=b||l(h(ea.getStart())),o()},j.on("SetContent",function(a){a.selection&&o()}),j._hasCaretEvents=!0),"apply"==b?m():n()}function Z(b){var c,d,e,f=b.startContainer,g=b.startOffset;if((b.startContainer!=b.endContainer||!m(b.startContainer.childNodes[b.startOffset]))&&(3==f.nodeType&&g>=f.nodeValue.length&&(g=ja(f),f=f.parentNode),1==f.nodeType))for(e=f.childNodes,g<e.length?(f=e[g],c=new a(f,da.getParent(f,da.isBlock))):(f=e[e.length-1],c=new a(f,da.getParent(f,da.isBlock)),c.next(!0)),d=c.current();d;d=c.next())if(3==d.nodeType&&!P(d))return b.setStart(d,0),void ea.setRng(b)}var $,_,aa,ba,ca={},da=j.dom,ea=j.selection,fa=new b(da),ga=j.schema.isValidChild,ha=da.isBlock,ia=j.settings.forced_root_block,ja=da.nodeIndex,ka="\ufeff",la=/^(src|href|style)$/,ma=!1,na=!0,oa=da.getContentEditable,pa=c.isBookmarkNode,qa=g.each,ra=g.grep,sa=g.walk,ta=g.extend,ua=function(a,b){a.clear_child_styles&&qa(da.select("*",b),function(b){qa(a.styles,function(a,c){da.setStyle(b,c,"")})})};ta(this,{get:r,register:s,unregister:t,apply:x,remove:y,toggle:z,match:B,matchAll:C,matchNode:A,canApply:D,formatChanged:E,getCssText:F}),p(),q(),j.on("BeforeGetContent",function(a){ba&&"raw"!=a.format&&ba()}),j.on("mouseup keydown",function(a){aa&&aa(a)})}}),g("5e",[],function(){var a=0,b=1,c=2,d=function(d,e){var f=d.length+e.length+2,g=new Array(f),h=new Array(f),i=function(a,b,c){return{start:a,end:b,diag:c}},j=function(f,g,h,i,k){var m=l(f,g,h,i);if(null===m||m.start===g&&m.diag===g-i||m.end===f&&m.diag===f-h)for(var n=f,o=h;n<g||o<i;)n<g&&o<i&&d[n]===e[o]?(k.push([a,d[n]]),++n,++o):g-f>i-h?(k.push([c,d[n]]),++n):(k.push([b,e[o]]),++o);else{j(f,m.start,h,m.start-m.diag,k);for(var p=m.start;p<m.end;++p)k.push([a,d[p]]);j(m.end,g,m.end-m.diag,i,k)}},k=function(a,b,c,f){for(var g=a;g-b<f&&g<c&&d[g]===e[g-b];)++g;return i(a,g,b)},l=function(a,b,c,f){var i=b-a,j=f-c;if(0===i||0===j)return null;var l=i-j,m=j+i,n=(m%2===0?m:m+1)/2;g[1+n]=a,h[1+n]=b+1;for(var o=0;o<=n;++o){for(var p=-o;p<=o;p+=2){var q=p+n;p===-o||p!=o&&g[q-1]<g[q+1]?g[q]=g[q+1]:g[q]=g[q-1]+1;for(var r=g[q],s=r-a+c-p;r<b&&s<f&&d[r]===e[s];)g[q]=++r,++s;if(l%2!=0&&l-o<=p&&p<=l+o&&h[q-l]<=g[q])return k(h[q-l],p+a-c,b,f)}for(p=l-o;p<=l+o;p+=2){for(q=p+n-l,p===l-o||p!=l+o&&h[q+1]<=h[q-1]?h[q]=h[q+1]-1:h[q]=h[q-1],r=h[q]-1,s=r-a+c-p;r>=a&&s>=c&&d[r]===e[s];)h[q]=r--,s--;if(l%2===0&&-o<=p&&p<=o&&h[q]<=g[q+l])return k(h[q],p+a-c,b,f)}}},m=[];return j(0,d.length,0,e.length,m),m};return{KEEP:a,DELETE:c,INSERT:b,diff:d}}),g("4h",["1g","d","5e"],function(a,b,c){var d=function(a){return 1===a.nodeType?a.outerHTML:3===a.nodeType?b.encodeRaw(a.data,!1):8===a.nodeType?"<!--"+a.data+"-->":""},e=function(a){var b,c,d;for(d=document.createElement("div"),b=document.createDocumentFragment(),a&&(d.innerHTML=a);c=d.firstChild;)b.appendChild(c);return b},f=function(a,b,c){var d=e(b);if(a.hasChildNodes()&&c<a.childNodes.length){var f=a.childNodes[c];f.parentNode.insertBefore(d,f)}else a.appendChild(d)},g=function(a,b){if(a.hasChildNodes()&&b<a.childNodes.length){var c=a.childNodes[b];c.parentNode.removeChild(c)}},h=function(b,d){var e=0;a.each(b,function(a){a[0]===c.KEEP?e++:a[0]===c.INSERT?(f(d,a[1],e),e++):a[0]===c.DELETE&&g(d,e)})},i=function(b){return a.filter(a.map(b.childNodes,d),function(a){return a.length>0})},j=function(b,e){var f=a.map(e.childNodes,d);return h(c.diff(f,b),e),e};return{read:i,write:j}}),g("1z",["1g","4h"],function(a,b){var c=function(a){return a.indexOf("</iframe>")!==-1},d=function(a){return{type:"fragmented",fragments:a,content:"",bookmark:null,beforeBookmark:null}},e=function(a){return{type:"complete",fragments:null,content:a,bookmark:null,beforeBookmark:null}},f=function(f){var g,h,i;return g=b.read(f.getBody()),i=a.map(g,function(a){return f.serializer.trimContent(a)}),h=i.join(""),c(h)?d(i):e(h)},g=function(a,c,d){"fragmented"===c.type?b.write(c.fragments,a.getBody()):a.setContent(c.content,{format:"raw"}),a.selection.moveToBookmark(d?c.beforeBookmark:c.bookmark)},h=function(a){return"fragmented"===a.type?a.fragments.join(""):a.content},i=function(a,b){return h(a)===h(b)};return{createFragmentedLevel:d,createCompleteLevel:e,createFromEditor:f,applyToEditor:g,isEq:i}}),g("u",["p","9","1z"],function(a,b,c){return function(a){function d(b){a.setDirty(b)}function e(a){n(!1),i.add({},a)}function f(){i.typing&&(n(!1),i.add())}var g,h,i=this,j=0,k=[],l=0,m=function(){return 0===l},n=function(a){m()&&(i.typing=a)};return a.on("init",function(){i.add()}),a.on("BeforeExecCommand",function(a){var b=a.command;"Undo"!==b&&"Redo"!==b&&"mceRepaint"!==b&&(f(),i.beforeChange())}),a.on("ExecCommand",function(a){var b=a.command;"Undo"!==b&&"Redo"!==b&&"mceRepaint"!==b&&e(a)}),a.on("ObjectResizeStart Cut",function(){i.beforeChange()}),a.on("SaveContent ObjectResized blur",e),a.on("DragEnd",e),a.on("KeyUp",function(b){var f=b.keyCode;b.isDefaultPrevented()||((f>=33&&f<=36||f>=37&&f<=40||45===f||b.ctrlKey)&&(e(),a.nodeChanged()),46!==f&&8!==f||a.nodeChanged(),h&&i.typing&&c.isEq(c.createFromEditor(a),k[0])===!1&&(a.isDirty()===!1&&(d(!0),a.fire("change",{level:k[0],lastLevel:null})),a.fire("TypingUndo"),h=!1,a.nodeChanged()))}),a.on("KeyDown",function(a){var b=a.keyCode;if(!a.isDefaultPrevented()){if(b>=33&&b<=36||b>=37&&b<=40||45===b)return void(i.typing&&e(a));var c=a.ctrlKey&&!a.altKey||a.metaKey;!(b<16||b>20)||224===b||91===b||i.typing||c||(i.beforeChange(),n(!0),i.add({},a),h=!0)}}),a.on("MouseDown",function(a){i.typing&&e(a)}),a.addShortcut("meta+z","","Undo"),a.addShortcut("meta+y,meta+shift+z","","Redo"),a.on("AddUndo Undo Redo ClearUndos",function(b){b.isDefaultPrevented()||a.nodeChanged()}),i={data:k,typing:!1,beforeChange:function(){m()&&(g=a.selection.getBookmark(2,!0))},add:function(e,f){var h,i,l,n=a.settings;if(l=c.createFromEditor(a),e=e||{},e=b.extend(e,l),m()===!1||a.removed)return null;if(i=k[j],a.fire("BeforeAddUndo",{level:e,lastLevel:i,originalEvent:f}).isDefaultPrevented())return null;if(i&&c.isEq(i,e))return null;if(k[j]&&(k[j].beforeBookmark=g),n.custom_undo_redo_levels&&k.length>n.custom_undo_redo_levels){for(h=0;h<k.length-1;h++)k[h]=k[h+1];k.length--,j=k.length}e.bookmark=a.selection.getBookmark(2,!0),j<k.length-1&&(k.length=j+1),k.push(e),j=k.length-1;var o={level:e,lastLevel:i,originalEvent:f};return a.fire("AddUndo",o),j>0&&(d(!0),a.fire("change",o)),e},undo:function(){var b;return i.typing&&(i.add(),i.typing=!1,n(!1)),j>0&&(b=k[--j],c.applyToEditor(a,b,!0),d(!0),a.fire("undo",{level:b})),b},redo:function(){var b;return j<k.length-1&&(b=k[++j],c.applyToEditor(a,b,!1),d(!0),a.fire("redo",{level:b})),b},clear:function(){k=[],j=0,i.typing=!1,i.data=k,a.fire("ClearUndos")},hasUndo:function(){return j>0||i.typing&&k[0]&&!c.isEq(c.createFromEditor(a),k[0])},hasRedo:function(){return j<k.length-1&&!i.typing},transact:function(a){return f(),i.beforeChange(),i.ignore(a),i.add()},ignore:function(a){try{l++,a()}finally{l--}},extra:function(b,d){var e,f;i.transact(b)&&(f=k[j].bookmark,e=k[j-1],c.applyToEditor(a,e,!0),i.transact(d)&&(k[j-1].beforeBookmark=f))}}}}),g("6o",["55","1r","4d","22"],function(a,b,c,d){var e=function(a){var b=c.isText(a)?a.dom().parentNode:a.dom();return void 0!==b&&null!==b&&b.ownerDocument.body.contains(b)},f=a.cached(function(){return g(b.fromDom(d))}),g=function(a){var c=a.dom().body;if(null===c||void 0===c)throw"Body is not available yet";return b.fromDom(c)};return{body:f,getBody:g,inBody:e}}),g("6p",["60","4z"],function(a,b){return function(c,d,e,f,g){return c(e,f)?b.some(e):a.isFunction(g)&&g(e)?b.none():d(e,f,g)}}),g("64",["60","3x","1m","4z","6o","1q","1r","6p"],function(a,b,c,d,e,f,g,h){var i=function(a){return n(e.body(),a)},j=function(b,e,f){for(var h=b.dom(),i=a.isFunction(f)?f:c.constant(!1);h.parentNode;){h=h.parentNode;var j=g.fromDom(h);if(e(j))return d.some(j);if(i(j))break}return d.none()},k=function(a,b,c){var d=function(a){return b(a)};return h(d,j,a,b,c)},l=function(a,b){var c=a.dom();return c.parentNode?m(g.fromDom(c.parentNode),function(c){return!f.eq(a,c)&&b(c)}):d.none()},m=function(a,d){var e=b.find(a.dom().childNodes,c.compose(d,g.fromDom));return e.map(g.fromDom)},n=function(a,b){var c=function(a){for(var e=0;e<a.childNodes.length;e++){if(b(g.fromDom(a.childNodes[e])))return d.some(g.fromDom(a.childNodes[e]));var f=c(a.childNodes[e]);if(f.isSome())return f}return d.none()};return c(a.dom())};return{first:i,ancestor:j,closest:k,sibling:l,child:m,descendant:n}}),g("5i",["1w","c","1j","1p","1k","44"],function(a,b,c,d,e,f){function g(a){return a>0}function h(a){return a<0}function i(a,b){for(var c;c=a(b);)if(!y(c))return c;return null}function j(a,c,d,e,f){var j=new b(a,e);if(h(c)){if((v(a)||y(a))&&(a=i(j.prev,!0),d(a)))return a;for(;a=i(j.prev,f);)if(d(a))return a}if(g(c)){if((v(a)||y(a))&&(a=i(j.next,!0),d(a)))return a;for(;a=i(j.next,f);)if(d(a))return a}return null}function k(a,b){for(a=a.parentNode;a&&a!=b;a=a.parentNode)if(u(a))return a;return b}function l(a,b){for(;a&&a!=b;){if(w(a))return a;a=a.parentNode}return null}function m(a,b,c){return l(a.container(),c)==l(b.container(),c)}function n(a,b,c){return k(a.container(),c)==k(b.container(),c)}function o(a,b){var c,d;return b?(c=b.container(),d=b.offset(),A(c)?c.childNodes[d+a]:null):null}function p(a,b){var c=b.ownerDocument.createRange();return a?(c.setStartBefore(b),c.setEndBefore(b)):(c.setStartAfter(b),c.setEndAfter(b)),c}function q(a,b,c){return l(b,a)==l(c,a)}function r(a,b,c){var d,e;for(e=a?"previousSibling":"nextSibling";c&&c!=b;){if(d=c[e],x(d)&&(d=d[e]),v(d)){if(q(b,d,c))return d;break}if(B(d))break;c=c.parentNode}return null}function s(a,b,d){var f,g,h,i,j=z(r,!0,b),k=z(r,!1,b);if(g=d.startContainer,h=d.startOffset,e.isCaretContainerBlock(g)){if(A(g)||(g=g.parentNode),i=g.getAttribute("data-mce-caret"),"before"==i&&(f=g.nextSibling,v(f)))return C(f);if("after"==i&&(f=g.previousSibling,v(f)))return D(f)}if(!d.collapsed)return d;if(c.isText(g)){if(x(g)){if(1===a){if(f=k(g))return C(f);if(f=j(g))return D(f)}if(a===-1){if(f=j(g))return D(f);if(f=k(g))return C(f)}return d}if(e.endsWithCaretContainer(g)&&h>=g.data.length-1)return 1===a&&(f=k(g))?C(f):d;if(e.startsWithCaretContainer(g)&&h<=1)return a===-1&&(f=j(g))?D(f):d;if(h===g.data.length)return f=k(g),f?C(f):d;if(0===h)return f=j(g),f?D(f):d}return d}function t(a,b){return v(o(a,b))}var u=c.isContentEditableTrue,v=c.isContentEditableFalse,w=c.matchStyleValues("display","block table table-cell table-caption list-item"),x=e.isCaretContainer,y=e.isCaretContainerBlock,z=a.curry,A=c.isElement,B=f.isCaretCandidate,C=z(p,!0),D=z(p,!1);return{isForwards:g,isBackwards:h,findNode:j,getEditingHost:k,getParentBlock:l,isInSameBlock:m,isInSameEditingHost:n,isBeforeContentEditableFalse:z(t,0),isAfterContentEditableFalse:z(t,-1),normalizeRange:s}}),g("4m",["1j","44","1p","5i","1g","1w"],function(a,b,c,d,e,f){function g(a,b){for(var c=[];a&&a!=b;)c.push(a),a=a.parentNode;return c}function h(a,b){return a.hasChildNodes()&&b<a.childNodes.length?a.childNodes[b]:null}function i(a,b){if(p(a)){if(r(b.previousSibling)&&!m(b.previousSibling))return c.before(b);if(m(b))return c(b,0)}if(q(a)){if(r(b.nextSibling)&&!m(b.nextSibling))return c.after(b);if(m(b))return c(b,b.data.length)}return q(a)?o(b)?c.before(b):c.after(b):c.before(b)}function j(b,e){var f;return!!a.isBr(b)&&(f=k(1,c.after(b),e),!!f&&!d.isInSameBlock(c.before(b),c.before(f),e))}function k(a,b,u){var v,w,x,y,z,A,B;if(!n(u)||!b)return null;if(b.isEqual(c.after(u))&&u.lastChild){if(B=c.after(u.lastChild),q(a)&&r(u.lastChild)&&n(u.lastChild))return o(u.lastChild)?c.before(u.lastChild):B}else B=b;if(v=B.container(),w=B.offset(),m(v)){if(q(a)&&w>0)return c(v,--w);if(p(a)&&w<v.length)return c(v,++w);x=v}else{if(q(a)&&w>0&&(y=h(v,w-1),r(y)))return!s(y)&&(z=d.findNode(y,a,t,y))?m(z)?c(z,z.data.length):c.after(z):m(y)?c(y,y.data.length):c.before(y);if(p(a)&&w<v.childNodes.length&&(y=h(v,w),r(y)))return j(y,u)?k(a,c.after(y),u):!s(y)&&(z=d.findNode(y,a,t,y))?m(z)?c(z,0):c.before(z):m(y)?c(y,0):c.after(y);x=B.getNode()}return(p(a)&&B.isAtEnd()||q(a)&&B.isAtStart())&&(x=d.findNode(x,a,f.constant(!0),u,!0),t(x))?i(a,x):(y=d.findNode(x,a,t,u),A=e.last(e.filter(g(v,u),l)),!A||y&&A.contains(y)?y?i(a,y):null:B=p(a)?c.after(A):c.before(A))}var l=a.isContentEditableFalse,m=a.isText,n=a.isElement,o=a.isBr,p=d.isForwards,q=d.isBackwards,r=b.isCaretCandidate,s=b.isAtomic,t=b.isEditableCaretCandidate;return function(a){return{next:function(b){return k(1,b,a)},prev:function(b){return k(-1,b,a)}}}}),g("5l",["1m","4z","44","1p","5i","4m","1j"],function(a,b,c,d,e,f,g){var h=function(a,b,c){var e=a?d.before(c):d.after(c);return o(a,b,e)},i=function(a){return g.isBr(a)?d.before(a):d.after(a)},j=function(a){return d.isTextPosition(a)?0===a.offset():c.isCaretCandidate(a.getNode())},k=function(a){return d.isTextPosition(a)?a.offset()===a.container().data.length:c.isCaretCandidate(a.getNode(!0))},l=function(a,b){return!d.isTextPosition(a)&&!d.isTextPosition(b)&&a.getNode()===b.getNode(!0)},m=function(a){return!d.isTextPosition(a)&&g.isBr(a.getNode())},n=function(a,b,c){return a?!l(b,c)&&!m(b)&&k(b)&&j(c):!l(c,b)&&j(b)&&k(c)},o=function(a,c,d){var e=new f(c);return b.from(a?e.next(d):e.prev(d))},p=function(a,c,d){return o(a,c,d).bind(function(f){return e.isInSameBlock(d,f,c)&&n(a,d,f)?o(a,c,f):b.some(f)})},q=function(a,e){var f=a?e.firstChild:e.lastChild;return g.isText(f)?b.some(new d(f,a?0:f.data.length)):f?c.isCaretCandidate(f)?b.some(a?d.before(f):i(f)):h(a,e,f):b.none()};return{fromPosition:o,navigate:p,positionIn:q}}),g("5h",["3x","4z","1q","1r","4d","64"],function(a,b,c,d,e,f){var g=function(b){var c=a.foldl(b,function(a,b){return a[b]=!0,a},{});return function(a){return c[e.name(a)]===!0}},h=g(["h1","h2","h3","h4","h5","h6","p","div","address","pre","form","blockquote","center","dir","fieldset","header","footer","article","section","hgroup","aside","nav","figure"]),i=function(a){return function(b){return c.eq(a,d.fromDom(b.dom().parentNode))}},j=function(a,d){return c.contains(a,d)?f.closest(d,h,i(a)):b.none()};return{getParentTextBlock:j}}),g("79",["64","48","6p"],function(a,b,c){var d=function(a){return b.one(a)},e=function(c,d,e){return a.ancestor(c,function(a){return b.is(a,d)},e)},f=function(c,d){return a.sibling(c,function(a){return b.is(a,d)})},g=function(c,d){return a.child(c,function(a){return b.is(a,d)})},h=function(a,c){return b.one(c,a)},i=function(a,d,f){return c(b.is,e,a,d,f)};return{first:d,ancestor:e,sibling:f,child:g,descendant:h,closest:i}}),g("6q",["79"],function(a){var b=function(b){return a.first(b).isSome()},c=function(b,c,d){return a.ancestor(b,c,d).isSome()},d=function(b,c){return a.sibling(b,c).isSome()},e=function(b,c){return a.child(b,c).isSome()},f=function(b,c){return a.descendant(b,c).isSome()},g=function(b,c,d){return a.closest(b,c,d).isSome()};return{any:b,ancestor:c,sibling:d,child:e,descendant:f,closest:g}}),g("65",["1m","1q","1r","6q","44","1j","c"],function(a,b,c,d,e,f,g){var h=function(e,f){var g=c.fromDom(e),h=c.fromDom(f);return d.ancestor(h,"pre,code",a.curry(b.eq,g))},i=function(a,b){return f.isText(b)&&/^[ \t\r\n]*$/.test(b.data)&&h(a,b)===!1},j=function(a){return f.isElement(a)&&"A"===a.nodeName&&a.hasAttribute("name")},k=function(a,b){return e.isCaretCandidate(b)&&i(a,b)===!1||j(b)||l(b)},l=f.hasAttribute("data-mce-bookmark"),m=f.hasAttribute("data-mce-bogus"),n=f.hasAttributeValue("data-mce-bogus","all"),o=function(a){var b,c,d=0;if(k(a,a))return!1;if(c=a.firstChild,!c)return!0;b=new g(c,a);do if(n(c))c=b.next(!0);else if(m(c))c=b.next();else if(f.isBr(c))d++,c=b.next();else{if(k(a,c))return!1;c=b.next()}while(c);return d<=1},p=function(a){return o(a.dom())};return{isEmpty:p}}),g("5f",["3x","1m","4z","5d","61","1q","1r","4d","64","59","5l","1p","5h","65","1j"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){var p=e.immutable("block","position"),q=e.immutable("from","to"),r=function(a,b){var c=g.fromDom(a),d=g.fromDom(b.container());return m.getParentTextBlock(c,d).map(function(a){return p(a,b)})},s=function(a){return f.eq(a.from().block(),a.to().block())===!1},t=function(a){return j.parent(a.from().block()).bind(function(b){return j.parent(a.to().block()).filter(function(a){return f.eq(b,a)})}).isSome()},u=function(a){return o.isContentEditableFalse(a.from().block())===!1&&o.isContentEditableFalse(a.to().block())===!1},v=function(a,b,d){return o.isBr(d.position().getNode())&&n.isEmpty(d.block())===!1?k.positionIn(!1,d.block().dom()).bind(function(e){return e.isEqual(d.position())?k.fromPosition(b,a,e).bind(function(b){return r(a,b)}):c.some(d)}).getOr(d):d},w=function(a,b,c){var e=r(a,l.fromRangeStart(c)),f=e.bind(function(c){return k.fromPosition(b,a,c.position()).bind(function(c){return r(a,c).map(function(c){return v(a,b,c)})})});return d.liftN([e,f],q).filter(function(a){return s(a)&&t(a)&&u(a)})},x=function(a,b,d){return d.collapsed?w(a,b,d):c.none()};return{read:x}}),g("5g",["3x","4z","4a","5c","1r","59","5l","1p","65","1j"],function(a,b,c,d,e,f,g,h,i,j){var k=function(h,k,l,m){var n=f.children(k);return j.isBr(m.getNode())&&(d.remove(e.fromDom(m.getNode())),m=g.positionIn(!1,l.dom()).getOr(m)),i.isEmpty(k)===!1&&a.each(n,function(a){c.append(l,a)}),i.isEmpty(k)&&d.remove(k),n.length>0?b.from(m):b.none()},l=function(a,b,c){return a?i.isEmpty(b)?(d.remove(b),g.positionIn(!0,c.dom())):g.positionIn(!1,b.dom()).bind(function(d){return k(a,c,b,d)}):i.isEmpty(c)?(d.remove(c),g.positionIn(!0,b.dom())):g.positionIn(!1,c.dom()).bind(function(d){return k(a,b,c,d)})};return{mergeBlocks:l}}),g("4i",["5f","5g"],function(a,b){var c=function(c,d){var e;return e=a.read(c.getBody(),d,c.selection.getRng()).bind(function(a){return b.mergeBlocks(d,a.from().block(),a.to().block())}),e.each(function(a){c.selection.setRng(a.toRange())}),e.isSome()};return{backspaceDelete:c}}),g("4j",["5d","1q","1r","5h","5g"],function(a,b,c,d,e){var f=function(f,g){var h=g.getRng();return a.liftN([d.getParentTextBlock(f,c.fromDom(h.startContainer)),d.getParentTextBlock(f,c.fromDom(h.endContainer))],function(a,c){return b.eq(a,c)===!1&&(h.deleteContents(),e.mergeBlocks(!0,a,c).each(function(a){g.setRng(a.toRange())}),!0)}).getOr(!1)},g=function(a,b){var d=c.fromDom(a.getBody());return a.selection.isCollapsed()===!1&&f(d,a.selection)};return{backspaceDelete:g}}),g("66",["3x","63","60","3y","3z","49"],function(a,b,c,d,e,f){var g=function(g){if(!c.isArray(g))throw new e("cases must be an array");if(0===g.length)throw new e("there must be at least one case");var h=[],i={};return a.each(g,function(j,k){var l=b.keys(j);if(1!==l.length)throw new e("one and only one name per case");var m=l[0],n=j[m];if(void 0!==i[m])throw new e("duplicate key detected:"+m);if("cata"===m)throw new e("cannot have a case named cata (sorry)");if(!c.isArray(n))throw new e("case arguments must be an array");h.push(m),i[m]=function(){var c=arguments.length;if(c!==n.length)throw new e("Wrong number of arguments to case "+m+". Expected "+n.length+" ("+n+"), got "+c);for(var i=new d(c),j=0;j<i.length;j++)i[j]=arguments[j];var l=function(c){var d=b.keys(c);if(h.length!==d.length)throw new e("Wrong number of arguments to match. Expected: "+h.join(",")+"\nActual: "+d.join(","));var f=a.forall(h,function(b){return a.contains(d,b)});if(!f)throw new e("Not all branches were specified when using match. Specified: "+d.join(", ")+"\nRequired: "+h.join(", "));return c[m].apply(null,i)};return{fold:function(){if(arguments.length!==g.length)throw new e("Wrong number of arguments to fold. Expected "+g.length+", got "+arguments.length);var a=arguments[k];return a.apply(null,i)},match:l,log:function(a){f.log(a,{constructors:h,constructor:m,params:i})}}}}),i};return{generate:g}}),g("5j",["66","4z","1r","5l","1p","5i","5h","65","1j"],function(a,b,c,d,e,f,g,h,i){var j=a.generate([{remove:["element"]},{moveToElement:["element"]},{moveToPosition:["position"]}]),k=function(a,b){var c=b.getNode(a===!1),d=a?"after":"before";return i.isElement(c)&&c.getAttribute("data-mce-caret")===d},l=function(a,d,e,f){var i=f.getNode(d===!1);return g.getParentTextBlock(c.fromDom(a),c.fromDom(e.getNode())).map(function(a){return h.isEmpty(a)?j.remove(a.dom()):j.moveToElement(i)}).orThunk(function(){return b.some(j.moveToElement(i))})},m=function(a,c,e){return d.fromPosition(c,a,e).bind(function(d){return c&&i.isContentEditableFalse(d.getNode())?l(a,c,e,d):c===!1&&i.isContentEditableFalse(d.getNode(!0))?l(a,c,e,d):c&&f.isAfterContentEditableFalse(e)?b.some(j.moveToPosition(d)):c===!1&&f.isBeforeContentEditableFalse(e)?b.some(j.moveToPosition(d)):b.none(); -})},n=function(a,c){return a&&i.isContentEditableFalse(c.nextSibling)?b.some(j.moveToElement(c.nextSibling)):a===!1&&i.isContentEditableFalse(c.previousSibling)?b.some(j.moveToElement(c.previousSibling)):b.none()},o=function(a,c,d){return k(c,d)?n(c,d.getNode(c===!1)).fold(function(){return m(a,c,d)},b.some):m(a,c,d)},p=function(a,c,d){var g=f.normalizeRange(c?1:-1,a,d),h=e.fromRangeStart(g);return c===!1&&f.isAfterContentEditableFalse(h)?b.some(j.remove(h.getNode(!0))):c&&f.isBeforeContentEditableFalse(h)?b.some(j.remove(h.getNode())):o(a,c,h)};return{read:p}}),g("67",[],function(){var a=/[\u0591-\u07FF\uFB1D-\uFDFF\uFE70-\uFEFC]/,b=function(b){return a.test(b)};return{hasStrongRtl:b}}),g("5p",["3x","1m","4z","5d","1k","5l","1p","5i","4m","e","1j","67"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m=function(a){return j.DOM.is(a,"a[href],code")},n=function(a){return"rtl"===j.DOM.getStyle(a,"direction",!0)||l.hasStrongRtl(a.textContent)},o=function(b,c){return a.filter(j.DOM.getParents(c.container(),"*",b),m)},p=function(a,b){var d=o(a,b);return c.from(d[0])},q=function(a,b){var d=o(a,b);return c.from(d[d.length-1])},r=function(a,b,c){var d=h.getParentBlock(b,a),e=h.getParentBlock(c,a);return d&&d===e},s=function(a,b){return!!b&&q(a,b).isSome()},t=function(a,b){return q(a,b).map(function(a){return w(a,!1,b).isNone()||w(a,!0,b).isNone()}).getOr(!1)},u=function(a){return e.isBeforeInline(a)||e.isAfterInline(a)},v=function(a,b){return f.positionIn(b,a)},w=function(a,b,c){return f.fromPosition(b,a,c)},x=function(a,b){var c=b.container(),d=b.offset();return a?e.isCaretContainerInline(c)?k.isText(c.nextSibling)?new g(c.nextSibling,0):g.after(c):e.isBeforeInline(b)?new g(c,d+1):b:e.isCaretContainerInline(c)?k.isText(c.previousSibling)?new g(c.previousSibling,c.previousSibling.data.length):g.before(c):e.isAfterInline(b)?new g(c,d-1):b},y=b.curry(x,!0),z=b.curry(x,!1);return{isInlineTarget:m,findInline:p,findRootInline:q,isInInline:s,isRtl:n,isAtInlineEndPoint:t,isAtZwsp:u,findCaretPositionIn:v,findCaretPosition:w,normalizePosition:x,normalizeForwards:y,normalizeBackwards:z,hasSameParentBlock:r}}),g("5k",["1m","4z","5d","4a","5c","1r","4d","64","59","44","1p","65","1j","5p"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){var o=function(a,b){var c=a.container(),d=a.offset();return k.isTextPosition(a)===!1&&c===b.parentNode&&d>k.before(b).offset()},p=function(a,b){return o(b,a)?new k(b.container(),b.offset()-1):b},q=function(a){return m.isText(a)?new k(a,0):k.before(a)},r=function(a){return m.isText(a)?new k(a,a.data.length):k.after(a)},s=function(a){return j.isCaretCandidate(a.previousSibling)?b.some(r(a.previousSibling)):a.previousSibling?n.findCaretPositionIn(a.previousSibling,!1):b.none()},t=function(a){return j.isCaretCandidate(a.nextSibling)?b.some(q(a.nextSibling)):a.nextSibling?n.findCaretPositionIn(a.nextSibling,!0):b.none()},u=function(a,c){var d=k.before(c.previousSibling?c.previousSibling:c.parentNode);return n.findCaretPosition(a,!1,d).fold(function(){return n.findCaretPosition(a,!0,k.after(c))},b.some)},v=function(a,c){return n.findCaretPosition(a,!0,k.after(c)).fold(function(){return n.findCaretPosition(a,!1,k.before(c))},b.some)},w=function(a,b){return s(b).orThunk(function(){return t(b)}).orThunk(function(){return u(a,b)})},x=function(a,b){return t(b).orThunk(function(){return s(b)}).orThunk(function(){return v(a,b)})},y=function(a,b,c){return a?x(b,c):w(b,c)},z=function(b,c,d){return y(b,c,d).map(a.curry(p,d))},A=function(a,b,c){c.fold(function(){a.focus()},function(c){a.selection.setRng(c.toRange(),b)})},B=function(a){return function(b){return b.dom()===a}},C=function(a,b){return b&&a.schema.getBlockElements().hasOwnProperty(g.name(b))},D=function(a){if(l.isEmpty(a)){var c=f.fromHtml('<br data-mce-bogus="1">');return e.empty(a),d.append(a,c),b.some(k.before(c.dom()))}return b.none()},E=function(a,b){return c.liftN([i.prevSibling(a),i.nextSibling(a),b],function(b,c,d){var f,g=b.dom(),h=c.dom();return m.isText(g)&&m.isText(h)?(f=g.data.length,g.appendData(h.data),e.remove(c),e.remove(a),d.container()===h?new k(g,f):d):(e.remove(a),d)}).orThunk(function(){return e.remove(a),b})},F=function(c,d,e){var f=z(d,c.getBody(),e.dom()),g=h.ancestor(e,a.curry(C,c),B(c.getBody())),i=E(e,f);g.bind(D).fold(function(){A(c,d,i)},function(a){A(c,d,b.some(a))})};return{deleteElement:F}}),g("4k",["1r","1p","5i","5f","5j","5k","5g","1j"],function(a,b,c,d,e,f,g,h){var i=function(b,c){return function(d){return f.deleteElement(b,c,a.fromDom(d)),!0}},j=function(a,c){return function(d){var e=c?b.before(d):b.after(d);return a.selection.setRng(e.toRange()),!0}},k=function(a){return function(b){return a.selection.setRng(b.toRange()),!0}},l=function(a,b){var c=e.read(a.getBody(),b,a.selection.getRng()).map(function(c){return c.fold(i(a,b),j(a,b),k(a))});return c.getOr(!1)},m=function(b,c){var d=b.selection.getNode();return!!h.isContentEditableFalse(d)&&(f.deleteElement(b,c,a.fromDom(b.selection.getNode())),!0)},n=function(a,b){for(;b&&b!==a;){if(h.isContentEditableTrue(b)||h.isContentEditableFalse(b))return b;b=b.parentNode}return null},o=function(a){var c,d=n(a.getBody(),a.selection.getNode());return h.isContentEditableTrue(d)&&a.dom.isBlock(d)&&a.dom.isEmpty(d)&&(c=a.dom.create("br",{"data-mce-bogus":"1"}),a.dom.setHTML(d,""),d.appendChild(c),a.selection.setRng(b.before(c).toRange())),!0},p=function(a,b){return a.selection.isCollapsed()?l(a,b):m(a,b)};return{backspaceDelete:p,paddEmptyElement:o}}),g("68",["1m","1j","1l"],function(a,b,c){var d=b.isText,e=function(a){return d(a)&&a.data[0]===c.ZWSP},f=function(a){return d(a)&&a.data[a.data.length-1]===c.ZWSP},g=function(a){return a.ownerDocument.createTextNode(c.ZWSP)},h=function(a){if(d(a.previousSibling))return f(a.previousSibling)?a.previousSibling:(a.previousSibling.appendData(c.ZWSP),a.previousSibling);if(d(a))return e(a)?a:(a.insertData(0,c.ZWSP),a);var b=g(a);return a.parentNode.insertBefore(b,a),b},i=function(a){if(d(a.nextSibling))return e(a.nextSibling)?a.nextSibling:(a.nextSibling.insertData(0,c.ZWSP),a.nextSibling);if(d(a))return f(a)?a:(a.appendData(c.ZWSP),a);var b=g(a);return a.nextSibling?a.parentNode.insertBefore(b,a.nextSibling):a.parentNode.appendChild(b),b},j=function(a,b){return a?h(b):i(b)};return{insertInline:j,insertInlineBefore:a.curry(j,!0),insertInlineAfter:a.curry(j,!1)}}),g("69",["3x","1k","1p","1j","1l","9"],function(a,b,c,d,e,f){var g=d.isElement,h=d.isText,i=function(a){var b=a.parentNode;b&&b.removeChild(a)},j=function(a){try{return a.nodeValue}catch(a){return""}},k=function(a,b){0===b.length?i(a):a.nodeValue=b},l=function(a){var b=e.trim(a);return{count:a.length-b.length,text:b}},m=function(a,b){return s(a),b},n=function(a,b){var d=l(a.data.substr(0,b.offset())),e=l(a.data.substr(b.offset())),f=d.text+e.text;return f.length>0?(k(a,f),new c(a,b.offset()-d.count)):b},o=function(b,d){var e=d.container(),f=a.indexOf(e.childNodes,b).map(function(a){return a<d.offset()?new c(e,d.offset()-1):d}).getOr(d);return s(b),f},p=function(a,b){return b.container()===a?n(a,b):m(a,b)},q=function(a,b){return b.container()===a.parentNode?o(a,b):m(a,b)},r=function(a,b){return c.isTextPosition(b)?p(a,b):q(a,b)},s=function(a){if(g(a)&&b.isCaretContainer(a)&&(b.hasContent(a)?a.removeAttribute("data-mce-caret"):i(a)),h(a)){var c=e.trim(j(a));k(a,c)}};return{removeAndReposition:r,remove:s}}),g("5m",["4z","1k","68","69","1p","1j","5p"],function(a,b,c,d,e,f,g){var h=function(a,b){return f.isText(a.container())?c.insertInline(b,a.container()):c.insertInline(b,a.getNode())},i=function(a,c){var d=c.get();return d&&a.container()===d&&b.isCaretContainerInline(d)},j=function(b,f){return f.fold(function(f){d.remove(b.get());var g=c.insertInlineBefore(f);return b.set(g),a.some(new e(g,g.length-1))},function(a){return g.findCaretPositionIn(a,!0).map(function(a){if(i(a,b))return new e(b.get(),1);d.remove(b.get());var c=h(a,!0);return b.set(c),new e(c,1)})},function(a){return g.findCaretPositionIn(a,!1).map(function(a){if(i(a,b))return new e(b.get(),b.get().length-1);d.remove(b.get());var c=h(a,!1);return b.set(c),new e(c,c.length-1)})},function(f){d.remove(b.get());var g=c.insertInlineAfter(f);return b.set(g),a.some(new e(g,1))})};return{renderCaret:j}}),g("6a",["4z"],function(a){var b=function(b,c){for(var d=0;d<b.length;d++){var e=b[d].apply(null,c);if(e.isSome())return e}return a.none()};return{evaluateUntil:b}}),g("5n",["66","1m","4z","5d","1k","1p","5i","1j","5p","6a"],function(a,b,c,d,e,f,g,h,i,j){var k=a.generate([{before:["element"]},{start:["element"]},{end:["element"]},{after:["element"]}]),l=function(a,b){var c=g.getParentBlock(b,a);return c?c:a},m=function(a,d){var e=i.normalizeForwards(d),f=l(a,e.container());return i.findRootInline(f,e).fold(function(){return i.findCaretPosition(f,!0,e).bind(b.curry(i.findRootInline,f)).map(function(a){return k.before(a)})},c.none)},n=function(a,b){var d=i.normalizeBackwards(b);return i.findRootInline(a,d).bind(function(a){var b=i.findCaretPosition(a,!1,d);return b.isNone()?c.some(k.start(a)):c.none()})},o=function(a,b){var d=i.normalizeForwards(b);return i.findRootInline(a,d).bind(function(a){var b=i.findCaretPosition(a,!0,d);return b.isNone()?c.some(k.end(a)):c.none()})},p=function(a,d){var e=i.normalizeBackwards(d),f=l(a,e.container());return i.findRootInline(f,e).fold(function(){return i.findCaretPosition(f,!1,e).bind(b.curry(i.findRootInline,f)).map(function(a){return k.after(a)})},c.none)},q=function(a){return i.isRtl(s(a))===!1},r=function(a,b){var c=j.evaluateUntil([m,n,o,p],[a,b]);return c.filter(q)},s=function(a){return a.fold(b.identity,b.identity,b.identity,b.identity)},t=function(a){return a.fold(b.constant("before"),b.constant("start"),b.constant("end"),b.constant("after"))},u=function(a){return a.fold(k.before,k.before,k.after,k.after)},v=function(a){return a.fold(k.start,k.start,k.end,k.end)},w=function(a,b){return t(a)===t(b)&&s(a)===s(b)},x=function(a,b,c,e,f){return d.liftN([i.findRootInline(b,c),i.findRootInline(b,e)],function(c,d){return c!==d&&i.hasSameParentBlock(b,c,d)?k.after(a?c:d):f}).getOr(f)},y=function(a,c){return a.fold(b.constant(!0),function(a){return!w(a,c)})},z=function(a,c,d,e){var f=i.normalizePosition(a,e),g=i.findCaretPosition(c,a,f).map(b.curry(i.normalizePosition,a)),h=g.fold(function(){return d.map(u)},function(e){return r(c,e).map(b.curry(x,a,c,f,e)).filter(b.curry(y,d))});return h.filter(q)},A=function(a,d){return a?d.fold(b.compose(c.some,k.start),c.none,b.compose(c.some,k.after),c.none):d.fold(c.none,b.compose(c.some,k.before),c.none,b.compose(c.some,k.end))},B=function(a,c,d){var e=i.normalizePosition(a,d),f=r(c,e);return r(c,e).bind(b.curry(A,a)).orThunk(function(){return z(a,c,f,d)})};return{readLocation:r,prevLocation:b.curry(B,!1),nextLocation:b.curry(B,!0),getElement:s,outside:u,inside:v}}),g("6b",[],function(){var a=function(b){var c=b,d=function(){return c},e=function(a){c=a},f=function(){return a(d())};return{get:d,set:e,clone:f}};return a}),g("5o",["3x","6b","1m","69","1p","5m","5n","5p"],function(a,b,c,d,e,f,g,h){var i=function(a,b){var c=a.dom.createRng();c.setStart(b.container(),b.offset()),c.setEnd(b.container(),b.offset()),a.selection.setRng(c)},j=function(a){return a.settings.inline_boundaries!==!1},k=function(a,b){a?b.setAttribute("data-mce-selected","1"):b.removeAttribute("data-mce-selected","1")},l=function(a,b,c){return f.renderCaret(b,c).map(function(b){return i(a,b),c})},m=function(a,b,c){var d=a.getBody(),f=e.fromRangeStart(a.selection.getRng()),h=c?g.nextLocation(d,f):g.prevLocation(d,f);return h.bind(function(c){return l(a,b,c)})},n=function(b,d){var e=b.select("a[href][data-mce-selected],code[data-mce-selected]"),f=a.filter(d,h.isInlineTarget);a.each(a.difference(e,f),c.curry(k,!1)),a.each(a.difference(f,e),c.curry(k,!0))},o=function(a,b){if(a.selection.isCollapsed()&&a.composing!==!0&&b.get()){var c=e.fromRangeStart(a.selection.getRng());e.isTextPosition(c)&&h.isAtZwsp(c)===!1&&(i(a,d.removeAndReposition(b.get(),c)),b.set(null))}},p=function(b,c,d){if(b.selection.isCollapsed()){var f=a.filter(d,h.isInlineTarget);a.each(f,function(a){var d=e.fromRangeStart(b.selection.getRng());g.readLocation(b.getBody(),d).bind(function(a){return l(b,c,a)})})}},q=function(a,b,c){return function(){return!!j(a)&&m(a,b,c).isSome()}},r=function(a){var c=new b(null);return a.on("NodeChange",function(b){j(a)&&(n(a.dom,b.parents),o(a,c),p(a,c,b.parents))}),c};return{move:q,setupSelectedState:r,setCaretPosition:i}}),g("4l",["1m","4z","5d","1r","1k","5l","1p","5i","5k","5m","5n","5o","5p"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){var n=function(a){return a.settings.inline_boundaries!==!1},o=function(a,b){var c=document.createRange();return c.setStart(a.container(),a.offset()),c.setEnd(b.container(),b.offset()),c},p=function(a){return c.liftN([m.findCaretPositionIn(a,!0),m.findCaretPositionIn(a,!1)],function(b,c){var d=m.normalizePosition(!0,b),e=m.normalizePosition(!1,c);return m.findCaretPosition(a,!0,d).map(function(a){return a.isEqual(e)}).getOr(!0)}).getOr(!0)},q=function(a,b){return function(c){return j.renderCaret(b,c).map(function(b){return l.setCaretPosition(a,b),!0}).getOr(!1)}},r=function(a,b,c,d){var e=a.getBody();a.undoManager.ignore(function(){a.selection.setRng(o(c,d)),a.execCommand("Delete"),k.readLocation(e,g.fromRangeStart(a.selection.getRng())).map(k.inside).map(q(a,b))}),a.nodeChanged()},s=function(a,b){var c=h.getParentBlock(b,a);return c?c:a},t=function(c,e,g,h){var j=s(c.getBody(),h.container()),l=k.readLocation(j,h);return l.bind(function(c){return g?c.fold(a.constant(b.some(k.inside(c))),b.none,a.constant(b.some(k.outside(c))),b.none):c.fold(b.none,a.constant(b.some(k.outside(c))),b.none,a.constant(b.some(k.inside(c))))}).map(q(c,e)).getOrThunk(function(){var a=f.navigate(g,j,h),b=a.bind(function(a){return k.readLocation(j,a)});return l.isSome()&&b.isSome()?m.findRootInline(j,h).map(function(a){return!!p(a)&&(i.deleteElement(c,g,d.fromDom(a)),!0)}).getOr(!1):b.bind(function(b){return a.map(function(a){return g?r(c,e,h,a):r(c,e,a,h),!0})}).getOr(!1)})},u=function(a,b,c){if(a.selection.isCollapsed()&&n(a)){var d=g.fromRangeStart(a.selection.getRng());return t(a,b,c,d)}return!1};return{backspaceDelete:u}}),g("20",["4i","4j","4k","4l"],function(a,b,c,d){var e=function(a,b){a.getDoc().execCommand(b,!1,null)},f=function(a){var b=a.dom,c=a.getBody();b.isEmpty(c)&&(a.setContent(""),c.firstChild&&b.isBlock(c.firstChild)?a.selection.setCursorLocation(c.firstChild,0):a.selection.setCursorLocation(c,0))},g=function(g){c.backspaceDelete(g,!1)||d.backspaceDelete(g,!1)||a.backspaceDelete(g,!1)||b.backspaceDelete(g,!1)||(e(g,"Delete"),f(g))},h=function(f){c.backspaceDelete(f,!0)||d.backspaceDelete(f,!0)||a.backspaceDelete(f,!0)||b.backspaceDelete(f,!0)||e(f,"ForwardDelete")};return{deleteCommand:g,forwardDeleteCommand:h}}),g("4n",["5l","1p","5i","1j"],function(a,b,c,d){var e=function(a){return d.isElement(a)&&/^(P|H[1-6]|DIV)$/.test(a.nodeName)},f=function(a,b){return b(a.endContainer)},g=function(a,b,c,d){var e=document.createRange();return e.setStart(a,b),e.setEnd(c,d),e},h=function(d){var h=b.fromRangeStart(d),i=b.fromRangeEnd(d),j=d.commonAncestorContainer;return d.collapsed===!1&&f(d,e)&&0===d.endOffset?a.fromPosition(!1,j,i).map(function(a){return!c.isInSameBlock(h,i,j)&&c.isInSameBlock(h,a,j)?g(h.container(),h.offset(),a.container(),a.offset()):d}).getOr(d):d},i=function(a){return h(a)};return{normalize:i}}),g("4o",["9","4m","1p"],function(a,b,c){var d=function(a){var b=a.firstChild,c=a.lastChild;return b&&"meta"===b.name&&(b=b.next),c&&"mce_marker"===c.attr("id")&&(c=c.prev),!(!b||b!==c)&&("ul"===b.name||"ol"===b.name)},e=function(a){var b=a.firstChild,c=a.lastChild;return b&&"META"===b.nodeName&&b.parentNode.removeChild(b),c&&"mce_marker"===c.id&&c.parentNode.removeChild(c),a},f=function(a,b,c){var d=b.serialize(c),f=a.createFragment(d);return e(f)},g=function(b){return a.grep(b.childNodes,function(a){return"LI"===a.nodeName})},h=function(a){return!a.firstChild},i=function(a){return a.length>0&&h(a[a.length-1])?a.slice(0,-1):a},j=function(a,b){var c=a.getParent(b,a.isBlock);return c&&"LI"===c.nodeName?c:null},k=function(a,b){return!!j(a,b)},l=function(a,b){var c=b.cloneRange(),d=b.cloneRange();return c.setStartBefore(a),d.setEndAfter(a),[c.cloneContents(),d.cloneContents()]},m=function(a,d){var e=c.before(a),f=new b(d),g=f.next(e);return g?g.toRange():null},n=function(a,d){var e=c.after(a),f=new b(d),g=f.prev(e);return g?g.toRange():null},o=function(b,c,d,e){var f=l(b,e),g=b.parentNode;return g.insertBefore(f[0],b),a.each(c,function(a){g.insertBefore(a,b)}),g.insertBefore(f[1],b),g.removeChild(b),n(c[c.length-1],d)},p=function(b,c,d){var e=b.parentNode;return a.each(c,function(a){e.insertBefore(a,b)}),m(b,d)},q=function(a,b,c,d){return d.insertAfter(b.reverse(),a),n(b[0],c)},r=function(a,d,e,h){var k=f(d,a,h),l=j(d,e.startContainer),m=i(g(k.firstChild)),n=1,r=2,s=d.getRoot(),t=function(a){var f=c.fromRangeStart(e),g=new b(d.getRoot()),h=a===n?g.prev(f):g.next(f);return!h||j(d,h.getNode())!==l};return t(n)?p(l,m,s):t(r)?q(l,m,s,d):o(l,m,s,e)};return{isListFragment:d,insertAtCaret:r,isParentBlockLi:k,trimListItems:i,listItems:g}}),g("21",["1p","4m","1v","1j","4n","6","n","4o","9"],function(a,b,c,d,e,f,g,h,i){var j=d.matchNodeNames("td th"),k=function(a,b,c){if("all"===c.getAttribute("data-mce-bogus"))c.parentNode.insertBefore(a.dom.createFragment(b),c);else{var d=c.firstChild,e=c.lastChild;!d||d===e&&"BR"===d.nodeName?a.dom.setHTML(c,b):a.selection.setContent(b)}},l=function(d,l,m){function n(a){function b(a){return d[a]&&3==d[a].nodeType}var c,d,e;return c=I.getRng(!0),d=c.startContainer,e=c.startOffset,3==d.nodeType&&(e>0?a=a.replace(/^ /," "):b("previousSibling")||(a=a.replace(/^ /," ")),e<d.length?a=a.replace(/ (<br>|)$/," "):b("nextSibling")||(a=a.replace(/( | )(<br>|)$/," "))),a}function o(){var a,b,c;a=I.getRng(!0),b=a.startContainer,c=a.startOffset,3==b.nodeType&&a.collapsed&&("\xa0"===b.data[c]?(b.deleteData(c,1),/[\u00a0| ]$/.test(l)||(l+=" ")):"\xa0"===b.data[c-1]&&(b.deleteData(c-1,1),/[\u00a0| ]$/.test(l)||(l=" "+l)))}function p(){if(G){var a=d.getBody(),b=new c(J);i.each(J.select("*[data-mce-fragment]"),function(c){for(var d=c.parentNode;d&&d!=a;d=d.parentNode)H[c.nodeName.toLowerCase()]&&b.compare(d,c)&&J.remove(c,!0)})}}function q(a){for(var b=a;b=b.walk();)1===b.type&&b.attr("data-mce-fragment","1")}function r(a){i.each(a.getElementsByTagName("*"),function(a){a.removeAttribute("data-mce-fragment")})}function s(a){return!!a.getAttribute("data-mce-fragment")}function t(a){return a&&!d.schema.getShortEndedElements()[a.nodeName]}function u(c){function e(a){for(var b=d.getBody();a&&a!==b;a=a.parentNode)if("false"===d.dom.getContentEditable(a))return a;return null}function g(c){var e=a.fromRangeStart(c),f=new b(d.getBody());if(e=f.next(e))return e.toRange()}var h,i,k;if(c){if(I.scrollIntoView(c),h=e(c))return J.remove(c),void I.select(h);C=J.createRng(),D=c.previousSibling,D&&3==D.nodeType?(C.setStart(D,D.nodeValue.length),f.ie||(E=c.nextSibling,E&&3==E.nodeType&&(D.appendData(E.data),E.parentNode.removeChild(E)))):(C.setStartBefore(c),C.setEndBefore(c)),i=J.getParent(c,J.isBlock),J.remove(c),i&&J.isEmpty(i)&&(d.$(i).empty(),C.setStart(i,0),C.setEnd(i,0),j(i)||s(i)||!(k=g(C))?J.add(i,J.create("br",{"data-mce-bogus":"1"})):(C=k,J.remove(i))),I.setRng(C)}}var v,w,x,y,z,A,B,C,D,E,F,G,H=d.schema.getTextInlineElements(),I=d.selection,J=d.dom;/^ | $/.test(l)&&(l=n(l)),v=d.parser,G=m.merge,w=new g({validate:d.settings.validate},d.schema),F='<span id="mce_marker" data-mce-type="bookmark">​</span>',A={content:l,format:"html",selection:!0},d.fire("BeforeSetContent",A),l=A.content,l.indexOf("{$caret}")==-1&&(l+="{$caret}"),l=l.replace(/\{\$caret\}/,F),C=I.getRng();var K=C.startContainer||(C.parentElement?C.parentElement():null),L=d.getBody();K===L&&I.isCollapsed()&&J.isBlock(L.firstChild)&&t(L.firstChild)&&J.isEmpty(L.firstChild)&&(C=J.createRng(),C.setStart(L.firstChild,0),C.setEnd(L.firstChild,0),I.setRng(C)),I.isCollapsed()||(d.selection.setRng(e.normalize(d.selection.getRng())),d.getDoc().execCommand("Delete",!1,null),o()),x=I.getNode();var M={context:x.nodeName.toLowerCase(),data:m.data};if(z=v.parse(l,M),m.paste===!0&&h.isListFragment(z)&&h.isParentBlockLi(J,x))return C=h.insertAtCaret(w,J,d.selection.getRng(!0),z),d.selection.setRng(C),void d.fire("SetContent",A);if(q(z),D=z.lastChild,"mce_marker"==D.attr("id"))for(B=D,D=D.prev;D;D=D.walk(!0))if(3==D.type||!J.isBlock(D.name)){d.schema.isValidChild(D.parent.name,"span")&&D.parent.insert(B,D,"br"===D.name);break}if(d._selectionOverrides.showBlockCaretContainer(x),M.invalid){for(I.setContent(F),x=I.getNode(),y=d.getBody(),9==x.nodeType?x=D=y:D=x;D!==y;)x=D,D=D.parentNode;l=x==y?y.innerHTML:J.getOuterHTML(x),l=w.serialize(v.parse(l.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i,function(){return w.serialize(z)}))),x==y?J.setHTML(y,l):J.setOuterHTML(x,l)}else l=w.serialize(z),k(d,l,x);p(),u(J.get("mce_marker")),r(d.getBody()),d.fire("SetContent",A),d.addVisual()},m=function(a){var b;return"string"!=typeof a?(b=i.extend({paste:a.paste,data:{paste:a.paste}},a),{content:a.content,details:b}):{content:a,details:{}}},n=function(a,b){var c=m(b);l(a,c.content,c.details)};return{insertAtCaret:n}}),g("v",["20","1j","h","c","6","21","9"],function(a,b,c,d,e,f,g){var h=g.each,i=g.extend,j=g.map,k=g.inArray,l=g.explode,m=e.ie&&e.ie<11,n=!0,o=!1;return function(g){function p(a,b,c,d){var e,f,i=0;if(!g.removed){if(/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(a)||d&&d.skip_focus||g.focus(),d=g.fire("BeforeExecCommand",{command:a,ui:b,value:c}),d.isDefaultPrevented())return!1;if(f=a.toLowerCase(),e=H.exec[f])return e(f,b,c),g.fire("ExecCommand",{command:a,ui:b,value:c}),!0;if(h(g.plugins,function(d){if(d.execCommand&&d.execCommand(a,b,c))return g.fire("ExecCommand",{command:a,ui:b,value:c}),i=!0,!1}),i)return i;if(g.theme&&g.theme.execCommand&&g.theme.execCommand(a,b,c))return g.fire("ExecCommand",{command:a,ui:b,value:c}),!0;try{i=g.getDoc().execCommand(a,b,c)}catch(a){}return!!i&&(g.fire("ExecCommand",{command:a,ui:b,value:c}),!0)}}function q(a){var b;if(!g.quirks.isHidden()&&!g.removed){if(a=a.toLowerCase(),b=H.state[a])return b(a);try{return g.getDoc().queryCommandState(a)}catch(a){}return!1}}function r(a){var b;if(!g.quirks.isHidden()&&!g.removed){if(a=a.toLowerCase(),b=H.value[a])return b(a);try{return g.getDoc().queryCommandValue(a)}catch(a){}}}function s(a,b){b=b||"exec",h(a,function(a,c){h(c.toLowerCase().split(","),function(c){H[b][c]=a})})}function t(a,b,c){a=a.toLowerCase(),H.exec[a]=function(a,d,e,f){return b.call(c||g,d,e,f)}}function u(a){if(a=a.toLowerCase(),H.exec[a])return!0;try{return g.getDoc().queryCommandSupported(a)}catch(a){}return!1}function v(a,b,c){a=a.toLowerCase(),H.state[a]=function(){return b.call(c||g)}}function w(a,b,c){a=a.toLowerCase(),H.value[a]=function(){return b.call(c||g)}}function x(a){return a=a.toLowerCase(),!!H.exec[a]}function y(a,b,c){return void 0===b&&(b=o),void 0===c&&(c=null),g.getDoc().execCommand(a,b,c)}function z(a){return F.match(a)}function A(a,b){F.toggle(a,b?{value:b}:void 0),g.nodeChanged()}function B(a){G=E.getBookmark(a)}function C(){E.moveToBookmark(G)}var D,E,F,G,H={state:{},exec:{},value:{}},I=g.settings;g.on("PreInit",function(){D=g.dom,E=g.selection,I=g.settings,F=g.formatter}),i(this,{execCommand:p,queryCommandState:q,queryCommandValue:r,queryCommandSupported:u,addCommands:s,addCommand:t,addQueryStateHandler:v,addQueryValueHandler:w,hasCustomCommand:x}),s({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){g.undoManager.add()},"Cut,Copy,Paste":function(a){var b,c=g.getDoc();try{y(a)}catch(a){b=n}if("paste"!==a||c.queryCommandEnabled(a)||(b=!0),b||!c.queryCommandSupported(a)){var d=g.translate("Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.");e.mac&&(d=d.replace(/Ctrl\+/g,"\u2318+")),g.notificationManager.open({text:d,type:"error"})}},unlink:function(){if(E.isCollapsed()){var a=g.dom.getParent(g.selection.getStart(),"a");return void(a&&g.dom.remove(a,!0))}F.remove("link")},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull,JustifyNone":function(a){var b=a.substring(7);"full"==b&&(b="justify"),h("left,center,right,justify".split(","),function(a){b!=a&&F.remove("align"+a)}),"none"!=b&&A("align"+b)},"InsertUnorderedList,InsertOrderedList":function(a){var b,c;y(a),b=D.getParent(E.getNode(),"ol,ul"),b&&(c=b.parentNode,/^(H[1-6]|P|ADDRESS|PRE)$/.test(c.nodeName)&&(B(),D.split(c,b),C()))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(a){A(a)},"ForeColor,HiliteColor,FontName":function(a,b,c){A(a,c)},FontSize:function(a,b,c){var d,e;c>=1&&c<=7&&(e=l(I.font_size_style_values),d=l(I.font_size_classes),c=d?d[c-1]||c:e[c-1]||c),A(a,c)},RemoveFormat:function(a){F.remove(a)},mceBlockQuote:function(){A("blockquote")},FormatBlock:function(a,b,c){return A(c||"p")},mceCleanup:function(){var a=E.getBookmark();g.setContent(g.getContent({cleanup:n}),{cleanup:n}),E.moveToBookmark(a)},mceRemoveNode:function(a,b,c){var d=c||E.getNode();d!=g.getBody()&&(B(),g.dom.remove(d,n),C())},mceSelectNodeDepth:function(a,b,c){var d=0;D.getParent(E.getNode(),function(a){if(1==a.nodeType&&d++==c)return E.select(a),o},g.getBody())},mceSelectNode:function(a,b,c){E.select(c)},mceInsertContent:function(a,b,c){f.insertAtCaret(g,c)},mceInsertRawHTML:function(a,b,c){E.setContent("tiny_mce_marker"),g.setContent(g.getContent().replace(/tiny_mce_marker/g,function(){return c}))},mceToggleFormat:function(a,b,c){A(c)},mceSetContent:function(a,b,c){g.setContent(c)},"Indent,Outdent":function(a){var b,c,d;b=I.indentation,c=/[a-z%]+$/i.exec(b),b=parseInt(b,10),q("InsertUnorderedList")||q("InsertOrderedList")?y(a):(I.forced_root_block||D.getParent(E.getNode(),D.isBlock)||F.apply("div"),h(E.getSelectedBlocks(),function(e){if("false"!==D.getContentEditable(e)&&"LI"!==e.nodeName){var f=g.getParam("indent_use_margin",!1)?"margin":"padding";f="TABLE"===e.nodeName?"margin":f,f+="rtl"==D.getStyle(e,"direction",!0)?"Right":"Left","outdent"==a?(d=Math.max(0,parseInt(e.style[f]||0,10)-b),D.setStyle(e,f,d?d+c:"")):(d=parseInt(e.style[f]||0,10)+b+c,D.setStyle(e,f,d))}}))},mceRepaint:function(){},InsertHorizontalRule:function(){g.execCommand("mceInsertContent",!1,"<hr />")},mceToggleVisualAid:function(){g.hasVisual=!g.hasVisual,g.addVisual()},mceReplaceContent:function(a,b,c){g.execCommand("mceInsertContent",!1,c.replace(/\{\$selection\}/g,E.getContent({format:"text"})))},mceInsertLink:function(a,b,c){var d;"string"==typeof c&&(c={href:c}),d=D.getParent(E.getNode(),"a"),c.href=c.href.replace(" ","%20"),d&&c.href||F.remove("link"),c.href&&F.apply("link",c,d)},selectAll:function(){var a,c=D.getRoot();if(E.getRng().setStart){var d=D.getParent(E.getStart(),b.isContentEditableTrue);d&&(a=D.createRng(),a.selectNodeContents(d),E.setRng(a))}else a=E.getRng(),a.item||(a.moveToElementText(c),a.select())},"delete":function(){a.deleteCommand(g)},forwardDelete:function(){a.forwardDeleteCommand(g)},mceNewDocument:function(){g.setContent("")},InsertLineBreak:function(a,b,e){function f(){for(var a,b=new d(p,r),c=g.schema.getNonEmptyElements();a=b.next();)if(c[a.nodeName.toLowerCase()]||a.length>0)return!0}var h,i,j,k=e,l=E.getRng(!0);new c(D).normalize(l);var o=l.startOffset,p=l.startContainer;if(1==p.nodeType&&p.hasChildNodes()){var q=o>p.childNodes.length-1;p=p.childNodes[Math.min(o,p.childNodes.length-1)]||p,o=q&&3==p.nodeType?p.nodeValue.length:0}var r=D.getParent(p,D.isBlock),s=r?r.nodeName.toUpperCase():"",t=r?D.getParent(r.parentNode,D.isBlock):null,u=t?t.nodeName.toUpperCase():"",v=k&&k.ctrlKey;"LI"!=u||v||(r=t,s=u),p&&3==p.nodeType&&o>=p.nodeValue.length&&(m||f()||(h=D.create("br"),l.insertNode(h),l.setStartAfter(h),l.setEndAfter(h),i=!0)),h=D.create("br"),l.insertNode(h);var w=D.doc.documentMode;return m&&"PRE"==s&&(!w||w<8)&&h.parentNode.insertBefore(D.doc.createTextNode("\r"),h),j=D.create("span",{}," "),h.parentNode.insertBefore(j,h),E.scrollIntoView(j),D.remove(j),i?(l.setStartBefore(h),l.setEndBefore(h)):(l.setStartAfter(h),l.setEndAfter(h)),E.setRng(l),g.undoManager.add(),n}}),s({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(a){var b="align"+a.substring(7),c=E.isCollapsed()?[D.getParent(E.getNode(),D.isBlock)]:E.getSelectedBlocks(),d=j(c,function(a){return!!F.matchNode(a,b)});return k(d,n)!==-1},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(a){return z(a)},mceBlockQuote:function(){return z("blockquote")},Outdent:function(){var a;if(I.inline_styles){if((a=D.getParent(E.getStart(),D.isBlock))&&parseInt(a.style.paddingLeft,10)>0)return n;if((a=D.getParent(E.getEnd(),D.isBlock))&&parseInt(a.style.paddingLeft,10)>0)return n}return q("InsertUnorderedList")||q("InsertOrderedList")||!I.inline_styles&&!!D.getParent(E.getNode(),"BLOCKQUOTE")},"InsertUnorderedList,InsertOrderedList":function(a){var b=D.getParent(E.getNode(),"ul,ol");return b&&("insertunorderedlist"===a&&"UL"===b.tagName||"insertorderedlist"===a&&"OL"===b.tagName)}},"state"),s({"FontSize,FontName":function(a){var b,c=0;return(b=D.getParent(E.getNode(),"span"))&&(c="fontsize"==a?b.style.fontSize:b.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()),c}},"value"),s({Undo:function(){g.undoManager.undo()},Redo:function(){g.undoManager.redo()}})}}),g("w",["22","9"],function(a,b){function c(b,g){var h,i,j=this;if(b=e(b),g=j.settings=g||{},h=g.base_uri,/^([\w\-]+):([^\/]{2})/i.test(b)||/^\s*#/.test(b))return void(j.source=b);var k=0===b.indexOf("//");0!==b.indexOf("/")||k||(b=(h?h.protocol||"http":"http")+"://mce_host"+b),/^[\w\-]*:?\/\//.test(b)||(i=g.base_uri?g.base_uri.path:new c(a.location.href).directory,""===g.base_uri.protocol?b="//mce_host"+j.toAbsPath(i,b):(b=/([^#?]*)([#?]?.*)/.exec(b),b=(h&&h.protocol||"http")+"://mce_host"+j.toAbsPath(i,b[1])+b[2])),b=b.replace(/@@/g,"(mce_at)"),b=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(b),d(f,function(a,c){var d=b[c];d&&(d=d.replace(/\(mce_at\)/g,"@@")),j[a]=d}),h&&(j.protocol||(j.protocol=h.protocol),j.userInfo||(j.userInfo=h.userInfo),j.port||"mce_host"!==j.host||(j.port=h.port),j.host&&"mce_host"!==j.host||(j.host=h.host),j.source=""),k&&(j.protocol="")}var d=b.each,e=b.trim,f="source protocol authority userInfo user password host port relative path directory file query anchor".split(" "),g={ftp:21,http:80,https:443,mailto:25};return c.prototype={setPath:function(a){var b=this;a=/^(.*?)\/?(\w+)?$/.exec(a),b.path=a[0],b.directory=a[1],b.file=a[2],b.source="",b.getURI()},toRelative:function(a){var b,d=this;if("./"===a)return a;if(a=new c(a,{base_uri:d}),"mce_host"!=a.host&&d.host!=a.host&&a.host||d.port!=a.port||d.protocol!=a.protocol&&""!==a.protocol)return a.getURI();var e=d.getURI(),f=a.getURI();return e==f||"/"==e.charAt(e.length-1)&&e.substr(0,e.length-1)==f?e:(b=d.toRelPath(d.path,a.path),a.query&&(b+="?"+a.query),a.anchor&&(b+="#"+a.anchor),b)},toAbsolute:function(a,b){return a=new c(a,{base_uri:this}),a.getURI(b&&this.isSameOrigin(a))},isSameOrigin:function(a){if(this.host==a.host&&this.protocol==a.protocol){if(this.port==a.port)return!0;var b=g[this.protocol];if(b&&(this.port||b)==(a.port||b))return!0}return!1},toRelPath:function(a,b){var c,d,e,f=0,g="";if(a=a.substring(0,a.lastIndexOf("/")),a=a.split("/"),c=b.split("/"),a.length>=c.length)for(d=0,e=a.length;d<e;d++)if(d>=c.length||a[d]!=c[d]){f=d+1;break}if(a.length<c.length)for(d=0,e=c.length;d<e;d++)if(d>=a.length||a[d]!=c[d]){f=d+1;break}if(1===f)return b;for(d=0,e=a.length-(f-1);d<e;d++)g+="../";for(d=f-1,e=c.length;d<e;d++)g+=d!=f-1?"/"+c[d]:c[d];return g},toAbsPath:function(a,b){var c,e,f,g=0,h=[];for(e=/\/$/.test(b)?"/":"",a=a.split("/"),b=b.split("/"), -d(a,function(a){a&&h.push(a)}),a=h,c=b.length-1,h=[];c>=0;c--)0!==b[c].length&&"."!==b[c]&&(".."!==b[c]?g>0?g--:h.push(b[c]):g++);return c=a.length-g,f=c<=0?h.reverse().join("/"):a.slice(0,c).join("/")+"/"+h.reverse().join("/"),0!==f.indexOf("/")&&(f="/"+f),e&&f.lastIndexOf("/")!==f.length-1&&(f+=e),f},getURI:function(a){var b,c=this;return c.source&&!a||(b="",a||(b+=c.protocol?c.protocol+"://":"//",c.userInfo&&(b+=c.userInfo+"@"),c.host&&(b+=c.host),c.port&&(b+=":"+c.port)),c.path&&(b+=c.path),c.query&&(b+="?"+c.query),c.anchor&&(b+="#"+c.anchor),c.source=b),c.source}},c.parseDataUri=function(a){var b,c;return a=decodeURIComponent(a).split(","),c=/data:([^;]+)/.exec(a[0]),c&&(b=c[1]),{type:b,data:a[1]}},c.getDocumentBaseUrl=function(a){var b;return b=0!==a.protocol.indexOf("http")&&"file:"!==a.protocol?a.href:a.protocol+"//"+a.host+a.pathname,/^[^:]+:\/\/\/?[^\/]+\//.test(b)&&(b=b.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,""),/[\/\\]$/.test(b)||(b+="/")),b},c}),g("x",["9"],function(a){function b(){}var c,d,e=a.each,f=a.extend;return b.extend=c=function(a){function b(){var a,b,c,e=this;if(!d&&(e.init&&e.init.apply(e,arguments),b=e.Mixins))for(a=b.length;a--;)c=b[a],c.init&&c.init.apply(e,arguments)}function g(){return this}function h(a,b){return function(){var c,d=this,e=d._super;return d._super=m[a],c=b.apply(d,arguments),d._super=e,c}}var i,j,k,l=this,m=l.prototype;d=!0,i=new l,d=!1,a.Mixins&&(e(a.Mixins,function(b){for(var c in b)"init"!==c&&(a[c]=b[c])}),m.Mixins&&(a.Mixins=m.Mixins.concat(a.Mixins))),a.Methods&&e(a.Methods.split(","),function(b){a[b]=g}),a.Properties&&e(a.Properties.split(","),function(b){var c="_"+b;a[b]=function(a){var b,d=this;return a!==b?(d[c]=a,d):d[c]}}),a.Statics&&e(a.Statics,function(a,c){b[c]=a}),a.Defaults&&m.Defaults&&(a.Defaults=f({},m.Defaults,a.Defaults));for(j in a)k=a[j],"function"==typeof k&&m[j]?i[j]=h(j,k):i[j]=k;return b.prototype=i,b.constructor=b,b.extend=c,b},b}),g("y",["9"],function(a){function b(b){function c(){return!1}function d(){return!0}function e(a,e){var f,h,i,k;if(a=a.toLowerCase(),e=e||{},e.type=a,e.target||(e.target=j),e.preventDefault||(e.preventDefault=function(){e.isDefaultPrevented=d},e.stopPropagation=function(){e.isPropagationStopped=d},e.stopImmediatePropagation=function(){e.isImmediatePropagationStopped=d},e.isDefaultPrevented=c,e.isPropagationStopped=c,e.isImmediatePropagationStopped=c),b.beforeFire&&b.beforeFire(e),f=m[a])for(h=0,i=f.length;h<i;h++){if(k=f[h],k.once&&g(a,k.func),e.isImmediatePropagationStopped())return e.stopPropagation(),e;if(k.func.call(j,e)===!1)return e.preventDefault(),e}return e}function f(b,d,e,f){var g,h,i;if(d===!1&&(d=c),d)for(d={func:d},f&&a.extend(d,f),h=b.toLowerCase().split(" "),i=h.length;i--;)b=h[i],g=m[b],g||(g=m[b]=[],k(b,!0)),e?g.unshift(d):g.push(d);return l}function g(a,b){var c,d,e,f,g;if(a)for(f=a.toLowerCase().split(" "),c=f.length;c--;){if(a=f[c],d=m[a],!a){for(e in m)k(e,!1),delete m[e];return l}if(d){if(b)for(g=d.length;g--;)d[g].func===b&&(d=d.slice(0,g).concat(d.slice(g+1)),m[a]=d);else d.length=0;d.length||(k(a,!1),delete m[a])}}else{for(a in m)k(a,!1);m={}}return l}function h(a,b,c){return f(a,b,c,{once:!0})}function i(a){return a=a.toLowerCase(),!(!m[a]||0===m[a].length)}var j,k,l=this,m={};b=b||{},j=b.scope||l,k=b.toggleEvent||c,l.fire=e,l.on=f,l.off=g,l.once=h,l.has=i}var c=a.makeMap("focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover draggesture dragdrop drop drag submit compositionstart compositionend compositionupdate touchstart touchmove touchend"," ");return b.isNative=function(a){return!!c[a.toLowerCase()]},b}),g("z",["y"],function(a){function b(b){return b._eventDispatcher||(b._eventDispatcher=new a({scope:b,toggleEvent:function(c,d){a.isNative(c)&&b.toggleNativeEvent&&b.toggleNativeEvent(c,d)}})),b._eventDispatcher}return{fire:function(a,c,d){var e=this;if(e.removed&&"remove"!==a)return c;if(c=b(e).fire(a,c,d),d!==!1&&e.parent)for(var f=e.parent();f&&!c.isPropagationStopped();)f.fire(a,c,!1),f=f.parent();return c},on:function(a,c,d){return b(this).on(a,c,d)},off:function(a,c){return b(this).off(a,c)},once:function(a,c){return b(this).once(a,c)},hasEventListeners:function(a){return b(this).has(a)}}}),g("5q",[],function(){function a(a){this.create=a.create}return a.create=function(b,c){return new a({create:function(a,d){function e(b){a.set(d,b.value)}function f(a){b.set(c,a.value)}var g;return a.on("change:"+d,f),b.on("change:"+c,e),g=a._bindings,g||(g=a._bindings=[],a.on("destroy",function(){for(var a=g.length;a--;)g[a]()})),g.push(function(){b.off("change:"+c,e)}),b.get(c)}})},a}),g("4p",["5q","x","z","9"],function(a,b,c,d){function e(a){return a.nodeType>0}function f(a,b){var c,g;if(a===b)return!0;if(null===a||null===b)return a===b;if("object"!=typeof a||"object"!=typeof b)return a===b;if(d.isArray(b)){if(a.length!==b.length)return!1;for(c=a.length;c--;)if(!f(a[c],b[c]))return!1}if(e(a)||e(b))return a===b;g={};for(c in b){if(!f(a[c],b[c]))return!1;g[c]=!0}for(c in a)if(!g[c]&&!f(a[c],b[c]))return!1;return!0}return b.extend({Mixins:[c],init:function(b){var c,d;b=b||{};for(c in b)d=b[c],d instanceof a&&(b[c]=d.create(this,c));this.data=b},set:function(b,c){var d,e,g=this.data[b];if(c instanceof a&&(c=c.create(this,b)),"object"==typeof b){for(d in b)this.set(d,b[d]);return this}return f(g,c)||(this.data[b]=c,e={target:this,name:b,value:c,oldValue:g},this.fire("change:"+b,e),this.fire("change",e)),this},get:function(a){return this.data[a]},has:function(a){return a in this.data},bind:function(b){return a.create(this,b)},destroy:function(){this.fire("destroy")}})}),g("2d",["x"],function(a){"use strict";function b(a){for(var b,c=[],d=a.length;d--;)b=a[d],b.__checked||(c.push(b),b.__checked=1);for(d=c.length;d--;)delete c[d].__checked;return c}var c,d=/^([\w\\*]+)?(?:#([\w\-\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i,e=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,f=/^\s*|\s*$/g,g=a.extend({init:function(a){function b(a){if(a)return a=a.toLowerCase(),function(b){return"*"===a||b.type===a}}function c(a){if(a)return function(b){return b._name===a}}function g(a){if(a)return a=a.split("."),function(b){for(var c=a.length;c--;)if(!b.classes.contains(a[c]))return!1;return!0}}function h(a,b,c){if(a)return function(d){var e=d[a]?d[a]():"";return b?"="===b?e===c:"*="===b?e.indexOf(c)>=0:"~="===b?(" "+e+" ").indexOf(" "+c+" ")>=0:"!="===b?e!=c:"^="===b?0===e.indexOf(c):"$="===b&&e.substr(e.length-c.length)===c:!!c}}function i(a){var b;if(a)return a=/(?:not\((.+)\))|(.+)/i.exec(a),a[1]?(b=k(a[1],[]),function(a){return!l(a,b)}):(a=a[2],function(b,c,d){return"first"===a?0===c:"last"===a?c===d-1:"even"===a?c%2===0:"odd"===a?c%2===1:!!b[a]&&b[a]()})}function j(a,e,j){function k(a){a&&e.push(a)}var l;return l=d.exec(a.replace(f,"")),k(b(l[1])),k(c(l[2])),k(g(l[3])),k(h(l[4],l[5],l[6])),k(i(l[7])),e.pseudo=!!l[7],e.direct=j,e}function k(a,b){var c,d,f,g=[];do if(e.exec(""),d=e.exec(a),d&&(a=d[3],g.push(d[1]),d[2])){c=d[3];break}while(d);for(c&&k(c,b),a=[],f=0;f<g.length;f++)">"!=g[f]&&a.push(j(g[f],[],">"===g[f-1]));return b.push(a),b}var l=this.match;this._selectors=k(a,[])},match:function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o;for(b=b||this._selectors,c=0,d=b.length;c<d;c++){for(g=b[c],f=g.length,o=a,n=0,e=f-1;e>=0;e--)for(j=g[e];o;){if(j.pseudo)for(m=o.parent().items(),k=l=m.length;k--&&m[k]!==o;);for(h=0,i=j.length;h<i;h++)if(!j[h](o,k,l)){h=i+1;break}if(h===i){n++;break}if(e===f-1)break;o=o.parent()}if(n===f)return!0}return!1},find:function(a){function d(a,b,c){var e,f,g,i,j,k=b[c];for(e=0,f=a.length;e<f;e++){for(j=a[e],g=0,i=k.length;g<i;g++)if(!k[g](j,e,f)){g=i+1;break}if(g===i)c==b.length-1?h.push(j):j.items&&d(j.items(),b,c+1);else if(k.direct)return;j.items&&d(j.items(),b,c)}}var e,f,h=[],i=this._selectors;if(a.items){for(e=0,f=i.length;e<f;e++)d(a.items(),i[e],0);f>1&&(h=b(h))}return c||(c=g.Collection),new c(h)}});return g}),g("2e",["9","2d","x"],function(a,b,c){"use strict";var d,e,f=Array.prototype.push,g=Array.prototype.slice;return e={length:0,init:function(a){a&&this.add(a)},add:function(b){var c=this;return a.isArray(b)?f.apply(c,b):b instanceof d?c.add(b.toArray()):f.call(c,b),c},set:function(a){var b,c=this,d=c.length;for(c.length=0,c.add(a),b=c.length;b<d;b++)delete c[b];return c},filter:function(a){var c,e,f,g,h=this,i=[];for("string"==typeof a?(a=new b(a),g=function(b){return a.match(b)}):g=a,c=0,e=h.length;c<e;c++)f=h[c],g(f)&&i.push(f);return new d(i)},slice:function(){return new d(g.apply(this,arguments))},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},each:function(b){return a.each(this,b),this},toArray:function(){return a.toArray(this)},indexOf:function(a){for(var b=this,c=b.length;c--&&b[c]!==a;);return c},reverse:function(){return new d(a.toArray(this).reverse())},hasClass:function(a){return!!this[0]&&this[0].classes.contains(a)},prop:function(a,b){var c,d,e=this;return b!==c?(e.each(function(c){c[a]&&c[a](b)}),e):(d=e[0],d&&d[a]?d[a]():void 0)},exec:function(b){var c=this,d=a.toArray(arguments).slice(1);return c.each(function(a){a[b]&&a[b].apply(a,d)}),c},remove:function(){for(var a=this.length;a--;)this[a].remove();return this},addClass:function(a){return this.each(function(b){b.classes.add(a)})},removeClass:function(a){return this.each(function(b){b.classes.remove(a)})}},a.each("fire on off show hide append prepend before after reflow".split(" "),function(b){e[b]=function(){var c=a.toArray(arguments);return this.each(function(a){b in a&&a[b].apply(a,c)}),this}}),a.each("text name disabled active selected checked visible parent value data".split(" "),function(a){e[a]=function(b){return this.prop(a,b)}}),d=c.extend(e),b.Collection=d,d}),g("4q",["6","9","e"],function(a,b,c){"use strict";var d=0,e={id:function(){return"mceu_"+d++},create:function(a,d,e){var f=document.createElement(a);return c.DOM.setAttribs(f,d),"string"==typeof e?f.innerHTML=e:b.each(e,function(a){a.nodeType&&f.appendChild(a)}),f},createFragment:function(a){return c.DOM.createFragment(a)},getWindowSize:function(){return c.DOM.getViewPort()},getSize:function(a){var b,c;if(a.getBoundingClientRect){var d=a.getBoundingClientRect();b=Math.max(d.width||d.right-d.left,a.offsetWidth),c=Math.max(d.height||d.bottom-d.bottom,a.offsetHeight)}else b=a.offsetWidth,c=a.offsetHeight;return{width:b,height:c}},getPos:function(a,b){return c.DOM.getPos(a,b||e.getContainer())},getContainer:function(){return a.container?a.container:document.body},getViewPort:function(a){return c.DOM.getViewPort(a)},get:function(a){return document.getElementById(a)},addClass:function(a,b){return c.DOM.addClass(a,b)},removeClass:function(a,b){return c.DOM.removeClass(a,b)},hasClass:function(a,b){return c.DOM.hasClass(a,b)},toggleClass:function(a,b,d){return c.DOM.toggleClass(a,b,d)},css:function(a,b,d){return c.DOM.setStyle(a,b,d)},getRuntimeStyle:function(a,b){return c.DOM.getStyle(a,b,!0)},on:function(a,b,d,e){return c.DOM.bind(a,b,d,e)},off:function(a,b,d){return c.DOM.unbind(a,b,d)},fire:function(a,b,d){return c.DOM.fire(a,b,d)},innerHtml:function(a,b){c.DOM.setHTML(a,b)}};return e}),g("4r",[],function(){"use strict";return{parseBox:function(a){var b,c=10;if(a)return"number"==typeof a?(a=a||0,{top:a,left:a,bottom:a,right:a}):(a=a.split(" "),b=a.length,1===b?a[1]=a[2]=a[3]=a[0]:2===b?(a[2]=a[0],a[3]=a[1]):3===b&&(a[3]=a[1]),{top:parseInt(a[0],c)||0,right:parseInt(a[1],c)||0,bottom:parseInt(a[2],c)||0,left:parseInt(a[3],c)||0})},measureBox:function(a,b){function c(b){var c=document.defaultView;return c?(b=b.replace(/[A-Z]/g,function(a){return"-"+a}),c.getComputedStyle(a,null).getPropertyValue(b)):a.currentStyle[b]}function d(a){var b=parseFloat(c(a),10);return isNaN(b)?0:b}return{top:d(b+"TopWidth"),right:d(b+"RightWidth"),bottom:d(b+"BottomWidth"),left:d(b+"LeftWidth")}}}}),g("4s",["9"],function(a){"use strict";function b(){}function c(a){this.cls=[],this.cls._map={},this.onchange=a||b,this.prefix=""}return a.extend(c.prototype,{add:function(a){return a&&!this.contains(a)&&(this.cls._map[a]=!0,this.cls.push(a),this._change()),this},remove:function(a){if(this.contains(a)){for(var b=0;b<this.cls.length&&this.cls[b]!==a;b++);this.cls.splice(b,1),delete this.cls._map[a],this._change()}return this},toggle:function(a,b){var c=this.contains(a);return c!==b&&(c?this.remove(a):this.add(a),this._change()),this},contains:function(a){return!!this.cls._map[a]},_change:function(){delete this.clsValue,this.onchange.call(this)}}),c.prototype.toString=function(){var a;if(this.clsValue)return this.clsValue;a="";for(var b=0;b<this.cls.length;b++)b>0&&(a+=" "),a+=this.prefix+this.cls[b];return a},c}),g("2f",["5"],function(a){var b,c={};return{add:function(d){var e=d.parent();if(e){if(!e._layout||e._layout.isNative())return;c[e._id]||(c[e._id]=e),b||(b=!0,a.requestAnimationFrame(function(){var a,d;b=!1;for(a in c)d=c[a],d.state.get("rendered")&&d.reflow();c={}},document.body))}},remove:function(a){c[a._id]&&delete c[a._id]}}}),g("2g",["x","9","y","4p","2e","4q","a","4r","4s","2f"],function(a,b,c,d,e,f,g,h,i,j){"use strict";function k(a){return a._eventDispatcher||(a._eventDispatcher=new c({scope:a,toggleEvent:function(b,d){d&&c.isNative(b)&&(a._nativeEvents||(a._nativeEvents={}),a._nativeEvents[b]=!0,a.state.get("rendered")&&l(a))}})),a._eventDispatcher}function l(a){function b(b){var c=a.getParentCtrl(b.target);c&&c.fire(b.type,b)}function c(){var a=j._lastHoverCtrl;a&&(a.fire("mouseleave",{target:a.getEl()}),a.parents().each(function(a){a.fire("mouseleave",{target:a.getEl()})}),j._lastHoverCtrl=null)}function d(b){var c,d,e,f=a.getParentCtrl(b.target),g=j._lastHoverCtrl,h=0;if(f!==g){if(j._lastHoverCtrl=f,d=f.parents().toArray().reverse(),d.push(f),g){for(e=g.parents().toArray().reverse(),e.push(g),h=0;h<e.length&&d[h]===e[h];h++);for(c=e.length-1;c>=h;c--)g=e[c],g.fire("mouseleave",{target:g.getEl()})}for(c=h;c<d.length;c++)f=d[c],f.fire("mouseenter",{target:f.getEl()})}}function e(b){b.preventDefault(),"mousewheel"==b.type?(b.deltaY=-.025*b.wheelDelta,b.wheelDeltaX&&(b.deltaX=-.025*b.wheelDeltaX)):(b.deltaX=0,b.deltaY=b.detail),b=a.fire("wheel",b)}var f,h,i,j,k,l;if(k=a._nativeEvents){for(i=a.parents().toArray(),i.unshift(a),f=0,h=i.length;!j&&f<h;f++)j=i[f]._eventsRoot;for(j||(j=i[i.length-1]||a),a._eventsRoot=j,h=f,f=0;f<h;f++)i[f]._eventsRoot=j;var m=j._delegates;m||(m=j._delegates={});for(l in k){if(!k)return!1;"wheel"!==l||o?("mouseenter"===l||"mouseleave"===l?j._hasMouseEnter||(g(j.getEl()).on("mouseleave",c).on("mouseover",d),j._hasMouseEnter=1):m[l]||(g(j.getEl()).on(l,b),m[l]=!0),k[l]=!1):n?g(a.getEl()).on("mousewheel",e):g(a.getEl()).on("DOMMouseScroll",e)}}}var m,n="onmousewheel"in document,o=!1,p="mce-",q=0,r={Statics:{classPrefix:p},isRtl:function(){return m.rtl},classPrefix:p,init:function(a){function c(a){var b;for(a=a.split(" "),b=0;b<a.length;b++)j.classes.add(a[b])}var e,f,j=this;j.settings=a=b.extend({},j.Defaults,a),j._id=a.id||"mceu_"+q++,j._aria={role:a.role},j._elmCache={},j.$=g,j.state=new d({visible:!0,active:!1,disabled:!1,value:""}),j.data=new d(a.data),j.classes=new i(function(){j.state.get("rendered")&&(j.getEl().className=this.toString())}),j.classes.prefix=j.classPrefix,e=a.classes,e&&(j.Defaults&&(f=j.Defaults.classes,f&&e!=f&&c(f)),c(e)),b.each("title text name visible disabled active value".split(" "),function(b){b in a&&j[b](a[b])}),j.on("click",function(){if(j.disabled())return!1}),j.settings=a,j.borderBox=h.parseBox(a.border),j.paddingBox=h.parseBox(a.padding),j.marginBox=h.parseBox(a.margin),a.hidden&&j.hide()},Properties:"parent,name",getContainerElm:function(){return f.getContainer()},getParentCtrl:function(a){for(var b,c=this.getRoot().controlIdLookup;a&&c&&!(b=c[a.id]);)a=a.parentNode;return b},initLayoutRect:function(){var a,b,c,d,e,g,i,j,k,l,m=this,n=m.settings,o=m.getEl();a=m.borderBox=m.borderBox||h.measureBox(o,"border"),m.paddingBox=m.paddingBox||h.measureBox(o,"padding"),m.marginBox=m.marginBox||h.measureBox(o,"margin"),l=f.getSize(o),j=n.minWidth,k=n.minHeight,e=j||l.width,g=k||l.height,c=n.width,d=n.height,i=n.autoResize,i="undefined"!=typeof i?i:!c&&!d,c=c||e,d=d||g;var p=a.left+a.right,q=a.top+a.bottom,r=n.maxWidth||65535,s=n.maxHeight||65535;return m._layoutRect=b={x:n.x||0,y:n.y||0,w:c,h:d,deltaW:p,deltaH:q,contentW:c-p,contentH:d-q,innerW:c-p,innerH:d-q,startMinWidth:j||0,startMinHeight:k||0,minW:Math.min(e,r),minH:Math.min(g,s),maxW:r,maxH:s,autoResize:i,scrollW:0},m._lastLayoutRect={},b},layoutRect:function(a){var b,c,d,e,f,g,h=this,i=h._layoutRect;return i||(i=h.initLayoutRect()),a?(d=i.deltaW,e=i.deltaH,a.x!==f&&(i.x=a.x),a.y!==f&&(i.y=a.y),a.minW!==f&&(i.minW=a.minW),a.minH!==f&&(i.minH=a.minH),c=a.w,c!==f&&(c=c<i.minW?i.minW:c,c=c>i.maxW?i.maxW:c,i.w=c,i.innerW=c-d),c=a.h,c!==f&&(c=c<i.minH?i.minH:c,c=c>i.maxH?i.maxH:c,i.h=c,i.innerH=c-e),c=a.innerW,c!==f&&(c=c<i.minW-d?i.minW-d:c,c=c>i.maxW-d?i.maxW-d:c,i.innerW=c,i.w=c+d),c=a.innerH,c!==f&&(c=c<i.minH-e?i.minH-e:c,c=c>i.maxH-e?i.maxH-e:c,i.innerH=c,i.h=c+e),a.contentW!==f&&(i.contentW=a.contentW),a.contentH!==f&&(i.contentH=a.contentH),b=h._lastLayoutRect,b.x===i.x&&b.y===i.y&&b.w===i.w&&b.h===i.h||(g=m.repaintControls,g&&g.map&&!g.map[h._id]&&(g.push(h),g.map[h._id]=!0),b.x=i.x,b.y=i.y,b.w=i.w,b.h=i.h),h):i},repaint:function(){var a,b,c,d,e,f,g,h,i,j,k=this;i=document.createRange?function(a){return a}:Math.round,a=k.getEl().style,d=k._layoutRect,h=k._lastRepaintRect||{},e=k.borderBox,f=e.left+e.right,g=e.top+e.bottom,d.x!==h.x&&(a.left=i(d.x)+"px",h.x=d.x),d.y!==h.y&&(a.top=i(d.y)+"px",h.y=d.y),d.w!==h.w&&(j=i(d.w-f),a.width=(j>=0?j:0)+"px",h.w=d.w),d.h!==h.h&&(j=i(d.h-g),a.height=(j>=0?j:0)+"px",h.h=d.h),k._hasBody&&d.innerW!==h.innerW&&(j=i(d.innerW),c=k.getEl("body"),c&&(b=c.style,b.width=(j>=0?j:0)+"px"),h.innerW=d.innerW),k._hasBody&&d.innerH!==h.innerH&&(j=i(d.innerH),c=c||k.getEl("body"),c&&(b=b||c.style,b.height=(j>=0?j:0)+"px"),h.innerH=d.innerH),k._lastRepaintRect=h,k.fire("repaint",{},!1)},updateLayoutRect:function(){var a=this;a.parent()._lastRect=null,f.css(a.getEl(),{width:"",height:""}),a._layoutRect=a._lastRepaintRect=a._lastLayoutRect=null,a.initLayoutRect()},on:function(a,b){function c(a){var b,c;return"string"!=typeof a?a:function(e){return b||d.parentsAndSelf().each(function(d){var e=d.settings.callbacks;if(e&&(b=e[a]))return c=d,!1}),b?b.call(c,e):(e.action=a,void this.fire("execute",e))}}var d=this;return k(d).on(a,c(b)),d},off:function(a,b){return k(this).off(a,b),this},fire:function(a,b,c){var d=this;if(b=b||{},b.control||(b.control=d),b=k(d).fire(a,b),c!==!1&&d.parent)for(var e=d.parent();e&&!b.isPropagationStopped();)e.fire(a,b,!1),e=e.parent();return b},hasEventListeners:function(a){return k(this).has(a)},parents:function(a){var b,c=this,d=new e;for(b=c.parent();b;b=b.parent())d.add(b);return a&&(d=d.filter(a)),d},parentsAndSelf:function(a){return new e(this).add(this.parents(a))},next:function(){var a=this.parent().items();return a[a.indexOf(this)+1]},prev:function(){var a=this.parent().items();return a[a.indexOf(this)-1]},innerHtml:function(a){return this.$el.html(a),this},getEl:function(a){var b=a?this._id+"-"+a:this._id;return this._elmCache[b]||(this._elmCache[b]=g("#"+b)[0]),this._elmCache[b]},show:function(){return this.visible(!0)},hide:function(){return this.visible(!1)},focus:function(){try{this.getEl().focus()}catch(a){}return this},blur:function(){return this.getEl().blur(),this},aria:function(a,b){var c=this,d=c.getEl(c.ariaTarget);return"undefined"==typeof b?c._aria[a]:(c._aria[a]=b,c.state.get("rendered")&&d.setAttribute("role"==a?a:"aria-"+a,b),c)},encode:function(a,b){return b!==!1&&(a=this.translate(a)),(a||"").replace(/[&<>"]/g,function(a){return"&#"+a.charCodeAt(0)+";"})},translate:function(a){return m.translate?m.translate(a):a},before:function(a){var b=this,c=b.parent();return c&&c.insert(a,c.items().indexOf(b),!0),b},after:function(a){var b=this,c=b.parent();return c&&c.insert(a,c.items().indexOf(b)),b},remove:function(){var a,b,c=this,d=c.getEl(),e=c.parent();if(c.items){var f=c.items().toArray();for(b=f.length;b--;)f[b].remove()}e&&e.items&&(a=[],e.items().each(function(b){b!==c&&a.push(b)}),e.items().set(a),e._lastRect=null),c._eventsRoot&&c._eventsRoot==c&&g(d).off();var h=c.getRoot().controlIdLookup;return h&&delete h[c._id],d&&d.parentNode&&d.parentNode.removeChild(d),c.state.set("rendered",!1),c.state.destroy(),c.fire("remove"),c},renderBefore:function(a){return g(a).before(this.renderHtml()),this.postRender(),this},renderTo:function(a){return g(a||this.getContainerElm()).append(this.renderHtml()),this.postRender(),this},preRender:function(){},render:function(){},renderHtml:function(){return'<div id="'+this._id+'" class="'+this.classes+'"></div>'},postRender:function(){var a,b,c,d,e,f=this,h=f.settings;f.$el=g(f.getEl()),f.state.set("rendered",!0);for(d in h)0===d.indexOf("on")&&f.on(d.substr(2),h[d]);if(f._eventsRoot){for(c=f.parent();!e&&c;c=c.parent())e=c._eventsRoot;if(e)for(d in e._nativeEvents)f._nativeEvents[d]=!0}l(f),h.style&&(a=f.getEl(),a&&(a.setAttribute("style",h.style),a.style.cssText=h.style)),f.settings.border&&(b=f.borderBox,f.$el.css({"border-top-width":b.top,"border-right-width":b.right,"border-bottom-width":b.bottom,"border-left-width":b.left}));var i=f.getRoot();i.controlIdLookup||(i.controlIdLookup={}),i.controlIdLookup[f._id]=f;for(var k in f._aria)f.aria(k,f._aria[k]);f.state.get("visible")===!1&&(f.getEl().style.display="none"),f.bindStates(),f.state.on("change:visible",function(a){var b,c=a.value;f.state.get("rendered")&&(f.getEl().style.display=c===!1?"none":"",f.getEl().getBoundingClientRect()),b=f.parent(),b&&(b._lastRect=null),f.fire(c?"show":"hide"),j.add(f)}),f.fire("postrender",{},!1)},bindStates:function(){},scrollIntoView:function(a){function b(a,b){var c,d,e=a;for(c=d=0;e&&e!=b&&e.nodeType;)c+=e.offsetLeft||0,d+=e.offsetTop||0,e=e.offsetParent;return{x:c,y:d}}var c,d,e,f,g,h,i=this.getEl(),j=i.parentNode,k=b(i,j);return c=k.x,d=k.y,e=i.offsetWidth,f=i.offsetHeight,g=j.clientWidth,h=j.clientHeight,"end"==a?(c-=g-e,d-=h-f):"center"==a&&(c-=g/2-e/2,d-=h/2-f/2),j.scrollLeft=c,j.scrollTop=d,this},getRoot:function(){for(var a,b=this,c=[];b;){if(b.rootControl){a=b.rootControl;break}c.push(b),a=b,b=b.parent()}a||(a=this);for(var d=c.length;d--;)c[d].rootControl=a;return a},reflow:function(){j.remove(this);var a=this.parent();return a&&a._layout&&!a._layout.isNative()&&a.reflow(),this}};return b.each("text title visible disabled active value".split(" "),function(a){r[a]=function(b){return 0===arguments.length?this.state.get(a):("undefined"!=typeof b&&this.state.set(a,b),this)}}),m=a.extend(r)}),g("2h",[],function(){"use strict";var a={};return{add:function(b,c){a[b.toLowerCase()]=c},has:function(b){return!!a[b.toLowerCase()]},create:function(b,c){var d;if("string"==typeof b?(c=c||{},c.type=b):(c=b,b=c.type),b=b.toLowerCase(),d=a[b],!d)throw new Error("Could not find control by type: "+b);return d=new d(c),d.type=b,d}}}),g("2i",[],function(){"use strict";var a=function(a){return!!a.getAttribute("data-mce-tabstop")};return function(b){function c(a){return a&&1===a.nodeType}function d(a){return a=a||u,c(a)?a.getAttribute("role"):null}function e(a){for(var b,c=a||u;c=c.parentNode;)if(b=d(c))return b}function f(a){var b=u;if(c(b))return b.getAttribute("aria-"+a)}function g(a){var b=a.tagName.toUpperCase();return"INPUT"==b||"TEXTAREA"==b||"SELECT"==b}function h(b){return!(!g(b)||b.hidden)||(!!a(b)||!!/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell|slider)$/.test(d(b)))}function i(a){function b(a){if(1==a.nodeType&&"none"!=a.style.display&&!a.disabled){h(a)&&c.push(a);for(var d=0;d<a.childNodes.length;d++)b(a.childNodes[d])}}var c=[];return b(a||w.getEl()),c}function j(a){var b,c;a=a||v,c=a.parents().toArray(),c.unshift(a);for(var d=0;d<c.length&&(b=c[d],!b.settings.ariaRoot);d++);return b}function k(a){var b=j(a),c=i(b.getEl());b.settings.ariaRemember&&"lastAriaIndex"in b?l(b.lastAriaIndex,c):l(0,c)}function l(a,b){return a<0?a=b.length-1:a>=b.length&&(a=0),b[a]&&b[a].focus(),a}function m(a,b){var c=-1,d=j();b=b||i(d.getEl());for(var e=0;e<b.length;e++)b[e]===u&&(c=e);c+=a,d.lastAriaIndex=l(c,b)}function n(){var a=e();"tablist"==a?m(-1,i(u.parentNode)):v.parent().submenu?s():m(-1)}function o(){var a=d(),b=e();"tablist"==b?m(1,i(u.parentNode)):"menuitem"==a&&"menu"==b&&f("haspopup")?t():m(1)}function p(){m(-1)}function q(){var a=d(),b=e();"menuitem"==a&&"menubar"==b?t():"button"==a&&f("haspopup")?t({key:"down"}):m(1)}function r(a){var b=e();if("tablist"==b){var c=i(v.getEl("body"))[0];c&&c.focus()}else m(a.shiftKey?-1:1)}function s(){v.fire("cancel")}function t(a){a=a||{},v.fire("click",{target:u,aria:a})}var u,v,w=b.root;try{u=document.activeElement}catch(a){u=document.body}return v=w.getParentCtrl(u),w.on("keydown",function(b){function c(b,c){g(u)||a(u)||"slider"!==d(u)&&c(b)!==!1&&b.preventDefault()}if(!b.isDefaultPrevented())switch(b.keyCode){case 37:c(b,n);break;case 39:c(b,o);break;case 38:c(b,p);break;case 40:c(b,q);break;case 27:s();break;case 14:case 13:case 32:c(b,t);break;case 9:r(b)!==!1&&b.preventDefault()}}),w.on("focusin",function(a){u=a.target,v=a.control}),{focusFirst:k}}}),g("2j",["2g","2e","2d","2h","2i","9","a","4s","2f"],function(a,b,c,d,e,f,g,h,i){"use strict";var j={};return a.extend({init:function(a){var c=this;c._super(a),a=c.settings,a.fixed&&c.state.set("fixed",!0),c._items=new b,c.isRtl()&&c.classes.add("rtl"),c.bodyClasses=new h(function(){c.state.get("rendered")&&(c.getEl("body").className=this.toString())}),c.bodyClasses.prefix=c.classPrefix,c.classes.add("container"),c.bodyClasses.add("container-body"),a.containerCls&&c.classes.add(a.containerCls),c._layout=d.create((a.layout||"")+"layout"),c.settings.items?c.add(c.settings.items):c.add(c.render()),c._hasBody=!0},items:function(){return this._items},find:function(a){return a=j[a]=j[a]||new c(a),a.find(this)},add:function(a){var b=this;return b.items().add(b.create(a)).parent(b),b},focus:function(a){var b,c,d,e=this;return a&&(c=e.keyboardNav||e.parents().eq(-1)[0].keyboardNav)?void c.focusFirst(e):(d=e.find("*"),e.statusbar&&d.add(e.statusbar.items()),d.each(function(a){return a.settings.autofocus?(b=null,!1):void(a.canFocus&&(b=b||a))}),b&&b.focus(),e)},replace:function(a,b){for(var c,d=this.items(),e=d.length;e--;)if(d[e]===a){d[e]=b;break}e>=0&&(c=b.getEl(),c&&c.parentNode.removeChild(c),c=a.getEl(),c&&c.parentNode.removeChild(c)),b.parent(this)},create:function(b){var c,e=this,g=[];return f.isArray(b)||(b=[b]),f.each(b,function(b){b&&(b instanceof a||("string"==typeof b&&(b={type:b}),c=f.extend({},e.settings.defaults,b),b.type=c.type=c.type||b.type||e.settings.defaultType||(c.defaults?c.defaults.type:null),b=d.create(c)),g.push(b))}),g},renderNew:function(){var a=this;return a.items().each(function(b,c){var d;b.parent(a),b.state.get("rendered")||(d=a.getEl("body"),d.hasChildNodes()&&c<=d.childNodes.length-1?g(d.childNodes[c]).before(b.renderHtml()):g(d).append(b.renderHtml()),b.postRender(),i.add(b))}),a._layout.applyClasses(a.items().filter(":visible")),a._lastRect=null,a},append:function(a){return this.add(a).renderNew()},prepend:function(a){var b=this;return b.items().set(b.create(a).concat(b.items().toArray())),b.renderNew()},insert:function(a,b,c){var d,e,f,g=this;return a=g.create(a),d=g.items(),!c&&b<d.length-1&&(b+=1),b>=0&&b<d.length&&(e=d.slice(0,b).toArray(),f=d.slice(b).toArray(),d.set(e.concat(a,f))),g.renderNew()},fromJSON:function(a){var b=this;for(var c in a)b.find("#"+c).value(a[c]);return b},toJSON:function(){var a=this,b={};return a.find("*").each(function(a){var c=a.name(),d=a.value();c&&"undefined"!=typeof d&&(b[c]=d)}),b},renderHtml:function(){var a=this,b=a._layout,c=this.settings.role;return a.preRender(),b.preRender(a),'<div id="'+a._id+'" class="'+a.classes+'"'+(c?' role="'+this.settings.role+'"':"")+'><div id="'+a._id+'-body" class="'+a.bodyClasses+'">'+(a.settings.html||"")+b.renderHtml(a)+"</div></div>"},postRender:function(){var a,b=this;return b.items().exec("postRender"),b._super(),b._layout.postRender(b),b.state.set("rendered",!0),b.settings.style&&b.$el.css(b.settings.style),b.settings.border&&(a=b.borderBox,b.$el.css({"border-top-width":a.top,"border-right-width":a.right,"border-bottom-width":a.bottom,"border-left-width":a.left})),b.parent()||(b.keyboardNav=new e({root:b})),b},initLayoutRect:function(){var a=this,b=a._super();return a._layout.recalc(a),b},recalc:function(){var a=this,b=a._layoutRect,c=a._lastRect;if(!c||c.w!=b.w||c.h!=b.h)return a._layout.recalc(a),b=a.layoutRect(),a._lastRect={x:b.x,y:b.y,w:b.w,h:b.h},!0},reflow:function(){var b;if(i.remove(this),this.visible()){for(a.repaintControls=[],a.repaintControls.map={},this.recalc(),b=a.repaintControls.length;b--;)a.repaintControls[b].repaint();"flow"!==this.settings.layout&&"stack"!==this.settings.layout&&this.repaint(),a.repaintControls=[]}return this}})}),g("2k",["a"],function(a){"use strict";function b(a){var b,c,d,e,f,g,h,i,j=Math.max;return b=a.documentElement,c=a.body,d=j(b.scrollWidth,c.scrollWidth),e=j(b.clientWidth,c.clientWidth),f=j(b.offsetWidth,c.offsetWidth),g=j(b.scrollHeight,c.scrollHeight),h=j(b.clientHeight,c.clientHeight),i=j(b.offsetHeight,c.offsetHeight),{width:d<f?e:d,height:g<i?h:g}}function c(a){var b,c;if(a.changedTouches)for(b="screenX screenY pageX pageY clientX clientY".split(" "),c=0;c<b.length;c++)a[b[c]]=a.changedTouches[0][b[c]]}return function(d,e){function f(){return n.getElementById(e.handle||d)}var g,h,i,j,k,l,m,n=e.document||document;e=e||{},i=function(d){var i,o,p=b(n);c(d),d.preventDefault(),h=d.button,i=f(),l=d.screenX,m=d.screenY,o=window.getComputedStyle?window.getComputedStyle(i,null).getPropertyValue("cursor"):i.runtimeStyle.cursor,g=a("<div></div>").css({position:"absolute",top:0,left:0,width:p.width,height:p.height,zIndex:2147483647,opacity:1e-4,cursor:o}).appendTo(n.body),a(n).on("mousemove touchmove",k).on("mouseup touchend",j),e.start(d)},k=function(a){return c(a),a.button!==h?j(a):(a.deltaX=a.screenX-l,a.deltaY=a.screenY-m,a.preventDefault(),void e.drag(a))},j=function(b){c(b),a(n).off("mousemove touchmove",k).off("mouseup touchend",j),g.remove(),e.stop&&e.stop(b)},this.destroy=function(){a(f()).off()},a(f()).on("mousedown touchstart",i)}}),g("2l",["a","2k"],function(a,b){"use strict";return{init:function(){var a=this;a.on("repaint",a.renderScroll)},renderScroll:function(){function c(){function b(b,g,h,i,j,k){var l,m,n,o,p,q,r,s,t;if(m=e.getEl("scroll"+b)){if(s=g.toLowerCase(),t=h.toLowerCase(),a(e.getEl("absend")).css(s,e.layoutRect()[i]-1),!j)return void a(m).css("display","none");a(m).css("display","block"),l=e.getEl("body"),n=e.getEl("scroll"+b+"t"),o=l["client"+h]-2*f,o-=c&&d?m["client"+k]:0,p=l["scroll"+h],q=o/p,r={},r[s]=l["offset"+g]+f,r[t]=o,a(m).css(r),r={},r[s]=l["scroll"+g]*q,r[t]=o*q,a(n).css(r)}}var c,d,g;g=e.getEl("body"),c=g.scrollWidth>g.clientWidth,d=g.scrollHeight>g.clientHeight,b("h","Left","Width","contentW",c,"Height"),b("v","Top","Height","contentH",d,"Width")}function d(){function c(c,d,g,h,i){var j,k=e._id+"-scroll"+c,l=e.classPrefix;a(e.getEl()).append('<div id="'+k+'" class="'+l+"scrollbar "+l+"scrollbar-"+c+'"><div id="'+k+'t" class="'+l+'scrollbar-thumb"></div></div>'),e.draghelper=new b(k+"t",{start:function(){j=e.getEl("body")["scroll"+d],a("#"+k).addClass(l+"active")},drag:function(a){var b,k,l,m,n=e.layoutRect();k=n.contentW>n.innerW,l=n.contentH>n.innerH,m=e.getEl("body")["client"+g]-2*f,m-=k&&l?e.getEl("scroll"+c)["client"+i]:0,b=m/e.getEl("body")["scroll"+g],e.getEl("body")["scroll"+d]=j+a["delta"+h]/b},stop:function(){a("#"+k).removeClass(l+"active")}})}e.classes.add("scroll"),c("v","Top","Height","Y","Width"),c("h","Left","Width","X","Height")}var e=this,f=2; -e.settings.autoScroll&&(e._hasScroll||(e._hasScroll=!0,d(),e.on("wheel",function(a){var b=e.getEl("body");b.scrollLeft+=10*(a.deltaX||0),b.scrollTop+=10*a.deltaY,c()}),a(e.getEl("body")).on("scroll",c)),c())}}}),g("2m",["2j","2l"],function(a,b){"use strict";return a.extend({Defaults:{layout:"fit",containerCls:"panel"},Mixins:[b],renderHtml:function(){var a=this,b=a._layout,c=a.settings.html;return a.preRender(),b.preRender(a),"undefined"==typeof c?c='<div id="'+a._id+'-body" class="'+a.bodyClasses+'">'+b.renderHtml(a)+"</div>":("function"==typeof c&&(c=c.call(a)),a._hasBody=!1),'<div id="'+a._id+'" class="'+a.classes+'" hidefocus="1" tabindex="-1" role="group">'+(a._preBodyHtml||"")+c+"</div>"}})}),g("2n",["4q"],function(a){"use strict";function b(b,c,d){var e,f,g,h,i,j,k,l,m,n;return m=a.getViewPort(),f=a.getPos(c),g=f.x,h=f.y,b.state.get("fixed")&&"static"==a.getRuntimeStyle(document.body,"position")&&(g-=m.x,h-=m.y),e=b.getEl(),n=a.getSize(e),i=n.width,j=n.height,n=a.getSize(c),k=n.width,l=n.height,d=(d||"").split(""),"b"===d[0]&&(h+=l),"r"===d[1]&&(g+=k),"c"===d[0]&&(h+=Math.round(l/2)),"c"===d[1]&&(g+=Math.round(k/2)),"b"===d[3]&&(h-=j),"r"===d[4]&&(g-=i),"c"===d[3]&&(h-=Math.round(j/2)),"c"===d[4]&&(g-=Math.round(i/2)),{x:g,y:h,w:i,h:j}}return{testMoveRel:function(c,d){for(var e=a.getViewPort(),f=0;f<d.length;f++){var g=b(this,c,d[f]);if(this.state.get("fixed")){if(g.x>0&&g.x+g.w<e.w&&g.y>0&&g.y+g.h<e.h)return d[f]}else if(g.x>e.x&&g.x+g.w<e.w+e.x&&g.y>e.y&&g.y+g.h<e.h+e.y)return d[f]}return d[0]},moveRel:function(a,c){"string"!=typeof c&&(c=this.testMoveRel(a,c));var d=b(this,a,c);return this.moveTo(d.x,d.y)},moveBy:function(a,b){var c=this,d=c.layoutRect();return c.moveTo(d.x+a,d.y+b),c},moveTo:function(b,c){function d(a,b,c){return a<0?0:a+c>b?(a=b-c,a<0?0:a):a}var e=this;if(e.settings.constrainToViewport){var f=a.getViewPort(window),g=e.layoutRect();b=d(b,f.w+f.x,g.w),c=d(c,f.h+f.y,g.h)}return e.state.get("rendered")?e.layoutRect({x:b,y:c}).repaint():(e.settings.x=b,e.settings.y=c),e.fire("move",{x:b,y:c}),e}}}),g("2o",["4q"],function(a){"use strict";return{resizeToContent:function(){this._layoutRect.autoResize=!0,this._lastRect=null,this.reflow()},resizeTo:function(b,c){if(b<=1||c<=1){var d=a.getWindowSize();b=b<=1?b*d.w:b,c=c<=1?c*d.h:c}return this._layoutRect.autoResize=!1,this.layoutRect({minW:b,minH:c,w:b,h:c}).reflow()},resizeBy:function(a,b){var c=this,d=c.layoutRect();return c.resizeTo(d.w+a,d.h+b)}}}),g("2p",["2m","2n","2o","4q","a","5"],function(a,b,c,d,e,f){"use strict";function g(a,b){for(;a;){if(a==b)return!0;a=a.parent()}}function h(a){for(var b=s.length;b--;){var c=s[b],d=c.getParentCtrl(a.target);if(c.settings.autohide){if(d&&(g(d,c)||c.parent()===d))continue;a=c.fire("autohide",{target:a.target}),a.isDefaultPrevented()||c.hide()}}}function i(){o||(o=function(a){2!=a.button&&h(a)},e(document).on("click touchstart",o))}function j(){p||(p=function(){var a;for(a=s.length;a--;)l(s[a])},e(window).on("scroll",p))}function k(){if(!q){var a=document.documentElement,b=a.clientWidth,c=a.clientHeight;q=function(){document.all&&b==a.clientWidth&&c==a.clientHeight||(b=a.clientWidth,c=a.clientHeight,u.hideAll())},e(window).on("resize",q)}}function l(a){function b(b,c){for(var d,e=0;e<s.length;e++)if(s[e]!=a)for(d=s[e].parent();d&&(d=d.parent());)d==a&&s[e].fixed(b).moveBy(0,c).repaint()}var c=d.getViewPort().y;a.settings.autofix&&(a.state.get("fixed")?a._autoFixY>c&&(a.fixed(!1).layoutRect({y:a._autoFixY}).repaint(),b(!1,a._autoFixY-c)):(a._autoFixY=a.layoutRect().y,a._autoFixY<c&&(a.fixed(!0).layoutRect({y:0}).repaint(),b(!0,c-a._autoFixY))))}function m(a,b){var c,d,f=u.zIndex||65535;if(a)t.push(b);else for(c=t.length;c--;)t[c]===b&&t.splice(c,1);if(t.length)for(c=0;c<t.length;c++)t[c].modal&&(f++,d=t[c]),t[c].getEl().style.zIndex=f,t[c].zIndex=f,f++;var g=e("#"+b.classPrefix+"modal-block",b.getContainerElm())[0];d?e(g).css("z-index",d.zIndex-1):g&&(g.parentNode.removeChild(g),r=!1),u.currentZIndex=f}function n(a){var b;for(b=s.length;b--;)s[b]===a&&s.splice(b,1);for(b=t.length;b--;)t[b]===a&&t.splice(b,1)}var o,p,q,r,s=[],t=[],u=a.extend({Mixins:[b,c],init:function(a){var b=this;b._super(a),b._eventsRoot=b,b.classes.add("floatpanel"),a.autohide&&(i(),k(),s.push(b)),a.autofix&&(j(),b.on("move",function(){l(this)})),b.on("postrender show",function(a){if(a.control==b){var c,d=b.classPrefix;b.modal&&!r&&(c=e("#"+d+"modal-block",b.getContainerElm()),c[0]||(c=e('<div id="'+d+'modal-block" class="'+d+"reset "+d+'fade"></div>').appendTo(b.getContainerElm())),f.setTimeout(function(){c.addClass(d+"in"),e(b.getEl()).addClass(d+"in")}),r=!0),m(!0,b)}}),b.on("show",function(){b.parents().each(function(a){if(a.state.get("fixed"))return b.fixed(!0),!1})}),a.popover&&(b._preBodyHtml='<div class="'+b.classPrefix+'arrow"></div>',b.classes.add("popover").add("bottom").add(b.isRtl()?"end":"start")),b.aria("label",a.ariaLabel),b.aria("labelledby",b._id),b.aria("describedby",b.describedBy||b._id+"-none")},fixed:function(a){var b=this;if(b.state.get("fixed")!=a){if(b.state.get("rendered")){var c=d.getViewPort();a?b.layoutRect().y-=c.y:b.layoutRect().y+=c.y}b.classes.toggle("fixed",a),b.state.set("fixed",a)}return b},show:function(){var a,b=this,c=b._super();for(a=s.length;a--&&s[a]!==b;);return a===-1&&s.push(b),c},hide:function(){return n(this),m(!1,this),this._super()},hideAll:function(){u.hideAll()},close:function(){var a=this;return a.fire("close").isDefaultPrevented()||(a.remove(),m(!1,a)),a},remove:function(){n(this),this._super()},postRender:function(){var a=this;return a.settings.bodyRole&&this.getEl("body").setAttribute("role",a.settings.bodyRole),a._super()}});return u.hideAll=function(){for(var a=s.length;a--;){var b=s[a];b&&b.settings.autohide&&(b.hide(),s.splice(a,1))}},u}),g("23",["2p","2m","4q","a","2k","4r","6","5"],function(a,b,c,d,e,f,g,h){"use strict";function i(a){var b,c="width=device-width,initial-scale=1.0,user-scalable=0,minimum-scale=1.0,maximum-scale=1.0",e=d("meta[name=viewport]")[0];g.overrideViewPort!==!1&&(e||(e=document.createElement("meta"),e.setAttribute("name","viewport"),document.getElementsByTagName("head")[0].appendChild(e)),b=e.getAttribute("content"),b&&"undefined"!=typeof n&&(n=b),e.setAttribute("content",a?c:n))}function j(a,b){k()&&b===!1&&d([document.documentElement,document.body]).removeClass(a+"fullscreen")}function k(){for(var a=0;a<m.length;a++)if(m[a]._fullscreen)return!0;return!1}function l(){function a(){var a,b,d=c.getWindowSize();for(a=0;a<m.length;a++)b=m[a].layoutRect(),m[a].moveTo(m[a].settings.x||Math.max(0,d.w/2-b.w/2),m[a].settings.y||Math.max(0,d.h/2-b.h/2))}if(!g.desktop){var b={w:window.innerWidth,h:window.innerHeight};h.setInterval(function(){var a=window.innerWidth,c=window.innerHeight;b.w==a&&b.h==c||(b={w:a,h:c},d(window).trigger("resize"))},100)}d(window).on("resize",a)}var m=[],n="",o=a.extend({modal:!0,Defaults:{border:1,layout:"flex",containerCls:"panel",role:"dialog",callbacks:{submit:function(){this.fire("submit",{data:this.toJSON()})},close:function(){this.close()}}},init:function(a){var d=this;d._super(a),d.isRtl()&&d.classes.add("rtl"),d.classes.add("window"),d.bodyClasses.add("window-body"),d.state.set("fixed",!0),a.buttons&&(d.statusbar=new b({layout:"flex",border:"1 0 0 0",spacing:3,padding:10,align:"center",pack:d.isRtl()?"start":"end",defaults:{type:"button"},items:a.buttons}),d.statusbar.classes.add("foot"),d.statusbar.parent(d)),d.on("click",function(a){var b=d.classPrefix+"close";(c.hasClass(a.target,b)||c.hasClass(a.target.parentNode,b))&&d.close()}),d.on("cancel",function(){d.close()}),d.aria("describedby",d.describedBy||d._id+"-none"),d.aria("label",a.title),d._fullscreen=!1},recalc:function(){var a,b,d,e,f=this,g=f.statusbar;f._fullscreen&&(f.layoutRect(c.getWindowSize()),f.layoutRect().contentH=f.layoutRect().innerH),f._super(),a=f.layoutRect(),f.settings.title&&!f._fullscreen&&(b=a.headerW,b>a.w&&(d=a.x-Math.max(0,b/2),f.layoutRect({w:b,x:d}),e=!0)),g&&(g.layoutRect({w:f.layoutRect().innerW}).recalc(),b=g.layoutRect().minW+a.deltaW,b>a.w&&(d=a.x-Math.max(0,b-a.w),f.layoutRect({w:b,x:d}),e=!0)),e&&f.recalc()},initLayoutRect:function(){var a,b=this,d=b._super(),e=0;if(b.settings.title&&!b._fullscreen){a=b.getEl("head");var f=c.getSize(a);d.headerW=f.width,d.headerH=f.height,e+=d.headerH}b.statusbar&&(e+=b.statusbar.layoutRect().h),d.deltaH+=e,d.minH+=e,d.h+=e;var g=c.getWindowSize();return d.x=b.settings.x||Math.max(0,g.w/2-d.w/2),d.y=b.settings.y||Math.max(0,g.h/2-d.h/2),d},renderHtml:function(){var a=this,b=a._layout,c=a._id,d=a.classPrefix,e=a.settings,f="",g="",h=e.html;return a.preRender(),b.preRender(a),e.title&&(f='<div id="'+c+'-head" class="'+d+'window-head"><div id="'+c+'-title" class="'+d+'title">'+a.encode(e.title)+'</div><div id="'+c+'-dragh" class="'+d+'dragh"></div><button type="button" class="'+d+'close" aria-hidden="true"><i class="mce-ico mce-i-remove"></i></button></div>'),e.url&&(h='<iframe src="'+e.url+'" tabindex="-1"></iframe>'),"undefined"==typeof h&&(h=b.renderHtml(a)),a.statusbar&&(g=a.statusbar.renderHtml()),'<div id="'+c+'" class="'+a.classes+'" hidefocus="1"><div class="'+a.classPrefix+'reset" role="application">'+f+'<div id="'+c+'-body" class="'+a.bodyClasses+'">'+h+"</div>"+g+"</div></div>"},fullscreen:function(a){var b,e,g=this,i=document.documentElement,j=g.classPrefix;if(a!=g._fullscreen)if(d(window).on("resize",function(){var a;if(g._fullscreen)if(b)g._timer||(g._timer=h.setTimeout(function(){var a=c.getWindowSize();g.moveTo(0,0).resizeTo(a.w,a.h),g._timer=0},50));else{a=(new Date).getTime();var d=c.getWindowSize();g.moveTo(0,0).resizeTo(d.w,d.h),(new Date).getTime()-a>50&&(b=!0)}}),e=g.layoutRect(),g._fullscreen=a,a){g._initial={x:e.x,y:e.y,w:e.w,h:e.h},g.borderBox=f.parseBox("0"),g.getEl("head").style.display="none",e.deltaH-=e.headerH+2,d([i,document.body]).addClass(j+"fullscreen"),g.classes.add("fullscreen");var k=c.getWindowSize();g.moveTo(0,0).resizeTo(k.w,k.h)}else g.borderBox=f.parseBox(g.settings.border),g.getEl("head").style.display="",e.deltaH+=e.headerH,d([i,document.body]).removeClass(j+"fullscreen"),g.classes.remove("fullscreen"),g.moveTo(g._initial.x,g._initial.y).resizeTo(g._initial.w,g._initial.h);return g.reflow()},postRender:function(){var a,b=this;setTimeout(function(){b.classes.add("in"),b.fire("open")},0),b._super(),b.statusbar&&b.statusbar.postRender(),b.focus(),this.dragHelper=new e(b._id+"-dragh",{start:function(){a={x:b.layoutRect().x,y:b.layoutRect().y}},drag:function(c){b.moveTo(a.x+c.deltaX,a.y+c.deltaY)}}),b.on("submit",function(a){a.isDefaultPrevented()||b.close()}),m.push(b),i(!0)},submit:function(){return this.fire("submit",{data:this.toJSON()})},remove:function(){var a,b=this;for(b.dragHelper.destroy(),b._super(),b.statusbar&&this.statusbar.remove(),j(b.classPrefix,!1),a=m.length;a--;)m[a]===b&&m.splice(a,1);i(m.length>0)},getContentWindow:function(){var a=this.getEl().getElementsByTagName("iframe")[0];return a?a.contentWindow:null}});return l(),o}),g("24",["23"],function(a){"use strict";var b=a.extend({init:function(a){a={border:1,padding:20,layout:"flex",pack:"center",align:"center",containerCls:"panel",autoScroll:!0,buttons:{type:"button",text:"Ok",action:"ok"},items:{type:"label",multiline:!0,maxWidth:500,maxHeight:200}},this._super(a)},Statics:{OK:1,OK_CANCEL:2,YES_NO:3,YES_NO_CANCEL:4,msgBox:function(c){function d(a,b,c){return{type:"button",text:a,subtype:c?"primary":"",onClick:function(a){a.control.parents()[1].close(),f(b)}}}var e,f=c.callback||function(){};switch(c.buttons){case b.OK_CANCEL:e=[d("Ok",!0,!0),d("Cancel",!1)];break;case b.YES_NO:case b.YES_NO_CANCEL:e=[d("Yes",1,!0),d("No",0)],c.buttons==b.YES_NO_CANCEL&&e.push(d("Cancel",-1));break;default:e=[d("Ok",!0,!0)]}return new a({padding:20,x:c.x,y:c.y,minWidth:300,minHeight:100,layout:"flex",pack:"center",align:"center",buttons:e,title:c.title,role:"alertdialog",items:{type:"label",multiline:!0,maxWidth:500,maxHeight:200,text:c.text},onPostRender:function(){this.aria("describedby",this.items()[0]._id)},onClose:c.onClose,onCancel:function(){f(!1)}}).renderTo(document.body).reflow()},alert:function(a,c){return"string"==typeof a&&(a={text:a}),a.callback=c,b.msgBox(a)},confirm:function(a,c){return"string"==typeof a&&(a={text:a}),a.callback=c,a.buttons=b.OK_CANCEL,b.msgBox(a)}}});return b}),g("10",["23","24"],function(a,b){return function(c){function d(){if(h.length)return h[h.length-1]}function e(a){c.fire("OpenWindow",{win:a})}function f(a){c.fire("CloseWindow",{win:a})}var g=this,h=[];g.windows=h,c.on("remove",function(){for(var a=h.length;a--;)h[a].close()}),g.open=function(b,d){var g;return c.editorManager.setActive(c),b.title=b.title||" ",b.url=b.url||b.file,b.url&&(b.width=parseInt(b.width||320,10),b.height=parseInt(b.height||240,10)),b.body&&(b.items={defaults:b.defaults,type:b.bodyType||"form",items:b.body,data:b.data,callbacks:b.commands}),b.url||b.buttons||(b.buttons=[{text:"Ok",subtype:"primary",onclick:function(){g.find("form")[0].submit()}},{text:"Cancel",onclick:function(){g.close()}}]),g=new a(b),h.push(g),g.on("close",function(){for(var a=h.length;a--;)h[a]===g&&h.splice(a,1);h.length||c.focus(),f(g)}),b.data&&g.on("postRender",function(){this.find("*").each(function(a){var c=a.name();c in b.data&&a.value(b.data[c])})}),g.features=b||{},g.params=d||{},1===h.length&&c.nodeChanged(),g=g.renderTo().reflow(),e(g),g},g.alert=function(a,d,g){var h;h=b.alert(a,function(){d?d.call(g||this):c.focus()}),h.on("close",function(){f(h)}),e(h)},g.confirm=function(a,c,d){var g;g=b.confirm(a,function(a){c.call(d||this,a)}),g.on("close",function(){f(g)}),e(g)},g.close=function(){d()&&d().close()},g.getParams=function(){return d()?d().params:null},g.setParams=function(a){d()&&(d().params=a)},g.getWindows=function(){return h}}}),g("2q",["2g","2n"],function(a,b){return a.extend({Mixins:[b],Defaults:{classes:"widget tooltip tooltip-n"},renderHtml:function(){var a=this,b=a.classPrefix;return'<div id="'+a._id+'" class="'+a.classes+'" role="presentation"><div class="'+b+'tooltip-arrow"></div><div class="'+b+'tooltip-inner">'+a.encode(a.state.get("text"))+"</div></div>"},bindStates:function(){var a=this;return a.state.on("change:text",function(b){a.getEl().lastChild.innerHTML=a.encode(b.value)}),a._super()},repaint:function(){var a,b,c=this;a=c.getEl().style,b=c._layoutRect,a.left=b.x+"px",a.top=b.y+"px",a.zIndex=131070}})}),g("2r",["2g","2q"],function(a,b){"use strict";var c,d=a.extend({init:function(a){var b=this;b._super(a),a=b.settings,b.canFocus=!0,a.tooltip&&d.tooltips!==!1&&(b.on("mouseenter",function(c){var d=b.tooltip().moveTo(-65535);if(c.control==b){var e=d.text(a.tooltip).show().testMoveRel(b.getEl(),["bc-tc","bc-tl","bc-tr"]);d.classes.toggle("tooltip-n","bc-tc"==e),d.classes.toggle("tooltip-nw","bc-tl"==e),d.classes.toggle("tooltip-ne","bc-tr"==e),d.moveRel(b.getEl(),e)}else d.hide()}),b.on("mouseleave mousedown click",function(){b.tooltip().hide()})),b.aria("label",a.ariaLabel||a.tooltip)},tooltip:function(){return c||(c=new b({type:"tooltip"}),c.renderTo()),c},postRender:function(){var a=this,b=a.settings;a._super(),a.parent()||!b.width&&!b.height||(a.initLayoutRect(),a.repaint()),b.autofocus&&a.focus()},bindStates:function(){function a(a){c.aria("disabled",a),c.classes.toggle("disabled",a)}function b(a){c.aria("pressed",a),c.classes.toggle("active",a)}var c=this;return c.state.on("change:disabled",function(b){a(b.value)}),c.state.on("change:active",function(a){b(a.value)}),c.state.get("disabled")&&a(!0),c.state.get("active")&&b(!0),c._super()},remove:function(){this._super(),c&&(c.remove(),c=null)}});return d}),g("2s",["2r"],function(a){"use strict";return a.extend({Defaults:{value:0},init:function(a){var b=this;b._super(a),b.classes.add("progress"),b.settings.filter||(b.settings.filter=function(a){return Math.round(a)})},renderHtml:function(){var a=this,b=a._id,c=this.classPrefix;return'<div id="'+b+'" class="'+a.classes+'"><div class="'+c+'bar-container"><div class="'+c+'bar"></div></div><div class="'+c+'text">0%</div></div>'},postRender:function(){var a=this;return a._super(),a.value(a.settings.value),a},bindStates:function(){function a(a){a=b.settings.filter(a),b.getEl().lastChild.innerHTML=a+"%",b.getEl().firstChild.firstChild.style.width=a+"%"}var b=this;return b.state.on("change:value",function(b){a(b.value)}),a(b.state.get("value")),b._super()}})}),g("25",["2g","2n","2s","5"],function(a,b,c,d){return a.extend({Mixins:[b],Defaults:{classes:"widget notification"},init:function(a){var b=this;b._super(a),a.text&&b.text(a.text),a.icon&&(b.icon=a.icon),a.color&&(b.color=a.color),a.type&&b.classes.add("notification-"+a.type),a.timeout&&(a.timeout<0||a.timeout>0)&&!a.closeButton?b.closeButton=!1:(b.classes.add("has-close"),b.closeButton=!0),a.progressBar&&(b.progressBar=new c),b.on("click",function(a){a.target.className.indexOf(b.classPrefix+"close")!=-1&&b.close()})},renderHtml:function(){var a=this,b=a.classPrefix,c="",d="",e="",f="";return a.icon&&(c='<i class="'+b+"ico "+b+"i-"+a.icon+'"></i>'),a.color&&(f=' style="background-color: '+a.color+'"'),a.closeButton&&(d='<button type="button" class="'+b+'close" aria-hidden="true">\xd7</button>'),a.progressBar&&(e=a.progressBar.renderHtml()),'<div id="'+a._id+'" class="'+a.classes+'"'+f+' role="presentation">'+c+'<div class="'+b+'notification-inner">'+a.state.get("text")+"</div>"+e+d+"</div>"},postRender:function(){var a=this;return d.setTimeout(function(){a.$el.addClass(a.classPrefix+"in")}),a._super()},bindStates:function(){var a=this;return a.state.on("change:text",function(b){a.getEl().childNodes[1].innerHTML=b.value}),a.progressBar&&a.progressBar.bindStates(),a._super()},close:function(){var a=this;return a.fire("close").isDefaultPrevented()||a.remove(),a},repaint:function(){var a,b,c=this;a=c.getEl().style,b=c._layoutRect,a.left=b.x+"px",a.top=b.y+"px",a.zIndex=65534}})}),g("11",["25","5","9"],function(a,b,c){return function(d){function e(){if(m.length)return m[m.length-1]}function f(){b.requestAnimationFrame(function(){g(),h()})}function g(){for(var a=0;a<m.length;a++)m[a].moveTo(0,0)}function h(){if(m.length>0){var a=m.slice(0,1)[0],b=d.inline?d.getElement():d.getContentAreaContainer();if(a.moveRel(b,"tc-tc"),m.length>1)for(var c=1;c<m.length;c++)m[c].moveRel(m[c-1].getEl(),"bc-tc")}}function i(a,b){if(!k(b))return null;var d=c.grep(a,function(a){return j(b,a)});return 0===d.length?null:d[0]}function j(a,b){return a.type===b.settings.type&&a.text===b.settings.text}function k(a){return!a.progressBar&&!a.timeout}var l=this,m=[];l.notifications=m,d.on("remove",function(){for(var a=m.length;a--;)m[a].close()}),d.on("ResizeEditor",h),d.on("ResizeWindow",f),l.open=function(b){if(!d.removed){var c;d.editorManager.setActive(d);var e=i(m,b);return null===e?(c=new a(b),m.push(c),b.timeout>0&&(c.timer=setTimeout(function(){c.close()},b.timeout)),c.on("close",function(){var a=m.length;for(c.timer&&d.getWin().clearTimeout(c.timer);a--;)m[a]===c&&m.splice(a,1);h()}),c.renderTo(),h()):c=e,c}},l.close=function(){e()&&e().close()},l.getNotifications=function(){return m},d.on("SkinLoaded",function(){var a=d.settings.service_message;a&&d.notificationManager.open({text:a,type:"warning",timeout:0,icon:""})})}}),g("12",["z","e","9"],function(a,b,c){function d(a,b){return"selectionchange"==b?a.getDoc():!a.inline&&/^mouse|touch|click|contextmenu|drop|dragover|dragend/.test(b)?a.getDoc().documentElement:a.settings.event_root?(a.eventRoot||(a.eventRoot=g.select(a.settings.event_root)[0]),a.eventRoot):a.getBody()}function e(a,b){function c(a){return!a.hidden&&!a.readonly}var e,h;if(a.delegates||(a.delegates={}),!a.delegates[b]&&!a.removed)if(e=d(a,b),a.settings.event_root){if(f||(f={},a.editorManager.on("removeEditor",function(){var b;if(!a.editorManager.activeEditor&&f){for(b in f)a.dom.unbind(d(a,b));f=null}})),f[b])return;h=function(d){for(var e=d.target,f=a.editorManager.editors,h=f.length;h--;){var i=f[h].getBody();(i===e||g.isChildOf(e,i))&&c(f[h])&&f[h].fire(b,d)}},f[b]=h,g.bind(e,b,h)}else h=function(d){c(a)&&a.fire(b,d)},g.bind(e,b,h),a.delegates[b]=h}var f,g=b.DOM,h={bindPendingEventDelegates:function(){var a=this;c.each(a._pendingNativeEvents,function(b){e(a,b)})},toggleNativeEvent:function(a,b){var c=this;"focus"!=a&&"blur"!=a&&(b?c.initialized?e(c,a):c._pendingNativeEvents?c._pendingNativeEvents.push(a):c._pendingNativeEvents=[a]:c.initialized&&(c.dom.unbind(d(c,a),a,c.delegates[a]),delete c.delegates[a]))},unbindAllNativeEvents:function(){var a,b=this;if(b.delegates){for(a in b.delegates)b.dom.unbind(d(b,a),a,b.delegates[a]);delete b.delegates}b.inline||(b.getBody().onload=null,b.dom.unbind(b.getWin()),b.dom.unbind(b.getDoc())),b.dom.unbind(b.getBody()),b.dom.unbind(b.getContainer())}};return h=c.extend({},a,h)}),g("13",["9","6"],function(a,b){var c=a.each,d=a.explode,e={f9:120,f10:121,f11:122},f=a.makeMap("alt,ctrl,shift,meta,access");return function(g){function h(a){var g,h,i={};c(d(a,"+"),function(a){a in f?i[a]=!0:/^[0-9]{2,}$/.test(a)?i.keyCode=parseInt(a,10):(i.charCode=a.charCodeAt(0),i.keyCode=e[a]||a.toUpperCase().charCodeAt(0))}),g=[i.keyCode];for(h in f)i[h]?g.push(h):i[h]=!1;return i.id=g.join(","),i.access&&(i.alt=!0,b.mac?i.ctrl=!0:i.shift=!0),i.meta&&(b.mac?i.meta=!0:(i.ctrl=!0,i.meta=!1)),i}function i(b,c,e,f){var i;return i=a.map(d(b,">"),h),i[i.length-1]=a.extend(i[i.length-1],{func:e,scope:f||g}),a.extend(i[0],{desc:g.translate(c),subpatterns:i.slice(1)})}function j(a){return a.altKey||a.ctrlKey||a.metaKey}function k(a){return"keydown"===a.type&&a.keyCode>=112&&a.keyCode<=123}function l(a,b){return!!b&&(b.ctrl==a.ctrlKey&&b.meta==a.metaKey&&(b.alt==a.altKey&&b.shift==a.shiftKey&&(!!(a.keyCode==b.keyCode||a.charCode&&a.charCode==b.charCode)&&(a.preventDefault(),!0))))}function m(a){return a.func?a.func.call(a.scope):null}var n=this,o={},p=[];g.on("keyup keypress keydown",function(a){!j(a)&&!k(a)||a.isDefaultPrevented()||(c(o,function(b){if(l(a,b))return p=b.subpatterns.slice(0),"keydown"==a.type&&m(b),!0}),l(a,p[0])&&(1===p.length&&"keydown"==a.type&&m(p[0]),p.shift()))}),n.add=function(b,e,f,h){var j;return j=f,"string"==typeof f?f=function(){g.execCommand(j,!1,null)}:a.isArray(j)&&(f=function(){g.execCommand(j[0],j[1],j[2])}),c(d(a.trim(b.toLowerCase())),function(a){var b=i(a,e,f,h);o[b.id]=b}),!0},n.remove=function(a){var b=i(a);return!!o[b.id]&&(delete o[b.id],!0)}}}),g("26",["9"],function(a){var b=function(b,c,d,e,f){return f=a.extend({id:c,theme:"modern",delta_width:0,delta_height:0,popup_css:"",plugins:"",document_base_url:d,add_form_submit_trigger:!0,submit_patch:!0,add_unload_trigger:!0,convert_urls:!0,relative_urls:!0,remove_script_host:!0,object_resizing:!0,doctype:"<!DOCTYPE html>",visual:!0,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",font_size_legacy_values:"xx-small,small,medium,large,x-large,xx-large,300%",forced_root_block:"p",hidden_input:!0,padd_empty_editor:!0,render_ui:!0,indentation:"30px",inline_styles:!0,convert_fonts_to_spans:!0,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist",entity_encoding:"named",url_converter:b.convertURL,url_converter_scope:b,ie7_compat:!0},e,f,{validate:!0,content_editable:f.inline}),e&&e.external_plugins&&f.external_plugins&&(f.external_plugins=a.extend({},e.external_plugins,f.external_plugins)),f};return{getEditorSettings:b}}),h("4t",window),g("2b",["g"],function(a){var b=a.PluginManager,c=function(a,c){for(var d in b.urls){var e=b.urls[d]+"/plugin"+c+".js";if(e===a)return d}return null},d=function(a,b){var d=c(b,a.suffix);return d?"Failed to load plugin: "+d+" from url "+b:"Failed to load plugin url: "+b},e=function(a,b){a.notificationManager.open({type:"error",text:b})},f=function(a,b){a._skinLoaded?e(a,b):a.on("SkinLoaded",function(){e(a,b)})},g=function(a,b){f(a,"Failed to upload image: "+b)},h=function(a,b){f(a,d(a,b))},i=function(a,b){f(a,"Failed to load content css: "+b[0])},j=function(a){var b=window.console;b&&!window.test&&(b.error?b.error.apply(b,arguments):b.log.apply(b,arguments))};return{pluginLoadError:h,uploadError:g,displayError:f,contentCssError:i,initError:j}}),g("6c",["1m","1k"],function(a,b){var c=function(a){return a.dom.select("*[data-mce-caret]")[0]},d=function(a){a.selection.setRng(a.selection.getRng())},e=function(a,c){c.hasAttribute("data-mce-caret")&&(b.showCaretContainerBlock(c),d(a),a.selection.scrollIntoView(c))},f=function(a,d){var f=c(a);if(f)return"compositionstart"===d.type?(d.preventDefault(),d.stopPropagation(),void e(f)):void(b.hasContent(f)&&e(a,f))},g=function(b){b.on("keyup compositionstart",a.curry(f,b))};return{setup:g}}),g("6r",["4","9","1w"],function(a,b,c){return function(c,d){function e(a,b){return a?a.replace(/\/$/,"")+"/"+b.replace(/^\//,""):b}function f(a,b,c,f){var g,h;g=new XMLHttpRequest,g.open("POST",d.url),g.withCredentials=d.credentials,g.upload.onprogress=function(a){f(a.loaded/a.total*100)},g.onerror=function(){c("Image upload failed due to a XHR Transport error. Code: "+g.status)},g.onload=function(){var a;return g.status<200||g.status>=300?void c("HTTP Error: "+g.status):(a=JSON.parse(g.responseText),a&&"string"==typeof a.location?void b(e(d.basePath,a.location)):void c("Invalid JSON: "+g.responseText))},h=new FormData,h.append("file",a.blob(),a.filename()),g.send(h)}function g(){return new a(function(a){a([])})}function h(a,b){return{url:b,blobInfo:a,status:!0}}function i(a,b){return{url:"",blobInfo:a,status:!1,error:b}}function j(a,c){b.each(p[a],function(a){a(c)}),delete p[a]}function k(b,d,e){return c.markPending(b.blobUri()),new a(function(a){var f,g,k=function(){};try{var l=function(){f&&(f.close(),g=k)},m=function(d){l(),c.markUploaded(b.blobUri(),d),j(b.blobUri(),h(b,d)),a(h(b,d))},n=function(d){l(),c.removeFailed(b.blobUri()),j(b.blobUri(),i(b,d)),a(i(b,d))};g=function(a){a<0||a>100||(f||(f=e()),f.progressBar.value(a))},d(b,m,n,g)}catch(c){a(i(b,c.message))}})}function l(a){return a===f}function m(b){var c=b.blobUri();return new a(function(a){p[c]=p[c]||[],p[c].push(a)})}function n(e,f){return e=b.grep(e,function(a){return!c.isUploaded(a.blobUri())}),a.all(b.map(e,function(a){return c.isPending(a.blobUri())?m(a):k(a,d.handler,f)}))}function o(a,b){return!d.url&&l(d.handler)?g():n(a,b)}var p={};return d=b.extend({credentials:!1,handler:f},d),{upload:o}}}),g("7a",["4"],function(a){function b(b){return new a(function(a,c){var d=function(){c("Cannot convert "+b+" to Blob. Resource might not exist or is inaccessible.")};try{var e=new XMLHttpRequest;e.open("GET",b,!0),e.responseType="blob",e.onload=function(){200==this.status?a(this.response):d()},e.onerror=d,e.send()}catch(a){d()}})}function c(a){var b,c;return a=decodeURIComponent(a).split(","),c=/data:([^;]+)/.exec(a[0]),c&&(b=c[1]),{type:b,data:a[1]}}function d(b){return new a(function(a){var d,e,f;b=c(b);try{d=atob(b.data)}catch(b){return void a(new Blob([]))}for(e=new Uint8Array(d.length),f=0;f<e.length;f++)e[f]=d.charCodeAt(f);a(new Blob([e],{type:b.type}))})}function e(a){return 0===a.indexOf("blob:")?b(a):0===a.indexOf("data:")?d(a):null}function f(b){return new a(function(a){var c=new FileReader;c.onloadend=function(){a(c.result)},c.readAsDataURL(b)})}return{uriToBlob:e,blobToDataUri:f,parseDataUri:c}}),g("6s",["4","1g","1w","7a","6"],function(a,b,c,d,e){var f=0,g=function(a){return(a||"blobid")+f++},h=function(a,b,c,e){var f,h;return 0===b.src.indexOf("blob:")?(h=a.getByUri(b.src),void(h?c({image:b,blobInfo:h}):d.uriToBlob(b.src).then(function(e){d.blobToDataUri(e).then(function(i){f=d.parseDataUri(i).data,h=a.create(g(),e,f),a.add(h),c({image:b,blobInfo:h})})},function(a){e(a)}))):(f=d.parseDataUri(b.src).data,h=a.findFirst(function(a){return a.base64()===f}),void(h?c({image:b,blobInfo:h}):d.uriToBlob(b.src).then(function(d){h=a.create(g(),d,f),a.add(h),c({image:b,blobInfo:h})},function(a){e(a)})))},i=function(a){return a?a.getElementsByTagName("img"):[]};return function(d,f){function g(g,k){var l,m;return k||(k=c.constant(!0)),l=b.filter(i(g),function(a){var b=a.src;return!!e.fileApi&&(!a.hasAttribute("data-mce-bogus")&&(!a.hasAttribute("data-mce-placeholder")&&(!(!b||b==e.transparentSrc)&&(0===b.indexOf("blob:")?!d.isUploaded(b):0===b.indexOf("data:")&&k(a)))))}),m=b.map(l,function(b){var c;return j[b.src]?new a(function(a){j[b.src].then(function(c){return"string"==typeof c?c:void a({image:b,blobInfo:c.blobInfo})})}):(c=new a(function(a,c){h(f,b,a,c)}).then(function(a){return delete j[a.image.src],a})["catch"](function(a){return delete j[b.src],a}),j[b.src]=c,c)}),a.all(m)}var j={};return{findAll:g}}}),g("2a",[],function(){var a=0,b=function(){var a=function(){return Math.round(4294967295*Math.random()).toString(36)},b=(new Date).getTime();return"s"+b.toString(36)+a()+a()+a()},c=function(c){return c+a++ +b()};return{uuid:c}}),h("7b",URL),g("6t",["1g","1w","2a","7b"],function(a,b,c,d){return function(){function e(a){var b={"image/jpeg":"jpg","image/jpg":"jpg","image/gif":"gif","image/png":"png"};return b[a.toLowerCase()]||"dat"}function f(a,b,c,d){return g("object"==typeof a?a:{id:a,name:d,blob:b,base64:c})}function g(a){var b,f;if(!a.blob||!a.base64)throw"blob and base64 representations of the image are required for BlobInfo to be created";return b=a.id||c.uuid("blobid"),f=a.name||b,{id:o(b),name:o(f),filename:o(f+"."+e(a.blob.type)),blob:o(a.blob),base64:o(a.base64),blobUri:o(a.blobUri||d.createObjectURL(a.blob)),uri:o(a.uri)}}function h(a){i(a.id())||n.push(a)}function i(a){return j(function(b){return b.id()===a})}function j(b){return a.filter(n,b)[0]}function k(a){return j(function(b){return b.blobUri()==a})}function l(b){n=a.filter(n,function(a){return a.blobUri()!==b||(d.revokeObjectURL(a.blobUri()),!1)})}function m(){a.each(n,function(a){d.revokeObjectURL(a.blobUri())}),n=[]}var n=[],o=b.constant;return{create:f,add:h,get:i,getByUri:k,findFirst:j,removeByUri:l,destroy:m}}}),g("6u",[],function(){return function(){function a(a,b){return{status:a,resultUri:b}}function b(a){return a in l}function c(a){var b=l[a];return b?b.resultUri:null}function d(a){return!!b(a)&&l[a].status===j}function e(a){return!!b(a)&&l[a].status===k}function f(b){l[b]=a(j,null)}function g(b,c){l[b]=a(k,c)}function h(a){delete l[a]}function i(){l={}}var j=1,k=2,l={};return{hasBlobUri:b,getResultUri:c,isPending:d,isUploaded:e,markPending:f,markUploaded:g,removeFailed:h,destroy:i}}}),g("6d",["1g","6r","6s","6t","6u","2b"],function(a,b,c,d,e,f){return function(g){function h(a){return function(b){return g.selection?a(b):[]}}function i(){return"?"+(new Date).getTime()}function j(a,b,c){var d=0;do d=a.indexOf(b,d),d!==-1&&(a=a.substring(0,d)+c+a.substr(d+b.length),d+=c.length-b.length+1);while(d!==-1);return a}function k(a,b,c){return a=j(a,'src="'+b+'"','src="'+c+'"'),a=j(a,'data-mce-src="'+b+'"','data-mce-src="'+c+'"')}function l(b,c){a.each(g.undoManager.data,function(d){"fragmented"===d.type?d.fragments=a.map(d.fragments,function(a){return k(a,b,c)}):d.content=k(d.content,b,c)})}function m(){return g.notificationManager.open({text:g.translate("Image uploading..."),type:"info",timeout:-1,progressBar:!0})}function n(a,b){w.removeByUri(a.src),l(a.src,b),g.$(a).attr({src:x.images_reuse_filename?b+i():b,"data-mce-src":g.convertURL(b,"src")})}function o(c){return u||(u=new b(y,{url:x.images_upload_url,basePath:x.images_upload_base_path,credentials:x.images_upload_credentials,handler:x.images_upload_handler})),r().then(h(function(b){var d;return d=a.map(b,function(a){return a.blobInfo}),u.upload(d,m).then(h(function(d){var e=a.map(d,function(a,c){var d=b[c].image;return a.status&&g.settings.images_replace_blob_uris!==!1?n(d,a.url):a.error&&f.uploadError(g,a.error),{element:d, -status:a.status}});return c&&c(e),e}))}))}function p(a){if(x.automatic_uploads!==!1)return o(a)}function q(a){return!x.images_dataimg_filter||x.images_dataimg_filter(a)}function r(){return v||(v=new c(y,w)),v.findAll(g.getBody(),q).then(h(function(b){return b=a.filter(b,function(a){return"string"!=typeof a||(f.displayError(g,a),!1)}),a.each(b,function(a){l(a.image.src,a.blobInfo.blobUri()),a.image.src=a.blobInfo.blobUri(),a.image.removeAttribute("data-mce-src")}),b}))}function s(){w.destroy(),y.destroy(),v=u=null}function t(b){return b.replace(/src="(blob:[^"]+)"/g,function(b,c){var d=y.getResultUri(c);if(d)return'src="'+d+'"';var e=w.getByUri(c);return e||(e=a.reduce(g.editorManager.editors,function(a,b){return a||b.editorUpload&&b.editorUpload.blobCache.getByUri(c)},null)),e?'src="data:'+e.blob().type+";base64,"+e.base64()+'"':b})}var u,v,w=new d,x=g.settings,y=new e;return g.on("setContent",function(){g.settings.automatic_uploads!==!1?p():r()}),g.on("RawSaveContent",function(a){a.content=t(a.content)}),g.on("getContent",function(a){a.source_view||"raw"==a.format||(a.content=t(a.content))}),g.on("PostRender",function(){g.parser.addNodeFilter("img",function(b){a.each(b,function(a){var b=a.attr("src");if(!w.getByUri(b)){var c=y.getResultUri(b);c&&a.attr("src",c)}})})}),{blobCache:w,uploadImages:o,uploadImagesAuto:p,scanForImages:r,destroy:s}}}),g("6e",["1m"],function(a){var b=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n=a.settings,o=a.dom,p=a.selection,q=a.schema,r=q.getBlockElements(),s=p.getStart(),t=a.getBody(),u=-16777215;if(m=n.forced_root_block,s&&1===s.nodeType&&m){for(;s&&s!==t;){if(r[s.nodeName])return;s=s.parentNode}if(b=p.getRng(),b.setStart){c=b.startContainer,d=b.startOffset,e=b.endContainer,f=b.endOffset;try{j=a.getDoc().activeElement===t}catch(a){}}else b.item&&(s=b.item(0),b=a.getDoc().body.createTextRange(),b.moveToElementText(s)),j=b.parentElement().ownerDocument===a.getDoc(),k=b.duplicate(),k.collapse(!0),d=k.move("character",u)*-1,k.collapsed||(k=b.duplicate(),k.collapse(!1),f=k.move("character",u)*-1-d);for(s=t.firstChild,l=t.nodeName.toLowerCase();s;)if((3===s.nodeType||1==s.nodeType&&!r[s.nodeName])&&q.isValidChild(l,m.toLowerCase())){if(3===s.nodeType&&0===s.nodeValue.length){h=s,s=s.nextSibling,o.remove(h);continue}g||(g=o.create(m,a.settings.forced_root_block_attrs),s.parentNode.insertBefore(g,s),i=!0),h=s,s=s.nextSibling,g.appendChild(h)}else g=null,s=s.nextSibling;if(i&&j){if(b.setStart)b.setStart(c,d),b.setEnd(e,f),p.setRng(b);else try{b=a.getDoc().body.createTextRange(),b.moveToElementText(t),b.collapse(!0),b.moveStart("character",d),f>0&&b.moveEnd("character",f),b.select()}catch(a){}a.nodeChanged()}}},c=function(c){c.settings.forced_root_block&&c.on("NodeChange",a.curry(b,c))};return{setup:c}}),g("7f",["1g","1j","43"],function(a,b,c){function d(e){function f(b){return a.map(b,function(a){return a=c.clone(a),a.node=e,a})}if(a.isArray(e))return a.reduce(e,function(a,b){return a.concat(d(b))},[]);if(b.isElement(e))return f(e.getClientRects());if(b.isText(e)){var g=e.ownerDocument.createRange();return g.setStart(e,0),g.setEnd(e,e.data.length),f(g.getClientRects())}}return{getClientRects:d}}),g("70",["1w","1g","1j","7f","43","5i","44"],function(a,b,c,d,e,f,g){function h(a,b){return Math.abs(a.left-b)}function i(a,b){return Math.abs(a.right-b)}function j(a,c){function d(a,b){return a>=b.left&&a<=b.right}return b.reduce(a,function(a,b){var e,f;return e=Math.min(h(a,c),i(a,c)),f=Math.min(h(b,c),i(b,c)),d(c,b)?b:d(c,a)?a:f==e&&p(b.node)?b:f<e?b:a})}function k(a,b,c,d){for(;d=q(d,a,g.isEditableCaretCandidate,b);)if(c(d))return}function l(a,c){function f(a,e){var f;return f=b.filter(d.getClientRects(e),function(b){return!a(b,c)}),g=g.concat(f),0===f.length}var g=[];return g.push(c),k(-1,a,r(f,e.isAbove),c.node),k(1,a,r(f,e.isBelow),c.node),g}function m(a){return b.filter(b.toArray(a.getElementsByTagName("*")),p)}function n(a,b){return{node:a.node,before:h(a,b)<i(a,b)}}function o(a,c,e){var f,g;return f=d.getClientRects(m(a)),f=b.filter(f,function(a){return e>=a.top&&e<=a.bottom}),g=j(f,c),g&&(g=j(l(a,g),c),g&&p(g.node))?n(g,c):null}var p=c.isContentEditableFalse,q=f.findNode,r=a.curry;return{findClosestClientRect:j,findLineNodeRects:l,closestCaret:o}}),g("7i",["1w","1g","7f","44","5i","4m","1p","43"],function(a,b,c,d,e,f,g,h){function i(a,b,c,f){for(;f=e.findNode(f,a,d.isEditableCaretCandidate,b);)if(c(f))return}function j(a,d,e,f,g,h){function j(f){var h,i,j;for(j=c.getClientRects(f),a==-1&&(j=j.reverse()),h=0;h<j.length;h++)if(i=j[h],!e(i,l)){if(n.length>0&&d(i,b.last(n))&&m++,i.line=m,g(i))return!0;n.push(i)}}var k,l,m=0,n=[];return(l=b.last(h.getClientRects()))?(k=h.getNode(),j(k),i(a,f,j,k),n):n}function k(a,b){return b.line>a}function l(a,b){return b.line===a}function m(a,c,d,e){function i(c){return 1==a?b.last(c.getClientRects()):b.last(c.getClientRects())}var j,k,l,m,n,o,p=new f(c),q=[],r=0;1==a?(j=p.next,k=h.isBelow,l=h.isAbove,m=g.after(e)):(j=p.prev,k=h.isAbove,l=h.isBelow,m=g.before(e)),o=i(m);do if(m.isVisible()&&(n=i(m),!l(n,o))){if(q.length>0&&k(n,b.last(q))&&r++,n=h.clone(n),n.position=m,n.line=r,d(n))return q;q.push(n)}while(m=j(m));return q}var n=a.curry,o=n(j,-1,h.isAbove,h.isBelow),p=n(j,1,h.isBelow,h.isAbove);return{upUntil:o,downUntil:p,positionsUntil:m,isAboveLine:n(k),isLine:n(l)}}),g("73",["1p","5i","1j","1w"],function(a,b,c,d){var e=c.isContentEditableTrue,f=c.isContentEditableFalse,g=function(a,b,c,d){return b._selectionOverrides.showCaret(a,c,d)},h=function(a){var b=a.ownerDocument.createRange();return b.selectNode(a),b},i=function(a,b){var c;return c=a.fire("BeforeObjectSelected",{target:b}),c.isDefaultPrevented()?null:h(b)},j=function(c,h){var i,j;return h=b.normalizeRange(1,c.getBody(),h),i=a.fromRangeStart(h),f(i.getNode())?g(1,c,i.getNode(),!i.isAtEnd()):f(i.getNode(!0))?g(1,c,i.getNode(!0),!1):(j=c.dom.getParent(i.getNode(),d.or(f,e)),f(j)?g(1,c,j,!1):null)},k=function(a,b){var c;return b&&b.collapsed?(c=j(a,b),c?c:b):b};return{showCaret:g,selectNode:i,renderCaretAtRange:j,renderRangeCaret:k}}),g("7c",["1k","1p","5i","4m","70","7i","1j","h","6","73","1g","1w"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m=g.isContentEditableFalse,n=h.getSelectedNode,o=c.isAfterContentEditableFalse,p=c.isBeforeContentEditableFalse,q=function(a,b){for(;b=a(b);)if(b.isVisible())return b;return b},r=function(a,b){var d=c.isInSameBlock(a,b);return!(d||!g.isBr(a.getNode()))||d},s=function(b){return a.isCaretContainerBlock(b.startContainer)},t=function(a,d,e){return e=c.normalizeRange(a,d,e),a===-1?b.fromRangeStart(e):b.fromRangeEnd(e)},u=function(a,b,c,d,e){var f,g,h,i;return!e.collapsed&&(f=n(e),m(f))?j.showCaret(a,b,f,a===-1):(i=s(e),g=t(a,b.getBody(),e),d(g)?j.selectNode(b,g.getNode(a===-1)):(g=c(g))?d(g)?j.showCaret(a,b,g.getNode(a===-1),1===a):(h=c(g),d(h)&&r(g,h)?j.showCaret(a,b,h.getNode(a===-1),1===a):i?j.renderRangeCaret(b,g.toRange()):null):i?e:null)},v=function(a,b,c,d){var g,h,i,l,q,r,s,u,v;if(v=n(d),g=t(a,b.getBody(),d),h=c(b.getBody(),f.isAboveLine(1),g),i=k.filter(h,f.isLine(1)),q=k.last(g.getClientRects()),p(g)&&(v=g.getNode()),o(g)&&(v=g.getNode(!0)),!q)return null;if(r=q.left,l=e.findClosestClientRect(i,r),l&&m(l.node))return s=Math.abs(r-l.left),u=Math.abs(r-l.right),j.showCaret(a,b,l.node,s<u);if(v){var w=f.positionsUntil(a,b.getBody(),f.isAboveLine(1),v);if(l=e.findClosestClientRect(k.filter(w,f.isLine(1)),r))return j.renderRangeCaret(b,l.position.toRange());if(l=k.last(k.filter(w,f.isLine(0))))return j.renderRangeCaret(b,l.position.toRange())}},w=function(a){var b=a.dom.create(a.settings.forced_root_block);return(!i.ie||i.ie>=11)&&(b.innerHTML='<br data-mce-bogus="1">'),b},x=function(a,c,e){var f,g,h,i=new d(a.getBody()),j=l.curry(q,i.next),k=l.curry(q,i.prev);if(e.collapsed&&a.settings.forced_root_block){if(f=a.dom.getParent(e.startContainer,"PRE"),!f)return;g=1===c?j(b.fromRangeStart(e)):k(b.fromRangeStart(e)),g||(h=w(a),1===c?a.$(f).after(h):a.$(f).before(h),a.selection.select(h,!0),a.selection.collapse())}},y=function(a,b){var c,e=new d(a.getBody()),f=l.curry(q,e.next),g=l.curry(q,e.prev),h=b?1:-1,i=b?f:g,j=b?p:o,k=a.selection.getRng();return(c=u(h,a,i,j,k))?c:(c=x(a,h,k),c?c:null)},z=function(a,b){var c,d=b?1:-1,e=b?f.downUntil:f.upUntil,g=a.selection.getRng();return(c=v(d,a,e,g))?c:(c=x(a,d,g),c?c:null)},A=function(a,b){return function(){var c=y(a,b);return!!c&&(a.selection.setRng(c),!0)}},B=function(a,b){return function(){var c=z(a,b);return!!c&&(a.selection.setRng(c),!0)}};return{moveH:A,moveV:B}}),g("7j",["60","3y","3z"],function(a,b,c){var d=function(a,b){return b},e=function(b,c){var d=a.isObject(b)&&a.isObject(c);return d?g(b,c):c},f=function(a){return function(){for(var d=new b(arguments.length),e=0;e<d.length;e++)d[e]=arguments[e];if(0===d.length)throw new c("Can't merge zero objects");for(var f={},g=0;g<d.length;g++){var h=d[g];for(var i in h)h.hasOwnProperty(i)&&(f[i]=a(f[i],h[i]))}return f}},g=f(e),h=f(d);return{deepMerge:g,merge:h}}),g("7d",["3x","1m","7j"],function(a,b,c){var d=function(d){return a.map(d,function(a){return c.merge({shiftKey:!1,altKey:!1,ctrlKey:!1,metaKey:!1,keyCode:0,action:b.noop},a)})},e=function(a,b){return b.keyCode===a.keyCode&&b.shiftKey===a.shiftKey&&b.altKey===a.altKey&&b.ctrlKey===a.ctrlKey&&b.metaKey===a.metaKey},f=function(b,c){return a.bind(d(b),function(a){return e(a,c)?[a]:[]})},g=function(a){var b=Array.prototype.slice.call(arguments,1);return function(){return a.apply(null,b)}};return{match:f,action:g}}),g("6v",["3x","6b","5o","7c","7d","p"],function(a,b,c,d,e,f){var g=function(b,g,h){var i=e.match([{keyCode:f.RIGHT,action:d.moveH(b,!0)},{keyCode:f.LEFT,action:d.moveH(b,!1)},{keyCode:f.UP,action:d.moveV(b,!1)},{keyCode:f.DOWN,action:d.moveV(b,!0)},{keyCode:f.RIGHT,action:c.move(b,g,!0)},{keyCode:f.LEFT,action:c.move(b,g,!1)}],h);a.find(i,function(a){return a.action()}).each(function(a){h.preventDefault()})},h=function(a,b){a.on("keydown",function(c){c.isDefaultPrevented()===!1&&g(a,b,c)})};return{setup:h}}),g("6w",["3x","4i","4j","4k","4l","7d","p"],function(a,b,c,d,e,f,g){var h=function(h,i,j){var k=f.match([{keyCode:g.BACKSPACE,action:f.action(d.backspaceDelete,h,!1)},{keyCode:g.DELETE,action:f.action(d.backspaceDelete,h,!0)},{keyCode:g.BACKSPACE,action:f.action(e.backspaceDelete,h,i,!1)},{keyCode:g.DELETE,action:f.action(e.backspaceDelete,h,i,!0)},{keyCode:g.BACKSPACE,action:f.action(c.backspaceDelete,h,!1)},{keyCode:g.DELETE,action:f.action(c.backspaceDelete,h,!0)},{keyCode:g.BACKSPACE,action:f.action(b.backspaceDelete,h,!1)},{keyCode:g.DELETE,action:f.action(b.backspaceDelete,h,!0)}],j);a.find(k,function(a){return a.action()}).each(function(a){j.preventDefault()})},i=function(b,c){var e=f.match([{keyCode:g.BACKSPACE,action:f.action(d.paddEmptyElement,b)},{keyCode:g.DELETE,action:f.action(d.paddEmptyElement,b)}],c);a.find(e,function(a){return a.action()})},j=function(a,b){a.on("keydown",function(c){c.isDefaultPrevented()===!1&&h(a,b,c)}),a.on("keyup",function(b){b.isDefaultPrevented()===!1&&i(a,b)})};return{setup:j}}),g("6x",["1k","1j","h","c","6","1l","9"],function(a,b,c,d,e,f,g){var h=e.ie&&e.ie<11,i=function(a){return a&&"A"===a.nodeName&&0===g.trim(f.trim(a.innerText||a.textContent)).length},j=function(a){return a&&/^(TD|TH|CAPTION)$/.test(a.nodeName)},k=function(a){a.innerHTML=h?"":'<br data-mce-bogus="1">'},l=function(a,b){return a.nodeName===b||a.previousSibling&&a.previousSibling.nodeName===b},m=function(a,b){return b&&a.isBlock(b)&&!/^(TD|TH|CAPTION|FORM)$/.test(b.nodeName)&&!/^(fixed|absolute)/i.test(b.style.position)&&"true"!==a.getContentEditable(b)},n=function(a,b,c){var d;a.isBlock(c)&&(d=b.getRng(),c.appendChild(a.create("span",null,"\xa0")),b.select(c),c.lastChild.outerHTML="",b.setRng(d))},o=function(a,b,c){var d,e=c,f=[];if(e){for(;e=e.firstChild;){if(a.isBlock(e))return;1!=e.nodeType||b[e.nodeName.toLowerCase()]||f.push(e)}for(d=f.length;d--;)e=f[d],!e.hasChildNodes()||e.firstChild==e.lastChild&&""===e.firstChild.nodeValue?a.remove(e):i(e)&&a.remove(e)}},p=function(a,c,d){return b.isText(c)===!1?d:a?1===d&&c.data.charAt(d-1)===f.ZWSP?0:d:d===c.data.length-1&&c.data.charAt(d)===f.ZWSP?c.data.length:d},q=function(a){var b=a.cloneRange();return b.setStart(a.startContainer,p(!0,a.startContainer,a.startOffset)),b.setEnd(a.endContainer,p(!1,a.endContainer,a.endOffset)),b},r=function(a){for(;a;){if(1===a.nodeType||3===a.nodeType&&a.data&&/[\r\n\s]/.test(a.data))return a;a=a.nextSibling}},s=function(b){function f(f){function x(a){var b,c,f,h,j=a;if(a){if(e.ie&&e.ie<9&&N&&N.firstChild&&N.firstChild==N.lastChild&&"BR"==N.firstChild.tagName&&g.remove(N.firstChild),/^(LI|DT|DD)$/.test(a.nodeName)){var k=r(a.firstChild);k&&/^(UL|OL|DL)$/.test(k.nodeName)&&a.insertBefore(g.doc.createTextNode("\xa0"),a.firstChild)}if(f=g.createRng(),e.ie||a.normalize(),a.hasChildNodes()){for(b=new d(a,a);c=b.current();){if(3==c.nodeType){f.setStart(c,0),f.setEnd(c,0);break}if(w[c.nodeName.toLowerCase()]){f.setStartBefore(c),f.setEndBefore(c);break}j=c,c=b.next()}c||(f.setStart(j,0),f.setEnd(j,0))}else"BR"==a.nodeName?a.nextSibling&&g.isBlock(a.nextSibling)?((!O||O<9)&&(h=g.create("br"),a.parentNode.insertBefore(h,a)),f.setStartBefore(a),f.setEndBefore(a)):(f.setStartAfter(a),f.setEndAfter(a)):(f.setStart(a,0),f.setEnd(a,0));i.setRng(f),g.remove(h),i.scrollIntoView(a)}}function y(a){var b=s.forced_root_block;b&&b.toLowerCase()===a.tagName.toLowerCase()&&g.setAttribs(a,s.forced_root_block_attrs)}function z(a){var b,c,d,e=L,f=u.getTextInlineElements();if(a||"TABLE"==T||"HR"==T?(b=g.create(a||V),y(b)):b=N.cloneNode(!1),d=b,s.keep_styles===!1)g.setAttrib(b,"style",null),g.setAttrib(b,"class",null);else do if(f[e.nodeName]){if("_mce_caret"==e.id)continue;c=e.cloneNode(!1),g.setAttrib(c,"id",""),b.hasChildNodes()?(c.appendChild(b.firstChild),b.appendChild(c)):(d=c,b.appendChild(c))}while((e=e.parentNode)&&e!=K);return h||(d.innerHTML='<br data-mce-bogus="1">'),b}function A(a){var b,c,e,f;if(f=p(a,L,M),3==L.nodeType&&(a?f>0:f<L.nodeValue.length))return!1;if(L.parentNode==N&&W&&!a)return!0;if(a&&1==L.nodeType&&L==N.firstChild)return!0;if(l(L,"TABLE")||l(L,"HR"))return W&&!a||!W&&a;for(b=new d(L,N),3==L.nodeType&&(a&&0===f?b.prev():a||f!=L.nodeValue.length||b.next());c=b.current();){if(1===c.nodeType){if(!c.getAttribute("data-mce-bogus")&&(e=c.nodeName.toLowerCase(),v[e]&&"br"!==e))return!1}else if(3===c.nodeType&&!/^[ \t\r\n]*$/.test(c.nodeValue))return!1;a?b.prev():b.next()}return!0}function B(a,c){var d,e,f,h,i,k,l=V||"P";if(e=g.getParent(a,g.isBlock),!e||!m(g,e)){if(e=e||K,k=e==b.getBody()||j(e)?e.nodeName.toLowerCase():e.parentNode.nodeName.toLowerCase(),!e.hasChildNodes())return d=g.create(l),y(d),e.appendChild(d),I.setStart(d,0),I.setEnd(d,0),d;for(h=a;h.parentNode!=e;)h=h.parentNode;for(;h&&!g.isBlock(h);)f=h,h=h.previousSibling;if(f&&u.isValidChild(k,l.toLowerCase())){for(d=g.create(l),y(d),f.parentNode.insertBefore(d,f),h=f;h&&!g.isBlock(h);)i=h.nextSibling,d.appendChild(h),h=i;I.setStart(a,c),I.setEnd(a,c)}}return a}function C(){function a(a){for(var b=S[a?"firstChild":"lastChild"];b&&1!=b.nodeType;)b=b[a?"nextSibling":"previousSibling"];return b===N}function c(){var a=S.parentNode;return/^(LI|DT|DD)$/.test(a.nodeName)?a:S}if(S!=b.getBody()){var d=S.parentNode.nodeName;/^(OL|UL|LI)$/.test(d)&&(V="LI"),Q=V?z(V):g.create("BR"),a(!0)&&a()?"LI"==d?g.insertAfter(Q,c()):g.replace(Q,S):a(!0)?"LI"==d?(g.insertAfter(Q,c()),Q.appendChild(g.doc.createTextNode(" ")),Q.appendChild(S)):S.parentNode.insertBefore(Q,S):a()?(g.insertAfter(Q,c()),n(g,i,Q)):(S=c(),J=I.cloneRange(),J.setStartAfter(N),J.setEndAfter(S),R=J.extractContents(),"LI"==V&&"LI"==R.firstChild.nodeName?(Q=R.firstChild,g.insertAfter(R,S)):(g.insertAfter(R,S),g.insertAfter(Q,S))),g.remove(N),x(Q),t.add()}}function D(){b.execCommand("InsertLineBreak",!1,f)}function E(a){do 3===a.nodeType&&(a.nodeValue=a.nodeValue.replace(/^[\r\n]+/,"")),a=a.firstChild;while(a)}function F(a){var b,c,d=g.getRoot();for(b=a;b!==d&&"false"!==g.getContentEditable(b);)"true"===g.getContentEditable(b)&&(c=b),b=b.parentNode;return b!==d?c:d}function G(a){var b;h||(a.normalize(),b=a.lastChild,b&&!/^(left|right)$/gi.test(g.getStyle(b,"float",!0))||g.add(a,"br"))}function H(){Q=/^(H[1-6]|PRE|FIGURE)$/.test(T)&&"HGROUP"!=U?z(V):z(),s.end_container_on_empty_block&&m(g,S)&&g.isEmpty(N)?Q=g.split(S,N):g.insertAfter(Q,N),x(Q)}var I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W;if(I=i.getRng(!0),!f.isDefaultPrevented()){if(!I.collapsed)return void b.execCommand("Delete");if(new c(g).normalize(I),L=I.startContainer,M=I.startOffset,V=(s.force_p_newlines?"p":"")||s.forced_root_block,V=V?V.toUpperCase():"",O=g.doc.documentMode,P=f.shiftKey,1==L.nodeType&&L.hasChildNodes()&&(W=M>L.childNodes.length-1,L=L.childNodes[Math.min(M,L.childNodes.length-1)]||L,M=W&&3==L.nodeType?L.nodeValue.length:0),K=F(L)){if(t.beforeChange(),!g.isBlock(K)&&K!=g.getRoot())return void(V&&!P||D());if((V&&!P||!V&&P)&&(L=B(L,M)),N=g.getParent(L,g.isBlock),S=N?g.getParent(N.parentNode,g.isBlock):null,T=N?N.nodeName.toUpperCase():"",U=S?S.nodeName.toUpperCase():"","LI"!=U||f.ctrlKey||(N=S,T=U),b.undoManager.typing&&(b.undoManager.typing=!1,b.undoManager.add()),/^(LI|DT|DD)$/.test(T)){if(!V&&P)return void D();if(g.isEmpty(N))return void C()}if("PRE"==T&&s.br_in_pre!==!1){if(!P)return void D()}else if(!V&&!P&&"LI"!=T||V&&P)return void D();V&&N===b.getBody()||(V=V||"P",a.isCaretContainerBlock(N)?(Q=a.showCaretContainerBlock(N),g.isEmpty(N)&&k(N),x(Q)):A()?H():A(!0)?(Q=N.parentNode.insertBefore(z(),N),n(g,i,Q),x(l(N,"HR")?Q:N)):(J=q(I).cloneRange(),J.setEndAfter(N),R=J.extractContents(),E(R),Q=R.firstChild,g.insertAfter(R,N),o(g,v,Q),G(N),g.isEmpty(N)&&k(N),Q.normalize(),g.isEmpty(Q)?(g.remove(Q),H()):x(Q)),g.setAttrib(Q,"id",""),b.fire("NewBlock",{newBlock:Q}),t.typing=!1,t.add())}}}var g=b.dom,i=b.selection,s=b.settings,t=b.undoManager,u=b.schema,v=u.getNonEmptyElements(),w=u.getMoveCaretBeforeOnEnterElements();b.on("keydown",function(a){13==a.keyCode&&f(a)!==!1&&a.preventDefault()})};return{setup:s}}),g("7e",["1m","1p","1j","5n"],function(a,b,c,d){var e=function(a,b){return i(a)&&c.isText(b.container())},f=function(a,b){var c=b.container(),d=b.offset();c.insertData(d,"\xa0"),a.selection.setCursorLocation(c,d+1)},g=function(a,b,c){return!!e(c,b)&&(f(a,b),!0)},h=function(c){var e=b.fromRangeStart(c.selection.getRng()),f=d.readLocation(c.getBody(),e);return f.map(a.curry(g,c,e)).getOr(!1)},i=function(b){return b.fold(a.constant(!1),a.constant(!0),a.constant(!0),a.constant(!1))},j=function(a){return!!a.selection.isCollapsed()&&h(a)};return{insertAtSelection:j}}),g("6y",["3x","7e","7d","p"],function(a,b,c,d){var e=function(e,f){var g=c.match([{keyCode:d.SPACEBAR,action:c.action(b.insertAtSelection,e)}],f);a.find(g,function(a){return a.action()}).each(function(a){f.preventDefault()})},f=function(a){a.on("keydown",function(b){b.isDefaultPrevented()===!1&&e(a,b)})};return{setup:f}}),g("6f",["6v","5o","6w","6x","6y"],function(a,b,c,d,e){var f=function(f){var g=b.setupSelectedState(f);a.setup(f,g),c.setup(f,g),d.setup(f),e.setup(f)};return{setup:f}}),g("6g",["h","6","5"],function(a,b,c){return function(d){function e(a){var b,c;if(c=d.$(a).parentsUntil(d.getBody()).add(a),c.length===g.length){for(b=c.length;b>=0&&c[b]===g[b];b--);if(b===-1)return g=c,!0}return g=c,!1}var f,g=[];"onselectionchange"in d.getDoc()||d.on("NodeChange Click MouseUp KeyUp Focus",function(b){var c,e;c=d.selection.getRng(),e={startContainer:c.startContainer,startOffset:c.startOffset,endContainer:c.endContainer,endOffset:c.endOffset},"nodechange"!=b.type&&a.compareRanges(e,f)||d.fire("SelectionChange"),f=e}),d.on("contextmenu",function(){d.fire("SelectionChange")}),d.on("SelectionChange",function(){var a=d.selection.getStart(!0);!b.range&&d.selection.isCollapsed()||!e(a)&&d.dom.isChildOf(a,d.getBody())&&d.nodeChanged({selectionChange:!0})}),d.on("MouseUp",function(a){a.isDefaultPrevented()||("IMG"==d.selection.getNode().nodeName?c.setEditorTimeout(d,function(){d.nodeChanged()}):d.nodeChanged())}),this.nodeChanged=function(a){var b,c,e,f=d.selection;d.initialized&&f&&!d.settings.disable_nodechange&&!d.readonly&&(e=d.getBody(),b=f.getStart(!0)||e,b.ownerDocument==d.getDoc()&&d.dom.isChildOf(b,e)||(b=e),c=[],d.dom.getParent(b,function(a){return a===e||void c.push(a)}),a=a||{},a.element=b,a.parents=c,d.fire("NodeChange",a))}}}),g("6z",["1k","69","1p","a","1j","h","43","5"],function(a,b,c,d,e,f,g,h){var i=e.isContentEditableFalse,j=function(a){return a&&/^(TD|TH)$/i.test(a.nodeName)};return function(c,e){function f(a,b){var d,e,f,h,i,j=g.collapse(a.getBoundingClientRect(),b);return"BODY"==c.tagName?(d=c.ownerDocument.documentElement,e=c.scrollLeft||d.scrollLeft,f=c.scrollTop||d.scrollTop):(i=c.getBoundingClientRect(),e=c.scrollLeft-i.left,f=c.scrollTop-i.top),j.left+=e,j.right+=e,j.top+=f,j.bottom+=f,j.width=1,h=a.offsetWidth-a.clientWidth,h>0&&(b&&(h*=-1),j.left+=h,j.right+=h),j}function k(){var b,e,f,g,h;for(b=d("*[contentEditable=false]",c),g=0;g<b.length;g++)e=b[g],f=e.previousSibling,a.endsWithCaretContainer(f)&&(h=f.data,1==h.length?f.parentNode.removeChild(f):f.deleteData(h.length-1,1)),f=e.nextSibling,a.startsWithCaretContainer(f)&&(h=f.data,1==h.length?f.parentNode.removeChild(f):f.deleteData(0,1));return null}function l(b,g){var h,k;return m(),j(g)?null:e(g)?(s=a.insertBlock("p",g,b),h=f(g,b),d(s).css("top",h.top),r=d('<div class="mce-visual-caret" data-mce-bogus="all"></div>').css(h).appendTo(c),b&&r.addClass("mce-visual-caret-before"),n(),k=g.ownerDocument.createRange(),k.setStart(s,0),k.setEnd(s,0),k):(s=a.insertInline(g,b),k=g.ownerDocument.createRange(),i(s.nextSibling)?(k.setStart(s,0),k.setEnd(s,0)):(k.setStart(s,1),k.setEnd(s,1)),k)}function m(){k(),s&&(b.remove(s),s=null),r&&(r.remove(),r=null),clearInterval(q)}function n(){q=h.setInterval(function(){d("div.mce-visual-caret",c).toggleClass("mce-visual-caret-hidden")},500)}function o(){h.clearInterval(q)}function p(){return".mce-visual-caret {position: absolute;background-color: black;background-color: currentcolor;}.mce-visual-caret-hidden {display: none;}*[data-mce-caret] {position: absolute;left: -1000px;right: auto;top: 0;margin: 0;padding: 0;}"}var q,r,s;return{show:l,hide:m,getCss:p,destroy:o}}});g("7g",[],function(){var a=function(a){var b,c,d,e;return e=a.getBoundingClientRect(),b=a.ownerDocument,c=b.documentElement,d=b.defaultView,{top:e.top+d.pageYOffset-c.clientTop,left:e.left+d.pageXOffset-c.clientLeft}},b=function(b){return b.inline?a(b.getBody()):{left:0,top:0}},c=function(a){var b=a.getBody();return a.inline?{left:b.scrollLeft,top:b.scrollTop}:{left:0,top:0}},d=function(a){var b=a.getBody(),c=a.getDoc().documentElement,d={left:b.scrollLeft,top:b.scrollTop},e={left:b.scrollLeft||c.scrollLeft,top:b.scrollTop||c.scrollTop};return a.inline?d:e},e=function(b,c){if(c.target.ownerDocument!==b.getDoc()){var e=a(b.getContentAreaContainer()),f=d(b);return{left:c.pageX-e.left+f.left,top:c.pageY-e.top+f.top}}return{left:c.pageX,top:c.pageY}},f=function(a,b,c){return{pageX:c.left-a.left+b.left,pageY:c.top-a.top+b.top}},g=function(a,d){return f(b(a),c(a),e(a,d))};return{calc:g}});g("71",["1j","1g","1w","5","e","7g"],function(a,b,c,d,e,f){var g=a.isContentEditableFalse,h=a.isContentEditableTrue,i=function(a,b){return g(b)&&b!==a},j=function(a,b,c){return b!==c&&!a.dom.isChildOf(b,c)&&!g(b)},k=function(a){var b=a.cloneNode(!0);return b.removeAttribute("data-mce-selected"),b},l=function(a,b,c,d){var e=b.cloneNode(!0);a.dom.setStyles(e,{width:c,height:d}),a.dom.setAttrib(e,"data-mce-selected",null);var f=a.dom.create("div",{"class":"mce-drag-container","data-mce-bogus":"all",unselectable:"on",contenteditable:"false"});return a.dom.setStyles(f,{position:"absolute",opacity:.5,overflow:"hidden",border:0,padding:0,margin:0,width:c,height:d}),a.dom.setStyles(e,{margin:0,boxSizing:"border-box"}),f.appendChild(e),f},m=function(a,b){a.parentNode!==b&&b.appendChild(a)},n=function(a,b,c,d,e,f){var g=0,h=0;a.style.left=b.pageX+"px",a.style.top=b.pageY+"px",b.pageX+c>e&&(g=b.pageX+c-e),b.pageY+d>f&&(h=b.pageY+d-f),a.style.width=c-g+"px",a.style.height=d-h+"px"},o=function(a){a&&a.parentNode&&a.parentNode.removeChild(a)},p=function(a){return 0===a.button},q=function(a){return a.element},r=function(a,b){return{pageX:b.pageX-a.relX,pageY:b.pageY+5}},s=function(a,d){return function(e){if(p(e)){var f=b.find(d.dom.getParents(e.target),c.or(g,h));if(i(d.getBody(),f)){var j=d.dom.getPos(f),k=d.getBody(),m=d.getDoc().documentElement;a.element=f,a.screenX=e.screenX,a.screenY=e.screenY,a.maxX=(d.inline?k.scrollWidth:m.offsetWidth)-2,a.maxY=(d.inline?k.scrollHeight:m.offsetHeight)-2,a.relX=e.pageX-j.x,a.relY=e.pageY-j.y,a.width=f.offsetWidth,a.height=f.offsetHeight,a.ghost=l(d,f,a.width,a.height)}}}},t=function(a,b){var c=d.throttle(function(a,c){b._selectionOverrides.hideFakeCaret(),b.selection.placeCaretAt(a,c)},0);return function(d){var e=Math.max(Math.abs(d.screenX-a.screenX),Math.abs(d.screenY-a.screenY));if(q(a)&&!a.dragging&&e>10){var g=b.fire("dragstart",{target:a.element});if(g.isDefaultPrevented())return;a.dragging=!0,b.focus()}if(a.dragging){var h=r(a,f.calc(b,d));m(a.ghost,b.getBody()),n(a.ghost,h,a.width,a.height,a.maxX,a.maxY),c(d.clientX,d.clientY)}}},u=function(a){var b=a.getSel().getRangeAt(0),c=b.startContainer;return 3===c.nodeType?c.parentNode:c},v=function(a,b){return function(c){if(a.dragging&&j(b,u(b.selection),a.element)){var d=k(a.element),e=b.fire("drop",{targetClone:d,clientX:c.clientX,clientY:c.clientY});e.isDefaultPrevented()||(d=e.targetClone,b.undoManager.transact(function(){o(a.element),b.insertContent(b.dom.getOuterHTML(d)),b._selectionOverrides.hideFakeCaret()}))}x(a)}},w=function(a,b){return function(){x(a),a.dragging&&b.fire("dragend")}},x=function(a){a.dragging=!1,a.element=null,o(a.ghost)},y=function(a){var b,c,d,f,g,h,i={};b=e.DOM,h=document,c=s(i,a),d=t(i,a),f=v(i,a),g=w(i,a),a.on("mousedown",c),a.on("mousemove",d),a.on("mouseup",f),b.bind(h,"mousemove",d),b.bind(h,"mouseup",g),a.on("remove",function(){b.unbind(h,"mousemove",d),b.unbind(h,"mouseup",g)})},z=function(a){a.on("drop",function(b){var c="undefined"!=typeof b.clientX?a.getDoc().elementFromPoint(b.clientX,b.clientY):null;(g(c)||g(a.dom.getContentEditableParent(c)))&&b.preventDefault()})},A=function(a){y(a),z(a)};return{init:A}}),g("7k",[],function(){var a=function(a){return void 0!==a.style};return{isSupported:a}}),g("7h",["60","3x","63","4z","5a","6o","1r","4d","7k","6l","3z","49","4t"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){var n=function(b,c,d){if(!a.isString(d))throw l.error("Invalid call to CSS.set. Property ",c,":: Value ",d,":: Element ",b),new k("CSS value must be a string: "+d);i.isSupported(b)&&b.style.setProperty(c,d)},o=function(a,b){i.isSupported(a)&&a.style.removeProperty(b)},p=function(a,b,c){var d=a.dom();n(d,b,c)},q=function(a,b){var d=a.dom();c.each(b,function(a,b){n(d,b,a)})},r=function(a,b){var d=a.dom();c.each(b,function(a,b){a.fold(function(){o(d,b)},function(a){n(d,b,a)})})},s=function(a,b){var c=a.dom(),d=m.getComputedStyle(c),e=d.getPropertyValue(b),g=""!==e||f.inBody(a)?e:t(c,b);return null===g?void 0:g},t=function(a,b){return i.isSupported(a)?a.style.getPropertyValue(b):""},u=function(a,b){var c=a.dom(),e=t(c,b);return d.from(e).filter(function(a){return a.length>0})},v=function(a,b,c){var d=g.fromTag(a);p(d,b,c);var e=u(d,b);return e.isSome()},w=function(a,b){var c=a.dom();o(c,b),e.has(a,"style")&&""===j.trim(e.get(a,"style"))&&e.remove(a,"style")},x=function(a,b){var c=e.get(a,"style"),d=b(a),f=void 0===c?e.remove:e.set;return f(a,"style",c),d},y=function(a,b){var c=a.dom(),d=b.dom();i.isSupported(c)&&i.isSupported(d)&&(d.style.cssText=c.style.cssText)},z=function(a){return a.dom().offsetWidth},A=function(a,b,c){u(a,c).each(function(a){u(b,c).isNone()&&p(b,c,a)})},B=function(a,c,d){h.isElement(a)&&h.isElement(c)&&b.each(d,function(b){A(a,c,b)})};return{copy:y,set:p,preserve:x,setAll:q,setOptions:r,remove:w,get:s,getRaw:u,isValidValue:v,reflow:z,transfer:B}}),g("72",["1m","1r","7h","59"],function(a,b,c,d){var e=function(a,b){var c=b.dom();return c[a]},f=function(a,b){return parseInt(c.get(b,a),10)},g=a.curry(e,"clientWidth"),h=a.curry(e,"clientHeight"),i=a.curry(f,"margin-top"),j=a.curry(f,"margin-left"),k=function(a){return a.dom().getBoundingClientRect()},l=function(a,b,c){var d=g(a),e=h(a);return b>=0&&c>=0&&b<=d&&c<=e},m=function(a,b,c,d){var e=k(b),f=a?e.left+b.dom().clientLeft+j(b):0,g=a?e.top+b.dom().clientTop+i(b):0,h=c-f,l=d-g;return{x:h,y:l}},n=function(a,c,e){var f=b.fromDom(a.getBody()),g=a.inline?f:d.documentElement(f),h=m(a.inline,g,c,e);return l(g,h.x,h.y)};return{isXYInContentArea:n}}),g("6h",["1k","1p","5i","4m","6z","70","1j","1n","71","72","6","73","5","p"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){function o(g){function o(a){return g.dom.hasClass(a,"mce-offscreen-selection")}function t(){var a=g.dom.get(M);return a?a.getElementsByTagName("*")[0]:a}function u(a){return g.dom.isBlock(a)}function v(a){a&&g.selection.setRng(a)}function w(){return g.selection.getRng()}function x(a,b){g.selection.scrollIntoView(a,b)}function y(a,b,c){var d;return d=g.fire("ShowCaret",{target:b,direction:a,before:c}),d.isDefaultPrevented()?null:(x(b,a===-1),L.show(c,b))}function z(a,d){return d=c.normalizeRange(a,K,d),a==-1?b.fromRangeStart(d):b.fromRangeEnd(d)}function A(b){b.hasAttribute("data-mce-caret")&&(a.showCaretContainerBlock(b),v(w()),x(b[0]))}function B(){function a(a){for(var b=g.getBody();a&&a!=b;){if(p(a)||q(a))return a;a=a.parentNode}return null}function c(b){var c=!1;b.on("touchstart",function(){c=!1}),b.on("touchmove",function(){c=!0}),b.on("touchend",function(d){var e=a(d.target);q(e)&&(c||(d.preventDefault(),F(l.selectNode(b,e))))})}g.on("mouseup",function(a){var b=w();b.collapsed&&j.isXYInContentArea(g,a.clientX,a.clientY)&&v(l.renderCaretAtRange(g,b))}),g.on("click",function(b){var c;c=a(b.target),c&&(q(c)&&(b.preventDefault(),g.focus()),p(c)&&g.dom.isChildOf(c,g.selection.getNode())&&G())}),g.on("blur NewBlock",function(){G(),I()});var e=function(a){var c=new d(a);if(!a.firstChild)return!1;var e=b.before(a.firstChild),f=c.next(e);return f&&!s(f)&&!r(f)},u=function(a,b){var c=g.dom.getParent(a,g.dom.isBlock),d=g.dom.getParent(b,g.dom.isBlock);return c===d},x=function(a,b){var c=g.dom.getParent(a,g.dom.isBlock),d=g.dom.getParent(b,g.dom.isBlock);return c&&!u(c,d)&&e(c)};c(g),g.on("mousedown",function(b){var c;if(j.isXYInContentArea(g,b.clientX,b.clientY)!==!1)if(c=a(b.target))q(c)?(b.preventDefault(),F(l.selectNode(g,c))):(G(),p(c)&&b.shiftKey||h.isXYWithinRange(b.clientX,b.clientY,g.selection.getRng())||g.selection.placeCaretAt(b.clientX,b.clientY));else{G(),I();var d=f.closestCaret(K,b.clientX,b.clientY);d&&(x(b.target,d.node)||(b.preventDefault(),g.getBody().focus(),v(y(1,d.node,d.before))))}}),g.on("keypress",function(a){if(!n.modifierPressed(a))switch(a.keyCode){default:q(g.selection.getNode())&&a.preventDefault()}}),g.on("getSelectionRange",function(a){var b=a.range;if(J){if(!J.parentNode)return void(J=null);b=b.cloneRange(),b.selectNode(J),a.range=b}}),g.on("setSelectionRange",function(a){var b;b=F(a.range,a.forward),b&&(a.range=b)}),g.on("AfterSetSelectionRange",function(a){var b=a.range;E(b)||I(),o(b.startContainer.parentNode)||G()}),g.on("focus",function(){m.setEditorTimeout(g,function(){g.selection.setRng(l.renderRangeCaret(g,g.selection.getRng()))},0)}),g.on("copy",function(a){var b=a.clipboardData;if(!a.isDefaultPrevented()&&a.clipboardData&&!k.ie){var c=t();c&&(a.preventDefault(),b.clearData(),b.setData("text/html",c.outerHTML),b.setData("text/plain",c.outerText))}}),i.init(g)}function C(){var a=g.contentStyles,b=".mce-content-body";a.push(L.getCss()),a.push(b+" .mce-offscreen-selection {position: absolute;left: -9999999999px;max-width: 1000000px;}"+b+" *[contentEditable=false] {cursor: default;}"+b+" *[contentEditable=true] {cursor: text;}")}function D(b){return a.isCaretContainer(b)||a.startsWithCaretContainer(b)||a.endsWithCaretContainer(b)}function E(a){return D(a.startContainer)||D(a.endContainer); -}function F(a,b){var c,d,e,f,h,i,j,l,m,n,o=g.$,p=g.dom;if(!a)return null;if(a.collapsed){if(!E(a))if(b===!1){if(l=z(-1,a),q(l.getNode(!0)))return y(-1,l.getNode(!0),!1);if(q(l.getNode()))return y(-1,l.getNode(),!l.isAtEnd())}else{if(l=z(1,a),q(l.getNode()))return y(1,l.getNode(),!l.isAtEnd());if(q(l.getNode(!0)))return y(1,l.getNode(!0),!1)}return null}return f=a.startContainer,h=a.startOffset,i=a.endOffset,3==f.nodeType&&0==h&&q(f.parentNode)&&(f=f.parentNode,h=p.nodeIndex(f),f=f.parentNode),1!=f.nodeType?null:(i==h+1&&(c=f.childNodes[h]),q(c)?(m=n=c.cloneNode(!0),j=g.fire("ObjectSelected",{target:c,targetClone:m}),j.isDefaultPrevented()?null:(m=j.targetClone,d=o("#"+M),0===d.length&&(d=o('<div data-mce-bogus="all" class="mce-offscreen-selection"></div>').attr("id",M),d.appendTo(g.getBody())),a=g.dom.createRng(),m===n&&k.ie?(d.empty().append('<p style="font-size: 0" data-mce-bogus="all">\xa0</p>').append(m),a.setStartAfter(d[0].firstChild.firstChild),a.setEndAfter(m)):(d.empty().append("\xa0").append(m).append("\xa0"),a.setStart(d[0].firstChild,1),a.setEnd(d[0].lastChild,0)),d.css({top:p.getPos(c,g.getBody()).y}),d[0].focus(),e=g.selection.getSel(),e.removeAllRanges(),e.addRange(a),g.$("*[data-mce-selected]").removeAttr("data-mce-selected"),c.setAttribute("data-mce-selected",1),J=c,I(),a)):null)}function G(){J&&(J.removeAttribute("data-mce-selected"),g.$("#"+M).remove(),J=null)}function H(){L.destroy(),J=null}function I(){L.hide()}var J,K=g.getBody(),L=new e(g.getBody(),u),M="sel-"+g.dom.uniqueId();return k.ceFalse&&(B(),C()),{showCaret:y,showBlockCaretContainer:A,hideFakeCaret:I,destroy:H}}var p=g.isContentEditableTrue,q=g.isContentEditableFalse,r=c.isAfterContentEditableFalse,s=c.isBeforeContentEditableFalse;return o}),g("74",["e"],function(a){function b(b,c,d){for(var e=[];c&&c!=b;c=c.parentNode)e.push(a.nodeIndex(c,d));return e}function c(a,b){var c,d,e;for(d=a,c=b.length-1;c>=0;c--){if(e=d.childNodes,b[c]>e.length-1)return null;d=e[b[c]]}return d}return{create:b,resolve:c}}),g("6i",["p","h","c","74","i","d","6","9","5","1k","1p","4m"],function(a,b,c,d,e,f,g,h,i,j,k,l){return function(c){function d(a,b){try{c.getDoc().execCommand(a,!1,b)}catch(a){}}function m(){var a=c.getDoc().documentMode;return a?a:6}function n(a){return a.isDefaultPrevented()}function o(a){var b,d;a.dataTransfer&&(c.selection.isCollapsed()&&"IMG"==a.target.tagName&&_.select(a.target),b=c.selection.getContent(),b.length>0&&(d=ga+escape(c.id)+","+escape(b),a.dataTransfer.setData(ha,d)))}function p(a){var b;return a.dataTransfer&&(b=a.dataTransfer.getData(ha),b&&b.indexOf(ga)>=0)?(b=b.substr(ga.length).split(","),{id:unescape(b[0]),html:unescape(b[1])}):null}function q(a,b){c.queryCommandSupported("mceInsertClipboardContent")?c.execCommand("mceInsertClipboardContent",!1,{content:a,internal:b}):c.execCommand("mceInsertContent",!1,a)}function r(){function a(a){var b=$.create("body"),c=a.cloneContents();return b.appendChild(c),_.serializer.serialize(b,{format:"html"})}function d(d){if(!d.setStart){if(d.item)return!1;var e=d.duplicate();return e.moveToElementText(c.getBody()),b.compareRanges(d,e)}var f=a(d),g=$.createRng();g.selectNode(c.getBody());var h=a(g);return f===h}c.on("keydown",function(a){var b,e,f=a.keyCode;if(!n(a)&&(f==Z||f==Y)){if(b=c.selection.isCollapsed(),e=c.getBody(),b&&!$.isEmpty(e))return;if(!b&&!d(c.selection.getRng()))return;a.preventDefault(),c.setContent(""),e.firstChild&&$.isBlock(e.firstChild)?c.selection.setCursorLocation(e.firstChild,0):c.selection.setCursorLocation(e,0),c.nodeChanged()}})}function s(){c.shortcuts.add("meta+a",null,"SelectAll")}function t(){c.settings.content_editable||$.bind(c.getDoc(),"mousedown mouseup",function(a){var b;if(a.target==c.getDoc().documentElement)if(b=_.getRng(),c.getBody().focus(),"mousedown"==a.type){if(j.isCaretContainer(b.startContainer))return;_.placeCaretAt(a.clientX,a.clientY)}else _.setRng(b)})}function u(){c.on("keydown",function(a){if(!n(a)&&a.keyCode===Y){if(!c.getBody().getElementsByTagName("hr").length)return;if(_.isCollapsed()&&0===_.getRng(!0).startOffset){var b=_.getNode(),d=b.previousSibling;if("HR"==b.nodeName)return $.remove(b),void a.preventDefault();d&&d.nodeName&&"hr"===d.nodeName.toLowerCase()&&($.remove(d),a.preventDefault())}}})}function v(){window.Range.prototype.getClientRects||c.on("mousedown",function(a){if(!n(a)&&"HTML"===a.target.nodeName){var b=c.getBody();b.blur(),i.setEditorTimeout(c,function(){b.focus()})}})}function w(){c.on("click",function(a){var b=a.target;/^(IMG|HR)$/.test(b.nodeName)&&"false"!==$.getContentEditableParent(b)&&(a.preventDefault(),c.selection.select(b),c.nodeChanged()),"A"==b.nodeName&&$.hasClass(b,"mce-item-anchor")&&(a.preventDefault(),_.select(b))})}function x(){function a(){var a=$.getAttribs(_.getStart().cloneNode(!1));return function(){var b=_.getStart();b!==c.getBody()&&($.setAttrib(b,"style",null),X(a,function(a){b.setAttributeNode(a.cloneNode(!0))}))}}function b(){return!_.isCollapsed()&&$.getParent(_.getStart(),$.isBlock)!=$.getParent(_.getEnd(),$.isBlock)}c.on("keypress",function(d){var e;if(!n(d)&&(8==d.keyCode||46==d.keyCode)&&b())return e=a(),c.getDoc().execCommand("delete",!1,null),e(),d.preventDefault(),!1}),$.bind(c.getDoc(),"cut",function(d){var e;!n(d)&&b()&&(e=a(),i.setEditorTimeout(c,function(){e()}))})}function y(){document.body.setAttribute("role","application")}function z(){c.on("keydown",function(a){if(!n(a)&&a.keyCode===Y&&_.isCollapsed()&&0===_.getRng(!0).startOffset){var b=_.getNode().previousSibling;if(b&&b.nodeName&&"table"===b.nodeName.toLowerCase())return a.preventDefault(),!1}})}function A(){m()>7||(d("RespectVisibilityInDesign",!0),c.contentStyles.push(".mceHideBrInPre pre br {display: none}"),$.addClass(c.getBody(),"mceHideBrInPre"),ba.addNodeFilter("pre",function(a){for(var b,c,d,f,g=a.length;g--;)for(b=a[g].getAll("br"),c=b.length;c--;)d=b[c],f=d.prev,f&&3===f.type&&"\n"!=f.value.charAt(f.value-1)?f.value+="\n":d.parent.insert(new e("#text",3),d,!0).value="\n"}),ca.addNodeFilter("pre",function(a){for(var b,c,d,e,f=a.length;f--;)for(b=a[f].getAll("br"),c=b.length;c--;)d=b[c],e=d.prev,e&&3==e.type&&(e.value=e.value.replace(/\r?\n$/,""))}))}function B(){$.bind(c.getBody(),"mouseup",function(){var a,b=_.getNode();"IMG"==b.nodeName&&((a=$.getStyle(b,"width"))&&($.setAttrib(b,"width",a.replace(/[^0-9%]+/g,"")),$.setStyle(b,"width","")),(a=$.getStyle(b,"height"))&&($.setAttrib(b,"height",a.replace(/[^0-9%]+/g,"")),$.setStyle(b,"height","")))})}function C(){c.on("keydown",function(b){var d,e,f,g,h;if(!n(b)&&b.keyCode==a.BACKSPACE&&(d=_.getRng(),e=d.startContainer,f=d.startOffset,g=$.getRoot(),h=e,d.collapsed&&0===f)){for(;h&&h.parentNode&&h.parentNode.firstChild==h&&h.parentNode!=g;)h=h.parentNode;"BLOCKQUOTE"===h.tagName&&(c.formatter.toggle("blockquote",null,h),d=$.createRng(),d.setStart(e,0),d.setEnd(e,0),_.setRng(d))}})}function D(){function a(){U(),d("StyleWithCSS",!1),d("enableInlineTableEditing",!1),aa.object_resizing||d("enableObjectResizing",!1)}aa.readonly||c.on("BeforeExecCommand MouseDown",a)}function E(){function a(){X($.select("a"),function(a){var b=a.parentNode,c=$.getRoot();if(b.lastChild===a){for(;b&&!$.isBlock(b);){if(b.parentNode.lastChild!==b||b===c)return;b=b.parentNode}$.add(b,"br",{"data-mce-bogus":1})}})}c.on("SetContent ExecCommand",function(b){"setcontent"!=b.type&&"mceInsertLink"!==b.command||a()})}function F(){aa.forced_root_block&&c.on("init",function(){d("DefaultParagraphSeparator",aa.forced_root_block)})}function G(){c.on("keydown",function(a){var b;n(a)||a.keyCode!=Y||(b=c.getDoc().selection.createRange(),b&&b.item&&(a.preventDefault(),c.undoManager.beforeChange(),$.remove(b.item(0)),c.undoManager.add()))})}function H(){var a;m()>=10&&(a="",X("p div h1 h2 h3 h4 h5 h6".split(" "),function(b,c){a+=(c>0?",":"")+b+":empty"}),c.contentStyles.push(a+"{padding-right: 1px !important}"))}function I(){m()<9&&(ba.addNodeFilter("noscript",function(a){for(var b,c,d=a.length;d--;)b=a[d],c=b.firstChild,c&&b.attr("data-mce-innertext",c.value)}),ca.addNodeFilter("noscript",function(a){for(var b,c,d,g=a.length;g--;)b=a[g],c=a[g].firstChild,c?c.value=f.decode(c.value):(d=b.attributes.map["data-mce-innertext"],d&&(b.attr("data-mce-innertext",null),c=new e("#text",3),c.value=d,c.raw=!0,b.append(c)))}))}function J(){function a(a,b){var c=h.createTextRange();try{c.moveToPoint(a,b)}catch(a){c=null}return c}function b(b){var d;b.button?(d=a(b.x,b.y),d&&(d.compareEndPoints("StartToStart",e)>0?d.setEndPoint("StartToStart",e):d.setEndPoint("EndToEnd",e),d.select())):c()}function c(){var a=g.selection.createRange();e&&!a.item&&0===a.compareEndPoints("StartToEnd",a)&&e.select(),$.unbind(g,"mouseup",c),$.unbind(g,"mousemove",b),e=d=0}var d,e,f,g=$.doc,h=g.body;g.documentElement.unselectable=!0,$.bind(g,"mousedown contextmenu",function(h){if("HTML"===h.target.nodeName){if(d&&c(),f=g.documentElement,f.scrollHeight>f.clientHeight)return;d=1,e=a(h.x,h.y),e&&($.bind(g,"mouseup",c),$.bind(g,"mousemove",b),$.getRoot().focus(),e.select())}})}function K(){c.on("keyup focusin mouseup",function(b){65==b.keyCode&&a.metaKeyPressed(b)||_.normalize()},!0)}function L(){c.contentStyles.push("img:-moz-broken {-moz-force-broken-image-icon:1;min-width:24px;min-height:24px}")}function M(){c.inline||c.on("keydown",function(){document.activeElement==document.body&&c.getWin().focus()})}function N(){c.inline||(c.contentStyles.push("body {min-height: 150px}"),c.on("click",function(a){var b;if("HTML"==a.target.nodeName){if(g.ie>11)return void c.getBody().focus();b=c.selection.getRng(),c.getBody().focus(),c.selection.setRng(b),c.selection.normalize(),c.nodeChanged()}}))}function O(){g.mac&&c.on("keydown",function(b){!a.metaKeyPressed(b)||b.shiftKey||37!=b.keyCode&&39!=b.keyCode||(b.preventDefault(),c.selection.getSel().modify("move",37==b.keyCode?"backward":"forward","lineboundary"))})}function P(){d("AutoUrlDetect",!1)}function Q(){c.on("click",function(a){var b=a.target;do if("A"===b.tagName)return void a.preventDefault();while(b=b.parentNode)}),c.contentStyles.push(".mce-content-body {-webkit-touch-callout: none}")}function R(){c.on("init",function(){c.dom.bind(c.getBody(),"submit",function(a){a.preventDefault()})})}function S(){ba.addNodeFilter("br",function(a){for(var b=a.length;b--;)"Apple-interchange-newline"==a[b].attr("class")&&a[b].remove()})}function T(){c.on("dragstart",function(a){o(a)}),c.on("drop",function(a){if(!n(a)){var d=p(a);if(d&&d.id!=c.id){a.preventDefault();var e=b.getCaretRangeFromPoint(a.x,a.y,c.getDoc());_.setRng(e),q(d.html,!0)}}})}function U(){}function V(){var a;return!da||c.removed?0:(a=c.selection.getSel(),!a||!a.rangeCount||0===a.rangeCount)}function W(){function b(a){var b=new l(a.getBody()),c=a.selection.getRng(),d=k.fromRangeStart(c),e=k.fromRangeEnd(c),f=b.prev(d),g=b.next(e);return!a.selection.isCollapsed()&&(!f||f.isAtStart()&&d.isEqual(f))&&(!g||g.isAtEnd()&&d.isEqual(g))}c.on("keypress",function(d){!n(d)&&!_.isCollapsed()&&d.charCode>31&&!a.metaKeyPressed(d)&&b(c)&&(d.preventDefault(),c.setContent(String.fromCharCode(d.charCode)),c.selection.select(c.getBody(),!0),c.selection.collapse(!1),c.nodeChanged())}),c.on("keydown",function(a){var d=a.keyCode;n(a)||d!=Z&&d!=Y||b(c)&&(a.preventDefault(),c.setContent(""),c.nodeChanged())})}var X=h.each,Y=a.BACKSPACE,Z=a.DELETE,$=c.dom,_=c.selection,aa=c.settings,ba=c.parser,ca=c.serializer,da=g.gecko,ea=g.ie,fa=g.webkit,ga="data:text/mce-internal,",ha=ea?"Text":"URL";return C(),r(),g.windowsPhone||K(),fa&&(W(),t(),w(),F(),R(),z(),S(),g.iOS?(M(),N(),Q()):s()),ea&&g.ie<11&&(u(),y(),A(),B(),G(),H(),I(),J()),g.ie>=11&&(N(),z()),g.ie&&(s(),P(),T()),da&&(W(),u(),v(),x(),D(),E(),L(),O(),z()),{refreshContentEditable:U,isHidden:V}}}),g("5r",["22","4t","6c","e","s","o","6d","2b","6e","t","l","i","j","6f","6g","6h","u","5","6i","9"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t){var u=d.DOM,v=function(a){var b=new k(a.settings,a.schema);return b.addAttributeFilter("src,href,style,tabindex",function(b,c){for(var d,e,f,g=b.length,h=a.dom;g--;)if(d=b[g],e=d.attr(c),f="data-mce-"+c,!d.attributes.map[f]){if(0===e.indexOf("data:")||0===e.indexOf("blob:"))continue;"style"===c?(e=h.serializeStyle(h.parseStyle(e),d.name),e.length||(e=null),d.attr(f,e),d.attr(c,e)):"tabindex"===c?(d.attr(f,e),d.attr(c,null)):d.attr(f,a.convertURL(e,c,d.name))}}),b.addNodeFilter("script",function(a){for(var b,c,d=a.length;d--;)b=a[d],c=b.attr("type")||"no/type",0!==c.indexOf("mce-")&&b.attr("type","mce-"+c)}),b.addNodeFilter("#cdata",function(a){for(var b,c=a.length;c--;)b=a[c],b.type=8,b.name="#comment",b.value="[CDATA["+b.value+"]]"}),b.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(b){for(var c,d=b.length,e=a.schema.getNonEmptyElements();d--;)c=b[d],c.isEmpty(e)&&0===c.getAll("br").length&&(c.append(new l("br",1)).shortEnded=!0)}),b},w=function(a){a.settings.auto_focus&&r.setEditorTimeout(a,function(){var b;b=a.settings.auto_focus===!0?a:a.editorManager.get(a.settings.auto_focus),b.destroyed||b.focus()},100)},x=function(a){a.bindPendingEventDelegates(),a.initialized=!0,a.fire("init"),a.focus(!0),a.nodeChanged({initial:!0}),a.execCallback("init_instance_callback",a),w(a)},y=function(a){return a.inline?u.styleSheetLoader:a.dom.styleSheetLoader},z=function(k,l){var r,w,z=k.settings,A=k.getElement(),B=k.getDoc();z.inline||(k.getElement().style.visibility=k.orgVisibility),l||z.content_editable||(B.open(),B.write(k.iframeHTML),B.close()),z.content_editable&&(k.on("remove",function(){var a=this.getBody();u.removeClass(a,"mce-content-body"),u.removeClass(a,"mce-edit-focus"),u.setAttrib(a,"contentEditable",null)}),u.addClass(A,"mce-content-body"),k.contentDocument=B=z.content_document||a,k.contentWindow=z.content_window||b,k.bodyElement=A,z.content_document=z.content_window=null,z.root_name=A.nodeName.toLowerCase()),r=k.getBody(),r.disabled=!0,k.readonly=z.readonly,k.readonly||(k.inline&&"static"===u.getStyle(r,"position",!0)&&(r.style.position="relative"),r.contentEditable=k.getParam("content_editable_state",!0)),r.disabled=!1,k.editorUpload=new g(k),k.schema=new m(z),k.dom=new d(B,{keep_values:!0,url_converter:k.convertURL,url_converter_scope:k,hex_colors:z.force_hex_style_colors,class_filter:z.class_filter,update_styles:!0,root_element:k.inline?k.getBody():null,collect:z.content_editable,schema:k.schema,onSetAttrib:function(a){k.fire("SetAttrib",a)}}),k.parser=v(k),k.serializer=new f(z,k),k.selection=new e(k.dom,k.getWin(),k.serializer,k),k.formatter=new j(k),k.undoManager=new q(k),k._nodeChangeDispatcher=new o(k),k._selectionOverrides=new p(k),c.setup(k),n.setup(k),i.setup(k),k.fire("PreInit"),z.browser_spellcheck||z.gecko_spellcheck||(B.body.spellcheck=!1,u.setAttrib(r,"spellcheck","false")),k.quirks=new s(k),k.fire("PostRender"),z.directionality&&(r.dir=z.directionality),z.nowrap&&(r.style.whiteSpace="nowrap"),z.protect&&k.on("BeforeSetContent",function(a){t.each(z.protect,function(b){a.content=a.content.replace(b,function(a){return"<!--mce:protected "+escape(a)+"-->"})})}),k.on("SetContent",function(){k.addVisual(k.getBody())}),z.padd_empty_editor&&k.on("PostProcess",function(a){a.content=a.content.replace(/^(<p[^>]*>( | |\s|\u00a0|<br \/>|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/,"")}),k.load({initial:!0,format:"html"}),k.startContent=k.getContent({format:"raw"}),k.on("compositionstart compositionend",function(a){k.composing="compositionstart"===a.type}),k.contentStyles.length>0&&(w="",t.each(k.contentStyles,function(a){w+=a+"\r\n"}),k.dom.addStyle(w)),y(k).loadAll(k.contentCSS,function(a){x(k)},function(a){x(k),h.contentCssError(k,a)})};return{initContentBody:z}}),g("4v",["g"],function(a){return a.PluginManager}),g("4w",["g"],function(a){return a.ThemeManager}),g("4u",["22","4t","e","6","5r","4v","4w","9","2a"],function(a,b,c,d,e,f,g,h,i){var j=c.DOM,k=function(a,b,c){var d,e,g=f.get(c);if(d=f.urls[c]||a.documentBaseUrl.replace(/\/$/,""),c=h.trim(c),g&&h.inArray(b,c)===-1){if(h.each(f.dependencies(c),function(c){k(a,b,c)}),a.plugins[c])return;e=new g(a,d,a.$),a.plugins[c]=e,e.init&&(e.init(a,d),b.push(c))}},l=function(a){var b=[];h.each(a.settings.plugins.replace(/\-/g,"").split(/[ ,]/),function(c){k(a,b,c)})},m=function(a){var b,c=a.settings;c.theme&&("function"!=typeof c.theme?(c.theme=c.theme.replace(/-/,""),b=g.get(c.theme),a.theme=new b(a,g.urls[c.theme]),a.theme.init&&a.theme.init(a,g.urls[c.theme]||a.documentBaseUrl.replace(/\/$/,""),a.$)):a.theme=c.theme)},n=function(a){var b,c,d,e,f,g=a.settings,h=a.getElement();return g.render_ui&&a.theme&&(a.orgDisplay=h.style.display,"function"!=typeof g.theme?(b=g.width||j.getStyle(h,"width")||"100%",c=g.height||j.getStyle(h,"height")||h.offsetHeight,d=g.min_height||100,e=/^[0-9\.]+(|px)$/i,e.test(""+b)&&(b=Math.max(parseInt(b,10),100)),e.test(""+c)&&(c=Math.max(parseInt(c,10),d)),f=a.theme.renderUI({targetNode:h,width:b,height:c,deltaWidth:g.delta_width,deltaHeight:g.delta_height}),g.content_editable||(c=(f.iframeHeight||c)+("number"==typeof c?f.deltaHeight||0:""),c<d&&(c=d))):(f=g.theme(a,h),f.editorContainer.nodeType&&(f.editorContainer.id=f.editorContainer.id||a.id+"_parent"),f.iframeContainer.nodeType&&(f.iframeContainer.id=f.iframeContainer.id||a.id+"_iframecontainer"),c=f.iframeHeight||h.offsetHeight),a.editorContainer=f.editorContainer,f.height=c),f},o=function(c,f){if(a.domain!==b.location.hostname&&d.ie&&d.ie<12){var g=i.uuid("mce");c[g]=function(){e.initContentBody(c)};var h='javascript:(function(){document.open();document.domain="'+a.domain+'";var ed = window.parent.tinymce.get("'+c.id+'");document.write(ed.iframeHTML);document.close();ed.'+g+"(true);})()";return j.setAttrib(f,"src",h),!0}return!1},p=function(a,b){var c,e,f=a.settings;a.iframeHTML=f.doctype+"<html><head>",f.document_base_url!=a.documentBaseUrl&&(a.iframeHTML+='<base href="'+a.documentBaseURI.getURI()+'" />'),!d.caretAfter&&f.ie7_compat&&(a.iframeHTML+='<meta http-equiv="X-UA-Compatible" content="IE=7" />'),a.iframeHTML+='<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />',c=f.body_id||"tinymce",c.indexOf("=")!=-1&&(c=a.getParam("body_id","","hash"),c=c[a.id]||c),e=f.body_class||"",e.indexOf("=")!=-1&&(e=a.getParam("body_class","","hash"),e=e[a.id]||""),f.content_security_policy&&(a.iframeHTML+='<meta http-equiv="Content-Security-Policy" content="'+f.content_security_policy+'" />'),a.iframeHTML+='</head><body id="'+c+'" class="mce-content-body '+e+'" data-id="'+a.id+'"><br></body></html>';var g=j.create("iframe",{id:a.id+"_ifr",frameBorder:"0",allowTransparency:"true",title:a.editorManager.translate("Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help"),style:{width:"100%",height:b.height,display:"block"}});g.onload=function(){g.onload=null,a.fire("load")};var h=o(a,g);return a.contentAreaContainer=b.iframeContainer,a.iframeElement=g,j.add(b.iframeContainer,g),h},q=function(a){var b,c=a.settings,d=a.getElement();if(a.rtl=c.rtl_ui||a.editorManager.i18n.rtl,a.editorManager.i18n.setCode(c.language),c.aria_label=c.aria_label||j.getAttrib(d,"aria-label",a.getLang("aria.rich_text_area")),a.fire("ScriptsLoaded"),m(a),l(a),b=n(a),c.content_css&&h.each(h.explode(c.content_css),function(b){a.contentCSS.push(a.documentBaseURI.toAbsolute(b))}),c.content_style&&a.contentStyles.push(c.content_style),c.content_editable)return e.initContentBody(a);var f=p(a,b);b.editorContainer&&(j.get(b.editorContainer).style.display=a.orgDisplay,a.hidden=j.isHidden(b.editorContainer)),a.getElement().style.display="none",j.setAttrib(a.id,"aria-hidden",!0),f||e.initContentBody(a)};return{init:q}}),g("27",["4t","e","7","f","6","2b","4u","11","4v","4w","9","10"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m=b.DOM,n=function(a,b){var c=a.settings,e=d.ScriptLoader;if(c.language&&"en"!=c.language&&!c.language_url&&(c.language_url=a.editorManager.baseURL+"/langs/"+c.language+".js"),c.language_url&&e.add(c.language_url),c.theme&&"function"!=typeof c.theme&&"-"!=c.theme.charAt(0)&&!j.urls[c.theme]){var h=c.theme_url;h=h?a.documentBaseURI.toAbsolute(h):"themes/"+c.theme+"/theme"+b+".js",j.load(c.theme,h)}k.isArray(c.plugins)&&(c.plugins=c.plugins.join(" ")),k.each(c.external_plugins,function(a,b){i.load(b,a),c.plugins+=" "+b}),k.each(c.plugins.split(/[ ,]/),function(a){if(a=k.trim(a),a&&!i.urls[a])if("-"===a.charAt(0)){a=a.substr(1,a.length);var c=i.dependencies(a);k.each(c,function(a){var c={prefix:"plugins/",resource:a,suffix:"/plugin"+b+".js"};a=i.createUrl(c,a),i.load(a.resource,a)})}else i.load(a,{prefix:"plugins/",resource:a,suffix:"/plugin"+b+".js"})}),e.loadQueue(function(){a.removed||g.init(a)},a,function(b){f.pluginLoadError(a,b[0]),a.removed||g.init(a)})},o=function(b){function d(){m.unbind(a,"ready",d),b.render()}var f=b.settings,g=b.id;if(!c.Event.domLoaded)return void m.bind(a,"ready",d);if(b.getElement()&&e.contentEditable){f.inline?b.inline=!0:(b.orgVisibility=b.getElement().style.visibility,b.getElement().style.visibility="hidden");var i=b.getElement().form||m.getParent(g,"form");i&&(b.formElement=i,f.hidden_input&&!/TEXTAREA|INPUT/i.test(b.getElement().nodeName)&&(m.insertAfter(m.create("input",{type:"hidden",name:g}),g),b.hasHiddenInput=!0),b.formEventDelegate=function(a){b.fire(a.type,a)},m.bind(i,"submit reset",b.formEventDelegate),b.on("reset",function(){b.setContent(b.startContent,{format:"raw"})}),!f.submit_patch||i.submit.nodeType||i.submit.length||i._mceOldSubmit||(i._mceOldSubmit=i.submit,i.submit=function(){return b.editorManager.triggerSave(),b.setDirty(!1),i._mceOldSubmit(i)})),b.windowManager=new l(b),b.notificationManager=new h(b),"xml"===f.encoding&&b.on("GetContent",function(a){a.save&&(a.content=m.encode(a.content))}),f.add_form_submit_trigger&&b.on("submit",function(){b.initialized&&b.save()}),f.add_unload_trigger&&(b._beforeUnload=function(){!b.initialized||b.destroyed||b.isHidden()||b.save({format:"raw",no_events:!0,set_dirty:!1})},b.editorManager.on("BeforeUnload",b._beforeUnload)),b.editorManager.add(b),n(b,b.suffix)}};return{render:o}}),g("28",[],function(){function a(a,b,c){try{a.getDoc().execCommand(b,!1,c)}catch(a){}}function b(a){var b,c;return b=a.getBody(),c=function(b){a.dom.getParents(b.target,"a").length>0&&b.preventDefault()},a.dom.bind(b,"click",c),{unbind:function(){a.dom.unbind(b,"click",c)}}}function c(c,d){c._clickBlocker&&(c._clickBlocker.unbind(),c._clickBlocker=null),d?(c._clickBlocker=b(c),c.selection.controlSelection.hideResizeRect(),c.readonly=!0,c.getBody().contentEditable=!1):(c.readonly=!1,c.getBody().contentEditable=!0,a(c,"StyleWithCSS",!1),a(c,"enableInlineTableEditing",!1),a(c,"enableObjectResizing",!1),c.focus(),c.nodeChanged())}function d(a,b){var d=a.readonly?"readonly":"design";b!=d&&(a.initialized?c(a,"readonly"==b):a.on("init",function(){c(a,"readonly"==b)}),a.fire("SwitchMode",{mode:b}))}return{setMode:d}}),g("29",[],function(){var a=function(a,b,c){var d=a.sidebars?a.sidebars:[];d.push({name:b,settings:c}),a.sidebars=d};return{add:a}}),g("14",["g","a","e","v","12","26","6","n","27","28","13","29","9","w","2a"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){function p(c,e,h){var i,j,l=this;i=l.documentBaseUrl=h.documentBaseURL,j=h.baseURI,e=f.getEditorSettings(l,c,i,h.defaultSettings,e),l.settings=e,a.language=e.language||"en",a.languageLoad=e.language_load,a.baseURL=h.baseURL,l.id=c,l.setDirty(!1),l.plugins={},l.documentBaseURI=new n(e.document_base_url,{base_uri:j}),l.baseURI=j,l.contentCSS=[],l.contentStyles=[],l.shortcuts=new k(l),l.loadedCSS={},l.editorCommands=new d(l),l.suffix=h.suffix,l.editorManager=h,l.inline=e.inline,e.cache_suffix&&(g.cacheSuffix=e.cache_suffix.replace(/^[\?\&]+/,"")),e.override_viewport===!1&&(g.overrideViewPort=!1),h.fire("SetupEditor",l),l.execCallback("setup",l),l.$=b.overrideDefaults(function(){return{context:l.inline?l.getBody():l.getDoc(),element:l.getBody()}})}var q=c.DOM,r=m.extend,s=m.each,t=m.trim,u=m.resolve,v=g.gecko,w=g.ie;return p.prototype={render:function(){i.render(this)},focus:function(a){function b(a){return f.dom.getParent(a,function(a){return"true"===f.dom.getContentEditable(a)})}var c,d,e,f=this,h=f.selection,i=f.settings.content_editable,j=f.getDoc(),k=f.getBody();if(!f.removed){if(!a){if(c=h.getRng(),c.item&&(d=c.item(0)),f.quirks.refreshContentEditable(),e=b(h.getNode()),f.$.contains(k,e))return e.focus(),h.normalize(),void f.editorManager.setActive(f);if(i||(g.opera||f.getBody().focus(),f.getWin().focus()),v||i){if(k.setActive)try{k.setActive()}catch(a){k.focus()}else f.selection.setRng(f.lastRng),k.focus();i&&h.normalize()}d&&d.ownerDocument==j&&(c=j.body.createControlRange(),c.addElement(d),c.select())}f.editorManager.setActive(f)}},execCallback:function(a){var b,c=this,d=c.settings[a];if(d)return c.callbackLookup&&(b=c.callbackLookup[a])&&(d=b.func,b=b.scope),"string"==typeof d&&(b=d.replace(/\.\w+$/,""),b=b?u(b):0,d=u(d),c.callbackLookup=c.callbackLookup||{},c.callbackLookup[a]={func:d,scope:b}),d.apply(b||c,Array.prototype.slice.call(arguments,1))},translate:function(a){if(a&&m.is(a,"string")){var b=this.settings.language||"en",c=this.editorManager.i18n;a=c.data[b+"."+a]||a.replace(/\{\#([^\}]+)\}/g,function(a,d){return c.data[b+"."+d]||"{#"+d+"}"})}return this.editorManager.translate(a)},getLang:function(a,b){return this.editorManager.i18n.data[(this.settings.language||"en")+"."+a]||(void 0!==b?b:"{#"+a+"}")},getParam:function(a,b,c){var d,e=a in this.settings?this.settings[a]:b;return"hash"===c?(d={},"string"==typeof e?s(e.indexOf("=")>0?e.split(/[;,](?![^=;,]*(?:[;,]|$))/):e.split(","),function(a){a=a.split("="),a.length>1?d[t(a[0])]=t(a[1]):d[t(a[0])]=t(a)}):d=e,d):e},nodeChanged:function(a){this._nodeChangeDispatcher.nodeChanged(a)},addButton:function(a,b){var c=this;b.cmd&&(b.onclick=function(){c.execCommand(b.cmd)}),b.text||b.icon||(b.icon=a),c.buttons=c.buttons||{},b.tooltip=b.tooltip||b.title,c.buttons[a]=b},addSidebar:function(a,b){return l.add(this,a,b)},addMenuItem:function(a,b){var c=this;b.cmd&&(b.onclick=function(){c.execCommand(b.cmd)}),c.menuItems=c.menuItems||{},c.menuItems[a]=b},addContextToolbar:function(a,b){var c,d=this;d.contextToolbars=d.contextToolbars||[],"string"==typeof a&&(c=a,a=function(a){return d.dom.is(a,c)}),d.contextToolbars.push({id:o.uuid("mcet"),predicate:a,items:b})},addCommand:function(a,b,c){this.editorCommands.addCommand(a,b,c)},addQueryStateHandler:function(a,b,c){this.editorCommands.addQueryStateHandler(a,b,c)},addQueryValueHandler:function(a,b,c){this.editorCommands.addQueryValueHandler(a,b,c)},addShortcut:function(a,b,c,d){this.shortcuts.add(a,b,c,d)},execCommand:function(a,b,c,d){return this.editorCommands.execCommand(a,b,c,d)},queryCommandState:function(a){return this.editorCommands.queryCommandState(a)},queryCommandValue:function(a){return this.editorCommands.queryCommandValue(a)},queryCommandSupported:function(a){return this.editorCommands.queryCommandSupported(a)},show:function(){var a=this;a.hidden&&(a.hidden=!1,a.inline?a.getBody().contentEditable=!0:(q.show(a.getContainer()),q.hide(a.id)),a.load(),a.fire("show"))},hide:function(){var a=this,b=a.getDoc();a.hidden||(w&&b&&!a.inline&&b.execCommand("SelectAll"),a.save(),a.inline?(a.getBody().contentEditable=!1,a==a.editorManager.focusedEditor&&(a.editorManager.focusedEditor=null)):(q.hide(a.getContainer()),q.setStyle(a.id,"display",a.orgDisplay)),a.hidden=!0,a.fire("hide"))},isHidden:function(){return!!this.hidden},setProgressState:function(a,b){this.fire("ProgressState",{state:a,time:b})},load:function(a){var b,c=this,d=c.getElement();return c.removed?"":d?(a=a||{},a.load=!0,b=c.setContent(void 0!==d.value?d.value:d.innerHTML,a),a.element=d,a.no_events||c.fire("LoadContent",a),a.element=d=null,b):void 0},save:function(a){var b,c,d=this,e=d.getElement();if(e&&d.initialized&&!d.removed)return a=a||{},a.save=!0,a.element=e,b=a.content=d.getContent(a),a.no_events||d.fire("SaveContent",a),"raw"==a.format&&d.fire("RawSaveContent",a),b=a.content,/TEXTAREA|INPUT/i.test(e.nodeName)?e.value=b:(d.inline||(e.innerHTML=b),(c=q.getParent(d.id,"form"))&&s(c.elements,function(a){if(a.name==d.id)return a.value=b,!1})),a.element=e=null,a.set_dirty!==!1&&d.setDirty(!1),b},setContent:function(a,b){var c,d,e=this,f=e.getBody();return b=b||{},b.format=b.format||"html",b.set=!0,b.content=a,b.no_events||e.fire("BeforeSetContent",b),a=b.content,0===a.length||/^\s+$/.test(a)?(d=w&&w<11?"":'<br data-mce-bogus="1">',"TABLE"==f.nodeName?a="<tr><td>"+d+"</td></tr>":/^(UL|OL)$/.test(f.nodeName)&&(a="<li>"+d+"</li>"),c=e.settings.forced_root_block,c&&e.schema.isValidChild(f.nodeName.toLowerCase(),c.toLowerCase())?(a=d,a=e.dom.createHTML(c,e.settings.forced_root_block_attrs,a)):w||a||(a='<br data-mce-bogus="1">'),e.dom.setHTML(f,a),e.fire("SetContent",b)):("raw"!==b.format&&(a=new h({validate:e.validate},e.schema).serialize(e.parser.parse(a,{isRootContent:!0}))),b.content=t(a),e.dom.setHTML(f,b.content),b.no_events||e.fire("SetContent",b)),b.content},getContent:function(a){var b,c=this,d=c.getBody();return c.removed?"":(a=a||{},a.format=a.format||"html",a.get=!0,a.getInner=!0,a.no_events||c.fire("BeforeGetContent",a),b="raw"==a.format?m.trim(c.serializer.getTrimmedContent()):"text"==a.format?d.innerText||d.textContent:c.serializer.serialize(d,a),"text"!=a.format?a.content=t(b):a.content=b,a.no_events||c.fire("GetContent",a),a.content)},insertContent:function(a,b){b&&(a=r({content:a},b)),this.execCommand("mceInsertContent",!1,a)},isDirty:function(){return!this.isNotDirty},setDirty:function(a){var b=!this.isNotDirty;this.isNotDirty=!a,a&&a!=b&&this.fire("dirty")},setMode:function(a){j.setMode(this,a)},getContainer:function(){var a=this;return a.container||(a.container=q.get(a.editorContainer||a.id+"_parent")),a.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return this.targetElm||(this.targetElm=q.get(this.id)),this.targetElm},getWin:function(){var a,b=this;return b.contentWindow||(a=b.iframeElement,a&&(b.contentWindow=a.contentWindow)),b.contentWindow},getDoc:function(){var a,b=this;return b.contentDocument||(a=b.getWin(),a&&(b.contentDocument=a.document)),b.contentDocument},getBody:function(){var a=this.getDoc();return this.bodyElement||(a?a.body:null)},convertURL:function(a,b,c){var d=this,e=d.settings;return e.urlconverter_callback?d.execCallback("urlconverter_callback",a,c,!0,b):!e.convert_urls||c&&"LINK"==c.nodeName||0===a.indexOf("file:")||0===a.length?a:e.relative_urls?d.documentBaseURI.toRelative(a):a=d.documentBaseURI.toAbsolute(a,e.remove_script_host)},addVisual:function(a){var b,c=this,d=c.settings,e=c.dom;a=a||c.getBody(),void 0===c.hasVisual&&(c.hasVisual=d.visual),s(e.select("table,a",a),function(a){var f;switch(a.nodeName){case"TABLE":return b=d.visual_table_class||"mce-item-table",f=e.getAttrib(a,"border"),void(f&&"0"!=f||!c.hasVisual?e.removeClass(a,b):e.addClass(a,b));case"A":return void(e.getAttrib(a,"href",!1)||(f=e.getAttrib(a,"name")||a.id,b=d.visual_anchor_class||"mce-item-anchor",f&&c.hasVisual?e.addClass(a,b):e.removeClass(a,b)))}}),c.fire("VisualAid",{element:a,hasVisual:c.hasVisual})},remove:function(){var a=this;a.removed||(a.save(),a.removed=1,a.unbindAllNativeEvents(),a.hasHiddenInput&&q.remove(a.getElement().nextSibling),a.inline||(w&&w<10&&a.getDoc().execCommand("SelectAll",!1,null),q.setStyle(a.id,"display",a.orgDisplay),a.getBody().onload=null),a.fire("remove"),a.editorManager.remove(a),q.remove(a.getContainer()),a._selectionOverrides.destroy(),a.editorUpload.destroy(),a.destroy())},destroy:function(a){var b,c=this;if(!c.destroyed){if(!a&&!c.removed)return void c.remove();a||(c.editorManager.off("beforeunload",c._beforeUnload),c.theme&&c.theme.destroy&&c.theme.destroy(),c.selection.destroy(),c.dom.destroy()),b=c.formElement,b&&(b._mceOldSubmit&&(b.submit=b._mceOldSubmit,b._mceOldSubmit=null),q.unbind(b,"submit reset",c.formEventDelegate)),c.contentAreaContainer=c.formElement=c.container=c.editorContainer=null,c.bodyElement=c.contentDocument=c.contentWindow=null,c.iframeElement=c.targetElm=null,c.selection&&(c.selection=c.selection.win=c.selection.dom=c.selection.dom.doc=null), -c.destroyed=1}},uploadImages:function(a){return this.editorUpload.uploadImages(a)},_scanForImages:function(){return this.editorUpload.scanForImages()}},r(p.prototype,e),p}),g("15",["9"],function(a){"use strict";var b={},c="en";return{setCode:function(a){a&&(c=a,this.rtl=!!this.data[a]&&"rtl"===this.data[a]._dir)},getCode:function(){return c},rtl:!1,add:function(a,c){var d=b[a];d||(b[a]=d={});for(var e in c)d[e]=c[e];this.setCode(a)},translate:function(d){function e(b){return a.is(b,"function")?Object.prototype.toString.call(b):f(b)?"":""+b}function f(b){return""===b||null===b||a.is(b,"undefined")}function g(b){return b=e(b),a.hasOwn(h,b)?e(h[b]):b}var h=b[c]||{};if(f(d))return"";if(a.is(d,"object")&&a.hasOwn(d,"raw"))return e(d.raw);if(a.is(d,"array")){var i=d.slice(1);d=g(d[0]).replace(/\{([0-9]+)\}/g,function(b,c){return a.hasOwn(i,c)?e(i[c]):b})}return g(d).replace(/{context:\w+}$/,"")},data:b}}),g("16",["e","5","6"],function(a,b,c){function d(a){function d(){try{return document.activeElement}catch(a){return document.body}}function j(a,b){if(b&&b.startContainer){if(!a.isChildOf(b.startContainer,a.getRoot())||!a.isChildOf(b.endContainer,a.getRoot()))return;return{startContainer:b.startContainer,startOffset:b.startOffset,endContainer:b.endContainer,endOffset:b.endOffset}}return b}function l(a,b){var c;return b.startContainer?(c=a.getDoc().createRange(),c.setStart(b.startContainer,b.startOffset),c.setEnd(b.endContainer,b.endOffset)):c=b,c}function m(m){var n=m.editor;n.on("init",function(){(n.inline||c.ie)&&("onbeforedeactivate"in document&&c.ie<9?n.dom.bind(n.getBody(),"beforedeactivate",function(a){if(a.target==n.getBody())try{n.lastRng=n.selection.getRng()}catch(a){}}):n.on("nodechange mouseup keyup",function(a){var b=d();"nodechange"==a.type&&a.selectionChange||(b&&b.id==n.id+"_ifr"&&(b=n.getBody()),n.dom.isChildOf(b,n.getBody())&&(n.lastRng=n.selection.getRng()))}),c.webkit&&!e&&(e=function(){var b=a.activeEditor;if(b&&b.selection){var c=b.selection.getRng();c&&!c.collapsed&&(n.lastRng=c)}},h.bind(document,"selectionchange",e)))}),n.on("setcontent",function(){n.lastRng=null}),n.on("mousedown",function(){n.selection.lastFocusBookmark=null}),n.on("focusin",function(){var b,c=a.focusedEditor;n.selection.lastFocusBookmark&&(b=l(n,n.selection.lastFocusBookmark),n.selection.lastFocusBookmark=null,n.selection.setRng(b)),c!=n&&(c&&c.fire("blur",{focusedEditor:n}),a.setActive(n),a.focusedEditor=n,n.fire("focus",{blurredEditor:c}),n.focus(!0)),n.lastRng=null}),n.on("focusout",function(){b.setEditorTimeout(n,function(){var b=a.focusedEditor;i(n,d())||b!=n||(n.fire("blur",{focusedEditor:null}),a.focusedEditor=null,n.selection&&(n.selection.lastFocusBookmark=null))})}),f||(f=function(b){var c,d=a.activeEditor;c=b.target,d&&c.ownerDocument===document&&(d.selection&&c!==d.getBody()&&k(n,c)&&(d.selection.lastFocusBookmark=j(d.dom,d.lastRng)),c===document.body||i(d,c)||a.focusedEditor!==d||(d.fire("blur",{focusedEditor:null}),a.focusedEditor=null))},h.bind(document,"focusin",f)),n.inline&&!g&&(g=function(b){var c=a.activeEditor,d=c.dom;if(c.inline&&d&&!d.isChildOf(b.target,c.getBody())){var e=c.selection.getRng();e.collapsed||(c.lastRng=e)}},h.bind(document,"mouseup",g))}function n(b){a.focusedEditor==b.editor&&(a.focusedEditor=null),a.activeEditor||(h.unbind(document,"selectionchange",e),h.unbind(document,"focusin",f),h.unbind(document,"mouseup",g),e=f=g=null)}a.on("AddEditor",m),a.on("RemoveEditor",n)}var e,f,g,h=a.DOM,i=function(a,b){var c=a?a.settings.custom_ui_selector:"",e=h.getParent(b,function(b){return d.isEditorUIElement(b)||!!c&&a.dom.is(b,c)});return null!==e},j=function(a){return a.inline===!0},k=function(a,b){return j(a)===!1||a.dom.isChildOf(b,a.getBody())===!1};return d.isEditorUIElement=function(a){return a.className.toString().indexOf("mce-")!==-1},d._isUIElement=i,d}),g("2c",["9"],function(a){var b=a.each,c=a.explode,d=function(a){a.on("AddEditor",function(a){var d=a.editor;d.on("preInit",function(){function a(a,c){b(c,function(b,c){b&&h.setStyle(a,c,b)}),h.rename(a,"span")}function e(a){h=d.dom,i.convert_fonts_to_spans&&b(h.select("font,u,strike",a.node),function(a){f[a.nodeName.toLowerCase()](h,a)})}var f,g,h,i=d.settings;i.inline_styles&&(g=c(i.font_size_legacy_values),f={font:function(b,c){a(c,{backgroundColor:c.style.backgroundColor,color:c.color,fontFamily:c.face,fontSize:g[parseInt(c.size,10)-1]})},u:function(b,c){"html4"===d.settings.schema&&a(c,{textDecoration:"underline"})},strike:function(b,c){a(c,{textDecoration:"line-through"})}},d.on("PreProcess SetContent",e))})})};return{register:d}}),g("17",["g","a","e","14","6","2b","16","2c","15","z","4","9","w"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){function n(a){v(s.editors,function(b){"scroll"===a.type?b.fire("ScrollWindow",a):b.fire("ResizeWindow",a)})}function o(a,c){c!==y&&(c?b(window).on("resize scroll",n):b(window).off("resize scroll",n),y=c)}function p(a){var b,c=s.editors;delete c[a.id];for(var d=0;d<c.length;d++)if(c[d]==a){c.splice(d,1),b=!0;break}return s.activeEditor==a&&(s.activeEditor=c[0]),s.focusedEditor==a&&(s.focusedEditor=null),b}function q(a){return a&&a.initialized&&!(a.getContainer()||a.getBody()).parentNode&&(p(a),a.unbindAllNativeEvents(),a.destroy(!0),a.removed=!0,a=null),a}var r,s,t=c.DOM,u=l.explode,v=l.each,w=l.extend,x=0,y=!1;return s={$:b,majorVersion:"4",minorVersion:"6.4",releaseDate:"2017-06-13",editors:[],i18n:i,activeEditor:null,setup:function(){var a,b,c,d,e=this,f="";if(b=m.getDocumentBaseUrl(document.location),/^[^:]+:\/\/\/?[^\/]+\//.test(b)&&(b=b.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,""),/[\/\\]$/.test(b)||(b+="/")),c=window.tinymce||window.tinyMCEPreInit)a=c.base||c.baseURL,f=c.suffix;else{for(var h=document.getElementsByTagName("script"),i=0;i<h.length;i++){d=h[i].src;var j=d.substring(d.lastIndexOf("/"));if(/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(d)){j.indexOf(".min")!=-1&&(f=".min"),a=d.substring(0,d.lastIndexOf("/"));break}}!a&&document.currentScript&&(d=document.currentScript.src,d.indexOf(".min")!=-1&&(f=".min"),a=d.substring(0,d.lastIndexOf("/")))}e.baseURL=new m(b).toAbsolute(a),e.documentBaseURL=b,e.baseURI=new m(e.baseURL),e.suffix=f,e.focusManager=new g(e)},overrideDefaults:function(b){var c,d;c=b.base_url,c&&(this.baseURL=new m(this.documentBaseURL).toAbsolute(c.replace(/\/+$/,"")),this.baseURI=new m(this.baseURL)),d=b.suffix,b.suffix&&(this.suffix=d),this.defaultSettings=b;var e=b.plugin_base_urls;for(var f in e)a.PluginManager.urls[f]=e[f]},init:function(a){function c(a,b){return a.inline&&b.tagName.toLowerCase()in o}function g(a){var b=a.id;return b||(b=a.name,b=b&&!t.get(b)?a.name:t.uniqueId(),a.setAttribute("id",b)),b}function h(b){var c=a[b];if(c)return c.apply(p,Array.prototype.slice.call(arguments,2))}function i(a,b){return b.constructor===RegExp?b.test(a.className):t.hasClass(a,b)}function j(a){var b,c=[];if(e.ie&&e.ie<11)return f.initError("TinyMCE does not support the browser you are using. For a list of supported browsers please see: https://www.tinymce.com/docs/get-started/system-requirements/"),[];if(a.types)return v(a.types,function(a){c=c.concat(t.select(a.selector))}),c;if(a.selector)return t.select(a.selector);if(a.target)return[a.target];switch(a.mode){case"exact":b=a.elements||"",b.length>0&&v(u(b),function(a){var b;(b=t.get(a))?c.push(b):v(document.forms,function(b){v(b.elements,function(b){b.name===a&&(a="mce_editor_"+x++,t.setAttrib(b,"id",a),c.push(b))})})});break;case"textareas":case"specific_textareas":v(t.select("textarea"),function(b){a.editor_deselector&&i(b,a.editor_deselector)||a.editor_selector&&!i(b,a.editor_selector)||c.push(b)})}return c}function m(){function e(a,b,c){var e=new d(a,b,p);n.push(e),e.on("init",function(){++k===i.length&&r(n)}),e.targetElm=e.targetElm||c,e.render()}var i,k=0,n=[];return t.unbind(window,"ready",m),h("onpageload"),i=b.unique(j(a)),a.types?void v(a.types,function(b){l.each(i,function(c){return!t.is(c,b.selector)||(e(g(c),w({},a,b),c),!1)})}):(l.each(i,function(a){q(p.get(a.id))}),i=l.grep(i,function(a){return!p.get(a.id)}),void(0===i.length?r([]):v(i,function(b){c(a,b)?f.initError("Could not initialize inline editor on invalid inline target element",b):e(g(b),a,b)})))}var n,o,p=this;o=l.makeMap("area base basefont br col frame hr img input isindex link meta param embed source wbr track colgroup option tbody tfoot thead tr script noscript style textarea video audio iframe object menu"," ");var r=function(a){n=a};return p.settings=a,t.bind(window,"ready",m),new k(function(a){n?a(n):r=function(b){a(b)}})},get:function(a){return arguments.length?a in this.editors?this.editors[a]:null:this.editors},add:function(a){var b=this,c=b.editors;return c[a.id]=a,c.push(a),o(c,!0),b.activeEditor=a,b.fire("AddEditor",{editor:a}),r||(r=function(){b.fire("BeforeUnload")},t.bind(window,"beforeunload",r)),a},createEditor:function(a,b){return this.add(new d(a,b,this))},remove:function(a){var b,c,d=this,e=d.editors;{if(a)return"string"==typeof a?(a=a.selector||a,void v(t.select(a),function(a){c=e[a.id],c&&d.remove(c)})):(c=a,e[c.id]?(p(c)&&d.fire("RemoveEditor",{editor:c}),e.length||t.unbind(window,"beforeunload",r),c.remove(),o(e,e.length>0),c):null);for(b=e.length-1;b>=0;b--)d.remove(e[b])}},execCommand:function(a,b,c){var e=this,f=e.get(c);switch(a){case"mceAddEditor":return e.get(c)||new d(c,e.settings,e).render(),!0;case"mceRemoveEditor":return f&&f.remove(),!0;case"mceToggleEditor":return f?(f.isHidden()?f.show():f.hide(),!0):(e.execCommand("mceAddEditor",0,c),!0)}return!!e.activeEditor&&e.activeEditor.execCommand(a,b,c)},triggerSave:function(){v(this.editors,function(a){a.save()})},addI18n:function(a,b){i.add(a,b)},translate:function(a){return i.translate(a)},setActive:function(a){var b=this.activeEditor;this.activeEditor!=a&&(b&&b.fire("deactivate",{relatedTarget:a}),a.fire("activate",{relatedTarget:b})),this.activeEditor=a}},w(s,j),s.setup(),h.register(s),s}),g("18",["z","9"],function(a,b){var c={send:function(a){function d(){!a.async||4==e.readyState||f++>1e4?(a.success&&f<1e4&&200==e.status?a.success.call(a.success_scope,""+e.responseText,e,a):a.error&&a.error.call(a.error_scope,f>1e4?"TIMED_OUT":"GENERAL",e,a),e=null):setTimeout(d,10)}var e,f=0;if(a.scope=a.scope||this,a.success_scope=a.success_scope||a.scope,a.error_scope=a.error_scope||a.scope,a.async=a.async!==!1,a.data=a.data||"",c.fire("beforeInitialize",{settings:a}),e=new XMLHttpRequest){if(e.overrideMimeType&&e.overrideMimeType(a.content_type),e.open(a.type||(a.data?"POST":"GET"),a.url,a.async),a.crossDomain&&(e.withCredentials=!0),a.content_type&&e.setRequestHeader("Content-Type",a.content_type),a.requestheaders&&b.each(a.requestheaders,function(a){e.setRequestHeader(a.key,a.value)}),e.setRequestHeader("X-Requested-With","XMLHttpRequest"),e=c.fire("beforeSend",{xhr:e,settings:a}).xhr,e.send(a.data),!a.async)return d();setTimeout(d,10)}}};return b.extend(c,a),c}),g("19",[],function(){function a(b,c){var d,e,f,g;if(c=c||'"',null===b)return"null";if(f=typeof b,"string"==f)return e="\bb\tt\nn\ff\rr\"\"''\\\\",c+b.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(a,b){return'"'===c&&"'"===a?a:(d=e.indexOf(b),d+1?"\\"+e.charAt(d+1):(a=b.charCodeAt().toString(16),"\\u"+"0000".substring(a.length)+a))})+c;if("object"==f){if(b.hasOwnProperty&&"[object Array]"===Object.prototype.toString.call(b)){for(d=0,e="[";d<b.length;d++)e+=(d>0?",":"")+a(b[d],c);return e+"]"}e="{";for(g in b)b.hasOwnProperty(g)&&(e+="function"!=typeof b[g]?(e.length>1?","+c:c)+g+c+":"+a(b[g],c):"");return e+"}"}return""+b}return{serialize:a,parse:function(a){try{return window[String.fromCharCode(101)+"val"]("("+a+")")}catch(a){}}}}),g("1a",["19","18","9"],function(a,b,c){function d(a){this.settings=e({},a),this.count=0}var e=c.extend;return d.sendRPC=function(a){return(new d).send(a)},d.prototype={send:function(c){var d=c.error,f=c.success;c=e(this.settings,c),c.success=function(b,e){b=a.parse(b),"undefined"==typeof b&&(b={error:"JSON Parse error."}),b.error?d.call(c.error_scope||c.scope,b.error,e):f.call(c.success_scope||c.scope,b.result)},c.error=function(a,b){d&&d.call(c.error_scope||c.scope,a,b)},c.data=a.serialize({id:c.id||"c"+this.count++,method:c.method,params:c.params}),c.content_type="application/json",b.send(c)}},d}),g("1b",["e"],function(a){return{callbacks:{},count:0,send:function(b){var c=this,d=a.DOM,e=void 0!==b.count?b.count:c.count,f="tinymce_jsonp_"+e;c.callbacks[e]=function(a){d.remove(f),delete c.callbacks[e],b.callback(a)},d.add(d.doc.body,"script",{id:f,src:b.url,type:"text/javascript"}),c.count++}}}),g("1c",[],function(){function a(){g=[];for(var a in f)g.push(a);d.length=g.length}function b(){function b(a){var b,c;return c=void 0!==a?j+a:d.indexOf(",",j),c===-1||c>d.length?null:(b=d.substring(j,c),j=c+1,b)}var c,d,g,j=0;if(f={},i){e.load(h),d=e.getAttribute(h)||"";do{var k=b();if(null===k)break;if(c=b(parseInt(k,32)||0),null!==c){if(k=b(),null===k)break;g=b(parseInt(k,32)||0),c&&(f[c]=g)}}while(null!==c);a()}}function c(){var b,c="";if(i){for(var d in f)b=f[d],c+=(c?",":"")+d.length.toString(32)+","+d+","+b.length.toString(32)+","+b;e.setAttribute(h,c);try{e.save(h)}catch(a){}a()}}var d,e,f,g,h,i;try{if(window.localStorage)return localStorage}catch(a){}return h="tinymce",e=document.documentElement,i=!!e.addBehavior,i&&e.addBehavior("#default#userData"),d={key:function(a){return g[a]},getItem:function(a){return a in f?f[a]:null},setItem:function(a,b){f[a]=""+b,c()},removeItem:function(a){delete f[a],c()},clear:function(){f={},c()}},b(),d}),g("1d",["e","7","f","g","9","6"],function(a,b,c,d,e,f){var g=function(g){g.DOM=a.DOM,g.ScriptLoader=c.ScriptLoader,g.PluginManager=d.PluginManager,g.ThemeManager=d.ThemeManager,g.dom=g.dom||{},g.dom.Event=b.Event,e.each("trim isArray is toArray makeMap each map grep inArray extend create walk createNS resolve explode _addCacheSuffix".split(" "),function(a){g[a]=e[a]}),e.each("isOpera isWebKit isIE isGecko isMac".split(" "),function(a){g[a]=f[a.substr(2).toLowerCase()]})};return{register:g}}),g("1e",[],function(){function a(a){function e(a,e,f){var g,h,i,j,k,l;return g=0,h=0,i=0,a/=255,e/=255,f/=255,k=b(a,b(e,f)),l=c(a,c(e,f)),k==l?(i=k,{h:0,s:0,v:100*i}):(j=a==k?e-f:f==k?a-e:f-a,g=a==k?3:f==k?1:5,g=60*(g-j/(l-k)),h=(l-k)/l,i=l,{h:d(g),s:d(100*h),v:d(100*i)})}function f(a,e,f){var g,h,i,j;if(a=(parseInt(a,10)||0)%360,e=parseInt(e,10)/100,f=parseInt(f,10)/100,e=c(0,b(e,1)),f=c(0,b(f,1)),0===e)return void(l=m=n=d(255*f));switch(g=a/60,h=f*e,i=h*(1-Math.abs(g%2-1)),j=f-h,Math.floor(g)){case 0:l=h,m=i,n=0;break;case 1:l=i,m=h,n=0;break;case 2:l=0,m=h,n=i;break;case 3:l=0,m=i,n=h;break;case 4:l=i,m=0,n=h;break;case 5:l=h,m=0,n=i;break;default:l=m=n=0}l=d(255*(l+j)),m=d(255*(m+j)),n=d(255*(n+j))}function g(){function a(a){return a=parseInt(a,10).toString(16),a.length>1?a:"0"+a}return"#"+a(l)+a(m)+a(n)}function h(){return{r:l,g:m,b:n}}function i(){return e(l,m,n)}function j(a){var b;return"object"==typeof a?"r"in a?(l=a.r,m=a.g,n=a.b):"v"in a&&f(a.h,a.s,a.v):(b=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)[^\)]*\)/gi.exec(a))?(l=parseInt(b[1],10),m=parseInt(b[2],10),n=parseInt(b[3],10)):(b=/#([0-F]{2})([0-F]{2})([0-F]{2})/gi.exec(a))?(l=parseInt(b[1],16),m=parseInt(b[2],16),n=parseInt(b[3],16)):(b=/#([0-F])([0-F])([0-F])/gi.exec(a))&&(l=parseInt(b[1]+b[1],16),m=parseInt(b[2]+b[2],16),n=parseInt(b[3]+b[3],16)),l=l<0?0:l>255?255:l,m=m<0?0:m>255?255:m,n=n<0?0:n>255?255:n,k}var k=this,l=0,m=0,n=0;a&&j(a),k.toRgb=h,k.toHsv=i,k.toHex=g,k.parse=j}var b=Math.min,c=Math.max,d=Math.round;return a}),g("2t",["x","9"],function(a,b){"use strict";return a.extend({Defaults:{firstControlClass:"first",lastControlClass:"last"},init:function(a){this.settings=b.extend({},this.Defaults,a)},preRender:function(a){a.bodyClasses.add(this.settings.containerClass)},applyClasses:function(a){var b,c,d,e,f=this,g=f.settings;b=g.firstControlClass,c=g.lastControlClass,a.each(function(a){a.classes.remove(b).remove(c).add(g.controlClass),a.visible()&&(d||(d=a),e=a)}),d&&d.classes.add(b),e&&e.classes.add(c)},renderHtml:function(a){var b=this,c="";return b.applyClasses(a.items()),a.items().each(function(a){c+=a.renderHtml()}),c},recalc:function(){},postRender:function(){},isNative:function(){return!1}})}),g("2u",["2t"],function(a){"use strict";return a.extend({Defaults:{containerClass:"abs-layout",controlClass:"abs-layout-item"},recalc:function(a){a.items().filter(":visible").each(function(a){var b=a.settings;a.layoutRect({x:b.x,y:b.y,w:b.w,h:b.h}),a.recalc&&a.recalc()})},renderHtml:function(a){return'<div id="'+a._id+'-absend" class="'+a.classPrefix+'abs-end"></div>'+this._super(a)}})}),g("2v",["2r"],function(a){"use strict";return a.extend({Defaults:{classes:"widget btn",role:"button"},init:function(a){var b,c=this;c._super(a),a=c.settings,b=c.settings.size,c.on("click mousedown",function(a){a.preventDefault()}),c.on("touchstart",function(a){c.fire("click",a),a.preventDefault()}),a.subtype&&c.classes.add(a.subtype),b&&c.classes.add("btn-"+b),a.icon&&c.icon(a.icon)},icon:function(a){return arguments.length?(this.state.set("icon",a),this):this.state.get("icon")},repaint:function(){var a,b=this.getEl().firstChild;b&&(a=b.style,a.width=a.height="100%"),this._super()},renderHtml:function(){var a,b=this,c=b._id,d=b.classPrefix,e=b.state.get("icon"),f=b.state.get("text"),g="";return a=b.settings.image,a?(e="none","string"!=typeof a&&(a=window.getSelection?a[0]:a[1]),a=" style=\"background-image: url('"+a+"')\""):a="",f&&(b.classes.add("btn-has-text"),g='<span class="'+d+'txt">'+b.encode(f)+"</span>"),e=e?d+"ico "+d+"i-"+e:"",'<div id="'+c+'" class="'+b.classes+'" tabindex="-1"><button role="presentation" type="button" tabindex="-1">'+(e?'<i class="'+e+'"'+a+"></i>":"")+g+"</button></div>"},bindStates:function(){function a(a){var e=c("span."+d,b.getEl());a?(e[0]||(c("button:first",b.getEl()).append('<span class="'+d+'"></span>'),e=c("span."+d,b.getEl())),e.html(b.encode(a))):e.remove(),b.classes.toggle("btn-has-text",!!a)}var b=this,c=b.$,d=b.classPrefix+"txt";return b.state.on("change:text",function(b){a(b.value)}),b.state.on("change:icon",function(c){var d=c.value,e=b.classPrefix;b.settings.icon=d,d=d?e+"ico "+e+"i-"+b.settings.icon:"";var f=b.getEl().firstChild,g=f.getElementsByTagName("i")[0];d?(g&&g==f.firstChild||(g=document.createElement("i"),f.insertBefore(g,f.firstChild)),g.className=d):g&&f.removeChild(g),a(b.state.get("text"))}),b._super()}})}),g("2w",["2j"],function(a){"use strict";return a.extend({Defaults:{defaultType:"button",role:"group"},renderHtml:function(){var a=this,b=a._layout;return a.classes.add("btn-group"),a.preRender(),b.preRender(a),'<div id="'+a._id+'" class="'+a.classes+'"><div id="'+a._id+'-body">'+(a.settings.html||"")+b.renderHtml(a)+"</div></div>"}})}),g("2x",["2r"],function(a){"use strict";return a.extend({Defaults:{classes:"checkbox",role:"checkbox",checked:!1},init:function(a){var b=this;b._super(a),b.on("click mousedown",function(a){a.preventDefault()}),b.on("click",function(a){a.preventDefault(),b.disabled()||b.checked(!b.checked())}),b.checked(b.settings.checked)},checked:function(a){return arguments.length?(this.state.set("checked",a),this):this.state.get("checked")},value:function(a){return arguments.length?this.checked(a):this.checked()},renderHtml:function(){var a=this,b=a._id,c=a.classPrefix;return'<div id="'+b+'" class="'+a.classes+'" unselectable="on" aria-labelledby="'+b+'-al" tabindex="-1"><i class="'+c+"ico "+c+'i-checkbox"></i><span id="'+b+'-al" class="'+c+'label">'+a.encode(a.state.get("text"))+"</span></div>"},bindStates:function(){function a(a){b.classes.toggle("checked",a),b.aria("checked",a)}var b=this;return b.state.on("change:text",function(a){b.getEl("al").firstChild.data=b.translate(a.value)}),b.state.on("change:checked change:value",function(c){b.fire("change"),a(c.value)}),b.state.on("change:icon",function(a){var c=a.value,d=b.classPrefix;if("undefined"==typeof c)return b.settings.icon;b.settings.icon=c,c=c?d+"ico "+d+"i-"+b.settings.icon:"";var e=b.getEl().firstChild,f=e.getElementsByTagName("i")[0];c?(f&&f==e.firstChild||(f=document.createElement("i"),e.insertBefore(f,e.firstChild)),f.className=c):f&&e.removeChild(f)}),b.state.get("checked")&&a(!0),b._super()}})}),g("2y",["2r","2h","4q","a","p","9"],function(a,b,c,d,e,f){"use strict";return a.extend({init:function(a){var b=this;b._super(a),a=b.settings,b.classes.add("combobox"),b.subinput=!0,b.ariaTarget="inp",a.menu=a.menu||a.values,a.menu&&(a.icon="caret"),b.on("click",function(c){var e=c.target,f=b.getEl();if(d.contains(f,e)||e==f)for(;e&&e!=f;)e.id&&e.id.indexOf("-open")!=-1&&(b.fire("action"),a.menu&&(b.showMenu(),c.aria&&b.menu.items()[0].focus())),e=e.parentNode}),b.on("keydown",function(a){var c;13==a.keyCode&&"INPUT"===a.target.nodeName&&(a.preventDefault(),b.parents().reverse().each(function(a){if(a.toJSON)return c=a,!1}),b.fire("submit",{data:c.toJSON()}))}),b.on("keyup",function(a){if("INPUT"==a.target.nodeName){var c=b.state.get("value"),d=a.target.value;d!==c&&(b.state.set("value",d),b.fire("autocomplete",a))}}),b.on("mouseover",function(a){var c=b.tooltip().moveTo(-65535);if(b.statusLevel()&&a.target.className.indexOf(b.classPrefix+"status")!==-1){var d=b.statusMessage()||"Ok",e=c.text(d).show().testMoveRel(a.target,["bc-tc","bc-tl","bc-tr"]);c.classes.toggle("tooltip-n","bc-tc"==e),c.classes.toggle("tooltip-nw","bc-tl"==e),c.classes.toggle("tooltip-ne","bc-tr"==e),c.moveRel(a.target,e)}})},statusLevel:function(a){return arguments.length>0&&this.state.set("statusLevel",a),this.state.get("statusLevel")},statusMessage:function(a){return arguments.length>0&&this.state.set("statusMessage",a),this.state.get("statusMessage")},showMenu:function(){var a,c=this,d=c.settings;c.menu||(a=d.menu||[],a.length?a={type:"menu",items:a}:a.type=a.type||"menu",c.menu=b.create(a).parent(c).renderTo(c.getContainerElm()),c.fire("createmenu"),c.menu.reflow(),c.menu.on("cancel",function(a){a.control===c.menu&&c.focus()}),c.menu.on("show hide",function(a){a.control.items().each(function(a){a.active(a.value()==c.value())})}).fire("show"),c.menu.on("select",function(a){c.value(a.control.value())}),c.on("focusin",function(a){"INPUT"==a.target.tagName.toUpperCase()&&c.menu.hide()}),c.aria("expanded",!0)),c.menu.show(),c.menu.layoutRect({w:c.layoutRect().w}),c.menu.moveRel(c.getEl(),c.isRtl()?["br-tr","tr-br"]:["bl-tl","tl-bl"])},focus:function(){this.getEl("inp").focus()},repaint:function(){var a,b,e=this,f=e.getEl(),g=e.getEl("open"),h=e.layoutRect(),i=0,j=f.firstChild;e.statusLevel()&&"none"!==e.statusLevel()&&(i=parseInt(c.getRuntimeStyle(j,"padding-right"),10)-parseInt(c.getRuntimeStyle(j,"padding-left"),10)),a=g?h.w-c.getSize(g).width-10:h.w-10;var k=document;return k.all&&(!k.documentMode||k.documentMode<=8)&&(b=e.layoutRect().h-2+"px"),d(j).css({width:a-i,lineHeight:b}),e._super(),e},postRender:function(){var a=this;return d(this.getEl("inp")).on("change",function(b){a.state.set("value",b.target.value),a.fire("change",b)}),a._super()},renderHtml:function(){var a,b,c=this,d=c._id,e=c.settings,f=c.classPrefix,g=c.state.get("value")||"",h="",i="",j="";return"spellcheck"in e&&(i+=' spellcheck="'+e.spellcheck+'"'),e.maxLength&&(i+=' maxlength="'+e.maxLength+'"'),e.size&&(i+=' size="'+e.size+'"'),e.subtype&&(i+=' type="'+e.subtype+'"'),j='<i id="'+d+'-status" class="mce-status mce-ico" style="display: none"></i>',c.disabled()&&(i+=' disabled="disabled"'),a=e.icon,a&&"caret"!=a&&(a=f+"ico "+f+"i-"+e.icon),b=c.state.get("text"),(a||b)&&(h='<div id="'+d+'-open" class="'+f+"btn "+f+'open" tabIndex="-1" role="button"><button id="'+d+'-action" type="button" hidefocus="1" tabindex="-1">'+("caret"!=a?'<i class="'+a+'"></i>':'<i class="'+f+'caret"></i>')+(b?(a?" ":"")+b:"")+"</button></div>",c.classes.add("has-open")),'<div id="'+d+'" class="'+c.classes+'"><input id="'+d+'-inp" class="'+f+'textbox" value="'+c.encode(g,!1)+'" hidefocus="1"'+i+' placeholder="'+c.encode(e.placeholder)+'" />'+j+h+"</div>"},value:function(a){return arguments.length?(this.state.set("value",a),this):(this.state.get("rendered")&&this.state.set("value",this.getEl("inp").value),this.state.get("value"))},showAutoComplete:function(a,c){var d=this;if(0===a.length)return void d.hideMenu();var e=function(a,b){return function(){d.fire("selectitem",{title:b,value:a})}};d.menu?d.menu.items().remove():d.menu=b.create({type:"menu",classes:"combobox-menu",layout:"flow"}).parent(d).renderTo(),f.each(a,function(a){d.menu.add({text:a.title,url:a.previewUrl,match:c,classes:"menu-item-ellipsis",onclick:e(a.value,a.title)})}),d.menu.renderNew(),d.hideMenu(),d.menu.on("cancel",function(a){a.control.parent()===d.menu&&(a.stopPropagation(),d.focus(),d.hideMenu())}),d.menu.on("select",function(){d.focus()});var g=d.layoutRect().w;d.menu.layoutRect({w:g,minW:0,maxW:g}),d.menu.reflow(),d.menu.show(),d.menu.moveRel(d.getEl(),d.isRtl()?["br-tr","tr-br"]:["bl-tl","tl-bl"])},hideMenu:function(){this.menu&&this.menu.hide()},bindStates:function(){var a=this;a.state.on("change:value",function(b){a.getEl("inp").value!=b.value&&(a.getEl("inp").value=b.value)}),a.state.on("change:disabled",function(b){a.getEl("inp").disabled=b.value}),a.state.on("change:statusLevel",function(b){var d=a.getEl("status"),e=a.classPrefix,f=b.value;c.css(d,"display","none"===f?"none":""),c.toggleClass(d,e+"i-checkmark","ok"===f),c.toggleClass(d,e+"i-warning","warn"===f),c.toggleClass(d,e+"i-error","error"===f),a.classes.toggle("has-status","none"!==f),a.repaint()}),c.on(a.getEl("status"),"mouseleave",function(){a.tooltip().hide()}),a.on("cancel",function(b){a.menu&&a.menu.visible()&&(b.stopPropagation(),a.hideMenu())});var b=function(a,b){b&&b.items().length>0&&b.items().eq(a)[0].focus()};return a.on("keydown",function(c){var d=c.keyCode;"INPUT"===c.target.nodeName&&(d===e.DOWN?(c.preventDefault(),a.fire("autocomplete"),b(0,a.menu)):d===e.UP&&(c.preventDefault(),b(-1,a.menu)))}),a._super()},remove:function(){d(this.getEl("inp")).off(),this.menu&&this.menu.remove(),this._super()}})}),g("2z",["2y"],function(a){"use strict";return a.extend({init:function(a){var b=this;a.spellcheck=!1,a.onaction&&(a.icon="none"),b._super(a),b.classes.add("colorbox"),b.on("change keyup postrender",function(){b.repaintColor(b.value())})},repaintColor:function(a){var b=this.getEl("open"),c=b?b.getElementsByTagName("i")[0]:null;if(c)try{c.style.background=a}catch(a){}},bindStates:function(){var a=this;return a.state.on("change:value",function(b){a.state.get("rendered")&&a.repaintColor(b.value)}),a._super()}})}),g("30",["2v","2p"],function(a,b){"use strict";return a.extend({showPanel:function(){var a=this,c=a.settings;if(a.active(!0),a.panel)a.panel.show();else{var d=c.panel;d.type&&(d={layout:"grid",items:d}),d.role=d.role||"dialog",d.popover=!0,d.autohide=!0,d.ariaRoot=!0,a.panel=new b(d).on("hide",function(){a.active(!1)}).on("cancel",function(b){b.stopPropagation(),a.focus(),a.hidePanel()}).parent(a).renderTo(a.getContainerElm()),a.panel.fire("show"),a.panel.reflow()}var e=a.panel.testMoveRel(a.getEl(),c.popoverAlign||(a.isRtl()?["bc-tc","bc-tl","bc-tr"]:["bc-tc","bc-tr","bc-tl"]));a.panel.classes.toggle("start","bc-tl"===e),a.panel.classes.toggle("end","bc-tr"===e),a.panel.moveRel(a.getEl(),e)},hidePanel:function(){var a=this;a.panel&&a.panel.hide()},postRender:function(){var a=this;return a.aria("haspopup",!0),a.on("click",function(b){b.control===a&&(a.panel&&a.panel.visible()?a.hidePanel():(a.showPanel(),a.panel.focus(!!b.aria)))}),a._super()},remove:function(){return this.panel&&(this.panel.remove(),this.panel=null),this._super()}})}),g("31",["30","e"],function(a,b){"use strict";var c=b.DOM;return a.extend({init:function(a){this._super(a),this.classes.add("colorbutton")},color:function(a){return a?(this._color=a,this.getEl("preview").style.backgroundColor=a,this):this._color},resetColor:function(){return this._color=null,this.getEl("preview").style.backgroundColor=null,this},renderHtml:function(){var a=this,b=a._id,c=a.classPrefix,d=a.state.get("text"),e=a.settings.icon?c+"ico "+c+"i-"+a.settings.icon:"",f=a.settings.image?" style=\"background-image: url('"+a.settings.image+"')\"":"",g="";return d&&(a.classes.add("btn-has-text"),g='<span class="'+c+'txt">'+a.encode(d)+"</span>"),'<div id="'+b+'" class="'+a.classes+'" role="button" tabindex="-1" aria-haspopup="true"><button role="presentation" hidefocus="1" type="button" tabindex="-1">'+(e?'<i class="'+e+'"'+f+"></i>":"")+'<span id="'+b+'-preview" class="'+c+'preview"></span>'+g+'</button><button type="button" class="'+c+'open" hidefocus="1" tabindex="-1"> <i class="'+c+'caret"></i></button></div>'},postRender:function(){var a=this,b=a.settings.onclick;return a.on("click",function(d){d.aria&&"down"==d.aria.key||d.control!=a||c.getParent(d.target,"."+a.classPrefix+"open")||(d.stopImmediatePropagation(),b.call(a,d))}),delete a.settings.onclick,a._super()}})}),g("32",["2r","2k","4q","1e"],function(a,b,c,d){"use strict";return a.extend({Defaults:{classes:"widget colorpicker"},init:function(a){this._super(a)},postRender:function(){function a(a,b){var d,e,f=c.getPos(a);return d=b.pageX-f.x,e=b.pageY-f.y,d=Math.max(0,Math.min(d/a.clientWidth,1)),e=Math.max(0,Math.min(e/a.clientHeight,1)),{x:d,y:e}}function e(a,b){var e=(360-a.h)/360;c.css(j,{top:100*e+"%"}),b||c.css(l,{left:a.s+"%",top:100-a.v+"%"}),k.style.background=new d({s:100,v:100,h:a.h}).toHex(),m.color().parse({s:a.s,v:a.v,h:a.h})}function f(b){var c;c=a(k,b),h.s=100*c.x,h.v=100*(1-c.y),e(h),m.fire("change")}function g(b){var c;c=a(i,b),h=n.toHsv(),h.h=360*(1-c.y),e(h,!0),m.fire("change")}var h,i,j,k,l,m=this,n=m.color();i=m.getEl("h"),j=m.getEl("hp"),k=m.getEl("sv"),l=m.getEl("svp"),m._repaint=function(){h=n.toHsv(),e(h)},m._super(),m._svdraghelper=new b(m._id+"-sv",{start:f,drag:f}),m._hdraghelper=new b(m._id+"-h",{start:g,drag:g}),m._repaint()},rgb:function(){return this.color().toRgb()},value:function(a){var b=this;return arguments.length?(b.color().parse(a),void(b._rendered&&b._repaint())):b.color().toHex()},color:function(){return this._color||(this._color=new d),this._color},renderHtml:function(){function a(){var a,b,c,d,g="";for(c="filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=",d=f.split(","),a=0,b=d.length-1;a<b;a++)g+='<div class="'+e+'colorpicker-h-chunk" style="height:'+100/b+"%;"+c+d[a]+",endColorstr="+d[a+1]+");-ms-"+c+d[a]+",endColorstr="+d[a+1]+')"></div>';return g}var b,c=this,d=c._id,e=c.classPrefix,f="#ff0000,#ff0080,#ff00ff,#8000ff,#0000ff,#0080ff,#00ffff,#00ff80,#00ff00,#80ff00,#ffff00,#ff8000,#ff0000",g="background: -ms-linear-gradient(top,"+f+");background: linear-gradient(to bottom,"+f+");";return b='<div id="'+d+'-h" class="'+e+'colorpicker-h" style="'+g+'">'+a()+'<div id="'+d+'-hp" class="'+e+'colorpicker-h-marker"></div></div>','<div id="'+d+'" class="'+c.classes+'"><div id="'+d+'-sv" class="'+e+'colorpicker-sv"><div class="'+e+'colorpicker-overlay1"><div class="'+e+'colorpicker-overlay2"><div id="'+d+'-svp" class="'+e+'colorpicker-selector1"><div class="'+e+'colorpicker-selector2"></div></div></div></div></div>'+b+"</div>"}})}),g("33",["2r"],function(a){"use strict";return a.extend({init:function(a){var b=this;a.delimiter||(a.delimiter="\xbb"),b._super(a),b.classes.add("path"),b.canFocus=!0,b.on("click",function(a){var c,d=a.target;(c=d.getAttribute("data-index"))&&b.fire("select",{value:b.row()[c],index:c})}),b.row(b.settings.row)},focus:function(){var a=this;return a.getEl().firstChild.focus(),a},row:function(a){return arguments.length?(this.state.set("row",a),this):this.state.get("row")},renderHtml:function(){var a=this;return'<div id="'+a._id+'" class="'+a.classes+'">'+a._getDataPathHtml(a.state.get("row"))+"</div>"},bindStates:function(){var a=this;return a.state.on("change:row",function(b){a.innerHtml(a._getDataPathHtml(b.value))}),a._super()},_getDataPathHtml:function(a){var b,c,d=this,e=a||[],f="",g=d.classPrefix;for(b=0,c=e.length;b<c;b++)f+=(b>0?'<div class="'+g+'divider" aria-hidden="true"> '+d.settings.delimiter+" </div>":"")+'<div role="button" class="'+g+"path-item"+(b==c-1?" "+g+"last":"")+'" data-index="'+b+'" tabindex="-1" id="'+d._id+"-"+b+'" aria-level="'+(b+1)+'">'+e[b].name+"</div>"; -return f||(f='<div class="'+g+'path-item">\xa0</div>'),f}})}),g("34",["33"],function(a){return a.extend({postRender:function(){function a(a){if(1===a.nodeType){if("BR"==a.nodeName||a.getAttribute("data-mce-bogus"))return!0;if("bookmark"===a.getAttribute("data-mce-type"))return!0}return!1}var b=this,c=b.settings.editor;return c.settings.elementpath!==!1&&(b.on("select",function(a){c.focus(),c.selection.select(this.row()[a.index].element),c.nodeChanged()}),c.on("nodeChange",function(d){for(var e=[],f=d.parents,g=f.length;g--;)if(1==f[g].nodeType&&!a(f[g])){var h=c.fire("ResolveName",{name:f[g].nodeName.toLowerCase(),target:f[g]});if(h.isDefaultPrevented()||e.push({name:h.name,element:f[g]}),h.isPropagationStopped())break}b.row(e)})),b._super()}})}),g("35",["2j"],function(a){"use strict";return a.extend({Defaults:{layout:"flex",align:"center",defaults:{flex:1}},renderHtml:function(){var a=this,b=a._layout,c=a.classPrefix;return a.classes.add("formitem"),b.preRender(a),'<div id="'+a._id+'" class="'+a.classes+'" hidefocus="1" tabindex="-1">'+(a.settings.title?'<div id="'+a._id+'-title" class="'+c+'title">'+a.settings.title+"</div>":"")+'<div id="'+a._id+'-body" class="'+a.bodyClasses+'">'+(a.settings.html||"")+b.renderHtml(a)+"</div></div>"}})}),g("36",["2j","35","9"],function(a,b,c){"use strict";return a.extend({Defaults:{containerCls:"form",layout:"flex",direction:"column",align:"stretch",flex:1,padding:20,labelGap:30,spacing:10,callbacks:{submit:function(){this.submit()}}},preRender:function(){var a=this,d=a.items();a.settings.formItemDefaults||(a.settings.formItemDefaults={layout:"flex",autoResize:"overflow",defaults:{flex:1}}),d.each(function(d){var e,f=d.settings.label;f&&(e=new b(c.extend({items:{type:"label",id:d._id+"-l",text:f,flex:0,forId:d._id,disabled:d.disabled()}},a.settings.formItemDefaults)),e.type="formitem",d.aria("labelledby",d._id+"-l"),"undefined"==typeof d.settings.flex&&(d.settings.flex=1),a.replace(d,e),e.add(d))})},submit:function(){return this.fire("submit",{data:this.toJSON()})},postRender:function(){var a=this;a._super(),a.fromJSON(a.settings.data)},bindStates:function(){function a(){var a,c,d,e=0,f=[];if(b.settings.labelGapCalc!==!1)for(d="children"==b.settings.labelGapCalc?b.find("formitem"):b.items(),d.filter("formitem").each(function(a){var b=a.items()[0],c=b.getEl().clientWidth;e=c>e?c:e,f.push(b)}),c=b.settings.labelGap||0,a=f.length;a--;)f[a].settings.minWidth=e+c}var b=this;b._super(),b.on("show",a),a()}})}),g("37",["36"],function(a){"use strict";return a.extend({Defaults:{containerCls:"fieldset",layout:"flex",direction:"column",align:"stretch",flex:1,padding:"25 15 5 15",labelGap:30,spacing:10,border:1},renderHtml:function(){var a=this,b=a._layout,c=a.classPrefix;return a.preRender(),b.preRender(a),'<fieldset id="'+a._id+'" class="'+a.classes+'" hidefocus="1" tabindex="-1">'+(a.settings.title?'<legend id="'+a._id+'-title" class="'+c+'fieldset-title">'+a.settings.title+"</legend>":"")+'<div id="'+a._id+'-body" class="'+a.bodyClasses+'">'+(a.settings.html||"")+b.renderHtml(a)+"</div></fieldset>"}})}),g("4x",["e","1j","1g","1w","9","2a"],function(a,b,c,d,e,f){var g=e.trim,h=function(a,b,c,d,e){return{type:a,title:b,url:c,level:d,attach:e}},i=function(a){for(;a=a.parentNode;){var c=a.contentEditable;if(c&&"inherit"!==c)return b.isContentEditableTrue(a)}return!1},j=function(b,c){return a.DOM.select(b,c)},k=function(a){return a.innerText||a.textContent},l=function(a){return a.id?a.id:f.uuid("h")},m=function(a){return a&&"A"===a.nodeName&&(a.id||a.name)},n=function(a){return m(a)&&p(a)},o=function(a){return a&&/^(H[1-6])$/.test(a.nodeName)},p=function(a){return i(a)&&!b.isContentEditableFalse(a)},q=function(a){return o(a)&&p(a)},r=function(a){return o(a)?parseInt(a.nodeName.substr(1),10):0},s=function(a){var b=l(a),c=function(){a.id=b};return h("header",k(a),"#"+b,r(a),c)},t=function(a){var b=a.id||a.name,c=k(a);return h("anchor",c?c:"#"+b,"#"+b,0,d.noop)},u=function(a){return c.map(c.filter(a,q),s)},v=function(a){return c.map(c.filter(a,n),t)},w=function(a){var b=j("h1,h2,h3,h4,h5,h6,a:not([href])",a);return b},x=function(a){return g(a.title).length>0},y=function(a){var b=w(a);return c.filter(u(b).concat(v(b)),x)};return{find:y}}),g("38",["4t","4x","17","2y","1g","1w","9"],function(a,b,c,d,e,f,g){"use strict";var h=function(){return a.tinymce?a.tinymce.activeEditor:c.activeEditor},i={},j=5,k=function(a){return{title:a.title,value:{title:{raw:a.title},url:a.url,attach:a.attach}}},l=function(a){return g.map(a,k)},m=function(a,b){return{title:a,value:{title:a,url:b,attach:f.noop}}},n=function(a,b){var c=e.find(b,function(b){return b.url===a});return!c},o=function(a,b,c){var d=b in a?a[b]:c;return d===!1?null:d},p=function(a,b,c,d){var h={title:"-"},j=function(a){var d=e.filter(a[c],function(a){return n(a,b)});return g.map(d,function(a){return{title:a,value:{title:a,url:a,attach:f.noop}}})},k=function(a){var c=e.filter(b,function(b){return b.type==a});return l(c)},p=function(){var a=k("anchor"),b=o(d,"anchor_top","#top"),c=o(d,"anchor_bottom","#bottom");return null!==b&&a.unshift(m("<top>",b)),null!==c&&a.push(m("<bottom>",c)),a},q=function(a){return e.reduce(a,function(a,b){var c=0===a.length||0===b.length;return c?a.concat(b):a.concat(h,b)},[])};return d.typeahead_urls===!1?[]:"file"===c?q([r(a,j(i)),r(a,k("header")),r(a,p())]):r(a,j(i))},q=function(a,b){var c=i[b];/^https?/.test(a)&&(c?e.indexOf(c,a)===-1&&(i[b]=c.slice(0,j).concat(a)):i[b]=[a])},r=function(a,b){var c=a.toLowerCase(),d=g.grep(b,function(a){return a.title.toLowerCase().indexOf(c)!==-1});return 1===d.length&&d[0].title===a?[]:d},s=function(a){var b=a.title;return b.raw?b.raw:b},t=function(a,c,d,e){var f=function(f){var g=b.find(d),h=p(f,g,e,c);a.showAutoComplete(h,f)};a.on("autocomplete",function(){f(a.value())}),a.on("selectitem",function(b){var c=b.value;a.value(c.url);var d=s(c);"image"===e?a.fire("change",{meta:{alt:d,attach:c.attach}}):a.fire("change",{meta:{text:d,attach:c.attach}}),a.focus()}),a.on("click",function(b){0===a.value().length&&"INPUT"===b.target.nodeName&&f("")}),a.on("PostRender",function(){a.getRoot().on("submit",function(b){b.isDefaultPrevented()||q(a.value(),e)})})},u=function(a){var b=a.status,c=a.message;return"valid"===b?{status:"ok",message:c}:"unknown"===b?{status:"warn",message:c}:"invalid"===b?{status:"warn",message:c}:{status:"none",message:""}},v=function(a,b,c){var d=b.filepicker_validator_handler;if(d){var e=function(b){return 0===b.length?void a.statusLevel("none"):void d({url:b,type:c},function(b){var c=u(b);a.statusMessage(c.message),a.statusLevel(c.status)})};a.state.on("change:value",function(a){e(a.value)})}};return d.extend({init:function(b){var c,d,e,f=this,i=h(),j=i.settings,k=b.filetype;b.spellcheck=!1,e=j.file_picker_types||j.file_browser_callback_types,e&&(e=g.makeMap(e,/[, ]/)),e&&!e[k]||(d=j.file_picker_callback,!d||e&&!e[k]?(d=j.file_browser_callback,!d||e&&!e[k]||(c=function(){d(f.getEl("inp").id,f.value(),k,a)})):c=function(){var a=f.fire("beforecall").meta;a=g.extend({filetype:k},a),d.call(i,function(a,b){f.value(a).fire("change",{meta:b})},f.value(),a)}),c&&(b.icon="browse",b.onaction=c),f._super(b),t(f,j,i.getBody(),k),v(f,j,k)}})}),g("39",["2u"],function(a){"use strict";return a.extend({recalc:function(a){var b=a.layoutRect(),c=a.paddingBox;a.items().filter(":visible").each(function(a){a.layoutRect({x:c.left,y:c.top,w:b.innerW-c.right-c.left,h:b.innerH-c.top-c.bottom}),a.recalc&&a.recalc()})}})}),g("3a",["2u"],function(a){"use strict";return a.extend({recalc:function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N=[],O=Math.max,P=Math.min;for(d=a.items().filter(":visible"),e=a.layoutRect(),f=a.paddingBox,g=a.settings,m=a.isRtl()?g.direction||"row-reversed":g.direction,h=g.align,i=a.isRtl()?g.pack||"end":g.pack,j=g.spacing||0,"row-reversed"!=m&&"column-reverse"!=m||(d=d.set(d.toArray().reverse()),m=m.split("-")[0]),"column"==m?(z="y",x="h",y="minH",A="maxH",C="innerH",B="top",D="deltaH",E="contentH",J="left",H="w",F="x",G="innerW",I="minW",K="right",L="deltaW",M="contentW"):(z="x",x="w",y="minW",A="maxW",C="innerW",B="left",D="deltaW",E="contentW",J="top",H="h",F="y",G="innerH",I="minH",K="bottom",L="deltaH",M="contentH"),l=e[C]-f[B]-f[B],w=k=0,b=0,c=d.length;b<c;b++)n=d[b],o=n.layoutRect(),p=n.settings,q=p.flex,l-=b<c-1?j:0,q>0&&(k+=q,o[A]&&N.push(n),o.flex=q),l-=o[y],r=f[J]+o[I]+f[K],r>w&&(w=r);if(u={},l<0?u[y]=e[y]-l+e[D]:u[y]=e[C]-l+e[D],u[I]=w+e[L],u[E]=e[C]-l,u[M]=w,u.minW=P(u.minW,e.maxW),u.minH=P(u.minH,e.maxH),u.minW=O(u.minW,e.startMinWidth),u.minH=O(u.minH,e.startMinHeight),!e.autoResize||u.minW==e.minW&&u.minH==e.minH){for(t=l/k,b=0,c=N.length;b<c;b++)n=N[b],o=n.layoutRect(),s=o[A],r=o[y]+o.flex*t,r>s?(l-=o[A]-o[y],k-=o.flex,o.flex=0,o.maxFlexSize=s):o.maxFlexSize=0;for(t=l/k,v=f[B],u={},0===k&&("end"==i?v=l+f[B]:"center"==i?(v=Math.round(e[C]/2-(e[C]-l)/2)+f[B],v<0&&(v=f[B])):"justify"==i&&(v=f[B],j=Math.floor(l/(d.length-1)))),u[F]=f[J],b=0,c=d.length;b<c;b++)n=d[b],o=n.layoutRect(),r=o.maxFlexSize||o[y],"center"===h?u[F]=Math.round(e[G]/2-o[H]/2):"stretch"===h?(u[H]=O(o[I]||0,e[G]-f[J]-f[K]),u[F]=f[J]):"end"===h&&(u[F]=e[G]-o[H]-f.top),o.flex>0&&(r+=o.flex*t),u[x]=r,u[z]=v,n.layoutRect(u),n.recalc&&n.recalc(),v+=r+j}else if(u.w=u.minW,u.h=u.minH,a.layoutRect(u),this.recalc(a),null===a._lastRect){var Q=a.parent();Q&&(Q._lastRect=null,Q.recalc())}}})}),g("3b",["2t"],function(a){return a.extend({Defaults:{containerClass:"flow-layout",controlClass:"flow-layout-item",endClass:"break"},recalc:function(a){a.items().filter(":visible").each(function(a){a.recalc&&a.recalc()})},isNative:function(){return!0}})}),g("4y",["1m","4z","1r","4d","e"],function(a,b,c,d,e){var f=function(a,c,d){for(;d!==c;){if(d.style[a]){var e=d.style[a];return""!==e?b.some(e):b.none()}d=d.parentNode}return b.none()},g=function(a){return/[0-9.]+px$/.test(a)?Math.round(72*parseInt(a,10)/96)+"pt":a},h=function(a){return a.replace(/[\'\"]/g,"").replace(/,\s+/g,",")},i=function(a,c){return b.from(e.DOM.getStyle(c,a,!0))},j=function(a){return function(e,g){return b.from(g).map(c.fromDom).filter(d.isElement).bind(function(b){return f(a,e,b.dom()).or(i(a,b.dom()))}).getOr("")}};return{getFontSize:j("fontSize"),getFontFamily:a.compose(h,j("fontFamily")),toPt:g}}),g("3c",["2g","2r","2p","9","1g","e","17","6","4y"],function(a,b,c,d,e,f,g,h,i){function j(a){a.settings.ui_container&&(h.container=f.DOM.select(a.settings.ui_container)[0])}function k(b){b.on("ScriptsLoaded",function(){b.rtl&&(a.rtl=!0)})}function l(a){function b(b,c){return function(){var d=this;a.on("nodeChange",function(e){var f=a.formatter,g=null;m(e.parents,function(a){if(m(b,function(b){if(c?f.matchNode(a,c,{value:b.value})&&(g=b.value):f.matchNode(a,b.value)&&(g=b.value),g)return!1}),g)return!1}),d.value(g)})}}function e(b){return function(){var c=this,d=function(a){return a?a.split(",")[0]:""};a.on("init nodeChange",function(e){var f,g=null;f=i.getFontFamily(a.getBody(),e.element),m(b,function(a){a.value.toLowerCase()===f.toLowerCase()&&(g=a.value)}),m(b,function(a){g||d(a.value).toLowerCase()!==d(f).toLowerCase()||(g=a.value)}),c.value(g),!g&&f&&c.text(d(f))})}}function f(b){return function(){var c=this;a.on("init nodeChange",function(d){var e,f,g=null;e=i.getFontSize(a.getBody(),d.element),f=i.toPt(e),m(b,function(a){a.value===e?g=e:a.value===f&&(g=f)}),c.value(g),g||c.text(f)})}}function g(a){a=a.replace(/;$/,"").split(";");for(var b=a.length;b--;)a[b]=a[b].split("=");return a}function h(){function b(a){var c=[];if(a)return m(a,function(a){var f={text:a.title,icon:a.icon};if(a.items)f.menu=b(a.items);else{var g=a.format||"custom"+d++;a.format||(a.name=g,e.push(a)),f.format=g,f.cmd=a.cmd}c.push(f)}),c}function c(){var c;return c=b(a.settings.style_formats_merge?a.settings.style_formats?f.concat(a.settings.style_formats):f:a.settings.style_formats||f)}var d=0,e=[],f=[{title:"Headings",items:[{title:"Heading 1",format:"h1"},{title:"Heading 2",format:"h2"},{title:"Heading 3",format:"h3"},{title:"Heading 4",format:"h4"},{title:"Heading 5",format:"h5"},{title:"Heading 6",format:"h6"}]},{title:"Inline",items:[{title:"Bold",icon:"bold",format:"bold"},{title:"Italic",icon:"italic",format:"italic"},{title:"Underline",icon:"underline",format:"underline"},{title:"Strikethrough",icon:"strikethrough",format:"strikethrough"},{title:"Superscript",icon:"superscript",format:"superscript"},{title:"Subscript",icon:"subscript",format:"subscript"},{title:"Code",icon:"code",format:"code"}]},{title:"Blocks",items:[{title:"Paragraph",format:"p"},{title:"Blockquote",format:"blockquote"},{title:"Div",format:"div"},{title:"Pre",format:"pre"}]},{title:"Alignment",items:[{title:"Left",icon:"alignleft",format:"alignleft"},{title:"Center",icon:"aligncenter",format:"aligncenter"},{title:"Right",icon:"alignright",format:"alignright"},{title:"Justify",icon:"alignjustify",format:"alignjustify"}]}];return a.on("init",function(){m(e,function(b){a.formatter.register(b.name,b)})}),{type:"menu",items:c(),onPostRender:function(b){a.fire("renderFormatsMenu",{control:b.control})},itemDefaults:{preview:!0,textStyle:function(){if(this.settings.format)return a.formatter.getCssText(this.settings.format)},onPostRender:function(){var b=this;b.parent().on("show",function(){var c,d;c=b.settings.format,c&&(b.disabled(!a.formatter.canApply(c)),b.active(a.formatter.match(c))),d=b.settings.cmd,d&&b.active(a.queryCommandState(d))})},onclick:function(){this.settings.format&&o(this.settings.format),this.settings.cmd&&a.execCommand(this.settings.cmd)}}}}function j(b){return function(){var c=this;a.formatter?a.formatter.formatChanged(b,function(a){c.active(a)}):a.on("init",function(){a.formatter.formatChanged(b,function(a){c.active(a)})})}}function k(b){return function(){function c(){var c="redo"==b?"hasRedo":"hasUndo";return!!a.undoManager&&a.undoManager[c]()}var d=this;d.disabled(!c()),a.on("Undo Redo AddUndo TypingUndo ClearUndos SwitchMode",function(){d.disabled(a.readonly||!c())})}}function l(){var b=this;a.on("VisualAid",function(a){b.active(a.hasVisual)}),b.active(a.hasVisual)}function o(b){b.control&&(b=b.control.value()),b&&a.execCommand("mceToggleFormat",!1,b)}function p(b){var c=b.length;return d.each(b,function(b){b.menu&&(b.hidden=0===p(b.menu));var d=b.format;d&&(b.hidden=!a.formatter.canApply(d)),b.hidden&&c--}),c}function q(b){var c=b.items().length;return b.items().each(function(b){b.menu&&b.visible(q(b.menu)>0),!b.menu&&b.settings.menu&&b.visible(p(b.settings.menu)>0);var d=b.settings.format;d&&b.visible(a.formatter.canApply(d)),b.visible()||c--}),c}var r;r=h(),m({bold:"Bold",italic:"Italic",underline:"Underline",strikethrough:"Strikethrough",subscript:"Subscript",superscript:"Superscript"},function(b,c){a.addButton(c,{tooltip:b,onPostRender:j(c),onclick:function(){o(c)}})}),m({outdent:["Decrease indent","Outdent"],indent:["Increase indent","Indent"],cut:["Cut","Cut"],copy:["Copy","Copy"],paste:["Paste","Paste"],help:["Help","mceHelp"],selectall:["Select all","SelectAll"],removeformat:["Clear formatting","RemoveFormat"],visualaid:["Visual aids","mceToggleVisualAid"],newdocument:["New document","mceNewDocument"]},function(b,c){a.addButton(c,{tooltip:b[0],cmd:b[1]})}),m({blockquote:["Blockquote","mceBlockQuote"],subscript:["Subscript","Subscript"],superscript:["Superscript","Superscript"],alignleft:["Align left","JustifyLeft"],aligncenter:["Align center","JustifyCenter"],alignright:["Align right","JustifyRight"],alignjustify:["Justify","JustifyFull"],alignnone:["No alignment","JustifyNone"]},function(b,c){a.addButton(c,{tooltip:b[0],cmd:b[1],onPostRender:j(c)})});var s=function(a){var b=a;return b.length>0&&"-"===b[0].text&&(b=b.slice(1)),b.length>0&&"-"===b[b.length-1].text&&(b=b.slice(0,b.length-1)),b},t=function(b){var c,e;if("string"==typeof b)e=b.split(" ");else if(d.isArray(b))return n(d.map(b,t));return c=d.grep(e,function(b){return"|"===b||b in a.menuItems}),d.map(c,function(b){return"|"===b?{text:"-"}:a.menuItems[b]})},u=function(b){var c=[{text:"-"}],e=d.grep(a.menuItems,function(a){return a.context===b});return d.each(e,function(a){"before"==a.separator&&c.push({text:"|"}),a.prependToContext?c.unshift(a):c.push(a),"after"==a.separator&&c.push({text:"|"})}),c},v=function(a){return s(a.insert_button_items?t(a.insert_button_items):u("insert"))};a.addButton("undo",{tooltip:"Undo",onPostRender:k("undo"),cmd:"undo"}),a.addButton("redo",{tooltip:"Redo",onPostRender:k("redo"),cmd:"redo"}),a.addMenuItem("newdocument",{text:"New document",icon:"newdocument",cmd:"mceNewDocument"}),a.addMenuItem("undo",{text:"Undo",icon:"undo",shortcut:"Meta+Z",onPostRender:k("undo"),cmd:"undo"}),a.addMenuItem("redo",{text:"Redo",icon:"redo",shortcut:"Meta+Y",onPostRender:k("redo"),cmd:"redo"}),a.addMenuItem("visualaid",{text:"Visual aids",selectable:!0,onPostRender:l,cmd:"mceToggleVisualAid"}),a.addButton("remove",{tooltip:"Remove",icon:"remove",cmd:"Delete"}),a.addButton("insert",{type:"menubutton",icon:"insert",menu:[],oncreatemenu:function(){this.menu.add(v(a.settings)),this.menu.renderNew()}}),m({cut:["Cut","Cut","Meta+X"],copy:["Copy","Copy","Meta+C"],paste:["Paste","Paste","Meta+V"],selectall:["Select all","SelectAll","Meta+A"],bold:["Bold","Bold","Meta+B"],italic:["Italic","Italic","Meta+I"],underline:["Underline","Underline","Meta+U"],strikethrough:["Strikethrough","Strikethrough"],subscript:["Subscript","Subscript"],superscript:["Superscript","Superscript"],removeformat:["Clear formatting","RemoveFormat"]},function(b,c){a.addMenuItem(c,{text:b[0],icon:c,shortcut:b[2],cmd:b[1]})}),a.on("mousedown",function(){c.hideAll()}),a.addButton("styleselect",{type:"menubutton",text:"Formats",menu:r,onShowMenu:function(){a.settings.style_formats_autohide&&q(this.menu)}}),a.addButton("formatselect",function(){var c=[],d=g(a.settings.block_formats||"Paragraph=p;Heading 1=h1;Heading 2=h2;Heading 3=h3;Heading 4=h4;Heading 5=h5;Heading 6=h6;Preformatted=pre");return m(d,function(b){c.push({text:b[0],value:b[1],textStyle:function(){return a.formatter.getCssText(b[1])}})}),{type:"listbox",text:d[0][0],values:c,fixedWidth:!0,onselect:o,onPostRender:b(c)}}),a.addButton("fontselect",function(){var b="Andale Mono=andale mono,monospace;Arial=arial,helvetica,sans-serif;Arial Black=arial black,sans-serif;Book Antiqua=book antiqua,palatino,serif;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier,monospace;Georgia=georgia,palatino,serif;Helvetica=helvetica,arial,sans-serif;Impact=impact,sans-serif;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco,monospace;Times New Roman=times new roman,times,serif;Trebuchet MS=trebuchet ms,geneva,sans-serif;Verdana=verdana,geneva,sans-serif;Webdings=webdings;Wingdings=wingdings,zapf dingbats",c=[],d=g(a.settings.font_formats||b);return m(d,function(a){c.push({text:{raw:a[0]},value:a[1],textStyle:a[1].indexOf("dings")==-1?"font-family:"+a[1]:""})}),{type:"listbox",text:"Font Family",tooltip:"Font Family",values:c,fixedWidth:!0,onPostRender:e(c),onselect:function(b){b.control.settings.value&&a.execCommand("FontName",!1,b.control.settings.value)}}}),a.addButton("fontsizeselect",function(){var b=[],c="8pt 10pt 12pt 14pt 18pt 24pt 36pt",d=a.settings.fontsize_formats||c;return m(d.split(" "),function(a){var c=a,d=a,e=a.split("=");e.length>1&&(c=e[0],d=e[1]),b.push({text:c,value:d})}),{type:"listbox",text:"Font Sizes",tooltip:"Font Sizes",values:b,fixedWidth:!0,onPostRender:f(b),onclick:function(b){b.control.settings.value&&a.execCommand("FontSize",!1,b.control.settings.value)}}}),a.addMenuItem("formats",{text:"Formats",menu:r})}var m=d.each,n=function(a){return e.reduce(a,function(a,b){return a.concat(b)},[])};return g.on("AddEditor",function(a){var b=a.editor;k(b),l(b),j(b)}),a.translate=function(a){return g.translate(a)},b.tooltips=!h.iOS,{}}),g("3d",["2u"],function(a){"use strict";return a.extend({recalc:function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E=[],F=[];b=a.settings,e=a.items().filter(":visible"),f=a.layoutRect(),d=b.columns||Math.ceil(Math.sqrt(e.length)),c=Math.ceil(e.length/d),s=b.spacingH||b.spacing||0,t=b.spacingV||b.spacing||0,u=b.alignH||b.align,v=b.alignV||b.align,q=a.paddingBox,C="reverseRows"in b?b.reverseRows:a.isRtl(),u&&"string"==typeof u&&(u=[u]),v&&"string"==typeof v&&(v=[v]);for(l=0;l<d;l++)E.push(0);for(m=0;m<c;m++)F.push(0);for(m=0;m<c;m++)for(l=0;l<d&&(k=e[m*d+l],k);l++)j=k.layoutRect(),y=j.minW,z=j.minH,E[l]=y>E[l]?y:E[l],F[m]=z>F[m]?z:F[m];for(A=f.innerW-q.left-q.right,w=0,l=0;l<d;l++)w+=E[l]+(l>0?s:0),A-=(l>0?s:0)+E[l];for(B=f.innerH-q.top-q.bottom,x=0,m=0;m<c;m++)x+=F[m]+(m>0?t:0),B-=(m>0?t:0)+F[m];if(w+=q.left+q.right,x+=q.top+q.bottom,i={},i.minW=w+(f.w-f.innerW),i.minH=x+(f.h-f.innerH),i.contentW=i.minW-f.deltaW,i.contentH=i.minH-f.deltaH,i.minW=Math.min(i.minW,f.maxW),i.minH=Math.min(i.minH,f.maxH),i.minW=Math.max(i.minW,f.startMinWidth),i.minH=Math.max(i.minH,f.startMinHeight),!f.autoResize||i.minW==f.minW&&i.minH==f.minH){f.autoResize&&(i=a.layoutRect(i),i.contentW=i.minW-f.deltaW,i.contentH=i.minH-f.deltaH);var G;G="start"==b.packV?0:B>0?Math.floor(B/c):0;var H=0,I=b.flexWidths;if(I)for(l=0;l<I.length;l++)H+=I[l];else H=d;var J=A/H;for(l=0;l<d;l++)E[l]+=I?I[l]*J:J;for(o=q.top,m=0;m<c;m++){for(n=q.left,h=F[m]+G,l=0;l<d&&(D=C?m*d+d-1-l:m*d+l,k=e[D],k);l++)p=k.settings,j=k.layoutRect(),g=Math.max(E[l],j.startMinWidth),j.x=n,j.y=o,r=p.alignH||(u?u[l]||u[0]:null),"center"==r?j.x=n+g/2-j.w/2:"right"==r?j.x=n+g-j.w:"stretch"==r&&(j.w=g),r=p.alignV||(v?v[l]||v[0]:null),"center"==r?j.y=o+h/2-j.h/2:"bottom"==r?j.y=o+h-j.h:"stretch"==r&&(j.h=h),k.layoutRect(j),n+=g+s,k.recalc&&k.recalc();o+=h+t}}else if(i.w=i.minW,i.h=i.minH,a.layoutRect(i),this.recalc(a),null===a._lastRect){var K=a.parent();K&&(K._lastRect=null,K.recalc())}}})}),g("3e",["2r","5"],function(a,b){"use strict";return a.extend({renderHtml:function(){var a=this;return a.classes.add("iframe"),a.canFocus=!1,'<iframe id="'+a._id+'" class="'+a.classes+'" tabindex="-1" src="'+(a.settings.url||"javascript:''")+'" frameborder="0"></iframe>'},src:function(a){this.getEl().src=a},html:function(a,c){var d=this,e=this.getEl().contentWindow.document.body;return e?(e.innerHTML=a,c&&c()):b.setTimeout(function(){d.html(a)}),this}})}),g("3f",["2r"],function(a){"use strict";return a.extend({init:function(a){var b=this;b._super(a),b.classes.add("widget").add("infobox"),b.canFocus=!1},severity:function(a){this.classes.remove("error"),this.classes.remove("warning"),this.classes.remove("success"),this.classes.add(a)},help:function(a){this.state.set("help",a)},renderHtml:function(){var a=this,b=a.classPrefix;return'<div id="'+a._id+'" class="'+a.classes+'"><div id="'+a._id+'-body">'+a.encode(a.state.get("text"))+'<button role="button" tabindex="-1"><i class="'+b+"ico "+b+'i-help"></i></button></div></div>'},bindStates:function(){var a=this;return a.state.on("change:text",function(b){a.getEl("body").firstChild.data=a.encode(b.value),a.state.get("rendered")&&a.updateLayoutRect()}),a.state.on("change:help",function(b){a.classes.toggle("has-help",b.value),a.state.get("rendered")&&a.updateLayoutRect()}),a._super()}})}),g("3g",["2r","4q"],function(a,b){"use strict";return a.extend({init:function(a){var b=this;b._super(a),b.classes.add("widget").add("label"),b.canFocus=!1,a.multiline&&b.classes.add("autoscroll"),a.strong&&b.classes.add("strong")},initLayoutRect:function(){var a=this,c=a._super();if(a.settings.multiline){var d=b.getSize(a.getEl());d.width>c.maxW&&(c.minW=c.maxW,a.classes.add("multiline")),a.getEl().style.width=c.minW+"px",c.startMinH=c.h=c.minH=Math.min(c.maxH,b.getSize(a.getEl()).height)}return c},repaint:function(){var a=this;return a.settings.multiline||(a.getEl().style.lineHeight=a.layoutRect().h+"px"),a._super()},severity:function(a){this.classes.remove("error"),this.classes.remove("warning"),this.classes.remove("success"),this.classes.add(a)},renderHtml:function(){var a,b,c=this,d=c.settings.forId,e=c.settings.html?c.settings.html:c.encode(c.state.get("text"));return!d&&(b=c.settings.forName)&&(a=c.getRoot().find("#"+b)[0],a&&(d=a._id)),d?'<label id="'+c._id+'" class="'+c.classes+'"'+(d?' for="'+d+'"':"")+">"+e+"</label>":'<span id="'+c._id+'" class="'+c.classes+'">'+e+"</span>"},bindStates:function(){var a=this;return a.state.on("change:text",function(b){a.innerHtml(a.encode(b.value)),a.state.get("rendered")&&a.updateLayoutRect()}),a._super()}})}),g("3h",["2j"],function(a){"use strict";return a.extend({Defaults:{role:"toolbar",layout:"flow"},init:function(a){var b=this;b._super(a),b.classes.add("toolbar")},postRender:function(){var a=this;return a.items().each(function(a){a.classes.add("toolbar-item")}),a._super()}})}),g("3i",["3h"],function(a){"use strict";return a.extend({Defaults:{role:"menubar",containerCls:"menubar",ariaRoot:!0,defaults:{type:"menubutton"}}})}),g("3j",["2v","2h","3i"],function(a,b,c){"use strict";function d(a,b){for(;a;){if(b===a)return!0;a=a.parentNode}return!1}var e=a.extend({init:function(a){var b=this;b._renderOpen=!0,b._super(a),a=b.settings,b.classes.add("menubtn"),a.fixedWidth&&b.classes.add("fixed-width"),b.aria("haspopup",!0),b.state.set("menu",a.menu||b.render())},showMenu:function(a){var c,d=this;return d.menu&&d.menu.visible()&&a!==!1?d.hideMenu():(d.menu||(c=d.state.get("menu")||[],c.length?c={type:"menu",items:c}:c.type=c.type||"menu",c.renderTo?d.menu=c.parent(d).show().renderTo():d.menu=b.create(c).parent(d).renderTo(),d.fire("createmenu"),d.menu.reflow(),d.menu.on("cancel",function(a){a.control.parent()===d.menu&&(a.stopPropagation(),d.focus(),d.hideMenu())}),d.menu.on("select",function(){d.focus()}),d.menu.on("show hide",function(a){a.control==d.menu&&d.activeMenu("show"==a.type),d.aria("expanded","show"==a.type)}).fire("show")),d.menu.show(),d.menu.layoutRect({w:d.layoutRect().w}),d.menu.moveRel(d.getEl(),d.isRtl()?["br-tr","tr-br"]:["bl-tl","tl-bl"]),void d.fire("showmenu"))},hideMenu:function(){var a=this;a.menu&&(a.menu.items().each(function(a){a.hideMenu&&a.hideMenu()}),a.menu.hide())},activeMenu:function(a){this.classes.toggle("active",a)},renderHtml:function(){var a,b=this,d=b._id,e=b.classPrefix,f=b.settings.icon,g=b.state.get("text"),h="";return a=b.settings.image,a?(f="none","string"!=typeof a&&(a=window.getSelection?a[0]:a[1]),a=" style=\"background-image: url('"+a+"')\""):a="",g&&(b.classes.add("btn-has-text"),h='<span class="'+e+'txt">'+b.encode(g)+"</span>"),f=b.settings.icon?e+"ico "+e+"i-"+f:"",b.aria("role",b.parent()instanceof c?"menuitem":"button"),'<div id="'+d+'" class="'+b.classes+'" tabindex="-1" aria-labelledby="'+d+'"><button id="'+d+'-open" role="presentation" type="button" tabindex="-1">'+(f?'<i class="'+f+'"'+a+"></i>":"")+h+' <i class="'+e+'caret"></i></button></div>'},postRender:function(){var a=this;return a.on("click",function(b){b.control===a&&d(b.target,a.getEl())&&(a.focus(),a.showMenu(!b.aria),b.aria&&a.menu.items().filter(":visible")[0].focus())}),a.on("mouseenter",function(b){var c,d=b.control,f=a.parent();d&&f&&d instanceof e&&d.parent()==f&&(f.items().filter("MenuButton").each(function(a){a.hideMenu&&a!=d&&(a.menu&&a.menu.visible()&&(c=!0),a.hideMenu())}),c&&(d.focus(),d.showMenu()))}),a._super()},bindStates:function(){var a=this;return a.state.on("change:menu",function(){a.menu&&a.menu.remove(),a.menu=null}),a._super()},remove:function(){this._super(),this.menu&&this.menu.remove()}});return e}),g("3k",["2r","2h","6","5"],function(a,b,c,d){"use strict";return a.extend({Defaults:{border:0,role:"menuitem"},init:function(a){var b,c=this;c._super(a),a=c.settings,c.classes.add("menu-item"),a.menu&&c.classes.add("menu-item-expand"),a.preview&&c.classes.add("menu-item-preview"),b=c.state.get("text"),"-"!==b&&"|"!==b||(c.classes.add("menu-item-sep"),c.aria("role","separator"),c.state.set("text","-")),a.selectable&&(c.aria("role","menuitemcheckbox"),c.classes.add("menu-item-checkbox"),a.icon="selected"),a.preview||a.selectable||c.classes.add("menu-item-normal"),c.on("mousedown",function(a){a.preventDefault()}),a.menu&&!a.ariaHideMenu&&c.aria("haspopup",!0)},hasMenus:function(){return!!this.settings.menu},showMenu:function(){var a,c=this,d=c.settings,e=c.parent();if(e.items().each(function(a){a!==c&&a.hideMenu()}),d.menu){a=c.menu,a?a.show():(a=d.menu,a.length?a={type:"menu",items:a}:a.type=a.type||"menu",e.settings.itemDefaults&&(a.itemDefaults=e.settings.itemDefaults),a=c.menu=b.create(a).parent(c).renderTo(),a.reflow(),a.on("cancel",function(b){b.stopPropagation(),c.focus(),a.hide()}),a.on("show hide",function(a){a.control.items&&a.control.items().each(function(a){a.active(a.settings.selected)})}).fire("show"),a.on("hide",function(b){b.control===a&&c.classes.remove("selected")}),a.submenu=!0),a._parentMenu=e,a.classes.add("menu-sub");var f=a.testMoveRel(c.getEl(),c.isRtl()?["tl-tr","bl-br","tr-tl","br-bl"]:["tr-tl","br-bl","tl-tr","bl-br"]);a.moveRel(c.getEl(),f),a.rel=f,f="menu-sub-"+f,a.classes.remove(a._lastRel).add(f),a._lastRel=f,c.classes.add("selected"),c.aria("expanded",!0)}},hideMenu:function(){var a=this;return a.menu&&(a.menu.items().each(function(a){a.hideMenu&&a.hideMenu()}),a.menu.hide(),a.aria("expanded",!1)),a},renderHtml:function(){function a(a){var b,d,e={};for(e=c.mac?{alt:"⌥",ctrl:"⌘",shift:"⇧",meta:"⌘"}:{meta:"Ctrl"},a=a.split("+"),b=0;b<a.length;b++)d=e[a[b].toLowerCase()],d&&(a[b]=d);return a.join("+")}function b(a){return a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function d(a){var c=h.match||"";return c?a.replace(new RegExp(b(c),"gi"),function(a){return"!mce~match["+a+"]mce~match!"}):a}function e(a){return a.replace(new RegExp(b("!mce~match["),"g"),"<b>").replace(new RegExp(b("]mce~match!"),"g"),"</b>")}var f=this,g=f._id,h=f.settings,i=f.classPrefix,j=f.state.get("text"),k=f.settings.icon,l="",m=h.shortcut,n=f.encode(h.url),o="";return k&&f.parent().classes.add("menu-has-icons"),h.image&&(l=" style=\"background-image: url('"+h.image+"')\""),m&&(m=a(m)),k=i+"ico "+i+"i-"+(f.settings.icon||"none"),o="-"!==j?'<i class="'+k+'"'+l+"></i>\xa0":"",j=e(f.encode(d(j))),n=e(f.encode(d(n))),'<div id="'+g+'" class="'+f.classes+'" tabindex="-1">'+o+("-"!==j?'<span id="'+g+'-text" class="'+i+'text">'+j+"</span>":"")+(m?'<div id="'+g+'-shortcut" class="'+i+'menu-shortcut">'+m+"</div>":"")+(h.menu?'<div class="'+i+'caret"></div>':"")+(n?'<div class="'+i+'menu-item-link">'+n+"</div>":"")+"</div>"},postRender:function(){var a=this,b=a.settings,c=b.textStyle;if("function"==typeof c&&(c=c.call(this)),c){var e=a.getEl("text");e&&e.setAttribute("style",c)}return a.on("mouseenter click",function(c){c.control===a&&(b.menu||"click"!==c.type?(a.showMenu(),c.aria&&a.menu.focus(!0)):(a.fire("select"),d.requestAnimationFrame(function(){a.parent().hideAll()})))}),a._super(),a},hover:function(){var a=this;return a.parent().items().each(function(a){a.classes.remove("selected")}),a.classes.toggle("selected",!0),a},active:function(a){return"undefined"!=typeof a&&this.aria("checked",a),this._super(a)},remove:function(){this._super(),this.menu&&this.menu.remove()}})}),g("3l",["a","2g","5"],function(a,b,c){"use strict";return function(d,e){var f,g,h=this,i=b.classPrefix;h.show=function(b,j){function k(){f&&(a(d).append('<div class="'+i+"throbber"+(e?" "+i+"throbber-inline":"")+'"></div>'),j&&j())}return h.hide(),f=!0,b?g=c.setTimeout(k,b):k(),h},h.hide=function(){var a=d.lastChild;return c.clearTimeout(g),a&&a.className.indexOf("throbber")!=-1&&a.parentNode.removeChild(a),f=!1,h}}}),g("3m",["2p","3k","3l","9"],function(a,b,c,d){"use strict";return a.extend({Defaults:{defaultType:"menuitem",border:1,layout:"stack",role:"application",bodyRole:"menu",ariaRoot:!0},init:function(a){var b=this;if(a.autohide=!0,a.constrainToViewport=!0,"function"==typeof a.items&&(a.itemsFactory=a.items,a.items=[]),a.itemDefaults)for(var c=a.items,e=c.length;e--;)c[e]=d.extend({},a.itemDefaults,c[e]);b._super(a),b.classes.add("menu")},repaint:function(){return this.classes.toggle("menu-align",!0), -this._super(),this.getEl().style.height="",this.getEl("body").style.height="",this},cancel:function(){var a=this;a.hideAll(),a.fire("select")},load:function(){function a(){e.throbber&&(e.throbber.hide(),e.throbber=null)}var b,d,e=this;d=e.settings.itemsFactory,d&&(e.throbber||(e.throbber=new c(e.getEl("body"),!0),0===e.items().length?(e.throbber.show(),e.fire("loading")):e.throbber.show(100,function(){e.items().remove(),e.fire("loading")}),e.on("hide close",a)),e.requestTime=b=(new Date).getTime(),e.settings.itemsFactory(function(c){return 0===c.length?void e.hide():void(e.requestTime===b&&(e.getEl().style.width="",e.getEl("body").style.width="",a(),e.items().remove(),e.getEl("body").innerHTML="",e.add(c),e.renderNew(),e.fire("loaded")))}))},hideAll:function(){var a=this;return this.find("menuitem").exec("hideMenu"),a._super()},preRender:function(){var a=this;return a.items().each(function(b){var c=b.settings;if(c.icon||c.image||c.selectable)return a._hasIcons=!0,!1}),a.settings.itemsFactory&&a.on("postrender",function(){a.settings.itemsFactory&&a.load()}),a._super()}})}),g("3n",["3j","3m"],function(a,b){"use strict";return a.extend({init:function(a){function b(c){for(var f=0;f<c.length;f++){if(d=c[f].selected||a.value===c[f].value)return e=e||c[f].text,g.state.set("value",c[f].value),!0;if(c[f].menu&&b(c[f].menu))return!0}}var c,d,e,f,g=this;g._super(a),a=g.settings,g._values=c=a.values,c&&("undefined"!=typeof a.value&&b(c),!d&&c.length>0&&(e=c[0].text,g.state.set("value",c[0].value)),g.state.set("menu",c)),g.state.set("text",a.text||e),g.classes.add("listbox"),g.on("select",function(b){var c=b.control;f&&(b.lastControl=f),a.multiple?c.active(!c.active()):g.value(b.control.value()),f=c})},bindStates:function(){function a(a,c){a instanceof b&&a.items().each(function(a){a.hasMenus()||a.active(a.value()===c)})}function c(a,b){var d;if(a)for(var e=0;e<a.length;e++){if(a[e].value===b)return a[e];if(a[e].menu&&(d=c(a[e].menu,b)))return d}}var d=this;return d.on("show",function(b){a(b.control,d.value())}),d.state.on("change:value",function(a){var b=c(d.state.get("menu"),a.value);b?d.text(b.text):d.text(d.settings.text)}),d._super()}})}),g("3o",["2x"],function(a){"use strict";return a.extend({Defaults:{classes:"radio",role:"radio"}})}),g("3p",["2r","2k"],function(a,b){"use strict";return a.extend({renderHtml:function(){var a=this,b=a.classPrefix;return a.classes.add("resizehandle"),"both"==a.settings.direction&&a.classes.add("resizehandle-both"),a.canFocus=!1,'<div id="'+a._id+'" class="'+a.classes+'"><i class="'+b+"ico "+b+'i-resize"></i></div>'},postRender:function(){var a=this;a._super(),a.resizeDragHelper=new b(this._id,{start:function(){a.fire("ResizeStart")},drag:function(b){"both"!=a.settings.direction&&(b.deltaX=0),a.fire("Resize",b)},stop:function(){a.fire("ResizeEnd")}})},remove:function(){return this.resizeDragHelper&&this.resizeDragHelper.destroy(),this._super()}})}),g("3q",["2r"],function(a){"use strict";function b(a){var b="";if(a)for(var c=0;c<a.length;c++)b+='<option value="'+a[c]+'">'+a[c]+"</option>";return b}return a.extend({Defaults:{classes:"selectbox",role:"selectbox",options:[]},init:function(a){var b=this;b._super(a),b.settings.size&&(b.size=b.settings.size),b.settings.options&&(b._options=b.settings.options),b.on("keydown",function(a){var c;13==a.keyCode&&(a.preventDefault(),b.parents().reverse().each(function(a){if(a.toJSON)return c=a,!1}),b.fire("submit",{data:c.toJSON()}))})},options:function(a){return arguments.length?(this.state.set("options",a),this):this.state.get("options")},renderHtml:function(){var a,c=this,d="";return a=b(c._options),c.size&&(d=' size = "'+c.size+'"'),'<select id="'+c._id+'" class="'+c.classes+'"'+d+">"+a+"</select>"},bindStates:function(){var a=this;return a.state.on("change:options",function(c){a.getEl().innerHTML=b(c.value)}),a._super()}})}),g("3r",["2r","2k","4q"],function(a,b,c){"use strict";function d(a,b,c){return a<b&&(a=b),a>c&&(a=c),a}function e(a,b,c){a.setAttribute("aria-"+b,c)}function f(a,b){var d,f,g,h,i,j;"v"==a.settings.orientation?(h="top",g="height",f="h"):(h="left",g="width",f="w"),j=a.getEl("handle"),d=(a.layoutRect()[f]||100)-c.getSize(j)[g],i=d*((b-a._minValue)/(a._maxValue-a._minValue))+"px",j.style[h]=i,j.style.height=a.layoutRect().h+"px",e(j,"valuenow",b),e(j,"valuetext",""+a.settings.previewFilter(b)),e(j,"valuemin",a._minValue),e(j,"valuemax",a._maxValue)}return a.extend({init:function(a){var b=this;a.previewFilter||(a.previewFilter=function(a){return Math.round(100*a)/100}),b._super(a),b.classes.add("slider"),"v"==a.orientation&&b.classes.add("vertical"),b._minValue=a.minValue||0,b._maxValue=a.maxValue||100,b._initValue=b.state.get("value")},renderHtml:function(){var a=this,b=a._id,c=a.classPrefix;return'<div id="'+b+'" class="'+a.classes+'"><div id="'+b+'-handle" class="'+c+'slider-handle" role="slider" tabindex="-1"></div></div>'},reset:function(){this.value(this._initValue).repaint()},postRender:function(){function a(a,b,c){return(c+a)/(b-a)}function e(a,b,c){return c*(b-a)-a}function f(b,c){function f(f){var g;g=n.value(),g=e(b,c,a(b,c,g)+.05*f),g=d(g,b,c),n.value(g),n.fire("dragstart",{value:g}),n.fire("drag",{value:g}),n.fire("dragend",{value:g})}n.on("keydown",function(a){switch(a.keyCode){case 37:case 38:f(-1);break;case 39:case 40:f(1)}})}function g(a,e,f){var g,h,i,o,p;n._dragHelper=new b(n._id,{handle:n._id+"-handle",start:function(a){g=a[j],h=parseInt(n.getEl("handle").style[k],10),i=(n.layoutRect()[m]||100)-c.getSize(f)[l],n.fire("dragstart",{value:p})},drag:function(b){var c=b[j]-g;o=d(h+c,0,i),f.style[k]=o+"px",p=a+o/i*(e-a),n.value(p),n.tooltip().text(""+n.settings.previewFilter(p)).show().moveRel(f,"bc tc"),n.fire("drag",{value:p})},stop:function(){n.tooltip().hide(),n.fire("dragend",{value:p})}})}var h,i,j,k,l,m,n=this;h=n._minValue,i=n._maxValue,"v"==n.settings.orientation?(j="screenY",k="top",l="height",m="h"):(j="screenX",k="left",l="width",m="w"),n._super(),f(h,i,n.getEl("handle")),g(h,i,n.getEl("handle"))},repaint:function(){this._super(),f(this,this.value())},bindStates:function(){var a=this;return a.state.on("change:value",function(b){f(a,b.value)}),a._super()}})}),g("3s",["2r"],function(a){"use strict";return a.extend({renderHtml:function(){var a=this;return a.classes.add("spacer"),a.canFocus=!1,'<div id="'+a._id+'" class="'+a.classes+'"></div>'}})}),g("3t",["3j","4q","a"],function(a,b,c){return a.extend({Defaults:{classes:"widget btn splitbtn",role:"button"},repaint:function(){var a,d,e=this,f=e.getEl(),g=e.layoutRect();return e._super(),a=f.firstChild,d=f.lastChild,c(a).css({width:g.w-b.getSize(d).width,height:g.h-2}),c(d).css({height:g.h-2}),e},activeMenu:function(a){var b=this;c(b.getEl().lastChild).toggleClass(b.classPrefix+"active",a)},renderHtml:function(){var a,b=this,c=b._id,d=b.classPrefix,e=b.state.get("icon"),f=b.state.get("text"),g="";return a=b.settings.image,a?(e="none","string"!=typeof a&&(a=window.getSelection?a[0]:a[1]),a=" style=\"background-image: url('"+a+"')\""):a="",e=b.settings.icon?d+"ico "+d+"i-"+e:"",f&&(b.classes.add("btn-has-text"),g='<span class="'+d+'txt">'+b.encode(f)+"</span>"),'<div id="'+c+'" class="'+b.classes+'" role="button" tabindex="-1"><button type="button" hidefocus="1" tabindex="-1">'+(e?'<i class="'+e+'"'+a+"></i>":"")+g+'</button><button type="button" class="'+d+'open" hidefocus="1" tabindex="-1">'+(b._menuBtnText?(e?"\xa0":"")+b._menuBtnText:"")+' <i class="'+d+'caret"></i></button></div>'},postRender:function(){var a=this,b=a.settings.onclick;return a.on("click",function(a){var c=a.target;if(a.control==this)for(;c;){if(a.aria&&"down"!=a.aria.key||"BUTTON"==c.nodeName&&c.className.indexOf("open")==-1)return a.stopImmediatePropagation(),void(b&&b.call(this,a));c=c.parentNode}}),delete a.settings.onclick,a._super()}})}),g("3u",["3b"],function(a){"use strict";return a.extend({Defaults:{containerClass:"stack-layout",controlClass:"stack-layout-item",endClass:"break"},isNative:function(){return!0}})}),g("3v",["2m","a","4q"],function(a,b,c){"use strict";return a.extend({Defaults:{layout:"absolute",defaults:{type:"panel"}},activateTab:function(a){var c;this.activeTabId&&(c=this.getEl(this.activeTabId),b(c).removeClass(this.classPrefix+"active"),c.setAttribute("aria-selected","false")),this.activeTabId="t"+a,c=this.getEl("t"+a),c.setAttribute("aria-selected","true"),b(c).addClass(this.classPrefix+"active"),this.items()[a].show().fire("showtab"),this.reflow(),this.items().each(function(b,c){a!=c&&b.hide()})},renderHtml:function(){var a=this,b=a._layout,c="",d=a.classPrefix;return a.preRender(),b.preRender(a),a.items().each(function(b,e){var f=a._id+"-t"+e;b.aria("role","tabpanel"),b.aria("labelledby",f),c+='<div id="'+f+'" class="'+d+'tab" unselectable="on" role="tab" aria-controls="'+b._id+'" aria-selected="false" tabIndex="-1">'+a.encode(b.settings.title)+"</div>"}),'<div id="'+a._id+'" class="'+a.classes+'" hidefocus="1" tabindex="-1"><div id="'+a._id+'-head" class="'+d+'tabs" role="tablist">'+c+'</div><div id="'+a._id+'-body" class="'+a.bodyClasses+'">'+b.renderHtml(a)+"</div></div>"},postRender:function(){var a=this;a._super(),a.settings.activeTab=a.settings.activeTab||0,a.activateTab(a.settings.activeTab),this.on("click",function(b){var c=b.target.parentNode;if(c&&c.id==a._id+"-head")for(var d=c.childNodes.length;d--;)c.childNodes[d]==b.target&&a.activateTab(d)})},initLayoutRect:function(){var a,b,d,e=this;b=c.getSize(e.getEl("head")).width,b=b<0?0:b,d=0,e.items().each(function(a){b=Math.max(b,a.layoutRect().minW),d=Math.max(d,a.layoutRect().minH)}),e.items().each(function(a){a.settings.x=0,a.settings.y=0,a.settings.w=b,a.settings.h=d,a.layoutRect({x:0,y:0,w:b,h:d})});var f=c.getSize(e.getEl("head")).height;return e.settings.minWidth=b,e.settings.minHeight=d+f,a=e._super(),a.deltaH+=f,a.innerH=a.h-a.deltaH,a}})}),g("3w",["2r","9","4q"],function(a,b,c){return a.extend({init:function(a){var b=this;b._super(a),b.classes.add("textbox"),a.multiline?b.classes.add("multiline"):(b.on("keydown",function(a){var c;13==a.keyCode&&(a.preventDefault(),b.parents().reverse().each(function(a){if(a.toJSON)return c=a,!1}),b.fire("submit",{data:c.toJSON()}))}),b.on("keyup",function(a){b.state.set("value",a.target.value)}))},repaint:function(){var a,b,c,d,e,f=this,g=0;a=f.getEl().style,b=f._layoutRect,e=f._lastRepaintRect||{};var h=document;return!f.settings.multiline&&h.all&&(!h.documentMode||h.documentMode<=8)&&(a.lineHeight=b.h-g+"px"),c=f.borderBox,d=c.left+c.right+8,g=c.top+c.bottom+(f.settings.multiline?8:0),b.x!==e.x&&(a.left=b.x+"px",e.x=b.x),b.y!==e.y&&(a.top=b.y+"px",e.y=b.y),b.w!==e.w&&(a.width=b.w-d+"px",e.w=b.w),b.h!==e.h&&(a.height=b.h-g+"px",e.h=b.h),f._lastRepaintRect=e,f.fire("repaint",{},!1),f},renderHtml:function(){var a,d,e=this,f=e.settings;return a={id:e._id,hidefocus:"1"},b.each(["rows","spellcheck","maxLength","size","readonly","min","max","step","list","pattern","placeholder","required","multiple"],function(b){a[b]=f[b]}),e.disabled()&&(a.disabled="disabled"),f.subtype&&(a.type=f.subtype),d=c.create(f.multiline?"textarea":"input",a),d.value=e.state.get("value"),d.className=e.classes,d.outerHTML},value:function(a){return arguments.length?(this.state.set("value",a),this):(this.state.get("rendered")&&this.state.set("value",this.getEl().value),this.state.get("value"))},postRender:function(){var a=this;a.getEl().value=a.state.get("value"),a._super(),a.$el.on("change",function(b){a.state.set("value",b.target.value),a.fire("change",b)})},bindStates:function(){var a=this;return a.state.on("change:value",function(b){a.getEl().value!=b.value&&(a.getEl().value=b.value)}),a.state.on("change:disabled",function(b){a.getEl().disabled=b.value}),a._super()},remove:function(){this.$el.off(),this._super()}})}),g("1f",["2d","2e","2f","2g","2h","2i","2j","2k","2l","2m","2n","2o","2p","23","24","2q","2r","2s","25","2t","2u","2v","2w","2x","2y","2z","30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f","3g","3h","3i","3j","3k","3l","3m","3n","3o","3p","3q","3r","3s","3t","3u","3v","3w"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,_,aa,ba,ca,da,ea){"use strict";var fa=function(a,b){e.add(a.split(".").pop(),b)},ga=function(a,b,c){var d,e;for(e=b.split(/[.\/]/),d=0;d<e.length-1;++d)void 0===a[e[d]]&&(a[e[d]]={}),a=a[e[d]];a[e[e.length-1]]=c,fa(b,c)},ha=function(fa){ga(fa,"ui.Selector",a),ga(fa,"ui.Collection",b),ga(fa,"ui.ReflowQueue",c),ga(fa,"ui.Control",d),ga(fa,"ui.Factory",e),ga(fa,"ui.KeyboardNavigation",f),ga(fa,"ui.Container",g),ga(fa,"ui.DragHelper",h),ga(fa,"ui.Scrollable",i),ga(fa,"ui.Panel",j),ga(fa,"ui.Movable",k),ga(fa,"ui.Resizable",l),ga(fa,"ui.FloatPanel",m),ga(fa,"ui.Window",n),ga(fa,"ui.MessageBox",o),ga(fa,"ui.Tooltip",p),ga(fa,"ui.Widget",q),ga(fa,"ui.Progress",r),ga(fa,"ui.Notification",s),ga(fa,"ui.Layout",t),ga(fa,"ui.AbsoluteLayout",u),ga(fa,"ui.Button",v),ga(fa,"ui.ButtonGroup",w),ga(fa,"ui.Checkbox",x),ga(fa,"ui.ComboBox",y),ga(fa,"ui.ColorBox",z),ga(fa,"ui.PanelButton",A),ga(fa,"ui.ColorButton",B),ga(fa,"ui.ColorPicker",C),ga(fa,"ui.Path",D),ga(fa,"ui.ElementPath",E),ga(fa,"ui.FormItem",F),ga(fa,"ui.Form",G),ga(fa,"ui.FieldSet",H),ga(fa,"ui.FilePicker",I),ga(fa,"ui.FitLayout",J),ga(fa,"ui.FlexLayout",K),ga(fa,"ui.FlowLayout",L),ga(fa,"ui.FormatControls",M),ga(fa,"ui.GridLayout",N),ga(fa,"ui.Iframe",O),ga(fa,"ui.InfoBox",P),ga(fa,"ui.Label",Q),ga(fa,"ui.Toolbar",R),ga(fa,"ui.MenuBar",S),ga(fa,"ui.MenuButton",T),ga(fa,"ui.MenuItem",U),ga(fa,"ui.Throbber",V),ga(fa,"ui.Menu",W),ga(fa,"ui.ListBox",X),ga(fa,"ui.Radio",Y),ga(fa,"ui.ResizeHandle",Z),ga(fa,"ui.SelectBox",$),ga(fa,"ui.Slider",_),ga(fa,"ui.Spacer",aa),ga(fa,"ui.SplitButton",ba),ga(fa,"ui.StackLayout",ca),ga(fa,"ui.TabPanel",da),ga(fa,"ui.TextBox",ea),ga(fa,"ui.Api",ia)},ia={appendTo:ha};return ia}),g("1",["3","4","5","6","7","8","9","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W){var X=O,Y=function(a,b,c){var d,e;for(e=b.split(/[.\/]/),d=0;d<e.length-1;++d)void 0===a[e[d]]&&(a[e[d]]={}),a=a[e[d]];a[e[e.length-1]]=c};return Y(X,"geom.Rect",a),Y(X,"util.Promise",b),Y(X,"util.Delay",c),Y(X,"Env",d),Y(X,"dom.EventUtils",e),Y(X,"dom.Sizzle",f),Y(X,"util.Tools",g),Y(X,"dom.DomQuery",h),Y(X,"html.Styles",i),Y(X,"dom.TreeWalker",j),Y(X,"html.Entities",k),Y(X,"dom.DOMUtils",l),Y(X,"dom.ScriptLoader",m),Y(X,"AddOnManager",n),Y(X,"dom.RangeUtils",o),Y(X,"html.Node",p),Y(X,"html.Schema",q),Y(X,"html.SaxParser",r),Y(X,"html.DomParser",s),Y(X,"html.Writer",t),Y(X,"html.Serializer",u),Y(X,"dom.Serializer",v),Y(X,"util.VK",w),Y(X,"dom.ControlSelection",x),Y(X,"dom.BookmarkManager",y),Y(X,"dom.Selection",z),Y(X,"Formatter",A),Y(X,"UndoManager",B),Y(X,"EditorCommands",C),Y(X,"util.URI",D),Y(X,"util.Class",E),Y(X,"util.EventDispatcher",F),Y(X,"util.Observable",G),Y(X,"WindowManager",H),Y(X,"NotificationManager",I),Y(X,"EditorObservable",J),Y(X,"Shortcuts",K),Y(X,"Editor",L),Y(X,"util.I18n",M),Y(X,"FocusManager",N),Y(X,"EditorManager",O),Y(X,"util.XHR",P),Y(X,"util.JSON",Q),Y(X,"util.JSONRequest",R),Y(X,"util.JSONP",S),Y(X,"util.LocalStorage",T),Y(X,"Compat",U),Y(X,"util.Color",V),W.appendTo(X),U.register(X),X}),g("2",[],function(){var a=this||window,b=function(b){"function"==typeof a.define&&(a.define.amd||(a.define("ephox/tinymce",[],function(){return b}),a.define("17",[],function(){return b}))),"object"==typeof module&&(module.exports=b)};return{exposeToModuleLoaders:b}}),g("0",["1","2"],function(a,b){return function(){return window.tinymce=a,window.tinyMCE=a,b.exposeToModuleLoaders(a),a}}),d("0")()}(); \ No newline at end of file +(function() { + + var defs = {}; // id -> {dependencies, definition, instance (possibly undefined)} + + // Used when there is no 'main' module. + // The name is probably (hopefully) unique so minification removes for releases. + var register_3795 = function(id) { + var module = dem(id); + var fragments = id.split('.'); + var target = Function('return this;')(); + for (var i = 0; i < fragments.length - 1; ++i) { + if (target[fragments[i]] === undefined) + target[fragments[i]] = {}; + target = target[fragments[i]]; + } + target[fragments[fragments.length - 1]] = module; + }; + + var instantiate = function(id) { + var actual = defs[id]; + var dependencies = actual.deps; + var definition = actual.defn; + var len = dependencies.length; + var instances = new Array(len); + for (var i = 0; i < len; ++i) + instances[i] = dem(dependencies[i]); + var defResult = definition.apply(null, instances); + if (defResult === undefined) + throw 'module [' + id + '] returned undefined'; + actual.instance = defResult; + }; + + var def = function(id, dependencies, definition) { + if (typeof id !== 'string') + throw 'module id must be a string'; + else if (dependencies === undefined) + throw 'no dependencies for ' + id; + else if (definition === undefined) + throw 'no definition function for ' + id; + defs[id] = { + deps: dependencies, + defn: definition, + instance: undefined + }; + }; + + var dem = function(id) { + var actual = defs[id]; + if (actual === undefined) + throw 'module [' + id + '] was undefined'; + else if (actual.instance === undefined) + instantiate(id); + return actual.instance; + }; + + var req = function(ids, callback) { + var len = ids.length; + var instances = new Array(len); + for (var i = 0; i < len; ++i) + instances[i] = dem(ids[i]); + callback.apply(null, instances); + }; + + var ephox = {}; + + ephox.bolt = { + module: { + api: { + define: def, + require: req, + demand: dem + } + } + }; + + var define = def; + var require = req; + var demand = dem; + // this helps with minification when using a lot of global references + var defineGlobal = function(id, ref) { + define(id, [], function() { + return ref; + }); + }; + /*jsc + ["tinymce.core.api.Main","tinymce.core.api.Tinymce","tinymce.core.Register","tinymce.core.geom.Rect","tinymce.core.util.Promise","tinymce.core.util.Delay","tinymce.core.Env","tinymce.core.dom.EventUtils","tinymce.core.dom.Sizzle","tinymce.core.util.Tools","tinymce.core.dom.DomQuery","tinymce.core.html.Styles","tinymce.core.dom.TreeWalker","tinymce.core.html.Entities","tinymce.core.dom.DOMUtils","tinymce.core.dom.ScriptLoader","tinymce.core.AddOnManager","tinymce.core.dom.RangeUtils","tinymce.core.html.Node","tinymce.core.html.Schema","tinymce.core.html.SaxParser","tinymce.core.html.DomParser","tinymce.core.html.Writer","tinymce.core.html.Serializer","tinymce.core.dom.Serializer","tinymce.core.util.VK","tinymce.core.dom.ControlSelection","tinymce.core.dom.BookmarkManager","tinymce.core.dom.Selection","tinymce.core.Formatter","tinymce.core.UndoManager","tinymce.core.EditorCommands","tinymce.core.util.URI","tinymce.core.util.Class","tinymce.core.util.EventDispatcher","tinymce.core.util.Observable","tinymce.core.WindowManager","tinymce.core.NotificationManager","tinymce.core.EditorObservable","tinymce.core.Shortcuts","tinymce.core.Editor","tinymce.core.util.I18n","tinymce.core.FocusManager","tinymce.core.EditorManager","tinymce.core.util.XHR","tinymce.core.util.JSON","tinymce.core.util.JSONRequest","tinymce.core.util.JSONP","tinymce.core.util.LocalStorage","tinymce.core.api.Compat","tinymce.core.util.Color","tinymce.core.ui.Api","tinymce.core.util.Arr","tinymce.core.dom.Range","tinymce.core.dom.StyleSheetLoader","tinymce.core.dom.NodeType","tinymce.core.caret.CaretContainer","tinymce.core.text.Zwsp","ephox.katamari.api.Fun","tinymce.core.dom.RangePoint","tinymce.core.caret.CaretBookmark","tinymce.core.caret.CaretPosition","ephox.sugar.api.dom.Compare","ephox.sugar.api.node.Element","tinymce.core.dom.ScrollIntoView","tinymce.core.dom.TridentSelection","tinymce.core.selection.FragmentReader","tinymce.core.dom.ElementUtils","tinymce.core.util.Fun","tinymce.core.fmt.Preview","tinymce.core.fmt.Hooks","tinymce.core.undo.Levels","tinymce.core.delete.DeleteCommands","tinymce.core.InsertContent","global!document","tinymce.core.ui.Window","tinymce.core.ui.MessageBox","tinymce.core.ui.Notification","tinymce.core.EditorSettings","tinymce.core.init.Render","tinymce.core.Mode","tinymce.core.ui.Sidebar","tinymce.core.util.Uuid","tinymce.core.ErrorReporter","tinymce.core.LegacyInput","tinymce.core.ui.Selector","tinymce.core.ui.Collection","tinymce.core.ui.ReflowQueue","tinymce.core.ui.Control","tinymce.core.ui.Factory","tinymce.core.ui.KeyboardNavigation","tinymce.core.ui.Container","tinymce.core.ui.DragHelper","tinymce.core.ui.Scrollable","tinymce.core.ui.Panel","tinymce.core.ui.Movable","tinymce.core.ui.Resizable","tinymce.core.ui.FloatPanel","tinymce.core.ui.Tooltip","tinymce.core.ui.Widget","tinymce.core.ui.Progress","tinymce.core.ui.Layout","tinymce.core.ui.AbsoluteLayout","tinymce.core.ui.Button","tinymce.core.ui.ButtonGroup","tinymce.core.ui.Checkbox","tinymce.core.ui.ComboBox","tinymce.core.ui.ColorBox","tinymce.core.ui.PanelButton","tinymce.core.ui.ColorButton","tinymce.core.ui.ColorPicker","tinymce.core.ui.Path","tinymce.core.ui.ElementPath","tinymce.core.ui.FormItem","tinymce.core.ui.Form","tinymce.core.ui.FieldSet","tinymce.core.ui.FilePicker","tinymce.core.ui.FitLayout","tinymce.core.ui.FlexLayout","tinymce.core.ui.FlowLayout","tinymce.core.ui.FormatControls","tinymce.core.ui.GridLayout","tinymce.core.ui.Iframe","tinymce.core.ui.InfoBox","tinymce.core.ui.Label","tinymce.core.ui.Toolbar","tinymce.core.ui.MenuBar","tinymce.core.ui.MenuButton","tinymce.core.ui.MenuItem","tinymce.core.ui.Throbber","tinymce.core.ui.Menu","tinymce.core.ui.ListBox","tinymce.core.ui.Radio","tinymce.core.ui.ResizeHandle","tinymce.core.ui.SelectBox","tinymce.core.ui.Slider","tinymce.core.ui.Spacer","tinymce.core.ui.SplitButton","tinymce.core.ui.StackLayout","tinymce.core.ui.TabPanel","tinymce.core.ui.TextBox","ephox.katamari.api.Arr","global!Array","global!Error","ephox.katamari.api.Future","ephox.katamari.api.Futures","ephox.katamari.api.Result","tinymce.core.geom.ClientRect","tinymce.core.caret.CaretCandidate","tinymce.core.text.ExtendingChar","ephox.sand.api.Node","ephox.sand.api.PlatformDetection","ephox.sugar.api.search.Selectors","global!console","ephox.sugar.api.dom.Insert","ephox.sugar.api.dom.Replication","ephox.sugar.api.node.Fragment","ephox.sugar.api.node.Node","tinymce.core.dom.ElementType","tinymce.core.dom.Parents","tinymce.core.selection.SelectionUtils","tinymce.core.undo.Fragments","tinymce.core.delete.BlockBoundaryDelete","tinymce.core.delete.BlockRangeDelete","tinymce.core.delete.CefDelete","tinymce.core.delete.InlineBoundaryDelete","tinymce.core.caret.CaretWalker","tinymce.core.dom.RangeNormalizer","tinymce.core.InsertList","tinymce.core.data.ObservableObject","tinymce.core.ui.DomUtils","tinymce.core.ui.BoxUtils","tinymce.core.ui.ClassList","global!window","tinymce.core.init.Init","tinymce.core.PluginManager","tinymce.core.ThemeManager","tinymce.core.content.LinkTargets","tinymce.core.fmt.FontInfo","ephox.katamari.api.Option","global!String","ephox.katamari.api.LazyValue","ephox.katamari.async.Bounce","ephox.katamari.async.AsyncValues","ephox.sand.util.Global","ephox.katamari.api.Thunk","ephox.sand.core.PlatformDetection","global!navigator","ephox.sugar.api.node.NodeTypes","ephox.sugar.api.search.Traverse","ephox.sugar.api.properties.Attr","ephox.sugar.api.dom.InsertAll","ephox.sugar.api.dom.Remove","ephox.katamari.api.Options","tinymce.core.undo.Diff","tinymce.core.delete.BlockBoundary","tinymce.core.delete.MergeBlocks","tinymce.core.delete.DeleteUtils","tinymce.core.caret.CaretUtils","tinymce.core.delete.CefDeleteAction","tinymce.core.delete.DeleteElement","tinymce.core.caret.CaretFinder","tinymce.core.keyboard.BoundaryCaret","tinymce.core.keyboard.BoundaryLocation","tinymce.core.keyboard.BoundarySelection","tinymce.core.keyboard.InlineUtils","tinymce.core.data.Binding","tinymce.core.init.InitContentBody","global!Object","global!setTimeout","ephox.katamari.api.Resolve","ephox.sand.core.Browser","ephox.sand.core.OperatingSystem","ephox.sand.detect.DeviceType","ephox.sand.detect.UaString","ephox.sand.info.PlatformInfo","ephox.katamari.api.Type","ephox.katamari.api.Struct","ephox.sugar.alien.Recurse","ephox.katamari.api.Obj","ephox.sugar.api.search.PredicateFind","tinymce.core.dom.Empty","ephox.katamari.api.Adt","tinymce.core.text.Bidi","tinymce.core.caret.CaretContainerInline","tinymce.core.caret.CaretContainerRemove","tinymce.core.util.LazyEvaluator","ephox.katamari.api.Cell","tinymce.core.caret.CaretContainerInput","tinymce.core.EditorUpload","tinymce.core.ForceBlocks","tinymce.core.keyboard.KeyboardOverrides","tinymce.core.NodeChange","tinymce.core.SelectionOverrides","tinymce.core.util.Quirks","ephox.katamari.api.Global","ephox.sand.detect.Version","ephox.katamari.api.Strings","ephox.katamari.data.Immutable","ephox.katamari.data.MixedBag","ephox.sugar.api.node.Body","ephox.sugar.impl.ClosestOrAncestor","ephox.sugar.api.search.SelectorExists","tinymce.core.file.Uploader","tinymce.core.file.ImageScanner","tinymce.core.file.BlobCache","tinymce.core.file.UploadStatus","tinymce.core.keyboard.ArrowKeys","tinymce.core.keyboard.DeleteBackspaceKeys","tinymce.core.keyboard.EnterKey","tinymce.core.keyboard.SpaceKey","tinymce.core.caret.FakeCaret","tinymce.core.caret.LineUtils","tinymce.core.DragDropOverrides","tinymce.core.EditorView","tinymce.core.keyboard.CefUtils","tinymce.core.dom.NodePath","global!Number","ephox.katamari.str.StrAppend","ephox.katamari.str.StringParts","ephox.katamari.util.BagUtils","ephox.sugar.api.search.SelectorFind","tinymce.core.file.Conversions","global!URL","tinymce.core.keyboard.CefNavigation","tinymce.core.keyboard.MatchKeys","tinymce.core.keyboard.InsertSpace","tinymce.core.dom.Dimensions","tinymce.core.dom.MousePosition","ephox.sugar.api.properties.Css","tinymce.core.caret.LineWalker","ephox.katamari.api.Merger","ephox.sugar.impl.Style"] + jsc*/ + /** + * Rect.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Contains various tools for rect/position calculation. + * + * @class tinymce.geom.Rect + */ + define( + 'tinymce.core.geom.Rect', [], + function() { + "use strict"; + + var min = Math.min, + max = Math.max, + round = Math.round; + + /** + * Returns the rect positioned based on the relative position name + * to the target rect. + * + * @method relativePosition + * @param {Rect} rect Source rect to modify into a new rect. + * @param {Rect} targetRect Rect to move relative to based on the rel option. + * @param {String} rel Relative position. For example: tr-bl. + */ + function relativePosition(rect, targetRect, rel) { + var x, y, w, h, targetW, targetH; + + x = targetRect.x; + y = targetRect.y; + w = rect.w; + h = rect.h; + targetW = targetRect.w; + targetH = targetRect.h; + + rel = (rel || '').split(''); + + if (rel[0] === 'b') { + y += targetH; + } + + if (rel[1] === 'r') { + x += targetW; + } + + if (rel[0] === 'c') { + y += round(targetH / 2); + } + + if (rel[1] === 'c') { + x += round(targetW / 2); + } + + if (rel[3] === 'b') { + y -= h; + } + + if (rel[4] === 'r') { + x -= w; + } + + if (rel[3] === 'c') { + y -= round(h / 2); + } + + if (rel[4] === 'c') { + x -= round(w / 2); + } + + return create(x, y, w, h); + } + + /** + * Tests various positions to get the most suitable one. + * + * @method findBestRelativePosition + * @param {Rect} rect Rect to use as source. + * @param {Rect} targetRect Rect to move relative to. + * @param {Rect} constrainRect Rect to constrain within. + * @param {Array} rels Array of relative positions to test against. + */ + function findBestRelativePosition(rect, targetRect, constrainRect, rels) { + var pos, i; + + for (i = 0; i < rels.length; i++) { + pos = relativePosition(rect, targetRect, rels[i]); + + if (pos.x >= constrainRect.x && pos.x + pos.w <= constrainRect.w + constrainRect.x && + pos.y >= constrainRect.y && pos.y + pos.h <= constrainRect.h + constrainRect.y) { + return rels[i]; + } + } + + return null; + } + + /** + * Inflates the rect in all directions. + * + * @method inflate + * @param {Rect} rect Rect to expand. + * @param {Number} w Relative width to expand by. + * @param {Number} h Relative height to expand by. + * @return {Rect} New expanded rect. + */ + function inflate(rect, w, h) { + return create(rect.x - w, rect.y - h, rect.w + w * 2, rect.h + h * 2); + } + + /** + * Returns the intersection of the specified rectangles. + * + * @method intersect + * @param {Rect} rect The first rectangle to compare. + * @param {Rect} cropRect The second rectangle to compare. + * @return {Rect} The intersection of the two rectangles or null if they don't intersect. + */ + function intersect(rect, cropRect) { + var x1, y1, x2, y2; + + x1 = max(rect.x, cropRect.x); + y1 = max(rect.y, cropRect.y); + x2 = min(rect.x + rect.w, cropRect.x + cropRect.w); + y2 = min(rect.y + rect.h, cropRect.y + cropRect.h); + + if (x2 - x1 < 0 || y2 - y1 < 0) { + return null; + } + + return create(x1, y1, x2 - x1, y2 - y1); + } + + /** + * Returns a rect clamped within the specified clamp rect. This forces the + * rect to be inside the clamp rect. + * + * @method clamp + * @param {Rect} rect Rectangle to force within clamp rect. + * @param {Rect} clampRect Rectable to force within. + * @param {Boolean} fixedSize True/false if size should be fixed. + * @return {Rect} Clamped rect. + */ + function clamp(rect, clampRect, fixedSize) { + var underflowX1, underflowY1, overflowX2, overflowY2, + x1, y1, x2, y2, cx2, cy2; + + x1 = rect.x; + y1 = rect.y; + x2 = rect.x + rect.w; + y2 = rect.y + rect.h; + cx2 = clampRect.x + clampRect.w; + cy2 = clampRect.y + clampRect.h; + + underflowX1 = max(0, clampRect.x - x1); + underflowY1 = max(0, clampRect.y - y1); + overflowX2 = max(0, x2 - cx2); + overflowY2 = max(0, y2 - cy2); + + x1 += underflowX1; + y1 += underflowY1; + + if (fixedSize) { + x2 += underflowX1; + y2 += underflowY1; + x1 -= overflowX2; + y1 -= overflowY2; + } + + x2 -= overflowX2; + y2 -= overflowY2; + + return create(x1, y1, x2 - x1, y2 - y1); + } + + /** + * Creates a new rectangle object. + * + * @method create + * @param {Number} x Rectangle x location. + * @param {Number} y Rectangle y location. + * @param {Number} w Rectangle width. + * @param {Number} h Rectangle height. + * @return {Rect} New rectangle object. + */ + function create(x, y, w, h) { + return { + x: x, + y: y, + w: w, + h: h + }; + } + + /** + * Creates a new rectangle object form a clientRects object. + * + * @method fromClientRect + * @param {ClientRect} clientRect DOM ClientRect object. + * @return {Rect} New rectangle object. + */ + function fromClientRect(clientRect) { + return create(clientRect.left, clientRect.top, clientRect.width, clientRect.height); + } + + return { + inflate: inflate, + relativePosition: relativePosition, + findBestRelativePosition: findBestRelativePosition, + intersect: intersect, + clamp: clamp, + create: create, + fromClientRect: fromClientRect + }; + } + ); + + /** + * Promise.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * Promise polyfill under MIT license: https://github.com/taylorhakes/promise-polyfill + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /* eslint-disable */ + /* jshint ignore:start */ + + /** + * Modifed to be a feature fill and wrapped as tinymce module. + */ + define( + 'tinymce.core.util.Promise', [], + function() { + if (window.Promise) { + return window.Promise; + } + + // Use polyfill for setImmediate for performance gains + var asap = Promise.immediateFn || (typeof setImmediate === 'function' && setImmediate) || + function(fn) { + setTimeout(fn, 1); + }; + + // Polyfill for Function.prototype.bind + function bind(fn, thisArg) { + return function() { + fn.apply(thisArg, arguments); + }; + } + + var isArray = Array.isArray || function(value) { + return Object.prototype.toString.call(value) === "[object Array]"; + }; + + function Promise(fn) { + if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new'); + if (typeof fn !== 'function') throw new TypeError('not a function'); + this._state = null; + this._value = null; + this._deferreds = []; + + doResolve(fn, bind(resolve, this), bind(reject, this)); + } + + function handle(deferred) { + var me = this; + if (this._state === null) { + this._deferreds.push(deferred); + return; + } + asap(function() { + var cb = me._state ? deferred.onFulfilled : deferred.onRejected; + if (cb === null) { + (me._state ? deferred.resolve : deferred.reject)(me._value); + return; + } + var ret; + try { + ret = cb(me._value); + } catch (e) { + deferred.reject(e); + return; + } + deferred.resolve(ret); + }); + } + + function resolve(newValue) { + try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure + if (newValue === this) throw new TypeError('A promise cannot be resolved with itself.'); + if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { + var then = newValue.then; + if (typeof then === 'function') { + doResolve(bind(then, newValue), bind(resolve, this), bind(reject, this)); + return; + } + } + this._state = true; + this._value = newValue; + finale.call(this); + } catch (e) { + reject.call(this, e); + } + } + + function reject(newValue) { + this._state = false; + this._value = newValue; + finale.call(this); + } + + function finale() { + for (var i = 0, len = this._deferreds.length; i < len; i++) { + handle.call(this, this._deferreds[i]); + } + this._deferreds = null; + } + + function Handler(onFulfilled, onRejected, resolve, reject) { + this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; + this.onRejected = typeof onRejected === 'function' ? onRejected : null; + this.resolve = resolve; + this.reject = reject; + } + + /** + * Take a potentially misbehaving resolver function and make sure + * onFulfilled and onRejected are only called once. + * + * Makes no guarantees about asynchrony. + */ + function doResolve(fn, onFulfilled, onRejected) { + var done = false; + try { + fn(function(value) { + if (done) return; + done = true; + onFulfilled(value); + }, function(reason) { + if (done) return; + done = true; + onRejected(reason); + }); + } catch (ex) { + if (done) return; + done = true; + onRejected(ex); + } + } + + Promise.prototype['catch'] = function(onRejected) { + return this.then(null, onRejected); + }; + + Promise.prototype.then = function(onFulfilled, onRejected) { + var me = this; + return new Promise(function(resolve, reject) { + handle.call(me, new Handler(onFulfilled, onRejected, resolve, reject)); + }); + }; + + Promise.all = function() { + var args = Array.prototype.slice.call(arguments.length === 1 && isArray(arguments[0]) ? arguments[0] : arguments); + + return new Promise(function(resolve, reject) { + if (args.length === 0) return resolve([]); + var remaining = args.length; + + function res(i, val) { + try { + if (val && (typeof val === 'object' || typeof val === 'function')) { + var then = val.then; + if (typeof then === 'function') { + then.call(val, function(val) { + res(i, val); + }, reject); + return; + } + } + args[i] = val; + if (--remaining === 0) { + resolve(args); + } + } catch (ex) { + reject(ex); + } + } + for (var i = 0; i < args.length; i++) { + res(i, args[i]); + } + }); + }; + + Promise.resolve = function(value) { + if (value && typeof value === 'object' && value.constructor === Promise) { + return value; + } + + return new Promise(function(resolve) { + resolve(value); + }); + }; + + Promise.reject = function(value) { + return new Promise(function(resolve, reject) { + reject(value); + }); + }; + + Promise.race = function(values) { + return new Promise(function(resolve, reject) { + for (var i = 0, len = values.length; i < len; i++) { + values[i].then(resolve, reject); + } + }); + }; + + return Promise; + } + ); + + /* jshint ignore:end */ + /* eslint-enable */ + /** + * Delay.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Utility class for working with delayed actions like setTimeout. + * + * @class tinymce.util.Delay + */ + define( + 'tinymce.core.util.Delay', [ + "tinymce.core.util.Promise" + ], + function(Promise) { + var requestAnimationFramePromise; + + function requestAnimationFrame(callback, element) { + var i, requestAnimationFrameFunc = window.requestAnimationFrame, + vendors = ['ms', 'moz', 'webkit']; + + function featurefill(callback) { + window.setTimeout(callback, 0); + } + + for (i = 0; i < vendors.length && !requestAnimationFrameFunc; i++) { + requestAnimationFrameFunc = window[vendors[i] + 'RequestAnimationFrame']; + } + + if (!requestAnimationFrameFunc) { + requestAnimationFrameFunc = featurefill; + } + + requestAnimationFrameFunc(callback, element); + } + + function wrappedSetTimeout(callback, time) { + if (typeof time != 'number') { + time = 0; + } + + return setTimeout(callback, time); + } + + function wrappedSetInterval(callback, time) { + if (typeof time != 'number') { + time = 1; // IE 8 needs it to be > 0 + } + + return setInterval(callback, time); + } + + function wrappedClearTimeout(id) { + return clearTimeout(id); + } + + function wrappedClearInterval(id) { + return clearInterval(id); + } + + function debounce(callback, time) { + var timer, func; + + func = function() { + var args = arguments; + + clearTimeout(timer); + + timer = wrappedSetTimeout(function() { + callback.apply(this, args); + }, time); + }; + + func.stop = function() { + clearTimeout(timer); + }; + + return func; + } + + return { + /** + * Requests an animation frame and fallbacks to a timeout on older browsers. + * + * @method requestAnimationFrame + * @param {function} callback Callback to execute when a new frame is available. + * @param {DOMElement} element Optional element to scope it to. + */ + requestAnimationFrame: function(callback, element) { + if (requestAnimationFramePromise) { + requestAnimationFramePromise.then(callback); + return; + } + + requestAnimationFramePromise = new Promise(function(resolve) { + if (!element) { + element = document.body; + } + + requestAnimationFrame(resolve, element); + }).then(callback); + }, + + /** + * Sets a timer in ms and executes the specified callback when the timer runs out. + * + * @method setTimeout + * @param {function} callback Callback to execute when timer runs out. + * @param {Number} time Optional time to wait before the callback is executed, defaults to 0. + * @return {Number} Timeout id number. + */ + setTimeout: wrappedSetTimeout, + + /** + * Sets an interval timer in ms and executes the specified callback at every interval of that time. + * + * @method setInterval + * @param {function} callback Callback to execute when interval time runs out. + * @param {Number} time Optional time to wait before the callback is executed, defaults to 0. + * @return {Number} Timeout id number. + */ + setInterval: wrappedSetInterval, + + /** + * Sets an editor timeout it's similar to setTimeout except that it checks if the editor instance is + * still alive when the callback gets executed. + * + * @method setEditorTimeout + * @param {tinymce.Editor} editor Editor instance to check the removed state on. + * @param {function} callback Callback to execute when timer runs out. + * @param {Number} time Optional time to wait before the callback is executed, defaults to 0. + * @return {Number} Timeout id number. + */ + setEditorTimeout: function(editor, callback, time) { + return wrappedSetTimeout(function() { + if (!editor.removed) { + callback(); + } + }, time); + }, + + /** + * Sets an interval timer it's similar to setInterval except that it checks if the editor instance is + * still alive when the callback gets executed. + * + * @method setEditorInterval + * @param {function} callback Callback to execute when interval time runs out. + * @param {Number} time Optional time to wait before the callback is executed, defaults to 0. + * @return {Number} Timeout id number. + */ + setEditorInterval: function(editor, callback, time) { + var timer; + + timer = wrappedSetInterval(function() { + if (!editor.removed) { + callback(); + } else { + clearInterval(timer); + } + }, time); + + return timer; + }, + + /** + * Creates debounced callback function that only gets executed once within the specified time. + * + * @method debounce + * @param {function} callback Callback to execute when timer finishes. + * @param {Number} time Optional time to wait before the callback is executed, defaults to 0. + * @return {Function} debounced function callback. + */ + debounce: debounce, + + // Throttle needs to be debounce due to backwards compatibility. + throttle: debounce, + + /** + * Clears an interval timer so it won't execute. + * + * @method clearInterval + * @param {Number} Interval timer id number. + */ + clearInterval: wrappedClearInterval, + + /** + * Clears an timeout timer so it won't execute. + * + * @method clearTimeout + * @param {Number} Timeout timer id number. + */ + clearTimeout: wrappedClearTimeout + }; + } + ); + + /** + * Env.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class contains various environment constants like browser versions etc. + * Normally you don't want to sniff specific browser versions but sometimes you have + * to when it's impossible to feature detect. So use this with care. + * + * @class tinymce.Env + * @static + */ + define( + 'tinymce.core.Env', [], + function() { + var nav = navigator, + userAgent = nav.userAgent; + var opera, webkit, ie, ie11, ie12, gecko, mac, iDevice, android, fileApi, phone, tablet, windowsPhone; + + function matchMediaQuery(query) { + return "matchMedia" in window ? matchMedia(query).matches : false; + } + + opera = window.opera && window.opera.buildNumber; + android = /Android/.test(userAgent); + webkit = /WebKit/.test(userAgent); + ie = !webkit && !opera && (/MSIE/gi).test(userAgent) && (/Explorer/gi).test(nav.appName); + ie = ie && /MSIE (\w+)\./.exec(userAgent)[1]; + ie11 = userAgent.indexOf('Trident/') != -1 && (userAgent.indexOf('rv:') != -1 || nav.appName.indexOf('Netscape') != -1) ? 11 : false; + ie12 = (userAgent.indexOf('Edge/') != -1 && !ie && !ie11) ? 12 : false; + ie = ie || ie11 || ie12; + gecko = !webkit && !ie11 && /Gecko/.test(userAgent); + mac = userAgent.indexOf('Mac') != -1; + iDevice = /(iPad|iPhone)/.test(userAgent); + fileApi = "FormData" in window && "FileReader" in window && "URL" in window && !!URL.createObjectURL; + phone = matchMediaQuery("only screen and (max-device-width: 480px)") && (android || iDevice); + tablet = matchMediaQuery("only screen and (min-width: 800px)") && (android || iDevice); + windowsPhone = userAgent.indexOf('Windows Phone') != -1; + + if (ie12) { + webkit = false; + } + + // Is a iPad/iPhone and not on iOS5 sniff the WebKit version since older iOS WebKit versions + // says it has contentEditable support but there is no visible caret. + var contentEditable = !iDevice || fileApi || userAgent.match(/AppleWebKit\/(\d*)/)[1] >= 534; + + return { + /** + * Constant that is true if the browser is Opera. + * + * @property opera + * @type Boolean + * @final + */ + opera: opera, + + /** + * Constant that is true if the browser is WebKit (Safari/Chrome). + * + * @property webKit + * @type Boolean + * @final + */ + webkit: webkit, + + /** + * Constant that is more than zero if the browser is IE. + * + * @property ie + * @type Boolean + * @final + */ + ie: ie, + + /** + * Constant that is true if the browser is Gecko. + * + * @property gecko + * @type Boolean + * @final + */ + gecko: gecko, + + /** + * Constant that is true if the os is Mac OS. + * + * @property mac + * @type Boolean + * @final + */ + mac: mac, + + /** + * Constant that is true if the os is iOS. + * + * @property iOS + * @type Boolean + * @final + */ + iOS: iDevice, + + /** + * Constant that is true if the os is android. + * + * @property android + * @type Boolean + * @final + */ + android: android, + + /** + * Constant that is true if the browser supports editing. + * + * @property contentEditable + * @type Boolean + * @final + */ + contentEditable: contentEditable, + + /** + * Transparent image data url. + * + * @property transparentSrc + * @type Boolean + * @final + */ + transparentSrc: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7", + + /** + * Returns true/false if the browser can or can't place the caret after a inline block like an image. + * + * @property noCaretAfter + * @type Boolean + * @final + */ + caretAfter: ie != 8, + + /** + * Constant that is true if the browser supports native DOM Ranges. IE 9+. + * + * @property range + * @type Boolean + */ + range: window.getSelection && "Range" in window, + + /** + * Returns the IE document mode for non IE browsers this will fake IE 10. + * + * @property documentMode + * @type Number + */ + documentMode: ie && !ie12 ? (document.documentMode || 7) : 10, + + /** + * Constant that is true if the browser has a modern file api. + * + * @property fileApi + * @type Boolean + */ + fileApi: fileApi, + + /** + * Constant that is true if the browser supports contentEditable=false regions. + * + * @property ceFalse + * @type Boolean + */ + ceFalse: (ie === false || ie > 8), + + /** + * Constant if CSP mode is possible or not. Meaning we can't use script urls for the iframe. + */ + canHaveCSP: (ie === false || ie > 11), + + desktop: !phone && !tablet, + windowsPhone: windowsPhone + }; + } + ); + + /** + * EventUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /*jshint loopfunc:true*/ + /*eslint no-loop-func:0 */ + + /** + * This class wraps the browsers native event logic with more convenient methods. + * + * @class tinymce.dom.EventUtils + */ + define( + 'tinymce.core.dom.EventUtils', [ + "tinymce.core.util.Delay", + "tinymce.core.Env" + ], + function(Delay, Env) { + "use strict"; + + var eventExpandoPrefix = "mce-data-"; + var mouseEventRe = /^(?:mouse|contextmenu)|click/; + var deprecated = { + keyLocation: 1, + layerX: 1, + layerY: 1, + returnValue: 1, + webkitMovementX: 1, + webkitMovementY: 1, + keyIdentifier: 1 + }; + + // Checks if it is our own isDefaultPrevented function + var hasIsDefaultPrevented = function(event) { + return event.isDefaultPrevented === returnTrue || event.isDefaultPrevented === returnFalse; + }; + + // Dummy function that gets replaced on the delegation state functions + var returnFalse = function() { + return false; + }; + + // Dummy function that gets replaced on the delegation state functions + var returnTrue = function() { + return true; + }; + + /** + * Binds a native event to a callback on the speified target. + */ + function addEvent(target, name, callback, capture) { + if (target.addEventListener) { + target.addEventListener(name, callback, capture || false); + } else if (target.attachEvent) { + target.attachEvent('on' + name, callback); + } + } + + /** + * Unbinds a native event callback on the specified target. + */ + function removeEvent(target, name, callback, capture) { + if (target.removeEventListener) { + target.removeEventListener(name, callback, capture || false); + } else if (target.detachEvent) { + target.detachEvent('on' + name, callback); + } + } + + /** + * Gets the event target based on shadow dom properties like path and deepPath. + */ + function getTargetFromShadowDom(event, defaultTarget) { + var path, target = defaultTarget; + + // When target element is inside Shadow DOM we need to take first element from path + // otherwise we'll get Shadow Root parent, not actual target element + + // Normalize target for WebComponents v0 implementation (in Chrome) + path = event.path; + if (path && path.length > 0) { + target = path[0]; + } + + // Normalize target for WebComponents v1 implementation (standard) + if (event.deepPath) { + path = event.deepPath(); + if (path && path.length > 0) { + target = path[0]; + } + } + + return target; + } + + /** + * Normalizes a native event object or just adds the event specific methods on a custom event. + */ + function fix(originalEvent, data) { + var name, event = data || {}, + undef; + + // Copy all properties from the original event + for (name in originalEvent) { + // layerX/layerY is deprecated in Chrome and produces a warning + if (!deprecated[name]) { + event[name] = originalEvent[name]; + } + } + + // Normalize target IE uses srcElement + if (!event.target) { + event.target = event.srcElement || document; + } + + // Experimental shadow dom support + if (Env.experimentalShadowDom) { + event.target = getTargetFromShadowDom(originalEvent, event.target); + } + + // Calculate pageX/Y if missing and clientX/Y available + if (originalEvent && mouseEventRe.test(originalEvent.type) && originalEvent.pageX === undef && originalEvent.clientX !== undef) { + var eventDoc = event.target.ownerDocument || document; + var doc = eventDoc.documentElement; + var body = eventDoc.body; + + event.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - + (doc && doc.clientLeft || body && body.clientLeft || 0); + + event.pageY = originalEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - + (doc && doc.clientTop || body && body.clientTop || 0); + } + + // Add preventDefault method + event.preventDefault = function() { + event.isDefaultPrevented = returnTrue; + + // Execute preventDefault on the original event object + if (originalEvent) { + if (originalEvent.preventDefault) { + originalEvent.preventDefault(); + } else { + originalEvent.returnValue = false; // IE + } + } + }; + + // Add stopPropagation + event.stopPropagation = function() { + event.isPropagationStopped = returnTrue; + + // Execute stopPropagation on the original event object + if (originalEvent) { + if (originalEvent.stopPropagation) { + originalEvent.stopPropagation(); + } else { + originalEvent.cancelBubble = true; // IE + } + } + }; + + // Add stopImmediatePropagation + event.stopImmediatePropagation = function() { + event.isImmediatePropagationStopped = returnTrue; + event.stopPropagation(); + }; + + // Add event delegation states + if (hasIsDefaultPrevented(event) === false) { + event.isDefaultPrevented = returnFalse; + event.isPropagationStopped = returnFalse; + event.isImmediatePropagationStopped = returnFalse; + } + + // Add missing metaKey for IE 8 + if (typeof event.metaKey == 'undefined') { + event.metaKey = false; + } + + return event; + } + + /** + * Bind a DOMContentLoaded event across browsers and executes the callback once the page DOM is initialized. + * It will also set/check the domLoaded state of the event_utils instance so ready isn't called multiple times. + */ + function bindOnReady(win, callback, eventUtils) { + var doc = win.document, + event = { + type: 'ready' + }; + + if (eventUtils.domLoaded) { + callback(event); + return; + } + + function isDocReady() { + // Check complete or interactive state if there is a body + // element on some iframes IE 8 will produce a null body + return doc.readyState === "complete" || (doc.readyState === "interactive" && doc.body); + } + + // Gets called when the DOM is ready + function readyHandler() { + if (!eventUtils.domLoaded) { + eventUtils.domLoaded = true; + callback(event); + } + } + + function waitForDomLoaded() { + if (isDocReady()) { + removeEvent(doc, "readystatechange", waitForDomLoaded); + readyHandler(); + } + } + + function tryScroll() { + try { + // If IE is used, use the trick by Diego Perini licensed under MIT by request to the author. + // http://javascript.nwbox.com/IEContentLoaded/ + doc.documentElement.doScroll("left"); + } catch (ex) { + Delay.setTimeout(tryScroll); + return; + } + + readyHandler(); + } + + // Use W3C method (exclude IE 9,10 - readyState "interactive" became valid only in IE 11) + if (doc.addEventListener && !(Env.ie && Env.ie < 11)) { + if (isDocReady()) { + readyHandler(); + } else { + addEvent(win, 'DOMContentLoaded', readyHandler); + } + } else { + // Use IE method + addEvent(doc, "readystatechange", waitForDomLoaded); + + // Wait until we can scroll, when we can the DOM is initialized + if (doc.documentElement.doScroll && win.self === win.top) { + tryScroll(); + } + } + + // Fallback if any of the above methods should fail for some odd reason + addEvent(win, 'load', readyHandler); + } + + /** + * This class enables you to bind/unbind native events to elements and normalize it's behavior across browsers. + */ + function EventUtils() { + var self = this, + events = {}, + count, expando, hasFocusIn, hasMouseEnterLeave, mouseEnterLeave; + + expando = eventExpandoPrefix + (+new Date()).toString(32); + hasMouseEnterLeave = "onmouseenter" in document.documentElement; + hasFocusIn = "onfocusin" in document.documentElement; + mouseEnterLeave = { + mouseenter: 'mouseover', + mouseleave: 'mouseout' + }; + count = 1; + + // State if the DOMContentLoaded was executed or not + self.domLoaded = false; + self.events = events; + + /** + * Executes all event handler callbacks for a specific event. + * + * @private + * @param {Event} evt Event object. + * @param {String} id Expando id value to look for. + */ + function executeHandlers(evt, id) { + var callbackList, i, l, callback, container = events[id]; + + callbackList = container && container[evt.type]; + if (callbackList) { + for (i = 0, l = callbackList.length; i < l; i++) { + callback = callbackList[i]; + + if (evt.type.indexOf('focus') !== -1) { + // console.log(`TinyMCE: ${id}:${evt.type}`/*, callback.func*/); + // console.trace(); + } + + // Check if callback exists might be removed if a unbind is called inside the callback + if (callback && callback.func.call(callback.scope, evt) === false) { + evt.preventDefault(); + } + + // Should we stop propagation to immediate listeners + if (evt.isImmediatePropagationStopped()) { + return; + } + } + } + } + + /** + * Binds a callback to an event on the specified target. + * + * @method bind + * @param {Object} target Target node/window or custom object. + * @param {String} names Name of the event to bind. + * @param {function} callback Callback function to execute when the event occurs. + * @param {Object} scope Scope to call the callback function on, defaults to target. + * @return {function} Callback function that got bound. + */ + self.bind = function(target, names, callback, scope) { + var id, callbackList, i, name, fakeName, nativeHandler, capture, win = window; + + // Native event handler function patches the event and executes the callbacks for the expando + function defaultNativeHandler(evt) { + executeHandlers(fix(evt || win.event), id); + } + + // Don't bind to text nodes or comments + if (!target || target.nodeType === 3 || target.nodeType === 8) { + return; + } + + // Create or get events id for the target + if (!target[expando]) { + id = count++; + target[expando] = id; + events[id] = {}; + } else { + id = target[expando]; + } + + // Setup the specified scope or use the target as a default + scope = scope || target; + + // Split names and bind each event, enables you to bind multiple events with one call + names = names.split(' '); + i = names.length; + while (i--) { + name = names[i]; + nativeHandler = defaultNativeHandler; + fakeName = capture = false; + + // Use ready instead of DOMContentLoaded + if (name === "DOMContentLoaded") { + name = "ready"; + } + + // DOM is already ready + if (self.domLoaded && name === "ready" && target.readyState == 'complete') { + callback.call(scope, fix({ + type: name + })); + continue; + } + + // Handle mouseenter/mouseleaver + if (!hasMouseEnterLeave) { + fakeName = mouseEnterLeave[name]; + + if (fakeName) { + nativeHandler = function(evt) { + var current, related; + + current = evt.currentTarget; + related = evt.relatedTarget; + + // Check if related is inside the current target if it's not then the event should + // be ignored since it's a mouseover/mouseout inside the element + if (related && current.contains) { + // Use contains for performance + related = current.contains(related); + } else { + while (related && related !== current) { + related = related.parentNode; + } + } + + // Fire fake event + if (!related) { + evt = fix(evt || win.event); + evt.type = evt.type === 'mouseout' ? 'mouseleave' : 'mouseenter'; + evt.target = current; + executeHandlers(evt, id); + } + }; + } + } + + // Fake bubbling of focusin/focusout + if (!hasFocusIn && (name === "focusin" || name === "focusout")) { + capture = true; + fakeName = name === "focusin" ? "focus" : "blur"; + nativeHandler = function(evt) { + evt = fix(evt || win.event); + evt.type = evt.type === 'focus' ? 'focusin' : 'focusout'; + executeHandlers(evt, id); + }; + } + + // Setup callback list and bind native event + callbackList = events[id][name]; + if (!callbackList) { + events[id][name] = callbackList = [{ + func: callback, + scope: scope + }]; + callbackList.fakeName = fakeName; + callbackList.capture = capture; + //callbackList.callback = callback; + + // Add the nativeHandler to the callback list so that we can later unbind it + callbackList.nativeHandler = nativeHandler; + + // Check if the target has native events support + + if (name === "ready") { + bindOnReady(target, nativeHandler, self); + } else { + addEvent(target, fakeName || name, nativeHandler, capture); + } + } else { + if (name === "ready" && self.domLoaded) { + callback({ + type: name + }); + } else { + // If it already has an native handler then just push the callback + callbackList.push({ + func: callback, + scope: scope + }); + } + } + } + + target = callbackList = 0; // Clean memory for IE + + return callback; + }; + + /** + * Unbinds the specified event by name, name and callback or all events on the target. + * + * @method unbind + * @param {Object} target Target node/window or custom object. + * @param {String} names Optional event name to unbind. + * @param {function} callback Optional callback function to unbind. + * @return {EventUtils} Event utils instance. + */ + self.unbind = function(target, names, callback) { + var id, callbackList, i, ci, name, eventMap; + + // Don't bind to text nodes or comments + if (!target || target.nodeType === 3 || target.nodeType === 8) { + return self; + } + + // Unbind event or events if the target has the expando + id = target[expando]; + if (id) { + eventMap = events[id]; + + // Specific callback + if (names) { + names = names.split(' '); + i = names.length; + while (i--) { + name = names[i]; + callbackList = eventMap[name]; + + // Unbind the event if it exists in the map + if (callbackList) { + // Remove specified callback + if (callback) { + ci = callbackList.length; + while (ci--) { + if (callbackList[ci].func === callback) { + var nativeHandler = callbackList.nativeHandler; + var fakeName = callbackList.fakeName, + capture = callbackList.capture; + + // Clone callbackList since unbind inside a callback would otherwise break the handlers loop + callbackList = callbackList.slice(0, ci).concat(callbackList.slice(ci + 1)); + callbackList.nativeHandler = nativeHandler; + callbackList.fakeName = fakeName; + callbackList.capture = capture; + + eventMap[name] = callbackList; + } + } + } + + // Remove all callbacks if there isn't a specified callback or there is no callbacks left + if (!callback || callbackList.length === 0) { + delete eventMap[name]; + removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture); + } + } + } + } else { + // All events for a specific element + for (name in eventMap) { + callbackList = eventMap[name]; + removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture); + } + + eventMap = {}; + } + + // Check if object is empty, if it isn't then we won't remove the expando map + for (name in eventMap) { + return self; + } + + // Delete event object + delete events[id]; + + // Remove expando from target + try { + // IE will fail here since it can't delete properties from window + delete target[expando]; + } catch (ex) { + // IE will set it to null + target[expando] = null; + } + } + + return self; + }; + + /** + * Fires the specified event on the specified target. + * + * @method fire + * @param {Object} target Target node/window or custom object. + * @param {String} name Event name to fire. + * @param {Object} args Optional arguments to send to the observers. + * @return {EventUtils} Event utils instance. + */ + self.fire = function(target, name, args) { + var id; + + // Don't bind to text nodes or comments + if (!target || target.nodeType === 3 || target.nodeType === 8) { + return self; + } + + // Build event object by patching the args + args = fix(null, args); + args.type = name; + args.target = target; + + do { + // Found an expando that means there is listeners to execute + id = target[expando]; + if (id) { + executeHandlers(args, id); + } + + // Walk up the DOM + target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow; + } while (target && !args.isPropagationStopped()); + + return self; + }; + + /** + * Removes all bound event listeners for the specified target. This will also remove any bound + * listeners to child nodes within that target. + * + * @method clean + * @param {Object} target Target node/window object. + * @return {EventUtils} Event utils instance. + */ + self.clean = function(target) { + var i, children, unbind = self.unbind; + + // Don't bind to text nodes or comments + if (!target || target.nodeType === 3 || target.nodeType === 8) { + return self; + } + + // Unbind any element on the specified target + if (target[expando]) { + unbind(target); + } + + // Target doesn't have getElementsByTagName it's probably a window object then use it's document to find the children + if (!target.getElementsByTagName) { + target = target.document; + } + + // Remove events from each child element + if (target && target.getElementsByTagName) { + unbind(target); + + children = target.getElementsByTagName('*'); + i = children.length; + while (i--) { + target = children[i]; + + if (target[expando]) { + unbind(target); + } + } + } + + return self; + }; + + /** + * Destroys the event object. Call this on IE to remove memory leaks. + */ + self.destroy = function() { + events = {}; + }; + + // Legacy function for canceling events + self.cancel = function(e) { + if (e) { + e.preventDefault(); + e.stopImmediatePropagation(); + } + + return false; + }; + } + + EventUtils.Event = new EventUtils(); + EventUtils.Event.bind(window, 'ready', function() {}); + + return EventUtils; + } + ); + + /** + * Sizzle.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + * + * @ignore-file + */ + + /*jshint bitwise:false, expr:true, noempty:false, sub:true, eqnull:true, latedef:false, maxlen:255 */ + /*eslint-disable */ + + /** + * Sizzle CSS Selector Engine v@VERSION + * http://sizzlejs.com/ + * + * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: @DATE + */ + define( + 'tinymce.core.dom.Sizzle', [], + function() { + var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function(a, b) { + if (a === b) { + hasDuplicate = true; + } + return 0; + }, + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function(elem) { + var i = 0, + len = this.length; + for (; i < len; i++) { + if (this[i] === elem) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g"), + + rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*"), + rcombinators = new RegExp("^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"), + + rattributeQuotes = new RegExp("=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g"), + + rpseudo = new RegExp(pseudos), + ridentifier = new RegExp("^" + identifier + "$"), + + matchExpr = { + "ID": new RegExp("^#(" + identifier + ")"), + "CLASS": new RegExp("^\\.(" + identifier + ")"), + "TAG": new RegExp("^(" + identifier + "|[*])"), + "ATTR": new RegExp("^" + attributes), + "PSEUDO": new RegExp("^" + pseudos), + "CHILD": new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i"), + "bool": new RegExp("^(?:" + booleans + ")$", "i"), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp("^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i") + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp("\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig"), + funescape = function(_, escaped, escapedWhitespace) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode(high + 0x10000) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode(high >> 10 | 0xD800, high & 0x3FF | 0xDC00); + }; + + // Optimize for push.apply( _, NodeList ) + try { + push.apply( + (arr = slice.call(preferredDoc.childNodes)), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[preferredDoc.childNodes.length].nodeType; + } catch (e) { + push = { + apply: arr.length ? + + // Leverage slice if possible + function(target, els) { + push_native.apply(target, slice.call(els)); + } : + + // Support: IE<9 + // Otherwise append directly + function(target, els) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ((target[j++] = els[i++])) {} + target.length = j - 1; + } + }; + } + + function Sizzle(selector, context, results, seed) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ((context ? context.ownerDocument || context : preferredDoc) !== document) { + setDocument(context); + } + + context = context || document; + results = results || []; + + if (!selector || typeof selector !== "string") { + return results; + } + + if ((nodeType = context.nodeType) !== 1 && nodeType !== 9) { + return []; + } + + if (documentIsHTML && !seed) { + + // Shortcuts + if ((match = rquickExpr.exec(selector))) { + // Speed-up: Sizzle("#ID") + if ((m = match[1])) { + if (nodeType === 9) { + elem = context.getElementById(m); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document (jQuery #6963) + if (elem && elem.parentNode) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if (elem.id === m) { + results.push(elem); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if (context.ownerDocument && (elem = context.ownerDocument.getElementById(m)) && + contains(context, elem) && elem.id === m) { + results.push(elem); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if (match[2]) { + push.apply(results, context.getElementsByTagName(selector)); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ((m = match[3]) && support.getElementsByClassName) { + push.apply(results, context.getElementsByClassName(m)); + return results; + } + } + + // QSA path + if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if (nodeType === 1 && context.nodeName.toLowerCase() !== "object") { + groups = tokenize(selector); + + if ((old = context.getAttribute("id"))) { + nid = old.replace(rescape, "\\$&"); + } else { + context.setAttribute("id", nid); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while (i--) { + groups[i] = nid + toSelector(groups[i]); + } + newContext = rsibling.test(selector) && testContext(context.parentNode) || context; + newSelector = groups.join(","); + } + + if (newSelector) { + try { + push.apply(results, + newContext.querySelectorAll(newSelector) + ); + return results; + } catch (qsaError) {} finally { + if (!old) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select(selector.replace(rtrim, "$1"), context, results, seed); + } + + /** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ + function createCache() { + var keys = []; + + function cache(key, value) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if (keys.push(key + " ") > Expr.cacheLength) { + // Only keep the most recent entries + delete cache[keys.shift()]; + } + return (cache[key + " "] = value); + } + return cache; + } + + /** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ + function markFunction(fn) { + fn[expando] = true; + return fn; + } + + /** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ + function assert(fn) { + var div = document.createElement("div"); + + try { + return !!fn(div); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if (div.parentNode) { + div.parentNode.removeChild(div); + } + // release memory in IE + div = null; + } + } + + /** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ + function addHandle(attrs, handler) { + var arr = attrs.split("|"), + i = attrs.length; + + while (i--) { + Expr.attrHandle[arr[i]] = handler; + } + } + + /** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ + function siblingCheck(a, b) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + (~b.sourceIndex || MAX_NEGATIVE) - + (~a.sourceIndex || MAX_NEGATIVE); + + // Use IE sourceIndex if available on both nodes + if (diff) { + return diff; + } + + // Check if b follows a + if (cur) { + while ((cur = cur.nextSibling)) { + if (cur === b) { + return -1; + } + } + } + + return a ? 1 : -1; + } + + /** + * Returns a function to use in pseudos for input types + * @param {String} type + */ + function createInputPseudo(type) { + return function(elem) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; + } + + /** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ + function createButtonPseudo(type) { + return function(elem) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; + } + + /** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ + function createPositionalPseudo(fn) { + return markFunction(function(argument) { + argument = +argument; + return markFunction(function(seed, matches) { + var j, + matchIndexes = fn([], seed.length, argument), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while (i--) { + if (seed[(j = matchIndexes[i])]) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); + } + + /** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ + function testContext(context) { + return context && typeof context.getElementsByTagName !== strundefined && context; + } + + // Expose support vars for convenience + support = Sizzle.support = {}; + + /** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ + isXML = Sizzle.isXML = function(elem) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; + }; + + /** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ + setDocument = Sizzle.setDocument = function(node) { + var hasCompare, + doc = node ? node.ownerDocument || node : preferredDoc, + parent = doc.defaultView; + + function getTop(win) { + // Edge throws a lovely Object expected if you try to get top on a detached reference see #2642 + try { + return win.top; + } catch (ex) { + // Ignore + } + + return null; + } + + // If no document and documentElement is available, return + if (doc === document || doc.nodeType !== 9 || !doc.documentElement) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsHTML = !isXML(doc); + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if (parent && parent !== getTop(parent)) { + // IE11 does not have attachEvent, so all must suffer + if (parent.addEventListener) { + parent.addEventListener("unload", function() { + setDocument(); + }, false); + } else if (parent.attachEvent) { + parent.attachEvent("onunload", function() { + setDocument(); + }); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function(div) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function(div) { + div.appendChild(doc.createComment("")); + return !div.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test(doc.getElementsByClassName); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function(div) { + docElem.appendChild(div).id = expando; + return !doc.getElementsByName || !doc.getElementsByName(expando).length; + }); + + // ID find and filter + if (support.getById) { + Expr.find["ID"] = function(id, context) { + if (typeof context.getElementById !== strundefined && documentIsHTML) { + var m = context.getElementById(id); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }; + Expr.filter["ID"] = function(id) { + var attrId = id.replace(runescape, funescape); + return function(elem) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function(id) { + var attrId = id.replace(runescape, funescape); + return function(elem) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function(tag, context) { + if (typeof context.getElementsByTagName !== strundefined) { + return context.getElementsByTagName(tag); + } + } : + function(tag, context) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName(tag); + + // Filter out possible comments + if (tag === "*") { + while ((elem = results[i++])) { + if (elem.nodeType === 1) { + tmp.push(elem); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function(className, context) { + if (documentIsHTML) { + return context.getElementsByClassName(className); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ((support.qsa = rnative.test(doc.querySelectorAll))) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function(div) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = "<select msallowcapture=''><option selected=''></option></select>"; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if (div.querySelectorAll("[msallowcapture^='']").length) { + rbuggyQSA.push("[*^$]=" + whitespace + "*(?:''|\"\")"); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if (!div.querySelectorAll("[selected]").length) { + rbuggyQSA.push("\\[" + whitespace + "*(?:value|" + booleans + ")"); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if (!div.querySelectorAll(":checked").length) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function(div) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute("type", "hidden"); + div.appendChild(input).setAttribute("name", "D"); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if (div.querySelectorAll("[name=d]").length) { + rbuggyQSA.push("name" + whitespace + "*[*^$|!~]?="); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if (!div.querySelectorAll(":enabled").length) { + rbuggyQSA.push(":enabled", ":disabled"); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ((support.matchesSelector = rnative.test((matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector)))) { + + assert(function(div) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call(div, "div"); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call(div, "[s!='']:x"); + rbuggyMatches.push("!=", pseudos); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp(rbuggyQSA.join("|")); + rbuggyMatches = rbuggyMatches.length && new RegExp(rbuggyMatches.join("|")); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test(docElem.compareDocumentPosition); + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = hasCompare || rnative.test(docElem.contains) ? + function(a, b) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!(bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains(bup) : + a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16 + )); + } : + function(a, b) { + if (b) { + while ((b = b.parentNode)) { + if (b === a) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function(a, b) { + + // Flag for duplicate removal + if (a === b) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if (compare) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = (a.ownerDocument || a) === (b.ownerDocument || b) ? + a.compareDocumentPosition(b) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if (compare & 1 || + (!support.sortDetached && b.compareDocumentPosition(a) === compare)) { + + // Choose the first element that is related to our preferred document + if (a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a)) { + return -1; + } + if (b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b)) { + return 1; + } + + // Maintain original order + return sortInput ? + (indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function(a, b) { + // Exit early if the nodes are identical + if (a === b) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [a], + bp = [b]; + + // Parentless nodes are either documents or disconnected + if (!aup || !bup) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + (indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if (aup === bup) { + return siblingCheck(a, b); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ((cur = cur.parentNode)) { + ap.unshift(cur); + } + cur = b; + while ((cur = cur.parentNode)) { + bp.unshift(cur); + } + + // Walk down the tree looking for a discrepancy + while (ap[i] === bp[i]) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck(ap[i], bp[i]) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; + }; + + Sizzle.matches = function(expr, elements) { + return Sizzle(expr, null, null, elements); + }; + + Sizzle.matchesSelector = function(elem, expr) { + // Set document vars if needed + if ((elem.ownerDocument || elem) !== document) { + setDocument(elem); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace(rattributeQuotes, "='$1']"); + + if (support.matchesSelector && documentIsHTML && + (!rbuggyMatches || !rbuggyMatches.test(expr)) && + (!rbuggyQSA || !rbuggyQSA.test(expr))) { + + try { + var ret = matches.call(elem, expr); + + // IE 9's matchesSelector returns false on disconnected nodes + if (ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11) { + return ret; + } + } catch (e) {} + } + + return Sizzle(expr, document, null, [elem]).length > 0; + }; + + Sizzle.contains = function(context, elem) { + // Set document vars if needed + if ((context.ownerDocument || context) !== document) { + setDocument(context); + } + return contains(context, elem); + }; + + Sizzle.attr = function(elem, name) { + // Set document vars if needed + if ((elem.ownerDocument || elem) !== document) { + setDocument(elem); + } + + var fn = Expr.attrHandle[name.toLowerCase()], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call(Expr.attrHandle, name.toLowerCase()) ? + fn(elem, name, !documentIsHTML) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute(name) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; + }; + + Sizzle.error = function(msg) { + throw new Error("Syntax error, unrecognized expression: " + msg); + }; + + /** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ + Sizzle.uniqueSort = function(results) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice(0); + results.sort(sortOrder); + + if (hasDuplicate) { + while ((elem = results[i++])) { + if (elem === results[i]) { + j = duplicates.push(i); + } + } + while (j--) { + results.splice(duplicates[j], 1); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; + }; + + /** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ + getText = Sizzle.getText = function(elem) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if (!nodeType) { + // If no nodeType, this is expected to be an array + while ((node = elem[i++])) { + // Do not traverse comment nodes + ret += getText(node); + } + } else if (nodeType === 1 || nodeType === 9 || nodeType === 11) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if (typeof elem.textContent === "string") { + return elem.textContent; + } else { + // Traverse its children + for (elem = elem.firstChild; elem; elem = elem.nextSibling) { + ret += getText(elem); + } + } + } else if (nodeType === 3 || nodeType === 4) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; + }; + + Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { + dir: "parentNode", + first: true + }, + " ": { + dir: "parentNode" + }, + "+": { + dir: "previousSibling", + first: true + }, + "~": { + dir: "previousSibling" + } + }, + + preFilter: { + "ATTR": function(match) { + match[1] = match[1].replace(runescape, funescape); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = (match[3] || match[4] || match[5] || "").replace(runescape, funescape); + + if (match[2] === "~=") { + match[3] = " " + match[3] + " "; + } + + return match.slice(0, 4); + }, + + "CHILD": function(match) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if (match[1].slice(0, 3) === "nth") { + // nth-* requires argument + if (!match[3]) { + Sizzle.error(match[0]); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +(match[4] ? match[5] + (match[6] || 1) : 2 * (match[3] === "even" || match[3] === "odd")); + match[5] = +((match[7] + match[8]) || match[3] === "odd"); + + // other types prohibit arguments + } else if (match[3]) { + Sizzle.error(match[0]); + } + + return match; + }, + + "PSEUDO": function(match) { + var excess, + unquoted = !match[6] && match[2]; + + if (matchExpr["CHILD"].test(match[0])) { + return null; + } + + // Accept quoted arguments as-is + if (match[3]) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if (unquoted && rpseudo.test(unquoted) && + // Get excess from tokenize (recursively) + (excess = tokenize(unquoted, true)) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)) { + + // excess is a negative index + match[0] = match[0].slice(0, excess); + match[2] = unquoted.slice(0, excess); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice(0, 3); + } + }, + + filter: { + + "TAG": function(nodeNameSelector) { + var nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function(elem) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function(className) { + var pattern = classCache[className + " "]; + + return pattern || + (pattern = new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)")) && + classCache(className, function(elem) { + return pattern.test(typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || ""); + }); + }, + + "ATTR": function(name, operator, check) { + return function(elem) { + var result = Sizzle.attr(elem, name); + + if (result == null) { + return operator === "!="; + } + if (!operator) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf(check) === 0 : + operator === "*=" ? check && result.indexOf(check) > -1 : + operator === "$=" ? check && result.slice(-check.length) === check : + operator === "~=" ? (" " + result + " ").indexOf(check) > -1 : + operator === "|=" ? result === check || result.slice(0, check.length + 1) === check + "-" : + false; + }; + }, + + "CHILD": function(type, what, argument, first, last) { + var simple = type.slice(0, 3) !== "nth", + forward = type.slice(-4) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function(elem) { + return !!elem.parentNode; + } : + + function(elem, context, xml) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if (parent) { + + // :(first|last|only)-(child|of-type) + if (simple) { + while (dir) { + node = elem; + while ((node = node[dir])) { + if (ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [forward ? parent.firstChild : parent.lastChild]; + + // non-xml :nth-child(...) stores cache data on `parent` + if (forward && useCache) { + // Seek `elem` from a previously-cached index + outerCache = parent[expando] || (parent[expando] = {}); + cache = outerCache[type] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[nodeIndex]; + + while ((node = ++nodeIndex && node && node[dir] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop())) { + + // When found, cache indexes on `parent` and break + if (node.nodeType === 1 && ++diff && node === elem) { + outerCache[type] = [dirruns, nodeIndex, diff]; + break; + } + } + + // Use previously-cached element index if available + } else if (useCache && (cache = (elem[expando] || (elem[expando] = {}))[type]) && cache[0] === dirruns) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ((node = ++nodeIndex && node && node[dir] || + (diff = nodeIndex = 0) || start.pop())) { + + if ((ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) && ++diff) { + // Cache the index of each encountered element + if (useCache) { + (node[expando] || (node[expando] = {}))[type] = [dirruns, diff]; + } + + if (node === elem) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || (diff % first === 0 && diff / first >= 0); + } + }; + }, + + "PSEUDO": function(pseudo, argument) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[pseudo] || Expr.setFilters[pseudo.toLowerCase()] || + Sizzle.error("unsupported pseudo: " + pseudo); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if (fn[expando]) { + return fn(argument); + } + + // But maintain support for old signatures + if (fn.length > 1) { + args = [pseudo, pseudo, "", argument]; + return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase()) ? + markFunction(function(seed, matches) { + var idx, + matched = fn(seed, argument), + i = matched.length; + while (i--) { + idx = indexOf.call(seed, matched[i]); + seed[idx] = !(matches[idx] = matched[i]); + } + }) : + function(elem) { + return fn(elem, 0, args); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function(selector) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile(selector.replace(rtrim, "$1")); + + return matcher[expando] ? + markFunction(function(seed, matches, context, xml) { + var elem, + unmatched = matcher(seed, null, xml, []), + i = seed.length; + + // Match elements unmatched by `matcher` + while (i--) { + if ((elem = unmatched[i])) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function(elem, context, xml) { + input[0] = elem; + matcher(input, null, xml, results); + return !results.pop(); + }; + }), + + "has": markFunction(function(selector) { + return function(elem) { + return Sizzle(selector, elem).length > 0; + }; + }), + + "contains": markFunction(function(text) { + text = text.replace(runescape, funescape); + return function(elem) { + return (elem.textContent || elem.innerText || getText(elem)).indexOf(text) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction(function(lang) { + // lang value must be a valid identifier + if (!ridentifier.test(lang || "")) { + Sizzle.error("unsupported lang: " + lang); + } + lang = lang.replace(runescape, funescape).toLowerCase(); + return function(elem) { + var elemLang; + do { + if ((elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang"))) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf(lang + "-") === 0; + } + } while ((elem = elem.parentNode) && elem.nodeType === 1); + return false; + }; + }), + + // Miscellaneous + "target": function(elem) { + var hash = window.location && window.location.hash; + return hash && hash.slice(1) === elem.id; + }, + + "root": function(elem) { + return elem === docElem; + }, + + "focus": function(elem) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function(elem) { + return elem.disabled === false; + }, + + "disabled": function(elem) { + return elem.disabled === true; + }, + + "checked": function(elem) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function(elem) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if (elem.parentNode) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function(elem) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for (elem = elem.firstChild; elem; elem = elem.nextSibling) { + if (elem.nodeType < 6) { + return false; + } + } + return true; + }, + + "parent": function(elem) { + return !Expr.pseudos["empty"](elem); + }, + + // Element/input types + "header": function(elem) { + return rheader.test(elem.nodeName); + }, + + "input": function(elem) { + return rinputs.test(elem.nodeName); + }, + + "button": function(elem) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function(elem) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ((attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text"); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [0]; + }), + + "last": createPositionalPseudo(function(matchIndexes, length) { + return [length - 1]; + }), + + "eq": createPositionalPseudo(function(matchIndexes, length, argument) { + return [argument < 0 ? argument + length : argument]; + }), + + "even": createPositionalPseudo(function(matchIndexes, length) { + var i = 0; + for (; i < length; i += 2) { + matchIndexes.push(i); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function(matchIndexes, length) { + var i = 1; + for (; i < length; i += 2) { + matchIndexes.push(i); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function(matchIndexes, length, argument) { + var i = argument < 0 ? argument + length : argument; + for (; --i >= 0;) { + matchIndexes.push(i); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function(matchIndexes, length, argument) { + var i = argument < 0 ? argument + length : argument; + for (; ++i < length;) { + matchIndexes.push(i); + } + return matchIndexes; + }) + } + }; + + Expr.pseudos["nth"] = Expr.pseudos["eq"]; + + // Add button/input type pseudos + for (i in { + radio: true, + checkbox: true, + file: true, + password: true, + image: true + }) { + Expr.pseudos[i] = createInputPseudo(i); + } + for (i in { + submit: true, + reset: true + }) { + Expr.pseudos[i] = createButtonPseudo(i); + } + + // Easy API for creating new setFilters + function setFilters() {} + setFilters.prototype = Expr.filters = Expr.pseudos; + Expr.setFilters = new setFilters(); + + tokenize = Sizzle.tokenize = function(selector, parseOnly) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[selector + " "]; + + if (cached) { + return parseOnly ? 0 : cached.slice(0); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while (soFar) { + + // Comma and first run + if (!matched || (match = rcomma.exec(soFar))) { + if (match) { + // Don't consume trailing commas as valid + soFar = soFar.slice(match[0].length) || soFar; + } + groups.push((tokens = [])); + } + + matched = false; + + // Combinators + if ((match = rcombinators.exec(soFar))) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace(rtrim, " ") + }); + soFar = soFar.slice(matched.length); + } + + // Filters + for (type in Expr.filter) { + if ((match = matchExpr[type].exec(soFar)) && (!preFilters[type] || + (match = preFilters[type](match)))) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice(matched.length); + } + } + + if (!matched) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error(selector) : + // Cache the tokens + tokenCache(selector, groups).slice(0); + }; + + function toSelector(tokens) { + var i = 0, + len = tokens.length, + selector = ""; + for (; i < len; i++) { + selector += tokens[i].value; + } + return selector; + } + + function addCombinator(matcher, combinator, base) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function(elem, context, xml) { + while ((elem = elem[dir])) { + if (elem.nodeType === 1 || checkNonElements) { + return matcher(elem, context, xml); + } + } + } : + + // Check against all ancestor/preceding elements + function(elem, context, xml) { + var oldCache, outerCache, + newCache = [dirruns, doneName]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if (xml) { + while ((elem = elem[dir])) { + if (elem.nodeType === 1 || checkNonElements) { + if (matcher(elem, context, xml)) { + return true; + } + } + } + } else { + while ((elem = elem[dir])) { + if (elem.nodeType === 1 || checkNonElements) { + outerCache = elem[expando] || (elem[expando] = {}); + if ((oldCache = outerCache[dir]) && + oldCache[0] === dirruns && oldCache[1] === doneName) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[2] = oldCache[2]); + } else { + // Reuse newcache so results back-propagate to previous elements + outerCache[dir] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ((newCache[2] = matcher(elem, context, xml))) { + return true; + } + } + } + } + } + }; + } + + function elementMatcher(matchers) { + return matchers.length > 1 ? + function(elem, context, xml) { + var i = matchers.length; + while (i--) { + if (!matchers[i](elem, context, xml)) { + return false; + } + } + return true; + } : + matchers[0]; + } + + function multipleContexts(selector, contexts, results) { + var i = 0, + len = contexts.length; + for (; i < len; i++) { + Sizzle(selector, contexts[i], results); + } + return results; + } + + function condense(unmatched, map, filter, context, xml) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for (; i < len; i++) { + if ((elem = unmatched[i])) { + if (!filter || filter(elem, context, xml)) { + newUnmatched.push(elem); + if (mapped) { + map.push(i); + } + } + } + } + + return newUnmatched; + } + + function setMatcher(preFilter, selector, matcher, postFilter, postFinder, postSelector) { + if (postFilter && !postFilter[expando]) { + postFilter = setMatcher(postFilter); + } + if (postFinder && !postFinder[expando]) { + postFinder = setMatcher(postFinder, postSelector); + } + return markFunction(function(seed, results, context, xml) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts(selector || "*", context.nodeType ? [context] : context, []), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && (seed || !selector) ? + condense(elems, preMap, preFilter, context, xml) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || (seed ? preFilter : preexisting || postFilter) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if (matcher) { + matcher(matcherIn, matcherOut, context, xml); + } + + // Apply postFilter + if (postFilter) { + temp = condense(matcherOut, postMap); + postFilter(temp, [], context, xml); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while (i--) { + if ((elem = temp[i])) { + matcherOut[postMap[i]] = !(matcherIn[postMap[i]] = elem); + } + } + } + + if (seed) { + if (postFinder || preFilter) { + if (postFinder) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while (i--) { + if ((elem = matcherOut[i])) { + // Restore matcherIn since elem is not yet a final match + temp.push((matcherIn[i] = elem)); + } + } + postFinder(null, (matcherOut = []), temp, xml); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while (i--) { + if ((elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call(seed, elem) : preMap[i]) > -1) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice(preexisting, matcherOut.length) : + matcherOut + ); + if (postFinder) { + postFinder(null, results, matcherOut, xml); + } else { + push.apply(results, matcherOut); + } + } + }); + } + + function matcherFromTokens(tokens) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[tokens[0].type], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator(function(elem) { + return elem === checkContext; + }, implicitRelative, true), + matchAnyContext = addCombinator(function(elem) { + return indexOf.call(checkContext, elem) > -1; + }, implicitRelative, true), + matchers = [function(elem, context, xml) { + return (!leadingRelative && (xml || context !== outermostContext)) || ( + (checkContext = context).nodeType ? + matchContext(elem, context, xml) : + matchAnyContext(elem, context, xml)); + }]; + + for (; i < len; i++) { + if ((matcher = Expr.relative[tokens[i].type])) { + matchers = [addCombinator(elementMatcher(matchers), matcher)]; + } else { + matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches); + + // Return special upon seeing a positional matcher + if (matcher[expando]) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for (; j < len; j++) { + if (Expr.relative[tokens[j].type]) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher(matchers), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice(0, i - 1).concat({ + value: tokens[i - 2].type === " " ? "*" : "" + }) + ).replace(rtrim, "$1"), + matcher, + i < j && matcherFromTokens(tokens.slice(i, j)), + j < len && matcherFromTokens((tokens = tokens.slice(j))), + j < len && toSelector(tokens) + ); + } + matchers.push(matcher); + } + } + + return elementMatcher(matchers); + } + + function matcherFromGroupMatchers(elementMatchers, setMatchers) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function(seed, context, xml, results, outermost) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]("*", outermost), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if (outermost) { + outermostContext = context !== document && context; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id + for (; i !== len && (elem = elems[i]) != null; i++) { + if (byElement && elem) { + j = 0; + while ((matcher = elementMatchers[j++])) { + if (matcher(elem, context, xml)) { + results.push(elem); + break; + } + } + if (outermost) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if (bySet) { + // They will have gone through all possible matchers + if ((elem = !matcher && elem)) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if (seed) { + unmatched.push(elem); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if (bySet && i !== matchedCount) { + j = 0; + while ((matcher = setMatchers[j++])) { + matcher(unmatched, setMatched, context, xml); + } + + if (seed) { + // Reintegrate element matches to eliminate the need for sorting + if (matchedCount > 0) { + while (i--) { + if (!(unmatched[i] || setMatched[i])) { + setMatched[i] = pop.call(results); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense(setMatched); + } + + // Add matches to results + push.apply(results, setMatched); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if (outermost && !seed && setMatched.length > 0 && + (matchedCount + setMatchers.length) > 1) { + + Sizzle.uniqueSort(results); + } + } + + // Override manipulation of globals by nested matchers + if (outermost) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction(superMatcher) : + superMatcher; + } + + compile = Sizzle.compile = function(selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[selector + " "]; + + if (!cached) { + // Generate a function of recursive functions that can be used to check each element + if (!match) { + match = tokenize(selector); + } + i = match.length; + while (i--) { + cached = matcherFromTokens(match[i]); + if (cached[expando]) { + setMatchers.push(cached); + } else { + elementMatchers.push(cached); + } + } + + // Cache the compiled function + cached = compilerCache(selector, matcherFromGroupMatchers(elementMatchers, setMatchers)); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; + }; + + /** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ + select = Sizzle.select = function(selector, context, results, seed) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize((selector = compiled.selector || selector)); + + results = results || []; + + // Try to minimize operations if there is no seed and only one group + if (match.length === 1) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice(0); + if (tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[tokens[1].type]) { + + context = (Expr.find["ID"](token.matches[0].replace(runescape, funescape), context) || [])[0]; + if (!context) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if (compiled) { + context = context.parentNode; + } + + selector = selector.slice(tokens.shift().value.length); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test(selector) ? 0 : tokens.length; + while (i--) { + token = tokens[i]; + + // Abort if we hit a combinator + if (Expr.relative[(type = token.type)]) { + break; + } + if ((find = Expr.find[type])) { + // Search, expanding context for leading sibling combinators + if ((seed = find( + token.matches[0].replace(runescape, funescape), + rsibling.test(tokens[0].type) && testContext(context.parentNode) || context + ))) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice(i, 1); + selector = seed.length && toSelector(tokens); + if (!selector) { + push.apply(results, seed); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + (compiled || compile(selector, match))( + seed, + context, !documentIsHTML, + results, + rsibling.test(selector) && testContext(context.parentNode) || context + ); + return results; + }; + + // One-time assignments + + // Sort stability + support.sortStable = expando.split("").sort(sortOrder).join("") === expando; + + // Support: Chrome 14-35+ + // Always assume duplicates if they aren't passed to the comparison function + support.detectDuplicates = !!hasDuplicate; + + // Initialize against the default document + setDocument(); + + // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) + // Detached nodes confoundingly follow *each other* + support.sortDetached = assert(function(div1) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition(document.createElement("div")) & 1; + }); + + // Support: IE<8 + // Prevent attribute/property "interpolation" + // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx + if (!assert(function(div) { + div.innerHTML = "<a href='#'></a>"; + return div.firstChild.getAttribute("href") === "#"; + })) { + addHandle("type|href|height|width", function(elem, name, isXML) { + if (!isXML) { + return elem.getAttribute(name, name.toLowerCase() === "type" ? 1 : 2); + } + }); + } + + // Support: IE<9 + // Use defaultValue in place of getAttribute("value") + if (!support.attributes || !assert(function(div) { + div.innerHTML = "<input/>"; + div.firstChild.setAttribute("value", ""); + return div.firstChild.getAttribute("value") === ""; + })) { + addHandle("value", function(elem, name, isXML) { + if (!isXML && elem.nodeName.toLowerCase() === "input") { + return elem.defaultValue; + } + }); + } + + // Support: IE<9 + // Use getAttributeNode to fetch booleans when getAttribute lies + if (!assert(function(div) { + return div.getAttribute("disabled") == null; + })) { + addHandle(booleans, function(elem, name, isXML) { + var val; + if (!isXML) { + return elem[name] === true ? name.toLowerCase() : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; + } + }); + } + + // EXPOSE + return Sizzle; + } + ); + + /*eslint-enable */ + + /** + * Arr.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Array utility class. + * + * @private + * @class tinymce.util.Arr + */ + define( + 'tinymce.core.util.Arr', [], + function() { + var isArray = Array.isArray || function(obj) { + return Object.prototype.toString.call(obj) === "[object Array]"; + }; + + function toArray(obj) { + var array = obj, + i, l; + + if (!isArray(obj)) { + array = []; + for (i = 0, l = obj.length; i < l; i++) { + array[i] = obj[i]; + } + } + + return array; + } + + function each(o, cb, s) { + var n, l; + + if (!o) { + return 0; + } + + s = s || o; + + if (o.length !== undefined) { + // Indexed arrays, needed for Safari + for (n = 0, l = o.length; n < l; n++) { + if (cb.call(s, o[n], n, o) === false) { + return 0; + } + } + } else { + // Hashtables + for (n in o) { + if (o.hasOwnProperty(n)) { + if (cb.call(s, o[n], n, o) === false) { + return 0; + } + } + } + } + + return 1; + } + + function map(array, callback) { + var out = []; + + each(array, function(item, index) { + out.push(callback(item, index, array)); + }); + + return out; + } + + function filter(a, f) { + var o = []; + + each(a, function(v, index) { + if (!f || f(v, index, a)) { + o.push(v); + } + }); + + return o; + } + + function indexOf(a, v) { + var i, l; + + if (a) { + for (i = 0, l = a.length; i < l; i++) { + if (a[i] === v) { + return i; + } + } + } + + return -1; + } + + function reduce(collection, iteratee, accumulator, thisArg) { + var i = 0; + + if (arguments.length < 3) { + accumulator = collection[0]; + } + + for (; i < collection.length; i++) { + accumulator = iteratee.call(thisArg, accumulator, collection[i], i); + } + + return accumulator; + } + + function findIndex(array, predicate, thisArg) { + var i, l; + + for (i = 0, l = array.length; i < l; i++) { + if (predicate.call(thisArg, array[i], i, array)) { + return i; + } + } + + return -1; + } + + function find(array, predicate, thisArg) { + var idx = findIndex(array, predicate, thisArg); + + if (idx !== -1) { + return array[idx]; + } + + return undefined; + } + + function last(collection) { + return collection[collection.length - 1]; + } + + return { + isArray: isArray, + toArray: toArray, + each: each, + map: map, + filter: filter, + indexOf: indexOf, + reduce: reduce, + findIndex: findIndex, + find: find, + last: last + }; + } + ); + /** + * Tools.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class contains various utlity functions. These are also exposed + * directly on the tinymce namespace. + * + * @class tinymce.util.Tools + */ + define( + 'tinymce.core.util.Tools', [ + "tinymce.core.Env", + "tinymce.core.util.Arr" + ], + function(Env, Arr) { + /** + * Removes whitespace from the beginning and end of a string. + * + * @method trim + * @param {String} s String to remove whitespace from. + * @return {String} New string with removed whitespace. + */ + var whiteSpaceRegExp = /^\s*|\s*$/g; + + function trim(str) { + return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, ''); + } + + /** + * Checks if a object is of a specific type for example an array. + * + * @method is + * @param {Object} obj Object to check type of. + * @param {string} type Optional type to check for. + * @return {Boolean} true/false if the object is of the specified type. + */ + function is(obj, type) { + if (!type) { + return obj !== undefined; + } + + if (type == 'array' && Arr.isArray(obj)) { + return true; + } + + return typeof obj == type; + } + + /** + * Makes a name/object map out of an array with names. + * + * @method makeMap + * @param {Array/String} items Items to make map out of. + * @param {String} delim Optional delimiter to split string by. + * @param {Object} map Optional map to add items to. + * @return {Object} Name/value map of items. + */ + function makeMap(items, delim, map) { + var i; + + items = items || []; + delim = delim || ','; + + if (typeof items == "string") { + items = items.split(delim); + } + + map = map || {}; + + i = items.length; + while (i--) { + map[items[i]] = {}; + } + + return map; + } + + /** + * JavaScript does not protect hasOwnProperty method, so it is possible to overwrite it. This is + * object independent version. + * + * @param {Object} obj + * @param {String} prop + * @returns {Boolean} + */ + function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + } + + /** + * Creates a class, subclass or static singleton. + * More details on this method can be found in the Wiki. + * + * @method create + * @param {String} s Class name, inheritance and prefix. + * @param {Object} p Collection of methods to add to the class. + * @param {Object} root Optional root object defaults to the global window object. + * @example + * // Creates a basic class + * tinymce.create('tinymce.somepackage.SomeClass', { + * SomeClass: function() { + * // Class constructor + * }, + * + * method: function() { + * // Some method + * } + * }); + * + * // Creates a basic subclass class + * tinymce.create('tinymce.somepackage.SomeSubClass:tinymce.somepackage.SomeClass', { + * SomeSubClass: function() { + * // Class constructor + * this.parent(); // Call parent constructor + * }, + * + * method: function() { + * // Some method + * this.parent(); // Call parent method + * }, + * + * 'static': { + * staticMethod: function() { + * // Static method + * } + * } + * }); + * + * // Creates a singleton/static class + * tinymce.create('static tinymce.somepackage.SomeSingletonClass', { + * method: function() { + * // Some method + * } + * }); + */ + function create(s, p, root) { + var self = this, + sp, ns, cn, scn, c, de = 0; + + // Parse : <prefix> <class>:<super class> + s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s); + cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name + + // Create namespace for new class + ns = self.createNS(s[3].replace(/\.\w+$/, ''), root); + + // Class already exists + if (ns[cn]) { + return; + } + + // Make pure static class + if (s[2] == 'static') { + ns[cn] = p; + + if (this.onCreate) { + this.onCreate(s[2], s[3], ns[cn]); + } + + return; + } + + // Create default constructor + if (!p[cn]) { + p[cn] = function() {}; + de = 1; + } + + // Add constructor and methods + ns[cn] = p[cn]; + self.extend(ns[cn].prototype, p); + + // Extend + if (s[5]) { + sp = self.resolve(s[5]).prototype; + scn = s[5].match(/\.(\w+)$/i)[1]; // Class name + + // Extend constructor + c = ns[cn]; + if (de) { + // Add passthrough constructor + ns[cn] = function() { + return sp[scn].apply(this, arguments); + }; + } else { + // Add inherit constructor + ns[cn] = function() { + this.parent = sp[scn]; + return c.apply(this, arguments); + }; + } + ns[cn].prototype[cn] = ns[cn]; + + // Add super methods + self.each(sp, function(f, n) { + ns[cn].prototype[n] = sp[n]; + }); + + // Add overridden methods + self.each(p, function(f, n) { + // Extend methods if needed + if (sp[n]) { + ns[cn].prototype[n] = function() { + this.parent = sp[n]; + return f.apply(this, arguments); + }; + } else { + if (n != cn) { + ns[cn].prototype[n] = f; + } + } + }); + } + + // Add static methods + /*jshint sub:true*/ + /*eslint dot-notation:0*/ + self.each(p['static'], function(f, n) { + ns[cn][n] = f; + }); + } + + function extend(obj, ext) { + var i, l, name, args = arguments, + value; + + for (i = 1, l = args.length; i < l; i++) { + ext = args[i]; + for (name in ext) { + if (ext.hasOwnProperty(name)) { + value = ext[name]; + + if (value !== undefined) { + obj[name] = value; + } + } + } + } + + return obj; + } + + /** + * Executed the specified function for each item in a object tree. + * + * @method walk + * @param {Object} o Object tree to walk though. + * @param {function} f Function to call for each item. + * @param {String} n Optional name of collection inside the objects to walk for example childNodes. + * @param {String} s Optional scope to execute the function in. + */ + function walk(o, f, n, s) { + s = s || this; + + if (o) { + if (n) { + o = o[n]; + } + + Arr.each(o, function(o, i) { + if (f.call(s, o, i, n) === false) { + return false; + } + + walk(o, f, n, s); + }); + } + } + + /** + * Creates a namespace on a specific object. + * + * @method createNS + * @param {String} n Namespace to create for example a.b.c.d. + * @param {Object} o Optional object to add namespace to, defaults to window. + * @return {Object} New namespace object the last item in path. + * @example + * // Create some namespace + * tinymce.createNS('tinymce.somepackage.subpackage'); + * + * // Add a singleton + * var tinymce.somepackage.subpackage.SomeSingleton = { + * method: function() { + * // Some method + * } + * }; + */ + function createNS(n, o) { + var i, v; + + o = o || window; + + n = n.split('.'); + for (i = 0; i < n.length; i++) { + v = n[i]; + + if (!o[v]) { + o[v] = {}; + } + + o = o[v]; + } + + return o; + } + + /** + * Resolves a string and returns the object from a specific structure. + * + * @method resolve + * @param {String} n Path to resolve for example a.b.c.d. + * @param {Object} o Optional object to search though, defaults to window. + * @return {Object} Last object in path or null if it couldn't be resolved. + * @example + * // Resolve a path into an object reference + * var obj = tinymce.resolve('a.b.c.d'); + */ + function resolve(n, o) { + var i, l; + + o = o || window; + + n = n.split('.'); + for (i = 0, l = n.length; i < l; i++) { + o = o[n[i]]; + + if (!o) { + break; + } + } + + return o; + } + + /** + * Splits a string but removes the whitespace before and after each value. + * + * @method explode + * @param {string} s String to split. + * @param {string} d Delimiter to split by. + * @example + * // Split a string into an array with a,b,c + * var arr = tinymce.explode('a, b, c'); + */ + function explode(s, d) { + if (!s || is(s, 'array')) { + return s; + } + + return Arr.map(s.split(d || ','), trim); + } + + function _addCacheSuffix(url) { + var cacheSuffix = Env.cacheSuffix; + + if (cacheSuffix) { + url += (url.indexOf('?') === -1 ? '?' : '&') + cacheSuffix; + } + + return url; + } + + return { + trim: trim, + + /** + * Returns true/false if the object is an array or not. + * + * @method isArray + * @param {Object} obj Object to check. + * @return {boolean} true/false state if the object is an array or not. + */ + isArray: Arr.isArray, + + is: is, + + /** + * Converts the specified object into a real JavaScript array. + * + * @method toArray + * @param {Object} obj Object to convert into array. + * @return {Array} Array object based in input. + */ + toArray: Arr.toArray, + makeMap: makeMap, + + /** + * Performs an iteration of all items in a collection such as an object or array. This method will execure the + * callback function for each item in the collection, if the callback returns false the iteration will terminate. + * The callback has the following format: cb(value, key_or_index). + * + * @method each + * @param {Object} o Collection to iterate. + * @param {function} cb Callback function to execute for each item. + * @param {Object} s Optional scope to execute the callback in. + * @example + * // Iterate an array + * tinymce.each([1,2,3], function(v, i) { + * console.debug("Value: " + v + ", Index: " + i); + * }); + * + * // Iterate an object + * tinymce.each({a: 1, b: 2, c: 3], function(v, k) { + * console.debug("Value: " + v + ", Key: " + k); + * }); + */ + each: Arr.each, + + /** + * Creates a new array by the return value of each iteration function call. This enables you to convert + * one array list into another. + * + * @method map + * @param {Array} array Array of items to iterate. + * @param {function} callback Function to call for each item. It's return value will be the new value. + * @return {Array} Array with new values based on function return values. + */ + map: Arr.map, + + /** + * Filters out items from the input array by calling the specified function for each item. + * If the function returns false the item will be excluded if it returns true it will be included. + * + * @method grep + * @param {Array} a Array of items to loop though. + * @param {function} f Function to call for each item. Include/exclude depends on it's return value. + * @return {Array} New array with values imported and filtered based in input. + * @example + * // Filter out some items, this will return an array with 4 and 5 + * var items = tinymce.grep([1,2,3,4,5], function(v) {return v > 3;}); + */ + grep: Arr.filter, + + /** + * Returns an index of the item or -1 if item is not present in the array. + * + * @method inArray + * @param {any} item Item to search for. + * @param {Array} arr Array to search in. + * @return {Number} index of the item or -1 if item was not found. + */ + inArray: Arr.indexOf, + + hasOwn: hasOwnProperty, + + extend: extend, + create: create, + walk: walk, + createNS: createNS, + resolve: resolve, + explode: explode, + _addCacheSuffix: _addCacheSuffix + }; + } + ); + /** + * DomQuery.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class mimics most of the jQuery API: + * + * This is whats currently implemented: + * - Utility functions + * - DOM traversial + * - DOM manipulation + * - Event binding + * + * This is not currently implemented: + * - Dimension + * - Ajax + * - Animation + * - Advanced chaining + * + * @example + * var $ = tinymce.dom.DomQuery; + * $('p').attr('attr', 'value').addClass('class'); + * + * @class tinymce.dom.DomQuery + */ + define( + 'tinymce.core.dom.DomQuery', [ + "tinymce.core.dom.EventUtils", + "tinymce.core.dom.Sizzle", + "tinymce.core.util.Tools", + "tinymce.core.Env" + ], + function(EventUtils, Sizzle, Tools, Env) { + var doc = document, + push = Array.prototype.push, + slice = Array.prototype.slice; + var rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/; + var Event = EventUtils.Event, + undef; + var skipUniques = Tools.makeMap('children,contents,next,prev'); + + function isDefined(obj) { + return typeof obj !== 'undefined'; + } + + function isString(obj) { + return typeof obj === 'string'; + } + + function isWindow(obj) { + return obj && obj == obj.window; + } + + function createFragment(html, fragDoc) { + var frag, node, container; + + fragDoc = fragDoc || doc; + container = fragDoc.createElement('div'); + frag = fragDoc.createDocumentFragment(); + container.innerHTML = html; + + while ((node = container.firstChild)) { + frag.appendChild(node); + } + + return frag; + } + + function domManipulate(targetNodes, sourceItem, callback, reverse) { + var i; + + if (isString(sourceItem)) { + sourceItem = createFragment(sourceItem, getElementDocument(targetNodes[0])); + } else if (sourceItem.length && !sourceItem.nodeType) { + sourceItem = DomQuery.makeArray(sourceItem); + + if (reverse) { + for (i = sourceItem.length - 1; i >= 0; i--) { + domManipulate(targetNodes, sourceItem[i], callback, reverse); + } + } else { + for (i = 0; i < sourceItem.length; i++) { + domManipulate(targetNodes, sourceItem[i], callback, reverse); + } + } + + return targetNodes; + } + + if (sourceItem.nodeType) { + i = targetNodes.length; + while (i--) { + callback.call(targetNodes[i], sourceItem); + } + } + + return targetNodes; + } + + function hasClass(node, className) { + return node && className && (' ' + node.className + ' ').indexOf(' ' + className + ' ') !== -1; + } + + function wrap(elements, wrapper, all) { + var lastParent, newWrapper; + + wrapper = DomQuery(wrapper)[0]; + + elements.each(function() { + var self = this; + + if (!all || lastParent != self.parentNode) { + lastParent = self.parentNode; + newWrapper = wrapper.cloneNode(false); + self.parentNode.insertBefore(newWrapper, self); + newWrapper.appendChild(self); + } else { + newWrapper.appendChild(self); + } + }); + + return elements; + } + + var numericCssMap = Tools.makeMap('fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom', ' '); + var booleanMap = Tools.makeMap('checked compact declare defer disabled ismap multiple nohref noshade nowrap readonly selected', ' '); + var propFix = { + 'for': 'htmlFor', + 'class': 'className', + 'readonly': 'readOnly' + }; + var cssFix = { + 'float': 'cssFloat' + }; + + var attrHooks = {}, + cssHooks = {}; + + function DomQuery(selector, context) { + /*eslint new-cap:0 */ + return new DomQuery.fn.init(selector, context); + } + + function inArray(item, array) { + var i; + + if (array.indexOf) { + return array.indexOf(item); + } + + i = array.length; + while (i--) { + if (array[i] === item) { + return i; + } + } + + return -1; + } + + var whiteSpaceRegExp = /^\s*|\s*$/g; + + function trim(str) { + return (str === null || str === undef) ? '' : ("" + str).replace(whiteSpaceRegExp, ''); + } + + function each(obj, callback) { + var length, key, i, undef, value; + + if (obj) { + length = obj.length; + + if (length === undef) { + // Loop object items + for (key in obj) { + if (obj.hasOwnProperty(key)) { + value = obj[key]; + if (callback.call(value, key, value) === false) { + break; + } + } + } + } else { + // Loop array items + for (i = 0; i < length; i++) { + value = obj[i]; + if (callback.call(value, i, value) === false) { + break; + } + } + } + } + + return obj; + } + + function grep(array, callback) { + var out = []; + + each(array, function(i, item) { + if (callback(item, i)) { + out.push(item); + } + }); + + return out; + } + + function getElementDocument(element) { + if (!element) { + return doc; + } + + if (element.nodeType == 9) { + return element; + } + + return element.ownerDocument; + } + + DomQuery.fn = DomQuery.prototype = { + constructor: DomQuery, + + /** + * Selector for the current set. + * + * @property selector + * @type String + */ + selector: "", + + /** + * Context used to create the set. + * + * @property context + * @type Element + */ + context: null, + + /** + * Number of items in the current set. + * + * @property length + * @type Number + */ + length: 0, + + /** + * Constructs a new DomQuery instance with the specified selector or context. + * + * @constructor + * @method init + * @param {String/Array/DomQuery} selector Optional CSS selector/Array or array like object or HTML string. + * @param {Document/Element} context Optional context to search in. + */ + init: function(selector, context) { + var self = this, + match, node; + + if (!selector) { + return self; + } + + if (selector.nodeType) { + self.context = self[0] = selector; + self.length = 1; + + return self; + } + + if (context && context.nodeType) { + self.context = context; + } else { + if (context) { + return DomQuery(selector).attr(context); + } + + self.context = context = document; + } + + if (isString(selector)) { + self.selector = selector; + + if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) { + match = [null, selector, null]; + } else { + match = rquickExpr.exec(selector); + } + + if (match) { + if (match[1]) { + node = createFragment(selector, getElementDocument(context)).firstChild; + + while (node) { + push.call(self, node); + node = node.nextSibling; + } + } else { + node = getElementDocument(context).getElementById(match[2]); + + if (!node) { + return self; + } + + if (node.id !== match[2]) { + return self.find(selector); + } + + self.length = 1; + self[0] = node; + } + } else { + return DomQuery(context).find(selector); + } + } else { + this.add(selector, false); + } + + return self; + }, + + /** + * Converts the current set to an array. + * + * @method toArray + * @return {Array} Array of all nodes in set. + */ + toArray: function() { + return Tools.toArray(this); + }, + + /** + * Adds new nodes to the set. + * + * @method add + * @param {Array/tinymce.core.dom.DomQuery} items Array of all nodes to add to set. + * @param {Boolean} sort Optional sort flag that enables sorting of elements. + * @return {tinymce.dom.DomQuery} New instance with nodes added. + */ + add: function(items, sort) { + var self = this, + nodes, i; + + if (isString(items)) { + return self.add(DomQuery(items)); + } + + if (sort !== false) { + nodes = DomQuery.unique(self.toArray().concat(DomQuery.makeArray(items))); + self.length = nodes.length; + for (i = 0; i < nodes.length; i++) { + self[i] = nodes[i]; + } + } else { + push.apply(self, DomQuery.makeArray(items)); + } + + return self; + }, + + /** + * Sets/gets attributes on the elements in the current set. + * + * @method attr + * @param {String/Object} name Name of attribute to get or an object with attributes to set. + * @param {String} value Optional value to set. + * @return {tinymce.dom.DomQuery/String} Current set or the specified attribute when only the name is specified. + */ + attr: function(name, value) { + var self = this, + hook; + + if (typeof name === "object") { + each(name, function(name, value) { + self.attr(name, value); + }); + } else if (isDefined(value)) { + this.each(function() { + var hook; + + if (this.nodeType === 1) { + hook = attrHooks[name]; + if (hook && hook.set) { + hook.set(this, value); + return; + } + + if (value === null) { + this.removeAttribute(name, 2); + } else { + this.setAttribute(name, value, 2); + } + } + }); + } else { + if (self[0] && self[0].nodeType === 1) { + hook = attrHooks[name]; + if (hook && hook.get) { + return hook.get(self[0], name); + } + + if (booleanMap[name]) { + return self.prop(name) ? name : undef; + } + + value = self[0].getAttribute(name, 2); + + if (value === null) { + value = undef; + } + } + + return value; + } + + return self; + }, + + /** + * Removes attributse on the elements in the current set. + * + * @method removeAttr + * @param {String/Object} name Name of attribute to remove. + * @return {tinymce.dom.DomQuery/String} Current set. + */ + removeAttr: function(name) { + return this.attr(name, null); + }, + + /** + * Sets/gets properties on the elements in the current set. + * + * @method attr + * @param {String/Object} name Name of property to get or an object with properties to set. + * @param {String} value Optional value to set. + * @return {tinymce.dom.DomQuery/String} Current set or the specified property when only the name is specified. + */ + prop: function(name, value) { + var self = this; + + name = propFix[name] || name; + + if (typeof name === "object") { + each(name, function(name, value) { + self.prop(name, value); + }); + } else if (isDefined(value)) { + this.each(function() { + if (this.nodeType == 1) { + this[name] = value; + } + }); + } else { + if (self[0] && self[0].nodeType && name in self[0]) { + return self[0][name]; + } + + return value; + } + + return self; + }, + + /** + * Sets/gets styles on the elements in the current set. + * + * @method css + * @param {String/Object} name Name of style to get or an object with styles to set. + * @param {String} value Optional value to set. + * @return {tinymce.dom.DomQuery/String} Current set or the specified style when only the name is specified. + */ + css: function(name, value) { + var self = this, + elm, hook; + + function camel(name) { + return name.replace(/-(\D)/g, function(a, b) { + return b.toUpperCase(); + }); + } + + function dashed(name) { + return name.replace(/[A-Z]/g, function(a) { + return '-' + a; + }); + } + + if (typeof name === "object") { + each(name, function(name, value) { + self.css(name, value); + }); + } else { + if (isDefined(value)) { + name = camel(name); + + // Default px suffix on these + if (typeof value === 'number' && !numericCssMap[name]) { + value += 'px'; + } + + self.each(function() { + var style = this.style; + + hook = cssHooks[name]; + if (hook && hook.set) { + hook.set(this, value); + return; + } + + try { + this.style[cssFix[name] || name] = value; + } catch (ex) { + // Ignore + } + + if (value === null || value === '') { + if (style.removeProperty) { + style.removeProperty(dashed(name)); + } else { + style.removeAttribute(name); + } + } + }); + } else { + elm = self[0]; + + hook = cssHooks[name]; + if (hook && hook.get) { + return hook.get(elm); + } + + if (elm.ownerDocument.defaultView) { + try { + return elm.ownerDocument.defaultView.getComputedStyle(elm, null).getPropertyValue(dashed(name)); + } catch (ex) { + return undef; + } + } else if (elm.currentStyle) { + return elm.currentStyle[camel(name)]; + } + } + } + + return self; + }, + + /** + * Removes all nodes in set from the document. + * + * @method remove + * @return {tinymce.dom.DomQuery} Current set with the removed nodes. + */ + remove: function() { + var self = this, + node, i = this.length; + + while (i--) { + node = self[i]; + Event.clean(node); + + if (node.parentNode) { + node.parentNode.removeChild(node); + } + } + + return this; + }, + + /** + * Empties all elements in set. + * + * @method empty + * @return {tinymce.dom.DomQuery} Current set with the empty nodes. + */ + empty: function() { + var self = this, + node, i = this.length; + + while (i--) { + node = self[i]; + while (node.firstChild) { + node.removeChild(node.firstChild); + } + } + + return this; + }, + + /** + * Sets or gets the HTML of the current set or first set node. + * + * @method html + * @param {String} value Optional innerHTML value to set on each element. + * @return {tinymce.dom.DomQuery/String} Current set or the innerHTML of the first element. + */ + html: function(value) { + var self = this, + i; + + if (isDefined(value)) { + i = self.length; + + try { + while (i--) { + self[i].innerHTML = value; + } + } catch (ex) { + // Workaround for "Unknown runtime error" when DIV is added to P on IE + DomQuery(self[i]).empty().append(value); + } + + return self; + } + + return self[0] ? self[0].innerHTML : ''; + }, + + /** + * Sets or gets the text of the current set or first set node. + * + * @method text + * @param {String} value Optional innerText value to set on each element. + * @return {tinymce.dom.DomQuery/String} Current set or the innerText of the first element. + */ + text: function(value) { + var self = this, + i; + + if (isDefined(value)) { + i = self.length; + while (i--) { + if ("innerText" in self[i]) { + self[i].innerText = value; + } else { + self[0].textContent = value; + } + } + + return self; + } + + return self[0] ? (self[0].innerText || self[0].textContent) : ''; + }, + + /** + * Appends the specified node/html or node set to the current set nodes. + * + * @method append + * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to append to each element in set. + * @return {tinymce.dom.DomQuery} Current set. + */ + append: function() { + return domManipulate(this, arguments, function(node) { + // Either element or Shadow Root + if (this.nodeType === 1 || (this.host && this.host.nodeType === 1)) { + this.appendChild(node); + } + }); + }, + + /** + * Prepends the specified node/html or node set to the current set nodes. + * + * @method prepend + * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to prepend to each element in set. + * @return {tinymce.dom.DomQuery} Current set. + */ + prepend: function() { + return domManipulate(this, arguments, function(node) { + // Either element or Shadow Root + if (this.nodeType === 1 || (this.host && this.host.nodeType === 1)) { + this.insertBefore(node, this.firstChild); + } + }, true); + }, + + /** + * Adds the specified elements before current set nodes. + * + * @method before + * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to add before to each element in set. + * @return {tinymce.dom.DomQuery} Current set. + */ + before: function() { + var self = this; + + if (self[0] && self[0].parentNode) { + return domManipulate(self, arguments, function(node) { + this.parentNode.insertBefore(node, this); + }); + } + + return self; + }, + + /** + * Adds the specified elements after current set nodes. + * + * @method after + * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to add after to each element in set. + * @return {tinymce.dom.DomQuery} Current set. + */ + after: function() { + var self = this; + + if (self[0] && self[0].parentNode) { + return domManipulate(self, arguments, function(node) { + this.parentNode.insertBefore(node, this.nextSibling); + }, true); + } + + return self; + }, + + /** + * Appends the specified set nodes to the specified selector/instance. + * + * @method appendTo + * @param {String/Element/Array/tinymce.dom.DomQuery} val Item to append the current set to. + * @return {tinymce.dom.DomQuery} Current set with the appended nodes. + */ + appendTo: function(val) { + DomQuery(val).append(this); + + return this; + }, + + /** + * Prepends the specified set nodes to the specified selector/instance. + * + * @method prependTo + * @param {String/Element/Array/tinymce.dom.DomQuery} val Item to prepend the current set to. + * @return {tinymce.dom.DomQuery} Current set with the prepended nodes. + */ + prependTo: function(val) { + DomQuery(val).prepend(this); + + return this; + }, + + /** + * Replaces the nodes in set with the specified content. + * + * @method replaceWith + * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to replace nodes with. + * @return {tinymce.dom.DomQuery} Set with replaced nodes. + */ + replaceWith: function(content) { + return this.before(content).remove(); + }, + + /** + * Wraps all elements in set with the specified wrapper. + * + * @method wrap + * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with. + * @return {tinymce.dom.DomQuery} Set with wrapped nodes. + */ + wrap: function(content) { + return wrap(this, content); + }, + + /** + * Wraps all nodes in set with the specified wrapper. If the nodes are siblings all of them + * will be wrapped in the same wrapper. + * + * @method wrapAll + * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with. + * @return {tinymce.dom.DomQuery} Set with wrapped nodes. + */ + wrapAll: function(content) { + return wrap(this, content, true); + }, + + /** + * Wraps all elements inner contents in set with the specified wrapper. + * + * @method wrapInner + * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with. + * @return {tinymce.dom.DomQuery} Set with wrapped nodes. + */ + wrapInner: function(content) { + this.each(function() { + DomQuery(this).contents().wrapAll(content); + }); + + return this; + }, + + /** + * Unwraps all elements by removing the parent element of each item in set. + * + * @method unwrap + * @return {tinymce.dom.DomQuery} Set with unwrapped nodes. + */ + unwrap: function() { + return this.parent().each(function() { + DomQuery(this).replaceWith(this.childNodes); + }); + }, + + /** + * Clones all nodes in set. + * + * @method clone + * @return {tinymce.dom.DomQuery} Set with cloned nodes. + */ + clone: function() { + var result = []; + + this.each(function() { + result.push(this.cloneNode(true)); + }); + + return DomQuery(result); + }, + + /** + * Adds the specified class name to the current set elements. + * + * @method addClass + * @param {String} className Class name to add. + * @return {tinymce.dom.DomQuery} Current set. + */ + addClass: function(className) { + return this.toggleClass(className, true); + }, + + /** + * Removes the specified class name to the current set elements. + * + * @method removeClass + * @param {String} className Class name to remove. + * @return {tinymce.dom.DomQuery} Current set. + */ + removeClass: function(className) { + return this.toggleClass(className, false); + }, + + /** + * Toggles the specified class name on the current set elements. + * + * @method toggleClass + * @param {String} className Class name to add/remove. + * @param {Boolean} state Optional state to toggle on/off. + * @return {tinymce.dom.DomQuery} Current set. + */ + toggleClass: function(className, state) { + var self = this; + + // Functions are not supported + if (typeof className != 'string') { + return self; + } + + if (className.indexOf(' ') !== -1) { + each(className.split(' '), function() { + self.toggleClass(this, state); + }); + } else { + self.each(function(index, node) { + var existingClassName, classState; + + classState = hasClass(node, className); + if (classState !== state) { + existingClassName = node.className; + + if (classState) { + node.className = trim((" " + existingClassName + " ").replace(' ' + className + ' ', ' ')); + } else { + node.className += existingClassName ? ' ' + className : className; + } + } + }); + } + + return self; + }, + + /** + * Returns true/false if the first item in set has the specified class. + * + * @method hasClass + * @param {String} className Class name to check for. + * @return {Boolean} True/false if the set has the specified class. + */ + hasClass: function(className) { + return hasClass(this[0], className); + }, + + /** + * Executes the callback function for each item DomQuery collection. If you return false in the + * callback it will break the loop. + * + * @method each + * @param {function} callback Callback function to execute for each item. + * @return {tinymce.dom.DomQuery} Current set. + */ + each: function(callback) { + return each(this, callback); + }, + + /** + * Binds an event with callback function to the elements in set. + * + * @method on + * @param {String} name Name of the event to bind. + * @param {function} callback Callback function to execute when the event occurs. + * @return {tinymce.dom.DomQuery} Current set. + */ + on: function(name, callback) { + return this.each(function() { + Event.bind(this, name, callback); + }); + }, + + /** + * Unbinds an event with callback function to the elements in set. + * + * @method off + * @param {String} name Optional name of the event to bind. + * @param {function} callback Optional callback function to execute when the event occurs. + * @return {tinymce.dom.DomQuery} Current set. + */ + off: function(name, callback) { + return this.each(function() { + Event.unbind(this, name, callback); + }); + }, + + /** + * Triggers the specified event by name or event object. + * + * @method trigger + * @param {String/Object} name Name of the event to trigger or event object. + * @return {tinymce.dom.DomQuery} Current set. + */ + trigger: function(name) { + return this.each(function() { + if (typeof name == 'object') { + Event.fire(this, name.type, name); + } else { + Event.fire(this, name); + } + }); + }, + + /** + * Shows all elements in set. + * + * @method show + * @return {tinymce.dom.DomQuery} Current set. + */ + show: function() { + return this.css('display', ''); + }, + + /** + * Hides all elements in set. + * + * @method hide + * @return {tinymce.dom.DomQuery} Current set. + */ + hide: function() { + return this.css('display', 'none'); + }, + + /** + * Slices the current set. + * + * @method slice + * @param {Number} start Start index to slice at. + * @param {Number} end Optional end index to end slice at. + * @return {tinymce.dom.DomQuery} Sliced set. + */ + slice: function() { + return new DomQuery(slice.apply(this, arguments)); + }, + + /** + * Makes the set equal to the specified index. + * + * @method eq + * @param {Number} index Index to set it equal to. + * @return {tinymce.dom.DomQuery} Single item set. + */ + eq: function(index) { + return index === -1 ? this.slice(index) : this.slice(index, +index + 1); + }, + + /** + * Makes the set equal to first element in set. + * + * @method first + * @return {tinymce.dom.DomQuery} Single item set. + */ + first: function() { + return this.eq(0); + }, + + /** + * Makes the set equal to last element in set. + * + * @method last + * @return {tinymce.dom.DomQuery} Single item set. + */ + last: function() { + return this.eq(-1); + }, + + /** + * Finds elements by the specified selector for each element in set. + * + * @method find + * @param {String} selector Selector to find elements by. + * @return {tinymce.dom.DomQuery} Set with matches elements. + */ + find: function(selector) { + var i, l, ret = []; + + for (i = 0, l = this.length; i < l; i++) { + DomQuery.find(selector, this[i], ret); + } + + return DomQuery(ret); + }, + + /** + * Filters the current set with the specified selector. + * + * @method filter + * @param {String/function} selector Selector to filter elements by. + * @return {tinymce.dom.DomQuery} Set with filtered elements. + */ + filter: function(selector) { + if (typeof selector == 'function') { + return DomQuery(grep(this.toArray(), function(item, i) { + return selector(i, item); + })); + } + + return DomQuery(DomQuery.filter(selector, this.toArray())); + }, + + /** + * Gets the current node or any parent matching the specified selector. + * + * @method closest + * @param {String/Element/tinymce.dom.DomQuery} selector Selector or element to find. + * @return {tinymce.dom.DomQuery} Set with closest elements. + */ + closest: function(selector) { + var result = []; + + if (selector instanceof DomQuery) { + selector = selector[0]; + } + + this.each(function(i, node) { + while (node) { + if (typeof selector == 'string' && DomQuery(node).is(selector)) { + result.push(node); + break; + } else if (node == selector) { + result.push(node); + break; + } + + node = node.parentNode; + } + }); + + return DomQuery(result); + }, + + /** + * Returns the offset of the first element in set or sets the top/left css properties of all elements in set. + * + * @method offset + * @param {Object} offset Optional offset object to set on each item. + * @return {Object/tinymce.dom.DomQuery} Returns the first element offset or the current set if you specified an offset. + */ + offset: function(offset) { + var elm, doc, docElm; + var x = 0, + y = 0, + pos; + + if (!offset) { + elm = this[0]; + + if (elm) { + doc = elm.ownerDocument; + docElm = doc.documentElement; + + if (elm.getBoundingClientRect) { + pos = elm.getBoundingClientRect(); + x = pos.left + (docElm.scrollLeft || doc.body.scrollLeft) - docElm.clientLeft; + y = pos.top + (docElm.scrollTop || doc.body.scrollTop) - docElm.clientTop; + } + } + + return { + left: x, + top: y + }; + } + + return this.css(offset); + }, + + push: push, + sort: [].sort, + splice: [].splice + }; + + // Static members + Tools.extend(DomQuery, { + /** + * Extends the specified object with one or more objects. + * + * @static + * @method extend + * @param {Object} target Target object to extend with new items. + * @param {Object..} object Object to extend the target with. + * @return {Object} Extended input object. + */ + extend: Tools.extend, + + /** + * Creates an array out of an array like object. + * + * @static + * @method makeArray + * @param {Object} object Object to convert to array. + * @return {Array} Array produced from object. + */ + makeArray: function(object) { + if (isWindow(object) || object.nodeType) { + return [object]; + } + + return Tools.toArray(object); + }, + + /** + * Returns the index of the specified item inside the array. + * + * @static + * @method inArray + * @param {Object} item Item to look for. + * @param {Array} array Array to look for item in. + * @return {Number} Index of the item or -1. + */ + inArray: inArray, + + /** + * Returns true/false if the specified object is an array or not. + * + * @static + * @method isArray + * @param {Object} array Object to check if it's an array or not. + * @return {Boolean} True/false if the object is an array. + */ + isArray: Tools.isArray, + + /** + * Executes the callback function for each item in array/object. If you return false in the + * callback it will break the loop. + * + * @static + * @method each + * @param {Object} obj Object to iterate. + * @param {function} callback Callback function to execute for each item. + */ + each: each, + + /** + * Removes whitespace from the beginning and end of a string. + * + * @static + * @method trim + * @param {String} str String to remove whitespace from. + * @return {String} New string with removed whitespace. + */ + trim: trim, + + /** + * Filters out items from the input array by calling the specified function for each item. + * If the function returns false the item will be excluded if it returns true it will be included. + * + * @static + * @method grep + * @param {Array} array Array of items to loop though. + * @param {function} callback Function to call for each item. Include/exclude depends on it's return value. + * @return {Array} New array with values imported and filtered based in input. + * @example + * // Filter out some items, this will return an array with 4 and 5 + * var items = DomQuery.grep([1, 2, 3, 4, 5], function(v) {return v > 3;}); + */ + grep: grep, + + // Sizzle + find: Sizzle, + expr: Sizzle.selectors, + unique: Sizzle.uniqueSort, + text: Sizzle.getText, + contains: Sizzle.contains, + filter: function(expr, elems, not) { + var i = elems.length; + + if (not) { + expr = ":not(" + expr + ")"; + } + + while (i--) { + if (elems[i].nodeType != 1) { + elems.splice(i, 1); + } + } + + if (elems.length === 1) { + elems = DomQuery.find.matchesSelector(elems[0], expr) ? [elems[0]] : []; + } else { + elems = DomQuery.find.matches(expr, elems); + } + + return elems; + } + }); + + function dir(el, prop, until) { + var matched = [], + cur = el[prop]; + + if (typeof until != 'string' && until instanceof DomQuery) { + until = until[0]; + } + + while (cur && cur.nodeType !== 9) { + if (until !== undefined) { + if (cur === until) { + break; + } + + if (typeof until == 'string' && DomQuery(cur).is(until)) { + break; + } + } + + if (cur.nodeType === 1) { + matched.push(cur); + } + + cur = cur[prop]; + } + + return matched; + } + + function sibling(node, siblingName, nodeType, until) { + var result = []; + + if (until instanceof DomQuery) { + until = until[0]; + } + + for (; node; node = node[siblingName]) { + if (nodeType && node.nodeType !== nodeType) { + continue; + } + + if (until !== undefined) { + if (node === until) { + break; + } + + if (typeof until == 'string' && DomQuery(node).is(until)) { + break; + } + } + + result.push(node); + } + + return result; + } + + function firstSibling(node, siblingName, nodeType) { + for (node = node[siblingName]; node; node = node[siblingName]) { + if (node.nodeType == nodeType) { + return node; + } + } + + return null; + } + + each({ + /** + * Returns a new collection with the parent of each item in current collection matching the optional selector. + * + * @method parent + * @param {Element/tinymce.dom.DomQuery} node Node to match parents against. + * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents. + */ + parent: function(node) { + var parent = node.parentNode; + + return parent && parent.nodeType !== 11 ? parent : null; + }, + + /** + * Returns a new collection with the all the parents of each item in current collection matching the optional selector. + * + * @method parents + * @param {Element/tinymce.dom.DomQuery} node Node to match parents against. + * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents. + */ + parents: function(node) { + return dir(node, "parentNode"); + }, + + /** + * Returns a new collection with next sibling of each item in current collection matching the optional selector. + * + * @method next + * @param {Element/tinymce.dom.DomQuery} node Node to match the next element against. + * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements. + */ + next: function(node) { + return firstSibling(node, 'nextSibling', 1); + }, + + /** + * Returns a new collection with previous sibling of each item in current collection matching the optional selector. + * + * @method prev + * @param {Element/tinymce.dom.DomQuery} node Node to match the previous element against. + * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements. + */ + prev: function(node) { + return firstSibling(node, 'previousSibling', 1); + }, + + /** + * Returns all child elements matching the optional selector. + * + * @method children + * @param {Element/tinymce.dom.DomQuery} node Node to match the elements against. + * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements. + */ + children: function(node) { + return sibling(node.firstChild, 'nextSibling', 1); + }, + + /** + * Returns all child nodes matching the optional selector. + * + * @method contents + * @param {Element/tinymce.dom.DomQuery} node Node to get the contents of. + * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements. + */ + contents: function(node) { + return Tools.toArray((node.nodeName === "iframe" ? node.contentDocument || node.contentWindow.document : node).childNodes); + } + }, function(name, fn) { + DomQuery.fn[name] = function(selector) { + var self = this, + result = []; + + self.each(function() { + var nodes = fn.call(result, this, selector, result); + + if (nodes) { + if (DomQuery.isArray(nodes)) { + result.push.apply(result, nodes); + } else { + result.push(nodes); + } + } + }); + + // If traversing on multiple elements we might get the same elements twice + if (this.length > 1) { + if (!skipUniques[name]) { + result = DomQuery.unique(result); + } + + if (name.indexOf('parents') === 0) { + result = result.reverse(); + } + } + + result = DomQuery(result); + + if (selector) { + return result.filter(selector); + } + + return result; + }; + }); + + each({ + /** + * Returns a new collection with the all the parents until the matching selector/element + * of each item in current collection matching the optional selector. + * + * @method parentsUntil + * @param {Element/tinymce.dom.DomQuery} node Node to find parent of. + * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element. + * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents. + */ + parentsUntil: function(node, until) { + return dir(node, "parentNode", until); + }, + + /** + * Returns a new collection with all next siblings of each item in current collection matching the optional selector. + * + * @method nextUntil + * @param {Element/tinymce.dom.DomQuery} node Node to find next siblings on. + * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element. + * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements. + */ + nextUntil: function(node, until) { + return sibling(node, 'nextSibling', 1, until).slice(1); + }, + + /** + * Returns a new collection with all previous siblings of each item in current collection matching the optional selector. + * + * @method prevUntil + * @param {Element/tinymce.dom.DomQuery} node Node to find previous siblings on. + * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element. + * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements. + */ + prevUntil: function(node, until) { + return sibling(node, 'previousSibling', 1, until).slice(1); + } + }, function(name, fn) { + DomQuery.fn[name] = function(selector, filter) { + var self = this, + result = []; + + self.each(function() { + var nodes = fn.call(result, this, selector, result); + + if (nodes) { + if (DomQuery.isArray(nodes)) { + result.push.apply(result, nodes); + } else { + result.push(nodes); + } + } + }); + + // If traversing on multiple elements we might get the same elements twice + if (this.length > 1) { + result = DomQuery.unique(result); + + if (name.indexOf('parents') === 0 || name === 'prevUntil') { + result = result.reverse(); + } + } + + result = DomQuery(result); + + if (filter) { + return result.filter(filter); + } + + return result; + }; + }); + + /** + * Returns true/false if the current set items matches the selector. + * + * @method is + * @param {String} selector Selector to match the elements against. + * @return {Boolean} True/false if the current set matches the selector. + */ + DomQuery.fn.is = function(selector) { + return !!selector && this.filter(selector).length > 0; + }; + + DomQuery.fn.init.prototype = DomQuery.fn; + + DomQuery.overrideDefaults = function(callback) { + var defaults; + + function sub(selector, context) { + defaults = defaults || callback(); + + if (arguments.length === 0) { + selector = defaults.element; + } + + if (!context) { + context = defaults.context; + } + + return new sub.fn.init(selector, context); + } + + DomQuery.extend(sub, this); + + return sub; + }; + + function appendHooks(targetHooks, prop, hooks) { + each(hooks, function(name, func) { + targetHooks[name] = targetHooks[name] || {}; + targetHooks[name][prop] = func; + }); + } + + if (Env.ie && Env.ie < 8) { + appendHooks(attrHooks, 'get', { + maxlength: function(elm) { + var value = elm.maxLength; + + if (value === 0x7fffffff) { + return undef; + } + + return value; + }, + + size: function(elm) { + var value = elm.size; + + if (value === 20) { + return undef; + } + + return value; + }, + + 'class': function(elm) { + return elm.className; + }, + + style: function(elm) { + var value = elm.style.cssText; + + if (value.length === 0) { + return undef; + } + + return value; + } + }); + + appendHooks(attrHooks, 'set', { + 'class': function(elm, value) { + elm.className = value; + }, + + style: function(elm, value) { + elm.style.cssText = value; + } + }); + } + + if (Env.ie && Env.ie < 9) { + /*jshint sub:true */ + /*eslint dot-notation: 0*/ + cssFix['float'] = 'styleFloat'; + + appendHooks(cssHooks, 'set', { + opacity: function(elm, value) { + var style = elm.style; + + if (value === null || value === '') { + style.removeAttribute('filter'); + } else { + style.zoom = 1; + style.filter = 'alpha(opacity=' + (value * 100) + ')'; + } + } + }); + } + + DomQuery.attrHooks = attrHooks; + DomQuery.cssHooks = cssHooks; + + return DomQuery; + } + ); + + /** + * Styles.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class is used to parse CSS styles it also compresses styles to reduce the output size. + * + * @example + * var Styles = new tinymce.html.Styles({ + * url_converter: function(url) { + * return url; + * } + * }); + * + * styles = Styles.parse('border: 1px solid red'); + * styles.color = 'red'; + * + * console.log(new tinymce.html.StyleSerializer().serialize(styles)); + * + * @class tinymce.html.Styles + * @version 3.4 + */ + define( + 'tinymce.core.html.Styles', [], + function() { + return function(settings, schema) { + /*jshint maxlen:255 */ + /*eslint max-len:0 */ + var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi, + urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi, + styleRegExp = /\s*([^:]+):\s*([^;]+);?/g, + trimRightRegExp = /\s+$/, + i, encodingLookup = {}, + encodingItems, validStyles, invalidStyles, invisibleChar = '\uFEFF'; + + settings = settings || {}; + + if (schema) { + validStyles = schema.getValidStyles(); + invalidStyles = schema.getInvalidStyles(); + } + + encodingItems = ('\\" \\\' \\; \\: ; : ' + invisibleChar).split(' '); + for (i = 0; i < encodingItems.length; i++) { + encodingLookup[encodingItems[i]] = invisibleChar + i; + encodingLookup[invisibleChar + i] = encodingItems[i]; + } + + function toHex(match, r, g, b) { + function hex(val) { + val = parseInt(val, 10).toString(16); + + return val.length > 1 ? val : '0' + val; // 0 -> 00 + } + + return '#' + hex(r) + hex(g) + hex(b); + } + + return { + /** + * Parses the specified RGB color value and returns a hex version of that color. + * + * @method toHex + * @param {String} color RGB string value like rgb(1,2,3) + * @return {String} Hex version of that RGB value like #FF00FF. + */ + toHex: function(color) { + return color.replace(rgbRegExp, toHex); + }, + + /** + * Parses the specified style value into an object collection. This parser will also + * merge and remove any redundant items that browsers might have added. It will also convert non hex + * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings. + * + * @method parse + * @param {String} css Style value to parse for example: border:1px solid red;. + * @return {Object} Object representation of that style like {border: '1px solid red'} + */ + parse: function(css) { + var styles = {}, + matches, name, value, isEncoded, urlConverter = settings.url_converter; + var urlConverterScope = settings.url_converter_scope || this; + + function compress(prefix, suffix, noJoin) { + var top, right, bottom, left; + + top = styles[prefix + '-top' + suffix]; + if (!top) { + return; + } + + right = styles[prefix + '-right' + suffix]; + if (!right) { + return; + } + + bottom = styles[prefix + '-bottom' + suffix]; + if (!bottom) { + return; + } + + left = styles[prefix + '-left' + suffix]; + if (!left) { + return; + } + + var box = [top, right, bottom, left]; + i = box.length - 1; + while (i--) { + if (box[i] !== box[i + 1]) { + break; + } + } + + if (i > -1 && noJoin) { + return; + } + + styles[prefix + suffix] = i == -1 ? box[0] : box.join(' '); + delete styles[prefix + '-top' + suffix]; + delete styles[prefix + '-right' + suffix]; + delete styles[prefix + '-bottom' + suffix]; + delete styles[prefix + '-left' + suffix]; + } + + /** + * Checks if the specific style can be compressed in other words if all border-width are equal. + */ + function canCompress(key) { + var value = styles[key], + i; + + if (!value) { + return; + } + + value = value.split(' '); + i = value.length; + while (i--) { + if (value[i] !== value[0]) { + return false; + } + } + + styles[key] = value[0]; + + return true; + } + + /** + * Compresses multiple styles into one style. + */ + function compress2(target, a, b, c) { + if (!canCompress(a)) { + return; + } + + if (!canCompress(b)) { + return; + } + + if (!canCompress(c)) { + return; + } + + // Compress + styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c]; + delete styles[a]; + delete styles[b]; + delete styles[c]; + } + + // Encodes the specified string by replacing all \" \' ; : with _<num> + function encode(str) { + isEncoded = true; + + return encodingLookup[str]; + } + + // Decodes the specified string by replacing all _<num> with it's original value \" \' etc + // It will also decode the \" \' if keepSlashes is set to fale or omitted + function decode(str, keepSlashes) { + if (isEncoded) { + str = str.replace(/\uFEFF[0-9]/g, function(str) { + return encodingLookup[str]; + }); + } + + if (!keepSlashes) { + str = str.replace(/\\([\'\";:])/g, "$1"); + } + + return str; + } + + function decodeSingleHexSequence(escSeq) { + return String.fromCharCode(parseInt(escSeq.slice(1), 16)); + } + + function decodeHexSequences(value) { + return value.replace(/\\[0-9a-f]+/gi, decodeSingleHexSequence); + } + + function processUrl(match, url, url2, url3, str, str2) { + str = str || str2; + + if (str) { + str = decode(str); + + // Force strings into single quote format + return "'" + str.replace(/\'/g, "\\'") + "'"; + } + + url = decode(url || url2 || url3); + + if (!settings.allow_script_urls) { + var scriptUrl = url.replace(/[\s\r\n]+/g, ''); + + if (/(java|vb)script:/i.test(scriptUrl)) { + return ""; + } + + if (!settings.allow_svg_data_urls && /^data:image\/svg/i.test(scriptUrl)) { + return ""; + } + } + + // Convert the URL to relative/absolute depending on config + if (urlConverter) { + url = urlConverter.call(urlConverterScope, url, 'style'); + } + + // Output new URL format + return "url('" + url.replace(/\'/g, "\\'") + "')"; + } + + if (css) { + css = css.replace(/[\u0000-\u001F]/g, ''); + + // Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing + css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) { + return str.replace(/[;:]/g, encode); + }); + + // Parse styles + while ((matches = styleRegExp.exec(css))) { + styleRegExp.lastIndex = matches.index + matches[0].length; + name = matches[1].replace(trimRightRegExp, '').toLowerCase(); + value = matches[2].replace(trimRightRegExp, ''); + + if (name && value) { + // Decode escaped sequences like \65 -> e + name = decodeHexSequences(name); + value = decodeHexSequences(value); + + // Skip properties with double quotes and sequences like \" \' in their names + // See 'mXSS Attacks: Attacking well-secured Web-Applications by using innerHTML Mutations' + // https://cure53.de/fp170.pdf + if (name.indexOf(invisibleChar) !== -1 || name.indexOf('"') !== -1) { + continue; + } + + // Don't allow behavior name or expression/comments within the values + if (!settings.allow_script_urls && (name == "behavior" || /expression\s*\(|\/\*|\*\//.test(value))) { + continue; + } + + // Opera will produce 700 instead of bold in their style values + if (name === 'font-weight' && value === '700') { + value = 'bold'; + } else if (name === 'color' || name === 'background-color') { // Lowercase colors like RED + value = value.toLowerCase(); + } + + // Convert RGB colors to HEX + value = value.replace(rgbRegExp, toHex); + + // Convert URLs and force them into url('value') format + value = value.replace(urlOrStrRegExp, processUrl); + styles[name] = isEncoded ? decode(value, true) : value; + } + } + // Compress the styles to reduce it's size for example IE will expand styles + compress("border", "", true); + compress("border", "-width"); + compress("border", "-color"); + compress("border", "-style"); + compress("padding", ""); + compress("margin", ""); + compress2('border', 'border-width', 'border-style', 'border-color'); + + // Remove pointless border, IE produces these + if (styles.border === 'medium none') { + delete styles.border; + } + + // IE 11 will produce a border-image: none when getting the style attribute from <p style="border: 1px solid red"></p> + // So let us assume it shouldn't be there + if (styles['border-image'] === 'none') { + delete styles['border-image']; + } + } + + return styles; + }, + + /** + * Serializes the specified style object into a string. + * + * @method serialize + * @param {Object} styles Object to serialize as string for example: {border: '1px solid red'} + * @param {String} elementName Optional element name, if specified only the styles that matches the schema will be serialized. + * @return {String} String representation of the style object for example: border: 1px solid red. + */ + serialize: function(styles, elementName) { + var css = '', + name, value; + + function serializeStyles(name) { + var styleList, i, l, value; + + styleList = validStyles[name]; + if (styleList) { + for (i = 0, l = styleList.length; i < l; i++) { + name = styleList[i]; + value = styles[name]; + + if (value) { + css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';'; + } + } + } + } + + function isValid(name, elementName) { + var styleMap; + + styleMap = invalidStyles['*']; + if (styleMap && styleMap[name]) { + return false; + } + + styleMap = invalidStyles[elementName]; + if (styleMap && styleMap[name]) { + return false; + } + + return true; + } + + // Serialize styles according to schema + if (elementName && validStyles) { + // Serialize global styles and element specific styles + serializeStyles('*'); + serializeStyles(elementName); + } else { + // Output the styles in the order they are inside the object + for (name in styles) { + value = styles[name]; + + if (value && (!invalidStyles || isValid(name, elementName))) { + css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';'; + } + } + } + + return css; + } + }; + }; + } + ); + + /** + * TreeWalker.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * TreeWalker class enables you to walk the DOM in a linear manner. + * + * @class tinymce.dom.TreeWalker + * @example + * var walker = new tinymce.dom.TreeWalker(startNode); + * + * do { + * console.log(walker.current()); + * } while (walker.next()); + */ + define( + 'tinymce.core.dom.TreeWalker', [], + function() { + /** + * Constructs a new TreeWalker instance. + * + * @constructor + * @method TreeWalker + * @param {Node} startNode Node to start walking from. + * @param {node} rootNode Optional root node to never walk out of. + */ + return function(startNode, rootNode) { + var node = startNode; + + function findSibling(node, startName, siblingName, shallow) { + var sibling, parent; + + if (node) { + // Walk into nodes if it has a start + if (!shallow && node[startName]) { + return node[startName]; + } + + // Return the sibling if it has one + if (node != rootNode) { + sibling = node[siblingName]; + if (sibling) { + return sibling; + } + + // Walk up the parents to look for siblings + for (parent = node.parentNode; parent && parent != rootNode; parent = parent.parentNode) { + sibling = parent[siblingName]; + if (sibling) { + return sibling; + } + } + } + } + } + + function findPreviousNode(node, startName, siblingName, shallow) { + var sibling, parent, child; + + if (node) { + sibling = node[siblingName]; + if (rootNode && sibling === rootNode) { + return; + } + + if (sibling) { + if (!shallow) { + // Walk up the parents to look for siblings + for (child = sibling[startName]; child; child = child[startName]) { + if (!child[startName]) { + return child; + } + } + } + + return sibling; + } + + parent = node.parentNode; + if (parent && parent !== rootNode) { + return parent; + } + } + } + + /** + * Returns the current node. + * + * @method current + * @return {Node} Current node where the walker is. + */ + this.current = function() { + return node; + }; + + /** + * Walks to the next node in tree. + * + * @method next + * @return {Node} Current node where the walker is after moving to the next node. + */ + this.next = function(shallow) { + node = findSibling(node, 'firstChild', 'nextSibling', shallow); + return node; + }; + + /** + * Walks to the previous node in tree. + * + * @method prev + * @return {Node} Current node where the walker is after moving to the previous node. + */ + this.prev = function(shallow) { + node = findSibling(node, 'lastChild', 'previousSibling', shallow); + return node; + }; + + this.prev2 = function(shallow) { + node = findPreviousNode(node, 'lastChild', 'previousSibling', shallow); + return node; + }; + }; + } + ); + + /** + * Entities.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /*jshint bitwise:false */ + /*eslint no-bitwise:0 */ + + /** + * Entity encoder class. + * + * @class tinymce.html.Entities + * @static + * @version 3.4 + */ + define( + 'tinymce.core.html.Entities', [ + "tinymce.core.util.Tools" + ], + function(Tools) { + var makeMap = Tools.makeMap; + + var namedEntities, baseEntities, reverseEntities, + attrsCharsRegExp = /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, + textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, + rawCharsRegExp = /[<>&\"\']/g, + entityRegExp = /&#([a-z0-9]+);?|&([a-z0-9]+);/gi, + asciiMap = { + 128: "\u20AC", + 130: "\u201A", + 131: "\u0192", + 132: "\u201E", + 133: "\u2026", + 134: "\u2020", + 135: "\u2021", + 136: "\u02C6", + 137: "\u2030", + 138: "\u0160", + 139: "\u2039", + 140: "\u0152", + 142: "\u017D", + 145: "\u2018", + 146: "\u2019", + 147: "\u201C", + 148: "\u201D", + 149: "\u2022", + 150: "\u2013", + 151: "\u2014", + 152: "\u02DC", + 153: "\u2122", + 154: "\u0161", + 155: "\u203A", + 156: "\u0153", + 158: "\u017E", + 159: "\u0178" + }; + + // Raw entities + baseEntities = { + '\"': '"', // Needs to be escaped since the YUI compressor would otherwise break the code + "'": ''', + '<': '<', + '>': '>', + '&': '&', + '\u0060': '`' + }; + + // Reverse lookup table for raw entities + reverseEntities = { + '<': '<', + '>': '>', + '&': '&', + '"': '"', + ''': "'" + }; + + // Decodes text by using the browser + function nativeDecode(text) { + var elm; + + elm = document.createElement("div"); + elm.innerHTML = text; + + return elm.textContent || elm.innerText || text; + } + + // Build a two way lookup table for the entities + function buildEntitiesLookup(items, radix) { + var i, chr, entity, lookup = {}; + + if (items) { + items = items.split(','); + radix = radix || 10; + + // Build entities lookup table + for (i = 0; i < items.length; i += 2) { + chr = String.fromCharCode(parseInt(items[i], radix)); + + // Only add non base entities + if (!baseEntities[chr]) { + entity = '&' + items[i + 1] + ';'; + lookup[chr] = entity; + lookup[entity] = chr; + } + } + + return lookup; + } + } + + // Unpack entities lookup where the numbers are in radix 32 to reduce the size + namedEntities = buildEntitiesLookup( + '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' + + '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' + + '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' + + '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' + + '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' + + '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' + + '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' + + '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' + + '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' + + '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' + + 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' + + 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' + + 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' + + 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' + + 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' + + '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' + + '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' + + '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' + + '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' + + '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' + + 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' + + 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' + + 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' + + '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' + + '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32); + + var Entities = { + /** + * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded. + * + * @method encodeRaw + * @param {String} text Text to encode. + * @param {Boolean} attr Optional flag to specify if the text is attribute contents. + * @return {String} Entity encoded text. + */ + encodeRaw: function(text, attr) { + return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) { + return baseEntities[chr] || chr; + }); + }, + + /** + * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents + * since it doesn't know if the context is within a attribute or text node. This was added for compatibility + * and is exposed as the DOMUtils.encode function. + * + * @method encodeAllRaw + * @param {String} text Text to encode. + * @return {String} Entity encoded text. + */ + encodeAllRaw: function(text) { + return ('' + text).replace(rawCharsRegExp, function(chr) { + return baseEntities[chr] || chr; + }); + }, + + /** + * Encodes the specified string using numeric entities. The core entities will be + * encoded as named ones but all non lower ascii characters will be encoded into numeric entities. + * + * @method encodeNumeric + * @param {String} text Text to encode. + * @param {Boolean} attr Optional flag to specify if the text is attribute contents. + * @return {String} Entity encoded text. + */ + encodeNumeric: function(text, attr) { + return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) { + // Multi byte sequence convert it to a single entity + if (chr.length > 1) { + return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';'; + } + + return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';'; + }); + }, + + /** + * Encodes the specified string using named entities. The core entities will be encoded + * as named ones but all non lower ascii characters will be encoded into named entities. + * + * @method encodeNamed + * @param {String} text Text to encode. + * @param {Boolean} attr Optional flag to specify if the text is attribute contents. + * @param {Object} entities Optional parameter with entities to use. + * @return {String} Entity encoded text. + */ + encodeNamed: function(text, attr, entities) { + entities = entities || namedEntities; + + return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) { + return baseEntities[chr] || entities[chr] || chr; + }); + }, + + /** + * Returns an encode function based on the name(s) and it's optional entities. + * + * @method getEncodeFunc + * @param {String} name Comma separated list of encoders for example named,numeric. + * @param {String} entities Optional parameter with entities to use instead of the built in set. + * @return {function} Encode function to be used. + */ + getEncodeFunc: function(name, entities) { + entities = buildEntitiesLookup(entities) || namedEntities; + + function encodeNamedAndNumeric(text, attr) { + return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) { + if (baseEntities[chr] !== undefined) { + return baseEntities[chr]; + } + + if (entities[chr] !== undefined) { + return entities[chr]; + } + + // Convert multi-byte sequences to a single entity. + if (chr.length > 1) { + return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';'; + } + + return '&#' + chr.charCodeAt(0) + ';'; + }); + } + + function encodeCustomNamed(text, attr) { + return Entities.encodeNamed(text, attr, entities); + } + + // Replace + with , to be compatible with previous TinyMCE versions + name = makeMap(name.replace(/\+/g, ',')); + + // Named and numeric encoder + if (name.named && name.numeric) { + return encodeNamedAndNumeric; + } + + // Named encoder + if (name.named) { + // Custom names + if (entities) { + return encodeCustomNamed; + } + + return Entities.encodeNamed; + } + + // Numeric + if (name.numeric) { + return Entities.encodeNumeric; + } + + // Raw encoder + return Entities.encodeRaw; + }, + + /** + * Decodes the specified string, this will replace entities with raw UTF characters. + * + * @method decode + * @param {String} text Text to entity decode. + * @return {String} Entity decoded string. + */ + decode: function(text) { + return text.replace(entityRegExp, function(all, numeric) { + if (numeric) { + if (numeric.charAt(0).toLowerCase() === 'x') { + numeric = parseInt(numeric.substr(1), 16); + } else { + numeric = parseInt(numeric, 10); + } + + // Support upper UTF + if (numeric > 0xFFFF) { + numeric -= 0x10000; + + return String.fromCharCode(0xD800 + (numeric >> 10), 0xDC00 + (numeric & 0x3FF)); + } + + return asciiMap[numeric] || String.fromCharCode(numeric); + } + + return reverseEntities[all] || namedEntities[all] || nativeDecode(all); + }); + } + }; + + return Entities; + } + ); + + /** + * Range.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Old IE Range. + * + * @private + * @class tinymce.dom.Range + */ + define( + 'tinymce.core.dom.Range', [ + "tinymce.core.util.Tools" + ], + function(Tools) { + // Range constructor + function Range(dom) { + var self = this, + doc = dom.doc, + EXTRACT = 0, + CLONE = 1, + DELETE = 2, + TRUE = true, + FALSE = false, + START_OFFSET = 'startOffset', + START_CONTAINER = 'startContainer', + END_CONTAINER = 'endContainer', + END_OFFSET = 'endOffset', + extend = Tools.extend, + nodeIndex = dom.nodeIndex; + + function createDocumentFragment() { + return doc.createDocumentFragment(); + } + + function setStart(n, o) { + _setEndPoint(TRUE, n, o); + } + + function setEnd(n, o) { + _setEndPoint(FALSE, n, o); + } + + function setStartBefore(n) { + setStart(n.parentNode, nodeIndex(n)); + } + + function setStartAfter(n) { + setStart(n.parentNode, nodeIndex(n) + 1); + } + + function setEndBefore(n) { + setEnd(n.parentNode, nodeIndex(n)); + } + + function setEndAfter(n) { + setEnd(n.parentNode, nodeIndex(n) + 1); + } + + function collapse(ts) { + if (ts) { + self[END_CONTAINER] = self[START_CONTAINER]; + self[END_OFFSET] = self[START_OFFSET]; + } else { + self[START_CONTAINER] = self[END_CONTAINER]; + self[START_OFFSET] = self[END_OFFSET]; + } + + self.collapsed = TRUE; + } + + function selectNode(n) { + setStartBefore(n); + setEndAfter(n); + } + + function selectNodeContents(n) { + setStart(n, 0); + setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length); + } + + function compareBoundaryPoints(h, r) { + var sc = self[START_CONTAINER], + so = self[START_OFFSET], + ec = self[END_CONTAINER], + eo = self[END_OFFSET], + rsc = r.startContainer, + rso = r.startOffset, + rec = r.endContainer, + reo = r.endOffset; + + // Check START_TO_START + if (h === 0) { + return _compareBoundaryPoints(sc, so, rsc, rso); + } + + // Check START_TO_END + if (h === 1) { + return _compareBoundaryPoints(ec, eo, rsc, rso); + } + + // Check END_TO_END + if (h === 2) { + return _compareBoundaryPoints(ec, eo, rec, reo); + } + + // Check END_TO_START + if (h === 3) { + return _compareBoundaryPoints(sc, so, rec, reo); + } + } + + function deleteContents() { + _traverse(DELETE); + } + + function extractContents() { + return _traverse(EXTRACT); + } + + function cloneContents() { + return _traverse(CLONE); + } + + function insertNode(n) { + var startContainer = this[START_CONTAINER], + startOffset = this[START_OFFSET], + nn, o; + + // Node is TEXT_NODE or CDATA + if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) { + if (!startOffset) { + // At the start of text + startContainer.parentNode.insertBefore(n, startContainer); + } else if (startOffset >= startContainer.nodeValue.length) { + // At the end of text + dom.insertAfter(n, startContainer); + } else { + // Middle, need to split + nn = startContainer.splitText(startOffset); + startContainer.parentNode.insertBefore(n, nn); + } + } else { + // Insert element node + if (startContainer.childNodes.length > 0) { + o = startContainer.childNodes[startOffset]; + } + + if (o) { + startContainer.insertBefore(n, o); + } else { + if (startContainer.nodeType == 3) { + dom.insertAfter(n, startContainer); + } else { + startContainer.appendChild(n); + } + } + } + } + + function surroundContents(n) { + var f = self.extractContents(); + + self.insertNode(n); + n.appendChild(f); + self.selectNode(n); + } + + function cloneRange() { + return extend(new Range(dom), { + startContainer: self[START_CONTAINER], + startOffset: self[START_OFFSET], + endContainer: self[END_CONTAINER], + endOffset: self[END_OFFSET], + collapsed: self.collapsed, + commonAncestorContainer: self.commonAncestorContainer + }); + } + + // Private methods + + function _getSelectedNode(container, offset) { + var child; + + // TEXT_NODE + if (container.nodeType == 3) { + return container; + } + + if (offset < 0) { + return container; + } + + child = container.firstChild; + while (child && offset > 0) { + --offset; + child = child.nextSibling; + } + + if (child) { + return child; + } + + return container; + } + + function _isCollapsed() { + return (self[START_CONTAINER] == self[END_CONTAINER] && self[START_OFFSET] == self[END_OFFSET]); + } + + function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) { + var c, offsetC, n, cmnRoot, childA, childB; + + // In the first case the boundary-points have the same container. A is before B + // if its offset is less than the offset of B, A is equal to B if its offset is + // equal to the offset of B, and A is after B if its offset is greater than the + // offset of B. + if (containerA == containerB) { + if (offsetA == offsetB) { + return 0; // equal + } + + if (offsetA < offsetB) { + return -1; // before + } + + return 1; // after + } + + // In the second case a child node C of the container of A is an ancestor + // container of B. In this case, A is before B if the offset of A is less than or + // equal to the index of the child node C and A is after B otherwise. + c = containerB; + while (c && c.parentNode != containerA) { + c = c.parentNode; + } + + if (c) { + offsetC = 0; + n = containerA.firstChild; + + while (n != c && offsetC < offsetA) { + offsetC++; + n = n.nextSibling; + } + + if (offsetA <= offsetC) { + return -1; // before + } + + return 1; // after + } + + // In the third case a child node C of the container of B is an ancestor container + // of A. In this case, A is before B if the index of the child node C is less than + // the offset of B and A is after B otherwise. + c = containerA; + while (c && c.parentNode != containerB) { + c = c.parentNode; + } + + if (c) { + offsetC = 0; + n = containerB.firstChild; + + while (n != c && offsetC < offsetB) { + offsetC++; + n = n.nextSibling; + } + + if (offsetC < offsetB) { + return -1; // before + } + + return 1; // after + } + + // In the fourth case, none of three other cases hold: the containers of A and B + // are siblings or descendants of sibling nodes. In this case, A is before B if + // the container of A is before the container of B in a pre-order traversal of the + // Ranges' context tree and A is after B otherwise. + cmnRoot = dom.findCommonAncestor(containerA, containerB); + childA = containerA; + + while (childA && childA.parentNode != cmnRoot) { + childA = childA.parentNode; + } + + if (!childA) { + childA = cmnRoot; + } + + childB = containerB; + while (childB && childB.parentNode != cmnRoot) { + childB = childB.parentNode; + } + + if (!childB) { + childB = cmnRoot; + } + + if (childA == childB) { + return 0; // equal + } + + n = cmnRoot.firstChild; + while (n) { + if (n == childA) { + return -1; // before + } + + if (n == childB) { + return 1; // after + } + + n = n.nextSibling; + } + } + + function _setEndPoint(st, n, o) { + var ec, sc; + + if (st) { + self[START_CONTAINER] = n; + self[START_OFFSET] = o; + } else { + self[END_CONTAINER] = n; + self[END_OFFSET] = o; + } + + // If one boundary-point of a Range is set to have a root container + // other than the current one for the Range, the Range is collapsed to + // the new position. This enforces the restriction that both boundary- + // points of a Range must have the same root container. + ec = self[END_CONTAINER]; + while (ec.parentNode) { + ec = ec.parentNode; + } + + sc = self[START_CONTAINER]; + while (sc.parentNode) { + sc = sc.parentNode; + } + + if (sc == ec) { + // The start position of a Range is guaranteed to never be after the + // end position. To enforce this restriction, if the start is set to + // be at a position after the end, the Range is collapsed to that + // position. + if (_compareBoundaryPoints(self[START_CONTAINER], self[START_OFFSET], self[END_CONTAINER], self[END_OFFSET]) > 0) { + self.collapse(st); + } + } else { + self.collapse(st); + } + + self.collapsed = _isCollapsed(); + self.commonAncestorContainer = dom.findCommonAncestor(self[START_CONTAINER], self[END_CONTAINER]); + } + + function _traverse(how) { + var c, endContainerDepth = 0, + startContainerDepth = 0, + p, depthDiff, startNode, endNode, sp, ep; + + if (self[START_CONTAINER] == self[END_CONTAINER]) { + return _traverseSameContainer(how); + } + + for (c = self[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) { + if (p == self[START_CONTAINER]) { + return _traverseCommonStartContainer(c, how); + } + + ++endContainerDepth; + } + + for (c = self[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) { + if (p == self[END_CONTAINER]) { + return _traverseCommonEndContainer(c, how); + } + + ++startContainerDepth; + } + + depthDiff = startContainerDepth - endContainerDepth; + + startNode = self[START_CONTAINER]; + while (depthDiff > 0) { + startNode = startNode.parentNode; + depthDiff--; + } + + endNode = self[END_CONTAINER]; + while (depthDiff < 0) { + endNode = endNode.parentNode; + depthDiff++; + } + + // ascend the ancestor hierarchy until we have a common parent. + for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) { + startNode = sp; + endNode = ep; + } + + return _traverseCommonAncestors(startNode, endNode, how); + } + + function _traverseSameContainer(how) { + var frag, s, sub, n, cnt, sibling, xferNode, start, len; + + if (how != DELETE) { + frag = createDocumentFragment(); + } + + // If selection is empty, just return the fragment + if (self[START_OFFSET] == self[END_OFFSET]) { + return frag; + } + + // Text node needs special case handling + if (self[START_CONTAINER].nodeType == 3) { // TEXT_NODE + // get the substring + s = self[START_CONTAINER].nodeValue; + sub = s.substring(self[START_OFFSET], self[END_OFFSET]); + + // set the original text node to its new value + if (how != CLONE) { + n = self[START_CONTAINER]; + start = self[START_OFFSET]; + len = self[END_OFFSET] - self[START_OFFSET]; + + if (start === 0 && len >= n.nodeValue.length - 1) { + n.parentNode.removeChild(n); + } else { + n.deleteData(start, len); + } + + // Nothing is partially selected, so collapse to start point + self.collapse(TRUE); + } + + if (how == DELETE) { + return; + } + + if (sub.length > 0) { + frag.appendChild(doc.createTextNode(sub)); + } + + return frag; + } + + // Copy nodes between the start/end offsets. + n = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]); + cnt = self[END_OFFSET] - self[START_OFFSET]; + + while (n && cnt > 0) { + sibling = n.nextSibling; + xferNode = _traverseFullySelected(n, how); + + if (frag) { + frag.appendChild(xferNode); + } + + --cnt; + n = sibling; + } + + // Nothing is partially selected, so collapse to start point + if (how != CLONE) { + self.collapse(TRUE); + } + + return frag; + } + + function _traverseCommonStartContainer(endAncestor, how) { + var frag, n, endIdx, cnt, sibling, xferNode; + + if (how != DELETE) { + frag = createDocumentFragment(); + } + + n = _traverseRightBoundary(endAncestor, how); + + if (frag) { + frag.appendChild(n); + } + + endIdx = nodeIndex(endAncestor); + cnt = endIdx - self[START_OFFSET]; + + if (cnt <= 0) { + // Collapse to just before the endAncestor, which + // is partially selected. + if (how != CLONE) { + self.setEndBefore(endAncestor); + self.collapse(FALSE); + } + + return frag; + } + + n = endAncestor.previousSibling; + while (cnt > 0) { + sibling = n.previousSibling; + xferNode = _traverseFullySelected(n, how); + + if (frag) { + frag.insertBefore(xferNode, frag.firstChild); + } + + --cnt; + n = sibling; + } + + // Collapse to just before the endAncestor, which + // is partially selected. + if (how != CLONE) { + self.setEndBefore(endAncestor); + self.collapse(FALSE); + } + + return frag; + } + + function _traverseCommonEndContainer(startAncestor, how) { + var frag, startIdx, n, cnt, sibling, xferNode; + + if (how != DELETE) { + frag = createDocumentFragment(); + } + + n = _traverseLeftBoundary(startAncestor, how); + if (frag) { + frag.appendChild(n); + } + + startIdx = nodeIndex(startAncestor); + ++startIdx; // Because we already traversed it + + cnt = self[END_OFFSET] - startIdx; + n = startAncestor.nextSibling; + while (n && cnt > 0) { + sibling = n.nextSibling; + xferNode = _traverseFullySelected(n, how); + + if (frag) { + frag.appendChild(xferNode); + } + + --cnt; + n = sibling; + } + + if (how != CLONE) { + self.setStartAfter(startAncestor); + self.collapse(TRUE); + } + + return frag; + } + + function _traverseCommonAncestors(startAncestor, endAncestor, how) { + var n, frag, startOffset, endOffset, cnt, sibling, nextSibling; + + if (how != DELETE) { + frag = createDocumentFragment(); + } + + n = _traverseLeftBoundary(startAncestor, how); + if (frag) { + frag.appendChild(n); + } + + startOffset = nodeIndex(startAncestor); + endOffset = nodeIndex(endAncestor); + ++startOffset; + + cnt = endOffset - startOffset; + sibling = startAncestor.nextSibling; + + while (cnt > 0) { + nextSibling = sibling.nextSibling; + n = _traverseFullySelected(sibling, how); + + if (frag) { + frag.appendChild(n); + } + + sibling = nextSibling; + --cnt; + } + + n = _traverseRightBoundary(endAncestor, how); + + if (frag) { + frag.appendChild(n); + } + + if (how != CLONE) { + self.setStartAfter(startAncestor); + self.collapse(TRUE); + } + + return frag; + } + + function _traverseRightBoundary(root, how) { + var next = _getSelectedNode(self[END_CONTAINER], self[END_OFFSET] - 1), + parent, clonedParent; + var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != self[END_CONTAINER]; + + if (next == root) { + return _traverseNode(next, isFullySelected, FALSE, how); + } + + parent = next.parentNode; + clonedParent = _traverseNode(parent, FALSE, FALSE, how); + + while (parent) { + while (next) { + prevSibling = next.previousSibling; + clonedChild = _traverseNode(next, isFullySelected, FALSE, how); + + if (how != DELETE) { + clonedParent.insertBefore(clonedChild, clonedParent.firstChild); + } + + isFullySelected = TRUE; + next = prevSibling; + } + + if (parent == root) { + return clonedParent; + } + + next = parent.previousSibling; + parent = parent.parentNode; + + clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how); + + if (how != DELETE) { + clonedGrandParent.appendChild(clonedParent); + } + + clonedParent = clonedGrandParent; + } + } + + function _traverseLeftBoundary(root, how) { + var next = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]), + isFullySelected = next != self[START_CONTAINER]; + var parent, clonedParent, nextSibling, clonedChild, clonedGrandParent; + + if (next == root) { + return _traverseNode(next, isFullySelected, TRUE, how); + } + + parent = next.parentNode; + clonedParent = _traverseNode(parent, FALSE, TRUE, how); + + while (parent) { + while (next) { + nextSibling = next.nextSibling; + clonedChild = _traverseNode(next, isFullySelected, TRUE, how); + + if (how != DELETE) { + clonedParent.appendChild(clonedChild); + } + + isFullySelected = TRUE; + next = nextSibling; + } + + if (parent == root) { + return clonedParent; + } + + next = parent.nextSibling; + parent = parent.parentNode; + + clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how); + + if (how != DELETE) { + clonedGrandParent.appendChild(clonedParent); + } + + clonedParent = clonedGrandParent; + } + } + + function _traverseNode(n, isFullySelected, isLeft, how) { + var txtValue, newNodeValue, oldNodeValue, offset, newNode; + + if (isFullySelected) { + return _traverseFullySelected(n, how); + } + + // TEXT_NODE + if (n.nodeType == 3) { + txtValue = n.nodeValue; + + if (isLeft) { + offset = self[START_OFFSET]; + newNodeValue = txtValue.substring(offset); + oldNodeValue = txtValue.substring(0, offset); + } else { + offset = self[END_OFFSET]; + newNodeValue = txtValue.substring(0, offset); + oldNodeValue = txtValue.substring(offset); + } + + if (how != CLONE) { + n.nodeValue = oldNodeValue; + } + + if (how == DELETE) { + return; + } + + newNode = dom.clone(n, FALSE); + newNode.nodeValue = newNodeValue; + + return newNode; + } + + if (how == DELETE) { + return; + } + + return dom.clone(n, FALSE); + } + + function _traverseFullySelected(n, how) { + if (how != DELETE) { + return how == CLONE ? dom.clone(n, TRUE) : n; + } + + n.parentNode.removeChild(n); + } + + function toStringIE() { + return dom.create('body', null, cloneContents()).outerText; + } + + extend(self, { + // Initial states + startContainer: doc, + startOffset: 0, + endContainer: doc, + endOffset: 0, + collapsed: TRUE, + commonAncestorContainer: doc, + + // Range constants + START_TO_START: 0, + START_TO_END: 1, + END_TO_END: 2, + END_TO_START: 3, + + // Public methods + setStart: setStart, + setEnd: setEnd, + setStartBefore: setStartBefore, + setStartAfter: setStartAfter, + setEndBefore: setEndBefore, + setEndAfter: setEndAfter, + collapse: collapse, + selectNode: selectNode, + selectNodeContents: selectNodeContents, + compareBoundaryPoints: compareBoundaryPoints, + deleteContents: deleteContents, + extractContents: extractContents, + cloneContents: cloneContents, + insertNode: insertNode, + surroundContents: surroundContents, + cloneRange: cloneRange, + toStringIE: toStringIE + }); + + return self; + } + + // Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype + Range.prototype.toString = function() { + return this.toStringIE(); + }; + + return Range; + } + ); + + defineGlobal("global!Array", Array); + defineGlobal("global!Error", Error); + define( + 'ephox.katamari.api.Fun', + + [ + 'global!Array', + 'global!Error' + ], + + function(Array, Error) { + + var noop = function() {}; + + var compose = function(fa, fb) { + return function() { + return fa(fb.apply(null, arguments)); + }; + }; + + var constant = function(value) { + return function() { + return value; + }; + }; + + var identity = function(x) { + return x; + }; + + var tripleEquals = function(a, b) { + return a === b; + }; + + // Don't use array slice(arguments), makes the whole function unoptimisable on Chrome + var curry = function(f) { + // equivalent to arguments.slice(1) + // starting at 1 because 0 is the f, makes things tricky. + // Pay attention to what variable is where, and the -1 magic. + // thankfully, we have tests for this. + var args = new Array(arguments.length - 1); + for (var i = 1; i < arguments.length; i++) args[i - 1] = arguments[i]; + + return function() { + var newArgs = new Array(arguments.length); + for (var j = 0; j < newArgs.length; j++) newArgs[j] = arguments[j]; + + var all = args.concat(newArgs); + return f.apply(null, all); + }; + }; + + var not = function(f) { + return function() { + return !f.apply(null, arguments); + }; + }; + + var die = function(msg) { + return function() { + throw new Error(msg); + }; + }; + + var apply = function(f) { + return f(); + }; + + var call = function(f) { + f(); + }; + + var never = constant(false); + var always = constant(true); + + + return { + noop: noop, + compose: compose, + constant: constant, + identity: identity, + tripleEquals: tripleEquals, + curry: curry, + not: not, + die: die, + apply: apply, + call: call, + never: never, + always: always + }; + } + ); + + defineGlobal("global!Object", Object); + define( + 'ephox.katamari.api.Option', + + [ + 'ephox.katamari.api.Fun', + 'global!Object' + ], + + function(Fun, Object) { + + var never = Fun.never; + var always = Fun.always; + + /** + Option objects support the following methods: + + fold :: this Option a -> ((() -> b, a -> b)) -> Option b + + is :: this Option a -> a -> Boolean + + isSome :: this Option a -> () -> Boolean + + isNone :: this Option a -> () -> Boolean + + getOr :: this Option a -> a -> a + + getOrThunk :: this Option a -> (() -> a) -> a + + getOrDie :: this Option a -> String -> a + + or :: this Option a -> Option a -> Option a + - if some: return self + - if none: return opt + + orThunk :: this Option a -> (() -> Option a) -> Option a + - Same as "or", but uses a thunk instead of a value + + map :: this Option a -> (a -> b) -> Option b + - "fmap" operation on the Option Functor. + - same as 'each' + + ap :: this Option a -> Option (a -> b) -> Option b + - "apply" operation on the Option Apply/Applicative. + - Equivalent to <*> in Haskell/PureScript. + + each :: this Option a -> (a -> b) -> Option b + - same as 'map' + + bind :: this Option a -> (a -> Option b) -> Option b + - "bind"/"flatMap" operation on the Option Bind/Monad. + - Equivalent to >>= in Haskell/PureScript; flatMap in Scala. + + flatten :: {this Option (Option a))} -> () -> Option a + - "flatten"/"join" operation on the Option Monad. + + exists :: this Option a -> (a -> Boolean) -> Boolean + + forall :: this Option a -> (a -> Boolean) -> Boolean + + filter :: this Option a -> (a -> Boolean) -> Option a + + equals :: this Option a -> Option a -> Boolean + + equals_ :: this Option a -> (Option a, a -> Boolean) -> Boolean + + toArray :: this Option a -> () -> [a] + + */ + + var none = function() { + return NONE; + }; + + var NONE = (function() { + var eq = function(o) { + return o.isNone(); + }; + + // inlined from peanut, maybe a micro-optimisation? + var call = function(thunk) { + return thunk(); + }; + var id = function(n) { + return n; + }; + var noop = function() {}; + + var me = { + fold: function(n, s) { + return n(); + }, + is: never, + isSome: never, + isNone: always, + getOr: id, + getOrThunk: call, + getOrDie: function(msg) { + throw new Error(msg || 'error: getOrDie called on none.'); + }, + or: id, + orThunk: call, + map: none, + ap: none, + each: noop, + bind: none, + flatten: none, + exists: never, + forall: always, + filter: none, + equals: eq, + equals_: eq, + toArray: function() { + return []; + }, + toString: Fun.constant("none()") + }; + if (Object.freeze) Object.freeze(me); + return me; + })(); + + + /** some :: a -> Option a */ + var some = function(a) { + + // inlined from peanut, maybe a micro-optimisation? + var constant_a = function() { + return a; + }; + + var self = function() { + // can't Fun.constant this one + return me; + }; + + var map = function(f) { + return some(f(a)); + }; + + var bind = function(f) { + return f(a); + }; + + var me = { + fold: function(n, s) { + return s(a); + }, + is: function(v) { + return a === v; + }, + isSome: always, + isNone: never, + getOr: constant_a, + getOrThunk: constant_a, + getOrDie: constant_a, + or: self, + orThunk: self, + map: map, + ap: function(optfab) { + return optfab.fold(none, function(fab) { + return some(fab(a)); + }); + }, + each: function(f) { + f(a); + }, + bind: bind, + flatten: constant_a, + exists: bind, + forall: bind, + filter: function(f) { + return f(a) ? me : NONE; + }, + equals: function(o) { + return o.is(a); + }, + equals_: function(o, elementEq) { + return o.fold( + never, + function(b) { + return elementEq(a, b); + } + ); + }, + toArray: function() { + return [a]; + }, + toString: function() { + return 'some(' + a + ')'; + } + }; + return me; + }; + + /** from :: undefined|null|a -> Option a */ + var from = function(value) { + return value === null || value === undefined ? NONE : some(value); + }; + + return { + some: some, + none: none, + from: from + }; + } + ); + + defineGlobal("global!String", String); + define( + 'ephox.katamari.api.Arr', + + [ + 'ephox.katamari.api.Option', + 'global!Array', + 'global!Error', + 'global!String' + ], + + function(Option, Array, Error, String) { + // Use the native Array.indexOf if it is available (IE9+) otherwise fall back to manual iteration + // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf + var rawIndexOf = (function() { + var pIndexOf = Array.prototype.indexOf; + + var fastIndex = function(xs, x) { + return pIndexOf.call(xs, x); + }; + + var slowIndex = function(xs, x) { + return slowIndexOf(xs, x); + }; + + return pIndexOf === undefined ? slowIndex : fastIndex; + })(); + + var indexOf = function(xs, x) { + // The rawIndexOf method does not wrap up in an option. This is for performance reasons. + var r = rawIndexOf(xs, x); + return r === -1 ? Option.none() : Option.some(r); + }; + + var contains = function(xs, x) { + return rawIndexOf(xs, x) > -1; + }; + + // Using findIndex is likely less optimal in Chrome (dynamic return type instead of bool) + // but if we need that micro-optimisation we can inline it later. + var exists = function(xs, pred) { + return findIndex(xs, pred).isSome(); + }; + + var range = function(num, f) { + var r = []; + for (var i = 0; i < num; i++) { + r.push(f(i)); + } + return r; + }; + + // It's a total micro optimisation, but these do make some difference. + // Particularly for browsers other than Chrome. + // - length caching + // http://jsperf.com/browser-diet-jquery-each-vs-for-loop/69 + // - not using push + // http://jsperf.com/array-direct-assignment-vs-push/2 + + var chunk = function(array, size) { + var r = []; + for (var i = 0; i < array.length; i += size) { + var s = array.slice(i, i + size); + r.push(s); + } + return r; + }; + + var map = function(xs, f) { + // pre-allocating array size when it's guaranteed to be known + // http://jsperf.com/push-allocated-vs-dynamic/22 + var len = xs.length; + var r = new Array(len); + for (var i = 0; i < len; i++) { + var x = xs[i]; + r[i] = f(x, i, xs); + } + return r; + }; + + // Unwound implementing other functions in terms of each. + // The code size is roughly the same, and it should allow for better optimisation. + var each = function(xs, f) { + for (var i = 0, len = xs.length; i < len; i++) { + var x = xs[i]; + f(x, i, xs); + } + }; + + var eachr = function(xs, f) { + for (var i = xs.length - 1; i >= 0; i--) { + var x = xs[i]; + f(x, i, xs); + } + }; + + var partition = function(xs, pred) { + var pass = []; + var fail = []; + for (var i = 0, len = xs.length; i < len; i++) { + var x = xs[i]; + var arr = pred(x, i, xs) ? pass : fail; + arr.push(x); + } + return { + pass: pass, + fail: fail + }; + }; + + var filter = function(xs, pred) { + var r = []; + for (var i = 0, len = xs.length; i < len; i++) { + var x = xs[i]; + if (pred(x, i, xs)) { + r.push(x); + } + } + return r; + }; + + /* + * Groups an array into contiguous arrays of like elements. Whether an element is like or not depends on f. + * + * f is a function that derives a value from an element - e.g. true or false, or a string. + * Elements are like if this function generates the same value for them (according to ===). + * + * + * Order of the elements is preserved. Arr.flatten() on the result will return the original list, as with Haskell groupBy function. + * For a good explanation, see the group function (which is a special case of groupBy) + * http://hackage.haskell.org/package/base-4.7.0.0/docs/Data-List.html#v:group + */ + var groupBy = function(xs, f) { + if (xs.length === 0) { + return []; + } else { + var wasType = f(xs[0]); // initial case for matching + var r = []; + var group = []; + + for (var i = 0, len = xs.length; i < len; i++) { + var x = xs[i]; + var type = f(x); + if (type !== wasType) { + r.push(group); + group = []; + } + wasType = type; + group.push(x); + } + if (group.length !== 0) { + r.push(group); + } + return r; + } + }; + + var foldr = function(xs, f, acc) { + eachr(xs, function(x) { + acc = f(acc, x); + }); + return acc; + }; + + var foldl = function(xs, f, acc) { + each(xs, function(x) { + acc = f(acc, x); + }); + return acc; + }; + + var find = function(xs, pred) { + for (var i = 0, len = xs.length; i < len; i++) { + var x = xs[i]; + if (pred(x, i, xs)) { + return Option.some(x); + } + } + return Option.none(); + }; + + var findIndex = function(xs, pred) { + for (var i = 0, len = xs.length; i < len; i++) { + var x = xs[i]; + if (pred(x, i, xs)) { + return Option.some(i); + } + } + + return Option.none(); + }; + + var slowIndexOf = function(xs, x) { + for (var i = 0, len = xs.length; i < len; ++i) { + if (xs[i] === x) { + return i; + } + } + + return -1; + }; + + var push = Array.prototype.push; + var flatten = function(xs) { + // Note, this is possible because push supports multiple arguments: + // http://jsperf.com/concat-push/6 + // Note that in the past, concat() would silently work (very slowly) for array-like objects. + // With this change it will throw an error. + var r = []; + for (var i = 0, len = xs.length; i < len; ++i) { + // Ensure that each value is an array itself + if (!Array.prototype.isPrototypeOf(xs[i])) throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs); + push.apply(r, xs[i]); + } + return r; + }; + + var bind = function(xs, f) { + var output = map(xs, f); + return flatten(output); + }; + + var forall = function(xs, pred) { + for (var i = 0, len = xs.length; i < len; ++i) { + var x = xs[i]; + if (pred(x, i, xs) !== true) { + return false; + } + } + return true; + }; + + var equal = function(a1, a2) { + return a1.length === a2.length && forall(a1, function(x, i) { + return x === a2[i]; + }); + }; + + var slice = Array.prototype.slice; + var reverse = function(xs) { + var r = slice.call(xs, 0); + r.reverse(); + return r; + }; + + var difference = function(a1, a2) { + return filter(a1, function(x) { + return !contains(a2, x); + }); + }; + + var mapToObject = function(xs, f) { + var r = {}; + for (var i = 0, len = xs.length; i < len; i++) { + var x = xs[i]; + r[String(x)] = f(x, i); + } + return r; + }; + + var pure = function(x) { + return [x]; + }; + + var sort = function(xs, comparator) { + var copy = slice.call(xs, 0); + copy.sort(comparator); + return copy; + }; + + var head = function(xs) { + return xs.length === 0 ? Option.none() : Option.some(xs[0]); + }; + + var last = function(xs) { + return xs.length === 0 ? Option.none() : Option.some(xs[xs.length - 1]); + }; + + return { + map: map, + each: each, + eachr: eachr, + partition: partition, + filter: filter, + groupBy: groupBy, + indexOf: indexOf, + foldr: foldr, + foldl: foldl, + find: find, + findIndex: findIndex, + flatten: flatten, + bind: bind, + forall: forall, + exists: exists, + contains: contains, + equal: equal, + reverse: reverse, + chunk: chunk, + difference: difference, + mapToObject: mapToObject, + pure: pure, + sort: sort, + range: range, + head: head, + last: last + }; + } + ); + defineGlobal("global!setTimeout", setTimeout); + define( + 'ephox.katamari.api.LazyValue', + + [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Option', + 'global!setTimeout' + ], + + function(Arr, Option, setTimeout) { + var nu = function(baseFn) { + var data = Option.none(); + var callbacks = []; + + /** map :: this LazyValue a -> (a -> b) -> LazyValue b */ + var map = function(f) { + return nu(function(nCallback) { + get(function(data) { + nCallback(f(data)); + }); + }); + }; + + var get = function(nCallback) { + if (isReady()) call(nCallback); + else callbacks.push(nCallback); + }; + + var set = function(x) { + data = Option.some(x); + run(callbacks); + callbacks = []; + }; + + var isReady = function() { + return data.isSome(); + }; + + var run = function(cbs) { + Arr.each(cbs, call); + }; + + var call = function(cb) { + data.each(function(x) { + setTimeout(function() { + cb(x); + }, 0); + }); + }; + + // Lazy values cache the value and kick off immediately + baseFn(set); + + return { + get: get, + map: map, + isReady: isReady + }; + }; + + var pure = function(a) { + return nu(function(callback) { + callback(a); + }); + }; + + return { + nu: nu, + pure: pure + }; + } + ); + define( + 'ephox.katamari.async.Bounce', + + [ + 'global!Array', + 'global!setTimeout' + ], + + function(Array, setTimeout) { + + var bounce = function(f) { + return function() { + var args = Array.prototype.slice.call(arguments); + var me = this; + setTimeout(function() { + f.apply(me, args); + }, 0); + }; + }; + + return { + bounce: bounce + }; + } + ); + + define( + 'ephox.katamari.api.Future', + + [ + 'ephox.katamari.api.LazyValue', + 'ephox.katamari.async.Bounce' + ], + + /** A future value that is evaluated on demand. The base function is re-evaluated each time 'get' is called. */ + function(LazyValue, Bounce) { + var nu = function(baseFn) { + var get = function(callback) { + baseFn(Bounce.bounce(callback)); + }; + + /** map :: this Future a -> (a -> b) -> Future b */ + var map = function(fab) { + return nu(function(callback) { + get(function(a) { + var value = fab(a); + callback(value); + }); + }); + }; + + /** bind :: this Future a -> (a -> Future b) -> Future b */ + var bind = function(aFutureB) { + return nu(function(callback) { + get(function(a) { + aFutureB(a).get(callback); + }); + }); + }; + + /** anonBind :: this Future a -> Future b -> Future b + * Returns a future, which evaluates the first future, ignores the result, then evaluates the second. + */ + var anonBind = function(futureB) { + return nu(function(callback) { + get(function(a) { + futureB.get(callback); + }); + }); + }; + + var toLazy = function() { + return LazyValue.nu(get); + }; + + return { + map: map, + bind: bind, + anonBind: anonBind, + toLazy: toLazy, + get: get + }; + + }; + + /** a -> Future a */ + var pure = function(a) { + return nu(function(callback) { + callback(a); + }); + }; + + return { + nu: nu, + pure: pure + }; + } + ); + + define( + 'ephox.katamari.async.AsyncValues', + + [ + 'ephox.katamari.api.Arr' + ], + + function(Arr) { + /* + * NOTE: an `asyncValue` must have a `get` function which gets given a callback and calls + * that callback with a value once it is ready + * + * e.g + * { + * get: function (callback) { callback(10); } + * } + */ + var par = function(asyncValues, nu) { + return nu(function(callback) { + var r = []; + var count = 0; + + var cb = function(i) { + return function(value) { + r[i] = value; + count++; + if (count >= asyncValues.length) { + callback(r); + } + }; + }; + + if (asyncValues.length === 0) { + callback([]); + } else { + Arr.each(asyncValues, function(asyncValue, i) { + asyncValue.get(cb(i)); + }); + } + }); + }; + + return { + par: par + }; + } + ); + define( + 'ephox.katamari.api.Futures', + + [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Future', + 'ephox.katamari.async.AsyncValues' + ], + + function(Arr, Future, AsyncValues) { + /** par :: [Future a] -> Future [a] */ + var par = function(futures) { + return AsyncValues.par(futures, Future.nu); + }; + + /** mapM :: [a] -> (a -> Future b) -> Future [b] */ + var mapM = function(array, fn) { + var futures = Arr.map(array, fn); + return par(futures); + }; + + /** Kleisli composition of two functions: a -> Future b. + * Note the order of arguments: g is invoked first, then the result passed to f. + * This is in line with f . g = \x -> f (g a) + * + * compose :: ((b -> Future c), (a -> Future b)) -> a -> Future c + */ + var compose = function(f, g) { + return function(a) { + return g(a).bind(f); + }; + }; + + return { + par: par, + mapM: mapM, + compose: compose + }; + } + ); + define( + 'ephox.katamari.api.Result', + + [ + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Option' + ], + + function(Fun, Option) { + /* The type signatures for Result + * is :: this Result a -> a -> Bool + * or :: this Result a -> Result a -> Result a + * orThunk :: this Result a -> (_ -> Result a) -> Result a + * map :: this Result a -> (a -> b) -> Result b + * each :: this Result a -> (a -> _) -> _ + * bind :: this Result a -> (a -> Result b) -> Result b + * fold :: this Result a -> (_ -> b, a -> b) -> b + * exists :: this Result a -> (a -> Bool) -> Bool + * forall :: this Result a -> (a -> Bool) -> Bool + * toOption :: this Result a -> Option a + * isValue :: this Result a -> Bool + * isError :: this Result a -> Bool + * getOr :: this Result a -> a -> a + * getOrThunk :: this Result a -> (_ -> a) -> a + * getOrDie :: this Result a -> a (or throws error) + */ + + var value = function(o) { + var is = function(v) { + return o === v; + }; + + var or = function(opt) { + return value(o); + }; + + var orThunk = function(f) { + return value(o); + }; + + var map = function(f) { + return value(f(o)); + }; + + var each = function(f) { + f(o); + }; + + var bind = function(f) { + return f(o); + }; + + var fold = function(_, onValue) { + return onValue(o); + }; + + var exists = function(f) { + return f(o); + }; + + var forall = function(f) { + return f(o); + }; + + var toOption = function() { + return Option.some(o); + }; + + return { + is: is, + isValue: Fun.constant(true), + isError: Fun.constant(false), + getOr: Fun.constant(o), + getOrThunk: Fun.constant(o), + getOrDie: Fun.constant(o), + or: or, + orThunk: orThunk, + fold: fold, + map: map, + each: each, + bind: bind, + exists: exists, + forall: forall, + toOption: toOption + }; + }; + + var error = function(message) { + var getOrThunk = function(f) { + return f(); + }; + + var getOrDie = function() { + return Fun.die(message)(); + }; + + var or = function(opt) { + return opt; + }; + + var orThunk = function(f) { + return f(); + }; + + var map = function(f) { + return error(message); + }; + + var bind = function(f) { + return error(message); + }; + + var fold = function(onError, _) { + return onError(message); + }; + + return { + is: Fun.constant(false), + isValue: Fun.constant(false), + isError: Fun.constant(true), + getOr: Fun.identity, + getOrThunk: getOrThunk, + getOrDie: getOrDie, + or: or, + orThunk: orThunk, + fold: fold, + map: map, + each: Fun.noop, + bind: bind, + exists: Fun.constant(false), + forall: Fun.constant(true), + toOption: Option.none + }; + }; + + return { + value: value, + error: error + }; + } + ); + + /** + * StyleSheetLoader.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class handles loading of external stylesheets and fires events when these are loaded. + * + * @class tinymce.dom.StyleSheetLoader + * @private + */ + define( + 'tinymce.core.dom.StyleSheetLoader', [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Future', + 'ephox.katamari.api.Futures', + 'ephox.katamari.api.Result', + 'tinymce.core.util.Delay', + 'tinymce.core.util.Tools' + ], + function(Arr, Fun, Future, Futures, Result, Delay, Tools) { + "use strict"; + + return function(document, settings) { + var idCount = 0, + loadedStates = {}, + maxLoadTime; + + settings = settings || {}; + maxLoadTime = settings.maxLoadTime || 5000; + + function appendToHead(node) { + document.getElementsByTagName('head')[0].appendChild(node); + } + + /** + * Loads the specified css style sheet file and call the loadedCallback once it's finished loading. + * + * @method load + * @param {String} url Url to be loaded. + * @param {Function} loadedCallback Callback to be executed when loaded. + * @param {Function} errorCallback Callback to be executed when failed loading. + */ + function load(url, loadedCallback, errorCallback) { + var link, style, startTime, state; + + function passed() { + var callbacks = state.passed, + i = callbacks.length; + + while (i--) { + callbacks[i](); + } + + state.status = 2; + state.passed = []; + state.failed = []; + } + + function failed() { + var callbacks = state.failed, + i = callbacks.length; + + while (i--) { + callbacks[i](); + } + + state.status = 3; + state.passed = []; + state.failed = []; + } + + // Sniffs for older WebKit versions that have the link.onload but a broken one + function isOldWebKit() { + var webKitChunks = navigator.userAgent.match(/WebKit\/(\d*)/); + return !!(webKitChunks && webKitChunks[1] < 536); + } + + // Calls the waitCallback until the test returns true or the timeout occurs + function wait(testCallback, waitCallback) { + if (!testCallback()) { + // Wait for timeout + if ((new Date().getTime()) - startTime < maxLoadTime) { + Delay.setTimeout(waitCallback); + } else { + failed(); + } + } + } + + // Workaround for WebKit that doesn't properly support the onload event for link elements + // Or WebKit that fires the onload event before the StyleSheet is added to the document + function waitForWebKitLinkLoaded() { + wait(function() { + var styleSheets = document.styleSheets, + styleSheet, i = styleSheets.length, + owner; + + while (i--) { + styleSheet = styleSheets[i]; + owner = styleSheet.ownerNode ? styleSheet.ownerNode : styleSheet.owningElement; + if (owner && owner.id === link.id) { + passed(); + return true; + } + } + }, waitForWebKitLinkLoaded); + } + + // Workaround for older Geckos that doesn't have any onload event for StyleSheets + function waitForGeckoLinkLoaded() { + wait(function() { + try { + // Accessing the cssRules will throw an exception until the CSS file is loaded + var cssRules = style.sheet.cssRules; + passed(); + return !!cssRules; + } catch (ex) { + // Ignore + } + }, waitForGeckoLinkLoaded); + } + + url = Tools._addCacheSuffix(url); + + if (!loadedStates[url]) { + state = { + passed: [], + failed: [] + }; + + loadedStates[url] = state; + } else { + state = loadedStates[url]; + } + + if (loadedCallback) { + state.passed.push(loadedCallback); + } + + if (errorCallback) { + state.failed.push(errorCallback); + } + + // Is loading wait for it to pass + if (state.status == 1) { + return; + } + + // Has finished loading and was success + if (state.status == 2) { + passed(); + return; + } + + // Has finished loading and was a failure + if (state.status == 3) { + failed(); + return; + } + + // Start loading + state.status = 1; + link = document.createElement('link'); + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.id = 'u' + (idCount++); + link.async = false; + link.defer = false; + startTime = new Date().getTime(); + + // Feature detect onload on link element and sniff older webkits since it has an broken onload event + if ("onload" in link && !isOldWebKit()) { + link.onload = waitForWebKitLinkLoaded; + link.onerror = failed; + } else { + // Sniff for old Firefox that doesn't support the onload event on link elements + // TODO: Remove this in the future when everyone uses modern browsers + if (navigator.userAgent.indexOf("Firefox") > 0) { + style = document.createElement('style'); + style.textContent = '@import "' + url + '"'; + waitForGeckoLinkLoaded(); + appendToHead(style); + return; + } + + // Use the id owner on older webkits + waitForWebKitLinkLoaded(); + } + + appendToHead(link); + link.href = url; + } + + var loadF = function(url) { + return Future.nu(function(resolve) { + load( + url, + Fun.compose(resolve, Fun.constant(Result.value(url))), + Fun.compose(resolve, Fun.constant(Result.error(url))) + ); + }); + }; + + var unbox = function(result) { + return result.fold(Fun.identity, Fun.identity); + }; + + var loadAll = function(urls, success, failure) { + Futures.par(Arr.map(urls, loadF)).get(function(result) { + var parts = Arr.partition(result, function(r) { + return r.isValue(); + }); + + if (parts.fail.length > 0) { + failure(parts.fail.map(unbox)); + } else { + success(parts.pass.map(unbox)); + } + }); + }; + + return { + load: load, + loadAll: loadAll + }; + }; + } + ); + + /** + * Schema.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Schema validator class. + * + * @class tinymce.html.Schema + * @example + * if (tinymce.activeEditor.schema.isValidChild('p', 'span')) + * alert('span is valid child of p.'); + * + * if (tinymce.activeEditor.schema.getElementRule('p')) + * alert('P is a valid element.'); + * + * @class tinymce.html.Schema + * @version 3.4 + */ + define( + 'tinymce.core.html.Schema', [ + "tinymce.core.util.Tools" + ], + function(Tools) { + var mapCache = {}, + dummyObj = {}; + var makeMap = Tools.makeMap, + each = Tools.each, + extend = Tools.extend, + explode = Tools.explode, + inArray = Tools.inArray; + + function split(items, delim) { + items = Tools.trim(items); + return items ? items.split(delim || ' ') : []; + } + + /** + * Builds a schema lookup table + * + * @private + * @param {String} type html4, html5 or html5-strict schema type. + * @return {Object} Schema lookup table. + */ + function compileSchema(type) { + var schema = {}, + globalAttributes, blockContent; + var phrasingContent, flowContent, html4BlockContent, html4PhrasingContent; + + function add(name, attributes, children) { + var ni, attributesOrder, element; + + function arrayToMap(array, obj) { + var map = {}, + i, l; + + for (i = 0, l = array.length; i < l; i++) { + map[array[i]] = obj || {}; + } + + return map; + } + + children = children || []; + attributes = attributes || ""; + + if (typeof children === "string") { + children = split(children); + } + + name = split(name); + ni = name.length; + while (ni--) { + attributesOrder = split([globalAttributes, attributes].join(' ')); + + element = { + attributes: arrayToMap(attributesOrder), + attributesOrder: attributesOrder, + children: arrayToMap(children, dummyObj) + }; + + schema[name[ni]] = element; + } + } + + function addAttrs(name, attributes) { + var ni, schemaItem, i, l; + + name = split(name); + ni = name.length; + attributes = split(attributes); + while (ni--) { + schemaItem = schema[name[ni]]; + for (i = 0, l = attributes.length; i < l; i++) { + schemaItem.attributes[attributes[i]] = {}; + schemaItem.attributesOrder.push(attributes[i]); + } + } + } + + // Use cached schema + if (mapCache[type]) { + return mapCache[type]; + } + + // Attributes present on all elements + globalAttributes = "id accesskey class dir lang style tabindex title role"; + + // Event attributes can be opt-in/opt-out + /*eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " + + "ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " + + "onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " + + "onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " + + "onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " + + "onwaiting" + );*/ + + // Block content elements + blockContent = + "address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"; + + // Phrasing content elements from the HTML5 spec (inline) + phrasingContent = + "a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd " + + "label map noscript object q s samp script select small span strong sub sup " + + "textarea u var #text #comment"; + + // Add HTML5 items to globalAttributes, blockContent, phrasingContent + if (type != "html4") { + globalAttributes += " contenteditable contextmenu draggable dropzone " + + "hidden spellcheck translate"; + blockContent += " article aside details dialog figure header footer hgroup section nav"; + phrasingContent += " audio canvas command datalist mark meter output picture " + + "progress time wbr video ruby bdi keygen"; + } + + // Add HTML4 elements unless it's html5-strict + if (type != "html5-strict") { + globalAttributes += " xml:lang"; + + html4PhrasingContent = "acronym applet basefont big font strike tt"; + phrasingContent = [phrasingContent, html4PhrasingContent].join(' '); + + each(split(html4PhrasingContent), function(name) { + add(name, "", phrasingContent); + }); + + html4BlockContent = "center dir isindex noframes"; + blockContent = [blockContent, html4BlockContent].join(' '); + + // Flow content elements from the HTML5 spec (block+inline) + flowContent = [blockContent, phrasingContent].join(' '); + + each(split(html4BlockContent), function(name) { + add(name, "", flowContent); + }); + } + + // Flow content elements from the HTML5 spec (block+inline) + flowContent = flowContent || [blockContent, phrasingContent].join(" "); + + // HTML4 base schema TODO: Move HTML5 specific attributes to HTML5 specific if statement + // Schema items <element name>, <specific attributes>, <children ..> + add("html", "manifest", "head body"); + add("head", "", "base command link meta noscript script style title"); + add("title hr noscript br"); + add("base", "href target"); + add("link", "href rel media hreflang type sizes hreflang"); + add("meta", "name http-equiv content charset"); + add("style", "media type scoped"); + add("script", "src async defer type charset"); + add("body", "onafterprint onbeforeprint onbeforeunload onblur onerror onfocus " + + "onhashchange onload onmessage onoffline ononline onpagehide onpageshow " + + "onpopstate onresize onscroll onstorage onunload", flowContent); + add("address dt dd div caption", "", flowContent); + add("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn", "", phrasingContent); + add("blockquote", "cite", flowContent); + add("ol", "reversed start type", "li"); + add("ul", "", "li"); + add("li", "value", flowContent); + add("dl", "", "dt dd"); + add("a", "href target rel media hreflang type", phrasingContent); + add("q", "cite", phrasingContent); + add("ins del", "cite datetime", flowContent); + add("img", "src sizes srcset alt usemap ismap width height"); + add("iframe", "src name width height", flowContent); + add("embed", "src type width height"); + add("object", "data type typemustmatch name usemap form width height", [flowContent, "param"].join(' ')); + add("param", "name value"); + add("map", "name", [flowContent, "area"].join(' ')); + add("area", "alt coords shape href target rel media hreflang type"); + add("table", "border", "caption colgroup thead tfoot tbody tr" + (type == "html4" ? " col" : "")); + add("colgroup", "span", "col"); + add("col", "span"); + add("tbody thead tfoot", "", "tr"); + add("tr", "", "td th"); + add("td", "colspan rowspan headers", flowContent); + add("th", "colspan rowspan headers scope abbr", flowContent); + add("form", "accept-charset action autocomplete enctype method name novalidate target", flowContent); + add("fieldset", "disabled form name", [flowContent, "legend"].join(' ')); + add("label", "form for", phrasingContent); + add("input", "accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate " + + "formtarget height list max maxlength min multiple name pattern readonly required size src step type value width" + ); + add("button", "disabled form formaction formenctype formmethod formnovalidate formtarget name type value", + type == "html4" ? flowContent : phrasingContent); + add("select", "disabled form multiple name required size", "option optgroup"); + add("optgroup", "disabled label", "option"); + add("option", "disabled label selected value"); + add("textarea", "cols dirname disabled form maxlength name readonly required rows wrap"); + add("menu", "type label", [flowContent, "li"].join(' ')); + add("noscript", "", flowContent); + + // Extend with HTML5 elements + if (type != "html4") { + add("wbr"); + add("ruby", "", [phrasingContent, "rt rp"].join(' ')); + add("figcaption", "", flowContent); + add("mark rt rp summary bdi", "", phrasingContent); + add("canvas", "width height", flowContent); + add("video", "src crossorigin poster preload autoplay mediagroup loop " + + "muted controls width height buffered", [flowContent, "track source"].join(' ')); + add("audio", "src crossorigin preload autoplay mediagroup loop muted controls " + + "buffered volume", [flowContent, "track source"].join(' ')); + add("picture", "", "img source"); + add("source", "src srcset type media sizes"); + add("track", "kind src srclang label default"); + add("datalist", "", [phrasingContent, "option"].join(' ')); + add("article section nav aside header footer", "", flowContent); + add("hgroup", "", "h1 h2 h3 h4 h5 h6"); + add("figure", "", [flowContent, "figcaption"].join(' ')); + add("time", "datetime", phrasingContent); + add("dialog", "open", flowContent); + add("command", "type label icon disabled checked radiogroup command"); + add("output", "for form name", phrasingContent); + add("progress", "value max", phrasingContent); + add("meter", "value min max low high optimum", phrasingContent); + add("details", "open", [flowContent, "summary"].join(' ')); + add("keygen", "autofocus challenge disabled form keytype name"); + } + + // Extend with HTML4 attributes unless it's html5-strict + if (type != "html5-strict") { + addAttrs("script", "language xml:space"); + addAttrs("style", "xml:space"); + addAttrs("object", "declare classid code codebase codetype archive standby align border hspace vspace"); + addAttrs("embed", "align name hspace vspace"); + addAttrs("param", "valuetype type"); + addAttrs("a", "charset name rev shape coords"); + addAttrs("br", "clear"); + addAttrs("applet", "codebase archive code object alt name width height align hspace vspace"); + addAttrs("img", "name longdesc align border hspace vspace"); + addAttrs("iframe", "longdesc frameborder marginwidth marginheight scrolling align"); + addAttrs("font basefont", "size color face"); + addAttrs("input", "usemap align"); + addAttrs("select", "onchange"); + addAttrs("textarea"); + addAttrs("h1 h2 h3 h4 h5 h6 div p legend caption", "align"); + addAttrs("ul", "type compact"); + addAttrs("li", "type"); + addAttrs("ol dl menu dir", "compact"); + addAttrs("pre", "width xml:space"); + addAttrs("hr", "align noshade size width"); + addAttrs("isindex", "prompt"); + addAttrs("table", "summary width frame rules cellspacing cellpadding align bgcolor"); + addAttrs("col", "width align char charoff valign"); + addAttrs("colgroup", "width align char charoff valign"); + addAttrs("thead", "align char charoff valign"); + addAttrs("tr", "align char charoff valign bgcolor"); + addAttrs("th", "axis align char charoff valign nowrap bgcolor width height"); + addAttrs("form", "accept"); + addAttrs("td", "abbr axis scope align char charoff valign nowrap bgcolor width height"); + addAttrs("tfoot", "align char charoff valign"); + addAttrs("tbody", "align char charoff valign"); + addAttrs("area", "nohref"); + addAttrs("body", "background bgcolor text link vlink alink"); + } + + // Extend with HTML5 attributes unless it's html4 + if (type != "html4") { + addAttrs("input button select textarea", "autofocus"); + addAttrs("input textarea", "placeholder"); + addAttrs("a", "download"); + addAttrs("link script img", "crossorigin"); + addAttrs("iframe", "sandbox seamless allowfullscreen"); // Excluded: srcdoc + } + + // Special: iframe, ruby, video, audio, label + + // Delete children of the same name from it's parent + // For example: form can't have a child of the name form + each(split('a form meter progress dfn'), function(name) { + if (schema[name]) { + delete schema[name].children[name]; + } + }); + + // Delete header, footer, sectioning and heading content descendants + /*each('dt th address', function(name) { + delete schema[name].children[name]; + });*/ + + // Caption can't have tables + delete schema.caption.children.table; + + // Delete scripts by default due to possible XSS + delete schema.script; + + // TODO: LI:s can only have value if parent is OL + + // TODO: Handle transparent elements + // a ins del canvas map + + mapCache[type] = schema; + + return schema; + } + + function compileElementMap(value, mode) { + var styles; + + if (value) { + styles = {}; + + if (typeof value == 'string') { + value = { + '*': value + }; + } + + // Convert styles into a rule list + each(value, function(value, key) { + styles[key] = styles[key.toUpperCase()] = mode == 'map' ? makeMap(value, /[, ]/) : explode(value, /[, ]/); + }); + } + + return styles; + } + + /** + * Constructs a new Schema instance. + * + * @constructor + * @method Schema + * @param {Object} settings Name/value settings object. + */ + return function(settings) { + var self = this, + elements = {}, + children = {}, + patternElements = [], + validStyles, invalidStyles, schemaItems; + var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap, validClasses; + var blockElementsMap, nonEmptyElementsMap, moveCaretBeforeOnEnterElementsMap, textBlockElementsMap, textInlineElementsMap; + var customElementsMap = {}, + specialElements = {}; + + // Creates an lookup table map object for the specified option or the default value + function createLookupTable(option, defaultValue, extendWith) { + var value = settings[option]; + + if (!value) { + // Get cached default map or make it if needed + value = mapCache[option]; + + if (!value) { + value = makeMap(defaultValue, ' ', makeMap(defaultValue.toUpperCase(), ' ')); + value = extend(value, extendWith); + + mapCache[option] = value; + } + } else { + // Create custom map + value = makeMap(value, /[, ]/, makeMap(value.toUpperCase(), /[, ]/)); + } + + return value; + } + + settings = settings || {}; + schemaItems = compileSchema(settings.schema); + + // Allow all elements and attributes if verify_html is set to false + if (settings.verify_html === false) { + settings.valid_elements = '*[*]'; + } + + validStyles = compileElementMap(settings.valid_styles); + invalidStyles = compileElementMap(settings.invalid_styles, 'map'); + validClasses = compileElementMap(settings.valid_classes, 'map'); + + // Setup map objects + whiteSpaceElementsMap = createLookupTable( + 'whitespace_elements', + 'pre script noscript style textarea video audio iframe object code' + ); + selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr'); + shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link ' + + 'meta param embed source wbr track'); + boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' + + 'noshade nowrap readonly selected autoplay loop controls'); + nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object ' + + 'script pre code', shortEndedElementsMap); + moveCaretBeforeOnEnterElementsMap = createLookupTable('move_caret_before_on_enter_elements', 'table', nonEmptyElementsMap); + textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' + + 'blockquote center dir fieldset header footer article section hgroup aside nav figure'); + blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' + + 'th tr td li ol ul caption dl dt dd noscript menu isindex option ' + + 'datalist select optgroup figcaption', textBlockElementsMap); + textInlineElementsMap = createLookupTable('text_inline_elements', 'span strong b em i font strike u var cite ' + + 'dfn code mark q sup sub samp'); + + each((settings.special || 'script noscript noframes noembed title style textarea xmp').split(' '), function(name) { + specialElements[name] = new RegExp('<\/' + name + '[^>]*>', 'gi'); + }); + + // Converts a wildcard expression string to a regexp for example *a will become /.*a/. + function patternToRegExp(str) { + return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$'); + } + + // Parses the specified valid_elements string and adds to the current rules + // This function is a bit hard to read since it's heavily optimized for speed + function addValidElements(validElements) { + var ei, el, ai, al, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder, + prefix, outputName, globalAttributes, globalAttributesOrder, key, value, + elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/, + attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/, + hasPatternsRegExp = /[*?+]/; + + if (validElements) { + // Split valid elements into an array with rules + validElements = split(validElements, ','); + + if (elements['@']) { + globalAttributes = elements['@'].attributes; + globalAttributesOrder = elements['@'].attributesOrder; + } + + // Loop all rules + for (ei = 0, el = validElements.length; ei < el; ei++) { + // Parse element rule + matches = elementRuleRegExp.exec(validElements[ei]); + if (matches) { + // Setup local names for matches + prefix = matches[1]; + elementName = matches[2]; + outputName = matches[3]; + attrData = matches[5]; + + // Create new attributes and attributesOrder + attributes = {}; + attributesOrder = []; + + // Create the new element + element = { + attributes: attributes, + attributesOrder: attributesOrder + }; + + // Padd empty elements prefix + if (prefix === '#') { + element.paddEmpty = true; + } + + // Remove empty elements prefix + if (prefix === '-') { + element.removeEmpty = true; + } + + if (matches[4] === '!') { + element.removeEmptyAttrs = true; + } + + // Copy attributes from global rule into current rule + if (globalAttributes) { + for (key in globalAttributes) { + attributes[key] = globalAttributes[key]; + } + + attributesOrder.push.apply(attributesOrder, globalAttributesOrder); + } + + // Attributes defined + if (attrData) { + attrData = split(attrData, '|'); + for (ai = 0, al = attrData.length; ai < al; ai++) { + matches = attrRuleRegExp.exec(attrData[ai]); + if (matches) { + attr = {}; + attrType = matches[1]; + attrName = matches[2].replace(/::/g, ':'); + prefix = matches[3]; + value = matches[4]; + + // Required + if (attrType === '!') { + element.attributesRequired = element.attributesRequired || []; + element.attributesRequired.push(attrName); + attr.required = true; + } + + // Denied from global + if (attrType === '-') { + delete attributes[attrName]; + attributesOrder.splice(inArray(attributesOrder, attrName), 1); + continue; + } + + // Default value + if (prefix) { + // Default value + if (prefix === '=') { + element.attributesDefault = element.attributesDefault || []; + element.attributesDefault.push({ + name: attrName, + value: value + }); + attr.defaultValue = value; + } + + // Forced value + if (prefix === ':') { + element.attributesForced = element.attributesForced || []; + element.attributesForced.push({ + name: attrName, + value: value + }); + attr.forcedValue = value; + } + + // Required values + if (prefix === '<') { + attr.validValues = makeMap(value, '?'); + } + } + + // Check for attribute patterns + if (hasPatternsRegExp.test(attrName)) { + element.attributePatterns = element.attributePatterns || []; + attr.pattern = patternToRegExp(attrName); + element.attributePatterns.push(attr); + } else { + // Add attribute to order list if it doesn't already exist + if (!attributes[attrName]) { + attributesOrder.push(attrName); + } + + attributes[attrName] = attr; + } + } + } + } + + // Global rule, store away these for later usage + if (!globalAttributes && elementName == '@') { + globalAttributes = attributes; + globalAttributesOrder = attributesOrder; + } + + // Handle substitute elements such as b/strong + if (outputName) { + element.outputName = elementName; + elements[outputName] = element; + } + + // Add pattern or exact element + if (hasPatternsRegExp.test(elementName)) { + element.pattern = patternToRegExp(elementName); + patternElements.push(element); + } else { + elements[elementName] = element; + } + } + } + } + } + + function setValidElements(validElements) { + elements = {}; + patternElements = []; + + addValidElements(validElements); + + each(schemaItems, function(element, name) { + children[name] = element.children; + }); + } + + // Adds custom non HTML elements to the schema + function addCustomElements(customElements) { + var customElementRegExp = /^(~)?(.+)$/; + + if (customElements) { + // Flush cached items since we are altering the default maps + mapCache.text_block_elements = mapCache.block_elements = null; + + each(split(customElements, ','), function(rule) { + var matches = customElementRegExp.exec(rule), + inline = matches[1] === '~', + cloneName = inline ? 'span' : 'div', + name = matches[2]; + + children[name] = children[cloneName]; + customElementsMap[name] = cloneName; + + // If it's not marked as inline then add it to valid block elements + if (!inline) { + blockElementsMap[name.toUpperCase()] = {}; + blockElementsMap[name] = {}; + } + + // Add elements clone if needed + if (!elements[name]) { + var customRule = elements[cloneName]; + + customRule = extend({}, customRule); + delete customRule.removeEmptyAttrs; + delete customRule.removeEmpty; + + elements[name] = customRule; + } + + // Add custom elements at span/div positions + each(children, function(element, elmName) { + if (element[cloneName]) { + children[elmName] = element = extend({}, children[elmName]); + element[name] = element[cloneName]; + } + }); + }); + } + } + + // Adds valid children to the schema object + function addValidChildren(validChildren) { + var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/; + + // Invalidate the schema cache if the schema is mutated + mapCache[settings.schema] = null; + + if (validChildren) { + each(split(validChildren, ','), function(rule) { + var matches = childRuleRegExp.exec(rule), + parent, prefix; + + if (matches) { + prefix = matches[1]; + + // Add/remove items from default + if (prefix) { + parent = children[matches[2]]; + } else { + parent = children[matches[2]] = { + '#comment': {} + }; + } + + parent = children[matches[2]]; + + each(split(matches[3], '|'), function(child) { + if (prefix === '-') { + delete parent[child]; + } else { + parent[child] = {}; + } + }); + } + }); + } + } + + function getElementRule(name) { + var element = elements[name], + i; + + // Exact match found + if (element) { + return element; + } + + // No exact match then try the patterns + i = patternElements.length; + while (i--) { + element = patternElements[i]; + + if (element.pattern.test(name)) { + return element; + } + } + } + + if (!settings.valid_elements) { + // No valid elements defined then clone the elements from the schema spec + each(schemaItems, function(element, name) { + elements[name] = { + attributes: element.attributes, + attributesOrder: element.attributesOrder + }; + + children[name] = element.children; + }); + + // Switch these on HTML4 + if (settings.schema != "html5") { + each(split('strong/b em/i'), function(item) { + item = split(item, '/'); + elements[item[1]].outputName = item[0]; + }); + } + + // Add default alt attribute for images, removed since alt="" is treated as presentational. + // elements.img.attributesDefault = [{name: 'alt', value: ''}]; + + // Remove these if they are empty by default + each(split('ol ul sub sup blockquote span font a table tbody tr strong em b i'), function(name) { + if (elements[name]) { + elements[name].removeEmpty = true; + } + }); + + // Padd these by default + each(split('p h1 h2 h3 h4 h5 h6 th td pre div address caption'), function(name) { + elements[name].paddEmpty = true; + }); + + // Remove these if they have no attributes + each(split('span'), function(name) { + elements[name].removeEmptyAttrs = true; + }); + + // Remove these by default + // TODO: Reenable in 4.1 + /*each(split('script style'), function(name) { + delete elements[name]; + });*/ + } else { + setValidElements(settings.valid_elements); + } + + addCustomElements(settings.custom_elements); + addValidChildren(settings.valid_children); + addValidElements(settings.extended_valid_elements); + + // Todo: Remove this when we fix list handling to be valid + addValidChildren('+ol[ul|ol],+ul[ul|ol]'); + + + // Some elements are not valid by themselves - require parents + each({ + dd: 'dl', + dt: 'dl', + li: 'ul ol', + td: 'tr', + th: 'tr', + tr: 'tbody thead tfoot', + tbody: 'table', + thead: 'table', + tfoot: 'table', + legend: 'fieldset', + area: 'map', + param: 'video audio object' + }, function(parents, item) { + if (elements[item]) { + elements[item].parentsRequired = split(parents); + } + }); + + + // Delete invalid elements + if (settings.invalid_elements) { + each(explode(settings.invalid_elements), function(item) { + if (elements[item]) { + delete elements[item]; + } + }); + } + + // If the user didn't allow span only allow internal spans + if (!getElementRule('span')) { + addValidElements('span[!data-mce-type|*]'); + } + + /** + * Name/value map object with valid parents and children to those parents. + * + * @example + * children = { + * div:{p:{}, h1:{}} + * }; + * @field children + * @type Object + */ + self.children = children; + + /** + * Name/value map object with valid styles for each element. + * + * @method getValidStyles + * @type Object + */ + self.getValidStyles = function() { + return validStyles; + }; + + /** + * Name/value map object with valid styles for each element. + * + * @method getInvalidStyles + * @type Object + */ + self.getInvalidStyles = function() { + return invalidStyles; + }; + + /** + * Name/value map object with valid classes for each element. + * + * @method getValidClasses + * @type Object + */ + self.getValidClasses = function() { + return validClasses; + }; + + /** + * Returns a map with boolean attributes. + * + * @method getBoolAttrs + * @return {Object} Name/value lookup map for boolean attributes. + */ + self.getBoolAttrs = function() { + return boolAttrMap; + }; + + /** + * Returns a map with block elements. + * + * @method getBlockElements + * @return {Object} Name/value lookup map for block elements. + */ + self.getBlockElements = function() { + return blockElementsMap; + }; + + /** + * Returns a map with text block elements. Such as: p,h1-h6,div,address + * + * @method getTextBlockElements + * @return {Object} Name/value lookup map for block elements. + */ + self.getTextBlockElements = function() { + return textBlockElementsMap; + }; + + /** + * Returns a map of inline text format nodes for example strong/span or ins. + * + * @method getTextInlineElements + * @return {Object} Name/value lookup map for text format elements. + */ + self.getTextInlineElements = function() { + return textInlineElementsMap; + }; + + /** + * Returns a map with short ended elements such as BR or IMG. + * + * @method getShortEndedElements + * @return {Object} Name/value lookup map for short ended elements. + */ + self.getShortEndedElements = function() { + return shortEndedElementsMap; + }; + + /** + * Returns a map with self closing tags such as <li>. + * + * @method getSelfClosingElements + * @return {Object} Name/value lookup map for self closing tags elements. + */ + self.getSelfClosingElements = function() { + return selfClosingElementsMap; + }; + + /** + * Returns a map with elements that should be treated as contents regardless if it has text + * content in them or not such as TD, VIDEO or IMG. + * + * @method getNonEmptyElements + * @return {Object} Name/value lookup map for non empty elements. + */ + self.getNonEmptyElements = function() { + return nonEmptyElementsMap; + }; + + /** + * Returns a map with elements that the caret should be moved in front of after enter is + * pressed + * + * @method getMoveCaretBeforeOnEnterElements + * @return {Object} Name/value lookup map for elements to place the caret in front of. + */ + self.getMoveCaretBeforeOnEnterElements = function() { + return moveCaretBeforeOnEnterElementsMap; + }; + + /** + * Returns a map with elements where white space is to be preserved like PRE or SCRIPT. + * + * @method getWhiteSpaceElements + * @return {Object} Name/value lookup map for white space elements. + */ + self.getWhiteSpaceElements = function() { + return whiteSpaceElementsMap; + }; + + /** + * Returns a map with special elements. These are elements that needs to be parsed + * in a special way such as script, style, textarea etc. The map object values + * are regexps used to find the end of the element. + * + * @method getSpecialElements + * @return {Object} Name/value lookup map for special elements. + */ + self.getSpecialElements = function() { + return specialElements; + }; + + /** + * Returns true/false if the specified element and it's child is valid or not + * according to the schema. + * + * @method isValidChild + * @param {String} name Element name to check for. + * @param {String} child Element child to verify. + * @return {Boolean} True/false if the element is a valid child of the specified parent. + */ + self.isValidChild = function(name, child) { + var parent = children[name.toLowerCase()]; + + return !!(parent && parent[child.toLowerCase()]); + }; + + /** + * Returns true/false if the specified element name and optional attribute is + * valid according to the schema. + * + * @method isValid + * @param {String} name Name of element to check. + * @param {String} attr Optional attribute name to check for. + * @return {Boolean} True/false if the element and attribute is valid. + */ + self.isValid = function(name, attr) { + var attrPatterns, i, rule = getElementRule(name); + + // Check if it's a valid element + if (rule) { + if (attr) { + // Check if attribute name exists + if (rule.attributes[attr]) { + return true; + } + + // Check if attribute matches a regexp pattern + attrPatterns = rule.attributePatterns; + if (attrPatterns) { + i = attrPatterns.length; + while (i--) { + if (attrPatterns[i].pattern.test(name)) { + return true; + } + } + } + } else { + return true; + } + } + + // No match + return false; + }; + + /** + * Returns true/false if the specified element is valid or not + * according to the schema. + * + * @method getElementRule + * @param {String} name Element name to check for. + * @return {Object} Element object or undefined if the element isn't valid. + */ + self.getElementRule = getElementRule; + + /** + * Returns an map object of all custom elements. + * + * @method getCustomElements + * @return {Object} Name/value map object of all custom elements. + */ + self.getCustomElements = function() { + return customElementsMap; + }; + + /** + * Parses a valid elements string and adds it to the schema. The valid elements + * format is for example "element[attr=default|otherattr]". + * Existing rules will be replaced with the ones specified, so this extends the schema. + * + * @method addValidElements + * @param {String} valid_elements String in the valid elements format to be parsed. + */ + self.addValidElements = addValidElements; + + /** + * Parses a valid elements string and sets it to the schema. The valid elements + * format is for example "element[attr=default|otherattr]". + * Existing rules will be replaced with the ones specified, so this extends the schema. + * + * @method setValidElements + * @param {String} valid_elements String in the valid elements format to be parsed. + */ + self.setValidElements = setValidElements; + + /** + * Adds custom non HTML elements to the schema. + * + * @method addCustomElements + * @param {String} custom_elements Comma separated list of custom elements to add. + */ + self.addCustomElements = addCustomElements; + + /** + * Parses a valid children string and adds them to the schema structure. The valid children + * format is for example: "element[child1|child2]". + * + * @method addValidChildren + * @param {String} valid_children Valid children elements string to parse + */ + self.addValidChildren = addValidChildren; + + self.elements = elements; + }; + } + ); + + /** + * DOMUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Utility class for various DOM manipulation and retrieval functions. + * + * @class tinymce.dom.DOMUtils + * @example + * // Add a class to an element by id in the page + * tinymce.DOM.addClass('someid', 'someclass'); + * + * // Add a class to an element by id inside the editor + * tinymce.activeEditor.dom.addClass('someid', 'someclass'); + */ + define( + 'tinymce.core.dom.DOMUtils', [ + 'tinymce.core.dom.DomQuery', + 'tinymce.core.dom.EventUtils', + 'tinymce.core.dom.Range', + 'tinymce.core.dom.Sizzle', + 'tinymce.core.dom.StyleSheetLoader', + 'tinymce.core.dom.TreeWalker', + 'tinymce.core.Env', + 'tinymce.core.html.Entities', + 'tinymce.core.html.Schema', + 'tinymce.core.html.Styles', + 'tinymce.core.util.Tools' + ], + function(DomQuery, EventUtils, Range, Sizzle, StyleSheetLoader, TreeWalker, Env, Entities, Schema, Styles, Tools) { + // Shorten names + var each = Tools.each, + is = Tools.is, + grep = Tools.grep, + trim = Tools.trim; + var isIE = Env.ie; + var simpleSelectorRe = /^([a-z0-9],?)+$/i; + var whiteSpaceRegExp = /^[ \t\r\n]*$/; + + function setupAttrHooks(domUtils, settings) { + var attrHooks = {}, + keepValues = settings.keep_values, + keepUrlHook; + + keepUrlHook = { + set: function($elm, value, name) { + if (settings.url_converter) { + value = settings.url_converter.call(settings.url_converter_scope || domUtils, value, name, $elm[0]); + } + + $elm.attr('data-mce-' + name, value).attr(name, value); + }, + + get: function($elm, name) { + return $elm.attr('data-mce-' + name) || $elm.attr(name); + } + }; + + attrHooks = { + style: { + set: function($elm, value) { + if (value !== null && typeof value === 'object') { + $elm.css(value); + return; + } + + if (keepValues) { + $elm.attr('data-mce-style', value); + } + + $elm.attr('style', value); + }, + + get: function($elm) { + var value = $elm.attr('data-mce-style') || $elm.attr('style'); + + value = domUtils.serializeStyle(domUtils.parseStyle(value), $elm[0].nodeName); + + return value; + } + } + }; + + if (keepValues) { + attrHooks.href = attrHooks.src = keepUrlHook; + } + + return attrHooks; + } + + function updateInternalStyleAttr(domUtils, $elm) { + var value = $elm.attr('style'); + + value = domUtils.serializeStyle(domUtils.parseStyle(value), $elm[0].nodeName); + + if (!value) { + value = null; + } + + $elm.attr('data-mce-style', value); + } + + function nodeIndex(node, normalized) { + var idx = 0, + lastNodeType, nodeType; + + if (node) { + for (lastNodeType = node.nodeType, node = node.previousSibling; node; node = node.previousSibling) { + nodeType = node.nodeType; + + // Normalize text nodes + if (normalized && nodeType == 3) { + if (nodeType == lastNodeType || !node.nodeValue.length) { + continue; + } + } + idx++; + lastNodeType = nodeType; + } + } + + return idx; + } + + /** + * Constructs a new DOMUtils instance. Consult the Wiki for more details on settings etc for this class. + * + * @constructor + * @method DOMUtils + * @param {Document} doc Document reference to bind the utility class to. + * @param {settings} settings Optional settings collection. + */ + function DOMUtils(doc, settings) { + var self = this, + blockElementsMap; + + self.doc = doc; + self.win = window; + self.files = {}; + self.counter = 0; + self.stdMode = !isIE || doc.documentMode >= 8; + self.boxModel = !isIE || doc.compatMode == "CSS1Compat" || self.stdMode; + self.styleSheetLoader = new StyleSheetLoader(doc); + self.boundEvents = []; + self.settings = settings = settings || {}; + self.schema = settings.schema ? settings.schema : new Schema({}); + self.styles = new Styles({ + url_converter: settings.url_converter, + url_converter_scope: settings.url_converter_scope + }, settings.schema); + + self.fixDoc(doc); + self.events = settings.ownEvents ? new EventUtils(settings.proxy) : EventUtils.Event; + self.attrHooks = setupAttrHooks(self, settings); + blockElementsMap = settings.schema ? settings.schema.getBlockElements() : {}; + self.$ = DomQuery.overrideDefaults(function() { + return { + context: doc, + element: self.getRoot() + }; + }); + + /** + * Returns true/false if the specified element is a block element or not. + * + * @method isBlock + * @param {Node/String} node Element/Node to check. + * @return {Boolean} True/False state if the node is a block element or not. + */ + self.isBlock = function(node) { + // Fix for #5446 + if (!node) { + return false; + } + + // This function is called in module pattern style since it might be executed with the wrong this scope + var type = node.nodeType; + + // If it's a node then check the type and use the nodeName + if (type) { + return !!(type === 1 && blockElementsMap[node.nodeName]); + } + + return !!blockElementsMap[node]; + }; + } + + DOMUtils.prototype = { + $$: function(elm) { + if (typeof elm == 'string') { + elm = this.get(elm); + } + + return this.$(elm); + }, + + root: null, + + fixDoc: function(doc) { + var settings = this.settings, + name; + + if (isIE && settings.schema) { + // Add missing HTML 4/5 elements to IE + ('abbr article aside audio canvas ' + + 'details figcaption figure footer ' + + 'header hgroup mark menu meter nav ' + + 'output progress section summary ' + + 'time video').replace(/\w+/g, function(name) { + doc.createElement(name); + }); + + // Create all custom elements + for (name in settings.schema.getCustomElements()) { + doc.createElement(name); + } + } + }, + + clone: function(node, deep) { + var self = this, + clone, doc; + + // TODO: Add feature detection here in the future + if (!isIE || node.nodeType !== 1 || deep) { + return node.cloneNode(deep); + } + + doc = self.doc; + + // Make a HTML5 safe shallow copy + if (!deep) { + clone = doc.createElement(node.nodeName); + + // Copy attribs + each(self.getAttribs(node), function(attr) { + self.setAttrib(clone, attr.nodeName, self.getAttrib(node, attr.nodeName)); + }); + + return clone; + } + + return clone.firstChild; + }, + + /** + * Returns the root node of the document. This is normally the body but might be a DIV. Parents like getParent will not + * go above the point of this root node. + * + * @method getRoot + * @return {Element} Root element for the utility class. + */ + getRoot: function() { + var self = this; + + return self.settings.root_element || self.doc.body; + }, + + /** + * Returns the viewport of the window. + * + * @method getViewPort + * @param {Window} win Optional window to get viewport of. + * @return {Object} Viewport object with fields x, y, w and h. + */ + getViewPort: function(win) { + var doc, rootElm; + + win = !win ? this.win : win; + doc = win.document; + rootElm = this.boxModel ? doc.documentElement : doc.body; + + // Returns viewport size excluding scrollbars + return { + x: win.pageXOffset || rootElm.scrollLeft, + y: win.pageYOffset || rootElm.scrollTop, + w: win.innerWidth || rootElm.clientWidth, + h: win.innerHeight || rootElm.clientHeight + }; + }, + + /** + * Returns the rectangle for a specific element. + * + * @method getRect + * @param {Element/String} elm Element object or element ID to get rectangle from. + * @return {object} Rectangle for specified element object with x, y, w, h fields. + */ + getRect: function(elm) { + var self = this, + pos, size; + + elm = self.get(elm); + pos = self.getPos(elm); + size = self.getSize(elm); + + return { + x: pos.x, + y: pos.y, + w: size.w, + h: size.h + }; + }, + + /** + * Returns the size dimensions of the specified element. + * + * @method getSize + * @param {Element/String} elm Element object or element ID to get rectangle from. + * @return {object} Rectangle for specified element object with w, h fields. + */ + getSize: function(elm) { + var self = this, + w, h; + + elm = self.get(elm); + w = self.getStyle(elm, 'width'); + h = self.getStyle(elm, 'height'); + + // Non pixel value, then force offset/clientWidth + if (w.indexOf('px') === -1) { + w = 0; + } + + // Non pixel value, then force offset/clientWidth + if (h.indexOf('px') === -1) { + h = 0; + } + + return { + w: parseInt(w, 10) || elm.offsetWidth || elm.clientWidth, + h: parseInt(h, 10) || elm.offsetHeight || elm.clientHeight + }; + }, + + /** + * Returns a node by the specified selector function. This function will + * loop through all parent nodes and call the specified function for each node. + * If the function then returns true indicating that it has found what it was looking for, the loop execution will then end + * and the node it found will be returned. + * + * @method getParent + * @param {Node/String} node DOM node to search parents on or ID string. + * @param {function} selector Selection function or CSS selector to execute on each node. + * @param {Node} root Optional root element, never go beyond this point. + * @return {Node} DOM Node or null if it wasn't found. + */ + getParent: function(node, selector, root) { + return this.getParents(node, selector, root, false); + }, + + /** + * Returns a node list of all parents matching the specified selector function or pattern. + * If the function then returns true indicating that it has found what it was looking for and that node will be collected. + * + * @method getParents + * @param {Node/String} node DOM node to search parents on or ID string. + * @param {function} selector Selection function to execute on each node or CSS pattern. + * @param {Node} root Optional root element, never go beyond this point. + * @return {Array} Array of nodes or null if it wasn't found. + */ + getParents: function(node, selector, root, collect) { + var self = this, + selectorVal, result = []; + + node = self.get(node); + collect = collect === undefined; + + // Default root on inline mode + root = root || (self.getRoot().nodeName != 'BODY' ? self.getRoot().parentNode : null); + + // Wrap node name as func + if (is(selector, 'string')) { + selectorVal = selector; + + if (selector === '*') { + selector = function(node) { + return node.nodeType == 1; + }; + } else { + selector = function(node) { + return self.is(node, selectorVal); + }; + } + } + + while (node) { + if (node == root || !node.nodeType || node.nodeType === 9) { + break; + } + + if (!selector || selector(node)) { + if (collect) { + result.push(node); + } else { + return node; + } + } + + node = node.parentNode; + } + + return collect ? result : null; + }, + + /** + * Returns the specified element by ID or the input element if it isn't a string. + * + * @method get + * @param {String/Element} n Element id to look for or element to just pass though. + * @return {Element} Element matching the specified id or null if it wasn't found. + */ + get: function(elm) { + var name; + + if (elm && this.doc && typeof elm == 'string') { + name = elm; + elm = this.doc.getElementById(elm); + + // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick + if (elm && elm.id !== name) { + return this.doc.getElementsByName(name)[1]; + } + } + + return elm; + }, + + /** + * Returns the next node that matches selector or function + * + * @method getNext + * @param {Node} node Node to find siblings from. + * @param {String/function} selector Selector CSS expression or function. + * @return {Node} Next node item matching the selector or null if it wasn't found. + */ + getNext: function(node, selector) { + return this._findSib(node, selector, 'nextSibling'); + }, + + /** + * Returns the previous node that matches selector or function + * + * @method getPrev + * @param {Node} node Node to find siblings from. + * @param {String/function} selector Selector CSS expression or function. + * @return {Node} Previous node item matching the selector or null if it wasn't found. + */ + getPrev: function(node, selector) { + return this._findSib(node, selector, 'previousSibling'); + }, + + // #ifndef jquery + + /** + * Selects specific elements by a CSS level 3 pattern. For example "div#a1 p.test". + * This function is optimized for the most common patterns needed in TinyMCE but it also performs well enough + * on more complex patterns. + * + * @method select + * @param {String} selector CSS level 3 pattern to select/find elements by. + * @param {Object} scope Optional root element/scope element to search in. + * @return {Array} Array with all matched elements. + * @example + * // Adds a class to all paragraphs in the currently active editor + * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass'); + * + * // Adds a class to all spans that have the test class in the currently active editor + * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('span.test'), 'someclass') + */ + select: function(selector, scope) { + var self = this; + + /*eslint new-cap:0 */ + return Sizzle(selector, self.get(scope) || self.settings.root_element || self.doc, []); + }, + + /** + * Returns true/false if the specified element matches the specified css pattern. + * + * @method is + * @param {Node/NodeList} elm DOM node to match or an array of nodes to match. + * @param {String} selector CSS pattern to match the element against. + */ + is: function(elm, selector) { + var i; + + if (!elm) { + return false; + } + + // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance + if (elm.length === undefined) { + // Simple all selector + if (selector === '*') { + return elm.nodeType == 1; + } + + // Simple selector just elements + if (simpleSelectorRe.test(selector)) { + selector = selector.toLowerCase().split(/,/); + elm = elm.nodeName.toLowerCase(); + + for (i = selector.length - 1; i >= 0; i--) { + if (selector[i] == elm) { + return true; + } + } + + return false; + } + } + + // Is non element + if (elm.nodeType && elm.nodeType != 1) { + return false; + } + + var elms = elm.nodeType ? [elm] : elm; + + /*eslint new-cap:0 */ + return Sizzle(selector, elms[0].ownerDocument || elms[0], null, elms).length > 0; + }, + + // #endif + + /** + * Adds the specified element to another element or elements. + * + * @method add + * @param {String/Element/Array} parentElm Element id string, DOM node element or array of ids or elements to add to. + * @param {String/Element} name Name of new element to add or existing element to add. + * @param {Object} attrs Optional object collection with arguments to add to the new element(s). + * @param {String} html Optional inner HTML contents to add for each element. + * @param {Boolean} create Optional flag if the element should be created or added. + * @return {Element/Array} Element that got created, or an array of created elements if multiple input elements + * were passed in. + * @example + * // Adds a new paragraph to the end of the active editor + * tinymce.activeEditor.dom.add(tinymce.activeEditor.getBody(), 'p', {title: 'my title'}, 'Some content'); + */ + add: function(parentElm, name, attrs, html, create) { + var self = this; + + return this.run(parentElm, function(parentElm) { + var newElm; + + newElm = is(name, 'string') ? self.doc.createElement(name) : name; + self.setAttribs(newElm, attrs); + + if (html) { + if (html.nodeType) { + newElm.appendChild(html); + } else { + self.setHTML(newElm, html); + } + } + + return !create ? parentElm.appendChild(newElm) : newElm; + }); + }, + + /** + * Creates a new element. + * + * @method create + * @param {String} name Name of new element. + * @param {Object} attrs Optional object name/value collection with element attributes. + * @param {String} html Optional HTML string to set as inner HTML of the element. + * @return {Element} HTML DOM node element that got created. + * @example + * // Adds an element where the caret/selection is in the active editor + * var el = tinymce.activeEditor.dom.create('div', {id: 'test', 'class': 'myclass'}, 'some content'); + * tinymce.activeEditor.selection.setNode(el); + */ + create: function(name, attrs, html) { + return this.add(this.doc.createElement(name), name, attrs, html, 1); + }, + + /** + * Creates HTML string for element. The element will be closed unless an empty inner HTML string is passed in. + * + * @method createHTML + * @param {String} name Name of new element. + * @param {Object} attrs Optional object name/value collection with element attributes. + * @param {String} html Optional HTML string to set as inner HTML of the element. + * @return {String} String with new HTML element, for example: <a href="#">test</a>. + * @example + * // Creates a html chunk and inserts it at the current selection/caret location + * tinymce.activeEditor.selection.setContent(tinymce.activeEditor.dom.createHTML('a', {href: 'test.html'}, 'some line')); + */ + createHTML: function(name, attrs, html) { + var outHtml = '', + key; + + outHtml += '<' + name; + + for (key in attrs) { + if (attrs.hasOwnProperty(key) && attrs[key] !== null && typeof attrs[key] != 'undefined') { + outHtml += ' ' + key + '="' + this.encode(attrs[key]) + '"'; + } + } + + // A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime + if (typeof html != "undefined") { + return outHtml + '>' + html + '</' + name + '>'; + } + + return outHtml + ' />'; + }, + + /** + * Creates a document fragment out of the specified HTML string. + * + * @method createFragment + * @param {String} html Html string to create fragment from. + * @return {DocumentFragment} Document fragment node. + */ + createFragment: function(html) { + var frag, node, doc = this.doc, + container; + + container = doc.createElement("div"); + frag = doc.createDocumentFragment(); + + if (html) { + container.innerHTML = html; + } + + while ((node = container.firstChild)) { + frag.appendChild(node); + } + + return frag; + }, + + /** + * Removes/deletes the specified element(s) from the DOM. + * + * @method remove + * @param {String/Element/Array} node ID of element or DOM element object or array containing multiple elements/ids. + * @param {Boolean} keepChildren Optional state to keep children or not. If set to true all children will be + * placed at the location of the removed element. + * @return {Element/Array} HTML DOM element that got removed, or an array of removed elements if multiple input elements + * were passed in. + * @example + * // Removes all paragraphs in the active editor + * tinymce.activeEditor.dom.remove(tinymce.activeEditor.dom.select('p')); + * + * // Removes an element by id in the document + * tinymce.DOM.remove('mydiv'); + */ + remove: function(node, keepChildren) { + node = this.$$(node); + + if (keepChildren) { + node.each(function() { + var child; + + while ((child = this.firstChild)) { + if (child.nodeType == 3 && child.data.length === 0) { + this.removeChild(child); + } else { + this.parentNode.insertBefore(child, this); + } + } + }).remove(); + } else { + node.remove(); + } + + return node.length > 1 ? node.toArray() : node[0]; + }, + + /** + * Sets the CSS style value on a HTML element. The name can be a camelcase string + * or the CSS style name like background-color. + * + * @method setStyle + * @param {String/Element/Array} elm HTML element/Array of elements to set CSS style value on. + * @param {String} name Name of the style value to set. + * @param {String} value Value to set on the style. + * @example + * // Sets a style value on all paragraphs in the currently active editor + * tinymce.activeEditor.dom.setStyle(tinymce.activeEditor.dom.select('p'), 'background-color', 'red'); + * + * // Sets a style value to an element by id in the current document + * tinymce.DOM.setStyle('mydiv', 'background-color', 'red'); + */ + setStyle: function(elm, name, value) { + elm = this.$$(elm).css(name, value); + + if (this.settings.update_styles) { + updateInternalStyleAttr(this, elm); + } + }, + + /** + * Returns the current style or runtime/computed value of an element. + * + * @method getStyle + * @param {String/Element} elm HTML element or element id string to get style from. + * @param {String} name Style name to return. + * @param {Boolean} computed Computed style. + * @return {String} Current style or computed style value of an element. + */ + getStyle: function(elm, name, computed) { + elm = this.$$(elm); + + if (computed) { + return elm.css(name); + } + + // Camelcase it, if needed + name = name.replace(/-(\D)/g, function(a, b) { + return b.toUpperCase(); + }); + + if (name == 'float') { + name = Env.ie && Env.ie < 12 ? 'styleFloat' : 'cssFloat'; + } + + return elm[0] && elm[0].style ? elm[0].style[name] : undefined; + }, + + /** + * Sets multiple styles on the specified element(s). + * + * @method setStyles + * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set styles on. + * @param {Object} styles Name/Value collection of style items to add to the element(s). + * @example + * // Sets styles on all paragraphs in the currently active editor + * tinymce.activeEditor.dom.setStyles(tinymce.activeEditor.dom.select('p'), {'background-color': 'red', 'color': 'green'}); + * + * // Sets styles to an element by id in the current document + * tinymce.DOM.setStyles('mydiv', {'background-color': 'red', 'color': 'green'}); + */ + setStyles: function(elm, styles) { + elm = this.$$(elm).css(styles); + + if (this.settings.update_styles) { + updateInternalStyleAttr(this, elm); + } + }, + + /** + * Removes all attributes from an element or elements. + * + * @method removeAllAttribs + * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to remove attributes from. + */ + removeAllAttribs: function(e) { + return this.run(e, function(e) { + var i, attrs = e.attributes; + for (i = attrs.length - 1; i >= 0; i--) { + e.removeAttributeNode(attrs.item(i)); + } + }); + }, + + /** + * Sets the specified attribute of an element or elements. + * + * @method setAttrib + * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attribute on. + * @param {String} name Name of attribute to set. + * @param {String} value Value to set on the attribute - if this value is falsy like null, 0 or '' it will remove + * the attribute instead. + * @example + * // Sets class attribute on all paragraphs in the active editor + * tinymce.activeEditor.dom.setAttrib(tinymce.activeEditor.dom.select('p'), 'class', 'myclass'); + * + * // Sets class attribute on a specific element in the current page + * tinymce.dom.setAttrib('mydiv', 'class', 'myclass'); + */ + setAttrib: function(elm, name, value) { + var self = this, + originalValue, hook, settings = self.settings; + + if (value === '') { + value = null; + } + + elm = self.$$(elm); + originalValue = elm.attr(name); + + if (!elm.length) { + return; + } + + hook = self.attrHooks[name]; + if (hook && hook.set) { + hook.set(elm, value, name); + } else { + elm.attr(name, value); + } + + if (originalValue != value && settings.onSetAttrib) { + settings.onSetAttrib({ + attrElm: elm, + attrName: name, + attrValue: value + }); + } + }, + + /** + * Sets two or more specified attributes of an element or elements. + * + * @method setAttribs + * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attributes on. + * @param {Object} attrs Name/Value collection of attribute items to add to the element(s). + * @example + * // Sets class and title attributes on all paragraphs in the active editor + * tinymce.activeEditor.dom.setAttribs(tinymce.activeEditor.dom.select('p'), {'class': 'myclass', title: 'some title'}); + * + * // Sets class and title attributes on a specific element in the current page + * tinymce.DOM.setAttribs('mydiv', {'class': 'myclass', title: 'some title'}); + */ + setAttribs: function(elm, attrs) { + var self = this; + + self.$$(elm).each(function(i, node) { + each(attrs, function(value, name) { + self.setAttrib(node, name, value); + }); + }); + }, + + /** + * Returns the specified attribute by name. + * + * @method getAttrib + * @param {String/Element} elm Element string id or DOM element to get attribute from. + * @param {String} name Name of attribute to get. + * @param {String} defaultVal Optional default value to return if the attribute didn't exist. + * @return {String} Attribute value string, default value or null if the attribute wasn't found. + */ + getAttrib: function(elm, name, defaultVal) { + var self = this, + hook, value; + + elm = self.$$(elm); + + if (elm.length) { + hook = self.attrHooks[name]; + + if (hook && hook.get) { + value = hook.get(elm, name); + } else { + value = elm.attr(name); + } + } + + if (typeof value == 'undefined') { + value = defaultVal || ''; + } + + return value; + }, + + /** + * Returns the absolute x, y position of a node. The position will be returned in an object with x, y fields. + * + * @method getPos + * @param {Element/String} elm HTML element or element id to get x, y position from. + * @param {Element} rootElm Optional root element to stop calculations at. + * @return {object} Absolute position of the specified element object with x, y fields. + */ + getPos: function(elm, rootElm) { + var self = this, + x = 0, + y = 0, + offsetParent, doc = self.doc, + body = doc.body, + pos; + + elm = self.get(elm); + rootElm = rootElm || body; + + if (elm) { + // Use getBoundingClientRect if it exists since it's faster than looping offset nodes + // Fallback to offsetParent calculations if the body isn't static better since it stops at the body root + if (rootElm === body && elm.getBoundingClientRect && DomQuery(body).css('position') === 'static') { + pos = elm.getBoundingClientRect(); + rootElm = self.boxModel ? doc.documentElement : body; + + // Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit + // Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position + x = pos.left + (doc.documentElement.scrollLeft || body.scrollLeft) - rootElm.clientLeft; + y = pos.top + (doc.documentElement.scrollTop || body.scrollTop) - rootElm.clientTop; + + return { + x: x, + y: y + }; + } + + offsetParent = elm; + while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) { + x += offsetParent.offsetLeft || 0; + y += offsetParent.offsetTop || 0; + offsetParent = offsetParent.offsetParent; + } + + offsetParent = elm.parentNode; + while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) { + x -= offsetParent.scrollLeft || 0; + y -= offsetParent.scrollTop || 0; + offsetParent = offsetParent.parentNode; + } + } + + return { + x: x, + y: y + }; + }, + + /** + * Parses the specified style value into an object collection. This parser will also + * merge and remove any redundant items that browsers might have added. It will also convert non-hex + * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings. + * + * @method parseStyle + * @param {String} cssText Style value to parse, for example: border:1px solid red;. + * @return {Object} Object representation of that style, for example: {border: '1px solid red'} + */ + parseStyle: function(cssText) { + return this.styles.parse(cssText); + }, + + /** + * Serializes the specified style object into a string. + * + * @method serializeStyle + * @param {Object} styles Object to serialize as string, for example: {border: '1px solid red'} + * @param {String} name Optional element name. + * @return {String} String representation of the style object, for example: border: 1px solid red. + */ + serializeStyle: function(styles, name) { + return this.styles.serialize(styles, name); + }, + + /** + * Adds a style element at the top of the document with the specified cssText content. + * + * @method addStyle + * @param {String} cssText CSS Text style to add to top of head of document. + */ + addStyle: function(cssText) { + var self = this, + doc = self.doc, + head, styleElm; + + // Prevent inline from loading the same styles twice + if (self !== DOMUtils.DOM && doc === document) { + var addedStyles = DOMUtils.DOM.addedStyles; + + addedStyles = addedStyles || []; + if (addedStyles[cssText]) { + return; + } + + addedStyles[cssText] = true; + DOMUtils.DOM.addedStyles = addedStyles; + } + + // Create style element if needed + styleElm = doc.getElementById('mceDefaultStyles'); + if (!styleElm) { + styleElm = doc.createElement('style'); + styleElm.id = 'mceDefaultStyles'; + styleElm.type = 'text/css'; + + head = doc.getElementsByTagName('head')[0]; + if (head.firstChild) { + head.insertBefore(styleElm, head.firstChild); + } else { + head.appendChild(styleElm); + } + } + + // Append style data to old or new style element + if (styleElm.styleSheet) { + styleElm.styleSheet.cssText += cssText; + } else { + styleElm.appendChild(doc.createTextNode(cssText)); + } + }, + + /** + * Imports/loads the specified CSS file into the document bound to the class. + * + * @method loadCSS + * @param {String} url URL to CSS file to load. + * @example + * // Loads a CSS file dynamically into the current document + * tinymce.DOM.loadCSS('somepath/some.css'); + * + * // Loads a CSS file into the currently active editor instance + * tinymce.activeEditor.dom.loadCSS('somepath/some.css'); + * + * // Loads a CSS file into an editor instance by id + * tinymce.get('someid').dom.loadCSS('somepath/some.css'); + * + * // Loads multiple CSS files into the current document + * tinymce.DOM.loadCSS('somepath/some.css,somepath/someother.css'); + */ + loadCSS: function(url) { + var self = this, + doc = self.doc, + head; + + // Prevent inline from loading the same CSS file twice + if (self !== DOMUtils.DOM && doc === document) { + DOMUtils.DOM.loadCSS(url); + return; + } + + if (!url) { + url = ''; + } + + head = doc.getElementsByTagName('head')[0]; + + each(url.split(','), function(url) { + var link; + + url = Tools._addCacheSuffix(url); + + if (self.files[url]) { + return; + } + + self.files[url] = true; + link = self.create('link', { + rel: 'stylesheet', + href: url + }); + + // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug + // This fix seems to resolve that issue by recalcing the document once a stylesheet finishes loading + // It's ugly but it seems to work fine. + if (isIE && doc.documentMode && doc.recalc) { + link.onload = function() { + if (doc.recalc) { + doc.recalc(); + } + + link.onload = null; + }; + } + + head.appendChild(link); + }); + }, + + /** + * Adds a class to the specified element or elements. + * + * @method addClass + * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs. + * @param {String} cls Class name to add to each element. + * @return {String/Array} String with new class value or array with new class values for all elements. + * @example + * // Adds a class to all paragraphs in the active editor + * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'myclass'); + * + * // Adds a class to a specific element in the current page + * tinymce.DOM.addClass('mydiv', 'myclass'); + */ + addClass: function(elm, cls) { + this.$$(elm).addClass(cls); + }, + + /** + * Removes a class from the specified element or elements. + * + * @method removeClass + * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs. + * @param {String} cls Class name to remove from each element. + * @return {String/Array} String of remaining class name(s), or an array of strings if multiple input elements + * were passed in. + * @example + * // Removes a class from all paragraphs in the active editor + * tinymce.activeEditor.dom.removeClass(tinymce.activeEditor.dom.select('p'), 'myclass'); + * + * // Removes a class from a specific element in the current page + * tinymce.DOM.removeClass('mydiv', 'myclass'); + */ + removeClass: function(elm, cls) { + this.toggleClass(elm, cls, false); + }, + + /** + * Returns true if the specified element has the specified class. + * + * @method hasClass + * @param {String/Element} elm HTML element or element id string to check CSS class on. + * @param {String} cls CSS class to check for. + * @return {Boolean} true/false if the specified element has the specified class. + */ + hasClass: function(elm, cls) { + return this.$$(elm).hasClass(cls); + }, + + /** + * Toggles the specified class on/off. + * + * @method toggleClass + * @param {Element} elm Element to toggle class on. + * @param {[type]} cls Class to toggle on/off. + * @param {[type]} state Optional state to set. + */ + toggleClass: function(elm, cls, state) { + this.$$(elm).toggleClass(cls, state).each(function() { + if (this.className === '') { + DomQuery(this).attr('class', null); + } + }); + }, + + /** + * Shows the specified element(s) by ID by setting the "display" style. + * + * @method show + * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to show. + */ + show: function(elm) { + this.$$(elm).show(); + }, + + /** + * Hides the specified element(s) by ID by setting the "display" style. + * + * @method hide + * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to hide. + * @example + * // Hides an element by id in the document + * tinymce.DOM.hide('myid'); + */ + hide: function(elm) { + this.$$(elm).hide(); + }, + + /** + * Returns true/false if the element is hidden or not by checking the "display" style. + * + * @method isHidden + * @param {String/Element} elm Id or element to check display state on. + * @return {Boolean} true/false if the element is hidden or not. + */ + isHidden: function(elm) { + return this.$$(elm).css('display') == 'none'; + }, + + /** + * Returns a unique id. This can be useful when generating elements on the fly. + * This method will not check if the element already exists. + * + * @method uniqueId + * @param {String} prefix Optional prefix to add in front of all ids - defaults to "mce_". + * @return {String} Unique id. + */ + uniqueId: function(prefix) { + return (!prefix ? 'mce_' : prefix) + (this.counter++); + }, + + /** + * Sets the specified HTML content inside the element or elements. The HTML will first be processed. This means + * URLs will get converted, hex color values fixed etc. Check processHTML for details. + * + * @method setHTML + * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set HTML inside of. + * @param {String} html HTML content to set as inner HTML of the element. + * @example + * // Sets the inner HTML of all paragraphs in the active editor + * tinymce.activeEditor.dom.setHTML(tinymce.activeEditor.dom.select('p'), 'some inner html'); + * + * // Sets the inner HTML of an element by id in the document + * tinymce.DOM.setHTML('mydiv', 'some inner html'); + */ + setHTML: function(elm, html) { + elm = this.$$(elm); + + if (isIE) { + elm.each(function(i, target) { + if (target.canHaveHTML === false) { + return; + } + + // Remove all child nodes, IE keeps empty text nodes in DOM + while (target.firstChild) { + target.removeChild(target.firstChild); + } + + try { + // IE will remove comments from the beginning + // unless you padd the contents with something + target.innerHTML = '<br>' + html; + target.removeChild(target.firstChild); + } catch (ex) { + // IE sometimes produces an unknown runtime error on innerHTML if it's a div inside a p + DomQuery('<div></div>').html('<br>' + html).contents().slice(1).appendTo(target); + } + + return html; + }); + } else { + elm.html(html); + } + }, + + /** + * Returns the outer HTML of an element. + * + * @method getOuterHTML + * @param {String/Element} elm Element ID or element object to get outer HTML from. + * @return {String} Outer HTML string. + * @example + * tinymce.DOM.getOuterHTML(editorElement); + * tinymce.activeEditor.getOuterHTML(tinymce.activeEditor.getBody()); + */ + getOuterHTML: function(elm) { + elm = this.get(elm); + + // Older FF doesn't have outerHTML 3.6 is still used by some orgaizations + return elm.nodeType == 1 && "outerHTML" in elm ? elm.outerHTML : DomQuery('<div></div>').append(DomQuery(elm).clone()).html(); + }, + + /** + * Sets the specified outer HTML on an element or elements. + * + * @method setOuterHTML + * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set outer HTML on. + * @param {Object} html HTML code to set as outer value for the element. + * @example + * // Sets the outer HTML of all paragraphs in the active editor + * tinymce.activeEditor.dom.setOuterHTML(tinymce.activeEditor.dom.select('p'), '<div>some html</div>'); + * + * // Sets the outer HTML of an element by id in the document + * tinymce.DOM.setOuterHTML('mydiv', '<div>some html</div>'); + */ + setOuterHTML: function(elm, html) { + var self = this; + + self.$$(elm).each(function() { + try { + // Older FF doesn't have outerHTML 3.6 is still used by some organizations + if ("outerHTML" in this) { + this.outerHTML = html; + return; + } + } catch (ex) { + // Ignore + } + + // OuterHTML for IE it sometimes produces an "unknown runtime error" + self.remove(DomQuery(this).html(html), true); + }); + }, + + /** + * Entity decodes a string. This method decodes any HTML entities, such as å. + * + * @method decode + * @param {String} s String to decode entities on. + * @return {String} Entity decoded string. + */ + decode: Entities.decode, + + /** + * Entity encodes a string. This method encodes the most common entities, such as <>"&. + * + * @method encode + * @param {String} text String to encode with entities. + * @return {String} Entity encoded string. + */ + encode: Entities.encodeAllRaw, + + /** + * Inserts an element after the reference element. + * + * @method insertAfter + * @param {Element} node Element to insert after the reference. + * @param {Element/String/Array} referenceNode Reference element, element id or array of elements to insert after. + * @return {Element/Array} Element that got added or an array with elements. + */ + insertAfter: function(node, referenceNode) { + referenceNode = this.get(referenceNode); + + return this.run(node, function(node) { + var parent, nextSibling; + + parent = referenceNode.parentNode; + nextSibling = referenceNode.nextSibling; + + if (nextSibling) { + parent.insertBefore(node, nextSibling); + } else { + parent.appendChild(node); + } + + return node; + }); + }, + + /** + * Replaces the specified element or elements with the new element specified. The new element will + * be cloned if multiple input elements are passed in. + * + * @method replace + * @param {Element} newElm New element to replace old ones with. + * @param {Element/String/Array} oldElm Element DOM node, element id or array of elements or ids to replace. + * @param {Boolean} keepChildren Optional keep children state, if set to true child nodes from the old object will be added + * to new ones. + */ + replace: function(newElm, oldElm, keepChildren) { + var self = this; + + return self.run(oldElm, function(oldElm) { + if (is(oldElm, 'array')) { + newElm = newElm.cloneNode(true); + } + + if (keepChildren) { + each(grep(oldElm.childNodes), function(node) { + newElm.appendChild(node); + }); + } + + return oldElm.parentNode.replaceChild(newElm, oldElm); + }); + }, + + /** + * Renames the specified element and keeps its attributes and children. + * + * @method rename + * @param {Element} elm Element to rename. + * @param {String} name Name of the new element. + * @return {Element} New element or the old element if it needed renaming. + */ + rename: function(elm, name) { + var self = this, + newElm; + + if (elm.nodeName != name.toUpperCase()) { + // Rename block element + newElm = self.create(name); + + // Copy attribs to new block + each(self.getAttribs(elm), function(attrNode) { + self.setAttrib(newElm, attrNode.nodeName, self.getAttrib(elm, attrNode.nodeName)); + }); + + // Replace block + self.replace(newElm, elm, 1); + } + + return newElm || elm; + }, + + /** + * Find the common ancestor of two elements. This is a shorter method than using the DOM Range logic. + * + * @method findCommonAncestor + * @param {Element} a Element to find common ancestor of. + * @param {Element} b Element to find common ancestor of. + * @return {Element} Common ancestor element of the two input elements. + */ + findCommonAncestor: function(a, b) { + var ps = a, + pe; + + while (ps) { + pe = b; + + while (pe && ps != pe) { + pe = pe.parentNode; + } + + if (ps == pe) { + break; + } + + ps = ps.parentNode; + } + + if (!ps && a.ownerDocument) { + return a.ownerDocument.documentElement; + } + + return ps; + }, + + /** + * Parses the specified RGB color value and returns a hex version of that color. + * + * @method toHex + * @param {String} rgbVal RGB string value like rgb(1,2,3) + * @return {String} Hex version of that RGB value like #FF00FF. + */ + toHex: function(rgbVal) { + return this.styles.toHex(Tools.trim(rgbVal)); + }, + + /** + * Executes the specified function on the element by id or dom element node or array of elements/id. + * + * @method run + * @param {String/Element/Array} elm ID or DOM element object or array with ids or elements. + * @param {function} func Function to execute for each item. + * @param {Object} scope Optional scope to execute the function in. + * @return {Object/Array} Single object, or an array of objects if multiple input elements were passed in. + */ + run: function(elm, func, scope) { + var self = this, + result; + + if (typeof elm === 'string') { + elm = self.get(elm); + } + + if (!elm) { + return false; + } + + scope = scope || this; + if (!elm.nodeType && (elm.length || elm.length === 0)) { + result = []; + + each(elm, function(elm, i) { + if (elm) { + if (typeof elm == 'string') { + elm = self.get(elm); + } + + result.push(func.call(scope, elm, i)); + } + }); + + return result; + } + + return func.call(scope, elm); + }, + + /** + * Returns a NodeList with attributes for the element. + * + * @method getAttribs + * @param {HTMLElement/string} elm Element node or string id to get attributes from. + * @return {NodeList} NodeList with attributes. + */ + getAttribs: function(elm) { + var attrs; + + elm = this.get(elm); + + if (!elm) { + return []; + } + + if (isIE) { + attrs = []; + + // Object will throw exception in IE + if (elm.nodeName == 'OBJECT') { + return elm.attributes; + } + + // IE doesn't keep the selected attribute if you clone option elements + if (elm.nodeName === 'OPTION' && this.getAttrib(elm, 'selected')) { + attrs.push({ + specified: 1, + nodeName: 'selected' + }); + } + + // It's crazy that this is faster in IE but it's because it returns all attributes all the time + var attrRegExp = /<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi; + elm.cloneNode(false).outerHTML.replace(attrRegExp, '').replace(/[\w:\-]+/gi, function(a) { + attrs.push({ + specified: 1, + nodeName: a + }); + }); + + return attrs; + } + + return elm.attributes; + }, + + /** + * Returns true/false if the specified node is to be considered empty or not. + * + * @example + * tinymce.DOM.isEmpty(node, {img: true}); + * @method isEmpty + * @param {Object} elements Optional name/value object with elements that are automatically treated as non-empty elements. + * @return {Boolean} true/false if the node is empty or not. + */ + isEmpty: function(node, elements) { + var self = this, + i, attributes, type, whitespace, walker, name, brCount = 0; + + node = node.firstChild; + if (node) { + walker = new TreeWalker(node, node.parentNode); + elements = elements || (self.schema ? self.schema.getNonEmptyElements() : null); + whitespace = self.schema ? self.schema.getWhiteSpaceElements() : {}; + + do { + type = node.nodeType; + + if (type === 1) { + // Ignore bogus elements + var bogusVal = node.getAttribute('data-mce-bogus'); + if (bogusVal) { + node = walker.next(bogusVal === 'all'); + continue; + } + + // Keep empty elements like <img /> + name = node.nodeName.toLowerCase(); + if (elements && elements[name]) { + // Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p> + if (name === 'br') { + brCount++; + node = walker.next(); + continue; + } + + return false; + } + + // Keep elements with data-bookmark attributes or name attribute like <a name="1"></a> + attributes = self.getAttribs(node); + i = attributes.length; + while (i--) { + name = attributes[i].nodeName; + if (name === "name" || name === 'data-mce-bookmark') { + return false; + } + } + } + + // Keep comment nodes + if (type == 8) { + return false; + } + + // Keep non whitespace text nodes + if (type === 3 && !whiteSpaceRegExp.test(node.nodeValue)) { + return false; + } + + // Keep whitespace preserve elements + if (type === 3 && node.parentNode && whitespace[node.parentNode.nodeName] && whiteSpaceRegExp.test(node.nodeValue)) { + return false; + } + + node = walker.next(); + } while (node); + } + + return brCount <= 1; + }, + + /** + * Creates a new DOM Range object. This will use the native DOM Range API if it's + * available. If it's not, it will fall back to the custom TinyMCE implementation. + * + * @method createRng + * @return {DOMRange} DOM Range object. + * @example + * var rng = tinymce.DOM.createRng(); + * alert(rng.startContainer + "," + rng.startOffset); + */ + createRng: function() { + var doc = this.doc; + + return doc.createRange ? doc.createRange() : new Range(this); + }, + + /** + * Returns the index of the specified node within its parent. + * + * @method nodeIndex + * @param {Node} node Node to look for. + * @param {boolean} normalized Optional true/false state if the index is what it would be after a normalization. + * @return {Number} Index of the specified node. + */ + nodeIndex: nodeIndex, + + /** + * Splits an element into two new elements and places the specified split + * element or elements between the new ones. For example splitting the paragraph at the bold element in + * this example <p>abc<b>abc</b>123</p> would produce <p>abc</p><b>abc</b><p>123</p>. + * + * @method split + * @param {Element} parentElm Parent element to split. + * @param {Element} splitElm Element to split at. + * @param {Element} replacementElm Optional replacement element to replace the split element with. + * @return {Element} Returns the split element or the replacement element if that is specified. + */ + split: function(parentElm, splitElm, replacementElm) { + var self = this, + r = self.createRng(), + bef, aft, pa; + + // W3C valid browsers tend to leave empty nodes to the left/right side of the contents - this makes sense + // but we don't want that in our code since it serves no purpose for the end user + // For example splitting this html at the bold element: + // <p>text 1<span><b>CHOP</b></span>text 2</p> + // would produce: + // <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p> + // this function will then trim off empty edges and produce: + // <p>text 1</p><b>CHOP</b><p>text 2</p> + function trimNode(node) { + var i, children = node.childNodes, + type = node.nodeType; + + function surroundedBySpans(node) { + var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN'; + var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN'; + return previousIsSpan && nextIsSpan; + } + + if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark') { + return; + } + + for (i = children.length - 1; i >= 0; i--) { + trimNode(children[i]); + } + + if (type != 9) { + // Keep non whitespace text nodes + if (type == 3 && node.nodeValue.length > 0) { + // If parent element isn't a block or there isn't any useful contents for example "<p> </p>" + // Also keep text nodes with only spaces if surrounded by spans. + // eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b + var trimmedLength = trim(node.nodeValue).length; + if (!self.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength === 0 && surroundedBySpans(node)) { + return; + } + } else if (type == 1) { + // If the only child is a bookmark then move it up + children = node.childNodes; + + // TODO fix this complex if + if (children.length == 1 && children[0] && children[0].nodeType == 1 && + children[0].getAttribute('data-mce-type') == 'bookmark') { + node.parentNode.insertBefore(children[0], node); + } + + // Keep non empty elements or img, hr etc + if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName)) { + return; + } + } + + self.remove(node); + } + + return node; + } + + if (parentElm && splitElm) { + // Get before chunk + r.setStart(parentElm.parentNode, self.nodeIndex(parentElm)); + r.setEnd(splitElm.parentNode, self.nodeIndex(splitElm)); + bef = r.extractContents(); + + // Get after chunk + r = self.createRng(); + r.setStart(splitElm.parentNode, self.nodeIndex(splitElm) + 1); + r.setEnd(parentElm.parentNode, self.nodeIndex(parentElm) + 1); + aft = r.extractContents(); + + // Insert before chunk + pa = parentElm.parentNode; + pa.insertBefore(trimNode(bef), parentElm); + + // Insert middle chunk + if (replacementElm) { + pa.insertBefore(replacementElm, parentElm); + //pa.replaceChild(replacementElm, splitElm); + } else { + pa.insertBefore(splitElm, parentElm); + } + + // Insert after chunk + pa.insertBefore(trimNode(aft), parentElm); + self.remove(parentElm); + + return replacementElm || splitElm; + } + }, + + /** + * Adds an event handler to the specified object. + * + * @method bind + * @param {Element/Document/Window/Array} target Target element to bind events to. + * handler to or an array of elements/ids/documents. + * @param {String} name Name of event handler to add, for example: click. + * @param {function} func Function to execute when the event occurs. + * @param {Object} scope Optional scope to execute the function in. + * @return {function} Function callback handler the same as the one passed in. + */ + bind: function(target, name, func, scope) { + var self = this; + + if (Tools.isArray(target)) { + var i = target.length; + + while (i--) { + target[i] = self.bind(target[i], name, func, scope); + } + + return target; + } + + // Collect all window/document events bound by editor instance + if (self.settings.collect && (target === self.doc || target === self.win)) { + self.boundEvents.push([target, name, func, scope]); + } + + return self.events.bind(target, name, func, scope || self); + }, + + /** + * Removes the specified event handler by name and function from an element or collection of elements. + * + * @method unbind + * @param {Element/Document/Window/Array} target Target element to unbind events on. + * @param {String} name Event handler name, for example: "click" + * @param {function} func Function to remove. + * @return {bool/Array} Bool state of true if the handler was removed, or an array of states if multiple input elements + * were passed in. + */ + unbind: function(target, name, func) { + var self = this, + i; + + if (Tools.isArray(target)) { + i = target.length; + + while (i--) { + target[i] = self.unbind(target[i], name, func); + } + + return target; + } + + // Remove any bound events matching the input + if (self.boundEvents && (target === self.doc || target === self.win)) { + i = self.boundEvents.length; + + while (i--) { + var item = self.boundEvents[i]; + + if (target == item[0] && (!name || name == item[1]) && (!func || func == item[2])) { + this.events.unbind(item[0], item[1], item[2]); + } + } + } + + return this.events.unbind(target, name, func); + }, + + /** + * Fires the specified event name with object on target. + * + * @method fire + * @param {Node/Document/Window} target Target element or object to fire event on. + * @param {String} name Name of the event to fire. + * @param {Object} evt Event object to send. + * @return {Event} Event object. + */ + fire: function(target, name, evt) { + return this.events.fire(target, name, evt); + }, + + // Returns the content editable state of a node + getContentEditable: function(node) { + var contentEditable; + + // Check type + if (!node || node.nodeType != 1) { + return null; + } + + // Check for fake content editable + contentEditable = node.getAttribute("data-mce-contenteditable"); + if (contentEditable && contentEditable !== "inherit") { + return contentEditable; + } + + // Check for real content editable + return node.contentEditable !== "inherit" ? node.contentEditable : null; + }, + + getContentEditableParent: function(node) { + var root = this.getRoot(), + state = null; + + for (; node && node !== root; node = node.parentNode) { + state = this.getContentEditable(node); + + if (state !== null) { + break; + } + } + + return state; + }, + + /** + * Destroys all internal references to the DOM to solve IE leak issues. + * + * @method destroy + */ + destroy: function() { + var self = this; + + // Unbind all events bound to window/document by editor instance + if (self.boundEvents) { + var i = self.boundEvents.length; + + while (i--) { + var item = self.boundEvents[i]; + this.events.unbind(item[0], item[1], item[2]); + } + + self.boundEvents = null; + } + + // Restore sizzle document to window.document + // Since the current document might be removed producing "Permission denied" on IE see #6325 + if (Sizzle.setDocument) { + Sizzle.setDocument(); + } + + self.win = self.doc = self.root = self.events = self.frag = null; + }, + + isChildOf: function(node, parent) { + while (node) { + if (parent === node) { + return true; + } + + node = node.parentNode; + } + + return false; + }, + + // #ifdef debug + + dumpRng: function(r) { + return ( + 'startContainer: ' + r.startContainer.nodeName + + ', startOffset: ' + r.startOffset + + ', endContainer: ' + r.endContainer.nodeName + + ', endOffset: ' + r.endOffset + ); + }, + + // #endif + + _findSib: function(node, selector, name) { + var self = this, + func = selector; + + if (node) { + // If expression make a function of it using is + if (typeof func == 'string') { + func = function(node) { + return self.is(node, selector); + }; + } + + // Loop all siblings + for (node = node[name]; node; node = node[name]) { + if (func(node)) { + return node; + } + } + } + + return null; + } + }; + + /** + * Instance of DOMUtils for the current document. + * + * @static + * @property DOM + * @type tinymce.dom.DOMUtils + * @example + * // Example of how to add a class to some element by id + * tinymce.DOM.addClass('someid', 'someclass'); + */ + DOMUtils.DOM = new DOMUtils(document); + DOMUtils.nodeIndex = nodeIndex; + + return DOMUtils; + } + ); + + /** + * ScriptLoader.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /*globals console*/ + + /** + * This class handles asynchronous/synchronous loading of JavaScript files it will execute callbacks + * when various items gets loaded. This class is useful to load external JavaScript files. + * + * @class tinymce.dom.ScriptLoader + * @example + * // Load a script from a specific URL using the global script loader + * tinymce.ScriptLoader.load('somescript.js'); + * + * // Load a script using a unique instance of the script loader + * var scriptLoader = new tinymce.dom.ScriptLoader(); + * + * scriptLoader.load('somescript.js'); + * + * // Load multiple scripts + * var scriptLoader = new tinymce.dom.ScriptLoader(); + * + * scriptLoader.add('somescript1.js'); + * scriptLoader.add('somescript2.js'); + * scriptLoader.add('somescript3.js'); + * + * scriptLoader.loadQueue(function() { + * alert('All scripts are now loaded.'); + * }); + */ + define( + 'tinymce.core.dom.ScriptLoader', [ + "tinymce.core.dom.DOMUtils", + "tinymce.core.util.Tools" + ], + function(DOMUtils, Tools) { + var DOM = DOMUtils.DOM; + var each = Tools.each, + grep = Tools.grep; + + var isFunction = function(f) { + return typeof f === 'function'; + }; + + function ScriptLoader() { + var QUEUED = 0, + LOADING = 1, + LOADED = 2, + FAILED = 3, + states = {}, + queue = [], + scriptLoadedCallbacks = {}, + queueLoadedCallbacks = [], + loading = 0, + undef; + + /** + * Loads a specific script directly without adding it to the load queue. + * + * @method load + * @param {String} url Absolute URL to script to add. + * @param {function} callback Optional success callback function when the script loaded successfully. + * @param {function} callback Optional failure callback function when the script failed to load. + */ + function loadScript(url, success, failure) { + var dom = DOM, + elm, id; + + // Execute callback when script is loaded + function done() { + dom.remove(id); + + if (elm) { + elm.onreadystatechange = elm.onload = elm = null; + } + + success(); + } + + function error() { + /*eslint no-console:0 */ + + // We can't mark it as done if there is a load error since + // A) We don't want to produce 404 errors on the server and + // B) the onerror event won't fire on all browsers. + // done(); + + if (isFunction(failure)) { + failure(); + } else { + // Report the error so it's easier for people to spot loading errors + if (typeof console !== "undefined" && console.log) { + console.log("Failed to load script: " + url); + } + } + } + + id = dom.uniqueId(); + + // Create new script element + elm = document.createElement('script'); + elm.id = id; + elm.type = 'text/javascript'; + elm.src = Tools._addCacheSuffix(url); + + // Seems that onreadystatechange works better on IE 10 onload seems to fire incorrectly + if ("onreadystatechange" in elm) { + elm.onreadystatechange = function() { + if (/loaded|complete/.test(elm.readyState)) { + done(); + } + }; + } else { + elm.onload = done; + } + + // Add onerror event will get fired on some browsers but not all of them + elm.onerror = error; + + // Add script to document + (document.getElementsByTagName('head')[0] || document.body).appendChild(elm); + } + + /** + * Returns true/false if a script has been loaded or not. + * + * @method isDone + * @param {String} url URL to check for. + * @return {Boolean} true/false if the URL is loaded. + */ + this.isDone = function(url) { + return states[url] == LOADED; + }; + + /** + * Marks a specific script to be loaded. This can be useful if a script got loaded outside + * the script loader or to skip it from loading some script. + * + * @method markDone + * @param {string} url Absolute URL to the script to mark as loaded. + */ + this.markDone = function(url) { + states[url] = LOADED; + }; + + /** + * Adds a specific script to the load queue of the script loader. + * + * @method add + * @param {String} url Absolute URL to script to add. + * @param {function} success Optional success callback function to execute when the script loades successfully. + * @param {Object} scope Optional scope to execute callback in. + * @param {function} failure Optional failure callback function to execute when the script failed to load. + */ + this.add = this.load = function(url, success, scope, failure) { + var state = states[url]; + + // Add url to load queue + if (state == undef) { + queue.push(url); + states[url] = QUEUED; + } + + if (success) { + // Store away callback for later execution + if (!scriptLoadedCallbacks[url]) { + scriptLoadedCallbacks[url] = []; + } + + scriptLoadedCallbacks[url].push({ + success: success, + failure: failure, + scope: scope || this + }); + } + }; + + this.remove = function(url) { + delete states[url]; + delete scriptLoadedCallbacks[url]; + }; + + /** + * Starts the loading of the queue. + * + * @method loadQueue + * @param {function} success Optional callback to execute when all queued items are loaded. + * @param {function} failure Optional callback to execute when queued items failed to load. + * @param {Object} scope Optional scope to execute the callback in. + */ + this.loadQueue = function(success, scope, failure) { + this.loadScripts(queue, success, scope, failure); + }; + + /** + * Loads the specified queue of files and executes the callback ones they are loaded. + * This method is generally not used outside this class but it might be useful in some scenarios. + * + * @method loadScripts + * @param {Array} scripts Array of queue items to load. + * @param {function} callback Optional callback to execute when scripts is loaded successfully. + * @param {Object} scope Optional scope to execute callback in. + * @param {function} callback Optional callback to execute if scripts failed to load. + */ + this.loadScripts = function(scripts, success, scope, failure) { + var loadScripts, failures = []; + + function execCallbacks(name, url) { + // Execute URL callback functions + each(scriptLoadedCallbacks[url], function(callback) { + if (isFunction(callback[name])) { + callback[name].call(callback.scope); + } + }); + + scriptLoadedCallbacks[url] = undef; + } + + queueLoadedCallbacks.push({ + success: success, + failure: failure, + scope: scope || this + }); + + loadScripts = function() { + var loadingScripts = grep(scripts); + + // Current scripts has been handled + scripts.length = 0; + + // Load scripts that needs to be loaded + each(loadingScripts, function(url) { + // Script is already loaded then execute script callbacks directly + if (states[url] === LOADED) { + execCallbacks('success', url); + return; + } + + if (states[url] === FAILED) { + execCallbacks('failure', url); + return; + } + + // Is script not loading then start loading it + if (states[url] !== LOADING) { + states[url] = LOADING; + loading++; + + loadScript(url, function() { + states[url] = LOADED; + loading--; + + execCallbacks('success', url); + + // Load more scripts if they where added by the recently loaded script + loadScripts(); + }, function() { + states[url] = FAILED; + loading--; + + failures.push(url); + execCallbacks('failure', url); + + // Load more scripts if they where added by the recently loaded script + loadScripts(); + }); + } + }); + + // No scripts are currently loading then execute all pending queue loaded callbacks + if (!loading) { + each(queueLoadedCallbacks, function(callback) { + if (failures.length === 0) { + if (isFunction(callback.success)) { + callback.success.call(callback.scope); + } + } else { + if (isFunction(callback.failure)) { + callback.failure.call(callback.scope, failures); + } + } + }); + + queueLoadedCallbacks.length = 0; + } + }; + + loadScripts(); + }; + } + + ScriptLoader.ScriptLoader = new ScriptLoader(); + + return ScriptLoader; + } + ); + + /** + * AddOnManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class handles the loading of themes/plugins or other add-ons and their language packs. + * + * @class tinymce.AddOnManager + */ + define( + 'tinymce.core.AddOnManager', [ + "tinymce.core.dom.ScriptLoader", + "tinymce.core.util.Tools" + ], + function(ScriptLoader, Tools) { + var each = Tools.each; + + function AddOnManager() { + var self = this; + + self.items = []; + self.urls = {}; + self.lookup = {}; + } + + AddOnManager.prototype = { + /** + * Returns the specified add on by the short name. + * + * @method get + * @param {String} name Add-on to look for. + * @return {tinymce.Theme/tinymce.Plugin} Theme or plugin add-on instance or undefined. + */ + get: function(name) { + if (this.lookup[name]) { + return this.lookup[name].instance; + } + + return undefined; + }, + + dependencies: function(name) { + var result; + + if (this.lookup[name]) { + result = this.lookup[name].dependencies; + } + + return result || []; + }, + + /** + * Loads a language pack for the specified add-on. + * + * @method requireLangPack + * @param {String} name Short name of the add-on. + * @param {String} languages Optional comma or space separated list of languages to check if it matches the name. + */ + requireLangPack: function(name, languages) { + var language = AddOnManager.language; + + if (language && AddOnManager.languageLoad !== false) { + if (languages) { + languages = ',' + languages + ','; + + // Load short form sv.js or long form sv_SE.js + if (languages.indexOf(',' + language.substr(0, 2) + ',') != -1) { + language = language.substr(0, 2); + } else if (languages.indexOf(',' + language + ',') == -1) { + return; + } + } + + ScriptLoader.ScriptLoader.add(this.urls[name] + '/langs/' + language + '.js'); + } + }, + + /** + * Adds a instance of the add-on by it's short name. + * + * @method add + * @param {String} id Short name/id for the add-on. + * @param {tinymce.Theme/tinymce.Plugin} addOn Theme or plugin to add. + * @return {tinymce.Theme/tinymce.Plugin} The same theme or plugin instance that got passed in. + * @example + * // Create a simple plugin + * tinymce.create('tinymce.plugins.TestPlugin', { + * TestPlugin: function(ed, url) { + * ed.on('click', function(e) { + * ed.windowManager.alert('Hello World!'); + * }); + * } + * }); + * + * // Register plugin using the add method + * tinymce.PluginManager.add('test', tinymce.plugins.TestPlugin); + * + * // Initialize TinyMCE + * tinymce.init({ + * ... + * plugins: '-test' // Init the plugin but don't try to load it + * }); + */ + add: function(id, addOn, dependencies) { + this.items.push(addOn); + this.lookup[id] = { + instance: addOn, + dependencies: dependencies + }; + + return addOn; + }, + + remove: function(name) { + delete this.urls[name]; + delete this.lookup[name]; + }, + + createUrl: function(baseUrl, dep) { + if (typeof dep === "object") { + return dep; + } + + return { + prefix: baseUrl.prefix, + resource: dep, + suffix: baseUrl.suffix + }; + }, + + /** + * Add a set of components that will make up the add-on. Using the url of the add-on name as the base url. + * This should be used in development mode. A new compressor/javascript munger process will ensure that the + * components are put together into the plugin.js file and compressed correctly. + * + * @method addComponents + * @param {String} pluginName name of the plugin to load scripts from (will be used to get the base url for the plugins). + * @param {Array} scripts Array containing the names of the scripts to load. + */ + addComponents: function(pluginName, scripts) { + var pluginUrl = this.urls[pluginName]; + + each(scripts, function(script) { + ScriptLoader.ScriptLoader.add(pluginUrl + "/" + script); + }); + }, + + /** + * Loads an add-on from a specific url. + * + * @method load + * @param {String} name Short name of the add-on that gets loaded. + * @param {String} addOnUrl URL to the add-on that will get loaded. + * @param {function} success Optional success callback to execute when an add-on is loaded. + * @param {Object} scope Optional scope to execute the callback in. + * @param {function} failure Optional failure callback to execute when an add-on failed to load. + * @example + * // Loads a plugin from an external URL + * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js'); + * + * // Initialize TinyMCE + * tinymce.init({ + * ... + * plugins: '-myplugin' // Don't try to load it again + * }); + */ + load: function(name, addOnUrl, success, scope, failure) { + var self = this, + url = addOnUrl; + + function loadDependencies() { + var dependencies = self.dependencies(name); + + each(dependencies, function(dep) { + var newUrl = self.createUrl(addOnUrl, dep); + + self.load(newUrl.resource, newUrl, undefined, undefined); + }); + + if (success) { + if (scope) { + success.call(scope); + } else { + success.call(ScriptLoader); + } + } + } + + if (self.urls[name]) { + return; + } + + if (typeof addOnUrl === "object") { + url = addOnUrl.prefix + addOnUrl.resource + addOnUrl.suffix; + } + + if (url.indexOf('/') !== 0 && url.indexOf('://') == -1) { + url = AddOnManager.baseURL + '/' + url; + } + + self.urls[name] = url.substring(0, url.lastIndexOf('/')); + + if (self.lookup[name]) { + loadDependencies(); + } else { + ScriptLoader.ScriptLoader.add(url, loadDependencies, scope, failure); + } + } + }; + + AddOnManager.PluginManager = new AddOnManager(); + AddOnManager.ThemeManager = new AddOnManager(); + + return AddOnManager; + } + ); + + /** + * TinyMCE theme class. + * + * @class tinymce.Theme + */ + + /** + * This method is responsible for rendering/generating the overall user interface with toolbars, buttons, iframe containers etc. + * + * @method renderUI + * @param {Object} obj Object parameter containing the targetNode DOM node that will be replaced visually with an editor instance. + * @return {Object} an object with items like iframeContainer, editorContainer, sizeContainer, deltaWidth, deltaHeight. + */ + + /** + * Plugin base class, this is a pseudo class that describes how a plugin is to be created for TinyMCE. The methods below are all optional. + * + * @class tinymce.Plugin + * @example + * tinymce.PluginManager.add('example', function(editor, url) { + * // Add a button that opens a window + * editor.addButton('example', { + * text: 'My button', + * icon: false, + * onclick: function() { + * // Open window + * editor.windowManager.open({ + * title: 'Example plugin', + * body: [ + * {type: 'textbox', name: 'title', label: 'Title'} + * ], + * onsubmit: function(e) { + * // Insert content when the window form is submitted + * editor.insertContent('Title: ' + e.data.title); + * } + * }); + * } + * }); + * + * // Adds a menu item to the tools menu + * editor.addMenuItem('example', { + * text: 'Example plugin', + * context: 'tools', + * onclick: function() { + * // Open window with a specific url + * editor.windowManager.open({ + * title: 'TinyMCE site', + * url: 'http://www.tinymce.com', + * width: 800, + * height: 600, + * buttons: [{ + * text: 'Close', + * onclick: 'close' + * }] + * }); + * } + * }); + * }); + */ + + /** + * NodeType.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Contains various node validation functions. + * + * @private + * @class tinymce.dom.NodeType + */ + define( + 'tinymce.core.dom.NodeType', [], + function() { + function isNodeType(type) { + return function(node) { + return !!node && node.nodeType == type; + }; + } + + var isElement = isNodeType(1); + + function matchNodeNames(names) { + names = names.toLowerCase().split(' '); + + return function(node) { + var i, name; + + if (node && node.nodeType) { + name = node.nodeName.toLowerCase(); + + for (i = 0; i < names.length; i++) { + if (name === names[i]) { + return true; + } + } + } + + return false; + }; + } + + function matchStyleValues(name, values) { + values = values.toLowerCase().split(' '); + + return function(node) { + var i, cssValue; + + if (isElement(node)) { + for (i = 0; i < values.length; i++) { + cssValue = node.ownerDocument.defaultView.getComputedStyle(node, null).getPropertyValue(name); + if (cssValue === values[i]) { + return true; + } + } + } + + return false; + }; + } + + function hasPropValue(propName, propValue) { + return function(node) { + return isElement(node) && node[propName] === propValue; + }; + } + + function hasAttribute(attrName, attrValue) { + return function(node) { + return isElement(node) && node.hasAttribute(attrName); + }; + } + + function hasAttributeValue(attrName, attrValue) { + return function(node) { + return isElement(node) && node.getAttribute(attrName) === attrValue; + }; + } + + function isBogus(node) { + return isElement(node) && node.hasAttribute('data-mce-bogus'); + } + + function hasContentEditableState(value) { + return function(node) { + if (isElement(node)) { + if (node.contentEditable === value) { + return true; + } + + if (node.getAttribute('data-mce-contenteditable') === value) { + return true; + } + } + + return false; + }; + } + + return { + isText: isNodeType(3), + isElement: isElement, + isComment: isNodeType(8), + isBr: matchNodeNames('br'), + isContentEditableTrue: hasContentEditableState('true'), + isContentEditableFalse: hasContentEditableState('false'), + matchNodeNames: matchNodeNames, + hasPropValue: hasPropValue, + hasAttribute: hasAttribute, + hasAttributeValue: hasAttributeValue, + matchStyleValues: matchStyleValues, + isBogus: isBogus + }; + } + ); + /** + * Zwsp.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Utility functions for working with zero width space + * characters used as character containers etc. + * + * @private + * @class tinymce.text.Zwsp + * @example + * var isZwsp = Zwsp.isZwsp('\uFEFF'); + * var abc = Zwsp.trim('a\uFEFFc'); + */ + define( + 'tinymce.core.text.Zwsp', [], + function() { + // This is technically not a ZWSP but a ZWNBSP or a BYTE ORDER MARK it used to be a ZWSP + var ZWSP = '\uFEFF'; + + var isZwsp = function(chr) { + return chr === ZWSP; + }; + + var trim = function(text) { + return text.replace(new RegExp(ZWSP, 'g'), ''); + }; + + return { + isZwsp: isZwsp, + ZWSP: ZWSP, + trim: trim + }; + } + ); + /** + * CaretContainer.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module handles caret containers. A caret container is a node that + * holds the caret for positional purposes. + * + * @private + * @class tinymce.caret.CaretContainer + */ + define( + 'tinymce.core.caret.CaretContainer', [ + "tinymce.core.dom.NodeType", + "tinymce.core.text.Zwsp" + ], + function(NodeType, Zwsp) { + var isElement = NodeType.isElement, + isText = NodeType.isText; + + function isCaretContainerBlock(node) { + if (isText(node)) { + node = node.parentNode; + } + + return isElement(node) && node.hasAttribute('data-mce-caret'); + } + + function isCaretContainerInline(node) { + return isText(node) && Zwsp.isZwsp(node.data); + } + + function isCaretContainer(node) { + return isCaretContainerBlock(node) || isCaretContainerInline(node); + } + + var hasContent = function(node) { + return node.firstChild !== node.lastChild || !NodeType.isBr(node.firstChild); + }; + + function insertInline(node, before) { + var doc, sibling, textNode, parentNode; + + doc = node.ownerDocument; + textNode = doc.createTextNode(Zwsp.ZWSP); + parentNode = node.parentNode; + + if (!before) { + sibling = node.nextSibling; + if (isText(sibling)) { + if (isCaretContainer(sibling)) { + return sibling; + } + + if (startsWithCaretContainer(sibling)) { + sibling.splitText(1); + return sibling; + } + } + + if (node.nextSibling) { + parentNode.insertBefore(textNode, node.nextSibling); + } else { + parentNode.appendChild(textNode); + } + } else { + sibling = node.previousSibling; + if (isText(sibling)) { + if (isCaretContainer(sibling)) { + return sibling; + } + + if (endsWithCaretContainer(sibling)) { + return sibling.splitText(sibling.data.length - 1); + } + } + + parentNode.insertBefore(textNode, node); + } + + return textNode; + } + + var prependInline = function(node) { + if (NodeType.isText(node)) { + var data = node.data; + if (data.length > 0 && data.charAt(0) !== Zwsp.ZWSP) { + node.insertData(0, Zwsp.ZWSP); + } + return node; + } else { + return null; + } + }; + + var appendInline = function(node) { + if (NodeType.isText(node)) { + var data = node.data; + if (data.length > 0 && data.charAt(data.length - 1) !== Zwsp.ZWSP) { + node.insertData(data.length, Zwsp.ZWSP); + } + return node; + } else { + return null; + } + }; + + var isBeforeInline = function(pos) { + return pos && NodeType.isText(pos.container()) && pos.container().data.charAt(pos.offset()) === Zwsp.ZWSP; + }; + + var isAfterInline = function(pos) { + return pos && NodeType.isText(pos.container()) && pos.container().data.charAt(pos.offset() - 1) === Zwsp.ZWSP; + }; + + function createBogusBr() { + var br = document.createElement('br'); + br.setAttribute('data-mce-bogus', '1'); + return br; + } + + function insertBlock(blockName, node, before) { + var doc, blockNode, parentNode; + + doc = node.ownerDocument; + blockNode = doc.createElement(blockName); + blockNode.setAttribute('data-mce-caret', before ? 'before' : 'after'); + blockNode.setAttribute('data-mce-bogus', 'all'); + blockNode.appendChild(createBogusBr()); + parentNode = node.parentNode; + + if (!before) { + if (node.nextSibling) { + parentNode.insertBefore(blockNode, node.nextSibling); + } else { + parentNode.appendChild(blockNode); + } + } else { + parentNode.insertBefore(blockNode, node); + } + + return blockNode; + } + + function startsWithCaretContainer(node) { + return isText(node) && node.data[0] == Zwsp.ZWSP; + } + + function endsWithCaretContainer(node) { + return isText(node) && node.data[node.data.length - 1] == Zwsp.ZWSP; + } + + function trimBogusBr(elm) { + var brs = elm.getElementsByTagName('br'); + var lastBr = brs[brs.length - 1]; + if (NodeType.isBogus(lastBr)) { + lastBr.parentNode.removeChild(lastBr); + } + } + + function showCaretContainerBlock(caretContainer) { + if (caretContainer && caretContainer.hasAttribute('data-mce-caret')) { + trimBogusBr(caretContainer); + caretContainer.removeAttribute('data-mce-caret'); + caretContainer.removeAttribute('data-mce-bogus'); + caretContainer.removeAttribute('style'); + caretContainer.removeAttribute('_moz_abspos'); + return caretContainer; + } + + return null; + } + + return { + isCaretContainer: isCaretContainer, + isCaretContainerBlock: isCaretContainerBlock, + isCaretContainerInline: isCaretContainerInline, + showCaretContainerBlock: showCaretContainerBlock, + insertInline: insertInline, + prependInline: prependInline, + appendInline: appendInline, + isBeforeInline: isBeforeInline, + isAfterInline: isAfterInline, + insertBlock: insertBlock, + hasContent: hasContent, + startsWithCaretContainer: startsWithCaretContainer, + endsWithCaretContainer: endsWithCaretContainer + }; + } + ); + /** + * RangeUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class contains a few utility methods for ranges. + * + * @class tinymce.dom.RangeUtils + */ + define( + 'tinymce.core.dom.RangeUtils', [ + "tinymce.core.util.Tools", + "tinymce.core.dom.TreeWalker", + "tinymce.core.dom.NodeType", + "tinymce.core.dom.Range", + "tinymce.core.caret.CaretContainer" + ], + function(Tools, TreeWalker, NodeType, Range, CaretContainer) { + var each = Tools.each, + isContentEditableTrue = NodeType.isContentEditableTrue, + isContentEditableFalse = NodeType.isContentEditableFalse, + isCaretContainer = CaretContainer.isCaretContainer; + + function hasCeProperty(node) { + return isContentEditableTrue(node) || isContentEditableFalse(node); + } + + function getEndChild(container, index) { + var childNodes = container.childNodes; + + index--; + + if (index > childNodes.length - 1) { + index = childNodes.length - 1; + } else if (index < 0) { + index = 0; + } + + return childNodes[index] || container; + } + + function findParent(node, rootNode, predicate) { + while (node && node !== rootNode) { + if (predicate(node)) { + return node; + } + + node = node.parentNode; + } + + return null; + } + + function hasParent(node, rootNode, predicate) { + return findParent(node, rootNode, predicate) !== null; + } + + function hasParentWithName(node, rootNode, name) { + return hasParent(node, rootNode, function(node) { + return node.nodeName === name; + }); + } + + function isFormatterCaret(node) { + return node.id === '_mce_caret'; + } + + function isCeFalseCaretContainer(node, rootNode) { + return isCaretContainer(node) && hasParent(node, rootNode, isFormatterCaret) === false; + } + + function RangeUtils(dom) { + /** + * Walks the specified range like object and executes the callback for each sibling collection it finds. + * + * @private + * @method walk + * @param {Object} rng Range like object. + * @param {function} callback Callback function to execute for each sibling collection. + */ + this.walk = function(rng, callback) { + var startContainer = rng.startContainer, + startOffset = rng.startOffset, + endContainer = rng.endContainer, + endOffset = rng.endOffset, + ancestor, startPoint, + endPoint, node, parent, siblings, nodes; + + // Handle table cell selection the table plugin enables + // you to fake select table cells and perform formatting actions on them + nodes = dom.select('td[data-mce-selected],th[data-mce-selected]'); + if (nodes.length > 0) { + each(nodes, function(node) { + callback([node]); + }); + + return; + } + + /** + * Excludes start/end text node if they are out side the range + * + * @private + * @param {Array} nodes Nodes to exclude items from. + * @return {Array} Array with nodes excluding the start/end container if needed. + */ + function exclude(nodes) { + var node; + + // First node is excluded + node = nodes[0]; + if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) { + nodes.splice(0, 1); + } + + // Last node is excluded + node = nodes[nodes.length - 1]; + if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) { + nodes.splice(nodes.length - 1, 1); + } + + return nodes; + } + + /** + * Collects siblings + * + * @private + * @param {Node} node Node to collect siblings from. + * @param {String} name Name of the sibling to check for. + * @param {Node} endNode + * @return {Array} Array of collected siblings. + */ + function collectSiblings(node, name, endNode) { + var siblings = []; + + for (; node && node != endNode; node = node[name]) { + siblings.push(node); + } + + return siblings; + } + + /** + * Find an end point this is the node just before the common ancestor root. + * + * @private + * @param {Node} node Node to start at. + * @param {Node} root Root/ancestor element to stop just before. + * @return {Node} Node just before the root element. + */ + function findEndPoint(node, root) { + do { + if (node.parentNode == root) { + return node; + } + + node = node.parentNode; + } while (node); + } + + function walkBoundary(startNode, endNode, next) { + var siblingName = next ? 'nextSibling' : 'previousSibling'; + + for (node = startNode, parent = node.parentNode; node && node != endNode; node = parent) { + parent = node.parentNode; + siblings = collectSiblings(node == startNode ? node : node[siblingName], siblingName); + + if (siblings.length) { + if (!next) { + siblings.reverse(); + } + + callback(exclude(siblings)); + } + } + } + + // If index based start position then resolve it + if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) { + startContainer = startContainer.childNodes[startOffset]; + } + + // If index based end position then resolve it + if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) { + endContainer = getEndChild(endContainer, endOffset); + } + + // Same container + if (startContainer == endContainer) { + return callback(exclude([startContainer])); + } + + // Find common ancestor and end points + ancestor = dom.findCommonAncestor(startContainer, endContainer); + + // Process left side + for (node = startContainer; node; node = node.parentNode) { + if (node === endContainer) { + return walkBoundary(startContainer, ancestor, true); + } + + if (node === ancestor) { + break; + } + } + + // Process right side + for (node = endContainer; node; node = node.parentNode) { + if (node === startContainer) { + return walkBoundary(endContainer, ancestor); + } + + if (node === ancestor) { + break; + } + } + + // Find start/end point + startPoint = findEndPoint(startContainer, ancestor) || startContainer; + endPoint = findEndPoint(endContainer, ancestor) || endContainer; + + // Walk left leaf + walkBoundary(startContainer, startPoint, true); + + // Walk the middle from start to end point + siblings = collectSiblings( + startPoint == startContainer ? startPoint : startPoint.nextSibling, + 'nextSibling', + endPoint == endContainer ? endPoint.nextSibling : endPoint + ); + + if (siblings.length) { + callback(exclude(siblings)); + } + + // Walk right leaf + walkBoundary(endContainer, endPoint); + }; + + /** + * Splits the specified range at it's start/end points. + * + * @private + * @param {Range/RangeObject} rng Range to split. + * @return {Object} Range position object. + */ + this.split = function(rng) { + var startContainer = rng.startContainer, + startOffset = rng.startOffset, + endContainer = rng.endContainer, + endOffset = rng.endOffset; + + function splitText(node, offset) { + return node.splitText(offset); + } + + // Handle single text node + if (startContainer == endContainer && startContainer.nodeType == 3) { + if (startOffset > 0 && startOffset < startContainer.nodeValue.length) { + endContainer = splitText(startContainer, startOffset); + startContainer = endContainer.previousSibling; + + if (endOffset > startOffset) { + endOffset = endOffset - startOffset; + startContainer = endContainer = splitText(endContainer, endOffset).previousSibling; + endOffset = endContainer.nodeValue.length; + startOffset = 0; + } else { + endOffset = 0; + } + } + } else { + // Split startContainer text node if needed + if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) { + startContainer = splitText(startContainer, startOffset); + startOffset = 0; + } + + // Split endContainer text node if needed + if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) { + endContainer = splitText(endContainer, endOffset).previousSibling; + endOffset = endContainer.nodeValue.length; + } + } + + return { + startContainer: startContainer, + startOffset: startOffset, + endContainer: endContainer, + endOffset: endOffset + }; + }; + + /** + * Normalizes the specified range by finding the closest best suitable caret location. + * + * @private + * @param {Range} rng Range to normalize. + * @return {Boolean} True/false if the specified range was normalized or not. + */ + this.normalize = function(rng) { + var normalized = false, + collapsed; + + function normalizeEndPoint(start) { + var container, offset, walker, body = dom.getRoot(), + node, nonEmptyElementsMap; + var directionLeft, isAfterNode; + + function isTableCell(node) { + return node && /^(TD|TH|CAPTION)$/.test(node.nodeName); + } + + function hasBrBeforeAfter(node, left) { + var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body); + + while ((node = walker[left ? 'prev' : 'next']())) { + if (node.nodeName === "BR") { + return true; + } + } + } + + function hasContentEditableFalseParent(node) { + while (node && node != body) { + if (isContentEditableFalse(node)) { + return true; + } + + node = node.parentNode; + } + + return false; + } + + function isPrevNode(node, name) { + return node.previousSibling && node.previousSibling.nodeName == name; + } + + // Walks the dom left/right to find a suitable text node to move the endpoint into + // It will only walk within the current parent block or body and will stop if it hits a block or a BR/IMG + function findTextNodeRelative(left, startNode) { + var walker, lastInlineElement, parentBlockContainer; + + startNode = startNode || container; + parentBlockContainer = dom.getParent(startNode.parentNode, dom.isBlock) || body; + + // Lean left before the BR element if it's the only BR within a block element. Gecko bug: #6680 + // This: <p><br>|</p> becomes <p>|<br></p> + if (left && startNode.nodeName == 'BR' && isAfterNode && dom.isEmpty(parentBlockContainer)) { + container = startNode.parentNode; + offset = dom.nodeIndex(startNode); + normalized = true; + return; + } + + // Walk left until we hit a text node we can move to or a block/br/img + walker = new TreeWalker(startNode, parentBlockContainer); + while ((node = walker[left ? 'prev' : 'next']())) { + // Break if we hit a non content editable node + if (dom.getContentEditableParent(node) === "false" || isCeFalseCaretContainer(node, dom.getRoot())) { + return; + } + + // Found text node that has a length + if (node.nodeType === 3 && node.nodeValue.length > 0) { + if (hasParentWithName(node, body, 'A') === false) { + container = node; + offset = left ? node.nodeValue.length : 0; + normalized = true; + } + + return; + } + + // Break if we find a block or a BR/IMG/INPUT etc + if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) { + return; + } + + lastInlineElement = node; + } + + // Only fetch the last inline element when in caret mode for now + if (collapsed && lastInlineElement) { + container = lastInlineElement; + normalized = true; + offset = 0; + } + } + + container = rng[(start ? 'start' : 'end') + 'Container']; + offset = rng[(start ? 'start' : 'end') + 'Offset']; + isAfterNode = container.nodeType == 1 && offset === container.childNodes.length; + nonEmptyElementsMap = dom.schema.getNonEmptyElements(); + directionLeft = start; + + if (isCaretContainer(container)) { + return; + } + + if (container.nodeType == 1 && offset > container.childNodes.length - 1) { + directionLeft = false; + } + + // If the container is a document move it to the body element + if (container.nodeType === 9) { + container = dom.getRoot(); + offset = 0; + } + + // If the container is body try move it into the closest text node or position + if (container === body) { + // If start is before/after a image, table etc + if (directionLeft) { + node = container.childNodes[offset > 0 ? offset - 1 : 0]; + if (node) { + if (isCaretContainer(node)) { + return; + } + + if (nonEmptyElementsMap[node.nodeName] || node.nodeName == "TABLE") { + return; + } + } + } + + // Resolve the index + if (container.hasChildNodes()) { + offset = Math.min(!directionLeft && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1); + container = container.childNodes[offset]; + offset = 0; + + // Don't normalize non collapsed selections like <p>[a</p><table></table>] + if (!collapsed && container === body.lastChild && container.nodeName === 'TABLE') { + return; + } + + if (hasContentEditableFalseParent(container) || isCaretContainer(container)) { + return; + } + + // Don't walk into elements that doesn't have any child nodes like a IMG + if (container.hasChildNodes() && !/TABLE/.test(container.nodeName)) { + // Walk the DOM to find a text node to place the caret at or a BR + node = container; + walker = new TreeWalker(container, body); + + do { + if (isContentEditableFalse(node) || isCaretContainer(node)) { + normalized = false; + break; + } + + // Found a text node use that position + if (node.nodeType === 3 && node.nodeValue.length > 0) { + offset = directionLeft ? 0 : node.nodeValue.length; + container = node; + normalized = true; + break; + } + + // Found a BR/IMG element that we can place the caret before + if (nonEmptyElementsMap[node.nodeName.toLowerCase()] && !isTableCell(node)) { + offset = dom.nodeIndex(node); + container = node.parentNode; + + // Put caret after image when moving the end point + if (node.nodeName == "IMG" && !directionLeft) { + offset++; + } + + normalized = true; + break; + } + } while ((node = (directionLeft ? walker.next() : walker.prev()))); + } + } + } + + // Lean the caret to the left if possible + if (collapsed) { + // So this: <b>x</b><i>|x</i> + // Becomes: <b>x|</b><i>x</i> + // Seems that only gecko has issues with this + if (container.nodeType === 3 && offset === 0) { + findTextNodeRelative(true); + } + + // Lean left into empty inline elements when the caret is before a BR + // So this: <i><b></b><i>|<br></i> + // Becomes: <i><b>|</b><i><br></i> + // Seems that only gecko has issues with this. + // Special edge case for <p><a>x</a>|<br></p> since we don't want <p><a>x|</a><br></p> + if (container.nodeType === 1) { + node = container.childNodes[offset]; + + // Offset is after the containers last child + // then use the previous child for normalization + if (!node) { + node = container.childNodes[offset - 1]; + } + + if (node && node.nodeName === 'BR' && !isPrevNode(node, 'A') && + !hasBrBeforeAfter(node) && !hasBrBeforeAfter(node, true)) { + findTextNodeRelative(true, node); + } + } + } + + // Lean the start of the selection right if possible + // So this: x[<b>x]</b> + // Becomes: x<b>[x]</b> + if (directionLeft && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) { + findTextNodeRelative(false); + } + + // Set endpoint if it was normalized + if (normalized) { + rng['set' + (start ? 'Start' : 'End')](container, offset); + } + } + + collapsed = rng.collapsed; + + normalizeEndPoint(true); + + if (!collapsed) { + normalizeEndPoint(); + } + + // If it was collapsed then make sure it still is + if (normalized && collapsed) { + rng.collapse(true); + } + + return normalized; + }; + } + + /** + * Compares two ranges and checks if they are equal. + * + * @static + * @method compareRanges + * @param {DOMRange} rng1 First range to compare. + * @param {DOMRange} rng2 First range to compare. + * @return {Boolean} true/false if the ranges are equal. + */ + RangeUtils.compareRanges = function(rng1, rng2) { + if (rng1 && rng2) { + // Compare native IE ranges + if (rng1.item || rng1.duplicate) { + // Both are control ranges and the selected element matches + if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0)) { + return true; + } + + // Both are text ranges and the range matches + if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) { + return true; + } + } else { + // Compare w3c ranges + return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset; + } + } + + return false; + }; + + /** + * Finds the closest selection rect tries to get the range from that. + */ + function findClosestIeRange(clientX, clientY, doc) { + var element, rng, rects; + + element = doc.elementFromPoint(clientX, clientY); + rng = doc.body.createTextRange(); + + if (!element || element.tagName == 'HTML') { + element = doc.body; + } + + rng.moveToElementText(element); + rects = Tools.toArray(rng.getClientRects()); + + rects = rects.sort(function(a, b) { + a = Math.abs(Math.max(a.top - clientY, a.bottom - clientY)); + b = Math.abs(Math.max(b.top - clientY, b.bottom - clientY)); + + return a - b; + }); + + if (rects.length > 0) { + clientY = (rects[0].bottom + rects[0].top) / 2; + + try { + rng.moveToPoint(clientX, clientY); + rng.collapse(true); + + return rng; + } catch (ex) { + // At least we tried + } + } + + return null; + } + + function moveOutOfContentEditableFalse(rng, rootNode) { + var parentElement = rng && rng.parentElement ? rng.parentElement() : null; + return isContentEditableFalse(findParent(parentElement, rootNode, hasCeProperty)) ? null : rng; + } + + /** + * Gets the caret range for the given x/y location. + * + * @static + * @method getCaretRangeFromPoint + * @param {Number} clientX X coordinate for range + * @param {Number} clientY Y coordinate for range + * @param {Document} doc Document that x/y are relative to + * @returns {Range} caret range + */ + RangeUtils.getCaretRangeFromPoint = function(clientX, clientY, doc) { + var rng, point; + + if (doc.caretPositionFromPoint) { + point = doc.caretPositionFromPoint(clientX, clientY); + rng = doc.createRange(); + rng.setStart(point.offsetNode, point.offset); + rng.collapse(true); + } else if (doc.caretRangeFromPoint) { + rng = doc.caretRangeFromPoint(clientX, clientY); + } else if (doc.body.createTextRange) { + rng = doc.body.createTextRange(); + + try { + rng.moveToPoint(clientX, clientY); + rng.collapse(true); + } catch (ex) { + rng = findClosestIeRange(clientX, clientY, doc); + } + + return moveOutOfContentEditableFalse(rng, doc.body); + } + + return rng; + }; + + RangeUtils.getSelectedNode = function(range) { + var startContainer = range.startContainer, + startOffset = range.startOffset; + + if (startContainer.hasChildNodes() && range.endOffset == startOffset + 1) { + return startContainer.childNodes[startOffset]; + } + + return null; + }; + + RangeUtils.getNode = function(container, offset) { + if (container.nodeType == 1 && container.hasChildNodes()) { + if (offset >= container.childNodes.length) { + offset = container.childNodes.length - 1; + } + + container = container.childNodes[offset]; + } + + return container; + }; + + return RangeUtils; + } + ); + + /** + * Node.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class is a minimalistic implementation of a DOM like node used by the DomParser class. + * + * @example + * var node = new tinymce.html.Node('strong', 1); + * someRoot.append(node); + * + * @class tinymce.html.Node + * @version 3.4 + */ + define( + 'tinymce.core.html.Node', [], + function() { + var whiteSpaceRegExp = /^[ \t\r\n]*$/; + var typeLookup = { + '#text': 3, + '#comment': 8, + '#cdata': 4, + '#pi': 7, + '#doctype': 10, + '#document-fragment': 11 + }; + + // Walks the tree left/right + function walk(node, rootNode, prev) { + var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', + siblingName = prev ? 'prev' : 'next'; + + // Walk into nodes if it has a start + if (node[startName]) { + return node[startName]; + } + + // Return the sibling if it has one + if (node !== rootNode) { + sibling = node[siblingName]; + + if (sibling) { + return sibling; + } + + // Walk up the parents to look for siblings + for (parent = node.parent; parent && parent !== rootNode; parent = parent.parent) { + sibling = parent[siblingName]; + + if (sibling) { + return sibling; + } + } + } + } + + /** + * Constructs a new Node instance. + * + * @constructor + * @method Node + * @param {String} name Name of the node type. + * @param {Number} type Numeric type representing the node. + */ + function Node(name, type) { + this.name = name; + this.type = type; + + if (type === 1) { + this.attributes = []; + this.attributes.map = {}; + } + } + + Node.prototype = { + /** + * Replaces the current node with the specified one. + * + * @example + * someNode.replace(someNewNode); + * + * @method replace + * @param {tinymce.html.Node} node Node to replace the current node with. + * @return {tinymce.html.Node} The old node that got replaced. + */ + replace: function(node) { + var self = this; + + if (node.parent) { + node.remove(); + } + + self.insert(node, self); + self.remove(); + + return self; + }, + + /** + * Gets/sets or removes an attribute by name. + * + * @example + * someNode.attr("name", "value"); // Sets an attribute + * console.log(someNode.attr("name")); // Gets an attribute + * someNode.attr("name", null); // Removes an attribute + * + * @method attr + * @param {String} name Attribute name to set or get. + * @param {String} value Optional value to set. + * @return {String/tinymce.html.Node} String or undefined on a get operation or the current node on a set operation. + */ + attr: function(name, value) { + var self = this, + attrs, i, undef; + + if (typeof name !== "string") { + for (i in name) { + self.attr(i, name[i]); + } + + return self; + } + + if ((attrs = self.attributes)) { + if (value !== undef) { + // Remove attribute + if (value === null) { + if (name in attrs.map) { + delete attrs.map[name]; + + i = attrs.length; + while (i--) { + if (attrs[i].name === name) { + attrs = attrs.splice(i, 1); + return self; + } + } + } + + return self; + } + + // Set attribute + if (name in attrs.map) { + // Set attribute + i = attrs.length; + while (i--) { + if (attrs[i].name === name) { + attrs[i].value = value; + break; + } + } + } else { + attrs.push({ + name: name, + value: value + }); + } + + attrs.map[name] = value; + + return self; + } + + return attrs.map[name]; + } + }, + + /** + * Does a shallow clones the node into a new node. It will also exclude id attributes since + * there should only be one id per document. + * + * @example + * var clonedNode = node.clone(); + * + * @method clone + * @return {tinymce.html.Node} New copy of the original node. + */ + clone: function() { + var self = this, + clone = new Node(self.name, self.type), + i, l, selfAttrs, selfAttr, cloneAttrs; + + // Clone element attributes + if ((selfAttrs = self.attributes)) { + cloneAttrs = []; + cloneAttrs.map = {}; + + for (i = 0, l = selfAttrs.length; i < l; i++) { + selfAttr = selfAttrs[i]; + + // Clone everything except id + if (selfAttr.name !== 'id') { + cloneAttrs[cloneAttrs.length] = { + name: selfAttr.name, + value: selfAttr.value + }; + cloneAttrs.map[selfAttr.name] = selfAttr.value; + } + } + + clone.attributes = cloneAttrs; + } + + clone.value = self.value; + clone.shortEnded = self.shortEnded; + + return clone; + }, + + /** + * Wraps the node in in another node. + * + * @example + * node.wrap(wrapperNode); + * + * @method wrap + */ + wrap: function(wrapper) { + var self = this; + + self.parent.insert(wrapper, self); + wrapper.append(self); + + return self; + }, + + /** + * Unwraps the node in other words it removes the node but keeps the children. + * + * @example + * node.unwrap(); + * + * @method unwrap + */ + unwrap: function() { + var self = this, + node, next; + + for (node = self.firstChild; node;) { + next = node.next; + self.insert(node, self, true); + node = next; + } + + self.remove(); + }, + + /** + * Removes the node from it's parent. + * + * @example + * node.remove(); + * + * @method remove + * @return {tinymce.html.Node} Current node that got removed. + */ + remove: function() { + var self = this, + parent = self.parent, + next = self.next, + prev = self.prev; + + if (parent) { + if (parent.firstChild === self) { + parent.firstChild = next; + + if (next) { + next.prev = null; + } + } else { + prev.next = next; + } + + if (parent.lastChild === self) { + parent.lastChild = prev; + + if (prev) { + prev.next = null; + } + } else { + next.prev = prev; + } + + self.parent = self.next = self.prev = null; + } + + return self; + }, + + /** + * Appends a new node as a child of the current node. + * + * @example + * node.append(someNode); + * + * @method append + * @param {tinymce.html.Node} node Node to append as a child of the current one. + * @return {tinymce.html.Node} The node that got appended. + */ + append: function(node) { + var self = this, + last; + + if (node.parent) { + node.remove(); + } + + last = self.lastChild; + if (last) { + last.next = node; + node.prev = last; + self.lastChild = node; + } else { + self.lastChild = self.firstChild = node; + } + + node.parent = self; + + return node; + }, + + /** + * Inserts a node at a specific position as a child of the current node. + * + * @example + * parentNode.insert(newChildNode, oldChildNode); + * + * @method insert + * @param {tinymce.html.Node} node Node to insert as a child of the current node. + * @param {tinymce.html.Node} refNode Reference node to set node before/after. + * @param {Boolean} before Optional state to insert the node before the reference node. + * @return {tinymce.html.Node} The node that got inserted. + */ + insert: function(node, refNode, before) { + var parent; + + if (node.parent) { + node.remove(); + } + + parent = refNode.parent || this; + + if (before) { + if (refNode === parent.firstChild) { + parent.firstChild = node; + } else { + refNode.prev.next = node; + } + + node.prev = refNode.prev; + node.next = refNode; + refNode.prev = node; + } else { + if (refNode === parent.lastChild) { + parent.lastChild = node; + } else { + refNode.next.prev = node; + } + + node.next = refNode.next; + node.prev = refNode; + refNode.next = node; + } + + node.parent = parent; + + return node; + }, + + /** + * Get all children by name. + * + * @method getAll + * @param {String} name Name of the child nodes to collect. + * @return {Array} Array with child nodes matchin the specified name. + */ + getAll: function(name) { + var self = this, + node, collection = []; + + for (node = self.firstChild; node; node = walk(node, self)) { + if (node.name === name) { + collection.push(node); + } + } + + return collection; + }, + + /** + * Removes all children of the current node. + * + * @method empty + * @return {tinymce.html.Node} The current node that got cleared. + */ + empty: function() { + var self = this, + nodes, i, node; + + // Remove all children + if (self.firstChild) { + nodes = []; + + // Collect the children + for (node = self.firstChild; node; node = walk(node, self)) { + nodes.push(node); + } + + // Remove the children + i = nodes.length; + while (i--) { + node = nodes[i]; + node.parent = node.firstChild = node.lastChild = node.next = node.prev = null; + } + } + + self.firstChild = self.lastChild = null; + + return self; + }, + + /** + * Returns true/false if the node is to be considered empty or not. + * + * @example + * node.isEmpty({img: true}); + * @method isEmpty + * @param {Object} elements Name/value object with elements that are automatically treated as non empty elements. + * @param {Object} whitespace Name/value object with elements that are automatically treated whitespace preservables. + * @return {Boolean} true/false if the node is empty or not. + */ + isEmpty: function(elements, whitespace) { + var self = this, + node = self.firstChild, + i, name; + + whitespace = whitespace || {}; + + if (node) { + do { + if (node.type === 1) { + // Ignore bogus elements + if (node.attributes.map['data-mce-bogus']) { + continue; + } + + // Keep empty elements like <img /> + if (elements[node.name]) { + return false; + } + + // Keep bookmark nodes and name attribute like <a name="1"></a> + i = node.attributes.length; + while (i--) { + name = node.attributes[i].name; + if (name === "name" || name.indexOf('data-mce-bookmark') === 0) { + return false; + } + } + } + + // Keep comments + if (node.type === 8) { + return false; + } + + // Keep non whitespace text nodes + if (node.type === 3 && !whiteSpaceRegExp.test(node.value)) { + return false; + } + + // Keep whitespace preserve elements + if (node.type === 3 && node.parent && whitespace[node.parent.name] && whiteSpaceRegExp.test(node.value)) { + return false; + } + } while ((node = walk(node, self))); + } + + return true; + }, + + /** + * Walks to the next or previous node and returns that node or null if it wasn't found. + * + * @method walk + * @param {Boolean} prev Optional previous node state defaults to false. + * @return {tinymce.html.Node} Node that is next to or previous of the current node. + */ + walk: function(prev) { + return walk(this, null, prev); + } + }; + + /** + * Creates a node of a specific type. + * + * @static + * @method create + * @param {String} name Name of the node type to create for example "b" or "#text". + * @param {Object} attrs Name/value collection of attributes that will be applied to elements. + */ + Node.create = function(name, attrs) { + var node, attrName; + + // Create node + node = new Node(name, typeLookup[name] || 1); + + // Add attributes if needed + if (attrs) { + for (attrName in attrs) { + node.attr(attrName, attrs[attrName]); + } + } + + return node; + }; + + return Node; + } + ); + + /** + * SaxParser.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /*eslint max-depth:[2, 9] */ + + /** + * This class parses HTML code using pure JavaScript and executes various events for each item it finds. It will + * always execute the events in the right order for tag soup code like <b><p></b></p>. It will also remove elements + * and attributes that doesn't fit the schema if the validate setting is enabled. + * + * @example + * var parser = new tinymce.html.SaxParser({ + * validate: true, + * + * comment: function(text) { + * console.log('Comment:', text); + * }, + * + * cdata: function(text) { + * console.log('CDATA:', text); + * }, + * + * text: function(text, raw) { + * console.log('Text:', text, 'Raw:', raw); + * }, + * + * start: function(name, attrs, empty) { + * console.log('Start:', name, attrs, empty); + * }, + * + * end: function(name) { + * console.log('End:', name); + * }, + * + * pi: function(name, text) { + * console.log('PI:', name, text); + * }, + * + * doctype: function(text) { + * console.log('DocType:', text); + * } + * }, schema); + * @class tinymce.html.SaxParser + * @version 3.4 + */ + define( + 'tinymce.core.html.SaxParser', [ + "tinymce.core.html.Schema", + "tinymce.core.html.Entities", + "tinymce.core.util.Tools" + ], + function(Schema, Entities, Tools) { + var each = Tools.each; + + var isValidPrefixAttrName = function(name) { + return name.indexOf('data-') === 0 || name.indexOf('aria-') === 0; + }; + + var trimComments = function(text) { + return text.replace(/<!--|-->/g, ''); + }; + + /** + * Returns the index of the end tag for a specific start tag. This can be + * used to skip all children of a parent element from being processed. + * + * @private + * @method findEndTag + * @param {tinymce.html.Schema} schema Schema instance to use to match short ended elements. + * @param {String} html HTML string to find the end tag in. + * @param {Number} startIndex Indext to start searching at should be after the start tag. + * @return {Number} Index of the end tag. + */ + function findEndTag(schema, html, startIndex) { + var count = 1, + index, matches, tokenRegExp, shortEndedElements; + + shortEndedElements = schema.getShortEndedElements(); + tokenRegExp = /<([!?\/])?([A-Za-z0-9\-_\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g; + tokenRegExp.lastIndex = index = startIndex; + + while ((matches = tokenRegExp.exec(html))) { + index = tokenRegExp.lastIndex; + + if (matches[1] === '/') { // End element + count--; + } else if (!matches[1]) { // Start element + if (matches[2] in shortEndedElements) { + continue; + } + + count++; + } + + if (count === 0) { + break; + } + } + + return index; + } + + /** + * Constructs a new SaxParser instance. + * + * @constructor + * @method SaxParser + * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks. + * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing. + */ + function SaxParser(settings, schema) { + var self = this; + + function noop() {} + + settings = settings || {}; + self.schema = schema = schema || new Schema(); + + if (settings.fix_self_closing !== false) { + settings.fix_self_closing = true; + } + + // Add handler functions from settings and setup default handlers + each('comment cdata text start end pi doctype'.split(' '), function(name) { + if (name) { + self[name] = settings[name] || noop; + } + }); + + /** + * Parses the specified HTML string and executes the callbacks for each item it finds. + * + * @example + * new SaxParser({...}).parse('<b>text</b>'); + * @method parse + * @param {String} html Html string to sax parse. + */ + self.parse = function(html) { + var self = this, + matches, index = 0, + value, endRegExp, stack = [], + attrList, i, text, name; + var isInternalElement, removeInternalElements, shortEndedElements, fillAttrsMap, isShortEnded; + var validate, elementRule, isValidElement, attr, attribsValue, validAttributesMap, validAttributePatterns; + var attributesRequired, attributesDefault, attributesForced, processHtml; + var anyAttributesRequired, selfClosing, tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0; + var decode = Entities.decode, + fixSelfClosing, filteredUrlAttrs = Tools.makeMap('src,href,data,background,formaction,poster'); + var scriptUriRegExp = /((java|vb)script|mhtml):/i, + dataUriRegExp = /^data:/i; + + function processEndTag(name) { + var pos, i; + + // Find position of parent of the same type + pos = stack.length; + while (pos--) { + if (stack[pos].name === name) { + break; + } + } + + // Found parent + if (pos >= 0) { + // Close all the open elements + for (i = stack.length - 1; i >= pos; i--) { + name = stack[i]; + + if (name.valid) { + self.end(name.name); + } + } + + // Remove the open elements from the stack + stack.length = pos; + } + } + + function parseAttribute(match, name, value, val2, val3) { + var attrRule, i, trimRegExp = /[\s\u0000-\u001F]+/g; + + name = name.toLowerCase(); + value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute + + // Validate name and value pass through all data- attributes + if (validate && !isInternalElement && isValidPrefixAttrName(name) === false) { + attrRule = validAttributesMap[name]; + + // Find rule by pattern matching + if (!attrRule && validAttributePatterns) { + i = validAttributePatterns.length; + while (i--) { + attrRule = validAttributePatterns[i]; + if (attrRule.pattern.test(name)) { + break; + } + } + + // No rule matched + if (i === -1) { + attrRule = null; + } + } + + // No attribute rule found + if (!attrRule) { + return; + } + + // Validate value + if (attrRule.validValues && !(value in attrRule.validValues)) { + return; + } + } + + // Block any javascript: urls or non image data uris + if (filteredUrlAttrs[name] && !settings.allow_script_urls) { + var uri = value.replace(trimRegExp, ''); + + try { + // Might throw malformed URI sequence + uri = decodeURIComponent(uri); + } catch (ex) { + // Fallback to non UTF-8 decoder + uri = unescape(uri); + } + + if (scriptUriRegExp.test(uri)) { + return; + } + + if (!settings.allow_html_data_urls && dataUriRegExp.test(uri) && !/^data:image\//i.test(uri)) { + return; + } + } + + // Block data or event attributes on elements marked as internal + if (isInternalElement && (name in filteredUrlAttrs || name.indexOf('on') === 0)) { + return; + } + + // Add attribute to list and map + attrList.map[name] = value; + attrList.push({ + name: name, + value: value + }); + } + + // Precompile RegExps and map objects + tokenRegExp = new RegExp('<(?:' + + '(?:!--([\\w\\W]*?)-->)|' + // Comment + '(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA + '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE + '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI + '(?:\\/([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)>)|' + // End element + '(?:([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element + ')', 'g'); + + attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g; + + // Setup lookup tables for empty elements and boolean attributes + shortEndedElements = schema.getShortEndedElements(); + selfClosing = settings.self_closing_elements || schema.getSelfClosingElements(); + fillAttrsMap = schema.getBoolAttrs(); + validate = settings.validate; + removeInternalElements = settings.remove_internals; + fixSelfClosing = settings.fix_self_closing; + specialElements = schema.getSpecialElements(); + processHtml = html + '>'; + + while ((matches = tokenRegExp.exec(processHtml))) { // Adds and extra '>' to keep regexps from doing catastrofic backtracking on malformed html + // Text + if (index < matches.index) { + self.text(decode(html.substr(index, matches.index - index))); + } + + if ((value = matches[6])) { // End element + value = value.toLowerCase(); + + // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements + if (value.charAt(0) === ':') { + value = value.substr(1); + } + + processEndTag(value); + } else if ((value = matches[7])) { // Start element + // Did we consume the extra character then treat it as text + // This handles the case with html like this: "text a<b text" + if (matches.index + matches[0].length > html.length) { + self.text(decode(html.substr(matches.index))); + index = matches.index + matches[0].length; + continue; + } + + value = value.toLowerCase(); + + // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements + if (value.charAt(0) === ':') { + value = value.substr(1); + } + + isShortEnded = value in shortEndedElements; + + // Is self closing tag for example an <li> after an open <li> + if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value) { + processEndTag(value); + } + + // Validate element + if (!validate || (elementRule = schema.getElementRule(value))) { + isValidElement = true; + + // Grab attributes map and patters when validation is enabled + if (validate) { + validAttributesMap = elementRule.attributes; + validAttributePatterns = elementRule.attributePatterns; + } + + // Parse attributes + if ((attribsValue = matches[8])) { + isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element + + // If the element has internal attributes then remove it if we are told to do so + if (isInternalElement && removeInternalElements) { + isValidElement = false; + } + + attrList = []; + attrList.map = {}; + + attribsValue.replace(attrRegExp, parseAttribute); + } else { + attrList = []; + attrList.map = {}; + } + + // Process attributes if validation is enabled + if (validate && !isInternalElement) { + attributesRequired = elementRule.attributesRequired; + attributesDefault = elementRule.attributesDefault; + attributesForced = elementRule.attributesForced; + anyAttributesRequired = elementRule.removeEmptyAttrs; + + // Check if any attribute exists + if (anyAttributesRequired && !attrList.length) { + isValidElement = false; + } + + // Handle forced attributes + if (attributesForced) { + i = attributesForced.length; + while (i--) { + attr = attributesForced[i]; + name = attr.name; + attrValue = attr.value; + + if (attrValue === '{$uid}') { + attrValue = 'mce_' + idCount++; + } + + attrList.map[name] = attrValue; + attrList.push({ + name: name, + value: attrValue + }); + } + } + + // Handle default attributes + if (attributesDefault) { + i = attributesDefault.length; + while (i--) { + attr = attributesDefault[i]; + name = attr.name; + + if (!(name in attrList.map)) { + attrValue = attr.value; + + if (attrValue === '{$uid}') { + attrValue = 'mce_' + idCount++; + } + + attrList.map[name] = attrValue; + attrList.push({ + name: name, + value: attrValue + }); + } + } + } + + // Handle required attributes + if (attributesRequired) { + i = attributesRequired.length; + while (i--) { + if (attributesRequired[i] in attrList.map) { + break; + } + } + + // None of the required attributes where found + if (i === -1) { + isValidElement = false; + } + } + + // Invalidate element if it's marked as bogus + if ((attr = attrList.map['data-mce-bogus'])) { + if (attr === 'all') { + index = findEndTag(schema, html, tokenRegExp.lastIndex); + tokenRegExp.lastIndex = index; + continue; + } + + isValidElement = false; + } + } + + if (isValidElement) { + self.start(value, attrList, isShortEnded); + } + } else { + isValidElement = false; + } + + // Treat script, noscript and style a bit different since they may include code that looks like elements + if ((endRegExp = specialElements[value])) { + endRegExp.lastIndex = index = matches.index + matches[0].length; + + if ((matches = endRegExp.exec(html))) { + if (isValidElement) { + text = html.substr(index, matches.index - index); + } + + index = matches.index + matches[0].length; + } else { + text = html.substr(index); + index = html.length; + } + + if (isValidElement) { + if (text.length > 0) { + self.text(text, true); + } + + self.end(value); + } + + tokenRegExp.lastIndex = index; + continue; + } + + // Push value on to stack + if (!isShortEnded) { + if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1) { + stack.push({ + name: value, + valid: isValidElement + }); + } else if (isValidElement) { + self.end(value); + } + } + } else if ((value = matches[1])) { // Comment + // Padd comment value to avoid browsers from parsing invalid comments as HTML + if (value.charAt(0) === '>') { + value = ' ' + value; + } + + if (!settings.allow_conditional_comments && value.substr(0, 3).toLowerCase() === '[if') { + value = ' ' + value; + } + + self.comment(value); + } else if ((value = matches[2])) { // CDATA + self.cdata(trimComments(value)); + } else if ((value = matches[3])) { // DOCTYPE + self.doctype(value); + } else if ((value = matches[4])) { // PI + self.pi(value, matches[5]); + } + + index = matches.index + matches[0].length; + } + + // Text + if (index < html.length) { + self.text(decode(html.substr(index))); + } + + // Close any open elements + for (i = stack.length - 1; i >= 0; i--) { + value = stack[i]; + + if (value.valid) { + self.end(value.name); + } + } + }; + } + + SaxParser.findEndTag = findEndTag; + + return SaxParser; + } + ); + /** + * DomParser.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class parses HTML code into a DOM like structure of nodes it will remove redundant whitespace and make + * sure that the node tree is valid according to the specified schema. + * So for example: <p>a<p>b</p>c</p> will become <p>a</p><p>b</p><p>c</p> + * + * @example + * var parser = new tinymce.html.DomParser({validate: true}, schema); + * var rootNode = parser.parse('<h1>content</h1>'); + * + * @class tinymce.html.DomParser + * @version 3.4 + */ + define( + 'tinymce.core.html.DomParser', [ + "tinymce.core.html.Node", + "tinymce.core.html.Schema", + "tinymce.core.html.SaxParser", + "tinymce.core.util.Tools" + ], + function(Node, Schema, SaxParser, Tools) { + var makeMap = Tools.makeMap, + each = Tools.each, + explode = Tools.explode, + extend = Tools.extend; + + var paddEmptyNode = function(settings, node) { + if (settings.padd_empty_with_br) { + node.empty().append(new Node('br', '1')).shortEnded = true; + } else { + node.empty().append(new Node('#text', '3')).value = '\u00a0'; + } + }; + + var hasOnlyChild = function(node, name) { + return node && node.firstChild === node.lastChild && node.firstChild.name === name; + }; + + /** + * Constructs a new DomParser instance. + * + * @constructor + * @method DomParser + * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks. + * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing. + */ + return function(settings, schema) { + var self = this, + nodeFilters = {}, + attributeFilters = [], + matchedNodes = {}, + matchedAttributes = {}; + + settings = settings || {}; + settings.validate = "validate" in settings ? settings.validate : true; + settings.root_name = settings.root_name || 'body'; + self.schema = schema = schema || new Schema(); + + function fixInvalidChildren(nodes) { + var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i; + var nonEmptyElements, whitespaceElements, nonSplitableElements, textBlockElements, specialElements, sibling, nextNode; + + nonSplitableElements = makeMap('tr,td,th,tbody,thead,tfoot,table'); + nonEmptyElements = schema.getNonEmptyElements(); + whitespaceElements = schema.getWhiteSpaceElements(); + textBlockElements = schema.getTextBlockElements(); + specialElements = schema.getSpecialElements(); + + for (ni = 0; ni < nodes.length; ni++) { + node = nodes[ni]; + + // Already removed or fixed + if (!node.parent || node.fixed) { + continue; + } + + // If the invalid element is a text block and the text block is within a parent LI element + // Then unwrap the first text block and convert other sibling text blocks to LI elements similar to Word/Open Office + if (textBlockElements[node.name] && node.parent.name == 'li') { + // Move sibling text blocks after LI element + sibling = node.next; + while (sibling) { + if (textBlockElements[sibling.name]) { + sibling.name = 'li'; + sibling.fixed = true; + node.parent.insert(sibling, node.parent); + } else { + break; + } + + sibling = sibling.next; + } + + // Unwrap current text block + node.unwrap(node); + continue; + } + + // Get list of all parent nodes until we find a valid parent to stick the child into + parents = [node]; + for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) && + !nonSplitableElements[parent.name]; parent = parent.parent) { + parents.push(parent); + } + + // Found a suitable parent + if (parent && parents.length > 1) { + // Reverse the array since it makes looping easier + parents.reverse(); + + // Clone the related parent and insert that after the moved node + newParent = currentNode = self.filterNode(parents[0].clone()); + + // Start cloning and moving children on the left side of the target node + for (i = 0; i < parents.length - 1; i++) { + if (schema.isValidChild(currentNode.name, parents[i].name)) { + tempNode = self.filterNode(parents[i].clone()); + currentNode.append(tempNode); + } else { + tempNode = currentNode; + } + + for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1];) { + nextNode = childNode.next; + tempNode.append(childNode); + childNode = nextNode; + } + + currentNode = tempNode; + } + + if (!newParent.isEmpty(nonEmptyElements, whitespaceElements)) { + parent.insert(newParent, parents[0], true); + parent.insert(node, newParent); + } else { + parent.insert(node, parents[0], true); + } + + // Check if the element is empty by looking through it's contents and special treatment for <p><br /></p> + parent = parents[0]; + if (parent.isEmpty(nonEmptyElements, whitespaceElements) || hasOnlyChild(parent, 'br')) { + parent.empty().remove(); + } + } else if (node.parent) { + // If it's an LI try to find a UL/OL for it or wrap it + if (node.name === 'li') { + sibling = node.prev; + if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) { + sibling.append(node); + continue; + } + + sibling = node.next; + if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) { + sibling.insert(node, sibling.firstChild, true); + continue; + } + + node.wrap(self.filterNode(new Node('ul', 1))); + continue; + } + + // Try wrapping the element in a DIV + if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) { + node.wrap(self.filterNode(new Node('div', 1))); + } else { + // We failed wrapping it, then remove or unwrap it + if (specialElements[node.name]) { + node.empty().remove(); + } else { + node.unwrap(); + } + } + } + } + } + + /** + * Runs the specified node though the element and attributes filters. + * + * @method filterNode + * @param {tinymce.html.Node} Node the node to run filters on. + * @return {tinymce.html.Node} The passed in node. + */ + self.filterNode = function(node) { + var i, name, list; + + // Run element filters + if (name in nodeFilters) { + list = matchedNodes[name]; + + if (list) { + list.push(node); + } else { + matchedNodes[name] = [node]; + } + } + + // Run attribute filters + i = attributeFilters.length; + while (i--) { + name = attributeFilters[i].name; + + if (name in node.attributes.map) { + list = matchedAttributes[name]; + + if (list) { + list.push(node); + } else { + matchedAttributes[name] = [node]; + } + } + } + + return node; + }; + + /** + * Adds a node filter function to the parser, the parser will collect the specified nodes by name + * and then execute the callback ones it has finished parsing the document. + * + * @example + * parser.addNodeFilter('p,h1', function(nodes, name) { + * for (var i = 0; i < nodes.length; i++) { + * console.log(nodes[i].name); + * } + * }); + * @method addNodeFilter + * @method {String} name Comma separated list of nodes to collect. + * @param {function} callback Callback function to execute once it has collected nodes. + */ + self.addNodeFilter = function(name, callback) { + each(explode(name), function(name) { + var list = nodeFilters[name]; + + if (!list) { + nodeFilters[name] = list = []; + } + + list.push(callback); + }); + }; + + /** + * Adds a attribute filter function to the parser, the parser will collect nodes that has the specified attributes + * and then execute the callback ones it has finished parsing the document. + * + * @example + * parser.addAttributeFilter('src,href', function(nodes, name) { + * for (var i = 0; i < nodes.length; i++) { + * console.log(nodes[i].name); + * } + * }); + * @method addAttributeFilter + * @method {String} name Comma separated list of nodes to collect. + * @param {function} callback Callback function to execute once it has collected nodes. + */ + self.addAttributeFilter = function(name, callback) { + each(explode(name), function(name) { + var i; + + for (i = 0; i < attributeFilters.length; i++) { + if (attributeFilters[i].name === name) { + attributeFilters[i].callbacks.push(callback); + return; + } + } + + attributeFilters.push({ + name: name, + callbacks: [callback] + }); + }); + }; + + /** + * Parses the specified HTML string into a DOM like node tree and returns the result. + * + * @example + * var rootNode = new DomParser({...}).parse('<b>text</b>'); + * @method parse + * @param {String} html Html string to sax parse. + * @param {Object} args Optional args object that gets passed to all filter functions. + * @return {tinymce.html.Node} Root node containing the tree. + */ + self.parse = function(html, args) { + var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate; + var blockElements, startWhiteSpaceRegExp, invalidChildren = [], + isInWhiteSpacePreservedElement; + var endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements; + var children, nonEmptyElements, rootBlockName; + + args = args || {}; + matchedNodes = {}; + matchedAttributes = {}; + blockElements = extend(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements()); + nonEmptyElements = schema.getNonEmptyElements(); + children = schema.children; + validate = settings.validate; + rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block; + + whiteSpaceElements = schema.getWhiteSpaceElements(); + startWhiteSpaceRegExp = /^[ \t\r\n]+/; + endWhiteSpaceRegExp = /[ \t\r\n]+$/; + allWhiteSpaceRegExp = /[ \t\r\n]+/g; + isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/; + + function addRootBlocks() { + var node = rootNode.firstChild, + next, rootBlockNode; + + // Removes whitespace at beginning and end of block so: + // <p> x </p> -> <p>x</p> + function trim(rootBlockNode) { + if (rootBlockNode) { + node = rootBlockNode.firstChild; + if (node && node.type == 3) { + node.value = node.value.replace(startWhiteSpaceRegExp, ''); + } + + node = rootBlockNode.lastChild; + if (node && node.type == 3) { + node.value = node.value.replace(endWhiteSpaceRegExp, ''); + } + } + } + + // Check if rootBlock is valid within rootNode for example if P is valid in H1 if H1 is the contentEditabe root + if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) { + return; + } + + while (node) { + next = node.next; + + if (node.type == 3 || (node.type == 1 && node.name !== 'p' && + !blockElements[node.name] && !node.attr('data-mce-type'))) { + if (!rootBlockNode) { + // Create a new root block element + rootBlockNode = createNode(rootBlockName, 1); + rootBlockNode.attr(settings.forced_root_block_attrs); + rootNode.insert(rootBlockNode, node); + rootBlockNode.append(node); + } else { + rootBlockNode.append(node); + } + } else { + trim(rootBlockNode); + rootBlockNode = null; + } + + node = next; + } + + trim(rootBlockNode); + } + + function createNode(name, type) { + var node = new Node(name, type), + list; + + if (name in nodeFilters) { + list = matchedNodes[name]; + + if (list) { + list.push(node); + } else { + matchedNodes[name] = [node]; + } + } + + return node; + } + + function removeWhitespaceBefore(node) { + var textNode, textNodeNext, textVal, sibling, blockElements = schema.getBlockElements(); + + for (textNode = node.prev; textNode && textNode.type === 3;) { + textVal = textNode.value.replace(endWhiteSpaceRegExp, ''); + + // Found a text node with non whitespace then trim that and break + if (textVal.length > 0) { + textNode.value = textVal; + return; + } + + textNodeNext = textNode.next; + + // Fix for bug #7543 where bogus nodes would produce empty + // text nodes and these would be removed if a nested list was before it + if (textNodeNext) { + if (textNodeNext.type == 3 && textNodeNext.value.length) { + textNode = textNode.prev; + continue; + } + + if (!blockElements[textNodeNext.name] && textNodeNext.name != 'script' && textNodeNext.name != 'style') { + textNode = textNode.prev; + continue; + } + } + + sibling = textNode.prev; + textNode.remove(); + textNode = sibling; + } + } + + function cloneAndExcludeBlocks(input) { + var name, output = {}; + + for (name in input) { + if (name !== 'li' && name != 'p') { + output[name] = input[name]; + } + } + + return output; + } + + parser = new SaxParser({ + validate: validate, + allow_script_urls: settings.allow_script_urls, + allow_conditional_comments: settings.allow_conditional_comments, + + // Exclude P and LI from DOM parsing since it's treated better by the DOM parser + self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()), + + cdata: function(text) { + node.append(createNode('#cdata', 4)).value = text; + }, + + text: function(text, raw) { + var textNode; + + // Trim all redundant whitespace on non white space elements + if (!isInWhiteSpacePreservedElement) { + text = text.replace(allWhiteSpaceRegExp, ' '); + + if (node.lastChild && blockElements[node.lastChild.name]) { + text = text.replace(startWhiteSpaceRegExp, ''); + } + } + + // Do we need to create the node + if (text.length !== 0) { + textNode = createNode('#text', 3); + textNode.raw = !!raw; + node.append(textNode).value = text; + } + }, + + comment: function(text) { + node.append(createNode('#comment', 8)).value = text; + }, + + pi: function(name, text) { + node.append(createNode(name, 7)).value = text; + removeWhitespaceBefore(node); + }, + + doctype: function(text) { + var newNode; + + newNode = node.append(createNode('#doctype', 10)); + newNode.value = text; + removeWhitespaceBefore(node); + }, + + start: function(name, attrs, empty) { + var newNode, attrFiltersLen, elementRule, attrName, parent; + + elementRule = validate ? schema.getElementRule(name) : {}; + if (elementRule) { + newNode = createNode(elementRule.outputName || name, 1); + newNode.attributes = attrs; + newNode.shortEnded = empty; + + node.append(newNode); + + // Check if node is valid child of the parent node is the child is + // unknown we don't collect it since it's probably a custom element + parent = children[node.name]; + if (parent && children[newNode.name] && !parent[newNode.name]) { + invalidChildren.push(newNode); + } + + attrFiltersLen = attributeFilters.length; + while (attrFiltersLen--) { + attrName = attributeFilters[attrFiltersLen].name; + + if (attrName in attrs.map) { + list = matchedAttributes[attrName]; + + if (list) { + list.push(newNode); + } else { + matchedAttributes[attrName] = [newNode]; + } + } + } + + // Trim whitespace before block + if (blockElements[name]) { + removeWhitespaceBefore(newNode); + } + + // Change current node if the element wasn't empty i.e not <br /> or <img /> + if (!empty) { + node = newNode; + } + + // Check if we are inside a whitespace preserved element + if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) { + isInWhiteSpacePreservedElement = true; + } + } + }, + + end: function(name) { + var textNode, elementRule, text, sibling, tempNode; + + elementRule = validate ? schema.getElementRule(name) : {}; + if (elementRule) { + if (blockElements[name]) { + if (!isInWhiteSpacePreservedElement) { + // Trim whitespace of the first node in a block + textNode = node.firstChild; + if (textNode && textNode.type === 3) { + text = textNode.value.replace(startWhiteSpaceRegExp, ''); + + // Any characters left after trim or should we remove it + if (text.length > 0) { + textNode.value = text; + textNode = textNode.next; + } else { + sibling = textNode.next; + textNode.remove(); + textNode = sibling; + + // Remove any pure whitespace siblings + while (textNode && textNode.type === 3) { + text = textNode.value; + sibling = textNode.next; + + if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) { + textNode.remove(); + textNode = sibling; + } + + textNode = sibling; + } + } + } + + // Trim whitespace of the last node in a block + textNode = node.lastChild; + if (textNode && textNode.type === 3) { + text = textNode.value.replace(endWhiteSpaceRegExp, ''); + + // Any characters left after trim or should we remove it + if (text.length > 0) { + textNode.value = text; + textNode = textNode.prev; + } else { + sibling = textNode.prev; + textNode.remove(); + textNode = sibling; + + // Remove any pure whitespace siblings + while (textNode && textNode.type === 3) { + text = textNode.value; + sibling = textNode.prev; + + if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) { + textNode.remove(); + textNode = sibling; + } + + textNode = sibling; + } + } + } + } + + // Trim start white space + // Removed due to: #5424 + /*textNode = node.prev; + if (textNode && textNode.type === 3) { + text = textNode.value.replace(startWhiteSpaceRegExp, ''); + + if (text.length > 0) + textNode.value = text; + else + textNode.remove(); + }*/ + } + + // Check if we exited a whitespace preserved element + if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) { + isInWhiteSpacePreservedElement = false; + } + + // Handle empty nodes + if (elementRule.removeEmpty || elementRule.paddEmpty) { + if (node.isEmpty(nonEmptyElements, whiteSpaceElements)) { + if (elementRule.paddEmpty) { + paddEmptyNode(settings, node); + } else { + // Leave nodes that have a name like <a name="name"> + if (!node.attributes.map.name && !node.attributes.map.id) { + tempNode = node.parent; + + if (blockElements[node.name]) { + node.empty().remove(); + } else { + node.unwrap(); + } + + node = tempNode; + return; + } + } + } + } + + node = node.parent; + } + } + }, schema); + + rootNode = node = new Node(args.context || settings.root_name, 11); + + parser.parse(html); + + // Fix invalid children or report invalid children in a contextual parsing + if (validate && invalidChildren.length) { + if (!args.context) { + fixInvalidChildren(invalidChildren); + } else { + args.invalid = true; + } + } + + // Wrap nodes in the root into block elements if the root is body + if (rootBlockName && (rootNode.name == 'body' || args.isRootContent)) { + addRootBlocks(); + } + + // Run filters only when the contents is valid + if (!args.invalid) { + // Run node filters + for (name in matchedNodes) { + list = nodeFilters[name]; + nodes = matchedNodes[name]; + + // Remove already removed children + fi = nodes.length; + while (fi--) { + if (!nodes[fi].parent) { + nodes.splice(fi, 1); + } + } + + for (i = 0, l = list.length; i < l; i++) { + list[i](nodes, name, args); + } + } + + // Run attribute filters + for (i = 0, l = attributeFilters.length; i < l; i++) { + list = attributeFilters[i]; + + if (list.name in matchedAttributes) { + nodes = matchedAttributes[list.name]; + + // Remove already removed children + fi = nodes.length; + while (fi--) { + if (!nodes[fi].parent) { + nodes.splice(fi, 1); + } + } + + for (fi = 0, fl = list.callbacks.length; fi < fl; fi++) { + list.callbacks[fi](nodes, list.name, args); + } + } + } + } + + return rootNode; + }; + + // Remove <br> at end of block elements Gecko and WebKit injects BR elements to + // make it possible to place the caret inside empty blocks. This logic tries to remove + // these elements and keep br elements that where intended to be there intact + if (settings.remove_trailing_brs) { + self.addNodeFilter('br', function(nodes) { + var i, l = nodes.length, + node, blockElements = extend({}, schema.getBlockElements()); + var nonEmptyElements = schema.getNonEmptyElements(), + parent, lastParent, prev, prevName; + var whiteSpaceElements = schema.getNonEmptyElements(); + var elementRule, textNode; + + // Remove brs from body element as well + blockElements.body = 1; + + // Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p> + for (i = 0; i < l; i++) { + node = nodes[i]; + parent = node.parent; + + if (blockElements[node.parent.name] && node === parent.lastChild) { + // Loop all nodes to the left of the current node and check for other BR elements + // excluding bookmarks since they are invisible + prev = node.prev; + while (prev) { + prevName = prev.name; + + // Ignore bookmarks + if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') { + // Found a non BR element + if (prevName !== "br") { + break; + } + + // Found another br it's a <br><br> structure then don't remove anything + if (prevName === 'br') { + node = null; + break; + } + } + + prev = prev.prev; + } + + if (node) { + node.remove(); + + // Is the parent to be considered empty after we removed the BR + if (parent.isEmpty(nonEmptyElements, whiteSpaceElements)) { + elementRule = schema.getElementRule(parent.name); + + // Remove or padd the element depending on schema rule + if (elementRule) { + if (elementRule.removeEmpty) { + parent.remove(); + } else if (elementRule.paddEmpty) { + paddEmptyNode(settings, parent); + } + } + } + } + } else { + // Replaces BR elements inside inline elements like <p><b><i><br></i></b></p> + // so they become <p><b><i> </i></b></p> + lastParent = node; + while (parent && parent.firstChild === lastParent && parent.lastChild === lastParent) { + lastParent = parent; + + if (blockElements[parent.name]) { + break; + } + + parent = parent.parent; + } + + if (lastParent === parent && settings.padd_empty_with_br !== true) { + textNode = new Node('#text', 3); + textNode.value = '\u00a0'; + node.replace(textNode); + } + } + } + }); + } + + + self.addAttributeFilter('href', function(nodes) { + var i = nodes.length, + node; + + var appendRel = function(rel) { + var parts = rel.split(' ').filter(function(p) { + return p.length > 0; + }); + return parts.concat(['noopener']).sort().join(' '); + }; + + var addNoOpener = function(rel) { + var newRel = rel ? Tools.trim(rel) : ''; + if (!/\b(noopener)\b/g.test(newRel)) { + return appendRel(newRel); + } else { + return newRel; + } + }; + + if (!settings.allow_unsafe_link_target) { + while (i--) { + node = nodes[i]; + if (node.name === 'a' && node.attr('target') === '_blank') { + node.attr('rel', addNoOpener(node.attr('rel'))); + } + } + } + }); + + // Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included. + if (!settings.allow_html_in_named_anchor) { + self.addAttributeFilter('id,name', function(nodes) { + var i = nodes.length, + sibling, prevSibling, parent, node; + + while (i--) { + node = nodes[i]; + if (node.name === 'a' && node.firstChild && !node.attr('href')) { + parent = node.parent; + + // Move children after current node + sibling = node.lastChild; + do { + prevSibling = sibling.prev; + parent.insert(sibling, node); + sibling = prevSibling; + } while (sibling); + } + } + }); + } + + if (settings.fix_list_elements) { + self.addNodeFilter('ul,ol', function(nodes) { + var i = nodes.length, + node, parentNode; + + while (i--) { + node = nodes[i]; + parentNode = node.parent; + + if (parentNode.name === 'ul' || parentNode.name === 'ol') { + if (node.prev && node.prev.name === 'li') { + node.prev.append(node); + } else { + var li = new Node('li', 1); + li.attr('style', 'list-style-type: none'); + node.wrap(li); + } + } + } + }); + } + + if (settings.validate && schema.getValidClasses()) { + self.addAttributeFilter('class', function(nodes) { + var i = nodes.length, + node, classList, ci, className, classValue; + var validClasses = schema.getValidClasses(), + validClassesMap, valid; + + while (i--) { + node = nodes[i]; + classList = node.attr('class').split(' '); + classValue = ''; + + for (ci = 0; ci < classList.length; ci++) { + className = classList[ci]; + valid = false; + + validClassesMap = validClasses['*']; + if (validClassesMap && validClassesMap[className]) { + valid = true; + } + + validClassesMap = validClasses[node.name]; + if (!valid && validClassesMap && validClassesMap[className]) { + valid = true; + } + + if (valid) { + if (classValue) { + classValue += ' '; + } + + classValue += className; + } + } + + if (!classValue.length) { + classValue = null; + } + + node.attr('class', classValue); + } + }); + } + }; + } + ); + + /** + * Writer.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class is used to write HTML tags out it can be used with the Serializer or the SaxParser. + * + * @class tinymce.html.Writer + * @example + * var writer = new tinymce.html.Writer({indent: true}); + * var parser = new tinymce.html.SaxParser(writer).parse('<p><br></p>'); + * console.log(writer.getContent()); + * + * @class tinymce.html.Writer + * @version 3.4 + */ + define( + 'tinymce.core.html.Writer', [ + "tinymce.core.html.Entities", + "tinymce.core.util.Tools" + ], + function(Entities, Tools) { + var makeMap = Tools.makeMap; + + /** + * Constructs a new Writer instance. + * + * @constructor + * @method Writer + * @param {Object} settings Name/value settings object. + */ + return function(settings) { + var html = [], + indent, indentBefore, indentAfter, encode, htmlOutput; + + settings = settings || {}; + indent = settings.indent; + indentBefore = makeMap(settings.indent_before || ''); + indentAfter = makeMap(settings.indent_after || ''); + encode = Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities); + htmlOutput = settings.element_format == "html"; + + return { + /** + * Writes the a start element such as <p id="a">. + * + * @method start + * @param {String} name Name of the element. + * @param {Array} attrs Optional attribute array or undefined if it hasn't any. + * @param {Boolean} empty Optional empty state if the tag should end like <br />. + */ + start: function(name, attrs, empty) { + var i, l, attr, value; + + if (indent && indentBefore[name] && html.length > 0) { + value = html[html.length - 1]; + + if (value.length > 0 && value !== '\n') { + html.push('\n'); + } + } + + html.push('<', name); + + if (attrs) { + for (i = 0, l = attrs.length; i < l; i++) { + attr = attrs[i]; + html.push(' ', attr.name, '="', encode(attr.value, true), '"'); + } + } + + if (!empty || htmlOutput) { + html[html.length] = '>'; + } else { + html[html.length] = ' />'; + } + + if (empty && indent && indentAfter[name] && html.length > 0) { + value = html[html.length - 1]; + + if (value.length > 0 && value !== '\n') { + html.push('\n'); + } + } + }, + + /** + * Writes the a end element such as </p>. + * + * @method end + * @param {String} name Name of the element. + */ + end: function(name) { + var value; + + /*if (indent && indentBefore[name] && html.length > 0) { + value = html[html.length - 1]; + + if (value.length > 0 && value !== '\n') + html.push('\n'); + }*/ + + html.push('</', name, '>'); + + if (indent && indentAfter[name] && html.length > 0) { + value = html[html.length - 1]; + + if (value.length > 0 && value !== '\n') { + html.push('\n'); + } + } + }, + + /** + * Writes a text node. + * + * @method text + * @param {String} text String to write out. + * @param {Boolean} raw Optional raw state if true the contents wont get encoded. + */ + text: function(text, raw) { + if (text.length > 0) { + html[html.length] = raw ? text : encode(text); + } + }, + + /** + * Writes a cdata node such as <![CDATA[data]]>. + * + * @method cdata + * @param {String} text String to write out inside the cdata. + */ + cdata: function(text) { + html.push('<![CDATA[', text, ']]>'); + }, + + /** + * Writes a comment node such as <!-- Comment -->. + * + * @method cdata + * @param {String} text String to write out inside the comment. + */ + comment: function(text) { + html.push('<!--', text, '-->'); + }, + + /** + * Writes a PI node such as <?xml attr="value" ?>. + * + * @method pi + * @param {String} name Name of the pi. + * @param {String} text String to write out inside the pi. + */ + pi: function(name, text) { + if (text) { + html.push('<?', name, ' ', encode(text), '?>'); + } else { + html.push('<?', name, '?>'); + } + + if (indent) { + html.push('\n'); + } + }, + + /** + * Writes a doctype node such as <!DOCTYPE data>. + * + * @method doctype + * @param {String} text String to write out inside the doctype. + */ + doctype: function(text) { + html.push('<!DOCTYPE', text, '>', indent ? '\n' : ''); + }, + + /** + * Resets the internal buffer if one wants to reuse the writer. + * + * @method reset + */ + reset: function() { + html.length = 0; + }, + + /** + * Returns the contents that got serialized. + * + * @method getContent + * @return {String} HTML contents that got written down. + */ + getContent: function() { + return html.join('').replace(/\n$/, ''); + } + }; + }; + } + ); + /** + * Serializer.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class is used to serialize down the DOM tree into a string using a Writer instance. + * + * + * @example + * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>')); + * @class tinymce.html.Serializer + * @version 3.4 + */ + define( + 'tinymce.core.html.Serializer', [ + "tinymce.core.html.Writer", + "tinymce.core.html.Schema" + ], + function(Writer, Schema) { + /** + * Constructs a new Serializer instance. + * + * @constructor + * @method Serializer + * @param {Object} settings Name/value settings object. + * @param {tinymce.html.Schema} schema Schema instance to use. + */ + return function(settings, schema) { + var self = this, + writer = new Writer(settings); + + settings = settings || {}; + settings.validate = "validate" in settings ? settings.validate : true; + + self.schema = schema = schema || new Schema(); + self.writer = writer; + + /** + * Serializes the specified node into a string. + * + * @example + * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>')); + * @method serialize + * @param {tinymce.html.Node} node Node instance to serialize. + * @return {String} String with HTML based on DOM tree. + */ + self.serialize = function(node) { + var handlers, validate; + + validate = settings.validate; + + handlers = { + // #text + 3: function(node) { + writer.text(node.value, node.raw); + }, + + // #comment + 8: function(node) { + writer.comment(node.value); + }, + + // Processing instruction + 7: function(node) { + writer.pi(node.name, node.value); + }, + + // Doctype + 10: function(node) { + writer.doctype(node.value); + }, + + // CDATA + 4: function(node) { + writer.cdata(node.value); + }, + + // Document fragment + 11: function(node) { + if ((node = node.firstChild)) { + do { + walk(node); + } while ((node = node.next)); + } + } + }; + + writer.reset(); + + function walk(node) { + var handler = handlers[node.type], + name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule; + + if (!handler) { + name = node.name; + isEmpty = node.shortEnded; + attrs = node.attributes; + + // Sort attributes + if (validate && attrs && attrs.length > 1) { + sortedAttrs = []; + sortedAttrs.map = {}; + + elementRule = schema.getElementRule(node.name); + if (elementRule) { + for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) { + attrName = elementRule.attributesOrder[i]; + + if (attrName in attrs.map) { + attrValue = attrs.map[attrName]; + sortedAttrs.map[attrName] = attrValue; + sortedAttrs.push({ + name: attrName, + value: attrValue + }); + } + } + + for (i = 0, l = attrs.length; i < l; i++) { + attrName = attrs[i].name; + + if (!(attrName in sortedAttrs.map)) { + attrValue = attrs.map[attrName]; + sortedAttrs.map[attrName] = attrValue; + sortedAttrs.push({ + name: attrName, + value: attrValue + }); + } + } + + attrs = sortedAttrs; + } + } + + writer.start(node.name, attrs, isEmpty); + + if (!isEmpty) { + if ((node = node.firstChild)) { + do { + walk(node); + } while ((node = node.next)); + } + + writer.end(name); + } + } else { + handler(node); + } + } + + // Serialize element and treat all non elements as fragments + if (node.type == 1 && !settings.inner) { + walk(node); + } else { + handlers[11](node); + } + + return writer.getContent(); + }; + }; + } + ); + + /** + * Serializer.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class is used to serialize DOM trees into a string. Consult the TinyMCE Wiki API for + * more details and examples on how to use this class. + * + * @class tinymce.dom.Serializer + */ + define( + 'tinymce.core.dom.Serializer', [ + "tinymce.core.dom.DOMUtils", + "tinymce.core.html.DomParser", + "tinymce.core.html.SaxParser", + "tinymce.core.html.Entities", + "tinymce.core.html.Serializer", + "tinymce.core.html.Node", + "tinymce.core.html.Schema", + "tinymce.core.Env", + "tinymce.core.util.Tools", + "tinymce.core.text.Zwsp" + ], + function(DOMUtils, DomParser, SaxParser, Entities, Serializer, Node, Schema, Env, Tools, Zwsp) { + var each = Tools.each, + trim = Tools.trim; + var DOM = DOMUtils.DOM; + + /** + * IE 11 has a fantastic bug where it will produce two trailing BR elements to iframe bodies when + * the iframe is hidden by display: none on a parent container. The DOM is actually out of sync + * with innerHTML in this case. It's like IE adds shadow DOM BR elements that appears on innerHTML + * but not as the lastChild of the body. So this fix simply removes the last two + * BR elements at the end of the document. + * + * Example of what happens: <body>text</body> becomes <body>text<br><br></body> + */ + function trimTrailingBr(rootNode) { + var brNode1, brNode2; + + function isBr(node) { + return node && node.name === 'br'; + } + + brNode1 = rootNode.lastChild; + if (isBr(brNode1)) { + brNode2 = brNode1.prev; + + if (isBr(brNode2)) { + brNode1.remove(); + brNode2.remove(); + } + } + } + + /** + * Constructs a new DOM serializer class. + * + * @constructor + * @method Serializer + * @param {Object} settings Serializer settings object. + * @param {tinymce.Editor} editor Optional editor to bind events to and get schema/dom from. + */ + return function(settings, editor) { + var dom, schema, htmlParser, tempAttrs = ["data-mce-selected"]; + + if (editor) { + dom = editor.dom; + schema = editor.schema; + } + + function trimHtml(html) { + var trimContentRegExp = new RegExp([ + '<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\\/span>', // Trim bogus spans like caret containers + '\\s?(' + tempAttrs.join('|') + ')="[^"]+"' // Trim temporaty data-mce prefixed attributes like data-mce-selected + ].join('|'), 'gi'); + + html = Zwsp.trim(html.replace(trimContentRegExp, '')); + + return html; + } + + function trimContent(html) { + var content = html; + var bogusAllRegExp = /<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g; + var endTagIndex, index, matchLength, matches, shortEndedElements, schema = editor.schema; + + content = trimHtml(content); + shortEndedElements = schema.getShortEndedElements(); + + // Remove all bogus elements marked with "all" + while ((matches = bogusAllRegExp.exec(content))) { + index = bogusAllRegExp.lastIndex; + matchLength = matches[0].length; + + if (shortEndedElements[matches[1]]) { + endTagIndex = index; + } else { + endTagIndex = SaxParser.findEndTag(schema, content, index); + } + + content = content.substring(0, index - matchLength) + content.substring(endTagIndex); + bogusAllRegExp.lastIndex = index - matchLength; + } + + return content; + } + + /** + * Returns a trimmed version of the editor contents to be used for the undo level. This + * will remove any data-mce-bogus="all" marked elements since these are used for UI it will also + * remove the data-mce-selected attributes used for selection of objects and caret containers. + * It will keep all data-mce-bogus="1" elements since these can be used to place the caret etc and will + * be removed by the serialization logic when you save. + * + * @private + * @return {String} HTML contents of the editor excluding some internal bogus elements. + */ + function getTrimmedContent() { + return trimContent(editor.getBody().innerHTML); + } + + function addTempAttr(name) { + if (Tools.inArray(tempAttrs, name) === -1) { + htmlParser.addAttributeFilter(name, function(nodes, name) { + var i = nodes.length; + + while (i--) { + nodes[i].attr(name, null); + } + }); + + tempAttrs.push(name); + } + } + + // Default DOM and Schema if they are undefined + dom = dom || DOM; + schema = schema || new Schema(settings); + settings.entity_encoding = settings.entity_encoding || 'named'; + settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true; + + htmlParser = new DomParser(settings, schema); + + // Convert tabindex back to elements when serializing contents + htmlParser.addAttributeFilter('data-mce-tabindex', function(nodes, name) { + var i = nodes.length, + node; + + while (i--) { + node = nodes[i]; + node.attr('tabindex', node.attributes.map['data-mce-tabindex']); + node.attr(name, null); + } + }); + + // Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed + htmlParser.addAttributeFilter('src,href,style', function(nodes, name) { + var i = nodes.length, + node, value, internalName = 'data-mce-' + name; + var urlConverter = settings.url_converter, + urlConverterScope = settings.url_converter_scope, + undef; + + while (i--) { + node = nodes[i]; + + value = node.attributes.map[internalName]; + if (value !== undef) { + // Set external name to internal value and remove internal + node.attr(name, value.length > 0 ? value : null); + node.attr(internalName, null); + } else { + // No internal attribute found then convert the value we have in the DOM + value = node.attributes.map[name]; + + if (name === "style") { + value = dom.serializeStyle(dom.parseStyle(value), node.name); + } else if (urlConverter) { + value = urlConverter.call(urlConverterScope, value, name, node.name); + } + + node.attr(name, value.length > 0 ? value : null); + } + } + }); + + // Remove internal classes mceItem<..> or mceSelected + htmlParser.addAttributeFilter('class', function(nodes) { + var i = nodes.length, + node, value; + + while (i--) { + node = nodes[i]; + value = node.attr('class'); + + if (value) { + value = node.attr('class').replace(/(?:^|\s)mce-item-\w+(?!\S)/g, ''); + node.attr('class', value.length > 0 ? value : null); + } + } + }); + + // Remove bookmark elements + htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) { + var i = nodes.length, + node; + + while (i--) { + node = nodes[i]; + + if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup) { + node.remove(); + } + } + }); + + htmlParser.addNodeFilter('noscript', function(nodes) { + var i = nodes.length, + node; + + while (i--) { + node = nodes[i].firstChild; + + if (node) { + node.value = Entities.decode(node.value); + } + } + }); + + // Force script into CDATA sections and remove the mce- prefix also add comments around styles + htmlParser.addNodeFilter('script,style', function(nodes, name) { + var i = nodes.length, + node, value, type; + + function trim(value) { + /*jshint maxlen:255 */ + /*eslint max-len:0 */ + return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n') + .replace(/^[\r\n]*|[\r\n]*$/g, '') + .replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '') + .replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, ''); + } + + while (i--) { + node = nodes[i]; + value = node.firstChild ? node.firstChild.value : ''; + + if (name === "script") { + // Remove mce- prefix from script elements and remove default type since the user specified + // a script element without type attribute + type = node.attr('type'); + if (type) { + node.attr('type', type == 'mce-no/type' ? null : type.replace(/^mce\-/, '')); + } + + if (value.length > 0) { + node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>'; + } + } else { + if (value.length > 0) { + node.firstChild.value = '<!--\n' + trim(value) + '\n-->'; + } + } + } + }); + + // Convert comments to cdata and handle protected comments + htmlParser.addNodeFilter('#comment', function(nodes) { + var i = nodes.length, + node; + + while (i--) { + node = nodes[i]; + + if (node.value.indexOf('[CDATA[') === 0) { + node.name = '#cdata'; + node.type = 4; + node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, ''); + } else if (node.value.indexOf('mce:protected ') === 0) { + node.name = "#text"; + node.type = 3; + node.raw = true; + node.value = unescape(node.value).substr(14); + } + } + }); + + htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) { + var i = nodes.length, + node; + + while (i--) { + node = nodes[i]; + if (node.type === 7) { + node.remove(); + } else if (node.type === 1) { + if (name === "input" && !("type" in node.attributes.map)) { + node.attr('type', 'text'); + } + } + } + }); + + // Remove internal data attributes + htmlParser.addAttributeFilter( + 'data-mce-src,data-mce-href,data-mce-style,' + + 'data-mce-selected,data-mce-expando,' + + 'data-mce-type,data-mce-resize', + + function(nodes, name) { + var i = nodes.length; + + while (i--) { + nodes[i].attr(name, null); + } + } + ); + + // Return public methods + return { + /** + * Schema instance that was used to when the Serializer was constructed. + * + * @field {tinymce.html.Schema} schema + */ + schema: schema, + + /** + * Adds a node filter function to the parser used by the serializer, the parser will collect the specified nodes by name + * and then execute the callback ones it has finished parsing the document. + * + * @example + * parser.addNodeFilter('p,h1', function(nodes, name) { + * for (var i = 0; i < nodes.length; i++) { + * console.log(nodes[i].name); + * } + * }); + * @method addNodeFilter + * @method {String} name Comma separated list of nodes to collect. + * @param {function} callback Callback function to execute once it has collected nodes. + */ + addNodeFilter: htmlParser.addNodeFilter, + + /** + * Adds a attribute filter function to the parser used by the serializer, the parser will + * collect nodes that has the specified attributes + * and then execute the callback ones it has finished parsing the document. + * + * @example + * parser.addAttributeFilter('src,href', function(nodes, name) { + * for (var i = 0; i < nodes.length; i++) { + * console.log(nodes[i].name); + * } + * }); + * @method addAttributeFilter + * @method {String} name Comma separated list of nodes to collect. + * @param {function} callback Callback function to execute once it has collected nodes. + */ + addAttributeFilter: htmlParser.addAttributeFilter, + + /** + * Serializes the specified browser DOM node into a HTML string. + * + * @method serialize + * @param {DOMNode} node DOM node to serialize. + * @param {Object} args Arguments option that gets passed to event handlers. + */ + serialize: function(node, args) { + var self = this, + impl, doc, oldDoc, htmlSerializer, content, rootNode; + + // Explorer won't clone contents of script and style and the + // selected index of select elements are cleared on a clone operation. + if (Env.ie && dom.select('script,style,select,map').length > 0) { + content = node.innerHTML; + node = node.cloneNode(false); + dom.setHTML(node, content); + } else { + node = node.cloneNode(true); + } + + // Nodes needs to be attached to something in WebKit/Opera + // This fix will make DOM ranges and make Sizzle happy! + impl = document.implementation; + if (impl.createHTMLDocument) { + // Create an empty HTML document + doc = impl.createHTMLDocument(""); + + // Add the element or it's children if it's a body element to the new document + each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) { + doc.body.appendChild(doc.importNode(node, true)); + }); + + // Grab first child or body element for serialization + if (node.nodeName != 'BODY') { + node = doc.body.firstChild; + } else { + node = doc.body; + } + + // set the new document in DOMUtils so createElement etc works + oldDoc = dom.doc; + dom.doc = doc; + } + + args = args || {}; + args.format = args.format || 'html'; + + // Don't wrap content if we want selected html + if (args.selection) { + args.forced_root_block = ''; + } + + // Pre process + if (!args.no_events) { + args.node = node; + self.onPreProcess(args); + } + + // Parse HTML + rootNode = htmlParser.parse(trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args); + trimTrailingBr(rootNode); + + // Serialize HTML + htmlSerializer = new Serializer(settings, schema); + args.content = htmlSerializer.serialize(rootNode); + + // Replace all BOM characters for now until we can find a better solution + if (!args.cleanup) { + args.content = Zwsp.trim(args.content); + args.content = args.content.replace(/\uFEFF/g, ''); + } + + // Post process + if (!args.no_events) { + self.onPostProcess(args); + } + + // Restore the old document if it was changed + if (oldDoc) { + dom.doc = oldDoc; + } + + args.node = null; + + return args.content; + }, + + /** + * Adds valid elements rules to the serializers schema instance this enables you to specify things + * like what elements should be outputted and what attributes specific elements might have. + * Consult the Wiki for more details on this format. + * + * @method addRules + * @param {String} rules Valid elements rules string to add to schema. + */ + addRules: function(rules) { + schema.addValidElements(rules); + }, + + /** + * Sets the valid elements rules to the serializers schema instance this enables you to specify things + * like what elements should be outputted and what attributes specific elements might have. + * Consult the Wiki for more details on this format. + * + * @method setRules + * @param {String} rules Valid elements rules string. + */ + setRules: function(rules) { + schema.setValidElements(rules); + }, + + onPreProcess: function(args) { + if (editor) { + editor.fire('PreProcess', args); + } + }, + + onPostProcess: function(args) { + if (editor) { + editor.fire('PostProcess', args); + } + }, + + /** + * Adds a temporary internal attribute these attributes will get removed on undo and + * when getting contents out of the editor. + * + * @method addTempAttr + * @param {String} name string + */ + addTempAttr: addTempAttr, + + // Internal + trimHtml: trimHtml, + getTrimmedContent: getTrimmedContent, + trimContent: trimContent + }; + }; + } + ); + + /** + * VK.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This file exposes a set of the common KeyCodes for use. Please grow it as needed. + */ + define( + 'tinymce.core.util.VK', [ + "tinymce.core.Env" + ], + function(Env) { + return { + BACKSPACE: 8, + DELETE: 46, + DOWN: 40, + ENTER: 13, + LEFT: 37, + RIGHT: 39, + SPACEBAR: 32, + TAB: 9, + UP: 38, + + modifierPressed: function(e) { + return e.shiftKey || e.ctrlKey || e.altKey || this.metaKeyPressed(e); + }, + + metaKeyPressed: function(e) { + // Check if ctrl or meta key is pressed. Edge case for AltGr on Windows where it produces ctrlKey+altKey states + return (Env.mac ? e.metaKey : e.ctrlKey && !e.altKey); + } + }; + } + ); + + /** + * ClientRect.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Utility functions for working with client rects. + * + * @private + * @class tinymce.geom.ClientRect + */ + define( + 'tinymce.core.geom.ClientRect', [], + function() { + var round = Math.round; + + function clone(rect) { + if (!rect) { + return { + left: 0, + top: 0, + bottom: 0, + right: 0, + width: 0, + height: 0 + }; + } + + return { + left: round(rect.left), + top: round(rect.top), + bottom: round(rect.bottom), + right: round(rect.right), + width: round(rect.width), + height: round(rect.height) + }; + } + + function collapse(clientRect, toStart) { + clientRect = clone(clientRect); + + if (toStart) { + clientRect.right = clientRect.left; + } else { + clientRect.left = clientRect.left + clientRect.width; + clientRect.right = clientRect.left; + } + + clientRect.width = 0; + + return clientRect; + } + + function isEqual(rect1, rect2) { + return ( + rect1.left === rect2.left && + rect1.top === rect2.top && + rect1.bottom === rect2.bottom && + rect1.right === rect2.right + ); + } + + function isValidOverflow(overflowY, clientRect1, clientRect2) { + return overflowY >= 0 && overflowY <= Math.min(clientRect1.height, clientRect2.height) / 2; + + } + + function isAbove(clientRect1, clientRect2) { + if ((clientRect1.bottom - clientRect1.height / 2) < clientRect2.top) { + return true; + } + + if (clientRect1.top > clientRect2.bottom) { + return false; + } + + return isValidOverflow(clientRect2.top - clientRect1.bottom, clientRect1, clientRect2); + } + + function isBelow(clientRect1, clientRect2) { + if (clientRect1.top > clientRect2.bottom) { + return true; + } + + if (clientRect1.bottom < clientRect2.top) { + return false; + } + + return isValidOverflow(clientRect2.bottom - clientRect1.top, clientRect1, clientRect2); + } + + function isLeft(clientRect1, clientRect2) { + return clientRect1.left < clientRect2.left; + } + + function isRight(clientRect1, clientRect2) { + return clientRect1.right > clientRect2.right; + } + + function compare(clientRect1, clientRect2) { + if (isAbove(clientRect1, clientRect2)) { + return -1; + } + + if (isBelow(clientRect1, clientRect2)) { + return 1; + } + + if (isLeft(clientRect1, clientRect2)) { + return -1; + } + + if (isRight(clientRect1, clientRect2)) { + return 1; + } + + return 0; + } + + function containsXY(clientRect, clientX, clientY) { + return ( + clientX >= clientRect.left && + clientX <= clientRect.right && + clientY >= clientRect.top && + clientY <= clientRect.bottom + ); + } + + return { + clone: clone, + collapse: collapse, + isEqual: isEqual, + isAbove: isAbove, + isBelow: isBelow, + isLeft: isLeft, + isRight: isRight, + compare: compare, + containsXY: containsXY + }; + } + ); + + /** + * RangePoint.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.dom.RangePoint', [ + 'ephox.katamari.api.Arr', + 'tinymce.core.geom.ClientRect' + ], + function(Arr, ClientRect) { + var isXYWithinRange = function(clientX, clientY, range) { + if (range.collapsed) { + return false; + } + + return Arr.foldl(range.getClientRects(), function(state, rect) { + return state || ClientRect.containsXY(rect, clientX, clientY); + }, false); + }; + + return { + isXYWithinRange: isXYWithinRange + }; + } + ); + /** + * ControlSelection.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class handles control selection of elements. Controls are elements + * that can be resized and needs to be selected as a whole. It adds custom resize handles + * to all browser engines that support properly disabling the built in resize logic. + * + * @class tinymce.dom.ControlSelection + */ + define( + 'tinymce.core.dom.ControlSelection', [ + 'ephox.katamari.api.Fun', + 'tinymce.core.dom.NodeType', + 'tinymce.core.dom.RangePoint', + 'tinymce.core.Env', + 'tinymce.core.util.Delay', + 'tinymce.core.util.Tools', + 'tinymce.core.util.VK' + ], + function(Fun, NodeType, RangePoint, Env, Delay, Tools, VK) { + var isContentEditableFalse = NodeType.isContentEditableFalse; + var isContentEditableTrue = NodeType.isContentEditableTrue; + + function getContentEditableRoot(root, node) { + while (node && node != root) { + if (isContentEditableTrue(node) || isContentEditableFalse(node)) { + return node; + } + + node = node.parentNode; + } + + return null; + } + + var isImage = function(elm) { + return elm && elm.nodeName === 'IMG'; + }; + + var isEventOnImageOutsideRange = function(evt, range) { + return isImage(evt.target) && !RangePoint.isXYWithinRange(evt.clientX, evt.clientY, range); + }; + + var contextMenuSelectImage = function(editor, evt) { + var target = evt.target; + + if (isEventOnImageOutsideRange(evt, editor.selection.getRng()) && !evt.isDefaultPrevented()) { + evt.preventDefault(); + editor.selection.select(target); + } + }; + + return function(selection, editor) { + var dom = editor.dom, + each = Tools.each; + var selectedElm, selectedElmGhost, resizeHelper, resizeHandles, selectedHandle, lastMouseDownEvent; + var startX, startY, selectedElmX, selectedElmY, startW, startH, ratio, resizeStarted; + var width, height, editableDoc = editor.getDoc(), + rootDocument = document, + isIE = Env.ie && Env.ie < 11; + var abs = Math.abs, + round = Math.round, + rootElement = editor.getBody(), + startScrollWidth, startScrollHeight; + + // Details about each resize handle how to scale etc + resizeHandles = { + // Name: x multiplier, y multiplier, delta size x, delta size y + /*n: [0.5, 0, 0, -1], + e: [1, 0.5, 1, 0], + s: [0.5, 1, 0, 1], + w: [0, 0.5, -1, 0],*/ + nw: [0, 0, -1, -1], + ne: [1, 0, 1, -1], + se: [1, 1, 1, 1], + sw: [0, 1, -1, 1] + }; + + // Add CSS for resize handles, cloned element and selected + var rootClass = '.mce-content-body'; + editor.contentStyles.push( + rootClass + ' div.mce-resizehandle {' + + 'position: absolute;' + + 'border: 1px solid black;' + + 'box-sizing: box-sizing;' + + 'background: #FFF;' + + 'width: 7px;' + + 'height: 7px;' + + 'z-index: 10000' + + '}' + + rootClass + ' .mce-resizehandle:hover {' + + 'background: #000' + + '}' + + rootClass + ' img[data-mce-selected],' + rootClass + ' hr[data-mce-selected] {' + + 'outline: 1px solid black;' + + 'resize: none' + // Have been talks about implementing this in browsers + '}' + + rootClass + ' .mce-clonedresizable {' + + 'position: absolute;' + + (Env.gecko ? '' : 'outline: 1px dashed black;') + // Gecko produces trails while resizing + 'opacity: .5;' + + 'filter: alpha(opacity=50);' + + 'z-index: 10000' + + '}' + + rootClass + ' .mce-resize-helper {' + + 'background: #555;' + + 'background: rgba(0,0,0,0.75);' + + 'border-radius: 3px;' + + 'border: 1px;' + + 'color: white;' + + 'display: none;' + + 'font-family: sans-serif;' + + 'font-size: 12px;' + + 'white-space: nowrap;' + + 'line-height: 14px;' + + 'margin: 5px 10px;' + + 'padding: 5px;' + + 'position: absolute;' + + 'z-index: 10001' + + '}' + ); + + function isResizable(elm) { + var selector = editor.settings.object_resizing; + + if (selector === false || Env.iOS) { + return false; + } + + if (typeof selector != 'string') { + selector = 'table,img,div'; + } + + if (elm.getAttribute('data-mce-resize') === 'false') { + return false; + } + + if (elm == editor.getBody()) { + return false; + } + + return editor.dom.is(elm, selector); + } + + function resizeGhostElement(e) { + var deltaX, deltaY, proportional; + var resizeHelperX, resizeHelperY; + + // Calc new width/height + deltaX = e.screenX - startX; + deltaY = e.screenY - startY; + + // Calc new size + width = deltaX * selectedHandle[2] + startW; + height = deltaY * selectedHandle[3] + startH; + + // Never scale down lower than 5 pixels + width = width < 5 ? 5 : width; + height = height < 5 ? 5 : height; + + if (selectedElm.nodeName == "IMG" && editor.settings.resize_img_proportional !== false) { + proportional = !VK.modifierPressed(e); + } else { + proportional = VK.modifierPressed(e) || (selectedElm.nodeName == "IMG" && selectedHandle[2] * selectedHandle[3] !== 0); + } + + // Constrain proportions + if (proportional) { + if (abs(deltaX) > abs(deltaY)) { + height = round(width * ratio); + width = round(height / ratio); + } else { + width = round(height / ratio); + height = round(width * ratio); + } + } + + // Update ghost size + dom.setStyles(selectedElmGhost, { + width: width, + height: height + }); + + // Update resize helper position + resizeHelperX = selectedHandle.startPos.x + deltaX; + resizeHelperY = selectedHandle.startPos.y + deltaY; + resizeHelperX = resizeHelperX > 0 ? resizeHelperX : 0; + resizeHelperY = resizeHelperY > 0 ? resizeHelperY : 0; + + dom.setStyles(resizeHelper, { + left: resizeHelperX, + top: resizeHelperY, + display: 'block' + }); + + resizeHelper.innerHTML = width + ' × ' + height; + + // Update ghost X position if needed + if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) { + dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width)); + } + + // Update ghost Y position if needed + if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) { + dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height)); + } + + // Calculate how must overflow we got + deltaX = rootElement.scrollWidth - startScrollWidth; + deltaY = rootElement.scrollHeight - startScrollHeight; + + // Re-position the resize helper based on the overflow + if (deltaX + deltaY !== 0) { + dom.setStyles(resizeHelper, { + left: resizeHelperX - deltaX, + top: resizeHelperY - deltaY + }); + } + + if (!resizeStarted) { + editor.fire('ObjectResizeStart', { + target: selectedElm, + width: startW, + height: startH + }); + resizeStarted = true; + } + } + + function endGhostResize() { + resizeStarted = false; + + function setSizeProp(name, value) { + if (value) { + // Resize by using style or attribute + if (selectedElm.style[name] || !editor.schema.isValid(selectedElm.nodeName.toLowerCase(), name)) { + dom.setStyle(selectedElm, name, value); + } else { + dom.setAttrib(selectedElm, name, value); + } + } + } + + // Set width/height properties + setSizeProp('width', width); + setSizeProp('height', height); + + dom.unbind(editableDoc, 'mousemove', resizeGhostElement); + dom.unbind(editableDoc, 'mouseup', endGhostResize); + + if (rootDocument != editableDoc) { + dom.unbind(rootDocument, 'mousemove', resizeGhostElement); + dom.unbind(rootDocument, 'mouseup', endGhostResize); + } + + // Remove ghost/helper and update resize handle positions + dom.remove(selectedElmGhost); + dom.remove(resizeHelper); + + if (!isIE || selectedElm.nodeName == "TABLE") { + showResizeRect(selectedElm); + } + + editor.fire('ObjectResized', { + target: selectedElm, + width: width, + height: height + }); + dom.setAttrib(selectedElm, 'style', dom.getAttrib(selectedElm, 'style')); + editor.nodeChanged(); + } + + function showResizeRect(targetElm, mouseDownHandleName, mouseDownEvent) { + var position, targetWidth, targetHeight, e, rect; + + hideResizeRect(); + unbindResizeHandleEvents(); + + // Get position and size of target + position = dom.getPos(targetElm, rootElement); + selectedElmX = position.x; + selectedElmY = position.y; + rect = targetElm.getBoundingClientRect(); // Fix for Gecko offsetHeight for table with caption + targetWidth = rect.width || (rect.right - rect.left); + targetHeight = rect.height || (rect.bottom - rect.top); + + // Reset width/height if user selects a new image/table + if (selectedElm != targetElm) { + detachResizeStartListener(); + selectedElm = targetElm; + width = height = 0; + } + + // Makes it possible to disable resizing + e = editor.fire('ObjectSelected', { + target: targetElm + }); + + if (isResizable(targetElm) && !e.isDefaultPrevented()) { + each(resizeHandles, function(handle, name) { + var handleElm; + + function startDrag(e) { + startX = e.screenX; + startY = e.screenY; + startW = selectedElm.clientWidth; + startH = selectedElm.clientHeight; + ratio = startH / startW; + selectedHandle = handle; + + handle.startPos = { + x: targetWidth * handle[0] + selectedElmX, + y: targetHeight * handle[1] + selectedElmY + }; + + startScrollWidth = rootElement.scrollWidth; + startScrollHeight = rootElement.scrollHeight; + + selectedElmGhost = selectedElm.cloneNode(true); + dom.addClass(selectedElmGhost, 'mce-clonedresizable'); + dom.setAttrib(selectedElmGhost, 'data-mce-bogus', 'all'); + selectedElmGhost.contentEditable = false; // Hides IE move layer cursor + selectedElmGhost.unSelectabe = true; + dom.setStyles(selectedElmGhost, { + left: selectedElmX, + top: selectedElmY, + margin: 0 + }); + + selectedElmGhost.removeAttribute('data-mce-selected'); + rootElement.appendChild(selectedElmGhost); + + dom.bind(editableDoc, 'mousemove', resizeGhostElement); + dom.bind(editableDoc, 'mouseup', endGhostResize); + + if (rootDocument != editableDoc) { + dom.bind(rootDocument, 'mousemove', resizeGhostElement); + dom.bind(rootDocument, 'mouseup', endGhostResize); + } + + resizeHelper = dom.add(rootElement, 'div', { + 'class': 'mce-resize-helper', + 'data-mce-bogus': 'all' + }, startW + ' × ' + startH); + } + + if (mouseDownHandleName) { + // Drag started by IE native resizestart + if (name == mouseDownHandleName) { + startDrag(mouseDownEvent); + } + + return; + } + + // Get existing or render resize handle + handleElm = dom.get('mceResizeHandle' + name); + if (handleElm) { + dom.remove(handleElm); + } + + handleElm = dom.add(rootElement, 'div', { + id: 'mceResizeHandle' + name, + 'data-mce-bogus': 'all', + 'class': 'mce-resizehandle', + unselectable: true, + style: 'cursor:' + name + '-resize; margin:0; padding:0' + }); + + // Hides IE move layer cursor + // If we set it on Chrome we get this wounderful bug: #6725 + if (Env.ie) { + handleElm.contentEditable = false; + } + + dom.bind(handleElm, 'mousedown', function(e) { + e.stopImmediatePropagation(); + e.preventDefault(); + startDrag(e); + }); + + handle.elm = handleElm; + + // Position element + dom.setStyles(handleElm, { + left: (targetWidth * handle[0] + selectedElmX) - (handleElm.offsetWidth / 2), + top: (targetHeight * handle[1] + selectedElmY) - (handleElm.offsetHeight / 2) + }); + }); + } else { + hideResizeRect(); + } + + selectedElm.setAttribute('data-mce-selected', '1'); + } + + function hideResizeRect() { + var name, handleElm; + + unbindResizeHandleEvents(); + + if (selectedElm) { + selectedElm.removeAttribute('data-mce-selected'); + } + + for (name in resizeHandles) { + handleElm = dom.get('mceResizeHandle' + name); + if (handleElm) { + dom.unbind(handleElm); + dom.remove(handleElm); + } + } + } + + function updateResizeRect(e) { + var startElm, controlElm; + + function isChildOrEqual(node, parent) { + if (node) { + do { + if (node === parent) { + return true; + } + } while ((node = node.parentNode)); + } + } + + // Ignore all events while resizing or if the editor instance was removed + if (resizeStarted || editor.removed) { + return; + } + + // Remove data-mce-selected from all elements since they might have been copied using Ctrl+c/v + each(dom.select('img[data-mce-selected],hr[data-mce-selected]'), function(img) { + img.removeAttribute('data-mce-selected'); + }); + + controlElm = e.type == 'mousedown' ? e.target : selection.getNode(); + controlElm = dom.$(controlElm).closest(isIE ? 'table' : 'table,img,hr')[0]; + + if (isChildOrEqual(controlElm, rootElement)) { + disableGeckoResize(); + startElm = selection.getStart(true); + + if (isChildOrEqual(startElm, controlElm) && isChildOrEqual(selection.getEnd(true), controlElm)) { + if (!isIE || (controlElm != startElm && startElm.nodeName !== 'IMG')) { + showResizeRect(controlElm); + return; + } + } + } + + hideResizeRect(); + } + + function attachEvent(elm, name, func) { + if (elm && elm.attachEvent) { + elm.attachEvent('on' + name, func); + } + } + + function detachEvent(elm, name, func) { + if (elm && elm.detachEvent) { + elm.detachEvent('on' + name, func); + } + } + + function resizeNativeStart(e) { + var target = e.srcElement, + pos, name, corner, cornerX, cornerY, relativeX, relativeY; + + pos = target.getBoundingClientRect(); + relativeX = lastMouseDownEvent.clientX - pos.left; + relativeY = lastMouseDownEvent.clientY - pos.top; + + // Figure out what corner we are draging on + for (name in resizeHandles) { + corner = resizeHandles[name]; + + cornerX = target.offsetWidth * corner[0]; + cornerY = target.offsetHeight * corner[1]; + + if (abs(cornerX - relativeX) < 8 && abs(cornerY - relativeY) < 8) { + selectedHandle = corner; + break; + } + } + + // Remove native selection and let the magic begin + resizeStarted = true; + editor.fire('ObjectResizeStart', { + target: selectedElm, + width: selectedElm.clientWidth, + height: selectedElm.clientHeight + }); + editor.getDoc().selection.empty(); + showResizeRect(target, name, lastMouseDownEvent); + } + + function preventDefault(e) { + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; // IE + } + } + + function isWithinContentEditableFalse(elm) { + return isContentEditableFalse(getContentEditableRoot(editor.getBody(), elm)); + } + + function nativeControlSelect(e) { + var target = e.srcElement; + + if (isWithinContentEditableFalse(target)) { + preventDefault(e); + return; + } + + if (target != selectedElm) { + editor.fire('ObjectSelected', { + target: target + }); + detachResizeStartListener(); + + if (target.id.indexOf('mceResizeHandle') === 0) { + e.returnValue = false; + return; + } + + if (target.nodeName == 'IMG' || target.nodeName == 'TABLE') { + hideResizeRect(); + selectedElm = target; + attachEvent(target, 'resizestart', resizeNativeStart); + } + } + } + + function detachResizeStartListener() { + detachEvent(selectedElm, 'resizestart', resizeNativeStart); + } + + function unbindResizeHandleEvents() { + for (var name in resizeHandles) { + var handle = resizeHandles[name]; + + if (handle.elm) { + dom.unbind(handle.elm); + delete handle.elm; + } + } + } + + function disableGeckoResize() { + try { + // Disable object resizing on Gecko + editor.getDoc().execCommand('enableObjectResizing', false, false); + } catch (ex) { + // Ignore + } + } + + function controlSelect(elm) { + var ctrlRng; + + if (!isIE) { + return; + } + + ctrlRng = editableDoc.body.createControlRange(); + + try { + ctrlRng.addElement(elm); + ctrlRng.select(); + return true; + } catch (ex) { + // Ignore since the element can't be control selected for example a P tag + } + } + + editor.on('init', function() { + if (isIE) { + // Hide the resize rect on resize and reselect the image + editor.on('ObjectResized', function(e) { + if (e.target.nodeName != 'TABLE') { + hideResizeRect(); + controlSelect(e.target); + } + }); + + attachEvent(rootElement, 'controlselect', nativeControlSelect); + + editor.on('mousedown', function(e) { + lastMouseDownEvent = e; + }); + } else { + disableGeckoResize(); + + // Sniff sniff, hard to feature detect this stuff + if (Env.ie >= 11) { + // Needs to be mousedown for drag/drop to work on IE 11 + // Needs to be click on Edge to properly select images + editor.on('mousedown click', function(e) { + var target = e.target, + nodeName = target.nodeName; + + if (!resizeStarted && /^(TABLE|IMG|HR)$/.test(nodeName) && !isWithinContentEditableFalse(target)) { + if (e.button !== 2) { + editor.selection.select(target, nodeName == 'TABLE'); + } + + // Only fire once since nodeChange is expensive + if (e.type == 'mousedown') { + editor.nodeChanged(); + } + } + }); + + editor.dom.bind(rootElement, 'mscontrolselect', function(e) { + function delayedSelect(node) { + Delay.setEditorTimeout(editor, function() { + editor.selection.select(node); + }); + } + + if (isWithinContentEditableFalse(e.target)) { + e.preventDefault(); + delayedSelect(e.target); + return; + } + + if (/^(TABLE|IMG|HR)$/.test(e.target.nodeName)) { + e.preventDefault(); + + // This moves the selection from being a control selection to a text like selection like in WebKit #6753 + // TODO: Fix this the day IE works like other browsers without this nasty native ugly control selections. + if (e.target.tagName == 'IMG') { + delayedSelect(e.target); + } + } + }); + } + } + + var throttledUpdateResizeRect = Delay.throttle(function(e) { + if (!editor.composing) { + updateResizeRect(e); + } + }); + + editor.on('nodechange ResizeEditor ResizeWindow drop', throttledUpdateResizeRect); + + // Update resize rect while typing in a table + editor.on('keyup compositionend', function(e) { + // Don't update the resize rect while composing since it blows away the IME see: #2710 + if (selectedElm && selectedElm.nodeName == "TABLE") { + throttledUpdateResizeRect(e); + } + }); + + editor.on('hide blur', hideResizeRect); + editor.on('contextmenu', Fun.curry(contextMenuSelectImage, editor)); + + // Hide rect on focusout since it would float on top of windows otherwise + //editor.on('focusout', hideResizeRect); + }); + + editor.on('remove', unbindResizeHandleEvents); + + function destroy() { + selectedElm = selectedElmGhost = null; + + if (isIE) { + detachResizeStartListener(); + detachEvent(rootElement, 'controlselect', nativeControlSelect); + } + } + + return { + isResizable: isResizable, + showResizeRect: showResizeRect, + hideResizeRect: hideResizeRect, + updateResizeRect: updateResizeRect, + controlSelect: controlSelect, + destroy: destroy + }; + }; + } + ); + + /** + * Fun.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Functional utility class. + * + * @private + * @class tinymce.util.Fun + */ + define( + 'tinymce.core.util.Fun', [], + function() { + var slice = [].slice; + + function constant(value) { + return function() { + return value; + }; + } + + function negate(predicate) { + return function(x) { + return !predicate(x); + }; + } + + function compose(f, g) { + return function(x) { + return f(g(x)); + }; + } + + function or() { + var args = slice.call(arguments); + + return function(x) { + for (var i = 0; i < args.length; i++) { + if (args[i](x)) { + return true; + } + } + + return false; + }; + } + + function and() { + var args = slice.call(arguments); + + return function(x) { + for (var i = 0; i < args.length; i++) { + if (!args[i](x)) { + return false; + } + } + + return true; + }; + } + + function curry(fn) { + var args = slice.call(arguments); + + if (args.length - 1 >= fn.length) { + return fn.apply(this, args.slice(1)); + } + + return function() { + var tempArgs = args.concat([].slice.call(arguments)); + return curry.apply(this, tempArgs); + }; + } + + function noop() {} + + return { + constant: constant, + negate: negate, + and: and, + or: or, + curry: curry, + compose: compose, + noop: noop + }; + } + ); + /** + * CaretCandidate.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module contains logic for handling caret candidates. A caret candidate is + * for example text nodes, images, input elements, cE=false elements etc. + * + * @private + * @class tinymce.caret.CaretCandidate + */ + define( + 'tinymce.core.caret.CaretCandidate', [ + "tinymce.core.dom.NodeType", + "tinymce.core.util.Arr", + "tinymce.core.caret.CaretContainer" + ], + function(NodeType, Arr, CaretContainer) { + var isContentEditableTrue = NodeType.isContentEditableTrue, + isContentEditableFalse = NodeType.isContentEditableFalse, + isBr = NodeType.isBr, + isText = NodeType.isText, + isInvalidTextElement = NodeType.matchNodeNames('script style textarea'), + isAtomicInline = NodeType.matchNodeNames('img input textarea hr iframe video audio object'), + isTable = NodeType.matchNodeNames('table'), + isCaretContainer = CaretContainer.isCaretContainer; + + function isCaretCandidate(node) { + if (isCaretContainer(node)) { + return false; + } + + if (isText(node)) { + if (isInvalidTextElement(node.parentNode)) { + return false; + } + + return true; + } + + return isAtomicInline(node) || isBr(node) || isTable(node) || isContentEditableFalse(node); + } + + function isInEditable(node, rootNode) { + for (node = node.parentNode; node && node != rootNode; node = node.parentNode) { + if (isContentEditableFalse(node)) { + return false; + } + + if (isContentEditableTrue(node)) { + return true; + } + } + + return true; + } + + function isAtomicContentEditableFalse(node) { + if (!isContentEditableFalse(node)) { + return false; + } + + return Arr.reduce(node.getElementsByTagName('*'), function(result, elm) { + return result || isContentEditableTrue(elm); + }, false) !== true; + } + + function isAtomic(node) { + return isAtomicInline(node) || isAtomicContentEditableFalse(node); + } + + function isEditableCaretCandidate(node, rootNode) { + return isCaretCandidate(node) && isInEditable(node, rootNode); + } + + return { + isCaretCandidate: isCaretCandidate, + isInEditable: isInEditable, + isAtomic: isAtomic, + isEditableCaretCandidate: isEditableCaretCandidate + }; + } + ); + /** + * ExtendingChar.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class contains logic for detecting extending characters. + * + * @private + * @class tinymce.text.ExtendingChar + * @example + * var isExtending = ExtendingChar.isExtendingChar('a'); + */ + define( + 'tinymce.core.text.ExtendingChar', [], + function() { + // Generated from: http://www.unicode.org/Public/UNIDATA/DerivedCoreProperties.txt + // Only includes the characters in that fit into UCS-2 16 bit + var extendingChars = new RegExp( + "[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A" + + "\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0" + + "\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08E3-\u0902\u093A\u093C" + + "\u0941-\u0948\u094D\u0951-\u0957\u0962-\u0963\u0981\u09BC\u09BE\u09C1-\u09C4\u09CD\u09D7\u09E2-\u09E3" + + "\u0A01-\u0A02\u0A3C\u0A41-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A51\u0A70-\u0A71\u0A75\u0A81-\u0A82\u0ABC" + + "\u0AC1-\u0AC5\u0AC7-\u0AC8\u0ACD\u0AE2-\u0AE3\u0B01\u0B3C\u0B3E\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B57" + + "\u0B62-\u0B63\u0B82\u0BBE\u0BC0\u0BCD\u0BD7\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56" + + "\u0C62-\u0C63\u0C81\u0CBC\u0CBF\u0CC2\u0CC6\u0CCC-\u0CCD\u0CD5-\u0CD6\u0CE2-\u0CE3\u0D01\u0D3E\u0D41-\u0D44" + + "\u0D4D\u0D57\u0D62-\u0D63\u0DCA\u0DCF\u0DD2-\u0DD4\u0DD6\u0DDF\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9" + + "\u0EBB-\u0EBC\u0EC8-\u0ECD\u0F18-\u0F19\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86-\u0F87\u0F8D-\u0F97" + + "\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039-\u103A\u103D-\u103E\u1058-\u1059\u105E-\u1060\u1071-\u1074" + + "\u1082\u1085-\u1086\u108D\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17B4-\u17B5" + + "\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u18A9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193B\u1A17-\u1A18" + + "\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABD\u1ABE\u1B00-\u1B03\u1B34" + + "\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80-\u1B81\u1BA2-\u1BA5\u1BA8-\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8-\u1BE9" + + "\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8-\u1CF9" + + "\u1DC0-\u1DF5\u1DFC-\u1DFF\u200C-\u200D\u20D0-\u20DC\u20DD-\u20E0\u20E1\u20E2-\u20E4\u20E5-\u20F0\u2CEF-\u2CF1" + + "\u2D7F\u2DE0-\u2DFF\u302A-\u302D\u302E-\u302F\u3099-\u309A\uA66F\uA670-\uA672\uA674-\uA67D\uA69E-\uA69F\uA6F0-\uA6F1" + + "\uA802\uA806\uA80B\uA825-\uA826\uA8C4\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC" + + "\uA9E5\uAA29-\uAA2E\uAA31-\uAA32\uAA35-\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7-\uAAB8\uAABE-\uAABF\uAAC1" + + "\uAAEC-\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFF9E-\uFF9F]" + ); + + function isExtendingChar(ch) { + return typeof ch == "string" && ch.charCodeAt(0) >= 768 && extendingChars.test(ch); + } + + return { + isExtendingChar: isExtendingChar + }; + } + ); + /** + * CaretPosition.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module contains logic for creating caret positions within a document a caretposition + * is similar to a DOMRange object but it doesn't have two endpoints and is also more lightweight + * since it's now updated live when the DOM changes. + * + * @private + * @class tinymce.caret.CaretPosition + * @example + * var caretPos1 = new CaretPosition(container, offset); + * var caretPos2 = CaretPosition.fromRangeStart(someRange); + */ + define( + 'tinymce.core.caret.CaretPosition', [ + "tinymce.core.util.Fun", + "tinymce.core.dom.NodeType", + "tinymce.core.dom.DOMUtils", + "tinymce.core.dom.RangeUtils", + "tinymce.core.caret.CaretCandidate", + "tinymce.core.geom.ClientRect", + "tinymce.core.text.ExtendingChar" + ], + function(Fun, NodeType, DOMUtils, RangeUtils, CaretCandidate, ClientRect, ExtendingChar) { + var isElement = NodeType.isElement, + isCaretCandidate = CaretCandidate.isCaretCandidate, + isBlock = NodeType.matchStyleValues('display', 'block table'), + isFloated = NodeType.matchStyleValues('float', 'left right'), + isValidElementCaretCandidate = Fun.and(isElement, isCaretCandidate, Fun.negate(isFloated)), + isNotPre = Fun.negate(NodeType.matchStyleValues('white-space', 'pre pre-line pre-wrap')), + isText = NodeType.isText, + isBr = NodeType.isBr, + nodeIndex = DOMUtils.nodeIndex, + resolveIndex = RangeUtils.getNode; + + function createRange(doc) { + return "createRange" in doc ? doc.createRange() : DOMUtils.DOM.createRng(); + } + + function isWhiteSpace(chr) { + return chr && /[\r\n\t ]/.test(chr); + } + + function isHiddenWhiteSpaceRange(range) { + var container = range.startContainer, + offset = range.startOffset, + text; + + if (isWhiteSpace(range.toString()) && isNotPre(container.parentNode)) { + text = container.data; + + if (isWhiteSpace(text[offset - 1]) || isWhiteSpace(text[offset + 1])) { + return true; + } + } + + return false; + } + + function getCaretPositionClientRects(caretPosition) { + var clientRects = [], + beforeNode, node; + + // Hack for older WebKit versions that doesn't + // support getBoundingClientRect on BR elements + function getBrClientRect(brNode) { + var doc = brNode.ownerDocument, + rng = createRange(doc), + nbsp = doc.createTextNode('\u00a0'), + parentNode = brNode.parentNode, + clientRect; + + parentNode.insertBefore(nbsp, brNode); + rng.setStart(nbsp, 0); + rng.setEnd(nbsp, 1); + clientRect = ClientRect.clone(rng.getBoundingClientRect()); + parentNode.removeChild(nbsp); + + return clientRect; + } + + function getBoundingClientRect(item) { + var clientRect, clientRects; + + clientRects = item.getClientRects(); + if (clientRects.length > 0) { + clientRect = ClientRect.clone(clientRects[0]); + } else { + clientRect = ClientRect.clone(item.getBoundingClientRect()); + } + + if (isBr(item) && clientRect.left === 0) { + return getBrClientRect(item); + } + + return clientRect; + } + + function collapseAndInflateWidth(clientRect, toStart) { + clientRect = ClientRect.collapse(clientRect, toStart); + clientRect.width = 1; + clientRect.right = clientRect.left + 1; + + return clientRect; + } + + function addUniqueAndValidRect(clientRect) { + if (clientRect.height === 0) { + return; + } + + if (clientRects.length > 0) { + if (ClientRect.isEqual(clientRect, clientRects[clientRects.length - 1])) { + return; + } + } + + clientRects.push(clientRect); + } + + function addCharacterOffset(container, offset) { + var range = createRange(container.ownerDocument); + + if (offset < container.data.length) { + if (ExtendingChar.isExtendingChar(container.data[offset])) { + return clientRects; + } + + // WebKit returns two client rects for a position after an extending + // character a\uxxx|b so expand on "b" and collapse to start of "b" box + if (ExtendingChar.isExtendingChar(container.data[offset - 1])) { + range.setStart(container, offset); + range.setEnd(container, offset + 1); + + if (!isHiddenWhiteSpaceRange(range)) { + addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(range), false)); + return clientRects; + } + } + } + + if (offset > 0) { + range.setStart(container, offset - 1); + range.setEnd(container, offset); + + if (!isHiddenWhiteSpaceRange(range)) { + addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(range), false)); + } + } + + if (offset < container.data.length) { + range.setStart(container, offset); + range.setEnd(container, offset + 1); + + if (!isHiddenWhiteSpaceRange(range)) { + addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(range), true)); + } + } + } + + if (isText(caretPosition.container())) { + addCharacterOffset(caretPosition.container(), caretPosition.offset()); + return clientRects; + } + + if (isElement(caretPosition.container())) { + if (caretPosition.isAtEnd()) { + node = resolveIndex(caretPosition.container(), caretPosition.offset()); + if (isText(node)) { + addCharacterOffset(node, node.data.length); + } + + if (isValidElementCaretCandidate(node) && !isBr(node)) { + addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(node), false)); + } + } else { + node = resolveIndex(caretPosition.container(), caretPosition.offset()); + if (isText(node)) { + addCharacterOffset(node, 0); + } + + if (isValidElementCaretCandidate(node) && caretPosition.isAtEnd()) { + addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(node), false)); + return clientRects; + } + + beforeNode = resolveIndex(caretPosition.container(), caretPosition.offset() - 1); + if (isValidElementCaretCandidate(beforeNode) && !isBr(beforeNode)) { + if (isBlock(beforeNode) || isBlock(node) || !isValidElementCaretCandidate(node)) { + addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(beforeNode), false)); + } + } + + if (isValidElementCaretCandidate(node)) { + addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(node), true)); + } + } + } + + return clientRects; + } + + /** + * Represents a location within the document by a container and an offset. + * + * @constructor + * @param {Node} container Container node. + * @param {Number} offset Offset within that container node. + * @param {Array} clientRects Optional client rects array for the position. + */ + function CaretPosition(container, offset, clientRects) { + function isAtStart() { + if (isText(container)) { + return offset === 0; + } + + return offset === 0; + } + + function isAtEnd() { + if (isText(container)) { + return offset >= container.data.length; + } + + return offset >= container.childNodes.length; + } + + function toRange() { + var range; + + range = createRange(container.ownerDocument); + range.setStart(container, offset); + range.setEnd(container, offset); + + return range; + } + + function getClientRects() { + if (!clientRects) { + clientRects = getCaretPositionClientRects(new CaretPosition(container, offset)); + } + + return clientRects; + } + + function isVisible() { + return getClientRects().length > 0; + } + + function isEqual(caretPosition) { + return caretPosition && container === caretPosition.container() && offset === caretPosition.offset(); + } + + function getNode(before) { + return resolveIndex(container, before ? offset - 1 : offset); + } + + return { + /** + * Returns the container node. + * + * @method container + * @return {Node} Container node. + */ + container: Fun.constant(container), + + /** + * Returns the offset within the container node. + * + * @method offset + * @return {Number} Offset within the container node. + */ + offset: Fun.constant(offset), + + /** + * Returns a range out of a the caret position. + * + * @method toRange + * @return {DOMRange} range for the caret position. + */ + toRange: toRange, + + /** + * Returns the client rects for the caret position. Might be multiple rects between + * block elements. + * + * @method getClientRects + * @return {Array} Array of client rects. + */ + getClientRects: getClientRects, + + /** + * Returns true if the caret location is visible/displayed on screen. + * + * @method isVisible + * @return {Boolean} true/false if the position is visible or not. + */ + isVisible: isVisible, + + /** + * Returns true if the caret location is at the beginning of text node or container. + * + * @method isVisible + * @return {Boolean} true/false if the position is at the beginning. + */ + isAtStart: isAtStart, + + /** + * Returns true if the caret location is at the end of text node or container. + * + * @method isVisible + * @return {Boolean} true/false if the position is at the end. + */ + isAtEnd: isAtEnd, + + /** + * Compares the caret position to another caret position. This will only compare the + * container and offset not it's visual position. + * + * @method isEqual + * @param {tinymce.caret.CaretPosition} caretPosition Caret position to compare with. + * @return {Boolean} true if the caret positions are equal. + */ + isEqual: isEqual, + + /** + * Returns the closest resolved node from a node index. That means if you have an offset after the + * last node in a container it will return that last node. + * + * @method getNode + * @return {Node} Node that is closest to the index. + */ + getNode: getNode + }; + } + + /** + * Creates a caret position from the start of a range. + * + * @method fromRangeStart + * @param {DOMRange} range DOM Range to create caret position from. + * @return {tinymce.caret.CaretPosition} Caret position from the start of DOM range. + */ + CaretPosition.fromRangeStart = function(range) { + return new CaretPosition(range.startContainer, range.startOffset); + }; + + /** + * Creates a caret position from the end of a range. + * + * @method fromRangeEnd + * @param {DOMRange} range DOM Range to create caret position from. + * @return {tinymce.caret.CaretPosition} Caret position from the end of DOM range. + */ + CaretPosition.fromRangeEnd = function(range) { + return new CaretPosition(range.endContainer, range.endOffset); + }; + + /** + * Creates a caret position from a node and places the offset after it. + * + * @method after + * @param {Node} node Node to get caret position from. + * @return {tinymce.caret.CaretPosition} Caret position from the node. + */ + CaretPosition.after = function(node) { + return new CaretPosition(node.parentNode, nodeIndex(node) + 1); + }; + + /** + * Creates a caret position from a node and places the offset before it. + * + * @method before + * @param {Node} node Node to get caret position from. + * @return {tinymce.caret.CaretPosition} Caret position from the node. + */ + CaretPosition.before = function(node) { + return new CaretPosition(node.parentNode, nodeIndex(node)); + }; + + CaretPosition.isAtStart = function(pos) { + return pos ? pos.isAtStart() : false; + }; + + CaretPosition.isAtEnd = function(pos) { + return pos ? pos.isAtEnd() : false; + }; + + CaretPosition.isTextPosition = function(pos) { + return pos ? NodeType.isText(pos.container()) : false; + }; + + return CaretPosition; + } + ); + /** + * CaretBookmark.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module creates or resolves xpath like string representation of a CaretPositions. + * + * The format is a / separated list of chunks with: + * <element|text()>[index|after|before] + * + * For example: + * p[0]/b[0]/text()[0],1 = <p><b>a|c</b></p> + * p[0]/img[0],before = <p>|<img></p> + * p[0]/img[0],after = <p><img>|</p> + * + * @private + * @static + * @class tinymce.caret.CaretBookmark + * @example + * var bookmark = CaretBookmark.create(rootElm, CaretPosition.before(rootElm.firstChild)); + * var caretPosition = CaretBookmark.resolve(bookmark); + */ + define( + 'tinymce.core.caret.CaretBookmark', [ + 'tinymce.core.dom.NodeType', + 'tinymce.core.dom.DOMUtils', + 'tinymce.core.util.Fun', + 'tinymce.core.util.Arr', + 'tinymce.core.caret.CaretPosition' + ], + function(NodeType, DomUtils, Fun, Arr, CaretPosition) { + var isText = NodeType.isText, + isBogus = NodeType.isBogus, + nodeIndex = DomUtils.nodeIndex; + + function normalizedParent(node) { + var parentNode = node.parentNode; + + if (isBogus(parentNode)) { + return normalizedParent(parentNode); + } + + return parentNode; + } + + function getChildNodes(node) { + if (!node) { + return []; + } + + return Arr.reduce(node.childNodes, function(result, node) { + if (isBogus(node) && node.nodeName != 'BR') { + result = result.concat(getChildNodes(node)); + } else { + result.push(node); + } + + return result; + }, []); + } + + function normalizedTextOffset(textNode, offset) { + while ((textNode = textNode.previousSibling)) { + if (!isText(textNode)) { + break; + } + + offset += textNode.data.length; + } + + return offset; + } + + function equal(targetValue) { + return function(value) { + return targetValue === value; + }; + } + + function normalizedNodeIndex(node) { + var nodes, index, numTextFragments; + + nodes = getChildNodes(normalizedParent(node)); + index = Arr.findIndex(nodes, equal(node), node); + nodes = nodes.slice(0, index + 1); + numTextFragments = Arr.reduce(nodes, function(result, node, i) { + if (isText(node) && isText(nodes[i - 1])) { + result++; + } + + return result; + }, 0); + + nodes = Arr.filter(nodes, NodeType.matchNodeNames(node.nodeName)); + index = Arr.findIndex(nodes, equal(node), node); + + return index - numTextFragments; + } + + function createPathItem(node) { + var name; + + if (isText(node)) { + name = 'text()'; + } else { + name = node.nodeName.toLowerCase(); + } + + return name + '[' + normalizedNodeIndex(node) + ']'; + } + + function parentsUntil(rootNode, node, predicate) { + var parents = []; + + for (node = node.parentNode; node != rootNode; node = node.parentNode) { + if (predicate && predicate(node)) { + break; + } + + parents.push(node); + } + + return parents; + } + + function create(rootNode, caretPosition) { + var container, offset, path = [], + outputOffset, childNodes, parents; + + container = caretPosition.container(); + offset = caretPosition.offset(); + + if (isText(container)) { + outputOffset = normalizedTextOffset(container, offset); + } else { + childNodes = container.childNodes; + if (offset >= childNodes.length) { + outputOffset = 'after'; + offset = childNodes.length - 1; + } else { + outputOffset = 'before'; + } + + container = childNodes[offset]; + } + + path.push(createPathItem(container)); + parents = parentsUntil(rootNode, container); + parents = Arr.filter(parents, Fun.negate(NodeType.isBogus)); + path = path.concat(Arr.map(parents, function(node) { + return createPathItem(node); + })); + + return path.reverse().join('/') + ',' + outputOffset; + } + + function resolvePathItem(node, name, index) { + var nodes = getChildNodes(node); + + nodes = Arr.filter(nodes, function(node, index) { + return !isText(node) || !isText(nodes[index - 1]); + }); + + nodes = Arr.filter(nodes, NodeType.matchNodeNames(name)); + return nodes[index]; + } + + function findTextPosition(container, offset) { + var node = container, + targetOffset = 0, + dataLen; + + while (isText(node)) { + dataLen = node.data.length; + + if (offset >= targetOffset && offset <= targetOffset + dataLen) { + container = node; + offset = offset - targetOffset; + break; + } + + if (!isText(node.nextSibling)) { + container = node; + offset = dataLen; + break; + } + + targetOffset += dataLen; + node = node.nextSibling; + } + + if (offset > container.data.length) { + offset = container.data.length; + } + + return new CaretPosition(container, offset); + } + + function resolve(rootNode, path) { + var parts, container, offset; + + if (!path) { + return null; + } + + parts = path.split(','); + path = parts[0].split('/'); + offset = parts.length > 1 ? parts[1] : 'before'; + + container = Arr.reduce(path, function(result, value) { + value = /([\w\-\(\)]+)\[([0-9]+)\]/.exec(value); + if (!value) { + return null; + } + + if (value[1] === 'text()') { + value[1] = '#text'; + } + + return resolvePathItem(result, value[1], parseInt(value[2], 10)); + }, rootNode); + + if (!container) { + return null; + } + + if (!isText(container)) { + if (offset === 'after') { + offset = nodeIndex(container) + 1; + } else { + offset = nodeIndex(container); + } + + return new CaretPosition(container.parentNode, offset); + } + + return findTextPosition(container, parseInt(offset, 10)); + } + + return { + /** + * Create a xpath bookmark location for the specified caret position. + * + * @method create + * @param {Node} rootNode Root node to create bookmark within. + * @param {tinymce.caret.CaretPosition} caretPosition Caret position within the root node. + * @return {String} String xpath like location of caret position. + */ + create: create, + + /** + * Resolves a xpath like bookmark location to the a caret position. + * + * @method resolve + * @param {Node} rootNode Root node to resolve xpath bookmark within. + * @param {String} bookmark Bookmark string to resolve. + * @return {tinymce.caret.CaretPosition} Caret position resolved from xpath like bookmark. + */ + resolve: resolve + }; + } + ); + /** + * BookmarkManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class handles selection bookmarks. + * + * @class tinymce.dom.BookmarkManager + */ + define( + 'tinymce.core.dom.BookmarkManager', [ + 'tinymce.core.caret.CaretBookmark', + 'tinymce.core.caret.CaretContainer', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.dom.NodeType', + 'tinymce.core.dom.RangeUtils', + 'tinymce.core.Env', + 'tinymce.core.text.Zwsp', + 'tinymce.core.util.Tools' + ], + function(CaretBookmark, CaretContainer, CaretPosition, NodeType, RangeUtils, Env, Zwsp, Tools) { + var isContentEditableFalse = NodeType.isContentEditableFalse; + + var getNormalizedTextOffset = function(container, offset) { + var node, trimmedOffset; + + trimmedOffset = Zwsp.trim(container.data.slice(0, offset)).length; + for (node = container.previousSibling; node && node.nodeType === 3; node = node.previousSibling) { + trimmedOffset += Zwsp.trim(node.data).length; + } + + return trimmedOffset; + }; + + /** + * Constructs a new BookmarkManager instance for a specific selection instance. + * + * @constructor + * @method BookmarkManager + * @param {tinymce.dom.Selection} selection Selection instance to handle bookmarks for. + */ + function BookmarkManager(selection) { + var dom = selection.dom; + + /** + * Returns a bookmark location for the current selection. This bookmark object + * can then be used to restore the selection after some content modification to the document. + * + * @method getBookmark + * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex. + * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization. + * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection. + * @example + * // Stores a bookmark of the current selection + * var bm = tinymce.activeEditor.selection.getBookmark(); + * + * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content'); + * + * // Restore the selection bookmark + * tinymce.activeEditor.selection.moveToBookmark(bm); + */ + this.getBookmark = function(type, normalized) { + var rng, rng2, id, collapsed, name, element, chr = '', + styles; + + function findIndex(name, element) { + var count = 0; + + Tools.each(dom.select(name), function(node) { + if (node.getAttribute('data-mce-bogus') === 'all') { + return; + } + + if (node == element) { + return false; + } + + count++; + }); + + return count; + } + + function normalizeTableCellSelection(rng) { + function moveEndPoint(start) { + var container, offset, childNodes, prefix = start ? 'start' : 'end'; + + container = rng[prefix + 'Container']; + offset = rng[prefix + 'Offset']; + + if (container.nodeType == 1 && container.nodeName == "TR") { + childNodes = container.childNodes; + container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)]; + if (container) { + offset = start ? 0 : container.childNodes.length; + rng['set' + (start ? 'Start' : 'End')](container, offset); + } + } + } + + moveEndPoint(true); + moveEndPoint(); + + return rng; + } + + function getLocation(rng) { + var root = dom.getRoot(), + bookmark = {}; + + function getPoint(rng, start) { + var container = rng[start ? 'startContainer' : 'endContainer'], + offset = rng[start ? 'startOffset' : 'endOffset'], + point = [], + childNodes, after = 0; + + if (container.nodeType === 3) { + point.push(normalized ? getNormalizedTextOffset(container, offset) : offset); + } else { + childNodes = container.childNodes; + + if (offset >= childNodes.length && childNodes.length) { + after = 1; + offset = Math.max(0, childNodes.length - 1); + } + + point.push(dom.nodeIndex(childNodes[offset], normalized) + after); + } + + for (; container && container != root; container = container.parentNode) { + point.push(dom.nodeIndex(container, normalized)); + } + + return point; + } + + bookmark.start = getPoint(rng, true); + + if (!selection.isCollapsed()) { + bookmark.end = getPoint(rng); + } + + return bookmark; + } + + function findAdjacentContentEditableFalseElm(rng) { + function findSibling(node, offset) { + var sibling; + + if (NodeType.isElement(node)) { + node = RangeUtils.getNode(node, offset); + if (isContentEditableFalse(node)) { + return node; + } + } + + if (CaretContainer.isCaretContainer(node)) { + if (NodeType.isText(node) && CaretContainer.isCaretContainerBlock(node)) { + node = node.parentNode; + } + + sibling = node.previousSibling; + if (isContentEditableFalse(sibling)) { + return sibling; + } + + sibling = node.nextSibling; + if (isContentEditableFalse(sibling)) { + return sibling; + } + } + } + + return findSibling(rng.startContainer, rng.startOffset) || findSibling(rng.endContainer, rng.endOffset); + } + + if (type == 2) { + element = selection.getNode(); + name = element ? element.nodeName : null; + rng = selection.getRng(); + + if (isContentEditableFalse(element) || name == 'IMG') { + return { + name: name, + index: findIndex(name, element) + }; + } + + if (selection.tridentSel) { + return selection.tridentSel.getBookmark(type); + } + + element = findAdjacentContentEditableFalseElm(rng); + if (element) { + name = element.tagName; + return { + name: name, + index: findIndex(name, element) + }; + } + + return getLocation(rng); + } + + if (type == 3) { + rng = selection.getRng(); + + return { + start: CaretBookmark.create(dom.getRoot(), CaretPosition.fromRangeStart(rng)), + end: CaretBookmark.create(dom.getRoot(), CaretPosition.fromRangeEnd(rng)) + }; + } + + // Handle simple range + if (type) { + return { + rng: selection.getRng() + }; + } + + rng = selection.getRng(); + id = dom.uniqueId(); + collapsed = selection.isCollapsed(); + styles = 'overflow:hidden;line-height:0px'; + + // Explorer method + if (rng.duplicate || rng.item) { + // Text selection + if (!rng.item) { + rng2 = rng.duplicate(); + + try { + // Insert start marker + rng.collapse(); + rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>'); + + // Insert end marker + if (!collapsed) { + rng2.collapse(false); + + // Detect the empty space after block elements in IE and move the + // end back one character <p></p>] becomes <p>]</p> + rng.moveToElementText(rng2.parentElement()); + if (rng.compareEndPoints('StartToEnd', rng2) === 0) { + rng2.move('character', -1); + } + + rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>'); + } + } catch (ex) { + // IE might throw unspecified error so lets ignore it + return null; + } + } else { + // Control selection + element = rng.item(0); + name = element.nodeName; + + return { + name: name, + index: findIndex(name, element) + }; + } + } else { + element = selection.getNode(); + name = element.nodeName; + if (name == 'IMG') { + return { + name: name, + index: findIndex(name, element) + }; + } + + // W3C method + rng2 = normalizeTableCellSelection(rng.cloneRange()); + + // Insert end marker + if (!collapsed) { + rng2.collapse(false); + rng2.insertNode(dom.create('span', { + 'data-mce-type': "bookmark", + id: id + '_end', + style: styles + }, chr)); + } + + rng = normalizeTableCellSelection(rng); + rng.collapse(true); + rng.insertNode(dom.create('span', { + 'data-mce-type': "bookmark", + id: id + '_start', + style: styles + }, chr)); + } + + selection.moveToBookmark({ + id: id, + keep: 1 + }); + + return { + id: id + }; + }; + + /** + * Restores the selection to the specified bookmark. + * + * @method moveToBookmark + * @param {Object} bookmark Bookmark to restore selection from. + * @return {Boolean} true/false if it was successful or not. + * @example + * // Stores a bookmark of the current selection + * var bm = tinymce.activeEditor.selection.getBookmark(); + * + * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content'); + * + * // Restore the selection bookmark + * tinymce.activeEditor.selection.moveToBookmark(bm); + */ + this.moveToBookmark = function(bookmark) { + var rng, root, startContainer, endContainer, startOffset, endOffset; + + function setEndPoint(start) { + var point = bookmark[start ? 'start' : 'end'], + i, node, offset, children; + + if (point) { + offset = point[0]; + + // Find container node + for (node = root, i = point.length - 1; i >= 1; i--) { + children = node.childNodes; + + if (point[i] > children.length - 1) { + return; + } + + node = children[point[i]]; + } + + // Move text offset to best suitable location + if (node.nodeType === 3) { + offset = Math.min(point[0], node.nodeValue.length); + } + + // Move element offset to best suitable location + if (node.nodeType === 1) { + offset = Math.min(point[0], node.childNodes.length); + } + + // Set offset within container node + if (start) { + rng.setStart(node, offset); + } else { + rng.setEnd(node, offset); + } + } + + return true; + } + + function restoreEndPoint(suffix) { + var marker = dom.get(bookmark.id + '_' + suffix), + node, idx, next, prev, keep = bookmark.keep; + + if (marker) { + node = marker.parentNode; + + if (suffix == 'start') { + if (!keep) { + idx = dom.nodeIndex(marker); + } else { + node = marker.firstChild; + idx = 1; + } + + startContainer = endContainer = node; + startOffset = endOffset = idx; + } else { + if (!keep) { + idx = dom.nodeIndex(marker); + } else { + node = marker.firstChild; + idx = 1; + } + + endContainer = node; + endOffset = idx; + } + + if (!keep) { + prev = marker.previousSibling; + next = marker.nextSibling; + + // Remove all marker text nodes + Tools.each(Tools.grep(marker.childNodes), function(node) { + if (node.nodeType == 3) { + node.nodeValue = node.nodeValue.replace(/\uFEFF/g, ''); + } + }); + + // Remove marker but keep children if for example contents where inserted into the marker + // Also remove duplicated instances of the marker for example by a + // split operation or by WebKit auto split on paste feature + while ((marker = dom.get(bookmark.id + '_' + suffix))) { + dom.remove(marker, 1); + } + + // If siblings are text nodes then merge them unless it's Opera since it some how removes the node + // and we are sniffing since adding a lot of detection code for a browser with 3% of the market + // isn't worth the effort. Sorry, Opera but it's just a fact + if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !Env.opera) { + idx = prev.nodeValue.length; + prev.appendData(next.nodeValue); + dom.remove(next); + + if (suffix == 'start') { + startContainer = endContainer = prev; + startOffset = endOffset = idx; + } else { + endContainer = prev; + endOffset = idx; + } + } + } + } + } + + function addBogus(node) { + // Adds a bogus BR element for empty block elements + if (dom.isBlock(node) && !node.innerHTML && !Env.ie) { + node.innerHTML = '<br data-mce-bogus="1" />'; + } + + return node; + } + + function resolveCaretPositionBookmark() { + var rng, pos; + + rng = dom.createRng(); + pos = CaretBookmark.resolve(dom.getRoot(), bookmark.start); + rng.setStart(pos.container(), pos.offset()); + + pos = CaretBookmark.resolve(dom.getRoot(), bookmark.end); + rng.setEnd(pos.container(), pos.offset()); + + return rng; + } + + if (bookmark) { + if (Tools.isArray(bookmark.start)) { + rng = dom.createRng(); + root = dom.getRoot(); + + if (selection.tridentSel) { + return selection.tridentSel.moveToBookmark(bookmark); + } + + if (setEndPoint(true) && setEndPoint()) { + selection.setRng(rng); + } + } else if (typeof bookmark.start == 'string') { + selection.setRng(resolveCaretPositionBookmark(bookmark)); + } else if (bookmark.id) { + // Restore start/end points + restoreEndPoint('start'); + restoreEndPoint('end'); + + if (startContainer) { + rng = dom.createRng(); + rng.setStart(addBogus(startContainer), startOffset); + rng.setEnd(addBogus(endContainer), endOffset); + selection.setRng(rng); + } + } else if (bookmark.name) { + selection.select(dom.select(bookmark.name)[bookmark.index]); + } else if (bookmark.rng) { + selection.setRng(bookmark.rng); + } + } + }; + } + + /** + * Returns true/false if the specified node is a bookmark node or not. + * + * @static + * @method isBookmarkNode + * @param {DOMNode} node DOM Node to check if it's a bookmark node or not. + * @return {Boolean} true/false if the node is a bookmark node or not. + */ + BookmarkManager.isBookmarkNode = function(node) { + return node && node.tagName === 'SPAN' && node.getAttribute('data-mce-type') === 'bookmark'; + }; + + return BookmarkManager; + } + ); + define( + 'ephox.katamari.api.Global', + + [], + + function() { + // Use window object as the global if it's available since CSP will block script evals + if (typeof window !== 'undefined') { + return window; + } else { + return Function('return this;')(); + } + } + ); + + + define( + 'ephox.katamari.api.Resolve', + + [ + 'ephox.katamari.api.Global' + ], + + function(Global) { + /** path :: ([String], JsObj?) -> JsObj */ + var path = function(parts, scope) { + var o = scope !== undefined ? scope : Global; + for (var i = 0; i < parts.length && o !== undefined && o !== null; ++i) + o = o[parts[i]]; + return o; + }; + + /** resolve :: (String, JsObj?) -> JsObj */ + var resolve = function(p, scope) { + var parts = p.split('.'); + return path(parts, scope); + }; + + /** step :: (JsObj, String) -> JsObj */ + var step = function(o, part) { + if (o[part] === undefined || o[part] === null) + o[part] = {}; + return o[part]; + }; + + /** forge :: ([String], JsObj?) -> JsObj */ + var forge = function(parts, target) { + var o = target !== undefined ? target : Global; + for (var i = 0; i < parts.length; ++i) + o = step(o, parts[i]); + return o; + }; + + /** namespace :: (String, JsObj?) -> JsObj */ + var namespace = function(name, target) { + var parts = name.split('.'); + return forge(parts, target); + }; + + return { + path: path, + resolve: resolve, + forge: forge, + namespace: namespace + }; + } + ); + + + define( + 'ephox.sand.util.Global', + + [ + 'ephox.katamari.api.Resolve' + ], + + function(Resolve) { + var unsafe = function(name, scope) { + return Resolve.resolve(name, scope); + }; + + var getOrDie = function(name, scope) { + var actual = unsafe(name, scope); + + if (actual === undefined) throw name + ' not available on this browser'; + return actual; + }; + + return { + getOrDie: getOrDie + }; + } + ); + define( + 'ephox.sand.api.Node', + + [ + 'ephox.sand.util.Global' + ], + + function(Global) { + /* + * MDN says (yes) for IE, but it's undefined on IE8 + */ + var node = function() { + var f = Global.getOrDie('Node'); + return f; + }; + + /* + * Most of numerosity doesn't alter the methods on the object. + * We're making an exception for Node, because bitwise and is so easy to get wrong. + * + * Might be nice to ADT this at some point instead of having individual methods. + */ + + var compareDocumentPosition = function(a, b, match) { + // Returns: 0 if e1 and e2 are the same node, or a bitmask comparing the positions + // of nodes e1 and e2 in their documents. See the URL below for bitmask interpretation + // https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition + return (a.compareDocumentPosition(b) & match) !== 0; + }; + + var documentPositionPreceding = function(a, b) { + return compareDocumentPosition(a, b, node().DOCUMENT_POSITION_PRECEDING); + }; + + var documentPositionContainedBy = function(a, b) { + return compareDocumentPosition(a, b, node().DOCUMENT_POSITION_CONTAINED_BY); + }; + + return { + documentPositionPreceding: documentPositionPreceding, + documentPositionContainedBy: documentPositionContainedBy + }; + } + ); + define( + 'ephox.katamari.api.Thunk', + + [], + + function() { + + var cached = function(f) { + var called = false; + var r; + return function() { + if (!called) { + called = true; + r = f.apply(null, arguments); + } + return r; + }; + }; + + return { + cached: cached + }; + } + ); + + defineGlobal("global!Number", Number); + define( + 'ephox.sand.detect.Version', + + [ + 'ephox.katamari.api.Arr', + 'global!Number', + 'global!String' + ], + + function(Arr, Number, String) { + var firstMatch = function(regexes, s) { + for (var i = 0; i < regexes.length; i++) { + var x = regexes[i]; + if (x.test(s)) return x; + } + return undefined; + }; + + var find = function(regexes, agent) { + var r = firstMatch(regexes, agent); + if (!r) return { + major: 0, + minor: 0 + }; + var group = function(i) { + return Number(agent.replace(r, '$' + i)); + }; + return nu(group(1), group(2)); + }; + + var detect = function(versionRegexes, agent) { + var cleanedAgent = String(agent).toLowerCase(); + + if (versionRegexes.length === 0) return unknown(); + return find(versionRegexes, cleanedAgent); + }; + + var unknown = function() { + return nu(0, 0); + }; + + var nu = function(major, minor) { + return { + major: major, + minor: minor + }; + }; + + return { + nu: nu, + detect: detect, + unknown: unknown + }; + } + ); + define( + 'ephox.sand.core.Browser', + + [ + 'ephox.katamari.api.Fun', + 'ephox.sand.detect.Version' + ], + + function(Fun, Version) { + var edge = 'Edge'; + var chrome = 'Chrome'; + var ie = 'IE'; + var opera = 'Opera'; + var firefox = 'Firefox'; + var safari = 'Safari'; + + var isBrowser = function(name, current) { + return function() { + return current === name; + }; + }; + + var unknown = function() { + return nu({ + current: undefined, + version: Version.unknown() + }); + }; + + var nu = function(info) { + var current = info.current; + var version = info.version; + + return { + current: current, + version: version, + + // INVESTIGATE: Rename to Edge ? + isEdge: isBrowser(edge, current), + isChrome: isBrowser(chrome, current), + // NOTE: isIe just looks too weird + isIE: isBrowser(ie, current), + isOpera: isBrowser(opera, current), + isFirefox: isBrowser(firefox, current), + isSafari: isBrowser(safari, current) + }; + }; + + return { + unknown: unknown, + nu: nu, + edge: Fun.constant(edge), + chrome: Fun.constant(chrome), + ie: Fun.constant(ie), + opera: Fun.constant(opera), + firefox: Fun.constant(firefox), + safari: Fun.constant(safari) + }; + } + ); + define( + 'ephox.sand.core.OperatingSystem', + + [ + 'ephox.katamari.api.Fun', + 'ephox.sand.detect.Version' + ], + + function(Fun, Version) { + var windows = 'Windows'; + var ios = 'iOS'; + var android = 'Android'; + var linux = 'Linux'; + var osx = 'OSX'; + var solaris = 'Solaris'; + var freebsd = 'FreeBSD'; + + // Though there is a bit of dupe with this and Browser, trying to + // reuse code makes it much harder to follow and change. + var isOS = function(name, current) { + return function() { + return current === name; + }; + }; + + var unknown = function() { + return nu({ + current: undefined, + version: Version.unknown() + }); + }; + + var nu = function(info) { + var current = info.current; + var version = info.version; + + return { + current: current, + version: version, + + isWindows: isOS(windows, current), + // TODO: Fix capitalisation + isiOS: isOS(ios, current), + isAndroid: isOS(android, current), + isOSX: isOS(osx, current), + isLinux: isOS(linux, current), + isSolaris: isOS(solaris, current), + isFreeBSD: isOS(freebsd, current) + }; + }; + + return { + unknown: unknown, + nu: nu, + + windows: Fun.constant(windows), + ios: Fun.constant(ios), + android: Fun.constant(android), + linux: Fun.constant(linux), + osx: Fun.constant(osx), + solaris: Fun.constant(solaris), + freebsd: Fun.constant(freebsd) + }; + } + ); + define( + 'ephox.sand.detect.DeviceType', + + [ + 'ephox.katamari.api.Fun' + ], + + function(Fun) { + return function(os, browser, userAgent) { + var isiPad = os.isiOS() && /ipad/i.test(userAgent) === true; + var isiPhone = os.isiOS() && !isiPad; + var isAndroid3 = os.isAndroid() && os.version.major === 3; + var isAndroid4 = os.isAndroid() && os.version.major === 4; + var isTablet = isiPad || isAndroid3 || (isAndroid4 && /mobile/i.test(userAgent) === true); + var isTouch = os.isiOS() || os.isAndroid(); + var isPhone = isTouch && !isTablet; + + var iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false; + + return { + isiPad: Fun.constant(isiPad), + isiPhone: Fun.constant(isiPhone), + isTablet: Fun.constant(isTablet), + isPhone: Fun.constant(isPhone), + isTouch: Fun.constant(isTouch), + isAndroid: os.isAndroid, + isiOS: os.isiOS, + isWebView: Fun.constant(iOSwebview) + }; + }; + } + ); + define( + 'ephox.sand.detect.UaString', + + [ + 'ephox.katamari.api.Arr', + 'ephox.sand.detect.Version', + 'global!String' + ], + + function(Arr, Version, String) { + var detect = function(candidates, userAgent) { + var agent = String(userAgent).toLowerCase(); + return Arr.find(candidates, function(candidate) { + return candidate.search(agent); + }); + }; + + // They (browser and os) are the same at the moment, but they might + // not stay that way. + var detectBrowser = function(browsers, userAgent) { + return detect(browsers, userAgent).map(function(browser) { + var version = Version.detect(browser.versionRegexes, userAgent); + return { + current: browser.name, + version: version + }; + }); + }; + + var detectOs = function(oses, userAgent) { + return detect(oses, userAgent).map(function(os) { + var version = Version.detect(os.versionRegexes, userAgent); + return { + current: os.name, + version: version + }; + }); + }; + + return { + detectBrowser: detectBrowser, + detectOs: detectOs + }; + } + ); + define( + 'ephox.katamari.str.StrAppend', + + [ + + ], + + function() { + var addToStart = function(str, prefix) { + return prefix + str; + }; + + var addToEnd = function(str, suffix) { + return str + suffix; + }; + + var removeFromStart = function(str, numChars) { + return str.substring(numChars); + }; + + var removeFromEnd = function(str, numChars) { + return str.substring(0, str.length - numChars); + }; + + return { + addToStart: addToStart, + addToEnd: addToEnd, + removeFromStart: removeFromStart, + removeFromEnd: removeFromEnd + }; + } + ); + define( + 'ephox.katamari.str.StringParts', + + [ + 'ephox.katamari.api.Option', + 'global!Error' + ], + + function(Option, Error) { + /** Return the first 'count' letters from 'str'. +- * e.g. first("abcde", 2) === "ab" +- */ + var first = function(str, count) { + return str.substr(0, count); + }; + + /** Return the last 'count' letters from 'str'. + * e.g. last("abcde", 2) === "de" + */ + var last = function(str, count) { + return str.substr(str.length - count, str.length); + }; + + var head = function(str) { + return str === '' ? Option.none() : Option.some(str.substr(0, 1)); + }; + + var tail = function(str) { + return str === '' ? Option.none() : Option.some(str.substring(1)); + }; + + return { + first: first, + last: last, + head: head, + tail: tail + }; + } + ); + define( + 'ephox.katamari.api.Strings', + + [ + 'ephox.katamari.str.StrAppend', + 'ephox.katamari.str.StringParts', + 'global!Error' + ], + + function(StrAppend, StringParts, Error) { + var checkRange = function(str, substr, start) { + if (substr === '') return true; + if (str.length < substr.length) return false; + var x = str.substr(start, start + substr.length); + return x === substr; + }; + + /** Given a string and object, perform template-replacements on the string, as specified by the object. + * Any template fields of the form ${name} are replaced by the string or number specified as obj["name"] + * Based on Douglas Crockford's 'supplant' method for template-replace of strings. Uses different template format. + */ + var supplant = function(str, obj) { + var isStringOrNumber = function(a) { + var t = typeof a; + return t === 'string' || t === 'number'; + }; + + return str.replace(/\${([^{}]*)}/g, + function(a, b) { + var value = obj[b]; + return isStringOrNumber(value) ? value : a; + } + ); + }; + + var removeLeading = function(str, prefix) { + return startsWith(str, prefix) ? StrAppend.removeFromStart(str, prefix.length) : str; + }; + + var removeTrailing = function(str, prefix) { + return endsWith(str, prefix) ? StrAppend.removeFromEnd(str, prefix.length) : str; + }; + + var ensureLeading = function(str, prefix) { + return startsWith(str, prefix) ? str : StrAppend.addToStart(str, prefix); + }; + + var ensureTrailing = function(str, prefix) { + return endsWith(str, prefix) ? str : StrAppend.addToEnd(str, prefix); + }; + + var contains = function(str, substr) { + return str.indexOf(substr) !== -1; + }; + + var capitalize = function(str) { + return StringParts.head(str).bind(function(head) { + return StringParts.tail(str).map(function(tail) { + return head.toUpperCase() + tail; + }); + }).getOr(str); + }; + + /** Does 'str' start with 'prefix'? + * Note: all strings start with the empty string. + * More formally, for all strings x, startsWith(x, ""). + * This is so that for all strings x and y, startsWith(y + x, y) + */ + var startsWith = function(str, prefix) { + return checkRange(str, prefix, 0); + }; + + /** Does 'str' end with 'suffix'? + * Note: all strings end with the empty string. + * More formally, for all strings x, endsWith(x, ""). + * This is so that for all strings x and y, endsWith(x + y, y) + */ + var endsWith = function(str, suffix) { + return checkRange(str, suffix, str.length - suffix.length); + }; + + + /** removes all leading and trailing spaces */ + var trim = function(str) { + return str.replace(/^\s+|\s+$/g, ''); + }; + + var lTrim = function(str) { + return str.replace(/^\s+/g, ''); + }; + + var rTrim = function(str) { + return str.replace(/\s+$/g, ''); + }; + + return { + supplant: supplant, + startsWith: startsWith, + removeLeading: removeLeading, + removeTrailing: removeTrailing, + ensureLeading: ensureLeading, + ensureTrailing: ensureTrailing, + endsWith: endsWith, + contains: contains, + trim: trim, + lTrim: lTrim, + rTrim: rTrim, + capitalize: capitalize + }; + } + ); + + define( + 'ephox.sand.info.PlatformInfo', + + [ + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Strings' + ], + + function(Fun, Strings) { + var normalVersionRegex = /.*?version\/\ ?([0-9]+)\.([0-9]+).*/; + + var checkContains = function(target) { + return function(uastring) { + return Strings.contains(uastring, target); + }; + }; + + var browsers = [{ + name: 'Edge', + versionRegexes: [/.*?edge\/ ?([0-9]+)\.([0-9]+)$/], + search: function(uastring) { + var monstrosity = Strings.contains(uastring, 'edge/') && Strings.contains(uastring, 'chrome') && Strings.contains(uastring, 'safari') && Strings.contains(uastring, 'applewebkit'); + return monstrosity; + } + }, + { + name: 'Chrome', + versionRegexes: [/.*?chrome\/([0-9]+)\.([0-9]+).*/, normalVersionRegex], + search: function(uastring) { + return Strings.contains(uastring, 'chrome') && !Strings.contains(uastring, 'chromeframe'); + } + }, + { + name: 'IE', + versionRegexes: [/.*?msie\ ?([0-9]+)\.([0-9]+).*/, /.*?rv:([0-9]+)\.([0-9]+).*/], + search: function(uastring) { + return Strings.contains(uastring, 'msie') || Strings.contains(uastring, 'trident'); + } + }, + // INVESTIGATE: Is this still the Opera user agent? + { + name: 'Opera', + versionRegexes: [normalVersionRegex, /.*?opera\/([0-9]+)\.([0-9]+).*/], + search: checkContains('opera') + }, + { + name: 'Firefox', + versionRegexes: [/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/], + search: checkContains('firefox') + }, + { + name: 'Safari', + versionRegexes: [normalVersionRegex, /.*?cpu os ([0-9]+)_([0-9]+).*/], + search: function(uastring) { + return (Strings.contains(uastring, 'safari') || Strings.contains(uastring, 'mobile/')) && Strings.contains(uastring, 'applewebkit'); + } + } + ]; + + var oses = [{ + name: 'Windows', + search: checkContains('win'), + versionRegexes: [/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/] + }, + { + name: 'iOS', + search: function(uastring) { + return Strings.contains(uastring, 'iphone') || Strings.contains(uastring, 'ipad'); + }, + versionRegexes: [/.*?version\/\ ?([0-9]+)\.([0-9]+).*/, /.*cpu os ([0-9]+)_([0-9]+).*/, /.*cpu iphone os ([0-9]+)_([0-9]+).*/] + }, + { + name: 'Android', + search: checkContains('android'), + versionRegexes: [/.*?android\ ?([0-9]+)\.([0-9]+).*/] + }, + { + name: 'OSX', + search: checkContains('os x'), + versionRegexes: [/.*?os\ x\ ?([0-9]+)_([0-9]+).*/] + }, + { + name: 'Linux', + search: checkContains('linux'), + versionRegexes: [] + }, + { + name: 'Solaris', + search: checkContains('sunos'), + versionRegexes: [] + }, + { + name: 'FreeBSD', + search: checkContains('freebsd'), + versionRegexes: [] + } + ]; + + return { + browsers: Fun.constant(browsers), + oses: Fun.constant(oses) + }; + } + ); + define( + 'ephox.sand.core.PlatformDetection', + + [ + 'ephox.sand.core.Browser', + 'ephox.sand.core.OperatingSystem', + 'ephox.sand.detect.DeviceType', + 'ephox.sand.detect.UaString', + 'ephox.sand.info.PlatformInfo' + ], + + function(Browser, OperatingSystem, DeviceType, UaString, PlatformInfo) { + var detect = function(userAgent) { + var browsers = PlatformInfo.browsers(); + var oses = PlatformInfo.oses(); + + var browser = UaString.detectBrowser(browsers, userAgent).fold( + Browser.unknown, + Browser.nu + ); + var os = UaString.detectOs(oses, userAgent).fold( + OperatingSystem.unknown, + OperatingSystem.nu + ); + var deviceType = DeviceType(os, browser, userAgent); + + return { + browser: browser, + os: os, + deviceType: deviceType + }; + }; + + return { + detect: detect + }; + } + ); + defineGlobal("global!navigator", navigator); + define( + 'ephox.sand.api.PlatformDetection', + + [ + 'ephox.katamari.api.Thunk', + 'ephox.sand.core.PlatformDetection', + 'global!navigator' + ], + + function(Thunk, PlatformDetection, navigator) { + var detect = Thunk.cached(function() { + var userAgent = navigator.userAgent; + return PlatformDetection.detect(userAgent); + }); + + return { + detect: detect + }; + } + ); + define("global!console", [], function() { + if (typeof console === "undefined") console = { + log: function() {} + }; + return console; + }); + defineGlobal("global!document", document); + define( + 'ephox.sugar.api.node.Element', + + [ + 'ephox.katamari.api.Fun', + 'global!Error', + 'global!console', + 'global!document' + ], + + function(Fun, Error, console, document) { + var fromHtml = function(html, scope) { + var doc = scope || document; + var div = doc.createElement('div'); + div.innerHTML = html; + if (!div.hasChildNodes() || div.childNodes.length > 1) { + console.error('HTML does not have a single root node', html); + throw 'HTML must have a single root node'; + } + return fromDom(div.childNodes[0]); + }; + + var fromTag = function(tag, scope) { + var doc = scope || document; + var node = doc.createElement(tag); + return fromDom(node); + }; + + var fromText = function(text, scope) { + var doc = scope || document; + var node = doc.createTextNode(text); + return fromDom(node); + }; + + var fromDom = function(node) { + if (node === null || node === undefined) throw new Error('Node cannot be null or undefined'); + return { + dom: Fun.constant(node) + }; + }; + + return { + fromHtml: fromHtml, + fromTag: fromTag, + fromText: fromText, + fromDom: fromDom + }; + } + ); + + define( + 'ephox.sugar.api.node.NodeTypes', + + [ + + ], + + function() { + return { + ATTRIBUTE: 2, + CDATA_SECTION: 4, + COMMENT: 8, + DOCUMENT: 9, + DOCUMENT_TYPE: 10, + DOCUMENT_FRAGMENT: 11, + ELEMENT: 1, + TEXT: 3, + PROCESSING_INSTRUCTION: 7, + ENTITY_REFERENCE: 5, + ENTITY: 6, + NOTATION: 12 + }; + } + ); + define( + 'ephox.sugar.api.search.Selectors', + + [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Option', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.api.node.NodeTypes', + 'global!Error', + 'global!document' + ], + + function(Arr, Option, Element, NodeTypes, Error, document) { + /* + * There's a lot of code here; the aim is to allow the browser to optimise constant comparisons, + * instead of doing object lookup feature detection on every call + */ + var STANDARD = 0; + var MSSTANDARD = 1; + var WEBKITSTANDARD = 2; + var FIREFOXSTANDARD = 3; + + var selectorType = (function() { + var test = document.createElement('span'); + // As of Chrome 34 / Safari 7.1 / FireFox 34, everyone except IE has the unprefixed function. + // Still check for the others, but do it last. + return test.matches !== undefined ? STANDARD : + test.msMatchesSelector !== undefined ? MSSTANDARD : + test.webkitMatchesSelector !== undefined ? WEBKITSTANDARD : + test.mozMatchesSelector !== undefined ? FIREFOXSTANDARD : + -1; + })(); + + + var ELEMENT = NodeTypes.ELEMENT; + var DOCUMENT = NodeTypes.DOCUMENT; + + var is = function(element, selector) { + var elem = element.dom(); + if (elem.nodeType !== ELEMENT) return false; // documents have querySelector but not matches + + // As of Chrome 34 / Safari 7.1 / FireFox 34, everyone except IE has the unprefixed function. + // Still check for the others, but do it last. + else if (selectorType === STANDARD) return elem.matches(selector); + else if (selectorType === MSSTANDARD) return elem.msMatchesSelector(selector); + else if (selectorType === WEBKITSTANDARD) return elem.webkitMatchesSelector(selector); + else if (selectorType === FIREFOXSTANDARD) return elem.mozMatchesSelector(selector); + else throw new Error('Browser lacks native selectors'); // unfortunately we can't throw this on startup :( + }; + + var bypassSelector = function(dom) { + // Only elements and documents support querySelector + return dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT || + // IE fix for complex queries on empty nodes: http://jsfiddle.net/spyder/fv9ptr5L/ + dom.childElementCount === 0; + }; + + var all = function(selector, scope) { + var base = scope === undefined ? document : scope.dom(); + return bypassSelector(base) ? [] : Arr.map(base.querySelectorAll(selector), Element.fromDom); + }; + + var one = function(selector, scope) { + var base = scope === undefined ? document : scope.dom(); + return bypassSelector(base) ? Option.none() : Option.from(base.querySelector(selector)).map(Element.fromDom); + }; + + return { + all: all, + is: is, + one: one + }; + } + ); + + define( + 'ephox.sugar.api.dom.Compare', + + [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Fun', + 'ephox.sand.api.Node', + 'ephox.sand.api.PlatformDetection', + 'ephox.sugar.api.search.Selectors' + ], + + function(Arr, Fun, Node, PlatformDetection, Selectors) { + + var eq = function(e1, e2) { + return e1.dom() === e2.dom(); + }; + + var isEqualNode = function(e1, e2) { + return e1.dom().isEqualNode(e2.dom()); + }; + + var member = function(element, elements) { + return Arr.exists(elements, Fun.curry(eq, element)); + }; + + // DOM contains() method returns true if e1===e2, we define our contains() to return false (a node does not contain itself). + var regularContains = function(e1, e2) { + var d1 = e1.dom(), + d2 = e2.dom(); + return d1 === d2 ? false : d1.contains(d2); + }; + + var ieContains = function(e1, e2) { + // IE only implements the contains() method for Element nodes. + // It fails for Text nodes, so implement it using compareDocumentPosition() + // https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect + // Note that compareDocumentPosition returns CONTAINED_BY if 'e2 *is_contained_by* e1': + // Also, compareDocumentPosition defines a node containing itself as false. + return Node.documentPositionContainedBy(e1.dom(), e2.dom()); + }; + + var browser = PlatformDetection.detect().browser; + + // Returns: true if node e1 contains e2, otherwise false. + // (returns false if e1===e2: A node does not contain itself). + var contains = browser.isIE() ? ieContains : regularContains; + + return { + eq: eq, + isEqualNode: isEqualNode, + member: member, + contains: contains, + + // Only used by DomUniverse. Remove (or should Selectors.is move here?) + is: Selectors.is + }; + } + ); + + /** + * ScrollIntoView.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.dom.ScrollIntoView', [ + 'tinymce.core.dom.NodeType' + ], + function(NodeType) { + var getPos = function(elm) { + var x = 0, + y = 0; + + var offsetParent = elm; + while (offsetParent && offsetParent.nodeType) { + x += offsetParent.offsetLeft || 0; + y += offsetParent.offsetTop || 0; + offsetParent = offsetParent.offsetParent; + } + + return { + x: x, + y: y + }; + }; + + var fireScrollIntoViewEvent = function(editor, elm, alignToTop) { + var scrollEvent = { + elm: elm, + alignToTop: alignToTop + }; + editor.fire('scrollIntoView', scrollEvent); + return scrollEvent.isDefaultPrevented(); + }; + + var scrollIntoView = function(editor, elm, alignToTop) { + var y, viewPort, dom = editor.dom, + root = dom.getRoot(), + viewPortY, viewPortH, offsetY = 0; + + if (fireScrollIntoViewEvent(editor, elm, alignToTop)) { + return; + } + + if (!NodeType.isElement(elm)) { + return; + } + + if (alignToTop === false) { + offsetY = elm.offsetHeight; + } + + if (root.nodeName !== 'BODY') { + var scrollContainer = editor.selection.getScrollContainer(); + if (scrollContainer) { + y = getPos(elm).y - getPos(scrollContainer).y + offsetY; + viewPortH = scrollContainer.clientHeight; + viewPortY = scrollContainer.scrollTop; + if (y < viewPortY || y + 25 > viewPortY + viewPortH) { + scrollContainer.scrollTop = y < viewPortY ? y : y - viewPortH + 25; + } + + return; + } + } + + viewPort = dom.getViewPort(editor.getWin()); + y = dom.getPos(elm).y + offsetY; + viewPortY = viewPort.y; + viewPortH = viewPort.h; + if (y < viewPort.y || y + 25 > viewPortY + viewPortH) { + editor.getWin().scrollTo(0, y < viewPortY ? y : y - viewPortH + 25); + } + }; + + return { + scrollIntoView: scrollIntoView + }; + } + ); + + /** + * TridentSelection.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Selection class for old explorer versions. This one fakes the + * native selection object available on modern browsers. + * + * @private + * @class tinymce.dom.TridentSelection + */ + define( + 'tinymce.core.dom.TridentSelection', [], + function() { + function Selection(selection) { + var self = this, + dom = selection.dom, + FALSE = false; + + function getPosition(rng, start) { + var checkRng, startIndex = 0, + endIndex, inside, + children, child, offset, index, position = -1, + parent; + + // Setup test range, collapse it and get the parent + checkRng = rng.duplicate(); + checkRng.collapse(start); + parent = checkRng.parentElement(); + + // Check if the selection is within the right document + if (parent.ownerDocument !== selection.dom.doc) { + return; + } + + // IE will report non editable elements as it's parent so look for an editable one + while (parent.contentEditable === "false") { + parent = parent.parentNode; + } + + // If parent doesn't have any children then return that we are inside the element + if (!parent.hasChildNodes()) { + return { + node: parent, + inside: 1 + }; + } + + // Setup node list and endIndex + children = parent.children; + endIndex = children.length - 1; + + // Perform a binary search for the position + while (startIndex <= endIndex) { + index = Math.floor((startIndex + endIndex) / 2); + + // Move selection to node and compare the ranges + child = children[index]; + checkRng.moveToElementText(child); + position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng); + + // Before/after or an exact match + if (position > 0) { + endIndex = index - 1; + } else if (position < 0) { + startIndex = index + 1; + } else { + return { + node: child + }; + } + } + + // Check if child position is before or we didn't find a position + if (position < 0) { + // No element child was found use the parent element and the offset inside that + if (!child) { + checkRng.moveToElementText(parent); + checkRng.collapse(true); + child = parent; + inside = true; + } else { + checkRng.collapse(false); + } + + // Walk character by character in text node until we hit the selected range endpoint, + // hit the end of document or parent isn't the right one + // We need to walk char by char since rng.text or rng.htmlText will trim line endings + offset = 0; + while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) { + if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) { + break; + } + + offset++; + } + } else { + // Child position is after the selection endpoint + checkRng.collapse(true); + + // Walk character by character in text node until we hit the selected range endpoint, hit + // the end of document or parent isn't the right one + offset = 0; + while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) { + if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) { + break; + } + + offset++; + } + } + + return { + node: child, + position: position, + offset: offset, + inside: inside + }; + } + + // Returns a W3C DOM compatible range object by using the IE Range API + function getRange() { + var ieRange = selection.getRng(), + domRange = dom.createRng(), + element, collapsed, tmpRange, element2, bookmark; + + // If selection is outside the current document just return an empty range + element = ieRange.item ? ieRange.item(0) : ieRange.parentElement(); + if (element.ownerDocument != dom.doc) { + return domRange; + } + + collapsed = selection.isCollapsed(); + + // Handle control selection + if (ieRange.item) { + domRange.setStart(element.parentNode, dom.nodeIndex(element)); + domRange.setEnd(domRange.startContainer, domRange.startOffset + 1); + + return domRange; + } + + function findEndPoint(start) { + var endPoint = getPosition(ieRange, start), + container, offset, textNodeOffset = 0, + sibling, undef, nodeValue; + + container = endPoint.node; + offset = endPoint.offset; + + if (endPoint.inside && !container.hasChildNodes()) { + domRange[start ? 'setStart' : 'setEnd'](container, 0); + return; + } + + if (offset === undef) { + domRange[start ? 'setStartBefore' : 'setEndAfter'](container); + return; + } + + if (endPoint.position < 0) { + sibling = endPoint.inside ? container.firstChild : container.nextSibling; + + if (!sibling) { + domRange[start ? 'setStartAfter' : 'setEndAfter'](container); + return; + } + + if (!offset) { + if (sibling.nodeType == 3) { + domRange[start ? 'setStart' : 'setEnd'](sibling, 0); + } else { + domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling); + } + + return; + } + + // Find the text node and offset + while (sibling) { + if (sibling.nodeType == 3) { + nodeValue = sibling.nodeValue; + textNodeOffset += nodeValue.length; + + // We are at or passed the position we where looking for + if (textNodeOffset >= offset) { + container = sibling; + textNodeOffset -= offset; + textNodeOffset = nodeValue.length - textNodeOffset; + break; + } + } + + sibling = sibling.nextSibling; + } + } else { + // Find the text node and offset + sibling = container.previousSibling; + + if (!sibling) { + return domRange[start ? 'setStartBefore' : 'setEndBefore'](container); + } + + // If there isn't any text to loop then use the first position + if (!offset) { + if (container.nodeType == 3) { + domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length); + } else { + domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling); + } + + return; + } + + while (sibling) { + if (sibling.nodeType == 3) { + textNodeOffset += sibling.nodeValue.length; + + // We are at or passed the position we where looking for + if (textNodeOffset >= offset) { + container = sibling; + textNodeOffset -= offset; + break; + } + } + + sibling = sibling.previousSibling; + } + } + + domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset); + } + + try { + // Find start point + findEndPoint(true); + + // Find end point if needed + if (!collapsed) { + findEndPoint(); + } + } catch (ex) { + // IE has a nasty bug where text nodes might throw "invalid argument" when you + // access the nodeValue or other properties of text nodes. This seems to happen when + // text nodes are split into two nodes by a delete/backspace call. + // So let us detect and try to fix it. + if (ex.number == -2147024809) { + // Get the current selection + bookmark = self.getBookmark(2); + + // Get start element + tmpRange = ieRange.duplicate(); + tmpRange.collapse(true); + element = tmpRange.parentElement(); + + // Get end element + if (!collapsed) { + tmpRange = ieRange.duplicate(); + tmpRange.collapse(false); + element2 = tmpRange.parentElement(); + element2.innerHTML = element2.innerHTML; + } + + // Remove the broken elements + element.innerHTML = element.innerHTML; + + // Restore the selection + self.moveToBookmark(bookmark); + + // Since the range has moved we need to re-get it + ieRange = selection.getRng(); + + // Find start point + findEndPoint(true); + + // Find end point if needed + if (!collapsed) { + findEndPoint(); + } + } else { + throw ex; // Throw other errors + } + } + + return domRange; + } + + this.getBookmark = function(type) { + var rng = selection.getRng(), + bookmark = {}; + + function getIndexes(node) { + var parent, root, children, i, indexes = []; + + parent = node.parentNode; + root = dom.getRoot().parentNode; + + while (parent != root && parent.nodeType !== 9) { + children = parent.children; + + i = children.length; + while (i--) { + if (node === children[i]) { + indexes.push(i); + break; + } + } + + node = parent; + parent = parent.parentNode; + } + + return indexes; + } + + function getBookmarkEndPoint(start) { + var position; + + position = getPosition(rng, start); + if (position) { + return { + position: position.position, + offset: position.offset, + indexes: getIndexes(position.node), + inside: position.inside + }; + } + } + + // Non ubstructive bookmark + if (type === 2) { + // Handle text selection + if (!rng.item) { + bookmark.start = getBookmarkEndPoint(true); + + if (!selection.isCollapsed()) { + bookmark.end = getBookmarkEndPoint(); + } + } else { + bookmark.start = { + ctrl: true, + indexes: getIndexes(rng.item(0)) + }; + } + } + + return bookmark; + }; + + this.moveToBookmark = function(bookmark) { + var rng, body = dom.doc.body; + + function resolveIndexes(indexes) { + var node, i, idx, children; + + node = dom.getRoot(); + for (i = indexes.length - 1; i >= 0; i--) { + children = node.children; + idx = indexes[i]; + + if (idx <= children.length - 1) { + node = children[idx]; + } + } + + return node; + } + + function setBookmarkEndPoint(start) { + var endPoint = bookmark[start ? 'start' : 'end'], + moveLeft, moveRng, undef, offset; + + if (endPoint) { + moveLeft = endPoint.position > 0; + + moveRng = body.createTextRange(); + moveRng.moveToElementText(resolveIndexes(endPoint.indexes)); + + offset = endPoint.offset; + if (offset !== undef) { + moveRng.collapse(endPoint.inside || moveLeft); + moveRng.moveStart('character', moveLeft ? -offset : offset); + } else { + moveRng.collapse(start); + } + + rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng); + + if (start) { + rng.collapse(true); + } + } + } + + if (bookmark.start) { + if (bookmark.start.ctrl) { + rng = body.createControlRange(); + rng.addElement(resolveIndexes(bookmark.start.indexes)); + rng.select(); + } else { + rng = body.createTextRange(); + setBookmarkEndPoint(true); + setBookmarkEndPoint(); + rng.select(); + } + } + }; + + this.addRange = function(rng) { + var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling, + doc = selection.dom.doc, + body = doc.body, + nativeRng, ctrlElm; + + function setEndPoint(start) { + var container, offset, marker, tmpRng, nodes; + + marker = dom.create('a'); + container = start ? startContainer : endContainer; + offset = start ? startOffset : endOffset; + tmpRng = ieRng.duplicate(); + + if (container == doc || container == doc.documentElement) { + container = body; + offset = 0; + } + + if (container.nodeType == 3) { + container.parentNode.insertBefore(marker, container); + tmpRng.moveToElementText(marker); + tmpRng.moveStart('character', offset); + dom.remove(marker); + ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng); + } else { + nodes = container.childNodes; + + if (nodes.length) { + if (offset >= nodes.length) { + dom.insertAfter(marker, nodes[nodes.length - 1]); + } else { + container.insertBefore(marker, nodes[offset]); + } + + tmpRng.moveToElementText(marker); + } else if (container.canHaveHTML) { + // Empty node selection for example <div>|</div> + // Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open + container.innerHTML = '<span></span>'; + marker = container.firstChild; + tmpRng.moveToElementText(marker); + tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason + } + + ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng); + dom.remove(marker); + } + } + + // Setup some shorter versions + startContainer = rng.startContainer; + startOffset = rng.startOffset; + endContainer = rng.endContainer; + endOffset = rng.endOffset; + ieRng = body.createTextRange(); + + // If single element selection then try making a control selection out of it + if (startContainer == endContainer && startContainer.nodeType == 1) { + // Trick to place the caret inside an empty block element like <p></p> + if (startOffset == endOffset && !startContainer.hasChildNodes()) { + if (startContainer.canHaveHTML) { + // Check if previous sibling is an empty block if it is then we need to render it + // IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236 + // Example this: <p></p><p>|</p> would become this: <p>|</p><p></p> + sibling = startContainer.previousSibling; + if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) { + sibling.innerHTML = ''; + } else { + sibling = null; + } + + startContainer.innerHTML = '<span></span><span></span>'; + ieRng.moveToElementText(startContainer.lastChild); + ieRng.select(); + dom.doc.selection.clear(); + startContainer.innerHTML = ''; + + if (sibling) { + sibling.innerHTML = ''; + } + return; + } + + startOffset = dom.nodeIndex(startContainer); + startContainer = startContainer.parentNode; + } + + if (startOffset == endOffset - 1) { + try { + ctrlElm = startContainer.childNodes[startOffset]; + ctrlRng = body.createControlRange(); + ctrlRng.addElement(ctrlElm); + ctrlRng.select(); + + // Check if the range produced is on the correct element and is a control range + // On IE 8 it will select the parent contentEditable container if you select an inner element see: #5398 + nativeRng = selection.getRng(); + if (nativeRng.item && ctrlElm === nativeRng.item(0)) { + return; + } + } catch (ex) { + // Ignore + } + } + } + + // Set start/end point of selection + setEndPoint(true); + setEndPoint(); + + // Select the new range and scroll it into view + ieRng.select(); + }; + + // Expose range method + this.getRangeAt = getRange; + } + + return Selection; + } + ); + + define( + 'ephox.katamari.api.Type', + + [ + 'global!Array', + 'global!String' + ], + + function(Array, String) { + var typeOf = function(x) { + if (x === null) return 'null'; + var t = typeof x; + if (t === 'object' && Array.prototype.isPrototypeOf(x)) return 'array'; + if (t === 'object' && String.prototype.isPrototypeOf(x)) return 'string'; + return t; + }; + + var isType = function(type) { + return function(value) { + return typeOf(value) === type; + }; + }; + + return { + isString: isType('string'), + isObject: isType('object'), + isArray: isType('array'), + isNull: isType('null'), + isBoolean: isType('boolean'), + isUndefined: isType('undefined'), + isFunction: isType('function'), + isNumber: isType('number') + }; + } + ); + + + define( + 'ephox.katamari.data.Immutable', + + [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Fun', + 'global!Array', + 'global!Error' + ], + + function(Arr, Fun, Array, Error) { + return function() { + var fields = arguments; + return function( /* values */ ) { + // Don't use array slice(arguments), makes the whole function unoptimisable on Chrome + var values = new Array(arguments.length); + for (var i = 0; i < values.length; i++) values[i] = arguments[i]; + + if (fields.length !== values.length) + throw new Error('Wrong number of arguments to struct. Expected "[' + fields.length + ']", got ' + values.length + ' arguments'); + + var struct = {}; + Arr.each(fields, function(name, i) { + struct[name] = Fun.constant(values[i]); + }); + return struct; + }; + }; + } + ); + + define( + 'ephox.katamari.api.Obj', + + [ + 'ephox.katamari.api.Option', + 'global!Object' + ], + + function(Option, Object) { + // There are many variations of Object iteration that are faster than the 'for-in' style: + // http://jsperf.com/object-keys-iteration/107 + // + // Use the native keys if it is available (IE9+), otherwise fall back to manually filtering + var keys = (function() { + var fastKeys = Object.keys; + + // This technically means that 'each' and 'find' on IE8 iterate through the object twice. + // This code doesn't run on IE8 much, so it's an acceptable tradeoff. + // If it becomes a problem we can always duplicate the feature detection inside each and find as well. + var slowKeys = function(o) { + var r = []; + for (var i in o) { + if (o.hasOwnProperty(i)) { + r.push(i); + } + } + return r; + }; + + return fastKeys === undefined ? slowKeys : fastKeys; + })(); + + + var each = function(obj, f) { + var props = keys(obj); + for (var k = 0, len = props.length; k < len; k++) { + var i = props[k]; + var x = obj[i]; + f(x, i, obj); + } + }; + + /** objectMap :: (JsObj(k, v), (v, k, JsObj(k, v) -> x)) -> JsObj(k, x) */ + var objectMap = function(obj, f) { + return tupleMap(obj, function(x, i, obj) { + return { + k: i, + v: f(x, i, obj) + }; + }); + }; + + /** tupleMap :: (JsObj(k, v), (v, k, JsObj(k, v) -> { k: x, v: y })) -> JsObj(x, y) */ + var tupleMap = function(obj, f) { + var r = {}; + each(obj, function(x, i) { + var tuple = f(x, i, obj); + r[tuple.k] = tuple.v; + }); + return r; + }; + + /** bifilter :: (JsObj(k, v), (v, k -> Bool)) -> { t: JsObj(k, v), f: JsObj(k, v) } */ + var bifilter = function(obj, pred) { + var t = {}; + var f = {}; + each(obj, function(x, i) { + var branch = pred(x, i) ? t : f; + branch[i] = x; + }); + return { + t: t, + f: f + }; + }; + + /** mapToArray :: (JsObj(k, v), (v, k -> a)) -> [a] */ + var mapToArray = function(obj, f) { + var r = []; + each(obj, function(value, name) { + r.push(f(value, name)); + }); + return r; + }; + + /** find :: (JsObj(k, v), (v, k, JsObj(k, v) -> Bool)) -> Option v */ + var find = function(obj, pred) { + var props = keys(obj); + for (var k = 0, len = props.length; k < len; k++) { + var i = props[k]; + var x = obj[i]; + if (pred(x, i, obj)) { + return Option.some(x); + } + } + return Option.none(); + }; + + /** values :: JsObj(k, v) -> [v] */ + var values = function(obj) { + return mapToArray(obj, function(v) { + return v; + }); + }; + + var size = function(obj) { + return values(obj).length; + }; + + return { + bifilter: bifilter, + each: each, + map: objectMap, + mapToArray: mapToArray, + tupleMap: tupleMap, + find: find, + keys: keys, + values: values, + size: size + }; + } + ); + define( + 'ephox.katamari.util.BagUtils', + + [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Type', + 'global!Error' + ], + + function(Arr, Type, Error) { + var sort = function(arr) { + return arr.slice(0).sort(); + }; + + var reqMessage = function(required, keys) { + throw new Error('All required keys (' + sort(required).join(', ') + ') were not specified. Specified keys were: ' + sort(keys).join(', ') + '.'); + }; + + var unsuppMessage = function(unsupported) { + throw new Error('Unsupported keys for object: ' + sort(unsupported).join(', ')); + }; + + var validateStrArr = function(label, array) { + if (!Type.isArray(array)) throw new Error('The ' + label + ' fields must be an array. Was: ' + array + '.'); + Arr.each(array, function(a) { + if (!Type.isString(a)) throw new Error('The value ' + a + ' in the ' + label + ' fields was not a string.'); + }); + }; + + var invalidTypeMessage = function(incorrect, type) { + throw new Error('All values need to be of type: ' + type + '. Keys (' + sort(incorrect).join(', ') + ') were not.'); + }; + + var checkDupes = function(everything) { + var sorted = sort(everything); + var dupe = Arr.find(sorted, function(s, i) { + return i < sorted.length - 1 && s === sorted[i + 1]; + }); + + dupe.each(function(d) { + throw new Error('The field: ' + d + ' occurs more than once in the combined fields: [' + sorted.join(', ') + '].'); + }); + }; + + return { + sort: sort, + reqMessage: reqMessage, + unsuppMessage: unsuppMessage, + validateStrArr: validateStrArr, + invalidTypeMessage: invalidTypeMessage, + checkDupes: checkDupes + }; + } + ); + define( + 'ephox.katamari.data.MixedBag', + + [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Obj', + 'ephox.katamari.api.Option', + 'ephox.katamari.util.BagUtils', + 'global!Error', + 'global!Object' + ], + + function(Arr, Fun, Obj, Option, BagUtils, Error, Object) { + + return function(required, optional) { + var everything = required.concat(optional); + if (everything.length === 0) throw new Error('You must specify at least one required or optional field.'); + + BagUtils.validateStrArr('required', required); + BagUtils.validateStrArr('optional', optional); + + BagUtils.checkDupes(everything); + + return function(obj) { + var keys = Obj.keys(obj); + + // Ensure all required keys are present. + var allReqd = Arr.forall(required, function(req) { + return Arr.contains(keys, req); + }); + + if (!allReqd) BagUtils.reqMessage(required, keys); + + var unsupported = Arr.filter(keys, function(key) { + return !Arr.contains(everything, key); + }); + + if (unsupported.length > 0) BagUtils.unsuppMessage(unsupported); + + var r = {}; + Arr.each(required, function(req) { + r[req] = Fun.constant(obj[req]); + }); + + Arr.each(optional, function(opt) { + r[opt] = Fun.constant(Object.prototype.hasOwnProperty.call(obj, opt) ? Option.some(obj[opt]) : Option.none()); + }); + + return r; + }; + }; + } + ); + define( + 'ephox.katamari.api.Struct', + + [ + 'ephox.katamari.data.Immutable', + 'ephox.katamari.data.MixedBag' + ], + + function(Immutable, MixedBag) { + return { + immutable: Immutable, + immutableBag: MixedBag + }; + } + ); + + define( + 'ephox.sugar.alien.Recurse', + + [ + + ], + + function() { + /** + * Applies f repeatedly until it completes (by returning Option.none()). + * + * Normally would just use recursion, but JavaScript lacks tail call optimisation. + * + * This is what recursion looks like when manually unravelled :) + */ + var toArray = function(target, f) { + var r = []; + + var recurse = function(e) { + r.push(e); + return f(e); + }; + + var cur = f(target); + do { + cur = cur.bind(recurse); + } while (cur.isSome()); + + return r; + }; + + return { + toArray: toArray + }; + } + ); + define( + 'ephox.sugar.api.search.Traverse', + + [ + 'ephox.katamari.api.Type', + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Option', + 'ephox.katamari.api.Struct', + 'ephox.sugar.alien.Recurse', + 'ephox.sugar.api.dom.Compare', + 'ephox.sugar.api.node.Element' + ], + + function(Type, Arr, Fun, Option, Struct, Recurse, Compare, Element) { + // The document associated with the current element + var owner = function(element) { + return Element.fromDom(element.dom().ownerDocument); + }; + + var documentElement = function(element) { + // TODO: Avoid unnecessary wrap/unwrap here + var doc = owner(element); + return Element.fromDom(doc.dom().documentElement); + }; + + // The window element associated with the element + var defaultView = function(element) { + var el = element.dom(); + var defaultView = el.ownerDocument.defaultView; + return Element.fromDom(defaultView); + }; + + var parent = function(element) { + var dom = element.dom(); + return Option.from(dom.parentNode).map(Element.fromDom); + }; + + var findIndex = function(element) { + return parent(element).bind(function(p) { + // TODO: Refactor out children so we can avoid the constant unwrapping + var kin = children(p); + return Arr.findIndex(kin, function(elem) { + return Compare.eq(element, elem); + }); + }); + }; + + var parents = function(element, isRoot) { + var stop = Type.isFunction(isRoot) ? isRoot : Fun.constant(false); + + // This is used a *lot* so it needs to be performant, not recursive + var dom = element.dom(); + var ret = []; + + while (dom.parentNode !== null && dom.parentNode !== undefined) { + var rawParent = dom.parentNode; + var parent = Element.fromDom(rawParent); + ret.push(parent); + + if (stop(parent) === true) break; + else dom = rawParent; + } + return ret; + }; + + var siblings = function(element) { + // TODO: Refactor out children so we can just not add self instead of filtering afterwards + var filterSelf = function(elements) { + return Arr.filter(elements, function(x) { + return !Compare.eq(element, x); + }); + }; + + return parent(element).map(children).map(filterSelf).getOr([]); + }; + + var offsetParent = function(element) { + var dom = element.dom(); + return Option.from(dom.offsetParent).map(Element.fromDom); + }; + + var prevSibling = function(element) { + var dom = element.dom(); + return Option.from(dom.previousSibling).map(Element.fromDom); + }; + + var nextSibling = function(element) { + var dom = element.dom(); + return Option.from(dom.nextSibling).map(Element.fromDom); + }; + + var prevSiblings = function(element) { + // This one needs to be reversed, so they're still in DOM order + return Arr.reverse(Recurse.toArray(element, prevSibling)); + }; + + var nextSiblings = function(element) { + return Recurse.toArray(element, nextSibling); + }; + + var children = function(element) { + var dom = element.dom(); + return Arr.map(dom.childNodes, Element.fromDom); + }; + + var child = function(element, index) { + var children = element.dom().childNodes; + return Option.from(children[index]).map(Element.fromDom); + }; + + var firstChild = function(element) { + return child(element, 0); + }; + + var lastChild = function(element) { + return child(element, element.dom().childNodes.length - 1); + }; + + var childNodesCount = function(element, index) { + return element.dom().childNodes.length; + }; + + var spot = Struct.immutable('element', 'offset'); + var leaf = function(element, offset) { + var cs = children(element); + return cs.length > 0 && offset < cs.length ? spot(cs[offset], 0) : spot(element, offset); + }; + + return { + owner: owner, + defaultView: defaultView, + documentElement: documentElement, + parent: parent, + findIndex: findIndex, + parents: parents, + siblings: siblings, + prevSibling: prevSibling, + offsetParent: offsetParent, + prevSiblings: prevSiblings, + nextSibling: nextSibling, + nextSiblings: nextSiblings, + children: children, + child: child, + firstChild: firstChild, + lastChild: lastChild, + childNodesCount: childNodesCount, + leaf: leaf + }; + } + ); + + define( + 'ephox.sugar.api.dom.Insert', + + [ + 'ephox.sugar.api.search.Traverse' + ], + + function(Traverse) { + var before = function(marker, element) { + var parent = Traverse.parent(marker); + parent.each(function(v) { + v.dom().insertBefore(element.dom(), marker.dom()); + }); + }; + + var after = function(marker, element) { + var sibling = Traverse.nextSibling(marker); + sibling.fold(function() { + var parent = Traverse.parent(marker); + parent.each(function(v) { + append(v, element); + }); + }, function(v) { + before(v, element); + }); + }; + + var prepend = function(parent, element) { + var firstChild = Traverse.firstChild(parent); + firstChild.fold(function() { + append(parent, element); + }, function(v) { + parent.dom().insertBefore(element.dom(), v.dom()); + }); + }; + + var append = function(parent, element) { + parent.dom().appendChild(element.dom()); + }; + + var appendAt = function(parent, element, index) { + Traverse.child(parent, index).fold(function() { + append(parent, element); + }, function(v) { + before(v, element); + }); + }; + + var wrap = function(element, wrapper) { + before(element, wrapper); + append(wrapper, element); + }; + + return { + before: before, + after: after, + prepend: prepend, + append: append, + appendAt: appendAt, + wrap: wrap + }; + } + ); + + define( + 'ephox.sugar.api.node.Node', + + [ + 'ephox.sugar.api.node.NodeTypes' + ], + + function(NodeTypes) { + var name = function(element) { + var r = element.dom().nodeName; + return r.toLowerCase(); + }; + + var type = function(element) { + return element.dom().nodeType; + }; + + var value = function(element) { + return element.dom().nodeValue; + }; + + var isType = function(t) { + return function(element) { + return type(element) === t; + }; + }; + + var isComment = function(element) { + return type(element) === NodeTypes.COMMENT || name(element) === '#comment'; + }; + + var isElement = isType(NodeTypes.ELEMENT); + var isText = isType(NodeTypes.TEXT); + var isDocument = isType(NodeTypes.DOCUMENT); + + return { + name: name, + type: type, + value: value, + isElement: isElement, + isText: isText, + isDocument: isDocument, + isComment: isComment + }; + } + ); + + define( + 'ephox.sugar.api.properties.Attr', + + [ + 'ephox.katamari.api.Type', + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Obj', + 'ephox.sugar.api.node.Node', + 'global!Error', + 'global!console' + ], + + /* + * Direct attribute manipulation has been around since IE8, but + * was apparently unstable until IE10. + */ + function(Type, Arr, Obj, Node, Error, console) { + var rawSet = function(dom, key, value) { + /* + * JQuery coerced everything to a string, and silently did nothing on text node/null/undefined. + * + * We fail on those invalid cases, only allowing numbers and booleans. + */ + if (Type.isString(value) || Type.isBoolean(value) || Type.isNumber(value)) { + dom.setAttribute(key, value + ''); + } else { + console.error('Invalid call to Attr.set. Key ', key, ':: Value ', value, ':: Element ', dom); + throw new Error('Attribute value was not simple'); + } + }; + + var set = function(element, key, value) { + rawSet(element.dom(), key, value); + }; + + var setAll = function(element, attrs) { + var dom = element.dom(); + Obj.each(attrs, function(v, k) { + rawSet(dom, k, v); + }); + }; + + var get = function(element, key) { + var v = element.dom().getAttribute(key); + + // undefined is the more appropriate value for JS, and this matches JQuery + return v === null ? undefined : v; + }; + + var has = function(element, key) { + var dom = element.dom(); + + // return false for non-element nodes, no point in throwing an error + return dom && dom.hasAttribute ? dom.hasAttribute(key) : false; + }; + + var remove = function(element, key) { + element.dom().removeAttribute(key); + }; + + var hasNone = function(element) { + var attrs = element.dom().attributes; + return attrs === undefined || attrs === null || attrs.length === 0; + }; + + var clone = function(element) { + return Arr.foldl(element.dom().attributes, function(acc, attr) { + acc[attr.name] = attr.value; + return acc; + }, {}); + }; + + var transferOne = function(source, destination, attr) { + // NOTE: We don't want to clobber any existing attributes + if (has(source, attr) && !has(destination, attr)) set(destination, attr, get(source, attr)); + }; + + // Transfer attributes(attrs) from source to destination, unless they are already present + var transfer = function(source, destination, attrs) { + if (!Node.isElement(source) || !Node.isElement(destination)) return; + Arr.each(attrs, function(attr) { + transferOne(source, destination, attr); + }); + }; + + return { + clone: clone, + set: set, + setAll: setAll, + get: get, + has: has, + remove: remove, + hasNone: hasNone, + transfer: transfer + }; + } + ); + + define( + 'ephox.sugar.api.dom.InsertAll', + + [ + 'ephox.katamari.api.Arr', + 'ephox.sugar.api.dom.Insert' + ], + + function(Arr, Insert) { + var before = function(marker, elements) { + Arr.each(elements, function(x) { + Insert.before(marker, x); + }); + }; + + var after = function(marker, elements) { + Arr.each(elements, function(x, i) { + var e = i === 0 ? marker : elements[i - 1]; + Insert.after(e, x); + }); + }; + + var prepend = function(parent, elements) { + Arr.each(elements.slice().reverse(), function(x) { + Insert.prepend(parent, x); + }); + }; + + var append = function(parent, elements) { + Arr.each(elements, function(x) { + Insert.append(parent, x); + }); + }; + + return { + before: before, + after: after, + prepend: prepend, + append: append + }; + } + ); + + define( + 'ephox.sugar.api.dom.Remove', + + [ + 'ephox.katamari.api.Arr', + 'ephox.sugar.api.dom.InsertAll', + 'ephox.sugar.api.search.Traverse' + ], + + function(Arr, InsertAll, Traverse) { + var empty = function(element) { + // shortcut "empty node" trick. Requires IE 9. + element.dom().textContent = ''; + + // If the contents was a single empty text node, the above doesn't remove it. But, it's still faster in general + // than removing every child node manually. + // The following is (probably) safe for performance as 99.9% of the time the trick works and + // Traverse.children will return an empty array. + Arr.each(Traverse.children(element), function(rogue) { + remove(rogue); + }); + }; + + var remove = function(element) { + var dom = element.dom(); + if (dom.parentNode !== null) + dom.parentNode.removeChild(dom); + }; + + var unwrap = function(wrapper) { + var children = Traverse.children(wrapper); + if (children.length > 0) + InsertAll.before(wrapper, children); + remove(wrapper); + }; + + return { + empty: empty, + remove: remove, + unwrap: unwrap + }; + } + ); + + define( + 'ephox.sugar.api.dom.Replication', + + [ + 'ephox.sugar.api.properties.Attr', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.api.dom.Insert', + 'ephox.sugar.api.dom.InsertAll', + 'ephox.sugar.api.dom.Remove', + 'ephox.sugar.api.search.Traverse' + ], + + function(Attr, Element, Insert, InsertAll, Remove, Traverse) { + var clone = function(original, deep) { + return Element.fromDom(original.dom().cloneNode(deep)); + }; + + /** Shallow clone - just the tag, no children */ + var shallow = function(original) { + return clone(original, false); + }; + + /** Deep clone - everything copied including children */ + var deep = function(original) { + return clone(original, true); + }; + + /** Shallow clone, with a new tag */ + var shallowAs = function(original, tag) { + var nu = Element.fromTag(tag); + + var attributes = Attr.clone(original); + Attr.setAll(nu, attributes); + + return nu; + }; + + /** Deep clone, with a new tag */ + var copy = function(original, tag) { + var nu = shallowAs(original, tag); + + // NOTE + // previously this used serialisation: + // nu.dom().innerHTML = original.dom().innerHTML; + // + // Clone should be equivalent (and faster), but if TD <-> TH toggle breaks, put it back. + + var cloneChildren = Traverse.children(deep(original)); + InsertAll.append(nu, cloneChildren); + + return nu; + }; + + /** Change the tag name, but keep all children */ + var mutate = function(original, tag) { + var nu = shallowAs(original, tag); + + Insert.before(original, nu); + var children = Traverse.children(original); + InsertAll.append(nu, children); + Remove.remove(original); + return nu; + }; + + return { + shallow: shallow, + shallowAs: shallowAs, + deep: deep, + copy: copy, + mutate: mutate + }; + } + ); + + define( + 'ephox.sugar.api.node.Fragment', + + [ + 'ephox.katamari.api.Arr', + 'ephox.sugar.api.node.Element', + 'global!document' + ], + + function(Arr, Element, document) { + var fromElements = function(elements, scope) { + var doc = scope || document; + var fragment = doc.createDocumentFragment(); + Arr.each(elements, function(element) { + fragment.appendChild(element.dom()); + }); + return Element.fromDom(fragment); + }; + + return { + fromElements: fromElements + }; + } + ); + + /** + * ElementType.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.dom.ElementType', [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Fun', + 'ephox.sugar.api.node.Node' + ], + function(Arr, Fun, Node) { + var blocks = [ + 'article', 'aside', 'details', 'div', 'dt', 'figcaption', 'footer', + 'form', 'fieldset', 'header', 'hgroup', 'html', 'main', 'nav', + 'section', 'summary', 'body', 'p', 'dl', 'multicol', 'dd', 'figure', + 'address', 'center', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'listing', 'xmp', 'pre', 'plaintext', 'menu', 'dir', 'ul', 'ol', 'li', 'hr', + 'table', 'tbody', 'thead', 'tfoot', 'th', 'tr', 'td', 'caption' + ]; + + var voids = [ + 'area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', + 'isindex', 'link', 'meta', 'param', 'embed', 'source', 'wbr', 'track' + ]; + + var tableCells = ['td', 'th']; + + var textBlocks = [ + 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'address', 'pre', 'form', + 'blockquote', 'center', 'dir', 'fieldset', 'header', 'footer', 'article', + 'section', 'hgroup', 'aside', 'nav', 'figure' + ]; + + var headings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; + + var lazyLookup = function(items) { + var lookup; + return function(node) { + lookup = lookup ? lookup : Arr.mapToObject(items, Fun.constant(true)); + return lookup.hasOwnProperty(Node.name(node)); + }; + }; + + var isHeading = lazyLookup(headings); + + var isBlock = lazyLookup(blocks); + + var isInline = function(node) { + return Node.isElement(node) && !isBlock(node); + }; + + return { + isBlock: isBlock, + isInline: isInline, + isHeading: isHeading, + isTextBlock: lazyLookup(textBlocks), + isVoid: lazyLookup(voids), + isTableCell: lazyLookup(tableCells) + }; + } + ); + + /** + * Parents.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.dom.Parents', [ + 'ephox.katamari.api.Fun', + 'ephox.sugar.api.dom.Compare', + 'ephox.sugar.api.search.Traverse' + ], + function(Fun, Compare, Traverse) { + var dropLast = function(xs) { + return xs.slice(0, -1); + }; + + var parentsUntil = function(startNode, rootElm, predicate) { + if (Compare.contains(rootElm, startNode)) { + return dropLast(Traverse.parents(startNode, function(elm) { + return predicate(elm) || Compare.eq(elm, rootElm); + })); + } else { + return []; + } + }; + + var parents = function(startNode, rootElm) { + return parentsUntil(startNode, rootElm, Fun.constant(false)); + }; + + var parentsAndSelf = function(startNode, rootElm) { + return [startNode].concat(parents(startNode, rootElm)); + }; + + return { + parentsUntil: parentsUntil, + parents: parents, + parentsAndSelf: parentsAndSelf + }; + } + ); + + define( + 'ephox.katamari.api.Options', + + [ + 'ephox.katamari.api.Option' + ], + + function(Option) { + /** cat :: [Option a] -> [a] */ + var cat = function(arr) { + var r = []; + var push = function(x) { + r.push(x); + }; + for (var i = 0; i < arr.length; i++) { + arr[i].each(push); + } + return r; + }; + + /** findMap :: ([a], (a, Int -> Option b)) -> Option b */ + var findMap = function(arr, f) { + for (var i = 0; i < arr.length; i++) { + var r = f(arr[i], i); + if (r.isSome()) { + return r; + } + } + return Option.none(); + }; + + /** + * if all elements in arr are 'some', their inner values are passed as arguments to f + * f must have arity arr.length + */ + var liftN = function(arr, f) { + var r = []; + for (var i = 0; i < arr.length; i++) { + var x = arr[i]; + if (x.isSome()) { + r.push(x.getOrDie()); + } else { + return Option.none(); + } + } + return Option.some(f.apply(null, r)); + }; + + return { + cat: cat, + findMap: findMap, + liftN: liftN + }; + } + ); + + /** + * SelectionUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.selection.SelectionUtils', [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Option', + 'ephox.katamari.api.Options', + 'ephox.sugar.api.dom.Compare', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.api.node.Node', + 'ephox.sugar.api.search.Traverse', + 'tinymce.core.dom.NodeType' + ], + function(Arr, Fun, Option, Options, Compare, Element, Node, Traverse, NodeType) { + var getStartNode = function(rng) { + var sc = rng.startContainer, + so = rng.startOffset; + if (NodeType.isText(sc)) { + return so === 0 ? Option.some(Element.fromDom(sc)) : Option.none(); + } else { + return Option.from(sc.childNodes[so]).map(Element.fromDom); + } + }; + + var getEndNode = function(rng) { + var ec = rng.endContainer, + eo = rng.endOffset; + if (NodeType.isText(ec)) { + return eo === ec.data.length ? Option.some(Element.fromDom(ec)) : Option.none(); + } else { + return Option.from(ec.childNodes[eo - 1]).map(Element.fromDom); + } + }; + + var getFirstChildren = function(node) { + return Traverse.firstChild(node).fold( + Fun.constant([node]), + function(child) { + return [node].concat(getFirstChildren(child)); + } + ); + }; + + var getLastChildren = function(node) { + return Traverse.lastChild(node).fold( + Fun.constant([node]), + function(child) { + if (Node.name(child) === 'br') { + return Traverse.prevSibling(child).map(function(sibling) { + return [node].concat(getLastChildren(sibling)); + }).getOr([]); + } else { + return [node].concat(getLastChildren(child)); + } + } + ); + }; + + var hasAllContentsSelected = function(elm, rng) { + return Options.liftN([getStartNode(rng), getEndNode(rng)], function(startNode, endNode) { + var start = Arr.find(getFirstChildren(elm), Fun.curry(Compare.eq, startNode)); + var end = Arr.find(getLastChildren(elm), Fun.curry(Compare.eq, endNode)); + return start.isSome() && end.isSome(); + }).getOr(false); + }; + + return { + hasAllContentsSelected: hasAllContentsSelected + }; + } + ); + + /** + * FragmentReader.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.selection.FragmentReader', [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Fun', + 'ephox.sugar.api.dom.Insert', + 'ephox.sugar.api.dom.Replication', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.api.node.Fragment', + 'ephox.sugar.api.node.Node', + 'tinymce.core.dom.ElementType', + 'tinymce.core.dom.Parents', + 'tinymce.core.selection.SelectionUtils' + ], + function(Arr, Fun, Insert, Replication, Element, Fragment, Node, ElementType, Parents, SelectionUtils) { + var findParentListContainer = function(parents) { + return Arr.find(parents, function(elm) { + return Node.name(elm) === 'ul' || Node.name(elm) === 'ol'; + }); + }; + + var getFullySelectedListWrappers = function(parents, rng) { + return Arr.find(parents, function(elm) { + return Node.name(elm) === 'li' && SelectionUtils.hasAllContentsSelected(elm, rng); + }).fold( + Fun.constant([]), + function(li) { + return findParentListContainer(parents).map(function(listCont) { + return [ + Element.fromTag('li'), + Element.fromTag(Node.name(listCont)) + ]; + }).getOr([]); + } + ); + }; + + var wrap = function(innerElm, elms) { + var wrapped = Arr.foldl(elms, function(acc, elm) { + Insert.append(elm, acc); + return elm; + }, innerElm); + return elms.length > 0 ? Fragment.fromElements([wrapped]) : wrapped; + }; + + var getWrapElements = function(rootNode, rng) { + var parents = Parents.parentsAndSelf(Element.fromDom(rng.commonAncestorContainer), Element.fromDom(rootNode)); + var wrapElements = Arr.filter(parents, function(elm) { + return ElementType.isInline(elm) || ElementType.isHeading(elm); + }); + var fullWrappers = getFullySelectedListWrappers(parents, rng); + return Arr.map(wrapElements.concat(fullWrappers), Replication.shallow); + }; + + var getFragmentFromRange = function(rootNode, rng) { + return wrap(Element.fromDom(rng.cloneContents()), getWrapElements(rootNode, rng)); + }; + + var read = function(rootNode, rng) { + return rng.collapsed ? Fragment.fromElements([]) : getFragmentFromRange(rootNode, rng); + }; + + return { + read: read + }; + } + ); + + /** + * Selection.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class handles text and control selection it's an crossbrowser utility class. + * Consult the TinyMCE Wiki API for more details and examples on how to use this class. + * + * @class tinymce.dom.Selection + * @example + * // Getting the currently selected node for the active editor + * alert(tinymce.activeEditor.selection.getNode().nodeName); + */ + define( + 'tinymce.core.dom.Selection', [ + 'ephox.sugar.api.dom.Compare', + 'ephox.sugar.api.node.Element', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.dom.BookmarkManager', + 'tinymce.core.dom.ControlSelection', + 'tinymce.core.dom.NodeType', + 'tinymce.core.dom.RangeUtils', + 'tinymce.core.dom.ScrollIntoView', + 'tinymce.core.dom.TreeWalker', + 'tinymce.core.dom.TridentSelection', + 'tinymce.core.Env', + 'tinymce.core.selection.FragmentReader', + 'tinymce.core.text.Zwsp', + 'tinymce.core.util.Tools' + ], + function( + Compare, Element, CaretPosition, BookmarkManager, ControlSelection, NodeType, RangeUtils, ScrollIntoView, TreeWalker, TridentSelection, Env, FragmentReader, + Zwsp, Tools + ) { + var each = Tools.each, + trim = Tools.trim; + var isIE = Env.ie; + + var isAttachedToDom = function(node) { + return !!(node && node.ownerDocument) && Compare.contains(Element.fromDom(node.ownerDocument), Element.fromDom(node)); + }; + + var isValidRange = function(rng) { + if (!rng) { + return false; + } else if (rng.select) { // Native IE range still produced by placeCaretAt + return true; + } else { + return isAttachedToDom(rng.startContainer) && isAttachedToDom(rng.endContainer); + } + }; + + /** + * Constructs a new selection instance. + * + * @constructor + * @method Selection + * @param {tinymce.dom.DOMUtils} dom DOMUtils object reference. + * @param {Window} win Window to bind the selection object to. + * @param {tinymce.Editor} editor Editor instance of the selection. + * @param {tinymce.dom.Serializer} serializer DOM serialization class to use for getContent. + */ + function Selection(dom, win, serializer, editor) { + var self = this; + + self.dom = dom; + self.win = win; + self.serializer = serializer; + self.editor = editor; + self.bookmarkManager = new BookmarkManager(self); + self.controlSelection = new ControlSelection(self, editor); + + // No W3C Range support + if (!self.win.getSelection) { + self.tridentSel = new TridentSelection(self); + } + } + + Selection.prototype = { + /** + * Move the selection cursor range to the specified node and offset. + * If there is no node specified it will move it to the first suitable location within the body. + * + * @method setCursorLocation + * @param {Node} node Optional node to put the cursor in. + * @param {Number} offset Optional offset from the start of the node to put the cursor at. + */ + setCursorLocation: function(node, offset) { + var self = this, + rng = self.dom.createRng(); + + if (!node) { + self._moveEndPoint(rng, self.editor.getBody(), true); + self.setRng(rng); + } else { + rng.setStart(node, offset); + rng.setEnd(node, offset); + self.setRng(rng); + self.collapse(false); + } + }, + + /** + * Returns the selected contents using the DOM serializer passed in to this class. + * + * @method getContent + * @param {Object} args Optional settings class with for example output format text or html. + * @return {String} Selected contents in for example HTML format. + * @example + * // Alerts the currently selected contents + * alert(tinymce.activeEditor.selection.getContent()); + * + * // Alerts the currently selected contents as plain text + * alert(tinymce.activeEditor.selection.getContent({format: 'text'})); + */ + getContent: function(args) { + var self = this, + rng = self.getRng(), + tmpElm = self.dom.create("body"); + var se = self.getSel(), + whiteSpaceBefore, whiteSpaceAfter, fragment; + + args = args || {}; + whiteSpaceBefore = whiteSpaceAfter = ''; + args.get = true; + args.format = args.format || 'html'; + args.selection = true; + self.editor.fire('BeforeGetContent', args); + + if (args.format === 'text') { + return self.isCollapsed() ? '' : Zwsp.trim(rng.text || (se.toString ? se.toString() : '')); + } + + if (rng.cloneContents) { + fragment = args.contextual ? FragmentReader.read(self.editor.getBody(), rng).dom() : rng.cloneContents(); + if (fragment) { + tmpElm.appendChild(fragment); + } + } else if (rng.item !== undefined || rng.htmlText !== undefined) { + // IE will produce invalid markup if elements are present that + // it doesn't understand like custom elements or HTML5 elements. + // Adding a BR in front of the contents and then remoiving it seems to fix it though. + tmpElm.innerHTML = '<br>' + (rng.item ? rng.item(0).outerHTML : rng.htmlText); + tmpElm.removeChild(tmpElm.firstChild); + } else { + tmpElm.innerHTML = rng.toString(); + } + + // Keep whitespace before and after + if (/^\s/.test(tmpElm.innerHTML)) { + whiteSpaceBefore = ' '; + } + + if (/\s+$/.test(tmpElm.innerHTML)) { + whiteSpaceAfter = ' '; + } + + args.getInner = true; + + args.content = self.isCollapsed() ? '' : whiteSpaceBefore + self.serializer.serialize(tmpElm, args) + whiteSpaceAfter; + self.editor.fire('GetContent', args); + + return args.content; + }, + + /** + * Sets the current selection to the specified content. If any contents is selected it will be replaced + * with the contents passed in to this function. If there is no selection the contents will be inserted + * where the caret is placed in the editor/page. + * + * @method setContent + * @param {String} content HTML contents to set could also be other formats depending on settings. + * @param {Object} args Optional settings object with for example data format. + * @example + * // Inserts some HTML contents at the current selection + * tinymce.activeEditor.selection.setContent('<strong>Some contents</strong>'); + */ + setContent: function(content, args) { + var self = this, + rng = self.getRng(), + caretNode, doc = self.win.document, + frag, temp; + + args = args || { + format: 'html' + }; + args.set = true; + args.selection = true; + args.content = content; + + // Dispatch before set content event + if (!args.no_events) { + self.editor.fire('BeforeSetContent', args); + } + + content = args.content; + + if (rng.insertNode) { + // Make caret marker since insertNode places the caret in the beginning of text after insert + content += '<span id="__caret">_</span>'; + + // Delete and insert new node + if (rng.startContainer == doc && rng.endContainer == doc) { + // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents + doc.body.innerHTML = content; + } else { + rng.deleteContents(); + + if (doc.body.childNodes.length === 0) { + doc.body.innerHTML = content; + } else { + // createContextualFragment doesn't exists in IE 9 DOMRanges + if (rng.createContextualFragment) { + rng.insertNode(rng.createContextualFragment(content)); + } else { + // Fake createContextualFragment call in IE 9 + frag = doc.createDocumentFragment(); + temp = doc.createElement('div'); + + frag.appendChild(temp); + temp.outerHTML = content; + + rng.insertNode(frag); + } + } + } + + // Move to caret marker + caretNode = self.dom.get('__caret'); + + // Make sure we wrap it compleatly, Opera fails with a simple select call + rng = doc.createRange(); + rng.setStartBefore(caretNode); + rng.setEndBefore(caretNode); + self.setRng(rng); + + // Remove the caret position + self.dom.remove('__caret'); + + try { + self.setRng(rng); + } catch (ex) { + // Might fail on Opera for some odd reason + } + } else { + if (rng.item) { + // Delete content and get caret text selection + doc.execCommand('Delete', false, null); + rng = self.getRng(); + } + + // Explorer removes spaces from the beginning of pasted contents + if (/^\s+/.test(content)) { + rng.pasteHTML('<span id="__mce_tmp">_</span>' + content); + self.dom.remove('__mce_tmp'); + } else { + rng.pasteHTML(content); + } + } + + // Dispatch set content event + if (!args.no_events) { + self.editor.fire('SetContent', args); + } + }, + + /** + * Returns the start element of a selection range. If the start is in a text + * node the parent element will be returned. + * + * @method getStart + * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element. + * @return {Element} Start element of selection range. + */ + getStart: function(real) { + var self = this, + rng = self.getRng(), + startElement, parentElement, checkRng, node; + + if (rng.duplicate || rng.item) { + // Control selection, return first item + if (rng.item) { + return rng.item(0); + } + + // Get start element + checkRng = rng.duplicate(); + checkRng.collapse(1); + startElement = checkRng.parentElement(); + if (startElement.ownerDocument !== self.dom.doc) { + startElement = self.dom.getRoot(); + } + + // Check if range parent is inside the start element, then return the inner parent element + // This will fix issues when a single element is selected, IE would otherwise return the wrong start element + parentElement = node = rng.parentElement(); + while ((node = node.parentNode)) { + if (node == startElement) { + startElement = parentElement; + break; + } + } + + return startElement; + } + + startElement = rng.startContainer; + + if (startElement.nodeType == 1 && startElement.hasChildNodes()) { + if (!real || !rng.collapsed) { + startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)]; + } + } + + if (startElement && startElement.nodeType == 3) { + return startElement.parentNode; + } + + return startElement; + }, + + /** + * Returns the end element of a selection range. If the end is in a text + * node the parent element will be returned. + * + * @method getEnd + * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element. + * @return {Element} End element of selection range. + */ + getEnd: function(real) { + var self = this, + rng = self.getRng(), + endElement, endOffset; + + if (rng.duplicate || rng.item) { + if (rng.item) { + return rng.item(0); + } + + rng = rng.duplicate(); + rng.collapse(0); + endElement = rng.parentElement(); + if (endElement.ownerDocument !== self.dom.doc) { + endElement = self.dom.getRoot(); + } + + if (endElement && endElement.nodeName == 'BODY') { + return endElement.lastChild || endElement; + } + + return endElement; + } + + endElement = rng.endContainer; + endOffset = rng.endOffset; + + if (endElement.nodeType == 1 && endElement.hasChildNodes()) { + if (!real || !rng.collapsed) { + endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset]; + } + } + + if (endElement && endElement.nodeType == 3) { + return endElement.parentNode; + } + + return endElement; + }, + + /** + * Returns a bookmark location for the current selection. This bookmark object + * can then be used to restore the selection after some content modification to the document. + * + * @method getBookmark + * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex. + * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization. + * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection. + * @example + * // Stores a bookmark of the current selection + * var bm = tinymce.activeEditor.selection.getBookmark(); + * + * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content'); + * + * // Restore the selection bookmark + * tinymce.activeEditor.selection.moveToBookmark(bm); + */ + getBookmark: function(type, normalized) { + return this.bookmarkManager.getBookmark(type, normalized); + }, + + /** + * Restores the selection to the specified bookmark. + * + * @method moveToBookmark + * @param {Object} bookmark Bookmark to restore selection from. + * @return {Boolean} true/false if it was successful or not. + * @example + * // Stores a bookmark of the current selection + * var bm = tinymce.activeEditor.selection.getBookmark(); + * + * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content'); + * + * // Restore the selection bookmark + * tinymce.activeEditor.selection.moveToBookmark(bm); + */ + moveToBookmark: function(bookmark) { + return this.bookmarkManager.moveToBookmark(bookmark); + }, + + /** + * Selects the specified element. This will place the start and end of the selection range around the element. + * + * @method select + * @param {Element} node HTML DOM element to select. + * @param {Boolean} content Optional bool state if the contents should be selected or not on non IE browser. + * @return {Element} Selected element the same element as the one that got passed in. + * @example + * // Select the first paragraph in the active editor + * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]); + */ + select: function(node, content) { + var self = this, + dom = self.dom, + rng = dom.createRng(), + idx; + + // Clear stored range set by FocusManager + self.lastFocusBookmark = null; + + if (node) { + if (!content && self.controlSelection.controlSelect(node)) { + return; + } + + idx = dom.nodeIndex(node); + rng.setStart(node.parentNode, idx); + rng.setEnd(node.parentNode, idx + 1); + + // Find first/last text node or BR element + if (content) { + self._moveEndPoint(rng, node, true); + self._moveEndPoint(rng, node); + } + + self.setRng(rng); + } + + return node; + }, + + /** + * Returns true/false if the selection range is collapsed or not. Collapsed means if it's a caret or a larger selection. + * + * @method isCollapsed + * @return {Boolean} true/false state if the selection range is collapsed or not. + * Collapsed means if it's a caret or a larger selection. + */ + isCollapsed: function() { + var self = this, + rng = self.getRng(), + sel = self.getSel(); + + if (!rng || rng.item) { + return false; + } + + if (rng.compareEndPoints) { + return rng.compareEndPoints('StartToEnd', rng) === 0; + } + + return !sel || rng.collapsed; + }, + + /** + * Collapse the selection to start or end of range. + * + * @method collapse + * @param {Boolean} toStart Optional boolean state if to collapse to end or not. Defaults to false. + */ + collapse: function(toStart) { + var self = this, + rng = self.getRng(), + node; + + // Control range on IE + if (rng.item) { + node = rng.item(0); + rng = self.win.document.body.createTextRange(); + rng.moveToElementText(node); + } + + rng.collapse(!!toStart); + self.setRng(rng); + }, + + /** + * Returns the browsers internal selection object. + * + * @method getSel + * @return {Selection} Internal browser selection object. + */ + getSel: function() { + var win = this.win; + + return win.getSelection ? win.getSelection() : win.document.selection; + }, + + /** + * Returns the browsers internal range object. + * + * @method getRng + * @param {Boolean} w3c Forces a compatible W3C range on IE. + * @return {Range} Internal browser range object. + * @see http://www.quirksmode.org/dom/range_intro.html + * @see http://www.dotvoid.com/2001/03/using-the-range-object-in-mozilla/ + */ + getRng: function(w3c) { + var self = this, + selection, rng, elm, doc, ieRng, evt; + + function tryCompareBoundaryPoints(how, sourceRange, destinationRange) { + try { + return sourceRange.compareBoundaryPoints(how, destinationRange); + } catch (ex) { + // Gecko throws wrong document exception if the range points + // to nodes that where removed from the dom #6690 + // Browsers should mutate existing DOMRange instances so that they always point + // to something in the document this is not the case in Gecko works fine in IE/WebKit/Blink + // For performance reasons just return -1 + return -1; + } + } + + if (!self.win) { + return null; + } + + doc = self.win.document; + + if (typeof doc === 'undefined' || doc === null) { + return null; + } + + // Use last rng passed from FocusManager if it's available this enables + // calls to editor.selection.getStart() to work when caret focus is lost on IE + if (!w3c && self.lastFocusBookmark) { + var bookmark = self.lastFocusBookmark; + + // Convert bookmark to range IE 11 fix + if (bookmark.startContainer) { + rng = doc.createRange(); + rng.setStart(bookmark.startContainer, bookmark.startOffset); + rng.setEnd(bookmark.endContainer, bookmark.endOffset); + } else { + rng = bookmark; + } + + return rng; + } + + // Found tridentSel object then we need to use that one + if (w3c && self.tridentSel) { + return self.tridentSel.getRangeAt(0); + } + + try { + if ((selection = self.getSel())) { + if (selection.rangeCount > 0) { + rng = selection.getRangeAt(0); + } else { + rng = selection.createRange ? selection.createRange() : doc.createRange(); + } + } + } catch (ex) { + // IE throws unspecified error here if TinyMCE is placed in a frame/iframe + } + + evt = self.editor.fire('GetSelectionRange', { + range: rng + }); + if (evt.range !== rng) { + return evt.range; + } + + // We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet + // IE 11 doesn't support the selection object so we check for that as well + if (isIE && rng && rng.setStart && doc.selection) { + try { + // IE will sometimes throw an exception here + ieRng = doc.selection.createRange(); + } catch (ex) { + // Ignore + } + + if (ieRng && ieRng.item) { + elm = ieRng.item(0); + rng = doc.createRange(); + rng.setStartBefore(elm); + rng.setEndAfter(elm); + } + } + + // No range found then create an empty one + // This can occur when the editor is placed in a hidden container element on Gecko + // Or on IE when there was an exception + if (!rng) { + rng = doc.createRange ? doc.createRange() : doc.body.createTextRange(); + } + + // If range is at start of document then move it to start of body + if (rng.setStart && rng.startContainer.nodeType === 9 && rng.collapsed) { + elm = self.dom.getRoot(); + rng.setStart(elm, 0); + rng.setEnd(elm, 0); + } + + if (self.selectedRange && self.explicitRange) { + if (tryCompareBoundaryPoints(rng.START_TO_START, rng, self.selectedRange) === 0 && + tryCompareBoundaryPoints(rng.END_TO_END, rng, self.selectedRange) === 0) { + // Safari, Opera and Chrome only ever select text which causes the range to change. + // This lets us use the originally set range if the selection hasn't been changed by the user. + rng = self.explicitRange; + } else { + self.selectedRange = null; + self.explicitRange = null; + } + } + + return rng; + }, + + /** + * Changes the selection to the specified DOM range. + * + * @method setRng + * @param {Range} rng Range to select. + * @param {Boolean} forward Optional boolean if the selection is forwards or backwards. + */ + setRng: function(rng, forward) { + var self = this, + sel, node, evt; + + if (!isValidRange(rng)) { + return; + } + + // Is IE specific range + if (rng.select) { + self.explicitRange = null; + + try { + rng.select(); + } catch (ex) { + // Needed for some odd IE bug #1843306 + } + + return; + } + + if (!self.tridentSel) { + sel = self.getSel(); + + evt = self.editor.fire('SetSelectionRange', { + range: rng, + forward: forward + }); + rng = evt.range; + + if (sel) { + self.explicitRange = rng; + + try { + sel.removeAllRanges(); + sel.addRange(rng); + } catch (ex) { + // IE might throw errors here if the editor is within a hidden container and selection is changed + } + + // Forward is set to false and we have an extend function + if (forward === false && sel.extend) { + sel.collapse(rng.endContainer, rng.endOffset); + sel.extend(rng.startContainer, rng.startOffset); + } + + // adding range isn't always successful so we need to check range count otherwise an exception can occur + self.selectedRange = sel.rangeCount > 0 ? sel.getRangeAt(0) : null; + } + + // WebKit egde case selecting images works better using setBaseAndExtent when the image is floated + if (!rng.collapsed && rng.startContainer === rng.endContainer && sel.setBaseAndExtent && !Env.ie) { + if (rng.endOffset - rng.startOffset < 2) { + if (rng.startContainer.hasChildNodes()) { + node = rng.startContainer.childNodes[rng.startOffset]; + if (node && node.tagName === 'IMG') { + sel.setBaseAndExtent( + rng.startContainer, + rng.startOffset, + rng.endContainer, + rng.endOffset + ); + + // Since the setBaseAndExtent is fixed in more recent Blink versions we + // need to detect if it's doing the wrong thing and falling back to the + // crazy incorrect behavior api call since that seems to be the only way + // to get it to work on Safari WebKit as of 2017-02-23 + if (sel.anchorNode !== rng.startContainer || sel.focusNode !== rng.endContainer) { + sel.setBaseAndExtent(node, 0, node, 1); + } + } + } + } + } + + self.editor.fire('AfterSetSelectionRange', { + range: rng, + forward: forward + }); + } else { + // Is W3C Range fake range on IE + if (rng.cloneRange) { + try { + self.tridentSel.addRange(rng); + } catch (ex) { + //IE9 throws an error here if called before selection is placed in the editor + } + } + } + }, + + /** + * Sets the current selection to the specified DOM element. + * + * @method setNode + * @param {Element} elm Element to set as the contents of the selection. + * @return {Element} Returns the element that got passed in. + * @example + * // Inserts a DOM node at current selection/caret location + * tinymce.activeEditor.selection.setNode(tinymce.activeEditor.dom.create('img', {src: 'some.gif', title: 'some title'})); + */ + setNode: function(elm) { + var self = this; + + self.setContent(self.dom.getOuterHTML(elm)); + + return elm; + }, + + /** + * Returns the currently selected element or the common ancestor element for both start and end of the selection. + * + * @method getNode + * @return {Element} Currently selected element or common ancestor element. + * @example + * // Alerts the currently selected elements node name + * alert(tinymce.activeEditor.selection.getNode().nodeName); + */ + getNode: function() { + var self = this, + rng = self.getRng(), + elm; + var startContainer, endContainer, startOffset, endOffset, root = self.dom.getRoot(); + + function skipEmptyTextNodes(node, forwards) { + var orig = node; + + while (node && node.nodeType === 3 && node.length === 0) { + node = forwards ? node.nextSibling : node.previousSibling; + } + + return node || orig; + } + + // Range maybe lost after the editor is made visible again + if (!rng) { + return root; + } + + startContainer = rng.startContainer; + endContainer = rng.endContainer; + startOffset = rng.startOffset; + endOffset = rng.endOffset; + + if (rng.setStart) { + elm = rng.commonAncestorContainer; + + // Handle selection a image or other control like element such as anchors + if (!rng.collapsed) { + if (startContainer == endContainer) { + if (endOffset - startOffset < 2) { + if (startContainer.hasChildNodes()) { + elm = startContainer.childNodes[startOffset]; + } + } + } + + // If the anchor node is a element instead of a text node then return this element + //if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1) + // return sel.anchorNode.childNodes[sel.anchorOffset]; + + // Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent. + // This happens when you double click an underlined word in FireFox. + if (startContainer.nodeType === 3 && endContainer.nodeType === 3) { + if (startContainer.length === startOffset) { + startContainer = skipEmptyTextNodes(startContainer.nextSibling, true); + } else { + startContainer = startContainer.parentNode; + } + + if (endOffset === 0) { + endContainer = skipEmptyTextNodes(endContainer.previousSibling, false); + } else { + endContainer = endContainer.parentNode; + } + + if (startContainer && startContainer === endContainer) { + return startContainer; + } + } + } + + if (elm && elm.nodeType == 3) { + return elm.parentNode; + } + + return elm; + } + + elm = rng.item ? rng.item(0) : rng.parentElement(); + + // IE 7 might return elements outside the iframe + if (elm.ownerDocument !== self.win.document) { + elm = root; + } + + return elm; + }, + + getSelectedBlocks: function(startElm, endElm) { + var self = this, + dom = self.dom, + node, root, selectedBlocks = []; + + root = dom.getRoot(); + startElm = dom.getParent(startElm || self.getStart(), dom.isBlock); + endElm = dom.getParent(endElm || self.getEnd(), dom.isBlock); + + if (startElm && startElm != root) { + selectedBlocks.push(startElm); + } + + if (startElm && endElm && startElm != endElm) { + node = startElm; + + var walker = new TreeWalker(startElm, root); + while ((node = walker.next()) && node != endElm) { + if (dom.isBlock(node)) { + selectedBlocks.push(node); + } + } + } + + if (endElm && startElm != endElm && endElm != root) { + selectedBlocks.push(endElm); + } + + return selectedBlocks; + }, + + isForward: function() { + var dom = this.dom, + sel = this.getSel(), + anchorRange, focusRange; + + // No support for selection direction then always return true + if (!sel || !sel.anchorNode || !sel.focusNode) { + return true; + } + + anchorRange = dom.createRng(); + anchorRange.setStart(sel.anchorNode, sel.anchorOffset); + anchorRange.collapse(true); + + focusRange = dom.createRng(); + focusRange.setStart(sel.focusNode, sel.focusOffset); + focusRange.collapse(true); + + return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0; + }, + + normalize: function() { + var self = this, + rng = self.getRng(); + + if (Env.range && new RangeUtils(self.dom).normalize(rng)) { + self.setRng(rng, self.isForward()); + } + + return rng; + }, + + /** + * Executes callback when the current selection starts/stops matching the specified selector. The current + * state will be passed to the callback as it's first argument. + * + * @method selectorChanged + * @param {String} selector CSS selector to check for. + * @param {function} callback Callback with state and args when the selector is matches or not. + */ + selectorChanged: function(selector, callback) { + var self = this, + currentSelectors; + + if (!self.selectorChangedData) { + self.selectorChangedData = {}; + currentSelectors = {}; + + self.editor.on('NodeChange', function(e) { + var node = e.element, + dom = self.dom, + parents = dom.getParents(node, null, dom.getRoot()), + matchedSelectors = {}; + + // Check for new matching selectors + each(self.selectorChangedData, function(callbacks, selector) { + each(parents, function(node) { + if (dom.is(node, selector)) { + if (!currentSelectors[selector]) { + // Execute callbacks + each(callbacks, function(callback) { + callback(true, { + node: node, + selector: selector, + parents: parents + }); + }); + + currentSelectors[selector] = callbacks; + } + + matchedSelectors[selector] = callbacks; + return false; + } + }); + }); + + // Check if current selectors still match + each(currentSelectors, function(callbacks, selector) { + if (!matchedSelectors[selector]) { + delete currentSelectors[selector]; + + each(callbacks, function(callback) { + callback(false, { + node: node, + selector: selector, + parents: parents + }); + }); + } + }); + }); + } + + // Add selector listeners + if (!self.selectorChangedData[selector]) { + self.selectorChangedData[selector] = []; + } + + self.selectorChangedData[selector].push(callback); + + return self; + }, + + getScrollContainer: function() { + var scrollContainer, node = this.dom.getRoot(); + + while (node && node.nodeName != 'BODY') { + if (node.scrollHeight > node.clientHeight) { + scrollContainer = node; + break; + } + + node = node.parentNode; + } + + return scrollContainer; + }, + + scrollIntoView: function(elm, alignToTop) { + ScrollIntoView.scrollIntoView(this.editor, elm, alignToTop); + }, + + placeCaretAt: function(clientX, clientY) { + this.setRng(RangeUtils.getCaretRangeFromPoint(clientX, clientY, this.editor.getDoc())); + }, + + _moveEndPoint: function(rng, node, start) { + var root = node, + walker = new TreeWalker(node, root); + var nonEmptyElementsMap = this.dom.schema.getNonEmptyElements(); + + do { + // Text node + if (node.nodeType == 3 && trim(node.nodeValue).length !== 0) { + if (start) { + rng.setStart(node, 0); + } else { + rng.setEnd(node, node.nodeValue.length); + } + + return; + } + + // BR/IMG/INPUT elements but not table cells + if (nonEmptyElementsMap[node.nodeName] && !/^(TD|TH)$/.test(node.nodeName)) { + if (start) { + rng.setStartBefore(node); + } else { + if (node.nodeName == 'BR') { + rng.setEndBefore(node); + } else { + rng.setEndAfter(node); + } + } + + return; + } + + // Found empty text block old IE can place the selection inside those + if (Env.ie && Env.ie < 11 && this.dom.isBlock(node) && this.dom.isEmpty(node)) { + if (start) { + rng.setStart(node, 0); + } else { + rng.setEnd(node, 0); + } + + return; + } + } while ((node = (start ? walker.next() : walker.prev()))); + + // Failed to find any text node or other suitable location then move to the root of body + if (root.nodeName == 'BODY') { + if (start) { + rng.setStart(root, 0); + } else { + rng.setEnd(root, root.childNodes.length); + } + } + }, + + getBoundingClientRect: function() { + var rng = this.getRng(); + return rng.collapsed ? CaretPosition.fromRangeStart(rng).getClientRects()[0] : rng.getBoundingClientRect(); + }, + + destroy: function() { + this.win = null; + this.controlSelection.destroy(); + } + }; + + return Selection; + } + ); + + /** + * ElementUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Utility class for various element specific functions. + * + * @private + * @class tinymce.dom.ElementUtils + */ + define( + 'tinymce.core.dom.ElementUtils', [ + "tinymce.core.dom.BookmarkManager", + "tinymce.core.util.Tools" + ], + function(BookmarkManager, Tools) { + var each = Tools.each; + + function ElementUtils(dom) { + /** + * Compares two nodes and checks if it's attributes and styles matches. + * This doesn't compare classes as items since their order is significant. + * + * @method compare + * @param {Node} node1 First node to compare with. + * @param {Node} node2 Second node to compare with. + * @return {boolean} True/false if the nodes are the same or not. + */ + this.compare = function(node1, node2) { + // Not the same name + if (node1.nodeName != node2.nodeName) { + return false; + } + + /** + * Returns all the nodes attributes excluding internal ones, styles and classes. + * + * @private + * @param {Node} node Node to get attributes from. + * @return {Object} Name/value object with attributes and attribute values. + */ + function getAttribs(node) { + var attribs = {}; + + each(dom.getAttribs(node), function(attr) { + var name = attr.nodeName.toLowerCase(); + + // Don't compare internal attributes or style + if (name.indexOf('_') !== 0 && name !== 'style' && name.indexOf('data-') !== 0) { + attribs[name] = dom.getAttrib(node, name); + } + }); + + return attribs; + } + + /** + * Compares two objects checks if it's key + value exists in the other one. + * + * @private + * @param {Object} obj1 First object to compare. + * @param {Object} obj2 Second object to compare. + * @return {boolean} True/false if the objects matches or not. + */ + function compareObjects(obj1, obj2) { + var value, name; + + for (name in obj1) { + // Obj1 has item obj2 doesn't have + if (obj1.hasOwnProperty(name)) { + value = obj2[name]; + + // Obj2 doesn't have obj1 item + if (typeof value == "undefined") { + return false; + } + + // Obj2 item has a different value + if (obj1[name] != value) { + return false; + } + + // Delete similar value + delete obj2[name]; + } + } + + // Check if obj 2 has something obj 1 doesn't have + for (name in obj2) { + // Obj2 has item obj1 doesn't have + if (obj2.hasOwnProperty(name)) { + return false; + } + } + + return true; + } + + // Attribs are not the same + if (!compareObjects(getAttribs(node1), getAttribs(node2))) { + return false; + } + + // Styles are not the same + if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) { + return false; + } + + return !BookmarkManager.isBookmarkNode(node1) && !BookmarkManager.isBookmarkNode(node2); + }; + } + + return ElementUtils; + } + ); + + /** + * Preview.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Internal class for generating previews styles for formats. + * + * Example: + * Preview.getCssText(editor, 'bold'); + * + * @private + * @class tinymce.fmt.Preview + */ + define( + 'tinymce.core.fmt.Preview', [ + "tinymce.core.dom.DOMUtils", + "tinymce.core.util.Tools", + "tinymce.core.html.Schema" + ], + function(DOMUtils, Tools, Schema) { + var each = Tools.each; + var dom = DOMUtils.DOM; + + function parsedSelectorToHtml(ancestry, editor) { + var elm, item, fragment; + var schema = editor && editor.schema || new Schema({}); + + function decorate(elm, item) { + if (item.classes.length) { + dom.addClass(elm, item.classes.join(' ')); + } + dom.setAttribs(elm, item.attrs); + } + + function createElement(sItem) { + var elm; + + item = typeof sItem === 'string' ? { + name: sItem, + classes: [], + attrs: {} + } : sItem; + + elm = dom.create(item.name); + decorate(elm, item); + return elm; + } + + function getRequiredParent(elm, candidate) { + var name = typeof elm !== 'string' ? elm.nodeName.toLowerCase() : elm; + var elmRule = schema.getElementRule(name); + var parentsRequired = elmRule && elmRule.parentsRequired; + + if (parentsRequired && parentsRequired.length) { + return candidate && Tools.inArray(parentsRequired, candidate) !== -1 ? candidate : parentsRequired[0]; + } else { + return false; + } + } + + function wrapInHtml(elm, ancestry, siblings) { + var parent, parentCandidate, parentRequired; + var ancestor = ancestry.length > 0 && ancestry[0]; + var ancestorName = ancestor && ancestor.name; + + parentRequired = getRequiredParent(elm, ancestorName); + + if (parentRequired) { + if (ancestorName == parentRequired) { + parentCandidate = ancestry[0]; + ancestry = ancestry.slice(1); + } else { + parentCandidate = parentRequired; + } + } else if (ancestor) { + parentCandidate = ancestry[0]; + ancestry = ancestry.slice(1); + } else if (!siblings) { + return elm; + } + + if (parentCandidate) { + parent = createElement(parentCandidate); + parent.appendChild(elm); + } + + if (siblings) { + if (!parent) { + // if no more ancestry, wrap in generic div + parent = dom.create('div'); + parent.appendChild(elm); + } + + Tools.each(siblings, function(sibling) { + var siblingElm = createElement(sibling); + parent.insertBefore(siblingElm, elm); + }); + } + + return wrapInHtml(parent, ancestry, parentCandidate && parentCandidate.siblings); + } + + if (ancestry && ancestry.length) { + item = ancestry[0]; + elm = createElement(item); + fragment = dom.create('div'); + fragment.appendChild(wrapInHtml(elm, ancestry.slice(1), item.siblings)); + return fragment; + } else { + return ''; + } + } + + + function selectorToHtml(selector, editor) { + return parsedSelectorToHtml(parseSelector(selector), editor); + } + + + function parseSelectorItem(item) { + var tagName; + var obj = { + classes: [], + attrs: {} + }; + + item = obj.selector = Tools.trim(item); + + if (item !== '*') { + // matching IDs, CLASSes, ATTRIBUTES and PSEUDOs + tagName = item.replace(/(?:([#\.]|::?)([\w\-]+)|(\[)([^\]]+)\]?)/g, function($0, $1, $2, $3, $4) { + switch ($1) { + case '#': + obj.attrs.id = $2; + break; + + case '.': + obj.classes.push($2); + break; + + case ':': + if (Tools.inArray('checked disabled enabled read-only required'.split(' '), $2) !== -1) { + obj.attrs[$2] = $2; + } + break; + } + + // atribute matched + if ($3 == '[') { + var m = $4.match(/([\w\-]+)(?:\=\"([^\"]+))?/); + if (m) { + obj.attrs[m[1]] = m[2]; + } + } + + return ''; + }); + } + + obj.name = tagName || 'div'; + return obj; + } + + + function parseSelector(selector) { + if (!selector || typeof selector !== 'string') { + return []; + } + + // take into account only first one + selector = selector.split(/\s*,\s*/)[0]; + + // tighten + selector = selector.replace(/\s*(~\+|~|\+|>)\s*/g, '$1'); + + // split either on > or on space, but not the one inside brackets + return Tools.map(selector.split(/(?:>|\s+(?![^\[\]]+\]))/), function(item) { + // process each sibling selector separately + var siblings = Tools.map(item.split(/(?:~\+|~|\+)/), parseSelectorItem); + var obj = siblings.pop(); // the last one is our real target + + if (siblings.length) { + obj.siblings = siblings; + } + return obj; + }).reverse(); + } + + + function getCssText(editor, format) { + var name, previewFrag, previewElm, items; + var previewCss = '', + parentFontSize, previewStyles; + + previewStyles = editor.settings.preview_styles; + + // No preview forced + if (previewStyles === false) { + return ''; + } + + // Default preview + if (typeof previewStyles !== 'string') { + previewStyles = 'font-family font-size font-weight font-style text-decoration ' + + 'text-transform color background-color border border-radius outline text-shadow'; + } + + // Removes any variables since these can't be previewed + function removeVars(val) { + return val.replace(/%(\w+)/g, ''); + } + + // Create block/inline element to use for preview + if (typeof format == "string") { + format = editor.formatter.get(format); + if (!format) { + return; + } + + format = format[0]; + } + + // Format specific preview override + // TODO: This should probably be further reduced by the previewStyles option + if ('preview' in format) { + previewStyles = format.preview; + if (previewStyles === false) { + return ''; + } + } + + name = format.block || format.inline || 'span'; + + items = parseSelector(format.selector); + if (items.length) { + if (!items[0].name) { // e.g. something like ul > .someClass was provided + items[0].name = name; + } + name = format.selector; + previewFrag = parsedSelectorToHtml(items, editor); + } else { + previewFrag = parsedSelectorToHtml([name], editor); + } + + previewElm = dom.select(name, previewFrag)[0] || previewFrag.firstChild; + + // Add format styles to preview element + each(format.styles, function(value, name) { + value = removeVars(value); + + if (value) { + dom.setStyle(previewElm, name, value); + } + }); + + // Add attributes to preview element + each(format.attributes, function(value, name) { + value = removeVars(value); + + if (value) { + dom.setAttrib(previewElm, name, value); + } + }); + + // Add classes to preview element + each(format.classes, function(value) { + value = removeVars(value); + + if (!dom.hasClass(previewElm, value)) { + dom.addClass(previewElm, value); + } + }); + + editor.fire('PreviewFormats'); + + // Add the previewElm outside the visual area + dom.setStyles(previewFrag, { + position: 'absolute', + left: -0xFFFF + }); + editor.getBody().appendChild(previewFrag); + + // Get parent container font size so we can compute px values out of em/% for older IE:s + parentFontSize = dom.getStyle(editor.getBody(), 'fontSize', true); + parentFontSize = /px$/.test(parentFontSize) ? parseInt(parentFontSize, 10) : 0; + + each(previewStyles.split(' '), function(name) { + var value = dom.getStyle(previewElm, name, true); + + // If background is transparent then check if the body has a background color we can use + if (name == 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) { + value = dom.getStyle(editor.getBody(), name, true); + + // Ignore white since it's the default color, not the nicest fix + // TODO: Fix this by detecting runtime style + if (dom.toHex(value).toLowerCase() == '#ffffff') { + return; + } + } + + if (name == 'color') { + // Ignore black since it's the default color, not the nicest fix + // TODO: Fix this by detecting runtime style + if (dom.toHex(value).toLowerCase() == '#000000') { + return; + } + } + + // Old IE won't calculate the font size so we need to do that manually + if (name == 'font-size') { + if (/em|%$/.test(value)) { + if (parentFontSize === 0) { + return; + } + + // Convert font size from em/% to px + value = parseFloat(value, 10) / (/%$/.test(value) ? 100 : 1); + value = (value * parentFontSize) + 'px'; + } + } + + if (name == "border" && value) { + previewCss += 'padding:0 2px;'; + } + + previewCss += name + ':' + value + ';'; + }); + + editor.fire('AfterPreviewFormats'); + + //previewCss += 'line-height:normal'; + + dom.remove(previewFrag); + + return previewCss; + } + + return { + getCssText: getCssText, + parseSelector: parseSelector, + selectorToHtml: selectorToHtml + }; + } + ); + + /** + * Hooks.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Internal class for overriding formatting. + * + * @private + * @class tinymce.fmt.Hooks + */ + define( + 'tinymce.core.fmt.Hooks', [ + "tinymce.core.util.Arr", + "tinymce.core.dom.NodeType", + "tinymce.core.dom.DomQuery" + ], + function(Arr, NodeType, $) { + var postProcessHooks = {}, + filter = Arr.filter, + each = Arr.each; + + function addPostProcessHook(name, hook) { + var hooks = postProcessHooks[name]; + + if (!hooks) { + postProcessHooks[name] = hooks = []; + } + + postProcessHooks[name].push(hook); + } + + function postProcess(name, editor) { + each(postProcessHooks[name], function(hook) { + hook(editor); + }); + } + + addPostProcessHook("pre", function(editor) { + var rng = editor.selection.getRng(), + isPre, blocks; + + function hasPreSibling(pre) { + return isPre(pre.previousSibling) && Arr.indexOf(blocks, pre.previousSibling) != -1; + } + + function joinPre(pre1, pre2) { + $(pre2).remove(); + $(pre1).append('<br><br>').append(pre2.childNodes); + } + + isPre = NodeType.matchNodeNames('pre'); + + if (!rng.collapsed) { + blocks = editor.selection.getSelectedBlocks(); + + each(filter(filter(blocks, isPre), hasPreSibling), function(pre) { + joinPre(pre.previousSibling, pre); + }); + } + }); + + return { + postProcess: postProcess + }; + } + ); + + /** + * Formatter.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Text formatter engine class. This class is used to apply formats like bold, italic, font size + * etc to the current selection or specific nodes. This engine was built to replace the browser's + * default formatting logic for execCommand due to its inconsistent and buggy behavior. + * + * @class tinymce.Formatter + * @example + * tinymce.activeEditor.formatter.register('mycustomformat', { + * inline: 'span', + * styles: {color: '#ff0000'} + * }); + * + * tinymce.activeEditor.formatter.apply('mycustomformat'); + */ + define( + 'tinymce.core.Formatter', [ + "tinymce.core.dom.TreeWalker", + "tinymce.core.dom.RangeUtils", + "tinymce.core.dom.BookmarkManager", + "tinymce.core.dom.ElementUtils", + "tinymce.core.dom.NodeType", + "tinymce.core.util.Fun", + "tinymce.core.util.Tools", + "tinymce.core.fmt.Preview", + "tinymce.core.fmt.Hooks" + ], + function(TreeWalker, RangeUtils, BookmarkManager, ElementUtils, NodeType, Fun, Tools, Preview, Hooks) { + /** + * Constructs a new formatter instance. + * + * @constructor Formatter + * @param {tinymce.Editor} ed Editor instance to construct the formatter engine to. + */ + return function(ed) { + var formats = {}, + dom = ed.dom, + selection = ed.selection, + rangeUtils = new RangeUtils(dom), + isValid = ed.schema.isValidChild, + isBlock = dom.isBlock, + forcedRootBlock = ed.settings.forced_root_block, + nodeIndex = dom.nodeIndex, + INVISIBLE_CHAR = '\uFEFF', + MCE_ATTR_RE = /^(src|href|style)$/, + FALSE = false, + TRUE = true, + formatChangeData, + undef, + getContentEditable = dom.getContentEditable, + disableCaretContainer, + markCaretContainersBogus, + isBookmarkNode = BookmarkManager.isBookmarkNode; + + var each = Tools.each, + grep = Tools.grep, + walk = Tools.walk, + extend = Tools.extend; + + function isTextBlock(name) { + if (name.nodeType) { + name = name.nodeName; + } + + return !!ed.schema.getTextBlockElements()[name.toLowerCase()]; + } + + function isTableCell(node) { + return /^(TH|TD)$/.test(node.nodeName); + } + + function isInlineBlock(node) { + return node && /^(IMG)$/.test(node.nodeName); + } + + function getParents(node, selector) { + return dom.getParents(node, selector, dom.getRoot()); + } + + function isCaretNode(node) { + return node.nodeType === 1 && node.id === '_mce_caret'; + } + + function defaultFormats() { + register({ + valigntop: [{ + selector: 'td,th', + styles: { + 'verticalAlign': 'top' + } + }], + + valignmiddle: [{ + selector: 'td,th', + styles: { + 'verticalAlign': 'middle' + } + }], + + valignbottom: [{ + selector: 'td,th', + styles: { + 'verticalAlign': 'bottom' + } + }], + + alignleft: [{ + selector: 'figure.image', + collapsed: false, + classes: 'align-left', + ceFalseOverride: true, + preview: 'font-family font-size' + }, + { + selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', + styles: { + textAlign: 'left' + }, + inherit: false, + preview: false, + defaultBlock: 'div' + }, + { + selector: 'img,table', + collapsed: false, + styles: { + 'float': 'left' + }, + preview: 'font-family font-size' + } + ], + + aligncenter: [{ + selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', + styles: { + textAlign: 'center' + }, + inherit: false, + preview: false, + defaultBlock: 'div' + }, + { + selector: 'figure.image', + collapsed: false, + classes: 'align-center', + ceFalseOverride: true, + preview: 'font-family font-size' + }, + { + selector: 'img', + collapsed: false, + styles: { + display: 'block', + marginLeft: 'auto', + marginRight: 'auto' + }, + preview: false + }, + { + selector: 'table', + collapsed: false, + styles: { + marginLeft: 'auto', + marginRight: 'auto' + }, + preview: 'font-family font-size' + } + ], + + alignright: [{ + selector: 'figure.image', + collapsed: false, + classes: 'align-right', + ceFalseOverride: true, + preview: 'font-family font-size' + }, + { + selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', + styles: { + textAlign: 'right' + }, + inherit: false, + preview: 'font-family font-size', + defaultBlock: 'div' + }, + { + selector: 'img,table', + collapsed: false, + styles: { + 'float': 'right' + }, + preview: 'font-family font-size' + } + ], + + alignjustify: [{ + selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', + styles: { + textAlign: 'justify' + }, + inherit: false, + defaultBlock: 'div', + preview: 'font-family font-size' + }], + + bold: [{ + inline: 'strong', + remove: 'all' + }, + { + inline: 'span', + styles: { + fontWeight: 'bold' + } + }, + { + inline: 'b', + remove: 'all' + } + ], + + italic: [{ + inline: 'em', + remove: 'all' + }, + { + inline: 'span', + styles: { + fontStyle: 'italic' + } + }, + { + inline: 'i', + remove: 'all' + } + ], + + underline: [{ + inline: 'span', + styles: { + textDecoration: 'underline' + }, + exact: true + }, + { + inline: 'u', + remove: 'all' + } + ], + + strikethrough: [{ + inline: 'span', + styles: { + textDecoration: 'line-through' + }, + exact: true + }, + { + inline: 'strike', + remove: 'all' + } + ], + + forecolor: { + inline: 'span', + styles: { + color: '%value' + }, + links: true, + remove_similar: true, + clear_child_styles: true + }, + hilitecolor: { + inline: 'span', + styles: { + backgroundColor: '%value' + }, + links: true, + remove_similar: true, + clear_child_styles: true + }, + fontname: { + inline: 'span', + styles: { + fontFamily: '%value' + }, + clear_child_styles: true + }, + fontsize: { + inline: 'span', + styles: { + fontSize: '%value' + }, + clear_child_styles: true + }, + fontsize_class: { + inline: 'span', + attributes: { + 'class': '%value' + } + }, + blockquote: { + block: 'blockquote', + wrapper: 1, + remove: 'all' + }, + subscript: { + inline: 'sub' + }, + superscript: { + inline: 'sup' + }, + code: { + inline: 'code' + }, + + link: { + inline: 'a', + selector: 'a', + remove: 'all', + split: true, + deep: true, + onmatch: function() { + return true; + }, + + onformat: function(elm, fmt, vars) { + each(vars, function(value, key) { + dom.setAttrib(elm, key, value); + }); + } + }, + + removeformat: [{ + selector: 'b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins', + remove: 'all', + split: true, + expand: false, + block_expand: true, + deep: true + }, + { + selector: 'span', + attributes: ['style', 'class'], + remove: 'empty', + split: true, + expand: false, + deep: true + }, + { + selector: '*', + attributes: ['style', 'class'], + split: false, + expand: false, + deep: true + } + ] + }); + + // Register default block formats + each('p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp'.split(/\s/), function(name) { + register(name, { + block: name, + remove: 'all' + }); + }); + + // Register user defined formats + register(ed.settings.formats); + } + + var clearChildStyles = function(format, node) { + if (format.clear_child_styles) { + each(dom.select('*', node), function(node) { + each(format.styles, function(value, name) { + dom.setStyle(node, name, ''); + }); + }); + } + }; + + function addKeyboardShortcuts() { + // Add some inline shortcuts + ed.addShortcut('meta+b', 'bold_desc', 'Bold'); + ed.addShortcut('meta+i', 'italic_desc', 'Italic'); + ed.addShortcut('meta+u', 'underline_desc', 'Underline'); + + // BlockFormat shortcuts keys + for (var i = 1; i <= 6; i++) { + ed.addShortcut('access+' + i, '', ['FormatBlock', false, 'h' + i]); + } + + ed.addShortcut('access+7', '', ['FormatBlock', false, 'p']); + ed.addShortcut('access+8', '', ['FormatBlock', false, 'div']); + ed.addShortcut('access+9', '', ['FormatBlock', false, 'address']); + } + + // Public functions + + /** + * Returns the format by name or all formats if no name is specified. + * + * @method get + * @param {String} name Optional name to retrieve by. + * @return {Array/Object} Array/Object with all registered formats or a specific format. + */ + function get(name) { + return name ? formats[name] : formats; + } + + /** + * Registers a specific format by name. + * + * @method register + * @param {Object/String} name Name of the format for example "bold". + * @param {Object/Array} format Optional format object or array of format variants + * can only be omitted if the first arg is an object. + */ + function register(name, format) { + if (name) { + if (typeof name !== 'string') { + each(name, function(format, name) { + register(name, format); + }); + } else { + // Force format into array and add it to internal collection + format = format.length ? format : [format]; + + each(format, function(format) { + // Set deep to false by default on selector formats this to avoid removing + // alignment on images inside paragraphs when alignment is changed on paragraphs + if (format.deep === undef) { + format.deep = !format.selector; + } + + // Default to true + if (format.split === undef) { + format.split = !format.selector || format.inline; + } + + // Default to true + if (format.remove === undef && format.selector && !format.inline) { + format.remove = 'none'; + } + + // Mark format as a mixed format inline + block level + if (format.selector && format.inline) { + format.mixed = true; + format.block_expand = true; + } + + // Split classes if needed + if (typeof format.classes === 'string') { + format.classes = format.classes.split(/\s+/); + } + }); + + formats[name] = format; + } + } + } + + /** + * Unregister a specific format by name. + * + * @method unregister + * @param {String} name Name of the format for example "bold". + */ + function unregister(name) { + if (name && formats[name]) { + delete formats[name]; + } + + return formats; + } + + function matchesUnInheritedFormatSelector(node, name) { + var formatList = get(name); + + if (formatList) { + for (var i = 0; i < formatList.length; i++) { + if (formatList[i].inherit === false && dom.is(node, formatList[i].selector)) { + return true; + } + } + } + + return false; + } + + function getTextDecoration(node) { + var decoration; + + ed.dom.getParent(node, function(n) { + decoration = ed.dom.getStyle(n, 'text-decoration'); + return decoration && decoration !== 'none'; + }); + + return decoration; + } + + function processUnderlineAndColor(node) { + var textDecoration; + if (node.nodeType === 1 && node.parentNode && node.parentNode.nodeType === 1) { + textDecoration = getTextDecoration(node.parentNode); + if (ed.dom.getStyle(node, 'color') && textDecoration) { + ed.dom.setStyle(node, 'text-decoration', textDecoration); + } else if (ed.dom.getStyle(node, 'text-decoration') === textDecoration) { + ed.dom.setStyle(node, 'text-decoration', null); + } + } + } + + /** + * Applies the specified format to the current selection or specified node. + * + * @method apply + * @param {String} name Name of format to apply. + * @param {Object} vars Optional list of variables to replace within format before applying it. + * @param {Node} node Optional node to apply the format to defaults to current selection. + */ + function apply(name, vars, node) { + var formatList = get(name), + format = formatList[0], + bookmark, rng, isCollapsed = !node && selection.isCollapsed(); + + function setElementFormat(elm, fmt) { + fmt = fmt || format; + + if (elm) { + if (fmt.onformat) { + fmt.onformat(elm, fmt, vars, node); + } + + each(fmt.styles, function(value, name) { + dom.setStyle(elm, name, replaceVars(value, vars)); + }); + + // Needed for the WebKit span spam bug + // TODO: Remove this once WebKit/Blink fixes this + if (fmt.styles) { + var styleVal = dom.getAttrib(elm, 'style'); + + if (styleVal) { + elm.setAttribute('data-mce-style', styleVal); + } + } + + each(fmt.attributes, function(value, name) { + dom.setAttrib(elm, name, replaceVars(value, vars)); + }); + + each(fmt.classes, function(value) { + value = replaceVars(value, vars); + + if (!dom.hasClass(elm, value)) { + dom.addClass(elm, value); + } + }); + } + } + + function applyNodeStyle(formatList, node) { + var found = false; + + if (!format.selector) { + return false; + } + + // Look for matching formats + each(formatList, function(format) { + // Check collapsed state if it exists + if ('collapsed' in format && format.collapsed !== isCollapsed) { + return; + } + + if (dom.is(node, format.selector) && !isCaretNode(node)) { + setElementFormat(node, format); + found = true; + return false; + } + }); + + return found; + } + + // This converts: <p>[a</p><p>]b</p> -> <p>[a]</p><p>b</p> + function adjustSelectionToVisibleSelection() { + function findSelectionEnd(start, end) { + var walker = new TreeWalker(end); + for (node = walker.prev2(); node; node = walker.prev2()) { + if (node.nodeType == 3 && node.data.length > 0) { + return node; + } + + if (node.childNodes.length > 1 || node == start || node.tagName == 'BR') { + return node; + } + } + } + + // Adjust selection so that a end container with a end offset of zero is not included in the selection + // as this isn't visible to the user. + var rng = ed.selection.getRng(); + var start = rng.startContainer; + var end = rng.endContainer; + + if (start != end && rng.endOffset === 0) { + var newEnd = findSelectionEnd(start, end); + var endOffset = newEnd.nodeType == 3 ? newEnd.data.length : newEnd.childNodes.length; + + rng.setEnd(newEnd, endOffset); + } + + return rng; + } + + function applyRngStyle(rng, bookmark, nodeSpecific) { + var newWrappers = [], + wrapName, wrapElm, contentEditable = true; + + // Setup wrapper element + wrapName = format.inline || format.block; + wrapElm = dom.create(wrapName); + setElementFormat(wrapElm); + + rangeUtils.walk(rng, function(nodes) { + var currentWrapElm; + + /** + * Process a list of nodes wrap them. + */ + function process(node) { + var nodeName, parentName, hasContentEditableState, lastContentEditable; + + lastContentEditable = contentEditable; + nodeName = node.nodeName.toLowerCase(); + parentName = node.parentNode.nodeName.toLowerCase(); + + // Node has a contentEditable value + if (node.nodeType === 1 && getContentEditable(node)) { + lastContentEditable = contentEditable; + contentEditable = getContentEditable(node) === "true"; + hasContentEditableState = true; // We don't want to wrap the container only it's children + } + + // Stop wrapping on br elements + if (isEq(nodeName, 'br')) { + currentWrapElm = 0; + + // Remove any br elements when we wrap things + if (format.block) { + dom.remove(node); + } + + return; + } + + // If node is wrapper type + if (format.wrapper && matchNode(node, name, vars)) { + currentWrapElm = 0; + return; + } + + // Can we rename the block + // TODO: Break this if up, too complex + if (contentEditable && !hasContentEditableState && format.block && + !format.wrapper && isTextBlock(nodeName) && isValid(parentName, wrapName)) { + node = dom.rename(node, wrapName); + setElementFormat(node); + newWrappers.push(node); + currentWrapElm = 0; + return; + } + + // Handle selector patterns + if (format.selector) { + var found = applyNodeStyle(formatList, node); + + // Continue processing if a selector match wasn't found and a inline element is defined + if (!format.inline || found) { + currentWrapElm = 0; + return; + } + } + + // Is it valid to wrap this item + // TODO: Break this if up, too complex + if (contentEditable && !hasContentEditableState && isValid(wrapName, nodeName) && isValid(parentName, wrapName) && + !(!nodeSpecific && node.nodeType === 3 && + node.nodeValue.length === 1 && + node.nodeValue.charCodeAt(0) === 65279) && + !isCaretNode(node) && + (!format.inline || !isBlock(node))) { + // Start wrapping + if (!currentWrapElm) { + // Wrap the node + currentWrapElm = dom.clone(wrapElm, FALSE); + node.parentNode.insertBefore(currentWrapElm, node); + newWrappers.push(currentWrapElm); + } + + currentWrapElm.appendChild(node); + } else { + // Start a new wrapper for possible children + currentWrapElm = 0; + + each(grep(node.childNodes), process); + + if (hasContentEditableState) { + contentEditable = lastContentEditable; // Restore last contentEditable state from stack + } + + // End the last wrapper + currentWrapElm = 0; + } + } + + // Process siblings from range + each(nodes, process); + }); + + // Apply formats to links as well to get the color of the underline to change as well + if (format.links === true) { + each(newWrappers, function(node) { + function process(node) { + if (node.nodeName === 'A') { + setElementFormat(node, format); + } + + each(grep(node.childNodes), process); + } + + process(node); + }); + } + + // Cleanup + each(newWrappers, function(node) { + var childCount; + + function getChildCount(node) { + var count = 0; + + each(node.childNodes, function(node) { + if (!isWhiteSpaceNode(node) && !isBookmarkNode(node)) { + count++; + } + }); + + return count; + } + + function getChildElementNode(root) { + var child = false; + each(root.childNodes, function(node) { + if (isElementNode(node)) { + child = node; + return false; // break loop + } + }); + return child; + } + + function matchNestedWrapper(node, filter) { + do { + if (getChildCount(node) !== 1) { + break; + } + + node = getChildElementNode(node); + if (!node) { + break; + } else if (filter(node)) { + return node; + } + } while (node); + + return null; + } + + function mergeStyles(node) { + var child, clone; + + child = getChildElementNode(node); + + // If child was found and of the same type as the current node + if (child && !isBookmarkNode(child) && matchName(child, format)) { + clone = dom.clone(child, FALSE); + setElementFormat(clone); + + dom.replace(clone, node, TRUE); + dom.remove(child, 1); + } + + return clone || node; + } + + childCount = getChildCount(node); + + // Remove empty nodes but only if there is multiple wrappers and they are not block + // elements so never remove single <h1></h1> since that would remove the + // current empty block element where the caret is at + if ((newWrappers.length > 1 || !isBlock(node)) && childCount === 0) { + dom.remove(node, 1); + return; + } + + if (format.inline || format.wrapper) { + // Merges the current node with it's children of similar type to reduce the number of elements + if (!format.exact && childCount === 1) { + node = mergeStyles(node); + } + + // Remove/merge children + each(formatList, function(format) { + // Merge all children of similar type will move styles from child to parent + // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span> + // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span> + each(dom.select(format.inline, node), function(child) { + if (!isElementNode(child)) { + return; + } + + removeFormat(format, vars, child, format.exact ? child : null); + }); + + clearChildStyles(format, node); + }); + + // Remove format if direct parent already has the same format + if (matchNode(node.parentNode, name, vars)) { + if (removeFormat(format, vars, node)) { + node = 0; + } + } + + // Remove format if any ancestor already has the same format + if (format.merge_with_parents) { + dom.getParent(node.parentNode, function(parent) { + if (matchNode(parent, name, vars)) { + if (removeFormat(format, vars, node)) { + node = 0; + } + return TRUE; + } + }); + } + + // fontSize defines the line height for the whole branch of nested style wrappers, + // therefore it should be set on the outermost wrapper + if (node && !isBlock(node) && !getStyle(node, 'fontSize')) { + var styleNode = matchNestedWrapper(node, hasStyle('fontSize')); + if (styleNode) { + apply('fontsize', { + value: getStyle(styleNode, 'fontSize') + }, node); + } + } + + // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b> + if (node && format.merge_siblings !== false) { + node = mergeSiblings(getNonWhiteSpaceSibling(node), node); + node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE)); + } + } + }); + } + + if (getContentEditable(selection.getNode()) === "false") { + node = selection.getNode(); + for (var i = 0, l = formatList.length; i < l; i++) { + if (formatList[i].ceFalseOverride && dom.is(node, formatList[i].selector)) { + setElementFormat(node, formatList[i]); + return; + } + } + + return; + } + + if (format) { + if (node) { + if (node.nodeType) { + if (!applyNodeStyle(formatList, node)) { + rng = dom.createRng(); + rng.setStartBefore(node); + rng.setEndAfter(node); + applyRngStyle(expandRng(rng, formatList), null, true); + } + } else { + applyRngStyle(node, null, true); + } + } else { + if (!isCollapsed || !format.inline || dom.select('td[data-mce-selected],th[data-mce-selected]').length) { + // Obtain selection node before selection is unselected by applyRngStyle() + var curSelNode = ed.selection.getNode(); + + // If the formats have a default block and we can't find a parent block then + // start wrapping it with a DIV this is for forced_root_blocks: false + // It's kind of a hack but people should be using the default block type P since all desktop editors work that way + if (!forcedRootBlock && formatList[0].defaultBlock && !dom.getParent(curSelNode, dom.isBlock)) { + apply(formatList[0].defaultBlock); + } + + // Apply formatting to selection + ed.selection.setRng(adjustSelectionToVisibleSelection()); + bookmark = selection.getBookmark(); + applyRngStyle(expandRng(selection.getRng(TRUE), formatList), bookmark); + + if (format.styles) { + // Colored nodes should be underlined so that the color of the underline matches the text color. + if (format.styles.color || format.styles.textDecoration) { + walk(curSelNode, processUnderlineAndColor, 'childNodes'); + processUnderlineAndColor(curSelNode); + } + + // nodes with font-size should have their own background color as well to fit the line-height (see TINY-882) + if (format.styles.backgroundColor) { + processChildElements(curSelNode, + hasStyle('fontSize'), + applyStyle('backgroundColor', replaceVars(format.styles.backgroundColor, vars)) + ); + } + } + + selection.moveToBookmark(bookmark); + moveStart(selection.getRng(TRUE)); + ed.nodeChanged(); + } else { + performCaretAction('apply', name, vars); + } + } + + Hooks.postProcess(name, ed); + } + } + + /** + * Removes the specified format from the current selection or specified node. + * + * @method remove + * @param {String} name Name of format to remove. + * @param {Object} vars Optional list of variables to replace within format before removing it. + * @param {Node/Range} node Optional node or DOM range to remove the format from defaults to current selection. + */ + function remove(name, vars, node, similar) { + var formatList = get(name), + format = formatList[0], + bookmark, rng, contentEditable = true; + + // Merges the styles for each node + function process(node) { + var children, i, l, lastContentEditable, hasContentEditableState; + + // Node has a contentEditable value + if (node.nodeType === 1 && getContentEditable(node)) { + lastContentEditable = contentEditable; + contentEditable = getContentEditable(node) === "true"; + hasContentEditableState = true; // We don't want to wrap the container only it's children + } + + // Grab the children first since the nodelist might be changed + children = grep(node.childNodes); + + // Process current node + if (contentEditable && !hasContentEditableState) { + for (i = 0, l = formatList.length; i < l; i++) { + if (removeFormat(formatList[i], vars, node, node)) { + break; + } + } + } + + // Process the children + if (format.deep) { + if (children.length) { + for (i = 0, l = children.length; i < l; i++) { + process(children[i]); + } + + if (hasContentEditableState) { + contentEditable = lastContentEditable; // Restore last contentEditable state from stack + } + } + } + } + + function findFormatRoot(container) { + var formatRoot; + + // Find format root + each(getParents(container.parentNode).reverse(), function(parent) { + var format; + + // Find format root element + if (!formatRoot && parent.id != '_start' && parent.id != '_end') { + // Is the node matching the format we are looking for + format = matchNode(parent, name, vars, similar); + if (format && format.split !== false) { + formatRoot = parent; + } + } + }); + + return formatRoot; + } + + function wrapAndSplit(formatRoot, container, target, split) { + var parent, clone, lastClone, firstClone, i, formatRootParent; + + // Format root found then clone formats and split it + if (formatRoot) { + formatRootParent = formatRoot.parentNode; + + for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) { + clone = dom.clone(parent, FALSE); + + for (i = 0; i < formatList.length; i++) { + if (removeFormat(formatList[i], vars, clone, clone)) { + clone = 0; + break; + } + } + + // Build wrapper node + if (clone) { + if (lastClone) { + clone.appendChild(lastClone); + } + + if (!firstClone) { + firstClone = clone; + } + + lastClone = clone; + } + } + + // Never split block elements if the format is mixed + if (split && (!format.mixed || !isBlock(formatRoot))) { + container = dom.split(formatRoot, container); + } + + // Wrap container in cloned formats + if (lastClone) { + target.parentNode.insertBefore(lastClone, target); + firstClone.appendChild(target); + } + } + + return container; + } + + function splitToFormatRoot(container) { + return wrapAndSplit(findFormatRoot(container), container, container, true); + } + + function unwrap(start) { + var node = dom.get(start ? '_start' : '_end'), + out = node[start ? 'firstChild' : 'lastChild']; + + // If the end is placed within the start the result will be removed + // So this checks if the out node is a bookmark node if it is it + // checks for another more suitable node + if (isBookmarkNode(out)) { + out = out[start ? 'firstChild' : 'lastChild']; + } + + // Since dom.remove removes empty text nodes then we need to try to find a better node + if (out.nodeType == 3 && out.data.length === 0) { + out = start ? node.previousSibling || node.nextSibling : node.nextSibling || node.previousSibling; + } + + dom.remove(node, true); + + return out; + } + + function removeRngStyle(rng) { + var startContainer, endContainer; + var commonAncestorContainer = rng.commonAncestorContainer; + + rng = expandRng(rng, formatList, TRUE); + + if (format.split) { + startContainer = getContainer(rng, TRUE); + endContainer = getContainer(rng); + + if (startContainer != endContainer) { + // WebKit will render the table incorrectly if we wrap a TH or TD in a SPAN + // so let's see if we can use the first child instead + // This will happen if you triple click a table cell and use remove formatting + if (/^(TR|TH|TD)$/.test(startContainer.nodeName) && startContainer.firstChild) { + if (startContainer.nodeName == "TR") { + startContainer = startContainer.firstChild.firstChild || startContainer; + } else { + startContainer = startContainer.firstChild || startContainer; + } + } + + // Try to adjust endContainer as well if cells on the same row were selected - bug #6410 + if (commonAncestorContainer && + /^T(HEAD|BODY|FOOT|R)$/.test(commonAncestorContainer.nodeName) && + isTableCell(endContainer) && endContainer.firstChild) { + endContainer = endContainer.firstChild || endContainer; + } + + if (dom.isChildOf(startContainer, endContainer) && !isBlock(endContainer) && + !isTableCell(startContainer) && !isTableCell(endContainer)) { + startContainer = wrap(startContainer, 'span', { + id: '_start', + 'data-mce-type': 'bookmark' + }); + splitToFormatRoot(startContainer); + startContainer = unwrap(TRUE); + return; + } + + // Wrap start/end nodes in span element since these might be cloned/moved + startContainer = wrap(startContainer, 'span', { + id: '_start', + 'data-mce-type': 'bookmark' + }); + endContainer = wrap(endContainer, 'span', { + id: '_end', + 'data-mce-type': 'bookmark' + }); + + // Split start/end + splitToFormatRoot(startContainer); + splitToFormatRoot(endContainer); + + // Unwrap start/end to get real elements again + startContainer = unwrap(TRUE); + endContainer = unwrap(); + } else { + startContainer = endContainer = splitToFormatRoot(startContainer); + } + + // Update range positions since they might have changed after the split operations + rng.startContainer = startContainer.parentNode ? startContainer.parentNode : startContainer; + rng.startOffset = nodeIndex(startContainer); + rng.endContainer = endContainer.parentNode ? endContainer.parentNode : endContainer; + rng.endOffset = nodeIndex(endContainer) + 1; + } + + // Remove items between start/end + rangeUtils.walk(rng, function(nodes) { + each(nodes, function(node) { + process(node); + + // Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined. + if (node.nodeType === 1 && ed.dom.getStyle(node, 'text-decoration') === 'underline' && + node.parentNode && getTextDecoration(node.parentNode) === 'underline') { + removeFormat({ + 'deep': false, + 'exact': true, + 'inline': 'span', + 'styles': { + 'textDecoration': 'underline' + } + }, null, node); + } + }); + }); + } + + // Handle node + if (node) { + if (node.nodeType) { + rng = dom.createRng(); + rng.setStartBefore(node); + rng.setEndAfter(node); + removeRngStyle(rng); + } else { + removeRngStyle(node); + } + + return; + } + + if (getContentEditable(selection.getNode()) === "false") { + node = selection.getNode(); + for (var i = 0, l = formatList.length; i < l; i++) { + if (formatList[i].ceFalseOverride) { + if (removeFormat(formatList[i], vars, node, node)) { + break; + } + } + } + + return; + } + + if (!selection.isCollapsed() || !format.inline || dom.select('td[data-mce-selected],th[data-mce-selected]').length) { + bookmark = selection.getBookmark(); + removeRngStyle(selection.getRng(TRUE)); + selection.moveToBookmark(bookmark); + + // Check if start element still has formatting then we are at: "<b>text|</b>text" + // and need to move the start into the next text node + if (format.inline && match(name, vars, selection.getStart())) { + moveStart(selection.getRng(true)); + } + + ed.nodeChanged(); + } else { + performCaretAction('remove', name, vars, similar); + } + } + + /** + * Toggles the specified format on/off. + * + * @method toggle + * @param {String} name Name of format to apply/remove. + * @param {Object} vars Optional list of variables to replace within format before applying/removing it. + * @param {Node} node Optional node to apply the format to or remove from. Defaults to current selection. + */ + function toggle(name, vars, node) { + var fmt = get(name); + + if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle)) { + remove(name, vars, node); + } else { + apply(name, vars, node); + } + } + + /** + * Return true/false if the specified node has the specified format. + * + * @method matchNode + * @param {Node} node Node to check the format on. + * @param {String} name Format name to check. + * @param {Object} vars Optional list of variables to replace before checking it. + * @param {Boolean} similar Match format that has similar properties. + * @return {Object} Returns the format object it matches or undefined if it doesn't match. + */ + function matchNode(node, name, vars, similar) { + var formatList = get(name), + format, i, classes; + + function matchItems(node, format, itemName) { + var key, value, items = format[itemName], + i; + + // Custom match + if (format.onmatch) { + return format.onmatch(node, format, itemName); + } + + // Check all items + if (items) { + // Non indexed object + if (items.length === undef) { + for (key in items) { + if (items.hasOwnProperty(key)) { + if (itemName === 'attributes') { + value = dom.getAttrib(node, key); + } else { + value = getStyle(node, key); + } + + if (similar && !value && !format.exact) { + return; + } + + if ((!similar || format.exact) && !isEq(value, normalizeStyleValue(replaceVars(items[key], vars), key))) { + return; + } + } + } + } else { + // Only one match needed for indexed arrays + for (i = 0; i < items.length; i++) { + if (itemName === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i])) { + return format; + } + } + } + } + + return format; + } + + if (formatList && node) { + // Check each format in list + for (i = 0; i < formatList.length; i++) { + format = formatList[i]; + + // Name name, attributes, styles and classes + if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) { + // Match classes + if ((classes = format.classes)) { + for (i = 0; i < classes.length; i++) { + if (!dom.hasClass(node, classes[i])) { + return; + } + } + } + + return format; + } + } + } + } + + /** + * Matches the current selection or specified node against the specified format name. + * + * @method match + * @param {String} name Name of format to match. + * @param {Object} vars Optional list of variables to replace before checking it. + * @param {Node} node Optional node to check. + * @return {boolean} true/false if the specified selection/node matches the format. + */ + function match(name, vars, node) { + var startNode; + + function matchParents(node) { + var root = dom.getRoot(); + + if (node === root) { + return false; + } + + // Find first node with similar format settings + node = dom.getParent(node, function(node) { + if (matchesUnInheritedFormatSelector(node, name)) { + return true; + } + + return node.parentNode === root || !!matchNode(node, name, vars, true); + }); + + // Do an exact check on the similar format element + return matchNode(node, name, vars); + } + + // Check specified node + if (node) { + return matchParents(node); + } + + // Check selected node + node = selection.getNode(); + if (matchParents(node)) { + return TRUE; + } + + // Check start node if it's different + startNode = selection.getStart(); + if (startNode != node) { + if (matchParents(startNode)) { + return TRUE; + } + } + + return FALSE; + } + + /** + * Matches the current selection against the array of formats and returns a new array with matching formats. + * + * @method matchAll + * @param {Array} names Name of format to match. + * @param {Object} vars Optional list of variables to replace before checking it. + * @return {Array} Array with matched formats. + */ + function matchAll(names, vars) { + var startElement, matchedFormatNames = [], + checkedMap = {}; + + // Check start of selection for formats + startElement = selection.getStart(); + dom.getParent(startElement, function(node) { + var i, name; + + for (i = 0; i < names.length; i++) { + name = names[i]; + + if (!checkedMap[name] && matchNode(node, name, vars)) { + checkedMap[name] = true; + matchedFormatNames.push(name); + } + } + }, dom.getRoot()); + + return matchedFormatNames; + } + + /** + * Returns true/false if the specified format can be applied to the current selection or not. It + * will currently only check the state for selector formats, it returns true on all other format types. + * + * @method canApply + * @param {String} name Name of format to check. + * @return {boolean} true/false if the specified format can be applied to the current selection/node. + */ + function canApply(name) { + var formatList = get(name), + startNode, parents, i, x, selector; + + if (formatList) { + startNode = selection.getStart(); + parents = getParents(startNode); + + for (x = formatList.length - 1; x >= 0; x--) { + selector = formatList[x].selector; + + // Format is not selector based then always return TRUE + // Is it has a defaultBlock then it's likely it can be applied for example align on a non block element line + if (!selector || formatList[x].defaultBlock) { + return TRUE; + } + + for (i = parents.length - 1; i >= 0; i--) { + if (dom.is(parents[i], selector)) { + return TRUE; + } + } + } + } + + return FALSE; + } + + /** + * Executes the specified callback when the current selection matches the formats or not. + * + * @method formatChanged + * @param {String} formats Comma separated list of formats to check for. + * @param {function} callback Callback with state and args when the format is changed/toggled on/off. + * @param {Boolean} similar True/false state if the match should handle similar or exact formats. + */ + function formatChanged(formats, callback, similar) { + var currentFormats; + + // Setup format node change logic + if (!formatChangeData) { + formatChangeData = {}; + currentFormats = {}; + + ed.on('NodeChange', function(e) { + var parents = getParents(e.element), + matchedFormats = {}; + + // Ignore bogus nodes like the <a> tag created by moveStart() + parents = Tools.grep(parents, function(node) { + return node.nodeType == 1 && !node.getAttribute('data-mce-bogus'); + }); + + // Check for new formats + each(formatChangeData, function(callbacks, format) { + each(parents, function(node) { + if (matchNode(node, format, {}, callbacks.similar)) { + if (!currentFormats[format]) { + // Execute callbacks + each(callbacks, function(callback) { + callback(true, { + node: node, + format: format, + parents: parents + }); + }); + + currentFormats[format] = callbacks; + } + + matchedFormats[format] = callbacks; + return false; + } + + if (matchesUnInheritedFormatSelector(node, format)) { + return false; + } + }); + }); + + // Check if current formats still match + each(currentFormats, function(callbacks, format) { + if (!matchedFormats[format]) { + delete currentFormats[format]; + + each(callbacks, function(callback) { + callback(false, { + node: e.element, + format: format, + parents: parents + }); + }); + } + }); + }); + } + + // Add format listeners + each(formats.split(','), function(format) { + if (!formatChangeData[format]) { + formatChangeData[format] = []; + formatChangeData[format].similar = similar; + } + + formatChangeData[format].push(callback); + }); + + return this; + } + + /** + * Returns a preview css text for the specified format. + * + * @method getCssText + * @param {String/Object} format Format to generate preview css text for. + * @return {String} Css text for the specified format. + * @example + * var cssText1 = editor.formatter.getCssText('bold'); + * var cssText2 = editor.formatter.getCssText({inline: 'b'}); + */ + function getCssText(format) { + return Preview.getCssText(ed, format); + } + + // Expose to public + extend(this, { + get: get, + register: register, + unregister: unregister, + apply: apply, + remove: remove, + toggle: toggle, + match: match, + matchAll: matchAll, + matchNode: matchNode, + canApply: canApply, + formatChanged: formatChanged, + getCssText: getCssText + }); + + // Initialize + defaultFormats(); + addKeyboardShortcuts(); + ed.on('BeforeGetContent', function(e) { + if (markCaretContainersBogus && e.format != 'raw') { + markCaretContainersBogus(); + } + }); + ed.on('mouseup keydown', function(e) { + if (disableCaretContainer) { + disableCaretContainer(e); + } + }); + + // Private functions + + /** + * Checks if the specified nodes name matches the format inline/block or selector. + * + * @private + * @param {Node} node Node to match against the specified format. + * @param {Object} format Format object o match with. + * @return {boolean} true/false if the format matches. + */ + function matchName(node, format) { + // Check for inline match + if (isEq(node, format.inline)) { + return TRUE; + } + + // Check for block match + if (isEq(node, format.block)) { + return TRUE; + } + + // Check for selector match + if (format.selector) { + return node.nodeType == 1 && dom.is(node, format.selector); + } + } + + /** + * Compares two string/nodes regardless of their case. + * + * @private + * @param {String/Node} str1 Node or string to compare. + * @param {String/Node} str2 Node or string to compare. + * @return {boolean} True/false if they match. + */ + function isEq(str1, str2) { + str1 = str1 || ''; + str2 = str2 || ''; + + str1 = '' + (str1.nodeName || str1); + str2 = '' + (str2.nodeName || str2); + + return str1.toLowerCase() == str2.toLowerCase(); + } + + function processChildElements(node, filter, process) { + each(node.childNodes, function(node) { + if (isElementNode(node)) { + if (filter(node)) { + process(node); + } + if (node.hasChildNodes()) { + processChildElements(node, filter, process); + } + } + }); + } + + function isElementNode(node) { + return node && node.nodeType === 1 && !isBookmarkNode(node) && !isCaretNode(node) && !NodeType.isBogus(node); + } + + function hasStyle(name) { + return Fun.curry(function(name, node) { + return !!(node && getStyle(node, name)); + }, name); + } + + function applyStyle(name, value) { + return Fun.curry(function(name, value, node) { + dom.setStyle(node, name, value); + }, name, value); + } + + /** + * Returns the style by name on the specified node. This method modifies the style + * contents to make it more easy to match. This will resolve a few browser issues. + * + * @private + * @param {Node} node to get style from. + * @param {String} name Style name to get. + * @return {String} Style item value. + */ + function getStyle(node, name) { + return normalizeStyleValue(dom.getStyle(node, name), name); + } + + /** + * Normalize style value by name. This method modifies the style contents + * to make it more easy to match. This will resolve a few browser issues. + * + * @private + * @param {String} value Value to get style from. + * @param {String} name Style name to get. + * @return {String} Style item value. + */ + function normalizeStyleValue(value, name) { + // Force the format to hex + if (name == 'color' || name == 'backgroundColor') { + value = dom.toHex(value); + } + + // Opera will return bold as 700 + if (name == 'fontWeight' && value == 700) { + value = 'bold'; + } + + // Normalize fontFamily so "'Font name', Font" becomes: "Font name,Font" + if (name == 'fontFamily') { + value = value.replace(/[\'\"]/g, '').replace(/,\s+/g, ','); + } + + return '' + value; + } + + /** + * Replaces variables in the value. The variable format is %var. + * + * @private + * @param {String} value Value to replace variables in. + * @param {Object} vars Name/value array with variables to replace. + * @return {String} New value with replaced variables. + */ + function replaceVars(value, vars) { + if (typeof value != "string") { + value = value(vars); + } else if (vars) { + value = value.replace(/%(\w+)/g, function(str, name) { + return vars[name] || str; + }); + } + + return value; + } + + function isWhiteSpaceNode(node) { + return node && node.nodeType === 3 && /^([\t \r\n]+|)$/.test(node.nodeValue); + } + + function wrap(node, name, attrs) { + var wrapper = dom.create(name, attrs); + + node.parentNode.insertBefore(wrapper, node); + wrapper.appendChild(node); + + return wrapper; + } + + /** + * Expands the specified range like object to depending on format. + * + * For example on block formats it will move the start/end position + * to the beginning of the current block. + * + * @private + * @param {Object} rng Range like object. + * @param {Array} format Array with formats to expand by. + * @param {Boolean} remove + * @return {Object} Expanded range like object. + */ + function expandRng(rng, format, remove) { + var lastIdx, leaf, endPoint, + startContainer = rng.startContainer, + startOffset = rng.startOffset, + endContainer = rng.endContainer, + endOffset = rng.endOffset; + + // This function walks up the tree if there is no siblings before/after the node + function findParentContainer(start) { + var container, parent, sibling, siblingName, root; + + container = parent = start ? startContainer : endContainer; + siblingName = start ? 'previousSibling' : 'nextSibling'; + root = dom.getRoot(); + + function isBogusBr(node) { + return node.nodeName == "BR" && node.getAttribute('data-mce-bogus') && !node.nextSibling; + } + + // If it's a text node and the offset is inside the text + if (container.nodeType == 3 && !isWhiteSpaceNode(container)) { + if (start ? startOffset > 0 : endOffset < container.nodeValue.length) { + return container; + } + } + + /*eslint no-constant-condition:0 */ + while (true) { + // Stop expanding on block elements + if (!format[0].block_expand && isBlock(parent)) { + return parent; + } + + // Walk left/right + for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) { + if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling) && !isBogusBr(sibling)) { + return parent; + } + } + + // Check if we can move up are we at root level or body level + if (parent == root || parent.parentNode == root) { + container = parent; + break; + } + + parent = parent.parentNode; + } + + return container; + } + + // This function walks down the tree to find the leaf at the selection. + // The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node. + function findLeaf(node, offset) { + if (offset === undef) { + offset = node.nodeType === 3 ? node.length : node.childNodes.length; + } + + while (node && node.hasChildNodes()) { + node = node.childNodes[offset]; + if (node) { + offset = node.nodeType === 3 ? node.length : node.childNodes.length; + } + } + return { + node: node, + offset: offset + }; + } + + // If index based start position then resolve it + if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) { + lastIdx = startContainer.childNodes.length - 1; + startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset]; + + if (startContainer.nodeType == 3) { + startOffset = 0; + } + } + + // If index based end position then resolve it + if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) { + lastIdx = endContainer.childNodes.length - 1; + endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1]; + + if (endContainer.nodeType == 3) { + endOffset = endContainer.nodeValue.length; + } + } + + // Expands the node to the closes contentEditable false element if it exists + function findParentContentEditable(node) { + var parent = node; + + while (parent) { + if (parent.nodeType === 1 && getContentEditable(parent)) { + return getContentEditable(parent) === "false" ? parent : node; + } + + parent = parent.parentNode; + } + + return node; + } + + function findWordEndPoint(container, offset, start) { + var walker, node, pos, lastTextNode; + + function findSpace(node, offset) { + var pos, pos2, str = node.nodeValue; + + if (typeof offset == "undefined") { + offset = start ? str.length : 0; + } + + if (start) { + pos = str.lastIndexOf(' ', offset); + pos2 = str.lastIndexOf('\u00a0', offset); + pos = pos > pos2 ? pos : pos2; + + // Include the space on remove to avoid tag soup + if (pos !== -1 && !remove) { + pos++; + } + } else { + pos = str.indexOf(' ', offset); + pos2 = str.indexOf('\u00a0', offset); + pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2; + } + + return pos; + } + + if (container.nodeType === 3) { + pos = findSpace(container, offset); + + if (pos !== -1) { + return { + container: container, + offset: pos + }; + } + + lastTextNode = container; + } + + // Walk the nodes inside the block + walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody()); + while ((node = walker[start ? 'prev' : 'next']())) { + if (node.nodeType === 3) { + lastTextNode = node; + pos = findSpace(node); + + if (pos !== -1) { + return { + container: node, + offset: pos + }; + } + } else if (isBlock(node)) { + break; + } + } + + if (lastTextNode) { + if (start) { + offset = 0; + } else { + offset = lastTextNode.length; + } + + return { + container: lastTextNode, + offset: offset + }; + } + } + + function findSelectorEndPoint(container, siblingName) { + var parents, i, y, curFormat; + + if (container.nodeType == 3 && container.nodeValue.length === 0 && container[siblingName]) { + container = container[siblingName]; + } + + parents = getParents(container); + for (i = 0; i < parents.length; i++) { + for (y = 0; y < format.length; y++) { + curFormat = format[y]; + + // If collapsed state is set then skip formats that doesn't match that + if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed) { + continue; + } + + if (dom.is(parents[i], curFormat.selector)) { + return parents[i]; + } + } + } + + return container; + } + + function findBlockEndPoint(container, siblingName) { + var node, root = dom.getRoot(); + + // Expand to block of similar type + if (!format[0].wrapper) { + node = dom.getParent(container, format[0].block, root); + } + + // Expand to first wrappable block element or any block element + if (!node) { + node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, function(node) { + // Fixes #6183 where it would expand to editable parent element in inline mode + return node != root && isTextBlock(node); + }); + } + + // Exclude inner lists from wrapping + if (node && format[0].wrapper) { + node = getParents(node, 'ul,ol').reverse()[0] || node; + } + + // Didn't find a block element look for first/last wrappable element + if (!node) { + node = container; + + while (node[siblingName] && !isBlock(node[siblingName])) { + node = node[siblingName]; + + // Break on BR but include it will be removed later on + // we can't remove it now since we need to check if it can be wrapped + if (isEq(node, 'br')) { + break; + } + } + } + + return node || container; + } + + // Expand to closest contentEditable element + startContainer = findParentContentEditable(startContainer); + endContainer = findParentContentEditable(endContainer); + + // Exclude bookmark nodes if possible + if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) { + startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode; + startContainer = startContainer.nextSibling || startContainer; + + if (startContainer.nodeType == 3) { + startOffset = 0; + } + } + + if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) { + endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode; + endContainer = endContainer.previousSibling || endContainer; + + if (endContainer.nodeType == 3) { + endOffset = endContainer.length; + } + } + + if (format[0].inline) { + if (rng.collapsed) { + // Expand left to closest word boundary + endPoint = findWordEndPoint(startContainer, startOffset, true); + if (endPoint) { + startContainer = endPoint.container; + startOffset = endPoint.offset; + } + + // Expand right to closest word boundary + endPoint = findWordEndPoint(endContainer, endOffset); + if (endPoint) { + endContainer = endPoint.container; + endOffset = endPoint.offset; + } + } + + // Avoid applying formatting to a trailing space. + leaf = findLeaf(endContainer, endOffset); + if (leaf.node) { + while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling) { + leaf = findLeaf(leaf.node.previousSibling); + } + + if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 && + leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') { + + if (leaf.offset > 1) { + endContainer = leaf.node; + endContainer.splitText(leaf.offset - 1); + } + } + } + } + + // Move start/end point up the tree if the leaves are sharp and if we are in different containers + // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>! + // This will reduce the number of wrapper elements that needs to be created + // Move start point up the tree + if (format[0].inline || format[0].block_expand) { + if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) { + startContainer = findParentContainer(true); + } + + if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) { + endContainer = findParentContainer(); + } + } + + // Expand start/end container to matching selector + if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) { + // Find new startContainer/endContainer if there is better one + startContainer = findSelectorEndPoint(startContainer, 'previousSibling'); + endContainer = findSelectorEndPoint(endContainer, 'nextSibling'); + } + + // Expand start/end container to matching block element or text node + if (format[0].block || format[0].selector) { + // Find new startContainer/endContainer if there is better one + startContainer = findBlockEndPoint(startContainer, 'previousSibling'); + endContainer = findBlockEndPoint(endContainer, 'nextSibling'); + + // Non block element then try to expand up the leaf + if (format[0].block) { + if (!isBlock(startContainer)) { + startContainer = findParentContainer(true); + } + + if (!isBlock(endContainer)) { + endContainer = findParentContainer(); + } + } + } + + // Setup index for startContainer + if (startContainer.nodeType == 1) { + startOffset = nodeIndex(startContainer); + startContainer = startContainer.parentNode; + } + + // Setup index for endContainer + if (endContainer.nodeType == 1) { + endOffset = nodeIndex(endContainer) + 1; + endContainer = endContainer.parentNode; + } + + // Return new range like object + return { + startContainer: startContainer, + startOffset: startOffset, + endContainer: endContainer, + endOffset: endOffset + }; + } + + function isColorFormatAndAnchor(node, format) { + return format.links && node.tagName == 'A'; + } + + /** + * Removes the specified format for the specified node. It will also remove the node if it doesn't have + * any attributes if the format specifies it to do so. + * + * @private + * @param {Object} format Format object with items to remove from node. + * @param {Object} vars Name/value object with variables to apply to format. + * @param {Node} node Node to remove the format styles on. + * @param {Node} compareNode Optional compare node, if specified the styles will be compared to that node. + * @return {Boolean} True/false if the node was removed or not. + */ + function removeFormat(format, vars, node, compareNode) { + var i, attrs, stylesModified; + + // Check if node matches format + if (!matchName(node, format) && !isColorFormatAndAnchor(node, format)) { + return FALSE; + } + + // Should we compare with format attribs and styles + if (format.remove != 'all') { + // Remove styles + each(format.styles, function(value, name) { + value = normalizeStyleValue(replaceVars(value, vars), name); + + // Indexed array + if (typeof name === 'number') { + name = value; + compareNode = 0; + } + + if (format.remove_similar || (!compareNode || isEq(getStyle(compareNode, name), value))) { + dom.setStyle(node, name, ''); + } + + stylesModified = 1; + }); + + // Remove style attribute if it's empty + if (stylesModified && dom.getAttrib(node, 'style') === '') { + node.removeAttribute('style'); + node.removeAttribute('data-mce-style'); + } + + // Remove attributes + each(format.attributes, function(value, name) { + var valueOut; + + value = replaceVars(value, vars); + + // Indexed array + if (typeof name === 'number') { + name = value; + compareNode = 0; + } + + if (!compareNode || isEq(dom.getAttrib(compareNode, name), value)) { + // Keep internal classes + if (name == 'class') { + value = dom.getAttrib(node, name); + if (value) { + // Build new class value where everything is removed except the internal prefixed classes + valueOut = ''; + each(value.split(/\s+/), function(cls) { + if (/mce\-\w+/.test(cls)) { + valueOut += (valueOut ? ' ' : '') + cls; + } + }); + + // We got some internal classes left + if (valueOut) { + dom.setAttrib(node, name, valueOut); + return; + } + } + } + + // IE6 has a bug where the attribute doesn't get removed correctly + if (name == "class") { + node.removeAttribute('className'); + } + + // Remove mce prefixed attributes + if (MCE_ATTR_RE.test(name)) { + node.removeAttribute('data-mce-' + name); + } + + node.removeAttribute(name); + } + }); + + // Remove classes + each(format.classes, function(value) { + value = replaceVars(value, vars); + + if (!compareNode || dom.hasClass(compareNode, value)) { + dom.removeClass(node, value); + } + }); + + // Check for non internal attributes + attrs = dom.getAttribs(node); + for (i = 0; i < attrs.length; i++) { + var attrName = attrs[i].nodeName; + if (attrName.indexOf('_') !== 0 && attrName.indexOf('data-') !== 0) { + return FALSE; + } + } + } + + // Remove the inline child if it's empty for example <b> or <span> + if (format.remove != 'none') { + removeNode(node, format); + return TRUE; + } + } + + /** + * Removes the node and wrap it's children in paragraphs before doing so or + * appends BR elements to the beginning/end of the block element if forcedRootBlocks is disabled. + * + * If the div in the node below gets removed: + * text<div>text</div>text + * + * Output becomes: + * text<div><br />text<br /></div>text + * + * So when the div is removed the result is: + * text<br />text<br />text + * + * @private + * @param {Node} node Node to remove + apply BR/P elements to. + * @param {Object} format Format rule. + * @return {Node} Input node. + */ + function removeNode(node, format) { + var parentNode = node.parentNode, + rootBlockElm; + + function find(node, next, inc) { + node = getNonWhiteSpaceSibling(node, next, inc); + + return !node || (node.nodeName == 'BR' || isBlock(node)); + } + + if (format.block) { + if (!forcedRootBlock) { + // Append BR elements if needed before we remove the block + if (isBlock(node) && !isBlock(parentNode)) { + if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1)) { + node.insertBefore(dom.create('br'), node.firstChild); + } + + if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1)) { + node.appendChild(dom.create('br')); + } + } + } else { + // Wrap the block in a forcedRootBlock if we are at the root of document + if (parentNode == dom.getRoot()) { + if (!format.list_block || !isEq(node, format.list_block)) { + each(grep(node.childNodes), function(node) { + if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) { + if (!rootBlockElm) { + rootBlockElm = wrap(node, forcedRootBlock); + dom.setAttribs(rootBlockElm, ed.settings.forced_root_block_attrs); + } else { + rootBlockElm.appendChild(node); + } + } else { + rootBlockElm = 0; + } + }); + } + } + } + } + + // Never remove nodes that isn't the specified inline element if a selector is specified too + if (format.selector && format.inline && !isEq(format.inline, node)) { + return; + } + + dom.remove(node, 1); + } + + /** + * Returns the next/previous non whitespace node. + * + * @private + * @param {Node} node Node to start at. + * @param {boolean} next (Optional) Include next or previous node defaults to previous. + * @param {boolean} inc (Optional) Include the current node in checking. Defaults to false. + * @return {Node} Next or previous node or undefined if it wasn't found. + */ + function getNonWhiteSpaceSibling(node, next, inc) { + if (node) { + next = next ? 'nextSibling' : 'previousSibling'; + + for (node = inc ? node : node[next]; node; node = node[next]) { + if (node.nodeType == 1 || !isWhiteSpaceNode(node)) { + return node; + } + } + } + } + + /** + * Merges the next/previous sibling element if they match. + * + * @private + * @param {Node} prev Previous node to compare/merge. + * @param {Node} next Next node to compare/merge. + * @return {Node} Next node if we didn't merge and prev node if we did. + */ + function mergeSiblings(prev, next) { + var sibling, tmpSibling, elementUtils = new ElementUtils(dom); + + function findElementSibling(node, siblingName) { + for (sibling = node; sibling; sibling = sibling[siblingName]) { + if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0) { + return node; + } + + if (sibling.nodeType == 1 && !isBookmarkNode(sibling)) { + return sibling; + } + } + + return node; + } + + // Check if next/prev exists and that they are elements + if (prev && next) { + // If previous sibling is empty then jump over it + prev = findElementSibling(prev, 'previousSibling'); + next = findElementSibling(next, 'nextSibling'); + + // Compare next and previous nodes + if (elementUtils.compare(prev, next)) { + // Append nodes between + for (sibling = prev.nextSibling; sibling && sibling != next;) { + tmpSibling = sibling; + sibling = sibling.nextSibling; + prev.appendChild(tmpSibling); + } + + // Remove next node + dom.remove(next); + + // Move children into prev node + each(grep(next.childNodes), function(node) { + prev.appendChild(node); + }); + + return prev; + } + } + + return next; + } + + function getContainer(rng, start) { + var container, offset, lastIdx; + + container = rng[start ? 'startContainer' : 'endContainer']; + offset = rng[start ? 'startOffset' : 'endOffset']; + + if (container.nodeType == 1) { + lastIdx = container.childNodes.length - 1; + + if (!start && offset) { + offset--; + } + + container = container.childNodes[offset > lastIdx ? lastIdx : offset]; + } + + // If start text node is excluded then walk to the next node + if (container.nodeType === 3 && start && offset >= container.nodeValue.length) { + container = new TreeWalker(container, ed.getBody()).next() || container; + } + + // If end text node is excluded then walk to the previous node + if (container.nodeType === 3 && !start && offset === 0) { + container = new TreeWalker(container, ed.getBody()).prev() || container; + } + + return container; + } + + function performCaretAction(type, name, vars, similar) { + var caretContainerId = '_mce_caret', + debug = ed.settings.caret_debug; + + // Creates a caret container bogus element + function createCaretContainer(fill) { + var caretContainer = dom.create('span', { + id: caretContainerId, + 'data-mce-bogus': true, + style: debug ? 'color:red' : '' + }); + + if (fill) { + caretContainer.appendChild(ed.getDoc().createTextNode(INVISIBLE_CHAR)); + } + + return caretContainer; + } + + function isCaretContainerEmpty(node, nodes) { + while (node) { + if ((node.nodeType === 3 && node.nodeValue !== INVISIBLE_CHAR) || node.childNodes.length > 1) { + return false; + } + + // Collect nodes + if (nodes && node.nodeType === 1) { + nodes.push(node); + } + + node = node.firstChild; + } + + return true; + } + + // Returns any parent caret container element + function getParentCaretContainer(node) { + while (node) { + if (node.id === caretContainerId) { + return node; + } + + node = node.parentNode; + } + } + + // Finds the first text node in the specified node + function findFirstTextNode(node) { + var walker; + + if (node) { + walker = new TreeWalker(node, node); + + for (node = walker.current(); node; node = walker.next()) { + if (node.nodeType === 3) { + return node; + } + } + } + } + + // Removes the caret container for the specified node or all on the current document + function removeCaretContainer(node, moveCaret) { + var child, rng; + + if (!node) { + node = getParentCaretContainer(selection.getStart()); + + if (!node) { + while ((node = dom.get(caretContainerId))) { + removeCaretContainer(node, false); + } + } + } else { + rng = selection.getRng(true); + + if (isCaretContainerEmpty(node)) { + if (moveCaret !== false) { + rng.setStartBefore(node); + rng.setEndBefore(node); + } + + dom.remove(node); + } else { + child = findFirstTextNode(node); + + if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) { + child.deleteData(0, 1); + + // Fix for bug #6976 + if (rng.startContainer == child && rng.startOffset > 0) { + rng.setStart(child, rng.startOffset - 1); + } + + if (rng.endContainer == child && rng.endOffset > 0) { + rng.setEnd(child, rng.endOffset - 1); + } + } + + dom.remove(node, 1); + } + + selection.setRng(rng); + } + } + + // Applies formatting to the caret position + function applyCaretFormat() { + var rng, caretContainer, textNode, offset, bookmark, container, text; + + rng = selection.getRng(true); + offset = rng.startOffset; + container = rng.startContainer; + text = container.nodeValue; + + caretContainer = getParentCaretContainer(selection.getStart()); + if (caretContainer) { + textNode = findFirstTextNode(caretContainer); + } + + // Expand to word if caret is in the middle of a text node and the char before/after is a alpha numeric character + var wordcharRegex = /[^\s\u00a0\u00ad\u200b\ufeff]/; + if (text && offset > 0 && offset < text.length && + wordcharRegex.test(text.charAt(offset)) && wordcharRegex.test(text.charAt(offset - 1))) { + // Get bookmark of caret position + bookmark = selection.getBookmark(); + + // Collapse bookmark range (WebKit) + rng.collapse(true); + + // Expand the range to the closest word and split it at those points + rng = expandRng(rng, get(name)); + rng = rangeUtils.split(rng); + + // Apply the format to the range + apply(name, vars, rng); + + // Move selection back to caret position + selection.moveToBookmark(bookmark); + } else { + if (!caretContainer || textNode.nodeValue !== INVISIBLE_CHAR) { + caretContainer = createCaretContainer(true); + textNode = caretContainer.firstChild; + + rng.insertNode(caretContainer); + offset = 1; + + apply(name, vars, caretContainer); + } else { + apply(name, vars, caretContainer); + } + + // Move selection to text node + selection.setCursorLocation(textNode, offset); + } + } + + function removeCaretFormat() { + var rng = selection.getRng(true), + container, offset, bookmark, + hasContentAfter, node, formatNode, parents = [], + i, caretContainer; + + container = rng.startContainer; + offset = rng.startOffset; + node = container; + + if (container.nodeType == 3) { + if (offset != container.nodeValue.length) { + hasContentAfter = true; + } + + node = node.parentNode; + } + + while (node) { + if (matchNode(node, name, vars, similar)) { + formatNode = node; + break; + } + + if (node.nextSibling) { + hasContentAfter = true; + } + + parents.push(node); + node = node.parentNode; + } + + // Node doesn't have the specified format + if (!formatNode) { + return; + } + + // Is there contents after the caret then remove the format on the element + if (hasContentAfter) { + // Get bookmark of caret position + bookmark = selection.getBookmark(); + + // Collapse bookmark range (WebKit) + rng.collapse(true); + + // Expand the range to the closest word and split it at those points + rng = expandRng(rng, get(name), true); + rng = rangeUtils.split(rng); + + // Remove the format from the range + remove(name, vars, rng); + + // Move selection back to caret position + selection.moveToBookmark(bookmark); + } else { + caretContainer = createCaretContainer(); + + node = caretContainer; + for (i = parents.length - 1; i >= 0; i--) { + node.appendChild(dom.clone(parents[i], false)); + node = node.firstChild; + } + + // Insert invisible character into inner most format element + node.appendChild(dom.doc.createTextNode(INVISIBLE_CHAR)); + node = node.firstChild; + + var block = dom.getParent(formatNode, isTextBlock); + + if (block && dom.isEmpty(block)) { + // Replace formatNode with caretContainer when removing format from empty block like <p><b>|</b></p> + formatNode.parentNode.replaceChild(caretContainer, formatNode); + } else { + // Insert caret container after the formatted node + dom.insertAfter(caretContainer, formatNode); + } + + // Move selection to text node + selection.setCursorLocation(node, 1); + + // If the formatNode is empty, we can remove it safely. + if (dom.isEmpty(formatNode)) { + dom.remove(formatNode); + } + } + } + + // Checks if the parent caret container node isn't empty if that is the case it + // will remove the bogus state on all children that isn't empty + function unmarkBogusCaretParents() { + var caretContainer; + + caretContainer = getParentCaretContainer(selection.getStart()); + if (caretContainer && !dom.isEmpty(caretContainer)) { + walk(caretContainer, function(node) { + if (node.nodeType == 1 && node.id !== caretContainerId && !dom.isEmpty(node)) { + dom.setAttrib(node, 'data-mce-bogus', null); + } + }, 'childNodes'); + } + } + + // Only bind the caret events once + if (!ed._hasCaretEvents) { + // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements + markCaretContainersBogus = function() { + var nodes = [], + i; + + if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) { + // Mark children + i = nodes.length; + while (i--) { + dom.setAttrib(nodes[i], 'data-mce-bogus', '1'); + } + } + }; + + disableCaretContainer = function(e) { + var keyCode = e.keyCode; + + removeCaretContainer(); + + // Remove caret container if it's empty + if (keyCode == 8 && selection.isCollapsed() && selection.getStart().innerHTML == INVISIBLE_CHAR) { + removeCaretContainer(getParentCaretContainer(selection.getStart())); + } + + // Remove caret container on keydown and it's left/right arrow keys + if (keyCode == 37 || keyCode == 39) { + removeCaretContainer(getParentCaretContainer(selection.getStart())); + } + + unmarkBogusCaretParents(); + }; + + // Remove bogus state if they got filled by contents using editor.selection.setContent + ed.on('SetContent', function(e) { + if (e.selection) { + unmarkBogusCaretParents(); + } + }); + ed._hasCaretEvents = true; + } + + // Do apply or remove caret format + if (type == "apply") { + applyCaretFormat(); + } else { + removeCaretFormat(); + } + } + + /** + * Moves the start to the first suitable text node. + */ + function moveStart(rng) { + var container = rng.startContainer, + offset = rng.startOffset, + walker, node, nodes; + + if (rng.startContainer == rng.endContainer) { + if (isInlineBlock(rng.startContainer.childNodes[rng.startOffset])) { + return; + } + } + + // Convert text node into index if possible + if (container.nodeType == 3 && offset >= container.nodeValue.length) { + // Get the parent container location and walk from there + offset = nodeIndex(container); + container = container.parentNode; + } + + // Move startContainer/startOffset in to a suitable node + if (container.nodeType == 1) { + nodes = container.childNodes; + if (offset < nodes.length) { + container = nodes[offset]; + walker = new TreeWalker(container, dom.getParent(container, dom.isBlock)); + } else { + container = nodes[nodes.length - 1]; + walker = new TreeWalker(container, dom.getParent(container, dom.isBlock)); + walker.next(true); + } + + for (node = walker.current(); node; node = walker.next()) { + if (node.nodeType == 3 && !isWhiteSpaceNode(node)) { + rng.setStart(node, 0); + selection.setRng(rng); + + return; + } + } + } + } + }; + } + ); + + /** + * Diff.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * JS Implementation of the O(ND) Difference Algorithm by Eugene W. Myers. + * + * @class tinymce.undo.Diff + * @private + */ + define( + 'tinymce.core.undo.Diff', [], + function() { + var KEEP = 0, + INSERT = 1, + DELETE = 2; + + var diff = function(left, right) { + var size = left.length + right.length + 2; + var vDown = new Array(size); + var vUp = new Array(size); + + var snake = function(start, end, diag) { + return { + start: start, + end: end, + diag: diag + }; + }; + + var buildScript = function(start1, end1, start2, end2, script) { + var middle = getMiddleSnake(start1, end1, start2, end2); + + if (middle === null || middle.start === end1 && middle.diag === end1 - end2 || + middle.end === start1 && middle.diag === start1 - start2) { + var i = start1; + var j = start2; + while (i < end1 || j < end2) { + if (i < end1 && j < end2 && left[i] === right[j]) { + script.push([KEEP, left[i]]); + ++i; + ++j; + } else { + if (end1 - start1 > end2 - start2) { + script.push([DELETE, left[i]]); + ++i; + } else { + script.push([INSERT, right[j]]); + ++j; + } + } + } + } else { + buildScript(start1, middle.start, start2, middle.start - middle.diag, script); + for (var i2 = middle.start; i2 < middle.end; ++i2) { + script.push([KEEP, left[i2]]); + } + buildScript(middle.end, end1, middle.end - middle.diag, end2, script); + } + }; + + var buildSnake = function(start, diag, end1, end2) { + var end = start; + while (end - diag < end2 && end < end1 && left[end] === right[end - diag]) { + ++end; + } + return snake(start, end, diag); + }; + + var getMiddleSnake = function(start1, end1, start2, end2) { + // Myers Algorithm + // Initialisations + var m = end1 - start1; + var n = end2 - start2; + if (m === 0 || n === 0) { + return null; + } + + var delta = m - n; + var sum = n + m; + var offset = (sum % 2 === 0 ? sum : sum + 1) / 2; + vDown[1 + offset] = start1; + vUp[1 + offset] = end1 + 1; + + for (var d = 0; d <= offset; ++d) { + // Down + for (var k = -d; k <= d; k += 2) { + // First step + + var i = k + offset; + if (k === -d || k != d && vDown[i - 1] < vDown[i + 1]) { + vDown[i] = vDown[i + 1]; + } else { + vDown[i] = vDown[i - 1] + 1; + } + + var x = vDown[i]; + var y = x - start1 + start2 - k; + + while (x < end1 && y < end2 && left[x] === right[y]) { + vDown[i] = ++x; + ++y; + } + // Second step + if (delta % 2 != 0 && delta - d <= k && k <= delta + d) { + if (vUp[i - delta] <= vDown[i]) { + return buildSnake(vUp[i - delta], k + start1 - start2, end1, end2); + } + } + } + + // Up + for (k = delta - d; k <= delta + d; k += 2) { + // First step + i = k + offset - delta; + if (k === delta - d || k != delta + d && vUp[i + 1] <= vUp[i - 1]) { + vUp[i] = vUp[i + 1] - 1; + } else { + vUp[i] = vUp[i - 1]; + } + + x = vUp[i] - 1; + y = x - start1 + start2 - k; + while (x >= start1 && y >= start2 && left[x] === right[y]) { + vUp[i] = x--; + y--; + } + // Second step + if (delta % 2 === 0 && -d <= k && k <= d) { + if (vUp[i] <= vDown[i + delta]) { + return buildSnake(vUp[i], k + start1 - start2, end1, end2); + } + } + } + } + }; + + var script = []; + buildScript(0, left.length, 0, right.length, script); + return script; + }; + + return { + KEEP: KEEP, + DELETE: DELETE, + INSERT: INSERT, + diff: diff + }; + } + ); + /** + * Fragments.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module reads and applies html fragments from/to dom nodes. + * + * @class tinymce.undo.Fragments + * @private + */ + define( + 'tinymce.core.undo.Fragments', [ + "tinymce.core.util.Arr", + "tinymce.core.html.Entities", + "tinymce.core.undo.Diff" + ], + function(Arr, Entities, Diff) { + var getOuterHtml = function(elm) { + if (elm.nodeType === 1) { + return elm.outerHTML; + } else if (elm.nodeType === 3) { + return Entities.encodeRaw(elm.data, false); + } else if (elm.nodeType === 8) { + return '<!--' + elm.data + '-->'; + } + + return ''; + }; + + var createFragment = function(html) { + var frag, node, container; + + container = document.createElement("div"); + frag = document.createDocumentFragment(); + + if (html) { + container.innerHTML = html; + } + + while ((node = container.firstChild)) { + frag.appendChild(node); + } + + return frag; + }; + + var insertAt = function(elm, html, index) { + var fragment = createFragment(html); + if (elm.hasChildNodes() && index < elm.childNodes.length) { + var target = elm.childNodes[index]; + target.parentNode.insertBefore(fragment, target); + } else { + elm.appendChild(fragment); + } + }; + + var removeAt = function(elm, index) { + if (elm.hasChildNodes() && index < elm.childNodes.length) { + var target = elm.childNodes[index]; + target.parentNode.removeChild(target); + } + }; + + var applyDiff = function(diff, elm) { + var index = 0; + Arr.each(diff, function(action) { + if (action[0] === Diff.KEEP) { + index++; + } else if (action[0] === Diff.INSERT) { + insertAt(elm, action[1], index); + index++; + } else if (action[0] === Diff.DELETE) { + removeAt(elm, index); + } + }); + }; + + var read = function(elm) { + return Arr.filter(Arr.map(elm.childNodes, getOuterHtml), function(item) { + return item.length > 0; + }); + }; + + var write = function(fragments, elm) { + var currentFragments = Arr.map(elm.childNodes, getOuterHtml); + applyDiff(Diff.diff(currentFragments, fragments), elm); + return elm; + }; + + return { + read: read, + write: write + }; + } + ); + /** + * Levels.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module handles getting/setting undo levels to/from editor instances. + * + * @class tinymce.undo.Levels + * @private + */ + define( + 'tinymce.core.undo.Levels', [ + "tinymce.core.util.Arr", + "tinymce.core.undo.Fragments" + ], + function(Arr, Fragments) { + var hasIframes = function(html) { + return html.indexOf('</iframe>') !== -1; + }; + + var createFragmentedLevel = function(fragments) { + return { + type: 'fragmented', + fragments: fragments, + content: '', + bookmark: null, + beforeBookmark: null + }; + }; + + var createCompleteLevel = function(content) { + return { + type: 'complete', + fragments: null, + content: content, + bookmark: null, + beforeBookmark: null + }; + }; + + var createFromEditor = function(editor) { + var fragments, content, trimmedFragments; + + fragments = Fragments.read(editor.getBody()); + trimmedFragments = Arr.map(fragments, function(html) { + return editor.serializer.trimContent(html); + }); + content = trimmedFragments.join(''); + + return hasIframes(content) ? createFragmentedLevel(trimmedFragments) : createCompleteLevel(content); + }; + + var applyToEditor = function(editor, level, before) { + if (level.type === 'fragmented') { + Fragments.write(level.fragments, editor.getBody()); + } else { + editor.setContent(level.content, { + format: 'raw' + }); + } + + editor.selection.moveToBookmark(before ? level.beforeBookmark : level.bookmark); + }; + + var getLevelContent = function(level) { + return level.type === 'fragmented' ? level.fragments.join('') : level.content; + }; + + var isEq = function(level1, level2) { + return getLevelContent(level1) === getLevelContent(level2); + }; + + return { + createFragmentedLevel: createFragmentedLevel, + createCompleteLevel: createCompleteLevel, + createFromEditor: createFromEditor, + applyToEditor: applyToEditor, + isEq: isEq + }; + } + ); + /** + * UndoManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class handles the undo/redo history levels for the editor. Since the built-in undo/redo has major drawbacks a custom one was needed. + * + * @class tinymce.UndoManager + */ + define( + 'tinymce.core.UndoManager', [ + "tinymce.core.util.VK", + "tinymce.core.util.Tools", + "tinymce.core.undo.Levels" + ], + function(VK, Tools, Levels) { + return function(editor) { + var self = this, + index = 0, + data = [], + beforeBookmark, isFirstTypedCharacter, locks = 0; + + var isUnlocked = function() { + return locks === 0; + }; + + var setTyping = function(typing) { + if (isUnlocked()) { + self.typing = typing; + } + }; + + function setDirty(state) { + editor.setDirty(state); + } + + function addNonTypingUndoLevel(e) { + setTyping(false); + self.add({}, e); + } + + function endTyping() { + if (self.typing) { + setTyping(false); + self.add(); + } + } + + // Add initial undo level when the editor is initialized + editor.on('init', function() { + self.add(); + }); + + // Get position before an execCommand is processed + editor.on('BeforeExecCommand', function(e) { + var cmd = e.command; + + if (cmd !== 'Undo' && cmd !== 'Redo' && cmd !== 'mceRepaint') { + endTyping(); + self.beforeChange(); + } + }); + + // Add undo level after an execCommand call was made + editor.on('ExecCommand', function(e) { + var cmd = e.command; + + if (cmd !== 'Undo' && cmd !== 'Redo' && cmd !== 'mceRepaint') { + addNonTypingUndoLevel(e); + } + }); + + editor.on('ObjectResizeStart Cut', function() { + self.beforeChange(); + }); + + editor.on('SaveContent ObjectResized blur', addNonTypingUndoLevel); + editor.on('DragEnd', addNonTypingUndoLevel); + + editor.on('KeyUp', function(e) { + var keyCode = e.keyCode; + + // If key is prevented then don't add undo level + // This would happen on keyboard shortcuts for example + if (e.isDefaultPrevented()) { + return; + } + + if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode === 45 || e.ctrlKey) { + addNonTypingUndoLevel(); + editor.nodeChanged(); + } + + if (keyCode === 46 || keyCode === 8) { + editor.nodeChanged(); + } + + // Fire a TypingUndo/Change event on the first character entered + if (isFirstTypedCharacter && self.typing && Levels.isEq(Levels.createFromEditor(editor), data[0]) === false) { + if (editor.isDirty() === false) { + setDirty(true); + editor.fire('change', { + level: data[0], + lastLevel: null + }); + } + + editor.fire('TypingUndo'); + isFirstTypedCharacter = false; + editor.nodeChanged(); + } + }); + + editor.on('KeyDown', function(e) { + var keyCode = e.keyCode; + + // If key is prevented then don't add undo level + // This would happen on keyboard shortcuts for example + if (e.isDefaultPrevented()) { + return; + } + + // Is character position keys left,right,up,down,home,end,pgdown,pgup,enter + if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode === 45) { + if (self.typing) { + addNonTypingUndoLevel(e); + } + + return; + } + + // If key isn't Ctrl+Alt/AltGr + var modKey = (e.ctrlKey && !e.altKey) || e.metaKey; + if ((keyCode < 16 || keyCode > 20) && keyCode !== 224 && keyCode !== 91 && !self.typing && !modKey) { + self.beforeChange(); + setTyping(true); + self.add({}, e); + isFirstTypedCharacter = true; + } + }); + + editor.on('MouseDown', function(e) { + if (self.typing) { + addNonTypingUndoLevel(e); + } + }); + + // Add keyboard shortcuts for undo/redo keys + editor.addShortcut('meta+z', '', 'Undo'); + editor.addShortcut('meta+y,meta+shift+z', '', 'Redo'); + + editor.on('AddUndo Undo Redo ClearUndos', function(e) { + if (!e.isDefaultPrevented()) { + editor.nodeChanged(); + } + }); + + /*eslint consistent-this:0 */ + self = { + // Explode for debugging reasons + data: data, + + /** + * State if the user is currently typing or not. This will add a typing operation into one undo + * level instead of one new level for each keystroke. + * + * @field {Boolean} typing + */ + typing: false, + + /** + * Stores away a bookmark to be used when performing an undo action so that the selection is before + * the change has been made. + * + * @method beforeChange + */ + beforeChange: function() { + if (isUnlocked()) { + beforeBookmark = editor.selection.getBookmark(2, true); + } + }, + + /** + * Adds a new undo level/snapshot to the undo list. + * + * @method add + * @param {Object} level Optional undo level object to add. + * @param {DOMEvent} event Optional event responsible for the creation of the undo level. + * @return {Object} Undo level that got added or null it a level wasn't needed. + */ + add: function(level, event) { + var i, settings = editor.settings, + lastLevel, currentLevel; + + currentLevel = Levels.createFromEditor(editor); + level = level || {}; + level = Tools.extend(level, currentLevel); + + if (isUnlocked() === false || editor.removed) { + return null; + } + + lastLevel = data[index]; + if (editor.fire('BeforeAddUndo', { + level: level, + lastLevel: lastLevel, + originalEvent: event + }).isDefaultPrevented()) { + return null; + } + + // Add undo level if needed + if (lastLevel && Levels.isEq(lastLevel, level)) { + return null; + } + + // Set before bookmark on previous level + if (data[index]) { + data[index].beforeBookmark = beforeBookmark; + } + + // Time to compress + if (settings.custom_undo_redo_levels) { + if (data.length > settings.custom_undo_redo_levels) { + for (i = 0; i < data.length - 1; i++) { + data[i] = data[i + 1]; + } + + data.length--; + index = data.length; + } + } + + // Get a non intrusive normalized bookmark + level.bookmark = editor.selection.getBookmark(2, true); + + // Crop array if needed + if (index < data.length - 1) { + data.length = index + 1; + } + + data.push(level); + index = data.length - 1; + + var args = { + level: level, + lastLevel: lastLevel, + originalEvent: event + }; + + editor.fire('AddUndo', args); + + if (index > 0) { + setDirty(true); + editor.fire('change', args); + } + + return level; + }, + + /** + * Undoes the last action. + * + * @method undo + * @return {Object} Undo level or null if no undo was performed. + */ + undo: function() { + var level; + + if (self.typing) { + self.add(); + self.typing = false; + setTyping(false); + } + + if (index > 0) { + level = data[--index]; + Levels.applyToEditor(editor, level, true); + setDirty(true); + editor.fire('undo', { + level: level + }); + } + + return level; + }, + + /** + * Redoes the last action. + * + * @method redo + * @return {Object} Redo level or null if no redo was performed. + */ + redo: function() { + var level; + + if (index < data.length - 1) { + level = data[++index]; + Levels.applyToEditor(editor, level, false); + setDirty(true); + editor.fire('redo', { + level: level + }); + } + + return level; + }, + + /** + * Removes all undo levels. + * + * @method clear + */ + clear: function() { + data = []; + index = 0; + self.typing = false; + self.data = data; + editor.fire('ClearUndos'); + }, + + /** + * Returns true/false if the undo manager has any undo levels. + * + * @method hasUndo + * @return {Boolean} true/false if the undo manager has any undo levels. + */ + hasUndo: function() { + // Has undo levels or typing and content isn't the same as the initial level + return index > 0 || (self.typing && data[0] && !Levels.isEq(Levels.createFromEditor(editor), data[0])); + }, + + /** + * Returns true/false if the undo manager has any redo levels. + * + * @method hasRedo + * @return {Boolean} true/false if the undo manager has any redo levels. + */ + hasRedo: function() { + return index < data.length - 1 && !self.typing; + }, + + /** + * Executes the specified mutator function as an undo transaction. The selection + * before the modification will be stored to the undo stack and if the DOM changes + * it will add a new undo level. Any logic within the translation that adds undo levels will + * be ignored. So a translation can include calls to execCommand or editor.insertContent. + * + * @method transact + * @param {function} callback Function that gets executed and has dom manipulation logic in it. + * @return {Object} Undo level that got added or null it a level wasn't needed. + */ + transact: function(callback) { + endTyping(); + self.beforeChange(); + self.ignore(callback); + return self.add(); + }, + + /** + * Executes the specified mutator function as an undo transaction. But without adding an undo level. + * Any logic within the translation that adds undo levels will be ignored. So a translation can + * include calls to execCommand or editor.insertContent. + * + * @method ignore + * @param {function} callback Function that gets executed and has dom manipulation logic in it. + * @return {Object} Undo level that got added or null it a level wasn't needed. + */ + ignore: function(callback) { + try { + locks++; + callback(); + } finally { + locks--; + } + }, + + /** + * Adds an extra "hidden" undo level by first applying the first mutation and store that to the undo stack + * then roll back that change and do the second mutation on top of the stack. This will produce an extra + * undo level that the user doesn't see until they undo. + * + * @method extra + * @param {function} callback1 Function that does mutation but gets stored as a "hidden" extra undo level. + * @param {function} callback2 Function that does mutation but gets displayed to the user. + */ + extra: function(callback1, callback2) { + var lastLevel, bookmark; + + if (self.transact(callback1)) { + bookmark = data[index].bookmark; + lastLevel = data[index - 1]; + Levels.applyToEditor(editor, lastLevel, true); + + if (self.transact(callback2)) { + data[index - 1].beforeBookmark = bookmark; + } + } + } + }; + + return self; + }; + } + ); + + define( + 'ephox.sugar.api.node.Body', + + [ + 'ephox.katamari.api.Thunk', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.api.node.Node', + 'global!document' + ], + + function(Thunk, Element, Node, document) { + + // Node.contains() is very, very, very good performance + // http://jsperf.com/closest-vs-contains/5 + var inBody = function(element) { + // Technically this is only required on IE, where contains() returns false for text nodes. + // But it's cheap enough to run everywhere and Sugar doesn't have platform detection (yet). + var dom = Node.isText(element) ? element.dom().parentNode : element.dom(); + + // use ownerDocument.body to ensure this works inside iframes. + // Normally contains is bad because an element "contains" itself, but here we want that. + return dom !== undefined && dom !== null && dom.ownerDocument.body.contains(dom); + }; + + var body = Thunk.cached(function() { + return getBody(Element.fromDom(document)); + }); + + var getBody = function(doc) { + var body = doc.dom().body; + if (body === null || body === undefined) throw 'Body is not available yet'; + return Element.fromDom(body); + }; + + return { + body: body, + getBody: getBody, + inBody: inBody + }; + } + ); + + define( + 'ephox.sugar.impl.ClosestOrAncestor', + + [ + 'ephox.katamari.api.Type', + 'ephox.katamari.api.Option' + ], + + function(Type, Option) { + return function(is, ancestor, scope, a, isRoot) { + return is(scope, a) ? + Option.some(scope) : + Type.isFunction(isRoot) && isRoot(scope) ? + Option.none() : + ancestor(scope, a, isRoot); + }; + } + ); + define( + 'ephox.sugar.api.search.PredicateFind', + + [ + 'ephox.katamari.api.Type', + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Option', + 'ephox.sugar.api.node.Body', + 'ephox.sugar.api.dom.Compare', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.impl.ClosestOrAncestor' + ], + + function(Type, Arr, Fun, Option, Body, Compare, Element, ClosestOrAncestor) { + var first = function(predicate) { + return descendant(Body.body(), predicate); + }; + + var ancestor = function(scope, predicate, isRoot) { + var element = scope.dom(); + var stop = Type.isFunction(isRoot) ? isRoot : Fun.constant(false); + + while (element.parentNode) { + element = element.parentNode; + var el = Element.fromDom(element); + + if (predicate(el)) return Option.some(el); + else if (stop(el)) break; + } + return Option.none(); + }; + + var closest = function(scope, predicate, isRoot) { + // This is required to avoid ClosestOrAncestor passing the predicate to itself + var is = function(scope) { + return predicate(scope); + }; + return ClosestOrAncestor(is, ancestor, scope, predicate, isRoot); + }; + + var sibling = function(scope, predicate) { + var element = scope.dom(); + if (!element.parentNode) return Option.none(); + + return child(Element.fromDom(element.parentNode), function(x) { + return !Compare.eq(scope, x) && predicate(x); + }); + }; + + var child = function(scope, predicate) { + var result = Arr.find(scope.dom().childNodes, + Fun.compose(predicate, Element.fromDom)); + return result.map(Element.fromDom); + }; + + var descendant = function(scope, predicate) { + var descend = function(element) { + for (var i = 0; i < element.childNodes.length; i++) { + if (predicate(Element.fromDom(element.childNodes[i]))) + return Option.some(Element.fromDom(element.childNodes[i])); + + var res = descend(element.childNodes[i]); + if (res.isSome()) + return res; + } + + return Option.none(); + }; + + return descend(scope.dom()); + }; + + return { + first: first, + ancestor: ancestor, + closest: closest, + sibling: sibling, + child: child, + descendant: descendant + }; + } + ); + + /** + * CaretUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Utility functions shared by the caret logic. + * + * @private + * @class tinymce.caret.CaretUtils + */ + define( + 'tinymce.core.caret.CaretUtils', [ + "tinymce.core.util.Fun", + "tinymce.core.dom.TreeWalker", + "tinymce.core.dom.NodeType", + "tinymce.core.caret.CaretPosition", + "tinymce.core.caret.CaretContainer", + "tinymce.core.caret.CaretCandidate" + ], + function(Fun, TreeWalker, NodeType, CaretPosition, CaretContainer, CaretCandidate) { + var isContentEditableTrue = NodeType.isContentEditableTrue, + isContentEditableFalse = NodeType.isContentEditableFalse, + isBlockLike = NodeType.matchStyleValues('display', 'block table table-cell table-caption list-item'), + isCaretContainer = CaretContainer.isCaretContainer, + isCaretContainerBlock = CaretContainer.isCaretContainerBlock, + curry = Fun.curry, + isElement = NodeType.isElement, + isCaretCandidate = CaretCandidate.isCaretCandidate; + + function isForwards(direction) { + return direction > 0; + } + + function isBackwards(direction) { + return direction < 0; + } + + function skipCaretContainers(walk, shallow) { + var node; + + while ((node = walk(shallow))) { + if (!isCaretContainerBlock(node)) { + return node; + } + } + + return null; + } + + function findNode(node, direction, predicateFn, rootNode, shallow) { + var walker = new TreeWalker(node, rootNode); + + if (isBackwards(direction)) { + if (isContentEditableFalse(node) || isCaretContainerBlock(node)) { + node = skipCaretContainers(walker.prev, true); + if (predicateFn(node)) { + return node; + } + } + + while ((node = skipCaretContainers(walker.prev, shallow))) { + if (predicateFn(node)) { + return node; + } + } + } + + if (isForwards(direction)) { + if (isContentEditableFalse(node) || isCaretContainerBlock(node)) { + node = skipCaretContainers(walker.next, true); + if (predicateFn(node)) { + return node; + } + } + + while ((node = skipCaretContainers(walker.next, shallow))) { + if (predicateFn(node)) { + return node; + } + } + } + + return null; + } + + function getEditingHost(node, rootNode) { + for (node = node.parentNode; node && node != rootNode; node = node.parentNode) { + if (isContentEditableTrue(node)) { + return node; + } + } + + return rootNode; + } + + function getParentBlock(node, rootNode) { + while (node && node != rootNode) { + if (isBlockLike(node)) { + return node; + } + + node = node.parentNode; + } + + return null; + } + + function isInSameBlock(caretPosition1, caretPosition2, rootNode) { + return getParentBlock(caretPosition1.container(), rootNode) == getParentBlock(caretPosition2.container(), rootNode); + } + + function isInSameEditingHost(caretPosition1, caretPosition2, rootNode) { + return getEditingHost(caretPosition1.container(), rootNode) == getEditingHost(caretPosition2.container(), rootNode); + } + + function getChildNodeAtRelativeOffset(relativeOffset, caretPosition) { + var container, offset; + + if (!caretPosition) { + return null; + } + + container = caretPosition.container(); + offset = caretPosition.offset(); + + if (!isElement(container)) { + return null; + } + + return container.childNodes[offset + relativeOffset]; + } + + function beforeAfter(before, node) { + var range = node.ownerDocument.createRange(); + + if (before) { + range.setStartBefore(node); + range.setEndBefore(node); + } else { + range.setStartAfter(node); + range.setEndAfter(node); + } + + return range; + } + + function isNodesInSameBlock(rootNode, node1, node2) { + return getParentBlock(node1, rootNode) == getParentBlock(node2, rootNode); + } + + function lean(left, rootNode, node) { + var sibling, siblingName; + + if (left) { + siblingName = 'previousSibling'; + } else { + siblingName = 'nextSibling'; + } + + while (node && node != rootNode) { + sibling = node[siblingName]; + + if (isCaretContainer(sibling)) { + sibling = sibling[siblingName]; + } + + if (isContentEditableFalse(sibling)) { + if (isNodesInSameBlock(rootNode, sibling, node)) { + return sibling; + } + + break; + } + + if (isCaretCandidate(sibling)) { + break; + } + + node = node.parentNode; + } + + return null; + } + + var before = curry(beforeAfter, true); + var after = curry(beforeAfter, false); + + function normalizeRange(direction, rootNode, range) { + var node, container, offset, location; + var leanLeft = curry(lean, true, rootNode); + var leanRight = curry(lean, false, rootNode); + + container = range.startContainer; + offset = range.startOffset; + + if (CaretContainer.isCaretContainerBlock(container)) { + if (!isElement(container)) { + container = container.parentNode; + } + + location = container.getAttribute('data-mce-caret'); + + if (location == 'before') { + node = container.nextSibling; + if (isContentEditableFalse(node)) { + return before(node); + } + } + + if (location == 'after') { + node = container.previousSibling; + if (isContentEditableFalse(node)) { + return after(node); + } + } + } + + if (!range.collapsed) { + return range; + } + + if (NodeType.isText(container)) { + if (isCaretContainer(container)) { + if (direction === 1) { + node = leanRight(container); + if (node) { + return before(node); + } + + node = leanLeft(container); + if (node) { + return after(node); + } + } + + if (direction === -1) { + node = leanLeft(container); + if (node) { + return after(node); + } + + node = leanRight(container); + if (node) { + return before(node); + } + } + + return range; + } + + if (CaretContainer.endsWithCaretContainer(container) && offset >= container.data.length - 1) { + if (direction === 1) { + node = leanRight(container); + if (node) { + return before(node); + } + } + + return range; + } + + if (CaretContainer.startsWithCaretContainer(container) && offset <= 1) { + if (direction === -1) { + node = leanLeft(container); + if (node) { + return after(node); + } + } + + return range; + } + + if (offset === container.data.length) { + node = leanRight(container); + if (node) { + return before(node); + } + + return range; + } + + if (offset === 0) { + node = leanLeft(container); + if (node) { + return after(node); + } + + return range; + } + } + + return range; + } + + function isNextToContentEditableFalse(relativeOffset, caretPosition) { + return isContentEditableFalse(getChildNodeAtRelativeOffset(relativeOffset, caretPosition)); + } + + return { + isForwards: isForwards, + isBackwards: isBackwards, + findNode: findNode, + getEditingHost: getEditingHost, + getParentBlock: getParentBlock, + isInSameBlock: isInSameBlock, + isInSameEditingHost: isInSameEditingHost, + isBeforeContentEditableFalse: curry(isNextToContentEditableFalse, 0), + isAfterContentEditableFalse: curry(isNextToContentEditableFalse, -1), + normalizeRange: normalizeRange + }; + } + ); + + /** + * CaretWalker.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module contains logic for moving around a virtual caret in logical order within a DOM element. + * + * It ignores the most obvious invalid caret locations such as within a script element or within a + * contentEditable=false element but it will return locations that isn't possible to render visually. + * + * @private + * @class tinymce.caret.CaretWalker + * @example + * var caretWalker = new CaretWalker(rootElm); + * + * var prevLogicalCaretPosition = caretWalker.prev(CaretPosition.fromRangeStart(range)); + * var nextLogicalCaretPosition = caretWalker.next(CaretPosition.fromRangeEnd(range)); + */ + define( + 'tinymce.core.caret.CaretWalker', [ + "tinymce.core.dom.NodeType", + "tinymce.core.caret.CaretCandidate", + "tinymce.core.caret.CaretPosition", + "tinymce.core.caret.CaretUtils", + "tinymce.core.util.Arr", + "tinymce.core.util.Fun" + ], + function(NodeType, CaretCandidate, CaretPosition, CaretUtils, Arr, Fun) { + var isContentEditableFalse = NodeType.isContentEditableFalse, + isText = NodeType.isText, + isElement = NodeType.isElement, + isBr = NodeType.isBr, + isForwards = CaretUtils.isForwards, + isBackwards = CaretUtils.isBackwards, + isCaretCandidate = CaretCandidate.isCaretCandidate, + isAtomic = CaretCandidate.isAtomic, + isEditableCaretCandidate = CaretCandidate.isEditableCaretCandidate; + + function getParents(node, rootNode) { + var parents = []; + + while (node && node != rootNode) { + parents.push(node); + node = node.parentNode; + } + + return parents; + } + + function nodeAtIndex(container, offset) { + if (container.hasChildNodes() && offset < container.childNodes.length) { + return container.childNodes[offset]; + } + + return null; + } + + function getCaretCandidatePosition(direction, node) { + if (isForwards(direction)) { + if (isCaretCandidate(node.previousSibling) && !isText(node.previousSibling)) { + return CaretPosition.before(node); + } + + if (isText(node)) { + return CaretPosition(node, 0); + } + } + + if (isBackwards(direction)) { + if (isCaretCandidate(node.nextSibling) && !isText(node.nextSibling)) { + return CaretPosition.after(node); + } + + if (isText(node)) { + return CaretPosition(node, node.data.length); + } + } + + if (isBackwards(direction)) { + if (isBr(node)) { + return CaretPosition.before(node); + } + + return CaretPosition.after(node); + } + + return CaretPosition.before(node); + } + + // Jumps over BR elements <p>|<br></p><p>a</p> -> <p><br></p><p>|a</p> + function isBrBeforeBlock(node, rootNode) { + var next; + + if (!NodeType.isBr(node)) { + return false; + } + + next = findCaretPosition(1, CaretPosition.after(node), rootNode); + if (!next) { + return false; + } + + return !CaretUtils.isInSameBlock(CaretPosition.before(node), CaretPosition.before(next), rootNode); + } + + function findCaretPosition(direction, startCaretPosition, rootNode) { + var container, offset, node, nextNode, innerNode, + rootContentEditableFalseElm, caretPosition; + + if (!isElement(rootNode) || !startCaretPosition) { + return null; + } + + if (startCaretPosition.isEqual(CaretPosition.after(rootNode)) && rootNode.lastChild) { + caretPosition = CaretPosition.after(rootNode.lastChild); + if (isBackwards(direction) && isCaretCandidate(rootNode.lastChild) && isElement(rootNode.lastChild)) { + return isBr(rootNode.lastChild) ? CaretPosition.before(rootNode.lastChild) : caretPosition; + } + } else { + caretPosition = startCaretPosition; + } + + container = caretPosition.container(); + offset = caretPosition.offset(); + + if (isText(container)) { + if (isBackwards(direction) && offset > 0) { + return CaretPosition(container, --offset); + } + + if (isForwards(direction) && offset < container.length) { + return CaretPosition(container, ++offset); + } + + node = container; + } else { + if (isBackwards(direction) && offset > 0) { + nextNode = nodeAtIndex(container, offset - 1); + if (isCaretCandidate(nextNode)) { + if (!isAtomic(nextNode)) { + innerNode = CaretUtils.findNode(nextNode, direction, isEditableCaretCandidate, nextNode); + if (innerNode) { + if (isText(innerNode)) { + return CaretPosition(innerNode, innerNode.data.length); + } + + return CaretPosition.after(innerNode); + } + } + + if (isText(nextNode)) { + return CaretPosition(nextNode, nextNode.data.length); + } + + return CaretPosition.before(nextNode); + } + } + + if (isForwards(direction) && offset < container.childNodes.length) { + nextNode = nodeAtIndex(container, offset); + if (isCaretCandidate(nextNode)) { + if (isBrBeforeBlock(nextNode, rootNode)) { + return findCaretPosition(direction, CaretPosition.after(nextNode), rootNode); + } + + if (!isAtomic(nextNode)) { + innerNode = CaretUtils.findNode(nextNode, direction, isEditableCaretCandidate, nextNode); + if (innerNode) { + if (isText(innerNode)) { + return CaretPosition(innerNode, 0); + } + + return CaretPosition.before(innerNode); + } + } + + if (isText(nextNode)) { + return CaretPosition(nextNode, 0); + } + + return CaretPosition.after(nextNode); + } + } + + node = caretPosition.getNode(); + } + + if ((isForwards(direction) && caretPosition.isAtEnd()) || (isBackwards(direction) && caretPosition.isAtStart())) { + node = CaretUtils.findNode(node, direction, Fun.constant(true), rootNode, true); + if (isEditableCaretCandidate(node)) { + return getCaretCandidatePosition(direction, node); + } + } + + nextNode = CaretUtils.findNode(node, direction, isEditableCaretCandidate, rootNode); + + rootContentEditableFalseElm = Arr.last(Arr.filter(getParents(container, rootNode), isContentEditableFalse)); + if (rootContentEditableFalseElm && (!nextNode || !rootContentEditableFalseElm.contains(nextNode))) { + if (isForwards(direction)) { + caretPosition = CaretPosition.after(rootContentEditableFalseElm); + } else { + caretPosition = CaretPosition.before(rootContentEditableFalseElm); + } + + return caretPosition; + } + + if (nextNode) { + return getCaretCandidatePosition(direction, nextNode); + } + + return null; + } + + return function(rootNode) { + return { + /** + * Returns the next logical caret position from the specificed input + * caretPoisiton or null if there isn't any more positions left for example + * at the end specified root element. + * + * @method next + * @param {tinymce.caret.CaretPosition} caretPosition Caret position to start from. + * @return {tinymce.caret.CaretPosition} CaretPosition or null if no position was found. + */ + next: function(caretPosition) { + return findCaretPosition(1, caretPosition, rootNode); + }, + + /** + * Returns the previous logical caret position from the specificed input + * caretPoisiton or null if there isn't any more positions left for example + * at the end specified root element. + * + * @method prev + * @param {tinymce.caret.CaretPosition} caretPosition Caret position to start from. + * @return {tinymce.caret.CaretPosition} CaretPosition or null if no position was found. + */ + prev: function(caretPosition) { + return findCaretPosition(-1, caretPosition, rootNode); + } + }; + }; + } + ); + /** + * CaretFinder.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.caret.CaretFinder', [ + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Option', + 'tinymce.core.caret.CaretCandidate', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.caret.CaretUtils', + 'tinymce.core.caret.CaretWalker', + 'tinymce.core.dom.NodeType' + ], + function(Fun, Option, CaretCandidate, CaretPosition, CaretUtils, CaretWalker, NodeType) { + var walkToPositionIn = function(forward, rootNode, startNode) { + var position = forward ? CaretPosition.before(startNode) : CaretPosition.after(startNode); + return fromPosition(forward, rootNode, position); + }; + + var afterElement = function(node) { + return NodeType.isBr(node) ? CaretPosition.before(node) : CaretPosition.after(node); + }; + + var isBeforeOrStart = function(position) { + if (CaretPosition.isTextPosition(position)) { + return position.offset() === 0; + } else { + return CaretCandidate.isCaretCandidate(position.getNode()); + } + }; + + var isAfterOrEnd = function(position) { + if (CaretPosition.isTextPosition(position)) { + return position.offset() === position.container().data.length; + } else { + return CaretCandidate.isCaretCandidate(position.getNode(true)); + } + }; + + var isBeforeAfterSameElement = function(from, to) { + return !CaretPosition.isTextPosition(from) && !CaretPosition.isTextPosition(to) && from.getNode() === to.getNode(true); + }; + + var isAtBr = function(position) { + return !CaretPosition.isTextPosition(position) && NodeType.isBr(position.getNode()); + }; + + var shouldSkipPosition = function(forward, from, to) { + if (forward) { + return !isBeforeAfterSameElement(from, to) && !isAtBr(from) && isAfterOrEnd(from) && isBeforeOrStart(to); + } else { + return !isBeforeAfterSameElement(to, from) && isBeforeOrStart(from) && isAfterOrEnd(to); + } + }; + + // Finds: <p>a|<b>b</b></p> -> <p>a<b>|b</b></p> + var fromPosition = function(forward, rootNode, position) { + var walker = new CaretWalker(rootNode); + return Option.from(forward ? walker.next(position) : walker.prev(position)); + }; + + // Finds: <p>a|<b>b</b></p> -> <p>a<b>b|</b></p> + var navigate = function(forward, rootNode, from) { + return fromPosition(forward, rootNode, from).bind(function(to) { + if (CaretUtils.isInSameBlock(from, to, rootNode) && shouldSkipPosition(forward, from, to)) { + return fromPosition(forward, rootNode, to); + } else { + return Option.some(to); + } + }); + }; + + var positionIn = function(forward, element) { + var startNode = forward ? element.firstChild : element.lastChild; + if (NodeType.isText(startNode)) { + return Option.some(new CaretPosition(startNode, forward ? 0 : startNode.data.length)); + } else if (startNode) { + if (CaretCandidate.isCaretCandidate(startNode)) { + return Option.some(forward ? CaretPosition.before(startNode) : afterElement(startNode)); + } else { + return walkToPositionIn(forward, element, startNode); + } + } else { + return Option.none(); + } + }; + + return { + fromPosition: fromPosition, + navigate: navigate, + positionIn: positionIn + }; + } + ); + + /** + * DeleteUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.delete.DeleteUtils', [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Option', + 'ephox.sugar.api.dom.Compare', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.api.node.Node', + 'ephox.sugar.api.search.PredicateFind' + ], + function(Arr, Option, Compare, Element, Node, PredicateFind) { + var toLookup = function(names) { + var lookup = Arr.foldl(names, function(acc, name) { + acc[name] = true; + return acc; + }, {}); + + return function(elm) { + return lookup[Node.name(elm)] === true; + }; + }; + + var isTextBlock = toLookup([ + 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'address', 'pre', 'form', 'blockquote', 'center', + 'dir', 'fieldset', 'header', 'footer', 'article', 'section', 'hgroup', 'aside', 'nav', 'figure' + ]); + + var isBeforeRoot = function(rootNode) { + return function(elm) { + return Compare.eq(rootNode, Element.fromDom(elm.dom().parentNode)); + }; + }; + + var getParentTextBlock = function(rootNode, elm) { + return Compare.contains(rootNode, elm) ? PredicateFind.closest(elm, isTextBlock, isBeforeRoot(rootNode)) : Option.none(); + }; + + return { + getParentTextBlock: getParentTextBlock + }; + } + ); + + define( + 'ephox.sugar.api.search.SelectorFind', + + [ + 'ephox.sugar.api.search.PredicateFind', + 'ephox.sugar.api.search.Selectors', + 'ephox.sugar.impl.ClosestOrAncestor' + ], + + function(PredicateFind, Selectors, ClosestOrAncestor) { + // TODO: An internal SelectorFilter module that doesn't Element.fromDom() everything + + var first = function(selector) { + return Selectors.one(selector); + }; + + var ancestor = function(scope, selector, isRoot) { + return PredicateFind.ancestor(scope, function(e) { + return Selectors.is(e, selector); + }, isRoot); + }; + + var sibling = function(scope, selector) { + return PredicateFind.sibling(scope, function(e) { + return Selectors.is(e, selector); + }); + }; + + var child = function(scope, selector) { + return PredicateFind.child(scope, function(e) { + return Selectors.is(e, selector); + }); + }; + + var descendant = function(scope, selector) { + return Selectors.one(selector, scope); + }; + + // Returns Some(closest ancestor element (sugared)) matching 'selector' up to isRoot, or None() otherwise + var closest = function(scope, selector, isRoot) { + return ClosestOrAncestor(Selectors.is, ancestor, scope, selector, isRoot); + }; + + return { + first: first, + ancestor: ancestor, + sibling: sibling, + child: child, + descendant: descendant, + closest: closest + }; + } + ); + + define( + 'ephox.sugar.api.search.SelectorExists', + + [ + 'ephox.sugar.api.search.SelectorFind' + ], + + function(SelectorFind) { + var any = function(selector) { + return SelectorFind.first(selector).isSome(); + }; + + var ancestor = function(scope, selector, isRoot) { + return SelectorFind.ancestor(scope, selector, isRoot).isSome(); + }; + + var sibling = function(scope, selector) { + return SelectorFind.sibling(scope, selector).isSome(); + }; + + var child = function(scope, selector) { + return SelectorFind.child(scope, selector).isSome(); + }; + + var descendant = function(scope, selector) { + return SelectorFind.descendant(scope, selector).isSome(); + }; + + var closest = function(scope, selector, isRoot) { + return SelectorFind.closest(scope, selector, isRoot).isSome(); + }; + + return { + any: any, + ancestor: ancestor, + sibling: sibling, + child: child, + descendant: descendant, + closest: closest + }; + } + ); + + /** + * Empty.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.dom.Empty', [ + 'ephox.katamari.api.Fun', + 'ephox.sugar.api.dom.Compare', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.api.search.SelectorExists', + 'tinymce.core.caret.CaretCandidate', + 'tinymce.core.dom.NodeType', + 'tinymce.core.dom.TreeWalker' + ], + function(Fun, Compare, Element, SelectorExists, CaretCandidate, NodeType, TreeWalker) { + var hasWhitespacePreserveParent = function(rootNode, node) { + var rootElement = Element.fromDom(rootNode); + var startNode = Element.fromDom(node); + return SelectorExists.ancestor(startNode, 'pre,code', Fun.curry(Compare.eq, rootElement)); + }; + + var isWhitespace = function(rootNode, node) { + return NodeType.isText(node) && /^[ \t\r\n]*$/.test(node.data) && hasWhitespacePreserveParent(rootNode, node) === false; + }; + + var isNamedAnchor = function(node) { + return NodeType.isElement(node) && node.nodeName === 'A' && node.hasAttribute('name'); + }; + + var isContent = function(rootNode, node) { + return (CaretCandidate.isCaretCandidate(node) && isWhitespace(rootNode, node) === false) || isNamedAnchor(node) || isBookmark(node); + }; + + var isBookmark = NodeType.hasAttribute('data-mce-bookmark'); + var isBogus = NodeType.hasAttribute('data-mce-bogus'); + var isBogusAll = NodeType.hasAttributeValue('data-mce-bogus', 'all'); + + var isEmptyNode = function(targetNode) { + var walker, node, brCount = 0; + + if (isContent(targetNode, targetNode)) { + return false; + } else { + node = targetNode.firstChild; + if (!node) { + return true; + } + + walker = new TreeWalker(node, targetNode); + do { + if (isBogusAll(node)) { + node = walker.next(true); + continue; + } + + if (isBogus(node)) { + node = walker.next(); + continue; + } + + if (NodeType.isBr(node)) { + brCount++; + node = walker.next(); + continue; + } + + if (isContent(targetNode, node)) { + return false; + } + + node = walker.next(); + } while (node); + + return brCount <= 1; + } + }; + + var isEmpty = function(elm) { + return isEmptyNode(elm.dom()); + }; + + return { + isEmpty: isEmpty + }; + } + ); + + /** + * BlockBoundary.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.delete.BlockBoundary', [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Option', + 'ephox.katamari.api.Options', + 'ephox.katamari.api.Struct', + 'ephox.sugar.api.dom.Compare', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.api.node.Node', + 'ephox.sugar.api.search.PredicateFind', + 'ephox.sugar.api.search.Traverse', + 'tinymce.core.caret.CaretFinder', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.delete.DeleteUtils', + 'tinymce.core.dom.Empty', + 'tinymce.core.dom.NodeType' + ], + function(Arr, Fun, Option, Options, Struct, Compare, Element, Node, PredicateFind, Traverse, CaretFinder, CaretPosition, DeleteUtils, Empty, NodeType) { + var BlockPosition = Struct.immutable('block', 'position'); + var BlockBoundary = Struct.immutable('from', 'to'); + + var getBlockPosition = function(rootNode, pos) { + var rootElm = Element.fromDom(rootNode); + var containerElm = Element.fromDom(pos.container()); + return DeleteUtils.getParentTextBlock(rootElm, containerElm).map(function(block) { + return BlockPosition(block, pos); + }); + }; + + var isDifferentBlocks = function(blockBoundary) { + return Compare.eq(blockBoundary.from().block(), blockBoundary.to().block()) === false; + }; + + var hasSameParent = function(blockBoundary) { + return Traverse.parent(blockBoundary.from().block()).bind(function(parent1) { + return Traverse.parent(blockBoundary.to().block()).filter(function(parent2) { + return Compare.eq(parent1, parent2); + }); + }).isSome(); + }; + + var isEditable = function(blockBoundary) { + return NodeType.isContentEditableFalse(blockBoundary.from().block()) === false && NodeType.isContentEditableFalse(blockBoundary.to().block()) === false; + }; + + var skipLastBr = function(rootNode, forward, blockPosition) { + if (NodeType.isBr(blockPosition.position().getNode()) && Empty.isEmpty(blockPosition.block()) === false) { + return CaretFinder.positionIn(false, blockPosition.block().dom()).bind(function(lastPositionInBlock) { + if (lastPositionInBlock.isEqual(blockPosition.position())) { + return CaretFinder.fromPosition(forward, rootNode, lastPositionInBlock).bind(function(to) { + return getBlockPosition(rootNode, to); + }); + } else { + return Option.some(blockPosition); + } + }).getOr(blockPosition); + } else { + return blockPosition; + } + }; + + var readFromRange = function(rootNode, forward, rng) { + var fromBlockPos = getBlockPosition(rootNode, CaretPosition.fromRangeStart(rng)); + var toBlockPos = fromBlockPos.bind(function(blockPos) { + return CaretFinder.fromPosition(forward, rootNode, blockPos.position()).bind(function(to) { + return getBlockPosition(rootNode, to).map(function(blockPos) { + return skipLastBr(rootNode, forward, blockPos); + }); + }); + }); + + return Options.liftN([fromBlockPos, toBlockPos], BlockBoundary).filter(function(blockBoundary) { + return isDifferentBlocks(blockBoundary) && hasSameParent(blockBoundary) && isEditable(blockBoundary); + }); + }; + + var read = function(rootNode, forward, rng) { + return rng.collapsed ? readFromRange(rootNode, forward, rng) : Option.none(); + }; + + return { + read: read + }; + } + ); + + /** + * MergeBlocks.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.delete.MergeBlocks', [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Option', + 'ephox.sugar.api.dom.Insert', + 'ephox.sugar.api.dom.Remove', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.api.search.Traverse', + 'tinymce.core.caret.CaretFinder', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.dom.Empty', + 'tinymce.core.dom.NodeType' + ], + function(Arr, Option, Insert, Remove, Element, Traverse, CaretFinder, CaretPosition, Empty, NodeType) { + var mergeBlocksAndReposition = function(forward, fromBlock, toBlock, toPosition) { + var children = Traverse.children(fromBlock); + + if (NodeType.isBr(toPosition.getNode())) { + Remove.remove(Element.fromDom(toPosition.getNode())); + toPosition = CaretFinder.positionIn(false, toBlock.dom()).getOr(toPosition); + } + + if (Empty.isEmpty(fromBlock) === false) { + Arr.each(children, function(node) { + Insert.append(toBlock, node); + }); + } + + if (Empty.isEmpty(fromBlock)) { + Remove.remove(fromBlock); + } + + return children.length > 0 ? Option.from(toPosition) : Option.none(); + }; + + var mergeBlocks = function(forward, block1, block2) { + if (forward) { + if (Empty.isEmpty(block1)) { + Remove.remove(block1); + return CaretFinder.positionIn(true, block2.dom()); + } else { + return CaretFinder.positionIn(false, block1.dom()).bind(function(toPosition) { + return mergeBlocksAndReposition(forward, block2, block1, toPosition); + }); + } + } else { + if (Empty.isEmpty(block2)) { + Remove.remove(block2); + return CaretFinder.positionIn(true, block1.dom()); + } else { + return CaretFinder.positionIn(false, block2.dom()).bind(function(toPosition) { + return mergeBlocksAndReposition(forward, block1, block2, toPosition); + }); + } + } + }; + + return { + mergeBlocks: mergeBlocks + }; + } + ); + + /** + * BlockBoundaryDelete.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.delete.BlockBoundaryDelete', [ + 'tinymce.core.delete.BlockBoundary', + 'tinymce.core.delete.MergeBlocks' + ], + function(BlockBoundary, MergeBlocks) { + var backspaceDelete = function(editor, forward) { + var position; + + position = BlockBoundary.read(editor.getBody(), forward, editor.selection.getRng()).bind(function(blockBoundary) { + return MergeBlocks.mergeBlocks(forward, blockBoundary.from().block(), blockBoundary.to().block()); + }); + + position.each(function(pos) { + editor.selection.setRng(pos.toRange()); + }); + + return position.isSome(); + }; + + return { + backspaceDelete: backspaceDelete + }; + } + ); + + /** + * BlockRangeDelete.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.delete.BlockRangeDelete', [ + 'ephox.katamari.api.Options', + 'ephox.sugar.api.dom.Compare', + 'ephox.sugar.api.node.Element', + 'tinymce.core.delete.DeleteUtils', + 'tinymce.core.delete.MergeBlocks' + ], + function(Options, Compare, Element, DeleteUtils, MergeBlocks) { + var deleteRange = function(rootNode, selection) { + var rng = selection.getRng(); + + return Options.liftN([ + DeleteUtils.getParentTextBlock(rootNode, Element.fromDom(rng.startContainer)), + DeleteUtils.getParentTextBlock(rootNode, Element.fromDom(rng.endContainer)) + ], function(block1, block2) { + if (Compare.eq(block1, block2) === false) { + rng.deleteContents(); + + MergeBlocks.mergeBlocks(true, block1, block2).each(function(pos) { + selection.setRng(pos.toRange()); + }); + + return true; + } else { + return false; + } + }).getOr(false); + }; + + var backspaceDelete = function(editor, forward) { + var rootNode = Element.fromDom(editor.getBody()); + + if (editor.selection.isCollapsed() === false) { + return deleteRange(rootNode, editor.selection); + } else { + return false; + } + }; + + return { + backspaceDelete: backspaceDelete + }; + } + ); + + define( + 'ephox.katamari.api.Adt', + + [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Obj', + 'ephox.katamari.api.Type', + 'global!Array', + 'global!Error', + 'global!console' + ], + + function(Arr, Obj, Type, Array, Error, console) { + /* + * Generates a church encoded ADT (https://en.wikipedia.org/wiki/Church_encoding) + * For syntax and use, look at the test code. + */ + var generate = function(cases) { + // validation + if (!Type.isArray(cases)) { + throw new Error('cases must be an array'); + } + if (cases.length === 0) { + throw new Error('there must be at least one case'); + } + + var constructors = []; + + // adt is mutated to add the individual cases + var adt = {}; + Arr.each(cases, function(acase, count) { + var keys = Obj.keys(acase); + + // validation + if (keys.length !== 1) { + throw new Error('one and only one name per case'); + } + + var key = keys[0]; + var value = acase[key]; + + // validation + if (adt[key] !== undefined) { + throw new Error('duplicate key detected:' + key); + } else if (key === 'cata') { + throw new Error('cannot have a case named cata (sorry)'); + } else if (!Type.isArray(value)) { + // this implicitly checks if acase is an object + throw new Error('case arguments must be an array'); + } + + constructors.push(key); + // + // constructor for key + // + adt[key] = function() { + var argLength = arguments.length; + + // validation + if (argLength !== value.length) { + throw new Error('Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + argLength); + } + + // Don't use array slice(arguments), makes the whole function unoptimisable on Chrome + var args = new Array(argLength); + for (var i = 0; i < args.length; i++) args[i] = arguments[i]; + + + var match = function(branches) { + var branchKeys = Obj.keys(branches); + if (constructors.length !== branchKeys.length) { + throw new Error('Wrong number of arguments to match. Expected: ' + constructors.join(',') + '\nActual: ' + branchKeys.join(',')); + } + + var allReqd = Arr.forall(constructors, function(reqKey) { + return Arr.contains(branchKeys, reqKey); + }); + + if (!allReqd) throw new Error('Not all branches were specified when using match. Specified: ' + branchKeys.join(', ') + '\nRequired: ' + constructors.join(', ')); + + return branches[key].apply(null, args); + }; + + // + // the fold function for key + // + return { + fold: function( /* arguments */ ) { + // runtime validation + if (arguments.length !== cases.length) { + throw new Error('Wrong number of arguments to fold. Expected ' + cases.length + ', got ' + arguments.length); + } + var target = arguments[count]; + return target.apply(null, args); + }, + match: match, + + // NOTE: Only for debugging. + log: function(label) { + console.log(label, { + constructors: constructors, + constructor: key, + params: args + }); + } + }; + }; + }); + + return adt; + }; + return { + generate: generate + }; + } + ); + /** + * CefDeleteAction.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.delete.CefDeleteAction', [ + 'ephox.katamari.api.Adt', + 'ephox.katamari.api.Option', + 'ephox.sugar.api.node.Element', + 'tinymce.core.caret.CaretFinder', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.caret.CaretUtils', + 'tinymce.core.delete.DeleteUtils', + 'tinymce.core.dom.Empty', + 'tinymce.core.dom.NodeType' + ], + function(Adt, Option, Element, CaretFinder, CaretPosition, CaretUtils, DeleteUtils, Empty, NodeType) { + var DeleteAction = Adt.generate([{ + remove: ['element'] + }, + { + moveToElement: ['element'] + }, + { + moveToPosition: ['position'] + } + ]); + + var isAtContentEditableBlockCaret = function(forward, from) { + var elm = from.getNode(forward === false); + var caretLocation = forward ? 'after' : 'before'; + return NodeType.isElement(elm) && elm.getAttribute('data-mce-caret') === caretLocation; + }; + + var deleteEmptyBlockOrMoveToCef = function(rootNode, forward, from, to) { + var toCefElm = to.getNode(forward === false); + return DeleteUtils.getParentTextBlock(Element.fromDom(rootNode), Element.fromDom(from.getNode())).map(function(blockElm) { + return Empty.isEmpty(blockElm) ? DeleteAction.remove(blockElm.dom()) : DeleteAction.moveToElement(toCefElm); + }).orThunk(function() { + return Option.some(DeleteAction.moveToElement(toCefElm)); + }); + }; + + var findCefPosition = function(rootNode, forward, from) { + return CaretFinder.fromPosition(forward, rootNode, from).bind(function(to) { + if (forward && NodeType.isContentEditableFalse(to.getNode())) { + return deleteEmptyBlockOrMoveToCef(rootNode, forward, from, to); + } else if (forward === false && NodeType.isContentEditableFalse(to.getNode(true))) { + return deleteEmptyBlockOrMoveToCef(rootNode, forward, from, to); + } else if (forward && CaretUtils.isAfterContentEditableFalse(from)) { + return Option.some(DeleteAction.moveToPosition(to)); + } else if (forward === false && CaretUtils.isBeforeContentEditableFalse(from)) { + return Option.some(DeleteAction.moveToPosition(to)); + } else { + return Option.none(); + } + }); + }; + + var getContentEditableBlockAction = function(forward, elm) { + if (forward && NodeType.isContentEditableFalse(elm.nextSibling)) { + return Option.some(DeleteAction.moveToElement(elm.nextSibling)); + } else if (forward === false && NodeType.isContentEditableFalse(elm.previousSibling)) { + return Option.some(DeleteAction.moveToElement(elm.previousSibling)); + } else { + return Option.none(); + } + }; + + var getContentEditableAction = function(rootNode, forward, from) { + if (isAtContentEditableBlockCaret(forward, from)) { + return getContentEditableBlockAction(forward, from.getNode(forward === false)) + .fold( + function() { + return findCefPosition(rootNode, forward, from); + }, + Option.some + ); + } else { + return findCefPosition(rootNode, forward, from); + } + }; + + var read = function(rootNode, forward, rng) { + var normalizedRange = CaretUtils.normalizeRange(forward ? 1 : -1, rootNode, rng); + var from = CaretPosition.fromRangeStart(normalizedRange); + + if (forward === false && CaretUtils.isAfterContentEditableFalse(from)) { + return Option.some(DeleteAction.remove(from.getNode(true))); + } else if (forward && CaretUtils.isBeforeContentEditableFalse(from)) { + return Option.some(DeleteAction.remove(from.getNode())); + } else { + return getContentEditableAction(rootNode, forward, from); + } + }; + + return { + read: read + }; + } + ); + + /** + * Bidi.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.text.Bidi', [], + function() { + var strongRtl = /[\u0591-\u07FF\uFB1D-\uFDFF\uFE70-\uFEFC]/; + + var hasStrongRtl = function(text) { + return strongRtl.test(text); + }; + + return { + hasStrongRtl: hasStrongRtl + }; + } + ); + /** + * InlineUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.keyboard.InlineUtils', [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Option', + 'ephox.katamari.api.Options', + 'tinymce.core.caret.CaretContainer', + 'tinymce.core.caret.CaretFinder', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.caret.CaretUtils', + 'tinymce.core.caret.CaretWalker', + 'tinymce.core.dom.DOMUtils', + 'tinymce.core.dom.NodeType', + 'tinymce.core.text.Bidi' + ], + function(Arr, Fun, Option, Options, CaretContainer, CaretFinder, CaretPosition, CaretUtils, CaretWalker, DOMUtils, NodeType, Bidi) { + var isInlineTarget = function(elm) { + return DOMUtils.DOM.is(elm, 'a[href],code'); + }; + + var isRtl = function(element) { + return DOMUtils.DOM.getStyle(element, 'direction', true) === 'rtl' || Bidi.hasStrongRtl(element.textContent); + }; + + var findInlineParents = function(rootNode, pos) { + return Arr.filter(DOMUtils.DOM.getParents(pos.container(), '*', rootNode), isInlineTarget); + }; + + var findInline = function(rootNode, pos) { + var parents = findInlineParents(rootNode, pos); + return Option.from(parents[0]); + }; + + var findRootInline = function(rootNode, pos) { + var parents = findInlineParents(rootNode, pos); + return Option.from(parents[parents.length - 1]); + }; + + var hasSameParentBlock = function(rootNode, node1, node2) { + var block1 = CaretUtils.getParentBlock(node1, rootNode); + var block2 = CaretUtils.getParentBlock(node2, rootNode); + return block1 && block1 === block2; + }; + + var isInInline = function(rootNode, pos) { + return pos ? findRootInline(rootNode, pos).isSome() : false; + }; + + var isAtInlineEndPoint = function(rootNode, pos) { + return findRootInline(rootNode, pos).map(function(inline) { + return findCaretPosition(inline, false, pos).isNone() || findCaretPosition(inline, true, pos).isNone(); + }).getOr(false); + }; + + var isAtZwsp = function(pos) { + return CaretContainer.isBeforeInline(pos) || CaretContainer.isAfterInline(pos); + }; + + var findCaretPositionIn = function(node, forward) { + return CaretFinder.positionIn(forward, node); + }; + + var findCaretPosition = function(rootNode, forward, from) { + return CaretFinder.fromPosition(forward, rootNode, from); + }; + + var normalizePosition = function(forward, pos) { + var container = pos.container(), + offset = pos.offset(); + + if (forward) { + if (CaretContainer.isCaretContainerInline(container)) { + if (NodeType.isText(container.nextSibling)) { + return new CaretPosition(container.nextSibling, 0); + } else { + return CaretPosition.after(container); + } + } else { + return CaretContainer.isBeforeInline(pos) ? new CaretPosition(container, offset + 1) : pos; + } + } else { + if (CaretContainer.isCaretContainerInline(container)) { + if (NodeType.isText(container.previousSibling)) { + return new CaretPosition(container.previousSibling, container.previousSibling.data.length); + } else { + return CaretPosition.before(container); + } + } else { + return CaretContainer.isAfterInline(pos) ? new CaretPosition(container, offset - 1) : pos; + } + } + }; + + var normalizeForwards = Fun.curry(normalizePosition, true); + var normalizeBackwards = Fun.curry(normalizePosition, false); + + return { + isInlineTarget: isInlineTarget, + findInline: findInline, + findRootInline: findRootInline, + isInInline: isInInline, + isRtl: isRtl, + isAtInlineEndPoint: isAtInlineEndPoint, + isAtZwsp: isAtZwsp, + findCaretPositionIn: findCaretPositionIn, + findCaretPosition: findCaretPosition, + normalizePosition: normalizePosition, + normalizeForwards: normalizeForwards, + normalizeBackwards: normalizeBackwards, + hasSameParentBlock: hasSameParentBlock + }; + } + ); + /** + * DeleteElement.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.delete.DeleteElement', [ + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Option', + 'ephox.katamari.api.Options', + 'ephox.sugar.api.dom.Insert', + 'ephox.sugar.api.dom.Remove', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.api.node.Node', + 'ephox.sugar.api.search.PredicateFind', + 'ephox.sugar.api.search.Traverse', + 'tinymce.core.caret.CaretCandidate', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.dom.Empty', + 'tinymce.core.dom.NodeType', + 'tinymce.core.keyboard.InlineUtils' + ], + function(Fun, Option, Options, Insert, Remove, Element, Node, PredicateFind, Traverse, CaretCandidate, CaretPosition, Empty, NodeType, InlineUtils) { + var needsReposition = function(pos, elm) { + var container = pos.container(); + var offset = pos.offset(); + return CaretPosition.isTextPosition(pos) === false && container === elm.parentNode && offset > CaretPosition.before(elm).offset(); + }; + + var reposition = function(elm, pos) { + return needsReposition(pos, elm) ? new CaretPosition(pos.container(), pos.offset() - 1) : pos; + }; + + var beforeOrStartOf = function(node) { + return NodeType.isText(node) ? new CaretPosition(node, 0) : CaretPosition.before(node); + }; + + var afterOrEndOf = function(node) { + return NodeType.isText(node) ? new CaretPosition(node, node.data.length) : CaretPosition.after(node); + }; + + var getPreviousSiblingCaretPosition = function(elm) { + if (CaretCandidate.isCaretCandidate(elm.previousSibling)) { + return Option.some(afterOrEndOf(elm.previousSibling)); + } else { + return elm.previousSibling ? InlineUtils.findCaretPositionIn(elm.previousSibling, false) : Option.none(); + } + }; + + var getNextSiblingCaretPosition = function(elm) { + if (CaretCandidate.isCaretCandidate(elm.nextSibling)) { + return Option.some(beforeOrStartOf(elm.nextSibling)); + } else { + return elm.nextSibling ? InlineUtils.findCaretPositionIn(elm.nextSibling, true) : Option.none(); + } + }; + + var findCaretPositionBackwardsFromElm = function(rootElement, elm) { + var startPosition = CaretPosition.before(elm.previousSibling ? elm.previousSibling : elm.parentNode); + return InlineUtils.findCaretPosition(rootElement, false, startPosition).fold( + function() { + return InlineUtils.findCaretPosition(rootElement, true, CaretPosition.after(elm)); + }, + Option.some + ); + }; + + var findCaretPositionForwardsFromElm = function(rootElement, elm) { + return InlineUtils.findCaretPosition(rootElement, true, CaretPosition.after(elm)).fold( + function() { + return InlineUtils.findCaretPosition(rootElement, false, CaretPosition.before(elm)); + }, + Option.some + ); + }; + + var findCaretPositionBackwards = function(rootElement, elm) { + return getPreviousSiblingCaretPosition(elm).orThunk(function() { + return getNextSiblingCaretPosition(elm); + }).orThunk(function() { + return findCaretPositionBackwardsFromElm(rootElement, elm); + }); + }; + + var findCaretPositionForward = function(rootElement, elm) { + return getNextSiblingCaretPosition(elm).orThunk(function() { + return getPreviousSiblingCaretPosition(elm); + }).orThunk(function() { + return findCaretPositionForwardsFromElm(rootElement, elm); + }); + }; + + var findCaretPosition = function(forward, rootElement, elm) { + return forward ? findCaretPositionForward(rootElement, elm) : findCaretPositionBackwards(rootElement, elm); + }; + + var findCaretPosOutsideElmAfterDelete = function(forward, rootElement, elm) { + return findCaretPosition(forward, rootElement, elm).map(Fun.curry(reposition, elm)); + }; + + var setSelection = function(editor, forward, pos) { + pos.fold( + function() { + editor.focus(); + }, + function(pos) { + editor.selection.setRng(pos.toRange(), forward); + } + ); + }; + + var eqRawNode = function(rawNode) { + return function(elm) { + return elm.dom() === rawNode; + }; + }; + + var isBlock = function(editor, elm) { + return elm && editor.schema.getBlockElements().hasOwnProperty(Node.name(elm)); + }; + + var paddEmptyBlock = function(elm) { + if (Empty.isEmpty(elm)) { + var br = Element.fromHtml('<br data-mce-bogus="1">'); + Remove.empty(elm); + Insert.append(elm, br); + return Option.some(CaretPosition.before(br.dom())); + } else { + return Option.none(); + } + }; + + // When deleting an element between two text nodes IE 11 doesn't automatically merge the adjacent text nodes + var deleteNormalized = function(elm, afterDeletePosOpt) { + return Options.liftN([Traverse.prevSibling(elm), Traverse.nextSibling(elm), afterDeletePosOpt], function(prev, next, afterDeletePos) { + var offset, prevNode = prev.dom(), + nextNode = next.dom(); + + if (NodeType.isText(prevNode) && NodeType.isText(nextNode)) { + offset = prevNode.data.length; + prevNode.appendData(nextNode.data); + Remove.remove(next); + Remove.remove(elm); + if (afterDeletePos.container() === nextNode) { + return new CaretPosition(prevNode, offset); + } else { + return afterDeletePos; + } + } else { + Remove.remove(elm); + return afterDeletePos; + } + }).orThunk(function() { + Remove.remove(elm); + return afterDeletePosOpt; + }); + }; + + var deleteElement = function(editor, forward, elm) { + var afterDeletePos = findCaretPosOutsideElmAfterDelete(forward, editor.getBody(), elm.dom()); + var parentBlock = PredicateFind.ancestor(elm, Fun.curry(isBlock, editor), eqRawNode(editor.getBody())); + var normalizedAfterDeletePos = deleteNormalized(elm, afterDeletePos); + + parentBlock.bind(paddEmptyBlock).fold( + function() { + setSelection(editor, forward, normalizedAfterDeletePos); + }, + function(paddPos) { + setSelection(editor, forward, Option.some(paddPos)); + } + ); + }; + + return { + deleteElement: deleteElement + }; + } + ); + /** + * CefDelete.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.delete.CefDelete', [ + 'ephox.sugar.api.node.Element', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.caret.CaretUtils', + 'tinymce.core.delete.BlockBoundary', + 'tinymce.core.delete.CefDeleteAction', + 'tinymce.core.delete.DeleteElement', + 'tinymce.core.delete.MergeBlocks', + 'tinymce.core.dom.NodeType' + ], + function(Element, CaretPosition, CaretUtils, BlockBoundary, CefDeleteAction, DeleteElement, MergeBlocks, NodeType) { + var deleteElement = function(editor, forward) { + return function(element) { + DeleteElement.deleteElement(editor, forward, Element.fromDom(element)); + return true; + }; + }; + + var moveToElement = function(editor, forward) { + return function(element) { + var pos = forward ? CaretPosition.before(element) : CaretPosition.after(element); + editor.selection.setRng(pos.toRange()); + return true; + }; + }; + + var moveToPosition = function(editor) { + return function(pos) { + editor.selection.setRng(pos.toRange()); + return true; + }; + }; + + var backspaceDeleteCaret = function(editor, forward) { + var result = CefDeleteAction.read(editor.getBody(), forward, editor.selection.getRng()).map(function(deleteAction) { + return deleteAction.fold( + deleteElement(editor, forward), + moveToElement(editor, forward), + moveToPosition(editor) + ); + }); + + return result.getOr(false); + }; + + var backspaceDeleteRange = function(editor, forward) { + var selectedElement = editor.selection.getNode(); + if (NodeType.isContentEditableFalse(selectedElement)) { + DeleteElement.deleteElement(editor, forward, Element.fromDom(editor.selection.getNode())); + return true; + } else { + return false; + } + }; + + var getContentEditableRoot = function(root, node) { + while (node && node !== root) { + if (NodeType.isContentEditableTrue(node) || NodeType.isContentEditableFalse(node)) { + return node; + } + + node = node.parentNode; + } + + return null; + }; + + var paddEmptyElement = function(editor) { + var br, ceRoot = getContentEditableRoot(editor.getBody(), editor.selection.getNode()); + + if (NodeType.isContentEditableTrue(ceRoot) && editor.dom.isBlock(ceRoot) && editor.dom.isEmpty(ceRoot)) { + br = editor.dom.create('br', { + "data-mce-bogus": "1" + }); + editor.dom.setHTML(ceRoot, ''); + ceRoot.appendChild(br); + editor.selection.setRng(CaretPosition.before(br).toRange()); + } + + return true; + }; + + var backspaceDelete = function(editor, forward) { + if (editor.selection.isCollapsed()) { + return backspaceDeleteCaret(editor, forward); + } else { + return backspaceDeleteRange(editor, forward); + } + }; + + return { + backspaceDelete: backspaceDelete, + paddEmptyElement: paddEmptyElement + }; + } + ); + + /** + * CaretContainerInline.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.caret.CaretContainerInline', [ + 'ephox.katamari.api.Fun', + 'tinymce.core.dom.NodeType', + 'tinymce.core.text.Zwsp' + ], + function(Fun, NodeType, Zwsp) { + var isText = NodeType.isText; + + var startsWithCaretContainer = function(node) { + return isText(node) && node.data[0] === Zwsp.ZWSP; + }; + + var endsWithCaretContainer = function(node) { + return isText(node) && node.data[node.data.length - 1] === Zwsp.ZWSP; + }; + + var createZwsp = function(node) { + return node.ownerDocument.createTextNode(Zwsp.ZWSP); + }; + + var insertBefore = function(node) { + if (isText(node.previousSibling)) { + if (endsWithCaretContainer(node.previousSibling)) { + return node.previousSibling; + } else { + node.previousSibling.appendData(Zwsp.ZWSP); + return node.previousSibling; + } + } else if (isText(node)) { + if (startsWithCaretContainer(node)) { + return node; + } else { + node.insertData(0, Zwsp.ZWSP); + return node; + } + } else { + var newNode = createZwsp(node); + node.parentNode.insertBefore(newNode, node); + return newNode; + } + }; + + var insertAfter = function(node) { + if (isText(node.nextSibling)) { + if (startsWithCaretContainer(node.nextSibling)) { + return node.nextSibling; + } else { + node.nextSibling.insertData(0, Zwsp.ZWSP); + return node.nextSibling; + } + } else if (isText(node)) { + if (endsWithCaretContainer(node)) { + return node; + } else { + node.appendData(Zwsp.ZWSP); + return node; + } + } else { + var newNode = createZwsp(node); + if (node.nextSibling) { + node.parentNode.insertBefore(newNode, node.nextSibling); + } else { + node.parentNode.appendChild(newNode); + } + return newNode; + } + }; + + var insertInline = function(before, node) { + return before ? insertBefore(node) : insertAfter(node); + }; + + return { + insertInline: insertInline, + insertInlineBefore: Fun.curry(insertInline, true), + insertInlineAfter: Fun.curry(insertInline, false) + }; + } + ); + /** + * CaretContainerRemove.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.caret.CaretContainerRemove', [ + 'ephox.katamari.api.Arr', + 'tinymce.core.caret.CaretContainer', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.dom.NodeType', + 'tinymce.core.text.Zwsp', + 'tinymce.core.util.Tools' + ], + function(Arr, CaretContainer, CaretPosition, NodeType, Zwsp, Tools) { + var isElement = NodeType.isElement; + var isText = NodeType.isText; + + var removeNode = function(node) { + var parentNode = node.parentNode; + if (parentNode) { + parentNode.removeChild(node); + } + }; + + var getNodeValue = function(node) { + try { + return node.nodeValue; + } catch (ex) { + // IE sometimes produces "Invalid argument" on nodes + return ""; + } + }; + + var setNodeValue = function(node, text) { + if (text.length === 0) { + removeNode(node); + } else { + node.nodeValue = text; + } + }; + + var trimCount = function(text) { + var trimmedText = Zwsp.trim(text); + return { + count: text.length - trimmedText.length, + text: trimmedText + }; + }; + + var removeUnchanged = function(caretContainer, pos) { + remove(caretContainer); + return pos; + }; + + var removeTextAndReposition = function(caretContainer, pos) { + var before = trimCount(caretContainer.data.substr(0, pos.offset())); + var after = trimCount(caretContainer.data.substr(pos.offset())); + var text = before.text + after.text; + + if (text.length > 0) { + setNodeValue(caretContainer, text); + return new CaretPosition(caretContainer, pos.offset() - before.count); + } else { + return pos; + } + }; + + var removeElementAndReposition = function(caretContainer, pos) { + var parentNode = pos.container(); + var newPosition = Arr.indexOf(parentNode.childNodes, caretContainer).map(function(index) { + return index < pos.offset() ? new CaretPosition(parentNode, pos.offset() - 1) : pos; + }).getOr(pos); + remove(caretContainer); + return newPosition; + }; + + var removeTextCaretContainer = function(caretContainer, pos) { + return pos.container() === caretContainer ? removeTextAndReposition(caretContainer, pos) : removeUnchanged(caretContainer, pos); + }; + + var removeElementCaretContainer = function(caretContainer, pos) { + return pos.container() === caretContainer.parentNode ? removeElementAndReposition(caretContainer, pos) : removeUnchanged(caretContainer, pos); + }; + + var removeAndReposition = function(container, pos) { + return CaretPosition.isTextPosition(pos) ? removeTextCaretContainer(container, pos) : removeElementCaretContainer(container, pos); + }; + + var remove = function(caretContainerNode) { + if (isElement(caretContainerNode) && CaretContainer.isCaretContainer(caretContainerNode)) { + if (CaretContainer.hasContent(caretContainerNode)) { + caretContainerNode.removeAttribute('data-mce-caret'); + } else { + removeNode(caretContainerNode); + } + } + + if (isText(caretContainerNode)) { + var text = Zwsp.trim(getNodeValue(caretContainerNode)); + setNodeValue(caretContainerNode, text); + } + }; + + return { + removeAndReposition: removeAndReposition, + remove: remove + }; + } + ); + /** + * BoundaryCaret.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.keyboard.BoundaryCaret', [ + 'ephox.katamari.api.Option', + 'tinymce.core.caret.CaretContainer', + 'tinymce.core.caret.CaretContainerInline', + 'tinymce.core.caret.CaretContainerRemove', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.dom.NodeType', + 'tinymce.core.keyboard.InlineUtils' + ], + function(Option, CaretContainer, CaretContainerInline, CaretContainerRemove, CaretPosition, NodeType, InlineUtils) { + var insertInlinePos = function(pos, before) { + if (NodeType.isText(pos.container())) { + return CaretContainerInline.insertInline(before, pos.container()); + } else { + return CaretContainerInline.insertInline(before, pos.getNode()); + } + }; + + var isPosCaretContainer = function(pos, caret) { + var caretNode = caret.get(); + return caretNode && pos.container() === caretNode && CaretContainer.isCaretContainerInline(caretNode); + }; + + var renderCaret = function(caret, location) { + return location.fold( + function(element) { // Before + CaretContainerRemove.remove(caret.get()); + var text = CaretContainerInline.insertInlineBefore(element); + caret.set(text); + return Option.some(new CaretPosition(text, text.length - 1)); + }, + function(element) { // Start + return InlineUtils.findCaretPositionIn(element, true).map(function(pos) { + if (!isPosCaretContainer(pos, caret)) { + CaretContainerRemove.remove(caret.get()); + var text = insertInlinePos(pos, true); + caret.set(text); + return new CaretPosition(text, 1); + } else { + return new CaretPosition(caret.get(), 1); + } + }); + }, + function(element) { // End + return InlineUtils.findCaretPositionIn(element, false).map(function(pos) { + if (!isPosCaretContainer(pos, caret)) { + CaretContainerRemove.remove(caret.get()); + var text = insertInlinePos(pos, false); + caret.set(text); + return new CaretPosition(text, text.length - 1); + } else { + return new CaretPosition(caret.get(), caret.get().length - 1); + } + }); + }, + function(element) { // After + CaretContainerRemove.remove(caret.get()); + var text = CaretContainerInline.insertInlineAfter(element); + caret.set(text); + return Option.some(new CaretPosition(text, 1)); + } + ); + }; + + return { + renderCaret: renderCaret + }; + } + ); + /** + * LazyEvaluator.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.util.LazyEvaluator', [ + 'ephox.katamari.api.Option' + ], + function(Option) { + var evaluateUntil = function(fns, args) { + for (var i = 0; i < fns.length; i++) { + var result = fns[i].apply(null, args); + if (result.isSome()) { + return result; + } + } + + return Option.none(); + }; + + return { + evaluateUntil: evaluateUntil + }; + } + ); + /** + * BoundaryLocation.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.keyboard.BoundaryLocation', [ + 'ephox.katamari.api.Adt', + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Option', + 'ephox.katamari.api.Options', + 'tinymce.core.caret.CaretContainer', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.caret.CaretUtils', + 'tinymce.core.dom.NodeType', + 'tinymce.core.keyboard.InlineUtils', + 'tinymce.core.util.LazyEvaluator' + ], + function(Adt, Fun, Option, Options, CaretContainer, CaretPosition, CaretUtils, NodeType, InlineUtils, LazyEvaluator) { + var Location = Adt.generate([{ + before: ['element'] + }, + { + start: ['element'] + }, + { + end: ['element'] + }, + { + after: ['element'] + } + ]); + + var rescope = function(rootNode, node) { + var parentBlock = CaretUtils.getParentBlock(node, rootNode); + return parentBlock ? parentBlock : rootNode; + }; + + var before = function(rootNode, pos) { + var nPos = InlineUtils.normalizeForwards(pos); + var scope = rescope(rootNode, nPos.container()); + return InlineUtils.findRootInline(scope, nPos).fold( + function() { + return InlineUtils.findCaretPosition(scope, true, nPos) + .bind(Fun.curry(InlineUtils.findRootInline, scope)) + .map(function(inline) { + return Location.before(inline); + }); + }, + Option.none + ); + }; + + var start = function(rootNode, pos) { + var nPos = InlineUtils.normalizeBackwards(pos); + return InlineUtils.findRootInline(rootNode, nPos).bind(function(inline) { + var prevPos = InlineUtils.findCaretPosition(inline, false, nPos); + return prevPos.isNone() ? Option.some(Location.start(inline)) : Option.none(); + }); + }; + + var end = function(rootNode, pos) { + var nPos = InlineUtils.normalizeForwards(pos); + return InlineUtils.findRootInline(rootNode, nPos).bind(function(inline) { + var nextPos = InlineUtils.findCaretPosition(inline, true, nPos); + return nextPos.isNone() ? Option.some(Location.end(inline)) : Option.none(); + }); + }; + + var after = function(rootNode, pos) { + var nPos = InlineUtils.normalizeBackwards(pos); + var scope = rescope(rootNode, nPos.container()); + return InlineUtils.findRootInline(scope, nPos).fold( + function() { + return InlineUtils.findCaretPosition(scope, false, nPos) + .bind(Fun.curry(InlineUtils.findRootInline, scope)) + .map(function(inline) { + return Location.after(inline); + }); + }, + Option.none + ); + }; + + var isValidLocation = function(location) { + return InlineUtils.isRtl(getElement(location)) === false; + }; + + var readLocation = function(rootNode, pos) { + var location = LazyEvaluator.evaluateUntil([ + before, + start, + end, + after + ], [rootNode, pos]); + + return location.filter(isValidLocation); + }; + + var getElement = function(location) { + return location.fold( + Fun.identity, // Before + Fun.identity, // Start + Fun.identity, // End + Fun.identity // After + ); + }; + + var getName = function(location) { + return location.fold( + Fun.constant('before'), // Before + Fun.constant('start'), // Start + Fun.constant('end'), // End + Fun.constant('after') // After + ); + }; + + var outside = function(location) { + return location.fold( + Location.before, // Before + Location.before, // Start + Location.after, // End + Location.after // After + ); + }; + + var inside = function(location) { + return location.fold( + Location.start, // Before + Location.start, // Start + Location.end, // End + Location.end // After + ); + }; + + var isEq = function(location1, location2) { + return getName(location1) === getName(location2) && getElement(location1) === getElement(location2); + }; + + var betweenInlines = function(forward, rootNode, from, to, location) { + return Options.liftN([ + InlineUtils.findRootInline(rootNode, from), + InlineUtils.findRootInline(rootNode, to) + ], function(fromInline, toInline) { + if (fromInline !== toInline && InlineUtils.hasSameParentBlock(rootNode, fromInline, toInline)) { + // Force after since some browsers normalize and lean left into the closest inline + return Location.after(forward ? fromInline : toInline); + } else { + return location; + } + }).getOr(location); + }; + + var skipNoMovement = function(fromLocation, toLocation) { + return fromLocation.fold( + Fun.constant(true), + function(fromLocation) { + return !isEq(fromLocation, toLocation); + } + ); + }; + + var findLocationTraverse = function(forward, rootNode, fromLocation, pos) { + var from = InlineUtils.normalizePosition(forward, pos); + var to = InlineUtils.findCaretPosition(rootNode, forward, from).map(Fun.curry(InlineUtils.normalizePosition, forward)); + + var location = to.fold( + function() { + return fromLocation.map(outside); + }, + function(to) { + return readLocation(rootNode, to) + .map(Fun.curry(betweenInlines, forward, rootNode, from, to)) + .filter(Fun.curry(skipNoMovement, fromLocation)); + } + ); + + return location.filter(isValidLocation); + }; + + var findLocationSimple = function(forward, location) { + if (forward) { + return location.fold( + Fun.compose(Option.some, Location.start), // Before -> Start + Option.none, + Fun.compose(Option.some, Location.after), // End -> After + Option.none + ); + } else { + return location.fold( + Option.none, + Fun.compose(Option.some, Location.before), // Before <- Start + Option.none, + Fun.compose(Option.some, Location.end) // End <- After + ); + } + }; + + var findLocation = function(forward, rootNode, pos) { + var from = InlineUtils.normalizePosition(forward, pos); + var fromLocation = readLocation(rootNode, from); + + return readLocation(rootNode, from).bind(Fun.curry(findLocationSimple, forward)).orThunk(function() { + return findLocationTraverse(forward, rootNode, fromLocation, pos); + }); + }; + + return { + readLocation: readLocation, + prevLocation: Fun.curry(findLocation, false), + nextLocation: Fun.curry(findLocation, true), + getElement: getElement, + outside: outside, + inside: inside + }; + } + ); + define( + 'ephox.katamari.api.Cell', + + [], + + function() { + var Cell = function(initial) { + var value = initial; + + var get = function() { + return value; + }; + + var set = function(v) { + value = v; + }; + + var clone = function() { + return Cell(get()); + }; + + return { + get: get, + set: set, + clone: clone + }; + }; + + return Cell; + } + ); + + /** + * BoundarySelection.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.keyboard.BoundarySelection', [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Cell', + 'ephox.katamari.api.Fun', + 'tinymce.core.caret.CaretContainerRemove', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.keyboard.BoundaryCaret', + 'tinymce.core.keyboard.BoundaryLocation', + 'tinymce.core.keyboard.InlineUtils' + ], + function(Arr, Cell, Fun, CaretContainerRemove, CaretPosition, BoundaryCaret, BoundaryLocation, InlineUtils) { + var setCaretPosition = function(editor, pos) { + var rng = editor.dom.createRng(); + rng.setStart(pos.container(), pos.offset()); + rng.setEnd(pos.container(), pos.offset()); + editor.selection.setRng(rng); + }; + + var isFeatureEnabled = function(editor) { + return editor.settings.inline_boundaries !== false; + }; + + var setSelected = function(state, elm) { + if (state) { + elm.setAttribute('data-mce-selected', '1'); + } else { + elm.removeAttribute('data-mce-selected', '1'); + } + }; + + var renderCaretLocation = function(editor, caret, location) { + return BoundaryCaret.renderCaret(caret, location).map(function(pos) { + setCaretPosition(editor, pos); + return location; + }); + }; + + var findLocation = function(editor, caret, forward) { + var rootNode = editor.getBody(); + var from = CaretPosition.fromRangeStart(editor.selection.getRng()); + var location = forward ? BoundaryLocation.nextLocation(rootNode, from) : BoundaryLocation.prevLocation(rootNode, from); + return location.bind(function(location) { + return renderCaretLocation(editor, caret, location); + }); + }; + + var toggleInlines = function(dom, elms) { + var selectedInlines = dom.select('a[href][data-mce-selected],code[data-mce-selected]'); + var targetInlines = Arr.filter(elms, InlineUtils.isInlineTarget); + Arr.each(Arr.difference(selectedInlines, targetInlines), Fun.curry(setSelected, false)); + Arr.each(Arr.difference(targetInlines, selectedInlines), Fun.curry(setSelected, true)); + }; + + var safeRemoveCaretContainer = function(editor, caret) { + if (editor.selection.isCollapsed() && editor.composing !== true && caret.get()) { + var pos = CaretPosition.fromRangeStart(editor.selection.getRng()); + if (CaretPosition.isTextPosition(pos) && InlineUtils.isAtZwsp(pos) === false) { + setCaretPosition(editor, CaretContainerRemove.removeAndReposition(caret.get(), pos)); + caret.set(null); + } + } + }; + + var renderInsideInlineCaret = function(editor, caret, elms) { + if (editor.selection.isCollapsed()) { + var inlines = Arr.filter(elms, InlineUtils.isInlineTarget); + Arr.each(inlines, function(inline) { + var pos = CaretPosition.fromRangeStart(editor.selection.getRng()); + BoundaryLocation.readLocation(editor.getBody(), pos).bind(function(location) { + return renderCaretLocation(editor, caret, location); + }); + }); + } + }; + + var move = function(editor, caret, forward) { + return function() { + return isFeatureEnabled(editor) ? findLocation(editor, caret, forward).isSome() : false; + }; + }; + + var setupSelectedState = function(editor) { + var caret = new Cell(null); + + editor.on('NodeChange', function(e) { + if (isFeatureEnabled(editor)) { + toggleInlines(editor.dom, e.parents); + safeRemoveCaretContainer(editor, caret); + renderInsideInlineCaret(editor, caret, e.parents); + } + }); + + return caret; + }; + + return { + move: move, + setupSelectedState: setupSelectedState, + setCaretPosition: setCaretPosition + }; + } + ); + /** + * InlineBoundaryDelete.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.delete.InlineBoundaryDelete', [ + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Option', + 'ephox.katamari.api.Options', + 'ephox.sugar.api.node.Element', + 'tinymce.core.caret.CaretContainer', + 'tinymce.core.caret.CaretFinder', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.caret.CaretUtils', + 'tinymce.core.delete.DeleteElement', + 'tinymce.core.keyboard.BoundaryCaret', + 'tinymce.core.keyboard.BoundaryLocation', + 'tinymce.core.keyboard.BoundarySelection', + 'tinymce.core.keyboard.InlineUtils' + ], + function( + Fun, Option, Options, Element, CaretContainer, CaretFinder, CaretPosition, CaretUtils, DeleteElement, BoundaryCaret, BoundaryLocation, BoundarySelection, + InlineUtils + ) { + var isFeatureEnabled = function(editor) { + return editor.settings.inline_boundaries !== false; + }; + + var rangeFromPositions = function(from, to) { + var range = document.createRange(); + + range.setStart(from.container(), from.offset()); + range.setEnd(to.container(), to.offset()); + + return range; + }; + + // Checks for delete at <code>|a</code> when there is only one item left except the zwsp caret container nodes + var hasOnlyTwoOrLessPositionsLeft = function(elm) { + return Options.liftN([ + InlineUtils.findCaretPositionIn(elm, true), + InlineUtils.findCaretPositionIn(elm, false) + ], function(firstPos, lastPos) { + var normalizedFirstPos = InlineUtils.normalizePosition(true, firstPos); + var normalizedLastPos = InlineUtils.normalizePosition(false, lastPos); + + return InlineUtils.findCaretPosition(elm, true, normalizedFirstPos).map(function(pos) { + return pos.isEqual(normalizedLastPos); + }).getOr(true); + }).getOr(true); + }; + + var setCaretLocation = function(editor, caret) { + return function(location) { + return BoundaryCaret.renderCaret(caret, location).map(function(pos) { + BoundarySelection.setCaretPosition(editor, pos); + return true; + }).getOr(false); + }; + }; + + var deleteFromTo = function(editor, caret, from, to) { + var rootNode = editor.getBody(); + + editor.undoManager.ignore(function() { + editor.selection.setRng(rangeFromPositions(from, to)); + editor.execCommand('Delete'); + + BoundaryLocation.readLocation(rootNode, CaretPosition.fromRangeStart(editor.selection.getRng())) + .map(BoundaryLocation.inside) + .map(setCaretLocation(editor, caret)); + }); + + editor.nodeChanged(); + }; + + var rescope = function(rootNode, node) { + var parentBlock = CaretUtils.getParentBlock(node, rootNode); + return parentBlock ? parentBlock : rootNode; + }; + + var backspaceDeleteCollapsed = function(editor, caret, forward, from) { + var rootNode = rescope(editor.getBody(), from.container()); + var fromLocation = BoundaryLocation.readLocation(rootNode, from); + + return fromLocation.bind(function(location) { + if (forward) { + return location.fold( + Fun.constant(Option.some(BoundaryLocation.inside(location))), // Before + Option.none, // Start + Fun.constant(Option.some(BoundaryLocation.outside(location))), // End + Option.none // After + ); + } else { + return location.fold( + Option.none, // Before + Fun.constant(Option.some(BoundaryLocation.outside(location))), // Start + Option.none, // End + Fun.constant(Option.some(BoundaryLocation.inside(location))) // After + ); + } + }) + .map(setCaretLocation(editor, caret)) + .getOrThunk(function() { + var toPosition = CaretFinder.navigate(forward, rootNode, from); + var toLocation = toPosition.bind(function(pos) { + return BoundaryLocation.readLocation(rootNode, pos); + }); + + if (fromLocation.isSome() && toLocation.isSome()) { + return InlineUtils.findRootInline(rootNode, from).map(function(elm) { + if (hasOnlyTwoOrLessPositionsLeft(elm)) { + DeleteElement.deleteElement(editor, forward, Element.fromDom(elm)); + return true; + } else { + return false; + } + }).getOr(false); + } else { + return toLocation.bind(function(_) { + return toPosition.map(function(to) { + if (forward) { + deleteFromTo(editor, caret, from, to); + } else { + deleteFromTo(editor, caret, to, from); + } + + return true; + }); + }).getOr(false); + } + }); + }; + + var backspaceDelete = function(editor, caret, forward) { + if (editor.selection.isCollapsed() && isFeatureEnabled(editor)) { + var from = CaretPosition.fromRangeStart(editor.selection.getRng()); + return backspaceDeleteCollapsed(editor, caret, forward, from); + } + + return false; + }; + + return { + backspaceDelete: backspaceDelete + }; + } + ); + /** + * Commands.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.delete.DeleteCommands', [ + 'tinymce.core.delete.BlockBoundaryDelete', + 'tinymce.core.delete.BlockRangeDelete', + 'tinymce.core.delete.CefDelete', + 'tinymce.core.delete.InlineBoundaryDelete' + ], + function(BlockBoundaryDelete, BlockRangeDelete, CefDelete, BoundaryDelete) { + var nativeCommand = function(editor, command) { + editor.getDoc().execCommand(command, false, null); + }; + + var paddEmptyBody = function(editor) { + var dom = editor.dom; + + // Check if body is empty after the delete call if so then set the contents + // to an empty string and move the caret to any block produced by that operation + // this fixes the issue with root blocks not being properly produced after a delete call on IE + var body = editor.getBody(); + + if (dom.isEmpty(body)) { + editor.setContent(''); + + if (body.firstChild && dom.isBlock(body.firstChild)) { + editor.selection.setCursorLocation(body.firstChild, 0); + } else { + editor.selection.setCursorLocation(body, 0); + } + } + }; + + var deleteCommand = function(editor) { + if (CefDelete.backspaceDelete(editor, false)) { + return; + } else if (BoundaryDelete.backspaceDelete(editor, false)) { + return; + } else if (BlockBoundaryDelete.backspaceDelete(editor, false)) { + return; + } else if (BlockRangeDelete.backspaceDelete(editor, false)) { + return; + } else { + nativeCommand(editor, 'Delete'); + paddEmptyBody(editor); + } + }; + + var forwardDeleteCommand = function(editor) { + if (CefDelete.backspaceDelete(editor, true)) { + return; + } else if (BoundaryDelete.backspaceDelete(editor, true)) { + return; + } else if (BlockBoundaryDelete.backspaceDelete(editor, true)) { + return; + } else if (BlockRangeDelete.backspaceDelete(editor, true)) { + return; + } else { + nativeCommand(editor, 'ForwardDelete'); + } + }; + + return { + deleteCommand: deleteCommand, + forwardDeleteCommand: forwardDeleteCommand + }; + } + ); + /** + * RangeNormalizer.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.dom.RangeNormalizer', [ + 'tinymce.core.caret.CaretFinder', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.caret.CaretUtils', + 'tinymce.core.dom.NodeType' + ], + function(CaretFinder, CaretPosition, CaretUtils, NodeType) { + var isTextBlock = function(elm) { + return NodeType.isElement(elm) && /^(P|H[1-6]|DIV)$/.test(elm.nodeName); + }; + + var matchEndContainer = function(rng, predicate) { + return predicate(rng.endContainer); + }; + + var createRange = function(sc, so, ec, eo) { + var rng = document.createRange(); + rng.setStart(sc, so); + rng.setEnd(ec, eo); + return rng; + }; + + // If you tripple click a paragraph in this case: + // <blockquote><p>a</p></blockquote><p>b</p> + // It would become this range in webkit: + // <blockquote><p>[a</p></blockquote><p>]b</p> + // We would want it to be: + // <blockquote><p>[a]</p></blockquote><p>b</p> + // Since it would otherwise produces spans out of thin air on insertContent for example. + var normalizeBlockSelection = function(rng) { + var startPos = CaretPosition.fromRangeStart(rng); + var endPos = CaretPosition.fromRangeEnd(rng); + var rootNode = rng.commonAncestorContainer; + + if (rng.collapsed === false && matchEndContainer(rng, isTextBlock) && rng.endOffset === 0) { + return CaretFinder.fromPosition(false, rootNode, endPos) + .map(function(newEndPos) { + if (!CaretUtils.isInSameBlock(startPos, endPos, rootNode) && CaretUtils.isInSameBlock(startPos, newEndPos, rootNode)) { + return createRange(startPos.container(), startPos.offset(), newEndPos.container(), newEndPos.offset()); + } else { + return rng; + } + }).getOr(rng); + } else { + return rng; + } + }; + + var normalize = function(rng) { + return normalizeBlockSelection(rng); + }; + + return { + normalize: normalize + }; + } + ); + /** + * InsertList.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Handles inserts of lists into the editor instance. + * + * @class tinymce.InsertList + * @private + */ + define( + 'tinymce.core.InsertList', [ + "tinymce.core.util.Tools", + "tinymce.core.caret.CaretWalker", + "tinymce.core.caret.CaretPosition" + ], + function(Tools, CaretWalker, CaretPosition) { + var isListFragment = function(fragment) { + var firstChild = fragment.firstChild; + var lastChild = fragment.lastChild; + + // Skip meta since it's likely <meta><ul>..</ul> + if (firstChild && firstChild.name === 'meta') { + firstChild = firstChild.next; + } + + // Skip mce_marker since it's likely <ul>..</ul><span id="mce_marker"></span> + if (lastChild && lastChild.attr('id') === 'mce_marker') { + lastChild = lastChild.prev; + } + + if (!firstChild || firstChild !== lastChild) { + return false; + } + + return firstChild.name === 'ul' || firstChild.name === 'ol'; + }; + + var cleanupDomFragment = function(domFragment) { + var firstChild = domFragment.firstChild; + var lastChild = domFragment.lastChild; + + // TODO: remove the meta tag from paste logic + if (firstChild && firstChild.nodeName === 'META') { + firstChild.parentNode.removeChild(firstChild); + } + + if (lastChild && lastChild.id === 'mce_marker') { + lastChild.parentNode.removeChild(lastChild); + } + + return domFragment; + }; + + var toDomFragment = function(dom, serializer, fragment) { + var html = serializer.serialize(fragment); + var domFragment = dom.createFragment(html); + + return cleanupDomFragment(domFragment); + }; + + var listItems = function(elm) { + return Tools.grep(elm.childNodes, function(child) { + return child.nodeName === 'LI'; + }); + }; + + var isEmpty = function(elm) { + return !elm.firstChild; + }; + + var trimListItems = function(elms) { + return elms.length > 0 && isEmpty(elms[elms.length - 1]) ? elms.slice(0, -1) : elms; + }; + + var getParentLi = function(dom, node) { + var parentBlock = dom.getParent(node, dom.isBlock); + return parentBlock && parentBlock.nodeName === 'LI' ? parentBlock : null; + }; + + var isParentBlockLi = function(dom, node) { + return !!getParentLi(dom, node); + }; + + var getSplit = function(parentNode, rng) { + var beforeRng = rng.cloneRange(); + var afterRng = rng.cloneRange(); + + beforeRng.setStartBefore(parentNode); + afterRng.setEndAfter(parentNode); + + return [ + beforeRng.cloneContents(), + afterRng.cloneContents() + ]; + }; + + var findFirstIn = function(node, rootNode) { + var caretPos = CaretPosition.before(node); + var caretWalker = new CaretWalker(rootNode); + var newCaretPos = caretWalker.next(caretPos); + + return newCaretPos ? newCaretPos.toRange() : null; + }; + + var findLastOf = function(node, rootNode) { + var caretPos = CaretPosition.after(node); + var caretWalker = new CaretWalker(rootNode); + var newCaretPos = caretWalker.prev(caretPos); + + return newCaretPos ? newCaretPos.toRange() : null; + }; + + var insertMiddle = function(target, elms, rootNode, rng) { + var parts = getSplit(target, rng); + var parentElm = target.parentNode; + + parentElm.insertBefore(parts[0], target); + Tools.each(elms, function(li) { + parentElm.insertBefore(li, target); + }); + parentElm.insertBefore(parts[1], target); + parentElm.removeChild(target); + + return findLastOf(elms[elms.length - 1], rootNode); + }; + + var insertBefore = function(target, elms, rootNode) { + var parentElm = target.parentNode; + + Tools.each(elms, function(elm) { + parentElm.insertBefore(elm, target); + }); + + return findFirstIn(target, rootNode); + }; + + var insertAfter = function(target, elms, rootNode, dom) { + dom.insertAfter(elms.reverse(), target); + return findLastOf(elms[0], rootNode); + }; + + var insertAtCaret = function(serializer, dom, rng, fragment) { + var domFragment = toDomFragment(dom, serializer, fragment); + var liTarget = getParentLi(dom, rng.startContainer); + var liElms = trimListItems(listItems(domFragment.firstChild)); + var BEGINNING = 1, + END = 2; + var rootNode = dom.getRoot(); + + var isAt = function(location) { + var caretPos = CaretPosition.fromRangeStart(rng); + var caretWalker = new CaretWalker(dom.getRoot()); + var newPos = location === BEGINNING ? caretWalker.prev(caretPos) : caretWalker.next(caretPos); + + return newPos ? getParentLi(dom, newPos.getNode()) !== liTarget : true; + }; + + if (isAt(BEGINNING)) { + return insertBefore(liTarget, liElms, rootNode); + } else if (isAt(END)) { + return insertAfter(liTarget, liElms, rootNode, dom); + } + + return insertMiddle(liTarget, liElms, rootNode, rng); + }; + + return { + isListFragment: isListFragment, + insertAtCaret: insertAtCaret, + isParentBlockLi: isParentBlockLi, + trimListItems: trimListItems, + listItems: listItems + }; + } + ); + /** + * InsertContent.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Handles inserts of contents into the editor instance. + * + * @class tinymce.InsertContent + * @private + */ + define( + 'tinymce.core.InsertContent', [ + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.caret.CaretWalker', + 'tinymce.core.dom.ElementUtils', + 'tinymce.core.dom.NodeType', + 'tinymce.core.dom.RangeNormalizer', + 'tinymce.core.Env', + 'tinymce.core.html.Serializer', + 'tinymce.core.InsertList', + 'tinymce.core.util.Tools' + ], + function(CaretPosition, CaretWalker, ElementUtils, NodeType, RangeNormalizer, Env, Serializer, InsertList, Tools) { + var isTableCell = NodeType.matchNodeNames('td th'); + + var validInsertion = function(editor, value, parentNode) { + // Should never insert content into bogus elements, since these can + // be resize handles or similar + if (parentNode.getAttribute('data-mce-bogus') === 'all') { + parentNode.parentNode.insertBefore(editor.dom.createFragment(value), parentNode); + } else { + // Check if parent is empty or only has one BR element then set the innerHTML of that parent + var node = parentNode.firstChild; + var node2 = parentNode.lastChild; + if (!node || (node === node2 && node.nodeName === 'BR')) { /// + editor.dom.setHTML(parentNode, value); + } else { + editor.selection.setContent(value); + } + } + }; + + var insertHtmlAtCaret = function(editor, value, details) { + var parser, serializer, parentNode, rootNode, fragment, args; + var marker, rng, node, node2, bookmarkHtml, merge; + var textInlineElements = editor.schema.getTextInlineElements(); + var selection = editor.selection, + dom = editor.dom; + + function trimOrPaddLeftRight(html) { + var rng, container, offset; + + rng = selection.getRng(true); + container = rng.startContainer; + offset = rng.startOffset; + + function hasSiblingText(siblingName) { + return container[siblingName] && container[siblingName].nodeType == 3; + } + + if (container.nodeType == 3) { + if (offset > 0) { + html = html.replace(/^ /, ' '); + } else if (!hasSiblingText('previousSibling')) { + html = html.replace(/^ /, ' '); + } + + if (offset < container.length) { + html = html.replace(/ (<br>|)$/, ' '); + } else if (!hasSiblingText('nextSibling')) { + html = html.replace(/( | )(<br>|)$/, ' '); + } + } + + return html; + } + + // Removes   from a [b] c -> a  c -> a c + function trimNbspAfterDeleteAndPaddValue() { + var rng, container, offset; + + rng = selection.getRng(true); + container = rng.startContainer; + offset = rng.startOffset; + + if (container.nodeType == 3 && rng.collapsed) { + if (container.data[offset] === '\u00a0') { + container.deleteData(offset, 1); + + if (!/[\u00a0| ]$/.test(value)) { + value += ' '; + } + } else if (container.data[offset - 1] === '\u00a0') { + container.deleteData(offset - 1, 1); + + if (!/[\u00a0| ]$/.test(value)) { + value = ' ' + value; + } + } + } + } + + function reduceInlineTextElements() { + if (merge) { + var root = editor.getBody(), + elementUtils = new ElementUtils(dom); + + Tools.each(dom.select('*[data-mce-fragment]'), function(node) { + for (var testNode = node.parentNode; testNode && testNode != root; testNode = testNode.parentNode) { + if (textInlineElements[node.nodeName.toLowerCase()] && elementUtils.compare(testNode, node)) { + dom.remove(node, true); + } + } + }); + } + } + + function markFragmentElements(fragment) { + var node = fragment; + + while ((node = node.walk())) { + if (node.type === 1) { + node.attr('data-mce-fragment', '1'); + } + } + } + + function umarkFragmentElements(elm) { + Tools.each(elm.getElementsByTagName('*'), function(elm) { + elm.removeAttribute('data-mce-fragment'); + }); + } + + function isPartOfFragment(node) { + return !!node.getAttribute('data-mce-fragment'); + } + + function canHaveChildren(node) { + return node && !editor.schema.getShortEndedElements()[node.nodeName]; + } + + function moveSelectionToMarker(marker) { + var parentEditableFalseElm, parentBlock, nextRng; + + function getContentEditableFalseParent(node) { + var root = editor.getBody(); + + for (; node && node !== root; node = node.parentNode) { + if (editor.dom.getContentEditable(node) === 'false') { + return node; + } + } + + return null; + } + + if (!marker) { + return; + } + + selection.scrollIntoView(marker); + + // If marker is in cE=false then move selection to that element instead + parentEditableFalseElm = getContentEditableFalseParent(marker); + if (parentEditableFalseElm) { + dom.remove(marker); + selection.select(parentEditableFalseElm); + return; + } + + // Move selection before marker and remove it + rng = dom.createRng(); + + // If previous sibling is a text node set the selection to the end of that node + node = marker.previousSibling; + if (node && node.nodeType == 3) { + rng.setStart(node, node.nodeValue.length); + + // TODO: Why can't we normalize on IE + if (!Env.ie) { + node2 = marker.nextSibling; + if (node2 && node2.nodeType == 3) { + node.appendData(node2.data); + node2.parentNode.removeChild(node2); + } + } + } else { + // If the previous sibling isn't a text node or doesn't exist set the selection before the marker node + rng.setStartBefore(marker); + rng.setEndBefore(marker); + } + + function findNextCaretRng(rng) { + var caretPos = CaretPosition.fromRangeStart(rng); + var caretWalker = new CaretWalker(editor.getBody()); + + caretPos = caretWalker.next(caretPos); + if (caretPos) { + return caretPos.toRange(); + } + } + + // Remove the marker node and set the new range + parentBlock = dom.getParent(marker, dom.isBlock); + dom.remove(marker); + + if (parentBlock && dom.isEmpty(parentBlock)) { + editor.$(parentBlock).empty(); + + rng.setStart(parentBlock, 0); + rng.setEnd(parentBlock, 0); + + if (!isTableCell(parentBlock) && !isPartOfFragment(parentBlock) && (nextRng = findNextCaretRng(rng))) { + rng = nextRng; + dom.remove(parentBlock); + } else { + dom.add(parentBlock, dom.create('br', { + 'data-mce-bogus': '1' + })); + } + } + + selection.setRng(rng); + } + + // Check for whitespace before/after value + if (/^ | $/.test(value)) { + value = trimOrPaddLeftRight(value); + } + + // Setup parser and serializer + parser = editor.parser; + merge = details.merge; + + serializer = new Serializer({ + validate: editor.settings.validate + }, editor.schema); + bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">​</span>'; + + // Run beforeSetContent handlers on the HTML to be inserted + args = { + content: value, + format: 'html', + selection: true + }; + editor.fire('BeforeSetContent', args); + value = args.content; + + // Add caret at end of contents if it's missing + if (value.indexOf('{$caret}') == -1) { + value += '{$caret}'; + } + + // Replace the caret marker with a span bookmark element + value = value.replace(/\{\$caret\}/, bookmarkHtml); + + // If selection is at <body>|<p></p> then move it into <body><p>|</p> + rng = selection.getRng(); + var caretElement = rng.startContainer || (rng.parentElement ? rng.parentElement() : null); + var body = editor.getBody(); + if (caretElement === body && selection.isCollapsed()) { + if (dom.isBlock(body.firstChild) && canHaveChildren(body.firstChild) && dom.isEmpty(body.firstChild)) { + rng = dom.createRng(); + rng.setStart(body.firstChild, 0); + rng.setEnd(body.firstChild, 0); + selection.setRng(rng); + } + } + + // Insert node maker where we will insert the new HTML and get it's parent + if (!selection.isCollapsed()) { + // Fix for #2595 seems that delete removes one extra character on + // WebKit for some odd reason if you double click select a word + editor.selection.setRng(RangeNormalizer.normalize(editor.selection.getRng())); + editor.getDoc().execCommand('Delete', false, null); + trimNbspAfterDeleteAndPaddValue(); + } + + parentNode = selection.getNode(); + + // Parse the fragment within the context of the parent node + var parserArgs = { + context: parentNode.nodeName.toLowerCase(), + data: details.data + }; + fragment = parser.parse(value, parserArgs); + + // Custom handling of lists + if (details.paste === true && InsertList.isListFragment(fragment) && InsertList.isParentBlockLi(dom, parentNode)) { + rng = InsertList.insertAtCaret(serializer, dom, editor.selection.getRng(true), fragment); + editor.selection.setRng(rng); + editor.fire('SetContent', args); + return; + } + + markFragmentElements(fragment); + + // Move the caret to a more suitable location + node = fragment.lastChild; + if (node.attr('id') == 'mce_marker') { + marker = node; + + for (node = node.prev; node; node = node.walk(true)) { + if (node.type == 3 || !dom.isBlock(node.name)) { + if (editor.schema.isValidChild(node.parent.name, 'span')) { + node.parent.insert(marker, node, node.name === 'br'); + } + break; + } + } + } + + editor._selectionOverrides.showBlockCaretContainer(parentNode); + + // If parser says valid we can insert the contents into that parent + if (!parserArgs.invalid) { + value = serializer.serialize(fragment); + validInsertion(editor, value, parentNode); + } else { + // If the fragment was invalid within that context then we need + // to parse and process the parent it's inserted into + + // Insert bookmark node and get the parent + selection.setContent(bookmarkHtml); + parentNode = selection.getNode(); + rootNode = editor.getBody(); + + // Opera will return the document node when selection is in root + if (parentNode.nodeType == 9) { + parentNode = node = rootNode; + } else { + node = parentNode; + } + + // Find the ancestor just before the root element + while (node !== rootNode) { + parentNode = node; + node = node.parentNode; + } + + // Get the outer/inner HTML depending on if we are in the root and parser and serialize that + value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode); + value = serializer.serialize( + parser.parse( + // Need to replace by using a function since $ in the contents would otherwise be a problem + value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() { + return serializer.serialize(fragment); + }) + ) + ); + + // Set the inner/outer HTML depending on if we are in the root or not + if (parentNode == rootNode) { + dom.setHTML(rootNode, value); + } else { + dom.setOuterHTML(parentNode, value); + } + } + + reduceInlineTextElements(); + moveSelectionToMarker(dom.get('mce_marker')); + umarkFragmentElements(editor.getBody()); + editor.fire('SetContent', args); + editor.addVisual(); + }; + + var processValue = function(value) { + var details; + + if (typeof value !== 'string') { + details = Tools.extend({ + paste: value.paste, + data: { + paste: value.paste + } + }, value); + + return { + content: value.content, + details: details + }; + } + + return { + content: value, + details: {} + }; + }; + + var insertAtCaret = function(editor, value) { + var result = processValue(value); + insertHtmlAtCaret(editor, result.content, result.details); + }; + + return { + insertAtCaret: insertAtCaret + }; + } + ); + /** + * EditorCommands.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class enables you to add custom editor commands and it contains + * overrides for native browser commands to address various bugs and issues. + * + * @class tinymce.EditorCommands + */ + define( + 'tinymce.core.EditorCommands', [ + 'tinymce.core.delete.DeleteCommands', + 'tinymce.core.dom.NodeType', + 'tinymce.core.dom.RangeUtils', + 'tinymce.core.dom.TreeWalker', + 'tinymce.core.Env', + 'tinymce.core.InsertContent', + 'tinymce.core.util.Tools' + ], + function(DeleteCommands, NodeType, RangeUtils, TreeWalker, Env, InsertContent, Tools) { + // Added for compression purposes + var each = Tools.each, + extend = Tools.extend; + var map = Tools.map, + inArray = Tools.inArray, + explode = Tools.explode; + var isOldIE = Env.ie && Env.ie < 11; + var TRUE = true, + FALSE = false; + + return function(editor) { + var dom, selection, formatter, + commands = { + state: {}, + exec: {}, + value: {} + }, + settings = editor.settings, + bookmark; + + editor.on('PreInit', function() { + dom = editor.dom; + selection = editor.selection; + settings = editor.settings; + formatter = editor.formatter; + }); + + /** + * Executes the specified command. + * + * @method execCommand + * @param {String} command Command to execute. + * @param {Boolean} ui Optional user interface state. + * @param {Object} value Optional value for command. + * @param {Object} args Optional extra arguments to the execCommand. + * @return {Boolean} true/false if the command was found or not. + */ + function execCommand(command, ui, value, args) { + var func, customCommand, state = 0; + + if (editor.removed) { + return; + } + + if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(command) && (!args || !args.skip_focus)) { + editor.focus(); + } + + args = editor.fire('BeforeExecCommand', { + command: command, + ui: ui, + value: value + }); + if (args.isDefaultPrevented()) { + return false; + } + + customCommand = command.toLowerCase(); + if ((func = commands.exec[customCommand])) { + func(customCommand, ui, value); + editor.fire('ExecCommand', { + command: command, + ui: ui, + value: value + }); + return true; + } + + // Plugin commands + each(editor.plugins, function(p) { + if (p.execCommand && p.execCommand(command, ui, value)) { + editor.fire('ExecCommand', { + command: command, + ui: ui, + value: value + }); + state = true; + return false; + } + }); + + if (state) { + return state; + } + + // Theme commands + if (editor.theme && editor.theme.execCommand && editor.theme.execCommand(command, ui, value)) { + editor.fire('ExecCommand', { + command: command, + ui: ui, + value: value + }); + return true; + } + + // Browser commands + try { + state = editor.getDoc().execCommand(command, ui, value); + } catch (ex) { + // Ignore old IE errors + } + + if (state) { + editor.fire('ExecCommand', { + command: command, + ui: ui, + value: value + }); + return true; + } + + return false; + } + + /** + * Queries the current state for a command for example if the current selection is "bold". + * + * @method queryCommandState + * @param {String} command Command to check the state of. + * @return {Boolean/Number} true/false if the selected contents is bold or not, -1 if it's not found. + */ + function queryCommandState(command) { + var func; + + if (editor.quirks.isHidden() || editor.removed) { + return; + } + + command = command.toLowerCase(); + if ((func = commands.state[command])) { + return func(command); + } + + // Browser commands + try { + return editor.getDoc().queryCommandState(command); + } catch (ex) { + // Fails sometimes see bug: 1896577 + } + + return false; + } + + /** + * Queries the command value for example the current fontsize. + * + * @method queryCommandValue + * @param {String} command Command to check the value of. + * @return {Object} Command value of false if it's not found. + */ + function queryCommandValue(command) { + var func; + + if (editor.quirks.isHidden() || editor.removed) { + return; + } + + command = command.toLowerCase(); + if ((func = commands.value[command])) { + return func(command); + } + + // Browser commands + try { + return editor.getDoc().queryCommandValue(command); + } catch (ex) { + // Fails sometimes see bug: 1896577 + } + } + + /** + * Adds commands to the command collection. + * + * @method addCommands + * @param {Object} commandList Name/value collection with commands to add, the names can also be comma separated. + * @param {String} type Optional type to add, defaults to exec. Can be value or state as well. + */ + function addCommands(commandList, type) { + type = type || 'exec'; + + each(commandList, function(callback, command) { + each(command.toLowerCase().split(','), function(command) { + commands[type][command] = callback; + }); + }); + } + + function addCommand(command, callback, scope) { + command = command.toLowerCase(); + commands.exec[command] = function(command, ui, value, args) { + return callback.call(scope || editor, ui, value, args); + }; + } + + /** + * Returns true/false if the command is supported or not. + * + * @method queryCommandSupported + * @param {String} command Command that we check support for. + * @return {Boolean} true/false if the command is supported or not. + */ + function queryCommandSupported(command) { + command = command.toLowerCase(); + + if (commands.exec[command]) { + return true; + } + + // Browser commands + try { + return editor.getDoc().queryCommandSupported(command); + } catch (ex) { + // Fails sometimes see bug: 1896577 + } + + return false; + } + + function addQueryStateHandler(command, callback, scope) { + command = command.toLowerCase(); + commands.state[command] = function() { + return callback.call(scope || editor); + }; + } + + function addQueryValueHandler(command, callback, scope) { + command = command.toLowerCase(); + commands.value[command] = function() { + return callback.call(scope || editor); + }; + } + + function hasCustomCommand(command) { + command = command.toLowerCase(); + return !!commands.exec[command]; + } + + // Expose public methods + extend(this, { + execCommand: execCommand, + queryCommandState: queryCommandState, + queryCommandValue: queryCommandValue, + queryCommandSupported: queryCommandSupported, + addCommands: addCommands, + addCommand: addCommand, + addQueryStateHandler: addQueryStateHandler, + addQueryValueHandler: addQueryValueHandler, + hasCustomCommand: hasCustomCommand + }); + + // Private methods + + function execNativeCommand(command, ui, value) { + if (ui === undefined) { + ui = FALSE; + } + + if (value === undefined) { + value = null; + } + + return editor.getDoc().execCommand(command, ui, value); + } + + function isFormatMatch(name) { + return formatter.match(name); + } + + function toggleFormat(name, value) { + formatter.toggle(name, value ? { + value: value + } : undefined); + editor.nodeChanged(); + } + + function storeSelection(type) { + bookmark = selection.getBookmark(type); + } + + function restoreSelection() { + selection.moveToBookmark(bookmark); + } + + // Add execCommand overrides + addCommands({ + // Ignore these, added for compatibility + 'mceResetDesignMode,mceBeginUndoLevel': function() {}, + + // Add undo manager logic + 'mceEndUndoLevel,mceAddUndoLevel': function() { + editor.undoManager.add(); + }, + + 'Cut,Copy,Paste': function(command) { + var doc = editor.getDoc(), + failed; + + // Try executing the native command + try { + execNativeCommand(command); + } catch (ex) { + // Command failed + failed = TRUE; + } + + // Chrome reports the paste command as supported however older IE:s will return false for cut/paste + if (command === 'paste' && !doc.queryCommandEnabled(command)) { + failed = true; + } + + // Present alert message about clipboard access not being available + if (failed || !doc.queryCommandSupported(command)) { + var msg = editor.translate( + "Your browser doesn't support direct access to the clipboard. " + + "Please use the Ctrl+X/C/V keyboard shortcuts instead." + ); + + if (Env.mac) { + msg = msg.replace(/Ctrl\+/g, '\u2318+'); + } + + editor.notificationManager.open({ + text: msg, + type: 'error' + }); + } + }, + + // Override unlink command + unlink: function() { + if (selection.isCollapsed()) { + var elm = editor.dom.getParent(editor.selection.getStart(), 'a'); + if (elm) { + editor.dom.remove(elm, true); + } + + return; + } + + formatter.remove("link"); + }, + + // Override justify commands to use the text formatter engine + 'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull,JustifyNone': function(command) { + var align = command.substring(7); + + if (align == 'full') { + align = 'justify'; + } + + // Remove all other alignments first + each('left,center,right,justify'.split(','), function(name) { + if (align != name) { + formatter.remove('align' + name); + } + }); + + if (align != 'none') { + toggleFormat('align' + align); + } + }, + + // Override list commands to fix WebKit bug + 'InsertUnorderedList,InsertOrderedList': function(command) { + var listElm, listParent; + + execNativeCommand(command); + + // WebKit produces lists within block elements so we need to split them + // we will replace the native list creation logic to custom logic later on + // TODO: Remove this when the list creation logic is removed + listElm = dom.getParent(selection.getNode(), 'ol,ul'); + if (listElm) { + listParent = listElm.parentNode; + + // If list is within a text block then split that block + if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) { + storeSelection(); + dom.split(listParent, listElm); + restoreSelection(); + } + } + }, + + // Override commands to use the text formatter engine + 'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) { + toggleFormat(command); + }, + + // Override commands to use the text formatter engine + 'ForeColor,HiliteColor,FontName': function(command, ui, value) { + toggleFormat(command, value); + }, + + FontSize: function(command, ui, value) { + var fontClasses, fontSizes; + + // Convert font size 1-7 to styles + if (value >= 1 && value <= 7) { + fontSizes = explode(settings.font_size_style_values); + fontClasses = explode(settings.font_size_classes); + + if (fontClasses) { + value = fontClasses[value - 1] || value; + } else { + value = fontSizes[value - 1] || value; + } + } + + toggleFormat(command, value); + }, + + RemoveFormat: function(command) { + formatter.remove(command); + }, + + mceBlockQuote: function() { + toggleFormat('blockquote'); + }, + + FormatBlock: function(command, ui, value) { + return toggleFormat(value || 'p'); + }, + + mceCleanup: function() { + var bookmark = selection.getBookmark(); + + editor.setContent(editor.getContent({ + cleanup: TRUE + }), { + cleanup: TRUE + }); + + selection.moveToBookmark(bookmark); + }, + + mceRemoveNode: function(command, ui, value) { + var node = value || selection.getNode(); + + // Make sure that the body node isn't removed + if (node != editor.getBody()) { + storeSelection(); + editor.dom.remove(node, TRUE); + restoreSelection(); + } + }, + + mceSelectNodeDepth: function(command, ui, value) { + var counter = 0; + + dom.getParent(selection.getNode(), function(node) { + if (node.nodeType == 1 && counter++ == value) { + selection.select(node); + return FALSE; + } + }, editor.getBody()); + }, + + mceSelectNode: function(command, ui, value) { + selection.select(value); + }, + + mceInsertContent: function(command, ui, value) { + InsertContent.insertAtCaret(editor, value); + }, + + mceInsertRawHTML: function(command, ui, value) { + selection.setContent('tiny_mce_marker'); + editor.setContent( + editor.getContent().replace(/tiny_mce_marker/g, function() { + return value; + }) + ); + }, + + mceToggleFormat: function(command, ui, value) { + toggleFormat(value); + }, + + mceSetContent: function(command, ui, value) { + editor.setContent(value); + }, + + 'Indent,Outdent': function(command) { + var intentValue, indentUnit, value; + + // Setup indent level + intentValue = settings.indentation; + indentUnit = /[a-z%]+$/i.exec(intentValue); + intentValue = parseInt(intentValue, 10); + + if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) { + // If forced_root_blocks is set to false we don't have a block to indent so lets create a div + if (!settings.forced_root_block && !dom.getParent(selection.getNode(), dom.isBlock)) { + formatter.apply('div'); + } + + each(selection.getSelectedBlocks(), function(element) { + if (dom.getContentEditable(element) === "false") { + return; + } + + if (element.nodeName !== "LI") { + var indentStyleName = editor.getParam('indent_use_margin', false) ? 'margin' : 'padding'; + indentStyleName = element.nodeName === 'TABLE' ? 'margin' : indentStyleName; + indentStyleName += dom.getStyle(element, 'direction', true) == 'rtl' ? 'Right' : 'Left'; + + if (command == 'outdent') { + value = Math.max(0, parseInt(element.style[indentStyleName] || 0, 10) - intentValue); + dom.setStyle(element, indentStyleName, value ? value + indentUnit : ''); + } else { + value = (parseInt(element.style[indentStyleName] || 0, 10) + intentValue) + indentUnit; + dom.setStyle(element, indentStyleName, value); + } + } + }); + } else { + execNativeCommand(command); + } + }, + + mceRepaint: function() {}, + + InsertHorizontalRule: function() { + editor.execCommand('mceInsertContent', false, '<hr />'); + }, + + mceToggleVisualAid: function() { + editor.hasVisual = !editor.hasVisual; + editor.addVisual(); + }, + + mceReplaceContent: function(command, ui, value) { + editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({ + format: 'text' + }))); + }, + + mceInsertLink: function(command, ui, value) { + var anchor; + + if (typeof value == 'string') { + value = { + href: value + }; + } + + anchor = dom.getParent(selection.getNode(), 'a'); + + // Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here. + value.href = value.href.replace(' ', '%20'); + + // Remove existing links if there could be child links or that the href isn't specified + if (!anchor || !value.href) { + formatter.remove('link'); + } + + // Apply new link to selection + if (value.href) { + formatter.apply('link', value, anchor); + } + }, + + selectAll: function() { + var root = dom.getRoot(), + rng; + + if (selection.getRng().setStart) { + var editingHost = dom.getParent(selection.getStart(), NodeType.isContentEditableTrue); + if (editingHost) { + rng = dom.createRng(); + rng.selectNodeContents(editingHost); + selection.setRng(rng); + } + } else { + // IE will render it's own root level block elements and sometimes + // even put font elements in them when the user starts typing. So we need to + // move the selection to a more suitable element from this: + // <body>|<p></p></body> to this: <body><p>|</p></body> + rng = selection.getRng(); + if (!rng.item) { + rng.moveToElementText(root); + rng.select(); + } + } + }, + + "delete": function() { + DeleteCommands.deleteCommand(editor); + }, + + "forwardDelete": function() { + DeleteCommands.forwardDeleteCommand(editor); + }, + + mceNewDocument: function() { + editor.setContent(''); + }, + + InsertLineBreak: function(command, ui, value) { + // We load the current event in from EnterKey.js when appropriate to heed + // certain event-specific variations such as ctrl-enter in a list + var evt = value; + var brElm, extraBr, marker; + var rng = selection.getRng(true); + new RangeUtils(dom).normalize(rng); + + var offset = rng.startOffset; + var container = rng.startContainer; + + // Resolve node index + if (container.nodeType == 1 && container.hasChildNodes()) { + var isAfterLastNodeInContainer = offset > container.childNodes.length - 1; + + container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container; + if (isAfterLastNodeInContainer && container.nodeType == 3) { + offset = container.nodeValue.length; + } else { + offset = 0; + } + } + + var parentBlock = dom.getParent(container, dom.isBlock); + var parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5 + var containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null; + var containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5 + + // Enter inside block contained within a LI then split or insert before/after LI + var isControlKey = evt && evt.ctrlKey; + if (containerBlockName == 'LI' && !isControlKey) { + parentBlock = containerBlock; + parentBlockName = containerBlockName; + } + + // Walks the parent block to the right and look for BR elements + function hasRightSideContent() { + var walker = new TreeWalker(container, parentBlock), + node; + var nonEmptyElementsMap = editor.schema.getNonEmptyElements(); + + while ((node = walker.next())) { + if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) { + return true; + } + } + } + + if (container && container.nodeType == 3 && offset >= container.nodeValue.length) { + // Insert extra BR element at the end block elements + if (!isOldIE && !hasRightSideContent()) { + brElm = dom.create('br'); + rng.insertNode(brElm); + rng.setStartAfter(brElm); + rng.setEndAfter(brElm); + extraBr = true; + } + } + + brElm = dom.create('br'); + rng.insertNode(brElm); + + // Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it + var documentMode = dom.doc.documentMode; + if (isOldIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) { + brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm); + } + + // Insert temp marker and scroll to that + marker = dom.create('span', {}, ' '); + brElm.parentNode.insertBefore(marker, brElm); + selection.scrollIntoView(marker); + dom.remove(marker); + + if (!extraBr) { + rng.setStartAfter(brElm); + rng.setEndAfter(brElm); + } else { + rng.setStartBefore(brElm); + rng.setEndBefore(brElm); + } + + selection.setRng(rng); + editor.undoManager.add(); + + return TRUE; + } + }); + + // Add queryCommandState overrides + addCommands({ + // Override justify commands + 'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) { + var name = 'align' + command.substring(7); + var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks(); + var matches = map(nodes, function(node) { + return !!formatter.matchNode(node, name); + }); + return inArray(matches, TRUE) !== -1; + }, + + 'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) { + return isFormatMatch(command); + }, + + mceBlockQuote: function() { + return isFormatMatch('blockquote'); + }, + + Outdent: function() { + var node; + + if (settings.inline_styles) { + if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) { + return TRUE; + } + + if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) { + return TRUE; + } + } + + return ( + queryCommandState('InsertUnorderedList') || + queryCommandState('InsertOrderedList') || + (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE')) + ); + }, + + 'InsertUnorderedList,InsertOrderedList': function(command) { + var list = dom.getParent(selection.getNode(), 'ul,ol'); + + return list && + ( + command === 'insertunorderedlist' && list.tagName === 'UL' || + command === 'insertorderedlist' && list.tagName === 'OL' + ); + } + }, 'state'); + + // Add queryCommandValue overrides + addCommands({ + 'FontSize,FontName': function(command) { + var value = 0, + parent; + + if ((parent = dom.getParent(selection.getNode(), 'span'))) { + if (command == 'fontsize') { + value = parent.style.fontSize; + } else { + value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase(); + } + } + + return value; + } + }, 'value'); + + // Add undo manager logic + addCommands({ + Undo: function() { + editor.undoManager.undo(); + }, + + Redo: function() { + editor.undoManager.redo(); + } + }); + }; + } + ); + + /** + * URI.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class handles parsing, modification and serialization of URI/URL strings. + * @class tinymce.util.URI + */ + define( + 'tinymce.core.util.URI', [ + 'global!document', + 'tinymce.core.util.Tools' + ], + function(document, Tools) { + var each = Tools.each, + trim = Tools.trim; + var queryParts = "source protocol authority userInfo user password host port relative path directory file query anchor".split(' '); + var DEFAULT_PORTS = { + 'ftp': 21, + 'http': 80, + 'https': 443, + 'mailto': 25 + }; + + /** + * Constructs a new URI instance. + * + * @constructor + * @method URI + * @param {String} url URI string to parse. + * @param {Object} settings Optional settings object. + */ + function URI(url, settings) { + var self = this, + baseUri, baseUrl; + + url = trim(url); + settings = self.settings = settings || {}; + baseUri = settings.base_uri; + + // Strange app protocol that isn't http/https or local anchor + // For example: mailto,skype,tel etc. + if (/^([\w\-]+):([^\/]{2})/i.test(url) || /^\s*#/.test(url)) { + self.source = url; + return; + } + + var isProtocolRelative = url.indexOf('//') === 0; + + // Absolute path with no host, fake host and protocol + if (url.indexOf('/') === 0 && !isProtocolRelative) { + url = (baseUri ? baseUri.protocol || 'http' : 'http') + '://mce_host' + url; + } + + // Relative path http:// or protocol relative //path + if (!/^[\w\-]*:?\/\//.test(url)) { + baseUrl = settings.base_uri ? settings.base_uri.path : new URI(document.location.href).directory; + if (settings.base_uri.protocol === "") { + url = '//mce_host' + self.toAbsPath(baseUrl, url); + } else { + url = /([^#?]*)([#?]?.*)/.exec(url); + url = ((baseUri && baseUri.protocol) || 'http') + '://mce_host' + self.toAbsPath(baseUrl, url[1]) + url[2]; + } + } + + // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri) + url = url.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something + + /*jshint maxlen: 255 */ + /*eslint max-len: 0 */ + url = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(url); + + each(queryParts, function(v, i) { + var part = url[i]; + + // Zope 3 workaround, they use @@something + if (part) { + part = part.replace(/\(mce_at\)/g, '@@'); + } + + self[v] = part; + }); + + if (baseUri) { + if (!self.protocol) { + self.protocol = baseUri.protocol; + } + + if (!self.userInfo) { + self.userInfo = baseUri.userInfo; + } + + if (!self.port && self.host === 'mce_host') { + self.port = baseUri.port; + } + + if (!self.host || self.host === 'mce_host') { + self.host = baseUri.host; + } + + self.source = ''; + } + + if (isProtocolRelative) { + self.protocol = ''; + } + + //t.path = t.path || '/'; + } + + URI.prototype = { + /** + * Sets the internal path part of the URI. + * + * @method setPath + * @param {string} path Path string to set. + */ + setPath: function(path) { + var self = this; + + path = /^(.*?)\/?(\w+)?$/.exec(path); + + // Update path parts + self.path = path[0]; + self.directory = path[1]; + self.file = path[2]; + + // Rebuild source + self.source = ''; + self.getURI(); + }, + + /** + * Converts the specified URI into a relative URI based on the current URI instance location. + * + * @method toRelative + * @param {String} uri URI to convert into a relative path/URI. + * @return {String} Relative URI from the point specified in the current URI instance. + * @example + * // Converts an absolute URL to an relative URL url will be somedir/somefile.htm + * var url = new tinymce.util.URI('http://www.site.com/dir/').toRelative('http://www.site.com/dir/somedir/somefile.htm'); + */ + toRelative: function(uri) { + var self = this, + output; + + if (uri === "./") { + return uri; + } + + uri = new URI(uri, { + base_uri: self + }); + + // Not on same domain/port or protocol + if ((uri.host != 'mce_host' && self.host != uri.host && uri.host) || self.port != uri.port || + (self.protocol != uri.protocol && uri.protocol !== "")) { + return uri.getURI(); + } + + var tu = self.getURI(), + uu = uri.getURI(); + + // Allow usage of the base_uri when relative_urls = true + if (tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu)) { + return tu; + } + + output = self.toRelPath(self.path, uri.path); + + // Add query + if (uri.query) { + output += '?' + uri.query; + } + + // Add anchor + if (uri.anchor) { + output += '#' + uri.anchor; + } + + return output; + }, + + /** + * Converts the specified URI into a absolute URI based on the current URI instance location. + * + * @method toAbsolute + * @param {String} uri URI to convert into a relative path/URI. + * @param {Boolean} noHost No host and protocol prefix. + * @return {String} Absolute URI from the point specified in the current URI instance. + * @example + * // Converts an relative URL to an absolute URL url will be http://www.site.com/dir/somedir/somefile.htm + * var url = new tinymce.util.URI('http://www.site.com/dir/').toAbsolute('somedir/somefile.htm'); + */ + toAbsolute: function(uri, noHost) { + uri = new URI(uri, { + base_uri: this + }); + + return uri.getURI(noHost && this.isSameOrigin(uri)); + }, + + /** + * Determine whether the given URI has the same origin as this URI. Based on RFC-6454. + * Supports default ports for protocols listed in DEFAULT_PORTS. Unsupported protocols will fail safe: they + * won't match, if the port specifications differ. + * + * @method isSameOrigin + * @param {tinymce.util.URI} uri Uri instance to compare. + * @returns {Boolean} True if the origins are the same. + */ + isSameOrigin: function(uri) { + if (this.host == uri.host && this.protocol == uri.protocol) { + if (this.port == uri.port) { + return true; + } + + var defaultPort = DEFAULT_PORTS[this.protocol]; + if (defaultPort && ((this.port || defaultPort) == (uri.port || defaultPort))) { + return true; + } + } + + return false; + }, + + /** + * Converts a absolute path into a relative path. + * + * @method toRelPath + * @param {String} base Base point to convert the path from. + * @param {String} path Absolute path to convert into a relative path. + */ + toRelPath: function(base, path) { + var items, breakPoint = 0, + out = '', + i, l; + + // Split the paths + base = base.substring(0, base.lastIndexOf('/')); + base = base.split('/'); + items = path.split('/'); + + if (base.length >= items.length) { + for (i = 0, l = base.length; i < l; i++) { + if (i >= items.length || base[i] != items[i]) { + breakPoint = i + 1; + break; + } + } + } + + if (base.length < items.length) { + for (i = 0, l = items.length; i < l; i++) { + if (i >= base.length || base[i] != items[i]) { + breakPoint = i + 1; + break; + } + } + } + + if (breakPoint === 1) { + return path; + } + + for (i = 0, l = base.length - (breakPoint - 1); i < l; i++) { + out += "../"; + } + + for (i = breakPoint - 1, l = items.length; i < l; i++) { + if (i != breakPoint - 1) { + out += "/" + items[i]; + } else { + out += items[i]; + } + } + + return out; + }, + + /** + * Converts a relative path into a absolute path. + * + * @method toAbsPath + * @param {String} base Base point to convert the path from. + * @param {String} path Relative path to convert into an absolute path. + */ + toAbsPath: function(base, path) { + var i, nb = 0, + o = [], + tr, outPath; + + // Split paths + tr = /\/$/.test(path) ? '/' : ''; + base = base.split('/'); + path = path.split('/'); + + // Remove empty chunks + each(base, function(k) { + if (k) { + o.push(k); + } + }); + + base = o; + + // Merge relURLParts chunks + for (i = path.length - 1, o = []; i >= 0; i--) { + // Ignore empty or . + if (path[i].length === 0 || path[i] === ".") { + continue; + } + + // Is parent + if (path[i] === '..') { + nb++; + continue; + } + + // Move up + if (nb > 0) { + nb--; + continue; + } + + o.push(path[i]); + } + + i = base.length - nb; + + // If /a/b/c or / + if (i <= 0) { + outPath = o.reverse().join('/'); + } else { + outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/'); + } + + // Add front / if it's needed + if (outPath.indexOf('/') !== 0) { + outPath = '/' + outPath; + } + + // Add traling / if it's needed + if (tr && outPath.lastIndexOf('/') !== outPath.length - 1) { + outPath += tr; + } + + return outPath; + }, + + /** + * Returns the full URI of the internal structure. + * + * @method getURI + * @param {Boolean} noProtoHost Optional no host and protocol part. Defaults to false. + */ + getURI: function(noProtoHost) { + var s, self = this; + + // Rebuild source + if (!self.source || noProtoHost) { + s = ''; + + if (!noProtoHost) { + if (self.protocol) { + s += self.protocol + '://'; + } else { + s += '//'; + } + + if (self.userInfo) { + s += self.userInfo + '@'; + } + + if (self.host) { + s += self.host; + } + + if (self.port) { + s += ':' + self.port; + } + } + + if (self.path) { + s += self.path; + } + + if (self.query) { + s += '?' + self.query; + } + + if (self.anchor) { + s += '#' + self.anchor; + } + + self.source = s; + } + + return self.source; + } + }; + + URI.parseDataUri = function(uri) { + var type, matches; + + uri = decodeURIComponent(uri).split(','); + + matches = /data:([^;]+)/.exec(uri[0]); + if (matches) { + type = matches[1]; + } + + return { + type: type, + data: uri[1] + }; + }; + + URI.getDocumentBaseUrl = function(loc) { + var baseUrl; + + // Pass applewebdata:// and other non web protocols though + if (loc.protocol.indexOf('http') !== 0 && loc.protocol !== 'file:') { + baseUrl = loc.href; + } else { + baseUrl = loc.protocol + '//' + loc.host + loc.pathname; + } + + if (/^[^:]+:\/\/\/?[^\/]+\//.test(baseUrl)) { + baseUrl = baseUrl.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, ''); + + if (!/[\/\\]$/.test(baseUrl)) { + baseUrl += '/'; + } + } + + return baseUrl; + }; + + return URI; + } + ); + + /** + * Class.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This utilitiy class is used for easier inheritance. + * + * Features: + * * Exposed super functions: this._super(); + * * Mixins + * * Dummy functions + * * Property functions: var value = object.value(); and object.value(newValue); + * * Static functions + * * Defaults settings + */ + define( + 'tinymce.core.util.Class', [ + "tinymce.core.util.Tools" + ], + function(Tools) { + var each = Tools.each, + extend = Tools.extend; + + var extendClass, initializing; + + function Class() {} + + // Provides classical inheritance, based on code made by John Resig + Class.extend = extendClass = function(prop) { + var self = this, + _super = self.prototype, + prototype, name, member; + + // The dummy class constructor + function Class() { + var i, mixins, mixin, self = this; + + // All construction is actually done in the init method + if (!initializing) { + // Run class constuctor + if (self.init) { + self.init.apply(self, arguments); + } + + // Run mixin constructors + mixins = self.Mixins; + if (mixins) { + i = mixins.length; + while (i--) { + mixin = mixins[i]; + if (mixin.init) { + mixin.init.apply(self, arguments); + } + } + } + } + } + + // Dummy function, needs to be extended in order to provide functionality + function dummy() { + return this; + } + + // Creates a overloaded method for the class + // this enables you to use this._super(); to call the super function + function createMethod(name, fn) { + return function() { + var self = this, + tmp = self._super, + ret; + + self._super = _super[name]; + ret = fn.apply(self, arguments); + self._super = tmp; + + return ret; + }; + } + + // Instantiate a base class (but only create the instance, + // don't run the init constructor) + initializing = true; + + /*eslint new-cap:0 */ + prototype = new self(); + initializing = false; + + // Add mixins + if (prop.Mixins) { + each(prop.Mixins, function(mixin) { + for (var name in mixin) { + if (name !== "init") { + prop[name] = mixin[name]; + } + } + }); + + if (_super.Mixins) { + prop.Mixins = _super.Mixins.concat(prop.Mixins); + } + } + + // Generate dummy methods + if (prop.Methods) { + each(prop.Methods.split(','), function(name) { + prop[name] = dummy; + }); + } + + // Generate property methods + if (prop.Properties) { + each(prop.Properties.split(','), function(name) { + var fieldName = '_' + name; + + prop[name] = function(value) { + var self = this, + undef; + + // Set value + if (value !== undef) { + self[fieldName] = value; + + return self; + } + + // Get value + return self[fieldName]; + }; + }); + } + + // Static functions + if (prop.Statics) { + each(prop.Statics, function(func, name) { + Class[name] = func; + }); + } + + // Default settings + if (prop.Defaults && _super.Defaults) { + prop.Defaults = extend({}, _super.Defaults, prop.Defaults); + } + + // Copy the properties over onto the new prototype + for (name in prop) { + member = prop[name]; + + if (typeof member == "function" && _super[name]) { + prototype[name] = createMethod(name, member); + } else { + prototype[name] = member; + } + } + + // Populate our constructed prototype object + Class.prototype = prototype; + + // Enforce the constructor to be what we expect + Class.constructor = Class; + + // And make this class extendible + Class.extend = extendClass; + + return Class; + }; + + return Class; + } + ); + /** + * EventDispatcher.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class lets you add/remove and fire events by name on the specified scope. This makes + * it easy to add event listener logic to any class. + * + * @class tinymce.util.EventDispatcher + * @example + * var eventDispatcher = new EventDispatcher(); + * + * eventDispatcher.on('click', function() {console.log('data');}); + * eventDispatcher.fire('click', {data: 123}); + */ + define( + 'tinymce.core.util.EventDispatcher', [ + "tinymce.core.util.Tools" + ], + function(Tools) { + var nativeEvents = Tools.makeMap( + "focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange " + + "mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover " + + "draggesture dragdrop drop drag submit " + + "compositionstart compositionend compositionupdate touchstart touchmove touchend", + ' ' + ); + + function Dispatcher(settings) { + var self = this, + scope, bindings = {}, + toggleEvent; + + function returnFalse() { + return false; + } + + function returnTrue() { + return true; + } + + settings = settings || {}; + scope = settings.scope || self; + toggleEvent = settings.toggleEvent || returnFalse; + + /** + * Fires the specified event by name. + * + * @method fire + * @param {String} name Name of the event to fire. + * @param {Object?} args Event arguments. + * @return {Object} Event args instance passed in. + * @example + * instance.fire('event', {...}); + */ + function fire(name, args) { + var handlers, i, l, callback; + + name = name.toLowerCase(); + args = args || {}; + args.type = name; + + // Setup target is there isn't one + if (!args.target) { + args.target = scope; + } + + // Add event delegation methods if they are missing + if (!args.preventDefault) { + // Add preventDefault method + args.preventDefault = function() { + args.isDefaultPrevented = returnTrue; + }; + + // Add stopPropagation + args.stopPropagation = function() { + args.isPropagationStopped = returnTrue; + }; + + // Add stopImmediatePropagation + args.stopImmediatePropagation = function() { + args.isImmediatePropagationStopped = returnTrue; + }; + + // Add event delegation states + args.isDefaultPrevented = returnFalse; + args.isPropagationStopped = returnFalse; + args.isImmediatePropagationStopped = returnFalse; + } + + if (settings.beforeFire) { + settings.beforeFire(args); + } + + handlers = bindings[name]; + if (handlers) { + for (i = 0, l = handlers.length; i < l; i++) { + callback = handlers[i]; + + // Unbind handlers marked with "once" + if (callback.once) { + off(name, callback.func); + } + + // Stop immediate propagation if needed + if (args.isImmediatePropagationStopped()) { + args.stopPropagation(); + return args; + } + + // If callback returns false then prevent default and stop all propagation + if (callback.func.call(scope, args) === false) { + args.preventDefault(); + return args; + } + } + } + + return args; + } + + /** + * Binds an event listener to a specific event by name. + * + * @method on + * @param {String} name Event name or space separated list of events to bind. + * @param {callback} callback Callback to be executed when the event occurs. + * @param {Boolean} first Optional flag if the event should be prepended. Use this with care. + * @return {Object} Current class instance. + * @example + * instance.on('event', function(e) { + * // Callback logic + * }); + */ + function on(name, callback, prepend, extra) { + var handlers, names, i; + + if (callback === false) { + callback = returnFalse; + } + + if (callback) { + callback = { + func: callback + }; + + if (extra) { + Tools.extend(callback, extra); + } + + names = name.toLowerCase().split(' '); + i = names.length; + while (i--) { + name = names[i]; + handlers = bindings[name]; + if (!handlers) { + handlers = bindings[name] = []; + toggleEvent(name, true); + } + + if (prepend) { + handlers.unshift(callback); + } else { + handlers.push(callback); + } + } + } + + return self; + } + + /** + * Unbinds an event listener to a specific event by name. + * + * @method off + * @param {String?} name Name of the event to unbind. + * @param {callback?} callback Callback to unbind. + * @return {Object} Current class instance. + * @example + * // Unbind specific callback + * instance.off('event', handler); + * + * // Unbind all listeners by name + * instance.off('event'); + * + * // Unbind all events + * instance.off(); + */ + function off(name, callback) { + var i, handlers, bindingName, names, hi; + + if (name) { + names = name.toLowerCase().split(' '); + i = names.length; + while (i--) { + name = names[i]; + handlers = bindings[name]; + + // Unbind all handlers + if (!name) { + for (bindingName in bindings) { + toggleEvent(bindingName, false); + delete bindings[bindingName]; + } + + return self; + } + + if (handlers) { + // Unbind all by name + if (!callback) { + handlers.length = 0; + } else { + // Unbind specific ones + hi = handlers.length; + while (hi--) { + if (handlers[hi].func === callback) { + handlers = handlers.slice(0, hi).concat(handlers.slice(hi + 1)); + bindings[name] = handlers; + } + } + } + + if (!handlers.length) { + toggleEvent(name, false); + delete bindings[name]; + } + } + } + } else { + for (name in bindings) { + toggleEvent(name, false); + } + + bindings = {}; + } + + return self; + } + + /** + * Binds an event listener to a specific event by name + * and automatically unbind the event once the callback fires. + * + * @method once + * @param {String} name Event name or space separated list of events to bind. + * @param {callback} callback Callback to be executed when the event occurs. + * @param {Boolean} first Optional flag if the event should be prepended. Use this with care. + * @return {Object} Current class instance. + * @example + * instance.once('event', function(e) { + * // Callback logic + * }); + */ + function once(name, callback, prepend) { + return on(name, callback, prepend, { + once: true + }); + } + + /** + * Returns true/false if the dispatcher has a event of the specified name. + * + * @method has + * @param {String} name Name of the event to check for. + * @return {Boolean} true/false if the event exists or not. + */ + function has(name) { + name = name.toLowerCase(); + return !(!bindings[name] || bindings[name].length === 0); + } + + // Expose + self.fire = fire; + self.on = on; + self.off = off; + self.once = once; + self.has = has; + } + + /** + * Returns true/false if the specified event name is a native browser event or not. + * + * @method isNative + * @param {String} name Name to check if it's native. + * @return {Boolean} true/false if the event is native or not. + * @static + */ + Dispatcher.isNative = function(name) { + return !!nativeEvents[name.toLowerCase()]; + }; + + return Dispatcher; + } + ); + + /** + * Observable.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This mixin will add event binding logic to classes. + * + * @mixin tinymce.util.Observable + */ + define( + 'tinymce.core.util.Observable', [ + "tinymce.core.util.EventDispatcher" + ], + function(EventDispatcher) { + function getEventDispatcher(obj) { + if (!obj._eventDispatcher) { + obj._eventDispatcher = new EventDispatcher({ + scope: obj, + toggleEvent: function(name, state) { + if (EventDispatcher.isNative(name) && obj.toggleNativeEvent) { + obj.toggleNativeEvent(name, state); + } + } + }); + } + + return obj._eventDispatcher; + } + + return { + /** + * Fires the specified event by name. Consult the + * <a href="/docs/advanced/events">event reference</a> for more details on each event. + * + * @method fire + * @param {String} name Name of the event to fire. + * @param {Object?} args Event arguments. + * @param {Boolean?} bubble True/false if the event is to be bubbled. + * @return {Object} Event args instance passed in. + * @example + * instance.fire('event', {...}); + */ + fire: function(name, args, bubble) { + var self = this; + + // Prevent all events except the remove event after the instance has been removed + if (self.removed && name !== "remove") { + return args; + } + + args = getEventDispatcher(self).fire(name, args, bubble); + + // Bubble event up to parents + if (bubble !== false && self.parent) { + var parent = self.parent(); + while (parent && !args.isPropagationStopped()) { + parent.fire(name, args, false); + parent = parent.parent(); + } + } + + return args; + }, + + /** + * Binds an event listener to a specific event by name. Consult the + * <a href="/docs/advanced/events">event reference</a> for more details on each event. + * + * @method on + * @param {String} name Event name or space separated list of events to bind. + * @param {callback} callback Callback to be executed when the event occurs. + * @param {Boolean} first Optional flag if the event should be prepended. Use this with care. + * @return {Object} Current class instance. + * @example + * instance.on('event', function(e) { + * // Callback logic + * }); + */ + on: function(name, callback, prepend) { + return getEventDispatcher(this).on(name, callback, prepend); + }, + + /** + * Unbinds an event listener to a specific event by name. Consult the + * <a href="/docs/advanced/events">event reference</a> for more details on each event. + * + * @method off + * @param {String?} name Name of the event to unbind. + * @param {callback?} callback Callback to unbind. + * @return {Object} Current class instance. + * @example + * // Unbind specific callback + * instance.off('event', handler); + * + * // Unbind all listeners by name + * instance.off('event'); + * + * // Unbind all events + * instance.off(); + */ + off: function(name, callback) { + return getEventDispatcher(this).off(name, callback); + }, + + /** + * Bind the event callback and once it fires the callback is removed. Consult the + * <a href="/docs/advanced/events">event reference</a> for more details on each event. + * + * @method once + * @param {String} name Name of the event to bind. + * @param {callback} callback Callback to bind only once. + * @return {Object} Current class instance. + */ + once: function(name, callback) { + return getEventDispatcher(this).once(name, callback); + }, + + /** + * Returns true/false if the object has a event of the specified name. + * + * @method hasEventListeners + * @param {String} name Name of the event to check for. + * @return {Boolean} true/false if the event exists or not. + */ + hasEventListeners: function(name) { + return getEventDispatcher(this).has(name); + } + }; + } + ); + /** + * Binding.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class gets dynamically extended to provide a binding between two models. This makes it possible to + * sync the state of two properties in two models by a layer of abstraction. + * + * @private + * @class tinymce.data.Binding + */ + define( + 'tinymce.core.data.Binding', [], + function() { + /** + * Constructs a new bidning. + * + * @constructor + * @method Binding + * @param {Object} settings Settings to the binding. + */ + function Binding(settings) { + this.create = settings.create; + } + + /** + * Creates a binding for a property on a model. + * + * @method create + * @param {tinymce.data.ObservableObject} model Model to create binding to. + * @param {String} name Name of property to bind. + * @return {tinymce.data.Binding} Binding instance. + */ + Binding.create = function(model, name) { + return new Binding({ + create: function(otherModel, otherName) { + var bindings; + + function fromSelfToOther(e) { + otherModel.set(otherName, e.value); + } + + function fromOtherToSelf(e) { + model.set(name, e.value); + } + + otherModel.on('change:' + otherName, fromOtherToSelf); + model.on('change:' + name, fromSelfToOther); + + // Keep track of the bindings + bindings = otherModel._bindings; + + if (!bindings) { + bindings = otherModel._bindings = []; + + otherModel.on('destroy', function() { + var i = bindings.length; + + while (i--) { + bindings[i](); + } + }); + } + + bindings.push(function() { + model.off('change:' + name, fromSelfToOther); + }); + + return model.get(name); + } + }); + }; + + return Binding; + } + ); + /** + * ObservableObject.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class is a object that is observable when properties changes a change event gets emitted. + * + * @private + * @class tinymce.data.ObservableObject + */ + define( + 'tinymce.core.data.ObservableObject', [ + 'tinymce.core.data.Binding', + 'tinymce.core.util.Class', + 'tinymce.core.util.Observable', + 'tinymce.core.util.Tools' + ], + function(Binding, Class, Observable, Tools) { + function isNode(node) { + return node.nodeType > 0; + } + + // Todo: Maybe this should be shallow compare since it might be huge object references + function isEqual(a, b) { + var k, checked; + + // Strict equals + if (a === b) { + return true; + } + + // Compare null + if (a === null || b === null) { + return a === b; + } + + // Compare number, boolean, string, undefined + if (typeof a !== "object" || typeof b !== "object") { + return a === b; + } + + // Compare arrays + if (Tools.isArray(b)) { + if (a.length !== b.length) { + return false; + } + + k = a.length; + while (k--) { + if (!isEqual(a[k], b[k])) { + return false; + } + } + } + + // Shallow compare nodes + if (isNode(a) || isNode(b)) { + return a === b; + } + + // Compare objects + checked = {}; + for (k in b) { + if (!isEqual(a[k], b[k])) { + return false; + } + + checked[k] = true; + } + + for (k in a) { + if (!checked[k] && !isEqual(a[k], b[k])) { + return false; + } + } + + return true; + } + + return Class.extend({ + Mixins: [Observable], + + /** + * Constructs a new observable object instance. + * + * @constructor + * @param {Object} data Initial data for the object. + */ + init: function(data) { + var name, value; + + data = data || {}; + + for (name in data) { + value = data[name]; + + if (value instanceof Binding) { + data[name] = value.create(this, name); + } + } + + this.data = data; + }, + + /** + * Sets a property on the value this will call + * observers if the value is a change from the current value. + * + * @method set + * @param {String/object} name Name of the property to set or a object of items to set. + * @param {Object} value Value to set for the property. + * @return {tinymce.data.ObservableObject} Observable object instance. + */ + set: function(name, value) { + var key, args, oldValue = this.data[name]; + + if (value instanceof Binding) { + value = value.create(this, name); + } + + if (typeof name === "object") { + for (key in name) { + this.set(key, name[key]); + } + + return this; + } + + if (!isEqual(oldValue, value)) { + this.data[name] = value; + + args = { + target: this, + name: name, + value: value, + oldValue: oldValue + }; + + this.fire('change:' + name, args); + this.fire('change', args); + } + + return this; + }, + + /** + * Gets a property by name. + * + * @method get + * @param {String} name Name of the property to get. + * @return {Object} Object value of propery. + */ + get: function(name) { + return this.data[name]; + }, + + /** + * Returns true/false if the specified property exists. + * + * @method has + * @param {String} name Name of the property to check for. + * @return {Boolean} true/false if the item exists. + */ + has: function(name) { + return name in this.data; + }, + + /** + * Returns a dynamic property binding for the specified property name. This makes + * it possible to sync the state of two properties in two ObservableObject instances. + * + * @method bind + * @param {String} name Name of the property to sync with the property it's inserted to. + * @return {tinymce.data.Binding} Data binding instance. + */ + bind: function(name) { + return Binding.create(this, name); + }, + + /** + * Destroys the observable object and fires the "destroy" + * event and clean up any internal resources. + * + * @method destroy + */ + destroy: function() { + this.fire('destroy'); + } + }); + } + ); + /** + * Selector.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /*eslint no-nested-ternary:0 */ + + /** + * Selector engine, enables you to select controls by using CSS like expressions. + * We currently only support basic CSS expressions to reduce the size of the core + * and the ones we support should be enough for most cases. + * + * @example + * Supported expressions: + * element + * element#name + * element.class + * element[attr] + * element[attr*=value] + * element[attr~=value] + * element[attr!=value] + * element[attr^=value] + * element[attr$=value] + * element:<state> + * element:not(<expression>) + * element:first + * element:last + * element:odd + * element:even + * element element + * element > element + * + * @class tinymce.ui.Selector + */ + define( + 'tinymce.core.ui.Selector', [ + "tinymce.core.util.Class" + ], + function(Class) { + "use strict"; + + /** + * Produces an array with a unique set of objects. It will not compare the values + * but the references of the objects. + * + * @private + * @method unqiue + * @param {Array} array Array to make into an array with unique items. + * @return {Array} Array with unique items. + */ + function unique(array) { + var uniqueItems = [], + i = array.length, + item; + + while (i--) { + item = array[i]; + + if (!item.__checked) { + uniqueItems.push(item); + item.__checked = 1; + } + } + + i = uniqueItems.length; + while (i--) { + delete uniqueItems[i].__checked; + } + + return uniqueItems; + } + + var expression = /^([\w\\*]+)?(?:#([\w\-\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i; + + /*jshint maxlen:255 */ + /*eslint max-len:0 */ + var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + whiteSpace = /^\s*|\s*$/g, + Collection; + + var Selector = Class.extend({ + /** + * Constructs a new Selector instance. + * + * @constructor + * @method init + * @param {String} selector CSS like selector expression. + */ + init: function(selector) { + var match = this.match; + + function compileNameFilter(name) { + if (name) { + name = name.toLowerCase(); + + return function(item) { + return name === '*' || item.type === name; + }; + } + } + + function compileIdFilter(id) { + if (id) { + return function(item) { + return item._name === id; + }; + } + } + + function compileClassesFilter(classes) { + if (classes) { + classes = classes.split('.'); + + return function(item) { + var i = classes.length; + + while (i--) { + if (!item.classes.contains(classes[i])) { + return false; + } + } + + return true; + }; + } + } + + function compileAttrFilter(name, cmp, check) { + if (name) { + return function(item) { + var value = item[name] ? item[name]() : ''; + + return !cmp ? !!check : + cmp === "=" ? value === check : + cmp === "*=" ? value.indexOf(check) >= 0 : + cmp === "~=" ? (" " + value + " ").indexOf(" " + check + " ") >= 0 : + cmp === "!=" ? value != check : + cmp === "^=" ? value.indexOf(check) === 0 : + cmp === "$=" ? value.substr(value.length - check.length) === check : + false; + }; + } + } + + function compilePsuedoFilter(name) { + var notSelectors; + + if (name) { + name = /(?:not\((.+)\))|(.+)/i.exec(name); + + if (!name[1]) { + name = name[2]; + + return function(item, index, length) { + return name === 'first' ? index === 0 : + name === 'last' ? index === length - 1 : + name === 'even' ? index % 2 === 0 : + name === 'odd' ? index % 2 === 1 : + item[name] ? item[name]() : + false; + }; + } + + // Compile not expression + notSelectors = parseChunks(name[1], []); + + return function(item) { + return !match(item, notSelectors); + }; + } + } + + function compile(selector, filters, direct) { + var parts; + + function add(filter) { + if (filter) { + filters.push(filter); + } + } + + // Parse expression into parts + parts = expression.exec(selector.replace(whiteSpace, '')); + + add(compileNameFilter(parts[1])); + add(compileIdFilter(parts[2])); + add(compileClassesFilter(parts[3])); + add(compileAttrFilter(parts[4], parts[5], parts[6])); + add(compilePsuedoFilter(parts[7])); + + // Mark the filter with pseudo for performance + filters.pseudo = !!parts[7]; + filters.direct = direct; + + return filters; + } + + // Parser logic based on Sizzle by John Resig + function parseChunks(selector, selectors) { + var parts = [], + extra, matches, i; + + do { + chunker.exec(""); + matches = chunker.exec(selector); + + if (matches) { + selector = matches[3]; + parts.push(matches[1]); + + if (matches[2]) { + extra = matches[3]; + break; + } + } + } while (matches); + + if (extra) { + parseChunks(extra, selectors); + } + + selector = []; + for (i = 0; i < parts.length; i++) { + if (parts[i] != '>') { + selector.push(compile(parts[i], [], parts[i - 1] === '>')); + } + } + + selectors.push(selector); + + return selectors; + } + + this._selectors = parseChunks(selector, []); + }, + + /** + * Returns true/false if the selector matches the specified control. + * + * @method match + * @param {tinymce.ui.Control} control Control to match against the selector. + * @param {Array} selectors Optional array of selectors, mostly used internally. + * @return {Boolean} true/false state if the control matches or not. + */ + match: function(control, selectors) { + var i, l, si, sl, selector, fi, fl, filters, index, length, siblings, count, item; + + selectors = selectors || this._selectors; + for (i = 0, l = selectors.length; i < l; i++) { + selector = selectors[i]; + sl = selector.length; + item = control; + count = 0; + + for (si = sl - 1; si >= 0; si--) { + filters = selector[si]; + + while (item) { + // Find the index and length since a pseudo filter like :first needs it + if (filters.pseudo) { + siblings = item.parent().items(); + index = length = siblings.length; + while (index--) { + if (siblings[index] === item) { + break; + } + } + } + + for (fi = 0, fl = filters.length; fi < fl; fi++) { + if (!filters[fi](item, index, length)) { + fi = fl + 1; + break; + } + } + + if (fi === fl) { + count++; + break; + } else { + // If it didn't match the right most expression then + // break since it's no point looking at the parents + if (si === sl - 1) { + break; + } + } + + item = item.parent(); + } + } + + // If we found all selectors then return true otherwise continue looking + if (count === sl) { + return true; + } + } + + return false; + }, + + /** + * Returns a tinymce.ui.Collection with matches of the specified selector inside the specified container. + * + * @method find + * @param {tinymce.ui.Control} container Container to look for items in. + * @return {tinymce.ui.Collection} Collection with matched elements. + */ + find: function(container) { + var matches = [], + i, l, selectors = this._selectors; + + function collect(items, selector, index) { + var i, l, fi, fl, item, filters = selector[index]; + + for (i = 0, l = items.length; i < l; i++) { + item = items[i]; + + // Run each filter against the item + for (fi = 0, fl = filters.length; fi < fl; fi++) { + if (!filters[fi](item, i, l)) { + fi = fl + 1; + break; + } + } + + // All filters matched the item + if (fi === fl) { + // Matched item is on the last expression like: panel toolbar [button] + if (index == selector.length - 1) { + matches.push(item); + } else { + // Collect next expression type + if (item.items) { + collect(item.items(), selector, index + 1); + } + } + } else if (filters.direct) { + return; + } + + // Collect child items + if (item.items) { + collect(item.items(), selector, index); + } + } + } + + if (container.items) { + for (i = 0, l = selectors.length; i < l; i++) { + collect(container.items(), selectors[i], 0); + } + + // Unique the matches if needed + if (l > 1) { + matches = unique(matches); + } + } + + // Fix for circular reference + if (!Collection) { + // TODO: Fix me! + Collection = Selector.Collection; + } + + return new Collection(matches); + } + }); + + return Selector; + } + ); + + /** + * Collection.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Control collection, this class contains control instances and it enables you to + * perform actions on all the contained items. This is very similar to how jQuery works. + * + * @example + * someCollection.show().disabled(true); + * + * @class tinymce.ui.Collection + */ + define( + 'tinymce.core.ui.Collection', [ + "tinymce.core.util.Tools", + "tinymce.core.ui.Selector", + "tinymce.core.util.Class" + ], + function(Tools, Selector, Class) { + "use strict"; + + var Collection, proto, push = Array.prototype.push, + slice = Array.prototype.slice; + + proto = { + /** + * Current number of contained control instances. + * + * @field length + * @type Number + */ + length: 0, + + /** + * Constructor for the collection. + * + * @constructor + * @method init + * @param {Array} items Optional array with items to add. + */ + init: function(items) { + if (items) { + this.add(items); + } + }, + + /** + * Adds new items to the control collection. + * + * @method add + * @param {Array} items Array if items to add to collection. + * @return {tinymce.ui.Collection} Current collection instance. + */ + add: function(items) { + var self = this; + + // Force single item into array + if (!Tools.isArray(items)) { + if (items instanceof Collection) { + self.add(items.toArray()); + } else { + push.call(self, items); + } + } else { + push.apply(self, items); + } + + return self; + }, + + /** + * Sets the contents of the collection. This will remove any existing items + * and replace them with the ones specified in the input array. + * + * @method set + * @param {Array} items Array with items to set into the Collection. + * @return {tinymce.ui.Collection} Collection instance. + */ + set: function(items) { + var self = this, + len = self.length, + i; + + self.length = 0; + self.add(items); + + // Remove old entries + for (i = self.length; i < len; i++) { + delete self[i]; + } + + return self; + }, + + /** + * Filters the collection item based on the specified selector expression or selector function. + * + * @method filter + * @param {String} selector Selector expression to filter items by. + * @return {tinymce.ui.Collection} Collection containing the filtered items. + */ + filter: function(selector) { + var self = this, + i, l, matches = [], + item, match; + + // Compile string into selector expression + if (typeof selector === "string") { + selector = new Selector(selector); + + match = function(item) { + return selector.match(item); + }; + } else { + // Use selector as matching function + match = selector; + } + + for (i = 0, l = self.length; i < l; i++) { + item = self[i]; + + if (match(item)) { + matches.push(item); + } + } + + return new Collection(matches); + }, + + /** + * Slices the items within the collection. + * + * @method slice + * @param {Number} index Index to slice at. + * @param {Number} len Optional length to slice. + * @return {tinymce.ui.Collection} Current collection. + */ + slice: function() { + return new Collection(slice.apply(this, arguments)); + }, + + /** + * Makes the current collection equal to the specified index. + * + * @method eq + * @param {Number} index Index of the item to set the collection to. + * @return {tinymce.ui.Collection} Current collection. + */ + eq: function(index) { + return index === -1 ? this.slice(index) : this.slice(index, +index + 1); + }, + + /** + * Executes the specified callback on each item in collection. + * + * @method each + * @param {function} callback Callback to execute for each item in collection. + * @return {tinymce.ui.Collection} Current collection instance. + */ + each: function(callback) { + Tools.each(this, callback); + + return this; + }, + + /** + * Returns an JavaScript array object of the contents inside the collection. + * + * @method toArray + * @return {Array} Array with all items from collection. + */ + toArray: function() { + return Tools.toArray(this); + }, + + /** + * Finds the index of the specified control or return -1 if it isn't in the collection. + * + * @method indexOf + * @param {Control} ctrl Control instance to look for. + * @return {Number} Index of the specified control or -1. + */ + indexOf: function(ctrl) { + var self = this, + i = self.length; + + while (i--) { + if (self[i] === ctrl) { + break; + } + } + + return i; + }, + + /** + * Returns a new collection of the contents in reverse order. + * + * @method reverse + * @return {tinymce.ui.Collection} Collection instance with reversed items. + */ + reverse: function() { + return new Collection(Tools.toArray(this).reverse()); + }, + + /** + * Returns true/false if the class exists or not. + * + * @method hasClass + * @param {String} cls Class to check for. + * @return {Boolean} true/false state if the class exists or not. + */ + hasClass: function(cls) { + return this[0] ? this[0].classes.contains(cls) : false; + }, + + /** + * Sets/gets the specific property on the items in the collection. The same as executing control.<property>(<value>); + * + * @method prop + * @param {String} name Property name to get/set. + * @param {Object} value Optional object value to set. + * @return {tinymce.ui.Collection} Current collection instance or value of the first item on a get operation. + */ + prop: function(name, value) { + var self = this, + undef, item; + + if (value !== undef) { + self.each(function(item) { + if (item[name]) { + item[name](value); + } + }); + + return self; + } + + item = self[0]; + + if (item && item[name]) { + return item[name](); + } + }, + + /** + * Executes the specific function name with optional arguments an all items in collection if it exists. + * + * @example collection.exec("myMethod", arg1, arg2, arg3); + * @method exec + * @param {String} name Name of the function to execute. + * @param {Object} ... Multiple arguments to pass to each function. + * @return {tinymce.ui.Collection} Current collection. + */ + exec: function(name) { + var self = this, + args = Tools.toArray(arguments).slice(1); + + self.each(function(item) { + if (item[name]) { + item[name].apply(item, args); + } + }); + + return self; + }, + + /** + * Remove all items from collection and DOM. + * + * @method remove + * @return {tinymce.ui.Collection} Current collection. + */ + remove: function() { + var i = this.length; + + while (i--) { + this[i].remove(); + } + + return this; + }, + + /** + * Adds a class to all items in the collection. + * + * @method addClass + * @param {String} cls Class to add to each item. + * @return {tinymce.ui.Collection} Current collection instance. + */ + addClass: function(cls) { + return this.each(function(item) { + item.classes.add(cls); + }); + }, + + /** + * Removes the specified class from all items in collection. + * + * @method removeClass + * @param {String} cls Class to remove from each item. + * @return {tinymce.ui.Collection} Current collection instance. + */ + removeClass: function(cls) { + return this.each(function(item) { + item.classes.remove(cls); + }); + } + + /** + * Fires the specified event by name and arguments on the control. This will execute all + * bound event handlers. + * + * @method fire + * @param {String} name Name of the event to fire. + * @param {Object} args Optional arguments to pass to the event. + * @return {tinymce.ui.Collection} Current collection instance. + */ + // fire: function(event, args) {}, -- Generated by code below + + /** + * Binds a callback to the specified event. This event can both be + * native browser events like "click" or custom ones like PostRender. + * + * The callback function will have two parameters the first one being the control that received the event + * the second one will be the event object either the browsers native event object or a custom JS object. + * + * @method on + * @param {String} name Name of the event to bind. For example "click". + * @param {String/function} callback Callback function to execute ones the event occurs. + * @return {tinymce.ui.Collection} Current collection instance. + */ + // on: function(name, callback) {}, -- Generated by code below + + /** + * Unbinds the specified event and optionally a specific callback. If you omit the name + * parameter all event handlers will be removed. If you omit the callback all event handles + * by the specified name will be removed. + * + * @method off + * @param {String} name Optional name for the event to unbind. + * @param {function} callback Optional callback function to unbind. + * @return {tinymce.ui.Collection} Current collection instance. + */ + // off: function(name, callback) {}, -- Generated by code below + + /** + * Shows the items in the current collection. + * + * @method show + * @return {tinymce.ui.Collection} Current collection instance. + */ + // show: function() {}, -- Generated by code below + + /** + * Hides the items in the current collection. + * + * @method hide + * @return {tinymce.ui.Collection} Current collection instance. + */ + // hide: function() {}, -- Generated by code below + + /** + * Sets/gets the text contents of the items in the current collection. + * + * @method text + * @return {tinymce.ui.Collection} Current collection instance or text value of the first item on a get operation. + */ + // text: function(value) {}, -- Generated by code below + + /** + * Sets/gets the name contents of the items in the current collection. + * + * @method name + * @return {tinymce.ui.Collection} Current collection instance or name value of the first item on a get operation. + */ + // name: function(value) {}, -- Generated by code below + + /** + * Sets/gets the disabled state on the items in the current collection. + * + * @method disabled + * @return {tinymce.ui.Collection} Current collection instance or disabled state of the first item on a get operation. + */ + // disabled: function(state) {}, -- Generated by code below + + /** + * Sets/gets the active state on the items in the current collection. + * + * @method active + * @return {tinymce.ui.Collection} Current collection instance or active state of the first item on a get operation. + */ + // active: function(state) {}, -- Generated by code below + + /** + * Sets/gets the selected state on the items in the current collection. + * + * @method selected + * @return {tinymce.ui.Collection} Current collection instance or selected state of the first item on a get operation. + */ + // selected: function(state) {}, -- Generated by code below + + /** + * Sets/gets the selected state on the items in the current collection. + * + * @method visible + * @return {tinymce.ui.Collection} Current collection instance or visible state of the first item on a get operation. + */ + // visible: function(state) {}, -- Generated by code below + }; + + // Extend tinymce.ui.Collection prototype with some generated control specific methods + Tools.each('fire on off show hide append prepend before after reflow'.split(' '), function(name) { + proto[name] = function() { + var args = Tools.toArray(arguments); + + this.each(function(ctrl) { + if (name in ctrl) { + ctrl[name].apply(ctrl, args); + } + }); + + return this; + }; + }); + + // Extend tinymce.ui.Collection prototype with some property methods + Tools.each('text name disabled active selected checked visible parent value data'.split(' '), function(name) { + proto[name] = function(value) { + return this.prop(name, value); + }; + }); + + // Create class based on the new prototype + Collection = Class.extend(proto); + + // Stick Collection into Selector to prevent circual references + Selector.Collection = Collection; + + return Collection; + } + ); + /** + * DomUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Private UI DomUtils proxy. + * + * @private + * @class tinymce.ui.DomUtils + */ + define( + 'tinymce.core.ui.DomUtils', [ + "tinymce.core.Env", + "tinymce.core.util.Tools", + "tinymce.core.dom.DOMUtils" + ], + function(Env, Tools, DOMUtils) { + "use strict"; + + var count = 0; + + var funcs = { + id: function() { + return 'mceu_' + (count++); + }, + + create: function(name, attrs, children) { + var elm = document.createElement(name); + + DOMUtils.DOM.setAttribs(elm, attrs); + + if (typeof children === 'string') { + elm.innerHTML = children; + } else { + Tools.each(children, function(child) { + if (child.nodeType) { + elm.appendChild(child); + } + }); + } + + return elm; + }, + + createFragment: function(html) { + return DOMUtils.DOM.createFragment(html); + }, + + getWindowSize: function() { + return DOMUtils.DOM.getViewPort(); + }, + + getSize: function(elm) { + var width, height; + + if (elm.getBoundingClientRect) { + var rect = elm.getBoundingClientRect(); + + width = Math.max(rect.width || (rect.right - rect.left), elm.offsetWidth); + height = Math.max(rect.height || (rect.bottom - rect.bottom), elm.offsetHeight); + } else { + width = elm.offsetWidth; + height = elm.offsetHeight; + } + + return { + width: width, + height: height + }; + }, + + getPos: function(elm, root) { + return DOMUtils.DOM.getPos(elm, root || funcs.getContainer()); + }, + + getContainer: function() { + return Env.container ? Env.container : document.body; + }, + + getViewPort: function(win) { + return DOMUtils.DOM.getViewPort(win); + }, + + get: function(id) { + return document.getElementById(id); + }, + + addClass: function(elm, cls) { + return DOMUtils.DOM.addClass(elm, cls); + }, + + removeClass: function(elm, cls) { + return DOMUtils.DOM.removeClass(elm, cls); + }, + + hasClass: function(elm, cls) { + return DOMUtils.DOM.hasClass(elm, cls); + }, + + toggleClass: function(elm, cls, state) { + return DOMUtils.DOM.toggleClass(elm, cls, state); + }, + + css: function(elm, name, value) { + return DOMUtils.DOM.setStyle(elm, name, value); + }, + + getRuntimeStyle: function(elm, name) { + return DOMUtils.DOM.getStyle(elm, name, true); + }, + + on: function(target, name, callback, scope) { + return DOMUtils.DOM.bind(target, name, callback, scope); + }, + + off: function(target, name, callback) { + return DOMUtils.DOM.unbind(target, name, callback); + }, + + fire: function(target, name, args) { + return DOMUtils.DOM.fire(target, name, args); + }, + + innerHtml: function(elm, html) { + // Workaround for <div> in <p> bug on IE 8 #6178 + DOMUtils.DOM.setHTML(elm, html); + } + }; + + return funcs; + } + ); + /** + * BoxUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Utility class for box parsing and measuring. + * + * @private + * @class tinymce.ui.BoxUtils + */ + define( + 'tinymce.core.ui.BoxUtils', [], + function() { + "use strict"; + + return { + /** + * Parses the specified box value. A box value contains 1-4 properties in clockwise order. + * + * @method parseBox + * @param {String/Number} value Box value "0 1 2 3" or "0" etc. + * @return {Object} Object with top/right/bottom/left properties. + * @private + */ + parseBox: function(value) { + var len, radix = 10; + + if (!value) { + return; + } + + if (typeof value === "number") { + value = value || 0; + + return { + top: value, + left: value, + bottom: value, + right: value + }; + } + + value = value.split(' '); + len = value.length; + + if (len === 1) { + value[1] = value[2] = value[3] = value[0]; + } else if (len === 2) { + value[2] = value[0]; + value[3] = value[1]; + } else if (len === 3) { + value[3] = value[1]; + } + + return { + top: parseInt(value[0], radix) || 0, + right: parseInt(value[1], radix) || 0, + bottom: parseInt(value[2], radix) || 0, + left: parseInt(value[3], radix) || 0 + }; + }, + + measureBox: function(elm, prefix) { + function getStyle(name) { + var defaultView = document.defaultView; + + if (defaultView) { + // Remove camelcase + name = name.replace(/[A-Z]/g, function(a) { + return '-' + a; + }); + + return defaultView.getComputedStyle(elm, null).getPropertyValue(name); + } + + return elm.currentStyle[name]; + } + + function getSide(name) { + var val = parseFloat(getStyle(name), 10); + + return isNaN(val) ? 0 : val; + } + + return { + top: getSide(prefix + "TopWidth"), + right: getSide(prefix + "RightWidth"), + bottom: getSide(prefix + "BottomWidth"), + left: getSide(prefix + "LeftWidth") + }; + } + }; + } + ); + + /** + * ClassList.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Handles adding and removal of classes. + * + * @private + * @class tinymce.ui.ClassList + */ + define( + 'tinymce.core.ui.ClassList', [ + "tinymce.core.util.Tools" + ], + function(Tools) { + "use strict"; + + function noop() {} + + /** + * Constructs a new class list the specified onchange + * callback will be executed when the class list gets modifed. + * + * @constructor ClassList + * @param {function} onchange Onchange callback to be executed. + */ + function ClassList(onchange) { + this.cls = []; + this.cls._map = {}; + this.onchange = onchange || noop; + this.prefix = ''; + } + + Tools.extend(ClassList.prototype, { + /** + * Adds a new class to the class list. + * + * @method add + * @param {String} cls Class to be added. + * @return {tinymce.ui.ClassList} Current class list instance. + */ + add: function(cls) { + if (cls && !this.contains(cls)) { + this.cls._map[cls] = true; + this.cls.push(cls); + this._change(); + } + + return this; + }, + + /** + * Removes the specified class from the class list. + * + * @method remove + * @param {String} cls Class to be removed. + * @return {tinymce.ui.ClassList} Current class list instance. + */ + remove: function(cls) { + if (this.contains(cls)) { + for (var i = 0; i < this.cls.length; i++) { + if (this.cls[i] === cls) { + break; + } + } + + this.cls.splice(i, 1); + delete this.cls._map[cls]; + this._change(); + } + + return this; + }, + + /** + * Toggles a class in the class list. + * + * @method toggle + * @param {String} cls Class to be added/removed. + * @param {Boolean} state Optional state if it should be added/removed. + * @return {tinymce.ui.ClassList} Current class list instance. + */ + toggle: function(cls, state) { + var curState = this.contains(cls); + + if (curState !== state) { + if (curState) { + this.remove(cls); + } else { + this.add(cls); + } + + this._change(); + } + + return this; + }, + + /** + * Returns true if the class list has the specified class. + * + * @method contains + * @param {String} cls Class to look for. + * @return {Boolean} true/false if the class exists or not. + */ + contains: function(cls) { + return !!this.cls._map[cls]; + }, + + /** + * Returns a space separated list of classes. + * + * @method toString + * @return {String} Space separated list of classes. + */ + + _change: function() { + delete this.clsValue; + this.onchange.call(this); + } + }); + + // IE 8 compatibility + ClassList.prototype.toString = function() { + var value; + + if (this.clsValue) { + return this.clsValue; + } + + value = ''; + for (var i = 0; i < this.cls.length; i++) { + if (i > 0) { + value += ' '; + } + + value += this.prefix + this.cls[i]; + } + + return value; + }; + + return ClassList; + } + ); + /** + * ReflowQueue.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class will automatically reflow controls on the next animation frame within a few milliseconds on older browsers. + * If the user manually reflows then the automatic reflow will be cancelled. This class is used internally when various control states + * changes that triggers a reflow. + * + * @class tinymce.ui.ReflowQueue + * @static + */ + define( + 'tinymce.core.ui.ReflowQueue', [ + "tinymce.core.util.Delay" + ], + function(Delay) { + var dirtyCtrls = {}, + animationFrameRequested; + + return { + /** + * Adds a control to the next automatic reflow call. This is the control that had a state + * change for example if the control was hidden/shown. + * + * @method add + * @param {tinymce.ui.Control} ctrl Control to add to queue. + */ + add: function(ctrl) { + var parent = ctrl.parent(); + + if (parent) { + if (!parent._layout || parent._layout.isNative()) { + return; + } + + if (!dirtyCtrls[parent._id]) { + dirtyCtrls[parent._id] = parent; + } + + if (!animationFrameRequested) { + animationFrameRequested = true; + + Delay.requestAnimationFrame(function() { + var id, ctrl; + + animationFrameRequested = false; + + for (id in dirtyCtrls) { + ctrl = dirtyCtrls[id]; + + if (ctrl.state.get('rendered')) { + ctrl.reflow(); + } + } + + dirtyCtrls = {}; + }, document.body); + } + } + }, + + /** + * Removes the specified control from the automatic reflow. This will happen when for example the user + * manually triggers a reflow. + * + * @method remove + * @param {tinymce.ui.Control} ctrl Control to remove from queue. + */ + remove: function(ctrl) { + if (dirtyCtrls[ctrl._id]) { + delete dirtyCtrls[ctrl._id]; + } + } + }; + } + ); + + /** + * Control.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /*eslint consistent-this:0 */ + + /** + * This is the base class for all controls and containers. All UI control instances inherit + * from this one as it has the base logic needed by all of them. + * + * @class tinymce.ui.Control + */ + define( + 'tinymce.core.ui.Control', [ + "tinymce.core.util.Class", + "tinymce.core.util.Tools", + "tinymce.core.util.EventDispatcher", + "tinymce.core.data.ObservableObject", + "tinymce.core.ui.Collection", + "tinymce.core.ui.DomUtils", + "tinymce.core.dom.DomQuery", + "tinymce.core.ui.BoxUtils", + "tinymce.core.ui.ClassList", + "tinymce.core.ui.ReflowQueue" + ], + function(Class, Tools, EventDispatcher, ObservableObject, Collection, DomUtils, $, BoxUtils, ClassList, ReflowQueue) { + "use strict"; + + var hasMouseWheelEventSupport = "onmousewheel" in document; + var hasWheelEventSupport = false; + var classPrefix = "mce-"; + var Control, idCounter = 0; + + var proto = { + Statics: { + classPrefix: classPrefix + }, + + isRtl: function() { + return Control.rtl; + }, + + /** + * Class/id prefix to use for all controls. + * + * @final + * @field {String} classPrefix + */ + classPrefix: classPrefix, + + /** + * Constructs a new control instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {String} style Style CSS properties to add. + * @setting {String} border Border box values example: 1 1 1 1 + * @setting {String} padding Padding box values example: 1 1 1 1 + * @setting {String} margin Margin box values example: 1 1 1 1 + * @setting {Number} minWidth Minimal width for the control. + * @setting {Number} minHeight Minimal height for the control. + * @setting {String} classes Space separated list of classes to add. + * @setting {String} role WAI-ARIA role to use for control. + * @setting {Boolean} hidden Is the control hidden by default. + * @setting {Boolean} disabled Is the control disabled by default. + * @setting {String} name Name of the control instance. + */ + init: function(settings) { + var self = this, + classes, defaultClasses; + + function applyClasses(classes) { + var i; + + classes = classes.split(' '); + for (i = 0; i < classes.length; i++) { + self.classes.add(classes[i]); + } + } + + self.settings = settings = Tools.extend({}, self.Defaults, settings); + + // Initial states + self._id = settings.id || ('mceu_' + (idCounter++)); + self._aria = { + role: settings.role + }; + self._elmCache = {}; + self.$ = $; + + self.state = new ObservableObject({ + visible: true, + active: false, + disabled: false, + value: '' + }); + + self.data = new ObservableObject(settings.data); + + self.classes = new ClassList(function() { + if (self.state.get('rendered')) { + self.getEl().className = this.toString(); + } + }); + self.classes.prefix = self.classPrefix; + + // Setup classes + classes = settings.classes; + if (classes) { + if (self.Defaults) { + defaultClasses = self.Defaults.classes; + + if (defaultClasses && classes != defaultClasses) { + applyClasses(defaultClasses); + } + } + + applyClasses(classes); + } + + Tools.each('title text name visible disabled active value'.split(' '), function(name) { + if (name in settings) { + self[name](settings[name]); + } + }); + + self.on('click', function() { + if (self.disabled()) { + return false; + } + }); + + /** + * Name/value object with settings for the current control. + * + * @field {Object} settings + */ + self.settings = settings; + + self.borderBox = BoxUtils.parseBox(settings.border); + self.paddingBox = BoxUtils.parseBox(settings.padding); + self.marginBox = BoxUtils.parseBox(settings.margin); + + if (settings.hidden) { + self.hide(); + } + }, + + // Will generate getter/setter methods for these properties + Properties: 'parent,name', + + /** + * Returns the root element to render controls into. + * + * @method getContainerElm + * @return {Element} HTML DOM element to render into. + */ + getContainerElm: function() { + return DomUtils.getContainer(); + }, + + /** + * Returns a control instance for the current DOM element. + * + * @method getParentCtrl + * @param {Element} elm HTML dom element to get parent control from. + * @return {tinymce.ui.Control} Control instance or undefined. + */ + getParentCtrl: function(elm) { + var ctrl, lookup = this.getRoot().controlIdLookup; + + while (elm && lookup) { + ctrl = lookup[elm.id]; + if (ctrl) { + break; + } + + elm = elm.parentNode; + } + + return ctrl; + }, + + /** + * Initializes the current controls layout rect. + * This will be executed by the layout managers to determine the + * default minWidth/minHeight etc. + * + * @method initLayoutRect + * @return {Object} Layout rect instance. + */ + initLayoutRect: function() { + var self = this, + settings = self.settings, + borderBox, layoutRect; + var elm = self.getEl(), + width, height, minWidth, minHeight, autoResize; + var startMinWidth, startMinHeight, initialSize; + + // Measure the current element + borderBox = self.borderBox = self.borderBox || BoxUtils.measureBox(elm, 'border'); + self.paddingBox = self.paddingBox || BoxUtils.measureBox(elm, 'padding'); + self.marginBox = self.marginBox || BoxUtils.measureBox(elm, 'margin'); + initialSize = DomUtils.getSize(elm); + + // Setup minWidth/minHeight and width/height + startMinWidth = settings.minWidth; + startMinHeight = settings.minHeight; + minWidth = startMinWidth || initialSize.width; + minHeight = startMinHeight || initialSize.height; + width = settings.width; + height = settings.height; + autoResize = settings.autoResize; + autoResize = typeof autoResize != "undefined" ? autoResize : !width && !height; + + width = width || minWidth; + height = height || minHeight; + + var deltaW = borderBox.left + borderBox.right; + var deltaH = borderBox.top + borderBox.bottom; + + var maxW = settings.maxWidth || 0xFFFF; + var maxH = settings.maxHeight || 0xFFFF; + + // Setup initial layout rect + self._layoutRect = layoutRect = { + x: settings.x || 0, + y: settings.y || 0, + w: width, + h: height, + deltaW: deltaW, + deltaH: deltaH, + contentW: width - deltaW, + contentH: height - deltaH, + innerW: width - deltaW, + innerH: height - deltaH, + startMinWidth: startMinWidth || 0, + startMinHeight: startMinHeight || 0, + minW: Math.min(minWidth, maxW), + minH: Math.min(minHeight, maxH), + maxW: maxW, + maxH: maxH, + autoResize: autoResize, + scrollW: 0 + }; + + self._lastLayoutRect = {}; + + return layoutRect; + }, + + /** + * Getter/setter for the current layout rect. + * + * @method layoutRect + * @param {Object} [newRect] Optional new layout rect. + * @return {tinymce.ui.Control/Object} Current control or rect object. + */ + layoutRect: function(newRect) { + var self = this, + curRect = self._layoutRect, + lastLayoutRect, size, deltaWidth, deltaHeight, undef, repaintControls; + + // Initialize default layout rect + if (!curRect) { + curRect = self.initLayoutRect(); + } + + // Set new rect values + if (newRect) { + // Calc deltas between inner and outer sizes + deltaWidth = curRect.deltaW; + deltaHeight = curRect.deltaH; + + // Set x position + if (newRect.x !== undef) { + curRect.x = newRect.x; + } + + // Set y position + if (newRect.y !== undef) { + curRect.y = newRect.y; + } + + // Set minW + if (newRect.minW !== undef) { + curRect.minW = newRect.minW; + } + + // Set minH + if (newRect.minH !== undef) { + curRect.minH = newRect.minH; + } + + // Set new width and calculate inner width + size = newRect.w; + if (size !== undef) { + size = size < curRect.minW ? curRect.minW : size; + size = size > curRect.maxW ? curRect.maxW : size; + curRect.w = size; + curRect.innerW = size - deltaWidth; + } + + // Set new height and calculate inner height + size = newRect.h; + if (size !== undef) { + size = size < curRect.minH ? curRect.minH : size; + size = size > curRect.maxH ? curRect.maxH : size; + curRect.h = size; + curRect.innerH = size - deltaHeight; + } + + // Set new inner width and calculate width + size = newRect.innerW; + if (size !== undef) { + size = size < curRect.minW - deltaWidth ? curRect.minW - deltaWidth : size; + size = size > curRect.maxW - deltaWidth ? curRect.maxW - deltaWidth : size; + curRect.innerW = size; + curRect.w = size + deltaWidth; + } + + // Set new height and calculate inner height + size = newRect.innerH; + if (size !== undef) { + size = size < curRect.minH - deltaHeight ? curRect.minH - deltaHeight : size; + size = size > curRect.maxH - deltaHeight ? curRect.maxH - deltaHeight : size; + curRect.innerH = size; + curRect.h = size + deltaHeight; + } + + // Set new contentW + if (newRect.contentW !== undef) { + curRect.contentW = newRect.contentW; + } + + // Set new contentH + if (newRect.contentH !== undef) { + curRect.contentH = newRect.contentH; + } + + // Compare last layout rect with the current one to see if we need to repaint or not + lastLayoutRect = self._lastLayoutRect; + if (lastLayoutRect.x !== curRect.x || lastLayoutRect.y !== curRect.y || + lastLayoutRect.w !== curRect.w || lastLayoutRect.h !== curRect.h) { + repaintControls = Control.repaintControls; + + if (repaintControls) { + if (repaintControls.map && !repaintControls.map[self._id]) { + repaintControls.push(self); + repaintControls.map[self._id] = true; + } + } + + lastLayoutRect.x = curRect.x; + lastLayoutRect.y = curRect.y; + lastLayoutRect.w = curRect.w; + lastLayoutRect.h = curRect.h; + } + + return self; + } + + return curRect; + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this, + style, bodyStyle, bodyElm, rect, borderBox; + var borderW, borderH, lastRepaintRect, round, value; + + // Use Math.round on all values on IE < 9 + round = !document.createRange ? Math.round : function(value) { + return value; + }; + + style = self.getEl().style; + rect = self._layoutRect; + lastRepaintRect = self._lastRepaintRect || {}; + + borderBox = self.borderBox; + borderW = borderBox.left + borderBox.right; + borderH = borderBox.top + borderBox.bottom; + + if (rect.x !== lastRepaintRect.x) { + style.left = round(rect.x) + 'px'; + lastRepaintRect.x = rect.x; + } + + if (rect.y !== lastRepaintRect.y) { + style.top = round(rect.y) + 'px'; + lastRepaintRect.y = rect.y; + } + + if (rect.w !== lastRepaintRect.w) { + value = round(rect.w - borderW); + style.width = (value >= 0 ? value : 0) + 'px'; + lastRepaintRect.w = rect.w; + } + + if (rect.h !== lastRepaintRect.h) { + value = round(rect.h - borderH); + style.height = (value >= 0 ? value : 0) + 'px'; + lastRepaintRect.h = rect.h; + } + + // Update body if needed + if (self._hasBody && rect.innerW !== lastRepaintRect.innerW) { + value = round(rect.innerW); + + bodyElm = self.getEl('body'); + if (bodyElm) { + bodyStyle = bodyElm.style; + bodyStyle.width = (value >= 0 ? value : 0) + 'px'; + } + + lastRepaintRect.innerW = rect.innerW; + } + + if (self._hasBody && rect.innerH !== lastRepaintRect.innerH) { + value = round(rect.innerH); + + bodyElm = bodyElm || self.getEl('body'); + if (bodyElm) { + bodyStyle = bodyStyle || bodyElm.style; + bodyStyle.height = (value >= 0 ? value : 0) + 'px'; + } + + lastRepaintRect.innerH = rect.innerH; + } + + self._lastRepaintRect = lastRepaintRect; + self.fire('repaint', {}, false); + }, + + /** + * Updates the controls layout rect by re-measuing it. + */ + updateLayoutRect: function() { + var self = this; + + self.parent()._lastRect = null; + + DomUtils.css(self.getEl(), { + width: '', + height: '' + }); + + self._layoutRect = self._lastRepaintRect = self._lastLayoutRect = null; + self.initLayoutRect(); + }, + + /** + * Binds a callback to the specified event. This event can both be + * native browser events like "click" or custom ones like PostRender. + * + * The callback function will be passed a DOM event like object that enables yout do stop propagation. + * + * @method on + * @param {String} name Name of the event to bind. For example "click". + * @param {String/function} callback Callback function to execute ones the event occurs. + * @return {tinymce.ui.Control} Current control object. + */ + on: function(name, callback) { + var self = this; + + function resolveCallbackName(name) { + var callback, scope; + + if (typeof name != 'string') { + return name; + } + + return function(e) { + if (!callback) { + self.parentsAndSelf().each(function(ctrl) { + var callbacks = ctrl.settings.callbacks; + + if (callbacks && (callback = callbacks[name])) { + scope = ctrl; + return false; + } + }); + } + + if (!callback) { + e.action = name; + this.fire('execute', e); + return; + } + + return callback.call(scope, e); + }; + } + + getEventDispatcher(self).on(name, resolveCallbackName(callback)); + + return self; + }, + + /** + * Unbinds the specified event and optionally a specific callback. If you omit the name + * parameter all event handlers will be removed. If you omit the callback all event handles + * by the specified name will be removed. + * + * @method off + * @param {String} [name] Name for the event to unbind. + * @param {function} [callback] Callback function to unbind. + * @return {tinymce.ui.Control} Current control object. + */ + off: function(name, callback) { + getEventDispatcher(this).off(name, callback); + return this; + }, + + /** + * Fires the specified event by name and arguments on the control. This will execute all + * bound event handlers. + * + * @method fire + * @param {String} name Name of the event to fire. + * @param {Object} [args] Arguments to pass to the event. + * @param {Boolean} [bubble] Value to control bubbling. Defaults to true. + * @return {Object} Current arguments object. + */ + fire: function(name, args, bubble) { + var self = this; + + args = args || {}; + + if (!args.control) { + args.control = self; + } + + args = getEventDispatcher(self).fire(name, args); + + // Bubble event up to parents + if (bubble !== false && self.parent) { + var parent = self.parent(); + while (parent && !args.isPropagationStopped()) { + parent.fire(name, args, false); + parent = parent.parent(); + } + } + + return args; + }, + + /** + * Returns true/false if the specified event has any listeners. + * + * @method hasEventListeners + * @param {String} name Name of the event to check for. + * @return {Boolean} True/false state if the event has listeners. + */ + hasEventListeners: function(name) { + return getEventDispatcher(this).has(name); + }, + + /** + * Returns a control collection with all parent controls. + * + * @method parents + * @param {String} selector Optional selector expression to find parents. + * @return {tinymce.ui.Collection} Collection with all parent controls. + */ + parents: function(selector) { + var self = this, + ctrl, parents = new Collection(); + + // Add each parent to collection + for (ctrl = self.parent(); ctrl; ctrl = ctrl.parent()) { + parents.add(ctrl); + } + + // Filter away everything that doesn't match the selector + if (selector) { + parents = parents.filter(selector); + } + + return parents; + }, + + /** + * Returns the current control and it's parents. + * + * @method parentsAndSelf + * @param {String} selector Optional selector expression to find parents. + * @return {tinymce.ui.Collection} Collection with all parent controls. + */ + parentsAndSelf: function(selector) { + return new Collection(this).add(this.parents(selector)); + }, + + /** + * Returns the control next to the current control. + * + * @method next + * @return {tinymce.ui.Control} Next control instance. + */ + next: function() { + var parentControls = this.parent().items(); + + return parentControls[parentControls.indexOf(this) + 1]; + }, + + /** + * Returns the control previous to the current control. + * + * @method prev + * @return {tinymce.ui.Control} Previous control instance. + */ + prev: function() { + var parentControls = this.parent().items(); + + return parentControls[parentControls.indexOf(this) - 1]; + }, + + /** + * Sets the inner HTML of the control element. + * + * @method innerHtml + * @param {String} html Html string to set as inner html. + * @return {tinymce.ui.Control} Current control object. + */ + innerHtml: function(html) { + this.$el.html(html); + return this; + }, + + /** + * Returns the control DOM element or sub element. + * + * @method getEl + * @param {String} [suffix] Suffix to get element by. + * @return {Element} HTML DOM element for the current control or it's children. + */ + getEl: function(suffix) { + var id = suffix ? this._id + '-' + suffix : this._id; + + if (!this._elmCache[id]) { + this._elmCache[id] = $('#' + id)[0]; + } + + return this._elmCache[id]; + }, + + /** + * Sets the visible state to true. + * + * @method show + * @return {tinymce.ui.Control} Current control instance. + */ + show: function() { + return this.visible(true); + }, + + /** + * Sets the visible state to false. + * + * @method hide + * @return {tinymce.ui.Control} Current control instance. + */ + hide: function() { + return this.visible(false); + }, + + /** + * Focuses the current control. + * + * @method focus + * @return {tinymce.ui.Control} Current control instance. + */ + focus: function() { + try { + this.getEl().focus(); + } catch (ex) { + // Ignore IE error + } + + return this; + }, + + /** + * Blurs the current control. + * + * @method blur + * @return {tinymce.ui.Control} Current control instance. + */ + blur: function() { + this.getEl().blur(); + + return this; + }, + + /** + * Sets the specified aria property. + * + * @method aria + * @param {String} name Name of the aria property to set. + * @param {String} value Value of the aria property. + * @return {tinymce.ui.Control} Current control instance. + */ + aria: function(name, value) { + var self = this, + elm = self.getEl(self.ariaTarget); + + if (typeof value === "undefined") { + return self._aria[name]; + } + + self._aria[name] = value; + + if (self.state.get('rendered')) { + elm.setAttribute(name == 'role' ? name : 'aria-' + name, value); + } + + return self; + }, + + /** + * Encodes the specified string with HTML entities. It will also + * translate the string to different languages. + * + * @method encode + * @param {String/Object/Array} text Text to entity encode. + * @param {Boolean} [translate=true] False if the contents shouldn't be translated. + * @return {String} Encoded and possible traslated string. + */ + encode: function(text, translate) { + if (translate !== false) { + text = this.translate(text); + } + + return (text || '').replace(/[&<>"]/g, function(match) { + return '&#' + match.charCodeAt(0) + ';'; + }); + }, + + /** + * Returns the translated string. + * + * @method translate + * @param {String} text Text to translate. + * @return {String} Translated string or the same as the input. + */ + translate: function(text) { + return Control.translate ? Control.translate(text) : text; + }, + + /** + * Adds items before the current control. + * + * @method before + * @param {Array/tinymce.ui.Collection} items Array of items to prepend before this control. + * @return {tinymce.ui.Control} Current control instance. + */ + before: function(items) { + var self = this, + parent = self.parent(); + + if (parent) { + parent.insert(items, parent.items().indexOf(self), true); + } + + return self; + }, + + /** + * Adds items after the current control. + * + * @method after + * @param {Array/tinymce.ui.Collection} items Array of items to append after this control. + * @return {tinymce.ui.Control} Current control instance. + */ + after: function(items) { + var self = this, + parent = self.parent(); + + if (parent) { + parent.insert(items, parent.items().indexOf(self)); + } + + return self; + }, + + /** + * Removes the current control from DOM and from UI collections. + * + * @method remove + * @return {tinymce.ui.Control} Current control instance. + */ + remove: function() { + var self = this, + elm = self.getEl(), + parent = self.parent(), + newItems, i; + + if (self.items) { + var controls = self.items().toArray(); + i = controls.length; + while (i--) { + controls[i].remove(); + } + } + + if (parent && parent.items) { + newItems = []; + + parent.items().each(function(item) { + if (item !== self) { + newItems.push(item); + } + }); + + parent.items().set(newItems); + parent._lastRect = null; + } + + if (self._eventsRoot && self._eventsRoot == self) { + $(elm).off(); + } + + var lookup = self.getRoot().controlIdLookup; + if (lookup) { + delete lookup[self._id]; + } + + if (elm && elm.parentNode) { + elm.parentNode.removeChild(elm); + } + + self.state.set('rendered', false); + self.state.destroy(); + + self.fire('remove'); + + return self; + }, + + /** + * Renders the control before the specified element. + * + * @method renderBefore + * @param {Element} elm Element to render before. + * @return {tinymce.ui.Control} Current control instance. + */ + renderBefore: function(elm) { + $(elm).before(this.renderHtml()); + this.postRender(); + return this; + }, + + /** + * Renders the control to the specified element. + * + * @method renderBefore + * @param {Element} elm Element to render to. + * @return {tinymce.ui.Control} Current control instance. + */ + renderTo: function(elm) { + $(elm || this.getContainerElm()).append(this.renderHtml()); + this.postRender(); + return this; + }, + + preRender: function() {}, + + render: function() {}, + + renderHtml: function() { + return '<div id="' + this._id + '" class="' + this.classes + '"></div>'; + }, + + /** + * Post render method. Called after the control has been rendered to the target. + * + * @method postRender + * @return {tinymce.ui.Control} Current control instance. + */ + postRender: function() { + var self = this, + settings = self.settings, + elm, box, parent, name, parentEventsRoot; + + self.$el = $(self.getEl()); + self.state.set('rendered', true); + + // Bind on<event> settings + for (name in settings) { + if (name.indexOf("on") === 0) { + self.on(name.substr(2), settings[name]); + } + } + + if (self._eventsRoot) { + for (parent = self.parent(); !parentEventsRoot && parent; parent = parent.parent()) { + parentEventsRoot = parent._eventsRoot; + } + + if (parentEventsRoot) { + for (name in parentEventsRoot._nativeEvents) { + self._nativeEvents[name] = true; + } + } + } + + bindPendingEvents(self); + + if (settings.style) { + elm = self.getEl(); + if (elm) { + elm.setAttribute('style', settings.style); + elm.style.cssText = settings.style; + } + } + + if (self.settings.border) { + box = self.borderBox; + self.$el.css({ + 'border-top-width': box.top, + 'border-right-width': box.right, + 'border-bottom-width': box.bottom, + 'border-left-width': box.left + }); + } + + // Add instance to lookup + var root = self.getRoot(); + if (!root.controlIdLookup) { + root.controlIdLookup = {}; + } + + root.controlIdLookup[self._id] = self; + + for (var key in self._aria) { + self.aria(key, self._aria[key]); + } + + if (self.state.get('visible') === false) { + self.getEl().style.display = 'none'; + } + + self.bindStates(); + + self.state.on('change:visible', function(e) { + var state = e.value, + parentCtrl; + + if (self.state.get('rendered')) { + self.getEl().style.display = state === false ? 'none' : ''; + + // Need to force a reflow here on IE 8 + self.getEl().getBoundingClientRect(); + } + + // Parent container needs to reflow + parentCtrl = self.parent(); + if (parentCtrl) { + parentCtrl._lastRect = null; + } + + self.fire(state ? 'show' : 'hide'); + + ReflowQueue.add(self); + }); + + self.fire('postrender', {}, false); + }, + + bindStates: function() {}, + + /** + * Scrolls the current control into view. + * + * @method scrollIntoView + * @param {String} align Alignment in view top|center|bottom. + * @return {tinymce.ui.Control} Current control instance. + */ + scrollIntoView: function(align) { + function getOffset(elm, rootElm) { + var x, y, parent = elm; + + x = y = 0; + while (parent && parent != rootElm && parent.nodeType) { + x += parent.offsetLeft || 0; + y += parent.offsetTop || 0; + parent = parent.offsetParent; + } + + return { + x: x, + y: y + }; + } + + var elm = this.getEl(), + parentElm = elm.parentNode; + var x, y, width, height, parentWidth, parentHeight; + var pos = getOffset(elm, parentElm); + + x = pos.x; + y = pos.y; + width = elm.offsetWidth; + height = elm.offsetHeight; + parentWidth = parentElm.clientWidth; + parentHeight = parentElm.clientHeight; + + if (align == "end") { + x -= parentWidth - width; + y -= parentHeight - height; + } else if (align == "center") { + x -= (parentWidth / 2) - (width / 2); + y -= (parentHeight / 2) - (height / 2); + } + + parentElm.scrollLeft = x; + parentElm.scrollTop = y; + + return this; + }, + + getRoot: function() { + var ctrl = this, + rootControl, parents = []; + + while (ctrl) { + if (ctrl.rootControl) { + rootControl = ctrl.rootControl; + break; + } + + parents.push(ctrl); + rootControl = ctrl; + ctrl = ctrl.parent(); + } + + if (!rootControl) { + rootControl = this; + } + + var i = parents.length; + while (i--) { + parents[i].rootControl = rootControl; + } + + return rootControl; + }, + + /** + * Reflows the current control and it's parents. + * This should be used after you for example append children to the current control so + * that the layout managers know that they need to reposition everything. + * + * @example + * container.append({type: 'button', text: 'My button'}).reflow(); + * + * @method reflow + * @return {tinymce.ui.Control} Current control instance. + */ + reflow: function() { + ReflowQueue.remove(this); + + var parent = this.parent(); + if (parent && parent._layout && !parent._layout.isNative()) { + parent.reflow(); + } + + return this; + } + + /** + * Sets/gets the parent container for the control. + * + * @method parent + * @param {tinymce.ui.Container} parent Optional parent to set. + * @return {tinymce.ui.Control} Parent control or the current control on a set action. + */ + // parent: function(parent) {} -- Generated + + /** + * Sets/gets the text for the control. + * + * @method text + * @param {String} value Value to set to control. + * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get. + */ + // text: function(value) {} -- Generated + + /** + * Sets/gets the disabled state on the control. + * + * @method disabled + * @param {Boolean} state Value to set to control. + * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get. + */ + // disabled: function(state) {} -- Generated + + /** + * Sets/gets the active for the control. + * + * @method active + * @param {Boolean} state Value to set to control. + * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get. + */ + // active: function(state) {} -- Generated + + /** + * Sets/gets the name for the control. + * + * @method name + * @param {String} value Value to set to control. + * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get. + */ + // name: function(value) {} -- Generated + + /** + * Sets/gets the title for the control. + * + * @method title + * @param {String} value Value to set to control. + * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get. + */ + // title: function(value) {} -- Generated + + /** + * Sets/gets the visible for the control. + * + * @method visible + * @param {Boolean} state Value to set to control. + * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get. + */ + // visible: function(value) {} -- Generated + }; + + /** + * Setup state properties. + */ + Tools.each('text title visible disabled active value'.split(' '), function(name) { + proto[name] = function(value) { + if (arguments.length === 0) { + return this.state.get(name); + } + + if (typeof value != "undefined") { + this.state.set(name, value); + } + + return this; + }; + }); + + Control = Class.extend(proto); + + function getEventDispatcher(obj) { + if (!obj._eventDispatcher) { + obj._eventDispatcher = new EventDispatcher({ + scope: obj, + toggleEvent: function(name, state) { + if (state && EventDispatcher.isNative(name)) { + if (!obj._nativeEvents) { + obj._nativeEvents = {}; + } + + obj._nativeEvents[name] = true; + + if (obj.state.get('rendered')) { + bindPendingEvents(obj); + } + } + } + }); + } + + return obj._eventDispatcher; + } + + function bindPendingEvents(eventCtrl) { + var i, l, parents, eventRootCtrl, nativeEvents, name; + + function delegate(e) { + var control = eventCtrl.getParentCtrl(e.target); + + if (control) { + control.fire(e.type, e); + } + } + + function mouseLeaveHandler() { + var ctrl = eventRootCtrl._lastHoverCtrl; + + if (ctrl) { + ctrl.fire("mouseleave", { + target: ctrl.getEl() + }); + + ctrl.parents().each(function(ctrl) { + ctrl.fire("mouseleave", { + target: ctrl.getEl() + }); + }); + + eventRootCtrl._lastHoverCtrl = null; + } + } + + function mouseEnterHandler(e) { + var ctrl = eventCtrl.getParentCtrl(e.target), + lastCtrl = eventRootCtrl._lastHoverCtrl, + idx = 0, + i, parents, lastParents; + + // Over on a new control + if (ctrl !== lastCtrl) { + eventRootCtrl._lastHoverCtrl = ctrl; + + parents = ctrl.parents().toArray().reverse(); + parents.push(ctrl); + + if (lastCtrl) { + lastParents = lastCtrl.parents().toArray().reverse(); + lastParents.push(lastCtrl); + + for (idx = 0; idx < lastParents.length; idx++) { + if (parents[idx] !== lastParents[idx]) { + break; + } + } + + for (i = lastParents.length - 1; i >= idx; i--) { + lastCtrl = lastParents[i]; + lastCtrl.fire("mouseleave", { + target: lastCtrl.getEl() + }); + } + } + + for (i = idx; i < parents.length; i++) { + ctrl = parents[i]; + ctrl.fire("mouseenter", { + target: ctrl.getEl() + }); + } + } + } + + function fixWheelEvent(e) { + e.preventDefault(); + + if (e.type == "mousewheel") { + e.deltaY = -1 / 40 * e.wheelDelta; + + if (e.wheelDeltaX) { + e.deltaX = -1 / 40 * e.wheelDeltaX; + } + } else { + e.deltaX = 0; + e.deltaY = e.detail; + } + + e = eventCtrl.fire("wheel", e); + } + + nativeEvents = eventCtrl._nativeEvents; + if (nativeEvents) { + // Find event root element if it exists + parents = eventCtrl.parents().toArray(); + parents.unshift(eventCtrl); + for (i = 0, l = parents.length; !eventRootCtrl && i < l; i++) { + eventRootCtrl = parents[i]._eventsRoot; + } + + // Event root wasn't found the use the root control + if (!eventRootCtrl) { + eventRootCtrl = parents[parents.length - 1] || eventCtrl; + } + + // Set the eventsRoot property on children that didn't have it + eventCtrl._eventsRoot = eventRootCtrl; + for (l = i, i = 0; i < l; i++) { + parents[i]._eventsRoot = eventRootCtrl; + } + + var eventRootDelegates = eventRootCtrl._delegates; + if (!eventRootDelegates) { + eventRootDelegates = eventRootCtrl._delegates = {}; + } + + // Bind native event delegates + for (name in nativeEvents) { + if (!nativeEvents) { + return false; + } + + if (name === "wheel" && !hasWheelEventSupport) { + if (hasMouseWheelEventSupport) { + $(eventCtrl.getEl()).on("mousewheel", fixWheelEvent); + } else { + $(eventCtrl.getEl()).on("DOMMouseScroll", fixWheelEvent); + } + + continue; + } + + // Special treatment for mousenter/mouseleave since these doesn't bubble + if (name === "mouseenter" || name === "mouseleave") { + // Fake mousenter/mouseleave + if (!eventRootCtrl._hasMouseEnter) { + $(eventRootCtrl.getEl()).on("mouseleave", mouseLeaveHandler).on("mouseover", mouseEnterHandler); + eventRootCtrl._hasMouseEnter = 1; + } + } else if (!eventRootDelegates[name]) { + $(eventRootCtrl.getEl()).on(name, delegate); + eventRootDelegates[name] = true; + } + + // Remove the event once it's bound + nativeEvents[name] = false; + } + } + } + + return Control; + } + ); + + /** + * Factory.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class is a factory for control instances. This enables you + * to create instances of controls without having to require the UI controls directly. + * + * It also allow you to override or add new control types. + * + * @class tinymce.ui.Factory + */ + define( + 'tinymce.core.ui.Factory', [], + function() { + "use strict"; + + var types = {}; + + return { + /** + * Adds a new control instance type to the factory. + * + * @method add + * @param {String} type Type name for example "button". + * @param {function} typeClass Class type function. + */ + add: function(type, typeClass) { + types[type.toLowerCase()] = typeClass; + }, + + /** + * Returns true/false if the specified type exists or not. + * + * @method has + * @param {String} type Type to look for. + * @return {Boolean} true/false if the control by name exists. + */ + has: function(type) { + return !!types[type.toLowerCase()]; + }, + + /** + * Creates a new control instance based on the settings provided. The instance created will be + * based on the specified type property it can also create whole structures of components out of + * the specified JSON object. + * + * @example + * tinymce.ui.Factory.create({ + * type: 'button', + * text: 'Hello world!' + * }); + * + * @method create + * @param {Object/String} settings Name/Value object with items used to create the type. + * @return {tinymce.ui.Control} Control instance based on the specified type. + */ + create: function(type, settings) { + var ControlType; + + // If string is specified then use it as the type + if (typeof type == 'string') { + settings = settings || {}; + settings.type = type; + } else { + settings = type; + type = settings.type; + } + + // Find control type + type = type.toLowerCase(); + ControlType = types[type]; + + // #if debug + + if (!ControlType) { + throw new Error("Could not find control by type: " + type); + } + + // #endif + + ControlType = new ControlType(settings); + ControlType.type = type; // Set the type on the instance, this will be used by the Selector engine + + return ControlType; + } + }; + } + ); + /** + * KeyboardNavigation.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class handles keyboard navigation of controls and elements. + * + * @class tinymce.ui.KeyboardNavigation + */ + define( + 'tinymce.core.ui.KeyboardNavigation', [], + function() { + "use strict"; + + var hasTabstopData = function(elm) { + return elm.getAttribute('data-mce-tabstop') ? true : false; + }; + + /** + * This class handles all keyboard navigation for WAI-ARIA support. Each root container + * gets an instance of this class. + * + * @constructor + */ + return function(settings) { + var root = settings.root, + focusedElement, focusedControl; + + function isElement(node) { + return node && node.nodeType === 1; + } + + try { + focusedElement = document.activeElement; + } catch (ex) { + // IE sometimes fails to return a proper element + focusedElement = document.body; + } + + focusedControl = root.getParentCtrl(focusedElement); + + /** + * Returns the currently focused elements wai aria role of the currently + * focused element or specified element. + * + * @private + * @param {Element} elm Optional element to get role from. + * @return {String} Role of specified element. + */ + function getRole(elm) { + elm = elm || focusedElement; + + if (isElement(elm)) { + return elm.getAttribute('role'); + } + + return null; + } + + /** + * Returns the wai role of the parent element of the currently + * focused element or specified element. + * + * @private + * @param {Element} elm Optional element to get parent role from. + * @return {String} Role of the first parent that has a role. + */ + function getParentRole(elm) { + var role, parent = elm || focusedElement; + + while ((parent = parent.parentNode)) { + if ((role = getRole(parent))) { + return role; + } + } + } + + /** + * Returns a wai aria property by name for example aria-selected. + * + * @private + * @param {String} name Name of the aria property to get for example "disabled". + * @return {String} Aria property value. + */ + function getAriaProp(name) { + var elm = focusedElement; + + if (isElement(elm)) { + return elm.getAttribute('aria-' + name); + } + } + + /** + * Is the element a text input element or not. + * + * @private + * @param {Element} elm Element to check if it's an text input element or not. + * @return {Boolean} True/false if the element is a text element or not. + */ + function isTextInputElement(elm) { + var tagName = elm.tagName.toUpperCase(); + + // Notice: since type can be "email" etc we don't check the type + // So all input elements gets treated as text input elements + return tagName == "INPUT" || tagName == "TEXTAREA" || tagName == "SELECT"; + } + + /** + * Returns true/false if the specified element can be focused or not. + * + * @private + * @param {Element} elm DOM element to check if it can be focused or not. + * @return {Boolean} True/false if the element can have focus. + */ + function canFocus(elm) { + if (isTextInputElement(elm) && !elm.hidden) { + return true; + } + + if (hasTabstopData(elm)) { + return true; + } + + if (/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell|slider)$/.test(getRole(elm))) { + return true; + } + + return false; + } + + /** + * Returns an array of focusable visible elements within the specified container element. + * + * @private + * @param {Element} elm DOM element to find focusable elements within. + * @return {Array} Array of focusable elements. + */ + function getFocusElements(elm) { + var elements = []; + + function collect(elm) { + if (elm.nodeType != 1 || elm.style.display == 'none' || elm.disabled) { + return; + } + + if (canFocus(elm)) { + elements.push(elm); + } + + for (var i = 0; i < elm.childNodes.length; i++) { + collect(elm.childNodes[i]); + } + } + + collect(elm || root.getEl()); + + return elements; + } + + /** + * Returns the navigation root control for the specified control. The navigation root + * is the control that the keyboard navigation gets scoped to for example a menubar or toolbar group. + * It will look for parents of the specified target control or the currently focused control if this option is omitted. + * + * @private + * @param {tinymce.ui.Control} targetControl Optional target control to find root of. + * @return {tinymce.ui.Control} Navigation root control. + */ + function getNavigationRoot(targetControl) { + var navigationRoot, controls; + + targetControl = targetControl || focusedControl; + controls = targetControl.parents().toArray(); + controls.unshift(targetControl); + + for (var i = 0; i < controls.length; i++) { + navigationRoot = controls[i]; + + if (navigationRoot.settings.ariaRoot) { + break; + } + } + + return navigationRoot; + } + + /** + * Focuses the first item in the specified targetControl element or the last aria index if the + * navigation root has the ariaRemember option enabled. + * + * @private + * @param {tinymce.ui.Control} targetControl Target control to focus the first item in. + */ + function focusFirst(targetControl) { + var navigationRoot = getNavigationRoot(targetControl); + var focusElements = getFocusElements(navigationRoot.getEl()); + + if (navigationRoot.settings.ariaRemember && "lastAriaIndex" in navigationRoot) { + moveFocusToIndex(navigationRoot.lastAriaIndex, focusElements); + } else { + moveFocusToIndex(0, focusElements); + } + } + + /** + * Moves the focus to the specified index within the elements list. + * This will scope the index to the size of the element list if it changed. + * + * @private + * @param {Number} idx Specified index to move to. + * @param {Array} elements Array with dom elements to move focus within. + * @return {Number} Input index or a changed index if it was out of range. + */ + function moveFocusToIndex(idx, elements) { + if (idx < 0) { + idx = elements.length - 1; + } else if (idx >= elements.length) { + idx = 0; + } + + if (elements[idx]) { + elements[idx].focus(); + } + + return idx; + } + + /** + * Moves the focus forwards or backwards. + * + * @private + * @param {Number} dir Direction to move in positive means forward, negative means backwards. + * @param {Array} elements Optional array of elements to move within defaults to the current navigation roots elements. + */ + function moveFocus(dir, elements) { + var idx = -1, + navigationRoot = getNavigationRoot(); + + elements = elements || getFocusElements(navigationRoot.getEl()); + + for (var i = 0; i < elements.length; i++) { + if (elements[i] === focusedElement) { + idx = i; + } + } + + idx += dir; + navigationRoot.lastAriaIndex = moveFocusToIndex(idx, elements); + } + + /** + * Moves the focus to the left this is called by the left key. + * + * @private + */ + function left() { + var parentRole = getParentRole(); + + if (parentRole == "tablist") { + moveFocus(-1, getFocusElements(focusedElement.parentNode)); + } else if (focusedControl.parent().submenu) { + cancel(); + } else { + moveFocus(-1); + } + } + + /** + * Moves the focus to the right this is called by the right key. + * + * @private + */ + function right() { + var role = getRole(), + parentRole = getParentRole(); + + if (parentRole == "tablist") { + moveFocus(1, getFocusElements(focusedElement.parentNode)); + } else if (role == "menuitem" && parentRole == "menu" && getAriaProp('haspopup')) { + enter(); + } else { + moveFocus(1); + } + } + + /** + * Moves the focus to the up this is called by the up key. + * + * @private + */ + function up() { + moveFocus(-1); + } + + /** + * Moves the focus to the up this is called by the down key. + * + * @private + */ + function down() { + var role = getRole(), + parentRole = getParentRole(); + + if (role == "menuitem" && parentRole == "menubar") { + enter(); + } else if (role == "button" && getAriaProp('haspopup')) { + enter({ + key: 'down' + }); + } else { + moveFocus(1); + } + } + + /** + * Moves the focus to the next item or previous item depending on shift key. + * + * @private + * @param {DOMEvent} e DOM event object. + */ + function tab(e) { + var parentRole = getParentRole(); + + if (parentRole == "tablist") { + var elm = getFocusElements(focusedControl.getEl('body'))[0]; + + if (elm) { + elm.focus(); + } + } else { + moveFocus(e.shiftKey ? -1 : 1); + } + } + + /** + * Calls the cancel event on the currently focused control. This is normally done using the Esc key. + * + * @private + */ + function cancel() { + focusedControl.fire('cancel'); + } + + /** + * Calls the click event on the currently focused control. This is normally done using the Enter/Space keys. + * + * @private + * @param {Object} aria Optional aria data to pass along with the enter event. + */ + function enter(aria) { + aria = aria || {}; + focusedControl.fire('click', { + target: focusedElement, + aria: aria + }); + } + + root.on('keydown', function(e) { + function handleNonTabOrEscEvent(e, handler) { + // Ignore non tab keys for text elements + if (isTextInputElement(focusedElement) || hasTabstopData(focusedElement)) { + return; + } + + if (getRole(focusedElement) === 'slider') { + return; + } + + if (handler(e) !== false) { + e.preventDefault(); + } + } + + if (e.isDefaultPrevented()) { + return; + } + + switch (e.keyCode) { + case 37: // DOM_VK_LEFT + handleNonTabOrEscEvent(e, left); + break; + + case 39: // DOM_VK_RIGHT + handleNonTabOrEscEvent(e, right); + break; + + case 38: // DOM_VK_UP + handleNonTabOrEscEvent(e, up); + break; + + case 40: // DOM_VK_DOWN + handleNonTabOrEscEvent(e, down); + break; + + case 27: // DOM_VK_ESCAPE + cancel(); + break; + + case 14: // DOM_VK_ENTER + case 13: // DOM_VK_RETURN + case 32: // DOM_VK_SPACE + handleNonTabOrEscEvent(e, enter); + break; + + case 9: // DOM_VK_TAB + if (tab(e) !== false) { + e.preventDefault(); + } + break; + } + }); + + root.on('focusin', function(e) { + focusedElement = e.target; + focusedControl = e.control; + }); + + return { + focusFirst: focusFirst + }; + }; + } + ); + /** + * Container.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Container control. This is extended by all controls that can have + * children such as panels etc. You can also use this class directly as an + * generic container instance. The container doesn't have any specific role or style. + * + * @-x-less Container.less + * @class tinymce.ui.Container + * @extends tinymce.ui.Control + */ + define( + 'tinymce.core.ui.Container', [ + "tinymce.core.ui.Control", + "tinymce.core.ui.Collection", + "tinymce.core.ui.Selector", + "tinymce.core.ui.Factory", + "tinymce.core.ui.KeyboardNavigation", + "tinymce.core.util.Tools", + "tinymce.core.dom.DomQuery", + "tinymce.core.ui.ClassList", + "tinymce.core.ui.ReflowQueue" + ], + function(Control, Collection, Selector, Factory, KeyboardNavigation, Tools, $, ClassList, ReflowQueue) { + "use strict"; + + var selectorCache = {}; + + return Control.extend({ + /** + * Constructs a new control instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Array} items Items to add to container in JSON format or control instances. + * @setting {String} layout Layout manager by name to use. + * @setting {Object} defaults Default settings to apply to all items. + */ + init: function(settings) { + var self = this; + + self._super(settings); + settings = self.settings; + + if (settings.fixed) { + self.state.set('fixed', true); + } + + self._items = new Collection(); + + if (self.isRtl()) { + self.classes.add('rtl'); + } + + self.bodyClasses = new ClassList(function() { + if (self.state.get('rendered')) { + self.getEl('body').className = this.toString(); + } + }); + self.bodyClasses.prefix = self.classPrefix; + + self.classes.add('container'); + self.bodyClasses.add('container-body'); + + if (settings.containerCls) { + self.classes.add(settings.containerCls); + } + + self._layout = Factory.create((settings.layout || '') + 'layout'); + + if (self.settings.items) { + self.add(self.settings.items); + } else { + self.add(self.render()); + } + + // TODO: Fix this! + self._hasBody = true; + }, + + /** + * Returns a collection of child items that the container currently have. + * + * @method items + * @return {tinymce.ui.Collection} Control collection direct child controls. + */ + items: function() { + return this._items; + }, + + /** + * Find child controls by selector. + * + * @method find + * @param {String} selector Selector CSS pattern to find children by. + * @return {tinymce.ui.Collection} Control collection with child controls. + */ + find: function(selector) { + selector = selectorCache[selector] = selectorCache[selector] || new Selector(selector); + + return selector.find(this); + }, + + /** + * Adds one or many items to the current container. This will create instances of + * the object representations if needed. + * + * @method add + * @param {Array/Object/tinymce.ui.Control} items Array or item that will be added to the container. + * @return {tinymce.ui.Collection} Current collection control. + */ + add: function(items) { + var self = this; + + self.items().add(self.create(items)).parent(self); + + return self; + }, + + /** + * Focuses the current container instance. This will look + * for the first control in the container and focus that. + * + * @method focus + * @param {Boolean} keyboard Optional true/false if the focus was a keyboard focus or not. + * @return {tinymce.ui.Collection} Current instance. + */ + focus: function(keyboard) { + var self = this, + focusCtrl, keyboardNav, items; + + if (keyboard) { + keyboardNav = self.keyboardNav || self.parents().eq(-1)[0].keyboardNav; + + if (keyboardNav) { + keyboardNav.focusFirst(self); + return; + } + } + + items = self.find('*'); + + // TODO: Figure out a better way to auto focus alert dialog buttons + if (self.statusbar) { + items.add(self.statusbar.items()); + } + + items.each(function(ctrl) { + if (ctrl.settings.autofocus) { + focusCtrl = null; + return false; + } + + if (ctrl.canFocus) { + focusCtrl = focusCtrl || ctrl; + } + }); + + if (focusCtrl) { + focusCtrl.focus(); + } + + return self; + }, + + /** + * Replaces the specified child control with a new control. + * + * @method replace + * @param {tinymce.ui.Control} oldItem Old item to be replaced. + * @param {tinymce.ui.Control} newItem New item to be inserted. + */ + replace: function(oldItem, newItem) { + var ctrlElm, items = this.items(), + i = items.length; + + // Replace the item in collection + while (i--) { + if (items[i] === oldItem) { + items[i] = newItem; + break; + } + } + + if (i >= 0) { + // Remove new item from DOM + ctrlElm = newItem.getEl(); + if (ctrlElm) { + ctrlElm.parentNode.removeChild(ctrlElm); + } + + // Remove old item from DOM + ctrlElm = oldItem.getEl(); + if (ctrlElm) { + ctrlElm.parentNode.removeChild(ctrlElm); + } + } + + // Adopt the item + newItem.parent(this); + }, + + /** + * Creates the specified items. If any of the items is plain JSON style objects + * it will convert these into real tinymce.ui.Control instances. + * + * @method create + * @param {Array} items Array of items to convert into control instances. + * @return {Array} Array with control instances. + */ + create: function(items) { + var self = this, + settings, ctrlItems = []; + + // Non array structure, then force it into an array + if (!Tools.isArray(items)) { + items = [items]; + } + + // Add default type to each child control + Tools.each(items, function(item) { + if (item) { + // Construct item if needed + if (!(item instanceof Control)) { + // Name only then convert it to an object + if (typeof item == "string") { + item = { + type: item + }; + } + + // Create control instance based on input settings and default settings + settings = Tools.extend({}, self.settings.defaults, item); + item.type = settings.type = settings.type || item.type || self.settings.defaultType || + (settings.defaults ? settings.defaults.type : null); + item = Factory.create(settings); + } + + ctrlItems.push(item); + } + }); + + return ctrlItems; + }, + + /** + * Renders new control instances. + * + * @private + */ + renderNew: function() { + var self = this; + + // Render any new items + self.items().each(function(ctrl, index) { + var containerElm; + + ctrl.parent(self); + + if (!ctrl.state.get('rendered')) { + containerElm = self.getEl('body'); + + // Insert or append the item + if (containerElm.hasChildNodes() && index <= containerElm.childNodes.length - 1) { + $(containerElm.childNodes[index]).before(ctrl.renderHtml()); + } else { + $(containerElm).append(ctrl.renderHtml()); + } + + ctrl.postRender(); + ReflowQueue.add(ctrl); + } + }); + + self._layout.applyClasses(self.items().filter(':visible')); + self._lastRect = null; + + return self; + }, + + /** + * Appends new instances to the current container. + * + * @method append + * @param {Array/tinymce.ui.Collection} items Array if controls to append. + * @return {tinymce.ui.Container} Current container instance. + */ + append: function(items) { + return this.add(items).renderNew(); + }, + + /** + * Prepends new instances to the current container. + * + * @method prepend + * @param {Array/tinymce.ui.Collection} items Array if controls to prepend. + * @return {tinymce.ui.Container} Current container instance. + */ + prepend: function(items) { + var self = this; + + self.items().set(self.create(items).concat(self.items().toArray())); + + return self.renderNew(); + }, + + /** + * Inserts an control at a specific index. + * + * @method insert + * @param {Array/tinymce.ui.Collection} items Array if controls to insert. + * @param {Number} index Index to insert controls at. + * @param {Boolean} [before=false] Inserts controls before the index. + */ + insert: function(items, index, before) { + var self = this, + curItems, beforeItems, afterItems; + + items = self.create(items); + curItems = self.items(); + + if (!before && index < curItems.length - 1) { + index += 1; + } + + if (index >= 0 && index < curItems.length) { + beforeItems = curItems.slice(0, index).toArray(); + afterItems = curItems.slice(index).toArray(); + curItems.set(beforeItems.concat(items, afterItems)); + } + + return self.renderNew(); + }, + + /** + * Populates the form fields from the specified JSON data object. + * + * Control items in the form that matches the data will have it's value set. + * + * @method fromJSON + * @param {Object} data JSON data object to set control values by. + * @return {tinymce.ui.Container} Current form instance. + */ + fromJSON: function(data) { + var self = this; + + for (var name in data) { + self.find('#' + name).value(data[name]); + } + + return self; + }, + + /** + * Serializes the form into a JSON object by getting all items + * that has a name and a value. + * + * @method toJSON + * @return {Object} JSON object with form data. + */ + toJSON: function() { + var self = this, + data = {}; + + self.find('*').each(function(ctrl) { + var name = ctrl.name(), + value = ctrl.value(); + + if (name && typeof value != "undefined") { + data[name] = value; + } + }); + + return data; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + layout = self._layout, + role = this.settings.role; + + self.preRender(); + layout.preRender(self); + + return ( + '<div id="' + self._id + '" class="' + self.classes + '"' + (role ? ' role="' + this.settings.role + '"' : '') + '>' + + '<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' + + (self.settings.html || '') + layout.renderHtml(self) + + '</div>' + + '</div>' + ); + }, + + /** + * Post render method. Called after the control has been rendered to the target. + * + * @method postRender + * @return {tinymce.ui.Container} Current combobox instance. + */ + postRender: function() { + var self = this, + box; + + self.items().exec('postRender'); + self._super(); + + self._layout.postRender(self); + self.state.set('rendered', true); + + if (self.settings.style) { + self.$el.css(self.settings.style); + } + + if (self.settings.border) { + box = self.borderBox; + self.$el.css({ + 'border-top-width': box.top, + 'border-right-width': box.right, + 'border-bottom-width': box.bottom, + 'border-left-width': box.left + }); + } + + if (!self.parent()) { + self.keyboardNav = new KeyboardNavigation({ + root: self + }); + } + + return self; + }, + + /** + * Initializes the current controls layout rect. + * This will be executed by the layout managers to determine the + * default minWidth/minHeight etc. + * + * @method initLayoutRect + * @return {Object} Layout rect instance. + */ + initLayoutRect: function() { + var self = this, + layoutRect = self._super(); + + // Recalc container size by asking layout manager + self._layout.recalc(self); + + return layoutRect; + }, + + /** + * Recalculates the positions of the controls in the current container. + * This is invoked by the reflow method and shouldn't be called directly. + * + * @method recalc + */ + recalc: function() { + var self = this, + rect = self._layoutRect, + lastRect = self._lastRect; + + if (!lastRect || lastRect.w != rect.w || lastRect.h != rect.h) { + self._layout.recalc(self); + rect = self.layoutRect(); + self._lastRect = { + x: rect.x, + y: rect.y, + w: rect.w, + h: rect.h + }; + return true; + } + }, + + /** + * Reflows the current container and it's children and possible parents. + * This should be used after you for example append children to the current control so + * that the layout managers know that they need to reposition everything. + * + * @example + * container.append({type: 'button', text: 'My button'}).reflow(); + * + * @method reflow + * @return {tinymce.ui.Container} Current container instance. + */ + reflow: function() { + var i; + + ReflowQueue.remove(this); + + if (this.visible()) { + Control.repaintControls = []; + Control.repaintControls.map = {}; + + this.recalc(); + i = Control.repaintControls.length; + + while (i--) { + Control.repaintControls[i].repaint(); + } + + // TODO: Fix me! + if (this.settings.layout !== "flow" && this.settings.layout !== "stack") { + this.repaint(); + } + + Control.repaintControls = []; + } + + return this; + } + }); + } + ); + /** + * DragHelper.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Drag/drop helper class. + * + * @example + * var dragHelper = new tinymce.ui.DragHelper('mydiv', { + * start: function(evt) { + * }, + * + * drag: function(evt) { + * }, + * + * end: function(evt) { + * } + * }); + * + * @class tinymce.ui.DragHelper + */ + define( + 'tinymce.core.ui.DragHelper', [ + "tinymce.core.dom.DomQuery" + ], + function($) { + "use strict"; + + function getDocumentSize(doc) { + var documentElement, body, scrollWidth, clientWidth; + var offsetWidth, scrollHeight, clientHeight, offsetHeight, max = Math.max; + + documentElement = doc.documentElement; + body = doc.body; + + scrollWidth = max(documentElement.scrollWidth, body.scrollWidth); + clientWidth = max(documentElement.clientWidth, body.clientWidth); + offsetWidth = max(documentElement.offsetWidth, body.offsetWidth); + + scrollHeight = max(documentElement.scrollHeight, body.scrollHeight); + clientHeight = max(documentElement.clientHeight, body.clientHeight); + offsetHeight = max(documentElement.offsetHeight, body.offsetHeight); + + return { + width: scrollWidth < offsetWidth ? clientWidth : scrollWidth, + height: scrollHeight < offsetHeight ? clientHeight : scrollHeight + }; + } + + function updateWithTouchData(e) { + var keys, i; + + if (e.changedTouches) { + keys = "screenX screenY pageX pageY clientX clientY".split(' '); + for (i = 0; i < keys.length; i++) { + e[keys[i]] = e.changedTouches[0][keys[i]]; + } + } + } + + return function(id, settings) { + var $eventOverlay, doc = settings.document || document, + downButton, start, stop, drag, startX, startY; + + settings = settings || {}; + + function getHandleElm() { + return doc.getElementById(settings.handle || id); + } + + start = function(e) { + var docSize = getDocumentSize(doc), + handleElm, cursor; + + updateWithTouchData(e); + + e.preventDefault(); + downButton = e.button; + handleElm = getHandleElm(); + startX = e.screenX; + startY = e.screenY; + + // Grab cursor from handle so we can place it on overlay + if (window.getComputedStyle) { + cursor = window.getComputedStyle(handleElm, null).getPropertyValue("cursor"); + } else { + cursor = handleElm.runtimeStyle.cursor; + } + + $eventOverlay = $('<div></div>').css({ + position: "absolute", + top: 0, + left: 0, + width: docSize.width, + height: docSize.height, + zIndex: 0x7FFFFFFF, + opacity: 0.0001, + cursor: cursor + }).appendTo(doc.body); + + $(doc).on('mousemove touchmove', drag).on('mouseup touchend', stop); + + settings.start(e); + }; + + drag = function(e) { + updateWithTouchData(e); + + if (e.button !== downButton) { + return stop(e); + } + + e.deltaX = e.screenX - startX; + e.deltaY = e.screenY - startY; + + e.preventDefault(); + settings.drag(e); + }; + + stop = function(e) { + updateWithTouchData(e); + + $(doc).off('mousemove touchmove', drag).off('mouseup touchend', stop); + + $eventOverlay.remove(); + + if (settings.stop) { + settings.stop(e); + } + }; + + /** + * Destroys the drag/drop helper instance. + * + * @method destroy + */ + this.destroy = function() { + $(getHandleElm()).off(); + }; + + $(getHandleElm()).on('mousedown touchstart', start); + }; + } + ); + /** + * Scrollable.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This mixin makes controls scrollable using custom scrollbars. + * + * @-x-less Scrollable.less + * @mixin tinymce.ui.Scrollable + */ + define( + 'tinymce.core.ui.Scrollable', [ + "tinymce.core.dom.DomQuery", + "tinymce.core.ui.DragHelper" + ], + function($, DragHelper) { + "use strict"; + + return { + init: function() { + var self = this; + self.on('repaint', self.renderScroll); + }, + + renderScroll: function() { + var self = this, + margin = 2; + + function repaintScroll() { + var hasScrollH, hasScrollV, bodyElm; + + function repaintAxis(axisName, posName, sizeName, contentSizeName, hasScroll, ax) { + var containerElm, scrollBarElm, scrollThumbElm; + var containerSize, scrollSize, ratio, rect; + var posNameLower, sizeNameLower; + + scrollBarElm = self.getEl('scroll' + axisName); + if (scrollBarElm) { + posNameLower = posName.toLowerCase(); + sizeNameLower = sizeName.toLowerCase(); + + $(self.getEl('absend')).css(posNameLower, self.layoutRect()[contentSizeName] - 1); + + if (!hasScroll) { + $(scrollBarElm).css('display', 'none'); + return; + } + + $(scrollBarElm).css('display', 'block'); + containerElm = self.getEl('body'); + scrollThumbElm = self.getEl('scroll' + axisName + "t"); + containerSize = containerElm["client" + sizeName] - (margin * 2); + containerSize -= hasScrollH && hasScrollV ? scrollBarElm["client" + ax] : 0; + scrollSize = containerElm["scroll" + sizeName]; + ratio = containerSize / scrollSize; + + rect = {}; + rect[posNameLower] = containerElm["offset" + posName] + margin; + rect[sizeNameLower] = containerSize; + $(scrollBarElm).css(rect); + + rect = {}; + rect[posNameLower] = containerElm["scroll" + posName] * ratio; + rect[sizeNameLower] = containerSize * ratio; + $(scrollThumbElm).css(rect); + } + } + + bodyElm = self.getEl('body'); + hasScrollH = bodyElm.scrollWidth > bodyElm.clientWidth; + hasScrollV = bodyElm.scrollHeight > bodyElm.clientHeight; + + repaintAxis("h", "Left", "Width", "contentW", hasScrollH, "Height"); + repaintAxis("v", "Top", "Height", "contentH", hasScrollV, "Width"); + } + + function addScroll() { + function addScrollAxis(axisName, posName, sizeName, deltaPosName, ax) { + var scrollStart, axisId = self._id + '-scroll' + axisName, + prefix = self.classPrefix; + + $(self.getEl()).append( + '<div id="' + axisId + '" class="' + prefix + 'scrollbar ' + prefix + 'scrollbar-' + axisName + '">' + + '<div id="' + axisId + 't" class="' + prefix + 'scrollbar-thumb"></div>' + + '</div>' + ); + + self.draghelper = new DragHelper(axisId + 't', { + start: function() { + scrollStart = self.getEl('body')["scroll" + posName]; + $('#' + axisId).addClass(prefix + 'active'); + }, + + drag: function(e) { + var ratio, hasScrollH, hasScrollV, containerSize, layoutRect = self.layoutRect(); + + hasScrollH = layoutRect.contentW > layoutRect.innerW; + hasScrollV = layoutRect.contentH > layoutRect.innerH; + containerSize = self.getEl('body')["client" + sizeName] - (margin * 2); + containerSize -= hasScrollH && hasScrollV ? self.getEl('scroll' + axisName)["client" + ax] : 0; + + ratio = containerSize / self.getEl('body')["scroll" + sizeName]; + self.getEl('body')["scroll" + posName] = scrollStart + (e["delta" + deltaPosName] / ratio); + }, + + stop: function() { + $('#' + axisId).removeClass(prefix + 'active'); + } + }); + } + + self.classes.add('scroll'); + + addScrollAxis("v", "Top", "Height", "Y", "Width"); + addScrollAxis("h", "Left", "Width", "X", "Height"); + } + + if (self.settings.autoScroll) { + if (!self._hasScroll) { + self._hasScroll = true; + addScroll(); + + self.on('wheel', function(e) { + var bodyEl = self.getEl('body'); + + bodyEl.scrollLeft += (e.deltaX || 0) * 10; + bodyEl.scrollTop += e.deltaY * 10; + + repaintScroll(); + }); + + $(self.getEl('body')).on("scroll", repaintScroll); + } + + repaintScroll(); + } + } + }; + } + ); + /** + * Panel.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a new panel. + * + * @-x-less Panel.less + * @class tinymce.ui.Panel + * @extends tinymce.ui.Container + * @mixes tinymce.ui.Scrollable + */ + define( + 'tinymce.core.ui.Panel', [ + "tinymce.core.ui.Container", + "tinymce.core.ui.Scrollable" + ], + function(Container, Scrollable) { + "use strict"; + + return Container.extend({ + Defaults: { + layout: 'fit', + containerCls: 'panel' + }, + + Mixins: [Scrollable], + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + layout = self._layout, + innerHtml = self.settings.html; + + self.preRender(); + layout.preRender(self); + + if (typeof innerHtml == "undefined") { + innerHtml = ( + '<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' + + layout.renderHtml(self) + + '</div>' + ); + } else { + if (typeof innerHtml == 'function') { + innerHtml = innerHtml.call(self); + } + + self._hasBody = false; + } + + return ( + '<div id="' + self._id + '" class="' + self.classes + '" hidefocus="1" tabindex="-1" role="group">' + + (self._preBodyHtml || '') + + innerHtml + + '</div>' + ); + } + }); + } + ); + + /** + * Movable.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Movable mixin. Makes controls movable absolute and relative to other elements. + * + * @mixin tinymce.ui.Movable + */ + define( + 'tinymce.core.ui.Movable', [ + "tinymce.core.ui.DomUtils" + ], + function(DomUtils) { + "use strict"; + + function calculateRelativePosition(ctrl, targetElm, rel) { + var ctrlElm, pos, x, y, selfW, selfH, targetW, targetH, viewport, size; + + viewport = DomUtils.getViewPort(); + + // Get pos of target + pos = DomUtils.getPos(targetElm); + x = pos.x; + y = pos.y; + + if (ctrl.state.get('fixed') && DomUtils.getRuntimeStyle(document.body, 'position') == 'static') { + x -= viewport.x; + y -= viewport.y; + } + + // Get size of self + ctrlElm = ctrl.getEl(); + size = DomUtils.getSize(ctrlElm); + selfW = size.width; + selfH = size.height; + + // Get size of target + size = DomUtils.getSize(targetElm); + targetW = size.width; + targetH = size.height; + + // Parse align string + rel = (rel || '').split(''); + + // Target corners + if (rel[0] === 'b') { + y += targetH; + } + + if (rel[1] === 'r') { + x += targetW; + } + + if (rel[0] === 'c') { + y += Math.round(targetH / 2); + } + + if (rel[1] === 'c') { + x += Math.round(targetW / 2); + } + + // Self corners + if (rel[3] === 'b') { + y -= selfH; + } + + if (rel[4] === 'r') { + x -= selfW; + } + + if (rel[3] === 'c') { + y -= Math.round(selfH / 2); + } + + if (rel[4] === 'c') { + x -= Math.round(selfW / 2); + } + + return { + x: x, + y: y, + w: selfW, + h: selfH + }; + } + + return { + /** + * Tests various positions to get the most suitable one. + * + * @method testMoveRel + * @param {DOMElement} elm Element to position against. + * @param {Array} rels Array with relative positions. + * @return {String} Best suitable relative position. + */ + testMoveRel: function(elm, rels) { + var viewPortRect = DomUtils.getViewPort(); + + for (var i = 0; i < rels.length; i++) { + var pos = calculateRelativePosition(this, elm, rels[i]); + + if (this.state.get('fixed')) { + if (pos.x > 0 && pos.x + pos.w < viewPortRect.w && pos.y > 0 && pos.y + pos.h < viewPortRect.h) { + return rels[i]; + } + } else { + if (pos.x > viewPortRect.x && pos.x + pos.w < viewPortRect.w + viewPortRect.x && + pos.y > viewPortRect.y && pos.y + pos.h < viewPortRect.h + viewPortRect.y) { + return rels[i]; + } + } + } + + return rels[0]; + }, + + /** + * Move relative to the specified element. + * + * @method moveRel + * @param {Element} elm Element to move relative to. + * @param {String} rel Relative mode. For example: br-tl. + * @return {tinymce.ui.Control} Current control instance. + */ + moveRel: function(elm, rel) { + if (typeof rel != 'string') { + rel = this.testMoveRel(elm, rel); + } + + var pos = calculateRelativePosition(this, elm, rel); + return this.moveTo(pos.x, pos.y); + }, + + /** + * Move by a relative x, y values. + * + * @method moveBy + * @param {Number} dx Relative x position. + * @param {Number} dy Relative y position. + * @return {tinymce.ui.Control} Current control instance. + */ + moveBy: function(dx, dy) { + var self = this, + rect = self.layoutRect(); + + self.moveTo(rect.x + dx, rect.y + dy); + + return self; + }, + + /** + * Move to absolute position. + * + * @method moveTo + * @param {Number} x Absolute x position. + * @param {Number} y Absolute y position. + * @return {tinymce.ui.Control} Current control instance. + */ + moveTo: function(x, y) { + var self = this; + + // TODO: Move this to some global class + function constrain(value, max, size) { + if (value < 0) { + return 0; + } + + if (value + size > max) { + value = max - size; + return value < 0 ? 0 : value; + } + + return value; + } + + if (self.settings.constrainToViewport) { + var viewPortRect = DomUtils.getViewPort(window); + var layoutRect = self.layoutRect(); + + x = constrain(x, viewPortRect.w + viewPortRect.x, layoutRect.w); + y = constrain(y, viewPortRect.h + viewPortRect.y, layoutRect.h); + } + + if (self.state.get('rendered')) { + self.layoutRect({ + x: x, + y: y + }).repaint(); + } else { + self.settings.x = x; + self.settings.y = y; + } + + self.fire('move', { + x: x, + y: y + }); + + return self; + } + }; + } + ); + /** + * Resizable.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Resizable mixin. Enables controls to be resized. + * + * @mixin tinymce.ui.Resizable + */ + define( + 'tinymce.core.ui.Resizable', [ + "tinymce.core.ui.DomUtils" + ], + function(DomUtils) { + "use strict"; + + return { + /** + * Resizes the control to contents. + * + * @method resizeToContent + */ + resizeToContent: function() { + this._layoutRect.autoResize = true; + this._lastRect = null; + this.reflow(); + }, + + /** + * Resizes the control to a specific width/height. + * + * @method resizeTo + * @param {Number} w Control width. + * @param {Number} h Control height. + * @return {tinymce.ui.Control} Current control instance. + */ + resizeTo: function(w, h) { + // TODO: Fix hack + if (w <= 1 || h <= 1) { + var rect = DomUtils.getWindowSize(); + + w = w <= 1 ? w * rect.w : w; + h = h <= 1 ? h * rect.h : h; + } + + this._layoutRect.autoResize = false; + return this.layoutRect({ + minW: w, + minH: h, + w: w, + h: h + }).reflow(); + }, + + /** + * Resizes the control to a specific relative width/height. + * + * @method resizeBy + * @param {Number} dw Relative control width. + * @param {Number} dh Relative control height. + * @return {tinymce.ui.Control} Current control instance. + */ + resizeBy: function(dw, dh) { + var self = this, + rect = self.layoutRect(); + + return self.resizeTo(rect.w + dw, rect.h + dh); + } + }; + } + ); + /** + * FloatPanel.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class creates a floating panel. + * + * @-x-less FloatPanel.less + * @class tinymce.ui.FloatPanel + * @extends tinymce.ui.Panel + * @mixes tinymce.ui.Movable + * @mixes tinymce.ui.Resizable + */ + define( + 'tinymce.core.ui.FloatPanel', [ + "tinymce.core.ui.Panel", + "tinymce.core.ui.Movable", + "tinymce.core.ui.Resizable", + "tinymce.core.ui.DomUtils", + "tinymce.core.dom.DomQuery", + "tinymce.core.util.Delay" + ], + function(Panel, Movable, Resizable, DomUtils, $, Delay) { + "use strict"; + + var documentClickHandler, documentScrollHandler, windowResizeHandler, visiblePanels = []; + var zOrder = [], + hasModal; + + function isChildOf(ctrl, parent) { + while (ctrl) { + if (ctrl == parent) { + return true; + } + + ctrl = ctrl.parent(); + } + } + + function skipOrHidePanels(e) { + // Hide any float panel when a click/focus out is out side that float panel and the + // float panels direct parent for example a click on a menu button + var i = visiblePanels.length; + + while (i--) { + var panel = visiblePanels[i], + clickCtrl = panel.getParentCtrl(e.target); + + if (panel.settings.autohide) { + if (clickCtrl) { + if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) { + continue; + } + } + + e = panel.fire('autohide', { + target: e.target + }); + if (!e.isDefaultPrevented()) { + panel.hide(); + } + } + } + } + + function bindDocumentClickHandler() { + + if (!documentClickHandler) { + documentClickHandler = function(e) { + // Gecko fires click event and in the wrong order on Mac so lets normalize + if (e.button == 2) { + return; + } + + skipOrHidePanels(e); + }; + + $(document).on('click touchstart', documentClickHandler); + } + } + + function bindDocumentScrollHandler() { + if (!documentScrollHandler) { + documentScrollHandler = function() { + var i; + + i = visiblePanels.length; + while (i--) { + repositionPanel(visiblePanels[i]); + } + }; + + $(window).on('scroll', documentScrollHandler); + } + } + + function bindWindowResizeHandler() { + if (!windowResizeHandler) { + var docElm = document.documentElement, + clientWidth = docElm.clientWidth, + clientHeight = docElm.clientHeight; + + windowResizeHandler = function() { + // Workaround for #7065 IE 7 fires resize events event though the window wasn't resized + if (!document.all || clientWidth != docElm.clientWidth || clientHeight != docElm.clientHeight) { + clientWidth = docElm.clientWidth; + clientHeight = docElm.clientHeight; + FloatPanel.hideAll(); + } + }; + + $(window).on('resize', windowResizeHandler); + } + } + + /** + * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will + * also reposition all child panels of the current panel. + */ + function repositionPanel(panel) { + var scrollY = DomUtils.getViewPort().y; + + function toggleFixedChildPanels(fixed, deltaY) { + var parent; + + for (var i = 0; i < visiblePanels.length; i++) { + if (visiblePanels[i] != panel) { + parent = visiblePanels[i].parent(); + + while (parent && (parent = parent.parent())) { + if (parent == panel) { + visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint(); + } + } + } + } + } + + if (panel.settings.autofix) { + if (!panel.state.get('fixed')) { + panel._autoFixY = panel.layoutRect().y; + + if (panel._autoFixY < scrollY) { + panel.fixed(true).layoutRect({ + y: 0 + }).repaint(); + toggleFixedChildPanels(true, scrollY - panel._autoFixY); + } + } else { + if (panel._autoFixY > scrollY) { + panel.fixed(false).layoutRect({ + y: panel._autoFixY + }).repaint(); + toggleFixedChildPanels(false, panel._autoFixY - scrollY); + } + } + } + } + + function addRemove(add, ctrl) { + var i, zIndex = FloatPanel.zIndex || 0xFFFF, + topModal; + + if (add) { + zOrder.push(ctrl); + } else { + i = zOrder.length; + + while (i--) { + if (zOrder[i] === ctrl) { + zOrder.splice(i, 1); + } + } + } + + if (zOrder.length) { + for (i = 0; i < zOrder.length; i++) { + if (zOrder[i].modal) { + zIndex++; + topModal = zOrder[i]; + } + + zOrder[i].getEl().style.zIndex = zIndex; + zOrder[i].zIndex = zIndex; + zIndex++; + } + } + + var modalBlockEl = $('#' + ctrl.classPrefix + 'modal-block', ctrl.getContainerElm())[0]; + + if (topModal) { + $(modalBlockEl).css('z-index', topModal.zIndex - 1); + } else if (modalBlockEl) { + modalBlockEl.parentNode.removeChild(modalBlockEl); + hasModal = false; + } + + FloatPanel.currentZIndex = zIndex; + } + + var FloatPanel = Panel.extend({ + Mixins: [Movable, Resizable], + + /** + * Constructs a new control instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Boolean} autohide Automatically hide the panel. + */ + init: function(settings) { + var self = this; + + self._super(settings); + self._eventsRoot = self; + + self.classes.add('floatpanel'); + + // Hide floatpanes on click out side the root button + if (settings.autohide) { + bindDocumentClickHandler(); + bindWindowResizeHandler(); + visiblePanels.push(self); + } + + if (settings.autofix) { + bindDocumentScrollHandler(); + + self.on('move', function() { + repositionPanel(this); + }); + } + + self.on('postrender show', function(e) { + if (e.control == self) { + var $modalBlockEl, prefix = self.classPrefix; + + if (self.modal && !hasModal) { + $modalBlockEl = $('#' + prefix + 'modal-block', self.getContainerElm()); + if (!$modalBlockEl[0]) { + $modalBlockEl = $( + '<div id="' + prefix + 'modal-block" class="' + prefix + 'reset ' + prefix + 'fade"></div>' + ).appendTo(self.getContainerElm()); + } + + Delay.setTimeout(function() { + $modalBlockEl.addClass(prefix + 'in'); + $(self.getEl()).addClass(prefix + 'in'); + }); + + hasModal = true; + } + + addRemove(true, self); + } + }); + + self.on('show', function() { + self.parents().each(function(ctrl) { + if (ctrl.state.get('fixed')) { + self.fixed(true); + return false; + } + }); + }); + + if (settings.popover) { + self._preBodyHtml = '<div class="' + self.classPrefix + 'arrow"></div>'; + self.classes.add('popover').add('bottom').add(self.isRtl() ? 'end' : 'start'); + } + + self.aria('label', settings.ariaLabel); + self.aria('labelledby', self._id); + self.aria('describedby', self.describedBy || self._id + '-none'); + }, + + fixed: function(state) { + var self = this; + + if (self.state.get('fixed') != state) { + if (self.state.get('rendered')) { + var viewport = DomUtils.getViewPort(); + + if (state) { + self.layoutRect().y -= viewport.y; + } else { + self.layoutRect().y += viewport.y; + } + } + + self.classes.toggle('fixed', state); + self.state.set('fixed', state); + } + + return self; + }, + + /** + * Shows the current float panel. + * + * @method show + * @return {tinymce.ui.FloatPanel} Current floatpanel instance. + */ + show: function() { + var self = this, + i, state = self._super(); + + i = visiblePanels.length; + while (i--) { + if (visiblePanels[i] === self) { + break; + } + } + + if (i === -1) { + visiblePanels.push(self); + } + + return state; + }, + + /** + * Hides the current float panel. + * + * @method hide + * @return {tinymce.ui.FloatPanel} Current floatpanel instance. + */ + hide: function() { + removeVisiblePanel(this); + addRemove(false, this); + + return this._super(); + }, + + /** + * Hide all visible float panels with he autohide setting enabled. This is for + * manually hiding floating menus or panels. + * + * @method hideAll + */ + hideAll: function() { + FloatPanel.hideAll(); + }, + + /** + * Closes the float panel. This will remove the float panel from page and fire the close event. + * + * @method close + */ + close: function() { + var self = this; + + if (!self.fire('close').isDefaultPrevented()) { + self.remove(); + addRemove(false, self); + } + + return self; + }, + + /** + * Removes the float panel from page. + * + * @method remove + */ + remove: function() { + removeVisiblePanel(this); + this._super(); + }, + + postRender: function() { + var self = this; + + if (self.settings.bodyRole) { + this.getEl('body').setAttribute('role', self.settings.bodyRole); + } + + return self._super(); + } + }); + + /** + * Hide all visible float panels with he autohide setting enabled. This is for + * manually hiding floating menus or panels. + * + * @static + * @method hideAll + */ + FloatPanel.hideAll = function() { + var i = visiblePanels.length; + + while (i--) { + var panel = visiblePanels[i]; + + if (panel && panel.settings.autohide) { + panel.hide(); + visiblePanels.splice(i, 1); + } + } + }; + + function removeVisiblePanel(panel) { + var i; + + i = visiblePanels.length; + while (i--) { + if (visiblePanels[i] === panel) { + visiblePanels.splice(i, 1); + } + } + + i = zOrder.length; + while (i--) { + if (zOrder[i] === panel) { + zOrder.splice(i, 1); + } + } + } + + return FloatPanel; + } + ); + + /** + * Window.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a new window. + * + * @-x-less Window.less + * @class tinymce.ui.Window + * @extends tinymce.ui.FloatPanel + */ + define( + 'tinymce.core.ui.Window', [ + "tinymce.core.ui.FloatPanel", + "tinymce.core.ui.Panel", + "tinymce.core.ui.DomUtils", + "tinymce.core.dom.DomQuery", + "tinymce.core.ui.DragHelper", + "tinymce.core.ui.BoxUtils", + "tinymce.core.Env", + "tinymce.core.util.Delay" + ], + function(FloatPanel, Panel, DomUtils, $, DragHelper, BoxUtils, Env, Delay) { + "use strict"; + + var windows = [], + oldMetaValue = ''; + + function toggleFullScreenState(state) { + var noScaleMetaValue = 'width=device-width,initial-scale=1.0,user-scalable=0,minimum-scale=1.0,maximum-scale=1.0', + viewport = $("meta[name=viewport]")[0], + contentValue; + + if (Env.overrideViewPort === false) { + return; + } + + if (!viewport) { + viewport = document.createElement('meta'); + viewport.setAttribute('name', 'viewport'); + document.getElementsByTagName('head')[0].appendChild(viewport); + } + + contentValue = viewport.getAttribute('content'); + if (contentValue && typeof oldMetaValue != 'undefined') { + oldMetaValue = contentValue; + } + + viewport.setAttribute('content', state ? noScaleMetaValue : oldMetaValue); + } + + function toggleBodyFullScreenClasses(classPrefix, state) { + if (checkFullscreenWindows() && state === false) { + $([document.documentElement, document.body]).removeClass(classPrefix + 'fullscreen'); + } + } + + function checkFullscreenWindows() { + for (var i = 0; i < windows.length; i++) { + if (windows[i]._fullscreen) { + return true; + } + } + return false; + } + + function handleWindowResize() { + if (!Env.desktop) { + var lastSize = { + w: window.innerWidth, + h: window.innerHeight + }; + + Delay.setInterval(function() { + var w = window.innerWidth, + h = window.innerHeight; + + if (lastSize.w != w || lastSize.h != h) { + lastSize = { + w: w, + h: h + }; + + $(window).trigger('resize'); + } + }, 100); + } + + function reposition() { + var i, rect = DomUtils.getWindowSize(), + layoutRect; + + for (i = 0; i < windows.length; i++) { + layoutRect = windows[i].layoutRect(); + + windows[i].moveTo( + windows[i].settings.x || Math.max(0, rect.w / 2 - layoutRect.w / 2), + windows[i].settings.y || Math.max(0, rect.h / 2 - layoutRect.h / 2) + ); + } + } + + $(window).on('resize', reposition); + } + + var Window = FloatPanel.extend({ + modal: true, + + Defaults: { + border: 1, + layout: 'flex', + containerCls: 'panel', + role: 'dialog', + callbacks: { + submit: function() { + this.fire('submit', { + data: this.toJSON() + }); + }, + + close: function() { + this.close(); + } + } + }, + + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + var self = this; + + self._super(settings); + + if (self.isRtl()) { + self.classes.add('rtl'); + } + + self.classes.add('window'); + self.bodyClasses.add('window-body'); + self.state.set('fixed', true); + + // Create statusbar + if (settings.buttons) { + self.statusbar = new Panel({ + layout: 'flex', + border: '1 0 0 0', + spacing: 3, + padding: 10, + align: 'center', + pack: self.isRtl() ? 'start' : 'end', + defaults: { + type: 'button' + }, + items: settings.buttons + }); + + self.statusbar.classes.add('foot'); + self.statusbar.parent(self); + } + + self.on('click', function(e) { + var closeClass = self.classPrefix + 'close'; + + if (DomUtils.hasClass(e.target, closeClass) || DomUtils.hasClass(e.target.parentNode, closeClass)) { + self.close(); + } + }); + + self.on('cancel', function() { + self.close(); + }); + + self.aria('describedby', self.describedBy || self._id + '-none'); + self.aria('label', settings.title); + self._fullscreen = false; + }, + + /** + * Recalculates the positions of the controls in the current container. + * This is invoked by the reflow method and shouldn't be called directly. + * + * @method recalc + */ + recalc: function() { + var self = this, + statusbar = self.statusbar, + layoutRect, width, x, needsRecalc; + + if (self._fullscreen) { + self.layoutRect(DomUtils.getWindowSize()); + self.layoutRect().contentH = self.layoutRect().innerH; + } + + self._super(); + + layoutRect = self.layoutRect(); + + // Resize window based on title width + if (self.settings.title && !self._fullscreen) { + width = layoutRect.headerW; + if (width > layoutRect.w) { + x = layoutRect.x - Math.max(0, width / 2); + self.layoutRect({ + w: width, + x: x + }); + needsRecalc = true; + } + } + + // Resize window based on statusbar width + if (statusbar) { + statusbar.layoutRect({ + w: self.layoutRect().innerW + }).recalc(); + + width = statusbar.layoutRect().minW + layoutRect.deltaW; + if (width > layoutRect.w) { + x = layoutRect.x - Math.max(0, width - layoutRect.w); + self.layoutRect({ + w: width, + x: x + }); + needsRecalc = true; + } + } + + // Recalc body and disable auto resize + if (needsRecalc) { + self.recalc(); + } + }, + + /** + * Initializes the current controls layout rect. + * This will be executed by the layout managers to determine the + * default minWidth/minHeight etc. + * + * @method initLayoutRect + * @return {Object} Layout rect instance. + */ + initLayoutRect: function() { + var self = this, + layoutRect = self._super(), + deltaH = 0, + headEl; + + // Reserve vertical space for title + if (self.settings.title && !self._fullscreen) { + headEl = self.getEl('head'); + + var size = DomUtils.getSize(headEl); + + layoutRect.headerW = size.width; + layoutRect.headerH = size.height; + + deltaH += layoutRect.headerH; + } + + // Reserve vertical space for statusbar + if (self.statusbar) { + deltaH += self.statusbar.layoutRect().h; + } + + layoutRect.deltaH += deltaH; + layoutRect.minH += deltaH; + //layoutRect.innerH -= deltaH; + layoutRect.h += deltaH; + + var rect = DomUtils.getWindowSize(); + + layoutRect.x = self.settings.x || Math.max(0, rect.w / 2 - layoutRect.w / 2); + layoutRect.y = self.settings.y || Math.max(0, rect.h / 2 - layoutRect.h / 2); + + return layoutRect; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + layout = self._layout, + id = self._id, + prefix = self.classPrefix; + var settings = self.settings, + headerHtml = '', + footerHtml = '', + html = settings.html; + + self.preRender(); + layout.preRender(self); + + if (settings.title) { + headerHtml = ( + '<div id="' + id + '-head" class="' + prefix + 'window-head">' + + '<div id="' + id + '-title" class="' + prefix + 'title">' + self.encode(settings.title) + '</div>' + + '<div id="' + id + '-dragh" class="' + prefix + 'dragh"></div>' + + '<button type="button" class="' + prefix + 'close" aria-hidden="true">' + + '<i class="mce-ico mce-i-remove"></i>' + + '</button>' + + '</div>' + ); + } + + if (settings.url) { + html = '<iframe src="' + settings.url + '" tabindex="-1"></iframe>'; + } + + if (typeof html == "undefined") { + html = layout.renderHtml(self); + } + + if (self.statusbar) { + footerHtml = self.statusbar.renderHtml(); + } + + return ( + '<div id="' + id + '" class="' + self.classes + '" hidefocus="1">' + + '<div class="' + self.classPrefix + 'reset" role="application">' + + headerHtml + + '<div id="' + id + '-body" class="' + self.bodyClasses + '">' + + html + + '</div>' + + footerHtml + + '</div>' + + '</div>' + ); + }, + + /** + * Switches the window fullscreen mode. + * + * @method fullscreen + * @param {Boolean} state True/false state. + * @return {tinymce.ui.Window} Current window instance. + */ + fullscreen: function(state) { + var self = this, + documentElement = document.documentElement, + slowRendering, prefix = self.classPrefix, + layoutRect; + + if (state != self._fullscreen) { + $(window).on('resize', function() { + var time; + + if (self._fullscreen) { + // Time the layout time if it's to slow use a timeout to not hog the CPU + if (!slowRendering) { + time = new Date().getTime(); + + var rect = DomUtils.getWindowSize(); + self.moveTo(0, 0).resizeTo(rect.w, rect.h); + + if ((new Date().getTime()) - time > 50) { + slowRendering = true; + } + } else { + if (!self._timer) { + self._timer = Delay.setTimeout(function() { + var rect = DomUtils.getWindowSize(); + self.moveTo(0, 0).resizeTo(rect.w, rect.h); + + self._timer = 0; + }, 50); + } + } + } + }); + + layoutRect = self.layoutRect(); + self._fullscreen = state; + + if (!state) { + self.borderBox = BoxUtils.parseBox(self.settings.border); + self.getEl('head').style.display = ''; + layoutRect.deltaH += layoutRect.headerH; + $([documentElement, document.body]).removeClass(prefix + 'fullscreen'); + self.classes.remove('fullscreen'); + self.moveTo(self._initial.x, self._initial.y).resizeTo(self._initial.w, self._initial.h); + } else { + self._initial = { + x: layoutRect.x, + y: layoutRect.y, + w: layoutRect.w, + h: layoutRect.h + }; + + self.borderBox = BoxUtils.parseBox('0'); + self.getEl('head').style.display = 'none'; + layoutRect.deltaH -= layoutRect.headerH + 2; + $([documentElement, document.body]).addClass(prefix + 'fullscreen'); + self.classes.add('fullscreen'); + + var rect = DomUtils.getWindowSize(); + self.moveTo(0, 0).resizeTo(rect.w, rect.h); + } + } + + return self.reflow(); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this, + startPos; + + setTimeout(function() { + self.classes.add('in'); + self.fire('open'); + }, 0); + + self._super(); + + if (self.statusbar) { + self.statusbar.postRender(); + } + + self.focus(); + + this.dragHelper = new DragHelper(self._id + '-dragh', { + start: function() { + startPos = { + x: self.layoutRect().x, + y: self.layoutRect().y + }; + }, + + drag: function(e) { + self.moveTo(startPos.x + e.deltaX, startPos.y + e.deltaY); + } + }); + + self.on('submit', function(e) { + if (!e.isDefaultPrevented()) { + self.close(); + } + }); + + windows.push(self); + toggleFullScreenState(true); + }, + + /** + * Fires a submit event with the serialized form. + * + * @method submit + * @return {Object} Event arguments object. + */ + submit: function() { + return this.fire('submit', { + data: this.toJSON() + }); + }, + + /** + * Removes the current control from DOM and from UI collections. + * + * @method remove + * @return {tinymce.ui.Control} Current control instance. + */ + remove: function() { + var self = this, + i; + + self.dragHelper.destroy(); + self._super(); + + if (self.statusbar) { + this.statusbar.remove(); + } + + toggleBodyFullScreenClasses(self.classPrefix, false); + + i = windows.length; + while (i--) { + if (windows[i] === self) { + windows.splice(i, 1); + } + } + + toggleFullScreenState(windows.length > 0); + }, + + /** + * Returns the contentWindow object of the iframe if it exists. + * + * @method getContentWindow + * @return {Window} window object or null. + */ + getContentWindow: function() { + var ifr = this.getEl().getElementsByTagName('iframe')[0]; + return ifr ? ifr.contentWindow : null; + } + }); + + handleWindowResize(); + + return Window; + } + ); + /** + * MessageBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class is used to create MessageBoxes like alerts/confirms etc. + * + * @class tinymce.ui.MessageBox + * @extends tinymce.ui.FloatPanel + */ + define( + 'tinymce.core.ui.MessageBox', [ + "tinymce.core.ui.Window" + ], + function(Window) { + "use strict"; + + var MessageBox = Window.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + settings = { + border: 1, + padding: 20, + layout: 'flex', + pack: "center", + align: "center", + containerCls: 'panel', + autoScroll: true, + buttons: { + type: "button", + text: "Ok", + action: "ok" + }, + items: { + type: "label", + multiline: true, + maxWidth: 500, + maxHeight: 200 + } + }; + + this._super(settings); + }, + + Statics: { + /** + * Ok buttons constant. + * + * @static + * @final + * @field {Number} OK + */ + OK: 1, + + /** + * Ok/cancel buttons constant. + * + * @static + * @final + * @field {Number} OK_CANCEL + */ + OK_CANCEL: 2, + + /** + * yes/no buttons constant. + * + * @static + * @final + * @field {Number} YES_NO + */ + YES_NO: 3, + + /** + * yes/no/cancel buttons constant. + * + * @static + * @final + * @field {Number} YES_NO_CANCEL + */ + YES_NO_CANCEL: 4, + + /** + * Constructs a new message box and renders it to the body element. + * + * @static + * @method msgBox + * @param {Object} settings Name/value object with settings. + */ + msgBox: function(settings) { + var buttons, callback = settings.callback || function() {}; + + function createButton(text, status, primary) { + return { + type: "button", + text: text, + subtype: primary ? 'primary' : '', + onClick: function(e) { + e.control.parents()[1].close(); + callback(status); + } + }; + } + + switch (settings.buttons) { + case MessageBox.OK_CANCEL: + buttons = [ + createButton('Ok', true, true), + createButton('Cancel', false) + ]; + break; + + case MessageBox.YES_NO: + case MessageBox.YES_NO_CANCEL: + buttons = [ + createButton('Yes', 1, true), + createButton('No', 0) + ]; + + if (settings.buttons == MessageBox.YES_NO_CANCEL) { + buttons.push(createButton('Cancel', -1)); + } + break; + + default: + buttons = [ + createButton('Ok', true, true) + ]; + break; + } + + return new Window({ + padding: 20, + x: settings.x, + y: settings.y, + minWidth: 300, + minHeight: 100, + layout: "flex", + pack: "center", + align: "center", + buttons: buttons, + title: settings.title, + role: 'alertdialog', + items: { + type: "label", + multiline: true, + maxWidth: 500, + maxHeight: 200, + text: settings.text + }, + onPostRender: function() { + this.aria('describedby', this.items()[0]._id); + }, + onClose: settings.onClose, + onCancel: function() { + callback(false); + } + }).renderTo(document.body).reflow(); + }, + + /** + * Creates a new alert dialog. + * + * @method alert + * @param {Object} settings Settings for the alert dialog. + * @param {function} [callback] Callback to execute when the user makes a choice. + */ + alert: function(settings, callback) { + if (typeof settings == "string") { + settings = { + text: settings + }; + } + + settings.callback = callback; + return MessageBox.msgBox(settings); + }, + + /** + * Creates a new confirm dialog. + * + * @method confirm + * @param {Object} settings Settings for the confirm dialog. + * @param {function} [callback] Callback to execute when the user makes a choice. + */ + confirm: function(settings, callback) { + if (typeof settings == "string") { + settings = { + text: settings + }; + } + + settings.callback = callback; + settings.buttons = MessageBox.OK_CANCEL; + + return MessageBox.msgBox(settings); + } + } + }); + + return MessageBox; + } + ); + + /** + * WindowManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class handles the creation of native windows and dialogs. This class can be extended to provide for example inline dialogs. + * + * @class tinymce.WindowManager + * @example + * // Opens a new dialog with the file.htm file and the size 320x240 + * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog. + * tinymce.activeEditor.windowManager.open({ + * url: 'file.htm', + * width: 320, + * height: 240 + * }, { + * custom_param: 1 + * }); + * + * // Displays an alert box using the active editors window manager instance + * tinymce.activeEditor.windowManager.alert('Hello world!'); + * + * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm + * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) { + * if (s) + * tinymce.activeEditor.windowManager.alert("Ok"); + * else + * tinymce.activeEditor.windowManager.alert("Cancel"); + * }); + */ + define( + 'tinymce.core.WindowManager', [ + "tinymce.core.ui.Window", + "tinymce.core.ui.MessageBox" + ], + function(Window, MessageBox) { + return function(editor) { + var self = this, + windows = []; + + function getTopMostWindow() { + if (windows.length) { + return windows[windows.length - 1]; + } + } + + function fireOpenEvent(win) { + editor.fire('OpenWindow', { + win: win + }); + } + + function fireCloseEvent(win) { + editor.fire('CloseWindow', { + win: win + }); + } + + self.windows = windows; + + editor.on('remove', function() { + var i = windows.length; + + while (i--) { + windows[i].close(); + } + }); + + /** + * Opens a new window. + * + * @method open + * @param {Object} args Optional name/value settings collection contains things like width/height/url etc. + * @param {Object} params Options like title, file, width, height etc. + * @option {String} title Window title. + * @option {String} file URL of the file to open in the window. + * @option {Number} width Width in pixels. + * @option {Number} height Height in pixels. + * @option {Boolean} autoScroll Specifies whether the popup window can have scrollbars if required (i.e. content + * larger than the popup size specified). + */ + self.open = function(args, params) { + var win; + + editor.editorManager.setActive(editor); + + args.title = args.title || ' '; + + // Handle URL + args.url = args.url || args.file; // Legacy + if (args.url) { + args.width = parseInt(args.width || 320, 10); + args.height = parseInt(args.height || 240, 10); + } + + // Handle body + if (args.body) { + args.items = { + defaults: args.defaults, + type: args.bodyType || 'form', + items: args.body, + data: args.data, + callbacks: args.commands + }; + } + + if (!args.url && !args.buttons) { + args.buttons = [{ + text: 'Ok', + subtype: 'primary', + onclick: function() { + win.find('form')[0].submit(); + } + }, + + { + text: 'Cancel', + onclick: function() { + win.close(); + } + } + ]; + } + + win = new Window(args); + windows.push(win); + + win.on('close', function() { + var i = windows.length; + + while (i--) { + if (windows[i] === win) { + windows.splice(i, 1); + } + } + + if (!windows.length) { + editor.focus(); + } + + fireCloseEvent(win); + }); + + // Handle data + if (args.data) { + win.on('postRender', function() { + this.find('*').each(function(ctrl) { + var name = ctrl.name(); + + if (name in args.data) { + ctrl.value(args.data[name]); + } + }); + }); + } + + // store args and parameters + win.features = args || {}; + win.params = params || {}; + + // Takes a snapshot in the FocusManager of the selection before focus is lost to dialog + if (windows.length === 1) { + editor.nodeChanged(); + } + + win = win.renderTo().reflow(); + + fireOpenEvent(win); + + return win; + }; + + /** + * Creates a alert dialog. Please don't use the blocking behavior of this + * native version use the callback method instead then it can be extended. + * + * @method alert + * @param {String} message Text to display in the new alert dialog. + * @param {function} callback Callback function to be executed after the user has selected ok. + * @param {Object} scope Optional scope to execute the callback in. + * @example + * // Displays an alert box using the active editors window manager instance + * tinymce.activeEditor.windowManager.alert('Hello world!'); + */ + self.alert = function(message, callback, scope) { + var win; + + win = MessageBox.alert(message, function() { + if (callback) { + callback.call(scope || this); + } else { + editor.focus(); + } + }); + + win.on('close', function() { + fireCloseEvent(win); + }); + + fireOpenEvent(win); + }; + + /** + * Creates a confirm dialog. Please don't use the blocking behavior of this + * native version use the callback method instead then it can be extended. + * + * @method confirm + * @param {String} message Text to display in the new confirm dialog. + * @param {function} callback Callback function to be executed after the user has selected ok or cancel. + * @param {Object} scope Optional scope to execute the callback in. + * @example + * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm + * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) { + * if (s) + * tinymce.activeEditor.windowManager.alert("Ok"); + * else + * tinymce.activeEditor.windowManager.alert("Cancel"); + * }); + */ + self.confirm = function(message, callback, scope) { + var win; + + win = MessageBox.confirm(message, function(state) { + callback.call(scope || this, state); + }); + + win.on('close', function() { + fireCloseEvent(win); + }); + + fireOpenEvent(win); + }; + + /** + * Closes the top most window. + * + * @method close + */ + self.close = function() { + if (getTopMostWindow()) { + getTopMostWindow().close(); + } + }; + + /** + * Returns the params of the last window open call. This can be used in iframe based + * dialog to get params passed from the tinymce plugin. + * + * @example + * var dialogArguments = top.tinymce.activeEditor.windowManager.getParams(); + * + * @method getParams + * @return {Object} Name/value object with parameters passed from windowManager.open call. + */ + self.getParams = function() { + return getTopMostWindow() ? getTopMostWindow().params : null; + }; + + /** + * Sets the params of the last opened window. + * + * @method setParams + * @param {Object} params Params object to set for the last opened window. + */ + self.setParams = function(params) { + if (getTopMostWindow()) { + getTopMostWindow().params = params; + } + }; + + /** + * Returns the currently opened window objects. + * + * @method getWindows + * @return {Array} Array of the currently opened windows. + */ + self.getWindows = function() { + return windows; + }; + }; + } + ); + + /** + * Tooltip.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a tooltip instance. + * + * @-x-less ToolTip.less + * @class tinymce.ui.ToolTip + * @extends tinymce.ui.Control + * @mixes tinymce.ui.Movable + */ + define( + 'tinymce.core.ui.Tooltip', [ + "tinymce.core.ui.Control", + "tinymce.core.ui.Movable" + ], + function(Control, Movable) { + return Control.extend({ + Mixins: [Movable], + + Defaults: { + classes: 'widget tooltip tooltip-n' + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + prefix = self.classPrefix; + + return ( + '<div id="' + self._id + '" class="' + self.classes + '" role="presentation">' + + '<div class="' + prefix + 'tooltip-arrow"></div>' + + '<div class="' + prefix + 'tooltip-inner">' + self.encode(self.state.get('text')) + '</div>' + + '</div>' + ); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:text', function(e) { + self.getEl().lastChild.innerHTML = self.encode(e.value); + }); + + return self._super(); + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this, + style, rect; + + style = self.getEl().style; + rect = self._layoutRect; + + style.left = rect.x + 'px'; + style.top = rect.y + 'px'; + style.zIndex = 0xFFFF + 0xFFFF; + } + }); + } + ); + /** + * Widget.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Widget base class a widget is a control that has a tooltip and some basic states. + * + * @class tinymce.ui.Widget + * @extends tinymce.ui.Control + */ + define( + 'tinymce.core.ui.Widget', [ + "tinymce.core.ui.Control", + "tinymce.core.ui.Tooltip" + ], + function(Control, Tooltip) { + "use strict"; + + var tooltip; + + var Widget = Control.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {String} tooltip Tooltip text to display when hovering. + * @setting {Boolean} autofocus True if the control should be focused when rendered. + * @setting {String} text Text to display inside widget. + */ + init: function(settings) { + var self = this; + + self._super(settings); + settings = self.settings; + self.canFocus = true; + + if (settings.tooltip && Widget.tooltips !== false) { + self.on('mouseenter', function(e) { + var tooltip = self.tooltip().moveTo(-0xFFFF); + + if (e.control == self) { + var rel = tooltip.text(settings.tooltip).show().testMoveRel(self.getEl(), ['bc-tc', 'bc-tl', 'bc-tr']); + + tooltip.classes.toggle('tooltip-n', rel == 'bc-tc'); + tooltip.classes.toggle('tooltip-nw', rel == 'bc-tl'); + tooltip.classes.toggle('tooltip-ne', rel == 'bc-tr'); + + tooltip.moveRel(self.getEl(), rel); + } else { + tooltip.hide(); + } + }); + + self.on('mouseleave mousedown click', function() { + self.tooltip().hide(); + }); + } + + self.aria('label', settings.ariaLabel || settings.tooltip); + }, + + /** + * Returns the current tooltip instance. + * + * @method tooltip + * @return {tinymce.ui.Tooltip} Tooltip instance. + */ + tooltip: function() { + if (!tooltip) { + tooltip = new Tooltip({ + type: 'tooltip' + }); + tooltip.renderTo(); + } + + return tooltip; + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this, + settings = self.settings; + + self._super(); + + if (!self.parent() && (settings.width || settings.height)) { + self.initLayoutRect(); + self.repaint(); + } + + if (settings.autofocus) { + self.focus(); + } + }, + + bindStates: function() { + var self = this; + + function disable(state) { + self.aria('disabled', state); + self.classes.toggle('disabled', state); + } + + function active(state) { + self.aria('pressed', state); + self.classes.toggle('active', state); + } + + self.state.on('change:disabled', function(e) { + disable(e.value); + }); + + self.state.on('change:active', function(e) { + active(e.value); + }); + + if (self.state.get('disabled')) { + disable(true); + } + + if (self.state.get('active')) { + active(true); + } + + return self._super(); + }, + + /** + * Removes the current control from DOM and from UI collections. + * + * @method remove + * @return {tinymce.ui.Control} Current control instance. + */ + remove: function() { + this._super(); + + if (tooltip) { + tooltip.remove(); + tooltip = null; + } + } + }); + + return Widget; + } + ); + + /** + * Progress.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Progress control. + * + * @-x-less Progress.less + * @class tinymce.ui.Progress + * @extends tinymce.ui.Control + */ + define( + 'tinymce.core.ui.Progress', [ + "tinymce.core.ui.Widget" + ], + function(Widget) { + "use strict"; + + return Widget.extend({ + Defaults: { + value: 0 + }, + + init: function(settings) { + var self = this; + + self._super(settings); + self.classes.add('progress'); + + if (!self.settings.filter) { + self.settings.filter = function(value) { + return Math.round(value); + }; + } + }, + + renderHtml: function() { + var self = this, + id = self._id, + prefix = this.classPrefix; + + return ( + '<div id="' + id + '" class="' + self.classes + '">' + + '<div class="' + prefix + 'bar-container">' + + '<div class="' + prefix + 'bar"></div>' + + '</div>' + + '<div class="' + prefix + 'text">0%</div>' + + '</div>' + ); + }, + + postRender: function() { + var self = this; + + self._super(); + self.value(self.settings.value); + + return self; + }, + + bindStates: function() { + var self = this; + + function setValue(value) { + value = self.settings.filter(value); + self.getEl().lastChild.innerHTML = value + '%'; + self.getEl().firstChild.firstChild.style.width = value + '%'; + } + + self.state.on('change:value', function(e) { + setValue(e.value); + }); + + setValue(self.state.get('value')); + + return self._super(); + } + }); + } + ); + /** + * Notification.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a notification instance. + * + * @-x-less Notification.less + * @class tinymce.ui.Notification + * @extends tinymce.ui.Container + * @mixes tinymce.ui.Movable + */ + define( + 'tinymce.core.ui.Notification', [ + "tinymce.core.ui.Control", + "tinymce.core.ui.Movable", + "tinymce.core.ui.Progress", + "tinymce.core.util.Delay" + ], + function(Control, Movable, Progress, Delay) { + return Control.extend({ + Mixins: [Movable], + + Defaults: { + classes: 'widget notification' + }, + + init: function(settings) { + var self = this; + + self._super(settings); + + if (settings.text) { + self.text(settings.text); + } + + if (settings.icon) { + self.icon = settings.icon; + } + + if (settings.color) { + self.color = settings.color; + } + + if (settings.type) { + self.classes.add('notification-' + settings.type); + } + + if (settings.timeout && (settings.timeout < 0 || settings.timeout > 0) && !settings.closeButton) { + self.closeButton = false; + } else { + self.classes.add('has-close'); + self.closeButton = true; + } + + if (settings.progressBar) { + self.progressBar = new Progress(); + } + + self.on('click', function(e) { + if (e.target.className.indexOf(self.classPrefix + 'close') != -1) { + self.close(); + } + }); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + prefix = self.classPrefix, + icon = '', + closeButton = '', + progressBar = '', + notificationStyle = ''; + + if (self.icon) { + icon = '<i class="' + prefix + 'ico' + ' ' + prefix + 'i-' + self.icon + '"></i>'; + } + + if (self.color) { + notificationStyle = ' style="background-color: ' + self.color + '"'; + } + + if (self.closeButton) { + closeButton = '<button type="button" class="' + prefix + 'close" aria-hidden="true">\u00d7</button>'; + } + + if (self.progressBar) { + progressBar = self.progressBar.renderHtml(); + } + + return ( + '<div id="' + self._id + '" class="' + self.classes + '"' + notificationStyle + ' role="presentation">' + + icon + + '<div class="' + prefix + 'notification-inner">' + self.state.get('text') + '</div>' + + progressBar + + closeButton + + '</div>' + ); + }, + + postRender: function() { + var self = this; + + Delay.setTimeout(function() { + self.$el.addClass(self.classPrefix + 'in'); + }); + + return self._super(); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:text', function(e) { + self.getEl().childNodes[1].innerHTML = e.value; + }); + if (self.progressBar) { + self.progressBar.bindStates(); + } + return self._super(); + }, + + close: function() { + var self = this; + + if (!self.fire('close').isDefaultPrevented()) { + self.remove(); + } + + return self; + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this, + style, rect; + + style = self.getEl().style; + rect = self._layoutRect; + + style.left = rect.x + 'px'; + style.top = rect.y + 'px'; + + // Hardcoded arbitrary z-value because we want the + // notifications under the other windows + style.zIndex = 0xFFFF - 1; + } + }); + } + ); + /** + * NotificationManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class handles the creation of TinyMCE's notifications. + * + * @class tinymce.NotificationManager + * @example + * // Opens a new notification of type "error" with text "An error occurred." + * tinymce.activeEditor.notificationManager.open({ + * text: 'An error occurred.', + * type: 'error' + * }); + */ + define( + 'tinymce.core.NotificationManager', [ + "tinymce.core.ui.Notification", + "tinymce.core.util.Delay", + "tinymce.core.util.Tools" + ], + function(Notification, Delay, Tools) { + return function(editor) { + var self = this, + notifications = []; + + function getLastNotification() { + if (notifications.length) { + return notifications[notifications.length - 1]; + } + } + + self.notifications = notifications; + + function resizeWindowEvent() { + Delay.requestAnimationFrame(function() { + prePositionNotifications(); + positionNotifications(); + }); + } + + // Since the viewport will change based on the present notifications, we need to move them all to the + // top left of the viewport to give an accurate size measurement so we can position them later. + function prePositionNotifications() { + for (var i = 0; i < notifications.length; i++) { + notifications[i].moveTo(0, 0); + } + } + + function positionNotifications() { + if (notifications.length > 0) { + var firstItem = notifications.slice(0, 1)[0]; + var container = editor.inline ? editor.getElement() : editor.getContentAreaContainer(); + firstItem.moveRel(container, 'tc-tc'); + if (notifications.length > 1) { + for (var i = 1; i < notifications.length; i++) { + notifications[i].moveRel(notifications[i - 1].getEl(), 'bc-tc'); + } + } + } + } + + editor.on('remove', function() { + var i = notifications.length; + + while (i--) { + notifications[i].close(); + } + }); + + editor.on('ResizeEditor', positionNotifications); + editor.on('ResizeWindow', resizeWindowEvent); + + /** + * Opens a new notification. + * + * @method open + * @param {Object} args Optional name/value settings collection contains things like timeout/color/message etc. + */ + self.open = function(args) { + // Never open notification if editor has been removed. + if (editor.removed) { + return; + } + + var notif; + + editor.editorManager.setActive(editor); + + var duplicate = findDuplicateMessage(notifications, args); + + if (duplicate === null) { + notif = new Notification(args); + notifications.push(notif); + + //If we have a timeout value + if (args.timeout > 0) { + notif.timer = setTimeout(function() { + notif.close(); + }, args.timeout); + } + + notif.on('close', function() { + var i = notifications.length; + + if (notif.timer) { + editor.getWin().clearTimeout(notif.timer); + } + + while (i--) { + if (notifications[i] === notif) { + notifications.splice(i, 1); + } + } + + positionNotifications(); + }); + + notif.renderTo(); + + positionNotifications(); + } else { + notif = duplicate; + } + + return notif; + }; + + /** + * Closes the top most notification. + * + * @method close + */ + self.close = function() { + if (getLastNotification()) { + getLastNotification().close(); + } + }; + + /** + * Returns the currently opened notification objects. + * + * @method getNotifications + * @return {Array} Array of the currently opened notifications. + */ + self.getNotifications = function() { + return notifications; + }; + + editor.on('SkinLoaded', function() { + var serviceMessage = editor.settings.service_message; + + if (serviceMessage) { + editor.notificationManager.open({ + text: serviceMessage, + type: 'warning', + timeout: 0, + icon: '' + }); + } + }); + + /** + * Finds any existing notification with the same properties as the new one. + * Returns either the found notification or null. + * + * @param {Notification[]} notificationArray - Array of current notifications + * @param {type: string, } newNotification - New notification object + * @returns {?Notification} + */ + function findDuplicateMessage(notificationArray, newNotification) { + if (!isPlainTextNotification(newNotification)) { + return null; + } + + var filteredNotifications = Tools.grep(notificationArray, function(notification) { + return isSameNotification(newNotification, notification); + }); + + return filteredNotifications.length === 0 ? null : filteredNotifications[0]; + } + + /** + * Checks if the passed in args object has the same + * type and text properties as the sent in notification. + * + * @param {type: string, text: string} a - New notification args object + * @param {Notification} b - Old notification + * @returns {boolean} + */ + function isSameNotification(a, b) { + return a.type === b.settings.type && a.text === b.settings.text; + } + + /** + * Checks that the notification does not have a progressBar + * or timeour property. + * + * @param {Notification} notification - Notification to check + * @returns {boolean} + */ + function isPlainTextNotification(notification) { + return !notification.progressBar && !notification.timeout; + } + + //self.positionNotifications = positionNotifications; + }; + } + ); + + /** + * EditorObservable.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This mixin contains the event logic for the tinymce.Editor class. + * + * @mixin tinymce.EditorObservable + * @extends tinymce.util.Observable + */ + define( + 'tinymce.core.EditorObservable', [ + "tinymce.core.util.Observable", + "tinymce.core.dom.DOMUtils", + "tinymce.core.util.Tools" + ], + function(Observable, DOMUtils, Tools) { + var DOM = DOMUtils.DOM, + customEventRootDelegates; + + /** + * Returns the event target so for the specified event. Some events fire + * only on document, some fire on documentElement etc. This also handles the + * custom event root setting where it returns that element instead of the body. + * + * @private + * @param {tinymce.Editor} editor Editor instance to get event target from. + * @param {String} eventName Name of the event for example "click". + * @return {Element/Document} HTML Element or document target to bind on. + */ + function getEventTarget(editor, eventName) { + if (eventName == 'selectionchange') { + return editor.getDoc(); + } + + // Need to bind mousedown/mouseup etc to document not body in iframe mode + // Since the user might click on the HTML element not the BODY + if (!editor.inline && /^mouse|touch|click|contextmenu|drop|dragover|dragend/.test(eventName)) { + return editor.getDoc().documentElement; + } + + // Bind to event root instead of body if it's defined + if (editor.settings.event_root) { + if (!editor.eventRoot) { + editor.eventRoot = DOM.select(editor.settings.event_root)[0]; + } + + return editor.eventRoot; + } + + return editor.getBody(); + } + + /** + * Binds a event delegate for the specified name this delegate will fire + * the event to the editor dispatcher. + * + * @private + * @param {tinymce.Editor} editor Editor instance to get event target from. + * @param {String} eventName Name of the event for example "click". + */ + function bindEventDelegate(editor, eventName) { + var eventRootElm, delegate; + + function isListening(editor) { + return !editor.hidden && !editor.readonly; + } + + if (!editor.delegates) { + editor.delegates = {}; + } + + if (editor.delegates[eventName] || editor.removed) { + return; + } + + eventRootElm = getEventTarget(editor, eventName); + + if (editor.settings.event_root) { + if (!customEventRootDelegates) { + customEventRootDelegates = {}; + editor.editorManager.on('removeEditor', function() { + var name; + + if (!editor.editorManager.activeEditor) { + if (customEventRootDelegates) { + for (name in customEventRootDelegates) { + editor.dom.unbind(getEventTarget(editor, name)); + } + + customEventRootDelegates = null; + } + } + }); + } + + if (customEventRootDelegates[eventName]) { + return; + } + + delegate = function(e) { + var target = e.target, + editors = editor.editorManager.editors, + i = editors.length; + + while (i--) { + var body = editors[i].getBody(); + + if (body === target || DOM.isChildOf(target, body)) { + if (isListening(editors[i])) { + editors[i].fire(eventName, e); + } + } + } + }; + + customEventRootDelegates[eventName] = delegate; + DOM.bind(eventRootElm, eventName, delegate); + } else { + delegate = function(e) { + if (isListening(editor)) { + editor.fire(eventName, e); + } + }; + + DOM.bind(eventRootElm, eventName, delegate); + editor.delegates[eventName] = delegate; + } + } + + var EditorObservable = { + /** + * Bind any pending event delegates. This gets executed after the target body/document is created. + * + * @private + */ + bindPendingEventDelegates: function() { + var self = this; + + Tools.each(self._pendingNativeEvents, function(name) { + bindEventDelegate(self, name); + }); + }, + + /** + * Toggles a native event on/off this is called by the EventDispatcher when + * the first native event handler is added and when the last native event handler is removed. + * + * @private + */ + toggleNativeEvent: function(name, state) { + var self = this; + + // Never bind focus/blur since the FocusManager fakes those + if (name == "focus" || name == "blur") { + return; + } + + if (state) { + if (self.initialized) { + bindEventDelegate(self, name); + } else { + if (!self._pendingNativeEvents) { + self._pendingNativeEvents = [name]; + } else { + self._pendingNativeEvents.push(name); + } + } + } else if (self.initialized) { + self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]); + delete self.delegates[name]; + } + }, + + /** + * Unbinds all native event handlers that means delegates, custom events bound using the Events API etc. + * + * @private + */ + unbindAllNativeEvents: function() { + var self = this, + name; + + if (self.delegates) { + for (name in self.delegates) { + self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]); + } + + delete self.delegates; + } + + if (!self.inline) { + self.getBody().onload = null; + self.dom.unbind(self.getWin()); + self.dom.unbind(self.getDoc()); + } + + self.dom.unbind(self.getBody()); + self.dom.unbind(self.getContainer()); + } + }; + + EditorObservable = Tools.extend({}, Observable, EditorObservable); + + return EditorObservable; + } + ); + + /** + * Shortcuts.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Contains logic for handling keyboard shortcuts. + * + * @class tinymce.Shortcuts + * @example + * editor.shortcuts.add('ctrl+a', "description of the shortcut", function() {}); + * editor.shortcuts.add('meta+a', "description of the shortcut", function() {}); // "meta" maps to Command on Mac and Ctrl on PC + * editor.shortcuts.add('ctrl+alt+a', "description of the shortcut", function() {}); + * editor.shortcuts.add('access+a', "description of the shortcut", function() {}); // "access" maps to ctrl+alt on Mac and shift+alt on PC + */ + define( + 'tinymce.core.Shortcuts', [ + 'tinymce.core.util.Tools', + 'tinymce.core.Env' + ], + function(Tools, Env) { + var each = Tools.each, + explode = Tools.explode; + + var keyCodeLookup = { + "f9": 120, + "f10": 121, + "f11": 122 + }; + + var modifierNames = Tools.makeMap('alt,ctrl,shift,meta,access'); + + return function(editor) { + var self = this, + shortcuts = {}, + pendingPatterns = []; + + function parseShortcut(pattern) { + var id, key, shortcut = {}; + + // Parse modifiers and keys ctrl+alt+b for example + each(explode(pattern, '+'), function(value) { + if (value in modifierNames) { + shortcut[value] = true; + } else { + // Allow numeric keycodes like ctrl+219 for ctrl+[ + if (/^[0-9]{2,}$/.test(value)) { + shortcut.keyCode = parseInt(value, 10); + } else { + shortcut.charCode = value.charCodeAt(0); + shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0); + } + } + }); + + // Generate unique id for modifier combination and set default state for unused modifiers + id = [shortcut.keyCode]; + for (key in modifierNames) { + if (shortcut[key]) { + id.push(key); + } else { + shortcut[key] = false; + } + } + shortcut.id = id.join(','); + + // Handle special access modifier differently depending on Mac/Win + if (shortcut.access) { + shortcut.alt = true; + + if (Env.mac) { + shortcut.ctrl = true; + } else { + shortcut.shift = true; + } + } + + // Handle special meta modifier differently depending on Mac/Win + if (shortcut.meta) { + if (Env.mac) { + shortcut.meta = true; + } else { + shortcut.ctrl = true; + shortcut.meta = false; + } + } + + return shortcut; + } + + function createShortcut(pattern, desc, cmdFunc, scope) { + var shortcuts; + + shortcuts = Tools.map(explode(pattern, '>'), parseShortcut); + shortcuts[shortcuts.length - 1] = Tools.extend(shortcuts[shortcuts.length - 1], { + func: cmdFunc, + scope: scope || editor + }); + + return Tools.extend(shortcuts[0], { + desc: editor.translate(desc), + subpatterns: shortcuts.slice(1) + }); + } + + function hasModifier(e) { + return e.altKey || e.ctrlKey || e.metaKey; + } + + function isFunctionKey(e) { + return e.type === "keydown" && e.keyCode >= 112 && e.keyCode <= 123; + } + + function matchShortcut(e, shortcut) { + if (!shortcut) { + return false; + } + + if (shortcut.ctrl != e.ctrlKey || shortcut.meta != e.metaKey) { + return false; + } + + if (shortcut.alt != e.altKey || shortcut.shift != e.shiftKey) { + return false; + } + + if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) { + e.preventDefault(); + return true; + } + + return false; + } + + function executeShortcutAction(shortcut) { + return shortcut.func ? shortcut.func.call(shortcut.scope) : null; + } + + editor.on('keyup keypress keydown', function(e) { + if ((hasModifier(e) || isFunctionKey(e)) && !e.isDefaultPrevented()) { + each(shortcuts, function(shortcut) { + if (matchShortcut(e, shortcut)) { + pendingPatterns = shortcut.subpatterns.slice(0); + + if (e.type == "keydown") { + executeShortcutAction(shortcut); + } + + return true; + } + }); + + if (matchShortcut(e, pendingPatterns[0])) { + if (pendingPatterns.length === 1) { + if (e.type == "keydown") { + executeShortcutAction(pendingPatterns[0]); + } + } + + pendingPatterns.shift(); + } + } + }); + + /** + * Adds a keyboard shortcut for some command or function. + * + * @method add + * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o. + * @param {String} desc Text description for the command. + * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed. + * @param {Object} scope Optional scope to execute the function in. + * @return {Boolean} true/false state if the shortcut was added or not. + */ + self.add = function(pattern, desc, cmdFunc, scope) { + var cmd; + + cmd = cmdFunc; + + if (typeof cmdFunc === 'string') { + cmdFunc = function() { + editor.execCommand(cmd, false, null); + }; + } else if (Tools.isArray(cmd)) { + cmdFunc = function() { + editor.execCommand(cmd[0], cmd[1], cmd[2]); + }; + } + + each(explode(Tools.trim(pattern.toLowerCase())), function(pattern) { + var shortcut = createShortcut(pattern, desc, cmdFunc, scope); + shortcuts[shortcut.id] = shortcut; + }); + + return true; + }; + + /** + * Remove a keyboard shortcut by pattern. + * + * @method remove + * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o. + * @return {Boolean} true/false state if the shortcut was removed or not. + */ + self.remove = function(pattern) { + var shortcut = createShortcut(pattern); + + if (shortcuts[shortcut.id]) { + delete shortcuts[shortcut.id]; + return true; + } + + return false; + }; + }; + } + ); + + /** + * DefaultSettings.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.EditorSettings', [ + 'tinymce.core.util.Tools' + ], + function(Tools) { + var getEditorSettings = function(editor, id, documentBaseUrl, defaultOverrideSettings, settings) { + settings = Tools.extend( + // Default settings + { + id: id, + theme: 'modern', + delta_width: 0, + delta_height: 0, + popup_css: '', + plugins: '', + document_base_url: documentBaseUrl, + add_form_submit_trigger: true, + submit_patch: true, + add_unload_trigger: true, + convert_urls: true, + relative_urls: true, + remove_script_host: true, + object_resizing: true, + doctype: '<!DOCTYPE html>', + visual: true, + font_size_style_values: 'xx-small,x-small,small,medium,large,x-large,xx-large', + + // See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size + font_size_legacy_values: 'xx-small,small,medium,large,x-large,xx-large,300%', + forced_root_block: 'p', + hidden_input: true, + padd_empty_editor: true, + render_ui: true, + indentation: '30px', + inline_styles: true, + convert_fonts_to_spans: true, + indent: 'simple', + indent_before: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' + + 'tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist', + indent_after: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' + + 'tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist', + entity_encoding: 'named', + url_converter: editor.convertURL, + url_converter_scope: editor, + ie7_compat: true + }, + + // tinymce.overrideDefaults settings + defaultOverrideSettings, + + // User settings + settings, + + // Forced settings + { + validate: true, + content_editable: settings.inline + } + ); + + // Merge external_plugins + if (defaultOverrideSettings && defaultOverrideSettings.external_plugins && settings.external_plugins) { + settings.external_plugins = Tools.extend({}, defaultOverrideSettings.external_plugins, settings.external_plugins); + } + + return settings; + }; + + return { + getEditorSettings: getEditorSettings + }; + } + ); + + defineGlobal("global!window", window); + /** + * ErrorReporter.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Various error reporting helper functions. + * + * @class tinymce.ErrorReporter + * @private + */ + define( + 'tinymce.core.ErrorReporter', [ + "tinymce.core.AddOnManager" + ], + function(AddOnManager) { + var PluginManager = AddOnManager.PluginManager; + + var resolvePluginName = function(targetUrl, suffix) { + for (var name in PluginManager.urls) { + var matchUrl = PluginManager.urls[name] + '/plugin' + suffix + '.js'; + if (matchUrl === targetUrl) { + return name; + } + } + + return null; + }; + + var pluginUrlToMessage = function(editor, url) { + var plugin = resolvePluginName(url, editor.suffix); + return plugin ? + 'Failed to load plugin: ' + plugin + ' from url ' + url : + 'Failed to load plugin url: ' + url; + }; + + var displayNotification = function(editor, message) { + editor.notificationManager.open({ + type: 'error', + text: message + }); + }; + + var displayError = function(editor, message) { + if (editor._skinLoaded) { + displayNotification(editor, message); + } else { + editor.on('SkinLoaded', function() { + displayNotification(editor, message); + }); + } + }; + + var uploadError = function(editor, message) { + displayError(editor, 'Failed to upload image: ' + message); + }; + + var pluginLoadError = function(editor, url) { + displayError(editor, pluginUrlToMessage(editor, url)); + }; + + var contentCssError = function(editor, urls) { + displayError(editor, 'Failed to load content css: ' + urls[0]); + }; + + var initError = function(message) { + var console = window.console; + if (console && !window.test) { // Skip test env + if (console.error) { + console.error.apply(console, arguments); + } else { + console.log.apply(console, arguments); + } + } + }; + + return { + pluginLoadError: pluginLoadError, + uploadError: uploadError, + displayError: displayError, + contentCssError: contentCssError, + initError: initError + }; + } + ); + /** + * CaretContainerInput.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module shows the invisble block that the caret is currently in when contents is added to that block. + */ + define( + 'tinymce.core.caret.CaretContainerInput', [ + 'ephox.katamari.api.Fun', + 'tinymce.core.caret.CaretContainer' + ], + function(Fun, CaretContainer) { + var findBlockCaretContainer = function(editor) { + return editor.dom.select('*[data-mce-caret]')[0]; + }; + + var removeIeControlRect = function(editor) { + editor.selection.setRng(editor.selection.getRng()); + }; + + var showBlockCaretContainer = function(editor, blockCaretContainer) { + if (blockCaretContainer.hasAttribute('data-mce-caret')) { + CaretContainer.showCaretContainerBlock(blockCaretContainer); + removeIeControlRect(editor); + editor.selection.scrollIntoView(blockCaretContainer); + } + }; + + var handleBlockContainer = function(editor, e) { + var blockCaretContainer = findBlockCaretContainer(editor); + + if (!blockCaretContainer) { + return; + } + + if (e.type === 'compositionstart') { + e.preventDefault(); + e.stopPropagation(); + showBlockCaretContainer(blockCaretContainer); + return; + } + + if (CaretContainer.hasContent(blockCaretContainer)) { + showBlockCaretContainer(editor, blockCaretContainer); + } + }; + + var setup = function(editor) { + editor.on('keyup compositionstart', Fun.curry(handleBlockContainer, editor)); + }; + + return { + setup: setup + }; + } + ); + /** + * Uploader.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Upload blobs or blob infos to the specified URL or handler. + * + * @private + * @class tinymce.file.Uploader + * @example + * var uploader = new Uploader({ + * url: '/upload.php', + * basePath: '/base/path', + * credentials: true, + * handler: function(data, success, failure) { + * ... + * } + * }); + * + * uploader.upload(blobInfos).then(function(result) { + * ... + * }); + */ + define( + 'tinymce.core.file.Uploader', [ + "tinymce.core.util.Promise", + "tinymce.core.util.Tools", + "tinymce.core.util.Fun" + ], + function(Promise, Tools, Fun) { + return function(uploadStatus, settings) { + var pendingPromises = {}; + + function pathJoin(path1, path2) { + if (path1) { + return path1.replace(/\/$/, '') + '/' + path2.replace(/^\//, ''); + } + + return path2; + } + + function defaultHandler(blobInfo, success, failure, progress) { + var xhr, formData; + + xhr = new XMLHttpRequest(); + xhr.open('POST', settings.url); + xhr.withCredentials = settings.credentials; + + xhr.upload.onprogress = function(e) { + progress(e.loaded / e.total * 100); + }; + + xhr.onerror = function() { + failure("Image upload failed due to a XHR Transport error. Code: " + xhr.status); + }; + + xhr.onload = function() { + var json; + + if (xhr.status < 200 || xhr.status >= 300) { + failure("HTTP Error: " + xhr.status); + return; + } + + json = JSON.parse(xhr.responseText); + + if (!json || typeof json.location != "string") { + failure("Invalid JSON: " + xhr.responseText); + return; + } + + success(pathJoin(settings.basePath, json.location)); + }; + + formData = new FormData(); + formData.append('file', blobInfo.blob(), blobInfo.filename()); + + xhr.send(formData); + } + + function noUpload() { + return new Promise(function(resolve) { + resolve([]); + }); + } + + function handlerSuccess(blobInfo, url) { + return { + url: url, + blobInfo: blobInfo, + status: true + }; + } + + function handlerFailure(blobInfo, error) { + return { + url: '', + blobInfo: blobInfo, + status: false, + error: error + }; + } + + function resolvePending(blobUri, result) { + Tools.each(pendingPromises[blobUri], function(resolve) { + resolve(result); + }); + + delete pendingPromises[blobUri]; + } + + function uploadBlobInfo(blobInfo, handler, openNotification) { + uploadStatus.markPending(blobInfo.blobUri()); + + return new Promise(function(resolve) { + var notification, progress; + + var noop = function() {}; + + try { + var closeNotification = function() { + if (notification) { + notification.close(); + progress = noop; // Once it's closed it's closed + } + }; + + var success = function(url) { + closeNotification(); + uploadStatus.markUploaded(blobInfo.blobUri(), url); + resolvePending(blobInfo.blobUri(), handlerSuccess(blobInfo, url)); + resolve(handlerSuccess(blobInfo, url)); + }; + + var failure = function(error) { + closeNotification(); + uploadStatus.removeFailed(blobInfo.blobUri()); + resolvePending(blobInfo.blobUri(), handlerFailure(blobInfo, error)); + resolve(handlerFailure(blobInfo, error)); + }; + + progress = function(percent) { + if (percent < 0 || percent > 100) { + return; + } + + if (!notification) { + notification = openNotification(); + } + + notification.progressBar.value(percent); + }; + + handler(blobInfo, success, failure, progress); + } catch (ex) { + resolve(handlerFailure(blobInfo, ex.message)); + } + }); + } + + function isDefaultHandler(handler) { + return handler === defaultHandler; + } + + function pendingUploadBlobInfo(blobInfo) { + var blobUri = blobInfo.blobUri(); + + return new Promise(function(resolve) { + pendingPromises[blobUri] = pendingPromises[blobUri] || []; + pendingPromises[blobUri].push(resolve); + }); + } + + function uploadBlobs(blobInfos, openNotification) { + blobInfos = Tools.grep(blobInfos, function(blobInfo) { + return !uploadStatus.isUploaded(blobInfo.blobUri()); + }); + + return Promise.all(Tools.map(blobInfos, function(blobInfo) { + return uploadStatus.isPending(blobInfo.blobUri()) ? + pendingUploadBlobInfo(blobInfo) : uploadBlobInfo(blobInfo, settings.handler, openNotification); + })); + } + + function upload(blobInfos, openNotification) { + return (!settings.url && isDefaultHandler(settings.handler)) ? noUpload() : uploadBlobs(blobInfos, openNotification); + } + + settings = Tools.extend({ + credentials: false, + // We are adding a notify argument to this (at the moment, until it doesn't work) + handler: defaultHandler + }, settings); + + return { + upload: upload + }; + }; + } + ); + /** + * Conversions.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Converts blob/uris back and forth. + * + * @private + * @class tinymce.file.Conversions + */ + define( + 'tinymce.core.file.Conversions', [ + "tinymce.core.util.Promise" + ], + function(Promise) { + function blobUriToBlob(url) { + return new Promise(function(resolve, reject) { + + var rejectWithError = function() { + reject("Cannot convert " + url + " to Blob. Resource might not exist or is inaccessible."); + }; + + try { + var xhr = new XMLHttpRequest(); + + xhr.open('GET', url, true); + xhr.responseType = 'blob'; + + xhr.onload = function() { + if (this.status == 200) { + resolve(this.response); + } else { + // IE11 makes it into onload but responds with status 500 + rejectWithError(); + } + }; + + // Chrome fires an error event instead of the exception + // Also there seems to be no way to intercept the message that is logged to the console + xhr.onerror = rejectWithError; + + xhr.send(); + } catch (ex) { + rejectWithError(); + } + }); + } + + function parseDataUri(uri) { + var type, matches; + + uri = decodeURIComponent(uri).split(','); + + matches = /data:([^;]+)/.exec(uri[0]); + if (matches) { + type = matches[1]; + } + + return { + type: type, + data: uri[1] + }; + } + + function dataUriToBlob(uri) { + return new Promise(function(resolve) { + var str, arr, i; + + uri = parseDataUri(uri); + + // Might throw error if data isn't proper base64 + try { + str = atob(uri.data); + } catch (e) { + resolve(new Blob([])); + return; + } + + arr = new Uint8Array(str.length); + + for (i = 0; i < arr.length; i++) { + arr[i] = str.charCodeAt(i); + } + + resolve(new Blob([arr], { + type: uri.type + })); + }); + } + + function uriToBlob(url) { + if (url.indexOf('blob:') === 0) { + return blobUriToBlob(url); + } + + if (url.indexOf('data:') === 0) { + return dataUriToBlob(url); + } + + return null; + } + + function blobToDataUri(blob) { + return new Promise(function(resolve) { + var reader = new FileReader(); + + reader.onloadend = function() { + resolve(reader.result); + }; + + reader.readAsDataURL(blob); + }); + } + + return { + uriToBlob: uriToBlob, + blobToDataUri: blobToDataUri, + parseDataUri: parseDataUri + }; + } + ); + /** + * ImageScanner.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Finds images with data uris or blob uris. If data uris are found it will convert them into blob uris. + * + * @private + * @class tinymce.file.ImageScanner + */ + define( + 'tinymce.core.file.ImageScanner', [ + "tinymce.core.util.Promise", + "tinymce.core.util.Arr", + "tinymce.core.util.Fun", + "tinymce.core.file.Conversions", + "tinymce.core.Env" + ], + function(Promise, Arr, Fun, Conversions, Env) { + var count = 0; + + var uniqueId = function(prefix) { + return (prefix || 'blobid') + (count++); + }; + + var imageToBlobInfo = function(blobCache, img, resolve, reject) { + var base64, blobInfo; + + if (img.src.indexOf('blob:') === 0) { + blobInfo = blobCache.getByUri(img.src); + + if (blobInfo) { + resolve({ + image: img, + blobInfo: blobInfo + }); + } else { + Conversions.uriToBlob(img.src).then(function(blob) { + Conversions.blobToDataUri(blob).then(function(dataUri) { + base64 = Conversions.parseDataUri(dataUri).data; + blobInfo = blobCache.create(uniqueId(), blob, base64); + blobCache.add(blobInfo); + + resolve({ + image: img, + blobInfo: blobInfo + }); + }); + }, function(err) { + reject(err); + }); + } + + return; + } + + base64 = Conversions.parseDataUri(img.src).data; + blobInfo = blobCache.findFirst(function(cachedBlobInfo) { + return cachedBlobInfo.base64() === base64; + }); + + if (blobInfo) { + resolve({ + image: img, + blobInfo: blobInfo + }); + } else { + Conversions.uriToBlob(img.src).then(function(blob) { + blobInfo = blobCache.create(uniqueId(), blob, base64); + blobCache.add(blobInfo); + + resolve({ + image: img, + blobInfo: blobInfo + }); + }, function(err) { + reject(err); + }); + } + }; + + var getAllImages = function(elm) { + return elm ? elm.getElementsByTagName('img') : []; + }; + + return function(uploadStatus, blobCache) { + var cachedPromises = {}; + + function findAll(elm, predicate) { + var images, promises; + + if (!predicate) { + predicate = Fun.constant(true); + } + + images = Arr.filter(getAllImages(elm), function(img) { + var src = img.src; + + if (!Env.fileApi) { + return false; + } + + if (img.hasAttribute('data-mce-bogus')) { + return false; + } + + if (img.hasAttribute('data-mce-placeholder')) { + return false; + } + + if (!src || src == Env.transparentSrc) { + return false; + } + + if (src.indexOf('blob:') === 0) { + return !uploadStatus.isUploaded(src); + } + + if (src.indexOf('data:') === 0) { + return predicate(img); + } + + return false; + }); + + promises = Arr.map(images, function(img) { + var newPromise; + + if (cachedPromises[img.src]) { + // Since the cached promise will return the cached image + // We need to wrap it and resolve with the actual image + return new Promise(function(resolve) { + cachedPromises[img.src].then(function(imageInfo) { + if (typeof imageInfo === 'string') { // error apparently + return imageInfo; + } + resolve({ + image: img, + blobInfo: imageInfo.blobInfo + }); + }); + }); + } + + newPromise = new Promise(function(resolve, reject) { + imageToBlobInfo(blobCache, img, resolve, reject); + }).then(function(result) { + delete cachedPromises[result.image.src]; + return result; + })['catch'](function(error) { + delete cachedPromises[img.src]; + return error; + }); + + cachedPromises[img.src] = newPromise; + + return newPromise; + }); + + return Promise.all(promises); + } + + return { + findAll: findAll + }; + }; + } + ); + /** + * Uuid.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Generates unique ids. + * + * @class tinymce.util.Uuid + * @private + */ + define( + 'tinymce.core.util.Uuid', [], + function() { + var count = 0; + + var seed = function() { + var rnd = function() { + return Math.round(Math.random() * 0xFFFFFFFF).toString(36); + }; + + var now = new Date().getTime(); + return 's' + now.toString(36) + rnd() + rnd() + rnd(); + }; + + var uuid = function(prefix) { + return prefix + (count++) + seed(); + }; + + return { + uuid: uuid + }; + } + ); + + defineGlobal("global!URL", URL); + /** + * BlobCache.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Hold blob info objects where a blob has extra internal information. + * + * @private + * @class tinymce.file.BlobCache + */ + define( + 'tinymce.core.file.BlobCache', [ + 'tinymce.core.util.Arr', + 'tinymce.core.util.Fun', + 'tinymce.core.util.Uuid', + 'global!URL' + ], + function(Arr, Fun, Uuid, URL) { + return function() { + var cache = [], + constant = Fun.constant; + + function mimeToExt(mime) { + var mimes = { + 'image/jpeg': 'jpg', + 'image/jpg': 'jpg', + 'image/gif': 'gif', + 'image/png': 'png' + }; + + return mimes[mime.toLowerCase()] || 'dat'; + } + + function create(o, blob, base64, filename) { + return typeof o === 'object' ? toBlobInfo(o) : toBlobInfo({ + id: o, + name: filename, + blob: blob, + base64: base64 + }); + } + + function toBlobInfo(o) { + var id, name; + + if (!o.blob || !o.base64) { + throw "blob and base64 representations of the image are required for BlobInfo to be created"; + } + + id = o.id || Uuid.uuid('blobid'); + name = o.name || id; + + return { + id: constant(id), + name: constant(name), + filename: constant(name + '.' + mimeToExt(o.blob.type)), + blob: constant(o.blob), + base64: constant(o.base64), + blobUri: constant(o.blobUri || URL.createObjectURL(o.blob)), + uri: constant(o.uri) + }; + } + + function add(blobInfo) { + if (!get(blobInfo.id())) { + cache.push(blobInfo); + } + } + + function get(id) { + return findFirst(function(cachedBlobInfo) { + return cachedBlobInfo.id() === id; + }); + } + + function findFirst(predicate) { + return Arr.filter(cache, predicate)[0]; + } + + function getByUri(blobUri) { + return findFirst(function(blobInfo) { + return blobInfo.blobUri() == blobUri; + }); + } + + function removeByUri(blobUri) { + cache = Arr.filter(cache, function(blobInfo) { + if (blobInfo.blobUri() === blobUri) { + URL.revokeObjectURL(blobInfo.blobUri()); + return false; + } + + return true; + }); + } + + function destroy() { + Arr.each(cache, function(cachedBlobInfo) { + URL.revokeObjectURL(cachedBlobInfo.blobUri()); + }); + + cache = []; + } + + return { + create: create, + add: add, + get: get, + getByUri: getByUri, + findFirst: findFirst, + removeByUri: removeByUri, + destroy: destroy + }; + }; + } + ); + /** + * UploadStatus.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Holds the current status of a blob uri, if it's pending or uploaded and what the result urls was. + * + * @private + * @class tinymce.file.UploadStatus + */ + define( + 'tinymce.core.file.UploadStatus', [], + function() { + return function() { + var PENDING = 1, + UPLOADED = 2; + var blobUriStatuses = {}; + + function createStatus(status, resultUri) { + return { + status: status, + resultUri: resultUri + }; + } + + function hasBlobUri(blobUri) { + return blobUri in blobUriStatuses; + } + + function getResultUri(blobUri) { + var result = blobUriStatuses[blobUri]; + + return result ? result.resultUri : null; + } + + function isPending(blobUri) { + return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === PENDING : false; + } + + function isUploaded(blobUri) { + return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === UPLOADED : false; + } + + function markPending(blobUri) { + blobUriStatuses[blobUri] = createStatus(PENDING, null); + } + + function markUploaded(blobUri, resultUri) { + blobUriStatuses[blobUri] = createStatus(UPLOADED, resultUri); + } + + function removeFailed(blobUri) { + delete blobUriStatuses[blobUri]; + } + + function destroy() { + blobUriStatuses = {}; + } + + return { + hasBlobUri: hasBlobUri, + getResultUri: getResultUri, + isPending: isPending, + isUploaded: isUploaded, + markPending: markPending, + markUploaded: markUploaded, + removeFailed: removeFailed, + destroy: destroy + }; + }; + } + ); + /** + * EditorUpload.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Handles image uploads, updates undo stack and patches over various internal functions. + * + * @private + * @class tinymce.EditorUpload + */ + define( + 'tinymce.core.EditorUpload', [ + "tinymce.core.util.Arr", + "tinymce.core.file.Uploader", + "tinymce.core.file.ImageScanner", + "tinymce.core.file.BlobCache", + "tinymce.core.file.UploadStatus", + "tinymce.core.ErrorReporter" + ], + function(Arr, Uploader, ImageScanner, BlobCache, UploadStatus, ErrorReporter) { + return function(editor) { + var blobCache = new BlobCache(), + uploader, imageScanner, settings = editor.settings; + var uploadStatus = new UploadStatus(); + + function aliveGuard(callback) { + return function(result) { + if (editor.selection) { + return callback(result); + } + + return []; + }; + } + + function cacheInvalidator() { + return '?' + (new Date()).getTime(); + } + + // Replaces strings without regexps to avoid FF regexp to big issue + function replaceString(content, search, replace) { + var index = 0; + + do { + index = content.indexOf(search, index); + + if (index !== -1) { + content = content.substring(0, index) + replace + content.substr(index + search.length); + index += replace.length - search.length + 1; + } + } while (index !== -1); + + return content; + } + + function replaceImageUrl(content, targetUrl, replacementUrl) { + content = replaceString(content, 'src="' + targetUrl + '"', 'src="' + replacementUrl + '"'); + content = replaceString(content, 'data-mce-src="' + targetUrl + '"', 'data-mce-src="' + replacementUrl + '"'); + + return content; + } + + function replaceUrlInUndoStack(targetUrl, replacementUrl) { + Arr.each(editor.undoManager.data, function(level) { + if (level.type === 'fragmented') { + level.fragments = Arr.map(level.fragments, function(fragment) { + return replaceImageUrl(fragment, targetUrl, replacementUrl); + }); + } else { + level.content = replaceImageUrl(level.content, targetUrl, replacementUrl); + } + }); + } + + function openNotification() { + return editor.notificationManager.open({ + text: editor.translate('Image uploading...'), + type: 'info', + timeout: -1, + progressBar: true + }); + } + + function replaceImageUri(image, resultUri) { + blobCache.removeByUri(image.src); + replaceUrlInUndoStack(image.src, resultUri); + + editor.$(image).attr({ + src: settings.images_reuse_filename ? resultUri + cacheInvalidator() : resultUri, + 'data-mce-src': editor.convertURL(resultUri, 'src') + }); + } + + function uploadImages(callback) { + if (!uploader) { + uploader = new Uploader(uploadStatus, { + url: settings.images_upload_url, + basePath: settings.images_upload_base_path, + credentials: settings.images_upload_credentials, + handler: settings.images_upload_handler + }); + } + + return scanForImages().then(aliveGuard(function(imageInfos) { + var blobInfos; + + blobInfos = Arr.map(imageInfos, function(imageInfo) { + return imageInfo.blobInfo; + }); + + return uploader.upload(blobInfos, openNotification).then(aliveGuard(function(result) { + var filteredResult = Arr.map(result, function(uploadInfo, index) { + var image = imageInfos[index].image; + + if (uploadInfo.status && editor.settings.images_replace_blob_uris !== false) { + replaceImageUri(image, uploadInfo.url); + } else if (uploadInfo.error) { + ErrorReporter.uploadError(editor, uploadInfo.error); + } + + return { + element: image, + status: uploadInfo.status + }; + }); + + if (callback) { + callback(filteredResult); + } + + return filteredResult; + })); + })); + } + + function uploadImagesAuto(callback) { + if (settings.automatic_uploads !== false) { + return uploadImages(callback); + } + } + + function isValidDataUriImage(imgElm) { + return settings.images_dataimg_filter ? settings.images_dataimg_filter(imgElm) : true; + } + + function scanForImages() { + if (!imageScanner) { + imageScanner = new ImageScanner(uploadStatus, blobCache); + } + + return imageScanner.findAll(editor.getBody(), isValidDataUriImage).then(aliveGuard(function(result) { + result = Arr.filter(result, function(resultItem) { + // ImageScanner internally converts images that it finds, but it may fail to do so if image source is inaccessible. + // In such case resultItem will contain appropriate text error message, instead of image data. + if (typeof resultItem === 'string') { + ErrorReporter.displayError(editor, resultItem); + return false; + } + return true; + }); + + Arr.each(result, function(resultItem) { + replaceUrlInUndoStack(resultItem.image.src, resultItem.blobInfo.blobUri()); + resultItem.image.src = resultItem.blobInfo.blobUri(); + resultItem.image.removeAttribute('data-mce-src'); + }); + + return result; + })); + } + + function destroy() { + blobCache.destroy(); + uploadStatus.destroy(); + imageScanner = uploader = null; + } + + function replaceBlobUris(content) { + return content.replace(/src="(blob:[^"]+)"/g, function(match, blobUri) { + var resultUri = uploadStatus.getResultUri(blobUri); + + if (resultUri) { + return 'src="' + resultUri + '"'; + } + + var blobInfo = blobCache.getByUri(blobUri); + + if (!blobInfo) { + blobInfo = Arr.reduce(editor.editorManager.editors, function(result, editor) { + return result || editor.editorUpload && editor.editorUpload.blobCache.getByUri(blobUri); + }, null); + } + + if (blobInfo) { + return 'src="data:' + blobInfo.blob().type + ';base64,' + blobInfo.base64() + '"'; + } + + return match; + }); + } + + editor.on('setContent', function() { + if (editor.settings.automatic_uploads !== false) { + uploadImagesAuto(); + } else { + scanForImages(); + } + }); + + editor.on('RawSaveContent', function(e) { + e.content = replaceBlobUris(e.content); + }); + + editor.on('getContent', function(e) { + if (e.source_view || e.format == 'raw') { + return; + } + + e.content = replaceBlobUris(e.content); + }); + + editor.on('PostRender', function() { + editor.parser.addNodeFilter('img', function(images) { + Arr.each(images, function(img) { + var src = img.attr('src'); + + if (blobCache.getByUri(src)) { + return; + } + + var resultUri = uploadStatus.getResultUri(src); + if (resultUri) { + img.attr('src', resultUri); + } + }); + }); + }); + + return { + blobCache: blobCache, + uploadImages: uploadImages, + uploadImagesAuto: uploadImagesAuto, + scanForImages: scanForImages, + destroy: destroy + }; + }; + } + ); + /** + * ForceBlocks.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Makes sure that everything gets wrapped in paragraphs. + * + * @private + * @class tinymce.ForceBlocks + */ + define( + 'tinymce.core.ForceBlocks', [ + 'ephox.katamari.api.Fun' + ], + function(Fun) { + var addRootBlocks = function(editor) { + var settings = editor.settings, + dom = editor.dom, + selection = editor.selection; + var schema = editor.schema, + blockElements = schema.getBlockElements(); + var node = selection.getStart(), + rootNode = editor.getBody(), + rng; + var startContainer, startOffset, endContainer, endOffset, rootBlockNode; + var tempNode, offset = -0xFFFFFF, + wrapped, restoreSelection; + var tmpRng, rootNodeName, forcedRootBlock; + + forcedRootBlock = settings.forced_root_block; + + if (!node || node.nodeType !== 1 || !forcedRootBlock) { + return; + } + + // Check if node is wrapped in block + while (node && node !== rootNode) { + if (blockElements[node.nodeName]) { + return; + } + + node = node.parentNode; + } + + // Get current selection + rng = selection.getRng(); + if (rng.setStart) { + startContainer = rng.startContainer; + startOffset = rng.startOffset; + endContainer = rng.endContainer; + endOffset = rng.endOffset; + + try { + restoreSelection = editor.getDoc().activeElement === rootNode; + } catch (ex) { + // IE throws unspecified error here sometimes + } + } else { + // Force control range into text range + if (rng.item) { + node = rng.item(0); + rng = editor.getDoc().body.createTextRange(); + rng.moveToElementText(node); + } + + restoreSelection = rng.parentElement().ownerDocument === editor.getDoc(); + tmpRng = rng.duplicate(); + tmpRng.collapse(true); + startOffset = tmpRng.move('character', offset) * -1; + + if (!tmpRng.collapsed) { + tmpRng = rng.duplicate(); + tmpRng.collapse(false); + endOffset = (tmpRng.move('character', offset) * -1) - startOffset; + } + } + + // Wrap non block elements and text nodes + node = rootNode.firstChild; + rootNodeName = rootNode.nodeName.toLowerCase(); + while (node) { + // TODO: Break this up, too complex + if (((node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName]))) && + schema.isValidChild(rootNodeName, forcedRootBlock.toLowerCase())) { + // Remove empty text nodes + if (node.nodeType === 3 && node.nodeValue.length === 0) { + tempNode = node; + node = node.nextSibling; + dom.remove(tempNode); + continue; + } + + if (!rootBlockNode) { + rootBlockNode = dom.create(forcedRootBlock, editor.settings.forced_root_block_attrs); + node.parentNode.insertBefore(rootBlockNode, node); + wrapped = true; + } + + tempNode = node; + node = node.nextSibling; + rootBlockNode.appendChild(tempNode); + } else { + rootBlockNode = null; + node = node.nextSibling; + } + } + + if (wrapped && restoreSelection) { + if (rng.setStart) { + rng.setStart(startContainer, startOffset); + rng.setEnd(endContainer, endOffset); + selection.setRng(rng); + } else { + // Only select if the previous selection was inside the document to prevent auto focus in quirks mode + try { + rng = editor.getDoc().body.createTextRange(); + rng.moveToElementText(rootNode); + rng.collapse(true); + rng.moveStart('character', startOffset); + + if (endOffset > 0) { + rng.moveEnd('character', endOffset); + } + + rng.select(); + } catch (ex) { + // Ignore + } + } + + editor.nodeChanged(); + } + }; + + var setup = function(editor) { + if (editor.settings.forced_root_block) { + editor.on('NodeChange', Fun.curry(addRootBlocks, editor)); + } + }; + + return { + setup: setup + }; + } + ); + /** + * Dimensions.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module measures nodes and returns client rects. The client rects has an + * extra node property. + * + * @private + * @class tinymce.dom.Dimensions + */ + define( + 'tinymce.core.dom.Dimensions', [ + "tinymce.core.util.Arr", + "tinymce.core.dom.NodeType", + "tinymce.core.geom.ClientRect" + ], + function(Arr, NodeType, ClientRect) { + + function getClientRects(node) { + function toArrayWithNode(clientRects) { + return Arr.map(clientRects, function(clientRect) { + clientRect = ClientRect.clone(clientRect); + clientRect.node = node; + + return clientRect; + }); + } + + if (Arr.isArray(node)) { + return Arr.reduce(node, function(result, node) { + return result.concat(getClientRects(node)); + }, []); + } + + if (NodeType.isElement(node)) { + return toArrayWithNode(node.getClientRects()); + } + + if (NodeType.isText(node)) { + var rng = node.ownerDocument.createRange(); + + rng.setStart(node, 0); + rng.setEnd(node, node.data.length); + + return toArrayWithNode(rng.getClientRects()); + } + } + + return { + /** + * Returns the client rects for a specific node. + * + * @method getClientRects + * @param {Array/DOMNode} node Node or array of nodes to get client rects on. + * @param {Array} Array of client rects with a extra node property. + */ + getClientRects: getClientRects + }; + } + ); + /** + * LineUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Utility functions for working with lines. + * + * @private + * @class tinymce.caret.LineUtils + */ + define( + 'tinymce.core.caret.LineUtils', [ + "tinymce.core.util.Fun", + "tinymce.core.util.Arr", + "tinymce.core.dom.NodeType", + "tinymce.core.dom.Dimensions", + "tinymce.core.geom.ClientRect", + "tinymce.core.caret.CaretUtils", + "tinymce.core.caret.CaretCandidate" + ], + function(Fun, Arr, NodeType, Dimensions, ClientRect, CaretUtils, CaretCandidate) { + var isContentEditableFalse = NodeType.isContentEditableFalse, + findNode = CaretUtils.findNode, + curry = Fun.curry; + + function distanceToRectLeft(clientRect, clientX) { + return Math.abs(clientRect.left - clientX); + } + + function distanceToRectRight(clientRect, clientX) { + return Math.abs(clientRect.right - clientX); + } + + function findClosestClientRect(clientRects, clientX) { + function isInside(clientX, clientRect) { + return clientX >= clientRect.left && clientX <= clientRect.right; + } + + return Arr.reduce(clientRects, function(oldClientRect, clientRect) { + var oldDistance, newDistance; + + oldDistance = Math.min(distanceToRectLeft(oldClientRect, clientX), distanceToRectRight(oldClientRect, clientX)); + newDistance = Math.min(distanceToRectLeft(clientRect, clientX), distanceToRectRight(clientRect, clientX)); + + if (isInside(clientX, clientRect)) { + return clientRect; + } + + if (isInside(clientX, oldClientRect)) { + return oldClientRect; + } + + // cE=false has higher priority + if (newDistance == oldDistance && isContentEditableFalse(clientRect.node)) { + return clientRect; + } + + if (newDistance < oldDistance) { + return clientRect; + } + + return oldClientRect; + }); + } + + function walkUntil(direction, rootNode, predicateFn, node) { + while ((node = findNode(node, direction, CaretCandidate.isEditableCaretCandidate, rootNode))) { + if (predicateFn(node)) { + return; + } + } + } + + function findLineNodeRects(rootNode, targetNodeRect) { + var clientRects = []; + + function collect(checkPosFn, node) { + var lineRects; + + lineRects = Arr.filter(Dimensions.getClientRects(node), function(clientRect) { + return !checkPosFn(clientRect, targetNodeRect); + }); + + clientRects = clientRects.concat(lineRects); + + return lineRects.length === 0; + } + + clientRects.push(targetNodeRect); + walkUntil(-1, rootNode, curry(collect, ClientRect.isAbove), targetNodeRect.node); + walkUntil(1, rootNode, curry(collect, ClientRect.isBelow), targetNodeRect.node); + + return clientRects; + } + + function getContentEditableFalseChildren(rootNode) { + return Arr.filter(Arr.toArray(rootNode.getElementsByTagName('*')), isContentEditableFalse); + } + + function caretInfo(clientRect, clientX) { + return { + node: clientRect.node, + before: distanceToRectLeft(clientRect, clientX) < distanceToRectRight(clientRect, clientX) + }; + } + + function closestCaret(rootNode, clientX, clientY) { + var contentEditableFalseNodeRects, closestNodeRect; + + contentEditableFalseNodeRects = Dimensions.getClientRects(getContentEditableFalseChildren(rootNode)); + contentEditableFalseNodeRects = Arr.filter(contentEditableFalseNodeRects, function(clientRect) { + return clientY >= clientRect.top && clientY <= clientRect.bottom; + }); + + closestNodeRect = findClosestClientRect(contentEditableFalseNodeRects, clientX); + if (closestNodeRect) { + closestNodeRect = findClosestClientRect(findLineNodeRects(rootNode, closestNodeRect), clientX); + if (closestNodeRect && isContentEditableFalse(closestNodeRect.node)) { + return caretInfo(closestNodeRect, clientX); + } + } + + return null; + } + + return { + findClosestClientRect: findClosestClientRect, + findLineNodeRects: findLineNodeRects, + closestCaret: closestCaret + }; + } + ); + /** + * LineWalker.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module lets you walk the document line by line + * returing nodes and client rects for each line. + * + * @private + * @class tinymce.caret.LineWalker + */ + define( + 'tinymce.core.caret.LineWalker', [ + "tinymce.core.util.Fun", + "tinymce.core.util.Arr", + "tinymce.core.dom.Dimensions", + "tinymce.core.caret.CaretCandidate", + "tinymce.core.caret.CaretUtils", + "tinymce.core.caret.CaretWalker", + "tinymce.core.caret.CaretPosition", + "tinymce.core.geom.ClientRect" + ], + function(Fun, Arr, Dimensions, CaretCandidate, CaretUtils, CaretWalker, CaretPosition, ClientRect) { + var curry = Fun.curry; + + function findUntil(direction, rootNode, predicateFn, node) { + while ((node = CaretUtils.findNode(node, direction, CaretCandidate.isEditableCaretCandidate, rootNode))) { + if (predicateFn(node)) { + return; + } + } + } + + function walkUntil(direction, isAboveFn, isBeflowFn, rootNode, predicateFn, caretPosition) { + var line = 0, + node, result = [], + targetClientRect; + + function add(node) { + var i, clientRect, clientRects; + + clientRects = Dimensions.getClientRects(node); + if (direction == -1) { + clientRects = clientRects.reverse(); + } + + for (i = 0; i < clientRects.length; i++) { + clientRect = clientRects[i]; + if (isBeflowFn(clientRect, targetClientRect)) { + continue; + } + + if (result.length > 0 && isAboveFn(clientRect, Arr.last(result))) { + line++; + } + + clientRect.line = line; + + if (predicateFn(clientRect)) { + return true; + } + + result.push(clientRect); + } + } + + targetClientRect = Arr.last(caretPosition.getClientRects()); + if (!targetClientRect) { + return result; + } + + node = caretPosition.getNode(); + add(node); + findUntil(direction, rootNode, add, node); + + return result; + } + + function aboveLineNumber(lineNumber, clientRect) { + return clientRect.line > lineNumber; + } + + function isLine(lineNumber, clientRect) { + return clientRect.line === lineNumber; + } + + var upUntil = curry(walkUntil, -1, ClientRect.isAbove, ClientRect.isBelow); + var downUntil = curry(walkUntil, 1, ClientRect.isBelow, ClientRect.isAbove); + + function positionsUntil(direction, rootNode, predicateFn, node) { + var caretWalker = new CaretWalker(rootNode), + walkFn, isBelowFn, isAboveFn, + caretPosition, result = [], + line = 0, + clientRect, targetClientRect; + + function getClientRect(caretPosition) { + if (direction == 1) { + return Arr.last(caretPosition.getClientRects()); + } + + return Arr.last(caretPosition.getClientRects()); + } + + if (direction == 1) { + walkFn = caretWalker.next; + isBelowFn = ClientRect.isBelow; + isAboveFn = ClientRect.isAbove; + caretPosition = CaretPosition.after(node); + } else { + walkFn = caretWalker.prev; + isBelowFn = ClientRect.isAbove; + isAboveFn = ClientRect.isBelow; + caretPosition = CaretPosition.before(node); + } + + targetClientRect = getClientRect(caretPosition); + + do { + if (!caretPosition.isVisible()) { + continue; + } + + clientRect = getClientRect(caretPosition); + + if (isAboveFn(clientRect, targetClientRect)) { + continue; + } + + if (result.length > 0 && isBelowFn(clientRect, Arr.last(result))) { + line++; + } + + clientRect = ClientRect.clone(clientRect); + clientRect.position = caretPosition; + clientRect.line = line; + + if (predicateFn(clientRect)) { + return result; + } + + result.push(clientRect); + } while ((caretPosition = walkFn(caretPosition))); + + return result; + } + + return { + upUntil: upUntil, + downUntil: downUntil, + + /** + * Find client rects with line and caret position until the predicate returns true. + * + * @method positionsUntil + * @param {Number} direction Direction forward/backward 1/-1. + * @param {DOMNode} rootNode Root node to walk within. + * @param {function} predicateFn Gets the client rect as it's input. + * @param {DOMNode} node Node to start walking from. + * @return {Array} Array of client rects with line and position properties. + */ + positionsUntil: positionsUntil, + + isAboveLine: curry(aboveLineNumber), + isLine: curry(isLine) + }; + } + ); + /** + * CefUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.keyboard.CefUtils', [ + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.caret.CaretUtils', + 'tinymce.core.dom.NodeType', + 'tinymce.core.util.Fun' + ], + function(CaretPosition, CaretUtils, NodeType, Fun) { + var isContentEditableTrue = NodeType.isContentEditableTrue; + var isContentEditableFalse = NodeType.isContentEditableFalse; + + var showCaret = function(direction, editor, node, before) { + // TODO: Figure out a better way to handle this dependency + return editor._selectionOverrides.showCaret(direction, node, before); + }; + + var getNodeRange = function(node) { + var rng = node.ownerDocument.createRange(); + rng.selectNode(node); + return rng; + }; + + var selectNode = function(editor, node) { + var e; + + e = editor.fire('BeforeObjectSelected', { + target: node + }); + if (e.isDefaultPrevented()) { + return null; + } + + return getNodeRange(node); + }; + + var renderCaretAtRange = function(editor, range) { + var caretPosition, ceRoot; + + range = CaretUtils.normalizeRange(1, editor.getBody(), range); + caretPosition = CaretPosition.fromRangeStart(range); + + if (isContentEditableFalse(caretPosition.getNode())) { + return showCaret(1, editor, caretPosition.getNode(), !caretPosition.isAtEnd()); + } + + if (isContentEditableFalse(caretPosition.getNode(true))) { + return showCaret(1, editor, caretPosition.getNode(true), false); + } + + // TODO: Should render caret before/after depending on where you click on the page forces after now + ceRoot = editor.dom.getParent(caretPosition.getNode(), Fun.or(isContentEditableFalse, isContentEditableTrue)); + if (isContentEditableFalse(ceRoot)) { + return showCaret(1, editor, ceRoot, false); + } + + return null; + }; + + var renderRangeCaret = function(editor, range) { + var caretRange; + + if (!range || !range.collapsed) { + return range; + } + + caretRange = renderCaretAtRange(editor, range); + if (caretRange) { + return caretRange; + } + + return range; + }; + + return { + showCaret: showCaret, + selectNode: selectNode, + renderCaretAtRange: renderCaretAtRange, + renderRangeCaret: renderRangeCaret + }; + } + ); + + /** + * CefNavigation.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.keyboard.CefNavigation', [ + 'tinymce.core.caret.CaretContainer', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.caret.CaretUtils', + 'tinymce.core.caret.CaretWalker', + 'tinymce.core.caret.LineUtils', + 'tinymce.core.caret.LineWalker', + 'tinymce.core.dom.NodeType', + 'tinymce.core.dom.RangeUtils', + 'tinymce.core.Env', + 'tinymce.core.keyboard.CefUtils', + 'tinymce.core.util.Arr', + 'tinymce.core.util.Fun' + ], + function(CaretContainer, CaretPosition, CaretUtils, CaretWalker, LineUtils, LineWalker, NodeType, RangeUtils, Env, CefUtils, Arr, Fun) { + var isContentEditableFalse = NodeType.isContentEditableFalse; + var getSelectedNode = RangeUtils.getSelectedNode; + var isAfterContentEditableFalse = CaretUtils.isAfterContentEditableFalse; + var isBeforeContentEditableFalse = CaretUtils.isBeforeContentEditableFalse; + + var getVisualCaretPosition = function(walkFn, caretPosition) { + while ((caretPosition = walkFn(caretPosition))) { + if (caretPosition.isVisible()) { + return caretPosition; + } + } + + return caretPosition; + }; + + var isMoveInsideSameBlock = function(fromCaretPosition, toCaretPosition) { + var inSameBlock = CaretUtils.isInSameBlock(fromCaretPosition, toCaretPosition); + + // Handle bogus BR <p>abc|<br></p> + if (!inSameBlock && NodeType.isBr(fromCaretPosition.getNode())) { + return true; + } + + return inSameBlock; + }; + + var isRangeInCaretContainerBlock = function(range) { + return CaretContainer.isCaretContainerBlock(range.startContainer); + }; + + var getNormalizedRangeEndPoint = function(direction, rootNode, range) { + range = CaretUtils.normalizeRange(direction, rootNode, range); + + if (direction === -1) { + return CaretPosition.fromRangeStart(range); + } + + return CaretPosition.fromRangeEnd(range); + }; + + var moveToCeFalseHorizontally = function(direction, editor, getNextPosFn, isBeforeContentEditableFalseFn, range) { + var node, caretPosition, peekCaretPosition, rangeIsInContainerBlock; + + if (!range.collapsed) { + node = getSelectedNode(range); + if (isContentEditableFalse(node)) { + return CefUtils.showCaret(direction, editor, node, direction === -1); + } + } + + rangeIsInContainerBlock = isRangeInCaretContainerBlock(range); + caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range); + + if (isBeforeContentEditableFalseFn(caretPosition)) { + return CefUtils.selectNode(editor, caretPosition.getNode(direction === -1)); + } + + caretPosition = getNextPosFn(caretPosition); + if (!caretPosition) { + if (rangeIsInContainerBlock) { + return range; + } + + return null; + } + + if (isBeforeContentEditableFalseFn(caretPosition)) { + return CefUtils.showCaret(direction, editor, caretPosition.getNode(direction === -1), direction === 1); + } + + // Peek ahead for handling of ab|c<span cE=false> -> abc|<span cE=false> + peekCaretPosition = getNextPosFn(caretPosition); + if (isBeforeContentEditableFalseFn(peekCaretPosition)) { + if (isMoveInsideSameBlock(caretPosition, peekCaretPosition)) { + return CefUtils.showCaret(direction, editor, peekCaretPosition.getNode(direction === -1), direction === 1); + } + } + + if (rangeIsInContainerBlock) { + return CefUtils.renderRangeCaret(editor, caretPosition.toRange()); + } + + return null; + }; + + var moveToCeFalseVertically = function(direction, editor, walkerFn, range) { + var caretPosition, linePositions, nextLinePositions, + closestNextLineRect, caretClientRect, clientX, + dist1, dist2, contentEditableFalseNode; + + contentEditableFalseNode = getSelectedNode(range); + caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range); + linePositions = walkerFn(editor.getBody(), LineWalker.isAboveLine(1), caretPosition); + nextLinePositions = Arr.filter(linePositions, LineWalker.isLine(1)); + caretClientRect = Arr.last(caretPosition.getClientRects()); + + if (isBeforeContentEditableFalse(caretPosition)) { + contentEditableFalseNode = caretPosition.getNode(); + } + + if (isAfterContentEditableFalse(caretPosition)) { + contentEditableFalseNode = caretPosition.getNode(true); + } + + if (!caretClientRect) { + return null; + } + + clientX = caretClientRect.left; + + closestNextLineRect = LineUtils.findClosestClientRect(nextLinePositions, clientX); + if (closestNextLineRect) { + if (isContentEditableFalse(closestNextLineRect.node)) { + dist1 = Math.abs(clientX - closestNextLineRect.left); + dist2 = Math.abs(clientX - closestNextLineRect.right); + + return CefUtils.showCaret(direction, editor, closestNextLineRect.node, dist1 < dist2); + } + } + + if (contentEditableFalseNode) { + var caretPositions = LineWalker.positionsUntil(direction, editor.getBody(), LineWalker.isAboveLine(1), contentEditableFalseNode); + + closestNextLineRect = LineUtils.findClosestClientRect(Arr.filter(caretPositions, LineWalker.isLine(1)), clientX); + if (closestNextLineRect) { + return CefUtils.renderRangeCaret(editor, closestNextLineRect.position.toRange()); + } + + closestNextLineRect = Arr.last(Arr.filter(caretPositions, LineWalker.isLine(0))); + if (closestNextLineRect) { + return CefUtils.renderRangeCaret(editor, closestNextLineRect.position.toRange()); + } + } + }; + + var createTextBlock = function(editor) { + var textBlock = editor.dom.create(editor.settings.forced_root_block); + + if (!Env.ie || Env.ie >= 11) { + textBlock.innerHTML = '<br data-mce-bogus="1">'; + } + + return textBlock; + }; + + var exitPreBlock = function(editor, direction, range) { + var pre, caretPos, newBlock; + var caretWalker = new CaretWalker(editor.getBody()); + var getNextVisualCaretPosition = Fun.curry(getVisualCaretPosition, caretWalker.next); + var getPrevVisualCaretPosition = Fun.curry(getVisualCaretPosition, caretWalker.prev); + + if (range.collapsed && editor.settings.forced_root_block) { + pre = editor.dom.getParent(range.startContainer, 'PRE'); + if (!pre) { + return; + } + + if (direction === 1) { + caretPos = getNextVisualCaretPosition(CaretPosition.fromRangeStart(range)); + } else { + caretPos = getPrevVisualCaretPosition(CaretPosition.fromRangeStart(range)); + } + + if (!caretPos) { + newBlock = createTextBlock(editor); + + if (direction === 1) { + editor.$(pre).after(newBlock); + } else { + editor.$(pre).before(newBlock); + } + + editor.selection.select(newBlock, true); + editor.selection.collapse(); + } + } + }; + + var getHorizontalRange = function(editor, forward) { + var caretWalker = new CaretWalker(editor.getBody()); + var getNextVisualCaretPosition = Fun.curry(getVisualCaretPosition, caretWalker.next); + var getPrevVisualCaretPosition = Fun.curry(getVisualCaretPosition, caretWalker.prev); + var newRange, direction = forward ? 1 : -1; + var getNextPosFn = forward ? getNextVisualCaretPosition : getPrevVisualCaretPosition; + var isBeforeContentEditableFalseFn = forward ? isBeforeContentEditableFalse : isAfterContentEditableFalse; + var range = editor.selection.getRng(); + + newRange = moveToCeFalseHorizontally(direction, editor, getNextPosFn, isBeforeContentEditableFalseFn, range); + if (newRange) { + return newRange; + } + + newRange = exitPreBlock(editor, direction, range); + if (newRange) { + return newRange; + } + + return null; + }; + + var getVerticalRange = function(editor, down) { + var newRange, direction = down ? 1 : -1; + var walkerFn = down ? LineWalker.downUntil : LineWalker.upUntil; + var range = editor.selection.getRng(); + + newRange = moveToCeFalseVertically(direction, editor, walkerFn, range); + if (newRange) { + return newRange; + } + + newRange = exitPreBlock(editor, direction, range); + if (newRange) { + return newRange; + } + + return null; + }; + + var moveH = function(editor, forward) { + return function() { + var newRng = getHorizontalRange(editor, forward); + + if (newRng) { + editor.selection.setRng(newRng); + return true; + } else { + return false; + } + }; + }; + + var moveV = function(editor, down) { + return function() { + var newRng = getVerticalRange(editor, down); + + if (newRng) { + editor.selection.setRng(newRng); + return true; + } else { + return false; + } + }; + }; + + return { + moveH: moveH, + moveV: moveV + }; + } + ); + + define( + 'ephox.katamari.api.Merger', + + [ + 'ephox.katamari.api.Type', + 'global!Array', + 'global!Error' + ], + + function(Type, Array, Error) { + + var shallow = function(old, nu) { + return nu; + }; + + var deep = function(old, nu) { + var bothObjects = Type.isObject(old) && Type.isObject(nu); + return bothObjects ? deepMerge(old, nu) : nu; + }; + + var baseMerge = function(merger) { + return function() { + // Don't use array slice(arguments), makes the whole function unoptimisable on Chrome + var objects = new Array(arguments.length); + for (var i = 0; i < objects.length; i++) objects[i] = arguments[i]; + + if (objects.length === 0) throw new Error('Can\'t merge zero objects'); + + var ret = {}; + for (var j = 0; j < objects.length; j++) { + var curObject = objects[j]; + for (var key in curObject) + if (curObject.hasOwnProperty(key)) { + ret[key] = merger(ret[key], curObject[key]); + } + } + return ret; + }; + }; + + var deepMerge = baseMerge(deep); + var merge = baseMerge(shallow); + + return { + deepMerge: deepMerge, + merge: merge + }; + } + ); + /** + * MatchKeys.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.keyboard.MatchKeys', [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Merger' + ], + function(Arr, Fun, Merger) { + var defaultPatterns = function(patterns) { + return Arr.map(patterns, function(pattern) { + return Merger.merge({ + shiftKey: false, + altKey: false, + ctrlKey: false, + metaKey: false, + keyCode: 0, + action: Fun.noop + }, pattern); + }); + }; + + var matchesEvent = function(pattern, evt) { + return ( + evt.keyCode === pattern.keyCode && + evt.shiftKey === pattern.shiftKey && + evt.altKey === pattern.altKey && + evt.ctrlKey === pattern.ctrlKey && + evt.metaKey === pattern.metaKey + ); + }; + + var match = function(patterns, evt) { + return Arr.bind(defaultPatterns(patterns), function(pattern) { + return matchesEvent(pattern, evt) ? [pattern] : []; + }); + }; + + var action = function(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function() { + return f.apply(null, args); + }; + }; + + return { + match: match, + action: action + }; + } + ); + /** + * ArrowKeys.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.keyboard.ArrowKeys', [ + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Cell', + 'tinymce.core.keyboard.BoundarySelection', + 'tinymce.core.keyboard.CefNavigation', + 'tinymce.core.keyboard.MatchKeys', + 'tinymce.core.util.VK' + ], + function(Arr, Cell, BoundarySelection, CefNavigation, MatchKeys, VK) { + var executeKeydownOverride = function(editor, caret, evt) { + var matches = MatchKeys.match([{ + keyCode: VK.RIGHT, + action: CefNavigation.moveH(editor, true) + }, + { + keyCode: VK.LEFT, + action: CefNavigation.moveH(editor, false) + }, + { + keyCode: VK.UP, + action: CefNavigation.moveV(editor, false) + }, + { + keyCode: VK.DOWN, + action: CefNavigation.moveV(editor, true) + }, + { + keyCode: VK.RIGHT, + action: BoundarySelection.move(editor, caret, true) + }, + { + keyCode: VK.LEFT, + action: BoundarySelection.move(editor, caret, false) + } + ], evt); + + Arr.find(matches, function(pattern) { + return pattern.action(); + }).each(function(_) { + evt.preventDefault(); + }); + }; + + var setup = function(editor, caret) { + editor.on('keydown', function(evt) { + if (evt.isDefaultPrevented() === false) { + executeKeydownOverride(editor, caret, evt); + } + }); + }; + + return { + setup: setup + }; + } + ); + + /** + * DeleteBackspaceKeys.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.keyboard.DeleteBackspaceKeys', [ + 'ephox.katamari.api.Arr', + 'tinymce.core.delete.BlockBoundaryDelete', + 'tinymce.core.delete.BlockRangeDelete', + 'tinymce.core.delete.CefDelete', + 'tinymce.core.delete.InlineBoundaryDelete', + 'tinymce.core.keyboard.MatchKeys', + 'tinymce.core.util.VK' + ], + function(Arr, BlockBoundaryDelete, BlockRangeDelete, CefDelete, InlineBoundaryDelete, MatchKeys, VK) { + var executeKeydownOverride = function(editor, caret, evt) { + var matches = MatchKeys.match([{ + keyCode: VK.BACKSPACE, + action: MatchKeys.action(CefDelete.backspaceDelete, editor, false) + }, + { + keyCode: VK.DELETE, + action: MatchKeys.action(CefDelete.backspaceDelete, editor, true) + }, + { + keyCode: VK.BACKSPACE, + action: MatchKeys.action(InlineBoundaryDelete.backspaceDelete, editor, caret, false) + }, + { + keyCode: VK.DELETE, + action: MatchKeys.action(InlineBoundaryDelete.backspaceDelete, editor, caret, true) + }, + { + keyCode: VK.BACKSPACE, + action: MatchKeys.action(BlockRangeDelete.backspaceDelete, editor, false) + }, + { + keyCode: VK.DELETE, + action: MatchKeys.action(BlockRangeDelete.backspaceDelete, editor, true) + }, + { + keyCode: VK.BACKSPACE, + action: MatchKeys.action(BlockBoundaryDelete.backspaceDelete, editor, false) + }, + { + keyCode: VK.DELETE, + action: MatchKeys.action(BlockBoundaryDelete.backspaceDelete, editor, true) + } + ], evt); + + Arr.find(matches, function(pattern) { + return pattern.action(); + }).each(function(_) { + evt.preventDefault(); + }); + }; + + var executeKeyupOverride = function(editor, evt) { + var matches = MatchKeys.match([{ + keyCode: VK.BACKSPACE, + action: MatchKeys.action(CefDelete.paddEmptyElement, editor) + }, + { + keyCode: VK.DELETE, + action: MatchKeys.action(CefDelete.paddEmptyElement, editor) + } + ], evt); + + Arr.find(matches, function(pattern) { + return pattern.action(); + }); + }; + + var setup = function(editor, caret) { + editor.on('keydown', function(evt) { + if (evt.isDefaultPrevented() === false) { + executeKeydownOverride(editor, caret, evt); + } + }); + + editor.on('keyup', function(evt) { + if (evt.isDefaultPrevented() === false) { + executeKeyupOverride(editor, evt); + } + }); + }; + + return { + setup: setup + }; + } + ); + + /** + * EnterKey.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Contains logic for handling the enter key to split/generate block elements. + */ + define( + 'tinymce.core.keyboard.EnterKey', [ + 'tinymce.core.caret.CaretContainer', + 'tinymce.core.dom.NodeType', + 'tinymce.core.dom.RangeUtils', + 'tinymce.core.dom.TreeWalker', + 'tinymce.core.Env', + 'tinymce.core.text.Zwsp', + 'tinymce.core.util.Tools' + ], + function(CaretContainer, NodeType, RangeUtils, TreeWalker, Env, Zwsp, Tools) { + var isIE = Env.ie && Env.ie < 11; + + var isEmptyAnchor = function(elm) { + return elm && elm.nodeName === "A" && Tools.trim(Zwsp.trim(elm.innerText || elm.textContent)).length === 0; + }; + + var isTableCell = function(node) { + return node && /^(TD|TH|CAPTION)$/.test(node.nodeName); + }; + + var emptyBlock = function(elm) { + // BR is needed in empty blocks on non IE browsers + elm.innerHTML = !isIE ? '<br data-mce-bogus="1">' : ''; + }; + + var containerAndSiblingName = function(container, nodeName) { + return container.nodeName === nodeName || (container.previousSibling && container.previousSibling.nodeName === nodeName); + }; + + // Returns true if the block can be split into two blocks or not + var canSplitBlock = function(dom, node) { + return node && + dom.isBlock(node) && + !/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) && + !/^(fixed|absolute)/i.test(node.style.position) && + dom.getContentEditable(node) !== "true"; + }; + + // Renders empty block on IE + var renderBlockOnIE = function(dom, selection, block) { + var oldRng; + + if (dom.isBlock(block)) { + oldRng = selection.getRng(); + block.appendChild(dom.create('span', null, '\u00a0')); + selection.select(block); + block.lastChild.outerHTML = ''; + selection.setRng(oldRng); + } + }; + + // Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>x</p> + var trimInlineElementsOnLeftSideOfBlock = function(dom, nonEmptyElementsMap, block) { + var node = block, + firstChilds = [], + i; + + if (!node) { + return; + } + + // Find inner most first child ex: <p><i><b>*</b></i></p> + while ((node = node.firstChild)) { + if (dom.isBlock(node)) { + return; + } + + if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) { + firstChilds.push(node); + } + } + + i = firstChilds.length; + while (i--) { + node = firstChilds[i]; + if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) { + dom.remove(node); + } else { + if (isEmptyAnchor(node)) { + dom.remove(node); + } + } + } + }; + + var normalizeZwspOffset = function(start, container, offset) { + if (NodeType.isText(container) === false) { + return offset; + } + if (start) { + return offset === 1 && container.data.charAt(offset - 1) === Zwsp.ZWSP ? 0 : offset; + } else { + return offset === container.data.length - 1 && container.data.charAt(offset) === Zwsp.ZWSP ? container.data.length : offset; + } + }; + + var includeZwspInRange = function(rng) { + var newRng = rng.cloneRange(); + newRng.setStart(rng.startContainer, normalizeZwspOffset(true, rng.startContainer, rng.startOffset)); + newRng.setEnd(rng.endContainer, normalizeZwspOffset(false, rng.endContainer, rng.endOffset)); + return newRng; + }; + + var firstNonWhiteSpaceNodeSibling = function(node) { + while (node) { + if (node.nodeType === 1 || (node.nodeType === 3 && node.data && /[\r\n\s]/.test(node.data))) { + return node; + } + + node = node.nextSibling; + } + }; + + var setup = function(editor) { + var dom = editor.dom, + selection = editor.selection, + settings = editor.settings; + var undoManager = editor.undoManager, + schema = editor.schema, + nonEmptyElementsMap = schema.getNonEmptyElements(), + moveCaretBeforeOnEnterElementsMap = schema.getMoveCaretBeforeOnEnterElements(); + + function handleEnterKey(evt) { + var rng, tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey, + newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer; + + // Moves the caret to a suitable position within the root for example in the first non + // pure whitespace text node or before an image + function moveToCaretPosition(root) { + var walker, node, rng, lastNode = root, + tempElm; + + if (!root) { + return; + } + + // Old IE versions doesn't properly render blocks with br elements in them + // For example <p><br></p> wont be rendered correctly in a contentEditable area + // until you remove the br producing <p></p> + if (Env.ie && Env.ie < 9 && parentBlock && parentBlock.firstChild) { + if (parentBlock.firstChild == parentBlock.lastChild && parentBlock.firstChild.tagName == 'BR') { + dom.remove(parentBlock.firstChild); + } + } + + if (/^(LI|DT|DD)$/.test(root.nodeName)) { + var firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild); + + if (firstChild && /^(UL|OL|DL)$/.test(firstChild.nodeName)) { + root.insertBefore(dom.doc.createTextNode('\u00a0'), root.firstChild); + } + } + + rng = dom.createRng(); + + // Normalize whitespace to remove empty text nodes. Fix for: #6904 + // Gecko will be able to place the caret in empty text nodes but it won't render propery + // Older IE versions will sometimes crash so for now ignore all IE versions + if (!Env.ie) { + root.normalize(); + } + + if (root.hasChildNodes()) { + walker = new TreeWalker(root, root); + + while ((node = walker.current())) { + if (node.nodeType == 3) { + rng.setStart(node, 0); + rng.setEnd(node, 0); + break; + } + + if (moveCaretBeforeOnEnterElementsMap[node.nodeName.toLowerCase()]) { + rng.setStartBefore(node); + rng.setEndBefore(node); + break; + } + + lastNode = node; + node = walker.next(); + } + + if (!node) { + rng.setStart(lastNode, 0); + rng.setEnd(lastNode, 0); + } + } else { + if (root.nodeName == 'BR') { + if (root.nextSibling && dom.isBlock(root.nextSibling)) { + // Trick on older IE versions to render the caret before the BR between two lists + if (!documentMode || documentMode < 9) { + tempElm = dom.create('br'); + root.parentNode.insertBefore(tempElm, root); + } + + rng.setStartBefore(root); + rng.setEndBefore(root); + } else { + rng.setStartAfter(root); + rng.setEndAfter(root); + } + } else { + rng.setStart(root, 0); + rng.setEnd(root, 0); + } + } + + selection.setRng(rng); + + // Remove tempElm created for old IE:s + dom.remove(tempElm); + selection.scrollIntoView(root); + } + + function setForcedBlockAttrs(node) { + var forcedRootBlockName = settings.forced_root_block; + + if (forcedRootBlockName && forcedRootBlockName.toLowerCase() === node.tagName.toLowerCase()) { + dom.setAttribs(node, settings.forced_root_block_attrs); + } + } + + // Creates a new block element by cloning the current one or creating a new one if the name is specified + // This function will also copy any text formatting from the parent block and add it to the new one + function createNewBlock(name) { + var node = container, + block, clonedNode, caretNode, textInlineElements = schema.getTextInlineElements(); + + if (name || parentBlockName == "TABLE" || parentBlockName == "HR") { + block = dom.create(name || newBlockName); + setForcedBlockAttrs(block); + } else { + block = parentBlock.cloneNode(false); + } + + caretNode = block; + + if (settings.keep_styles === false) { + dom.setAttrib(block, 'style', null); // wipe out any styles that came over with the block + dom.setAttrib(block, 'class', null); + } else { + // Clone any parent styles + do { + if (textInlineElements[node.nodeName]) { + // Never clone a caret containers + if (node.id == '_mce_caret') { + continue; + } + + clonedNode = node.cloneNode(false); + dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique + + if (block.hasChildNodes()) { + clonedNode.appendChild(block.firstChild); + block.appendChild(clonedNode); + } else { + caretNode = clonedNode; + block.appendChild(clonedNode); + } + } + } while ((node = node.parentNode) && node != editableRoot); + } + + // BR is needed in empty blocks on non IE browsers + if (!isIE) { + caretNode.innerHTML = '<br data-mce-bogus="1">'; + } + + return block; + } + + // Returns true/false if the caret is at the start/end of the parent block element + function isCaretAtStartOrEndOfBlock(start) { + var walker, node, name, normalizedOffset; + + normalizedOffset = normalizeZwspOffset(start, container, offset); + + // Caret is in the middle of a text node like "a|b" + if (container.nodeType == 3 && (start ? normalizedOffset > 0 : normalizedOffset < container.nodeValue.length)) { + return false; + } + + // If after the last element in block node edge case for #5091 + if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) { + return true; + } + + // If the caret if before the first element in parentBlock + if (start && container.nodeType == 1 && container == parentBlock.firstChild) { + return true; + } + + // Caret can be before/after a table or a hr + if (containerAndSiblingName(container, 'TABLE') || containerAndSiblingName(container, 'HR')) { + return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start); + } + + // Walk the DOM and look for text nodes or non empty elements + walker = new TreeWalker(container, parentBlock); + + // If caret is in beginning or end of a text block then jump to the next/previous node + if (container.nodeType == 3) { + if (start && normalizedOffset === 0) { + walker.prev(); + } else if (!start && normalizedOffset == container.nodeValue.length) { + walker.next(); + } + } + + while ((node = walker.current())) { + if (node.nodeType === 1) { + // Ignore bogus elements + if (!node.getAttribute('data-mce-bogus')) { + // Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p> + name = node.nodeName.toLowerCase(); + if (nonEmptyElementsMap[name] && name !== 'br') { + return false; + } + } + } else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) { + return false; + } + + if (start) { + walker.prev(); + } else { + walker.next(); + } + } + + return true; + } + + // Wraps any text nodes or inline elements in the specified forced root block name + function wrapSelfAndSiblingsInDefaultBlock(container, offset) { + var newBlock, parentBlock, startNode, node, next, rootBlockName, blockName = newBlockName || 'P'; + + // Not in a block element or in a table cell or caption + parentBlock = dom.getParent(container, dom.isBlock); + if (!parentBlock || !canSplitBlock(dom, parentBlock)) { + parentBlock = parentBlock || editableRoot; + + if (parentBlock == editor.getBody() || isTableCell(parentBlock)) { + rootBlockName = parentBlock.nodeName.toLowerCase(); + } else { + rootBlockName = parentBlock.parentNode.nodeName.toLowerCase(); + } + + if (!parentBlock.hasChildNodes()) { + newBlock = dom.create(blockName); + setForcedBlockAttrs(newBlock); + parentBlock.appendChild(newBlock); + rng.setStart(newBlock, 0); + rng.setEnd(newBlock, 0); + return newBlock; + } + + // Find parent that is the first child of parentBlock + node = container; + while (node.parentNode != parentBlock) { + node = node.parentNode; + } + + // Loop left to find start node start wrapping at + while (node && !dom.isBlock(node)) { + startNode = node; + node = node.previousSibling; + } + + if (startNode && schema.isValidChild(rootBlockName, blockName.toLowerCase())) { + newBlock = dom.create(blockName); + setForcedBlockAttrs(newBlock); + startNode.parentNode.insertBefore(newBlock, startNode); + + // Start wrapping until we hit a block + node = startNode; + while (node && !dom.isBlock(node)) { + next = node.nextSibling; + newBlock.appendChild(node); + node = next; + } + + // Restore range to it's past location + rng.setStart(container, offset); + rng.setEnd(container, offset); + } + } + + return container; + } + + // Inserts a block or br before/after or in the middle of a split list of the LI is empty + function handleEmptyListItem() { + function isFirstOrLastLi(first) { + var node = containerBlock[first ? 'firstChild' : 'lastChild']; + + // Find first/last element since there might be whitespace there + while (node) { + if (node.nodeType == 1) { + break; + } + + node = node[first ? 'nextSibling' : 'previousSibling']; + } + + return node === parentBlock; + } + + function getContainerBlock() { + var containerBlockParent = containerBlock.parentNode; + + if (/^(LI|DT|DD)$/.test(containerBlockParent.nodeName)) { + return containerBlockParent; + } + + return containerBlock; + } + + if (containerBlock == editor.getBody()) { + return; + } + + // Check if we are in an nested list + var containerBlockParentName = containerBlock.parentNode.nodeName; + if (/^(OL|UL|LI)$/.test(containerBlockParentName)) { + newBlockName = 'LI'; + } + + newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR'); + + if (isFirstOrLastLi(true) && isFirstOrLastLi()) { + if (containerBlockParentName == 'LI') { + // Nested list is inside a LI + dom.insertAfter(newBlock, getContainerBlock()); + } else { + // Is first and last list item then replace the OL/UL with a text block + dom.replace(newBlock, containerBlock); + } + } else if (isFirstOrLastLi(true)) { + if (containerBlockParentName == 'LI') { + // List nested in an LI then move the list to a new sibling LI + dom.insertAfter(newBlock, getContainerBlock()); + newBlock.appendChild(dom.doc.createTextNode(' ')); // Needed for IE so the caret can be placed + newBlock.appendChild(containerBlock); + } else { + // First LI in list then remove LI and add text block before list + containerBlock.parentNode.insertBefore(newBlock, containerBlock); + } + } else if (isFirstOrLastLi()) { + // Last LI in list then remove LI and add text block after list + dom.insertAfter(newBlock, getContainerBlock()); + renderBlockOnIE(dom, selection, newBlock); + } else { + // Middle LI in list the split the list and insert a text block in the middle + // Extract after fragment and insert it after the current block + containerBlock = getContainerBlock(); + tmpRng = rng.cloneRange(); + tmpRng.setStartAfter(parentBlock); + tmpRng.setEndAfter(containerBlock); + fragment = tmpRng.extractContents(); + + if (newBlockName == 'LI' && fragment.firstChild.nodeName == 'LI') { + newBlock = fragment.firstChild; + dom.insertAfter(fragment, containerBlock); + } else { + dom.insertAfter(fragment, containerBlock); + dom.insertAfter(newBlock, containerBlock); + } + } + + dom.remove(parentBlock); + moveToCaretPosition(newBlock); + undoManager.add(); + } + + // Inserts a BR element if the forced_root_block option is set to false or empty string + function insertBr() { + editor.execCommand("InsertLineBreak", false, evt); + } + + // Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element + function trimLeadingLineBreaks(node) { + do { + if (node.nodeType === 3) { + node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, ''); + } + + node = node.firstChild; + } while (node); + } + + function getEditableRoot(node) { + var root = dom.getRoot(), + parent, editableRoot; + + // Get all parents until we hit a non editable parent or the root + parent = node; + while (parent !== root && dom.getContentEditable(parent) !== "false") { + if (dom.getContentEditable(parent) === "true") { + editableRoot = parent; + } + + parent = parent.parentNode; + } + + return parent !== root ? editableRoot : root; + } + + // Adds a BR at the end of blocks that only contains an IMG or INPUT since + // these might be floated and then they won't expand the block + function addBrToBlockIfNeeded(block) { + var lastChild; + + // IE will render the blocks correctly other browsers needs a BR + if (!isIE) { + block.normalize(); // Remove empty text nodes that got left behind by the extract + + // Check if the block is empty or contains a floated last child + lastChild = block.lastChild; + if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) { + dom.add(block, 'br'); + } + } + } + + function insertNewBlockAfter() { + // If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup + if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') { + newBlock = createNewBlock(newBlockName); + } else { + newBlock = createNewBlock(); + } + + // Split the current container block element if enter is pressed inside an empty inner block element + if (settings.end_container_on_empty_block && canSplitBlock(dom, containerBlock) && dom.isEmpty(parentBlock)) { + // Split container block for example a BLOCKQUOTE at the current blockParent location for example a P + newBlock = dom.split(containerBlock, parentBlock); + } else { + dom.insertAfter(newBlock, parentBlock); + } + + moveToCaretPosition(newBlock); + } + + rng = selection.getRng(true); + + // Event is blocked by some other handler for example the lists plugin + if (evt.isDefaultPrevented()) { + return; + } + + // Delete any selected contents + if (!rng.collapsed) { + editor.execCommand('Delete'); + return; + } + + // Setup range items and newBlockName + new RangeUtils(dom).normalize(rng); + container = rng.startContainer; + offset = rng.startOffset; + newBlockName = (settings.force_p_newlines ? 'p' : '') || settings.forced_root_block; + newBlockName = newBlockName ? newBlockName.toUpperCase() : ''; + documentMode = dom.doc.documentMode; + shiftKey = evt.shiftKey; + + // Resolve node index + if (container.nodeType == 1 && container.hasChildNodes()) { + isAfterLastNodeInContainer = offset > container.childNodes.length - 1; + + container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container; + if (isAfterLastNodeInContainer && container.nodeType == 3) { + offset = container.nodeValue.length; + } else { + offset = 0; + } + } + + // Get editable root node, normally the body element but sometimes a div or span + editableRoot = getEditableRoot(container); + + // If there is no editable root then enter is done inside a contentEditable false element + if (!editableRoot) { + return; + } + + undoManager.beforeChange(); + + // If editable root isn't block nor the root of the editor + if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) { + if (!newBlockName || shiftKey) { + insertBr(); + } + + return; + } + + // Wrap the current node and it's sibling in a default block if it's needed. + // for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td> + // This won't happen if root blocks are disabled or the shiftKey is pressed + if ((newBlockName && !shiftKey) || (!newBlockName && shiftKey)) { + container = wrapSelfAndSiblingsInDefaultBlock(container, offset); + } + + // Find parent block and setup empty block paddings + parentBlock = dom.getParent(container, dom.isBlock); + containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null; + + // Setup block names + parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5 + containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5 + + // Enter inside block contained within a LI then split or insert before/after LI + if (containerBlockName == 'LI' && !evt.ctrlKey) { + parentBlock = containerBlock; + parentBlockName = containerBlockName; + } + + if (editor.undoManager.typing) { + editor.undoManager.typing = false; + editor.undoManager.add(); + } + + // Handle enter in list item + if (/^(LI|DT|DD)$/.test(parentBlockName)) { + if (!newBlockName && shiftKey) { + insertBr(); + return; + } + + // Handle enter inside an empty list item + if (dom.isEmpty(parentBlock)) { + handleEmptyListItem(); + return; + } + } + + // Don't split PRE tags but insert a BR instead easier when writing code samples etc + if (parentBlockName == 'PRE' && settings.br_in_pre !== false) { + if (!shiftKey) { + insertBr(); + return; + } + } else { + // If no root block is configured then insert a BR by default or if the shiftKey is pressed + if ((!newBlockName && !shiftKey && parentBlockName != 'LI') || (newBlockName && shiftKey)) { + insertBr(); + return; + } + } + + // If parent block is root then never insert new blocks + if (newBlockName && parentBlock === editor.getBody()) { + return; + } + + // Default block name if it's not configured + newBlockName = newBlockName || 'P'; + + // Insert new block before/after the parent block depending on caret location + if (CaretContainer.isCaretContainerBlock(parentBlock)) { + newBlock = CaretContainer.showCaretContainerBlock(parentBlock); + if (dom.isEmpty(parentBlock)) { + emptyBlock(parentBlock); + } + moveToCaretPosition(newBlock); + } else if (isCaretAtStartOrEndOfBlock()) { + insertNewBlockAfter(); + } else if (isCaretAtStartOrEndOfBlock(true)) { + // Insert new block before + newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock); + renderBlockOnIE(dom, selection, newBlock); + + // Adjust caret position if HR + containerAndSiblingName(parentBlock, 'HR') ? moveToCaretPosition(newBlock) : moveToCaretPosition(parentBlock); + } else { + // Extract after fragment and insert it after the current block + tmpRng = includeZwspInRange(rng).cloneRange(); + tmpRng.setEndAfter(parentBlock); + fragment = tmpRng.extractContents(); + trimLeadingLineBreaks(fragment); + newBlock = fragment.firstChild; + dom.insertAfter(fragment, parentBlock); + trimInlineElementsOnLeftSideOfBlock(dom, nonEmptyElementsMap, newBlock); + addBrToBlockIfNeeded(parentBlock); + + if (dom.isEmpty(parentBlock)) { + emptyBlock(parentBlock); + } + + newBlock.normalize(); + + // New block might become empty if it's <p><b>a |</b></p> + if (dom.isEmpty(newBlock)) { + dom.remove(newBlock); + insertNewBlockAfter(); + } else { + moveToCaretPosition(newBlock); + } + } + + dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique + + // Allow custom handling of new blocks + editor.fire('NewBlock', { + newBlock: newBlock + }); + + undoManager.typing = false; + undoManager.add(); + } + + editor.on('keydown', function(evt) { + if (evt.keyCode == 13) { + if (handleEnterKey(evt) !== false) { + evt.preventDefault(); + } + } + }); + }; + + return { + setup: setup + }; + } + ); + + /** + * InsertSpace.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.keyboard.InsertSpace', [ + 'ephox.katamari.api.Fun', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.dom.NodeType', + 'tinymce.core.keyboard.BoundaryLocation' + ], + function(Fun, CaretPosition, NodeType, BoundaryLocation) { + var isValidInsertPoint = function(location, caretPosition) { + return isAtStartOrEnd(location) && NodeType.isText(caretPosition.container()); + }; + + var insertNbspAtPosition = function(editor, caretPosition) { + var container = caretPosition.container(); + var offset = caretPosition.offset(); + + container.insertData(offset, '\u00a0'); + editor.selection.setCursorLocation(container, offset + 1); + }; + + var insertAtLocation = function(editor, caretPosition, location) { + if (isValidInsertPoint(location, caretPosition)) { + insertNbspAtPosition(editor, caretPosition); + return true; + } else { + return false; + } + }; + + var insertAtCaret = function(editor) { + var caretPosition = CaretPosition.fromRangeStart(editor.selection.getRng()); + var boundaryLocation = BoundaryLocation.readLocation(editor.getBody(), caretPosition); + return boundaryLocation.map(Fun.curry(insertAtLocation, editor, caretPosition)).getOr(false); + }; + + var isAtStartOrEnd = function(location) { + return location.fold( + Fun.constant(false), // Before + Fun.constant(true), // Start + Fun.constant(true), // End + Fun.constant(false) // After + ); + }; + + var insertAtSelection = function(editor) { + return editor.selection.isCollapsed() ? insertAtCaret(editor) : false; + }; + + return { + insertAtSelection: insertAtSelection + }; + } + ); + + /** + * SpaceKey.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.keyboard.SpaceKey', [ + 'ephox.katamari.api.Arr', + 'tinymce.core.keyboard.InsertSpace', + 'tinymce.core.keyboard.MatchKeys', + 'tinymce.core.util.VK' + ], + function(Arr, InsertSpace, MatchKeys, VK) { + var executeKeydownOverride = function(editor, evt) { + var matches = MatchKeys.match([{ + keyCode: VK.SPACEBAR, + action: MatchKeys.action(InsertSpace.insertAtSelection, editor) + }], evt); + + Arr.find(matches, function(pattern) { + return pattern.action(); + }).each(function(_) { + evt.preventDefault(); + }); + }; + + var setup = function(editor) { + editor.on('keydown', function(evt) { + if (evt.isDefaultPrevented() === false) { + executeKeydownOverride(editor, evt); + } + }); + }; + + return { + setup: setup + }; + } + ); + + /** + * KeyboardOverrides.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.keyboard.KeyboardOverrides', [ + 'tinymce.core.keyboard.ArrowKeys', + 'tinymce.core.keyboard.BoundarySelection', + 'tinymce.core.keyboard.DeleteBackspaceKeys', + 'tinymce.core.keyboard.EnterKey', + 'tinymce.core.keyboard.SpaceKey' + ], + function(ArrowKeys, BoundarySelection, DeleteBackspaceKeys, EnterKey, SpaceKey) { + var setup = function(editor) { + var caret = BoundarySelection.setupSelectedState(editor); + + ArrowKeys.setup(editor, caret); + DeleteBackspaceKeys.setup(editor, caret); + EnterKey.setup(editor); + SpaceKey.setup(editor); + }; + + return { + setup: setup + }; + } + ); + /** + * NodeChange.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class handles the nodechange event dispatching both manual and through selection change events. + * + * @class tinymce.NodeChange + * @private + */ + define( + 'tinymce.core.NodeChange', [ + "tinymce.core.dom.RangeUtils", + "tinymce.core.Env", + "tinymce.core.util.Delay" + ], + function(RangeUtils, Env, Delay) { + return function(editor) { + var lastRng, lastPath = []; + + /** + * Returns true/false if the current element path has been changed or not. + * + * @private + * @return {Boolean} True if the element path is the same false if it's not. + */ + function isSameElementPath(startElm) { + var i, currentPath; + + currentPath = editor.$(startElm).parentsUntil(editor.getBody()).add(startElm); + if (currentPath.length === lastPath.length) { + for (i = currentPath.length; i >= 0; i--) { + if (currentPath[i] !== lastPath[i]) { + break; + } + } + + if (i === -1) { + lastPath = currentPath; + return true; + } + } + + lastPath = currentPath; + + return false; + } + + // Gecko doesn't support the "selectionchange" event + if (!('onselectionchange' in editor.getDoc())) { + editor.on('NodeChange Click MouseUp KeyUp Focus', function(e) { + var nativeRng, fakeRng; + + // Since DOM Ranges mutate on modification + // of the DOM we need to clone it's contents + nativeRng = editor.selection.getRng(); + fakeRng = { + startContainer: nativeRng.startContainer, + startOffset: nativeRng.startOffset, + endContainer: nativeRng.endContainer, + endOffset: nativeRng.endOffset + }; + + // Always treat nodechange as a selectionchange since applying + // formatting to the current range wouldn't update the range but it's parent + if (e.type == 'nodechange' || !RangeUtils.compareRanges(fakeRng, lastRng)) { + editor.fire('SelectionChange'); + } + + lastRng = fakeRng; + }); + } + + // IE has a bug where it fires a selectionchange on right click that has a range at the start of the body + // When the contextmenu event fires the selection is located at the right location + editor.on('contextmenu', function() { + editor.fire('SelectionChange'); + }); + + // Selection change is delayed ~200ms on IE when you click inside the current range + editor.on('SelectionChange', function() { + var startElm = editor.selection.getStart(true); + + // IE 8 will fire a selectionchange event with an incorrect selection + // when focusing out of table cells. Click inside cell -> toolbar = Invalid SelectionChange event + if (!Env.range && editor.selection.isCollapsed()) { + return; + } + + if (!isSameElementPath(startElm) && editor.dom.isChildOf(startElm, editor.getBody())) { + editor.nodeChanged({ + selectionChange: true + }); + } + }); + + // Fire an extra nodeChange on mouseup for compatibility reasons + editor.on('MouseUp', function(e) { + if (!e.isDefaultPrevented()) { + // Delay nodeChanged call for WebKit edge case issue where the range + // isn't updated until after you click outside a selected image + if (editor.selection.getNode().nodeName == 'IMG') { + Delay.setEditorTimeout(editor, function() { + editor.nodeChanged(); + }); + } else { + editor.nodeChanged(); + } + } + }); + + /** + * Dispatches out a onNodeChange event to all observers. This method should be called when you + * need to update the UI states or element path etc. + * + * @method nodeChanged + * @param {Object} args Optional args to pass to NodeChange event handlers. + */ + this.nodeChanged = function(args) { + var selection = editor.selection, + node, parents, root; + + // Fix for bug #1896577 it seems that this can not be fired while the editor is loading + if (editor.initialized && selection && !editor.settings.disable_nodechange && !editor.readonly) { + // Get start node + root = editor.getBody(); + node = selection.getStart(true) || root; + + // Make sure the node is within the editor root or is the editor root + if (node.ownerDocument != editor.getDoc() || !editor.dom.isChildOf(node, root)) { + node = root; + } + + // Get parents and add them to object + parents = []; + editor.dom.getParent(node, function(node) { + if (node === root) { + return true; + } + + parents.push(node); + }); + + args = args || {}; + args.element = node; + args.parents = parents; + + editor.fire('NodeChange', args); + } + }; + }; + } + ); + + /** + * FakeCaret.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module contains logic for rendering a fake visual caret. + * + * @private + * @class tinymce.caret.FakeCaret + */ + define( + 'tinymce.core.caret.FakeCaret', [ + 'tinymce.core.caret.CaretContainer', + 'tinymce.core.caret.CaretContainerRemove', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.dom.DomQuery', + 'tinymce.core.dom.NodeType', + 'tinymce.core.dom.RangeUtils', + 'tinymce.core.geom.ClientRect', + 'tinymce.core.util.Delay' + ], + function(CaretContainer, CaretContainerRemove, CaretPosition, DomQuery, NodeType, RangeUtils, ClientRect, Delay) { + var isContentEditableFalse = NodeType.isContentEditableFalse; + + var isTableCell = function(node) { + return node && /^(TD|TH)$/i.test(node.nodeName); + }; + + return function(rootNode, isBlock) { + var cursorInterval, $lastVisualCaret, caretContainerNode; + + function getAbsoluteClientRect(node, before) { + var clientRect = ClientRect.collapse(node.getBoundingClientRect(), before), + docElm, scrollX, scrollY, margin, rootRect; + + if (rootNode.tagName == 'BODY') { + docElm = rootNode.ownerDocument.documentElement; + scrollX = rootNode.scrollLeft || docElm.scrollLeft; + scrollY = rootNode.scrollTop || docElm.scrollTop; + } else { + rootRect = rootNode.getBoundingClientRect(); + scrollX = rootNode.scrollLeft - rootRect.left; + scrollY = rootNode.scrollTop - rootRect.top; + } + + clientRect.left += scrollX; + clientRect.right += scrollX; + clientRect.top += scrollY; + clientRect.bottom += scrollY; + clientRect.width = 1; + + margin = node.offsetWidth - node.clientWidth; + + if (margin > 0) { + if (before) { + margin *= -1; + } + + clientRect.left += margin; + clientRect.right += margin; + } + + return clientRect; + } + + function trimInlineCaretContainers() { + var contentEditableFalseNodes, node, sibling, i, data; + + contentEditableFalseNodes = DomQuery('*[contentEditable=false]', rootNode); + for (i = 0; i < contentEditableFalseNodes.length; i++) { + node = contentEditableFalseNodes[i]; + + sibling = node.previousSibling; + if (CaretContainer.endsWithCaretContainer(sibling)) { + data = sibling.data; + + if (data.length == 1) { + sibling.parentNode.removeChild(sibling); + } else { + sibling.deleteData(data.length - 1, 1); + } + } + + sibling = node.nextSibling; + if (CaretContainer.startsWithCaretContainer(sibling)) { + data = sibling.data; + + if (data.length == 1) { + sibling.parentNode.removeChild(sibling); + } else { + sibling.deleteData(0, 1); + } + } + } + + return null; + } + + function show(before, node) { + var clientRect, rng; + + hide(); + + if (isTableCell(node)) { + return null; + } + + if (isBlock(node)) { + caretContainerNode = CaretContainer.insertBlock('p', node, before); + clientRect = getAbsoluteClientRect(node, before); + DomQuery(caretContainerNode).css('top', clientRect.top); + + $lastVisualCaret = DomQuery('<div class="mce-visual-caret" data-mce-bogus="all"></div>').css(clientRect).appendTo(rootNode); + + if (before) { + $lastVisualCaret.addClass('mce-visual-caret-before'); + } + + startBlink(); + + rng = node.ownerDocument.createRange(); + rng.setStart(caretContainerNode, 0); + rng.setEnd(caretContainerNode, 0); + } else { + caretContainerNode = CaretContainer.insertInline(node, before); + rng = node.ownerDocument.createRange(); + + if (isContentEditableFalse(caretContainerNode.nextSibling)) { + rng.setStart(caretContainerNode, 0); + rng.setEnd(caretContainerNode, 0); + } else { + rng.setStart(caretContainerNode, 1); + rng.setEnd(caretContainerNode, 1); + } + + return rng; + } + + return rng; + } + + function hide() { + trimInlineCaretContainers(); + + if (caretContainerNode) { + CaretContainerRemove.remove(caretContainerNode); + caretContainerNode = null; + } + + if ($lastVisualCaret) { + $lastVisualCaret.remove(); + $lastVisualCaret = null; + } + + clearInterval(cursorInterval); + } + + function startBlink() { + cursorInterval = Delay.setInterval(function() { + DomQuery('div.mce-visual-caret', rootNode).toggleClass('mce-visual-caret-hidden'); + }, 500); + } + + function destroy() { + Delay.clearInterval(cursorInterval); + } + + function getCss() { + return ( + '.mce-visual-caret {' + + 'position: absolute;' + + 'background-color: black;' + + 'background-color: currentcolor;' + + '}' + + '.mce-visual-caret-hidden {' + + 'display: none;' + + '}' + + '*[data-mce-caret] {' + + 'position: absolute;' + + 'left: -1000px;' + + 'right: auto;' + + 'top: 0;' + + 'margin: 0;' + + 'padding: 0;' + + '}' + ); + } + + return { + show: show, + hide: hide, + getCss: getCss, + destroy: destroy + }; + }; + } + ); + /** + * MousePosition.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module calculates an absolute coordinate inside the editor body for both local and global mouse events. + * + * @private + * @class tinymce.dom.MousePosition + */ + define( + 'tinymce.core.dom.MousePosition', [], + function() { + var getAbsolutePosition = function(elm) { + var doc, docElem, win, clientRect; + + clientRect = elm.getBoundingClientRect(); + doc = elm.ownerDocument; + docElem = doc.documentElement; + win = doc.defaultView; + + return { + top: clientRect.top + win.pageYOffset - docElem.clientTop, + left: clientRect.left + win.pageXOffset - docElem.clientLeft + }; + }; + + var getBodyPosition = function(editor) { + return editor.inline ? getAbsolutePosition(editor.getBody()) : { + left: 0, + top: 0 + }; + }; + + var getScrollPosition = function(editor) { + var body = editor.getBody(); + return editor.inline ? { + left: body.scrollLeft, + top: body.scrollTop + } : { + left: 0, + top: 0 + }; + }; + + var getBodyScroll = function(editor) { + var body = editor.getBody(), + docElm = editor.getDoc().documentElement; + var inlineScroll = { + left: body.scrollLeft, + top: body.scrollTop + }; + var iframeScroll = { + left: body.scrollLeft || docElm.scrollLeft, + top: body.scrollTop || docElm.scrollTop + }; + + return editor.inline ? inlineScroll : iframeScroll; + }; + + var getMousePosition = function(editor, event) { + if (event.target.ownerDocument !== editor.getDoc()) { + var iframePosition = getAbsolutePosition(editor.getContentAreaContainer()); + var scrollPosition = getBodyScroll(editor); + + return { + left: event.pageX - iframePosition.left + scrollPosition.left, + top: event.pageY - iframePosition.top + scrollPosition.top + }; + } + + return { + left: event.pageX, + top: event.pageY + }; + }; + + var calculatePosition = function(bodyPosition, scrollPosition, mousePosition) { + return { + pageX: (mousePosition.left - bodyPosition.left) + scrollPosition.left, + pageY: (mousePosition.top - bodyPosition.top) + scrollPosition.top + }; + }; + + var calc = function(editor, event) { + return calculatePosition(getBodyPosition(editor), getScrollPosition(editor), getMousePosition(editor, event)); + }; + + return { + calc: calc + }; + } + ); + + /** + * DragDropOverrides.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module contains logic overriding the drag/drop logic of the editor. + * + * @private + * @class tinymce.DragDropOverrides + */ + define( + 'tinymce.core.DragDropOverrides', [ + "tinymce.core.dom.NodeType", + "tinymce.core.util.Arr", + "tinymce.core.util.Fun", + "tinymce.core.util.Delay", + "tinymce.core.dom.DOMUtils", + "tinymce.core.dom.MousePosition" + ], + function( + NodeType, Arr, Fun, Delay, DOMUtils, MousePosition + ) { + var isContentEditableFalse = NodeType.isContentEditableFalse, + isContentEditableTrue = NodeType.isContentEditableTrue; + + var isDraggable = function(rootElm, elm) { + return isContentEditableFalse(elm) && elm !== rootElm; + }; + + var isValidDropTarget = function(editor, targetElement, dragElement) { + if (targetElement === dragElement || editor.dom.isChildOf(targetElement, dragElement)) { + return false; + } + + if (isContentEditableFalse(targetElement)) { + return false; + } + + return true; + }; + + var cloneElement = function(elm) { + var cloneElm = elm.cloneNode(true); + cloneElm.removeAttribute('data-mce-selected'); + return cloneElm; + }; + + var createGhost = function(editor, elm, width, height) { + var clonedElm = elm.cloneNode(true); + + editor.dom.setStyles(clonedElm, { + width: width, + height: height + }); + editor.dom.setAttrib(clonedElm, 'data-mce-selected', null); + + var ghostElm = editor.dom.create('div', { + 'class': 'mce-drag-container', + 'data-mce-bogus': 'all', + unselectable: 'on', + contenteditable: 'false' + }); + + editor.dom.setStyles(ghostElm, { + position: 'absolute', + opacity: 0.5, + overflow: 'hidden', + border: 0, + padding: 0, + margin: 0, + width: width, + height: height + }); + + editor.dom.setStyles(clonedElm, { + margin: 0, + boxSizing: 'border-box' + }); + + ghostElm.appendChild(clonedElm); + + return ghostElm; + }; + + var appendGhostToBody = function(ghostElm, bodyElm) { + if (ghostElm.parentNode !== bodyElm) { + bodyElm.appendChild(ghostElm); + } + }; + + var moveGhost = function(ghostElm, position, width, height, maxX, maxY) { + var overflowX = 0, + overflowY = 0; + + ghostElm.style.left = position.pageX + 'px'; + ghostElm.style.top = position.pageY + 'px'; + + if (position.pageX + width > maxX) { + overflowX = (position.pageX + width) - maxX; + } + + if (position.pageY + height > maxY) { + overflowY = (position.pageY + height) - maxY; + } + + ghostElm.style.width = (width - overflowX) + 'px'; + ghostElm.style.height = (height - overflowY) + 'px'; + }; + + var removeElement = function(elm) { + if (elm && elm.parentNode) { + elm.parentNode.removeChild(elm); + } + }; + + var isLeftMouseButtonPressed = function(e) { + return e.button === 0; + }; + + var hasDraggableElement = function(state) { + return state.element; + }; + + var applyRelPos = function(state, position) { + return { + pageX: position.pageX - state.relX, + pageY: position.pageY + 5 + }; + }; + + var start = function(state, editor) { + return function(e) { + if (isLeftMouseButtonPressed(e)) { + var ceElm = Arr.find(editor.dom.getParents(e.target), Fun.or(isContentEditableFalse, isContentEditableTrue)); + + if (isDraggable(editor.getBody(), ceElm)) { + var elmPos = editor.dom.getPos(ceElm); + var bodyElm = editor.getBody(); + var docElm = editor.getDoc().documentElement; + + state.element = ceElm; + state.screenX = e.screenX; + state.screenY = e.screenY; + state.maxX = (editor.inline ? bodyElm.scrollWidth : docElm.offsetWidth) - 2; + state.maxY = (editor.inline ? bodyElm.scrollHeight : docElm.offsetHeight) - 2; + state.relX = e.pageX - elmPos.x; + state.relY = e.pageY - elmPos.y; + state.width = ceElm.offsetWidth; + state.height = ceElm.offsetHeight; + state.ghost = createGhost(editor, ceElm, state.width, state.height); + } + } + }; + }; + + var move = function(state, editor) { + // Reduces laggy drag behavior on Gecko + var throttledPlaceCaretAt = Delay.throttle(function(clientX, clientY) { + editor._selectionOverrides.hideFakeCaret(); + editor.selection.placeCaretAt(clientX, clientY); + }, 0); + + return function(e) { + var movement = Math.max(Math.abs(e.screenX - state.screenX), Math.abs(e.screenY - state.screenY)); + + if (hasDraggableElement(state) && !state.dragging && movement > 10) { + var args = editor.fire('dragstart', { + target: state.element + }); + if (args.isDefaultPrevented()) { + return; + } + + state.dragging = true; + editor.focus(); + } + + if (state.dragging) { + var targetPos = applyRelPos(state, MousePosition.calc(editor, e)); + + appendGhostToBody(state.ghost, editor.getBody()); + moveGhost(state.ghost, targetPos, state.width, state.height, state.maxX, state.maxY); + + throttledPlaceCaretAt(e.clientX, e.clientY); + } + }; + }; + + // Returns the raw element instead of the fake cE=false element + var getRawTarget = function(selection) { + var rng = selection.getSel().getRangeAt(0); + var startContainer = rng.startContainer; + return startContainer.nodeType === 3 ? startContainer.parentNode : startContainer; + }; + + var drop = function(state, editor) { + return function(e) { + if (state.dragging) { + if (isValidDropTarget(editor, getRawTarget(editor.selection), state.element)) { + var targetClone = cloneElement(state.element); + + var args = editor.fire('drop', { + targetClone: targetClone, + clientX: e.clientX, + clientY: e.clientY + }); + + if (!args.isDefaultPrevented()) { + targetClone = args.targetClone; + + editor.undoManager.transact(function() { + removeElement(state.element); + editor.insertContent(editor.dom.getOuterHTML(targetClone)); + editor._selectionOverrides.hideFakeCaret(); + }); + } + } + } + + removeDragState(state); + }; + }; + + var stop = function(state, editor) { + return function() { + removeDragState(state); + if (state.dragging) { + editor.fire('dragend'); + } + }; + }; + + var removeDragState = function(state) { + state.dragging = false; + state.element = null; + removeElement(state.ghost); + }; + + var bindFakeDragEvents = function(editor) { + var state = {}, + pageDom, dragStartHandler, dragHandler, dropHandler, dragEndHandler, rootDocument; + + pageDom = DOMUtils.DOM; + rootDocument = document; + dragStartHandler = start(state, editor); + dragHandler = move(state, editor); + dropHandler = drop(state, editor); + dragEndHandler = stop(state, editor); + + editor.on('mousedown', dragStartHandler); + editor.on('mousemove', dragHandler); + editor.on('mouseup', dropHandler); + + pageDom.bind(rootDocument, 'mousemove', dragHandler); + pageDom.bind(rootDocument, 'mouseup', dragEndHandler); + + editor.on('remove', function() { + pageDom.unbind(rootDocument, 'mousemove', dragHandler); + pageDom.unbind(rootDocument, 'mouseup', dragEndHandler); + }); + }; + + var blockIeDrop = function(editor) { + editor.on('drop', function(e) { + // FF doesn't pass out clientX/clientY for drop since this is for IE we just use null instead + var realTarget = typeof e.clientX !== 'undefined' ? editor.getDoc().elementFromPoint(e.clientX, e.clientY) : null; + + if (isContentEditableFalse(realTarget) || isContentEditableFalse(editor.dom.getContentEditableParent(realTarget))) { + e.preventDefault(); + } + }); + }; + + var init = function(editor) { + bindFakeDragEvents(editor); + blockIeDrop(editor); + }; + + return { + init: init + }; + } + ); + + define( + 'ephox.sugar.impl.Style', + + [ + + ], + + function() { + // some elements, such as mathml, don't have style attributes + var isSupported = function(dom) { + return dom.style !== undefined; + }; + + return { + isSupported: isSupported + }; + } + ); + define( + 'ephox.sugar.api.properties.Css', + + [ + 'ephox.katamari.api.Type', + 'ephox.katamari.api.Arr', + 'ephox.katamari.api.Obj', + 'ephox.katamari.api.Option', + 'ephox.sugar.api.properties.Attr', + 'ephox.sugar.api.node.Body', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.api.node.Node', + 'ephox.sugar.impl.Style', + 'ephox.katamari.api.Strings', + 'global!Error', + 'global!console', + 'global!window' + ], + + function(Type, Arr, Obj, Option, Attr, Body, Element, Node, Style, Strings, Error, console, window) { + var internalSet = function(dom, property, value) { + // This is going to hurt. Apologies. + // JQuery coerces numbers to pixels for certain property names, and other times lets numbers through. + // we're going to be explicit; strings only. + if (!Type.isString(value)) { + console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom); + throw new Error('CSS value must be a string: ' + value); + } + + // removed: support for dom().style[property] where prop is camel case instead of normal property name + if (Style.isSupported(dom)) dom.style.setProperty(property, value); + }; + + var internalRemove = function(dom, property) { + /* + * IE9 and above - MDN doesn't have details, but here's a couple of random internet claims + * + * http://help.dottoro.com/ljopsjck.php + * http://stackoverflow.com/a/7901886/7546 + */ + if (Style.isSupported(dom)) dom.style.removeProperty(property); + }; + + var set = function(element, property, value) { + var dom = element.dom(); + internalSet(dom, property, value); + }; + + var setAll = function(element, css) { + var dom = element.dom(); + + Obj.each(css, function(v, k) { + internalSet(dom, k, v); + }); + }; + + var setOptions = function(element, css) { + var dom = element.dom(); + + Obj.each(css, function(v, k) { + v.fold(function() { + internalRemove(dom, k); + }, function(value) { + internalSet(dom, k, value); + }); + }); + }; + + /* + * NOTE: For certain properties, this returns the "used value" which is subtly different to the "computed value" (despite calling getComputedStyle). + * Blame CSS 2.0. + * + * https://developer.mozilla.org/en-US/docs/Web/CSS/used_value + */ + var get = function(element, property) { + var dom = element.dom(); + /* + * IE9 and above per + * https://developer.mozilla.org/en/docs/Web/API/window.getComputedStyle + * + * Not in numerosity, because it doesn't memoize and looking this up dynamically in performance critical code would be horrendous. + * + * JQuery has some magic here for IE popups, but we don't really need that. + * It also uses element.ownerDocument.defaultView to handle iframes but that hasn't been required since FF 3.6. + */ + var styles = window.getComputedStyle(dom); + var r = styles.getPropertyValue(property); + + // jquery-ism: If r is an empty string, check that the element is not in a document. If it isn't, return the raw value. + // Turns out we do this a lot. + var v = (r === '' && !Body.inBody(element)) ? getUnsafeProperty(dom, property) : r; + + // undefined is the more appropriate value for JS. JQuery coerces to an empty string, but screw that! + return v === null ? undefined : v; + }; + + var getUnsafeProperty = function(dom, property) { + // removed: support for dom().style[property] where prop is camel case instead of normal property name + // empty string is what the browsers (IE11 and Chrome) return when the propertyValue doesn't exists. + return Style.isSupported(dom) ? dom.style.getPropertyValue(property) : ''; + }; + + /* + * Gets the raw value from the style attribute. Useful for retrieving "used values" from the DOM: + * https://developer.mozilla.org/en-US/docs/Web/CSS/used_value + * + * Returns NONE if the property isn't set, or the value is an empty string. + */ + var getRaw = function(element, property) { + var dom = element.dom(); + var raw = getUnsafeProperty(dom, property); + + return Option.from(raw).filter(function(r) { + return r.length > 0; + }); + }; + + var getAllRaw = function(element) { + var css = {}; + var dom = element.dom(); + + if (Style.isSupported(dom)) { + for (var i = 0; i < dom.style.length; i++) { + var ruleName = dom.style.item(i); + css[ruleName] = dom.style[ruleName]; + } + } + return css; + }; + + var isValidValue = function(tag, property, value) { + var element = Element.fromTag(tag); + set(element, property, value); + var style = getRaw(element, property); + return style.isSome(); + }; + + var remove = function(element, property) { + var dom = element.dom(); + + internalRemove(dom, property); + + if (Attr.has(element, 'style') && Strings.trim(Attr.get(element, 'style')) === '') { + // No more styles left, remove the style attribute as well + Attr.remove(element, 'style'); + } + }; + + var preserve = function(element, f) { + var oldStyles = Attr.get(element, 'style'); + var result = f(element); + var restore = oldStyles === undefined ? Attr.remove : Attr.set; + restore(element, 'style', oldStyles); + return result; + }; + + var copy = function(source, target) { + var sourceDom = source.dom(); + var targetDom = target.dom(); + if (Style.isSupported(sourceDom) && Style.isSupported(targetDom)) { + targetDom.style.cssText = sourceDom.style.cssText; + } + }; + + var reflow = function(e) { + /* NOTE: + * do not rely on this return value. + * It's here so the closure compiler doesn't optimise the property access away. + */ + return e.dom().offsetWidth; + }; + + var transferOne = function(source, destination, style) { + getRaw(source, style).each(function(value) { + // NOTE: We don't want to clobber any existing inline styles. + if (getRaw(destination, style).isNone()) set(destination, style, value); + }); + }; + + var transfer = function(source, destination, styles) { + if (!Node.isElement(source) || !Node.isElement(destination)) return; + Arr.each(styles, function(style) { + transferOne(source, destination, style); + }); + }; + + return { + copy: copy, + set: set, + preserve: preserve, + setAll: setAll, + setOptions: setOptions, + remove: remove, + get: get, + getRaw: getRaw, + getAllRaw: getAllRaw, + isValidValue: isValidValue, + reflow: reflow, + transfer: transfer + }; + } + ); + + /** + * EditorView.js + * + * Released under LGPL License. + * Copyright (c) 1999-2016 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.EditorView', [ + 'ephox.katamari.api.Fun', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.api.properties.Css', + 'ephox.sugar.api.search.Traverse' + ], + function(Fun, Element, Css, Traverse) { + var getProp = function(propName, elm) { + var rawElm = elm.dom(); + return rawElm[propName]; + }; + + var getComputedSizeProp = function(propName, elm) { + return parseInt(Css.get(elm, propName), 10); + }; + + var getClientWidth = Fun.curry(getProp, 'clientWidth'); + var getClientHeight = Fun.curry(getProp, 'clientHeight'); + var getMarginTop = Fun.curry(getComputedSizeProp, 'margin-top'); + var getMarginLeft = Fun.curry(getComputedSizeProp, 'margin-left'); + + var getBoundingClientRect = function(elm) { + return elm.dom().getBoundingClientRect(); + }; + + var isInsideElementContentArea = function(bodyElm, clientX, clientY) { + var clientWidth = getClientWidth(bodyElm); + var clientHeight = getClientHeight(bodyElm); + + return clientX >= 0 && clientY >= 0 && clientX <= clientWidth && clientY <= clientHeight; + }; + + var transpose = function(inline, elm, clientX, clientY) { + var clientRect = getBoundingClientRect(elm); + var deltaX = inline ? clientRect.left + elm.dom().clientLeft + getMarginLeft(elm) : 0; + var deltaY = inline ? clientRect.top + elm.dom().clientTop + getMarginTop(elm) : 0; + var x = clientX - deltaX; + var y = clientY - deltaY; + + return { + x: x, + y: y + }; + }; + + // Checks if the specified coordinate is within the visual content area excluding the scrollbars + var isXYInContentArea = function(editor, clientX, clientY) { + var bodyElm = Element.fromDom(editor.getBody()); + var targetElm = editor.inline ? bodyElm : Traverse.documentElement(bodyElm); + var transposedPoint = transpose(editor.inline, targetElm, clientX, clientY); + + return isInsideElementContentArea(targetElm, transposedPoint.x, transposedPoint.y); + }; + + return { + isXYInContentArea: isXYInContentArea + }; + } + ); + + /** + * SelectionOverrides.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module contains logic overriding the selection with keyboard/mouse + * around contentEditable=false regions. + * + * @example + * // Disable the default cE=false selection + * tinymce.activeEditor.on('ShowCaret BeforeObjectSelected', function(e) { + * e.preventDefault(); + * }); + * + * @private + * @class tinymce.SelectionOverrides + */ + define( + 'tinymce.core.SelectionOverrides', [ + 'tinymce.core.caret.CaretContainer', + 'tinymce.core.caret.CaretPosition', + 'tinymce.core.caret.CaretUtils', + 'tinymce.core.caret.CaretWalker', + 'tinymce.core.caret.FakeCaret', + 'tinymce.core.caret.LineUtils', + 'tinymce.core.dom.NodeType', + 'tinymce.core.dom.RangePoint', + 'tinymce.core.DragDropOverrides', + 'tinymce.core.EditorView', + 'tinymce.core.Env', + 'tinymce.core.keyboard.CefUtils', + 'tinymce.core.util.Delay', + 'tinymce.core.util.VK' + ], + function( + CaretContainer, CaretPosition, CaretUtils, CaretWalker, FakeCaret, LineUtils, NodeType, RangePoint, DragDropOverrides, EditorView, Env, CefUtils, Delay, + VK + ) { + var isContentEditableTrue = NodeType.isContentEditableTrue, + isContentEditableFalse = NodeType.isContentEditableFalse, + isAfterContentEditableFalse = CaretUtils.isAfterContentEditableFalse, + isBeforeContentEditableFalse = CaretUtils.isBeforeContentEditableFalse; + + function SelectionOverrides(editor) { + var rootNode = editor.getBody(); + var fakeCaret = new FakeCaret(editor.getBody(), isBlock), + realSelectionId = 'sel-' + editor.dom.uniqueId(), + selectedContentEditableNode; + + function isFakeSelectionElement(elm) { + return editor.dom.hasClass(elm, 'mce-offscreen-selection'); + } + + function getRealSelectionElement() { + var container = editor.dom.get(realSelectionId); + return container ? container.getElementsByTagName('*')[0] : container; + } + + function isBlock(node) { + return editor.dom.isBlock(node); + } + + function setRange(range) { + //console.log('setRange', range); + if (range) { + editor.selection.setRng(range); + } + } + + function getRange() { + return editor.selection.getRng(); + } + + function scrollIntoView(node, alignToTop) { + editor.selection.scrollIntoView(node, alignToTop); + } + + function showCaret(direction, node, before) { + var e; + + e = editor.fire('ShowCaret', { + target: node, + direction: direction, + before: before + }); + + if (e.isDefaultPrevented()) { + return null; + } + + scrollIntoView(node, direction === -1); + + return fakeCaret.show(before, node); + } + + function getNormalizedRangeEndPoint(direction, range) { + range = CaretUtils.normalizeRange(direction, rootNode, range); + + if (direction == -1) { + return CaretPosition.fromRangeStart(range); + } + + return CaretPosition.fromRangeEnd(range); + } + + function showBlockCaretContainer(blockCaretContainer) { + if (blockCaretContainer.hasAttribute('data-mce-caret')) { + CaretContainer.showCaretContainerBlock(blockCaretContainer); + setRange(getRange()); // Removes control rect on IE + scrollIntoView(blockCaretContainer[0]); + } + } + + function registerEvents() { + function getContentEditableRoot(node) { + var root = editor.getBody(); + + while (node && node != root) { + if (isContentEditableTrue(node) || isContentEditableFalse(node)) { + return node; + } + + node = node.parentNode; + } + + return null; + } + + // Some browsers (Chrome) lets you place the caret after a cE=false + // Make sure we render the caret container in this case + editor.on('mouseup', function(e) { + var range = getRange(); + + if (range.collapsed && EditorView.isXYInContentArea(editor, e.clientX, e.clientY)) { + setRange(CefUtils.renderCaretAtRange(editor, range)); + } + }); + + editor.on('click', function(e) { + var contentEditableRoot; + + contentEditableRoot = getContentEditableRoot(e.target); + if (contentEditableRoot) { + // Prevent clicks on links in a cE=false element + if (isContentEditableFalse(contentEditableRoot)) { + e.preventDefault(); + editor.focus(); + } + + // Removes fake selection if a cE=true is clicked within a cE=false like the toc title + if (isContentEditableTrue(contentEditableRoot)) { + if (editor.dom.isChildOf(contentEditableRoot, editor.selection.getNode())) { + removeContentEditableSelection(); + } + } + } + }); + + editor.on('blur NewBlock', function() { + removeContentEditableSelection(); + hideFakeCaret(); + }); + + function handleTouchSelect(editor) { + var moved = false; + + editor.on('touchstart', function() { + moved = false; + }); + + editor.on('touchmove', function() { + moved = true; + }); + + editor.on('touchend', function(e) { + var contentEditableRoot = getContentEditableRoot(e.target); + + if (isContentEditableFalse(contentEditableRoot)) { + if (!moved) { + e.preventDefault(); + setContentEditableSelection(CefUtils.selectNode(editor, contentEditableRoot)); + } + } + }); + } + + var hasNormalCaretPosition = function(elm) { + var caretWalker = new CaretWalker(elm); + + if (!elm.firstChild) { + return false; + } + + var startPos = CaretPosition.before(elm.firstChild); + var newPos = caretWalker.next(startPos); + + return newPos && !isBeforeContentEditableFalse(newPos) && !isAfterContentEditableFalse(newPos); + }; + + var isInSameBlock = function(node1, node2) { + var block1 = editor.dom.getParent(node1, editor.dom.isBlock); + var block2 = editor.dom.getParent(node2, editor.dom.isBlock); + return block1 === block2; + }; + + // Checks if the target node is in a block and if that block has a caret position better than the + // suggested caretNode this is to prevent the caret from being sucked in towards a cE=false block if + // they are adjacent on the vertical axis + var hasBetterMouseTarget = function(targetNode, caretNode) { + var targetBlock = editor.dom.getParent(targetNode, editor.dom.isBlock); + var caretBlock = editor.dom.getParent(caretNode, editor.dom.isBlock); + + return targetBlock && !isInSameBlock(targetBlock, caretBlock) && hasNormalCaretPosition(targetBlock); + }; + + handleTouchSelect(editor); + + editor.on('mousedown', function(e) { + var contentEditableRoot; + + if (EditorView.isXYInContentArea(editor, e.clientX, e.clientY) === false) { + return; + } + + contentEditableRoot = getContentEditableRoot(e.target); + if (contentEditableRoot) { + if (isContentEditableFalse(contentEditableRoot)) { + e.preventDefault(); + setContentEditableSelection(CefUtils.selectNode(editor, contentEditableRoot)); + } else { + removeContentEditableSelection(); + + // Check that we're not attempting a shift + click select within a contenteditable='true' element + if (!(isContentEditableTrue(contentEditableRoot) && e.shiftKey) && !RangePoint.isXYWithinRange(e.clientX, e.clientY, editor.selection.getRng())) { + editor.selection.placeCaretAt(e.clientX, e.clientY); + } + } + } else { + // Remove needs to be called here since the mousedown might alter the selection without calling selection.setRng + // and therefore not fire the AfterSetSelectionRange event. + removeContentEditableSelection(); + hideFakeCaret(); + + var caretInfo = LineUtils.closestCaret(rootNode, e.clientX, e.clientY); + if (caretInfo) { + if (!hasBetterMouseTarget(e.target, caretInfo.node)) { + e.preventDefault(); + editor.getBody().focus(); + setRange(showCaret(1, caretInfo.node, caretInfo.before)); + } + } + } + }); + + editor.on('keypress', function(e) { + if (VK.modifierPressed(e)) { + return; + } + + switch (e.keyCode) { + default: if (isContentEditableFalse(editor.selection.getNode())) { + e.preventDefault(); + } + break; + } + }); + + editor.on('getSelectionRange', function(e) { + var rng = e.range; + + if (selectedContentEditableNode) { + if (!selectedContentEditableNode.parentNode) { + selectedContentEditableNode = null; + return; + } + + rng = rng.cloneRange(); + rng.selectNode(selectedContentEditableNode); + e.range = rng; + } + }); + + editor.on('setSelectionRange', function(e) { + var rng; + + rng = setContentEditableSelection(e.range, e.forward); + if (rng) { + e.range = rng; + } + }); + + editor.on('AfterSetSelectionRange', function(e) { + var rng = e.range; + + if (!isRangeInCaretContainer(rng)) { + hideFakeCaret(); + } + + if (!isFakeSelectionElement(rng.startContainer.parentNode)) { + removeContentEditableSelection(); + } + }); + + editor.on('focus', function() { + // Make sure we have a proper fake caret on focus + Delay.setEditorTimeout(editor, function() { + editor.selection.setRng(CefUtils.renderRangeCaret(editor, editor.selection.getRng())); + }, 0); + }); + + editor.on('copy', function(e) { + var clipboardData = e.clipboardData; + + // Make sure we get proper html/text for the fake cE=false selection + // Doesn't work at all on Edge since it doesn't have proper clipboardData support + if (!e.isDefaultPrevented() && e.clipboardData && !Env.ie) { + var realSelectionElement = getRealSelectionElement(); + if (realSelectionElement) { + e.preventDefault(); + clipboardData.clearData(); + clipboardData.setData('text/html', realSelectionElement.outerHTML); + clipboardData.setData('text/plain', realSelectionElement.outerText); + } + } + }); + + DragDropOverrides.init(editor); + } + + function addCss() { + var styles = editor.contentStyles, + rootClass = '.mce-content-body'; + + styles.push(fakeCaret.getCss()); + styles.push( + rootClass + ' .mce-offscreen-selection {' + + 'position: absolute;' + + 'left: -9999999999px;' + + 'max-width: 1000000px;' + + '}' + + rootClass + ' *[contentEditable=false] {' + + 'cursor: default;' + + '}' + + rootClass + ' *[contentEditable=true] {' + + 'cursor: text;' + + '}' + ); + } + + function isWithinCaretContainer(node) { + return ( + CaretContainer.isCaretContainer(node) || + CaretContainer.startsWithCaretContainer(node) || + CaretContainer.endsWithCaretContainer(node) + ); + } + + function isRangeInCaretContainer(rng) { + return isWithinCaretContainer(rng.startContainer) || isWithinCaretContainer(rng.endContainer); + } + + function setContentEditableSelection(range, forward) { + var node, $ = editor.$, + dom = editor.dom, + $realSelectionContainer, sel, + startContainer, startOffset, endOffset, e, caretPosition, targetClone, origTargetClone; + + if (!range) { + return null; + } + + if (range.collapsed) { + if (!isRangeInCaretContainer(range)) { + if (forward === false) { + caretPosition = getNormalizedRangeEndPoint(-1, range); + + if (isContentEditableFalse(caretPosition.getNode(true))) { + return showCaret(-1, caretPosition.getNode(true), false); + } + + if (isContentEditableFalse(caretPosition.getNode())) { + return showCaret(-1, caretPosition.getNode(), !caretPosition.isAtEnd()); + } + } else { + caretPosition = getNormalizedRangeEndPoint(1, range); + + if (isContentEditableFalse(caretPosition.getNode())) { + return showCaret(1, caretPosition.getNode(), !caretPosition.isAtEnd()); + } + + if (isContentEditableFalse(caretPosition.getNode(true))) { + return showCaret(1, caretPosition.getNode(true), false); + } + } + } + + return null; + } + + startContainer = range.startContainer; + startOffset = range.startOffset; + endOffset = range.endOffset; + + // Normalizes <span cE=false>[</span>] to [<span cE=false></span>] + if (startContainer.nodeType == 3 && startOffset == 0 && isContentEditableFalse(startContainer.parentNode)) { + startContainer = startContainer.parentNode; + startOffset = dom.nodeIndex(startContainer); + startContainer = startContainer.parentNode; + } + + if (startContainer.nodeType != 1) { + return null; + } + + if (endOffset == startOffset + 1) { + node = startContainer.childNodes[startOffset]; + } + + if (!isContentEditableFalse(node)) { + return null; + } + + targetClone = origTargetClone = node.cloneNode(true); + e = editor.fire('ObjectSelected', { + target: node, + targetClone: targetClone + }); + if (e.isDefaultPrevented()) { + return null; + } + + targetClone = e.targetClone; + $realSelectionContainer = $('#' + realSelectionId); + if ($realSelectionContainer.length === 0) { + $realSelectionContainer = $( + '<div data-mce-bogus="all" class="mce-offscreen-selection"></div>' + ).attr('id', realSelectionId); + + $realSelectionContainer.appendTo(editor.getBody()); + } + + range = editor.dom.createRng(); + + // WHY is IE making things so hard! Copy on <i contentEditable="false">x</i> produces: <em>x</em> + // This is a ridiculous hack where we place the selection from a block over the inline element + // so that just the inline element is copied as is and not converted. + if (targetClone === origTargetClone && Env.ie) { + $realSelectionContainer.empty().append('<p style="font-size: 0" data-mce-bogus="all">\u00a0</p>').append(targetClone); + range.setStartAfter($realSelectionContainer[0].firstChild.firstChild); + range.setEndAfter(targetClone); + } else { + $realSelectionContainer.empty().append('\u00a0').append(targetClone).append('\u00a0'); + range.setStart($realSelectionContainer[0].firstChild, 1); + range.setEnd($realSelectionContainer[0].lastChild, 0); + } + + $realSelectionContainer.css({ + top: dom.getPos(node, editor.getBody()).y + }); + + $realSelectionContainer[0].focus(); + sel = editor.selection.getSel(); + sel.removeAllRanges(); + sel.addRange(range); + + editor.$('*[data-mce-selected]').removeAttr('data-mce-selected'); + node.setAttribute('data-mce-selected', 1); + selectedContentEditableNode = node; + hideFakeCaret(); + + return range; + } + + function removeContentEditableSelection() { + if (selectedContentEditableNode) { + selectedContentEditableNode.removeAttribute('data-mce-selected'); + editor.$('#' + realSelectionId).remove(); + selectedContentEditableNode = null; + } + } + + function destroy() { + fakeCaret.destroy(); + selectedContentEditableNode = null; + } + + function hideFakeCaret() { + fakeCaret.hide(); + } + + if (Env.ceFalse) { + registerEvents(); + addCss(); + } + + return { + showCaret: showCaret, + showBlockCaretContainer: showBlockCaretContainer, + hideFakeCaret: hideFakeCaret, + destroy: destroy + }; + } + + return SelectionOverrides; + } + ); + + /** + * NodePath.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Handles paths of nodes within an element. + * + * @private + * @class tinymce.dom.NodePath + */ + define( + 'tinymce.core.dom.NodePath', [ + "tinymce.core.dom.DOMUtils" + ], + function(DOMUtils) { + function create(rootNode, targetNode, normalized) { + var path = []; + + for (; targetNode && targetNode != rootNode; targetNode = targetNode.parentNode) { + path.push(DOMUtils.nodeIndex(targetNode, normalized)); + } + + return path; + } + + function resolve(rootNode, path) { + var i, node, children; + + for (node = rootNode, i = path.length - 1; i >= 0; i--) { + children = node.childNodes; + + if (path[i] > children.length - 1) { + return null; + } + + node = children[path[i]]; + } + + return node; + } + + return { + create: create, + resolve: resolve + }; + } + ); + /** + * Quirks.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + * + * @ignore-file + */ + + /** + * This file includes fixes for various browser quirks it's made to make it easy to add/remove browser specific fixes. + * + * @private + * @class tinymce.util.Quirks + */ + define( + 'tinymce.core.util.Quirks', [ + "tinymce.core.util.VK", + "tinymce.core.dom.RangeUtils", + "tinymce.core.dom.TreeWalker", + "tinymce.core.dom.NodePath", + "tinymce.core.html.Node", + "tinymce.core.html.Entities", + "tinymce.core.Env", + "tinymce.core.util.Tools", + "tinymce.core.util.Delay", + "tinymce.core.caret.CaretContainer", + "tinymce.core.caret.CaretPosition", + "tinymce.core.caret.CaretWalker" + ], + function(VK, RangeUtils, TreeWalker, NodePath, Node, Entities, Env, Tools, Delay, CaretContainer, CaretPosition, CaretWalker) { + return function(editor) { + var each = Tools.each; + var BACKSPACE = VK.BACKSPACE, + DELETE = VK.DELETE, + dom = editor.dom, + selection = editor.selection, + settings = editor.settings, + parser = editor.parser, + serializer = editor.serializer; + var isGecko = Env.gecko, + isIE = Env.ie, + isWebKit = Env.webkit; + var mceInternalUrlPrefix = 'data:text/mce-internal,'; + var mceInternalDataType = isIE ? 'Text' : 'URL'; + + /** + * Executes a command with a specific state this can be to enable/disable browser editing features. + */ + function setEditorCommandState(cmd, state) { + try { + editor.getDoc().execCommand(cmd, false, state); + } catch (ex) { + // Ignore + } + } + + /** + * Returns current IE document mode. + */ + function getDocumentMode() { + var documentMode = editor.getDoc().documentMode; + + return documentMode ? documentMode : 6; + } + + /** + * Returns true/false if the event is prevented or not. + * + * @private + * @param {Event} e Event object. + * @return {Boolean} true/false if the event is prevented or not. + */ + function isDefaultPrevented(e) { + return e.isDefaultPrevented(); + } + + /** + * Sets Text/URL data on the event's dataTransfer object to a special data:text/mce-internal url. + * This is to workaround the inability to set custom contentType on IE and Safari. + * The editor's selected content is encoded into this url so drag and drop between editors will work. + * + * @private + * @param {DragEvent} e Event object + */ + function setMceInternalContent(e) { + var selectionHtml, internalContent; + + if (e.dataTransfer) { + if (editor.selection.isCollapsed() && e.target.tagName == 'IMG') { + selection.select(e.target); + } + + selectionHtml = editor.selection.getContent(); + + // Safari/IE doesn't support custom dataTransfer items so we can only use URL and Text + if (selectionHtml.length > 0) { + internalContent = mceInternalUrlPrefix + escape(editor.id) + ',' + escape(selectionHtml); + e.dataTransfer.setData(mceInternalDataType, internalContent); + } + } + } + + /** + * Gets content of special data:text/mce-internal url on the event's dataTransfer object. + * This is to workaround the inability to set custom contentType on IE and Safari. + * The editor's selected content is encoded into this url so drag and drop between editors will work. + * + * @private + * @param {DragEvent} e Event object + * @returns {String} mce-internal content + */ + function getMceInternalContent(e) { + var internalContent; + + if (e.dataTransfer) { + internalContent = e.dataTransfer.getData(mceInternalDataType); + + if (internalContent && internalContent.indexOf(mceInternalUrlPrefix) >= 0) { + internalContent = internalContent.substr(mceInternalUrlPrefix.length).split(','); + + return { + id: unescape(internalContent[0]), + html: unescape(internalContent[1]) + }; + } + } + + return null; + } + + /** + * Inserts contents using the paste clipboard command if it's available if it isn't it will fallback + * to the core command. + * + * @private + * @param {String} content Content to insert at selection. + * @param {Boolean} internal State if the paste is to be considered internal or external. + */ + function insertClipboardContents(content, internal) { + if (editor.queryCommandSupported('mceInsertClipboardContent')) { + editor.execCommand('mceInsertClipboardContent', false, { + content: content, + internal: internal + }); + } else { + editor.execCommand('mceInsertContent', false, content); + } + } + + /** + * Makes sure that the editor body becomes empty when backspace or delete is pressed in empty editors. + * + * For example: + * <p><b>|</b></p> + * + * Or: + * <h1>|</h1> + * + * Or: + * [<h1></h1>] + */ + function emptyEditorWhenDeleting() { + function serializeRng(rng) { + var body = dom.create("body"); + var contents = rng.cloneContents(); + body.appendChild(contents); + return selection.serializer.serialize(body, { + format: 'html' + }); + } + + function allContentsSelected(rng) { + if (!rng.setStart) { + if (rng.item) { + return false; + } + + var bodyRng = rng.duplicate(); + bodyRng.moveToElementText(editor.getBody()); + return RangeUtils.compareRanges(rng, bodyRng); + } + + var selection = serializeRng(rng); + + var allRng = dom.createRng(); + allRng.selectNode(editor.getBody()); + + var allSelection = serializeRng(allRng); + return selection === allSelection; + } + + editor.on('keydown', function(e) { + var keyCode = e.keyCode, + isCollapsed, body; + + // Empty the editor if it's needed for example backspace at <p><b>|</b></p> + if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) { + isCollapsed = editor.selection.isCollapsed(); + body = editor.getBody(); + + // Selection is collapsed but the editor isn't empty + if (isCollapsed && !dom.isEmpty(body)) { + return; + } + + // Selection isn't collapsed but not all the contents is selected + if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) { + return; + } + + // Manually empty the editor + e.preventDefault(); + editor.setContent(''); + + if (body.firstChild && dom.isBlock(body.firstChild)) { + editor.selection.setCursorLocation(body.firstChild, 0); + } else { + editor.selection.setCursorLocation(body, 0); + } + + editor.nodeChanged(); + } + }); + } + + /** + * WebKit doesn't select all the nodes in the body when you press Ctrl+A. + * IE selects more than the contents <body>[<p>a</p>]</body> instead of <body><p>[a]</p]</body> see bug #6438 + * This selects the whole body so that backspace/delete logic will delete everything + */ + function selectAll() { + editor.shortcuts.add('meta+a', null, 'SelectAll'); + } + + /** + * WebKit has a weird issue where it some times fails to properly convert keypresses to input method keystrokes. + * The IME on Mac doesn't initialize when it doesn't fire a proper focus event. + * + * This seems to happen when the user manages to click the documentElement element then the window doesn't get proper focus until + * you enter a character into the editor. + * + * It also happens when the first focus in made to the body. + * + * See: https://bugs.webkit.org/show_bug.cgi?id=83566 + */ + function inputMethodFocus() { + if (!editor.settings.content_editable) { + // Case 1 IME doesn't initialize if you focus the document + // Disabled since it was interferring with the cE=false logic + // Also coultn't reproduce the issue on Safari 9 + /*dom.bind(editor.getDoc(), 'focusin', function() { + selection.setRng(selection.getRng()); + });*/ + + // Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event + // Needs to be both down/up due to weird rendering bug on Chrome Windows + dom.bind(editor.getDoc(), 'mousedown mouseup', function(e) { + var rng; + + if (e.target == editor.getDoc().documentElement) { + rng = selection.getRng(); + editor.getBody().focus(); + + if (e.type == 'mousedown') { + if (CaretContainer.isCaretContainer(rng.startContainer)) { + return; + } + + // Edge case for mousedown, drag select and mousedown again within selection on Chrome Windows to render caret + selection.placeCaretAt(e.clientX, e.clientY); + } else { + selection.setRng(rng); + } + } + }); + } + } + + /** + * Backspacing in FireFox/IE from a paragraph into a horizontal rule results in a floating text node because the + * browser just deletes the paragraph - the browser fails to merge the text node with a horizontal rule so it is + * left there. TinyMCE sees a floating text node and wraps it in a paragraph on the key up event (ForceBlocks.js + * addRootBlocks), meaning the action does nothing. With this code, FireFox/IE matche the behaviour of other + * browsers. + * + * It also fixes a bug on Firefox where it's impossible to delete HR elements. + */ + function removeHrOnBackspace() { + editor.on('keydown', function(e) { + if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) { + // Check if there is any HR elements this is faster since getRng on IE 7 & 8 is slow + if (!editor.getBody().getElementsByTagName('hr').length) { + return; + } + + if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) { + var node = selection.getNode(); + var previousSibling = node.previousSibling; + + if (node.nodeName == 'HR') { + dom.remove(node); + e.preventDefault(); + return; + } + + if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") { + dom.remove(previousSibling); + e.preventDefault(); + } + } + } + }); + } + + /** + * Firefox 3.x has an issue where the body element won't get proper focus if you click out + * side it's rectangle. + */ + function focusBody() { + // Fix for a focus bug in FF 3.x where the body element + // wouldn't get proper focus if the user clicked on the HTML element + if (!window.Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4 + editor.on('mousedown', function(e) { + if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") { + var body = editor.getBody(); + + // Blur the body it's focused but not correctly focused + body.blur(); + + // Refocus the body after a little while + Delay.setEditorTimeout(editor, function() { + body.focus(); + }); + } + }); + } + } + + /** + * WebKit has a bug where it isn't possible to select image, hr or anchor elements + * by clicking on them so we need to fake that. + */ + function selectControlElements() { + editor.on('click', function(e) { + var target = e.target; + + // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 + // WebKit can't even do simple things like selecting an image + // Needs to be the setBaseAndExtend or it will fail to select floated images + if (/^(IMG|HR)$/.test(target.nodeName) && dom.getContentEditableParent(target) !== "false") { + e.preventDefault(); + editor.selection.select(target); + editor.nodeChanged(); + } + + if (target.nodeName == 'A' && dom.hasClass(target, 'mce-item-anchor')) { + e.preventDefault(); + selection.select(target); + } + }); + } + + /** + * Fixes a Gecko bug where the style attribute gets added to the wrong element when deleting between two block elements. + * + * Fixes do backspace/delete on this: + * <p>bla[ck</p><p style="color:red">r]ed</p> + * + * Would become: + * <p>bla|ed</p> + * + * Instead of: + * <p style="color:red">bla|ed</p> + */ + function removeStylesWhenDeletingAcrossBlockElements() { + function getAttributeApplyFunction() { + var template = dom.getAttribs(selection.getStart().cloneNode(false)); + + return function() { + var target = selection.getStart(); + + if (target !== editor.getBody()) { + dom.setAttrib(target, "style", null); + + each(template, function(attr) { + target.setAttributeNode(attr.cloneNode(true)); + }); + } + }; + } + + function isSelectionAcrossElements() { + return !selection.isCollapsed() && + dom.getParent(selection.getStart(), dom.isBlock) != dom.getParent(selection.getEnd(), dom.isBlock); + } + + editor.on('keypress', function(e) { + var applyAttributes; + + if (!isDefaultPrevented(e) && (e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) { + applyAttributes = getAttributeApplyFunction(); + editor.getDoc().execCommand('delete', false, null); + applyAttributes(); + e.preventDefault(); + return false; + } + }); + + dom.bind(editor.getDoc(), 'cut', function(e) { + var applyAttributes; + + if (!isDefaultPrevented(e) && isSelectionAcrossElements()) { + applyAttributes = getAttributeApplyFunction(); + + Delay.setEditorTimeout(editor, function() { + applyAttributes(); + }); + } + }); + } + + /** + * Screen readers on IE needs to have the role application set on the body. + */ + function ensureBodyHasRoleApplication() { + document.body.setAttribute("role", "application"); + } + + /** + * Backspacing into a table behaves differently depending upon browser type. + * Therefore, disable Backspace when cursor immediately follows a table. + */ + function disableBackspaceIntoATable() { + editor.on('keydown', function(e) { + if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) { + if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) { + var previousSibling = selection.getNode().previousSibling; + if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") { + e.preventDefault(); + return false; + } + } + } + }); + } + + /** + * Old IE versions can't properly render BR elements in PRE tags white in contentEditable mode. So this + * logic adds a \n before the BR so that it will get rendered. + */ + function addNewLinesBeforeBrInPre() { + // IE8+ rendering mode does the right thing with BR in PRE + if (getDocumentMode() > 7) { + return; + } + + // Enable display: none in area and add a specific class that hides all BR elements in PRE to + // avoid the caret from getting stuck at the BR elements while pressing the right arrow key + setEditorCommandState('RespectVisibilityInDesign', true); + editor.contentStyles.push('.mceHideBrInPre pre br {display: none}'); + dom.addClass(editor.getBody(), 'mceHideBrInPre'); + + // Adds a \n before all BR elements in PRE to get them visual + parser.addNodeFilter('pre', function(nodes) { + var i = nodes.length, + brNodes, j, brElm, sibling; + + while (i--) { + brNodes = nodes[i].getAll('br'); + j = brNodes.length; + while (j--) { + brElm = brNodes[j]; + + // Add \n before BR in PRE elements on older IE:s so the new lines get rendered + sibling = brElm.prev; + if (sibling && sibling.type === 3 && sibling.value.charAt(sibling.value - 1) != '\n') { + sibling.value += '\n'; + } else { + brElm.parent.insert(new Node('#text', 3), brElm, true).value = '\n'; + } + } + } + }); + + // Removes any \n before BR elements in PRE since other browsers and in contentEditable=false mode they will be visible + serializer.addNodeFilter('pre', function(nodes) { + var i = nodes.length, + brNodes, j, brElm, sibling; + + while (i--) { + brNodes = nodes[i].getAll('br'); + j = brNodes.length; + while (j--) { + brElm = brNodes[j]; + sibling = brElm.prev; + if (sibling && sibling.type == 3) { + sibling.value = sibling.value.replace(/\r?\n$/, ''); + } + } + } + }); + } + + /** + * Moves style width/height to attribute width/height when the user resizes an image on IE. + */ + function removePreSerializedStylesWhenSelectingControls() { + dom.bind(editor.getBody(), 'mouseup', function() { + var value, node = selection.getNode(); + + // Moved styles to attributes on IMG eements + if (node.nodeName == 'IMG') { + // Convert style width to width attribute + if ((value = dom.getStyle(node, 'width'))) { + dom.setAttrib(node, 'width', value.replace(/[^0-9%]+/g, '')); + dom.setStyle(node, 'width', ''); + } + + // Convert style height to height attribute + if ((value = dom.getStyle(node, 'height'))) { + dom.setAttrib(node, 'height', value.replace(/[^0-9%]+/g, '')); + dom.setStyle(node, 'height', ''); + } + } + }); + } + + /** + * Removes a blockquote when backspace is pressed at the beginning of it. + * + * For example: + * <blockquote><p>|x</p></blockquote> + * + * Becomes: + * <p>|x</p> + */ + function removeBlockQuoteOnBackSpace() { + // Add block quote deletion handler + editor.on('keydown', function(e) { + var rng, container, offset, root, parent; + + if (isDefaultPrevented(e) || e.keyCode != VK.BACKSPACE) { + return; + } + + rng = selection.getRng(); + container = rng.startContainer; + offset = rng.startOffset; + root = dom.getRoot(); + parent = container; + + if (!rng.collapsed || offset !== 0) { + return; + } + + while (parent && parent.parentNode && parent.parentNode.firstChild == parent && parent.parentNode != root) { + parent = parent.parentNode; + } + + // Is the cursor at the beginning of a blockquote? + if (parent.tagName === 'BLOCKQUOTE') { + // Remove the blockquote + editor.formatter.toggle('blockquote', null, parent); + + // Move the caret to the beginning of container + rng = dom.createRng(); + rng.setStart(container, 0); + rng.setEnd(container, 0); + selection.setRng(rng); + } + }); + } + + /** + * Sets various Gecko editing options on mouse down and before a execCommand to disable inline table editing that is broken etc. + */ + function setGeckoEditingOptions() { + function setOpts() { + refreshContentEditable(); + + setEditorCommandState("StyleWithCSS", false); + setEditorCommandState("enableInlineTableEditing", false); + + if (!settings.object_resizing) { + setEditorCommandState("enableObjectResizing", false); + } + } + + if (!settings.readonly) { + editor.on('BeforeExecCommand MouseDown', setOpts); + } + } + + /** + * Fixes a gecko link bug, when a link is placed at the end of block elements there is + * no way to move the caret behind the link. This fix adds a bogus br element after the link. + * + * For example this: + * <p><b><a href="#">x</a></b></p> + * + * Becomes this: + * <p><b><a href="#">x</a></b><br></p> + */ + function addBrAfterLastLinks() { + function fixLinks() { + each(dom.select('a'), function(node) { + var parentNode = node.parentNode, + root = dom.getRoot(); + + if (parentNode.lastChild === node) { + while (parentNode && !dom.isBlock(parentNode)) { + if (parentNode.parentNode.lastChild !== parentNode || parentNode === root) { + return; + } + + parentNode = parentNode.parentNode; + } + + dom.add(parentNode, 'br', { + 'data-mce-bogus': 1 + }); + } + }); + } + + editor.on('SetContent ExecCommand', function(e) { + if (e.type == "setcontent" || e.command === 'mceInsertLink') { + fixLinks(); + } + }); + } + + /** + * WebKit will produce DIV elements here and there by default. But since TinyMCE uses paragraphs by + * default we want to change that behavior. + */ + function setDefaultBlockType() { + if (settings.forced_root_block) { + editor.on('init', function() { + setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block); + }); + } + } + + /** + * Deletes the selected image on IE instead of navigating to previous page. + */ + function deleteControlItemOnBackSpace() { + editor.on('keydown', function(e) { + var rng; + + if (!isDefaultPrevented(e) && e.keyCode == BACKSPACE) { + rng = editor.getDoc().selection.createRange(); + if (rng && rng.item) { + e.preventDefault(); + editor.undoManager.beforeChange(); + dom.remove(rng.item(0)); + editor.undoManager.add(); + } + } + }); + } + + /** + * IE10 doesn't properly render block elements with the right height until you add contents to them. + * This fixes that by adding a padding-right to all empty text block elements. + * See: https://connect.microsoft.com/IE/feedback/details/743881 + */ + function renderEmptyBlocksFix() { + var emptyBlocksCSS; + + // IE10+ + if (getDocumentMode() >= 10) { + emptyBlocksCSS = ''; + each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) { + emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty'; + }); + + editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}'); + } + } + + /** + * Old IE versions can't retain contents within noscript elements so this logic will store the contents + * as a attribute and the insert that value as it's raw text when the DOM is serialized. + */ + function keepNoScriptContents() { + if (getDocumentMode() < 9) { + parser.addNodeFilter('noscript', function(nodes) { + var i = nodes.length, + node, textNode; + + while (i--) { + node = nodes[i]; + textNode = node.firstChild; + + if (textNode) { + node.attr('data-mce-innertext', textNode.value); + } + } + }); + + serializer.addNodeFilter('noscript', function(nodes) { + var i = nodes.length, + node, textNode, value; + + while (i--) { + node = nodes[i]; + textNode = nodes[i].firstChild; + + if (textNode) { + textNode.value = Entities.decode(textNode.value); + } else { + // Old IE can't retain noscript value so an attribute is used to store it + value = node.attributes.map['data-mce-innertext']; + if (value) { + node.attr('data-mce-innertext', null); + textNode = new Node('#text', 3); + textNode.value = value; + textNode.raw = true; + node.append(textNode); + } + } + } + }); + } + } + + /** + * IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode. + */ + function fixCaretSelectionOfDocumentElementOnIe() { + var doc = dom.doc, + body = doc.body, + started, startRng, htmlElm; + + // Return range from point or null if it failed + function rngFromPoint(x, y) { + var rng = body.createTextRange(); + + try { + rng.moveToPoint(x, y); + } catch (ex) { + // IE sometimes throws and exception, so lets just ignore it + rng = null; + } + + return rng; + } + + // Fires while the selection is changing + function selectionChange(e) { + var pointRng; + + // Check if the button is down or not + if (e.button) { + // Create range from mouse position + pointRng = rngFromPoint(e.x, e.y); + + if (pointRng) { + // Check if pointRange is before/after selection then change the endPoint + if (pointRng.compareEndPoints('StartToStart', startRng) > 0) { + pointRng.setEndPoint('StartToStart', startRng); + } else { + pointRng.setEndPoint('EndToEnd', startRng); + } + + pointRng.select(); + } + } else { + endSelection(); + } + } + + // Removes listeners + function endSelection() { + var rng = doc.selection.createRange(); + + // If the range is collapsed then use the last start range + if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0) { + startRng.select(); + } + + dom.unbind(doc, 'mouseup', endSelection); + dom.unbind(doc, 'mousemove', selectionChange); + startRng = started = 0; + } + + // Make HTML element unselectable since we are going to handle selection by hand + doc.documentElement.unselectable = true; + + // Detect when user selects outside BODY + dom.bind(doc, 'mousedown contextmenu', function(e) { + if (e.target.nodeName === 'HTML') { + if (started) { + endSelection(); + } + + // Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML + htmlElm = doc.documentElement; + if (htmlElm.scrollHeight > htmlElm.clientHeight) { + return; + } + + started = 1; + // Setup start position + startRng = rngFromPoint(e.x, e.y); + if (startRng) { + // Listen for selection change events + dom.bind(doc, 'mouseup', endSelection); + dom.bind(doc, 'mousemove', selectionChange); + + dom.getRoot().focus(); + startRng.select(); + } + } + }); + } + + /** + * Fixes selection issues where the caret can be placed between two inline elements like <b>a</b>|<b>b</b> + * this fix will lean the caret right into the closest inline element. + */ + function normalizeSelection() { + // Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i> except for Ctrl+A since it selects everything + editor.on('keyup focusin mouseup', function(e) { + if (e.keyCode != 65 || !VK.metaKeyPressed(e)) { + selection.normalize(); + } + }, true); + } + + /** + * Forces Gecko to render a broken image icon if it fails to load an image. + */ + function showBrokenImageIcon() { + editor.contentStyles.push( + 'img:-moz-broken {' + + '-moz-force-broken-image-icon:1;' + + 'min-width:24px;' + + 'min-height:24px' + + '}' + ); + } + + /** + * iOS has a bug where it's impossible to type if the document has a touchstart event + * bound and the user touches the document while having the on screen keyboard visible. + * + * The touch event moves the focus to the parent document while having the caret inside the iframe + * this fix moves the focus back into the iframe document. + */ + function restoreFocusOnKeyDown() { + if (!editor.inline) { + editor.on('keydown', function() { + if (document.activeElement == document.body) { + editor.getWin().focus(); + } + }); + } + } + + /** + * IE 11 has an annoying issue where you can't move focus into the editor + * by clicking on the white area HTML element. We used to be able to to fix this with + * the fixCaretSelectionOfDocumentElementOnIe fix. But since M$ removed the selection + * object it's not possible anymore. So we need to hack in a ungly CSS to force the + * body to be at least 150px. If the user clicks the HTML element out side this 150px region + * we simply move the focus into the first paragraph. Not ideal since you loose the + * positioning of the caret but goot enough for most cases. + */ + function bodyHeight() { + if (!editor.inline) { + editor.contentStyles.push('body {min-height: 150px}'); + editor.on('click', function(e) { + var rng; + + if (e.target.nodeName == 'HTML') { + // Edge seems to only need focus if we set the range + // the caret will become invisible and moved out of the iframe!! + if (Env.ie > 11) { + editor.getBody().focus(); + return; + } + + // Need to store away non collapsed ranges since the focus call will mess that up see #7382 + rng = editor.selection.getRng(); + editor.getBody().focus(); + editor.selection.setRng(rng); + editor.selection.normalize(); + editor.nodeChanged(); + } + }); + } + } + + /** + * Firefox on Mac OS will move the browser back to the previous page if you press CMD+Left arrow. + * You might then loose all your work so we need to block that behavior and replace it with our own. + */ + function blockCmdArrowNavigation() { + if (Env.mac) { + editor.on('keydown', function(e) { + if (VK.metaKeyPressed(e) && !e.shiftKey && (e.keyCode == 37 || e.keyCode == 39)) { + e.preventDefault(); + editor.selection.getSel().modify('move', e.keyCode == 37 ? 'backward' : 'forward', 'lineboundary'); + } + }); + } + } + + /** + * Disables the autolinking in IE 9+ this is then re-enabled by the autolink plugin. + */ + function disableAutoUrlDetect() { + setEditorCommandState("AutoUrlDetect", false); + } + + /** + * iOS 7.1 introduced two new bugs: + * 1) It's possible to open links within a contentEditable area by clicking on them. + * 2) If you hold down the finger it will display the link/image touch callout menu. + */ + function tapLinksAndImages() { + editor.on('click', function(e) { + var elm = e.target; + + do { + if (elm.tagName === 'A') { + e.preventDefault(); + return; + } + } while ((elm = elm.parentNode)); + }); + + editor.contentStyles.push('.mce-content-body {-webkit-touch-callout: none}'); + } + + /** + * iOS Safari and possible other browsers have a bug where it won't fire + * a click event when a contentEditable is focused. This function fakes click events + * by using touchstart/touchend and measuring the time and distance travelled. + */ + /* + function touchClickEvent() { + editor.on('touchstart', function(e) { + var elm, time, startTouch, changedTouches; + + elm = e.target; + time = new Date().getTime(); + changedTouches = e.changedTouches; + + if (!changedTouches || changedTouches.length > 1) { + return; + } + + startTouch = changedTouches[0]; + + editor.once('touchend', function(e) { + var endTouch = e.changedTouches[0], args; + + if (new Date().getTime() - time > 500) { + return; + } + + if (Math.abs(startTouch.clientX - endTouch.clientX) > 5) { + return; + } + + if (Math.abs(startTouch.clientY - endTouch.clientY) > 5) { + return; + } + + args = { + target: elm + }; + + each('pageX pageY clientX clientY screenX screenY'.split(' '), function(key) { + args[key] = endTouch[key]; + }); + + args = editor.fire('click', args); + + if (!args.isDefaultPrevented()) { + // iOS WebKit can't place the caret properly once + // you bind touch events so we need to do this manually + // TODO: Expand to the closest word? Touble tap still works. + editor.selection.placeCaretAt(endTouch.clientX, endTouch.clientY); + editor.nodeChanged(); + } + }); + }); + } + */ + + /** + * WebKit has a bug where it will allow forms to be submitted if they are inside a contentEditable element. + * For example this: <form><button></form> + */ + function blockFormSubmitInsideEditor() { + editor.on('init', function() { + editor.dom.bind(editor.getBody(), 'submit', function(e) { + e.preventDefault(); + }); + }); + } + + /** + * Sometimes WebKit/Blink generates BR elements with the Apple-interchange-newline class. + * + * Scenario: + * 1) Create a table 2x2. + * 2) Select and copy cells A2-B2. + * 3) Paste and it will add BR element to table cell. + */ + function removeAppleInterchangeBrs() { + parser.addNodeFilter('br', function(nodes) { + var i = nodes.length; + + while (i--) { + if (nodes[i].attr('class') == 'Apple-interchange-newline') { + nodes[i].remove(); + } + } + }); + } + + /** + * IE cannot set custom contentType's on drag events, and also does not properly drag/drop between + * editors. This uses a special data:text/mce-internal URL to pass data when drag/drop between editors. + */ + function ieInternalDragAndDrop() { + editor.on('dragstart', function(e) { + setMceInternalContent(e); + }); + + editor.on('drop', function(e) { + if (!isDefaultPrevented(e)) { + var internalContent = getMceInternalContent(e); + + if (internalContent && internalContent.id != editor.id) { + e.preventDefault(); + + var rng = RangeUtils.getCaretRangeFromPoint(e.x, e.y, editor.getDoc()); + selection.setRng(rng); + insertClipboardContents(internalContent.html, true); + } + } + }); + } + + function refreshContentEditable() { + // No-op since Mozilla seems to have fixed the caret repaint issues + } + + function isHidden() { + var sel; + + if (!isGecko || editor.removed) { + return 0; + } + + // Weird, wheres that cursor selection? + sel = editor.selection.getSel(); + return (!sel || !sel.rangeCount || sel.rangeCount === 0); + } + + /** + * Properly empties the editor if all contents is selected and deleted this to + * prevent empty paragraphs from being produced at beginning/end of contents. + */ + function emptyEditorOnDeleteEverything() { + function isEverythingSelected(editor) { + var caretWalker = new CaretWalker(editor.getBody()); + var rng = editor.selection.getRng(); + var startCaretPos = CaretPosition.fromRangeStart(rng); + var endCaretPos = CaretPosition.fromRangeEnd(rng); + var prev = caretWalker.prev(startCaretPos); + var next = caretWalker.next(endCaretPos); + + return !editor.selection.isCollapsed() && + (!prev || (prev.isAtStart() && startCaretPos.isEqual(prev))) && + (!next || (next.isAtEnd() && startCaretPos.isEqual(next))); + } + + // Type over case delete and insert this won't cover typeover with a IME but at least it covers the common case + editor.on('keypress', function(e) { + if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode > 31 && !VK.metaKeyPressed(e)) { + if (isEverythingSelected(editor)) { + e.preventDefault(); + editor.setContent(String.fromCharCode(e.charCode)); + editor.selection.select(editor.getBody(), true); + editor.selection.collapse(false); + editor.nodeChanged(); + } + } + }); + + editor.on('keydown', function(e) { + var keyCode = e.keyCode; + + if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) { + if (isEverythingSelected(editor)) { + e.preventDefault(); + editor.setContent(''); + editor.nodeChanged(); + } + } + }); + } + + // All browsers + removeBlockQuoteOnBackSpace(); + emptyEditorWhenDeleting(); + + // Windows phone will return a range like [body, 0] on mousedown so + // it will always normalize to the wrong location + if (!Env.windowsPhone) { + normalizeSelection(); + } + + // WebKit + if (isWebKit) { + emptyEditorOnDeleteEverything(); + inputMethodFocus(); + selectControlElements(); + setDefaultBlockType(); + blockFormSubmitInsideEditor(); + disableBackspaceIntoATable(); + removeAppleInterchangeBrs(); + + //touchClickEvent(); + + // iOS + if (Env.iOS) { + restoreFocusOnKeyDown(); + bodyHeight(); + tapLinksAndImages(); + } else { + selectAll(); + } + } + + // IE + if (isIE && Env.ie < 11) { + removeHrOnBackspace(); + ensureBodyHasRoleApplication(); + addNewLinesBeforeBrInPre(); + removePreSerializedStylesWhenSelectingControls(); + deleteControlItemOnBackSpace(); + renderEmptyBlocksFix(); + keepNoScriptContents(); + fixCaretSelectionOfDocumentElementOnIe(); + } + + if (Env.ie >= 11) { + bodyHeight(); + disableBackspaceIntoATable(); + } + + if (Env.ie) { + selectAll(); + disableAutoUrlDetect(); + ieInternalDragAndDrop(); + } + + // Gecko + if (isGecko) { + emptyEditorOnDeleteEverything(); + removeHrOnBackspace(); + focusBody(); + removeStylesWhenDeletingAcrossBlockElements(); + setGeckoEditingOptions(); + addBrAfterLastLinks(); + showBrokenImageIcon(); + blockCmdArrowNavigation(); + disableBackspaceIntoATable(); + } + + return { + refreshContentEditable: refreshContentEditable, + isHidden: isHidden + }; + }; + } + ); + + /** + * InitContentBody.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.init.InitContentBody', [ + 'global!document', + 'global!window', + 'tinymce.core.caret.CaretContainerInput', + 'tinymce.core.dom.DOMUtils', + 'tinymce.core.dom.Selection', + 'tinymce.core.dom.Serializer', + 'tinymce.core.EditorUpload', + 'tinymce.core.ErrorReporter', + 'tinymce.core.ForceBlocks', + 'tinymce.core.Formatter', + 'tinymce.core.html.DomParser', + 'tinymce.core.html.Node', + 'tinymce.core.html.Schema', + 'tinymce.core.keyboard.KeyboardOverrides', + 'tinymce.core.NodeChange', + 'tinymce.core.SelectionOverrides', + 'tinymce.core.UndoManager', + 'tinymce.core.util.Delay', + 'tinymce.core.util.Quirks', + 'tinymce.core.util.Tools' + ], + function( + document, window, CaretContainerInput, DOMUtils, Selection, Serializer, EditorUpload, ErrorReporter, ForceBlocks, Formatter, DomParser, Node, Schema, KeyboardOverrides, + NodeChange, SelectionOverrides, UndoManager, Delay, Quirks, Tools + ) { + var DOM = DOMUtils.DOM; + + var createParser = function(editor) { + var parser = new DomParser(editor.settings, editor.schema); + + // Convert src and href into data-mce-src, data-mce-href and data-mce-style + parser.addAttributeFilter('src,href,style,tabindex', function(nodes, name) { + var i = nodes.length, + node, dom = editor.dom, + value, internalName; + + while (i--) { + node = nodes[i]; + value = node.attr(name); + internalName = 'data-mce-' + name; + + // Add internal attribute if we need to we don't on a refresh of the document + if (!node.attributes.map[internalName]) { + // Don't duplicate these since they won't get modified by any browser + if (value.indexOf('data:') === 0 || value.indexOf('blob:') === 0) { + continue; + } + + if (name === "style") { + value = dom.serializeStyle(dom.parseStyle(value), node.name); + + if (!value.length) { + value = null; + } + + node.attr(internalName, value); + node.attr(name, value); + } else if (name === "tabindex") { + node.attr(internalName, value); + node.attr(name, null); + } else { + node.attr(internalName, editor.convertURL(value, name, node.name)); + } + } + } + }); + + // Keep scripts from executing + parser.addNodeFilter('script', function(nodes) { + var i = nodes.length, + node, type; + + while (i--) { + node = nodes[i]; + type = node.attr('type') || 'no/type'; + if (type.indexOf('mce-') !== 0) { + node.attr('type', 'mce-' + type); + } + } + }); + + parser.addNodeFilter('#cdata', function(nodes) { + var i = nodes.length, + node; + + while (i--) { + node = nodes[i]; + node.type = 8; + node.name = '#comment'; + node.value = '[CDATA[' + node.value + ']]'; + } + }); + + parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes) { + var i = nodes.length, + node, nonEmptyElements = editor.schema.getNonEmptyElements(); + + while (i--) { + node = nodes[i]; + + if (node.isEmpty(nonEmptyElements) && node.getAll('br').length === 0) { + node.append(new Node('br', 1)).shortEnded = true; + } + } + }); + + return parser; + }; + + var autoFocus = function(editor) { + if (editor.settings.auto_focus) { + Delay.setEditorTimeout(editor, function() { + var focusEditor; + + if (editor.settings.auto_focus === true) { + focusEditor = editor; + } else { + focusEditor = editor.editorManager.get(editor.settings.auto_focus); + } + + if (!focusEditor.destroyed) { + focusEditor.focus(); + } + }, 100); + } + }; + + var initEditor = function(editor) { + editor.bindPendingEventDelegates(); + editor.initialized = true; + editor.fire('init'); + editor.focus(true); + editor.nodeChanged({ + initial: true + }); + editor.execCallback('init_instance_callback', editor); + autoFocus(editor); + }; + + var getStyleSheetLoader = function(editor) { + return editor.inline ? DOM.styleSheetLoader : editor.dom.styleSheetLoader; + }; + + var initContentBody = function(editor, skipWrite) { + var settings = editor.settings, + targetElm = editor.getElement(), + doc = editor.getDoc(), + body, contentCssText; + + // Restore visibility on target element + if (!settings.inline) { + editor.getElement().style.visibility = editor.orgVisibility; + } + + // Setup iframe body + if (!skipWrite && !settings.content_editable) { + doc.open(); + doc.write(editor.iframeHTML); + doc.close(); + } + + if (settings.content_editable) { + editor.on('remove', function() { + var bodyEl = this.getBody(); + + DOM.removeClass(bodyEl, 'mce-content-body'); + DOM.removeClass(bodyEl, 'mce-edit-focus'); + DOM.setAttrib(bodyEl, 'contentEditable', null); + }); + + DOM.addClass(targetElm, 'mce-content-body'); + editor.contentDocument = doc = settings.content_document || document; + editor.contentWindow = settings.content_window || window; + editor.bodyElement = targetElm; + + // Prevent leak in IE + settings.content_document = settings.content_window = null; + + // TODO: Fix this + settings.root_name = targetElm.nodeName.toLowerCase(); + } + + // It will not steal focus while setting contentEditable + body = editor.getBody(); + body.disabled = true; + editor.readonly = settings.readonly; + + if (!editor.readonly) { + if (editor.inline && DOM.getStyle(body, 'position', true) === 'static') { + body.style.position = 'relative'; + } + + body.contentEditable = editor.getParam('content_editable_state', true); + } + + body.disabled = false; + + editor.editorUpload = new EditorUpload(editor); + editor.schema = new Schema(settings); + editor.dom = new DOMUtils(doc, { + keep_values: true, + url_converter: editor.convertURL, + url_converter_scope: editor, + hex_colors: settings.force_hex_style_colors, + class_filter: settings.class_filter, + update_styles: true, + root_element: editor.inline ? editor.getBody() : null, + collect: settings.content_editable, + schema: editor.schema, + onSetAttrib: function(e) { + editor.fire('SetAttrib', e); + } + }); + + editor.parser = createParser(editor); + editor.serializer = new Serializer(settings, editor); + editor.selection = new Selection(editor.dom, editor.getWin(), editor.serializer, editor); + editor.formatter = new Formatter(editor); + editor.undoManager = new UndoManager(editor); + editor._nodeChangeDispatcher = new NodeChange(editor); + editor._selectionOverrides = new SelectionOverrides(editor); + + CaretContainerInput.setup(editor); + KeyboardOverrides.setup(editor); + ForceBlocks.setup(editor); + + editor.fire('PreInit'); + + if (!settings.browser_spellcheck && !settings.gecko_spellcheck) { + doc.body.spellcheck = false; // Gecko + DOM.setAttrib(body, "spellcheck", "false"); + } + + editor.quirks = new Quirks(editor); + editor.fire('PostRender'); + + if (settings.directionality) { + body.dir = settings.directionality; + } + + if (settings.nowrap) { + body.style.whiteSpace = "nowrap"; + } + + if (settings.protect) { + editor.on('BeforeSetContent', function(e) { + Tools.each(settings.protect, function(pattern) { + e.content = e.content.replace(pattern, function(str) { + return '<!--mce:protected ' + escape(str) + '-->'; + }); + }); + }); + } + + editor.on('SetContent', function() { + editor.addVisual(editor.getBody()); + }); + + // Remove empty contents + if (settings.padd_empty_editor) { + editor.on('PostProcess', function(e) { + e.content = e.content.replace(/^(<p[^>]*>( | |\s|\u00a0|<br \/>|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, ''); + }); + } + + editor.load({ + initial: true, + format: 'html' + }); + editor.startContent = editor.getContent({ + format: 'raw' + }); + + editor.on('compositionstart compositionend', function(e) { + editor.composing = e.type === 'compositionstart'; + }); + + // Add editor specific CSS styles + if (editor.contentStyles.length > 0) { + contentCssText = ''; + + Tools.each(editor.contentStyles, function(style) { + contentCssText += style + "\r\n"; + }); + + editor.dom.addStyle(contentCssText); + } + + getStyleSheetLoader(editor).loadAll( + editor.contentCSS, + function(_) { + initEditor(editor); + }, + function(urls) { + initEditor(editor); + ErrorReporter.contentCssError(editor, urls); + } + ); + }; + + return { + initContentBody: initContentBody + }; + } + ); + + /** + * PluginManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.PluginManager', [ + 'tinymce.core.AddOnManager' + ], + function(AddOnManager) { + return AddOnManager.PluginManager; + } + ); + + /** + * ThemeManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.ThemeManager', [ + 'tinymce.core.AddOnManager' + ], + function(AddOnManager) { + return AddOnManager.ThemeManager; + } + ); + + /** + * Init.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.init.Init', [ + 'global!document', + 'global!window', + 'tinymce.core.dom.DOMUtils', + 'tinymce.core.Env', + 'tinymce.core.init.InitContentBody', + 'tinymce.core.PluginManager', + 'tinymce.core.ThemeManager', + 'tinymce.core.util.Tools', + 'tinymce.core.util.Uuid' + ], + function(document, window, DOMUtils, Env, InitContentBody, PluginManager, ThemeManager, Tools, Uuid) { + var DOM = DOMUtils.DOM; + + var initPlugin = function(editor, initializedPlugins, plugin) { + var Plugin = PluginManager.get(plugin), + pluginUrl, pluginInstance; + + pluginUrl = PluginManager.urls[plugin] || editor.documentBaseUrl.replace(/\/$/, ''); + plugin = Tools.trim(plugin); + if (Plugin && Tools.inArray(initializedPlugins, plugin) === -1) { + Tools.each(PluginManager.dependencies(plugin), function(dep) { + initPlugin(editor, initializedPlugins, dep); + }); + + if (editor.plugins[plugin]) { + return; + } + + pluginInstance = new Plugin(editor, pluginUrl, editor.$); + + editor.plugins[plugin] = pluginInstance; + + if (pluginInstance.init) { + pluginInstance.init(editor, pluginUrl); + initializedPlugins.push(plugin); + } + } + }; + + var initPlugins = function(editor) { + var initializedPlugins = []; + + Tools.each(editor.settings.plugins.replace(/\-/g, '').split(/[ ,]/), function(name) { + initPlugin(editor, initializedPlugins, name); + }); + }; + + var initTheme = function(editor) { + var Theme, settings = editor.settings; + + if (settings.theme) { + if (typeof settings.theme != "function") { + settings.theme = settings.theme.replace(/-/, ''); + Theme = ThemeManager.get(settings.theme); + editor.theme = new Theme(editor, ThemeManager.urls[settings.theme]); + + if (editor.theme.init) { + editor.theme.init(editor, ThemeManager.urls[settings.theme] || editor.documentBaseUrl.replace(/\/$/, ''), editor.$); + } + } else { + editor.theme = settings.theme; + } + } + }; + + var measueBox = function(editor) { + var w, h, minHeight, re, o, settings = editor.settings, + elm = editor.getElement(); + + // Measure box + if (settings.render_ui && editor.theme) { + editor.orgDisplay = elm.style.display; + + if (typeof settings.theme != "function") { + w = settings.width || DOM.getStyle(elm, 'width') || '100%'; + h = settings.height || DOM.getStyle(elm, 'height') || elm.offsetHeight; + minHeight = settings.min_height || 100; + re = /^[0-9\.]+(|px)$/i; + + if (re.test('' + w)) { + w = Math.max(parseInt(w, 10), 100); + } + + if (re.test('' + h)) { + h = Math.max(parseInt(h, 10), minHeight); + } + + // Render UI + o = editor.theme.renderUI({ + targetNode: elm, + width: w, + height: h, + deltaWidth: settings.delta_width, + deltaHeight: settings.delta_height + }); + + // Resize editor + if (!settings.content_editable) { + h = (o.iframeHeight || h) + (typeof h === 'number' ? (o.deltaHeight || 0) : ''); + if (h < minHeight) { + h = minHeight; + } + } + } else { + o = settings.theme(editor, elm); + + if (o.editorContainer.nodeType) { + o.editorContainer.id = o.editorContainer.id || editor.id + "_parent"; + } + + if (o.iframeContainer.nodeType) { + o.iframeContainer.id = o.iframeContainer.id || editor.id + "_iframecontainer"; + } + + // Use specified iframe height or the targets offsetHeight + h = o.iframeHeight || elm.offsetHeight; + } + + editor.editorContainer = o.editorContainer; + o.height = h; + } + + return o; + }; + + var relaxDomain = function(editor, ifr) { + // Domain relaxing is required since the user has messed around with document.domain + // This only applies to IE 11 other browsers including Edge seems to handle document.domain + if (document.domain !== window.location.hostname && Env.ie && Env.ie < 12) { + var bodyUuid = Uuid.uuid('mce'); + + editor[bodyUuid] = function() { + InitContentBody.initContentBody(editor); + }; + + /*eslint no-script-url:0 */ + var domainRelaxUrl = 'javascript:(function(){' + + 'document.open();document.domain="' + document.domain + '";' + + 'var ed = window.parent.tinymce.get("' + editor.id + '");document.write(ed.iframeHTML);' + + 'document.close();ed.' + bodyUuid + '(true);})()'; + + DOM.setAttrib(ifr, 'src', domainRelaxUrl); + return true; + } + + return false; + }; + + var createIframe = function(editor, o) { + var settings = editor.settings, + bodyId, bodyClass; + + editor.iframeHTML = settings.doctype + '<html><head>'; + + // We only need to override paths if we have to + // IE has a bug where it remove site absolute urls to relative ones if this is specified + if (settings.document_base_url != editor.documentBaseUrl) { + editor.iframeHTML += '<base href="' + editor.documentBaseURI.getURI() + '" />'; + } + + // IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode. + if (!Env.caretAfter && settings.ie7_compat) { + editor.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />'; + } + + editor.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />'; + + bodyId = settings.body_id || 'tinymce'; + if (bodyId.indexOf('=') != -1) { + bodyId = editor.getParam('body_id', '', 'hash'); + bodyId = bodyId[editor.id] || bodyId; + } + + bodyClass = settings.body_class || ''; + if (bodyClass.indexOf('=') != -1) { + bodyClass = editor.getParam('body_class', '', 'hash'); + bodyClass = bodyClass[editor.id] || ''; + } + + if (settings.content_security_policy) { + editor.iframeHTML += '<meta http-equiv="Content-Security-Policy" content="' + settings.content_security_policy + '" />'; + } + + editor.iframeHTML += '</head><body id="' + bodyId + + '" class="mce-content-body ' + bodyClass + + '" data-id="' + editor.id + '"><br></body></html>'; + + // Create iframe + // TODO: ACC add the appropriate description on this. + var ifr = DOM.create('iframe', { + id: editor.id + "_ifr", + frameBorder: '0', + allowTransparency: "true", + title: editor.editorManager.translate( + "Rich Text Area. Press ALT-F9 for menu. " + + "Press ALT-F10 for toolbar. Press ALT-0 for help" + ), + style: { + width: '100%', + height: o.height, + display: 'block' // Important for Gecko to render the iframe correctly + } + }); + + ifr.onload = function() { + ifr.onload = null; + editor.fire("load"); + }; + + var isDomainRelaxed = relaxDomain(editor, ifr); + + editor.contentAreaContainer = o.iframeContainer; + editor.iframeElement = ifr; + + DOM.add(o.iframeContainer, ifr); + + return isDomainRelaxed; + }; + + var init = function(editor) { + var settings = editor.settings, + elm = editor.getElement(), + boxInfo; + + editor.rtl = settings.rtl_ui || editor.editorManager.i18n.rtl; + editor.editorManager.i18n.setCode(settings.language); + settings.aria_label = settings.aria_label || DOM.getAttrib(elm, 'aria-label', editor.getLang('aria.rich_text_area')); + + editor.fire('ScriptsLoaded'); + + initTheme(editor); + initPlugins(editor); + boxInfo = measueBox(editor); + + // Load specified content CSS last + if (settings.content_css) { + Tools.each(Tools.explode(settings.content_css), function(u) { + editor.contentCSS.push(editor.documentBaseURI.toAbsolute(u)); + }); + } + + // Load specified content CSS last + if (settings.content_style) { + editor.contentStyles.push(settings.content_style); + } + + // Content editable mode ends here + if (settings.content_editable) { + return InitContentBody.initContentBody(editor); + } + + var isDomainRelaxed = createIframe(editor, boxInfo); + + if (boxInfo.editorContainer) { + DOM.get(boxInfo.editorContainer).style.display = editor.orgDisplay; + editor.hidden = DOM.isHidden(boxInfo.editorContainer); + } + + editor.getElement().style.display = 'none'; + DOM.setAttrib(editor.id, 'aria-hidden', true); + + if (!isDomainRelaxed) { + InitContentBody.initContentBody(editor); + } + }; + + return { + init: init + }; + } + ); + + /** + * Render.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.init.Render', [ + 'global!window', + 'tinymce.core.dom.DOMUtils', + 'tinymce.core.dom.EventUtils', + 'tinymce.core.dom.ScriptLoader', + 'tinymce.core.Env', + 'tinymce.core.ErrorReporter', + 'tinymce.core.init.Init', + 'tinymce.core.NotificationManager', + 'tinymce.core.PluginManager', + 'tinymce.core.ThemeManager', + 'tinymce.core.util.Tools', + 'tinymce.core.WindowManager' + ], + function(window, DOMUtils, EventUtils, ScriptLoader, Env, ErrorReporter, Init, NotificationManager, PluginManager, ThemeManager, Tools, WindowManager) { + var DOM = DOMUtils.DOM; + + var loadScripts = function(editor, suffix) { + var settings = editor.settings, + scriptLoader = ScriptLoader.ScriptLoader; + + if (settings.language && settings.language != 'en' && !settings.language_url) { + settings.language_url = editor.editorManager.baseURL + '/langs/' + settings.language + '.js'; + } + + if (settings.language_url) { + scriptLoader.add(settings.language_url); + } + + if (settings.theme && typeof settings.theme != "function" && + settings.theme.charAt(0) != '-' && !ThemeManager.urls[settings.theme]) { + var themeUrl = settings.theme_url; + + if (themeUrl) { + themeUrl = editor.documentBaseURI.toAbsolute(themeUrl); + } else { + themeUrl = 'themes/' + settings.theme + '/theme' + suffix + '.js'; + } + + ThemeManager.load(settings.theme, themeUrl); + } + + if (Tools.isArray(settings.plugins)) { + settings.plugins = settings.plugins.join(' '); + } + + Tools.each(settings.external_plugins, function(url, name) { + PluginManager.load(name, url); + settings.plugins += ' ' + name; + }); + + Tools.each(settings.plugins.split(/[ ,]/), function(plugin) { + plugin = Tools.trim(plugin); + + if (plugin && !PluginManager.urls[plugin]) { + if (plugin.charAt(0) === '-') { + plugin = plugin.substr(1, plugin.length); + + var dependencies = PluginManager.dependencies(plugin); + + Tools.each(dependencies, function(dep) { + var defaultSettings = { + prefix: 'plugins/', + resource: dep, + suffix: '/plugin' + suffix + '.js' + }; + + dep = PluginManager.createUrl(defaultSettings, dep); + PluginManager.load(dep.resource, dep); + }); + } else { + PluginManager.load(plugin, { + prefix: 'plugins/', + resource: plugin, + suffix: '/plugin' + suffix + '.js' + }); + } + } + }); + + scriptLoader.loadQueue(function() { + if (!editor.removed) { + Init.init(editor); + } + }, editor, function(urls) { + ErrorReporter.pluginLoadError(editor, urls[0]); + + if (!editor.removed) { + Init.init(editor); + } + }); + }; + + var render = function(editor) { + var settings = editor.settings, + id = editor.id; + + function readyHandler() { + DOM.unbind(window, 'ready', readyHandler); + editor.render(); + } + + // Page is not loaded yet, wait for it + if (!EventUtils.Event.domLoaded) { + DOM.bind(window, 'ready', readyHandler); + return; + } + + // Element not found, then skip initialization + if (!editor.getElement()) { + return; + } + + // No editable support old iOS versions etc + if (!Env.contentEditable) { + return; + } + + // Hide target element early to prevent content flashing + if (!settings.inline) { + editor.orgVisibility = editor.getElement().style.visibility; + editor.getElement().style.visibility = 'hidden'; + } else { + editor.inline = true; + } + + var form = editor.getElement().form || DOM.getParent(id, 'form'); + if (form) { + editor.formElement = form; + + // Add hidden input for non input elements inside form elements + if (settings.hidden_input && !/TEXTAREA|INPUT/i.test(editor.getElement().nodeName)) { + DOM.insertAfter(DOM.create('input', { + type: 'hidden', + name: id + }), id); + editor.hasHiddenInput = true; + } + + // Pass submit/reset from form to editor instance + editor.formEventDelegate = function(e) { + editor.fire(e.type, e); + }; + + DOM.bind(form, 'submit reset', editor.formEventDelegate); + + // Reset contents in editor when the form is reset + editor.on('reset', function() { + editor.setContent(editor.startContent, { + format: 'raw' + }); + }); + + // Check page uses id="submit" or name="submit" for it's submit button + if (settings.submit_patch && !form.submit.nodeType && !form.submit.length && !form._mceOldSubmit) { + form._mceOldSubmit = form.submit; + form.submit = function() { + editor.editorManager.triggerSave(); + editor.setDirty(false); + + return form._mceOldSubmit(form); + }; + } + } + + editor.windowManager = new WindowManager(editor); + editor.notificationManager = new NotificationManager(editor); + + if (settings.encoding === 'xml') { + editor.on('GetContent', function(e) { + if (e.save) { + e.content = DOM.encode(e.content); + } + }); + } + + if (settings.add_form_submit_trigger) { + editor.on('submit', function() { + if (editor.initialized) { + editor.save(); + } + }); + } + + if (settings.add_unload_trigger) { + editor._beforeUnload = function() { + if (editor.initialized && !editor.destroyed && !editor.isHidden()) { + editor.save({ + format: 'raw', + no_events: true, + set_dirty: false + }); + } + }; + + editor.editorManager.on('BeforeUnload', editor._beforeUnload); + } + + editor.editorManager.add(editor); + loadScripts(editor, editor.suffix); + }; + + return { + render: render + }; + } + ); + + /** + * Mode.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Mode switcher logic. + * + * @private + * @class tinymce.Mode + */ + define( + 'tinymce.core.Mode', [], + function() { + function setEditorCommandState(editor, cmd, state) { + try { + editor.getDoc().execCommand(cmd, false, state); + } catch (ex) { + // Ignore + } + } + + function clickBlocker(editor) { + var target, handler; + + target = editor.getBody(); + + handler = function(e) { + if (editor.dom.getParents(e.target, 'a').length > 0) { + e.preventDefault(); + } + }; + + editor.dom.bind(target, 'click', handler); + + return { + unbind: function() { + editor.dom.unbind(target, 'click', handler); + } + }; + } + + function toggleReadOnly(editor, state) { + if (editor._clickBlocker) { + editor._clickBlocker.unbind(); + editor._clickBlocker = null; + } + + if (state) { + editor._clickBlocker = clickBlocker(editor); + editor.selection.controlSelection.hideResizeRect(); + editor.readonly = true; + editor.getBody().contentEditable = false; + } else { + editor.readonly = false; + editor.getBody().contentEditable = true; + setEditorCommandState(editor, "StyleWithCSS", false); + setEditorCommandState(editor, "enableInlineTableEditing", false); + setEditorCommandState(editor, "enableObjectResizing", false); + editor.focus(); + editor.nodeChanged(); + } + } + + function setMode(editor, mode) { + var currentMode = editor.readonly ? 'readonly' : 'design'; + + if (mode == currentMode) { + return; + } + + if (editor.initialized) { + toggleReadOnly(editor, mode == 'readonly'); + } else { + editor.on('init', function() { + toggleReadOnly(editor, mode == 'readonly'); + }); + } + + // Event is NOT preventable + editor.fire('SwitchMode', { + mode: mode + }); + } + + return { + setMode: setMode + }; + } + ); + /** + * Sidebar.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module handle sidebar instances for the editor. + * + * @class tinymce.ui.Sidebar + * @private + */ + define( + 'tinymce.core.ui.Sidebar', [], + function() { + var add = function(editor, name, settings) { + var sidebars = editor.sidebars ? editor.sidebars : []; + sidebars.push({ + name: name, + settings: settings + }); + editor.sidebars = sidebars; + }; + + return { + add: add + }; + } + ); + + /** + * Editor.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /*jshint scripturl:true */ + + /** + * Include the base event class documentation. + * + * @include ../../../../../tools/docs/tinymce.Event.js + */ + + /** + * This class contains the core logic for a TinyMCE editor. + * + * @class tinymce.Editor + * @mixes tinymce.util.Observable + * @example + * // Add a class to all paragraphs in the editor. + * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass'); + * + * // Gets the current editors selection as text + * tinymce.activeEditor.selection.getContent({format: 'text'}); + * + * // Creates a new editor instance + * var ed = new tinymce.Editor('textareaid', { + * some_setting: 1 + * }, tinymce.EditorManager); + * + * ed.render(); + */ + define( + 'tinymce.core.Editor', [ + 'tinymce.core.AddOnManager', + 'tinymce.core.dom.DomQuery', + 'tinymce.core.dom.DOMUtils', + 'tinymce.core.EditorCommands', + 'tinymce.core.EditorObservable', + 'tinymce.core.EditorSettings', + 'tinymce.core.Env', + 'tinymce.core.html.Serializer', + 'tinymce.core.init.Render', + 'tinymce.core.Mode', + 'tinymce.core.Shortcuts', + 'tinymce.core.ui.Sidebar', + 'tinymce.core.util.Tools', + 'tinymce.core.util.URI', + 'tinymce.core.util.Uuid' + ], + function(AddOnManager, DomQuery, DOMUtils, EditorCommands, EditorObservable, EditorSettings, Env, Serializer, Render, Mode, Shortcuts, Sidebar, Tools, URI, Uuid) { + // Shorten these names + var DOM = DOMUtils.DOM; + var extend = Tools.extend, + each = Tools.each; + var trim = Tools.trim, + resolve = Tools.resolve; + var isGecko = Env.gecko, + ie = Env.ie; + + /** + * Include Editor API docs. + * + * @include ../../../../../tools/docs/tinymce.Editor.js + */ + + /** + * Constructs a editor instance by id. + * + * @constructor + * @method Editor + * @param {String} id Unique id for the editor. + * @param {Object} settings Settings for the editor. + * @param {tinymce.EditorManager} editorManager EditorManager instance. + */ + function Editor(id, settings, editorManager) { + var self = this, + documentBaseUrl, baseUri; + + documentBaseUrl = self.documentBaseUrl = editorManager.documentBaseURL; + baseUri = editorManager.baseURI; + + /** + * Name/value collection with editor settings. + * + * @property settings + * @type Object + * @example + * // Get the value of the theme setting + * tinymce.activeEditor.windowManager.alert("You are using the " + tinymce.activeEditor.settings.theme + " theme"); + */ + settings = EditorSettings.getEditorSettings(self, id, documentBaseUrl, editorManager.defaultSettings, settings); + self.settings = settings; + + AddOnManager.language = settings.language || 'en'; + AddOnManager.languageLoad = settings.language_load; + AddOnManager.baseURL = editorManager.baseURL; + + /** + * Editor instance id, normally the same as the div/textarea that was replaced. + * + * @property id + * @type String + */ + self.id = id; + + /** + * State to force the editor to return false on a isDirty call. + * + * @property isNotDirty + * @type Boolean + * @deprecated Use editor.setDirty instead. + */ + self.setDirty(false); + + /** + * Name/Value object containing plugin instances. + * + * @property plugins + * @type Object + * @example + * // Execute a method inside a plugin directly + * tinymce.activeEditor.plugins.someplugin.someMethod(); + */ + self.plugins = {}; + + /** + * URI object to document configured for the TinyMCE instance. + * + * @property documentBaseURI + * @type tinymce.util.URI + * @example + * // Get relative URL from the location of document_base_url + * tinymce.activeEditor.documentBaseURI.toRelative('/somedir/somefile.htm'); + * + * // Get absolute URL from the location of document_base_url + * tinymce.activeEditor.documentBaseURI.toAbsolute('somefile.htm'); + */ + self.documentBaseURI = new URI(settings.document_base_url, { + base_uri: baseUri + }); + + /** + * URI object to current document that holds the TinyMCE editor instance. + * + * @property baseURI + * @type tinymce.util.URI + * @example + * // Get relative URL from the location of the API + * tinymce.activeEditor.baseURI.toRelative('/somedir/somefile.htm'); + * + * // Get absolute URL from the location of the API + * tinymce.activeEditor.baseURI.toAbsolute('somefile.htm'); + */ + self.baseURI = baseUri; + + /** + * Array with CSS files to load into the iframe. + * + * @property contentCSS + * @type Array + */ + self.contentCSS = []; + + /** + * Array of CSS styles to add to head of document when the editor loads. + * + * @property contentStyles + * @type Array + */ + self.contentStyles = []; + + // Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic + self.shortcuts = new Shortcuts(self); + self.loadedCSS = {}; + self.editorCommands = new EditorCommands(self); + self.suffix = editorManager.suffix; + self.editorManager = editorManager; + self.inline = settings.inline; + + if (settings.cache_suffix) { + Env.cacheSuffix = settings.cache_suffix.replace(/^[\?\&]+/, ''); + } + + if (settings.override_viewport === false) { + Env.overrideViewPort = false; + } + + // Call setup + editorManager.fire('SetupEditor', self); + self.execCallback('setup', self); + + /** + * Dom query instance with default scope to the editor document and default element is the body of the editor. + * + * @property $ + * @type tinymce.dom.DomQuery + * @example + * tinymce.activeEditor.$('p').css('color', 'red'); + * tinymce.activeEditor.$().append('<p>new</p>'); + */ + self.$ = DomQuery.overrideDefaults(function() { + return { + context: self.inline ? self.getBody() : self.getDoc(), + element: self.getBody() + }; + }); + } + + Editor.prototype = { + /** + * Renders the editor/adds it to the page. + * + * @method render + */ + render: function() { + Render.render(this); + }, + + /** + * Focuses/activates the editor. This will set this editor as the activeEditor in the tinymce collection + * it will also place DOM focus inside the editor. + * + * @method focus + * @param {Boolean} skipFocus Skip DOM focus. Just set is as the active editor. + */ + focus: function(skipFocus) { + var self = this, + selection = self.selection, + contentEditable = self.settings.content_editable, + rng; + var controlElm, doc = self.getDoc(), + body = self.getBody(), + contentEditableHost; + + function getContentEditableHost(node) { + return self.dom.getParent(node, function(node) { + return self.dom.getContentEditable(node) === "true"; + }); + } + + if (self.removed) { + return; + } + + if (!skipFocus) { + // Get selected control element + rng = selection.getRng(); + if (rng.item) { + controlElm = rng.item(0); + } + + self.quirks.refreshContentEditable(); + + // Move focus to contentEditable=true child if needed + contentEditableHost = getContentEditableHost(selection.getNode()); + if (self.$.contains(body, contentEditableHost)) { + contentEditableHost.focus(); + selection.normalize(); + self.editorManager.setActive(self); + return; + } + + // Focus the window iframe + if (!contentEditable) { + // WebKit needs this call to fire focusin event properly see #5948 + // But Opera pre Blink engine will produce an empty selection so skip Opera + if (!Env.opera) { + self.getBody().focus(); + } + + self.getWin().focus(); + } + + // Focus the body as well since it's contentEditable + if (isGecko || contentEditable) { + // Check for setActive since it doesn't scroll to the element + if (body.setActive) { + // IE 11 sometimes throws "Invalid function" then fallback to focus + try { + body.setActive(); + } catch (ex) { + body.focus(); + } + } else { + // Restore previous selection before focus to prevent Chrome from + // jumping to the top of the document in long inline editors + self.selection.setRng(self.lastRng); + body.focus(); + } + + if (contentEditable) { + selection.normalize(); + } + } + + // Restore selected control element + // This is needed when for example an image is selected within a + // layer a call to focus will then remove the control selection + if (controlElm && controlElm.ownerDocument == doc) { + rng = doc.body.createControlRange(); + rng.addElement(controlElm); + rng.select(); + } + } + + self.editorManager.setActive(self); + }, + + /** + * Executes a legacy callback. This method is useful to call old 2.x option callbacks. + * There new event model is a better way to add callback so this method might be removed in the future. + * + * @method execCallback + * @param {String} name Name of the callback to execute. + * @return {Object} Return value passed from callback function. + */ + execCallback: function(name) { + var self = this, + callback = self.settings[name], + scope; + + if (!callback) { + return; + } + + // Look through lookup + if (self.callbackLookup && (scope = self.callbackLookup[name])) { + callback = scope.func; + scope = scope.scope; + } + + if (typeof callback === 'string') { + scope = callback.replace(/\.\w+$/, ''); + scope = scope ? resolve(scope) : 0; + callback = resolve(callback); + self.callbackLookup = self.callbackLookup || {}; + self.callbackLookup[name] = { + func: callback, + scope: scope + }; + } + + return callback.apply(scope || self, Array.prototype.slice.call(arguments, 1)); + }, + + /** + * Translates the specified string by replacing variables with language pack items it will also check if there is + * a key matching the input. + * + * @method translate + * @param {String} text String to translate by the language pack data. + * @return {String} Translated string. + */ + translate: function(text) { + if (text && Tools.is(text, 'string')) { + var lang = this.settings.language || 'en', + i18n = this.editorManager.i18n; + + text = i18n.data[lang + '.' + text] || text.replace(/\{\#([^\}]+)\}/g, function(a, b) { + return i18n.data[lang + '.' + b] || '{#' + b + '}'; + }); + } + + return this.editorManager.translate(text); + }, + + /** + * Returns a language pack item by name/key. + * + * @method getLang + * @param {String} name Name/key to get from the language pack. + * @param {String} defaultVal Optional default value to retrieve. + */ + getLang: function(name, defaultVal) { + return ( + this.editorManager.i18n.data[(this.settings.language || 'en') + '.' + name] || + (defaultVal !== undefined ? defaultVal : '{#' + name + '}') + ); + }, + + /** + * Returns a configuration parameter by name. + * + * @method getParam + * @param {String} name Configruation parameter to retrieve. + * @param {String} defaultVal Optional default value to return. + * @param {String} type Optional type parameter. + * @return {String} Configuration parameter value or default value. + * @example + * // Returns a specific config value from the currently active editor + * var someval = tinymce.activeEditor.getParam('myvalue'); + * + * // Returns a specific config value from a specific editor instance by id + * var someval2 = tinymce.get('my_editor').getParam('myvalue'); + */ + getParam: function(name, defaultVal, type) { + var value = name in this.settings ? this.settings[name] : defaultVal, + output; + + if (type === 'hash') { + output = {}; + + if (typeof value === 'string') { + each(value.indexOf('=') > 0 ? value.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value.split(','), function(value) { + value = value.split('='); + + if (value.length > 1) { + output[trim(value[0])] = trim(value[1]); + } else { + output[trim(value[0])] = trim(value); + } + }); + } else { + output = value; + } + + return output; + } + + return value; + }, + + /** + * Dispatches out a onNodeChange event to all observers. This method should be called when you + * need to update the UI states or element path etc. + * + * @method nodeChanged + * @param {Object} args Optional args to pass to NodeChange event handlers. + */ + nodeChanged: function(args) { + this._nodeChangeDispatcher.nodeChanged(args); + }, + + /** + * Adds a button that later gets created by the theme in the editors toolbars. + * + * @method addButton + * @param {String} name Button name to add. + * @param {Object} settings Settings object with title, cmd etc. + * @example + * // Adds a custom button to the editor that inserts contents when clicked + * tinymce.init({ + * ... + * + * toolbar: 'example' + * + * setup: function(ed) { + * ed.addButton('example', { + * title: 'My title', + * image: '../js/tinymce/plugins/example/img/example.gif', + * onclick: function() { + * ed.insertContent('Hello world!!'); + * } + * }); + * } + * }); + */ + addButton: function(name, settings) { + var self = this; + + if (settings.cmd) { + settings.onclick = function() { + self.execCommand(settings.cmd); + }; + } + + if (!settings.text && !settings.icon) { + settings.icon = name; + } + + self.buttons = self.buttons || {}; + settings.tooltip = settings.tooltip || settings.title; + self.buttons[name] = settings; + }, + + /** + * Adds a sidebar for the editor instance. + * + * @method addSidebar + * @param {String} name Sidebar name to add. + * @param {Object} settings Settings object with icon, onshow etc. + * @example + * // Adds a custom sidebar that when clicked logs the panel element + * tinymce.init({ + * ... + * setup: function(ed) { + * ed.addSidebar('example', { + * tooltip: 'My sidebar', + * icon: 'my-side-bar', + * onshow: function(api) { + * console.log(api.element()); + * } + * }); + * } + * }); + */ + addSidebar: function(name, settings) { + return Sidebar.add(this, name, settings); + }, + + /** + * Adds a menu item to be used in the menus of the theme. There might be multiple instances + * of this menu item for example it might be used in the main menus of the theme but also in + * the context menu so make sure that it's self contained and supports multiple instances. + * + * @method addMenuItem + * @param {String} name Menu item name to add. + * @param {Object} settings Settings object with title, cmd etc. + * @example + * // Adds a custom menu item to the editor that inserts contents when clicked + * // The context option allows you to add the menu item to an existing default menu + * tinymce.init({ + * ... + * + * setup: function(ed) { + * ed.addMenuItem('example', { + * text: 'My menu item', + * context: 'tools', + * onclick: function() { + * ed.insertContent('Hello world!!'); + * } + * }); + * } + * }); + */ + addMenuItem: function(name, settings) { + var self = this; + + if (settings.cmd) { + settings.onclick = function() { + self.execCommand(settings.cmd); + }; + } + + self.menuItems = self.menuItems || {}; + self.menuItems[name] = settings; + }, + + /** + * Adds a contextual toolbar to be rendered when the selector matches. + * + * @method addContextToolbar + * @param {function/string} predicate Predicate that needs to return true if provided strings get converted into CSS predicates. + * @param {String/Array} items String or array with items to add to the context toolbar. + */ + addContextToolbar: function(predicate, items) { + var self = this, + selector; + + self.contextToolbars = self.contextToolbars || []; + + // Convert selector to predicate + if (typeof predicate == "string") { + selector = predicate; + predicate = function(elm) { + return self.dom.is(elm, selector); + }; + } + + self.contextToolbars.push({ + id: Uuid.uuid('mcet'), + predicate: predicate, + items: items + }); + }, + + /** + * Adds a custom command to the editor, you can also override existing commands with this method. + * The command that you add can be executed with execCommand. + * + * @method addCommand + * @param {String} name Command name to add/override. + * @param {addCommandCallback} callback Function to execute when the command occurs. + * @param {Object} scope Optional scope to execute the function in. + * @example + * // Adds a custom command that later can be executed using execCommand + * tinymce.init({ + * ... + * + * setup: function(ed) { + * // Register example command + * ed.addCommand('mycommand', function(ui, v) { + * ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format: 'text'})); + * }); + * } + * }); + */ + addCommand: function(name, callback, scope) { + /** + * Callback function that gets called when a command is executed. + * + * @callback addCommandCallback + * @param {Boolean} ui Display UI state true/false. + * @param {Object} value Optional value for command. + * @return {Boolean} True/false state if the command was handled or not. + */ + this.editorCommands.addCommand(name, callback, scope); + }, + + /** + * Adds a custom query state command to the editor, you can also override existing commands with this method. + * The command that you add can be executed with queryCommandState function. + * + * @method addQueryStateHandler + * @param {String} name Command name to add/override. + * @param {addQueryStateHandlerCallback} callback Function to execute when the command state retrieval occurs. + * @param {Object} scope Optional scope to execute the function in. + */ + addQueryStateHandler: function(name, callback, scope) { + /** + * Callback function that gets called when a queryCommandState is executed. + * + * @callback addQueryStateHandlerCallback + * @return {Boolean} True/false state if the command is enabled or not like is it bold. + */ + this.editorCommands.addQueryStateHandler(name, callback, scope); + }, + + /** + * Adds a custom query value command to the editor, you can also override existing commands with this method. + * The command that you add can be executed with queryCommandValue function. + * + * @method addQueryValueHandler + * @param {String} name Command name to add/override. + * @param {addQueryValueHandlerCallback} callback Function to execute when the command value retrieval occurs. + * @param {Object} scope Optional scope to execute the function in. + */ + addQueryValueHandler: function(name, callback, scope) { + /** + * Callback function that gets called when a queryCommandValue is executed. + * + * @callback addQueryValueHandlerCallback + * @return {Object} Value of the command or undefined. + */ + this.editorCommands.addQueryValueHandler(name, callback, scope); + }, + + /** + * Adds a keyboard shortcut for some command or function. + * + * @method addShortcut + * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o. + * @param {String} desc Text description for the command. + * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed. + * @param {Object} sc Optional scope to execute the function in. + * @return {Boolean} true/false state if the shortcut was added or not. + */ + addShortcut: function(pattern, desc, cmdFunc, scope) { + this.shortcuts.add(pattern, desc, cmdFunc, scope); + }, + + /** + * Executes a command on the current instance. These commands can be TinyMCE internal commands prefixed with "mce" or + * they can be build in browser commands such as "Bold". A compleate list of browser commands is available on MSDN or Mozilla.org. + * This function will dispatch the execCommand function on each plugin, theme or the execcommand_callback option if none of these + * return true it will handle the command as a internal browser command. + * + * @method execCommand + * @param {String} cmd Command name to execute, for example mceLink or Bold. + * @param {Boolean} ui True/false state if a UI (dialog) should be presented or not. + * @param {mixed} value Optional command value, this can be anything. + * @param {Object} args Optional arguments object. + */ + execCommand: function(cmd, ui, value, args) { + return this.editorCommands.execCommand(cmd, ui, value, args); + }, + + /** + * Returns a command specific state, for example if bold is enabled or not. + * + * @method queryCommandState + * @param {string} cmd Command to query state from. + * @return {Boolean} Command specific state, for example if bold is enabled or not. + */ + queryCommandState: function(cmd) { + return this.editorCommands.queryCommandState(cmd); + }, + + /** + * Returns a command specific value, for example the current font size. + * + * @method queryCommandValue + * @param {string} cmd Command to query value from. + * @return {Object} Command specific value, for example the current font size. + */ + queryCommandValue: function(cmd) { + return this.editorCommands.queryCommandValue(cmd); + }, + + /** + * Returns true/false if the command is supported or not. + * + * @method queryCommandSupported + * @param {String} cmd Command that we check support for. + * @return {Boolean} true/false if the command is supported or not. + */ + queryCommandSupported: function(cmd) { + return this.editorCommands.queryCommandSupported(cmd); + }, + + /** + * Shows the editor and hides any textarea/div that the editor is supposed to replace. + * + * @method show + */ + show: function() { + var self = this; + + if (self.hidden) { + self.hidden = false; + + if (self.inline) { + self.getBody().contentEditable = true; + } else { + DOM.show(self.getContainer()); + DOM.hide(self.id); + } + + self.load(); + self.fire('show'); + } + }, + + /** + * Hides the editor and shows any textarea/div that the editor is supposed to replace. + * + * @method hide + */ + hide: function() { + var self = this, + doc = self.getDoc(); + + if (!self.hidden) { + // Fixed bug where IE has a blinking cursor left from the editor + if (ie && doc && !self.inline) { + doc.execCommand('SelectAll'); + } + + // We must save before we hide so Safari doesn't crash + self.save(); + + if (self.inline) { + self.getBody().contentEditable = false; + + // Make sure the editor gets blurred + if (self == self.editorManager.focusedEditor) { + self.editorManager.focusedEditor = null; + } + } else { + DOM.hide(self.getContainer()); + DOM.setStyle(self.id, 'display', self.orgDisplay); + } + + self.hidden = true; + self.fire('hide'); + } + }, + + /** + * Returns true/false if the editor is hidden or not. + * + * @method isHidden + * @return {Boolean} True/false if the editor is hidden or not. + */ + isHidden: function() { + return !!this.hidden; + }, + + /** + * Sets the progress state, this will display a throbber/progess for the editor. + * This is ideal for asynchronous operations like an AJAX save call. + * + * @method setProgressState + * @param {Boolean} state Boolean state if the progress should be shown or hidden. + * @param {Number} time Optional time to wait before the progress gets shown. + * @return {Boolean} Same as the input state. + * @example + * // Show progress for the active editor + * tinymce.activeEditor.setProgressState(true); + * + * // Hide progress for the active editor + * tinymce.activeEditor.setProgressState(false); + * + * // Show progress after 3 seconds + * tinymce.activeEditor.setProgressState(true, 3000); + */ + setProgressState: function(state, time) { + this.fire('ProgressState', { + state: state, + time: time + }); + }, + + /** + * Loads contents from the textarea or div element that got converted into an editor instance. + * This method will move the contents from that textarea or div into the editor by using setContent + * so all events etc that method has will get dispatched as well. + * + * @method load + * @param {Object} args Optional content object, this gets passed around through the whole load process. + * @return {String} HTML string that got set into the editor. + */ + load: function(args) { + var self = this, + elm = self.getElement(), + html; + + if (self.removed) { + return ''; + } + + if (elm) { + args = args || {}; + args.load = true; + + html = self.setContent(elm.value !== undefined ? elm.value : elm.innerHTML, args); + args.element = elm; + + if (!args.no_events) { + self.fire('LoadContent', args); + } + + args.element = elm = null; + + return html; + } + }, + + /** + * Saves the contents from a editor out to the textarea or div element that got converted into an editor instance. + * This method will move the HTML contents from the editor into that textarea or div by getContent + * so all events etc that method has will get dispatched as well. + * + * @method save + * @param {Object} args Optional content object, this gets passed around through the whole save process. + * @return {String} HTML string that got set into the textarea/div. + */ + save: function(args) { + var self = this, + elm = self.getElement(), + html, form; + + if (!elm || !self.initialized || self.removed) { + return; + } + + args = args || {}; + args.save = true; + + args.element = elm; + html = args.content = self.getContent(args); + + if (!args.no_events) { + self.fire('SaveContent', args); + } + + // Always run this internal event + if (args.format == 'raw') { + self.fire('RawSaveContent', args); + } + + html = args.content; + + if (!/TEXTAREA|INPUT/i.test(elm.nodeName)) { + // Update DIV element when not in inline mode + if (!self.inline) { + elm.innerHTML = html; + } + + // Update hidden form element + if ((form = DOM.getParent(self.id, 'form'))) { + each(form.elements, function(elm) { + if (elm.name == self.id) { + elm.value = html; + return false; + } + }); + } + } else { + elm.value = html; + } + + args.element = elm = null; + + if (args.set_dirty !== false) { + self.setDirty(false); + } + + return html; + }, + + /** + * Sets the specified content to the editor instance, this will cleanup the content before it gets set using + * the different cleanup rules options. + * + * @method setContent + * @param {String} content Content to set to editor, normally HTML contents but can be other formats as well. + * @param {Object} args Optional content object, this gets passed around through the whole set process. + * @return {String} HTML string that got set into the editor. + * @example + * // Sets the HTML contents of the activeEditor editor + * tinymce.activeEditor.setContent('<span>some</span> html'); + * + * // Sets the raw contents of the activeEditor editor + * tinymce.activeEditor.setContent('<span>some</span> html', {format: 'raw'}); + * + * // Sets the content of a specific editor (my_editor in this example) + * tinymce.get('my_editor').setContent(data); + * + * // Sets the bbcode contents of the activeEditor editor if the bbcode plugin was added + * tinymce.activeEditor.setContent('[b]some[/b] html', {format: 'bbcode'}); + */ + setContent: function(content, args) { + var self = this, + body = self.getBody(), + forcedRootBlockName, padd; + + // Setup args object + args = args || {}; + args.format = args.format || 'html'; + args.set = true; + args.content = content; + + // Do preprocessing + if (!args.no_events) { + self.fire('BeforeSetContent', args); + } + + content = args.content; + + // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content + // It will also be impossible to place the caret in the editor unless there is a BR element present + if (content.length === 0 || /^\s+$/.test(content)) { + padd = ie && ie < 11 ? '' : '<br data-mce-bogus="1">'; + + // Todo: There is a lot more root elements that need special padding + // so separate this and add all of them at some point. + if (body.nodeName == 'TABLE') { + content = '<tr><td>' + padd + '</td></tr>'; + } else if (/^(UL|OL)$/.test(body.nodeName)) { + content = '<li>' + padd + '</li>'; + } + + forcedRootBlockName = self.settings.forced_root_block; + + // Check if forcedRootBlock is configured and that the block is a valid child of the body + if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) { + // Padd with bogus BR elements on modern browsers and IE 7 and 8 since they don't render empty P tags properly + content = padd; + content = self.dom.createHTML(forcedRootBlockName, self.settings.forced_root_block_attrs, content); + } else if (!ie && !content) { + // We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret + content = '<br data-mce-bogus="1">'; + } + + self.dom.setHTML(body, content); + + self.fire('SetContent', args); + } else { + // Parse and serialize the html + if (args.format !== 'raw') { + content = new Serializer({ + validate: self.validate + }, self.schema).serialize( + self.parser.parse(content, { + isRootContent: true + }) + ); + } + + // Set the new cleaned contents to the editor + args.content = trim(content); + self.dom.setHTML(body, args.content); + + // Do post processing + if (!args.no_events) { + self.fire('SetContent', args); + } + + // Don't normalize selection if the focused element isn't the body in + // content editable mode since it will steal focus otherwise + /*if (!self.settings.content_editable || document.activeElement === self.getBody()) { + self.selection.normalize(); + }*/ + } + + return args.content; + }, + + /** + * Gets the content from the editor instance, this will cleanup the content before it gets returned using + * the different cleanup rules options. + * + * @method getContent + * @param {Object} args Optional content object, this gets passed around through the whole get process. + * @return {String} Cleaned content string, normally HTML contents. + * @example + * // Get the HTML contents of the currently active editor + * console.debug(tinymce.activeEditor.getContent()); + * + * // Get the raw contents of the currently active editor + * tinymce.activeEditor.getContent({format: 'raw'}); + * + * // Get content of a specific editor: + * tinymce.get('content id').getContent() + */ + getContent: function(args) { + var self = this, + content, body = self.getBody(); + + if (self.removed) { + return ''; + } + + // Setup args object + args = args || {}; + args.format = args.format || 'html'; + args.get = true; + args.getInner = true; + + // Do preprocessing + if (!args.no_events) { + self.fire('BeforeGetContent', args); + } + + // Get raw contents or by default the cleaned contents + if (args.format == 'raw') { + content = Tools.trim(self.serializer.getTrimmedContent()); + } else if (args.format == 'text') { + content = body.innerText || body.textContent; + } else { + content = self.serializer.serialize(body, args); + } + + // Trim whitespace in beginning/end of HTML + if (args.format != 'text') { + args.content = trim(content); + } else { + args.content = content; + } + + // Do post processing + if (!args.no_events) { + self.fire('GetContent', args); + } + + return args.content; + }, + + /** + * Inserts content at caret position. + * + * @method insertContent + * @param {String} content Content to insert. + * @param {Object} args Optional args to pass to insert call. + */ + insertContent: function(content, args) { + if (args) { + content = extend({ + content: content + }, args); + } + + this.execCommand('mceInsertContent', false, content); + }, + + /** + * Returns true/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents. + * + * The dirty state is automatically set to true if you do modifications to the content in other + * words when new undo levels is created or if you undo/redo to update the contents of the editor. It will also be set + * to false if you call editor.save(). + * + * @method isDirty + * @return {Boolean} True/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents. + * @example + * if (tinymce.activeEditor.isDirty()) + * alert("You must save your contents."); + */ + isDirty: function() { + return !this.isNotDirty; + }, + + /** + * Explicitly sets the dirty state. This will fire the dirty event if the editor dirty state is changed from false to true + * by invoking this method. + * + * @method setDirty + * @param {Boolean} state True/false if the editor is considered dirty. + * @example + * function ajaxSave() { + * var editor = tinymce.get('elm1'); + * + * // Save contents using some XHR call + * alert(editor.getContent()); + * + * editor.setDirty(false); // Force not dirty state + * } + */ + setDirty: function(state) { + var oldState = !this.isNotDirty; + + this.isNotDirty = !state; + + if (state && state != oldState) { + this.fire('dirty'); + } + }, + + /** + * Sets the editor mode. Mode can be for example "design", "code" or "readonly". + * + * @method setMode + * @param {String} mode Mode to set the editor in. + */ + setMode: function(mode) { + Mode.setMode(this, mode); + }, + + /** + * Returns the editors container element. The container element wrappes in + * all the elements added to the page for the editor. Such as UI, iframe etc. + * + * @method getContainer + * @return {Element} HTML DOM element for the editor container. + */ + getContainer: function() { + var self = this; + + if (!self.container) { + self.container = DOM.get(self.editorContainer || self.id + '_parent'); + } + + return self.container; + }, + + /** + * Returns the editors content area container element. The this element is the one who + * holds the iframe or the editable element. + * + * @method getContentAreaContainer + * @return {Element} HTML DOM element for the editor area container. + */ + getContentAreaContainer: function() { + return this.contentAreaContainer; + }, + + /** + * Returns the target element/textarea that got replaced with a TinyMCE editor instance. + * + * @method getElement + * @return {Element} HTML DOM element for the replaced element. + */ + getElement: function() { + if (!this.targetElm) { + this.targetElm = DOM.get(this.id); + } + + return this.targetElm; + }, + + /** + * Returns the iframes window object. + * + * @method getWin + * @return {Window} Iframe DOM window object. + */ + getWin: function() { + var self = this, + elm; + + if (!self.contentWindow) { + elm = self.iframeElement; + + if (elm) { + self.contentWindow = elm.contentWindow; + } + } + + return self.contentWindow; + }, + + /** + * Returns the iframes document object. + * + * @method getDoc + * @return {Document} Iframe DOM document object. + */ + getDoc: function() { + var self = this, + win; + + if (!self.contentDocument) { + win = self.getWin(); + + if (win) { + self.contentDocument = win.document; + } + } + + return self.contentDocument; + }, + + /** + * Returns the root element of the editable area. + * For a non-inline iframe-based editor, returns the iframe's body element. + * + * @method getBody + * @return {Element} The root element of the editable area. + */ + getBody: function() { + var doc = this.getDoc(); + return this.bodyElement || (doc ? doc.body : null); + }, + + /** + * URL converter function this gets executed each time a user adds an img, a or + * any other element that has a URL in it. This will be called both by the DOM and HTML + * manipulation functions. + * + * @method convertURL + * @param {string} url URL to convert. + * @param {string} name Attribute name src, href etc. + * @param {string/HTMLElement} elm Tag name or HTML DOM element depending on HTML or DOM insert. + * @return {string} Converted URL string. + */ + convertURL: function(url, name, elm) { + var self = this, + settings = self.settings; + + // Use callback instead + if (settings.urlconverter_callback) { + return self.execCallback('urlconverter_callback', url, elm, true, name); + } + + // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs + if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0 || url.length === 0) { + return url; + } + + // Convert to relative + if (settings.relative_urls) { + return self.documentBaseURI.toRelative(url); + } + + // Convert to absolute + url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host); + + return url; + }, + + /** + * Adds visual aid for tables, anchors etc so they can be more easily edited inside the editor. + * + * @method addVisual + * @param {Element} elm Optional root element to loop though to find tables etc that needs the visual aid. + */ + addVisual: function(elm) { + var self = this, + settings = self.settings, + dom = self.dom, + cls; + + elm = elm || self.getBody(); + + if (self.hasVisual === undefined) { + self.hasVisual = settings.visual; + } + + each(dom.select('table,a', elm), function(elm) { + var value; + + switch (elm.nodeName) { + case 'TABLE': + cls = settings.visual_table_class || 'mce-item-table'; + value = dom.getAttrib(elm, 'border'); + + if ((!value || value == '0') && self.hasVisual) { + dom.addClass(elm, cls); + } else { + dom.removeClass(elm, cls); + } + + return; + + case 'A': + if (!dom.getAttrib(elm, 'href', false)) { + value = dom.getAttrib(elm, 'name') || elm.id; + cls = settings.visual_anchor_class || 'mce-item-anchor'; + + if (value && self.hasVisual) { + dom.addClass(elm, cls); + } else { + dom.removeClass(elm, cls); + } + } + + return; + } + }); + + self.fire('VisualAid', { + element: elm, + hasVisual: self.hasVisual + }); + }, + + /** + * Removes the editor from the dom and tinymce collection. + * + * @method remove + */ + remove: function() { + var self = this; + + if (!self.removed) { + self.save(); + self.removed = 1; + self.unbindAllNativeEvents(); + + // Remove any hidden input + if (self.hasHiddenInput) { + DOM.remove(self.getElement().nextSibling); + } + + if (!self.inline) { + // IE 9 has a bug where the selection stops working if you place the + // caret inside the editor then remove the iframe + if (ie && ie < 10) { + self.getDoc().execCommand('SelectAll', false, null); + } + + DOM.setStyle(self.id, 'display', self.orgDisplay); + self.getBody().onload = null; // Prevent #6816 + } + + self.fire('remove'); + + self.editorManager.remove(self); + DOM.remove(self.getContainer()); + self._selectionOverrides.destroy(); + self.editorUpload.destroy(); + self.destroy(); + } + }, + + /** + * Destroys the editor instance by removing all events, element references or other resources + * that could leak memory. This method will be called automatically when the page is unloaded + * but you can also call it directly if you know what you are doing. + * + * @method destroy + * @param {Boolean} automatic Optional state if the destroy is an automatic destroy or user called one. + */ + destroy: function(automatic) { + var self = this, + form; + + // One time is enough + if (self.destroyed) { + return; + } + + // If user manually calls destroy and not remove + // Users seems to have logic that calls destroy instead of remove + if (!automatic && !self.removed) { + self.remove(); + return; + } + + if (!automatic) { + self.editorManager.off('beforeunload', self._beforeUnload); + + // Manual destroy + if (self.theme && self.theme.destroy) { + self.theme.destroy(); + } + + // Destroy controls, selection and dom + self.selection.destroy(); + self.dom.destroy(); + } + + form = self.formElement; + if (form) { + if (form._mceOldSubmit) { + form.submit = form._mceOldSubmit; + form._mceOldSubmit = null; + } + + DOM.unbind(form, 'submit reset', self.formEventDelegate); + } + + self.contentAreaContainer = self.formElement = self.container = self.editorContainer = null; + self.bodyElement = self.contentDocument = self.contentWindow = null; + self.iframeElement = self.targetElm = null; + + if (self.selection) { + self.selection = self.selection.win = self.selection.dom = self.selection.dom.doc = null; + } + + self.destroyed = 1; + }, + + /** + * Uploads all data uri/blob uri images in the editor contents to server. + * + * @method uploadImages + * @param {function} callback Optional callback with images and status for each image. + * @return {tinymce.util.Promise} Promise instance. + */ + uploadImages: function(callback) { + return this.editorUpload.uploadImages(callback); + }, + + // Internal functions + + _scanForImages: function() { + return this.editorUpload.scanForImages(); + } + }; + + extend(Editor.prototype, EditorObservable); + + return Editor; + } + ); + + /** + * I18n.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * I18n class that handles translation of TinyMCE UI. + * Uses po style with csharp style parameters. + * + * @class tinymce.util.I18n + */ + define( + 'tinymce.core.util.I18n', [ + "tinymce.core.util.Tools" + ], + function(Tools) { + "use strict"; + + var data = {}, + code = "en"; + + return { + /** + * Sets the current language code. + * + * @method setCode + * @param {String} newCode Current language code. + */ + setCode: function(newCode) { + if (newCode) { + code = newCode; + this.rtl = this.data[newCode] ? this.data[newCode]._dir === 'rtl' : false; + } + }, + + /** + * Returns the current language code. + * + * @method getCode + * @return {String} Current language code. + */ + getCode: function() { + return code; + }, + + /** + * Property gets set to true if a RTL language pack was loaded. + * + * @property rtl + * @type Boolean + */ + rtl: false, + + /** + * Adds translations for a specific language code. + * + * @method add + * @param {String} code Language code like sv_SE. + * @param {Array} items Name/value array with English en_US to sv_SE. + */ + add: function(code, items) { + var langData = data[code]; + + if (!langData) { + data[code] = langData = {}; + } + + for (var name in items) { + langData[name] = items[name]; + } + + this.setCode(code); + }, + + /** + * Translates the specified text. + * + * It has a few formats: + * I18n.translate("Text"); + * I18n.translate(["Text {0}/{1}", 0, 1]); + * I18n.translate({raw: "Raw string"}); + * + * @method translate + * @param {String/Object/Array} text Text to translate. + * @return {String} String that got translated. + */ + translate: function(text) { + var langData = data[code] || {}; + + /** + * number - string + * null, undefined and empty string - empty string + * array - comma-delimited string + * object - in [object Object] + * function - in [object Function] + * + * @param obj + * @returns {string} + */ + function toString(obj) { + if (Tools.is(obj, 'function')) { + return Object.prototype.toString.call(obj); + } + return !isEmpty(obj) ? '' + obj : ''; + } + + function isEmpty(text) { + return text === '' || text === null || Tools.is(text, 'undefined'); + } + + function getLangData(text) { + // make sure we work on a string and return a string + text = toString(text); + return Tools.hasOwn(langData, text) ? toString(langData[text]) : text; + } + + + if (isEmpty(text)) { + return ''; + } + + if (Tools.is(text, 'object') && Tools.hasOwn(text, 'raw')) { + return toString(text.raw); + } + + if (Tools.is(text, 'array')) { + var values = text.slice(1); + text = getLangData(text[0]).replace(/\{([0-9]+)\}/g, function($1, $2) { + return Tools.hasOwn(values, $2) ? toString(values[$2]) : $1; + }); + } + + return getLangData(text).replace(/{context:\w+}$/, ''); + }, + + data: data + }; + } + ); + /** + * FocusManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class manages the focus/blur state of the editor. This class is needed since some + * browsers fire false focus/blur states when the selection is moved to a UI dialog or similar. + * + * This class will fire two events focus and blur on the editor instances that got affected. + * It will also handle the restore of selection when the focus is lost and returned. + * + * @class tinymce.FocusManager + */ + define( + 'tinymce.core.FocusManager', [ + "tinymce.core.dom.DOMUtils", + "tinymce.core.util.Delay", + "tinymce.core.Env" + ], + function(DOMUtils, Delay, Env) { + var selectionChangeHandler, documentFocusInHandler, documentMouseUpHandler, DOM = DOMUtils.DOM; + + var isUIElement = function(editor, elm) { + var customSelector = editor ? editor.settings.custom_ui_selector : ''; + var parent = DOM.getParent(elm, function(elm) { + return ( + FocusManager.isEditorUIElement(elm) || + (customSelector ? editor.dom.is(elm, customSelector) : false) + ); + }); + return parent !== null; + }; + + var isInlineEditor = function(editor) { + return editor.inline === true; + }; + + var isElementOursideInlineEditor = function(editor, target) { + return isInlineEditor(editor) === false || editor.dom.isChildOf(target, editor.getBody()) === false; + }; + + /** + * Constructs a new focus manager instance. + * + * @constructor FocusManager + * @param {tinymce.EditorManager} editorManager Editor manager instance to handle focus for. + */ + function FocusManager(editorManager) { + function getActiveElement() { + try { + return document.activeElement; + } catch (ex) { + // IE sometimes fails to get the activeElement when resizing table + // TODO: Investigate this + return document.body; + } + } + + // We can't store a real range on IE 11 since it gets mutated so we need to use a bookmark object + // TODO: Move this to a separate range utils class since it's it's logic is present in Selection as well. + function createBookmark(dom, rng) { + if (rng && rng.startContainer) { + // Verify that the range is within the root of the editor + if (!dom.isChildOf(rng.startContainer, dom.getRoot()) || !dom.isChildOf(rng.endContainer, dom.getRoot())) { + return; + } + + return { + startContainer: rng.startContainer, + startOffset: rng.startOffset, + endContainer: rng.endContainer, + endOffset: rng.endOffset + }; + } + + return rng; + } + + function bookmarkToRng(editor, bookmark) { + var rng; + + if (bookmark.startContainer) { + rng = editor.getDoc().createRange(); + rng.setStart(bookmark.startContainer, bookmark.startOffset); + rng.setEnd(bookmark.endContainer, bookmark.endOffset); + } else { + rng = bookmark; + } + + return rng; + } + + function registerEvents(e) { + var editor = e.editor; + + editor.on('init', function() { + // Gecko/WebKit has ghost selections in iframes and IE only has one selection per browser tab + if (editor.inline || Env.ie) { + // Use the onbeforedeactivate event when available since it works better see #7023 + if ("onbeforedeactivate" in document && Env.ie < 9) { + editor.dom.bind(editor.getBody(), 'beforedeactivate', function(e) { + if (e.target != editor.getBody()) { + return; + } + + try { + editor.lastRng = editor.selection.getRng(); + } catch (ex) { + // IE throws "Unexcpected call to method or property access" some times so lets ignore it + } + }); + } else { + // On other browsers take snapshot on nodechange in inline mode since they have Ghost selections for iframes + editor.on('nodechange mouseup keyup', function(e) { + var node = getActiveElement(); + + // Only act on manual nodechanges + if (e.type == 'nodechange' && e.selectionChange) { + return; + } + + // IE 11 reports active element as iframe not body of iframe + if (node && node.id == editor.id + '_ifr') { + node = editor.getBody(); + } + + if (editor.dom.isChildOf(node, editor.getBody())) { + editor.lastRng = editor.selection.getRng(); + } + }); + } + + // Handles the issue with WebKit not retaining selection within inline document + // If the user releases the mouse out side the body since a mouse up event wont occur on the body + if (Env.webkit && !selectionChangeHandler) { + selectionChangeHandler = function() { + var activeEditor = editorManager.activeEditor; + + if (activeEditor && activeEditor.selection) { + var rng = activeEditor.selection.getRng(); + + // Store when it's non collapsed + if (rng && !rng.collapsed) { + editor.lastRng = rng; + } + } + }; + + DOM.bind(document, 'selectionchange', selectionChangeHandler); + } + } + }); + + editor.on('setcontent', function() { + editor.lastRng = null; + }); + + // Remove last selection bookmark on mousedown see #6305 + editor.on('mousedown', function() { + editor.selection.lastFocusBookmark = null; + }); + + editor.on('focusin', function() { + var focusedEditor = editorManager.focusedEditor, + lastRng; + + if (editor.selection.lastFocusBookmark) { + lastRng = bookmarkToRng(editor, editor.selection.lastFocusBookmark); + editor.selection.lastFocusBookmark = null; + editor.selection.setRng(lastRng); + } + + if (focusedEditor != editor) { + if (focusedEditor) { + focusedEditor.fire('blur', { + focusedEditor: editor + }); + } + + editorManager.setActive(editor); + editorManager.focusedEditor = editor; + editor.fire('focus', { + blurredEditor: focusedEditor + }); + editor.focus(true); + } + + editor.lastRng = null; + }); + + editor.on('focusout', function() { + Delay.setEditorTimeout(editor, function() { + var focusedEditor = editorManager.focusedEditor; + + // Still the same editor the blur was outside any editor UI + if (!isUIElement(editor, getActiveElement()) && focusedEditor == editor) { + editor.fire('blur', { + focusedEditor: null + }); + editorManager.focusedEditor = null; + + // Make sure selection is valid could be invalid if the editor is blured and removed before the timeout occurs + if (editor.selection) { + editor.selection.lastFocusBookmark = null; + } + } + }); + }); + + // Check if focus is moved to an element outside the active editor by checking if the target node + // isn't within the body of the activeEditor nor a UI element such as a dialog child control + if (!documentFocusInHandler) { + documentFocusInHandler = function(e) { + var activeEditor = editorManager.activeEditor, + target; + + target = e.target; + + if (activeEditor && target.ownerDocument === document) { + // Check to make sure we have a valid selection don't update the bookmark if it's + // a focusin to the body of the editor see #7025 + if (activeEditor.selection && target !== activeEditor.getBody() && isElementOursideInlineEditor(editor, target)) { + activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.dom, activeEditor.lastRng); + } + + // Fire a blur event if the element isn't a UI element + if (target !== document.body && !isUIElement(activeEditor, target) && editorManager.focusedEditor === activeEditor) { + activeEditor.fire('blur', { + focusedEditor: null + }); + editorManager.focusedEditor = null; + } + } + }; + + DOM.bind(document, 'focusin', documentFocusInHandler); + } + + // Handle edge case when user starts the selection inside the editor and releases + // the mouse outside the editor producing a new selection. This weird workaround is needed since + // Gecko doesn't have the "selectionchange" event we need to do this. Fixes: #6843 + if (editor.inline && !documentMouseUpHandler) { + documentMouseUpHandler = function(e) { + var activeEditor = editorManager.activeEditor, + dom = activeEditor.dom; + + if (activeEditor.inline && dom && !dom.isChildOf(e.target, activeEditor.getBody())) { + var rng = activeEditor.selection.getRng(); + + if (!rng.collapsed) { + activeEditor.lastRng = rng; + } + } + }; + + DOM.bind(document, 'mouseup', documentMouseUpHandler); + } + } + + function unregisterDocumentEvents(e) { + if (editorManager.focusedEditor == e.editor) { + editorManager.focusedEditor = null; + } + + if (!editorManager.activeEditor) { + DOM.unbind(document, 'selectionchange', selectionChangeHandler); + DOM.unbind(document, 'focusin', documentFocusInHandler); + DOM.unbind(document, 'mouseup', documentMouseUpHandler); + selectionChangeHandler = documentFocusInHandler = documentMouseUpHandler = null; + } + } + + editorManager.on('AddEditor', registerEvents); + editorManager.on('RemoveEditor', unregisterDocumentEvents); + } + + /** + * Returns true if the specified element is part of the UI for example an button or text input. + * + * @method isEditorUIElement + * @param {Element} elm Element to check if it's part of the UI or not. + * @return {Boolean} True/false state if the element is part of the UI or not. + */ + FocusManager.isEditorUIElement = function(elm) { + // Needs to be converted to string since svg can have focus: #6776 + return elm.className.toString().indexOf('mce-') !== -1; + }; + + FocusManager._isUIElement = isUIElement; + + return FocusManager; + } + ); + + /** + * LegacyInput.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Converts legacy input to modern HTML. + * + * @class tinymce.LegacyInput + * @private + */ + define( + 'tinymce.core.LegacyInput', [ + "tinymce.core.util.Tools" + ], + function(Tools) { + var each = Tools.each, + explode = Tools.explode; + + var register = function(EditorManager) { + EditorManager.on('AddEditor', function(e) { + var editor = e.editor; + + editor.on('preInit', function() { + var filters, fontSizes, dom, settings = editor.settings; + + function replaceWithSpan(node, styles) { + each(styles, function(value, name) { + if (value) { + dom.setStyle(node, name, value); + } + }); + + dom.rename(node, 'span'); + } + + function convert(e) { + dom = editor.dom; + + if (settings.convert_fonts_to_spans) { + each(dom.select('font,u,strike', e.node), function(node) { + filters[node.nodeName.toLowerCase()](dom, node); + }); + } + } + + if (settings.inline_styles) { + fontSizes = explode(settings.font_size_legacy_values); + + filters = { + font: function(dom, node) { + replaceWithSpan(node, { + backgroundColor: node.style.backgroundColor, + color: node.color, + fontFamily: node.face, + fontSize: fontSizes[parseInt(node.size, 10) - 1] + }); + }, + + u: function(dom, node) { + // HTML5 allows U element + if (editor.settings.schema === "html4") { + replaceWithSpan(node, { + textDecoration: 'underline' + }); + } + }, + + strike: function(dom, node) { + replaceWithSpan(node, { + textDecoration: 'line-through' + }); + } + }; + + editor.on('PreProcess SetContent', convert); + } + }); + }); + }; + + return { + register: register + }; + } + ); + /** + * EditorManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class used as a factory for manager for tinymce.Editor instances. + * + * @example + * tinymce.EditorManager.init({}); + * + * @class tinymce.EditorManager + * @mixes tinymce.util.Observable + * @static + */ + define( + 'tinymce.core.EditorManager', [ + 'tinymce.core.AddOnManager', + 'tinymce.core.dom.DomQuery', + 'tinymce.core.dom.DOMUtils', + 'tinymce.core.Editor', + 'tinymce.core.Env', + 'tinymce.core.ErrorReporter', + 'tinymce.core.FocusManager', + 'tinymce.core.LegacyInput', + 'tinymce.core.util.I18n', + 'tinymce.core.util.Observable', + 'tinymce.core.util.Promise', + 'tinymce.core.util.Tools', + 'tinymce.core.util.URI' + ], + function(AddOnManager, DomQuery, DOMUtils, Editor, Env, ErrorReporter, FocusManager, LegacyInput, I18n, Observable, Promise, Tools, URI) { + var DOM = DOMUtils.DOM; + var explode = Tools.explode, + each = Tools.each, + extend = Tools.extend; + var instanceCounter = 0, + beforeUnloadDelegate, EditorManager, boundGlobalEvents = false; + + function globalEventDelegate(e) { + each(EditorManager.editors, function(editor) { + if (e.type === 'scroll') { + editor.fire('ScrollWindow', e); + } else { + editor.fire('ResizeWindow', e); + } + }); + } + + function toggleGlobalEvents(editors, state) { + if (state !== boundGlobalEvents) { + if (state) { + DomQuery(window).on('resize scroll', globalEventDelegate); + } else { + DomQuery(window).off('resize scroll', globalEventDelegate); + } + + boundGlobalEvents = state; + } + } + + function removeEditorFromList(editor) { + var editors = EditorManager.editors, + removedFromList; + + delete editors[editor.id]; + + for (var i = 0; i < editors.length; i++) { + if (editors[i] == editor) { + editors.splice(i, 1); + removedFromList = true; + break; + } + } + + // Select another editor since the active one was removed + if (EditorManager.activeEditor == editor) { + EditorManager.activeEditor = editors[0]; + } + + // Clear focusedEditor if necessary, so that we don't try to blur the destroyed editor + if (EditorManager.focusedEditor == editor) { + EditorManager.focusedEditor = null; + } + + return removedFromList; + } + + function purgeDestroyedEditor(editor) { + // User has manually destroyed the editor lets clean up the mess + if (editor && editor.initialized && !(editor.getContainer() || editor.getBody()).parentNode) { + removeEditorFromList(editor); + editor.unbindAllNativeEvents(); + editor.destroy(true); + editor.removed = true; + editor = null; + } + + return editor; + } + + EditorManager = { + /** + * Dom query instance. + * + * @property $ + * @type tinymce.dom.DomQuery + */ + $: DomQuery, + + /** + * Major version of TinyMCE build. + * + * @property majorVersion + * @type String + */ + majorVersion: '4', + + /** + * Minor version of TinyMCE build. + * + * @property minorVersion + * @type String + */ + minorVersion: '6.4', + + /** + * Release date of TinyMCE build. + * + * @property releaseDate + * @type String + */ + releaseDate: '2017-06-13', + + /** + * Collection of editor instances. + * + * @property editors + * @type Object + * @example + * for (edId in tinymce.editors) + * tinymce.editors[edId].save(); + */ + editors: [], + + /** + * Collection of language pack data. + * + * @property i18n + * @type Object + */ + i18n: I18n, + + /** + * Currently active editor instance. + * + * @property activeEditor + * @type tinymce.Editor + * @example + * tinyMCE.activeEditor.selection.getContent(); + * tinymce.EditorManager.activeEditor.selection.getContent(); + */ + activeEditor: null, + + setup: function() { + var self = this, + baseURL, documentBaseURL, suffix = "", + preInit, src; + + // Get base URL for the current document + documentBaseURL = URI.getDocumentBaseUrl(document.location); + + // Check if the URL is a document based format like: http://site/dir/file and file:/// + // leave other formats like applewebdata://... intact + if (/^[^:]+:\/\/\/?[^\/]+\//.test(documentBaseURL)) { + documentBaseURL = documentBaseURL.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, ''); + + if (!/[\/\\]$/.test(documentBaseURL)) { + documentBaseURL += '/'; + } + } + + // If tinymce is defined and has a base use that or use the old tinyMCEPreInit + preInit = window.tinymce || window.tinyMCEPreInit; + if (preInit) { + baseURL = preInit.base || preInit.baseURL; + suffix = preInit.suffix; + } else { + // Get base where the tinymce script is located + var scripts = document.getElementsByTagName('script'); + for (var i = 0; i < scripts.length; i++) { + src = scripts[i].src; + + // Script types supported: + // tinymce.js tinymce.min.js tinymce.dev.js + // tinymce.jquery.js tinymce.jquery.min.js tinymce.jquery.dev.js + // tinymce.full.js tinymce.full.min.js tinymce.full.dev.js + var srcScript = src.substring(src.lastIndexOf('/')); + if (/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(src)) { + if (srcScript.indexOf('.min') != -1) { + suffix = '.min'; + } + + baseURL = src.substring(0, src.lastIndexOf('/')); + break; + } + } + + // We didn't find any baseURL by looking at the script elements + // Try to use the document.currentScript as a fallback + if (!baseURL && document.currentScript) { + src = document.currentScript.src; + + if (src.indexOf('.min') != -1) { + suffix = '.min'; + } + + baseURL = src.substring(0, src.lastIndexOf('/')); + } + } + + /** + * Base URL where the root directory if TinyMCE is located. + * + * @property baseURL + * @type String + */ + self.baseURL = new URI(documentBaseURL).toAbsolute(baseURL); + + /** + * Document base URL where the current document is located. + * + * @property documentBaseURL + * @type String + */ + self.documentBaseURL = documentBaseURL; + + /** + * Absolute baseURI for the installation path of TinyMCE. + * + * @property baseURI + * @type tinymce.util.URI + */ + self.baseURI = new URI(self.baseURL); + + /** + * Current suffix to add to each plugin/theme that gets loaded for example ".min". + * + * @property suffix + * @type String + */ + self.suffix = suffix; + + self.focusManager = new FocusManager(self); + }, + + /** + * Overrides the default settings for editor instances. + * + * @method overrideDefaults + * @param {Object} defaultSettings Defaults settings object. + */ + overrideDefaults: function(defaultSettings) { + var baseUrl, suffix; + + baseUrl = defaultSettings.base_url; + if (baseUrl) { + this.baseURL = new URI(this.documentBaseURL).toAbsolute(baseUrl.replace(/\/+$/, '')); + this.baseURI = new URI(this.baseURL); + } + + suffix = defaultSettings.suffix; + if (defaultSettings.suffix) { + this.suffix = suffix; + } + + this.defaultSettings = defaultSettings; + + var pluginBaseUrls = defaultSettings.plugin_base_urls; + for (var name in pluginBaseUrls) { + AddOnManager.PluginManager.urls[name] = pluginBaseUrls[name]; + } + }, + + /** + * Initializes a set of editors. This method will create editors based on various settings. + * + * @method init + * @param {Object} settings Settings object to be passed to each editor instance. + * @return {tinymce.util.Promise} Promise that gets resolved with an array of editors when all editor instances are initialized. + * @example + * // Initializes a editor using the longer method + * tinymce.EditorManager.init({ + * some_settings : 'some value' + * }); + * + * // Initializes a editor instance using the shorter version and with a promise + * tinymce.init({ + * some_settings : 'some value' + * }).then(function(editors) { + * ... + * }); + */ + init: function(settings) { + var self = this, + result, invalidInlineTargets; + + invalidInlineTargets = Tools.makeMap( + 'area base basefont br col frame hr img input isindex link meta param embed source wbr track ' + + 'colgroup option tbody tfoot thead tr script noscript style textarea video audio iframe object menu', + ' ' + ); + + function isInvalidInlineTarget(settings, elm) { + return settings.inline && elm.tagName.toLowerCase() in invalidInlineTargets; + } + + function createId(elm) { + var id = elm.id; + + // Use element id, or unique name or generate a unique id + if (!id) { + id = elm.name; + + if (id && !DOM.get(id)) { + id = elm.name; + } else { + // Generate unique name + id = DOM.uniqueId(); + } + + elm.setAttribute('id', id); + } + + return id; + } + + function execCallback(name) { + var callback = settings[name]; + + if (!callback) { + return; + } + + return callback.apply(self, Array.prototype.slice.call(arguments, 2)); + } + + function hasClass(elm, className) { + return className.constructor === RegExp ? className.test(elm.className) : DOM.hasClass(elm, className); + } + + function findTargets(settings) { + var l, targets = []; + + if (Env.ie && Env.ie < 11) { + ErrorReporter.initError( + 'TinyMCE does not support the browser you are using. For a list of supported' + + ' browsers please see: https://www.tinymce.com/docs/get-started/system-requirements/' + ); + return []; + } + + if (settings.types) { + each(settings.types, function(type) { + targets = targets.concat(DOM.select(type.selector)); + }); + + return targets; + } else if (settings.selector) { + return DOM.select(settings.selector); + } else if (settings.target) { + return [settings.target]; + } + + // Fallback to old setting + switch (settings.mode) { + case "exact": + l = settings.elements || ''; + + if (l.length > 0) { + each(explode(l), function(id) { + var elm; + + if ((elm = DOM.get(id))) { + targets.push(elm); + } else { + each(document.forms, function(f) { + each(f.elements, function(e) { + if (e.name === id) { + id = 'mce_editor_' + instanceCounter++; + DOM.setAttrib(e, 'id', id); + targets.push(e); + } + }); + }); + } + }); + } + break; + + case "textareas": + case "specific_textareas": + each(DOM.select('textarea'), function(elm) { + if (settings.editor_deselector && hasClass(elm, settings.editor_deselector)) { + return; + } + + if (!settings.editor_selector || hasClass(elm, settings.editor_selector)) { + targets.push(elm); + } + }); + break; + } + + return targets; + } + + var provideResults = function(editors) { + result = editors; + }; + + function initEditors() { + var initCount = 0, + editors = [], + targets; + + function createEditor(id, settings, targetElm) { + var editor = new Editor(id, settings, self); + + editors.push(editor); + + editor.on('init', function() { + if (++initCount === targets.length) { + provideResults(editors); + } + }); + + editor.targetElm = editor.targetElm || targetElm; + editor.render(); + } + + DOM.unbind(window, 'ready', initEditors); + execCallback('onpageload'); + + targets = DomQuery.unique(findTargets(settings)); + + // TODO: Deprecate this one + if (settings.types) { + each(settings.types, function(type) { + Tools.each(targets, function(elm) { + if (DOM.is(elm, type.selector)) { + createEditor(createId(elm), extend({}, settings, type), elm); + return false; + } + + return true; + }); + }); + + return; + } + + Tools.each(targets, function(elm) { + purgeDestroyedEditor(self.get(elm.id)); + }); + + targets = Tools.grep(targets, function(elm) { + return !self.get(elm.id); + }); + + if (targets.length === 0) { + provideResults([]); + } else { + each(targets, function(elm) { + if (isInvalidInlineTarget(settings, elm)) { + ErrorReporter.initError('Could not initialize inline editor on invalid inline target element', elm); + } else { + createEditor(createId(elm), settings, elm); + } + }); + } + } + + self.settings = settings; + DOM.bind(window, 'ready', initEditors); + + return new Promise(function(resolve) { + if (result) { + resolve(result); + } else { + provideResults = function(editors) { + resolve(editors); + }; + } + }); + }, + + /** + * Returns a editor instance by id. + * + * @method get + * @param {String/Number} id Editor instance id or index to return. + * @return {tinymce.Editor} Editor instance to return. + * @example + * // Adds an onclick event to an editor by id (shorter version) + * tinymce.get('mytextbox').on('click', function(e) { + * ed.windowManager.alert('Hello world!'); + * }); + * + * // Adds an onclick event to an editor by id (longer version) + * tinymce.EditorManager.get('mytextbox').on('click', function(e) { + * ed.windowManager.alert('Hello world!'); + * }); + */ + get: function(id) { + if (!arguments.length) { + return this.editors; + } + + return id in this.editors ? this.editors[id] : null; + }, + + /** + * Adds an editor instance to the editor collection. This will also set it as the active editor. + * + * @method add + * @param {tinymce.Editor} editor Editor instance to add to the collection. + * @return {tinymce.Editor} The same instance that got passed in. + */ + add: function(editor) { + var self = this, + editors = self.editors; + + // Add named and index editor instance + editors[editor.id] = editor; + editors.push(editor); + + toggleGlobalEvents(editors, true); + + // Doesn't call setActive method since we don't want + // to fire a bunch of activate/deactivate calls while initializing + self.activeEditor = editor; + + self.fire('AddEditor', { + editor: editor + }); + + if (!beforeUnloadDelegate) { + beforeUnloadDelegate = function() { + self.fire('BeforeUnload'); + }; + + DOM.bind(window, 'beforeunload', beforeUnloadDelegate); + } + + return editor; + }, + + /** + * Creates an editor instance and adds it to the EditorManager collection. + * + * @method createEditor + * @param {String} id Instance id to use for editor. + * @param {Object} settings Editor instance settings. + * @return {tinymce.Editor} Editor instance that got created. + */ + createEditor: function(id, settings) { + return this.add(new Editor(id, settings, this)); + }, + + /** + * Removes a editor or editors form page. + * + * @example + * // Remove all editors bound to divs + * tinymce.remove('div'); + * + * // Remove all editors bound to textareas + * tinymce.remove('textarea'); + * + * // Remove all editors + * tinymce.remove(); + * + * // Remove specific instance by id + * tinymce.remove('#id'); + * + * @method remove + * @param {tinymce.Editor/String/Object} [selector] CSS selector or editor instance to remove. + * @return {tinymce.Editor} The editor that got passed in will be return if it was found otherwise null. + */ + remove: function(selector) { + var self = this, + i, editors = self.editors, + editor; + + // Remove all editors + if (!selector) { + for (i = editors.length - 1; i >= 0; i--) { + self.remove(editors[i]); + } + + return; + } + + // Remove editors by selector + if (typeof selector == "string") { + selector = selector.selector || selector; + + each(DOM.select(selector), function(elm) { + editor = editors[elm.id]; + + if (editor) { + self.remove(editor); + } + }); + + return; + } + + // Remove specific editor + editor = selector; + + // Not in the collection + if (!editors[editor.id]) { + return null; + } + + if (removeEditorFromList(editor)) { + self.fire('RemoveEditor', { + editor: editor + }); + } + + if (!editors.length) { + DOM.unbind(window, 'beforeunload', beforeUnloadDelegate); + } + + editor.remove(); + + toggleGlobalEvents(editors, editors.length > 0); + + return editor; + }, + + /** + * Executes a specific command on the currently active editor. + * + * @method execCommand + * @param {String} cmd Command to perform for example Bold. + * @param {Boolean} ui Optional boolean state if a UI should be presented for the command or not. + * @param {String} value Optional value parameter like for example an URL to a link. + * @return {Boolean} true/false if the command was executed or not. + */ + execCommand: function(cmd, ui, value) { + var self = this, + editor = self.get(value); + + // Manager commands + switch (cmd) { + case "mceAddEditor": + if (!self.get(value)) { + new Editor(value, self.settings, self).render(); + } + + return true; + + case "mceRemoveEditor": + if (editor) { + editor.remove(); + } + + return true; + + case 'mceToggleEditor': + if (!editor) { + self.execCommand('mceAddEditor', 0, value); + return true; + } + + if (editor.isHidden()) { + editor.show(); + } else { + editor.hide(); + } + + return true; + } + + // Run command on active editor + if (self.activeEditor) { + return self.activeEditor.execCommand(cmd, ui, value); + } + + return false; + }, + + /** + * Calls the save method on all editor instances in the collection. This can be useful when a form is to be submitted. + * + * @method triggerSave + * @example + * // Saves all contents + * tinyMCE.triggerSave(); + */ + triggerSave: function() { + each(this.editors, function(editor) { + editor.save(); + }); + }, + + /** + * Adds a language pack, this gets called by the loaded language files like en.js. + * + * @method addI18n + * @param {String} code Optional language code. + * @param {Object} items Name/value object with translations. + */ + addI18n: function(code, items) { + I18n.add(code, items); + }, + + /** + * Translates the specified string using the language pack items. + * + * @method translate + * @param {String/Array/Object} text String to translate + * @return {String} Translated string. + */ + translate: function(text) { + return I18n.translate(text); + }, + + /** + * Sets the active editor instance and fires the deactivate/activate events. + * + * @method setActive + * @param {tinymce.Editor} editor Editor instance to set as the active instance. + */ + setActive: function(editor) { + var activeEditor = this.activeEditor; + + if (this.activeEditor != editor) { + if (activeEditor) { + activeEditor.fire('deactivate', { + relatedTarget: editor + }); + } + + editor.fire('activate', { + relatedTarget: activeEditor + }); + } + + this.activeEditor = editor; + } + }; + + extend(EditorManager, Observable); + + EditorManager.setup(); + LegacyInput.register(EditorManager); + + return EditorManager; + } + ); + + /** + * XHR.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class enables you to send XMLHTTPRequests cross browser. + * @class tinymce.util.XHR + * @mixes tinymce.util.Observable + * @static + * @example + * // Sends a low level Ajax request + * tinymce.util.XHR.send({ + * url: 'someurl', + * success: function(text) { + * console.debug(text); + * } + * }); + * + * // Add custom header to XHR request + * tinymce.util.XHR.on('beforeSend', function(e) { + * e.xhr.setRequestHeader('X-Requested-With', 'Something'); + * }); + */ + define( + 'tinymce.core.util.XHR', [ + "tinymce.core.util.Observable", + "tinymce.core.util.Tools" + ], + function(Observable, Tools) { + var XHR = { + /** + * Sends a XMLHTTPRequest. + * Consult the Wiki for details on what settings this method takes. + * + * @method send + * @param {Object} settings Object will target URL, callbacks and other info needed to make the request. + */ + send: function(settings) { + var xhr, count = 0; + + function ready() { + if (!settings.async || xhr.readyState == 4 || count++ > 10000) { + if (settings.success && count < 10000 && xhr.status == 200) { + settings.success.call(settings.success_scope, '' + xhr.responseText, xhr, settings); + } else if (settings.error) { + settings.error.call(settings.error_scope, count > 10000 ? 'TIMED_OUT' : 'GENERAL', xhr, settings); + } + + xhr = null; + } else { + setTimeout(ready, 10); + } + } + + // Default settings + settings.scope = settings.scope || this; + settings.success_scope = settings.success_scope || settings.scope; + settings.error_scope = settings.error_scope || settings.scope; + settings.async = settings.async === false ? false : true; + settings.data = settings.data || ''; + + XHR.fire('beforeInitialize', { + settings: settings + }); + + xhr = new XMLHttpRequest(); + + if (xhr) { + if (xhr.overrideMimeType) { + xhr.overrideMimeType(settings.content_type); + } + + xhr.open(settings.type || (settings.data ? 'POST' : 'GET'), settings.url, settings.async); + + if (settings.crossDomain) { + xhr.withCredentials = true; + } + + if (settings.content_type) { + xhr.setRequestHeader('Content-Type', settings.content_type); + } + + if (settings.requestheaders) { + Tools.each(settings.requestheaders, function(header) { + xhr.setRequestHeader(header.key, header.value); + }); + } + + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + + xhr = XHR.fire('beforeSend', { + xhr: xhr, + settings: settings + }).xhr; + xhr.send(settings.data); + + // Syncronous request + if (!settings.async) { + return ready(); + } + + // Wait for response, onReadyStateChange can not be used since it leaks memory in IE + setTimeout(ready, 10); + } + } + }; + + Tools.extend(XHR, Observable); + + return XHR; + } + ); + + /** + * JSON.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * JSON parser and serializer class. + * + * @class tinymce.util.JSON + * @static + * @example + * // JSON parse a string into an object + * var obj = tinymce.util.JSON.parse(somestring); + * + * // JSON serialize a object into an string + * var str = tinymce.util.JSON.serialize(obj); + */ + define( + 'tinymce.core.util.JSON', [], + function() { + function serialize(o, quote) { + var i, v, t, name; + + quote = quote || '"'; + + if (o === null) { + return 'null'; + } + + t = typeof o; + + if (t == 'string') { + v = '\bb\tt\nn\ff\rr\""\'\'\\\\'; + + /*eslint no-control-regex:0 */ + return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) { + // Make sure single quotes never get encoded inside double quotes for JSON compatibility + if (quote === '"' && a === "'") { + return a; + } + + i = v.indexOf(b); + + if (i + 1) { + return '\\' + v.charAt(i + 1); + } + + a = b.charCodeAt().toString(16); + + return '\\u' + '0000'.substring(a.length) + a; + }) + quote; + } + + if (t == 'object') { + if (o.hasOwnProperty && Object.prototype.toString.call(o) === '[object Array]') { + for (i = 0, v = '['; i < o.length; i++) { + v += (i > 0 ? ',' : '') + serialize(o[i], quote); + } + + return v + ']'; + } + + v = '{'; + + for (name in o) { + if (o.hasOwnProperty(name)) { + v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name + + quote + ':' + serialize(o[name], quote) : ''; + } + } + + return v + '}'; + } + + return '' + o; + } + + return { + /** + * Serializes the specified object as a JSON string. + * + * @method serialize + * @param {Object} obj Object to serialize as a JSON string. + * @param {String} quote Optional quote string defaults to ". + * @return {string} JSON string serialized from input. + */ + serialize: serialize, + + /** + * Unserializes/parses the specified JSON string into a object. + * + * @method parse + * @param {string} s JSON String to parse into a JavaScript object. + * @return {Object} Object from input JSON string or undefined if it failed. + */ + parse: function(text) { + try { + // Trick uglify JS + return window[String.fromCharCode(101) + 'val']('(' + text + ')'); + } catch (ex) { + // Ignore + } + } + + /**#@-*/ + }; + } + ); + + /** + * JSONRequest.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class enables you to use JSON-RPC to call backend methods. + * + * @class tinymce.util.JSONRequest + * @example + * var json = new tinymce.util.JSONRequest({ + * url: 'somebackend.php' + * }); + * + * // Send RPC call 1 + * json.send({ + * method: 'someMethod1', + * params: ['a', 'b'], + * success: function(result) { + * console.dir(result); + * } + * }); + * + * // Send RPC call 2 + * json.send({ + * method: 'someMethod2', + * params: ['a', 'b'], + * success: function(result) { + * console.dir(result); + * } + * }); + */ + define( + 'tinymce.core.util.JSONRequest', [ + "tinymce.core.util.JSON", + "tinymce.core.util.XHR", + "tinymce.core.util.Tools" + ], + function(JSON, XHR, Tools) { + var extend = Tools.extend; + + function JSONRequest(settings) { + this.settings = extend({}, settings); + this.count = 0; + } + + /** + * Simple helper function to send a JSON-RPC request without the need to initialize an object. + * Consult the Wiki API documentation for more details on what you can pass to this function. + * + * @method sendRPC + * @static + * @param {Object} o Call object where there are three field id, method and params this object should also contain callbacks etc. + */ + JSONRequest.sendRPC = function(o) { + return new JSONRequest().send(o); + }; + + JSONRequest.prototype = { + /** + * Sends a JSON-RPC call. Consult the Wiki API documentation for more details on what you can pass to this function. + * + * @method send + * @param {Object} args Call object where there are three field id, method and params this object should also contain callbacks etc. + */ + send: function(args) { + var ecb = args.error, + scb = args.success; + + args = extend(this.settings, args); + + args.success = function(c, x) { + c = JSON.parse(c); + + if (typeof c == 'undefined') { + c = { + error: 'JSON Parse error.' + }; + } + + if (c.error) { + ecb.call(args.error_scope || args.scope, c.error, x); + } else { + scb.call(args.success_scope || args.scope, c.result); + } + }; + + args.error = function(ty, x) { + if (ecb) { + ecb.call(args.error_scope || args.scope, ty, x); + } + }; + + args.data = JSON.serialize({ + id: args.id || 'c' + (this.count++), + method: args.method, + params: args.params + }); + + // JSON content type for Ruby on rails. Bug: #1883287 + args.content_type = 'application/json'; + + XHR.send(args); + } + }; + + return JSONRequest; + } + ); + /** + * JSONP.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.util.JSONP', [ + "tinymce.core.dom.DOMUtils" + ], + function(DOMUtils) { + return { + callbacks: {}, + count: 0, + + send: function(settings) { + var self = this, + dom = DOMUtils.DOM, + count = settings.count !== undefined ? settings.count : self.count; + var id = 'tinymce_jsonp_' + count; + + self.callbacks[count] = function(json) { + dom.remove(id); + delete self.callbacks[count]; + + settings.callback(json); + }; + + dom.add(dom.doc.body, 'script', { + id: id, + src: settings.url, + type: 'text/javascript' + }); + + self.count++; + } + }; + } + ); + /** + * LocalStorage.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class will simulate LocalStorage on IE 7 and return the native version on modern browsers. + * Storage is done using userData on IE 7 and a special serialization format. The format is designed + * to be as small as possible by making sure that the keys and values doesn't need to be encoded. This + * makes it possible to store for example HTML data. + * + * Storage format for userData: + * <base 32 key length>,<key string>,<base 32 value length>,<value>,... + * + * For example this data key1=value1,key2=value2 would be: + * 4,key1,6,value1,4,key2,6,value2 + * + * @class tinymce.util.LocalStorage + * @static + * @version 4.0 + * @example + * tinymce.util.LocalStorage.setItem('key', 'value'); + * var value = tinymce.util.LocalStorage.getItem('key'); + */ + define( + 'tinymce.core.util.LocalStorage', [], + function() { + var LocalStorage, storageElm, items, keys, userDataKey, hasOldIEDataSupport; + + // Check for native support + try { + if (window.localStorage) { + return localStorage; + } + } catch (ex) { + // Ignore + } + + userDataKey = "tinymce"; + storageElm = document.documentElement; + hasOldIEDataSupport = !!storageElm.addBehavior; + + if (hasOldIEDataSupport) { + storageElm.addBehavior('#default#userData'); + } + + /** + * Gets the keys names and updates LocalStorage.length property. Since IE7 doesn't have any getters/setters. + */ + function updateKeys() { + keys = []; + + for (var key in items) { + keys.push(key); + } + + LocalStorage.length = keys.length; + } + + /** + * Loads the userData string and parses it into the items structure. + */ + function load() { + var key, data, value, pos = 0; + + items = {}; + + // localStorage can be disabled on WebKit/Gecko so make a dummy storage + if (!hasOldIEDataSupport) { + return; + } + + function next(end) { + var value, nextPos; + + nextPos = end !== undefined ? pos + end : data.indexOf(',', pos); + if (nextPos === -1 || nextPos > data.length) { + return null; + } + + value = data.substring(pos, nextPos); + pos = nextPos + 1; + + return value; + } + + storageElm.load(userDataKey); + data = storageElm.getAttribute(userDataKey) || ''; + + do { + var offset = next(); + if (offset === null) { + break; + } + + key = next(parseInt(offset, 32) || 0); + if (key !== null) { + offset = next(); + if (offset === null) { + break; + } + + value = next(parseInt(offset, 32) || 0); + + if (key) { + items[key] = value; + } + } + } while (key !== null); + + updateKeys(); + } + + /** + * Saves the items structure into a the userData format. + */ + function save() { + var value, data = ''; + + // localStorage can be disabled on WebKit/Gecko so make a dummy storage + if (!hasOldIEDataSupport) { + return; + } + + for (var key in items) { + value = items[key]; + data += (data ? ',' : '') + key.length.toString(32) + ',' + key + ',' + value.length.toString(32) + ',' + value; + } + + storageElm.setAttribute(userDataKey, data); + + try { + storageElm.save(userDataKey); + } catch (ex) { + // Ignore disk full + } + + updateKeys(); + } + + LocalStorage = { + /** + * Length of the number of items in storage. + * + * @property length + * @type Number + * @return {Number} Number of items in storage. + */ + //length:0, + + /** + * Returns the key name by index. + * + * @method key + * @param {Number} index Index of key to return. + * @return {String} Key value or null if it wasn't found. + */ + key: function(index) { + return keys[index]; + }, + + /** + * Returns the value if the specified key or null if it wasn't found. + * + * @method getItem + * @param {String} key Key of item to retrieve. + * @return {String} Value of the specified item or null if it wasn't found. + */ + getItem: function(key) { + return key in items ? items[key] : null; + }, + + /** + * Sets the value of the specified item by it's key. + * + * @method setItem + * @param {String} key Key of the item to set. + * @param {String} value Value of the item to set. + */ + setItem: function(key, value) { + items[key] = "" + value; + save(); + }, + + /** + * Removes the specified item by key. + * + * @method removeItem + * @param {String} key Key of item to remove. + */ + removeItem: function(key) { + delete items[key]; + save(); + }, + + /** + * Removes all items. + * + * @method clear + */ + clear: function() { + items = {}; + save(); + } + }; + + load(); + + return LocalStorage; + } + ); + + /** + * Compat.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * TinyMCE core class. + * + * @static + * @class tinymce + * @borrow-members tinymce.EditorManager + * @borrow-members tinymce.util.Tools + */ + define( + 'tinymce.core.api.Compat', [ + "tinymce.core.dom.DOMUtils", + "tinymce.core.dom.EventUtils", + "tinymce.core.dom.ScriptLoader", + "tinymce.core.AddOnManager", + "tinymce.core.util.Tools", + "tinymce.core.Env" + ], + function(DOMUtils, EventUtils, ScriptLoader, AddOnManager, Tools, Env) { + var register = function(tinymce) { + /** + * @property {tinymce.dom.DOMUtils} DOM Global DOM instance. + * @property {tinymce.dom.ScriptLoader} ScriptLoader Global ScriptLoader instance. + * @property {tinymce.AddOnManager} PluginManager Global PluginManager instance. + * @property {tinymce.AddOnManager} ThemeManager Global ThemeManager instance. + */ + tinymce.DOM = DOMUtils.DOM; + tinymce.ScriptLoader = ScriptLoader.ScriptLoader; + tinymce.PluginManager = AddOnManager.PluginManager; + tinymce.ThemeManager = AddOnManager.ThemeManager; + + tinymce.dom = tinymce.dom || {}; + tinymce.dom.Event = EventUtils.Event; + + Tools.each( + 'trim isArray is toArray makeMap each map grep inArray extend create walk createNS resolve explode _addCacheSuffix'.split(' '), + function(key) { + tinymce[key] = Tools[key]; + } + ); + + Tools.each('isOpera isWebKit isIE isGecko isMac'.split(' '), function(name) { + tinymce[name] = Env[name.substr(2).toLowerCase()]; + }); + }; + + return { + register: register + }; + } + ); + + // Describe the different namespaces + + /** + * Root level namespace this contains classes directly related to the TinyMCE editor. + * + * @namespace tinymce + */ + + /** + * Contains classes for handling the browsers DOM. + * + * @namespace tinymce.dom + */ + + /** + * Contains html parser and serializer logic. + * + * @namespace tinymce.html + */ + + /** + * Contains the different UI types such as buttons, listboxes etc. + * + * @namespace tinymce.ui + */ + + /** + * Contains various utility classes such as json parser, cookies etc. + * + * @namespace tinymce.util + */ + + /** + * Contains modules to handle data binding. + * + * @namespace tinymce.data + */ + + /** + * Color.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class lets you parse/serialize colors and convert rgb/hsb. + * + * @class tinymce.util.Color + * @example + * var white = new tinymce.util.Color({r: 255, g: 255, b: 255}); + * var red = new tinymce.util.Color('#FF0000'); + * + * console.log(white.toHex(), red.toHsv()); + */ + define( + 'tinymce.core.util.Color', [], + function() { + var min = Math.min, + max = Math.max, + round = Math.round; + + /** + * Constructs a new color instance. + * + * @constructor + * @method Color + * @param {String} value Optional initial value to parse. + */ + function Color(value) { + var self = this, + r = 0, + g = 0, + b = 0; + + function rgb2hsv(r, g, b) { + var h, s, v, d, minRGB, maxRGB; + + h = 0; + s = 0; + v = 0; + r = r / 255; + g = g / 255; + b = b / 255; + + minRGB = min(r, min(g, b)); + maxRGB = max(r, max(g, b)); + + if (minRGB == maxRGB) { + v = minRGB; + + return { + h: 0, + s: 0, + v: v * 100 + }; + } + + /*eslint no-nested-ternary:0 */ + d = (r == minRGB) ? g - b : ((b == minRGB) ? r - g : b - r); + h = (r == minRGB) ? 3 : ((b == minRGB) ? 1 : 5); + h = 60 * (h - d / (maxRGB - minRGB)); + s = (maxRGB - minRGB) / maxRGB; + v = maxRGB; + + return { + h: round(h), + s: round(s * 100), + v: round(v * 100) + }; + } + + function hsvToRgb(hue, saturation, brightness) { + var side, chroma, x, match; + + hue = (parseInt(hue, 10) || 0) % 360; + saturation = parseInt(saturation, 10) / 100; + brightness = parseInt(brightness, 10) / 100; + saturation = max(0, min(saturation, 1)); + brightness = max(0, min(brightness, 1)); + + if (saturation === 0) { + r = g = b = round(255 * brightness); + return; + } + + side = hue / 60; + chroma = brightness * saturation; + x = chroma * (1 - Math.abs(side % 2 - 1)); + match = brightness - chroma; + + switch (Math.floor(side)) { + case 0: + r = chroma; + g = x; + b = 0; + break; + + case 1: + r = x; + g = chroma; + b = 0; + break; + + case 2: + r = 0; + g = chroma; + b = x; + break; + + case 3: + r = 0; + g = x; + b = chroma; + break; + + case 4: + r = x; + g = 0; + b = chroma; + break; + + case 5: + r = chroma; + g = 0; + b = x; + break; + + default: + r = g = b = 0; + } + + r = round(255 * (r + match)); + g = round(255 * (g + match)); + b = round(255 * (b + match)); + } + + /** + * Returns the hex string of the current color. For example: #ff00ff + * + * @method toHex + * @return {String} Hex string of current color. + */ + function toHex() { + function hex(val) { + val = parseInt(val, 10).toString(16); + + return val.length > 1 ? val : '0' + val; + } + + return '#' + hex(r) + hex(g) + hex(b); + } + + /** + * Returns the r, g, b values of the color. Each channel has a range from 0-255. + * + * @method toRgb + * @return {Object} Object with r, g, b fields. + */ + function toRgb() { + return { + r: r, + g: g, + b: b + }; + } + + /** + * Returns the h, s, v values of the color. Ranges: h=0-360, s=0-100, v=0-100. + * + * @method toHsv + * @return {Object} Object with h, s, v fields. + */ + function toHsv() { + return rgb2hsv(r, g, b); + } + + /** + * Parses the specified value and populates the color instance. + * + * Supported format examples: + * * rbg(255,0,0) + * * #ff0000 + * * #fff + * * {r: 255, g: 0, b: 0} + * * {h: 360, s: 100, v: 100} + * + * @method parse + * @param {Object/String} value Color value to parse. + * @return {tinymce.util.Color} Current color instance. + */ + function parse(value) { + var matches; + + if (typeof value == 'object') { + if ("r" in value) { + r = value.r; + g = value.g; + b = value.b; + } else if ("v" in value) { + hsvToRgb(value.h, value.s, value.v); + } + } else { + if ((matches = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)[^\)]*\)/gi.exec(value))) { + r = parseInt(matches[1], 10); + g = parseInt(matches[2], 10); + b = parseInt(matches[3], 10); + } else if ((matches = /#([0-F]{2})([0-F]{2})([0-F]{2})/gi.exec(value))) { + r = parseInt(matches[1], 16); + g = parseInt(matches[2], 16); + b = parseInt(matches[3], 16); + } else if ((matches = /#([0-F])([0-F])([0-F])/gi.exec(value))) { + r = parseInt(matches[1] + matches[1], 16); + g = parseInt(matches[2] + matches[2], 16); + b = parseInt(matches[3] + matches[3], 16); + } + } + + r = r < 0 ? 0 : (r > 255 ? 255 : r); + g = g < 0 ? 0 : (g > 255 ? 255 : g); + b = b < 0 ? 0 : (b > 255 ? 255 : b); + + return self; + } + + if (value) { + parse(value); + } + + self.toRgb = toRgb; + self.toHsv = toHsv; + self.toHex = toHex; + self.parse = parse; + } + + return Color; + } + ); + + /** + * Layout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Base layout manager class. + * + * @class tinymce.ui.Layout + */ + define( + 'tinymce.core.ui.Layout', [ + "tinymce.core.util.Class", + "tinymce.core.util.Tools" + ], + function(Class, Tools) { + "use strict"; + + return Class.extend({ + Defaults: { + firstControlClass: 'first', + lastControlClass: 'last' + }, + + /** + * Constructs a layout instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + this.settings = Tools.extend({}, this.Defaults, settings); + }, + + /** + * This method gets invoked before the layout renders the controls. + * + * @method preRender + * @param {tinymce.ui.Container} container Container instance to preRender. + */ + preRender: function(container) { + container.bodyClasses.add(this.settings.containerClass); + }, + + /** + * Applies layout classes to the container. + * + * @private + */ + applyClasses: function(items) { + var self = this, + settings = self.settings, + firstClass, lastClass, firstItem, lastItem; + + firstClass = settings.firstControlClass; + lastClass = settings.lastControlClass; + + items.each(function(item) { + item.classes.remove(firstClass).remove(lastClass).add(settings.controlClass); + + if (item.visible()) { + if (!firstItem) { + firstItem = item; + } + + lastItem = item; + } + }); + + if (firstItem) { + firstItem.classes.add(firstClass); + } + + if (lastItem) { + lastItem.classes.add(lastClass); + } + }, + + /** + * Renders the specified container and any layout specific HTML. + * + * @method renderHtml + * @param {tinymce.ui.Container} container Container to render HTML for. + */ + renderHtml: function(container) { + var self = this, + html = ''; + + self.applyClasses(container.items()); + + container.items().each(function(item) { + html += item.renderHtml(); + }); + + return html; + }, + + /** + * Recalculates the positions of the controls in the specified container. + * + * @method recalc + * @param {tinymce.ui.Container} container Container instance to recalc. + */ + recalc: function() {}, + + /** + * This method gets invoked after the layout renders the controls. + * + * @method postRender + * @param {tinymce.ui.Container} container Container instance to postRender. + */ + postRender: function() {}, + + isNative: function() { + return false; + } + }); + } + ); + /** + * AbsoluteLayout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * LayoutManager for absolute positioning. This layout manager is more of + * a base class for other layouts but can be created and used directly. + * + * @-x-less AbsoluteLayout.less + * @class tinymce.ui.AbsoluteLayout + * @extends tinymce.ui.Layout + */ + define( + 'tinymce.core.ui.AbsoluteLayout', [ + "tinymce.core.ui.Layout" + ], + function(Layout) { + "use strict"; + + return Layout.extend({ + Defaults: { + containerClass: 'abs-layout', + controlClass: 'abs-layout-item' + }, + + /** + * Recalculates the positions of the controls in the specified container. + * + * @method recalc + * @param {tinymce.ui.Container} container Container instance to recalc. + */ + recalc: function(container) { + container.items().filter(':visible').each(function(ctrl) { + var settings = ctrl.settings; + + ctrl.layoutRect({ + x: settings.x, + y: settings.y, + w: settings.w, + h: settings.h + }); + + if (ctrl.recalc) { + ctrl.recalc(); + } + }); + }, + + /** + * Renders the specified container and any layout specific HTML. + * + * @method renderHtml + * @param {tinymce.ui.Container} container Container to render HTML for. + */ + renderHtml: function(container) { + return '<div id="' + container._id + '-absend" class="' + container.classPrefix + 'abs-end"></div>' + this._super(container); + } + }); + } + ); + /** + * Button.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class is used to create buttons. You can create them directly or through the Factory. + * + * @example + * // Create and render a button to the body element + * tinymce.ui.Factory.create({ + * type: 'button', + * text: 'My button' + * }).renderTo(document.body); + * + * @-x-less Button.less + * @class tinymce.ui.Button + * @extends tinymce.ui.Widget + */ + define( + 'tinymce.core.ui.Button', [ + "tinymce.core.ui.Widget" + ], + function(Widget) { + "use strict"; + + return Widget.extend({ + Defaults: { + classes: "widget btn", + role: "button" + }, + + /** + * Constructs a new button instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {String} size Size of the button small|medium|large. + * @setting {String} image Image to use for icon. + * @setting {String} icon Icon to use for button. + */ + init: function(settings) { + var self = this, + size; + + self._super(settings); + settings = self.settings; + + size = self.settings.size; + + self.on('click mousedown', function(e) { + e.preventDefault(); + }); + + self.on('touchstart', function(e) { + self.fire('click', e); + e.preventDefault(); + }); + + if (settings.subtype) { + self.classes.add(settings.subtype); + } + + if (size) { + self.classes.add('btn-' + size); + } + + if (settings.icon) { + self.icon(settings.icon); + } + }, + + /** + * Sets/gets the current button icon. + * + * @method icon + * @param {String} [icon] New icon identifier. + * @return {String|tinymce.ui.MenuButton} Current icon or current MenuButton instance. + */ + icon: function(icon) { + if (!arguments.length) { + return this.state.get('icon'); + } + + this.state.set('icon', icon); + + return this; + }, + + /** + * Repaints the button for example after it's been resizes by a layout engine. + * + * @method repaint + */ + repaint: function() { + var btnElm = this.getEl().firstChild, + btnStyle; + + if (btnElm) { + btnStyle = btnElm.style; + btnStyle.width = btnStyle.height = "100%"; + } + + this._super(); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + id = self._id, + prefix = self.classPrefix; + var icon = self.state.get('icon'), + image, text = self.state.get('text'), + textHtml = ''; + + image = self.settings.image; + if (image) { + icon = 'none'; + + // Support for [high dpi, low dpi] image sources + if (typeof image != "string") { + image = window.getSelection ? image[0] : image[1]; + } + + image = ' style="background-image: url(\'' + image + '\')"'; + } else { + image = ''; + } + + if (text) { + self.classes.add('btn-has-text'); + textHtml = '<span class="' + prefix + 'txt">' + self.encode(text) + '</span>'; + } + + icon = icon ? prefix + 'ico ' + prefix + 'i-' + icon : ''; + + return ( + '<div id="' + id + '" class="' + self.classes + '" tabindex="-1">' + + '<button role="presentation" type="button" tabindex="-1">' + + (icon ? '<i class="' + icon + '"' + image + '></i>' : '') + + textHtml + + '</button>' + + '</div>' + ); + }, + + bindStates: function() { + var self = this, + $ = self.$, + textCls = self.classPrefix + 'txt'; + + function setButtonText(text) { + var $span = $('span.' + textCls, self.getEl()); + + if (text) { + if (!$span[0]) { + $('button:first', self.getEl()).append('<span class="' + textCls + '"></span>'); + $span = $('span.' + textCls, self.getEl()); + } + + $span.html(self.encode(text)); + } else { + $span.remove(); + } + + self.classes.toggle('btn-has-text', !!text); + } + + self.state.on('change:text', function(e) { + setButtonText(e.value); + }); + + self.state.on('change:icon', function(e) { + var icon = e.value, + prefix = self.classPrefix; + + self.settings.icon = icon; + icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : ''; + + var btnElm = self.getEl().firstChild, + iconElm = btnElm.getElementsByTagName('i')[0]; + + if (icon) { + if (!iconElm || iconElm != btnElm.firstChild) { + iconElm = document.createElement('i'); + btnElm.insertBefore(iconElm, btnElm.firstChild); + } + + iconElm.className = icon; + } else if (iconElm) { + btnElm.removeChild(iconElm); + } + + setButtonText(self.state.get('text')); + }); + + return self._super(); + } + }); + } + ); + + /** + * ButtonGroup.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This control enables you to put multiple buttons into a group. This is + * useful when you want to combine similar toolbar buttons into a group. + * + * @example + * // Create and render a buttongroup with two buttons to the body element + * tinymce.ui.Factory.create({ + * type: 'buttongroup', + * items: [ + * {text: 'Button A'}, + * {text: 'Button B'} + * ] + * }).renderTo(document.body); + * + * @-x-less ButtonGroup.less + * @class tinymce.ui.ButtonGroup + * @extends tinymce.ui.Container + */ + define( + 'tinymce.core.ui.ButtonGroup', [ + "tinymce.core.ui.Container" + ], + function(Container) { + "use strict"; + + return Container.extend({ + Defaults: { + defaultType: 'button', + role: 'group' + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + layout = self._layout; + + self.classes.add('btn-group'); + self.preRender(); + layout.preRender(self); + + return ( + '<div id="' + self._id + '" class="' + self.classes + '">' + + '<div id="' + self._id + '-body">' + + (self.settings.html || '') + layout.renderHtml(self) + + '</div>' + + '</div>' + ); + } + }); + } + ); + /** + * Checkbox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This control creates a custom checkbox. + * + * @example + * // Create and render a checkbox to the body element + * tinymce.ui.Factory.create({ + * type: 'checkbox', + * checked: true, + * text: 'My checkbox' + * }).renderTo(document.body); + * + * @-x-less Checkbox.less + * @class tinymce.ui.Checkbox + * @extends tinymce.ui.Widget + */ + define( + 'tinymce.core.ui.Checkbox', [ + "tinymce.core.ui.Widget" + ], + function(Widget) { + "use strict"; + + return Widget.extend({ + Defaults: { + classes: "checkbox", + role: "checkbox", + checked: false + }, + + /** + * Constructs a new Checkbox instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Boolean} checked True if the checkbox should be checked by default. + */ + init: function(settings) { + var self = this; + + self._super(settings); + + self.on('click mousedown', function(e) { + e.preventDefault(); + }); + + self.on('click', function(e) { + e.preventDefault(); + + if (!self.disabled()) { + self.checked(!self.checked()); + } + }); + + self.checked(self.settings.checked); + }, + + /** + * Getter/setter function for the checked state. + * + * @method checked + * @param {Boolean} [state] State to be set. + * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation. + */ + checked: function(state) { + if (!arguments.length) { + return this.state.get('checked'); + } + + this.state.set('checked', state); + + return this; + }, + + /** + * Getter/setter function for the value state. + * + * @method value + * @param {Boolean} [state] State to be set. + * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation. + */ + value: function(state) { + if (!arguments.length) { + return this.checked(); + } + + return this.checked(state); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + id = self._id, + prefix = self.classPrefix; + + return ( + '<div id="' + id + '" class="' + self.classes + '" unselectable="on" aria-labelledby="' + id + '-al" tabindex="-1">' + + '<i class="' + prefix + 'ico ' + prefix + 'i-checkbox"></i>' + + '<span id="' + id + '-al" class="' + prefix + 'label">' + self.encode(self.state.get('text')) + '</span>' + + '</div>' + ); + }, + + bindStates: function() { + var self = this; + + function checked(state) { + self.classes.toggle("checked", state); + self.aria('checked', state); + } + + self.state.on('change:text', function(e) { + self.getEl('al').firstChild.data = self.translate(e.value); + }); + + self.state.on('change:checked change:value', function(e) { + self.fire('change'); + checked(e.value); + }); + + self.state.on('change:icon', function(e) { + var icon = e.value, + prefix = self.classPrefix; + + if (typeof icon == 'undefined') { + return self.settings.icon; + } + + self.settings.icon = icon; + icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : ''; + + var btnElm = self.getEl().firstChild, + iconElm = btnElm.getElementsByTagName('i')[0]; + + if (icon) { + if (!iconElm || iconElm != btnElm.firstChild) { + iconElm = document.createElement('i'); + btnElm.insertBefore(iconElm, btnElm.firstChild); + } + + iconElm.className = icon; + } else if (iconElm) { + btnElm.removeChild(iconElm); + } + }); + + if (self.state.get('checked')) { + checked(true); + } + + return self._super(); + } + }); + } + ); + /** + * ComboBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class creates a combobox control. Select box that you select a value from or + * type a value into. + * + * @-x-less ComboBox.less + * @class tinymce.ui.ComboBox + * @extends tinymce.ui.Widget + */ + define( + 'tinymce.core.ui.ComboBox', [ + "tinymce.core.ui.Widget", + "tinymce.core.ui.Factory", + "tinymce.core.ui.DomUtils", + "tinymce.core.dom.DomQuery", + "tinymce.core.util.VK", + "tinymce.core.util.Tools" + ], + function(Widget, Factory, DomUtils, $, VK, Tools) { + "use strict"; + + return Widget.extend({ + /** + * Constructs a new control instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {String} placeholder Placeholder text to display. + */ + init: function(settings) { + var self = this; + + self._super(settings); + settings = self.settings; + + self.classes.add('combobox'); + self.subinput = true; + self.ariaTarget = 'inp'; // TODO: Figure out a better way + + settings.menu = settings.menu || settings.values; + + if (settings.menu) { + settings.icon = 'caret'; + } + + self.on('click', function(e) { + var elm = e.target, + root = self.getEl(); + + if (!$.contains(root, elm) && elm != root) { + return; + } + + while (elm && elm != root) { + if (elm.id && elm.id.indexOf('-open') != -1) { + self.fire('action'); + + if (settings.menu) { + self.showMenu(); + + if (e.aria) { + self.menu.items()[0].focus(); + } + } + } + + elm = elm.parentNode; + } + }); + + // TODO: Rework this + self.on('keydown', function(e) { + var rootControl; + + if (e.keyCode == 13 && e.target.nodeName === 'INPUT') { + e.preventDefault(); + + // Find root control that we can do toJSON on + self.parents().reverse().each(function(ctrl) { + if (ctrl.toJSON) { + rootControl = ctrl; + return false; + } + }); + + // Fire event on current text box with the serialized data of the whole form + self.fire('submit', { + data: rootControl.toJSON() + }); + } + }); + + self.on('keyup', function(e) { + if (e.target.nodeName == "INPUT") { + var oldValue = self.state.get('value'); + var newValue = e.target.value; + + if (newValue !== oldValue) { + self.state.set('value', newValue); + self.fire('autocomplete', e); + } + } + }); + + self.on('mouseover', function(e) { + var tooltip = self.tooltip().moveTo(-0xFFFF); + + if (self.statusLevel() && e.target.className.indexOf(self.classPrefix + 'status') !== -1) { + var statusMessage = self.statusMessage() || 'Ok'; + var rel = tooltip.text(statusMessage).show().testMoveRel(e.target, ['bc-tc', 'bc-tl', 'bc-tr']); + + tooltip.classes.toggle('tooltip-n', rel == 'bc-tc'); + tooltip.classes.toggle('tooltip-nw', rel == 'bc-tl'); + tooltip.classes.toggle('tooltip-ne', rel == 'bc-tr'); + + tooltip.moveRel(e.target, rel); + } + }); + }, + + statusLevel: function(value) { + if (arguments.length > 0) { + this.state.set('statusLevel', value); + } + + return this.state.get('statusLevel'); + }, + + statusMessage: function(value) { + if (arguments.length > 0) { + this.state.set('statusMessage', value); + } + + return this.state.get('statusMessage'); + }, + + showMenu: function() { + var self = this, + settings = self.settings, + menu; + + if (!self.menu) { + menu = settings.menu || []; + + // Is menu array then auto constuct menu control + if (menu.length) { + menu = { + type: 'menu', + items: menu + }; + } else { + menu.type = menu.type || 'menu'; + } + + self.menu = Factory.create(menu).parent(self).renderTo(self.getContainerElm()); + self.fire('createmenu'); + self.menu.reflow(); + self.menu.on('cancel', function(e) { + if (e.control === self.menu) { + self.focus(); + } + }); + + self.menu.on('show hide', function(e) { + e.control.items().each(function(ctrl) { + ctrl.active(ctrl.value() == self.value()); + }); + }).fire('show'); + + self.menu.on('select', function(e) { + self.value(e.control.value()); + }); + + self.on('focusin', function(e) { + if (e.target.tagName.toUpperCase() == 'INPUT') { + self.menu.hide(); + } + }); + + self.aria('expanded', true); + } + + self.menu.show(); + self.menu.layoutRect({ + w: self.layoutRect().w + }); + self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']); + }, + + /** + * Focuses the input area of the control. + * + * @method focus + */ + focus: function() { + this.getEl('inp').focus(); + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this, + elm = self.getEl(), + openElm = self.getEl('open'), + rect = self.layoutRect(); + var width, lineHeight, innerPadding = 0, + inputElm = elm.firstChild; + + if (self.statusLevel() && self.statusLevel() !== 'none') { + innerPadding = ( + parseInt(DomUtils.getRuntimeStyle(inputElm, 'padding-right'), 10) - + parseInt(DomUtils.getRuntimeStyle(inputElm, 'padding-left'), 10) + ); + } + + if (openElm) { + width = rect.w - DomUtils.getSize(openElm).width - 10; + } else { + width = rect.w - 10; + } + + // Detect old IE 7+8 add lineHeight to align caret vertically in the middle + var doc = document; + if (doc.all && (!doc.documentMode || doc.documentMode <= 8)) { + lineHeight = (self.layoutRect().h - 2) + 'px'; + } + + $(inputElm).css({ + width: width - innerPadding, + lineHeight: lineHeight + }); + + self._super(); + + return self; + }, + + /** + * Post render method. Called after the control has been rendered to the target. + * + * @method postRender + * @return {tinymce.ui.ComboBox} Current combobox instance. + */ + postRender: function() { + var self = this; + + $(this.getEl('inp')).on('change', function(e) { + self.state.set('value', e.target.value); + self.fire('change', e); + }); + + return self._super(); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + id = self._id, + settings = self.settings, + prefix = self.classPrefix; + var value = self.state.get('value') || ''; + var icon, text, openBtnHtml = '', + extraAttrs = '', + statusHtml = ''; + + if ("spellcheck" in settings) { + extraAttrs += ' spellcheck="' + settings.spellcheck + '"'; + } + + if (settings.maxLength) { + extraAttrs += ' maxlength="' + settings.maxLength + '"'; + } + + if (settings.size) { + extraAttrs += ' size="' + settings.size + '"'; + } + + if (settings.subtype) { + extraAttrs += ' type="' + settings.subtype + '"'; + } + + statusHtml = '<i id="' + id + '-status" class="mce-status mce-ico" style="display: none"></i>'; + + if (self.disabled()) { + extraAttrs += ' disabled="disabled"'; + } + + icon = settings.icon; + if (icon && icon != 'caret') { + icon = prefix + 'ico ' + prefix + 'i-' + settings.icon; + } + + text = self.state.get('text'); + + if (icon || text) { + openBtnHtml = ( + '<div id="' + id + '-open" class="' + prefix + 'btn ' + prefix + 'open" tabIndex="-1" role="button">' + + '<button id="' + id + '-action" type="button" hidefocus="1" tabindex="-1">' + + (icon != 'caret' ? '<i class="' + icon + '"></i>' : '<i class="' + prefix + 'caret"></i>') + + (text ? (icon ? ' ' : '') + text : '') + + '</button>' + + '</div>' + ); + + self.classes.add('has-open'); + } + + return ( + '<div id="' + id + '" class="' + self.classes + '">' + + '<input id="' + id + '-inp" class="' + prefix + 'textbox" value="' + + self.encode(value, false) + '" hidefocus="1"' + extraAttrs + ' placeholder="' + + self.encode(settings.placeholder) + '" />' + + statusHtml + + openBtnHtml + + '</div>' + ); + }, + + value: function(value) { + if (arguments.length) { + this.state.set('value', value); + return this; + } + + // Make sure the real state is in sync + if (this.state.get('rendered')) { + this.state.set('value', this.getEl('inp').value); + } + + return this.state.get('value'); + }, + + showAutoComplete: function(items, term) { + var self = this; + + if (items.length === 0) { + self.hideMenu(); + return; + } + + var insert = function(value, title) { + return function() { + self.fire('selectitem', { + title: title, + value: value + }); + }; + }; + + if (self.menu) { + self.menu.items().remove(); + } else { + self.menu = Factory.create({ + type: 'menu', + classes: 'combobox-menu', + layout: 'flow' + }).parent(self).renderTo(); + } + + Tools.each(items, function(item) { + self.menu.add({ + text: item.title, + url: item.previewUrl, + match: term, + classes: 'menu-item-ellipsis', + onclick: insert(item.value, item.title) + }); + }); + + self.menu.renderNew(); + self.hideMenu(); + + self.menu.on('cancel', function(e) { + if (e.control.parent() === self.menu) { + e.stopPropagation(); + self.focus(); + self.hideMenu(); + } + }); + + self.menu.on('select', function() { + self.focus(); + }); + + var maxW = self.layoutRect().w; + self.menu.layoutRect({ + w: maxW, + minW: 0, + maxW: maxW + }); + self.menu.reflow(); + self.menu.show(); + self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']); + }, + + hideMenu: function() { + if (this.menu) { + this.menu.hide(); + } + }, + + bindStates: function() { + var self = this; + + self.state.on('change:value', function(e) { + if (self.getEl('inp').value != e.value) { + self.getEl('inp').value = e.value; + } + }); + + self.state.on('change:disabled', function(e) { + self.getEl('inp').disabled = e.value; + }); + + self.state.on('change:statusLevel', function(e) { + var statusIconElm = self.getEl('status'); + var prefix = self.classPrefix, + value = e.value; + + DomUtils.css(statusIconElm, 'display', value === 'none' ? 'none' : ''); + DomUtils.toggleClass(statusIconElm, prefix + 'i-checkmark', value === 'ok'); + DomUtils.toggleClass(statusIconElm, prefix + 'i-warning', value === 'warn'); + DomUtils.toggleClass(statusIconElm, prefix + 'i-error', value === 'error'); + self.classes.toggle('has-status', value !== 'none'); + self.repaint(); + }); + + DomUtils.on(self.getEl('status'), 'mouseleave', function() { + self.tooltip().hide(); + }); + + self.on('cancel', function(e) { + if (self.menu && self.menu.visible()) { + e.stopPropagation(); + self.hideMenu(); + } + }); + + var focusIdx = function(idx, menu) { + if (menu && menu.items().length > 0) { + menu.items().eq(idx)[0].focus(); + } + }; + + self.on('keydown', function(e) { + var keyCode = e.keyCode; + + if (e.target.nodeName === 'INPUT') { + if (keyCode === VK.DOWN) { + e.preventDefault(); + self.fire('autocomplete'); + focusIdx(0, self.menu); + } else if (keyCode === VK.UP) { + e.preventDefault(); + focusIdx(-1, self.menu); + } + } + }); + + return self._super(); + }, + + remove: function() { + $(this.getEl('inp')).off(); + + if (this.menu) { + this.menu.remove(); + } + + this._super(); + } + }); + } + ); + /** + * ColorBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This widget lets you enter colors and browse for colors by pressing the color button. It also displays + * a preview of the current color. + * + * @-x-less ColorBox.less + * @class tinymce.ui.ColorBox + * @extends tinymce.ui.ComboBox + */ + define( + 'tinymce.core.ui.ColorBox', [ + "tinymce.core.ui.ComboBox" + ], + function(ComboBox) { + "use strict"; + + return ComboBox.extend({ + /** + * Constructs a new control instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + var self = this; + + settings.spellcheck = false; + + if (settings.onaction) { + settings.icon = 'none'; + } + + self._super(settings); + + self.classes.add('colorbox'); + self.on('change keyup postrender', function() { + self.repaintColor(self.value()); + }); + }, + + repaintColor: function(value) { + var openElm = this.getEl('open'); + var elm = openElm ? openElm.getElementsByTagName('i')[0] : null; + + if (elm) { + try { + elm.style.background = value; + } catch (ex) { + // Ignore + } + } + }, + + bindStates: function() { + var self = this; + + self.state.on('change:value', function(e) { + if (self.state.get('rendered')) { + self.repaintColor(e.value); + } + }); + + return self._super(); + } + }); + } + ); + /** + * PanelButton.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a new panel button. + * + * @class tinymce.ui.PanelButton + * @extends tinymce.ui.Button + */ + define( + 'tinymce.core.ui.PanelButton', [ + "tinymce.core.ui.Button", + "tinymce.core.ui.FloatPanel" + ], + function(Button, FloatPanel) { + "use strict"; + + return Button.extend({ + /** + * Shows the panel for the button. + * + * @method showPanel + */ + showPanel: function() { + var self = this, + settings = self.settings; + + self.active(true); + + if (!self.panel) { + var panelSettings = settings.panel; + + // Wrap panel in grid layout if type if specified + // This makes it possible to add forms or other containers directly in the panel option + if (panelSettings.type) { + panelSettings = { + layout: 'grid', + items: panelSettings + }; + } + + panelSettings.role = panelSettings.role || 'dialog'; + panelSettings.popover = true; + panelSettings.autohide = true; + panelSettings.ariaRoot = true; + + self.panel = new FloatPanel(panelSettings).on('hide', function() { + self.active(false); + }).on('cancel', function(e) { + e.stopPropagation(); + self.focus(); + self.hidePanel(); + }).parent(self).renderTo(self.getContainerElm()); + + self.panel.fire('show'); + self.panel.reflow(); + } else { + self.panel.show(); + } + + var rel = self.panel.testMoveRel(self.getEl(), settings.popoverAlign || (self.isRtl() ? ['bc-tc', 'bc-tl', 'bc-tr'] : ['bc-tc', 'bc-tr', 'bc-tl'])); + + self.panel.classes.toggle('start', rel === 'bc-tl'); + self.panel.classes.toggle('end', rel === 'bc-tr'); + + self.panel.moveRel(self.getEl(), rel); + }, + + /** + * Hides the panel for the button. + * + * @method hidePanel + */ + hidePanel: function() { + var self = this; + + if (self.panel) { + self.panel.hide(); + } + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this; + + self.aria('haspopup', true); + + self.on('click', function(e) { + if (e.control === self) { + if (self.panel && self.panel.visible()) { + self.hidePanel(); + } else { + self.showPanel(); + self.panel.focus(!!e.aria); + } + } + }); + + return self._super(); + }, + + remove: function() { + if (this.panel) { + this.panel.remove(); + this.panel = null; + } + + return this._super(); + } + }); + } + ); + /** + * ColorButton.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class creates a color button control. This is a split button in which the main + * button has a visual representation of the currently selected color. When clicked + * the caret button displays a color picker, allowing the user to select a new color. + * + * @-x-less ColorButton.less + * @class tinymce.ui.ColorButton + * @extends tinymce.ui.PanelButton + */ + define( + 'tinymce.core.ui.ColorButton', [ + "tinymce.core.ui.PanelButton", + "tinymce.core.dom.DOMUtils" + ], + function(PanelButton, DomUtils) { + "use strict"; + + var DOM = DomUtils.DOM; + + return PanelButton.extend({ + /** + * Constructs a new ColorButton instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + this._super(settings); + this.classes.add('colorbutton'); + }, + + /** + * Getter/setter for the current color. + * + * @method color + * @param {String} [color] Color to set. + * @return {String|tinymce.ui.ColorButton} Current color or current instance. + */ + color: function(color) { + if (color) { + this._color = color; + this.getEl('preview').style.backgroundColor = color; + return this; + } + + return this._color; + }, + + /** + * Resets the current color. + * + * @method resetColor + * @return {tinymce.ui.ColorButton} Current instance. + */ + resetColor: function() { + this._color = null; + this.getEl('preview').style.backgroundColor = null; + return this; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + id = self._id, + prefix = self.classPrefix, + text = self.state.get('text'); + var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : ''; + var image = self.settings.image ? ' style="background-image: url(\'' + self.settings.image + '\')"' : '', + textHtml = ''; + + if (text) { + self.classes.add('btn-has-text'); + textHtml = '<span class="' + prefix + 'txt">' + self.encode(text) + '</span>'; + } + + return ( + '<div id="' + id + '" class="' + self.classes + '" role="button" tabindex="-1" aria-haspopup="true">' + + '<button role="presentation" hidefocus="1" type="button" tabindex="-1">' + + (icon ? '<i class="' + icon + '"' + image + '></i>' : '') + + '<span id="' + id + '-preview" class="' + prefix + 'preview"></span>' + + textHtml + + '</button>' + + '<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' + + ' <i class="' + prefix + 'caret"></i>' + + '</button>' + + '</div>' + ); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this, + onClickHandler = self.settings.onclick; + + self.on('click', function(e) { + if (e.aria && e.aria.key == 'down') { + return; + } + + if (e.control == self && !DOM.getParent(e.target, '.' + self.classPrefix + 'open')) { + e.stopImmediatePropagation(); + onClickHandler.call(self, e); + } + }); + + delete self.settings.onclick; + + return self._super(); + } + }); + } + ); + + /** + * ColorPicker.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Color picker widget lets you select colors. + * + * @-x-less ColorPicker.less + * @class tinymce.ui.ColorPicker + * @extends tinymce.ui.Widget + */ + define( + 'tinymce.core.ui.ColorPicker', [ + "tinymce.core.ui.Widget", + "tinymce.core.ui.DragHelper", + "tinymce.core.ui.DomUtils", + "tinymce.core.util.Color" + ], + function(Widget, DragHelper, DomUtils, Color) { + "use strict"; + + return Widget.extend({ + Defaults: { + classes: "widget colorpicker" + }, + + /** + * Constructs a new colorpicker instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {String} color Initial color value. + */ + init: function(settings) { + this._super(settings); + }, + + postRender: function() { + var self = this, + color = self.color(), + hsv, hueRootElm, huePointElm, svRootElm, svPointElm; + + hueRootElm = self.getEl('h'); + huePointElm = self.getEl('hp'); + svRootElm = self.getEl('sv'); + svPointElm = self.getEl('svp'); + + function getPos(elm, event) { + var pos = DomUtils.getPos(elm), + x, y; + + x = event.pageX - pos.x; + y = event.pageY - pos.y; + + x = Math.max(0, Math.min(x / elm.clientWidth, 1)); + y = Math.max(0, Math.min(y / elm.clientHeight, 1)); + + return { + x: x, + y: y + }; + } + + function updateColor(hsv, hueUpdate) { + var hue = (360 - hsv.h) / 360; + + DomUtils.css(huePointElm, { + top: (hue * 100) + '%' + }); + + if (!hueUpdate) { + DomUtils.css(svPointElm, { + left: hsv.s + '%', + top: (100 - hsv.v) + '%' + }); + } + + svRootElm.style.background = new Color({ + s: 100, + v: 100, + h: hsv.h + }).toHex(); + self.color().parse({ + s: hsv.s, + v: hsv.v, + h: hsv.h + }); + } + + function updateSaturationAndValue(e) { + var pos; + + pos = getPos(svRootElm, e); + hsv.s = pos.x * 100; + hsv.v = (1 - pos.y) * 100; + + updateColor(hsv); + self.fire('change'); + } + + function updateHue(e) { + var pos; + + pos = getPos(hueRootElm, e); + hsv = color.toHsv(); + hsv.h = (1 - pos.y) * 360; + updateColor(hsv, true); + self.fire('change'); + } + + self._repaint = function() { + hsv = color.toHsv(); + updateColor(hsv); + }; + + self._super(); + + self._svdraghelper = new DragHelper(self._id + '-sv', { + start: updateSaturationAndValue, + drag: updateSaturationAndValue + }); + + self._hdraghelper = new DragHelper(self._id + '-h', { + start: updateHue, + drag: updateHue + }); + + self._repaint(); + }, + + rgb: function() { + return this.color().toRgb(); + }, + + value: function(value) { + var self = this; + + if (arguments.length) { + self.color().parse(value); + + if (self._rendered) { + self._repaint(); + } + } else { + return self.color().toHex(); + } + }, + + color: function() { + if (!this._color) { + this._color = new Color(); + } + + return this._color; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + id = self._id, + prefix = self.classPrefix, + hueHtml; + var stops = '#ff0000,#ff0080,#ff00ff,#8000ff,#0000ff,#0080ff,#00ffff,#00ff80,#00ff00,#80ff00,#ffff00,#ff8000,#ff0000'; + + function getOldIeFallbackHtml() { + var i, l, html = '', + gradientPrefix, stopsList; + + gradientPrefix = 'filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='; + stopsList = stops.split(','); + for (i = 0, l = stopsList.length - 1; i < l; i++) { + html += ( + '<div class="' + prefix + 'colorpicker-h-chunk" style="' + + 'height:' + (100 / l) + '%;' + + gradientPrefix + stopsList[i] + ',endColorstr=' + stopsList[i + 1] + ');' + + '-ms-' + gradientPrefix + stopsList[i] + ',endColorstr=' + stopsList[i + 1] + ')' + + '"></div>' + ); + } + + return html; + } + + var gradientCssText = ( + 'background: -ms-linear-gradient(top,' + stops + ');' + + 'background: linear-gradient(to bottom,' + stops + ');' + ); + + hueHtml = ( + '<div id="' + id + '-h" class="' + prefix + 'colorpicker-h" style="' + gradientCssText + '">' + + getOldIeFallbackHtml() + + '<div id="' + id + '-hp" class="' + prefix + 'colorpicker-h-marker"></div>' + + '</div>' + ); + + return ( + '<div id="' + id + '" class="' + self.classes + '">' + + '<div id="' + id + '-sv" class="' + prefix + 'colorpicker-sv">' + + '<div class="' + prefix + 'colorpicker-overlay1">' + + '<div class="' + prefix + 'colorpicker-overlay2">' + + '<div id="' + id + '-svp" class="' + prefix + 'colorpicker-selector1">' + + '<div class="' + prefix + 'colorpicker-selector2"></div>' + + '</div>' + + '</div>' + + '</div>' + + '</div>' + + hueHtml + + '</div>' + ); + } + }); + } + ); + /** + * Path.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a new path control. + * + * @-x-less Path.less + * @class tinymce.ui.Path + * @extends tinymce.ui.Widget + */ + define( + 'tinymce.core.ui.Path', [ + "tinymce.core.ui.Widget" + ], + function(Widget) { + "use strict"; + + return Widget.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {String} delimiter Delimiter to display between row in path. + */ + init: function(settings) { + var self = this; + + if (!settings.delimiter) { + settings.delimiter = '\u00BB'; + } + + self._super(settings); + self.classes.add('path'); + self.canFocus = true; + + self.on('click', function(e) { + var index, target = e.target; + + if ((index = target.getAttribute('data-index'))) { + self.fire('select', { + value: self.row()[index], + index: index + }); + } + }); + + self.row(self.settings.row); + }, + + /** + * Focuses the current control. + * + * @method focus + * @return {tinymce.ui.Control} Current control instance. + */ + focus: function() { + var self = this; + + self.getEl().firstChild.focus(); + + return self; + }, + + /** + * Sets/gets the data to be used for the path. + * + * @method row + * @param {Array} row Array with row name is rendered to path. + */ + row: function(row) { + if (!arguments.length) { + return this.state.get('row'); + } + + this.state.set('row', row); + + return this; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this; + + return ( + '<div id="' + self._id + '" class="' + self.classes + '">' + + self._getDataPathHtml(self.state.get('row')) + + '</div>' + ); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:row', function(e) { + self.innerHtml(self._getDataPathHtml(e.value)); + }); + + return self._super(); + }, + + _getDataPathHtml: function(data) { + var self = this, + parts = data || [], + i, l, html = '', + prefix = self.classPrefix; + + for (i = 0, l = parts.length; i < l; i++) { + html += ( + (i > 0 ? '<div class="' + prefix + 'divider" aria-hidden="true"> ' + self.settings.delimiter + ' </div>' : '') + + '<div role="button" class="' + prefix + 'path-item' + (i == l - 1 ? ' ' + prefix + 'last' : '') + '" data-index="' + + i + '" tabindex="-1" id="' + self._id + '-' + i + '" aria-level="' + (i + 1) + '">' + parts[i].name + '</div>' + ); + } + + if (!html) { + html = '<div class="' + prefix + 'path-item">\u00a0</div>'; + } + + return html; + } + }); + } + ); + + /** + * ElementPath.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This control creates an path for the current selections parent elements in TinyMCE. + * + * @class tinymce.ui.ElementPath + * @extends tinymce.ui.Path + */ + define( + 'tinymce.core.ui.ElementPath', [ + "tinymce.core.ui.Path" + ], + function(Path) { + return Path.extend({ + /** + * Post render method. Called after the control has been rendered to the target. + * + * @method postRender + * @return {tinymce.ui.ElementPath} Current combobox instance. + */ + postRender: function() { + var self = this, + editor = self.settings.editor; + + function isHidden(elm) { + if (elm.nodeType === 1) { + if (elm.nodeName == "BR" || !!elm.getAttribute('data-mce-bogus')) { + return true; + } + + if (elm.getAttribute('data-mce-type') === 'bookmark') { + return true; + } + } + + return false; + } + + if (editor.settings.elementpath !== false) { + self.on('select', function(e) { + editor.focus(); + editor.selection.select(this.row()[e.index].element); + editor.nodeChanged(); + }); + + editor.on('nodeChange', function(e) { + var outParents = [], + parents = e.parents, + i = parents.length; + + while (i--) { + if (parents[i].nodeType == 1 && !isHidden(parents[i])) { + var args = editor.fire('ResolveName', { + name: parents[i].nodeName.toLowerCase(), + target: parents[i] + }); + + if (!args.isDefaultPrevented()) { + outParents.push({ + name: args.name, + element: parents[i] + }); + } + + if (args.isPropagationStopped()) { + break; + } + } + } + + self.row(outParents); + }); + } + + return self._super(); + } + }); + } + ); + /** + * FormItem.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class is a container created by the form element with + * a label and control item. + * + * @class tinymce.ui.FormItem + * @extends tinymce.ui.Container + * @setting {String} label Label to display for the form item. + */ + define( + 'tinymce.core.ui.FormItem', [ + "tinymce.core.ui.Container" + ], + function(Container) { + "use strict"; + + return Container.extend({ + Defaults: { + layout: 'flex', + align: 'center', + defaults: { + flex: 1 + } + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + layout = self._layout, + prefix = self.classPrefix; + + self.classes.add('formitem'); + layout.preRender(self); + + return ( + '<div id="' + self._id + '" class="' + self.classes + '" hidefocus="1" tabindex="-1">' + + (self.settings.title ? ('<div id="' + self._id + '-title" class="' + prefix + 'title">' + + self.settings.title + '</div>') : '') + + '<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' + + (self.settings.html || '') + layout.renderHtml(self) + + '</div>' + + '</div>' + ); + } + }); + } + ); + /** + * Form.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class creates a form container. A form container has the ability + * to automatically wrap items in tinymce.ui.FormItem instances. + * + * Each FormItem instance is a container for the label and the item. + * + * @example + * tinymce.ui.Factory.create({ + * type: 'form', + * items: [ + * {type: 'textbox', label: 'My text box'} + * ] + * }).renderTo(document.body); + * + * @class tinymce.ui.Form + * @extends tinymce.ui.Container + */ + define( + 'tinymce.core.ui.Form', [ + "tinymce.core.ui.Container", + "tinymce.core.ui.FormItem", + "tinymce.core.util.Tools" + ], + function(Container, FormItem, Tools) { + "use strict"; + + return Container.extend({ + Defaults: { + containerCls: 'form', + layout: 'flex', + direction: 'column', + align: 'stretch', + flex: 1, + padding: 20, + labelGap: 30, + spacing: 10, + callbacks: { + submit: function() { + this.submit(); + } + } + }, + + /** + * This method gets invoked before the control is rendered. + * + * @method preRender + */ + preRender: function() { + var self = this, + items = self.items(); + + if (!self.settings.formItemDefaults) { + self.settings.formItemDefaults = { + layout: 'flex', + autoResize: "overflow", + defaults: { + flex: 1 + } + }; + } + + // Wrap any labeled items in FormItems + items.each(function(ctrl) { + var formItem, label = ctrl.settings.label; + + if (label) { + formItem = new FormItem(Tools.extend({ + items: { + type: 'label', + id: ctrl._id + '-l', + text: label, + flex: 0, + forId: ctrl._id, + disabled: ctrl.disabled() + } + }, self.settings.formItemDefaults)); + + formItem.type = 'formitem'; + ctrl.aria('labelledby', ctrl._id + '-l'); + + if (typeof ctrl.settings.flex == "undefined") { + ctrl.settings.flex = 1; + } + + self.replace(ctrl, formItem); + formItem.add(ctrl); + } + }); + }, + + /** + * Fires a submit event with the serialized form. + * + * @method submit + * @return {Object} Event arguments object. + */ + submit: function() { + return this.fire('submit', { + data: this.toJSON() + }); + }, + + /** + * Post render method. Called after the control has been rendered to the target. + * + * @method postRender + * @return {tinymce.ui.ComboBox} Current combobox instance. + */ + postRender: function() { + var self = this; + + self._super(); + self.fromJSON(self.settings.data); + }, + + bindStates: function() { + var self = this; + + self._super(); + + function recalcLabels() { + var maxLabelWidth = 0, + labels = [], + i, labelGap, items; + + if (self.settings.labelGapCalc === false) { + return; + } + + if (self.settings.labelGapCalc == "children") { + items = self.find('formitem'); + } else { + items = self.items(); + } + + items.filter('formitem').each(function(item) { + var labelCtrl = item.items()[0], + labelWidth = labelCtrl.getEl().clientWidth; + + maxLabelWidth = labelWidth > maxLabelWidth ? labelWidth : maxLabelWidth; + labels.push(labelCtrl); + }); + + labelGap = self.settings.labelGap || 0; + + i = labels.length; + while (i--) { + labels[i].settings.minWidth = maxLabelWidth + labelGap; + } + } + + self.on('show', recalcLabels); + recalcLabels(); + } + }); + } + ); + /** + * FieldSet.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class creates fieldset containers. + * + * @-x-less FieldSet.less + * @class tinymce.ui.FieldSet + * @extends tinymce.ui.Form + */ + define( + 'tinymce.core.ui.FieldSet', [ + "tinymce.core.ui.Form" + ], + function(Form) { + "use strict"; + + return Form.extend({ + Defaults: { + containerCls: 'fieldset', + layout: 'flex', + direction: 'column', + align: 'stretch', + flex: 1, + padding: "25 15 5 15", + labelGap: 30, + spacing: 10, + border: 1 + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + layout = self._layout, + prefix = self.classPrefix; + + self.preRender(); + layout.preRender(self); + + return ( + '<fieldset id="' + self._id + '" class="' + self.classes + '" hidefocus="1" tabindex="-1">' + + (self.settings.title ? ('<legend id="' + self._id + '-title" class="' + prefix + 'fieldset-title">' + + self.settings.title + '</legend>') : '') + + '<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' + + (self.settings.html || '') + layout.renderHtml(self) + + '</div>' + + '</fieldset>' + ); + } + }); + } + ); + /** + * LinkTargets.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This module is enables you to get anything that you can link to in a element. + * + * @private + * @class tinymce.content.LinkTargets + */ + define( + 'tinymce.core.content.LinkTargets', [ + 'tinymce.core.dom.DOMUtils', + 'tinymce.core.dom.NodeType', + 'tinymce.core.util.Arr', + 'tinymce.core.util.Fun', + 'tinymce.core.util.Tools', + 'tinymce.core.util.Uuid' + ], + function(DOMUtils, NodeType, Arr, Fun, Tools, Uuid) { + var trim = Tools.trim; + + var create = function(type, title, url, level, attach) { + return { + type: type, + title: title, + url: url, + level: level, + attach: attach + }; + }; + + var isChildOfContentEditableTrue = function(node) { + while ((node = node.parentNode)) { + var value = node.contentEditable; + if (value && value !== 'inherit') { + return NodeType.isContentEditableTrue(node); + } + } + + return false; + }; + + var select = function(selector, root) { + return DOMUtils.DOM.select(selector, root); + }; + + var getElementText = function(elm) { + return elm.innerText || elm.textContent; + }; + + var getOrGenerateId = function(elm) { + return elm.id ? elm.id : Uuid.uuid('h'); + }; + + var isAnchor = function(elm) { + return elm && elm.nodeName === 'A' && (elm.id || elm.name); + }; + + var isValidAnchor = function(elm) { + return isAnchor(elm) && isEditable(elm); + }; + + var isHeader = function(elm) { + return elm && /^(H[1-6])$/.test(elm.nodeName); + }; + + var isEditable = function(elm) { + return isChildOfContentEditableTrue(elm) && !NodeType.isContentEditableFalse(elm); + }; + + var isValidHeader = function(elm) { + return isHeader(elm) && isEditable(elm); + }; + + var getLevel = function(elm) { + return isHeader(elm) ? parseInt(elm.nodeName.substr(1), 10) : 0; + }; + + var headerTarget = function(elm) { + var headerId = getOrGenerateId(elm); + + var attach = function() { + elm.id = headerId; + }; + + return create('header', getElementText(elm), '#' + headerId, getLevel(elm), attach); + }; + + var anchorTarget = function(elm) { + var anchorId = elm.id || elm.name; + var anchorText = getElementText(elm); + + return create('anchor', anchorText ? anchorText : '#' + anchorId, '#' + anchorId, 0, Fun.noop); + }; + + var getHeaderTargets = function(elms) { + return Arr.map(Arr.filter(elms, isValidHeader), headerTarget); + }; + + var getAnchorTargets = function(elms) { + return Arr.map(Arr.filter(elms, isValidAnchor), anchorTarget); + }; + + var getTargetElements = function(elm) { + var elms = select('h1,h2,h3,h4,h5,h6,a:not([href])', elm); + return elms; + }; + + var hasTitle = function(target) { + return trim(target.title).length > 0; + }; + + var find = function(elm) { + var elms = getTargetElements(elm); + return Arr.filter(getHeaderTargets(elms).concat(getAnchorTargets(elms)), hasTitle); + }; + + return { + find: find + }; + } + ); + + /** + * FilePicker.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class creates a file picker control. + * + * @class tinymce.ui.FilePicker + * @extends tinymce.ui.ComboBox + */ + define( + 'tinymce.core.ui.FilePicker', [ + 'global!window', + 'tinymce.core.content.LinkTargets', + 'tinymce.core.EditorManager', + 'tinymce.core.ui.ComboBox', + 'tinymce.core.util.Arr', + 'tinymce.core.util.Fun', + 'tinymce.core.util.Tools' + ], + function(window, LinkTargets, EditorManager, ComboBox, Arr, Fun, Tools) { + "use strict"; + + var getActiveEditor = function() { + return window.tinymce ? window.tinymce.activeEditor : EditorManager.activeEditor; + }; + + var history = {}; + var HISTORY_LENGTH = 5; + + var toMenuItem = function(target) { + return { + title: target.title, + value: { + title: { + raw: target.title + }, + url: target.url, + attach: target.attach + } + }; + }; + + var toMenuItems = function(targets) { + return Tools.map(targets, toMenuItem); + }; + + var staticMenuItem = function(title, url) { + return { + title: title, + value: { + title: title, + url: url, + attach: Fun.noop + } + }; + }; + + var isUniqueUrl = function(url, targets) { + var foundTarget = Arr.find(targets, function(target) { + return target.url === url; + }); + + return !foundTarget; + }; + + var getSetting = function(editorSettings, name, defaultValue) { + var value = name in editorSettings ? editorSettings[name] : defaultValue; + return value === false ? null : value; + }; + + var createMenuItems = function(term, targets, fileType, editorSettings) { + var separator = { + title: '-' + }; + + var fromHistoryMenuItems = function(history) { + var uniqueHistory = Arr.filter(history[fileType], function(url) { + return isUniqueUrl(url, targets); + }); + + return Tools.map(uniqueHistory, function(url) { + return { + title: url, + value: { + title: url, + url: url, + attach: Fun.noop + } + }; + }); + }; + + var fromMenuItems = function(type) { + var filteredTargets = Arr.filter(targets, function(target) { + return target.type == type; + }); + + return toMenuItems(filteredTargets); + }; + + var anchorMenuItems = function() { + var anchorMenuItems = fromMenuItems('anchor'); + var topAnchor = getSetting(editorSettings, 'anchor_top', '#top'); + var bottomAchor = getSetting(editorSettings, 'anchor_bottom', '#bottom'); + + if (topAnchor !== null) { + anchorMenuItems.unshift(staticMenuItem('<top>', topAnchor)); + } + + if (bottomAchor !== null) { + anchorMenuItems.push(staticMenuItem('<bottom>', bottomAchor)); + } + + return anchorMenuItems; + }; + + var join = function(items) { + return Arr.reduce(items, function(a, b) { + var bothEmpty = a.length === 0 || b.length === 0; + return bothEmpty ? a.concat(b) : a.concat(separator, b); + }, []); + }; + + if (editorSettings.typeahead_urls === false) { + return []; + } + + return fileType === 'file' ? join([ + filterByQuery(term, fromHistoryMenuItems(history)), + filterByQuery(term, fromMenuItems('header')), + filterByQuery(term, anchorMenuItems()) + ]) : filterByQuery(term, fromHistoryMenuItems(history)); + }; + + var addToHistory = function(url, fileType) { + var items = history[fileType]; + + if (!/^https?/.test(url)) { + return; + } + + if (items) { + if (Arr.indexOf(items, url) === -1) { + history[fileType] = items.slice(0, HISTORY_LENGTH).concat(url); + } + } else { + history[fileType] = [url]; + } + }; + + var filterByQuery = function(term, menuItems) { + var lowerCaseTerm = term.toLowerCase(); + var result = Tools.grep(menuItems, function(item) { + return item.title.toLowerCase().indexOf(lowerCaseTerm) !== -1; + }); + + return result.length === 1 && result[0].title === term ? [] : result; + }; + + var getTitle = function(linkDetails) { + var title = linkDetails.title; + return title.raw ? title.raw : title; + }; + + var setupAutoCompleteHandler = function(ctrl, editorSettings, bodyElm, fileType) { + var autocomplete = function(term) { + var linkTargets = LinkTargets.find(bodyElm); + var menuItems = createMenuItems(term, linkTargets, fileType, editorSettings); + ctrl.showAutoComplete(menuItems, term); + }; + + ctrl.on('autocomplete', function() { + autocomplete(ctrl.value()); + }); + + ctrl.on('selectitem', function(e) { + var linkDetails = e.value; + + ctrl.value(linkDetails.url); + var title = getTitle(linkDetails); + + if (fileType === 'image') { + ctrl.fire('change', { + meta: { + alt: title, + attach: linkDetails.attach + } + }); + } else { + ctrl.fire('change', { + meta: { + text: title, + attach: linkDetails.attach + } + }); + } + + ctrl.focus(); + }); + + ctrl.on('click', function(e) { + if (ctrl.value().length === 0 && e.target.nodeName === 'INPUT') { + autocomplete(''); + } + }); + + ctrl.on('PostRender', function() { + ctrl.getRoot().on('submit', function(e) { + if (!e.isDefaultPrevented()) { + addToHistory(ctrl.value(), fileType); + } + }); + }); + }; + + var statusToUiState = function(result) { + var status = result.status, + message = result.message; + + if (status === 'valid') { + return { + status: 'ok', + message: message + }; + } else if (status === 'unknown') { + return { + status: 'warn', + message: message + }; + } else if (status === 'invalid') { + return { + status: 'warn', + message: message + }; + } else { + return { + status: 'none', + message: '' + }; + } + }; + + var setupLinkValidatorHandler = function(ctrl, editorSettings, fileType) { + var validatorHandler = editorSettings.filepicker_validator_handler; + if (validatorHandler) { + var validateUrl = function(url) { + if (url.length === 0) { + ctrl.statusLevel('none'); + return; + } + + validatorHandler({ + url: url, + type: fileType + }, function(result) { + var uiState = statusToUiState(result); + + ctrl.statusMessage(uiState.message); + ctrl.statusLevel(uiState.status); + }); + }; + + ctrl.state.on('change:value', function(e) { + validateUrl(e.value); + }); + } + }; + + return ComboBox.extend({ + /** + * Constructs a new control instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + var self = this, + editor = getActiveEditor(), + editorSettings = editor.settings; + var actionCallback, fileBrowserCallback, fileBrowserCallbackTypes; + var fileType = settings.filetype; + + settings.spellcheck = false; + + fileBrowserCallbackTypes = editorSettings.file_picker_types || editorSettings.file_browser_callback_types; + if (fileBrowserCallbackTypes) { + fileBrowserCallbackTypes = Tools.makeMap(fileBrowserCallbackTypes, /[, ]/); + } + + if (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[fileType]) { + fileBrowserCallback = editorSettings.file_picker_callback; + if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[fileType])) { + actionCallback = function() { + var meta = self.fire('beforecall').meta; + + meta = Tools.extend({ + filetype: fileType + }, meta); + + // file_picker_callback(callback, currentValue, metaData) + fileBrowserCallback.call( + editor, + function(value, meta) { + self.value(value).fire('change', { + meta: meta + }); + }, + self.value(), + meta + ); + }; + } else { + // Legacy callback: file_picker_callback(id, currentValue, filetype, window) + fileBrowserCallback = editorSettings.file_browser_callback; + if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[fileType])) { + actionCallback = function() { + fileBrowserCallback( + self.getEl('inp').id, + self.value(), + fileType, + window + ); + }; + } + } + } + + if (actionCallback) { + settings.icon = 'browse'; + settings.onaction = actionCallback; + } + + self._super(settings); + + setupAutoCompleteHandler(self, editorSettings, editor.getBody(), fileType); + setupLinkValidatorHandler(self, editorSettings, fileType); + } + }); + } + ); + /** + * FitLayout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This layout manager will resize the control to be the size of it's parent container. + * In other words width: 100% and height: 100%. + * + * @-x-less FitLayout.less + * @class tinymce.ui.FitLayout + * @extends tinymce.ui.AbsoluteLayout + */ + define( + 'tinymce.core.ui.FitLayout', [ + "tinymce.core.ui.AbsoluteLayout" + ], + function(AbsoluteLayout) { + "use strict"; + + return AbsoluteLayout.extend({ + /** + * Recalculates the positions of the controls in the specified container. + * + * @method recalc + * @param {tinymce.ui.Container} container Container instance to recalc. + */ + recalc: function(container) { + var contLayoutRect = container.layoutRect(), + paddingBox = container.paddingBox; + + container.items().filter(':visible').each(function(ctrl) { + ctrl.layoutRect({ + x: paddingBox.left, + y: paddingBox.top, + w: contLayoutRect.innerW - paddingBox.right - paddingBox.left, + h: contLayoutRect.innerH - paddingBox.top - paddingBox.bottom + }); + + if (ctrl.recalc) { + ctrl.recalc(); + } + }); + } + }); + } + ); + /** + * FlexLayout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This layout manager works similar to the CSS flex box. + * + * @setting {String} direction row|row-reverse|column|column-reverse + * @setting {Number} flex A positive-number to flex by. + * @setting {String} align start|end|center|stretch + * @setting {String} pack start|end|justify + * + * @class tinymce.ui.FlexLayout + * @extends tinymce.ui.AbsoluteLayout + */ + define( + 'tinymce.core.ui.FlexLayout', [ + "tinymce.core.ui.AbsoluteLayout" + ], + function(AbsoluteLayout) { + "use strict"; + + return AbsoluteLayout.extend({ + /** + * Recalculates the positions of the controls in the specified container. + * + * @method recalc + * @param {tinymce.ui.Container} container Container instance to recalc. + */ + recalc: function(container) { + // A ton of variables, needs to be in the same scope for performance + var i, l, items, contLayoutRect, contPaddingBox, contSettings, align, pack, spacing, totalFlex, availableSpace, direction; + var ctrl, ctrlLayoutRect, ctrlSettings, flex, maxSizeItems = [], + size, maxSize, ratio, rect, pos, maxAlignEndPos; + var sizeName, minSizeName, posName, maxSizeName, beforeName, innerSizeName, deltaSizeName, contentSizeName; + var alignAxisName, alignInnerSizeName, alignSizeName, alignMinSizeName, alignBeforeName, alignAfterName; + var alignDeltaSizeName, alignContentSizeName; + var max = Math.max, + min = Math.min; + + // Get container items, properties and settings + items = container.items().filter(':visible'); + contLayoutRect = container.layoutRect(); + contPaddingBox = container.paddingBox; + contSettings = container.settings; + direction = container.isRtl() ? (contSettings.direction || 'row-reversed') : contSettings.direction; + align = contSettings.align; + pack = container.isRtl() ? (contSettings.pack || 'end') : contSettings.pack; + spacing = contSettings.spacing || 0; + + if (direction == "row-reversed" || direction == "column-reverse") { + items = items.set(items.toArray().reverse()); + direction = direction.split('-')[0]; + } + + // Setup axis variable name for row/column direction since the calculations is the same + if (direction == "column") { + posName = "y"; + sizeName = "h"; + minSizeName = "minH"; + maxSizeName = "maxH"; + innerSizeName = "innerH"; + beforeName = 'top'; + deltaSizeName = "deltaH"; + contentSizeName = "contentH"; + + alignBeforeName = "left"; + alignSizeName = "w"; + alignAxisName = "x"; + alignInnerSizeName = "innerW"; + alignMinSizeName = "minW"; + alignAfterName = "right"; + alignDeltaSizeName = "deltaW"; + alignContentSizeName = "contentW"; + } else { + posName = "x"; + sizeName = "w"; + minSizeName = "minW"; + maxSizeName = "maxW"; + innerSizeName = "innerW"; + beforeName = 'left'; + deltaSizeName = "deltaW"; + contentSizeName = "contentW"; + + alignBeforeName = "top"; + alignSizeName = "h"; + alignAxisName = "y"; + alignInnerSizeName = "innerH"; + alignMinSizeName = "minH"; + alignAfterName = "bottom"; + alignDeltaSizeName = "deltaH"; + alignContentSizeName = "contentH"; + } + + // Figure out total flex, availableSpace and collect any max size elements + availableSpace = contLayoutRect[innerSizeName] - contPaddingBox[beforeName] - contPaddingBox[beforeName]; + maxAlignEndPos = totalFlex = 0; + for (i = 0, l = items.length; i < l; i++) { + ctrl = items[i]; + ctrlLayoutRect = ctrl.layoutRect(); + ctrlSettings = ctrl.settings; + flex = ctrlSettings.flex; + availableSpace -= (i < l - 1 ? spacing : 0); + + if (flex > 0) { + totalFlex += flex; + + // Flexed item has a max size then we need to check if we will hit that size + if (ctrlLayoutRect[maxSizeName]) { + maxSizeItems.push(ctrl); + } + + ctrlLayoutRect.flex = flex; + } + + availableSpace -= ctrlLayoutRect[minSizeName]; + + // Calculate the align end position to be used to check for overflow/underflow + size = contPaddingBox[alignBeforeName] + ctrlLayoutRect[alignMinSizeName] + contPaddingBox[alignAfterName]; + if (size > maxAlignEndPos) { + maxAlignEndPos = size; + } + } + + // Calculate minW/minH + rect = {}; + if (availableSpace < 0) { + rect[minSizeName] = contLayoutRect[minSizeName] - availableSpace + contLayoutRect[deltaSizeName]; + } else { + rect[minSizeName] = contLayoutRect[innerSizeName] - availableSpace + contLayoutRect[deltaSizeName]; + } + + rect[alignMinSizeName] = maxAlignEndPos + contLayoutRect[alignDeltaSizeName]; + + rect[contentSizeName] = contLayoutRect[innerSizeName] - availableSpace; + rect[alignContentSizeName] = maxAlignEndPos; + rect.minW = min(rect.minW, contLayoutRect.maxW); + rect.minH = min(rect.minH, contLayoutRect.maxH); + rect.minW = max(rect.minW, contLayoutRect.startMinWidth); + rect.minH = max(rect.minH, contLayoutRect.startMinHeight); + + // Resize container container if minSize was changed + if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) { + rect.w = rect.minW; + rect.h = rect.minH; + + container.layoutRect(rect); + this.recalc(container); + + // Forced recalc for example if items are hidden/shown + if (container._lastRect === null) { + var parentCtrl = container.parent(); + if (parentCtrl) { + parentCtrl._lastRect = null; + parentCtrl.recalc(); + } + } + + return; + } + + // Handle max size elements, check if they will become to wide with current options + ratio = availableSpace / totalFlex; + for (i = 0, l = maxSizeItems.length; i < l; i++) { + ctrl = maxSizeItems[i]; + ctrlLayoutRect = ctrl.layoutRect(); + maxSize = ctrlLayoutRect[maxSizeName]; + size = ctrlLayoutRect[minSizeName] + ctrlLayoutRect.flex * ratio; + + if (size > maxSize) { + availableSpace -= (ctrlLayoutRect[maxSizeName] - ctrlLayoutRect[minSizeName]); + totalFlex -= ctrlLayoutRect.flex; + ctrlLayoutRect.flex = 0; + ctrlLayoutRect.maxFlexSize = maxSize; + } else { + ctrlLayoutRect.maxFlexSize = 0; + } + } + + // Setup new ratio, target layout rect, start position + ratio = availableSpace / totalFlex; + pos = contPaddingBox[beforeName]; + rect = {}; + + // Handle pack setting moves the start position to end, center + if (totalFlex === 0) { + if (pack == "end") { + pos = availableSpace + contPaddingBox[beforeName]; + } else if (pack == "center") { + pos = Math.round( + (contLayoutRect[innerSizeName] / 2) - ((contLayoutRect[innerSizeName] - availableSpace) / 2) + ) + contPaddingBox[beforeName]; + + if (pos < 0) { + pos = contPaddingBox[beforeName]; + } + } else if (pack == "justify") { + pos = contPaddingBox[beforeName]; + spacing = Math.floor(availableSpace / (items.length - 1)); + } + } + + // Default aligning (start) the other ones needs to be calculated while doing the layout + rect[alignAxisName] = contPaddingBox[alignBeforeName]; + + // Start laying out controls + for (i = 0, l = items.length; i < l; i++) { + ctrl = items[i]; + ctrlLayoutRect = ctrl.layoutRect(); + size = ctrlLayoutRect.maxFlexSize || ctrlLayoutRect[minSizeName]; + + // Align the control on the other axis + if (align === "center") { + rect[alignAxisName] = Math.round((contLayoutRect[alignInnerSizeName] / 2) - (ctrlLayoutRect[alignSizeName] / 2)); + } else if (align === "stretch") { + rect[alignSizeName] = max( + ctrlLayoutRect[alignMinSizeName] || 0, + contLayoutRect[alignInnerSizeName] - contPaddingBox[alignBeforeName] - contPaddingBox[alignAfterName] + ); + rect[alignAxisName] = contPaddingBox[alignBeforeName]; + } else if (align === "end") { + rect[alignAxisName] = contLayoutRect[alignInnerSizeName] - ctrlLayoutRect[alignSizeName] - contPaddingBox.top; + } + + // Calculate new size based on flex + if (ctrlLayoutRect.flex > 0) { + size += ctrlLayoutRect.flex * ratio; + } + + rect[sizeName] = size; + rect[posName] = pos; + ctrl.layoutRect(rect); + + // Recalculate containers + if (ctrl.recalc) { + ctrl.recalc(); + } + + // Move x/y position + pos += size + spacing; + } + } + }); + } + ); + /** + * FlowLayout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This layout manager will place the controls by using the browsers native layout. + * + * @-x-less FlowLayout.less + * @class tinymce.ui.FlowLayout + * @extends tinymce.ui.Layout + */ + define( + 'tinymce.core.ui.FlowLayout', [ + "tinymce.core.ui.Layout" + ], + function(Layout) { + return Layout.extend({ + Defaults: { + containerClass: 'flow-layout', + controlClass: 'flow-layout-item', + endClass: 'break' + }, + + /** + * Recalculates the positions of the controls in the specified container. + * + * @method recalc + * @param {tinymce.ui.Container} container Container instance to recalc. + */ + recalc: function(container) { + container.items().filter(':visible').each(function(ctrl) { + if (ctrl.recalc) { + ctrl.recalc(); + } + }); + }, + + isNative: function() { + return true; + } + }); + } + ); + /** + * FontInfo.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Internal class for computing font size for elements. + * + * @private + * @class tinymce.fmt.FontInfo + */ + define( + 'tinymce.core.fmt.FontInfo', [ + 'ephox.katamari.api.Fun', + 'ephox.katamari.api.Option', + 'ephox.sugar.api.node.Element', + 'ephox.sugar.api.node.Node', + 'tinymce.core.dom.DOMUtils' + ], + function(Fun, Option, Element, Node, DOMUtils) { + var getSpecifiedFontProp = function(propName, rootElm, elm) { + while (elm !== rootElm) { + if (elm.style[propName]) { + var foundStyle = elm.style[propName]; + return foundStyle !== '' ? Option.some(foundStyle) : Option.none(); + } + elm = elm.parentNode; + } + return Option.none(); + }; + + var toPt = function(fontSize) { + if (/[0-9.]+px$/.test(fontSize)) { + return Math.round(parseInt(fontSize, 10) * 72 / 96) + 'pt'; + } + + return fontSize; + }; + + var normalizeFontFamily = function(fontFamily) { + // 'Font name', Font -> Font name,Font + return fontFamily.replace(/[\'\"]/g, '').replace(/,\s+/g, ','); + }; + + var getComputedFontProp = function(propName, elm) { + return Option.from(DOMUtils.DOM.getStyle(elm, propName, true)); + }; + + var getFontProp = function(propName) { + return function(rootElm, elm) { + return Option.from(elm) + .map(Element.fromDom) + .filter(Node.isElement) + .bind(function(element) { + return getSpecifiedFontProp(propName, rootElm, element.dom()) + .or(getComputedFontProp(propName, element.dom())); + }) + .getOr(''); + }; + }; + + return { + getFontSize: getFontProp('fontSize'), + getFontFamily: Fun.compose(normalizeFontFamily, getFontProp('fontFamily')), + toPt: toPt + }; + } + ); + + /** + * FormatControls.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Internal class containing all TinyMCE specific control types such as + * format listboxes, fontlist boxes, toolbar buttons etc. + * + * @class tinymce.ui.FormatControls + */ + define( + 'tinymce.core.ui.FormatControls', [ + "tinymce.core.ui.Control", + "tinymce.core.ui.Widget", + "tinymce.core.ui.FloatPanel", + "tinymce.core.util.Tools", + "tinymce.core.util.Arr", + "tinymce.core.dom.DOMUtils", + "tinymce.core.EditorManager", + "tinymce.core.Env", + "tinymce.core.fmt.FontInfo" + ], + function(Control, Widget, FloatPanel, Tools, Arr, DOMUtils, EditorManager, Env, FontInfo) { + var each = Tools.each; + + var flatten = function(ar) { + return Arr.reduce(ar, function(result, item) { + return result.concat(item); + }, []); + }; + + EditorManager.on('AddEditor', function(e) { + var editor = e.editor; + + setupRtlMode(editor); + registerControls(editor); + setupContainer(editor); + }); + + Control.translate = function(text) { + return EditorManager.translate(text); + }; + + Widget.tooltips = !Env.iOS; + + function setupContainer(editor) { + if (editor.settings.ui_container) { + Env.container = DOMUtils.DOM.select(editor.settings.ui_container)[0]; + } + } + + function setupRtlMode(editor) { + editor.on('ScriptsLoaded', function() { + if (editor.rtl) { + Control.rtl = true; + } + }); + } + + function registerControls(editor) { + var formatMenu; + + function createListBoxChangeHandler(items, formatName) { + return function() { + var self = this; + + editor.on('nodeChange', function(e) { + var formatter = editor.formatter; + var value = null; + + each(e.parents, function(node) { + each(items, function(item) { + if (formatName) { + if (formatter.matchNode(node, formatName, { + value: item.value + })) { + value = item.value; + } + } else { + if (formatter.matchNode(node, item.value)) { + value = item.value; + } + } + + if (value) { + return false; + } + }); + + if (value) { + return false; + } + }); + + self.value(value); + }); + }; + } + + function createFontNameListBoxChangeHandler(items) { + return function() { + var self = this; + + var getFirstFont = function(fontFamily) { + return fontFamily ? fontFamily.split(',')[0] : ''; + }; + + editor.on('init nodeChange', function(e) { + var fontFamily, value = null; + + fontFamily = FontInfo.getFontFamily(editor.getBody(), e.element); + + each(items, function(item) { + if (item.value.toLowerCase() === fontFamily.toLowerCase()) { + value = item.value; + } + }); + + each(items, function(item) { + if (!value && getFirstFont(item.value).toLowerCase() === getFirstFont(fontFamily).toLowerCase()) { + value = item.value; + } + }); + + self.value(value); + + if (!value && fontFamily) { + self.text(getFirstFont(fontFamily)); + } + }); + }; + } + + function createFontSizeListBoxChangeHandler(items) { + return function() { + var self = this; + + editor.on('init nodeChange', function(e) { + var px, pt, value = null; + + px = FontInfo.getFontSize(editor.getBody(), e.element); + pt = FontInfo.toPt(px); + + each(items, function(item) { + if (item.value === px) { + value = px; + } else if (item.value === pt) { + value = pt; + } + }); + + self.value(value); + + if (!value) { + self.text(pt); + } + }); + }; + } + + function createFormats(formats) { + formats = formats.replace(/;$/, '').split(';'); + + var i = formats.length; + while (i--) { + formats[i] = formats[i].split('='); + } + + return formats; + } + + function createFormatMenu() { + var count = 0, + newFormats = []; + + var defaultStyleFormats = [{ + title: 'Headings', + items: [{ + title: 'Heading 1', + format: 'h1' + }, + { + title: 'Heading 2', + format: 'h2' + }, + { + title: 'Heading 3', + format: 'h3' + }, + { + title: 'Heading 4', + format: 'h4' + }, + { + title: 'Heading 5', + format: 'h5' + }, + { + title: 'Heading 6', + format: 'h6' + } + ] + }, + + { + title: 'Inline', + items: [{ + title: 'Bold', + icon: 'bold', + format: 'bold' + }, + { + title: 'Italic', + icon: 'italic', + format: 'italic' + }, + { + title: 'Underline', + icon: 'underline', + format: 'underline' + }, + { + title: 'Strikethrough', + icon: 'strikethrough', + format: 'strikethrough' + }, + { + title: 'Superscript', + icon: 'superscript', + format: 'superscript' + }, + { + title: 'Subscript', + icon: 'subscript', + format: 'subscript' + }, + { + title: 'Code', + icon: 'code', + format: 'code' + } + ] + }, + + { + title: 'Blocks', + items: [{ + title: 'Paragraph', + format: 'p' + }, + { + title: 'Blockquote', + format: 'blockquote' + }, + { + title: 'Div', + format: 'div' + }, + { + title: 'Pre', + format: 'pre' + } + ] + }, + + { + title: 'Alignment', + items: [{ + title: 'Left', + icon: 'alignleft', + format: 'alignleft' + }, + { + title: 'Center', + icon: 'aligncenter', + format: 'aligncenter' + }, + { + title: 'Right', + icon: 'alignright', + format: 'alignright' + }, + { + title: 'Justify', + icon: 'alignjustify', + format: 'alignjustify' + } + ] + } + ]; + + function createMenu(formats) { + var menu = []; + + if (!formats) { + return; + } + + each(formats, function(format) { + var menuItem = { + text: format.title, + icon: format.icon + }; + + if (format.items) { + menuItem.menu = createMenu(format.items); + } else { + var formatName = format.format || "custom" + count++; + + if (!format.format) { + format.name = formatName; + newFormats.push(format); + } + + menuItem.format = formatName; + menuItem.cmd = format.cmd; + } + + menu.push(menuItem); + }); + + return menu; + } + + function createStylesMenu() { + var menu; + + if (editor.settings.style_formats_merge) { + if (editor.settings.style_formats) { + menu = createMenu(defaultStyleFormats.concat(editor.settings.style_formats)); + } else { + menu = createMenu(defaultStyleFormats); + } + } else { + menu = createMenu(editor.settings.style_formats || defaultStyleFormats); + } + + return menu; + } + + editor.on('init', function() { + each(newFormats, function(format) { + editor.formatter.register(format.name, format); + }); + }); + + return { + type: 'menu', + items: createStylesMenu(), + onPostRender: function(e) { + editor.fire('renderFormatsMenu', { + control: e.control + }); + }, + itemDefaults: { + preview: true, + + textStyle: function() { + if (this.settings.format) { + return editor.formatter.getCssText(this.settings.format); + } + }, + + onPostRender: function() { + var self = this; + + self.parent().on('show', function() { + var formatName, command; + + formatName = self.settings.format; + if (formatName) { + self.disabled(!editor.formatter.canApply(formatName)); + self.active(editor.formatter.match(formatName)); + } + + command = self.settings.cmd; + if (command) { + self.active(editor.queryCommandState(command)); + } + }); + }, + + onclick: function() { + if (this.settings.format) { + toggleFormat(this.settings.format); + } + + if (this.settings.cmd) { + editor.execCommand(this.settings.cmd); + } + } + } + }; + } + + formatMenu = createFormatMenu(); + + function initOnPostRender(name) { + return function() { + var self = this; + + // TODO: Fix this + if (editor.formatter) { + editor.formatter.formatChanged(name, function(state) { + self.active(state); + }); + } else { + editor.on('init', function() { + editor.formatter.formatChanged(name, function(state) { + self.active(state); + }); + }); + } + }; + } + + // Simple format controls <control/format>:<UI text> + each({ + bold: 'Bold', + italic: 'Italic', + underline: 'Underline', + strikethrough: 'Strikethrough', + subscript: 'Subscript', + superscript: 'Superscript' + }, function(text, name) { + editor.addButton(name, { + tooltip: text, + onPostRender: initOnPostRender(name), + onclick: function() { + toggleFormat(name); + } + }); + }); + + // Simple command controls <control>:[<UI text>,<Command>] + each({ + outdent: ['Decrease indent', 'Outdent'], + indent: ['Increase indent', 'Indent'], + cut: ['Cut', 'Cut'], + copy: ['Copy', 'Copy'], + paste: ['Paste', 'Paste'], + help: ['Help', 'mceHelp'], + selectall: ['Select all', 'SelectAll'], + removeformat: ['Clear formatting', 'RemoveFormat'], + visualaid: ['Visual aids', 'mceToggleVisualAid'], + newdocument: ['New document', 'mceNewDocument'] + }, function(item, name) { + editor.addButton(name, { + tooltip: item[0], + cmd: item[1] + }); + }); + + // Simple command controls with format state + each({ + blockquote: ['Blockquote', 'mceBlockQuote'], + subscript: ['Subscript', 'Subscript'], + superscript: ['Superscript', 'Superscript'], + alignleft: ['Align left', 'JustifyLeft'], + aligncenter: ['Align center', 'JustifyCenter'], + alignright: ['Align right', 'JustifyRight'], + alignjustify: ['Justify', 'JustifyFull'], + alignnone: ['No alignment', 'JustifyNone'] + }, function(item, name) { + editor.addButton(name, { + tooltip: item[0], + cmd: item[1], + onPostRender: initOnPostRender(name) + }); + }); + + function toggleUndoRedoState(type) { + return function() { + var self = this; + + function checkState() { + var typeFn = type == 'redo' ? 'hasRedo' : 'hasUndo'; + return editor.undoManager ? editor.undoManager[typeFn]() : false; + } + + self.disabled(!checkState()); + editor.on('Undo Redo AddUndo TypingUndo ClearUndos SwitchMode', function() { + self.disabled(editor.readonly || !checkState()); + }); + }; + } + + function toggleVisualAidState() { + var self = this; + + editor.on('VisualAid', function(e) { + self.active(e.hasVisual); + }); + + self.active(editor.hasVisual); + } + + var trimMenuItems = function(menuItems) { + var outputMenuItems = menuItems; + + if (outputMenuItems.length > 0 && outputMenuItems[0].text === '-') { + outputMenuItems = outputMenuItems.slice(1); + } + + if (outputMenuItems.length > 0 && outputMenuItems[outputMenuItems.length - 1].text === '-') { + outputMenuItems = outputMenuItems.slice(0, outputMenuItems.length - 1); + } + + return outputMenuItems; + }; + + var createCustomMenuItems = function(names) { + var items, nameList; + + if (typeof names === 'string') { + nameList = names.split(' '); + } else if (Tools.isArray(names)) { + return flatten(Tools.map(names, createCustomMenuItems)); + } + + items = Tools.grep(nameList, function(name) { + return name === '|' || name in editor.menuItems; + }); + + return Tools.map(items, function(name) { + return name === '|' ? { + text: '-' + } : editor.menuItems[name]; + }); + }; + + var createContextMenuItems = function(context) { + var outputMenuItems = [{ + text: '-' + }]; + var menuItems = Tools.grep(editor.menuItems, function(menuItem) { + return menuItem.context === context; + }); + + Tools.each(menuItems, function(menuItem) { + if (menuItem.separator == 'before') { + outputMenuItems.push({ + text: '|' + }); + } + + if (menuItem.prependToContext) { + outputMenuItems.unshift(menuItem); + } else { + outputMenuItems.push(menuItem); + } + + if (menuItem.separator == 'after') { + outputMenuItems.push({ + text: '|' + }); + } + }); + + return outputMenuItems; + }; + + var createInsertMenu = function(editorSettings) { + if (editorSettings.insert_button_items) { + return trimMenuItems(createCustomMenuItems(editorSettings.insert_button_items)); + } else { + return trimMenuItems(createContextMenuItems('insert')); + } + }; + + editor.addButton('undo', { + tooltip: 'Undo', + onPostRender: toggleUndoRedoState('undo'), + cmd: 'undo' + }); + + editor.addButton('redo', { + tooltip: 'Redo', + onPostRender: toggleUndoRedoState('redo'), + cmd: 'redo' + }); + + editor.addMenuItem('newdocument', { + text: 'New document', + icon: 'newdocument', + cmd: 'mceNewDocument' + }); + + editor.addMenuItem('undo', { + text: 'Undo', + icon: 'undo', + shortcut: 'Meta+Z', + onPostRender: toggleUndoRedoState('undo'), + cmd: 'undo' + }); + + editor.addMenuItem('redo', { + text: 'Redo', + icon: 'redo', + shortcut: 'Meta+Y', + onPostRender: toggleUndoRedoState('redo'), + cmd: 'redo' + }); + + editor.addMenuItem('visualaid', { + text: 'Visual aids', + selectable: true, + onPostRender: toggleVisualAidState, + cmd: 'mceToggleVisualAid' + }); + + editor.addButton('remove', { + tooltip: 'Remove', + icon: 'remove', + cmd: 'Delete' + }); + + editor.addButton('insert', { + type: 'menubutton', + icon: 'insert', + menu: [], + oncreatemenu: function() { + this.menu.add(createInsertMenu(editor.settings)); + this.menu.renderNew(); + } + }); + + each({ + cut: ['Cut', 'Cut', 'Meta+X'], + copy: ['Copy', 'Copy', 'Meta+C'], + paste: ['Paste', 'Paste', 'Meta+V'], + selectall: ['Select all', 'SelectAll', 'Meta+A'], + bold: ['Bold', 'Bold', 'Meta+B'], + italic: ['Italic', 'Italic', 'Meta+I'], + underline: ['Underline', 'Underline', 'Meta+U'], + strikethrough: ['Strikethrough', 'Strikethrough'], + subscript: ['Subscript', 'Subscript'], + superscript: ['Superscript', 'Superscript'], + removeformat: ['Clear formatting', 'RemoveFormat'] + }, function(item, name) { + editor.addMenuItem(name, { + text: item[0], + icon: name, + shortcut: item[2], + cmd: item[1] + }); + }); + + editor.on('mousedown', function() { + FloatPanel.hideAll(); + }); + + function toggleFormat(fmt) { + if (fmt.control) { + fmt = fmt.control.value(); + } + + if (fmt) { + editor.execCommand('mceToggleFormat', false, fmt); + } + } + + function hideMenuObjects(menu) { + var count = menu.length; + + Tools.each(menu, function(item) { + if (item.menu) { + item.hidden = hideMenuObjects(item.menu) === 0; + } + + var formatName = item.format; + if (formatName) { + item.hidden = !editor.formatter.canApply(formatName); + } + + if (item.hidden) { + count--; + } + }); + + return count; + } + + function hideFormatMenuItems(menu) { + var count = menu.items().length; + + menu.items().each(function(item) { + if (item.menu) { + item.visible(hideFormatMenuItems(item.menu) > 0); + } + + if (!item.menu && item.settings.menu) { + item.visible(hideMenuObjects(item.settings.menu) > 0); + } + + var formatName = item.settings.format; + if (formatName) { + item.visible(editor.formatter.canApply(formatName)); + } + + if (!item.visible()) { + count--; + } + }); + + return count; + } + + editor.addButton('styleselect', { + type: 'menubutton', + text: 'Formats', + menu: formatMenu, + onShowMenu: function() { + if (editor.settings.style_formats_autohide) { + hideFormatMenuItems(this.menu); + } + } + }); + + editor.addButton('formatselect', function() { + var items = [], + blocks = createFormats(editor.settings.block_formats || + 'Paragraph=p;' + + 'Heading 1=h1;' + + 'Heading 2=h2;' + + 'Heading 3=h3;' + + 'Heading 4=h4;' + + 'Heading 5=h5;' + + 'Heading 6=h6;' + + 'Preformatted=pre' + ); + + each(blocks, function(block) { + items.push({ + text: block[0], + value: block[1], + textStyle: function() { + return editor.formatter.getCssText(block[1]); + } + }); + }); + + return { + type: 'listbox', + text: blocks[0][0], + values: items, + fixedWidth: true, + onselect: toggleFormat, + onPostRender: createListBoxChangeHandler(items) + }; + }); + + editor.addButton('fontselect', function() { + var defaultFontsFormats = + 'Andale Mono=andale mono,monospace;' + + 'Arial=arial,helvetica,sans-serif;' + + 'Arial Black=arial black,sans-serif;' + + 'Book Antiqua=book antiqua,palatino,serif;' + + 'Comic Sans MS=comic sans ms,sans-serif;' + + 'Courier New=courier new,courier,monospace;' + + 'Georgia=georgia,palatino,serif;' + + 'Helvetica=helvetica,arial,sans-serif;' + + 'Impact=impact,sans-serif;' + + 'Symbol=symbol;' + + 'Tahoma=tahoma,arial,helvetica,sans-serif;' + + 'Terminal=terminal,monaco,monospace;' + + 'Times New Roman=times new roman,times,serif;' + + 'Trebuchet MS=trebuchet ms,geneva,sans-serif;' + + 'Verdana=verdana,geneva,sans-serif;' + + 'Webdings=webdings;' + + 'Wingdings=wingdings,zapf dingbats'; + + var items = [], + fonts = createFormats(editor.settings.font_formats || defaultFontsFormats); + + each(fonts, function(font) { + items.push({ + text: { + raw: font[0] + }, + value: font[1], + textStyle: font[1].indexOf('dings') == -1 ? 'font-family:' + font[1] : '' + }); + }); + + return { + type: 'listbox', + text: 'Font Family', + tooltip: 'Font Family', + values: items, + fixedWidth: true, + onPostRender: createFontNameListBoxChangeHandler(items), + onselect: function(e) { + if (e.control.settings.value) { + editor.execCommand('FontName', false, e.control.settings.value); + } + } + }; + }); + + editor.addButton('fontsizeselect', function() { + var items = [], + defaultFontsizeFormats = '8pt 10pt 12pt 14pt 18pt 24pt 36pt'; + var fontsizeFormats = editor.settings.fontsize_formats || defaultFontsizeFormats; + + each(fontsizeFormats.split(' '), function(item) { + var text = item, + value = item; + // Allow text=value font sizes. + var values = item.split('='); + if (values.length > 1) { + text = values[0]; + value = values[1]; + } + items.push({ + text: text, + value: value + }); + }); + + return { + type: 'listbox', + text: 'Font Sizes', + tooltip: 'Font Sizes', + values: items, + fixedWidth: true, + onPostRender: createFontSizeListBoxChangeHandler(items), + onclick: function(e) { + if (e.control.settings.value) { + editor.execCommand('FontSize', false, e.control.settings.value); + } + } + }; + }); + + editor.addMenuItem('formats', { + text: 'Formats', + menu: formatMenu + }); + } + + return {}; + } + ); + + /** + * GridLayout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This layout manager places controls in a grid. + * + * @setting {Number} spacing Spacing between controls. + * @setting {Number} spacingH Horizontal spacing between controls. + * @setting {Number} spacingV Vertical spacing between controls. + * @setting {Number} columns Number of columns to use. + * @setting {String/Array} alignH start|end|center|stretch or array of values for each column. + * @setting {String/Array} alignV start|end|center|stretch or array of values for each column. + * @setting {String} pack start|end + * + * @class tinymce.ui.GridLayout + * @extends tinymce.ui.AbsoluteLayout + */ + define( + 'tinymce.core.ui.GridLayout', [ + "tinymce.core.ui.AbsoluteLayout" + ], + function(AbsoluteLayout) { + "use strict"; + + return AbsoluteLayout.extend({ + /** + * Recalculates the positions of the controls in the specified container. + * + * @method recalc + * @param {tinymce.ui.Container} container Container instance to recalc. + */ + recalc: function(container) { + var settings, rows, cols, items, contLayoutRect, width, height, rect, + ctrlLayoutRect, ctrl, x, y, posX, posY, ctrlSettings, contPaddingBox, align, spacingH, spacingV, alignH, alignV, maxX, maxY, + colWidths = [], + rowHeights = [], + ctrlMinWidth, ctrlMinHeight, availableWidth, availableHeight, reverseRows, idx; + + // Get layout settings + settings = container.settings; + items = container.items().filter(':visible'); + contLayoutRect = container.layoutRect(); + cols = settings.columns || Math.ceil(Math.sqrt(items.length)); + rows = Math.ceil(items.length / cols); + spacingH = settings.spacingH || settings.spacing || 0; + spacingV = settings.spacingV || settings.spacing || 0; + alignH = settings.alignH || settings.align; + alignV = settings.alignV || settings.align; + contPaddingBox = container.paddingBox; + reverseRows = 'reverseRows' in settings ? settings.reverseRows : container.isRtl(); + + if (alignH && typeof alignH == "string") { + alignH = [alignH]; + } + + if (alignV && typeof alignV == "string") { + alignV = [alignV]; + } + + // Zero padd columnWidths + for (x = 0; x < cols; x++) { + colWidths.push(0); + } + + // Zero padd rowHeights + for (y = 0; y < rows; y++) { + rowHeights.push(0); + } + + // Calculate columnWidths and rowHeights + for (y = 0; y < rows; y++) { + for (x = 0; x < cols; x++) { + ctrl = items[y * cols + x]; + + // Out of bounds + if (!ctrl) { + break; + } + + ctrlLayoutRect = ctrl.layoutRect(); + ctrlMinWidth = ctrlLayoutRect.minW; + ctrlMinHeight = ctrlLayoutRect.minH; + + colWidths[x] = ctrlMinWidth > colWidths[x] ? ctrlMinWidth : colWidths[x]; + rowHeights[y] = ctrlMinHeight > rowHeights[y] ? ctrlMinHeight : rowHeights[y]; + } + } + + // Calculate maxX + availableWidth = contLayoutRect.innerW - contPaddingBox.left - contPaddingBox.right; + for (maxX = 0, x = 0; x < cols; x++) { + maxX += colWidths[x] + (x > 0 ? spacingH : 0); + availableWidth -= (x > 0 ? spacingH : 0) + colWidths[x]; + } + + // Calculate maxY + availableHeight = contLayoutRect.innerH - contPaddingBox.top - contPaddingBox.bottom; + for (maxY = 0, y = 0; y < rows; y++) { + maxY += rowHeights[y] + (y > 0 ? spacingV : 0); + availableHeight -= (y > 0 ? spacingV : 0) + rowHeights[y]; + } + + maxX += contPaddingBox.left + contPaddingBox.right; + maxY += contPaddingBox.top + contPaddingBox.bottom; + + // Calculate minW/minH + rect = {}; + rect.minW = maxX + (contLayoutRect.w - contLayoutRect.innerW); + rect.minH = maxY + (contLayoutRect.h - contLayoutRect.innerH); + + rect.contentW = rect.minW - contLayoutRect.deltaW; + rect.contentH = rect.minH - contLayoutRect.deltaH; + rect.minW = Math.min(rect.minW, contLayoutRect.maxW); + rect.minH = Math.min(rect.minH, contLayoutRect.maxH); + rect.minW = Math.max(rect.minW, contLayoutRect.startMinWidth); + rect.minH = Math.max(rect.minH, contLayoutRect.startMinHeight); + + // Resize container container if minSize was changed + if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) { + rect.w = rect.minW; + rect.h = rect.minH; + + container.layoutRect(rect); + this.recalc(container); + + // Forced recalc for example if items are hidden/shown + if (container._lastRect === null) { + var parentCtrl = container.parent(); + if (parentCtrl) { + parentCtrl._lastRect = null; + parentCtrl.recalc(); + } + } + + return; + } + + // Update contentW/contentH so absEnd moves correctly + if (contLayoutRect.autoResize) { + rect = container.layoutRect(rect); + rect.contentW = rect.minW - contLayoutRect.deltaW; + rect.contentH = rect.minH - contLayoutRect.deltaH; + } + + var flexV; + + if (settings.packV == 'start') { + flexV = 0; + } else { + flexV = availableHeight > 0 ? Math.floor(availableHeight / rows) : 0; + } + + // Calculate totalFlex + var totalFlex = 0; + var flexWidths = settings.flexWidths; + if (flexWidths) { + for (x = 0; x < flexWidths.length; x++) { + totalFlex += flexWidths[x]; + } + } else { + totalFlex = cols; + } + + // Calculate new column widths based on flex values + var ratio = availableWidth / totalFlex; + for (x = 0; x < cols; x++) { + colWidths[x] += flexWidths ? flexWidths[x] * ratio : ratio; + } + + // Move/resize controls + posY = contPaddingBox.top; + for (y = 0; y < rows; y++) { + posX = contPaddingBox.left; + height = rowHeights[y] + flexV; + + for (x = 0; x < cols; x++) { + if (reverseRows) { + idx = y * cols + cols - 1 - x; + } else { + idx = y * cols + x; + } + + ctrl = items[idx]; + + // No more controls to render then break + if (!ctrl) { + break; + } + + // Get control settings and calculate x, y + ctrlSettings = ctrl.settings; + ctrlLayoutRect = ctrl.layoutRect(); + width = Math.max(colWidths[x], ctrlLayoutRect.startMinWidth); + ctrlLayoutRect.x = posX; + ctrlLayoutRect.y = posY; + + // Align control horizontal + align = ctrlSettings.alignH || (alignH ? (alignH[x] || alignH[0]) : null); + if (align == "center") { + ctrlLayoutRect.x = posX + (width / 2) - (ctrlLayoutRect.w / 2); + } else if (align == "right") { + ctrlLayoutRect.x = posX + width - ctrlLayoutRect.w; + } else if (align == "stretch") { + ctrlLayoutRect.w = width; + } + + // Align control vertical + align = ctrlSettings.alignV || (alignV ? (alignV[x] || alignV[0]) : null); + if (align == "center") { + ctrlLayoutRect.y = posY + (height / 2) - (ctrlLayoutRect.h / 2); + } else if (align == "bottom") { + ctrlLayoutRect.y = posY + height - ctrlLayoutRect.h; + } else if (align == "stretch") { + ctrlLayoutRect.h = height; + } + + ctrl.layoutRect(ctrlLayoutRect); + + posX += width + spacingH; + + if (ctrl.recalc) { + ctrl.recalc(); + } + } + + posY += height + spacingV; + } + } + }); + } + ); + + /** + * Iframe.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /*jshint scripturl:true */ + + /** + * This class creates an iframe. + * + * @setting {String} url Url to open in the iframe. + * + * @-x-less Iframe.less + * @class tinymce.ui.Iframe + * @extends tinymce.ui.Widget + */ + define( + 'tinymce.core.ui.Iframe', [ + "tinymce.core.ui.Widget", + "tinymce.core.util.Delay" + ], + function(Widget, Delay) { + "use strict"; + + return Widget.extend({ + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this; + + self.classes.add('iframe'); + self.canFocus = false; + + /*eslint no-script-url:0 */ + return ( + '<iframe id="' + self._id + '" class="' + self.classes + '" tabindex="-1" src="' + + (self.settings.url || "javascript:''") + '" frameborder="0"></iframe>' + ); + }, + + /** + * Setter for the iframe source. + * + * @method src + * @param {String} src Source URL for iframe. + */ + src: function(src) { + this.getEl().src = src; + }, + + /** + * Inner HTML for the iframe. + * + * @method html + * @param {String} html HTML string to set as HTML inside the iframe. + * @param {function} callback Optional callback to execute when the iframe body is filled with contents. + * @return {tinymce.ui.Iframe} Current iframe control. + */ + html: function(html, callback) { + var self = this, + body = this.getEl().contentWindow.document.body; + + // Wait for iframe to initialize IE 10 takes time + if (!body) { + Delay.setTimeout(function() { + self.html(html); + }); + } else { + body.innerHTML = html; + + if (callback) { + callback(); + } + } + + return this; + } + }); + } + ); + + /** + * InfoBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * .... + * + * @-x-less InfoBox.less + * @class tinymce.ui.InfoBox + * @extends tinymce.ui.Widget + */ + define( + 'tinymce.core.ui.InfoBox', [ + "tinymce.core.ui.Widget" + ], + function(Widget) { + "use strict"; + + return Widget.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Boolean} multiline Multiline label. + */ + init: function(settings) { + var self = this; + + self._super(settings); + self.classes.add('widget').add('infobox'); + self.canFocus = false; + }, + + severity: function(level) { + this.classes.remove('error'); + this.classes.remove('warning'); + this.classes.remove('success'); + this.classes.add(level); + }, + + help: function(state) { + this.state.set('help', state); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + prefix = self.classPrefix; + + return ( + '<div id="' + self._id + '" class="' + self.classes + '">' + + '<div id="' + self._id + '-body">' + + self.encode(self.state.get('text')) + + '<button role="button" tabindex="-1">' + + '<i class="' + prefix + 'ico ' + prefix + 'i-help"></i>' + + '</button>' + + '</div>' + + '</div>' + ); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:text', function(e) { + self.getEl('body').firstChild.data = self.encode(e.value); + + if (self.state.get('rendered')) { + self.updateLayoutRect(); + } + }); + + self.state.on('change:help', function(e) { + self.classes.toggle('has-help', e.value); + + if (self.state.get('rendered')) { + self.updateLayoutRect(); + } + }); + + return self._super(); + } + }); + } + ); + + /** + * Label.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class creates a label element. A label is a simple text control + * that can be bound to other controls. + * + * @-x-less Label.less + * @class tinymce.ui.Label + * @extends tinymce.ui.Widget + */ + define( + 'tinymce.core.ui.Label', [ + "tinymce.core.ui.Widget", + "tinymce.core.ui.DomUtils" + ], + function(Widget, DomUtils) { + "use strict"; + + return Widget.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Boolean} multiline Multiline label. + */ + init: function(settings) { + var self = this; + + self._super(settings); + self.classes.add('widget').add('label'); + self.canFocus = false; + + if (settings.multiline) { + self.classes.add('autoscroll'); + } + + if (settings.strong) { + self.classes.add('strong'); + } + }, + + /** + * Initializes the current controls layout rect. + * This will be executed by the layout managers to determine the + * default minWidth/minHeight etc. + * + * @method initLayoutRect + * @return {Object} Layout rect instance. + */ + initLayoutRect: function() { + var self = this, + layoutRect = self._super(); + + if (self.settings.multiline) { + var size = DomUtils.getSize(self.getEl()); + + // Check if the text fits within maxW if not then try word wrapping it + if (size.width > layoutRect.maxW) { + layoutRect.minW = layoutRect.maxW; + self.classes.add('multiline'); + } + + self.getEl().style.width = layoutRect.minW + 'px'; + layoutRect.startMinH = layoutRect.h = layoutRect.minH = Math.min(layoutRect.maxH, DomUtils.getSize(self.getEl()).height); + } + + return layoutRect; + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this; + + if (!self.settings.multiline) { + self.getEl().style.lineHeight = self.layoutRect().h + 'px'; + } + + return self._super(); + }, + + severity: function(level) { + this.classes.remove('error'); + this.classes.remove('warning'); + this.classes.remove('success'); + this.classes.add(level); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + targetCtrl, forName, forId = self.settings.forId; + var text = self.settings.html ? self.settings.html : self.encode(self.state.get('text')); + + if (!forId && (forName = self.settings.forName)) { + targetCtrl = self.getRoot().find('#' + forName)[0]; + + if (targetCtrl) { + forId = targetCtrl._id; + } + } + + if (forId) { + return ( + '<label id="' + self._id + '" class="' + self.classes + '"' + (forId ? ' for="' + forId + '"' : '') + '>' + + text + + '</label>' + ); + } + + return ( + '<span id="' + self._id + '" class="' + self.classes + '">' + + text + + '</span>' + ); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:text', function(e) { + self.innerHtml(self.encode(e.value)); + + if (self.state.get('rendered')) { + self.updateLayoutRect(); + } + }); + + return self._super(); + } + }); + } + ); + + /** + * Toolbar.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a new toolbar. + * + * @class tinymce.ui.Toolbar + * @extends tinymce.ui.Container + */ + define( + 'tinymce.core.ui.Toolbar', [ + "tinymce.core.ui.Container" + ], + function(Container) { + "use strict"; + + return Container.extend({ + Defaults: { + role: 'toolbar', + layout: 'flow' + }, + + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + var self = this; + + self._super(settings); + self.classes.add('toolbar'); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this; + + self.items().each(function(ctrl) { + ctrl.classes.add('toolbar-item'); + }); + + return self._super(); + } + }); + } + ); + /** + * MenuBar.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a new menubar. + * + * @-x-less MenuBar.less + * @class tinymce.ui.MenuBar + * @extends tinymce.ui.Container + */ + define( + 'tinymce.core.ui.MenuBar', [ + "tinymce.core.ui.Toolbar" + ], + function(Toolbar) { + "use strict"; + + return Toolbar.extend({ + Defaults: { + role: 'menubar', + containerCls: 'menubar', + ariaRoot: true, + defaults: { + type: 'menubutton' + } + } + }); + } + ); + /** + * MenuButton.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a new menu button. + * + * @-x-less MenuButton.less + * @class tinymce.ui.MenuButton + * @extends tinymce.ui.Button + */ + define( + 'tinymce.core.ui.MenuButton', [ + "tinymce.core.ui.Button", + "tinymce.core.ui.Factory", + "tinymce.core.ui.MenuBar" + ], + function(Button, Factory, MenuBar) { + "use strict"; + + // TODO: Maybe add as some global function + function isChildOf(node, parent) { + while (node) { + if (parent === node) { + return true; + } + + node = node.parentNode; + } + + return false; + } + + var MenuButton = Button.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + var self = this; + + self._renderOpen = true; + + self._super(settings); + settings = self.settings; + + self.classes.add('menubtn'); + + if (settings.fixedWidth) { + self.classes.add('fixed-width'); + } + + self.aria('haspopup', true); + + self.state.set('menu', settings.menu || self.render()); + }, + + /** + * Shows the menu for the button. + * + * @method showMenu + */ + showMenu: function(toggle) { + var self = this, + menu; + + if (self.menu && self.menu.visible() && toggle !== false) { + return self.hideMenu(); + } + + if (!self.menu) { + menu = self.state.get('menu') || []; + + // Is menu array then auto constuct menu control + if (menu.length) { + menu = { + type: 'menu', + items: menu + }; + } else { + menu.type = menu.type || 'menu'; + } + + if (!menu.renderTo) { + self.menu = Factory.create(menu).parent(self).renderTo(); + } else { + self.menu = menu.parent(self).show().renderTo(); + } + + self.fire('createmenu'); + self.menu.reflow(); + self.menu.on('cancel', function(e) { + if (e.control.parent() === self.menu) { + e.stopPropagation(); + self.focus(); + self.hideMenu(); + } + }); + + // Move focus to button when a menu item is selected/clicked + self.menu.on('select', function() { + self.focus(); + }); + + self.menu.on('show hide', function(e) { + if (e.control == self.menu) { + self.activeMenu(e.type == 'show'); + } + + self.aria('expanded', e.type == 'show'); + }).fire('show'); + } + + self.menu.show(); + self.menu.layoutRect({ + w: self.layoutRect().w + }); + self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']); + self.fire('showmenu'); + }, + + /** + * Hides the menu for the button. + * + * @method hideMenu + */ + hideMenu: function() { + var self = this; + + if (self.menu) { + self.menu.items().each(function(item) { + if (item.hideMenu) { + item.hideMenu(); + } + }); + + self.menu.hide(); + } + }, + + /** + * Sets the active menu state. + * + * @private + */ + activeMenu: function(state) { + this.classes.toggle('active', state); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + id = self._id, + prefix = self.classPrefix; + var icon = self.settings.icon, + image, text = self.state.get('text'), + textHtml = ''; + + image = self.settings.image; + if (image) { + icon = 'none'; + + // Support for [high dpi, low dpi] image sources + if (typeof image != "string") { + image = window.getSelection ? image[0] : image[1]; + } + + image = ' style="background-image: url(\'' + image + '\')"'; + } else { + image = ''; + } + + if (text) { + self.classes.add('btn-has-text'); + textHtml = '<span class="' + prefix + 'txt">' + self.encode(text) + '</span>'; + } + + icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : ''; + + self.aria('role', self.parent() instanceof MenuBar ? 'menuitem' : 'button'); + + return ( + '<div id="' + id + '" class="' + self.classes + '" tabindex="-1" aria-labelledby="' + id + '">' + + '<button id="' + id + '-open" role="presentation" type="button" tabindex="-1">' + + (icon ? '<i class="' + icon + '"' + image + '></i>' : '') + + textHtml + + ' <i class="' + prefix + 'caret"></i>' + + '</button>' + + '</div>' + ); + }, + + /** + * Gets invoked after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this; + + self.on('click', function(e) { + if (e.control === self && isChildOf(e.target, self.getEl())) { + self.focus(); + self.showMenu(!e.aria); + + if (e.aria) { + self.menu.items().filter(':visible')[0].focus(); + } + } + }); + + self.on('mouseenter', function(e) { + var overCtrl = e.control, + parent = self.parent(), + hasVisibleSiblingMenu; + + if (overCtrl && parent && overCtrl instanceof MenuButton && overCtrl.parent() == parent) { + parent.items().filter('MenuButton').each(function(ctrl) { + if (ctrl.hideMenu && ctrl != overCtrl) { + if (ctrl.menu && ctrl.menu.visible()) { + hasVisibleSiblingMenu = true; + } + + ctrl.hideMenu(); + } + }); + + if (hasVisibleSiblingMenu) { + overCtrl.focus(); // Fix for: #5887 + overCtrl.showMenu(); + } + } + }); + + return self._super(); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:menu', function() { + if (self.menu) { + self.menu.remove(); + } + + self.menu = null; + }); + + return self._super(); + }, + + /** + * Removes the control and it's menus. + * + * @method remove + */ + remove: function() { + this._super(); + + if (this.menu) { + this.menu.remove(); + } + } + }); + + return MenuButton; + } + ); + + /** + * MenuItem.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a new menu item. + * + * @-x-less MenuItem.less + * @class tinymce.ui.MenuItem + * @extends tinymce.ui.Control + */ + define( + 'tinymce.core.ui.MenuItem', [ + "tinymce.core.ui.Widget", + "tinymce.core.ui.Factory", + "tinymce.core.Env", + "tinymce.core.util.Delay" + ], + function(Widget, Factory, Env, Delay) { + "use strict"; + + return Widget.extend({ + Defaults: { + border: 0, + role: 'menuitem' + }, + + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Boolean} selectable Selectable menu. + * @setting {Array} menu Submenu array with items. + * @setting {String} shortcut Shortcut to display for menu item. Example: Ctrl+X + */ + init: function(settings) { + var self = this, + text; + + self._super(settings); + + settings = self.settings; + + self.classes.add('menu-item'); + + if (settings.menu) { + self.classes.add('menu-item-expand'); + } + + if (settings.preview) { + self.classes.add('menu-item-preview'); + } + + text = self.state.get('text'); + if (text === '-' || text === '|') { + self.classes.add('menu-item-sep'); + self.aria('role', 'separator'); + self.state.set('text', '-'); + } + + if (settings.selectable) { + self.aria('role', 'menuitemcheckbox'); + self.classes.add('menu-item-checkbox'); + settings.icon = 'selected'; + } + + if (!settings.preview && !settings.selectable) { + self.classes.add('menu-item-normal'); + } + + self.on('mousedown', function(e) { + e.preventDefault(); + }); + + if (settings.menu && !settings.ariaHideMenu) { + self.aria('haspopup', true); + } + }, + + /** + * Returns true/false if the menuitem has sub menu. + * + * @method hasMenus + * @return {Boolean} True/false state if it has submenu. + */ + hasMenus: function() { + return !!this.settings.menu; + }, + + /** + * Shows the menu for the menu item. + * + * @method showMenu + */ + showMenu: function() { + var self = this, + settings = self.settings, + menu, parent = self.parent(); + + parent.items().each(function(ctrl) { + if (ctrl !== self) { + ctrl.hideMenu(); + } + }); + + if (settings.menu) { + menu = self.menu; + + if (!menu) { + menu = settings.menu; + + // Is menu array then auto constuct menu control + if (menu.length) { + menu = { + type: 'menu', + items: menu + }; + } else { + menu.type = menu.type || 'menu'; + } + + if (parent.settings.itemDefaults) { + menu.itemDefaults = parent.settings.itemDefaults; + } + + menu = self.menu = Factory.create(menu).parent(self).renderTo(); + menu.reflow(); + menu.on('cancel', function(e) { + e.stopPropagation(); + self.focus(); + menu.hide(); + }); + menu.on('show hide', function(e) { + if (e.control.items) { + e.control.items().each(function(ctrl) { + ctrl.active(ctrl.settings.selected); + }); + } + }).fire('show'); + + menu.on('hide', function(e) { + if (e.control === menu) { + self.classes.remove('selected'); + } + }); + + menu.submenu = true; + } else { + menu.show(); + } + + menu._parentMenu = parent; + + menu.classes.add('menu-sub'); + + var rel = menu.testMoveRel( + self.getEl(), + self.isRtl() ? ['tl-tr', 'bl-br', 'tr-tl', 'br-bl'] : ['tr-tl', 'br-bl', 'tl-tr', 'bl-br'] + ); + + menu.moveRel(self.getEl(), rel); + menu.rel = rel; + + rel = 'menu-sub-' + rel; + menu.classes.remove(menu._lastRel).add(rel); + menu._lastRel = rel; + + self.classes.add('selected'); + self.aria('expanded', true); + } + }, + + /** + * Hides the menu for the menu item. + * + * @method hideMenu + */ + hideMenu: function() { + var self = this; + + if (self.menu) { + self.menu.items().each(function(item) { + if (item.hideMenu) { + item.hideMenu(); + } + }); + + self.menu.hide(); + self.aria('expanded', false); + } + + return self; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + id = self._id, + settings = self.settings, + prefix = self.classPrefix, + text = self.state.get('text'); + var icon = self.settings.icon, + image = '', + shortcut = settings.shortcut; + var url = self.encode(settings.url), + iconHtml = ''; + + // Converts shortcut format to Mac/PC variants + function convertShortcut(shortcut) { + var i, value, replace = {}; + + if (Env.mac) { + replace = { + alt: '⌥', + ctrl: '⌘', + shift: '⇧', + meta: '⌘' + }; + } else { + replace = { + meta: 'Ctrl' + }; + } + + shortcut = shortcut.split('+'); + + for (i = 0; i < shortcut.length; i++) { + value = replace[shortcut[i].toLowerCase()]; + + if (value) { + shortcut[i] = value; + } + } + + return shortcut.join('+'); + } + + function escapeRegExp(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + } + + function markMatches(text) { + var match = settings.match || ''; + + return match ? text.replace(new RegExp(escapeRegExp(match), 'gi'), function(match) { + return '!mce~match[' + match + ']mce~match!'; + }) : text; + } + + function boldMatches(text) { + return text. + replace(new RegExp(escapeRegExp('!mce~match['), 'g'), '<b>'). + replace(new RegExp(escapeRegExp(']mce~match!'), 'g'), '</b>'); + } + + if (icon) { + self.parent().classes.add('menu-has-icons'); + } + + if (settings.image) { + image = ' style="background-image: url(\'' + settings.image + '\')"'; + } + + if (shortcut) { + shortcut = convertShortcut(shortcut); + } + + icon = prefix + 'ico ' + prefix + 'i-' + (self.settings.icon || 'none'); + iconHtml = (text !== '-' ? '<i class="' + icon + '"' + image + '></i>\u00a0' : ''); + + text = boldMatches(self.encode(markMatches(text))); + url = boldMatches(self.encode(markMatches(url))); + + return ( + '<div id="' + id + '" class="' + self.classes + '" tabindex="-1">' + + iconHtml + + (text !== '-' ? '<span id="' + id + '-text" class="' + prefix + 'text">' + text + '</span>' : '') + + (shortcut ? '<div id="' + id + '-shortcut" class="' + prefix + 'menu-shortcut">' + shortcut + '</div>' : '') + + (settings.menu ? '<div class="' + prefix + 'caret"></div>' : '') + + (url ? '<div class="' + prefix + 'menu-item-link">' + url + '</div>' : '') + + '</div>' + ); + }, + + /** + * Gets invoked after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this, + settings = self.settings; + + var textStyle = settings.textStyle; + if (typeof textStyle == "function") { + textStyle = textStyle.call(this); + } + + if (textStyle) { + var textElm = self.getEl('text'); + if (textElm) { + textElm.setAttribute('style', textStyle); + } + } + + self.on('mouseenter click', function(e) { + if (e.control === self) { + if (!settings.menu && e.type === 'click') { + self.fire('select'); + + // Edge will crash if you stress it see #2660 + Delay.requestAnimationFrame(function() { + self.parent().hideAll(); + }); + } else { + self.showMenu(); + + if (e.aria) { + self.menu.focus(true); + } + } + } + }); + + self._super(); + + return self; + }, + + hover: function() { + var self = this; + + self.parent().items().each(function(ctrl) { + ctrl.classes.remove('selected'); + }); + + self.classes.toggle('selected', true); + + return self; + }, + + active: function(state) { + if (typeof state != "undefined") { + this.aria('checked', state); + } + + return this._super(state); + }, + + /** + * Removes the control and it's menus. + * + * @method remove + */ + remove: function() { + this._super(); + + if (this.menu) { + this.menu.remove(); + } + } + }); + } + ); + + /** + * Throbber.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This class enables you to display a Throbber for any element. + * + * @-x-less Throbber.less + * @class tinymce.ui.Throbber + */ + define( + 'tinymce.core.ui.Throbber', [ + "tinymce.core.dom.DomQuery", + "tinymce.core.ui.Control", + "tinymce.core.util.Delay" + ], + function($, Control, Delay) { + "use strict"; + + /** + * Constructs a new throbber. + * + * @constructor + * @param {Element} elm DOM Html element to display throbber in. + * @param {Boolean} inline Optional true/false state if the throbber should be appended to end of element for infinite scroll. + */ + return function(elm, inline) { + var self = this, + state, classPrefix = Control.classPrefix, + timer; + + /** + * Shows the throbber. + * + * @method show + * @param {Number} [time] Time to wait before showing. + * @param {function} [callback] Optional callback to execute when the throbber is shown. + * @return {tinymce.ui.Throbber} Current throbber instance. + */ + self.show = function(time, callback) { + function render() { + if (state) { + $(elm).append( + '<div class="' + classPrefix + 'throbber' + (inline ? ' ' + classPrefix + 'throbber-inline' : '') + '"></div>' + ); + + if (callback) { + callback(); + } + } + } + + self.hide(); + + state = true; + + if (time) { + timer = Delay.setTimeout(render, time); + } else { + render(); + } + + return self; + }; + + /** + * Hides the throbber. + * + * @method hide + * @return {tinymce.ui.Throbber} Current throbber instance. + */ + self.hide = function() { + var child = elm.lastChild; + + Delay.clearTimeout(timer); + + if (child && child.className.indexOf('throbber') != -1) { + child.parentNode.removeChild(child); + } + + state = false; + + return self; + }; + }; + } + ); + + /** + * Menu.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a new menu. + * + * @-x-less Menu.less + * @class tinymce.ui.Menu + * @extends tinymce.ui.FloatPanel + */ + define( + 'tinymce.core.ui.Menu', [ + "tinymce.core.ui.FloatPanel", + "tinymce.core.ui.MenuItem", + "tinymce.core.ui.Throbber", + "tinymce.core.util.Tools" + ], + function(FloatPanel, MenuItem, Throbber, Tools) { + "use strict"; + + return FloatPanel.extend({ + Defaults: { + defaultType: 'menuitem', + border: 1, + layout: 'stack', + role: 'application', + bodyRole: 'menu', + ariaRoot: true + }, + + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + var self = this; + + settings.autohide = true; + settings.constrainToViewport = true; + + if (typeof settings.items === 'function') { + settings.itemsFactory = settings.items; + settings.items = []; + } + + if (settings.itemDefaults) { + var items = settings.items, + i = items.length; + + while (i--) { + items[i] = Tools.extend({}, settings.itemDefaults, items[i]); + } + } + + self._super(settings); + self.classes.add('menu'); + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + this.classes.toggle('menu-align', true); + + this._super(); + + this.getEl().style.height = ''; + this.getEl('body').style.height = ''; + + return this; + }, + + /** + * Hides/closes the menu. + * + * @method cancel + */ + cancel: function() { + var self = this; + + self.hideAll(); + self.fire('select'); + }, + + /** + * Loads new items from the factory items function. + * + * @method load + */ + load: function() { + var self = this, + time, factory; + + function hideThrobber() { + if (self.throbber) { + self.throbber.hide(); + self.throbber = null; + } + } + + factory = self.settings.itemsFactory; + if (!factory) { + return; + } + + if (!self.throbber) { + self.throbber = new Throbber(self.getEl('body'), true); + + if (self.items().length === 0) { + self.throbber.show(); + self.fire('loading'); + } else { + self.throbber.show(100, function() { + self.items().remove(); + self.fire('loading'); + }); + } + + self.on('hide close', hideThrobber); + } + + self.requestTime = time = new Date().getTime(); + + self.settings.itemsFactory(function(items) { + if (items.length === 0) { + self.hide(); + return; + } + + if (self.requestTime !== time) { + return; + } + + self.getEl().style.width = ''; + self.getEl('body').style.width = ''; + + hideThrobber(); + self.items().remove(); + self.getEl('body').innerHTML = ''; + + self.add(items); + self.renderNew(); + self.fire('loaded'); + }); + }, + + /** + * Hide menu and all sub menus. + * + * @method hideAll + */ + hideAll: function() { + var self = this; + + this.find('menuitem').exec('hideMenu'); + + return self._super(); + }, + + /** + * Invoked before the menu is rendered. + * + * @method preRender + */ + preRender: function() { + var self = this; + + self.items().each(function(ctrl) { + var settings = ctrl.settings; + + if (settings.icon || settings.image || settings.selectable) { + self._hasIcons = true; + return false; + } + }); + + if (self.settings.itemsFactory) { + self.on('postrender', function() { + if (self.settings.itemsFactory) { + self.load(); + } + }); + } + + return self._super(); + } + }); + } + ); + + /** + * ListBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a new list box control. + * + * @-x-less ListBox.less + * @class tinymce.ui.ListBox + * @extends tinymce.ui.MenuButton + */ + define( + 'tinymce.core.ui.ListBox', [ + "tinymce.core.ui.MenuButton", + "tinymce.core.ui.Menu" + ], + function(MenuButton, Menu) { + "use strict"; + + return MenuButton.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Array} values Array with values to add to list box. + */ + init: function(settings) { + var self = this, + values, selected, selectedText, lastItemCtrl; + + function setSelected(menuValues) { + // Try to find a selected value + for (var i = 0; i < menuValues.length; i++) { + selected = menuValues[i].selected || settings.value === menuValues[i].value; + + if (selected) { + selectedText = selectedText || menuValues[i].text; + self.state.set('value', menuValues[i].value); + return true; + } + + // If the value has a submenu, try to find the selected values in that menu + if (menuValues[i].menu) { + if (setSelected(menuValues[i].menu)) { + return true; + } + } + } + } + + self._super(settings); + settings = self.settings; + + self._values = values = settings.values; + if (values) { + if (typeof settings.value != "undefined") { + setSelected(values); + } + + // Default with first item + if (!selected && values.length > 0) { + selectedText = values[0].text; + self.state.set('value', values[0].value); + } + + self.state.set('menu', values); + } + + self.state.set('text', settings.text || selectedText); + + self.classes.add('listbox'); + + self.on('select', function(e) { + var ctrl = e.control; + + if (lastItemCtrl) { + e.lastControl = lastItemCtrl; + } + + if (settings.multiple) { + ctrl.active(!ctrl.active()); + } else { + self.value(e.control.value()); + } + + lastItemCtrl = ctrl; + }); + }, + + /** + * Getter/setter function for the control value. + * + * @method value + * @param {String} [value] Value to be set. + * @return {Boolean/tinymce.ui.ListBox} Value or self if it's a set operation. + */ + bindStates: function() { + var self = this; + + function activateMenuItemsByValue(menu, value) { + if (menu instanceof Menu) { + menu.items().each(function(ctrl) { + if (!ctrl.hasMenus()) { + ctrl.active(ctrl.value() === value); + } + }); + } + } + + function getSelectedItem(menuValues, value) { + var selectedItem; + + if (!menuValues) { + return; + } + + for (var i = 0; i < menuValues.length; i++) { + if (menuValues[i].value === value) { + return menuValues[i]; + } + + if (menuValues[i].menu) { + selectedItem = getSelectedItem(menuValues[i].menu, value); + if (selectedItem) { + return selectedItem; + } + } + } + } + + self.on('show', function(e) { + activateMenuItemsByValue(e.control, self.value()); + }); + + self.state.on('change:value', function(e) { + var selectedItem = getSelectedItem(self.state.get('menu'), e.value); + + if (selectedItem) { + self.text(selectedItem.text); + } else { + self.text(self.settings.text); + } + }); + + return self._super(); + } + }); + } + ); + + /** + * Radio.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a new radio button. + * + * @-x-less Radio.less + * @class tinymce.ui.Radio + * @extends tinymce.ui.Checkbox + */ + define( + 'tinymce.core.ui.Radio', [ + "tinymce.core.ui.Checkbox" + ], + function(Checkbox) { + "use strict"; + + return Checkbox.extend({ + Defaults: { + classes: "radio", + role: "radio" + } + }); + } + ); + /** + * ResizeHandle.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Renders a resize handle that fires ResizeStart, Resize and ResizeEnd events. + * + * @-x-less ResizeHandle.less + * @class tinymce.ui.ResizeHandle + * @extends tinymce.ui.Widget + */ + define( + 'tinymce.core.ui.ResizeHandle', [ + "tinymce.core.ui.Widget", + "tinymce.core.ui.DragHelper" + ], + function(Widget, DragHelper) { + "use strict"; + + return Widget.extend({ + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + prefix = self.classPrefix; + + self.classes.add('resizehandle'); + + if (self.settings.direction == "both") { + self.classes.add('resizehandle-both'); + } + + self.canFocus = false; + + return ( + '<div id="' + self._id + '" class="' + self.classes + '">' + + '<i class="' + prefix + 'ico ' + prefix + 'i-resize"></i>' + + '</div>' + ); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this; + + self._super(); + + self.resizeDragHelper = new DragHelper(this._id, { + start: function() { + self.fire('ResizeStart'); + }, + + drag: function(e) { + if (self.settings.direction != "both") { + e.deltaX = 0; + } + + self.fire('Resize', e); + }, + + stop: function() { + self.fire('ResizeEnd'); + } + }); + }, + + remove: function() { + if (this.resizeDragHelper) { + this.resizeDragHelper.destroy(); + } + + return this._super(); + } + }); + } + ); + + /** + * SelectBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a new select box control. + * + * @-x-less SelectBox.less + * @class tinymce.ui.SelectBox + * @extends tinymce.ui.Widget + */ + define( + 'tinymce.core.ui.SelectBox', [ + "tinymce.core.ui.Widget" + ], + function(Widget) { + "use strict"; + + function createOptions(options) { + var strOptions = ''; + if (options) { + for (var i = 0; i < options.length; i++) { + strOptions += '<option value="' + options[i] + '">' + options[i] + '</option>'; + } + } + return strOptions; + } + + return Widget.extend({ + Defaults: { + classes: "selectbox", + role: "selectbox", + options: [] + }, + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Array} options Array with options to add to the select box. + */ + init: function(settings) { + var self = this; + + self._super(settings); + + if (self.settings.size) { + self.size = self.settings.size; + } + + if (self.settings.options) { + self._options = self.settings.options; + } + + self.on('keydown', function(e) { + var rootControl; + + if (e.keyCode == 13) { + e.preventDefault(); + + // Find root control that we can do toJSON on + self.parents().reverse().each(function(ctrl) { + if (ctrl.toJSON) { + rootControl = ctrl; + return false; + } + }); + + // Fire event on current text box with the serialized data of the whole form + self.fire('submit', { + data: rootControl.toJSON() + }); + } + }); + }, + + /** + * Getter/setter function for the options state. + * + * @method options + * @param {Array} [state] State to be set. + * @return {Array|tinymce.ui.SelectBox} Array of string options. + */ + options: function(state) { + if (!arguments.length) { + return this.state.get('options'); + } + + this.state.set('options', state); + + return this; + }, + + renderHtml: function() { + var self = this, + options, size = ''; + + options = createOptions(self._options); + + if (self.size) { + size = ' size = "' + self.size + '"'; + } + + return ( + '<select id="' + self._id + '" class="' + self.classes + '"' + size + '>' + + options + + '</select>' + ); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:options', function(e) { + self.getEl().innerHTML = createOptions(e.value); + }); + + return self._super(); + } + }); + } + ); + + /** + * Slider.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Slider control. + * + * @-x-less Slider.less + * @class tinymce.ui.Slider + * @extends tinymce.ui.Widget + */ + define( + 'tinymce.core.ui.Slider', [ + "tinymce.core.ui.Widget", + "tinymce.core.ui.DragHelper", + "tinymce.core.ui.DomUtils" + ], + function(Widget, DragHelper, DomUtils) { + "use strict"; + + function constrain(value, minVal, maxVal) { + if (value < minVal) { + value = minVal; + } + + if (value > maxVal) { + value = maxVal; + } + + return value; + } + + function setAriaProp(el, name, value) { + el.setAttribute('aria-' + name, value); + } + + function updateSliderHandle(ctrl, value) { + var maxHandlePos, shortSizeName, sizeName, stylePosName, styleValue, handleEl; + + if (ctrl.settings.orientation == "v") { + stylePosName = "top"; + sizeName = "height"; + shortSizeName = "h"; + } else { + stylePosName = "left"; + sizeName = "width"; + shortSizeName = "w"; + } + + handleEl = ctrl.getEl('handle'); + maxHandlePos = (ctrl.layoutRect()[shortSizeName] || 100) - DomUtils.getSize(handleEl)[sizeName]; + + styleValue = (maxHandlePos * ((value - ctrl._minValue) / (ctrl._maxValue - ctrl._minValue))) + 'px'; + handleEl.style[stylePosName] = styleValue; + handleEl.style.height = ctrl.layoutRect().h + 'px'; + + setAriaProp(handleEl, 'valuenow', value); + setAriaProp(handleEl, 'valuetext', '' + ctrl.settings.previewFilter(value)); + setAriaProp(handleEl, 'valuemin', ctrl._minValue); + setAriaProp(handleEl, 'valuemax', ctrl._maxValue); + } + + return Widget.extend({ + init: function(settings) { + var self = this; + + if (!settings.previewFilter) { + settings.previewFilter = function(value) { + return Math.round(value * 100) / 100.0; + }; + } + + self._super(settings); + self.classes.add('slider'); + + if (settings.orientation == "v") { + self.classes.add('vertical'); + } + + self._minValue = settings.minValue || 0; + self._maxValue = settings.maxValue || 100; + self._initValue = self.state.get('value'); + }, + + renderHtml: function() { + var self = this, + id = self._id, + prefix = self.classPrefix; + + return ( + '<div id="' + id + '" class="' + self.classes + '">' + + '<div id="' + id + '-handle" class="' + prefix + 'slider-handle" role="slider" tabindex="-1"></div>' + + '</div>' + ); + }, + + reset: function() { + this.value(this._initValue).repaint(); + }, + + postRender: function() { + var self = this, + minValue, maxValue, screenCordName, + stylePosName, sizeName, shortSizeName; + + function toFraction(min, max, val) { + return (val + min) / (max - min); + } + + function fromFraction(min, max, val) { + return (val * (max - min)) - min; + } + + function handleKeyboard(minValue, maxValue) { + function alter(delta) { + var value; + + value = self.value(); + value = fromFraction(minValue, maxValue, toFraction(minValue, maxValue, value) + (delta * 0.05)); + value = constrain(value, minValue, maxValue); + + self.value(value); + + self.fire('dragstart', { + value: value + }); + self.fire('drag', { + value: value + }); + self.fire('dragend', { + value: value + }); + } + + self.on('keydown', function(e) { + switch (e.keyCode) { + case 37: + case 38: + alter(-1); + break; + + case 39: + case 40: + alter(1); + break; + } + }); + } + + function handleDrag(minValue, maxValue, handleEl) { + var startPos, startHandlePos, maxHandlePos, handlePos, value; + + self._dragHelper = new DragHelper(self._id, { + handle: self._id + "-handle", + + start: function(e) { + startPos = e[screenCordName]; + startHandlePos = parseInt(self.getEl('handle').style[stylePosName], 10); + maxHandlePos = (self.layoutRect()[shortSizeName] || 100) - DomUtils.getSize(handleEl)[sizeName]; + self.fire('dragstart', { + value: value + }); + }, + + drag: function(e) { + var delta = e[screenCordName] - startPos; + + handlePos = constrain(startHandlePos + delta, 0, maxHandlePos); + handleEl.style[stylePosName] = handlePos + 'px'; + + value = minValue + (handlePos / maxHandlePos) * (maxValue - minValue); + self.value(value); + + self.tooltip().text('' + self.settings.previewFilter(value)).show().moveRel(handleEl, 'bc tc'); + + self.fire('drag', { + value: value + }); + }, + + stop: function() { + self.tooltip().hide(); + self.fire('dragend', { + value: value + }); + } + }); + } + + minValue = self._minValue; + maxValue = self._maxValue; + + if (self.settings.orientation == "v") { + screenCordName = "screenY"; + stylePosName = "top"; + sizeName = "height"; + shortSizeName = "h"; + } else { + screenCordName = "screenX"; + stylePosName = "left"; + sizeName = "width"; + shortSizeName = "w"; + } + + self._super(); + + handleKeyboard(minValue, maxValue, self.getEl('handle')); + handleDrag(minValue, maxValue, self.getEl('handle')); + }, + + repaint: function() { + this._super(); + updateSliderHandle(this, this.value()); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:value', function(e) { + updateSliderHandle(self, e.value); + }); + + return self._super(); + } + }); + } + ); + /** + * Spacer.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a spacer. This control is used in flex layouts for example. + * + * @-x-less Spacer.less + * @class tinymce.ui.Spacer + * @extends tinymce.ui.Widget + */ + define( + 'tinymce.core.ui.Spacer', [ + "tinymce.core.ui.Widget" + ], + function(Widget) { + "use strict"; + + return Widget.extend({ + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this; + + self.classes.add('spacer'); + self.canFocus = false; + + return '<div id="' + self._id + '" class="' + self.classes + '"></div>'; + } + }); + } + ); + /** + * SplitButton.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a split button. + * + * @-x-less SplitButton.less + * @class tinymce.ui.SplitButton + * @extends tinymce.ui.Button + */ + define( + 'tinymce.core.ui.SplitButton', [ + "tinymce.core.ui.MenuButton", + "tinymce.core.ui.DomUtils", + "tinymce.core.dom.DomQuery" + ], + function(MenuButton, DomUtils, $) { + return MenuButton.extend({ + Defaults: { + classes: "widget btn splitbtn", + role: "button" + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this, + elm = self.getEl(), + rect = self.layoutRect(), + mainButtonElm, menuButtonElm; + + self._super(); + + mainButtonElm = elm.firstChild; + menuButtonElm = elm.lastChild; + + $(mainButtonElm).css({ + width: rect.w - DomUtils.getSize(menuButtonElm).width, + height: rect.h - 2 + }); + + $(menuButtonElm).css({ + height: rect.h - 2 + }); + + return self; + }, + + /** + * Sets the active menu state. + * + * @private + */ + activeMenu: function(state) { + var self = this; + + $(self.getEl().lastChild).toggleClass(self.classPrefix + 'active', state); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + id = self._id, + prefix = self.classPrefix, + image; + var icon = self.state.get('icon'), + text = self.state.get('text'), + textHtml = ''; + + image = self.settings.image; + if (image) { + icon = 'none'; + + // Support for [high dpi, low dpi] image sources + if (typeof image != "string") { + image = window.getSelection ? image[0] : image[1]; + } + + image = ' style="background-image: url(\'' + image + '\')"'; + } else { + image = ''; + } + + icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : ''; + + if (text) { + self.classes.add('btn-has-text'); + textHtml = '<span class="' + prefix + 'txt">' + self.encode(text) + '</span>'; + } + + return ( + '<div id="' + id + '" class="' + self.classes + '" role="button" tabindex="-1">' + + '<button type="button" hidefocus="1" tabindex="-1">' + + (icon ? '<i class="' + icon + '"' + image + '></i>' : '') + + textHtml + + '</button>' + + '<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' + + //(icon ? '<i class="' + icon + '"></i>' : '') + + (self._menuBtnText ? (icon ? '\u00a0' : '') + self._menuBtnText : '') + + ' <i class="' + prefix + 'caret"></i>' + + '</button>' + + '</div>' + ); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this, + onClickHandler = self.settings.onclick; + + self.on('click', function(e) { + var node = e.target; + + if (e.control == this) { + // Find clicks that is on the main button + while (node) { + if ((e.aria && e.aria.key != 'down') || (node.nodeName == 'BUTTON' && node.className.indexOf('open') == -1)) { + e.stopImmediatePropagation(); + + if (onClickHandler) { + onClickHandler.call(this, e); + } + + return; + } + + node = node.parentNode; + } + } + }); + + delete self.settings.onclick; + + return self._super(); + } + }); + } + ); + /** + * StackLayout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This layout uses the browsers layout when the items are blocks. + * + * @-x-less StackLayout.less + * @class tinymce.ui.StackLayout + * @extends tinymce.ui.FlowLayout + */ + define( + 'tinymce.core.ui.StackLayout', [ + "tinymce.core.ui.FlowLayout" + ], + function(FlowLayout) { + "use strict"; + + return FlowLayout.extend({ + Defaults: { + containerClass: 'stack-layout', + controlClass: 'stack-layout-item', + endClass: 'break' + }, + + isNative: function() { + return true; + } + }); + } + ); + /** + * TabPanel.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a tab panel control. + * + * @-x-less TabPanel.less + * @class tinymce.ui.TabPanel + * @extends tinymce.ui.Panel + * + * @setting {Number} activeTab Active tab index. + */ + define( + 'tinymce.core.ui.TabPanel', [ + "tinymce.core.ui.Panel", + "tinymce.core.dom.DomQuery", + "tinymce.core.ui.DomUtils" + ], + function(Panel, $, DomUtils) { + "use strict"; + + return Panel.extend({ + Defaults: { + layout: 'absolute', + defaults: { + type: 'panel' + } + }, + + /** + * Activates the specified tab by index. + * + * @method activateTab + * @param {Number} idx Index of the tab to activate. + */ + activateTab: function(idx) { + var activeTabElm; + + if (this.activeTabId) { + activeTabElm = this.getEl(this.activeTabId); + $(activeTabElm).removeClass(this.classPrefix + 'active'); + activeTabElm.setAttribute('aria-selected', "false"); + } + + this.activeTabId = 't' + idx; + + activeTabElm = this.getEl('t' + idx); + activeTabElm.setAttribute('aria-selected', "true"); + $(activeTabElm).addClass(this.classPrefix + 'active'); + + this.items()[idx].show().fire('showtab'); + this.reflow(); + + this.items().each(function(item, i) { + if (idx != i) { + item.hide(); + } + }); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + layout = self._layout, + tabsHtml = '', + prefix = self.classPrefix; + + self.preRender(); + layout.preRender(self); + + self.items().each(function(ctrl, i) { + var id = self._id + '-t' + i; + + ctrl.aria('role', 'tabpanel'); + ctrl.aria('labelledby', id); + + tabsHtml += ( + '<div id="' + id + '" class="' + prefix + 'tab" ' + + 'unselectable="on" role="tab" aria-controls="' + ctrl._id + '" aria-selected="false" tabIndex="-1">' + + self.encode(ctrl.settings.title) + + '</div>' + ); + }); + + return ( + '<div id="' + self._id + '" class="' + self.classes + '" hidefocus="1" tabindex="-1">' + + '<div id="' + self._id + '-head" class="' + prefix + 'tabs" role="tablist">' + + tabsHtml + + '</div>' + + '<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' + + layout.renderHtml(self) + + '</div>' + + '</div>' + ); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this; + + self._super(); + + self.settings.activeTab = self.settings.activeTab || 0; + self.activateTab(self.settings.activeTab); + + this.on('click', function(e) { + var targetParent = e.target.parentNode; + + if (targetParent && targetParent.id == self._id + '-head') { + var i = targetParent.childNodes.length; + + while (i--) { + if (targetParent.childNodes[i] == e.target) { + self.activateTab(i); + } + } + } + }); + }, + + /** + * Initializes the current controls layout rect. + * This will be executed by the layout managers to determine the + * default minWidth/minHeight etc. + * + * @method initLayoutRect + * @return {Object} Layout rect instance. + */ + initLayoutRect: function() { + var self = this, + rect, minW, minH; + + minW = DomUtils.getSize(self.getEl('head')).width; + minW = minW < 0 ? 0 : minW; + minH = 0; + + self.items().each(function(item) { + minW = Math.max(minW, item.layoutRect().minW); + minH = Math.max(minH, item.layoutRect().minH); + }); + + self.items().each(function(ctrl) { + ctrl.settings.x = 0; + ctrl.settings.y = 0; + ctrl.settings.w = minW; + ctrl.settings.h = minH; + + ctrl.layoutRect({ + x: 0, + y: 0, + w: minW, + h: minH + }); + }); + + var headH = DomUtils.getSize(self.getEl('head')).height; + + self.settings.minWidth = minW; + self.settings.minHeight = minH + headH; + + rect = self._super(); + rect.deltaH += headH; + rect.innerH = rect.h - rect.deltaH; + + return rect; + } + }); + } + ); + + /** + * TextBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * Creates a new textbox. + * + * @-x-less TextBox.less + * @class tinymce.ui.TextBox + * @extends tinymce.ui.Widget + */ + define( + 'tinymce.core.ui.TextBox', [ + "tinymce.core.ui.Widget", + "tinymce.core.util.Tools", + "tinymce.core.ui.DomUtils" + ], + function(Widget, Tools, DomUtils) { + return Widget.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Boolean} multiline True if the textbox is a multiline control. + * @setting {Number} maxLength Max length for the textbox. + * @setting {Number} size Size of the textbox in characters. + */ + init: function(settings) { + var self = this; + + self._super(settings); + + self.classes.add('textbox'); + + if (settings.multiline) { + self.classes.add('multiline'); + } else { + self.on('keydown', function(e) { + var rootControl; + + if (e.keyCode == 13) { + e.preventDefault(); + + // Find root control that we can do toJSON on + self.parents().reverse().each(function(ctrl) { + if (ctrl.toJSON) { + rootControl = ctrl; + return false; + } + }); + + // Fire event on current text box with the serialized data of the whole form + self.fire('submit', { + data: rootControl.toJSON() + }); + } + }); + + self.on('keyup', function(e) { + self.state.set('value', e.target.value); + }); + } + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this, + style, rect, borderBox, borderW, borderH = 0, + lastRepaintRect; + + style = self.getEl().style; + rect = self._layoutRect; + lastRepaintRect = self._lastRepaintRect || {}; + + // Detect old IE 7+8 add lineHeight to align caret vertically in the middle + var doc = document; + if (!self.settings.multiline && doc.all && (!doc.documentMode || doc.documentMode <= 8)) { + style.lineHeight = (rect.h - borderH) + 'px'; + } + + borderBox = self.borderBox; + borderW = borderBox.left + borderBox.right + 8; + borderH = borderBox.top + borderBox.bottom + (self.settings.multiline ? 8 : 0); + + if (rect.x !== lastRepaintRect.x) { + style.left = rect.x + 'px'; + lastRepaintRect.x = rect.x; + } + + if (rect.y !== lastRepaintRect.y) { + style.top = rect.y + 'px'; + lastRepaintRect.y = rect.y; + } + + if (rect.w !== lastRepaintRect.w) { + style.width = (rect.w - borderW) + 'px'; + lastRepaintRect.w = rect.w; + } + + if (rect.h !== lastRepaintRect.h) { + style.height = (rect.h - borderH) + 'px'; + lastRepaintRect.h = rect.h; + } + + self._lastRepaintRect = lastRepaintRect; + self.fire('repaint', {}, false); + + return self; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, + settings = self.settings, + attrs, elm; + + attrs = { + id: self._id, + hidefocus: '1' + }; + + Tools.each([ + 'rows', 'spellcheck', 'maxLength', 'size', 'readonly', 'min', + 'max', 'step', 'list', 'pattern', 'placeholder', 'required', 'multiple' + ], function(name) { + attrs[name] = settings[name]; + }); + + if (self.disabled()) { + attrs.disabled = 'disabled'; + } + + if (settings.subtype) { + attrs.type = settings.subtype; + } + + elm = DomUtils.create(settings.multiline ? 'textarea' : 'input', attrs); + elm.value = self.state.get('value'); + elm.className = self.classes; + + return elm.outerHTML; + }, + + value: function(value) { + if (arguments.length) { + this.state.set('value', value); + return this; + } + + // Make sure the real state is in sync + if (this.state.get('rendered')) { + this.state.set('value', this.getEl().value); + } + + return this.state.get('value'); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this; + + self.getEl().value = self.state.get('value'); + self._super(); + + self.$el.on('change', function(e) { + self.state.set('value', e.target.value); + self.fire('change', e); + }); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:value', function(e) { + if (self.getEl().value != e.value) { + self.getEl().value = e.value; + } + }); + + self.state.on('change:disabled', function(e) { + self.getEl().disabled = e.value; + }); + + return self._super(); + }, + + remove: function() { + this.$el.off(); + this._super(); + } + }); + } + ); + + /** + * Api.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.ui.Api', [ + 'tinymce.core.ui.Selector', + 'tinymce.core.ui.Collection', + 'tinymce.core.ui.ReflowQueue', + 'tinymce.core.ui.Control', + 'tinymce.core.ui.Factory', + 'tinymce.core.ui.KeyboardNavigation', + 'tinymce.core.ui.Container', + 'tinymce.core.ui.DragHelper', + 'tinymce.core.ui.Scrollable', + 'tinymce.core.ui.Panel', + 'tinymce.core.ui.Movable', + 'tinymce.core.ui.Resizable', + 'tinymce.core.ui.FloatPanel', + 'tinymce.core.ui.Window', + 'tinymce.core.ui.MessageBox', + 'tinymce.core.ui.Tooltip', + 'tinymce.core.ui.Widget', + 'tinymce.core.ui.Progress', + 'tinymce.core.ui.Notification', + 'tinymce.core.ui.Layout', + 'tinymce.core.ui.AbsoluteLayout', + 'tinymce.core.ui.Button', + 'tinymce.core.ui.ButtonGroup', + 'tinymce.core.ui.Checkbox', + 'tinymce.core.ui.ComboBox', + 'tinymce.core.ui.ColorBox', + 'tinymce.core.ui.PanelButton', + 'tinymce.core.ui.ColorButton', + 'tinymce.core.ui.ColorPicker', + 'tinymce.core.ui.Path', + 'tinymce.core.ui.ElementPath', + 'tinymce.core.ui.FormItem', + 'tinymce.core.ui.Form', + 'tinymce.core.ui.FieldSet', + 'tinymce.core.ui.FilePicker', + 'tinymce.core.ui.FitLayout', + 'tinymce.core.ui.FlexLayout', + 'tinymce.core.ui.FlowLayout', + 'tinymce.core.ui.FormatControls', + 'tinymce.core.ui.GridLayout', + 'tinymce.core.ui.Iframe', + 'tinymce.core.ui.InfoBox', + 'tinymce.core.ui.Label', + 'tinymce.core.ui.Toolbar', + 'tinymce.core.ui.MenuBar', + 'tinymce.core.ui.MenuButton', + 'tinymce.core.ui.MenuItem', + 'tinymce.core.ui.Throbber', + 'tinymce.core.ui.Menu', + 'tinymce.core.ui.ListBox', + 'tinymce.core.ui.Radio', + 'tinymce.core.ui.ResizeHandle', + 'tinymce.core.ui.SelectBox', + 'tinymce.core.ui.Slider', + 'tinymce.core.ui.Spacer', + 'tinymce.core.ui.SplitButton', + 'tinymce.core.ui.StackLayout', + 'tinymce.core.ui.TabPanel', + 'tinymce.core.ui.TextBox' + ], + function( + Selector, Collection, ReflowQueue, Control, Factory, KeyboardNavigation, Container, DragHelper, Scrollable, Panel, Movable, + Resizable, FloatPanel, Window, MessageBox, Tooltip, Widget, Progress, Notification, Layout, AbsoluteLayout, Button, + ButtonGroup, Checkbox, ComboBox, ColorBox, PanelButton, ColorButton, ColorPicker, Path, ElementPath, FormItem, Form, + FieldSet, FilePicker, FitLayout, FlexLayout, FlowLayout, FormatControls, GridLayout, Iframe, InfoBox, Label, Toolbar, + MenuBar, MenuButton, MenuItem, Throbber, Menu, ListBox, Radio, ResizeHandle, SelectBox, Slider, Spacer, SplitButton, + StackLayout, TabPanel, TextBox + ) { + "use strict"; + + var registerToFactory = function(id, ref) { + Factory.add(id.split('.').pop(), ref); + }; + + var expose = function(target, id, ref) { + var i, fragments; + + fragments = id.split(/[.\/]/); + for (i = 0; i < fragments.length - 1; ++i) { + if (target[fragments[i]] === undefined) { + target[fragments[i]] = {}; + } + + target = target[fragments[i]]; + } + + target[fragments[fragments.length - 1]] = ref; + + registerToFactory(id, ref); + }; + + var appendTo = function(target) { + expose(target, 'ui.Selector', Selector); + expose(target, 'ui.Collection', Collection); + expose(target, 'ui.ReflowQueue', ReflowQueue); + expose(target, 'ui.Control', Control); + expose(target, 'ui.Factory', Factory); + expose(target, 'ui.KeyboardNavigation', KeyboardNavigation); + expose(target, 'ui.Container', Container); + expose(target, 'ui.DragHelper', DragHelper); + expose(target, 'ui.Scrollable', Scrollable); + expose(target, 'ui.Panel', Panel); + expose(target, 'ui.Movable', Movable); + expose(target, 'ui.Resizable', Resizable); + expose(target, 'ui.FloatPanel', FloatPanel); + expose(target, 'ui.Window', Window); + expose(target, 'ui.MessageBox', MessageBox); + expose(target, 'ui.Tooltip', Tooltip); + expose(target, 'ui.Widget', Widget); + expose(target, 'ui.Progress', Progress); + expose(target, 'ui.Notification', Notification); + expose(target, 'ui.Layout', Layout); + expose(target, 'ui.AbsoluteLayout', AbsoluteLayout); + expose(target, 'ui.Button', Button); + expose(target, 'ui.ButtonGroup', ButtonGroup); + expose(target, 'ui.Checkbox', Checkbox); + expose(target, 'ui.ComboBox', ComboBox); + expose(target, 'ui.ColorBox', ColorBox); + expose(target, 'ui.PanelButton', PanelButton); + expose(target, 'ui.ColorButton', ColorButton); + expose(target, 'ui.ColorPicker', ColorPicker); + expose(target, 'ui.Path', Path); + expose(target, 'ui.ElementPath', ElementPath); + expose(target, 'ui.FormItem', FormItem); + expose(target, 'ui.Form', Form); + expose(target, 'ui.FieldSet', FieldSet); + expose(target, 'ui.FilePicker', FilePicker); + expose(target, 'ui.FitLayout', FitLayout); + expose(target, 'ui.FlexLayout', FlexLayout); + expose(target, 'ui.FlowLayout', FlowLayout); + expose(target, 'ui.FormatControls', FormatControls); + expose(target, 'ui.GridLayout', GridLayout); + expose(target, 'ui.Iframe', Iframe); + expose(target, 'ui.InfoBox', InfoBox); + expose(target, 'ui.Label', Label); + expose(target, 'ui.Toolbar', Toolbar); + expose(target, 'ui.MenuBar', MenuBar); + expose(target, 'ui.MenuButton', MenuButton); + expose(target, 'ui.MenuItem', MenuItem); + expose(target, 'ui.Throbber', Throbber); + expose(target, 'ui.Menu', Menu); + expose(target, 'ui.ListBox', ListBox); + expose(target, 'ui.Radio', Radio); + expose(target, 'ui.ResizeHandle', ResizeHandle); + expose(target, 'ui.SelectBox', SelectBox); + expose(target, 'ui.Slider', Slider); + expose(target, 'ui.Spacer', Spacer); + expose(target, 'ui.SplitButton', SplitButton); + expose(target, 'ui.StackLayout', StackLayout); + expose(target, 'ui.TabPanel', TabPanel); + expose(target, 'ui.TextBox', TextBox); + expose(target, 'ui.Api', Api); + }; + + var Api = { + appendTo: appendTo + }; + + return Api; + } + ); + /** + * Tinymce.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.api.Tinymce', [ + 'tinymce.core.geom.Rect', + 'tinymce.core.util.Promise', + 'tinymce.core.util.Delay', + 'tinymce.core.Env', + 'tinymce.core.dom.EventUtils', + 'tinymce.core.dom.Sizzle', + 'tinymce.core.util.Tools', + 'tinymce.core.dom.DomQuery', + 'tinymce.core.html.Styles', + 'tinymce.core.dom.TreeWalker', + 'tinymce.core.html.Entities', + 'tinymce.core.dom.DOMUtils', + 'tinymce.core.dom.ScriptLoader', + 'tinymce.core.AddOnManager', + 'tinymce.core.dom.RangeUtils', + 'tinymce.core.html.Node', + 'tinymce.core.html.Schema', + 'tinymce.core.html.SaxParser', + 'tinymce.core.html.DomParser', + 'tinymce.core.html.Writer', + 'tinymce.core.html.Serializer', + 'tinymce.core.dom.Serializer', + 'tinymce.core.util.VK', + 'tinymce.core.dom.ControlSelection', + 'tinymce.core.dom.BookmarkManager', + 'tinymce.core.dom.Selection', + 'tinymce.core.Formatter', + 'tinymce.core.UndoManager', + 'tinymce.core.EditorCommands', + 'tinymce.core.util.URI', + 'tinymce.core.util.Class', + 'tinymce.core.util.EventDispatcher', + 'tinymce.core.util.Observable', + 'tinymce.core.WindowManager', + 'tinymce.core.NotificationManager', + 'tinymce.core.EditorObservable', + 'tinymce.core.Shortcuts', + 'tinymce.core.Editor', + 'tinymce.core.util.I18n', + 'tinymce.core.FocusManager', + 'tinymce.core.EditorManager', + 'tinymce.core.util.XHR', + 'tinymce.core.util.JSON', + 'tinymce.core.util.JSONRequest', + 'tinymce.core.util.JSONP', + 'tinymce.core.util.LocalStorage', + 'tinymce.core.api.Compat', + 'tinymce.core.util.Color', + 'tinymce.core.ui.Api' + ], + function( + Rect, Promise, Delay, Env, EventUtils, Sizzle, Tools, DomQuery, Styles, TreeWalker, Entities, DOMUtils, ScriptLoader, AddOnManager, + RangeUtils, Node, Schema, SaxParser, DomParser, Writer, HtmlSerializer, DomSerializer, VK, ControlSelection, BookmarkManager, Selection, + Formatter, UndoManager, EditorCommands, URI, Class, EventDispatcher, Observable, WindowManager, + NotificationManager, EditorObservable, Shortcuts, Editor, I18n, FocusManager, EditorManager, + XHR, JSON, JSONRequest, JSONP, LocalStorage, Compat, Color, Api + ) { + var tinymce = EditorManager; + + var expose = function(target, id, ref) { + var i, fragments; + + fragments = id.split(/[.\/]/); + for (i = 0; i < fragments.length - 1; ++i) { + if (target[fragments[i]] === undefined) { + target[fragments[i]] = {}; + } + + target = target[fragments[i]]; + } + + target[fragments[fragments.length - 1]] = ref; + }; + + expose(tinymce, 'geom.Rect', Rect); + expose(tinymce, 'util.Promise', Promise); + expose(tinymce, 'util.Delay', Delay); + expose(tinymce, 'Env', Env); + expose(tinymce, 'dom.EventUtils', EventUtils); + expose(tinymce, 'dom.Sizzle', Sizzle); + expose(tinymce, 'util.Tools', Tools); + expose(tinymce, 'dom.DomQuery', DomQuery); + expose(tinymce, 'html.Styles', Styles); + expose(tinymce, 'dom.TreeWalker', TreeWalker); + expose(tinymce, 'html.Entities', Entities); + expose(tinymce, 'dom.DOMUtils', DOMUtils); + expose(tinymce, 'dom.ScriptLoader', ScriptLoader); + expose(tinymce, 'AddOnManager', AddOnManager); + expose(tinymce, 'dom.RangeUtils', RangeUtils); + expose(tinymce, 'html.Node', Node); + expose(tinymce, 'html.Schema', Schema); + expose(tinymce, 'html.SaxParser', SaxParser); + expose(tinymce, 'html.DomParser', DomParser); + expose(tinymce, 'html.Writer', Writer); + expose(tinymce, 'html.Serializer', HtmlSerializer); + expose(tinymce, 'dom.Serializer', DomSerializer); + expose(tinymce, 'util.VK', VK); + expose(tinymce, 'dom.ControlSelection', ControlSelection); + expose(tinymce, 'dom.BookmarkManager', BookmarkManager); + expose(tinymce, 'dom.Selection', Selection); + expose(tinymce, 'Formatter', Formatter); + expose(tinymce, 'UndoManager', UndoManager); + expose(tinymce, 'EditorCommands', EditorCommands); + expose(tinymce, 'util.URI', URI); + expose(tinymce, 'util.Class', Class); + expose(tinymce, 'util.EventDispatcher', EventDispatcher); + expose(tinymce, 'util.Observable', Observable); + expose(tinymce, 'WindowManager', WindowManager); + expose(tinymce, 'NotificationManager', NotificationManager); + expose(tinymce, 'EditorObservable', EditorObservable); + expose(tinymce, 'Shortcuts', Shortcuts); + expose(tinymce, 'Editor', Editor); + expose(tinymce, 'util.I18n', I18n); + expose(tinymce, 'FocusManager', FocusManager); + expose(tinymce, 'EditorManager', EditorManager); + expose(tinymce, 'util.XHR', XHR); + expose(tinymce, 'util.JSON', JSON); + expose(tinymce, 'util.JSONRequest', JSONRequest); + expose(tinymce, 'util.JSONP', JSONP); + expose(tinymce, 'util.LocalStorage', LocalStorage); + expose(tinymce, 'Compat', Compat); + expose(tinymce, 'util.Color', Color); + + Api.appendTo(tinymce); + + Compat.register(tinymce); + + return tinymce; + } + ); + + /** + * Register.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + /** + * This registers tinymce in common module loaders. + * + * @private + * @class tinymce.Register + */ + define( + 'tinymce.core.Register', [], + function() { + /*eslint consistent-this: 0 */ + var context = this || window; + + var exposeToModuleLoaders = function(tinymce) { + if (typeof context.define === "function") { + // Bolt + if (!context.define.amd) { + context.define("ephox/tinymce", [], function() { + return tinymce; + }); + + context.define("tinymce.core.EditorManager", [], function() { + return tinymce; + }); + } + } + + if (typeof module === 'object') { + /* global module */ + module.exports = tinymce; + } + }; + + return { + exposeToModuleLoaders: exposeToModuleLoaders + }; + } + ); + + /** + * Main.js + * + * Released under LGPL License. + * Copyright (c) 1999-2017 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + + define( + 'tinymce.core.api.Main', [ + 'tinymce.core.api.Tinymce', + 'tinymce.core.Register' + ], + function(tinymce, Register) { + return function() { + window.tinymce = tinymce; + window.tinyMCE = tinymce; + Register.exposeToModuleLoaders(tinymce); + return tinymce; + }; + } + ); + + dem('tinymce.core.api.Main')(); +})(); \ No newline at end of file From 583bee6e311d2e0002b80a253f4f8701395c92da Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Wed, 1 Aug 2018 13:02:23 -0500 Subject: [PATCH 0673/1171] MC-3309: Build stabilization Revert changes --- lib/web/mage/adminhtml/events.js | 5 - lib/web/tiny_mce_4/tinymce.min.js | 57095 +--------------------------- 2 files changed, 15 insertions(+), 57085 deletions(-) diff --git a/lib/web/mage/adminhtml/events.js b/lib/web/mage/adminhtml/events.js index 2231e28217b5d..67e04c5ca9c15 100644 --- a/lib/web/mage/adminhtml/events.js +++ b/lib/web/mage/adminhtml/events.js @@ -134,11 +134,6 @@ define([ results = [], result, len, i, eventArgs, method, eventHandler; - if (eventName.toLowerCase().indexOf('tinymce') !== -1) { - // console.log(eventName, tinyMCE.activeEditor ? tinyMCE.activeEditor.getBody().innerHTML : ''); - // console.trace(); - } - if (this.arrEvents[evtName] != null) { len = this.arrEvents[evtName].length; //optimization diff --git a/lib/web/tiny_mce_4/tinymce.min.js b/lib/web/tiny_mce_4/tinymce.min.js index 8515aa6fdf132..d2d51d9d3ce38 100755 --- a/lib/web/tiny_mce_4/tinymce.min.js +++ b/lib/web/tiny_mce_4/tinymce.min.js @@ -1,57081 +1,16 @@ // 4.6.4 (2017-06-13) -(function() { - - var defs = {}; // id -> {dependencies, definition, instance (possibly undefined)} - - // Used when there is no 'main' module. - // The name is probably (hopefully) unique so minification removes for releases. - var register_3795 = function(id) { - var module = dem(id); - var fragments = id.split('.'); - var target = Function('return this;')(); - for (var i = 0; i < fragments.length - 1; ++i) { - if (target[fragments[i]] === undefined) - target[fragments[i]] = {}; - target = target[fragments[i]]; - } - target[fragments[fragments.length - 1]] = module; - }; - - var instantiate = function(id) { - var actual = defs[id]; - var dependencies = actual.deps; - var definition = actual.defn; - var len = dependencies.length; - var instances = new Array(len); - for (var i = 0; i < len; ++i) - instances[i] = dem(dependencies[i]); - var defResult = definition.apply(null, instances); - if (defResult === undefined) - throw 'module [' + id + '] returned undefined'; - actual.instance = defResult; - }; - - var def = function(id, dependencies, definition) { - if (typeof id !== 'string') - throw 'module id must be a string'; - else if (dependencies === undefined) - throw 'no dependencies for ' + id; - else if (definition === undefined) - throw 'no definition function for ' + id; - defs[id] = { - deps: dependencies, - defn: definition, - instance: undefined - }; - }; - - var dem = function(id) { - var actual = defs[id]; - if (actual === undefined) - throw 'module [' + id + '] was undefined'; - else if (actual.instance === undefined) - instantiate(id); - return actual.instance; - }; - - var req = function(ids, callback) { - var len = ids.length; - var instances = new Array(len); - for (var i = 0; i < len; ++i) - instances[i] = dem(ids[i]); - callback.apply(null, instances); - }; - - var ephox = {}; - - ephox.bolt = { - module: { - api: { - define: def, - require: req, - demand: dem - } - } - }; - - var define = def; - var require = req; - var demand = dem; - // this helps with minification when using a lot of global references - var defineGlobal = function(id, ref) { - define(id, [], function() { - return ref; - }); - }; - /*jsc - ["tinymce.core.api.Main","tinymce.core.api.Tinymce","tinymce.core.Register","tinymce.core.geom.Rect","tinymce.core.util.Promise","tinymce.core.util.Delay","tinymce.core.Env","tinymce.core.dom.EventUtils","tinymce.core.dom.Sizzle","tinymce.core.util.Tools","tinymce.core.dom.DomQuery","tinymce.core.html.Styles","tinymce.core.dom.TreeWalker","tinymce.core.html.Entities","tinymce.core.dom.DOMUtils","tinymce.core.dom.ScriptLoader","tinymce.core.AddOnManager","tinymce.core.dom.RangeUtils","tinymce.core.html.Node","tinymce.core.html.Schema","tinymce.core.html.SaxParser","tinymce.core.html.DomParser","tinymce.core.html.Writer","tinymce.core.html.Serializer","tinymce.core.dom.Serializer","tinymce.core.util.VK","tinymce.core.dom.ControlSelection","tinymce.core.dom.BookmarkManager","tinymce.core.dom.Selection","tinymce.core.Formatter","tinymce.core.UndoManager","tinymce.core.EditorCommands","tinymce.core.util.URI","tinymce.core.util.Class","tinymce.core.util.EventDispatcher","tinymce.core.util.Observable","tinymce.core.WindowManager","tinymce.core.NotificationManager","tinymce.core.EditorObservable","tinymce.core.Shortcuts","tinymce.core.Editor","tinymce.core.util.I18n","tinymce.core.FocusManager","tinymce.core.EditorManager","tinymce.core.util.XHR","tinymce.core.util.JSON","tinymce.core.util.JSONRequest","tinymce.core.util.JSONP","tinymce.core.util.LocalStorage","tinymce.core.api.Compat","tinymce.core.util.Color","tinymce.core.ui.Api","tinymce.core.util.Arr","tinymce.core.dom.Range","tinymce.core.dom.StyleSheetLoader","tinymce.core.dom.NodeType","tinymce.core.caret.CaretContainer","tinymce.core.text.Zwsp","ephox.katamari.api.Fun","tinymce.core.dom.RangePoint","tinymce.core.caret.CaretBookmark","tinymce.core.caret.CaretPosition","ephox.sugar.api.dom.Compare","ephox.sugar.api.node.Element","tinymce.core.dom.ScrollIntoView","tinymce.core.dom.TridentSelection","tinymce.core.selection.FragmentReader","tinymce.core.dom.ElementUtils","tinymce.core.util.Fun","tinymce.core.fmt.Preview","tinymce.core.fmt.Hooks","tinymce.core.undo.Levels","tinymce.core.delete.DeleteCommands","tinymce.core.InsertContent","global!document","tinymce.core.ui.Window","tinymce.core.ui.MessageBox","tinymce.core.ui.Notification","tinymce.core.EditorSettings","tinymce.core.init.Render","tinymce.core.Mode","tinymce.core.ui.Sidebar","tinymce.core.util.Uuid","tinymce.core.ErrorReporter","tinymce.core.LegacyInput","tinymce.core.ui.Selector","tinymce.core.ui.Collection","tinymce.core.ui.ReflowQueue","tinymce.core.ui.Control","tinymce.core.ui.Factory","tinymce.core.ui.KeyboardNavigation","tinymce.core.ui.Container","tinymce.core.ui.DragHelper","tinymce.core.ui.Scrollable","tinymce.core.ui.Panel","tinymce.core.ui.Movable","tinymce.core.ui.Resizable","tinymce.core.ui.FloatPanel","tinymce.core.ui.Tooltip","tinymce.core.ui.Widget","tinymce.core.ui.Progress","tinymce.core.ui.Layout","tinymce.core.ui.AbsoluteLayout","tinymce.core.ui.Button","tinymce.core.ui.ButtonGroup","tinymce.core.ui.Checkbox","tinymce.core.ui.ComboBox","tinymce.core.ui.ColorBox","tinymce.core.ui.PanelButton","tinymce.core.ui.ColorButton","tinymce.core.ui.ColorPicker","tinymce.core.ui.Path","tinymce.core.ui.ElementPath","tinymce.core.ui.FormItem","tinymce.core.ui.Form","tinymce.core.ui.FieldSet","tinymce.core.ui.FilePicker","tinymce.core.ui.FitLayout","tinymce.core.ui.FlexLayout","tinymce.core.ui.FlowLayout","tinymce.core.ui.FormatControls","tinymce.core.ui.GridLayout","tinymce.core.ui.Iframe","tinymce.core.ui.InfoBox","tinymce.core.ui.Label","tinymce.core.ui.Toolbar","tinymce.core.ui.MenuBar","tinymce.core.ui.MenuButton","tinymce.core.ui.MenuItem","tinymce.core.ui.Throbber","tinymce.core.ui.Menu","tinymce.core.ui.ListBox","tinymce.core.ui.Radio","tinymce.core.ui.ResizeHandle","tinymce.core.ui.SelectBox","tinymce.core.ui.Slider","tinymce.core.ui.Spacer","tinymce.core.ui.SplitButton","tinymce.core.ui.StackLayout","tinymce.core.ui.TabPanel","tinymce.core.ui.TextBox","ephox.katamari.api.Arr","global!Array","global!Error","ephox.katamari.api.Future","ephox.katamari.api.Futures","ephox.katamari.api.Result","tinymce.core.geom.ClientRect","tinymce.core.caret.CaretCandidate","tinymce.core.text.ExtendingChar","ephox.sand.api.Node","ephox.sand.api.PlatformDetection","ephox.sugar.api.search.Selectors","global!console","ephox.sugar.api.dom.Insert","ephox.sugar.api.dom.Replication","ephox.sugar.api.node.Fragment","ephox.sugar.api.node.Node","tinymce.core.dom.ElementType","tinymce.core.dom.Parents","tinymce.core.selection.SelectionUtils","tinymce.core.undo.Fragments","tinymce.core.delete.BlockBoundaryDelete","tinymce.core.delete.BlockRangeDelete","tinymce.core.delete.CefDelete","tinymce.core.delete.InlineBoundaryDelete","tinymce.core.caret.CaretWalker","tinymce.core.dom.RangeNormalizer","tinymce.core.InsertList","tinymce.core.data.ObservableObject","tinymce.core.ui.DomUtils","tinymce.core.ui.BoxUtils","tinymce.core.ui.ClassList","global!window","tinymce.core.init.Init","tinymce.core.PluginManager","tinymce.core.ThemeManager","tinymce.core.content.LinkTargets","tinymce.core.fmt.FontInfo","ephox.katamari.api.Option","global!String","ephox.katamari.api.LazyValue","ephox.katamari.async.Bounce","ephox.katamari.async.AsyncValues","ephox.sand.util.Global","ephox.katamari.api.Thunk","ephox.sand.core.PlatformDetection","global!navigator","ephox.sugar.api.node.NodeTypes","ephox.sugar.api.search.Traverse","ephox.sugar.api.properties.Attr","ephox.sugar.api.dom.InsertAll","ephox.sugar.api.dom.Remove","ephox.katamari.api.Options","tinymce.core.undo.Diff","tinymce.core.delete.BlockBoundary","tinymce.core.delete.MergeBlocks","tinymce.core.delete.DeleteUtils","tinymce.core.caret.CaretUtils","tinymce.core.delete.CefDeleteAction","tinymce.core.delete.DeleteElement","tinymce.core.caret.CaretFinder","tinymce.core.keyboard.BoundaryCaret","tinymce.core.keyboard.BoundaryLocation","tinymce.core.keyboard.BoundarySelection","tinymce.core.keyboard.InlineUtils","tinymce.core.data.Binding","tinymce.core.init.InitContentBody","global!Object","global!setTimeout","ephox.katamari.api.Resolve","ephox.sand.core.Browser","ephox.sand.core.OperatingSystem","ephox.sand.detect.DeviceType","ephox.sand.detect.UaString","ephox.sand.info.PlatformInfo","ephox.katamari.api.Type","ephox.katamari.api.Struct","ephox.sugar.alien.Recurse","ephox.katamari.api.Obj","ephox.sugar.api.search.PredicateFind","tinymce.core.dom.Empty","ephox.katamari.api.Adt","tinymce.core.text.Bidi","tinymce.core.caret.CaretContainerInline","tinymce.core.caret.CaretContainerRemove","tinymce.core.util.LazyEvaluator","ephox.katamari.api.Cell","tinymce.core.caret.CaretContainerInput","tinymce.core.EditorUpload","tinymce.core.ForceBlocks","tinymce.core.keyboard.KeyboardOverrides","tinymce.core.NodeChange","tinymce.core.SelectionOverrides","tinymce.core.util.Quirks","ephox.katamari.api.Global","ephox.sand.detect.Version","ephox.katamari.api.Strings","ephox.katamari.data.Immutable","ephox.katamari.data.MixedBag","ephox.sugar.api.node.Body","ephox.sugar.impl.ClosestOrAncestor","ephox.sugar.api.search.SelectorExists","tinymce.core.file.Uploader","tinymce.core.file.ImageScanner","tinymce.core.file.BlobCache","tinymce.core.file.UploadStatus","tinymce.core.keyboard.ArrowKeys","tinymce.core.keyboard.DeleteBackspaceKeys","tinymce.core.keyboard.EnterKey","tinymce.core.keyboard.SpaceKey","tinymce.core.caret.FakeCaret","tinymce.core.caret.LineUtils","tinymce.core.DragDropOverrides","tinymce.core.EditorView","tinymce.core.keyboard.CefUtils","tinymce.core.dom.NodePath","global!Number","ephox.katamari.str.StrAppend","ephox.katamari.str.StringParts","ephox.katamari.util.BagUtils","ephox.sugar.api.search.SelectorFind","tinymce.core.file.Conversions","global!URL","tinymce.core.keyboard.CefNavigation","tinymce.core.keyboard.MatchKeys","tinymce.core.keyboard.InsertSpace","tinymce.core.dom.Dimensions","tinymce.core.dom.MousePosition","ephox.sugar.api.properties.Css","tinymce.core.caret.LineWalker","ephox.katamari.api.Merger","ephox.sugar.impl.Style"] - jsc*/ - /** - * Rect.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Contains various tools for rect/position calculation. - * - * @class tinymce.geom.Rect - */ - define( - 'tinymce.core.geom.Rect', [], - function() { - "use strict"; - - var min = Math.min, - max = Math.max, - round = Math.round; - - /** - * Returns the rect positioned based on the relative position name - * to the target rect. - * - * @method relativePosition - * @param {Rect} rect Source rect to modify into a new rect. - * @param {Rect} targetRect Rect to move relative to based on the rel option. - * @param {String} rel Relative position. For example: tr-bl. - */ - function relativePosition(rect, targetRect, rel) { - var x, y, w, h, targetW, targetH; - - x = targetRect.x; - y = targetRect.y; - w = rect.w; - h = rect.h; - targetW = targetRect.w; - targetH = targetRect.h; - - rel = (rel || '').split(''); - - if (rel[0] === 'b') { - y += targetH; - } - - if (rel[1] === 'r') { - x += targetW; - } - - if (rel[0] === 'c') { - y += round(targetH / 2); - } - - if (rel[1] === 'c') { - x += round(targetW / 2); - } - - if (rel[3] === 'b') { - y -= h; - } - - if (rel[4] === 'r') { - x -= w; - } - - if (rel[3] === 'c') { - y -= round(h / 2); - } - - if (rel[4] === 'c') { - x -= round(w / 2); - } - - return create(x, y, w, h); - } - - /** - * Tests various positions to get the most suitable one. - * - * @method findBestRelativePosition - * @param {Rect} rect Rect to use as source. - * @param {Rect} targetRect Rect to move relative to. - * @param {Rect} constrainRect Rect to constrain within. - * @param {Array} rels Array of relative positions to test against. - */ - function findBestRelativePosition(rect, targetRect, constrainRect, rels) { - var pos, i; - - for (i = 0; i < rels.length; i++) { - pos = relativePosition(rect, targetRect, rels[i]); - - if (pos.x >= constrainRect.x && pos.x + pos.w <= constrainRect.w + constrainRect.x && - pos.y >= constrainRect.y && pos.y + pos.h <= constrainRect.h + constrainRect.y) { - return rels[i]; - } - } - - return null; - } - - /** - * Inflates the rect in all directions. - * - * @method inflate - * @param {Rect} rect Rect to expand. - * @param {Number} w Relative width to expand by. - * @param {Number} h Relative height to expand by. - * @return {Rect} New expanded rect. - */ - function inflate(rect, w, h) { - return create(rect.x - w, rect.y - h, rect.w + w * 2, rect.h + h * 2); - } - - /** - * Returns the intersection of the specified rectangles. - * - * @method intersect - * @param {Rect} rect The first rectangle to compare. - * @param {Rect} cropRect The second rectangle to compare. - * @return {Rect} The intersection of the two rectangles or null if they don't intersect. - */ - function intersect(rect, cropRect) { - var x1, y1, x2, y2; - - x1 = max(rect.x, cropRect.x); - y1 = max(rect.y, cropRect.y); - x2 = min(rect.x + rect.w, cropRect.x + cropRect.w); - y2 = min(rect.y + rect.h, cropRect.y + cropRect.h); - - if (x2 - x1 < 0 || y2 - y1 < 0) { - return null; - } - - return create(x1, y1, x2 - x1, y2 - y1); - } - - /** - * Returns a rect clamped within the specified clamp rect. This forces the - * rect to be inside the clamp rect. - * - * @method clamp - * @param {Rect} rect Rectangle to force within clamp rect. - * @param {Rect} clampRect Rectable to force within. - * @param {Boolean} fixedSize True/false if size should be fixed. - * @return {Rect} Clamped rect. - */ - function clamp(rect, clampRect, fixedSize) { - var underflowX1, underflowY1, overflowX2, overflowY2, - x1, y1, x2, y2, cx2, cy2; - - x1 = rect.x; - y1 = rect.y; - x2 = rect.x + rect.w; - y2 = rect.y + rect.h; - cx2 = clampRect.x + clampRect.w; - cy2 = clampRect.y + clampRect.h; - - underflowX1 = max(0, clampRect.x - x1); - underflowY1 = max(0, clampRect.y - y1); - overflowX2 = max(0, x2 - cx2); - overflowY2 = max(0, y2 - cy2); - - x1 += underflowX1; - y1 += underflowY1; - - if (fixedSize) { - x2 += underflowX1; - y2 += underflowY1; - x1 -= overflowX2; - y1 -= overflowY2; - } - - x2 -= overflowX2; - y2 -= overflowY2; - - return create(x1, y1, x2 - x1, y2 - y1); - } - - /** - * Creates a new rectangle object. - * - * @method create - * @param {Number} x Rectangle x location. - * @param {Number} y Rectangle y location. - * @param {Number} w Rectangle width. - * @param {Number} h Rectangle height. - * @return {Rect} New rectangle object. - */ - function create(x, y, w, h) { - return { - x: x, - y: y, - w: w, - h: h - }; - } - - /** - * Creates a new rectangle object form a clientRects object. - * - * @method fromClientRect - * @param {ClientRect} clientRect DOM ClientRect object. - * @return {Rect} New rectangle object. - */ - function fromClientRect(clientRect) { - return create(clientRect.left, clientRect.top, clientRect.width, clientRect.height); - } - - return { - inflate: inflate, - relativePosition: relativePosition, - findBestRelativePosition: findBestRelativePosition, - intersect: intersect, - clamp: clamp, - create: create, - fromClientRect: fromClientRect - }; - } - ); - - /** - * Promise.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * Promise polyfill under MIT license: https://github.com/taylorhakes/promise-polyfill - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /* eslint-disable */ - /* jshint ignore:start */ - - /** - * Modifed to be a feature fill and wrapped as tinymce module. - */ - define( - 'tinymce.core.util.Promise', [], - function() { - if (window.Promise) { - return window.Promise; - } - - // Use polyfill for setImmediate for performance gains - var asap = Promise.immediateFn || (typeof setImmediate === 'function' && setImmediate) || - function(fn) { - setTimeout(fn, 1); - }; - - // Polyfill for Function.prototype.bind - function bind(fn, thisArg) { - return function() { - fn.apply(thisArg, arguments); - }; - } - - var isArray = Array.isArray || function(value) { - return Object.prototype.toString.call(value) === "[object Array]"; - }; - - function Promise(fn) { - if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new'); - if (typeof fn !== 'function') throw new TypeError('not a function'); - this._state = null; - this._value = null; - this._deferreds = []; - - doResolve(fn, bind(resolve, this), bind(reject, this)); - } - - function handle(deferred) { - var me = this; - if (this._state === null) { - this._deferreds.push(deferred); - return; - } - asap(function() { - var cb = me._state ? deferred.onFulfilled : deferred.onRejected; - if (cb === null) { - (me._state ? deferred.resolve : deferred.reject)(me._value); - return; - } - var ret; - try { - ret = cb(me._value); - } catch (e) { - deferred.reject(e); - return; - } - deferred.resolve(ret); - }); - } - - function resolve(newValue) { - try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure - if (newValue === this) throw new TypeError('A promise cannot be resolved with itself.'); - if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { - var then = newValue.then; - if (typeof then === 'function') { - doResolve(bind(then, newValue), bind(resolve, this), bind(reject, this)); - return; - } - } - this._state = true; - this._value = newValue; - finale.call(this); - } catch (e) { - reject.call(this, e); - } - } - - function reject(newValue) { - this._state = false; - this._value = newValue; - finale.call(this); - } - - function finale() { - for (var i = 0, len = this._deferreds.length; i < len; i++) { - handle.call(this, this._deferreds[i]); - } - this._deferreds = null; - } - - function Handler(onFulfilled, onRejected, resolve, reject) { - this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; - this.onRejected = typeof onRejected === 'function' ? onRejected : null; - this.resolve = resolve; - this.reject = reject; - } - - /** - * Take a potentially misbehaving resolver function and make sure - * onFulfilled and onRejected are only called once. - * - * Makes no guarantees about asynchrony. - */ - function doResolve(fn, onFulfilled, onRejected) { - var done = false; - try { - fn(function(value) { - if (done) return; - done = true; - onFulfilled(value); - }, function(reason) { - if (done) return; - done = true; - onRejected(reason); - }); - } catch (ex) { - if (done) return; - done = true; - onRejected(ex); - } - } - - Promise.prototype['catch'] = function(onRejected) { - return this.then(null, onRejected); - }; - - Promise.prototype.then = function(onFulfilled, onRejected) { - var me = this; - return new Promise(function(resolve, reject) { - handle.call(me, new Handler(onFulfilled, onRejected, resolve, reject)); - }); - }; - - Promise.all = function() { - var args = Array.prototype.slice.call(arguments.length === 1 && isArray(arguments[0]) ? arguments[0] : arguments); - - return new Promise(function(resolve, reject) { - if (args.length === 0) return resolve([]); - var remaining = args.length; - - function res(i, val) { - try { - if (val && (typeof val === 'object' || typeof val === 'function')) { - var then = val.then; - if (typeof then === 'function') { - then.call(val, function(val) { - res(i, val); - }, reject); - return; - } - } - args[i] = val; - if (--remaining === 0) { - resolve(args); - } - } catch (ex) { - reject(ex); - } - } - for (var i = 0; i < args.length; i++) { - res(i, args[i]); - } - }); - }; - - Promise.resolve = function(value) { - if (value && typeof value === 'object' && value.constructor === Promise) { - return value; - } - - return new Promise(function(resolve) { - resolve(value); - }); - }; - - Promise.reject = function(value) { - return new Promise(function(resolve, reject) { - reject(value); - }); - }; - - Promise.race = function(values) { - return new Promise(function(resolve, reject) { - for (var i = 0, len = values.length; i < len; i++) { - values[i].then(resolve, reject); - } - }); - }; - - return Promise; - } - ); - - /* jshint ignore:end */ - /* eslint-enable */ - /** - * Delay.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Utility class for working with delayed actions like setTimeout. - * - * @class tinymce.util.Delay - */ - define( - 'tinymce.core.util.Delay', [ - "tinymce.core.util.Promise" - ], - function(Promise) { - var requestAnimationFramePromise; - - function requestAnimationFrame(callback, element) { - var i, requestAnimationFrameFunc = window.requestAnimationFrame, - vendors = ['ms', 'moz', 'webkit']; - - function featurefill(callback) { - window.setTimeout(callback, 0); - } - - for (i = 0; i < vendors.length && !requestAnimationFrameFunc; i++) { - requestAnimationFrameFunc = window[vendors[i] + 'RequestAnimationFrame']; - } - - if (!requestAnimationFrameFunc) { - requestAnimationFrameFunc = featurefill; - } - - requestAnimationFrameFunc(callback, element); - } - - function wrappedSetTimeout(callback, time) { - if (typeof time != 'number') { - time = 0; - } - - return setTimeout(callback, time); - } - - function wrappedSetInterval(callback, time) { - if (typeof time != 'number') { - time = 1; // IE 8 needs it to be > 0 - } - - return setInterval(callback, time); - } - - function wrappedClearTimeout(id) { - return clearTimeout(id); - } - - function wrappedClearInterval(id) { - return clearInterval(id); - } - - function debounce(callback, time) { - var timer, func; - - func = function() { - var args = arguments; - - clearTimeout(timer); - - timer = wrappedSetTimeout(function() { - callback.apply(this, args); - }, time); - }; - - func.stop = function() { - clearTimeout(timer); - }; - - return func; - } - - return { - /** - * Requests an animation frame and fallbacks to a timeout on older browsers. - * - * @method requestAnimationFrame - * @param {function} callback Callback to execute when a new frame is available. - * @param {DOMElement} element Optional element to scope it to. - */ - requestAnimationFrame: function(callback, element) { - if (requestAnimationFramePromise) { - requestAnimationFramePromise.then(callback); - return; - } - - requestAnimationFramePromise = new Promise(function(resolve) { - if (!element) { - element = document.body; - } - - requestAnimationFrame(resolve, element); - }).then(callback); - }, - - /** - * Sets a timer in ms and executes the specified callback when the timer runs out. - * - * @method setTimeout - * @param {function} callback Callback to execute when timer runs out. - * @param {Number} time Optional time to wait before the callback is executed, defaults to 0. - * @return {Number} Timeout id number. - */ - setTimeout: wrappedSetTimeout, - - /** - * Sets an interval timer in ms and executes the specified callback at every interval of that time. - * - * @method setInterval - * @param {function} callback Callback to execute when interval time runs out. - * @param {Number} time Optional time to wait before the callback is executed, defaults to 0. - * @return {Number} Timeout id number. - */ - setInterval: wrappedSetInterval, - - /** - * Sets an editor timeout it's similar to setTimeout except that it checks if the editor instance is - * still alive when the callback gets executed. - * - * @method setEditorTimeout - * @param {tinymce.Editor} editor Editor instance to check the removed state on. - * @param {function} callback Callback to execute when timer runs out. - * @param {Number} time Optional time to wait before the callback is executed, defaults to 0. - * @return {Number} Timeout id number. - */ - setEditorTimeout: function(editor, callback, time) { - return wrappedSetTimeout(function() { - if (!editor.removed) { - callback(); - } - }, time); - }, - - /** - * Sets an interval timer it's similar to setInterval except that it checks if the editor instance is - * still alive when the callback gets executed. - * - * @method setEditorInterval - * @param {function} callback Callback to execute when interval time runs out. - * @param {Number} time Optional time to wait before the callback is executed, defaults to 0. - * @return {Number} Timeout id number. - */ - setEditorInterval: function(editor, callback, time) { - var timer; - - timer = wrappedSetInterval(function() { - if (!editor.removed) { - callback(); - } else { - clearInterval(timer); - } - }, time); - - return timer; - }, - - /** - * Creates debounced callback function that only gets executed once within the specified time. - * - * @method debounce - * @param {function} callback Callback to execute when timer finishes. - * @param {Number} time Optional time to wait before the callback is executed, defaults to 0. - * @return {Function} debounced function callback. - */ - debounce: debounce, - - // Throttle needs to be debounce due to backwards compatibility. - throttle: debounce, - - /** - * Clears an interval timer so it won't execute. - * - * @method clearInterval - * @param {Number} Interval timer id number. - */ - clearInterval: wrappedClearInterval, - - /** - * Clears an timeout timer so it won't execute. - * - * @method clearTimeout - * @param {Number} Timeout timer id number. - */ - clearTimeout: wrappedClearTimeout - }; - } - ); - - /** - * Env.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class contains various environment constants like browser versions etc. - * Normally you don't want to sniff specific browser versions but sometimes you have - * to when it's impossible to feature detect. So use this with care. - * - * @class tinymce.Env - * @static - */ - define( - 'tinymce.core.Env', [], - function() { - var nav = navigator, - userAgent = nav.userAgent; - var opera, webkit, ie, ie11, ie12, gecko, mac, iDevice, android, fileApi, phone, tablet, windowsPhone; - - function matchMediaQuery(query) { - return "matchMedia" in window ? matchMedia(query).matches : false; - } - - opera = window.opera && window.opera.buildNumber; - android = /Android/.test(userAgent); - webkit = /WebKit/.test(userAgent); - ie = !webkit && !opera && (/MSIE/gi).test(userAgent) && (/Explorer/gi).test(nav.appName); - ie = ie && /MSIE (\w+)\./.exec(userAgent)[1]; - ie11 = userAgent.indexOf('Trident/') != -1 && (userAgent.indexOf('rv:') != -1 || nav.appName.indexOf('Netscape') != -1) ? 11 : false; - ie12 = (userAgent.indexOf('Edge/') != -1 && !ie && !ie11) ? 12 : false; - ie = ie || ie11 || ie12; - gecko = !webkit && !ie11 && /Gecko/.test(userAgent); - mac = userAgent.indexOf('Mac') != -1; - iDevice = /(iPad|iPhone)/.test(userAgent); - fileApi = "FormData" in window && "FileReader" in window && "URL" in window && !!URL.createObjectURL; - phone = matchMediaQuery("only screen and (max-device-width: 480px)") && (android || iDevice); - tablet = matchMediaQuery("only screen and (min-width: 800px)") && (android || iDevice); - windowsPhone = userAgent.indexOf('Windows Phone') != -1; - - if (ie12) { - webkit = false; - } - - // Is a iPad/iPhone and not on iOS5 sniff the WebKit version since older iOS WebKit versions - // says it has contentEditable support but there is no visible caret. - var contentEditable = !iDevice || fileApi || userAgent.match(/AppleWebKit\/(\d*)/)[1] >= 534; - - return { - /** - * Constant that is true if the browser is Opera. - * - * @property opera - * @type Boolean - * @final - */ - opera: opera, - - /** - * Constant that is true if the browser is WebKit (Safari/Chrome). - * - * @property webKit - * @type Boolean - * @final - */ - webkit: webkit, - - /** - * Constant that is more than zero if the browser is IE. - * - * @property ie - * @type Boolean - * @final - */ - ie: ie, - - /** - * Constant that is true if the browser is Gecko. - * - * @property gecko - * @type Boolean - * @final - */ - gecko: gecko, - - /** - * Constant that is true if the os is Mac OS. - * - * @property mac - * @type Boolean - * @final - */ - mac: mac, - - /** - * Constant that is true if the os is iOS. - * - * @property iOS - * @type Boolean - * @final - */ - iOS: iDevice, - - /** - * Constant that is true if the os is android. - * - * @property android - * @type Boolean - * @final - */ - android: android, - - /** - * Constant that is true if the browser supports editing. - * - * @property contentEditable - * @type Boolean - * @final - */ - contentEditable: contentEditable, - - /** - * Transparent image data url. - * - * @property transparentSrc - * @type Boolean - * @final - */ - transparentSrc: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7", - - /** - * Returns true/false if the browser can or can't place the caret after a inline block like an image. - * - * @property noCaretAfter - * @type Boolean - * @final - */ - caretAfter: ie != 8, - - /** - * Constant that is true if the browser supports native DOM Ranges. IE 9+. - * - * @property range - * @type Boolean - */ - range: window.getSelection && "Range" in window, - - /** - * Returns the IE document mode for non IE browsers this will fake IE 10. - * - * @property documentMode - * @type Number - */ - documentMode: ie && !ie12 ? (document.documentMode || 7) : 10, - - /** - * Constant that is true if the browser has a modern file api. - * - * @property fileApi - * @type Boolean - */ - fileApi: fileApi, - - /** - * Constant that is true if the browser supports contentEditable=false regions. - * - * @property ceFalse - * @type Boolean - */ - ceFalse: (ie === false || ie > 8), - - /** - * Constant if CSP mode is possible or not. Meaning we can't use script urls for the iframe. - */ - canHaveCSP: (ie === false || ie > 11), - - desktop: !phone && !tablet, - windowsPhone: windowsPhone - }; - } - ); - - /** - * EventUtils.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /*jshint loopfunc:true*/ - /*eslint no-loop-func:0 */ - - /** - * This class wraps the browsers native event logic with more convenient methods. - * - * @class tinymce.dom.EventUtils - */ - define( - 'tinymce.core.dom.EventUtils', [ - "tinymce.core.util.Delay", - "tinymce.core.Env" - ], - function(Delay, Env) { - "use strict"; - - var eventExpandoPrefix = "mce-data-"; - var mouseEventRe = /^(?:mouse|contextmenu)|click/; - var deprecated = { - keyLocation: 1, - layerX: 1, - layerY: 1, - returnValue: 1, - webkitMovementX: 1, - webkitMovementY: 1, - keyIdentifier: 1 - }; - - // Checks if it is our own isDefaultPrevented function - var hasIsDefaultPrevented = function(event) { - return event.isDefaultPrevented === returnTrue || event.isDefaultPrevented === returnFalse; - }; - - // Dummy function that gets replaced on the delegation state functions - var returnFalse = function() { - return false; - }; - - // Dummy function that gets replaced on the delegation state functions - var returnTrue = function() { - return true; - }; - - /** - * Binds a native event to a callback on the speified target. - */ - function addEvent(target, name, callback, capture) { - if (target.addEventListener) { - target.addEventListener(name, callback, capture || false); - } else if (target.attachEvent) { - target.attachEvent('on' + name, callback); - } - } - - /** - * Unbinds a native event callback on the specified target. - */ - function removeEvent(target, name, callback, capture) { - if (target.removeEventListener) { - target.removeEventListener(name, callback, capture || false); - } else if (target.detachEvent) { - target.detachEvent('on' + name, callback); - } - } - - /** - * Gets the event target based on shadow dom properties like path and deepPath. - */ - function getTargetFromShadowDom(event, defaultTarget) { - var path, target = defaultTarget; - - // When target element is inside Shadow DOM we need to take first element from path - // otherwise we'll get Shadow Root parent, not actual target element - - // Normalize target for WebComponents v0 implementation (in Chrome) - path = event.path; - if (path && path.length > 0) { - target = path[0]; - } - - // Normalize target for WebComponents v1 implementation (standard) - if (event.deepPath) { - path = event.deepPath(); - if (path && path.length > 0) { - target = path[0]; - } - } - - return target; - } - - /** - * Normalizes a native event object or just adds the event specific methods on a custom event. - */ - function fix(originalEvent, data) { - var name, event = data || {}, - undef; - - // Copy all properties from the original event - for (name in originalEvent) { - // layerX/layerY is deprecated in Chrome and produces a warning - if (!deprecated[name]) { - event[name] = originalEvent[name]; - } - } - - // Normalize target IE uses srcElement - if (!event.target) { - event.target = event.srcElement || document; - } - - // Experimental shadow dom support - if (Env.experimentalShadowDom) { - event.target = getTargetFromShadowDom(originalEvent, event.target); - } - - // Calculate pageX/Y if missing and clientX/Y available - if (originalEvent && mouseEventRe.test(originalEvent.type) && originalEvent.pageX === undef && originalEvent.clientX !== undef) { - var eventDoc = event.target.ownerDocument || document; - var doc = eventDoc.documentElement; - var body = eventDoc.body; - - event.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - - (doc && doc.clientLeft || body && body.clientLeft || 0); - - event.pageY = originalEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - - (doc && doc.clientTop || body && body.clientTop || 0); - } - - // Add preventDefault method - event.preventDefault = function() { - event.isDefaultPrevented = returnTrue; - - // Execute preventDefault on the original event object - if (originalEvent) { - if (originalEvent.preventDefault) { - originalEvent.preventDefault(); - } else { - originalEvent.returnValue = false; // IE - } - } - }; - - // Add stopPropagation - event.stopPropagation = function() { - event.isPropagationStopped = returnTrue; - - // Execute stopPropagation on the original event object - if (originalEvent) { - if (originalEvent.stopPropagation) { - originalEvent.stopPropagation(); - } else { - originalEvent.cancelBubble = true; // IE - } - } - }; - - // Add stopImmediatePropagation - event.stopImmediatePropagation = function() { - event.isImmediatePropagationStopped = returnTrue; - event.stopPropagation(); - }; - - // Add event delegation states - if (hasIsDefaultPrevented(event) === false) { - event.isDefaultPrevented = returnFalse; - event.isPropagationStopped = returnFalse; - event.isImmediatePropagationStopped = returnFalse; - } - - // Add missing metaKey for IE 8 - if (typeof event.metaKey == 'undefined') { - event.metaKey = false; - } - - return event; - } - - /** - * Bind a DOMContentLoaded event across browsers and executes the callback once the page DOM is initialized. - * It will also set/check the domLoaded state of the event_utils instance so ready isn't called multiple times. - */ - function bindOnReady(win, callback, eventUtils) { - var doc = win.document, - event = { - type: 'ready' - }; - - if (eventUtils.domLoaded) { - callback(event); - return; - } - - function isDocReady() { - // Check complete or interactive state if there is a body - // element on some iframes IE 8 will produce a null body - return doc.readyState === "complete" || (doc.readyState === "interactive" && doc.body); - } - - // Gets called when the DOM is ready - function readyHandler() { - if (!eventUtils.domLoaded) { - eventUtils.domLoaded = true; - callback(event); - } - } - - function waitForDomLoaded() { - if (isDocReady()) { - removeEvent(doc, "readystatechange", waitForDomLoaded); - readyHandler(); - } - } - - function tryScroll() { - try { - // If IE is used, use the trick by Diego Perini licensed under MIT by request to the author. - // http://javascript.nwbox.com/IEContentLoaded/ - doc.documentElement.doScroll("left"); - } catch (ex) { - Delay.setTimeout(tryScroll); - return; - } - - readyHandler(); - } - - // Use W3C method (exclude IE 9,10 - readyState "interactive" became valid only in IE 11) - if (doc.addEventListener && !(Env.ie && Env.ie < 11)) { - if (isDocReady()) { - readyHandler(); - } else { - addEvent(win, 'DOMContentLoaded', readyHandler); - } - } else { - // Use IE method - addEvent(doc, "readystatechange", waitForDomLoaded); - - // Wait until we can scroll, when we can the DOM is initialized - if (doc.documentElement.doScroll && win.self === win.top) { - tryScroll(); - } - } - - // Fallback if any of the above methods should fail for some odd reason - addEvent(win, 'load', readyHandler); - } - - /** - * This class enables you to bind/unbind native events to elements and normalize it's behavior across browsers. - */ - function EventUtils() { - var self = this, - events = {}, - count, expando, hasFocusIn, hasMouseEnterLeave, mouseEnterLeave; - - expando = eventExpandoPrefix + (+new Date()).toString(32); - hasMouseEnterLeave = "onmouseenter" in document.documentElement; - hasFocusIn = "onfocusin" in document.documentElement; - mouseEnterLeave = { - mouseenter: 'mouseover', - mouseleave: 'mouseout' - }; - count = 1; - - // State if the DOMContentLoaded was executed or not - self.domLoaded = false; - self.events = events; - - /** - * Executes all event handler callbacks for a specific event. - * - * @private - * @param {Event} evt Event object. - * @param {String} id Expando id value to look for. - */ - function executeHandlers(evt, id) { - var callbackList, i, l, callback, container = events[id]; - - callbackList = container && container[evt.type]; - if (callbackList) { - for (i = 0, l = callbackList.length; i < l; i++) { - callback = callbackList[i]; - - if (evt.type.indexOf('focus') !== -1) { - // console.log(`TinyMCE: ${id}:${evt.type}`/*, callback.func*/); - // console.trace(); - } - - // Check if callback exists might be removed if a unbind is called inside the callback - if (callback && callback.func.call(callback.scope, evt) === false) { - evt.preventDefault(); - } - - // Should we stop propagation to immediate listeners - if (evt.isImmediatePropagationStopped()) { - return; - } - } - } - } - - /** - * Binds a callback to an event on the specified target. - * - * @method bind - * @param {Object} target Target node/window or custom object. - * @param {String} names Name of the event to bind. - * @param {function} callback Callback function to execute when the event occurs. - * @param {Object} scope Scope to call the callback function on, defaults to target. - * @return {function} Callback function that got bound. - */ - self.bind = function(target, names, callback, scope) { - var id, callbackList, i, name, fakeName, nativeHandler, capture, win = window; - - // Native event handler function patches the event and executes the callbacks for the expando - function defaultNativeHandler(evt) { - executeHandlers(fix(evt || win.event), id); - } - - // Don't bind to text nodes or comments - if (!target || target.nodeType === 3 || target.nodeType === 8) { - return; - } - - // Create or get events id for the target - if (!target[expando]) { - id = count++; - target[expando] = id; - events[id] = {}; - } else { - id = target[expando]; - } - - // Setup the specified scope or use the target as a default - scope = scope || target; - - // Split names and bind each event, enables you to bind multiple events with one call - names = names.split(' '); - i = names.length; - while (i--) { - name = names[i]; - nativeHandler = defaultNativeHandler; - fakeName = capture = false; - - // Use ready instead of DOMContentLoaded - if (name === "DOMContentLoaded") { - name = "ready"; - } - - // DOM is already ready - if (self.domLoaded && name === "ready" && target.readyState == 'complete') { - callback.call(scope, fix({ - type: name - })); - continue; - } - - // Handle mouseenter/mouseleaver - if (!hasMouseEnterLeave) { - fakeName = mouseEnterLeave[name]; - - if (fakeName) { - nativeHandler = function(evt) { - var current, related; - - current = evt.currentTarget; - related = evt.relatedTarget; - - // Check if related is inside the current target if it's not then the event should - // be ignored since it's a mouseover/mouseout inside the element - if (related && current.contains) { - // Use contains for performance - related = current.contains(related); - } else { - while (related && related !== current) { - related = related.parentNode; - } - } - - // Fire fake event - if (!related) { - evt = fix(evt || win.event); - evt.type = evt.type === 'mouseout' ? 'mouseleave' : 'mouseenter'; - evt.target = current; - executeHandlers(evt, id); - } - }; - } - } - - // Fake bubbling of focusin/focusout - if (!hasFocusIn && (name === "focusin" || name === "focusout")) { - capture = true; - fakeName = name === "focusin" ? "focus" : "blur"; - nativeHandler = function(evt) { - evt = fix(evt || win.event); - evt.type = evt.type === 'focus' ? 'focusin' : 'focusout'; - executeHandlers(evt, id); - }; - } - - // Setup callback list and bind native event - callbackList = events[id][name]; - if (!callbackList) { - events[id][name] = callbackList = [{ - func: callback, - scope: scope - }]; - callbackList.fakeName = fakeName; - callbackList.capture = capture; - //callbackList.callback = callback; - - // Add the nativeHandler to the callback list so that we can later unbind it - callbackList.nativeHandler = nativeHandler; - - // Check if the target has native events support - - if (name === "ready") { - bindOnReady(target, nativeHandler, self); - } else { - addEvent(target, fakeName || name, nativeHandler, capture); - } - } else { - if (name === "ready" && self.domLoaded) { - callback({ - type: name - }); - } else { - // If it already has an native handler then just push the callback - callbackList.push({ - func: callback, - scope: scope - }); - } - } - } - - target = callbackList = 0; // Clean memory for IE - - return callback; - }; - - /** - * Unbinds the specified event by name, name and callback or all events on the target. - * - * @method unbind - * @param {Object} target Target node/window or custom object. - * @param {String} names Optional event name to unbind. - * @param {function} callback Optional callback function to unbind. - * @return {EventUtils} Event utils instance. - */ - self.unbind = function(target, names, callback) { - var id, callbackList, i, ci, name, eventMap; - - // Don't bind to text nodes or comments - if (!target || target.nodeType === 3 || target.nodeType === 8) { - return self; - } - - // Unbind event or events if the target has the expando - id = target[expando]; - if (id) { - eventMap = events[id]; - - // Specific callback - if (names) { - names = names.split(' '); - i = names.length; - while (i--) { - name = names[i]; - callbackList = eventMap[name]; - - // Unbind the event if it exists in the map - if (callbackList) { - // Remove specified callback - if (callback) { - ci = callbackList.length; - while (ci--) { - if (callbackList[ci].func === callback) { - var nativeHandler = callbackList.nativeHandler; - var fakeName = callbackList.fakeName, - capture = callbackList.capture; - - // Clone callbackList since unbind inside a callback would otherwise break the handlers loop - callbackList = callbackList.slice(0, ci).concat(callbackList.slice(ci + 1)); - callbackList.nativeHandler = nativeHandler; - callbackList.fakeName = fakeName; - callbackList.capture = capture; - - eventMap[name] = callbackList; - } - } - } - - // Remove all callbacks if there isn't a specified callback or there is no callbacks left - if (!callback || callbackList.length === 0) { - delete eventMap[name]; - removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture); - } - } - } - } else { - // All events for a specific element - for (name in eventMap) { - callbackList = eventMap[name]; - removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture); - } - - eventMap = {}; - } - - // Check if object is empty, if it isn't then we won't remove the expando map - for (name in eventMap) { - return self; - } - - // Delete event object - delete events[id]; - - // Remove expando from target - try { - // IE will fail here since it can't delete properties from window - delete target[expando]; - } catch (ex) { - // IE will set it to null - target[expando] = null; - } - } - - return self; - }; - - /** - * Fires the specified event on the specified target. - * - * @method fire - * @param {Object} target Target node/window or custom object. - * @param {String} name Event name to fire. - * @param {Object} args Optional arguments to send to the observers. - * @return {EventUtils} Event utils instance. - */ - self.fire = function(target, name, args) { - var id; - - // Don't bind to text nodes or comments - if (!target || target.nodeType === 3 || target.nodeType === 8) { - return self; - } - - // Build event object by patching the args - args = fix(null, args); - args.type = name; - args.target = target; - - do { - // Found an expando that means there is listeners to execute - id = target[expando]; - if (id) { - executeHandlers(args, id); - } - - // Walk up the DOM - target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow; - } while (target && !args.isPropagationStopped()); - - return self; - }; - - /** - * Removes all bound event listeners for the specified target. This will also remove any bound - * listeners to child nodes within that target. - * - * @method clean - * @param {Object} target Target node/window object. - * @return {EventUtils} Event utils instance. - */ - self.clean = function(target) { - var i, children, unbind = self.unbind; - - // Don't bind to text nodes or comments - if (!target || target.nodeType === 3 || target.nodeType === 8) { - return self; - } - - // Unbind any element on the specified target - if (target[expando]) { - unbind(target); - } - - // Target doesn't have getElementsByTagName it's probably a window object then use it's document to find the children - if (!target.getElementsByTagName) { - target = target.document; - } - - // Remove events from each child element - if (target && target.getElementsByTagName) { - unbind(target); - - children = target.getElementsByTagName('*'); - i = children.length; - while (i--) { - target = children[i]; - - if (target[expando]) { - unbind(target); - } - } - } - - return self; - }; - - /** - * Destroys the event object. Call this on IE to remove memory leaks. - */ - self.destroy = function() { - events = {}; - }; - - // Legacy function for canceling events - self.cancel = function(e) { - if (e) { - e.preventDefault(); - e.stopImmediatePropagation(); - } - - return false; - }; - } - - EventUtils.Event = new EventUtils(); - EventUtils.Event.bind(window, 'ready', function() {}); - - return EventUtils; - } - ); - - /** - * Sizzle.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - * - * @ignore-file - */ - - /*jshint bitwise:false, expr:true, noempty:false, sub:true, eqnull:true, latedef:false, maxlen:255 */ - /*eslint-disable */ - - /** - * Sizzle CSS Selector Engine v@VERSION - * http://sizzlejs.com/ - * - * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: @DATE - */ - define( - 'tinymce.core.dom.Sizzle', [], - function() { - var i, - support, - Expr, - getText, - isXML, - tokenize, - compile, - select, - outermostContext, - sortInput, - hasDuplicate, - - // Local document vars - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - // Instance-specific data - expando = "sizzle" + -(new Date()), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - sortOrder = function(a, b) { - if (a === b) { - hasDuplicate = true; - } - return 0; - }, - - // General-purpose constants - strundefined = typeof undefined, - MAX_NEGATIVE = 1 << 31, - - // Instance methods - hasOwn = ({}).hasOwnProperty, - arr = [], - pop = arr.pop, - push_native = arr.push, - push = arr.push, - slice = arr.slice, - // Use a stripped-down indexOf if we can't use a native one - indexOf = arr.indexOf || function(elem) { - var i = 0, - len = this.length; - for (; i < len; i++) { - if (this[i] === elem) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - - // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", - - // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + - "*\\]", - - pseudos = ":(" + identifier + ")(?:\\((" + - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - // 3. anything else (capture 2) - ".*" + - ")\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g"), - - rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*"), - rcombinators = new RegExp("^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"), - - rattributeQuotes = new RegExp("=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g"), - - rpseudo = new RegExp(pseudos), - ridentifier = new RegExp("^" + identifier + "$"), - - matchExpr = { - "ID": new RegExp("^#(" + identifier + ")"), - "CLASS": new RegExp("^\\.(" + identifier + ")"), - "TAG": new RegExp("^(" + identifier + "|[*])"), - "ATTR": new RegExp("^" + attributes), - "PSEUDO": new RegExp("^" + pseudos), - "CHILD": new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i"), - "bool": new RegExp("^(?:" + booleans + ")$", "i"), - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp("^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + - whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i") - }, - - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rnative = /^[^{]+\{\s*\[native \w/, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - rescape = /'|\\/g, - - // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp("\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig"), - funescape = function(_, escaped, escapedWhitespace) { - var high = "0x" + escaped - 0x10000; - // NaN means non-codepoint - // Support: Firefox<24 - // Workaround erroneous numeric interpretation of +"0x" - return high !== high || escapedWhitespace ? - escaped : - high < 0 ? - // BMP codepoint - String.fromCharCode(high + 0x10000) : - // Supplemental Plane codepoint (surrogate pair) - String.fromCharCode(high >> 10 | 0xD800, high & 0x3FF | 0xDC00); - }; - - // Optimize for push.apply( _, NodeList ) - try { - push.apply( - (arr = slice.call(preferredDoc.childNodes)), - preferredDoc.childNodes - ); - // Support: Android<4.0 - // Detect silently failing push.apply - arr[preferredDoc.childNodes.length].nodeType; - } catch (e) { - push = { - apply: arr.length ? - - // Leverage slice if possible - function(target, els) { - push_native.apply(target, slice.call(els)); - } : - - // Support: IE<9 - // Otherwise append directly - function(target, els) { - var j = target.length, - i = 0; - // Can't trust NodeList.length - while ((target[j++] = els[i++])) {} - target.length = j - 1; - } - }; - } - - function Sizzle(selector, context, results, seed) { - var match, elem, m, nodeType, - // QSA vars - i, groups, old, nid, newContext, newSelector; - - if ((context ? context.ownerDocument || context : preferredDoc) !== document) { - setDocument(context); - } - - context = context || document; - results = results || []; - - if (!selector || typeof selector !== "string") { - return results; - } - - if ((nodeType = context.nodeType) !== 1 && nodeType !== 9) { - return []; - } - - if (documentIsHTML && !seed) { - - // Shortcuts - if ((match = rquickExpr.exec(selector))) { - // Speed-up: Sizzle("#ID") - if ((m = match[1])) { - if (nodeType === 9) { - elem = context.getElementById(m); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document (jQuery #6963) - if (elem && elem.parentNode) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if (elem.id === m) { - results.push(elem); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if (context.ownerDocument && (elem = context.ownerDocument.getElementById(m)) && - contains(context, elem) && elem.id === m) { - results.push(elem); - return results; - } - } - - // Speed-up: Sizzle("TAG") - } else if (match[2]) { - push.apply(results, context.getElementsByTagName(selector)); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ((m = match[3]) && support.getElementsByClassName) { - push.apply(results, context.getElementsByClassName(m)); - return results; - } - } - - // QSA path - if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) { - nid = old = expando; - newContext = context; - newSelector = nodeType === 9 && selector; - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if (nodeType === 1 && context.nodeName.toLowerCase() !== "object") { - groups = tokenize(selector); - - if ((old = context.getAttribute("id"))) { - nid = old.replace(rescape, "\\$&"); - } else { - context.setAttribute("id", nid); - } - nid = "[id='" + nid + "'] "; - - i = groups.length; - while (i--) { - groups[i] = nid + toSelector(groups[i]); - } - newContext = rsibling.test(selector) && testContext(context.parentNode) || context; - newSelector = groups.join(","); - } - - if (newSelector) { - try { - push.apply(results, - newContext.querySelectorAll(newSelector) - ); - return results; - } catch (qsaError) {} finally { - if (!old) { - context.removeAttribute("id"); - } - } - } - } - } - - // All others - return select(selector.replace(rtrim, "$1"), context, results, seed); - } - - /** - * Create key-value caches of limited size - * @returns {Function(string, Object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ - function createCache() { - var keys = []; - - function cache(key, value) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if (keys.push(key + " ") > Expr.cacheLength) { - // Only keep the most recent entries - delete cache[keys.shift()]; - } - return (cache[key + " "] = value); - } - return cache; - } - - /** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ - function markFunction(fn) { - fn[expando] = true; - return fn; - } - - /** - * Support testing using an element - * @param {Function} fn Passed the created div and expects a boolean result - */ - function assert(fn) { - var div = document.createElement("div"); - - try { - return !!fn(div); - } catch (e) { - return false; - } finally { - // Remove from its parent by default - if (div.parentNode) { - div.parentNode.removeChild(div); - } - // release memory in IE - div = null; - } - } - - /** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ - function addHandle(attrs, handler) { - var arr = attrs.split("|"), - i = attrs.length; - - while (i--) { - Expr.attrHandle[arr[i]] = handler; - } - } - - /** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ - function siblingCheck(a, b) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - (~b.sourceIndex || MAX_NEGATIVE) - - (~a.sourceIndex || MAX_NEGATIVE); - - // Use IE sourceIndex if available on both nodes - if (diff) { - return diff; - } - - // Check if b follows a - if (cur) { - while ((cur = cur.nextSibling)) { - if (cur === b) { - return -1; - } - } - } - - return a ? 1 : -1; - } - - /** - * Returns a function to use in pseudos for input types - * @param {String} type - */ - function createInputPseudo(type) { - return function(elem) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; - } - - /** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ - function createButtonPseudo(type) { - return function(elem) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; - } - - /** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ - function createPositionalPseudo(fn) { - return markFunction(function(argument) { - argument = +argument; - return markFunction(function(seed, matches) { - var j, - matchIndexes = fn([], seed.length, argument), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while (i--) { - if (seed[(j = matchIndexes[i])]) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); - } - - /** - * Checks a node for validity as a Sizzle context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ - function testContext(context) { - return context && typeof context.getElementsByTagName !== strundefined && context; - } - - // Expose support vars for convenience - support = Sizzle.support = {}; - - /** - * Detects XML nodes - * @param {Element|Object} elem An element or a document - * @returns {Boolean} True iff elem is a non-HTML XML node - */ - isXML = Sizzle.isXML = function(elem) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; - }; - - /** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ - setDocument = Sizzle.setDocument = function(node) { - var hasCompare, - doc = node ? node.ownerDocument || node : preferredDoc, - parent = doc.defaultView; - - function getTop(win) { - // Edge throws a lovely Object expected if you try to get top on a detached reference see #2642 - try { - return win.top; - } catch (ex) { - // Ignore - } - - return null; - } - - // If no document and documentElement is available, return - if (doc === document || doc.nodeType !== 9 || !doc.documentElement) { - return document; - } - - // Set our document - document = doc; - docElem = doc.documentElement; - - // Support tests - documentIsHTML = !isXML(doc); - - // Support: IE>8 - // If iframe document is assigned to "document" variable and if iframe has been reloaded, - // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 - // IE6-8 do not support the defaultView property so parent will be undefined - if (parent && parent !== getTop(parent)) { - // IE11 does not have attachEvent, so all must suffer - if (parent.addEventListener) { - parent.addEventListener("unload", function() { - setDocument(); - }, false); - } else if (parent.attachEvent) { - parent.attachEvent("onunload", function() { - setDocument(); - }); - } - } - - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) - support.attributes = assert(function(div) { - div.className = "i"; - return !div.getAttribute("className"); - }); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert(function(div) { - div.appendChild(doc.createComment("")); - return !div.getElementsByTagName("*").length; - }); - - // Support: IE<9 - support.getElementsByClassName = rnative.test(doc.getElementsByClassName); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert(function(div) { - docElem.appendChild(div).id = expando; - return !doc.getElementsByName || !doc.getElementsByName(expando).length; - }); - - // ID find and filter - if (support.getById) { - Expr.find["ID"] = function(id, context) { - if (typeof context.getElementById !== strundefined && documentIsHTML) { - var m = context.getElementById(id); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - }; - Expr.filter["ID"] = function(id) { - var attrId = id.replace(runescape, funescape); - return function(elem) { - return elem.getAttribute("id") === attrId; - }; - }; - } else { - // Support: IE6/7 - // getElementById is not reliable as a find shortcut - delete Expr.find["ID"]; - - Expr.filter["ID"] = function(id) { - var attrId = id.replace(runescape, funescape); - return function(elem) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - } - - // Tag - Expr.find["TAG"] = support.getElementsByTagName ? - function(tag, context) { - if (typeof context.getElementsByTagName !== strundefined) { - return context.getElementsByTagName(tag); - } - } : - function(tag, context) { - var elem, - tmp = [], - i = 0, - results = context.getElementsByTagName(tag); - - // Filter out possible comments - if (tag === "*") { - while ((elem = results[i++])) { - if (elem.nodeType === 1) { - tmp.push(elem); - } - } - - return tmp; - } - return results; - }; - - // Class - Expr.find["CLASS"] = support.getElementsByClassName && function(className, context) { - if (documentIsHTML) { - return context.getElementsByClassName(className); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See http://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; - - if ((support.qsa = rnative.test(doc.querySelectorAll))) { - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function(div) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - div.innerHTML = "<select msallowcapture=''><option selected=''></option></select>"; - - // Support: IE8, Opera 11-12.16 - // Nothing should be selected when empty strings follow ^= or $= or *= - // The test attribute must be unknown in Opera but "safe" for WinRT - // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if (div.querySelectorAll("[msallowcapture^='']").length) { - rbuggyQSA.push("[*^$]=" + whitespace + "*(?:''|\"\")"); - } - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if (!div.querySelectorAll("[selected]").length) { - rbuggyQSA.push("\\[" + whitespace + "*(?:value|" + booleans + ")"); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if (!div.querySelectorAll(":checked").length) { - rbuggyQSA.push(":checked"); - } - }); - - assert(function(div) { - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - var input = doc.createElement("input"); - input.setAttribute("type", "hidden"); - div.appendChild(input).setAttribute("name", "D"); - - // Support: IE8 - // Enforce case-sensitivity of name attribute - if (div.querySelectorAll("[name=d]").length) { - rbuggyQSA.push("name" + whitespace + "*[*^$|!~]?="); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if (!div.querySelectorAll(":enabled").length) { - rbuggyQSA.push(":enabled", ":disabled"); - } - - // Opera 10-11 does not throw on post-comma invalid pseudos - div.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } - - if ((support.matchesSelector = rnative.test((matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector)))) { - - assert(function(div) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call(div, "div"); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call(div, "[s!='']:x"); - rbuggyMatches.push("!=", pseudos); - }); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp(rbuggyQSA.join("|")); - rbuggyMatches = rbuggyMatches.length && new RegExp(rbuggyMatches.join("|")); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test(docElem.compareDocumentPosition); - - // Element contains another - // Purposefully does not implement inclusive descendent - // As in, an element does not contain itself - contains = hasCompare || rnative.test(docElem.contains) ? - function(a, b) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!(bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains(bup) : - a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16 - )); - } : - function(a, b) { - if (b) { - while ((b = b.parentNode)) { - if (b === a) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = hasCompare ? - function(a, b) { - - // Flag for duplicate removal - if (a === b) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if (compare) { - return compare; - } - - // Calculate position if both inputs belong to the same document - compare = (a.ownerDocument || a) === (b.ownerDocument || b) ? - a.compareDocumentPosition(b) : - - // Otherwise we know they are disconnected - 1; - - // Disconnected nodes - if (compare & 1 || - (!support.sortDetached && b.compareDocumentPosition(a) === compare)) { - - // Choose the first element that is related to our preferred document - if (a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a)) { - return -1; - } - if (b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b)) { - return 1; - } - - // Maintain original order - return sortInput ? - (indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) : - 0; - } - - return compare & 4 ? -1 : 1; - } : - function(a, b) { - // Exit early if the nodes are identical - if (a === b) { - hasDuplicate = true; - return 0; - } - - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [a], - bp = [b]; - - // Parentless nodes are either documents or disconnected - if (!aup || !bup) { - return a === doc ? -1 : - b === doc ? 1 : - aup ? -1 : - bup ? 1 : - sortInput ? - (indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) : - 0; - - // If the nodes are siblings, we can do a quick check - } else if (aup === bup) { - return siblingCheck(a, b); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ((cur = cur.parentNode)) { - ap.unshift(cur); - } - cur = b; - while ((cur = cur.parentNode)) { - bp.unshift(cur); - } - - // Walk down the tree looking for a discrepancy - while (ap[i] === bp[i]) { - i++; - } - - return i ? - // Do a sibling check if the nodes have a common ancestor - siblingCheck(ap[i], bp[i]) : - - // Otherwise nodes in our document sort first - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; - - return doc; - }; - - Sizzle.matches = function(expr, elements) { - return Sizzle(expr, null, null, elements); - }; - - Sizzle.matchesSelector = function(elem, expr) { - // Set document vars if needed - if ((elem.ownerDocument || elem) !== document) { - setDocument(elem); - } - - // Make sure that attribute selectors are quoted - expr = expr.replace(rattributeQuotes, "='$1']"); - - if (support.matchesSelector && documentIsHTML && - (!rbuggyMatches || !rbuggyMatches.test(expr)) && - (!rbuggyQSA || !rbuggyQSA.test(expr))) { - - try { - var ret = matches.call(elem, expr); - - // IE 9's matchesSelector returns false on disconnected nodes - if (ret || support.disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11) { - return ret; - } - } catch (e) {} - } - - return Sizzle(expr, document, null, [elem]).length > 0; - }; - - Sizzle.contains = function(context, elem) { - // Set document vars if needed - if ((context.ownerDocument || context) !== document) { - setDocument(context); - } - return contains(context, elem); - }; - - Sizzle.attr = function(elem, name) { - // Set document vars if needed - if ((elem.ownerDocument || elem) !== document) { - setDocument(elem); - } - - var fn = Expr.attrHandle[name.toLowerCase()], - // Don't get fooled by Object.prototype properties (jQuery #13807) - val = fn && hasOwn.call(Expr.attrHandle, name.toLowerCase()) ? - fn(elem, name, !documentIsHTML) : - undefined; - - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute(name) : - (val = elem.getAttributeNode(name)) && val.specified ? - val.value : - null; - }; - - Sizzle.error = function(msg) { - throw new Error("Syntax error, unrecognized expression: " + msg); - }; - - /** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ - Sizzle.uniqueSort = function(results) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice(0); - results.sort(sortOrder); - - if (hasDuplicate) { - while ((elem = results[i++])) { - if (elem === results[i]) { - j = duplicates.push(i); - } - } - while (j--) { - results.splice(duplicates[j], 1); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; - }; - - /** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ - getText = Sizzle.getText = function(elem) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if (!nodeType) { - // If no nodeType, this is expected to be an array - while ((node = elem[i++])) { - // Do not traverse comment nodes - ret += getText(node); - } - } else if (nodeType === 1 || nodeType === 9 || nodeType === 11) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if (typeof elem.textContent === "string") { - return elem.textContent; - } else { - // Traverse its children - for (elem = elem.firstChild; elem; elem = elem.nextSibling) { - ret += getText(elem); - } - } - } else if (nodeType === 3 || nodeType === 4) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - - return ret; - }; - - Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { - dir: "parentNode", - first: true - }, - " ": { - dir: "parentNode" - }, - "+": { - dir: "previousSibling", - first: true - }, - "~": { - dir: "previousSibling" - } - }, - - preFilter: { - "ATTR": function(match) { - match[1] = match[1].replace(runescape, funescape); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = (match[3] || match[4] || match[5] || "").replace(runescape, funescape); - - if (match[2] === "~=") { - match[3] = " " + match[3] + " "; - } - - return match.slice(0, 4); - }, - - "CHILD": function(match) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if (match[1].slice(0, 3) === "nth") { - // nth-* requires argument - if (!match[3]) { - Sizzle.error(match[0]); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[4] = +(match[4] ? match[5] + (match[6] || 1) : 2 * (match[3] === "even" || match[3] === "odd")); - match[5] = +((match[7] + match[8]) || match[3] === "odd"); - - // other types prohibit arguments - } else if (match[3]) { - Sizzle.error(match[0]); - } - - return match; - }, - - "PSEUDO": function(match) { - var excess, - unquoted = !match[6] && match[2]; - - if (matchExpr["CHILD"].test(match[0])) { - return null; - } - - // Accept quoted arguments as-is - if (match[3]) { - match[2] = match[4] || match[5] || ""; - - // Strip excess characters from unquoted arguments - } else if (unquoted && rpseudo.test(unquoted) && - // Get excess from tokenize (recursively) - (excess = tokenize(unquoted, true)) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)) { - - // excess is a negative index - match[0] = match[0].slice(0, excess); - match[2] = unquoted.slice(0, excess); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice(0, 3); - } - }, - - filter: { - - "TAG": function(nodeNameSelector) { - var nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase(); - return nodeNameSelector === "*" ? - function() { - return true; - } : - function(elem) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function(className) { - var pattern = classCache[className + " "]; - - return pattern || - (pattern = new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)")) && - classCache(className, function(elem) { - return pattern.test(typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || ""); - }); - }, - - "ATTR": function(name, operator, check) { - return function(elem) { - var result = Sizzle.attr(elem, name); - - if (result == null) { - return operator === "!="; - } - if (!operator) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf(check) === 0 : - operator === "*=" ? check && result.indexOf(check) > -1 : - operator === "$=" ? check && result.slice(-check.length) === check : - operator === "~=" ? (" " + result + " ").indexOf(check) > -1 : - operator === "|=" ? result === check || result.slice(0, check.length + 1) === check + "-" : - false; - }; - }, - - "CHILD": function(type, what, argument, first, last) { - var simple = type.slice(0, 3) !== "nth", - forward = type.slice(-4) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function(elem) { - return !!elem.parentNode; - } : - - function(elem, context, xml) { - var cache, outerCache, node, diff, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType; - - if (parent) { - - // :(first|last|only)-(child|of-type) - if (simple) { - while (dir) { - node = elem; - while ((node = node[dir])) { - if (ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) { - return false; - } - } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [forward ? parent.firstChild : parent.lastChild]; - - // non-xml :nth-child(...) stores cache data on `parent` - if (forward && useCache) { - // Seek `elem` from a previously-cached index - outerCache = parent[expando] || (parent[expando] = {}); - cache = outerCache[type] || []; - nodeIndex = cache[0] === dirruns && cache[1]; - diff = cache[0] === dirruns && cache[2]; - node = nodeIndex && parent.childNodes[nodeIndex]; - - while ((node = ++nodeIndex && node && node[dir] || - - // Fallback to seeking `elem` from the start - (diff = nodeIndex = 0) || start.pop())) { - - // When found, cache indexes on `parent` and break - if (node.nodeType === 1 && ++diff && node === elem) { - outerCache[type] = [dirruns, nodeIndex, diff]; - break; - } - } - - // Use previously-cached element index if available - } else if (useCache && (cache = (elem[expando] || (elem[expando] = {}))[type]) && cache[0] === dirruns) { - diff = cache[1]; - - // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) - } else { - // Use the same loop as above to seek `elem` from the start - while ((node = ++nodeIndex && node && node[dir] || - (diff = nodeIndex = 0) || start.pop())) { - - if ((ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) && ++diff) { - // Cache the index of each encountered element - if (useCache) { - (node[expando] || (node[expando] = {}))[type] = [dirruns, diff]; - } - - if (node === elem) { - break; - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || (diff % first === 0 && diff / first >= 0); - } - }; - }, - - "PSEUDO": function(pseudo, argument) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[pseudo] || Expr.setFilters[pseudo.toLowerCase()] || - Sizzle.error("unsupported pseudo: " + pseudo); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if (fn[expando]) { - return fn(argument); - } - - // But maintain support for old signatures - if (fn.length > 1) { - args = [pseudo, pseudo, "", argument]; - return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase()) ? - markFunction(function(seed, matches) { - var idx, - matched = fn(seed, argument), - i = matched.length; - while (i--) { - idx = indexOf.call(seed, matched[i]); - seed[idx] = !(matches[idx] = matched[i]); - } - }) : - function(elem) { - return fn(elem, 0, args); - }; - } - - return fn; - } - }, - - pseudos: { - // Potentially complex pseudos - "not": markFunction(function(selector) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile(selector.replace(rtrim, "$1")); - - return matcher[expando] ? - markFunction(function(seed, matches, context, xml) { - var elem, - unmatched = matcher(seed, null, xml, []), - i = seed.length; - - // Match elements unmatched by `matcher` - while (i--) { - if ((elem = unmatched[i])) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function(elem, context, xml) { - input[0] = elem; - matcher(input, null, xml, results); - return !results.pop(); - }; - }), - - "has": markFunction(function(selector) { - return function(elem) { - return Sizzle(selector, elem).length > 0; - }; - }), - - "contains": markFunction(function(text) { - text = text.replace(runescape, funescape); - return function(elem) { - return (elem.textContent || elem.innerText || getText(elem)).indexOf(text) > -1; - }; - }), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction(function(lang) { - // lang value must be a valid identifier - if (!ridentifier.test(lang || "")) { - Sizzle.error("unsupported lang: " + lang); - } - lang = lang.replace(runescape, funescape).toLowerCase(); - return function(elem) { - var elemLang; - do { - if ((elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute("xml:lang") || elem.getAttribute("lang"))) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf(lang + "-") === 0; - } - } while ((elem = elem.parentNode) && elem.nodeType === 1); - return false; - }; - }), - - // Miscellaneous - "target": function(elem) { - var hash = window.location && window.location.hash; - return hash && hash.slice(1) === elem.id; - }, - - "root": function(elem) { - return elem === docElem; - }, - - "focus": function(elem) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - // Boolean properties - "enabled": function(elem) { - return elem.disabled === false; - }, - - "disabled": function(elem) { - return elem.disabled === true; - }, - - "checked": function(elem) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function(elem) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if (elem.parentNode) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - "empty": function(elem) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for (elem = elem.firstChild; elem; elem = elem.nextSibling) { - if (elem.nodeType < 6) { - return false; - } - } - return true; - }, - - "parent": function(elem) { - return !Expr.pseudos["empty"](elem); - }, - - // Element/input types - "header": function(elem) { - return rheader.test(elem.nodeName); - }, - - "input": function(elem) { - return rinputs.test(elem.nodeName); - }, - - "button": function(elem) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function(elem) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - ((attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text"); - }, - - // Position-in-collection - "first": createPositionalPseudo(function() { - return [0]; - }), - - "last": createPositionalPseudo(function(matchIndexes, length) { - return [length - 1]; - }), - - "eq": createPositionalPseudo(function(matchIndexes, length, argument) { - return [argument < 0 ? argument + length : argument]; - }), - - "even": createPositionalPseudo(function(matchIndexes, length) { - var i = 0; - for (; i < length; i += 2) { - matchIndexes.push(i); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function(matchIndexes, length) { - var i = 1; - for (; i < length; i += 2) { - matchIndexes.push(i); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function(matchIndexes, length, argument) { - var i = argument < 0 ? argument + length : argument; - for (; --i >= 0;) { - matchIndexes.push(i); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function(matchIndexes, length, argument) { - var i = argument < 0 ? argument + length : argument; - for (; ++i < length;) { - matchIndexes.push(i); - } - return matchIndexes; - }) - } - }; - - Expr.pseudos["nth"] = Expr.pseudos["eq"]; - - // Add button/input type pseudos - for (i in { - radio: true, - checkbox: true, - file: true, - password: true, - image: true - }) { - Expr.pseudos[i] = createInputPseudo(i); - } - for (i in { - submit: true, - reset: true - }) { - Expr.pseudos[i] = createButtonPseudo(i); - } - - // Easy API for creating new setFilters - function setFilters() {} - setFilters.prototype = Expr.filters = Expr.pseudos; - Expr.setFilters = new setFilters(); - - tokenize = Sizzle.tokenize = function(selector, parseOnly) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[selector + " "]; - - if (cached) { - return parseOnly ? 0 : cached.slice(0); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while (soFar) { - - // Comma and first run - if (!matched || (match = rcomma.exec(soFar))) { - if (match) { - // Don't consume trailing commas as valid - soFar = soFar.slice(match[0].length) || soFar; - } - groups.push((tokens = [])); - } - - matched = false; - - // Combinators - if ((match = rcombinators.exec(soFar))) { - matched = match.shift(); - tokens.push({ - value: matched, - // Cast descendant combinators to space - type: match[0].replace(rtrim, " ") - }); - soFar = soFar.slice(matched.length); - } - - // Filters - for (type in Expr.filter) { - if ((match = matchExpr[type].exec(soFar)) && (!preFilters[type] || - (match = preFilters[type](match)))) { - matched = match.shift(); - tokens.push({ - value: matched, - type: type, - matches: match - }); - soFar = soFar.slice(matched.length); - } - } - - if (!matched) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error(selector) : - // Cache the tokens - tokenCache(selector, groups).slice(0); - }; - - function toSelector(tokens) { - var i = 0, - len = tokens.length, - selector = ""; - for (; i < len; i++) { - selector += tokens[i].value; - } - return selector; - } - - function addCombinator(matcher, combinator, base) { - var dir = combinator.dir, - checkNonElements = base && dir === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function(elem, context, xml) { - while ((elem = elem[dir])) { - if (elem.nodeType === 1 || checkNonElements) { - return matcher(elem, context, xml); - } - } - } : - - // Check against all ancestor/preceding elements - function(elem, context, xml) { - var oldCache, outerCache, - newCache = [dirruns, doneName]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching - if (xml) { - while ((elem = elem[dir])) { - if (elem.nodeType === 1 || checkNonElements) { - if (matcher(elem, context, xml)) { - return true; - } - } - } - } else { - while ((elem = elem[dir])) { - if (elem.nodeType === 1 || checkNonElements) { - outerCache = elem[expando] || (elem[expando] = {}); - if ((oldCache = outerCache[dir]) && - oldCache[0] === dirruns && oldCache[1] === doneName) { - - // Assign to newCache so results back-propagate to previous elements - return (newCache[2] = oldCache[2]); - } else { - // Reuse newcache so results back-propagate to previous elements - outerCache[dir] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ((newCache[2] = matcher(elem, context, xml))) { - return true; - } - } - } - } - } - }; - } - - function elementMatcher(matchers) { - return matchers.length > 1 ? - function(elem, context, xml) { - var i = matchers.length; - while (i--) { - if (!matchers[i](elem, context, xml)) { - return false; - } - } - return true; - } : - matchers[0]; - } - - function multipleContexts(selector, contexts, results) { - var i = 0, - len = contexts.length; - for (; i < len; i++) { - Sizzle(selector, contexts[i], results); - } - return results; - } - - function condense(unmatched, map, filter, context, xml) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for (; i < len; i++) { - if ((elem = unmatched[i])) { - if (!filter || filter(elem, context, xml)) { - newUnmatched.push(elem); - if (mapped) { - map.push(i); - } - } - } - } - - return newUnmatched; - } - - function setMatcher(preFilter, selector, matcher, postFilter, postFinder, postSelector) { - if (postFilter && !postFilter[expando]) { - postFilter = setMatcher(postFilter); - } - if (postFinder && !postFinder[expando]) { - postFinder = setMatcher(postFinder, postSelector); - } - return markFunction(function(seed, results, context, xml) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts(selector || "*", context.nodeType ? [context] : context, []), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && (seed || !selector) ? - condense(elems, preMap, preFilter, context, xml) : - elems, - - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || (seed ? preFilter : preexisting || postFilter) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if (matcher) { - matcher(matcherIn, matcherOut, context, xml); - } - - // Apply postFilter - if (postFilter) { - temp = condense(matcherOut, postMap); - postFilter(temp, [], context, xml); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while (i--) { - if ((elem = temp[i])) { - matcherOut[postMap[i]] = !(matcherIn[postMap[i]] = elem); - } - } - } - - if (seed) { - if (postFinder || preFilter) { - if (postFinder) { - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while (i--) { - if ((elem = matcherOut[i])) { - // Restore matcherIn since elem is not yet a final match - temp.push((matcherIn[i] = elem)); - } - } - postFinder(null, (matcherOut = []), temp, xml); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while (i--) { - if ((elem = matcherOut[i]) && - (temp = postFinder ? indexOf.call(seed, elem) : preMap[i]) > -1) { - - seed[temp] = !(results[temp] = elem); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice(preexisting, matcherOut.length) : - matcherOut - ); - if (postFinder) { - postFinder(null, results, matcherOut, xml); - } else { - push.apply(results, matcherOut); - } - } - }); - } - - function matcherFromTokens(tokens) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[tokens[0].type], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator(function(elem) { - return elem === checkContext; - }, implicitRelative, true), - matchAnyContext = addCombinator(function(elem) { - return indexOf.call(checkContext, elem) > -1; - }, implicitRelative, true), - matchers = [function(elem, context, xml) { - return (!leadingRelative && (xml || context !== outermostContext)) || ( - (checkContext = context).nodeType ? - matchContext(elem, context, xml) : - matchAnyContext(elem, context, xml)); - }]; - - for (; i < len; i++) { - if ((matcher = Expr.relative[tokens[i].type])) { - matchers = [addCombinator(elementMatcher(matchers), matcher)]; - } else { - matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches); - - // Return special upon seeing a positional matcher - if (matcher[expando]) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for (; j < len; j++) { - if (Expr.relative[tokens[j].type]) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher(matchers), - i > 1 && toSelector( - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice(0, i - 1).concat({ - value: tokens[i - 2].type === " " ? "*" : "" - }) - ).replace(rtrim, "$1"), - matcher, - i < j && matcherFromTokens(tokens.slice(i, j)), - j < len && matcherFromTokens((tokens = tokens.slice(j))), - j < len && toSelector(tokens) - ); - } - matchers.push(matcher); - } - } - - return elementMatcher(matchers); - } - - function matcherFromGroupMatchers(elementMatchers, setMatchers) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function(seed, context, xml, results, outermost) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find["TAG"]("*", outermost), - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), - len = elems.length; - - if (outermost) { - outermostContext = context !== document && context; - } - - // Add elements passing elementMatchers directly to results - // Keep `i` a string if there are no elements so `matchedCount` will be "00" below - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id - for (; i !== len && (elem = elems[i]) != null; i++) { - if (byElement && elem) { - j = 0; - while ((matcher = elementMatchers[j++])) { - if (matcher(elem, context, xml)) { - results.push(elem); - break; - } - } - if (outermost) { - dirruns = dirrunsUnique; - } - } - - // Track unmatched elements for set filters - if (bySet) { - // They will have gone through all possible matchers - if ((elem = !matcher && elem)) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if (seed) { - unmatched.push(elem); - } - } - } - - // Apply set filters to unmatched elements - matchedCount += i; - if (bySet && i !== matchedCount) { - j = 0; - while ((matcher = setMatchers[j++])) { - matcher(unmatched, setMatched, context, xml); - } - - if (seed) { - // Reintegrate element matches to eliminate the need for sorting - if (matchedCount > 0) { - while (i--) { - if (!(unmatched[i] || setMatched[i])) { - setMatched[i] = pop.call(results); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense(setMatched); - } - - // Add matches to results - push.apply(results, setMatched); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if (outermost && !seed && setMatched.length > 0 && - (matchedCount + setMatchers.length) > 1) { - - Sizzle.uniqueSort(results); - } - } - - // Override manipulation of globals by nested matchers - if (outermost) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction(superMatcher) : - superMatcher; - } - - compile = Sizzle.compile = function(selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[selector + " "]; - - if (!cached) { - // Generate a function of recursive functions that can be used to check each element - if (!match) { - match = tokenize(selector); - } - i = match.length; - while (i--) { - cached = matcherFromTokens(match[i]); - if (cached[expando]) { - setMatchers.push(cached); - } else { - elementMatchers.push(cached); - } - } - - // Cache the compiled function - cached = compilerCache(selector, matcherFromGroupMatchers(elementMatchers, setMatchers)); - - // Save selector and tokenization - cached.selector = selector; - } - return cached; - }; - - /** - * A low-level selection function that works with Sizzle's compiled - * selector functions - * @param {String|Function} selector A selector or a pre-compiled - * selector function built with Sizzle.compile - * @param {Element} context - * @param {Array} [results] - * @param {Array} [seed] A set of elements to match against - */ - select = Sizzle.select = function(selector, context, results, seed) { - var i, tokens, token, type, find, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize((selector = compiled.selector || selector)); - - results = results || []; - - // Try to minimize operations if there is no seed and only one group - if (match.length === 1) { - - // Take a shortcut and set the context if the root selector is an ID - tokens = match[0] = match[0].slice(0); - if (tokens.length > 2 && (token = tokens[0]).type === "ID" && - support.getById && context.nodeType === 9 && documentIsHTML && - Expr.relative[tokens[1].type]) { - - context = (Expr.find["ID"](token.matches[0].replace(runescape, funescape), context) || [])[0]; - if (!context) { - return results; - - // Precompiled matchers will still verify ancestry, so step up a level - } else if (compiled) { - context = context.parentNode; - } - - selector = selector.slice(tokens.shift().value.length); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr["needsContext"].test(selector) ? 0 : tokens.length; - while (i--) { - token = tokens[i]; - - // Abort if we hit a combinator - if (Expr.relative[(type = token.type)]) { - break; - } - if ((find = Expr.find[type])) { - // Search, expanding context for leading sibling combinators - if ((seed = find( - token.matches[0].replace(runescape, funescape), - rsibling.test(tokens[0].type) && testContext(context.parentNode) || context - ))) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice(i, 1); - selector = seed.length && toSelector(tokens); - if (!selector) { - push.apply(results, seed); - return results; - } - - break; - } - } - } - } - - // Compile and execute a filtering function if one is not provided - // Provide `match` to avoid retokenization if we modified the selector above - (compiled || compile(selector, match))( - seed, - context, !documentIsHTML, - results, - rsibling.test(selector) && testContext(context.parentNode) || context - ); - return results; - }; - - // One-time assignments - - // Sort stability - support.sortStable = expando.split("").sort(sortOrder).join("") === expando; - - // Support: Chrome 14-35+ - // Always assume duplicates if they aren't passed to the comparison function - support.detectDuplicates = !!hasDuplicate; - - // Initialize against the default document - setDocument(); - - // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) - // Detached nodes confoundingly follow *each other* - support.sortDetached = assert(function(div1) { - // Should return 1, but returns 4 (following) - return div1.compareDocumentPosition(document.createElement("div")) & 1; - }); - - // Support: IE<8 - // Prevent attribute/property "interpolation" - // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx - if (!assert(function(div) { - div.innerHTML = "<a href='#'></a>"; - return div.firstChild.getAttribute("href") === "#"; - })) { - addHandle("type|href|height|width", function(elem, name, isXML) { - if (!isXML) { - return elem.getAttribute(name, name.toLowerCase() === "type" ? 1 : 2); - } - }); - } - - // Support: IE<9 - // Use defaultValue in place of getAttribute("value") - if (!support.attributes || !assert(function(div) { - div.innerHTML = "<input/>"; - div.firstChild.setAttribute("value", ""); - return div.firstChild.getAttribute("value") === ""; - })) { - addHandle("value", function(elem, name, isXML) { - if (!isXML && elem.nodeName.toLowerCase() === "input") { - return elem.defaultValue; - } - }); - } - - // Support: IE<9 - // Use getAttributeNode to fetch booleans when getAttribute lies - if (!assert(function(div) { - return div.getAttribute("disabled") == null; - })) { - addHandle(booleans, function(elem, name, isXML) { - var val; - if (!isXML) { - return elem[name] === true ? name.toLowerCase() : - (val = elem.getAttributeNode(name)) && val.specified ? - val.value : - null; - } - }); - } - - // EXPOSE - return Sizzle; - } - ); - - /*eslint-enable */ - - /** - * Arr.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Array utility class. - * - * @private - * @class tinymce.util.Arr - */ - define( - 'tinymce.core.util.Arr', [], - function() { - var isArray = Array.isArray || function(obj) { - return Object.prototype.toString.call(obj) === "[object Array]"; - }; - - function toArray(obj) { - var array = obj, - i, l; - - if (!isArray(obj)) { - array = []; - for (i = 0, l = obj.length; i < l; i++) { - array[i] = obj[i]; - } - } - - return array; - } - - function each(o, cb, s) { - var n, l; - - if (!o) { - return 0; - } - - s = s || o; - - if (o.length !== undefined) { - // Indexed arrays, needed for Safari - for (n = 0, l = o.length; n < l; n++) { - if (cb.call(s, o[n], n, o) === false) { - return 0; - } - } - } else { - // Hashtables - for (n in o) { - if (o.hasOwnProperty(n)) { - if (cb.call(s, o[n], n, o) === false) { - return 0; - } - } - } - } - - return 1; - } - - function map(array, callback) { - var out = []; - - each(array, function(item, index) { - out.push(callback(item, index, array)); - }); - - return out; - } - - function filter(a, f) { - var o = []; - - each(a, function(v, index) { - if (!f || f(v, index, a)) { - o.push(v); - } - }); - - return o; - } - - function indexOf(a, v) { - var i, l; - - if (a) { - for (i = 0, l = a.length; i < l; i++) { - if (a[i] === v) { - return i; - } - } - } - - return -1; - } - - function reduce(collection, iteratee, accumulator, thisArg) { - var i = 0; - - if (arguments.length < 3) { - accumulator = collection[0]; - } - - for (; i < collection.length; i++) { - accumulator = iteratee.call(thisArg, accumulator, collection[i], i); - } - - return accumulator; - } - - function findIndex(array, predicate, thisArg) { - var i, l; - - for (i = 0, l = array.length; i < l; i++) { - if (predicate.call(thisArg, array[i], i, array)) { - return i; - } - } - - return -1; - } - - function find(array, predicate, thisArg) { - var idx = findIndex(array, predicate, thisArg); - - if (idx !== -1) { - return array[idx]; - } - - return undefined; - } - - function last(collection) { - return collection[collection.length - 1]; - } - - return { - isArray: isArray, - toArray: toArray, - each: each, - map: map, - filter: filter, - indexOf: indexOf, - reduce: reduce, - findIndex: findIndex, - find: find, - last: last - }; - } - ); - /** - * Tools.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class contains various utlity functions. These are also exposed - * directly on the tinymce namespace. - * - * @class tinymce.util.Tools - */ - define( - 'tinymce.core.util.Tools', [ - "tinymce.core.Env", - "tinymce.core.util.Arr" - ], - function(Env, Arr) { - /** - * Removes whitespace from the beginning and end of a string. - * - * @method trim - * @param {String} s String to remove whitespace from. - * @return {String} New string with removed whitespace. - */ - var whiteSpaceRegExp = /^\s*|\s*$/g; - - function trim(str) { - return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, ''); - } - - /** - * Checks if a object is of a specific type for example an array. - * - * @method is - * @param {Object} obj Object to check type of. - * @param {string} type Optional type to check for. - * @return {Boolean} true/false if the object is of the specified type. - */ - function is(obj, type) { - if (!type) { - return obj !== undefined; - } - - if (type == 'array' && Arr.isArray(obj)) { - return true; - } - - return typeof obj == type; - } - - /** - * Makes a name/object map out of an array with names. - * - * @method makeMap - * @param {Array/String} items Items to make map out of. - * @param {String} delim Optional delimiter to split string by. - * @param {Object} map Optional map to add items to. - * @return {Object} Name/value map of items. - */ - function makeMap(items, delim, map) { - var i; - - items = items || []; - delim = delim || ','; - - if (typeof items == "string") { - items = items.split(delim); - } - - map = map || {}; - - i = items.length; - while (i--) { - map[items[i]] = {}; - } - - return map; - } - - /** - * JavaScript does not protect hasOwnProperty method, so it is possible to overwrite it. This is - * object independent version. - * - * @param {Object} obj - * @param {String} prop - * @returns {Boolean} - */ - function hasOwnProperty(obj, prop) { - return Object.prototype.hasOwnProperty.call(obj, prop); - } - - /** - * Creates a class, subclass or static singleton. - * More details on this method can be found in the Wiki. - * - * @method create - * @param {String} s Class name, inheritance and prefix. - * @param {Object} p Collection of methods to add to the class. - * @param {Object} root Optional root object defaults to the global window object. - * @example - * // Creates a basic class - * tinymce.create('tinymce.somepackage.SomeClass', { - * SomeClass: function() { - * // Class constructor - * }, - * - * method: function() { - * // Some method - * } - * }); - * - * // Creates a basic subclass class - * tinymce.create('tinymce.somepackage.SomeSubClass:tinymce.somepackage.SomeClass', { - * SomeSubClass: function() { - * // Class constructor - * this.parent(); // Call parent constructor - * }, - * - * method: function() { - * // Some method - * this.parent(); // Call parent method - * }, - * - * 'static': { - * staticMethod: function() { - * // Static method - * } - * } - * }); - * - * // Creates a singleton/static class - * tinymce.create('static tinymce.somepackage.SomeSingletonClass', { - * method: function() { - * // Some method - * } - * }); - */ - function create(s, p, root) { - var self = this, - sp, ns, cn, scn, c, de = 0; - - // Parse : <prefix> <class>:<super class> - s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s); - cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name - - // Create namespace for new class - ns = self.createNS(s[3].replace(/\.\w+$/, ''), root); - - // Class already exists - if (ns[cn]) { - return; - } - - // Make pure static class - if (s[2] == 'static') { - ns[cn] = p; - - if (this.onCreate) { - this.onCreate(s[2], s[3], ns[cn]); - } - - return; - } - - // Create default constructor - if (!p[cn]) { - p[cn] = function() {}; - de = 1; - } - - // Add constructor and methods - ns[cn] = p[cn]; - self.extend(ns[cn].prototype, p); - - // Extend - if (s[5]) { - sp = self.resolve(s[5]).prototype; - scn = s[5].match(/\.(\w+)$/i)[1]; // Class name - - // Extend constructor - c = ns[cn]; - if (de) { - // Add passthrough constructor - ns[cn] = function() { - return sp[scn].apply(this, arguments); - }; - } else { - // Add inherit constructor - ns[cn] = function() { - this.parent = sp[scn]; - return c.apply(this, arguments); - }; - } - ns[cn].prototype[cn] = ns[cn]; - - // Add super methods - self.each(sp, function(f, n) { - ns[cn].prototype[n] = sp[n]; - }); - - // Add overridden methods - self.each(p, function(f, n) { - // Extend methods if needed - if (sp[n]) { - ns[cn].prototype[n] = function() { - this.parent = sp[n]; - return f.apply(this, arguments); - }; - } else { - if (n != cn) { - ns[cn].prototype[n] = f; - } - } - }); - } - - // Add static methods - /*jshint sub:true*/ - /*eslint dot-notation:0*/ - self.each(p['static'], function(f, n) { - ns[cn][n] = f; - }); - } - - function extend(obj, ext) { - var i, l, name, args = arguments, - value; - - for (i = 1, l = args.length; i < l; i++) { - ext = args[i]; - for (name in ext) { - if (ext.hasOwnProperty(name)) { - value = ext[name]; - - if (value !== undefined) { - obj[name] = value; - } - } - } - } - - return obj; - } - - /** - * Executed the specified function for each item in a object tree. - * - * @method walk - * @param {Object} o Object tree to walk though. - * @param {function} f Function to call for each item. - * @param {String} n Optional name of collection inside the objects to walk for example childNodes. - * @param {String} s Optional scope to execute the function in. - */ - function walk(o, f, n, s) { - s = s || this; - - if (o) { - if (n) { - o = o[n]; - } - - Arr.each(o, function(o, i) { - if (f.call(s, o, i, n) === false) { - return false; - } - - walk(o, f, n, s); - }); - } - } - - /** - * Creates a namespace on a specific object. - * - * @method createNS - * @param {String} n Namespace to create for example a.b.c.d. - * @param {Object} o Optional object to add namespace to, defaults to window. - * @return {Object} New namespace object the last item in path. - * @example - * // Create some namespace - * tinymce.createNS('tinymce.somepackage.subpackage'); - * - * // Add a singleton - * var tinymce.somepackage.subpackage.SomeSingleton = { - * method: function() { - * // Some method - * } - * }; - */ - function createNS(n, o) { - var i, v; - - o = o || window; - - n = n.split('.'); - for (i = 0; i < n.length; i++) { - v = n[i]; - - if (!o[v]) { - o[v] = {}; - } - - o = o[v]; - } - - return o; - } - - /** - * Resolves a string and returns the object from a specific structure. - * - * @method resolve - * @param {String} n Path to resolve for example a.b.c.d. - * @param {Object} o Optional object to search though, defaults to window. - * @return {Object} Last object in path or null if it couldn't be resolved. - * @example - * // Resolve a path into an object reference - * var obj = tinymce.resolve('a.b.c.d'); - */ - function resolve(n, o) { - var i, l; - - o = o || window; - - n = n.split('.'); - for (i = 0, l = n.length; i < l; i++) { - o = o[n[i]]; - - if (!o) { - break; - } - } - - return o; - } - - /** - * Splits a string but removes the whitespace before and after each value. - * - * @method explode - * @param {string} s String to split. - * @param {string} d Delimiter to split by. - * @example - * // Split a string into an array with a,b,c - * var arr = tinymce.explode('a, b, c'); - */ - function explode(s, d) { - if (!s || is(s, 'array')) { - return s; - } - - return Arr.map(s.split(d || ','), trim); - } - - function _addCacheSuffix(url) { - var cacheSuffix = Env.cacheSuffix; - - if (cacheSuffix) { - url += (url.indexOf('?') === -1 ? '?' : '&') + cacheSuffix; - } - - return url; - } - - return { - trim: trim, - - /** - * Returns true/false if the object is an array or not. - * - * @method isArray - * @param {Object} obj Object to check. - * @return {boolean} true/false state if the object is an array or not. - */ - isArray: Arr.isArray, - - is: is, - - /** - * Converts the specified object into a real JavaScript array. - * - * @method toArray - * @param {Object} obj Object to convert into array. - * @return {Array} Array object based in input. - */ - toArray: Arr.toArray, - makeMap: makeMap, - - /** - * Performs an iteration of all items in a collection such as an object or array. This method will execure the - * callback function for each item in the collection, if the callback returns false the iteration will terminate. - * The callback has the following format: cb(value, key_or_index). - * - * @method each - * @param {Object} o Collection to iterate. - * @param {function} cb Callback function to execute for each item. - * @param {Object} s Optional scope to execute the callback in. - * @example - * // Iterate an array - * tinymce.each([1,2,3], function(v, i) { - * console.debug("Value: " + v + ", Index: " + i); - * }); - * - * // Iterate an object - * tinymce.each({a: 1, b: 2, c: 3], function(v, k) { - * console.debug("Value: " + v + ", Key: " + k); - * }); - */ - each: Arr.each, - - /** - * Creates a new array by the return value of each iteration function call. This enables you to convert - * one array list into another. - * - * @method map - * @param {Array} array Array of items to iterate. - * @param {function} callback Function to call for each item. It's return value will be the new value. - * @return {Array} Array with new values based on function return values. - */ - map: Arr.map, - - /** - * Filters out items from the input array by calling the specified function for each item. - * If the function returns false the item will be excluded if it returns true it will be included. - * - * @method grep - * @param {Array} a Array of items to loop though. - * @param {function} f Function to call for each item. Include/exclude depends on it's return value. - * @return {Array} New array with values imported and filtered based in input. - * @example - * // Filter out some items, this will return an array with 4 and 5 - * var items = tinymce.grep([1,2,3,4,5], function(v) {return v > 3;}); - */ - grep: Arr.filter, - - /** - * Returns an index of the item or -1 if item is not present in the array. - * - * @method inArray - * @param {any} item Item to search for. - * @param {Array} arr Array to search in. - * @return {Number} index of the item or -1 if item was not found. - */ - inArray: Arr.indexOf, - - hasOwn: hasOwnProperty, - - extend: extend, - create: create, - walk: walk, - createNS: createNS, - resolve: resolve, - explode: explode, - _addCacheSuffix: _addCacheSuffix - }; - } - ); - /** - * DomQuery.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class mimics most of the jQuery API: - * - * This is whats currently implemented: - * - Utility functions - * - DOM traversial - * - DOM manipulation - * - Event binding - * - * This is not currently implemented: - * - Dimension - * - Ajax - * - Animation - * - Advanced chaining - * - * @example - * var $ = tinymce.dom.DomQuery; - * $('p').attr('attr', 'value').addClass('class'); - * - * @class tinymce.dom.DomQuery - */ - define( - 'tinymce.core.dom.DomQuery', [ - "tinymce.core.dom.EventUtils", - "tinymce.core.dom.Sizzle", - "tinymce.core.util.Tools", - "tinymce.core.Env" - ], - function(EventUtils, Sizzle, Tools, Env) { - var doc = document, - push = Array.prototype.push, - slice = Array.prototype.slice; - var rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/; - var Event = EventUtils.Event, - undef; - var skipUniques = Tools.makeMap('children,contents,next,prev'); - - function isDefined(obj) { - return typeof obj !== 'undefined'; - } - - function isString(obj) { - return typeof obj === 'string'; - } - - function isWindow(obj) { - return obj && obj == obj.window; - } - - function createFragment(html, fragDoc) { - var frag, node, container; - - fragDoc = fragDoc || doc; - container = fragDoc.createElement('div'); - frag = fragDoc.createDocumentFragment(); - container.innerHTML = html; - - while ((node = container.firstChild)) { - frag.appendChild(node); - } - - return frag; - } - - function domManipulate(targetNodes, sourceItem, callback, reverse) { - var i; - - if (isString(sourceItem)) { - sourceItem = createFragment(sourceItem, getElementDocument(targetNodes[0])); - } else if (sourceItem.length && !sourceItem.nodeType) { - sourceItem = DomQuery.makeArray(sourceItem); - - if (reverse) { - for (i = sourceItem.length - 1; i >= 0; i--) { - domManipulate(targetNodes, sourceItem[i], callback, reverse); - } - } else { - for (i = 0; i < sourceItem.length; i++) { - domManipulate(targetNodes, sourceItem[i], callback, reverse); - } - } - - return targetNodes; - } - - if (sourceItem.nodeType) { - i = targetNodes.length; - while (i--) { - callback.call(targetNodes[i], sourceItem); - } - } - - return targetNodes; - } - - function hasClass(node, className) { - return node && className && (' ' + node.className + ' ').indexOf(' ' + className + ' ') !== -1; - } - - function wrap(elements, wrapper, all) { - var lastParent, newWrapper; - - wrapper = DomQuery(wrapper)[0]; - - elements.each(function() { - var self = this; - - if (!all || lastParent != self.parentNode) { - lastParent = self.parentNode; - newWrapper = wrapper.cloneNode(false); - self.parentNode.insertBefore(newWrapper, self); - newWrapper.appendChild(self); - } else { - newWrapper.appendChild(self); - } - }); - - return elements; - } - - var numericCssMap = Tools.makeMap('fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom', ' '); - var booleanMap = Tools.makeMap('checked compact declare defer disabled ismap multiple nohref noshade nowrap readonly selected', ' '); - var propFix = { - 'for': 'htmlFor', - 'class': 'className', - 'readonly': 'readOnly' - }; - var cssFix = { - 'float': 'cssFloat' - }; - - var attrHooks = {}, - cssHooks = {}; - - function DomQuery(selector, context) { - /*eslint new-cap:0 */ - return new DomQuery.fn.init(selector, context); - } - - function inArray(item, array) { - var i; - - if (array.indexOf) { - return array.indexOf(item); - } - - i = array.length; - while (i--) { - if (array[i] === item) { - return i; - } - } - - return -1; - } - - var whiteSpaceRegExp = /^\s*|\s*$/g; - - function trim(str) { - return (str === null || str === undef) ? '' : ("" + str).replace(whiteSpaceRegExp, ''); - } - - function each(obj, callback) { - var length, key, i, undef, value; - - if (obj) { - length = obj.length; - - if (length === undef) { - // Loop object items - for (key in obj) { - if (obj.hasOwnProperty(key)) { - value = obj[key]; - if (callback.call(value, key, value) === false) { - break; - } - } - } - } else { - // Loop array items - for (i = 0; i < length; i++) { - value = obj[i]; - if (callback.call(value, i, value) === false) { - break; - } - } - } - } - - return obj; - } - - function grep(array, callback) { - var out = []; - - each(array, function(i, item) { - if (callback(item, i)) { - out.push(item); - } - }); - - return out; - } - - function getElementDocument(element) { - if (!element) { - return doc; - } - - if (element.nodeType == 9) { - return element; - } - - return element.ownerDocument; - } - - DomQuery.fn = DomQuery.prototype = { - constructor: DomQuery, - - /** - * Selector for the current set. - * - * @property selector - * @type String - */ - selector: "", - - /** - * Context used to create the set. - * - * @property context - * @type Element - */ - context: null, - - /** - * Number of items in the current set. - * - * @property length - * @type Number - */ - length: 0, - - /** - * Constructs a new DomQuery instance with the specified selector or context. - * - * @constructor - * @method init - * @param {String/Array/DomQuery} selector Optional CSS selector/Array or array like object or HTML string. - * @param {Document/Element} context Optional context to search in. - */ - init: function(selector, context) { - var self = this, - match, node; - - if (!selector) { - return self; - } - - if (selector.nodeType) { - self.context = self[0] = selector; - self.length = 1; - - return self; - } - - if (context && context.nodeType) { - self.context = context; - } else { - if (context) { - return DomQuery(selector).attr(context); - } - - self.context = context = document; - } - - if (isString(selector)) { - self.selector = selector; - - if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) { - match = [null, selector, null]; - } else { - match = rquickExpr.exec(selector); - } - - if (match) { - if (match[1]) { - node = createFragment(selector, getElementDocument(context)).firstChild; - - while (node) { - push.call(self, node); - node = node.nextSibling; - } - } else { - node = getElementDocument(context).getElementById(match[2]); - - if (!node) { - return self; - } - - if (node.id !== match[2]) { - return self.find(selector); - } - - self.length = 1; - self[0] = node; - } - } else { - return DomQuery(context).find(selector); - } - } else { - this.add(selector, false); - } - - return self; - }, - - /** - * Converts the current set to an array. - * - * @method toArray - * @return {Array} Array of all nodes in set. - */ - toArray: function() { - return Tools.toArray(this); - }, - - /** - * Adds new nodes to the set. - * - * @method add - * @param {Array/tinymce.core.dom.DomQuery} items Array of all nodes to add to set. - * @param {Boolean} sort Optional sort flag that enables sorting of elements. - * @return {tinymce.dom.DomQuery} New instance with nodes added. - */ - add: function(items, sort) { - var self = this, - nodes, i; - - if (isString(items)) { - return self.add(DomQuery(items)); - } - - if (sort !== false) { - nodes = DomQuery.unique(self.toArray().concat(DomQuery.makeArray(items))); - self.length = nodes.length; - for (i = 0; i < nodes.length; i++) { - self[i] = nodes[i]; - } - } else { - push.apply(self, DomQuery.makeArray(items)); - } - - return self; - }, - - /** - * Sets/gets attributes on the elements in the current set. - * - * @method attr - * @param {String/Object} name Name of attribute to get or an object with attributes to set. - * @param {String} value Optional value to set. - * @return {tinymce.dom.DomQuery/String} Current set or the specified attribute when only the name is specified. - */ - attr: function(name, value) { - var self = this, - hook; - - if (typeof name === "object") { - each(name, function(name, value) { - self.attr(name, value); - }); - } else if (isDefined(value)) { - this.each(function() { - var hook; - - if (this.nodeType === 1) { - hook = attrHooks[name]; - if (hook && hook.set) { - hook.set(this, value); - return; - } - - if (value === null) { - this.removeAttribute(name, 2); - } else { - this.setAttribute(name, value, 2); - } - } - }); - } else { - if (self[0] && self[0].nodeType === 1) { - hook = attrHooks[name]; - if (hook && hook.get) { - return hook.get(self[0], name); - } - - if (booleanMap[name]) { - return self.prop(name) ? name : undef; - } - - value = self[0].getAttribute(name, 2); - - if (value === null) { - value = undef; - } - } - - return value; - } - - return self; - }, - - /** - * Removes attributse on the elements in the current set. - * - * @method removeAttr - * @param {String/Object} name Name of attribute to remove. - * @return {tinymce.dom.DomQuery/String} Current set. - */ - removeAttr: function(name) { - return this.attr(name, null); - }, - - /** - * Sets/gets properties on the elements in the current set. - * - * @method attr - * @param {String/Object} name Name of property to get or an object with properties to set. - * @param {String} value Optional value to set. - * @return {tinymce.dom.DomQuery/String} Current set or the specified property when only the name is specified. - */ - prop: function(name, value) { - var self = this; - - name = propFix[name] || name; - - if (typeof name === "object") { - each(name, function(name, value) { - self.prop(name, value); - }); - } else if (isDefined(value)) { - this.each(function() { - if (this.nodeType == 1) { - this[name] = value; - } - }); - } else { - if (self[0] && self[0].nodeType && name in self[0]) { - return self[0][name]; - } - - return value; - } - - return self; - }, - - /** - * Sets/gets styles on the elements in the current set. - * - * @method css - * @param {String/Object} name Name of style to get or an object with styles to set. - * @param {String} value Optional value to set. - * @return {tinymce.dom.DomQuery/String} Current set or the specified style when only the name is specified. - */ - css: function(name, value) { - var self = this, - elm, hook; - - function camel(name) { - return name.replace(/-(\D)/g, function(a, b) { - return b.toUpperCase(); - }); - } - - function dashed(name) { - return name.replace(/[A-Z]/g, function(a) { - return '-' + a; - }); - } - - if (typeof name === "object") { - each(name, function(name, value) { - self.css(name, value); - }); - } else { - if (isDefined(value)) { - name = camel(name); - - // Default px suffix on these - if (typeof value === 'number' && !numericCssMap[name]) { - value += 'px'; - } - - self.each(function() { - var style = this.style; - - hook = cssHooks[name]; - if (hook && hook.set) { - hook.set(this, value); - return; - } - - try { - this.style[cssFix[name] || name] = value; - } catch (ex) { - // Ignore - } - - if (value === null || value === '') { - if (style.removeProperty) { - style.removeProperty(dashed(name)); - } else { - style.removeAttribute(name); - } - } - }); - } else { - elm = self[0]; - - hook = cssHooks[name]; - if (hook && hook.get) { - return hook.get(elm); - } - - if (elm.ownerDocument.defaultView) { - try { - return elm.ownerDocument.defaultView.getComputedStyle(elm, null).getPropertyValue(dashed(name)); - } catch (ex) { - return undef; - } - } else if (elm.currentStyle) { - return elm.currentStyle[camel(name)]; - } - } - } - - return self; - }, - - /** - * Removes all nodes in set from the document. - * - * @method remove - * @return {tinymce.dom.DomQuery} Current set with the removed nodes. - */ - remove: function() { - var self = this, - node, i = this.length; - - while (i--) { - node = self[i]; - Event.clean(node); - - if (node.parentNode) { - node.parentNode.removeChild(node); - } - } - - return this; - }, - - /** - * Empties all elements in set. - * - * @method empty - * @return {tinymce.dom.DomQuery} Current set with the empty nodes. - */ - empty: function() { - var self = this, - node, i = this.length; - - while (i--) { - node = self[i]; - while (node.firstChild) { - node.removeChild(node.firstChild); - } - } - - return this; - }, - - /** - * Sets or gets the HTML of the current set or first set node. - * - * @method html - * @param {String} value Optional innerHTML value to set on each element. - * @return {tinymce.dom.DomQuery/String} Current set or the innerHTML of the first element. - */ - html: function(value) { - var self = this, - i; - - if (isDefined(value)) { - i = self.length; - - try { - while (i--) { - self[i].innerHTML = value; - } - } catch (ex) { - // Workaround for "Unknown runtime error" when DIV is added to P on IE - DomQuery(self[i]).empty().append(value); - } - - return self; - } - - return self[0] ? self[0].innerHTML : ''; - }, - - /** - * Sets or gets the text of the current set or first set node. - * - * @method text - * @param {String} value Optional innerText value to set on each element. - * @return {tinymce.dom.DomQuery/String} Current set or the innerText of the first element. - */ - text: function(value) { - var self = this, - i; - - if (isDefined(value)) { - i = self.length; - while (i--) { - if ("innerText" in self[i]) { - self[i].innerText = value; - } else { - self[0].textContent = value; - } - } - - return self; - } - - return self[0] ? (self[0].innerText || self[0].textContent) : ''; - }, - - /** - * Appends the specified node/html or node set to the current set nodes. - * - * @method append - * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to append to each element in set. - * @return {tinymce.dom.DomQuery} Current set. - */ - append: function() { - return domManipulate(this, arguments, function(node) { - // Either element or Shadow Root - if (this.nodeType === 1 || (this.host && this.host.nodeType === 1)) { - this.appendChild(node); - } - }); - }, - - /** - * Prepends the specified node/html or node set to the current set nodes. - * - * @method prepend - * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to prepend to each element in set. - * @return {tinymce.dom.DomQuery} Current set. - */ - prepend: function() { - return domManipulate(this, arguments, function(node) { - // Either element or Shadow Root - if (this.nodeType === 1 || (this.host && this.host.nodeType === 1)) { - this.insertBefore(node, this.firstChild); - } - }, true); - }, - - /** - * Adds the specified elements before current set nodes. - * - * @method before - * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to add before to each element in set. - * @return {tinymce.dom.DomQuery} Current set. - */ - before: function() { - var self = this; - - if (self[0] && self[0].parentNode) { - return domManipulate(self, arguments, function(node) { - this.parentNode.insertBefore(node, this); - }); - } - - return self; - }, - - /** - * Adds the specified elements after current set nodes. - * - * @method after - * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to add after to each element in set. - * @return {tinymce.dom.DomQuery} Current set. - */ - after: function() { - var self = this; - - if (self[0] && self[0].parentNode) { - return domManipulate(self, arguments, function(node) { - this.parentNode.insertBefore(node, this.nextSibling); - }, true); - } - - return self; - }, - - /** - * Appends the specified set nodes to the specified selector/instance. - * - * @method appendTo - * @param {String/Element/Array/tinymce.dom.DomQuery} val Item to append the current set to. - * @return {tinymce.dom.DomQuery} Current set with the appended nodes. - */ - appendTo: function(val) { - DomQuery(val).append(this); - - return this; - }, - - /** - * Prepends the specified set nodes to the specified selector/instance. - * - * @method prependTo - * @param {String/Element/Array/tinymce.dom.DomQuery} val Item to prepend the current set to. - * @return {tinymce.dom.DomQuery} Current set with the prepended nodes. - */ - prependTo: function(val) { - DomQuery(val).prepend(this); - - return this; - }, - - /** - * Replaces the nodes in set with the specified content. - * - * @method replaceWith - * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to replace nodes with. - * @return {tinymce.dom.DomQuery} Set with replaced nodes. - */ - replaceWith: function(content) { - return this.before(content).remove(); - }, - - /** - * Wraps all elements in set with the specified wrapper. - * - * @method wrap - * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with. - * @return {tinymce.dom.DomQuery} Set with wrapped nodes. - */ - wrap: function(content) { - return wrap(this, content); - }, - - /** - * Wraps all nodes in set with the specified wrapper. If the nodes are siblings all of them - * will be wrapped in the same wrapper. - * - * @method wrapAll - * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with. - * @return {tinymce.dom.DomQuery} Set with wrapped nodes. - */ - wrapAll: function(content) { - return wrap(this, content, true); - }, - - /** - * Wraps all elements inner contents in set with the specified wrapper. - * - * @method wrapInner - * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with. - * @return {tinymce.dom.DomQuery} Set with wrapped nodes. - */ - wrapInner: function(content) { - this.each(function() { - DomQuery(this).contents().wrapAll(content); - }); - - return this; - }, - - /** - * Unwraps all elements by removing the parent element of each item in set. - * - * @method unwrap - * @return {tinymce.dom.DomQuery} Set with unwrapped nodes. - */ - unwrap: function() { - return this.parent().each(function() { - DomQuery(this).replaceWith(this.childNodes); - }); - }, - - /** - * Clones all nodes in set. - * - * @method clone - * @return {tinymce.dom.DomQuery} Set with cloned nodes. - */ - clone: function() { - var result = []; - - this.each(function() { - result.push(this.cloneNode(true)); - }); - - return DomQuery(result); - }, - - /** - * Adds the specified class name to the current set elements. - * - * @method addClass - * @param {String} className Class name to add. - * @return {tinymce.dom.DomQuery} Current set. - */ - addClass: function(className) { - return this.toggleClass(className, true); - }, - - /** - * Removes the specified class name to the current set elements. - * - * @method removeClass - * @param {String} className Class name to remove. - * @return {tinymce.dom.DomQuery} Current set. - */ - removeClass: function(className) { - return this.toggleClass(className, false); - }, - - /** - * Toggles the specified class name on the current set elements. - * - * @method toggleClass - * @param {String} className Class name to add/remove. - * @param {Boolean} state Optional state to toggle on/off. - * @return {tinymce.dom.DomQuery} Current set. - */ - toggleClass: function(className, state) { - var self = this; - - // Functions are not supported - if (typeof className != 'string') { - return self; - } - - if (className.indexOf(' ') !== -1) { - each(className.split(' '), function() { - self.toggleClass(this, state); - }); - } else { - self.each(function(index, node) { - var existingClassName, classState; - - classState = hasClass(node, className); - if (classState !== state) { - existingClassName = node.className; - - if (classState) { - node.className = trim((" " + existingClassName + " ").replace(' ' + className + ' ', ' ')); - } else { - node.className += existingClassName ? ' ' + className : className; - } - } - }); - } - - return self; - }, - - /** - * Returns true/false if the first item in set has the specified class. - * - * @method hasClass - * @param {String} className Class name to check for. - * @return {Boolean} True/false if the set has the specified class. - */ - hasClass: function(className) { - return hasClass(this[0], className); - }, - - /** - * Executes the callback function for each item DomQuery collection. If you return false in the - * callback it will break the loop. - * - * @method each - * @param {function} callback Callback function to execute for each item. - * @return {tinymce.dom.DomQuery} Current set. - */ - each: function(callback) { - return each(this, callback); - }, - - /** - * Binds an event with callback function to the elements in set. - * - * @method on - * @param {String} name Name of the event to bind. - * @param {function} callback Callback function to execute when the event occurs. - * @return {tinymce.dom.DomQuery} Current set. - */ - on: function(name, callback) { - return this.each(function() { - Event.bind(this, name, callback); - }); - }, - - /** - * Unbinds an event with callback function to the elements in set. - * - * @method off - * @param {String} name Optional name of the event to bind. - * @param {function} callback Optional callback function to execute when the event occurs. - * @return {tinymce.dom.DomQuery} Current set. - */ - off: function(name, callback) { - return this.each(function() { - Event.unbind(this, name, callback); - }); - }, - - /** - * Triggers the specified event by name or event object. - * - * @method trigger - * @param {String/Object} name Name of the event to trigger or event object. - * @return {tinymce.dom.DomQuery} Current set. - */ - trigger: function(name) { - return this.each(function() { - if (typeof name == 'object') { - Event.fire(this, name.type, name); - } else { - Event.fire(this, name); - } - }); - }, - - /** - * Shows all elements in set. - * - * @method show - * @return {tinymce.dom.DomQuery} Current set. - */ - show: function() { - return this.css('display', ''); - }, - - /** - * Hides all elements in set. - * - * @method hide - * @return {tinymce.dom.DomQuery} Current set. - */ - hide: function() { - return this.css('display', 'none'); - }, - - /** - * Slices the current set. - * - * @method slice - * @param {Number} start Start index to slice at. - * @param {Number} end Optional end index to end slice at. - * @return {tinymce.dom.DomQuery} Sliced set. - */ - slice: function() { - return new DomQuery(slice.apply(this, arguments)); - }, - - /** - * Makes the set equal to the specified index. - * - * @method eq - * @param {Number} index Index to set it equal to. - * @return {tinymce.dom.DomQuery} Single item set. - */ - eq: function(index) { - return index === -1 ? this.slice(index) : this.slice(index, +index + 1); - }, - - /** - * Makes the set equal to first element in set. - * - * @method first - * @return {tinymce.dom.DomQuery} Single item set. - */ - first: function() { - return this.eq(0); - }, - - /** - * Makes the set equal to last element in set. - * - * @method last - * @return {tinymce.dom.DomQuery} Single item set. - */ - last: function() { - return this.eq(-1); - }, - - /** - * Finds elements by the specified selector for each element in set. - * - * @method find - * @param {String} selector Selector to find elements by. - * @return {tinymce.dom.DomQuery} Set with matches elements. - */ - find: function(selector) { - var i, l, ret = []; - - for (i = 0, l = this.length; i < l; i++) { - DomQuery.find(selector, this[i], ret); - } - - return DomQuery(ret); - }, - - /** - * Filters the current set with the specified selector. - * - * @method filter - * @param {String/function} selector Selector to filter elements by. - * @return {tinymce.dom.DomQuery} Set with filtered elements. - */ - filter: function(selector) { - if (typeof selector == 'function') { - return DomQuery(grep(this.toArray(), function(item, i) { - return selector(i, item); - })); - } - - return DomQuery(DomQuery.filter(selector, this.toArray())); - }, - - /** - * Gets the current node or any parent matching the specified selector. - * - * @method closest - * @param {String/Element/tinymce.dom.DomQuery} selector Selector or element to find. - * @return {tinymce.dom.DomQuery} Set with closest elements. - */ - closest: function(selector) { - var result = []; - - if (selector instanceof DomQuery) { - selector = selector[0]; - } - - this.each(function(i, node) { - while (node) { - if (typeof selector == 'string' && DomQuery(node).is(selector)) { - result.push(node); - break; - } else if (node == selector) { - result.push(node); - break; - } - - node = node.parentNode; - } - }); - - return DomQuery(result); - }, - - /** - * Returns the offset of the first element in set or sets the top/left css properties of all elements in set. - * - * @method offset - * @param {Object} offset Optional offset object to set on each item. - * @return {Object/tinymce.dom.DomQuery} Returns the first element offset or the current set if you specified an offset. - */ - offset: function(offset) { - var elm, doc, docElm; - var x = 0, - y = 0, - pos; - - if (!offset) { - elm = this[0]; - - if (elm) { - doc = elm.ownerDocument; - docElm = doc.documentElement; - - if (elm.getBoundingClientRect) { - pos = elm.getBoundingClientRect(); - x = pos.left + (docElm.scrollLeft || doc.body.scrollLeft) - docElm.clientLeft; - y = pos.top + (docElm.scrollTop || doc.body.scrollTop) - docElm.clientTop; - } - } - - return { - left: x, - top: y - }; - } - - return this.css(offset); - }, - - push: push, - sort: [].sort, - splice: [].splice - }; - - // Static members - Tools.extend(DomQuery, { - /** - * Extends the specified object with one or more objects. - * - * @static - * @method extend - * @param {Object} target Target object to extend with new items. - * @param {Object..} object Object to extend the target with. - * @return {Object} Extended input object. - */ - extend: Tools.extend, - - /** - * Creates an array out of an array like object. - * - * @static - * @method makeArray - * @param {Object} object Object to convert to array. - * @return {Array} Array produced from object. - */ - makeArray: function(object) { - if (isWindow(object) || object.nodeType) { - return [object]; - } - - return Tools.toArray(object); - }, - - /** - * Returns the index of the specified item inside the array. - * - * @static - * @method inArray - * @param {Object} item Item to look for. - * @param {Array} array Array to look for item in. - * @return {Number} Index of the item or -1. - */ - inArray: inArray, - - /** - * Returns true/false if the specified object is an array or not. - * - * @static - * @method isArray - * @param {Object} array Object to check if it's an array or not. - * @return {Boolean} True/false if the object is an array. - */ - isArray: Tools.isArray, - - /** - * Executes the callback function for each item in array/object. If you return false in the - * callback it will break the loop. - * - * @static - * @method each - * @param {Object} obj Object to iterate. - * @param {function} callback Callback function to execute for each item. - */ - each: each, - - /** - * Removes whitespace from the beginning and end of a string. - * - * @static - * @method trim - * @param {String} str String to remove whitespace from. - * @return {String} New string with removed whitespace. - */ - trim: trim, - - /** - * Filters out items from the input array by calling the specified function for each item. - * If the function returns false the item will be excluded if it returns true it will be included. - * - * @static - * @method grep - * @param {Array} array Array of items to loop though. - * @param {function} callback Function to call for each item. Include/exclude depends on it's return value. - * @return {Array} New array with values imported and filtered based in input. - * @example - * // Filter out some items, this will return an array with 4 and 5 - * var items = DomQuery.grep([1, 2, 3, 4, 5], function(v) {return v > 3;}); - */ - grep: grep, - - // Sizzle - find: Sizzle, - expr: Sizzle.selectors, - unique: Sizzle.uniqueSort, - text: Sizzle.getText, - contains: Sizzle.contains, - filter: function(expr, elems, not) { - var i = elems.length; - - if (not) { - expr = ":not(" + expr + ")"; - } - - while (i--) { - if (elems[i].nodeType != 1) { - elems.splice(i, 1); - } - } - - if (elems.length === 1) { - elems = DomQuery.find.matchesSelector(elems[0], expr) ? [elems[0]] : []; - } else { - elems = DomQuery.find.matches(expr, elems); - } - - return elems; - } - }); - - function dir(el, prop, until) { - var matched = [], - cur = el[prop]; - - if (typeof until != 'string' && until instanceof DomQuery) { - until = until[0]; - } - - while (cur && cur.nodeType !== 9) { - if (until !== undefined) { - if (cur === until) { - break; - } - - if (typeof until == 'string' && DomQuery(cur).is(until)) { - break; - } - } - - if (cur.nodeType === 1) { - matched.push(cur); - } - - cur = cur[prop]; - } - - return matched; - } - - function sibling(node, siblingName, nodeType, until) { - var result = []; - - if (until instanceof DomQuery) { - until = until[0]; - } - - for (; node; node = node[siblingName]) { - if (nodeType && node.nodeType !== nodeType) { - continue; - } - - if (until !== undefined) { - if (node === until) { - break; - } - - if (typeof until == 'string' && DomQuery(node).is(until)) { - break; - } - } - - result.push(node); - } - - return result; - } - - function firstSibling(node, siblingName, nodeType) { - for (node = node[siblingName]; node; node = node[siblingName]) { - if (node.nodeType == nodeType) { - return node; - } - } - - return null; - } - - each({ - /** - * Returns a new collection with the parent of each item in current collection matching the optional selector. - * - * @method parent - * @param {Element/tinymce.dom.DomQuery} node Node to match parents against. - * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents. - */ - parent: function(node) { - var parent = node.parentNode; - - return parent && parent.nodeType !== 11 ? parent : null; - }, - - /** - * Returns a new collection with the all the parents of each item in current collection matching the optional selector. - * - * @method parents - * @param {Element/tinymce.dom.DomQuery} node Node to match parents against. - * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents. - */ - parents: function(node) { - return dir(node, "parentNode"); - }, - - /** - * Returns a new collection with next sibling of each item in current collection matching the optional selector. - * - * @method next - * @param {Element/tinymce.dom.DomQuery} node Node to match the next element against. - * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements. - */ - next: function(node) { - return firstSibling(node, 'nextSibling', 1); - }, - - /** - * Returns a new collection with previous sibling of each item in current collection matching the optional selector. - * - * @method prev - * @param {Element/tinymce.dom.DomQuery} node Node to match the previous element against. - * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements. - */ - prev: function(node) { - return firstSibling(node, 'previousSibling', 1); - }, - - /** - * Returns all child elements matching the optional selector. - * - * @method children - * @param {Element/tinymce.dom.DomQuery} node Node to match the elements against. - * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements. - */ - children: function(node) { - return sibling(node.firstChild, 'nextSibling', 1); - }, - - /** - * Returns all child nodes matching the optional selector. - * - * @method contents - * @param {Element/tinymce.dom.DomQuery} node Node to get the contents of. - * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements. - */ - contents: function(node) { - return Tools.toArray((node.nodeName === "iframe" ? node.contentDocument || node.contentWindow.document : node).childNodes); - } - }, function(name, fn) { - DomQuery.fn[name] = function(selector) { - var self = this, - result = []; - - self.each(function() { - var nodes = fn.call(result, this, selector, result); - - if (nodes) { - if (DomQuery.isArray(nodes)) { - result.push.apply(result, nodes); - } else { - result.push(nodes); - } - } - }); - - // If traversing on multiple elements we might get the same elements twice - if (this.length > 1) { - if (!skipUniques[name]) { - result = DomQuery.unique(result); - } - - if (name.indexOf('parents') === 0) { - result = result.reverse(); - } - } - - result = DomQuery(result); - - if (selector) { - return result.filter(selector); - } - - return result; - }; - }); - - each({ - /** - * Returns a new collection with the all the parents until the matching selector/element - * of each item in current collection matching the optional selector. - * - * @method parentsUntil - * @param {Element/tinymce.dom.DomQuery} node Node to find parent of. - * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element. - * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents. - */ - parentsUntil: function(node, until) { - return dir(node, "parentNode", until); - }, - - /** - * Returns a new collection with all next siblings of each item in current collection matching the optional selector. - * - * @method nextUntil - * @param {Element/tinymce.dom.DomQuery} node Node to find next siblings on. - * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element. - * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements. - */ - nextUntil: function(node, until) { - return sibling(node, 'nextSibling', 1, until).slice(1); - }, - - /** - * Returns a new collection with all previous siblings of each item in current collection matching the optional selector. - * - * @method prevUntil - * @param {Element/tinymce.dom.DomQuery} node Node to find previous siblings on. - * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element. - * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements. - */ - prevUntil: function(node, until) { - return sibling(node, 'previousSibling', 1, until).slice(1); - } - }, function(name, fn) { - DomQuery.fn[name] = function(selector, filter) { - var self = this, - result = []; - - self.each(function() { - var nodes = fn.call(result, this, selector, result); - - if (nodes) { - if (DomQuery.isArray(nodes)) { - result.push.apply(result, nodes); - } else { - result.push(nodes); - } - } - }); - - // If traversing on multiple elements we might get the same elements twice - if (this.length > 1) { - result = DomQuery.unique(result); - - if (name.indexOf('parents') === 0 || name === 'prevUntil') { - result = result.reverse(); - } - } - - result = DomQuery(result); - - if (filter) { - return result.filter(filter); - } - - return result; - }; - }); - - /** - * Returns true/false if the current set items matches the selector. - * - * @method is - * @param {String} selector Selector to match the elements against. - * @return {Boolean} True/false if the current set matches the selector. - */ - DomQuery.fn.is = function(selector) { - return !!selector && this.filter(selector).length > 0; - }; - - DomQuery.fn.init.prototype = DomQuery.fn; - - DomQuery.overrideDefaults = function(callback) { - var defaults; - - function sub(selector, context) { - defaults = defaults || callback(); - - if (arguments.length === 0) { - selector = defaults.element; - } - - if (!context) { - context = defaults.context; - } - - return new sub.fn.init(selector, context); - } - - DomQuery.extend(sub, this); - - return sub; - }; - - function appendHooks(targetHooks, prop, hooks) { - each(hooks, function(name, func) { - targetHooks[name] = targetHooks[name] || {}; - targetHooks[name][prop] = func; - }); - } - - if (Env.ie && Env.ie < 8) { - appendHooks(attrHooks, 'get', { - maxlength: function(elm) { - var value = elm.maxLength; - - if (value === 0x7fffffff) { - return undef; - } - - return value; - }, - - size: function(elm) { - var value = elm.size; - - if (value === 20) { - return undef; - } - - return value; - }, - - 'class': function(elm) { - return elm.className; - }, - - style: function(elm) { - var value = elm.style.cssText; - - if (value.length === 0) { - return undef; - } - - return value; - } - }); - - appendHooks(attrHooks, 'set', { - 'class': function(elm, value) { - elm.className = value; - }, - - style: function(elm, value) { - elm.style.cssText = value; - } - }); - } - - if (Env.ie && Env.ie < 9) { - /*jshint sub:true */ - /*eslint dot-notation: 0*/ - cssFix['float'] = 'styleFloat'; - - appendHooks(cssHooks, 'set', { - opacity: function(elm, value) { - var style = elm.style; - - if (value === null || value === '') { - style.removeAttribute('filter'); - } else { - style.zoom = 1; - style.filter = 'alpha(opacity=' + (value * 100) + ')'; - } - } - }); - } - - DomQuery.attrHooks = attrHooks; - DomQuery.cssHooks = cssHooks; - - return DomQuery; - } - ); - - /** - * Styles.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class is used to parse CSS styles it also compresses styles to reduce the output size. - * - * @example - * var Styles = new tinymce.html.Styles({ - * url_converter: function(url) { - * return url; - * } - * }); - * - * styles = Styles.parse('border: 1px solid red'); - * styles.color = 'red'; - * - * console.log(new tinymce.html.StyleSerializer().serialize(styles)); - * - * @class tinymce.html.Styles - * @version 3.4 - */ - define( - 'tinymce.core.html.Styles', [], - function() { - return function(settings, schema) { - /*jshint maxlen:255 */ - /*eslint max-len:0 */ - var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi, - urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi, - styleRegExp = /\s*([^:]+):\s*([^;]+);?/g, - trimRightRegExp = /\s+$/, - i, encodingLookup = {}, - encodingItems, validStyles, invalidStyles, invisibleChar = '\uFEFF'; - - settings = settings || {}; - - if (schema) { - validStyles = schema.getValidStyles(); - invalidStyles = schema.getInvalidStyles(); - } - - encodingItems = ('\\" \\\' \\; \\: ; : ' + invisibleChar).split(' '); - for (i = 0; i < encodingItems.length; i++) { - encodingLookup[encodingItems[i]] = invisibleChar + i; - encodingLookup[invisibleChar + i] = encodingItems[i]; - } - - function toHex(match, r, g, b) { - function hex(val) { - val = parseInt(val, 10).toString(16); - - return val.length > 1 ? val : '0' + val; // 0 -> 00 - } - - return '#' + hex(r) + hex(g) + hex(b); - } - - return { - /** - * Parses the specified RGB color value and returns a hex version of that color. - * - * @method toHex - * @param {String} color RGB string value like rgb(1,2,3) - * @return {String} Hex version of that RGB value like #FF00FF. - */ - toHex: function(color) { - return color.replace(rgbRegExp, toHex); - }, - - /** - * Parses the specified style value into an object collection. This parser will also - * merge and remove any redundant items that browsers might have added. It will also convert non hex - * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings. - * - * @method parse - * @param {String} css Style value to parse for example: border:1px solid red;. - * @return {Object} Object representation of that style like {border: '1px solid red'} - */ - parse: function(css) { - var styles = {}, - matches, name, value, isEncoded, urlConverter = settings.url_converter; - var urlConverterScope = settings.url_converter_scope || this; - - function compress(prefix, suffix, noJoin) { - var top, right, bottom, left; - - top = styles[prefix + '-top' + suffix]; - if (!top) { - return; - } - - right = styles[prefix + '-right' + suffix]; - if (!right) { - return; - } - - bottom = styles[prefix + '-bottom' + suffix]; - if (!bottom) { - return; - } - - left = styles[prefix + '-left' + suffix]; - if (!left) { - return; - } - - var box = [top, right, bottom, left]; - i = box.length - 1; - while (i--) { - if (box[i] !== box[i + 1]) { - break; - } - } - - if (i > -1 && noJoin) { - return; - } - - styles[prefix + suffix] = i == -1 ? box[0] : box.join(' '); - delete styles[prefix + '-top' + suffix]; - delete styles[prefix + '-right' + suffix]; - delete styles[prefix + '-bottom' + suffix]; - delete styles[prefix + '-left' + suffix]; - } - - /** - * Checks if the specific style can be compressed in other words if all border-width are equal. - */ - function canCompress(key) { - var value = styles[key], - i; - - if (!value) { - return; - } - - value = value.split(' '); - i = value.length; - while (i--) { - if (value[i] !== value[0]) { - return false; - } - } - - styles[key] = value[0]; - - return true; - } - - /** - * Compresses multiple styles into one style. - */ - function compress2(target, a, b, c) { - if (!canCompress(a)) { - return; - } - - if (!canCompress(b)) { - return; - } - - if (!canCompress(c)) { - return; - } - - // Compress - styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c]; - delete styles[a]; - delete styles[b]; - delete styles[c]; - } - - // Encodes the specified string by replacing all \" \' ; : with _<num> - function encode(str) { - isEncoded = true; - - return encodingLookup[str]; - } - - // Decodes the specified string by replacing all _<num> with it's original value \" \' etc - // It will also decode the \" \' if keepSlashes is set to fale or omitted - function decode(str, keepSlashes) { - if (isEncoded) { - str = str.replace(/\uFEFF[0-9]/g, function(str) { - return encodingLookup[str]; - }); - } - - if (!keepSlashes) { - str = str.replace(/\\([\'\";:])/g, "$1"); - } - - return str; - } - - function decodeSingleHexSequence(escSeq) { - return String.fromCharCode(parseInt(escSeq.slice(1), 16)); - } - - function decodeHexSequences(value) { - return value.replace(/\\[0-9a-f]+/gi, decodeSingleHexSequence); - } - - function processUrl(match, url, url2, url3, str, str2) { - str = str || str2; - - if (str) { - str = decode(str); - - // Force strings into single quote format - return "'" + str.replace(/\'/g, "\\'") + "'"; - } - - url = decode(url || url2 || url3); - - if (!settings.allow_script_urls) { - var scriptUrl = url.replace(/[\s\r\n]+/g, ''); - - if (/(java|vb)script:/i.test(scriptUrl)) { - return ""; - } - - if (!settings.allow_svg_data_urls && /^data:image\/svg/i.test(scriptUrl)) { - return ""; - } - } - - // Convert the URL to relative/absolute depending on config - if (urlConverter) { - url = urlConverter.call(urlConverterScope, url, 'style'); - } - - // Output new URL format - return "url('" + url.replace(/\'/g, "\\'") + "')"; - } - - if (css) { - css = css.replace(/[\u0000-\u001F]/g, ''); - - // Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing - css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) { - return str.replace(/[;:]/g, encode); - }); - - // Parse styles - while ((matches = styleRegExp.exec(css))) { - styleRegExp.lastIndex = matches.index + matches[0].length; - name = matches[1].replace(trimRightRegExp, '').toLowerCase(); - value = matches[2].replace(trimRightRegExp, ''); - - if (name && value) { - // Decode escaped sequences like \65 -> e - name = decodeHexSequences(name); - value = decodeHexSequences(value); - - // Skip properties with double quotes and sequences like \" \' in their names - // See 'mXSS Attacks: Attacking well-secured Web-Applications by using innerHTML Mutations' - // https://cure53.de/fp170.pdf - if (name.indexOf(invisibleChar) !== -1 || name.indexOf('"') !== -1) { - continue; - } - - // Don't allow behavior name or expression/comments within the values - if (!settings.allow_script_urls && (name == "behavior" || /expression\s*\(|\/\*|\*\//.test(value))) { - continue; - } - - // Opera will produce 700 instead of bold in their style values - if (name === 'font-weight' && value === '700') { - value = 'bold'; - } else if (name === 'color' || name === 'background-color') { // Lowercase colors like RED - value = value.toLowerCase(); - } - - // Convert RGB colors to HEX - value = value.replace(rgbRegExp, toHex); - - // Convert URLs and force them into url('value') format - value = value.replace(urlOrStrRegExp, processUrl); - styles[name] = isEncoded ? decode(value, true) : value; - } - } - // Compress the styles to reduce it's size for example IE will expand styles - compress("border", "", true); - compress("border", "-width"); - compress("border", "-color"); - compress("border", "-style"); - compress("padding", ""); - compress("margin", ""); - compress2('border', 'border-width', 'border-style', 'border-color'); - - // Remove pointless border, IE produces these - if (styles.border === 'medium none') { - delete styles.border; - } - - // IE 11 will produce a border-image: none when getting the style attribute from <p style="border: 1px solid red"></p> - // So let us assume it shouldn't be there - if (styles['border-image'] === 'none') { - delete styles['border-image']; - } - } - - return styles; - }, - - /** - * Serializes the specified style object into a string. - * - * @method serialize - * @param {Object} styles Object to serialize as string for example: {border: '1px solid red'} - * @param {String} elementName Optional element name, if specified only the styles that matches the schema will be serialized. - * @return {String} String representation of the style object for example: border: 1px solid red. - */ - serialize: function(styles, elementName) { - var css = '', - name, value; - - function serializeStyles(name) { - var styleList, i, l, value; - - styleList = validStyles[name]; - if (styleList) { - for (i = 0, l = styleList.length; i < l; i++) { - name = styleList[i]; - value = styles[name]; - - if (value) { - css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';'; - } - } - } - } - - function isValid(name, elementName) { - var styleMap; - - styleMap = invalidStyles['*']; - if (styleMap && styleMap[name]) { - return false; - } - - styleMap = invalidStyles[elementName]; - if (styleMap && styleMap[name]) { - return false; - } - - return true; - } - - // Serialize styles according to schema - if (elementName && validStyles) { - // Serialize global styles and element specific styles - serializeStyles('*'); - serializeStyles(elementName); - } else { - // Output the styles in the order they are inside the object - for (name in styles) { - value = styles[name]; - - if (value && (!invalidStyles || isValid(name, elementName))) { - css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';'; - } - } - } - - return css; - } - }; - }; - } - ); - - /** - * TreeWalker.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * TreeWalker class enables you to walk the DOM in a linear manner. - * - * @class tinymce.dom.TreeWalker - * @example - * var walker = new tinymce.dom.TreeWalker(startNode); - * - * do { - * console.log(walker.current()); - * } while (walker.next()); - */ - define( - 'tinymce.core.dom.TreeWalker', [], - function() { - /** - * Constructs a new TreeWalker instance. - * - * @constructor - * @method TreeWalker - * @param {Node} startNode Node to start walking from. - * @param {node} rootNode Optional root node to never walk out of. - */ - return function(startNode, rootNode) { - var node = startNode; - - function findSibling(node, startName, siblingName, shallow) { - var sibling, parent; - - if (node) { - // Walk into nodes if it has a start - if (!shallow && node[startName]) { - return node[startName]; - } - - // Return the sibling if it has one - if (node != rootNode) { - sibling = node[siblingName]; - if (sibling) { - return sibling; - } - - // Walk up the parents to look for siblings - for (parent = node.parentNode; parent && parent != rootNode; parent = parent.parentNode) { - sibling = parent[siblingName]; - if (sibling) { - return sibling; - } - } - } - } - } - - function findPreviousNode(node, startName, siblingName, shallow) { - var sibling, parent, child; - - if (node) { - sibling = node[siblingName]; - if (rootNode && sibling === rootNode) { - return; - } - - if (sibling) { - if (!shallow) { - // Walk up the parents to look for siblings - for (child = sibling[startName]; child; child = child[startName]) { - if (!child[startName]) { - return child; - } - } - } - - return sibling; - } - - parent = node.parentNode; - if (parent && parent !== rootNode) { - return parent; - } - } - } - - /** - * Returns the current node. - * - * @method current - * @return {Node} Current node where the walker is. - */ - this.current = function() { - return node; - }; - - /** - * Walks to the next node in tree. - * - * @method next - * @return {Node} Current node where the walker is after moving to the next node. - */ - this.next = function(shallow) { - node = findSibling(node, 'firstChild', 'nextSibling', shallow); - return node; - }; - - /** - * Walks to the previous node in tree. - * - * @method prev - * @return {Node} Current node where the walker is after moving to the previous node. - */ - this.prev = function(shallow) { - node = findSibling(node, 'lastChild', 'previousSibling', shallow); - return node; - }; - - this.prev2 = function(shallow) { - node = findPreviousNode(node, 'lastChild', 'previousSibling', shallow); - return node; - }; - }; - } - ); - - /** - * Entities.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /*jshint bitwise:false */ - /*eslint no-bitwise:0 */ - - /** - * Entity encoder class. - * - * @class tinymce.html.Entities - * @static - * @version 3.4 - */ - define( - 'tinymce.core.html.Entities', [ - "tinymce.core.util.Tools" - ], - function(Tools) { - var makeMap = Tools.makeMap; - - var namedEntities, baseEntities, reverseEntities, - attrsCharsRegExp = /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, - textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, - rawCharsRegExp = /[<>&\"\']/g, - entityRegExp = /&#([a-z0-9]+);?|&([a-z0-9]+);/gi, - asciiMap = { - 128: "\u20AC", - 130: "\u201A", - 131: "\u0192", - 132: "\u201E", - 133: "\u2026", - 134: "\u2020", - 135: "\u2021", - 136: "\u02C6", - 137: "\u2030", - 138: "\u0160", - 139: "\u2039", - 140: "\u0152", - 142: "\u017D", - 145: "\u2018", - 146: "\u2019", - 147: "\u201C", - 148: "\u201D", - 149: "\u2022", - 150: "\u2013", - 151: "\u2014", - 152: "\u02DC", - 153: "\u2122", - 154: "\u0161", - 155: "\u203A", - 156: "\u0153", - 158: "\u017E", - 159: "\u0178" - }; - - // Raw entities - baseEntities = { - '\"': '"', // Needs to be escaped since the YUI compressor would otherwise break the code - "'": ''', - '<': '<', - '>': '>', - '&': '&', - '\u0060': '`' - }; - - // Reverse lookup table for raw entities - reverseEntities = { - '<': '<', - '>': '>', - '&': '&', - '"': '"', - ''': "'" - }; - - // Decodes text by using the browser - function nativeDecode(text) { - var elm; - - elm = document.createElement("div"); - elm.innerHTML = text; - - return elm.textContent || elm.innerText || text; - } - - // Build a two way lookup table for the entities - function buildEntitiesLookup(items, radix) { - var i, chr, entity, lookup = {}; - - if (items) { - items = items.split(','); - radix = radix || 10; - - // Build entities lookup table - for (i = 0; i < items.length; i += 2) { - chr = String.fromCharCode(parseInt(items[i], radix)); - - // Only add non base entities - if (!baseEntities[chr]) { - entity = '&' + items[i + 1] + ';'; - lookup[chr] = entity; - lookup[entity] = chr; - } - } - - return lookup; - } - } - - // Unpack entities lookup where the numbers are in radix 32 to reduce the size - namedEntities = buildEntitiesLookup( - '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' + - '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' + - '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' + - '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' + - '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' + - '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' + - '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' + - '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' + - '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' + - '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' + - 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' + - 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' + - 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' + - 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' + - 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' + - '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' + - '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' + - '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' + - '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' + - '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' + - 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' + - 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' + - 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' + - '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' + - '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32); - - var Entities = { - /** - * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded. - * - * @method encodeRaw - * @param {String} text Text to encode. - * @param {Boolean} attr Optional flag to specify if the text is attribute contents. - * @return {String} Entity encoded text. - */ - encodeRaw: function(text, attr) { - return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) { - return baseEntities[chr] || chr; - }); - }, - - /** - * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents - * since it doesn't know if the context is within a attribute or text node. This was added for compatibility - * and is exposed as the DOMUtils.encode function. - * - * @method encodeAllRaw - * @param {String} text Text to encode. - * @return {String} Entity encoded text. - */ - encodeAllRaw: function(text) { - return ('' + text).replace(rawCharsRegExp, function(chr) { - return baseEntities[chr] || chr; - }); - }, - - /** - * Encodes the specified string using numeric entities. The core entities will be - * encoded as named ones but all non lower ascii characters will be encoded into numeric entities. - * - * @method encodeNumeric - * @param {String} text Text to encode. - * @param {Boolean} attr Optional flag to specify if the text is attribute contents. - * @return {String} Entity encoded text. - */ - encodeNumeric: function(text, attr) { - return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) { - // Multi byte sequence convert it to a single entity - if (chr.length > 1) { - return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';'; - } - - return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';'; - }); - }, - - /** - * Encodes the specified string using named entities. The core entities will be encoded - * as named ones but all non lower ascii characters will be encoded into named entities. - * - * @method encodeNamed - * @param {String} text Text to encode. - * @param {Boolean} attr Optional flag to specify if the text is attribute contents. - * @param {Object} entities Optional parameter with entities to use. - * @return {String} Entity encoded text. - */ - encodeNamed: function(text, attr, entities) { - entities = entities || namedEntities; - - return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) { - return baseEntities[chr] || entities[chr] || chr; - }); - }, - - /** - * Returns an encode function based on the name(s) and it's optional entities. - * - * @method getEncodeFunc - * @param {String} name Comma separated list of encoders for example named,numeric. - * @param {String} entities Optional parameter with entities to use instead of the built in set. - * @return {function} Encode function to be used. - */ - getEncodeFunc: function(name, entities) { - entities = buildEntitiesLookup(entities) || namedEntities; - - function encodeNamedAndNumeric(text, attr) { - return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) { - if (baseEntities[chr] !== undefined) { - return baseEntities[chr]; - } - - if (entities[chr] !== undefined) { - return entities[chr]; - } - - // Convert multi-byte sequences to a single entity. - if (chr.length > 1) { - return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';'; - } - - return '&#' + chr.charCodeAt(0) + ';'; - }); - } - - function encodeCustomNamed(text, attr) { - return Entities.encodeNamed(text, attr, entities); - } - - // Replace + with , to be compatible with previous TinyMCE versions - name = makeMap(name.replace(/\+/g, ',')); - - // Named and numeric encoder - if (name.named && name.numeric) { - return encodeNamedAndNumeric; - } - - // Named encoder - if (name.named) { - // Custom names - if (entities) { - return encodeCustomNamed; - } - - return Entities.encodeNamed; - } - - // Numeric - if (name.numeric) { - return Entities.encodeNumeric; - } - - // Raw encoder - return Entities.encodeRaw; - }, - - /** - * Decodes the specified string, this will replace entities with raw UTF characters. - * - * @method decode - * @param {String} text Text to entity decode. - * @return {String} Entity decoded string. - */ - decode: function(text) { - return text.replace(entityRegExp, function(all, numeric) { - if (numeric) { - if (numeric.charAt(0).toLowerCase() === 'x') { - numeric = parseInt(numeric.substr(1), 16); - } else { - numeric = parseInt(numeric, 10); - } - - // Support upper UTF - if (numeric > 0xFFFF) { - numeric -= 0x10000; - - return String.fromCharCode(0xD800 + (numeric >> 10), 0xDC00 + (numeric & 0x3FF)); - } - - return asciiMap[numeric] || String.fromCharCode(numeric); - } - - return reverseEntities[all] || namedEntities[all] || nativeDecode(all); - }); - } - }; - - return Entities; - } - ); - - /** - * Range.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Old IE Range. - * - * @private - * @class tinymce.dom.Range - */ - define( - 'tinymce.core.dom.Range', [ - "tinymce.core.util.Tools" - ], - function(Tools) { - // Range constructor - function Range(dom) { - var self = this, - doc = dom.doc, - EXTRACT = 0, - CLONE = 1, - DELETE = 2, - TRUE = true, - FALSE = false, - START_OFFSET = 'startOffset', - START_CONTAINER = 'startContainer', - END_CONTAINER = 'endContainer', - END_OFFSET = 'endOffset', - extend = Tools.extend, - nodeIndex = dom.nodeIndex; - - function createDocumentFragment() { - return doc.createDocumentFragment(); - } - - function setStart(n, o) { - _setEndPoint(TRUE, n, o); - } - - function setEnd(n, o) { - _setEndPoint(FALSE, n, o); - } - - function setStartBefore(n) { - setStart(n.parentNode, nodeIndex(n)); - } - - function setStartAfter(n) { - setStart(n.parentNode, nodeIndex(n) + 1); - } - - function setEndBefore(n) { - setEnd(n.parentNode, nodeIndex(n)); - } - - function setEndAfter(n) { - setEnd(n.parentNode, nodeIndex(n) + 1); - } - - function collapse(ts) { - if (ts) { - self[END_CONTAINER] = self[START_CONTAINER]; - self[END_OFFSET] = self[START_OFFSET]; - } else { - self[START_CONTAINER] = self[END_CONTAINER]; - self[START_OFFSET] = self[END_OFFSET]; - } - - self.collapsed = TRUE; - } - - function selectNode(n) { - setStartBefore(n); - setEndAfter(n); - } - - function selectNodeContents(n) { - setStart(n, 0); - setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length); - } - - function compareBoundaryPoints(h, r) { - var sc = self[START_CONTAINER], - so = self[START_OFFSET], - ec = self[END_CONTAINER], - eo = self[END_OFFSET], - rsc = r.startContainer, - rso = r.startOffset, - rec = r.endContainer, - reo = r.endOffset; - - // Check START_TO_START - if (h === 0) { - return _compareBoundaryPoints(sc, so, rsc, rso); - } - - // Check START_TO_END - if (h === 1) { - return _compareBoundaryPoints(ec, eo, rsc, rso); - } - - // Check END_TO_END - if (h === 2) { - return _compareBoundaryPoints(ec, eo, rec, reo); - } - - // Check END_TO_START - if (h === 3) { - return _compareBoundaryPoints(sc, so, rec, reo); - } - } - - function deleteContents() { - _traverse(DELETE); - } - - function extractContents() { - return _traverse(EXTRACT); - } - - function cloneContents() { - return _traverse(CLONE); - } - - function insertNode(n) { - var startContainer = this[START_CONTAINER], - startOffset = this[START_OFFSET], - nn, o; - - // Node is TEXT_NODE or CDATA - if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) { - if (!startOffset) { - // At the start of text - startContainer.parentNode.insertBefore(n, startContainer); - } else if (startOffset >= startContainer.nodeValue.length) { - // At the end of text - dom.insertAfter(n, startContainer); - } else { - // Middle, need to split - nn = startContainer.splitText(startOffset); - startContainer.parentNode.insertBefore(n, nn); - } - } else { - // Insert element node - if (startContainer.childNodes.length > 0) { - o = startContainer.childNodes[startOffset]; - } - - if (o) { - startContainer.insertBefore(n, o); - } else { - if (startContainer.nodeType == 3) { - dom.insertAfter(n, startContainer); - } else { - startContainer.appendChild(n); - } - } - } - } - - function surroundContents(n) { - var f = self.extractContents(); - - self.insertNode(n); - n.appendChild(f); - self.selectNode(n); - } - - function cloneRange() { - return extend(new Range(dom), { - startContainer: self[START_CONTAINER], - startOffset: self[START_OFFSET], - endContainer: self[END_CONTAINER], - endOffset: self[END_OFFSET], - collapsed: self.collapsed, - commonAncestorContainer: self.commonAncestorContainer - }); - } - - // Private methods - - function _getSelectedNode(container, offset) { - var child; - - // TEXT_NODE - if (container.nodeType == 3) { - return container; - } - - if (offset < 0) { - return container; - } - - child = container.firstChild; - while (child && offset > 0) { - --offset; - child = child.nextSibling; - } - - if (child) { - return child; - } - - return container; - } - - function _isCollapsed() { - return (self[START_CONTAINER] == self[END_CONTAINER] && self[START_OFFSET] == self[END_OFFSET]); - } - - function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) { - var c, offsetC, n, cmnRoot, childA, childB; - - // In the first case the boundary-points have the same container. A is before B - // if its offset is less than the offset of B, A is equal to B if its offset is - // equal to the offset of B, and A is after B if its offset is greater than the - // offset of B. - if (containerA == containerB) { - if (offsetA == offsetB) { - return 0; // equal - } - - if (offsetA < offsetB) { - return -1; // before - } - - return 1; // after - } - - // In the second case a child node C of the container of A is an ancestor - // container of B. In this case, A is before B if the offset of A is less than or - // equal to the index of the child node C and A is after B otherwise. - c = containerB; - while (c && c.parentNode != containerA) { - c = c.parentNode; - } - - if (c) { - offsetC = 0; - n = containerA.firstChild; - - while (n != c && offsetC < offsetA) { - offsetC++; - n = n.nextSibling; - } - - if (offsetA <= offsetC) { - return -1; // before - } - - return 1; // after - } - - // In the third case a child node C of the container of B is an ancestor container - // of A. In this case, A is before B if the index of the child node C is less than - // the offset of B and A is after B otherwise. - c = containerA; - while (c && c.parentNode != containerB) { - c = c.parentNode; - } - - if (c) { - offsetC = 0; - n = containerB.firstChild; - - while (n != c && offsetC < offsetB) { - offsetC++; - n = n.nextSibling; - } - - if (offsetC < offsetB) { - return -1; // before - } - - return 1; // after - } - - // In the fourth case, none of three other cases hold: the containers of A and B - // are siblings or descendants of sibling nodes. In this case, A is before B if - // the container of A is before the container of B in a pre-order traversal of the - // Ranges' context tree and A is after B otherwise. - cmnRoot = dom.findCommonAncestor(containerA, containerB); - childA = containerA; - - while (childA && childA.parentNode != cmnRoot) { - childA = childA.parentNode; - } - - if (!childA) { - childA = cmnRoot; - } - - childB = containerB; - while (childB && childB.parentNode != cmnRoot) { - childB = childB.parentNode; - } - - if (!childB) { - childB = cmnRoot; - } - - if (childA == childB) { - return 0; // equal - } - - n = cmnRoot.firstChild; - while (n) { - if (n == childA) { - return -1; // before - } - - if (n == childB) { - return 1; // after - } - - n = n.nextSibling; - } - } - - function _setEndPoint(st, n, o) { - var ec, sc; - - if (st) { - self[START_CONTAINER] = n; - self[START_OFFSET] = o; - } else { - self[END_CONTAINER] = n; - self[END_OFFSET] = o; - } - - // If one boundary-point of a Range is set to have a root container - // other than the current one for the Range, the Range is collapsed to - // the new position. This enforces the restriction that both boundary- - // points of a Range must have the same root container. - ec = self[END_CONTAINER]; - while (ec.parentNode) { - ec = ec.parentNode; - } - - sc = self[START_CONTAINER]; - while (sc.parentNode) { - sc = sc.parentNode; - } - - if (sc == ec) { - // The start position of a Range is guaranteed to never be after the - // end position. To enforce this restriction, if the start is set to - // be at a position after the end, the Range is collapsed to that - // position. - if (_compareBoundaryPoints(self[START_CONTAINER], self[START_OFFSET], self[END_CONTAINER], self[END_OFFSET]) > 0) { - self.collapse(st); - } - } else { - self.collapse(st); - } - - self.collapsed = _isCollapsed(); - self.commonAncestorContainer = dom.findCommonAncestor(self[START_CONTAINER], self[END_CONTAINER]); - } - - function _traverse(how) { - var c, endContainerDepth = 0, - startContainerDepth = 0, - p, depthDiff, startNode, endNode, sp, ep; - - if (self[START_CONTAINER] == self[END_CONTAINER]) { - return _traverseSameContainer(how); - } - - for (c = self[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) { - if (p == self[START_CONTAINER]) { - return _traverseCommonStartContainer(c, how); - } - - ++endContainerDepth; - } - - for (c = self[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) { - if (p == self[END_CONTAINER]) { - return _traverseCommonEndContainer(c, how); - } - - ++startContainerDepth; - } - - depthDiff = startContainerDepth - endContainerDepth; - - startNode = self[START_CONTAINER]; - while (depthDiff > 0) { - startNode = startNode.parentNode; - depthDiff--; - } - - endNode = self[END_CONTAINER]; - while (depthDiff < 0) { - endNode = endNode.parentNode; - depthDiff++; - } - - // ascend the ancestor hierarchy until we have a common parent. - for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) { - startNode = sp; - endNode = ep; - } - - return _traverseCommonAncestors(startNode, endNode, how); - } - - function _traverseSameContainer(how) { - var frag, s, sub, n, cnt, sibling, xferNode, start, len; - - if (how != DELETE) { - frag = createDocumentFragment(); - } - - // If selection is empty, just return the fragment - if (self[START_OFFSET] == self[END_OFFSET]) { - return frag; - } - - // Text node needs special case handling - if (self[START_CONTAINER].nodeType == 3) { // TEXT_NODE - // get the substring - s = self[START_CONTAINER].nodeValue; - sub = s.substring(self[START_OFFSET], self[END_OFFSET]); - - // set the original text node to its new value - if (how != CLONE) { - n = self[START_CONTAINER]; - start = self[START_OFFSET]; - len = self[END_OFFSET] - self[START_OFFSET]; - - if (start === 0 && len >= n.nodeValue.length - 1) { - n.parentNode.removeChild(n); - } else { - n.deleteData(start, len); - } - - // Nothing is partially selected, so collapse to start point - self.collapse(TRUE); - } - - if (how == DELETE) { - return; - } - - if (sub.length > 0) { - frag.appendChild(doc.createTextNode(sub)); - } - - return frag; - } - - // Copy nodes between the start/end offsets. - n = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]); - cnt = self[END_OFFSET] - self[START_OFFSET]; - - while (n && cnt > 0) { - sibling = n.nextSibling; - xferNode = _traverseFullySelected(n, how); - - if (frag) { - frag.appendChild(xferNode); - } - - --cnt; - n = sibling; - } - - // Nothing is partially selected, so collapse to start point - if (how != CLONE) { - self.collapse(TRUE); - } - - return frag; - } - - function _traverseCommonStartContainer(endAncestor, how) { - var frag, n, endIdx, cnt, sibling, xferNode; - - if (how != DELETE) { - frag = createDocumentFragment(); - } - - n = _traverseRightBoundary(endAncestor, how); - - if (frag) { - frag.appendChild(n); - } - - endIdx = nodeIndex(endAncestor); - cnt = endIdx - self[START_OFFSET]; - - if (cnt <= 0) { - // Collapse to just before the endAncestor, which - // is partially selected. - if (how != CLONE) { - self.setEndBefore(endAncestor); - self.collapse(FALSE); - } - - return frag; - } - - n = endAncestor.previousSibling; - while (cnt > 0) { - sibling = n.previousSibling; - xferNode = _traverseFullySelected(n, how); - - if (frag) { - frag.insertBefore(xferNode, frag.firstChild); - } - - --cnt; - n = sibling; - } - - // Collapse to just before the endAncestor, which - // is partially selected. - if (how != CLONE) { - self.setEndBefore(endAncestor); - self.collapse(FALSE); - } - - return frag; - } - - function _traverseCommonEndContainer(startAncestor, how) { - var frag, startIdx, n, cnt, sibling, xferNode; - - if (how != DELETE) { - frag = createDocumentFragment(); - } - - n = _traverseLeftBoundary(startAncestor, how); - if (frag) { - frag.appendChild(n); - } - - startIdx = nodeIndex(startAncestor); - ++startIdx; // Because we already traversed it - - cnt = self[END_OFFSET] - startIdx; - n = startAncestor.nextSibling; - while (n && cnt > 0) { - sibling = n.nextSibling; - xferNode = _traverseFullySelected(n, how); - - if (frag) { - frag.appendChild(xferNode); - } - - --cnt; - n = sibling; - } - - if (how != CLONE) { - self.setStartAfter(startAncestor); - self.collapse(TRUE); - } - - return frag; - } - - function _traverseCommonAncestors(startAncestor, endAncestor, how) { - var n, frag, startOffset, endOffset, cnt, sibling, nextSibling; - - if (how != DELETE) { - frag = createDocumentFragment(); - } - - n = _traverseLeftBoundary(startAncestor, how); - if (frag) { - frag.appendChild(n); - } - - startOffset = nodeIndex(startAncestor); - endOffset = nodeIndex(endAncestor); - ++startOffset; - - cnt = endOffset - startOffset; - sibling = startAncestor.nextSibling; - - while (cnt > 0) { - nextSibling = sibling.nextSibling; - n = _traverseFullySelected(sibling, how); - - if (frag) { - frag.appendChild(n); - } - - sibling = nextSibling; - --cnt; - } - - n = _traverseRightBoundary(endAncestor, how); - - if (frag) { - frag.appendChild(n); - } - - if (how != CLONE) { - self.setStartAfter(startAncestor); - self.collapse(TRUE); - } - - return frag; - } - - function _traverseRightBoundary(root, how) { - var next = _getSelectedNode(self[END_CONTAINER], self[END_OFFSET] - 1), - parent, clonedParent; - var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != self[END_CONTAINER]; - - if (next == root) { - return _traverseNode(next, isFullySelected, FALSE, how); - } - - parent = next.parentNode; - clonedParent = _traverseNode(parent, FALSE, FALSE, how); - - while (parent) { - while (next) { - prevSibling = next.previousSibling; - clonedChild = _traverseNode(next, isFullySelected, FALSE, how); - - if (how != DELETE) { - clonedParent.insertBefore(clonedChild, clonedParent.firstChild); - } - - isFullySelected = TRUE; - next = prevSibling; - } - - if (parent == root) { - return clonedParent; - } - - next = parent.previousSibling; - parent = parent.parentNode; - - clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how); - - if (how != DELETE) { - clonedGrandParent.appendChild(clonedParent); - } - - clonedParent = clonedGrandParent; - } - } - - function _traverseLeftBoundary(root, how) { - var next = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]), - isFullySelected = next != self[START_CONTAINER]; - var parent, clonedParent, nextSibling, clonedChild, clonedGrandParent; - - if (next == root) { - return _traverseNode(next, isFullySelected, TRUE, how); - } - - parent = next.parentNode; - clonedParent = _traverseNode(parent, FALSE, TRUE, how); - - while (parent) { - while (next) { - nextSibling = next.nextSibling; - clonedChild = _traverseNode(next, isFullySelected, TRUE, how); - - if (how != DELETE) { - clonedParent.appendChild(clonedChild); - } - - isFullySelected = TRUE; - next = nextSibling; - } - - if (parent == root) { - return clonedParent; - } - - next = parent.nextSibling; - parent = parent.parentNode; - - clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how); - - if (how != DELETE) { - clonedGrandParent.appendChild(clonedParent); - } - - clonedParent = clonedGrandParent; - } - } - - function _traverseNode(n, isFullySelected, isLeft, how) { - var txtValue, newNodeValue, oldNodeValue, offset, newNode; - - if (isFullySelected) { - return _traverseFullySelected(n, how); - } - - // TEXT_NODE - if (n.nodeType == 3) { - txtValue = n.nodeValue; - - if (isLeft) { - offset = self[START_OFFSET]; - newNodeValue = txtValue.substring(offset); - oldNodeValue = txtValue.substring(0, offset); - } else { - offset = self[END_OFFSET]; - newNodeValue = txtValue.substring(0, offset); - oldNodeValue = txtValue.substring(offset); - } - - if (how != CLONE) { - n.nodeValue = oldNodeValue; - } - - if (how == DELETE) { - return; - } - - newNode = dom.clone(n, FALSE); - newNode.nodeValue = newNodeValue; - - return newNode; - } - - if (how == DELETE) { - return; - } - - return dom.clone(n, FALSE); - } - - function _traverseFullySelected(n, how) { - if (how != DELETE) { - return how == CLONE ? dom.clone(n, TRUE) : n; - } - - n.parentNode.removeChild(n); - } - - function toStringIE() { - return dom.create('body', null, cloneContents()).outerText; - } - - extend(self, { - // Initial states - startContainer: doc, - startOffset: 0, - endContainer: doc, - endOffset: 0, - collapsed: TRUE, - commonAncestorContainer: doc, - - // Range constants - START_TO_START: 0, - START_TO_END: 1, - END_TO_END: 2, - END_TO_START: 3, - - // Public methods - setStart: setStart, - setEnd: setEnd, - setStartBefore: setStartBefore, - setStartAfter: setStartAfter, - setEndBefore: setEndBefore, - setEndAfter: setEndAfter, - collapse: collapse, - selectNode: selectNode, - selectNodeContents: selectNodeContents, - compareBoundaryPoints: compareBoundaryPoints, - deleteContents: deleteContents, - extractContents: extractContents, - cloneContents: cloneContents, - insertNode: insertNode, - surroundContents: surroundContents, - cloneRange: cloneRange, - toStringIE: toStringIE - }); - - return self; - } - - // Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype - Range.prototype.toString = function() { - return this.toStringIE(); - }; - - return Range; - } - ); - - defineGlobal("global!Array", Array); - defineGlobal("global!Error", Error); - define( - 'ephox.katamari.api.Fun', - - [ - 'global!Array', - 'global!Error' - ], - - function(Array, Error) { - - var noop = function() {}; - - var compose = function(fa, fb) { - return function() { - return fa(fb.apply(null, arguments)); - }; - }; - - var constant = function(value) { - return function() { - return value; - }; - }; - - var identity = function(x) { - return x; - }; - - var tripleEquals = function(a, b) { - return a === b; - }; - - // Don't use array slice(arguments), makes the whole function unoptimisable on Chrome - var curry = function(f) { - // equivalent to arguments.slice(1) - // starting at 1 because 0 is the f, makes things tricky. - // Pay attention to what variable is where, and the -1 magic. - // thankfully, we have tests for this. - var args = new Array(arguments.length - 1); - for (var i = 1; i < arguments.length; i++) args[i - 1] = arguments[i]; - - return function() { - var newArgs = new Array(arguments.length); - for (var j = 0; j < newArgs.length; j++) newArgs[j] = arguments[j]; - - var all = args.concat(newArgs); - return f.apply(null, all); - }; - }; - - var not = function(f) { - return function() { - return !f.apply(null, arguments); - }; - }; - - var die = function(msg) { - return function() { - throw new Error(msg); - }; - }; - - var apply = function(f) { - return f(); - }; - - var call = function(f) { - f(); - }; - - var never = constant(false); - var always = constant(true); - - - return { - noop: noop, - compose: compose, - constant: constant, - identity: identity, - tripleEquals: tripleEquals, - curry: curry, - not: not, - die: die, - apply: apply, - call: call, - never: never, - always: always - }; - } - ); - - defineGlobal("global!Object", Object); - define( - 'ephox.katamari.api.Option', - - [ - 'ephox.katamari.api.Fun', - 'global!Object' - ], - - function(Fun, Object) { - - var never = Fun.never; - var always = Fun.always; - - /** - Option objects support the following methods: - - fold :: this Option a -> ((() -> b, a -> b)) -> Option b - - is :: this Option a -> a -> Boolean - - isSome :: this Option a -> () -> Boolean - - isNone :: this Option a -> () -> Boolean - - getOr :: this Option a -> a -> a - - getOrThunk :: this Option a -> (() -> a) -> a - - getOrDie :: this Option a -> String -> a - - or :: this Option a -> Option a -> Option a - - if some: return self - - if none: return opt - - orThunk :: this Option a -> (() -> Option a) -> Option a - - Same as "or", but uses a thunk instead of a value - - map :: this Option a -> (a -> b) -> Option b - - "fmap" operation on the Option Functor. - - same as 'each' - - ap :: this Option a -> Option (a -> b) -> Option b - - "apply" operation on the Option Apply/Applicative. - - Equivalent to <*> in Haskell/PureScript. - - each :: this Option a -> (a -> b) -> Option b - - same as 'map' - - bind :: this Option a -> (a -> Option b) -> Option b - - "bind"/"flatMap" operation on the Option Bind/Monad. - - Equivalent to >>= in Haskell/PureScript; flatMap in Scala. - - flatten :: {this Option (Option a))} -> () -> Option a - - "flatten"/"join" operation on the Option Monad. - - exists :: this Option a -> (a -> Boolean) -> Boolean - - forall :: this Option a -> (a -> Boolean) -> Boolean - - filter :: this Option a -> (a -> Boolean) -> Option a - - equals :: this Option a -> Option a -> Boolean - - equals_ :: this Option a -> (Option a, a -> Boolean) -> Boolean - - toArray :: this Option a -> () -> [a] - - */ - - var none = function() { - return NONE; - }; - - var NONE = (function() { - var eq = function(o) { - return o.isNone(); - }; - - // inlined from peanut, maybe a micro-optimisation? - var call = function(thunk) { - return thunk(); - }; - var id = function(n) { - return n; - }; - var noop = function() {}; - - var me = { - fold: function(n, s) { - return n(); - }, - is: never, - isSome: never, - isNone: always, - getOr: id, - getOrThunk: call, - getOrDie: function(msg) { - throw new Error(msg || 'error: getOrDie called on none.'); - }, - or: id, - orThunk: call, - map: none, - ap: none, - each: noop, - bind: none, - flatten: none, - exists: never, - forall: always, - filter: none, - equals: eq, - equals_: eq, - toArray: function() { - return []; - }, - toString: Fun.constant("none()") - }; - if (Object.freeze) Object.freeze(me); - return me; - })(); - - - /** some :: a -> Option a */ - var some = function(a) { - - // inlined from peanut, maybe a micro-optimisation? - var constant_a = function() { - return a; - }; - - var self = function() { - // can't Fun.constant this one - return me; - }; - - var map = function(f) { - return some(f(a)); - }; - - var bind = function(f) { - return f(a); - }; - - var me = { - fold: function(n, s) { - return s(a); - }, - is: function(v) { - return a === v; - }, - isSome: always, - isNone: never, - getOr: constant_a, - getOrThunk: constant_a, - getOrDie: constant_a, - or: self, - orThunk: self, - map: map, - ap: function(optfab) { - return optfab.fold(none, function(fab) { - return some(fab(a)); - }); - }, - each: function(f) { - f(a); - }, - bind: bind, - flatten: constant_a, - exists: bind, - forall: bind, - filter: function(f) { - return f(a) ? me : NONE; - }, - equals: function(o) { - return o.is(a); - }, - equals_: function(o, elementEq) { - return o.fold( - never, - function(b) { - return elementEq(a, b); - } - ); - }, - toArray: function() { - return [a]; - }, - toString: function() { - return 'some(' + a + ')'; - } - }; - return me; - }; - - /** from :: undefined|null|a -> Option a */ - var from = function(value) { - return value === null || value === undefined ? NONE : some(value); - }; - - return { - some: some, - none: none, - from: from - }; - } - ); - - defineGlobal("global!String", String); - define( - 'ephox.katamari.api.Arr', - - [ - 'ephox.katamari.api.Option', - 'global!Array', - 'global!Error', - 'global!String' - ], - - function(Option, Array, Error, String) { - // Use the native Array.indexOf if it is available (IE9+) otherwise fall back to manual iteration - // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf - var rawIndexOf = (function() { - var pIndexOf = Array.prototype.indexOf; - - var fastIndex = function(xs, x) { - return pIndexOf.call(xs, x); - }; - - var slowIndex = function(xs, x) { - return slowIndexOf(xs, x); - }; - - return pIndexOf === undefined ? slowIndex : fastIndex; - })(); - - var indexOf = function(xs, x) { - // The rawIndexOf method does not wrap up in an option. This is for performance reasons. - var r = rawIndexOf(xs, x); - return r === -1 ? Option.none() : Option.some(r); - }; - - var contains = function(xs, x) { - return rawIndexOf(xs, x) > -1; - }; - - // Using findIndex is likely less optimal in Chrome (dynamic return type instead of bool) - // but if we need that micro-optimisation we can inline it later. - var exists = function(xs, pred) { - return findIndex(xs, pred).isSome(); - }; - - var range = function(num, f) { - var r = []; - for (var i = 0; i < num; i++) { - r.push(f(i)); - } - return r; - }; - - // It's a total micro optimisation, but these do make some difference. - // Particularly for browsers other than Chrome. - // - length caching - // http://jsperf.com/browser-diet-jquery-each-vs-for-loop/69 - // - not using push - // http://jsperf.com/array-direct-assignment-vs-push/2 - - var chunk = function(array, size) { - var r = []; - for (var i = 0; i < array.length; i += size) { - var s = array.slice(i, i + size); - r.push(s); - } - return r; - }; - - var map = function(xs, f) { - // pre-allocating array size when it's guaranteed to be known - // http://jsperf.com/push-allocated-vs-dynamic/22 - var len = xs.length; - var r = new Array(len); - for (var i = 0; i < len; i++) { - var x = xs[i]; - r[i] = f(x, i, xs); - } - return r; - }; - - // Unwound implementing other functions in terms of each. - // The code size is roughly the same, and it should allow for better optimisation. - var each = function(xs, f) { - for (var i = 0, len = xs.length; i < len; i++) { - var x = xs[i]; - f(x, i, xs); - } - }; - - var eachr = function(xs, f) { - for (var i = xs.length - 1; i >= 0; i--) { - var x = xs[i]; - f(x, i, xs); - } - }; - - var partition = function(xs, pred) { - var pass = []; - var fail = []; - for (var i = 0, len = xs.length; i < len; i++) { - var x = xs[i]; - var arr = pred(x, i, xs) ? pass : fail; - arr.push(x); - } - return { - pass: pass, - fail: fail - }; - }; - - var filter = function(xs, pred) { - var r = []; - for (var i = 0, len = xs.length; i < len; i++) { - var x = xs[i]; - if (pred(x, i, xs)) { - r.push(x); - } - } - return r; - }; - - /* - * Groups an array into contiguous arrays of like elements. Whether an element is like or not depends on f. - * - * f is a function that derives a value from an element - e.g. true or false, or a string. - * Elements are like if this function generates the same value for them (according to ===). - * - * - * Order of the elements is preserved. Arr.flatten() on the result will return the original list, as with Haskell groupBy function. - * For a good explanation, see the group function (which is a special case of groupBy) - * http://hackage.haskell.org/package/base-4.7.0.0/docs/Data-List.html#v:group - */ - var groupBy = function(xs, f) { - if (xs.length === 0) { - return []; - } else { - var wasType = f(xs[0]); // initial case for matching - var r = []; - var group = []; - - for (var i = 0, len = xs.length; i < len; i++) { - var x = xs[i]; - var type = f(x); - if (type !== wasType) { - r.push(group); - group = []; - } - wasType = type; - group.push(x); - } - if (group.length !== 0) { - r.push(group); - } - return r; - } - }; - - var foldr = function(xs, f, acc) { - eachr(xs, function(x) { - acc = f(acc, x); - }); - return acc; - }; - - var foldl = function(xs, f, acc) { - each(xs, function(x) { - acc = f(acc, x); - }); - return acc; - }; - - var find = function(xs, pred) { - for (var i = 0, len = xs.length; i < len; i++) { - var x = xs[i]; - if (pred(x, i, xs)) { - return Option.some(x); - } - } - return Option.none(); - }; - - var findIndex = function(xs, pred) { - for (var i = 0, len = xs.length; i < len; i++) { - var x = xs[i]; - if (pred(x, i, xs)) { - return Option.some(i); - } - } - - return Option.none(); - }; - - var slowIndexOf = function(xs, x) { - for (var i = 0, len = xs.length; i < len; ++i) { - if (xs[i] === x) { - return i; - } - } - - return -1; - }; - - var push = Array.prototype.push; - var flatten = function(xs) { - // Note, this is possible because push supports multiple arguments: - // http://jsperf.com/concat-push/6 - // Note that in the past, concat() would silently work (very slowly) for array-like objects. - // With this change it will throw an error. - var r = []; - for (var i = 0, len = xs.length; i < len; ++i) { - // Ensure that each value is an array itself - if (!Array.prototype.isPrototypeOf(xs[i])) throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs); - push.apply(r, xs[i]); - } - return r; - }; - - var bind = function(xs, f) { - var output = map(xs, f); - return flatten(output); - }; - - var forall = function(xs, pred) { - for (var i = 0, len = xs.length; i < len; ++i) { - var x = xs[i]; - if (pred(x, i, xs) !== true) { - return false; - } - } - return true; - }; - - var equal = function(a1, a2) { - return a1.length === a2.length && forall(a1, function(x, i) { - return x === a2[i]; - }); - }; - - var slice = Array.prototype.slice; - var reverse = function(xs) { - var r = slice.call(xs, 0); - r.reverse(); - return r; - }; - - var difference = function(a1, a2) { - return filter(a1, function(x) { - return !contains(a2, x); - }); - }; - - var mapToObject = function(xs, f) { - var r = {}; - for (var i = 0, len = xs.length; i < len; i++) { - var x = xs[i]; - r[String(x)] = f(x, i); - } - return r; - }; - - var pure = function(x) { - return [x]; - }; - - var sort = function(xs, comparator) { - var copy = slice.call(xs, 0); - copy.sort(comparator); - return copy; - }; - - var head = function(xs) { - return xs.length === 0 ? Option.none() : Option.some(xs[0]); - }; - - var last = function(xs) { - return xs.length === 0 ? Option.none() : Option.some(xs[xs.length - 1]); - }; - - return { - map: map, - each: each, - eachr: eachr, - partition: partition, - filter: filter, - groupBy: groupBy, - indexOf: indexOf, - foldr: foldr, - foldl: foldl, - find: find, - findIndex: findIndex, - flatten: flatten, - bind: bind, - forall: forall, - exists: exists, - contains: contains, - equal: equal, - reverse: reverse, - chunk: chunk, - difference: difference, - mapToObject: mapToObject, - pure: pure, - sort: sort, - range: range, - head: head, - last: last - }; - } - ); - defineGlobal("global!setTimeout", setTimeout); - define( - 'ephox.katamari.api.LazyValue', - - [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Option', - 'global!setTimeout' - ], - - function(Arr, Option, setTimeout) { - var nu = function(baseFn) { - var data = Option.none(); - var callbacks = []; - - /** map :: this LazyValue a -> (a -> b) -> LazyValue b */ - var map = function(f) { - return nu(function(nCallback) { - get(function(data) { - nCallback(f(data)); - }); - }); - }; - - var get = function(nCallback) { - if (isReady()) call(nCallback); - else callbacks.push(nCallback); - }; - - var set = function(x) { - data = Option.some(x); - run(callbacks); - callbacks = []; - }; - - var isReady = function() { - return data.isSome(); - }; - - var run = function(cbs) { - Arr.each(cbs, call); - }; - - var call = function(cb) { - data.each(function(x) { - setTimeout(function() { - cb(x); - }, 0); - }); - }; - - // Lazy values cache the value and kick off immediately - baseFn(set); - - return { - get: get, - map: map, - isReady: isReady - }; - }; - - var pure = function(a) { - return nu(function(callback) { - callback(a); - }); - }; - - return { - nu: nu, - pure: pure - }; - } - ); - define( - 'ephox.katamari.async.Bounce', - - [ - 'global!Array', - 'global!setTimeout' - ], - - function(Array, setTimeout) { - - var bounce = function(f) { - return function() { - var args = Array.prototype.slice.call(arguments); - var me = this; - setTimeout(function() { - f.apply(me, args); - }, 0); - }; - }; - - return { - bounce: bounce - }; - } - ); - - define( - 'ephox.katamari.api.Future', - - [ - 'ephox.katamari.api.LazyValue', - 'ephox.katamari.async.Bounce' - ], - - /** A future value that is evaluated on demand. The base function is re-evaluated each time 'get' is called. */ - function(LazyValue, Bounce) { - var nu = function(baseFn) { - var get = function(callback) { - baseFn(Bounce.bounce(callback)); - }; - - /** map :: this Future a -> (a -> b) -> Future b */ - var map = function(fab) { - return nu(function(callback) { - get(function(a) { - var value = fab(a); - callback(value); - }); - }); - }; - - /** bind :: this Future a -> (a -> Future b) -> Future b */ - var bind = function(aFutureB) { - return nu(function(callback) { - get(function(a) { - aFutureB(a).get(callback); - }); - }); - }; - - /** anonBind :: this Future a -> Future b -> Future b - * Returns a future, which evaluates the first future, ignores the result, then evaluates the second. - */ - var anonBind = function(futureB) { - return nu(function(callback) { - get(function(a) { - futureB.get(callback); - }); - }); - }; - - var toLazy = function() { - return LazyValue.nu(get); - }; - - return { - map: map, - bind: bind, - anonBind: anonBind, - toLazy: toLazy, - get: get - }; - - }; - - /** a -> Future a */ - var pure = function(a) { - return nu(function(callback) { - callback(a); - }); - }; - - return { - nu: nu, - pure: pure - }; - } - ); - - define( - 'ephox.katamari.async.AsyncValues', - - [ - 'ephox.katamari.api.Arr' - ], - - function(Arr) { - /* - * NOTE: an `asyncValue` must have a `get` function which gets given a callback and calls - * that callback with a value once it is ready - * - * e.g - * { - * get: function (callback) { callback(10); } - * } - */ - var par = function(asyncValues, nu) { - return nu(function(callback) { - var r = []; - var count = 0; - - var cb = function(i) { - return function(value) { - r[i] = value; - count++; - if (count >= asyncValues.length) { - callback(r); - } - }; - }; - - if (asyncValues.length === 0) { - callback([]); - } else { - Arr.each(asyncValues, function(asyncValue, i) { - asyncValue.get(cb(i)); - }); - } - }); - }; - - return { - par: par - }; - } - ); - define( - 'ephox.katamari.api.Futures', - - [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Future', - 'ephox.katamari.async.AsyncValues' - ], - - function(Arr, Future, AsyncValues) { - /** par :: [Future a] -> Future [a] */ - var par = function(futures) { - return AsyncValues.par(futures, Future.nu); - }; - - /** mapM :: [a] -> (a -> Future b) -> Future [b] */ - var mapM = function(array, fn) { - var futures = Arr.map(array, fn); - return par(futures); - }; - - /** Kleisli composition of two functions: a -> Future b. - * Note the order of arguments: g is invoked first, then the result passed to f. - * This is in line with f . g = \x -> f (g a) - * - * compose :: ((b -> Future c), (a -> Future b)) -> a -> Future c - */ - var compose = function(f, g) { - return function(a) { - return g(a).bind(f); - }; - }; - - return { - par: par, - mapM: mapM, - compose: compose - }; - } - ); - define( - 'ephox.katamari.api.Result', - - [ - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Option' - ], - - function(Fun, Option) { - /* The type signatures for Result - * is :: this Result a -> a -> Bool - * or :: this Result a -> Result a -> Result a - * orThunk :: this Result a -> (_ -> Result a) -> Result a - * map :: this Result a -> (a -> b) -> Result b - * each :: this Result a -> (a -> _) -> _ - * bind :: this Result a -> (a -> Result b) -> Result b - * fold :: this Result a -> (_ -> b, a -> b) -> b - * exists :: this Result a -> (a -> Bool) -> Bool - * forall :: this Result a -> (a -> Bool) -> Bool - * toOption :: this Result a -> Option a - * isValue :: this Result a -> Bool - * isError :: this Result a -> Bool - * getOr :: this Result a -> a -> a - * getOrThunk :: this Result a -> (_ -> a) -> a - * getOrDie :: this Result a -> a (or throws error) - */ - - var value = function(o) { - var is = function(v) { - return o === v; - }; - - var or = function(opt) { - return value(o); - }; - - var orThunk = function(f) { - return value(o); - }; - - var map = function(f) { - return value(f(o)); - }; - - var each = function(f) { - f(o); - }; - - var bind = function(f) { - return f(o); - }; - - var fold = function(_, onValue) { - return onValue(o); - }; - - var exists = function(f) { - return f(o); - }; - - var forall = function(f) { - return f(o); - }; - - var toOption = function() { - return Option.some(o); - }; - - return { - is: is, - isValue: Fun.constant(true), - isError: Fun.constant(false), - getOr: Fun.constant(o), - getOrThunk: Fun.constant(o), - getOrDie: Fun.constant(o), - or: or, - orThunk: orThunk, - fold: fold, - map: map, - each: each, - bind: bind, - exists: exists, - forall: forall, - toOption: toOption - }; - }; - - var error = function(message) { - var getOrThunk = function(f) { - return f(); - }; - - var getOrDie = function() { - return Fun.die(message)(); - }; - - var or = function(opt) { - return opt; - }; - - var orThunk = function(f) { - return f(); - }; - - var map = function(f) { - return error(message); - }; - - var bind = function(f) { - return error(message); - }; - - var fold = function(onError, _) { - return onError(message); - }; - - return { - is: Fun.constant(false), - isValue: Fun.constant(false), - isError: Fun.constant(true), - getOr: Fun.identity, - getOrThunk: getOrThunk, - getOrDie: getOrDie, - or: or, - orThunk: orThunk, - fold: fold, - map: map, - each: Fun.noop, - bind: bind, - exists: Fun.constant(false), - forall: Fun.constant(true), - toOption: Option.none - }; - }; - - return { - value: value, - error: error - }; - } - ); - - /** - * StyleSheetLoader.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class handles loading of external stylesheets and fires events when these are loaded. - * - * @class tinymce.dom.StyleSheetLoader - * @private - */ - define( - 'tinymce.core.dom.StyleSheetLoader', [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Future', - 'ephox.katamari.api.Futures', - 'ephox.katamari.api.Result', - 'tinymce.core.util.Delay', - 'tinymce.core.util.Tools' - ], - function(Arr, Fun, Future, Futures, Result, Delay, Tools) { - "use strict"; - - return function(document, settings) { - var idCount = 0, - loadedStates = {}, - maxLoadTime; - - settings = settings || {}; - maxLoadTime = settings.maxLoadTime || 5000; - - function appendToHead(node) { - document.getElementsByTagName('head')[0].appendChild(node); - } - - /** - * Loads the specified css style sheet file and call the loadedCallback once it's finished loading. - * - * @method load - * @param {String} url Url to be loaded. - * @param {Function} loadedCallback Callback to be executed when loaded. - * @param {Function} errorCallback Callback to be executed when failed loading. - */ - function load(url, loadedCallback, errorCallback) { - var link, style, startTime, state; - - function passed() { - var callbacks = state.passed, - i = callbacks.length; - - while (i--) { - callbacks[i](); - } - - state.status = 2; - state.passed = []; - state.failed = []; - } - - function failed() { - var callbacks = state.failed, - i = callbacks.length; - - while (i--) { - callbacks[i](); - } - - state.status = 3; - state.passed = []; - state.failed = []; - } - - // Sniffs for older WebKit versions that have the link.onload but a broken one - function isOldWebKit() { - var webKitChunks = navigator.userAgent.match(/WebKit\/(\d*)/); - return !!(webKitChunks && webKitChunks[1] < 536); - } - - // Calls the waitCallback until the test returns true or the timeout occurs - function wait(testCallback, waitCallback) { - if (!testCallback()) { - // Wait for timeout - if ((new Date().getTime()) - startTime < maxLoadTime) { - Delay.setTimeout(waitCallback); - } else { - failed(); - } - } - } - - // Workaround for WebKit that doesn't properly support the onload event for link elements - // Or WebKit that fires the onload event before the StyleSheet is added to the document - function waitForWebKitLinkLoaded() { - wait(function() { - var styleSheets = document.styleSheets, - styleSheet, i = styleSheets.length, - owner; - - while (i--) { - styleSheet = styleSheets[i]; - owner = styleSheet.ownerNode ? styleSheet.ownerNode : styleSheet.owningElement; - if (owner && owner.id === link.id) { - passed(); - return true; - } - } - }, waitForWebKitLinkLoaded); - } - - // Workaround for older Geckos that doesn't have any onload event for StyleSheets - function waitForGeckoLinkLoaded() { - wait(function() { - try { - // Accessing the cssRules will throw an exception until the CSS file is loaded - var cssRules = style.sheet.cssRules; - passed(); - return !!cssRules; - } catch (ex) { - // Ignore - } - }, waitForGeckoLinkLoaded); - } - - url = Tools._addCacheSuffix(url); - - if (!loadedStates[url]) { - state = { - passed: [], - failed: [] - }; - - loadedStates[url] = state; - } else { - state = loadedStates[url]; - } - - if (loadedCallback) { - state.passed.push(loadedCallback); - } - - if (errorCallback) { - state.failed.push(errorCallback); - } - - // Is loading wait for it to pass - if (state.status == 1) { - return; - } - - // Has finished loading and was success - if (state.status == 2) { - passed(); - return; - } - - // Has finished loading and was a failure - if (state.status == 3) { - failed(); - return; - } - - // Start loading - state.status = 1; - link = document.createElement('link'); - link.rel = 'stylesheet'; - link.type = 'text/css'; - link.id = 'u' + (idCount++); - link.async = false; - link.defer = false; - startTime = new Date().getTime(); - - // Feature detect onload on link element and sniff older webkits since it has an broken onload event - if ("onload" in link && !isOldWebKit()) { - link.onload = waitForWebKitLinkLoaded; - link.onerror = failed; - } else { - // Sniff for old Firefox that doesn't support the onload event on link elements - // TODO: Remove this in the future when everyone uses modern browsers - if (navigator.userAgent.indexOf("Firefox") > 0) { - style = document.createElement('style'); - style.textContent = '@import "' + url + '"'; - waitForGeckoLinkLoaded(); - appendToHead(style); - return; - } - - // Use the id owner on older webkits - waitForWebKitLinkLoaded(); - } - - appendToHead(link); - link.href = url; - } - - var loadF = function(url) { - return Future.nu(function(resolve) { - load( - url, - Fun.compose(resolve, Fun.constant(Result.value(url))), - Fun.compose(resolve, Fun.constant(Result.error(url))) - ); - }); - }; - - var unbox = function(result) { - return result.fold(Fun.identity, Fun.identity); - }; - - var loadAll = function(urls, success, failure) { - Futures.par(Arr.map(urls, loadF)).get(function(result) { - var parts = Arr.partition(result, function(r) { - return r.isValue(); - }); - - if (parts.fail.length > 0) { - failure(parts.fail.map(unbox)); - } else { - success(parts.pass.map(unbox)); - } - }); - }; - - return { - load: load, - loadAll: loadAll - }; - }; - } - ); - - /** - * Schema.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Schema validator class. - * - * @class tinymce.html.Schema - * @example - * if (tinymce.activeEditor.schema.isValidChild('p', 'span')) - * alert('span is valid child of p.'); - * - * if (tinymce.activeEditor.schema.getElementRule('p')) - * alert('P is a valid element.'); - * - * @class tinymce.html.Schema - * @version 3.4 - */ - define( - 'tinymce.core.html.Schema', [ - "tinymce.core.util.Tools" - ], - function(Tools) { - var mapCache = {}, - dummyObj = {}; - var makeMap = Tools.makeMap, - each = Tools.each, - extend = Tools.extend, - explode = Tools.explode, - inArray = Tools.inArray; - - function split(items, delim) { - items = Tools.trim(items); - return items ? items.split(delim || ' ') : []; - } - - /** - * Builds a schema lookup table - * - * @private - * @param {String} type html4, html5 or html5-strict schema type. - * @return {Object} Schema lookup table. - */ - function compileSchema(type) { - var schema = {}, - globalAttributes, blockContent; - var phrasingContent, flowContent, html4BlockContent, html4PhrasingContent; - - function add(name, attributes, children) { - var ni, attributesOrder, element; - - function arrayToMap(array, obj) { - var map = {}, - i, l; - - for (i = 0, l = array.length; i < l; i++) { - map[array[i]] = obj || {}; - } - - return map; - } - - children = children || []; - attributes = attributes || ""; - - if (typeof children === "string") { - children = split(children); - } - - name = split(name); - ni = name.length; - while (ni--) { - attributesOrder = split([globalAttributes, attributes].join(' ')); - - element = { - attributes: arrayToMap(attributesOrder), - attributesOrder: attributesOrder, - children: arrayToMap(children, dummyObj) - }; - - schema[name[ni]] = element; - } - } - - function addAttrs(name, attributes) { - var ni, schemaItem, i, l; - - name = split(name); - ni = name.length; - attributes = split(attributes); - while (ni--) { - schemaItem = schema[name[ni]]; - for (i = 0, l = attributes.length; i < l; i++) { - schemaItem.attributes[attributes[i]] = {}; - schemaItem.attributesOrder.push(attributes[i]); - } - } - } - - // Use cached schema - if (mapCache[type]) { - return mapCache[type]; - } - - // Attributes present on all elements - globalAttributes = "id accesskey class dir lang style tabindex title role"; - - // Event attributes can be opt-in/opt-out - /*eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " + - "ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " + - "onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " + - "onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " + - "onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " + - "onwaiting" - );*/ - - // Block content elements - blockContent = - "address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"; - - // Phrasing content elements from the HTML5 spec (inline) - phrasingContent = - "a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd " + - "label map noscript object q s samp script select small span strong sub sup " + - "textarea u var #text #comment"; - - // Add HTML5 items to globalAttributes, blockContent, phrasingContent - if (type != "html4") { - globalAttributes += " contenteditable contextmenu draggable dropzone " + - "hidden spellcheck translate"; - blockContent += " article aside details dialog figure header footer hgroup section nav"; - phrasingContent += " audio canvas command datalist mark meter output picture " + - "progress time wbr video ruby bdi keygen"; - } - - // Add HTML4 elements unless it's html5-strict - if (type != "html5-strict") { - globalAttributes += " xml:lang"; - - html4PhrasingContent = "acronym applet basefont big font strike tt"; - phrasingContent = [phrasingContent, html4PhrasingContent].join(' '); - - each(split(html4PhrasingContent), function(name) { - add(name, "", phrasingContent); - }); - - html4BlockContent = "center dir isindex noframes"; - blockContent = [blockContent, html4BlockContent].join(' '); - - // Flow content elements from the HTML5 spec (block+inline) - flowContent = [blockContent, phrasingContent].join(' '); - - each(split(html4BlockContent), function(name) { - add(name, "", flowContent); - }); - } - - // Flow content elements from the HTML5 spec (block+inline) - flowContent = flowContent || [blockContent, phrasingContent].join(" "); - - // HTML4 base schema TODO: Move HTML5 specific attributes to HTML5 specific if statement - // Schema items <element name>, <specific attributes>, <children ..> - add("html", "manifest", "head body"); - add("head", "", "base command link meta noscript script style title"); - add("title hr noscript br"); - add("base", "href target"); - add("link", "href rel media hreflang type sizes hreflang"); - add("meta", "name http-equiv content charset"); - add("style", "media type scoped"); - add("script", "src async defer type charset"); - add("body", "onafterprint onbeforeprint onbeforeunload onblur onerror onfocus " + - "onhashchange onload onmessage onoffline ononline onpagehide onpageshow " + - "onpopstate onresize onscroll onstorage onunload", flowContent); - add("address dt dd div caption", "", flowContent); - add("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn", "", phrasingContent); - add("blockquote", "cite", flowContent); - add("ol", "reversed start type", "li"); - add("ul", "", "li"); - add("li", "value", flowContent); - add("dl", "", "dt dd"); - add("a", "href target rel media hreflang type", phrasingContent); - add("q", "cite", phrasingContent); - add("ins del", "cite datetime", flowContent); - add("img", "src sizes srcset alt usemap ismap width height"); - add("iframe", "src name width height", flowContent); - add("embed", "src type width height"); - add("object", "data type typemustmatch name usemap form width height", [flowContent, "param"].join(' ')); - add("param", "name value"); - add("map", "name", [flowContent, "area"].join(' ')); - add("area", "alt coords shape href target rel media hreflang type"); - add("table", "border", "caption colgroup thead tfoot tbody tr" + (type == "html4" ? " col" : "")); - add("colgroup", "span", "col"); - add("col", "span"); - add("tbody thead tfoot", "", "tr"); - add("tr", "", "td th"); - add("td", "colspan rowspan headers", flowContent); - add("th", "colspan rowspan headers scope abbr", flowContent); - add("form", "accept-charset action autocomplete enctype method name novalidate target", flowContent); - add("fieldset", "disabled form name", [flowContent, "legend"].join(' ')); - add("label", "form for", phrasingContent); - add("input", "accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate " + - "formtarget height list max maxlength min multiple name pattern readonly required size src step type value width" - ); - add("button", "disabled form formaction formenctype formmethod formnovalidate formtarget name type value", - type == "html4" ? flowContent : phrasingContent); - add("select", "disabled form multiple name required size", "option optgroup"); - add("optgroup", "disabled label", "option"); - add("option", "disabled label selected value"); - add("textarea", "cols dirname disabled form maxlength name readonly required rows wrap"); - add("menu", "type label", [flowContent, "li"].join(' ')); - add("noscript", "", flowContent); - - // Extend with HTML5 elements - if (type != "html4") { - add("wbr"); - add("ruby", "", [phrasingContent, "rt rp"].join(' ')); - add("figcaption", "", flowContent); - add("mark rt rp summary bdi", "", phrasingContent); - add("canvas", "width height", flowContent); - add("video", "src crossorigin poster preload autoplay mediagroup loop " + - "muted controls width height buffered", [flowContent, "track source"].join(' ')); - add("audio", "src crossorigin preload autoplay mediagroup loop muted controls " + - "buffered volume", [flowContent, "track source"].join(' ')); - add("picture", "", "img source"); - add("source", "src srcset type media sizes"); - add("track", "kind src srclang label default"); - add("datalist", "", [phrasingContent, "option"].join(' ')); - add("article section nav aside header footer", "", flowContent); - add("hgroup", "", "h1 h2 h3 h4 h5 h6"); - add("figure", "", [flowContent, "figcaption"].join(' ')); - add("time", "datetime", phrasingContent); - add("dialog", "open", flowContent); - add("command", "type label icon disabled checked radiogroup command"); - add("output", "for form name", phrasingContent); - add("progress", "value max", phrasingContent); - add("meter", "value min max low high optimum", phrasingContent); - add("details", "open", [flowContent, "summary"].join(' ')); - add("keygen", "autofocus challenge disabled form keytype name"); - } - - // Extend with HTML4 attributes unless it's html5-strict - if (type != "html5-strict") { - addAttrs("script", "language xml:space"); - addAttrs("style", "xml:space"); - addAttrs("object", "declare classid code codebase codetype archive standby align border hspace vspace"); - addAttrs("embed", "align name hspace vspace"); - addAttrs("param", "valuetype type"); - addAttrs("a", "charset name rev shape coords"); - addAttrs("br", "clear"); - addAttrs("applet", "codebase archive code object alt name width height align hspace vspace"); - addAttrs("img", "name longdesc align border hspace vspace"); - addAttrs("iframe", "longdesc frameborder marginwidth marginheight scrolling align"); - addAttrs("font basefont", "size color face"); - addAttrs("input", "usemap align"); - addAttrs("select", "onchange"); - addAttrs("textarea"); - addAttrs("h1 h2 h3 h4 h5 h6 div p legend caption", "align"); - addAttrs("ul", "type compact"); - addAttrs("li", "type"); - addAttrs("ol dl menu dir", "compact"); - addAttrs("pre", "width xml:space"); - addAttrs("hr", "align noshade size width"); - addAttrs("isindex", "prompt"); - addAttrs("table", "summary width frame rules cellspacing cellpadding align bgcolor"); - addAttrs("col", "width align char charoff valign"); - addAttrs("colgroup", "width align char charoff valign"); - addAttrs("thead", "align char charoff valign"); - addAttrs("tr", "align char charoff valign bgcolor"); - addAttrs("th", "axis align char charoff valign nowrap bgcolor width height"); - addAttrs("form", "accept"); - addAttrs("td", "abbr axis scope align char charoff valign nowrap bgcolor width height"); - addAttrs("tfoot", "align char charoff valign"); - addAttrs("tbody", "align char charoff valign"); - addAttrs("area", "nohref"); - addAttrs("body", "background bgcolor text link vlink alink"); - } - - // Extend with HTML5 attributes unless it's html4 - if (type != "html4") { - addAttrs("input button select textarea", "autofocus"); - addAttrs("input textarea", "placeholder"); - addAttrs("a", "download"); - addAttrs("link script img", "crossorigin"); - addAttrs("iframe", "sandbox seamless allowfullscreen"); // Excluded: srcdoc - } - - // Special: iframe, ruby, video, audio, label - - // Delete children of the same name from it's parent - // For example: form can't have a child of the name form - each(split('a form meter progress dfn'), function(name) { - if (schema[name]) { - delete schema[name].children[name]; - } - }); - - // Delete header, footer, sectioning and heading content descendants - /*each('dt th address', function(name) { - delete schema[name].children[name]; - });*/ - - // Caption can't have tables - delete schema.caption.children.table; - - // Delete scripts by default due to possible XSS - delete schema.script; - - // TODO: LI:s can only have value if parent is OL - - // TODO: Handle transparent elements - // a ins del canvas map - - mapCache[type] = schema; - - return schema; - } - - function compileElementMap(value, mode) { - var styles; - - if (value) { - styles = {}; - - if (typeof value == 'string') { - value = { - '*': value - }; - } - - // Convert styles into a rule list - each(value, function(value, key) { - styles[key] = styles[key.toUpperCase()] = mode == 'map' ? makeMap(value, /[, ]/) : explode(value, /[, ]/); - }); - } - - return styles; - } - - /** - * Constructs a new Schema instance. - * - * @constructor - * @method Schema - * @param {Object} settings Name/value settings object. - */ - return function(settings) { - var self = this, - elements = {}, - children = {}, - patternElements = [], - validStyles, invalidStyles, schemaItems; - var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap, validClasses; - var blockElementsMap, nonEmptyElementsMap, moveCaretBeforeOnEnterElementsMap, textBlockElementsMap, textInlineElementsMap; - var customElementsMap = {}, - specialElements = {}; - - // Creates an lookup table map object for the specified option or the default value - function createLookupTable(option, defaultValue, extendWith) { - var value = settings[option]; - - if (!value) { - // Get cached default map or make it if needed - value = mapCache[option]; - - if (!value) { - value = makeMap(defaultValue, ' ', makeMap(defaultValue.toUpperCase(), ' ')); - value = extend(value, extendWith); - - mapCache[option] = value; - } - } else { - // Create custom map - value = makeMap(value, /[, ]/, makeMap(value.toUpperCase(), /[, ]/)); - } - - return value; - } - - settings = settings || {}; - schemaItems = compileSchema(settings.schema); - - // Allow all elements and attributes if verify_html is set to false - if (settings.verify_html === false) { - settings.valid_elements = '*[*]'; - } - - validStyles = compileElementMap(settings.valid_styles); - invalidStyles = compileElementMap(settings.invalid_styles, 'map'); - validClasses = compileElementMap(settings.valid_classes, 'map'); - - // Setup map objects - whiteSpaceElementsMap = createLookupTable( - 'whitespace_elements', - 'pre script noscript style textarea video audio iframe object code' - ); - selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr'); - shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link ' + - 'meta param embed source wbr track'); - boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' + - 'noshade nowrap readonly selected autoplay loop controls'); - nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object ' + - 'script pre code', shortEndedElementsMap); - moveCaretBeforeOnEnterElementsMap = createLookupTable('move_caret_before_on_enter_elements', 'table', nonEmptyElementsMap); - textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' + - 'blockquote center dir fieldset header footer article section hgroup aside nav figure'); - blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' + - 'th tr td li ol ul caption dl dt dd noscript menu isindex option ' + - 'datalist select optgroup figcaption', textBlockElementsMap); - textInlineElementsMap = createLookupTable('text_inline_elements', 'span strong b em i font strike u var cite ' + - 'dfn code mark q sup sub samp'); - - each((settings.special || 'script noscript noframes noembed title style textarea xmp').split(' '), function(name) { - specialElements[name] = new RegExp('<\/' + name + '[^>]*>', 'gi'); - }); - - // Converts a wildcard expression string to a regexp for example *a will become /.*a/. - function patternToRegExp(str) { - return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$'); - } - - // Parses the specified valid_elements string and adds to the current rules - // This function is a bit hard to read since it's heavily optimized for speed - function addValidElements(validElements) { - var ei, el, ai, al, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder, - prefix, outputName, globalAttributes, globalAttributesOrder, key, value, - elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/, - attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/, - hasPatternsRegExp = /[*?+]/; - - if (validElements) { - // Split valid elements into an array with rules - validElements = split(validElements, ','); - - if (elements['@']) { - globalAttributes = elements['@'].attributes; - globalAttributesOrder = elements['@'].attributesOrder; - } - - // Loop all rules - for (ei = 0, el = validElements.length; ei < el; ei++) { - // Parse element rule - matches = elementRuleRegExp.exec(validElements[ei]); - if (matches) { - // Setup local names for matches - prefix = matches[1]; - elementName = matches[2]; - outputName = matches[3]; - attrData = matches[5]; - - // Create new attributes and attributesOrder - attributes = {}; - attributesOrder = []; - - // Create the new element - element = { - attributes: attributes, - attributesOrder: attributesOrder - }; - - // Padd empty elements prefix - if (prefix === '#') { - element.paddEmpty = true; - } - - // Remove empty elements prefix - if (prefix === '-') { - element.removeEmpty = true; - } - - if (matches[4] === '!') { - element.removeEmptyAttrs = true; - } - - // Copy attributes from global rule into current rule - if (globalAttributes) { - for (key in globalAttributes) { - attributes[key] = globalAttributes[key]; - } - - attributesOrder.push.apply(attributesOrder, globalAttributesOrder); - } - - // Attributes defined - if (attrData) { - attrData = split(attrData, '|'); - for (ai = 0, al = attrData.length; ai < al; ai++) { - matches = attrRuleRegExp.exec(attrData[ai]); - if (matches) { - attr = {}; - attrType = matches[1]; - attrName = matches[2].replace(/::/g, ':'); - prefix = matches[3]; - value = matches[4]; - - // Required - if (attrType === '!') { - element.attributesRequired = element.attributesRequired || []; - element.attributesRequired.push(attrName); - attr.required = true; - } - - // Denied from global - if (attrType === '-') { - delete attributes[attrName]; - attributesOrder.splice(inArray(attributesOrder, attrName), 1); - continue; - } - - // Default value - if (prefix) { - // Default value - if (prefix === '=') { - element.attributesDefault = element.attributesDefault || []; - element.attributesDefault.push({ - name: attrName, - value: value - }); - attr.defaultValue = value; - } - - // Forced value - if (prefix === ':') { - element.attributesForced = element.attributesForced || []; - element.attributesForced.push({ - name: attrName, - value: value - }); - attr.forcedValue = value; - } - - // Required values - if (prefix === '<') { - attr.validValues = makeMap(value, '?'); - } - } - - // Check for attribute patterns - if (hasPatternsRegExp.test(attrName)) { - element.attributePatterns = element.attributePatterns || []; - attr.pattern = patternToRegExp(attrName); - element.attributePatterns.push(attr); - } else { - // Add attribute to order list if it doesn't already exist - if (!attributes[attrName]) { - attributesOrder.push(attrName); - } - - attributes[attrName] = attr; - } - } - } - } - - // Global rule, store away these for later usage - if (!globalAttributes && elementName == '@') { - globalAttributes = attributes; - globalAttributesOrder = attributesOrder; - } - - // Handle substitute elements such as b/strong - if (outputName) { - element.outputName = elementName; - elements[outputName] = element; - } - - // Add pattern or exact element - if (hasPatternsRegExp.test(elementName)) { - element.pattern = patternToRegExp(elementName); - patternElements.push(element); - } else { - elements[elementName] = element; - } - } - } - } - } - - function setValidElements(validElements) { - elements = {}; - patternElements = []; - - addValidElements(validElements); - - each(schemaItems, function(element, name) { - children[name] = element.children; - }); - } - - // Adds custom non HTML elements to the schema - function addCustomElements(customElements) { - var customElementRegExp = /^(~)?(.+)$/; - - if (customElements) { - // Flush cached items since we are altering the default maps - mapCache.text_block_elements = mapCache.block_elements = null; - - each(split(customElements, ','), function(rule) { - var matches = customElementRegExp.exec(rule), - inline = matches[1] === '~', - cloneName = inline ? 'span' : 'div', - name = matches[2]; - - children[name] = children[cloneName]; - customElementsMap[name] = cloneName; - - // If it's not marked as inline then add it to valid block elements - if (!inline) { - blockElementsMap[name.toUpperCase()] = {}; - blockElementsMap[name] = {}; - } - - // Add elements clone if needed - if (!elements[name]) { - var customRule = elements[cloneName]; - - customRule = extend({}, customRule); - delete customRule.removeEmptyAttrs; - delete customRule.removeEmpty; - - elements[name] = customRule; - } - - // Add custom elements at span/div positions - each(children, function(element, elmName) { - if (element[cloneName]) { - children[elmName] = element = extend({}, children[elmName]); - element[name] = element[cloneName]; - } - }); - }); - } - } - - // Adds valid children to the schema object - function addValidChildren(validChildren) { - var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/; - - // Invalidate the schema cache if the schema is mutated - mapCache[settings.schema] = null; - - if (validChildren) { - each(split(validChildren, ','), function(rule) { - var matches = childRuleRegExp.exec(rule), - parent, prefix; - - if (matches) { - prefix = matches[1]; - - // Add/remove items from default - if (prefix) { - parent = children[matches[2]]; - } else { - parent = children[matches[2]] = { - '#comment': {} - }; - } - - parent = children[matches[2]]; - - each(split(matches[3], '|'), function(child) { - if (prefix === '-') { - delete parent[child]; - } else { - parent[child] = {}; - } - }); - } - }); - } - } - - function getElementRule(name) { - var element = elements[name], - i; - - // Exact match found - if (element) { - return element; - } - - // No exact match then try the patterns - i = patternElements.length; - while (i--) { - element = patternElements[i]; - - if (element.pattern.test(name)) { - return element; - } - } - } - - if (!settings.valid_elements) { - // No valid elements defined then clone the elements from the schema spec - each(schemaItems, function(element, name) { - elements[name] = { - attributes: element.attributes, - attributesOrder: element.attributesOrder - }; - - children[name] = element.children; - }); - - // Switch these on HTML4 - if (settings.schema != "html5") { - each(split('strong/b em/i'), function(item) { - item = split(item, '/'); - elements[item[1]].outputName = item[0]; - }); - } - - // Add default alt attribute for images, removed since alt="" is treated as presentational. - // elements.img.attributesDefault = [{name: 'alt', value: ''}]; - - // Remove these if they are empty by default - each(split('ol ul sub sup blockquote span font a table tbody tr strong em b i'), function(name) { - if (elements[name]) { - elements[name].removeEmpty = true; - } - }); - - // Padd these by default - each(split('p h1 h2 h3 h4 h5 h6 th td pre div address caption'), function(name) { - elements[name].paddEmpty = true; - }); - - // Remove these if they have no attributes - each(split('span'), function(name) { - elements[name].removeEmptyAttrs = true; - }); - - // Remove these by default - // TODO: Reenable in 4.1 - /*each(split('script style'), function(name) { - delete elements[name]; - });*/ - } else { - setValidElements(settings.valid_elements); - } - - addCustomElements(settings.custom_elements); - addValidChildren(settings.valid_children); - addValidElements(settings.extended_valid_elements); - - // Todo: Remove this when we fix list handling to be valid - addValidChildren('+ol[ul|ol],+ul[ul|ol]'); - - - // Some elements are not valid by themselves - require parents - each({ - dd: 'dl', - dt: 'dl', - li: 'ul ol', - td: 'tr', - th: 'tr', - tr: 'tbody thead tfoot', - tbody: 'table', - thead: 'table', - tfoot: 'table', - legend: 'fieldset', - area: 'map', - param: 'video audio object' - }, function(parents, item) { - if (elements[item]) { - elements[item].parentsRequired = split(parents); - } - }); - - - // Delete invalid elements - if (settings.invalid_elements) { - each(explode(settings.invalid_elements), function(item) { - if (elements[item]) { - delete elements[item]; - } - }); - } - - // If the user didn't allow span only allow internal spans - if (!getElementRule('span')) { - addValidElements('span[!data-mce-type|*]'); - } - - /** - * Name/value map object with valid parents and children to those parents. - * - * @example - * children = { - * div:{p:{}, h1:{}} - * }; - * @field children - * @type Object - */ - self.children = children; - - /** - * Name/value map object with valid styles for each element. - * - * @method getValidStyles - * @type Object - */ - self.getValidStyles = function() { - return validStyles; - }; - - /** - * Name/value map object with valid styles for each element. - * - * @method getInvalidStyles - * @type Object - */ - self.getInvalidStyles = function() { - return invalidStyles; - }; - - /** - * Name/value map object with valid classes for each element. - * - * @method getValidClasses - * @type Object - */ - self.getValidClasses = function() { - return validClasses; - }; - - /** - * Returns a map with boolean attributes. - * - * @method getBoolAttrs - * @return {Object} Name/value lookup map for boolean attributes. - */ - self.getBoolAttrs = function() { - return boolAttrMap; - }; - - /** - * Returns a map with block elements. - * - * @method getBlockElements - * @return {Object} Name/value lookup map for block elements. - */ - self.getBlockElements = function() { - return blockElementsMap; - }; - - /** - * Returns a map with text block elements. Such as: p,h1-h6,div,address - * - * @method getTextBlockElements - * @return {Object} Name/value lookup map for block elements. - */ - self.getTextBlockElements = function() { - return textBlockElementsMap; - }; - - /** - * Returns a map of inline text format nodes for example strong/span or ins. - * - * @method getTextInlineElements - * @return {Object} Name/value lookup map for text format elements. - */ - self.getTextInlineElements = function() { - return textInlineElementsMap; - }; - - /** - * Returns a map with short ended elements such as BR or IMG. - * - * @method getShortEndedElements - * @return {Object} Name/value lookup map for short ended elements. - */ - self.getShortEndedElements = function() { - return shortEndedElementsMap; - }; - - /** - * Returns a map with self closing tags such as <li>. - * - * @method getSelfClosingElements - * @return {Object} Name/value lookup map for self closing tags elements. - */ - self.getSelfClosingElements = function() { - return selfClosingElementsMap; - }; - - /** - * Returns a map with elements that should be treated as contents regardless if it has text - * content in them or not such as TD, VIDEO or IMG. - * - * @method getNonEmptyElements - * @return {Object} Name/value lookup map for non empty elements. - */ - self.getNonEmptyElements = function() { - return nonEmptyElementsMap; - }; - - /** - * Returns a map with elements that the caret should be moved in front of after enter is - * pressed - * - * @method getMoveCaretBeforeOnEnterElements - * @return {Object} Name/value lookup map for elements to place the caret in front of. - */ - self.getMoveCaretBeforeOnEnterElements = function() { - return moveCaretBeforeOnEnterElementsMap; - }; - - /** - * Returns a map with elements where white space is to be preserved like PRE or SCRIPT. - * - * @method getWhiteSpaceElements - * @return {Object} Name/value lookup map for white space elements. - */ - self.getWhiteSpaceElements = function() { - return whiteSpaceElementsMap; - }; - - /** - * Returns a map with special elements. These are elements that needs to be parsed - * in a special way such as script, style, textarea etc. The map object values - * are regexps used to find the end of the element. - * - * @method getSpecialElements - * @return {Object} Name/value lookup map for special elements. - */ - self.getSpecialElements = function() { - return specialElements; - }; - - /** - * Returns true/false if the specified element and it's child is valid or not - * according to the schema. - * - * @method isValidChild - * @param {String} name Element name to check for. - * @param {String} child Element child to verify. - * @return {Boolean} True/false if the element is a valid child of the specified parent. - */ - self.isValidChild = function(name, child) { - var parent = children[name.toLowerCase()]; - - return !!(parent && parent[child.toLowerCase()]); - }; - - /** - * Returns true/false if the specified element name and optional attribute is - * valid according to the schema. - * - * @method isValid - * @param {String} name Name of element to check. - * @param {String} attr Optional attribute name to check for. - * @return {Boolean} True/false if the element and attribute is valid. - */ - self.isValid = function(name, attr) { - var attrPatterns, i, rule = getElementRule(name); - - // Check if it's a valid element - if (rule) { - if (attr) { - // Check if attribute name exists - if (rule.attributes[attr]) { - return true; - } - - // Check if attribute matches a regexp pattern - attrPatterns = rule.attributePatterns; - if (attrPatterns) { - i = attrPatterns.length; - while (i--) { - if (attrPatterns[i].pattern.test(name)) { - return true; - } - } - } - } else { - return true; - } - } - - // No match - return false; - }; - - /** - * Returns true/false if the specified element is valid or not - * according to the schema. - * - * @method getElementRule - * @param {String} name Element name to check for. - * @return {Object} Element object or undefined if the element isn't valid. - */ - self.getElementRule = getElementRule; - - /** - * Returns an map object of all custom elements. - * - * @method getCustomElements - * @return {Object} Name/value map object of all custom elements. - */ - self.getCustomElements = function() { - return customElementsMap; - }; - - /** - * Parses a valid elements string and adds it to the schema. The valid elements - * format is for example "element[attr=default|otherattr]". - * Existing rules will be replaced with the ones specified, so this extends the schema. - * - * @method addValidElements - * @param {String} valid_elements String in the valid elements format to be parsed. - */ - self.addValidElements = addValidElements; - - /** - * Parses a valid elements string and sets it to the schema. The valid elements - * format is for example "element[attr=default|otherattr]". - * Existing rules will be replaced with the ones specified, so this extends the schema. - * - * @method setValidElements - * @param {String} valid_elements String in the valid elements format to be parsed. - */ - self.setValidElements = setValidElements; - - /** - * Adds custom non HTML elements to the schema. - * - * @method addCustomElements - * @param {String} custom_elements Comma separated list of custom elements to add. - */ - self.addCustomElements = addCustomElements; - - /** - * Parses a valid children string and adds them to the schema structure. The valid children - * format is for example: "element[child1|child2]". - * - * @method addValidChildren - * @param {String} valid_children Valid children elements string to parse - */ - self.addValidChildren = addValidChildren; - - self.elements = elements; - }; - } - ); - - /** - * DOMUtils.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Utility class for various DOM manipulation and retrieval functions. - * - * @class tinymce.dom.DOMUtils - * @example - * // Add a class to an element by id in the page - * tinymce.DOM.addClass('someid', 'someclass'); - * - * // Add a class to an element by id inside the editor - * tinymce.activeEditor.dom.addClass('someid', 'someclass'); - */ - define( - 'tinymce.core.dom.DOMUtils', [ - 'tinymce.core.dom.DomQuery', - 'tinymce.core.dom.EventUtils', - 'tinymce.core.dom.Range', - 'tinymce.core.dom.Sizzle', - 'tinymce.core.dom.StyleSheetLoader', - 'tinymce.core.dom.TreeWalker', - 'tinymce.core.Env', - 'tinymce.core.html.Entities', - 'tinymce.core.html.Schema', - 'tinymce.core.html.Styles', - 'tinymce.core.util.Tools' - ], - function(DomQuery, EventUtils, Range, Sizzle, StyleSheetLoader, TreeWalker, Env, Entities, Schema, Styles, Tools) { - // Shorten names - var each = Tools.each, - is = Tools.is, - grep = Tools.grep, - trim = Tools.trim; - var isIE = Env.ie; - var simpleSelectorRe = /^([a-z0-9],?)+$/i; - var whiteSpaceRegExp = /^[ \t\r\n]*$/; - - function setupAttrHooks(domUtils, settings) { - var attrHooks = {}, - keepValues = settings.keep_values, - keepUrlHook; - - keepUrlHook = { - set: function($elm, value, name) { - if (settings.url_converter) { - value = settings.url_converter.call(settings.url_converter_scope || domUtils, value, name, $elm[0]); - } - - $elm.attr('data-mce-' + name, value).attr(name, value); - }, - - get: function($elm, name) { - return $elm.attr('data-mce-' + name) || $elm.attr(name); - } - }; - - attrHooks = { - style: { - set: function($elm, value) { - if (value !== null && typeof value === 'object') { - $elm.css(value); - return; - } - - if (keepValues) { - $elm.attr('data-mce-style', value); - } - - $elm.attr('style', value); - }, - - get: function($elm) { - var value = $elm.attr('data-mce-style') || $elm.attr('style'); - - value = domUtils.serializeStyle(domUtils.parseStyle(value), $elm[0].nodeName); - - return value; - } - } - }; - - if (keepValues) { - attrHooks.href = attrHooks.src = keepUrlHook; - } - - return attrHooks; - } - - function updateInternalStyleAttr(domUtils, $elm) { - var value = $elm.attr('style'); - - value = domUtils.serializeStyle(domUtils.parseStyle(value), $elm[0].nodeName); - - if (!value) { - value = null; - } - - $elm.attr('data-mce-style', value); - } - - function nodeIndex(node, normalized) { - var idx = 0, - lastNodeType, nodeType; - - if (node) { - for (lastNodeType = node.nodeType, node = node.previousSibling; node; node = node.previousSibling) { - nodeType = node.nodeType; - - // Normalize text nodes - if (normalized && nodeType == 3) { - if (nodeType == lastNodeType || !node.nodeValue.length) { - continue; - } - } - idx++; - lastNodeType = nodeType; - } - } - - return idx; - } - - /** - * Constructs a new DOMUtils instance. Consult the Wiki for more details on settings etc for this class. - * - * @constructor - * @method DOMUtils - * @param {Document} doc Document reference to bind the utility class to. - * @param {settings} settings Optional settings collection. - */ - function DOMUtils(doc, settings) { - var self = this, - blockElementsMap; - - self.doc = doc; - self.win = window; - self.files = {}; - self.counter = 0; - self.stdMode = !isIE || doc.documentMode >= 8; - self.boxModel = !isIE || doc.compatMode == "CSS1Compat" || self.stdMode; - self.styleSheetLoader = new StyleSheetLoader(doc); - self.boundEvents = []; - self.settings = settings = settings || {}; - self.schema = settings.schema ? settings.schema : new Schema({}); - self.styles = new Styles({ - url_converter: settings.url_converter, - url_converter_scope: settings.url_converter_scope - }, settings.schema); - - self.fixDoc(doc); - self.events = settings.ownEvents ? new EventUtils(settings.proxy) : EventUtils.Event; - self.attrHooks = setupAttrHooks(self, settings); - blockElementsMap = settings.schema ? settings.schema.getBlockElements() : {}; - self.$ = DomQuery.overrideDefaults(function() { - return { - context: doc, - element: self.getRoot() - }; - }); - - /** - * Returns true/false if the specified element is a block element or not. - * - * @method isBlock - * @param {Node/String} node Element/Node to check. - * @return {Boolean} True/False state if the node is a block element or not. - */ - self.isBlock = function(node) { - // Fix for #5446 - if (!node) { - return false; - } - - // This function is called in module pattern style since it might be executed with the wrong this scope - var type = node.nodeType; - - // If it's a node then check the type and use the nodeName - if (type) { - return !!(type === 1 && blockElementsMap[node.nodeName]); - } - - return !!blockElementsMap[node]; - }; - } - - DOMUtils.prototype = { - $$: function(elm) { - if (typeof elm == 'string') { - elm = this.get(elm); - } - - return this.$(elm); - }, - - root: null, - - fixDoc: function(doc) { - var settings = this.settings, - name; - - if (isIE && settings.schema) { - // Add missing HTML 4/5 elements to IE - ('abbr article aside audio canvas ' + - 'details figcaption figure footer ' + - 'header hgroup mark menu meter nav ' + - 'output progress section summary ' + - 'time video').replace(/\w+/g, function(name) { - doc.createElement(name); - }); - - // Create all custom elements - for (name in settings.schema.getCustomElements()) { - doc.createElement(name); - } - } - }, - - clone: function(node, deep) { - var self = this, - clone, doc; - - // TODO: Add feature detection here in the future - if (!isIE || node.nodeType !== 1 || deep) { - return node.cloneNode(deep); - } - - doc = self.doc; - - // Make a HTML5 safe shallow copy - if (!deep) { - clone = doc.createElement(node.nodeName); - - // Copy attribs - each(self.getAttribs(node), function(attr) { - self.setAttrib(clone, attr.nodeName, self.getAttrib(node, attr.nodeName)); - }); - - return clone; - } - - return clone.firstChild; - }, - - /** - * Returns the root node of the document. This is normally the body but might be a DIV. Parents like getParent will not - * go above the point of this root node. - * - * @method getRoot - * @return {Element} Root element for the utility class. - */ - getRoot: function() { - var self = this; - - return self.settings.root_element || self.doc.body; - }, - - /** - * Returns the viewport of the window. - * - * @method getViewPort - * @param {Window} win Optional window to get viewport of. - * @return {Object} Viewport object with fields x, y, w and h. - */ - getViewPort: function(win) { - var doc, rootElm; - - win = !win ? this.win : win; - doc = win.document; - rootElm = this.boxModel ? doc.documentElement : doc.body; - - // Returns viewport size excluding scrollbars - return { - x: win.pageXOffset || rootElm.scrollLeft, - y: win.pageYOffset || rootElm.scrollTop, - w: win.innerWidth || rootElm.clientWidth, - h: win.innerHeight || rootElm.clientHeight - }; - }, - - /** - * Returns the rectangle for a specific element. - * - * @method getRect - * @param {Element/String} elm Element object or element ID to get rectangle from. - * @return {object} Rectangle for specified element object with x, y, w, h fields. - */ - getRect: function(elm) { - var self = this, - pos, size; - - elm = self.get(elm); - pos = self.getPos(elm); - size = self.getSize(elm); - - return { - x: pos.x, - y: pos.y, - w: size.w, - h: size.h - }; - }, - - /** - * Returns the size dimensions of the specified element. - * - * @method getSize - * @param {Element/String} elm Element object or element ID to get rectangle from. - * @return {object} Rectangle for specified element object with w, h fields. - */ - getSize: function(elm) { - var self = this, - w, h; - - elm = self.get(elm); - w = self.getStyle(elm, 'width'); - h = self.getStyle(elm, 'height'); - - // Non pixel value, then force offset/clientWidth - if (w.indexOf('px') === -1) { - w = 0; - } - - // Non pixel value, then force offset/clientWidth - if (h.indexOf('px') === -1) { - h = 0; - } - - return { - w: parseInt(w, 10) || elm.offsetWidth || elm.clientWidth, - h: parseInt(h, 10) || elm.offsetHeight || elm.clientHeight - }; - }, - - /** - * Returns a node by the specified selector function. This function will - * loop through all parent nodes and call the specified function for each node. - * If the function then returns true indicating that it has found what it was looking for, the loop execution will then end - * and the node it found will be returned. - * - * @method getParent - * @param {Node/String} node DOM node to search parents on or ID string. - * @param {function} selector Selection function or CSS selector to execute on each node. - * @param {Node} root Optional root element, never go beyond this point. - * @return {Node} DOM Node or null if it wasn't found. - */ - getParent: function(node, selector, root) { - return this.getParents(node, selector, root, false); - }, - - /** - * Returns a node list of all parents matching the specified selector function or pattern. - * If the function then returns true indicating that it has found what it was looking for and that node will be collected. - * - * @method getParents - * @param {Node/String} node DOM node to search parents on or ID string. - * @param {function} selector Selection function to execute on each node or CSS pattern. - * @param {Node} root Optional root element, never go beyond this point. - * @return {Array} Array of nodes or null if it wasn't found. - */ - getParents: function(node, selector, root, collect) { - var self = this, - selectorVal, result = []; - - node = self.get(node); - collect = collect === undefined; - - // Default root on inline mode - root = root || (self.getRoot().nodeName != 'BODY' ? self.getRoot().parentNode : null); - - // Wrap node name as func - if (is(selector, 'string')) { - selectorVal = selector; - - if (selector === '*') { - selector = function(node) { - return node.nodeType == 1; - }; - } else { - selector = function(node) { - return self.is(node, selectorVal); - }; - } - } - - while (node) { - if (node == root || !node.nodeType || node.nodeType === 9) { - break; - } - - if (!selector || selector(node)) { - if (collect) { - result.push(node); - } else { - return node; - } - } - - node = node.parentNode; - } - - return collect ? result : null; - }, - - /** - * Returns the specified element by ID or the input element if it isn't a string. - * - * @method get - * @param {String/Element} n Element id to look for or element to just pass though. - * @return {Element} Element matching the specified id or null if it wasn't found. - */ - get: function(elm) { - var name; - - if (elm && this.doc && typeof elm == 'string') { - name = elm; - elm = this.doc.getElementById(elm); - - // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick - if (elm && elm.id !== name) { - return this.doc.getElementsByName(name)[1]; - } - } - - return elm; - }, - - /** - * Returns the next node that matches selector or function - * - * @method getNext - * @param {Node} node Node to find siblings from. - * @param {String/function} selector Selector CSS expression or function. - * @return {Node} Next node item matching the selector or null if it wasn't found. - */ - getNext: function(node, selector) { - return this._findSib(node, selector, 'nextSibling'); - }, - - /** - * Returns the previous node that matches selector or function - * - * @method getPrev - * @param {Node} node Node to find siblings from. - * @param {String/function} selector Selector CSS expression or function. - * @return {Node} Previous node item matching the selector or null if it wasn't found. - */ - getPrev: function(node, selector) { - return this._findSib(node, selector, 'previousSibling'); - }, - - // #ifndef jquery - - /** - * Selects specific elements by a CSS level 3 pattern. For example "div#a1 p.test". - * This function is optimized for the most common patterns needed in TinyMCE but it also performs well enough - * on more complex patterns. - * - * @method select - * @param {String} selector CSS level 3 pattern to select/find elements by. - * @param {Object} scope Optional root element/scope element to search in. - * @return {Array} Array with all matched elements. - * @example - * // Adds a class to all paragraphs in the currently active editor - * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass'); - * - * // Adds a class to all spans that have the test class in the currently active editor - * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('span.test'), 'someclass') - */ - select: function(selector, scope) { - var self = this; - - /*eslint new-cap:0 */ - return Sizzle(selector, self.get(scope) || self.settings.root_element || self.doc, []); - }, - - /** - * Returns true/false if the specified element matches the specified css pattern. - * - * @method is - * @param {Node/NodeList} elm DOM node to match or an array of nodes to match. - * @param {String} selector CSS pattern to match the element against. - */ - is: function(elm, selector) { - var i; - - if (!elm) { - return false; - } - - // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance - if (elm.length === undefined) { - // Simple all selector - if (selector === '*') { - return elm.nodeType == 1; - } - - // Simple selector just elements - if (simpleSelectorRe.test(selector)) { - selector = selector.toLowerCase().split(/,/); - elm = elm.nodeName.toLowerCase(); - - for (i = selector.length - 1; i >= 0; i--) { - if (selector[i] == elm) { - return true; - } - } - - return false; - } - } - - // Is non element - if (elm.nodeType && elm.nodeType != 1) { - return false; - } - - var elms = elm.nodeType ? [elm] : elm; - - /*eslint new-cap:0 */ - return Sizzle(selector, elms[0].ownerDocument || elms[0], null, elms).length > 0; - }, - - // #endif - - /** - * Adds the specified element to another element or elements. - * - * @method add - * @param {String/Element/Array} parentElm Element id string, DOM node element or array of ids or elements to add to. - * @param {String/Element} name Name of new element to add or existing element to add. - * @param {Object} attrs Optional object collection with arguments to add to the new element(s). - * @param {String} html Optional inner HTML contents to add for each element. - * @param {Boolean} create Optional flag if the element should be created or added. - * @return {Element/Array} Element that got created, or an array of created elements if multiple input elements - * were passed in. - * @example - * // Adds a new paragraph to the end of the active editor - * tinymce.activeEditor.dom.add(tinymce.activeEditor.getBody(), 'p', {title: 'my title'}, 'Some content'); - */ - add: function(parentElm, name, attrs, html, create) { - var self = this; - - return this.run(parentElm, function(parentElm) { - var newElm; - - newElm = is(name, 'string') ? self.doc.createElement(name) : name; - self.setAttribs(newElm, attrs); - - if (html) { - if (html.nodeType) { - newElm.appendChild(html); - } else { - self.setHTML(newElm, html); - } - } - - return !create ? parentElm.appendChild(newElm) : newElm; - }); - }, - - /** - * Creates a new element. - * - * @method create - * @param {String} name Name of new element. - * @param {Object} attrs Optional object name/value collection with element attributes. - * @param {String} html Optional HTML string to set as inner HTML of the element. - * @return {Element} HTML DOM node element that got created. - * @example - * // Adds an element where the caret/selection is in the active editor - * var el = tinymce.activeEditor.dom.create('div', {id: 'test', 'class': 'myclass'}, 'some content'); - * tinymce.activeEditor.selection.setNode(el); - */ - create: function(name, attrs, html) { - return this.add(this.doc.createElement(name), name, attrs, html, 1); - }, - - /** - * Creates HTML string for element. The element will be closed unless an empty inner HTML string is passed in. - * - * @method createHTML - * @param {String} name Name of new element. - * @param {Object} attrs Optional object name/value collection with element attributes. - * @param {String} html Optional HTML string to set as inner HTML of the element. - * @return {String} String with new HTML element, for example: <a href="#">test</a>. - * @example - * // Creates a html chunk and inserts it at the current selection/caret location - * tinymce.activeEditor.selection.setContent(tinymce.activeEditor.dom.createHTML('a', {href: 'test.html'}, 'some line')); - */ - createHTML: function(name, attrs, html) { - var outHtml = '', - key; - - outHtml += '<' + name; - - for (key in attrs) { - if (attrs.hasOwnProperty(key) && attrs[key] !== null && typeof attrs[key] != 'undefined') { - outHtml += ' ' + key + '="' + this.encode(attrs[key]) + '"'; - } - } - - // A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime - if (typeof html != "undefined") { - return outHtml + '>' + html + '</' + name + '>'; - } - - return outHtml + ' />'; - }, - - /** - * Creates a document fragment out of the specified HTML string. - * - * @method createFragment - * @param {String} html Html string to create fragment from. - * @return {DocumentFragment} Document fragment node. - */ - createFragment: function(html) { - var frag, node, doc = this.doc, - container; - - container = doc.createElement("div"); - frag = doc.createDocumentFragment(); - - if (html) { - container.innerHTML = html; - } - - while ((node = container.firstChild)) { - frag.appendChild(node); - } - - return frag; - }, - - /** - * Removes/deletes the specified element(s) from the DOM. - * - * @method remove - * @param {String/Element/Array} node ID of element or DOM element object or array containing multiple elements/ids. - * @param {Boolean} keepChildren Optional state to keep children or not. If set to true all children will be - * placed at the location of the removed element. - * @return {Element/Array} HTML DOM element that got removed, or an array of removed elements if multiple input elements - * were passed in. - * @example - * // Removes all paragraphs in the active editor - * tinymce.activeEditor.dom.remove(tinymce.activeEditor.dom.select('p')); - * - * // Removes an element by id in the document - * tinymce.DOM.remove('mydiv'); - */ - remove: function(node, keepChildren) { - node = this.$$(node); - - if (keepChildren) { - node.each(function() { - var child; - - while ((child = this.firstChild)) { - if (child.nodeType == 3 && child.data.length === 0) { - this.removeChild(child); - } else { - this.parentNode.insertBefore(child, this); - } - } - }).remove(); - } else { - node.remove(); - } - - return node.length > 1 ? node.toArray() : node[0]; - }, - - /** - * Sets the CSS style value on a HTML element. The name can be a camelcase string - * or the CSS style name like background-color. - * - * @method setStyle - * @param {String/Element/Array} elm HTML element/Array of elements to set CSS style value on. - * @param {String} name Name of the style value to set. - * @param {String} value Value to set on the style. - * @example - * // Sets a style value on all paragraphs in the currently active editor - * tinymce.activeEditor.dom.setStyle(tinymce.activeEditor.dom.select('p'), 'background-color', 'red'); - * - * // Sets a style value to an element by id in the current document - * tinymce.DOM.setStyle('mydiv', 'background-color', 'red'); - */ - setStyle: function(elm, name, value) { - elm = this.$$(elm).css(name, value); - - if (this.settings.update_styles) { - updateInternalStyleAttr(this, elm); - } - }, - - /** - * Returns the current style or runtime/computed value of an element. - * - * @method getStyle - * @param {String/Element} elm HTML element or element id string to get style from. - * @param {String} name Style name to return. - * @param {Boolean} computed Computed style. - * @return {String} Current style or computed style value of an element. - */ - getStyle: function(elm, name, computed) { - elm = this.$$(elm); - - if (computed) { - return elm.css(name); - } - - // Camelcase it, if needed - name = name.replace(/-(\D)/g, function(a, b) { - return b.toUpperCase(); - }); - - if (name == 'float') { - name = Env.ie && Env.ie < 12 ? 'styleFloat' : 'cssFloat'; - } - - return elm[0] && elm[0].style ? elm[0].style[name] : undefined; - }, - - /** - * Sets multiple styles on the specified element(s). - * - * @method setStyles - * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set styles on. - * @param {Object} styles Name/Value collection of style items to add to the element(s). - * @example - * // Sets styles on all paragraphs in the currently active editor - * tinymce.activeEditor.dom.setStyles(tinymce.activeEditor.dom.select('p'), {'background-color': 'red', 'color': 'green'}); - * - * // Sets styles to an element by id in the current document - * tinymce.DOM.setStyles('mydiv', {'background-color': 'red', 'color': 'green'}); - */ - setStyles: function(elm, styles) { - elm = this.$$(elm).css(styles); - - if (this.settings.update_styles) { - updateInternalStyleAttr(this, elm); - } - }, - - /** - * Removes all attributes from an element or elements. - * - * @method removeAllAttribs - * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to remove attributes from. - */ - removeAllAttribs: function(e) { - return this.run(e, function(e) { - var i, attrs = e.attributes; - for (i = attrs.length - 1; i >= 0; i--) { - e.removeAttributeNode(attrs.item(i)); - } - }); - }, - - /** - * Sets the specified attribute of an element or elements. - * - * @method setAttrib - * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attribute on. - * @param {String} name Name of attribute to set. - * @param {String} value Value to set on the attribute - if this value is falsy like null, 0 or '' it will remove - * the attribute instead. - * @example - * // Sets class attribute on all paragraphs in the active editor - * tinymce.activeEditor.dom.setAttrib(tinymce.activeEditor.dom.select('p'), 'class', 'myclass'); - * - * // Sets class attribute on a specific element in the current page - * tinymce.dom.setAttrib('mydiv', 'class', 'myclass'); - */ - setAttrib: function(elm, name, value) { - var self = this, - originalValue, hook, settings = self.settings; - - if (value === '') { - value = null; - } - - elm = self.$$(elm); - originalValue = elm.attr(name); - - if (!elm.length) { - return; - } - - hook = self.attrHooks[name]; - if (hook && hook.set) { - hook.set(elm, value, name); - } else { - elm.attr(name, value); - } - - if (originalValue != value && settings.onSetAttrib) { - settings.onSetAttrib({ - attrElm: elm, - attrName: name, - attrValue: value - }); - } - }, - - /** - * Sets two or more specified attributes of an element or elements. - * - * @method setAttribs - * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attributes on. - * @param {Object} attrs Name/Value collection of attribute items to add to the element(s). - * @example - * // Sets class and title attributes on all paragraphs in the active editor - * tinymce.activeEditor.dom.setAttribs(tinymce.activeEditor.dom.select('p'), {'class': 'myclass', title: 'some title'}); - * - * // Sets class and title attributes on a specific element in the current page - * tinymce.DOM.setAttribs('mydiv', {'class': 'myclass', title: 'some title'}); - */ - setAttribs: function(elm, attrs) { - var self = this; - - self.$$(elm).each(function(i, node) { - each(attrs, function(value, name) { - self.setAttrib(node, name, value); - }); - }); - }, - - /** - * Returns the specified attribute by name. - * - * @method getAttrib - * @param {String/Element} elm Element string id or DOM element to get attribute from. - * @param {String} name Name of attribute to get. - * @param {String} defaultVal Optional default value to return if the attribute didn't exist. - * @return {String} Attribute value string, default value or null if the attribute wasn't found. - */ - getAttrib: function(elm, name, defaultVal) { - var self = this, - hook, value; - - elm = self.$$(elm); - - if (elm.length) { - hook = self.attrHooks[name]; - - if (hook && hook.get) { - value = hook.get(elm, name); - } else { - value = elm.attr(name); - } - } - - if (typeof value == 'undefined') { - value = defaultVal || ''; - } - - return value; - }, - - /** - * Returns the absolute x, y position of a node. The position will be returned in an object with x, y fields. - * - * @method getPos - * @param {Element/String} elm HTML element or element id to get x, y position from. - * @param {Element} rootElm Optional root element to stop calculations at. - * @return {object} Absolute position of the specified element object with x, y fields. - */ - getPos: function(elm, rootElm) { - var self = this, - x = 0, - y = 0, - offsetParent, doc = self.doc, - body = doc.body, - pos; - - elm = self.get(elm); - rootElm = rootElm || body; - - if (elm) { - // Use getBoundingClientRect if it exists since it's faster than looping offset nodes - // Fallback to offsetParent calculations if the body isn't static better since it stops at the body root - if (rootElm === body && elm.getBoundingClientRect && DomQuery(body).css('position') === 'static') { - pos = elm.getBoundingClientRect(); - rootElm = self.boxModel ? doc.documentElement : body; - - // Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit - // Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position - x = pos.left + (doc.documentElement.scrollLeft || body.scrollLeft) - rootElm.clientLeft; - y = pos.top + (doc.documentElement.scrollTop || body.scrollTop) - rootElm.clientTop; - - return { - x: x, - y: y - }; - } - - offsetParent = elm; - while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) { - x += offsetParent.offsetLeft || 0; - y += offsetParent.offsetTop || 0; - offsetParent = offsetParent.offsetParent; - } - - offsetParent = elm.parentNode; - while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) { - x -= offsetParent.scrollLeft || 0; - y -= offsetParent.scrollTop || 0; - offsetParent = offsetParent.parentNode; - } - } - - return { - x: x, - y: y - }; - }, - - /** - * Parses the specified style value into an object collection. This parser will also - * merge and remove any redundant items that browsers might have added. It will also convert non-hex - * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings. - * - * @method parseStyle - * @param {String} cssText Style value to parse, for example: border:1px solid red;. - * @return {Object} Object representation of that style, for example: {border: '1px solid red'} - */ - parseStyle: function(cssText) { - return this.styles.parse(cssText); - }, - - /** - * Serializes the specified style object into a string. - * - * @method serializeStyle - * @param {Object} styles Object to serialize as string, for example: {border: '1px solid red'} - * @param {String} name Optional element name. - * @return {String} String representation of the style object, for example: border: 1px solid red. - */ - serializeStyle: function(styles, name) { - return this.styles.serialize(styles, name); - }, - - /** - * Adds a style element at the top of the document with the specified cssText content. - * - * @method addStyle - * @param {String} cssText CSS Text style to add to top of head of document. - */ - addStyle: function(cssText) { - var self = this, - doc = self.doc, - head, styleElm; - - // Prevent inline from loading the same styles twice - if (self !== DOMUtils.DOM && doc === document) { - var addedStyles = DOMUtils.DOM.addedStyles; - - addedStyles = addedStyles || []; - if (addedStyles[cssText]) { - return; - } - - addedStyles[cssText] = true; - DOMUtils.DOM.addedStyles = addedStyles; - } - - // Create style element if needed - styleElm = doc.getElementById('mceDefaultStyles'); - if (!styleElm) { - styleElm = doc.createElement('style'); - styleElm.id = 'mceDefaultStyles'; - styleElm.type = 'text/css'; - - head = doc.getElementsByTagName('head')[0]; - if (head.firstChild) { - head.insertBefore(styleElm, head.firstChild); - } else { - head.appendChild(styleElm); - } - } - - // Append style data to old or new style element - if (styleElm.styleSheet) { - styleElm.styleSheet.cssText += cssText; - } else { - styleElm.appendChild(doc.createTextNode(cssText)); - } - }, - - /** - * Imports/loads the specified CSS file into the document bound to the class. - * - * @method loadCSS - * @param {String} url URL to CSS file to load. - * @example - * // Loads a CSS file dynamically into the current document - * tinymce.DOM.loadCSS('somepath/some.css'); - * - * // Loads a CSS file into the currently active editor instance - * tinymce.activeEditor.dom.loadCSS('somepath/some.css'); - * - * // Loads a CSS file into an editor instance by id - * tinymce.get('someid').dom.loadCSS('somepath/some.css'); - * - * // Loads multiple CSS files into the current document - * tinymce.DOM.loadCSS('somepath/some.css,somepath/someother.css'); - */ - loadCSS: function(url) { - var self = this, - doc = self.doc, - head; - - // Prevent inline from loading the same CSS file twice - if (self !== DOMUtils.DOM && doc === document) { - DOMUtils.DOM.loadCSS(url); - return; - } - - if (!url) { - url = ''; - } - - head = doc.getElementsByTagName('head')[0]; - - each(url.split(','), function(url) { - var link; - - url = Tools._addCacheSuffix(url); - - if (self.files[url]) { - return; - } - - self.files[url] = true; - link = self.create('link', { - rel: 'stylesheet', - href: url - }); - - // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug - // This fix seems to resolve that issue by recalcing the document once a stylesheet finishes loading - // It's ugly but it seems to work fine. - if (isIE && doc.documentMode && doc.recalc) { - link.onload = function() { - if (doc.recalc) { - doc.recalc(); - } - - link.onload = null; - }; - } - - head.appendChild(link); - }); - }, - - /** - * Adds a class to the specified element or elements. - * - * @method addClass - * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs. - * @param {String} cls Class name to add to each element. - * @return {String/Array} String with new class value or array with new class values for all elements. - * @example - * // Adds a class to all paragraphs in the active editor - * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'myclass'); - * - * // Adds a class to a specific element in the current page - * tinymce.DOM.addClass('mydiv', 'myclass'); - */ - addClass: function(elm, cls) { - this.$$(elm).addClass(cls); - }, - - /** - * Removes a class from the specified element or elements. - * - * @method removeClass - * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs. - * @param {String} cls Class name to remove from each element. - * @return {String/Array} String of remaining class name(s), or an array of strings if multiple input elements - * were passed in. - * @example - * // Removes a class from all paragraphs in the active editor - * tinymce.activeEditor.dom.removeClass(tinymce.activeEditor.dom.select('p'), 'myclass'); - * - * // Removes a class from a specific element in the current page - * tinymce.DOM.removeClass('mydiv', 'myclass'); - */ - removeClass: function(elm, cls) { - this.toggleClass(elm, cls, false); - }, - - /** - * Returns true if the specified element has the specified class. - * - * @method hasClass - * @param {String/Element} elm HTML element or element id string to check CSS class on. - * @param {String} cls CSS class to check for. - * @return {Boolean} true/false if the specified element has the specified class. - */ - hasClass: function(elm, cls) { - return this.$$(elm).hasClass(cls); - }, - - /** - * Toggles the specified class on/off. - * - * @method toggleClass - * @param {Element} elm Element to toggle class on. - * @param {[type]} cls Class to toggle on/off. - * @param {[type]} state Optional state to set. - */ - toggleClass: function(elm, cls, state) { - this.$$(elm).toggleClass(cls, state).each(function() { - if (this.className === '') { - DomQuery(this).attr('class', null); - } - }); - }, - - /** - * Shows the specified element(s) by ID by setting the "display" style. - * - * @method show - * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to show. - */ - show: function(elm) { - this.$$(elm).show(); - }, - - /** - * Hides the specified element(s) by ID by setting the "display" style. - * - * @method hide - * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to hide. - * @example - * // Hides an element by id in the document - * tinymce.DOM.hide('myid'); - */ - hide: function(elm) { - this.$$(elm).hide(); - }, - - /** - * Returns true/false if the element is hidden or not by checking the "display" style. - * - * @method isHidden - * @param {String/Element} elm Id or element to check display state on. - * @return {Boolean} true/false if the element is hidden or not. - */ - isHidden: function(elm) { - return this.$$(elm).css('display') == 'none'; - }, - - /** - * Returns a unique id. This can be useful when generating elements on the fly. - * This method will not check if the element already exists. - * - * @method uniqueId - * @param {String} prefix Optional prefix to add in front of all ids - defaults to "mce_". - * @return {String} Unique id. - */ - uniqueId: function(prefix) { - return (!prefix ? 'mce_' : prefix) + (this.counter++); - }, - - /** - * Sets the specified HTML content inside the element or elements. The HTML will first be processed. This means - * URLs will get converted, hex color values fixed etc. Check processHTML for details. - * - * @method setHTML - * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set HTML inside of. - * @param {String} html HTML content to set as inner HTML of the element. - * @example - * // Sets the inner HTML of all paragraphs in the active editor - * tinymce.activeEditor.dom.setHTML(tinymce.activeEditor.dom.select('p'), 'some inner html'); - * - * // Sets the inner HTML of an element by id in the document - * tinymce.DOM.setHTML('mydiv', 'some inner html'); - */ - setHTML: function(elm, html) { - elm = this.$$(elm); - - if (isIE) { - elm.each(function(i, target) { - if (target.canHaveHTML === false) { - return; - } - - // Remove all child nodes, IE keeps empty text nodes in DOM - while (target.firstChild) { - target.removeChild(target.firstChild); - } - - try { - // IE will remove comments from the beginning - // unless you padd the contents with something - target.innerHTML = '<br>' + html; - target.removeChild(target.firstChild); - } catch (ex) { - // IE sometimes produces an unknown runtime error on innerHTML if it's a div inside a p - DomQuery('<div></div>').html('<br>' + html).contents().slice(1).appendTo(target); - } - - return html; - }); - } else { - elm.html(html); - } - }, - - /** - * Returns the outer HTML of an element. - * - * @method getOuterHTML - * @param {String/Element} elm Element ID or element object to get outer HTML from. - * @return {String} Outer HTML string. - * @example - * tinymce.DOM.getOuterHTML(editorElement); - * tinymce.activeEditor.getOuterHTML(tinymce.activeEditor.getBody()); - */ - getOuterHTML: function(elm) { - elm = this.get(elm); - - // Older FF doesn't have outerHTML 3.6 is still used by some orgaizations - return elm.nodeType == 1 && "outerHTML" in elm ? elm.outerHTML : DomQuery('<div></div>').append(DomQuery(elm).clone()).html(); - }, - - /** - * Sets the specified outer HTML on an element or elements. - * - * @method setOuterHTML - * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set outer HTML on. - * @param {Object} html HTML code to set as outer value for the element. - * @example - * // Sets the outer HTML of all paragraphs in the active editor - * tinymce.activeEditor.dom.setOuterHTML(tinymce.activeEditor.dom.select('p'), '<div>some html</div>'); - * - * // Sets the outer HTML of an element by id in the document - * tinymce.DOM.setOuterHTML('mydiv', '<div>some html</div>'); - */ - setOuterHTML: function(elm, html) { - var self = this; - - self.$$(elm).each(function() { - try { - // Older FF doesn't have outerHTML 3.6 is still used by some organizations - if ("outerHTML" in this) { - this.outerHTML = html; - return; - } - } catch (ex) { - // Ignore - } - - // OuterHTML for IE it sometimes produces an "unknown runtime error" - self.remove(DomQuery(this).html(html), true); - }); - }, - - /** - * Entity decodes a string. This method decodes any HTML entities, such as å. - * - * @method decode - * @param {String} s String to decode entities on. - * @return {String} Entity decoded string. - */ - decode: Entities.decode, - - /** - * Entity encodes a string. This method encodes the most common entities, such as <>"&. - * - * @method encode - * @param {String} text String to encode with entities. - * @return {String} Entity encoded string. - */ - encode: Entities.encodeAllRaw, - - /** - * Inserts an element after the reference element. - * - * @method insertAfter - * @param {Element} node Element to insert after the reference. - * @param {Element/String/Array} referenceNode Reference element, element id or array of elements to insert after. - * @return {Element/Array} Element that got added or an array with elements. - */ - insertAfter: function(node, referenceNode) { - referenceNode = this.get(referenceNode); - - return this.run(node, function(node) { - var parent, nextSibling; - - parent = referenceNode.parentNode; - nextSibling = referenceNode.nextSibling; - - if (nextSibling) { - parent.insertBefore(node, nextSibling); - } else { - parent.appendChild(node); - } - - return node; - }); - }, - - /** - * Replaces the specified element or elements with the new element specified. The new element will - * be cloned if multiple input elements are passed in. - * - * @method replace - * @param {Element} newElm New element to replace old ones with. - * @param {Element/String/Array} oldElm Element DOM node, element id or array of elements or ids to replace. - * @param {Boolean} keepChildren Optional keep children state, if set to true child nodes from the old object will be added - * to new ones. - */ - replace: function(newElm, oldElm, keepChildren) { - var self = this; - - return self.run(oldElm, function(oldElm) { - if (is(oldElm, 'array')) { - newElm = newElm.cloneNode(true); - } - - if (keepChildren) { - each(grep(oldElm.childNodes), function(node) { - newElm.appendChild(node); - }); - } - - return oldElm.parentNode.replaceChild(newElm, oldElm); - }); - }, - - /** - * Renames the specified element and keeps its attributes and children. - * - * @method rename - * @param {Element} elm Element to rename. - * @param {String} name Name of the new element. - * @return {Element} New element or the old element if it needed renaming. - */ - rename: function(elm, name) { - var self = this, - newElm; - - if (elm.nodeName != name.toUpperCase()) { - // Rename block element - newElm = self.create(name); - - // Copy attribs to new block - each(self.getAttribs(elm), function(attrNode) { - self.setAttrib(newElm, attrNode.nodeName, self.getAttrib(elm, attrNode.nodeName)); - }); - - // Replace block - self.replace(newElm, elm, 1); - } - - return newElm || elm; - }, - - /** - * Find the common ancestor of two elements. This is a shorter method than using the DOM Range logic. - * - * @method findCommonAncestor - * @param {Element} a Element to find common ancestor of. - * @param {Element} b Element to find common ancestor of. - * @return {Element} Common ancestor element of the two input elements. - */ - findCommonAncestor: function(a, b) { - var ps = a, - pe; - - while (ps) { - pe = b; - - while (pe && ps != pe) { - pe = pe.parentNode; - } - - if (ps == pe) { - break; - } - - ps = ps.parentNode; - } - - if (!ps && a.ownerDocument) { - return a.ownerDocument.documentElement; - } - - return ps; - }, - - /** - * Parses the specified RGB color value and returns a hex version of that color. - * - * @method toHex - * @param {String} rgbVal RGB string value like rgb(1,2,3) - * @return {String} Hex version of that RGB value like #FF00FF. - */ - toHex: function(rgbVal) { - return this.styles.toHex(Tools.trim(rgbVal)); - }, - - /** - * Executes the specified function on the element by id or dom element node or array of elements/id. - * - * @method run - * @param {String/Element/Array} elm ID or DOM element object or array with ids or elements. - * @param {function} func Function to execute for each item. - * @param {Object} scope Optional scope to execute the function in. - * @return {Object/Array} Single object, or an array of objects if multiple input elements were passed in. - */ - run: function(elm, func, scope) { - var self = this, - result; - - if (typeof elm === 'string') { - elm = self.get(elm); - } - - if (!elm) { - return false; - } - - scope = scope || this; - if (!elm.nodeType && (elm.length || elm.length === 0)) { - result = []; - - each(elm, function(elm, i) { - if (elm) { - if (typeof elm == 'string') { - elm = self.get(elm); - } - - result.push(func.call(scope, elm, i)); - } - }); - - return result; - } - - return func.call(scope, elm); - }, - - /** - * Returns a NodeList with attributes for the element. - * - * @method getAttribs - * @param {HTMLElement/string} elm Element node or string id to get attributes from. - * @return {NodeList} NodeList with attributes. - */ - getAttribs: function(elm) { - var attrs; - - elm = this.get(elm); - - if (!elm) { - return []; - } - - if (isIE) { - attrs = []; - - // Object will throw exception in IE - if (elm.nodeName == 'OBJECT') { - return elm.attributes; - } - - // IE doesn't keep the selected attribute if you clone option elements - if (elm.nodeName === 'OPTION' && this.getAttrib(elm, 'selected')) { - attrs.push({ - specified: 1, - nodeName: 'selected' - }); - } - - // It's crazy that this is faster in IE but it's because it returns all attributes all the time - var attrRegExp = /<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi; - elm.cloneNode(false).outerHTML.replace(attrRegExp, '').replace(/[\w:\-]+/gi, function(a) { - attrs.push({ - specified: 1, - nodeName: a - }); - }); - - return attrs; - } - - return elm.attributes; - }, - - /** - * Returns true/false if the specified node is to be considered empty or not. - * - * @example - * tinymce.DOM.isEmpty(node, {img: true}); - * @method isEmpty - * @param {Object} elements Optional name/value object with elements that are automatically treated as non-empty elements. - * @return {Boolean} true/false if the node is empty or not. - */ - isEmpty: function(node, elements) { - var self = this, - i, attributes, type, whitespace, walker, name, brCount = 0; - - node = node.firstChild; - if (node) { - walker = new TreeWalker(node, node.parentNode); - elements = elements || (self.schema ? self.schema.getNonEmptyElements() : null); - whitespace = self.schema ? self.schema.getWhiteSpaceElements() : {}; - - do { - type = node.nodeType; - - if (type === 1) { - // Ignore bogus elements - var bogusVal = node.getAttribute('data-mce-bogus'); - if (bogusVal) { - node = walker.next(bogusVal === 'all'); - continue; - } - - // Keep empty elements like <img /> - name = node.nodeName.toLowerCase(); - if (elements && elements[name]) { - // Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p> - if (name === 'br') { - brCount++; - node = walker.next(); - continue; - } - - return false; - } - - // Keep elements with data-bookmark attributes or name attribute like <a name="1"></a> - attributes = self.getAttribs(node); - i = attributes.length; - while (i--) { - name = attributes[i].nodeName; - if (name === "name" || name === 'data-mce-bookmark') { - return false; - } - } - } - - // Keep comment nodes - if (type == 8) { - return false; - } - - // Keep non whitespace text nodes - if (type === 3 && !whiteSpaceRegExp.test(node.nodeValue)) { - return false; - } - - // Keep whitespace preserve elements - if (type === 3 && node.parentNode && whitespace[node.parentNode.nodeName] && whiteSpaceRegExp.test(node.nodeValue)) { - return false; - } - - node = walker.next(); - } while (node); - } - - return brCount <= 1; - }, - - /** - * Creates a new DOM Range object. This will use the native DOM Range API if it's - * available. If it's not, it will fall back to the custom TinyMCE implementation. - * - * @method createRng - * @return {DOMRange} DOM Range object. - * @example - * var rng = tinymce.DOM.createRng(); - * alert(rng.startContainer + "," + rng.startOffset); - */ - createRng: function() { - var doc = this.doc; - - return doc.createRange ? doc.createRange() : new Range(this); - }, - - /** - * Returns the index of the specified node within its parent. - * - * @method nodeIndex - * @param {Node} node Node to look for. - * @param {boolean} normalized Optional true/false state if the index is what it would be after a normalization. - * @return {Number} Index of the specified node. - */ - nodeIndex: nodeIndex, - - /** - * Splits an element into two new elements and places the specified split - * element or elements between the new ones. For example splitting the paragraph at the bold element in - * this example <p>abc<b>abc</b>123</p> would produce <p>abc</p><b>abc</b><p>123</p>. - * - * @method split - * @param {Element} parentElm Parent element to split. - * @param {Element} splitElm Element to split at. - * @param {Element} replacementElm Optional replacement element to replace the split element with. - * @return {Element} Returns the split element or the replacement element if that is specified. - */ - split: function(parentElm, splitElm, replacementElm) { - var self = this, - r = self.createRng(), - bef, aft, pa; - - // W3C valid browsers tend to leave empty nodes to the left/right side of the contents - this makes sense - // but we don't want that in our code since it serves no purpose for the end user - // For example splitting this html at the bold element: - // <p>text 1<span><b>CHOP</b></span>text 2</p> - // would produce: - // <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p> - // this function will then trim off empty edges and produce: - // <p>text 1</p><b>CHOP</b><p>text 2</p> - function trimNode(node) { - var i, children = node.childNodes, - type = node.nodeType; - - function surroundedBySpans(node) { - var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN'; - var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN'; - return previousIsSpan && nextIsSpan; - } - - if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark') { - return; - } - - for (i = children.length - 1; i >= 0; i--) { - trimNode(children[i]); - } - - if (type != 9) { - // Keep non whitespace text nodes - if (type == 3 && node.nodeValue.length > 0) { - // If parent element isn't a block or there isn't any useful contents for example "<p> </p>" - // Also keep text nodes with only spaces if surrounded by spans. - // eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b - var trimmedLength = trim(node.nodeValue).length; - if (!self.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength === 0 && surroundedBySpans(node)) { - return; - } - } else if (type == 1) { - // If the only child is a bookmark then move it up - children = node.childNodes; - - // TODO fix this complex if - if (children.length == 1 && children[0] && children[0].nodeType == 1 && - children[0].getAttribute('data-mce-type') == 'bookmark') { - node.parentNode.insertBefore(children[0], node); - } - - // Keep non empty elements or img, hr etc - if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName)) { - return; - } - } - - self.remove(node); - } - - return node; - } - - if (parentElm && splitElm) { - // Get before chunk - r.setStart(parentElm.parentNode, self.nodeIndex(parentElm)); - r.setEnd(splitElm.parentNode, self.nodeIndex(splitElm)); - bef = r.extractContents(); - - // Get after chunk - r = self.createRng(); - r.setStart(splitElm.parentNode, self.nodeIndex(splitElm) + 1); - r.setEnd(parentElm.parentNode, self.nodeIndex(parentElm) + 1); - aft = r.extractContents(); - - // Insert before chunk - pa = parentElm.parentNode; - pa.insertBefore(trimNode(bef), parentElm); - - // Insert middle chunk - if (replacementElm) { - pa.insertBefore(replacementElm, parentElm); - //pa.replaceChild(replacementElm, splitElm); - } else { - pa.insertBefore(splitElm, parentElm); - } - - // Insert after chunk - pa.insertBefore(trimNode(aft), parentElm); - self.remove(parentElm); - - return replacementElm || splitElm; - } - }, - - /** - * Adds an event handler to the specified object. - * - * @method bind - * @param {Element/Document/Window/Array} target Target element to bind events to. - * handler to or an array of elements/ids/documents. - * @param {String} name Name of event handler to add, for example: click. - * @param {function} func Function to execute when the event occurs. - * @param {Object} scope Optional scope to execute the function in. - * @return {function} Function callback handler the same as the one passed in. - */ - bind: function(target, name, func, scope) { - var self = this; - - if (Tools.isArray(target)) { - var i = target.length; - - while (i--) { - target[i] = self.bind(target[i], name, func, scope); - } - - return target; - } - - // Collect all window/document events bound by editor instance - if (self.settings.collect && (target === self.doc || target === self.win)) { - self.boundEvents.push([target, name, func, scope]); - } - - return self.events.bind(target, name, func, scope || self); - }, - - /** - * Removes the specified event handler by name and function from an element or collection of elements. - * - * @method unbind - * @param {Element/Document/Window/Array} target Target element to unbind events on. - * @param {String} name Event handler name, for example: "click" - * @param {function} func Function to remove. - * @return {bool/Array} Bool state of true if the handler was removed, or an array of states if multiple input elements - * were passed in. - */ - unbind: function(target, name, func) { - var self = this, - i; - - if (Tools.isArray(target)) { - i = target.length; - - while (i--) { - target[i] = self.unbind(target[i], name, func); - } - - return target; - } - - // Remove any bound events matching the input - if (self.boundEvents && (target === self.doc || target === self.win)) { - i = self.boundEvents.length; - - while (i--) { - var item = self.boundEvents[i]; - - if (target == item[0] && (!name || name == item[1]) && (!func || func == item[2])) { - this.events.unbind(item[0], item[1], item[2]); - } - } - } - - return this.events.unbind(target, name, func); - }, - - /** - * Fires the specified event name with object on target. - * - * @method fire - * @param {Node/Document/Window} target Target element or object to fire event on. - * @param {String} name Name of the event to fire. - * @param {Object} evt Event object to send. - * @return {Event} Event object. - */ - fire: function(target, name, evt) { - return this.events.fire(target, name, evt); - }, - - // Returns the content editable state of a node - getContentEditable: function(node) { - var contentEditable; - - // Check type - if (!node || node.nodeType != 1) { - return null; - } - - // Check for fake content editable - contentEditable = node.getAttribute("data-mce-contenteditable"); - if (contentEditable && contentEditable !== "inherit") { - return contentEditable; - } - - // Check for real content editable - return node.contentEditable !== "inherit" ? node.contentEditable : null; - }, - - getContentEditableParent: function(node) { - var root = this.getRoot(), - state = null; - - for (; node && node !== root; node = node.parentNode) { - state = this.getContentEditable(node); - - if (state !== null) { - break; - } - } - - return state; - }, - - /** - * Destroys all internal references to the DOM to solve IE leak issues. - * - * @method destroy - */ - destroy: function() { - var self = this; - - // Unbind all events bound to window/document by editor instance - if (self.boundEvents) { - var i = self.boundEvents.length; - - while (i--) { - var item = self.boundEvents[i]; - this.events.unbind(item[0], item[1], item[2]); - } - - self.boundEvents = null; - } - - // Restore sizzle document to window.document - // Since the current document might be removed producing "Permission denied" on IE see #6325 - if (Sizzle.setDocument) { - Sizzle.setDocument(); - } - - self.win = self.doc = self.root = self.events = self.frag = null; - }, - - isChildOf: function(node, parent) { - while (node) { - if (parent === node) { - return true; - } - - node = node.parentNode; - } - - return false; - }, - - // #ifdef debug - - dumpRng: function(r) { - return ( - 'startContainer: ' + r.startContainer.nodeName + - ', startOffset: ' + r.startOffset + - ', endContainer: ' + r.endContainer.nodeName + - ', endOffset: ' + r.endOffset - ); - }, - - // #endif - - _findSib: function(node, selector, name) { - var self = this, - func = selector; - - if (node) { - // If expression make a function of it using is - if (typeof func == 'string') { - func = function(node) { - return self.is(node, selector); - }; - } - - // Loop all siblings - for (node = node[name]; node; node = node[name]) { - if (func(node)) { - return node; - } - } - } - - return null; - } - }; - - /** - * Instance of DOMUtils for the current document. - * - * @static - * @property DOM - * @type tinymce.dom.DOMUtils - * @example - * // Example of how to add a class to some element by id - * tinymce.DOM.addClass('someid', 'someclass'); - */ - DOMUtils.DOM = new DOMUtils(document); - DOMUtils.nodeIndex = nodeIndex; - - return DOMUtils; - } - ); - - /** - * ScriptLoader.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /*globals console*/ - - /** - * This class handles asynchronous/synchronous loading of JavaScript files it will execute callbacks - * when various items gets loaded. This class is useful to load external JavaScript files. - * - * @class tinymce.dom.ScriptLoader - * @example - * // Load a script from a specific URL using the global script loader - * tinymce.ScriptLoader.load('somescript.js'); - * - * // Load a script using a unique instance of the script loader - * var scriptLoader = new tinymce.dom.ScriptLoader(); - * - * scriptLoader.load('somescript.js'); - * - * // Load multiple scripts - * var scriptLoader = new tinymce.dom.ScriptLoader(); - * - * scriptLoader.add('somescript1.js'); - * scriptLoader.add('somescript2.js'); - * scriptLoader.add('somescript3.js'); - * - * scriptLoader.loadQueue(function() { - * alert('All scripts are now loaded.'); - * }); - */ - define( - 'tinymce.core.dom.ScriptLoader', [ - "tinymce.core.dom.DOMUtils", - "tinymce.core.util.Tools" - ], - function(DOMUtils, Tools) { - var DOM = DOMUtils.DOM; - var each = Tools.each, - grep = Tools.grep; - - var isFunction = function(f) { - return typeof f === 'function'; - }; - - function ScriptLoader() { - var QUEUED = 0, - LOADING = 1, - LOADED = 2, - FAILED = 3, - states = {}, - queue = [], - scriptLoadedCallbacks = {}, - queueLoadedCallbacks = [], - loading = 0, - undef; - - /** - * Loads a specific script directly without adding it to the load queue. - * - * @method load - * @param {String} url Absolute URL to script to add. - * @param {function} callback Optional success callback function when the script loaded successfully. - * @param {function} callback Optional failure callback function when the script failed to load. - */ - function loadScript(url, success, failure) { - var dom = DOM, - elm, id; - - // Execute callback when script is loaded - function done() { - dom.remove(id); - - if (elm) { - elm.onreadystatechange = elm.onload = elm = null; - } - - success(); - } - - function error() { - /*eslint no-console:0 */ - - // We can't mark it as done if there is a load error since - // A) We don't want to produce 404 errors on the server and - // B) the onerror event won't fire on all browsers. - // done(); - - if (isFunction(failure)) { - failure(); - } else { - // Report the error so it's easier for people to spot loading errors - if (typeof console !== "undefined" && console.log) { - console.log("Failed to load script: " + url); - } - } - } - - id = dom.uniqueId(); - - // Create new script element - elm = document.createElement('script'); - elm.id = id; - elm.type = 'text/javascript'; - elm.src = Tools._addCacheSuffix(url); - - // Seems that onreadystatechange works better on IE 10 onload seems to fire incorrectly - if ("onreadystatechange" in elm) { - elm.onreadystatechange = function() { - if (/loaded|complete/.test(elm.readyState)) { - done(); - } - }; - } else { - elm.onload = done; - } - - // Add onerror event will get fired on some browsers but not all of them - elm.onerror = error; - - // Add script to document - (document.getElementsByTagName('head')[0] || document.body).appendChild(elm); - } - - /** - * Returns true/false if a script has been loaded or not. - * - * @method isDone - * @param {String} url URL to check for. - * @return {Boolean} true/false if the URL is loaded. - */ - this.isDone = function(url) { - return states[url] == LOADED; - }; - - /** - * Marks a specific script to be loaded. This can be useful if a script got loaded outside - * the script loader or to skip it from loading some script. - * - * @method markDone - * @param {string} url Absolute URL to the script to mark as loaded. - */ - this.markDone = function(url) { - states[url] = LOADED; - }; - - /** - * Adds a specific script to the load queue of the script loader. - * - * @method add - * @param {String} url Absolute URL to script to add. - * @param {function} success Optional success callback function to execute when the script loades successfully. - * @param {Object} scope Optional scope to execute callback in. - * @param {function} failure Optional failure callback function to execute when the script failed to load. - */ - this.add = this.load = function(url, success, scope, failure) { - var state = states[url]; - - // Add url to load queue - if (state == undef) { - queue.push(url); - states[url] = QUEUED; - } - - if (success) { - // Store away callback for later execution - if (!scriptLoadedCallbacks[url]) { - scriptLoadedCallbacks[url] = []; - } - - scriptLoadedCallbacks[url].push({ - success: success, - failure: failure, - scope: scope || this - }); - } - }; - - this.remove = function(url) { - delete states[url]; - delete scriptLoadedCallbacks[url]; - }; - - /** - * Starts the loading of the queue. - * - * @method loadQueue - * @param {function} success Optional callback to execute when all queued items are loaded. - * @param {function} failure Optional callback to execute when queued items failed to load. - * @param {Object} scope Optional scope to execute the callback in. - */ - this.loadQueue = function(success, scope, failure) { - this.loadScripts(queue, success, scope, failure); - }; - - /** - * Loads the specified queue of files and executes the callback ones they are loaded. - * This method is generally not used outside this class but it might be useful in some scenarios. - * - * @method loadScripts - * @param {Array} scripts Array of queue items to load. - * @param {function} callback Optional callback to execute when scripts is loaded successfully. - * @param {Object} scope Optional scope to execute callback in. - * @param {function} callback Optional callback to execute if scripts failed to load. - */ - this.loadScripts = function(scripts, success, scope, failure) { - var loadScripts, failures = []; - - function execCallbacks(name, url) { - // Execute URL callback functions - each(scriptLoadedCallbacks[url], function(callback) { - if (isFunction(callback[name])) { - callback[name].call(callback.scope); - } - }); - - scriptLoadedCallbacks[url] = undef; - } - - queueLoadedCallbacks.push({ - success: success, - failure: failure, - scope: scope || this - }); - - loadScripts = function() { - var loadingScripts = grep(scripts); - - // Current scripts has been handled - scripts.length = 0; - - // Load scripts that needs to be loaded - each(loadingScripts, function(url) { - // Script is already loaded then execute script callbacks directly - if (states[url] === LOADED) { - execCallbacks('success', url); - return; - } - - if (states[url] === FAILED) { - execCallbacks('failure', url); - return; - } - - // Is script not loading then start loading it - if (states[url] !== LOADING) { - states[url] = LOADING; - loading++; - - loadScript(url, function() { - states[url] = LOADED; - loading--; - - execCallbacks('success', url); - - // Load more scripts if they where added by the recently loaded script - loadScripts(); - }, function() { - states[url] = FAILED; - loading--; - - failures.push(url); - execCallbacks('failure', url); - - // Load more scripts if they where added by the recently loaded script - loadScripts(); - }); - } - }); - - // No scripts are currently loading then execute all pending queue loaded callbacks - if (!loading) { - each(queueLoadedCallbacks, function(callback) { - if (failures.length === 0) { - if (isFunction(callback.success)) { - callback.success.call(callback.scope); - } - } else { - if (isFunction(callback.failure)) { - callback.failure.call(callback.scope, failures); - } - } - }); - - queueLoadedCallbacks.length = 0; - } - }; - - loadScripts(); - }; - } - - ScriptLoader.ScriptLoader = new ScriptLoader(); - - return ScriptLoader; - } - ); - - /** - * AddOnManager.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class handles the loading of themes/plugins or other add-ons and their language packs. - * - * @class tinymce.AddOnManager - */ - define( - 'tinymce.core.AddOnManager', [ - "tinymce.core.dom.ScriptLoader", - "tinymce.core.util.Tools" - ], - function(ScriptLoader, Tools) { - var each = Tools.each; - - function AddOnManager() { - var self = this; - - self.items = []; - self.urls = {}; - self.lookup = {}; - } - - AddOnManager.prototype = { - /** - * Returns the specified add on by the short name. - * - * @method get - * @param {String} name Add-on to look for. - * @return {tinymce.Theme/tinymce.Plugin} Theme or plugin add-on instance or undefined. - */ - get: function(name) { - if (this.lookup[name]) { - return this.lookup[name].instance; - } - - return undefined; - }, - - dependencies: function(name) { - var result; - - if (this.lookup[name]) { - result = this.lookup[name].dependencies; - } - - return result || []; - }, - - /** - * Loads a language pack for the specified add-on. - * - * @method requireLangPack - * @param {String} name Short name of the add-on. - * @param {String} languages Optional comma or space separated list of languages to check if it matches the name. - */ - requireLangPack: function(name, languages) { - var language = AddOnManager.language; - - if (language && AddOnManager.languageLoad !== false) { - if (languages) { - languages = ',' + languages + ','; - - // Load short form sv.js or long form sv_SE.js - if (languages.indexOf(',' + language.substr(0, 2) + ',') != -1) { - language = language.substr(0, 2); - } else if (languages.indexOf(',' + language + ',') == -1) { - return; - } - } - - ScriptLoader.ScriptLoader.add(this.urls[name] + '/langs/' + language + '.js'); - } - }, - - /** - * Adds a instance of the add-on by it's short name. - * - * @method add - * @param {String} id Short name/id for the add-on. - * @param {tinymce.Theme/tinymce.Plugin} addOn Theme or plugin to add. - * @return {tinymce.Theme/tinymce.Plugin} The same theme or plugin instance that got passed in. - * @example - * // Create a simple plugin - * tinymce.create('tinymce.plugins.TestPlugin', { - * TestPlugin: function(ed, url) { - * ed.on('click', function(e) { - * ed.windowManager.alert('Hello World!'); - * }); - * } - * }); - * - * // Register plugin using the add method - * tinymce.PluginManager.add('test', tinymce.plugins.TestPlugin); - * - * // Initialize TinyMCE - * tinymce.init({ - * ... - * plugins: '-test' // Init the plugin but don't try to load it - * }); - */ - add: function(id, addOn, dependencies) { - this.items.push(addOn); - this.lookup[id] = { - instance: addOn, - dependencies: dependencies - }; - - return addOn; - }, - - remove: function(name) { - delete this.urls[name]; - delete this.lookup[name]; - }, - - createUrl: function(baseUrl, dep) { - if (typeof dep === "object") { - return dep; - } - - return { - prefix: baseUrl.prefix, - resource: dep, - suffix: baseUrl.suffix - }; - }, - - /** - * Add a set of components that will make up the add-on. Using the url of the add-on name as the base url. - * This should be used in development mode. A new compressor/javascript munger process will ensure that the - * components are put together into the plugin.js file and compressed correctly. - * - * @method addComponents - * @param {String} pluginName name of the plugin to load scripts from (will be used to get the base url for the plugins). - * @param {Array} scripts Array containing the names of the scripts to load. - */ - addComponents: function(pluginName, scripts) { - var pluginUrl = this.urls[pluginName]; - - each(scripts, function(script) { - ScriptLoader.ScriptLoader.add(pluginUrl + "/" + script); - }); - }, - - /** - * Loads an add-on from a specific url. - * - * @method load - * @param {String} name Short name of the add-on that gets loaded. - * @param {String} addOnUrl URL to the add-on that will get loaded. - * @param {function} success Optional success callback to execute when an add-on is loaded. - * @param {Object} scope Optional scope to execute the callback in. - * @param {function} failure Optional failure callback to execute when an add-on failed to load. - * @example - * // Loads a plugin from an external URL - * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js'); - * - * // Initialize TinyMCE - * tinymce.init({ - * ... - * plugins: '-myplugin' // Don't try to load it again - * }); - */ - load: function(name, addOnUrl, success, scope, failure) { - var self = this, - url = addOnUrl; - - function loadDependencies() { - var dependencies = self.dependencies(name); - - each(dependencies, function(dep) { - var newUrl = self.createUrl(addOnUrl, dep); - - self.load(newUrl.resource, newUrl, undefined, undefined); - }); - - if (success) { - if (scope) { - success.call(scope); - } else { - success.call(ScriptLoader); - } - } - } - - if (self.urls[name]) { - return; - } - - if (typeof addOnUrl === "object") { - url = addOnUrl.prefix + addOnUrl.resource + addOnUrl.suffix; - } - - if (url.indexOf('/') !== 0 && url.indexOf('://') == -1) { - url = AddOnManager.baseURL + '/' + url; - } - - self.urls[name] = url.substring(0, url.lastIndexOf('/')); - - if (self.lookup[name]) { - loadDependencies(); - } else { - ScriptLoader.ScriptLoader.add(url, loadDependencies, scope, failure); - } - } - }; - - AddOnManager.PluginManager = new AddOnManager(); - AddOnManager.ThemeManager = new AddOnManager(); - - return AddOnManager; - } - ); - - /** - * TinyMCE theme class. - * - * @class tinymce.Theme - */ - - /** - * This method is responsible for rendering/generating the overall user interface with toolbars, buttons, iframe containers etc. - * - * @method renderUI - * @param {Object} obj Object parameter containing the targetNode DOM node that will be replaced visually with an editor instance. - * @return {Object} an object with items like iframeContainer, editorContainer, sizeContainer, deltaWidth, deltaHeight. - */ - - /** - * Plugin base class, this is a pseudo class that describes how a plugin is to be created for TinyMCE. The methods below are all optional. - * - * @class tinymce.Plugin - * @example - * tinymce.PluginManager.add('example', function(editor, url) { - * // Add a button that opens a window - * editor.addButton('example', { - * text: 'My button', - * icon: false, - * onclick: function() { - * // Open window - * editor.windowManager.open({ - * title: 'Example plugin', - * body: [ - * {type: 'textbox', name: 'title', label: 'Title'} - * ], - * onsubmit: function(e) { - * // Insert content when the window form is submitted - * editor.insertContent('Title: ' + e.data.title); - * } - * }); - * } - * }); - * - * // Adds a menu item to the tools menu - * editor.addMenuItem('example', { - * text: 'Example plugin', - * context: 'tools', - * onclick: function() { - * // Open window with a specific url - * editor.windowManager.open({ - * title: 'TinyMCE site', - * url: 'http://www.tinymce.com', - * width: 800, - * height: 600, - * buttons: [{ - * text: 'Close', - * onclick: 'close' - * }] - * }); - * } - * }); - * }); - */ - - /** - * NodeType.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Contains various node validation functions. - * - * @private - * @class tinymce.dom.NodeType - */ - define( - 'tinymce.core.dom.NodeType', [], - function() { - function isNodeType(type) { - return function(node) { - return !!node && node.nodeType == type; - }; - } - - var isElement = isNodeType(1); - - function matchNodeNames(names) { - names = names.toLowerCase().split(' '); - - return function(node) { - var i, name; - - if (node && node.nodeType) { - name = node.nodeName.toLowerCase(); - - for (i = 0; i < names.length; i++) { - if (name === names[i]) { - return true; - } - } - } - - return false; - }; - } - - function matchStyleValues(name, values) { - values = values.toLowerCase().split(' '); - - return function(node) { - var i, cssValue; - - if (isElement(node)) { - for (i = 0; i < values.length; i++) { - cssValue = node.ownerDocument.defaultView.getComputedStyle(node, null).getPropertyValue(name); - if (cssValue === values[i]) { - return true; - } - } - } - - return false; - }; - } - - function hasPropValue(propName, propValue) { - return function(node) { - return isElement(node) && node[propName] === propValue; - }; - } - - function hasAttribute(attrName, attrValue) { - return function(node) { - return isElement(node) && node.hasAttribute(attrName); - }; - } - - function hasAttributeValue(attrName, attrValue) { - return function(node) { - return isElement(node) && node.getAttribute(attrName) === attrValue; - }; - } - - function isBogus(node) { - return isElement(node) && node.hasAttribute('data-mce-bogus'); - } - - function hasContentEditableState(value) { - return function(node) { - if (isElement(node)) { - if (node.contentEditable === value) { - return true; - } - - if (node.getAttribute('data-mce-contenteditable') === value) { - return true; - } - } - - return false; - }; - } - - return { - isText: isNodeType(3), - isElement: isElement, - isComment: isNodeType(8), - isBr: matchNodeNames('br'), - isContentEditableTrue: hasContentEditableState('true'), - isContentEditableFalse: hasContentEditableState('false'), - matchNodeNames: matchNodeNames, - hasPropValue: hasPropValue, - hasAttribute: hasAttribute, - hasAttributeValue: hasAttributeValue, - matchStyleValues: matchStyleValues, - isBogus: isBogus - }; - } - ); - /** - * Zwsp.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Utility functions for working with zero width space - * characters used as character containers etc. - * - * @private - * @class tinymce.text.Zwsp - * @example - * var isZwsp = Zwsp.isZwsp('\uFEFF'); - * var abc = Zwsp.trim('a\uFEFFc'); - */ - define( - 'tinymce.core.text.Zwsp', [], - function() { - // This is technically not a ZWSP but a ZWNBSP or a BYTE ORDER MARK it used to be a ZWSP - var ZWSP = '\uFEFF'; - - var isZwsp = function(chr) { - return chr === ZWSP; - }; - - var trim = function(text) { - return text.replace(new RegExp(ZWSP, 'g'), ''); - }; - - return { - isZwsp: isZwsp, - ZWSP: ZWSP, - trim: trim - }; - } - ); - /** - * CaretContainer.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module handles caret containers. A caret container is a node that - * holds the caret for positional purposes. - * - * @private - * @class tinymce.caret.CaretContainer - */ - define( - 'tinymce.core.caret.CaretContainer', [ - "tinymce.core.dom.NodeType", - "tinymce.core.text.Zwsp" - ], - function(NodeType, Zwsp) { - var isElement = NodeType.isElement, - isText = NodeType.isText; - - function isCaretContainerBlock(node) { - if (isText(node)) { - node = node.parentNode; - } - - return isElement(node) && node.hasAttribute('data-mce-caret'); - } - - function isCaretContainerInline(node) { - return isText(node) && Zwsp.isZwsp(node.data); - } - - function isCaretContainer(node) { - return isCaretContainerBlock(node) || isCaretContainerInline(node); - } - - var hasContent = function(node) { - return node.firstChild !== node.lastChild || !NodeType.isBr(node.firstChild); - }; - - function insertInline(node, before) { - var doc, sibling, textNode, parentNode; - - doc = node.ownerDocument; - textNode = doc.createTextNode(Zwsp.ZWSP); - parentNode = node.parentNode; - - if (!before) { - sibling = node.nextSibling; - if (isText(sibling)) { - if (isCaretContainer(sibling)) { - return sibling; - } - - if (startsWithCaretContainer(sibling)) { - sibling.splitText(1); - return sibling; - } - } - - if (node.nextSibling) { - parentNode.insertBefore(textNode, node.nextSibling); - } else { - parentNode.appendChild(textNode); - } - } else { - sibling = node.previousSibling; - if (isText(sibling)) { - if (isCaretContainer(sibling)) { - return sibling; - } - - if (endsWithCaretContainer(sibling)) { - return sibling.splitText(sibling.data.length - 1); - } - } - - parentNode.insertBefore(textNode, node); - } - - return textNode; - } - - var prependInline = function(node) { - if (NodeType.isText(node)) { - var data = node.data; - if (data.length > 0 && data.charAt(0) !== Zwsp.ZWSP) { - node.insertData(0, Zwsp.ZWSP); - } - return node; - } else { - return null; - } - }; - - var appendInline = function(node) { - if (NodeType.isText(node)) { - var data = node.data; - if (data.length > 0 && data.charAt(data.length - 1) !== Zwsp.ZWSP) { - node.insertData(data.length, Zwsp.ZWSP); - } - return node; - } else { - return null; - } - }; - - var isBeforeInline = function(pos) { - return pos && NodeType.isText(pos.container()) && pos.container().data.charAt(pos.offset()) === Zwsp.ZWSP; - }; - - var isAfterInline = function(pos) { - return pos && NodeType.isText(pos.container()) && pos.container().data.charAt(pos.offset() - 1) === Zwsp.ZWSP; - }; - - function createBogusBr() { - var br = document.createElement('br'); - br.setAttribute('data-mce-bogus', '1'); - return br; - } - - function insertBlock(blockName, node, before) { - var doc, blockNode, parentNode; - - doc = node.ownerDocument; - blockNode = doc.createElement(blockName); - blockNode.setAttribute('data-mce-caret', before ? 'before' : 'after'); - blockNode.setAttribute('data-mce-bogus', 'all'); - blockNode.appendChild(createBogusBr()); - parentNode = node.parentNode; - - if (!before) { - if (node.nextSibling) { - parentNode.insertBefore(blockNode, node.nextSibling); - } else { - parentNode.appendChild(blockNode); - } - } else { - parentNode.insertBefore(blockNode, node); - } - - return blockNode; - } - - function startsWithCaretContainer(node) { - return isText(node) && node.data[0] == Zwsp.ZWSP; - } - - function endsWithCaretContainer(node) { - return isText(node) && node.data[node.data.length - 1] == Zwsp.ZWSP; - } - - function trimBogusBr(elm) { - var brs = elm.getElementsByTagName('br'); - var lastBr = brs[brs.length - 1]; - if (NodeType.isBogus(lastBr)) { - lastBr.parentNode.removeChild(lastBr); - } - } - - function showCaretContainerBlock(caretContainer) { - if (caretContainer && caretContainer.hasAttribute('data-mce-caret')) { - trimBogusBr(caretContainer); - caretContainer.removeAttribute('data-mce-caret'); - caretContainer.removeAttribute('data-mce-bogus'); - caretContainer.removeAttribute('style'); - caretContainer.removeAttribute('_moz_abspos'); - return caretContainer; - } - - return null; - } - - return { - isCaretContainer: isCaretContainer, - isCaretContainerBlock: isCaretContainerBlock, - isCaretContainerInline: isCaretContainerInline, - showCaretContainerBlock: showCaretContainerBlock, - insertInline: insertInline, - prependInline: prependInline, - appendInline: appendInline, - isBeforeInline: isBeforeInline, - isAfterInline: isAfterInline, - insertBlock: insertBlock, - hasContent: hasContent, - startsWithCaretContainer: startsWithCaretContainer, - endsWithCaretContainer: endsWithCaretContainer - }; - } - ); - /** - * RangeUtils.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class contains a few utility methods for ranges. - * - * @class tinymce.dom.RangeUtils - */ - define( - 'tinymce.core.dom.RangeUtils', [ - "tinymce.core.util.Tools", - "tinymce.core.dom.TreeWalker", - "tinymce.core.dom.NodeType", - "tinymce.core.dom.Range", - "tinymce.core.caret.CaretContainer" - ], - function(Tools, TreeWalker, NodeType, Range, CaretContainer) { - var each = Tools.each, - isContentEditableTrue = NodeType.isContentEditableTrue, - isContentEditableFalse = NodeType.isContentEditableFalse, - isCaretContainer = CaretContainer.isCaretContainer; - - function hasCeProperty(node) { - return isContentEditableTrue(node) || isContentEditableFalse(node); - } - - function getEndChild(container, index) { - var childNodes = container.childNodes; - - index--; - - if (index > childNodes.length - 1) { - index = childNodes.length - 1; - } else if (index < 0) { - index = 0; - } - - return childNodes[index] || container; - } - - function findParent(node, rootNode, predicate) { - while (node && node !== rootNode) { - if (predicate(node)) { - return node; - } - - node = node.parentNode; - } - - return null; - } - - function hasParent(node, rootNode, predicate) { - return findParent(node, rootNode, predicate) !== null; - } - - function hasParentWithName(node, rootNode, name) { - return hasParent(node, rootNode, function(node) { - return node.nodeName === name; - }); - } - - function isFormatterCaret(node) { - return node.id === '_mce_caret'; - } - - function isCeFalseCaretContainer(node, rootNode) { - return isCaretContainer(node) && hasParent(node, rootNode, isFormatterCaret) === false; - } - - function RangeUtils(dom) { - /** - * Walks the specified range like object and executes the callback for each sibling collection it finds. - * - * @private - * @method walk - * @param {Object} rng Range like object. - * @param {function} callback Callback function to execute for each sibling collection. - */ - this.walk = function(rng, callback) { - var startContainer = rng.startContainer, - startOffset = rng.startOffset, - endContainer = rng.endContainer, - endOffset = rng.endOffset, - ancestor, startPoint, - endPoint, node, parent, siblings, nodes; - - // Handle table cell selection the table plugin enables - // you to fake select table cells and perform formatting actions on them - nodes = dom.select('td[data-mce-selected],th[data-mce-selected]'); - if (nodes.length > 0) { - each(nodes, function(node) { - callback([node]); - }); - - return; - } - - /** - * Excludes start/end text node if they are out side the range - * - * @private - * @param {Array} nodes Nodes to exclude items from. - * @return {Array} Array with nodes excluding the start/end container if needed. - */ - function exclude(nodes) { - var node; - - // First node is excluded - node = nodes[0]; - if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) { - nodes.splice(0, 1); - } - - // Last node is excluded - node = nodes[nodes.length - 1]; - if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) { - nodes.splice(nodes.length - 1, 1); - } - - return nodes; - } - - /** - * Collects siblings - * - * @private - * @param {Node} node Node to collect siblings from. - * @param {String} name Name of the sibling to check for. - * @param {Node} endNode - * @return {Array} Array of collected siblings. - */ - function collectSiblings(node, name, endNode) { - var siblings = []; - - for (; node && node != endNode; node = node[name]) { - siblings.push(node); - } - - return siblings; - } - - /** - * Find an end point this is the node just before the common ancestor root. - * - * @private - * @param {Node} node Node to start at. - * @param {Node} root Root/ancestor element to stop just before. - * @return {Node} Node just before the root element. - */ - function findEndPoint(node, root) { - do { - if (node.parentNode == root) { - return node; - } - - node = node.parentNode; - } while (node); - } - - function walkBoundary(startNode, endNode, next) { - var siblingName = next ? 'nextSibling' : 'previousSibling'; - - for (node = startNode, parent = node.parentNode; node && node != endNode; node = parent) { - parent = node.parentNode; - siblings = collectSiblings(node == startNode ? node : node[siblingName], siblingName); - - if (siblings.length) { - if (!next) { - siblings.reverse(); - } - - callback(exclude(siblings)); - } - } - } - - // If index based start position then resolve it - if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) { - startContainer = startContainer.childNodes[startOffset]; - } - - // If index based end position then resolve it - if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) { - endContainer = getEndChild(endContainer, endOffset); - } - - // Same container - if (startContainer == endContainer) { - return callback(exclude([startContainer])); - } - - // Find common ancestor and end points - ancestor = dom.findCommonAncestor(startContainer, endContainer); - - // Process left side - for (node = startContainer; node; node = node.parentNode) { - if (node === endContainer) { - return walkBoundary(startContainer, ancestor, true); - } - - if (node === ancestor) { - break; - } - } - - // Process right side - for (node = endContainer; node; node = node.parentNode) { - if (node === startContainer) { - return walkBoundary(endContainer, ancestor); - } - - if (node === ancestor) { - break; - } - } - - // Find start/end point - startPoint = findEndPoint(startContainer, ancestor) || startContainer; - endPoint = findEndPoint(endContainer, ancestor) || endContainer; - - // Walk left leaf - walkBoundary(startContainer, startPoint, true); - - // Walk the middle from start to end point - siblings = collectSiblings( - startPoint == startContainer ? startPoint : startPoint.nextSibling, - 'nextSibling', - endPoint == endContainer ? endPoint.nextSibling : endPoint - ); - - if (siblings.length) { - callback(exclude(siblings)); - } - - // Walk right leaf - walkBoundary(endContainer, endPoint); - }; - - /** - * Splits the specified range at it's start/end points. - * - * @private - * @param {Range/RangeObject} rng Range to split. - * @return {Object} Range position object. - */ - this.split = function(rng) { - var startContainer = rng.startContainer, - startOffset = rng.startOffset, - endContainer = rng.endContainer, - endOffset = rng.endOffset; - - function splitText(node, offset) { - return node.splitText(offset); - } - - // Handle single text node - if (startContainer == endContainer && startContainer.nodeType == 3) { - if (startOffset > 0 && startOffset < startContainer.nodeValue.length) { - endContainer = splitText(startContainer, startOffset); - startContainer = endContainer.previousSibling; - - if (endOffset > startOffset) { - endOffset = endOffset - startOffset; - startContainer = endContainer = splitText(endContainer, endOffset).previousSibling; - endOffset = endContainer.nodeValue.length; - startOffset = 0; - } else { - endOffset = 0; - } - } - } else { - // Split startContainer text node if needed - if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) { - startContainer = splitText(startContainer, startOffset); - startOffset = 0; - } - - // Split endContainer text node if needed - if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) { - endContainer = splitText(endContainer, endOffset).previousSibling; - endOffset = endContainer.nodeValue.length; - } - } - - return { - startContainer: startContainer, - startOffset: startOffset, - endContainer: endContainer, - endOffset: endOffset - }; - }; - - /** - * Normalizes the specified range by finding the closest best suitable caret location. - * - * @private - * @param {Range} rng Range to normalize. - * @return {Boolean} True/false if the specified range was normalized or not. - */ - this.normalize = function(rng) { - var normalized = false, - collapsed; - - function normalizeEndPoint(start) { - var container, offset, walker, body = dom.getRoot(), - node, nonEmptyElementsMap; - var directionLeft, isAfterNode; - - function isTableCell(node) { - return node && /^(TD|TH|CAPTION)$/.test(node.nodeName); - } - - function hasBrBeforeAfter(node, left) { - var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body); - - while ((node = walker[left ? 'prev' : 'next']())) { - if (node.nodeName === "BR") { - return true; - } - } - } - - function hasContentEditableFalseParent(node) { - while (node && node != body) { - if (isContentEditableFalse(node)) { - return true; - } - - node = node.parentNode; - } - - return false; - } - - function isPrevNode(node, name) { - return node.previousSibling && node.previousSibling.nodeName == name; - } - - // Walks the dom left/right to find a suitable text node to move the endpoint into - // It will only walk within the current parent block or body and will stop if it hits a block or a BR/IMG - function findTextNodeRelative(left, startNode) { - var walker, lastInlineElement, parentBlockContainer; - - startNode = startNode || container; - parentBlockContainer = dom.getParent(startNode.parentNode, dom.isBlock) || body; - - // Lean left before the BR element if it's the only BR within a block element. Gecko bug: #6680 - // This: <p><br>|</p> becomes <p>|<br></p> - if (left && startNode.nodeName == 'BR' && isAfterNode && dom.isEmpty(parentBlockContainer)) { - container = startNode.parentNode; - offset = dom.nodeIndex(startNode); - normalized = true; - return; - } - - // Walk left until we hit a text node we can move to or a block/br/img - walker = new TreeWalker(startNode, parentBlockContainer); - while ((node = walker[left ? 'prev' : 'next']())) { - // Break if we hit a non content editable node - if (dom.getContentEditableParent(node) === "false" || isCeFalseCaretContainer(node, dom.getRoot())) { - return; - } - - // Found text node that has a length - if (node.nodeType === 3 && node.nodeValue.length > 0) { - if (hasParentWithName(node, body, 'A') === false) { - container = node; - offset = left ? node.nodeValue.length : 0; - normalized = true; - } - - return; - } - - // Break if we find a block or a BR/IMG/INPUT etc - if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) { - return; - } - - lastInlineElement = node; - } - - // Only fetch the last inline element when in caret mode for now - if (collapsed && lastInlineElement) { - container = lastInlineElement; - normalized = true; - offset = 0; - } - } - - container = rng[(start ? 'start' : 'end') + 'Container']; - offset = rng[(start ? 'start' : 'end') + 'Offset']; - isAfterNode = container.nodeType == 1 && offset === container.childNodes.length; - nonEmptyElementsMap = dom.schema.getNonEmptyElements(); - directionLeft = start; - - if (isCaretContainer(container)) { - return; - } - - if (container.nodeType == 1 && offset > container.childNodes.length - 1) { - directionLeft = false; - } - - // If the container is a document move it to the body element - if (container.nodeType === 9) { - container = dom.getRoot(); - offset = 0; - } - - // If the container is body try move it into the closest text node or position - if (container === body) { - // If start is before/after a image, table etc - if (directionLeft) { - node = container.childNodes[offset > 0 ? offset - 1 : 0]; - if (node) { - if (isCaretContainer(node)) { - return; - } - - if (nonEmptyElementsMap[node.nodeName] || node.nodeName == "TABLE") { - return; - } - } - } - - // Resolve the index - if (container.hasChildNodes()) { - offset = Math.min(!directionLeft && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1); - container = container.childNodes[offset]; - offset = 0; - - // Don't normalize non collapsed selections like <p>[a</p><table></table>] - if (!collapsed && container === body.lastChild && container.nodeName === 'TABLE') { - return; - } - - if (hasContentEditableFalseParent(container) || isCaretContainer(container)) { - return; - } - - // Don't walk into elements that doesn't have any child nodes like a IMG - if (container.hasChildNodes() && !/TABLE/.test(container.nodeName)) { - // Walk the DOM to find a text node to place the caret at or a BR - node = container; - walker = new TreeWalker(container, body); - - do { - if (isContentEditableFalse(node) || isCaretContainer(node)) { - normalized = false; - break; - } - - // Found a text node use that position - if (node.nodeType === 3 && node.nodeValue.length > 0) { - offset = directionLeft ? 0 : node.nodeValue.length; - container = node; - normalized = true; - break; - } - - // Found a BR/IMG element that we can place the caret before - if (nonEmptyElementsMap[node.nodeName.toLowerCase()] && !isTableCell(node)) { - offset = dom.nodeIndex(node); - container = node.parentNode; - - // Put caret after image when moving the end point - if (node.nodeName == "IMG" && !directionLeft) { - offset++; - } - - normalized = true; - break; - } - } while ((node = (directionLeft ? walker.next() : walker.prev()))); - } - } - } - - // Lean the caret to the left if possible - if (collapsed) { - // So this: <b>x</b><i>|x</i> - // Becomes: <b>x|</b><i>x</i> - // Seems that only gecko has issues with this - if (container.nodeType === 3 && offset === 0) { - findTextNodeRelative(true); - } - - // Lean left into empty inline elements when the caret is before a BR - // So this: <i><b></b><i>|<br></i> - // Becomes: <i><b>|</b><i><br></i> - // Seems that only gecko has issues with this. - // Special edge case for <p><a>x</a>|<br></p> since we don't want <p><a>x|</a><br></p> - if (container.nodeType === 1) { - node = container.childNodes[offset]; - - // Offset is after the containers last child - // then use the previous child for normalization - if (!node) { - node = container.childNodes[offset - 1]; - } - - if (node && node.nodeName === 'BR' && !isPrevNode(node, 'A') && - !hasBrBeforeAfter(node) && !hasBrBeforeAfter(node, true)) { - findTextNodeRelative(true, node); - } - } - } - - // Lean the start of the selection right if possible - // So this: x[<b>x]</b> - // Becomes: x<b>[x]</b> - if (directionLeft && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) { - findTextNodeRelative(false); - } - - // Set endpoint if it was normalized - if (normalized) { - rng['set' + (start ? 'Start' : 'End')](container, offset); - } - } - - collapsed = rng.collapsed; - - normalizeEndPoint(true); - - if (!collapsed) { - normalizeEndPoint(); - } - - // If it was collapsed then make sure it still is - if (normalized && collapsed) { - rng.collapse(true); - } - - return normalized; - }; - } - - /** - * Compares two ranges and checks if they are equal. - * - * @static - * @method compareRanges - * @param {DOMRange} rng1 First range to compare. - * @param {DOMRange} rng2 First range to compare. - * @return {Boolean} true/false if the ranges are equal. - */ - RangeUtils.compareRanges = function(rng1, rng2) { - if (rng1 && rng2) { - // Compare native IE ranges - if (rng1.item || rng1.duplicate) { - // Both are control ranges and the selected element matches - if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0)) { - return true; - } - - // Both are text ranges and the range matches - if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) { - return true; - } - } else { - // Compare w3c ranges - return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset; - } - } - - return false; - }; - - /** - * Finds the closest selection rect tries to get the range from that. - */ - function findClosestIeRange(clientX, clientY, doc) { - var element, rng, rects; - - element = doc.elementFromPoint(clientX, clientY); - rng = doc.body.createTextRange(); - - if (!element || element.tagName == 'HTML') { - element = doc.body; - } - - rng.moveToElementText(element); - rects = Tools.toArray(rng.getClientRects()); - - rects = rects.sort(function(a, b) { - a = Math.abs(Math.max(a.top - clientY, a.bottom - clientY)); - b = Math.abs(Math.max(b.top - clientY, b.bottom - clientY)); - - return a - b; - }); - - if (rects.length > 0) { - clientY = (rects[0].bottom + rects[0].top) / 2; - - try { - rng.moveToPoint(clientX, clientY); - rng.collapse(true); - - return rng; - } catch (ex) { - // At least we tried - } - } - - return null; - } - - function moveOutOfContentEditableFalse(rng, rootNode) { - var parentElement = rng && rng.parentElement ? rng.parentElement() : null; - return isContentEditableFalse(findParent(parentElement, rootNode, hasCeProperty)) ? null : rng; - } - - /** - * Gets the caret range for the given x/y location. - * - * @static - * @method getCaretRangeFromPoint - * @param {Number} clientX X coordinate for range - * @param {Number} clientY Y coordinate for range - * @param {Document} doc Document that x/y are relative to - * @returns {Range} caret range - */ - RangeUtils.getCaretRangeFromPoint = function(clientX, clientY, doc) { - var rng, point; - - if (doc.caretPositionFromPoint) { - point = doc.caretPositionFromPoint(clientX, clientY); - rng = doc.createRange(); - rng.setStart(point.offsetNode, point.offset); - rng.collapse(true); - } else if (doc.caretRangeFromPoint) { - rng = doc.caretRangeFromPoint(clientX, clientY); - } else if (doc.body.createTextRange) { - rng = doc.body.createTextRange(); - - try { - rng.moveToPoint(clientX, clientY); - rng.collapse(true); - } catch (ex) { - rng = findClosestIeRange(clientX, clientY, doc); - } - - return moveOutOfContentEditableFalse(rng, doc.body); - } - - return rng; - }; - - RangeUtils.getSelectedNode = function(range) { - var startContainer = range.startContainer, - startOffset = range.startOffset; - - if (startContainer.hasChildNodes() && range.endOffset == startOffset + 1) { - return startContainer.childNodes[startOffset]; - } - - return null; - }; - - RangeUtils.getNode = function(container, offset) { - if (container.nodeType == 1 && container.hasChildNodes()) { - if (offset >= container.childNodes.length) { - offset = container.childNodes.length - 1; - } - - container = container.childNodes[offset]; - } - - return container; - }; - - return RangeUtils; - } - ); - - /** - * Node.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class is a minimalistic implementation of a DOM like node used by the DomParser class. - * - * @example - * var node = new tinymce.html.Node('strong', 1); - * someRoot.append(node); - * - * @class tinymce.html.Node - * @version 3.4 - */ - define( - 'tinymce.core.html.Node', [], - function() { - var whiteSpaceRegExp = /^[ \t\r\n]*$/; - var typeLookup = { - '#text': 3, - '#comment': 8, - '#cdata': 4, - '#pi': 7, - '#doctype': 10, - '#document-fragment': 11 - }; - - // Walks the tree left/right - function walk(node, rootNode, prev) { - var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', - siblingName = prev ? 'prev' : 'next'; - - // Walk into nodes if it has a start - if (node[startName]) { - return node[startName]; - } - - // Return the sibling if it has one - if (node !== rootNode) { - sibling = node[siblingName]; - - if (sibling) { - return sibling; - } - - // Walk up the parents to look for siblings - for (parent = node.parent; parent && parent !== rootNode; parent = parent.parent) { - sibling = parent[siblingName]; - - if (sibling) { - return sibling; - } - } - } - } - - /** - * Constructs a new Node instance. - * - * @constructor - * @method Node - * @param {String} name Name of the node type. - * @param {Number} type Numeric type representing the node. - */ - function Node(name, type) { - this.name = name; - this.type = type; - - if (type === 1) { - this.attributes = []; - this.attributes.map = {}; - } - } - - Node.prototype = { - /** - * Replaces the current node with the specified one. - * - * @example - * someNode.replace(someNewNode); - * - * @method replace - * @param {tinymce.html.Node} node Node to replace the current node with. - * @return {tinymce.html.Node} The old node that got replaced. - */ - replace: function(node) { - var self = this; - - if (node.parent) { - node.remove(); - } - - self.insert(node, self); - self.remove(); - - return self; - }, - - /** - * Gets/sets or removes an attribute by name. - * - * @example - * someNode.attr("name", "value"); // Sets an attribute - * console.log(someNode.attr("name")); // Gets an attribute - * someNode.attr("name", null); // Removes an attribute - * - * @method attr - * @param {String} name Attribute name to set or get. - * @param {String} value Optional value to set. - * @return {String/tinymce.html.Node} String or undefined on a get operation or the current node on a set operation. - */ - attr: function(name, value) { - var self = this, - attrs, i, undef; - - if (typeof name !== "string") { - for (i in name) { - self.attr(i, name[i]); - } - - return self; - } - - if ((attrs = self.attributes)) { - if (value !== undef) { - // Remove attribute - if (value === null) { - if (name in attrs.map) { - delete attrs.map[name]; - - i = attrs.length; - while (i--) { - if (attrs[i].name === name) { - attrs = attrs.splice(i, 1); - return self; - } - } - } - - return self; - } - - // Set attribute - if (name in attrs.map) { - // Set attribute - i = attrs.length; - while (i--) { - if (attrs[i].name === name) { - attrs[i].value = value; - break; - } - } - } else { - attrs.push({ - name: name, - value: value - }); - } - - attrs.map[name] = value; - - return self; - } - - return attrs.map[name]; - } - }, - - /** - * Does a shallow clones the node into a new node. It will also exclude id attributes since - * there should only be one id per document. - * - * @example - * var clonedNode = node.clone(); - * - * @method clone - * @return {tinymce.html.Node} New copy of the original node. - */ - clone: function() { - var self = this, - clone = new Node(self.name, self.type), - i, l, selfAttrs, selfAttr, cloneAttrs; - - // Clone element attributes - if ((selfAttrs = self.attributes)) { - cloneAttrs = []; - cloneAttrs.map = {}; - - for (i = 0, l = selfAttrs.length; i < l; i++) { - selfAttr = selfAttrs[i]; - - // Clone everything except id - if (selfAttr.name !== 'id') { - cloneAttrs[cloneAttrs.length] = { - name: selfAttr.name, - value: selfAttr.value - }; - cloneAttrs.map[selfAttr.name] = selfAttr.value; - } - } - - clone.attributes = cloneAttrs; - } - - clone.value = self.value; - clone.shortEnded = self.shortEnded; - - return clone; - }, - - /** - * Wraps the node in in another node. - * - * @example - * node.wrap(wrapperNode); - * - * @method wrap - */ - wrap: function(wrapper) { - var self = this; - - self.parent.insert(wrapper, self); - wrapper.append(self); - - return self; - }, - - /** - * Unwraps the node in other words it removes the node but keeps the children. - * - * @example - * node.unwrap(); - * - * @method unwrap - */ - unwrap: function() { - var self = this, - node, next; - - for (node = self.firstChild; node;) { - next = node.next; - self.insert(node, self, true); - node = next; - } - - self.remove(); - }, - - /** - * Removes the node from it's parent. - * - * @example - * node.remove(); - * - * @method remove - * @return {tinymce.html.Node} Current node that got removed. - */ - remove: function() { - var self = this, - parent = self.parent, - next = self.next, - prev = self.prev; - - if (parent) { - if (parent.firstChild === self) { - parent.firstChild = next; - - if (next) { - next.prev = null; - } - } else { - prev.next = next; - } - - if (parent.lastChild === self) { - parent.lastChild = prev; - - if (prev) { - prev.next = null; - } - } else { - next.prev = prev; - } - - self.parent = self.next = self.prev = null; - } - - return self; - }, - - /** - * Appends a new node as a child of the current node. - * - * @example - * node.append(someNode); - * - * @method append - * @param {tinymce.html.Node} node Node to append as a child of the current one. - * @return {tinymce.html.Node} The node that got appended. - */ - append: function(node) { - var self = this, - last; - - if (node.parent) { - node.remove(); - } - - last = self.lastChild; - if (last) { - last.next = node; - node.prev = last; - self.lastChild = node; - } else { - self.lastChild = self.firstChild = node; - } - - node.parent = self; - - return node; - }, - - /** - * Inserts a node at a specific position as a child of the current node. - * - * @example - * parentNode.insert(newChildNode, oldChildNode); - * - * @method insert - * @param {tinymce.html.Node} node Node to insert as a child of the current node. - * @param {tinymce.html.Node} refNode Reference node to set node before/after. - * @param {Boolean} before Optional state to insert the node before the reference node. - * @return {tinymce.html.Node} The node that got inserted. - */ - insert: function(node, refNode, before) { - var parent; - - if (node.parent) { - node.remove(); - } - - parent = refNode.parent || this; - - if (before) { - if (refNode === parent.firstChild) { - parent.firstChild = node; - } else { - refNode.prev.next = node; - } - - node.prev = refNode.prev; - node.next = refNode; - refNode.prev = node; - } else { - if (refNode === parent.lastChild) { - parent.lastChild = node; - } else { - refNode.next.prev = node; - } - - node.next = refNode.next; - node.prev = refNode; - refNode.next = node; - } - - node.parent = parent; - - return node; - }, - - /** - * Get all children by name. - * - * @method getAll - * @param {String} name Name of the child nodes to collect. - * @return {Array} Array with child nodes matchin the specified name. - */ - getAll: function(name) { - var self = this, - node, collection = []; - - for (node = self.firstChild; node; node = walk(node, self)) { - if (node.name === name) { - collection.push(node); - } - } - - return collection; - }, - - /** - * Removes all children of the current node. - * - * @method empty - * @return {tinymce.html.Node} The current node that got cleared. - */ - empty: function() { - var self = this, - nodes, i, node; - - // Remove all children - if (self.firstChild) { - nodes = []; - - // Collect the children - for (node = self.firstChild; node; node = walk(node, self)) { - nodes.push(node); - } - - // Remove the children - i = nodes.length; - while (i--) { - node = nodes[i]; - node.parent = node.firstChild = node.lastChild = node.next = node.prev = null; - } - } - - self.firstChild = self.lastChild = null; - - return self; - }, - - /** - * Returns true/false if the node is to be considered empty or not. - * - * @example - * node.isEmpty({img: true}); - * @method isEmpty - * @param {Object} elements Name/value object with elements that are automatically treated as non empty elements. - * @param {Object} whitespace Name/value object with elements that are automatically treated whitespace preservables. - * @return {Boolean} true/false if the node is empty or not. - */ - isEmpty: function(elements, whitespace) { - var self = this, - node = self.firstChild, - i, name; - - whitespace = whitespace || {}; - - if (node) { - do { - if (node.type === 1) { - // Ignore bogus elements - if (node.attributes.map['data-mce-bogus']) { - continue; - } - - // Keep empty elements like <img /> - if (elements[node.name]) { - return false; - } - - // Keep bookmark nodes and name attribute like <a name="1"></a> - i = node.attributes.length; - while (i--) { - name = node.attributes[i].name; - if (name === "name" || name.indexOf('data-mce-bookmark') === 0) { - return false; - } - } - } - - // Keep comments - if (node.type === 8) { - return false; - } - - // Keep non whitespace text nodes - if (node.type === 3 && !whiteSpaceRegExp.test(node.value)) { - return false; - } - - // Keep whitespace preserve elements - if (node.type === 3 && node.parent && whitespace[node.parent.name] && whiteSpaceRegExp.test(node.value)) { - return false; - } - } while ((node = walk(node, self))); - } - - return true; - }, - - /** - * Walks to the next or previous node and returns that node or null if it wasn't found. - * - * @method walk - * @param {Boolean} prev Optional previous node state defaults to false. - * @return {tinymce.html.Node} Node that is next to or previous of the current node. - */ - walk: function(prev) { - return walk(this, null, prev); - } - }; - - /** - * Creates a node of a specific type. - * - * @static - * @method create - * @param {String} name Name of the node type to create for example "b" or "#text". - * @param {Object} attrs Name/value collection of attributes that will be applied to elements. - */ - Node.create = function(name, attrs) { - var node, attrName; - - // Create node - node = new Node(name, typeLookup[name] || 1); - - // Add attributes if needed - if (attrs) { - for (attrName in attrs) { - node.attr(attrName, attrs[attrName]); - } - } - - return node; - }; - - return Node; - } - ); - - /** - * SaxParser.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /*eslint max-depth:[2, 9] */ - - /** - * This class parses HTML code using pure JavaScript and executes various events for each item it finds. It will - * always execute the events in the right order for tag soup code like <b><p></b></p>. It will also remove elements - * and attributes that doesn't fit the schema if the validate setting is enabled. - * - * @example - * var parser = new tinymce.html.SaxParser({ - * validate: true, - * - * comment: function(text) { - * console.log('Comment:', text); - * }, - * - * cdata: function(text) { - * console.log('CDATA:', text); - * }, - * - * text: function(text, raw) { - * console.log('Text:', text, 'Raw:', raw); - * }, - * - * start: function(name, attrs, empty) { - * console.log('Start:', name, attrs, empty); - * }, - * - * end: function(name) { - * console.log('End:', name); - * }, - * - * pi: function(name, text) { - * console.log('PI:', name, text); - * }, - * - * doctype: function(text) { - * console.log('DocType:', text); - * } - * }, schema); - * @class tinymce.html.SaxParser - * @version 3.4 - */ - define( - 'tinymce.core.html.SaxParser', [ - "tinymce.core.html.Schema", - "tinymce.core.html.Entities", - "tinymce.core.util.Tools" - ], - function(Schema, Entities, Tools) { - var each = Tools.each; - - var isValidPrefixAttrName = function(name) { - return name.indexOf('data-') === 0 || name.indexOf('aria-') === 0; - }; - - var trimComments = function(text) { - return text.replace(/<!--|-->/g, ''); - }; - - /** - * Returns the index of the end tag for a specific start tag. This can be - * used to skip all children of a parent element from being processed. - * - * @private - * @method findEndTag - * @param {tinymce.html.Schema} schema Schema instance to use to match short ended elements. - * @param {String} html HTML string to find the end tag in. - * @param {Number} startIndex Indext to start searching at should be after the start tag. - * @return {Number} Index of the end tag. - */ - function findEndTag(schema, html, startIndex) { - var count = 1, - index, matches, tokenRegExp, shortEndedElements; - - shortEndedElements = schema.getShortEndedElements(); - tokenRegExp = /<([!?\/])?([A-Za-z0-9\-_\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g; - tokenRegExp.lastIndex = index = startIndex; - - while ((matches = tokenRegExp.exec(html))) { - index = tokenRegExp.lastIndex; - - if (matches[1] === '/') { // End element - count--; - } else if (!matches[1]) { // Start element - if (matches[2] in shortEndedElements) { - continue; - } - - count++; - } - - if (count === 0) { - break; - } - } - - return index; - } - - /** - * Constructs a new SaxParser instance. - * - * @constructor - * @method SaxParser - * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks. - * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing. - */ - function SaxParser(settings, schema) { - var self = this; - - function noop() {} - - settings = settings || {}; - self.schema = schema = schema || new Schema(); - - if (settings.fix_self_closing !== false) { - settings.fix_self_closing = true; - } - - // Add handler functions from settings and setup default handlers - each('comment cdata text start end pi doctype'.split(' '), function(name) { - if (name) { - self[name] = settings[name] || noop; - } - }); - - /** - * Parses the specified HTML string and executes the callbacks for each item it finds. - * - * @example - * new SaxParser({...}).parse('<b>text</b>'); - * @method parse - * @param {String} html Html string to sax parse. - */ - self.parse = function(html) { - var self = this, - matches, index = 0, - value, endRegExp, stack = [], - attrList, i, text, name; - var isInternalElement, removeInternalElements, shortEndedElements, fillAttrsMap, isShortEnded; - var validate, elementRule, isValidElement, attr, attribsValue, validAttributesMap, validAttributePatterns; - var attributesRequired, attributesDefault, attributesForced, processHtml; - var anyAttributesRequired, selfClosing, tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0; - var decode = Entities.decode, - fixSelfClosing, filteredUrlAttrs = Tools.makeMap('src,href,data,background,formaction,poster'); - var scriptUriRegExp = /((java|vb)script|mhtml):/i, - dataUriRegExp = /^data:/i; - - function processEndTag(name) { - var pos, i; - - // Find position of parent of the same type - pos = stack.length; - while (pos--) { - if (stack[pos].name === name) { - break; - } - } - - // Found parent - if (pos >= 0) { - // Close all the open elements - for (i = stack.length - 1; i >= pos; i--) { - name = stack[i]; - - if (name.valid) { - self.end(name.name); - } - } - - // Remove the open elements from the stack - stack.length = pos; - } - } - - function parseAttribute(match, name, value, val2, val3) { - var attrRule, i, trimRegExp = /[\s\u0000-\u001F]+/g; - - name = name.toLowerCase(); - value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute - - // Validate name and value pass through all data- attributes - if (validate && !isInternalElement && isValidPrefixAttrName(name) === false) { - attrRule = validAttributesMap[name]; - - // Find rule by pattern matching - if (!attrRule && validAttributePatterns) { - i = validAttributePatterns.length; - while (i--) { - attrRule = validAttributePatterns[i]; - if (attrRule.pattern.test(name)) { - break; - } - } - - // No rule matched - if (i === -1) { - attrRule = null; - } - } - - // No attribute rule found - if (!attrRule) { - return; - } - - // Validate value - if (attrRule.validValues && !(value in attrRule.validValues)) { - return; - } - } - - // Block any javascript: urls or non image data uris - if (filteredUrlAttrs[name] && !settings.allow_script_urls) { - var uri = value.replace(trimRegExp, ''); - - try { - // Might throw malformed URI sequence - uri = decodeURIComponent(uri); - } catch (ex) { - // Fallback to non UTF-8 decoder - uri = unescape(uri); - } - - if (scriptUriRegExp.test(uri)) { - return; - } - - if (!settings.allow_html_data_urls && dataUriRegExp.test(uri) && !/^data:image\//i.test(uri)) { - return; - } - } - - // Block data or event attributes on elements marked as internal - if (isInternalElement && (name in filteredUrlAttrs || name.indexOf('on') === 0)) { - return; - } - - // Add attribute to list and map - attrList.map[name] = value; - attrList.push({ - name: name, - value: value - }); - } - - // Precompile RegExps and map objects - tokenRegExp = new RegExp('<(?:' + - '(?:!--([\\w\\W]*?)-->)|' + // Comment - '(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA - '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE - '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI - '(?:\\/([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)>)|' + // End element - '(?:([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element - ')', 'g'); - - attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g; - - // Setup lookup tables for empty elements and boolean attributes - shortEndedElements = schema.getShortEndedElements(); - selfClosing = settings.self_closing_elements || schema.getSelfClosingElements(); - fillAttrsMap = schema.getBoolAttrs(); - validate = settings.validate; - removeInternalElements = settings.remove_internals; - fixSelfClosing = settings.fix_self_closing; - specialElements = schema.getSpecialElements(); - processHtml = html + '>'; - - while ((matches = tokenRegExp.exec(processHtml))) { // Adds and extra '>' to keep regexps from doing catastrofic backtracking on malformed html - // Text - if (index < matches.index) { - self.text(decode(html.substr(index, matches.index - index))); - } - - if ((value = matches[6])) { // End element - value = value.toLowerCase(); - - // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements - if (value.charAt(0) === ':') { - value = value.substr(1); - } - - processEndTag(value); - } else if ((value = matches[7])) { // Start element - // Did we consume the extra character then treat it as text - // This handles the case with html like this: "text a<b text" - if (matches.index + matches[0].length > html.length) { - self.text(decode(html.substr(matches.index))); - index = matches.index + matches[0].length; - continue; - } - - value = value.toLowerCase(); - - // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements - if (value.charAt(0) === ':') { - value = value.substr(1); - } - - isShortEnded = value in shortEndedElements; - - // Is self closing tag for example an <li> after an open <li> - if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value) { - processEndTag(value); - } - - // Validate element - if (!validate || (elementRule = schema.getElementRule(value))) { - isValidElement = true; - - // Grab attributes map and patters when validation is enabled - if (validate) { - validAttributesMap = elementRule.attributes; - validAttributePatterns = elementRule.attributePatterns; - } - - // Parse attributes - if ((attribsValue = matches[8])) { - isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element - - // If the element has internal attributes then remove it if we are told to do so - if (isInternalElement && removeInternalElements) { - isValidElement = false; - } - - attrList = []; - attrList.map = {}; - - attribsValue.replace(attrRegExp, parseAttribute); - } else { - attrList = []; - attrList.map = {}; - } - - // Process attributes if validation is enabled - if (validate && !isInternalElement) { - attributesRequired = elementRule.attributesRequired; - attributesDefault = elementRule.attributesDefault; - attributesForced = elementRule.attributesForced; - anyAttributesRequired = elementRule.removeEmptyAttrs; - - // Check if any attribute exists - if (anyAttributesRequired && !attrList.length) { - isValidElement = false; - } - - // Handle forced attributes - if (attributesForced) { - i = attributesForced.length; - while (i--) { - attr = attributesForced[i]; - name = attr.name; - attrValue = attr.value; - - if (attrValue === '{$uid}') { - attrValue = 'mce_' + idCount++; - } - - attrList.map[name] = attrValue; - attrList.push({ - name: name, - value: attrValue - }); - } - } - - // Handle default attributes - if (attributesDefault) { - i = attributesDefault.length; - while (i--) { - attr = attributesDefault[i]; - name = attr.name; - - if (!(name in attrList.map)) { - attrValue = attr.value; - - if (attrValue === '{$uid}') { - attrValue = 'mce_' + idCount++; - } - - attrList.map[name] = attrValue; - attrList.push({ - name: name, - value: attrValue - }); - } - } - } - - // Handle required attributes - if (attributesRequired) { - i = attributesRequired.length; - while (i--) { - if (attributesRequired[i] in attrList.map) { - break; - } - } - - // None of the required attributes where found - if (i === -1) { - isValidElement = false; - } - } - - // Invalidate element if it's marked as bogus - if ((attr = attrList.map['data-mce-bogus'])) { - if (attr === 'all') { - index = findEndTag(schema, html, tokenRegExp.lastIndex); - tokenRegExp.lastIndex = index; - continue; - } - - isValidElement = false; - } - } - - if (isValidElement) { - self.start(value, attrList, isShortEnded); - } - } else { - isValidElement = false; - } - - // Treat script, noscript and style a bit different since they may include code that looks like elements - if ((endRegExp = specialElements[value])) { - endRegExp.lastIndex = index = matches.index + matches[0].length; - - if ((matches = endRegExp.exec(html))) { - if (isValidElement) { - text = html.substr(index, matches.index - index); - } - - index = matches.index + matches[0].length; - } else { - text = html.substr(index); - index = html.length; - } - - if (isValidElement) { - if (text.length > 0) { - self.text(text, true); - } - - self.end(value); - } - - tokenRegExp.lastIndex = index; - continue; - } - - // Push value on to stack - if (!isShortEnded) { - if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1) { - stack.push({ - name: value, - valid: isValidElement - }); - } else if (isValidElement) { - self.end(value); - } - } - } else if ((value = matches[1])) { // Comment - // Padd comment value to avoid browsers from parsing invalid comments as HTML - if (value.charAt(0) === '>') { - value = ' ' + value; - } - - if (!settings.allow_conditional_comments && value.substr(0, 3).toLowerCase() === '[if') { - value = ' ' + value; - } - - self.comment(value); - } else if ((value = matches[2])) { // CDATA - self.cdata(trimComments(value)); - } else if ((value = matches[3])) { // DOCTYPE - self.doctype(value); - } else if ((value = matches[4])) { // PI - self.pi(value, matches[5]); - } - - index = matches.index + matches[0].length; - } - - // Text - if (index < html.length) { - self.text(decode(html.substr(index))); - } - - // Close any open elements - for (i = stack.length - 1; i >= 0; i--) { - value = stack[i]; - - if (value.valid) { - self.end(value.name); - } - } - }; - } - - SaxParser.findEndTag = findEndTag; - - return SaxParser; - } - ); - /** - * DomParser.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class parses HTML code into a DOM like structure of nodes it will remove redundant whitespace and make - * sure that the node tree is valid according to the specified schema. - * So for example: <p>a<p>b</p>c</p> will become <p>a</p><p>b</p><p>c</p> - * - * @example - * var parser = new tinymce.html.DomParser({validate: true}, schema); - * var rootNode = parser.parse('<h1>content</h1>'); - * - * @class tinymce.html.DomParser - * @version 3.4 - */ - define( - 'tinymce.core.html.DomParser', [ - "tinymce.core.html.Node", - "tinymce.core.html.Schema", - "tinymce.core.html.SaxParser", - "tinymce.core.util.Tools" - ], - function(Node, Schema, SaxParser, Tools) { - var makeMap = Tools.makeMap, - each = Tools.each, - explode = Tools.explode, - extend = Tools.extend; - - var paddEmptyNode = function(settings, node) { - if (settings.padd_empty_with_br) { - node.empty().append(new Node('br', '1')).shortEnded = true; - } else { - node.empty().append(new Node('#text', '3')).value = '\u00a0'; - } - }; - - var hasOnlyChild = function(node, name) { - return node && node.firstChild === node.lastChild && node.firstChild.name === name; - }; - - /** - * Constructs a new DomParser instance. - * - * @constructor - * @method DomParser - * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks. - * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing. - */ - return function(settings, schema) { - var self = this, - nodeFilters = {}, - attributeFilters = [], - matchedNodes = {}, - matchedAttributes = {}; - - settings = settings || {}; - settings.validate = "validate" in settings ? settings.validate : true; - settings.root_name = settings.root_name || 'body'; - self.schema = schema = schema || new Schema(); - - function fixInvalidChildren(nodes) { - var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i; - var nonEmptyElements, whitespaceElements, nonSplitableElements, textBlockElements, specialElements, sibling, nextNode; - - nonSplitableElements = makeMap('tr,td,th,tbody,thead,tfoot,table'); - nonEmptyElements = schema.getNonEmptyElements(); - whitespaceElements = schema.getWhiteSpaceElements(); - textBlockElements = schema.getTextBlockElements(); - specialElements = schema.getSpecialElements(); - - for (ni = 0; ni < nodes.length; ni++) { - node = nodes[ni]; - - // Already removed or fixed - if (!node.parent || node.fixed) { - continue; - } - - // If the invalid element is a text block and the text block is within a parent LI element - // Then unwrap the first text block and convert other sibling text blocks to LI elements similar to Word/Open Office - if (textBlockElements[node.name] && node.parent.name == 'li') { - // Move sibling text blocks after LI element - sibling = node.next; - while (sibling) { - if (textBlockElements[sibling.name]) { - sibling.name = 'li'; - sibling.fixed = true; - node.parent.insert(sibling, node.parent); - } else { - break; - } - - sibling = sibling.next; - } - - // Unwrap current text block - node.unwrap(node); - continue; - } - - // Get list of all parent nodes until we find a valid parent to stick the child into - parents = [node]; - for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) && - !nonSplitableElements[parent.name]; parent = parent.parent) { - parents.push(parent); - } - - // Found a suitable parent - if (parent && parents.length > 1) { - // Reverse the array since it makes looping easier - parents.reverse(); - - // Clone the related parent and insert that after the moved node - newParent = currentNode = self.filterNode(parents[0].clone()); - - // Start cloning and moving children on the left side of the target node - for (i = 0; i < parents.length - 1; i++) { - if (schema.isValidChild(currentNode.name, parents[i].name)) { - tempNode = self.filterNode(parents[i].clone()); - currentNode.append(tempNode); - } else { - tempNode = currentNode; - } - - for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1];) { - nextNode = childNode.next; - tempNode.append(childNode); - childNode = nextNode; - } - - currentNode = tempNode; - } - - if (!newParent.isEmpty(nonEmptyElements, whitespaceElements)) { - parent.insert(newParent, parents[0], true); - parent.insert(node, newParent); - } else { - parent.insert(node, parents[0], true); - } - - // Check if the element is empty by looking through it's contents and special treatment for <p><br /></p> - parent = parents[0]; - if (parent.isEmpty(nonEmptyElements, whitespaceElements) || hasOnlyChild(parent, 'br')) { - parent.empty().remove(); - } - } else if (node.parent) { - // If it's an LI try to find a UL/OL for it or wrap it - if (node.name === 'li') { - sibling = node.prev; - if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) { - sibling.append(node); - continue; - } - - sibling = node.next; - if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) { - sibling.insert(node, sibling.firstChild, true); - continue; - } - - node.wrap(self.filterNode(new Node('ul', 1))); - continue; - } - - // Try wrapping the element in a DIV - if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) { - node.wrap(self.filterNode(new Node('div', 1))); - } else { - // We failed wrapping it, then remove or unwrap it - if (specialElements[node.name]) { - node.empty().remove(); - } else { - node.unwrap(); - } - } - } - } - } - - /** - * Runs the specified node though the element and attributes filters. - * - * @method filterNode - * @param {tinymce.html.Node} Node the node to run filters on. - * @return {tinymce.html.Node} The passed in node. - */ - self.filterNode = function(node) { - var i, name, list; - - // Run element filters - if (name in nodeFilters) { - list = matchedNodes[name]; - - if (list) { - list.push(node); - } else { - matchedNodes[name] = [node]; - } - } - - // Run attribute filters - i = attributeFilters.length; - while (i--) { - name = attributeFilters[i].name; - - if (name in node.attributes.map) { - list = matchedAttributes[name]; - - if (list) { - list.push(node); - } else { - matchedAttributes[name] = [node]; - } - } - } - - return node; - }; - - /** - * Adds a node filter function to the parser, the parser will collect the specified nodes by name - * and then execute the callback ones it has finished parsing the document. - * - * @example - * parser.addNodeFilter('p,h1', function(nodes, name) { - * for (var i = 0; i < nodes.length; i++) { - * console.log(nodes[i].name); - * } - * }); - * @method addNodeFilter - * @method {String} name Comma separated list of nodes to collect. - * @param {function} callback Callback function to execute once it has collected nodes. - */ - self.addNodeFilter = function(name, callback) { - each(explode(name), function(name) { - var list = nodeFilters[name]; - - if (!list) { - nodeFilters[name] = list = []; - } - - list.push(callback); - }); - }; - - /** - * Adds a attribute filter function to the parser, the parser will collect nodes that has the specified attributes - * and then execute the callback ones it has finished parsing the document. - * - * @example - * parser.addAttributeFilter('src,href', function(nodes, name) { - * for (var i = 0; i < nodes.length; i++) { - * console.log(nodes[i].name); - * } - * }); - * @method addAttributeFilter - * @method {String} name Comma separated list of nodes to collect. - * @param {function} callback Callback function to execute once it has collected nodes. - */ - self.addAttributeFilter = function(name, callback) { - each(explode(name), function(name) { - var i; - - for (i = 0; i < attributeFilters.length; i++) { - if (attributeFilters[i].name === name) { - attributeFilters[i].callbacks.push(callback); - return; - } - } - - attributeFilters.push({ - name: name, - callbacks: [callback] - }); - }); - }; - - /** - * Parses the specified HTML string into a DOM like node tree and returns the result. - * - * @example - * var rootNode = new DomParser({...}).parse('<b>text</b>'); - * @method parse - * @param {String} html Html string to sax parse. - * @param {Object} args Optional args object that gets passed to all filter functions. - * @return {tinymce.html.Node} Root node containing the tree. - */ - self.parse = function(html, args) { - var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate; - var blockElements, startWhiteSpaceRegExp, invalidChildren = [], - isInWhiteSpacePreservedElement; - var endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements; - var children, nonEmptyElements, rootBlockName; - - args = args || {}; - matchedNodes = {}; - matchedAttributes = {}; - blockElements = extend(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements()); - nonEmptyElements = schema.getNonEmptyElements(); - children = schema.children; - validate = settings.validate; - rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block; - - whiteSpaceElements = schema.getWhiteSpaceElements(); - startWhiteSpaceRegExp = /^[ \t\r\n]+/; - endWhiteSpaceRegExp = /[ \t\r\n]+$/; - allWhiteSpaceRegExp = /[ \t\r\n]+/g; - isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/; - - function addRootBlocks() { - var node = rootNode.firstChild, - next, rootBlockNode; - - // Removes whitespace at beginning and end of block so: - // <p> x </p> -> <p>x</p> - function trim(rootBlockNode) { - if (rootBlockNode) { - node = rootBlockNode.firstChild; - if (node && node.type == 3) { - node.value = node.value.replace(startWhiteSpaceRegExp, ''); - } - - node = rootBlockNode.lastChild; - if (node && node.type == 3) { - node.value = node.value.replace(endWhiteSpaceRegExp, ''); - } - } - } - - // Check if rootBlock is valid within rootNode for example if P is valid in H1 if H1 is the contentEditabe root - if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) { - return; - } - - while (node) { - next = node.next; - - if (node.type == 3 || (node.type == 1 && node.name !== 'p' && - !blockElements[node.name] && !node.attr('data-mce-type'))) { - if (!rootBlockNode) { - // Create a new root block element - rootBlockNode = createNode(rootBlockName, 1); - rootBlockNode.attr(settings.forced_root_block_attrs); - rootNode.insert(rootBlockNode, node); - rootBlockNode.append(node); - } else { - rootBlockNode.append(node); - } - } else { - trim(rootBlockNode); - rootBlockNode = null; - } - - node = next; - } - - trim(rootBlockNode); - } - - function createNode(name, type) { - var node = new Node(name, type), - list; - - if (name in nodeFilters) { - list = matchedNodes[name]; - - if (list) { - list.push(node); - } else { - matchedNodes[name] = [node]; - } - } - - return node; - } - - function removeWhitespaceBefore(node) { - var textNode, textNodeNext, textVal, sibling, blockElements = schema.getBlockElements(); - - for (textNode = node.prev; textNode && textNode.type === 3;) { - textVal = textNode.value.replace(endWhiteSpaceRegExp, ''); - - // Found a text node with non whitespace then trim that and break - if (textVal.length > 0) { - textNode.value = textVal; - return; - } - - textNodeNext = textNode.next; - - // Fix for bug #7543 where bogus nodes would produce empty - // text nodes and these would be removed if a nested list was before it - if (textNodeNext) { - if (textNodeNext.type == 3 && textNodeNext.value.length) { - textNode = textNode.prev; - continue; - } - - if (!blockElements[textNodeNext.name] && textNodeNext.name != 'script' && textNodeNext.name != 'style') { - textNode = textNode.prev; - continue; - } - } - - sibling = textNode.prev; - textNode.remove(); - textNode = sibling; - } - } - - function cloneAndExcludeBlocks(input) { - var name, output = {}; - - for (name in input) { - if (name !== 'li' && name != 'p') { - output[name] = input[name]; - } - } - - return output; - } - - parser = new SaxParser({ - validate: validate, - allow_script_urls: settings.allow_script_urls, - allow_conditional_comments: settings.allow_conditional_comments, - - // Exclude P and LI from DOM parsing since it's treated better by the DOM parser - self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()), - - cdata: function(text) { - node.append(createNode('#cdata', 4)).value = text; - }, - - text: function(text, raw) { - var textNode; - - // Trim all redundant whitespace on non white space elements - if (!isInWhiteSpacePreservedElement) { - text = text.replace(allWhiteSpaceRegExp, ' '); - - if (node.lastChild && blockElements[node.lastChild.name]) { - text = text.replace(startWhiteSpaceRegExp, ''); - } - } - - // Do we need to create the node - if (text.length !== 0) { - textNode = createNode('#text', 3); - textNode.raw = !!raw; - node.append(textNode).value = text; - } - }, - - comment: function(text) { - node.append(createNode('#comment', 8)).value = text; - }, - - pi: function(name, text) { - node.append(createNode(name, 7)).value = text; - removeWhitespaceBefore(node); - }, - - doctype: function(text) { - var newNode; - - newNode = node.append(createNode('#doctype', 10)); - newNode.value = text; - removeWhitespaceBefore(node); - }, - - start: function(name, attrs, empty) { - var newNode, attrFiltersLen, elementRule, attrName, parent; - - elementRule = validate ? schema.getElementRule(name) : {}; - if (elementRule) { - newNode = createNode(elementRule.outputName || name, 1); - newNode.attributes = attrs; - newNode.shortEnded = empty; - - node.append(newNode); - - // Check if node is valid child of the parent node is the child is - // unknown we don't collect it since it's probably a custom element - parent = children[node.name]; - if (parent && children[newNode.name] && !parent[newNode.name]) { - invalidChildren.push(newNode); - } - - attrFiltersLen = attributeFilters.length; - while (attrFiltersLen--) { - attrName = attributeFilters[attrFiltersLen].name; - - if (attrName in attrs.map) { - list = matchedAttributes[attrName]; - - if (list) { - list.push(newNode); - } else { - matchedAttributes[attrName] = [newNode]; - } - } - } - - // Trim whitespace before block - if (blockElements[name]) { - removeWhitespaceBefore(newNode); - } - - // Change current node if the element wasn't empty i.e not <br /> or <img /> - if (!empty) { - node = newNode; - } - - // Check if we are inside a whitespace preserved element - if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) { - isInWhiteSpacePreservedElement = true; - } - } - }, - - end: function(name) { - var textNode, elementRule, text, sibling, tempNode; - - elementRule = validate ? schema.getElementRule(name) : {}; - if (elementRule) { - if (blockElements[name]) { - if (!isInWhiteSpacePreservedElement) { - // Trim whitespace of the first node in a block - textNode = node.firstChild; - if (textNode && textNode.type === 3) { - text = textNode.value.replace(startWhiteSpaceRegExp, ''); - - // Any characters left after trim or should we remove it - if (text.length > 0) { - textNode.value = text; - textNode = textNode.next; - } else { - sibling = textNode.next; - textNode.remove(); - textNode = sibling; - - // Remove any pure whitespace siblings - while (textNode && textNode.type === 3) { - text = textNode.value; - sibling = textNode.next; - - if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) { - textNode.remove(); - textNode = sibling; - } - - textNode = sibling; - } - } - } - - // Trim whitespace of the last node in a block - textNode = node.lastChild; - if (textNode && textNode.type === 3) { - text = textNode.value.replace(endWhiteSpaceRegExp, ''); - - // Any characters left after trim or should we remove it - if (text.length > 0) { - textNode.value = text; - textNode = textNode.prev; - } else { - sibling = textNode.prev; - textNode.remove(); - textNode = sibling; - - // Remove any pure whitespace siblings - while (textNode && textNode.type === 3) { - text = textNode.value; - sibling = textNode.prev; - - if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) { - textNode.remove(); - textNode = sibling; - } - - textNode = sibling; - } - } - } - } - - // Trim start white space - // Removed due to: #5424 - /*textNode = node.prev; - if (textNode && textNode.type === 3) { - text = textNode.value.replace(startWhiteSpaceRegExp, ''); - - if (text.length > 0) - textNode.value = text; - else - textNode.remove(); - }*/ - } - - // Check if we exited a whitespace preserved element - if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) { - isInWhiteSpacePreservedElement = false; - } - - // Handle empty nodes - if (elementRule.removeEmpty || elementRule.paddEmpty) { - if (node.isEmpty(nonEmptyElements, whiteSpaceElements)) { - if (elementRule.paddEmpty) { - paddEmptyNode(settings, node); - } else { - // Leave nodes that have a name like <a name="name"> - if (!node.attributes.map.name && !node.attributes.map.id) { - tempNode = node.parent; - - if (blockElements[node.name]) { - node.empty().remove(); - } else { - node.unwrap(); - } - - node = tempNode; - return; - } - } - } - } - - node = node.parent; - } - } - }, schema); - - rootNode = node = new Node(args.context || settings.root_name, 11); - - parser.parse(html); - - // Fix invalid children or report invalid children in a contextual parsing - if (validate && invalidChildren.length) { - if (!args.context) { - fixInvalidChildren(invalidChildren); - } else { - args.invalid = true; - } - } - - // Wrap nodes in the root into block elements if the root is body - if (rootBlockName && (rootNode.name == 'body' || args.isRootContent)) { - addRootBlocks(); - } - - // Run filters only when the contents is valid - if (!args.invalid) { - // Run node filters - for (name in matchedNodes) { - list = nodeFilters[name]; - nodes = matchedNodes[name]; - - // Remove already removed children - fi = nodes.length; - while (fi--) { - if (!nodes[fi].parent) { - nodes.splice(fi, 1); - } - } - - for (i = 0, l = list.length; i < l; i++) { - list[i](nodes, name, args); - } - } - - // Run attribute filters - for (i = 0, l = attributeFilters.length; i < l; i++) { - list = attributeFilters[i]; - - if (list.name in matchedAttributes) { - nodes = matchedAttributes[list.name]; - - // Remove already removed children - fi = nodes.length; - while (fi--) { - if (!nodes[fi].parent) { - nodes.splice(fi, 1); - } - } - - for (fi = 0, fl = list.callbacks.length; fi < fl; fi++) { - list.callbacks[fi](nodes, list.name, args); - } - } - } - } - - return rootNode; - }; - - // Remove <br> at end of block elements Gecko and WebKit injects BR elements to - // make it possible to place the caret inside empty blocks. This logic tries to remove - // these elements and keep br elements that where intended to be there intact - if (settings.remove_trailing_brs) { - self.addNodeFilter('br', function(nodes) { - var i, l = nodes.length, - node, blockElements = extend({}, schema.getBlockElements()); - var nonEmptyElements = schema.getNonEmptyElements(), - parent, lastParent, prev, prevName; - var whiteSpaceElements = schema.getNonEmptyElements(); - var elementRule, textNode; - - // Remove brs from body element as well - blockElements.body = 1; - - // Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p> - for (i = 0; i < l; i++) { - node = nodes[i]; - parent = node.parent; - - if (blockElements[node.parent.name] && node === parent.lastChild) { - // Loop all nodes to the left of the current node and check for other BR elements - // excluding bookmarks since they are invisible - prev = node.prev; - while (prev) { - prevName = prev.name; - - // Ignore bookmarks - if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') { - // Found a non BR element - if (prevName !== "br") { - break; - } - - // Found another br it's a <br><br> structure then don't remove anything - if (prevName === 'br') { - node = null; - break; - } - } - - prev = prev.prev; - } - - if (node) { - node.remove(); - - // Is the parent to be considered empty after we removed the BR - if (parent.isEmpty(nonEmptyElements, whiteSpaceElements)) { - elementRule = schema.getElementRule(parent.name); - - // Remove or padd the element depending on schema rule - if (elementRule) { - if (elementRule.removeEmpty) { - parent.remove(); - } else if (elementRule.paddEmpty) { - paddEmptyNode(settings, parent); - } - } - } - } - } else { - // Replaces BR elements inside inline elements like <p><b><i><br></i></b></p> - // so they become <p><b><i> </i></b></p> - lastParent = node; - while (parent && parent.firstChild === lastParent && parent.lastChild === lastParent) { - lastParent = parent; - - if (blockElements[parent.name]) { - break; - } - - parent = parent.parent; - } - - if (lastParent === parent && settings.padd_empty_with_br !== true) { - textNode = new Node('#text', 3); - textNode.value = '\u00a0'; - node.replace(textNode); - } - } - } - }); - } - - - self.addAttributeFilter('href', function(nodes) { - var i = nodes.length, - node; - - var appendRel = function(rel) { - var parts = rel.split(' ').filter(function(p) { - return p.length > 0; - }); - return parts.concat(['noopener']).sort().join(' '); - }; - - var addNoOpener = function(rel) { - var newRel = rel ? Tools.trim(rel) : ''; - if (!/\b(noopener)\b/g.test(newRel)) { - return appendRel(newRel); - } else { - return newRel; - } - }; - - if (!settings.allow_unsafe_link_target) { - while (i--) { - node = nodes[i]; - if (node.name === 'a' && node.attr('target') === '_blank') { - node.attr('rel', addNoOpener(node.attr('rel'))); - } - } - } - }); - - // Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included. - if (!settings.allow_html_in_named_anchor) { - self.addAttributeFilter('id,name', function(nodes) { - var i = nodes.length, - sibling, prevSibling, parent, node; - - while (i--) { - node = nodes[i]; - if (node.name === 'a' && node.firstChild && !node.attr('href')) { - parent = node.parent; - - // Move children after current node - sibling = node.lastChild; - do { - prevSibling = sibling.prev; - parent.insert(sibling, node); - sibling = prevSibling; - } while (sibling); - } - } - }); - } - - if (settings.fix_list_elements) { - self.addNodeFilter('ul,ol', function(nodes) { - var i = nodes.length, - node, parentNode; - - while (i--) { - node = nodes[i]; - parentNode = node.parent; - - if (parentNode.name === 'ul' || parentNode.name === 'ol') { - if (node.prev && node.prev.name === 'li') { - node.prev.append(node); - } else { - var li = new Node('li', 1); - li.attr('style', 'list-style-type: none'); - node.wrap(li); - } - } - } - }); - } - - if (settings.validate && schema.getValidClasses()) { - self.addAttributeFilter('class', function(nodes) { - var i = nodes.length, - node, classList, ci, className, classValue; - var validClasses = schema.getValidClasses(), - validClassesMap, valid; - - while (i--) { - node = nodes[i]; - classList = node.attr('class').split(' '); - classValue = ''; - - for (ci = 0; ci < classList.length; ci++) { - className = classList[ci]; - valid = false; - - validClassesMap = validClasses['*']; - if (validClassesMap && validClassesMap[className]) { - valid = true; - } - - validClassesMap = validClasses[node.name]; - if (!valid && validClassesMap && validClassesMap[className]) { - valid = true; - } - - if (valid) { - if (classValue) { - classValue += ' '; - } - - classValue += className; - } - } - - if (!classValue.length) { - classValue = null; - } - - node.attr('class', classValue); - } - }); - } - }; - } - ); - - /** - * Writer.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class is used to write HTML tags out it can be used with the Serializer or the SaxParser. - * - * @class tinymce.html.Writer - * @example - * var writer = new tinymce.html.Writer({indent: true}); - * var parser = new tinymce.html.SaxParser(writer).parse('<p><br></p>'); - * console.log(writer.getContent()); - * - * @class tinymce.html.Writer - * @version 3.4 - */ - define( - 'tinymce.core.html.Writer', [ - "tinymce.core.html.Entities", - "tinymce.core.util.Tools" - ], - function(Entities, Tools) { - var makeMap = Tools.makeMap; - - /** - * Constructs a new Writer instance. - * - * @constructor - * @method Writer - * @param {Object} settings Name/value settings object. - */ - return function(settings) { - var html = [], - indent, indentBefore, indentAfter, encode, htmlOutput; - - settings = settings || {}; - indent = settings.indent; - indentBefore = makeMap(settings.indent_before || ''); - indentAfter = makeMap(settings.indent_after || ''); - encode = Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities); - htmlOutput = settings.element_format == "html"; - - return { - /** - * Writes the a start element such as <p id="a">. - * - * @method start - * @param {String} name Name of the element. - * @param {Array} attrs Optional attribute array or undefined if it hasn't any. - * @param {Boolean} empty Optional empty state if the tag should end like <br />. - */ - start: function(name, attrs, empty) { - var i, l, attr, value; - - if (indent && indentBefore[name] && html.length > 0) { - value = html[html.length - 1]; - - if (value.length > 0 && value !== '\n') { - html.push('\n'); - } - } - - html.push('<', name); - - if (attrs) { - for (i = 0, l = attrs.length; i < l; i++) { - attr = attrs[i]; - html.push(' ', attr.name, '="', encode(attr.value, true), '"'); - } - } - - if (!empty || htmlOutput) { - html[html.length] = '>'; - } else { - html[html.length] = ' />'; - } - - if (empty && indent && indentAfter[name] && html.length > 0) { - value = html[html.length - 1]; - - if (value.length > 0 && value !== '\n') { - html.push('\n'); - } - } - }, - - /** - * Writes the a end element such as </p>. - * - * @method end - * @param {String} name Name of the element. - */ - end: function(name) { - var value; - - /*if (indent && indentBefore[name] && html.length > 0) { - value = html[html.length - 1]; - - if (value.length > 0 && value !== '\n') - html.push('\n'); - }*/ - - html.push('</', name, '>'); - - if (indent && indentAfter[name] && html.length > 0) { - value = html[html.length - 1]; - - if (value.length > 0 && value !== '\n') { - html.push('\n'); - } - } - }, - - /** - * Writes a text node. - * - * @method text - * @param {String} text String to write out. - * @param {Boolean} raw Optional raw state if true the contents wont get encoded. - */ - text: function(text, raw) { - if (text.length > 0) { - html[html.length] = raw ? text : encode(text); - } - }, - - /** - * Writes a cdata node such as <![CDATA[data]]>. - * - * @method cdata - * @param {String} text String to write out inside the cdata. - */ - cdata: function(text) { - html.push('<![CDATA[', text, ']]>'); - }, - - /** - * Writes a comment node such as <!-- Comment -->. - * - * @method cdata - * @param {String} text String to write out inside the comment. - */ - comment: function(text) { - html.push('<!--', text, '-->'); - }, - - /** - * Writes a PI node such as <?xml attr="value" ?>. - * - * @method pi - * @param {String} name Name of the pi. - * @param {String} text String to write out inside the pi. - */ - pi: function(name, text) { - if (text) { - html.push('<?', name, ' ', encode(text), '?>'); - } else { - html.push('<?', name, '?>'); - } - - if (indent) { - html.push('\n'); - } - }, - - /** - * Writes a doctype node such as <!DOCTYPE data>. - * - * @method doctype - * @param {String} text String to write out inside the doctype. - */ - doctype: function(text) { - html.push('<!DOCTYPE', text, '>', indent ? '\n' : ''); - }, - - /** - * Resets the internal buffer if one wants to reuse the writer. - * - * @method reset - */ - reset: function() { - html.length = 0; - }, - - /** - * Returns the contents that got serialized. - * - * @method getContent - * @return {String} HTML contents that got written down. - */ - getContent: function() { - return html.join('').replace(/\n$/, ''); - } - }; - }; - } - ); - /** - * Serializer.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class is used to serialize down the DOM tree into a string using a Writer instance. - * - * - * @example - * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>')); - * @class tinymce.html.Serializer - * @version 3.4 - */ - define( - 'tinymce.core.html.Serializer', [ - "tinymce.core.html.Writer", - "tinymce.core.html.Schema" - ], - function(Writer, Schema) { - /** - * Constructs a new Serializer instance. - * - * @constructor - * @method Serializer - * @param {Object} settings Name/value settings object. - * @param {tinymce.html.Schema} schema Schema instance to use. - */ - return function(settings, schema) { - var self = this, - writer = new Writer(settings); - - settings = settings || {}; - settings.validate = "validate" in settings ? settings.validate : true; - - self.schema = schema = schema || new Schema(); - self.writer = writer; - - /** - * Serializes the specified node into a string. - * - * @example - * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>')); - * @method serialize - * @param {tinymce.html.Node} node Node instance to serialize. - * @return {String} String with HTML based on DOM tree. - */ - self.serialize = function(node) { - var handlers, validate; - - validate = settings.validate; - - handlers = { - // #text - 3: function(node) { - writer.text(node.value, node.raw); - }, - - // #comment - 8: function(node) { - writer.comment(node.value); - }, - - // Processing instruction - 7: function(node) { - writer.pi(node.name, node.value); - }, - - // Doctype - 10: function(node) { - writer.doctype(node.value); - }, - - // CDATA - 4: function(node) { - writer.cdata(node.value); - }, - - // Document fragment - 11: function(node) { - if ((node = node.firstChild)) { - do { - walk(node); - } while ((node = node.next)); - } - } - }; - - writer.reset(); - - function walk(node) { - var handler = handlers[node.type], - name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule; - - if (!handler) { - name = node.name; - isEmpty = node.shortEnded; - attrs = node.attributes; - - // Sort attributes - if (validate && attrs && attrs.length > 1) { - sortedAttrs = []; - sortedAttrs.map = {}; - - elementRule = schema.getElementRule(node.name); - if (elementRule) { - for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) { - attrName = elementRule.attributesOrder[i]; - - if (attrName in attrs.map) { - attrValue = attrs.map[attrName]; - sortedAttrs.map[attrName] = attrValue; - sortedAttrs.push({ - name: attrName, - value: attrValue - }); - } - } - - for (i = 0, l = attrs.length; i < l; i++) { - attrName = attrs[i].name; - - if (!(attrName in sortedAttrs.map)) { - attrValue = attrs.map[attrName]; - sortedAttrs.map[attrName] = attrValue; - sortedAttrs.push({ - name: attrName, - value: attrValue - }); - } - } - - attrs = sortedAttrs; - } - } - - writer.start(node.name, attrs, isEmpty); - - if (!isEmpty) { - if ((node = node.firstChild)) { - do { - walk(node); - } while ((node = node.next)); - } - - writer.end(name); - } - } else { - handler(node); - } - } - - // Serialize element and treat all non elements as fragments - if (node.type == 1 && !settings.inner) { - walk(node); - } else { - handlers[11](node); - } - - return writer.getContent(); - }; - }; - } - ); - - /** - * Serializer.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class is used to serialize DOM trees into a string. Consult the TinyMCE Wiki API for - * more details and examples on how to use this class. - * - * @class tinymce.dom.Serializer - */ - define( - 'tinymce.core.dom.Serializer', [ - "tinymce.core.dom.DOMUtils", - "tinymce.core.html.DomParser", - "tinymce.core.html.SaxParser", - "tinymce.core.html.Entities", - "tinymce.core.html.Serializer", - "tinymce.core.html.Node", - "tinymce.core.html.Schema", - "tinymce.core.Env", - "tinymce.core.util.Tools", - "tinymce.core.text.Zwsp" - ], - function(DOMUtils, DomParser, SaxParser, Entities, Serializer, Node, Schema, Env, Tools, Zwsp) { - var each = Tools.each, - trim = Tools.trim; - var DOM = DOMUtils.DOM; - - /** - * IE 11 has a fantastic bug where it will produce two trailing BR elements to iframe bodies when - * the iframe is hidden by display: none on a parent container. The DOM is actually out of sync - * with innerHTML in this case. It's like IE adds shadow DOM BR elements that appears on innerHTML - * but not as the lastChild of the body. So this fix simply removes the last two - * BR elements at the end of the document. - * - * Example of what happens: <body>text</body> becomes <body>text<br><br></body> - */ - function trimTrailingBr(rootNode) { - var brNode1, brNode2; - - function isBr(node) { - return node && node.name === 'br'; - } - - brNode1 = rootNode.lastChild; - if (isBr(brNode1)) { - brNode2 = brNode1.prev; - - if (isBr(brNode2)) { - brNode1.remove(); - brNode2.remove(); - } - } - } - - /** - * Constructs a new DOM serializer class. - * - * @constructor - * @method Serializer - * @param {Object} settings Serializer settings object. - * @param {tinymce.Editor} editor Optional editor to bind events to and get schema/dom from. - */ - return function(settings, editor) { - var dom, schema, htmlParser, tempAttrs = ["data-mce-selected"]; - - if (editor) { - dom = editor.dom; - schema = editor.schema; - } - - function trimHtml(html) { - var trimContentRegExp = new RegExp([ - '<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\\/span>', // Trim bogus spans like caret containers - '\\s?(' + tempAttrs.join('|') + ')="[^"]+"' // Trim temporaty data-mce prefixed attributes like data-mce-selected - ].join('|'), 'gi'); - - html = Zwsp.trim(html.replace(trimContentRegExp, '')); - - return html; - } - - function trimContent(html) { - var content = html; - var bogusAllRegExp = /<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g; - var endTagIndex, index, matchLength, matches, shortEndedElements, schema = editor.schema; - - content = trimHtml(content); - shortEndedElements = schema.getShortEndedElements(); - - // Remove all bogus elements marked with "all" - while ((matches = bogusAllRegExp.exec(content))) { - index = bogusAllRegExp.lastIndex; - matchLength = matches[0].length; - - if (shortEndedElements[matches[1]]) { - endTagIndex = index; - } else { - endTagIndex = SaxParser.findEndTag(schema, content, index); - } - - content = content.substring(0, index - matchLength) + content.substring(endTagIndex); - bogusAllRegExp.lastIndex = index - matchLength; - } - - return content; - } - - /** - * Returns a trimmed version of the editor contents to be used for the undo level. This - * will remove any data-mce-bogus="all" marked elements since these are used for UI it will also - * remove the data-mce-selected attributes used for selection of objects and caret containers. - * It will keep all data-mce-bogus="1" elements since these can be used to place the caret etc and will - * be removed by the serialization logic when you save. - * - * @private - * @return {String} HTML contents of the editor excluding some internal bogus elements. - */ - function getTrimmedContent() { - return trimContent(editor.getBody().innerHTML); - } - - function addTempAttr(name) { - if (Tools.inArray(tempAttrs, name) === -1) { - htmlParser.addAttributeFilter(name, function(nodes, name) { - var i = nodes.length; - - while (i--) { - nodes[i].attr(name, null); - } - }); - - tempAttrs.push(name); - } - } - - // Default DOM and Schema if they are undefined - dom = dom || DOM; - schema = schema || new Schema(settings); - settings.entity_encoding = settings.entity_encoding || 'named'; - settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true; - - htmlParser = new DomParser(settings, schema); - - // Convert tabindex back to elements when serializing contents - htmlParser.addAttributeFilter('data-mce-tabindex', function(nodes, name) { - var i = nodes.length, - node; - - while (i--) { - node = nodes[i]; - node.attr('tabindex', node.attributes.map['data-mce-tabindex']); - node.attr(name, null); - } - }); - - // Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed - htmlParser.addAttributeFilter('src,href,style', function(nodes, name) { - var i = nodes.length, - node, value, internalName = 'data-mce-' + name; - var urlConverter = settings.url_converter, - urlConverterScope = settings.url_converter_scope, - undef; - - while (i--) { - node = nodes[i]; - - value = node.attributes.map[internalName]; - if (value !== undef) { - // Set external name to internal value and remove internal - node.attr(name, value.length > 0 ? value : null); - node.attr(internalName, null); - } else { - // No internal attribute found then convert the value we have in the DOM - value = node.attributes.map[name]; - - if (name === "style") { - value = dom.serializeStyle(dom.parseStyle(value), node.name); - } else if (urlConverter) { - value = urlConverter.call(urlConverterScope, value, name, node.name); - } - - node.attr(name, value.length > 0 ? value : null); - } - } - }); - - // Remove internal classes mceItem<..> or mceSelected - htmlParser.addAttributeFilter('class', function(nodes) { - var i = nodes.length, - node, value; - - while (i--) { - node = nodes[i]; - value = node.attr('class'); - - if (value) { - value = node.attr('class').replace(/(?:^|\s)mce-item-\w+(?!\S)/g, ''); - node.attr('class', value.length > 0 ? value : null); - } - } - }); - - // Remove bookmark elements - htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) { - var i = nodes.length, - node; - - while (i--) { - node = nodes[i]; - - if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup) { - node.remove(); - } - } - }); - - htmlParser.addNodeFilter('noscript', function(nodes) { - var i = nodes.length, - node; - - while (i--) { - node = nodes[i].firstChild; - - if (node) { - node.value = Entities.decode(node.value); - } - } - }); - - // Force script into CDATA sections and remove the mce- prefix also add comments around styles - htmlParser.addNodeFilter('script,style', function(nodes, name) { - var i = nodes.length, - node, value, type; - - function trim(value) { - /*jshint maxlen:255 */ - /*eslint max-len:0 */ - return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n') - .replace(/^[\r\n]*|[\r\n]*$/g, '') - .replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '') - .replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, ''); - } - - while (i--) { - node = nodes[i]; - value = node.firstChild ? node.firstChild.value : ''; - - if (name === "script") { - // Remove mce- prefix from script elements and remove default type since the user specified - // a script element without type attribute - type = node.attr('type'); - if (type) { - node.attr('type', type == 'mce-no/type' ? null : type.replace(/^mce\-/, '')); - } - - if (value.length > 0) { - node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>'; - } - } else { - if (value.length > 0) { - node.firstChild.value = '<!--\n' + trim(value) + '\n-->'; - } - } - } - }); - - // Convert comments to cdata and handle protected comments - htmlParser.addNodeFilter('#comment', function(nodes) { - var i = nodes.length, - node; - - while (i--) { - node = nodes[i]; - - if (node.value.indexOf('[CDATA[') === 0) { - node.name = '#cdata'; - node.type = 4; - node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, ''); - } else if (node.value.indexOf('mce:protected ') === 0) { - node.name = "#text"; - node.type = 3; - node.raw = true; - node.value = unescape(node.value).substr(14); - } - } - }); - - htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) { - var i = nodes.length, - node; - - while (i--) { - node = nodes[i]; - if (node.type === 7) { - node.remove(); - } else if (node.type === 1) { - if (name === "input" && !("type" in node.attributes.map)) { - node.attr('type', 'text'); - } - } - } - }); - - // Remove internal data attributes - htmlParser.addAttributeFilter( - 'data-mce-src,data-mce-href,data-mce-style,' + - 'data-mce-selected,data-mce-expando,' + - 'data-mce-type,data-mce-resize', - - function(nodes, name) { - var i = nodes.length; - - while (i--) { - nodes[i].attr(name, null); - } - } - ); - - // Return public methods - return { - /** - * Schema instance that was used to when the Serializer was constructed. - * - * @field {tinymce.html.Schema} schema - */ - schema: schema, - - /** - * Adds a node filter function to the parser used by the serializer, the parser will collect the specified nodes by name - * and then execute the callback ones it has finished parsing the document. - * - * @example - * parser.addNodeFilter('p,h1', function(nodes, name) { - * for (var i = 0; i < nodes.length; i++) { - * console.log(nodes[i].name); - * } - * }); - * @method addNodeFilter - * @method {String} name Comma separated list of nodes to collect. - * @param {function} callback Callback function to execute once it has collected nodes. - */ - addNodeFilter: htmlParser.addNodeFilter, - - /** - * Adds a attribute filter function to the parser used by the serializer, the parser will - * collect nodes that has the specified attributes - * and then execute the callback ones it has finished parsing the document. - * - * @example - * parser.addAttributeFilter('src,href', function(nodes, name) { - * for (var i = 0; i < nodes.length; i++) { - * console.log(nodes[i].name); - * } - * }); - * @method addAttributeFilter - * @method {String} name Comma separated list of nodes to collect. - * @param {function} callback Callback function to execute once it has collected nodes. - */ - addAttributeFilter: htmlParser.addAttributeFilter, - - /** - * Serializes the specified browser DOM node into a HTML string. - * - * @method serialize - * @param {DOMNode} node DOM node to serialize. - * @param {Object} args Arguments option that gets passed to event handlers. - */ - serialize: function(node, args) { - var self = this, - impl, doc, oldDoc, htmlSerializer, content, rootNode; - - // Explorer won't clone contents of script and style and the - // selected index of select elements are cleared on a clone operation. - if (Env.ie && dom.select('script,style,select,map').length > 0) { - content = node.innerHTML; - node = node.cloneNode(false); - dom.setHTML(node, content); - } else { - node = node.cloneNode(true); - } - - // Nodes needs to be attached to something in WebKit/Opera - // This fix will make DOM ranges and make Sizzle happy! - impl = document.implementation; - if (impl.createHTMLDocument) { - // Create an empty HTML document - doc = impl.createHTMLDocument(""); - - // Add the element or it's children if it's a body element to the new document - each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) { - doc.body.appendChild(doc.importNode(node, true)); - }); - - // Grab first child or body element for serialization - if (node.nodeName != 'BODY') { - node = doc.body.firstChild; - } else { - node = doc.body; - } - - // set the new document in DOMUtils so createElement etc works - oldDoc = dom.doc; - dom.doc = doc; - } - - args = args || {}; - args.format = args.format || 'html'; - - // Don't wrap content if we want selected html - if (args.selection) { - args.forced_root_block = ''; - } - - // Pre process - if (!args.no_events) { - args.node = node; - self.onPreProcess(args); - } - - // Parse HTML - rootNode = htmlParser.parse(trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args); - trimTrailingBr(rootNode); - - // Serialize HTML - htmlSerializer = new Serializer(settings, schema); - args.content = htmlSerializer.serialize(rootNode); - - // Replace all BOM characters for now until we can find a better solution - if (!args.cleanup) { - args.content = Zwsp.trim(args.content); - args.content = args.content.replace(/\uFEFF/g, ''); - } - - // Post process - if (!args.no_events) { - self.onPostProcess(args); - } - - // Restore the old document if it was changed - if (oldDoc) { - dom.doc = oldDoc; - } - - args.node = null; - - return args.content; - }, - - /** - * Adds valid elements rules to the serializers schema instance this enables you to specify things - * like what elements should be outputted and what attributes specific elements might have. - * Consult the Wiki for more details on this format. - * - * @method addRules - * @param {String} rules Valid elements rules string to add to schema. - */ - addRules: function(rules) { - schema.addValidElements(rules); - }, - - /** - * Sets the valid elements rules to the serializers schema instance this enables you to specify things - * like what elements should be outputted and what attributes specific elements might have. - * Consult the Wiki for more details on this format. - * - * @method setRules - * @param {String} rules Valid elements rules string. - */ - setRules: function(rules) { - schema.setValidElements(rules); - }, - - onPreProcess: function(args) { - if (editor) { - editor.fire('PreProcess', args); - } - }, - - onPostProcess: function(args) { - if (editor) { - editor.fire('PostProcess', args); - } - }, - - /** - * Adds a temporary internal attribute these attributes will get removed on undo and - * when getting contents out of the editor. - * - * @method addTempAttr - * @param {String} name string - */ - addTempAttr: addTempAttr, - - // Internal - trimHtml: trimHtml, - getTrimmedContent: getTrimmedContent, - trimContent: trimContent - }; - }; - } - ); - - /** - * VK.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This file exposes a set of the common KeyCodes for use. Please grow it as needed. - */ - define( - 'tinymce.core.util.VK', [ - "tinymce.core.Env" - ], - function(Env) { - return { - BACKSPACE: 8, - DELETE: 46, - DOWN: 40, - ENTER: 13, - LEFT: 37, - RIGHT: 39, - SPACEBAR: 32, - TAB: 9, - UP: 38, - - modifierPressed: function(e) { - return e.shiftKey || e.ctrlKey || e.altKey || this.metaKeyPressed(e); - }, - - metaKeyPressed: function(e) { - // Check if ctrl or meta key is pressed. Edge case for AltGr on Windows where it produces ctrlKey+altKey states - return (Env.mac ? e.metaKey : e.ctrlKey && !e.altKey); - } - }; - } - ); - - /** - * ClientRect.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Utility functions for working with client rects. - * - * @private - * @class tinymce.geom.ClientRect - */ - define( - 'tinymce.core.geom.ClientRect', [], - function() { - var round = Math.round; - - function clone(rect) { - if (!rect) { - return { - left: 0, - top: 0, - bottom: 0, - right: 0, - width: 0, - height: 0 - }; - } - - return { - left: round(rect.left), - top: round(rect.top), - bottom: round(rect.bottom), - right: round(rect.right), - width: round(rect.width), - height: round(rect.height) - }; - } - - function collapse(clientRect, toStart) { - clientRect = clone(clientRect); - - if (toStart) { - clientRect.right = clientRect.left; - } else { - clientRect.left = clientRect.left + clientRect.width; - clientRect.right = clientRect.left; - } - - clientRect.width = 0; - - return clientRect; - } - - function isEqual(rect1, rect2) { - return ( - rect1.left === rect2.left && - rect1.top === rect2.top && - rect1.bottom === rect2.bottom && - rect1.right === rect2.right - ); - } - - function isValidOverflow(overflowY, clientRect1, clientRect2) { - return overflowY >= 0 && overflowY <= Math.min(clientRect1.height, clientRect2.height) / 2; - - } - - function isAbove(clientRect1, clientRect2) { - if ((clientRect1.bottom - clientRect1.height / 2) < clientRect2.top) { - return true; - } - - if (clientRect1.top > clientRect2.bottom) { - return false; - } - - return isValidOverflow(clientRect2.top - clientRect1.bottom, clientRect1, clientRect2); - } - - function isBelow(clientRect1, clientRect2) { - if (clientRect1.top > clientRect2.bottom) { - return true; - } - - if (clientRect1.bottom < clientRect2.top) { - return false; - } - - return isValidOverflow(clientRect2.bottom - clientRect1.top, clientRect1, clientRect2); - } - - function isLeft(clientRect1, clientRect2) { - return clientRect1.left < clientRect2.left; - } - - function isRight(clientRect1, clientRect2) { - return clientRect1.right > clientRect2.right; - } - - function compare(clientRect1, clientRect2) { - if (isAbove(clientRect1, clientRect2)) { - return -1; - } - - if (isBelow(clientRect1, clientRect2)) { - return 1; - } - - if (isLeft(clientRect1, clientRect2)) { - return -1; - } - - if (isRight(clientRect1, clientRect2)) { - return 1; - } - - return 0; - } - - function containsXY(clientRect, clientX, clientY) { - return ( - clientX >= clientRect.left && - clientX <= clientRect.right && - clientY >= clientRect.top && - clientY <= clientRect.bottom - ); - } - - return { - clone: clone, - collapse: collapse, - isEqual: isEqual, - isAbove: isAbove, - isBelow: isBelow, - isLeft: isLeft, - isRight: isRight, - compare: compare, - containsXY: containsXY - }; - } - ); - - /** - * RangePoint.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.dom.RangePoint', [ - 'ephox.katamari.api.Arr', - 'tinymce.core.geom.ClientRect' - ], - function(Arr, ClientRect) { - var isXYWithinRange = function(clientX, clientY, range) { - if (range.collapsed) { - return false; - } - - return Arr.foldl(range.getClientRects(), function(state, rect) { - return state || ClientRect.containsXY(rect, clientX, clientY); - }, false); - }; - - return { - isXYWithinRange: isXYWithinRange - }; - } - ); - /** - * ControlSelection.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class handles control selection of elements. Controls are elements - * that can be resized and needs to be selected as a whole. It adds custom resize handles - * to all browser engines that support properly disabling the built in resize logic. - * - * @class tinymce.dom.ControlSelection - */ - define( - 'tinymce.core.dom.ControlSelection', [ - 'ephox.katamari.api.Fun', - 'tinymce.core.dom.NodeType', - 'tinymce.core.dom.RangePoint', - 'tinymce.core.Env', - 'tinymce.core.util.Delay', - 'tinymce.core.util.Tools', - 'tinymce.core.util.VK' - ], - function(Fun, NodeType, RangePoint, Env, Delay, Tools, VK) { - var isContentEditableFalse = NodeType.isContentEditableFalse; - var isContentEditableTrue = NodeType.isContentEditableTrue; - - function getContentEditableRoot(root, node) { - while (node && node != root) { - if (isContentEditableTrue(node) || isContentEditableFalse(node)) { - return node; - } - - node = node.parentNode; - } - - return null; - } - - var isImage = function(elm) { - return elm && elm.nodeName === 'IMG'; - }; - - var isEventOnImageOutsideRange = function(evt, range) { - return isImage(evt.target) && !RangePoint.isXYWithinRange(evt.clientX, evt.clientY, range); - }; - - var contextMenuSelectImage = function(editor, evt) { - var target = evt.target; - - if (isEventOnImageOutsideRange(evt, editor.selection.getRng()) && !evt.isDefaultPrevented()) { - evt.preventDefault(); - editor.selection.select(target); - } - }; - - return function(selection, editor) { - var dom = editor.dom, - each = Tools.each; - var selectedElm, selectedElmGhost, resizeHelper, resizeHandles, selectedHandle, lastMouseDownEvent; - var startX, startY, selectedElmX, selectedElmY, startW, startH, ratio, resizeStarted; - var width, height, editableDoc = editor.getDoc(), - rootDocument = document, - isIE = Env.ie && Env.ie < 11; - var abs = Math.abs, - round = Math.round, - rootElement = editor.getBody(), - startScrollWidth, startScrollHeight; - - // Details about each resize handle how to scale etc - resizeHandles = { - // Name: x multiplier, y multiplier, delta size x, delta size y - /*n: [0.5, 0, 0, -1], - e: [1, 0.5, 1, 0], - s: [0.5, 1, 0, 1], - w: [0, 0.5, -1, 0],*/ - nw: [0, 0, -1, -1], - ne: [1, 0, 1, -1], - se: [1, 1, 1, 1], - sw: [0, 1, -1, 1] - }; - - // Add CSS for resize handles, cloned element and selected - var rootClass = '.mce-content-body'; - editor.contentStyles.push( - rootClass + ' div.mce-resizehandle {' + - 'position: absolute;' + - 'border: 1px solid black;' + - 'box-sizing: box-sizing;' + - 'background: #FFF;' + - 'width: 7px;' + - 'height: 7px;' + - 'z-index: 10000' + - '}' + - rootClass + ' .mce-resizehandle:hover {' + - 'background: #000' + - '}' + - rootClass + ' img[data-mce-selected],' + rootClass + ' hr[data-mce-selected] {' + - 'outline: 1px solid black;' + - 'resize: none' + // Have been talks about implementing this in browsers - '}' + - rootClass + ' .mce-clonedresizable {' + - 'position: absolute;' + - (Env.gecko ? '' : 'outline: 1px dashed black;') + // Gecko produces trails while resizing - 'opacity: .5;' + - 'filter: alpha(opacity=50);' + - 'z-index: 10000' + - '}' + - rootClass + ' .mce-resize-helper {' + - 'background: #555;' + - 'background: rgba(0,0,0,0.75);' + - 'border-radius: 3px;' + - 'border: 1px;' + - 'color: white;' + - 'display: none;' + - 'font-family: sans-serif;' + - 'font-size: 12px;' + - 'white-space: nowrap;' + - 'line-height: 14px;' + - 'margin: 5px 10px;' + - 'padding: 5px;' + - 'position: absolute;' + - 'z-index: 10001' + - '}' - ); - - function isResizable(elm) { - var selector = editor.settings.object_resizing; - - if (selector === false || Env.iOS) { - return false; - } - - if (typeof selector != 'string') { - selector = 'table,img,div'; - } - - if (elm.getAttribute('data-mce-resize') === 'false') { - return false; - } - - if (elm == editor.getBody()) { - return false; - } - - return editor.dom.is(elm, selector); - } - - function resizeGhostElement(e) { - var deltaX, deltaY, proportional; - var resizeHelperX, resizeHelperY; - - // Calc new width/height - deltaX = e.screenX - startX; - deltaY = e.screenY - startY; - - // Calc new size - width = deltaX * selectedHandle[2] + startW; - height = deltaY * selectedHandle[3] + startH; - - // Never scale down lower than 5 pixels - width = width < 5 ? 5 : width; - height = height < 5 ? 5 : height; - - if (selectedElm.nodeName == "IMG" && editor.settings.resize_img_proportional !== false) { - proportional = !VK.modifierPressed(e); - } else { - proportional = VK.modifierPressed(e) || (selectedElm.nodeName == "IMG" && selectedHandle[2] * selectedHandle[3] !== 0); - } - - // Constrain proportions - if (proportional) { - if (abs(deltaX) > abs(deltaY)) { - height = round(width * ratio); - width = round(height / ratio); - } else { - width = round(height / ratio); - height = round(width * ratio); - } - } - - // Update ghost size - dom.setStyles(selectedElmGhost, { - width: width, - height: height - }); - - // Update resize helper position - resizeHelperX = selectedHandle.startPos.x + deltaX; - resizeHelperY = selectedHandle.startPos.y + deltaY; - resizeHelperX = resizeHelperX > 0 ? resizeHelperX : 0; - resizeHelperY = resizeHelperY > 0 ? resizeHelperY : 0; - - dom.setStyles(resizeHelper, { - left: resizeHelperX, - top: resizeHelperY, - display: 'block' - }); - - resizeHelper.innerHTML = width + ' × ' + height; - - // Update ghost X position if needed - if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) { - dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width)); - } - - // Update ghost Y position if needed - if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) { - dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height)); - } - - // Calculate how must overflow we got - deltaX = rootElement.scrollWidth - startScrollWidth; - deltaY = rootElement.scrollHeight - startScrollHeight; - - // Re-position the resize helper based on the overflow - if (deltaX + deltaY !== 0) { - dom.setStyles(resizeHelper, { - left: resizeHelperX - deltaX, - top: resizeHelperY - deltaY - }); - } - - if (!resizeStarted) { - editor.fire('ObjectResizeStart', { - target: selectedElm, - width: startW, - height: startH - }); - resizeStarted = true; - } - } - - function endGhostResize() { - resizeStarted = false; - - function setSizeProp(name, value) { - if (value) { - // Resize by using style or attribute - if (selectedElm.style[name] || !editor.schema.isValid(selectedElm.nodeName.toLowerCase(), name)) { - dom.setStyle(selectedElm, name, value); - } else { - dom.setAttrib(selectedElm, name, value); - } - } - } - - // Set width/height properties - setSizeProp('width', width); - setSizeProp('height', height); - - dom.unbind(editableDoc, 'mousemove', resizeGhostElement); - dom.unbind(editableDoc, 'mouseup', endGhostResize); - - if (rootDocument != editableDoc) { - dom.unbind(rootDocument, 'mousemove', resizeGhostElement); - dom.unbind(rootDocument, 'mouseup', endGhostResize); - } - - // Remove ghost/helper and update resize handle positions - dom.remove(selectedElmGhost); - dom.remove(resizeHelper); - - if (!isIE || selectedElm.nodeName == "TABLE") { - showResizeRect(selectedElm); - } - - editor.fire('ObjectResized', { - target: selectedElm, - width: width, - height: height - }); - dom.setAttrib(selectedElm, 'style', dom.getAttrib(selectedElm, 'style')); - editor.nodeChanged(); - } - - function showResizeRect(targetElm, mouseDownHandleName, mouseDownEvent) { - var position, targetWidth, targetHeight, e, rect; - - hideResizeRect(); - unbindResizeHandleEvents(); - - // Get position and size of target - position = dom.getPos(targetElm, rootElement); - selectedElmX = position.x; - selectedElmY = position.y; - rect = targetElm.getBoundingClientRect(); // Fix for Gecko offsetHeight for table with caption - targetWidth = rect.width || (rect.right - rect.left); - targetHeight = rect.height || (rect.bottom - rect.top); - - // Reset width/height if user selects a new image/table - if (selectedElm != targetElm) { - detachResizeStartListener(); - selectedElm = targetElm; - width = height = 0; - } - - // Makes it possible to disable resizing - e = editor.fire('ObjectSelected', { - target: targetElm - }); - - if (isResizable(targetElm) && !e.isDefaultPrevented()) { - each(resizeHandles, function(handle, name) { - var handleElm; - - function startDrag(e) { - startX = e.screenX; - startY = e.screenY; - startW = selectedElm.clientWidth; - startH = selectedElm.clientHeight; - ratio = startH / startW; - selectedHandle = handle; - - handle.startPos = { - x: targetWidth * handle[0] + selectedElmX, - y: targetHeight * handle[1] + selectedElmY - }; - - startScrollWidth = rootElement.scrollWidth; - startScrollHeight = rootElement.scrollHeight; - - selectedElmGhost = selectedElm.cloneNode(true); - dom.addClass(selectedElmGhost, 'mce-clonedresizable'); - dom.setAttrib(selectedElmGhost, 'data-mce-bogus', 'all'); - selectedElmGhost.contentEditable = false; // Hides IE move layer cursor - selectedElmGhost.unSelectabe = true; - dom.setStyles(selectedElmGhost, { - left: selectedElmX, - top: selectedElmY, - margin: 0 - }); - - selectedElmGhost.removeAttribute('data-mce-selected'); - rootElement.appendChild(selectedElmGhost); - - dom.bind(editableDoc, 'mousemove', resizeGhostElement); - dom.bind(editableDoc, 'mouseup', endGhostResize); - - if (rootDocument != editableDoc) { - dom.bind(rootDocument, 'mousemove', resizeGhostElement); - dom.bind(rootDocument, 'mouseup', endGhostResize); - } - - resizeHelper = dom.add(rootElement, 'div', { - 'class': 'mce-resize-helper', - 'data-mce-bogus': 'all' - }, startW + ' × ' + startH); - } - - if (mouseDownHandleName) { - // Drag started by IE native resizestart - if (name == mouseDownHandleName) { - startDrag(mouseDownEvent); - } - - return; - } - - // Get existing or render resize handle - handleElm = dom.get('mceResizeHandle' + name); - if (handleElm) { - dom.remove(handleElm); - } - - handleElm = dom.add(rootElement, 'div', { - id: 'mceResizeHandle' + name, - 'data-mce-bogus': 'all', - 'class': 'mce-resizehandle', - unselectable: true, - style: 'cursor:' + name + '-resize; margin:0; padding:0' - }); - - // Hides IE move layer cursor - // If we set it on Chrome we get this wounderful bug: #6725 - if (Env.ie) { - handleElm.contentEditable = false; - } - - dom.bind(handleElm, 'mousedown', function(e) { - e.stopImmediatePropagation(); - e.preventDefault(); - startDrag(e); - }); - - handle.elm = handleElm; - - // Position element - dom.setStyles(handleElm, { - left: (targetWidth * handle[0] + selectedElmX) - (handleElm.offsetWidth / 2), - top: (targetHeight * handle[1] + selectedElmY) - (handleElm.offsetHeight / 2) - }); - }); - } else { - hideResizeRect(); - } - - selectedElm.setAttribute('data-mce-selected', '1'); - } - - function hideResizeRect() { - var name, handleElm; - - unbindResizeHandleEvents(); - - if (selectedElm) { - selectedElm.removeAttribute('data-mce-selected'); - } - - for (name in resizeHandles) { - handleElm = dom.get('mceResizeHandle' + name); - if (handleElm) { - dom.unbind(handleElm); - dom.remove(handleElm); - } - } - } - - function updateResizeRect(e) { - var startElm, controlElm; - - function isChildOrEqual(node, parent) { - if (node) { - do { - if (node === parent) { - return true; - } - } while ((node = node.parentNode)); - } - } - - // Ignore all events while resizing or if the editor instance was removed - if (resizeStarted || editor.removed) { - return; - } - - // Remove data-mce-selected from all elements since they might have been copied using Ctrl+c/v - each(dom.select('img[data-mce-selected],hr[data-mce-selected]'), function(img) { - img.removeAttribute('data-mce-selected'); - }); - - controlElm = e.type == 'mousedown' ? e.target : selection.getNode(); - controlElm = dom.$(controlElm).closest(isIE ? 'table' : 'table,img,hr')[0]; - - if (isChildOrEqual(controlElm, rootElement)) { - disableGeckoResize(); - startElm = selection.getStart(true); - - if (isChildOrEqual(startElm, controlElm) && isChildOrEqual(selection.getEnd(true), controlElm)) { - if (!isIE || (controlElm != startElm && startElm.nodeName !== 'IMG')) { - showResizeRect(controlElm); - return; - } - } - } - - hideResizeRect(); - } - - function attachEvent(elm, name, func) { - if (elm && elm.attachEvent) { - elm.attachEvent('on' + name, func); - } - } - - function detachEvent(elm, name, func) { - if (elm && elm.detachEvent) { - elm.detachEvent('on' + name, func); - } - } - - function resizeNativeStart(e) { - var target = e.srcElement, - pos, name, corner, cornerX, cornerY, relativeX, relativeY; - - pos = target.getBoundingClientRect(); - relativeX = lastMouseDownEvent.clientX - pos.left; - relativeY = lastMouseDownEvent.clientY - pos.top; - - // Figure out what corner we are draging on - for (name in resizeHandles) { - corner = resizeHandles[name]; - - cornerX = target.offsetWidth * corner[0]; - cornerY = target.offsetHeight * corner[1]; - - if (abs(cornerX - relativeX) < 8 && abs(cornerY - relativeY) < 8) { - selectedHandle = corner; - break; - } - } - - // Remove native selection and let the magic begin - resizeStarted = true; - editor.fire('ObjectResizeStart', { - target: selectedElm, - width: selectedElm.clientWidth, - height: selectedElm.clientHeight - }); - editor.getDoc().selection.empty(); - showResizeRect(target, name, lastMouseDownEvent); - } - - function preventDefault(e) { - if (e.preventDefault) { - e.preventDefault(); - } else { - e.returnValue = false; // IE - } - } - - function isWithinContentEditableFalse(elm) { - return isContentEditableFalse(getContentEditableRoot(editor.getBody(), elm)); - } - - function nativeControlSelect(e) { - var target = e.srcElement; - - if (isWithinContentEditableFalse(target)) { - preventDefault(e); - return; - } - - if (target != selectedElm) { - editor.fire('ObjectSelected', { - target: target - }); - detachResizeStartListener(); - - if (target.id.indexOf('mceResizeHandle') === 0) { - e.returnValue = false; - return; - } - - if (target.nodeName == 'IMG' || target.nodeName == 'TABLE') { - hideResizeRect(); - selectedElm = target; - attachEvent(target, 'resizestart', resizeNativeStart); - } - } - } - - function detachResizeStartListener() { - detachEvent(selectedElm, 'resizestart', resizeNativeStart); - } - - function unbindResizeHandleEvents() { - for (var name in resizeHandles) { - var handle = resizeHandles[name]; - - if (handle.elm) { - dom.unbind(handle.elm); - delete handle.elm; - } - } - } - - function disableGeckoResize() { - try { - // Disable object resizing on Gecko - editor.getDoc().execCommand('enableObjectResizing', false, false); - } catch (ex) { - // Ignore - } - } - - function controlSelect(elm) { - var ctrlRng; - - if (!isIE) { - return; - } - - ctrlRng = editableDoc.body.createControlRange(); - - try { - ctrlRng.addElement(elm); - ctrlRng.select(); - return true; - } catch (ex) { - // Ignore since the element can't be control selected for example a P tag - } - } - - editor.on('init', function() { - if (isIE) { - // Hide the resize rect on resize and reselect the image - editor.on('ObjectResized', function(e) { - if (e.target.nodeName != 'TABLE') { - hideResizeRect(); - controlSelect(e.target); - } - }); - - attachEvent(rootElement, 'controlselect', nativeControlSelect); - - editor.on('mousedown', function(e) { - lastMouseDownEvent = e; - }); - } else { - disableGeckoResize(); - - // Sniff sniff, hard to feature detect this stuff - if (Env.ie >= 11) { - // Needs to be mousedown for drag/drop to work on IE 11 - // Needs to be click on Edge to properly select images - editor.on('mousedown click', function(e) { - var target = e.target, - nodeName = target.nodeName; - - if (!resizeStarted && /^(TABLE|IMG|HR)$/.test(nodeName) && !isWithinContentEditableFalse(target)) { - if (e.button !== 2) { - editor.selection.select(target, nodeName == 'TABLE'); - } - - // Only fire once since nodeChange is expensive - if (e.type == 'mousedown') { - editor.nodeChanged(); - } - } - }); - - editor.dom.bind(rootElement, 'mscontrolselect', function(e) { - function delayedSelect(node) { - Delay.setEditorTimeout(editor, function() { - editor.selection.select(node); - }); - } - - if (isWithinContentEditableFalse(e.target)) { - e.preventDefault(); - delayedSelect(e.target); - return; - } - - if (/^(TABLE|IMG|HR)$/.test(e.target.nodeName)) { - e.preventDefault(); - - // This moves the selection from being a control selection to a text like selection like in WebKit #6753 - // TODO: Fix this the day IE works like other browsers without this nasty native ugly control selections. - if (e.target.tagName == 'IMG') { - delayedSelect(e.target); - } - } - }); - } - } - - var throttledUpdateResizeRect = Delay.throttle(function(e) { - if (!editor.composing) { - updateResizeRect(e); - } - }); - - editor.on('nodechange ResizeEditor ResizeWindow drop', throttledUpdateResizeRect); - - // Update resize rect while typing in a table - editor.on('keyup compositionend', function(e) { - // Don't update the resize rect while composing since it blows away the IME see: #2710 - if (selectedElm && selectedElm.nodeName == "TABLE") { - throttledUpdateResizeRect(e); - } - }); - - editor.on('hide blur', hideResizeRect); - editor.on('contextmenu', Fun.curry(contextMenuSelectImage, editor)); - - // Hide rect on focusout since it would float on top of windows otherwise - //editor.on('focusout', hideResizeRect); - }); - - editor.on('remove', unbindResizeHandleEvents); - - function destroy() { - selectedElm = selectedElmGhost = null; - - if (isIE) { - detachResizeStartListener(); - detachEvent(rootElement, 'controlselect', nativeControlSelect); - } - } - - return { - isResizable: isResizable, - showResizeRect: showResizeRect, - hideResizeRect: hideResizeRect, - updateResizeRect: updateResizeRect, - controlSelect: controlSelect, - destroy: destroy - }; - }; - } - ); - - /** - * Fun.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Functional utility class. - * - * @private - * @class tinymce.util.Fun - */ - define( - 'tinymce.core.util.Fun', [], - function() { - var slice = [].slice; - - function constant(value) { - return function() { - return value; - }; - } - - function negate(predicate) { - return function(x) { - return !predicate(x); - }; - } - - function compose(f, g) { - return function(x) { - return f(g(x)); - }; - } - - function or() { - var args = slice.call(arguments); - - return function(x) { - for (var i = 0; i < args.length; i++) { - if (args[i](x)) { - return true; - } - } - - return false; - }; - } - - function and() { - var args = slice.call(arguments); - - return function(x) { - for (var i = 0; i < args.length; i++) { - if (!args[i](x)) { - return false; - } - } - - return true; - }; - } - - function curry(fn) { - var args = slice.call(arguments); - - if (args.length - 1 >= fn.length) { - return fn.apply(this, args.slice(1)); - } - - return function() { - var tempArgs = args.concat([].slice.call(arguments)); - return curry.apply(this, tempArgs); - }; - } - - function noop() {} - - return { - constant: constant, - negate: negate, - and: and, - or: or, - curry: curry, - compose: compose, - noop: noop - }; - } - ); - /** - * CaretCandidate.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module contains logic for handling caret candidates. A caret candidate is - * for example text nodes, images, input elements, cE=false elements etc. - * - * @private - * @class tinymce.caret.CaretCandidate - */ - define( - 'tinymce.core.caret.CaretCandidate', [ - "tinymce.core.dom.NodeType", - "tinymce.core.util.Arr", - "tinymce.core.caret.CaretContainer" - ], - function(NodeType, Arr, CaretContainer) { - var isContentEditableTrue = NodeType.isContentEditableTrue, - isContentEditableFalse = NodeType.isContentEditableFalse, - isBr = NodeType.isBr, - isText = NodeType.isText, - isInvalidTextElement = NodeType.matchNodeNames('script style textarea'), - isAtomicInline = NodeType.matchNodeNames('img input textarea hr iframe video audio object'), - isTable = NodeType.matchNodeNames('table'), - isCaretContainer = CaretContainer.isCaretContainer; - - function isCaretCandidate(node) { - if (isCaretContainer(node)) { - return false; - } - - if (isText(node)) { - if (isInvalidTextElement(node.parentNode)) { - return false; - } - - return true; - } - - return isAtomicInline(node) || isBr(node) || isTable(node) || isContentEditableFalse(node); - } - - function isInEditable(node, rootNode) { - for (node = node.parentNode; node && node != rootNode; node = node.parentNode) { - if (isContentEditableFalse(node)) { - return false; - } - - if (isContentEditableTrue(node)) { - return true; - } - } - - return true; - } - - function isAtomicContentEditableFalse(node) { - if (!isContentEditableFalse(node)) { - return false; - } - - return Arr.reduce(node.getElementsByTagName('*'), function(result, elm) { - return result || isContentEditableTrue(elm); - }, false) !== true; - } - - function isAtomic(node) { - return isAtomicInline(node) || isAtomicContentEditableFalse(node); - } - - function isEditableCaretCandidate(node, rootNode) { - return isCaretCandidate(node) && isInEditable(node, rootNode); - } - - return { - isCaretCandidate: isCaretCandidate, - isInEditable: isInEditable, - isAtomic: isAtomic, - isEditableCaretCandidate: isEditableCaretCandidate - }; - } - ); - /** - * ExtendingChar.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class contains logic for detecting extending characters. - * - * @private - * @class tinymce.text.ExtendingChar - * @example - * var isExtending = ExtendingChar.isExtendingChar('a'); - */ - define( - 'tinymce.core.text.ExtendingChar', [], - function() { - // Generated from: http://www.unicode.org/Public/UNIDATA/DerivedCoreProperties.txt - // Only includes the characters in that fit into UCS-2 16 bit - var extendingChars = new RegExp( - "[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A" + - "\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0" + - "\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08E3-\u0902\u093A\u093C" + - "\u0941-\u0948\u094D\u0951-\u0957\u0962-\u0963\u0981\u09BC\u09BE\u09C1-\u09C4\u09CD\u09D7\u09E2-\u09E3" + - "\u0A01-\u0A02\u0A3C\u0A41-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A51\u0A70-\u0A71\u0A75\u0A81-\u0A82\u0ABC" + - "\u0AC1-\u0AC5\u0AC7-\u0AC8\u0ACD\u0AE2-\u0AE3\u0B01\u0B3C\u0B3E\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B57" + - "\u0B62-\u0B63\u0B82\u0BBE\u0BC0\u0BCD\u0BD7\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56" + - "\u0C62-\u0C63\u0C81\u0CBC\u0CBF\u0CC2\u0CC6\u0CCC-\u0CCD\u0CD5-\u0CD6\u0CE2-\u0CE3\u0D01\u0D3E\u0D41-\u0D44" + - "\u0D4D\u0D57\u0D62-\u0D63\u0DCA\u0DCF\u0DD2-\u0DD4\u0DD6\u0DDF\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9" + - "\u0EBB-\u0EBC\u0EC8-\u0ECD\u0F18-\u0F19\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86-\u0F87\u0F8D-\u0F97" + - "\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039-\u103A\u103D-\u103E\u1058-\u1059\u105E-\u1060\u1071-\u1074" + - "\u1082\u1085-\u1086\u108D\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17B4-\u17B5" + - "\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u18A9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193B\u1A17-\u1A18" + - "\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABD\u1ABE\u1B00-\u1B03\u1B34" + - "\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80-\u1B81\u1BA2-\u1BA5\u1BA8-\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8-\u1BE9" + - "\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8-\u1CF9" + - "\u1DC0-\u1DF5\u1DFC-\u1DFF\u200C-\u200D\u20D0-\u20DC\u20DD-\u20E0\u20E1\u20E2-\u20E4\u20E5-\u20F0\u2CEF-\u2CF1" + - "\u2D7F\u2DE0-\u2DFF\u302A-\u302D\u302E-\u302F\u3099-\u309A\uA66F\uA670-\uA672\uA674-\uA67D\uA69E-\uA69F\uA6F0-\uA6F1" + - "\uA802\uA806\uA80B\uA825-\uA826\uA8C4\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC" + - "\uA9E5\uAA29-\uAA2E\uAA31-\uAA32\uAA35-\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7-\uAAB8\uAABE-\uAABF\uAAC1" + - "\uAAEC-\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFF9E-\uFF9F]" - ); - - function isExtendingChar(ch) { - return typeof ch == "string" && ch.charCodeAt(0) >= 768 && extendingChars.test(ch); - } - - return { - isExtendingChar: isExtendingChar - }; - } - ); - /** - * CaretPosition.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module contains logic for creating caret positions within a document a caretposition - * is similar to a DOMRange object but it doesn't have two endpoints and is also more lightweight - * since it's now updated live when the DOM changes. - * - * @private - * @class tinymce.caret.CaretPosition - * @example - * var caretPos1 = new CaretPosition(container, offset); - * var caretPos2 = CaretPosition.fromRangeStart(someRange); - */ - define( - 'tinymce.core.caret.CaretPosition', [ - "tinymce.core.util.Fun", - "tinymce.core.dom.NodeType", - "tinymce.core.dom.DOMUtils", - "tinymce.core.dom.RangeUtils", - "tinymce.core.caret.CaretCandidate", - "tinymce.core.geom.ClientRect", - "tinymce.core.text.ExtendingChar" - ], - function(Fun, NodeType, DOMUtils, RangeUtils, CaretCandidate, ClientRect, ExtendingChar) { - var isElement = NodeType.isElement, - isCaretCandidate = CaretCandidate.isCaretCandidate, - isBlock = NodeType.matchStyleValues('display', 'block table'), - isFloated = NodeType.matchStyleValues('float', 'left right'), - isValidElementCaretCandidate = Fun.and(isElement, isCaretCandidate, Fun.negate(isFloated)), - isNotPre = Fun.negate(NodeType.matchStyleValues('white-space', 'pre pre-line pre-wrap')), - isText = NodeType.isText, - isBr = NodeType.isBr, - nodeIndex = DOMUtils.nodeIndex, - resolveIndex = RangeUtils.getNode; - - function createRange(doc) { - return "createRange" in doc ? doc.createRange() : DOMUtils.DOM.createRng(); - } - - function isWhiteSpace(chr) { - return chr && /[\r\n\t ]/.test(chr); - } - - function isHiddenWhiteSpaceRange(range) { - var container = range.startContainer, - offset = range.startOffset, - text; - - if (isWhiteSpace(range.toString()) && isNotPre(container.parentNode)) { - text = container.data; - - if (isWhiteSpace(text[offset - 1]) || isWhiteSpace(text[offset + 1])) { - return true; - } - } - - return false; - } - - function getCaretPositionClientRects(caretPosition) { - var clientRects = [], - beforeNode, node; - - // Hack for older WebKit versions that doesn't - // support getBoundingClientRect on BR elements - function getBrClientRect(brNode) { - var doc = brNode.ownerDocument, - rng = createRange(doc), - nbsp = doc.createTextNode('\u00a0'), - parentNode = brNode.parentNode, - clientRect; - - parentNode.insertBefore(nbsp, brNode); - rng.setStart(nbsp, 0); - rng.setEnd(nbsp, 1); - clientRect = ClientRect.clone(rng.getBoundingClientRect()); - parentNode.removeChild(nbsp); - - return clientRect; - } - - function getBoundingClientRect(item) { - var clientRect, clientRects; - - clientRects = item.getClientRects(); - if (clientRects.length > 0) { - clientRect = ClientRect.clone(clientRects[0]); - } else { - clientRect = ClientRect.clone(item.getBoundingClientRect()); - } - - if (isBr(item) && clientRect.left === 0) { - return getBrClientRect(item); - } - - return clientRect; - } - - function collapseAndInflateWidth(clientRect, toStart) { - clientRect = ClientRect.collapse(clientRect, toStart); - clientRect.width = 1; - clientRect.right = clientRect.left + 1; - - return clientRect; - } - - function addUniqueAndValidRect(clientRect) { - if (clientRect.height === 0) { - return; - } - - if (clientRects.length > 0) { - if (ClientRect.isEqual(clientRect, clientRects[clientRects.length - 1])) { - return; - } - } - - clientRects.push(clientRect); - } - - function addCharacterOffset(container, offset) { - var range = createRange(container.ownerDocument); - - if (offset < container.data.length) { - if (ExtendingChar.isExtendingChar(container.data[offset])) { - return clientRects; - } - - // WebKit returns two client rects for a position after an extending - // character a\uxxx|b so expand on "b" and collapse to start of "b" box - if (ExtendingChar.isExtendingChar(container.data[offset - 1])) { - range.setStart(container, offset); - range.setEnd(container, offset + 1); - - if (!isHiddenWhiteSpaceRange(range)) { - addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(range), false)); - return clientRects; - } - } - } - - if (offset > 0) { - range.setStart(container, offset - 1); - range.setEnd(container, offset); - - if (!isHiddenWhiteSpaceRange(range)) { - addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(range), false)); - } - } - - if (offset < container.data.length) { - range.setStart(container, offset); - range.setEnd(container, offset + 1); - - if (!isHiddenWhiteSpaceRange(range)) { - addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(range), true)); - } - } - } - - if (isText(caretPosition.container())) { - addCharacterOffset(caretPosition.container(), caretPosition.offset()); - return clientRects; - } - - if (isElement(caretPosition.container())) { - if (caretPosition.isAtEnd()) { - node = resolveIndex(caretPosition.container(), caretPosition.offset()); - if (isText(node)) { - addCharacterOffset(node, node.data.length); - } - - if (isValidElementCaretCandidate(node) && !isBr(node)) { - addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(node), false)); - } - } else { - node = resolveIndex(caretPosition.container(), caretPosition.offset()); - if (isText(node)) { - addCharacterOffset(node, 0); - } - - if (isValidElementCaretCandidate(node) && caretPosition.isAtEnd()) { - addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(node), false)); - return clientRects; - } - - beforeNode = resolveIndex(caretPosition.container(), caretPosition.offset() - 1); - if (isValidElementCaretCandidate(beforeNode) && !isBr(beforeNode)) { - if (isBlock(beforeNode) || isBlock(node) || !isValidElementCaretCandidate(node)) { - addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(beforeNode), false)); - } - } - - if (isValidElementCaretCandidate(node)) { - addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(node), true)); - } - } - } - - return clientRects; - } - - /** - * Represents a location within the document by a container and an offset. - * - * @constructor - * @param {Node} container Container node. - * @param {Number} offset Offset within that container node. - * @param {Array} clientRects Optional client rects array for the position. - */ - function CaretPosition(container, offset, clientRects) { - function isAtStart() { - if (isText(container)) { - return offset === 0; - } - - return offset === 0; - } - - function isAtEnd() { - if (isText(container)) { - return offset >= container.data.length; - } - - return offset >= container.childNodes.length; - } - - function toRange() { - var range; - - range = createRange(container.ownerDocument); - range.setStart(container, offset); - range.setEnd(container, offset); - - return range; - } - - function getClientRects() { - if (!clientRects) { - clientRects = getCaretPositionClientRects(new CaretPosition(container, offset)); - } - - return clientRects; - } - - function isVisible() { - return getClientRects().length > 0; - } - - function isEqual(caretPosition) { - return caretPosition && container === caretPosition.container() && offset === caretPosition.offset(); - } - - function getNode(before) { - return resolveIndex(container, before ? offset - 1 : offset); - } - - return { - /** - * Returns the container node. - * - * @method container - * @return {Node} Container node. - */ - container: Fun.constant(container), - - /** - * Returns the offset within the container node. - * - * @method offset - * @return {Number} Offset within the container node. - */ - offset: Fun.constant(offset), - - /** - * Returns a range out of a the caret position. - * - * @method toRange - * @return {DOMRange} range for the caret position. - */ - toRange: toRange, - - /** - * Returns the client rects for the caret position. Might be multiple rects between - * block elements. - * - * @method getClientRects - * @return {Array} Array of client rects. - */ - getClientRects: getClientRects, - - /** - * Returns true if the caret location is visible/displayed on screen. - * - * @method isVisible - * @return {Boolean} true/false if the position is visible or not. - */ - isVisible: isVisible, - - /** - * Returns true if the caret location is at the beginning of text node or container. - * - * @method isVisible - * @return {Boolean} true/false if the position is at the beginning. - */ - isAtStart: isAtStart, - - /** - * Returns true if the caret location is at the end of text node or container. - * - * @method isVisible - * @return {Boolean} true/false if the position is at the end. - */ - isAtEnd: isAtEnd, - - /** - * Compares the caret position to another caret position. This will only compare the - * container and offset not it's visual position. - * - * @method isEqual - * @param {tinymce.caret.CaretPosition} caretPosition Caret position to compare with. - * @return {Boolean} true if the caret positions are equal. - */ - isEqual: isEqual, - - /** - * Returns the closest resolved node from a node index. That means if you have an offset after the - * last node in a container it will return that last node. - * - * @method getNode - * @return {Node} Node that is closest to the index. - */ - getNode: getNode - }; - } - - /** - * Creates a caret position from the start of a range. - * - * @method fromRangeStart - * @param {DOMRange} range DOM Range to create caret position from. - * @return {tinymce.caret.CaretPosition} Caret position from the start of DOM range. - */ - CaretPosition.fromRangeStart = function(range) { - return new CaretPosition(range.startContainer, range.startOffset); - }; - - /** - * Creates a caret position from the end of a range. - * - * @method fromRangeEnd - * @param {DOMRange} range DOM Range to create caret position from. - * @return {tinymce.caret.CaretPosition} Caret position from the end of DOM range. - */ - CaretPosition.fromRangeEnd = function(range) { - return new CaretPosition(range.endContainer, range.endOffset); - }; - - /** - * Creates a caret position from a node and places the offset after it. - * - * @method after - * @param {Node} node Node to get caret position from. - * @return {tinymce.caret.CaretPosition} Caret position from the node. - */ - CaretPosition.after = function(node) { - return new CaretPosition(node.parentNode, nodeIndex(node) + 1); - }; - - /** - * Creates a caret position from a node and places the offset before it. - * - * @method before - * @param {Node} node Node to get caret position from. - * @return {tinymce.caret.CaretPosition} Caret position from the node. - */ - CaretPosition.before = function(node) { - return new CaretPosition(node.parentNode, nodeIndex(node)); - }; - - CaretPosition.isAtStart = function(pos) { - return pos ? pos.isAtStart() : false; - }; - - CaretPosition.isAtEnd = function(pos) { - return pos ? pos.isAtEnd() : false; - }; - - CaretPosition.isTextPosition = function(pos) { - return pos ? NodeType.isText(pos.container()) : false; - }; - - return CaretPosition; - } - ); - /** - * CaretBookmark.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module creates or resolves xpath like string representation of a CaretPositions. - * - * The format is a / separated list of chunks with: - * <element|text()>[index|after|before] - * - * For example: - * p[0]/b[0]/text()[0],1 = <p><b>a|c</b></p> - * p[0]/img[0],before = <p>|<img></p> - * p[0]/img[0],after = <p><img>|</p> - * - * @private - * @static - * @class tinymce.caret.CaretBookmark - * @example - * var bookmark = CaretBookmark.create(rootElm, CaretPosition.before(rootElm.firstChild)); - * var caretPosition = CaretBookmark.resolve(bookmark); - */ - define( - 'tinymce.core.caret.CaretBookmark', [ - 'tinymce.core.dom.NodeType', - 'tinymce.core.dom.DOMUtils', - 'tinymce.core.util.Fun', - 'tinymce.core.util.Arr', - 'tinymce.core.caret.CaretPosition' - ], - function(NodeType, DomUtils, Fun, Arr, CaretPosition) { - var isText = NodeType.isText, - isBogus = NodeType.isBogus, - nodeIndex = DomUtils.nodeIndex; - - function normalizedParent(node) { - var parentNode = node.parentNode; - - if (isBogus(parentNode)) { - return normalizedParent(parentNode); - } - - return parentNode; - } - - function getChildNodes(node) { - if (!node) { - return []; - } - - return Arr.reduce(node.childNodes, function(result, node) { - if (isBogus(node) && node.nodeName != 'BR') { - result = result.concat(getChildNodes(node)); - } else { - result.push(node); - } - - return result; - }, []); - } - - function normalizedTextOffset(textNode, offset) { - while ((textNode = textNode.previousSibling)) { - if (!isText(textNode)) { - break; - } - - offset += textNode.data.length; - } - - return offset; - } - - function equal(targetValue) { - return function(value) { - return targetValue === value; - }; - } - - function normalizedNodeIndex(node) { - var nodes, index, numTextFragments; - - nodes = getChildNodes(normalizedParent(node)); - index = Arr.findIndex(nodes, equal(node), node); - nodes = nodes.slice(0, index + 1); - numTextFragments = Arr.reduce(nodes, function(result, node, i) { - if (isText(node) && isText(nodes[i - 1])) { - result++; - } - - return result; - }, 0); - - nodes = Arr.filter(nodes, NodeType.matchNodeNames(node.nodeName)); - index = Arr.findIndex(nodes, equal(node), node); - - return index - numTextFragments; - } - - function createPathItem(node) { - var name; - - if (isText(node)) { - name = 'text()'; - } else { - name = node.nodeName.toLowerCase(); - } - - return name + '[' + normalizedNodeIndex(node) + ']'; - } - - function parentsUntil(rootNode, node, predicate) { - var parents = []; - - for (node = node.parentNode; node != rootNode; node = node.parentNode) { - if (predicate && predicate(node)) { - break; - } - - parents.push(node); - } - - return parents; - } - - function create(rootNode, caretPosition) { - var container, offset, path = [], - outputOffset, childNodes, parents; - - container = caretPosition.container(); - offset = caretPosition.offset(); - - if (isText(container)) { - outputOffset = normalizedTextOffset(container, offset); - } else { - childNodes = container.childNodes; - if (offset >= childNodes.length) { - outputOffset = 'after'; - offset = childNodes.length - 1; - } else { - outputOffset = 'before'; - } - - container = childNodes[offset]; - } - - path.push(createPathItem(container)); - parents = parentsUntil(rootNode, container); - parents = Arr.filter(parents, Fun.negate(NodeType.isBogus)); - path = path.concat(Arr.map(parents, function(node) { - return createPathItem(node); - })); - - return path.reverse().join('/') + ',' + outputOffset; - } - - function resolvePathItem(node, name, index) { - var nodes = getChildNodes(node); - - nodes = Arr.filter(nodes, function(node, index) { - return !isText(node) || !isText(nodes[index - 1]); - }); - - nodes = Arr.filter(nodes, NodeType.matchNodeNames(name)); - return nodes[index]; - } - - function findTextPosition(container, offset) { - var node = container, - targetOffset = 0, - dataLen; - - while (isText(node)) { - dataLen = node.data.length; - - if (offset >= targetOffset && offset <= targetOffset + dataLen) { - container = node; - offset = offset - targetOffset; - break; - } - - if (!isText(node.nextSibling)) { - container = node; - offset = dataLen; - break; - } - - targetOffset += dataLen; - node = node.nextSibling; - } - - if (offset > container.data.length) { - offset = container.data.length; - } - - return new CaretPosition(container, offset); - } - - function resolve(rootNode, path) { - var parts, container, offset; - - if (!path) { - return null; - } - - parts = path.split(','); - path = parts[0].split('/'); - offset = parts.length > 1 ? parts[1] : 'before'; - - container = Arr.reduce(path, function(result, value) { - value = /([\w\-\(\)]+)\[([0-9]+)\]/.exec(value); - if (!value) { - return null; - } - - if (value[1] === 'text()') { - value[1] = '#text'; - } - - return resolvePathItem(result, value[1], parseInt(value[2], 10)); - }, rootNode); - - if (!container) { - return null; - } - - if (!isText(container)) { - if (offset === 'after') { - offset = nodeIndex(container) + 1; - } else { - offset = nodeIndex(container); - } - - return new CaretPosition(container.parentNode, offset); - } - - return findTextPosition(container, parseInt(offset, 10)); - } - - return { - /** - * Create a xpath bookmark location for the specified caret position. - * - * @method create - * @param {Node} rootNode Root node to create bookmark within. - * @param {tinymce.caret.CaretPosition} caretPosition Caret position within the root node. - * @return {String} String xpath like location of caret position. - */ - create: create, - - /** - * Resolves a xpath like bookmark location to the a caret position. - * - * @method resolve - * @param {Node} rootNode Root node to resolve xpath bookmark within. - * @param {String} bookmark Bookmark string to resolve. - * @return {tinymce.caret.CaretPosition} Caret position resolved from xpath like bookmark. - */ - resolve: resolve - }; - } - ); - /** - * BookmarkManager.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class handles selection bookmarks. - * - * @class tinymce.dom.BookmarkManager - */ - define( - 'tinymce.core.dom.BookmarkManager', [ - 'tinymce.core.caret.CaretBookmark', - 'tinymce.core.caret.CaretContainer', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.dom.NodeType', - 'tinymce.core.dom.RangeUtils', - 'tinymce.core.Env', - 'tinymce.core.text.Zwsp', - 'tinymce.core.util.Tools' - ], - function(CaretBookmark, CaretContainer, CaretPosition, NodeType, RangeUtils, Env, Zwsp, Tools) { - var isContentEditableFalse = NodeType.isContentEditableFalse; - - var getNormalizedTextOffset = function(container, offset) { - var node, trimmedOffset; - - trimmedOffset = Zwsp.trim(container.data.slice(0, offset)).length; - for (node = container.previousSibling; node && node.nodeType === 3; node = node.previousSibling) { - trimmedOffset += Zwsp.trim(node.data).length; - } - - return trimmedOffset; - }; - - /** - * Constructs a new BookmarkManager instance for a specific selection instance. - * - * @constructor - * @method BookmarkManager - * @param {tinymce.dom.Selection} selection Selection instance to handle bookmarks for. - */ - function BookmarkManager(selection) { - var dom = selection.dom; - - /** - * Returns a bookmark location for the current selection. This bookmark object - * can then be used to restore the selection after some content modification to the document. - * - * @method getBookmark - * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex. - * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization. - * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection. - * @example - * // Stores a bookmark of the current selection - * var bm = tinymce.activeEditor.selection.getBookmark(); - * - * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content'); - * - * // Restore the selection bookmark - * tinymce.activeEditor.selection.moveToBookmark(bm); - */ - this.getBookmark = function(type, normalized) { - var rng, rng2, id, collapsed, name, element, chr = '', - styles; - - function findIndex(name, element) { - var count = 0; - - Tools.each(dom.select(name), function(node) { - if (node.getAttribute('data-mce-bogus') === 'all') { - return; - } - - if (node == element) { - return false; - } - - count++; - }); - - return count; - } - - function normalizeTableCellSelection(rng) { - function moveEndPoint(start) { - var container, offset, childNodes, prefix = start ? 'start' : 'end'; - - container = rng[prefix + 'Container']; - offset = rng[prefix + 'Offset']; - - if (container.nodeType == 1 && container.nodeName == "TR") { - childNodes = container.childNodes; - container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)]; - if (container) { - offset = start ? 0 : container.childNodes.length; - rng['set' + (start ? 'Start' : 'End')](container, offset); - } - } - } - - moveEndPoint(true); - moveEndPoint(); - - return rng; - } - - function getLocation(rng) { - var root = dom.getRoot(), - bookmark = {}; - - function getPoint(rng, start) { - var container = rng[start ? 'startContainer' : 'endContainer'], - offset = rng[start ? 'startOffset' : 'endOffset'], - point = [], - childNodes, after = 0; - - if (container.nodeType === 3) { - point.push(normalized ? getNormalizedTextOffset(container, offset) : offset); - } else { - childNodes = container.childNodes; - - if (offset >= childNodes.length && childNodes.length) { - after = 1; - offset = Math.max(0, childNodes.length - 1); - } - - point.push(dom.nodeIndex(childNodes[offset], normalized) + after); - } - - for (; container && container != root; container = container.parentNode) { - point.push(dom.nodeIndex(container, normalized)); - } - - return point; - } - - bookmark.start = getPoint(rng, true); - - if (!selection.isCollapsed()) { - bookmark.end = getPoint(rng); - } - - return bookmark; - } - - function findAdjacentContentEditableFalseElm(rng) { - function findSibling(node, offset) { - var sibling; - - if (NodeType.isElement(node)) { - node = RangeUtils.getNode(node, offset); - if (isContentEditableFalse(node)) { - return node; - } - } - - if (CaretContainer.isCaretContainer(node)) { - if (NodeType.isText(node) && CaretContainer.isCaretContainerBlock(node)) { - node = node.parentNode; - } - - sibling = node.previousSibling; - if (isContentEditableFalse(sibling)) { - return sibling; - } - - sibling = node.nextSibling; - if (isContentEditableFalse(sibling)) { - return sibling; - } - } - } - - return findSibling(rng.startContainer, rng.startOffset) || findSibling(rng.endContainer, rng.endOffset); - } - - if (type == 2) { - element = selection.getNode(); - name = element ? element.nodeName : null; - rng = selection.getRng(); - - if (isContentEditableFalse(element) || name == 'IMG') { - return { - name: name, - index: findIndex(name, element) - }; - } - - if (selection.tridentSel) { - return selection.tridentSel.getBookmark(type); - } - - element = findAdjacentContentEditableFalseElm(rng); - if (element) { - name = element.tagName; - return { - name: name, - index: findIndex(name, element) - }; - } - - return getLocation(rng); - } - - if (type == 3) { - rng = selection.getRng(); - - return { - start: CaretBookmark.create(dom.getRoot(), CaretPosition.fromRangeStart(rng)), - end: CaretBookmark.create(dom.getRoot(), CaretPosition.fromRangeEnd(rng)) - }; - } - - // Handle simple range - if (type) { - return { - rng: selection.getRng() - }; - } - - rng = selection.getRng(); - id = dom.uniqueId(); - collapsed = selection.isCollapsed(); - styles = 'overflow:hidden;line-height:0px'; - - // Explorer method - if (rng.duplicate || rng.item) { - // Text selection - if (!rng.item) { - rng2 = rng.duplicate(); - - try { - // Insert start marker - rng.collapse(); - rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>'); - - // Insert end marker - if (!collapsed) { - rng2.collapse(false); - - // Detect the empty space after block elements in IE and move the - // end back one character <p></p>] becomes <p>]</p> - rng.moveToElementText(rng2.parentElement()); - if (rng.compareEndPoints('StartToEnd', rng2) === 0) { - rng2.move('character', -1); - } - - rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>'); - } - } catch (ex) { - // IE might throw unspecified error so lets ignore it - return null; - } - } else { - // Control selection - element = rng.item(0); - name = element.nodeName; - - return { - name: name, - index: findIndex(name, element) - }; - } - } else { - element = selection.getNode(); - name = element.nodeName; - if (name == 'IMG') { - return { - name: name, - index: findIndex(name, element) - }; - } - - // W3C method - rng2 = normalizeTableCellSelection(rng.cloneRange()); - - // Insert end marker - if (!collapsed) { - rng2.collapse(false); - rng2.insertNode(dom.create('span', { - 'data-mce-type': "bookmark", - id: id + '_end', - style: styles - }, chr)); - } - - rng = normalizeTableCellSelection(rng); - rng.collapse(true); - rng.insertNode(dom.create('span', { - 'data-mce-type': "bookmark", - id: id + '_start', - style: styles - }, chr)); - } - - selection.moveToBookmark({ - id: id, - keep: 1 - }); - - return { - id: id - }; - }; - - /** - * Restores the selection to the specified bookmark. - * - * @method moveToBookmark - * @param {Object} bookmark Bookmark to restore selection from. - * @return {Boolean} true/false if it was successful or not. - * @example - * // Stores a bookmark of the current selection - * var bm = tinymce.activeEditor.selection.getBookmark(); - * - * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content'); - * - * // Restore the selection bookmark - * tinymce.activeEditor.selection.moveToBookmark(bm); - */ - this.moveToBookmark = function(bookmark) { - var rng, root, startContainer, endContainer, startOffset, endOffset; - - function setEndPoint(start) { - var point = bookmark[start ? 'start' : 'end'], - i, node, offset, children; - - if (point) { - offset = point[0]; - - // Find container node - for (node = root, i = point.length - 1; i >= 1; i--) { - children = node.childNodes; - - if (point[i] > children.length - 1) { - return; - } - - node = children[point[i]]; - } - - // Move text offset to best suitable location - if (node.nodeType === 3) { - offset = Math.min(point[0], node.nodeValue.length); - } - - // Move element offset to best suitable location - if (node.nodeType === 1) { - offset = Math.min(point[0], node.childNodes.length); - } - - // Set offset within container node - if (start) { - rng.setStart(node, offset); - } else { - rng.setEnd(node, offset); - } - } - - return true; - } - - function restoreEndPoint(suffix) { - var marker = dom.get(bookmark.id + '_' + suffix), - node, idx, next, prev, keep = bookmark.keep; - - if (marker) { - node = marker.parentNode; - - if (suffix == 'start') { - if (!keep) { - idx = dom.nodeIndex(marker); - } else { - node = marker.firstChild; - idx = 1; - } - - startContainer = endContainer = node; - startOffset = endOffset = idx; - } else { - if (!keep) { - idx = dom.nodeIndex(marker); - } else { - node = marker.firstChild; - idx = 1; - } - - endContainer = node; - endOffset = idx; - } - - if (!keep) { - prev = marker.previousSibling; - next = marker.nextSibling; - - // Remove all marker text nodes - Tools.each(Tools.grep(marker.childNodes), function(node) { - if (node.nodeType == 3) { - node.nodeValue = node.nodeValue.replace(/\uFEFF/g, ''); - } - }); - - // Remove marker but keep children if for example contents where inserted into the marker - // Also remove duplicated instances of the marker for example by a - // split operation or by WebKit auto split on paste feature - while ((marker = dom.get(bookmark.id + '_' + suffix))) { - dom.remove(marker, 1); - } - - // If siblings are text nodes then merge them unless it's Opera since it some how removes the node - // and we are sniffing since adding a lot of detection code for a browser with 3% of the market - // isn't worth the effort. Sorry, Opera but it's just a fact - if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !Env.opera) { - idx = prev.nodeValue.length; - prev.appendData(next.nodeValue); - dom.remove(next); - - if (suffix == 'start') { - startContainer = endContainer = prev; - startOffset = endOffset = idx; - } else { - endContainer = prev; - endOffset = idx; - } - } - } - } - } - - function addBogus(node) { - // Adds a bogus BR element for empty block elements - if (dom.isBlock(node) && !node.innerHTML && !Env.ie) { - node.innerHTML = '<br data-mce-bogus="1" />'; - } - - return node; - } - - function resolveCaretPositionBookmark() { - var rng, pos; - - rng = dom.createRng(); - pos = CaretBookmark.resolve(dom.getRoot(), bookmark.start); - rng.setStart(pos.container(), pos.offset()); - - pos = CaretBookmark.resolve(dom.getRoot(), bookmark.end); - rng.setEnd(pos.container(), pos.offset()); - - return rng; - } - - if (bookmark) { - if (Tools.isArray(bookmark.start)) { - rng = dom.createRng(); - root = dom.getRoot(); - - if (selection.tridentSel) { - return selection.tridentSel.moveToBookmark(bookmark); - } - - if (setEndPoint(true) && setEndPoint()) { - selection.setRng(rng); - } - } else if (typeof bookmark.start == 'string') { - selection.setRng(resolveCaretPositionBookmark(bookmark)); - } else if (bookmark.id) { - // Restore start/end points - restoreEndPoint('start'); - restoreEndPoint('end'); - - if (startContainer) { - rng = dom.createRng(); - rng.setStart(addBogus(startContainer), startOffset); - rng.setEnd(addBogus(endContainer), endOffset); - selection.setRng(rng); - } - } else if (bookmark.name) { - selection.select(dom.select(bookmark.name)[bookmark.index]); - } else if (bookmark.rng) { - selection.setRng(bookmark.rng); - } - } - }; - } - - /** - * Returns true/false if the specified node is a bookmark node or not. - * - * @static - * @method isBookmarkNode - * @param {DOMNode} node DOM Node to check if it's a bookmark node or not. - * @return {Boolean} true/false if the node is a bookmark node or not. - */ - BookmarkManager.isBookmarkNode = function(node) { - return node && node.tagName === 'SPAN' && node.getAttribute('data-mce-type') === 'bookmark'; - }; - - return BookmarkManager; - } - ); - define( - 'ephox.katamari.api.Global', - - [], - - function() { - // Use window object as the global if it's available since CSP will block script evals - if (typeof window !== 'undefined') { - return window; - } else { - return Function('return this;')(); - } - } - ); - - - define( - 'ephox.katamari.api.Resolve', - - [ - 'ephox.katamari.api.Global' - ], - - function(Global) { - /** path :: ([String], JsObj?) -> JsObj */ - var path = function(parts, scope) { - var o = scope !== undefined ? scope : Global; - for (var i = 0; i < parts.length && o !== undefined && o !== null; ++i) - o = o[parts[i]]; - return o; - }; - - /** resolve :: (String, JsObj?) -> JsObj */ - var resolve = function(p, scope) { - var parts = p.split('.'); - return path(parts, scope); - }; - - /** step :: (JsObj, String) -> JsObj */ - var step = function(o, part) { - if (o[part] === undefined || o[part] === null) - o[part] = {}; - return o[part]; - }; - - /** forge :: ([String], JsObj?) -> JsObj */ - var forge = function(parts, target) { - var o = target !== undefined ? target : Global; - for (var i = 0; i < parts.length; ++i) - o = step(o, parts[i]); - return o; - }; - - /** namespace :: (String, JsObj?) -> JsObj */ - var namespace = function(name, target) { - var parts = name.split('.'); - return forge(parts, target); - }; - - return { - path: path, - resolve: resolve, - forge: forge, - namespace: namespace - }; - } - ); - - - define( - 'ephox.sand.util.Global', - - [ - 'ephox.katamari.api.Resolve' - ], - - function(Resolve) { - var unsafe = function(name, scope) { - return Resolve.resolve(name, scope); - }; - - var getOrDie = function(name, scope) { - var actual = unsafe(name, scope); - - if (actual === undefined) throw name + ' not available on this browser'; - return actual; - }; - - return { - getOrDie: getOrDie - }; - } - ); - define( - 'ephox.sand.api.Node', - - [ - 'ephox.sand.util.Global' - ], - - function(Global) { - /* - * MDN says (yes) for IE, but it's undefined on IE8 - */ - var node = function() { - var f = Global.getOrDie('Node'); - return f; - }; - - /* - * Most of numerosity doesn't alter the methods on the object. - * We're making an exception for Node, because bitwise and is so easy to get wrong. - * - * Might be nice to ADT this at some point instead of having individual methods. - */ - - var compareDocumentPosition = function(a, b, match) { - // Returns: 0 if e1 and e2 are the same node, or a bitmask comparing the positions - // of nodes e1 and e2 in their documents. See the URL below for bitmask interpretation - // https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition - return (a.compareDocumentPosition(b) & match) !== 0; - }; - - var documentPositionPreceding = function(a, b) { - return compareDocumentPosition(a, b, node().DOCUMENT_POSITION_PRECEDING); - }; - - var documentPositionContainedBy = function(a, b) { - return compareDocumentPosition(a, b, node().DOCUMENT_POSITION_CONTAINED_BY); - }; - - return { - documentPositionPreceding: documentPositionPreceding, - documentPositionContainedBy: documentPositionContainedBy - }; - } - ); - define( - 'ephox.katamari.api.Thunk', - - [], - - function() { - - var cached = function(f) { - var called = false; - var r; - return function() { - if (!called) { - called = true; - r = f.apply(null, arguments); - } - return r; - }; - }; - - return { - cached: cached - }; - } - ); - - defineGlobal("global!Number", Number); - define( - 'ephox.sand.detect.Version', - - [ - 'ephox.katamari.api.Arr', - 'global!Number', - 'global!String' - ], - - function(Arr, Number, String) { - var firstMatch = function(regexes, s) { - for (var i = 0; i < regexes.length; i++) { - var x = regexes[i]; - if (x.test(s)) return x; - } - return undefined; - }; - - var find = function(regexes, agent) { - var r = firstMatch(regexes, agent); - if (!r) return { - major: 0, - minor: 0 - }; - var group = function(i) { - return Number(agent.replace(r, '$' + i)); - }; - return nu(group(1), group(2)); - }; - - var detect = function(versionRegexes, agent) { - var cleanedAgent = String(agent).toLowerCase(); - - if (versionRegexes.length === 0) return unknown(); - return find(versionRegexes, cleanedAgent); - }; - - var unknown = function() { - return nu(0, 0); - }; - - var nu = function(major, minor) { - return { - major: major, - minor: minor - }; - }; - - return { - nu: nu, - detect: detect, - unknown: unknown - }; - } - ); - define( - 'ephox.sand.core.Browser', - - [ - 'ephox.katamari.api.Fun', - 'ephox.sand.detect.Version' - ], - - function(Fun, Version) { - var edge = 'Edge'; - var chrome = 'Chrome'; - var ie = 'IE'; - var opera = 'Opera'; - var firefox = 'Firefox'; - var safari = 'Safari'; - - var isBrowser = function(name, current) { - return function() { - return current === name; - }; - }; - - var unknown = function() { - return nu({ - current: undefined, - version: Version.unknown() - }); - }; - - var nu = function(info) { - var current = info.current; - var version = info.version; - - return { - current: current, - version: version, - - // INVESTIGATE: Rename to Edge ? - isEdge: isBrowser(edge, current), - isChrome: isBrowser(chrome, current), - // NOTE: isIe just looks too weird - isIE: isBrowser(ie, current), - isOpera: isBrowser(opera, current), - isFirefox: isBrowser(firefox, current), - isSafari: isBrowser(safari, current) - }; - }; - - return { - unknown: unknown, - nu: nu, - edge: Fun.constant(edge), - chrome: Fun.constant(chrome), - ie: Fun.constant(ie), - opera: Fun.constant(opera), - firefox: Fun.constant(firefox), - safari: Fun.constant(safari) - }; - } - ); - define( - 'ephox.sand.core.OperatingSystem', - - [ - 'ephox.katamari.api.Fun', - 'ephox.sand.detect.Version' - ], - - function(Fun, Version) { - var windows = 'Windows'; - var ios = 'iOS'; - var android = 'Android'; - var linux = 'Linux'; - var osx = 'OSX'; - var solaris = 'Solaris'; - var freebsd = 'FreeBSD'; - - // Though there is a bit of dupe with this and Browser, trying to - // reuse code makes it much harder to follow and change. - var isOS = function(name, current) { - return function() { - return current === name; - }; - }; - - var unknown = function() { - return nu({ - current: undefined, - version: Version.unknown() - }); - }; - - var nu = function(info) { - var current = info.current; - var version = info.version; - - return { - current: current, - version: version, - - isWindows: isOS(windows, current), - // TODO: Fix capitalisation - isiOS: isOS(ios, current), - isAndroid: isOS(android, current), - isOSX: isOS(osx, current), - isLinux: isOS(linux, current), - isSolaris: isOS(solaris, current), - isFreeBSD: isOS(freebsd, current) - }; - }; - - return { - unknown: unknown, - nu: nu, - - windows: Fun.constant(windows), - ios: Fun.constant(ios), - android: Fun.constant(android), - linux: Fun.constant(linux), - osx: Fun.constant(osx), - solaris: Fun.constant(solaris), - freebsd: Fun.constant(freebsd) - }; - } - ); - define( - 'ephox.sand.detect.DeviceType', - - [ - 'ephox.katamari.api.Fun' - ], - - function(Fun) { - return function(os, browser, userAgent) { - var isiPad = os.isiOS() && /ipad/i.test(userAgent) === true; - var isiPhone = os.isiOS() && !isiPad; - var isAndroid3 = os.isAndroid() && os.version.major === 3; - var isAndroid4 = os.isAndroid() && os.version.major === 4; - var isTablet = isiPad || isAndroid3 || (isAndroid4 && /mobile/i.test(userAgent) === true); - var isTouch = os.isiOS() || os.isAndroid(); - var isPhone = isTouch && !isTablet; - - var iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false; - - return { - isiPad: Fun.constant(isiPad), - isiPhone: Fun.constant(isiPhone), - isTablet: Fun.constant(isTablet), - isPhone: Fun.constant(isPhone), - isTouch: Fun.constant(isTouch), - isAndroid: os.isAndroid, - isiOS: os.isiOS, - isWebView: Fun.constant(iOSwebview) - }; - }; - } - ); - define( - 'ephox.sand.detect.UaString', - - [ - 'ephox.katamari.api.Arr', - 'ephox.sand.detect.Version', - 'global!String' - ], - - function(Arr, Version, String) { - var detect = function(candidates, userAgent) { - var agent = String(userAgent).toLowerCase(); - return Arr.find(candidates, function(candidate) { - return candidate.search(agent); - }); - }; - - // They (browser and os) are the same at the moment, but they might - // not stay that way. - var detectBrowser = function(browsers, userAgent) { - return detect(browsers, userAgent).map(function(browser) { - var version = Version.detect(browser.versionRegexes, userAgent); - return { - current: browser.name, - version: version - }; - }); - }; - - var detectOs = function(oses, userAgent) { - return detect(oses, userAgent).map(function(os) { - var version = Version.detect(os.versionRegexes, userAgent); - return { - current: os.name, - version: version - }; - }); - }; - - return { - detectBrowser: detectBrowser, - detectOs: detectOs - }; - } - ); - define( - 'ephox.katamari.str.StrAppend', - - [ - - ], - - function() { - var addToStart = function(str, prefix) { - return prefix + str; - }; - - var addToEnd = function(str, suffix) { - return str + suffix; - }; - - var removeFromStart = function(str, numChars) { - return str.substring(numChars); - }; - - var removeFromEnd = function(str, numChars) { - return str.substring(0, str.length - numChars); - }; - - return { - addToStart: addToStart, - addToEnd: addToEnd, - removeFromStart: removeFromStart, - removeFromEnd: removeFromEnd - }; - } - ); - define( - 'ephox.katamari.str.StringParts', - - [ - 'ephox.katamari.api.Option', - 'global!Error' - ], - - function(Option, Error) { - /** Return the first 'count' letters from 'str'. -- * e.g. first("abcde", 2) === "ab" -- */ - var first = function(str, count) { - return str.substr(0, count); - }; - - /** Return the last 'count' letters from 'str'. - * e.g. last("abcde", 2) === "de" - */ - var last = function(str, count) { - return str.substr(str.length - count, str.length); - }; - - var head = function(str) { - return str === '' ? Option.none() : Option.some(str.substr(0, 1)); - }; - - var tail = function(str) { - return str === '' ? Option.none() : Option.some(str.substring(1)); - }; - - return { - first: first, - last: last, - head: head, - tail: tail - }; - } - ); - define( - 'ephox.katamari.api.Strings', - - [ - 'ephox.katamari.str.StrAppend', - 'ephox.katamari.str.StringParts', - 'global!Error' - ], - - function(StrAppend, StringParts, Error) { - var checkRange = function(str, substr, start) { - if (substr === '') return true; - if (str.length < substr.length) return false; - var x = str.substr(start, start + substr.length); - return x === substr; - }; - - /** Given a string and object, perform template-replacements on the string, as specified by the object. - * Any template fields of the form ${name} are replaced by the string or number specified as obj["name"] - * Based on Douglas Crockford's 'supplant' method for template-replace of strings. Uses different template format. - */ - var supplant = function(str, obj) { - var isStringOrNumber = function(a) { - var t = typeof a; - return t === 'string' || t === 'number'; - }; - - return str.replace(/\${([^{}]*)}/g, - function(a, b) { - var value = obj[b]; - return isStringOrNumber(value) ? value : a; - } - ); - }; - - var removeLeading = function(str, prefix) { - return startsWith(str, prefix) ? StrAppend.removeFromStart(str, prefix.length) : str; - }; - - var removeTrailing = function(str, prefix) { - return endsWith(str, prefix) ? StrAppend.removeFromEnd(str, prefix.length) : str; - }; - - var ensureLeading = function(str, prefix) { - return startsWith(str, prefix) ? str : StrAppend.addToStart(str, prefix); - }; - - var ensureTrailing = function(str, prefix) { - return endsWith(str, prefix) ? str : StrAppend.addToEnd(str, prefix); - }; - - var contains = function(str, substr) { - return str.indexOf(substr) !== -1; - }; - - var capitalize = function(str) { - return StringParts.head(str).bind(function(head) { - return StringParts.tail(str).map(function(tail) { - return head.toUpperCase() + tail; - }); - }).getOr(str); - }; - - /** Does 'str' start with 'prefix'? - * Note: all strings start with the empty string. - * More formally, for all strings x, startsWith(x, ""). - * This is so that for all strings x and y, startsWith(y + x, y) - */ - var startsWith = function(str, prefix) { - return checkRange(str, prefix, 0); - }; - - /** Does 'str' end with 'suffix'? - * Note: all strings end with the empty string. - * More formally, for all strings x, endsWith(x, ""). - * This is so that for all strings x and y, endsWith(x + y, y) - */ - var endsWith = function(str, suffix) { - return checkRange(str, suffix, str.length - suffix.length); - }; - - - /** removes all leading and trailing spaces */ - var trim = function(str) { - return str.replace(/^\s+|\s+$/g, ''); - }; - - var lTrim = function(str) { - return str.replace(/^\s+/g, ''); - }; - - var rTrim = function(str) { - return str.replace(/\s+$/g, ''); - }; - - return { - supplant: supplant, - startsWith: startsWith, - removeLeading: removeLeading, - removeTrailing: removeTrailing, - ensureLeading: ensureLeading, - ensureTrailing: ensureTrailing, - endsWith: endsWith, - contains: contains, - trim: trim, - lTrim: lTrim, - rTrim: rTrim, - capitalize: capitalize - }; - } - ); - - define( - 'ephox.sand.info.PlatformInfo', - - [ - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Strings' - ], - - function(Fun, Strings) { - var normalVersionRegex = /.*?version\/\ ?([0-9]+)\.([0-9]+).*/; - - var checkContains = function(target) { - return function(uastring) { - return Strings.contains(uastring, target); - }; - }; - - var browsers = [{ - name: 'Edge', - versionRegexes: [/.*?edge\/ ?([0-9]+)\.([0-9]+)$/], - search: function(uastring) { - var monstrosity = Strings.contains(uastring, 'edge/') && Strings.contains(uastring, 'chrome') && Strings.contains(uastring, 'safari') && Strings.contains(uastring, 'applewebkit'); - return monstrosity; - } - }, - { - name: 'Chrome', - versionRegexes: [/.*?chrome\/([0-9]+)\.([0-9]+).*/, normalVersionRegex], - search: function(uastring) { - return Strings.contains(uastring, 'chrome') && !Strings.contains(uastring, 'chromeframe'); - } - }, - { - name: 'IE', - versionRegexes: [/.*?msie\ ?([0-9]+)\.([0-9]+).*/, /.*?rv:([0-9]+)\.([0-9]+).*/], - search: function(uastring) { - return Strings.contains(uastring, 'msie') || Strings.contains(uastring, 'trident'); - } - }, - // INVESTIGATE: Is this still the Opera user agent? - { - name: 'Opera', - versionRegexes: [normalVersionRegex, /.*?opera\/([0-9]+)\.([0-9]+).*/], - search: checkContains('opera') - }, - { - name: 'Firefox', - versionRegexes: [/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/], - search: checkContains('firefox') - }, - { - name: 'Safari', - versionRegexes: [normalVersionRegex, /.*?cpu os ([0-9]+)_([0-9]+).*/], - search: function(uastring) { - return (Strings.contains(uastring, 'safari') || Strings.contains(uastring, 'mobile/')) && Strings.contains(uastring, 'applewebkit'); - } - } - ]; - - var oses = [{ - name: 'Windows', - search: checkContains('win'), - versionRegexes: [/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/] - }, - { - name: 'iOS', - search: function(uastring) { - return Strings.contains(uastring, 'iphone') || Strings.contains(uastring, 'ipad'); - }, - versionRegexes: [/.*?version\/\ ?([0-9]+)\.([0-9]+).*/, /.*cpu os ([0-9]+)_([0-9]+).*/, /.*cpu iphone os ([0-9]+)_([0-9]+).*/] - }, - { - name: 'Android', - search: checkContains('android'), - versionRegexes: [/.*?android\ ?([0-9]+)\.([0-9]+).*/] - }, - { - name: 'OSX', - search: checkContains('os x'), - versionRegexes: [/.*?os\ x\ ?([0-9]+)_([0-9]+).*/] - }, - { - name: 'Linux', - search: checkContains('linux'), - versionRegexes: [] - }, - { - name: 'Solaris', - search: checkContains('sunos'), - versionRegexes: [] - }, - { - name: 'FreeBSD', - search: checkContains('freebsd'), - versionRegexes: [] - } - ]; - - return { - browsers: Fun.constant(browsers), - oses: Fun.constant(oses) - }; - } - ); - define( - 'ephox.sand.core.PlatformDetection', - - [ - 'ephox.sand.core.Browser', - 'ephox.sand.core.OperatingSystem', - 'ephox.sand.detect.DeviceType', - 'ephox.sand.detect.UaString', - 'ephox.sand.info.PlatformInfo' - ], - - function(Browser, OperatingSystem, DeviceType, UaString, PlatformInfo) { - var detect = function(userAgent) { - var browsers = PlatformInfo.browsers(); - var oses = PlatformInfo.oses(); - - var browser = UaString.detectBrowser(browsers, userAgent).fold( - Browser.unknown, - Browser.nu - ); - var os = UaString.detectOs(oses, userAgent).fold( - OperatingSystem.unknown, - OperatingSystem.nu - ); - var deviceType = DeviceType(os, browser, userAgent); - - return { - browser: browser, - os: os, - deviceType: deviceType - }; - }; - - return { - detect: detect - }; - } - ); - defineGlobal("global!navigator", navigator); - define( - 'ephox.sand.api.PlatformDetection', - - [ - 'ephox.katamari.api.Thunk', - 'ephox.sand.core.PlatformDetection', - 'global!navigator' - ], - - function(Thunk, PlatformDetection, navigator) { - var detect = Thunk.cached(function() { - var userAgent = navigator.userAgent; - return PlatformDetection.detect(userAgent); - }); - - return { - detect: detect - }; - } - ); - define("global!console", [], function() { - if (typeof console === "undefined") console = { - log: function() {} - }; - return console; - }); - defineGlobal("global!document", document); - define( - 'ephox.sugar.api.node.Element', - - [ - 'ephox.katamari.api.Fun', - 'global!Error', - 'global!console', - 'global!document' - ], - - function(Fun, Error, console, document) { - var fromHtml = function(html, scope) { - var doc = scope || document; - var div = doc.createElement('div'); - div.innerHTML = html; - if (!div.hasChildNodes() || div.childNodes.length > 1) { - console.error('HTML does not have a single root node', html); - throw 'HTML must have a single root node'; - } - return fromDom(div.childNodes[0]); - }; - - var fromTag = function(tag, scope) { - var doc = scope || document; - var node = doc.createElement(tag); - return fromDom(node); - }; - - var fromText = function(text, scope) { - var doc = scope || document; - var node = doc.createTextNode(text); - return fromDom(node); - }; - - var fromDom = function(node) { - if (node === null || node === undefined) throw new Error('Node cannot be null or undefined'); - return { - dom: Fun.constant(node) - }; - }; - - return { - fromHtml: fromHtml, - fromTag: fromTag, - fromText: fromText, - fromDom: fromDom - }; - } - ); - - define( - 'ephox.sugar.api.node.NodeTypes', - - [ - - ], - - function() { - return { - ATTRIBUTE: 2, - CDATA_SECTION: 4, - COMMENT: 8, - DOCUMENT: 9, - DOCUMENT_TYPE: 10, - DOCUMENT_FRAGMENT: 11, - ELEMENT: 1, - TEXT: 3, - PROCESSING_INSTRUCTION: 7, - ENTITY_REFERENCE: 5, - ENTITY: 6, - NOTATION: 12 - }; - } - ); - define( - 'ephox.sugar.api.search.Selectors', - - [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Option', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.api.node.NodeTypes', - 'global!Error', - 'global!document' - ], - - function(Arr, Option, Element, NodeTypes, Error, document) { - /* - * There's a lot of code here; the aim is to allow the browser to optimise constant comparisons, - * instead of doing object lookup feature detection on every call - */ - var STANDARD = 0; - var MSSTANDARD = 1; - var WEBKITSTANDARD = 2; - var FIREFOXSTANDARD = 3; - - var selectorType = (function() { - var test = document.createElement('span'); - // As of Chrome 34 / Safari 7.1 / FireFox 34, everyone except IE has the unprefixed function. - // Still check for the others, but do it last. - return test.matches !== undefined ? STANDARD : - test.msMatchesSelector !== undefined ? MSSTANDARD : - test.webkitMatchesSelector !== undefined ? WEBKITSTANDARD : - test.mozMatchesSelector !== undefined ? FIREFOXSTANDARD : - -1; - })(); - - - var ELEMENT = NodeTypes.ELEMENT; - var DOCUMENT = NodeTypes.DOCUMENT; - - var is = function(element, selector) { - var elem = element.dom(); - if (elem.nodeType !== ELEMENT) return false; // documents have querySelector but not matches - - // As of Chrome 34 / Safari 7.1 / FireFox 34, everyone except IE has the unprefixed function. - // Still check for the others, but do it last. - else if (selectorType === STANDARD) return elem.matches(selector); - else if (selectorType === MSSTANDARD) return elem.msMatchesSelector(selector); - else if (selectorType === WEBKITSTANDARD) return elem.webkitMatchesSelector(selector); - else if (selectorType === FIREFOXSTANDARD) return elem.mozMatchesSelector(selector); - else throw new Error('Browser lacks native selectors'); // unfortunately we can't throw this on startup :( - }; - - var bypassSelector = function(dom) { - // Only elements and documents support querySelector - return dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT || - // IE fix for complex queries on empty nodes: http://jsfiddle.net/spyder/fv9ptr5L/ - dom.childElementCount === 0; - }; - - var all = function(selector, scope) { - var base = scope === undefined ? document : scope.dom(); - return bypassSelector(base) ? [] : Arr.map(base.querySelectorAll(selector), Element.fromDom); - }; - - var one = function(selector, scope) { - var base = scope === undefined ? document : scope.dom(); - return bypassSelector(base) ? Option.none() : Option.from(base.querySelector(selector)).map(Element.fromDom); - }; - - return { - all: all, - is: is, - one: one - }; - } - ); - - define( - 'ephox.sugar.api.dom.Compare', - - [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Fun', - 'ephox.sand.api.Node', - 'ephox.sand.api.PlatformDetection', - 'ephox.sugar.api.search.Selectors' - ], - - function(Arr, Fun, Node, PlatformDetection, Selectors) { - - var eq = function(e1, e2) { - return e1.dom() === e2.dom(); - }; - - var isEqualNode = function(e1, e2) { - return e1.dom().isEqualNode(e2.dom()); - }; - - var member = function(element, elements) { - return Arr.exists(elements, Fun.curry(eq, element)); - }; - - // DOM contains() method returns true if e1===e2, we define our contains() to return false (a node does not contain itself). - var regularContains = function(e1, e2) { - var d1 = e1.dom(), - d2 = e2.dom(); - return d1 === d2 ? false : d1.contains(d2); - }; - - var ieContains = function(e1, e2) { - // IE only implements the contains() method for Element nodes. - // It fails for Text nodes, so implement it using compareDocumentPosition() - // https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect - // Note that compareDocumentPosition returns CONTAINED_BY if 'e2 *is_contained_by* e1': - // Also, compareDocumentPosition defines a node containing itself as false. - return Node.documentPositionContainedBy(e1.dom(), e2.dom()); - }; - - var browser = PlatformDetection.detect().browser; - - // Returns: true if node e1 contains e2, otherwise false. - // (returns false if e1===e2: A node does not contain itself). - var contains = browser.isIE() ? ieContains : regularContains; - - return { - eq: eq, - isEqualNode: isEqualNode, - member: member, - contains: contains, - - // Only used by DomUniverse. Remove (or should Selectors.is move here?) - is: Selectors.is - }; - } - ); - - /** - * ScrollIntoView.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.dom.ScrollIntoView', [ - 'tinymce.core.dom.NodeType' - ], - function(NodeType) { - var getPos = function(elm) { - var x = 0, - y = 0; - - var offsetParent = elm; - while (offsetParent && offsetParent.nodeType) { - x += offsetParent.offsetLeft || 0; - y += offsetParent.offsetTop || 0; - offsetParent = offsetParent.offsetParent; - } - - return { - x: x, - y: y - }; - }; - - var fireScrollIntoViewEvent = function(editor, elm, alignToTop) { - var scrollEvent = { - elm: elm, - alignToTop: alignToTop - }; - editor.fire('scrollIntoView', scrollEvent); - return scrollEvent.isDefaultPrevented(); - }; - - var scrollIntoView = function(editor, elm, alignToTop) { - var y, viewPort, dom = editor.dom, - root = dom.getRoot(), - viewPortY, viewPortH, offsetY = 0; - - if (fireScrollIntoViewEvent(editor, elm, alignToTop)) { - return; - } - - if (!NodeType.isElement(elm)) { - return; - } - - if (alignToTop === false) { - offsetY = elm.offsetHeight; - } - - if (root.nodeName !== 'BODY') { - var scrollContainer = editor.selection.getScrollContainer(); - if (scrollContainer) { - y = getPos(elm).y - getPos(scrollContainer).y + offsetY; - viewPortH = scrollContainer.clientHeight; - viewPortY = scrollContainer.scrollTop; - if (y < viewPortY || y + 25 > viewPortY + viewPortH) { - scrollContainer.scrollTop = y < viewPortY ? y : y - viewPortH + 25; - } - - return; - } - } - - viewPort = dom.getViewPort(editor.getWin()); - y = dom.getPos(elm).y + offsetY; - viewPortY = viewPort.y; - viewPortH = viewPort.h; - if (y < viewPort.y || y + 25 > viewPortY + viewPortH) { - editor.getWin().scrollTo(0, y < viewPortY ? y : y - viewPortH + 25); - } - }; - - return { - scrollIntoView: scrollIntoView - }; - } - ); - - /** - * TridentSelection.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Selection class for old explorer versions. This one fakes the - * native selection object available on modern browsers. - * - * @private - * @class tinymce.dom.TridentSelection - */ - define( - 'tinymce.core.dom.TridentSelection', [], - function() { - function Selection(selection) { - var self = this, - dom = selection.dom, - FALSE = false; - - function getPosition(rng, start) { - var checkRng, startIndex = 0, - endIndex, inside, - children, child, offset, index, position = -1, - parent; - - // Setup test range, collapse it and get the parent - checkRng = rng.duplicate(); - checkRng.collapse(start); - parent = checkRng.parentElement(); - - // Check if the selection is within the right document - if (parent.ownerDocument !== selection.dom.doc) { - return; - } - - // IE will report non editable elements as it's parent so look for an editable one - while (parent.contentEditable === "false") { - parent = parent.parentNode; - } - - // If parent doesn't have any children then return that we are inside the element - if (!parent.hasChildNodes()) { - return { - node: parent, - inside: 1 - }; - } - - // Setup node list and endIndex - children = parent.children; - endIndex = children.length - 1; - - // Perform a binary search for the position - while (startIndex <= endIndex) { - index = Math.floor((startIndex + endIndex) / 2); - - // Move selection to node and compare the ranges - child = children[index]; - checkRng.moveToElementText(child); - position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng); - - // Before/after or an exact match - if (position > 0) { - endIndex = index - 1; - } else if (position < 0) { - startIndex = index + 1; - } else { - return { - node: child - }; - } - } - - // Check if child position is before or we didn't find a position - if (position < 0) { - // No element child was found use the parent element and the offset inside that - if (!child) { - checkRng.moveToElementText(parent); - checkRng.collapse(true); - child = parent; - inside = true; - } else { - checkRng.collapse(false); - } - - // Walk character by character in text node until we hit the selected range endpoint, - // hit the end of document or parent isn't the right one - // We need to walk char by char since rng.text or rng.htmlText will trim line endings - offset = 0; - while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) { - if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) { - break; - } - - offset++; - } - } else { - // Child position is after the selection endpoint - checkRng.collapse(true); - - // Walk character by character in text node until we hit the selected range endpoint, hit - // the end of document or parent isn't the right one - offset = 0; - while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) { - if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) { - break; - } - - offset++; - } - } - - return { - node: child, - position: position, - offset: offset, - inside: inside - }; - } - - // Returns a W3C DOM compatible range object by using the IE Range API - function getRange() { - var ieRange = selection.getRng(), - domRange = dom.createRng(), - element, collapsed, tmpRange, element2, bookmark; - - // If selection is outside the current document just return an empty range - element = ieRange.item ? ieRange.item(0) : ieRange.parentElement(); - if (element.ownerDocument != dom.doc) { - return domRange; - } - - collapsed = selection.isCollapsed(); - - // Handle control selection - if (ieRange.item) { - domRange.setStart(element.parentNode, dom.nodeIndex(element)); - domRange.setEnd(domRange.startContainer, domRange.startOffset + 1); - - return domRange; - } - - function findEndPoint(start) { - var endPoint = getPosition(ieRange, start), - container, offset, textNodeOffset = 0, - sibling, undef, nodeValue; - - container = endPoint.node; - offset = endPoint.offset; - - if (endPoint.inside && !container.hasChildNodes()) { - domRange[start ? 'setStart' : 'setEnd'](container, 0); - return; - } - - if (offset === undef) { - domRange[start ? 'setStartBefore' : 'setEndAfter'](container); - return; - } - - if (endPoint.position < 0) { - sibling = endPoint.inside ? container.firstChild : container.nextSibling; - - if (!sibling) { - domRange[start ? 'setStartAfter' : 'setEndAfter'](container); - return; - } - - if (!offset) { - if (sibling.nodeType == 3) { - domRange[start ? 'setStart' : 'setEnd'](sibling, 0); - } else { - domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling); - } - - return; - } - - // Find the text node and offset - while (sibling) { - if (sibling.nodeType == 3) { - nodeValue = sibling.nodeValue; - textNodeOffset += nodeValue.length; - - // We are at or passed the position we where looking for - if (textNodeOffset >= offset) { - container = sibling; - textNodeOffset -= offset; - textNodeOffset = nodeValue.length - textNodeOffset; - break; - } - } - - sibling = sibling.nextSibling; - } - } else { - // Find the text node and offset - sibling = container.previousSibling; - - if (!sibling) { - return domRange[start ? 'setStartBefore' : 'setEndBefore'](container); - } - - // If there isn't any text to loop then use the first position - if (!offset) { - if (container.nodeType == 3) { - domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length); - } else { - domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling); - } - - return; - } - - while (sibling) { - if (sibling.nodeType == 3) { - textNodeOffset += sibling.nodeValue.length; - - // We are at or passed the position we where looking for - if (textNodeOffset >= offset) { - container = sibling; - textNodeOffset -= offset; - break; - } - } - - sibling = sibling.previousSibling; - } - } - - domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset); - } - - try { - // Find start point - findEndPoint(true); - - // Find end point if needed - if (!collapsed) { - findEndPoint(); - } - } catch (ex) { - // IE has a nasty bug where text nodes might throw "invalid argument" when you - // access the nodeValue or other properties of text nodes. This seems to happen when - // text nodes are split into two nodes by a delete/backspace call. - // So let us detect and try to fix it. - if (ex.number == -2147024809) { - // Get the current selection - bookmark = self.getBookmark(2); - - // Get start element - tmpRange = ieRange.duplicate(); - tmpRange.collapse(true); - element = tmpRange.parentElement(); - - // Get end element - if (!collapsed) { - tmpRange = ieRange.duplicate(); - tmpRange.collapse(false); - element2 = tmpRange.parentElement(); - element2.innerHTML = element2.innerHTML; - } - - // Remove the broken elements - element.innerHTML = element.innerHTML; - - // Restore the selection - self.moveToBookmark(bookmark); - - // Since the range has moved we need to re-get it - ieRange = selection.getRng(); - - // Find start point - findEndPoint(true); - - // Find end point if needed - if (!collapsed) { - findEndPoint(); - } - } else { - throw ex; // Throw other errors - } - } - - return domRange; - } - - this.getBookmark = function(type) { - var rng = selection.getRng(), - bookmark = {}; - - function getIndexes(node) { - var parent, root, children, i, indexes = []; - - parent = node.parentNode; - root = dom.getRoot().parentNode; - - while (parent != root && parent.nodeType !== 9) { - children = parent.children; - - i = children.length; - while (i--) { - if (node === children[i]) { - indexes.push(i); - break; - } - } - - node = parent; - parent = parent.parentNode; - } - - return indexes; - } - - function getBookmarkEndPoint(start) { - var position; - - position = getPosition(rng, start); - if (position) { - return { - position: position.position, - offset: position.offset, - indexes: getIndexes(position.node), - inside: position.inside - }; - } - } - - // Non ubstructive bookmark - if (type === 2) { - // Handle text selection - if (!rng.item) { - bookmark.start = getBookmarkEndPoint(true); - - if (!selection.isCollapsed()) { - bookmark.end = getBookmarkEndPoint(); - } - } else { - bookmark.start = { - ctrl: true, - indexes: getIndexes(rng.item(0)) - }; - } - } - - return bookmark; - }; - - this.moveToBookmark = function(bookmark) { - var rng, body = dom.doc.body; - - function resolveIndexes(indexes) { - var node, i, idx, children; - - node = dom.getRoot(); - for (i = indexes.length - 1; i >= 0; i--) { - children = node.children; - idx = indexes[i]; - - if (idx <= children.length - 1) { - node = children[idx]; - } - } - - return node; - } - - function setBookmarkEndPoint(start) { - var endPoint = bookmark[start ? 'start' : 'end'], - moveLeft, moveRng, undef, offset; - - if (endPoint) { - moveLeft = endPoint.position > 0; - - moveRng = body.createTextRange(); - moveRng.moveToElementText(resolveIndexes(endPoint.indexes)); - - offset = endPoint.offset; - if (offset !== undef) { - moveRng.collapse(endPoint.inside || moveLeft); - moveRng.moveStart('character', moveLeft ? -offset : offset); - } else { - moveRng.collapse(start); - } - - rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng); - - if (start) { - rng.collapse(true); - } - } - } - - if (bookmark.start) { - if (bookmark.start.ctrl) { - rng = body.createControlRange(); - rng.addElement(resolveIndexes(bookmark.start.indexes)); - rng.select(); - } else { - rng = body.createTextRange(); - setBookmarkEndPoint(true); - setBookmarkEndPoint(); - rng.select(); - } - } - }; - - this.addRange = function(rng) { - var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling, - doc = selection.dom.doc, - body = doc.body, - nativeRng, ctrlElm; - - function setEndPoint(start) { - var container, offset, marker, tmpRng, nodes; - - marker = dom.create('a'); - container = start ? startContainer : endContainer; - offset = start ? startOffset : endOffset; - tmpRng = ieRng.duplicate(); - - if (container == doc || container == doc.documentElement) { - container = body; - offset = 0; - } - - if (container.nodeType == 3) { - container.parentNode.insertBefore(marker, container); - tmpRng.moveToElementText(marker); - tmpRng.moveStart('character', offset); - dom.remove(marker); - ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng); - } else { - nodes = container.childNodes; - - if (nodes.length) { - if (offset >= nodes.length) { - dom.insertAfter(marker, nodes[nodes.length - 1]); - } else { - container.insertBefore(marker, nodes[offset]); - } - - tmpRng.moveToElementText(marker); - } else if (container.canHaveHTML) { - // Empty node selection for example <div>|</div> - // Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open - container.innerHTML = '<span></span>'; - marker = container.firstChild; - tmpRng.moveToElementText(marker); - tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason - } - - ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng); - dom.remove(marker); - } - } - - // Setup some shorter versions - startContainer = rng.startContainer; - startOffset = rng.startOffset; - endContainer = rng.endContainer; - endOffset = rng.endOffset; - ieRng = body.createTextRange(); - - // If single element selection then try making a control selection out of it - if (startContainer == endContainer && startContainer.nodeType == 1) { - // Trick to place the caret inside an empty block element like <p></p> - if (startOffset == endOffset && !startContainer.hasChildNodes()) { - if (startContainer.canHaveHTML) { - // Check if previous sibling is an empty block if it is then we need to render it - // IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236 - // Example this: <p></p><p>|</p> would become this: <p>|</p><p></p> - sibling = startContainer.previousSibling; - if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) { - sibling.innerHTML = ''; - } else { - sibling = null; - } - - startContainer.innerHTML = '<span></span><span></span>'; - ieRng.moveToElementText(startContainer.lastChild); - ieRng.select(); - dom.doc.selection.clear(); - startContainer.innerHTML = ''; - - if (sibling) { - sibling.innerHTML = ''; - } - return; - } - - startOffset = dom.nodeIndex(startContainer); - startContainer = startContainer.parentNode; - } - - if (startOffset == endOffset - 1) { - try { - ctrlElm = startContainer.childNodes[startOffset]; - ctrlRng = body.createControlRange(); - ctrlRng.addElement(ctrlElm); - ctrlRng.select(); - - // Check if the range produced is on the correct element and is a control range - // On IE 8 it will select the parent contentEditable container if you select an inner element see: #5398 - nativeRng = selection.getRng(); - if (nativeRng.item && ctrlElm === nativeRng.item(0)) { - return; - } - } catch (ex) { - // Ignore - } - } - } - - // Set start/end point of selection - setEndPoint(true); - setEndPoint(); - - // Select the new range and scroll it into view - ieRng.select(); - }; - - // Expose range method - this.getRangeAt = getRange; - } - - return Selection; - } - ); - - define( - 'ephox.katamari.api.Type', - - [ - 'global!Array', - 'global!String' - ], - - function(Array, String) { - var typeOf = function(x) { - if (x === null) return 'null'; - var t = typeof x; - if (t === 'object' && Array.prototype.isPrototypeOf(x)) return 'array'; - if (t === 'object' && String.prototype.isPrototypeOf(x)) return 'string'; - return t; - }; - - var isType = function(type) { - return function(value) { - return typeOf(value) === type; - }; - }; - - return { - isString: isType('string'), - isObject: isType('object'), - isArray: isType('array'), - isNull: isType('null'), - isBoolean: isType('boolean'), - isUndefined: isType('undefined'), - isFunction: isType('function'), - isNumber: isType('number') - }; - } - ); - - - define( - 'ephox.katamari.data.Immutable', - - [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Fun', - 'global!Array', - 'global!Error' - ], - - function(Arr, Fun, Array, Error) { - return function() { - var fields = arguments; - return function( /* values */ ) { - // Don't use array slice(arguments), makes the whole function unoptimisable on Chrome - var values = new Array(arguments.length); - for (var i = 0; i < values.length; i++) values[i] = arguments[i]; - - if (fields.length !== values.length) - throw new Error('Wrong number of arguments to struct. Expected "[' + fields.length + ']", got ' + values.length + ' arguments'); - - var struct = {}; - Arr.each(fields, function(name, i) { - struct[name] = Fun.constant(values[i]); - }); - return struct; - }; - }; - } - ); - - define( - 'ephox.katamari.api.Obj', - - [ - 'ephox.katamari.api.Option', - 'global!Object' - ], - - function(Option, Object) { - // There are many variations of Object iteration that are faster than the 'for-in' style: - // http://jsperf.com/object-keys-iteration/107 - // - // Use the native keys if it is available (IE9+), otherwise fall back to manually filtering - var keys = (function() { - var fastKeys = Object.keys; - - // This technically means that 'each' and 'find' on IE8 iterate through the object twice. - // This code doesn't run on IE8 much, so it's an acceptable tradeoff. - // If it becomes a problem we can always duplicate the feature detection inside each and find as well. - var slowKeys = function(o) { - var r = []; - for (var i in o) { - if (o.hasOwnProperty(i)) { - r.push(i); - } - } - return r; - }; - - return fastKeys === undefined ? slowKeys : fastKeys; - })(); - - - var each = function(obj, f) { - var props = keys(obj); - for (var k = 0, len = props.length; k < len; k++) { - var i = props[k]; - var x = obj[i]; - f(x, i, obj); - } - }; - - /** objectMap :: (JsObj(k, v), (v, k, JsObj(k, v) -> x)) -> JsObj(k, x) */ - var objectMap = function(obj, f) { - return tupleMap(obj, function(x, i, obj) { - return { - k: i, - v: f(x, i, obj) - }; - }); - }; - - /** tupleMap :: (JsObj(k, v), (v, k, JsObj(k, v) -> { k: x, v: y })) -> JsObj(x, y) */ - var tupleMap = function(obj, f) { - var r = {}; - each(obj, function(x, i) { - var tuple = f(x, i, obj); - r[tuple.k] = tuple.v; - }); - return r; - }; - - /** bifilter :: (JsObj(k, v), (v, k -> Bool)) -> { t: JsObj(k, v), f: JsObj(k, v) } */ - var bifilter = function(obj, pred) { - var t = {}; - var f = {}; - each(obj, function(x, i) { - var branch = pred(x, i) ? t : f; - branch[i] = x; - }); - return { - t: t, - f: f - }; - }; - - /** mapToArray :: (JsObj(k, v), (v, k -> a)) -> [a] */ - var mapToArray = function(obj, f) { - var r = []; - each(obj, function(value, name) { - r.push(f(value, name)); - }); - return r; - }; - - /** find :: (JsObj(k, v), (v, k, JsObj(k, v) -> Bool)) -> Option v */ - var find = function(obj, pred) { - var props = keys(obj); - for (var k = 0, len = props.length; k < len; k++) { - var i = props[k]; - var x = obj[i]; - if (pred(x, i, obj)) { - return Option.some(x); - } - } - return Option.none(); - }; - - /** values :: JsObj(k, v) -> [v] */ - var values = function(obj) { - return mapToArray(obj, function(v) { - return v; - }); - }; - - var size = function(obj) { - return values(obj).length; - }; - - return { - bifilter: bifilter, - each: each, - map: objectMap, - mapToArray: mapToArray, - tupleMap: tupleMap, - find: find, - keys: keys, - values: values, - size: size - }; - } - ); - define( - 'ephox.katamari.util.BagUtils', - - [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Type', - 'global!Error' - ], - - function(Arr, Type, Error) { - var sort = function(arr) { - return arr.slice(0).sort(); - }; - - var reqMessage = function(required, keys) { - throw new Error('All required keys (' + sort(required).join(', ') + ') were not specified. Specified keys were: ' + sort(keys).join(', ') + '.'); - }; - - var unsuppMessage = function(unsupported) { - throw new Error('Unsupported keys for object: ' + sort(unsupported).join(', ')); - }; - - var validateStrArr = function(label, array) { - if (!Type.isArray(array)) throw new Error('The ' + label + ' fields must be an array. Was: ' + array + '.'); - Arr.each(array, function(a) { - if (!Type.isString(a)) throw new Error('The value ' + a + ' in the ' + label + ' fields was not a string.'); - }); - }; - - var invalidTypeMessage = function(incorrect, type) { - throw new Error('All values need to be of type: ' + type + '. Keys (' + sort(incorrect).join(', ') + ') were not.'); - }; - - var checkDupes = function(everything) { - var sorted = sort(everything); - var dupe = Arr.find(sorted, function(s, i) { - return i < sorted.length - 1 && s === sorted[i + 1]; - }); - - dupe.each(function(d) { - throw new Error('The field: ' + d + ' occurs more than once in the combined fields: [' + sorted.join(', ') + '].'); - }); - }; - - return { - sort: sort, - reqMessage: reqMessage, - unsuppMessage: unsuppMessage, - validateStrArr: validateStrArr, - invalidTypeMessage: invalidTypeMessage, - checkDupes: checkDupes - }; - } - ); - define( - 'ephox.katamari.data.MixedBag', - - [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Obj', - 'ephox.katamari.api.Option', - 'ephox.katamari.util.BagUtils', - 'global!Error', - 'global!Object' - ], - - function(Arr, Fun, Obj, Option, BagUtils, Error, Object) { - - return function(required, optional) { - var everything = required.concat(optional); - if (everything.length === 0) throw new Error('You must specify at least one required or optional field.'); - - BagUtils.validateStrArr('required', required); - BagUtils.validateStrArr('optional', optional); - - BagUtils.checkDupes(everything); - - return function(obj) { - var keys = Obj.keys(obj); - - // Ensure all required keys are present. - var allReqd = Arr.forall(required, function(req) { - return Arr.contains(keys, req); - }); - - if (!allReqd) BagUtils.reqMessage(required, keys); - - var unsupported = Arr.filter(keys, function(key) { - return !Arr.contains(everything, key); - }); - - if (unsupported.length > 0) BagUtils.unsuppMessage(unsupported); - - var r = {}; - Arr.each(required, function(req) { - r[req] = Fun.constant(obj[req]); - }); - - Arr.each(optional, function(opt) { - r[opt] = Fun.constant(Object.prototype.hasOwnProperty.call(obj, opt) ? Option.some(obj[opt]) : Option.none()); - }); - - return r; - }; - }; - } - ); - define( - 'ephox.katamari.api.Struct', - - [ - 'ephox.katamari.data.Immutable', - 'ephox.katamari.data.MixedBag' - ], - - function(Immutable, MixedBag) { - return { - immutable: Immutable, - immutableBag: MixedBag - }; - } - ); - - define( - 'ephox.sugar.alien.Recurse', - - [ - - ], - - function() { - /** - * Applies f repeatedly until it completes (by returning Option.none()). - * - * Normally would just use recursion, but JavaScript lacks tail call optimisation. - * - * This is what recursion looks like when manually unravelled :) - */ - var toArray = function(target, f) { - var r = []; - - var recurse = function(e) { - r.push(e); - return f(e); - }; - - var cur = f(target); - do { - cur = cur.bind(recurse); - } while (cur.isSome()); - - return r; - }; - - return { - toArray: toArray - }; - } - ); - define( - 'ephox.sugar.api.search.Traverse', - - [ - 'ephox.katamari.api.Type', - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Option', - 'ephox.katamari.api.Struct', - 'ephox.sugar.alien.Recurse', - 'ephox.sugar.api.dom.Compare', - 'ephox.sugar.api.node.Element' - ], - - function(Type, Arr, Fun, Option, Struct, Recurse, Compare, Element) { - // The document associated with the current element - var owner = function(element) { - return Element.fromDom(element.dom().ownerDocument); - }; - - var documentElement = function(element) { - // TODO: Avoid unnecessary wrap/unwrap here - var doc = owner(element); - return Element.fromDom(doc.dom().documentElement); - }; - - // The window element associated with the element - var defaultView = function(element) { - var el = element.dom(); - var defaultView = el.ownerDocument.defaultView; - return Element.fromDom(defaultView); - }; - - var parent = function(element) { - var dom = element.dom(); - return Option.from(dom.parentNode).map(Element.fromDom); - }; - - var findIndex = function(element) { - return parent(element).bind(function(p) { - // TODO: Refactor out children so we can avoid the constant unwrapping - var kin = children(p); - return Arr.findIndex(kin, function(elem) { - return Compare.eq(element, elem); - }); - }); - }; - - var parents = function(element, isRoot) { - var stop = Type.isFunction(isRoot) ? isRoot : Fun.constant(false); - - // This is used a *lot* so it needs to be performant, not recursive - var dom = element.dom(); - var ret = []; - - while (dom.parentNode !== null && dom.parentNode !== undefined) { - var rawParent = dom.parentNode; - var parent = Element.fromDom(rawParent); - ret.push(parent); - - if (stop(parent) === true) break; - else dom = rawParent; - } - return ret; - }; - - var siblings = function(element) { - // TODO: Refactor out children so we can just not add self instead of filtering afterwards - var filterSelf = function(elements) { - return Arr.filter(elements, function(x) { - return !Compare.eq(element, x); - }); - }; - - return parent(element).map(children).map(filterSelf).getOr([]); - }; - - var offsetParent = function(element) { - var dom = element.dom(); - return Option.from(dom.offsetParent).map(Element.fromDom); - }; - - var prevSibling = function(element) { - var dom = element.dom(); - return Option.from(dom.previousSibling).map(Element.fromDom); - }; - - var nextSibling = function(element) { - var dom = element.dom(); - return Option.from(dom.nextSibling).map(Element.fromDom); - }; - - var prevSiblings = function(element) { - // This one needs to be reversed, so they're still in DOM order - return Arr.reverse(Recurse.toArray(element, prevSibling)); - }; - - var nextSiblings = function(element) { - return Recurse.toArray(element, nextSibling); - }; - - var children = function(element) { - var dom = element.dom(); - return Arr.map(dom.childNodes, Element.fromDom); - }; - - var child = function(element, index) { - var children = element.dom().childNodes; - return Option.from(children[index]).map(Element.fromDom); - }; - - var firstChild = function(element) { - return child(element, 0); - }; - - var lastChild = function(element) { - return child(element, element.dom().childNodes.length - 1); - }; - - var childNodesCount = function(element, index) { - return element.dom().childNodes.length; - }; - - var spot = Struct.immutable('element', 'offset'); - var leaf = function(element, offset) { - var cs = children(element); - return cs.length > 0 && offset < cs.length ? spot(cs[offset], 0) : spot(element, offset); - }; - - return { - owner: owner, - defaultView: defaultView, - documentElement: documentElement, - parent: parent, - findIndex: findIndex, - parents: parents, - siblings: siblings, - prevSibling: prevSibling, - offsetParent: offsetParent, - prevSiblings: prevSiblings, - nextSibling: nextSibling, - nextSiblings: nextSiblings, - children: children, - child: child, - firstChild: firstChild, - lastChild: lastChild, - childNodesCount: childNodesCount, - leaf: leaf - }; - } - ); - - define( - 'ephox.sugar.api.dom.Insert', - - [ - 'ephox.sugar.api.search.Traverse' - ], - - function(Traverse) { - var before = function(marker, element) { - var parent = Traverse.parent(marker); - parent.each(function(v) { - v.dom().insertBefore(element.dom(), marker.dom()); - }); - }; - - var after = function(marker, element) { - var sibling = Traverse.nextSibling(marker); - sibling.fold(function() { - var parent = Traverse.parent(marker); - parent.each(function(v) { - append(v, element); - }); - }, function(v) { - before(v, element); - }); - }; - - var prepend = function(parent, element) { - var firstChild = Traverse.firstChild(parent); - firstChild.fold(function() { - append(parent, element); - }, function(v) { - parent.dom().insertBefore(element.dom(), v.dom()); - }); - }; - - var append = function(parent, element) { - parent.dom().appendChild(element.dom()); - }; - - var appendAt = function(parent, element, index) { - Traverse.child(parent, index).fold(function() { - append(parent, element); - }, function(v) { - before(v, element); - }); - }; - - var wrap = function(element, wrapper) { - before(element, wrapper); - append(wrapper, element); - }; - - return { - before: before, - after: after, - prepend: prepend, - append: append, - appendAt: appendAt, - wrap: wrap - }; - } - ); - - define( - 'ephox.sugar.api.node.Node', - - [ - 'ephox.sugar.api.node.NodeTypes' - ], - - function(NodeTypes) { - var name = function(element) { - var r = element.dom().nodeName; - return r.toLowerCase(); - }; - - var type = function(element) { - return element.dom().nodeType; - }; - - var value = function(element) { - return element.dom().nodeValue; - }; - - var isType = function(t) { - return function(element) { - return type(element) === t; - }; - }; - - var isComment = function(element) { - return type(element) === NodeTypes.COMMENT || name(element) === '#comment'; - }; - - var isElement = isType(NodeTypes.ELEMENT); - var isText = isType(NodeTypes.TEXT); - var isDocument = isType(NodeTypes.DOCUMENT); - - return { - name: name, - type: type, - value: value, - isElement: isElement, - isText: isText, - isDocument: isDocument, - isComment: isComment - }; - } - ); - - define( - 'ephox.sugar.api.properties.Attr', - - [ - 'ephox.katamari.api.Type', - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Obj', - 'ephox.sugar.api.node.Node', - 'global!Error', - 'global!console' - ], - - /* - * Direct attribute manipulation has been around since IE8, but - * was apparently unstable until IE10. - */ - function(Type, Arr, Obj, Node, Error, console) { - var rawSet = function(dom, key, value) { - /* - * JQuery coerced everything to a string, and silently did nothing on text node/null/undefined. - * - * We fail on those invalid cases, only allowing numbers and booleans. - */ - if (Type.isString(value) || Type.isBoolean(value) || Type.isNumber(value)) { - dom.setAttribute(key, value + ''); - } else { - console.error('Invalid call to Attr.set. Key ', key, ':: Value ', value, ':: Element ', dom); - throw new Error('Attribute value was not simple'); - } - }; - - var set = function(element, key, value) { - rawSet(element.dom(), key, value); - }; - - var setAll = function(element, attrs) { - var dom = element.dom(); - Obj.each(attrs, function(v, k) { - rawSet(dom, k, v); - }); - }; - - var get = function(element, key) { - var v = element.dom().getAttribute(key); - - // undefined is the more appropriate value for JS, and this matches JQuery - return v === null ? undefined : v; - }; - - var has = function(element, key) { - var dom = element.dom(); - - // return false for non-element nodes, no point in throwing an error - return dom && dom.hasAttribute ? dom.hasAttribute(key) : false; - }; - - var remove = function(element, key) { - element.dom().removeAttribute(key); - }; - - var hasNone = function(element) { - var attrs = element.dom().attributes; - return attrs === undefined || attrs === null || attrs.length === 0; - }; - - var clone = function(element) { - return Arr.foldl(element.dom().attributes, function(acc, attr) { - acc[attr.name] = attr.value; - return acc; - }, {}); - }; - - var transferOne = function(source, destination, attr) { - // NOTE: We don't want to clobber any existing attributes - if (has(source, attr) && !has(destination, attr)) set(destination, attr, get(source, attr)); - }; - - // Transfer attributes(attrs) from source to destination, unless they are already present - var transfer = function(source, destination, attrs) { - if (!Node.isElement(source) || !Node.isElement(destination)) return; - Arr.each(attrs, function(attr) { - transferOne(source, destination, attr); - }); - }; - - return { - clone: clone, - set: set, - setAll: setAll, - get: get, - has: has, - remove: remove, - hasNone: hasNone, - transfer: transfer - }; - } - ); - - define( - 'ephox.sugar.api.dom.InsertAll', - - [ - 'ephox.katamari.api.Arr', - 'ephox.sugar.api.dom.Insert' - ], - - function(Arr, Insert) { - var before = function(marker, elements) { - Arr.each(elements, function(x) { - Insert.before(marker, x); - }); - }; - - var after = function(marker, elements) { - Arr.each(elements, function(x, i) { - var e = i === 0 ? marker : elements[i - 1]; - Insert.after(e, x); - }); - }; - - var prepend = function(parent, elements) { - Arr.each(elements.slice().reverse(), function(x) { - Insert.prepend(parent, x); - }); - }; - - var append = function(parent, elements) { - Arr.each(elements, function(x) { - Insert.append(parent, x); - }); - }; - - return { - before: before, - after: after, - prepend: prepend, - append: append - }; - } - ); - - define( - 'ephox.sugar.api.dom.Remove', - - [ - 'ephox.katamari.api.Arr', - 'ephox.sugar.api.dom.InsertAll', - 'ephox.sugar.api.search.Traverse' - ], - - function(Arr, InsertAll, Traverse) { - var empty = function(element) { - // shortcut "empty node" trick. Requires IE 9. - element.dom().textContent = ''; - - // If the contents was a single empty text node, the above doesn't remove it. But, it's still faster in general - // than removing every child node manually. - // The following is (probably) safe for performance as 99.9% of the time the trick works and - // Traverse.children will return an empty array. - Arr.each(Traverse.children(element), function(rogue) { - remove(rogue); - }); - }; - - var remove = function(element) { - var dom = element.dom(); - if (dom.parentNode !== null) - dom.parentNode.removeChild(dom); - }; - - var unwrap = function(wrapper) { - var children = Traverse.children(wrapper); - if (children.length > 0) - InsertAll.before(wrapper, children); - remove(wrapper); - }; - - return { - empty: empty, - remove: remove, - unwrap: unwrap - }; - } - ); - - define( - 'ephox.sugar.api.dom.Replication', - - [ - 'ephox.sugar.api.properties.Attr', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.api.dom.Insert', - 'ephox.sugar.api.dom.InsertAll', - 'ephox.sugar.api.dom.Remove', - 'ephox.sugar.api.search.Traverse' - ], - - function(Attr, Element, Insert, InsertAll, Remove, Traverse) { - var clone = function(original, deep) { - return Element.fromDom(original.dom().cloneNode(deep)); - }; - - /** Shallow clone - just the tag, no children */ - var shallow = function(original) { - return clone(original, false); - }; - - /** Deep clone - everything copied including children */ - var deep = function(original) { - return clone(original, true); - }; - - /** Shallow clone, with a new tag */ - var shallowAs = function(original, tag) { - var nu = Element.fromTag(tag); - - var attributes = Attr.clone(original); - Attr.setAll(nu, attributes); - - return nu; - }; - - /** Deep clone, with a new tag */ - var copy = function(original, tag) { - var nu = shallowAs(original, tag); - - // NOTE - // previously this used serialisation: - // nu.dom().innerHTML = original.dom().innerHTML; - // - // Clone should be equivalent (and faster), but if TD <-> TH toggle breaks, put it back. - - var cloneChildren = Traverse.children(deep(original)); - InsertAll.append(nu, cloneChildren); - - return nu; - }; - - /** Change the tag name, but keep all children */ - var mutate = function(original, tag) { - var nu = shallowAs(original, tag); - - Insert.before(original, nu); - var children = Traverse.children(original); - InsertAll.append(nu, children); - Remove.remove(original); - return nu; - }; - - return { - shallow: shallow, - shallowAs: shallowAs, - deep: deep, - copy: copy, - mutate: mutate - }; - } - ); - - define( - 'ephox.sugar.api.node.Fragment', - - [ - 'ephox.katamari.api.Arr', - 'ephox.sugar.api.node.Element', - 'global!document' - ], - - function(Arr, Element, document) { - var fromElements = function(elements, scope) { - var doc = scope || document; - var fragment = doc.createDocumentFragment(); - Arr.each(elements, function(element) { - fragment.appendChild(element.dom()); - }); - return Element.fromDom(fragment); - }; - - return { - fromElements: fromElements - }; - } - ); - - /** - * ElementType.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.dom.ElementType', [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Fun', - 'ephox.sugar.api.node.Node' - ], - function(Arr, Fun, Node) { - var blocks = [ - 'article', 'aside', 'details', 'div', 'dt', 'figcaption', 'footer', - 'form', 'fieldset', 'header', 'hgroup', 'html', 'main', 'nav', - 'section', 'summary', 'body', 'p', 'dl', 'multicol', 'dd', 'figure', - 'address', 'center', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', - 'listing', 'xmp', 'pre', 'plaintext', 'menu', 'dir', 'ul', 'ol', 'li', 'hr', - 'table', 'tbody', 'thead', 'tfoot', 'th', 'tr', 'td', 'caption' - ]; - - var voids = [ - 'area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', - 'isindex', 'link', 'meta', 'param', 'embed', 'source', 'wbr', 'track' - ]; - - var tableCells = ['td', 'th']; - - var textBlocks = [ - 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'address', 'pre', 'form', - 'blockquote', 'center', 'dir', 'fieldset', 'header', 'footer', 'article', - 'section', 'hgroup', 'aside', 'nav', 'figure' - ]; - - var headings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; - - var lazyLookup = function(items) { - var lookup; - return function(node) { - lookup = lookup ? lookup : Arr.mapToObject(items, Fun.constant(true)); - return lookup.hasOwnProperty(Node.name(node)); - }; - }; - - var isHeading = lazyLookup(headings); - - var isBlock = lazyLookup(blocks); - - var isInline = function(node) { - return Node.isElement(node) && !isBlock(node); - }; - - return { - isBlock: isBlock, - isInline: isInline, - isHeading: isHeading, - isTextBlock: lazyLookup(textBlocks), - isVoid: lazyLookup(voids), - isTableCell: lazyLookup(tableCells) - }; - } - ); - - /** - * Parents.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.dom.Parents', [ - 'ephox.katamari.api.Fun', - 'ephox.sugar.api.dom.Compare', - 'ephox.sugar.api.search.Traverse' - ], - function(Fun, Compare, Traverse) { - var dropLast = function(xs) { - return xs.slice(0, -1); - }; - - var parentsUntil = function(startNode, rootElm, predicate) { - if (Compare.contains(rootElm, startNode)) { - return dropLast(Traverse.parents(startNode, function(elm) { - return predicate(elm) || Compare.eq(elm, rootElm); - })); - } else { - return []; - } - }; - - var parents = function(startNode, rootElm) { - return parentsUntil(startNode, rootElm, Fun.constant(false)); - }; - - var parentsAndSelf = function(startNode, rootElm) { - return [startNode].concat(parents(startNode, rootElm)); - }; - - return { - parentsUntil: parentsUntil, - parents: parents, - parentsAndSelf: parentsAndSelf - }; - } - ); - - define( - 'ephox.katamari.api.Options', - - [ - 'ephox.katamari.api.Option' - ], - - function(Option) { - /** cat :: [Option a] -> [a] */ - var cat = function(arr) { - var r = []; - var push = function(x) { - r.push(x); - }; - for (var i = 0; i < arr.length; i++) { - arr[i].each(push); - } - return r; - }; - - /** findMap :: ([a], (a, Int -> Option b)) -> Option b */ - var findMap = function(arr, f) { - for (var i = 0; i < arr.length; i++) { - var r = f(arr[i], i); - if (r.isSome()) { - return r; - } - } - return Option.none(); - }; - - /** - * if all elements in arr are 'some', their inner values are passed as arguments to f - * f must have arity arr.length - */ - var liftN = function(arr, f) { - var r = []; - for (var i = 0; i < arr.length; i++) { - var x = arr[i]; - if (x.isSome()) { - r.push(x.getOrDie()); - } else { - return Option.none(); - } - } - return Option.some(f.apply(null, r)); - }; - - return { - cat: cat, - findMap: findMap, - liftN: liftN - }; - } - ); - - /** - * SelectionUtils.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.selection.SelectionUtils', [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Option', - 'ephox.katamari.api.Options', - 'ephox.sugar.api.dom.Compare', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.api.node.Node', - 'ephox.sugar.api.search.Traverse', - 'tinymce.core.dom.NodeType' - ], - function(Arr, Fun, Option, Options, Compare, Element, Node, Traverse, NodeType) { - var getStartNode = function(rng) { - var sc = rng.startContainer, - so = rng.startOffset; - if (NodeType.isText(sc)) { - return so === 0 ? Option.some(Element.fromDom(sc)) : Option.none(); - } else { - return Option.from(sc.childNodes[so]).map(Element.fromDom); - } - }; - - var getEndNode = function(rng) { - var ec = rng.endContainer, - eo = rng.endOffset; - if (NodeType.isText(ec)) { - return eo === ec.data.length ? Option.some(Element.fromDom(ec)) : Option.none(); - } else { - return Option.from(ec.childNodes[eo - 1]).map(Element.fromDom); - } - }; - - var getFirstChildren = function(node) { - return Traverse.firstChild(node).fold( - Fun.constant([node]), - function(child) { - return [node].concat(getFirstChildren(child)); - } - ); - }; - - var getLastChildren = function(node) { - return Traverse.lastChild(node).fold( - Fun.constant([node]), - function(child) { - if (Node.name(child) === 'br') { - return Traverse.prevSibling(child).map(function(sibling) { - return [node].concat(getLastChildren(sibling)); - }).getOr([]); - } else { - return [node].concat(getLastChildren(child)); - } - } - ); - }; - - var hasAllContentsSelected = function(elm, rng) { - return Options.liftN([getStartNode(rng), getEndNode(rng)], function(startNode, endNode) { - var start = Arr.find(getFirstChildren(elm), Fun.curry(Compare.eq, startNode)); - var end = Arr.find(getLastChildren(elm), Fun.curry(Compare.eq, endNode)); - return start.isSome() && end.isSome(); - }).getOr(false); - }; - - return { - hasAllContentsSelected: hasAllContentsSelected - }; - } - ); - - /** - * FragmentReader.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.selection.FragmentReader', [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Fun', - 'ephox.sugar.api.dom.Insert', - 'ephox.sugar.api.dom.Replication', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.api.node.Fragment', - 'ephox.sugar.api.node.Node', - 'tinymce.core.dom.ElementType', - 'tinymce.core.dom.Parents', - 'tinymce.core.selection.SelectionUtils' - ], - function(Arr, Fun, Insert, Replication, Element, Fragment, Node, ElementType, Parents, SelectionUtils) { - var findParentListContainer = function(parents) { - return Arr.find(parents, function(elm) { - return Node.name(elm) === 'ul' || Node.name(elm) === 'ol'; - }); - }; - - var getFullySelectedListWrappers = function(parents, rng) { - return Arr.find(parents, function(elm) { - return Node.name(elm) === 'li' && SelectionUtils.hasAllContentsSelected(elm, rng); - }).fold( - Fun.constant([]), - function(li) { - return findParentListContainer(parents).map(function(listCont) { - return [ - Element.fromTag('li'), - Element.fromTag(Node.name(listCont)) - ]; - }).getOr([]); - } - ); - }; - - var wrap = function(innerElm, elms) { - var wrapped = Arr.foldl(elms, function(acc, elm) { - Insert.append(elm, acc); - return elm; - }, innerElm); - return elms.length > 0 ? Fragment.fromElements([wrapped]) : wrapped; - }; - - var getWrapElements = function(rootNode, rng) { - var parents = Parents.parentsAndSelf(Element.fromDom(rng.commonAncestorContainer), Element.fromDom(rootNode)); - var wrapElements = Arr.filter(parents, function(elm) { - return ElementType.isInline(elm) || ElementType.isHeading(elm); - }); - var fullWrappers = getFullySelectedListWrappers(parents, rng); - return Arr.map(wrapElements.concat(fullWrappers), Replication.shallow); - }; - - var getFragmentFromRange = function(rootNode, rng) { - return wrap(Element.fromDom(rng.cloneContents()), getWrapElements(rootNode, rng)); - }; - - var read = function(rootNode, rng) { - return rng.collapsed ? Fragment.fromElements([]) : getFragmentFromRange(rootNode, rng); - }; - - return { - read: read - }; - } - ); - - /** - * Selection.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class handles text and control selection it's an crossbrowser utility class. - * Consult the TinyMCE Wiki API for more details and examples on how to use this class. - * - * @class tinymce.dom.Selection - * @example - * // Getting the currently selected node for the active editor - * alert(tinymce.activeEditor.selection.getNode().nodeName); - */ - define( - 'tinymce.core.dom.Selection', [ - 'ephox.sugar.api.dom.Compare', - 'ephox.sugar.api.node.Element', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.dom.BookmarkManager', - 'tinymce.core.dom.ControlSelection', - 'tinymce.core.dom.NodeType', - 'tinymce.core.dom.RangeUtils', - 'tinymce.core.dom.ScrollIntoView', - 'tinymce.core.dom.TreeWalker', - 'tinymce.core.dom.TridentSelection', - 'tinymce.core.Env', - 'tinymce.core.selection.FragmentReader', - 'tinymce.core.text.Zwsp', - 'tinymce.core.util.Tools' - ], - function( - Compare, Element, CaretPosition, BookmarkManager, ControlSelection, NodeType, RangeUtils, ScrollIntoView, TreeWalker, TridentSelection, Env, FragmentReader, - Zwsp, Tools - ) { - var each = Tools.each, - trim = Tools.trim; - var isIE = Env.ie; - - var isAttachedToDom = function(node) { - return !!(node && node.ownerDocument) && Compare.contains(Element.fromDom(node.ownerDocument), Element.fromDom(node)); - }; - - var isValidRange = function(rng) { - if (!rng) { - return false; - } else if (rng.select) { // Native IE range still produced by placeCaretAt - return true; - } else { - return isAttachedToDom(rng.startContainer) && isAttachedToDom(rng.endContainer); - } - }; - - /** - * Constructs a new selection instance. - * - * @constructor - * @method Selection - * @param {tinymce.dom.DOMUtils} dom DOMUtils object reference. - * @param {Window} win Window to bind the selection object to. - * @param {tinymce.Editor} editor Editor instance of the selection. - * @param {tinymce.dom.Serializer} serializer DOM serialization class to use for getContent. - */ - function Selection(dom, win, serializer, editor) { - var self = this; - - self.dom = dom; - self.win = win; - self.serializer = serializer; - self.editor = editor; - self.bookmarkManager = new BookmarkManager(self); - self.controlSelection = new ControlSelection(self, editor); - - // No W3C Range support - if (!self.win.getSelection) { - self.tridentSel = new TridentSelection(self); - } - } - - Selection.prototype = { - /** - * Move the selection cursor range to the specified node and offset. - * If there is no node specified it will move it to the first suitable location within the body. - * - * @method setCursorLocation - * @param {Node} node Optional node to put the cursor in. - * @param {Number} offset Optional offset from the start of the node to put the cursor at. - */ - setCursorLocation: function(node, offset) { - var self = this, - rng = self.dom.createRng(); - - if (!node) { - self._moveEndPoint(rng, self.editor.getBody(), true); - self.setRng(rng); - } else { - rng.setStart(node, offset); - rng.setEnd(node, offset); - self.setRng(rng); - self.collapse(false); - } - }, - - /** - * Returns the selected contents using the DOM serializer passed in to this class. - * - * @method getContent - * @param {Object} args Optional settings class with for example output format text or html. - * @return {String} Selected contents in for example HTML format. - * @example - * // Alerts the currently selected contents - * alert(tinymce.activeEditor.selection.getContent()); - * - * // Alerts the currently selected contents as plain text - * alert(tinymce.activeEditor.selection.getContent({format: 'text'})); - */ - getContent: function(args) { - var self = this, - rng = self.getRng(), - tmpElm = self.dom.create("body"); - var se = self.getSel(), - whiteSpaceBefore, whiteSpaceAfter, fragment; - - args = args || {}; - whiteSpaceBefore = whiteSpaceAfter = ''; - args.get = true; - args.format = args.format || 'html'; - args.selection = true; - self.editor.fire('BeforeGetContent', args); - - if (args.format === 'text') { - return self.isCollapsed() ? '' : Zwsp.trim(rng.text || (se.toString ? se.toString() : '')); - } - - if (rng.cloneContents) { - fragment = args.contextual ? FragmentReader.read(self.editor.getBody(), rng).dom() : rng.cloneContents(); - if (fragment) { - tmpElm.appendChild(fragment); - } - } else if (rng.item !== undefined || rng.htmlText !== undefined) { - // IE will produce invalid markup if elements are present that - // it doesn't understand like custom elements or HTML5 elements. - // Adding a BR in front of the contents and then remoiving it seems to fix it though. - tmpElm.innerHTML = '<br>' + (rng.item ? rng.item(0).outerHTML : rng.htmlText); - tmpElm.removeChild(tmpElm.firstChild); - } else { - tmpElm.innerHTML = rng.toString(); - } - - // Keep whitespace before and after - if (/^\s/.test(tmpElm.innerHTML)) { - whiteSpaceBefore = ' '; - } - - if (/\s+$/.test(tmpElm.innerHTML)) { - whiteSpaceAfter = ' '; - } - - args.getInner = true; - - args.content = self.isCollapsed() ? '' : whiteSpaceBefore + self.serializer.serialize(tmpElm, args) + whiteSpaceAfter; - self.editor.fire('GetContent', args); - - return args.content; - }, - - /** - * Sets the current selection to the specified content. If any contents is selected it will be replaced - * with the contents passed in to this function. If there is no selection the contents will be inserted - * where the caret is placed in the editor/page. - * - * @method setContent - * @param {String} content HTML contents to set could also be other formats depending on settings. - * @param {Object} args Optional settings object with for example data format. - * @example - * // Inserts some HTML contents at the current selection - * tinymce.activeEditor.selection.setContent('<strong>Some contents</strong>'); - */ - setContent: function(content, args) { - var self = this, - rng = self.getRng(), - caretNode, doc = self.win.document, - frag, temp; - - args = args || { - format: 'html' - }; - args.set = true; - args.selection = true; - args.content = content; - - // Dispatch before set content event - if (!args.no_events) { - self.editor.fire('BeforeSetContent', args); - } - - content = args.content; - - if (rng.insertNode) { - // Make caret marker since insertNode places the caret in the beginning of text after insert - content += '<span id="__caret">_</span>'; - - // Delete and insert new node - if (rng.startContainer == doc && rng.endContainer == doc) { - // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents - doc.body.innerHTML = content; - } else { - rng.deleteContents(); - - if (doc.body.childNodes.length === 0) { - doc.body.innerHTML = content; - } else { - // createContextualFragment doesn't exists in IE 9 DOMRanges - if (rng.createContextualFragment) { - rng.insertNode(rng.createContextualFragment(content)); - } else { - // Fake createContextualFragment call in IE 9 - frag = doc.createDocumentFragment(); - temp = doc.createElement('div'); - - frag.appendChild(temp); - temp.outerHTML = content; - - rng.insertNode(frag); - } - } - } - - // Move to caret marker - caretNode = self.dom.get('__caret'); - - // Make sure we wrap it compleatly, Opera fails with a simple select call - rng = doc.createRange(); - rng.setStartBefore(caretNode); - rng.setEndBefore(caretNode); - self.setRng(rng); - - // Remove the caret position - self.dom.remove('__caret'); - - try { - self.setRng(rng); - } catch (ex) { - // Might fail on Opera for some odd reason - } - } else { - if (rng.item) { - // Delete content and get caret text selection - doc.execCommand('Delete', false, null); - rng = self.getRng(); - } - - // Explorer removes spaces from the beginning of pasted contents - if (/^\s+/.test(content)) { - rng.pasteHTML('<span id="__mce_tmp">_</span>' + content); - self.dom.remove('__mce_tmp'); - } else { - rng.pasteHTML(content); - } - } - - // Dispatch set content event - if (!args.no_events) { - self.editor.fire('SetContent', args); - } - }, - - /** - * Returns the start element of a selection range. If the start is in a text - * node the parent element will be returned. - * - * @method getStart - * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element. - * @return {Element} Start element of selection range. - */ - getStart: function(real) { - var self = this, - rng = self.getRng(), - startElement, parentElement, checkRng, node; - - if (rng.duplicate || rng.item) { - // Control selection, return first item - if (rng.item) { - return rng.item(0); - } - - // Get start element - checkRng = rng.duplicate(); - checkRng.collapse(1); - startElement = checkRng.parentElement(); - if (startElement.ownerDocument !== self.dom.doc) { - startElement = self.dom.getRoot(); - } - - // Check if range parent is inside the start element, then return the inner parent element - // This will fix issues when a single element is selected, IE would otherwise return the wrong start element - parentElement = node = rng.parentElement(); - while ((node = node.parentNode)) { - if (node == startElement) { - startElement = parentElement; - break; - } - } - - return startElement; - } - - startElement = rng.startContainer; - - if (startElement.nodeType == 1 && startElement.hasChildNodes()) { - if (!real || !rng.collapsed) { - startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)]; - } - } - - if (startElement && startElement.nodeType == 3) { - return startElement.parentNode; - } - - return startElement; - }, - - /** - * Returns the end element of a selection range. If the end is in a text - * node the parent element will be returned. - * - * @method getEnd - * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element. - * @return {Element} End element of selection range. - */ - getEnd: function(real) { - var self = this, - rng = self.getRng(), - endElement, endOffset; - - if (rng.duplicate || rng.item) { - if (rng.item) { - return rng.item(0); - } - - rng = rng.duplicate(); - rng.collapse(0); - endElement = rng.parentElement(); - if (endElement.ownerDocument !== self.dom.doc) { - endElement = self.dom.getRoot(); - } - - if (endElement && endElement.nodeName == 'BODY') { - return endElement.lastChild || endElement; - } - - return endElement; - } - - endElement = rng.endContainer; - endOffset = rng.endOffset; - - if (endElement.nodeType == 1 && endElement.hasChildNodes()) { - if (!real || !rng.collapsed) { - endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset]; - } - } - - if (endElement && endElement.nodeType == 3) { - return endElement.parentNode; - } - - return endElement; - }, - - /** - * Returns a bookmark location for the current selection. This bookmark object - * can then be used to restore the selection after some content modification to the document. - * - * @method getBookmark - * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex. - * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization. - * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection. - * @example - * // Stores a bookmark of the current selection - * var bm = tinymce.activeEditor.selection.getBookmark(); - * - * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content'); - * - * // Restore the selection bookmark - * tinymce.activeEditor.selection.moveToBookmark(bm); - */ - getBookmark: function(type, normalized) { - return this.bookmarkManager.getBookmark(type, normalized); - }, - - /** - * Restores the selection to the specified bookmark. - * - * @method moveToBookmark - * @param {Object} bookmark Bookmark to restore selection from. - * @return {Boolean} true/false if it was successful or not. - * @example - * // Stores a bookmark of the current selection - * var bm = tinymce.activeEditor.selection.getBookmark(); - * - * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content'); - * - * // Restore the selection bookmark - * tinymce.activeEditor.selection.moveToBookmark(bm); - */ - moveToBookmark: function(bookmark) { - return this.bookmarkManager.moveToBookmark(bookmark); - }, - - /** - * Selects the specified element. This will place the start and end of the selection range around the element. - * - * @method select - * @param {Element} node HTML DOM element to select. - * @param {Boolean} content Optional bool state if the contents should be selected or not on non IE browser. - * @return {Element} Selected element the same element as the one that got passed in. - * @example - * // Select the first paragraph in the active editor - * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]); - */ - select: function(node, content) { - var self = this, - dom = self.dom, - rng = dom.createRng(), - idx; - - // Clear stored range set by FocusManager - self.lastFocusBookmark = null; - - if (node) { - if (!content && self.controlSelection.controlSelect(node)) { - return; - } - - idx = dom.nodeIndex(node); - rng.setStart(node.parentNode, idx); - rng.setEnd(node.parentNode, idx + 1); - - // Find first/last text node or BR element - if (content) { - self._moveEndPoint(rng, node, true); - self._moveEndPoint(rng, node); - } - - self.setRng(rng); - } - - return node; - }, - - /** - * Returns true/false if the selection range is collapsed or not. Collapsed means if it's a caret or a larger selection. - * - * @method isCollapsed - * @return {Boolean} true/false state if the selection range is collapsed or not. - * Collapsed means if it's a caret or a larger selection. - */ - isCollapsed: function() { - var self = this, - rng = self.getRng(), - sel = self.getSel(); - - if (!rng || rng.item) { - return false; - } - - if (rng.compareEndPoints) { - return rng.compareEndPoints('StartToEnd', rng) === 0; - } - - return !sel || rng.collapsed; - }, - - /** - * Collapse the selection to start or end of range. - * - * @method collapse - * @param {Boolean} toStart Optional boolean state if to collapse to end or not. Defaults to false. - */ - collapse: function(toStart) { - var self = this, - rng = self.getRng(), - node; - - // Control range on IE - if (rng.item) { - node = rng.item(0); - rng = self.win.document.body.createTextRange(); - rng.moveToElementText(node); - } - - rng.collapse(!!toStart); - self.setRng(rng); - }, - - /** - * Returns the browsers internal selection object. - * - * @method getSel - * @return {Selection} Internal browser selection object. - */ - getSel: function() { - var win = this.win; - - return win.getSelection ? win.getSelection() : win.document.selection; - }, - - /** - * Returns the browsers internal range object. - * - * @method getRng - * @param {Boolean} w3c Forces a compatible W3C range on IE. - * @return {Range} Internal browser range object. - * @see http://www.quirksmode.org/dom/range_intro.html - * @see http://www.dotvoid.com/2001/03/using-the-range-object-in-mozilla/ - */ - getRng: function(w3c) { - var self = this, - selection, rng, elm, doc, ieRng, evt; - - function tryCompareBoundaryPoints(how, sourceRange, destinationRange) { - try { - return sourceRange.compareBoundaryPoints(how, destinationRange); - } catch (ex) { - // Gecko throws wrong document exception if the range points - // to nodes that where removed from the dom #6690 - // Browsers should mutate existing DOMRange instances so that they always point - // to something in the document this is not the case in Gecko works fine in IE/WebKit/Blink - // For performance reasons just return -1 - return -1; - } - } - - if (!self.win) { - return null; - } - - doc = self.win.document; - - if (typeof doc === 'undefined' || doc === null) { - return null; - } - - // Use last rng passed from FocusManager if it's available this enables - // calls to editor.selection.getStart() to work when caret focus is lost on IE - if (!w3c && self.lastFocusBookmark) { - var bookmark = self.lastFocusBookmark; - - // Convert bookmark to range IE 11 fix - if (bookmark.startContainer) { - rng = doc.createRange(); - rng.setStart(bookmark.startContainer, bookmark.startOffset); - rng.setEnd(bookmark.endContainer, bookmark.endOffset); - } else { - rng = bookmark; - } - - return rng; - } - - // Found tridentSel object then we need to use that one - if (w3c && self.tridentSel) { - return self.tridentSel.getRangeAt(0); - } - - try { - if ((selection = self.getSel())) { - if (selection.rangeCount > 0) { - rng = selection.getRangeAt(0); - } else { - rng = selection.createRange ? selection.createRange() : doc.createRange(); - } - } - } catch (ex) { - // IE throws unspecified error here if TinyMCE is placed in a frame/iframe - } - - evt = self.editor.fire('GetSelectionRange', { - range: rng - }); - if (evt.range !== rng) { - return evt.range; - } - - // We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet - // IE 11 doesn't support the selection object so we check for that as well - if (isIE && rng && rng.setStart && doc.selection) { - try { - // IE will sometimes throw an exception here - ieRng = doc.selection.createRange(); - } catch (ex) { - // Ignore - } - - if (ieRng && ieRng.item) { - elm = ieRng.item(0); - rng = doc.createRange(); - rng.setStartBefore(elm); - rng.setEndAfter(elm); - } - } - - // No range found then create an empty one - // This can occur when the editor is placed in a hidden container element on Gecko - // Or on IE when there was an exception - if (!rng) { - rng = doc.createRange ? doc.createRange() : doc.body.createTextRange(); - } - - // If range is at start of document then move it to start of body - if (rng.setStart && rng.startContainer.nodeType === 9 && rng.collapsed) { - elm = self.dom.getRoot(); - rng.setStart(elm, 0); - rng.setEnd(elm, 0); - } - - if (self.selectedRange && self.explicitRange) { - if (tryCompareBoundaryPoints(rng.START_TO_START, rng, self.selectedRange) === 0 && - tryCompareBoundaryPoints(rng.END_TO_END, rng, self.selectedRange) === 0) { - // Safari, Opera and Chrome only ever select text which causes the range to change. - // This lets us use the originally set range if the selection hasn't been changed by the user. - rng = self.explicitRange; - } else { - self.selectedRange = null; - self.explicitRange = null; - } - } - - return rng; - }, - - /** - * Changes the selection to the specified DOM range. - * - * @method setRng - * @param {Range} rng Range to select. - * @param {Boolean} forward Optional boolean if the selection is forwards or backwards. - */ - setRng: function(rng, forward) { - var self = this, - sel, node, evt; - - if (!isValidRange(rng)) { - return; - } - - // Is IE specific range - if (rng.select) { - self.explicitRange = null; - - try { - rng.select(); - } catch (ex) { - // Needed for some odd IE bug #1843306 - } - - return; - } - - if (!self.tridentSel) { - sel = self.getSel(); - - evt = self.editor.fire('SetSelectionRange', { - range: rng, - forward: forward - }); - rng = evt.range; - - if (sel) { - self.explicitRange = rng; - - try { - sel.removeAllRanges(); - sel.addRange(rng); - } catch (ex) { - // IE might throw errors here if the editor is within a hidden container and selection is changed - } - - // Forward is set to false and we have an extend function - if (forward === false && sel.extend) { - sel.collapse(rng.endContainer, rng.endOffset); - sel.extend(rng.startContainer, rng.startOffset); - } - - // adding range isn't always successful so we need to check range count otherwise an exception can occur - self.selectedRange = sel.rangeCount > 0 ? sel.getRangeAt(0) : null; - } - - // WebKit egde case selecting images works better using setBaseAndExtent when the image is floated - if (!rng.collapsed && rng.startContainer === rng.endContainer && sel.setBaseAndExtent && !Env.ie) { - if (rng.endOffset - rng.startOffset < 2) { - if (rng.startContainer.hasChildNodes()) { - node = rng.startContainer.childNodes[rng.startOffset]; - if (node && node.tagName === 'IMG') { - sel.setBaseAndExtent( - rng.startContainer, - rng.startOffset, - rng.endContainer, - rng.endOffset - ); - - // Since the setBaseAndExtent is fixed in more recent Blink versions we - // need to detect if it's doing the wrong thing and falling back to the - // crazy incorrect behavior api call since that seems to be the only way - // to get it to work on Safari WebKit as of 2017-02-23 - if (sel.anchorNode !== rng.startContainer || sel.focusNode !== rng.endContainer) { - sel.setBaseAndExtent(node, 0, node, 1); - } - } - } - } - } - - self.editor.fire('AfterSetSelectionRange', { - range: rng, - forward: forward - }); - } else { - // Is W3C Range fake range on IE - if (rng.cloneRange) { - try { - self.tridentSel.addRange(rng); - } catch (ex) { - //IE9 throws an error here if called before selection is placed in the editor - } - } - } - }, - - /** - * Sets the current selection to the specified DOM element. - * - * @method setNode - * @param {Element} elm Element to set as the contents of the selection. - * @return {Element} Returns the element that got passed in. - * @example - * // Inserts a DOM node at current selection/caret location - * tinymce.activeEditor.selection.setNode(tinymce.activeEditor.dom.create('img', {src: 'some.gif', title: 'some title'})); - */ - setNode: function(elm) { - var self = this; - - self.setContent(self.dom.getOuterHTML(elm)); - - return elm; - }, - - /** - * Returns the currently selected element or the common ancestor element for both start and end of the selection. - * - * @method getNode - * @return {Element} Currently selected element or common ancestor element. - * @example - * // Alerts the currently selected elements node name - * alert(tinymce.activeEditor.selection.getNode().nodeName); - */ - getNode: function() { - var self = this, - rng = self.getRng(), - elm; - var startContainer, endContainer, startOffset, endOffset, root = self.dom.getRoot(); - - function skipEmptyTextNodes(node, forwards) { - var orig = node; - - while (node && node.nodeType === 3 && node.length === 0) { - node = forwards ? node.nextSibling : node.previousSibling; - } - - return node || orig; - } - - // Range maybe lost after the editor is made visible again - if (!rng) { - return root; - } - - startContainer = rng.startContainer; - endContainer = rng.endContainer; - startOffset = rng.startOffset; - endOffset = rng.endOffset; - - if (rng.setStart) { - elm = rng.commonAncestorContainer; - - // Handle selection a image or other control like element such as anchors - if (!rng.collapsed) { - if (startContainer == endContainer) { - if (endOffset - startOffset < 2) { - if (startContainer.hasChildNodes()) { - elm = startContainer.childNodes[startOffset]; - } - } - } - - // If the anchor node is a element instead of a text node then return this element - //if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1) - // return sel.anchorNode.childNodes[sel.anchorOffset]; - - // Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent. - // This happens when you double click an underlined word in FireFox. - if (startContainer.nodeType === 3 && endContainer.nodeType === 3) { - if (startContainer.length === startOffset) { - startContainer = skipEmptyTextNodes(startContainer.nextSibling, true); - } else { - startContainer = startContainer.parentNode; - } - - if (endOffset === 0) { - endContainer = skipEmptyTextNodes(endContainer.previousSibling, false); - } else { - endContainer = endContainer.parentNode; - } - - if (startContainer && startContainer === endContainer) { - return startContainer; - } - } - } - - if (elm && elm.nodeType == 3) { - return elm.parentNode; - } - - return elm; - } - - elm = rng.item ? rng.item(0) : rng.parentElement(); - - // IE 7 might return elements outside the iframe - if (elm.ownerDocument !== self.win.document) { - elm = root; - } - - return elm; - }, - - getSelectedBlocks: function(startElm, endElm) { - var self = this, - dom = self.dom, - node, root, selectedBlocks = []; - - root = dom.getRoot(); - startElm = dom.getParent(startElm || self.getStart(), dom.isBlock); - endElm = dom.getParent(endElm || self.getEnd(), dom.isBlock); - - if (startElm && startElm != root) { - selectedBlocks.push(startElm); - } - - if (startElm && endElm && startElm != endElm) { - node = startElm; - - var walker = new TreeWalker(startElm, root); - while ((node = walker.next()) && node != endElm) { - if (dom.isBlock(node)) { - selectedBlocks.push(node); - } - } - } - - if (endElm && startElm != endElm && endElm != root) { - selectedBlocks.push(endElm); - } - - return selectedBlocks; - }, - - isForward: function() { - var dom = this.dom, - sel = this.getSel(), - anchorRange, focusRange; - - // No support for selection direction then always return true - if (!sel || !sel.anchorNode || !sel.focusNode) { - return true; - } - - anchorRange = dom.createRng(); - anchorRange.setStart(sel.anchorNode, sel.anchorOffset); - anchorRange.collapse(true); - - focusRange = dom.createRng(); - focusRange.setStart(sel.focusNode, sel.focusOffset); - focusRange.collapse(true); - - return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0; - }, - - normalize: function() { - var self = this, - rng = self.getRng(); - - if (Env.range && new RangeUtils(self.dom).normalize(rng)) { - self.setRng(rng, self.isForward()); - } - - return rng; - }, - - /** - * Executes callback when the current selection starts/stops matching the specified selector. The current - * state will be passed to the callback as it's first argument. - * - * @method selectorChanged - * @param {String} selector CSS selector to check for. - * @param {function} callback Callback with state and args when the selector is matches or not. - */ - selectorChanged: function(selector, callback) { - var self = this, - currentSelectors; - - if (!self.selectorChangedData) { - self.selectorChangedData = {}; - currentSelectors = {}; - - self.editor.on('NodeChange', function(e) { - var node = e.element, - dom = self.dom, - parents = dom.getParents(node, null, dom.getRoot()), - matchedSelectors = {}; - - // Check for new matching selectors - each(self.selectorChangedData, function(callbacks, selector) { - each(parents, function(node) { - if (dom.is(node, selector)) { - if (!currentSelectors[selector]) { - // Execute callbacks - each(callbacks, function(callback) { - callback(true, { - node: node, - selector: selector, - parents: parents - }); - }); - - currentSelectors[selector] = callbacks; - } - - matchedSelectors[selector] = callbacks; - return false; - } - }); - }); - - // Check if current selectors still match - each(currentSelectors, function(callbacks, selector) { - if (!matchedSelectors[selector]) { - delete currentSelectors[selector]; - - each(callbacks, function(callback) { - callback(false, { - node: node, - selector: selector, - parents: parents - }); - }); - } - }); - }); - } - - // Add selector listeners - if (!self.selectorChangedData[selector]) { - self.selectorChangedData[selector] = []; - } - - self.selectorChangedData[selector].push(callback); - - return self; - }, - - getScrollContainer: function() { - var scrollContainer, node = this.dom.getRoot(); - - while (node && node.nodeName != 'BODY') { - if (node.scrollHeight > node.clientHeight) { - scrollContainer = node; - break; - } - - node = node.parentNode; - } - - return scrollContainer; - }, - - scrollIntoView: function(elm, alignToTop) { - ScrollIntoView.scrollIntoView(this.editor, elm, alignToTop); - }, - - placeCaretAt: function(clientX, clientY) { - this.setRng(RangeUtils.getCaretRangeFromPoint(clientX, clientY, this.editor.getDoc())); - }, - - _moveEndPoint: function(rng, node, start) { - var root = node, - walker = new TreeWalker(node, root); - var nonEmptyElementsMap = this.dom.schema.getNonEmptyElements(); - - do { - // Text node - if (node.nodeType == 3 && trim(node.nodeValue).length !== 0) { - if (start) { - rng.setStart(node, 0); - } else { - rng.setEnd(node, node.nodeValue.length); - } - - return; - } - - // BR/IMG/INPUT elements but not table cells - if (nonEmptyElementsMap[node.nodeName] && !/^(TD|TH)$/.test(node.nodeName)) { - if (start) { - rng.setStartBefore(node); - } else { - if (node.nodeName == 'BR') { - rng.setEndBefore(node); - } else { - rng.setEndAfter(node); - } - } - - return; - } - - // Found empty text block old IE can place the selection inside those - if (Env.ie && Env.ie < 11 && this.dom.isBlock(node) && this.dom.isEmpty(node)) { - if (start) { - rng.setStart(node, 0); - } else { - rng.setEnd(node, 0); - } - - return; - } - } while ((node = (start ? walker.next() : walker.prev()))); - - // Failed to find any text node or other suitable location then move to the root of body - if (root.nodeName == 'BODY') { - if (start) { - rng.setStart(root, 0); - } else { - rng.setEnd(root, root.childNodes.length); - } - } - }, - - getBoundingClientRect: function() { - var rng = this.getRng(); - return rng.collapsed ? CaretPosition.fromRangeStart(rng).getClientRects()[0] : rng.getBoundingClientRect(); - }, - - destroy: function() { - this.win = null; - this.controlSelection.destroy(); - } - }; - - return Selection; - } - ); - - /** - * ElementUtils.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Utility class for various element specific functions. - * - * @private - * @class tinymce.dom.ElementUtils - */ - define( - 'tinymce.core.dom.ElementUtils', [ - "tinymce.core.dom.BookmarkManager", - "tinymce.core.util.Tools" - ], - function(BookmarkManager, Tools) { - var each = Tools.each; - - function ElementUtils(dom) { - /** - * Compares two nodes and checks if it's attributes and styles matches. - * This doesn't compare classes as items since their order is significant. - * - * @method compare - * @param {Node} node1 First node to compare with. - * @param {Node} node2 Second node to compare with. - * @return {boolean} True/false if the nodes are the same or not. - */ - this.compare = function(node1, node2) { - // Not the same name - if (node1.nodeName != node2.nodeName) { - return false; - } - - /** - * Returns all the nodes attributes excluding internal ones, styles and classes. - * - * @private - * @param {Node} node Node to get attributes from. - * @return {Object} Name/value object with attributes and attribute values. - */ - function getAttribs(node) { - var attribs = {}; - - each(dom.getAttribs(node), function(attr) { - var name = attr.nodeName.toLowerCase(); - - // Don't compare internal attributes or style - if (name.indexOf('_') !== 0 && name !== 'style' && name.indexOf('data-') !== 0) { - attribs[name] = dom.getAttrib(node, name); - } - }); - - return attribs; - } - - /** - * Compares two objects checks if it's key + value exists in the other one. - * - * @private - * @param {Object} obj1 First object to compare. - * @param {Object} obj2 Second object to compare. - * @return {boolean} True/false if the objects matches or not. - */ - function compareObjects(obj1, obj2) { - var value, name; - - for (name in obj1) { - // Obj1 has item obj2 doesn't have - if (obj1.hasOwnProperty(name)) { - value = obj2[name]; - - // Obj2 doesn't have obj1 item - if (typeof value == "undefined") { - return false; - } - - // Obj2 item has a different value - if (obj1[name] != value) { - return false; - } - - // Delete similar value - delete obj2[name]; - } - } - - // Check if obj 2 has something obj 1 doesn't have - for (name in obj2) { - // Obj2 has item obj1 doesn't have - if (obj2.hasOwnProperty(name)) { - return false; - } - } - - return true; - } - - // Attribs are not the same - if (!compareObjects(getAttribs(node1), getAttribs(node2))) { - return false; - } - - // Styles are not the same - if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) { - return false; - } - - return !BookmarkManager.isBookmarkNode(node1) && !BookmarkManager.isBookmarkNode(node2); - }; - } - - return ElementUtils; - } - ); - - /** - * Preview.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Internal class for generating previews styles for formats. - * - * Example: - * Preview.getCssText(editor, 'bold'); - * - * @private - * @class tinymce.fmt.Preview - */ - define( - 'tinymce.core.fmt.Preview', [ - "tinymce.core.dom.DOMUtils", - "tinymce.core.util.Tools", - "tinymce.core.html.Schema" - ], - function(DOMUtils, Tools, Schema) { - var each = Tools.each; - var dom = DOMUtils.DOM; - - function parsedSelectorToHtml(ancestry, editor) { - var elm, item, fragment; - var schema = editor && editor.schema || new Schema({}); - - function decorate(elm, item) { - if (item.classes.length) { - dom.addClass(elm, item.classes.join(' ')); - } - dom.setAttribs(elm, item.attrs); - } - - function createElement(sItem) { - var elm; - - item = typeof sItem === 'string' ? { - name: sItem, - classes: [], - attrs: {} - } : sItem; - - elm = dom.create(item.name); - decorate(elm, item); - return elm; - } - - function getRequiredParent(elm, candidate) { - var name = typeof elm !== 'string' ? elm.nodeName.toLowerCase() : elm; - var elmRule = schema.getElementRule(name); - var parentsRequired = elmRule && elmRule.parentsRequired; - - if (parentsRequired && parentsRequired.length) { - return candidate && Tools.inArray(parentsRequired, candidate) !== -1 ? candidate : parentsRequired[0]; - } else { - return false; - } - } - - function wrapInHtml(elm, ancestry, siblings) { - var parent, parentCandidate, parentRequired; - var ancestor = ancestry.length > 0 && ancestry[0]; - var ancestorName = ancestor && ancestor.name; - - parentRequired = getRequiredParent(elm, ancestorName); - - if (parentRequired) { - if (ancestorName == parentRequired) { - parentCandidate = ancestry[0]; - ancestry = ancestry.slice(1); - } else { - parentCandidate = parentRequired; - } - } else if (ancestor) { - parentCandidate = ancestry[0]; - ancestry = ancestry.slice(1); - } else if (!siblings) { - return elm; - } - - if (parentCandidate) { - parent = createElement(parentCandidate); - parent.appendChild(elm); - } - - if (siblings) { - if (!parent) { - // if no more ancestry, wrap in generic div - parent = dom.create('div'); - parent.appendChild(elm); - } - - Tools.each(siblings, function(sibling) { - var siblingElm = createElement(sibling); - parent.insertBefore(siblingElm, elm); - }); - } - - return wrapInHtml(parent, ancestry, parentCandidate && parentCandidate.siblings); - } - - if (ancestry && ancestry.length) { - item = ancestry[0]; - elm = createElement(item); - fragment = dom.create('div'); - fragment.appendChild(wrapInHtml(elm, ancestry.slice(1), item.siblings)); - return fragment; - } else { - return ''; - } - } - - - function selectorToHtml(selector, editor) { - return parsedSelectorToHtml(parseSelector(selector), editor); - } - - - function parseSelectorItem(item) { - var tagName; - var obj = { - classes: [], - attrs: {} - }; - - item = obj.selector = Tools.trim(item); - - if (item !== '*') { - // matching IDs, CLASSes, ATTRIBUTES and PSEUDOs - tagName = item.replace(/(?:([#\.]|::?)([\w\-]+)|(\[)([^\]]+)\]?)/g, function($0, $1, $2, $3, $4) { - switch ($1) { - case '#': - obj.attrs.id = $2; - break; - - case '.': - obj.classes.push($2); - break; - - case ':': - if (Tools.inArray('checked disabled enabled read-only required'.split(' '), $2) !== -1) { - obj.attrs[$2] = $2; - } - break; - } - - // atribute matched - if ($3 == '[') { - var m = $4.match(/([\w\-]+)(?:\=\"([^\"]+))?/); - if (m) { - obj.attrs[m[1]] = m[2]; - } - } - - return ''; - }); - } - - obj.name = tagName || 'div'; - return obj; - } - - - function parseSelector(selector) { - if (!selector || typeof selector !== 'string') { - return []; - } - - // take into account only first one - selector = selector.split(/\s*,\s*/)[0]; - - // tighten - selector = selector.replace(/\s*(~\+|~|\+|>)\s*/g, '$1'); - - // split either on > or on space, but not the one inside brackets - return Tools.map(selector.split(/(?:>|\s+(?![^\[\]]+\]))/), function(item) { - // process each sibling selector separately - var siblings = Tools.map(item.split(/(?:~\+|~|\+)/), parseSelectorItem); - var obj = siblings.pop(); // the last one is our real target - - if (siblings.length) { - obj.siblings = siblings; - } - return obj; - }).reverse(); - } - - - function getCssText(editor, format) { - var name, previewFrag, previewElm, items; - var previewCss = '', - parentFontSize, previewStyles; - - previewStyles = editor.settings.preview_styles; - - // No preview forced - if (previewStyles === false) { - return ''; - } - - // Default preview - if (typeof previewStyles !== 'string') { - previewStyles = 'font-family font-size font-weight font-style text-decoration ' + - 'text-transform color background-color border border-radius outline text-shadow'; - } - - // Removes any variables since these can't be previewed - function removeVars(val) { - return val.replace(/%(\w+)/g, ''); - } - - // Create block/inline element to use for preview - if (typeof format == "string") { - format = editor.formatter.get(format); - if (!format) { - return; - } - - format = format[0]; - } - - // Format specific preview override - // TODO: This should probably be further reduced by the previewStyles option - if ('preview' in format) { - previewStyles = format.preview; - if (previewStyles === false) { - return ''; - } - } - - name = format.block || format.inline || 'span'; - - items = parseSelector(format.selector); - if (items.length) { - if (!items[0].name) { // e.g. something like ul > .someClass was provided - items[0].name = name; - } - name = format.selector; - previewFrag = parsedSelectorToHtml(items, editor); - } else { - previewFrag = parsedSelectorToHtml([name], editor); - } - - previewElm = dom.select(name, previewFrag)[0] || previewFrag.firstChild; - - // Add format styles to preview element - each(format.styles, function(value, name) { - value = removeVars(value); - - if (value) { - dom.setStyle(previewElm, name, value); - } - }); - - // Add attributes to preview element - each(format.attributes, function(value, name) { - value = removeVars(value); - - if (value) { - dom.setAttrib(previewElm, name, value); - } - }); - - // Add classes to preview element - each(format.classes, function(value) { - value = removeVars(value); - - if (!dom.hasClass(previewElm, value)) { - dom.addClass(previewElm, value); - } - }); - - editor.fire('PreviewFormats'); - - // Add the previewElm outside the visual area - dom.setStyles(previewFrag, { - position: 'absolute', - left: -0xFFFF - }); - editor.getBody().appendChild(previewFrag); - - // Get parent container font size so we can compute px values out of em/% for older IE:s - parentFontSize = dom.getStyle(editor.getBody(), 'fontSize', true); - parentFontSize = /px$/.test(parentFontSize) ? parseInt(parentFontSize, 10) : 0; - - each(previewStyles.split(' '), function(name) { - var value = dom.getStyle(previewElm, name, true); - - // If background is transparent then check if the body has a background color we can use - if (name == 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) { - value = dom.getStyle(editor.getBody(), name, true); - - // Ignore white since it's the default color, not the nicest fix - // TODO: Fix this by detecting runtime style - if (dom.toHex(value).toLowerCase() == '#ffffff') { - return; - } - } - - if (name == 'color') { - // Ignore black since it's the default color, not the nicest fix - // TODO: Fix this by detecting runtime style - if (dom.toHex(value).toLowerCase() == '#000000') { - return; - } - } - - // Old IE won't calculate the font size so we need to do that manually - if (name == 'font-size') { - if (/em|%$/.test(value)) { - if (parentFontSize === 0) { - return; - } - - // Convert font size from em/% to px - value = parseFloat(value, 10) / (/%$/.test(value) ? 100 : 1); - value = (value * parentFontSize) + 'px'; - } - } - - if (name == "border" && value) { - previewCss += 'padding:0 2px;'; - } - - previewCss += name + ':' + value + ';'; - }); - - editor.fire('AfterPreviewFormats'); - - //previewCss += 'line-height:normal'; - - dom.remove(previewFrag); - - return previewCss; - } - - return { - getCssText: getCssText, - parseSelector: parseSelector, - selectorToHtml: selectorToHtml - }; - } - ); - - /** - * Hooks.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Internal class for overriding formatting. - * - * @private - * @class tinymce.fmt.Hooks - */ - define( - 'tinymce.core.fmt.Hooks', [ - "tinymce.core.util.Arr", - "tinymce.core.dom.NodeType", - "tinymce.core.dom.DomQuery" - ], - function(Arr, NodeType, $) { - var postProcessHooks = {}, - filter = Arr.filter, - each = Arr.each; - - function addPostProcessHook(name, hook) { - var hooks = postProcessHooks[name]; - - if (!hooks) { - postProcessHooks[name] = hooks = []; - } - - postProcessHooks[name].push(hook); - } - - function postProcess(name, editor) { - each(postProcessHooks[name], function(hook) { - hook(editor); - }); - } - - addPostProcessHook("pre", function(editor) { - var rng = editor.selection.getRng(), - isPre, blocks; - - function hasPreSibling(pre) { - return isPre(pre.previousSibling) && Arr.indexOf(blocks, pre.previousSibling) != -1; - } - - function joinPre(pre1, pre2) { - $(pre2).remove(); - $(pre1).append('<br><br>').append(pre2.childNodes); - } - - isPre = NodeType.matchNodeNames('pre'); - - if (!rng.collapsed) { - blocks = editor.selection.getSelectedBlocks(); - - each(filter(filter(blocks, isPre), hasPreSibling), function(pre) { - joinPre(pre.previousSibling, pre); - }); - } - }); - - return { - postProcess: postProcess - }; - } - ); - - /** - * Formatter.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Text formatter engine class. This class is used to apply formats like bold, italic, font size - * etc to the current selection or specific nodes. This engine was built to replace the browser's - * default formatting logic for execCommand due to its inconsistent and buggy behavior. - * - * @class tinymce.Formatter - * @example - * tinymce.activeEditor.formatter.register('mycustomformat', { - * inline: 'span', - * styles: {color: '#ff0000'} - * }); - * - * tinymce.activeEditor.formatter.apply('mycustomformat'); - */ - define( - 'tinymce.core.Formatter', [ - "tinymce.core.dom.TreeWalker", - "tinymce.core.dom.RangeUtils", - "tinymce.core.dom.BookmarkManager", - "tinymce.core.dom.ElementUtils", - "tinymce.core.dom.NodeType", - "tinymce.core.util.Fun", - "tinymce.core.util.Tools", - "tinymce.core.fmt.Preview", - "tinymce.core.fmt.Hooks" - ], - function(TreeWalker, RangeUtils, BookmarkManager, ElementUtils, NodeType, Fun, Tools, Preview, Hooks) { - /** - * Constructs a new formatter instance. - * - * @constructor Formatter - * @param {tinymce.Editor} ed Editor instance to construct the formatter engine to. - */ - return function(ed) { - var formats = {}, - dom = ed.dom, - selection = ed.selection, - rangeUtils = new RangeUtils(dom), - isValid = ed.schema.isValidChild, - isBlock = dom.isBlock, - forcedRootBlock = ed.settings.forced_root_block, - nodeIndex = dom.nodeIndex, - INVISIBLE_CHAR = '\uFEFF', - MCE_ATTR_RE = /^(src|href|style)$/, - FALSE = false, - TRUE = true, - formatChangeData, - undef, - getContentEditable = dom.getContentEditable, - disableCaretContainer, - markCaretContainersBogus, - isBookmarkNode = BookmarkManager.isBookmarkNode; - - var each = Tools.each, - grep = Tools.grep, - walk = Tools.walk, - extend = Tools.extend; - - function isTextBlock(name) { - if (name.nodeType) { - name = name.nodeName; - } - - return !!ed.schema.getTextBlockElements()[name.toLowerCase()]; - } - - function isTableCell(node) { - return /^(TH|TD)$/.test(node.nodeName); - } - - function isInlineBlock(node) { - return node && /^(IMG)$/.test(node.nodeName); - } - - function getParents(node, selector) { - return dom.getParents(node, selector, dom.getRoot()); - } - - function isCaretNode(node) { - return node.nodeType === 1 && node.id === '_mce_caret'; - } - - function defaultFormats() { - register({ - valigntop: [{ - selector: 'td,th', - styles: { - 'verticalAlign': 'top' - } - }], - - valignmiddle: [{ - selector: 'td,th', - styles: { - 'verticalAlign': 'middle' - } - }], - - valignbottom: [{ - selector: 'td,th', - styles: { - 'verticalAlign': 'bottom' - } - }], - - alignleft: [{ - selector: 'figure.image', - collapsed: false, - classes: 'align-left', - ceFalseOverride: true, - preview: 'font-family font-size' - }, - { - selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', - styles: { - textAlign: 'left' - }, - inherit: false, - preview: false, - defaultBlock: 'div' - }, - { - selector: 'img,table', - collapsed: false, - styles: { - 'float': 'left' - }, - preview: 'font-family font-size' - } - ], - - aligncenter: [{ - selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', - styles: { - textAlign: 'center' - }, - inherit: false, - preview: false, - defaultBlock: 'div' - }, - { - selector: 'figure.image', - collapsed: false, - classes: 'align-center', - ceFalseOverride: true, - preview: 'font-family font-size' - }, - { - selector: 'img', - collapsed: false, - styles: { - display: 'block', - marginLeft: 'auto', - marginRight: 'auto' - }, - preview: false - }, - { - selector: 'table', - collapsed: false, - styles: { - marginLeft: 'auto', - marginRight: 'auto' - }, - preview: 'font-family font-size' - } - ], - - alignright: [{ - selector: 'figure.image', - collapsed: false, - classes: 'align-right', - ceFalseOverride: true, - preview: 'font-family font-size' - }, - { - selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', - styles: { - textAlign: 'right' - }, - inherit: false, - preview: 'font-family font-size', - defaultBlock: 'div' - }, - { - selector: 'img,table', - collapsed: false, - styles: { - 'float': 'right' - }, - preview: 'font-family font-size' - } - ], - - alignjustify: [{ - selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', - styles: { - textAlign: 'justify' - }, - inherit: false, - defaultBlock: 'div', - preview: 'font-family font-size' - }], - - bold: [{ - inline: 'strong', - remove: 'all' - }, - { - inline: 'span', - styles: { - fontWeight: 'bold' - } - }, - { - inline: 'b', - remove: 'all' - } - ], - - italic: [{ - inline: 'em', - remove: 'all' - }, - { - inline: 'span', - styles: { - fontStyle: 'italic' - } - }, - { - inline: 'i', - remove: 'all' - } - ], - - underline: [{ - inline: 'span', - styles: { - textDecoration: 'underline' - }, - exact: true - }, - { - inline: 'u', - remove: 'all' - } - ], - - strikethrough: [{ - inline: 'span', - styles: { - textDecoration: 'line-through' - }, - exact: true - }, - { - inline: 'strike', - remove: 'all' - } - ], - - forecolor: { - inline: 'span', - styles: { - color: '%value' - }, - links: true, - remove_similar: true, - clear_child_styles: true - }, - hilitecolor: { - inline: 'span', - styles: { - backgroundColor: '%value' - }, - links: true, - remove_similar: true, - clear_child_styles: true - }, - fontname: { - inline: 'span', - styles: { - fontFamily: '%value' - }, - clear_child_styles: true - }, - fontsize: { - inline: 'span', - styles: { - fontSize: '%value' - }, - clear_child_styles: true - }, - fontsize_class: { - inline: 'span', - attributes: { - 'class': '%value' - } - }, - blockquote: { - block: 'blockquote', - wrapper: 1, - remove: 'all' - }, - subscript: { - inline: 'sub' - }, - superscript: { - inline: 'sup' - }, - code: { - inline: 'code' - }, - - link: { - inline: 'a', - selector: 'a', - remove: 'all', - split: true, - deep: true, - onmatch: function() { - return true; - }, - - onformat: function(elm, fmt, vars) { - each(vars, function(value, key) { - dom.setAttrib(elm, key, value); - }); - } - }, - - removeformat: [{ - selector: 'b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins', - remove: 'all', - split: true, - expand: false, - block_expand: true, - deep: true - }, - { - selector: 'span', - attributes: ['style', 'class'], - remove: 'empty', - split: true, - expand: false, - deep: true - }, - { - selector: '*', - attributes: ['style', 'class'], - split: false, - expand: false, - deep: true - } - ] - }); - - // Register default block formats - each('p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp'.split(/\s/), function(name) { - register(name, { - block: name, - remove: 'all' - }); - }); - - // Register user defined formats - register(ed.settings.formats); - } - - var clearChildStyles = function(format, node) { - if (format.clear_child_styles) { - each(dom.select('*', node), function(node) { - each(format.styles, function(value, name) { - dom.setStyle(node, name, ''); - }); - }); - } - }; - - function addKeyboardShortcuts() { - // Add some inline shortcuts - ed.addShortcut('meta+b', 'bold_desc', 'Bold'); - ed.addShortcut('meta+i', 'italic_desc', 'Italic'); - ed.addShortcut('meta+u', 'underline_desc', 'Underline'); - - // BlockFormat shortcuts keys - for (var i = 1; i <= 6; i++) { - ed.addShortcut('access+' + i, '', ['FormatBlock', false, 'h' + i]); - } - - ed.addShortcut('access+7', '', ['FormatBlock', false, 'p']); - ed.addShortcut('access+8', '', ['FormatBlock', false, 'div']); - ed.addShortcut('access+9', '', ['FormatBlock', false, 'address']); - } - - // Public functions - - /** - * Returns the format by name or all formats if no name is specified. - * - * @method get - * @param {String} name Optional name to retrieve by. - * @return {Array/Object} Array/Object with all registered formats or a specific format. - */ - function get(name) { - return name ? formats[name] : formats; - } - - /** - * Registers a specific format by name. - * - * @method register - * @param {Object/String} name Name of the format for example "bold". - * @param {Object/Array} format Optional format object or array of format variants - * can only be omitted if the first arg is an object. - */ - function register(name, format) { - if (name) { - if (typeof name !== 'string') { - each(name, function(format, name) { - register(name, format); - }); - } else { - // Force format into array and add it to internal collection - format = format.length ? format : [format]; - - each(format, function(format) { - // Set deep to false by default on selector formats this to avoid removing - // alignment on images inside paragraphs when alignment is changed on paragraphs - if (format.deep === undef) { - format.deep = !format.selector; - } - - // Default to true - if (format.split === undef) { - format.split = !format.selector || format.inline; - } - - // Default to true - if (format.remove === undef && format.selector && !format.inline) { - format.remove = 'none'; - } - - // Mark format as a mixed format inline + block level - if (format.selector && format.inline) { - format.mixed = true; - format.block_expand = true; - } - - // Split classes if needed - if (typeof format.classes === 'string') { - format.classes = format.classes.split(/\s+/); - } - }); - - formats[name] = format; - } - } - } - - /** - * Unregister a specific format by name. - * - * @method unregister - * @param {String} name Name of the format for example "bold". - */ - function unregister(name) { - if (name && formats[name]) { - delete formats[name]; - } - - return formats; - } - - function matchesUnInheritedFormatSelector(node, name) { - var formatList = get(name); - - if (formatList) { - for (var i = 0; i < formatList.length; i++) { - if (formatList[i].inherit === false && dom.is(node, formatList[i].selector)) { - return true; - } - } - } - - return false; - } - - function getTextDecoration(node) { - var decoration; - - ed.dom.getParent(node, function(n) { - decoration = ed.dom.getStyle(n, 'text-decoration'); - return decoration && decoration !== 'none'; - }); - - return decoration; - } - - function processUnderlineAndColor(node) { - var textDecoration; - if (node.nodeType === 1 && node.parentNode && node.parentNode.nodeType === 1) { - textDecoration = getTextDecoration(node.parentNode); - if (ed.dom.getStyle(node, 'color') && textDecoration) { - ed.dom.setStyle(node, 'text-decoration', textDecoration); - } else if (ed.dom.getStyle(node, 'text-decoration') === textDecoration) { - ed.dom.setStyle(node, 'text-decoration', null); - } - } - } - - /** - * Applies the specified format to the current selection or specified node. - * - * @method apply - * @param {String} name Name of format to apply. - * @param {Object} vars Optional list of variables to replace within format before applying it. - * @param {Node} node Optional node to apply the format to defaults to current selection. - */ - function apply(name, vars, node) { - var formatList = get(name), - format = formatList[0], - bookmark, rng, isCollapsed = !node && selection.isCollapsed(); - - function setElementFormat(elm, fmt) { - fmt = fmt || format; - - if (elm) { - if (fmt.onformat) { - fmt.onformat(elm, fmt, vars, node); - } - - each(fmt.styles, function(value, name) { - dom.setStyle(elm, name, replaceVars(value, vars)); - }); - - // Needed for the WebKit span spam bug - // TODO: Remove this once WebKit/Blink fixes this - if (fmt.styles) { - var styleVal = dom.getAttrib(elm, 'style'); - - if (styleVal) { - elm.setAttribute('data-mce-style', styleVal); - } - } - - each(fmt.attributes, function(value, name) { - dom.setAttrib(elm, name, replaceVars(value, vars)); - }); - - each(fmt.classes, function(value) { - value = replaceVars(value, vars); - - if (!dom.hasClass(elm, value)) { - dom.addClass(elm, value); - } - }); - } - } - - function applyNodeStyle(formatList, node) { - var found = false; - - if (!format.selector) { - return false; - } - - // Look for matching formats - each(formatList, function(format) { - // Check collapsed state if it exists - if ('collapsed' in format && format.collapsed !== isCollapsed) { - return; - } - - if (dom.is(node, format.selector) && !isCaretNode(node)) { - setElementFormat(node, format); - found = true; - return false; - } - }); - - return found; - } - - // This converts: <p>[a</p><p>]b</p> -> <p>[a]</p><p>b</p> - function adjustSelectionToVisibleSelection() { - function findSelectionEnd(start, end) { - var walker = new TreeWalker(end); - for (node = walker.prev2(); node; node = walker.prev2()) { - if (node.nodeType == 3 && node.data.length > 0) { - return node; - } - - if (node.childNodes.length > 1 || node == start || node.tagName == 'BR') { - return node; - } - } - } - - // Adjust selection so that a end container with a end offset of zero is not included in the selection - // as this isn't visible to the user. - var rng = ed.selection.getRng(); - var start = rng.startContainer; - var end = rng.endContainer; - - if (start != end && rng.endOffset === 0) { - var newEnd = findSelectionEnd(start, end); - var endOffset = newEnd.nodeType == 3 ? newEnd.data.length : newEnd.childNodes.length; - - rng.setEnd(newEnd, endOffset); - } - - return rng; - } - - function applyRngStyle(rng, bookmark, nodeSpecific) { - var newWrappers = [], - wrapName, wrapElm, contentEditable = true; - - // Setup wrapper element - wrapName = format.inline || format.block; - wrapElm = dom.create(wrapName); - setElementFormat(wrapElm); - - rangeUtils.walk(rng, function(nodes) { - var currentWrapElm; - - /** - * Process a list of nodes wrap them. - */ - function process(node) { - var nodeName, parentName, hasContentEditableState, lastContentEditable; - - lastContentEditable = contentEditable; - nodeName = node.nodeName.toLowerCase(); - parentName = node.parentNode.nodeName.toLowerCase(); - - // Node has a contentEditable value - if (node.nodeType === 1 && getContentEditable(node)) { - lastContentEditable = contentEditable; - contentEditable = getContentEditable(node) === "true"; - hasContentEditableState = true; // We don't want to wrap the container only it's children - } - - // Stop wrapping on br elements - if (isEq(nodeName, 'br')) { - currentWrapElm = 0; - - // Remove any br elements when we wrap things - if (format.block) { - dom.remove(node); - } - - return; - } - - // If node is wrapper type - if (format.wrapper && matchNode(node, name, vars)) { - currentWrapElm = 0; - return; - } - - // Can we rename the block - // TODO: Break this if up, too complex - if (contentEditable && !hasContentEditableState && format.block && - !format.wrapper && isTextBlock(nodeName) && isValid(parentName, wrapName)) { - node = dom.rename(node, wrapName); - setElementFormat(node); - newWrappers.push(node); - currentWrapElm = 0; - return; - } - - // Handle selector patterns - if (format.selector) { - var found = applyNodeStyle(formatList, node); - - // Continue processing if a selector match wasn't found and a inline element is defined - if (!format.inline || found) { - currentWrapElm = 0; - return; - } - } - - // Is it valid to wrap this item - // TODO: Break this if up, too complex - if (contentEditable && !hasContentEditableState && isValid(wrapName, nodeName) && isValid(parentName, wrapName) && - !(!nodeSpecific && node.nodeType === 3 && - node.nodeValue.length === 1 && - node.nodeValue.charCodeAt(0) === 65279) && - !isCaretNode(node) && - (!format.inline || !isBlock(node))) { - // Start wrapping - if (!currentWrapElm) { - // Wrap the node - currentWrapElm = dom.clone(wrapElm, FALSE); - node.parentNode.insertBefore(currentWrapElm, node); - newWrappers.push(currentWrapElm); - } - - currentWrapElm.appendChild(node); - } else { - // Start a new wrapper for possible children - currentWrapElm = 0; - - each(grep(node.childNodes), process); - - if (hasContentEditableState) { - contentEditable = lastContentEditable; // Restore last contentEditable state from stack - } - - // End the last wrapper - currentWrapElm = 0; - } - } - - // Process siblings from range - each(nodes, process); - }); - - // Apply formats to links as well to get the color of the underline to change as well - if (format.links === true) { - each(newWrappers, function(node) { - function process(node) { - if (node.nodeName === 'A') { - setElementFormat(node, format); - } - - each(grep(node.childNodes), process); - } - - process(node); - }); - } - - // Cleanup - each(newWrappers, function(node) { - var childCount; - - function getChildCount(node) { - var count = 0; - - each(node.childNodes, function(node) { - if (!isWhiteSpaceNode(node) && !isBookmarkNode(node)) { - count++; - } - }); - - return count; - } - - function getChildElementNode(root) { - var child = false; - each(root.childNodes, function(node) { - if (isElementNode(node)) { - child = node; - return false; // break loop - } - }); - return child; - } - - function matchNestedWrapper(node, filter) { - do { - if (getChildCount(node) !== 1) { - break; - } - - node = getChildElementNode(node); - if (!node) { - break; - } else if (filter(node)) { - return node; - } - } while (node); - - return null; - } - - function mergeStyles(node) { - var child, clone; - - child = getChildElementNode(node); - - // If child was found and of the same type as the current node - if (child && !isBookmarkNode(child) && matchName(child, format)) { - clone = dom.clone(child, FALSE); - setElementFormat(clone); - - dom.replace(clone, node, TRUE); - dom.remove(child, 1); - } - - return clone || node; - } - - childCount = getChildCount(node); - - // Remove empty nodes but only if there is multiple wrappers and they are not block - // elements so never remove single <h1></h1> since that would remove the - // current empty block element where the caret is at - if ((newWrappers.length > 1 || !isBlock(node)) && childCount === 0) { - dom.remove(node, 1); - return; - } - - if (format.inline || format.wrapper) { - // Merges the current node with it's children of similar type to reduce the number of elements - if (!format.exact && childCount === 1) { - node = mergeStyles(node); - } - - // Remove/merge children - each(formatList, function(format) { - // Merge all children of similar type will move styles from child to parent - // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span> - // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span> - each(dom.select(format.inline, node), function(child) { - if (!isElementNode(child)) { - return; - } - - removeFormat(format, vars, child, format.exact ? child : null); - }); - - clearChildStyles(format, node); - }); - - // Remove format if direct parent already has the same format - if (matchNode(node.parentNode, name, vars)) { - if (removeFormat(format, vars, node)) { - node = 0; - } - } - - // Remove format if any ancestor already has the same format - if (format.merge_with_parents) { - dom.getParent(node.parentNode, function(parent) { - if (matchNode(parent, name, vars)) { - if (removeFormat(format, vars, node)) { - node = 0; - } - return TRUE; - } - }); - } - - // fontSize defines the line height for the whole branch of nested style wrappers, - // therefore it should be set on the outermost wrapper - if (node && !isBlock(node) && !getStyle(node, 'fontSize')) { - var styleNode = matchNestedWrapper(node, hasStyle('fontSize')); - if (styleNode) { - apply('fontsize', { - value: getStyle(styleNode, 'fontSize') - }, node); - } - } - - // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b> - if (node && format.merge_siblings !== false) { - node = mergeSiblings(getNonWhiteSpaceSibling(node), node); - node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE)); - } - } - }); - } - - if (getContentEditable(selection.getNode()) === "false") { - node = selection.getNode(); - for (var i = 0, l = formatList.length; i < l; i++) { - if (formatList[i].ceFalseOverride && dom.is(node, formatList[i].selector)) { - setElementFormat(node, formatList[i]); - return; - } - } - - return; - } - - if (format) { - if (node) { - if (node.nodeType) { - if (!applyNodeStyle(formatList, node)) { - rng = dom.createRng(); - rng.setStartBefore(node); - rng.setEndAfter(node); - applyRngStyle(expandRng(rng, formatList), null, true); - } - } else { - applyRngStyle(node, null, true); - } - } else { - if (!isCollapsed || !format.inline || dom.select('td[data-mce-selected],th[data-mce-selected]').length) { - // Obtain selection node before selection is unselected by applyRngStyle() - var curSelNode = ed.selection.getNode(); - - // If the formats have a default block and we can't find a parent block then - // start wrapping it with a DIV this is for forced_root_blocks: false - // It's kind of a hack but people should be using the default block type P since all desktop editors work that way - if (!forcedRootBlock && formatList[0].defaultBlock && !dom.getParent(curSelNode, dom.isBlock)) { - apply(formatList[0].defaultBlock); - } - - // Apply formatting to selection - ed.selection.setRng(adjustSelectionToVisibleSelection()); - bookmark = selection.getBookmark(); - applyRngStyle(expandRng(selection.getRng(TRUE), formatList), bookmark); - - if (format.styles) { - // Colored nodes should be underlined so that the color of the underline matches the text color. - if (format.styles.color || format.styles.textDecoration) { - walk(curSelNode, processUnderlineAndColor, 'childNodes'); - processUnderlineAndColor(curSelNode); - } - - // nodes with font-size should have their own background color as well to fit the line-height (see TINY-882) - if (format.styles.backgroundColor) { - processChildElements(curSelNode, - hasStyle('fontSize'), - applyStyle('backgroundColor', replaceVars(format.styles.backgroundColor, vars)) - ); - } - } - - selection.moveToBookmark(bookmark); - moveStart(selection.getRng(TRUE)); - ed.nodeChanged(); - } else { - performCaretAction('apply', name, vars); - } - } - - Hooks.postProcess(name, ed); - } - } - - /** - * Removes the specified format from the current selection or specified node. - * - * @method remove - * @param {String} name Name of format to remove. - * @param {Object} vars Optional list of variables to replace within format before removing it. - * @param {Node/Range} node Optional node or DOM range to remove the format from defaults to current selection. - */ - function remove(name, vars, node, similar) { - var formatList = get(name), - format = formatList[0], - bookmark, rng, contentEditable = true; - - // Merges the styles for each node - function process(node) { - var children, i, l, lastContentEditable, hasContentEditableState; - - // Node has a contentEditable value - if (node.nodeType === 1 && getContentEditable(node)) { - lastContentEditable = contentEditable; - contentEditable = getContentEditable(node) === "true"; - hasContentEditableState = true; // We don't want to wrap the container only it's children - } - - // Grab the children first since the nodelist might be changed - children = grep(node.childNodes); - - // Process current node - if (contentEditable && !hasContentEditableState) { - for (i = 0, l = formatList.length; i < l; i++) { - if (removeFormat(formatList[i], vars, node, node)) { - break; - } - } - } - - // Process the children - if (format.deep) { - if (children.length) { - for (i = 0, l = children.length; i < l; i++) { - process(children[i]); - } - - if (hasContentEditableState) { - contentEditable = lastContentEditable; // Restore last contentEditable state from stack - } - } - } - } - - function findFormatRoot(container) { - var formatRoot; - - // Find format root - each(getParents(container.parentNode).reverse(), function(parent) { - var format; - - // Find format root element - if (!formatRoot && parent.id != '_start' && parent.id != '_end') { - // Is the node matching the format we are looking for - format = matchNode(parent, name, vars, similar); - if (format && format.split !== false) { - formatRoot = parent; - } - } - }); - - return formatRoot; - } - - function wrapAndSplit(formatRoot, container, target, split) { - var parent, clone, lastClone, firstClone, i, formatRootParent; - - // Format root found then clone formats and split it - if (formatRoot) { - formatRootParent = formatRoot.parentNode; - - for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) { - clone = dom.clone(parent, FALSE); - - for (i = 0; i < formatList.length; i++) { - if (removeFormat(formatList[i], vars, clone, clone)) { - clone = 0; - break; - } - } - - // Build wrapper node - if (clone) { - if (lastClone) { - clone.appendChild(lastClone); - } - - if (!firstClone) { - firstClone = clone; - } - - lastClone = clone; - } - } - - // Never split block elements if the format is mixed - if (split && (!format.mixed || !isBlock(formatRoot))) { - container = dom.split(formatRoot, container); - } - - // Wrap container in cloned formats - if (lastClone) { - target.parentNode.insertBefore(lastClone, target); - firstClone.appendChild(target); - } - } - - return container; - } - - function splitToFormatRoot(container) { - return wrapAndSplit(findFormatRoot(container), container, container, true); - } - - function unwrap(start) { - var node = dom.get(start ? '_start' : '_end'), - out = node[start ? 'firstChild' : 'lastChild']; - - // If the end is placed within the start the result will be removed - // So this checks if the out node is a bookmark node if it is it - // checks for another more suitable node - if (isBookmarkNode(out)) { - out = out[start ? 'firstChild' : 'lastChild']; - } - - // Since dom.remove removes empty text nodes then we need to try to find a better node - if (out.nodeType == 3 && out.data.length === 0) { - out = start ? node.previousSibling || node.nextSibling : node.nextSibling || node.previousSibling; - } - - dom.remove(node, true); - - return out; - } - - function removeRngStyle(rng) { - var startContainer, endContainer; - var commonAncestorContainer = rng.commonAncestorContainer; - - rng = expandRng(rng, formatList, TRUE); - - if (format.split) { - startContainer = getContainer(rng, TRUE); - endContainer = getContainer(rng); - - if (startContainer != endContainer) { - // WebKit will render the table incorrectly if we wrap a TH or TD in a SPAN - // so let's see if we can use the first child instead - // This will happen if you triple click a table cell and use remove formatting - if (/^(TR|TH|TD)$/.test(startContainer.nodeName) && startContainer.firstChild) { - if (startContainer.nodeName == "TR") { - startContainer = startContainer.firstChild.firstChild || startContainer; - } else { - startContainer = startContainer.firstChild || startContainer; - } - } - - // Try to adjust endContainer as well if cells on the same row were selected - bug #6410 - if (commonAncestorContainer && - /^T(HEAD|BODY|FOOT|R)$/.test(commonAncestorContainer.nodeName) && - isTableCell(endContainer) && endContainer.firstChild) { - endContainer = endContainer.firstChild || endContainer; - } - - if (dom.isChildOf(startContainer, endContainer) && !isBlock(endContainer) && - !isTableCell(startContainer) && !isTableCell(endContainer)) { - startContainer = wrap(startContainer, 'span', { - id: '_start', - 'data-mce-type': 'bookmark' - }); - splitToFormatRoot(startContainer); - startContainer = unwrap(TRUE); - return; - } - - // Wrap start/end nodes in span element since these might be cloned/moved - startContainer = wrap(startContainer, 'span', { - id: '_start', - 'data-mce-type': 'bookmark' - }); - endContainer = wrap(endContainer, 'span', { - id: '_end', - 'data-mce-type': 'bookmark' - }); - - // Split start/end - splitToFormatRoot(startContainer); - splitToFormatRoot(endContainer); - - // Unwrap start/end to get real elements again - startContainer = unwrap(TRUE); - endContainer = unwrap(); - } else { - startContainer = endContainer = splitToFormatRoot(startContainer); - } - - // Update range positions since they might have changed after the split operations - rng.startContainer = startContainer.parentNode ? startContainer.parentNode : startContainer; - rng.startOffset = nodeIndex(startContainer); - rng.endContainer = endContainer.parentNode ? endContainer.parentNode : endContainer; - rng.endOffset = nodeIndex(endContainer) + 1; - } - - // Remove items between start/end - rangeUtils.walk(rng, function(nodes) { - each(nodes, function(node) { - process(node); - - // Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined. - if (node.nodeType === 1 && ed.dom.getStyle(node, 'text-decoration') === 'underline' && - node.parentNode && getTextDecoration(node.parentNode) === 'underline') { - removeFormat({ - 'deep': false, - 'exact': true, - 'inline': 'span', - 'styles': { - 'textDecoration': 'underline' - } - }, null, node); - } - }); - }); - } - - // Handle node - if (node) { - if (node.nodeType) { - rng = dom.createRng(); - rng.setStartBefore(node); - rng.setEndAfter(node); - removeRngStyle(rng); - } else { - removeRngStyle(node); - } - - return; - } - - if (getContentEditable(selection.getNode()) === "false") { - node = selection.getNode(); - for (var i = 0, l = formatList.length; i < l; i++) { - if (formatList[i].ceFalseOverride) { - if (removeFormat(formatList[i], vars, node, node)) { - break; - } - } - } - - return; - } - - if (!selection.isCollapsed() || !format.inline || dom.select('td[data-mce-selected],th[data-mce-selected]').length) { - bookmark = selection.getBookmark(); - removeRngStyle(selection.getRng(TRUE)); - selection.moveToBookmark(bookmark); - - // Check if start element still has formatting then we are at: "<b>text|</b>text" - // and need to move the start into the next text node - if (format.inline && match(name, vars, selection.getStart())) { - moveStart(selection.getRng(true)); - } - - ed.nodeChanged(); - } else { - performCaretAction('remove', name, vars, similar); - } - } - - /** - * Toggles the specified format on/off. - * - * @method toggle - * @param {String} name Name of format to apply/remove. - * @param {Object} vars Optional list of variables to replace within format before applying/removing it. - * @param {Node} node Optional node to apply the format to or remove from. Defaults to current selection. - */ - function toggle(name, vars, node) { - var fmt = get(name); - - if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle)) { - remove(name, vars, node); - } else { - apply(name, vars, node); - } - } - - /** - * Return true/false if the specified node has the specified format. - * - * @method matchNode - * @param {Node} node Node to check the format on. - * @param {String} name Format name to check. - * @param {Object} vars Optional list of variables to replace before checking it. - * @param {Boolean} similar Match format that has similar properties. - * @return {Object} Returns the format object it matches or undefined if it doesn't match. - */ - function matchNode(node, name, vars, similar) { - var formatList = get(name), - format, i, classes; - - function matchItems(node, format, itemName) { - var key, value, items = format[itemName], - i; - - // Custom match - if (format.onmatch) { - return format.onmatch(node, format, itemName); - } - - // Check all items - if (items) { - // Non indexed object - if (items.length === undef) { - for (key in items) { - if (items.hasOwnProperty(key)) { - if (itemName === 'attributes') { - value = dom.getAttrib(node, key); - } else { - value = getStyle(node, key); - } - - if (similar && !value && !format.exact) { - return; - } - - if ((!similar || format.exact) && !isEq(value, normalizeStyleValue(replaceVars(items[key], vars), key))) { - return; - } - } - } - } else { - // Only one match needed for indexed arrays - for (i = 0; i < items.length; i++) { - if (itemName === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i])) { - return format; - } - } - } - } - - return format; - } - - if (formatList && node) { - // Check each format in list - for (i = 0; i < formatList.length; i++) { - format = formatList[i]; - - // Name name, attributes, styles and classes - if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) { - // Match classes - if ((classes = format.classes)) { - for (i = 0; i < classes.length; i++) { - if (!dom.hasClass(node, classes[i])) { - return; - } - } - } - - return format; - } - } - } - } - - /** - * Matches the current selection or specified node against the specified format name. - * - * @method match - * @param {String} name Name of format to match. - * @param {Object} vars Optional list of variables to replace before checking it. - * @param {Node} node Optional node to check. - * @return {boolean} true/false if the specified selection/node matches the format. - */ - function match(name, vars, node) { - var startNode; - - function matchParents(node) { - var root = dom.getRoot(); - - if (node === root) { - return false; - } - - // Find first node with similar format settings - node = dom.getParent(node, function(node) { - if (matchesUnInheritedFormatSelector(node, name)) { - return true; - } - - return node.parentNode === root || !!matchNode(node, name, vars, true); - }); - - // Do an exact check on the similar format element - return matchNode(node, name, vars); - } - - // Check specified node - if (node) { - return matchParents(node); - } - - // Check selected node - node = selection.getNode(); - if (matchParents(node)) { - return TRUE; - } - - // Check start node if it's different - startNode = selection.getStart(); - if (startNode != node) { - if (matchParents(startNode)) { - return TRUE; - } - } - - return FALSE; - } - - /** - * Matches the current selection against the array of formats and returns a new array with matching formats. - * - * @method matchAll - * @param {Array} names Name of format to match. - * @param {Object} vars Optional list of variables to replace before checking it. - * @return {Array} Array with matched formats. - */ - function matchAll(names, vars) { - var startElement, matchedFormatNames = [], - checkedMap = {}; - - // Check start of selection for formats - startElement = selection.getStart(); - dom.getParent(startElement, function(node) { - var i, name; - - for (i = 0; i < names.length; i++) { - name = names[i]; - - if (!checkedMap[name] && matchNode(node, name, vars)) { - checkedMap[name] = true; - matchedFormatNames.push(name); - } - } - }, dom.getRoot()); - - return matchedFormatNames; - } - - /** - * Returns true/false if the specified format can be applied to the current selection or not. It - * will currently only check the state for selector formats, it returns true on all other format types. - * - * @method canApply - * @param {String} name Name of format to check. - * @return {boolean} true/false if the specified format can be applied to the current selection/node. - */ - function canApply(name) { - var formatList = get(name), - startNode, parents, i, x, selector; - - if (formatList) { - startNode = selection.getStart(); - parents = getParents(startNode); - - for (x = formatList.length - 1; x >= 0; x--) { - selector = formatList[x].selector; - - // Format is not selector based then always return TRUE - // Is it has a defaultBlock then it's likely it can be applied for example align on a non block element line - if (!selector || formatList[x].defaultBlock) { - return TRUE; - } - - for (i = parents.length - 1; i >= 0; i--) { - if (dom.is(parents[i], selector)) { - return TRUE; - } - } - } - } - - return FALSE; - } - - /** - * Executes the specified callback when the current selection matches the formats or not. - * - * @method formatChanged - * @param {String} formats Comma separated list of formats to check for. - * @param {function} callback Callback with state and args when the format is changed/toggled on/off. - * @param {Boolean} similar True/false state if the match should handle similar or exact formats. - */ - function formatChanged(formats, callback, similar) { - var currentFormats; - - // Setup format node change logic - if (!formatChangeData) { - formatChangeData = {}; - currentFormats = {}; - - ed.on('NodeChange', function(e) { - var parents = getParents(e.element), - matchedFormats = {}; - - // Ignore bogus nodes like the <a> tag created by moveStart() - parents = Tools.grep(parents, function(node) { - return node.nodeType == 1 && !node.getAttribute('data-mce-bogus'); - }); - - // Check for new formats - each(formatChangeData, function(callbacks, format) { - each(parents, function(node) { - if (matchNode(node, format, {}, callbacks.similar)) { - if (!currentFormats[format]) { - // Execute callbacks - each(callbacks, function(callback) { - callback(true, { - node: node, - format: format, - parents: parents - }); - }); - - currentFormats[format] = callbacks; - } - - matchedFormats[format] = callbacks; - return false; - } - - if (matchesUnInheritedFormatSelector(node, format)) { - return false; - } - }); - }); - - // Check if current formats still match - each(currentFormats, function(callbacks, format) { - if (!matchedFormats[format]) { - delete currentFormats[format]; - - each(callbacks, function(callback) { - callback(false, { - node: e.element, - format: format, - parents: parents - }); - }); - } - }); - }); - } - - // Add format listeners - each(formats.split(','), function(format) { - if (!formatChangeData[format]) { - formatChangeData[format] = []; - formatChangeData[format].similar = similar; - } - - formatChangeData[format].push(callback); - }); - - return this; - } - - /** - * Returns a preview css text for the specified format. - * - * @method getCssText - * @param {String/Object} format Format to generate preview css text for. - * @return {String} Css text for the specified format. - * @example - * var cssText1 = editor.formatter.getCssText('bold'); - * var cssText2 = editor.formatter.getCssText({inline: 'b'}); - */ - function getCssText(format) { - return Preview.getCssText(ed, format); - } - - // Expose to public - extend(this, { - get: get, - register: register, - unregister: unregister, - apply: apply, - remove: remove, - toggle: toggle, - match: match, - matchAll: matchAll, - matchNode: matchNode, - canApply: canApply, - formatChanged: formatChanged, - getCssText: getCssText - }); - - // Initialize - defaultFormats(); - addKeyboardShortcuts(); - ed.on('BeforeGetContent', function(e) { - if (markCaretContainersBogus && e.format != 'raw') { - markCaretContainersBogus(); - } - }); - ed.on('mouseup keydown', function(e) { - if (disableCaretContainer) { - disableCaretContainer(e); - } - }); - - // Private functions - - /** - * Checks if the specified nodes name matches the format inline/block or selector. - * - * @private - * @param {Node} node Node to match against the specified format. - * @param {Object} format Format object o match with. - * @return {boolean} true/false if the format matches. - */ - function matchName(node, format) { - // Check for inline match - if (isEq(node, format.inline)) { - return TRUE; - } - - // Check for block match - if (isEq(node, format.block)) { - return TRUE; - } - - // Check for selector match - if (format.selector) { - return node.nodeType == 1 && dom.is(node, format.selector); - } - } - - /** - * Compares two string/nodes regardless of their case. - * - * @private - * @param {String/Node} str1 Node or string to compare. - * @param {String/Node} str2 Node or string to compare. - * @return {boolean} True/false if they match. - */ - function isEq(str1, str2) { - str1 = str1 || ''; - str2 = str2 || ''; - - str1 = '' + (str1.nodeName || str1); - str2 = '' + (str2.nodeName || str2); - - return str1.toLowerCase() == str2.toLowerCase(); - } - - function processChildElements(node, filter, process) { - each(node.childNodes, function(node) { - if (isElementNode(node)) { - if (filter(node)) { - process(node); - } - if (node.hasChildNodes()) { - processChildElements(node, filter, process); - } - } - }); - } - - function isElementNode(node) { - return node && node.nodeType === 1 && !isBookmarkNode(node) && !isCaretNode(node) && !NodeType.isBogus(node); - } - - function hasStyle(name) { - return Fun.curry(function(name, node) { - return !!(node && getStyle(node, name)); - }, name); - } - - function applyStyle(name, value) { - return Fun.curry(function(name, value, node) { - dom.setStyle(node, name, value); - }, name, value); - } - - /** - * Returns the style by name on the specified node. This method modifies the style - * contents to make it more easy to match. This will resolve a few browser issues. - * - * @private - * @param {Node} node to get style from. - * @param {String} name Style name to get. - * @return {String} Style item value. - */ - function getStyle(node, name) { - return normalizeStyleValue(dom.getStyle(node, name), name); - } - - /** - * Normalize style value by name. This method modifies the style contents - * to make it more easy to match. This will resolve a few browser issues. - * - * @private - * @param {String} value Value to get style from. - * @param {String} name Style name to get. - * @return {String} Style item value. - */ - function normalizeStyleValue(value, name) { - // Force the format to hex - if (name == 'color' || name == 'backgroundColor') { - value = dom.toHex(value); - } - - // Opera will return bold as 700 - if (name == 'fontWeight' && value == 700) { - value = 'bold'; - } - - // Normalize fontFamily so "'Font name', Font" becomes: "Font name,Font" - if (name == 'fontFamily') { - value = value.replace(/[\'\"]/g, '').replace(/,\s+/g, ','); - } - - return '' + value; - } - - /** - * Replaces variables in the value. The variable format is %var. - * - * @private - * @param {String} value Value to replace variables in. - * @param {Object} vars Name/value array with variables to replace. - * @return {String} New value with replaced variables. - */ - function replaceVars(value, vars) { - if (typeof value != "string") { - value = value(vars); - } else if (vars) { - value = value.replace(/%(\w+)/g, function(str, name) { - return vars[name] || str; - }); - } - - return value; - } - - function isWhiteSpaceNode(node) { - return node && node.nodeType === 3 && /^([\t \r\n]+|)$/.test(node.nodeValue); - } - - function wrap(node, name, attrs) { - var wrapper = dom.create(name, attrs); - - node.parentNode.insertBefore(wrapper, node); - wrapper.appendChild(node); - - return wrapper; - } - - /** - * Expands the specified range like object to depending on format. - * - * For example on block formats it will move the start/end position - * to the beginning of the current block. - * - * @private - * @param {Object} rng Range like object. - * @param {Array} format Array with formats to expand by. - * @param {Boolean} remove - * @return {Object} Expanded range like object. - */ - function expandRng(rng, format, remove) { - var lastIdx, leaf, endPoint, - startContainer = rng.startContainer, - startOffset = rng.startOffset, - endContainer = rng.endContainer, - endOffset = rng.endOffset; - - // This function walks up the tree if there is no siblings before/after the node - function findParentContainer(start) { - var container, parent, sibling, siblingName, root; - - container = parent = start ? startContainer : endContainer; - siblingName = start ? 'previousSibling' : 'nextSibling'; - root = dom.getRoot(); - - function isBogusBr(node) { - return node.nodeName == "BR" && node.getAttribute('data-mce-bogus') && !node.nextSibling; - } - - // If it's a text node and the offset is inside the text - if (container.nodeType == 3 && !isWhiteSpaceNode(container)) { - if (start ? startOffset > 0 : endOffset < container.nodeValue.length) { - return container; - } - } - - /*eslint no-constant-condition:0 */ - while (true) { - // Stop expanding on block elements - if (!format[0].block_expand && isBlock(parent)) { - return parent; - } - - // Walk left/right - for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) { - if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling) && !isBogusBr(sibling)) { - return parent; - } - } - - // Check if we can move up are we at root level or body level - if (parent == root || parent.parentNode == root) { - container = parent; - break; - } - - parent = parent.parentNode; - } - - return container; - } - - // This function walks down the tree to find the leaf at the selection. - // The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node. - function findLeaf(node, offset) { - if (offset === undef) { - offset = node.nodeType === 3 ? node.length : node.childNodes.length; - } - - while (node && node.hasChildNodes()) { - node = node.childNodes[offset]; - if (node) { - offset = node.nodeType === 3 ? node.length : node.childNodes.length; - } - } - return { - node: node, - offset: offset - }; - } - - // If index based start position then resolve it - if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) { - lastIdx = startContainer.childNodes.length - 1; - startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset]; - - if (startContainer.nodeType == 3) { - startOffset = 0; - } - } - - // If index based end position then resolve it - if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) { - lastIdx = endContainer.childNodes.length - 1; - endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1]; - - if (endContainer.nodeType == 3) { - endOffset = endContainer.nodeValue.length; - } - } - - // Expands the node to the closes contentEditable false element if it exists - function findParentContentEditable(node) { - var parent = node; - - while (parent) { - if (parent.nodeType === 1 && getContentEditable(parent)) { - return getContentEditable(parent) === "false" ? parent : node; - } - - parent = parent.parentNode; - } - - return node; - } - - function findWordEndPoint(container, offset, start) { - var walker, node, pos, lastTextNode; - - function findSpace(node, offset) { - var pos, pos2, str = node.nodeValue; - - if (typeof offset == "undefined") { - offset = start ? str.length : 0; - } - - if (start) { - pos = str.lastIndexOf(' ', offset); - pos2 = str.lastIndexOf('\u00a0', offset); - pos = pos > pos2 ? pos : pos2; - - // Include the space on remove to avoid tag soup - if (pos !== -1 && !remove) { - pos++; - } - } else { - pos = str.indexOf(' ', offset); - pos2 = str.indexOf('\u00a0', offset); - pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2; - } - - return pos; - } - - if (container.nodeType === 3) { - pos = findSpace(container, offset); - - if (pos !== -1) { - return { - container: container, - offset: pos - }; - } - - lastTextNode = container; - } - - // Walk the nodes inside the block - walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody()); - while ((node = walker[start ? 'prev' : 'next']())) { - if (node.nodeType === 3) { - lastTextNode = node; - pos = findSpace(node); - - if (pos !== -1) { - return { - container: node, - offset: pos - }; - } - } else if (isBlock(node)) { - break; - } - } - - if (lastTextNode) { - if (start) { - offset = 0; - } else { - offset = lastTextNode.length; - } - - return { - container: lastTextNode, - offset: offset - }; - } - } - - function findSelectorEndPoint(container, siblingName) { - var parents, i, y, curFormat; - - if (container.nodeType == 3 && container.nodeValue.length === 0 && container[siblingName]) { - container = container[siblingName]; - } - - parents = getParents(container); - for (i = 0; i < parents.length; i++) { - for (y = 0; y < format.length; y++) { - curFormat = format[y]; - - // If collapsed state is set then skip formats that doesn't match that - if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed) { - continue; - } - - if (dom.is(parents[i], curFormat.selector)) { - return parents[i]; - } - } - } - - return container; - } - - function findBlockEndPoint(container, siblingName) { - var node, root = dom.getRoot(); - - // Expand to block of similar type - if (!format[0].wrapper) { - node = dom.getParent(container, format[0].block, root); - } - - // Expand to first wrappable block element or any block element - if (!node) { - node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, function(node) { - // Fixes #6183 where it would expand to editable parent element in inline mode - return node != root && isTextBlock(node); - }); - } - - // Exclude inner lists from wrapping - if (node && format[0].wrapper) { - node = getParents(node, 'ul,ol').reverse()[0] || node; - } - - // Didn't find a block element look for first/last wrappable element - if (!node) { - node = container; - - while (node[siblingName] && !isBlock(node[siblingName])) { - node = node[siblingName]; - - // Break on BR but include it will be removed later on - // we can't remove it now since we need to check if it can be wrapped - if (isEq(node, 'br')) { - break; - } - } - } - - return node || container; - } - - // Expand to closest contentEditable element - startContainer = findParentContentEditable(startContainer); - endContainer = findParentContentEditable(endContainer); - - // Exclude bookmark nodes if possible - if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) { - startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode; - startContainer = startContainer.nextSibling || startContainer; - - if (startContainer.nodeType == 3) { - startOffset = 0; - } - } - - if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) { - endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode; - endContainer = endContainer.previousSibling || endContainer; - - if (endContainer.nodeType == 3) { - endOffset = endContainer.length; - } - } - - if (format[0].inline) { - if (rng.collapsed) { - // Expand left to closest word boundary - endPoint = findWordEndPoint(startContainer, startOffset, true); - if (endPoint) { - startContainer = endPoint.container; - startOffset = endPoint.offset; - } - - // Expand right to closest word boundary - endPoint = findWordEndPoint(endContainer, endOffset); - if (endPoint) { - endContainer = endPoint.container; - endOffset = endPoint.offset; - } - } - - // Avoid applying formatting to a trailing space. - leaf = findLeaf(endContainer, endOffset); - if (leaf.node) { - while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling) { - leaf = findLeaf(leaf.node.previousSibling); - } - - if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 && - leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') { - - if (leaf.offset > 1) { - endContainer = leaf.node; - endContainer.splitText(leaf.offset - 1); - } - } - } - } - - // Move start/end point up the tree if the leaves are sharp and if we are in different containers - // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>! - // This will reduce the number of wrapper elements that needs to be created - // Move start point up the tree - if (format[0].inline || format[0].block_expand) { - if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) { - startContainer = findParentContainer(true); - } - - if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) { - endContainer = findParentContainer(); - } - } - - // Expand start/end container to matching selector - if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) { - // Find new startContainer/endContainer if there is better one - startContainer = findSelectorEndPoint(startContainer, 'previousSibling'); - endContainer = findSelectorEndPoint(endContainer, 'nextSibling'); - } - - // Expand start/end container to matching block element or text node - if (format[0].block || format[0].selector) { - // Find new startContainer/endContainer if there is better one - startContainer = findBlockEndPoint(startContainer, 'previousSibling'); - endContainer = findBlockEndPoint(endContainer, 'nextSibling'); - - // Non block element then try to expand up the leaf - if (format[0].block) { - if (!isBlock(startContainer)) { - startContainer = findParentContainer(true); - } - - if (!isBlock(endContainer)) { - endContainer = findParentContainer(); - } - } - } - - // Setup index for startContainer - if (startContainer.nodeType == 1) { - startOffset = nodeIndex(startContainer); - startContainer = startContainer.parentNode; - } - - // Setup index for endContainer - if (endContainer.nodeType == 1) { - endOffset = nodeIndex(endContainer) + 1; - endContainer = endContainer.parentNode; - } - - // Return new range like object - return { - startContainer: startContainer, - startOffset: startOffset, - endContainer: endContainer, - endOffset: endOffset - }; - } - - function isColorFormatAndAnchor(node, format) { - return format.links && node.tagName == 'A'; - } - - /** - * Removes the specified format for the specified node. It will also remove the node if it doesn't have - * any attributes if the format specifies it to do so. - * - * @private - * @param {Object} format Format object with items to remove from node. - * @param {Object} vars Name/value object with variables to apply to format. - * @param {Node} node Node to remove the format styles on. - * @param {Node} compareNode Optional compare node, if specified the styles will be compared to that node. - * @return {Boolean} True/false if the node was removed or not. - */ - function removeFormat(format, vars, node, compareNode) { - var i, attrs, stylesModified; - - // Check if node matches format - if (!matchName(node, format) && !isColorFormatAndAnchor(node, format)) { - return FALSE; - } - - // Should we compare with format attribs and styles - if (format.remove != 'all') { - // Remove styles - each(format.styles, function(value, name) { - value = normalizeStyleValue(replaceVars(value, vars), name); - - // Indexed array - if (typeof name === 'number') { - name = value; - compareNode = 0; - } - - if (format.remove_similar || (!compareNode || isEq(getStyle(compareNode, name), value))) { - dom.setStyle(node, name, ''); - } - - stylesModified = 1; - }); - - // Remove style attribute if it's empty - if (stylesModified && dom.getAttrib(node, 'style') === '') { - node.removeAttribute('style'); - node.removeAttribute('data-mce-style'); - } - - // Remove attributes - each(format.attributes, function(value, name) { - var valueOut; - - value = replaceVars(value, vars); - - // Indexed array - if (typeof name === 'number') { - name = value; - compareNode = 0; - } - - if (!compareNode || isEq(dom.getAttrib(compareNode, name), value)) { - // Keep internal classes - if (name == 'class') { - value = dom.getAttrib(node, name); - if (value) { - // Build new class value where everything is removed except the internal prefixed classes - valueOut = ''; - each(value.split(/\s+/), function(cls) { - if (/mce\-\w+/.test(cls)) { - valueOut += (valueOut ? ' ' : '') + cls; - } - }); - - // We got some internal classes left - if (valueOut) { - dom.setAttrib(node, name, valueOut); - return; - } - } - } - - // IE6 has a bug where the attribute doesn't get removed correctly - if (name == "class") { - node.removeAttribute('className'); - } - - // Remove mce prefixed attributes - if (MCE_ATTR_RE.test(name)) { - node.removeAttribute('data-mce-' + name); - } - - node.removeAttribute(name); - } - }); - - // Remove classes - each(format.classes, function(value) { - value = replaceVars(value, vars); - - if (!compareNode || dom.hasClass(compareNode, value)) { - dom.removeClass(node, value); - } - }); - - // Check for non internal attributes - attrs = dom.getAttribs(node); - for (i = 0; i < attrs.length; i++) { - var attrName = attrs[i].nodeName; - if (attrName.indexOf('_') !== 0 && attrName.indexOf('data-') !== 0) { - return FALSE; - } - } - } - - // Remove the inline child if it's empty for example <b> or <span> - if (format.remove != 'none') { - removeNode(node, format); - return TRUE; - } - } - - /** - * Removes the node and wrap it's children in paragraphs before doing so or - * appends BR elements to the beginning/end of the block element if forcedRootBlocks is disabled. - * - * If the div in the node below gets removed: - * text<div>text</div>text - * - * Output becomes: - * text<div><br />text<br /></div>text - * - * So when the div is removed the result is: - * text<br />text<br />text - * - * @private - * @param {Node} node Node to remove + apply BR/P elements to. - * @param {Object} format Format rule. - * @return {Node} Input node. - */ - function removeNode(node, format) { - var parentNode = node.parentNode, - rootBlockElm; - - function find(node, next, inc) { - node = getNonWhiteSpaceSibling(node, next, inc); - - return !node || (node.nodeName == 'BR' || isBlock(node)); - } - - if (format.block) { - if (!forcedRootBlock) { - // Append BR elements if needed before we remove the block - if (isBlock(node) && !isBlock(parentNode)) { - if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1)) { - node.insertBefore(dom.create('br'), node.firstChild); - } - - if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1)) { - node.appendChild(dom.create('br')); - } - } - } else { - // Wrap the block in a forcedRootBlock if we are at the root of document - if (parentNode == dom.getRoot()) { - if (!format.list_block || !isEq(node, format.list_block)) { - each(grep(node.childNodes), function(node) { - if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) { - if (!rootBlockElm) { - rootBlockElm = wrap(node, forcedRootBlock); - dom.setAttribs(rootBlockElm, ed.settings.forced_root_block_attrs); - } else { - rootBlockElm.appendChild(node); - } - } else { - rootBlockElm = 0; - } - }); - } - } - } - } - - // Never remove nodes that isn't the specified inline element if a selector is specified too - if (format.selector && format.inline && !isEq(format.inline, node)) { - return; - } - - dom.remove(node, 1); - } - - /** - * Returns the next/previous non whitespace node. - * - * @private - * @param {Node} node Node to start at. - * @param {boolean} next (Optional) Include next or previous node defaults to previous. - * @param {boolean} inc (Optional) Include the current node in checking. Defaults to false. - * @return {Node} Next or previous node or undefined if it wasn't found. - */ - function getNonWhiteSpaceSibling(node, next, inc) { - if (node) { - next = next ? 'nextSibling' : 'previousSibling'; - - for (node = inc ? node : node[next]; node; node = node[next]) { - if (node.nodeType == 1 || !isWhiteSpaceNode(node)) { - return node; - } - } - } - } - - /** - * Merges the next/previous sibling element if they match. - * - * @private - * @param {Node} prev Previous node to compare/merge. - * @param {Node} next Next node to compare/merge. - * @return {Node} Next node if we didn't merge and prev node if we did. - */ - function mergeSiblings(prev, next) { - var sibling, tmpSibling, elementUtils = new ElementUtils(dom); - - function findElementSibling(node, siblingName) { - for (sibling = node; sibling; sibling = sibling[siblingName]) { - if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0) { - return node; - } - - if (sibling.nodeType == 1 && !isBookmarkNode(sibling)) { - return sibling; - } - } - - return node; - } - - // Check if next/prev exists and that they are elements - if (prev && next) { - // If previous sibling is empty then jump over it - prev = findElementSibling(prev, 'previousSibling'); - next = findElementSibling(next, 'nextSibling'); - - // Compare next and previous nodes - if (elementUtils.compare(prev, next)) { - // Append nodes between - for (sibling = prev.nextSibling; sibling && sibling != next;) { - tmpSibling = sibling; - sibling = sibling.nextSibling; - prev.appendChild(tmpSibling); - } - - // Remove next node - dom.remove(next); - - // Move children into prev node - each(grep(next.childNodes), function(node) { - prev.appendChild(node); - }); - - return prev; - } - } - - return next; - } - - function getContainer(rng, start) { - var container, offset, lastIdx; - - container = rng[start ? 'startContainer' : 'endContainer']; - offset = rng[start ? 'startOffset' : 'endOffset']; - - if (container.nodeType == 1) { - lastIdx = container.childNodes.length - 1; - - if (!start && offset) { - offset--; - } - - container = container.childNodes[offset > lastIdx ? lastIdx : offset]; - } - - // If start text node is excluded then walk to the next node - if (container.nodeType === 3 && start && offset >= container.nodeValue.length) { - container = new TreeWalker(container, ed.getBody()).next() || container; - } - - // If end text node is excluded then walk to the previous node - if (container.nodeType === 3 && !start && offset === 0) { - container = new TreeWalker(container, ed.getBody()).prev() || container; - } - - return container; - } - - function performCaretAction(type, name, vars, similar) { - var caretContainerId = '_mce_caret', - debug = ed.settings.caret_debug; - - // Creates a caret container bogus element - function createCaretContainer(fill) { - var caretContainer = dom.create('span', { - id: caretContainerId, - 'data-mce-bogus': true, - style: debug ? 'color:red' : '' - }); - - if (fill) { - caretContainer.appendChild(ed.getDoc().createTextNode(INVISIBLE_CHAR)); - } - - return caretContainer; - } - - function isCaretContainerEmpty(node, nodes) { - while (node) { - if ((node.nodeType === 3 && node.nodeValue !== INVISIBLE_CHAR) || node.childNodes.length > 1) { - return false; - } - - // Collect nodes - if (nodes && node.nodeType === 1) { - nodes.push(node); - } - - node = node.firstChild; - } - - return true; - } - - // Returns any parent caret container element - function getParentCaretContainer(node) { - while (node) { - if (node.id === caretContainerId) { - return node; - } - - node = node.parentNode; - } - } - - // Finds the first text node in the specified node - function findFirstTextNode(node) { - var walker; - - if (node) { - walker = new TreeWalker(node, node); - - for (node = walker.current(); node; node = walker.next()) { - if (node.nodeType === 3) { - return node; - } - } - } - } - - // Removes the caret container for the specified node or all on the current document - function removeCaretContainer(node, moveCaret) { - var child, rng; - - if (!node) { - node = getParentCaretContainer(selection.getStart()); - - if (!node) { - while ((node = dom.get(caretContainerId))) { - removeCaretContainer(node, false); - } - } - } else { - rng = selection.getRng(true); - - if (isCaretContainerEmpty(node)) { - if (moveCaret !== false) { - rng.setStartBefore(node); - rng.setEndBefore(node); - } - - dom.remove(node); - } else { - child = findFirstTextNode(node); - - if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) { - child.deleteData(0, 1); - - // Fix for bug #6976 - if (rng.startContainer == child && rng.startOffset > 0) { - rng.setStart(child, rng.startOffset - 1); - } - - if (rng.endContainer == child && rng.endOffset > 0) { - rng.setEnd(child, rng.endOffset - 1); - } - } - - dom.remove(node, 1); - } - - selection.setRng(rng); - } - } - - // Applies formatting to the caret position - function applyCaretFormat() { - var rng, caretContainer, textNode, offset, bookmark, container, text; - - rng = selection.getRng(true); - offset = rng.startOffset; - container = rng.startContainer; - text = container.nodeValue; - - caretContainer = getParentCaretContainer(selection.getStart()); - if (caretContainer) { - textNode = findFirstTextNode(caretContainer); - } - - // Expand to word if caret is in the middle of a text node and the char before/after is a alpha numeric character - var wordcharRegex = /[^\s\u00a0\u00ad\u200b\ufeff]/; - if (text && offset > 0 && offset < text.length && - wordcharRegex.test(text.charAt(offset)) && wordcharRegex.test(text.charAt(offset - 1))) { - // Get bookmark of caret position - bookmark = selection.getBookmark(); - - // Collapse bookmark range (WebKit) - rng.collapse(true); - - // Expand the range to the closest word and split it at those points - rng = expandRng(rng, get(name)); - rng = rangeUtils.split(rng); - - // Apply the format to the range - apply(name, vars, rng); - - // Move selection back to caret position - selection.moveToBookmark(bookmark); - } else { - if (!caretContainer || textNode.nodeValue !== INVISIBLE_CHAR) { - caretContainer = createCaretContainer(true); - textNode = caretContainer.firstChild; - - rng.insertNode(caretContainer); - offset = 1; - - apply(name, vars, caretContainer); - } else { - apply(name, vars, caretContainer); - } - - // Move selection to text node - selection.setCursorLocation(textNode, offset); - } - } - - function removeCaretFormat() { - var rng = selection.getRng(true), - container, offset, bookmark, - hasContentAfter, node, formatNode, parents = [], - i, caretContainer; - - container = rng.startContainer; - offset = rng.startOffset; - node = container; - - if (container.nodeType == 3) { - if (offset != container.nodeValue.length) { - hasContentAfter = true; - } - - node = node.parentNode; - } - - while (node) { - if (matchNode(node, name, vars, similar)) { - formatNode = node; - break; - } - - if (node.nextSibling) { - hasContentAfter = true; - } - - parents.push(node); - node = node.parentNode; - } - - // Node doesn't have the specified format - if (!formatNode) { - return; - } - - // Is there contents after the caret then remove the format on the element - if (hasContentAfter) { - // Get bookmark of caret position - bookmark = selection.getBookmark(); - - // Collapse bookmark range (WebKit) - rng.collapse(true); - - // Expand the range to the closest word and split it at those points - rng = expandRng(rng, get(name), true); - rng = rangeUtils.split(rng); - - // Remove the format from the range - remove(name, vars, rng); - - // Move selection back to caret position - selection.moveToBookmark(bookmark); - } else { - caretContainer = createCaretContainer(); - - node = caretContainer; - for (i = parents.length - 1; i >= 0; i--) { - node.appendChild(dom.clone(parents[i], false)); - node = node.firstChild; - } - - // Insert invisible character into inner most format element - node.appendChild(dom.doc.createTextNode(INVISIBLE_CHAR)); - node = node.firstChild; - - var block = dom.getParent(formatNode, isTextBlock); - - if (block && dom.isEmpty(block)) { - // Replace formatNode with caretContainer when removing format from empty block like <p><b>|</b></p> - formatNode.parentNode.replaceChild(caretContainer, formatNode); - } else { - // Insert caret container after the formatted node - dom.insertAfter(caretContainer, formatNode); - } - - // Move selection to text node - selection.setCursorLocation(node, 1); - - // If the formatNode is empty, we can remove it safely. - if (dom.isEmpty(formatNode)) { - dom.remove(formatNode); - } - } - } - - // Checks if the parent caret container node isn't empty if that is the case it - // will remove the bogus state on all children that isn't empty - function unmarkBogusCaretParents() { - var caretContainer; - - caretContainer = getParentCaretContainer(selection.getStart()); - if (caretContainer && !dom.isEmpty(caretContainer)) { - walk(caretContainer, function(node) { - if (node.nodeType == 1 && node.id !== caretContainerId && !dom.isEmpty(node)) { - dom.setAttrib(node, 'data-mce-bogus', null); - } - }, 'childNodes'); - } - } - - // Only bind the caret events once - if (!ed._hasCaretEvents) { - // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements - markCaretContainersBogus = function() { - var nodes = [], - i; - - if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) { - // Mark children - i = nodes.length; - while (i--) { - dom.setAttrib(nodes[i], 'data-mce-bogus', '1'); - } - } - }; - - disableCaretContainer = function(e) { - var keyCode = e.keyCode; - - removeCaretContainer(); - - // Remove caret container if it's empty - if (keyCode == 8 && selection.isCollapsed() && selection.getStart().innerHTML == INVISIBLE_CHAR) { - removeCaretContainer(getParentCaretContainer(selection.getStart())); - } - - // Remove caret container on keydown and it's left/right arrow keys - if (keyCode == 37 || keyCode == 39) { - removeCaretContainer(getParentCaretContainer(selection.getStart())); - } - - unmarkBogusCaretParents(); - }; - - // Remove bogus state if they got filled by contents using editor.selection.setContent - ed.on('SetContent', function(e) { - if (e.selection) { - unmarkBogusCaretParents(); - } - }); - ed._hasCaretEvents = true; - } - - // Do apply or remove caret format - if (type == "apply") { - applyCaretFormat(); - } else { - removeCaretFormat(); - } - } - - /** - * Moves the start to the first suitable text node. - */ - function moveStart(rng) { - var container = rng.startContainer, - offset = rng.startOffset, - walker, node, nodes; - - if (rng.startContainer == rng.endContainer) { - if (isInlineBlock(rng.startContainer.childNodes[rng.startOffset])) { - return; - } - } - - // Convert text node into index if possible - if (container.nodeType == 3 && offset >= container.nodeValue.length) { - // Get the parent container location and walk from there - offset = nodeIndex(container); - container = container.parentNode; - } - - // Move startContainer/startOffset in to a suitable node - if (container.nodeType == 1) { - nodes = container.childNodes; - if (offset < nodes.length) { - container = nodes[offset]; - walker = new TreeWalker(container, dom.getParent(container, dom.isBlock)); - } else { - container = nodes[nodes.length - 1]; - walker = new TreeWalker(container, dom.getParent(container, dom.isBlock)); - walker.next(true); - } - - for (node = walker.current(); node; node = walker.next()) { - if (node.nodeType == 3 && !isWhiteSpaceNode(node)) { - rng.setStart(node, 0); - selection.setRng(rng); - - return; - } - } - } - } - }; - } - ); - - /** - * Diff.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * JS Implementation of the O(ND) Difference Algorithm by Eugene W. Myers. - * - * @class tinymce.undo.Diff - * @private - */ - define( - 'tinymce.core.undo.Diff', [], - function() { - var KEEP = 0, - INSERT = 1, - DELETE = 2; - - var diff = function(left, right) { - var size = left.length + right.length + 2; - var vDown = new Array(size); - var vUp = new Array(size); - - var snake = function(start, end, diag) { - return { - start: start, - end: end, - diag: diag - }; - }; - - var buildScript = function(start1, end1, start2, end2, script) { - var middle = getMiddleSnake(start1, end1, start2, end2); - - if (middle === null || middle.start === end1 && middle.diag === end1 - end2 || - middle.end === start1 && middle.diag === start1 - start2) { - var i = start1; - var j = start2; - while (i < end1 || j < end2) { - if (i < end1 && j < end2 && left[i] === right[j]) { - script.push([KEEP, left[i]]); - ++i; - ++j; - } else { - if (end1 - start1 > end2 - start2) { - script.push([DELETE, left[i]]); - ++i; - } else { - script.push([INSERT, right[j]]); - ++j; - } - } - } - } else { - buildScript(start1, middle.start, start2, middle.start - middle.diag, script); - for (var i2 = middle.start; i2 < middle.end; ++i2) { - script.push([KEEP, left[i2]]); - } - buildScript(middle.end, end1, middle.end - middle.diag, end2, script); - } - }; - - var buildSnake = function(start, diag, end1, end2) { - var end = start; - while (end - diag < end2 && end < end1 && left[end] === right[end - diag]) { - ++end; - } - return snake(start, end, diag); - }; - - var getMiddleSnake = function(start1, end1, start2, end2) { - // Myers Algorithm - // Initialisations - var m = end1 - start1; - var n = end2 - start2; - if (m === 0 || n === 0) { - return null; - } - - var delta = m - n; - var sum = n + m; - var offset = (sum % 2 === 0 ? sum : sum + 1) / 2; - vDown[1 + offset] = start1; - vUp[1 + offset] = end1 + 1; - - for (var d = 0; d <= offset; ++d) { - // Down - for (var k = -d; k <= d; k += 2) { - // First step - - var i = k + offset; - if (k === -d || k != d && vDown[i - 1] < vDown[i + 1]) { - vDown[i] = vDown[i + 1]; - } else { - vDown[i] = vDown[i - 1] + 1; - } - - var x = vDown[i]; - var y = x - start1 + start2 - k; - - while (x < end1 && y < end2 && left[x] === right[y]) { - vDown[i] = ++x; - ++y; - } - // Second step - if (delta % 2 != 0 && delta - d <= k && k <= delta + d) { - if (vUp[i - delta] <= vDown[i]) { - return buildSnake(vUp[i - delta], k + start1 - start2, end1, end2); - } - } - } - - // Up - for (k = delta - d; k <= delta + d; k += 2) { - // First step - i = k + offset - delta; - if (k === delta - d || k != delta + d && vUp[i + 1] <= vUp[i - 1]) { - vUp[i] = vUp[i + 1] - 1; - } else { - vUp[i] = vUp[i - 1]; - } - - x = vUp[i] - 1; - y = x - start1 + start2 - k; - while (x >= start1 && y >= start2 && left[x] === right[y]) { - vUp[i] = x--; - y--; - } - // Second step - if (delta % 2 === 0 && -d <= k && k <= d) { - if (vUp[i] <= vDown[i + delta]) { - return buildSnake(vUp[i], k + start1 - start2, end1, end2); - } - } - } - } - }; - - var script = []; - buildScript(0, left.length, 0, right.length, script); - return script; - }; - - return { - KEEP: KEEP, - DELETE: DELETE, - INSERT: INSERT, - diff: diff - }; - } - ); - /** - * Fragments.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module reads and applies html fragments from/to dom nodes. - * - * @class tinymce.undo.Fragments - * @private - */ - define( - 'tinymce.core.undo.Fragments', [ - "tinymce.core.util.Arr", - "tinymce.core.html.Entities", - "tinymce.core.undo.Diff" - ], - function(Arr, Entities, Diff) { - var getOuterHtml = function(elm) { - if (elm.nodeType === 1) { - return elm.outerHTML; - } else if (elm.nodeType === 3) { - return Entities.encodeRaw(elm.data, false); - } else if (elm.nodeType === 8) { - return '<!--' + elm.data + '-->'; - } - - return ''; - }; - - var createFragment = function(html) { - var frag, node, container; - - container = document.createElement("div"); - frag = document.createDocumentFragment(); - - if (html) { - container.innerHTML = html; - } - - while ((node = container.firstChild)) { - frag.appendChild(node); - } - - return frag; - }; - - var insertAt = function(elm, html, index) { - var fragment = createFragment(html); - if (elm.hasChildNodes() && index < elm.childNodes.length) { - var target = elm.childNodes[index]; - target.parentNode.insertBefore(fragment, target); - } else { - elm.appendChild(fragment); - } - }; - - var removeAt = function(elm, index) { - if (elm.hasChildNodes() && index < elm.childNodes.length) { - var target = elm.childNodes[index]; - target.parentNode.removeChild(target); - } - }; - - var applyDiff = function(diff, elm) { - var index = 0; - Arr.each(diff, function(action) { - if (action[0] === Diff.KEEP) { - index++; - } else if (action[0] === Diff.INSERT) { - insertAt(elm, action[1], index); - index++; - } else if (action[0] === Diff.DELETE) { - removeAt(elm, index); - } - }); - }; - - var read = function(elm) { - return Arr.filter(Arr.map(elm.childNodes, getOuterHtml), function(item) { - return item.length > 0; - }); - }; - - var write = function(fragments, elm) { - var currentFragments = Arr.map(elm.childNodes, getOuterHtml); - applyDiff(Diff.diff(currentFragments, fragments), elm); - return elm; - }; - - return { - read: read, - write: write - }; - } - ); - /** - * Levels.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module handles getting/setting undo levels to/from editor instances. - * - * @class tinymce.undo.Levels - * @private - */ - define( - 'tinymce.core.undo.Levels', [ - "tinymce.core.util.Arr", - "tinymce.core.undo.Fragments" - ], - function(Arr, Fragments) { - var hasIframes = function(html) { - return html.indexOf('</iframe>') !== -1; - }; - - var createFragmentedLevel = function(fragments) { - return { - type: 'fragmented', - fragments: fragments, - content: '', - bookmark: null, - beforeBookmark: null - }; - }; - - var createCompleteLevel = function(content) { - return { - type: 'complete', - fragments: null, - content: content, - bookmark: null, - beforeBookmark: null - }; - }; - - var createFromEditor = function(editor) { - var fragments, content, trimmedFragments; - - fragments = Fragments.read(editor.getBody()); - trimmedFragments = Arr.map(fragments, function(html) { - return editor.serializer.trimContent(html); - }); - content = trimmedFragments.join(''); - - return hasIframes(content) ? createFragmentedLevel(trimmedFragments) : createCompleteLevel(content); - }; - - var applyToEditor = function(editor, level, before) { - if (level.type === 'fragmented') { - Fragments.write(level.fragments, editor.getBody()); - } else { - editor.setContent(level.content, { - format: 'raw' - }); - } - - editor.selection.moveToBookmark(before ? level.beforeBookmark : level.bookmark); - }; - - var getLevelContent = function(level) { - return level.type === 'fragmented' ? level.fragments.join('') : level.content; - }; - - var isEq = function(level1, level2) { - return getLevelContent(level1) === getLevelContent(level2); - }; - - return { - createFragmentedLevel: createFragmentedLevel, - createCompleteLevel: createCompleteLevel, - createFromEditor: createFromEditor, - applyToEditor: applyToEditor, - isEq: isEq - }; - } - ); - /** - * UndoManager.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class handles the undo/redo history levels for the editor. Since the built-in undo/redo has major drawbacks a custom one was needed. - * - * @class tinymce.UndoManager - */ - define( - 'tinymce.core.UndoManager', [ - "tinymce.core.util.VK", - "tinymce.core.util.Tools", - "tinymce.core.undo.Levels" - ], - function(VK, Tools, Levels) { - return function(editor) { - var self = this, - index = 0, - data = [], - beforeBookmark, isFirstTypedCharacter, locks = 0; - - var isUnlocked = function() { - return locks === 0; - }; - - var setTyping = function(typing) { - if (isUnlocked()) { - self.typing = typing; - } - }; - - function setDirty(state) { - editor.setDirty(state); - } - - function addNonTypingUndoLevel(e) { - setTyping(false); - self.add({}, e); - } - - function endTyping() { - if (self.typing) { - setTyping(false); - self.add(); - } - } - - // Add initial undo level when the editor is initialized - editor.on('init', function() { - self.add(); - }); - - // Get position before an execCommand is processed - editor.on('BeforeExecCommand', function(e) { - var cmd = e.command; - - if (cmd !== 'Undo' && cmd !== 'Redo' && cmd !== 'mceRepaint') { - endTyping(); - self.beforeChange(); - } - }); - - // Add undo level after an execCommand call was made - editor.on('ExecCommand', function(e) { - var cmd = e.command; - - if (cmd !== 'Undo' && cmd !== 'Redo' && cmd !== 'mceRepaint') { - addNonTypingUndoLevel(e); - } - }); - - editor.on('ObjectResizeStart Cut', function() { - self.beforeChange(); - }); - - editor.on('SaveContent ObjectResized blur', addNonTypingUndoLevel); - editor.on('DragEnd', addNonTypingUndoLevel); - - editor.on('KeyUp', function(e) { - var keyCode = e.keyCode; - - // If key is prevented then don't add undo level - // This would happen on keyboard shortcuts for example - if (e.isDefaultPrevented()) { - return; - } - - if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode === 45 || e.ctrlKey) { - addNonTypingUndoLevel(); - editor.nodeChanged(); - } - - if (keyCode === 46 || keyCode === 8) { - editor.nodeChanged(); - } - - // Fire a TypingUndo/Change event on the first character entered - if (isFirstTypedCharacter && self.typing && Levels.isEq(Levels.createFromEditor(editor), data[0]) === false) { - if (editor.isDirty() === false) { - setDirty(true); - editor.fire('change', { - level: data[0], - lastLevel: null - }); - } - - editor.fire('TypingUndo'); - isFirstTypedCharacter = false; - editor.nodeChanged(); - } - }); - - editor.on('KeyDown', function(e) { - var keyCode = e.keyCode; - - // If key is prevented then don't add undo level - // This would happen on keyboard shortcuts for example - if (e.isDefaultPrevented()) { - return; - } - - // Is character position keys left,right,up,down,home,end,pgdown,pgup,enter - if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode === 45) { - if (self.typing) { - addNonTypingUndoLevel(e); - } - - return; - } - - // If key isn't Ctrl+Alt/AltGr - var modKey = (e.ctrlKey && !e.altKey) || e.metaKey; - if ((keyCode < 16 || keyCode > 20) && keyCode !== 224 && keyCode !== 91 && !self.typing && !modKey) { - self.beforeChange(); - setTyping(true); - self.add({}, e); - isFirstTypedCharacter = true; - } - }); - - editor.on('MouseDown', function(e) { - if (self.typing) { - addNonTypingUndoLevel(e); - } - }); - - // Add keyboard shortcuts for undo/redo keys - editor.addShortcut('meta+z', '', 'Undo'); - editor.addShortcut('meta+y,meta+shift+z', '', 'Redo'); - - editor.on('AddUndo Undo Redo ClearUndos', function(e) { - if (!e.isDefaultPrevented()) { - editor.nodeChanged(); - } - }); - - /*eslint consistent-this:0 */ - self = { - // Explode for debugging reasons - data: data, - - /** - * State if the user is currently typing or not. This will add a typing operation into one undo - * level instead of one new level for each keystroke. - * - * @field {Boolean} typing - */ - typing: false, - - /** - * Stores away a bookmark to be used when performing an undo action so that the selection is before - * the change has been made. - * - * @method beforeChange - */ - beforeChange: function() { - if (isUnlocked()) { - beforeBookmark = editor.selection.getBookmark(2, true); - } - }, - - /** - * Adds a new undo level/snapshot to the undo list. - * - * @method add - * @param {Object} level Optional undo level object to add. - * @param {DOMEvent} event Optional event responsible for the creation of the undo level. - * @return {Object} Undo level that got added or null it a level wasn't needed. - */ - add: function(level, event) { - var i, settings = editor.settings, - lastLevel, currentLevel; - - currentLevel = Levels.createFromEditor(editor); - level = level || {}; - level = Tools.extend(level, currentLevel); - - if (isUnlocked() === false || editor.removed) { - return null; - } - - lastLevel = data[index]; - if (editor.fire('BeforeAddUndo', { - level: level, - lastLevel: lastLevel, - originalEvent: event - }).isDefaultPrevented()) { - return null; - } - - // Add undo level if needed - if (lastLevel && Levels.isEq(lastLevel, level)) { - return null; - } - - // Set before bookmark on previous level - if (data[index]) { - data[index].beforeBookmark = beforeBookmark; - } - - // Time to compress - if (settings.custom_undo_redo_levels) { - if (data.length > settings.custom_undo_redo_levels) { - for (i = 0; i < data.length - 1; i++) { - data[i] = data[i + 1]; - } - - data.length--; - index = data.length; - } - } - - // Get a non intrusive normalized bookmark - level.bookmark = editor.selection.getBookmark(2, true); - - // Crop array if needed - if (index < data.length - 1) { - data.length = index + 1; - } - - data.push(level); - index = data.length - 1; - - var args = { - level: level, - lastLevel: lastLevel, - originalEvent: event - }; - - editor.fire('AddUndo', args); - - if (index > 0) { - setDirty(true); - editor.fire('change', args); - } - - return level; - }, - - /** - * Undoes the last action. - * - * @method undo - * @return {Object} Undo level or null if no undo was performed. - */ - undo: function() { - var level; - - if (self.typing) { - self.add(); - self.typing = false; - setTyping(false); - } - - if (index > 0) { - level = data[--index]; - Levels.applyToEditor(editor, level, true); - setDirty(true); - editor.fire('undo', { - level: level - }); - } - - return level; - }, - - /** - * Redoes the last action. - * - * @method redo - * @return {Object} Redo level or null if no redo was performed. - */ - redo: function() { - var level; - - if (index < data.length - 1) { - level = data[++index]; - Levels.applyToEditor(editor, level, false); - setDirty(true); - editor.fire('redo', { - level: level - }); - } - - return level; - }, - - /** - * Removes all undo levels. - * - * @method clear - */ - clear: function() { - data = []; - index = 0; - self.typing = false; - self.data = data; - editor.fire('ClearUndos'); - }, - - /** - * Returns true/false if the undo manager has any undo levels. - * - * @method hasUndo - * @return {Boolean} true/false if the undo manager has any undo levels. - */ - hasUndo: function() { - // Has undo levels or typing and content isn't the same as the initial level - return index > 0 || (self.typing && data[0] && !Levels.isEq(Levels.createFromEditor(editor), data[0])); - }, - - /** - * Returns true/false if the undo manager has any redo levels. - * - * @method hasRedo - * @return {Boolean} true/false if the undo manager has any redo levels. - */ - hasRedo: function() { - return index < data.length - 1 && !self.typing; - }, - - /** - * Executes the specified mutator function as an undo transaction. The selection - * before the modification will be stored to the undo stack and if the DOM changes - * it will add a new undo level. Any logic within the translation that adds undo levels will - * be ignored. So a translation can include calls to execCommand or editor.insertContent. - * - * @method transact - * @param {function} callback Function that gets executed and has dom manipulation logic in it. - * @return {Object} Undo level that got added or null it a level wasn't needed. - */ - transact: function(callback) { - endTyping(); - self.beforeChange(); - self.ignore(callback); - return self.add(); - }, - - /** - * Executes the specified mutator function as an undo transaction. But without adding an undo level. - * Any logic within the translation that adds undo levels will be ignored. So a translation can - * include calls to execCommand or editor.insertContent. - * - * @method ignore - * @param {function} callback Function that gets executed and has dom manipulation logic in it. - * @return {Object} Undo level that got added or null it a level wasn't needed. - */ - ignore: function(callback) { - try { - locks++; - callback(); - } finally { - locks--; - } - }, - - /** - * Adds an extra "hidden" undo level by first applying the first mutation and store that to the undo stack - * then roll back that change and do the second mutation on top of the stack. This will produce an extra - * undo level that the user doesn't see until they undo. - * - * @method extra - * @param {function} callback1 Function that does mutation but gets stored as a "hidden" extra undo level. - * @param {function} callback2 Function that does mutation but gets displayed to the user. - */ - extra: function(callback1, callback2) { - var lastLevel, bookmark; - - if (self.transact(callback1)) { - bookmark = data[index].bookmark; - lastLevel = data[index - 1]; - Levels.applyToEditor(editor, lastLevel, true); - - if (self.transact(callback2)) { - data[index - 1].beforeBookmark = bookmark; - } - } - } - }; - - return self; - }; - } - ); - - define( - 'ephox.sugar.api.node.Body', - - [ - 'ephox.katamari.api.Thunk', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.api.node.Node', - 'global!document' - ], - - function(Thunk, Element, Node, document) { - - // Node.contains() is very, very, very good performance - // http://jsperf.com/closest-vs-contains/5 - var inBody = function(element) { - // Technically this is only required on IE, where contains() returns false for text nodes. - // But it's cheap enough to run everywhere and Sugar doesn't have platform detection (yet). - var dom = Node.isText(element) ? element.dom().parentNode : element.dom(); - - // use ownerDocument.body to ensure this works inside iframes. - // Normally contains is bad because an element "contains" itself, but here we want that. - return dom !== undefined && dom !== null && dom.ownerDocument.body.contains(dom); - }; - - var body = Thunk.cached(function() { - return getBody(Element.fromDom(document)); - }); - - var getBody = function(doc) { - var body = doc.dom().body; - if (body === null || body === undefined) throw 'Body is not available yet'; - return Element.fromDom(body); - }; - - return { - body: body, - getBody: getBody, - inBody: inBody - }; - } - ); - - define( - 'ephox.sugar.impl.ClosestOrAncestor', - - [ - 'ephox.katamari.api.Type', - 'ephox.katamari.api.Option' - ], - - function(Type, Option) { - return function(is, ancestor, scope, a, isRoot) { - return is(scope, a) ? - Option.some(scope) : - Type.isFunction(isRoot) && isRoot(scope) ? - Option.none() : - ancestor(scope, a, isRoot); - }; - } - ); - define( - 'ephox.sugar.api.search.PredicateFind', - - [ - 'ephox.katamari.api.Type', - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Option', - 'ephox.sugar.api.node.Body', - 'ephox.sugar.api.dom.Compare', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.impl.ClosestOrAncestor' - ], - - function(Type, Arr, Fun, Option, Body, Compare, Element, ClosestOrAncestor) { - var first = function(predicate) { - return descendant(Body.body(), predicate); - }; - - var ancestor = function(scope, predicate, isRoot) { - var element = scope.dom(); - var stop = Type.isFunction(isRoot) ? isRoot : Fun.constant(false); - - while (element.parentNode) { - element = element.parentNode; - var el = Element.fromDom(element); - - if (predicate(el)) return Option.some(el); - else if (stop(el)) break; - } - return Option.none(); - }; - - var closest = function(scope, predicate, isRoot) { - // This is required to avoid ClosestOrAncestor passing the predicate to itself - var is = function(scope) { - return predicate(scope); - }; - return ClosestOrAncestor(is, ancestor, scope, predicate, isRoot); - }; - - var sibling = function(scope, predicate) { - var element = scope.dom(); - if (!element.parentNode) return Option.none(); - - return child(Element.fromDom(element.parentNode), function(x) { - return !Compare.eq(scope, x) && predicate(x); - }); - }; - - var child = function(scope, predicate) { - var result = Arr.find(scope.dom().childNodes, - Fun.compose(predicate, Element.fromDom)); - return result.map(Element.fromDom); - }; - - var descendant = function(scope, predicate) { - var descend = function(element) { - for (var i = 0; i < element.childNodes.length; i++) { - if (predicate(Element.fromDom(element.childNodes[i]))) - return Option.some(Element.fromDom(element.childNodes[i])); - - var res = descend(element.childNodes[i]); - if (res.isSome()) - return res; - } - - return Option.none(); - }; - - return descend(scope.dom()); - }; - - return { - first: first, - ancestor: ancestor, - closest: closest, - sibling: sibling, - child: child, - descendant: descendant - }; - } - ); - - /** - * CaretUtils.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Utility functions shared by the caret logic. - * - * @private - * @class tinymce.caret.CaretUtils - */ - define( - 'tinymce.core.caret.CaretUtils', [ - "tinymce.core.util.Fun", - "tinymce.core.dom.TreeWalker", - "tinymce.core.dom.NodeType", - "tinymce.core.caret.CaretPosition", - "tinymce.core.caret.CaretContainer", - "tinymce.core.caret.CaretCandidate" - ], - function(Fun, TreeWalker, NodeType, CaretPosition, CaretContainer, CaretCandidate) { - var isContentEditableTrue = NodeType.isContentEditableTrue, - isContentEditableFalse = NodeType.isContentEditableFalse, - isBlockLike = NodeType.matchStyleValues('display', 'block table table-cell table-caption list-item'), - isCaretContainer = CaretContainer.isCaretContainer, - isCaretContainerBlock = CaretContainer.isCaretContainerBlock, - curry = Fun.curry, - isElement = NodeType.isElement, - isCaretCandidate = CaretCandidate.isCaretCandidate; - - function isForwards(direction) { - return direction > 0; - } - - function isBackwards(direction) { - return direction < 0; - } - - function skipCaretContainers(walk, shallow) { - var node; - - while ((node = walk(shallow))) { - if (!isCaretContainerBlock(node)) { - return node; - } - } - - return null; - } - - function findNode(node, direction, predicateFn, rootNode, shallow) { - var walker = new TreeWalker(node, rootNode); - - if (isBackwards(direction)) { - if (isContentEditableFalse(node) || isCaretContainerBlock(node)) { - node = skipCaretContainers(walker.prev, true); - if (predicateFn(node)) { - return node; - } - } - - while ((node = skipCaretContainers(walker.prev, shallow))) { - if (predicateFn(node)) { - return node; - } - } - } - - if (isForwards(direction)) { - if (isContentEditableFalse(node) || isCaretContainerBlock(node)) { - node = skipCaretContainers(walker.next, true); - if (predicateFn(node)) { - return node; - } - } - - while ((node = skipCaretContainers(walker.next, shallow))) { - if (predicateFn(node)) { - return node; - } - } - } - - return null; - } - - function getEditingHost(node, rootNode) { - for (node = node.parentNode; node && node != rootNode; node = node.parentNode) { - if (isContentEditableTrue(node)) { - return node; - } - } - - return rootNode; - } - - function getParentBlock(node, rootNode) { - while (node && node != rootNode) { - if (isBlockLike(node)) { - return node; - } - - node = node.parentNode; - } - - return null; - } - - function isInSameBlock(caretPosition1, caretPosition2, rootNode) { - return getParentBlock(caretPosition1.container(), rootNode) == getParentBlock(caretPosition2.container(), rootNode); - } - - function isInSameEditingHost(caretPosition1, caretPosition2, rootNode) { - return getEditingHost(caretPosition1.container(), rootNode) == getEditingHost(caretPosition2.container(), rootNode); - } - - function getChildNodeAtRelativeOffset(relativeOffset, caretPosition) { - var container, offset; - - if (!caretPosition) { - return null; - } - - container = caretPosition.container(); - offset = caretPosition.offset(); - - if (!isElement(container)) { - return null; - } - - return container.childNodes[offset + relativeOffset]; - } - - function beforeAfter(before, node) { - var range = node.ownerDocument.createRange(); - - if (before) { - range.setStartBefore(node); - range.setEndBefore(node); - } else { - range.setStartAfter(node); - range.setEndAfter(node); - } - - return range; - } - - function isNodesInSameBlock(rootNode, node1, node2) { - return getParentBlock(node1, rootNode) == getParentBlock(node2, rootNode); - } - - function lean(left, rootNode, node) { - var sibling, siblingName; - - if (left) { - siblingName = 'previousSibling'; - } else { - siblingName = 'nextSibling'; - } - - while (node && node != rootNode) { - sibling = node[siblingName]; - - if (isCaretContainer(sibling)) { - sibling = sibling[siblingName]; - } - - if (isContentEditableFalse(sibling)) { - if (isNodesInSameBlock(rootNode, sibling, node)) { - return sibling; - } - - break; - } - - if (isCaretCandidate(sibling)) { - break; - } - - node = node.parentNode; - } - - return null; - } - - var before = curry(beforeAfter, true); - var after = curry(beforeAfter, false); - - function normalizeRange(direction, rootNode, range) { - var node, container, offset, location; - var leanLeft = curry(lean, true, rootNode); - var leanRight = curry(lean, false, rootNode); - - container = range.startContainer; - offset = range.startOffset; - - if (CaretContainer.isCaretContainerBlock(container)) { - if (!isElement(container)) { - container = container.parentNode; - } - - location = container.getAttribute('data-mce-caret'); - - if (location == 'before') { - node = container.nextSibling; - if (isContentEditableFalse(node)) { - return before(node); - } - } - - if (location == 'after') { - node = container.previousSibling; - if (isContentEditableFalse(node)) { - return after(node); - } - } - } - - if (!range.collapsed) { - return range; - } - - if (NodeType.isText(container)) { - if (isCaretContainer(container)) { - if (direction === 1) { - node = leanRight(container); - if (node) { - return before(node); - } - - node = leanLeft(container); - if (node) { - return after(node); - } - } - - if (direction === -1) { - node = leanLeft(container); - if (node) { - return after(node); - } - - node = leanRight(container); - if (node) { - return before(node); - } - } - - return range; - } - - if (CaretContainer.endsWithCaretContainer(container) && offset >= container.data.length - 1) { - if (direction === 1) { - node = leanRight(container); - if (node) { - return before(node); - } - } - - return range; - } - - if (CaretContainer.startsWithCaretContainer(container) && offset <= 1) { - if (direction === -1) { - node = leanLeft(container); - if (node) { - return after(node); - } - } - - return range; - } - - if (offset === container.data.length) { - node = leanRight(container); - if (node) { - return before(node); - } - - return range; - } - - if (offset === 0) { - node = leanLeft(container); - if (node) { - return after(node); - } - - return range; - } - } - - return range; - } - - function isNextToContentEditableFalse(relativeOffset, caretPosition) { - return isContentEditableFalse(getChildNodeAtRelativeOffset(relativeOffset, caretPosition)); - } - - return { - isForwards: isForwards, - isBackwards: isBackwards, - findNode: findNode, - getEditingHost: getEditingHost, - getParentBlock: getParentBlock, - isInSameBlock: isInSameBlock, - isInSameEditingHost: isInSameEditingHost, - isBeforeContentEditableFalse: curry(isNextToContentEditableFalse, 0), - isAfterContentEditableFalse: curry(isNextToContentEditableFalse, -1), - normalizeRange: normalizeRange - }; - } - ); - - /** - * CaretWalker.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module contains logic for moving around a virtual caret in logical order within a DOM element. - * - * It ignores the most obvious invalid caret locations such as within a script element or within a - * contentEditable=false element but it will return locations that isn't possible to render visually. - * - * @private - * @class tinymce.caret.CaretWalker - * @example - * var caretWalker = new CaretWalker(rootElm); - * - * var prevLogicalCaretPosition = caretWalker.prev(CaretPosition.fromRangeStart(range)); - * var nextLogicalCaretPosition = caretWalker.next(CaretPosition.fromRangeEnd(range)); - */ - define( - 'tinymce.core.caret.CaretWalker', [ - "tinymce.core.dom.NodeType", - "tinymce.core.caret.CaretCandidate", - "tinymce.core.caret.CaretPosition", - "tinymce.core.caret.CaretUtils", - "tinymce.core.util.Arr", - "tinymce.core.util.Fun" - ], - function(NodeType, CaretCandidate, CaretPosition, CaretUtils, Arr, Fun) { - var isContentEditableFalse = NodeType.isContentEditableFalse, - isText = NodeType.isText, - isElement = NodeType.isElement, - isBr = NodeType.isBr, - isForwards = CaretUtils.isForwards, - isBackwards = CaretUtils.isBackwards, - isCaretCandidate = CaretCandidate.isCaretCandidate, - isAtomic = CaretCandidate.isAtomic, - isEditableCaretCandidate = CaretCandidate.isEditableCaretCandidate; - - function getParents(node, rootNode) { - var parents = []; - - while (node && node != rootNode) { - parents.push(node); - node = node.parentNode; - } - - return parents; - } - - function nodeAtIndex(container, offset) { - if (container.hasChildNodes() && offset < container.childNodes.length) { - return container.childNodes[offset]; - } - - return null; - } - - function getCaretCandidatePosition(direction, node) { - if (isForwards(direction)) { - if (isCaretCandidate(node.previousSibling) && !isText(node.previousSibling)) { - return CaretPosition.before(node); - } - - if (isText(node)) { - return CaretPosition(node, 0); - } - } - - if (isBackwards(direction)) { - if (isCaretCandidate(node.nextSibling) && !isText(node.nextSibling)) { - return CaretPosition.after(node); - } - - if (isText(node)) { - return CaretPosition(node, node.data.length); - } - } - - if (isBackwards(direction)) { - if (isBr(node)) { - return CaretPosition.before(node); - } - - return CaretPosition.after(node); - } - - return CaretPosition.before(node); - } - - // Jumps over BR elements <p>|<br></p><p>a</p> -> <p><br></p><p>|a</p> - function isBrBeforeBlock(node, rootNode) { - var next; - - if (!NodeType.isBr(node)) { - return false; - } - - next = findCaretPosition(1, CaretPosition.after(node), rootNode); - if (!next) { - return false; - } - - return !CaretUtils.isInSameBlock(CaretPosition.before(node), CaretPosition.before(next), rootNode); - } - - function findCaretPosition(direction, startCaretPosition, rootNode) { - var container, offset, node, nextNode, innerNode, - rootContentEditableFalseElm, caretPosition; - - if (!isElement(rootNode) || !startCaretPosition) { - return null; - } - - if (startCaretPosition.isEqual(CaretPosition.after(rootNode)) && rootNode.lastChild) { - caretPosition = CaretPosition.after(rootNode.lastChild); - if (isBackwards(direction) && isCaretCandidate(rootNode.lastChild) && isElement(rootNode.lastChild)) { - return isBr(rootNode.lastChild) ? CaretPosition.before(rootNode.lastChild) : caretPosition; - } - } else { - caretPosition = startCaretPosition; - } - - container = caretPosition.container(); - offset = caretPosition.offset(); - - if (isText(container)) { - if (isBackwards(direction) && offset > 0) { - return CaretPosition(container, --offset); - } - - if (isForwards(direction) && offset < container.length) { - return CaretPosition(container, ++offset); - } - - node = container; - } else { - if (isBackwards(direction) && offset > 0) { - nextNode = nodeAtIndex(container, offset - 1); - if (isCaretCandidate(nextNode)) { - if (!isAtomic(nextNode)) { - innerNode = CaretUtils.findNode(nextNode, direction, isEditableCaretCandidate, nextNode); - if (innerNode) { - if (isText(innerNode)) { - return CaretPosition(innerNode, innerNode.data.length); - } - - return CaretPosition.after(innerNode); - } - } - - if (isText(nextNode)) { - return CaretPosition(nextNode, nextNode.data.length); - } - - return CaretPosition.before(nextNode); - } - } - - if (isForwards(direction) && offset < container.childNodes.length) { - nextNode = nodeAtIndex(container, offset); - if (isCaretCandidate(nextNode)) { - if (isBrBeforeBlock(nextNode, rootNode)) { - return findCaretPosition(direction, CaretPosition.after(nextNode), rootNode); - } - - if (!isAtomic(nextNode)) { - innerNode = CaretUtils.findNode(nextNode, direction, isEditableCaretCandidate, nextNode); - if (innerNode) { - if (isText(innerNode)) { - return CaretPosition(innerNode, 0); - } - - return CaretPosition.before(innerNode); - } - } - - if (isText(nextNode)) { - return CaretPosition(nextNode, 0); - } - - return CaretPosition.after(nextNode); - } - } - - node = caretPosition.getNode(); - } - - if ((isForwards(direction) && caretPosition.isAtEnd()) || (isBackwards(direction) && caretPosition.isAtStart())) { - node = CaretUtils.findNode(node, direction, Fun.constant(true), rootNode, true); - if (isEditableCaretCandidate(node)) { - return getCaretCandidatePosition(direction, node); - } - } - - nextNode = CaretUtils.findNode(node, direction, isEditableCaretCandidate, rootNode); - - rootContentEditableFalseElm = Arr.last(Arr.filter(getParents(container, rootNode), isContentEditableFalse)); - if (rootContentEditableFalseElm && (!nextNode || !rootContentEditableFalseElm.contains(nextNode))) { - if (isForwards(direction)) { - caretPosition = CaretPosition.after(rootContentEditableFalseElm); - } else { - caretPosition = CaretPosition.before(rootContentEditableFalseElm); - } - - return caretPosition; - } - - if (nextNode) { - return getCaretCandidatePosition(direction, nextNode); - } - - return null; - } - - return function(rootNode) { - return { - /** - * Returns the next logical caret position from the specificed input - * caretPoisiton or null if there isn't any more positions left for example - * at the end specified root element. - * - * @method next - * @param {tinymce.caret.CaretPosition} caretPosition Caret position to start from. - * @return {tinymce.caret.CaretPosition} CaretPosition or null if no position was found. - */ - next: function(caretPosition) { - return findCaretPosition(1, caretPosition, rootNode); - }, - - /** - * Returns the previous logical caret position from the specificed input - * caretPoisiton or null if there isn't any more positions left for example - * at the end specified root element. - * - * @method prev - * @param {tinymce.caret.CaretPosition} caretPosition Caret position to start from. - * @return {tinymce.caret.CaretPosition} CaretPosition or null if no position was found. - */ - prev: function(caretPosition) { - return findCaretPosition(-1, caretPosition, rootNode); - } - }; - }; - } - ); - /** - * CaretFinder.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.caret.CaretFinder', [ - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Option', - 'tinymce.core.caret.CaretCandidate', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.caret.CaretUtils', - 'tinymce.core.caret.CaretWalker', - 'tinymce.core.dom.NodeType' - ], - function(Fun, Option, CaretCandidate, CaretPosition, CaretUtils, CaretWalker, NodeType) { - var walkToPositionIn = function(forward, rootNode, startNode) { - var position = forward ? CaretPosition.before(startNode) : CaretPosition.after(startNode); - return fromPosition(forward, rootNode, position); - }; - - var afterElement = function(node) { - return NodeType.isBr(node) ? CaretPosition.before(node) : CaretPosition.after(node); - }; - - var isBeforeOrStart = function(position) { - if (CaretPosition.isTextPosition(position)) { - return position.offset() === 0; - } else { - return CaretCandidate.isCaretCandidate(position.getNode()); - } - }; - - var isAfterOrEnd = function(position) { - if (CaretPosition.isTextPosition(position)) { - return position.offset() === position.container().data.length; - } else { - return CaretCandidate.isCaretCandidate(position.getNode(true)); - } - }; - - var isBeforeAfterSameElement = function(from, to) { - return !CaretPosition.isTextPosition(from) && !CaretPosition.isTextPosition(to) && from.getNode() === to.getNode(true); - }; - - var isAtBr = function(position) { - return !CaretPosition.isTextPosition(position) && NodeType.isBr(position.getNode()); - }; - - var shouldSkipPosition = function(forward, from, to) { - if (forward) { - return !isBeforeAfterSameElement(from, to) && !isAtBr(from) && isAfterOrEnd(from) && isBeforeOrStart(to); - } else { - return !isBeforeAfterSameElement(to, from) && isBeforeOrStart(from) && isAfterOrEnd(to); - } - }; - - // Finds: <p>a|<b>b</b></p> -> <p>a<b>|b</b></p> - var fromPosition = function(forward, rootNode, position) { - var walker = new CaretWalker(rootNode); - return Option.from(forward ? walker.next(position) : walker.prev(position)); - }; - - // Finds: <p>a|<b>b</b></p> -> <p>a<b>b|</b></p> - var navigate = function(forward, rootNode, from) { - return fromPosition(forward, rootNode, from).bind(function(to) { - if (CaretUtils.isInSameBlock(from, to, rootNode) && shouldSkipPosition(forward, from, to)) { - return fromPosition(forward, rootNode, to); - } else { - return Option.some(to); - } - }); - }; - - var positionIn = function(forward, element) { - var startNode = forward ? element.firstChild : element.lastChild; - if (NodeType.isText(startNode)) { - return Option.some(new CaretPosition(startNode, forward ? 0 : startNode.data.length)); - } else if (startNode) { - if (CaretCandidate.isCaretCandidate(startNode)) { - return Option.some(forward ? CaretPosition.before(startNode) : afterElement(startNode)); - } else { - return walkToPositionIn(forward, element, startNode); - } - } else { - return Option.none(); - } - }; - - return { - fromPosition: fromPosition, - navigate: navigate, - positionIn: positionIn - }; - } - ); - - /** - * DeleteUtils.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.delete.DeleteUtils', [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Option', - 'ephox.sugar.api.dom.Compare', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.api.node.Node', - 'ephox.sugar.api.search.PredicateFind' - ], - function(Arr, Option, Compare, Element, Node, PredicateFind) { - var toLookup = function(names) { - var lookup = Arr.foldl(names, function(acc, name) { - acc[name] = true; - return acc; - }, {}); - - return function(elm) { - return lookup[Node.name(elm)] === true; - }; - }; - - var isTextBlock = toLookup([ - 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'address', 'pre', 'form', 'blockquote', 'center', - 'dir', 'fieldset', 'header', 'footer', 'article', 'section', 'hgroup', 'aside', 'nav', 'figure' - ]); - - var isBeforeRoot = function(rootNode) { - return function(elm) { - return Compare.eq(rootNode, Element.fromDom(elm.dom().parentNode)); - }; - }; - - var getParentTextBlock = function(rootNode, elm) { - return Compare.contains(rootNode, elm) ? PredicateFind.closest(elm, isTextBlock, isBeforeRoot(rootNode)) : Option.none(); - }; - - return { - getParentTextBlock: getParentTextBlock - }; - } - ); - - define( - 'ephox.sugar.api.search.SelectorFind', - - [ - 'ephox.sugar.api.search.PredicateFind', - 'ephox.sugar.api.search.Selectors', - 'ephox.sugar.impl.ClosestOrAncestor' - ], - - function(PredicateFind, Selectors, ClosestOrAncestor) { - // TODO: An internal SelectorFilter module that doesn't Element.fromDom() everything - - var first = function(selector) { - return Selectors.one(selector); - }; - - var ancestor = function(scope, selector, isRoot) { - return PredicateFind.ancestor(scope, function(e) { - return Selectors.is(e, selector); - }, isRoot); - }; - - var sibling = function(scope, selector) { - return PredicateFind.sibling(scope, function(e) { - return Selectors.is(e, selector); - }); - }; - - var child = function(scope, selector) { - return PredicateFind.child(scope, function(e) { - return Selectors.is(e, selector); - }); - }; - - var descendant = function(scope, selector) { - return Selectors.one(selector, scope); - }; - - // Returns Some(closest ancestor element (sugared)) matching 'selector' up to isRoot, or None() otherwise - var closest = function(scope, selector, isRoot) { - return ClosestOrAncestor(Selectors.is, ancestor, scope, selector, isRoot); - }; - - return { - first: first, - ancestor: ancestor, - sibling: sibling, - child: child, - descendant: descendant, - closest: closest - }; - } - ); - - define( - 'ephox.sugar.api.search.SelectorExists', - - [ - 'ephox.sugar.api.search.SelectorFind' - ], - - function(SelectorFind) { - var any = function(selector) { - return SelectorFind.first(selector).isSome(); - }; - - var ancestor = function(scope, selector, isRoot) { - return SelectorFind.ancestor(scope, selector, isRoot).isSome(); - }; - - var sibling = function(scope, selector) { - return SelectorFind.sibling(scope, selector).isSome(); - }; - - var child = function(scope, selector) { - return SelectorFind.child(scope, selector).isSome(); - }; - - var descendant = function(scope, selector) { - return SelectorFind.descendant(scope, selector).isSome(); - }; - - var closest = function(scope, selector, isRoot) { - return SelectorFind.closest(scope, selector, isRoot).isSome(); - }; - - return { - any: any, - ancestor: ancestor, - sibling: sibling, - child: child, - descendant: descendant, - closest: closest - }; - } - ); - - /** - * Empty.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.dom.Empty', [ - 'ephox.katamari.api.Fun', - 'ephox.sugar.api.dom.Compare', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.api.search.SelectorExists', - 'tinymce.core.caret.CaretCandidate', - 'tinymce.core.dom.NodeType', - 'tinymce.core.dom.TreeWalker' - ], - function(Fun, Compare, Element, SelectorExists, CaretCandidate, NodeType, TreeWalker) { - var hasWhitespacePreserveParent = function(rootNode, node) { - var rootElement = Element.fromDom(rootNode); - var startNode = Element.fromDom(node); - return SelectorExists.ancestor(startNode, 'pre,code', Fun.curry(Compare.eq, rootElement)); - }; - - var isWhitespace = function(rootNode, node) { - return NodeType.isText(node) && /^[ \t\r\n]*$/.test(node.data) && hasWhitespacePreserveParent(rootNode, node) === false; - }; - - var isNamedAnchor = function(node) { - return NodeType.isElement(node) && node.nodeName === 'A' && node.hasAttribute('name'); - }; - - var isContent = function(rootNode, node) { - return (CaretCandidate.isCaretCandidate(node) && isWhitespace(rootNode, node) === false) || isNamedAnchor(node) || isBookmark(node); - }; - - var isBookmark = NodeType.hasAttribute('data-mce-bookmark'); - var isBogus = NodeType.hasAttribute('data-mce-bogus'); - var isBogusAll = NodeType.hasAttributeValue('data-mce-bogus', 'all'); - - var isEmptyNode = function(targetNode) { - var walker, node, brCount = 0; - - if (isContent(targetNode, targetNode)) { - return false; - } else { - node = targetNode.firstChild; - if (!node) { - return true; - } - - walker = new TreeWalker(node, targetNode); - do { - if (isBogusAll(node)) { - node = walker.next(true); - continue; - } - - if (isBogus(node)) { - node = walker.next(); - continue; - } - - if (NodeType.isBr(node)) { - brCount++; - node = walker.next(); - continue; - } - - if (isContent(targetNode, node)) { - return false; - } - - node = walker.next(); - } while (node); - - return brCount <= 1; - } - }; - - var isEmpty = function(elm) { - return isEmptyNode(elm.dom()); - }; - - return { - isEmpty: isEmpty - }; - } - ); - - /** - * BlockBoundary.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.delete.BlockBoundary', [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Option', - 'ephox.katamari.api.Options', - 'ephox.katamari.api.Struct', - 'ephox.sugar.api.dom.Compare', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.api.node.Node', - 'ephox.sugar.api.search.PredicateFind', - 'ephox.sugar.api.search.Traverse', - 'tinymce.core.caret.CaretFinder', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.delete.DeleteUtils', - 'tinymce.core.dom.Empty', - 'tinymce.core.dom.NodeType' - ], - function(Arr, Fun, Option, Options, Struct, Compare, Element, Node, PredicateFind, Traverse, CaretFinder, CaretPosition, DeleteUtils, Empty, NodeType) { - var BlockPosition = Struct.immutable('block', 'position'); - var BlockBoundary = Struct.immutable('from', 'to'); - - var getBlockPosition = function(rootNode, pos) { - var rootElm = Element.fromDom(rootNode); - var containerElm = Element.fromDom(pos.container()); - return DeleteUtils.getParentTextBlock(rootElm, containerElm).map(function(block) { - return BlockPosition(block, pos); - }); - }; - - var isDifferentBlocks = function(blockBoundary) { - return Compare.eq(blockBoundary.from().block(), blockBoundary.to().block()) === false; - }; - - var hasSameParent = function(blockBoundary) { - return Traverse.parent(blockBoundary.from().block()).bind(function(parent1) { - return Traverse.parent(blockBoundary.to().block()).filter(function(parent2) { - return Compare.eq(parent1, parent2); - }); - }).isSome(); - }; - - var isEditable = function(blockBoundary) { - return NodeType.isContentEditableFalse(blockBoundary.from().block()) === false && NodeType.isContentEditableFalse(blockBoundary.to().block()) === false; - }; - - var skipLastBr = function(rootNode, forward, blockPosition) { - if (NodeType.isBr(blockPosition.position().getNode()) && Empty.isEmpty(blockPosition.block()) === false) { - return CaretFinder.positionIn(false, blockPosition.block().dom()).bind(function(lastPositionInBlock) { - if (lastPositionInBlock.isEqual(blockPosition.position())) { - return CaretFinder.fromPosition(forward, rootNode, lastPositionInBlock).bind(function(to) { - return getBlockPosition(rootNode, to); - }); - } else { - return Option.some(blockPosition); - } - }).getOr(blockPosition); - } else { - return blockPosition; - } - }; - - var readFromRange = function(rootNode, forward, rng) { - var fromBlockPos = getBlockPosition(rootNode, CaretPosition.fromRangeStart(rng)); - var toBlockPos = fromBlockPos.bind(function(blockPos) { - return CaretFinder.fromPosition(forward, rootNode, blockPos.position()).bind(function(to) { - return getBlockPosition(rootNode, to).map(function(blockPos) { - return skipLastBr(rootNode, forward, blockPos); - }); - }); - }); - - return Options.liftN([fromBlockPos, toBlockPos], BlockBoundary).filter(function(blockBoundary) { - return isDifferentBlocks(blockBoundary) && hasSameParent(blockBoundary) && isEditable(blockBoundary); - }); - }; - - var read = function(rootNode, forward, rng) { - return rng.collapsed ? readFromRange(rootNode, forward, rng) : Option.none(); - }; - - return { - read: read - }; - } - ); - - /** - * MergeBlocks.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.delete.MergeBlocks', [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Option', - 'ephox.sugar.api.dom.Insert', - 'ephox.sugar.api.dom.Remove', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.api.search.Traverse', - 'tinymce.core.caret.CaretFinder', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.dom.Empty', - 'tinymce.core.dom.NodeType' - ], - function(Arr, Option, Insert, Remove, Element, Traverse, CaretFinder, CaretPosition, Empty, NodeType) { - var mergeBlocksAndReposition = function(forward, fromBlock, toBlock, toPosition) { - var children = Traverse.children(fromBlock); - - if (NodeType.isBr(toPosition.getNode())) { - Remove.remove(Element.fromDom(toPosition.getNode())); - toPosition = CaretFinder.positionIn(false, toBlock.dom()).getOr(toPosition); - } - - if (Empty.isEmpty(fromBlock) === false) { - Arr.each(children, function(node) { - Insert.append(toBlock, node); - }); - } - - if (Empty.isEmpty(fromBlock)) { - Remove.remove(fromBlock); - } - - return children.length > 0 ? Option.from(toPosition) : Option.none(); - }; - - var mergeBlocks = function(forward, block1, block2) { - if (forward) { - if (Empty.isEmpty(block1)) { - Remove.remove(block1); - return CaretFinder.positionIn(true, block2.dom()); - } else { - return CaretFinder.positionIn(false, block1.dom()).bind(function(toPosition) { - return mergeBlocksAndReposition(forward, block2, block1, toPosition); - }); - } - } else { - if (Empty.isEmpty(block2)) { - Remove.remove(block2); - return CaretFinder.positionIn(true, block1.dom()); - } else { - return CaretFinder.positionIn(false, block2.dom()).bind(function(toPosition) { - return mergeBlocksAndReposition(forward, block1, block2, toPosition); - }); - } - } - }; - - return { - mergeBlocks: mergeBlocks - }; - } - ); - - /** - * BlockBoundaryDelete.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.delete.BlockBoundaryDelete', [ - 'tinymce.core.delete.BlockBoundary', - 'tinymce.core.delete.MergeBlocks' - ], - function(BlockBoundary, MergeBlocks) { - var backspaceDelete = function(editor, forward) { - var position; - - position = BlockBoundary.read(editor.getBody(), forward, editor.selection.getRng()).bind(function(blockBoundary) { - return MergeBlocks.mergeBlocks(forward, blockBoundary.from().block(), blockBoundary.to().block()); - }); - - position.each(function(pos) { - editor.selection.setRng(pos.toRange()); - }); - - return position.isSome(); - }; - - return { - backspaceDelete: backspaceDelete - }; - } - ); - - /** - * BlockRangeDelete.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.delete.BlockRangeDelete', [ - 'ephox.katamari.api.Options', - 'ephox.sugar.api.dom.Compare', - 'ephox.sugar.api.node.Element', - 'tinymce.core.delete.DeleteUtils', - 'tinymce.core.delete.MergeBlocks' - ], - function(Options, Compare, Element, DeleteUtils, MergeBlocks) { - var deleteRange = function(rootNode, selection) { - var rng = selection.getRng(); - - return Options.liftN([ - DeleteUtils.getParentTextBlock(rootNode, Element.fromDom(rng.startContainer)), - DeleteUtils.getParentTextBlock(rootNode, Element.fromDom(rng.endContainer)) - ], function(block1, block2) { - if (Compare.eq(block1, block2) === false) { - rng.deleteContents(); - - MergeBlocks.mergeBlocks(true, block1, block2).each(function(pos) { - selection.setRng(pos.toRange()); - }); - - return true; - } else { - return false; - } - }).getOr(false); - }; - - var backspaceDelete = function(editor, forward) { - var rootNode = Element.fromDom(editor.getBody()); - - if (editor.selection.isCollapsed() === false) { - return deleteRange(rootNode, editor.selection); - } else { - return false; - } - }; - - return { - backspaceDelete: backspaceDelete - }; - } - ); - - define( - 'ephox.katamari.api.Adt', - - [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Obj', - 'ephox.katamari.api.Type', - 'global!Array', - 'global!Error', - 'global!console' - ], - - function(Arr, Obj, Type, Array, Error, console) { - /* - * Generates a church encoded ADT (https://en.wikipedia.org/wiki/Church_encoding) - * For syntax and use, look at the test code. - */ - var generate = function(cases) { - // validation - if (!Type.isArray(cases)) { - throw new Error('cases must be an array'); - } - if (cases.length === 0) { - throw new Error('there must be at least one case'); - } - - var constructors = []; - - // adt is mutated to add the individual cases - var adt = {}; - Arr.each(cases, function(acase, count) { - var keys = Obj.keys(acase); - - // validation - if (keys.length !== 1) { - throw new Error('one and only one name per case'); - } - - var key = keys[0]; - var value = acase[key]; - - // validation - if (adt[key] !== undefined) { - throw new Error('duplicate key detected:' + key); - } else if (key === 'cata') { - throw new Error('cannot have a case named cata (sorry)'); - } else if (!Type.isArray(value)) { - // this implicitly checks if acase is an object - throw new Error('case arguments must be an array'); - } - - constructors.push(key); - // - // constructor for key - // - adt[key] = function() { - var argLength = arguments.length; - - // validation - if (argLength !== value.length) { - throw new Error('Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + argLength); - } - - // Don't use array slice(arguments), makes the whole function unoptimisable on Chrome - var args = new Array(argLength); - for (var i = 0; i < args.length; i++) args[i] = arguments[i]; - - - var match = function(branches) { - var branchKeys = Obj.keys(branches); - if (constructors.length !== branchKeys.length) { - throw new Error('Wrong number of arguments to match. Expected: ' + constructors.join(',') + '\nActual: ' + branchKeys.join(',')); - } - - var allReqd = Arr.forall(constructors, function(reqKey) { - return Arr.contains(branchKeys, reqKey); - }); - - if (!allReqd) throw new Error('Not all branches were specified when using match. Specified: ' + branchKeys.join(', ') + '\nRequired: ' + constructors.join(', ')); - - return branches[key].apply(null, args); - }; - - // - // the fold function for key - // - return { - fold: function( /* arguments */ ) { - // runtime validation - if (arguments.length !== cases.length) { - throw new Error('Wrong number of arguments to fold. Expected ' + cases.length + ', got ' + arguments.length); - } - var target = arguments[count]; - return target.apply(null, args); - }, - match: match, - - // NOTE: Only for debugging. - log: function(label) { - console.log(label, { - constructors: constructors, - constructor: key, - params: args - }); - } - }; - }; - }); - - return adt; - }; - return { - generate: generate - }; - } - ); - /** - * CefDeleteAction.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.delete.CefDeleteAction', [ - 'ephox.katamari.api.Adt', - 'ephox.katamari.api.Option', - 'ephox.sugar.api.node.Element', - 'tinymce.core.caret.CaretFinder', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.caret.CaretUtils', - 'tinymce.core.delete.DeleteUtils', - 'tinymce.core.dom.Empty', - 'tinymce.core.dom.NodeType' - ], - function(Adt, Option, Element, CaretFinder, CaretPosition, CaretUtils, DeleteUtils, Empty, NodeType) { - var DeleteAction = Adt.generate([{ - remove: ['element'] - }, - { - moveToElement: ['element'] - }, - { - moveToPosition: ['position'] - } - ]); - - var isAtContentEditableBlockCaret = function(forward, from) { - var elm = from.getNode(forward === false); - var caretLocation = forward ? 'after' : 'before'; - return NodeType.isElement(elm) && elm.getAttribute('data-mce-caret') === caretLocation; - }; - - var deleteEmptyBlockOrMoveToCef = function(rootNode, forward, from, to) { - var toCefElm = to.getNode(forward === false); - return DeleteUtils.getParentTextBlock(Element.fromDom(rootNode), Element.fromDom(from.getNode())).map(function(blockElm) { - return Empty.isEmpty(blockElm) ? DeleteAction.remove(blockElm.dom()) : DeleteAction.moveToElement(toCefElm); - }).orThunk(function() { - return Option.some(DeleteAction.moveToElement(toCefElm)); - }); - }; - - var findCefPosition = function(rootNode, forward, from) { - return CaretFinder.fromPosition(forward, rootNode, from).bind(function(to) { - if (forward && NodeType.isContentEditableFalse(to.getNode())) { - return deleteEmptyBlockOrMoveToCef(rootNode, forward, from, to); - } else if (forward === false && NodeType.isContentEditableFalse(to.getNode(true))) { - return deleteEmptyBlockOrMoveToCef(rootNode, forward, from, to); - } else if (forward && CaretUtils.isAfterContentEditableFalse(from)) { - return Option.some(DeleteAction.moveToPosition(to)); - } else if (forward === false && CaretUtils.isBeforeContentEditableFalse(from)) { - return Option.some(DeleteAction.moveToPosition(to)); - } else { - return Option.none(); - } - }); - }; - - var getContentEditableBlockAction = function(forward, elm) { - if (forward && NodeType.isContentEditableFalse(elm.nextSibling)) { - return Option.some(DeleteAction.moveToElement(elm.nextSibling)); - } else if (forward === false && NodeType.isContentEditableFalse(elm.previousSibling)) { - return Option.some(DeleteAction.moveToElement(elm.previousSibling)); - } else { - return Option.none(); - } - }; - - var getContentEditableAction = function(rootNode, forward, from) { - if (isAtContentEditableBlockCaret(forward, from)) { - return getContentEditableBlockAction(forward, from.getNode(forward === false)) - .fold( - function() { - return findCefPosition(rootNode, forward, from); - }, - Option.some - ); - } else { - return findCefPosition(rootNode, forward, from); - } - }; - - var read = function(rootNode, forward, rng) { - var normalizedRange = CaretUtils.normalizeRange(forward ? 1 : -1, rootNode, rng); - var from = CaretPosition.fromRangeStart(normalizedRange); - - if (forward === false && CaretUtils.isAfterContentEditableFalse(from)) { - return Option.some(DeleteAction.remove(from.getNode(true))); - } else if (forward && CaretUtils.isBeforeContentEditableFalse(from)) { - return Option.some(DeleteAction.remove(from.getNode())); - } else { - return getContentEditableAction(rootNode, forward, from); - } - }; - - return { - read: read - }; - } - ); - - /** - * Bidi.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.text.Bidi', [], - function() { - var strongRtl = /[\u0591-\u07FF\uFB1D-\uFDFF\uFE70-\uFEFC]/; - - var hasStrongRtl = function(text) { - return strongRtl.test(text); - }; - - return { - hasStrongRtl: hasStrongRtl - }; - } - ); - /** - * InlineUtils.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.keyboard.InlineUtils', [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Option', - 'ephox.katamari.api.Options', - 'tinymce.core.caret.CaretContainer', - 'tinymce.core.caret.CaretFinder', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.caret.CaretUtils', - 'tinymce.core.caret.CaretWalker', - 'tinymce.core.dom.DOMUtils', - 'tinymce.core.dom.NodeType', - 'tinymce.core.text.Bidi' - ], - function(Arr, Fun, Option, Options, CaretContainer, CaretFinder, CaretPosition, CaretUtils, CaretWalker, DOMUtils, NodeType, Bidi) { - var isInlineTarget = function(elm) { - return DOMUtils.DOM.is(elm, 'a[href],code'); - }; - - var isRtl = function(element) { - return DOMUtils.DOM.getStyle(element, 'direction', true) === 'rtl' || Bidi.hasStrongRtl(element.textContent); - }; - - var findInlineParents = function(rootNode, pos) { - return Arr.filter(DOMUtils.DOM.getParents(pos.container(), '*', rootNode), isInlineTarget); - }; - - var findInline = function(rootNode, pos) { - var parents = findInlineParents(rootNode, pos); - return Option.from(parents[0]); - }; - - var findRootInline = function(rootNode, pos) { - var parents = findInlineParents(rootNode, pos); - return Option.from(parents[parents.length - 1]); - }; - - var hasSameParentBlock = function(rootNode, node1, node2) { - var block1 = CaretUtils.getParentBlock(node1, rootNode); - var block2 = CaretUtils.getParentBlock(node2, rootNode); - return block1 && block1 === block2; - }; - - var isInInline = function(rootNode, pos) { - return pos ? findRootInline(rootNode, pos).isSome() : false; - }; - - var isAtInlineEndPoint = function(rootNode, pos) { - return findRootInline(rootNode, pos).map(function(inline) { - return findCaretPosition(inline, false, pos).isNone() || findCaretPosition(inline, true, pos).isNone(); - }).getOr(false); - }; - - var isAtZwsp = function(pos) { - return CaretContainer.isBeforeInline(pos) || CaretContainer.isAfterInline(pos); - }; - - var findCaretPositionIn = function(node, forward) { - return CaretFinder.positionIn(forward, node); - }; - - var findCaretPosition = function(rootNode, forward, from) { - return CaretFinder.fromPosition(forward, rootNode, from); - }; - - var normalizePosition = function(forward, pos) { - var container = pos.container(), - offset = pos.offset(); - - if (forward) { - if (CaretContainer.isCaretContainerInline(container)) { - if (NodeType.isText(container.nextSibling)) { - return new CaretPosition(container.nextSibling, 0); - } else { - return CaretPosition.after(container); - } - } else { - return CaretContainer.isBeforeInline(pos) ? new CaretPosition(container, offset + 1) : pos; - } - } else { - if (CaretContainer.isCaretContainerInline(container)) { - if (NodeType.isText(container.previousSibling)) { - return new CaretPosition(container.previousSibling, container.previousSibling.data.length); - } else { - return CaretPosition.before(container); - } - } else { - return CaretContainer.isAfterInline(pos) ? new CaretPosition(container, offset - 1) : pos; - } - } - }; - - var normalizeForwards = Fun.curry(normalizePosition, true); - var normalizeBackwards = Fun.curry(normalizePosition, false); - - return { - isInlineTarget: isInlineTarget, - findInline: findInline, - findRootInline: findRootInline, - isInInline: isInInline, - isRtl: isRtl, - isAtInlineEndPoint: isAtInlineEndPoint, - isAtZwsp: isAtZwsp, - findCaretPositionIn: findCaretPositionIn, - findCaretPosition: findCaretPosition, - normalizePosition: normalizePosition, - normalizeForwards: normalizeForwards, - normalizeBackwards: normalizeBackwards, - hasSameParentBlock: hasSameParentBlock - }; - } - ); - /** - * DeleteElement.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.delete.DeleteElement', [ - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Option', - 'ephox.katamari.api.Options', - 'ephox.sugar.api.dom.Insert', - 'ephox.sugar.api.dom.Remove', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.api.node.Node', - 'ephox.sugar.api.search.PredicateFind', - 'ephox.sugar.api.search.Traverse', - 'tinymce.core.caret.CaretCandidate', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.dom.Empty', - 'tinymce.core.dom.NodeType', - 'tinymce.core.keyboard.InlineUtils' - ], - function(Fun, Option, Options, Insert, Remove, Element, Node, PredicateFind, Traverse, CaretCandidate, CaretPosition, Empty, NodeType, InlineUtils) { - var needsReposition = function(pos, elm) { - var container = pos.container(); - var offset = pos.offset(); - return CaretPosition.isTextPosition(pos) === false && container === elm.parentNode && offset > CaretPosition.before(elm).offset(); - }; - - var reposition = function(elm, pos) { - return needsReposition(pos, elm) ? new CaretPosition(pos.container(), pos.offset() - 1) : pos; - }; - - var beforeOrStartOf = function(node) { - return NodeType.isText(node) ? new CaretPosition(node, 0) : CaretPosition.before(node); - }; - - var afterOrEndOf = function(node) { - return NodeType.isText(node) ? new CaretPosition(node, node.data.length) : CaretPosition.after(node); - }; - - var getPreviousSiblingCaretPosition = function(elm) { - if (CaretCandidate.isCaretCandidate(elm.previousSibling)) { - return Option.some(afterOrEndOf(elm.previousSibling)); - } else { - return elm.previousSibling ? InlineUtils.findCaretPositionIn(elm.previousSibling, false) : Option.none(); - } - }; - - var getNextSiblingCaretPosition = function(elm) { - if (CaretCandidate.isCaretCandidate(elm.nextSibling)) { - return Option.some(beforeOrStartOf(elm.nextSibling)); - } else { - return elm.nextSibling ? InlineUtils.findCaretPositionIn(elm.nextSibling, true) : Option.none(); - } - }; - - var findCaretPositionBackwardsFromElm = function(rootElement, elm) { - var startPosition = CaretPosition.before(elm.previousSibling ? elm.previousSibling : elm.parentNode); - return InlineUtils.findCaretPosition(rootElement, false, startPosition).fold( - function() { - return InlineUtils.findCaretPosition(rootElement, true, CaretPosition.after(elm)); - }, - Option.some - ); - }; - - var findCaretPositionForwardsFromElm = function(rootElement, elm) { - return InlineUtils.findCaretPosition(rootElement, true, CaretPosition.after(elm)).fold( - function() { - return InlineUtils.findCaretPosition(rootElement, false, CaretPosition.before(elm)); - }, - Option.some - ); - }; - - var findCaretPositionBackwards = function(rootElement, elm) { - return getPreviousSiblingCaretPosition(elm).orThunk(function() { - return getNextSiblingCaretPosition(elm); - }).orThunk(function() { - return findCaretPositionBackwardsFromElm(rootElement, elm); - }); - }; - - var findCaretPositionForward = function(rootElement, elm) { - return getNextSiblingCaretPosition(elm).orThunk(function() { - return getPreviousSiblingCaretPosition(elm); - }).orThunk(function() { - return findCaretPositionForwardsFromElm(rootElement, elm); - }); - }; - - var findCaretPosition = function(forward, rootElement, elm) { - return forward ? findCaretPositionForward(rootElement, elm) : findCaretPositionBackwards(rootElement, elm); - }; - - var findCaretPosOutsideElmAfterDelete = function(forward, rootElement, elm) { - return findCaretPosition(forward, rootElement, elm).map(Fun.curry(reposition, elm)); - }; - - var setSelection = function(editor, forward, pos) { - pos.fold( - function() { - editor.focus(); - }, - function(pos) { - editor.selection.setRng(pos.toRange(), forward); - } - ); - }; - - var eqRawNode = function(rawNode) { - return function(elm) { - return elm.dom() === rawNode; - }; - }; - - var isBlock = function(editor, elm) { - return elm && editor.schema.getBlockElements().hasOwnProperty(Node.name(elm)); - }; - - var paddEmptyBlock = function(elm) { - if (Empty.isEmpty(elm)) { - var br = Element.fromHtml('<br data-mce-bogus="1">'); - Remove.empty(elm); - Insert.append(elm, br); - return Option.some(CaretPosition.before(br.dom())); - } else { - return Option.none(); - } - }; - - // When deleting an element between two text nodes IE 11 doesn't automatically merge the adjacent text nodes - var deleteNormalized = function(elm, afterDeletePosOpt) { - return Options.liftN([Traverse.prevSibling(elm), Traverse.nextSibling(elm), afterDeletePosOpt], function(prev, next, afterDeletePos) { - var offset, prevNode = prev.dom(), - nextNode = next.dom(); - - if (NodeType.isText(prevNode) && NodeType.isText(nextNode)) { - offset = prevNode.data.length; - prevNode.appendData(nextNode.data); - Remove.remove(next); - Remove.remove(elm); - if (afterDeletePos.container() === nextNode) { - return new CaretPosition(prevNode, offset); - } else { - return afterDeletePos; - } - } else { - Remove.remove(elm); - return afterDeletePos; - } - }).orThunk(function() { - Remove.remove(elm); - return afterDeletePosOpt; - }); - }; - - var deleteElement = function(editor, forward, elm) { - var afterDeletePos = findCaretPosOutsideElmAfterDelete(forward, editor.getBody(), elm.dom()); - var parentBlock = PredicateFind.ancestor(elm, Fun.curry(isBlock, editor), eqRawNode(editor.getBody())); - var normalizedAfterDeletePos = deleteNormalized(elm, afterDeletePos); - - parentBlock.bind(paddEmptyBlock).fold( - function() { - setSelection(editor, forward, normalizedAfterDeletePos); - }, - function(paddPos) { - setSelection(editor, forward, Option.some(paddPos)); - } - ); - }; - - return { - deleteElement: deleteElement - }; - } - ); - /** - * CefDelete.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.delete.CefDelete', [ - 'ephox.sugar.api.node.Element', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.caret.CaretUtils', - 'tinymce.core.delete.BlockBoundary', - 'tinymce.core.delete.CefDeleteAction', - 'tinymce.core.delete.DeleteElement', - 'tinymce.core.delete.MergeBlocks', - 'tinymce.core.dom.NodeType' - ], - function(Element, CaretPosition, CaretUtils, BlockBoundary, CefDeleteAction, DeleteElement, MergeBlocks, NodeType) { - var deleteElement = function(editor, forward) { - return function(element) { - DeleteElement.deleteElement(editor, forward, Element.fromDom(element)); - return true; - }; - }; - - var moveToElement = function(editor, forward) { - return function(element) { - var pos = forward ? CaretPosition.before(element) : CaretPosition.after(element); - editor.selection.setRng(pos.toRange()); - return true; - }; - }; - - var moveToPosition = function(editor) { - return function(pos) { - editor.selection.setRng(pos.toRange()); - return true; - }; - }; - - var backspaceDeleteCaret = function(editor, forward) { - var result = CefDeleteAction.read(editor.getBody(), forward, editor.selection.getRng()).map(function(deleteAction) { - return deleteAction.fold( - deleteElement(editor, forward), - moveToElement(editor, forward), - moveToPosition(editor) - ); - }); - - return result.getOr(false); - }; - - var backspaceDeleteRange = function(editor, forward) { - var selectedElement = editor.selection.getNode(); - if (NodeType.isContentEditableFalse(selectedElement)) { - DeleteElement.deleteElement(editor, forward, Element.fromDom(editor.selection.getNode())); - return true; - } else { - return false; - } - }; - - var getContentEditableRoot = function(root, node) { - while (node && node !== root) { - if (NodeType.isContentEditableTrue(node) || NodeType.isContentEditableFalse(node)) { - return node; - } - - node = node.parentNode; - } - - return null; - }; - - var paddEmptyElement = function(editor) { - var br, ceRoot = getContentEditableRoot(editor.getBody(), editor.selection.getNode()); - - if (NodeType.isContentEditableTrue(ceRoot) && editor.dom.isBlock(ceRoot) && editor.dom.isEmpty(ceRoot)) { - br = editor.dom.create('br', { - "data-mce-bogus": "1" - }); - editor.dom.setHTML(ceRoot, ''); - ceRoot.appendChild(br); - editor.selection.setRng(CaretPosition.before(br).toRange()); - } - - return true; - }; - - var backspaceDelete = function(editor, forward) { - if (editor.selection.isCollapsed()) { - return backspaceDeleteCaret(editor, forward); - } else { - return backspaceDeleteRange(editor, forward); - } - }; - - return { - backspaceDelete: backspaceDelete, - paddEmptyElement: paddEmptyElement - }; - } - ); - - /** - * CaretContainerInline.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.caret.CaretContainerInline', [ - 'ephox.katamari.api.Fun', - 'tinymce.core.dom.NodeType', - 'tinymce.core.text.Zwsp' - ], - function(Fun, NodeType, Zwsp) { - var isText = NodeType.isText; - - var startsWithCaretContainer = function(node) { - return isText(node) && node.data[0] === Zwsp.ZWSP; - }; - - var endsWithCaretContainer = function(node) { - return isText(node) && node.data[node.data.length - 1] === Zwsp.ZWSP; - }; - - var createZwsp = function(node) { - return node.ownerDocument.createTextNode(Zwsp.ZWSP); - }; - - var insertBefore = function(node) { - if (isText(node.previousSibling)) { - if (endsWithCaretContainer(node.previousSibling)) { - return node.previousSibling; - } else { - node.previousSibling.appendData(Zwsp.ZWSP); - return node.previousSibling; - } - } else if (isText(node)) { - if (startsWithCaretContainer(node)) { - return node; - } else { - node.insertData(0, Zwsp.ZWSP); - return node; - } - } else { - var newNode = createZwsp(node); - node.parentNode.insertBefore(newNode, node); - return newNode; - } - }; - - var insertAfter = function(node) { - if (isText(node.nextSibling)) { - if (startsWithCaretContainer(node.nextSibling)) { - return node.nextSibling; - } else { - node.nextSibling.insertData(0, Zwsp.ZWSP); - return node.nextSibling; - } - } else if (isText(node)) { - if (endsWithCaretContainer(node)) { - return node; - } else { - node.appendData(Zwsp.ZWSP); - return node; - } - } else { - var newNode = createZwsp(node); - if (node.nextSibling) { - node.parentNode.insertBefore(newNode, node.nextSibling); - } else { - node.parentNode.appendChild(newNode); - } - return newNode; - } - }; - - var insertInline = function(before, node) { - return before ? insertBefore(node) : insertAfter(node); - }; - - return { - insertInline: insertInline, - insertInlineBefore: Fun.curry(insertInline, true), - insertInlineAfter: Fun.curry(insertInline, false) - }; - } - ); - /** - * CaretContainerRemove.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.caret.CaretContainerRemove', [ - 'ephox.katamari.api.Arr', - 'tinymce.core.caret.CaretContainer', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.dom.NodeType', - 'tinymce.core.text.Zwsp', - 'tinymce.core.util.Tools' - ], - function(Arr, CaretContainer, CaretPosition, NodeType, Zwsp, Tools) { - var isElement = NodeType.isElement; - var isText = NodeType.isText; - - var removeNode = function(node) { - var parentNode = node.parentNode; - if (parentNode) { - parentNode.removeChild(node); - } - }; - - var getNodeValue = function(node) { - try { - return node.nodeValue; - } catch (ex) { - // IE sometimes produces "Invalid argument" on nodes - return ""; - } - }; - - var setNodeValue = function(node, text) { - if (text.length === 0) { - removeNode(node); - } else { - node.nodeValue = text; - } - }; - - var trimCount = function(text) { - var trimmedText = Zwsp.trim(text); - return { - count: text.length - trimmedText.length, - text: trimmedText - }; - }; - - var removeUnchanged = function(caretContainer, pos) { - remove(caretContainer); - return pos; - }; - - var removeTextAndReposition = function(caretContainer, pos) { - var before = trimCount(caretContainer.data.substr(0, pos.offset())); - var after = trimCount(caretContainer.data.substr(pos.offset())); - var text = before.text + after.text; - - if (text.length > 0) { - setNodeValue(caretContainer, text); - return new CaretPosition(caretContainer, pos.offset() - before.count); - } else { - return pos; - } - }; - - var removeElementAndReposition = function(caretContainer, pos) { - var parentNode = pos.container(); - var newPosition = Arr.indexOf(parentNode.childNodes, caretContainer).map(function(index) { - return index < pos.offset() ? new CaretPosition(parentNode, pos.offset() - 1) : pos; - }).getOr(pos); - remove(caretContainer); - return newPosition; - }; - - var removeTextCaretContainer = function(caretContainer, pos) { - return pos.container() === caretContainer ? removeTextAndReposition(caretContainer, pos) : removeUnchanged(caretContainer, pos); - }; - - var removeElementCaretContainer = function(caretContainer, pos) { - return pos.container() === caretContainer.parentNode ? removeElementAndReposition(caretContainer, pos) : removeUnchanged(caretContainer, pos); - }; - - var removeAndReposition = function(container, pos) { - return CaretPosition.isTextPosition(pos) ? removeTextCaretContainer(container, pos) : removeElementCaretContainer(container, pos); - }; - - var remove = function(caretContainerNode) { - if (isElement(caretContainerNode) && CaretContainer.isCaretContainer(caretContainerNode)) { - if (CaretContainer.hasContent(caretContainerNode)) { - caretContainerNode.removeAttribute('data-mce-caret'); - } else { - removeNode(caretContainerNode); - } - } - - if (isText(caretContainerNode)) { - var text = Zwsp.trim(getNodeValue(caretContainerNode)); - setNodeValue(caretContainerNode, text); - } - }; - - return { - removeAndReposition: removeAndReposition, - remove: remove - }; - } - ); - /** - * BoundaryCaret.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.keyboard.BoundaryCaret', [ - 'ephox.katamari.api.Option', - 'tinymce.core.caret.CaretContainer', - 'tinymce.core.caret.CaretContainerInline', - 'tinymce.core.caret.CaretContainerRemove', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.dom.NodeType', - 'tinymce.core.keyboard.InlineUtils' - ], - function(Option, CaretContainer, CaretContainerInline, CaretContainerRemove, CaretPosition, NodeType, InlineUtils) { - var insertInlinePos = function(pos, before) { - if (NodeType.isText(pos.container())) { - return CaretContainerInline.insertInline(before, pos.container()); - } else { - return CaretContainerInline.insertInline(before, pos.getNode()); - } - }; - - var isPosCaretContainer = function(pos, caret) { - var caretNode = caret.get(); - return caretNode && pos.container() === caretNode && CaretContainer.isCaretContainerInline(caretNode); - }; - - var renderCaret = function(caret, location) { - return location.fold( - function(element) { // Before - CaretContainerRemove.remove(caret.get()); - var text = CaretContainerInline.insertInlineBefore(element); - caret.set(text); - return Option.some(new CaretPosition(text, text.length - 1)); - }, - function(element) { // Start - return InlineUtils.findCaretPositionIn(element, true).map(function(pos) { - if (!isPosCaretContainer(pos, caret)) { - CaretContainerRemove.remove(caret.get()); - var text = insertInlinePos(pos, true); - caret.set(text); - return new CaretPosition(text, 1); - } else { - return new CaretPosition(caret.get(), 1); - } - }); - }, - function(element) { // End - return InlineUtils.findCaretPositionIn(element, false).map(function(pos) { - if (!isPosCaretContainer(pos, caret)) { - CaretContainerRemove.remove(caret.get()); - var text = insertInlinePos(pos, false); - caret.set(text); - return new CaretPosition(text, text.length - 1); - } else { - return new CaretPosition(caret.get(), caret.get().length - 1); - } - }); - }, - function(element) { // After - CaretContainerRemove.remove(caret.get()); - var text = CaretContainerInline.insertInlineAfter(element); - caret.set(text); - return Option.some(new CaretPosition(text, 1)); - } - ); - }; - - return { - renderCaret: renderCaret - }; - } - ); - /** - * LazyEvaluator.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.util.LazyEvaluator', [ - 'ephox.katamari.api.Option' - ], - function(Option) { - var evaluateUntil = function(fns, args) { - for (var i = 0; i < fns.length; i++) { - var result = fns[i].apply(null, args); - if (result.isSome()) { - return result; - } - } - - return Option.none(); - }; - - return { - evaluateUntil: evaluateUntil - }; - } - ); - /** - * BoundaryLocation.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.keyboard.BoundaryLocation', [ - 'ephox.katamari.api.Adt', - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Option', - 'ephox.katamari.api.Options', - 'tinymce.core.caret.CaretContainer', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.caret.CaretUtils', - 'tinymce.core.dom.NodeType', - 'tinymce.core.keyboard.InlineUtils', - 'tinymce.core.util.LazyEvaluator' - ], - function(Adt, Fun, Option, Options, CaretContainer, CaretPosition, CaretUtils, NodeType, InlineUtils, LazyEvaluator) { - var Location = Adt.generate([{ - before: ['element'] - }, - { - start: ['element'] - }, - { - end: ['element'] - }, - { - after: ['element'] - } - ]); - - var rescope = function(rootNode, node) { - var parentBlock = CaretUtils.getParentBlock(node, rootNode); - return parentBlock ? parentBlock : rootNode; - }; - - var before = function(rootNode, pos) { - var nPos = InlineUtils.normalizeForwards(pos); - var scope = rescope(rootNode, nPos.container()); - return InlineUtils.findRootInline(scope, nPos).fold( - function() { - return InlineUtils.findCaretPosition(scope, true, nPos) - .bind(Fun.curry(InlineUtils.findRootInline, scope)) - .map(function(inline) { - return Location.before(inline); - }); - }, - Option.none - ); - }; - - var start = function(rootNode, pos) { - var nPos = InlineUtils.normalizeBackwards(pos); - return InlineUtils.findRootInline(rootNode, nPos).bind(function(inline) { - var prevPos = InlineUtils.findCaretPosition(inline, false, nPos); - return prevPos.isNone() ? Option.some(Location.start(inline)) : Option.none(); - }); - }; - - var end = function(rootNode, pos) { - var nPos = InlineUtils.normalizeForwards(pos); - return InlineUtils.findRootInline(rootNode, nPos).bind(function(inline) { - var nextPos = InlineUtils.findCaretPosition(inline, true, nPos); - return nextPos.isNone() ? Option.some(Location.end(inline)) : Option.none(); - }); - }; - - var after = function(rootNode, pos) { - var nPos = InlineUtils.normalizeBackwards(pos); - var scope = rescope(rootNode, nPos.container()); - return InlineUtils.findRootInline(scope, nPos).fold( - function() { - return InlineUtils.findCaretPosition(scope, false, nPos) - .bind(Fun.curry(InlineUtils.findRootInline, scope)) - .map(function(inline) { - return Location.after(inline); - }); - }, - Option.none - ); - }; - - var isValidLocation = function(location) { - return InlineUtils.isRtl(getElement(location)) === false; - }; - - var readLocation = function(rootNode, pos) { - var location = LazyEvaluator.evaluateUntil([ - before, - start, - end, - after - ], [rootNode, pos]); - - return location.filter(isValidLocation); - }; - - var getElement = function(location) { - return location.fold( - Fun.identity, // Before - Fun.identity, // Start - Fun.identity, // End - Fun.identity // After - ); - }; - - var getName = function(location) { - return location.fold( - Fun.constant('before'), // Before - Fun.constant('start'), // Start - Fun.constant('end'), // End - Fun.constant('after') // After - ); - }; - - var outside = function(location) { - return location.fold( - Location.before, // Before - Location.before, // Start - Location.after, // End - Location.after // After - ); - }; - - var inside = function(location) { - return location.fold( - Location.start, // Before - Location.start, // Start - Location.end, // End - Location.end // After - ); - }; - - var isEq = function(location1, location2) { - return getName(location1) === getName(location2) && getElement(location1) === getElement(location2); - }; - - var betweenInlines = function(forward, rootNode, from, to, location) { - return Options.liftN([ - InlineUtils.findRootInline(rootNode, from), - InlineUtils.findRootInline(rootNode, to) - ], function(fromInline, toInline) { - if (fromInline !== toInline && InlineUtils.hasSameParentBlock(rootNode, fromInline, toInline)) { - // Force after since some browsers normalize and lean left into the closest inline - return Location.after(forward ? fromInline : toInline); - } else { - return location; - } - }).getOr(location); - }; - - var skipNoMovement = function(fromLocation, toLocation) { - return fromLocation.fold( - Fun.constant(true), - function(fromLocation) { - return !isEq(fromLocation, toLocation); - } - ); - }; - - var findLocationTraverse = function(forward, rootNode, fromLocation, pos) { - var from = InlineUtils.normalizePosition(forward, pos); - var to = InlineUtils.findCaretPosition(rootNode, forward, from).map(Fun.curry(InlineUtils.normalizePosition, forward)); - - var location = to.fold( - function() { - return fromLocation.map(outside); - }, - function(to) { - return readLocation(rootNode, to) - .map(Fun.curry(betweenInlines, forward, rootNode, from, to)) - .filter(Fun.curry(skipNoMovement, fromLocation)); - } - ); - - return location.filter(isValidLocation); - }; - - var findLocationSimple = function(forward, location) { - if (forward) { - return location.fold( - Fun.compose(Option.some, Location.start), // Before -> Start - Option.none, - Fun.compose(Option.some, Location.after), // End -> After - Option.none - ); - } else { - return location.fold( - Option.none, - Fun.compose(Option.some, Location.before), // Before <- Start - Option.none, - Fun.compose(Option.some, Location.end) // End <- After - ); - } - }; - - var findLocation = function(forward, rootNode, pos) { - var from = InlineUtils.normalizePosition(forward, pos); - var fromLocation = readLocation(rootNode, from); - - return readLocation(rootNode, from).bind(Fun.curry(findLocationSimple, forward)).orThunk(function() { - return findLocationTraverse(forward, rootNode, fromLocation, pos); - }); - }; - - return { - readLocation: readLocation, - prevLocation: Fun.curry(findLocation, false), - nextLocation: Fun.curry(findLocation, true), - getElement: getElement, - outside: outside, - inside: inside - }; - } - ); - define( - 'ephox.katamari.api.Cell', - - [], - - function() { - var Cell = function(initial) { - var value = initial; - - var get = function() { - return value; - }; - - var set = function(v) { - value = v; - }; - - var clone = function() { - return Cell(get()); - }; - - return { - get: get, - set: set, - clone: clone - }; - }; - - return Cell; - } - ); - - /** - * BoundarySelection.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.keyboard.BoundarySelection', [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Cell', - 'ephox.katamari.api.Fun', - 'tinymce.core.caret.CaretContainerRemove', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.keyboard.BoundaryCaret', - 'tinymce.core.keyboard.BoundaryLocation', - 'tinymce.core.keyboard.InlineUtils' - ], - function(Arr, Cell, Fun, CaretContainerRemove, CaretPosition, BoundaryCaret, BoundaryLocation, InlineUtils) { - var setCaretPosition = function(editor, pos) { - var rng = editor.dom.createRng(); - rng.setStart(pos.container(), pos.offset()); - rng.setEnd(pos.container(), pos.offset()); - editor.selection.setRng(rng); - }; - - var isFeatureEnabled = function(editor) { - return editor.settings.inline_boundaries !== false; - }; - - var setSelected = function(state, elm) { - if (state) { - elm.setAttribute('data-mce-selected', '1'); - } else { - elm.removeAttribute('data-mce-selected', '1'); - } - }; - - var renderCaretLocation = function(editor, caret, location) { - return BoundaryCaret.renderCaret(caret, location).map(function(pos) { - setCaretPosition(editor, pos); - return location; - }); - }; - - var findLocation = function(editor, caret, forward) { - var rootNode = editor.getBody(); - var from = CaretPosition.fromRangeStart(editor.selection.getRng()); - var location = forward ? BoundaryLocation.nextLocation(rootNode, from) : BoundaryLocation.prevLocation(rootNode, from); - return location.bind(function(location) { - return renderCaretLocation(editor, caret, location); - }); - }; - - var toggleInlines = function(dom, elms) { - var selectedInlines = dom.select('a[href][data-mce-selected],code[data-mce-selected]'); - var targetInlines = Arr.filter(elms, InlineUtils.isInlineTarget); - Arr.each(Arr.difference(selectedInlines, targetInlines), Fun.curry(setSelected, false)); - Arr.each(Arr.difference(targetInlines, selectedInlines), Fun.curry(setSelected, true)); - }; - - var safeRemoveCaretContainer = function(editor, caret) { - if (editor.selection.isCollapsed() && editor.composing !== true && caret.get()) { - var pos = CaretPosition.fromRangeStart(editor.selection.getRng()); - if (CaretPosition.isTextPosition(pos) && InlineUtils.isAtZwsp(pos) === false) { - setCaretPosition(editor, CaretContainerRemove.removeAndReposition(caret.get(), pos)); - caret.set(null); - } - } - }; - - var renderInsideInlineCaret = function(editor, caret, elms) { - if (editor.selection.isCollapsed()) { - var inlines = Arr.filter(elms, InlineUtils.isInlineTarget); - Arr.each(inlines, function(inline) { - var pos = CaretPosition.fromRangeStart(editor.selection.getRng()); - BoundaryLocation.readLocation(editor.getBody(), pos).bind(function(location) { - return renderCaretLocation(editor, caret, location); - }); - }); - } - }; - - var move = function(editor, caret, forward) { - return function() { - return isFeatureEnabled(editor) ? findLocation(editor, caret, forward).isSome() : false; - }; - }; - - var setupSelectedState = function(editor) { - var caret = new Cell(null); - - editor.on('NodeChange', function(e) { - if (isFeatureEnabled(editor)) { - toggleInlines(editor.dom, e.parents); - safeRemoveCaretContainer(editor, caret); - renderInsideInlineCaret(editor, caret, e.parents); - } - }); - - return caret; - }; - - return { - move: move, - setupSelectedState: setupSelectedState, - setCaretPosition: setCaretPosition - }; - } - ); - /** - * InlineBoundaryDelete.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.delete.InlineBoundaryDelete', [ - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Option', - 'ephox.katamari.api.Options', - 'ephox.sugar.api.node.Element', - 'tinymce.core.caret.CaretContainer', - 'tinymce.core.caret.CaretFinder', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.caret.CaretUtils', - 'tinymce.core.delete.DeleteElement', - 'tinymce.core.keyboard.BoundaryCaret', - 'tinymce.core.keyboard.BoundaryLocation', - 'tinymce.core.keyboard.BoundarySelection', - 'tinymce.core.keyboard.InlineUtils' - ], - function( - Fun, Option, Options, Element, CaretContainer, CaretFinder, CaretPosition, CaretUtils, DeleteElement, BoundaryCaret, BoundaryLocation, BoundarySelection, - InlineUtils - ) { - var isFeatureEnabled = function(editor) { - return editor.settings.inline_boundaries !== false; - }; - - var rangeFromPositions = function(from, to) { - var range = document.createRange(); - - range.setStart(from.container(), from.offset()); - range.setEnd(to.container(), to.offset()); - - return range; - }; - - // Checks for delete at <code>|a</code> when there is only one item left except the zwsp caret container nodes - var hasOnlyTwoOrLessPositionsLeft = function(elm) { - return Options.liftN([ - InlineUtils.findCaretPositionIn(elm, true), - InlineUtils.findCaretPositionIn(elm, false) - ], function(firstPos, lastPos) { - var normalizedFirstPos = InlineUtils.normalizePosition(true, firstPos); - var normalizedLastPos = InlineUtils.normalizePosition(false, lastPos); - - return InlineUtils.findCaretPosition(elm, true, normalizedFirstPos).map(function(pos) { - return pos.isEqual(normalizedLastPos); - }).getOr(true); - }).getOr(true); - }; - - var setCaretLocation = function(editor, caret) { - return function(location) { - return BoundaryCaret.renderCaret(caret, location).map(function(pos) { - BoundarySelection.setCaretPosition(editor, pos); - return true; - }).getOr(false); - }; - }; - - var deleteFromTo = function(editor, caret, from, to) { - var rootNode = editor.getBody(); - - editor.undoManager.ignore(function() { - editor.selection.setRng(rangeFromPositions(from, to)); - editor.execCommand('Delete'); - - BoundaryLocation.readLocation(rootNode, CaretPosition.fromRangeStart(editor.selection.getRng())) - .map(BoundaryLocation.inside) - .map(setCaretLocation(editor, caret)); - }); - - editor.nodeChanged(); - }; - - var rescope = function(rootNode, node) { - var parentBlock = CaretUtils.getParentBlock(node, rootNode); - return parentBlock ? parentBlock : rootNode; - }; - - var backspaceDeleteCollapsed = function(editor, caret, forward, from) { - var rootNode = rescope(editor.getBody(), from.container()); - var fromLocation = BoundaryLocation.readLocation(rootNode, from); - - return fromLocation.bind(function(location) { - if (forward) { - return location.fold( - Fun.constant(Option.some(BoundaryLocation.inside(location))), // Before - Option.none, // Start - Fun.constant(Option.some(BoundaryLocation.outside(location))), // End - Option.none // After - ); - } else { - return location.fold( - Option.none, // Before - Fun.constant(Option.some(BoundaryLocation.outside(location))), // Start - Option.none, // End - Fun.constant(Option.some(BoundaryLocation.inside(location))) // After - ); - } - }) - .map(setCaretLocation(editor, caret)) - .getOrThunk(function() { - var toPosition = CaretFinder.navigate(forward, rootNode, from); - var toLocation = toPosition.bind(function(pos) { - return BoundaryLocation.readLocation(rootNode, pos); - }); - - if (fromLocation.isSome() && toLocation.isSome()) { - return InlineUtils.findRootInline(rootNode, from).map(function(elm) { - if (hasOnlyTwoOrLessPositionsLeft(elm)) { - DeleteElement.deleteElement(editor, forward, Element.fromDom(elm)); - return true; - } else { - return false; - } - }).getOr(false); - } else { - return toLocation.bind(function(_) { - return toPosition.map(function(to) { - if (forward) { - deleteFromTo(editor, caret, from, to); - } else { - deleteFromTo(editor, caret, to, from); - } - - return true; - }); - }).getOr(false); - } - }); - }; - - var backspaceDelete = function(editor, caret, forward) { - if (editor.selection.isCollapsed() && isFeatureEnabled(editor)) { - var from = CaretPosition.fromRangeStart(editor.selection.getRng()); - return backspaceDeleteCollapsed(editor, caret, forward, from); - } - - return false; - }; - - return { - backspaceDelete: backspaceDelete - }; - } - ); - /** - * Commands.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.delete.DeleteCommands', [ - 'tinymce.core.delete.BlockBoundaryDelete', - 'tinymce.core.delete.BlockRangeDelete', - 'tinymce.core.delete.CefDelete', - 'tinymce.core.delete.InlineBoundaryDelete' - ], - function(BlockBoundaryDelete, BlockRangeDelete, CefDelete, BoundaryDelete) { - var nativeCommand = function(editor, command) { - editor.getDoc().execCommand(command, false, null); - }; - - var paddEmptyBody = function(editor) { - var dom = editor.dom; - - // Check if body is empty after the delete call if so then set the contents - // to an empty string and move the caret to any block produced by that operation - // this fixes the issue with root blocks not being properly produced after a delete call on IE - var body = editor.getBody(); - - if (dom.isEmpty(body)) { - editor.setContent(''); - - if (body.firstChild && dom.isBlock(body.firstChild)) { - editor.selection.setCursorLocation(body.firstChild, 0); - } else { - editor.selection.setCursorLocation(body, 0); - } - } - }; - - var deleteCommand = function(editor) { - if (CefDelete.backspaceDelete(editor, false)) { - return; - } else if (BoundaryDelete.backspaceDelete(editor, false)) { - return; - } else if (BlockBoundaryDelete.backspaceDelete(editor, false)) { - return; - } else if (BlockRangeDelete.backspaceDelete(editor, false)) { - return; - } else { - nativeCommand(editor, 'Delete'); - paddEmptyBody(editor); - } - }; - - var forwardDeleteCommand = function(editor) { - if (CefDelete.backspaceDelete(editor, true)) { - return; - } else if (BoundaryDelete.backspaceDelete(editor, true)) { - return; - } else if (BlockBoundaryDelete.backspaceDelete(editor, true)) { - return; - } else if (BlockRangeDelete.backspaceDelete(editor, true)) { - return; - } else { - nativeCommand(editor, 'ForwardDelete'); - } - }; - - return { - deleteCommand: deleteCommand, - forwardDeleteCommand: forwardDeleteCommand - }; - } - ); - /** - * RangeNormalizer.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.dom.RangeNormalizer', [ - 'tinymce.core.caret.CaretFinder', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.caret.CaretUtils', - 'tinymce.core.dom.NodeType' - ], - function(CaretFinder, CaretPosition, CaretUtils, NodeType) { - var isTextBlock = function(elm) { - return NodeType.isElement(elm) && /^(P|H[1-6]|DIV)$/.test(elm.nodeName); - }; - - var matchEndContainer = function(rng, predicate) { - return predicate(rng.endContainer); - }; - - var createRange = function(sc, so, ec, eo) { - var rng = document.createRange(); - rng.setStart(sc, so); - rng.setEnd(ec, eo); - return rng; - }; - - // If you tripple click a paragraph in this case: - // <blockquote><p>a</p></blockquote><p>b</p> - // It would become this range in webkit: - // <blockquote><p>[a</p></blockquote><p>]b</p> - // We would want it to be: - // <blockquote><p>[a]</p></blockquote><p>b</p> - // Since it would otherwise produces spans out of thin air on insertContent for example. - var normalizeBlockSelection = function(rng) { - var startPos = CaretPosition.fromRangeStart(rng); - var endPos = CaretPosition.fromRangeEnd(rng); - var rootNode = rng.commonAncestorContainer; - - if (rng.collapsed === false && matchEndContainer(rng, isTextBlock) && rng.endOffset === 0) { - return CaretFinder.fromPosition(false, rootNode, endPos) - .map(function(newEndPos) { - if (!CaretUtils.isInSameBlock(startPos, endPos, rootNode) && CaretUtils.isInSameBlock(startPos, newEndPos, rootNode)) { - return createRange(startPos.container(), startPos.offset(), newEndPos.container(), newEndPos.offset()); - } else { - return rng; - } - }).getOr(rng); - } else { - return rng; - } - }; - - var normalize = function(rng) { - return normalizeBlockSelection(rng); - }; - - return { - normalize: normalize - }; - } - ); - /** - * InsertList.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Handles inserts of lists into the editor instance. - * - * @class tinymce.InsertList - * @private - */ - define( - 'tinymce.core.InsertList', [ - "tinymce.core.util.Tools", - "tinymce.core.caret.CaretWalker", - "tinymce.core.caret.CaretPosition" - ], - function(Tools, CaretWalker, CaretPosition) { - var isListFragment = function(fragment) { - var firstChild = fragment.firstChild; - var lastChild = fragment.lastChild; - - // Skip meta since it's likely <meta><ul>..</ul> - if (firstChild && firstChild.name === 'meta') { - firstChild = firstChild.next; - } - - // Skip mce_marker since it's likely <ul>..</ul><span id="mce_marker"></span> - if (lastChild && lastChild.attr('id') === 'mce_marker') { - lastChild = lastChild.prev; - } - - if (!firstChild || firstChild !== lastChild) { - return false; - } - - return firstChild.name === 'ul' || firstChild.name === 'ol'; - }; - - var cleanupDomFragment = function(domFragment) { - var firstChild = domFragment.firstChild; - var lastChild = domFragment.lastChild; - - // TODO: remove the meta tag from paste logic - if (firstChild && firstChild.nodeName === 'META') { - firstChild.parentNode.removeChild(firstChild); - } - - if (lastChild && lastChild.id === 'mce_marker') { - lastChild.parentNode.removeChild(lastChild); - } - - return domFragment; - }; - - var toDomFragment = function(dom, serializer, fragment) { - var html = serializer.serialize(fragment); - var domFragment = dom.createFragment(html); - - return cleanupDomFragment(domFragment); - }; - - var listItems = function(elm) { - return Tools.grep(elm.childNodes, function(child) { - return child.nodeName === 'LI'; - }); - }; - - var isEmpty = function(elm) { - return !elm.firstChild; - }; - - var trimListItems = function(elms) { - return elms.length > 0 && isEmpty(elms[elms.length - 1]) ? elms.slice(0, -1) : elms; - }; - - var getParentLi = function(dom, node) { - var parentBlock = dom.getParent(node, dom.isBlock); - return parentBlock && parentBlock.nodeName === 'LI' ? parentBlock : null; - }; - - var isParentBlockLi = function(dom, node) { - return !!getParentLi(dom, node); - }; - - var getSplit = function(parentNode, rng) { - var beforeRng = rng.cloneRange(); - var afterRng = rng.cloneRange(); - - beforeRng.setStartBefore(parentNode); - afterRng.setEndAfter(parentNode); - - return [ - beforeRng.cloneContents(), - afterRng.cloneContents() - ]; - }; - - var findFirstIn = function(node, rootNode) { - var caretPos = CaretPosition.before(node); - var caretWalker = new CaretWalker(rootNode); - var newCaretPos = caretWalker.next(caretPos); - - return newCaretPos ? newCaretPos.toRange() : null; - }; - - var findLastOf = function(node, rootNode) { - var caretPos = CaretPosition.after(node); - var caretWalker = new CaretWalker(rootNode); - var newCaretPos = caretWalker.prev(caretPos); - - return newCaretPos ? newCaretPos.toRange() : null; - }; - - var insertMiddle = function(target, elms, rootNode, rng) { - var parts = getSplit(target, rng); - var parentElm = target.parentNode; - - parentElm.insertBefore(parts[0], target); - Tools.each(elms, function(li) { - parentElm.insertBefore(li, target); - }); - parentElm.insertBefore(parts[1], target); - parentElm.removeChild(target); - - return findLastOf(elms[elms.length - 1], rootNode); - }; - - var insertBefore = function(target, elms, rootNode) { - var parentElm = target.parentNode; - - Tools.each(elms, function(elm) { - parentElm.insertBefore(elm, target); - }); - - return findFirstIn(target, rootNode); - }; - - var insertAfter = function(target, elms, rootNode, dom) { - dom.insertAfter(elms.reverse(), target); - return findLastOf(elms[0], rootNode); - }; - - var insertAtCaret = function(serializer, dom, rng, fragment) { - var domFragment = toDomFragment(dom, serializer, fragment); - var liTarget = getParentLi(dom, rng.startContainer); - var liElms = trimListItems(listItems(domFragment.firstChild)); - var BEGINNING = 1, - END = 2; - var rootNode = dom.getRoot(); - - var isAt = function(location) { - var caretPos = CaretPosition.fromRangeStart(rng); - var caretWalker = new CaretWalker(dom.getRoot()); - var newPos = location === BEGINNING ? caretWalker.prev(caretPos) : caretWalker.next(caretPos); - - return newPos ? getParentLi(dom, newPos.getNode()) !== liTarget : true; - }; - - if (isAt(BEGINNING)) { - return insertBefore(liTarget, liElms, rootNode); - } else if (isAt(END)) { - return insertAfter(liTarget, liElms, rootNode, dom); - } - - return insertMiddle(liTarget, liElms, rootNode, rng); - }; - - return { - isListFragment: isListFragment, - insertAtCaret: insertAtCaret, - isParentBlockLi: isParentBlockLi, - trimListItems: trimListItems, - listItems: listItems - }; - } - ); - /** - * InsertContent.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Handles inserts of contents into the editor instance. - * - * @class tinymce.InsertContent - * @private - */ - define( - 'tinymce.core.InsertContent', [ - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.caret.CaretWalker', - 'tinymce.core.dom.ElementUtils', - 'tinymce.core.dom.NodeType', - 'tinymce.core.dom.RangeNormalizer', - 'tinymce.core.Env', - 'tinymce.core.html.Serializer', - 'tinymce.core.InsertList', - 'tinymce.core.util.Tools' - ], - function(CaretPosition, CaretWalker, ElementUtils, NodeType, RangeNormalizer, Env, Serializer, InsertList, Tools) { - var isTableCell = NodeType.matchNodeNames('td th'); - - var validInsertion = function(editor, value, parentNode) { - // Should never insert content into bogus elements, since these can - // be resize handles or similar - if (parentNode.getAttribute('data-mce-bogus') === 'all') { - parentNode.parentNode.insertBefore(editor.dom.createFragment(value), parentNode); - } else { - // Check if parent is empty or only has one BR element then set the innerHTML of that parent - var node = parentNode.firstChild; - var node2 = parentNode.lastChild; - if (!node || (node === node2 && node.nodeName === 'BR')) { /// - editor.dom.setHTML(parentNode, value); - } else { - editor.selection.setContent(value); - } - } - }; - - var insertHtmlAtCaret = function(editor, value, details) { - var parser, serializer, parentNode, rootNode, fragment, args; - var marker, rng, node, node2, bookmarkHtml, merge; - var textInlineElements = editor.schema.getTextInlineElements(); - var selection = editor.selection, - dom = editor.dom; - - function trimOrPaddLeftRight(html) { - var rng, container, offset; - - rng = selection.getRng(true); - container = rng.startContainer; - offset = rng.startOffset; - - function hasSiblingText(siblingName) { - return container[siblingName] && container[siblingName].nodeType == 3; - } - - if (container.nodeType == 3) { - if (offset > 0) { - html = html.replace(/^ /, ' '); - } else if (!hasSiblingText('previousSibling')) { - html = html.replace(/^ /, ' '); - } - - if (offset < container.length) { - html = html.replace(/ (<br>|)$/, ' '); - } else if (!hasSiblingText('nextSibling')) { - html = html.replace(/( | )(<br>|)$/, ' '); - } - } - - return html; - } - - // Removes   from a [b] c -> a  c -> a c - function trimNbspAfterDeleteAndPaddValue() { - var rng, container, offset; - - rng = selection.getRng(true); - container = rng.startContainer; - offset = rng.startOffset; - - if (container.nodeType == 3 && rng.collapsed) { - if (container.data[offset] === '\u00a0') { - container.deleteData(offset, 1); - - if (!/[\u00a0| ]$/.test(value)) { - value += ' '; - } - } else if (container.data[offset - 1] === '\u00a0') { - container.deleteData(offset - 1, 1); - - if (!/[\u00a0| ]$/.test(value)) { - value = ' ' + value; - } - } - } - } - - function reduceInlineTextElements() { - if (merge) { - var root = editor.getBody(), - elementUtils = new ElementUtils(dom); - - Tools.each(dom.select('*[data-mce-fragment]'), function(node) { - for (var testNode = node.parentNode; testNode && testNode != root; testNode = testNode.parentNode) { - if (textInlineElements[node.nodeName.toLowerCase()] && elementUtils.compare(testNode, node)) { - dom.remove(node, true); - } - } - }); - } - } - - function markFragmentElements(fragment) { - var node = fragment; - - while ((node = node.walk())) { - if (node.type === 1) { - node.attr('data-mce-fragment', '1'); - } - } - } - - function umarkFragmentElements(elm) { - Tools.each(elm.getElementsByTagName('*'), function(elm) { - elm.removeAttribute('data-mce-fragment'); - }); - } - - function isPartOfFragment(node) { - return !!node.getAttribute('data-mce-fragment'); - } - - function canHaveChildren(node) { - return node && !editor.schema.getShortEndedElements()[node.nodeName]; - } - - function moveSelectionToMarker(marker) { - var parentEditableFalseElm, parentBlock, nextRng; - - function getContentEditableFalseParent(node) { - var root = editor.getBody(); - - for (; node && node !== root; node = node.parentNode) { - if (editor.dom.getContentEditable(node) === 'false') { - return node; - } - } - - return null; - } - - if (!marker) { - return; - } - - selection.scrollIntoView(marker); - - // If marker is in cE=false then move selection to that element instead - parentEditableFalseElm = getContentEditableFalseParent(marker); - if (parentEditableFalseElm) { - dom.remove(marker); - selection.select(parentEditableFalseElm); - return; - } - - // Move selection before marker and remove it - rng = dom.createRng(); - - // If previous sibling is a text node set the selection to the end of that node - node = marker.previousSibling; - if (node && node.nodeType == 3) { - rng.setStart(node, node.nodeValue.length); - - // TODO: Why can't we normalize on IE - if (!Env.ie) { - node2 = marker.nextSibling; - if (node2 && node2.nodeType == 3) { - node.appendData(node2.data); - node2.parentNode.removeChild(node2); - } - } - } else { - // If the previous sibling isn't a text node or doesn't exist set the selection before the marker node - rng.setStartBefore(marker); - rng.setEndBefore(marker); - } - - function findNextCaretRng(rng) { - var caretPos = CaretPosition.fromRangeStart(rng); - var caretWalker = new CaretWalker(editor.getBody()); - - caretPos = caretWalker.next(caretPos); - if (caretPos) { - return caretPos.toRange(); - } - } - - // Remove the marker node and set the new range - parentBlock = dom.getParent(marker, dom.isBlock); - dom.remove(marker); - - if (parentBlock && dom.isEmpty(parentBlock)) { - editor.$(parentBlock).empty(); - - rng.setStart(parentBlock, 0); - rng.setEnd(parentBlock, 0); - - if (!isTableCell(parentBlock) && !isPartOfFragment(parentBlock) && (nextRng = findNextCaretRng(rng))) { - rng = nextRng; - dom.remove(parentBlock); - } else { - dom.add(parentBlock, dom.create('br', { - 'data-mce-bogus': '1' - })); - } - } - - selection.setRng(rng); - } - - // Check for whitespace before/after value - if (/^ | $/.test(value)) { - value = trimOrPaddLeftRight(value); - } - - // Setup parser and serializer - parser = editor.parser; - merge = details.merge; - - serializer = new Serializer({ - validate: editor.settings.validate - }, editor.schema); - bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">​</span>'; - - // Run beforeSetContent handlers on the HTML to be inserted - args = { - content: value, - format: 'html', - selection: true - }; - editor.fire('BeforeSetContent', args); - value = args.content; - - // Add caret at end of contents if it's missing - if (value.indexOf('{$caret}') == -1) { - value += '{$caret}'; - } - - // Replace the caret marker with a span bookmark element - value = value.replace(/\{\$caret\}/, bookmarkHtml); - - // If selection is at <body>|<p></p> then move it into <body><p>|</p> - rng = selection.getRng(); - var caretElement = rng.startContainer || (rng.parentElement ? rng.parentElement() : null); - var body = editor.getBody(); - if (caretElement === body && selection.isCollapsed()) { - if (dom.isBlock(body.firstChild) && canHaveChildren(body.firstChild) && dom.isEmpty(body.firstChild)) { - rng = dom.createRng(); - rng.setStart(body.firstChild, 0); - rng.setEnd(body.firstChild, 0); - selection.setRng(rng); - } - } - - // Insert node maker where we will insert the new HTML and get it's parent - if (!selection.isCollapsed()) { - // Fix for #2595 seems that delete removes one extra character on - // WebKit for some odd reason if you double click select a word - editor.selection.setRng(RangeNormalizer.normalize(editor.selection.getRng())); - editor.getDoc().execCommand('Delete', false, null); - trimNbspAfterDeleteAndPaddValue(); - } - - parentNode = selection.getNode(); - - // Parse the fragment within the context of the parent node - var parserArgs = { - context: parentNode.nodeName.toLowerCase(), - data: details.data - }; - fragment = parser.parse(value, parserArgs); - - // Custom handling of lists - if (details.paste === true && InsertList.isListFragment(fragment) && InsertList.isParentBlockLi(dom, parentNode)) { - rng = InsertList.insertAtCaret(serializer, dom, editor.selection.getRng(true), fragment); - editor.selection.setRng(rng); - editor.fire('SetContent', args); - return; - } - - markFragmentElements(fragment); - - // Move the caret to a more suitable location - node = fragment.lastChild; - if (node.attr('id') == 'mce_marker') { - marker = node; - - for (node = node.prev; node; node = node.walk(true)) { - if (node.type == 3 || !dom.isBlock(node.name)) { - if (editor.schema.isValidChild(node.parent.name, 'span')) { - node.parent.insert(marker, node, node.name === 'br'); - } - break; - } - } - } - - editor._selectionOverrides.showBlockCaretContainer(parentNode); - - // If parser says valid we can insert the contents into that parent - if (!parserArgs.invalid) { - value = serializer.serialize(fragment); - validInsertion(editor, value, parentNode); - } else { - // If the fragment was invalid within that context then we need - // to parse and process the parent it's inserted into - - // Insert bookmark node and get the parent - selection.setContent(bookmarkHtml); - parentNode = selection.getNode(); - rootNode = editor.getBody(); - - // Opera will return the document node when selection is in root - if (parentNode.nodeType == 9) { - parentNode = node = rootNode; - } else { - node = parentNode; - } - - // Find the ancestor just before the root element - while (node !== rootNode) { - parentNode = node; - node = node.parentNode; - } - - // Get the outer/inner HTML depending on if we are in the root and parser and serialize that - value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode); - value = serializer.serialize( - parser.parse( - // Need to replace by using a function since $ in the contents would otherwise be a problem - value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() { - return serializer.serialize(fragment); - }) - ) - ); - - // Set the inner/outer HTML depending on if we are in the root or not - if (parentNode == rootNode) { - dom.setHTML(rootNode, value); - } else { - dom.setOuterHTML(parentNode, value); - } - } - - reduceInlineTextElements(); - moveSelectionToMarker(dom.get('mce_marker')); - umarkFragmentElements(editor.getBody()); - editor.fire('SetContent', args); - editor.addVisual(); - }; - - var processValue = function(value) { - var details; - - if (typeof value !== 'string') { - details = Tools.extend({ - paste: value.paste, - data: { - paste: value.paste - } - }, value); - - return { - content: value.content, - details: details - }; - } - - return { - content: value, - details: {} - }; - }; - - var insertAtCaret = function(editor, value) { - var result = processValue(value); - insertHtmlAtCaret(editor, result.content, result.details); - }; - - return { - insertAtCaret: insertAtCaret - }; - } - ); - /** - * EditorCommands.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class enables you to add custom editor commands and it contains - * overrides for native browser commands to address various bugs and issues. - * - * @class tinymce.EditorCommands - */ - define( - 'tinymce.core.EditorCommands', [ - 'tinymce.core.delete.DeleteCommands', - 'tinymce.core.dom.NodeType', - 'tinymce.core.dom.RangeUtils', - 'tinymce.core.dom.TreeWalker', - 'tinymce.core.Env', - 'tinymce.core.InsertContent', - 'tinymce.core.util.Tools' - ], - function(DeleteCommands, NodeType, RangeUtils, TreeWalker, Env, InsertContent, Tools) { - // Added for compression purposes - var each = Tools.each, - extend = Tools.extend; - var map = Tools.map, - inArray = Tools.inArray, - explode = Tools.explode; - var isOldIE = Env.ie && Env.ie < 11; - var TRUE = true, - FALSE = false; - - return function(editor) { - var dom, selection, formatter, - commands = { - state: {}, - exec: {}, - value: {} - }, - settings = editor.settings, - bookmark; - - editor.on('PreInit', function() { - dom = editor.dom; - selection = editor.selection; - settings = editor.settings; - formatter = editor.formatter; - }); - - /** - * Executes the specified command. - * - * @method execCommand - * @param {String} command Command to execute. - * @param {Boolean} ui Optional user interface state. - * @param {Object} value Optional value for command. - * @param {Object} args Optional extra arguments to the execCommand. - * @return {Boolean} true/false if the command was found or not. - */ - function execCommand(command, ui, value, args) { - var func, customCommand, state = 0; - - if (editor.removed) { - return; - } - - if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(command) && (!args || !args.skip_focus)) { - editor.focus(); - } - - args = editor.fire('BeforeExecCommand', { - command: command, - ui: ui, - value: value - }); - if (args.isDefaultPrevented()) { - return false; - } - - customCommand = command.toLowerCase(); - if ((func = commands.exec[customCommand])) { - func(customCommand, ui, value); - editor.fire('ExecCommand', { - command: command, - ui: ui, - value: value - }); - return true; - } - - // Plugin commands - each(editor.plugins, function(p) { - if (p.execCommand && p.execCommand(command, ui, value)) { - editor.fire('ExecCommand', { - command: command, - ui: ui, - value: value - }); - state = true; - return false; - } - }); - - if (state) { - return state; - } - - // Theme commands - if (editor.theme && editor.theme.execCommand && editor.theme.execCommand(command, ui, value)) { - editor.fire('ExecCommand', { - command: command, - ui: ui, - value: value - }); - return true; - } - - // Browser commands - try { - state = editor.getDoc().execCommand(command, ui, value); - } catch (ex) { - // Ignore old IE errors - } - - if (state) { - editor.fire('ExecCommand', { - command: command, - ui: ui, - value: value - }); - return true; - } - - return false; - } - - /** - * Queries the current state for a command for example if the current selection is "bold". - * - * @method queryCommandState - * @param {String} command Command to check the state of. - * @return {Boolean/Number} true/false if the selected contents is bold or not, -1 if it's not found. - */ - function queryCommandState(command) { - var func; - - if (editor.quirks.isHidden() || editor.removed) { - return; - } - - command = command.toLowerCase(); - if ((func = commands.state[command])) { - return func(command); - } - - // Browser commands - try { - return editor.getDoc().queryCommandState(command); - } catch (ex) { - // Fails sometimes see bug: 1896577 - } - - return false; - } - - /** - * Queries the command value for example the current fontsize. - * - * @method queryCommandValue - * @param {String} command Command to check the value of. - * @return {Object} Command value of false if it's not found. - */ - function queryCommandValue(command) { - var func; - - if (editor.quirks.isHidden() || editor.removed) { - return; - } - - command = command.toLowerCase(); - if ((func = commands.value[command])) { - return func(command); - } - - // Browser commands - try { - return editor.getDoc().queryCommandValue(command); - } catch (ex) { - // Fails sometimes see bug: 1896577 - } - } - - /** - * Adds commands to the command collection. - * - * @method addCommands - * @param {Object} commandList Name/value collection with commands to add, the names can also be comma separated. - * @param {String} type Optional type to add, defaults to exec. Can be value or state as well. - */ - function addCommands(commandList, type) { - type = type || 'exec'; - - each(commandList, function(callback, command) { - each(command.toLowerCase().split(','), function(command) { - commands[type][command] = callback; - }); - }); - } - - function addCommand(command, callback, scope) { - command = command.toLowerCase(); - commands.exec[command] = function(command, ui, value, args) { - return callback.call(scope || editor, ui, value, args); - }; - } - - /** - * Returns true/false if the command is supported or not. - * - * @method queryCommandSupported - * @param {String} command Command that we check support for. - * @return {Boolean} true/false if the command is supported or not. - */ - function queryCommandSupported(command) { - command = command.toLowerCase(); - - if (commands.exec[command]) { - return true; - } - - // Browser commands - try { - return editor.getDoc().queryCommandSupported(command); - } catch (ex) { - // Fails sometimes see bug: 1896577 - } - - return false; - } - - function addQueryStateHandler(command, callback, scope) { - command = command.toLowerCase(); - commands.state[command] = function() { - return callback.call(scope || editor); - }; - } - - function addQueryValueHandler(command, callback, scope) { - command = command.toLowerCase(); - commands.value[command] = function() { - return callback.call(scope || editor); - }; - } - - function hasCustomCommand(command) { - command = command.toLowerCase(); - return !!commands.exec[command]; - } - - // Expose public methods - extend(this, { - execCommand: execCommand, - queryCommandState: queryCommandState, - queryCommandValue: queryCommandValue, - queryCommandSupported: queryCommandSupported, - addCommands: addCommands, - addCommand: addCommand, - addQueryStateHandler: addQueryStateHandler, - addQueryValueHandler: addQueryValueHandler, - hasCustomCommand: hasCustomCommand - }); - - // Private methods - - function execNativeCommand(command, ui, value) { - if (ui === undefined) { - ui = FALSE; - } - - if (value === undefined) { - value = null; - } - - return editor.getDoc().execCommand(command, ui, value); - } - - function isFormatMatch(name) { - return formatter.match(name); - } - - function toggleFormat(name, value) { - formatter.toggle(name, value ? { - value: value - } : undefined); - editor.nodeChanged(); - } - - function storeSelection(type) { - bookmark = selection.getBookmark(type); - } - - function restoreSelection() { - selection.moveToBookmark(bookmark); - } - - // Add execCommand overrides - addCommands({ - // Ignore these, added for compatibility - 'mceResetDesignMode,mceBeginUndoLevel': function() {}, - - // Add undo manager logic - 'mceEndUndoLevel,mceAddUndoLevel': function() { - editor.undoManager.add(); - }, - - 'Cut,Copy,Paste': function(command) { - var doc = editor.getDoc(), - failed; - - // Try executing the native command - try { - execNativeCommand(command); - } catch (ex) { - // Command failed - failed = TRUE; - } - - // Chrome reports the paste command as supported however older IE:s will return false for cut/paste - if (command === 'paste' && !doc.queryCommandEnabled(command)) { - failed = true; - } - - // Present alert message about clipboard access not being available - if (failed || !doc.queryCommandSupported(command)) { - var msg = editor.translate( - "Your browser doesn't support direct access to the clipboard. " + - "Please use the Ctrl+X/C/V keyboard shortcuts instead." - ); - - if (Env.mac) { - msg = msg.replace(/Ctrl\+/g, '\u2318+'); - } - - editor.notificationManager.open({ - text: msg, - type: 'error' - }); - } - }, - - // Override unlink command - unlink: function() { - if (selection.isCollapsed()) { - var elm = editor.dom.getParent(editor.selection.getStart(), 'a'); - if (elm) { - editor.dom.remove(elm, true); - } - - return; - } - - formatter.remove("link"); - }, - - // Override justify commands to use the text formatter engine - 'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull,JustifyNone': function(command) { - var align = command.substring(7); - - if (align == 'full') { - align = 'justify'; - } - - // Remove all other alignments first - each('left,center,right,justify'.split(','), function(name) { - if (align != name) { - formatter.remove('align' + name); - } - }); - - if (align != 'none') { - toggleFormat('align' + align); - } - }, - - // Override list commands to fix WebKit bug - 'InsertUnorderedList,InsertOrderedList': function(command) { - var listElm, listParent; - - execNativeCommand(command); - - // WebKit produces lists within block elements so we need to split them - // we will replace the native list creation logic to custom logic later on - // TODO: Remove this when the list creation logic is removed - listElm = dom.getParent(selection.getNode(), 'ol,ul'); - if (listElm) { - listParent = listElm.parentNode; - - // If list is within a text block then split that block - if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) { - storeSelection(); - dom.split(listParent, listElm); - restoreSelection(); - } - } - }, - - // Override commands to use the text formatter engine - 'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) { - toggleFormat(command); - }, - - // Override commands to use the text formatter engine - 'ForeColor,HiliteColor,FontName': function(command, ui, value) { - toggleFormat(command, value); - }, - - FontSize: function(command, ui, value) { - var fontClasses, fontSizes; - - // Convert font size 1-7 to styles - if (value >= 1 && value <= 7) { - fontSizes = explode(settings.font_size_style_values); - fontClasses = explode(settings.font_size_classes); - - if (fontClasses) { - value = fontClasses[value - 1] || value; - } else { - value = fontSizes[value - 1] || value; - } - } - - toggleFormat(command, value); - }, - - RemoveFormat: function(command) { - formatter.remove(command); - }, - - mceBlockQuote: function() { - toggleFormat('blockquote'); - }, - - FormatBlock: function(command, ui, value) { - return toggleFormat(value || 'p'); - }, - - mceCleanup: function() { - var bookmark = selection.getBookmark(); - - editor.setContent(editor.getContent({ - cleanup: TRUE - }), { - cleanup: TRUE - }); - - selection.moveToBookmark(bookmark); - }, - - mceRemoveNode: function(command, ui, value) { - var node = value || selection.getNode(); - - // Make sure that the body node isn't removed - if (node != editor.getBody()) { - storeSelection(); - editor.dom.remove(node, TRUE); - restoreSelection(); - } - }, - - mceSelectNodeDepth: function(command, ui, value) { - var counter = 0; - - dom.getParent(selection.getNode(), function(node) { - if (node.nodeType == 1 && counter++ == value) { - selection.select(node); - return FALSE; - } - }, editor.getBody()); - }, - - mceSelectNode: function(command, ui, value) { - selection.select(value); - }, - - mceInsertContent: function(command, ui, value) { - InsertContent.insertAtCaret(editor, value); - }, - - mceInsertRawHTML: function(command, ui, value) { - selection.setContent('tiny_mce_marker'); - editor.setContent( - editor.getContent().replace(/tiny_mce_marker/g, function() { - return value; - }) - ); - }, - - mceToggleFormat: function(command, ui, value) { - toggleFormat(value); - }, - - mceSetContent: function(command, ui, value) { - editor.setContent(value); - }, - - 'Indent,Outdent': function(command) { - var intentValue, indentUnit, value; - - // Setup indent level - intentValue = settings.indentation; - indentUnit = /[a-z%]+$/i.exec(intentValue); - intentValue = parseInt(intentValue, 10); - - if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) { - // If forced_root_blocks is set to false we don't have a block to indent so lets create a div - if (!settings.forced_root_block && !dom.getParent(selection.getNode(), dom.isBlock)) { - formatter.apply('div'); - } - - each(selection.getSelectedBlocks(), function(element) { - if (dom.getContentEditable(element) === "false") { - return; - } - - if (element.nodeName !== "LI") { - var indentStyleName = editor.getParam('indent_use_margin', false) ? 'margin' : 'padding'; - indentStyleName = element.nodeName === 'TABLE' ? 'margin' : indentStyleName; - indentStyleName += dom.getStyle(element, 'direction', true) == 'rtl' ? 'Right' : 'Left'; - - if (command == 'outdent') { - value = Math.max(0, parseInt(element.style[indentStyleName] || 0, 10) - intentValue); - dom.setStyle(element, indentStyleName, value ? value + indentUnit : ''); - } else { - value = (parseInt(element.style[indentStyleName] || 0, 10) + intentValue) + indentUnit; - dom.setStyle(element, indentStyleName, value); - } - } - }); - } else { - execNativeCommand(command); - } - }, - - mceRepaint: function() {}, - - InsertHorizontalRule: function() { - editor.execCommand('mceInsertContent', false, '<hr />'); - }, - - mceToggleVisualAid: function() { - editor.hasVisual = !editor.hasVisual; - editor.addVisual(); - }, - - mceReplaceContent: function(command, ui, value) { - editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({ - format: 'text' - }))); - }, - - mceInsertLink: function(command, ui, value) { - var anchor; - - if (typeof value == 'string') { - value = { - href: value - }; - } - - anchor = dom.getParent(selection.getNode(), 'a'); - - // Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here. - value.href = value.href.replace(' ', '%20'); - - // Remove existing links if there could be child links or that the href isn't specified - if (!anchor || !value.href) { - formatter.remove('link'); - } - - // Apply new link to selection - if (value.href) { - formatter.apply('link', value, anchor); - } - }, - - selectAll: function() { - var root = dom.getRoot(), - rng; - - if (selection.getRng().setStart) { - var editingHost = dom.getParent(selection.getStart(), NodeType.isContentEditableTrue); - if (editingHost) { - rng = dom.createRng(); - rng.selectNodeContents(editingHost); - selection.setRng(rng); - } - } else { - // IE will render it's own root level block elements and sometimes - // even put font elements in them when the user starts typing. So we need to - // move the selection to a more suitable element from this: - // <body>|<p></p></body> to this: <body><p>|</p></body> - rng = selection.getRng(); - if (!rng.item) { - rng.moveToElementText(root); - rng.select(); - } - } - }, - - "delete": function() { - DeleteCommands.deleteCommand(editor); - }, - - "forwardDelete": function() { - DeleteCommands.forwardDeleteCommand(editor); - }, - - mceNewDocument: function() { - editor.setContent(''); - }, - - InsertLineBreak: function(command, ui, value) { - // We load the current event in from EnterKey.js when appropriate to heed - // certain event-specific variations such as ctrl-enter in a list - var evt = value; - var brElm, extraBr, marker; - var rng = selection.getRng(true); - new RangeUtils(dom).normalize(rng); - - var offset = rng.startOffset; - var container = rng.startContainer; - - // Resolve node index - if (container.nodeType == 1 && container.hasChildNodes()) { - var isAfterLastNodeInContainer = offset > container.childNodes.length - 1; - - container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container; - if (isAfterLastNodeInContainer && container.nodeType == 3) { - offset = container.nodeValue.length; - } else { - offset = 0; - } - } - - var parentBlock = dom.getParent(container, dom.isBlock); - var parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5 - var containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null; - var containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5 - - // Enter inside block contained within a LI then split or insert before/after LI - var isControlKey = evt && evt.ctrlKey; - if (containerBlockName == 'LI' && !isControlKey) { - parentBlock = containerBlock; - parentBlockName = containerBlockName; - } - - // Walks the parent block to the right and look for BR elements - function hasRightSideContent() { - var walker = new TreeWalker(container, parentBlock), - node; - var nonEmptyElementsMap = editor.schema.getNonEmptyElements(); - - while ((node = walker.next())) { - if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) { - return true; - } - } - } - - if (container && container.nodeType == 3 && offset >= container.nodeValue.length) { - // Insert extra BR element at the end block elements - if (!isOldIE && !hasRightSideContent()) { - brElm = dom.create('br'); - rng.insertNode(brElm); - rng.setStartAfter(brElm); - rng.setEndAfter(brElm); - extraBr = true; - } - } - - brElm = dom.create('br'); - rng.insertNode(brElm); - - // Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it - var documentMode = dom.doc.documentMode; - if (isOldIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) { - brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm); - } - - // Insert temp marker and scroll to that - marker = dom.create('span', {}, ' '); - brElm.parentNode.insertBefore(marker, brElm); - selection.scrollIntoView(marker); - dom.remove(marker); - - if (!extraBr) { - rng.setStartAfter(brElm); - rng.setEndAfter(brElm); - } else { - rng.setStartBefore(brElm); - rng.setEndBefore(brElm); - } - - selection.setRng(rng); - editor.undoManager.add(); - - return TRUE; - } - }); - - // Add queryCommandState overrides - addCommands({ - // Override justify commands - 'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) { - var name = 'align' + command.substring(7); - var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks(); - var matches = map(nodes, function(node) { - return !!formatter.matchNode(node, name); - }); - return inArray(matches, TRUE) !== -1; - }, - - 'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) { - return isFormatMatch(command); - }, - - mceBlockQuote: function() { - return isFormatMatch('blockquote'); - }, - - Outdent: function() { - var node; - - if (settings.inline_styles) { - if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) { - return TRUE; - } - - if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) { - return TRUE; - } - } - - return ( - queryCommandState('InsertUnorderedList') || - queryCommandState('InsertOrderedList') || - (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE')) - ); - }, - - 'InsertUnorderedList,InsertOrderedList': function(command) { - var list = dom.getParent(selection.getNode(), 'ul,ol'); - - return list && - ( - command === 'insertunorderedlist' && list.tagName === 'UL' || - command === 'insertorderedlist' && list.tagName === 'OL' - ); - } - }, 'state'); - - // Add queryCommandValue overrides - addCommands({ - 'FontSize,FontName': function(command) { - var value = 0, - parent; - - if ((parent = dom.getParent(selection.getNode(), 'span'))) { - if (command == 'fontsize') { - value = parent.style.fontSize; - } else { - value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase(); - } - } - - return value; - } - }, 'value'); - - // Add undo manager logic - addCommands({ - Undo: function() { - editor.undoManager.undo(); - }, - - Redo: function() { - editor.undoManager.redo(); - } - }); - }; - } - ); - - /** - * URI.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class handles parsing, modification and serialization of URI/URL strings. - * @class tinymce.util.URI - */ - define( - 'tinymce.core.util.URI', [ - 'global!document', - 'tinymce.core.util.Tools' - ], - function(document, Tools) { - var each = Tools.each, - trim = Tools.trim; - var queryParts = "source protocol authority userInfo user password host port relative path directory file query anchor".split(' '); - var DEFAULT_PORTS = { - 'ftp': 21, - 'http': 80, - 'https': 443, - 'mailto': 25 - }; - - /** - * Constructs a new URI instance. - * - * @constructor - * @method URI - * @param {String} url URI string to parse. - * @param {Object} settings Optional settings object. - */ - function URI(url, settings) { - var self = this, - baseUri, baseUrl; - - url = trim(url); - settings = self.settings = settings || {}; - baseUri = settings.base_uri; - - // Strange app protocol that isn't http/https or local anchor - // For example: mailto,skype,tel etc. - if (/^([\w\-]+):([^\/]{2})/i.test(url) || /^\s*#/.test(url)) { - self.source = url; - return; - } - - var isProtocolRelative = url.indexOf('//') === 0; - - // Absolute path with no host, fake host and protocol - if (url.indexOf('/') === 0 && !isProtocolRelative) { - url = (baseUri ? baseUri.protocol || 'http' : 'http') + '://mce_host' + url; - } - - // Relative path http:// or protocol relative //path - if (!/^[\w\-]*:?\/\//.test(url)) { - baseUrl = settings.base_uri ? settings.base_uri.path : new URI(document.location.href).directory; - if (settings.base_uri.protocol === "") { - url = '//mce_host' + self.toAbsPath(baseUrl, url); - } else { - url = /([^#?]*)([#?]?.*)/.exec(url); - url = ((baseUri && baseUri.protocol) || 'http') + '://mce_host' + self.toAbsPath(baseUrl, url[1]) + url[2]; - } - } - - // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri) - url = url.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something - - /*jshint maxlen: 255 */ - /*eslint max-len: 0 */ - url = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(url); - - each(queryParts, function(v, i) { - var part = url[i]; - - // Zope 3 workaround, they use @@something - if (part) { - part = part.replace(/\(mce_at\)/g, '@@'); - } - - self[v] = part; - }); - - if (baseUri) { - if (!self.protocol) { - self.protocol = baseUri.protocol; - } - - if (!self.userInfo) { - self.userInfo = baseUri.userInfo; - } - - if (!self.port && self.host === 'mce_host') { - self.port = baseUri.port; - } - - if (!self.host || self.host === 'mce_host') { - self.host = baseUri.host; - } - - self.source = ''; - } - - if (isProtocolRelative) { - self.protocol = ''; - } - - //t.path = t.path || '/'; - } - - URI.prototype = { - /** - * Sets the internal path part of the URI. - * - * @method setPath - * @param {string} path Path string to set. - */ - setPath: function(path) { - var self = this; - - path = /^(.*?)\/?(\w+)?$/.exec(path); - - // Update path parts - self.path = path[0]; - self.directory = path[1]; - self.file = path[2]; - - // Rebuild source - self.source = ''; - self.getURI(); - }, - - /** - * Converts the specified URI into a relative URI based on the current URI instance location. - * - * @method toRelative - * @param {String} uri URI to convert into a relative path/URI. - * @return {String} Relative URI from the point specified in the current URI instance. - * @example - * // Converts an absolute URL to an relative URL url will be somedir/somefile.htm - * var url = new tinymce.util.URI('http://www.site.com/dir/').toRelative('http://www.site.com/dir/somedir/somefile.htm'); - */ - toRelative: function(uri) { - var self = this, - output; - - if (uri === "./") { - return uri; - } - - uri = new URI(uri, { - base_uri: self - }); - - // Not on same domain/port or protocol - if ((uri.host != 'mce_host' && self.host != uri.host && uri.host) || self.port != uri.port || - (self.protocol != uri.protocol && uri.protocol !== "")) { - return uri.getURI(); - } - - var tu = self.getURI(), - uu = uri.getURI(); - - // Allow usage of the base_uri when relative_urls = true - if (tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu)) { - return tu; - } - - output = self.toRelPath(self.path, uri.path); - - // Add query - if (uri.query) { - output += '?' + uri.query; - } - - // Add anchor - if (uri.anchor) { - output += '#' + uri.anchor; - } - - return output; - }, - - /** - * Converts the specified URI into a absolute URI based on the current URI instance location. - * - * @method toAbsolute - * @param {String} uri URI to convert into a relative path/URI. - * @param {Boolean} noHost No host and protocol prefix. - * @return {String} Absolute URI from the point specified in the current URI instance. - * @example - * // Converts an relative URL to an absolute URL url will be http://www.site.com/dir/somedir/somefile.htm - * var url = new tinymce.util.URI('http://www.site.com/dir/').toAbsolute('somedir/somefile.htm'); - */ - toAbsolute: function(uri, noHost) { - uri = new URI(uri, { - base_uri: this - }); - - return uri.getURI(noHost && this.isSameOrigin(uri)); - }, - - /** - * Determine whether the given URI has the same origin as this URI. Based on RFC-6454. - * Supports default ports for protocols listed in DEFAULT_PORTS. Unsupported protocols will fail safe: they - * won't match, if the port specifications differ. - * - * @method isSameOrigin - * @param {tinymce.util.URI} uri Uri instance to compare. - * @returns {Boolean} True if the origins are the same. - */ - isSameOrigin: function(uri) { - if (this.host == uri.host && this.protocol == uri.protocol) { - if (this.port == uri.port) { - return true; - } - - var defaultPort = DEFAULT_PORTS[this.protocol]; - if (defaultPort && ((this.port || defaultPort) == (uri.port || defaultPort))) { - return true; - } - } - - return false; - }, - - /** - * Converts a absolute path into a relative path. - * - * @method toRelPath - * @param {String} base Base point to convert the path from. - * @param {String} path Absolute path to convert into a relative path. - */ - toRelPath: function(base, path) { - var items, breakPoint = 0, - out = '', - i, l; - - // Split the paths - base = base.substring(0, base.lastIndexOf('/')); - base = base.split('/'); - items = path.split('/'); - - if (base.length >= items.length) { - for (i = 0, l = base.length; i < l; i++) { - if (i >= items.length || base[i] != items[i]) { - breakPoint = i + 1; - break; - } - } - } - - if (base.length < items.length) { - for (i = 0, l = items.length; i < l; i++) { - if (i >= base.length || base[i] != items[i]) { - breakPoint = i + 1; - break; - } - } - } - - if (breakPoint === 1) { - return path; - } - - for (i = 0, l = base.length - (breakPoint - 1); i < l; i++) { - out += "../"; - } - - for (i = breakPoint - 1, l = items.length; i < l; i++) { - if (i != breakPoint - 1) { - out += "/" + items[i]; - } else { - out += items[i]; - } - } - - return out; - }, - - /** - * Converts a relative path into a absolute path. - * - * @method toAbsPath - * @param {String} base Base point to convert the path from. - * @param {String} path Relative path to convert into an absolute path. - */ - toAbsPath: function(base, path) { - var i, nb = 0, - o = [], - tr, outPath; - - // Split paths - tr = /\/$/.test(path) ? '/' : ''; - base = base.split('/'); - path = path.split('/'); - - // Remove empty chunks - each(base, function(k) { - if (k) { - o.push(k); - } - }); - - base = o; - - // Merge relURLParts chunks - for (i = path.length - 1, o = []; i >= 0; i--) { - // Ignore empty or . - if (path[i].length === 0 || path[i] === ".") { - continue; - } - - // Is parent - if (path[i] === '..') { - nb++; - continue; - } - - // Move up - if (nb > 0) { - nb--; - continue; - } - - o.push(path[i]); - } - - i = base.length - nb; - - // If /a/b/c or / - if (i <= 0) { - outPath = o.reverse().join('/'); - } else { - outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/'); - } - - // Add front / if it's needed - if (outPath.indexOf('/') !== 0) { - outPath = '/' + outPath; - } - - // Add traling / if it's needed - if (tr && outPath.lastIndexOf('/') !== outPath.length - 1) { - outPath += tr; - } - - return outPath; - }, - - /** - * Returns the full URI of the internal structure. - * - * @method getURI - * @param {Boolean} noProtoHost Optional no host and protocol part. Defaults to false. - */ - getURI: function(noProtoHost) { - var s, self = this; - - // Rebuild source - if (!self.source || noProtoHost) { - s = ''; - - if (!noProtoHost) { - if (self.protocol) { - s += self.protocol + '://'; - } else { - s += '//'; - } - - if (self.userInfo) { - s += self.userInfo + '@'; - } - - if (self.host) { - s += self.host; - } - - if (self.port) { - s += ':' + self.port; - } - } - - if (self.path) { - s += self.path; - } - - if (self.query) { - s += '?' + self.query; - } - - if (self.anchor) { - s += '#' + self.anchor; - } - - self.source = s; - } - - return self.source; - } - }; - - URI.parseDataUri = function(uri) { - var type, matches; - - uri = decodeURIComponent(uri).split(','); - - matches = /data:([^;]+)/.exec(uri[0]); - if (matches) { - type = matches[1]; - } - - return { - type: type, - data: uri[1] - }; - }; - - URI.getDocumentBaseUrl = function(loc) { - var baseUrl; - - // Pass applewebdata:// and other non web protocols though - if (loc.protocol.indexOf('http') !== 0 && loc.protocol !== 'file:') { - baseUrl = loc.href; - } else { - baseUrl = loc.protocol + '//' + loc.host + loc.pathname; - } - - if (/^[^:]+:\/\/\/?[^\/]+\//.test(baseUrl)) { - baseUrl = baseUrl.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, ''); - - if (!/[\/\\]$/.test(baseUrl)) { - baseUrl += '/'; - } - } - - return baseUrl; - }; - - return URI; - } - ); - - /** - * Class.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This utilitiy class is used for easier inheritance. - * - * Features: - * * Exposed super functions: this._super(); - * * Mixins - * * Dummy functions - * * Property functions: var value = object.value(); and object.value(newValue); - * * Static functions - * * Defaults settings - */ - define( - 'tinymce.core.util.Class', [ - "tinymce.core.util.Tools" - ], - function(Tools) { - var each = Tools.each, - extend = Tools.extend; - - var extendClass, initializing; - - function Class() {} - - // Provides classical inheritance, based on code made by John Resig - Class.extend = extendClass = function(prop) { - var self = this, - _super = self.prototype, - prototype, name, member; - - // The dummy class constructor - function Class() { - var i, mixins, mixin, self = this; - - // All construction is actually done in the init method - if (!initializing) { - // Run class constuctor - if (self.init) { - self.init.apply(self, arguments); - } - - // Run mixin constructors - mixins = self.Mixins; - if (mixins) { - i = mixins.length; - while (i--) { - mixin = mixins[i]; - if (mixin.init) { - mixin.init.apply(self, arguments); - } - } - } - } - } - - // Dummy function, needs to be extended in order to provide functionality - function dummy() { - return this; - } - - // Creates a overloaded method for the class - // this enables you to use this._super(); to call the super function - function createMethod(name, fn) { - return function() { - var self = this, - tmp = self._super, - ret; - - self._super = _super[name]; - ret = fn.apply(self, arguments); - self._super = tmp; - - return ret; - }; - } - - // Instantiate a base class (but only create the instance, - // don't run the init constructor) - initializing = true; - - /*eslint new-cap:0 */ - prototype = new self(); - initializing = false; - - // Add mixins - if (prop.Mixins) { - each(prop.Mixins, function(mixin) { - for (var name in mixin) { - if (name !== "init") { - prop[name] = mixin[name]; - } - } - }); - - if (_super.Mixins) { - prop.Mixins = _super.Mixins.concat(prop.Mixins); - } - } - - // Generate dummy methods - if (prop.Methods) { - each(prop.Methods.split(','), function(name) { - prop[name] = dummy; - }); - } - - // Generate property methods - if (prop.Properties) { - each(prop.Properties.split(','), function(name) { - var fieldName = '_' + name; - - prop[name] = function(value) { - var self = this, - undef; - - // Set value - if (value !== undef) { - self[fieldName] = value; - - return self; - } - - // Get value - return self[fieldName]; - }; - }); - } - - // Static functions - if (prop.Statics) { - each(prop.Statics, function(func, name) { - Class[name] = func; - }); - } - - // Default settings - if (prop.Defaults && _super.Defaults) { - prop.Defaults = extend({}, _super.Defaults, prop.Defaults); - } - - // Copy the properties over onto the new prototype - for (name in prop) { - member = prop[name]; - - if (typeof member == "function" && _super[name]) { - prototype[name] = createMethod(name, member); - } else { - prototype[name] = member; - } - } - - // Populate our constructed prototype object - Class.prototype = prototype; - - // Enforce the constructor to be what we expect - Class.constructor = Class; - - // And make this class extendible - Class.extend = extendClass; - - return Class; - }; - - return Class; - } - ); - /** - * EventDispatcher.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class lets you add/remove and fire events by name on the specified scope. This makes - * it easy to add event listener logic to any class. - * - * @class tinymce.util.EventDispatcher - * @example - * var eventDispatcher = new EventDispatcher(); - * - * eventDispatcher.on('click', function() {console.log('data');}); - * eventDispatcher.fire('click', {data: 123}); - */ - define( - 'tinymce.core.util.EventDispatcher', [ - "tinymce.core.util.Tools" - ], - function(Tools) { - var nativeEvents = Tools.makeMap( - "focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange " + - "mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover " + - "draggesture dragdrop drop drag submit " + - "compositionstart compositionend compositionupdate touchstart touchmove touchend", - ' ' - ); - - function Dispatcher(settings) { - var self = this, - scope, bindings = {}, - toggleEvent; - - function returnFalse() { - return false; - } - - function returnTrue() { - return true; - } - - settings = settings || {}; - scope = settings.scope || self; - toggleEvent = settings.toggleEvent || returnFalse; - - /** - * Fires the specified event by name. - * - * @method fire - * @param {String} name Name of the event to fire. - * @param {Object?} args Event arguments. - * @return {Object} Event args instance passed in. - * @example - * instance.fire('event', {...}); - */ - function fire(name, args) { - var handlers, i, l, callback; - - name = name.toLowerCase(); - args = args || {}; - args.type = name; - - // Setup target is there isn't one - if (!args.target) { - args.target = scope; - } - - // Add event delegation methods if they are missing - if (!args.preventDefault) { - // Add preventDefault method - args.preventDefault = function() { - args.isDefaultPrevented = returnTrue; - }; - - // Add stopPropagation - args.stopPropagation = function() { - args.isPropagationStopped = returnTrue; - }; - - // Add stopImmediatePropagation - args.stopImmediatePropagation = function() { - args.isImmediatePropagationStopped = returnTrue; - }; - - // Add event delegation states - args.isDefaultPrevented = returnFalse; - args.isPropagationStopped = returnFalse; - args.isImmediatePropagationStopped = returnFalse; - } - - if (settings.beforeFire) { - settings.beforeFire(args); - } - - handlers = bindings[name]; - if (handlers) { - for (i = 0, l = handlers.length; i < l; i++) { - callback = handlers[i]; - - // Unbind handlers marked with "once" - if (callback.once) { - off(name, callback.func); - } - - // Stop immediate propagation if needed - if (args.isImmediatePropagationStopped()) { - args.stopPropagation(); - return args; - } - - // If callback returns false then prevent default and stop all propagation - if (callback.func.call(scope, args) === false) { - args.preventDefault(); - return args; - } - } - } - - return args; - } - - /** - * Binds an event listener to a specific event by name. - * - * @method on - * @param {String} name Event name or space separated list of events to bind. - * @param {callback} callback Callback to be executed when the event occurs. - * @param {Boolean} first Optional flag if the event should be prepended. Use this with care. - * @return {Object} Current class instance. - * @example - * instance.on('event', function(e) { - * // Callback logic - * }); - */ - function on(name, callback, prepend, extra) { - var handlers, names, i; - - if (callback === false) { - callback = returnFalse; - } - - if (callback) { - callback = { - func: callback - }; - - if (extra) { - Tools.extend(callback, extra); - } - - names = name.toLowerCase().split(' '); - i = names.length; - while (i--) { - name = names[i]; - handlers = bindings[name]; - if (!handlers) { - handlers = bindings[name] = []; - toggleEvent(name, true); - } - - if (prepend) { - handlers.unshift(callback); - } else { - handlers.push(callback); - } - } - } - - return self; - } - - /** - * Unbinds an event listener to a specific event by name. - * - * @method off - * @param {String?} name Name of the event to unbind. - * @param {callback?} callback Callback to unbind. - * @return {Object} Current class instance. - * @example - * // Unbind specific callback - * instance.off('event', handler); - * - * // Unbind all listeners by name - * instance.off('event'); - * - * // Unbind all events - * instance.off(); - */ - function off(name, callback) { - var i, handlers, bindingName, names, hi; - - if (name) { - names = name.toLowerCase().split(' '); - i = names.length; - while (i--) { - name = names[i]; - handlers = bindings[name]; - - // Unbind all handlers - if (!name) { - for (bindingName in bindings) { - toggleEvent(bindingName, false); - delete bindings[bindingName]; - } - - return self; - } - - if (handlers) { - // Unbind all by name - if (!callback) { - handlers.length = 0; - } else { - // Unbind specific ones - hi = handlers.length; - while (hi--) { - if (handlers[hi].func === callback) { - handlers = handlers.slice(0, hi).concat(handlers.slice(hi + 1)); - bindings[name] = handlers; - } - } - } - - if (!handlers.length) { - toggleEvent(name, false); - delete bindings[name]; - } - } - } - } else { - for (name in bindings) { - toggleEvent(name, false); - } - - bindings = {}; - } - - return self; - } - - /** - * Binds an event listener to a specific event by name - * and automatically unbind the event once the callback fires. - * - * @method once - * @param {String} name Event name or space separated list of events to bind. - * @param {callback} callback Callback to be executed when the event occurs. - * @param {Boolean} first Optional flag if the event should be prepended. Use this with care. - * @return {Object} Current class instance. - * @example - * instance.once('event', function(e) { - * // Callback logic - * }); - */ - function once(name, callback, prepend) { - return on(name, callback, prepend, { - once: true - }); - } - - /** - * Returns true/false if the dispatcher has a event of the specified name. - * - * @method has - * @param {String} name Name of the event to check for. - * @return {Boolean} true/false if the event exists or not. - */ - function has(name) { - name = name.toLowerCase(); - return !(!bindings[name] || bindings[name].length === 0); - } - - // Expose - self.fire = fire; - self.on = on; - self.off = off; - self.once = once; - self.has = has; - } - - /** - * Returns true/false if the specified event name is a native browser event or not. - * - * @method isNative - * @param {String} name Name to check if it's native. - * @return {Boolean} true/false if the event is native or not. - * @static - */ - Dispatcher.isNative = function(name) { - return !!nativeEvents[name.toLowerCase()]; - }; - - return Dispatcher; - } - ); - - /** - * Observable.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This mixin will add event binding logic to classes. - * - * @mixin tinymce.util.Observable - */ - define( - 'tinymce.core.util.Observable', [ - "tinymce.core.util.EventDispatcher" - ], - function(EventDispatcher) { - function getEventDispatcher(obj) { - if (!obj._eventDispatcher) { - obj._eventDispatcher = new EventDispatcher({ - scope: obj, - toggleEvent: function(name, state) { - if (EventDispatcher.isNative(name) && obj.toggleNativeEvent) { - obj.toggleNativeEvent(name, state); - } - } - }); - } - - return obj._eventDispatcher; - } - - return { - /** - * Fires the specified event by name. Consult the - * <a href="/docs/advanced/events">event reference</a> for more details on each event. - * - * @method fire - * @param {String} name Name of the event to fire. - * @param {Object?} args Event arguments. - * @param {Boolean?} bubble True/false if the event is to be bubbled. - * @return {Object} Event args instance passed in. - * @example - * instance.fire('event', {...}); - */ - fire: function(name, args, bubble) { - var self = this; - - // Prevent all events except the remove event after the instance has been removed - if (self.removed && name !== "remove") { - return args; - } - - args = getEventDispatcher(self).fire(name, args, bubble); - - // Bubble event up to parents - if (bubble !== false && self.parent) { - var parent = self.parent(); - while (parent && !args.isPropagationStopped()) { - parent.fire(name, args, false); - parent = parent.parent(); - } - } - - return args; - }, - - /** - * Binds an event listener to a specific event by name. Consult the - * <a href="/docs/advanced/events">event reference</a> for more details on each event. - * - * @method on - * @param {String} name Event name or space separated list of events to bind. - * @param {callback} callback Callback to be executed when the event occurs. - * @param {Boolean} first Optional flag if the event should be prepended. Use this with care. - * @return {Object} Current class instance. - * @example - * instance.on('event', function(e) { - * // Callback logic - * }); - */ - on: function(name, callback, prepend) { - return getEventDispatcher(this).on(name, callback, prepend); - }, - - /** - * Unbinds an event listener to a specific event by name. Consult the - * <a href="/docs/advanced/events">event reference</a> for more details on each event. - * - * @method off - * @param {String?} name Name of the event to unbind. - * @param {callback?} callback Callback to unbind. - * @return {Object} Current class instance. - * @example - * // Unbind specific callback - * instance.off('event', handler); - * - * // Unbind all listeners by name - * instance.off('event'); - * - * // Unbind all events - * instance.off(); - */ - off: function(name, callback) { - return getEventDispatcher(this).off(name, callback); - }, - - /** - * Bind the event callback and once it fires the callback is removed. Consult the - * <a href="/docs/advanced/events">event reference</a> for more details on each event. - * - * @method once - * @param {String} name Name of the event to bind. - * @param {callback} callback Callback to bind only once. - * @return {Object} Current class instance. - */ - once: function(name, callback) { - return getEventDispatcher(this).once(name, callback); - }, - - /** - * Returns true/false if the object has a event of the specified name. - * - * @method hasEventListeners - * @param {String} name Name of the event to check for. - * @return {Boolean} true/false if the event exists or not. - */ - hasEventListeners: function(name) { - return getEventDispatcher(this).has(name); - } - }; - } - ); - /** - * Binding.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class gets dynamically extended to provide a binding between two models. This makes it possible to - * sync the state of two properties in two models by a layer of abstraction. - * - * @private - * @class tinymce.data.Binding - */ - define( - 'tinymce.core.data.Binding', [], - function() { - /** - * Constructs a new bidning. - * - * @constructor - * @method Binding - * @param {Object} settings Settings to the binding. - */ - function Binding(settings) { - this.create = settings.create; - } - - /** - * Creates a binding for a property on a model. - * - * @method create - * @param {tinymce.data.ObservableObject} model Model to create binding to. - * @param {String} name Name of property to bind. - * @return {tinymce.data.Binding} Binding instance. - */ - Binding.create = function(model, name) { - return new Binding({ - create: function(otherModel, otherName) { - var bindings; - - function fromSelfToOther(e) { - otherModel.set(otherName, e.value); - } - - function fromOtherToSelf(e) { - model.set(name, e.value); - } - - otherModel.on('change:' + otherName, fromOtherToSelf); - model.on('change:' + name, fromSelfToOther); - - // Keep track of the bindings - bindings = otherModel._bindings; - - if (!bindings) { - bindings = otherModel._bindings = []; - - otherModel.on('destroy', function() { - var i = bindings.length; - - while (i--) { - bindings[i](); - } - }); - } - - bindings.push(function() { - model.off('change:' + name, fromSelfToOther); - }); - - return model.get(name); - } - }); - }; - - return Binding; - } - ); - /** - * ObservableObject.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class is a object that is observable when properties changes a change event gets emitted. - * - * @private - * @class tinymce.data.ObservableObject - */ - define( - 'tinymce.core.data.ObservableObject', [ - 'tinymce.core.data.Binding', - 'tinymce.core.util.Class', - 'tinymce.core.util.Observable', - 'tinymce.core.util.Tools' - ], - function(Binding, Class, Observable, Tools) { - function isNode(node) { - return node.nodeType > 0; - } - - // Todo: Maybe this should be shallow compare since it might be huge object references - function isEqual(a, b) { - var k, checked; - - // Strict equals - if (a === b) { - return true; - } - - // Compare null - if (a === null || b === null) { - return a === b; - } - - // Compare number, boolean, string, undefined - if (typeof a !== "object" || typeof b !== "object") { - return a === b; - } - - // Compare arrays - if (Tools.isArray(b)) { - if (a.length !== b.length) { - return false; - } - - k = a.length; - while (k--) { - if (!isEqual(a[k], b[k])) { - return false; - } - } - } - - // Shallow compare nodes - if (isNode(a) || isNode(b)) { - return a === b; - } - - // Compare objects - checked = {}; - for (k in b) { - if (!isEqual(a[k], b[k])) { - return false; - } - - checked[k] = true; - } - - for (k in a) { - if (!checked[k] && !isEqual(a[k], b[k])) { - return false; - } - } - - return true; - } - - return Class.extend({ - Mixins: [Observable], - - /** - * Constructs a new observable object instance. - * - * @constructor - * @param {Object} data Initial data for the object. - */ - init: function(data) { - var name, value; - - data = data || {}; - - for (name in data) { - value = data[name]; - - if (value instanceof Binding) { - data[name] = value.create(this, name); - } - } - - this.data = data; - }, - - /** - * Sets a property on the value this will call - * observers if the value is a change from the current value. - * - * @method set - * @param {String/object} name Name of the property to set or a object of items to set. - * @param {Object} value Value to set for the property. - * @return {tinymce.data.ObservableObject} Observable object instance. - */ - set: function(name, value) { - var key, args, oldValue = this.data[name]; - - if (value instanceof Binding) { - value = value.create(this, name); - } - - if (typeof name === "object") { - for (key in name) { - this.set(key, name[key]); - } - - return this; - } - - if (!isEqual(oldValue, value)) { - this.data[name] = value; - - args = { - target: this, - name: name, - value: value, - oldValue: oldValue - }; - - this.fire('change:' + name, args); - this.fire('change', args); - } - - return this; - }, - - /** - * Gets a property by name. - * - * @method get - * @param {String} name Name of the property to get. - * @return {Object} Object value of propery. - */ - get: function(name) { - return this.data[name]; - }, - - /** - * Returns true/false if the specified property exists. - * - * @method has - * @param {String} name Name of the property to check for. - * @return {Boolean} true/false if the item exists. - */ - has: function(name) { - return name in this.data; - }, - - /** - * Returns a dynamic property binding for the specified property name. This makes - * it possible to sync the state of two properties in two ObservableObject instances. - * - * @method bind - * @param {String} name Name of the property to sync with the property it's inserted to. - * @return {tinymce.data.Binding} Data binding instance. - */ - bind: function(name) { - return Binding.create(this, name); - }, - - /** - * Destroys the observable object and fires the "destroy" - * event and clean up any internal resources. - * - * @method destroy - */ - destroy: function() { - this.fire('destroy'); - } - }); - } - ); - /** - * Selector.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /*eslint no-nested-ternary:0 */ - - /** - * Selector engine, enables you to select controls by using CSS like expressions. - * We currently only support basic CSS expressions to reduce the size of the core - * and the ones we support should be enough for most cases. - * - * @example - * Supported expressions: - * element - * element#name - * element.class - * element[attr] - * element[attr*=value] - * element[attr~=value] - * element[attr!=value] - * element[attr^=value] - * element[attr$=value] - * element:<state> - * element:not(<expression>) - * element:first - * element:last - * element:odd - * element:even - * element element - * element > element - * - * @class tinymce.ui.Selector - */ - define( - 'tinymce.core.ui.Selector', [ - "tinymce.core.util.Class" - ], - function(Class) { - "use strict"; - - /** - * Produces an array with a unique set of objects. It will not compare the values - * but the references of the objects. - * - * @private - * @method unqiue - * @param {Array} array Array to make into an array with unique items. - * @return {Array} Array with unique items. - */ - function unique(array) { - var uniqueItems = [], - i = array.length, - item; - - while (i--) { - item = array[i]; - - if (!item.__checked) { - uniqueItems.push(item); - item.__checked = 1; - } - } - - i = uniqueItems.length; - while (i--) { - delete uniqueItems[i].__checked; - } - - return uniqueItems; - } - - var expression = /^([\w\\*]+)?(?:#([\w\-\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i; - - /*jshint maxlen:255 */ - /*eslint max-len:0 */ - var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, - whiteSpace = /^\s*|\s*$/g, - Collection; - - var Selector = Class.extend({ - /** - * Constructs a new Selector instance. - * - * @constructor - * @method init - * @param {String} selector CSS like selector expression. - */ - init: function(selector) { - var match = this.match; - - function compileNameFilter(name) { - if (name) { - name = name.toLowerCase(); - - return function(item) { - return name === '*' || item.type === name; - }; - } - } - - function compileIdFilter(id) { - if (id) { - return function(item) { - return item._name === id; - }; - } - } - - function compileClassesFilter(classes) { - if (classes) { - classes = classes.split('.'); - - return function(item) { - var i = classes.length; - - while (i--) { - if (!item.classes.contains(classes[i])) { - return false; - } - } - - return true; - }; - } - } - - function compileAttrFilter(name, cmp, check) { - if (name) { - return function(item) { - var value = item[name] ? item[name]() : ''; - - return !cmp ? !!check : - cmp === "=" ? value === check : - cmp === "*=" ? value.indexOf(check) >= 0 : - cmp === "~=" ? (" " + value + " ").indexOf(" " + check + " ") >= 0 : - cmp === "!=" ? value != check : - cmp === "^=" ? value.indexOf(check) === 0 : - cmp === "$=" ? value.substr(value.length - check.length) === check : - false; - }; - } - } - - function compilePsuedoFilter(name) { - var notSelectors; - - if (name) { - name = /(?:not\((.+)\))|(.+)/i.exec(name); - - if (!name[1]) { - name = name[2]; - - return function(item, index, length) { - return name === 'first' ? index === 0 : - name === 'last' ? index === length - 1 : - name === 'even' ? index % 2 === 0 : - name === 'odd' ? index % 2 === 1 : - item[name] ? item[name]() : - false; - }; - } - - // Compile not expression - notSelectors = parseChunks(name[1], []); - - return function(item) { - return !match(item, notSelectors); - }; - } - } - - function compile(selector, filters, direct) { - var parts; - - function add(filter) { - if (filter) { - filters.push(filter); - } - } - - // Parse expression into parts - parts = expression.exec(selector.replace(whiteSpace, '')); - - add(compileNameFilter(parts[1])); - add(compileIdFilter(parts[2])); - add(compileClassesFilter(parts[3])); - add(compileAttrFilter(parts[4], parts[5], parts[6])); - add(compilePsuedoFilter(parts[7])); - - // Mark the filter with pseudo for performance - filters.pseudo = !!parts[7]; - filters.direct = direct; - - return filters; - } - - // Parser logic based on Sizzle by John Resig - function parseChunks(selector, selectors) { - var parts = [], - extra, matches, i; - - do { - chunker.exec(""); - matches = chunker.exec(selector); - - if (matches) { - selector = matches[3]; - parts.push(matches[1]); - - if (matches[2]) { - extra = matches[3]; - break; - } - } - } while (matches); - - if (extra) { - parseChunks(extra, selectors); - } - - selector = []; - for (i = 0; i < parts.length; i++) { - if (parts[i] != '>') { - selector.push(compile(parts[i], [], parts[i - 1] === '>')); - } - } - - selectors.push(selector); - - return selectors; - } - - this._selectors = parseChunks(selector, []); - }, - - /** - * Returns true/false if the selector matches the specified control. - * - * @method match - * @param {tinymce.ui.Control} control Control to match against the selector. - * @param {Array} selectors Optional array of selectors, mostly used internally. - * @return {Boolean} true/false state if the control matches or not. - */ - match: function(control, selectors) { - var i, l, si, sl, selector, fi, fl, filters, index, length, siblings, count, item; - - selectors = selectors || this._selectors; - for (i = 0, l = selectors.length; i < l; i++) { - selector = selectors[i]; - sl = selector.length; - item = control; - count = 0; - - for (si = sl - 1; si >= 0; si--) { - filters = selector[si]; - - while (item) { - // Find the index and length since a pseudo filter like :first needs it - if (filters.pseudo) { - siblings = item.parent().items(); - index = length = siblings.length; - while (index--) { - if (siblings[index] === item) { - break; - } - } - } - - for (fi = 0, fl = filters.length; fi < fl; fi++) { - if (!filters[fi](item, index, length)) { - fi = fl + 1; - break; - } - } - - if (fi === fl) { - count++; - break; - } else { - // If it didn't match the right most expression then - // break since it's no point looking at the parents - if (si === sl - 1) { - break; - } - } - - item = item.parent(); - } - } - - // If we found all selectors then return true otherwise continue looking - if (count === sl) { - return true; - } - } - - return false; - }, - - /** - * Returns a tinymce.ui.Collection with matches of the specified selector inside the specified container. - * - * @method find - * @param {tinymce.ui.Control} container Container to look for items in. - * @return {tinymce.ui.Collection} Collection with matched elements. - */ - find: function(container) { - var matches = [], - i, l, selectors = this._selectors; - - function collect(items, selector, index) { - var i, l, fi, fl, item, filters = selector[index]; - - for (i = 0, l = items.length; i < l; i++) { - item = items[i]; - - // Run each filter against the item - for (fi = 0, fl = filters.length; fi < fl; fi++) { - if (!filters[fi](item, i, l)) { - fi = fl + 1; - break; - } - } - - // All filters matched the item - if (fi === fl) { - // Matched item is on the last expression like: panel toolbar [button] - if (index == selector.length - 1) { - matches.push(item); - } else { - // Collect next expression type - if (item.items) { - collect(item.items(), selector, index + 1); - } - } - } else if (filters.direct) { - return; - } - - // Collect child items - if (item.items) { - collect(item.items(), selector, index); - } - } - } - - if (container.items) { - for (i = 0, l = selectors.length; i < l; i++) { - collect(container.items(), selectors[i], 0); - } - - // Unique the matches if needed - if (l > 1) { - matches = unique(matches); - } - } - - // Fix for circular reference - if (!Collection) { - // TODO: Fix me! - Collection = Selector.Collection; - } - - return new Collection(matches); - } - }); - - return Selector; - } - ); - - /** - * Collection.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Control collection, this class contains control instances and it enables you to - * perform actions on all the contained items. This is very similar to how jQuery works. - * - * @example - * someCollection.show().disabled(true); - * - * @class tinymce.ui.Collection - */ - define( - 'tinymce.core.ui.Collection', [ - "tinymce.core.util.Tools", - "tinymce.core.ui.Selector", - "tinymce.core.util.Class" - ], - function(Tools, Selector, Class) { - "use strict"; - - var Collection, proto, push = Array.prototype.push, - slice = Array.prototype.slice; - - proto = { - /** - * Current number of contained control instances. - * - * @field length - * @type Number - */ - length: 0, - - /** - * Constructor for the collection. - * - * @constructor - * @method init - * @param {Array} items Optional array with items to add. - */ - init: function(items) { - if (items) { - this.add(items); - } - }, - - /** - * Adds new items to the control collection. - * - * @method add - * @param {Array} items Array if items to add to collection. - * @return {tinymce.ui.Collection} Current collection instance. - */ - add: function(items) { - var self = this; - - // Force single item into array - if (!Tools.isArray(items)) { - if (items instanceof Collection) { - self.add(items.toArray()); - } else { - push.call(self, items); - } - } else { - push.apply(self, items); - } - - return self; - }, - - /** - * Sets the contents of the collection. This will remove any existing items - * and replace them with the ones specified in the input array. - * - * @method set - * @param {Array} items Array with items to set into the Collection. - * @return {tinymce.ui.Collection} Collection instance. - */ - set: function(items) { - var self = this, - len = self.length, - i; - - self.length = 0; - self.add(items); - - // Remove old entries - for (i = self.length; i < len; i++) { - delete self[i]; - } - - return self; - }, - - /** - * Filters the collection item based on the specified selector expression or selector function. - * - * @method filter - * @param {String} selector Selector expression to filter items by. - * @return {tinymce.ui.Collection} Collection containing the filtered items. - */ - filter: function(selector) { - var self = this, - i, l, matches = [], - item, match; - - // Compile string into selector expression - if (typeof selector === "string") { - selector = new Selector(selector); - - match = function(item) { - return selector.match(item); - }; - } else { - // Use selector as matching function - match = selector; - } - - for (i = 0, l = self.length; i < l; i++) { - item = self[i]; - - if (match(item)) { - matches.push(item); - } - } - - return new Collection(matches); - }, - - /** - * Slices the items within the collection. - * - * @method slice - * @param {Number} index Index to slice at. - * @param {Number} len Optional length to slice. - * @return {tinymce.ui.Collection} Current collection. - */ - slice: function() { - return new Collection(slice.apply(this, arguments)); - }, - - /** - * Makes the current collection equal to the specified index. - * - * @method eq - * @param {Number} index Index of the item to set the collection to. - * @return {tinymce.ui.Collection} Current collection. - */ - eq: function(index) { - return index === -1 ? this.slice(index) : this.slice(index, +index + 1); - }, - - /** - * Executes the specified callback on each item in collection. - * - * @method each - * @param {function} callback Callback to execute for each item in collection. - * @return {tinymce.ui.Collection} Current collection instance. - */ - each: function(callback) { - Tools.each(this, callback); - - return this; - }, - - /** - * Returns an JavaScript array object of the contents inside the collection. - * - * @method toArray - * @return {Array} Array with all items from collection. - */ - toArray: function() { - return Tools.toArray(this); - }, - - /** - * Finds the index of the specified control or return -1 if it isn't in the collection. - * - * @method indexOf - * @param {Control} ctrl Control instance to look for. - * @return {Number} Index of the specified control or -1. - */ - indexOf: function(ctrl) { - var self = this, - i = self.length; - - while (i--) { - if (self[i] === ctrl) { - break; - } - } - - return i; - }, - - /** - * Returns a new collection of the contents in reverse order. - * - * @method reverse - * @return {tinymce.ui.Collection} Collection instance with reversed items. - */ - reverse: function() { - return new Collection(Tools.toArray(this).reverse()); - }, - - /** - * Returns true/false if the class exists or not. - * - * @method hasClass - * @param {String} cls Class to check for. - * @return {Boolean} true/false state if the class exists or not. - */ - hasClass: function(cls) { - return this[0] ? this[0].classes.contains(cls) : false; - }, - - /** - * Sets/gets the specific property on the items in the collection. The same as executing control.<property>(<value>); - * - * @method prop - * @param {String} name Property name to get/set. - * @param {Object} value Optional object value to set. - * @return {tinymce.ui.Collection} Current collection instance or value of the first item on a get operation. - */ - prop: function(name, value) { - var self = this, - undef, item; - - if (value !== undef) { - self.each(function(item) { - if (item[name]) { - item[name](value); - } - }); - - return self; - } - - item = self[0]; - - if (item && item[name]) { - return item[name](); - } - }, - - /** - * Executes the specific function name with optional arguments an all items in collection if it exists. - * - * @example collection.exec("myMethod", arg1, arg2, arg3); - * @method exec - * @param {String} name Name of the function to execute. - * @param {Object} ... Multiple arguments to pass to each function. - * @return {tinymce.ui.Collection} Current collection. - */ - exec: function(name) { - var self = this, - args = Tools.toArray(arguments).slice(1); - - self.each(function(item) { - if (item[name]) { - item[name].apply(item, args); - } - }); - - return self; - }, - - /** - * Remove all items from collection and DOM. - * - * @method remove - * @return {tinymce.ui.Collection} Current collection. - */ - remove: function() { - var i = this.length; - - while (i--) { - this[i].remove(); - } - - return this; - }, - - /** - * Adds a class to all items in the collection. - * - * @method addClass - * @param {String} cls Class to add to each item. - * @return {tinymce.ui.Collection} Current collection instance. - */ - addClass: function(cls) { - return this.each(function(item) { - item.classes.add(cls); - }); - }, - - /** - * Removes the specified class from all items in collection. - * - * @method removeClass - * @param {String} cls Class to remove from each item. - * @return {tinymce.ui.Collection} Current collection instance. - */ - removeClass: function(cls) { - return this.each(function(item) { - item.classes.remove(cls); - }); - } - - /** - * Fires the specified event by name and arguments on the control. This will execute all - * bound event handlers. - * - * @method fire - * @param {String} name Name of the event to fire. - * @param {Object} args Optional arguments to pass to the event. - * @return {tinymce.ui.Collection} Current collection instance. - */ - // fire: function(event, args) {}, -- Generated by code below - - /** - * Binds a callback to the specified event. This event can both be - * native browser events like "click" or custom ones like PostRender. - * - * The callback function will have two parameters the first one being the control that received the event - * the second one will be the event object either the browsers native event object or a custom JS object. - * - * @method on - * @param {String} name Name of the event to bind. For example "click". - * @param {String/function} callback Callback function to execute ones the event occurs. - * @return {tinymce.ui.Collection} Current collection instance. - */ - // on: function(name, callback) {}, -- Generated by code below - - /** - * Unbinds the specified event and optionally a specific callback. If you omit the name - * parameter all event handlers will be removed. If you omit the callback all event handles - * by the specified name will be removed. - * - * @method off - * @param {String} name Optional name for the event to unbind. - * @param {function} callback Optional callback function to unbind. - * @return {tinymce.ui.Collection} Current collection instance. - */ - // off: function(name, callback) {}, -- Generated by code below - - /** - * Shows the items in the current collection. - * - * @method show - * @return {tinymce.ui.Collection} Current collection instance. - */ - // show: function() {}, -- Generated by code below - - /** - * Hides the items in the current collection. - * - * @method hide - * @return {tinymce.ui.Collection} Current collection instance. - */ - // hide: function() {}, -- Generated by code below - - /** - * Sets/gets the text contents of the items in the current collection. - * - * @method text - * @return {tinymce.ui.Collection} Current collection instance or text value of the first item on a get operation. - */ - // text: function(value) {}, -- Generated by code below - - /** - * Sets/gets the name contents of the items in the current collection. - * - * @method name - * @return {tinymce.ui.Collection} Current collection instance or name value of the first item on a get operation. - */ - // name: function(value) {}, -- Generated by code below - - /** - * Sets/gets the disabled state on the items in the current collection. - * - * @method disabled - * @return {tinymce.ui.Collection} Current collection instance or disabled state of the first item on a get operation. - */ - // disabled: function(state) {}, -- Generated by code below - - /** - * Sets/gets the active state on the items in the current collection. - * - * @method active - * @return {tinymce.ui.Collection} Current collection instance or active state of the first item on a get operation. - */ - // active: function(state) {}, -- Generated by code below - - /** - * Sets/gets the selected state on the items in the current collection. - * - * @method selected - * @return {tinymce.ui.Collection} Current collection instance or selected state of the first item on a get operation. - */ - // selected: function(state) {}, -- Generated by code below - - /** - * Sets/gets the selected state on the items in the current collection. - * - * @method visible - * @return {tinymce.ui.Collection} Current collection instance or visible state of the first item on a get operation. - */ - // visible: function(state) {}, -- Generated by code below - }; - - // Extend tinymce.ui.Collection prototype with some generated control specific methods - Tools.each('fire on off show hide append prepend before after reflow'.split(' '), function(name) { - proto[name] = function() { - var args = Tools.toArray(arguments); - - this.each(function(ctrl) { - if (name in ctrl) { - ctrl[name].apply(ctrl, args); - } - }); - - return this; - }; - }); - - // Extend tinymce.ui.Collection prototype with some property methods - Tools.each('text name disabled active selected checked visible parent value data'.split(' '), function(name) { - proto[name] = function(value) { - return this.prop(name, value); - }; - }); - - // Create class based on the new prototype - Collection = Class.extend(proto); - - // Stick Collection into Selector to prevent circual references - Selector.Collection = Collection; - - return Collection; - } - ); - /** - * DomUtils.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Private UI DomUtils proxy. - * - * @private - * @class tinymce.ui.DomUtils - */ - define( - 'tinymce.core.ui.DomUtils', [ - "tinymce.core.Env", - "tinymce.core.util.Tools", - "tinymce.core.dom.DOMUtils" - ], - function(Env, Tools, DOMUtils) { - "use strict"; - - var count = 0; - - var funcs = { - id: function() { - return 'mceu_' + (count++); - }, - - create: function(name, attrs, children) { - var elm = document.createElement(name); - - DOMUtils.DOM.setAttribs(elm, attrs); - - if (typeof children === 'string') { - elm.innerHTML = children; - } else { - Tools.each(children, function(child) { - if (child.nodeType) { - elm.appendChild(child); - } - }); - } - - return elm; - }, - - createFragment: function(html) { - return DOMUtils.DOM.createFragment(html); - }, - - getWindowSize: function() { - return DOMUtils.DOM.getViewPort(); - }, - - getSize: function(elm) { - var width, height; - - if (elm.getBoundingClientRect) { - var rect = elm.getBoundingClientRect(); - - width = Math.max(rect.width || (rect.right - rect.left), elm.offsetWidth); - height = Math.max(rect.height || (rect.bottom - rect.bottom), elm.offsetHeight); - } else { - width = elm.offsetWidth; - height = elm.offsetHeight; - } - - return { - width: width, - height: height - }; - }, - - getPos: function(elm, root) { - return DOMUtils.DOM.getPos(elm, root || funcs.getContainer()); - }, - - getContainer: function() { - return Env.container ? Env.container : document.body; - }, - - getViewPort: function(win) { - return DOMUtils.DOM.getViewPort(win); - }, - - get: function(id) { - return document.getElementById(id); - }, - - addClass: function(elm, cls) { - return DOMUtils.DOM.addClass(elm, cls); - }, - - removeClass: function(elm, cls) { - return DOMUtils.DOM.removeClass(elm, cls); - }, - - hasClass: function(elm, cls) { - return DOMUtils.DOM.hasClass(elm, cls); - }, - - toggleClass: function(elm, cls, state) { - return DOMUtils.DOM.toggleClass(elm, cls, state); - }, - - css: function(elm, name, value) { - return DOMUtils.DOM.setStyle(elm, name, value); - }, - - getRuntimeStyle: function(elm, name) { - return DOMUtils.DOM.getStyle(elm, name, true); - }, - - on: function(target, name, callback, scope) { - return DOMUtils.DOM.bind(target, name, callback, scope); - }, - - off: function(target, name, callback) { - return DOMUtils.DOM.unbind(target, name, callback); - }, - - fire: function(target, name, args) { - return DOMUtils.DOM.fire(target, name, args); - }, - - innerHtml: function(elm, html) { - // Workaround for <div> in <p> bug on IE 8 #6178 - DOMUtils.DOM.setHTML(elm, html); - } - }; - - return funcs; - } - ); - /** - * BoxUtils.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Utility class for box parsing and measuring. - * - * @private - * @class tinymce.ui.BoxUtils - */ - define( - 'tinymce.core.ui.BoxUtils', [], - function() { - "use strict"; - - return { - /** - * Parses the specified box value. A box value contains 1-4 properties in clockwise order. - * - * @method parseBox - * @param {String/Number} value Box value "0 1 2 3" or "0" etc. - * @return {Object} Object with top/right/bottom/left properties. - * @private - */ - parseBox: function(value) { - var len, radix = 10; - - if (!value) { - return; - } - - if (typeof value === "number") { - value = value || 0; - - return { - top: value, - left: value, - bottom: value, - right: value - }; - } - - value = value.split(' '); - len = value.length; - - if (len === 1) { - value[1] = value[2] = value[3] = value[0]; - } else if (len === 2) { - value[2] = value[0]; - value[3] = value[1]; - } else if (len === 3) { - value[3] = value[1]; - } - - return { - top: parseInt(value[0], radix) || 0, - right: parseInt(value[1], radix) || 0, - bottom: parseInt(value[2], radix) || 0, - left: parseInt(value[3], radix) || 0 - }; - }, - - measureBox: function(elm, prefix) { - function getStyle(name) { - var defaultView = document.defaultView; - - if (defaultView) { - // Remove camelcase - name = name.replace(/[A-Z]/g, function(a) { - return '-' + a; - }); - - return defaultView.getComputedStyle(elm, null).getPropertyValue(name); - } - - return elm.currentStyle[name]; - } - - function getSide(name) { - var val = parseFloat(getStyle(name), 10); - - return isNaN(val) ? 0 : val; - } - - return { - top: getSide(prefix + "TopWidth"), - right: getSide(prefix + "RightWidth"), - bottom: getSide(prefix + "BottomWidth"), - left: getSide(prefix + "LeftWidth") - }; - } - }; - } - ); - - /** - * ClassList.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Handles adding and removal of classes. - * - * @private - * @class tinymce.ui.ClassList - */ - define( - 'tinymce.core.ui.ClassList', [ - "tinymce.core.util.Tools" - ], - function(Tools) { - "use strict"; - - function noop() {} - - /** - * Constructs a new class list the specified onchange - * callback will be executed when the class list gets modifed. - * - * @constructor ClassList - * @param {function} onchange Onchange callback to be executed. - */ - function ClassList(onchange) { - this.cls = []; - this.cls._map = {}; - this.onchange = onchange || noop; - this.prefix = ''; - } - - Tools.extend(ClassList.prototype, { - /** - * Adds a new class to the class list. - * - * @method add - * @param {String} cls Class to be added. - * @return {tinymce.ui.ClassList} Current class list instance. - */ - add: function(cls) { - if (cls && !this.contains(cls)) { - this.cls._map[cls] = true; - this.cls.push(cls); - this._change(); - } - - return this; - }, - - /** - * Removes the specified class from the class list. - * - * @method remove - * @param {String} cls Class to be removed. - * @return {tinymce.ui.ClassList} Current class list instance. - */ - remove: function(cls) { - if (this.contains(cls)) { - for (var i = 0; i < this.cls.length; i++) { - if (this.cls[i] === cls) { - break; - } - } - - this.cls.splice(i, 1); - delete this.cls._map[cls]; - this._change(); - } - - return this; - }, - - /** - * Toggles a class in the class list. - * - * @method toggle - * @param {String} cls Class to be added/removed. - * @param {Boolean} state Optional state if it should be added/removed. - * @return {tinymce.ui.ClassList} Current class list instance. - */ - toggle: function(cls, state) { - var curState = this.contains(cls); - - if (curState !== state) { - if (curState) { - this.remove(cls); - } else { - this.add(cls); - } - - this._change(); - } - - return this; - }, - - /** - * Returns true if the class list has the specified class. - * - * @method contains - * @param {String} cls Class to look for. - * @return {Boolean} true/false if the class exists or not. - */ - contains: function(cls) { - return !!this.cls._map[cls]; - }, - - /** - * Returns a space separated list of classes. - * - * @method toString - * @return {String} Space separated list of classes. - */ - - _change: function() { - delete this.clsValue; - this.onchange.call(this); - } - }); - - // IE 8 compatibility - ClassList.prototype.toString = function() { - var value; - - if (this.clsValue) { - return this.clsValue; - } - - value = ''; - for (var i = 0; i < this.cls.length; i++) { - if (i > 0) { - value += ' '; - } - - value += this.prefix + this.cls[i]; - } - - return value; - }; - - return ClassList; - } - ); - /** - * ReflowQueue.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class will automatically reflow controls on the next animation frame within a few milliseconds on older browsers. - * If the user manually reflows then the automatic reflow will be cancelled. This class is used internally when various control states - * changes that triggers a reflow. - * - * @class tinymce.ui.ReflowQueue - * @static - */ - define( - 'tinymce.core.ui.ReflowQueue', [ - "tinymce.core.util.Delay" - ], - function(Delay) { - var dirtyCtrls = {}, - animationFrameRequested; - - return { - /** - * Adds a control to the next automatic reflow call. This is the control that had a state - * change for example if the control was hidden/shown. - * - * @method add - * @param {tinymce.ui.Control} ctrl Control to add to queue. - */ - add: function(ctrl) { - var parent = ctrl.parent(); - - if (parent) { - if (!parent._layout || parent._layout.isNative()) { - return; - } - - if (!dirtyCtrls[parent._id]) { - dirtyCtrls[parent._id] = parent; - } - - if (!animationFrameRequested) { - animationFrameRequested = true; - - Delay.requestAnimationFrame(function() { - var id, ctrl; - - animationFrameRequested = false; - - for (id in dirtyCtrls) { - ctrl = dirtyCtrls[id]; - - if (ctrl.state.get('rendered')) { - ctrl.reflow(); - } - } - - dirtyCtrls = {}; - }, document.body); - } - } - }, - - /** - * Removes the specified control from the automatic reflow. This will happen when for example the user - * manually triggers a reflow. - * - * @method remove - * @param {tinymce.ui.Control} ctrl Control to remove from queue. - */ - remove: function(ctrl) { - if (dirtyCtrls[ctrl._id]) { - delete dirtyCtrls[ctrl._id]; - } - } - }; - } - ); - - /** - * Control.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /*eslint consistent-this:0 */ - - /** - * This is the base class for all controls and containers. All UI control instances inherit - * from this one as it has the base logic needed by all of them. - * - * @class tinymce.ui.Control - */ - define( - 'tinymce.core.ui.Control', [ - "tinymce.core.util.Class", - "tinymce.core.util.Tools", - "tinymce.core.util.EventDispatcher", - "tinymce.core.data.ObservableObject", - "tinymce.core.ui.Collection", - "tinymce.core.ui.DomUtils", - "tinymce.core.dom.DomQuery", - "tinymce.core.ui.BoxUtils", - "tinymce.core.ui.ClassList", - "tinymce.core.ui.ReflowQueue" - ], - function(Class, Tools, EventDispatcher, ObservableObject, Collection, DomUtils, $, BoxUtils, ClassList, ReflowQueue) { - "use strict"; - - var hasMouseWheelEventSupport = "onmousewheel" in document; - var hasWheelEventSupport = false; - var classPrefix = "mce-"; - var Control, idCounter = 0; - - var proto = { - Statics: { - classPrefix: classPrefix - }, - - isRtl: function() { - return Control.rtl; - }, - - /** - * Class/id prefix to use for all controls. - * - * @final - * @field {String} classPrefix - */ - classPrefix: classPrefix, - - /** - * Constructs a new control instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {String} style Style CSS properties to add. - * @setting {String} border Border box values example: 1 1 1 1 - * @setting {String} padding Padding box values example: 1 1 1 1 - * @setting {String} margin Margin box values example: 1 1 1 1 - * @setting {Number} minWidth Minimal width for the control. - * @setting {Number} minHeight Minimal height for the control. - * @setting {String} classes Space separated list of classes to add. - * @setting {String} role WAI-ARIA role to use for control. - * @setting {Boolean} hidden Is the control hidden by default. - * @setting {Boolean} disabled Is the control disabled by default. - * @setting {String} name Name of the control instance. - */ - init: function(settings) { - var self = this, - classes, defaultClasses; - - function applyClasses(classes) { - var i; - - classes = classes.split(' '); - for (i = 0; i < classes.length; i++) { - self.classes.add(classes[i]); - } - } - - self.settings = settings = Tools.extend({}, self.Defaults, settings); - - // Initial states - self._id = settings.id || ('mceu_' + (idCounter++)); - self._aria = { - role: settings.role - }; - self._elmCache = {}; - self.$ = $; - - self.state = new ObservableObject({ - visible: true, - active: false, - disabled: false, - value: '' - }); - - self.data = new ObservableObject(settings.data); - - self.classes = new ClassList(function() { - if (self.state.get('rendered')) { - self.getEl().className = this.toString(); - } - }); - self.classes.prefix = self.classPrefix; - - // Setup classes - classes = settings.classes; - if (classes) { - if (self.Defaults) { - defaultClasses = self.Defaults.classes; - - if (defaultClasses && classes != defaultClasses) { - applyClasses(defaultClasses); - } - } - - applyClasses(classes); - } - - Tools.each('title text name visible disabled active value'.split(' '), function(name) { - if (name in settings) { - self[name](settings[name]); - } - }); - - self.on('click', function() { - if (self.disabled()) { - return false; - } - }); - - /** - * Name/value object with settings for the current control. - * - * @field {Object} settings - */ - self.settings = settings; - - self.borderBox = BoxUtils.parseBox(settings.border); - self.paddingBox = BoxUtils.parseBox(settings.padding); - self.marginBox = BoxUtils.parseBox(settings.margin); - - if (settings.hidden) { - self.hide(); - } - }, - - // Will generate getter/setter methods for these properties - Properties: 'parent,name', - - /** - * Returns the root element to render controls into. - * - * @method getContainerElm - * @return {Element} HTML DOM element to render into. - */ - getContainerElm: function() { - return DomUtils.getContainer(); - }, - - /** - * Returns a control instance for the current DOM element. - * - * @method getParentCtrl - * @param {Element} elm HTML dom element to get parent control from. - * @return {tinymce.ui.Control} Control instance or undefined. - */ - getParentCtrl: function(elm) { - var ctrl, lookup = this.getRoot().controlIdLookup; - - while (elm && lookup) { - ctrl = lookup[elm.id]; - if (ctrl) { - break; - } - - elm = elm.parentNode; - } - - return ctrl; - }, - - /** - * Initializes the current controls layout rect. - * This will be executed by the layout managers to determine the - * default minWidth/minHeight etc. - * - * @method initLayoutRect - * @return {Object} Layout rect instance. - */ - initLayoutRect: function() { - var self = this, - settings = self.settings, - borderBox, layoutRect; - var elm = self.getEl(), - width, height, minWidth, minHeight, autoResize; - var startMinWidth, startMinHeight, initialSize; - - // Measure the current element - borderBox = self.borderBox = self.borderBox || BoxUtils.measureBox(elm, 'border'); - self.paddingBox = self.paddingBox || BoxUtils.measureBox(elm, 'padding'); - self.marginBox = self.marginBox || BoxUtils.measureBox(elm, 'margin'); - initialSize = DomUtils.getSize(elm); - - // Setup minWidth/minHeight and width/height - startMinWidth = settings.minWidth; - startMinHeight = settings.minHeight; - minWidth = startMinWidth || initialSize.width; - minHeight = startMinHeight || initialSize.height; - width = settings.width; - height = settings.height; - autoResize = settings.autoResize; - autoResize = typeof autoResize != "undefined" ? autoResize : !width && !height; - - width = width || minWidth; - height = height || minHeight; - - var deltaW = borderBox.left + borderBox.right; - var deltaH = borderBox.top + borderBox.bottom; - - var maxW = settings.maxWidth || 0xFFFF; - var maxH = settings.maxHeight || 0xFFFF; - - // Setup initial layout rect - self._layoutRect = layoutRect = { - x: settings.x || 0, - y: settings.y || 0, - w: width, - h: height, - deltaW: deltaW, - deltaH: deltaH, - contentW: width - deltaW, - contentH: height - deltaH, - innerW: width - deltaW, - innerH: height - deltaH, - startMinWidth: startMinWidth || 0, - startMinHeight: startMinHeight || 0, - minW: Math.min(minWidth, maxW), - minH: Math.min(minHeight, maxH), - maxW: maxW, - maxH: maxH, - autoResize: autoResize, - scrollW: 0 - }; - - self._lastLayoutRect = {}; - - return layoutRect; - }, - - /** - * Getter/setter for the current layout rect. - * - * @method layoutRect - * @param {Object} [newRect] Optional new layout rect. - * @return {tinymce.ui.Control/Object} Current control or rect object. - */ - layoutRect: function(newRect) { - var self = this, - curRect = self._layoutRect, - lastLayoutRect, size, deltaWidth, deltaHeight, undef, repaintControls; - - // Initialize default layout rect - if (!curRect) { - curRect = self.initLayoutRect(); - } - - // Set new rect values - if (newRect) { - // Calc deltas between inner and outer sizes - deltaWidth = curRect.deltaW; - deltaHeight = curRect.deltaH; - - // Set x position - if (newRect.x !== undef) { - curRect.x = newRect.x; - } - - // Set y position - if (newRect.y !== undef) { - curRect.y = newRect.y; - } - - // Set minW - if (newRect.minW !== undef) { - curRect.minW = newRect.minW; - } - - // Set minH - if (newRect.minH !== undef) { - curRect.minH = newRect.minH; - } - - // Set new width and calculate inner width - size = newRect.w; - if (size !== undef) { - size = size < curRect.minW ? curRect.minW : size; - size = size > curRect.maxW ? curRect.maxW : size; - curRect.w = size; - curRect.innerW = size - deltaWidth; - } - - // Set new height and calculate inner height - size = newRect.h; - if (size !== undef) { - size = size < curRect.minH ? curRect.minH : size; - size = size > curRect.maxH ? curRect.maxH : size; - curRect.h = size; - curRect.innerH = size - deltaHeight; - } - - // Set new inner width and calculate width - size = newRect.innerW; - if (size !== undef) { - size = size < curRect.minW - deltaWidth ? curRect.minW - deltaWidth : size; - size = size > curRect.maxW - deltaWidth ? curRect.maxW - deltaWidth : size; - curRect.innerW = size; - curRect.w = size + deltaWidth; - } - - // Set new height and calculate inner height - size = newRect.innerH; - if (size !== undef) { - size = size < curRect.minH - deltaHeight ? curRect.minH - deltaHeight : size; - size = size > curRect.maxH - deltaHeight ? curRect.maxH - deltaHeight : size; - curRect.innerH = size; - curRect.h = size + deltaHeight; - } - - // Set new contentW - if (newRect.contentW !== undef) { - curRect.contentW = newRect.contentW; - } - - // Set new contentH - if (newRect.contentH !== undef) { - curRect.contentH = newRect.contentH; - } - - // Compare last layout rect with the current one to see if we need to repaint or not - lastLayoutRect = self._lastLayoutRect; - if (lastLayoutRect.x !== curRect.x || lastLayoutRect.y !== curRect.y || - lastLayoutRect.w !== curRect.w || lastLayoutRect.h !== curRect.h) { - repaintControls = Control.repaintControls; - - if (repaintControls) { - if (repaintControls.map && !repaintControls.map[self._id]) { - repaintControls.push(self); - repaintControls.map[self._id] = true; - } - } - - lastLayoutRect.x = curRect.x; - lastLayoutRect.y = curRect.y; - lastLayoutRect.w = curRect.w; - lastLayoutRect.h = curRect.h; - } - - return self; - } - - return curRect; - }, - - /** - * Repaints the control after a layout operation. - * - * @method repaint - */ - repaint: function() { - var self = this, - style, bodyStyle, bodyElm, rect, borderBox; - var borderW, borderH, lastRepaintRect, round, value; - - // Use Math.round on all values on IE < 9 - round = !document.createRange ? Math.round : function(value) { - return value; - }; - - style = self.getEl().style; - rect = self._layoutRect; - lastRepaintRect = self._lastRepaintRect || {}; - - borderBox = self.borderBox; - borderW = borderBox.left + borderBox.right; - borderH = borderBox.top + borderBox.bottom; - - if (rect.x !== lastRepaintRect.x) { - style.left = round(rect.x) + 'px'; - lastRepaintRect.x = rect.x; - } - - if (rect.y !== lastRepaintRect.y) { - style.top = round(rect.y) + 'px'; - lastRepaintRect.y = rect.y; - } - - if (rect.w !== lastRepaintRect.w) { - value = round(rect.w - borderW); - style.width = (value >= 0 ? value : 0) + 'px'; - lastRepaintRect.w = rect.w; - } - - if (rect.h !== lastRepaintRect.h) { - value = round(rect.h - borderH); - style.height = (value >= 0 ? value : 0) + 'px'; - lastRepaintRect.h = rect.h; - } - - // Update body if needed - if (self._hasBody && rect.innerW !== lastRepaintRect.innerW) { - value = round(rect.innerW); - - bodyElm = self.getEl('body'); - if (bodyElm) { - bodyStyle = bodyElm.style; - bodyStyle.width = (value >= 0 ? value : 0) + 'px'; - } - - lastRepaintRect.innerW = rect.innerW; - } - - if (self._hasBody && rect.innerH !== lastRepaintRect.innerH) { - value = round(rect.innerH); - - bodyElm = bodyElm || self.getEl('body'); - if (bodyElm) { - bodyStyle = bodyStyle || bodyElm.style; - bodyStyle.height = (value >= 0 ? value : 0) + 'px'; - } - - lastRepaintRect.innerH = rect.innerH; - } - - self._lastRepaintRect = lastRepaintRect; - self.fire('repaint', {}, false); - }, - - /** - * Updates the controls layout rect by re-measuing it. - */ - updateLayoutRect: function() { - var self = this; - - self.parent()._lastRect = null; - - DomUtils.css(self.getEl(), { - width: '', - height: '' - }); - - self._layoutRect = self._lastRepaintRect = self._lastLayoutRect = null; - self.initLayoutRect(); - }, - - /** - * Binds a callback to the specified event. This event can both be - * native browser events like "click" or custom ones like PostRender. - * - * The callback function will be passed a DOM event like object that enables yout do stop propagation. - * - * @method on - * @param {String} name Name of the event to bind. For example "click". - * @param {String/function} callback Callback function to execute ones the event occurs. - * @return {tinymce.ui.Control} Current control object. - */ - on: function(name, callback) { - var self = this; - - function resolveCallbackName(name) { - var callback, scope; - - if (typeof name != 'string') { - return name; - } - - return function(e) { - if (!callback) { - self.parentsAndSelf().each(function(ctrl) { - var callbacks = ctrl.settings.callbacks; - - if (callbacks && (callback = callbacks[name])) { - scope = ctrl; - return false; - } - }); - } - - if (!callback) { - e.action = name; - this.fire('execute', e); - return; - } - - return callback.call(scope, e); - }; - } - - getEventDispatcher(self).on(name, resolveCallbackName(callback)); - - return self; - }, - - /** - * Unbinds the specified event and optionally a specific callback. If you omit the name - * parameter all event handlers will be removed. If you omit the callback all event handles - * by the specified name will be removed. - * - * @method off - * @param {String} [name] Name for the event to unbind. - * @param {function} [callback] Callback function to unbind. - * @return {tinymce.ui.Control} Current control object. - */ - off: function(name, callback) { - getEventDispatcher(this).off(name, callback); - return this; - }, - - /** - * Fires the specified event by name and arguments on the control. This will execute all - * bound event handlers. - * - * @method fire - * @param {String} name Name of the event to fire. - * @param {Object} [args] Arguments to pass to the event. - * @param {Boolean} [bubble] Value to control bubbling. Defaults to true. - * @return {Object} Current arguments object. - */ - fire: function(name, args, bubble) { - var self = this; - - args = args || {}; - - if (!args.control) { - args.control = self; - } - - args = getEventDispatcher(self).fire(name, args); - - // Bubble event up to parents - if (bubble !== false && self.parent) { - var parent = self.parent(); - while (parent && !args.isPropagationStopped()) { - parent.fire(name, args, false); - parent = parent.parent(); - } - } - - return args; - }, - - /** - * Returns true/false if the specified event has any listeners. - * - * @method hasEventListeners - * @param {String} name Name of the event to check for. - * @return {Boolean} True/false state if the event has listeners. - */ - hasEventListeners: function(name) { - return getEventDispatcher(this).has(name); - }, - - /** - * Returns a control collection with all parent controls. - * - * @method parents - * @param {String} selector Optional selector expression to find parents. - * @return {tinymce.ui.Collection} Collection with all parent controls. - */ - parents: function(selector) { - var self = this, - ctrl, parents = new Collection(); - - // Add each parent to collection - for (ctrl = self.parent(); ctrl; ctrl = ctrl.parent()) { - parents.add(ctrl); - } - - // Filter away everything that doesn't match the selector - if (selector) { - parents = parents.filter(selector); - } - - return parents; - }, - - /** - * Returns the current control and it's parents. - * - * @method parentsAndSelf - * @param {String} selector Optional selector expression to find parents. - * @return {tinymce.ui.Collection} Collection with all parent controls. - */ - parentsAndSelf: function(selector) { - return new Collection(this).add(this.parents(selector)); - }, - - /** - * Returns the control next to the current control. - * - * @method next - * @return {tinymce.ui.Control} Next control instance. - */ - next: function() { - var parentControls = this.parent().items(); - - return parentControls[parentControls.indexOf(this) + 1]; - }, - - /** - * Returns the control previous to the current control. - * - * @method prev - * @return {tinymce.ui.Control} Previous control instance. - */ - prev: function() { - var parentControls = this.parent().items(); - - return parentControls[parentControls.indexOf(this) - 1]; - }, - - /** - * Sets the inner HTML of the control element. - * - * @method innerHtml - * @param {String} html Html string to set as inner html. - * @return {tinymce.ui.Control} Current control object. - */ - innerHtml: function(html) { - this.$el.html(html); - return this; - }, - - /** - * Returns the control DOM element or sub element. - * - * @method getEl - * @param {String} [suffix] Suffix to get element by. - * @return {Element} HTML DOM element for the current control or it's children. - */ - getEl: function(suffix) { - var id = suffix ? this._id + '-' + suffix : this._id; - - if (!this._elmCache[id]) { - this._elmCache[id] = $('#' + id)[0]; - } - - return this._elmCache[id]; - }, - - /** - * Sets the visible state to true. - * - * @method show - * @return {tinymce.ui.Control} Current control instance. - */ - show: function() { - return this.visible(true); - }, - - /** - * Sets the visible state to false. - * - * @method hide - * @return {tinymce.ui.Control} Current control instance. - */ - hide: function() { - return this.visible(false); - }, - - /** - * Focuses the current control. - * - * @method focus - * @return {tinymce.ui.Control} Current control instance. - */ - focus: function() { - try { - this.getEl().focus(); - } catch (ex) { - // Ignore IE error - } - - return this; - }, - - /** - * Blurs the current control. - * - * @method blur - * @return {tinymce.ui.Control} Current control instance. - */ - blur: function() { - this.getEl().blur(); - - return this; - }, - - /** - * Sets the specified aria property. - * - * @method aria - * @param {String} name Name of the aria property to set. - * @param {String} value Value of the aria property. - * @return {tinymce.ui.Control} Current control instance. - */ - aria: function(name, value) { - var self = this, - elm = self.getEl(self.ariaTarget); - - if (typeof value === "undefined") { - return self._aria[name]; - } - - self._aria[name] = value; - - if (self.state.get('rendered')) { - elm.setAttribute(name == 'role' ? name : 'aria-' + name, value); - } - - return self; - }, - - /** - * Encodes the specified string with HTML entities. It will also - * translate the string to different languages. - * - * @method encode - * @param {String/Object/Array} text Text to entity encode. - * @param {Boolean} [translate=true] False if the contents shouldn't be translated. - * @return {String} Encoded and possible traslated string. - */ - encode: function(text, translate) { - if (translate !== false) { - text = this.translate(text); - } - - return (text || '').replace(/[&<>"]/g, function(match) { - return '&#' + match.charCodeAt(0) + ';'; - }); - }, - - /** - * Returns the translated string. - * - * @method translate - * @param {String} text Text to translate. - * @return {String} Translated string or the same as the input. - */ - translate: function(text) { - return Control.translate ? Control.translate(text) : text; - }, - - /** - * Adds items before the current control. - * - * @method before - * @param {Array/tinymce.ui.Collection} items Array of items to prepend before this control. - * @return {tinymce.ui.Control} Current control instance. - */ - before: function(items) { - var self = this, - parent = self.parent(); - - if (parent) { - parent.insert(items, parent.items().indexOf(self), true); - } - - return self; - }, - - /** - * Adds items after the current control. - * - * @method after - * @param {Array/tinymce.ui.Collection} items Array of items to append after this control. - * @return {tinymce.ui.Control} Current control instance. - */ - after: function(items) { - var self = this, - parent = self.parent(); - - if (parent) { - parent.insert(items, parent.items().indexOf(self)); - } - - return self; - }, - - /** - * Removes the current control from DOM and from UI collections. - * - * @method remove - * @return {tinymce.ui.Control} Current control instance. - */ - remove: function() { - var self = this, - elm = self.getEl(), - parent = self.parent(), - newItems, i; - - if (self.items) { - var controls = self.items().toArray(); - i = controls.length; - while (i--) { - controls[i].remove(); - } - } - - if (parent && parent.items) { - newItems = []; - - parent.items().each(function(item) { - if (item !== self) { - newItems.push(item); - } - }); - - parent.items().set(newItems); - parent._lastRect = null; - } - - if (self._eventsRoot && self._eventsRoot == self) { - $(elm).off(); - } - - var lookup = self.getRoot().controlIdLookup; - if (lookup) { - delete lookup[self._id]; - } - - if (elm && elm.parentNode) { - elm.parentNode.removeChild(elm); - } - - self.state.set('rendered', false); - self.state.destroy(); - - self.fire('remove'); - - return self; - }, - - /** - * Renders the control before the specified element. - * - * @method renderBefore - * @param {Element} elm Element to render before. - * @return {tinymce.ui.Control} Current control instance. - */ - renderBefore: function(elm) { - $(elm).before(this.renderHtml()); - this.postRender(); - return this; - }, - - /** - * Renders the control to the specified element. - * - * @method renderBefore - * @param {Element} elm Element to render to. - * @return {tinymce.ui.Control} Current control instance. - */ - renderTo: function(elm) { - $(elm || this.getContainerElm()).append(this.renderHtml()); - this.postRender(); - return this; - }, - - preRender: function() {}, - - render: function() {}, - - renderHtml: function() { - return '<div id="' + this._id + '" class="' + this.classes + '"></div>'; - }, - - /** - * Post render method. Called after the control has been rendered to the target. - * - * @method postRender - * @return {tinymce.ui.Control} Current control instance. - */ - postRender: function() { - var self = this, - settings = self.settings, - elm, box, parent, name, parentEventsRoot; - - self.$el = $(self.getEl()); - self.state.set('rendered', true); - - // Bind on<event> settings - for (name in settings) { - if (name.indexOf("on") === 0) { - self.on(name.substr(2), settings[name]); - } - } - - if (self._eventsRoot) { - for (parent = self.parent(); !parentEventsRoot && parent; parent = parent.parent()) { - parentEventsRoot = parent._eventsRoot; - } - - if (parentEventsRoot) { - for (name in parentEventsRoot._nativeEvents) { - self._nativeEvents[name] = true; - } - } - } - - bindPendingEvents(self); - - if (settings.style) { - elm = self.getEl(); - if (elm) { - elm.setAttribute('style', settings.style); - elm.style.cssText = settings.style; - } - } - - if (self.settings.border) { - box = self.borderBox; - self.$el.css({ - 'border-top-width': box.top, - 'border-right-width': box.right, - 'border-bottom-width': box.bottom, - 'border-left-width': box.left - }); - } - - // Add instance to lookup - var root = self.getRoot(); - if (!root.controlIdLookup) { - root.controlIdLookup = {}; - } - - root.controlIdLookup[self._id] = self; - - for (var key in self._aria) { - self.aria(key, self._aria[key]); - } - - if (self.state.get('visible') === false) { - self.getEl().style.display = 'none'; - } - - self.bindStates(); - - self.state.on('change:visible', function(e) { - var state = e.value, - parentCtrl; - - if (self.state.get('rendered')) { - self.getEl().style.display = state === false ? 'none' : ''; - - // Need to force a reflow here on IE 8 - self.getEl().getBoundingClientRect(); - } - - // Parent container needs to reflow - parentCtrl = self.parent(); - if (parentCtrl) { - parentCtrl._lastRect = null; - } - - self.fire(state ? 'show' : 'hide'); - - ReflowQueue.add(self); - }); - - self.fire('postrender', {}, false); - }, - - bindStates: function() {}, - - /** - * Scrolls the current control into view. - * - * @method scrollIntoView - * @param {String} align Alignment in view top|center|bottom. - * @return {tinymce.ui.Control} Current control instance. - */ - scrollIntoView: function(align) { - function getOffset(elm, rootElm) { - var x, y, parent = elm; - - x = y = 0; - while (parent && parent != rootElm && parent.nodeType) { - x += parent.offsetLeft || 0; - y += parent.offsetTop || 0; - parent = parent.offsetParent; - } - - return { - x: x, - y: y - }; - } - - var elm = this.getEl(), - parentElm = elm.parentNode; - var x, y, width, height, parentWidth, parentHeight; - var pos = getOffset(elm, parentElm); - - x = pos.x; - y = pos.y; - width = elm.offsetWidth; - height = elm.offsetHeight; - parentWidth = parentElm.clientWidth; - parentHeight = parentElm.clientHeight; - - if (align == "end") { - x -= parentWidth - width; - y -= parentHeight - height; - } else if (align == "center") { - x -= (parentWidth / 2) - (width / 2); - y -= (parentHeight / 2) - (height / 2); - } - - parentElm.scrollLeft = x; - parentElm.scrollTop = y; - - return this; - }, - - getRoot: function() { - var ctrl = this, - rootControl, parents = []; - - while (ctrl) { - if (ctrl.rootControl) { - rootControl = ctrl.rootControl; - break; - } - - parents.push(ctrl); - rootControl = ctrl; - ctrl = ctrl.parent(); - } - - if (!rootControl) { - rootControl = this; - } - - var i = parents.length; - while (i--) { - parents[i].rootControl = rootControl; - } - - return rootControl; - }, - - /** - * Reflows the current control and it's parents. - * This should be used after you for example append children to the current control so - * that the layout managers know that they need to reposition everything. - * - * @example - * container.append({type: 'button', text: 'My button'}).reflow(); - * - * @method reflow - * @return {tinymce.ui.Control} Current control instance. - */ - reflow: function() { - ReflowQueue.remove(this); - - var parent = this.parent(); - if (parent && parent._layout && !parent._layout.isNative()) { - parent.reflow(); - } - - return this; - } - - /** - * Sets/gets the parent container for the control. - * - * @method parent - * @param {tinymce.ui.Container} parent Optional parent to set. - * @return {tinymce.ui.Control} Parent control or the current control on a set action. - */ - // parent: function(parent) {} -- Generated - - /** - * Sets/gets the text for the control. - * - * @method text - * @param {String} value Value to set to control. - * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get. - */ - // text: function(value) {} -- Generated - - /** - * Sets/gets the disabled state on the control. - * - * @method disabled - * @param {Boolean} state Value to set to control. - * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get. - */ - // disabled: function(state) {} -- Generated - - /** - * Sets/gets the active for the control. - * - * @method active - * @param {Boolean} state Value to set to control. - * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get. - */ - // active: function(state) {} -- Generated - - /** - * Sets/gets the name for the control. - * - * @method name - * @param {String} value Value to set to control. - * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get. - */ - // name: function(value) {} -- Generated - - /** - * Sets/gets the title for the control. - * - * @method title - * @param {String} value Value to set to control. - * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get. - */ - // title: function(value) {} -- Generated - - /** - * Sets/gets the visible for the control. - * - * @method visible - * @param {Boolean} state Value to set to control. - * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get. - */ - // visible: function(value) {} -- Generated - }; - - /** - * Setup state properties. - */ - Tools.each('text title visible disabled active value'.split(' '), function(name) { - proto[name] = function(value) { - if (arguments.length === 0) { - return this.state.get(name); - } - - if (typeof value != "undefined") { - this.state.set(name, value); - } - - return this; - }; - }); - - Control = Class.extend(proto); - - function getEventDispatcher(obj) { - if (!obj._eventDispatcher) { - obj._eventDispatcher = new EventDispatcher({ - scope: obj, - toggleEvent: function(name, state) { - if (state && EventDispatcher.isNative(name)) { - if (!obj._nativeEvents) { - obj._nativeEvents = {}; - } - - obj._nativeEvents[name] = true; - - if (obj.state.get('rendered')) { - bindPendingEvents(obj); - } - } - } - }); - } - - return obj._eventDispatcher; - } - - function bindPendingEvents(eventCtrl) { - var i, l, parents, eventRootCtrl, nativeEvents, name; - - function delegate(e) { - var control = eventCtrl.getParentCtrl(e.target); - - if (control) { - control.fire(e.type, e); - } - } - - function mouseLeaveHandler() { - var ctrl = eventRootCtrl._lastHoverCtrl; - - if (ctrl) { - ctrl.fire("mouseleave", { - target: ctrl.getEl() - }); - - ctrl.parents().each(function(ctrl) { - ctrl.fire("mouseleave", { - target: ctrl.getEl() - }); - }); - - eventRootCtrl._lastHoverCtrl = null; - } - } - - function mouseEnterHandler(e) { - var ctrl = eventCtrl.getParentCtrl(e.target), - lastCtrl = eventRootCtrl._lastHoverCtrl, - idx = 0, - i, parents, lastParents; - - // Over on a new control - if (ctrl !== lastCtrl) { - eventRootCtrl._lastHoverCtrl = ctrl; - - parents = ctrl.parents().toArray().reverse(); - parents.push(ctrl); - - if (lastCtrl) { - lastParents = lastCtrl.parents().toArray().reverse(); - lastParents.push(lastCtrl); - - for (idx = 0; idx < lastParents.length; idx++) { - if (parents[idx] !== lastParents[idx]) { - break; - } - } - - for (i = lastParents.length - 1; i >= idx; i--) { - lastCtrl = lastParents[i]; - lastCtrl.fire("mouseleave", { - target: lastCtrl.getEl() - }); - } - } - - for (i = idx; i < parents.length; i++) { - ctrl = parents[i]; - ctrl.fire("mouseenter", { - target: ctrl.getEl() - }); - } - } - } - - function fixWheelEvent(e) { - e.preventDefault(); - - if (e.type == "mousewheel") { - e.deltaY = -1 / 40 * e.wheelDelta; - - if (e.wheelDeltaX) { - e.deltaX = -1 / 40 * e.wheelDeltaX; - } - } else { - e.deltaX = 0; - e.deltaY = e.detail; - } - - e = eventCtrl.fire("wheel", e); - } - - nativeEvents = eventCtrl._nativeEvents; - if (nativeEvents) { - // Find event root element if it exists - parents = eventCtrl.parents().toArray(); - parents.unshift(eventCtrl); - for (i = 0, l = parents.length; !eventRootCtrl && i < l; i++) { - eventRootCtrl = parents[i]._eventsRoot; - } - - // Event root wasn't found the use the root control - if (!eventRootCtrl) { - eventRootCtrl = parents[parents.length - 1] || eventCtrl; - } - - // Set the eventsRoot property on children that didn't have it - eventCtrl._eventsRoot = eventRootCtrl; - for (l = i, i = 0; i < l; i++) { - parents[i]._eventsRoot = eventRootCtrl; - } - - var eventRootDelegates = eventRootCtrl._delegates; - if (!eventRootDelegates) { - eventRootDelegates = eventRootCtrl._delegates = {}; - } - - // Bind native event delegates - for (name in nativeEvents) { - if (!nativeEvents) { - return false; - } - - if (name === "wheel" && !hasWheelEventSupport) { - if (hasMouseWheelEventSupport) { - $(eventCtrl.getEl()).on("mousewheel", fixWheelEvent); - } else { - $(eventCtrl.getEl()).on("DOMMouseScroll", fixWheelEvent); - } - - continue; - } - - // Special treatment for mousenter/mouseleave since these doesn't bubble - if (name === "mouseenter" || name === "mouseleave") { - // Fake mousenter/mouseleave - if (!eventRootCtrl._hasMouseEnter) { - $(eventRootCtrl.getEl()).on("mouseleave", mouseLeaveHandler).on("mouseover", mouseEnterHandler); - eventRootCtrl._hasMouseEnter = 1; - } - } else if (!eventRootDelegates[name]) { - $(eventRootCtrl.getEl()).on(name, delegate); - eventRootDelegates[name] = true; - } - - // Remove the event once it's bound - nativeEvents[name] = false; - } - } - } - - return Control; - } - ); - - /** - * Factory.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class is a factory for control instances. This enables you - * to create instances of controls without having to require the UI controls directly. - * - * It also allow you to override or add new control types. - * - * @class tinymce.ui.Factory - */ - define( - 'tinymce.core.ui.Factory', [], - function() { - "use strict"; - - var types = {}; - - return { - /** - * Adds a new control instance type to the factory. - * - * @method add - * @param {String} type Type name for example "button". - * @param {function} typeClass Class type function. - */ - add: function(type, typeClass) { - types[type.toLowerCase()] = typeClass; - }, - - /** - * Returns true/false if the specified type exists or not. - * - * @method has - * @param {String} type Type to look for. - * @return {Boolean} true/false if the control by name exists. - */ - has: function(type) { - return !!types[type.toLowerCase()]; - }, - - /** - * Creates a new control instance based on the settings provided. The instance created will be - * based on the specified type property it can also create whole structures of components out of - * the specified JSON object. - * - * @example - * tinymce.ui.Factory.create({ - * type: 'button', - * text: 'Hello world!' - * }); - * - * @method create - * @param {Object/String} settings Name/Value object with items used to create the type. - * @return {tinymce.ui.Control} Control instance based on the specified type. - */ - create: function(type, settings) { - var ControlType; - - // If string is specified then use it as the type - if (typeof type == 'string') { - settings = settings || {}; - settings.type = type; - } else { - settings = type; - type = settings.type; - } - - // Find control type - type = type.toLowerCase(); - ControlType = types[type]; - - // #if debug - - if (!ControlType) { - throw new Error("Could not find control by type: " + type); - } - - // #endif - - ControlType = new ControlType(settings); - ControlType.type = type; // Set the type on the instance, this will be used by the Selector engine - - return ControlType; - } - }; - } - ); - /** - * KeyboardNavigation.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class handles keyboard navigation of controls and elements. - * - * @class tinymce.ui.KeyboardNavigation - */ - define( - 'tinymce.core.ui.KeyboardNavigation', [], - function() { - "use strict"; - - var hasTabstopData = function(elm) { - return elm.getAttribute('data-mce-tabstop') ? true : false; - }; - - /** - * This class handles all keyboard navigation for WAI-ARIA support. Each root container - * gets an instance of this class. - * - * @constructor - */ - return function(settings) { - var root = settings.root, - focusedElement, focusedControl; - - function isElement(node) { - return node && node.nodeType === 1; - } - - try { - focusedElement = document.activeElement; - } catch (ex) { - // IE sometimes fails to return a proper element - focusedElement = document.body; - } - - focusedControl = root.getParentCtrl(focusedElement); - - /** - * Returns the currently focused elements wai aria role of the currently - * focused element or specified element. - * - * @private - * @param {Element} elm Optional element to get role from. - * @return {String} Role of specified element. - */ - function getRole(elm) { - elm = elm || focusedElement; - - if (isElement(elm)) { - return elm.getAttribute('role'); - } - - return null; - } - - /** - * Returns the wai role of the parent element of the currently - * focused element or specified element. - * - * @private - * @param {Element} elm Optional element to get parent role from. - * @return {String} Role of the first parent that has a role. - */ - function getParentRole(elm) { - var role, parent = elm || focusedElement; - - while ((parent = parent.parentNode)) { - if ((role = getRole(parent))) { - return role; - } - } - } - - /** - * Returns a wai aria property by name for example aria-selected. - * - * @private - * @param {String} name Name of the aria property to get for example "disabled". - * @return {String} Aria property value. - */ - function getAriaProp(name) { - var elm = focusedElement; - - if (isElement(elm)) { - return elm.getAttribute('aria-' + name); - } - } - - /** - * Is the element a text input element or not. - * - * @private - * @param {Element} elm Element to check if it's an text input element or not. - * @return {Boolean} True/false if the element is a text element or not. - */ - function isTextInputElement(elm) { - var tagName = elm.tagName.toUpperCase(); - - // Notice: since type can be "email" etc we don't check the type - // So all input elements gets treated as text input elements - return tagName == "INPUT" || tagName == "TEXTAREA" || tagName == "SELECT"; - } - - /** - * Returns true/false if the specified element can be focused or not. - * - * @private - * @param {Element} elm DOM element to check if it can be focused or not. - * @return {Boolean} True/false if the element can have focus. - */ - function canFocus(elm) { - if (isTextInputElement(elm) && !elm.hidden) { - return true; - } - - if (hasTabstopData(elm)) { - return true; - } - - if (/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell|slider)$/.test(getRole(elm))) { - return true; - } - - return false; - } - - /** - * Returns an array of focusable visible elements within the specified container element. - * - * @private - * @param {Element} elm DOM element to find focusable elements within. - * @return {Array} Array of focusable elements. - */ - function getFocusElements(elm) { - var elements = []; - - function collect(elm) { - if (elm.nodeType != 1 || elm.style.display == 'none' || elm.disabled) { - return; - } - - if (canFocus(elm)) { - elements.push(elm); - } - - for (var i = 0; i < elm.childNodes.length; i++) { - collect(elm.childNodes[i]); - } - } - - collect(elm || root.getEl()); - - return elements; - } - - /** - * Returns the navigation root control for the specified control. The navigation root - * is the control that the keyboard navigation gets scoped to for example a menubar or toolbar group. - * It will look for parents of the specified target control or the currently focused control if this option is omitted. - * - * @private - * @param {tinymce.ui.Control} targetControl Optional target control to find root of. - * @return {tinymce.ui.Control} Navigation root control. - */ - function getNavigationRoot(targetControl) { - var navigationRoot, controls; - - targetControl = targetControl || focusedControl; - controls = targetControl.parents().toArray(); - controls.unshift(targetControl); - - for (var i = 0; i < controls.length; i++) { - navigationRoot = controls[i]; - - if (navigationRoot.settings.ariaRoot) { - break; - } - } - - return navigationRoot; - } - - /** - * Focuses the first item in the specified targetControl element or the last aria index if the - * navigation root has the ariaRemember option enabled. - * - * @private - * @param {tinymce.ui.Control} targetControl Target control to focus the first item in. - */ - function focusFirst(targetControl) { - var navigationRoot = getNavigationRoot(targetControl); - var focusElements = getFocusElements(navigationRoot.getEl()); - - if (navigationRoot.settings.ariaRemember && "lastAriaIndex" in navigationRoot) { - moveFocusToIndex(navigationRoot.lastAriaIndex, focusElements); - } else { - moveFocusToIndex(0, focusElements); - } - } - - /** - * Moves the focus to the specified index within the elements list. - * This will scope the index to the size of the element list if it changed. - * - * @private - * @param {Number} idx Specified index to move to. - * @param {Array} elements Array with dom elements to move focus within. - * @return {Number} Input index or a changed index if it was out of range. - */ - function moveFocusToIndex(idx, elements) { - if (idx < 0) { - idx = elements.length - 1; - } else if (idx >= elements.length) { - idx = 0; - } - - if (elements[idx]) { - elements[idx].focus(); - } - - return idx; - } - - /** - * Moves the focus forwards or backwards. - * - * @private - * @param {Number} dir Direction to move in positive means forward, negative means backwards. - * @param {Array} elements Optional array of elements to move within defaults to the current navigation roots elements. - */ - function moveFocus(dir, elements) { - var idx = -1, - navigationRoot = getNavigationRoot(); - - elements = elements || getFocusElements(navigationRoot.getEl()); - - for (var i = 0; i < elements.length; i++) { - if (elements[i] === focusedElement) { - idx = i; - } - } - - idx += dir; - navigationRoot.lastAriaIndex = moveFocusToIndex(idx, elements); - } - - /** - * Moves the focus to the left this is called by the left key. - * - * @private - */ - function left() { - var parentRole = getParentRole(); - - if (parentRole == "tablist") { - moveFocus(-1, getFocusElements(focusedElement.parentNode)); - } else if (focusedControl.parent().submenu) { - cancel(); - } else { - moveFocus(-1); - } - } - - /** - * Moves the focus to the right this is called by the right key. - * - * @private - */ - function right() { - var role = getRole(), - parentRole = getParentRole(); - - if (parentRole == "tablist") { - moveFocus(1, getFocusElements(focusedElement.parentNode)); - } else if (role == "menuitem" && parentRole == "menu" && getAriaProp('haspopup')) { - enter(); - } else { - moveFocus(1); - } - } - - /** - * Moves the focus to the up this is called by the up key. - * - * @private - */ - function up() { - moveFocus(-1); - } - - /** - * Moves the focus to the up this is called by the down key. - * - * @private - */ - function down() { - var role = getRole(), - parentRole = getParentRole(); - - if (role == "menuitem" && parentRole == "menubar") { - enter(); - } else if (role == "button" && getAriaProp('haspopup')) { - enter({ - key: 'down' - }); - } else { - moveFocus(1); - } - } - - /** - * Moves the focus to the next item or previous item depending on shift key. - * - * @private - * @param {DOMEvent} e DOM event object. - */ - function tab(e) { - var parentRole = getParentRole(); - - if (parentRole == "tablist") { - var elm = getFocusElements(focusedControl.getEl('body'))[0]; - - if (elm) { - elm.focus(); - } - } else { - moveFocus(e.shiftKey ? -1 : 1); - } - } - - /** - * Calls the cancel event on the currently focused control. This is normally done using the Esc key. - * - * @private - */ - function cancel() { - focusedControl.fire('cancel'); - } - - /** - * Calls the click event on the currently focused control. This is normally done using the Enter/Space keys. - * - * @private - * @param {Object} aria Optional aria data to pass along with the enter event. - */ - function enter(aria) { - aria = aria || {}; - focusedControl.fire('click', { - target: focusedElement, - aria: aria - }); - } - - root.on('keydown', function(e) { - function handleNonTabOrEscEvent(e, handler) { - // Ignore non tab keys for text elements - if (isTextInputElement(focusedElement) || hasTabstopData(focusedElement)) { - return; - } - - if (getRole(focusedElement) === 'slider') { - return; - } - - if (handler(e) !== false) { - e.preventDefault(); - } - } - - if (e.isDefaultPrevented()) { - return; - } - - switch (e.keyCode) { - case 37: // DOM_VK_LEFT - handleNonTabOrEscEvent(e, left); - break; - - case 39: // DOM_VK_RIGHT - handleNonTabOrEscEvent(e, right); - break; - - case 38: // DOM_VK_UP - handleNonTabOrEscEvent(e, up); - break; - - case 40: // DOM_VK_DOWN - handleNonTabOrEscEvent(e, down); - break; - - case 27: // DOM_VK_ESCAPE - cancel(); - break; - - case 14: // DOM_VK_ENTER - case 13: // DOM_VK_RETURN - case 32: // DOM_VK_SPACE - handleNonTabOrEscEvent(e, enter); - break; - - case 9: // DOM_VK_TAB - if (tab(e) !== false) { - e.preventDefault(); - } - break; - } - }); - - root.on('focusin', function(e) { - focusedElement = e.target; - focusedControl = e.control; - }); - - return { - focusFirst: focusFirst - }; - }; - } - ); - /** - * Container.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Container control. This is extended by all controls that can have - * children such as panels etc. You can also use this class directly as an - * generic container instance. The container doesn't have any specific role or style. - * - * @-x-less Container.less - * @class tinymce.ui.Container - * @extends tinymce.ui.Control - */ - define( - 'tinymce.core.ui.Container', [ - "tinymce.core.ui.Control", - "tinymce.core.ui.Collection", - "tinymce.core.ui.Selector", - "tinymce.core.ui.Factory", - "tinymce.core.ui.KeyboardNavigation", - "tinymce.core.util.Tools", - "tinymce.core.dom.DomQuery", - "tinymce.core.ui.ClassList", - "tinymce.core.ui.ReflowQueue" - ], - function(Control, Collection, Selector, Factory, KeyboardNavigation, Tools, $, ClassList, ReflowQueue) { - "use strict"; - - var selectorCache = {}; - - return Control.extend({ - /** - * Constructs a new control instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {Array} items Items to add to container in JSON format or control instances. - * @setting {String} layout Layout manager by name to use. - * @setting {Object} defaults Default settings to apply to all items. - */ - init: function(settings) { - var self = this; - - self._super(settings); - settings = self.settings; - - if (settings.fixed) { - self.state.set('fixed', true); - } - - self._items = new Collection(); - - if (self.isRtl()) { - self.classes.add('rtl'); - } - - self.bodyClasses = new ClassList(function() { - if (self.state.get('rendered')) { - self.getEl('body').className = this.toString(); - } - }); - self.bodyClasses.prefix = self.classPrefix; - - self.classes.add('container'); - self.bodyClasses.add('container-body'); - - if (settings.containerCls) { - self.classes.add(settings.containerCls); - } - - self._layout = Factory.create((settings.layout || '') + 'layout'); - - if (self.settings.items) { - self.add(self.settings.items); - } else { - self.add(self.render()); - } - - // TODO: Fix this! - self._hasBody = true; - }, - - /** - * Returns a collection of child items that the container currently have. - * - * @method items - * @return {tinymce.ui.Collection} Control collection direct child controls. - */ - items: function() { - return this._items; - }, - - /** - * Find child controls by selector. - * - * @method find - * @param {String} selector Selector CSS pattern to find children by. - * @return {tinymce.ui.Collection} Control collection with child controls. - */ - find: function(selector) { - selector = selectorCache[selector] = selectorCache[selector] || new Selector(selector); - - return selector.find(this); - }, - - /** - * Adds one or many items to the current container. This will create instances of - * the object representations if needed. - * - * @method add - * @param {Array/Object/tinymce.ui.Control} items Array or item that will be added to the container. - * @return {tinymce.ui.Collection} Current collection control. - */ - add: function(items) { - var self = this; - - self.items().add(self.create(items)).parent(self); - - return self; - }, - - /** - * Focuses the current container instance. This will look - * for the first control in the container and focus that. - * - * @method focus - * @param {Boolean} keyboard Optional true/false if the focus was a keyboard focus or not. - * @return {tinymce.ui.Collection} Current instance. - */ - focus: function(keyboard) { - var self = this, - focusCtrl, keyboardNav, items; - - if (keyboard) { - keyboardNav = self.keyboardNav || self.parents().eq(-1)[0].keyboardNav; - - if (keyboardNav) { - keyboardNav.focusFirst(self); - return; - } - } - - items = self.find('*'); - - // TODO: Figure out a better way to auto focus alert dialog buttons - if (self.statusbar) { - items.add(self.statusbar.items()); - } - - items.each(function(ctrl) { - if (ctrl.settings.autofocus) { - focusCtrl = null; - return false; - } - - if (ctrl.canFocus) { - focusCtrl = focusCtrl || ctrl; - } - }); - - if (focusCtrl) { - focusCtrl.focus(); - } - - return self; - }, - - /** - * Replaces the specified child control with a new control. - * - * @method replace - * @param {tinymce.ui.Control} oldItem Old item to be replaced. - * @param {tinymce.ui.Control} newItem New item to be inserted. - */ - replace: function(oldItem, newItem) { - var ctrlElm, items = this.items(), - i = items.length; - - // Replace the item in collection - while (i--) { - if (items[i] === oldItem) { - items[i] = newItem; - break; - } - } - - if (i >= 0) { - // Remove new item from DOM - ctrlElm = newItem.getEl(); - if (ctrlElm) { - ctrlElm.parentNode.removeChild(ctrlElm); - } - - // Remove old item from DOM - ctrlElm = oldItem.getEl(); - if (ctrlElm) { - ctrlElm.parentNode.removeChild(ctrlElm); - } - } - - // Adopt the item - newItem.parent(this); - }, - - /** - * Creates the specified items. If any of the items is plain JSON style objects - * it will convert these into real tinymce.ui.Control instances. - * - * @method create - * @param {Array} items Array of items to convert into control instances. - * @return {Array} Array with control instances. - */ - create: function(items) { - var self = this, - settings, ctrlItems = []; - - // Non array structure, then force it into an array - if (!Tools.isArray(items)) { - items = [items]; - } - - // Add default type to each child control - Tools.each(items, function(item) { - if (item) { - // Construct item if needed - if (!(item instanceof Control)) { - // Name only then convert it to an object - if (typeof item == "string") { - item = { - type: item - }; - } - - // Create control instance based on input settings and default settings - settings = Tools.extend({}, self.settings.defaults, item); - item.type = settings.type = settings.type || item.type || self.settings.defaultType || - (settings.defaults ? settings.defaults.type : null); - item = Factory.create(settings); - } - - ctrlItems.push(item); - } - }); - - return ctrlItems; - }, - - /** - * Renders new control instances. - * - * @private - */ - renderNew: function() { - var self = this; - - // Render any new items - self.items().each(function(ctrl, index) { - var containerElm; - - ctrl.parent(self); - - if (!ctrl.state.get('rendered')) { - containerElm = self.getEl('body'); - - // Insert or append the item - if (containerElm.hasChildNodes() && index <= containerElm.childNodes.length - 1) { - $(containerElm.childNodes[index]).before(ctrl.renderHtml()); - } else { - $(containerElm).append(ctrl.renderHtml()); - } - - ctrl.postRender(); - ReflowQueue.add(ctrl); - } - }); - - self._layout.applyClasses(self.items().filter(':visible')); - self._lastRect = null; - - return self; - }, - - /** - * Appends new instances to the current container. - * - * @method append - * @param {Array/tinymce.ui.Collection} items Array if controls to append. - * @return {tinymce.ui.Container} Current container instance. - */ - append: function(items) { - return this.add(items).renderNew(); - }, - - /** - * Prepends new instances to the current container. - * - * @method prepend - * @param {Array/tinymce.ui.Collection} items Array if controls to prepend. - * @return {tinymce.ui.Container} Current container instance. - */ - prepend: function(items) { - var self = this; - - self.items().set(self.create(items).concat(self.items().toArray())); - - return self.renderNew(); - }, - - /** - * Inserts an control at a specific index. - * - * @method insert - * @param {Array/tinymce.ui.Collection} items Array if controls to insert. - * @param {Number} index Index to insert controls at. - * @param {Boolean} [before=false] Inserts controls before the index. - */ - insert: function(items, index, before) { - var self = this, - curItems, beforeItems, afterItems; - - items = self.create(items); - curItems = self.items(); - - if (!before && index < curItems.length - 1) { - index += 1; - } - - if (index >= 0 && index < curItems.length) { - beforeItems = curItems.slice(0, index).toArray(); - afterItems = curItems.slice(index).toArray(); - curItems.set(beforeItems.concat(items, afterItems)); - } - - return self.renderNew(); - }, - - /** - * Populates the form fields from the specified JSON data object. - * - * Control items in the form that matches the data will have it's value set. - * - * @method fromJSON - * @param {Object} data JSON data object to set control values by. - * @return {tinymce.ui.Container} Current form instance. - */ - fromJSON: function(data) { - var self = this; - - for (var name in data) { - self.find('#' + name).value(data[name]); - } - - return self; - }, - - /** - * Serializes the form into a JSON object by getting all items - * that has a name and a value. - * - * @method toJSON - * @return {Object} JSON object with form data. - */ - toJSON: function() { - var self = this, - data = {}; - - self.find('*').each(function(ctrl) { - var name = ctrl.name(), - value = ctrl.value(); - - if (name && typeof value != "undefined") { - data[name] = value; - } - }); - - return data; - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - layout = self._layout, - role = this.settings.role; - - self.preRender(); - layout.preRender(self); - - return ( - '<div id="' + self._id + '" class="' + self.classes + '"' + (role ? ' role="' + this.settings.role + '"' : '') + '>' + - '<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' + - (self.settings.html || '') + layout.renderHtml(self) + - '</div>' + - '</div>' - ); - }, - - /** - * Post render method. Called after the control has been rendered to the target. - * - * @method postRender - * @return {tinymce.ui.Container} Current combobox instance. - */ - postRender: function() { - var self = this, - box; - - self.items().exec('postRender'); - self._super(); - - self._layout.postRender(self); - self.state.set('rendered', true); - - if (self.settings.style) { - self.$el.css(self.settings.style); - } - - if (self.settings.border) { - box = self.borderBox; - self.$el.css({ - 'border-top-width': box.top, - 'border-right-width': box.right, - 'border-bottom-width': box.bottom, - 'border-left-width': box.left - }); - } - - if (!self.parent()) { - self.keyboardNav = new KeyboardNavigation({ - root: self - }); - } - - return self; - }, - - /** - * Initializes the current controls layout rect. - * This will be executed by the layout managers to determine the - * default minWidth/minHeight etc. - * - * @method initLayoutRect - * @return {Object} Layout rect instance. - */ - initLayoutRect: function() { - var self = this, - layoutRect = self._super(); - - // Recalc container size by asking layout manager - self._layout.recalc(self); - - return layoutRect; - }, - - /** - * Recalculates the positions of the controls in the current container. - * This is invoked by the reflow method and shouldn't be called directly. - * - * @method recalc - */ - recalc: function() { - var self = this, - rect = self._layoutRect, - lastRect = self._lastRect; - - if (!lastRect || lastRect.w != rect.w || lastRect.h != rect.h) { - self._layout.recalc(self); - rect = self.layoutRect(); - self._lastRect = { - x: rect.x, - y: rect.y, - w: rect.w, - h: rect.h - }; - return true; - } - }, - - /** - * Reflows the current container and it's children and possible parents. - * This should be used after you for example append children to the current control so - * that the layout managers know that they need to reposition everything. - * - * @example - * container.append({type: 'button', text: 'My button'}).reflow(); - * - * @method reflow - * @return {tinymce.ui.Container} Current container instance. - */ - reflow: function() { - var i; - - ReflowQueue.remove(this); - - if (this.visible()) { - Control.repaintControls = []; - Control.repaintControls.map = {}; - - this.recalc(); - i = Control.repaintControls.length; - - while (i--) { - Control.repaintControls[i].repaint(); - } - - // TODO: Fix me! - if (this.settings.layout !== "flow" && this.settings.layout !== "stack") { - this.repaint(); - } - - Control.repaintControls = []; - } - - return this; - } - }); - } - ); - /** - * DragHelper.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Drag/drop helper class. - * - * @example - * var dragHelper = new tinymce.ui.DragHelper('mydiv', { - * start: function(evt) { - * }, - * - * drag: function(evt) { - * }, - * - * end: function(evt) { - * } - * }); - * - * @class tinymce.ui.DragHelper - */ - define( - 'tinymce.core.ui.DragHelper', [ - "tinymce.core.dom.DomQuery" - ], - function($) { - "use strict"; - - function getDocumentSize(doc) { - var documentElement, body, scrollWidth, clientWidth; - var offsetWidth, scrollHeight, clientHeight, offsetHeight, max = Math.max; - - documentElement = doc.documentElement; - body = doc.body; - - scrollWidth = max(documentElement.scrollWidth, body.scrollWidth); - clientWidth = max(documentElement.clientWidth, body.clientWidth); - offsetWidth = max(documentElement.offsetWidth, body.offsetWidth); - - scrollHeight = max(documentElement.scrollHeight, body.scrollHeight); - clientHeight = max(documentElement.clientHeight, body.clientHeight); - offsetHeight = max(documentElement.offsetHeight, body.offsetHeight); - - return { - width: scrollWidth < offsetWidth ? clientWidth : scrollWidth, - height: scrollHeight < offsetHeight ? clientHeight : scrollHeight - }; - } - - function updateWithTouchData(e) { - var keys, i; - - if (e.changedTouches) { - keys = "screenX screenY pageX pageY clientX clientY".split(' '); - for (i = 0; i < keys.length; i++) { - e[keys[i]] = e.changedTouches[0][keys[i]]; - } - } - } - - return function(id, settings) { - var $eventOverlay, doc = settings.document || document, - downButton, start, stop, drag, startX, startY; - - settings = settings || {}; - - function getHandleElm() { - return doc.getElementById(settings.handle || id); - } - - start = function(e) { - var docSize = getDocumentSize(doc), - handleElm, cursor; - - updateWithTouchData(e); - - e.preventDefault(); - downButton = e.button; - handleElm = getHandleElm(); - startX = e.screenX; - startY = e.screenY; - - // Grab cursor from handle so we can place it on overlay - if (window.getComputedStyle) { - cursor = window.getComputedStyle(handleElm, null).getPropertyValue("cursor"); - } else { - cursor = handleElm.runtimeStyle.cursor; - } - - $eventOverlay = $('<div></div>').css({ - position: "absolute", - top: 0, - left: 0, - width: docSize.width, - height: docSize.height, - zIndex: 0x7FFFFFFF, - opacity: 0.0001, - cursor: cursor - }).appendTo(doc.body); - - $(doc).on('mousemove touchmove', drag).on('mouseup touchend', stop); - - settings.start(e); - }; - - drag = function(e) { - updateWithTouchData(e); - - if (e.button !== downButton) { - return stop(e); - } - - e.deltaX = e.screenX - startX; - e.deltaY = e.screenY - startY; - - e.preventDefault(); - settings.drag(e); - }; - - stop = function(e) { - updateWithTouchData(e); - - $(doc).off('mousemove touchmove', drag).off('mouseup touchend', stop); - - $eventOverlay.remove(); - - if (settings.stop) { - settings.stop(e); - } - }; - - /** - * Destroys the drag/drop helper instance. - * - * @method destroy - */ - this.destroy = function() { - $(getHandleElm()).off(); - }; - - $(getHandleElm()).on('mousedown touchstart', start); - }; - } - ); - /** - * Scrollable.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This mixin makes controls scrollable using custom scrollbars. - * - * @-x-less Scrollable.less - * @mixin tinymce.ui.Scrollable - */ - define( - 'tinymce.core.ui.Scrollable', [ - "tinymce.core.dom.DomQuery", - "tinymce.core.ui.DragHelper" - ], - function($, DragHelper) { - "use strict"; - - return { - init: function() { - var self = this; - self.on('repaint', self.renderScroll); - }, - - renderScroll: function() { - var self = this, - margin = 2; - - function repaintScroll() { - var hasScrollH, hasScrollV, bodyElm; - - function repaintAxis(axisName, posName, sizeName, contentSizeName, hasScroll, ax) { - var containerElm, scrollBarElm, scrollThumbElm; - var containerSize, scrollSize, ratio, rect; - var posNameLower, sizeNameLower; - - scrollBarElm = self.getEl('scroll' + axisName); - if (scrollBarElm) { - posNameLower = posName.toLowerCase(); - sizeNameLower = sizeName.toLowerCase(); - - $(self.getEl('absend')).css(posNameLower, self.layoutRect()[contentSizeName] - 1); - - if (!hasScroll) { - $(scrollBarElm).css('display', 'none'); - return; - } - - $(scrollBarElm).css('display', 'block'); - containerElm = self.getEl('body'); - scrollThumbElm = self.getEl('scroll' + axisName + "t"); - containerSize = containerElm["client" + sizeName] - (margin * 2); - containerSize -= hasScrollH && hasScrollV ? scrollBarElm["client" + ax] : 0; - scrollSize = containerElm["scroll" + sizeName]; - ratio = containerSize / scrollSize; - - rect = {}; - rect[posNameLower] = containerElm["offset" + posName] + margin; - rect[sizeNameLower] = containerSize; - $(scrollBarElm).css(rect); - - rect = {}; - rect[posNameLower] = containerElm["scroll" + posName] * ratio; - rect[sizeNameLower] = containerSize * ratio; - $(scrollThumbElm).css(rect); - } - } - - bodyElm = self.getEl('body'); - hasScrollH = bodyElm.scrollWidth > bodyElm.clientWidth; - hasScrollV = bodyElm.scrollHeight > bodyElm.clientHeight; - - repaintAxis("h", "Left", "Width", "contentW", hasScrollH, "Height"); - repaintAxis("v", "Top", "Height", "contentH", hasScrollV, "Width"); - } - - function addScroll() { - function addScrollAxis(axisName, posName, sizeName, deltaPosName, ax) { - var scrollStart, axisId = self._id + '-scroll' + axisName, - prefix = self.classPrefix; - - $(self.getEl()).append( - '<div id="' + axisId + '" class="' + prefix + 'scrollbar ' + prefix + 'scrollbar-' + axisName + '">' + - '<div id="' + axisId + 't" class="' + prefix + 'scrollbar-thumb"></div>' + - '</div>' - ); - - self.draghelper = new DragHelper(axisId + 't', { - start: function() { - scrollStart = self.getEl('body')["scroll" + posName]; - $('#' + axisId).addClass(prefix + 'active'); - }, - - drag: function(e) { - var ratio, hasScrollH, hasScrollV, containerSize, layoutRect = self.layoutRect(); - - hasScrollH = layoutRect.contentW > layoutRect.innerW; - hasScrollV = layoutRect.contentH > layoutRect.innerH; - containerSize = self.getEl('body')["client" + sizeName] - (margin * 2); - containerSize -= hasScrollH && hasScrollV ? self.getEl('scroll' + axisName)["client" + ax] : 0; - - ratio = containerSize / self.getEl('body')["scroll" + sizeName]; - self.getEl('body')["scroll" + posName] = scrollStart + (e["delta" + deltaPosName] / ratio); - }, - - stop: function() { - $('#' + axisId).removeClass(prefix + 'active'); - } - }); - } - - self.classes.add('scroll'); - - addScrollAxis("v", "Top", "Height", "Y", "Width"); - addScrollAxis("h", "Left", "Width", "X", "Height"); - } - - if (self.settings.autoScroll) { - if (!self._hasScroll) { - self._hasScroll = true; - addScroll(); - - self.on('wheel', function(e) { - var bodyEl = self.getEl('body'); - - bodyEl.scrollLeft += (e.deltaX || 0) * 10; - bodyEl.scrollTop += e.deltaY * 10; - - repaintScroll(); - }); - - $(self.getEl('body')).on("scroll", repaintScroll); - } - - repaintScroll(); - } - } - }; - } - ); - /** - * Panel.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a new panel. - * - * @-x-less Panel.less - * @class tinymce.ui.Panel - * @extends tinymce.ui.Container - * @mixes tinymce.ui.Scrollable - */ - define( - 'tinymce.core.ui.Panel', [ - "tinymce.core.ui.Container", - "tinymce.core.ui.Scrollable" - ], - function(Container, Scrollable) { - "use strict"; - - return Container.extend({ - Defaults: { - layout: 'fit', - containerCls: 'panel' - }, - - Mixins: [Scrollable], - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - layout = self._layout, - innerHtml = self.settings.html; - - self.preRender(); - layout.preRender(self); - - if (typeof innerHtml == "undefined") { - innerHtml = ( - '<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' + - layout.renderHtml(self) + - '</div>' - ); - } else { - if (typeof innerHtml == 'function') { - innerHtml = innerHtml.call(self); - } - - self._hasBody = false; - } - - return ( - '<div id="' + self._id + '" class="' + self.classes + '" hidefocus="1" tabindex="-1" role="group">' + - (self._preBodyHtml || '') + - innerHtml + - '</div>' - ); - } - }); - } - ); - - /** - * Movable.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Movable mixin. Makes controls movable absolute and relative to other elements. - * - * @mixin tinymce.ui.Movable - */ - define( - 'tinymce.core.ui.Movable', [ - "tinymce.core.ui.DomUtils" - ], - function(DomUtils) { - "use strict"; - - function calculateRelativePosition(ctrl, targetElm, rel) { - var ctrlElm, pos, x, y, selfW, selfH, targetW, targetH, viewport, size; - - viewport = DomUtils.getViewPort(); - - // Get pos of target - pos = DomUtils.getPos(targetElm); - x = pos.x; - y = pos.y; - - if (ctrl.state.get('fixed') && DomUtils.getRuntimeStyle(document.body, 'position') == 'static') { - x -= viewport.x; - y -= viewport.y; - } - - // Get size of self - ctrlElm = ctrl.getEl(); - size = DomUtils.getSize(ctrlElm); - selfW = size.width; - selfH = size.height; - - // Get size of target - size = DomUtils.getSize(targetElm); - targetW = size.width; - targetH = size.height; - - // Parse align string - rel = (rel || '').split(''); - - // Target corners - if (rel[0] === 'b') { - y += targetH; - } - - if (rel[1] === 'r') { - x += targetW; - } - - if (rel[0] === 'c') { - y += Math.round(targetH / 2); - } - - if (rel[1] === 'c') { - x += Math.round(targetW / 2); - } - - // Self corners - if (rel[3] === 'b') { - y -= selfH; - } - - if (rel[4] === 'r') { - x -= selfW; - } - - if (rel[3] === 'c') { - y -= Math.round(selfH / 2); - } - - if (rel[4] === 'c') { - x -= Math.round(selfW / 2); - } - - return { - x: x, - y: y, - w: selfW, - h: selfH - }; - } - - return { - /** - * Tests various positions to get the most suitable one. - * - * @method testMoveRel - * @param {DOMElement} elm Element to position against. - * @param {Array} rels Array with relative positions. - * @return {String} Best suitable relative position. - */ - testMoveRel: function(elm, rels) { - var viewPortRect = DomUtils.getViewPort(); - - for (var i = 0; i < rels.length; i++) { - var pos = calculateRelativePosition(this, elm, rels[i]); - - if (this.state.get('fixed')) { - if (pos.x > 0 && pos.x + pos.w < viewPortRect.w && pos.y > 0 && pos.y + pos.h < viewPortRect.h) { - return rels[i]; - } - } else { - if (pos.x > viewPortRect.x && pos.x + pos.w < viewPortRect.w + viewPortRect.x && - pos.y > viewPortRect.y && pos.y + pos.h < viewPortRect.h + viewPortRect.y) { - return rels[i]; - } - } - } - - return rels[0]; - }, - - /** - * Move relative to the specified element. - * - * @method moveRel - * @param {Element} elm Element to move relative to. - * @param {String} rel Relative mode. For example: br-tl. - * @return {tinymce.ui.Control} Current control instance. - */ - moveRel: function(elm, rel) { - if (typeof rel != 'string') { - rel = this.testMoveRel(elm, rel); - } - - var pos = calculateRelativePosition(this, elm, rel); - return this.moveTo(pos.x, pos.y); - }, - - /** - * Move by a relative x, y values. - * - * @method moveBy - * @param {Number} dx Relative x position. - * @param {Number} dy Relative y position. - * @return {tinymce.ui.Control} Current control instance. - */ - moveBy: function(dx, dy) { - var self = this, - rect = self.layoutRect(); - - self.moveTo(rect.x + dx, rect.y + dy); - - return self; - }, - - /** - * Move to absolute position. - * - * @method moveTo - * @param {Number} x Absolute x position. - * @param {Number} y Absolute y position. - * @return {tinymce.ui.Control} Current control instance. - */ - moveTo: function(x, y) { - var self = this; - - // TODO: Move this to some global class - function constrain(value, max, size) { - if (value < 0) { - return 0; - } - - if (value + size > max) { - value = max - size; - return value < 0 ? 0 : value; - } - - return value; - } - - if (self.settings.constrainToViewport) { - var viewPortRect = DomUtils.getViewPort(window); - var layoutRect = self.layoutRect(); - - x = constrain(x, viewPortRect.w + viewPortRect.x, layoutRect.w); - y = constrain(y, viewPortRect.h + viewPortRect.y, layoutRect.h); - } - - if (self.state.get('rendered')) { - self.layoutRect({ - x: x, - y: y - }).repaint(); - } else { - self.settings.x = x; - self.settings.y = y; - } - - self.fire('move', { - x: x, - y: y - }); - - return self; - } - }; - } - ); - /** - * Resizable.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Resizable mixin. Enables controls to be resized. - * - * @mixin tinymce.ui.Resizable - */ - define( - 'tinymce.core.ui.Resizable', [ - "tinymce.core.ui.DomUtils" - ], - function(DomUtils) { - "use strict"; - - return { - /** - * Resizes the control to contents. - * - * @method resizeToContent - */ - resizeToContent: function() { - this._layoutRect.autoResize = true; - this._lastRect = null; - this.reflow(); - }, - - /** - * Resizes the control to a specific width/height. - * - * @method resizeTo - * @param {Number} w Control width. - * @param {Number} h Control height. - * @return {tinymce.ui.Control} Current control instance. - */ - resizeTo: function(w, h) { - // TODO: Fix hack - if (w <= 1 || h <= 1) { - var rect = DomUtils.getWindowSize(); - - w = w <= 1 ? w * rect.w : w; - h = h <= 1 ? h * rect.h : h; - } - - this._layoutRect.autoResize = false; - return this.layoutRect({ - minW: w, - minH: h, - w: w, - h: h - }).reflow(); - }, - - /** - * Resizes the control to a specific relative width/height. - * - * @method resizeBy - * @param {Number} dw Relative control width. - * @param {Number} dh Relative control height. - * @return {tinymce.ui.Control} Current control instance. - */ - resizeBy: function(dw, dh) { - var self = this, - rect = self.layoutRect(); - - return self.resizeTo(rect.w + dw, rect.h + dh); - } - }; - } - ); - /** - * FloatPanel.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class creates a floating panel. - * - * @-x-less FloatPanel.less - * @class tinymce.ui.FloatPanel - * @extends tinymce.ui.Panel - * @mixes tinymce.ui.Movable - * @mixes tinymce.ui.Resizable - */ - define( - 'tinymce.core.ui.FloatPanel', [ - "tinymce.core.ui.Panel", - "tinymce.core.ui.Movable", - "tinymce.core.ui.Resizable", - "tinymce.core.ui.DomUtils", - "tinymce.core.dom.DomQuery", - "tinymce.core.util.Delay" - ], - function(Panel, Movable, Resizable, DomUtils, $, Delay) { - "use strict"; - - var documentClickHandler, documentScrollHandler, windowResizeHandler, visiblePanels = []; - var zOrder = [], - hasModal; - - function isChildOf(ctrl, parent) { - while (ctrl) { - if (ctrl == parent) { - return true; - } - - ctrl = ctrl.parent(); - } - } - - function skipOrHidePanels(e) { - // Hide any float panel when a click/focus out is out side that float panel and the - // float panels direct parent for example a click on a menu button - var i = visiblePanels.length; - - while (i--) { - var panel = visiblePanels[i], - clickCtrl = panel.getParentCtrl(e.target); - - if (panel.settings.autohide) { - if (clickCtrl) { - if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) { - continue; - } - } - - e = panel.fire('autohide', { - target: e.target - }); - if (!e.isDefaultPrevented()) { - panel.hide(); - } - } - } - } - - function bindDocumentClickHandler() { - - if (!documentClickHandler) { - documentClickHandler = function(e) { - // Gecko fires click event and in the wrong order on Mac so lets normalize - if (e.button == 2) { - return; - } - - skipOrHidePanels(e); - }; - - $(document).on('click touchstart', documentClickHandler); - } - } - - function bindDocumentScrollHandler() { - if (!documentScrollHandler) { - documentScrollHandler = function() { - var i; - - i = visiblePanels.length; - while (i--) { - repositionPanel(visiblePanels[i]); - } - }; - - $(window).on('scroll', documentScrollHandler); - } - } - - function bindWindowResizeHandler() { - if (!windowResizeHandler) { - var docElm = document.documentElement, - clientWidth = docElm.clientWidth, - clientHeight = docElm.clientHeight; - - windowResizeHandler = function() { - // Workaround for #7065 IE 7 fires resize events event though the window wasn't resized - if (!document.all || clientWidth != docElm.clientWidth || clientHeight != docElm.clientHeight) { - clientWidth = docElm.clientWidth; - clientHeight = docElm.clientHeight; - FloatPanel.hideAll(); - } - }; - - $(window).on('resize', windowResizeHandler); - } - } - - /** - * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will - * also reposition all child panels of the current panel. - */ - function repositionPanel(panel) { - var scrollY = DomUtils.getViewPort().y; - - function toggleFixedChildPanels(fixed, deltaY) { - var parent; - - for (var i = 0; i < visiblePanels.length; i++) { - if (visiblePanels[i] != panel) { - parent = visiblePanels[i].parent(); - - while (parent && (parent = parent.parent())) { - if (parent == panel) { - visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint(); - } - } - } - } - } - - if (panel.settings.autofix) { - if (!panel.state.get('fixed')) { - panel._autoFixY = panel.layoutRect().y; - - if (panel._autoFixY < scrollY) { - panel.fixed(true).layoutRect({ - y: 0 - }).repaint(); - toggleFixedChildPanels(true, scrollY - panel._autoFixY); - } - } else { - if (panel._autoFixY > scrollY) { - panel.fixed(false).layoutRect({ - y: panel._autoFixY - }).repaint(); - toggleFixedChildPanels(false, panel._autoFixY - scrollY); - } - } - } - } - - function addRemove(add, ctrl) { - var i, zIndex = FloatPanel.zIndex || 0xFFFF, - topModal; - - if (add) { - zOrder.push(ctrl); - } else { - i = zOrder.length; - - while (i--) { - if (zOrder[i] === ctrl) { - zOrder.splice(i, 1); - } - } - } - - if (zOrder.length) { - for (i = 0; i < zOrder.length; i++) { - if (zOrder[i].modal) { - zIndex++; - topModal = zOrder[i]; - } - - zOrder[i].getEl().style.zIndex = zIndex; - zOrder[i].zIndex = zIndex; - zIndex++; - } - } - - var modalBlockEl = $('#' + ctrl.classPrefix + 'modal-block', ctrl.getContainerElm())[0]; - - if (topModal) { - $(modalBlockEl).css('z-index', topModal.zIndex - 1); - } else if (modalBlockEl) { - modalBlockEl.parentNode.removeChild(modalBlockEl); - hasModal = false; - } - - FloatPanel.currentZIndex = zIndex; - } - - var FloatPanel = Panel.extend({ - Mixins: [Movable, Resizable], - - /** - * Constructs a new control instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {Boolean} autohide Automatically hide the panel. - */ - init: function(settings) { - var self = this; - - self._super(settings); - self._eventsRoot = self; - - self.classes.add('floatpanel'); - - // Hide floatpanes on click out side the root button - if (settings.autohide) { - bindDocumentClickHandler(); - bindWindowResizeHandler(); - visiblePanels.push(self); - } - - if (settings.autofix) { - bindDocumentScrollHandler(); - - self.on('move', function() { - repositionPanel(this); - }); - } - - self.on('postrender show', function(e) { - if (e.control == self) { - var $modalBlockEl, prefix = self.classPrefix; - - if (self.modal && !hasModal) { - $modalBlockEl = $('#' + prefix + 'modal-block', self.getContainerElm()); - if (!$modalBlockEl[0]) { - $modalBlockEl = $( - '<div id="' + prefix + 'modal-block" class="' + prefix + 'reset ' + prefix + 'fade"></div>' - ).appendTo(self.getContainerElm()); - } - - Delay.setTimeout(function() { - $modalBlockEl.addClass(prefix + 'in'); - $(self.getEl()).addClass(prefix + 'in'); - }); - - hasModal = true; - } - - addRemove(true, self); - } - }); - - self.on('show', function() { - self.parents().each(function(ctrl) { - if (ctrl.state.get('fixed')) { - self.fixed(true); - return false; - } - }); - }); - - if (settings.popover) { - self._preBodyHtml = '<div class="' + self.classPrefix + 'arrow"></div>'; - self.classes.add('popover').add('bottom').add(self.isRtl() ? 'end' : 'start'); - } - - self.aria('label', settings.ariaLabel); - self.aria('labelledby', self._id); - self.aria('describedby', self.describedBy || self._id + '-none'); - }, - - fixed: function(state) { - var self = this; - - if (self.state.get('fixed') != state) { - if (self.state.get('rendered')) { - var viewport = DomUtils.getViewPort(); - - if (state) { - self.layoutRect().y -= viewport.y; - } else { - self.layoutRect().y += viewport.y; - } - } - - self.classes.toggle('fixed', state); - self.state.set('fixed', state); - } - - return self; - }, - - /** - * Shows the current float panel. - * - * @method show - * @return {tinymce.ui.FloatPanel} Current floatpanel instance. - */ - show: function() { - var self = this, - i, state = self._super(); - - i = visiblePanels.length; - while (i--) { - if (visiblePanels[i] === self) { - break; - } - } - - if (i === -1) { - visiblePanels.push(self); - } - - return state; - }, - - /** - * Hides the current float panel. - * - * @method hide - * @return {tinymce.ui.FloatPanel} Current floatpanel instance. - */ - hide: function() { - removeVisiblePanel(this); - addRemove(false, this); - - return this._super(); - }, - - /** - * Hide all visible float panels with he autohide setting enabled. This is for - * manually hiding floating menus or panels. - * - * @method hideAll - */ - hideAll: function() { - FloatPanel.hideAll(); - }, - - /** - * Closes the float panel. This will remove the float panel from page and fire the close event. - * - * @method close - */ - close: function() { - var self = this; - - if (!self.fire('close').isDefaultPrevented()) { - self.remove(); - addRemove(false, self); - } - - return self; - }, - - /** - * Removes the float panel from page. - * - * @method remove - */ - remove: function() { - removeVisiblePanel(this); - this._super(); - }, - - postRender: function() { - var self = this; - - if (self.settings.bodyRole) { - this.getEl('body').setAttribute('role', self.settings.bodyRole); - } - - return self._super(); - } - }); - - /** - * Hide all visible float panels with he autohide setting enabled. This is for - * manually hiding floating menus or panels. - * - * @static - * @method hideAll - */ - FloatPanel.hideAll = function() { - var i = visiblePanels.length; - - while (i--) { - var panel = visiblePanels[i]; - - if (panel && panel.settings.autohide) { - panel.hide(); - visiblePanels.splice(i, 1); - } - } - }; - - function removeVisiblePanel(panel) { - var i; - - i = visiblePanels.length; - while (i--) { - if (visiblePanels[i] === panel) { - visiblePanels.splice(i, 1); - } - } - - i = zOrder.length; - while (i--) { - if (zOrder[i] === panel) { - zOrder.splice(i, 1); - } - } - } - - return FloatPanel; - } - ); - - /** - * Window.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a new window. - * - * @-x-less Window.less - * @class tinymce.ui.Window - * @extends tinymce.ui.FloatPanel - */ - define( - 'tinymce.core.ui.Window', [ - "tinymce.core.ui.FloatPanel", - "tinymce.core.ui.Panel", - "tinymce.core.ui.DomUtils", - "tinymce.core.dom.DomQuery", - "tinymce.core.ui.DragHelper", - "tinymce.core.ui.BoxUtils", - "tinymce.core.Env", - "tinymce.core.util.Delay" - ], - function(FloatPanel, Panel, DomUtils, $, DragHelper, BoxUtils, Env, Delay) { - "use strict"; - - var windows = [], - oldMetaValue = ''; - - function toggleFullScreenState(state) { - var noScaleMetaValue = 'width=device-width,initial-scale=1.0,user-scalable=0,minimum-scale=1.0,maximum-scale=1.0', - viewport = $("meta[name=viewport]")[0], - contentValue; - - if (Env.overrideViewPort === false) { - return; - } - - if (!viewport) { - viewport = document.createElement('meta'); - viewport.setAttribute('name', 'viewport'); - document.getElementsByTagName('head')[0].appendChild(viewport); - } - - contentValue = viewport.getAttribute('content'); - if (contentValue && typeof oldMetaValue != 'undefined') { - oldMetaValue = contentValue; - } - - viewport.setAttribute('content', state ? noScaleMetaValue : oldMetaValue); - } - - function toggleBodyFullScreenClasses(classPrefix, state) { - if (checkFullscreenWindows() && state === false) { - $([document.documentElement, document.body]).removeClass(classPrefix + 'fullscreen'); - } - } - - function checkFullscreenWindows() { - for (var i = 0; i < windows.length; i++) { - if (windows[i]._fullscreen) { - return true; - } - } - return false; - } - - function handleWindowResize() { - if (!Env.desktop) { - var lastSize = { - w: window.innerWidth, - h: window.innerHeight - }; - - Delay.setInterval(function() { - var w = window.innerWidth, - h = window.innerHeight; - - if (lastSize.w != w || lastSize.h != h) { - lastSize = { - w: w, - h: h - }; - - $(window).trigger('resize'); - } - }, 100); - } - - function reposition() { - var i, rect = DomUtils.getWindowSize(), - layoutRect; - - for (i = 0; i < windows.length; i++) { - layoutRect = windows[i].layoutRect(); - - windows[i].moveTo( - windows[i].settings.x || Math.max(0, rect.w / 2 - layoutRect.w / 2), - windows[i].settings.y || Math.max(0, rect.h / 2 - layoutRect.h / 2) - ); - } - } - - $(window).on('resize', reposition); - } - - var Window = FloatPanel.extend({ - modal: true, - - Defaults: { - border: 1, - layout: 'flex', - containerCls: 'panel', - role: 'dialog', - callbacks: { - submit: function() { - this.fire('submit', { - data: this.toJSON() - }); - }, - - close: function() { - this.close(); - } - } - }, - - /** - * Constructs a instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - */ - init: function(settings) { - var self = this; - - self._super(settings); - - if (self.isRtl()) { - self.classes.add('rtl'); - } - - self.classes.add('window'); - self.bodyClasses.add('window-body'); - self.state.set('fixed', true); - - // Create statusbar - if (settings.buttons) { - self.statusbar = new Panel({ - layout: 'flex', - border: '1 0 0 0', - spacing: 3, - padding: 10, - align: 'center', - pack: self.isRtl() ? 'start' : 'end', - defaults: { - type: 'button' - }, - items: settings.buttons - }); - - self.statusbar.classes.add('foot'); - self.statusbar.parent(self); - } - - self.on('click', function(e) { - var closeClass = self.classPrefix + 'close'; - - if (DomUtils.hasClass(e.target, closeClass) || DomUtils.hasClass(e.target.parentNode, closeClass)) { - self.close(); - } - }); - - self.on('cancel', function() { - self.close(); - }); - - self.aria('describedby', self.describedBy || self._id + '-none'); - self.aria('label', settings.title); - self._fullscreen = false; - }, - - /** - * Recalculates the positions of the controls in the current container. - * This is invoked by the reflow method and shouldn't be called directly. - * - * @method recalc - */ - recalc: function() { - var self = this, - statusbar = self.statusbar, - layoutRect, width, x, needsRecalc; - - if (self._fullscreen) { - self.layoutRect(DomUtils.getWindowSize()); - self.layoutRect().contentH = self.layoutRect().innerH; - } - - self._super(); - - layoutRect = self.layoutRect(); - - // Resize window based on title width - if (self.settings.title && !self._fullscreen) { - width = layoutRect.headerW; - if (width > layoutRect.w) { - x = layoutRect.x - Math.max(0, width / 2); - self.layoutRect({ - w: width, - x: x - }); - needsRecalc = true; - } - } - - // Resize window based on statusbar width - if (statusbar) { - statusbar.layoutRect({ - w: self.layoutRect().innerW - }).recalc(); - - width = statusbar.layoutRect().minW + layoutRect.deltaW; - if (width > layoutRect.w) { - x = layoutRect.x - Math.max(0, width - layoutRect.w); - self.layoutRect({ - w: width, - x: x - }); - needsRecalc = true; - } - } - - // Recalc body and disable auto resize - if (needsRecalc) { - self.recalc(); - } - }, - - /** - * Initializes the current controls layout rect. - * This will be executed by the layout managers to determine the - * default minWidth/minHeight etc. - * - * @method initLayoutRect - * @return {Object} Layout rect instance. - */ - initLayoutRect: function() { - var self = this, - layoutRect = self._super(), - deltaH = 0, - headEl; - - // Reserve vertical space for title - if (self.settings.title && !self._fullscreen) { - headEl = self.getEl('head'); - - var size = DomUtils.getSize(headEl); - - layoutRect.headerW = size.width; - layoutRect.headerH = size.height; - - deltaH += layoutRect.headerH; - } - - // Reserve vertical space for statusbar - if (self.statusbar) { - deltaH += self.statusbar.layoutRect().h; - } - - layoutRect.deltaH += deltaH; - layoutRect.minH += deltaH; - //layoutRect.innerH -= deltaH; - layoutRect.h += deltaH; - - var rect = DomUtils.getWindowSize(); - - layoutRect.x = self.settings.x || Math.max(0, rect.w / 2 - layoutRect.w / 2); - layoutRect.y = self.settings.y || Math.max(0, rect.h / 2 - layoutRect.h / 2); - - return layoutRect; - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - layout = self._layout, - id = self._id, - prefix = self.classPrefix; - var settings = self.settings, - headerHtml = '', - footerHtml = '', - html = settings.html; - - self.preRender(); - layout.preRender(self); - - if (settings.title) { - headerHtml = ( - '<div id="' + id + '-head" class="' + prefix + 'window-head">' + - '<div id="' + id + '-title" class="' + prefix + 'title">' + self.encode(settings.title) + '</div>' + - '<div id="' + id + '-dragh" class="' + prefix + 'dragh"></div>' + - '<button type="button" class="' + prefix + 'close" aria-hidden="true">' + - '<i class="mce-ico mce-i-remove"></i>' + - '</button>' + - '</div>' - ); - } - - if (settings.url) { - html = '<iframe src="' + settings.url + '" tabindex="-1"></iframe>'; - } - - if (typeof html == "undefined") { - html = layout.renderHtml(self); - } - - if (self.statusbar) { - footerHtml = self.statusbar.renderHtml(); - } - - return ( - '<div id="' + id + '" class="' + self.classes + '" hidefocus="1">' + - '<div class="' + self.classPrefix + 'reset" role="application">' + - headerHtml + - '<div id="' + id + '-body" class="' + self.bodyClasses + '">' + - html + - '</div>' + - footerHtml + - '</div>' + - '</div>' - ); - }, - - /** - * Switches the window fullscreen mode. - * - * @method fullscreen - * @param {Boolean} state True/false state. - * @return {tinymce.ui.Window} Current window instance. - */ - fullscreen: function(state) { - var self = this, - documentElement = document.documentElement, - slowRendering, prefix = self.classPrefix, - layoutRect; - - if (state != self._fullscreen) { - $(window).on('resize', function() { - var time; - - if (self._fullscreen) { - // Time the layout time if it's to slow use a timeout to not hog the CPU - if (!slowRendering) { - time = new Date().getTime(); - - var rect = DomUtils.getWindowSize(); - self.moveTo(0, 0).resizeTo(rect.w, rect.h); - - if ((new Date().getTime()) - time > 50) { - slowRendering = true; - } - } else { - if (!self._timer) { - self._timer = Delay.setTimeout(function() { - var rect = DomUtils.getWindowSize(); - self.moveTo(0, 0).resizeTo(rect.w, rect.h); - - self._timer = 0; - }, 50); - } - } - } - }); - - layoutRect = self.layoutRect(); - self._fullscreen = state; - - if (!state) { - self.borderBox = BoxUtils.parseBox(self.settings.border); - self.getEl('head').style.display = ''; - layoutRect.deltaH += layoutRect.headerH; - $([documentElement, document.body]).removeClass(prefix + 'fullscreen'); - self.classes.remove('fullscreen'); - self.moveTo(self._initial.x, self._initial.y).resizeTo(self._initial.w, self._initial.h); - } else { - self._initial = { - x: layoutRect.x, - y: layoutRect.y, - w: layoutRect.w, - h: layoutRect.h - }; - - self.borderBox = BoxUtils.parseBox('0'); - self.getEl('head').style.display = 'none'; - layoutRect.deltaH -= layoutRect.headerH + 2; - $([documentElement, document.body]).addClass(prefix + 'fullscreen'); - self.classes.add('fullscreen'); - - var rect = DomUtils.getWindowSize(); - self.moveTo(0, 0).resizeTo(rect.w, rect.h); - } - } - - return self.reflow(); - }, - - /** - * Called after the control has been rendered. - * - * @method postRender - */ - postRender: function() { - var self = this, - startPos; - - setTimeout(function() { - self.classes.add('in'); - self.fire('open'); - }, 0); - - self._super(); - - if (self.statusbar) { - self.statusbar.postRender(); - } - - self.focus(); - - this.dragHelper = new DragHelper(self._id + '-dragh', { - start: function() { - startPos = { - x: self.layoutRect().x, - y: self.layoutRect().y - }; - }, - - drag: function(e) { - self.moveTo(startPos.x + e.deltaX, startPos.y + e.deltaY); - } - }); - - self.on('submit', function(e) { - if (!e.isDefaultPrevented()) { - self.close(); - } - }); - - windows.push(self); - toggleFullScreenState(true); - }, - - /** - * Fires a submit event with the serialized form. - * - * @method submit - * @return {Object} Event arguments object. - */ - submit: function() { - return this.fire('submit', { - data: this.toJSON() - }); - }, - - /** - * Removes the current control from DOM and from UI collections. - * - * @method remove - * @return {tinymce.ui.Control} Current control instance. - */ - remove: function() { - var self = this, - i; - - self.dragHelper.destroy(); - self._super(); - - if (self.statusbar) { - this.statusbar.remove(); - } - - toggleBodyFullScreenClasses(self.classPrefix, false); - - i = windows.length; - while (i--) { - if (windows[i] === self) { - windows.splice(i, 1); - } - } - - toggleFullScreenState(windows.length > 0); - }, - - /** - * Returns the contentWindow object of the iframe if it exists. - * - * @method getContentWindow - * @return {Window} window object or null. - */ - getContentWindow: function() { - var ifr = this.getEl().getElementsByTagName('iframe')[0]; - return ifr ? ifr.contentWindow : null; - } - }); - - handleWindowResize(); - - return Window; - } - ); - /** - * MessageBox.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class is used to create MessageBoxes like alerts/confirms etc. - * - * @class tinymce.ui.MessageBox - * @extends tinymce.ui.FloatPanel - */ - define( - 'tinymce.core.ui.MessageBox', [ - "tinymce.core.ui.Window" - ], - function(Window) { - "use strict"; - - var MessageBox = Window.extend({ - /** - * Constructs a instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - */ - init: function(settings) { - settings = { - border: 1, - padding: 20, - layout: 'flex', - pack: "center", - align: "center", - containerCls: 'panel', - autoScroll: true, - buttons: { - type: "button", - text: "Ok", - action: "ok" - }, - items: { - type: "label", - multiline: true, - maxWidth: 500, - maxHeight: 200 - } - }; - - this._super(settings); - }, - - Statics: { - /** - * Ok buttons constant. - * - * @static - * @final - * @field {Number} OK - */ - OK: 1, - - /** - * Ok/cancel buttons constant. - * - * @static - * @final - * @field {Number} OK_CANCEL - */ - OK_CANCEL: 2, - - /** - * yes/no buttons constant. - * - * @static - * @final - * @field {Number} YES_NO - */ - YES_NO: 3, - - /** - * yes/no/cancel buttons constant. - * - * @static - * @final - * @field {Number} YES_NO_CANCEL - */ - YES_NO_CANCEL: 4, - - /** - * Constructs a new message box and renders it to the body element. - * - * @static - * @method msgBox - * @param {Object} settings Name/value object with settings. - */ - msgBox: function(settings) { - var buttons, callback = settings.callback || function() {}; - - function createButton(text, status, primary) { - return { - type: "button", - text: text, - subtype: primary ? 'primary' : '', - onClick: function(e) { - e.control.parents()[1].close(); - callback(status); - } - }; - } - - switch (settings.buttons) { - case MessageBox.OK_CANCEL: - buttons = [ - createButton('Ok', true, true), - createButton('Cancel', false) - ]; - break; - - case MessageBox.YES_NO: - case MessageBox.YES_NO_CANCEL: - buttons = [ - createButton('Yes', 1, true), - createButton('No', 0) - ]; - - if (settings.buttons == MessageBox.YES_NO_CANCEL) { - buttons.push(createButton('Cancel', -1)); - } - break; - - default: - buttons = [ - createButton('Ok', true, true) - ]; - break; - } - - return new Window({ - padding: 20, - x: settings.x, - y: settings.y, - minWidth: 300, - minHeight: 100, - layout: "flex", - pack: "center", - align: "center", - buttons: buttons, - title: settings.title, - role: 'alertdialog', - items: { - type: "label", - multiline: true, - maxWidth: 500, - maxHeight: 200, - text: settings.text - }, - onPostRender: function() { - this.aria('describedby', this.items()[0]._id); - }, - onClose: settings.onClose, - onCancel: function() { - callback(false); - } - }).renderTo(document.body).reflow(); - }, - - /** - * Creates a new alert dialog. - * - * @method alert - * @param {Object} settings Settings for the alert dialog. - * @param {function} [callback] Callback to execute when the user makes a choice. - */ - alert: function(settings, callback) { - if (typeof settings == "string") { - settings = { - text: settings - }; - } - - settings.callback = callback; - return MessageBox.msgBox(settings); - }, - - /** - * Creates a new confirm dialog. - * - * @method confirm - * @param {Object} settings Settings for the confirm dialog. - * @param {function} [callback] Callback to execute when the user makes a choice. - */ - confirm: function(settings, callback) { - if (typeof settings == "string") { - settings = { - text: settings - }; - } - - settings.callback = callback; - settings.buttons = MessageBox.OK_CANCEL; - - return MessageBox.msgBox(settings); - } - } - }); - - return MessageBox; - } - ); - - /** - * WindowManager.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class handles the creation of native windows and dialogs. This class can be extended to provide for example inline dialogs. - * - * @class tinymce.WindowManager - * @example - * // Opens a new dialog with the file.htm file and the size 320x240 - * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog. - * tinymce.activeEditor.windowManager.open({ - * url: 'file.htm', - * width: 320, - * height: 240 - * }, { - * custom_param: 1 - * }); - * - * // Displays an alert box using the active editors window manager instance - * tinymce.activeEditor.windowManager.alert('Hello world!'); - * - * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm - * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) { - * if (s) - * tinymce.activeEditor.windowManager.alert("Ok"); - * else - * tinymce.activeEditor.windowManager.alert("Cancel"); - * }); - */ - define( - 'tinymce.core.WindowManager', [ - "tinymce.core.ui.Window", - "tinymce.core.ui.MessageBox" - ], - function(Window, MessageBox) { - return function(editor) { - var self = this, - windows = []; - - function getTopMostWindow() { - if (windows.length) { - return windows[windows.length - 1]; - } - } - - function fireOpenEvent(win) { - editor.fire('OpenWindow', { - win: win - }); - } - - function fireCloseEvent(win) { - editor.fire('CloseWindow', { - win: win - }); - } - - self.windows = windows; - - editor.on('remove', function() { - var i = windows.length; - - while (i--) { - windows[i].close(); - } - }); - - /** - * Opens a new window. - * - * @method open - * @param {Object} args Optional name/value settings collection contains things like width/height/url etc. - * @param {Object} params Options like title, file, width, height etc. - * @option {String} title Window title. - * @option {String} file URL of the file to open in the window. - * @option {Number} width Width in pixels. - * @option {Number} height Height in pixels. - * @option {Boolean} autoScroll Specifies whether the popup window can have scrollbars if required (i.e. content - * larger than the popup size specified). - */ - self.open = function(args, params) { - var win; - - editor.editorManager.setActive(editor); - - args.title = args.title || ' '; - - // Handle URL - args.url = args.url || args.file; // Legacy - if (args.url) { - args.width = parseInt(args.width || 320, 10); - args.height = parseInt(args.height || 240, 10); - } - - // Handle body - if (args.body) { - args.items = { - defaults: args.defaults, - type: args.bodyType || 'form', - items: args.body, - data: args.data, - callbacks: args.commands - }; - } - - if (!args.url && !args.buttons) { - args.buttons = [{ - text: 'Ok', - subtype: 'primary', - onclick: function() { - win.find('form')[0].submit(); - } - }, - - { - text: 'Cancel', - onclick: function() { - win.close(); - } - } - ]; - } - - win = new Window(args); - windows.push(win); - - win.on('close', function() { - var i = windows.length; - - while (i--) { - if (windows[i] === win) { - windows.splice(i, 1); - } - } - - if (!windows.length) { - editor.focus(); - } - - fireCloseEvent(win); - }); - - // Handle data - if (args.data) { - win.on('postRender', function() { - this.find('*').each(function(ctrl) { - var name = ctrl.name(); - - if (name in args.data) { - ctrl.value(args.data[name]); - } - }); - }); - } - - // store args and parameters - win.features = args || {}; - win.params = params || {}; - - // Takes a snapshot in the FocusManager of the selection before focus is lost to dialog - if (windows.length === 1) { - editor.nodeChanged(); - } - - win = win.renderTo().reflow(); - - fireOpenEvent(win); - - return win; - }; - - /** - * Creates a alert dialog. Please don't use the blocking behavior of this - * native version use the callback method instead then it can be extended. - * - * @method alert - * @param {String} message Text to display in the new alert dialog. - * @param {function} callback Callback function to be executed after the user has selected ok. - * @param {Object} scope Optional scope to execute the callback in. - * @example - * // Displays an alert box using the active editors window manager instance - * tinymce.activeEditor.windowManager.alert('Hello world!'); - */ - self.alert = function(message, callback, scope) { - var win; - - win = MessageBox.alert(message, function() { - if (callback) { - callback.call(scope || this); - } else { - editor.focus(); - } - }); - - win.on('close', function() { - fireCloseEvent(win); - }); - - fireOpenEvent(win); - }; - - /** - * Creates a confirm dialog. Please don't use the blocking behavior of this - * native version use the callback method instead then it can be extended. - * - * @method confirm - * @param {String} message Text to display in the new confirm dialog. - * @param {function} callback Callback function to be executed after the user has selected ok or cancel. - * @param {Object} scope Optional scope to execute the callback in. - * @example - * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm - * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) { - * if (s) - * tinymce.activeEditor.windowManager.alert("Ok"); - * else - * tinymce.activeEditor.windowManager.alert("Cancel"); - * }); - */ - self.confirm = function(message, callback, scope) { - var win; - - win = MessageBox.confirm(message, function(state) { - callback.call(scope || this, state); - }); - - win.on('close', function() { - fireCloseEvent(win); - }); - - fireOpenEvent(win); - }; - - /** - * Closes the top most window. - * - * @method close - */ - self.close = function() { - if (getTopMostWindow()) { - getTopMostWindow().close(); - } - }; - - /** - * Returns the params of the last window open call. This can be used in iframe based - * dialog to get params passed from the tinymce plugin. - * - * @example - * var dialogArguments = top.tinymce.activeEditor.windowManager.getParams(); - * - * @method getParams - * @return {Object} Name/value object with parameters passed from windowManager.open call. - */ - self.getParams = function() { - return getTopMostWindow() ? getTopMostWindow().params : null; - }; - - /** - * Sets the params of the last opened window. - * - * @method setParams - * @param {Object} params Params object to set for the last opened window. - */ - self.setParams = function(params) { - if (getTopMostWindow()) { - getTopMostWindow().params = params; - } - }; - - /** - * Returns the currently opened window objects. - * - * @method getWindows - * @return {Array} Array of the currently opened windows. - */ - self.getWindows = function() { - return windows; - }; - }; - } - ); - - /** - * Tooltip.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a tooltip instance. - * - * @-x-less ToolTip.less - * @class tinymce.ui.ToolTip - * @extends tinymce.ui.Control - * @mixes tinymce.ui.Movable - */ - define( - 'tinymce.core.ui.Tooltip', [ - "tinymce.core.ui.Control", - "tinymce.core.ui.Movable" - ], - function(Control, Movable) { - return Control.extend({ - Mixins: [Movable], - - Defaults: { - classes: 'widget tooltip tooltip-n' - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - prefix = self.classPrefix; - - return ( - '<div id="' + self._id + '" class="' + self.classes + '" role="presentation">' + - '<div class="' + prefix + 'tooltip-arrow"></div>' + - '<div class="' + prefix + 'tooltip-inner">' + self.encode(self.state.get('text')) + '</div>' + - '</div>' - ); - }, - - bindStates: function() { - var self = this; - - self.state.on('change:text', function(e) { - self.getEl().lastChild.innerHTML = self.encode(e.value); - }); - - return self._super(); - }, - - /** - * Repaints the control after a layout operation. - * - * @method repaint - */ - repaint: function() { - var self = this, - style, rect; - - style = self.getEl().style; - rect = self._layoutRect; - - style.left = rect.x + 'px'; - style.top = rect.y + 'px'; - style.zIndex = 0xFFFF + 0xFFFF; - } - }); - } - ); - /** - * Widget.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Widget base class a widget is a control that has a tooltip and some basic states. - * - * @class tinymce.ui.Widget - * @extends tinymce.ui.Control - */ - define( - 'tinymce.core.ui.Widget', [ - "tinymce.core.ui.Control", - "tinymce.core.ui.Tooltip" - ], - function(Control, Tooltip) { - "use strict"; - - var tooltip; - - var Widget = Control.extend({ - /** - * Constructs a instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {String} tooltip Tooltip text to display when hovering. - * @setting {Boolean} autofocus True if the control should be focused when rendered. - * @setting {String} text Text to display inside widget. - */ - init: function(settings) { - var self = this; - - self._super(settings); - settings = self.settings; - self.canFocus = true; - - if (settings.tooltip && Widget.tooltips !== false) { - self.on('mouseenter', function(e) { - var tooltip = self.tooltip().moveTo(-0xFFFF); - - if (e.control == self) { - var rel = tooltip.text(settings.tooltip).show().testMoveRel(self.getEl(), ['bc-tc', 'bc-tl', 'bc-tr']); - - tooltip.classes.toggle('tooltip-n', rel == 'bc-tc'); - tooltip.classes.toggle('tooltip-nw', rel == 'bc-tl'); - tooltip.classes.toggle('tooltip-ne', rel == 'bc-tr'); - - tooltip.moveRel(self.getEl(), rel); - } else { - tooltip.hide(); - } - }); - - self.on('mouseleave mousedown click', function() { - self.tooltip().hide(); - }); - } - - self.aria('label', settings.ariaLabel || settings.tooltip); - }, - - /** - * Returns the current tooltip instance. - * - * @method tooltip - * @return {tinymce.ui.Tooltip} Tooltip instance. - */ - tooltip: function() { - if (!tooltip) { - tooltip = new Tooltip({ - type: 'tooltip' - }); - tooltip.renderTo(); - } - - return tooltip; - }, - - /** - * Called after the control has been rendered. - * - * @method postRender - */ - postRender: function() { - var self = this, - settings = self.settings; - - self._super(); - - if (!self.parent() && (settings.width || settings.height)) { - self.initLayoutRect(); - self.repaint(); - } - - if (settings.autofocus) { - self.focus(); - } - }, - - bindStates: function() { - var self = this; - - function disable(state) { - self.aria('disabled', state); - self.classes.toggle('disabled', state); - } - - function active(state) { - self.aria('pressed', state); - self.classes.toggle('active', state); - } - - self.state.on('change:disabled', function(e) { - disable(e.value); - }); - - self.state.on('change:active', function(e) { - active(e.value); - }); - - if (self.state.get('disabled')) { - disable(true); - } - - if (self.state.get('active')) { - active(true); - } - - return self._super(); - }, - - /** - * Removes the current control from DOM and from UI collections. - * - * @method remove - * @return {tinymce.ui.Control} Current control instance. - */ - remove: function() { - this._super(); - - if (tooltip) { - tooltip.remove(); - tooltip = null; - } - } - }); - - return Widget; - } - ); - - /** - * Progress.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Progress control. - * - * @-x-less Progress.less - * @class tinymce.ui.Progress - * @extends tinymce.ui.Control - */ - define( - 'tinymce.core.ui.Progress', [ - "tinymce.core.ui.Widget" - ], - function(Widget) { - "use strict"; - - return Widget.extend({ - Defaults: { - value: 0 - }, - - init: function(settings) { - var self = this; - - self._super(settings); - self.classes.add('progress'); - - if (!self.settings.filter) { - self.settings.filter = function(value) { - return Math.round(value); - }; - } - }, - - renderHtml: function() { - var self = this, - id = self._id, - prefix = this.classPrefix; - - return ( - '<div id="' + id + '" class="' + self.classes + '">' + - '<div class="' + prefix + 'bar-container">' + - '<div class="' + prefix + 'bar"></div>' + - '</div>' + - '<div class="' + prefix + 'text">0%</div>' + - '</div>' - ); - }, - - postRender: function() { - var self = this; - - self._super(); - self.value(self.settings.value); - - return self; - }, - - bindStates: function() { - var self = this; - - function setValue(value) { - value = self.settings.filter(value); - self.getEl().lastChild.innerHTML = value + '%'; - self.getEl().firstChild.firstChild.style.width = value + '%'; - } - - self.state.on('change:value', function(e) { - setValue(e.value); - }); - - setValue(self.state.get('value')); - - return self._super(); - } - }); - } - ); - /** - * Notification.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a notification instance. - * - * @-x-less Notification.less - * @class tinymce.ui.Notification - * @extends tinymce.ui.Container - * @mixes tinymce.ui.Movable - */ - define( - 'tinymce.core.ui.Notification', [ - "tinymce.core.ui.Control", - "tinymce.core.ui.Movable", - "tinymce.core.ui.Progress", - "tinymce.core.util.Delay" - ], - function(Control, Movable, Progress, Delay) { - return Control.extend({ - Mixins: [Movable], - - Defaults: { - classes: 'widget notification' - }, - - init: function(settings) { - var self = this; - - self._super(settings); - - if (settings.text) { - self.text(settings.text); - } - - if (settings.icon) { - self.icon = settings.icon; - } - - if (settings.color) { - self.color = settings.color; - } - - if (settings.type) { - self.classes.add('notification-' + settings.type); - } - - if (settings.timeout && (settings.timeout < 0 || settings.timeout > 0) && !settings.closeButton) { - self.closeButton = false; - } else { - self.classes.add('has-close'); - self.closeButton = true; - } - - if (settings.progressBar) { - self.progressBar = new Progress(); - } - - self.on('click', function(e) { - if (e.target.className.indexOf(self.classPrefix + 'close') != -1) { - self.close(); - } - }); - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - prefix = self.classPrefix, - icon = '', - closeButton = '', - progressBar = '', - notificationStyle = ''; - - if (self.icon) { - icon = '<i class="' + prefix + 'ico' + ' ' + prefix + 'i-' + self.icon + '"></i>'; - } - - if (self.color) { - notificationStyle = ' style="background-color: ' + self.color + '"'; - } - - if (self.closeButton) { - closeButton = '<button type="button" class="' + prefix + 'close" aria-hidden="true">\u00d7</button>'; - } - - if (self.progressBar) { - progressBar = self.progressBar.renderHtml(); - } - - return ( - '<div id="' + self._id + '" class="' + self.classes + '"' + notificationStyle + ' role="presentation">' + - icon + - '<div class="' + prefix + 'notification-inner">' + self.state.get('text') + '</div>' + - progressBar + - closeButton + - '</div>' - ); - }, - - postRender: function() { - var self = this; - - Delay.setTimeout(function() { - self.$el.addClass(self.classPrefix + 'in'); - }); - - return self._super(); - }, - - bindStates: function() { - var self = this; - - self.state.on('change:text', function(e) { - self.getEl().childNodes[1].innerHTML = e.value; - }); - if (self.progressBar) { - self.progressBar.bindStates(); - } - return self._super(); - }, - - close: function() { - var self = this; - - if (!self.fire('close').isDefaultPrevented()) { - self.remove(); - } - - return self; - }, - - /** - * Repaints the control after a layout operation. - * - * @method repaint - */ - repaint: function() { - var self = this, - style, rect; - - style = self.getEl().style; - rect = self._layoutRect; - - style.left = rect.x + 'px'; - style.top = rect.y + 'px'; - - // Hardcoded arbitrary z-value because we want the - // notifications under the other windows - style.zIndex = 0xFFFF - 1; - } - }); - } - ); - /** - * NotificationManager.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class handles the creation of TinyMCE's notifications. - * - * @class tinymce.NotificationManager - * @example - * // Opens a new notification of type "error" with text "An error occurred." - * tinymce.activeEditor.notificationManager.open({ - * text: 'An error occurred.', - * type: 'error' - * }); - */ - define( - 'tinymce.core.NotificationManager', [ - "tinymce.core.ui.Notification", - "tinymce.core.util.Delay", - "tinymce.core.util.Tools" - ], - function(Notification, Delay, Tools) { - return function(editor) { - var self = this, - notifications = []; - - function getLastNotification() { - if (notifications.length) { - return notifications[notifications.length - 1]; - } - } - - self.notifications = notifications; - - function resizeWindowEvent() { - Delay.requestAnimationFrame(function() { - prePositionNotifications(); - positionNotifications(); - }); - } - - // Since the viewport will change based on the present notifications, we need to move them all to the - // top left of the viewport to give an accurate size measurement so we can position them later. - function prePositionNotifications() { - for (var i = 0; i < notifications.length; i++) { - notifications[i].moveTo(0, 0); - } - } - - function positionNotifications() { - if (notifications.length > 0) { - var firstItem = notifications.slice(0, 1)[0]; - var container = editor.inline ? editor.getElement() : editor.getContentAreaContainer(); - firstItem.moveRel(container, 'tc-tc'); - if (notifications.length > 1) { - for (var i = 1; i < notifications.length; i++) { - notifications[i].moveRel(notifications[i - 1].getEl(), 'bc-tc'); - } - } - } - } - - editor.on('remove', function() { - var i = notifications.length; - - while (i--) { - notifications[i].close(); - } - }); - - editor.on('ResizeEditor', positionNotifications); - editor.on('ResizeWindow', resizeWindowEvent); - - /** - * Opens a new notification. - * - * @method open - * @param {Object} args Optional name/value settings collection contains things like timeout/color/message etc. - */ - self.open = function(args) { - // Never open notification if editor has been removed. - if (editor.removed) { - return; - } - - var notif; - - editor.editorManager.setActive(editor); - - var duplicate = findDuplicateMessage(notifications, args); - - if (duplicate === null) { - notif = new Notification(args); - notifications.push(notif); - - //If we have a timeout value - if (args.timeout > 0) { - notif.timer = setTimeout(function() { - notif.close(); - }, args.timeout); - } - - notif.on('close', function() { - var i = notifications.length; - - if (notif.timer) { - editor.getWin().clearTimeout(notif.timer); - } - - while (i--) { - if (notifications[i] === notif) { - notifications.splice(i, 1); - } - } - - positionNotifications(); - }); - - notif.renderTo(); - - positionNotifications(); - } else { - notif = duplicate; - } - - return notif; - }; - - /** - * Closes the top most notification. - * - * @method close - */ - self.close = function() { - if (getLastNotification()) { - getLastNotification().close(); - } - }; - - /** - * Returns the currently opened notification objects. - * - * @method getNotifications - * @return {Array} Array of the currently opened notifications. - */ - self.getNotifications = function() { - return notifications; - }; - - editor.on('SkinLoaded', function() { - var serviceMessage = editor.settings.service_message; - - if (serviceMessage) { - editor.notificationManager.open({ - text: serviceMessage, - type: 'warning', - timeout: 0, - icon: '' - }); - } - }); - - /** - * Finds any existing notification with the same properties as the new one. - * Returns either the found notification or null. - * - * @param {Notification[]} notificationArray - Array of current notifications - * @param {type: string, } newNotification - New notification object - * @returns {?Notification} - */ - function findDuplicateMessage(notificationArray, newNotification) { - if (!isPlainTextNotification(newNotification)) { - return null; - } - - var filteredNotifications = Tools.grep(notificationArray, function(notification) { - return isSameNotification(newNotification, notification); - }); - - return filteredNotifications.length === 0 ? null : filteredNotifications[0]; - } - - /** - * Checks if the passed in args object has the same - * type and text properties as the sent in notification. - * - * @param {type: string, text: string} a - New notification args object - * @param {Notification} b - Old notification - * @returns {boolean} - */ - function isSameNotification(a, b) { - return a.type === b.settings.type && a.text === b.settings.text; - } - - /** - * Checks that the notification does not have a progressBar - * or timeour property. - * - * @param {Notification} notification - Notification to check - * @returns {boolean} - */ - function isPlainTextNotification(notification) { - return !notification.progressBar && !notification.timeout; - } - - //self.positionNotifications = positionNotifications; - }; - } - ); - - /** - * EditorObservable.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This mixin contains the event logic for the tinymce.Editor class. - * - * @mixin tinymce.EditorObservable - * @extends tinymce.util.Observable - */ - define( - 'tinymce.core.EditorObservable', [ - "tinymce.core.util.Observable", - "tinymce.core.dom.DOMUtils", - "tinymce.core.util.Tools" - ], - function(Observable, DOMUtils, Tools) { - var DOM = DOMUtils.DOM, - customEventRootDelegates; - - /** - * Returns the event target so for the specified event. Some events fire - * only on document, some fire on documentElement etc. This also handles the - * custom event root setting where it returns that element instead of the body. - * - * @private - * @param {tinymce.Editor} editor Editor instance to get event target from. - * @param {String} eventName Name of the event for example "click". - * @return {Element/Document} HTML Element or document target to bind on. - */ - function getEventTarget(editor, eventName) { - if (eventName == 'selectionchange') { - return editor.getDoc(); - } - - // Need to bind mousedown/mouseup etc to document not body in iframe mode - // Since the user might click on the HTML element not the BODY - if (!editor.inline && /^mouse|touch|click|contextmenu|drop|dragover|dragend/.test(eventName)) { - return editor.getDoc().documentElement; - } - - // Bind to event root instead of body if it's defined - if (editor.settings.event_root) { - if (!editor.eventRoot) { - editor.eventRoot = DOM.select(editor.settings.event_root)[0]; - } - - return editor.eventRoot; - } - - return editor.getBody(); - } - - /** - * Binds a event delegate for the specified name this delegate will fire - * the event to the editor dispatcher. - * - * @private - * @param {tinymce.Editor} editor Editor instance to get event target from. - * @param {String} eventName Name of the event for example "click". - */ - function bindEventDelegate(editor, eventName) { - var eventRootElm, delegate; - - function isListening(editor) { - return !editor.hidden && !editor.readonly; - } - - if (!editor.delegates) { - editor.delegates = {}; - } - - if (editor.delegates[eventName] || editor.removed) { - return; - } - - eventRootElm = getEventTarget(editor, eventName); - - if (editor.settings.event_root) { - if (!customEventRootDelegates) { - customEventRootDelegates = {}; - editor.editorManager.on('removeEditor', function() { - var name; - - if (!editor.editorManager.activeEditor) { - if (customEventRootDelegates) { - for (name in customEventRootDelegates) { - editor.dom.unbind(getEventTarget(editor, name)); - } - - customEventRootDelegates = null; - } - } - }); - } - - if (customEventRootDelegates[eventName]) { - return; - } - - delegate = function(e) { - var target = e.target, - editors = editor.editorManager.editors, - i = editors.length; - - while (i--) { - var body = editors[i].getBody(); - - if (body === target || DOM.isChildOf(target, body)) { - if (isListening(editors[i])) { - editors[i].fire(eventName, e); - } - } - } - }; - - customEventRootDelegates[eventName] = delegate; - DOM.bind(eventRootElm, eventName, delegate); - } else { - delegate = function(e) { - if (isListening(editor)) { - editor.fire(eventName, e); - } - }; - - DOM.bind(eventRootElm, eventName, delegate); - editor.delegates[eventName] = delegate; - } - } - - var EditorObservable = { - /** - * Bind any pending event delegates. This gets executed after the target body/document is created. - * - * @private - */ - bindPendingEventDelegates: function() { - var self = this; - - Tools.each(self._pendingNativeEvents, function(name) { - bindEventDelegate(self, name); - }); - }, - - /** - * Toggles a native event on/off this is called by the EventDispatcher when - * the first native event handler is added and when the last native event handler is removed. - * - * @private - */ - toggleNativeEvent: function(name, state) { - var self = this; - - // Never bind focus/blur since the FocusManager fakes those - if (name == "focus" || name == "blur") { - return; - } - - if (state) { - if (self.initialized) { - bindEventDelegate(self, name); - } else { - if (!self._pendingNativeEvents) { - self._pendingNativeEvents = [name]; - } else { - self._pendingNativeEvents.push(name); - } - } - } else if (self.initialized) { - self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]); - delete self.delegates[name]; - } - }, - - /** - * Unbinds all native event handlers that means delegates, custom events bound using the Events API etc. - * - * @private - */ - unbindAllNativeEvents: function() { - var self = this, - name; - - if (self.delegates) { - for (name in self.delegates) { - self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]); - } - - delete self.delegates; - } - - if (!self.inline) { - self.getBody().onload = null; - self.dom.unbind(self.getWin()); - self.dom.unbind(self.getDoc()); - } - - self.dom.unbind(self.getBody()); - self.dom.unbind(self.getContainer()); - } - }; - - EditorObservable = Tools.extend({}, Observable, EditorObservable); - - return EditorObservable; - } - ); - - /** - * Shortcuts.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Contains logic for handling keyboard shortcuts. - * - * @class tinymce.Shortcuts - * @example - * editor.shortcuts.add('ctrl+a', "description of the shortcut", function() {}); - * editor.shortcuts.add('meta+a', "description of the shortcut", function() {}); // "meta" maps to Command on Mac and Ctrl on PC - * editor.shortcuts.add('ctrl+alt+a', "description of the shortcut", function() {}); - * editor.shortcuts.add('access+a', "description of the shortcut", function() {}); // "access" maps to ctrl+alt on Mac and shift+alt on PC - */ - define( - 'tinymce.core.Shortcuts', [ - 'tinymce.core.util.Tools', - 'tinymce.core.Env' - ], - function(Tools, Env) { - var each = Tools.each, - explode = Tools.explode; - - var keyCodeLookup = { - "f9": 120, - "f10": 121, - "f11": 122 - }; - - var modifierNames = Tools.makeMap('alt,ctrl,shift,meta,access'); - - return function(editor) { - var self = this, - shortcuts = {}, - pendingPatterns = []; - - function parseShortcut(pattern) { - var id, key, shortcut = {}; - - // Parse modifiers and keys ctrl+alt+b for example - each(explode(pattern, '+'), function(value) { - if (value in modifierNames) { - shortcut[value] = true; - } else { - // Allow numeric keycodes like ctrl+219 for ctrl+[ - if (/^[0-9]{2,}$/.test(value)) { - shortcut.keyCode = parseInt(value, 10); - } else { - shortcut.charCode = value.charCodeAt(0); - shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0); - } - } - }); - - // Generate unique id for modifier combination and set default state for unused modifiers - id = [shortcut.keyCode]; - for (key in modifierNames) { - if (shortcut[key]) { - id.push(key); - } else { - shortcut[key] = false; - } - } - shortcut.id = id.join(','); - - // Handle special access modifier differently depending on Mac/Win - if (shortcut.access) { - shortcut.alt = true; - - if (Env.mac) { - shortcut.ctrl = true; - } else { - shortcut.shift = true; - } - } - - // Handle special meta modifier differently depending on Mac/Win - if (shortcut.meta) { - if (Env.mac) { - shortcut.meta = true; - } else { - shortcut.ctrl = true; - shortcut.meta = false; - } - } - - return shortcut; - } - - function createShortcut(pattern, desc, cmdFunc, scope) { - var shortcuts; - - shortcuts = Tools.map(explode(pattern, '>'), parseShortcut); - shortcuts[shortcuts.length - 1] = Tools.extend(shortcuts[shortcuts.length - 1], { - func: cmdFunc, - scope: scope || editor - }); - - return Tools.extend(shortcuts[0], { - desc: editor.translate(desc), - subpatterns: shortcuts.slice(1) - }); - } - - function hasModifier(e) { - return e.altKey || e.ctrlKey || e.metaKey; - } - - function isFunctionKey(e) { - return e.type === "keydown" && e.keyCode >= 112 && e.keyCode <= 123; - } - - function matchShortcut(e, shortcut) { - if (!shortcut) { - return false; - } - - if (shortcut.ctrl != e.ctrlKey || shortcut.meta != e.metaKey) { - return false; - } - - if (shortcut.alt != e.altKey || shortcut.shift != e.shiftKey) { - return false; - } - - if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) { - e.preventDefault(); - return true; - } - - return false; - } - - function executeShortcutAction(shortcut) { - return shortcut.func ? shortcut.func.call(shortcut.scope) : null; - } - - editor.on('keyup keypress keydown', function(e) { - if ((hasModifier(e) || isFunctionKey(e)) && !e.isDefaultPrevented()) { - each(shortcuts, function(shortcut) { - if (matchShortcut(e, shortcut)) { - pendingPatterns = shortcut.subpatterns.slice(0); - - if (e.type == "keydown") { - executeShortcutAction(shortcut); - } - - return true; - } - }); - - if (matchShortcut(e, pendingPatterns[0])) { - if (pendingPatterns.length === 1) { - if (e.type == "keydown") { - executeShortcutAction(pendingPatterns[0]); - } - } - - pendingPatterns.shift(); - } - } - }); - - /** - * Adds a keyboard shortcut for some command or function. - * - * @method add - * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o. - * @param {String} desc Text description for the command. - * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed. - * @param {Object} scope Optional scope to execute the function in. - * @return {Boolean} true/false state if the shortcut was added or not. - */ - self.add = function(pattern, desc, cmdFunc, scope) { - var cmd; - - cmd = cmdFunc; - - if (typeof cmdFunc === 'string') { - cmdFunc = function() { - editor.execCommand(cmd, false, null); - }; - } else if (Tools.isArray(cmd)) { - cmdFunc = function() { - editor.execCommand(cmd[0], cmd[1], cmd[2]); - }; - } - - each(explode(Tools.trim(pattern.toLowerCase())), function(pattern) { - var shortcut = createShortcut(pattern, desc, cmdFunc, scope); - shortcuts[shortcut.id] = shortcut; - }); - - return true; - }; - - /** - * Remove a keyboard shortcut by pattern. - * - * @method remove - * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o. - * @return {Boolean} true/false state if the shortcut was removed or not. - */ - self.remove = function(pattern) { - var shortcut = createShortcut(pattern); - - if (shortcuts[shortcut.id]) { - delete shortcuts[shortcut.id]; - return true; - } - - return false; - }; - }; - } - ); - - /** - * DefaultSettings.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.EditorSettings', [ - 'tinymce.core.util.Tools' - ], - function(Tools) { - var getEditorSettings = function(editor, id, documentBaseUrl, defaultOverrideSettings, settings) { - settings = Tools.extend( - // Default settings - { - id: id, - theme: 'modern', - delta_width: 0, - delta_height: 0, - popup_css: '', - plugins: '', - document_base_url: documentBaseUrl, - add_form_submit_trigger: true, - submit_patch: true, - add_unload_trigger: true, - convert_urls: true, - relative_urls: true, - remove_script_host: true, - object_resizing: true, - doctype: '<!DOCTYPE html>', - visual: true, - font_size_style_values: 'xx-small,x-small,small,medium,large,x-large,xx-large', - - // See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size - font_size_legacy_values: 'xx-small,small,medium,large,x-large,xx-large,300%', - forced_root_block: 'p', - hidden_input: true, - padd_empty_editor: true, - render_ui: true, - indentation: '30px', - inline_styles: true, - convert_fonts_to_spans: true, - indent: 'simple', - indent_before: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' + - 'tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist', - indent_after: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' + - 'tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist', - entity_encoding: 'named', - url_converter: editor.convertURL, - url_converter_scope: editor, - ie7_compat: true - }, - - // tinymce.overrideDefaults settings - defaultOverrideSettings, - - // User settings - settings, - - // Forced settings - { - validate: true, - content_editable: settings.inline - } - ); - - // Merge external_plugins - if (defaultOverrideSettings && defaultOverrideSettings.external_plugins && settings.external_plugins) { - settings.external_plugins = Tools.extend({}, defaultOverrideSettings.external_plugins, settings.external_plugins); - } - - return settings; - }; - - return { - getEditorSettings: getEditorSettings - }; - } - ); - - defineGlobal("global!window", window); - /** - * ErrorReporter.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Various error reporting helper functions. - * - * @class tinymce.ErrorReporter - * @private - */ - define( - 'tinymce.core.ErrorReporter', [ - "tinymce.core.AddOnManager" - ], - function(AddOnManager) { - var PluginManager = AddOnManager.PluginManager; - - var resolvePluginName = function(targetUrl, suffix) { - for (var name in PluginManager.urls) { - var matchUrl = PluginManager.urls[name] + '/plugin' + suffix + '.js'; - if (matchUrl === targetUrl) { - return name; - } - } - - return null; - }; - - var pluginUrlToMessage = function(editor, url) { - var plugin = resolvePluginName(url, editor.suffix); - return plugin ? - 'Failed to load plugin: ' + plugin + ' from url ' + url : - 'Failed to load plugin url: ' + url; - }; - - var displayNotification = function(editor, message) { - editor.notificationManager.open({ - type: 'error', - text: message - }); - }; - - var displayError = function(editor, message) { - if (editor._skinLoaded) { - displayNotification(editor, message); - } else { - editor.on('SkinLoaded', function() { - displayNotification(editor, message); - }); - } - }; - - var uploadError = function(editor, message) { - displayError(editor, 'Failed to upload image: ' + message); - }; - - var pluginLoadError = function(editor, url) { - displayError(editor, pluginUrlToMessage(editor, url)); - }; - - var contentCssError = function(editor, urls) { - displayError(editor, 'Failed to load content css: ' + urls[0]); - }; - - var initError = function(message) { - var console = window.console; - if (console && !window.test) { // Skip test env - if (console.error) { - console.error.apply(console, arguments); - } else { - console.log.apply(console, arguments); - } - } - }; - - return { - pluginLoadError: pluginLoadError, - uploadError: uploadError, - displayError: displayError, - contentCssError: contentCssError, - initError: initError - }; - } - ); - /** - * CaretContainerInput.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module shows the invisble block that the caret is currently in when contents is added to that block. - */ - define( - 'tinymce.core.caret.CaretContainerInput', [ - 'ephox.katamari.api.Fun', - 'tinymce.core.caret.CaretContainer' - ], - function(Fun, CaretContainer) { - var findBlockCaretContainer = function(editor) { - return editor.dom.select('*[data-mce-caret]')[0]; - }; - - var removeIeControlRect = function(editor) { - editor.selection.setRng(editor.selection.getRng()); - }; - - var showBlockCaretContainer = function(editor, blockCaretContainer) { - if (blockCaretContainer.hasAttribute('data-mce-caret')) { - CaretContainer.showCaretContainerBlock(blockCaretContainer); - removeIeControlRect(editor); - editor.selection.scrollIntoView(blockCaretContainer); - } - }; - - var handleBlockContainer = function(editor, e) { - var blockCaretContainer = findBlockCaretContainer(editor); - - if (!blockCaretContainer) { - return; - } - - if (e.type === 'compositionstart') { - e.preventDefault(); - e.stopPropagation(); - showBlockCaretContainer(blockCaretContainer); - return; - } - - if (CaretContainer.hasContent(blockCaretContainer)) { - showBlockCaretContainer(editor, blockCaretContainer); - } - }; - - var setup = function(editor) { - editor.on('keyup compositionstart', Fun.curry(handleBlockContainer, editor)); - }; - - return { - setup: setup - }; - } - ); - /** - * Uploader.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Upload blobs or blob infos to the specified URL or handler. - * - * @private - * @class tinymce.file.Uploader - * @example - * var uploader = new Uploader({ - * url: '/upload.php', - * basePath: '/base/path', - * credentials: true, - * handler: function(data, success, failure) { - * ... - * } - * }); - * - * uploader.upload(blobInfos).then(function(result) { - * ... - * }); - */ - define( - 'tinymce.core.file.Uploader', [ - "tinymce.core.util.Promise", - "tinymce.core.util.Tools", - "tinymce.core.util.Fun" - ], - function(Promise, Tools, Fun) { - return function(uploadStatus, settings) { - var pendingPromises = {}; - - function pathJoin(path1, path2) { - if (path1) { - return path1.replace(/\/$/, '') + '/' + path2.replace(/^\//, ''); - } - - return path2; - } - - function defaultHandler(blobInfo, success, failure, progress) { - var xhr, formData; - - xhr = new XMLHttpRequest(); - xhr.open('POST', settings.url); - xhr.withCredentials = settings.credentials; - - xhr.upload.onprogress = function(e) { - progress(e.loaded / e.total * 100); - }; - - xhr.onerror = function() { - failure("Image upload failed due to a XHR Transport error. Code: " + xhr.status); - }; - - xhr.onload = function() { - var json; - - if (xhr.status < 200 || xhr.status >= 300) { - failure("HTTP Error: " + xhr.status); - return; - } - - json = JSON.parse(xhr.responseText); - - if (!json || typeof json.location != "string") { - failure("Invalid JSON: " + xhr.responseText); - return; - } - - success(pathJoin(settings.basePath, json.location)); - }; - - formData = new FormData(); - formData.append('file', blobInfo.blob(), blobInfo.filename()); - - xhr.send(formData); - } - - function noUpload() { - return new Promise(function(resolve) { - resolve([]); - }); - } - - function handlerSuccess(blobInfo, url) { - return { - url: url, - blobInfo: blobInfo, - status: true - }; - } - - function handlerFailure(blobInfo, error) { - return { - url: '', - blobInfo: blobInfo, - status: false, - error: error - }; - } - - function resolvePending(blobUri, result) { - Tools.each(pendingPromises[blobUri], function(resolve) { - resolve(result); - }); - - delete pendingPromises[blobUri]; - } - - function uploadBlobInfo(blobInfo, handler, openNotification) { - uploadStatus.markPending(blobInfo.blobUri()); - - return new Promise(function(resolve) { - var notification, progress; - - var noop = function() {}; - - try { - var closeNotification = function() { - if (notification) { - notification.close(); - progress = noop; // Once it's closed it's closed - } - }; - - var success = function(url) { - closeNotification(); - uploadStatus.markUploaded(blobInfo.blobUri(), url); - resolvePending(blobInfo.blobUri(), handlerSuccess(blobInfo, url)); - resolve(handlerSuccess(blobInfo, url)); - }; - - var failure = function(error) { - closeNotification(); - uploadStatus.removeFailed(blobInfo.blobUri()); - resolvePending(blobInfo.blobUri(), handlerFailure(blobInfo, error)); - resolve(handlerFailure(blobInfo, error)); - }; - - progress = function(percent) { - if (percent < 0 || percent > 100) { - return; - } - - if (!notification) { - notification = openNotification(); - } - - notification.progressBar.value(percent); - }; - - handler(blobInfo, success, failure, progress); - } catch (ex) { - resolve(handlerFailure(blobInfo, ex.message)); - } - }); - } - - function isDefaultHandler(handler) { - return handler === defaultHandler; - } - - function pendingUploadBlobInfo(blobInfo) { - var blobUri = blobInfo.blobUri(); - - return new Promise(function(resolve) { - pendingPromises[blobUri] = pendingPromises[blobUri] || []; - pendingPromises[blobUri].push(resolve); - }); - } - - function uploadBlobs(blobInfos, openNotification) { - blobInfos = Tools.grep(blobInfos, function(blobInfo) { - return !uploadStatus.isUploaded(blobInfo.blobUri()); - }); - - return Promise.all(Tools.map(blobInfos, function(blobInfo) { - return uploadStatus.isPending(blobInfo.blobUri()) ? - pendingUploadBlobInfo(blobInfo) : uploadBlobInfo(blobInfo, settings.handler, openNotification); - })); - } - - function upload(blobInfos, openNotification) { - return (!settings.url && isDefaultHandler(settings.handler)) ? noUpload() : uploadBlobs(blobInfos, openNotification); - } - - settings = Tools.extend({ - credentials: false, - // We are adding a notify argument to this (at the moment, until it doesn't work) - handler: defaultHandler - }, settings); - - return { - upload: upload - }; - }; - } - ); - /** - * Conversions.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Converts blob/uris back and forth. - * - * @private - * @class tinymce.file.Conversions - */ - define( - 'tinymce.core.file.Conversions', [ - "tinymce.core.util.Promise" - ], - function(Promise) { - function blobUriToBlob(url) { - return new Promise(function(resolve, reject) { - - var rejectWithError = function() { - reject("Cannot convert " + url + " to Blob. Resource might not exist or is inaccessible."); - }; - - try { - var xhr = new XMLHttpRequest(); - - xhr.open('GET', url, true); - xhr.responseType = 'blob'; - - xhr.onload = function() { - if (this.status == 200) { - resolve(this.response); - } else { - // IE11 makes it into onload but responds with status 500 - rejectWithError(); - } - }; - - // Chrome fires an error event instead of the exception - // Also there seems to be no way to intercept the message that is logged to the console - xhr.onerror = rejectWithError; - - xhr.send(); - } catch (ex) { - rejectWithError(); - } - }); - } - - function parseDataUri(uri) { - var type, matches; - - uri = decodeURIComponent(uri).split(','); - - matches = /data:([^;]+)/.exec(uri[0]); - if (matches) { - type = matches[1]; - } - - return { - type: type, - data: uri[1] - }; - } - - function dataUriToBlob(uri) { - return new Promise(function(resolve) { - var str, arr, i; - - uri = parseDataUri(uri); - - // Might throw error if data isn't proper base64 - try { - str = atob(uri.data); - } catch (e) { - resolve(new Blob([])); - return; - } - - arr = new Uint8Array(str.length); - - for (i = 0; i < arr.length; i++) { - arr[i] = str.charCodeAt(i); - } - - resolve(new Blob([arr], { - type: uri.type - })); - }); - } - - function uriToBlob(url) { - if (url.indexOf('blob:') === 0) { - return blobUriToBlob(url); - } - - if (url.indexOf('data:') === 0) { - return dataUriToBlob(url); - } - - return null; - } - - function blobToDataUri(blob) { - return new Promise(function(resolve) { - var reader = new FileReader(); - - reader.onloadend = function() { - resolve(reader.result); - }; - - reader.readAsDataURL(blob); - }); - } - - return { - uriToBlob: uriToBlob, - blobToDataUri: blobToDataUri, - parseDataUri: parseDataUri - }; - } - ); - /** - * ImageScanner.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Finds images with data uris or blob uris. If data uris are found it will convert them into blob uris. - * - * @private - * @class tinymce.file.ImageScanner - */ - define( - 'tinymce.core.file.ImageScanner', [ - "tinymce.core.util.Promise", - "tinymce.core.util.Arr", - "tinymce.core.util.Fun", - "tinymce.core.file.Conversions", - "tinymce.core.Env" - ], - function(Promise, Arr, Fun, Conversions, Env) { - var count = 0; - - var uniqueId = function(prefix) { - return (prefix || 'blobid') + (count++); - }; - - var imageToBlobInfo = function(blobCache, img, resolve, reject) { - var base64, blobInfo; - - if (img.src.indexOf('blob:') === 0) { - blobInfo = blobCache.getByUri(img.src); - - if (blobInfo) { - resolve({ - image: img, - blobInfo: blobInfo - }); - } else { - Conversions.uriToBlob(img.src).then(function(blob) { - Conversions.blobToDataUri(blob).then(function(dataUri) { - base64 = Conversions.parseDataUri(dataUri).data; - blobInfo = blobCache.create(uniqueId(), blob, base64); - blobCache.add(blobInfo); - - resolve({ - image: img, - blobInfo: blobInfo - }); - }); - }, function(err) { - reject(err); - }); - } - - return; - } - - base64 = Conversions.parseDataUri(img.src).data; - blobInfo = blobCache.findFirst(function(cachedBlobInfo) { - return cachedBlobInfo.base64() === base64; - }); - - if (blobInfo) { - resolve({ - image: img, - blobInfo: blobInfo - }); - } else { - Conversions.uriToBlob(img.src).then(function(blob) { - blobInfo = blobCache.create(uniqueId(), blob, base64); - blobCache.add(blobInfo); - - resolve({ - image: img, - blobInfo: blobInfo - }); - }, function(err) { - reject(err); - }); - } - }; - - var getAllImages = function(elm) { - return elm ? elm.getElementsByTagName('img') : []; - }; - - return function(uploadStatus, blobCache) { - var cachedPromises = {}; - - function findAll(elm, predicate) { - var images, promises; - - if (!predicate) { - predicate = Fun.constant(true); - } - - images = Arr.filter(getAllImages(elm), function(img) { - var src = img.src; - - if (!Env.fileApi) { - return false; - } - - if (img.hasAttribute('data-mce-bogus')) { - return false; - } - - if (img.hasAttribute('data-mce-placeholder')) { - return false; - } - - if (!src || src == Env.transparentSrc) { - return false; - } - - if (src.indexOf('blob:') === 0) { - return !uploadStatus.isUploaded(src); - } - - if (src.indexOf('data:') === 0) { - return predicate(img); - } - - return false; - }); - - promises = Arr.map(images, function(img) { - var newPromise; - - if (cachedPromises[img.src]) { - // Since the cached promise will return the cached image - // We need to wrap it and resolve with the actual image - return new Promise(function(resolve) { - cachedPromises[img.src].then(function(imageInfo) { - if (typeof imageInfo === 'string') { // error apparently - return imageInfo; - } - resolve({ - image: img, - blobInfo: imageInfo.blobInfo - }); - }); - }); - } - - newPromise = new Promise(function(resolve, reject) { - imageToBlobInfo(blobCache, img, resolve, reject); - }).then(function(result) { - delete cachedPromises[result.image.src]; - return result; - })['catch'](function(error) { - delete cachedPromises[img.src]; - return error; - }); - - cachedPromises[img.src] = newPromise; - - return newPromise; - }); - - return Promise.all(promises); - } - - return { - findAll: findAll - }; - }; - } - ); - /** - * Uuid.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Generates unique ids. - * - * @class tinymce.util.Uuid - * @private - */ - define( - 'tinymce.core.util.Uuid', [], - function() { - var count = 0; - - var seed = function() { - var rnd = function() { - return Math.round(Math.random() * 0xFFFFFFFF).toString(36); - }; - - var now = new Date().getTime(); - return 's' + now.toString(36) + rnd() + rnd() + rnd(); - }; - - var uuid = function(prefix) { - return prefix + (count++) + seed(); - }; - - return { - uuid: uuid - }; - } - ); - - defineGlobal("global!URL", URL); - /** - * BlobCache.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Hold blob info objects where a blob has extra internal information. - * - * @private - * @class tinymce.file.BlobCache - */ - define( - 'tinymce.core.file.BlobCache', [ - 'tinymce.core.util.Arr', - 'tinymce.core.util.Fun', - 'tinymce.core.util.Uuid', - 'global!URL' - ], - function(Arr, Fun, Uuid, URL) { - return function() { - var cache = [], - constant = Fun.constant; - - function mimeToExt(mime) { - var mimes = { - 'image/jpeg': 'jpg', - 'image/jpg': 'jpg', - 'image/gif': 'gif', - 'image/png': 'png' - }; - - return mimes[mime.toLowerCase()] || 'dat'; - } - - function create(o, blob, base64, filename) { - return typeof o === 'object' ? toBlobInfo(o) : toBlobInfo({ - id: o, - name: filename, - blob: blob, - base64: base64 - }); - } - - function toBlobInfo(o) { - var id, name; - - if (!o.blob || !o.base64) { - throw "blob and base64 representations of the image are required for BlobInfo to be created"; - } - - id = o.id || Uuid.uuid('blobid'); - name = o.name || id; - - return { - id: constant(id), - name: constant(name), - filename: constant(name + '.' + mimeToExt(o.blob.type)), - blob: constant(o.blob), - base64: constant(o.base64), - blobUri: constant(o.blobUri || URL.createObjectURL(o.blob)), - uri: constant(o.uri) - }; - } - - function add(blobInfo) { - if (!get(blobInfo.id())) { - cache.push(blobInfo); - } - } - - function get(id) { - return findFirst(function(cachedBlobInfo) { - return cachedBlobInfo.id() === id; - }); - } - - function findFirst(predicate) { - return Arr.filter(cache, predicate)[0]; - } - - function getByUri(blobUri) { - return findFirst(function(blobInfo) { - return blobInfo.blobUri() == blobUri; - }); - } - - function removeByUri(blobUri) { - cache = Arr.filter(cache, function(blobInfo) { - if (blobInfo.blobUri() === blobUri) { - URL.revokeObjectURL(blobInfo.blobUri()); - return false; - } - - return true; - }); - } - - function destroy() { - Arr.each(cache, function(cachedBlobInfo) { - URL.revokeObjectURL(cachedBlobInfo.blobUri()); - }); - - cache = []; - } - - return { - create: create, - add: add, - get: get, - getByUri: getByUri, - findFirst: findFirst, - removeByUri: removeByUri, - destroy: destroy - }; - }; - } - ); - /** - * UploadStatus.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Holds the current status of a blob uri, if it's pending or uploaded and what the result urls was. - * - * @private - * @class tinymce.file.UploadStatus - */ - define( - 'tinymce.core.file.UploadStatus', [], - function() { - return function() { - var PENDING = 1, - UPLOADED = 2; - var blobUriStatuses = {}; - - function createStatus(status, resultUri) { - return { - status: status, - resultUri: resultUri - }; - } - - function hasBlobUri(blobUri) { - return blobUri in blobUriStatuses; - } - - function getResultUri(blobUri) { - var result = blobUriStatuses[blobUri]; - - return result ? result.resultUri : null; - } - - function isPending(blobUri) { - return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === PENDING : false; - } - - function isUploaded(blobUri) { - return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === UPLOADED : false; - } - - function markPending(blobUri) { - blobUriStatuses[blobUri] = createStatus(PENDING, null); - } - - function markUploaded(blobUri, resultUri) { - blobUriStatuses[blobUri] = createStatus(UPLOADED, resultUri); - } - - function removeFailed(blobUri) { - delete blobUriStatuses[blobUri]; - } - - function destroy() { - blobUriStatuses = {}; - } - - return { - hasBlobUri: hasBlobUri, - getResultUri: getResultUri, - isPending: isPending, - isUploaded: isUploaded, - markPending: markPending, - markUploaded: markUploaded, - removeFailed: removeFailed, - destroy: destroy - }; - }; - } - ); - /** - * EditorUpload.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Handles image uploads, updates undo stack and patches over various internal functions. - * - * @private - * @class tinymce.EditorUpload - */ - define( - 'tinymce.core.EditorUpload', [ - "tinymce.core.util.Arr", - "tinymce.core.file.Uploader", - "tinymce.core.file.ImageScanner", - "tinymce.core.file.BlobCache", - "tinymce.core.file.UploadStatus", - "tinymce.core.ErrorReporter" - ], - function(Arr, Uploader, ImageScanner, BlobCache, UploadStatus, ErrorReporter) { - return function(editor) { - var blobCache = new BlobCache(), - uploader, imageScanner, settings = editor.settings; - var uploadStatus = new UploadStatus(); - - function aliveGuard(callback) { - return function(result) { - if (editor.selection) { - return callback(result); - } - - return []; - }; - } - - function cacheInvalidator() { - return '?' + (new Date()).getTime(); - } - - // Replaces strings without regexps to avoid FF regexp to big issue - function replaceString(content, search, replace) { - var index = 0; - - do { - index = content.indexOf(search, index); - - if (index !== -1) { - content = content.substring(0, index) + replace + content.substr(index + search.length); - index += replace.length - search.length + 1; - } - } while (index !== -1); - - return content; - } - - function replaceImageUrl(content, targetUrl, replacementUrl) { - content = replaceString(content, 'src="' + targetUrl + '"', 'src="' + replacementUrl + '"'); - content = replaceString(content, 'data-mce-src="' + targetUrl + '"', 'data-mce-src="' + replacementUrl + '"'); - - return content; - } - - function replaceUrlInUndoStack(targetUrl, replacementUrl) { - Arr.each(editor.undoManager.data, function(level) { - if (level.type === 'fragmented') { - level.fragments = Arr.map(level.fragments, function(fragment) { - return replaceImageUrl(fragment, targetUrl, replacementUrl); - }); - } else { - level.content = replaceImageUrl(level.content, targetUrl, replacementUrl); - } - }); - } - - function openNotification() { - return editor.notificationManager.open({ - text: editor.translate('Image uploading...'), - type: 'info', - timeout: -1, - progressBar: true - }); - } - - function replaceImageUri(image, resultUri) { - blobCache.removeByUri(image.src); - replaceUrlInUndoStack(image.src, resultUri); - - editor.$(image).attr({ - src: settings.images_reuse_filename ? resultUri + cacheInvalidator() : resultUri, - 'data-mce-src': editor.convertURL(resultUri, 'src') - }); - } - - function uploadImages(callback) { - if (!uploader) { - uploader = new Uploader(uploadStatus, { - url: settings.images_upload_url, - basePath: settings.images_upload_base_path, - credentials: settings.images_upload_credentials, - handler: settings.images_upload_handler - }); - } - - return scanForImages().then(aliveGuard(function(imageInfos) { - var blobInfos; - - blobInfos = Arr.map(imageInfos, function(imageInfo) { - return imageInfo.blobInfo; - }); - - return uploader.upload(blobInfos, openNotification).then(aliveGuard(function(result) { - var filteredResult = Arr.map(result, function(uploadInfo, index) { - var image = imageInfos[index].image; - - if (uploadInfo.status && editor.settings.images_replace_blob_uris !== false) { - replaceImageUri(image, uploadInfo.url); - } else if (uploadInfo.error) { - ErrorReporter.uploadError(editor, uploadInfo.error); - } - - return { - element: image, - status: uploadInfo.status - }; - }); - - if (callback) { - callback(filteredResult); - } - - return filteredResult; - })); - })); - } - - function uploadImagesAuto(callback) { - if (settings.automatic_uploads !== false) { - return uploadImages(callback); - } - } - - function isValidDataUriImage(imgElm) { - return settings.images_dataimg_filter ? settings.images_dataimg_filter(imgElm) : true; - } - - function scanForImages() { - if (!imageScanner) { - imageScanner = new ImageScanner(uploadStatus, blobCache); - } - - return imageScanner.findAll(editor.getBody(), isValidDataUriImage).then(aliveGuard(function(result) { - result = Arr.filter(result, function(resultItem) { - // ImageScanner internally converts images that it finds, but it may fail to do so if image source is inaccessible. - // In such case resultItem will contain appropriate text error message, instead of image data. - if (typeof resultItem === 'string') { - ErrorReporter.displayError(editor, resultItem); - return false; - } - return true; - }); - - Arr.each(result, function(resultItem) { - replaceUrlInUndoStack(resultItem.image.src, resultItem.blobInfo.blobUri()); - resultItem.image.src = resultItem.blobInfo.blobUri(); - resultItem.image.removeAttribute('data-mce-src'); - }); - - return result; - })); - } - - function destroy() { - blobCache.destroy(); - uploadStatus.destroy(); - imageScanner = uploader = null; - } - - function replaceBlobUris(content) { - return content.replace(/src="(blob:[^"]+)"/g, function(match, blobUri) { - var resultUri = uploadStatus.getResultUri(blobUri); - - if (resultUri) { - return 'src="' + resultUri + '"'; - } - - var blobInfo = blobCache.getByUri(blobUri); - - if (!blobInfo) { - blobInfo = Arr.reduce(editor.editorManager.editors, function(result, editor) { - return result || editor.editorUpload && editor.editorUpload.blobCache.getByUri(blobUri); - }, null); - } - - if (blobInfo) { - return 'src="data:' + blobInfo.blob().type + ';base64,' + blobInfo.base64() + '"'; - } - - return match; - }); - } - - editor.on('setContent', function() { - if (editor.settings.automatic_uploads !== false) { - uploadImagesAuto(); - } else { - scanForImages(); - } - }); - - editor.on('RawSaveContent', function(e) { - e.content = replaceBlobUris(e.content); - }); - - editor.on('getContent', function(e) { - if (e.source_view || e.format == 'raw') { - return; - } - - e.content = replaceBlobUris(e.content); - }); - - editor.on('PostRender', function() { - editor.parser.addNodeFilter('img', function(images) { - Arr.each(images, function(img) { - var src = img.attr('src'); - - if (blobCache.getByUri(src)) { - return; - } - - var resultUri = uploadStatus.getResultUri(src); - if (resultUri) { - img.attr('src', resultUri); - } - }); - }); - }); - - return { - blobCache: blobCache, - uploadImages: uploadImages, - uploadImagesAuto: uploadImagesAuto, - scanForImages: scanForImages, - destroy: destroy - }; - }; - } - ); - /** - * ForceBlocks.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Makes sure that everything gets wrapped in paragraphs. - * - * @private - * @class tinymce.ForceBlocks - */ - define( - 'tinymce.core.ForceBlocks', [ - 'ephox.katamari.api.Fun' - ], - function(Fun) { - var addRootBlocks = function(editor) { - var settings = editor.settings, - dom = editor.dom, - selection = editor.selection; - var schema = editor.schema, - blockElements = schema.getBlockElements(); - var node = selection.getStart(), - rootNode = editor.getBody(), - rng; - var startContainer, startOffset, endContainer, endOffset, rootBlockNode; - var tempNode, offset = -0xFFFFFF, - wrapped, restoreSelection; - var tmpRng, rootNodeName, forcedRootBlock; - - forcedRootBlock = settings.forced_root_block; - - if (!node || node.nodeType !== 1 || !forcedRootBlock) { - return; - } - - // Check if node is wrapped in block - while (node && node !== rootNode) { - if (blockElements[node.nodeName]) { - return; - } - - node = node.parentNode; - } - - // Get current selection - rng = selection.getRng(); - if (rng.setStart) { - startContainer = rng.startContainer; - startOffset = rng.startOffset; - endContainer = rng.endContainer; - endOffset = rng.endOffset; - - try { - restoreSelection = editor.getDoc().activeElement === rootNode; - } catch (ex) { - // IE throws unspecified error here sometimes - } - } else { - // Force control range into text range - if (rng.item) { - node = rng.item(0); - rng = editor.getDoc().body.createTextRange(); - rng.moveToElementText(node); - } - - restoreSelection = rng.parentElement().ownerDocument === editor.getDoc(); - tmpRng = rng.duplicate(); - tmpRng.collapse(true); - startOffset = tmpRng.move('character', offset) * -1; - - if (!tmpRng.collapsed) { - tmpRng = rng.duplicate(); - tmpRng.collapse(false); - endOffset = (tmpRng.move('character', offset) * -1) - startOffset; - } - } - - // Wrap non block elements and text nodes - node = rootNode.firstChild; - rootNodeName = rootNode.nodeName.toLowerCase(); - while (node) { - // TODO: Break this up, too complex - if (((node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName]))) && - schema.isValidChild(rootNodeName, forcedRootBlock.toLowerCase())) { - // Remove empty text nodes - if (node.nodeType === 3 && node.nodeValue.length === 0) { - tempNode = node; - node = node.nextSibling; - dom.remove(tempNode); - continue; - } - - if (!rootBlockNode) { - rootBlockNode = dom.create(forcedRootBlock, editor.settings.forced_root_block_attrs); - node.parentNode.insertBefore(rootBlockNode, node); - wrapped = true; - } - - tempNode = node; - node = node.nextSibling; - rootBlockNode.appendChild(tempNode); - } else { - rootBlockNode = null; - node = node.nextSibling; - } - } - - if (wrapped && restoreSelection) { - if (rng.setStart) { - rng.setStart(startContainer, startOffset); - rng.setEnd(endContainer, endOffset); - selection.setRng(rng); - } else { - // Only select if the previous selection was inside the document to prevent auto focus in quirks mode - try { - rng = editor.getDoc().body.createTextRange(); - rng.moveToElementText(rootNode); - rng.collapse(true); - rng.moveStart('character', startOffset); - - if (endOffset > 0) { - rng.moveEnd('character', endOffset); - } - - rng.select(); - } catch (ex) { - // Ignore - } - } - - editor.nodeChanged(); - } - }; - - var setup = function(editor) { - if (editor.settings.forced_root_block) { - editor.on('NodeChange', Fun.curry(addRootBlocks, editor)); - } - }; - - return { - setup: setup - }; - } - ); - /** - * Dimensions.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module measures nodes and returns client rects. The client rects has an - * extra node property. - * - * @private - * @class tinymce.dom.Dimensions - */ - define( - 'tinymce.core.dom.Dimensions', [ - "tinymce.core.util.Arr", - "tinymce.core.dom.NodeType", - "tinymce.core.geom.ClientRect" - ], - function(Arr, NodeType, ClientRect) { - - function getClientRects(node) { - function toArrayWithNode(clientRects) { - return Arr.map(clientRects, function(clientRect) { - clientRect = ClientRect.clone(clientRect); - clientRect.node = node; - - return clientRect; - }); - } - - if (Arr.isArray(node)) { - return Arr.reduce(node, function(result, node) { - return result.concat(getClientRects(node)); - }, []); - } - - if (NodeType.isElement(node)) { - return toArrayWithNode(node.getClientRects()); - } - - if (NodeType.isText(node)) { - var rng = node.ownerDocument.createRange(); - - rng.setStart(node, 0); - rng.setEnd(node, node.data.length); - - return toArrayWithNode(rng.getClientRects()); - } - } - - return { - /** - * Returns the client rects for a specific node. - * - * @method getClientRects - * @param {Array/DOMNode} node Node or array of nodes to get client rects on. - * @param {Array} Array of client rects with a extra node property. - */ - getClientRects: getClientRects - }; - } - ); - /** - * LineUtils.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Utility functions for working with lines. - * - * @private - * @class tinymce.caret.LineUtils - */ - define( - 'tinymce.core.caret.LineUtils', [ - "tinymce.core.util.Fun", - "tinymce.core.util.Arr", - "tinymce.core.dom.NodeType", - "tinymce.core.dom.Dimensions", - "tinymce.core.geom.ClientRect", - "tinymce.core.caret.CaretUtils", - "tinymce.core.caret.CaretCandidate" - ], - function(Fun, Arr, NodeType, Dimensions, ClientRect, CaretUtils, CaretCandidate) { - var isContentEditableFalse = NodeType.isContentEditableFalse, - findNode = CaretUtils.findNode, - curry = Fun.curry; - - function distanceToRectLeft(clientRect, clientX) { - return Math.abs(clientRect.left - clientX); - } - - function distanceToRectRight(clientRect, clientX) { - return Math.abs(clientRect.right - clientX); - } - - function findClosestClientRect(clientRects, clientX) { - function isInside(clientX, clientRect) { - return clientX >= clientRect.left && clientX <= clientRect.right; - } - - return Arr.reduce(clientRects, function(oldClientRect, clientRect) { - var oldDistance, newDistance; - - oldDistance = Math.min(distanceToRectLeft(oldClientRect, clientX), distanceToRectRight(oldClientRect, clientX)); - newDistance = Math.min(distanceToRectLeft(clientRect, clientX), distanceToRectRight(clientRect, clientX)); - - if (isInside(clientX, clientRect)) { - return clientRect; - } - - if (isInside(clientX, oldClientRect)) { - return oldClientRect; - } - - // cE=false has higher priority - if (newDistance == oldDistance && isContentEditableFalse(clientRect.node)) { - return clientRect; - } - - if (newDistance < oldDistance) { - return clientRect; - } - - return oldClientRect; - }); - } - - function walkUntil(direction, rootNode, predicateFn, node) { - while ((node = findNode(node, direction, CaretCandidate.isEditableCaretCandidate, rootNode))) { - if (predicateFn(node)) { - return; - } - } - } - - function findLineNodeRects(rootNode, targetNodeRect) { - var clientRects = []; - - function collect(checkPosFn, node) { - var lineRects; - - lineRects = Arr.filter(Dimensions.getClientRects(node), function(clientRect) { - return !checkPosFn(clientRect, targetNodeRect); - }); - - clientRects = clientRects.concat(lineRects); - - return lineRects.length === 0; - } - - clientRects.push(targetNodeRect); - walkUntil(-1, rootNode, curry(collect, ClientRect.isAbove), targetNodeRect.node); - walkUntil(1, rootNode, curry(collect, ClientRect.isBelow), targetNodeRect.node); - - return clientRects; - } - - function getContentEditableFalseChildren(rootNode) { - return Arr.filter(Arr.toArray(rootNode.getElementsByTagName('*')), isContentEditableFalse); - } - - function caretInfo(clientRect, clientX) { - return { - node: clientRect.node, - before: distanceToRectLeft(clientRect, clientX) < distanceToRectRight(clientRect, clientX) - }; - } - - function closestCaret(rootNode, clientX, clientY) { - var contentEditableFalseNodeRects, closestNodeRect; - - contentEditableFalseNodeRects = Dimensions.getClientRects(getContentEditableFalseChildren(rootNode)); - contentEditableFalseNodeRects = Arr.filter(contentEditableFalseNodeRects, function(clientRect) { - return clientY >= clientRect.top && clientY <= clientRect.bottom; - }); - - closestNodeRect = findClosestClientRect(contentEditableFalseNodeRects, clientX); - if (closestNodeRect) { - closestNodeRect = findClosestClientRect(findLineNodeRects(rootNode, closestNodeRect), clientX); - if (closestNodeRect && isContentEditableFalse(closestNodeRect.node)) { - return caretInfo(closestNodeRect, clientX); - } - } - - return null; - } - - return { - findClosestClientRect: findClosestClientRect, - findLineNodeRects: findLineNodeRects, - closestCaret: closestCaret - }; - } - ); - /** - * LineWalker.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module lets you walk the document line by line - * returing nodes and client rects for each line. - * - * @private - * @class tinymce.caret.LineWalker - */ - define( - 'tinymce.core.caret.LineWalker', [ - "tinymce.core.util.Fun", - "tinymce.core.util.Arr", - "tinymce.core.dom.Dimensions", - "tinymce.core.caret.CaretCandidate", - "tinymce.core.caret.CaretUtils", - "tinymce.core.caret.CaretWalker", - "tinymce.core.caret.CaretPosition", - "tinymce.core.geom.ClientRect" - ], - function(Fun, Arr, Dimensions, CaretCandidate, CaretUtils, CaretWalker, CaretPosition, ClientRect) { - var curry = Fun.curry; - - function findUntil(direction, rootNode, predicateFn, node) { - while ((node = CaretUtils.findNode(node, direction, CaretCandidate.isEditableCaretCandidate, rootNode))) { - if (predicateFn(node)) { - return; - } - } - } - - function walkUntil(direction, isAboveFn, isBeflowFn, rootNode, predicateFn, caretPosition) { - var line = 0, - node, result = [], - targetClientRect; - - function add(node) { - var i, clientRect, clientRects; - - clientRects = Dimensions.getClientRects(node); - if (direction == -1) { - clientRects = clientRects.reverse(); - } - - for (i = 0; i < clientRects.length; i++) { - clientRect = clientRects[i]; - if (isBeflowFn(clientRect, targetClientRect)) { - continue; - } - - if (result.length > 0 && isAboveFn(clientRect, Arr.last(result))) { - line++; - } - - clientRect.line = line; - - if (predicateFn(clientRect)) { - return true; - } - - result.push(clientRect); - } - } - - targetClientRect = Arr.last(caretPosition.getClientRects()); - if (!targetClientRect) { - return result; - } - - node = caretPosition.getNode(); - add(node); - findUntil(direction, rootNode, add, node); - - return result; - } - - function aboveLineNumber(lineNumber, clientRect) { - return clientRect.line > lineNumber; - } - - function isLine(lineNumber, clientRect) { - return clientRect.line === lineNumber; - } - - var upUntil = curry(walkUntil, -1, ClientRect.isAbove, ClientRect.isBelow); - var downUntil = curry(walkUntil, 1, ClientRect.isBelow, ClientRect.isAbove); - - function positionsUntil(direction, rootNode, predicateFn, node) { - var caretWalker = new CaretWalker(rootNode), - walkFn, isBelowFn, isAboveFn, - caretPosition, result = [], - line = 0, - clientRect, targetClientRect; - - function getClientRect(caretPosition) { - if (direction == 1) { - return Arr.last(caretPosition.getClientRects()); - } - - return Arr.last(caretPosition.getClientRects()); - } - - if (direction == 1) { - walkFn = caretWalker.next; - isBelowFn = ClientRect.isBelow; - isAboveFn = ClientRect.isAbove; - caretPosition = CaretPosition.after(node); - } else { - walkFn = caretWalker.prev; - isBelowFn = ClientRect.isAbove; - isAboveFn = ClientRect.isBelow; - caretPosition = CaretPosition.before(node); - } - - targetClientRect = getClientRect(caretPosition); - - do { - if (!caretPosition.isVisible()) { - continue; - } - - clientRect = getClientRect(caretPosition); - - if (isAboveFn(clientRect, targetClientRect)) { - continue; - } - - if (result.length > 0 && isBelowFn(clientRect, Arr.last(result))) { - line++; - } - - clientRect = ClientRect.clone(clientRect); - clientRect.position = caretPosition; - clientRect.line = line; - - if (predicateFn(clientRect)) { - return result; - } - - result.push(clientRect); - } while ((caretPosition = walkFn(caretPosition))); - - return result; - } - - return { - upUntil: upUntil, - downUntil: downUntil, - - /** - * Find client rects with line and caret position until the predicate returns true. - * - * @method positionsUntil - * @param {Number} direction Direction forward/backward 1/-1. - * @param {DOMNode} rootNode Root node to walk within. - * @param {function} predicateFn Gets the client rect as it's input. - * @param {DOMNode} node Node to start walking from. - * @return {Array} Array of client rects with line and position properties. - */ - positionsUntil: positionsUntil, - - isAboveLine: curry(aboveLineNumber), - isLine: curry(isLine) - }; - } - ); - /** - * CefUtils.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.keyboard.CefUtils', [ - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.caret.CaretUtils', - 'tinymce.core.dom.NodeType', - 'tinymce.core.util.Fun' - ], - function(CaretPosition, CaretUtils, NodeType, Fun) { - var isContentEditableTrue = NodeType.isContentEditableTrue; - var isContentEditableFalse = NodeType.isContentEditableFalse; - - var showCaret = function(direction, editor, node, before) { - // TODO: Figure out a better way to handle this dependency - return editor._selectionOverrides.showCaret(direction, node, before); - }; - - var getNodeRange = function(node) { - var rng = node.ownerDocument.createRange(); - rng.selectNode(node); - return rng; - }; - - var selectNode = function(editor, node) { - var e; - - e = editor.fire('BeforeObjectSelected', { - target: node - }); - if (e.isDefaultPrevented()) { - return null; - } - - return getNodeRange(node); - }; - - var renderCaretAtRange = function(editor, range) { - var caretPosition, ceRoot; - - range = CaretUtils.normalizeRange(1, editor.getBody(), range); - caretPosition = CaretPosition.fromRangeStart(range); - - if (isContentEditableFalse(caretPosition.getNode())) { - return showCaret(1, editor, caretPosition.getNode(), !caretPosition.isAtEnd()); - } - - if (isContentEditableFalse(caretPosition.getNode(true))) { - return showCaret(1, editor, caretPosition.getNode(true), false); - } - - // TODO: Should render caret before/after depending on where you click on the page forces after now - ceRoot = editor.dom.getParent(caretPosition.getNode(), Fun.or(isContentEditableFalse, isContentEditableTrue)); - if (isContentEditableFalse(ceRoot)) { - return showCaret(1, editor, ceRoot, false); - } - - return null; - }; - - var renderRangeCaret = function(editor, range) { - var caretRange; - - if (!range || !range.collapsed) { - return range; - } - - caretRange = renderCaretAtRange(editor, range); - if (caretRange) { - return caretRange; - } - - return range; - }; - - return { - showCaret: showCaret, - selectNode: selectNode, - renderCaretAtRange: renderCaretAtRange, - renderRangeCaret: renderRangeCaret - }; - } - ); - - /** - * CefNavigation.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.keyboard.CefNavigation', [ - 'tinymce.core.caret.CaretContainer', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.caret.CaretUtils', - 'tinymce.core.caret.CaretWalker', - 'tinymce.core.caret.LineUtils', - 'tinymce.core.caret.LineWalker', - 'tinymce.core.dom.NodeType', - 'tinymce.core.dom.RangeUtils', - 'tinymce.core.Env', - 'tinymce.core.keyboard.CefUtils', - 'tinymce.core.util.Arr', - 'tinymce.core.util.Fun' - ], - function(CaretContainer, CaretPosition, CaretUtils, CaretWalker, LineUtils, LineWalker, NodeType, RangeUtils, Env, CefUtils, Arr, Fun) { - var isContentEditableFalse = NodeType.isContentEditableFalse; - var getSelectedNode = RangeUtils.getSelectedNode; - var isAfterContentEditableFalse = CaretUtils.isAfterContentEditableFalse; - var isBeforeContentEditableFalse = CaretUtils.isBeforeContentEditableFalse; - - var getVisualCaretPosition = function(walkFn, caretPosition) { - while ((caretPosition = walkFn(caretPosition))) { - if (caretPosition.isVisible()) { - return caretPosition; - } - } - - return caretPosition; - }; - - var isMoveInsideSameBlock = function(fromCaretPosition, toCaretPosition) { - var inSameBlock = CaretUtils.isInSameBlock(fromCaretPosition, toCaretPosition); - - // Handle bogus BR <p>abc|<br></p> - if (!inSameBlock && NodeType.isBr(fromCaretPosition.getNode())) { - return true; - } - - return inSameBlock; - }; - - var isRangeInCaretContainerBlock = function(range) { - return CaretContainer.isCaretContainerBlock(range.startContainer); - }; - - var getNormalizedRangeEndPoint = function(direction, rootNode, range) { - range = CaretUtils.normalizeRange(direction, rootNode, range); - - if (direction === -1) { - return CaretPosition.fromRangeStart(range); - } - - return CaretPosition.fromRangeEnd(range); - }; - - var moveToCeFalseHorizontally = function(direction, editor, getNextPosFn, isBeforeContentEditableFalseFn, range) { - var node, caretPosition, peekCaretPosition, rangeIsInContainerBlock; - - if (!range.collapsed) { - node = getSelectedNode(range); - if (isContentEditableFalse(node)) { - return CefUtils.showCaret(direction, editor, node, direction === -1); - } - } - - rangeIsInContainerBlock = isRangeInCaretContainerBlock(range); - caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range); - - if (isBeforeContentEditableFalseFn(caretPosition)) { - return CefUtils.selectNode(editor, caretPosition.getNode(direction === -1)); - } - - caretPosition = getNextPosFn(caretPosition); - if (!caretPosition) { - if (rangeIsInContainerBlock) { - return range; - } - - return null; - } - - if (isBeforeContentEditableFalseFn(caretPosition)) { - return CefUtils.showCaret(direction, editor, caretPosition.getNode(direction === -1), direction === 1); - } - - // Peek ahead for handling of ab|c<span cE=false> -> abc|<span cE=false> - peekCaretPosition = getNextPosFn(caretPosition); - if (isBeforeContentEditableFalseFn(peekCaretPosition)) { - if (isMoveInsideSameBlock(caretPosition, peekCaretPosition)) { - return CefUtils.showCaret(direction, editor, peekCaretPosition.getNode(direction === -1), direction === 1); - } - } - - if (rangeIsInContainerBlock) { - return CefUtils.renderRangeCaret(editor, caretPosition.toRange()); - } - - return null; - }; - - var moveToCeFalseVertically = function(direction, editor, walkerFn, range) { - var caretPosition, linePositions, nextLinePositions, - closestNextLineRect, caretClientRect, clientX, - dist1, dist2, contentEditableFalseNode; - - contentEditableFalseNode = getSelectedNode(range); - caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range); - linePositions = walkerFn(editor.getBody(), LineWalker.isAboveLine(1), caretPosition); - nextLinePositions = Arr.filter(linePositions, LineWalker.isLine(1)); - caretClientRect = Arr.last(caretPosition.getClientRects()); - - if (isBeforeContentEditableFalse(caretPosition)) { - contentEditableFalseNode = caretPosition.getNode(); - } - - if (isAfterContentEditableFalse(caretPosition)) { - contentEditableFalseNode = caretPosition.getNode(true); - } - - if (!caretClientRect) { - return null; - } - - clientX = caretClientRect.left; - - closestNextLineRect = LineUtils.findClosestClientRect(nextLinePositions, clientX); - if (closestNextLineRect) { - if (isContentEditableFalse(closestNextLineRect.node)) { - dist1 = Math.abs(clientX - closestNextLineRect.left); - dist2 = Math.abs(clientX - closestNextLineRect.right); - - return CefUtils.showCaret(direction, editor, closestNextLineRect.node, dist1 < dist2); - } - } - - if (contentEditableFalseNode) { - var caretPositions = LineWalker.positionsUntil(direction, editor.getBody(), LineWalker.isAboveLine(1), contentEditableFalseNode); - - closestNextLineRect = LineUtils.findClosestClientRect(Arr.filter(caretPositions, LineWalker.isLine(1)), clientX); - if (closestNextLineRect) { - return CefUtils.renderRangeCaret(editor, closestNextLineRect.position.toRange()); - } - - closestNextLineRect = Arr.last(Arr.filter(caretPositions, LineWalker.isLine(0))); - if (closestNextLineRect) { - return CefUtils.renderRangeCaret(editor, closestNextLineRect.position.toRange()); - } - } - }; - - var createTextBlock = function(editor) { - var textBlock = editor.dom.create(editor.settings.forced_root_block); - - if (!Env.ie || Env.ie >= 11) { - textBlock.innerHTML = '<br data-mce-bogus="1">'; - } - - return textBlock; - }; - - var exitPreBlock = function(editor, direction, range) { - var pre, caretPos, newBlock; - var caretWalker = new CaretWalker(editor.getBody()); - var getNextVisualCaretPosition = Fun.curry(getVisualCaretPosition, caretWalker.next); - var getPrevVisualCaretPosition = Fun.curry(getVisualCaretPosition, caretWalker.prev); - - if (range.collapsed && editor.settings.forced_root_block) { - pre = editor.dom.getParent(range.startContainer, 'PRE'); - if (!pre) { - return; - } - - if (direction === 1) { - caretPos = getNextVisualCaretPosition(CaretPosition.fromRangeStart(range)); - } else { - caretPos = getPrevVisualCaretPosition(CaretPosition.fromRangeStart(range)); - } - - if (!caretPos) { - newBlock = createTextBlock(editor); - - if (direction === 1) { - editor.$(pre).after(newBlock); - } else { - editor.$(pre).before(newBlock); - } - - editor.selection.select(newBlock, true); - editor.selection.collapse(); - } - } - }; - - var getHorizontalRange = function(editor, forward) { - var caretWalker = new CaretWalker(editor.getBody()); - var getNextVisualCaretPosition = Fun.curry(getVisualCaretPosition, caretWalker.next); - var getPrevVisualCaretPosition = Fun.curry(getVisualCaretPosition, caretWalker.prev); - var newRange, direction = forward ? 1 : -1; - var getNextPosFn = forward ? getNextVisualCaretPosition : getPrevVisualCaretPosition; - var isBeforeContentEditableFalseFn = forward ? isBeforeContentEditableFalse : isAfterContentEditableFalse; - var range = editor.selection.getRng(); - - newRange = moveToCeFalseHorizontally(direction, editor, getNextPosFn, isBeforeContentEditableFalseFn, range); - if (newRange) { - return newRange; - } - - newRange = exitPreBlock(editor, direction, range); - if (newRange) { - return newRange; - } - - return null; - }; - - var getVerticalRange = function(editor, down) { - var newRange, direction = down ? 1 : -1; - var walkerFn = down ? LineWalker.downUntil : LineWalker.upUntil; - var range = editor.selection.getRng(); - - newRange = moveToCeFalseVertically(direction, editor, walkerFn, range); - if (newRange) { - return newRange; - } - - newRange = exitPreBlock(editor, direction, range); - if (newRange) { - return newRange; - } - - return null; - }; - - var moveH = function(editor, forward) { - return function() { - var newRng = getHorizontalRange(editor, forward); - - if (newRng) { - editor.selection.setRng(newRng); - return true; - } else { - return false; - } - }; - }; - - var moveV = function(editor, down) { - return function() { - var newRng = getVerticalRange(editor, down); - - if (newRng) { - editor.selection.setRng(newRng); - return true; - } else { - return false; - } - }; - }; - - return { - moveH: moveH, - moveV: moveV - }; - } - ); - - define( - 'ephox.katamari.api.Merger', - - [ - 'ephox.katamari.api.Type', - 'global!Array', - 'global!Error' - ], - - function(Type, Array, Error) { - - var shallow = function(old, nu) { - return nu; - }; - - var deep = function(old, nu) { - var bothObjects = Type.isObject(old) && Type.isObject(nu); - return bothObjects ? deepMerge(old, nu) : nu; - }; - - var baseMerge = function(merger) { - return function() { - // Don't use array slice(arguments), makes the whole function unoptimisable on Chrome - var objects = new Array(arguments.length); - for (var i = 0; i < objects.length; i++) objects[i] = arguments[i]; - - if (objects.length === 0) throw new Error('Can\'t merge zero objects'); - - var ret = {}; - for (var j = 0; j < objects.length; j++) { - var curObject = objects[j]; - for (var key in curObject) - if (curObject.hasOwnProperty(key)) { - ret[key] = merger(ret[key], curObject[key]); - } - } - return ret; - }; - }; - - var deepMerge = baseMerge(deep); - var merge = baseMerge(shallow); - - return { - deepMerge: deepMerge, - merge: merge - }; - } - ); - /** - * MatchKeys.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.keyboard.MatchKeys', [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Merger' - ], - function(Arr, Fun, Merger) { - var defaultPatterns = function(patterns) { - return Arr.map(patterns, function(pattern) { - return Merger.merge({ - shiftKey: false, - altKey: false, - ctrlKey: false, - metaKey: false, - keyCode: 0, - action: Fun.noop - }, pattern); - }); - }; - - var matchesEvent = function(pattern, evt) { - return ( - evt.keyCode === pattern.keyCode && - evt.shiftKey === pattern.shiftKey && - evt.altKey === pattern.altKey && - evt.ctrlKey === pattern.ctrlKey && - evt.metaKey === pattern.metaKey - ); - }; - - var match = function(patterns, evt) { - return Arr.bind(defaultPatterns(patterns), function(pattern) { - return matchesEvent(pattern, evt) ? [pattern] : []; - }); - }; - - var action = function(f) { - var args = Array.prototype.slice.call(arguments, 1); - return function() { - return f.apply(null, args); - }; - }; - - return { - match: match, - action: action - }; - } - ); - /** - * ArrowKeys.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.keyboard.ArrowKeys', [ - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Cell', - 'tinymce.core.keyboard.BoundarySelection', - 'tinymce.core.keyboard.CefNavigation', - 'tinymce.core.keyboard.MatchKeys', - 'tinymce.core.util.VK' - ], - function(Arr, Cell, BoundarySelection, CefNavigation, MatchKeys, VK) { - var executeKeydownOverride = function(editor, caret, evt) { - var matches = MatchKeys.match([{ - keyCode: VK.RIGHT, - action: CefNavigation.moveH(editor, true) - }, - { - keyCode: VK.LEFT, - action: CefNavigation.moveH(editor, false) - }, - { - keyCode: VK.UP, - action: CefNavigation.moveV(editor, false) - }, - { - keyCode: VK.DOWN, - action: CefNavigation.moveV(editor, true) - }, - { - keyCode: VK.RIGHT, - action: BoundarySelection.move(editor, caret, true) - }, - { - keyCode: VK.LEFT, - action: BoundarySelection.move(editor, caret, false) - } - ], evt); - - Arr.find(matches, function(pattern) { - return pattern.action(); - }).each(function(_) { - evt.preventDefault(); - }); - }; - - var setup = function(editor, caret) { - editor.on('keydown', function(evt) { - if (evt.isDefaultPrevented() === false) { - executeKeydownOverride(editor, caret, evt); - } - }); - }; - - return { - setup: setup - }; - } - ); - - /** - * DeleteBackspaceKeys.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.keyboard.DeleteBackspaceKeys', [ - 'ephox.katamari.api.Arr', - 'tinymce.core.delete.BlockBoundaryDelete', - 'tinymce.core.delete.BlockRangeDelete', - 'tinymce.core.delete.CefDelete', - 'tinymce.core.delete.InlineBoundaryDelete', - 'tinymce.core.keyboard.MatchKeys', - 'tinymce.core.util.VK' - ], - function(Arr, BlockBoundaryDelete, BlockRangeDelete, CefDelete, InlineBoundaryDelete, MatchKeys, VK) { - var executeKeydownOverride = function(editor, caret, evt) { - var matches = MatchKeys.match([{ - keyCode: VK.BACKSPACE, - action: MatchKeys.action(CefDelete.backspaceDelete, editor, false) - }, - { - keyCode: VK.DELETE, - action: MatchKeys.action(CefDelete.backspaceDelete, editor, true) - }, - { - keyCode: VK.BACKSPACE, - action: MatchKeys.action(InlineBoundaryDelete.backspaceDelete, editor, caret, false) - }, - { - keyCode: VK.DELETE, - action: MatchKeys.action(InlineBoundaryDelete.backspaceDelete, editor, caret, true) - }, - { - keyCode: VK.BACKSPACE, - action: MatchKeys.action(BlockRangeDelete.backspaceDelete, editor, false) - }, - { - keyCode: VK.DELETE, - action: MatchKeys.action(BlockRangeDelete.backspaceDelete, editor, true) - }, - { - keyCode: VK.BACKSPACE, - action: MatchKeys.action(BlockBoundaryDelete.backspaceDelete, editor, false) - }, - { - keyCode: VK.DELETE, - action: MatchKeys.action(BlockBoundaryDelete.backspaceDelete, editor, true) - } - ], evt); - - Arr.find(matches, function(pattern) { - return pattern.action(); - }).each(function(_) { - evt.preventDefault(); - }); - }; - - var executeKeyupOverride = function(editor, evt) { - var matches = MatchKeys.match([{ - keyCode: VK.BACKSPACE, - action: MatchKeys.action(CefDelete.paddEmptyElement, editor) - }, - { - keyCode: VK.DELETE, - action: MatchKeys.action(CefDelete.paddEmptyElement, editor) - } - ], evt); - - Arr.find(matches, function(pattern) { - return pattern.action(); - }); - }; - - var setup = function(editor, caret) { - editor.on('keydown', function(evt) { - if (evt.isDefaultPrevented() === false) { - executeKeydownOverride(editor, caret, evt); - } - }); - - editor.on('keyup', function(evt) { - if (evt.isDefaultPrevented() === false) { - executeKeyupOverride(editor, evt); - } - }); - }; - - return { - setup: setup - }; - } - ); - - /** - * EnterKey.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Contains logic for handling the enter key to split/generate block elements. - */ - define( - 'tinymce.core.keyboard.EnterKey', [ - 'tinymce.core.caret.CaretContainer', - 'tinymce.core.dom.NodeType', - 'tinymce.core.dom.RangeUtils', - 'tinymce.core.dom.TreeWalker', - 'tinymce.core.Env', - 'tinymce.core.text.Zwsp', - 'tinymce.core.util.Tools' - ], - function(CaretContainer, NodeType, RangeUtils, TreeWalker, Env, Zwsp, Tools) { - var isIE = Env.ie && Env.ie < 11; - - var isEmptyAnchor = function(elm) { - return elm && elm.nodeName === "A" && Tools.trim(Zwsp.trim(elm.innerText || elm.textContent)).length === 0; - }; - - var isTableCell = function(node) { - return node && /^(TD|TH|CAPTION)$/.test(node.nodeName); - }; - - var emptyBlock = function(elm) { - // BR is needed in empty blocks on non IE browsers - elm.innerHTML = !isIE ? '<br data-mce-bogus="1">' : ''; - }; - - var containerAndSiblingName = function(container, nodeName) { - return container.nodeName === nodeName || (container.previousSibling && container.previousSibling.nodeName === nodeName); - }; - - // Returns true if the block can be split into two blocks or not - var canSplitBlock = function(dom, node) { - return node && - dom.isBlock(node) && - !/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) && - !/^(fixed|absolute)/i.test(node.style.position) && - dom.getContentEditable(node) !== "true"; - }; - - // Renders empty block on IE - var renderBlockOnIE = function(dom, selection, block) { - var oldRng; - - if (dom.isBlock(block)) { - oldRng = selection.getRng(); - block.appendChild(dom.create('span', null, '\u00a0')); - selection.select(block); - block.lastChild.outerHTML = ''; - selection.setRng(oldRng); - } - }; - - // Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>x</p> - var trimInlineElementsOnLeftSideOfBlock = function(dom, nonEmptyElementsMap, block) { - var node = block, - firstChilds = [], - i; - - if (!node) { - return; - } - - // Find inner most first child ex: <p><i><b>*</b></i></p> - while ((node = node.firstChild)) { - if (dom.isBlock(node)) { - return; - } - - if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) { - firstChilds.push(node); - } - } - - i = firstChilds.length; - while (i--) { - node = firstChilds[i]; - if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) { - dom.remove(node); - } else { - if (isEmptyAnchor(node)) { - dom.remove(node); - } - } - } - }; - - var normalizeZwspOffset = function(start, container, offset) { - if (NodeType.isText(container) === false) { - return offset; - } - if (start) { - return offset === 1 && container.data.charAt(offset - 1) === Zwsp.ZWSP ? 0 : offset; - } else { - return offset === container.data.length - 1 && container.data.charAt(offset) === Zwsp.ZWSP ? container.data.length : offset; - } - }; - - var includeZwspInRange = function(rng) { - var newRng = rng.cloneRange(); - newRng.setStart(rng.startContainer, normalizeZwspOffset(true, rng.startContainer, rng.startOffset)); - newRng.setEnd(rng.endContainer, normalizeZwspOffset(false, rng.endContainer, rng.endOffset)); - return newRng; - }; - - var firstNonWhiteSpaceNodeSibling = function(node) { - while (node) { - if (node.nodeType === 1 || (node.nodeType === 3 && node.data && /[\r\n\s]/.test(node.data))) { - return node; - } - - node = node.nextSibling; - } - }; - - var setup = function(editor) { - var dom = editor.dom, - selection = editor.selection, - settings = editor.settings; - var undoManager = editor.undoManager, - schema = editor.schema, - nonEmptyElementsMap = schema.getNonEmptyElements(), - moveCaretBeforeOnEnterElementsMap = schema.getMoveCaretBeforeOnEnterElements(); - - function handleEnterKey(evt) { - var rng, tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey, - newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer; - - // Moves the caret to a suitable position within the root for example in the first non - // pure whitespace text node or before an image - function moveToCaretPosition(root) { - var walker, node, rng, lastNode = root, - tempElm; - - if (!root) { - return; - } - - // Old IE versions doesn't properly render blocks with br elements in them - // For example <p><br></p> wont be rendered correctly in a contentEditable area - // until you remove the br producing <p></p> - if (Env.ie && Env.ie < 9 && parentBlock && parentBlock.firstChild) { - if (parentBlock.firstChild == parentBlock.lastChild && parentBlock.firstChild.tagName == 'BR') { - dom.remove(parentBlock.firstChild); - } - } - - if (/^(LI|DT|DD)$/.test(root.nodeName)) { - var firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild); - - if (firstChild && /^(UL|OL|DL)$/.test(firstChild.nodeName)) { - root.insertBefore(dom.doc.createTextNode('\u00a0'), root.firstChild); - } - } - - rng = dom.createRng(); - - // Normalize whitespace to remove empty text nodes. Fix for: #6904 - // Gecko will be able to place the caret in empty text nodes but it won't render propery - // Older IE versions will sometimes crash so for now ignore all IE versions - if (!Env.ie) { - root.normalize(); - } - - if (root.hasChildNodes()) { - walker = new TreeWalker(root, root); - - while ((node = walker.current())) { - if (node.nodeType == 3) { - rng.setStart(node, 0); - rng.setEnd(node, 0); - break; - } - - if (moveCaretBeforeOnEnterElementsMap[node.nodeName.toLowerCase()]) { - rng.setStartBefore(node); - rng.setEndBefore(node); - break; - } - - lastNode = node; - node = walker.next(); - } - - if (!node) { - rng.setStart(lastNode, 0); - rng.setEnd(lastNode, 0); - } - } else { - if (root.nodeName == 'BR') { - if (root.nextSibling && dom.isBlock(root.nextSibling)) { - // Trick on older IE versions to render the caret before the BR between two lists - if (!documentMode || documentMode < 9) { - tempElm = dom.create('br'); - root.parentNode.insertBefore(tempElm, root); - } - - rng.setStartBefore(root); - rng.setEndBefore(root); - } else { - rng.setStartAfter(root); - rng.setEndAfter(root); - } - } else { - rng.setStart(root, 0); - rng.setEnd(root, 0); - } - } - - selection.setRng(rng); - - // Remove tempElm created for old IE:s - dom.remove(tempElm); - selection.scrollIntoView(root); - } - - function setForcedBlockAttrs(node) { - var forcedRootBlockName = settings.forced_root_block; - - if (forcedRootBlockName && forcedRootBlockName.toLowerCase() === node.tagName.toLowerCase()) { - dom.setAttribs(node, settings.forced_root_block_attrs); - } - } - - // Creates a new block element by cloning the current one or creating a new one if the name is specified - // This function will also copy any text formatting from the parent block and add it to the new one - function createNewBlock(name) { - var node = container, - block, clonedNode, caretNode, textInlineElements = schema.getTextInlineElements(); - - if (name || parentBlockName == "TABLE" || parentBlockName == "HR") { - block = dom.create(name || newBlockName); - setForcedBlockAttrs(block); - } else { - block = parentBlock.cloneNode(false); - } - - caretNode = block; - - if (settings.keep_styles === false) { - dom.setAttrib(block, 'style', null); // wipe out any styles that came over with the block - dom.setAttrib(block, 'class', null); - } else { - // Clone any parent styles - do { - if (textInlineElements[node.nodeName]) { - // Never clone a caret containers - if (node.id == '_mce_caret') { - continue; - } - - clonedNode = node.cloneNode(false); - dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique - - if (block.hasChildNodes()) { - clonedNode.appendChild(block.firstChild); - block.appendChild(clonedNode); - } else { - caretNode = clonedNode; - block.appendChild(clonedNode); - } - } - } while ((node = node.parentNode) && node != editableRoot); - } - - // BR is needed in empty blocks on non IE browsers - if (!isIE) { - caretNode.innerHTML = '<br data-mce-bogus="1">'; - } - - return block; - } - - // Returns true/false if the caret is at the start/end of the parent block element - function isCaretAtStartOrEndOfBlock(start) { - var walker, node, name, normalizedOffset; - - normalizedOffset = normalizeZwspOffset(start, container, offset); - - // Caret is in the middle of a text node like "a|b" - if (container.nodeType == 3 && (start ? normalizedOffset > 0 : normalizedOffset < container.nodeValue.length)) { - return false; - } - - // If after the last element in block node edge case for #5091 - if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) { - return true; - } - - // If the caret if before the first element in parentBlock - if (start && container.nodeType == 1 && container == parentBlock.firstChild) { - return true; - } - - // Caret can be before/after a table or a hr - if (containerAndSiblingName(container, 'TABLE') || containerAndSiblingName(container, 'HR')) { - return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start); - } - - // Walk the DOM and look for text nodes or non empty elements - walker = new TreeWalker(container, parentBlock); - - // If caret is in beginning or end of a text block then jump to the next/previous node - if (container.nodeType == 3) { - if (start && normalizedOffset === 0) { - walker.prev(); - } else if (!start && normalizedOffset == container.nodeValue.length) { - walker.next(); - } - } - - while ((node = walker.current())) { - if (node.nodeType === 1) { - // Ignore bogus elements - if (!node.getAttribute('data-mce-bogus')) { - // Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p> - name = node.nodeName.toLowerCase(); - if (nonEmptyElementsMap[name] && name !== 'br') { - return false; - } - } - } else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) { - return false; - } - - if (start) { - walker.prev(); - } else { - walker.next(); - } - } - - return true; - } - - // Wraps any text nodes or inline elements in the specified forced root block name - function wrapSelfAndSiblingsInDefaultBlock(container, offset) { - var newBlock, parentBlock, startNode, node, next, rootBlockName, blockName = newBlockName || 'P'; - - // Not in a block element or in a table cell or caption - parentBlock = dom.getParent(container, dom.isBlock); - if (!parentBlock || !canSplitBlock(dom, parentBlock)) { - parentBlock = parentBlock || editableRoot; - - if (parentBlock == editor.getBody() || isTableCell(parentBlock)) { - rootBlockName = parentBlock.nodeName.toLowerCase(); - } else { - rootBlockName = parentBlock.parentNode.nodeName.toLowerCase(); - } - - if (!parentBlock.hasChildNodes()) { - newBlock = dom.create(blockName); - setForcedBlockAttrs(newBlock); - parentBlock.appendChild(newBlock); - rng.setStart(newBlock, 0); - rng.setEnd(newBlock, 0); - return newBlock; - } - - // Find parent that is the first child of parentBlock - node = container; - while (node.parentNode != parentBlock) { - node = node.parentNode; - } - - // Loop left to find start node start wrapping at - while (node && !dom.isBlock(node)) { - startNode = node; - node = node.previousSibling; - } - - if (startNode && schema.isValidChild(rootBlockName, blockName.toLowerCase())) { - newBlock = dom.create(blockName); - setForcedBlockAttrs(newBlock); - startNode.parentNode.insertBefore(newBlock, startNode); - - // Start wrapping until we hit a block - node = startNode; - while (node && !dom.isBlock(node)) { - next = node.nextSibling; - newBlock.appendChild(node); - node = next; - } - - // Restore range to it's past location - rng.setStart(container, offset); - rng.setEnd(container, offset); - } - } - - return container; - } - - // Inserts a block or br before/after or in the middle of a split list of the LI is empty - function handleEmptyListItem() { - function isFirstOrLastLi(first) { - var node = containerBlock[first ? 'firstChild' : 'lastChild']; - - // Find first/last element since there might be whitespace there - while (node) { - if (node.nodeType == 1) { - break; - } - - node = node[first ? 'nextSibling' : 'previousSibling']; - } - - return node === parentBlock; - } - - function getContainerBlock() { - var containerBlockParent = containerBlock.parentNode; - - if (/^(LI|DT|DD)$/.test(containerBlockParent.nodeName)) { - return containerBlockParent; - } - - return containerBlock; - } - - if (containerBlock == editor.getBody()) { - return; - } - - // Check if we are in an nested list - var containerBlockParentName = containerBlock.parentNode.nodeName; - if (/^(OL|UL|LI)$/.test(containerBlockParentName)) { - newBlockName = 'LI'; - } - - newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR'); - - if (isFirstOrLastLi(true) && isFirstOrLastLi()) { - if (containerBlockParentName == 'LI') { - // Nested list is inside a LI - dom.insertAfter(newBlock, getContainerBlock()); - } else { - // Is first and last list item then replace the OL/UL with a text block - dom.replace(newBlock, containerBlock); - } - } else if (isFirstOrLastLi(true)) { - if (containerBlockParentName == 'LI') { - // List nested in an LI then move the list to a new sibling LI - dom.insertAfter(newBlock, getContainerBlock()); - newBlock.appendChild(dom.doc.createTextNode(' ')); // Needed for IE so the caret can be placed - newBlock.appendChild(containerBlock); - } else { - // First LI in list then remove LI and add text block before list - containerBlock.parentNode.insertBefore(newBlock, containerBlock); - } - } else if (isFirstOrLastLi()) { - // Last LI in list then remove LI and add text block after list - dom.insertAfter(newBlock, getContainerBlock()); - renderBlockOnIE(dom, selection, newBlock); - } else { - // Middle LI in list the split the list and insert a text block in the middle - // Extract after fragment and insert it after the current block - containerBlock = getContainerBlock(); - tmpRng = rng.cloneRange(); - tmpRng.setStartAfter(parentBlock); - tmpRng.setEndAfter(containerBlock); - fragment = tmpRng.extractContents(); - - if (newBlockName == 'LI' && fragment.firstChild.nodeName == 'LI') { - newBlock = fragment.firstChild; - dom.insertAfter(fragment, containerBlock); - } else { - dom.insertAfter(fragment, containerBlock); - dom.insertAfter(newBlock, containerBlock); - } - } - - dom.remove(parentBlock); - moveToCaretPosition(newBlock); - undoManager.add(); - } - - // Inserts a BR element if the forced_root_block option is set to false or empty string - function insertBr() { - editor.execCommand("InsertLineBreak", false, evt); - } - - // Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element - function trimLeadingLineBreaks(node) { - do { - if (node.nodeType === 3) { - node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, ''); - } - - node = node.firstChild; - } while (node); - } - - function getEditableRoot(node) { - var root = dom.getRoot(), - parent, editableRoot; - - // Get all parents until we hit a non editable parent or the root - parent = node; - while (parent !== root && dom.getContentEditable(parent) !== "false") { - if (dom.getContentEditable(parent) === "true") { - editableRoot = parent; - } - - parent = parent.parentNode; - } - - return parent !== root ? editableRoot : root; - } - - // Adds a BR at the end of blocks that only contains an IMG or INPUT since - // these might be floated and then they won't expand the block - function addBrToBlockIfNeeded(block) { - var lastChild; - - // IE will render the blocks correctly other browsers needs a BR - if (!isIE) { - block.normalize(); // Remove empty text nodes that got left behind by the extract - - // Check if the block is empty or contains a floated last child - lastChild = block.lastChild; - if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) { - dom.add(block, 'br'); - } - } - } - - function insertNewBlockAfter() { - // If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup - if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') { - newBlock = createNewBlock(newBlockName); - } else { - newBlock = createNewBlock(); - } - - // Split the current container block element if enter is pressed inside an empty inner block element - if (settings.end_container_on_empty_block && canSplitBlock(dom, containerBlock) && dom.isEmpty(parentBlock)) { - // Split container block for example a BLOCKQUOTE at the current blockParent location for example a P - newBlock = dom.split(containerBlock, parentBlock); - } else { - dom.insertAfter(newBlock, parentBlock); - } - - moveToCaretPosition(newBlock); - } - - rng = selection.getRng(true); - - // Event is blocked by some other handler for example the lists plugin - if (evt.isDefaultPrevented()) { - return; - } - - // Delete any selected contents - if (!rng.collapsed) { - editor.execCommand('Delete'); - return; - } - - // Setup range items and newBlockName - new RangeUtils(dom).normalize(rng); - container = rng.startContainer; - offset = rng.startOffset; - newBlockName = (settings.force_p_newlines ? 'p' : '') || settings.forced_root_block; - newBlockName = newBlockName ? newBlockName.toUpperCase() : ''; - documentMode = dom.doc.documentMode; - shiftKey = evt.shiftKey; - - // Resolve node index - if (container.nodeType == 1 && container.hasChildNodes()) { - isAfterLastNodeInContainer = offset > container.childNodes.length - 1; - - container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container; - if (isAfterLastNodeInContainer && container.nodeType == 3) { - offset = container.nodeValue.length; - } else { - offset = 0; - } - } - - // Get editable root node, normally the body element but sometimes a div or span - editableRoot = getEditableRoot(container); - - // If there is no editable root then enter is done inside a contentEditable false element - if (!editableRoot) { - return; - } - - undoManager.beforeChange(); - - // If editable root isn't block nor the root of the editor - if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) { - if (!newBlockName || shiftKey) { - insertBr(); - } - - return; - } - - // Wrap the current node and it's sibling in a default block if it's needed. - // for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td> - // This won't happen if root blocks are disabled or the shiftKey is pressed - if ((newBlockName && !shiftKey) || (!newBlockName && shiftKey)) { - container = wrapSelfAndSiblingsInDefaultBlock(container, offset); - } - - // Find parent block and setup empty block paddings - parentBlock = dom.getParent(container, dom.isBlock); - containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null; - - // Setup block names - parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5 - containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5 - - // Enter inside block contained within a LI then split or insert before/after LI - if (containerBlockName == 'LI' && !evt.ctrlKey) { - parentBlock = containerBlock; - parentBlockName = containerBlockName; - } - - if (editor.undoManager.typing) { - editor.undoManager.typing = false; - editor.undoManager.add(); - } - - // Handle enter in list item - if (/^(LI|DT|DD)$/.test(parentBlockName)) { - if (!newBlockName && shiftKey) { - insertBr(); - return; - } - - // Handle enter inside an empty list item - if (dom.isEmpty(parentBlock)) { - handleEmptyListItem(); - return; - } - } - - // Don't split PRE tags but insert a BR instead easier when writing code samples etc - if (parentBlockName == 'PRE' && settings.br_in_pre !== false) { - if (!shiftKey) { - insertBr(); - return; - } - } else { - // If no root block is configured then insert a BR by default or if the shiftKey is pressed - if ((!newBlockName && !shiftKey && parentBlockName != 'LI') || (newBlockName && shiftKey)) { - insertBr(); - return; - } - } - - // If parent block is root then never insert new blocks - if (newBlockName && parentBlock === editor.getBody()) { - return; - } - - // Default block name if it's not configured - newBlockName = newBlockName || 'P'; - - // Insert new block before/after the parent block depending on caret location - if (CaretContainer.isCaretContainerBlock(parentBlock)) { - newBlock = CaretContainer.showCaretContainerBlock(parentBlock); - if (dom.isEmpty(parentBlock)) { - emptyBlock(parentBlock); - } - moveToCaretPosition(newBlock); - } else if (isCaretAtStartOrEndOfBlock()) { - insertNewBlockAfter(); - } else if (isCaretAtStartOrEndOfBlock(true)) { - // Insert new block before - newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock); - renderBlockOnIE(dom, selection, newBlock); - - // Adjust caret position if HR - containerAndSiblingName(parentBlock, 'HR') ? moveToCaretPosition(newBlock) : moveToCaretPosition(parentBlock); - } else { - // Extract after fragment and insert it after the current block - tmpRng = includeZwspInRange(rng).cloneRange(); - tmpRng.setEndAfter(parentBlock); - fragment = tmpRng.extractContents(); - trimLeadingLineBreaks(fragment); - newBlock = fragment.firstChild; - dom.insertAfter(fragment, parentBlock); - trimInlineElementsOnLeftSideOfBlock(dom, nonEmptyElementsMap, newBlock); - addBrToBlockIfNeeded(parentBlock); - - if (dom.isEmpty(parentBlock)) { - emptyBlock(parentBlock); - } - - newBlock.normalize(); - - // New block might become empty if it's <p><b>a |</b></p> - if (dom.isEmpty(newBlock)) { - dom.remove(newBlock); - insertNewBlockAfter(); - } else { - moveToCaretPosition(newBlock); - } - } - - dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique - - // Allow custom handling of new blocks - editor.fire('NewBlock', { - newBlock: newBlock - }); - - undoManager.typing = false; - undoManager.add(); - } - - editor.on('keydown', function(evt) { - if (evt.keyCode == 13) { - if (handleEnterKey(evt) !== false) { - evt.preventDefault(); - } - } - }); - }; - - return { - setup: setup - }; - } - ); - - /** - * InsertSpace.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.keyboard.InsertSpace', [ - 'ephox.katamari.api.Fun', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.dom.NodeType', - 'tinymce.core.keyboard.BoundaryLocation' - ], - function(Fun, CaretPosition, NodeType, BoundaryLocation) { - var isValidInsertPoint = function(location, caretPosition) { - return isAtStartOrEnd(location) && NodeType.isText(caretPosition.container()); - }; - - var insertNbspAtPosition = function(editor, caretPosition) { - var container = caretPosition.container(); - var offset = caretPosition.offset(); - - container.insertData(offset, '\u00a0'); - editor.selection.setCursorLocation(container, offset + 1); - }; - - var insertAtLocation = function(editor, caretPosition, location) { - if (isValidInsertPoint(location, caretPosition)) { - insertNbspAtPosition(editor, caretPosition); - return true; - } else { - return false; - } - }; - - var insertAtCaret = function(editor) { - var caretPosition = CaretPosition.fromRangeStart(editor.selection.getRng()); - var boundaryLocation = BoundaryLocation.readLocation(editor.getBody(), caretPosition); - return boundaryLocation.map(Fun.curry(insertAtLocation, editor, caretPosition)).getOr(false); - }; - - var isAtStartOrEnd = function(location) { - return location.fold( - Fun.constant(false), // Before - Fun.constant(true), // Start - Fun.constant(true), // End - Fun.constant(false) // After - ); - }; - - var insertAtSelection = function(editor) { - return editor.selection.isCollapsed() ? insertAtCaret(editor) : false; - }; - - return { - insertAtSelection: insertAtSelection - }; - } - ); - - /** - * SpaceKey.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.keyboard.SpaceKey', [ - 'ephox.katamari.api.Arr', - 'tinymce.core.keyboard.InsertSpace', - 'tinymce.core.keyboard.MatchKeys', - 'tinymce.core.util.VK' - ], - function(Arr, InsertSpace, MatchKeys, VK) { - var executeKeydownOverride = function(editor, evt) { - var matches = MatchKeys.match([{ - keyCode: VK.SPACEBAR, - action: MatchKeys.action(InsertSpace.insertAtSelection, editor) - }], evt); - - Arr.find(matches, function(pattern) { - return pattern.action(); - }).each(function(_) { - evt.preventDefault(); - }); - }; - - var setup = function(editor) { - editor.on('keydown', function(evt) { - if (evt.isDefaultPrevented() === false) { - executeKeydownOverride(editor, evt); - } - }); - }; - - return { - setup: setup - }; - } - ); - - /** - * KeyboardOverrides.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.keyboard.KeyboardOverrides', [ - 'tinymce.core.keyboard.ArrowKeys', - 'tinymce.core.keyboard.BoundarySelection', - 'tinymce.core.keyboard.DeleteBackspaceKeys', - 'tinymce.core.keyboard.EnterKey', - 'tinymce.core.keyboard.SpaceKey' - ], - function(ArrowKeys, BoundarySelection, DeleteBackspaceKeys, EnterKey, SpaceKey) { - var setup = function(editor) { - var caret = BoundarySelection.setupSelectedState(editor); - - ArrowKeys.setup(editor, caret); - DeleteBackspaceKeys.setup(editor, caret); - EnterKey.setup(editor); - SpaceKey.setup(editor); - }; - - return { - setup: setup - }; - } - ); - /** - * NodeChange.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class handles the nodechange event dispatching both manual and through selection change events. - * - * @class tinymce.NodeChange - * @private - */ - define( - 'tinymce.core.NodeChange', [ - "tinymce.core.dom.RangeUtils", - "tinymce.core.Env", - "tinymce.core.util.Delay" - ], - function(RangeUtils, Env, Delay) { - return function(editor) { - var lastRng, lastPath = []; - - /** - * Returns true/false if the current element path has been changed or not. - * - * @private - * @return {Boolean} True if the element path is the same false if it's not. - */ - function isSameElementPath(startElm) { - var i, currentPath; - - currentPath = editor.$(startElm).parentsUntil(editor.getBody()).add(startElm); - if (currentPath.length === lastPath.length) { - for (i = currentPath.length; i >= 0; i--) { - if (currentPath[i] !== lastPath[i]) { - break; - } - } - - if (i === -1) { - lastPath = currentPath; - return true; - } - } - - lastPath = currentPath; - - return false; - } - - // Gecko doesn't support the "selectionchange" event - if (!('onselectionchange' in editor.getDoc())) { - editor.on('NodeChange Click MouseUp KeyUp Focus', function(e) { - var nativeRng, fakeRng; - - // Since DOM Ranges mutate on modification - // of the DOM we need to clone it's contents - nativeRng = editor.selection.getRng(); - fakeRng = { - startContainer: nativeRng.startContainer, - startOffset: nativeRng.startOffset, - endContainer: nativeRng.endContainer, - endOffset: nativeRng.endOffset - }; - - // Always treat nodechange as a selectionchange since applying - // formatting to the current range wouldn't update the range but it's parent - if (e.type == 'nodechange' || !RangeUtils.compareRanges(fakeRng, lastRng)) { - editor.fire('SelectionChange'); - } - - lastRng = fakeRng; - }); - } - - // IE has a bug where it fires a selectionchange on right click that has a range at the start of the body - // When the contextmenu event fires the selection is located at the right location - editor.on('contextmenu', function() { - editor.fire('SelectionChange'); - }); - - // Selection change is delayed ~200ms on IE when you click inside the current range - editor.on('SelectionChange', function() { - var startElm = editor.selection.getStart(true); - - // IE 8 will fire a selectionchange event with an incorrect selection - // when focusing out of table cells. Click inside cell -> toolbar = Invalid SelectionChange event - if (!Env.range && editor.selection.isCollapsed()) { - return; - } - - if (!isSameElementPath(startElm) && editor.dom.isChildOf(startElm, editor.getBody())) { - editor.nodeChanged({ - selectionChange: true - }); - } - }); - - // Fire an extra nodeChange on mouseup for compatibility reasons - editor.on('MouseUp', function(e) { - if (!e.isDefaultPrevented()) { - // Delay nodeChanged call for WebKit edge case issue where the range - // isn't updated until after you click outside a selected image - if (editor.selection.getNode().nodeName == 'IMG') { - Delay.setEditorTimeout(editor, function() { - editor.nodeChanged(); - }); - } else { - editor.nodeChanged(); - } - } - }); - - /** - * Dispatches out a onNodeChange event to all observers. This method should be called when you - * need to update the UI states or element path etc. - * - * @method nodeChanged - * @param {Object} args Optional args to pass to NodeChange event handlers. - */ - this.nodeChanged = function(args) { - var selection = editor.selection, - node, parents, root; - - // Fix for bug #1896577 it seems that this can not be fired while the editor is loading - if (editor.initialized && selection && !editor.settings.disable_nodechange && !editor.readonly) { - // Get start node - root = editor.getBody(); - node = selection.getStart(true) || root; - - // Make sure the node is within the editor root or is the editor root - if (node.ownerDocument != editor.getDoc() || !editor.dom.isChildOf(node, root)) { - node = root; - } - - // Get parents and add them to object - parents = []; - editor.dom.getParent(node, function(node) { - if (node === root) { - return true; - } - - parents.push(node); - }); - - args = args || {}; - args.element = node; - args.parents = parents; - - editor.fire('NodeChange', args); - } - }; - }; - } - ); - - /** - * FakeCaret.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module contains logic for rendering a fake visual caret. - * - * @private - * @class tinymce.caret.FakeCaret - */ - define( - 'tinymce.core.caret.FakeCaret', [ - 'tinymce.core.caret.CaretContainer', - 'tinymce.core.caret.CaretContainerRemove', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.dom.DomQuery', - 'tinymce.core.dom.NodeType', - 'tinymce.core.dom.RangeUtils', - 'tinymce.core.geom.ClientRect', - 'tinymce.core.util.Delay' - ], - function(CaretContainer, CaretContainerRemove, CaretPosition, DomQuery, NodeType, RangeUtils, ClientRect, Delay) { - var isContentEditableFalse = NodeType.isContentEditableFalse; - - var isTableCell = function(node) { - return node && /^(TD|TH)$/i.test(node.nodeName); - }; - - return function(rootNode, isBlock) { - var cursorInterval, $lastVisualCaret, caretContainerNode; - - function getAbsoluteClientRect(node, before) { - var clientRect = ClientRect.collapse(node.getBoundingClientRect(), before), - docElm, scrollX, scrollY, margin, rootRect; - - if (rootNode.tagName == 'BODY') { - docElm = rootNode.ownerDocument.documentElement; - scrollX = rootNode.scrollLeft || docElm.scrollLeft; - scrollY = rootNode.scrollTop || docElm.scrollTop; - } else { - rootRect = rootNode.getBoundingClientRect(); - scrollX = rootNode.scrollLeft - rootRect.left; - scrollY = rootNode.scrollTop - rootRect.top; - } - - clientRect.left += scrollX; - clientRect.right += scrollX; - clientRect.top += scrollY; - clientRect.bottom += scrollY; - clientRect.width = 1; - - margin = node.offsetWidth - node.clientWidth; - - if (margin > 0) { - if (before) { - margin *= -1; - } - - clientRect.left += margin; - clientRect.right += margin; - } - - return clientRect; - } - - function trimInlineCaretContainers() { - var contentEditableFalseNodes, node, sibling, i, data; - - contentEditableFalseNodes = DomQuery('*[contentEditable=false]', rootNode); - for (i = 0; i < contentEditableFalseNodes.length; i++) { - node = contentEditableFalseNodes[i]; - - sibling = node.previousSibling; - if (CaretContainer.endsWithCaretContainer(sibling)) { - data = sibling.data; - - if (data.length == 1) { - sibling.parentNode.removeChild(sibling); - } else { - sibling.deleteData(data.length - 1, 1); - } - } - - sibling = node.nextSibling; - if (CaretContainer.startsWithCaretContainer(sibling)) { - data = sibling.data; - - if (data.length == 1) { - sibling.parentNode.removeChild(sibling); - } else { - sibling.deleteData(0, 1); - } - } - } - - return null; - } - - function show(before, node) { - var clientRect, rng; - - hide(); - - if (isTableCell(node)) { - return null; - } - - if (isBlock(node)) { - caretContainerNode = CaretContainer.insertBlock('p', node, before); - clientRect = getAbsoluteClientRect(node, before); - DomQuery(caretContainerNode).css('top', clientRect.top); - - $lastVisualCaret = DomQuery('<div class="mce-visual-caret" data-mce-bogus="all"></div>').css(clientRect).appendTo(rootNode); - - if (before) { - $lastVisualCaret.addClass('mce-visual-caret-before'); - } - - startBlink(); - - rng = node.ownerDocument.createRange(); - rng.setStart(caretContainerNode, 0); - rng.setEnd(caretContainerNode, 0); - } else { - caretContainerNode = CaretContainer.insertInline(node, before); - rng = node.ownerDocument.createRange(); - - if (isContentEditableFalse(caretContainerNode.nextSibling)) { - rng.setStart(caretContainerNode, 0); - rng.setEnd(caretContainerNode, 0); - } else { - rng.setStart(caretContainerNode, 1); - rng.setEnd(caretContainerNode, 1); - } - - return rng; - } - - return rng; - } - - function hide() { - trimInlineCaretContainers(); - - if (caretContainerNode) { - CaretContainerRemove.remove(caretContainerNode); - caretContainerNode = null; - } - - if ($lastVisualCaret) { - $lastVisualCaret.remove(); - $lastVisualCaret = null; - } - - clearInterval(cursorInterval); - } - - function startBlink() { - cursorInterval = Delay.setInterval(function() { - DomQuery('div.mce-visual-caret', rootNode).toggleClass('mce-visual-caret-hidden'); - }, 500); - } - - function destroy() { - Delay.clearInterval(cursorInterval); - } - - function getCss() { - return ( - '.mce-visual-caret {' + - 'position: absolute;' + - 'background-color: black;' + - 'background-color: currentcolor;' + - '}' + - '.mce-visual-caret-hidden {' + - 'display: none;' + - '}' + - '*[data-mce-caret] {' + - 'position: absolute;' + - 'left: -1000px;' + - 'right: auto;' + - 'top: 0;' + - 'margin: 0;' + - 'padding: 0;' + - '}' - ); - } - - return { - show: show, - hide: hide, - getCss: getCss, - destroy: destroy - }; - }; - } - ); - /** - * MousePosition.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module calculates an absolute coordinate inside the editor body for both local and global mouse events. - * - * @private - * @class tinymce.dom.MousePosition - */ - define( - 'tinymce.core.dom.MousePosition', [], - function() { - var getAbsolutePosition = function(elm) { - var doc, docElem, win, clientRect; - - clientRect = elm.getBoundingClientRect(); - doc = elm.ownerDocument; - docElem = doc.documentElement; - win = doc.defaultView; - - return { - top: clientRect.top + win.pageYOffset - docElem.clientTop, - left: clientRect.left + win.pageXOffset - docElem.clientLeft - }; - }; - - var getBodyPosition = function(editor) { - return editor.inline ? getAbsolutePosition(editor.getBody()) : { - left: 0, - top: 0 - }; - }; - - var getScrollPosition = function(editor) { - var body = editor.getBody(); - return editor.inline ? { - left: body.scrollLeft, - top: body.scrollTop - } : { - left: 0, - top: 0 - }; - }; - - var getBodyScroll = function(editor) { - var body = editor.getBody(), - docElm = editor.getDoc().documentElement; - var inlineScroll = { - left: body.scrollLeft, - top: body.scrollTop - }; - var iframeScroll = { - left: body.scrollLeft || docElm.scrollLeft, - top: body.scrollTop || docElm.scrollTop - }; - - return editor.inline ? inlineScroll : iframeScroll; - }; - - var getMousePosition = function(editor, event) { - if (event.target.ownerDocument !== editor.getDoc()) { - var iframePosition = getAbsolutePosition(editor.getContentAreaContainer()); - var scrollPosition = getBodyScroll(editor); - - return { - left: event.pageX - iframePosition.left + scrollPosition.left, - top: event.pageY - iframePosition.top + scrollPosition.top - }; - } - - return { - left: event.pageX, - top: event.pageY - }; - }; - - var calculatePosition = function(bodyPosition, scrollPosition, mousePosition) { - return { - pageX: (mousePosition.left - bodyPosition.left) + scrollPosition.left, - pageY: (mousePosition.top - bodyPosition.top) + scrollPosition.top - }; - }; - - var calc = function(editor, event) { - return calculatePosition(getBodyPosition(editor), getScrollPosition(editor), getMousePosition(editor, event)); - }; - - return { - calc: calc - }; - } - ); - - /** - * DragDropOverrides.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module contains logic overriding the drag/drop logic of the editor. - * - * @private - * @class tinymce.DragDropOverrides - */ - define( - 'tinymce.core.DragDropOverrides', [ - "tinymce.core.dom.NodeType", - "tinymce.core.util.Arr", - "tinymce.core.util.Fun", - "tinymce.core.util.Delay", - "tinymce.core.dom.DOMUtils", - "tinymce.core.dom.MousePosition" - ], - function( - NodeType, Arr, Fun, Delay, DOMUtils, MousePosition - ) { - var isContentEditableFalse = NodeType.isContentEditableFalse, - isContentEditableTrue = NodeType.isContentEditableTrue; - - var isDraggable = function(rootElm, elm) { - return isContentEditableFalse(elm) && elm !== rootElm; - }; - - var isValidDropTarget = function(editor, targetElement, dragElement) { - if (targetElement === dragElement || editor.dom.isChildOf(targetElement, dragElement)) { - return false; - } - - if (isContentEditableFalse(targetElement)) { - return false; - } - - return true; - }; - - var cloneElement = function(elm) { - var cloneElm = elm.cloneNode(true); - cloneElm.removeAttribute('data-mce-selected'); - return cloneElm; - }; - - var createGhost = function(editor, elm, width, height) { - var clonedElm = elm.cloneNode(true); - - editor.dom.setStyles(clonedElm, { - width: width, - height: height - }); - editor.dom.setAttrib(clonedElm, 'data-mce-selected', null); - - var ghostElm = editor.dom.create('div', { - 'class': 'mce-drag-container', - 'data-mce-bogus': 'all', - unselectable: 'on', - contenteditable: 'false' - }); - - editor.dom.setStyles(ghostElm, { - position: 'absolute', - opacity: 0.5, - overflow: 'hidden', - border: 0, - padding: 0, - margin: 0, - width: width, - height: height - }); - - editor.dom.setStyles(clonedElm, { - margin: 0, - boxSizing: 'border-box' - }); - - ghostElm.appendChild(clonedElm); - - return ghostElm; - }; - - var appendGhostToBody = function(ghostElm, bodyElm) { - if (ghostElm.parentNode !== bodyElm) { - bodyElm.appendChild(ghostElm); - } - }; - - var moveGhost = function(ghostElm, position, width, height, maxX, maxY) { - var overflowX = 0, - overflowY = 0; - - ghostElm.style.left = position.pageX + 'px'; - ghostElm.style.top = position.pageY + 'px'; - - if (position.pageX + width > maxX) { - overflowX = (position.pageX + width) - maxX; - } - - if (position.pageY + height > maxY) { - overflowY = (position.pageY + height) - maxY; - } - - ghostElm.style.width = (width - overflowX) + 'px'; - ghostElm.style.height = (height - overflowY) + 'px'; - }; - - var removeElement = function(elm) { - if (elm && elm.parentNode) { - elm.parentNode.removeChild(elm); - } - }; - - var isLeftMouseButtonPressed = function(e) { - return e.button === 0; - }; - - var hasDraggableElement = function(state) { - return state.element; - }; - - var applyRelPos = function(state, position) { - return { - pageX: position.pageX - state.relX, - pageY: position.pageY + 5 - }; - }; - - var start = function(state, editor) { - return function(e) { - if (isLeftMouseButtonPressed(e)) { - var ceElm = Arr.find(editor.dom.getParents(e.target), Fun.or(isContentEditableFalse, isContentEditableTrue)); - - if (isDraggable(editor.getBody(), ceElm)) { - var elmPos = editor.dom.getPos(ceElm); - var bodyElm = editor.getBody(); - var docElm = editor.getDoc().documentElement; - - state.element = ceElm; - state.screenX = e.screenX; - state.screenY = e.screenY; - state.maxX = (editor.inline ? bodyElm.scrollWidth : docElm.offsetWidth) - 2; - state.maxY = (editor.inline ? bodyElm.scrollHeight : docElm.offsetHeight) - 2; - state.relX = e.pageX - elmPos.x; - state.relY = e.pageY - elmPos.y; - state.width = ceElm.offsetWidth; - state.height = ceElm.offsetHeight; - state.ghost = createGhost(editor, ceElm, state.width, state.height); - } - } - }; - }; - - var move = function(state, editor) { - // Reduces laggy drag behavior on Gecko - var throttledPlaceCaretAt = Delay.throttle(function(clientX, clientY) { - editor._selectionOverrides.hideFakeCaret(); - editor.selection.placeCaretAt(clientX, clientY); - }, 0); - - return function(e) { - var movement = Math.max(Math.abs(e.screenX - state.screenX), Math.abs(e.screenY - state.screenY)); - - if (hasDraggableElement(state) && !state.dragging && movement > 10) { - var args = editor.fire('dragstart', { - target: state.element - }); - if (args.isDefaultPrevented()) { - return; - } - - state.dragging = true; - editor.focus(); - } - - if (state.dragging) { - var targetPos = applyRelPos(state, MousePosition.calc(editor, e)); - - appendGhostToBody(state.ghost, editor.getBody()); - moveGhost(state.ghost, targetPos, state.width, state.height, state.maxX, state.maxY); - - throttledPlaceCaretAt(e.clientX, e.clientY); - } - }; - }; - - // Returns the raw element instead of the fake cE=false element - var getRawTarget = function(selection) { - var rng = selection.getSel().getRangeAt(0); - var startContainer = rng.startContainer; - return startContainer.nodeType === 3 ? startContainer.parentNode : startContainer; - }; - - var drop = function(state, editor) { - return function(e) { - if (state.dragging) { - if (isValidDropTarget(editor, getRawTarget(editor.selection), state.element)) { - var targetClone = cloneElement(state.element); - - var args = editor.fire('drop', { - targetClone: targetClone, - clientX: e.clientX, - clientY: e.clientY - }); - - if (!args.isDefaultPrevented()) { - targetClone = args.targetClone; - - editor.undoManager.transact(function() { - removeElement(state.element); - editor.insertContent(editor.dom.getOuterHTML(targetClone)); - editor._selectionOverrides.hideFakeCaret(); - }); - } - } - } - - removeDragState(state); - }; - }; - - var stop = function(state, editor) { - return function() { - removeDragState(state); - if (state.dragging) { - editor.fire('dragend'); - } - }; - }; - - var removeDragState = function(state) { - state.dragging = false; - state.element = null; - removeElement(state.ghost); - }; - - var bindFakeDragEvents = function(editor) { - var state = {}, - pageDom, dragStartHandler, dragHandler, dropHandler, dragEndHandler, rootDocument; - - pageDom = DOMUtils.DOM; - rootDocument = document; - dragStartHandler = start(state, editor); - dragHandler = move(state, editor); - dropHandler = drop(state, editor); - dragEndHandler = stop(state, editor); - - editor.on('mousedown', dragStartHandler); - editor.on('mousemove', dragHandler); - editor.on('mouseup', dropHandler); - - pageDom.bind(rootDocument, 'mousemove', dragHandler); - pageDom.bind(rootDocument, 'mouseup', dragEndHandler); - - editor.on('remove', function() { - pageDom.unbind(rootDocument, 'mousemove', dragHandler); - pageDom.unbind(rootDocument, 'mouseup', dragEndHandler); - }); - }; - - var blockIeDrop = function(editor) { - editor.on('drop', function(e) { - // FF doesn't pass out clientX/clientY for drop since this is for IE we just use null instead - var realTarget = typeof e.clientX !== 'undefined' ? editor.getDoc().elementFromPoint(e.clientX, e.clientY) : null; - - if (isContentEditableFalse(realTarget) || isContentEditableFalse(editor.dom.getContentEditableParent(realTarget))) { - e.preventDefault(); - } - }); - }; - - var init = function(editor) { - bindFakeDragEvents(editor); - blockIeDrop(editor); - }; - - return { - init: init - }; - } - ); - - define( - 'ephox.sugar.impl.Style', - - [ - - ], - - function() { - // some elements, such as mathml, don't have style attributes - var isSupported = function(dom) { - return dom.style !== undefined; - }; - - return { - isSupported: isSupported - }; - } - ); - define( - 'ephox.sugar.api.properties.Css', - - [ - 'ephox.katamari.api.Type', - 'ephox.katamari.api.Arr', - 'ephox.katamari.api.Obj', - 'ephox.katamari.api.Option', - 'ephox.sugar.api.properties.Attr', - 'ephox.sugar.api.node.Body', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.api.node.Node', - 'ephox.sugar.impl.Style', - 'ephox.katamari.api.Strings', - 'global!Error', - 'global!console', - 'global!window' - ], - - function(Type, Arr, Obj, Option, Attr, Body, Element, Node, Style, Strings, Error, console, window) { - var internalSet = function(dom, property, value) { - // This is going to hurt. Apologies. - // JQuery coerces numbers to pixels for certain property names, and other times lets numbers through. - // we're going to be explicit; strings only. - if (!Type.isString(value)) { - console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom); - throw new Error('CSS value must be a string: ' + value); - } - - // removed: support for dom().style[property] where prop is camel case instead of normal property name - if (Style.isSupported(dom)) dom.style.setProperty(property, value); - }; - - var internalRemove = function(dom, property) { - /* - * IE9 and above - MDN doesn't have details, but here's a couple of random internet claims - * - * http://help.dottoro.com/ljopsjck.php - * http://stackoverflow.com/a/7901886/7546 - */ - if (Style.isSupported(dom)) dom.style.removeProperty(property); - }; - - var set = function(element, property, value) { - var dom = element.dom(); - internalSet(dom, property, value); - }; - - var setAll = function(element, css) { - var dom = element.dom(); - - Obj.each(css, function(v, k) { - internalSet(dom, k, v); - }); - }; - - var setOptions = function(element, css) { - var dom = element.dom(); - - Obj.each(css, function(v, k) { - v.fold(function() { - internalRemove(dom, k); - }, function(value) { - internalSet(dom, k, value); - }); - }); - }; - - /* - * NOTE: For certain properties, this returns the "used value" which is subtly different to the "computed value" (despite calling getComputedStyle). - * Blame CSS 2.0. - * - * https://developer.mozilla.org/en-US/docs/Web/CSS/used_value - */ - var get = function(element, property) { - var dom = element.dom(); - /* - * IE9 and above per - * https://developer.mozilla.org/en/docs/Web/API/window.getComputedStyle - * - * Not in numerosity, because it doesn't memoize and looking this up dynamically in performance critical code would be horrendous. - * - * JQuery has some magic here for IE popups, but we don't really need that. - * It also uses element.ownerDocument.defaultView to handle iframes but that hasn't been required since FF 3.6. - */ - var styles = window.getComputedStyle(dom); - var r = styles.getPropertyValue(property); - - // jquery-ism: If r is an empty string, check that the element is not in a document. If it isn't, return the raw value. - // Turns out we do this a lot. - var v = (r === '' && !Body.inBody(element)) ? getUnsafeProperty(dom, property) : r; - - // undefined is the more appropriate value for JS. JQuery coerces to an empty string, but screw that! - return v === null ? undefined : v; - }; - - var getUnsafeProperty = function(dom, property) { - // removed: support for dom().style[property] where prop is camel case instead of normal property name - // empty string is what the browsers (IE11 and Chrome) return when the propertyValue doesn't exists. - return Style.isSupported(dom) ? dom.style.getPropertyValue(property) : ''; - }; - - /* - * Gets the raw value from the style attribute. Useful for retrieving "used values" from the DOM: - * https://developer.mozilla.org/en-US/docs/Web/CSS/used_value - * - * Returns NONE if the property isn't set, or the value is an empty string. - */ - var getRaw = function(element, property) { - var dom = element.dom(); - var raw = getUnsafeProperty(dom, property); - - return Option.from(raw).filter(function(r) { - return r.length > 0; - }); - }; - - var getAllRaw = function(element) { - var css = {}; - var dom = element.dom(); - - if (Style.isSupported(dom)) { - for (var i = 0; i < dom.style.length; i++) { - var ruleName = dom.style.item(i); - css[ruleName] = dom.style[ruleName]; - } - } - return css; - }; - - var isValidValue = function(tag, property, value) { - var element = Element.fromTag(tag); - set(element, property, value); - var style = getRaw(element, property); - return style.isSome(); - }; - - var remove = function(element, property) { - var dom = element.dom(); - - internalRemove(dom, property); - - if (Attr.has(element, 'style') && Strings.trim(Attr.get(element, 'style')) === '') { - // No more styles left, remove the style attribute as well - Attr.remove(element, 'style'); - } - }; - - var preserve = function(element, f) { - var oldStyles = Attr.get(element, 'style'); - var result = f(element); - var restore = oldStyles === undefined ? Attr.remove : Attr.set; - restore(element, 'style', oldStyles); - return result; - }; - - var copy = function(source, target) { - var sourceDom = source.dom(); - var targetDom = target.dom(); - if (Style.isSupported(sourceDom) && Style.isSupported(targetDom)) { - targetDom.style.cssText = sourceDom.style.cssText; - } - }; - - var reflow = function(e) { - /* NOTE: - * do not rely on this return value. - * It's here so the closure compiler doesn't optimise the property access away. - */ - return e.dom().offsetWidth; - }; - - var transferOne = function(source, destination, style) { - getRaw(source, style).each(function(value) { - // NOTE: We don't want to clobber any existing inline styles. - if (getRaw(destination, style).isNone()) set(destination, style, value); - }); - }; - - var transfer = function(source, destination, styles) { - if (!Node.isElement(source) || !Node.isElement(destination)) return; - Arr.each(styles, function(style) { - transferOne(source, destination, style); - }); - }; - - return { - copy: copy, - set: set, - preserve: preserve, - setAll: setAll, - setOptions: setOptions, - remove: remove, - get: get, - getRaw: getRaw, - getAllRaw: getAllRaw, - isValidValue: isValidValue, - reflow: reflow, - transfer: transfer - }; - } - ); - - /** - * EditorView.js - * - * Released under LGPL License. - * Copyright (c) 1999-2016 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.EditorView', [ - 'ephox.katamari.api.Fun', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.api.properties.Css', - 'ephox.sugar.api.search.Traverse' - ], - function(Fun, Element, Css, Traverse) { - var getProp = function(propName, elm) { - var rawElm = elm.dom(); - return rawElm[propName]; - }; - - var getComputedSizeProp = function(propName, elm) { - return parseInt(Css.get(elm, propName), 10); - }; - - var getClientWidth = Fun.curry(getProp, 'clientWidth'); - var getClientHeight = Fun.curry(getProp, 'clientHeight'); - var getMarginTop = Fun.curry(getComputedSizeProp, 'margin-top'); - var getMarginLeft = Fun.curry(getComputedSizeProp, 'margin-left'); - - var getBoundingClientRect = function(elm) { - return elm.dom().getBoundingClientRect(); - }; - - var isInsideElementContentArea = function(bodyElm, clientX, clientY) { - var clientWidth = getClientWidth(bodyElm); - var clientHeight = getClientHeight(bodyElm); - - return clientX >= 0 && clientY >= 0 && clientX <= clientWidth && clientY <= clientHeight; - }; - - var transpose = function(inline, elm, clientX, clientY) { - var clientRect = getBoundingClientRect(elm); - var deltaX = inline ? clientRect.left + elm.dom().clientLeft + getMarginLeft(elm) : 0; - var deltaY = inline ? clientRect.top + elm.dom().clientTop + getMarginTop(elm) : 0; - var x = clientX - deltaX; - var y = clientY - deltaY; - - return { - x: x, - y: y - }; - }; - - // Checks if the specified coordinate is within the visual content area excluding the scrollbars - var isXYInContentArea = function(editor, clientX, clientY) { - var bodyElm = Element.fromDom(editor.getBody()); - var targetElm = editor.inline ? bodyElm : Traverse.documentElement(bodyElm); - var transposedPoint = transpose(editor.inline, targetElm, clientX, clientY); - - return isInsideElementContentArea(targetElm, transposedPoint.x, transposedPoint.y); - }; - - return { - isXYInContentArea: isXYInContentArea - }; - } - ); - - /** - * SelectionOverrides.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module contains logic overriding the selection with keyboard/mouse - * around contentEditable=false regions. - * - * @example - * // Disable the default cE=false selection - * tinymce.activeEditor.on('ShowCaret BeforeObjectSelected', function(e) { - * e.preventDefault(); - * }); - * - * @private - * @class tinymce.SelectionOverrides - */ - define( - 'tinymce.core.SelectionOverrides', [ - 'tinymce.core.caret.CaretContainer', - 'tinymce.core.caret.CaretPosition', - 'tinymce.core.caret.CaretUtils', - 'tinymce.core.caret.CaretWalker', - 'tinymce.core.caret.FakeCaret', - 'tinymce.core.caret.LineUtils', - 'tinymce.core.dom.NodeType', - 'tinymce.core.dom.RangePoint', - 'tinymce.core.DragDropOverrides', - 'tinymce.core.EditorView', - 'tinymce.core.Env', - 'tinymce.core.keyboard.CefUtils', - 'tinymce.core.util.Delay', - 'tinymce.core.util.VK' - ], - function( - CaretContainer, CaretPosition, CaretUtils, CaretWalker, FakeCaret, LineUtils, NodeType, RangePoint, DragDropOverrides, EditorView, Env, CefUtils, Delay, - VK - ) { - var isContentEditableTrue = NodeType.isContentEditableTrue, - isContentEditableFalse = NodeType.isContentEditableFalse, - isAfterContentEditableFalse = CaretUtils.isAfterContentEditableFalse, - isBeforeContentEditableFalse = CaretUtils.isBeforeContentEditableFalse; - - function SelectionOverrides(editor) { - var rootNode = editor.getBody(); - var fakeCaret = new FakeCaret(editor.getBody(), isBlock), - realSelectionId = 'sel-' + editor.dom.uniqueId(), - selectedContentEditableNode; - - function isFakeSelectionElement(elm) { - return editor.dom.hasClass(elm, 'mce-offscreen-selection'); - } - - function getRealSelectionElement() { - var container = editor.dom.get(realSelectionId); - return container ? container.getElementsByTagName('*')[0] : container; - } - - function isBlock(node) { - return editor.dom.isBlock(node); - } - - function setRange(range) { - //console.log('setRange', range); - if (range) { - editor.selection.setRng(range); - } - } - - function getRange() { - return editor.selection.getRng(); - } - - function scrollIntoView(node, alignToTop) { - editor.selection.scrollIntoView(node, alignToTop); - } - - function showCaret(direction, node, before) { - var e; - - e = editor.fire('ShowCaret', { - target: node, - direction: direction, - before: before - }); - - if (e.isDefaultPrevented()) { - return null; - } - - scrollIntoView(node, direction === -1); - - return fakeCaret.show(before, node); - } - - function getNormalizedRangeEndPoint(direction, range) { - range = CaretUtils.normalizeRange(direction, rootNode, range); - - if (direction == -1) { - return CaretPosition.fromRangeStart(range); - } - - return CaretPosition.fromRangeEnd(range); - } - - function showBlockCaretContainer(blockCaretContainer) { - if (blockCaretContainer.hasAttribute('data-mce-caret')) { - CaretContainer.showCaretContainerBlock(blockCaretContainer); - setRange(getRange()); // Removes control rect on IE - scrollIntoView(blockCaretContainer[0]); - } - } - - function registerEvents() { - function getContentEditableRoot(node) { - var root = editor.getBody(); - - while (node && node != root) { - if (isContentEditableTrue(node) || isContentEditableFalse(node)) { - return node; - } - - node = node.parentNode; - } - - return null; - } - - // Some browsers (Chrome) lets you place the caret after a cE=false - // Make sure we render the caret container in this case - editor.on('mouseup', function(e) { - var range = getRange(); - - if (range.collapsed && EditorView.isXYInContentArea(editor, e.clientX, e.clientY)) { - setRange(CefUtils.renderCaretAtRange(editor, range)); - } - }); - - editor.on('click', function(e) { - var contentEditableRoot; - - contentEditableRoot = getContentEditableRoot(e.target); - if (contentEditableRoot) { - // Prevent clicks on links in a cE=false element - if (isContentEditableFalse(contentEditableRoot)) { - e.preventDefault(); - editor.focus(); - } - - // Removes fake selection if a cE=true is clicked within a cE=false like the toc title - if (isContentEditableTrue(contentEditableRoot)) { - if (editor.dom.isChildOf(contentEditableRoot, editor.selection.getNode())) { - removeContentEditableSelection(); - } - } - } - }); - - editor.on('blur NewBlock', function() { - removeContentEditableSelection(); - hideFakeCaret(); - }); - - function handleTouchSelect(editor) { - var moved = false; - - editor.on('touchstart', function() { - moved = false; - }); - - editor.on('touchmove', function() { - moved = true; - }); - - editor.on('touchend', function(e) { - var contentEditableRoot = getContentEditableRoot(e.target); - - if (isContentEditableFalse(contentEditableRoot)) { - if (!moved) { - e.preventDefault(); - setContentEditableSelection(CefUtils.selectNode(editor, contentEditableRoot)); - } - } - }); - } - - var hasNormalCaretPosition = function(elm) { - var caretWalker = new CaretWalker(elm); - - if (!elm.firstChild) { - return false; - } - - var startPos = CaretPosition.before(elm.firstChild); - var newPos = caretWalker.next(startPos); - - return newPos && !isBeforeContentEditableFalse(newPos) && !isAfterContentEditableFalse(newPos); - }; - - var isInSameBlock = function(node1, node2) { - var block1 = editor.dom.getParent(node1, editor.dom.isBlock); - var block2 = editor.dom.getParent(node2, editor.dom.isBlock); - return block1 === block2; - }; - - // Checks if the target node is in a block and if that block has a caret position better than the - // suggested caretNode this is to prevent the caret from being sucked in towards a cE=false block if - // they are adjacent on the vertical axis - var hasBetterMouseTarget = function(targetNode, caretNode) { - var targetBlock = editor.dom.getParent(targetNode, editor.dom.isBlock); - var caretBlock = editor.dom.getParent(caretNode, editor.dom.isBlock); - - return targetBlock && !isInSameBlock(targetBlock, caretBlock) && hasNormalCaretPosition(targetBlock); - }; - - handleTouchSelect(editor); - - editor.on('mousedown', function(e) { - var contentEditableRoot; - - if (EditorView.isXYInContentArea(editor, e.clientX, e.clientY) === false) { - return; - } - - contentEditableRoot = getContentEditableRoot(e.target); - if (contentEditableRoot) { - if (isContentEditableFalse(contentEditableRoot)) { - e.preventDefault(); - setContentEditableSelection(CefUtils.selectNode(editor, contentEditableRoot)); - } else { - removeContentEditableSelection(); - - // Check that we're not attempting a shift + click select within a contenteditable='true' element - if (!(isContentEditableTrue(contentEditableRoot) && e.shiftKey) && !RangePoint.isXYWithinRange(e.clientX, e.clientY, editor.selection.getRng())) { - editor.selection.placeCaretAt(e.clientX, e.clientY); - } - } - } else { - // Remove needs to be called here since the mousedown might alter the selection without calling selection.setRng - // and therefore not fire the AfterSetSelectionRange event. - removeContentEditableSelection(); - hideFakeCaret(); - - var caretInfo = LineUtils.closestCaret(rootNode, e.clientX, e.clientY); - if (caretInfo) { - if (!hasBetterMouseTarget(e.target, caretInfo.node)) { - e.preventDefault(); - editor.getBody().focus(); - setRange(showCaret(1, caretInfo.node, caretInfo.before)); - } - } - } - }); - - editor.on('keypress', function(e) { - if (VK.modifierPressed(e)) { - return; - } - - switch (e.keyCode) { - default: if (isContentEditableFalse(editor.selection.getNode())) { - e.preventDefault(); - } - break; - } - }); - - editor.on('getSelectionRange', function(e) { - var rng = e.range; - - if (selectedContentEditableNode) { - if (!selectedContentEditableNode.parentNode) { - selectedContentEditableNode = null; - return; - } - - rng = rng.cloneRange(); - rng.selectNode(selectedContentEditableNode); - e.range = rng; - } - }); - - editor.on('setSelectionRange', function(e) { - var rng; - - rng = setContentEditableSelection(e.range, e.forward); - if (rng) { - e.range = rng; - } - }); - - editor.on('AfterSetSelectionRange', function(e) { - var rng = e.range; - - if (!isRangeInCaretContainer(rng)) { - hideFakeCaret(); - } - - if (!isFakeSelectionElement(rng.startContainer.parentNode)) { - removeContentEditableSelection(); - } - }); - - editor.on('focus', function() { - // Make sure we have a proper fake caret on focus - Delay.setEditorTimeout(editor, function() { - editor.selection.setRng(CefUtils.renderRangeCaret(editor, editor.selection.getRng())); - }, 0); - }); - - editor.on('copy', function(e) { - var clipboardData = e.clipboardData; - - // Make sure we get proper html/text for the fake cE=false selection - // Doesn't work at all on Edge since it doesn't have proper clipboardData support - if (!e.isDefaultPrevented() && e.clipboardData && !Env.ie) { - var realSelectionElement = getRealSelectionElement(); - if (realSelectionElement) { - e.preventDefault(); - clipboardData.clearData(); - clipboardData.setData('text/html', realSelectionElement.outerHTML); - clipboardData.setData('text/plain', realSelectionElement.outerText); - } - } - }); - - DragDropOverrides.init(editor); - } - - function addCss() { - var styles = editor.contentStyles, - rootClass = '.mce-content-body'; - - styles.push(fakeCaret.getCss()); - styles.push( - rootClass + ' .mce-offscreen-selection {' + - 'position: absolute;' + - 'left: -9999999999px;' + - 'max-width: 1000000px;' + - '}' + - rootClass + ' *[contentEditable=false] {' + - 'cursor: default;' + - '}' + - rootClass + ' *[contentEditable=true] {' + - 'cursor: text;' + - '}' - ); - } - - function isWithinCaretContainer(node) { - return ( - CaretContainer.isCaretContainer(node) || - CaretContainer.startsWithCaretContainer(node) || - CaretContainer.endsWithCaretContainer(node) - ); - } - - function isRangeInCaretContainer(rng) { - return isWithinCaretContainer(rng.startContainer) || isWithinCaretContainer(rng.endContainer); - } - - function setContentEditableSelection(range, forward) { - var node, $ = editor.$, - dom = editor.dom, - $realSelectionContainer, sel, - startContainer, startOffset, endOffset, e, caretPosition, targetClone, origTargetClone; - - if (!range) { - return null; - } - - if (range.collapsed) { - if (!isRangeInCaretContainer(range)) { - if (forward === false) { - caretPosition = getNormalizedRangeEndPoint(-1, range); - - if (isContentEditableFalse(caretPosition.getNode(true))) { - return showCaret(-1, caretPosition.getNode(true), false); - } - - if (isContentEditableFalse(caretPosition.getNode())) { - return showCaret(-1, caretPosition.getNode(), !caretPosition.isAtEnd()); - } - } else { - caretPosition = getNormalizedRangeEndPoint(1, range); - - if (isContentEditableFalse(caretPosition.getNode())) { - return showCaret(1, caretPosition.getNode(), !caretPosition.isAtEnd()); - } - - if (isContentEditableFalse(caretPosition.getNode(true))) { - return showCaret(1, caretPosition.getNode(true), false); - } - } - } - - return null; - } - - startContainer = range.startContainer; - startOffset = range.startOffset; - endOffset = range.endOffset; - - // Normalizes <span cE=false>[</span>] to [<span cE=false></span>] - if (startContainer.nodeType == 3 && startOffset == 0 && isContentEditableFalse(startContainer.parentNode)) { - startContainer = startContainer.parentNode; - startOffset = dom.nodeIndex(startContainer); - startContainer = startContainer.parentNode; - } - - if (startContainer.nodeType != 1) { - return null; - } - - if (endOffset == startOffset + 1) { - node = startContainer.childNodes[startOffset]; - } - - if (!isContentEditableFalse(node)) { - return null; - } - - targetClone = origTargetClone = node.cloneNode(true); - e = editor.fire('ObjectSelected', { - target: node, - targetClone: targetClone - }); - if (e.isDefaultPrevented()) { - return null; - } - - targetClone = e.targetClone; - $realSelectionContainer = $('#' + realSelectionId); - if ($realSelectionContainer.length === 0) { - $realSelectionContainer = $( - '<div data-mce-bogus="all" class="mce-offscreen-selection"></div>' - ).attr('id', realSelectionId); - - $realSelectionContainer.appendTo(editor.getBody()); - } - - range = editor.dom.createRng(); - - // WHY is IE making things so hard! Copy on <i contentEditable="false">x</i> produces: <em>x</em> - // This is a ridiculous hack where we place the selection from a block over the inline element - // so that just the inline element is copied as is and not converted. - if (targetClone === origTargetClone && Env.ie) { - $realSelectionContainer.empty().append('<p style="font-size: 0" data-mce-bogus="all">\u00a0</p>').append(targetClone); - range.setStartAfter($realSelectionContainer[0].firstChild.firstChild); - range.setEndAfter(targetClone); - } else { - $realSelectionContainer.empty().append('\u00a0').append(targetClone).append('\u00a0'); - range.setStart($realSelectionContainer[0].firstChild, 1); - range.setEnd($realSelectionContainer[0].lastChild, 0); - } - - $realSelectionContainer.css({ - top: dom.getPos(node, editor.getBody()).y - }); - - $realSelectionContainer[0].focus(); - sel = editor.selection.getSel(); - sel.removeAllRanges(); - sel.addRange(range); - - editor.$('*[data-mce-selected]').removeAttr('data-mce-selected'); - node.setAttribute('data-mce-selected', 1); - selectedContentEditableNode = node; - hideFakeCaret(); - - return range; - } - - function removeContentEditableSelection() { - if (selectedContentEditableNode) { - selectedContentEditableNode.removeAttribute('data-mce-selected'); - editor.$('#' + realSelectionId).remove(); - selectedContentEditableNode = null; - } - } - - function destroy() { - fakeCaret.destroy(); - selectedContentEditableNode = null; - } - - function hideFakeCaret() { - fakeCaret.hide(); - } - - if (Env.ceFalse) { - registerEvents(); - addCss(); - } - - return { - showCaret: showCaret, - showBlockCaretContainer: showBlockCaretContainer, - hideFakeCaret: hideFakeCaret, - destroy: destroy - }; - } - - return SelectionOverrides; - } - ); - - /** - * NodePath.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Handles paths of nodes within an element. - * - * @private - * @class tinymce.dom.NodePath - */ - define( - 'tinymce.core.dom.NodePath', [ - "tinymce.core.dom.DOMUtils" - ], - function(DOMUtils) { - function create(rootNode, targetNode, normalized) { - var path = []; - - for (; targetNode && targetNode != rootNode; targetNode = targetNode.parentNode) { - path.push(DOMUtils.nodeIndex(targetNode, normalized)); - } - - return path; - } - - function resolve(rootNode, path) { - var i, node, children; - - for (node = rootNode, i = path.length - 1; i >= 0; i--) { - children = node.childNodes; - - if (path[i] > children.length - 1) { - return null; - } - - node = children[path[i]]; - } - - return node; - } - - return { - create: create, - resolve: resolve - }; - } - ); - /** - * Quirks.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - * - * @ignore-file - */ - - /** - * This file includes fixes for various browser quirks it's made to make it easy to add/remove browser specific fixes. - * - * @private - * @class tinymce.util.Quirks - */ - define( - 'tinymce.core.util.Quirks', [ - "tinymce.core.util.VK", - "tinymce.core.dom.RangeUtils", - "tinymce.core.dom.TreeWalker", - "tinymce.core.dom.NodePath", - "tinymce.core.html.Node", - "tinymce.core.html.Entities", - "tinymce.core.Env", - "tinymce.core.util.Tools", - "tinymce.core.util.Delay", - "tinymce.core.caret.CaretContainer", - "tinymce.core.caret.CaretPosition", - "tinymce.core.caret.CaretWalker" - ], - function(VK, RangeUtils, TreeWalker, NodePath, Node, Entities, Env, Tools, Delay, CaretContainer, CaretPosition, CaretWalker) { - return function(editor) { - var each = Tools.each; - var BACKSPACE = VK.BACKSPACE, - DELETE = VK.DELETE, - dom = editor.dom, - selection = editor.selection, - settings = editor.settings, - parser = editor.parser, - serializer = editor.serializer; - var isGecko = Env.gecko, - isIE = Env.ie, - isWebKit = Env.webkit; - var mceInternalUrlPrefix = 'data:text/mce-internal,'; - var mceInternalDataType = isIE ? 'Text' : 'URL'; - - /** - * Executes a command with a specific state this can be to enable/disable browser editing features. - */ - function setEditorCommandState(cmd, state) { - try { - editor.getDoc().execCommand(cmd, false, state); - } catch (ex) { - // Ignore - } - } - - /** - * Returns current IE document mode. - */ - function getDocumentMode() { - var documentMode = editor.getDoc().documentMode; - - return documentMode ? documentMode : 6; - } - - /** - * Returns true/false if the event is prevented or not. - * - * @private - * @param {Event} e Event object. - * @return {Boolean} true/false if the event is prevented or not. - */ - function isDefaultPrevented(e) { - return e.isDefaultPrevented(); - } - - /** - * Sets Text/URL data on the event's dataTransfer object to a special data:text/mce-internal url. - * This is to workaround the inability to set custom contentType on IE and Safari. - * The editor's selected content is encoded into this url so drag and drop between editors will work. - * - * @private - * @param {DragEvent} e Event object - */ - function setMceInternalContent(e) { - var selectionHtml, internalContent; - - if (e.dataTransfer) { - if (editor.selection.isCollapsed() && e.target.tagName == 'IMG') { - selection.select(e.target); - } - - selectionHtml = editor.selection.getContent(); - - // Safari/IE doesn't support custom dataTransfer items so we can only use URL and Text - if (selectionHtml.length > 0) { - internalContent = mceInternalUrlPrefix + escape(editor.id) + ',' + escape(selectionHtml); - e.dataTransfer.setData(mceInternalDataType, internalContent); - } - } - } - - /** - * Gets content of special data:text/mce-internal url on the event's dataTransfer object. - * This is to workaround the inability to set custom contentType on IE and Safari. - * The editor's selected content is encoded into this url so drag and drop between editors will work. - * - * @private - * @param {DragEvent} e Event object - * @returns {String} mce-internal content - */ - function getMceInternalContent(e) { - var internalContent; - - if (e.dataTransfer) { - internalContent = e.dataTransfer.getData(mceInternalDataType); - - if (internalContent && internalContent.indexOf(mceInternalUrlPrefix) >= 0) { - internalContent = internalContent.substr(mceInternalUrlPrefix.length).split(','); - - return { - id: unescape(internalContent[0]), - html: unescape(internalContent[1]) - }; - } - } - - return null; - } - - /** - * Inserts contents using the paste clipboard command if it's available if it isn't it will fallback - * to the core command. - * - * @private - * @param {String} content Content to insert at selection. - * @param {Boolean} internal State if the paste is to be considered internal or external. - */ - function insertClipboardContents(content, internal) { - if (editor.queryCommandSupported('mceInsertClipboardContent')) { - editor.execCommand('mceInsertClipboardContent', false, { - content: content, - internal: internal - }); - } else { - editor.execCommand('mceInsertContent', false, content); - } - } - - /** - * Makes sure that the editor body becomes empty when backspace or delete is pressed in empty editors. - * - * For example: - * <p><b>|</b></p> - * - * Or: - * <h1>|</h1> - * - * Or: - * [<h1></h1>] - */ - function emptyEditorWhenDeleting() { - function serializeRng(rng) { - var body = dom.create("body"); - var contents = rng.cloneContents(); - body.appendChild(contents); - return selection.serializer.serialize(body, { - format: 'html' - }); - } - - function allContentsSelected(rng) { - if (!rng.setStart) { - if (rng.item) { - return false; - } - - var bodyRng = rng.duplicate(); - bodyRng.moveToElementText(editor.getBody()); - return RangeUtils.compareRanges(rng, bodyRng); - } - - var selection = serializeRng(rng); - - var allRng = dom.createRng(); - allRng.selectNode(editor.getBody()); - - var allSelection = serializeRng(allRng); - return selection === allSelection; - } - - editor.on('keydown', function(e) { - var keyCode = e.keyCode, - isCollapsed, body; - - // Empty the editor if it's needed for example backspace at <p><b>|</b></p> - if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) { - isCollapsed = editor.selection.isCollapsed(); - body = editor.getBody(); - - // Selection is collapsed but the editor isn't empty - if (isCollapsed && !dom.isEmpty(body)) { - return; - } - - // Selection isn't collapsed but not all the contents is selected - if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) { - return; - } - - // Manually empty the editor - e.preventDefault(); - editor.setContent(''); - - if (body.firstChild && dom.isBlock(body.firstChild)) { - editor.selection.setCursorLocation(body.firstChild, 0); - } else { - editor.selection.setCursorLocation(body, 0); - } - - editor.nodeChanged(); - } - }); - } - - /** - * WebKit doesn't select all the nodes in the body when you press Ctrl+A. - * IE selects more than the contents <body>[<p>a</p>]</body> instead of <body><p>[a]</p]</body> see bug #6438 - * This selects the whole body so that backspace/delete logic will delete everything - */ - function selectAll() { - editor.shortcuts.add('meta+a', null, 'SelectAll'); - } - - /** - * WebKit has a weird issue where it some times fails to properly convert keypresses to input method keystrokes. - * The IME on Mac doesn't initialize when it doesn't fire a proper focus event. - * - * This seems to happen when the user manages to click the documentElement element then the window doesn't get proper focus until - * you enter a character into the editor. - * - * It also happens when the first focus in made to the body. - * - * See: https://bugs.webkit.org/show_bug.cgi?id=83566 - */ - function inputMethodFocus() { - if (!editor.settings.content_editable) { - // Case 1 IME doesn't initialize if you focus the document - // Disabled since it was interferring with the cE=false logic - // Also coultn't reproduce the issue on Safari 9 - /*dom.bind(editor.getDoc(), 'focusin', function() { - selection.setRng(selection.getRng()); - });*/ - - // Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event - // Needs to be both down/up due to weird rendering bug on Chrome Windows - dom.bind(editor.getDoc(), 'mousedown mouseup', function(e) { - var rng; - - if (e.target == editor.getDoc().documentElement) { - rng = selection.getRng(); - editor.getBody().focus(); - - if (e.type == 'mousedown') { - if (CaretContainer.isCaretContainer(rng.startContainer)) { - return; - } - - // Edge case for mousedown, drag select and mousedown again within selection on Chrome Windows to render caret - selection.placeCaretAt(e.clientX, e.clientY); - } else { - selection.setRng(rng); - } - } - }); - } - } - - /** - * Backspacing in FireFox/IE from a paragraph into a horizontal rule results in a floating text node because the - * browser just deletes the paragraph - the browser fails to merge the text node with a horizontal rule so it is - * left there. TinyMCE sees a floating text node and wraps it in a paragraph on the key up event (ForceBlocks.js - * addRootBlocks), meaning the action does nothing. With this code, FireFox/IE matche the behaviour of other - * browsers. - * - * It also fixes a bug on Firefox where it's impossible to delete HR elements. - */ - function removeHrOnBackspace() { - editor.on('keydown', function(e) { - if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) { - // Check if there is any HR elements this is faster since getRng on IE 7 & 8 is slow - if (!editor.getBody().getElementsByTagName('hr').length) { - return; - } - - if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) { - var node = selection.getNode(); - var previousSibling = node.previousSibling; - - if (node.nodeName == 'HR') { - dom.remove(node); - e.preventDefault(); - return; - } - - if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") { - dom.remove(previousSibling); - e.preventDefault(); - } - } - } - }); - } - - /** - * Firefox 3.x has an issue where the body element won't get proper focus if you click out - * side it's rectangle. - */ - function focusBody() { - // Fix for a focus bug in FF 3.x where the body element - // wouldn't get proper focus if the user clicked on the HTML element - if (!window.Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4 - editor.on('mousedown', function(e) { - if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") { - var body = editor.getBody(); - - // Blur the body it's focused but not correctly focused - body.blur(); - - // Refocus the body after a little while - Delay.setEditorTimeout(editor, function() { - body.focus(); - }); - } - }); - } - } - - /** - * WebKit has a bug where it isn't possible to select image, hr or anchor elements - * by clicking on them so we need to fake that. - */ - function selectControlElements() { - editor.on('click', function(e) { - var target = e.target; - - // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 - // WebKit can't even do simple things like selecting an image - // Needs to be the setBaseAndExtend or it will fail to select floated images - if (/^(IMG|HR)$/.test(target.nodeName) && dom.getContentEditableParent(target) !== "false") { - e.preventDefault(); - editor.selection.select(target); - editor.nodeChanged(); - } - - if (target.nodeName == 'A' && dom.hasClass(target, 'mce-item-anchor')) { - e.preventDefault(); - selection.select(target); - } - }); - } - - /** - * Fixes a Gecko bug where the style attribute gets added to the wrong element when deleting between two block elements. - * - * Fixes do backspace/delete on this: - * <p>bla[ck</p><p style="color:red">r]ed</p> - * - * Would become: - * <p>bla|ed</p> - * - * Instead of: - * <p style="color:red">bla|ed</p> - */ - function removeStylesWhenDeletingAcrossBlockElements() { - function getAttributeApplyFunction() { - var template = dom.getAttribs(selection.getStart().cloneNode(false)); - - return function() { - var target = selection.getStart(); - - if (target !== editor.getBody()) { - dom.setAttrib(target, "style", null); - - each(template, function(attr) { - target.setAttributeNode(attr.cloneNode(true)); - }); - } - }; - } - - function isSelectionAcrossElements() { - return !selection.isCollapsed() && - dom.getParent(selection.getStart(), dom.isBlock) != dom.getParent(selection.getEnd(), dom.isBlock); - } - - editor.on('keypress', function(e) { - var applyAttributes; - - if (!isDefaultPrevented(e) && (e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) { - applyAttributes = getAttributeApplyFunction(); - editor.getDoc().execCommand('delete', false, null); - applyAttributes(); - e.preventDefault(); - return false; - } - }); - - dom.bind(editor.getDoc(), 'cut', function(e) { - var applyAttributes; - - if (!isDefaultPrevented(e) && isSelectionAcrossElements()) { - applyAttributes = getAttributeApplyFunction(); - - Delay.setEditorTimeout(editor, function() { - applyAttributes(); - }); - } - }); - } - - /** - * Screen readers on IE needs to have the role application set on the body. - */ - function ensureBodyHasRoleApplication() { - document.body.setAttribute("role", "application"); - } - - /** - * Backspacing into a table behaves differently depending upon browser type. - * Therefore, disable Backspace when cursor immediately follows a table. - */ - function disableBackspaceIntoATable() { - editor.on('keydown', function(e) { - if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) { - if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) { - var previousSibling = selection.getNode().previousSibling; - if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") { - e.preventDefault(); - return false; - } - } - } - }); - } - - /** - * Old IE versions can't properly render BR elements in PRE tags white in contentEditable mode. So this - * logic adds a \n before the BR so that it will get rendered. - */ - function addNewLinesBeforeBrInPre() { - // IE8+ rendering mode does the right thing with BR in PRE - if (getDocumentMode() > 7) { - return; - } - - // Enable display: none in area and add a specific class that hides all BR elements in PRE to - // avoid the caret from getting stuck at the BR elements while pressing the right arrow key - setEditorCommandState('RespectVisibilityInDesign', true); - editor.contentStyles.push('.mceHideBrInPre pre br {display: none}'); - dom.addClass(editor.getBody(), 'mceHideBrInPre'); - - // Adds a \n before all BR elements in PRE to get them visual - parser.addNodeFilter('pre', function(nodes) { - var i = nodes.length, - brNodes, j, brElm, sibling; - - while (i--) { - brNodes = nodes[i].getAll('br'); - j = brNodes.length; - while (j--) { - brElm = brNodes[j]; - - // Add \n before BR in PRE elements on older IE:s so the new lines get rendered - sibling = brElm.prev; - if (sibling && sibling.type === 3 && sibling.value.charAt(sibling.value - 1) != '\n') { - sibling.value += '\n'; - } else { - brElm.parent.insert(new Node('#text', 3), brElm, true).value = '\n'; - } - } - } - }); - - // Removes any \n before BR elements in PRE since other browsers and in contentEditable=false mode they will be visible - serializer.addNodeFilter('pre', function(nodes) { - var i = nodes.length, - brNodes, j, brElm, sibling; - - while (i--) { - brNodes = nodes[i].getAll('br'); - j = brNodes.length; - while (j--) { - brElm = brNodes[j]; - sibling = brElm.prev; - if (sibling && sibling.type == 3) { - sibling.value = sibling.value.replace(/\r?\n$/, ''); - } - } - } - }); - } - - /** - * Moves style width/height to attribute width/height when the user resizes an image on IE. - */ - function removePreSerializedStylesWhenSelectingControls() { - dom.bind(editor.getBody(), 'mouseup', function() { - var value, node = selection.getNode(); - - // Moved styles to attributes on IMG eements - if (node.nodeName == 'IMG') { - // Convert style width to width attribute - if ((value = dom.getStyle(node, 'width'))) { - dom.setAttrib(node, 'width', value.replace(/[^0-9%]+/g, '')); - dom.setStyle(node, 'width', ''); - } - - // Convert style height to height attribute - if ((value = dom.getStyle(node, 'height'))) { - dom.setAttrib(node, 'height', value.replace(/[^0-9%]+/g, '')); - dom.setStyle(node, 'height', ''); - } - } - }); - } - - /** - * Removes a blockquote when backspace is pressed at the beginning of it. - * - * For example: - * <blockquote><p>|x</p></blockquote> - * - * Becomes: - * <p>|x</p> - */ - function removeBlockQuoteOnBackSpace() { - // Add block quote deletion handler - editor.on('keydown', function(e) { - var rng, container, offset, root, parent; - - if (isDefaultPrevented(e) || e.keyCode != VK.BACKSPACE) { - return; - } - - rng = selection.getRng(); - container = rng.startContainer; - offset = rng.startOffset; - root = dom.getRoot(); - parent = container; - - if (!rng.collapsed || offset !== 0) { - return; - } - - while (parent && parent.parentNode && parent.parentNode.firstChild == parent && parent.parentNode != root) { - parent = parent.parentNode; - } - - // Is the cursor at the beginning of a blockquote? - if (parent.tagName === 'BLOCKQUOTE') { - // Remove the blockquote - editor.formatter.toggle('blockquote', null, parent); - - // Move the caret to the beginning of container - rng = dom.createRng(); - rng.setStart(container, 0); - rng.setEnd(container, 0); - selection.setRng(rng); - } - }); - } - - /** - * Sets various Gecko editing options on mouse down and before a execCommand to disable inline table editing that is broken etc. - */ - function setGeckoEditingOptions() { - function setOpts() { - refreshContentEditable(); - - setEditorCommandState("StyleWithCSS", false); - setEditorCommandState("enableInlineTableEditing", false); - - if (!settings.object_resizing) { - setEditorCommandState("enableObjectResizing", false); - } - } - - if (!settings.readonly) { - editor.on('BeforeExecCommand MouseDown', setOpts); - } - } - - /** - * Fixes a gecko link bug, when a link is placed at the end of block elements there is - * no way to move the caret behind the link. This fix adds a bogus br element after the link. - * - * For example this: - * <p><b><a href="#">x</a></b></p> - * - * Becomes this: - * <p><b><a href="#">x</a></b><br></p> - */ - function addBrAfterLastLinks() { - function fixLinks() { - each(dom.select('a'), function(node) { - var parentNode = node.parentNode, - root = dom.getRoot(); - - if (parentNode.lastChild === node) { - while (parentNode && !dom.isBlock(parentNode)) { - if (parentNode.parentNode.lastChild !== parentNode || parentNode === root) { - return; - } - - parentNode = parentNode.parentNode; - } - - dom.add(parentNode, 'br', { - 'data-mce-bogus': 1 - }); - } - }); - } - - editor.on('SetContent ExecCommand', function(e) { - if (e.type == "setcontent" || e.command === 'mceInsertLink') { - fixLinks(); - } - }); - } - - /** - * WebKit will produce DIV elements here and there by default. But since TinyMCE uses paragraphs by - * default we want to change that behavior. - */ - function setDefaultBlockType() { - if (settings.forced_root_block) { - editor.on('init', function() { - setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block); - }); - } - } - - /** - * Deletes the selected image on IE instead of navigating to previous page. - */ - function deleteControlItemOnBackSpace() { - editor.on('keydown', function(e) { - var rng; - - if (!isDefaultPrevented(e) && e.keyCode == BACKSPACE) { - rng = editor.getDoc().selection.createRange(); - if (rng && rng.item) { - e.preventDefault(); - editor.undoManager.beforeChange(); - dom.remove(rng.item(0)); - editor.undoManager.add(); - } - } - }); - } - - /** - * IE10 doesn't properly render block elements with the right height until you add contents to them. - * This fixes that by adding a padding-right to all empty text block elements. - * See: https://connect.microsoft.com/IE/feedback/details/743881 - */ - function renderEmptyBlocksFix() { - var emptyBlocksCSS; - - // IE10+ - if (getDocumentMode() >= 10) { - emptyBlocksCSS = ''; - each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) { - emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty'; - }); - - editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}'); - } - } - - /** - * Old IE versions can't retain contents within noscript elements so this logic will store the contents - * as a attribute and the insert that value as it's raw text when the DOM is serialized. - */ - function keepNoScriptContents() { - if (getDocumentMode() < 9) { - parser.addNodeFilter('noscript', function(nodes) { - var i = nodes.length, - node, textNode; - - while (i--) { - node = nodes[i]; - textNode = node.firstChild; - - if (textNode) { - node.attr('data-mce-innertext', textNode.value); - } - } - }); - - serializer.addNodeFilter('noscript', function(nodes) { - var i = nodes.length, - node, textNode, value; - - while (i--) { - node = nodes[i]; - textNode = nodes[i].firstChild; - - if (textNode) { - textNode.value = Entities.decode(textNode.value); - } else { - // Old IE can't retain noscript value so an attribute is used to store it - value = node.attributes.map['data-mce-innertext']; - if (value) { - node.attr('data-mce-innertext', null); - textNode = new Node('#text', 3); - textNode.value = value; - textNode.raw = true; - node.append(textNode); - } - } - } - }); - } - } - - /** - * IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode. - */ - function fixCaretSelectionOfDocumentElementOnIe() { - var doc = dom.doc, - body = doc.body, - started, startRng, htmlElm; - - // Return range from point or null if it failed - function rngFromPoint(x, y) { - var rng = body.createTextRange(); - - try { - rng.moveToPoint(x, y); - } catch (ex) { - // IE sometimes throws and exception, so lets just ignore it - rng = null; - } - - return rng; - } - - // Fires while the selection is changing - function selectionChange(e) { - var pointRng; - - // Check if the button is down or not - if (e.button) { - // Create range from mouse position - pointRng = rngFromPoint(e.x, e.y); - - if (pointRng) { - // Check if pointRange is before/after selection then change the endPoint - if (pointRng.compareEndPoints('StartToStart', startRng) > 0) { - pointRng.setEndPoint('StartToStart', startRng); - } else { - pointRng.setEndPoint('EndToEnd', startRng); - } - - pointRng.select(); - } - } else { - endSelection(); - } - } - - // Removes listeners - function endSelection() { - var rng = doc.selection.createRange(); - - // If the range is collapsed then use the last start range - if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0) { - startRng.select(); - } - - dom.unbind(doc, 'mouseup', endSelection); - dom.unbind(doc, 'mousemove', selectionChange); - startRng = started = 0; - } - - // Make HTML element unselectable since we are going to handle selection by hand - doc.documentElement.unselectable = true; - - // Detect when user selects outside BODY - dom.bind(doc, 'mousedown contextmenu', function(e) { - if (e.target.nodeName === 'HTML') { - if (started) { - endSelection(); - } - - // Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML - htmlElm = doc.documentElement; - if (htmlElm.scrollHeight > htmlElm.clientHeight) { - return; - } - - started = 1; - // Setup start position - startRng = rngFromPoint(e.x, e.y); - if (startRng) { - // Listen for selection change events - dom.bind(doc, 'mouseup', endSelection); - dom.bind(doc, 'mousemove', selectionChange); - - dom.getRoot().focus(); - startRng.select(); - } - } - }); - } - - /** - * Fixes selection issues where the caret can be placed between two inline elements like <b>a</b>|<b>b</b> - * this fix will lean the caret right into the closest inline element. - */ - function normalizeSelection() { - // Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i> except for Ctrl+A since it selects everything - editor.on('keyup focusin mouseup', function(e) { - if (e.keyCode != 65 || !VK.metaKeyPressed(e)) { - selection.normalize(); - } - }, true); - } - - /** - * Forces Gecko to render a broken image icon if it fails to load an image. - */ - function showBrokenImageIcon() { - editor.contentStyles.push( - 'img:-moz-broken {' + - '-moz-force-broken-image-icon:1;' + - 'min-width:24px;' + - 'min-height:24px' + - '}' - ); - } - - /** - * iOS has a bug where it's impossible to type if the document has a touchstart event - * bound and the user touches the document while having the on screen keyboard visible. - * - * The touch event moves the focus to the parent document while having the caret inside the iframe - * this fix moves the focus back into the iframe document. - */ - function restoreFocusOnKeyDown() { - if (!editor.inline) { - editor.on('keydown', function() { - if (document.activeElement == document.body) { - editor.getWin().focus(); - } - }); - } - } - - /** - * IE 11 has an annoying issue where you can't move focus into the editor - * by clicking on the white area HTML element. We used to be able to to fix this with - * the fixCaretSelectionOfDocumentElementOnIe fix. But since M$ removed the selection - * object it's not possible anymore. So we need to hack in a ungly CSS to force the - * body to be at least 150px. If the user clicks the HTML element out side this 150px region - * we simply move the focus into the first paragraph. Not ideal since you loose the - * positioning of the caret but goot enough for most cases. - */ - function bodyHeight() { - if (!editor.inline) { - editor.contentStyles.push('body {min-height: 150px}'); - editor.on('click', function(e) { - var rng; - - if (e.target.nodeName == 'HTML') { - // Edge seems to only need focus if we set the range - // the caret will become invisible and moved out of the iframe!! - if (Env.ie > 11) { - editor.getBody().focus(); - return; - } - - // Need to store away non collapsed ranges since the focus call will mess that up see #7382 - rng = editor.selection.getRng(); - editor.getBody().focus(); - editor.selection.setRng(rng); - editor.selection.normalize(); - editor.nodeChanged(); - } - }); - } - } - - /** - * Firefox on Mac OS will move the browser back to the previous page if you press CMD+Left arrow. - * You might then loose all your work so we need to block that behavior and replace it with our own. - */ - function blockCmdArrowNavigation() { - if (Env.mac) { - editor.on('keydown', function(e) { - if (VK.metaKeyPressed(e) && !e.shiftKey && (e.keyCode == 37 || e.keyCode == 39)) { - e.preventDefault(); - editor.selection.getSel().modify('move', e.keyCode == 37 ? 'backward' : 'forward', 'lineboundary'); - } - }); - } - } - - /** - * Disables the autolinking in IE 9+ this is then re-enabled by the autolink plugin. - */ - function disableAutoUrlDetect() { - setEditorCommandState("AutoUrlDetect", false); - } - - /** - * iOS 7.1 introduced two new bugs: - * 1) It's possible to open links within a contentEditable area by clicking on them. - * 2) If you hold down the finger it will display the link/image touch callout menu. - */ - function tapLinksAndImages() { - editor.on('click', function(e) { - var elm = e.target; - - do { - if (elm.tagName === 'A') { - e.preventDefault(); - return; - } - } while ((elm = elm.parentNode)); - }); - - editor.contentStyles.push('.mce-content-body {-webkit-touch-callout: none}'); - } - - /** - * iOS Safari and possible other browsers have a bug where it won't fire - * a click event when a contentEditable is focused. This function fakes click events - * by using touchstart/touchend and measuring the time and distance travelled. - */ - /* - function touchClickEvent() { - editor.on('touchstart', function(e) { - var elm, time, startTouch, changedTouches; - - elm = e.target; - time = new Date().getTime(); - changedTouches = e.changedTouches; - - if (!changedTouches || changedTouches.length > 1) { - return; - } - - startTouch = changedTouches[0]; - - editor.once('touchend', function(e) { - var endTouch = e.changedTouches[0], args; - - if (new Date().getTime() - time > 500) { - return; - } - - if (Math.abs(startTouch.clientX - endTouch.clientX) > 5) { - return; - } - - if (Math.abs(startTouch.clientY - endTouch.clientY) > 5) { - return; - } - - args = { - target: elm - }; - - each('pageX pageY clientX clientY screenX screenY'.split(' '), function(key) { - args[key] = endTouch[key]; - }); - - args = editor.fire('click', args); - - if (!args.isDefaultPrevented()) { - // iOS WebKit can't place the caret properly once - // you bind touch events so we need to do this manually - // TODO: Expand to the closest word? Touble tap still works. - editor.selection.placeCaretAt(endTouch.clientX, endTouch.clientY); - editor.nodeChanged(); - } - }); - }); - } - */ - - /** - * WebKit has a bug where it will allow forms to be submitted if they are inside a contentEditable element. - * For example this: <form><button></form> - */ - function blockFormSubmitInsideEditor() { - editor.on('init', function() { - editor.dom.bind(editor.getBody(), 'submit', function(e) { - e.preventDefault(); - }); - }); - } - - /** - * Sometimes WebKit/Blink generates BR elements with the Apple-interchange-newline class. - * - * Scenario: - * 1) Create a table 2x2. - * 2) Select and copy cells A2-B2. - * 3) Paste and it will add BR element to table cell. - */ - function removeAppleInterchangeBrs() { - parser.addNodeFilter('br', function(nodes) { - var i = nodes.length; - - while (i--) { - if (nodes[i].attr('class') == 'Apple-interchange-newline') { - nodes[i].remove(); - } - } - }); - } - - /** - * IE cannot set custom contentType's on drag events, and also does not properly drag/drop between - * editors. This uses a special data:text/mce-internal URL to pass data when drag/drop between editors. - */ - function ieInternalDragAndDrop() { - editor.on('dragstart', function(e) { - setMceInternalContent(e); - }); - - editor.on('drop', function(e) { - if (!isDefaultPrevented(e)) { - var internalContent = getMceInternalContent(e); - - if (internalContent && internalContent.id != editor.id) { - e.preventDefault(); - - var rng = RangeUtils.getCaretRangeFromPoint(e.x, e.y, editor.getDoc()); - selection.setRng(rng); - insertClipboardContents(internalContent.html, true); - } - } - }); - } - - function refreshContentEditable() { - // No-op since Mozilla seems to have fixed the caret repaint issues - } - - function isHidden() { - var sel; - - if (!isGecko || editor.removed) { - return 0; - } - - // Weird, wheres that cursor selection? - sel = editor.selection.getSel(); - return (!sel || !sel.rangeCount || sel.rangeCount === 0); - } - - /** - * Properly empties the editor if all contents is selected and deleted this to - * prevent empty paragraphs from being produced at beginning/end of contents. - */ - function emptyEditorOnDeleteEverything() { - function isEverythingSelected(editor) { - var caretWalker = new CaretWalker(editor.getBody()); - var rng = editor.selection.getRng(); - var startCaretPos = CaretPosition.fromRangeStart(rng); - var endCaretPos = CaretPosition.fromRangeEnd(rng); - var prev = caretWalker.prev(startCaretPos); - var next = caretWalker.next(endCaretPos); - - return !editor.selection.isCollapsed() && - (!prev || (prev.isAtStart() && startCaretPos.isEqual(prev))) && - (!next || (next.isAtEnd() && startCaretPos.isEqual(next))); - } - - // Type over case delete and insert this won't cover typeover with a IME but at least it covers the common case - editor.on('keypress', function(e) { - if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode > 31 && !VK.metaKeyPressed(e)) { - if (isEverythingSelected(editor)) { - e.preventDefault(); - editor.setContent(String.fromCharCode(e.charCode)); - editor.selection.select(editor.getBody(), true); - editor.selection.collapse(false); - editor.nodeChanged(); - } - } - }); - - editor.on('keydown', function(e) { - var keyCode = e.keyCode; - - if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) { - if (isEverythingSelected(editor)) { - e.preventDefault(); - editor.setContent(''); - editor.nodeChanged(); - } - } - }); - } - - // All browsers - removeBlockQuoteOnBackSpace(); - emptyEditorWhenDeleting(); - - // Windows phone will return a range like [body, 0] on mousedown so - // it will always normalize to the wrong location - if (!Env.windowsPhone) { - normalizeSelection(); - } - - // WebKit - if (isWebKit) { - emptyEditorOnDeleteEverything(); - inputMethodFocus(); - selectControlElements(); - setDefaultBlockType(); - blockFormSubmitInsideEditor(); - disableBackspaceIntoATable(); - removeAppleInterchangeBrs(); - - //touchClickEvent(); - - // iOS - if (Env.iOS) { - restoreFocusOnKeyDown(); - bodyHeight(); - tapLinksAndImages(); - } else { - selectAll(); - } - } - - // IE - if (isIE && Env.ie < 11) { - removeHrOnBackspace(); - ensureBodyHasRoleApplication(); - addNewLinesBeforeBrInPre(); - removePreSerializedStylesWhenSelectingControls(); - deleteControlItemOnBackSpace(); - renderEmptyBlocksFix(); - keepNoScriptContents(); - fixCaretSelectionOfDocumentElementOnIe(); - } - - if (Env.ie >= 11) { - bodyHeight(); - disableBackspaceIntoATable(); - } - - if (Env.ie) { - selectAll(); - disableAutoUrlDetect(); - ieInternalDragAndDrop(); - } - - // Gecko - if (isGecko) { - emptyEditorOnDeleteEverything(); - removeHrOnBackspace(); - focusBody(); - removeStylesWhenDeletingAcrossBlockElements(); - setGeckoEditingOptions(); - addBrAfterLastLinks(); - showBrokenImageIcon(); - blockCmdArrowNavigation(); - disableBackspaceIntoATable(); - } - - return { - refreshContentEditable: refreshContentEditable, - isHidden: isHidden - }; - }; - } - ); - - /** - * InitContentBody.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.init.InitContentBody', [ - 'global!document', - 'global!window', - 'tinymce.core.caret.CaretContainerInput', - 'tinymce.core.dom.DOMUtils', - 'tinymce.core.dom.Selection', - 'tinymce.core.dom.Serializer', - 'tinymce.core.EditorUpload', - 'tinymce.core.ErrorReporter', - 'tinymce.core.ForceBlocks', - 'tinymce.core.Formatter', - 'tinymce.core.html.DomParser', - 'tinymce.core.html.Node', - 'tinymce.core.html.Schema', - 'tinymce.core.keyboard.KeyboardOverrides', - 'tinymce.core.NodeChange', - 'tinymce.core.SelectionOverrides', - 'tinymce.core.UndoManager', - 'tinymce.core.util.Delay', - 'tinymce.core.util.Quirks', - 'tinymce.core.util.Tools' - ], - function( - document, window, CaretContainerInput, DOMUtils, Selection, Serializer, EditorUpload, ErrorReporter, ForceBlocks, Formatter, DomParser, Node, Schema, KeyboardOverrides, - NodeChange, SelectionOverrides, UndoManager, Delay, Quirks, Tools - ) { - var DOM = DOMUtils.DOM; - - var createParser = function(editor) { - var parser = new DomParser(editor.settings, editor.schema); - - // Convert src and href into data-mce-src, data-mce-href and data-mce-style - parser.addAttributeFilter('src,href,style,tabindex', function(nodes, name) { - var i = nodes.length, - node, dom = editor.dom, - value, internalName; - - while (i--) { - node = nodes[i]; - value = node.attr(name); - internalName = 'data-mce-' + name; - - // Add internal attribute if we need to we don't on a refresh of the document - if (!node.attributes.map[internalName]) { - // Don't duplicate these since they won't get modified by any browser - if (value.indexOf('data:') === 0 || value.indexOf('blob:') === 0) { - continue; - } - - if (name === "style") { - value = dom.serializeStyle(dom.parseStyle(value), node.name); - - if (!value.length) { - value = null; - } - - node.attr(internalName, value); - node.attr(name, value); - } else if (name === "tabindex") { - node.attr(internalName, value); - node.attr(name, null); - } else { - node.attr(internalName, editor.convertURL(value, name, node.name)); - } - } - } - }); - - // Keep scripts from executing - parser.addNodeFilter('script', function(nodes) { - var i = nodes.length, - node, type; - - while (i--) { - node = nodes[i]; - type = node.attr('type') || 'no/type'; - if (type.indexOf('mce-') !== 0) { - node.attr('type', 'mce-' + type); - } - } - }); - - parser.addNodeFilter('#cdata', function(nodes) { - var i = nodes.length, - node; - - while (i--) { - node = nodes[i]; - node.type = 8; - node.name = '#comment'; - node.value = '[CDATA[' + node.value + ']]'; - } - }); - - parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes) { - var i = nodes.length, - node, nonEmptyElements = editor.schema.getNonEmptyElements(); - - while (i--) { - node = nodes[i]; - - if (node.isEmpty(nonEmptyElements) && node.getAll('br').length === 0) { - node.append(new Node('br', 1)).shortEnded = true; - } - } - }); - - return parser; - }; - - var autoFocus = function(editor) { - if (editor.settings.auto_focus) { - Delay.setEditorTimeout(editor, function() { - var focusEditor; - - if (editor.settings.auto_focus === true) { - focusEditor = editor; - } else { - focusEditor = editor.editorManager.get(editor.settings.auto_focus); - } - - if (!focusEditor.destroyed) { - focusEditor.focus(); - } - }, 100); - } - }; - - var initEditor = function(editor) { - editor.bindPendingEventDelegates(); - editor.initialized = true; - editor.fire('init'); - editor.focus(true); - editor.nodeChanged({ - initial: true - }); - editor.execCallback('init_instance_callback', editor); - autoFocus(editor); - }; - - var getStyleSheetLoader = function(editor) { - return editor.inline ? DOM.styleSheetLoader : editor.dom.styleSheetLoader; - }; - - var initContentBody = function(editor, skipWrite) { - var settings = editor.settings, - targetElm = editor.getElement(), - doc = editor.getDoc(), - body, contentCssText; - - // Restore visibility on target element - if (!settings.inline) { - editor.getElement().style.visibility = editor.orgVisibility; - } - - // Setup iframe body - if (!skipWrite && !settings.content_editable) { - doc.open(); - doc.write(editor.iframeHTML); - doc.close(); - } - - if (settings.content_editable) { - editor.on('remove', function() { - var bodyEl = this.getBody(); - - DOM.removeClass(bodyEl, 'mce-content-body'); - DOM.removeClass(bodyEl, 'mce-edit-focus'); - DOM.setAttrib(bodyEl, 'contentEditable', null); - }); - - DOM.addClass(targetElm, 'mce-content-body'); - editor.contentDocument = doc = settings.content_document || document; - editor.contentWindow = settings.content_window || window; - editor.bodyElement = targetElm; - - // Prevent leak in IE - settings.content_document = settings.content_window = null; - - // TODO: Fix this - settings.root_name = targetElm.nodeName.toLowerCase(); - } - - // It will not steal focus while setting contentEditable - body = editor.getBody(); - body.disabled = true; - editor.readonly = settings.readonly; - - if (!editor.readonly) { - if (editor.inline && DOM.getStyle(body, 'position', true) === 'static') { - body.style.position = 'relative'; - } - - body.contentEditable = editor.getParam('content_editable_state', true); - } - - body.disabled = false; - - editor.editorUpload = new EditorUpload(editor); - editor.schema = new Schema(settings); - editor.dom = new DOMUtils(doc, { - keep_values: true, - url_converter: editor.convertURL, - url_converter_scope: editor, - hex_colors: settings.force_hex_style_colors, - class_filter: settings.class_filter, - update_styles: true, - root_element: editor.inline ? editor.getBody() : null, - collect: settings.content_editable, - schema: editor.schema, - onSetAttrib: function(e) { - editor.fire('SetAttrib', e); - } - }); - - editor.parser = createParser(editor); - editor.serializer = new Serializer(settings, editor); - editor.selection = new Selection(editor.dom, editor.getWin(), editor.serializer, editor); - editor.formatter = new Formatter(editor); - editor.undoManager = new UndoManager(editor); - editor._nodeChangeDispatcher = new NodeChange(editor); - editor._selectionOverrides = new SelectionOverrides(editor); - - CaretContainerInput.setup(editor); - KeyboardOverrides.setup(editor); - ForceBlocks.setup(editor); - - editor.fire('PreInit'); - - if (!settings.browser_spellcheck && !settings.gecko_spellcheck) { - doc.body.spellcheck = false; // Gecko - DOM.setAttrib(body, "spellcheck", "false"); - } - - editor.quirks = new Quirks(editor); - editor.fire('PostRender'); - - if (settings.directionality) { - body.dir = settings.directionality; - } - - if (settings.nowrap) { - body.style.whiteSpace = "nowrap"; - } - - if (settings.protect) { - editor.on('BeforeSetContent', function(e) { - Tools.each(settings.protect, function(pattern) { - e.content = e.content.replace(pattern, function(str) { - return '<!--mce:protected ' + escape(str) + '-->'; - }); - }); - }); - } - - editor.on('SetContent', function() { - editor.addVisual(editor.getBody()); - }); - - // Remove empty contents - if (settings.padd_empty_editor) { - editor.on('PostProcess', function(e) { - e.content = e.content.replace(/^(<p[^>]*>( | |\s|\u00a0|<br \/>|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, ''); - }); - } - - editor.load({ - initial: true, - format: 'html' - }); - editor.startContent = editor.getContent({ - format: 'raw' - }); - - editor.on('compositionstart compositionend', function(e) { - editor.composing = e.type === 'compositionstart'; - }); - - // Add editor specific CSS styles - if (editor.contentStyles.length > 0) { - contentCssText = ''; - - Tools.each(editor.contentStyles, function(style) { - contentCssText += style + "\r\n"; - }); - - editor.dom.addStyle(contentCssText); - } - - getStyleSheetLoader(editor).loadAll( - editor.contentCSS, - function(_) { - initEditor(editor); - }, - function(urls) { - initEditor(editor); - ErrorReporter.contentCssError(editor, urls); - } - ); - }; - - return { - initContentBody: initContentBody - }; - } - ); - - /** - * PluginManager.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.PluginManager', [ - 'tinymce.core.AddOnManager' - ], - function(AddOnManager) { - return AddOnManager.PluginManager; - } - ); - - /** - * ThemeManager.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.ThemeManager', [ - 'tinymce.core.AddOnManager' - ], - function(AddOnManager) { - return AddOnManager.ThemeManager; - } - ); - - /** - * Init.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.init.Init', [ - 'global!document', - 'global!window', - 'tinymce.core.dom.DOMUtils', - 'tinymce.core.Env', - 'tinymce.core.init.InitContentBody', - 'tinymce.core.PluginManager', - 'tinymce.core.ThemeManager', - 'tinymce.core.util.Tools', - 'tinymce.core.util.Uuid' - ], - function(document, window, DOMUtils, Env, InitContentBody, PluginManager, ThemeManager, Tools, Uuid) { - var DOM = DOMUtils.DOM; - - var initPlugin = function(editor, initializedPlugins, plugin) { - var Plugin = PluginManager.get(plugin), - pluginUrl, pluginInstance; - - pluginUrl = PluginManager.urls[plugin] || editor.documentBaseUrl.replace(/\/$/, ''); - plugin = Tools.trim(plugin); - if (Plugin && Tools.inArray(initializedPlugins, plugin) === -1) { - Tools.each(PluginManager.dependencies(plugin), function(dep) { - initPlugin(editor, initializedPlugins, dep); - }); - - if (editor.plugins[plugin]) { - return; - } - - pluginInstance = new Plugin(editor, pluginUrl, editor.$); - - editor.plugins[plugin] = pluginInstance; - - if (pluginInstance.init) { - pluginInstance.init(editor, pluginUrl); - initializedPlugins.push(plugin); - } - } - }; - - var initPlugins = function(editor) { - var initializedPlugins = []; - - Tools.each(editor.settings.plugins.replace(/\-/g, '').split(/[ ,]/), function(name) { - initPlugin(editor, initializedPlugins, name); - }); - }; - - var initTheme = function(editor) { - var Theme, settings = editor.settings; - - if (settings.theme) { - if (typeof settings.theme != "function") { - settings.theme = settings.theme.replace(/-/, ''); - Theme = ThemeManager.get(settings.theme); - editor.theme = new Theme(editor, ThemeManager.urls[settings.theme]); - - if (editor.theme.init) { - editor.theme.init(editor, ThemeManager.urls[settings.theme] || editor.documentBaseUrl.replace(/\/$/, ''), editor.$); - } - } else { - editor.theme = settings.theme; - } - } - }; - - var measueBox = function(editor) { - var w, h, minHeight, re, o, settings = editor.settings, - elm = editor.getElement(); - - // Measure box - if (settings.render_ui && editor.theme) { - editor.orgDisplay = elm.style.display; - - if (typeof settings.theme != "function") { - w = settings.width || DOM.getStyle(elm, 'width') || '100%'; - h = settings.height || DOM.getStyle(elm, 'height') || elm.offsetHeight; - minHeight = settings.min_height || 100; - re = /^[0-9\.]+(|px)$/i; - - if (re.test('' + w)) { - w = Math.max(parseInt(w, 10), 100); - } - - if (re.test('' + h)) { - h = Math.max(parseInt(h, 10), minHeight); - } - - // Render UI - o = editor.theme.renderUI({ - targetNode: elm, - width: w, - height: h, - deltaWidth: settings.delta_width, - deltaHeight: settings.delta_height - }); - - // Resize editor - if (!settings.content_editable) { - h = (o.iframeHeight || h) + (typeof h === 'number' ? (o.deltaHeight || 0) : ''); - if (h < minHeight) { - h = minHeight; - } - } - } else { - o = settings.theme(editor, elm); - - if (o.editorContainer.nodeType) { - o.editorContainer.id = o.editorContainer.id || editor.id + "_parent"; - } - - if (o.iframeContainer.nodeType) { - o.iframeContainer.id = o.iframeContainer.id || editor.id + "_iframecontainer"; - } - - // Use specified iframe height or the targets offsetHeight - h = o.iframeHeight || elm.offsetHeight; - } - - editor.editorContainer = o.editorContainer; - o.height = h; - } - - return o; - }; - - var relaxDomain = function(editor, ifr) { - // Domain relaxing is required since the user has messed around with document.domain - // This only applies to IE 11 other browsers including Edge seems to handle document.domain - if (document.domain !== window.location.hostname && Env.ie && Env.ie < 12) { - var bodyUuid = Uuid.uuid('mce'); - - editor[bodyUuid] = function() { - InitContentBody.initContentBody(editor); - }; - - /*eslint no-script-url:0 */ - var domainRelaxUrl = 'javascript:(function(){' + - 'document.open();document.domain="' + document.domain + '";' + - 'var ed = window.parent.tinymce.get("' + editor.id + '");document.write(ed.iframeHTML);' + - 'document.close();ed.' + bodyUuid + '(true);})()'; - - DOM.setAttrib(ifr, 'src', domainRelaxUrl); - return true; - } - - return false; - }; - - var createIframe = function(editor, o) { - var settings = editor.settings, - bodyId, bodyClass; - - editor.iframeHTML = settings.doctype + '<html><head>'; - - // We only need to override paths if we have to - // IE has a bug where it remove site absolute urls to relative ones if this is specified - if (settings.document_base_url != editor.documentBaseUrl) { - editor.iframeHTML += '<base href="' + editor.documentBaseURI.getURI() + '" />'; - } - - // IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode. - if (!Env.caretAfter && settings.ie7_compat) { - editor.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />'; - } - - editor.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />'; - - bodyId = settings.body_id || 'tinymce'; - if (bodyId.indexOf('=') != -1) { - bodyId = editor.getParam('body_id', '', 'hash'); - bodyId = bodyId[editor.id] || bodyId; - } - - bodyClass = settings.body_class || ''; - if (bodyClass.indexOf('=') != -1) { - bodyClass = editor.getParam('body_class', '', 'hash'); - bodyClass = bodyClass[editor.id] || ''; - } - - if (settings.content_security_policy) { - editor.iframeHTML += '<meta http-equiv="Content-Security-Policy" content="' + settings.content_security_policy + '" />'; - } - - editor.iframeHTML += '</head><body id="' + bodyId + - '" class="mce-content-body ' + bodyClass + - '" data-id="' + editor.id + '"><br></body></html>'; - - // Create iframe - // TODO: ACC add the appropriate description on this. - var ifr = DOM.create('iframe', { - id: editor.id + "_ifr", - frameBorder: '0', - allowTransparency: "true", - title: editor.editorManager.translate( - "Rich Text Area. Press ALT-F9 for menu. " + - "Press ALT-F10 for toolbar. Press ALT-0 for help" - ), - style: { - width: '100%', - height: o.height, - display: 'block' // Important for Gecko to render the iframe correctly - } - }); - - ifr.onload = function() { - ifr.onload = null; - editor.fire("load"); - }; - - var isDomainRelaxed = relaxDomain(editor, ifr); - - editor.contentAreaContainer = o.iframeContainer; - editor.iframeElement = ifr; - - DOM.add(o.iframeContainer, ifr); - - return isDomainRelaxed; - }; - - var init = function(editor) { - var settings = editor.settings, - elm = editor.getElement(), - boxInfo; - - editor.rtl = settings.rtl_ui || editor.editorManager.i18n.rtl; - editor.editorManager.i18n.setCode(settings.language); - settings.aria_label = settings.aria_label || DOM.getAttrib(elm, 'aria-label', editor.getLang('aria.rich_text_area')); - - editor.fire('ScriptsLoaded'); - - initTheme(editor); - initPlugins(editor); - boxInfo = measueBox(editor); - - // Load specified content CSS last - if (settings.content_css) { - Tools.each(Tools.explode(settings.content_css), function(u) { - editor.contentCSS.push(editor.documentBaseURI.toAbsolute(u)); - }); - } - - // Load specified content CSS last - if (settings.content_style) { - editor.contentStyles.push(settings.content_style); - } - - // Content editable mode ends here - if (settings.content_editable) { - return InitContentBody.initContentBody(editor); - } - - var isDomainRelaxed = createIframe(editor, boxInfo); - - if (boxInfo.editorContainer) { - DOM.get(boxInfo.editorContainer).style.display = editor.orgDisplay; - editor.hidden = DOM.isHidden(boxInfo.editorContainer); - } - - editor.getElement().style.display = 'none'; - DOM.setAttrib(editor.id, 'aria-hidden', true); - - if (!isDomainRelaxed) { - InitContentBody.initContentBody(editor); - } - }; - - return { - init: init - }; - } - ); - - /** - * Render.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.init.Render', [ - 'global!window', - 'tinymce.core.dom.DOMUtils', - 'tinymce.core.dom.EventUtils', - 'tinymce.core.dom.ScriptLoader', - 'tinymce.core.Env', - 'tinymce.core.ErrorReporter', - 'tinymce.core.init.Init', - 'tinymce.core.NotificationManager', - 'tinymce.core.PluginManager', - 'tinymce.core.ThemeManager', - 'tinymce.core.util.Tools', - 'tinymce.core.WindowManager' - ], - function(window, DOMUtils, EventUtils, ScriptLoader, Env, ErrorReporter, Init, NotificationManager, PluginManager, ThemeManager, Tools, WindowManager) { - var DOM = DOMUtils.DOM; - - var loadScripts = function(editor, suffix) { - var settings = editor.settings, - scriptLoader = ScriptLoader.ScriptLoader; - - if (settings.language && settings.language != 'en' && !settings.language_url) { - settings.language_url = editor.editorManager.baseURL + '/langs/' + settings.language + '.js'; - } - - if (settings.language_url) { - scriptLoader.add(settings.language_url); - } - - if (settings.theme && typeof settings.theme != "function" && - settings.theme.charAt(0) != '-' && !ThemeManager.urls[settings.theme]) { - var themeUrl = settings.theme_url; - - if (themeUrl) { - themeUrl = editor.documentBaseURI.toAbsolute(themeUrl); - } else { - themeUrl = 'themes/' + settings.theme + '/theme' + suffix + '.js'; - } - - ThemeManager.load(settings.theme, themeUrl); - } - - if (Tools.isArray(settings.plugins)) { - settings.plugins = settings.plugins.join(' '); - } - - Tools.each(settings.external_plugins, function(url, name) { - PluginManager.load(name, url); - settings.plugins += ' ' + name; - }); - - Tools.each(settings.plugins.split(/[ ,]/), function(plugin) { - plugin = Tools.trim(plugin); - - if (plugin && !PluginManager.urls[plugin]) { - if (plugin.charAt(0) === '-') { - plugin = plugin.substr(1, plugin.length); - - var dependencies = PluginManager.dependencies(plugin); - - Tools.each(dependencies, function(dep) { - var defaultSettings = { - prefix: 'plugins/', - resource: dep, - suffix: '/plugin' + suffix + '.js' - }; - - dep = PluginManager.createUrl(defaultSettings, dep); - PluginManager.load(dep.resource, dep); - }); - } else { - PluginManager.load(plugin, { - prefix: 'plugins/', - resource: plugin, - suffix: '/plugin' + suffix + '.js' - }); - } - } - }); - - scriptLoader.loadQueue(function() { - if (!editor.removed) { - Init.init(editor); - } - }, editor, function(urls) { - ErrorReporter.pluginLoadError(editor, urls[0]); - - if (!editor.removed) { - Init.init(editor); - } - }); - }; - - var render = function(editor) { - var settings = editor.settings, - id = editor.id; - - function readyHandler() { - DOM.unbind(window, 'ready', readyHandler); - editor.render(); - } - - // Page is not loaded yet, wait for it - if (!EventUtils.Event.domLoaded) { - DOM.bind(window, 'ready', readyHandler); - return; - } - - // Element not found, then skip initialization - if (!editor.getElement()) { - return; - } - - // No editable support old iOS versions etc - if (!Env.contentEditable) { - return; - } - - // Hide target element early to prevent content flashing - if (!settings.inline) { - editor.orgVisibility = editor.getElement().style.visibility; - editor.getElement().style.visibility = 'hidden'; - } else { - editor.inline = true; - } - - var form = editor.getElement().form || DOM.getParent(id, 'form'); - if (form) { - editor.formElement = form; - - // Add hidden input for non input elements inside form elements - if (settings.hidden_input && !/TEXTAREA|INPUT/i.test(editor.getElement().nodeName)) { - DOM.insertAfter(DOM.create('input', { - type: 'hidden', - name: id - }), id); - editor.hasHiddenInput = true; - } - - // Pass submit/reset from form to editor instance - editor.formEventDelegate = function(e) { - editor.fire(e.type, e); - }; - - DOM.bind(form, 'submit reset', editor.formEventDelegate); - - // Reset contents in editor when the form is reset - editor.on('reset', function() { - editor.setContent(editor.startContent, { - format: 'raw' - }); - }); - - // Check page uses id="submit" or name="submit" for it's submit button - if (settings.submit_patch && !form.submit.nodeType && !form.submit.length && !form._mceOldSubmit) { - form._mceOldSubmit = form.submit; - form.submit = function() { - editor.editorManager.triggerSave(); - editor.setDirty(false); - - return form._mceOldSubmit(form); - }; - } - } - - editor.windowManager = new WindowManager(editor); - editor.notificationManager = new NotificationManager(editor); - - if (settings.encoding === 'xml') { - editor.on('GetContent', function(e) { - if (e.save) { - e.content = DOM.encode(e.content); - } - }); - } - - if (settings.add_form_submit_trigger) { - editor.on('submit', function() { - if (editor.initialized) { - editor.save(); - } - }); - } - - if (settings.add_unload_trigger) { - editor._beforeUnload = function() { - if (editor.initialized && !editor.destroyed && !editor.isHidden()) { - editor.save({ - format: 'raw', - no_events: true, - set_dirty: false - }); - } - }; - - editor.editorManager.on('BeforeUnload', editor._beforeUnload); - } - - editor.editorManager.add(editor); - loadScripts(editor, editor.suffix); - }; - - return { - render: render - }; - } - ); - - /** - * Mode.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Mode switcher logic. - * - * @private - * @class tinymce.Mode - */ - define( - 'tinymce.core.Mode', [], - function() { - function setEditorCommandState(editor, cmd, state) { - try { - editor.getDoc().execCommand(cmd, false, state); - } catch (ex) { - // Ignore - } - } - - function clickBlocker(editor) { - var target, handler; - - target = editor.getBody(); - - handler = function(e) { - if (editor.dom.getParents(e.target, 'a').length > 0) { - e.preventDefault(); - } - }; - - editor.dom.bind(target, 'click', handler); - - return { - unbind: function() { - editor.dom.unbind(target, 'click', handler); - } - }; - } - - function toggleReadOnly(editor, state) { - if (editor._clickBlocker) { - editor._clickBlocker.unbind(); - editor._clickBlocker = null; - } - - if (state) { - editor._clickBlocker = clickBlocker(editor); - editor.selection.controlSelection.hideResizeRect(); - editor.readonly = true; - editor.getBody().contentEditable = false; - } else { - editor.readonly = false; - editor.getBody().contentEditable = true; - setEditorCommandState(editor, "StyleWithCSS", false); - setEditorCommandState(editor, "enableInlineTableEditing", false); - setEditorCommandState(editor, "enableObjectResizing", false); - editor.focus(); - editor.nodeChanged(); - } - } - - function setMode(editor, mode) { - var currentMode = editor.readonly ? 'readonly' : 'design'; - - if (mode == currentMode) { - return; - } - - if (editor.initialized) { - toggleReadOnly(editor, mode == 'readonly'); - } else { - editor.on('init', function() { - toggleReadOnly(editor, mode == 'readonly'); - }); - } - - // Event is NOT preventable - editor.fire('SwitchMode', { - mode: mode - }); - } - - return { - setMode: setMode - }; - } - ); - /** - * Sidebar.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module handle sidebar instances for the editor. - * - * @class tinymce.ui.Sidebar - * @private - */ - define( - 'tinymce.core.ui.Sidebar', [], - function() { - var add = function(editor, name, settings) { - var sidebars = editor.sidebars ? editor.sidebars : []; - sidebars.push({ - name: name, - settings: settings - }); - editor.sidebars = sidebars; - }; - - return { - add: add - }; - } - ); - - /** - * Editor.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /*jshint scripturl:true */ - - /** - * Include the base event class documentation. - * - * @include ../../../../../tools/docs/tinymce.Event.js - */ - - /** - * This class contains the core logic for a TinyMCE editor. - * - * @class tinymce.Editor - * @mixes tinymce.util.Observable - * @example - * // Add a class to all paragraphs in the editor. - * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass'); - * - * // Gets the current editors selection as text - * tinymce.activeEditor.selection.getContent({format: 'text'}); - * - * // Creates a new editor instance - * var ed = new tinymce.Editor('textareaid', { - * some_setting: 1 - * }, tinymce.EditorManager); - * - * ed.render(); - */ - define( - 'tinymce.core.Editor', [ - 'tinymce.core.AddOnManager', - 'tinymce.core.dom.DomQuery', - 'tinymce.core.dom.DOMUtils', - 'tinymce.core.EditorCommands', - 'tinymce.core.EditorObservable', - 'tinymce.core.EditorSettings', - 'tinymce.core.Env', - 'tinymce.core.html.Serializer', - 'tinymce.core.init.Render', - 'tinymce.core.Mode', - 'tinymce.core.Shortcuts', - 'tinymce.core.ui.Sidebar', - 'tinymce.core.util.Tools', - 'tinymce.core.util.URI', - 'tinymce.core.util.Uuid' - ], - function(AddOnManager, DomQuery, DOMUtils, EditorCommands, EditorObservable, EditorSettings, Env, Serializer, Render, Mode, Shortcuts, Sidebar, Tools, URI, Uuid) { - // Shorten these names - var DOM = DOMUtils.DOM; - var extend = Tools.extend, - each = Tools.each; - var trim = Tools.trim, - resolve = Tools.resolve; - var isGecko = Env.gecko, - ie = Env.ie; - - /** - * Include Editor API docs. - * - * @include ../../../../../tools/docs/tinymce.Editor.js - */ - - /** - * Constructs a editor instance by id. - * - * @constructor - * @method Editor - * @param {String} id Unique id for the editor. - * @param {Object} settings Settings for the editor. - * @param {tinymce.EditorManager} editorManager EditorManager instance. - */ - function Editor(id, settings, editorManager) { - var self = this, - documentBaseUrl, baseUri; - - documentBaseUrl = self.documentBaseUrl = editorManager.documentBaseURL; - baseUri = editorManager.baseURI; - - /** - * Name/value collection with editor settings. - * - * @property settings - * @type Object - * @example - * // Get the value of the theme setting - * tinymce.activeEditor.windowManager.alert("You are using the " + tinymce.activeEditor.settings.theme + " theme"); - */ - settings = EditorSettings.getEditorSettings(self, id, documentBaseUrl, editorManager.defaultSettings, settings); - self.settings = settings; - - AddOnManager.language = settings.language || 'en'; - AddOnManager.languageLoad = settings.language_load; - AddOnManager.baseURL = editorManager.baseURL; - - /** - * Editor instance id, normally the same as the div/textarea that was replaced. - * - * @property id - * @type String - */ - self.id = id; - - /** - * State to force the editor to return false on a isDirty call. - * - * @property isNotDirty - * @type Boolean - * @deprecated Use editor.setDirty instead. - */ - self.setDirty(false); - - /** - * Name/Value object containing plugin instances. - * - * @property plugins - * @type Object - * @example - * // Execute a method inside a plugin directly - * tinymce.activeEditor.plugins.someplugin.someMethod(); - */ - self.plugins = {}; - - /** - * URI object to document configured for the TinyMCE instance. - * - * @property documentBaseURI - * @type tinymce.util.URI - * @example - * // Get relative URL from the location of document_base_url - * tinymce.activeEditor.documentBaseURI.toRelative('/somedir/somefile.htm'); - * - * // Get absolute URL from the location of document_base_url - * tinymce.activeEditor.documentBaseURI.toAbsolute('somefile.htm'); - */ - self.documentBaseURI = new URI(settings.document_base_url, { - base_uri: baseUri - }); - - /** - * URI object to current document that holds the TinyMCE editor instance. - * - * @property baseURI - * @type tinymce.util.URI - * @example - * // Get relative URL from the location of the API - * tinymce.activeEditor.baseURI.toRelative('/somedir/somefile.htm'); - * - * // Get absolute URL from the location of the API - * tinymce.activeEditor.baseURI.toAbsolute('somefile.htm'); - */ - self.baseURI = baseUri; - - /** - * Array with CSS files to load into the iframe. - * - * @property contentCSS - * @type Array - */ - self.contentCSS = []; - - /** - * Array of CSS styles to add to head of document when the editor loads. - * - * @property contentStyles - * @type Array - */ - self.contentStyles = []; - - // Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic - self.shortcuts = new Shortcuts(self); - self.loadedCSS = {}; - self.editorCommands = new EditorCommands(self); - self.suffix = editorManager.suffix; - self.editorManager = editorManager; - self.inline = settings.inline; - - if (settings.cache_suffix) { - Env.cacheSuffix = settings.cache_suffix.replace(/^[\?\&]+/, ''); - } - - if (settings.override_viewport === false) { - Env.overrideViewPort = false; - } - - // Call setup - editorManager.fire('SetupEditor', self); - self.execCallback('setup', self); - - /** - * Dom query instance with default scope to the editor document and default element is the body of the editor. - * - * @property $ - * @type tinymce.dom.DomQuery - * @example - * tinymce.activeEditor.$('p').css('color', 'red'); - * tinymce.activeEditor.$().append('<p>new</p>'); - */ - self.$ = DomQuery.overrideDefaults(function() { - return { - context: self.inline ? self.getBody() : self.getDoc(), - element: self.getBody() - }; - }); - } - - Editor.prototype = { - /** - * Renders the editor/adds it to the page. - * - * @method render - */ - render: function() { - Render.render(this); - }, - - /** - * Focuses/activates the editor. This will set this editor as the activeEditor in the tinymce collection - * it will also place DOM focus inside the editor. - * - * @method focus - * @param {Boolean} skipFocus Skip DOM focus. Just set is as the active editor. - */ - focus: function(skipFocus) { - var self = this, - selection = self.selection, - contentEditable = self.settings.content_editable, - rng; - var controlElm, doc = self.getDoc(), - body = self.getBody(), - contentEditableHost; - - function getContentEditableHost(node) { - return self.dom.getParent(node, function(node) { - return self.dom.getContentEditable(node) === "true"; - }); - } - - if (self.removed) { - return; - } - - if (!skipFocus) { - // Get selected control element - rng = selection.getRng(); - if (rng.item) { - controlElm = rng.item(0); - } - - self.quirks.refreshContentEditable(); - - // Move focus to contentEditable=true child if needed - contentEditableHost = getContentEditableHost(selection.getNode()); - if (self.$.contains(body, contentEditableHost)) { - contentEditableHost.focus(); - selection.normalize(); - self.editorManager.setActive(self); - return; - } - - // Focus the window iframe - if (!contentEditable) { - // WebKit needs this call to fire focusin event properly see #5948 - // But Opera pre Blink engine will produce an empty selection so skip Opera - if (!Env.opera) { - self.getBody().focus(); - } - - self.getWin().focus(); - } - - // Focus the body as well since it's contentEditable - if (isGecko || contentEditable) { - // Check for setActive since it doesn't scroll to the element - if (body.setActive) { - // IE 11 sometimes throws "Invalid function" then fallback to focus - try { - body.setActive(); - } catch (ex) { - body.focus(); - } - } else { - // Restore previous selection before focus to prevent Chrome from - // jumping to the top of the document in long inline editors - self.selection.setRng(self.lastRng); - body.focus(); - } - - if (contentEditable) { - selection.normalize(); - } - } - - // Restore selected control element - // This is needed when for example an image is selected within a - // layer a call to focus will then remove the control selection - if (controlElm && controlElm.ownerDocument == doc) { - rng = doc.body.createControlRange(); - rng.addElement(controlElm); - rng.select(); - } - } - - self.editorManager.setActive(self); - }, - - /** - * Executes a legacy callback. This method is useful to call old 2.x option callbacks. - * There new event model is a better way to add callback so this method might be removed in the future. - * - * @method execCallback - * @param {String} name Name of the callback to execute. - * @return {Object} Return value passed from callback function. - */ - execCallback: function(name) { - var self = this, - callback = self.settings[name], - scope; - - if (!callback) { - return; - } - - // Look through lookup - if (self.callbackLookup && (scope = self.callbackLookup[name])) { - callback = scope.func; - scope = scope.scope; - } - - if (typeof callback === 'string') { - scope = callback.replace(/\.\w+$/, ''); - scope = scope ? resolve(scope) : 0; - callback = resolve(callback); - self.callbackLookup = self.callbackLookup || {}; - self.callbackLookup[name] = { - func: callback, - scope: scope - }; - } - - return callback.apply(scope || self, Array.prototype.slice.call(arguments, 1)); - }, - - /** - * Translates the specified string by replacing variables with language pack items it will also check if there is - * a key matching the input. - * - * @method translate - * @param {String} text String to translate by the language pack data. - * @return {String} Translated string. - */ - translate: function(text) { - if (text && Tools.is(text, 'string')) { - var lang = this.settings.language || 'en', - i18n = this.editorManager.i18n; - - text = i18n.data[lang + '.' + text] || text.replace(/\{\#([^\}]+)\}/g, function(a, b) { - return i18n.data[lang + '.' + b] || '{#' + b + '}'; - }); - } - - return this.editorManager.translate(text); - }, - - /** - * Returns a language pack item by name/key. - * - * @method getLang - * @param {String} name Name/key to get from the language pack. - * @param {String} defaultVal Optional default value to retrieve. - */ - getLang: function(name, defaultVal) { - return ( - this.editorManager.i18n.data[(this.settings.language || 'en') + '.' + name] || - (defaultVal !== undefined ? defaultVal : '{#' + name + '}') - ); - }, - - /** - * Returns a configuration parameter by name. - * - * @method getParam - * @param {String} name Configruation parameter to retrieve. - * @param {String} defaultVal Optional default value to return. - * @param {String} type Optional type parameter. - * @return {String} Configuration parameter value or default value. - * @example - * // Returns a specific config value from the currently active editor - * var someval = tinymce.activeEditor.getParam('myvalue'); - * - * // Returns a specific config value from a specific editor instance by id - * var someval2 = tinymce.get('my_editor').getParam('myvalue'); - */ - getParam: function(name, defaultVal, type) { - var value = name in this.settings ? this.settings[name] : defaultVal, - output; - - if (type === 'hash') { - output = {}; - - if (typeof value === 'string') { - each(value.indexOf('=') > 0 ? value.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value.split(','), function(value) { - value = value.split('='); - - if (value.length > 1) { - output[trim(value[0])] = trim(value[1]); - } else { - output[trim(value[0])] = trim(value); - } - }); - } else { - output = value; - } - - return output; - } - - return value; - }, - - /** - * Dispatches out a onNodeChange event to all observers. This method should be called when you - * need to update the UI states or element path etc. - * - * @method nodeChanged - * @param {Object} args Optional args to pass to NodeChange event handlers. - */ - nodeChanged: function(args) { - this._nodeChangeDispatcher.nodeChanged(args); - }, - - /** - * Adds a button that later gets created by the theme in the editors toolbars. - * - * @method addButton - * @param {String} name Button name to add. - * @param {Object} settings Settings object with title, cmd etc. - * @example - * // Adds a custom button to the editor that inserts contents when clicked - * tinymce.init({ - * ... - * - * toolbar: 'example' - * - * setup: function(ed) { - * ed.addButton('example', { - * title: 'My title', - * image: '../js/tinymce/plugins/example/img/example.gif', - * onclick: function() { - * ed.insertContent('Hello world!!'); - * } - * }); - * } - * }); - */ - addButton: function(name, settings) { - var self = this; - - if (settings.cmd) { - settings.onclick = function() { - self.execCommand(settings.cmd); - }; - } - - if (!settings.text && !settings.icon) { - settings.icon = name; - } - - self.buttons = self.buttons || {}; - settings.tooltip = settings.tooltip || settings.title; - self.buttons[name] = settings; - }, - - /** - * Adds a sidebar for the editor instance. - * - * @method addSidebar - * @param {String} name Sidebar name to add. - * @param {Object} settings Settings object with icon, onshow etc. - * @example - * // Adds a custom sidebar that when clicked logs the panel element - * tinymce.init({ - * ... - * setup: function(ed) { - * ed.addSidebar('example', { - * tooltip: 'My sidebar', - * icon: 'my-side-bar', - * onshow: function(api) { - * console.log(api.element()); - * } - * }); - * } - * }); - */ - addSidebar: function(name, settings) { - return Sidebar.add(this, name, settings); - }, - - /** - * Adds a menu item to be used in the menus of the theme. There might be multiple instances - * of this menu item for example it might be used in the main menus of the theme but also in - * the context menu so make sure that it's self contained and supports multiple instances. - * - * @method addMenuItem - * @param {String} name Menu item name to add. - * @param {Object} settings Settings object with title, cmd etc. - * @example - * // Adds a custom menu item to the editor that inserts contents when clicked - * // The context option allows you to add the menu item to an existing default menu - * tinymce.init({ - * ... - * - * setup: function(ed) { - * ed.addMenuItem('example', { - * text: 'My menu item', - * context: 'tools', - * onclick: function() { - * ed.insertContent('Hello world!!'); - * } - * }); - * } - * }); - */ - addMenuItem: function(name, settings) { - var self = this; - - if (settings.cmd) { - settings.onclick = function() { - self.execCommand(settings.cmd); - }; - } - - self.menuItems = self.menuItems || {}; - self.menuItems[name] = settings; - }, - - /** - * Adds a contextual toolbar to be rendered when the selector matches. - * - * @method addContextToolbar - * @param {function/string} predicate Predicate that needs to return true if provided strings get converted into CSS predicates. - * @param {String/Array} items String or array with items to add to the context toolbar. - */ - addContextToolbar: function(predicate, items) { - var self = this, - selector; - - self.contextToolbars = self.contextToolbars || []; - - // Convert selector to predicate - if (typeof predicate == "string") { - selector = predicate; - predicate = function(elm) { - return self.dom.is(elm, selector); - }; - } - - self.contextToolbars.push({ - id: Uuid.uuid('mcet'), - predicate: predicate, - items: items - }); - }, - - /** - * Adds a custom command to the editor, you can also override existing commands with this method. - * The command that you add can be executed with execCommand. - * - * @method addCommand - * @param {String} name Command name to add/override. - * @param {addCommandCallback} callback Function to execute when the command occurs. - * @param {Object} scope Optional scope to execute the function in. - * @example - * // Adds a custom command that later can be executed using execCommand - * tinymce.init({ - * ... - * - * setup: function(ed) { - * // Register example command - * ed.addCommand('mycommand', function(ui, v) { - * ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format: 'text'})); - * }); - * } - * }); - */ - addCommand: function(name, callback, scope) { - /** - * Callback function that gets called when a command is executed. - * - * @callback addCommandCallback - * @param {Boolean} ui Display UI state true/false. - * @param {Object} value Optional value for command. - * @return {Boolean} True/false state if the command was handled or not. - */ - this.editorCommands.addCommand(name, callback, scope); - }, - - /** - * Adds a custom query state command to the editor, you can also override existing commands with this method. - * The command that you add can be executed with queryCommandState function. - * - * @method addQueryStateHandler - * @param {String} name Command name to add/override. - * @param {addQueryStateHandlerCallback} callback Function to execute when the command state retrieval occurs. - * @param {Object} scope Optional scope to execute the function in. - */ - addQueryStateHandler: function(name, callback, scope) { - /** - * Callback function that gets called when a queryCommandState is executed. - * - * @callback addQueryStateHandlerCallback - * @return {Boolean} True/false state if the command is enabled or not like is it bold. - */ - this.editorCommands.addQueryStateHandler(name, callback, scope); - }, - - /** - * Adds a custom query value command to the editor, you can also override existing commands with this method. - * The command that you add can be executed with queryCommandValue function. - * - * @method addQueryValueHandler - * @param {String} name Command name to add/override. - * @param {addQueryValueHandlerCallback} callback Function to execute when the command value retrieval occurs. - * @param {Object} scope Optional scope to execute the function in. - */ - addQueryValueHandler: function(name, callback, scope) { - /** - * Callback function that gets called when a queryCommandValue is executed. - * - * @callback addQueryValueHandlerCallback - * @return {Object} Value of the command or undefined. - */ - this.editorCommands.addQueryValueHandler(name, callback, scope); - }, - - /** - * Adds a keyboard shortcut for some command or function. - * - * @method addShortcut - * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o. - * @param {String} desc Text description for the command. - * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed. - * @param {Object} sc Optional scope to execute the function in. - * @return {Boolean} true/false state if the shortcut was added or not. - */ - addShortcut: function(pattern, desc, cmdFunc, scope) { - this.shortcuts.add(pattern, desc, cmdFunc, scope); - }, - - /** - * Executes a command on the current instance. These commands can be TinyMCE internal commands prefixed with "mce" or - * they can be build in browser commands such as "Bold". A compleate list of browser commands is available on MSDN or Mozilla.org. - * This function will dispatch the execCommand function on each plugin, theme or the execcommand_callback option if none of these - * return true it will handle the command as a internal browser command. - * - * @method execCommand - * @param {String} cmd Command name to execute, for example mceLink or Bold. - * @param {Boolean} ui True/false state if a UI (dialog) should be presented or not. - * @param {mixed} value Optional command value, this can be anything. - * @param {Object} args Optional arguments object. - */ - execCommand: function(cmd, ui, value, args) { - return this.editorCommands.execCommand(cmd, ui, value, args); - }, - - /** - * Returns a command specific state, for example if bold is enabled or not. - * - * @method queryCommandState - * @param {string} cmd Command to query state from. - * @return {Boolean} Command specific state, for example if bold is enabled or not. - */ - queryCommandState: function(cmd) { - return this.editorCommands.queryCommandState(cmd); - }, - - /** - * Returns a command specific value, for example the current font size. - * - * @method queryCommandValue - * @param {string} cmd Command to query value from. - * @return {Object} Command specific value, for example the current font size. - */ - queryCommandValue: function(cmd) { - return this.editorCommands.queryCommandValue(cmd); - }, - - /** - * Returns true/false if the command is supported or not. - * - * @method queryCommandSupported - * @param {String} cmd Command that we check support for. - * @return {Boolean} true/false if the command is supported or not. - */ - queryCommandSupported: function(cmd) { - return this.editorCommands.queryCommandSupported(cmd); - }, - - /** - * Shows the editor and hides any textarea/div that the editor is supposed to replace. - * - * @method show - */ - show: function() { - var self = this; - - if (self.hidden) { - self.hidden = false; - - if (self.inline) { - self.getBody().contentEditable = true; - } else { - DOM.show(self.getContainer()); - DOM.hide(self.id); - } - - self.load(); - self.fire('show'); - } - }, - - /** - * Hides the editor and shows any textarea/div that the editor is supposed to replace. - * - * @method hide - */ - hide: function() { - var self = this, - doc = self.getDoc(); - - if (!self.hidden) { - // Fixed bug where IE has a blinking cursor left from the editor - if (ie && doc && !self.inline) { - doc.execCommand('SelectAll'); - } - - // We must save before we hide so Safari doesn't crash - self.save(); - - if (self.inline) { - self.getBody().contentEditable = false; - - // Make sure the editor gets blurred - if (self == self.editorManager.focusedEditor) { - self.editorManager.focusedEditor = null; - } - } else { - DOM.hide(self.getContainer()); - DOM.setStyle(self.id, 'display', self.orgDisplay); - } - - self.hidden = true; - self.fire('hide'); - } - }, - - /** - * Returns true/false if the editor is hidden or not. - * - * @method isHidden - * @return {Boolean} True/false if the editor is hidden or not. - */ - isHidden: function() { - return !!this.hidden; - }, - - /** - * Sets the progress state, this will display a throbber/progess for the editor. - * This is ideal for asynchronous operations like an AJAX save call. - * - * @method setProgressState - * @param {Boolean} state Boolean state if the progress should be shown or hidden. - * @param {Number} time Optional time to wait before the progress gets shown. - * @return {Boolean} Same as the input state. - * @example - * // Show progress for the active editor - * tinymce.activeEditor.setProgressState(true); - * - * // Hide progress for the active editor - * tinymce.activeEditor.setProgressState(false); - * - * // Show progress after 3 seconds - * tinymce.activeEditor.setProgressState(true, 3000); - */ - setProgressState: function(state, time) { - this.fire('ProgressState', { - state: state, - time: time - }); - }, - - /** - * Loads contents from the textarea or div element that got converted into an editor instance. - * This method will move the contents from that textarea or div into the editor by using setContent - * so all events etc that method has will get dispatched as well. - * - * @method load - * @param {Object} args Optional content object, this gets passed around through the whole load process. - * @return {String} HTML string that got set into the editor. - */ - load: function(args) { - var self = this, - elm = self.getElement(), - html; - - if (self.removed) { - return ''; - } - - if (elm) { - args = args || {}; - args.load = true; - - html = self.setContent(elm.value !== undefined ? elm.value : elm.innerHTML, args); - args.element = elm; - - if (!args.no_events) { - self.fire('LoadContent', args); - } - - args.element = elm = null; - - return html; - } - }, - - /** - * Saves the contents from a editor out to the textarea or div element that got converted into an editor instance. - * This method will move the HTML contents from the editor into that textarea or div by getContent - * so all events etc that method has will get dispatched as well. - * - * @method save - * @param {Object} args Optional content object, this gets passed around through the whole save process. - * @return {String} HTML string that got set into the textarea/div. - */ - save: function(args) { - var self = this, - elm = self.getElement(), - html, form; - - if (!elm || !self.initialized || self.removed) { - return; - } - - args = args || {}; - args.save = true; - - args.element = elm; - html = args.content = self.getContent(args); - - if (!args.no_events) { - self.fire('SaveContent', args); - } - - // Always run this internal event - if (args.format == 'raw') { - self.fire('RawSaveContent', args); - } - - html = args.content; - - if (!/TEXTAREA|INPUT/i.test(elm.nodeName)) { - // Update DIV element when not in inline mode - if (!self.inline) { - elm.innerHTML = html; - } - - // Update hidden form element - if ((form = DOM.getParent(self.id, 'form'))) { - each(form.elements, function(elm) { - if (elm.name == self.id) { - elm.value = html; - return false; - } - }); - } - } else { - elm.value = html; - } - - args.element = elm = null; - - if (args.set_dirty !== false) { - self.setDirty(false); - } - - return html; - }, - - /** - * Sets the specified content to the editor instance, this will cleanup the content before it gets set using - * the different cleanup rules options. - * - * @method setContent - * @param {String} content Content to set to editor, normally HTML contents but can be other formats as well. - * @param {Object} args Optional content object, this gets passed around through the whole set process. - * @return {String} HTML string that got set into the editor. - * @example - * // Sets the HTML contents of the activeEditor editor - * tinymce.activeEditor.setContent('<span>some</span> html'); - * - * // Sets the raw contents of the activeEditor editor - * tinymce.activeEditor.setContent('<span>some</span> html', {format: 'raw'}); - * - * // Sets the content of a specific editor (my_editor in this example) - * tinymce.get('my_editor').setContent(data); - * - * // Sets the bbcode contents of the activeEditor editor if the bbcode plugin was added - * tinymce.activeEditor.setContent('[b]some[/b] html', {format: 'bbcode'}); - */ - setContent: function(content, args) { - var self = this, - body = self.getBody(), - forcedRootBlockName, padd; - - // Setup args object - args = args || {}; - args.format = args.format || 'html'; - args.set = true; - args.content = content; - - // Do preprocessing - if (!args.no_events) { - self.fire('BeforeSetContent', args); - } - - content = args.content; - - // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content - // It will also be impossible to place the caret in the editor unless there is a BR element present - if (content.length === 0 || /^\s+$/.test(content)) { - padd = ie && ie < 11 ? '' : '<br data-mce-bogus="1">'; - - // Todo: There is a lot more root elements that need special padding - // so separate this and add all of them at some point. - if (body.nodeName == 'TABLE') { - content = '<tr><td>' + padd + '</td></tr>'; - } else if (/^(UL|OL)$/.test(body.nodeName)) { - content = '<li>' + padd + '</li>'; - } - - forcedRootBlockName = self.settings.forced_root_block; - - // Check if forcedRootBlock is configured and that the block is a valid child of the body - if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) { - // Padd with bogus BR elements on modern browsers and IE 7 and 8 since they don't render empty P tags properly - content = padd; - content = self.dom.createHTML(forcedRootBlockName, self.settings.forced_root_block_attrs, content); - } else if (!ie && !content) { - // We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret - content = '<br data-mce-bogus="1">'; - } - - self.dom.setHTML(body, content); - - self.fire('SetContent', args); - } else { - // Parse and serialize the html - if (args.format !== 'raw') { - content = new Serializer({ - validate: self.validate - }, self.schema).serialize( - self.parser.parse(content, { - isRootContent: true - }) - ); - } - - // Set the new cleaned contents to the editor - args.content = trim(content); - self.dom.setHTML(body, args.content); - - // Do post processing - if (!args.no_events) { - self.fire('SetContent', args); - } - - // Don't normalize selection if the focused element isn't the body in - // content editable mode since it will steal focus otherwise - /*if (!self.settings.content_editable || document.activeElement === self.getBody()) { - self.selection.normalize(); - }*/ - } - - return args.content; - }, - - /** - * Gets the content from the editor instance, this will cleanup the content before it gets returned using - * the different cleanup rules options. - * - * @method getContent - * @param {Object} args Optional content object, this gets passed around through the whole get process. - * @return {String} Cleaned content string, normally HTML contents. - * @example - * // Get the HTML contents of the currently active editor - * console.debug(tinymce.activeEditor.getContent()); - * - * // Get the raw contents of the currently active editor - * tinymce.activeEditor.getContent({format: 'raw'}); - * - * // Get content of a specific editor: - * tinymce.get('content id').getContent() - */ - getContent: function(args) { - var self = this, - content, body = self.getBody(); - - if (self.removed) { - return ''; - } - - // Setup args object - args = args || {}; - args.format = args.format || 'html'; - args.get = true; - args.getInner = true; - - // Do preprocessing - if (!args.no_events) { - self.fire('BeforeGetContent', args); - } - - // Get raw contents or by default the cleaned contents - if (args.format == 'raw') { - content = Tools.trim(self.serializer.getTrimmedContent()); - } else if (args.format == 'text') { - content = body.innerText || body.textContent; - } else { - content = self.serializer.serialize(body, args); - } - - // Trim whitespace in beginning/end of HTML - if (args.format != 'text') { - args.content = trim(content); - } else { - args.content = content; - } - - // Do post processing - if (!args.no_events) { - self.fire('GetContent', args); - } - - return args.content; - }, - - /** - * Inserts content at caret position. - * - * @method insertContent - * @param {String} content Content to insert. - * @param {Object} args Optional args to pass to insert call. - */ - insertContent: function(content, args) { - if (args) { - content = extend({ - content: content - }, args); - } - - this.execCommand('mceInsertContent', false, content); - }, - - /** - * Returns true/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents. - * - * The dirty state is automatically set to true if you do modifications to the content in other - * words when new undo levels is created or if you undo/redo to update the contents of the editor. It will also be set - * to false if you call editor.save(). - * - * @method isDirty - * @return {Boolean} True/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents. - * @example - * if (tinymce.activeEditor.isDirty()) - * alert("You must save your contents."); - */ - isDirty: function() { - return !this.isNotDirty; - }, - - /** - * Explicitly sets the dirty state. This will fire the dirty event if the editor dirty state is changed from false to true - * by invoking this method. - * - * @method setDirty - * @param {Boolean} state True/false if the editor is considered dirty. - * @example - * function ajaxSave() { - * var editor = tinymce.get('elm1'); - * - * // Save contents using some XHR call - * alert(editor.getContent()); - * - * editor.setDirty(false); // Force not dirty state - * } - */ - setDirty: function(state) { - var oldState = !this.isNotDirty; - - this.isNotDirty = !state; - - if (state && state != oldState) { - this.fire('dirty'); - } - }, - - /** - * Sets the editor mode. Mode can be for example "design", "code" or "readonly". - * - * @method setMode - * @param {String} mode Mode to set the editor in. - */ - setMode: function(mode) { - Mode.setMode(this, mode); - }, - - /** - * Returns the editors container element. The container element wrappes in - * all the elements added to the page for the editor. Such as UI, iframe etc. - * - * @method getContainer - * @return {Element} HTML DOM element for the editor container. - */ - getContainer: function() { - var self = this; - - if (!self.container) { - self.container = DOM.get(self.editorContainer || self.id + '_parent'); - } - - return self.container; - }, - - /** - * Returns the editors content area container element. The this element is the one who - * holds the iframe or the editable element. - * - * @method getContentAreaContainer - * @return {Element} HTML DOM element for the editor area container. - */ - getContentAreaContainer: function() { - return this.contentAreaContainer; - }, - - /** - * Returns the target element/textarea that got replaced with a TinyMCE editor instance. - * - * @method getElement - * @return {Element} HTML DOM element for the replaced element. - */ - getElement: function() { - if (!this.targetElm) { - this.targetElm = DOM.get(this.id); - } - - return this.targetElm; - }, - - /** - * Returns the iframes window object. - * - * @method getWin - * @return {Window} Iframe DOM window object. - */ - getWin: function() { - var self = this, - elm; - - if (!self.contentWindow) { - elm = self.iframeElement; - - if (elm) { - self.contentWindow = elm.contentWindow; - } - } - - return self.contentWindow; - }, - - /** - * Returns the iframes document object. - * - * @method getDoc - * @return {Document} Iframe DOM document object. - */ - getDoc: function() { - var self = this, - win; - - if (!self.contentDocument) { - win = self.getWin(); - - if (win) { - self.contentDocument = win.document; - } - } - - return self.contentDocument; - }, - - /** - * Returns the root element of the editable area. - * For a non-inline iframe-based editor, returns the iframe's body element. - * - * @method getBody - * @return {Element} The root element of the editable area. - */ - getBody: function() { - var doc = this.getDoc(); - return this.bodyElement || (doc ? doc.body : null); - }, - - /** - * URL converter function this gets executed each time a user adds an img, a or - * any other element that has a URL in it. This will be called both by the DOM and HTML - * manipulation functions. - * - * @method convertURL - * @param {string} url URL to convert. - * @param {string} name Attribute name src, href etc. - * @param {string/HTMLElement} elm Tag name or HTML DOM element depending on HTML or DOM insert. - * @return {string} Converted URL string. - */ - convertURL: function(url, name, elm) { - var self = this, - settings = self.settings; - - // Use callback instead - if (settings.urlconverter_callback) { - return self.execCallback('urlconverter_callback', url, elm, true, name); - } - - // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs - if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0 || url.length === 0) { - return url; - } - - // Convert to relative - if (settings.relative_urls) { - return self.documentBaseURI.toRelative(url); - } - - // Convert to absolute - url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host); - - return url; - }, - - /** - * Adds visual aid for tables, anchors etc so they can be more easily edited inside the editor. - * - * @method addVisual - * @param {Element} elm Optional root element to loop though to find tables etc that needs the visual aid. - */ - addVisual: function(elm) { - var self = this, - settings = self.settings, - dom = self.dom, - cls; - - elm = elm || self.getBody(); - - if (self.hasVisual === undefined) { - self.hasVisual = settings.visual; - } - - each(dom.select('table,a', elm), function(elm) { - var value; - - switch (elm.nodeName) { - case 'TABLE': - cls = settings.visual_table_class || 'mce-item-table'; - value = dom.getAttrib(elm, 'border'); - - if ((!value || value == '0') && self.hasVisual) { - dom.addClass(elm, cls); - } else { - dom.removeClass(elm, cls); - } - - return; - - case 'A': - if (!dom.getAttrib(elm, 'href', false)) { - value = dom.getAttrib(elm, 'name') || elm.id; - cls = settings.visual_anchor_class || 'mce-item-anchor'; - - if (value && self.hasVisual) { - dom.addClass(elm, cls); - } else { - dom.removeClass(elm, cls); - } - } - - return; - } - }); - - self.fire('VisualAid', { - element: elm, - hasVisual: self.hasVisual - }); - }, - - /** - * Removes the editor from the dom and tinymce collection. - * - * @method remove - */ - remove: function() { - var self = this; - - if (!self.removed) { - self.save(); - self.removed = 1; - self.unbindAllNativeEvents(); - - // Remove any hidden input - if (self.hasHiddenInput) { - DOM.remove(self.getElement().nextSibling); - } - - if (!self.inline) { - // IE 9 has a bug where the selection stops working if you place the - // caret inside the editor then remove the iframe - if (ie && ie < 10) { - self.getDoc().execCommand('SelectAll', false, null); - } - - DOM.setStyle(self.id, 'display', self.orgDisplay); - self.getBody().onload = null; // Prevent #6816 - } - - self.fire('remove'); - - self.editorManager.remove(self); - DOM.remove(self.getContainer()); - self._selectionOverrides.destroy(); - self.editorUpload.destroy(); - self.destroy(); - } - }, - - /** - * Destroys the editor instance by removing all events, element references or other resources - * that could leak memory. This method will be called automatically when the page is unloaded - * but you can also call it directly if you know what you are doing. - * - * @method destroy - * @param {Boolean} automatic Optional state if the destroy is an automatic destroy or user called one. - */ - destroy: function(automatic) { - var self = this, - form; - - // One time is enough - if (self.destroyed) { - return; - } - - // If user manually calls destroy and not remove - // Users seems to have logic that calls destroy instead of remove - if (!automatic && !self.removed) { - self.remove(); - return; - } - - if (!automatic) { - self.editorManager.off('beforeunload', self._beforeUnload); - - // Manual destroy - if (self.theme && self.theme.destroy) { - self.theme.destroy(); - } - - // Destroy controls, selection and dom - self.selection.destroy(); - self.dom.destroy(); - } - - form = self.formElement; - if (form) { - if (form._mceOldSubmit) { - form.submit = form._mceOldSubmit; - form._mceOldSubmit = null; - } - - DOM.unbind(form, 'submit reset', self.formEventDelegate); - } - - self.contentAreaContainer = self.formElement = self.container = self.editorContainer = null; - self.bodyElement = self.contentDocument = self.contentWindow = null; - self.iframeElement = self.targetElm = null; - - if (self.selection) { - self.selection = self.selection.win = self.selection.dom = self.selection.dom.doc = null; - } - - self.destroyed = 1; - }, - - /** - * Uploads all data uri/blob uri images in the editor contents to server. - * - * @method uploadImages - * @param {function} callback Optional callback with images and status for each image. - * @return {tinymce.util.Promise} Promise instance. - */ - uploadImages: function(callback) { - return this.editorUpload.uploadImages(callback); - }, - - // Internal functions - - _scanForImages: function() { - return this.editorUpload.scanForImages(); - } - }; - - extend(Editor.prototype, EditorObservable); - - return Editor; - } - ); - - /** - * I18n.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * I18n class that handles translation of TinyMCE UI. - * Uses po style with csharp style parameters. - * - * @class tinymce.util.I18n - */ - define( - 'tinymce.core.util.I18n', [ - "tinymce.core.util.Tools" - ], - function(Tools) { - "use strict"; - - var data = {}, - code = "en"; - - return { - /** - * Sets the current language code. - * - * @method setCode - * @param {String} newCode Current language code. - */ - setCode: function(newCode) { - if (newCode) { - code = newCode; - this.rtl = this.data[newCode] ? this.data[newCode]._dir === 'rtl' : false; - } - }, - - /** - * Returns the current language code. - * - * @method getCode - * @return {String} Current language code. - */ - getCode: function() { - return code; - }, - - /** - * Property gets set to true if a RTL language pack was loaded. - * - * @property rtl - * @type Boolean - */ - rtl: false, - - /** - * Adds translations for a specific language code. - * - * @method add - * @param {String} code Language code like sv_SE. - * @param {Array} items Name/value array with English en_US to sv_SE. - */ - add: function(code, items) { - var langData = data[code]; - - if (!langData) { - data[code] = langData = {}; - } - - for (var name in items) { - langData[name] = items[name]; - } - - this.setCode(code); - }, - - /** - * Translates the specified text. - * - * It has a few formats: - * I18n.translate("Text"); - * I18n.translate(["Text {0}/{1}", 0, 1]); - * I18n.translate({raw: "Raw string"}); - * - * @method translate - * @param {String/Object/Array} text Text to translate. - * @return {String} String that got translated. - */ - translate: function(text) { - var langData = data[code] || {}; - - /** - * number - string - * null, undefined and empty string - empty string - * array - comma-delimited string - * object - in [object Object] - * function - in [object Function] - * - * @param obj - * @returns {string} - */ - function toString(obj) { - if (Tools.is(obj, 'function')) { - return Object.prototype.toString.call(obj); - } - return !isEmpty(obj) ? '' + obj : ''; - } - - function isEmpty(text) { - return text === '' || text === null || Tools.is(text, 'undefined'); - } - - function getLangData(text) { - // make sure we work on a string and return a string - text = toString(text); - return Tools.hasOwn(langData, text) ? toString(langData[text]) : text; - } - - - if (isEmpty(text)) { - return ''; - } - - if (Tools.is(text, 'object') && Tools.hasOwn(text, 'raw')) { - return toString(text.raw); - } - - if (Tools.is(text, 'array')) { - var values = text.slice(1); - text = getLangData(text[0]).replace(/\{([0-9]+)\}/g, function($1, $2) { - return Tools.hasOwn(values, $2) ? toString(values[$2]) : $1; - }); - } - - return getLangData(text).replace(/{context:\w+}$/, ''); - }, - - data: data - }; - } - ); - /** - * FocusManager.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class manages the focus/blur state of the editor. This class is needed since some - * browsers fire false focus/blur states when the selection is moved to a UI dialog or similar. - * - * This class will fire two events focus and blur on the editor instances that got affected. - * It will also handle the restore of selection when the focus is lost and returned. - * - * @class tinymce.FocusManager - */ - define( - 'tinymce.core.FocusManager', [ - "tinymce.core.dom.DOMUtils", - "tinymce.core.util.Delay", - "tinymce.core.Env" - ], - function(DOMUtils, Delay, Env) { - var selectionChangeHandler, documentFocusInHandler, documentMouseUpHandler, DOM = DOMUtils.DOM; - - var isUIElement = function(editor, elm) { - var customSelector = editor ? editor.settings.custom_ui_selector : ''; - var parent = DOM.getParent(elm, function(elm) { - return ( - FocusManager.isEditorUIElement(elm) || - (customSelector ? editor.dom.is(elm, customSelector) : false) - ); - }); - return parent !== null; - }; - - var isInlineEditor = function(editor) { - return editor.inline === true; - }; - - var isElementOursideInlineEditor = function(editor, target) { - return isInlineEditor(editor) === false || editor.dom.isChildOf(target, editor.getBody()) === false; - }; - - /** - * Constructs a new focus manager instance. - * - * @constructor FocusManager - * @param {tinymce.EditorManager} editorManager Editor manager instance to handle focus for. - */ - function FocusManager(editorManager) { - function getActiveElement() { - try { - return document.activeElement; - } catch (ex) { - // IE sometimes fails to get the activeElement when resizing table - // TODO: Investigate this - return document.body; - } - } - - // We can't store a real range on IE 11 since it gets mutated so we need to use a bookmark object - // TODO: Move this to a separate range utils class since it's it's logic is present in Selection as well. - function createBookmark(dom, rng) { - if (rng && rng.startContainer) { - // Verify that the range is within the root of the editor - if (!dom.isChildOf(rng.startContainer, dom.getRoot()) || !dom.isChildOf(rng.endContainer, dom.getRoot())) { - return; - } - - return { - startContainer: rng.startContainer, - startOffset: rng.startOffset, - endContainer: rng.endContainer, - endOffset: rng.endOffset - }; - } - - return rng; - } - - function bookmarkToRng(editor, bookmark) { - var rng; - - if (bookmark.startContainer) { - rng = editor.getDoc().createRange(); - rng.setStart(bookmark.startContainer, bookmark.startOffset); - rng.setEnd(bookmark.endContainer, bookmark.endOffset); - } else { - rng = bookmark; - } - - return rng; - } - - function registerEvents(e) { - var editor = e.editor; - - editor.on('init', function() { - // Gecko/WebKit has ghost selections in iframes and IE only has one selection per browser tab - if (editor.inline || Env.ie) { - // Use the onbeforedeactivate event when available since it works better see #7023 - if ("onbeforedeactivate" in document && Env.ie < 9) { - editor.dom.bind(editor.getBody(), 'beforedeactivate', function(e) { - if (e.target != editor.getBody()) { - return; - } - - try { - editor.lastRng = editor.selection.getRng(); - } catch (ex) { - // IE throws "Unexcpected call to method or property access" some times so lets ignore it - } - }); - } else { - // On other browsers take snapshot on nodechange in inline mode since they have Ghost selections for iframes - editor.on('nodechange mouseup keyup', function(e) { - var node = getActiveElement(); - - // Only act on manual nodechanges - if (e.type == 'nodechange' && e.selectionChange) { - return; - } - - // IE 11 reports active element as iframe not body of iframe - if (node && node.id == editor.id + '_ifr') { - node = editor.getBody(); - } - - if (editor.dom.isChildOf(node, editor.getBody())) { - editor.lastRng = editor.selection.getRng(); - } - }); - } - - // Handles the issue with WebKit not retaining selection within inline document - // If the user releases the mouse out side the body since a mouse up event wont occur on the body - if (Env.webkit && !selectionChangeHandler) { - selectionChangeHandler = function() { - var activeEditor = editorManager.activeEditor; - - if (activeEditor && activeEditor.selection) { - var rng = activeEditor.selection.getRng(); - - // Store when it's non collapsed - if (rng && !rng.collapsed) { - editor.lastRng = rng; - } - } - }; - - DOM.bind(document, 'selectionchange', selectionChangeHandler); - } - } - }); - - editor.on('setcontent', function() { - editor.lastRng = null; - }); - - // Remove last selection bookmark on mousedown see #6305 - editor.on('mousedown', function() { - editor.selection.lastFocusBookmark = null; - }); - - editor.on('focusin', function() { - var focusedEditor = editorManager.focusedEditor, - lastRng; - - if (editor.selection.lastFocusBookmark) { - lastRng = bookmarkToRng(editor, editor.selection.lastFocusBookmark); - editor.selection.lastFocusBookmark = null; - editor.selection.setRng(lastRng); - } - - if (focusedEditor != editor) { - if (focusedEditor) { - focusedEditor.fire('blur', { - focusedEditor: editor - }); - } - - editorManager.setActive(editor); - editorManager.focusedEditor = editor; - editor.fire('focus', { - blurredEditor: focusedEditor - }); - editor.focus(true); - } - - editor.lastRng = null; - }); - - editor.on('focusout', function() { - Delay.setEditorTimeout(editor, function() { - var focusedEditor = editorManager.focusedEditor; - - // Still the same editor the blur was outside any editor UI - if (!isUIElement(editor, getActiveElement()) && focusedEditor == editor) { - editor.fire('blur', { - focusedEditor: null - }); - editorManager.focusedEditor = null; - - // Make sure selection is valid could be invalid if the editor is blured and removed before the timeout occurs - if (editor.selection) { - editor.selection.lastFocusBookmark = null; - } - } - }); - }); - - // Check if focus is moved to an element outside the active editor by checking if the target node - // isn't within the body of the activeEditor nor a UI element such as a dialog child control - if (!documentFocusInHandler) { - documentFocusInHandler = function(e) { - var activeEditor = editorManager.activeEditor, - target; - - target = e.target; - - if (activeEditor && target.ownerDocument === document) { - // Check to make sure we have a valid selection don't update the bookmark if it's - // a focusin to the body of the editor see #7025 - if (activeEditor.selection && target !== activeEditor.getBody() && isElementOursideInlineEditor(editor, target)) { - activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.dom, activeEditor.lastRng); - } - - // Fire a blur event if the element isn't a UI element - if (target !== document.body && !isUIElement(activeEditor, target) && editorManager.focusedEditor === activeEditor) { - activeEditor.fire('blur', { - focusedEditor: null - }); - editorManager.focusedEditor = null; - } - } - }; - - DOM.bind(document, 'focusin', documentFocusInHandler); - } - - // Handle edge case when user starts the selection inside the editor and releases - // the mouse outside the editor producing a new selection. This weird workaround is needed since - // Gecko doesn't have the "selectionchange" event we need to do this. Fixes: #6843 - if (editor.inline && !documentMouseUpHandler) { - documentMouseUpHandler = function(e) { - var activeEditor = editorManager.activeEditor, - dom = activeEditor.dom; - - if (activeEditor.inline && dom && !dom.isChildOf(e.target, activeEditor.getBody())) { - var rng = activeEditor.selection.getRng(); - - if (!rng.collapsed) { - activeEditor.lastRng = rng; - } - } - }; - - DOM.bind(document, 'mouseup', documentMouseUpHandler); - } - } - - function unregisterDocumentEvents(e) { - if (editorManager.focusedEditor == e.editor) { - editorManager.focusedEditor = null; - } - - if (!editorManager.activeEditor) { - DOM.unbind(document, 'selectionchange', selectionChangeHandler); - DOM.unbind(document, 'focusin', documentFocusInHandler); - DOM.unbind(document, 'mouseup', documentMouseUpHandler); - selectionChangeHandler = documentFocusInHandler = documentMouseUpHandler = null; - } - } - - editorManager.on('AddEditor', registerEvents); - editorManager.on('RemoveEditor', unregisterDocumentEvents); - } - - /** - * Returns true if the specified element is part of the UI for example an button or text input. - * - * @method isEditorUIElement - * @param {Element} elm Element to check if it's part of the UI or not. - * @return {Boolean} True/false state if the element is part of the UI or not. - */ - FocusManager.isEditorUIElement = function(elm) { - // Needs to be converted to string since svg can have focus: #6776 - return elm.className.toString().indexOf('mce-') !== -1; - }; - - FocusManager._isUIElement = isUIElement; - - return FocusManager; - } - ); - - /** - * LegacyInput.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Converts legacy input to modern HTML. - * - * @class tinymce.LegacyInput - * @private - */ - define( - 'tinymce.core.LegacyInput', [ - "tinymce.core.util.Tools" - ], - function(Tools) { - var each = Tools.each, - explode = Tools.explode; - - var register = function(EditorManager) { - EditorManager.on('AddEditor', function(e) { - var editor = e.editor; - - editor.on('preInit', function() { - var filters, fontSizes, dom, settings = editor.settings; - - function replaceWithSpan(node, styles) { - each(styles, function(value, name) { - if (value) { - dom.setStyle(node, name, value); - } - }); - - dom.rename(node, 'span'); - } - - function convert(e) { - dom = editor.dom; - - if (settings.convert_fonts_to_spans) { - each(dom.select('font,u,strike', e.node), function(node) { - filters[node.nodeName.toLowerCase()](dom, node); - }); - } - } - - if (settings.inline_styles) { - fontSizes = explode(settings.font_size_legacy_values); - - filters = { - font: function(dom, node) { - replaceWithSpan(node, { - backgroundColor: node.style.backgroundColor, - color: node.color, - fontFamily: node.face, - fontSize: fontSizes[parseInt(node.size, 10) - 1] - }); - }, - - u: function(dom, node) { - // HTML5 allows U element - if (editor.settings.schema === "html4") { - replaceWithSpan(node, { - textDecoration: 'underline' - }); - } - }, - - strike: function(dom, node) { - replaceWithSpan(node, { - textDecoration: 'line-through' - }); - } - }; - - editor.on('PreProcess SetContent', convert); - } - }); - }); - }; - - return { - register: register - }; - } - ); - /** - * EditorManager.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class used as a factory for manager for tinymce.Editor instances. - * - * @example - * tinymce.EditorManager.init({}); - * - * @class tinymce.EditorManager - * @mixes tinymce.util.Observable - * @static - */ - define( - 'tinymce.core.EditorManager', [ - 'tinymce.core.AddOnManager', - 'tinymce.core.dom.DomQuery', - 'tinymce.core.dom.DOMUtils', - 'tinymce.core.Editor', - 'tinymce.core.Env', - 'tinymce.core.ErrorReporter', - 'tinymce.core.FocusManager', - 'tinymce.core.LegacyInput', - 'tinymce.core.util.I18n', - 'tinymce.core.util.Observable', - 'tinymce.core.util.Promise', - 'tinymce.core.util.Tools', - 'tinymce.core.util.URI' - ], - function(AddOnManager, DomQuery, DOMUtils, Editor, Env, ErrorReporter, FocusManager, LegacyInput, I18n, Observable, Promise, Tools, URI) { - var DOM = DOMUtils.DOM; - var explode = Tools.explode, - each = Tools.each, - extend = Tools.extend; - var instanceCounter = 0, - beforeUnloadDelegate, EditorManager, boundGlobalEvents = false; - - function globalEventDelegate(e) { - each(EditorManager.editors, function(editor) { - if (e.type === 'scroll') { - editor.fire('ScrollWindow', e); - } else { - editor.fire('ResizeWindow', e); - } - }); - } - - function toggleGlobalEvents(editors, state) { - if (state !== boundGlobalEvents) { - if (state) { - DomQuery(window).on('resize scroll', globalEventDelegate); - } else { - DomQuery(window).off('resize scroll', globalEventDelegate); - } - - boundGlobalEvents = state; - } - } - - function removeEditorFromList(editor) { - var editors = EditorManager.editors, - removedFromList; - - delete editors[editor.id]; - - for (var i = 0; i < editors.length; i++) { - if (editors[i] == editor) { - editors.splice(i, 1); - removedFromList = true; - break; - } - } - - // Select another editor since the active one was removed - if (EditorManager.activeEditor == editor) { - EditorManager.activeEditor = editors[0]; - } - - // Clear focusedEditor if necessary, so that we don't try to blur the destroyed editor - if (EditorManager.focusedEditor == editor) { - EditorManager.focusedEditor = null; - } - - return removedFromList; - } - - function purgeDestroyedEditor(editor) { - // User has manually destroyed the editor lets clean up the mess - if (editor && editor.initialized && !(editor.getContainer() || editor.getBody()).parentNode) { - removeEditorFromList(editor); - editor.unbindAllNativeEvents(); - editor.destroy(true); - editor.removed = true; - editor = null; - } - - return editor; - } - - EditorManager = { - /** - * Dom query instance. - * - * @property $ - * @type tinymce.dom.DomQuery - */ - $: DomQuery, - - /** - * Major version of TinyMCE build. - * - * @property majorVersion - * @type String - */ - majorVersion: '4', - - /** - * Minor version of TinyMCE build. - * - * @property minorVersion - * @type String - */ - minorVersion: '6.4', - - /** - * Release date of TinyMCE build. - * - * @property releaseDate - * @type String - */ - releaseDate: '2017-06-13', - - /** - * Collection of editor instances. - * - * @property editors - * @type Object - * @example - * for (edId in tinymce.editors) - * tinymce.editors[edId].save(); - */ - editors: [], - - /** - * Collection of language pack data. - * - * @property i18n - * @type Object - */ - i18n: I18n, - - /** - * Currently active editor instance. - * - * @property activeEditor - * @type tinymce.Editor - * @example - * tinyMCE.activeEditor.selection.getContent(); - * tinymce.EditorManager.activeEditor.selection.getContent(); - */ - activeEditor: null, - - setup: function() { - var self = this, - baseURL, documentBaseURL, suffix = "", - preInit, src; - - // Get base URL for the current document - documentBaseURL = URI.getDocumentBaseUrl(document.location); - - // Check if the URL is a document based format like: http://site/dir/file and file:/// - // leave other formats like applewebdata://... intact - if (/^[^:]+:\/\/\/?[^\/]+\//.test(documentBaseURL)) { - documentBaseURL = documentBaseURL.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, ''); - - if (!/[\/\\]$/.test(documentBaseURL)) { - documentBaseURL += '/'; - } - } - - // If tinymce is defined and has a base use that or use the old tinyMCEPreInit - preInit = window.tinymce || window.tinyMCEPreInit; - if (preInit) { - baseURL = preInit.base || preInit.baseURL; - suffix = preInit.suffix; - } else { - // Get base where the tinymce script is located - var scripts = document.getElementsByTagName('script'); - for (var i = 0; i < scripts.length; i++) { - src = scripts[i].src; - - // Script types supported: - // tinymce.js tinymce.min.js tinymce.dev.js - // tinymce.jquery.js tinymce.jquery.min.js tinymce.jquery.dev.js - // tinymce.full.js tinymce.full.min.js tinymce.full.dev.js - var srcScript = src.substring(src.lastIndexOf('/')); - if (/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(src)) { - if (srcScript.indexOf('.min') != -1) { - suffix = '.min'; - } - - baseURL = src.substring(0, src.lastIndexOf('/')); - break; - } - } - - // We didn't find any baseURL by looking at the script elements - // Try to use the document.currentScript as a fallback - if (!baseURL && document.currentScript) { - src = document.currentScript.src; - - if (src.indexOf('.min') != -1) { - suffix = '.min'; - } - - baseURL = src.substring(0, src.lastIndexOf('/')); - } - } - - /** - * Base URL where the root directory if TinyMCE is located. - * - * @property baseURL - * @type String - */ - self.baseURL = new URI(documentBaseURL).toAbsolute(baseURL); - - /** - * Document base URL where the current document is located. - * - * @property documentBaseURL - * @type String - */ - self.documentBaseURL = documentBaseURL; - - /** - * Absolute baseURI for the installation path of TinyMCE. - * - * @property baseURI - * @type tinymce.util.URI - */ - self.baseURI = new URI(self.baseURL); - - /** - * Current suffix to add to each plugin/theme that gets loaded for example ".min". - * - * @property suffix - * @type String - */ - self.suffix = suffix; - - self.focusManager = new FocusManager(self); - }, - - /** - * Overrides the default settings for editor instances. - * - * @method overrideDefaults - * @param {Object} defaultSettings Defaults settings object. - */ - overrideDefaults: function(defaultSettings) { - var baseUrl, suffix; - - baseUrl = defaultSettings.base_url; - if (baseUrl) { - this.baseURL = new URI(this.documentBaseURL).toAbsolute(baseUrl.replace(/\/+$/, '')); - this.baseURI = new URI(this.baseURL); - } - - suffix = defaultSettings.suffix; - if (defaultSettings.suffix) { - this.suffix = suffix; - } - - this.defaultSettings = defaultSettings; - - var pluginBaseUrls = defaultSettings.plugin_base_urls; - for (var name in pluginBaseUrls) { - AddOnManager.PluginManager.urls[name] = pluginBaseUrls[name]; - } - }, - - /** - * Initializes a set of editors. This method will create editors based on various settings. - * - * @method init - * @param {Object} settings Settings object to be passed to each editor instance. - * @return {tinymce.util.Promise} Promise that gets resolved with an array of editors when all editor instances are initialized. - * @example - * // Initializes a editor using the longer method - * tinymce.EditorManager.init({ - * some_settings : 'some value' - * }); - * - * // Initializes a editor instance using the shorter version and with a promise - * tinymce.init({ - * some_settings : 'some value' - * }).then(function(editors) { - * ... - * }); - */ - init: function(settings) { - var self = this, - result, invalidInlineTargets; - - invalidInlineTargets = Tools.makeMap( - 'area base basefont br col frame hr img input isindex link meta param embed source wbr track ' + - 'colgroup option tbody tfoot thead tr script noscript style textarea video audio iframe object menu', - ' ' - ); - - function isInvalidInlineTarget(settings, elm) { - return settings.inline && elm.tagName.toLowerCase() in invalidInlineTargets; - } - - function createId(elm) { - var id = elm.id; - - // Use element id, or unique name or generate a unique id - if (!id) { - id = elm.name; - - if (id && !DOM.get(id)) { - id = elm.name; - } else { - // Generate unique name - id = DOM.uniqueId(); - } - - elm.setAttribute('id', id); - } - - return id; - } - - function execCallback(name) { - var callback = settings[name]; - - if (!callback) { - return; - } - - return callback.apply(self, Array.prototype.slice.call(arguments, 2)); - } - - function hasClass(elm, className) { - return className.constructor === RegExp ? className.test(elm.className) : DOM.hasClass(elm, className); - } - - function findTargets(settings) { - var l, targets = []; - - if (Env.ie && Env.ie < 11) { - ErrorReporter.initError( - 'TinyMCE does not support the browser you are using. For a list of supported' + - ' browsers please see: https://www.tinymce.com/docs/get-started/system-requirements/' - ); - return []; - } - - if (settings.types) { - each(settings.types, function(type) { - targets = targets.concat(DOM.select(type.selector)); - }); - - return targets; - } else if (settings.selector) { - return DOM.select(settings.selector); - } else if (settings.target) { - return [settings.target]; - } - - // Fallback to old setting - switch (settings.mode) { - case "exact": - l = settings.elements || ''; - - if (l.length > 0) { - each(explode(l), function(id) { - var elm; - - if ((elm = DOM.get(id))) { - targets.push(elm); - } else { - each(document.forms, function(f) { - each(f.elements, function(e) { - if (e.name === id) { - id = 'mce_editor_' + instanceCounter++; - DOM.setAttrib(e, 'id', id); - targets.push(e); - } - }); - }); - } - }); - } - break; - - case "textareas": - case "specific_textareas": - each(DOM.select('textarea'), function(elm) { - if (settings.editor_deselector && hasClass(elm, settings.editor_deselector)) { - return; - } - - if (!settings.editor_selector || hasClass(elm, settings.editor_selector)) { - targets.push(elm); - } - }); - break; - } - - return targets; - } - - var provideResults = function(editors) { - result = editors; - }; - - function initEditors() { - var initCount = 0, - editors = [], - targets; - - function createEditor(id, settings, targetElm) { - var editor = new Editor(id, settings, self); - - editors.push(editor); - - editor.on('init', function() { - if (++initCount === targets.length) { - provideResults(editors); - } - }); - - editor.targetElm = editor.targetElm || targetElm; - editor.render(); - } - - DOM.unbind(window, 'ready', initEditors); - execCallback('onpageload'); - - targets = DomQuery.unique(findTargets(settings)); - - // TODO: Deprecate this one - if (settings.types) { - each(settings.types, function(type) { - Tools.each(targets, function(elm) { - if (DOM.is(elm, type.selector)) { - createEditor(createId(elm), extend({}, settings, type), elm); - return false; - } - - return true; - }); - }); - - return; - } - - Tools.each(targets, function(elm) { - purgeDestroyedEditor(self.get(elm.id)); - }); - - targets = Tools.grep(targets, function(elm) { - return !self.get(elm.id); - }); - - if (targets.length === 0) { - provideResults([]); - } else { - each(targets, function(elm) { - if (isInvalidInlineTarget(settings, elm)) { - ErrorReporter.initError('Could not initialize inline editor on invalid inline target element', elm); - } else { - createEditor(createId(elm), settings, elm); - } - }); - } - } - - self.settings = settings; - DOM.bind(window, 'ready', initEditors); - - return new Promise(function(resolve) { - if (result) { - resolve(result); - } else { - provideResults = function(editors) { - resolve(editors); - }; - } - }); - }, - - /** - * Returns a editor instance by id. - * - * @method get - * @param {String/Number} id Editor instance id or index to return. - * @return {tinymce.Editor} Editor instance to return. - * @example - * // Adds an onclick event to an editor by id (shorter version) - * tinymce.get('mytextbox').on('click', function(e) { - * ed.windowManager.alert('Hello world!'); - * }); - * - * // Adds an onclick event to an editor by id (longer version) - * tinymce.EditorManager.get('mytextbox').on('click', function(e) { - * ed.windowManager.alert('Hello world!'); - * }); - */ - get: function(id) { - if (!arguments.length) { - return this.editors; - } - - return id in this.editors ? this.editors[id] : null; - }, - - /** - * Adds an editor instance to the editor collection. This will also set it as the active editor. - * - * @method add - * @param {tinymce.Editor} editor Editor instance to add to the collection. - * @return {tinymce.Editor} The same instance that got passed in. - */ - add: function(editor) { - var self = this, - editors = self.editors; - - // Add named and index editor instance - editors[editor.id] = editor; - editors.push(editor); - - toggleGlobalEvents(editors, true); - - // Doesn't call setActive method since we don't want - // to fire a bunch of activate/deactivate calls while initializing - self.activeEditor = editor; - - self.fire('AddEditor', { - editor: editor - }); - - if (!beforeUnloadDelegate) { - beforeUnloadDelegate = function() { - self.fire('BeforeUnload'); - }; - - DOM.bind(window, 'beforeunload', beforeUnloadDelegate); - } - - return editor; - }, - - /** - * Creates an editor instance and adds it to the EditorManager collection. - * - * @method createEditor - * @param {String} id Instance id to use for editor. - * @param {Object} settings Editor instance settings. - * @return {tinymce.Editor} Editor instance that got created. - */ - createEditor: function(id, settings) { - return this.add(new Editor(id, settings, this)); - }, - - /** - * Removes a editor or editors form page. - * - * @example - * // Remove all editors bound to divs - * tinymce.remove('div'); - * - * // Remove all editors bound to textareas - * tinymce.remove('textarea'); - * - * // Remove all editors - * tinymce.remove(); - * - * // Remove specific instance by id - * tinymce.remove('#id'); - * - * @method remove - * @param {tinymce.Editor/String/Object} [selector] CSS selector or editor instance to remove. - * @return {tinymce.Editor} The editor that got passed in will be return if it was found otherwise null. - */ - remove: function(selector) { - var self = this, - i, editors = self.editors, - editor; - - // Remove all editors - if (!selector) { - for (i = editors.length - 1; i >= 0; i--) { - self.remove(editors[i]); - } - - return; - } - - // Remove editors by selector - if (typeof selector == "string") { - selector = selector.selector || selector; - - each(DOM.select(selector), function(elm) { - editor = editors[elm.id]; - - if (editor) { - self.remove(editor); - } - }); - - return; - } - - // Remove specific editor - editor = selector; - - // Not in the collection - if (!editors[editor.id]) { - return null; - } - - if (removeEditorFromList(editor)) { - self.fire('RemoveEditor', { - editor: editor - }); - } - - if (!editors.length) { - DOM.unbind(window, 'beforeunload', beforeUnloadDelegate); - } - - editor.remove(); - - toggleGlobalEvents(editors, editors.length > 0); - - return editor; - }, - - /** - * Executes a specific command on the currently active editor. - * - * @method execCommand - * @param {String} cmd Command to perform for example Bold. - * @param {Boolean} ui Optional boolean state if a UI should be presented for the command or not. - * @param {String} value Optional value parameter like for example an URL to a link. - * @return {Boolean} true/false if the command was executed or not. - */ - execCommand: function(cmd, ui, value) { - var self = this, - editor = self.get(value); - - // Manager commands - switch (cmd) { - case "mceAddEditor": - if (!self.get(value)) { - new Editor(value, self.settings, self).render(); - } - - return true; - - case "mceRemoveEditor": - if (editor) { - editor.remove(); - } - - return true; - - case 'mceToggleEditor': - if (!editor) { - self.execCommand('mceAddEditor', 0, value); - return true; - } - - if (editor.isHidden()) { - editor.show(); - } else { - editor.hide(); - } - - return true; - } - - // Run command on active editor - if (self.activeEditor) { - return self.activeEditor.execCommand(cmd, ui, value); - } - - return false; - }, - - /** - * Calls the save method on all editor instances in the collection. This can be useful when a form is to be submitted. - * - * @method triggerSave - * @example - * // Saves all contents - * tinyMCE.triggerSave(); - */ - triggerSave: function() { - each(this.editors, function(editor) { - editor.save(); - }); - }, - - /** - * Adds a language pack, this gets called by the loaded language files like en.js. - * - * @method addI18n - * @param {String} code Optional language code. - * @param {Object} items Name/value object with translations. - */ - addI18n: function(code, items) { - I18n.add(code, items); - }, - - /** - * Translates the specified string using the language pack items. - * - * @method translate - * @param {String/Array/Object} text String to translate - * @return {String} Translated string. - */ - translate: function(text) { - return I18n.translate(text); - }, - - /** - * Sets the active editor instance and fires the deactivate/activate events. - * - * @method setActive - * @param {tinymce.Editor} editor Editor instance to set as the active instance. - */ - setActive: function(editor) { - var activeEditor = this.activeEditor; - - if (this.activeEditor != editor) { - if (activeEditor) { - activeEditor.fire('deactivate', { - relatedTarget: editor - }); - } - - editor.fire('activate', { - relatedTarget: activeEditor - }); - } - - this.activeEditor = editor; - } - }; - - extend(EditorManager, Observable); - - EditorManager.setup(); - LegacyInput.register(EditorManager); - - return EditorManager; - } - ); - - /** - * XHR.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class enables you to send XMLHTTPRequests cross browser. - * @class tinymce.util.XHR - * @mixes tinymce.util.Observable - * @static - * @example - * // Sends a low level Ajax request - * tinymce.util.XHR.send({ - * url: 'someurl', - * success: function(text) { - * console.debug(text); - * } - * }); - * - * // Add custom header to XHR request - * tinymce.util.XHR.on('beforeSend', function(e) { - * e.xhr.setRequestHeader('X-Requested-With', 'Something'); - * }); - */ - define( - 'tinymce.core.util.XHR', [ - "tinymce.core.util.Observable", - "tinymce.core.util.Tools" - ], - function(Observable, Tools) { - var XHR = { - /** - * Sends a XMLHTTPRequest. - * Consult the Wiki for details on what settings this method takes. - * - * @method send - * @param {Object} settings Object will target URL, callbacks and other info needed to make the request. - */ - send: function(settings) { - var xhr, count = 0; - - function ready() { - if (!settings.async || xhr.readyState == 4 || count++ > 10000) { - if (settings.success && count < 10000 && xhr.status == 200) { - settings.success.call(settings.success_scope, '' + xhr.responseText, xhr, settings); - } else if (settings.error) { - settings.error.call(settings.error_scope, count > 10000 ? 'TIMED_OUT' : 'GENERAL', xhr, settings); - } - - xhr = null; - } else { - setTimeout(ready, 10); - } - } - - // Default settings - settings.scope = settings.scope || this; - settings.success_scope = settings.success_scope || settings.scope; - settings.error_scope = settings.error_scope || settings.scope; - settings.async = settings.async === false ? false : true; - settings.data = settings.data || ''; - - XHR.fire('beforeInitialize', { - settings: settings - }); - - xhr = new XMLHttpRequest(); - - if (xhr) { - if (xhr.overrideMimeType) { - xhr.overrideMimeType(settings.content_type); - } - - xhr.open(settings.type || (settings.data ? 'POST' : 'GET'), settings.url, settings.async); - - if (settings.crossDomain) { - xhr.withCredentials = true; - } - - if (settings.content_type) { - xhr.setRequestHeader('Content-Type', settings.content_type); - } - - if (settings.requestheaders) { - Tools.each(settings.requestheaders, function(header) { - xhr.setRequestHeader(header.key, header.value); - }); - } - - xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - - xhr = XHR.fire('beforeSend', { - xhr: xhr, - settings: settings - }).xhr; - xhr.send(settings.data); - - // Syncronous request - if (!settings.async) { - return ready(); - } - - // Wait for response, onReadyStateChange can not be used since it leaks memory in IE - setTimeout(ready, 10); - } - } - }; - - Tools.extend(XHR, Observable); - - return XHR; - } - ); - - /** - * JSON.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * JSON parser and serializer class. - * - * @class tinymce.util.JSON - * @static - * @example - * // JSON parse a string into an object - * var obj = tinymce.util.JSON.parse(somestring); - * - * // JSON serialize a object into an string - * var str = tinymce.util.JSON.serialize(obj); - */ - define( - 'tinymce.core.util.JSON', [], - function() { - function serialize(o, quote) { - var i, v, t, name; - - quote = quote || '"'; - - if (o === null) { - return 'null'; - } - - t = typeof o; - - if (t == 'string') { - v = '\bb\tt\nn\ff\rr\""\'\'\\\\'; - - /*eslint no-control-regex:0 */ - return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) { - // Make sure single quotes never get encoded inside double quotes for JSON compatibility - if (quote === '"' && a === "'") { - return a; - } - - i = v.indexOf(b); - - if (i + 1) { - return '\\' + v.charAt(i + 1); - } - - a = b.charCodeAt().toString(16); - - return '\\u' + '0000'.substring(a.length) + a; - }) + quote; - } - - if (t == 'object') { - if (o.hasOwnProperty && Object.prototype.toString.call(o) === '[object Array]') { - for (i = 0, v = '['; i < o.length; i++) { - v += (i > 0 ? ',' : '') + serialize(o[i], quote); - } - - return v + ']'; - } - - v = '{'; - - for (name in o) { - if (o.hasOwnProperty(name)) { - v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name + - quote + ':' + serialize(o[name], quote) : ''; - } - } - - return v + '}'; - } - - return '' + o; - } - - return { - /** - * Serializes the specified object as a JSON string. - * - * @method serialize - * @param {Object} obj Object to serialize as a JSON string. - * @param {String} quote Optional quote string defaults to ". - * @return {string} JSON string serialized from input. - */ - serialize: serialize, - - /** - * Unserializes/parses the specified JSON string into a object. - * - * @method parse - * @param {string} s JSON String to parse into a JavaScript object. - * @return {Object} Object from input JSON string or undefined if it failed. - */ - parse: function(text) { - try { - // Trick uglify JS - return window[String.fromCharCode(101) + 'val']('(' + text + ')'); - } catch (ex) { - // Ignore - } - } - - /**#@-*/ - }; - } - ); - - /** - * JSONRequest.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class enables you to use JSON-RPC to call backend methods. - * - * @class tinymce.util.JSONRequest - * @example - * var json = new tinymce.util.JSONRequest({ - * url: 'somebackend.php' - * }); - * - * // Send RPC call 1 - * json.send({ - * method: 'someMethod1', - * params: ['a', 'b'], - * success: function(result) { - * console.dir(result); - * } - * }); - * - * // Send RPC call 2 - * json.send({ - * method: 'someMethod2', - * params: ['a', 'b'], - * success: function(result) { - * console.dir(result); - * } - * }); - */ - define( - 'tinymce.core.util.JSONRequest', [ - "tinymce.core.util.JSON", - "tinymce.core.util.XHR", - "tinymce.core.util.Tools" - ], - function(JSON, XHR, Tools) { - var extend = Tools.extend; - - function JSONRequest(settings) { - this.settings = extend({}, settings); - this.count = 0; - } - - /** - * Simple helper function to send a JSON-RPC request without the need to initialize an object. - * Consult the Wiki API documentation for more details on what you can pass to this function. - * - * @method sendRPC - * @static - * @param {Object} o Call object where there are three field id, method and params this object should also contain callbacks etc. - */ - JSONRequest.sendRPC = function(o) { - return new JSONRequest().send(o); - }; - - JSONRequest.prototype = { - /** - * Sends a JSON-RPC call. Consult the Wiki API documentation for more details on what you can pass to this function. - * - * @method send - * @param {Object} args Call object where there are three field id, method and params this object should also contain callbacks etc. - */ - send: function(args) { - var ecb = args.error, - scb = args.success; - - args = extend(this.settings, args); - - args.success = function(c, x) { - c = JSON.parse(c); - - if (typeof c == 'undefined') { - c = { - error: 'JSON Parse error.' - }; - } - - if (c.error) { - ecb.call(args.error_scope || args.scope, c.error, x); - } else { - scb.call(args.success_scope || args.scope, c.result); - } - }; - - args.error = function(ty, x) { - if (ecb) { - ecb.call(args.error_scope || args.scope, ty, x); - } - }; - - args.data = JSON.serialize({ - id: args.id || 'c' + (this.count++), - method: args.method, - params: args.params - }); - - // JSON content type for Ruby on rails. Bug: #1883287 - args.content_type = 'application/json'; - - XHR.send(args); - } - }; - - return JSONRequest; - } - ); - /** - * JSONP.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.util.JSONP', [ - "tinymce.core.dom.DOMUtils" - ], - function(DOMUtils) { - return { - callbacks: {}, - count: 0, - - send: function(settings) { - var self = this, - dom = DOMUtils.DOM, - count = settings.count !== undefined ? settings.count : self.count; - var id = 'tinymce_jsonp_' + count; - - self.callbacks[count] = function(json) { - dom.remove(id); - delete self.callbacks[count]; - - settings.callback(json); - }; - - dom.add(dom.doc.body, 'script', { - id: id, - src: settings.url, - type: 'text/javascript' - }); - - self.count++; - } - }; - } - ); - /** - * LocalStorage.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class will simulate LocalStorage on IE 7 and return the native version on modern browsers. - * Storage is done using userData on IE 7 and a special serialization format. The format is designed - * to be as small as possible by making sure that the keys and values doesn't need to be encoded. This - * makes it possible to store for example HTML data. - * - * Storage format for userData: - * <base 32 key length>,<key string>,<base 32 value length>,<value>,... - * - * For example this data key1=value1,key2=value2 would be: - * 4,key1,6,value1,4,key2,6,value2 - * - * @class tinymce.util.LocalStorage - * @static - * @version 4.0 - * @example - * tinymce.util.LocalStorage.setItem('key', 'value'); - * var value = tinymce.util.LocalStorage.getItem('key'); - */ - define( - 'tinymce.core.util.LocalStorage', [], - function() { - var LocalStorage, storageElm, items, keys, userDataKey, hasOldIEDataSupport; - - // Check for native support - try { - if (window.localStorage) { - return localStorage; - } - } catch (ex) { - // Ignore - } - - userDataKey = "tinymce"; - storageElm = document.documentElement; - hasOldIEDataSupport = !!storageElm.addBehavior; - - if (hasOldIEDataSupport) { - storageElm.addBehavior('#default#userData'); - } - - /** - * Gets the keys names and updates LocalStorage.length property. Since IE7 doesn't have any getters/setters. - */ - function updateKeys() { - keys = []; - - for (var key in items) { - keys.push(key); - } - - LocalStorage.length = keys.length; - } - - /** - * Loads the userData string and parses it into the items structure. - */ - function load() { - var key, data, value, pos = 0; - - items = {}; - - // localStorage can be disabled on WebKit/Gecko so make a dummy storage - if (!hasOldIEDataSupport) { - return; - } - - function next(end) { - var value, nextPos; - - nextPos = end !== undefined ? pos + end : data.indexOf(',', pos); - if (nextPos === -1 || nextPos > data.length) { - return null; - } - - value = data.substring(pos, nextPos); - pos = nextPos + 1; - - return value; - } - - storageElm.load(userDataKey); - data = storageElm.getAttribute(userDataKey) || ''; - - do { - var offset = next(); - if (offset === null) { - break; - } - - key = next(parseInt(offset, 32) || 0); - if (key !== null) { - offset = next(); - if (offset === null) { - break; - } - - value = next(parseInt(offset, 32) || 0); - - if (key) { - items[key] = value; - } - } - } while (key !== null); - - updateKeys(); - } - - /** - * Saves the items structure into a the userData format. - */ - function save() { - var value, data = ''; - - // localStorage can be disabled on WebKit/Gecko so make a dummy storage - if (!hasOldIEDataSupport) { - return; - } - - for (var key in items) { - value = items[key]; - data += (data ? ',' : '') + key.length.toString(32) + ',' + key + ',' + value.length.toString(32) + ',' + value; - } - - storageElm.setAttribute(userDataKey, data); - - try { - storageElm.save(userDataKey); - } catch (ex) { - // Ignore disk full - } - - updateKeys(); - } - - LocalStorage = { - /** - * Length of the number of items in storage. - * - * @property length - * @type Number - * @return {Number} Number of items in storage. - */ - //length:0, - - /** - * Returns the key name by index. - * - * @method key - * @param {Number} index Index of key to return. - * @return {String} Key value or null if it wasn't found. - */ - key: function(index) { - return keys[index]; - }, - - /** - * Returns the value if the specified key or null if it wasn't found. - * - * @method getItem - * @param {String} key Key of item to retrieve. - * @return {String} Value of the specified item or null if it wasn't found. - */ - getItem: function(key) { - return key in items ? items[key] : null; - }, - - /** - * Sets the value of the specified item by it's key. - * - * @method setItem - * @param {String} key Key of the item to set. - * @param {String} value Value of the item to set. - */ - setItem: function(key, value) { - items[key] = "" + value; - save(); - }, - - /** - * Removes the specified item by key. - * - * @method removeItem - * @param {String} key Key of item to remove. - */ - removeItem: function(key) { - delete items[key]; - save(); - }, - - /** - * Removes all items. - * - * @method clear - */ - clear: function() { - items = {}; - save(); - } - }; - - load(); - - return LocalStorage; - } - ); - - /** - * Compat.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * TinyMCE core class. - * - * @static - * @class tinymce - * @borrow-members tinymce.EditorManager - * @borrow-members tinymce.util.Tools - */ - define( - 'tinymce.core.api.Compat', [ - "tinymce.core.dom.DOMUtils", - "tinymce.core.dom.EventUtils", - "tinymce.core.dom.ScriptLoader", - "tinymce.core.AddOnManager", - "tinymce.core.util.Tools", - "tinymce.core.Env" - ], - function(DOMUtils, EventUtils, ScriptLoader, AddOnManager, Tools, Env) { - var register = function(tinymce) { - /** - * @property {tinymce.dom.DOMUtils} DOM Global DOM instance. - * @property {tinymce.dom.ScriptLoader} ScriptLoader Global ScriptLoader instance. - * @property {tinymce.AddOnManager} PluginManager Global PluginManager instance. - * @property {tinymce.AddOnManager} ThemeManager Global ThemeManager instance. - */ - tinymce.DOM = DOMUtils.DOM; - tinymce.ScriptLoader = ScriptLoader.ScriptLoader; - tinymce.PluginManager = AddOnManager.PluginManager; - tinymce.ThemeManager = AddOnManager.ThemeManager; - - tinymce.dom = tinymce.dom || {}; - tinymce.dom.Event = EventUtils.Event; - - Tools.each( - 'trim isArray is toArray makeMap each map grep inArray extend create walk createNS resolve explode _addCacheSuffix'.split(' '), - function(key) { - tinymce[key] = Tools[key]; - } - ); - - Tools.each('isOpera isWebKit isIE isGecko isMac'.split(' '), function(name) { - tinymce[name] = Env[name.substr(2).toLowerCase()]; - }); - }; - - return { - register: register - }; - } - ); - - // Describe the different namespaces - - /** - * Root level namespace this contains classes directly related to the TinyMCE editor. - * - * @namespace tinymce - */ - - /** - * Contains classes for handling the browsers DOM. - * - * @namespace tinymce.dom - */ - - /** - * Contains html parser and serializer logic. - * - * @namespace tinymce.html - */ - - /** - * Contains the different UI types such as buttons, listboxes etc. - * - * @namespace tinymce.ui - */ - - /** - * Contains various utility classes such as json parser, cookies etc. - * - * @namespace tinymce.util - */ - - /** - * Contains modules to handle data binding. - * - * @namespace tinymce.data - */ - - /** - * Color.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class lets you parse/serialize colors and convert rgb/hsb. - * - * @class tinymce.util.Color - * @example - * var white = new tinymce.util.Color({r: 255, g: 255, b: 255}); - * var red = new tinymce.util.Color('#FF0000'); - * - * console.log(white.toHex(), red.toHsv()); - */ - define( - 'tinymce.core.util.Color', [], - function() { - var min = Math.min, - max = Math.max, - round = Math.round; - - /** - * Constructs a new color instance. - * - * @constructor - * @method Color - * @param {String} value Optional initial value to parse. - */ - function Color(value) { - var self = this, - r = 0, - g = 0, - b = 0; - - function rgb2hsv(r, g, b) { - var h, s, v, d, minRGB, maxRGB; - - h = 0; - s = 0; - v = 0; - r = r / 255; - g = g / 255; - b = b / 255; - - minRGB = min(r, min(g, b)); - maxRGB = max(r, max(g, b)); - - if (minRGB == maxRGB) { - v = minRGB; - - return { - h: 0, - s: 0, - v: v * 100 - }; - } - - /*eslint no-nested-ternary:0 */ - d = (r == minRGB) ? g - b : ((b == minRGB) ? r - g : b - r); - h = (r == minRGB) ? 3 : ((b == minRGB) ? 1 : 5); - h = 60 * (h - d / (maxRGB - minRGB)); - s = (maxRGB - minRGB) / maxRGB; - v = maxRGB; - - return { - h: round(h), - s: round(s * 100), - v: round(v * 100) - }; - } - - function hsvToRgb(hue, saturation, brightness) { - var side, chroma, x, match; - - hue = (parseInt(hue, 10) || 0) % 360; - saturation = parseInt(saturation, 10) / 100; - brightness = parseInt(brightness, 10) / 100; - saturation = max(0, min(saturation, 1)); - brightness = max(0, min(brightness, 1)); - - if (saturation === 0) { - r = g = b = round(255 * brightness); - return; - } - - side = hue / 60; - chroma = brightness * saturation; - x = chroma * (1 - Math.abs(side % 2 - 1)); - match = brightness - chroma; - - switch (Math.floor(side)) { - case 0: - r = chroma; - g = x; - b = 0; - break; - - case 1: - r = x; - g = chroma; - b = 0; - break; - - case 2: - r = 0; - g = chroma; - b = x; - break; - - case 3: - r = 0; - g = x; - b = chroma; - break; - - case 4: - r = x; - g = 0; - b = chroma; - break; - - case 5: - r = chroma; - g = 0; - b = x; - break; - - default: - r = g = b = 0; - } - - r = round(255 * (r + match)); - g = round(255 * (g + match)); - b = round(255 * (b + match)); - } - - /** - * Returns the hex string of the current color. For example: #ff00ff - * - * @method toHex - * @return {String} Hex string of current color. - */ - function toHex() { - function hex(val) { - val = parseInt(val, 10).toString(16); - - return val.length > 1 ? val : '0' + val; - } - - return '#' + hex(r) + hex(g) + hex(b); - } - - /** - * Returns the r, g, b values of the color. Each channel has a range from 0-255. - * - * @method toRgb - * @return {Object} Object with r, g, b fields. - */ - function toRgb() { - return { - r: r, - g: g, - b: b - }; - } - - /** - * Returns the h, s, v values of the color. Ranges: h=0-360, s=0-100, v=0-100. - * - * @method toHsv - * @return {Object} Object with h, s, v fields. - */ - function toHsv() { - return rgb2hsv(r, g, b); - } - - /** - * Parses the specified value and populates the color instance. - * - * Supported format examples: - * * rbg(255,0,0) - * * #ff0000 - * * #fff - * * {r: 255, g: 0, b: 0} - * * {h: 360, s: 100, v: 100} - * - * @method parse - * @param {Object/String} value Color value to parse. - * @return {tinymce.util.Color} Current color instance. - */ - function parse(value) { - var matches; - - if (typeof value == 'object') { - if ("r" in value) { - r = value.r; - g = value.g; - b = value.b; - } else if ("v" in value) { - hsvToRgb(value.h, value.s, value.v); - } - } else { - if ((matches = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)[^\)]*\)/gi.exec(value))) { - r = parseInt(matches[1], 10); - g = parseInt(matches[2], 10); - b = parseInt(matches[3], 10); - } else if ((matches = /#([0-F]{2})([0-F]{2})([0-F]{2})/gi.exec(value))) { - r = parseInt(matches[1], 16); - g = parseInt(matches[2], 16); - b = parseInt(matches[3], 16); - } else if ((matches = /#([0-F])([0-F])([0-F])/gi.exec(value))) { - r = parseInt(matches[1] + matches[1], 16); - g = parseInt(matches[2] + matches[2], 16); - b = parseInt(matches[3] + matches[3], 16); - } - } - - r = r < 0 ? 0 : (r > 255 ? 255 : r); - g = g < 0 ? 0 : (g > 255 ? 255 : g); - b = b < 0 ? 0 : (b > 255 ? 255 : b); - - return self; - } - - if (value) { - parse(value); - } - - self.toRgb = toRgb; - self.toHsv = toHsv; - self.toHex = toHex; - self.parse = parse; - } - - return Color; - } - ); - - /** - * Layout.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Base layout manager class. - * - * @class tinymce.ui.Layout - */ - define( - 'tinymce.core.ui.Layout', [ - "tinymce.core.util.Class", - "tinymce.core.util.Tools" - ], - function(Class, Tools) { - "use strict"; - - return Class.extend({ - Defaults: { - firstControlClass: 'first', - lastControlClass: 'last' - }, - - /** - * Constructs a layout instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - */ - init: function(settings) { - this.settings = Tools.extend({}, this.Defaults, settings); - }, - - /** - * This method gets invoked before the layout renders the controls. - * - * @method preRender - * @param {tinymce.ui.Container} container Container instance to preRender. - */ - preRender: function(container) { - container.bodyClasses.add(this.settings.containerClass); - }, - - /** - * Applies layout classes to the container. - * - * @private - */ - applyClasses: function(items) { - var self = this, - settings = self.settings, - firstClass, lastClass, firstItem, lastItem; - - firstClass = settings.firstControlClass; - lastClass = settings.lastControlClass; - - items.each(function(item) { - item.classes.remove(firstClass).remove(lastClass).add(settings.controlClass); - - if (item.visible()) { - if (!firstItem) { - firstItem = item; - } - - lastItem = item; - } - }); - - if (firstItem) { - firstItem.classes.add(firstClass); - } - - if (lastItem) { - lastItem.classes.add(lastClass); - } - }, - - /** - * Renders the specified container and any layout specific HTML. - * - * @method renderHtml - * @param {tinymce.ui.Container} container Container to render HTML for. - */ - renderHtml: function(container) { - var self = this, - html = ''; - - self.applyClasses(container.items()); - - container.items().each(function(item) { - html += item.renderHtml(); - }); - - return html; - }, - - /** - * Recalculates the positions of the controls in the specified container. - * - * @method recalc - * @param {tinymce.ui.Container} container Container instance to recalc. - */ - recalc: function() {}, - - /** - * This method gets invoked after the layout renders the controls. - * - * @method postRender - * @param {tinymce.ui.Container} container Container instance to postRender. - */ - postRender: function() {}, - - isNative: function() { - return false; - } - }); - } - ); - /** - * AbsoluteLayout.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * LayoutManager for absolute positioning. This layout manager is more of - * a base class for other layouts but can be created and used directly. - * - * @-x-less AbsoluteLayout.less - * @class tinymce.ui.AbsoluteLayout - * @extends tinymce.ui.Layout - */ - define( - 'tinymce.core.ui.AbsoluteLayout', [ - "tinymce.core.ui.Layout" - ], - function(Layout) { - "use strict"; - - return Layout.extend({ - Defaults: { - containerClass: 'abs-layout', - controlClass: 'abs-layout-item' - }, - - /** - * Recalculates the positions of the controls in the specified container. - * - * @method recalc - * @param {tinymce.ui.Container} container Container instance to recalc. - */ - recalc: function(container) { - container.items().filter(':visible').each(function(ctrl) { - var settings = ctrl.settings; - - ctrl.layoutRect({ - x: settings.x, - y: settings.y, - w: settings.w, - h: settings.h - }); - - if (ctrl.recalc) { - ctrl.recalc(); - } - }); - }, - - /** - * Renders the specified container and any layout specific HTML. - * - * @method renderHtml - * @param {tinymce.ui.Container} container Container to render HTML for. - */ - renderHtml: function(container) { - return '<div id="' + container._id + '-absend" class="' + container.classPrefix + 'abs-end"></div>' + this._super(container); - } - }); - } - ); - /** - * Button.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class is used to create buttons. You can create them directly or through the Factory. - * - * @example - * // Create and render a button to the body element - * tinymce.ui.Factory.create({ - * type: 'button', - * text: 'My button' - * }).renderTo(document.body); - * - * @-x-less Button.less - * @class tinymce.ui.Button - * @extends tinymce.ui.Widget - */ - define( - 'tinymce.core.ui.Button', [ - "tinymce.core.ui.Widget" - ], - function(Widget) { - "use strict"; - - return Widget.extend({ - Defaults: { - classes: "widget btn", - role: "button" - }, - - /** - * Constructs a new button instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {String} size Size of the button small|medium|large. - * @setting {String} image Image to use for icon. - * @setting {String} icon Icon to use for button. - */ - init: function(settings) { - var self = this, - size; - - self._super(settings); - settings = self.settings; - - size = self.settings.size; - - self.on('click mousedown', function(e) { - e.preventDefault(); - }); - - self.on('touchstart', function(e) { - self.fire('click', e); - e.preventDefault(); - }); - - if (settings.subtype) { - self.classes.add(settings.subtype); - } - - if (size) { - self.classes.add('btn-' + size); - } - - if (settings.icon) { - self.icon(settings.icon); - } - }, - - /** - * Sets/gets the current button icon. - * - * @method icon - * @param {String} [icon] New icon identifier. - * @return {String|tinymce.ui.MenuButton} Current icon or current MenuButton instance. - */ - icon: function(icon) { - if (!arguments.length) { - return this.state.get('icon'); - } - - this.state.set('icon', icon); - - return this; - }, - - /** - * Repaints the button for example after it's been resizes by a layout engine. - * - * @method repaint - */ - repaint: function() { - var btnElm = this.getEl().firstChild, - btnStyle; - - if (btnElm) { - btnStyle = btnElm.style; - btnStyle.width = btnStyle.height = "100%"; - } - - this._super(); - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - id = self._id, - prefix = self.classPrefix; - var icon = self.state.get('icon'), - image, text = self.state.get('text'), - textHtml = ''; - - image = self.settings.image; - if (image) { - icon = 'none'; - - // Support for [high dpi, low dpi] image sources - if (typeof image != "string") { - image = window.getSelection ? image[0] : image[1]; - } - - image = ' style="background-image: url(\'' + image + '\')"'; - } else { - image = ''; - } - - if (text) { - self.classes.add('btn-has-text'); - textHtml = '<span class="' + prefix + 'txt">' + self.encode(text) + '</span>'; - } - - icon = icon ? prefix + 'ico ' + prefix + 'i-' + icon : ''; - - return ( - '<div id="' + id + '" class="' + self.classes + '" tabindex="-1">' + - '<button role="presentation" type="button" tabindex="-1">' + - (icon ? '<i class="' + icon + '"' + image + '></i>' : '') + - textHtml + - '</button>' + - '</div>' - ); - }, - - bindStates: function() { - var self = this, - $ = self.$, - textCls = self.classPrefix + 'txt'; - - function setButtonText(text) { - var $span = $('span.' + textCls, self.getEl()); - - if (text) { - if (!$span[0]) { - $('button:first', self.getEl()).append('<span class="' + textCls + '"></span>'); - $span = $('span.' + textCls, self.getEl()); - } - - $span.html(self.encode(text)); - } else { - $span.remove(); - } - - self.classes.toggle('btn-has-text', !!text); - } - - self.state.on('change:text', function(e) { - setButtonText(e.value); - }); - - self.state.on('change:icon', function(e) { - var icon = e.value, - prefix = self.classPrefix; - - self.settings.icon = icon; - icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : ''; - - var btnElm = self.getEl().firstChild, - iconElm = btnElm.getElementsByTagName('i')[0]; - - if (icon) { - if (!iconElm || iconElm != btnElm.firstChild) { - iconElm = document.createElement('i'); - btnElm.insertBefore(iconElm, btnElm.firstChild); - } - - iconElm.className = icon; - } else if (iconElm) { - btnElm.removeChild(iconElm); - } - - setButtonText(self.state.get('text')); - }); - - return self._super(); - } - }); - } - ); - - /** - * ButtonGroup.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This control enables you to put multiple buttons into a group. This is - * useful when you want to combine similar toolbar buttons into a group. - * - * @example - * // Create and render a buttongroup with two buttons to the body element - * tinymce.ui.Factory.create({ - * type: 'buttongroup', - * items: [ - * {text: 'Button A'}, - * {text: 'Button B'} - * ] - * }).renderTo(document.body); - * - * @-x-less ButtonGroup.less - * @class tinymce.ui.ButtonGroup - * @extends tinymce.ui.Container - */ - define( - 'tinymce.core.ui.ButtonGroup', [ - "tinymce.core.ui.Container" - ], - function(Container) { - "use strict"; - - return Container.extend({ - Defaults: { - defaultType: 'button', - role: 'group' - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - layout = self._layout; - - self.classes.add('btn-group'); - self.preRender(); - layout.preRender(self); - - return ( - '<div id="' + self._id + '" class="' + self.classes + '">' + - '<div id="' + self._id + '-body">' + - (self.settings.html || '') + layout.renderHtml(self) + - '</div>' + - '</div>' - ); - } - }); - } - ); - /** - * Checkbox.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This control creates a custom checkbox. - * - * @example - * // Create and render a checkbox to the body element - * tinymce.ui.Factory.create({ - * type: 'checkbox', - * checked: true, - * text: 'My checkbox' - * }).renderTo(document.body); - * - * @-x-less Checkbox.less - * @class tinymce.ui.Checkbox - * @extends tinymce.ui.Widget - */ - define( - 'tinymce.core.ui.Checkbox', [ - "tinymce.core.ui.Widget" - ], - function(Widget) { - "use strict"; - - return Widget.extend({ - Defaults: { - classes: "checkbox", - role: "checkbox", - checked: false - }, - - /** - * Constructs a new Checkbox instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {Boolean} checked True if the checkbox should be checked by default. - */ - init: function(settings) { - var self = this; - - self._super(settings); - - self.on('click mousedown', function(e) { - e.preventDefault(); - }); - - self.on('click', function(e) { - e.preventDefault(); - - if (!self.disabled()) { - self.checked(!self.checked()); - } - }); - - self.checked(self.settings.checked); - }, - - /** - * Getter/setter function for the checked state. - * - * @method checked - * @param {Boolean} [state] State to be set. - * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation. - */ - checked: function(state) { - if (!arguments.length) { - return this.state.get('checked'); - } - - this.state.set('checked', state); - - return this; - }, - - /** - * Getter/setter function for the value state. - * - * @method value - * @param {Boolean} [state] State to be set. - * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation. - */ - value: function(state) { - if (!arguments.length) { - return this.checked(); - } - - return this.checked(state); - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - id = self._id, - prefix = self.classPrefix; - - return ( - '<div id="' + id + '" class="' + self.classes + '" unselectable="on" aria-labelledby="' + id + '-al" tabindex="-1">' + - '<i class="' + prefix + 'ico ' + prefix + 'i-checkbox"></i>' + - '<span id="' + id + '-al" class="' + prefix + 'label">' + self.encode(self.state.get('text')) + '</span>' + - '</div>' - ); - }, - - bindStates: function() { - var self = this; - - function checked(state) { - self.classes.toggle("checked", state); - self.aria('checked', state); - } - - self.state.on('change:text', function(e) { - self.getEl('al').firstChild.data = self.translate(e.value); - }); - - self.state.on('change:checked change:value', function(e) { - self.fire('change'); - checked(e.value); - }); - - self.state.on('change:icon', function(e) { - var icon = e.value, - prefix = self.classPrefix; - - if (typeof icon == 'undefined') { - return self.settings.icon; - } - - self.settings.icon = icon; - icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : ''; - - var btnElm = self.getEl().firstChild, - iconElm = btnElm.getElementsByTagName('i')[0]; - - if (icon) { - if (!iconElm || iconElm != btnElm.firstChild) { - iconElm = document.createElement('i'); - btnElm.insertBefore(iconElm, btnElm.firstChild); - } - - iconElm.className = icon; - } else if (iconElm) { - btnElm.removeChild(iconElm); - } - }); - - if (self.state.get('checked')) { - checked(true); - } - - return self._super(); - } - }); - } - ); - /** - * ComboBox.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class creates a combobox control. Select box that you select a value from or - * type a value into. - * - * @-x-less ComboBox.less - * @class tinymce.ui.ComboBox - * @extends tinymce.ui.Widget - */ - define( - 'tinymce.core.ui.ComboBox', [ - "tinymce.core.ui.Widget", - "tinymce.core.ui.Factory", - "tinymce.core.ui.DomUtils", - "tinymce.core.dom.DomQuery", - "tinymce.core.util.VK", - "tinymce.core.util.Tools" - ], - function(Widget, Factory, DomUtils, $, VK, Tools) { - "use strict"; - - return Widget.extend({ - /** - * Constructs a new control instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {String} placeholder Placeholder text to display. - */ - init: function(settings) { - var self = this; - - self._super(settings); - settings = self.settings; - - self.classes.add('combobox'); - self.subinput = true; - self.ariaTarget = 'inp'; // TODO: Figure out a better way - - settings.menu = settings.menu || settings.values; - - if (settings.menu) { - settings.icon = 'caret'; - } - - self.on('click', function(e) { - var elm = e.target, - root = self.getEl(); - - if (!$.contains(root, elm) && elm != root) { - return; - } - - while (elm && elm != root) { - if (elm.id && elm.id.indexOf('-open') != -1) { - self.fire('action'); - - if (settings.menu) { - self.showMenu(); - - if (e.aria) { - self.menu.items()[0].focus(); - } - } - } - - elm = elm.parentNode; - } - }); - - // TODO: Rework this - self.on('keydown', function(e) { - var rootControl; - - if (e.keyCode == 13 && e.target.nodeName === 'INPUT') { - e.preventDefault(); - - // Find root control that we can do toJSON on - self.parents().reverse().each(function(ctrl) { - if (ctrl.toJSON) { - rootControl = ctrl; - return false; - } - }); - - // Fire event on current text box with the serialized data of the whole form - self.fire('submit', { - data: rootControl.toJSON() - }); - } - }); - - self.on('keyup', function(e) { - if (e.target.nodeName == "INPUT") { - var oldValue = self.state.get('value'); - var newValue = e.target.value; - - if (newValue !== oldValue) { - self.state.set('value', newValue); - self.fire('autocomplete', e); - } - } - }); - - self.on('mouseover', function(e) { - var tooltip = self.tooltip().moveTo(-0xFFFF); - - if (self.statusLevel() && e.target.className.indexOf(self.classPrefix + 'status') !== -1) { - var statusMessage = self.statusMessage() || 'Ok'; - var rel = tooltip.text(statusMessage).show().testMoveRel(e.target, ['bc-tc', 'bc-tl', 'bc-tr']); - - tooltip.classes.toggle('tooltip-n', rel == 'bc-tc'); - tooltip.classes.toggle('tooltip-nw', rel == 'bc-tl'); - tooltip.classes.toggle('tooltip-ne', rel == 'bc-tr'); - - tooltip.moveRel(e.target, rel); - } - }); - }, - - statusLevel: function(value) { - if (arguments.length > 0) { - this.state.set('statusLevel', value); - } - - return this.state.get('statusLevel'); - }, - - statusMessage: function(value) { - if (arguments.length > 0) { - this.state.set('statusMessage', value); - } - - return this.state.get('statusMessage'); - }, - - showMenu: function() { - var self = this, - settings = self.settings, - menu; - - if (!self.menu) { - menu = settings.menu || []; - - // Is menu array then auto constuct menu control - if (menu.length) { - menu = { - type: 'menu', - items: menu - }; - } else { - menu.type = menu.type || 'menu'; - } - - self.menu = Factory.create(menu).parent(self).renderTo(self.getContainerElm()); - self.fire('createmenu'); - self.menu.reflow(); - self.menu.on('cancel', function(e) { - if (e.control === self.menu) { - self.focus(); - } - }); - - self.menu.on('show hide', function(e) { - e.control.items().each(function(ctrl) { - ctrl.active(ctrl.value() == self.value()); - }); - }).fire('show'); - - self.menu.on('select', function(e) { - self.value(e.control.value()); - }); - - self.on('focusin', function(e) { - if (e.target.tagName.toUpperCase() == 'INPUT') { - self.menu.hide(); - } - }); - - self.aria('expanded', true); - } - - self.menu.show(); - self.menu.layoutRect({ - w: self.layoutRect().w - }); - self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']); - }, - - /** - * Focuses the input area of the control. - * - * @method focus - */ - focus: function() { - this.getEl('inp').focus(); - }, - - /** - * Repaints the control after a layout operation. - * - * @method repaint - */ - repaint: function() { - var self = this, - elm = self.getEl(), - openElm = self.getEl('open'), - rect = self.layoutRect(); - var width, lineHeight, innerPadding = 0, - inputElm = elm.firstChild; - - if (self.statusLevel() && self.statusLevel() !== 'none') { - innerPadding = ( - parseInt(DomUtils.getRuntimeStyle(inputElm, 'padding-right'), 10) - - parseInt(DomUtils.getRuntimeStyle(inputElm, 'padding-left'), 10) - ); - } - - if (openElm) { - width = rect.w - DomUtils.getSize(openElm).width - 10; - } else { - width = rect.w - 10; - } - - // Detect old IE 7+8 add lineHeight to align caret vertically in the middle - var doc = document; - if (doc.all && (!doc.documentMode || doc.documentMode <= 8)) { - lineHeight = (self.layoutRect().h - 2) + 'px'; - } - - $(inputElm).css({ - width: width - innerPadding, - lineHeight: lineHeight - }); - - self._super(); - - return self; - }, - - /** - * Post render method. Called after the control has been rendered to the target. - * - * @method postRender - * @return {tinymce.ui.ComboBox} Current combobox instance. - */ - postRender: function() { - var self = this; - - $(this.getEl('inp')).on('change', function(e) { - self.state.set('value', e.target.value); - self.fire('change', e); - }); - - return self._super(); - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - id = self._id, - settings = self.settings, - prefix = self.classPrefix; - var value = self.state.get('value') || ''; - var icon, text, openBtnHtml = '', - extraAttrs = '', - statusHtml = ''; - - if ("spellcheck" in settings) { - extraAttrs += ' spellcheck="' + settings.spellcheck + '"'; - } - - if (settings.maxLength) { - extraAttrs += ' maxlength="' + settings.maxLength + '"'; - } - - if (settings.size) { - extraAttrs += ' size="' + settings.size + '"'; - } - - if (settings.subtype) { - extraAttrs += ' type="' + settings.subtype + '"'; - } - - statusHtml = '<i id="' + id + '-status" class="mce-status mce-ico" style="display: none"></i>'; - - if (self.disabled()) { - extraAttrs += ' disabled="disabled"'; - } - - icon = settings.icon; - if (icon && icon != 'caret') { - icon = prefix + 'ico ' + prefix + 'i-' + settings.icon; - } - - text = self.state.get('text'); - - if (icon || text) { - openBtnHtml = ( - '<div id="' + id + '-open" class="' + prefix + 'btn ' + prefix + 'open" tabIndex="-1" role="button">' + - '<button id="' + id + '-action" type="button" hidefocus="1" tabindex="-1">' + - (icon != 'caret' ? '<i class="' + icon + '"></i>' : '<i class="' + prefix + 'caret"></i>') + - (text ? (icon ? ' ' : '') + text : '') + - '</button>' + - '</div>' - ); - - self.classes.add('has-open'); - } - - return ( - '<div id="' + id + '" class="' + self.classes + '">' + - '<input id="' + id + '-inp" class="' + prefix + 'textbox" value="' + - self.encode(value, false) + '" hidefocus="1"' + extraAttrs + ' placeholder="' + - self.encode(settings.placeholder) + '" />' + - statusHtml + - openBtnHtml + - '</div>' - ); - }, - - value: function(value) { - if (arguments.length) { - this.state.set('value', value); - return this; - } - - // Make sure the real state is in sync - if (this.state.get('rendered')) { - this.state.set('value', this.getEl('inp').value); - } - - return this.state.get('value'); - }, - - showAutoComplete: function(items, term) { - var self = this; - - if (items.length === 0) { - self.hideMenu(); - return; - } - - var insert = function(value, title) { - return function() { - self.fire('selectitem', { - title: title, - value: value - }); - }; - }; - - if (self.menu) { - self.menu.items().remove(); - } else { - self.menu = Factory.create({ - type: 'menu', - classes: 'combobox-menu', - layout: 'flow' - }).parent(self).renderTo(); - } - - Tools.each(items, function(item) { - self.menu.add({ - text: item.title, - url: item.previewUrl, - match: term, - classes: 'menu-item-ellipsis', - onclick: insert(item.value, item.title) - }); - }); - - self.menu.renderNew(); - self.hideMenu(); - - self.menu.on('cancel', function(e) { - if (e.control.parent() === self.menu) { - e.stopPropagation(); - self.focus(); - self.hideMenu(); - } - }); - - self.menu.on('select', function() { - self.focus(); - }); - - var maxW = self.layoutRect().w; - self.menu.layoutRect({ - w: maxW, - minW: 0, - maxW: maxW - }); - self.menu.reflow(); - self.menu.show(); - self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']); - }, - - hideMenu: function() { - if (this.menu) { - this.menu.hide(); - } - }, - - bindStates: function() { - var self = this; - - self.state.on('change:value', function(e) { - if (self.getEl('inp').value != e.value) { - self.getEl('inp').value = e.value; - } - }); - - self.state.on('change:disabled', function(e) { - self.getEl('inp').disabled = e.value; - }); - - self.state.on('change:statusLevel', function(e) { - var statusIconElm = self.getEl('status'); - var prefix = self.classPrefix, - value = e.value; - - DomUtils.css(statusIconElm, 'display', value === 'none' ? 'none' : ''); - DomUtils.toggleClass(statusIconElm, prefix + 'i-checkmark', value === 'ok'); - DomUtils.toggleClass(statusIconElm, prefix + 'i-warning', value === 'warn'); - DomUtils.toggleClass(statusIconElm, prefix + 'i-error', value === 'error'); - self.classes.toggle('has-status', value !== 'none'); - self.repaint(); - }); - - DomUtils.on(self.getEl('status'), 'mouseleave', function() { - self.tooltip().hide(); - }); - - self.on('cancel', function(e) { - if (self.menu && self.menu.visible()) { - e.stopPropagation(); - self.hideMenu(); - } - }); - - var focusIdx = function(idx, menu) { - if (menu && menu.items().length > 0) { - menu.items().eq(idx)[0].focus(); - } - }; - - self.on('keydown', function(e) { - var keyCode = e.keyCode; - - if (e.target.nodeName === 'INPUT') { - if (keyCode === VK.DOWN) { - e.preventDefault(); - self.fire('autocomplete'); - focusIdx(0, self.menu); - } else if (keyCode === VK.UP) { - e.preventDefault(); - focusIdx(-1, self.menu); - } - } - }); - - return self._super(); - }, - - remove: function() { - $(this.getEl('inp')).off(); - - if (this.menu) { - this.menu.remove(); - } - - this._super(); - } - }); - } - ); - /** - * ColorBox.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This widget lets you enter colors and browse for colors by pressing the color button. It also displays - * a preview of the current color. - * - * @-x-less ColorBox.less - * @class tinymce.ui.ColorBox - * @extends tinymce.ui.ComboBox - */ - define( - 'tinymce.core.ui.ColorBox', [ - "tinymce.core.ui.ComboBox" - ], - function(ComboBox) { - "use strict"; - - return ComboBox.extend({ - /** - * Constructs a new control instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - */ - init: function(settings) { - var self = this; - - settings.spellcheck = false; - - if (settings.onaction) { - settings.icon = 'none'; - } - - self._super(settings); - - self.classes.add('colorbox'); - self.on('change keyup postrender', function() { - self.repaintColor(self.value()); - }); - }, - - repaintColor: function(value) { - var openElm = this.getEl('open'); - var elm = openElm ? openElm.getElementsByTagName('i')[0] : null; - - if (elm) { - try { - elm.style.background = value; - } catch (ex) { - // Ignore - } - } - }, - - bindStates: function() { - var self = this; - - self.state.on('change:value', function(e) { - if (self.state.get('rendered')) { - self.repaintColor(e.value); - } - }); - - return self._super(); - } - }); - } - ); - /** - * PanelButton.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a new panel button. - * - * @class tinymce.ui.PanelButton - * @extends tinymce.ui.Button - */ - define( - 'tinymce.core.ui.PanelButton', [ - "tinymce.core.ui.Button", - "tinymce.core.ui.FloatPanel" - ], - function(Button, FloatPanel) { - "use strict"; - - return Button.extend({ - /** - * Shows the panel for the button. - * - * @method showPanel - */ - showPanel: function() { - var self = this, - settings = self.settings; - - self.active(true); - - if (!self.panel) { - var panelSettings = settings.panel; - - // Wrap panel in grid layout if type if specified - // This makes it possible to add forms or other containers directly in the panel option - if (panelSettings.type) { - panelSettings = { - layout: 'grid', - items: panelSettings - }; - } - - panelSettings.role = panelSettings.role || 'dialog'; - panelSettings.popover = true; - panelSettings.autohide = true; - panelSettings.ariaRoot = true; - - self.panel = new FloatPanel(panelSettings).on('hide', function() { - self.active(false); - }).on('cancel', function(e) { - e.stopPropagation(); - self.focus(); - self.hidePanel(); - }).parent(self).renderTo(self.getContainerElm()); - - self.panel.fire('show'); - self.panel.reflow(); - } else { - self.panel.show(); - } - - var rel = self.panel.testMoveRel(self.getEl(), settings.popoverAlign || (self.isRtl() ? ['bc-tc', 'bc-tl', 'bc-tr'] : ['bc-tc', 'bc-tr', 'bc-tl'])); - - self.panel.classes.toggle('start', rel === 'bc-tl'); - self.panel.classes.toggle('end', rel === 'bc-tr'); - - self.panel.moveRel(self.getEl(), rel); - }, - - /** - * Hides the panel for the button. - * - * @method hidePanel - */ - hidePanel: function() { - var self = this; - - if (self.panel) { - self.panel.hide(); - } - }, - - /** - * Called after the control has been rendered. - * - * @method postRender - */ - postRender: function() { - var self = this; - - self.aria('haspopup', true); - - self.on('click', function(e) { - if (e.control === self) { - if (self.panel && self.panel.visible()) { - self.hidePanel(); - } else { - self.showPanel(); - self.panel.focus(!!e.aria); - } - } - }); - - return self._super(); - }, - - remove: function() { - if (this.panel) { - this.panel.remove(); - this.panel = null; - } - - return this._super(); - } - }); - } - ); - /** - * ColorButton.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class creates a color button control. This is a split button in which the main - * button has a visual representation of the currently selected color. When clicked - * the caret button displays a color picker, allowing the user to select a new color. - * - * @-x-less ColorButton.less - * @class tinymce.ui.ColorButton - * @extends tinymce.ui.PanelButton - */ - define( - 'tinymce.core.ui.ColorButton', [ - "tinymce.core.ui.PanelButton", - "tinymce.core.dom.DOMUtils" - ], - function(PanelButton, DomUtils) { - "use strict"; - - var DOM = DomUtils.DOM; - - return PanelButton.extend({ - /** - * Constructs a new ColorButton instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - */ - init: function(settings) { - this._super(settings); - this.classes.add('colorbutton'); - }, - - /** - * Getter/setter for the current color. - * - * @method color - * @param {String} [color] Color to set. - * @return {String|tinymce.ui.ColorButton} Current color or current instance. - */ - color: function(color) { - if (color) { - this._color = color; - this.getEl('preview').style.backgroundColor = color; - return this; - } - - return this._color; - }, - - /** - * Resets the current color. - * - * @method resetColor - * @return {tinymce.ui.ColorButton} Current instance. - */ - resetColor: function() { - this._color = null; - this.getEl('preview').style.backgroundColor = null; - return this; - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - id = self._id, - prefix = self.classPrefix, - text = self.state.get('text'); - var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : ''; - var image = self.settings.image ? ' style="background-image: url(\'' + self.settings.image + '\')"' : '', - textHtml = ''; - - if (text) { - self.classes.add('btn-has-text'); - textHtml = '<span class="' + prefix + 'txt">' + self.encode(text) + '</span>'; - } - - return ( - '<div id="' + id + '" class="' + self.classes + '" role="button" tabindex="-1" aria-haspopup="true">' + - '<button role="presentation" hidefocus="1" type="button" tabindex="-1">' + - (icon ? '<i class="' + icon + '"' + image + '></i>' : '') + - '<span id="' + id + '-preview" class="' + prefix + 'preview"></span>' + - textHtml + - '</button>' + - '<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' + - ' <i class="' + prefix + 'caret"></i>' + - '</button>' + - '</div>' - ); - }, - - /** - * Called after the control has been rendered. - * - * @method postRender - */ - postRender: function() { - var self = this, - onClickHandler = self.settings.onclick; - - self.on('click', function(e) { - if (e.aria && e.aria.key == 'down') { - return; - } - - if (e.control == self && !DOM.getParent(e.target, '.' + self.classPrefix + 'open')) { - e.stopImmediatePropagation(); - onClickHandler.call(self, e); - } - }); - - delete self.settings.onclick; - - return self._super(); - } - }); - } - ); - - /** - * ColorPicker.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Color picker widget lets you select colors. - * - * @-x-less ColorPicker.less - * @class tinymce.ui.ColorPicker - * @extends tinymce.ui.Widget - */ - define( - 'tinymce.core.ui.ColorPicker', [ - "tinymce.core.ui.Widget", - "tinymce.core.ui.DragHelper", - "tinymce.core.ui.DomUtils", - "tinymce.core.util.Color" - ], - function(Widget, DragHelper, DomUtils, Color) { - "use strict"; - - return Widget.extend({ - Defaults: { - classes: "widget colorpicker" - }, - - /** - * Constructs a new colorpicker instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {String} color Initial color value. - */ - init: function(settings) { - this._super(settings); - }, - - postRender: function() { - var self = this, - color = self.color(), - hsv, hueRootElm, huePointElm, svRootElm, svPointElm; - - hueRootElm = self.getEl('h'); - huePointElm = self.getEl('hp'); - svRootElm = self.getEl('sv'); - svPointElm = self.getEl('svp'); - - function getPos(elm, event) { - var pos = DomUtils.getPos(elm), - x, y; - - x = event.pageX - pos.x; - y = event.pageY - pos.y; - - x = Math.max(0, Math.min(x / elm.clientWidth, 1)); - y = Math.max(0, Math.min(y / elm.clientHeight, 1)); - - return { - x: x, - y: y - }; - } - - function updateColor(hsv, hueUpdate) { - var hue = (360 - hsv.h) / 360; - - DomUtils.css(huePointElm, { - top: (hue * 100) + '%' - }); - - if (!hueUpdate) { - DomUtils.css(svPointElm, { - left: hsv.s + '%', - top: (100 - hsv.v) + '%' - }); - } - - svRootElm.style.background = new Color({ - s: 100, - v: 100, - h: hsv.h - }).toHex(); - self.color().parse({ - s: hsv.s, - v: hsv.v, - h: hsv.h - }); - } - - function updateSaturationAndValue(e) { - var pos; - - pos = getPos(svRootElm, e); - hsv.s = pos.x * 100; - hsv.v = (1 - pos.y) * 100; - - updateColor(hsv); - self.fire('change'); - } - - function updateHue(e) { - var pos; - - pos = getPos(hueRootElm, e); - hsv = color.toHsv(); - hsv.h = (1 - pos.y) * 360; - updateColor(hsv, true); - self.fire('change'); - } - - self._repaint = function() { - hsv = color.toHsv(); - updateColor(hsv); - }; - - self._super(); - - self._svdraghelper = new DragHelper(self._id + '-sv', { - start: updateSaturationAndValue, - drag: updateSaturationAndValue - }); - - self._hdraghelper = new DragHelper(self._id + '-h', { - start: updateHue, - drag: updateHue - }); - - self._repaint(); - }, - - rgb: function() { - return this.color().toRgb(); - }, - - value: function(value) { - var self = this; - - if (arguments.length) { - self.color().parse(value); - - if (self._rendered) { - self._repaint(); - } - } else { - return self.color().toHex(); - } - }, - - color: function() { - if (!this._color) { - this._color = new Color(); - } - - return this._color; - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - id = self._id, - prefix = self.classPrefix, - hueHtml; - var stops = '#ff0000,#ff0080,#ff00ff,#8000ff,#0000ff,#0080ff,#00ffff,#00ff80,#00ff00,#80ff00,#ffff00,#ff8000,#ff0000'; - - function getOldIeFallbackHtml() { - var i, l, html = '', - gradientPrefix, stopsList; - - gradientPrefix = 'filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='; - stopsList = stops.split(','); - for (i = 0, l = stopsList.length - 1; i < l; i++) { - html += ( - '<div class="' + prefix + 'colorpicker-h-chunk" style="' + - 'height:' + (100 / l) + '%;' + - gradientPrefix + stopsList[i] + ',endColorstr=' + stopsList[i + 1] + ');' + - '-ms-' + gradientPrefix + stopsList[i] + ',endColorstr=' + stopsList[i + 1] + ')' + - '"></div>' - ); - } - - return html; - } - - var gradientCssText = ( - 'background: -ms-linear-gradient(top,' + stops + ');' + - 'background: linear-gradient(to bottom,' + stops + ');' - ); - - hueHtml = ( - '<div id="' + id + '-h" class="' + prefix + 'colorpicker-h" style="' + gradientCssText + '">' + - getOldIeFallbackHtml() + - '<div id="' + id + '-hp" class="' + prefix + 'colorpicker-h-marker"></div>' + - '</div>' - ); - - return ( - '<div id="' + id + '" class="' + self.classes + '">' + - '<div id="' + id + '-sv" class="' + prefix + 'colorpicker-sv">' + - '<div class="' + prefix + 'colorpicker-overlay1">' + - '<div class="' + prefix + 'colorpicker-overlay2">' + - '<div id="' + id + '-svp" class="' + prefix + 'colorpicker-selector1">' + - '<div class="' + prefix + 'colorpicker-selector2"></div>' + - '</div>' + - '</div>' + - '</div>' + - '</div>' + - hueHtml + - '</div>' - ); - } - }); - } - ); - /** - * Path.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a new path control. - * - * @-x-less Path.less - * @class tinymce.ui.Path - * @extends tinymce.ui.Widget - */ - define( - 'tinymce.core.ui.Path', [ - "tinymce.core.ui.Widget" - ], - function(Widget) { - "use strict"; - - return Widget.extend({ - /** - * Constructs a instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {String} delimiter Delimiter to display between row in path. - */ - init: function(settings) { - var self = this; - - if (!settings.delimiter) { - settings.delimiter = '\u00BB'; - } - - self._super(settings); - self.classes.add('path'); - self.canFocus = true; - - self.on('click', function(e) { - var index, target = e.target; - - if ((index = target.getAttribute('data-index'))) { - self.fire('select', { - value: self.row()[index], - index: index - }); - } - }); - - self.row(self.settings.row); - }, - - /** - * Focuses the current control. - * - * @method focus - * @return {tinymce.ui.Control} Current control instance. - */ - focus: function() { - var self = this; - - self.getEl().firstChild.focus(); - - return self; - }, - - /** - * Sets/gets the data to be used for the path. - * - * @method row - * @param {Array} row Array with row name is rendered to path. - */ - row: function(row) { - if (!arguments.length) { - return this.state.get('row'); - } - - this.state.set('row', row); - - return this; - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this; - - return ( - '<div id="' + self._id + '" class="' + self.classes + '">' + - self._getDataPathHtml(self.state.get('row')) + - '</div>' - ); - }, - - bindStates: function() { - var self = this; - - self.state.on('change:row', function(e) { - self.innerHtml(self._getDataPathHtml(e.value)); - }); - - return self._super(); - }, - - _getDataPathHtml: function(data) { - var self = this, - parts = data || [], - i, l, html = '', - prefix = self.classPrefix; - - for (i = 0, l = parts.length; i < l; i++) { - html += ( - (i > 0 ? '<div class="' + prefix + 'divider" aria-hidden="true"> ' + self.settings.delimiter + ' </div>' : '') + - '<div role="button" class="' + prefix + 'path-item' + (i == l - 1 ? ' ' + prefix + 'last' : '') + '" data-index="' + - i + '" tabindex="-1" id="' + self._id + '-' + i + '" aria-level="' + (i + 1) + '">' + parts[i].name + '</div>' - ); - } - - if (!html) { - html = '<div class="' + prefix + 'path-item">\u00a0</div>'; - } - - return html; - } - }); - } - ); - - /** - * ElementPath.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This control creates an path for the current selections parent elements in TinyMCE. - * - * @class tinymce.ui.ElementPath - * @extends tinymce.ui.Path - */ - define( - 'tinymce.core.ui.ElementPath', [ - "tinymce.core.ui.Path" - ], - function(Path) { - return Path.extend({ - /** - * Post render method. Called after the control has been rendered to the target. - * - * @method postRender - * @return {tinymce.ui.ElementPath} Current combobox instance. - */ - postRender: function() { - var self = this, - editor = self.settings.editor; - - function isHidden(elm) { - if (elm.nodeType === 1) { - if (elm.nodeName == "BR" || !!elm.getAttribute('data-mce-bogus')) { - return true; - } - - if (elm.getAttribute('data-mce-type') === 'bookmark') { - return true; - } - } - - return false; - } - - if (editor.settings.elementpath !== false) { - self.on('select', function(e) { - editor.focus(); - editor.selection.select(this.row()[e.index].element); - editor.nodeChanged(); - }); - - editor.on('nodeChange', function(e) { - var outParents = [], - parents = e.parents, - i = parents.length; - - while (i--) { - if (parents[i].nodeType == 1 && !isHidden(parents[i])) { - var args = editor.fire('ResolveName', { - name: parents[i].nodeName.toLowerCase(), - target: parents[i] - }); - - if (!args.isDefaultPrevented()) { - outParents.push({ - name: args.name, - element: parents[i] - }); - } - - if (args.isPropagationStopped()) { - break; - } - } - } - - self.row(outParents); - }); - } - - return self._super(); - } - }); - } - ); - /** - * FormItem.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class is a container created by the form element with - * a label and control item. - * - * @class tinymce.ui.FormItem - * @extends tinymce.ui.Container - * @setting {String} label Label to display for the form item. - */ - define( - 'tinymce.core.ui.FormItem', [ - "tinymce.core.ui.Container" - ], - function(Container) { - "use strict"; - - return Container.extend({ - Defaults: { - layout: 'flex', - align: 'center', - defaults: { - flex: 1 - } - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - layout = self._layout, - prefix = self.classPrefix; - - self.classes.add('formitem'); - layout.preRender(self); - - return ( - '<div id="' + self._id + '" class="' + self.classes + '" hidefocus="1" tabindex="-1">' + - (self.settings.title ? ('<div id="' + self._id + '-title" class="' + prefix + 'title">' + - self.settings.title + '</div>') : '') + - '<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' + - (self.settings.html || '') + layout.renderHtml(self) + - '</div>' + - '</div>' - ); - } - }); - } - ); - /** - * Form.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class creates a form container. A form container has the ability - * to automatically wrap items in tinymce.ui.FormItem instances. - * - * Each FormItem instance is a container for the label and the item. - * - * @example - * tinymce.ui.Factory.create({ - * type: 'form', - * items: [ - * {type: 'textbox', label: 'My text box'} - * ] - * }).renderTo(document.body); - * - * @class tinymce.ui.Form - * @extends tinymce.ui.Container - */ - define( - 'tinymce.core.ui.Form', [ - "tinymce.core.ui.Container", - "tinymce.core.ui.FormItem", - "tinymce.core.util.Tools" - ], - function(Container, FormItem, Tools) { - "use strict"; - - return Container.extend({ - Defaults: { - containerCls: 'form', - layout: 'flex', - direction: 'column', - align: 'stretch', - flex: 1, - padding: 20, - labelGap: 30, - spacing: 10, - callbacks: { - submit: function() { - this.submit(); - } - } - }, - - /** - * This method gets invoked before the control is rendered. - * - * @method preRender - */ - preRender: function() { - var self = this, - items = self.items(); - - if (!self.settings.formItemDefaults) { - self.settings.formItemDefaults = { - layout: 'flex', - autoResize: "overflow", - defaults: { - flex: 1 - } - }; - } - - // Wrap any labeled items in FormItems - items.each(function(ctrl) { - var formItem, label = ctrl.settings.label; - - if (label) { - formItem = new FormItem(Tools.extend({ - items: { - type: 'label', - id: ctrl._id + '-l', - text: label, - flex: 0, - forId: ctrl._id, - disabled: ctrl.disabled() - } - }, self.settings.formItemDefaults)); - - formItem.type = 'formitem'; - ctrl.aria('labelledby', ctrl._id + '-l'); - - if (typeof ctrl.settings.flex == "undefined") { - ctrl.settings.flex = 1; - } - - self.replace(ctrl, formItem); - formItem.add(ctrl); - } - }); - }, - - /** - * Fires a submit event with the serialized form. - * - * @method submit - * @return {Object} Event arguments object. - */ - submit: function() { - return this.fire('submit', { - data: this.toJSON() - }); - }, - - /** - * Post render method. Called after the control has been rendered to the target. - * - * @method postRender - * @return {tinymce.ui.ComboBox} Current combobox instance. - */ - postRender: function() { - var self = this; - - self._super(); - self.fromJSON(self.settings.data); - }, - - bindStates: function() { - var self = this; - - self._super(); - - function recalcLabels() { - var maxLabelWidth = 0, - labels = [], - i, labelGap, items; - - if (self.settings.labelGapCalc === false) { - return; - } - - if (self.settings.labelGapCalc == "children") { - items = self.find('formitem'); - } else { - items = self.items(); - } - - items.filter('formitem').each(function(item) { - var labelCtrl = item.items()[0], - labelWidth = labelCtrl.getEl().clientWidth; - - maxLabelWidth = labelWidth > maxLabelWidth ? labelWidth : maxLabelWidth; - labels.push(labelCtrl); - }); - - labelGap = self.settings.labelGap || 0; - - i = labels.length; - while (i--) { - labels[i].settings.minWidth = maxLabelWidth + labelGap; - } - } - - self.on('show', recalcLabels); - recalcLabels(); - } - }); - } - ); - /** - * FieldSet.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class creates fieldset containers. - * - * @-x-less FieldSet.less - * @class tinymce.ui.FieldSet - * @extends tinymce.ui.Form - */ - define( - 'tinymce.core.ui.FieldSet', [ - "tinymce.core.ui.Form" - ], - function(Form) { - "use strict"; - - return Form.extend({ - Defaults: { - containerCls: 'fieldset', - layout: 'flex', - direction: 'column', - align: 'stretch', - flex: 1, - padding: "25 15 5 15", - labelGap: 30, - spacing: 10, - border: 1 - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - layout = self._layout, - prefix = self.classPrefix; - - self.preRender(); - layout.preRender(self); - - return ( - '<fieldset id="' + self._id + '" class="' + self.classes + '" hidefocus="1" tabindex="-1">' + - (self.settings.title ? ('<legend id="' + self._id + '-title" class="' + prefix + 'fieldset-title">' + - self.settings.title + '</legend>') : '') + - '<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' + - (self.settings.html || '') + layout.renderHtml(self) + - '</div>' + - '</fieldset>' - ); - } - }); - } - ); - /** - * LinkTargets.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This module is enables you to get anything that you can link to in a element. - * - * @private - * @class tinymce.content.LinkTargets - */ - define( - 'tinymce.core.content.LinkTargets', [ - 'tinymce.core.dom.DOMUtils', - 'tinymce.core.dom.NodeType', - 'tinymce.core.util.Arr', - 'tinymce.core.util.Fun', - 'tinymce.core.util.Tools', - 'tinymce.core.util.Uuid' - ], - function(DOMUtils, NodeType, Arr, Fun, Tools, Uuid) { - var trim = Tools.trim; - - var create = function(type, title, url, level, attach) { - return { - type: type, - title: title, - url: url, - level: level, - attach: attach - }; - }; - - var isChildOfContentEditableTrue = function(node) { - while ((node = node.parentNode)) { - var value = node.contentEditable; - if (value && value !== 'inherit') { - return NodeType.isContentEditableTrue(node); - } - } - - return false; - }; - - var select = function(selector, root) { - return DOMUtils.DOM.select(selector, root); - }; - - var getElementText = function(elm) { - return elm.innerText || elm.textContent; - }; - - var getOrGenerateId = function(elm) { - return elm.id ? elm.id : Uuid.uuid('h'); - }; - - var isAnchor = function(elm) { - return elm && elm.nodeName === 'A' && (elm.id || elm.name); - }; - - var isValidAnchor = function(elm) { - return isAnchor(elm) && isEditable(elm); - }; - - var isHeader = function(elm) { - return elm && /^(H[1-6])$/.test(elm.nodeName); - }; - - var isEditable = function(elm) { - return isChildOfContentEditableTrue(elm) && !NodeType.isContentEditableFalse(elm); - }; - - var isValidHeader = function(elm) { - return isHeader(elm) && isEditable(elm); - }; - - var getLevel = function(elm) { - return isHeader(elm) ? parseInt(elm.nodeName.substr(1), 10) : 0; - }; - - var headerTarget = function(elm) { - var headerId = getOrGenerateId(elm); - - var attach = function() { - elm.id = headerId; - }; - - return create('header', getElementText(elm), '#' + headerId, getLevel(elm), attach); - }; - - var anchorTarget = function(elm) { - var anchorId = elm.id || elm.name; - var anchorText = getElementText(elm); - - return create('anchor', anchorText ? anchorText : '#' + anchorId, '#' + anchorId, 0, Fun.noop); - }; - - var getHeaderTargets = function(elms) { - return Arr.map(Arr.filter(elms, isValidHeader), headerTarget); - }; - - var getAnchorTargets = function(elms) { - return Arr.map(Arr.filter(elms, isValidAnchor), anchorTarget); - }; - - var getTargetElements = function(elm) { - var elms = select('h1,h2,h3,h4,h5,h6,a:not([href])', elm); - return elms; - }; - - var hasTitle = function(target) { - return trim(target.title).length > 0; - }; - - var find = function(elm) { - var elms = getTargetElements(elm); - return Arr.filter(getHeaderTargets(elms).concat(getAnchorTargets(elms)), hasTitle); - }; - - return { - find: find - }; - } - ); - - /** - * FilePicker.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class creates a file picker control. - * - * @class tinymce.ui.FilePicker - * @extends tinymce.ui.ComboBox - */ - define( - 'tinymce.core.ui.FilePicker', [ - 'global!window', - 'tinymce.core.content.LinkTargets', - 'tinymce.core.EditorManager', - 'tinymce.core.ui.ComboBox', - 'tinymce.core.util.Arr', - 'tinymce.core.util.Fun', - 'tinymce.core.util.Tools' - ], - function(window, LinkTargets, EditorManager, ComboBox, Arr, Fun, Tools) { - "use strict"; - - var getActiveEditor = function() { - return window.tinymce ? window.tinymce.activeEditor : EditorManager.activeEditor; - }; - - var history = {}; - var HISTORY_LENGTH = 5; - - var toMenuItem = function(target) { - return { - title: target.title, - value: { - title: { - raw: target.title - }, - url: target.url, - attach: target.attach - } - }; - }; - - var toMenuItems = function(targets) { - return Tools.map(targets, toMenuItem); - }; - - var staticMenuItem = function(title, url) { - return { - title: title, - value: { - title: title, - url: url, - attach: Fun.noop - } - }; - }; - - var isUniqueUrl = function(url, targets) { - var foundTarget = Arr.find(targets, function(target) { - return target.url === url; - }); - - return !foundTarget; - }; - - var getSetting = function(editorSettings, name, defaultValue) { - var value = name in editorSettings ? editorSettings[name] : defaultValue; - return value === false ? null : value; - }; - - var createMenuItems = function(term, targets, fileType, editorSettings) { - var separator = { - title: '-' - }; - - var fromHistoryMenuItems = function(history) { - var uniqueHistory = Arr.filter(history[fileType], function(url) { - return isUniqueUrl(url, targets); - }); - - return Tools.map(uniqueHistory, function(url) { - return { - title: url, - value: { - title: url, - url: url, - attach: Fun.noop - } - }; - }); - }; - - var fromMenuItems = function(type) { - var filteredTargets = Arr.filter(targets, function(target) { - return target.type == type; - }); - - return toMenuItems(filteredTargets); - }; - - var anchorMenuItems = function() { - var anchorMenuItems = fromMenuItems('anchor'); - var topAnchor = getSetting(editorSettings, 'anchor_top', '#top'); - var bottomAchor = getSetting(editorSettings, 'anchor_bottom', '#bottom'); - - if (topAnchor !== null) { - anchorMenuItems.unshift(staticMenuItem('<top>', topAnchor)); - } - - if (bottomAchor !== null) { - anchorMenuItems.push(staticMenuItem('<bottom>', bottomAchor)); - } - - return anchorMenuItems; - }; - - var join = function(items) { - return Arr.reduce(items, function(a, b) { - var bothEmpty = a.length === 0 || b.length === 0; - return bothEmpty ? a.concat(b) : a.concat(separator, b); - }, []); - }; - - if (editorSettings.typeahead_urls === false) { - return []; - } - - return fileType === 'file' ? join([ - filterByQuery(term, fromHistoryMenuItems(history)), - filterByQuery(term, fromMenuItems('header')), - filterByQuery(term, anchorMenuItems()) - ]) : filterByQuery(term, fromHistoryMenuItems(history)); - }; - - var addToHistory = function(url, fileType) { - var items = history[fileType]; - - if (!/^https?/.test(url)) { - return; - } - - if (items) { - if (Arr.indexOf(items, url) === -1) { - history[fileType] = items.slice(0, HISTORY_LENGTH).concat(url); - } - } else { - history[fileType] = [url]; - } - }; - - var filterByQuery = function(term, menuItems) { - var lowerCaseTerm = term.toLowerCase(); - var result = Tools.grep(menuItems, function(item) { - return item.title.toLowerCase().indexOf(lowerCaseTerm) !== -1; - }); - - return result.length === 1 && result[0].title === term ? [] : result; - }; - - var getTitle = function(linkDetails) { - var title = linkDetails.title; - return title.raw ? title.raw : title; - }; - - var setupAutoCompleteHandler = function(ctrl, editorSettings, bodyElm, fileType) { - var autocomplete = function(term) { - var linkTargets = LinkTargets.find(bodyElm); - var menuItems = createMenuItems(term, linkTargets, fileType, editorSettings); - ctrl.showAutoComplete(menuItems, term); - }; - - ctrl.on('autocomplete', function() { - autocomplete(ctrl.value()); - }); - - ctrl.on('selectitem', function(e) { - var linkDetails = e.value; - - ctrl.value(linkDetails.url); - var title = getTitle(linkDetails); - - if (fileType === 'image') { - ctrl.fire('change', { - meta: { - alt: title, - attach: linkDetails.attach - } - }); - } else { - ctrl.fire('change', { - meta: { - text: title, - attach: linkDetails.attach - } - }); - } - - ctrl.focus(); - }); - - ctrl.on('click', function(e) { - if (ctrl.value().length === 0 && e.target.nodeName === 'INPUT') { - autocomplete(''); - } - }); - - ctrl.on('PostRender', function() { - ctrl.getRoot().on('submit', function(e) { - if (!e.isDefaultPrevented()) { - addToHistory(ctrl.value(), fileType); - } - }); - }); - }; - - var statusToUiState = function(result) { - var status = result.status, - message = result.message; - - if (status === 'valid') { - return { - status: 'ok', - message: message - }; - } else if (status === 'unknown') { - return { - status: 'warn', - message: message - }; - } else if (status === 'invalid') { - return { - status: 'warn', - message: message - }; - } else { - return { - status: 'none', - message: '' - }; - } - }; - - var setupLinkValidatorHandler = function(ctrl, editorSettings, fileType) { - var validatorHandler = editorSettings.filepicker_validator_handler; - if (validatorHandler) { - var validateUrl = function(url) { - if (url.length === 0) { - ctrl.statusLevel('none'); - return; - } - - validatorHandler({ - url: url, - type: fileType - }, function(result) { - var uiState = statusToUiState(result); - - ctrl.statusMessage(uiState.message); - ctrl.statusLevel(uiState.status); - }); - }; - - ctrl.state.on('change:value', function(e) { - validateUrl(e.value); - }); - } - }; - - return ComboBox.extend({ - /** - * Constructs a new control instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - */ - init: function(settings) { - var self = this, - editor = getActiveEditor(), - editorSettings = editor.settings; - var actionCallback, fileBrowserCallback, fileBrowserCallbackTypes; - var fileType = settings.filetype; - - settings.spellcheck = false; - - fileBrowserCallbackTypes = editorSettings.file_picker_types || editorSettings.file_browser_callback_types; - if (fileBrowserCallbackTypes) { - fileBrowserCallbackTypes = Tools.makeMap(fileBrowserCallbackTypes, /[, ]/); - } - - if (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[fileType]) { - fileBrowserCallback = editorSettings.file_picker_callback; - if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[fileType])) { - actionCallback = function() { - var meta = self.fire('beforecall').meta; - - meta = Tools.extend({ - filetype: fileType - }, meta); - - // file_picker_callback(callback, currentValue, metaData) - fileBrowserCallback.call( - editor, - function(value, meta) { - self.value(value).fire('change', { - meta: meta - }); - }, - self.value(), - meta - ); - }; - } else { - // Legacy callback: file_picker_callback(id, currentValue, filetype, window) - fileBrowserCallback = editorSettings.file_browser_callback; - if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[fileType])) { - actionCallback = function() { - fileBrowserCallback( - self.getEl('inp').id, - self.value(), - fileType, - window - ); - }; - } - } - } - - if (actionCallback) { - settings.icon = 'browse'; - settings.onaction = actionCallback; - } - - self._super(settings); - - setupAutoCompleteHandler(self, editorSettings, editor.getBody(), fileType); - setupLinkValidatorHandler(self, editorSettings, fileType); - } - }); - } - ); - /** - * FitLayout.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This layout manager will resize the control to be the size of it's parent container. - * In other words width: 100% and height: 100%. - * - * @-x-less FitLayout.less - * @class tinymce.ui.FitLayout - * @extends tinymce.ui.AbsoluteLayout - */ - define( - 'tinymce.core.ui.FitLayout', [ - "tinymce.core.ui.AbsoluteLayout" - ], - function(AbsoluteLayout) { - "use strict"; - - return AbsoluteLayout.extend({ - /** - * Recalculates the positions of the controls in the specified container. - * - * @method recalc - * @param {tinymce.ui.Container} container Container instance to recalc. - */ - recalc: function(container) { - var contLayoutRect = container.layoutRect(), - paddingBox = container.paddingBox; - - container.items().filter(':visible').each(function(ctrl) { - ctrl.layoutRect({ - x: paddingBox.left, - y: paddingBox.top, - w: contLayoutRect.innerW - paddingBox.right - paddingBox.left, - h: contLayoutRect.innerH - paddingBox.top - paddingBox.bottom - }); - - if (ctrl.recalc) { - ctrl.recalc(); - } - }); - } - }); - } - ); - /** - * FlexLayout.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This layout manager works similar to the CSS flex box. - * - * @setting {String} direction row|row-reverse|column|column-reverse - * @setting {Number} flex A positive-number to flex by. - * @setting {String} align start|end|center|stretch - * @setting {String} pack start|end|justify - * - * @class tinymce.ui.FlexLayout - * @extends tinymce.ui.AbsoluteLayout - */ - define( - 'tinymce.core.ui.FlexLayout', [ - "tinymce.core.ui.AbsoluteLayout" - ], - function(AbsoluteLayout) { - "use strict"; - - return AbsoluteLayout.extend({ - /** - * Recalculates the positions of the controls in the specified container. - * - * @method recalc - * @param {tinymce.ui.Container} container Container instance to recalc. - */ - recalc: function(container) { - // A ton of variables, needs to be in the same scope for performance - var i, l, items, contLayoutRect, contPaddingBox, contSettings, align, pack, spacing, totalFlex, availableSpace, direction; - var ctrl, ctrlLayoutRect, ctrlSettings, flex, maxSizeItems = [], - size, maxSize, ratio, rect, pos, maxAlignEndPos; - var sizeName, minSizeName, posName, maxSizeName, beforeName, innerSizeName, deltaSizeName, contentSizeName; - var alignAxisName, alignInnerSizeName, alignSizeName, alignMinSizeName, alignBeforeName, alignAfterName; - var alignDeltaSizeName, alignContentSizeName; - var max = Math.max, - min = Math.min; - - // Get container items, properties and settings - items = container.items().filter(':visible'); - contLayoutRect = container.layoutRect(); - contPaddingBox = container.paddingBox; - contSettings = container.settings; - direction = container.isRtl() ? (contSettings.direction || 'row-reversed') : contSettings.direction; - align = contSettings.align; - pack = container.isRtl() ? (contSettings.pack || 'end') : contSettings.pack; - spacing = contSettings.spacing || 0; - - if (direction == "row-reversed" || direction == "column-reverse") { - items = items.set(items.toArray().reverse()); - direction = direction.split('-')[0]; - } - - // Setup axis variable name for row/column direction since the calculations is the same - if (direction == "column") { - posName = "y"; - sizeName = "h"; - minSizeName = "minH"; - maxSizeName = "maxH"; - innerSizeName = "innerH"; - beforeName = 'top'; - deltaSizeName = "deltaH"; - contentSizeName = "contentH"; - - alignBeforeName = "left"; - alignSizeName = "w"; - alignAxisName = "x"; - alignInnerSizeName = "innerW"; - alignMinSizeName = "minW"; - alignAfterName = "right"; - alignDeltaSizeName = "deltaW"; - alignContentSizeName = "contentW"; - } else { - posName = "x"; - sizeName = "w"; - minSizeName = "minW"; - maxSizeName = "maxW"; - innerSizeName = "innerW"; - beforeName = 'left'; - deltaSizeName = "deltaW"; - contentSizeName = "contentW"; - - alignBeforeName = "top"; - alignSizeName = "h"; - alignAxisName = "y"; - alignInnerSizeName = "innerH"; - alignMinSizeName = "minH"; - alignAfterName = "bottom"; - alignDeltaSizeName = "deltaH"; - alignContentSizeName = "contentH"; - } - - // Figure out total flex, availableSpace and collect any max size elements - availableSpace = contLayoutRect[innerSizeName] - contPaddingBox[beforeName] - contPaddingBox[beforeName]; - maxAlignEndPos = totalFlex = 0; - for (i = 0, l = items.length; i < l; i++) { - ctrl = items[i]; - ctrlLayoutRect = ctrl.layoutRect(); - ctrlSettings = ctrl.settings; - flex = ctrlSettings.flex; - availableSpace -= (i < l - 1 ? spacing : 0); - - if (flex > 0) { - totalFlex += flex; - - // Flexed item has a max size then we need to check if we will hit that size - if (ctrlLayoutRect[maxSizeName]) { - maxSizeItems.push(ctrl); - } - - ctrlLayoutRect.flex = flex; - } - - availableSpace -= ctrlLayoutRect[minSizeName]; - - // Calculate the align end position to be used to check for overflow/underflow - size = contPaddingBox[alignBeforeName] + ctrlLayoutRect[alignMinSizeName] + contPaddingBox[alignAfterName]; - if (size > maxAlignEndPos) { - maxAlignEndPos = size; - } - } - - // Calculate minW/minH - rect = {}; - if (availableSpace < 0) { - rect[minSizeName] = contLayoutRect[minSizeName] - availableSpace + contLayoutRect[deltaSizeName]; - } else { - rect[minSizeName] = contLayoutRect[innerSizeName] - availableSpace + contLayoutRect[deltaSizeName]; - } - - rect[alignMinSizeName] = maxAlignEndPos + contLayoutRect[alignDeltaSizeName]; - - rect[contentSizeName] = contLayoutRect[innerSizeName] - availableSpace; - rect[alignContentSizeName] = maxAlignEndPos; - rect.minW = min(rect.minW, contLayoutRect.maxW); - rect.minH = min(rect.minH, contLayoutRect.maxH); - rect.minW = max(rect.minW, contLayoutRect.startMinWidth); - rect.minH = max(rect.minH, contLayoutRect.startMinHeight); - - // Resize container container if minSize was changed - if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) { - rect.w = rect.minW; - rect.h = rect.minH; - - container.layoutRect(rect); - this.recalc(container); - - // Forced recalc for example if items are hidden/shown - if (container._lastRect === null) { - var parentCtrl = container.parent(); - if (parentCtrl) { - parentCtrl._lastRect = null; - parentCtrl.recalc(); - } - } - - return; - } - - // Handle max size elements, check if they will become to wide with current options - ratio = availableSpace / totalFlex; - for (i = 0, l = maxSizeItems.length; i < l; i++) { - ctrl = maxSizeItems[i]; - ctrlLayoutRect = ctrl.layoutRect(); - maxSize = ctrlLayoutRect[maxSizeName]; - size = ctrlLayoutRect[minSizeName] + ctrlLayoutRect.flex * ratio; - - if (size > maxSize) { - availableSpace -= (ctrlLayoutRect[maxSizeName] - ctrlLayoutRect[minSizeName]); - totalFlex -= ctrlLayoutRect.flex; - ctrlLayoutRect.flex = 0; - ctrlLayoutRect.maxFlexSize = maxSize; - } else { - ctrlLayoutRect.maxFlexSize = 0; - } - } - - // Setup new ratio, target layout rect, start position - ratio = availableSpace / totalFlex; - pos = contPaddingBox[beforeName]; - rect = {}; - - // Handle pack setting moves the start position to end, center - if (totalFlex === 0) { - if (pack == "end") { - pos = availableSpace + contPaddingBox[beforeName]; - } else if (pack == "center") { - pos = Math.round( - (contLayoutRect[innerSizeName] / 2) - ((contLayoutRect[innerSizeName] - availableSpace) / 2) - ) + contPaddingBox[beforeName]; - - if (pos < 0) { - pos = contPaddingBox[beforeName]; - } - } else if (pack == "justify") { - pos = contPaddingBox[beforeName]; - spacing = Math.floor(availableSpace / (items.length - 1)); - } - } - - // Default aligning (start) the other ones needs to be calculated while doing the layout - rect[alignAxisName] = contPaddingBox[alignBeforeName]; - - // Start laying out controls - for (i = 0, l = items.length; i < l; i++) { - ctrl = items[i]; - ctrlLayoutRect = ctrl.layoutRect(); - size = ctrlLayoutRect.maxFlexSize || ctrlLayoutRect[minSizeName]; - - // Align the control on the other axis - if (align === "center") { - rect[alignAxisName] = Math.round((contLayoutRect[alignInnerSizeName] / 2) - (ctrlLayoutRect[alignSizeName] / 2)); - } else if (align === "stretch") { - rect[alignSizeName] = max( - ctrlLayoutRect[alignMinSizeName] || 0, - contLayoutRect[alignInnerSizeName] - contPaddingBox[alignBeforeName] - contPaddingBox[alignAfterName] - ); - rect[alignAxisName] = contPaddingBox[alignBeforeName]; - } else if (align === "end") { - rect[alignAxisName] = contLayoutRect[alignInnerSizeName] - ctrlLayoutRect[alignSizeName] - contPaddingBox.top; - } - - // Calculate new size based on flex - if (ctrlLayoutRect.flex > 0) { - size += ctrlLayoutRect.flex * ratio; - } - - rect[sizeName] = size; - rect[posName] = pos; - ctrl.layoutRect(rect); - - // Recalculate containers - if (ctrl.recalc) { - ctrl.recalc(); - } - - // Move x/y position - pos += size + spacing; - } - } - }); - } - ); - /** - * FlowLayout.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This layout manager will place the controls by using the browsers native layout. - * - * @-x-less FlowLayout.less - * @class tinymce.ui.FlowLayout - * @extends tinymce.ui.Layout - */ - define( - 'tinymce.core.ui.FlowLayout', [ - "tinymce.core.ui.Layout" - ], - function(Layout) { - return Layout.extend({ - Defaults: { - containerClass: 'flow-layout', - controlClass: 'flow-layout-item', - endClass: 'break' - }, - - /** - * Recalculates the positions of the controls in the specified container. - * - * @method recalc - * @param {tinymce.ui.Container} container Container instance to recalc. - */ - recalc: function(container) { - container.items().filter(':visible').each(function(ctrl) { - if (ctrl.recalc) { - ctrl.recalc(); - } - }); - }, - - isNative: function() { - return true; - } - }); - } - ); - /** - * FontInfo.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Internal class for computing font size for elements. - * - * @private - * @class tinymce.fmt.FontInfo - */ - define( - 'tinymce.core.fmt.FontInfo', [ - 'ephox.katamari.api.Fun', - 'ephox.katamari.api.Option', - 'ephox.sugar.api.node.Element', - 'ephox.sugar.api.node.Node', - 'tinymce.core.dom.DOMUtils' - ], - function(Fun, Option, Element, Node, DOMUtils) { - var getSpecifiedFontProp = function(propName, rootElm, elm) { - while (elm !== rootElm) { - if (elm.style[propName]) { - var foundStyle = elm.style[propName]; - return foundStyle !== '' ? Option.some(foundStyle) : Option.none(); - } - elm = elm.parentNode; - } - return Option.none(); - }; - - var toPt = function(fontSize) { - if (/[0-9.]+px$/.test(fontSize)) { - return Math.round(parseInt(fontSize, 10) * 72 / 96) + 'pt'; - } - - return fontSize; - }; - - var normalizeFontFamily = function(fontFamily) { - // 'Font name', Font -> Font name,Font - return fontFamily.replace(/[\'\"]/g, '').replace(/,\s+/g, ','); - }; - - var getComputedFontProp = function(propName, elm) { - return Option.from(DOMUtils.DOM.getStyle(elm, propName, true)); - }; - - var getFontProp = function(propName) { - return function(rootElm, elm) { - return Option.from(elm) - .map(Element.fromDom) - .filter(Node.isElement) - .bind(function(element) { - return getSpecifiedFontProp(propName, rootElm, element.dom()) - .or(getComputedFontProp(propName, element.dom())); - }) - .getOr(''); - }; - }; - - return { - getFontSize: getFontProp('fontSize'), - getFontFamily: Fun.compose(normalizeFontFamily, getFontProp('fontFamily')), - toPt: toPt - }; - } - ); - - /** - * FormatControls.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Internal class containing all TinyMCE specific control types such as - * format listboxes, fontlist boxes, toolbar buttons etc. - * - * @class tinymce.ui.FormatControls - */ - define( - 'tinymce.core.ui.FormatControls', [ - "tinymce.core.ui.Control", - "tinymce.core.ui.Widget", - "tinymce.core.ui.FloatPanel", - "tinymce.core.util.Tools", - "tinymce.core.util.Arr", - "tinymce.core.dom.DOMUtils", - "tinymce.core.EditorManager", - "tinymce.core.Env", - "tinymce.core.fmt.FontInfo" - ], - function(Control, Widget, FloatPanel, Tools, Arr, DOMUtils, EditorManager, Env, FontInfo) { - var each = Tools.each; - - var flatten = function(ar) { - return Arr.reduce(ar, function(result, item) { - return result.concat(item); - }, []); - }; - - EditorManager.on('AddEditor', function(e) { - var editor = e.editor; - - setupRtlMode(editor); - registerControls(editor); - setupContainer(editor); - }); - - Control.translate = function(text) { - return EditorManager.translate(text); - }; - - Widget.tooltips = !Env.iOS; - - function setupContainer(editor) { - if (editor.settings.ui_container) { - Env.container = DOMUtils.DOM.select(editor.settings.ui_container)[0]; - } - } - - function setupRtlMode(editor) { - editor.on('ScriptsLoaded', function() { - if (editor.rtl) { - Control.rtl = true; - } - }); - } - - function registerControls(editor) { - var formatMenu; - - function createListBoxChangeHandler(items, formatName) { - return function() { - var self = this; - - editor.on('nodeChange', function(e) { - var formatter = editor.formatter; - var value = null; - - each(e.parents, function(node) { - each(items, function(item) { - if (formatName) { - if (formatter.matchNode(node, formatName, { - value: item.value - })) { - value = item.value; - } - } else { - if (formatter.matchNode(node, item.value)) { - value = item.value; - } - } - - if (value) { - return false; - } - }); - - if (value) { - return false; - } - }); - - self.value(value); - }); - }; - } - - function createFontNameListBoxChangeHandler(items) { - return function() { - var self = this; - - var getFirstFont = function(fontFamily) { - return fontFamily ? fontFamily.split(',')[0] : ''; - }; - - editor.on('init nodeChange', function(e) { - var fontFamily, value = null; - - fontFamily = FontInfo.getFontFamily(editor.getBody(), e.element); - - each(items, function(item) { - if (item.value.toLowerCase() === fontFamily.toLowerCase()) { - value = item.value; - } - }); - - each(items, function(item) { - if (!value && getFirstFont(item.value).toLowerCase() === getFirstFont(fontFamily).toLowerCase()) { - value = item.value; - } - }); - - self.value(value); - - if (!value && fontFamily) { - self.text(getFirstFont(fontFamily)); - } - }); - }; - } - - function createFontSizeListBoxChangeHandler(items) { - return function() { - var self = this; - - editor.on('init nodeChange', function(e) { - var px, pt, value = null; - - px = FontInfo.getFontSize(editor.getBody(), e.element); - pt = FontInfo.toPt(px); - - each(items, function(item) { - if (item.value === px) { - value = px; - } else if (item.value === pt) { - value = pt; - } - }); - - self.value(value); - - if (!value) { - self.text(pt); - } - }); - }; - } - - function createFormats(formats) { - formats = formats.replace(/;$/, '').split(';'); - - var i = formats.length; - while (i--) { - formats[i] = formats[i].split('='); - } - - return formats; - } - - function createFormatMenu() { - var count = 0, - newFormats = []; - - var defaultStyleFormats = [{ - title: 'Headings', - items: [{ - title: 'Heading 1', - format: 'h1' - }, - { - title: 'Heading 2', - format: 'h2' - }, - { - title: 'Heading 3', - format: 'h3' - }, - { - title: 'Heading 4', - format: 'h4' - }, - { - title: 'Heading 5', - format: 'h5' - }, - { - title: 'Heading 6', - format: 'h6' - } - ] - }, - - { - title: 'Inline', - items: [{ - title: 'Bold', - icon: 'bold', - format: 'bold' - }, - { - title: 'Italic', - icon: 'italic', - format: 'italic' - }, - { - title: 'Underline', - icon: 'underline', - format: 'underline' - }, - { - title: 'Strikethrough', - icon: 'strikethrough', - format: 'strikethrough' - }, - { - title: 'Superscript', - icon: 'superscript', - format: 'superscript' - }, - { - title: 'Subscript', - icon: 'subscript', - format: 'subscript' - }, - { - title: 'Code', - icon: 'code', - format: 'code' - } - ] - }, - - { - title: 'Blocks', - items: [{ - title: 'Paragraph', - format: 'p' - }, - { - title: 'Blockquote', - format: 'blockquote' - }, - { - title: 'Div', - format: 'div' - }, - { - title: 'Pre', - format: 'pre' - } - ] - }, - - { - title: 'Alignment', - items: [{ - title: 'Left', - icon: 'alignleft', - format: 'alignleft' - }, - { - title: 'Center', - icon: 'aligncenter', - format: 'aligncenter' - }, - { - title: 'Right', - icon: 'alignright', - format: 'alignright' - }, - { - title: 'Justify', - icon: 'alignjustify', - format: 'alignjustify' - } - ] - } - ]; - - function createMenu(formats) { - var menu = []; - - if (!formats) { - return; - } - - each(formats, function(format) { - var menuItem = { - text: format.title, - icon: format.icon - }; - - if (format.items) { - menuItem.menu = createMenu(format.items); - } else { - var formatName = format.format || "custom" + count++; - - if (!format.format) { - format.name = formatName; - newFormats.push(format); - } - - menuItem.format = formatName; - menuItem.cmd = format.cmd; - } - - menu.push(menuItem); - }); - - return menu; - } - - function createStylesMenu() { - var menu; - - if (editor.settings.style_formats_merge) { - if (editor.settings.style_formats) { - menu = createMenu(defaultStyleFormats.concat(editor.settings.style_formats)); - } else { - menu = createMenu(defaultStyleFormats); - } - } else { - menu = createMenu(editor.settings.style_formats || defaultStyleFormats); - } - - return menu; - } - - editor.on('init', function() { - each(newFormats, function(format) { - editor.formatter.register(format.name, format); - }); - }); - - return { - type: 'menu', - items: createStylesMenu(), - onPostRender: function(e) { - editor.fire('renderFormatsMenu', { - control: e.control - }); - }, - itemDefaults: { - preview: true, - - textStyle: function() { - if (this.settings.format) { - return editor.formatter.getCssText(this.settings.format); - } - }, - - onPostRender: function() { - var self = this; - - self.parent().on('show', function() { - var formatName, command; - - formatName = self.settings.format; - if (formatName) { - self.disabled(!editor.formatter.canApply(formatName)); - self.active(editor.formatter.match(formatName)); - } - - command = self.settings.cmd; - if (command) { - self.active(editor.queryCommandState(command)); - } - }); - }, - - onclick: function() { - if (this.settings.format) { - toggleFormat(this.settings.format); - } - - if (this.settings.cmd) { - editor.execCommand(this.settings.cmd); - } - } - } - }; - } - - formatMenu = createFormatMenu(); - - function initOnPostRender(name) { - return function() { - var self = this; - - // TODO: Fix this - if (editor.formatter) { - editor.formatter.formatChanged(name, function(state) { - self.active(state); - }); - } else { - editor.on('init', function() { - editor.formatter.formatChanged(name, function(state) { - self.active(state); - }); - }); - } - }; - } - - // Simple format controls <control/format>:<UI text> - each({ - bold: 'Bold', - italic: 'Italic', - underline: 'Underline', - strikethrough: 'Strikethrough', - subscript: 'Subscript', - superscript: 'Superscript' - }, function(text, name) { - editor.addButton(name, { - tooltip: text, - onPostRender: initOnPostRender(name), - onclick: function() { - toggleFormat(name); - } - }); - }); - - // Simple command controls <control>:[<UI text>,<Command>] - each({ - outdent: ['Decrease indent', 'Outdent'], - indent: ['Increase indent', 'Indent'], - cut: ['Cut', 'Cut'], - copy: ['Copy', 'Copy'], - paste: ['Paste', 'Paste'], - help: ['Help', 'mceHelp'], - selectall: ['Select all', 'SelectAll'], - removeformat: ['Clear formatting', 'RemoveFormat'], - visualaid: ['Visual aids', 'mceToggleVisualAid'], - newdocument: ['New document', 'mceNewDocument'] - }, function(item, name) { - editor.addButton(name, { - tooltip: item[0], - cmd: item[1] - }); - }); - - // Simple command controls with format state - each({ - blockquote: ['Blockquote', 'mceBlockQuote'], - subscript: ['Subscript', 'Subscript'], - superscript: ['Superscript', 'Superscript'], - alignleft: ['Align left', 'JustifyLeft'], - aligncenter: ['Align center', 'JustifyCenter'], - alignright: ['Align right', 'JustifyRight'], - alignjustify: ['Justify', 'JustifyFull'], - alignnone: ['No alignment', 'JustifyNone'] - }, function(item, name) { - editor.addButton(name, { - tooltip: item[0], - cmd: item[1], - onPostRender: initOnPostRender(name) - }); - }); - - function toggleUndoRedoState(type) { - return function() { - var self = this; - - function checkState() { - var typeFn = type == 'redo' ? 'hasRedo' : 'hasUndo'; - return editor.undoManager ? editor.undoManager[typeFn]() : false; - } - - self.disabled(!checkState()); - editor.on('Undo Redo AddUndo TypingUndo ClearUndos SwitchMode', function() { - self.disabled(editor.readonly || !checkState()); - }); - }; - } - - function toggleVisualAidState() { - var self = this; - - editor.on('VisualAid', function(e) { - self.active(e.hasVisual); - }); - - self.active(editor.hasVisual); - } - - var trimMenuItems = function(menuItems) { - var outputMenuItems = menuItems; - - if (outputMenuItems.length > 0 && outputMenuItems[0].text === '-') { - outputMenuItems = outputMenuItems.slice(1); - } - - if (outputMenuItems.length > 0 && outputMenuItems[outputMenuItems.length - 1].text === '-') { - outputMenuItems = outputMenuItems.slice(0, outputMenuItems.length - 1); - } - - return outputMenuItems; - }; - - var createCustomMenuItems = function(names) { - var items, nameList; - - if (typeof names === 'string') { - nameList = names.split(' '); - } else if (Tools.isArray(names)) { - return flatten(Tools.map(names, createCustomMenuItems)); - } - - items = Tools.grep(nameList, function(name) { - return name === '|' || name in editor.menuItems; - }); - - return Tools.map(items, function(name) { - return name === '|' ? { - text: '-' - } : editor.menuItems[name]; - }); - }; - - var createContextMenuItems = function(context) { - var outputMenuItems = [{ - text: '-' - }]; - var menuItems = Tools.grep(editor.menuItems, function(menuItem) { - return menuItem.context === context; - }); - - Tools.each(menuItems, function(menuItem) { - if (menuItem.separator == 'before') { - outputMenuItems.push({ - text: '|' - }); - } - - if (menuItem.prependToContext) { - outputMenuItems.unshift(menuItem); - } else { - outputMenuItems.push(menuItem); - } - - if (menuItem.separator == 'after') { - outputMenuItems.push({ - text: '|' - }); - } - }); - - return outputMenuItems; - }; - - var createInsertMenu = function(editorSettings) { - if (editorSettings.insert_button_items) { - return trimMenuItems(createCustomMenuItems(editorSettings.insert_button_items)); - } else { - return trimMenuItems(createContextMenuItems('insert')); - } - }; - - editor.addButton('undo', { - tooltip: 'Undo', - onPostRender: toggleUndoRedoState('undo'), - cmd: 'undo' - }); - - editor.addButton('redo', { - tooltip: 'Redo', - onPostRender: toggleUndoRedoState('redo'), - cmd: 'redo' - }); - - editor.addMenuItem('newdocument', { - text: 'New document', - icon: 'newdocument', - cmd: 'mceNewDocument' - }); - - editor.addMenuItem('undo', { - text: 'Undo', - icon: 'undo', - shortcut: 'Meta+Z', - onPostRender: toggleUndoRedoState('undo'), - cmd: 'undo' - }); - - editor.addMenuItem('redo', { - text: 'Redo', - icon: 'redo', - shortcut: 'Meta+Y', - onPostRender: toggleUndoRedoState('redo'), - cmd: 'redo' - }); - - editor.addMenuItem('visualaid', { - text: 'Visual aids', - selectable: true, - onPostRender: toggleVisualAidState, - cmd: 'mceToggleVisualAid' - }); - - editor.addButton('remove', { - tooltip: 'Remove', - icon: 'remove', - cmd: 'Delete' - }); - - editor.addButton('insert', { - type: 'menubutton', - icon: 'insert', - menu: [], - oncreatemenu: function() { - this.menu.add(createInsertMenu(editor.settings)); - this.menu.renderNew(); - } - }); - - each({ - cut: ['Cut', 'Cut', 'Meta+X'], - copy: ['Copy', 'Copy', 'Meta+C'], - paste: ['Paste', 'Paste', 'Meta+V'], - selectall: ['Select all', 'SelectAll', 'Meta+A'], - bold: ['Bold', 'Bold', 'Meta+B'], - italic: ['Italic', 'Italic', 'Meta+I'], - underline: ['Underline', 'Underline', 'Meta+U'], - strikethrough: ['Strikethrough', 'Strikethrough'], - subscript: ['Subscript', 'Subscript'], - superscript: ['Superscript', 'Superscript'], - removeformat: ['Clear formatting', 'RemoveFormat'] - }, function(item, name) { - editor.addMenuItem(name, { - text: item[0], - icon: name, - shortcut: item[2], - cmd: item[1] - }); - }); - - editor.on('mousedown', function() { - FloatPanel.hideAll(); - }); - - function toggleFormat(fmt) { - if (fmt.control) { - fmt = fmt.control.value(); - } - - if (fmt) { - editor.execCommand('mceToggleFormat', false, fmt); - } - } - - function hideMenuObjects(menu) { - var count = menu.length; - - Tools.each(menu, function(item) { - if (item.menu) { - item.hidden = hideMenuObjects(item.menu) === 0; - } - - var formatName = item.format; - if (formatName) { - item.hidden = !editor.formatter.canApply(formatName); - } - - if (item.hidden) { - count--; - } - }); - - return count; - } - - function hideFormatMenuItems(menu) { - var count = menu.items().length; - - menu.items().each(function(item) { - if (item.menu) { - item.visible(hideFormatMenuItems(item.menu) > 0); - } - - if (!item.menu && item.settings.menu) { - item.visible(hideMenuObjects(item.settings.menu) > 0); - } - - var formatName = item.settings.format; - if (formatName) { - item.visible(editor.formatter.canApply(formatName)); - } - - if (!item.visible()) { - count--; - } - }); - - return count; - } - - editor.addButton('styleselect', { - type: 'menubutton', - text: 'Formats', - menu: formatMenu, - onShowMenu: function() { - if (editor.settings.style_formats_autohide) { - hideFormatMenuItems(this.menu); - } - } - }); - - editor.addButton('formatselect', function() { - var items = [], - blocks = createFormats(editor.settings.block_formats || - 'Paragraph=p;' + - 'Heading 1=h1;' + - 'Heading 2=h2;' + - 'Heading 3=h3;' + - 'Heading 4=h4;' + - 'Heading 5=h5;' + - 'Heading 6=h6;' + - 'Preformatted=pre' - ); - - each(blocks, function(block) { - items.push({ - text: block[0], - value: block[1], - textStyle: function() { - return editor.formatter.getCssText(block[1]); - } - }); - }); - - return { - type: 'listbox', - text: blocks[0][0], - values: items, - fixedWidth: true, - onselect: toggleFormat, - onPostRender: createListBoxChangeHandler(items) - }; - }); - - editor.addButton('fontselect', function() { - var defaultFontsFormats = - 'Andale Mono=andale mono,monospace;' + - 'Arial=arial,helvetica,sans-serif;' + - 'Arial Black=arial black,sans-serif;' + - 'Book Antiqua=book antiqua,palatino,serif;' + - 'Comic Sans MS=comic sans ms,sans-serif;' + - 'Courier New=courier new,courier,monospace;' + - 'Georgia=georgia,palatino,serif;' + - 'Helvetica=helvetica,arial,sans-serif;' + - 'Impact=impact,sans-serif;' + - 'Symbol=symbol;' + - 'Tahoma=tahoma,arial,helvetica,sans-serif;' + - 'Terminal=terminal,monaco,monospace;' + - 'Times New Roman=times new roman,times,serif;' + - 'Trebuchet MS=trebuchet ms,geneva,sans-serif;' + - 'Verdana=verdana,geneva,sans-serif;' + - 'Webdings=webdings;' + - 'Wingdings=wingdings,zapf dingbats'; - - var items = [], - fonts = createFormats(editor.settings.font_formats || defaultFontsFormats); - - each(fonts, function(font) { - items.push({ - text: { - raw: font[0] - }, - value: font[1], - textStyle: font[1].indexOf('dings') == -1 ? 'font-family:' + font[1] : '' - }); - }); - - return { - type: 'listbox', - text: 'Font Family', - tooltip: 'Font Family', - values: items, - fixedWidth: true, - onPostRender: createFontNameListBoxChangeHandler(items), - onselect: function(e) { - if (e.control.settings.value) { - editor.execCommand('FontName', false, e.control.settings.value); - } - } - }; - }); - - editor.addButton('fontsizeselect', function() { - var items = [], - defaultFontsizeFormats = '8pt 10pt 12pt 14pt 18pt 24pt 36pt'; - var fontsizeFormats = editor.settings.fontsize_formats || defaultFontsizeFormats; - - each(fontsizeFormats.split(' '), function(item) { - var text = item, - value = item; - // Allow text=value font sizes. - var values = item.split('='); - if (values.length > 1) { - text = values[0]; - value = values[1]; - } - items.push({ - text: text, - value: value - }); - }); - - return { - type: 'listbox', - text: 'Font Sizes', - tooltip: 'Font Sizes', - values: items, - fixedWidth: true, - onPostRender: createFontSizeListBoxChangeHandler(items), - onclick: function(e) { - if (e.control.settings.value) { - editor.execCommand('FontSize', false, e.control.settings.value); - } - } - }; - }); - - editor.addMenuItem('formats', { - text: 'Formats', - menu: formatMenu - }); - } - - return {}; - } - ); - - /** - * GridLayout.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This layout manager places controls in a grid. - * - * @setting {Number} spacing Spacing between controls. - * @setting {Number} spacingH Horizontal spacing between controls. - * @setting {Number} spacingV Vertical spacing between controls. - * @setting {Number} columns Number of columns to use. - * @setting {String/Array} alignH start|end|center|stretch or array of values for each column. - * @setting {String/Array} alignV start|end|center|stretch or array of values for each column. - * @setting {String} pack start|end - * - * @class tinymce.ui.GridLayout - * @extends tinymce.ui.AbsoluteLayout - */ - define( - 'tinymce.core.ui.GridLayout', [ - "tinymce.core.ui.AbsoluteLayout" - ], - function(AbsoluteLayout) { - "use strict"; - - return AbsoluteLayout.extend({ - /** - * Recalculates the positions of the controls in the specified container. - * - * @method recalc - * @param {tinymce.ui.Container} container Container instance to recalc. - */ - recalc: function(container) { - var settings, rows, cols, items, contLayoutRect, width, height, rect, - ctrlLayoutRect, ctrl, x, y, posX, posY, ctrlSettings, contPaddingBox, align, spacingH, spacingV, alignH, alignV, maxX, maxY, - colWidths = [], - rowHeights = [], - ctrlMinWidth, ctrlMinHeight, availableWidth, availableHeight, reverseRows, idx; - - // Get layout settings - settings = container.settings; - items = container.items().filter(':visible'); - contLayoutRect = container.layoutRect(); - cols = settings.columns || Math.ceil(Math.sqrt(items.length)); - rows = Math.ceil(items.length / cols); - spacingH = settings.spacingH || settings.spacing || 0; - spacingV = settings.spacingV || settings.spacing || 0; - alignH = settings.alignH || settings.align; - alignV = settings.alignV || settings.align; - contPaddingBox = container.paddingBox; - reverseRows = 'reverseRows' in settings ? settings.reverseRows : container.isRtl(); - - if (alignH && typeof alignH == "string") { - alignH = [alignH]; - } - - if (alignV && typeof alignV == "string") { - alignV = [alignV]; - } - - // Zero padd columnWidths - for (x = 0; x < cols; x++) { - colWidths.push(0); - } - - // Zero padd rowHeights - for (y = 0; y < rows; y++) { - rowHeights.push(0); - } - - // Calculate columnWidths and rowHeights - for (y = 0; y < rows; y++) { - for (x = 0; x < cols; x++) { - ctrl = items[y * cols + x]; - - // Out of bounds - if (!ctrl) { - break; - } - - ctrlLayoutRect = ctrl.layoutRect(); - ctrlMinWidth = ctrlLayoutRect.minW; - ctrlMinHeight = ctrlLayoutRect.minH; - - colWidths[x] = ctrlMinWidth > colWidths[x] ? ctrlMinWidth : colWidths[x]; - rowHeights[y] = ctrlMinHeight > rowHeights[y] ? ctrlMinHeight : rowHeights[y]; - } - } - - // Calculate maxX - availableWidth = contLayoutRect.innerW - contPaddingBox.left - contPaddingBox.right; - for (maxX = 0, x = 0; x < cols; x++) { - maxX += colWidths[x] + (x > 0 ? spacingH : 0); - availableWidth -= (x > 0 ? spacingH : 0) + colWidths[x]; - } - - // Calculate maxY - availableHeight = contLayoutRect.innerH - contPaddingBox.top - contPaddingBox.bottom; - for (maxY = 0, y = 0; y < rows; y++) { - maxY += rowHeights[y] + (y > 0 ? spacingV : 0); - availableHeight -= (y > 0 ? spacingV : 0) + rowHeights[y]; - } - - maxX += contPaddingBox.left + contPaddingBox.right; - maxY += contPaddingBox.top + contPaddingBox.bottom; - - // Calculate minW/minH - rect = {}; - rect.minW = maxX + (contLayoutRect.w - contLayoutRect.innerW); - rect.minH = maxY + (contLayoutRect.h - contLayoutRect.innerH); - - rect.contentW = rect.minW - contLayoutRect.deltaW; - rect.contentH = rect.minH - contLayoutRect.deltaH; - rect.minW = Math.min(rect.minW, contLayoutRect.maxW); - rect.minH = Math.min(rect.minH, contLayoutRect.maxH); - rect.minW = Math.max(rect.minW, contLayoutRect.startMinWidth); - rect.minH = Math.max(rect.minH, contLayoutRect.startMinHeight); - - // Resize container container if minSize was changed - if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) { - rect.w = rect.minW; - rect.h = rect.minH; - - container.layoutRect(rect); - this.recalc(container); - - // Forced recalc for example if items are hidden/shown - if (container._lastRect === null) { - var parentCtrl = container.parent(); - if (parentCtrl) { - parentCtrl._lastRect = null; - parentCtrl.recalc(); - } - } - - return; - } - - // Update contentW/contentH so absEnd moves correctly - if (contLayoutRect.autoResize) { - rect = container.layoutRect(rect); - rect.contentW = rect.minW - contLayoutRect.deltaW; - rect.contentH = rect.minH - contLayoutRect.deltaH; - } - - var flexV; - - if (settings.packV == 'start') { - flexV = 0; - } else { - flexV = availableHeight > 0 ? Math.floor(availableHeight / rows) : 0; - } - - // Calculate totalFlex - var totalFlex = 0; - var flexWidths = settings.flexWidths; - if (flexWidths) { - for (x = 0; x < flexWidths.length; x++) { - totalFlex += flexWidths[x]; - } - } else { - totalFlex = cols; - } - - // Calculate new column widths based on flex values - var ratio = availableWidth / totalFlex; - for (x = 0; x < cols; x++) { - colWidths[x] += flexWidths ? flexWidths[x] * ratio : ratio; - } - - // Move/resize controls - posY = contPaddingBox.top; - for (y = 0; y < rows; y++) { - posX = contPaddingBox.left; - height = rowHeights[y] + flexV; - - for (x = 0; x < cols; x++) { - if (reverseRows) { - idx = y * cols + cols - 1 - x; - } else { - idx = y * cols + x; - } - - ctrl = items[idx]; - - // No more controls to render then break - if (!ctrl) { - break; - } - - // Get control settings and calculate x, y - ctrlSettings = ctrl.settings; - ctrlLayoutRect = ctrl.layoutRect(); - width = Math.max(colWidths[x], ctrlLayoutRect.startMinWidth); - ctrlLayoutRect.x = posX; - ctrlLayoutRect.y = posY; - - // Align control horizontal - align = ctrlSettings.alignH || (alignH ? (alignH[x] || alignH[0]) : null); - if (align == "center") { - ctrlLayoutRect.x = posX + (width / 2) - (ctrlLayoutRect.w / 2); - } else if (align == "right") { - ctrlLayoutRect.x = posX + width - ctrlLayoutRect.w; - } else if (align == "stretch") { - ctrlLayoutRect.w = width; - } - - // Align control vertical - align = ctrlSettings.alignV || (alignV ? (alignV[x] || alignV[0]) : null); - if (align == "center") { - ctrlLayoutRect.y = posY + (height / 2) - (ctrlLayoutRect.h / 2); - } else if (align == "bottom") { - ctrlLayoutRect.y = posY + height - ctrlLayoutRect.h; - } else if (align == "stretch") { - ctrlLayoutRect.h = height; - } - - ctrl.layoutRect(ctrlLayoutRect); - - posX += width + spacingH; - - if (ctrl.recalc) { - ctrl.recalc(); - } - } - - posY += height + spacingV; - } - } - }); - } - ); - - /** - * Iframe.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /*jshint scripturl:true */ - - /** - * This class creates an iframe. - * - * @setting {String} url Url to open in the iframe. - * - * @-x-less Iframe.less - * @class tinymce.ui.Iframe - * @extends tinymce.ui.Widget - */ - define( - 'tinymce.core.ui.Iframe', [ - "tinymce.core.ui.Widget", - "tinymce.core.util.Delay" - ], - function(Widget, Delay) { - "use strict"; - - return Widget.extend({ - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this; - - self.classes.add('iframe'); - self.canFocus = false; - - /*eslint no-script-url:0 */ - return ( - '<iframe id="' + self._id + '" class="' + self.classes + '" tabindex="-1" src="' + - (self.settings.url || "javascript:''") + '" frameborder="0"></iframe>' - ); - }, - - /** - * Setter for the iframe source. - * - * @method src - * @param {String} src Source URL for iframe. - */ - src: function(src) { - this.getEl().src = src; - }, - - /** - * Inner HTML for the iframe. - * - * @method html - * @param {String} html HTML string to set as HTML inside the iframe. - * @param {function} callback Optional callback to execute when the iframe body is filled with contents. - * @return {tinymce.ui.Iframe} Current iframe control. - */ - html: function(html, callback) { - var self = this, - body = this.getEl().contentWindow.document.body; - - // Wait for iframe to initialize IE 10 takes time - if (!body) { - Delay.setTimeout(function() { - self.html(html); - }); - } else { - body.innerHTML = html; - - if (callback) { - callback(); - } - } - - return this; - } - }); - } - ); - - /** - * InfoBox.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * .... - * - * @-x-less InfoBox.less - * @class tinymce.ui.InfoBox - * @extends tinymce.ui.Widget - */ - define( - 'tinymce.core.ui.InfoBox', [ - "tinymce.core.ui.Widget" - ], - function(Widget) { - "use strict"; - - return Widget.extend({ - /** - * Constructs a instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {Boolean} multiline Multiline label. - */ - init: function(settings) { - var self = this; - - self._super(settings); - self.classes.add('widget').add('infobox'); - self.canFocus = false; - }, - - severity: function(level) { - this.classes.remove('error'); - this.classes.remove('warning'); - this.classes.remove('success'); - this.classes.add(level); - }, - - help: function(state) { - this.state.set('help', state); - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - prefix = self.classPrefix; - - return ( - '<div id="' + self._id + '" class="' + self.classes + '">' + - '<div id="' + self._id + '-body">' + - self.encode(self.state.get('text')) + - '<button role="button" tabindex="-1">' + - '<i class="' + prefix + 'ico ' + prefix + 'i-help"></i>' + - '</button>' + - '</div>' + - '</div>' - ); - }, - - bindStates: function() { - var self = this; - - self.state.on('change:text', function(e) { - self.getEl('body').firstChild.data = self.encode(e.value); - - if (self.state.get('rendered')) { - self.updateLayoutRect(); - } - }); - - self.state.on('change:help', function(e) { - self.classes.toggle('has-help', e.value); - - if (self.state.get('rendered')) { - self.updateLayoutRect(); - } - }); - - return self._super(); - } - }); - } - ); - - /** - * Label.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class creates a label element. A label is a simple text control - * that can be bound to other controls. - * - * @-x-less Label.less - * @class tinymce.ui.Label - * @extends tinymce.ui.Widget - */ - define( - 'tinymce.core.ui.Label', [ - "tinymce.core.ui.Widget", - "tinymce.core.ui.DomUtils" - ], - function(Widget, DomUtils) { - "use strict"; - - return Widget.extend({ - /** - * Constructs a instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {Boolean} multiline Multiline label. - */ - init: function(settings) { - var self = this; - - self._super(settings); - self.classes.add('widget').add('label'); - self.canFocus = false; - - if (settings.multiline) { - self.classes.add('autoscroll'); - } - - if (settings.strong) { - self.classes.add('strong'); - } - }, - - /** - * Initializes the current controls layout rect. - * This will be executed by the layout managers to determine the - * default minWidth/minHeight etc. - * - * @method initLayoutRect - * @return {Object} Layout rect instance. - */ - initLayoutRect: function() { - var self = this, - layoutRect = self._super(); - - if (self.settings.multiline) { - var size = DomUtils.getSize(self.getEl()); - - // Check if the text fits within maxW if not then try word wrapping it - if (size.width > layoutRect.maxW) { - layoutRect.minW = layoutRect.maxW; - self.classes.add('multiline'); - } - - self.getEl().style.width = layoutRect.minW + 'px'; - layoutRect.startMinH = layoutRect.h = layoutRect.minH = Math.min(layoutRect.maxH, DomUtils.getSize(self.getEl()).height); - } - - return layoutRect; - }, - - /** - * Repaints the control after a layout operation. - * - * @method repaint - */ - repaint: function() { - var self = this; - - if (!self.settings.multiline) { - self.getEl().style.lineHeight = self.layoutRect().h + 'px'; - } - - return self._super(); - }, - - severity: function(level) { - this.classes.remove('error'); - this.classes.remove('warning'); - this.classes.remove('success'); - this.classes.add(level); - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - targetCtrl, forName, forId = self.settings.forId; - var text = self.settings.html ? self.settings.html : self.encode(self.state.get('text')); - - if (!forId && (forName = self.settings.forName)) { - targetCtrl = self.getRoot().find('#' + forName)[0]; - - if (targetCtrl) { - forId = targetCtrl._id; - } - } - - if (forId) { - return ( - '<label id="' + self._id + '" class="' + self.classes + '"' + (forId ? ' for="' + forId + '"' : '') + '>' + - text + - '</label>' - ); - } - - return ( - '<span id="' + self._id + '" class="' + self.classes + '">' + - text + - '</span>' - ); - }, - - bindStates: function() { - var self = this; - - self.state.on('change:text', function(e) { - self.innerHtml(self.encode(e.value)); - - if (self.state.get('rendered')) { - self.updateLayoutRect(); - } - }); - - return self._super(); - } - }); - } - ); - - /** - * Toolbar.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a new toolbar. - * - * @class tinymce.ui.Toolbar - * @extends tinymce.ui.Container - */ - define( - 'tinymce.core.ui.Toolbar', [ - "tinymce.core.ui.Container" - ], - function(Container) { - "use strict"; - - return Container.extend({ - Defaults: { - role: 'toolbar', - layout: 'flow' - }, - - /** - * Constructs a instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - */ - init: function(settings) { - var self = this; - - self._super(settings); - self.classes.add('toolbar'); - }, - - /** - * Called after the control has been rendered. - * - * @method postRender - */ - postRender: function() { - var self = this; - - self.items().each(function(ctrl) { - ctrl.classes.add('toolbar-item'); - }); - - return self._super(); - } - }); - } - ); - /** - * MenuBar.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a new menubar. - * - * @-x-less MenuBar.less - * @class tinymce.ui.MenuBar - * @extends tinymce.ui.Container - */ - define( - 'tinymce.core.ui.MenuBar', [ - "tinymce.core.ui.Toolbar" - ], - function(Toolbar) { - "use strict"; - - return Toolbar.extend({ - Defaults: { - role: 'menubar', - containerCls: 'menubar', - ariaRoot: true, - defaults: { - type: 'menubutton' - } - } - }); - } - ); - /** - * MenuButton.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a new menu button. - * - * @-x-less MenuButton.less - * @class tinymce.ui.MenuButton - * @extends tinymce.ui.Button - */ - define( - 'tinymce.core.ui.MenuButton', [ - "tinymce.core.ui.Button", - "tinymce.core.ui.Factory", - "tinymce.core.ui.MenuBar" - ], - function(Button, Factory, MenuBar) { - "use strict"; - - // TODO: Maybe add as some global function - function isChildOf(node, parent) { - while (node) { - if (parent === node) { - return true; - } - - node = node.parentNode; - } - - return false; - } - - var MenuButton = Button.extend({ - /** - * Constructs a instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - */ - init: function(settings) { - var self = this; - - self._renderOpen = true; - - self._super(settings); - settings = self.settings; - - self.classes.add('menubtn'); - - if (settings.fixedWidth) { - self.classes.add('fixed-width'); - } - - self.aria('haspopup', true); - - self.state.set('menu', settings.menu || self.render()); - }, - - /** - * Shows the menu for the button. - * - * @method showMenu - */ - showMenu: function(toggle) { - var self = this, - menu; - - if (self.menu && self.menu.visible() && toggle !== false) { - return self.hideMenu(); - } - - if (!self.menu) { - menu = self.state.get('menu') || []; - - // Is menu array then auto constuct menu control - if (menu.length) { - menu = { - type: 'menu', - items: menu - }; - } else { - menu.type = menu.type || 'menu'; - } - - if (!menu.renderTo) { - self.menu = Factory.create(menu).parent(self).renderTo(); - } else { - self.menu = menu.parent(self).show().renderTo(); - } - - self.fire('createmenu'); - self.menu.reflow(); - self.menu.on('cancel', function(e) { - if (e.control.parent() === self.menu) { - e.stopPropagation(); - self.focus(); - self.hideMenu(); - } - }); - - // Move focus to button when a menu item is selected/clicked - self.menu.on('select', function() { - self.focus(); - }); - - self.menu.on('show hide', function(e) { - if (e.control == self.menu) { - self.activeMenu(e.type == 'show'); - } - - self.aria('expanded', e.type == 'show'); - }).fire('show'); - } - - self.menu.show(); - self.menu.layoutRect({ - w: self.layoutRect().w - }); - self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']); - self.fire('showmenu'); - }, - - /** - * Hides the menu for the button. - * - * @method hideMenu - */ - hideMenu: function() { - var self = this; - - if (self.menu) { - self.menu.items().each(function(item) { - if (item.hideMenu) { - item.hideMenu(); - } - }); - - self.menu.hide(); - } - }, - - /** - * Sets the active menu state. - * - * @private - */ - activeMenu: function(state) { - this.classes.toggle('active', state); - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - id = self._id, - prefix = self.classPrefix; - var icon = self.settings.icon, - image, text = self.state.get('text'), - textHtml = ''; - - image = self.settings.image; - if (image) { - icon = 'none'; - - // Support for [high dpi, low dpi] image sources - if (typeof image != "string") { - image = window.getSelection ? image[0] : image[1]; - } - - image = ' style="background-image: url(\'' + image + '\')"'; - } else { - image = ''; - } - - if (text) { - self.classes.add('btn-has-text'); - textHtml = '<span class="' + prefix + 'txt">' + self.encode(text) + '</span>'; - } - - icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : ''; - - self.aria('role', self.parent() instanceof MenuBar ? 'menuitem' : 'button'); - - return ( - '<div id="' + id + '" class="' + self.classes + '" tabindex="-1" aria-labelledby="' + id + '">' + - '<button id="' + id + '-open" role="presentation" type="button" tabindex="-1">' + - (icon ? '<i class="' + icon + '"' + image + '></i>' : '') + - textHtml + - ' <i class="' + prefix + 'caret"></i>' + - '</button>' + - '</div>' - ); - }, - - /** - * Gets invoked after the control has been rendered. - * - * @method postRender - */ - postRender: function() { - var self = this; - - self.on('click', function(e) { - if (e.control === self && isChildOf(e.target, self.getEl())) { - self.focus(); - self.showMenu(!e.aria); - - if (e.aria) { - self.menu.items().filter(':visible')[0].focus(); - } - } - }); - - self.on('mouseenter', function(e) { - var overCtrl = e.control, - parent = self.parent(), - hasVisibleSiblingMenu; - - if (overCtrl && parent && overCtrl instanceof MenuButton && overCtrl.parent() == parent) { - parent.items().filter('MenuButton').each(function(ctrl) { - if (ctrl.hideMenu && ctrl != overCtrl) { - if (ctrl.menu && ctrl.menu.visible()) { - hasVisibleSiblingMenu = true; - } - - ctrl.hideMenu(); - } - }); - - if (hasVisibleSiblingMenu) { - overCtrl.focus(); // Fix for: #5887 - overCtrl.showMenu(); - } - } - }); - - return self._super(); - }, - - bindStates: function() { - var self = this; - - self.state.on('change:menu', function() { - if (self.menu) { - self.menu.remove(); - } - - self.menu = null; - }); - - return self._super(); - }, - - /** - * Removes the control and it's menus. - * - * @method remove - */ - remove: function() { - this._super(); - - if (this.menu) { - this.menu.remove(); - } - } - }); - - return MenuButton; - } - ); - - /** - * MenuItem.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a new menu item. - * - * @-x-less MenuItem.less - * @class tinymce.ui.MenuItem - * @extends tinymce.ui.Control - */ - define( - 'tinymce.core.ui.MenuItem', [ - "tinymce.core.ui.Widget", - "tinymce.core.ui.Factory", - "tinymce.core.Env", - "tinymce.core.util.Delay" - ], - function(Widget, Factory, Env, Delay) { - "use strict"; - - return Widget.extend({ - Defaults: { - border: 0, - role: 'menuitem' - }, - - /** - * Constructs a instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {Boolean} selectable Selectable menu. - * @setting {Array} menu Submenu array with items. - * @setting {String} shortcut Shortcut to display for menu item. Example: Ctrl+X - */ - init: function(settings) { - var self = this, - text; - - self._super(settings); - - settings = self.settings; - - self.classes.add('menu-item'); - - if (settings.menu) { - self.classes.add('menu-item-expand'); - } - - if (settings.preview) { - self.classes.add('menu-item-preview'); - } - - text = self.state.get('text'); - if (text === '-' || text === '|') { - self.classes.add('menu-item-sep'); - self.aria('role', 'separator'); - self.state.set('text', '-'); - } - - if (settings.selectable) { - self.aria('role', 'menuitemcheckbox'); - self.classes.add('menu-item-checkbox'); - settings.icon = 'selected'; - } - - if (!settings.preview && !settings.selectable) { - self.classes.add('menu-item-normal'); - } - - self.on('mousedown', function(e) { - e.preventDefault(); - }); - - if (settings.menu && !settings.ariaHideMenu) { - self.aria('haspopup', true); - } - }, - - /** - * Returns true/false if the menuitem has sub menu. - * - * @method hasMenus - * @return {Boolean} True/false state if it has submenu. - */ - hasMenus: function() { - return !!this.settings.menu; - }, - - /** - * Shows the menu for the menu item. - * - * @method showMenu - */ - showMenu: function() { - var self = this, - settings = self.settings, - menu, parent = self.parent(); - - parent.items().each(function(ctrl) { - if (ctrl !== self) { - ctrl.hideMenu(); - } - }); - - if (settings.menu) { - menu = self.menu; - - if (!menu) { - menu = settings.menu; - - // Is menu array then auto constuct menu control - if (menu.length) { - menu = { - type: 'menu', - items: menu - }; - } else { - menu.type = menu.type || 'menu'; - } - - if (parent.settings.itemDefaults) { - menu.itemDefaults = parent.settings.itemDefaults; - } - - menu = self.menu = Factory.create(menu).parent(self).renderTo(); - menu.reflow(); - menu.on('cancel', function(e) { - e.stopPropagation(); - self.focus(); - menu.hide(); - }); - menu.on('show hide', function(e) { - if (e.control.items) { - e.control.items().each(function(ctrl) { - ctrl.active(ctrl.settings.selected); - }); - } - }).fire('show'); - - menu.on('hide', function(e) { - if (e.control === menu) { - self.classes.remove('selected'); - } - }); - - menu.submenu = true; - } else { - menu.show(); - } - - menu._parentMenu = parent; - - menu.classes.add('menu-sub'); - - var rel = menu.testMoveRel( - self.getEl(), - self.isRtl() ? ['tl-tr', 'bl-br', 'tr-tl', 'br-bl'] : ['tr-tl', 'br-bl', 'tl-tr', 'bl-br'] - ); - - menu.moveRel(self.getEl(), rel); - menu.rel = rel; - - rel = 'menu-sub-' + rel; - menu.classes.remove(menu._lastRel).add(rel); - menu._lastRel = rel; - - self.classes.add('selected'); - self.aria('expanded', true); - } - }, - - /** - * Hides the menu for the menu item. - * - * @method hideMenu - */ - hideMenu: function() { - var self = this; - - if (self.menu) { - self.menu.items().each(function(item) { - if (item.hideMenu) { - item.hideMenu(); - } - }); - - self.menu.hide(); - self.aria('expanded', false); - } - - return self; - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - id = self._id, - settings = self.settings, - prefix = self.classPrefix, - text = self.state.get('text'); - var icon = self.settings.icon, - image = '', - shortcut = settings.shortcut; - var url = self.encode(settings.url), - iconHtml = ''; - - // Converts shortcut format to Mac/PC variants - function convertShortcut(shortcut) { - var i, value, replace = {}; - - if (Env.mac) { - replace = { - alt: '⌥', - ctrl: '⌘', - shift: '⇧', - meta: '⌘' - }; - } else { - replace = { - meta: 'Ctrl' - }; - } - - shortcut = shortcut.split('+'); - - for (i = 0; i < shortcut.length; i++) { - value = replace[shortcut[i].toLowerCase()]; - - if (value) { - shortcut[i] = value; - } - } - - return shortcut.join('+'); - } - - function escapeRegExp(str) { - return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - } - - function markMatches(text) { - var match = settings.match || ''; - - return match ? text.replace(new RegExp(escapeRegExp(match), 'gi'), function(match) { - return '!mce~match[' + match + ']mce~match!'; - }) : text; - } - - function boldMatches(text) { - return text. - replace(new RegExp(escapeRegExp('!mce~match['), 'g'), '<b>'). - replace(new RegExp(escapeRegExp(']mce~match!'), 'g'), '</b>'); - } - - if (icon) { - self.parent().classes.add('menu-has-icons'); - } - - if (settings.image) { - image = ' style="background-image: url(\'' + settings.image + '\')"'; - } - - if (shortcut) { - shortcut = convertShortcut(shortcut); - } - - icon = prefix + 'ico ' + prefix + 'i-' + (self.settings.icon || 'none'); - iconHtml = (text !== '-' ? '<i class="' + icon + '"' + image + '></i>\u00a0' : ''); - - text = boldMatches(self.encode(markMatches(text))); - url = boldMatches(self.encode(markMatches(url))); - - return ( - '<div id="' + id + '" class="' + self.classes + '" tabindex="-1">' + - iconHtml + - (text !== '-' ? '<span id="' + id + '-text" class="' + prefix + 'text">' + text + '</span>' : '') + - (shortcut ? '<div id="' + id + '-shortcut" class="' + prefix + 'menu-shortcut">' + shortcut + '</div>' : '') + - (settings.menu ? '<div class="' + prefix + 'caret"></div>' : '') + - (url ? '<div class="' + prefix + 'menu-item-link">' + url + '</div>' : '') + - '</div>' - ); - }, - - /** - * Gets invoked after the control has been rendered. - * - * @method postRender - */ - postRender: function() { - var self = this, - settings = self.settings; - - var textStyle = settings.textStyle; - if (typeof textStyle == "function") { - textStyle = textStyle.call(this); - } - - if (textStyle) { - var textElm = self.getEl('text'); - if (textElm) { - textElm.setAttribute('style', textStyle); - } - } - - self.on('mouseenter click', function(e) { - if (e.control === self) { - if (!settings.menu && e.type === 'click') { - self.fire('select'); - - // Edge will crash if you stress it see #2660 - Delay.requestAnimationFrame(function() { - self.parent().hideAll(); - }); - } else { - self.showMenu(); - - if (e.aria) { - self.menu.focus(true); - } - } - } - }); - - self._super(); - - return self; - }, - - hover: function() { - var self = this; - - self.parent().items().each(function(ctrl) { - ctrl.classes.remove('selected'); - }); - - self.classes.toggle('selected', true); - - return self; - }, - - active: function(state) { - if (typeof state != "undefined") { - this.aria('checked', state); - } - - return this._super(state); - }, - - /** - * Removes the control and it's menus. - * - * @method remove - */ - remove: function() { - this._super(); - - if (this.menu) { - this.menu.remove(); - } - } - }); - } - ); - - /** - * Throbber.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This class enables you to display a Throbber for any element. - * - * @-x-less Throbber.less - * @class tinymce.ui.Throbber - */ - define( - 'tinymce.core.ui.Throbber', [ - "tinymce.core.dom.DomQuery", - "tinymce.core.ui.Control", - "tinymce.core.util.Delay" - ], - function($, Control, Delay) { - "use strict"; - - /** - * Constructs a new throbber. - * - * @constructor - * @param {Element} elm DOM Html element to display throbber in. - * @param {Boolean} inline Optional true/false state if the throbber should be appended to end of element for infinite scroll. - */ - return function(elm, inline) { - var self = this, - state, classPrefix = Control.classPrefix, - timer; - - /** - * Shows the throbber. - * - * @method show - * @param {Number} [time] Time to wait before showing. - * @param {function} [callback] Optional callback to execute when the throbber is shown. - * @return {tinymce.ui.Throbber} Current throbber instance. - */ - self.show = function(time, callback) { - function render() { - if (state) { - $(elm).append( - '<div class="' + classPrefix + 'throbber' + (inline ? ' ' + classPrefix + 'throbber-inline' : '') + '"></div>' - ); - - if (callback) { - callback(); - } - } - } - - self.hide(); - - state = true; - - if (time) { - timer = Delay.setTimeout(render, time); - } else { - render(); - } - - return self; - }; - - /** - * Hides the throbber. - * - * @method hide - * @return {tinymce.ui.Throbber} Current throbber instance. - */ - self.hide = function() { - var child = elm.lastChild; - - Delay.clearTimeout(timer); - - if (child && child.className.indexOf('throbber') != -1) { - child.parentNode.removeChild(child); - } - - state = false; - - return self; - }; - }; - } - ); - - /** - * Menu.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a new menu. - * - * @-x-less Menu.less - * @class tinymce.ui.Menu - * @extends tinymce.ui.FloatPanel - */ - define( - 'tinymce.core.ui.Menu', [ - "tinymce.core.ui.FloatPanel", - "tinymce.core.ui.MenuItem", - "tinymce.core.ui.Throbber", - "tinymce.core.util.Tools" - ], - function(FloatPanel, MenuItem, Throbber, Tools) { - "use strict"; - - return FloatPanel.extend({ - Defaults: { - defaultType: 'menuitem', - border: 1, - layout: 'stack', - role: 'application', - bodyRole: 'menu', - ariaRoot: true - }, - - /** - * Constructs a instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - */ - init: function(settings) { - var self = this; - - settings.autohide = true; - settings.constrainToViewport = true; - - if (typeof settings.items === 'function') { - settings.itemsFactory = settings.items; - settings.items = []; - } - - if (settings.itemDefaults) { - var items = settings.items, - i = items.length; - - while (i--) { - items[i] = Tools.extend({}, settings.itemDefaults, items[i]); - } - } - - self._super(settings); - self.classes.add('menu'); - }, - - /** - * Repaints the control after a layout operation. - * - * @method repaint - */ - repaint: function() { - this.classes.toggle('menu-align', true); - - this._super(); - - this.getEl().style.height = ''; - this.getEl('body').style.height = ''; - - return this; - }, - - /** - * Hides/closes the menu. - * - * @method cancel - */ - cancel: function() { - var self = this; - - self.hideAll(); - self.fire('select'); - }, - - /** - * Loads new items from the factory items function. - * - * @method load - */ - load: function() { - var self = this, - time, factory; - - function hideThrobber() { - if (self.throbber) { - self.throbber.hide(); - self.throbber = null; - } - } - - factory = self.settings.itemsFactory; - if (!factory) { - return; - } - - if (!self.throbber) { - self.throbber = new Throbber(self.getEl('body'), true); - - if (self.items().length === 0) { - self.throbber.show(); - self.fire('loading'); - } else { - self.throbber.show(100, function() { - self.items().remove(); - self.fire('loading'); - }); - } - - self.on('hide close', hideThrobber); - } - - self.requestTime = time = new Date().getTime(); - - self.settings.itemsFactory(function(items) { - if (items.length === 0) { - self.hide(); - return; - } - - if (self.requestTime !== time) { - return; - } - - self.getEl().style.width = ''; - self.getEl('body').style.width = ''; - - hideThrobber(); - self.items().remove(); - self.getEl('body').innerHTML = ''; - - self.add(items); - self.renderNew(); - self.fire('loaded'); - }); - }, - - /** - * Hide menu and all sub menus. - * - * @method hideAll - */ - hideAll: function() { - var self = this; - - this.find('menuitem').exec('hideMenu'); - - return self._super(); - }, - - /** - * Invoked before the menu is rendered. - * - * @method preRender - */ - preRender: function() { - var self = this; - - self.items().each(function(ctrl) { - var settings = ctrl.settings; - - if (settings.icon || settings.image || settings.selectable) { - self._hasIcons = true; - return false; - } - }); - - if (self.settings.itemsFactory) { - self.on('postrender', function() { - if (self.settings.itemsFactory) { - self.load(); - } - }); - } - - return self._super(); - } - }); - } - ); - - /** - * ListBox.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a new list box control. - * - * @-x-less ListBox.less - * @class tinymce.ui.ListBox - * @extends tinymce.ui.MenuButton - */ - define( - 'tinymce.core.ui.ListBox', [ - "tinymce.core.ui.MenuButton", - "tinymce.core.ui.Menu" - ], - function(MenuButton, Menu) { - "use strict"; - - return MenuButton.extend({ - /** - * Constructs a instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {Array} values Array with values to add to list box. - */ - init: function(settings) { - var self = this, - values, selected, selectedText, lastItemCtrl; - - function setSelected(menuValues) { - // Try to find a selected value - for (var i = 0; i < menuValues.length; i++) { - selected = menuValues[i].selected || settings.value === menuValues[i].value; - - if (selected) { - selectedText = selectedText || menuValues[i].text; - self.state.set('value', menuValues[i].value); - return true; - } - - // If the value has a submenu, try to find the selected values in that menu - if (menuValues[i].menu) { - if (setSelected(menuValues[i].menu)) { - return true; - } - } - } - } - - self._super(settings); - settings = self.settings; - - self._values = values = settings.values; - if (values) { - if (typeof settings.value != "undefined") { - setSelected(values); - } - - // Default with first item - if (!selected && values.length > 0) { - selectedText = values[0].text; - self.state.set('value', values[0].value); - } - - self.state.set('menu', values); - } - - self.state.set('text', settings.text || selectedText); - - self.classes.add('listbox'); - - self.on('select', function(e) { - var ctrl = e.control; - - if (lastItemCtrl) { - e.lastControl = lastItemCtrl; - } - - if (settings.multiple) { - ctrl.active(!ctrl.active()); - } else { - self.value(e.control.value()); - } - - lastItemCtrl = ctrl; - }); - }, - - /** - * Getter/setter function for the control value. - * - * @method value - * @param {String} [value] Value to be set. - * @return {Boolean/tinymce.ui.ListBox} Value or self if it's a set operation. - */ - bindStates: function() { - var self = this; - - function activateMenuItemsByValue(menu, value) { - if (menu instanceof Menu) { - menu.items().each(function(ctrl) { - if (!ctrl.hasMenus()) { - ctrl.active(ctrl.value() === value); - } - }); - } - } - - function getSelectedItem(menuValues, value) { - var selectedItem; - - if (!menuValues) { - return; - } - - for (var i = 0; i < menuValues.length; i++) { - if (menuValues[i].value === value) { - return menuValues[i]; - } - - if (menuValues[i].menu) { - selectedItem = getSelectedItem(menuValues[i].menu, value); - if (selectedItem) { - return selectedItem; - } - } - } - } - - self.on('show', function(e) { - activateMenuItemsByValue(e.control, self.value()); - }); - - self.state.on('change:value', function(e) { - var selectedItem = getSelectedItem(self.state.get('menu'), e.value); - - if (selectedItem) { - self.text(selectedItem.text); - } else { - self.text(self.settings.text); - } - }); - - return self._super(); - } - }); - } - ); - - /** - * Radio.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a new radio button. - * - * @-x-less Radio.less - * @class tinymce.ui.Radio - * @extends tinymce.ui.Checkbox - */ - define( - 'tinymce.core.ui.Radio', [ - "tinymce.core.ui.Checkbox" - ], - function(Checkbox) { - "use strict"; - - return Checkbox.extend({ - Defaults: { - classes: "radio", - role: "radio" - } - }); - } - ); - /** - * ResizeHandle.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Renders a resize handle that fires ResizeStart, Resize and ResizeEnd events. - * - * @-x-less ResizeHandle.less - * @class tinymce.ui.ResizeHandle - * @extends tinymce.ui.Widget - */ - define( - 'tinymce.core.ui.ResizeHandle', [ - "tinymce.core.ui.Widget", - "tinymce.core.ui.DragHelper" - ], - function(Widget, DragHelper) { - "use strict"; - - return Widget.extend({ - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - prefix = self.classPrefix; - - self.classes.add('resizehandle'); - - if (self.settings.direction == "both") { - self.classes.add('resizehandle-both'); - } - - self.canFocus = false; - - return ( - '<div id="' + self._id + '" class="' + self.classes + '">' + - '<i class="' + prefix + 'ico ' + prefix + 'i-resize"></i>' + - '</div>' - ); - }, - - /** - * Called after the control has been rendered. - * - * @method postRender - */ - postRender: function() { - var self = this; - - self._super(); - - self.resizeDragHelper = new DragHelper(this._id, { - start: function() { - self.fire('ResizeStart'); - }, - - drag: function(e) { - if (self.settings.direction != "both") { - e.deltaX = 0; - } - - self.fire('Resize', e); - }, - - stop: function() { - self.fire('ResizeEnd'); - } - }); - }, - - remove: function() { - if (this.resizeDragHelper) { - this.resizeDragHelper.destroy(); - } - - return this._super(); - } - }); - } - ); - - /** - * SelectBox.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a new select box control. - * - * @-x-less SelectBox.less - * @class tinymce.ui.SelectBox - * @extends tinymce.ui.Widget - */ - define( - 'tinymce.core.ui.SelectBox', [ - "tinymce.core.ui.Widget" - ], - function(Widget) { - "use strict"; - - function createOptions(options) { - var strOptions = ''; - if (options) { - for (var i = 0; i < options.length; i++) { - strOptions += '<option value="' + options[i] + '">' + options[i] + '</option>'; - } - } - return strOptions; - } - - return Widget.extend({ - Defaults: { - classes: "selectbox", - role: "selectbox", - options: [] - }, - /** - * Constructs a instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {Array} options Array with options to add to the select box. - */ - init: function(settings) { - var self = this; - - self._super(settings); - - if (self.settings.size) { - self.size = self.settings.size; - } - - if (self.settings.options) { - self._options = self.settings.options; - } - - self.on('keydown', function(e) { - var rootControl; - - if (e.keyCode == 13) { - e.preventDefault(); - - // Find root control that we can do toJSON on - self.parents().reverse().each(function(ctrl) { - if (ctrl.toJSON) { - rootControl = ctrl; - return false; - } - }); - - // Fire event on current text box with the serialized data of the whole form - self.fire('submit', { - data: rootControl.toJSON() - }); - } - }); - }, - - /** - * Getter/setter function for the options state. - * - * @method options - * @param {Array} [state] State to be set. - * @return {Array|tinymce.ui.SelectBox} Array of string options. - */ - options: function(state) { - if (!arguments.length) { - return this.state.get('options'); - } - - this.state.set('options', state); - - return this; - }, - - renderHtml: function() { - var self = this, - options, size = ''; - - options = createOptions(self._options); - - if (self.size) { - size = ' size = "' + self.size + '"'; - } - - return ( - '<select id="' + self._id + '" class="' + self.classes + '"' + size + '>' + - options + - '</select>' - ); - }, - - bindStates: function() { - var self = this; - - self.state.on('change:options', function(e) { - self.getEl().innerHTML = createOptions(e.value); - }); - - return self._super(); - } - }); - } - ); - - /** - * Slider.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Slider control. - * - * @-x-less Slider.less - * @class tinymce.ui.Slider - * @extends tinymce.ui.Widget - */ - define( - 'tinymce.core.ui.Slider', [ - "tinymce.core.ui.Widget", - "tinymce.core.ui.DragHelper", - "tinymce.core.ui.DomUtils" - ], - function(Widget, DragHelper, DomUtils) { - "use strict"; - - function constrain(value, minVal, maxVal) { - if (value < minVal) { - value = minVal; - } - - if (value > maxVal) { - value = maxVal; - } - - return value; - } - - function setAriaProp(el, name, value) { - el.setAttribute('aria-' + name, value); - } - - function updateSliderHandle(ctrl, value) { - var maxHandlePos, shortSizeName, sizeName, stylePosName, styleValue, handleEl; - - if (ctrl.settings.orientation == "v") { - stylePosName = "top"; - sizeName = "height"; - shortSizeName = "h"; - } else { - stylePosName = "left"; - sizeName = "width"; - shortSizeName = "w"; - } - - handleEl = ctrl.getEl('handle'); - maxHandlePos = (ctrl.layoutRect()[shortSizeName] || 100) - DomUtils.getSize(handleEl)[sizeName]; - - styleValue = (maxHandlePos * ((value - ctrl._minValue) / (ctrl._maxValue - ctrl._minValue))) + 'px'; - handleEl.style[stylePosName] = styleValue; - handleEl.style.height = ctrl.layoutRect().h + 'px'; - - setAriaProp(handleEl, 'valuenow', value); - setAriaProp(handleEl, 'valuetext', '' + ctrl.settings.previewFilter(value)); - setAriaProp(handleEl, 'valuemin', ctrl._minValue); - setAriaProp(handleEl, 'valuemax', ctrl._maxValue); - } - - return Widget.extend({ - init: function(settings) { - var self = this; - - if (!settings.previewFilter) { - settings.previewFilter = function(value) { - return Math.round(value * 100) / 100.0; - }; - } - - self._super(settings); - self.classes.add('slider'); - - if (settings.orientation == "v") { - self.classes.add('vertical'); - } - - self._minValue = settings.minValue || 0; - self._maxValue = settings.maxValue || 100; - self._initValue = self.state.get('value'); - }, - - renderHtml: function() { - var self = this, - id = self._id, - prefix = self.classPrefix; - - return ( - '<div id="' + id + '" class="' + self.classes + '">' + - '<div id="' + id + '-handle" class="' + prefix + 'slider-handle" role="slider" tabindex="-1"></div>' + - '</div>' - ); - }, - - reset: function() { - this.value(this._initValue).repaint(); - }, - - postRender: function() { - var self = this, - minValue, maxValue, screenCordName, - stylePosName, sizeName, shortSizeName; - - function toFraction(min, max, val) { - return (val + min) / (max - min); - } - - function fromFraction(min, max, val) { - return (val * (max - min)) - min; - } - - function handleKeyboard(minValue, maxValue) { - function alter(delta) { - var value; - - value = self.value(); - value = fromFraction(minValue, maxValue, toFraction(minValue, maxValue, value) + (delta * 0.05)); - value = constrain(value, minValue, maxValue); - - self.value(value); - - self.fire('dragstart', { - value: value - }); - self.fire('drag', { - value: value - }); - self.fire('dragend', { - value: value - }); - } - - self.on('keydown', function(e) { - switch (e.keyCode) { - case 37: - case 38: - alter(-1); - break; - - case 39: - case 40: - alter(1); - break; - } - }); - } - - function handleDrag(minValue, maxValue, handleEl) { - var startPos, startHandlePos, maxHandlePos, handlePos, value; - - self._dragHelper = new DragHelper(self._id, { - handle: self._id + "-handle", - - start: function(e) { - startPos = e[screenCordName]; - startHandlePos = parseInt(self.getEl('handle').style[stylePosName], 10); - maxHandlePos = (self.layoutRect()[shortSizeName] || 100) - DomUtils.getSize(handleEl)[sizeName]; - self.fire('dragstart', { - value: value - }); - }, - - drag: function(e) { - var delta = e[screenCordName] - startPos; - - handlePos = constrain(startHandlePos + delta, 0, maxHandlePos); - handleEl.style[stylePosName] = handlePos + 'px'; - - value = minValue + (handlePos / maxHandlePos) * (maxValue - minValue); - self.value(value); - - self.tooltip().text('' + self.settings.previewFilter(value)).show().moveRel(handleEl, 'bc tc'); - - self.fire('drag', { - value: value - }); - }, - - stop: function() { - self.tooltip().hide(); - self.fire('dragend', { - value: value - }); - } - }); - } - - minValue = self._minValue; - maxValue = self._maxValue; - - if (self.settings.orientation == "v") { - screenCordName = "screenY"; - stylePosName = "top"; - sizeName = "height"; - shortSizeName = "h"; - } else { - screenCordName = "screenX"; - stylePosName = "left"; - sizeName = "width"; - shortSizeName = "w"; - } - - self._super(); - - handleKeyboard(minValue, maxValue, self.getEl('handle')); - handleDrag(minValue, maxValue, self.getEl('handle')); - }, - - repaint: function() { - this._super(); - updateSliderHandle(this, this.value()); - }, - - bindStates: function() { - var self = this; - - self.state.on('change:value', function(e) { - updateSliderHandle(self, e.value); - }); - - return self._super(); - } - }); - } - ); - /** - * Spacer.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a spacer. This control is used in flex layouts for example. - * - * @-x-less Spacer.less - * @class tinymce.ui.Spacer - * @extends tinymce.ui.Widget - */ - define( - 'tinymce.core.ui.Spacer', [ - "tinymce.core.ui.Widget" - ], - function(Widget) { - "use strict"; - - return Widget.extend({ - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this; - - self.classes.add('spacer'); - self.canFocus = false; - - return '<div id="' + self._id + '" class="' + self.classes + '"></div>'; - } - }); - } - ); - /** - * SplitButton.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a split button. - * - * @-x-less SplitButton.less - * @class tinymce.ui.SplitButton - * @extends tinymce.ui.Button - */ - define( - 'tinymce.core.ui.SplitButton', [ - "tinymce.core.ui.MenuButton", - "tinymce.core.ui.DomUtils", - "tinymce.core.dom.DomQuery" - ], - function(MenuButton, DomUtils, $) { - return MenuButton.extend({ - Defaults: { - classes: "widget btn splitbtn", - role: "button" - }, - - /** - * Repaints the control after a layout operation. - * - * @method repaint - */ - repaint: function() { - var self = this, - elm = self.getEl(), - rect = self.layoutRect(), - mainButtonElm, menuButtonElm; - - self._super(); - - mainButtonElm = elm.firstChild; - menuButtonElm = elm.lastChild; - - $(mainButtonElm).css({ - width: rect.w - DomUtils.getSize(menuButtonElm).width, - height: rect.h - 2 - }); - - $(menuButtonElm).css({ - height: rect.h - 2 - }); - - return self; - }, - - /** - * Sets the active menu state. - * - * @private - */ - activeMenu: function(state) { - var self = this; - - $(self.getEl().lastChild).toggleClass(self.classPrefix + 'active', state); - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - id = self._id, - prefix = self.classPrefix, - image; - var icon = self.state.get('icon'), - text = self.state.get('text'), - textHtml = ''; - - image = self.settings.image; - if (image) { - icon = 'none'; - - // Support for [high dpi, low dpi] image sources - if (typeof image != "string") { - image = window.getSelection ? image[0] : image[1]; - } - - image = ' style="background-image: url(\'' + image + '\')"'; - } else { - image = ''; - } - - icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : ''; - - if (text) { - self.classes.add('btn-has-text'); - textHtml = '<span class="' + prefix + 'txt">' + self.encode(text) + '</span>'; - } - - return ( - '<div id="' + id + '" class="' + self.classes + '" role="button" tabindex="-1">' + - '<button type="button" hidefocus="1" tabindex="-1">' + - (icon ? '<i class="' + icon + '"' + image + '></i>' : '') + - textHtml + - '</button>' + - '<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' + - //(icon ? '<i class="' + icon + '"></i>' : '') + - (self._menuBtnText ? (icon ? '\u00a0' : '') + self._menuBtnText : '') + - ' <i class="' + prefix + 'caret"></i>' + - '</button>' + - '</div>' - ); - }, - - /** - * Called after the control has been rendered. - * - * @method postRender - */ - postRender: function() { - var self = this, - onClickHandler = self.settings.onclick; - - self.on('click', function(e) { - var node = e.target; - - if (e.control == this) { - // Find clicks that is on the main button - while (node) { - if ((e.aria && e.aria.key != 'down') || (node.nodeName == 'BUTTON' && node.className.indexOf('open') == -1)) { - e.stopImmediatePropagation(); - - if (onClickHandler) { - onClickHandler.call(this, e); - } - - return; - } - - node = node.parentNode; - } - } - }); - - delete self.settings.onclick; - - return self._super(); - } - }); - } - ); - /** - * StackLayout.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This layout uses the browsers layout when the items are blocks. - * - * @-x-less StackLayout.less - * @class tinymce.ui.StackLayout - * @extends tinymce.ui.FlowLayout - */ - define( - 'tinymce.core.ui.StackLayout', [ - "tinymce.core.ui.FlowLayout" - ], - function(FlowLayout) { - "use strict"; - - return FlowLayout.extend({ - Defaults: { - containerClass: 'stack-layout', - controlClass: 'stack-layout-item', - endClass: 'break' - }, - - isNative: function() { - return true; - } - }); - } - ); - /** - * TabPanel.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a tab panel control. - * - * @-x-less TabPanel.less - * @class tinymce.ui.TabPanel - * @extends tinymce.ui.Panel - * - * @setting {Number} activeTab Active tab index. - */ - define( - 'tinymce.core.ui.TabPanel', [ - "tinymce.core.ui.Panel", - "tinymce.core.dom.DomQuery", - "tinymce.core.ui.DomUtils" - ], - function(Panel, $, DomUtils) { - "use strict"; - - return Panel.extend({ - Defaults: { - layout: 'absolute', - defaults: { - type: 'panel' - } - }, - - /** - * Activates the specified tab by index. - * - * @method activateTab - * @param {Number} idx Index of the tab to activate. - */ - activateTab: function(idx) { - var activeTabElm; - - if (this.activeTabId) { - activeTabElm = this.getEl(this.activeTabId); - $(activeTabElm).removeClass(this.classPrefix + 'active'); - activeTabElm.setAttribute('aria-selected', "false"); - } - - this.activeTabId = 't' + idx; - - activeTabElm = this.getEl('t' + idx); - activeTabElm.setAttribute('aria-selected', "true"); - $(activeTabElm).addClass(this.classPrefix + 'active'); - - this.items()[idx].show().fire('showtab'); - this.reflow(); - - this.items().each(function(item, i) { - if (idx != i) { - item.hide(); - } - }); - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - layout = self._layout, - tabsHtml = '', - prefix = self.classPrefix; - - self.preRender(); - layout.preRender(self); - - self.items().each(function(ctrl, i) { - var id = self._id + '-t' + i; - - ctrl.aria('role', 'tabpanel'); - ctrl.aria('labelledby', id); - - tabsHtml += ( - '<div id="' + id + '" class="' + prefix + 'tab" ' + - 'unselectable="on" role="tab" aria-controls="' + ctrl._id + '" aria-selected="false" tabIndex="-1">' + - self.encode(ctrl.settings.title) + - '</div>' - ); - }); - - return ( - '<div id="' + self._id + '" class="' + self.classes + '" hidefocus="1" tabindex="-1">' + - '<div id="' + self._id + '-head" class="' + prefix + 'tabs" role="tablist">' + - tabsHtml + - '</div>' + - '<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' + - layout.renderHtml(self) + - '</div>' + - '</div>' - ); - }, - - /** - * Called after the control has been rendered. - * - * @method postRender - */ - postRender: function() { - var self = this; - - self._super(); - - self.settings.activeTab = self.settings.activeTab || 0; - self.activateTab(self.settings.activeTab); - - this.on('click', function(e) { - var targetParent = e.target.parentNode; - - if (targetParent && targetParent.id == self._id + '-head') { - var i = targetParent.childNodes.length; - - while (i--) { - if (targetParent.childNodes[i] == e.target) { - self.activateTab(i); - } - } - } - }); - }, - - /** - * Initializes the current controls layout rect. - * This will be executed by the layout managers to determine the - * default minWidth/minHeight etc. - * - * @method initLayoutRect - * @return {Object} Layout rect instance. - */ - initLayoutRect: function() { - var self = this, - rect, minW, minH; - - minW = DomUtils.getSize(self.getEl('head')).width; - minW = minW < 0 ? 0 : minW; - minH = 0; - - self.items().each(function(item) { - minW = Math.max(minW, item.layoutRect().minW); - minH = Math.max(minH, item.layoutRect().minH); - }); - - self.items().each(function(ctrl) { - ctrl.settings.x = 0; - ctrl.settings.y = 0; - ctrl.settings.w = minW; - ctrl.settings.h = minH; - - ctrl.layoutRect({ - x: 0, - y: 0, - w: minW, - h: minH - }); - }); - - var headH = DomUtils.getSize(self.getEl('head')).height; - - self.settings.minWidth = minW; - self.settings.minHeight = minH + headH; - - rect = self._super(); - rect.deltaH += headH; - rect.innerH = rect.h - rect.deltaH; - - return rect; - } - }); - } - ); - - /** - * TextBox.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * Creates a new textbox. - * - * @-x-less TextBox.less - * @class tinymce.ui.TextBox - * @extends tinymce.ui.Widget - */ - define( - 'tinymce.core.ui.TextBox', [ - "tinymce.core.ui.Widget", - "tinymce.core.util.Tools", - "tinymce.core.ui.DomUtils" - ], - function(Widget, Tools, DomUtils) { - return Widget.extend({ - /** - * Constructs a instance with the specified settings. - * - * @constructor - * @param {Object} settings Name/value object with settings. - * @setting {Boolean} multiline True if the textbox is a multiline control. - * @setting {Number} maxLength Max length for the textbox. - * @setting {Number} size Size of the textbox in characters. - */ - init: function(settings) { - var self = this; - - self._super(settings); - - self.classes.add('textbox'); - - if (settings.multiline) { - self.classes.add('multiline'); - } else { - self.on('keydown', function(e) { - var rootControl; - - if (e.keyCode == 13) { - e.preventDefault(); - - // Find root control that we can do toJSON on - self.parents().reverse().each(function(ctrl) { - if (ctrl.toJSON) { - rootControl = ctrl; - return false; - } - }); - - // Fire event on current text box with the serialized data of the whole form - self.fire('submit', { - data: rootControl.toJSON() - }); - } - }); - - self.on('keyup', function(e) { - self.state.set('value', e.target.value); - }); - } - }, - - /** - * Repaints the control after a layout operation. - * - * @method repaint - */ - repaint: function() { - var self = this, - style, rect, borderBox, borderW, borderH = 0, - lastRepaintRect; - - style = self.getEl().style; - rect = self._layoutRect; - lastRepaintRect = self._lastRepaintRect || {}; - - // Detect old IE 7+8 add lineHeight to align caret vertically in the middle - var doc = document; - if (!self.settings.multiline && doc.all && (!doc.documentMode || doc.documentMode <= 8)) { - style.lineHeight = (rect.h - borderH) + 'px'; - } - - borderBox = self.borderBox; - borderW = borderBox.left + borderBox.right + 8; - borderH = borderBox.top + borderBox.bottom + (self.settings.multiline ? 8 : 0); - - if (rect.x !== lastRepaintRect.x) { - style.left = rect.x + 'px'; - lastRepaintRect.x = rect.x; - } - - if (rect.y !== lastRepaintRect.y) { - style.top = rect.y + 'px'; - lastRepaintRect.y = rect.y; - } - - if (rect.w !== lastRepaintRect.w) { - style.width = (rect.w - borderW) + 'px'; - lastRepaintRect.w = rect.w; - } - - if (rect.h !== lastRepaintRect.h) { - style.height = (rect.h - borderH) + 'px'; - lastRepaintRect.h = rect.h; - } - - self._lastRepaintRect = lastRepaintRect; - self.fire('repaint', {}, false); - - return self; - }, - - /** - * Renders the control as a HTML string. - * - * @method renderHtml - * @return {String} HTML representing the control. - */ - renderHtml: function() { - var self = this, - settings = self.settings, - attrs, elm; - - attrs = { - id: self._id, - hidefocus: '1' - }; - - Tools.each([ - 'rows', 'spellcheck', 'maxLength', 'size', 'readonly', 'min', - 'max', 'step', 'list', 'pattern', 'placeholder', 'required', 'multiple' - ], function(name) { - attrs[name] = settings[name]; - }); - - if (self.disabled()) { - attrs.disabled = 'disabled'; - } - - if (settings.subtype) { - attrs.type = settings.subtype; - } - - elm = DomUtils.create(settings.multiline ? 'textarea' : 'input', attrs); - elm.value = self.state.get('value'); - elm.className = self.classes; - - return elm.outerHTML; - }, - - value: function(value) { - if (arguments.length) { - this.state.set('value', value); - return this; - } - - // Make sure the real state is in sync - if (this.state.get('rendered')) { - this.state.set('value', this.getEl().value); - } - - return this.state.get('value'); - }, - - /** - * Called after the control has been rendered. - * - * @method postRender - */ - postRender: function() { - var self = this; - - self.getEl().value = self.state.get('value'); - self._super(); - - self.$el.on('change', function(e) { - self.state.set('value', e.target.value); - self.fire('change', e); - }); - }, - - bindStates: function() { - var self = this; - - self.state.on('change:value', function(e) { - if (self.getEl().value != e.value) { - self.getEl().value = e.value; - } - }); - - self.state.on('change:disabled', function(e) { - self.getEl().disabled = e.value; - }); - - return self._super(); - }, - - remove: function() { - this.$el.off(); - this._super(); - } - }); - } - ); - - /** - * Api.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.ui.Api', [ - 'tinymce.core.ui.Selector', - 'tinymce.core.ui.Collection', - 'tinymce.core.ui.ReflowQueue', - 'tinymce.core.ui.Control', - 'tinymce.core.ui.Factory', - 'tinymce.core.ui.KeyboardNavigation', - 'tinymce.core.ui.Container', - 'tinymce.core.ui.DragHelper', - 'tinymce.core.ui.Scrollable', - 'tinymce.core.ui.Panel', - 'tinymce.core.ui.Movable', - 'tinymce.core.ui.Resizable', - 'tinymce.core.ui.FloatPanel', - 'tinymce.core.ui.Window', - 'tinymce.core.ui.MessageBox', - 'tinymce.core.ui.Tooltip', - 'tinymce.core.ui.Widget', - 'tinymce.core.ui.Progress', - 'tinymce.core.ui.Notification', - 'tinymce.core.ui.Layout', - 'tinymce.core.ui.AbsoluteLayout', - 'tinymce.core.ui.Button', - 'tinymce.core.ui.ButtonGroup', - 'tinymce.core.ui.Checkbox', - 'tinymce.core.ui.ComboBox', - 'tinymce.core.ui.ColorBox', - 'tinymce.core.ui.PanelButton', - 'tinymce.core.ui.ColorButton', - 'tinymce.core.ui.ColorPicker', - 'tinymce.core.ui.Path', - 'tinymce.core.ui.ElementPath', - 'tinymce.core.ui.FormItem', - 'tinymce.core.ui.Form', - 'tinymce.core.ui.FieldSet', - 'tinymce.core.ui.FilePicker', - 'tinymce.core.ui.FitLayout', - 'tinymce.core.ui.FlexLayout', - 'tinymce.core.ui.FlowLayout', - 'tinymce.core.ui.FormatControls', - 'tinymce.core.ui.GridLayout', - 'tinymce.core.ui.Iframe', - 'tinymce.core.ui.InfoBox', - 'tinymce.core.ui.Label', - 'tinymce.core.ui.Toolbar', - 'tinymce.core.ui.MenuBar', - 'tinymce.core.ui.MenuButton', - 'tinymce.core.ui.MenuItem', - 'tinymce.core.ui.Throbber', - 'tinymce.core.ui.Menu', - 'tinymce.core.ui.ListBox', - 'tinymce.core.ui.Radio', - 'tinymce.core.ui.ResizeHandle', - 'tinymce.core.ui.SelectBox', - 'tinymce.core.ui.Slider', - 'tinymce.core.ui.Spacer', - 'tinymce.core.ui.SplitButton', - 'tinymce.core.ui.StackLayout', - 'tinymce.core.ui.TabPanel', - 'tinymce.core.ui.TextBox' - ], - function( - Selector, Collection, ReflowQueue, Control, Factory, KeyboardNavigation, Container, DragHelper, Scrollable, Panel, Movable, - Resizable, FloatPanel, Window, MessageBox, Tooltip, Widget, Progress, Notification, Layout, AbsoluteLayout, Button, - ButtonGroup, Checkbox, ComboBox, ColorBox, PanelButton, ColorButton, ColorPicker, Path, ElementPath, FormItem, Form, - FieldSet, FilePicker, FitLayout, FlexLayout, FlowLayout, FormatControls, GridLayout, Iframe, InfoBox, Label, Toolbar, - MenuBar, MenuButton, MenuItem, Throbber, Menu, ListBox, Radio, ResizeHandle, SelectBox, Slider, Spacer, SplitButton, - StackLayout, TabPanel, TextBox - ) { - "use strict"; - - var registerToFactory = function(id, ref) { - Factory.add(id.split('.').pop(), ref); - }; - - var expose = function(target, id, ref) { - var i, fragments; - - fragments = id.split(/[.\/]/); - for (i = 0; i < fragments.length - 1; ++i) { - if (target[fragments[i]] === undefined) { - target[fragments[i]] = {}; - } - - target = target[fragments[i]]; - } - - target[fragments[fragments.length - 1]] = ref; - - registerToFactory(id, ref); - }; - - var appendTo = function(target) { - expose(target, 'ui.Selector', Selector); - expose(target, 'ui.Collection', Collection); - expose(target, 'ui.ReflowQueue', ReflowQueue); - expose(target, 'ui.Control', Control); - expose(target, 'ui.Factory', Factory); - expose(target, 'ui.KeyboardNavigation', KeyboardNavigation); - expose(target, 'ui.Container', Container); - expose(target, 'ui.DragHelper', DragHelper); - expose(target, 'ui.Scrollable', Scrollable); - expose(target, 'ui.Panel', Panel); - expose(target, 'ui.Movable', Movable); - expose(target, 'ui.Resizable', Resizable); - expose(target, 'ui.FloatPanel', FloatPanel); - expose(target, 'ui.Window', Window); - expose(target, 'ui.MessageBox', MessageBox); - expose(target, 'ui.Tooltip', Tooltip); - expose(target, 'ui.Widget', Widget); - expose(target, 'ui.Progress', Progress); - expose(target, 'ui.Notification', Notification); - expose(target, 'ui.Layout', Layout); - expose(target, 'ui.AbsoluteLayout', AbsoluteLayout); - expose(target, 'ui.Button', Button); - expose(target, 'ui.ButtonGroup', ButtonGroup); - expose(target, 'ui.Checkbox', Checkbox); - expose(target, 'ui.ComboBox', ComboBox); - expose(target, 'ui.ColorBox', ColorBox); - expose(target, 'ui.PanelButton', PanelButton); - expose(target, 'ui.ColorButton', ColorButton); - expose(target, 'ui.ColorPicker', ColorPicker); - expose(target, 'ui.Path', Path); - expose(target, 'ui.ElementPath', ElementPath); - expose(target, 'ui.FormItem', FormItem); - expose(target, 'ui.Form', Form); - expose(target, 'ui.FieldSet', FieldSet); - expose(target, 'ui.FilePicker', FilePicker); - expose(target, 'ui.FitLayout', FitLayout); - expose(target, 'ui.FlexLayout', FlexLayout); - expose(target, 'ui.FlowLayout', FlowLayout); - expose(target, 'ui.FormatControls', FormatControls); - expose(target, 'ui.GridLayout', GridLayout); - expose(target, 'ui.Iframe', Iframe); - expose(target, 'ui.InfoBox', InfoBox); - expose(target, 'ui.Label', Label); - expose(target, 'ui.Toolbar', Toolbar); - expose(target, 'ui.MenuBar', MenuBar); - expose(target, 'ui.MenuButton', MenuButton); - expose(target, 'ui.MenuItem', MenuItem); - expose(target, 'ui.Throbber', Throbber); - expose(target, 'ui.Menu', Menu); - expose(target, 'ui.ListBox', ListBox); - expose(target, 'ui.Radio', Radio); - expose(target, 'ui.ResizeHandle', ResizeHandle); - expose(target, 'ui.SelectBox', SelectBox); - expose(target, 'ui.Slider', Slider); - expose(target, 'ui.Spacer', Spacer); - expose(target, 'ui.SplitButton', SplitButton); - expose(target, 'ui.StackLayout', StackLayout); - expose(target, 'ui.TabPanel', TabPanel); - expose(target, 'ui.TextBox', TextBox); - expose(target, 'ui.Api', Api); - }; - - var Api = { - appendTo: appendTo - }; - - return Api; - } - ); - /** - * Tinymce.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.api.Tinymce', [ - 'tinymce.core.geom.Rect', - 'tinymce.core.util.Promise', - 'tinymce.core.util.Delay', - 'tinymce.core.Env', - 'tinymce.core.dom.EventUtils', - 'tinymce.core.dom.Sizzle', - 'tinymce.core.util.Tools', - 'tinymce.core.dom.DomQuery', - 'tinymce.core.html.Styles', - 'tinymce.core.dom.TreeWalker', - 'tinymce.core.html.Entities', - 'tinymce.core.dom.DOMUtils', - 'tinymce.core.dom.ScriptLoader', - 'tinymce.core.AddOnManager', - 'tinymce.core.dom.RangeUtils', - 'tinymce.core.html.Node', - 'tinymce.core.html.Schema', - 'tinymce.core.html.SaxParser', - 'tinymce.core.html.DomParser', - 'tinymce.core.html.Writer', - 'tinymce.core.html.Serializer', - 'tinymce.core.dom.Serializer', - 'tinymce.core.util.VK', - 'tinymce.core.dom.ControlSelection', - 'tinymce.core.dom.BookmarkManager', - 'tinymce.core.dom.Selection', - 'tinymce.core.Formatter', - 'tinymce.core.UndoManager', - 'tinymce.core.EditorCommands', - 'tinymce.core.util.URI', - 'tinymce.core.util.Class', - 'tinymce.core.util.EventDispatcher', - 'tinymce.core.util.Observable', - 'tinymce.core.WindowManager', - 'tinymce.core.NotificationManager', - 'tinymce.core.EditorObservable', - 'tinymce.core.Shortcuts', - 'tinymce.core.Editor', - 'tinymce.core.util.I18n', - 'tinymce.core.FocusManager', - 'tinymce.core.EditorManager', - 'tinymce.core.util.XHR', - 'tinymce.core.util.JSON', - 'tinymce.core.util.JSONRequest', - 'tinymce.core.util.JSONP', - 'tinymce.core.util.LocalStorage', - 'tinymce.core.api.Compat', - 'tinymce.core.util.Color', - 'tinymce.core.ui.Api' - ], - function( - Rect, Promise, Delay, Env, EventUtils, Sizzle, Tools, DomQuery, Styles, TreeWalker, Entities, DOMUtils, ScriptLoader, AddOnManager, - RangeUtils, Node, Schema, SaxParser, DomParser, Writer, HtmlSerializer, DomSerializer, VK, ControlSelection, BookmarkManager, Selection, - Formatter, UndoManager, EditorCommands, URI, Class, EventDispatcher, Observable, WindowManager, - NotificationManager, EditorObservable, Shortcuts, Editor, I18n, FocusManager, EditorManager, - XHR, JSON, JSONRequest, JSONP, LocalStorage, Compat, Color, Api - ) { - var tinymce = EditorManager; - - var expose = function(target, id, ref) { - var i, fragments; - - fragments = id.split(/[.\/]/); - for (i = 0; i < fragments.length - 1; ++i) { - if (target[fragments[i]] === undefined) { - target[fragments[i]] = {}; - } - - target = target[fragments[i]]; - } - - target[fragments[fragments.length - 1]] = ref; - }; - - expose(tinymce, 'geom.Rect', Rect); - expose(tinymce, 'util.Promise', Promise); - expose(tinymce, 'util.Delay', Delay); - expose(tinymce, 'Env', Env); - expose(tinymce, 'dom.EventUtils', EventUtils); - expose(tinymce, 'dom.Sizzle', Sizzle); - expose(tinymce, 'util.Tools', Tools); - expose(tinymce, 'dom.DomQuery', DomQuery); - expose(tinymce, 'html.Styles', Styles); - expose(tinymce, 'dom.TreeWalker', TreeWalker); - expose(tinymce, 'html.Entities', Entities); - expose(tinymce, 'dom.DOMUtils', DOMUtils); - expose(tinymce, 'dom.ScriptLoader', ScriptLoader); - expose(tinymce, 'AddOnManager', AddOnManager); - expose(tinymce, 'dom.RangeUtils', RangeUtils); - expose(tinymce, 'html.Node', Node); - expose(tinymce, 'html.Schema', Schema); - expose(tinymce, 'html.SaxParser', SaxParser); - expose(tinymce, 'html.DomParser', DomParser); - expose(tinymce, 'html.Writer', Writer); - expose(tinymce, 'html.Serializer', HtmlSerializer); - expose(tinymce, 'dom.Serializer', DomSerializer); - expose(tinymce, 'util.VK', VK); - expose(tinymce, 'dom.ControlSelection', ControlSelection); - expose(tinymce, 'dom.BookmarkManager', BookmarkManager); - expose(tinymce, 'dom.Selection', Selection); - expose(tinymce, 'Formatter', Formatter); - expose(tinymce, 'UndoManager', UndoManager); - expose(tinymce, 'EditorCommands', EditorCommands); - expose(tinymce, 'util.URI', URI); - expose(tinymce, 'util.Class', Class); - expose(tinymce, 'util.EventDispatcher', EventDispatcher); - expose(tinymce, 'util.Observable', Observable); - expose(tinymce, 'WindowManager', WindowManager); - expose(tinymce, 'NotificationManager', NotificationManager); - expose(tinymce, 'EditorObservable', EditorObservable); - expose(tinymce, 'Shortcuts', Shortcuts); - expose(tinymce, 'Editor', Editor); - expose(tinymce, 'util.I18n', I18n); - expose(tinymce, 'FocusManager', FocusManager); - expose(tinymce, 'EditorManager', EditorManager); - expose(tinymce, 'util.XHR', XHR); - expose(tinymce, 'util.JSON', JSON); - expose(tinymce, 'util.JSONRequest', JSONRequest); - expose(tinymce, 'util.JSONP', JSONP); - expose(tinymce, 'util.LocalStorage', LocalStorage); - expose(tinymce, 'Compat', Compat); - expose(tinymce, 'util.Color', Color); - - Api.appendTo(tinymce); - - Compat.register(tinymce); - - return tinymce; - } - ); - - /** - * Register.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - /** - * This registers tinymce in common module loaders. - * - * @private - * @class tinymce.Register - */ - define( - 'tinymce.core.Register', [], - function() { - /*eslint consistent-this: 0 */ - var context = this || window; - - var exposeToModuleLoaders = function(tinymce) { - if (typeof context.define === "function") { - // Bolt - if (!context.define.amd) { - context.define("ephox/tinymce", [], function() { - return tinymce; - }); - - context.define("tinymce.core.EditorManager", [], function() { - return tinymce; - }); - } - } - - if (typeof module === 'object') { - /* global module */ - module.exports = tinymce; - } - }; - - return { - exposeToModuleLoaders: exposeToModuleLoaders - }; - } - ); - - /** - * Main.js - * - * Released under LGPL License. - * Copyright (c) 1999-2017 Ephox Corp. All rights reserved - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - - define( - 'tinymce.core.api.Main', [ - 'tinymce.core.api.Tinymce', - 'tinymce.core.Register' - ], - function(tinymce, Register) { - return function() { - window.tinymce = tinymce; - window.tinyMCE = tinymce; - Register.exposeToModuleLoaders(tinymce); - return tinymce; - }; - } - ); - - dem('tinymce.core.api.Main')(); -})(); \ No newline at end of file +!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;i<g;++i)h[i]=d(e[i]);var j=f.apply(null,h);if(void 0===j)throw"module ["+b+"] returned undefined";c.instance=j},c=function(b,c,d){if("string"!=typeof b)throw"module id must be a string";if(void 0===c)throw"no dependencies for "+b;if(void 0===d)throw"no definition function for "+b;a[b]={deps:c,defn:d,instance:void 0}},d=function(c){var d=a[c];if(void 0===d)throw"module ["+c+"] was undefined";return void 0===d.instance&&b(c),d.instance},e=function(a,b){for(var c=a.length,e=new Array(c),f=0;f<c;++f)e.push(d(a[f]));b.apply(null,b)},f={};f.bolt={module:{api:{define:c,require:e,demand:d}}};var g=c,h=function(a,b){g(a,[],function(){return b})};g("3",[],function(){"use strict";function a(a,b,c){var d,e,g,h,i,k;return d=b.x,e=b.y,g=a.w,h=a.h,i=b.w,k=b.h,c=(c||"").split(""),"b"===c[0]&&(e+=k),"r"===c[1]&&(d+=i),"c"===c[0]&&(e+=j(k/2)),"c"===c[1]&&(d+=j(i/2)),"b"===c[3]&&(e-=h),"r"===c[4]&&(d-=g),"c"===c[3]&&(e-=j(h/2)),"c"===c[4]&&(d-=j(g/2)),f(d,e,g,h)}function b(b,c,d,e){var f,g;for(g=0;g<e.length;g++)if(f=a(b,c,e[g]),f.x>=d.x&&f.x+f.w<=d.w+d.x&&f.y>=d.y&&f.y+f.h<=d.h+d.y)return e[g];return null}function c(a,b,c){return f(a.x-b,a.y-c,a.w+2*b,a.h+2*c)}function d(a,b){var c,d,e,g;return c=i(a.x,b.x),d=i(a.y,b.y),e=h(a.x+a.w,b.x+b.w),g=h(a.y+a.h,b.y+b.h),e-c<0||g-d<0?null:f(c,d,e-c,g-d)}function e(a,b,c){var d,e,g,h,j,k,l,m,n,o;return j=a.x,k=a.y,l=a.x+a.w,m=a.y+a.h,n=b.x+b.w,o=b.y+b.h,d=i(0,b.x-j),e=i(0,b.y-k),g=i(0,l-n),h=i(0,m-o),j+=d,k+=e,c&&(l+=d,m+=e,j-=g,k-=h),l-=g,m-=h,f(j,k,l-j,m-k)}function f(a,b,c,d){return{x:a,y:b,w:c,h:d}}function g(a){return f(a.left,a.top,a.width,a.height)}var h=Math.min,i=Math.max,j=Math.round;return{inflate:c,relativePosition:a,findBestRelativePosition:b,intersect:d,clamp:e,create:f,fromClientRect:g}}),g("4",[],function(){function a(a,b){return function(){a.apply(b,arguments)}}function b(b){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof b)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],h(b,a(d,this),a(e,this))}function c(a){var b=this;return null===this._state?void this._deferreds.push(a):void i(function(){var c=b._state?a.onFulfilled:a.onRejected;if(null===c)return void(b._state?a.resolve:a.reject)(b._value);var d;try{d=c(b._value)}catch(b){return void a.reject(b)}a.resolve(d)})}function d(b){try{if(b===this)throw new TypeError("A promise cannot be resolved with itself.");if(b&&("object"==typeof b||"function"==typeof b)){var c=b.then;if("function"==typeof c)return void h(a(c,b),a(d,this),a(e,this))}this._state=!0,this._value=b,f.call(this)}catch(a){e.call(this,a)}}function e(a){this._state=!1,this._value=a,f.call(this)}function f(){for(var a=0,b=this._deferreds.length;a<b;a++)c.call(this,this._deferreds[a]);this._deferreds=null}function g(a,b,c,d){this.onFulfilled="function"==typeof a?a:null,this.onRejected="function"==typeof b?b:null,this.resolve=c,this.reject=d}function h(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(a){if(d)return;d=!0,c(a)}}if(window.Promise)return window.Promise;var i=b.immediateFn||"function"==typeof setImmediate&&setImmediate||function(a){setTimeout(a,1)},j=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};return b.prototype["catch"]=function(a){return this.then(null,a)},b.prototype.then=function(a,d){var e=this;return new b(function(b,f){c.call(e,new g(a,d,b,f))})},b.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&j(arguments[0])?arguments[0]:arguments);return new b(function(b,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,a)},c)}a[f]=g,0===--e&&b(a)}catch(a){c(a)}}if(0===a.length)return b([]);for(var e=a.length,f=0;f<a.length;f++)d(f,a[f])})},b.resolve=function(a){return a&&"object"==typeof a&&a.constructor===b?a:new b(function(b){b(a)})},b.reject=function(a){return new b(function(b,c){c(a)})},b.race=function(a){return new b(function(b,c){for(var d=0,e=a.length;d<e;d++)a[d].then(b,c)})},b}),g("5",["4"],function(a){function b(a,b){function c(a){window.setTimeout(a,0)}var d,e=window.requestAnimationFrame,f=["ms","moz","webkit"];for(d=0;d<f.length&&!e;d++)e=window[f[d]+"RequestAnimationFrame"];e||(e=c),e(a,b)}function c(a,b){return"number"!=typeof b&&(b=0),setTimeout(a,b)}function d(a,b){return"number"!=typeof b&&(b=1),setInterval(a,b)}function e(a){return clearTimeout(a)}function f(a){return clearInterval(a)}function g(a,b){var d,e;return e=function(){var e=arguments;clearTimeout(d),d=c(function(){a.apply(this,e)},b)},e.stop=function(){clearTimeout(d)},e}var h;return{requestAnimationFrame:function(c,d){return h?void h.then(c):void(h=new a(function(a){d||(d=document.body),b(a,d)}).then(c))},setTimeout:c,setInterval:d,setEditorTimeout:function(a,b,d){return c(function(){a.removed||b()},d)},setEditorInterval:function(a,b,c){var e;return e=d(function(){a.removed?clearInterval(e):b()},c)},debounce:g,throttle:g,clearInterval:f,clearTimeout:e}}),g("6",[],function(){function a(a){return"matchMedia"in window&&matchMedia(a).matches}var b,c,d,e,f,g,h,i,j,k,l,m,n,o=navigator,p=o.userAgent;b=window.opera&&window.opera.buildNumber,j=/Android/.test(p),c=/WebKit/.test(p),d=!c&&!b&&/MSIE/gi.test(p)&&/Explorer/gi.test(o.appName),d=d&&/MSIE (\w+)\./.exec(p)[1],e=p.indexOf("Trident/")!=-1&&(p.indexOf("rv:")!=-1||o.appName.indexOf("Netscape")!=-1)&&11,f=p.indexOf("Edge/")!=-1&&!d&&!e&&12,d=d||e||f,g=!c&&!e&&/Gecko/.test(p),h=p.indexOf("Mac")!=-1,i=/(iPad|iPhone)/.test(p),k="FormData"in window&&"FileReader"in window&&"URL"in window&&!!URL.createObjectURL,l=a("only screen and (max-device-width: 480px)")&&(j||i),m=a("only screen and (min-width: 800px)")&&(j||i),n=p.indexOf("Windows Phone")!=-1,f&&(c=!1);var q=!i||k||p.match(/AppleWebKit\/(\d*)/)[1]>=534;return{opera:b,webkit:c,ie:d,gecko:g,mac:h,iOS:i,android:j,contentEditable:q,transparentSrc:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",caretAfter:8!=d,range:window.getSelection&&"Range"in window,documentMode:d&&!f?document.documentMode||7:10,fileApi:k,ceFalse:d===!1||d>8,canHaveCSP:d===!1||d>11,desktop:!l&&!m,windowsPhone:n}}),g("7",["5","6"],function(a,b){"use strict";function c(a,b,c,d){a.addEventListener?a.addEventListener(b,c,d||!1):a.attachEvent&&a.attachEvent("on"+b,c)}function d(a,b,c,d){a.removeEventListener?a.removeEventListener(b,c,d||!1):a.detachEvent&&a.detachEvent("on"+b,c)}function e(a,b){var c,d=b;return c=a.path,c&&c.length>0&&(d=c[0]),a.deepPath&&(c=a.deepPath(),c&&c.length>0&&(d=c[0])),d}function f(a,c){var d,f,g=c||{};for(d in a)k[d]||(g[d]=a[d]);if(g.target||(g.target=g.srcElement||document),b.experimentalShadowDom&&(g.target=e(a,g.target)),a&&j.test(a.type)&&a.pageX===f&&a.clientX!==f){var h=g.target.ownerDocument||document,i=h.documentElement,o=h.body;g.pageX=a.clientX+(i&&i.scrollLeft||o&&o.scrollLeft||0)-(i&&i.clientLeft||o&&o.clientLeft||0),g.pageY=a.clientY+(i&&i.scrollTop||o&&o.scrollTop||0)-(i&&i.clientTop||o&&o.clientTop||0)}return g.preventDefault=function(){g.isDefaultPrevented=n,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},g.stopPropagation=function(){g.isPropagationStopped=n,a&&(a.stopPropagation?a.stopPropagation():a.cancelBubble=!0)},g.stopImmediatePropagation=function(){g.isImmediatePropagationStopped=n,g.stopPropagation()},l(g)===!1&&(g.isDefaultPrevented=m,g.isPropagationStopped=m,g.isImmediatePropagationStopped=m),"undefined"==typeof g.metaKey&&(g.metaKey=!1),g}function g(e,f,g){function h(){return"complete"===l.readyState||"interactive"===l.readyState&&l.body}function i(){g.domLoaded||(g.domLoaded=!0,f(m))}function j(){h()&&(d(l,"readystatechange",j),i())}function k(){try{l.documentElement.doScroll("left")}catch(b){return void a.setTimeout(k)}i()}var l=e.document,m={type:"ready"};return g.domLoaded?void f(m):(!l.addEventListener||b.ie&&b.ie<11?(c(l,"readystatechange",j),l.documentElement.doScroll&&e.self===e.top&&k()):h()?i():c(e,"DOMContentLoaded",i),void c(e,"load",i))}function h(){function a(a,b){var c,d,e,f,g=m[b];if(c=g&&g[a.type])for(d=0,e=c.length;d<e;d++)if(f=c[d],f&&f.func.call(f.scope,a)===!1&&a.preventDefault(),a.isImmediatePropagationStopped())return}var b,e,h,j,k,l=this,m={};e=i+(+new Date).toString(32),j="onmouseenter"in document.documentElement,h="onfocusin"in document.documentElement,k={mouseenter:"mouseover",mouseleave:"mouseout"},b=1,l.domLoaded=!1,l.events=m,l.bind=function(d,i,n,o){function p(b){a(f(b||x.event),q)}var q,r,s,t,u,v,w,x=window;if(d&&3!==d.nodeType&&8!==d.nodeType){for(d[e]?q=d[e]:(q=b++,d[e]=q,m[q]={}),o=o||d,i=i.split(" "),s=i.length;s--;)t=i[s],v=p,u=w=!1,"DOMContentLoaded"===t&&(t="ready"),l.domLoaded&&"ready"===t&&"complete"==d.readyState?n.call(o,f({type:t})):(j||(u=k[t],u&&(v=function(b){var c,d;if(c=b.currentTarget,d=b.relatedTarget,d&&c.contains)d=c.contains(d);else for(;d&&d!==c;)d=d.parentNode;d||(b=f(b||x.event),b.type="mouseout"===b.type?"mouseleave":"mouseenter",b.target=c,a(b,q))})),h||"focusin"!==t&&"focusout"!==t||(w=!0,u="focusin"===t?"focus":"blur",v=function(b){b=f(b||x.event),b.type="focus"===b.type?"focusin":"focusout",a(b,q)}),r=m[q][t],r?"ready"===t&&l.domLoaded?n({type:t}):r.push({func:n,scope:o}):(m[q][t]=r=[{func:n,scope:o}],r.fakeName=u,r.capture=w,r.nativeHandler=v,"ready"===t?g(d,v,l):c(d,u||t,v,w)));return d=r=0,n}},l.unbind=function(a,b,c){var f,g,h,i,j,k;if(!a||3===a.nodeType||8===a.nodeType)return l;if(f=a[e]){if(k=m[f],b){for(b=b.split(" "),h=b.length;h--;)if(j=b[h],g=k[j]){if(c)for(i=g.length;i--;)if(g[i].func===c){var n=g.nativeHandler,o=g.fakeName,p=g.capture;g=g.slice(0,i).concat(g.slice(i+1)),g.nativeHandler=n,g.fakeName=o,g.capture=p,k[j]=g}c&&0!==g.length||(delete k[j],d(a,g.fakeName||j,g.nativeHandler,g.capture))}}else{for(j in k)g=k[j],d(a,g.fakeName||j,g.nativeHandler,g.capture);k={}}for(j in k)return l;delete m[f];try{delete a[e]}catch(b){a[e]=null}}return l},l.fire=function(b,c,d){var g;if(!b||3===b.nodeType||8===b.nodeType)return l;d=f(null,d),d.type=c,d.target=b;do g=b[e],g&&a(d,g),b=b.parentNode||b.ownerDocument||b.defaultView||b.parentWindow;while(b&&!d.isPropagationStopped());return l},l.clean=function(a){var b,c,d=l.unbind;if(!a||3===a.nodeType||8===a.nodeType)return l;if(a[e]&&d(a),a.getElementsByTagName||(a=a.document),a&&a.getElementsByTagName)for(d(a),c=a.getElementsByTagName("*"),b=c.length;b--;)a=c[b],a[e]&&d(a);return l},l.destroy=function(){m={}},l.cancel=function(a){return a&&(a.preventDefault(),a.stopImmediatePropagation()),!1}}var i="mce-data-",j=/^(?:mouse|contextmenu)|click/,k={keyLocation:1,layerX:1,layerY:1,returnValue:1,webkitMovementX:1,webkitMovementY:1,keyIdentifier:1},l=function(a){return a.isDefaultPrevented===n||a.isDefaultPrevented===m},m=function(){return!1},n=function(){return!0};return h.Event=new h,h.Event.bind(window,"ready",function(){}),h}),g("8",[],function(){function a(a,b,c,d){var e,f,g,h,i,k,m,n,o,p;if((b?b.ownerDocument||b:N)!==F&&E(b),b=b||F,c=c||[],!a||"string"!=typeof a)return c;if(1!==(h=b.nodeType)&&9!==h)return[];if(H&&!d){if(e=qa.exec(a))if(g=e[1]){if(9===h){if(f=b.getElementById(g),!f||!f.parentNode)return c;if(f.id===g)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(g))&&L(b,f)&&f.id===g)return c.push(f),c}else{if(e[2])return $.apply(c,b.getElementsByTagName(a)),c;if((g=e[3])&&u.getElementsByClassName)return $.apply(c,b.getElementsByClassName(g)),c}if(u.qsa&&(!I||!I.test(a))){if(n=m=M,o=b,p=9===h&&a,1===h&&"object"!==b.nodeName.toLowerCase()){for(k=y(a),(m=b.getAttribute("id"))?n=m.replace(sa,"\\$&"):b.setAttribute("id",n),n="[id='"+n+"'] ",i=k.length;i--;)k[i]=n+l(k[i]);o=ra.test(a)&&j(b.parentNode)||b,p=k.join(",")}if(p)try{return $.apply(c,o.querySelectorAll(p)),c}catch(a){}finally{m||b.removeAttribute("id")}}}return A(a.replace(ga,"$1"),b,c,d)}function b(){function a(c,d){return b.push(c+" ")>v.cacheLength&&delete a[b.shift()],a[c+" "]=d}var b=[];return a}function c(a){return a[M]=!0,a}function d(a){var b=F.createElement("div");try{return!!a(b)}catch(a){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function e(a,b){for(var c=a.split("|"),d=a.length;d--;)v.attrHandle[c[d]]=b}function f(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||V)-(~a.sourceIndex||V);if(d)return d;if(c)for(;c=c.nextSibling;)if(c===b)return-1;return a?1:-1}function g(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function h(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function i(a){return c(function(b){return b=+b,c(function(c,d){for(var e,f=a([],c.length,b),g=f.length;g--;)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function j(a){return a&&typeof a.getElementsByTagName!==U&&a}function k(){}function l(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function m(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=P++;return b.first?function(b,c,f){for(;b=b[d];)if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[O,f];if(g){for(;b=b[d];)if((1===b.nodeType||e)&&a(b,c,g))return!0}else for(;b=b[d];)if(1===b.nodeType||e){if(i=b[M]||(b[M]={}),(h=i[d])&&h[0]===O&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function n(a){return a.length>1?function(b,c,d){for(var e=a.length;e--;)if(!a[e](b,c,d))return!1;return!0}:a[0]}function o(b,c,d){for(var e=0,f=c.length;e<f;e++)a(b,c[e],d);return d}function p(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function q(a,b,d,e,f,g){return e&&!e[M]&&(e=q(e)),f&&!f[M]&&(f=q(f,g)),c(function(c,g,h,i){var j,k,l,m=[],n=[],q=g.length,r=c||o(b||"*",h.nodeType?[h]:h,[]),s=!a||!c&&b?r:p(r,m,a,h,i),t=d?f||(c?a:q||e)?[]:g:s;if(d&&d(s,t,h,i),e)for(j=p(t,n),e(j,[],h,i),k=j.length;k--;)(l=j[k])&&(t[n[k]]=!(s[n[k]]=l));if(c){if(f||a){if(f){for(j=[],k=t.length;k--;)(l=t[k])&&j.push(s[k]=l);f(null,t=[],j,i)}for(k=t.length;k--;)(l=t[k])&&(j=f?aa.call(c,l):m[k])>-1&&(c[j]=!(g[j]=l))}}else t=p(t===g?t.splice(q,t.length):t),f?f(null,g,t,i):$.apply(g,t)})}function r(a){for(var b,c,d,e=a.length,f=v.relative[a[0].type],g=f||v.relative[" "],h=f?1:0,i=m(function(a){return a===b},g,!0),j=m(function(a){return aa.call(b,a)>-1},g,!0),k=[function(a,c,d){return!f&&(d||c!==B)||((b=c).nodeType?i(a,c,d):j(a,c,d))}];h<e;h++)if(c=v.relative[a[h].type])k=[m(n(k),c)];else{if(c=v.filter[a[h].type].apply(null,a[h].matches),c[M]){for(d=++h;d<e&&!v.relative[a[d].type];d++);return q(h>1&&n(k),h>1&&l(a.slice(0,h-1).concat({value:" "===a[h-2].type?"*":""})).replace(ga,"$1"),c,h<d&&r(a.slice(h,d)),d<e&&r(a=a.slice(d)),d<e&&l(a))}k.push(c)}return n(k)}function s(b,d){var e=d.length>0,f=b.length>0,g=function(c,g,h,i,j){var k,l,m,n=0,o="0",q=c&&[],r=[],s=B,t=c||f&&v.find.TAG("*",j),u=O+=null==s?1:Math.random()||.1,w=t.length;for(j&&(B=g!==F&&g);o!==w&&null!=(k=t[o]);o++){if(f&&k){for(l=0;m=b[l++];)if(m(k,g,h)){i.push(k);break}j&&(O=u)}e&&((k=!m&&k)&&n--,c&&q.push(k))}if(n+=o,e&&o!==n){for(l=0;m=d[l++];)m(q,r,g,h);if(c){if(n>0)for(;o--;)q[o]||r[o]||(r[o]=Y.call(i));r=p(r)}$.apply(i,r),j&&!c&&r.length>0&&n+d.length>1&&a.uniqueSort(i)}return j&&(O=u,B=s),q};return e?c(g):g}var t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M="sizzle"+-new Date,N=window.document,O=0,P=0,Q=b(),R=b(),S=b(),T=function(a,b){return a===b&&(D=!0),0},U="undefined",V=1<<31,W={}.hasOwnProperty,X=[],Y=X.pop,Z=X.push,$=X.push,_=X.slice,aa=X.indexOf||function(a){for(var b=0,c=this.length;b<c;b++)if(this[b]===a)return b;return-1},ba="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",ca="[\\x20\\t\\r\\n\\f]",da="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",ea="\\["+ca+"*("+da+")(?:"+ca+"*([*^$|!~]?=)"+ca+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+da+"))|)"+ca+"*\\]",fa=":("+da+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+ea+")*)|.*)\\)|)",ga=new RegExp("^"+ca+"+|((?:^|[^\\\\])(?:\\\\.)*)"+ca+"+$","g"),ha=new RegExp("^"+ca+"*,"+ca+"*"),ia=new RegExp("^"+ca+"*([>+~]|"+ca+")"+ca+"*"),ja=new RegExp("="+ca+"*([^\\]'\"]*?)"+ca+"*\\]","g"),ka=new RegExp(fa),la=new RegExp("^"+da+"$"),ma={ID:new RegExp("^#("+da+")"),CLASS:new RegExp("^\\.("+da+")"),TAG:new RegExp("^("+da+"|[*])"),ATTR:new RegExp("^"+ea),PSEUDO:new RegExp("^"+fa),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ca+"*(even|odd|(([+-]|)(\\d*)n|)"+ca+"*(?:([+-]|)"+ca+"*(\\d+)|))"+ca+"*\\)|)","i"),bool:new RegExp("^(?:"+ba+")$","i"),needsContext:new RegExp("^"+ca+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ca+"*((?:-\\d)?\\d*)"+ca+"*\\)|)(?=[^-]|$)","i")},na=/^(?:input|select|textarea|button)$/i,oa=/^h\d$/i,pa=/^[^{]+\{\s*\[native \w/,qa=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ra=/[+~]/,sa=/'|\\/g,ta=new RegExp("\\\\([\\da-f]{1,6}"+ca+"?|("+ca+")|.)","ig"),ua=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{$.apply(X=_.call(N.childNodes),N.childNodes),X[N.childNodes.length].nodeType}catch(a){$={apply:X.length?function(a,b){Z.apply(a,_.call(b))}:function(a,b){for(var c=a.length,d=0;a[c++]=b[d++];);a.length=c-1}}}u=a.support={},x=a.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},E=a.setDocument=function(a){function b(a){try{return a.top}catch(a){}return null}var c,e=a?a.ownerDocument||a:N,g=e.defaultView;return e!==F&&9===e.nodeType&&e.documentElement?(F=e,G=e.documentElement,H=!x(e),g&&g!==b(g)&&(g.addEventListener?g.addEventListener("unload",function(){E()},!1):g.attachEvent&&g.attachEvent("onunload",function(){E()})),u.attributes=d(function(a){return a.className="i",!a.getAttribute("className")}),u.getElementsByTagName=d(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),u.getElementsByClassName=pa.test(e.getElementsByClassName),u.getById=d(function(a){return G.appendChild(a).id=M,!e.getElementsByName||!e.getElementsByName(M).length}),u.getById?(v.find.ID=function(a,b){if(typeof b.getElementById!==U&&H){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},v.filter.ID=function(a){var b=a.replace(ta,ua);return function(a){return a.getAttribute("id")===b}}):(delete v.find.ID,v.filter.ID=function(a){var b=a.replace(ta,ua);return function(a){var c=typeof a.getAttributeNode!==U&&a.getAttributeNode("id");return c&&c.value===b}}),v.find.TAG=u.getElementsByTagName?function(a,b){if(typeof b.getElementsByTagName!==U)return b.getElementsByTagName(a)}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){for(;c=f[e++];)1===c.nodeType&&d.push(c);return d}return f},v.find.CLASS=u.getElementsByClassName&&function(a,b){if(H)return b.getElementsByClassName(a)},J=[],I=[],(u.qsa=pa.test(e.querySelectorAll))&&(d(function(a){a.innerHTML="<select msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&I.push("[*^$]="+ca+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||I.push("\\["+ca+"*(?:value|"+ba+")"),a.querySelectorAll(":checked").length||I.push(":checked")}),d(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&I.push("name"+ca+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||I.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),I.push(",.*:")})),(u.matchesSelector=pa.test(K=G.matches||G.webkitMatchesSelector||G.mozMatchesSelector||G.oMatchesSelector||G.msMatchesSelector))&&d(function(a){u.disconnectedMatch=K.call(a,"div"),K.call(a,"[s!='']:x"),J.push("!=",fa)}),I=I.length&&new RegExp(I.join("|")),J=J.length&&new RegExp(J.join("|")),c=pa.test(G.compareDocumentPosition),L=c||pa.test(G.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)for(;b=b.parentNode;)if(b===a)return!0;return!1},T=c?function(a,b){if(a===b)return D=!0,0;var c=!a.compareDocumentPosition-!b.compareDocumentPosition;return c?c:(c=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&c||!u.sortDetached&&b.compareDocumentPosition(a)===c?a===e||a.ownerDocument===N&&L(N,a)?-1:b===e||b.ownerDocument===N&&L(N,b)?1:C?aa.call(C,a)-aa.call(C,b):0:4&c?-1:1)}:function(a,b){if(a===b)return D=!0,0;var c,d=0,g=a.parentNode,h=b.parentNode,i=[a],j=[b];if(!g||!h)return a===e?-1:b===e?1:g?-1:h?1:C?aa.call(C,a)-aa.call(C,b):0;if(g===h)return f(a,b);for(c=a;c=c.parentNode;)i.unshift(c);for(c=b;c=c.parentNode;)j.unshift(c);for(;i[d]===j[d];)d++;return d?f(i[d],j[d]):i[d]===N?-1:j[d]===N?1:0},e):F},a.matches=function(b,c){return a(b,null,null,c)},a.matchesSelector=function(b,c){if((b.ownerDocument||b)!==F&&E(b),c=c.replace(ja,"='$1']"),u.matchesSelector&&H&&(!J||!J.test(c))&&(!I||!I.test(c)))try{var d=K.call(b,c);if(d||u.disconnectedMatch||b.document&&11!==b.document.nodeType)return d}catch(a){}return a(c,F,null,[b]).length>0},a.contains=function(a,b){return(a.ownerDocument||a)!==F&&E(a),L(a,b)},a.attr=function(a,b){(a.ownerDocument||a)!==F&&E(a);var c=v.attrHandle[b.toLowerCase()],d=c&&W.call(v.attrHandle,b.toLowerCase())?c(a,b,!H):void 0;return void 0!==d?d:u.attributes||!H?a.getAttribute(b):(d=a.getAttributeNode(b))&&d.specified?d.value:null},a.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},a.uniqueSort=function(a){var b,c=[],d=0,e=0;if(D=!u.detectDuplicates,C=!u.sortStable&&a.slice(0),a.sort(T),D){for(;b=a[e++];)b===a[e]&&(d=c.push(e));for(;d--;)a.splice(c[d],1)}return C=null,a},w=a.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(1===e||9===e||11===e){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=w(a)}else if(3===e||4===e)return a.nodeValue}else for(;b=a[d++];)c+=w(b);return c},v=a.selectors={cacheLength:50,createPseudo:c,match:ma,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ta,ua),a[3]=(a[3]||a[4]||a[5]||"").replace(ta,ua),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(b){return b[1]=b[1].toLowerCase(),"nth"===b[1].slice(0,3)?(b[3]||a.error(b[0]),b[4]=+(b[4]?b[5]+(b[6]||1):2*("even"===b[3]||"odd"===b[3])),b[5]=+(b[7]+b[8]||"odd"===b[3])):b[3]&&a.error(b[0]),b},PSEUDO:function(a){var b,c=!a[6]&&a[2];return ma.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&ka.test(c)&&(b=y(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ta,ua).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=Q[a+" "];return b||(b=new RegExp("(^|"+ca+")"+a+"("+ca+"|$)"))&&Q(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==U&&a.getAttribute("class")||"")})},ATTR:function(b,c,d){return function(e){var f=a.attr(e,b);return null==f?"!="===c:!c||(f+="","="===c?f===d:"!="===c?f!==d:"^="===c?d&&0===f.indexOf(d):"*="===c?d&&f.indexOf(d)>-1:"$="===c?d&&f.slice(-d.length)===d:"~="===c?(" "+f+" ").indexOf(d)>-1:"|="===c&&(f===d||f.slice(0,d.length+1)===d+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){for(;p;){for(l=b;l=l[p];)if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){for(k=q[M]||(q[M]={}),j=k[a]||[],n=j[0]===O&&j[1],m=j[0]===O&&j[2],l=n&&q.childNodes[n];l=++n&&l&&l[p]||(m=n=0)||o.pop();)if(1===l.nodeType&&++m&&l===b){k[a]=[O,n,m];break}}else if(s&&(j=(b[M]||(b[M]={}))[a])&&j[0]===O)m=j[1];else for(;(l=++n&&l&&l[p]||(m=n=0)||o.pop())&&((h?l.nodeName.toLowerCase()!==r:1!==l.nodeType)||!++m||(s&&((l[M]||(l[M]={}))[a]=[O,m]),l!==b)););return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(b,d){var e,f=v.pseudos[b]||v.setFilters[b.toLowerCase()]||a.error("unsupported pseudo: "+b);return f[M]?f(d):f.length>1?(e=[b,b,"",d],v.setFilters.hasOwnProperty(b.toLowerCase())?c(function(a,b){for(var c,e=f(a,d),g=e.length;g--;)c=aa.call(a,e[g]),a[c]=!(b[c]=e[g])}):function(a){return f(a,0,e)}):f}},pseudos:{not:c(function(a){var b=[],d=[],e=z(a.replace(ga,"$1"));return e[M]?c(function(a,b,c,d){for(var f,g=e(a,null,d,[]),h=a.length;h--;)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,c,f){return b[0]=a,e(b,null,f,d),!d.pop()}}),has:c(function(b){return function(c){return a(b,c).length>0}}),contains:c(function(a){return a=a.replace(ta,ua),function(b){return(b.textContent||b.innerText||w(b)).indexOf(a)>-1}}),lang:c(function(b){return la.test(b||"")||a.error("unsupported lang: "+b),b=b.replace(ta,ua).toLowerCase(),function(a){var c;do if(c=H?a.lang:a.getAttribute("xml:lang")||a.getAttribute("lang"))return c=c.toLowerCase(),c===b||0===c.indexOf(b+"-");while((a=a.parentNode)&&1===a.nodeType);return!1}}),target:function(a){var b=window.location&&window.location.hash;return b&&b.slice(1)===a.id},root:function(a){return a===G},focus:function(a){return a===F.activeElement&&(!F.hasFocus||F.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!v.pseudos.empty(a)},header:function(a){return oa.test(a.nodeName)},input:function(a){return na.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:i(function(){return[0]}),last:i(function(a,b){return[b-1]}),eq:i(function(a,b,c){return[c<0?c+b:c]}),even:i(function(a,b){for(var c=0;c<b;c+=2)a.push(c);return a}),odd:i(function(a,b){for(var c=1;c<b;c+=2)a.push(c);return a}),lt:i(function(a,b,c){for(var d=c<0?c+b:c;--d>=0;)a.push(d);return a}),gt:i(function(a,b,c){for(var d=c<0?c+b:c;++d<b;)a.push(d);return a})}},v.pseudos.nth=v.pseudos.eq;for(t in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})v.pseudos[t]=g(t);for(t in{submit:!0,reset:!0})v.pseudos[t]=h(t);return k.prototype=v.filters=v.pseudos,v.setFilters=new k,y=a.tokenize=function(b,c){var d,e,f,g,h,i,j,k=R[b+" "];if(k)return c?0:k.slice(0);for(h=b,i=[],j=v.preFilter;h;){d&&!(e=ha.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),d=!1,(e=ia.exec(h))&&(d=e.shift(),f.push({value:d,type:e[0].replace(ga," ")}),h=h.slice(d.length));for(g in v.filter)!(e=ma[g].exec(h))||j[g]&&!(e=j[g](e))||(d=e.shift(),f.push({value:d,type:g,matches:e}),h=h.slice(d.length));if(!d)break}return c?h.length:h?a.error(b):R(b,i).slice(0)},z=a.compile=function(a,b){var c,d=[],e=[],f=S[a+" "];if(!f){for(b||(b=y(a)),c=b.length;c--;)f=r(b[c]),f[M]?d.push(f):e.push(f);f=S(a,s(e,d)),f.selector=a}return f},A=a.select=function(a,b,c,d){var e,f,g,h,i,k="function"==typeof a&&a,m=!d&&y(a=k.selector||a);if(c=c||[],1===m.length){if(f=m[0]=m[0].slice(0),f.length>2&&"ID"===(g=f[0]).type&&u.getById&&9===b.nodeType&&H&&v.relative[f[1].type]){if(b=(v.find.ID(g.matches[0].replace(ta,ua),b)||[])[0],!b)return c;k&&(b=b.parentNode),a=a.slice(f.shift().value.length)}for(e=ma.needsContext.test(a)?0:f.length;e--&&(g=f[e],!v.relative[h=g.type]);)if((i=v.find[h])&&(d=i(g.matches[0].replace(ta,ua),ra.test(f[0].type)&&j(b.parentNode)||b))){if(f.splice(e,1),a=d.length&&l(f),!a)return $.apply(c,d),c;break}}return(k||z(a,m))(d,b,!H,c,ra.test(a)&&j(b.parentNode)||b),c},u.sortStable=M.split("").sort(T).join("")===M,u.detectDuplicates=!!D,E(),u.sortDetached=d(function(a){return 1&a.compareDocumentPosition(F.createElement("div"))}),d(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||e("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),u.attributes&&d(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||e("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),d(function(a){return null==a.getAttribute("disabled")})||e(ba,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),a}),g("1g",[],function(){function a(a){var b,c,d=a;if(!j(a))for(d=[],b=0,c=a.length;b<c;b++)d[b]=a[b];return d}function b(a,b,c){var d,e;if(!a)return 0;if(c=c||a,void 0!==a.length){for(d=0,e=a.length;d<e;d++)if(b.call(c,a[d],d,a)===!1)return 0}else for(d in a)if(a.hasOwnProperty(d)&&b.call(c,a[d],d,a)===!1)return 0;return 1}function c(a,c){var d=[];return b(a,function(b,e){d.push(c(b,e,a))}),d}function d(a,c){var d=[];return b(a,function(b,e){c&&!c(b,e,a)||d.push(b)}),d}function e(a,b){var c,d;if(a)for(c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1}function f(a,b,c,d){var e=0;for(arguments.length<3&&(c=a[0]);e<a.length;e++)c=b.call(d,c,a[e],e);return c}function g(a,b,c){var d,e;for(d=0,e=a.length;d<e;d++)if(b.call(c,a[d],d,a))return d;return-1}function h(a,b,c){var d=g(a,b,c);if(d!==-1)return a[d]}function i(a){return a[a.length-1]}var j=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};return{isArray:j,toArray:a,each:b,map:c,filter:d,indexOf:e,reduce:f,findIndex:g,find:h,last:i}}),g("9",["6","1g"],function(a,b){function c(a){return null===a||void 0===a?"":(""+a).replace(n,"")}function d(a,c){return c?!("array"!=c||!b.isArray(a))||typeof a==c:void 0!==a}function e(a,b,c){var d;for(a=a||[],b=b||",","string"==typeof a&&(a=a.split(b)),c=c||{},d=a.length;d--;)c[a[d]]={};return c}function f(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function g(a,b,c){var d,e,f,g,h,i=this,j=0;if(a=/^((static) )?([\w.]+)(:([\w.]+))?/.exec(a),f=a[3].match(/(^|\.)(\w+)$/i)[2],e=i.createNS(a[3].replace(/\.\w+$/,""),c),!e[f]){if("static"==a[2])return e[f]=b,void(this.onCreate&&this.onCreate(a[2],a[3],e[f]));b[f]||(b[f]=function(){},j=1),e[f]=b[f],i.extend(e[f].prototype,b),a[5]&&(d=i.resolve(a[5]).prototype,g=a[5].match(/\.(\w+)$/i)[1],h=e[f],j?e[f]=function(){return d[g].apply(this,arguments)}:e[f]=function(){return this.parent=d[g],h.apply(this,arguments)},e[f].prototype[f]=e[f],i.each(d,function(a,b){e[f].prototype[b]=d[b]}),i.each(b,function(a,b){d[b]?e[f].prototype[b]=function(){return this.parent=d[b],a.apply(this,arguments)}:b!=f&&(e[f].prototype[b]=a)})),i.each(b["static"],function(a,b){e[f][b]=a})}}function h(a,b){var c,d,e,f,g=arguments;for(c=1,d=g.length;c<d;c++){b=g[c];for(e in b)b.hasOwnProperty(e)&&(f=b[e],void 0!==f&&(a[e]=f))}return a}function i(a,c,d,e){e=e||this,a&&(d&&(a=a[d]),b.each(a,function(a,b){return c.call(e,a,b,d)!==!1&&void i(a,c,d,e)}))}function j(a,b){var c,d;for(b=b||window,a=a.split("."),c=0;c<a.length;c++)d=a[c],b[d]||(b[d]={}),b=b[d];return b}function k(a,b){var c,d;for(b=b||window,a=a.split("."),c=0,d=a.length;c<d&&(b=b[a[c]],b);c++);return b}function l(a,e){return!a||d(a,"array")?a:b.map(a.split(e||","),c)}function m(b){var c=a.cacheSuffix;return c&&(b+=(b.indexOf("?")===-1?"?":"&")+c),b}var n=/^\s*|\s*$/g;return{trim:c,isArray:b.isArray,is:d,toArray:b.toArray,makeMap:e,each:b.each,map:b.map, +grep:b.filter,inArray:b.indexOf,hasOwn:f,extend:h,create:g,walk:i,createNS:j,resolve:k,explode:l,_addCacheSuffix:m}}),g("a",["7","8","9","6"],function(a,b,c,d){function e(a){return"undefined"!=typeof a}function f(a){return"string"==typeof a}function g(a){return a&&a==a.window}function h(a,b){var c,d,e;for(b=b||w,e=b.createElement("div"),c=b.createDocumentFragment(),e.innerHTML=a;d=e.firstChild;)c.appendChild(d);return c}function i(a,b,c,d){var e;if(f(b))b=h(b,q(a[0]));else if(b.length&&!b.nodeType){if(b=l.makeArray(b),d)for(e=b.length-1;e>=0;e--)i(a,b[e],c,d);else for(e=0;e<b.length;e++)i(a,b[e],c,d);return a}if(b.nodeType)for(e=a.length;e--;)c.call(a[e],b);return a}function j(a,b){return a&&b&&(" "+a.className+" ").indexOf(" "+b+" ")!==-1}function k(a,b,c){var d,e;return b=l(b)[0],a.each(function(){var a=this;c&&d==a.parentNode?e.appendChild(a):(d=a.parentNode,e=b.cloneNode(!1),a.parentNode.insertBefore(e,a),e.appendChild(a))}),a}function l(a,b){return new l.fn.init(a,b)}function m(a,b){var c;if(b.indexOf)return b.indexOf(a);for(c=b.length;c--;)if(b[c]===a)return c;return-1}function n(a){return null===a||a===v?"":(""+a).replace(I,"")}function o(a,b){var c,d,e,f,g;if(a)if(c=a.length,c===f){for(d in a)if(a.hasOwnProperty(d)&&(g=a[d],b.call(g,d,g)===!1))break}else for(e=0;e<c&&(g=a[e],b.call(g,e,g)!==!1);e++);return a}function p(a,b){var c=[];return o(a,function(a,d){b(d,a)&&c.push(d)}),c}function q(a){return a?9==a.nodeType?a:a.ownerDocument:w}function r(a,b,c){var d=[],e=a[b];for("string"!=typeof c&&c instanceof l&&(c=c[0]);e&&9!==e.nodeType;){if(void 0!==c){if(e===c)break;if("string"==typeof c&&l(e).is(c))break}1===e.nodeType&&d.push(e),e=e[b]}return d}function s(a,b,c,d){var e=[];for(d instanceof l&&(d=d[0]);a;a=a[b])if(!c||a.nodeType===c){if(void 0!==d){if(a===d)break;if("string"==typeof d&&l(a).is(d))break}e.push(a)}return e}function t(a,b,c){for(a=a[b];a;a=a[b])if(a.nodeType==c)return a;return null}function u(a,b,c){o(c,function(c,d){a[c]=a[c]||{},a[c][b]=d})}var v,w=document,x=Array.prototype.push,y=Array.prototype.slice,z=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,A=a.Event,B=c.makeMap("children,contents,next,prev"),C=c.makeMap("fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom"," "),D=c.makeMap("checked compact declare defer disabled ismap multiple nohref noshade nowrap readonly selected"," "),E={"for":"htmlFor","class":"className",readonly:"readOnly"},F={"float":"cssFloat"},G={},H={},I=/^\s*|\s*$/g;return l.fn=l.prototype={constructor:l,selector:"",context:null,length:0,init:function(a,b){var c,d,e=this;if(!a)return e;if(a.nodeType)return e.context=e[0]=a,e.length=1,e;if(b&&b.nodeType)e.context=b;else{if(b)return l(a).attr(b);e.context=b=document}if(f(a)){if(e.selector=a,c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c)return l(b).find(a);if(c[1])for(d=h(a,q(b)).firstChild;d;)x.call(e,d),d=d.nextSibling;else{if(d=q(b).getElementById(c[2]),!d)return e;if(d.id!==c[2])return e.find(a);e.length=1,e[0]=d}}else this.add(a,!1);return e},toArray:function(){return c.toArray(this)},add:function(a,b){var c,d,e=this;if(f(a))return e.add(l(a));if(b!==!1)for(c=l.unique(e.toArray().concat(l.makeArray(a))),e.length=c.length,d=0;d<c.length;d++)e[d]=c[d];else x.apply(e,l.makeArray(a));return e},attr:function(a,b){var c,d=this;if("object"==typeof a)o(a,function(a,b){d.attr(a,b)});else{if(!e(b)){if(d[0]&&1===d[0].nodeType){if(c=G[a],c&&c.get)return c.get(d[0],a);if(D[a])return d.prop(a)?a:v;b=d[0].getAttribute(a,2),null===b&&(b=v)}return b}this.each(function(){var c;if(1===this.nodeType){if(c=G[a],c&&c.set)return void c.set(this,b);null===b?this.removeAttribute(a,2):this.setAttribute(a,b,2)}})}return d},removeAttr:function(a){return this.attr(a,null)},prop:function(a,b){var c=this;if(a=E[a]||a,"object"==typeof a)o(a,function(a,b){c.prop(a,b)});else{if(!e(b))return c[0]&&c[0].nodeType&&a in c[0]?c[0][a]:b;this.each(function(){1==this.nodeType&&(this[a]=b)})}return c},css:function(a,b){function c(a){return a.replace(/-(\D)/g,function(a,b){return b.toUpperCase()})}function d(a){return a.replace(/[A-Z]/g,function(a){return"-"+a})}var f,g,h=this;if("object"==typeof a)o(a,function(a,b){h.css(a,b)});else if(e(b))a=c(a),"number"!=typeof b||C[a]||(b+="px"),h.each(function(){var c=this.style;if(g=H[a],g&&g.set)return void g.set(this,b);try{this.style[F[a]||a]=b}catch(a){}null!==b&&""!==b||(c.removeProperty?c.removeProperty(d(a)):c.removeAttribute(a))});else{if(f=h[0],g=H[a],g&&g.get)return g.get(f);if(f.ownerDocument.defaultView)try{return f.ownerDocument.defaultView.getComputedStyle(f,null).getPropertyValue(d(a))}catch(a){return v}else if(f.currentStyle)return f.currentStyle[c(a)]}return h},remove:function(){for(var a,b=this,c=this.length;c--;)a=b[c],A.clean(a),a.parentNode&&a.parentNode.removeChild(a);return this},empty:function(){for(var a,b=this,c=this.length;c--;)for(a=b[c];a.firstChild;)a.removeChild(a.firstChild);return this},html:function(a){var b,c=this;if(e(a)){b=c.length;try{for(;b--;)c[b].innerHTML=a}catch(d){l(c[b]).empty().append(a)}return c}return c[0]?c[0].innerHTML:""},text:function(a){var b,c=this;if(e(a)){for(b=c.length;b--;)"innerText"in c[b]?c[b].innerText=a:c[0].textContent=a;return c}return c[0]?c[0].innerText||c[0].textContent:""},append:function(){return i(this,arguments,function(a){(1===this.nodeType||this.host&&1===this.host.nodeType)&&this.appendChild(a)})},prepend:function(){return i(this,arguments,function(a){(1===this.nodeType||this.host&&1===this.host.nodeType)&&this.insertBefore(a,this.firstChild)},!0)},before:function(){var a=this;return a[0]&&a[0].parentNode?i(a,arguments,function(a){this.parentNode.insertBefore(a,this)}):a},after:function(){var a=this;return a[0]&&a[0].parentNode?i(a,arguments,function(a){this.parentNode.insertBefore(a,this.nextSibling)},!0):a},appendTo:function(a){return l(a).append(this),this},prependTo:function(a){return l(a).prepend(this),this},replaceWith:function(a){return this.before(a).remove()},wrap:function(a){return k(this,a)},wrapAll:function(a){return k(this,a,!0)},wrapInner:function(a){return this.each(function(){l(this).contents().wrapAll(a)}),this},unwrap:function(){return this.parent().each(function(){l(this).replaceWith(this.childNodes)})},clone:function(){var a=[];return this.each(function(){a.push(this.cloneNode(!0))}),l(a)},addClass:function(a){return this.toggleClass(a,!0)},removeClass:function(a){return this.toggleClass(a,!1)},toggleClass:function(a,b){var c=this;return"string"!=typeof a?c:(a.indexOf(" ")!==-1?o(a.split(" "),function(){c.toggleClass(this,b)}):c.each(function(c,d){var e,f;f=j(d,a),f!==b&&(e=d.className,f?d.className=n((" "+e+" ").replace(" "+a+" "," ")):d.className+=e?" "+a:a)}),c)},hasClass:function(a){return j(this[0],a)},each:function(a){return o(this,a)},on:function(a,b){return this.each(function(){A.bind(this,a,b)})},off:function(a,b){return this.each(function(){A.unbind(this,a,b)})},trigger:function(a){return this.each(function(){"object"==typeof a?A.fire(this,a.type,a):A.fire(this,a)})},show:function(){return this.css("display","")},hide:function(){return this.css("display","none")},slice:function(){return new l(y.apply(this,arguments))},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},find:function(a){var b,c,d=[];for(b=0,c=this.length;b<c;b++)l.find(a,this[b],d);return l(d)},filter:function(a){return l("function"==typeof a?p(this.toArray(),function(b,c){return a(c,b)}):l.filter(a,this.toArray()))},closest:function(a){var b=[];return a instanceof l&&(a=a[0]),this.each(function(c,d){for(;d;){if("string"==typeof a&&l(d).is(a)){b.push(d);break}if(d==a){b.push(d);break}d=d.parentNode}}),l(b)},offset:function(a){var b,c,d,e,f=0,g=0;return a?this.css(a):(b=this[0],b&&(c=b.ownerDocument,d=c.documentElement,b.getBoundingClientRect&&(e=b.getBoundingClientRect(),f=e.left+(d.scrollLeft||c.body.scrollLeft)-d.clientLeft,g=e.top+(d.scrollTop||c.body.scrollTop)-d.clientTop)),{left:f,top:g})},push:x,sort:[].sort,splice:[].splice},c.extend(l,{extend:c.extend,makeArray:function(a){return g(a)||a.nodeType?[a]:c.toArray(a)},inArray:m,isArray:c.isArray,each:o,trim:n,grep:p,find:b,expr:b.selectors,unique:b.uniqueSort,text:b.getText,contains:b.contains,filter:function(a,b,c){var d=b.length;for(c&&(a=":not("+a+")");d--;)1!=b[d].nodeType&&b.splice(d,1);return b=1===b.length?l.find.matchesSelector(b[0],a)?[b[0]]:[]:l.find.matches(a,b)}}),o({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return r(a,"parentNode")},next:function(a){return t(a,"nextSibling",1)},prev:function(a){return t(a,"previousSibling",1)},children:function(a){return s(a.firstChild,"nextSibling",1)},contents:function(a){return c.toArray(("iframe"===a.nodeName?a.contentDocument||a.contentWindow.document:a).childNodes)}},function(a,b){l.fn[a]=function(c){var d=this,e=[];return d.each(function(){var a=b.call(e,this,c,e);a&&(l.isArray(a)?e.push.apply(e,a):e.push(a))}),this.length>1&&(B[a]||(e=l.unique(e)),0===a.indexOf("parents")&&(e=e.reverse())),e=l(e),c?e.filter(c):e}}),o({parentsUntil:function(a,b){return r(a,"parentNode",b)},nextUntil:function(a,b){return s(a,"nextSibling",1,b).slice(1)},prevUntil:function(a,b){return s(a,"previousSibling",1,b).slice(1)}},function(a,b){l.fn[a]=function(c,d){var e=this,f=[];return e.each(function(){var a=b.call(f,this,c,f);a&&(l.isArray(a)?f.push.apply(f,a):f.push(a))}),this.length>1&&(f=l.unique(f),0!==a.indexOf("parents")&&"prevUntil"!==a||(f=f.reverse())),f=l(f),d?f.filter(d):f}}),l.fn.is=function(a){return!!a&&this.filter(a).length>0},l.fn.init.prototype=l.fn,l.overrideDefaults=function(a){function b(d,e){return c=c||a(),0===arguments.length&&(d=c.element),e||(e=c.context),new b.fn.init(d,e)}var c;return l.extend(b,this),b},d.ie&&d.ie<8&&(u(G,"get",{maxlength:function(a){var b=a.maxLength;return 2147483647===b?v:b},size:function(a){var b=a.size;return 20===b?v:b},"class":function(a){return a.className},style:function(a){var b=a.style.cssText;return 0===b.length?v:b}}),u(G,"set",{"class":function(a,b){a.className=b},style:function(a,b){a.style.cssText=b}})),d.ie&&d.ie<9&&(F["float"]="styleFloat",u(H,"set",{opacity:function(a,b){var c=a.style;null===b||""===b?c.removeAttribute("filter"):(c.zoom=1,c.filter="alpha(opacity="+100*b+")")}})),l.attrHooks=G,l.cssHooks=H,l}),g("b",[],function(){return function(a,b){function c(a,b,c,d){function e(a){return a=parseInt(a,10).toString(16),a.length>1?a:"0"+a}return"#"+e(b)+e(c)+e(d)}var d,e,f,g,h=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,i=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,j=/\s*([^:]+):\s*([^;]+);?/g,k=/\s+$/,l={},m="\ufeff";for(a=a||{},b&&(f=b.getValidStyles(),g=b.getInvalidStyles()),e=("\\\" \\' \\; \\: ; : "+m).split(" "),d=0;d<e.length;d++)l[e[d]]=m+d,l[m+d]=e[d];return{toHex:function(a){return a.replace(h,c)},parse:function(b){function e(a,b,c){var e,f,g,h;if(e=w[a+"-top"+b],e&&(f=w[a+"-right"+b],f&&(g=w[a+"-bottom"+b],g&&(h=w[a+"-left"+b])))){var i=[e,f,g,h];for(d=i.length-1;d--&&i[d]===i[d+1];);d>-1&&c||(w[a+b]=d==-1?i[0]:i.join(" "),delete w[a+"-top"+b],delete w[a+"-right"+b],delete w[a+"-bottom"+b],delete w[a+"-left"+b])}}function f(a){var b,c=w[a];if(c){for(c=c.split(" "),b=c.length;b--;)if(c[b]!==c[0])return!1;return w[a]=c[0],!0}}function g(a,b,c,d){f(b)&&f(c)&&f(d)&&(w[a]=w[b]+" "+w[c]+" "+w[d],delete w[b],delete w[c],delete w[d])}function n(a){return v=!0,l[a]}function o(a,b){return v&&(a=a.replace(/\uFEFF[0-9]/g,function(a){return l[a]})),b||(a=a.replace(/\\([\'\";:])/g,"$1")),a}function p(a){return String.fromCharCode(parseInt(a.slice(1),16))}function q(a){return a.replace(/\\[0-9a-f]+/gi,p)}function r(b,c,d,e,f,g){if(f=f||g)return f=o(f),"'"+f.replace(/\'/g,"\\'")+"'";if(c=o(c||d||e),!a.allow_script_urls){var h=c.replace(/[\s\r\n]+/g,"");if(/(java|vb)script:/i.test(h))return"";if(!a.allow_svg_data_urls&&/^data:image\/svg/i.test(h))return""}return x&&(c=x.call(y,c,"style")),"url('"+c.replace(/\'/g,"\\'")+"')"}var s,t,u,v,w={},x=a.url_converter,y=a.url_converter_scope||this;if(b){for(b=b.replace(/[\u0000-\u001F]/g,""),b=b.replace(/\\[\"\';:\uFEFF]/g,n).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(a){return a.replace(/[;:]/g,n)});s=j.exec(b);)if(j.lastIndex=s.index+s[0].length,t=s[1].replace(k,"").toLowerCase(),u=s[2].replace(k,""),t&&u){if(t=q(t),u=q(u),t.indexOf(m)!==-1||t.indexOf('"')!==-1)continue;if(!a.allow_script_urls&&("behavior"==t||/expression\s*\(|\/\*|\*\//.test(u)))continue;"font-weight"===t&&"700"===u?u="bold":"color"!==t&&"background-color"!==t||(u=u.toLowerCase()),u=u.replace(h,c),u=u.replace(i,r),w[t]=v?o(u,!0):u}e("border","",!0),e("border","-width"),e("border","-color"),e("border","-style"),e("padding",""),e("margin",""),g("border","border-width","border-style","border-color"),"medium none"===w.border&&delete w.border,"none"===w["border-image"]&&delete w["border-image"]}return w},serialize:function(a,b){function c(b){var c,d,e,g;if(c=f[b])for(d=0,e=c.length;d<e;d++)b=c[d],g=a[b],g&&(i+=(i.length>0?" ":"")+b+": "+g+";")}function d(a,b){var c;return c=g["*"],(!c||!c[a])&&(c=g[b],!c||!c[a])}var e,h,i="";if(b&&f)c("*"),c(b);else for(e in a)h=a[e],!h||g&&!d(e,b)||(i+=(i.length>0?" ":"")+e+": "+h+";");return i}}}}),g("c",[],function(){return function(a,b){function c(a,c,d,e){var f,g;if(a){if(!e&&a[c])return a[c];if(a!=b){if(f=a[d])return f;for(g=a.parentNode;g&&g!=b;g=g.parentNode)if(f=g[d])return f}}}function d(a,c,d,e){var f,g,h;if(a){if(f=a[d],b&&f===b)return;if(f){if(!e)for(h=f[c];h;h=h[c])if(!h[c])return h;return f}if(g=a.parentNode,g&&g!==b)return g}}var e=a;this.current=function(){return e},this.next=function(a){return e=c(e,"firstChild","nextSibling",a)},this.prev=function(a){return e=c(e,"lastChild","previousSibling",a)},this.prev2=function(a){return e=d(e,"lastChild","previousSibling",a)}}}),g("d",["9"],function(a){function b(a){var b;return b=document.createElement("div"),b.innerHTML=a,b.textContent||b.innerText||a}function c(a,b){var c,d,f,g={};if(a){for(a=a.split(","),b=b||10,c=0;c<a.length;c+=2)d=String.fromCharCode(parseInt(a[c],b)),e[d]||(f="&"+a[c+1]+";",g[d]=f,g[f]=d);return g}}var d,e,f,g=a.makeMap,h=/[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,i=/[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,j=/[<>&\"\']/g,k=/&#([a-z0-9]+);?|&([a-z0-9]+);/gi,l={128:"\u20ac",130:"\u201a",131:"\u0192",132:"\u201e",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02c6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017d",145:"\u2018",146:"\u2019",147:"\u201c",148:"\u201d",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02dc",153:"\u2122",154:"\u0161",155:"\u203a",156:"\u0153",158:"\u017e",159:"\u0178"};e={'"':""","'":"'","<":"<",">":">","&":"&","`":"`"},f={"<":"<",">":">","&":"&",""":'"',"'":"'"},d=c("50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,t9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro",32);var m={encodeRaw:function(a,b){return a.replace(b?h:i,function(a){return e[a]||a})},encodeAllRaw:function(a){return(""+a).replace(j,function(a){return e[a]||a})},encodeNumeric:function(a,b){return a.replace(b?h:i,function(a){return a.length>1?"&#"+(1024*(a.charCodeAt(0)-55296)+(a.charCodeAt(1)-56320)+65536)+";":e[a]||"&#"+a.charCodeAt(0)+";"})},encodeNamed:function(a,b,c){return c=c||d,a.replace(b?h:i,function(a){return e[a]||c[a]||a})},getEncodeFunc:function(a,b){function f(a,c){return a.replace(c?h:i,function(a){return void 0!==e[a]?e[a]:void 0!==b[a]?b[a]:a.length>1?"&#"+(1024*(a.charCodeAt(0)-55296)+(a.charCodeAt(1)-56320)+65536)+";":"&#"+a.charCodeAt(0)+";"})}function j(a,c){return m.encodeNamed(a,c,b)}return b=c(b)||d,a=g(a.replace(/\+/g,",")),a.named&&a.numeric?f:a.named?b?j:m.encodeNamed:a.numeric?m.encodeNumeric:m.encodeRaw},decode:function(a){return a.replace(k,function(a,c){return c?(c="x"===c.charAt(0).toLowerCase()?parseInt(c.substr(1),16):parseInt(c,10),c>65535?(c-=65536,String.fromCharCode(55296+(c>>10),56320+(1023&c))):l[c]||String.fromCharCode(c)):f[a]||d[a]||b(a)})}};return m}),g("1h",["9"],function(a){function b(c){function d(){return J.createDocumentFragment()}function e(a,b){x(N,a,b)}function f(a,b){x(O,a,b)}function g(a){e(a.parentNode,U(a))}function h(a){e(a.parentNode,U(a)+1)}function i(a){f(a.parentNode,U(a))}function j(a){f(a.parentNode,U(a)+1)}function k(a){a?(I[R]=I[Q],I[S]=I[P]):(I[Q]=I[R],I[P]=I[S]),I.collapsed=N}function l(a){g(a),j(a)}function m(a){e(a,0),f(a,1===a.nodeType?a.childNodes.length:a.nodeValue.length)}function n(a,b){var c=I[Q],d=I[P],e=I[R],f=I[S],g=b.startContainer,h=b.startOffset,i=b.endContainer,j=b.endOffset;return 0===a?w(c,d,g,h):1===a?w(e,f,g,h):2===a?w(e,f,i,j):3===a?w(c,d,i,j):void 0}function o(){y(M)}function p(){return y(K)}function q(){return y(L)}function r(a){var b,d,e=this[Q],f=this[P];3!==e.nodeType&&4!==e.nodeType||!e.nodeValue?(e.childNodes.length>0&&(d=e.childNodes[f]),d?e.insertBefore(a,d):3==e.nodeType?c.insertAfter(a,e):e.appendChild(a)):f?f>=e.nodeValue.length?c.insertAfter(a,e):(b=e.splitText(f),e.parentNode.insertBefore(a,b)):e.parentNode.insertBefore(a,e)}function s(a){var b=I.extractContents();I.insertNode(a),a.appendChild(b),I.selectNode(a)}function t(){return T(new b(c),{startContainer:I[Q],startOffset:I[P],endContainer:I[R],endOffset:I[S],collapsed:I.collapsed,commonAncestorContainer:I.commonAncestorContainer})}function u(a,b){var c;if(3==a.nodeType)return a;if(b<0)return a;for(c=a.firstChild;c&&b>0;)--b,c=c.nextSibling;return c?c:a}function v(){return I[Q]==I[R]&&I[P]==I[S]}function w(a,b,d,e){var f,g,h,i,j,k;if(a==d)return b==e?0:b<e?-1:1;for(f=d;f&&f.parentNode!=a;)f=f.parentNode;if(f){for(g=0,h=a.firstChild;h!=f&&g<b;)g++,h=h.nextSibling;return b<=g?-1:1}for(f=a;f&&f.parentNode!=d;)f=f.parentNode;if(f){for(g=0,h=d.firstChild;h!=f&&g<e;)g++,h=h.nextSibling;return g<e?-1:1}for(i=c.findCommonAncestor(a,d),j=a;j&&j.parentNode!=i;)j=j.parentNode;for(j||(j=i),k=d;k&&k.parentNode!=i;)k=k.parentNode;if(k||(k=i),j==k)return 0;for(h=i.firstChild;h;){if(h==j)return-1;if(h==k)return 1;h=h.nextSibling}}function x(a,b,d){var e,f;for(a?(I[Q]=b,I[P]=d):(I[R]=b,I[S]=d),e=I[R];e.parentNode;)e=e.parentNode;for(f=I[Q];f.parentNode;)f=f.parentNode;f==e?w(I[Q],I[P],I[R],I[S])>0&&I.collapse(a):I.collapse(a),I.collapsed=v(),I.commonAncestorContainer=c.findCommonAncestor(I[Q],I[R])}function y(a){var b,c,d,e,f,g,h,i=0,j=0;if(I[Q]==I[R])return z(a);for(b=I[R],c=b.parentNode;c;b=c,c=c.parentNode){if(c==I[Q])return A(b,a);++i}for(b=I[Q],c=b.parentNode;c;b=c,c=c.parentNode){if(c==I[R])return B(b,a);++j}for(d=j-i,e=I[Q];d>0;)e=e.parentNode,d--;for(f=I[R];d<0;)f=f.parentNode,d++;for(g=e.parentNode,h=f.parentNode;g!=h;g=g.parentNode,h=h.parentNode)e=g,f=h;return C(e,f,a)}function z(a){var b,c,e,f,g,h,i,j,k;if(a!=M&&(b=d()),I[P]==I[S])return b;if(3==I[Q].nodeType){if(c=I[Q].nodeValue,e=c.substring(I[P],I[S]),a!=L&&(f=I[Q],j=I[P],k=I[S]-I[P],0===j&&k>=f.nodeValue.length-1?f.parentNode.removeChild(f):f.deleteData(j,k),I.collapse(N)),a==M)return;return e.length>0&&b.appendChild(J.createTextNode(e)),b}for(f=u(I[Q],I[P]),g=I[S]-I[P];f&&g>0;)h=f.nextSibling,i=G(f,a),b&&b.appendChild(i),--g,f=h;return a!=L&&I.collapse(N),b}function A(a,b){var c,e,f,g,h,i;if(b!=M&&(c=d()),e=D(a,b),c&&c.appendChild(e),f=U(a),g=f-I[P],g<=0)return b!=L&&(I.setEndBefore(a),I.collapse(O)),c;for(e=a.previousSibling;g>0;)h=e.previousSibling,i=G(e,b),c&&c.insertBefore(i,c.firstChild),--g,e=h;return b!=L&&(I.setEndBefore(a),I.collapse(O)),c}function B(a,b){var c,e,f,g,h,i;for(b!=M&&(c=d()),f=E(a,b),c&&c.appendChild(f),e=U(a),++e,g=I[S]-e,f=a.nextSibling;f&&g>0;)h=f.nextSibling,i=G(f,b),c&&c.appendChild(i),--g,f=h;return b!=L&&(I.setStartAfter(a),I.collapse(N)),c}function C(a,b,c){var e,f,g,h,i,j,k;for(c!=M&&(f=d()),e=E(a,c),f&&f.appendChild(e),g=U(a),h=U(b),++g,i=h-g,j=a.nextSibling;i>0;)k=j.nextSibling,e=G(j,c),f&&f.appendChild(e),j=k,--i;return e=D(b,c),f&&f.appendChild(e),c!=L&&(I.setStartAfter(a),I.collapse(N)),f}function D(a,b){var c,d,e,f,g,h=u(I[R],I[S]-1),i=h!=I[R];if(h==a)return F(h,i,O,b);for(c=h.parentNode,d=F(c,O,O,b);c;){for(;h;)e=h.previousSibling,f=F(h,i,O,b),b!=M&&d.insertBefore(f,d.firstChild),i=N,h=e;if(c==a)return d;h=c.previousSibling,c=c.parentNode,g=F(c,O,O,b),b!=M&&g.appendChild(d),d=g}}function E(a,b){var c,d,e,f,g,h=u(I[Q],I[P]),i=h!=I[Q];if(h==a)return F(h,i,N,b);for(c=h.parentNode,d=F(c,O,N,b);c;){for(;h;)e=h.nextSibling,f=F(h,i,N,b),b!=M&&d.appendChild(f),i=N,h=e;if(c==a)return d;h=c.nextSibling,c=c.parentNode,g=F(c,O,N,b),b!=M&&g.appendChild(d),d=g}}function F(a,b,d,e){var f,g,h,i,j;if(b)return G(a,e);if(3==a.nodeType){if(f=a.nodeValue,d?(i=I[P],g=f.substring(i),h=f.substring(0,i)):(i=I[S],g=f.substring(0,i),h=f.substring(i)),e!=L&&(a.nodeValue=h),e==M)return;return j=c.clone(a,O),j.nodeValue=g,j}if(e!=M)return c.clone(a,O)}function G(a,b){return b!=M?b==L?c.clone(a,N):a:void a.parentNode.removeChild(a)}function H(){return c.create("body",null,q()).outerText}var I=this,J=c.doc,K=0,L=1,M=2,N=!0,O=!1,P="startOffset",Q="startContainer",R="endContainer",S="endOffset",T=a.extend,U=c.nodeIndex;return T(I,{startContainer:J,startOffset:0,endContainer:J,endOffset:0,collapsed:N,commonAncestorContainer:J,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:e,setEnd:f,setStartBefore:g,setStartAfter:h,setEndBefore:i,setEndAfter:j,collapse:k,selectNode:l,selectNodeContents:m,compareBoundaryPoints:n,deleteContents:o,extractContents:p,cloneContents:q,insertNode:r,surroundContents:s,cloneRange:t,toStringIE:H}),I}return b.prototype.toString=function(){return this.toStringIE()},b}),h("3y",Array),h("3z",Error),g("1m",["3y","3z"],function(a,b){var c=function(){},d=function(a,b){return function(){return a(b.apply(null,arguments))}},e=function(a){return function(){return a}},f=function(a){return a},g=function(a,b){return a===b},h=function(b){for(var c=new a(arguments.length-1),d=1;d<arguments.length;d++)c[d-1]=arguments[d];return function(){for(var d=new a(arguments.length),e=0;e<d.length;e++)d[e]=arguments[e];var f=c.concat(d);return b.apply(null,f)}},i=function(a){return function(){return!a.apply(null,arguments)}},j=function(a){return function(){throw new b(a)}},k=function(a){return a()},l=function(a){a()},m=e(!1),n=e(!0);return{noop:c,compose:d,constant:e,identity:f,tripleEquals:g,curry:h,not:i,die:j,apply:k,call:l,never:m,always:n}}),h("5s",Object),g("4z",["1m","5s"],function(a,b){var c=a.never,d=a.always,e=function(){return f},f=function(){var f=function(a){return a.isNone()},g=function(a){return a()},h=function(a){return a},i=function(){},j={fold:function(a,b){return a()},is:c,isSome:c,isNone:d,getOr:h,getOrThunk:g,getOrDie:function(a){throw new Error(a||"error: getOrDie called on none.")},or:h,orThunk:g,map:e,ap:e,each:i,bind:e,flatten:e,exists:c,forall:d,filter:e,equals:f,equals_:f,toArray:function(){return[]},toString:a.constant("none()")};return b.freeze&&b.freeze(j),j}(),g=function(a){var b=function(){return a},h=function(){return k},i=function(b){return g(b(a))},j=function(b){return b(a)},k={fold:function(b,c){return c(a)},is:function(b){return a===b},isSome:d,isNone:c,getOr:b,getOrThunk:b,getOrDie:b,or:h,orThunk:h,map:i,ap:function(b){return b.fold(e,function(b){return g(b(a))})},each:function(b){b(a)},bind:j,flatten:b,exists:j,forall:j,filter:function(b){return b(a)?k:f},equals:function(b){return b.is(a)},equals_:function(b,d){return b.fold(c,function(b){return d(a,b)})},toArray:function(){return[a]},toString:function(){return"some("+a+")"}};return k},h=function(a){return null===a||void 0===a?f:g(a)};return{some:g,none:e,from:h}}),h("50",String),g("3x",["4z","3y","3z","50"],function(a,b,c,d){var e=function(){var a=b.prototype.indexOf,c=function(b,c){return a.call(b,c)},d=function(a,b){return u(a,b)};return void 0===a?d:c}(),f=function(b,c){var d=e(b,c);return d===-1?a.none():a.some(d)},g=function(a,b){return e(a,b)>-1},h=function(a,b){return t(a,b).isSome()},i=function(a,b){for(var c=[],d=0;d<a;d++)c.push(b(d));return c},j=function(a,b){for(var c=[],d=0;d<a.length;d+=b){var e=a.slice(d,d+b);c.push(e)}return c},k=function(a,c){for(var d=a.length,e=new b(d),f=0;f<d;f++){var g=a[f];e[f]=c(g,f,a)}return e},l=function(a,b){for(var c=0,d=a.length;c<d;c++){var e=a[c];b(e,c,a)}},m=function(a,b){for(var c=a.length-1;c>=0;c--){var d=a[c];b(d,c,a)}},n=function(a,b){for(var c=[],d=[],e=0,f=a.length;e<f;e++){var g=a[e],h=b(g,e,a)?c:d;h.push(g)}return{pass:c,fail:d}},o=function(a,b){for(var c=[],d=0,e=a.length;d<e;d++){var f=a[d];b(f,d,a)&&c.push(f)}return c},p=function(a,b){if(0===a.length)return[];for(var c=b(a[0]),d=[],e=[],f=0,g=a.length;f<g;f++){var h=a[f],i=b(h);i!==c&&(d.push(e),e=[]),c=i,e.push(h)}return 0!==e.length&&d.push(e),d},q=function(a,b,c){return m(a,function(a){c=b(c,a)}),c},r=function(a,b,c){return l(a,function(a){c=b(c,a)}),c},s=function(b,c){for(var d=0,e=b.length;d<e;d++){var f=b[d];if(c(f,d,b))return a.some(f)}return a.none()},t=function(b,c){for(var d=0,e=b.length;d<e;d++){var f=b[d];if(c(f,d,b))return a.some(d)}return a.none()},u=function(a,b){for(var c=0,d=a.length;c<d;++c)if(a[c]===b)return c;return-1},v=b.prototype.push,w=function(a){for(var d=[],e=0,f=a.length;e<f;++e){if(!b.prototype.isPrototypeOf(a[e]))throw new c("Arr.flatten item "+e+" was not an array, input: "+a);v.apply(d,a[e])}return d},x=function(a,b){var c=k(a,b);return w(c)},y=function(a,b){for(var c=0,d=a.length;c<d;++c){var e=a[c];if(b(e,c,a)!==!0)return!1}return!0},z=function(a,b){return a.length===b.length&&y(a,function(a,c){return a===b[c]})},A=b.prototype.slice,B=function(a){var b=A.call(a,0);return b.reverse(),b},C=function(a,b){return o(a,function(a){return!g(b,a)})},D=function(a,b){for(var c={},e=0,f=a.length;e<f;e++){var g=a[e];c[d(g)]=b(g,e)}return c},E=function(a){return[a]},F=function(a,b){var c=A.call(a,0);return c.sort(b),c};return{map:k,each:l,eachr:m,partition:n,filter:o,groupBy:p,indexOf:f,foldr:q,foldl:r,find:s,findIndex:t,flatten:w,bind:x,forall:y,exists:h,contains:g,equal:z,reverse:B,chunk:j,difference:C,mapToObject:D,pure:E,sort:F,range:i}}),h("5t",setTimeout),g("51",["3x","4z","5t"],function(a,b,c){var d=function(e){var f=b.none(),g=[],h=function(a){return d(function(b){i(function(c){b(a(c))})})},i=function(a){k()?m(a):g.push(a)},j=function(a){f=b.some(a),l(g),g=[]},k=function(){return f.isSome()},l=function(b){a.each(b,m)},m=function(a){f.each(function(b){c(function(){a(b)},0)})};return e(j),{get:i,map:h,isReady:k}},e=function(a){return d(function(b){b(a)})};return{nu:d,pure:e}}),g("52",["3y","5t"],function(a,b){var c=function(c){return function(){var d=a.prototype.slice.call(arguments),e=this;b(function(){c.apply(e,d)},0)}};return{bounce:c}}),g("40",["51","52"],function(a,b){var c=function(d){var e=function(a){d(b.bounce(a))},f=function(a){return c(function(b){e(function(c){var d=a(c);b(d)})})},g=function(a){return c(function(b){e(function(c){a(c).get(b)})})},h=function(a){return c(function(b){e(function(c){a.get(b)})})},i=function(){return a.nu(e)};return{map:f,bind:g,anonBind:h,toLazy:i,get:e}},d=function(a){return c(function(b){b(a)})};return{nu:c,pure:d}}),g("53",["3x"],function(a){var b=function(b,c){return c(function(c){var d=[],e=0,f=function(a){return function(f){d[a]=f,e++,e>=b.length&&c(d)}};0===b.length?c([]):a.each(b,function(a,b){a.get(f(b))})})};return{par:b}}),g("41",["3x","40","53"],function(a,b,c){var d=function(a){return c.par(a,b.nu)},e=function(b,c){var e=a.map(b,c);return d(e)},f=function(a,b){return function(c){return b(c).bind(a)}};return{par:d,mapM:e,compose:f}}),g("42",["1m","4z"],function(a,b){var c=function(d){var e=function(a){return d===a},f=function(a){return c(d)},g=function(a){return c(d)},h=function(a){return c(a(d))},i=function(a){a(d)},j=function(a){return a(d)},k=function(a,b){return b(d)},l=function(a){return a(d)},m=function(a){return a(d)},n=function(){return b.some(d)};return{is:e,isValue:a.constant(!0),isError:a.constant(!1),getOr:a.constant(d),getOrThunk:a.constant(d),getOrDie:a.constant(d),or:f,orThunk:g,fold:k,map:h,each:i,bind:j,exists:l,forall:m,toOption:n}},d=function(c){var e=function(a){return a()},f=function(){return a.die(c)()},g=function(a){return a},h=function(a){return a()},i=function(a){return d(c)},j=function(a){return d(c)},k=function(a,b){return a(c)};return{is:a.constant(!1),isValue:a.constant(!1),isError:a.constant(!0),getOr:a.identity,getOrThunk:e,getOrDie:f,or:g,orThunk:h,fold:k,map:i,each:a.noop,bind:j,exists:a.constant(!1),forall:a.constant(!0),toOption:b.none}};return{value:c,error:d}}),g("1i",["3x","1m","40","41","42","5","9"],function(a,b,c,d,e,f,g){"use strict";return function(h,i){function j(a){h.getElementsByTagName("head")[0].appendChild(a)}function k(a,b,c){function d(){for(var a=t.passed,b=a.length;b--;)a[b]();t.status=2,t.passed=[],t.failed=[]}function e(){for(var a=t.failed,b=a.length;b--;)a[b]();t.status=3,t.passed=[],t.failed=[]}function i(){var a=navigator.userAgent.match(/WebKit\/(\d*)/);return!!(a&&a[1]<536)}function k(a,b){a()||((new Date).getTime()-s<l?f.setTimeout(b):e())}function o(){k(function(){for(var a,b,c=h.styleSheets,e=c.length;e--;)if(a=c[e],b=a.ownerNode?a.ownerNode:a.owningElement,b&&b.id===q.id)return d(),!0},o)}function p(){k(function(){try{var a=r.sheet.cssRules;return d(),!!a}catch(a){}},p)}var q,r,s,t;if(a=g._addCacheSuffix(a),n[a]?t=n[a]:(t={passed:[],failed:[]},n[a]=t),b&&t.passed.push(b),c&&t.failed.push(c),1!=t.status){if(2==t.status)return void d();if(3==t.status)return void e();if(t.status=1,q=h.createElement("link"),q.rel="stylesheet",q.type="text/css",q.id="u"+m++,q.async=!1,q.defer=!1,s=(new Date).getTime(),"onload"in q&&!i())q.onload=o,q.onerror=e;else{if(navigator.userAgent.indexOf("Firefox")>0)return r=h.createElement("style"), +r.textContent='@import "'+a+'"',p(),void j(r);o()}j(q),q.href=a}}var l,m=0,n={};i=i||{},l=i.maxLoadTime||5e3;var o=function(a){return c.nu(function(c){k(a,b.compose(c,b.constant(e.value(a))),b.compose(c,b.constant(e.error(a))))})},p=function(a){return a.fold(b.identity,b.identity)},q=function(b,c,e){d.par(a.map(b,o)).get(function(b){var d=a.partition(b,function(a){return a.isValue()});d.fail.length>0?e(d.fail.map(p)):c(d.pass.map(p))})};return{load:k,loadAll:q}}}),g("j",["9"],function(a){function b(b,c){return b=a.trim(b),b?b.split(c||" "):[]}function c(a){function c(a,c,d){function e(a,b){var c,d,e={};for(c=0,d=a.length;c<d;c++)e[a[c]]=b||{};return e}var h,i,j;for(d=d||[],c=c||"","string"==typeof d&&(d=b(d)),a=b(a),h=a.length;h--;)i=b([g,c].join(" ")),j={attributes:e(i),attributesOrder:i,children:e(d,f)},n[a[h]]=j}function d(a,c){var d,e,f,g;for(a=b(a),d=a.length,c=b(c);d--;)for(e=n[a[d]],f=0,g=c.length;f<g;f++)e.attributes[c[f]]={},e.attributesOrder.push(c[f])}var g,i,j,k,l,m,n={};return e[a]?e[a]:(g="id accesskey class dir lang style tabindex title role",i="address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul",j="a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd label map noscript object q s samp script select small span strong sub sup textarea u var #text #comment","html4"!=a&&(g+=" contenteditable contextmenu draggable dropzone hidden spellcheck translate",i+=" article aside details dialog figure header footer hgroup section nav",j+=" audio canvas command datalist mark meter output picture progress time wbr video ruby bdi keygen"),"html5-strict"!=a&&(g+=" xml:lang",m="acronym applet basefont big font strike tt",j=[j,m].join(" "),h(b(m),function(a){c(a,"",j)}),l="center dir isindex noframes",i=[i,l].join(" "),k=[i,j].join(" "),h(b(l),function(a){c(a,"",k)})),k=k||[i,j].join(" "),c("html","manifest","head body"),c("head","","base command link meta noscript script style title"),c("title hr noscript br"),c("base","href target"),c("link","href rel media hreflang type sizes hreflang"),c("meta","name http-equiv content charset"),c("style","media type scoped"),c("script","src async defer type charset"),c("body","onafterprint onbeforeprint onbeforeunload onblur onerror onfocus onhashchange onload onmessage onoffline ononline onpagehide onpageshow onpopstate onresize onscroll onstorage onunload",k),c("address dt dd div caption","",k),c("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn","",j),c("blockquote","cite",k),c("ol","reversed start type","li"),c("ul","","li"),c("li","value",k),c("dl","","dt dd"),c("a","href target rel media hreflang type",j),c("q","cite",j),c("ins del","cite datetime",k),c("img","src sizes srcset alt usemap ismap width height"),c("iframe","src name width height",k),c("embed","src type width height"),c("object","data type typemustmatch name usemap form width height",[k,"param"].join(" ")),c("param","name value"),c("map","name",[k,"area"].join(" ")),c("area","alt coords shape href target rel media hreflang type"),c("table","border","caption colgroup thead tfoot tbody tr"+("html4"==a?" col":"")),c("colgroup","span","col"),c("col","span"),c("tbody thead tfoot","","tr"),c("tr","","td th"),c("td","colspan rowspan headers",k),c("th","colspan rowspan headers scope abbr",k),c("form","accept-charset action autocomplete enctype method name novalidate target",k),c("fieldset","disabled form name",[k,"legend"].join(" ")),c("label","form for",j),c("input","accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"),c("button","disabled form formaction formenctype formmethod formnovalidate formtarget name type value","html4"==a?k:j),c("select","disabled form multiple name required size","option optgroup"),c("optgroup","disabled label","option"),c("option","disabled label selected value"),c("textarea","cols dirname disabled form maxlength name readonly required rows wrap"),c("menu","type label",[k,"li"].join(" ")),c("noscript","",k),"html4"!=a&&(c("wbr"),c("ruby","",[j,"rt rp"].join(" ")),c("figcaption","",k),c("mark rt rp summary bdi","",j),c("canvas","width height",k),c("video","src crossorigin poster preload autoplay mediagroup loop muted controls width height buffered",[k,"track source"].join(" ")),c("audio","src crossorigin preload autoplay mediagroup loop muted controls buffered volume",[k,"track source"].join(" ")),c("picture","","img source"),c("source","src srcset type media sizes"),c("track","kind src srclang label default"),c("datalist","",[j,"option"].join(" ")),c("article section nav aside header footer","",k),c("hgroup","","h1 h2 h3 h4 h5 h6"),c("figure","",[k,"figcaption"].join(" ")),c("time","datetime",j),c("dialog","open",k),c("command","type label icon disabled checked radiogroup command"),c("output","for form name",j),c("progress","value max",j),c("meter","value min max low high optimum",j),c("details","open",[k,"summary"].join(" ")),c("keygen","autofocus challenge disabled form keytype name")),"html5-strict"!=a&&(d("script","language xml:space"),d("style","xml:space"),d("object","declare classid code codebase codetype archive standby align border hspace vspace"),d("embed","align name hspace vspace"),d("param","valuetype type"),d("a","charset name rev shape coords"),d("br","clear"),d("applet","codebase archive code object alt name width height align hspace vspace"),d("img","name longdesc align border hspace vspace"),d("iframe","longdesc frameborder marginwidth marginheight scrolling align"),d("font basefont","size color face"),d("input","usemap align"),d("select","onchange"),d("textarea"),d("h1 h2 h3 h4 h5 h6 div p legend caption","align"),d("ul","type compact"),d("li","type"),d("ol dl menu dir","compact"),d("pre","width xml:space"),d("hr","align noshade size width"),d("isindex","prompt"),d("table","summary width frame rules cellspacing cellpadding align bgcolor"),d("col","width align char charoff valign"),d("colgroup","width align char charoff valign"),d("thead","align char charoff valign"),d("tr","align char charoff valign bgcolor"),d("th","axis align char charoff valign nowrap bgcolor width height"),d("form","accept"),d("td","abbr axis scope align char charoff valign nowrap bgcolor width height"),d("tfoot","align char charoff valign"),d("tbody","align char charoff valign"),d("area","nohref"),d("body","background bgcolor text link vlink alink")),"html4"!=a&&(d("input button select textarea","autofocus"),d("input textarea","placeholder"),d("a","download"),d("link script img","crossorigin"),d("iframe","sandbox seamless allowfullscreen")),h(b("a form meter progress dfn"),function(a){n[a]&&delete n[a].children[a]}),delete n.caption.children.table,delete n.script,e[a]=n,n)}function d(a,b){var c;return a&&(c={},"string"==typeof a&&(a={"*":a}),h(a,function(a,d){c[d]=c[d.toUpperCase()]="map"==b?g(a,/[, ]/):j(a,/[, ]/)})),c}var e={},f={},g=a.makeMap,h=a.each,i=a.extend,j=a.explode,k=a.inArray;return function(a){function f(b,c,d){var f=a[b];return f?f=g(f,/[, ]/,g(f.toUpperCase(),/[, ]/)):(f=e[b],f||(f=g(c," ",g(c.toUpperCase()," ")),f=i(f,d),e[b]=f)),f}function l(a){return new RegExp("^"+a.replace(/([?+*])/g,".$1")+"$")}function m(a){var c,d,e,f,h,i,j,m,n,o,p,q,r,s,t,u,v,w,x,y=/^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,z=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,A=/[*?+]/;if(a)for(a=b(a,","),F["@"]&&(u=F["@"].attributes,v=F["@"].attributesOrder),c=0,d=a.length;c<d;c++)if(h=y.exec(a[c])){if(s=h[1],n=h[2],t=h[3],m=h[5],q={},r=[],i={attributes:q,attributesOrder:r},"#"===s&&(i.paddEmpty=!0),"-"===s&&(i.removeEmpty=!0),"!"===h[4]&&(i.removeEmptyAttrs=!0),u){for(w in u)q[w]=u[w];r.push.apply(r,v)}if(m)for(m=b(m,"|"),e=0,f=m.length;e<f;e++)if(h=z.exec(m[e])){if(j={},p=h[1],o=h[2].replace(/::/g,":"),s=h[3],x=h[4],"!"===p&&(i.attributesRequired=i.attributesRequired||[],i.attributesRequired.push(o),j.required=!0),"-"===p){delete q[o],r.splice(k(r,o),1);continue}s&&("="===s&&(i.attributesDefault=i.attributesDefault||[],i.attributesDefault.push({name:o,value:x}),j.defaultValue=x),":"===s&&(i.attributesForced=i.attributesForced||[],i.attributesForced.push({name:o,value:x}),j.forcedValue=x),"<"===s&&(j.validValues=g(x,"?"))),A.test(o)?(i.attributePatterns=i.attributePatterns||[],j.pattern=l(o),i.attributePatterns.push(j)):(q[o]||r.push(o),q[o]=j)}u||"@"!=n||(u=q,v=r),t&&(i.outputName=n,F[t]=i),A.test(n)?(i.pattern=l(n),H.push(i)):F[n]=i}}function n(a){F={},H=[],m(a),h(t,function(a,b){G[b]=a.children})}function o(a){var c=/^(~)?(.+)$/;a&&(e.text_block_elements=e.block_elements=null,h(b(a,","),function(a){var b=c.exec(a),d="~"===b[1],e=d?"span":"div",f=b[2];if(G[f]=G[e],I[f]=e,d||(z[f.toUpperCase()]={},z[f]={}),!F[f]){var g=F[e];g=i({},g),delete g.removeEmptyAttrs,delete g.removeEmpty,F[f]=g}h(G,function(a,b){a[e]&&(G[b]=a=i({},G[b]),a[f]=a[e])})}))}function p(c){var d=/^([+\-]?)(\w+)\[([^\]]+)\]$/;e[a.schema]=null,c&&h(b(c,","),function(a){var c,e,f=d.exec(a);f&&(e=f[1],c=e?G[f[2]]:G[f[2]]={"#comment":{}},c=G[f[2]],h(b(f[3],"|"),function(a){"-"===e?delete c[a]:c[a]={}}))})}function q(a){var b,c=F[a];if(c)return c;for(b=H.length;b--;)if(c=H[b],c.pattern.test(a))return c}var r,s,t,u,v,w,x,y,z,A,B,C,D,E=this,F={},G={},H=[],I={},J={};a=a||{},t=c(a.schema),a.verify_html===!1&&(a.valid_elements="*[*]"),r=d(a.valid_styles),s=d(a.invalid_styles,"map"),y=d(a.valid_classes,"map"),u=f("whitespace_elements","pre script noscript style textarea video audio iframe object code"),v=f("self_closing_elements","colgroup dd dt li option p td tfoot th thead tr"),w=f("short_ended_elements","area base basefont br col frame hr img input isindex link meta param embed source wbr track"),x=f("boolean_attributes","checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls"),A=f("non_empty_elements","td th iframe video audio object script pre code",w),B=f("move_caret_before_on_enter_elements","table",A),C=f("text_block_elements","h1 h2 h3 h4 h5 h6 p div address pre form blockquote center dir fieldset header footer article section hgroup aside nav figure"),z=f("block_elements","hr table tbody thead tfoot th tr td li ol ul caption dl dt dd noscript menu isindex option datalist select optgroup figcaption",C),D=f("text_inline_elements","span strong b em i font strike u var cite dfn code mark q sup sub samp"),h((a.special||"script noscript noframes noembed title style textarea xmp").split(" "),function(a){J[a]=new RegExp("</"+a+"[^>]*>","gi")}),a.valid_elements?n(a.valid_elements):(h(t,function(a,b){F[b]={attributes:a.attributes,attributesOrder:a.attributesOrder},G[b]=a.children}),"html5"!=a.schema&&h(b("strong/b em/i"),function(a){a=b(a,"/"),F[a[1]].outputName=a[0]}),h(b("ol ul sub sup blockquote span font a table tbody tr strong em b i"),function(a){F[a]&&(F[a].removeEmpty=!0)}),h(b("p h1 h2 h3 h4 h5 h6 th td pre div address caption"),function(a){F[a].paddEmpty=!0}),h(b("span"),function(a){F[a].removeEmptyAttrs=!0})),o(a.custom_elements),p(a.valid_children),m(a.extended_valid_elements),p("+ol[ul|ol],+ul[ul|ol]"),h({dd:"dl",dt:"dl",li:"ul ol",td:"tr",th:"tr",tr:"tbody thead tfoot",tbody:"table",thead:"table",tfoot:"table",legend:"fieldset",area:"map",param:"video audio object"},function(a,c){F[c]&&(F[c].parentsRequired=b(a))}),a.invalid_elements&&h(j(a.invalid_elements),function(a){F[a]&&delete F[a]}),q("span")||m("span[!data-mce-type|*]"),E.children=G,E.getValidStyles=function(){return r},E.getInvalidStyles=function(){return s},E.getValidClasses=function(){return y},E.getBoolAttrs=function(){return x},E.getBlockElements=function(){return z},E.getTextBlockElements=function(){return C},E.getTextInlineElements=function(){return D},E.getShortEndedElements=function(){return w},E.getSelfClosingElements=function(){return v},E.getNonEmptyElements=function(){return A},E.getMoveCaretBeforeOnEnterElements=function(){return B},E.getWhiteSpaceElements=function(){return u},E.getSpecialElements=function(){return J},E.isValidChild=function(a,b){var c=G[a.toLowerCase()];return!(!c||!c[b.toLowerCase()])},E.isValid=function(a,b){var c,d,e=q(a);if(e){if(!b)return!0;if(e.attributes[b])return!0;if(c=e.attributePatterns)for(d=c.length;d--;)if(c[d].pattern.test(a))return!0}return!1},E.getElementRule=q,E.getCustomElements=function(){return I},E.addValidElements=m,E.setValidElements=n,E.addCustomElements=o,E.addValidChildren=p,E.elements=F}}),g("e",["a","7","1h","8","1i","c","6","d","j","b","9"],function(a,b,c,d,e,f,g,h,i,j,k){function l(a,b){var c,d={},e=b.keep_values;return c={set:function(c,d,e){b.url_converter&&(d=b.url_converter.call(b.url_converter_scope||a,d,e,c[0])),c.attr("data-mce-"+e,d).attr(e,d)},get:function(a,b){return a.attr("data-mce-"+b)||a.attr(b)}},d={style:{set:function(a,b){return null!==b&&"object"==typeof b?void a.css(b):(e&&a.attr("data-mce-style",b),void a.attr("style",b))},get:function(b){var c=b.attr("data-mce-style")||b.attr("style");return c=a.serializeStyle(a.parseStyle(c),b[0].nodeName)}}},e&&(d.href=d.src=c),d}function m(a,b){var c=b.attr("style");c=a.serializeStyle(a.parseStyle(c),b[0].nodeName),c||(c=null),b.attr("data-mce-style",c)}function n(a,b){var c,d,e=0;if(a)for(c=a.nodeType,a=a.previousSibling;a;a=a.previousSibling)d=a.nodeType,(!b||3!=d||d!=c&&a.nodeValue.length)&&(e++,c=d);return e}function o(c,d){var f,g=this;g.doc=c,g.win=window,g.files={},g.counter=0,g.stdMode=!t||c.documentMode>=8,g.boxModel=!t||"CSS1Compat"==c.compatMode||g.stdMode,g.styleSheetLoader=new e(c),g.boundEvents=[],g.settings=d=d||{},g.schema=d.schema?d.schema:new i({}),g.styles=new j({url_converter:d.url_converter,url_converter_scope:d.url_converter_scope},d.schema),g.fixDoc(c),g.events=d.ownEvents?new b(d.proxy):b.Event,g.attrHooks=l(g,d),f=d.schema?d.schema.getBlockElements():{},g.$=a.overrideDefaults(function(){return{context:c,element:g.getRoot()}}),g.isBlock=function(a){if(!a)return!1;var b=a.nodeType;return b?!(1!==b||!f[a.nodeName]):!!f[a]}}var p=k.each,q=k.is,r=k.grep,s=k.trim,t=g.ie,u=/^([a-z0-9],?)+$/i,v=/^[ \t\r\n]*$/;return o.prototype={$$:function(a){return"string"==typeof a&&(a=this.get(a)),this.$(a)},root:null,fixDoc:function(a){var b,c=this.settings;if(t&&c.schema){"abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video".replace(/\w+/g,function(b){a.createElement(b)});for(b in c.schema.getCustomElements())a.createElement(b)}},clone:function(a,b){var c,d,e=this;return!t||1!==a.nodeType||b?a.cloneNode(b):(d=e.doc,b?c.firstChild:(c=d.createElement(a.nodeName),p(e.getAttribs(a),function(b){e.setAttrib(c,b.nodeName,e.getAttrib(a,b.nodeName))}),c))},getRoot:function(){var a=this;return a.settings.root_element||a.doc.body},getViewPort:function(a){var b,c;return a=a?a:this.win,b=a.document,c=this.boxModel?b.documentElement:b.body,{x:a.pageXOffset||c.scrollLeft,y:a.pageYOffset||c.scrollTop,w:a.innerWidth||c.clientWidth,h:a.innerHeight||c.clientHeight}},getRect:function(a){var b,c,d=this;return a=d.get(a),b=d.getPos(a),c=d.getSize(a),{x:b.x,y:b.y,w:c.w,h:c.h}},getSize:function(a){var b,c,d=this;return a=d.get(a),b=d.getStyle(a,"width"),c=d.getStyle(a,"height"),b.indexOf("px")===-1&&(b=0),c.indexOf("px")===-1&&(c=0),{w:parseInt(b,10)||a.offsetWidth||a.clientWidth,h:parseInt(c,10)||a.offsetHeight||a.clientHeight}},getParent:function(a,b,c){return this.getParents(a,b,c,!1)},getParents:function(a,b,c,d){var e,f=this,g=[];for(a=f.get(a),d=void 0===d,c=c||("BODY"!=f.getRoot().nodeName?f.getRoot().parentNode:null),q(b,"string")&&(e=b,b="*"===b?function(a){return 1==a.nodeType}:function(a){return f.is(a,e)});a&&a!=c&&a.nodeType&&9!==a.nodeType;){if(!b||b(a)){if(!d)return a;g.push(a)}a=a.parentNode}return d?g:null},get:function(a){var b;return a&&this.doc&&"string"==typeof a&&(b=a,a=this.doc.getElementById(a),a&&a.id!==b)?this.doc.getElementsByName(b)[1]:a},getNext:function(a,b){return this._findSib(a,b,"nextSibling")},getPrev:function(a,b){return this._findSib(a,b,"previousSibling")},select:function(a,b){var c=this;return d(a,c.get(b)||c.settings.root_element||c.doc,[])},is:function(a,b){var c;if(!a)return!1;if(void 0===a.length){if("*"===b)return 1==a.nodeType;if(u.test(b)){for(b=b.toLowerCase().split(/,/),a=a.nodeName.toLowerCase(),c=b.length-1;c>=0;c--)if(b[c]==a)return!0;return!1}}if(a.nodeType&&1!=a.nodeType)return!1;var e=a.nodeType?[a]:a;return d(b,e[0].ownerDocument||e[0],null,e).length>0},add:function(a,b,c,d,e){var f=this;return this.run(a,function(a){var g;return g=q(b,"string")?f.doc.createElement(b):b,f.setAttribs(g,c),d&&(d.nodeType?g.appendChild(d):f.setHTML(g,d)),e?g:a.appendChild(g)})},create:function(a,b,c){return this.add(this.doc.createElement(a),a,b,c,1)},createHTML:function(a,b,c){var d,e="";e+="<"+a;for(d in b)b.hasOwnProperty(d)&&null!==b[d]&&"undefined"!=typeof b[d]&&(e+=" "+d+'="'+this.encode(b[d])+'"');return"undefined"!=typeof c?e+">"+c+"</"+a+">":e+" />"},createFragment:function(a){var b,c,d,e=this.doc;for(d=e.createElement("div"),b=e.createDocumentFragment(),a&&(d.innerHTML=a);c=d.firstChild;)b.appendChild(c);return b},remove:function(a,b){return a=this.$$(a),b?a.each(function(){for(var a;a=this.firstChild;)3==a.nodeType&&0===a.data.length?this.removeChild(a):this.parentNode.insertBefore(a,this)}).remove():a.remove(),a.length>1?a.toArray():a[0]},setStyle:function(a,b,c){a=this.$$(a).css(b,c),this.settings.update_styles&&m(this,a)},getStyle:function(a,b,c){return a=this.$$(a),c?a.css(b):(b=b.replace(/-(\D)/g,function(a,b){return b.toUpperCase()}),"float"==b&&(b=g.ie&&g.ie<12?"styleFloat":"cssFloat"),a[0]&&a[0].style?a[0].style[b]:void 0)},setStyles:function(a,b){a=this.$$(a).css(b),this.settings.update_styles&&m(this,a)},removeAllAttribs:function(a){return this.run(a,function(a){var b,c=a.attributes;for(b=c.length-1;b>=0;b--)a.removeAttributeNode(c.item(b))})},setAttrib:function(a,b,c){var d,e,f=this,g=f.settings;""===c&&(c=null),a=f.$$(a),d=a.attr(b),a.length&&(e=f.attrHooks[b],e&&e.set?e.set(a,c,b):a.attr(b,c),d!=c&&g.onSetAttrib&&g.onSetAttrib({attrElm:a,attrName:b,attrValue:c}))},setAttribs:function(a,b){var c=this;c.$$(a).each(function(a,d){p(b,function(a,b){c.setAttrib(d,b,a)})})},getAttrib:function(a,b,c){var d,e,f=this;return a=f.$$(a),a.length&&(d=f.attrHooks[b],e=d&&d.get?d.get(a,b):a.attr(b)),"undefined"==typeof e&&(e=c||""),e},getPos:function(b,c){var d,e,f=this,g=0,h=0,i=f.doc,j=i.body;if(b=f.get(b),c=c||j,b){if(c===j&&b.getBoundingClientRect&&"static"===a(j).css("position"))return e=b.getBoundingClientRect(),c=f.boxModel?i.documentElement:j,g=e.left+(i.documentElement.scrollLeft||j.scrollLeft)-c.clientLeft,h=e.top+(i.documentElement.scrollTop||j.scrollTop)-c.clientTop,{x:g,y:h};for(d=b;d&&d!=c&&d.nodeType;)g+=d.offsetLeft||0,h+=d.offsetTop||0,d=d.offsetParent;for(d=b.parentNode;d&&d!=c&&d.nodeType;)g-=d.scrollLeft||0,h-=d.scrollTop||0,d=d.parentNode}return{x:g,y:h}},parseStyle:function(a){return this.styles.parse(a)},serializeStyle:function(a,b){return this.styles.serialize(a,b)},addStyle:function(a){var b,c,d=this,e=d.doc;if(d!==o.DOM&&e===document){var f=o.DOM.addedStyles;if(f=f||[],f[a])return;f[a]=!0,o.DOM.addedStyles=f}c=e.getElementById("mceDefaultStyles"),c||(c=e.createElement("style"),c.id="mceDefaultStyles",c.type="text/css",b=e.getElementsByTagName("head")[0],b.firstChild?b.insertBefore(c,b.firstChild):b.appendChild(c)),c.styleSheet?c.styleSheet.cssText+=a:c.appendChild(e.createTextNode(a))},loadCSS:function(a){var b,c=this,d=c.doc;return c!==o.DOM&&d===document?void o.DOM.loadCSS(a):(a||(a=""),b=d.getElementsByTagName("head")[0],void p(a.split(","),function(a){var e;a=k._addCacheSuffix(a),c.files[a]||(c.files[a]=!0,e=c.create("link",{rel:"stylesheet",href:a}),t&&d.documentMode&&d.recalc&&(e.onload=function(){d.recalc&&d.recalc(),e.onload=null}),b.appendChild(e))}))},addClass:function(a,b){this.$$(a).addClass(b)},removeClass:function(a,b){this.toggleClass(a,b,!1)},hasClass:function(a,b){return this.$$(a).hasClass(b)},toggleClass:function(b,c,d){this.$$(b).toggleClass(c,d).each(function(){""===this.className&&a(this).attr("class",null)})},show:function(a){this.$$(a).show()},hide:function(a){this.$$(a).hide()},isHidden:function(a){return"none"==this.$$(a).css("display")},uniqueId:function(a){return(a?a:"mce_")+this.counter++},setHTML:function(b,c){b=this.$$(b),t?b.each(function(b,d){if(d.canHaveHTML!==!1){for(;d.firstChild;)d.removeChild(d.firstChild);try{d.innerHTML="<br>"+c,d.removeChild(d.firstChild)}catch(b){a("<div></div>").html("<br>"+c).contents().slice(1).appendTo(d)}return c}}):b.html(c)},getOuterHTML:function(b){return b=this.get(b),1==b.nodeType&&"outerHTML"in b?b.outerHTML:a("<div></div>").append(a(b).clone()).html()},setOuterHTML:function(b,c){var d=this;d.$$(b).each(function(){try{if("outerHTML"in this)return void(this.outerHTML=c)}catch(a){}d.remove(a(this).html(c),!0)})},decode:h.decode,encode:h.encodeAllRaw,insertAfter:function(a,b){return b=this.get(b),this.run(a,function(a){var c,d;return c=b.parentNode,d=b.nextSibling,d?c.insertBefore(a,d):c.appendChild(a),a})},replace:function(a,b,c){var d=this;return d.run(b,function(b){return q(b,"array")&&(a=a.cloneNode(!0)),c&&p(r(b.childNodes),function(b){a.appendChild(b)}),b.parentNode.replaceChild(a,b)})},rename:function(a,b){var c,d=this;return a.nodeName!=b.toUpperCase()&&(c=d.create(b),p(d.getAttribs(a),function(b){d.setAttrib(c,b.nodeName,d.getAttrib(a,b.nodeName))}),d.replace(c,a,1)),c||a},findCommonAncestor:function(a,b){for(var c,d=a;d;){for(c=b;c&&d!=c;)c=c.parentNode;if(d==c)break;d=d.parentNode}return!d&&a.ownerDocument?a.ownerDocument.documentElement:d},toHex:function(a){return this.styles.toHex(k.trim(a))},run:function(a,b,c){var d,e=this;return"string"==typeof a&&(a=e.get(a)),!!a&&(c=c||this,a.nodeType||!a.length&&0!==a.length?b.call(c,a):(d=[],p(a,function(a,f){a&&("string"==typeof a&&(a=e.get(a)),d.push(b.call(c,a,f)))}),d))},getAttribs:function(a){var b;if(a=this.get(a),!a)return[];if(t){if(b=[],"OBJECT"==a.nodeName)return a.attributes;"OPTION"===a.nodeName&&this.getAttrib(a,"selected")&&b.push({specified:1,nodeName:"selected"});var c=/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;return a.cloneNode(!1).outerHTML.replace(c,"").replace(/[\w:\-]+/gi,function(a){b.push({specified:1,nodeName:a})}),b}return a.attributes},isEmpty:function(a,b){var c,d,e,g,h,i,j=this,k=0;if(a=a.firstChild){h=new f(a,a.parentNode),b=b||(j.schema?j.schema.getNonEmptyElements():null),g=j.schema?j.schema.getWhiteSpaceElements():{};do{if(e=a.nodeType,1===e){var l=a.getAttribute("data-mce-bogus");if(l){a=h.next("all"===l);continue}if(i=a.nodeName.toLowerCase(),b&&b[i]){if("br"===i){k++,a=h.next();continue}return!1}for(d=j.getAttribs(a),c=d.length;c--;)if(i=d[c].nodeName,"name"===i||"data-mce-bookmark"===i)return!1}if(8==e)return!1;if(3===e&&!v.test(a.nodeValue))return!1;if(3===e&&a.parentNode&&g[a.parentNode.nodeName]&&v.test(a.nodeValue))return!1;a=h.next()}while(a)}return k<=1},createRng:function(){var a=this.doc;return a.createRange?a.createRange():new c(this)},nodeIndex:n,split:function(a,b,c){function d(a){function b(a){var b=a.previousSibling&&"SPAN"==a.previousSibling.nodeName,c=a.nextSibling&&"SPAN"==a.nextSibling.nodeName;return b&&c}var c,e=a.childNodes,f=a.nodeType;if(1!=f||"bookmark"!=a.getAttribute("data-mce-type")){for(c=e.length-1;c>=0;c--)d(e[c]);if(9!=f){if(3==f&&a.nodeValue.length>0){var g=s(a.nodeValue).length;if(!h.isBlock(a.parentNode)||g>0||0===g&&b(a))return}else if(1==f&&(e=a.childNodes,1==e.length&&e[0]&&1==e[0].nodeType&&"bookmark"==e[0].getAttribute("data-mce-type")&&a.parentNode.insertBefore(e[0],a),e.length||/^(br|hr|input|img)$/i.test(a.nodeName)))return;h.remove(a)}return a}}var e,f,g,h=this,i=h.createRng();if(a&&b)return i.setStart(a.parentNode,h.nodeIndex(a)),i.setEnd(b.parentNode,h.nodeIndex(b)),e=i.extractContents(),i=h.createRng(),i.setStart(b.parentNode,h.nodeIndex(b)+1),i.setEnd(a.parentNode,h.nodeIndex(a)+1),f=i.extractContents(),g=a.parentNode,g.insertBefore(d(e),a),c?g.insertBefore(c,a):g.insertBefore(b,a),g.insertBefore(d(f),a),h.remove(a),c||b},bind:function(a,b,c,d){var e=this;if(k.isArray(a)){for(var f=a.length;f--;)a[f]=e.bind(a[f],b,c,d);return a}return!e.settings.collect||a!==e.doc&&a!==e.win||e.boundEvents.push([a,b,c,d]),e.events.bind(a,b,c,d||e)},unbind:function(a,b,c){var d,e=this;if(k.isArray(a)){for(d=a.length;d--;)a[d]=e.unbind(a[d],b,c);return a}if(e.boundEvents&&(a===e.doc||a===e.win))for(d=e.boundEvents.length;d--;){var f=e.boundEvents[d];a!=f[0]||b&&b!=f[1]||c&&c!=f[2]||this.events.unbind(f[0],f[1],f[2])}return this.events.unbind(a,b,c)},fire:function(a,b,c){return this.events.fire(a,b,c)},getContentEditable:function(a){var b;return a&&1==a.nodeType?(b=a.getAttribute("data-mce-contenteditable"),b&&"inherit"!==b?b:"inherit"!==a.contentEditable?a.contentEditable:null):null},getContentEditableParent:function(a){for(var b=this.getRoot(),c=null;a&&a!==b&&(c=this.getContentEditable(a),null===c);a=a.parentNode);return c},destroy:function(){var a=this;if(a.boundEvents){for(var b=a.boundEvents.length;b--;){var c=a.boundEvents[b];this.events.unbind(c[0],c[1],c[2])}a.boundEvents=null}d.setDocument&&d.setDocument(),a.win=a.doc=a.root=a.events=a.frag=null},isChildOf:function(a,b){for(;a;){if(b===a)return!0;a=a.parentNode}return!1},dumpRng:function(a){return"startContainer: "+a.startContainer.nodeName+", startOffset: "+a.startOffset+", endContainer: "+a.endContainer.nodeName+", endOffset: "+a.endOffset},_findSib:function(a,b,c){var d=this,e=b;if(a)for("string"==typeof e&&(e=function(a){return d.is(a,b)}),a=a[c];a;a=a[c])if(e(a))return a;return null}},o.DOM=new o(document),o.nodeIndex=n,o}),g("f",["e","9"],function(a,b){function c(){function a(a,c,e){function f(){k.remove(j),i&&(i.onreadystatechange=i.onload=i=null),c()}function h(){g(e)?e():"undefined"!=typeof console&&console.log&&console.log("Failed to load script: "+a)}var i,j,k=d;j=k.uniqueId(),i=document.createElement("script"),i.id=j,i.type="text/javascript",i.src=b._addCacheSuffix(a),"onreadystatechange"in i?i.onreadystatechange=function(){/loaded|complete/.test(i.readyState)&&f()}:i.onload=f,i.onerror=h,(document.getElementsByTagName("head")[0]||document.body).appendChild(i)}var c,h=0,i=1,j=2,k=3,l={},m=[],n={},o=[],p=0;this.isDone=function(a){return l[a]==j},this.markDone=function(a){l[a]=j},this.add=this.load=function(a,b,d,e){var f=l[a];f==c&&(m.push(a),l[a]=h),b&&(n[a]||(n[a]=[]),n[a].push({success:b,failure:e,scope:d||this}))},this.remove=function(a){delete l[a],delete n[a]},this.loadQueue=function(a,b,c){this.loadScripts(m,a,b,c)},this.loadScripts=function(b,d,h,m){function q(a,b){e(n[b],function(b){g(b[a])&&b[a].call(b.scope)}),n[b]=c}var r,s=[];o.push({success:d,failure:m,scope:h||this}),(r=function(){var c=f(b);b.length=0,e(c,function(b){return l[b]===j?void q("success",b):l[b]===k?void q("failure",b):void(l[b]!==i&&(l[b]=i,p++,a(b,function(){l[b]=j,p--,q("success",b),r()},function(){l[b]=k,p--,s.push(b),q("failure",b),r()})))}),p||(e(o,function(a){0===s.length?g(a.success)&&a.success.call(a.scope):g(a.failure)&&a.failure.call(a.scope,s)}),o.length=0)})()}}var d=a.DOM,e=b.each,f=b.grep,g=function(a){return"function"==typeof a};return c.ScriptLoader=new c,c}),g("g",["f","9"],function(a,b){function c(){var a=this;a.items=[],a.urls={},a.lookup={}}var d=b.each;return c.prototype={get:function(a){if(this.lookup[a])return this.lookup[a].instance},dependencies:function(a){var b;return this.lookup[a]&&(b=this.lookup[a].dependencies),b||[]},requireLangPack:function(b,d){var e=c.language;if(e&&c.languageLoad!==!1){if(d)if(d=","+d+",",d.indexOf(","+e.substr(0,2)+",")!=-1)e=e.substr(0,2);else if(d.indexOf(","+e+",")==-1)return;a.ScriptLoader.add(this.urls[b]+"/langs/"+e+".js")}},add:function(a,b,c){return this.items.push(b),this.lookup[a]={instance:b,dependencies:c},b},remove:function(a){delete this.urls[a],delete this.lookup[a]},createUrl:function(a,b){return"object"==typeof b?b:{prefix:a.prefix,resource:b,suffix:a.suffix}},addComponents:function(b,c){var e=this.urls[b];d(c,function(b){a.ScriptLoader.add(e+"/"+b)})},load:function(b,e,f,g,h){function i(){var c=j.dependencies(b);d(c,function(a){var b=j.createUrl(e,a);j.load(b.resource,b,void 0,void 0)}),f&&(g?f.call(g):f.call(a))}var j=this,k=e;j.urls[b]||("object"==typeof e&&(k=e.prefix+e.resource+e.suffix),0!==k.indexOf("/")&&k.indexOf("://")==-1&&(k=c.baseURL+"/"+k),j.urls[b]=k.substring(0,k.lastIndexOf("/")),j.lookup[b]?i():a.ScriptLoader.add(k,i,g,h))}},c.PluginManager=new c,c.ThemeManager=new c,c}),g("1j",[],function(){function a(a){return function(b){return!!b&&b.nodeType==a}}function b(a){return a=a.toLowerCase().split(" "),function(b){var c,d;if(b&&b.nodeType)for(d=b.nodeName.toLowerCase(),c=0;c<a.length;c++)if(d===a[c])return!0;return!1}}function c(a,b){return b=b.toLowerCase().split(" "),function(c){var d,e;if(i(c))for(d=0;d<b.length;d++)if(e=c.ownerDocument.defaultView.getComputedStyle(c,null).getPropertyValue(a),e===b[d])return!0;return!1}}function d(a,b){return function(c){return i(c)&&c[a]===b}}function e(a,b){return function(b){return i(b)&&b.hasAttribute(a)}}function f(a,b){return function(c){return i(c)&&c.getAttribute(a)===b}}function g(a){return i(a)&&a.hasAttribute("data-mce-bogus")}function h(a){return function(b){if(i(b)){if(b.contentEditable===a)return!0;if(b.getAttribute("data-mce-contenteditable")===a)return!0}return!1}}var i=a(1);return{isText:a(3),isElement:i,isComment:a(8),isBr:b("br"),isContentEditableTrue:h("true"),isContentEditableFalse:h("false"),matchNodeNames:b,hasPropValue:d,hasAttribute:e,hasAttributeValue:f,matchStyleValues:c,isBogus:g}}),g("1l",[],function(){var a="\ufeff",b=function(b){return b===a},c=function(b){return b.replace(new RegExp(a,"g"),"")};return{isZwsp:b,ZWSP:a,trim:c}}),g("1k",["1j","1l"],function(a,b){function c(a){return n(a)&&(a=a.parentNode),m(a)&&a.hasAttribute("data-mce-caret")}function d(a){return n(a)&&b.isZwsp(a.data)}function e(a){return c(a)||d(a)}function f(a,c){var d,f,g,h;if(d=a.ownerDocument,g=d.createTextNode(b.ZWSP),h=a.parentNode,c){if(f=a.previousSibling,n(f)){if(e(f))return f;if(j(f))return f.splitText(f.data.length-1)}h.insertBefore(g,a)}else{if(f=a.nextSibling,n(f)){if(e(f))return f;if(i(f))return f.splitText(1),f}a.nextSibling?h.insertBefore(g,a.nextSibling):h.appendChild(g)}return g}function g(){var a=document.createElement("br");return a.setAttribute("data-mce-bogus","1"),a}function h(a,b,c){var d,e,f;return d=b.ownerDocument,e=d.createElement(a),e.setAttribute("data-mce-caret",c?"before":"after"),e.setAttribute("data-mce-bogus","all"),e.appendChild(g()),f=b.parentNode,c?f.insertBefore(e,b):b.nextSibling?f.insertBefore(e,b.nextSibling):f.appendChild(e),e}function i(a){return n(a)&&a.data[0]==b.ZWSP}function j(a){return n(a)&&a.data[a.data.length-1]==b.ZWSP}function k(b){var c=b.getElementsByTagName("br"),d=c[c.length-1];a.isBogus(d)&&d.parentNode.removeChild(d)}function l(a){return a&&a.hasAttribute("data-mce-caret")?(k(a),a.removeAttribute("data-mce-caret"),a.removeAttribute("data-mce-bogus"),a.removeAttribute("style"),a.removeAttribute("_moz_abspos"),a):null}var m=a.isElement,n=a.isText,o=function(b){return b.firstChild!==b.lastChild||!a.isBr(b.firstChild)},p=function(c){if(a.isText(c)){var d=c.data;return d.length>0&&d.charAt(0)!==b.ZWSP&&c.insertData(0,b.ZWSP),c}return null},q=function(c){if(a.isText(c)){var d=c.data;return d.length>0&&d.charAt(d.length-1)!==b.ZWSP&&c.insertData(d.length,b.ZWSP),c}return null},r=function(c){return c&&a.isText(c.container())&&c.container().data.charAt(c.offset())===b.ZWSP},s=function(c){return c&&a.isText(c.container())&&c.container().data.charAt(c.offset()-1)===b.ZWSP};return{isCaretContainer:e,isCaretContainerBlock:c,isCaretContainerInline:d,showCaretContainerBlock:l,insertInline:f,prependInline:p,appendInline:q,isBeforeInline:r, +isAfterInline:s,insertBlock:h,hasContent:o,startsWithCaretContainer:i,endsWithCaretContainer:j}}),g("h",["9","c","1j","1h","1k"],function(a,b,c,d,e){function f(a){return q(a)||r(a)}function g(a,b){var c=a.childNodes;return b--,b>c.length-1?b=c.length-1:b<0&&(b=0),c[b]||a}function h(a,b,c){for(;a&&a!==b;){if(c(a))return a;a=a.parentNode}return null}function i(a,b,c){return null!==h(a,b,c)}function j(a,b,c){return i(a,b,function(a){return a.nodeName===c})}function k(a){return"_mce_caret"===a.id}function l(a,b){return s(a)&&i(a,b,k)===!1}function m(a){this.walk=function(b,c){function d(a){var b;return b=a[0],3===b.nodeType&&b===q&&r>=b.nodeValue.length&&a.splice(0,1),b=a[a.length-1],0===t&&a.length>0&&b===s&&3===b.nodeType&&a.splice(a.length-1,1),a}function e(a,b,c){for(var d=[];a&&a!=c;a=a[b])d.push(a);return d}function f(a,b){do{if(a.parentNode==b)return a;a=a.parentNode}while(a)}function h(a,b,f){var g=f?"nextSibling":"previousSibling";for(l=a,m=l.parentNode;l&&l!=b;l=m)m=l.parentNode,n=e(l==a?l:l[g],g),n.length&&(f||n.reverse(),c(d(n)))}var i,j,k,l,m,n,o,q=b.startContainer,r=b.startOffset,s=b.endContainer,t=b.endOffset;if(o=a.select("td[data-mce-selected],th[data-mce-selected]"),o.length>0)return void p(o,function(a){c([a])});if(1==q.nodeType&&q.hasChildNodes()&&(q=q.childNodes[r]),1==s.nodeType&&s.hasChildNodes()&&(s=g(s,t)),q==s)return c(d([q]));for(i=a.findCommonAncestor(q,s),l=q;l;l=l.parentNode){if(l===s)return h(q,i,!0);if(l===i)break}for(l=s;l;l=l.parentNode){if(l===q)return h(s,i);if(l===i)break}j=f(q,i)||q,k=f(s,i)||s,h(q,j,!0),n=e(j==q?j:j.nextSibling,"nextSibling",k==s?k.nextSibling:k),n.length&&c(d(n)),h(s,k)},this.split=function(a){function b(a,b){return a.splitText(b)}var c=a.startContainer,d=a.startOffset,e=a.endContainer,f=a.endOffset;return c==e&&3==c.nodeType?d>0&&d<c.nodeValue.length&&(e=b(c,d),c=e.previousSibling,f>d?(f-=d,c=e=b(e,f).previousSibling,f=e.nodeValue.length,d=0):f=0):(3==c.nodeType&&d>0&&d<c.nodeValue.length&&(c=b(c,d),d=0),3==e.nodeType&&f>0&&f<e.nodeValue.length&&(e=b(e,f).previousSibling,f=e.nodeValue.length)),{startContainer:c,startOffset:d,endContainer:e,endOffset:f}},this.normalize=function(c){function d(d){function g(a){return a&&/^(TD|TH|CAPTION)$/.test(a.nodeName)}function h(c,d){for(var e=new b(c,a.getParent(c.parentNode,a.isBlock)||w);c=e[d?"prev":"next"]();)if("BR"===c.nodeName)return!0}function i(a){for(;a&&a!=w;){if(r(a))return!0;a=a.parentNode}return!1}function k(a,b){return a.previousSibling&&a.previousSibling.nodeName==b}function m(c,d){var g,h,i;if(d=d||n,i=a.getParent(d.parentNode,a.isBlock)||w,c&&"BR"==d.nodeName&&v&&a.isEmpty(i))return n=d.parentNode,o=a.nodeIndex(d),void(f=!0);for(g=new b(d,i);q=g[c?"prev":"next"]();){if("false"===a.getContentEditableParent(q)||l(q,a.getRoot()))return;if(3===q.nodeType&&q.nodeValue.length>0)return void(j(q,w,"A")===!1&&(n=q,o=c?q.nodeValue.length:0,f=!0));if(a.isBlock(q)||t[q.nodeName.toLowerCase()])return;h=q}e&&h&&(n=h,f=!0,o=0)}var n,o,p,q,t,u,v,w=a.getRoot();if(n=c[(d?"start":"end")+"Container"],o=c[(d?"start":"end")+"Offset"],v=1==n.nodeType&&o===n.childNodes.length,t=a.schema.getNonEmptyElements(),u=d,!s(n)){if(1==n.nodeType&&o>n.childNodes.length-1&&(u=!1),9===n.nodeType&&(n=a.getRoot(),o=0),n===w){if(u&&(q=n.childNodes[o>0?o-1:0])){if(s(q))return;if(t[q.nodeName]||"TABLE"==q.nodeName)return}if(n.hasChildNodes()){if(o=Math.min(!u&&o>0?o-1:o,n.childNodes.length-1),n=n.childNodes[o],o=0,!e&&n===w.lastChild&&"TABLE"===n.nodeName)return;if(i(n)||s(n))return;if(n.hasChildNodes()&&!/TABLE/.test(n.nodeName)){q=n,p=new b(n,w);do{if(r(q)||s(q)){f=!1;break}if(3===q.nodeType&&q.nodeValue.length>0){o=u?0:q.nodeValue.length,n=q,f=!0;break}if(t[q.nodeName.toLowerCase()]&&!g(q)){o=a.nodeIndex(q),n=q.parentNode,"IMG"!=q.nodeName||u||o++,f=!0;break}}while(q=u?p.next():p.prev())}}}e&&(3===n.nodeType&&0===o&&m(!0),1===n.nodeType&&(q=n.childNodes[o],q||(q=n.childNodes[o-1]),!q||"BR"!==q.nodeName||k(q,"A")||h(q)||h(q,!0)||m(!0,q))),u&&!e&&3===n.nodeType&&o===n.nodeValue.length&&m(!1),f&&c["set"+(d?"Start":"End")](n,o)}}var e,f=!1;return e=c.collapsed,d(!0),e||d(),f&&e&&c.collapse(!0),f}}function n(b,c,d){var e,f,g;if(e=d.elementFromPoint(b,c),f=d.body.createTextRange(),e&&"HTML"!=e.tagName||(e=d.body),f.moveToElementText(e),g=a.toArray(f.getClientRects()),g=g.sort(function(a,b){return a=Math.abs(Math.max(a.top-c,a.bottom-c)),b=Math.abs(Math.max(b.top-c,b.bottom-c)),a-b}),g.length>0){c=(g[0].bottom+g[0].top)/2;try{return f.moveToPoint(b,c),f.collapse(!0),f}catch(a){}}return null}function o(a,b){var c=a&&a.parentElement?a.parentElement():null;return r(h(c,b,f))?null:a}var p=a.each,q=c.isContentEditableTrue,r=c.isContentEditableFalse,s=e.isCaretContainer;return m.compareRanges=function(a,b){if(a&&b){if(!a.item&&!a.duplicate)return a.startContainer==b.startContainer&&a.startOffset==b.startOffset;if(a.item&&b.item&&a.item(0)===b.item(0))return!0;if(a.isEqual&&b.isEqual&&b.isEqual(a))return!0}return!1},m.getCaretRangeFromPoint=function(a,b,c){var d,e;if(c.caretPositionFromPoint)e=c.caretPositionFromPoint(a,b),d=c.createRange(),d.setStart(e.offsetNode,e.offset),d.collapse(!0);else if(c.caretRangeFromPoint)d=c.caretRangeFromPoint(a,b);else if(c.body.createTextRange){d=c.body.createTextRange();try{d.moveToPoint(a,b),d.collapse(!0)}catch(e){d=n(a,b,c)}return o(d,c.body)}return d},m.getSelectedNode=function(a){var b=a.startContainer,c=a.startOffset;return b.hasChildNodes()&&a.endOffset==c+1?b.childNodes[c]:null},m.getNode=function(a,b){return 1==a.nodeType&&a.hasChildNodes()&&(b>=a.childNodes.length&&(b=a.childNodes.length-1),a=a.childNodes[b]),a},m}),g("i",[],function(){function a(a,b,c){var d,e,f=c?"lastChild":"firstChild",g=c?"prev":"next";if(a[f])return a[f];if(a!==b){if(d=a[g])return d;for(e=a.parent;e&&e!==b;e=e.parent)if(d=e[g])return d}}function b(a,b){this.name=a,this.type=b,1===b&&(this.attributes=[],this.attributes.map={})}var c=/^[ \t\r\n]*$/,d={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};return b.prototype={replace:function(a){var b=this;return a.parent&&a.remove(),b.insert(a,b),b.remove(),b},attr:function(a,b){var c,d,e,f=this;if("string"!=typeof a){for(d in a)f.attr(d,a[d]);return f}if(c=f.attributes){if(b!==e){if(null===b){if(a in c.map)for(delete c.map[a],d=c.length;d--;)if(c[d].name===a)return c=c.splice(d,1),f;return f}if(a in c.map){for(d=c.length;d--;)if(c[d].name===a){c[d].value=b;break}}else c.push({name:a,value:b});return c.map[a]=b,f}return c.map[a]}},clone:function(){var a,c,d,e,f,g=this,h=new b(g.name,g.type);if(d=g.attributes){for(f=[],f.map={},a=0,c=d.length;a<c;a++)e=d[a],"id"!==e.name&&(f[f.length]={name:e.name,value:e.value},f.map[e.name]=e.value);h.attributes=f}return h.value=g.value,h.shortEnded=g.shortEnded,h},wrap:function(a){var b=this;return b.parent.insert(a,b),a.append(b),b},unwrap:function(){var a,b,c=this;for(a=c.firstChild;a;)b=a.next,c.insert(a,c,!0),a=b;c.remove()},remove:function(){var a=this,b=a.parent,c=a.next,d=a.prev;return b&&(b.firstChild===a?(b.firstChild=c,c&&(c.prev=null)):d.next=c,b.lastChild===a?(b.lastChild=d,d&&(d.next=null)):c.prev=d,a.parent=a.next=a.prev=null),a},append:function(a){var b,c=this;return a.parent&&a.remove(),b=c.lastChild,b?(b.next=a,a.prev=b,c.lastChild=a):c.lastChild=c.firstChild=a,a.parent=c,a},insert:function(a,b,c){var d;return a.parent&&a.remove(),d=b.parent||this,c?(b===d.firstChild?d.firstChild=a:b.prev.next=a,a.prev=b.prev,a.next=b,b.prev=a):(b===d.lastChild?d.lastChild=a:b.next.prev=a,a.next=b.next,a.prev=b,b.next=a),a.parent=d,a},getAll:function(b){var c,d=this,e=[];for(c=d.firstChild;c;c=a(c,d))c.name===b&&e.push(c);return e},empty:function(){var b,c,d,e=this;if(e.firstChild){for(b=[],d=e.firstChild;d;d=a(d,e))b.push(d);for(c=b.length;c--;)d=b[c],d.parent=d.firstChild=d.lastChild=d.next=d.prev=null}return e.firstChild=e.lastChild=null,e},isEmpty:function(b,d){var e,f,g=this,h=g.firstChild;if(d=d||{},h)do{if(1===h.type){if(h.attributes.map["data-mce-bogus"])continue;if(b[h.name])return!1;for(e=h.attributes.length;e--;)if(f=h.attributes[e].name,"name"===f||0===f.indexOf("data-mce-bookmark"))return!1}if(8===h.type)return!1;if(3===h.type&&!c.test(h.value))return!1;if(3===h.type&&h.parent&&d[h.parent.name]&&c.test(h.value))return!1}while(h=a(h,g));return!0},walk:function(b){return a(this,null,b)}},b.create=function(a,c){var e,f;if(e=new b(a,d[a]||1),c)for(f in c)e.attr(f,c[f]);return e},b}),g("k",["j","d","9"],function(a,b,c){function d(a,b,c){var d,e,f,g,h=1;for(g=a.getShortEndedElements(),f=/<([!?\/])?([A-Za-z0-9\-_\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g,f.lastIndex=d=c;e=f.exec(b);){if(d=f.lastIndex,"/"===e[1])h--;else if(!e[1]){if(e[2]in g)continue;h++}if(0===h)break}return d}function e(e,i){function j(){}var k=this;e=e||{},k.schema=i=i||new a,e.fix_self_closing!==!1&&(e.fix_self_closing=!0),f("comment cdata text start end pi doctype".split(" "),function(a){a&&(k[a]=e[a]||j)}),k.parse=function(a){function f(a){var b,c;for(b=Q.length;b--&&Q[b].name!==a;);if(b>=0){for(c=Q.length-1;c>=b;c--)a=Q[c],a.valid&&O.end(a.name);Q.length=b}}function j(a,b,c,d,f){var h,i,j=/[\s\u0000-\u001F]+/g;if(b=b.toLowerCase(),c=b in u?b:S(c||d||f||""),w&&!r&&g(b)===!1){if(h=B[b],!h&&C){for(i=C.length;i--&&(h=C[i],!h.pattern.test(b)););i===-1&&(h=null)}if(!h)return;if(h.validValues&&!(c in h.validValues))return}if(T[b]&&!e.allow_script_urls){var k=c.replace(j,"");try{k=decodeURIComponent(k)}catch(a){k=unescape(k)}if(U.test(k))return;if(!e.allow_html_data_urls&&V.test(k)&&!/^data:image\//i.test(k))return}r&&(b in T||0===b.indexOf("on"))||(n.map[b]=c,n.push({name:b,value:c}))}var k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O=this,P=0,Q=[],R=0,S=b.decode,T=c.makeMap("src,href,data,background,formaction,poster"),U=/((java|vb)script|mhtml):/i,V=/^data:/i;for(J=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)>)|(?:([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)((?:\\s+[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*|\\/|\\s+)>))","g"),K=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g,t=i.getShortEndedElements(),I=e.self_closing_elements||i.getSelfClosingElements(),u=i.getBoolAttrs(),w=e.validate,s=e.remove_internals,N=e.fix_self_closing,L=i.getSpecialElements(),G=a+">";k=J.exec(G);){if(P<k.index&&O.text(S(a.substr(P,k.index-P))),l=k[6])l=l.toLowerCase(),":"===l.charAt(0)&&(l=l.substr(1)),f(l);else if(l=k[7]){if(k.index+k[0].length>a.length){O.text(S(a.substr(k.index))),P=k.index+k[0].length;continue}if(l=l.toLowerCase(),":"===l.charAt(0)&&(l=l.substr(1)),v=l in t,N&&I[l]&&Q.length>0&&Q[Q.length-1].name===l&&f(l),!w||(x=i.getElementRule(l))){if(y=!0,w&&(B=x.attributes,C=x.attributePatterns),(A=k[8])?(r=A.indexOf("data-mce-type")!==-1,r&&s&&(y=!1),n=[],n.map={},A.replace(K,j)):(n=[],n.map={}),w&&!r){if(D=x.attributesRequired,E=x.attributesDefault,F=x.attributesForced,H=x.removeEmptyAttrs,H&&!n.length&&(y=!1),F)for(o=F.length;o--;)z=F[o],q=z.name,M=z.value,"{$uid}"===M&&(M="mce_"+R++),n.map[q]=M,n.push({name:q,value:M});if(E)for(o=E.length;o--;)z=E[o],q=z.name,q in n.map||(M=z.value,"{$uid}"===M&&(M="mce_"+R++),n.map[q]=M,n.push({name:q,value:M}));if(D){for(o=D.length;o--&&!(D[o]in n.map););o===-1&&(y=!1)}if(z=n.map["data-mce-bogus"]){if("all"===z){P=d(i,a,J.lastIndex),J.lastIndex=P;continue}y=!1}}y&&O.start(l,n,v)}else y=!1;if(m=L[l]){m.lastIndex=P=k.index+k[0].length,(k=m.exec(a))?(y&&(p=a.substr(P,k.index-P)),P=k.index+k[0].length):(p=a.substr(P),P=a.length),y&&(p.length>0&&O.text(p,!0),O.end(l)),J.lastIndex=P;continue}v||(A&&A.indexOf("/")==A.length-1?y&&O.end(l):Q.push({name:l,valid:y}))}else(l=k[1])?(">"===l.charAt(0)&&(l=" "+l),e.allow_conditional_comments||"[if"!==l.substr(0,3).toLowerCase()||(l=" "+l),O.comment(l)):(l=k[2])?O.cdata(h(l)):(l=k[3])?O.doctype(l):(l=k[4])&&O.pi(l,k[5]);P=k.index+k[0].length}for(P<a.length&&O.text(S(a.substr(P))),o=Q.length-1;o>=0;o--)l=Q[o],l.valid&&O.end(l.name)}}var f=c.each,g=function(a){return 0===a.indexOf("data-")||0===a.indexOf("aria-")},h=function(a){return a.replace(/<!--|-->/g,"")};return e.findEndTag=d,e}),g("l",["i","j","k","9"],function(a,b,c,d){var e=d.makeMap,f=d.each,g=d.explode,h=d.extend,i=function(b,c){b.padd_empty_with_br?c.empty().append(new a("br","1")).shortEnded=!0:c.empty().append(new a("#text","3")).value="\xa0"},j=function(a,b){return a&&a.firstChild===a.lastChild&&a.firstChild.name===b};return function(k,l){function m(b){var c,d,f,g,h,i,k,m,o,p,q,r,s,t,u,v;for(r=e("tr,td,th,tbody,thead,tfoot,table"),p=l.getNonEmptyElements(),q=l.getWhiteSpaceElements(),s=l.getTextBlockElements(),t=l.getSpecialElements(),c=0;c<b.length;c++)if(d=b[c],d.parent&&!d.fixed)if(s[d.name]&&"li"==d.parent.name){for(u=d.next;u&&s[u.name];)u.name="li",u.fixed=!0,d.parent.insert(u,d.parent),u=u.next;d.unwrap(d)}else{for(g=[d],f=d.parent;f&&!l.isValidChild(f.name,d.name)&&!r[f.name];f=f.parent)g.push(f);if(f&&g.length>1){for(g.reverse(),h=i=n.filterNode(g[0].clone()),o=0;o<g.length-1;o++){for(l.isValidChild(i.name,g[o].name)?(k=n.filterNode(g[o].clone()),i.append(k)):k=i,m=g[o].firstChild;m&&m!=g[o+1];)v=m.next,k.append(m),m=v;i=k}h.isEmpty(p,q)?f.insert(d,g[0],!0):(f.insert(h,g[0],!0),f.insert(d,h)),f=g[0],(f.isEmpty(p,q)||j(f,"br"))&&f.empty().remove()}else if(d.parent){if("li"===d.name){if(u=d.prev,u&&("ul"===u.name||"ul"===u.name)){u.append(d);continue}if(u=d.next,u&&("ul"===u.name||"ul"===u.name)){u.insert(d,u.firstChild,!0);continue}d.wrap(n.filterNode(new a("ul",1)));continue}l.isValidChild(d.parent.name,"div")&&l.isValidChild("div",d.name)?d.wrap(n.filterNode(new a("div",1))):t[d.name]?d.empty().remove():d.unwrap()}}}var n=this,o={},p=[],q={},r={};k=k||{},k.validate=!("validate"in k)||k.validate,k.root_name=k.root_name||"body",n.schema=l=l||new b,n.filterNode=function(a){var b,c,d;c in o&&(d=q[c],d?d.push(a):q[c]=[a]),b=p.length;for(;b--;)c=p[b].name,c in a.attributes.map&&(d=r[c],d?d.push(a):r[c]=[a]);return a},n.addNodeFilter=function(a,b){f(g(a),function(a){var c=o[a];c||(o[a]=c=[]),c.push(b)})},n.addAttributeFilter=function(a,b){f(g(a),function(a){var c;for(c=0;c<p.length;c++)if(p[c].name===a)return void p[c].callbacks.push(b);p.push({name:a,callbacks:[b]})})},n.parse=function(b,d){function f(){function a(a){a&&(d=a.firstChild,d&&3==d.type&&(d.value=d.value.replace(E,"")),d=a.lastChild,d&&3==d.type&&(d.value=d.value.replace(G,"")))}var b,c,d=t.firstChild;if(l.isValidChild(t.name,M.toLowerCase())){for(;d;)b=d.next,3==d.type||1==d.type&&"p"!==d.name&&!D[d.name]&&!d.attr("data-mce-type")?c?c.append(d):(c=g(M,1),c.attr(k.forced_root_block_attrs),t.insert(c,d),c.append(d)):(a(c),c=null),d=b;a(c)}}function g(b,c){var d,e=new a(b,c);return b in o&&(d=q[b],d?d.push(e):q[b]=[e]),e}function j(a){var b,c,d,e,f=l.getBlockElements();for(b=a.prev;b&&3===b.type;){if(d=b.value.replace(G,""),d.length>0)return void(b.value=d);if(c=b.next){if(3==c.type&&c.value.length){b=b.prev;continue}if(!f[c.name]&&"script"!=c.name&&"style"!=c.name){b=b.prev;continue}}e=b.prev,b.remove(),b=e}}function n(a){var b,c={};for(b in a)"li"!==b&&"p"!=b&&(c[b]=a[b]);return c}var s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N=[];if(d=d||{},q={},r={},D=h(e("script,style,head,html,body,title,meta,param"),l.getBlockElements()),L=l.getNonEmptyElements(),K=l.children,C=k.validate,M="forced_root_block"in d?d.forced_root_block:k.forced_root_block,J=l.getWhiteSpaceElements(),E=/^[ \t\r\n]+/,G=/[ \t\r\n]+$/,H=/[ \t\r\n]+/g,I=/^[ \t\r\n]+$/,s=new c({validate:C,allow_script_urls:k.allow_script_urls,allow_conditional_comments:k.allow_conditional_comments,self_closing_elements:n(l.getSelfClosingElements()),cdata:function(a){u.append(g("#cdata",4)).value=a},text:function(a,b){var c;F||(a=a.replace(H," "),u.lastChild&&D[u.lastChild.name]&&(a=a.replace(E,""))),0!==a.length&&(c=g("#text",3),c.raw=!!b,u.append(c).value=a)},comment:function(a){u.append(g("#comment",8)).value=a},pi:function(a,b){u.append(g(a,7)).value=b,j(u)},doctype:function(a){var b;b=u.append(g("#doctype",10)),b.value=a,j(u)},start:function(a,b,c){var d,e,f,h,i;if(f=C?l.getElementRule(a):{}){for(d=g(f.outputName||a,1),d.attributes=b,d.shortEnded=c,u.append(d),i=K[u.name],i&&K[d.name]&&!i[d.name]&&N.push(d),e=p.length;e--;)h=p[e].name,h in b.map&&(A=r[h],A?A.push(d):r[h]=[d]);D[a]&&j(d),c||(u=d),!F&&J[a]&&(F=!0)}},end:function(a){var b,c,d,e,f;if(c=C?l.getElementRule(a):{}){if(D[a]&&!F){if(b=u.firstChild,b&&3===b.type)if(d=b.value.replace(E,""),d.length>0)b.value=d,b=b.next;else for(e=b.next,b.remove(),b=e;b&&3===b.type;)d=b.value,e=b.next,(0===d.length||I.test(d))&&(b.remove(),b=e),b=e;if(b=u.lastChild,b&&3===b.type)if(d=b.value.replace(G,""),d.length>0)b.value=d,b=b.prev;else for(e=b.prev,b.remove(),b=e;b&&3===b.type;)d=b.value,e=b.prev,(0===d.length||I.test(d))&&(b.remove(),b=e),b=e}if(F&&J[a]&&(F=!1),(c.removeEmpty||c.paddEmpty)&&u.isEmpty(L,J))if(c.paddEmpty)i(k,u);else if(!u.attributes.map.name&&!u.attributes.map.id)return f=u.parent,D[u.name]?u.empty().remove():u.unwrap(),void(u=f);u=u.parent}}},l),t=u=new a(d.context||k.root_name,11),s.parse(b),C&&N.length&&(d.context?d.invalid=!0:m(N)),M&&("body"==t.name||d.isRootContent)&&f(),!d.invalid){for(B in q){for(A=o[B],v=q[B],y=v.length;y--;)v[y].parent||v.splice(y,1);for(w=0,x=A.length;w<x;w++)A[w](v,B,d)}for(w=0,x=p.length;w<x;w++)if(A=p[w],A.name in r){for(v=r[A.name],y=v.length;y--;)v[y].parent||v.splice(y,1);for(y=0,z=A.callbacks.length;y<z;y++)A.callbacks[y](v,A.name,d)}}return t},k.remove_trailing_brs&&n.addNodeFilter("br",function(b){var c,d,e,f,g,j,m,n,o=b.length,p=h({},l.getBlockElements()),q=l.getNonEmptyElements(),r=l.getNonEmptyElements();for(p.body=1,c=0;c<o;c++)if(d=b[c],e=d.parent,p[d.parent.name]&&d===e.lastChild){for(g=d.prev;g;){if(j=g.name,"span"!==j||"bookmark"!==g.attr("data-mce-type")){if("br"!==j)break;if("br"===j){d=null;break}}g=g.prev}d&&(d.remove(),e.isEmpty(q,r)&&(m=l.getElementRule(e.name),m&&(m.removeEmpty?e.remove():m.paddEmpty&&i(k,e))))}else{for(f=d;e&&e.firstChild===f&&e.lastChild===f&&(f=e,!p[e.name]);)e=e.parent;f===e&&k.padd_empty_with_br!==!0&&(n=new a("#text",3),n.value="\xa0",d.replace(n))}}),n.addAttributeFilter("href",function(a){var b,c=a.length,e=function(a){var b=a.split(" ").filter(function(a){return a.length>0});return b.concat(["noopener"]).sort().join(" ")},f=function(a){var b=a?d.trim(a):"";return/\b(noopener)\b/g.test(b)?b:e(b)};if(!k.allow_unsafe_link_target)for(;c--;)b=a[c],"a"===b.name&&"_blank"===b.attr("target")&&b.attr("rel",f(b.attr("rel")))}),k.allow_html_in_named_anchor||n.addAttributeFilter("id,name",function(a){for(var b,c,d,e,f=a.length;f--;)if(e=a[f],"a"===e.name&&e.firstChild&&!e.attr("href")){d=e.parent,b=e.lastChild;do c=b.prev,d.insert(b,e),b=c;while(b)}}),k.fix_list_elements&&n.addNodeFilter("ul,ol",function(b){for(var c,d,e=b.length;e--;)if(c=b[e],d=c.parent,"ul"===d.name||"ol"===d.name)if(c.prev&&"li"===c.prev.name)c.prev.append(c);else{var f=new a("li",1);f.attr("style","list-style-type: none"),c.wrap(f)}}),k.validate&&l.getValidClasses()&&n.addAttributeFilter("class",function(a){for(var b,c,d,e,f,g,h,i=a.length,j=l.getValidClasses();i--;){for(b=a[i],c=b.attr("class").split(" "),f="",d=0;d<c.length;d++)e=c[d],h=!1,g=j["*"],g&&g[e]&&(h=!0),g=j[b.name],!h&&g&&g[e]&&(h=!0),h&&(f&&(f+=" "),f+=e);f.length||(f=null),b.attr("class",f)}})}}),g("m",["d","9"],function(a,b){var c=b.makeMap;return function(b){var d,e,f,g,h,i=[];return b=b||{},d=b.indent,e=c(b.indent_before||""),f=c(b.indent_after||""),g=a.getEncodeFunc(b.entity_encoding||"raw",b.entities),h="html"==b.element_format,{start:function(a,b,c){var j,k,l,m;if(d&&e[a]&&i.length>0&&(m=i[i.length-1],m.length>0&&"\n"!==m&&i.push("\n")),i.push("<",a),b)for(j=0,k=b.length;j<k;j++)l=b[j],i.push(" ",l.name,'="',g(l.value,!0),'"');!c||h?i[i.length]=">":i[i.length]=" />",c&&d&&f[a]&&i.length>0&&(m=i[i.length-1],m.length>0&&"\n"!==m&&i.push("\n"))},end:function(a){var b;i.push("</",a,">"),d&&f[a]&&i.length>0&&(b=i[i.length-1],b.length>0&&"\n"!==b&&i.push("\n"))},text:function(a,b){a.length>0&&(i[i.length]=b?a:g(a))},cdata:function(a){i.push("<![CDATA[",a,"]]>")},comment:function(a){i.push("<!--",a,"-->")},pi:function(a,b){b?i.push("<?",a," ",g(b),"?>"):i.push("<?",a,"?>"),d&&i.push("\n")},doctype:function(a){i.push("<!DOCTYPE",a,">",d?"\n":"")},reset:function(){i.length=0},getContent:function(){return i.join("").replace(/\n$/,"")}}}}),g("n",["m","j"],function(a,b){return function(c,d){var e=this,f=new a(c);c=c||{},c.validate=!("validate"in c)||c.validate,e.schema=d=d||new b,e.writer=f,e.serialize=function(a){function b(a){var c,h,i,j,k,l,m,n,o,p=e[a.type];if(p)p(a);else{if(c=a.name,h=a.shortEnded,i=a.attributes,g&&i&&i.length>1&&(l=[],l.map={},o=d.getElementRule(a.name))){for(m=0,n=o.attributesOrder.length;m<n;m++)j=o.attributesOrder[m],j in i.map&&(k=i.map[j],l.map[j]=k,l.push({name:j,value:k}));for(m=0,n=i.length;m<n;m++)j=i[m].name,j in l.map||(k=i.map[j],l.map[j]=k,l.push({name:j,value:k}));i=l}if(f.start(a.name,i,h),!h){if(a=a.firstChild)do b(a);while(a=a.next);f.end(c)}}}var e,g;return g=c.validate,e={3:function(a){f.text(a.value,a.raw)},8:function(a){f.comment(a.value)},7:function(a){f.pi(a.name,a.value)},10:function(a){f.doctype(a.value)},4:function(a){f.cdata(a.value)},11:function(a){if(a=a.firstChild)do b(a);while(a=a.next)}},f.reset(),1!=a.type||c.inner?e[11](a):b(a),f.getContent()}}}),g("o",["e","l","k","d","n","i","j","6","9","1l"],function(a,b,c,d,e,f,g,h,i,j){function k(a){function b(a){return a&&"br"===a.name}var c,d;c=a.lastChild,b(c)&&(d=c.prev,b(d)&&(c.remove(),d.remove()))}var l=i.each,m=i.trim,n=a.DOM;return function(a,f){function o(a){var b=new RegExp(["<span[^>]+data-mce-bogus[^>]+>[\u200b\ufeff]+<\\/span>","\\s?("+v.join("|")+')="[^"]+"'].join("|"),"gi");return a=j.trim(a.replace(b,""))}function p(a){var b,d,e,g,h,i=a,j=/<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g,k=f.schema;for(i=o(i),h=k.getShortEndedElements();g=j.exec(i);)d=j.lastIndex,e=g[0].length,b=h[g[1]]?d:c.findEndTag(k,i,d),i=i.substring(0,d-e)+i.substring(b),j.lastIndex=d-e;return i}function q(){return p(f.getBody().innerHTML)}function r(a){i.inArray(v,a)===-1&&(u.addAttributeFilter(a,function(a,b){for(var c=a.length;c--;)a[c].attr(b,null)}),v.push(a))}var s,t,u,v=["data-mce-selected"];return f&&(s=f.dom,t=f.schema),s=s||n,t=t||new g(a),a.entity_encoding=a.entity_encoding||"named",a.remove_trailing_brs=!("remove_trailing_brs"in a)||a.remove_trailing_brs,u=new b(a,t),u.addAttributeFilter("data-mce-tabindex",function(a,b){for(var c,d=a.length;d--;)c=a[d],c.attr("tabindex",c.attributes.map["data-mce-tabindex"]),c.attr(b,null)}),u.addAttributeFilter("src,href,style",function(b,c){for(var d,e,f,g=b.length,h="data-mce-"+c,i=a.url_converter,j=a.url_converter_scope;g--;)d=b[g],e=d.attributes.map[h],e!==f?(d.attr(c,e.length>0?e:null),d.attr(h,null)):(e=d.attributes.map[c],"style"===c?e=s.serializeStyle(s.parseStyle(e),d.name):i&&(e=i.call(j,e,c,d.name)),d.attr(c,e.length>0?e:null))}),u.addAttributeFilter("class",function(a){for(var b,c,d=a.length;d--;)b=a[d],c=b.attr("class"),c&&(c=b.attr("class").replace(/(?:^|\s)mce-item-\w+(?!\S)/g,""),b.attr("class",c.length>0?c:null))}),u.addAttributeFilter("data-mce-type",function(a,b,c){for(var d,e=a.length;e--;)d=a[e],"bookmark"!==d.attributes.map["data-mce-type"]||c.cleanup||d.remove()}),u.addNodeFilter("noscript",function(a){for(var b,c=a.length;c--;)b=a[c].firstChild,b&&(b.value=d.decode(b.value))}),u.addNodeFilter("script,style",function(a,b){function c(a){return a.replace(/(<!--\[CDATA\[|\]\]-->)/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi,"").replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g,"")}for(var d,e,f,g=a.length;g--;)d=a[g],e=d.firstChild?d.firstChild.value:"","script"===b?(f=d.attr("type"),f&&d.attr("type","mce-no/type"==f?null:f.replace(/^mce\-/,"")),e.length>0&&(d.firstChild.value="// <![CDATA[\n"+c(e)+"\n// ]]>")):e.length>0&&(d.firstChild.value="<!--\n"+c(e)+"\n-->")}),u.addNodeFilter("#comment",function(a){for(var b,c=a.length;c--;)b=a[c],0===b.value.indexOf("[CDATA[")?(b.name="#cdata",b.type=4,b.value=b.value.replace(/^\[CDATA\[|\]\]$/g,"")):0===b.value.indexOf("mce:protected ")&&(b.name="#text",b.type=3,b.raw=!0,b.value=unescape(b.value).substr(14))}),u.addNodeFilter("xml:namespace,input",function(a,b){for(var c,d=a.length;d--;)c=a[d],7===c.type?c.remove():1===c.type&&("input"!==b||"type"in c.attributes.map||c.attr("type","text"))}),u.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style,data-mce-selected,data-mce-expando,data-mce-type,data-mce-resize",function(a,b){for(var c=a.length;c--;)a[c].attr(b,null)}),{schema:t,addNodeFilter:u.addNodeFilter,addAttributeFilter:u.addAttributeFilter,serialize:function(b,c){var d,f,g,i,n,o,p=this;return h.ie&&s.select("script,style,select,map").length>0?(n=b.innerHTML,b=b.cloneNode(!1),s.setHTML(b,n)):b=b.cloneNode(!0),d=document.implementation,d.createHTMLDocument&&(f=d.createHTMLDocument(""),l("BODY"==b.nodeName?b.childNodes:[b],function(a){f.body.appendChild(f.importNode(a,!0))}),b="BODY"!=b.nodeName?f.body.firstChild:f.body,g=s.doc,s.doc=f),c=c||{},c.format=c.format||"html",c.selection&&(c.forced_root_block=""),c.no_events||(c.node=b,p.onPreProcess(c)),o=u.parse(m(c.getInner?b.innerHTML:s.getOuterHTML(b)),c),k(o),i=new e(a,t),c.content=i.serialize(o),c.cleanup||(c.content=j.trim(c.content),c.content=c.content.replace(/\uFEFF/g,"")),c.no_events||p.onPostProcess(c),g&&(s.doc=g),c.node=null,c.content},addRules:function(a){t.addValidElements(a)},setRules:function(a){t.setValidElements(a)},onPreProcess:function(a){f&&f.fire("PreProcess",a)},onPostProcess:function(a){f&&f.fire("PostProcess",a)},addTempAttr:r,trimHtml:o,getTrimmedContent:q,trimContent:p}}}),g("p",["6"],function(a){return{BACKSPACE:8,DELETE:46,DOWN:40,ENTER:13,LEFT:37,RIGHT:39,SPACEBAR:32,TAB:9,UP:38,modifierPressed:function(a){return a.shiftKey||a.ctrlKey||a.altKey||this.metaKeyPressed(a)},metaKeyPressed:function(b){return a.mac?b.metaKey:b.ctrlKey&&!b.altKey}}}),g("43",[],function(){function a(a){return a?{left:k(a.left),top:k(a.top),bottom:k(a.bottom),right:k(a.right),width:k(a.width),height:k(a.height)}:{left:0,top:0,bottom:0,right:0,width:0,height:0}}function b(b,c){return b=a(b),c?b.right=b.left:(b.left=b.left+b.width,b.right=b.left),b.width=0,b}function c(a,b){return a.left===b.left&&a.top===b.top&&a.bottom===b.bottom&&a.right===b.right}function d(a,b,c){return a>=0&&a<=Math.min(b.height,c.height)/2}function e(a,b){return a.bottom-a.height/2<b.top||!(a.top>b.bottom)&&d(b.top-a.bottom,a,b)}function f(a,b){return a.top>b.bottom||!(a.bottom<b.top)&&d(b.bottom-a.top,a,b)}function g(a,b){return a.left<b.left}function h(a,b){return a.right>b.right}function i(a,b){return e(a,b)?-1:f(a,b)?1:g(a,b)?-1:h(a,b)?1:0}function j(a,b,c){return b>=a.left&&b<=a.right&&c>=a.top&&c<=a.bottom}var k=Math.round;return{clone:a,collapse:b,isEqual:c,isAbove:e,isBelow:f,isLeft:g,isRight:h,compare:i,containsXY:j}}),g("1n",["3x","43"],function(a,b){var c=function(c,d,e){return!e.collapsed&&a.foldl(e.getClientRects(),function(a,e){return a||b.containsXY(e,c,d)},!1)};return{isXYWithinRange:c}}),g("q",["1m","1j","1n","6","5","9","p"],function(a,b,c,d,e,f,g){function h(a,b){for(;b&&b!=a;){if(j(b)||i(b))return b;b=b.parentNode}return null}var i=b.isContentEditableFalse,j=b.isContentEditableTrue,k=function(a){return a&&"IMG"===a.nodeName},l=function(a,b){return k(a.target)&&!c.isXYWithinRange(a.clientX,a.clientY,b)},m=function(a,b){var c=b.target;l(b,a.selection.getRng())&&!b.isDefaultPrevented()&&(b.preventDefault(),a.selection.select(c))};return function(b,c){function j(a){var b=c.settings.object_resizing;return b!==!1&&!d.iOS&&("string"!=typeof b&&(b="table,img,div"),"false"!==a.getAttribute("data-mce-resize")&&(a!=c.getBody()&&c.dom.is(a,b)))}function k(a){var b,d,e,f,h;b=a.screenX-H,d=a.screenY-I,P=b*F[2]+L,Q=d*F[3]+M,P=P<5?5:P,Q=Q<5?5:Q,e="IMG"==B.nodeName&&c.settings.resize_img_proportional!==!1?!g.modifierPressed(a):g.modifierPressed(a)||"IMG"==B.nodeName&&F[2]*F[3]!==0,e&&(Y(b)>Y(d)?(Q=Z(P*N),P=Z(Q/N)):(P=Z(Q/N),Q=Z(P*N))),T.setStyles(C,{width:P,height:Q}),f=F.startPos.x+b,h=F.startPos.y+d,f=f>0?f:0,h=h>0?h:0,T.setStyles(D,{left:f,top:h,display:"block"}),D.innerHTML=P+" × "+Q,F[2]<0&&C.clientWidth<=P&&T.setStyle(C,"left",J+(L-P)),F[3]<0&&C.clientHeight<=Q&&T.setStyle(C,"top",K+(M-Q)),b=$.scrollWidth-R,d=$.scrollHeight-S,b+d!==0&&T.setStyles(D,{left:f-b,top:h-d}),O||(c.fire("ObjectResizeStart",{target:B,width:L,height:M}),O=!0)}function l(){function a(a,b){b&&(B.style[a]||!c.schema.isValid(B.nodeName.toLowerCase(),a)?T.setStyle(B,a,b):T.setAttrib(B,a,b))}O=!1,a("width",P),a("height",Q),T.unbind(V,"mousemove",k),T.unbind(V,"mouseup",l),W!=V&&(T.unbind(W,"mousemove",k),T.unbind(W,"mouseup",l)),T.remove(C),T.remove(D),X&&"TABLE"!=B.nodeName||n(B),c.fire("ObjectResized",{target:B,width:P,height:Q}),T.setAttrib(B,"style",T.getAttrib(B,"style")),c.nodeChanged()}function n(a,b,e){var f,g,h,i,m;o(),x(),f=T.getPos(a,$),J=f.x,K=f.y,m=a.getBoundingClientRect(),g=m.width||m.right-m.left,h=m.height||m.bottom-m.top,B!=a&&(w(),B=a,P=Q=0),i=c.fire("ObjectSelected",{target:a}),j(a)&&!i.isDefaultPrevented()?U(E,function(a,c){function f(b){H=b.screenX,I=b.screenY,L=B.clientWidth,M=B.clientHeight,N=M/L,F=a,a.startPos={x:g*a[0]+J,y:h*a[1]+K},R=$.scrollWidth,S=$.scrollHeight,C=B.cloneNode(!0),T.addClass(C,"mce-clonedresizable"),T.setAttrib(C,"data-mce-bogus","all"),C.contentEditable=!1,C.unSelectabe=!0,T.setStyles(C,{left:J,top:K,margin:0}),C.removeAttribute("data-mce-selected"),$.appendChild(C),T.bind(V,"mousemove",k),T.bind(V,"mouseup",l),W!=V&&(T.bind(W,"mousemove",k),T.bind(W,"mouseup",l)),D=T.add($,"div",{"class":"mce-resize-helper","data-mce-bogus":"all"},L+" × "+M)}var i;return b?void(c==b&&f(e)):(i=T.get("mceResizeHandle"+c),i&&T.remove(i),i=T.add($,"div",{id:"mceResizeHandle"+c,"data-mce-bogus":"all","class":"mce-resizehandle",unselectable:!0,style:"cursor:"+c+"-resize; margin:0; padding:0"}),d.ie&&(i.contentEditable=!1),T.bind(i,"mousedown",function(a){a.stopImmediatePropagation(),a.preventDefault(),f(a)}),a.elm=i,void T.setStyles(i,{left:g*a[0]+J-i.offsetWidth/2,top:h*a[1]+K-i.offsetHeight/2}))}):o(),B.setAttribute("data-mce-selected","1")}function o(){var a,b;x(),B&&B.removeAttribute("data-mce-selected");for(a in E)b=T.get("mceResizeHandle"+a),b&&(T.unbind(b),T.remove(b))}function p(a){function d(a,b){if(a)do if(a===b)return!0;while(a=a.parentNode)}var e,f;if(!O&&!c.removed)return U(T.select("img[data-mce-selected],hr[data-mce-selected]"),function(a){a.removeAttribute("data-mce-selected")}),f="mousedown"==a.type?a.target:b.getNode(),f=T.$(f).closest(X?"table":"table,img,hr")[0],d(f,$)&&(y(),e=b.getStart(!0),d(e,f)&&d(b.getEnd(!0),f)&&(!X||f!=e&&"IMG"!==e.nodeName))?void n(f):void o()}function q(a,b,c){a&&a.attachEvent&&a.attachEvent("on"+b,c)}function r(a,b,c){a&&a.detachEvent&&a.detachEvent("on"+b,c)}function s(a){var b,d,e,f,g,h,i,j=a.srcElement;b=j.getBoundingClientRect(),h=G.clientX-b.left,i=G.clientY-b.top;for(d in E)if(e=E[d],f=j.offsetWidth*e[0],g=j.offsetHeight*e[1],Y(f-h)<8&&Y(g-i)<8){F=e;break}O=!0,c.fire("ObjectResizeStart",{target:B,width:B.clientWidth,height:B.clientHeight}),c.getDoc().selection.empty(),n(j,d,G)}function t(a){a.preventDefault?a.preventDefault():a.returnValue=!1}function u(a){return i(h(c.getBody(),a))}function v(a){var b=a.srcElement;if(u(b))return void t(a);if(b!=B){if(c.fire("ObjectSelected",{target:b}),w(),0===b.id.indexOf("mceResizeHandle"))return void(a.returnValue=!1);"IMG"!=b.nodeName&&"TABLE"!=b.nodeName||(o(),B=b,q(b,"resizestart",s))}}function w(){r(B,"resizestart",s)}function x(){for(var a in E){var b=E[a];b.elm&&(T.unbind(b.elm),delete b.elm)}}function y(){try{c.getDoc().execCommand("enableObjectResizing",!1,!1); +}catch(a){}}function z(a){var b;if(X){b=V.body.createControlRange();try{return b.addElement(a),b.select(),!0}catch(a){}}}function A(){B=C=null,X&&(w(),r($,"controlselect",v))}var B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T=c.dom,U=f.each,V=c.getDoc(),W=document,X=d.ie&&d.ie<11,Y=Math.abs,Z=Math.round,$=c.getBody();E={nw:[0,0,-1,-1],ne:[1,0,1,-1],se:[1,1,1,1],sw:[0,1,-1,1]};var _=".mce-content-body";return c.contentStyles.push(_+" div.mce-resizehandle {position: absolute;border: 1px solid black;box-sizing: box-sizing;background: #FFF;width: 7px;height: 7px;z-index: 10000}"+_+" .mce-resizehandle:hover {background: #000}"+_+" img[data-mce-selected],"+_+" hr[data-mce-selected] {outline: 1px solid black;resize: none}"+_+" .mce-clonedresizable {position: absolute;"+(d.gecko?"":"outline: 1px dashed black;")+"opacity: .5;filter: alpha(opacity=50);z-index: 10000}"+_+" .mce-resize-helper {background: #555;background: rgba(0,0,0,0.75);border-radius: 3px;border: 1px;color: white;display: none;font-family: sans-serif;font-size: 12px;white-space: nowrap;line-height: 14px;margin: 5px 10px;padding: 5px;position: absolute;z-index: 10001}"),c.on("init",function(){X?(c.on("ObjectResized",function(a){"TABLE"!=a.target.nodeName&&(o(),z(a.target))}),q($,"controlselect",v),c.on("mousedown",function(a){G=a})):(y(),d.ie>=11&&(c.on("mousedown click",function(a){var b=a.target,d=b.nodeName;O||!/^(TABLE|IMG|HR)$/.test(d)||u(b)||(2!==a.button&&c.selection.select(b,"TABLE"==d),"mousedown"==a.type&&c.nodeChanged())}),c.dom.bind($,"mscontrolselect",function(a){function b(a){e.setEditorTimeout(c,function(){c.selection.select(a)})}return u(a.target)?(a.preventDefault(),void b(a.target)):void(/^(TABLE|IMG|HR)$/.test(a.target.nodeName)&&(a.preventDefault(),"IMG"==a.target.tagName&&b(a.target)))})));var b=e.throttle(function(a){c.composing||p(a)});c.on("nodechange ResizeEditor ResizeWindow drop",b),c.on("keyup compositionend",function(a){B&&"TABLE"==B.nodeName&&b(a)}),c.on("hide blur",o),c.on("contextmenu",a.curry(m,c))}),c.on("remove",x),{isResizable:j,showResizeRect:n,hideResizeRect:o,updateResizeRect:p,controlSelect:z,destroy:A}}}),g("1w",[],function(){function a(a){return function(){return a}}function b(a){return function(b){return!a(b)}}function c(a,b){return function(c){return a(b(c))}}function d(){var a=h.call(arguments);return function(b){for(var c=0;c<a.length;c++)if(a[c](b))return!0;return!1}}function e(){var a=h.call(arguments);return function(b){for(var c=0;c<a.length;c++)if(!a[c](b))return!1;return!0}}function f(a){var b=h.call(arguments);return b.length-1>=a.length?a.apply(this,b.slice(1)):function(){var a=b.concat([].slice.call(arguments));return f.apply(this,a)}}function g(){}var h=[].slice;return{constant:a,negate:b,and:e,or:d,curry:f,compose:c,noop:g}}),g("44",["1j","1g","1k"],function(a,b,c){function d(a){return!p(a)&&(l(a)?!m(a.parentNode):n(a)||k(a)||o(a)||j(a))}function e(a,b){for(a=a.parentNode;a&&a!=b;a=a.parentNode){if(j(a))return!1;if(i(a))return!0}return!0}function f(a){return!!j(a)&&b.reduce(a.getElementsByTagName("*"),function(a,b){return a||i(b)},!1)!==!0}function g(a){return n(a)||f(a)}function h(a,b){return d(a)&&e(a,b)}var i=a.isContentEditableTrue,j=a.isContentEditableFalse,k=a.isBr,l=a.isText,m=a.matchNodeNames("script style textarea"),n=a.matchNodeNames("img input textarea hr iframe video audio object"),o=a.matchNodeNames("table"),p=c.isCaretContainer;return{isCaretCandidate:d,isInEditable:e,isAtomic:g,isEditableCaretCandidate:h}}),g("45",[],function(){function a(a){return"string"==typeof a&&a.charCodeAt(0)>=768&&b.test(a)}var b=new RegExp("[\u0300-\u036f\u0483-\u0487\u0488-\u0489\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7-\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e3-\u0902\u093a\u093c\u0941-\u0948\u094d\u0951-\u0957\u0962-\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2-\u09e3\u0a01-\u0a02\u0a3c\u0a41-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a70-\u0a71\u0a75\u0a81-\u0a82\u0abc\u0ac1-\u0ac5\u0ac7-\u0ac8\u0acd\u0ae2-\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62-\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c00\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c62-\u0c63\u0c81\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc-\u0ccd\u0cd5-\u0cd6\u0ce2-\u0ce3\u0d01\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62-\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb-\u0ebc\u0ec8-\u0ecd\u0f18-\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039-\u103a\u103d-\u103e\u1058-\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085-\u1086\u108d\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17b4-\u17b5\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193b\u1a17-\u1a18\u1a1b\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1ab0-\u1abd\u1abe\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80-\u1b81\u1ba2-\u1ba5\u1ba8-\u1ba9\u1bab-\u1bad\u1be6\u1be8-\u1be9\u1bed\u1bef-\u1bf1\u1c2c-\u1c33\u1c36-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1cf4\u1cf8-\u1cf9\u1dc0-\u1df5\u1dfc-\u1dff\u200c-\u200d\u20d0-\u20dc\u20dd-\u20e0\u20e1\u20e2-\u20e4\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302d\u302e-\u302f\u3099-\u309a\ua66f\ua670-\ua672\ua674-\ua67d\ua69e-\ua69f\ua6f0-\ua6f1\ua802\ua806\ua80b\ua825-\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\ua9e5\uaa29-\uaa2e\uaa31-\uaa32\uaa35-\uaa36\uaa43\uaa4c\uaa7c\uaab0\uaab2-\uaab4\uaab7-\uaab8\uaabe-\uaabf\uaac1\uaaec-\uaaed\uaaf6\uabe5\uabe8\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\uff9e-\uff9f]");return{isExtendingChar:a}}),g("1p",["1w","1j","e","h","44","43","45"],function(a,b,c,d,e,f,g){function h(a){return"createRange"in a?a.createRange():c.DOM.createRng()}function i(a){return a&&/[\r\n\t ]/.test(a)}function j(a){var b,c=a.startContainer,d=a.startOffset;return!!(i(a.toString())&&r(c.parentNode)&&(b=c.data,i(b[d-1])||i(b[d+1])))}function k(a){function b(a){var b,c=a.ownerDocument,d=h(c),e=c.createTextNode("\xa0"),g=a.parentNode;return g.insertBefore(e,a),d.setStart(e,0),d.setEnd(e,1),b=f.clone(d.getBoundingClientRect()),g.removeChild(e),b}function c(a){var c,d;return d=a.getClientRects(),c=d.length>0?f.clone(d[0]):f.clone(a.getBoundingClientRect()),t(a)&&0===c.left?b(a):c}function d(a,b){return a=f.collapse(a,b),a.width=1,a.right=a.left+1,a}function e(a){0!==a.height&&(n.length>0&&f.isEqual(a,n[n.length-1])||n.push(a))}function i(a,b){var f=h(a.ownerDocument);if(b<a.data.length){if(g.isExtendingChar(a.data[b]))return n;if(g.isExtendingChar(a.data[b-1])&&(f.setStart(a,b),f.setEnd(a,b+1),!j(f)))return e(d(c(f),!1)),n}b>0&&(f.setStart(a,b-1),f.setEnd(a,b),j(f)||e(d(c(f),!1))),b<a.data.length&&(f.setStart(a,b),f.setEnd(a,b+1),j(f)||e(d(c(f),!0)))}var k,l,n=[];if(s(a.container()))return i(a.container(),a.offset()),n;if(m(a.container()))if(a.isAtEnd())l=v(a.container(),a.offset()),s(l)&&i(l,l.data.length),q(l)&&!t(l)&&e(d(c(l),!1));else{if(l=v(a.container(),a.offset()),s(l)&&i(l,0),q(l)&&a.isAtEnd())return e(d(c(l),!1)),n;k=v(a.container(),a.offset()-1),q(k)&&!t(k)&&(o(k)||o(l)||!q(l))&&e(d(c(k),!1)),q(l)&&e(d(c(l),!0))}return n}function l(b,c,d){function e(){return s(b)?0===c:0===c}function f(){return s(b)?c>=b.data.length:c>=b.childNodes.length}function g(){var a;return a=h(b.ownerDocument),a.setStart(b,c),a.setEnd(b,c),a}function i(){return d||(d=k(new l(b,c))),d}function j(){return i().length>0}function m(a){return a&&b===a.container()&&c===a.offset()}function n(a){return v(b,a?c-1:c)}return{container:a.constant(b),offset:a.constant(c),toRange:g,getClientRects:i,isVisible:j,isAtStart:e,isAtEnd:f,isEqual:m,getNode:n}}var m=b.isElement,n=e.isCaretCandidate,o=b.matchStyleValues("display","block table"),p=b.matchStyleValues("float","left right"),q=a.and(m,n,a.negate(p)),r=a.negate(b.matchStyleValues("white-space","pre pre-line pre-wrap")),s=b.isText,t=b.isBr,u=c.nodeIndex,v=d.getNode;return l.fromRangeStart=function(a){return new l(a.startContainer,a.startOffset)},l.fromRangeEnd=function(a){return new l(a.endContainer,a.endOffset)},l.after=function(a){return new l(a.parentNode,u(a)+1)},l.before=function(a){return new l(a.parentNode,u(a))},l.isAtStart=function(a){return!!a&&a.isAtStart()},l.isAtEnd=function(a){return!!a&&a.isAtEnd()},l.isTextPosition=function(a){return!!a&&b.isText(a.container())},l}),g("1o",["1j","e","1w","1g","1p"],function(a,b,c,d,e){function f(a){var b=a.parentNode;return r(b)?f(b):b}function g(a){return a?d.reduce(a.childNodes,function(a,b){return r(b)&&"BR"!=b.nodeName?a=a.concat(g(b)):a.push(b),a},[]):[]}function h(a,b){for(;(a=a.previousSibling)&&q(a);)b+=a.data.length;return b}function i(a){return function(b){return a===b}}function j(b){var c,e,h;return c=g(f(b)),e=d.findIndex(c,i(b),b),c=c.slice(0,e+1),h=d.reduce(c,function(a,b,d){return q(b)&&q(c[d-1])&&a++,a},0),c=d.filter(c,a.matchNodeNames(b.nodeName)),e=d.findIndex(c,i(b),b),e-h}function k(a){var b;return b=q(a)?"text()":a.nodeName.toLowerCase(),b+"["+j(a)+"]"}function l(a,b,c){var d=[];for(b=b.parentNode;b!=a&&(!c||!c(b));b=b.parentNode)d.push(b);return d}function m(b,e){var f,g,i,j,m,n=[];return f=e.container(),g=e.offset(),q(f)?i=h(f,g):(j=f.childNodes,g>=j.length?(i="after",g=j.length-1):i="before",f=j[g]),n.push(k(f)),m=l(b,f),m=d.filter(m,c.negate(a.isBogus)),n=n.concat(d.map(m,function(a){return k(a)})),n.reverse().join("/")+","+i}function n(b,c,e){var f=g(b);return f=d.filter(f,function(a,b){return!q(a)||!q(f[b-1])}),f=d.filter(f,a.matchNodeNames(c)),f[e]}function o(a,b){for(var c,d=a,f=0;q(d);){if(c=d.data.length,b>=f&&b<=f+c){a=d,b-=f;break}if(!q(d.nextSibling)){a=d,b=c;break}f+=c,d=d.nextSibling}return b>a.data.length&&(b=a.data.length),new e(a,b)}function p(a,b){var c,f,g;return b?(c=b.split(","),b=c[0].split("/"),g=c.length>1?c[1]:"before",f=d.reduce(b,function(a,b){return(b=/([\w\-\(\)]+)\[([0-9]+)\]/.exec(b))?("text()"===b[1]&&(b[1]="#text"),n(a,b[1],parseInt(b[2],10))):null},a),f?q(f)?o(f,parseInt(g,10)):(g="after"===g?s(f)+1:s(f),new e(f.parentNode,g)):null):null}var q=a.isText,r=a.isBogus,s=b.nodeIndex;return{create:m,resolve:p}}),g("r",["1o","1k","1p","1j","h","6","1l","9"],function(a,b,c,d,e,f,g,h){function i(g){var i=g.dom;this.getBookmark=function(f,l){function m(a,b){var c=0;return h.each(i.select(a),function(a){if("all"!==a.getAttribute("data-mce-bogus"))return a!=b&&void c++}),c}function n(a){function b(b){var c,d,e,f=b?"start":"end";c=a[f+"Container"],d=a[f+"Offset"],1==c.nodeType&&"TR"==c.nodeName&&(e=c.childNodes,c=e[Math.min(b?d:d-1,e.length-1)],c&&(d=b?0:c.childNodes.length,a["set"+(b?"Start":"End")](c,d)))}return b(!0),b(),a}function o(a){function b(a,b){var d,e=a[b?"startContainer":"endContainer"],f=a[b?"startOffset":"endOffset"],g=[],h=0;for(3===e.nodeType?g.push(l?k(e,f):f):(d=e.childNodes,f>=d.length&&d.length&&(h=1,f=Math.max(0,d.length-1)),g.push(i.nodeIndex(d[f],l)+h));e&&e!=c;e=e.parentNode)g.push(i.nodeIndex(e,l));return g}var c=i.getRoot(),d={};return d.start=b(a,!0),g.isCollapsed()||(d.end=b(a)),d}function p(a){function c(a,c){var f;if(d.isElement(a)&&(a=e.getNode(a,c),j(a)))return a;if(b.isCaretContainer(a)){if(d.isText(a)&&b.isCaretContainerBlock(a)&&(a=a.parentNode),f=a.previousSibling,j(f))return f;if(f=a.nextSibling,j(f))return f}}return c(a.startContainer,a.startOffset)||c(a.endContainer,a.endOffset)}var q,r,s,t,u,v,w,x="";if(2==f)return v=g.getNode(),u=v?v.nodeName:null,q=g.getRng(),j(v)||"IMG"==u?{name:u,index:m(u,v)}:g.tridentSel?g.tridentSel.getBookmark(f):(v=p(q),v?(u=v.tagName,{name:u,index:m(u,v)}):o(q));if(3==f)return q=g.getRng(),{start:a.create(i.getRoot(),c.fromRangeStart(q)),end:a.create(i.getRoot(),c.fromRangeEnd(q))};if(f)return{rng:g.getRng()};if(q=g.getRng(),s=i.uniqueId(),t=g.isCollapsed(),w="overflow:hidden;line-height:0px",q.duplicate||q.item){if(q.item)return v=q.item(0),u=v.nodeName,{name:u,index:m(u,v)};r=q.duplicate();try{q.collapse(),q.pasteHTML('<span data-mce-type="bookmark" id="'+s+'_start" style="'+w+'">'+x+"</span>"),t||(r.collapse(!1),q.moveToElementText(r.parentElement()),0===q.compareEndPoints("StartToEnd",r)&&r.move("character",-1),r.pasteHTML('<span data-mce-type="bookmark" id="'+s+'_end" style="'+w+'">'+x+"</span>"))}catch(a){return null}}else{if(v=g.getNode(),u=v.nodeName,"IMG"==u)return{name:u,index:m(u,v)};r=n(q.cloneRange()),t||(r.collapse(!1),r.insertNode(i.create("span",{"data-mce-type":"bookmark",id:s+"_end",style:w},x))),q=n(q),q.collapse(!0),q.insertNode(i.create("span",{"data-mce-type":"bookmark",id:s+"_start",style:w},x))}return g.moveToBookmark({id:s,keep:1}),{id:s}},this.moveToBookmark=function(b){function c(a){var c,d,e,f,g=b[a?"start":"end"];if(g){for(e=g[0],d=l,c=g.length-1;c>=1;c--){if(f=d.childNodes,g[c]>f.length-1)return;d=f[g[c]]}3===d.nodeType&&(e=Math.min(g[0],d.nodeValue.length)),1===d.nodeType&&(e=Math.min(g[0],d.childNodes.length)),a?k.setStart(d,e):k.setEnd(d,e)}return!0}function d(a){var c,d,e,g,j=i.get(b.id+"_"+a),k=b.keep;if(j&&(c=j.parentNode,"start"==a?(k?(c=j.firstChild,d=1):d=i.nodeIndex(j),m=n=c,o=p=d):(k?(c=j.firstChild,d=1):d=i.nodeIndex(j),n=c,p=d),!k)){for(g=j.previousSibling,e=j.nextSibling,h.each(h.grep(j.childNodes),function(a){3==a.nodeType&&(a.nodeValue=a.nodeValue.replace(/\uFEFF/g,""))});j=i.get(b.id+"_"+a);)i.remove(j,1);g&&e&&g.nodeType==e.nodeType&&3==g.nodeType&&!f.opera&&(d=g.nodeValue.length,g.appendData(e.nodeValue),i.remove(e),"start"==a?(m=n=g,o=p=d):(n=g,p=d))}}function e(a){return!i.isBlock(a)||a.innerHTML||f.ie||(a.innerHTML='<br data-mce-bogus="1" />'),a}function j(){var c,d;return c=i.createRng(),d=a.resolve(i.getRoot(),b.start),c.setStart(d.container(),d.offset()),d=a.resolve(i.getRoot(),b.end),c.setEnd(d.container(),d.offset()),c}var k,l,m,n,o,p;if(b)if(h.isArray(b.start)){if(k=i.createRng(),l=i.getRoot(),g.tridentSel)return g.tridentSel.moveToBookmark(b);c(!0)&&c()&&g.setRng(k)}else"string"==typeof b.start?g.setRng(j(b)):b.id?(d("start"),d("end"),m&&(k=i.createRng(),k.setStart(e(m),o),k.setEnd(e(n),p),g.setRng(k))):b.name?g.select(i.select(b.name)[b.index]):b.rng&&g.setRng(b.rng)}}var j=d.isContentEditableFalse,k=function(a,b){var c,d;for(d=g.trim(a.data.slice(0,b)).length,c=a.previousSibling;c&&3===c.nodeType;c=c.previousSibling)d+=g.trim(c.data).length;return d};return i.isBookmarkNode=function(a){return a&&"SPAN"===a.tagName&&"bookmark"===a.getAttribute("data-mce-type")},i}),g("6j",[],function(){return"undefined"!=typeof window?window:Function("return this;")()}),g("5u",["6j"],function(a){var b=function(b,c){for(var d=void 0!==c?c:a,e=0;e<b.length&&void 0!==d&&null!==d;++e)d=d[b[e]];return d},c=function(a,c){var d=a.split(".");return b(d,c)},d=function(a,b){return void 0!==a[b]&&null!==a[b]||(a[b]={}),a[b]},e=function(b,c){for(var e=void 0!==c?c:a,f=0;f<b.length;++f)e=d(e,b[f]);return e},f=function(a,b){var c=a.split(".");return e(c,b)};return{path:b,resolve:c,forge:e,namespace:f}}),g("54",["5u"],function(a){var b=function(b,c){return a.resolve(b,c)},c=function(a,c){var d=b(a,c);if(void 0===d)throw a+" not available on this browser";return d};return{getOrDie:c}}),g("46",["54"],function(a){var b=function(){var b=a.getOrDie("Node");return b},c=function(a,b,c){return 0!==(a.compareDocumentPosition(b)&c)},d=function(a,d){return c(a,d,b().DOCUMENT_POSITION_PRECEDING)},e=function(a,d){return c(a,d,b().DOCUMENT_POSITION_CONTAINED_BY)};return{documentPositionPreceding:d,documentPositionContainedBy:e}}),g("55",[],function(){var a=function(a){var b,c=!1;return function(){return c||(c=!0,b=a.apply(null,arguments)),b}};return{cached:a}}),h("75",Number),g("6k",["3x","75","50"],function(a,b,c){var d=function(a,b){for(var c=0;c<a.length;c++){var d=a[c];if(d.test(b))return d}},e=function(a,c){var e=d(a,c);if(!e)return{major:0,minor:0};var f=function(a){return b(c.replace(e,"$"+a))};return h(f(1),f(2))},f=function(a,b){var d=c(b).toLowerCase();return 0===a.length?g():e(a,d)},g=function(){return h(0,0)},h=function(a,b){return{major:a,minor:b}};return{nu:h,detect:f,unknown:g}}),g("5v",["1m","6k"],function(a,b){var c="Edge",d="Chrome",e="IE",f="Opera",g="Firefox",h="Safari",i=function(a,b){return function(){return b===a}},j=function(){return k({current:void 0,version:b.unknown()})},k=function(a){var b=a.current,j=a.version;return{current:b,version:j,isEdge:i(c,b),isChrome:i(d,b),isIE:i(e,b),isOpera:i(f,b),isFirefox:i(g,b),isSafari:i(h,b)}};return{unknown:j,nu:k,edge:a.constant(c),chrome:a.constant(d),ie:a.constant(e),opera:a.constant(f),firefox:a.constant(g),safari:a.constant(h)}}),g("5w",["1m","6k"],function(a,b){var c="Windows",d="iOS",e="Android",f="Linux",g="OSX",h="Solaris",i="FreeBSD",j=function(a,b){return function(){return b===a}},k=function(){return l({current:void 0,version:b.unknown()})},l=function(a){var b=a.current,k=a.version;return{current:b,version:k,isWindows:j(c,b),isiOS:j(d,b),isAndroid:j(e,b),isOSX:j(g,b),isLinux:j(f,b),isSolaris:j(h,b),isFreeBSD:j(i,b)}};return{unknown:k,nu:l,windows:a.constant(c),ios:a.constant(d),android:a.constant(e),linux:a.constant(f),osx:a.constant(g),solaris:a.constant(h),freebsd:a.constant(i)}}),g("5x",["1m"],function(a){return function(b,c,d){var e=b.isiOS()&&/ipad/i.test(d)===!0,f=b.isiOS()&&!e,g=b.isAndroid()&&3===b.version.major,h=b.isAndroid()&&4===b.version.major,i=e||g||h&&/mobile/i.test(d)===!0,j=b.isiOS()||b.isAndroid(),k=j&&!i,l=c.isSafari()&&b.isiOS()&&/safari/i.test(d)===!1;return{isiPad:a.constant(e),isiPhone:a.constant(f),isTablet:a.constant(i),isPhone:a.constant(k),isTouch:a.constant(j),isAndroid:b.isAndroid,isiOS:b.isiOS,isWebView:a.constant(l)}}}),g("5y",["3x","6k","50"],function(a,b,c){var d=function(b,d){var e=c(d).toLowerCase();return a.find(b,function(a){return a.search(e)})},e=function(a,c){return d(a,c).map(function(a){var d=b.detect(a.versionRegexes,c);return{current:a.name,version:d}})},f=function(a,c){return d(a,c).map(function(a){var d=b.detect(a.versionRegexes,c);return{current:a.name,version:d}})};return{detectBrowser:e,detectOs:f}}),g("76",[],function(){var a=function(a,b){return b+a},b=function(a,b){return a+b},c=function(a,b){return a.substring(b)},d=function(a,b){return a.substring(0,a.length-b)};return{addToStart:a,addToEnd:b,removeFromStart:c,removeFromEnd:d}}),g("77",["4z","3z"],function(a,b){var c=function(a,b){return a.substr(0,b)},d=function(a,b){return a.substr(a.length-b,a.length)},e=function(b){return""===b?a.none():a.some(b.substr(0,1))},f=function(b){return""===b?a.none():a.some(b.substring(1))};return{first:c,last:d,head:e,tail:f}}),g("6l",["76","77","3z"],function(a,b,c){var d=function(a,b,c){if(""===b)return!0;if(a.length<b.length)return!1;var d=a.substr(c,c+b.length);return d===b},e=function(a,b){var c=function(a){var b=typeof a;return"string"===b||"number"===b};return a.replace(/\${([^{}]*)}/g,function(a,d){var e=b[d];return c(e)?e:a})},f=function(b,c){return l(b,c)?a.removeFromStart(b,c.length):b},g=function(b,c){return m(b,c)?a.removeFromEnd(b,c.length):b},h=function(b,c){return l(b,c)?b:a.addToStart(b,c)},i=function(b,c){return m(b,c)?b:a.addToEnd(b,c)},j=function(a,b){return a.indexOf(b)!==-1},k=function(a){return b.head(a).bind(function(c){return b.tail(a).map(function(a){return c.toUpperCase()+a})}).getOr(a)},l=function(a,b){return d(a,b,0)},m=function(a,b){return d(a,b,a.length-b.length)},n=function(a){return a.replace(/^\s+|\s+$/g,"")},o=function(a){return a.replace(/^\s+/g,"")},p=function(a){return a.replace(/\s+$/g,"")};return{supplant:e,startsWith:l,removeLeading:f,removeTrailing:g,ensureLeading:h,ensureTrailing:i,endsWith:m,contains:j,trim:n,lTrim:o,rTrim:p,capitalize:k}}),g("5z",["1m","6l"],function(a,b){var c=/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,d=function(a){return function(c){return b.contains(c,a)}},e=[{name:"Edge",versionRegexes:[/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],search:function(a){var c=b.contains(a,"edge/")&&b.contains(a,"chrome")&&b.contains(a,"safari")&&b.contains(a,"applewebkit");return c}},{name:"Chrome",versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/,c],search:function(a){return b.contains(a,"chrome")&&!b.contains(a,"chromeframe")}},{name:"IE",versionRegexes:[/.*?msie\ ?([0-9]+)\.([0-9]+).*/,/.*?rv:([0-9]+)\.([0-9]+).*/],search:function(a){return b.contains(a,"msie")||b.contains(a,"trident")}},{name:"Opera",versionRegexes:[c,/.*?opera\/([0-9]+)\.([0-9]+).*/],search:d("opera")},{name:"Firefox",versionRegexes:[/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],search:d("firefox")},{name:"Safari",versionRegexes:[c,/.*?cpu os ([0-9]+)_([0-9]+).*/],search:function(a){return(b.contains(a,"safari")||b.contains(a,"mobile/"))&&b.contains(a,"applewebkit")}}],f=[{name:"Windows",search:d("win"),versionRegexes:[/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]},{name:"iOS",search:function(a){return b.contains(a,"iphone")||b.contains(a,"ipad")},versionRegexes:[/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,/.*cpu os ([0-9]+)_([0-9]+).*/,/.*cpu iphone os ([0-9]+)_([0-9]+).*/]},{name:"Android",search:d("android"),versionRegexes:[/.*?android\ ?([0-9]+)\.([0-9]+).*/]},{name:"OSX",search:d("os x"),versionRegexes:[/.*?os\ x\ ?([0-9]+)_([0-9]+).*/]},{name:"Linux",search:d("linux"),versionRegexes:[]},{name:"Solaris",search:d("sunos"),versionRegexes:[]},{name:"FreeBSD",search:d("freebsd"),versionRegexes:[]}];return{browsers:a.constant(e),oses:a.constant(f)}}),g("56",["5v","5w","5x","5y","5z"],function(a,b,c,d,e){var f=function(f){var g=e.browsers(),h=e.oses(),i=d.detectBrowser(g,f).fold(a.unknown,a.nu),j=d.detectOs(h,f).fold(b.unknown,b.nu),k=c(j,i,f);return{browser:i,os:j,deviceType:k}};return{detect:f}}),h("57",navigator),g("47",["55","56","57"],function(a,b,c){var d=a.cached(function(){var a=c.userAgent;return b.detect(a)});return{detect:d}}),g("49",[],function(){return"undefined"==typeof console&&(console={log:function(){}}),console}),h("22",document),g("1r",["1m","3z","49","22"],function(a,b,c,d){var e=function(a,b){var e=b||d,f=e.createElement("div");if(f.innerHTML=a,!f.hasChildNodes()||f.childNodes.length>1)throw c.error("HTML does not have a single root node",a),"HTML must have a single root node";return h(f.childNodes[0])},f=function(a,b){var c=b||d,e=c.createElement(a);return h(e)},g=function(a,b){var c=b||d,e=c.createTextNode(a);return h(e)},h=function(c){if(null===c||void 0===c)throw new b("Node cannot be null or undefined");return{dom:a.constant(c)}};return{fromHtml:e,fromTag:f,fromText:g,fromDom:h}}),g("58",[],function(){return{ATTRIBUTE:2,CDATA_SECTION:4,COMMENT:8,DOCUMENT:9,DOCUMENT_TYPE:10,DOCUMENT_FRAGMENT:11,ELEMENT:1,TEXT:3,PROCESSING_INSTRUCTION:7,ENTITY_REFERENCE:5,ENTITY:6,NOTATION:12}}),g("48",["3x","4z","1r","58","3z","22"],function(a,b,c,d,e,f){var g=0,h=1,i=2,j=3,k=function(){var a=f.createElement("span");return void 0!==a.matches?g:void 0!==a.msMatchesSelector?h:void 0!==a.webkitMatchesSelector?i:void 0!==a.mozMatchesSelector?j:-1}(),l=d.ELEMENT,m=d.DOCUMENT,n=function(a,b){var c=a.dom();if(c.nodeType!==l)return!1;if(k===g)return c.matches(b);if(k===h)return c.msMatchesSelector(b);if(k===i)return c.webkitMatchesSelector(b);if(k===j)return c.mozMatchesSelector(b);throw new e("Browser lacks native selectors")},o=function(a){return a.nodeType!==l&&a.nodeType!==m||0===a.childElementCount},p=function(b,d){var e=void 0===d?f:d.dom();return o(e)?[]:a.map(e.querySelectorAll(b),c.fromDom)},q=function(a,d){var e=void 0===d?f:d.dom();return o(e)?b.none():b.from(e.querySelector(a)).map(c.fromDom)};return{all:p,is:n,one:q}}),g("1q",["3x","1m","46","47","48"],function(a,b,c,d,e){var f=function(a,b){return a.dom()===b.dom()},g=function(a,b){return a.dom().isEqualNode(b.dom())},h=function(c,d){return a.exists(d,b.curry(f,c))},i=function(a,b){var c=a.dom(),d=b.dom();return c!==d&&c.contains(d)},j=function(a,b){return c.documentPositionContainedBy(a.dom(),b.dom())},k=d.detect().browser,l=k.isIE()?j:i;return{eq:f,isEqualNode:g,member:h,contains:l,is:e.is}}),g("1s",["1j"],function(a){var b=function(a){for(var b=0,c=0,d=a;d&&d.nodeType;)b+=d.offsetLeft||0,c+=d.offsetTop||0,d=d.offsetParent;return{x:b,y:c}},c=function(a,b,c){var d={elm:b,alignToTop:c};return a.fire("scrollIntoView",d),d.isDefaultPrevented()},d=function(d,e,f){var g,h,i,j,k=d.dom,l=k.getRoot(),m=0;if(!c(d,e,f)&&a.isElement(e)){if(f===!1&&(m=e.offsetHeight),"BODY"!==l.nodeName){var n=d.selection.getScrollContainer();if(n)return g=b(e).y-b(n).y+m,j=n.clientHeight,i=n.scrollTop,void((g<i||g+25>i+j)&&(n.scrollTop=g<i?g:g-j+25))}h=k.getViewPort(d.getWin()),g=k.getPos(e).y+m,i=h.y,j=h.h,(g<h.y||g+25>i+j)&&d.getWin().scrollTo(0,g<i?g:g-j+25)}};return{scrollIntoView:d}}),g("1t",[],function(){function a(a){function b(b,c){var d,e,f,g,h,i,j,k,l=0,m=-1;if(d=b.duplicate(),d.collapse(c),k=d.parentElement(),k.ownerDocument===a.dom.doc){for(;"false"===k.contentEditable;)k=k.parentNode;if(!k.hasChildNodes())return{node:k,inside:1};for(g=k.children,e=g.length-1;l<=e;)if(j=Math.floor((l+e)/2),h=g[j],d.moveToElementText(h),m=d.compareEndPoints(c?"StartToStart":"EndToEnd",b),m>0)e=j-1;else{if(!(m<0))return{node:h};l=j+1}if(m<0)for(h?d.collapse(!1):(d.moveToElementText(k),d.collapse(!0),h=k,f=!0),i=0;0!==d.compareEndPoints(c?"StartToStart":"StartToEnd",b)&&0!==d.move("character",1)&&k==d.parentElement();)i++;else for(d.collapse(!0),i=0;0!==d.compareEndPoints(c?"StartToStart":"StartToEnd",b)&&0!==d.move("character",-1)&&k==d.parentElement();)i++;return{node:h,position:m,offset:i,inside:f}}}function c(){function c(a){var c,d,e,f,g,h=b(k,a),i=0;if(c=h.node,d=h.offset,h.inside&&!c.hasChildNodes())return void l[a?"setStart":"setEnd"](c,0);if(d===f)return void l[a?"setStartBefore":"setEndAfter"](c);if(h.position<0){if(e=h.inside?c.firstChild:c.nextSibling,!e)return void l[a?"setStartAfter":"setEndAfter"](c);if(!d)return void(3==e.nodeType?l[a?"setStart":"setEnd"](e,0):l[a?"setStartBefore":"setEndBefore"](e));for(;e;){if(3==e.nodeType&&(g=e.nodeValue,i+=g.length,i>=d)){c=e,i-=d,i=g.length-i;break}e=e.nextSibling}}else{if(e=c.previousSibling,!e)return l[a?"setStartBefore":"setEndBefore"](c);if(!d)return void(3==c.nodeType?l[a?"setStart":"setEnd"](e,c.nodeValue.length):l[a?"setStartAfter":"setEndAfter"](e));for(;e;){if(3==e.nodeType&&(i+=e.nodeValue.length,i>=d)){c=e,i-=d;break}e=e.previousSibling}}l[a?"setStart":"setEnd"](c,i)}var f,g,h,i,j,k=a.getRng(),l=e.createRng();if(f=k.item?k.item(0):k.parentElement(),f.ownerDocument!=e.doc)return l;if(g=a.isCollapsed(),k.item)return l.setStart(f.parentNode,e.nodeIndex(f)),l.setEnd(l.startContainer,l.startOffset+1),l;try{c(!0),g||c()}catch(b){if(b.number!=-2147024809)throw b;j=d.getBookmark(2),h=k.duplicate(),h.collapse(!0),f=h.parentElement(),g||(h=k.duplicate(),h.collapse(!1),i=h.parentElement(),i.innerHTML=i.innerHTML),f.innerHTML=f.innerHTML,d.moveToBookmark(j),k=a.getRng(),c(!0),g||c()}return l}var d=this,e=a.dom,f=!1;this.getBookmark=function(c){function d(a){var b,c,d,f,g=[];for(b=a.parentNode,c=e.getRoot().parentNode;b!=c&&9!==b.nodeType;){for(d=b.children,f=d.length;f--;)if(a===d[f]){g.push(f);break}a=b,b=b.parentNode}return g}function f(a){var c;if(c=b(g,a))return{position:c.position,offset:c.offset,indexes:d(c.node),inside:c.inside}}var g=a.getRng(),h={};return 2===c&&(g.item?h.start={ctrl:!0,indexes:d(g.item(0))}:(h.start=f(!0),a.isCollapsed()||(h.end=f()))),h},this.moveToBookmark=function(a){function b(a){var b,c,d,f;for(b=e.getRoot(),c=a.length-1;c>=0;c--)f=b.children,d=a[c],d<=f.length-1&&(b=f[d]);return b}function c(c){var e,g,h,i,j=a[c?"start":"end"];j&&(e=j.position>0,g=f.createTextRange(),g.moveToElementText(b(j.indexes)),i=j.offset,i!==h?(g.collapse(j.inside||e),g.moveStart("character",e?-i:i)):g.collapse(c),d.setEndPoint(c?"StartToStart":"EndToStart",g),c&&d.collapse(!0))}var d,f=e.doc.body;a.start&&(a.start.ctrl?(d=f.createControlRange(),d.addElement(b(a.start.indexes)),d.select()):(d=f.createTextRange(),c(!0),c(),d.select()))},this.addRange=function(b){function c(a){var b,c,g,l,m;g=e.create("a"),b=a?h:j,c=a?i:k,l=d.duplicate(),b!=o&&b!=o.documentElement||(b=p,c=0),3==b.nodeType?(b.parentNode.insertBefore(g,b),l.moveToElementText(g),l.moveStart("character",c),e.remove(g),d.setEndPoint(a?"StartToStart":"EndToEnd",l)):(m=b.childNodes,m.length?(c>=m.length?e.insertAfter(g,m[m.length-1]):b.insertBefore(g,m[c]),l.moveToElementText(g)):b.canHaveHTML&&(b.innerHTML="<span></span>",g=b.firstChild,l.moveToElementText(g),l.collapse(f)),d.setEndPoint(a?"StartToStart":"EndToEnd",l),e.remove(g))}var d,g,h,i,j,k,l,m,n,o=a.dom.doc,p=o.body;if(h=b.startContainer,i=b.startOffset,j=b.endContainer,k=b.endOffset,d=p.createTextRange(),h==j&&1==h.nodeType){if(i==k&&!h.hasChildNodes()){if(h.canHaveHTML)return l=h.previousSibling,l&&!l.hasChildNodes()&&e.isBlock(l)?l.innerHTML="":l=null,h.innerHTML="<span></span><span></span>",d.moveToElementText(h.lastChild),d.select(),e.doc.selection.clear(),h.innerHTML="",void(l&&(l.innerHTML=""));i=e.nodeIndex(h),h=h.parentNode}if(i==k-1)try{if(n=h.childNodes[i],g=p.createControlRange(),g.addElement(n),g.select(),m=a.getRng(),m.item&&n===m.item(0))return}catch(a){}}c(!0),c(),d.select()},this.getRangeAt=c}return a}),g("60",["3y","50"],function(a,b){var c=function(c){if(null===c)return"null";var d=typeof c;return"object"===d&&a.prototype.isPrototypeOf(c)?"array":"object"===d&&b.prototype.isPrototypeOf(c)?"string":d},d=function(a){return function(b){return c(b)===a}};return{isString:d("string"),isObject:d("object"),isArray:d("array"),isNull:d("null"),isBoolean:d("boolean"),isUndefined:d("undefined"),isFunction:d("function"),isNumber:d("number")}}),g("6m",["3x","1m","3y","3z"],function(a,b,c,d){return function(){var e=arguments;return function(){for(var f=new c(arguments.length),g=0;g<f.length;g++)f[g]=arguments[g];if(e.length!==f.length)throw new d('Wrong number of arguments to struct. Expected "['+e.length+']", got '+f.length+" arguments");var h={};return a.each(e,function(a,c){h[a]=b.constant(f[c])}),h}}}),g("63",["4z","5s"],function(a,b){var c=function(){var a=b.keys,c=function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b};return void 0===a?c:a}(),d=function(a,b){for(var d=c(a),e=0,f=d.length;e<f;e++){var g=d[e],h=a[g];b(h,g,a)}},e=function(a,b){return f(a,function(a,c,d){return{k:c,v:b(a,c,d)}})},f=function(a,b){var c={};return d(a,function(d,e){var f=b(d,e,a);c[f.k]=f.v}),c},g=function(a,b){var c={},e={};return d(a,function(a,d){var f=b(a,d)?c:e;f[d]=a}),{t:c,f:e}},h=function(a,b){var c=[];return d(a,function(a,d){c.push(b(a,d))}),c},i=function(b,d){for(var e=c(b),f=0,g=e.length;f<g;f++){var h=e[f],i=b[h];if(d(i,h,b))return a.some(i)}return a.none()},j=function(a){return h(a,function(a){return a})},k=function(a){return j(a).length};return{bifilter:g,each:d,map:e,mapToArray:h,tupleMap:f,find:i,keys:c,values:j,size:k}}),g("78",["3x","60","3z"],function(a,b,c){var d=function(a){return a.slice(0).sort()},e=function(a,b){throw new c("All required keys ("+d(a).join(", ")+") were not specified. Specified keys were: "+d(b).join(", ")+".")},f=function(a){throw new c("Unsupported keys for object: "+d(a).join(", "))},g=function(d,e){if(!b.isArray(e))throw new c("The "+d+" fields must be an array. Was: "+e+".");a.each(e,function(a){if(!b.isString(a))throw new c("The value "+a+" in the "+d+" fields was not a string.")})},h=function(a,b){throw new c("All values need to be of type: "+b+". Keys ("+d(a).join(", ")+") were not.")},i=function(b){var e=d(b),f=a.find(e,function(a,b){return b<e.length-1&&a===e[b+1]});f.each(function(a){throw new c("The field: "+a+" occurs more than once in the combined fields: ["+e.join(", ")+"].")})};return{sort:d,reqMessage:e,unsuppMessage:f,validateStrArr:g,invalidTypeMessage:h,checkDupes:i}}),g("6n",["3x","1m","63","4z","78","3z","5s"],function(a,b,c,d,e,f,g){return function(h,i){var j=h.concat(i);if(0===j.length)throw new f("You must specify at least one required or optional field."); +return e.validateStrArr("required",h),e.validateStrArr("optional",i),e.checkDupes(j),function(f){var k=c.keys(f),l=a.forall(h,function(b){return a.contains(k,b)});l||e.reqMessage(h,k);var m=a.filter(k,function(b){return!a.contains(j,b)});m.length>0&&e.unsuppMessage(m);var n={};return a.each(h,function(a){n[a]=b.constant(f[a])}),a.each(i,function(a){n[a]=b.constant(g.prototype.hasOwnProperty.call(f,a)?d.some(f[a]):d.none())}),n}}}),g("61",["6m","6n"],function(a,b){return{immutable:a,immutableBag:b}}),g("62",[],function(){var a=function(a,b){var c=[],d=function(a){return c.push(a),b(a)},e=b(a);do e=e.bind(d);while(e.isSome());return c};return{toArray:a}}),g("59",["60","3x","1m","4z","61","62","1q","1r"],function(a,b,c,d,e,f,g,h){var i=function(a){return h.fromDom(a.dom().ownerDocument)},j=function(a){var b=i(a);return h.fromDom(b.dom().documentElement)},k=function(a){var b=a.dom(),c=b.ownerDocument.defaultView;return h.fromDom(c)},l=function(a){var b=a.dom();return d.from(b.parentNode).map(h.fromDom)},m=function(a){return l(a).bind(function(c){var d=u(c);return b.findIndex(d,function(b){return g.eq(a,b)})})},n=function(b,d){for(var e=a.isFunction(d)?d:c.constant(!1),f=b.dom(),g=[];null!==f.parentNode&&void 0!==f.parentNode;){var i=f.parentNode,j=h.fromDom(i);if(g.push(j),e(j)===!0)break;f=i}return g},o=function(a){var c=function(c){return b.filter(c,function(b){return!g.eq(a,b)})};return l(a).map(u).map(c).getOr([])},p=function(a){var b=a.dom();return d.from(b.offsetParent).map(h.fromDom)},q=function(a){var b=a.dom();return d.from(b.previousSibling).map(h.fromDom)},r=function(a){var b=a.dom();return d.from(b.nextSibling).map(h.fromDom)},s=function(a){return b.reverse(f.toArray(a,q))},t=function(a){return f.toArray(a,r)},u=function(a){var c=a.dom();return b.map(c.childNodes,h.fromDom)},v=function(a,b){var c=a.dom().childNodes;return d.from(c[b]).map(h.fromDom)},w=function(a){return v(a,0)},x=function(a){return v(a,a.dom().childNodes.length-1)},y=e.immutable("element","offset"),z=function(a,b){var c=u(a);return c.length>0&&b<c.length?y(c[b],0):y(a,b)};return{owner:i,defaultView:k,documentElement:j,parent:l,findIndex:m,parents:n,siblings:o,prevSibling:q,offsetParent:p,prevSiblings:s,nextSibling:r,nextSiblings:t,children:u,child:v,firstChild:w,lastChild:x,leaf:z}}),g("4a",["59"],function(a){var b=function(b,c){var d=a.parent(b);d.each(function(a){a.dom().insertBefore(c.dom(),b.dom())})},c=function(c,d){var f=a.nextSibling(c);f.fold(function(){var b=a.parent(c);b.each(function(a){e(a,d)})},function(a){b(a,d)})},d=function(b,c){var d=a.firstChild(b);d.fold(function(){e(b,c)},function(a){b.dom().insertBefore(c.dom(),a.dom())})},e=function(a,b){a.dom().appendChild(b.dom())},f=function(c,d,f){a.child(c,f).fold(function(){e(c,d)},function(a){b(a,d)})},g=function(a,c){b(a,c),e(c,a)};return{before:b,after:c,prepend:d,append:e,appendAt:f,wrap:g}}),g("4d",["58"],function(a){var b=function(a){var b=a.dom().nodeName;return b.toLowerCase()},c=function(a){return a.dom().nodeType},d=function(a){return a.dom().nodeValue},e=function(a){return function(b){return c(b)===a}},f=function(d){return c(d)===a.COMMENT||"#comment"===b(d)},g=e(a.ELEMENT),h=e(a.TEXT),i=e(a.DOCUMENT);return{name:b,type:c,value:d,isElement:g,isText:h,isDocument:i,isComment:f}}),g("5a",["60","3x","63","4d","3z","49"],function(a,b,c,d,e,f){var g=function(b,c,d){if(!(a.isString(d)||a.isBoolean(d)||a.isNumber(d)))throw f.error("Invalid call to Attr.set. Key ",c,":: Value ",d,":: Element ",b),new e("Attribute value was not simple");b.setAttribute(c,d+"")},h=function(a,b,c){g(a.dom(),b,c)},i=function(a,b){var d=a.dom();c.each(b,function(a,b){g(d,b,a)})},j=function(a,b){var c=a.dom().getAttribute(b);return null===c?void 0:c},k=function(a,b){var c=a.dom();return!(!c||!c.hasAttribute)&&c.hasAttribute(b)},l=function(a,b){a.dom().removeAttribute(b)},m=function(a){var b=a.dom().attributes;return void 0===b||null===b||0===b.length},n=function(a){return b.foldl(a.dom().attributes,function(a,b){return a[b.name]=b.value,a},{})},o=function(a,b,c){k(a,c)&&!k(b,c)&&h(b,c,j(a,c))},p=function(a,c,e){d.isElement(a)&&d.isElement(c)&&b.each(e,function(b){o(a,c,b)})};return{clone:n,set:h,setAll:i,get:j,has:k,remove:l,hasNone:m,transfer:p}}),g("5b",["3x","4a"],function(a,b){var c=function(c,d){a.each(d,function(a){b.before(c,a)})},d=function(c,d){a.each(d,function(a,e){var f=0===e?c:d[e-1];b.after(f,a)})},e=function(c,d){a.each(d.slice().reverse(),function(a){b.prepend(c,a)})},f=function(c,d){a.each(d,function(a){b.append(c,a)})};return{before:c,after:d,prepend:e,append:f}}),g("5c",["3x","5b","59"],function(a,b,c){var d=function(b){b.dom().textContent="",a.each(c.children(b),function(a){e(a)})},e=function(a){var b=a.dom();null!==b.parentNode&&b.parentNode.removeChild(b)},f=function(a){var d=c.children(a);d.length>0&&b.before(a,d),e(a)};return{empty:d,remove:e,unwrap:f}}),g("4b",["5a","1r","4a","5b","5c","59"],function(a,b,c,d,e,f){var g=function(a,c){return b.fromDom(a.dom().cloneNode(c))},h=function(a){return g(a,!1)},i=function(a){return g(a,!0)},j=function(c,d){var e=b.fromTag(d),f=a.clone(c);return a.setAll(e,f),e},k=function(a,b){var c=j(a,b),e=f.children(i(a));return d.append(c,e),c},l=function(a,b){var g=j(a,b);c.before(a,g);var h=f.children(a);return d.append(g,h),e.remove(a),g};return{shallow:h,shallowAs:j,deep:i,copy:k,mutate:l}}),g("4c",["3x","1r","22"],function(a,b,c){var d=function(d,e){var f=e||c,g=f.createDocumentFragment();return a.each(d,function(a){g.appendChild(a.dom())}),b.fromDom(g)};return{fromElements:d}}),g("4e",["3x","1m","4d"],function(a,b,c){var d=["article","aside","details","div","dt","figcaption","footer","form","fieldset","header","hgroup","html","main","nav","section","summary","body","p","dl","multicol","dd","figure","address","center","blockquote","h1","h2","h3","h4","h5","h6","listing","xmp","pre","plaintext","menu","dir","ul","ol","li","hr","table","tbody","thead","tfoot","th","tr","td","caption"],e=["area","base","basefont","br","col","frame","hr","img","input","isindex","link","meta","param","embed","source","wbr","track"],f=["td","th"],g=["h1","h2","h3","h4","h5","h6","p","div","address","pre","form","blockquote","center","dir","fieldset","header","footer","article","section","hgroup","aside","nav","figure"],h=["h1","h2","h3","h4","h5","h6"],i=function(d){var e;return function(f){return e=e?e:a.mapToObject(d,b.constant(!0)),e.hasOwnProperty(c.name(f))}},j=i(h),k=i(d),l=function(a){return c.isElement(a)&&!k(a)};return{isBlock:k,isInline:l,isHeading:j,isTextBlock:i(g),isVoid:i(e),isTableCell:i(f)}}),g("4f",["1m","1q","59"],function(a,b,c){var d=function(a){return a.slice(0,-1)},e=function(a,e,f){return b.contains(e,a)?d(c.parents(a,function(a){return f(a)||b.eq(a,e)})):[]},f=function(b,c){return e(b,c,a.constant(!1))},g=function(a,b){return[a].concat(f(a,b))};return{parentsUntil:e,parents:f,parentsAndSelf:g}}),g("5d",["4z"],function(a){var b=function(a){for(var b=[],c=function(a){b.push(a)},d=0;d<a.length;d++)a[d].each(c);return b},c=function(b,c){for(var d=0;d<b.length;d++){var e=c(b[d],d);if(e.isSome())return e}return a.none()},d=function(b,c){for(var d=[],e=0;e<b.length;e++){var f=b[e];if(!f.isSome())return a.none();d.push(f.getOrDie())}return a.some(c.apply(null,d))};return{cat:b,findMap:c,liftN:d}}),g("4g",["3x","1m","4z","5d","1q","1r","4d","59","1j"],function(a,b,c,d,e,f,g,h,i){var j=function(a){var b=a.startContainer,d=a.startOffset;return i.isText(b)?0===d?c.some(f.fromDom(b)):c.none():c.from(b.childNodes[d]).map(f.fromDom)},k=function(a){var b=a.endContainer,d=a.endOffset;return i.isText(b)?d===b.data.length?c.some(f.fromDom(b)):c.none():c.from(b.childNodes[d-1]).map(f.fromDom)},l=function(a){return h.firstChild(a).fold(b.constant([a]),function(b){return[a].concat(l(b))})},m=function(a){return h.lastChild(a).fold(b.constant([a]),function(b){return"br"===g.name(b)?h.prevSibling(b).map(function(b){return[a].concat(m(b))}).getOr([]):[a].concat(m(b))})},n=function(c,f){return d.liftN([j(f),k(f)],function(d,f){var g=a.find(l(c),b.curry(e.eq,d)),h=a.find(m(c),b.curry(e.eq,f));return g.isSome()&&h.isSome()}).getOr(!1)};return{hasAllContentsSelected:n}}),g("1u",["3x","1m","4a","4b","1r","4c","4d","4e","4f","4g"],function(a,b,c,d,e,f,g,h,i,j){var k=function(b){return a.find(b,function(a){return"ul"===g.name(a)||"ol"===g.name(a)})},l=function(c,d){return a.find(c,function(a){return"li"===g.name(a)&&j.hasAllContentsSelected(a,d)}).fold(b.constant([]),function(a){return k(c).map(function(a){return[e.fromTag("li"),e.fromTag(g.name(a))]}).getOr([])})},m=function(b,d){var e=a.foldl(d,function(a,b){return c.append(b,a),b},b);return d.length>0?f.fromElements([e]):e},n=function(b,c){var f=i.parentsAndSelf(e.fromDom(c.commonAncestorContainer),e.fromDom(b)),g=a.filter(f,function(a){return h.isInline(a)||h.isHeading(a)}),j=l(f,c);return a.map(g.concat(j),d.shallow)},o=function(a,b){return m(e.fromDom(b.cloneContents()),n(a,b))},p=function(a,b){return b.collapsed?f.fromElements([]):o(a,b)};return{read:p}}),g("s",["1q","1r","1p","r","q","1j","h","1s","c","1t","6","1u","1l","9"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){function o(a,b,c,f){var g=this;g.dom=a,g.win=b,g.serializer=c,g.editor=f,g.bookmarkManager=new d(g),g.controlSelection=new e(g,f),g.win.getSelection||(g.tridentSel=new j(g))}var p=n.each,q=n.trim,r=k.ie,s=function(c){return!(!c||!c.ownerDocument)&&a.contains(b.fromDom(c.ownerDocument),b.fromDom(c))},t=function(a){return!!a&&(!!a.select||s(a.startContainer)&&s(a.endContainer))};return o.prototype={setCursorLocation:function(a,b){var c=this,d=c.dom.createRng();a?(d.setStart(a,b),d.setEnd(a,b),c.setRng(d),c.collapse(!1)):(c._moveEndPoint(d,c.editor.getBody(),!0),c.setRng(d))},getContent:function(a){var b,c,d,e=this,f=e.getRng(),g=e.dom.create("body"),h=e.getSel();return a=a||{},b=c="",a.get=!0,a.format=a.format||"html",a.selection=!0,e.editor.fire("BeforeGetContent",a),"text"===a.format?e.isCollapsed()?"":m.trim(f.text||(h.toString?h.toString():"")):(f.cloneContents?(d=a.contextual?l.read(e.editor.getBody(),f).dom():f.cloneContents(),d&&g.appendChild(d)):void 0!==f.item||void 0!==f.htmlText?(g.innerHTML="<br>"+(f.item?f.item(0).outerHTML:f.htmlText),g.removeChild(g.firstChild)):g.innerHTML=f.toString(),/^\s/.test(g.innerHTML)&&(b=" "),/\s+$/.test(g.innerHTML)&&(c=" "),a.getInner=!0,a.content=e.isCollapsed()?"":b+e.serializer.serialize(g,a)+c,e.editor.fire("GetContent",a),a.content)},setContent:function(a,b){var c,d,e,f=this,g=f.getRng(),h=f.win.document;if(b=b||{format:"html"},b.set=!0,b.selection=!0,b.content=a,b.no_events||f.editor.fire("BeforeSetContent",b),a=b.content,g.insertNode){a+='<span id="__caret">_</span>',g.startContainer==h&&g.endContainer==h?h.body.innerHTML=a:(g.deleteContents(),0===h.body.childNodes.length?h.body.innerHTML=a:g.createContextualFragment?g.insertNode(g.createContextualFragment(a)):(d=h.createDocumentFragment(),e=h.createElement("div"),d.appendChild(e),e.outerHTML=a,g.insertNode(d))),c=f.dom.get("__caret"),g=h.createRange(),g.setStartBefore(c),g.setEndBefore(c),f.setRng(g),f.dom.remove("__caret");try{f.setRng(g)}catch(a){}}else g.item&&(h.execCommand("Delete",!1,null),g=f.getRng()),/^\s+/.test(a)?(g.pasteHTML('<span id="__mce_tmp">_</span>'+a),f.dom.remove("__mce_tmp")):g.pasteHTML(a);b.no_events||f.editor.fire("SetContent",b)},getStart:function(a){var b,c,d,e,f=this,g=f.getRng();if(g.duplicate||g.item){if(g.item)return g.item(0);for(d=g.duplicate(),d.collapse(1),b=d.parentElement(),b.ownerDocument!==f.dom.doc&&(b=f.dom.getRoot()),c=e=g.parentElement();e=e.parentNode;)if(e==b){b=c;break}return b}return b=g.startContainer,1==b.nodeType&&b.hasChildNodes()&&(a&&g.collapsed||(b=b.childNodes[Math.min(b.childNodes.length-1,g.startOffset)])),b&&3==b.nodeType?b.parentNode:b},getEnd:function(a){var b,c,d=this,e=d.getRng();return e.duplicate||e.item?e.item?e.item(0):(e=e.duplicate(),e.collapse(0),b=e.parentElement(),b.ownerDocument!==d.dom.doc&&(b=d.dom.getRoot()),b&&"BODY"==b.nodeName?b.lastChild||b:b):(b=e.endContainer,c=e.endOffset,1==b.nodeType&&b.hasChildNodes()&&(a&&e.collapsed||(b=b.childNodes[c>0?c-1:c])),b&&3==b.nodeType?b.parentNode:b)},getBookmark:function(a,b){return this.bookmarkManager.getBookmark(a,b)},moveToBookmark:function(a){return this.bookmarkManager.moveToBookmark(a)},select:function(a,b){var c,d=this,e=d.dom,f=e.createRng();if(d.lastFocusBookmark=null,a){if(!b&&d.controlSelection.controlSelect(a))return;c=e.nodeIndex(a),f.setStart(a.parentNode,c),f.setEnd(a.parentNode,c+1),b&&(d._moveEndPoint(f,a,!0),d._moveEndPoint(f,a)),d.setRng(f)}return a},isCollapsed:function(){var a=this,b=a.getRng(),c=a.getSel();return!(!b||b.item)&&(b.compareEndPoints?0===b.compareEndPoints("StartToEnd",b):!c||b.collapsed)},collapse:function(a){var b,c=this,d=c.getRng();d.item&&(b=d.item(0),d=c.win.document.body.createTextRange(),d.moveToElementText(b)),d.collapse(!!a),c.setRng(d)},getSel:function(){var a=this.win;return a.getSelection?a.getSelection():a.document.selection},getRng:function(a){function b(a,b,c){try{return b.compareBoundaryPoints(a,c)}catch(a){return-1}}var c,d,e,f,g,h,i=this;if(!i.win)return null;if(f=i.win.document,"undefined"==typeof f||null===f)return null;if(!a&&i.lastFocusBookmark){var j=i.lastFocusBookmark;return j.startContainer?(d=f.createRange(),d.setStart(j.startContainer,j.startOffset),d.setEnd(j.endContainer,j.endOffset)):d=j,d}if(a&&i.tridentSel)return i.tridentSel.getRangeAt(0);try{(c=i.getSel())&&(d=c.rangeCount>0?c.getRangeAt(0):c.createRange?c.createRange():f.createRange())}catch(a){}if(h=i.editor.fire("GetSelectionRange",{range:d}),h.range!==d)return h.range;if(r&&d&&d.setStart&&f.selection){try{g=f.selection.createRange()}catch(a){}g&&g.item&&(e=g.item(0),d=f.createRange(),d.setStartBefore(e),d.setEndAfter(e))}return d||(d=f.createRange?f.createRange():f.body.createTextRange()),d.setStart&&9===d.startContainer.nodeType&&d.collapsed&&(e=i.dom.getRoot(),d.setStart(e,0),d.setEnd(e,0)),i.selectedRange&&i.explicitRange&&(0===b(d.START_TO_START,d,i.selectedRange)&&0===b(d.END_TO_END,d,i.selectedRange)?d=i.explicitRange:(i.selectedRange=null,i.explicitRange=null)),d},setRng:function(a,b){var c,d,e,f=this;if(t(a))if(a.select){f.explicitRange=null;try{a.select()}catch(a){}}else if(f.tridentSel){if(a.cloneRange)try{f.tridentSel.addRange(a)}catch(a){}}else{if(c=f.getSel(),e=f.editor.fire("SetSelectionRange",{range:a,forward:b}),a=e.range,c){f.explicitRange=a;try{c.removeAllRanges(),c.addRange(a)}catch(a){}b===!1&&c.extend&&(c.collapse(a.endContainer,a.endOffset),c.extend(a.startContainer,a.startOffset)),f.selectedRange=c.rangeCount>0?c.getRangeAt(0):null}a.collapsed||a.startContainer!==a.endContainer||!c.setBaseAndExtent||k.ie||a.endOffset-a.startOffset<2&&a.startContainer.hasChildNodes()&&(d=a.startContainer.childNodes[a.startOffset],d&&"IMG"===d.tagName&&(c.setBaseAndExtent(a.startContainer,a.startOffset,a.endContainer,a.endOffset),c.anchorNode===a.startContainer&&c.focusNode===a.endContainer||c.setBaseAndExtent(d,0,d,1))),f.editor.fire("AfterSetSelectionRange",{range:a,forward:b})}},setNode:function(a){var b=this;return b.setContent(b.dom.getOuterHTML(a)),a},getNode:function(){function a(a,b){for(var c=a;a&&3===a.nodeType&&0===a.length;)a=b?a.nextSibling:a.previousSibling;return a||c}var b,c,d,e,f,g=this,h=g.getRng(),i=g.dom.getRoot();return h?(c=h.startContainer,d=h.endContainer,e=h.startOffset,f=h.endOffset,h.setStart?(b=h.commonAncestorContainer,!h.collapsed&&(c==d&&f-e<2&&c.hasChildNodes()&&(b=c.childNodes[e]),3===c.nodeType&&3===d.nodeType&&(c=c.length===e?a(c.nextSibling,!0):c.parentNode,d=0===f?a(d.previousSibling,!1):d.parentNode,c&&c===d))?c:b&&3==b.nodeType?b.parentNode:b):(b=h.item?h.item(0):h.parentElement(),b.ownerDocument!==g.win.document&&(b=i),b)):i},getSelectedBlocks:function(a,b){var c,d,e=this,f=e.dom,g=[];if(d=f.getRoot(),a=f.getParent(a||e.getStart(),f.isBlock),b=f.getParent(b||e.getEnd(),f.isBlock),a&&a!=d&&g.push(a),a&&b&&a!=b){c=a;for(var h=new i(a,d);(c=h.next())&&c!=b;)f.isBlock(c)&&g.push(c)}return b&&a!=b&&b!=d&&g.push(b),g},isForward:function(){var a,b,c=this.dom,d=this.getSel();return!(d&&d.anchorNode&&d.focusNode)||(a=c.createRng(),a.setStart(d.anchorNode,d.anchorOffset),a.collapse(!0),b=c.createRng(),b.setStart(d.focusNode,d.focusOffset),b.collapse(!0),a.compareBoundaryPoints(a.START_TO_START,b)<=0)},normalize:function(){var a=this,b=a.getRng();return k.range&&new g(a.dom).normalize(b)&&a.setRng(b,a.isForward()),b},selectorChanged:function(a,b){var c,d=this;return d.selectorChangedData||(d.selectorChangedData={},c={},d.editor.on("NodeChange",function(a){var b=a.element,e=d.dom,f=e.getParents(b,null,e.getRoot()),g={};p(d.selectorChangedData,function(a,b){p(f,function(d){if(e.is(d,b))return c[b]||(p(a,function(a){a(!0,{node:d,selector:b,parents:f})}),c[b]=a),g[b]=a,!1})}),p(c,function(a,d){g[d]||(delete c[d],p(a,function(a){a(!1,{node:b,selector:d,parents:f})}))})})),d.selectorChangedData[a]||(d.selectorChangedData[a]=[]),d.selectorChangedData[a].push(b),d},getScrollContainer:function(){for(var a,b=this.dom.getRoot();b&&"BODY"!=b.nodeName;){if(b.scrollHeight>b.clientHeight){a=b;break}b=b.parentNode}return a},scrollIntoView:function(a,b){h.scrollIntoView(this.editor,a,b)},placeCaretAt:function(a,b){this.setRng(g.getCaretRangeFromPoint(a,b,this.editor.getDoc()))},_moveEndPoint:function(a,b,c){var d=b,e=new i(b,d),f=this.dom.schema.getNonEmptyElements();do{if(3==b.nodeType&&0!==q(b.nodeValue).length)return void(c?a.setStart(b,0):a.setEnd(b,b.nodeValue.length));if(f[b.nodeName]&&!/^(TD|TH)$/.test(b.nodeName))return void(c?a.setStartBefore(b):"BR"==b.nodeName?a.setEndBefore(b):a.setEndAfter(b));if(k.ie&&k.ie<11&&this.dom.isBlock(b)&&this.dom.isEmpty(b))return void(c?a.setStart(b,0):a.setEnd(b,0))}while(b=c?e.next():e.prev());"BODY"==d.nodeName&&(c?a.setStart(d,0):a.setEnd(d,d.childNodes.length))},getBoundingClientRect:function(){var a=this.getRng();return a.collapsed?c.fromRangeStart(a).getClientRects()[0]:a.getBoundingClientRect()},destroy:function(){this.win=null,this.controlSelection.destroy()}},o}),g("1v",["r","9"],function(a,b){function c(b){this.compare=function(c,e){function f(a){var c={};return d(b.getAttribs(a),function(d){var e=d.nodeName.toLowerCase();0!==e.indexOf("_")&&"style"!==e&&0!==e.indexOf("data-")&&(c[e]=b.getAttrib(a,e))}),c}function g(a,b){var c,d;for(d in a)if(a.hasOwnProperty(d)){if(c=b[d],"undefined"==typeof c)return!1;if(a[d]!=c)return!1;delete b[d]}for(d in b)if(b.hasOwnProperty(d))return!1;return!0}return c.nodeName==e.nodeName&&(!!g(f(c),f(e))&&(!!g(b.parseStyle(b.getAttrib(c,"style")),b.parseStyle(b.getAttrib(e,"style")))&&(!a.isBookmarkNode(c)&&!a.isBookmarkNode(e))))}}var d=b.each;return c}),g("1x",["e","9","j"],function(a,b,c){function d(a,d){function e(a,b){b.classes.length&&j.addClass(a,b.classes.join(" ")),j.setAttribs(a,b.attrs)}function f(a){var b;return k="string"==typeof a?{name:a,classes:[],attrs:{}}:a,b=j.create(k.name),e(b,k),b}function g(a,c){var d="string"!=typeof a?a.nodeName.toLowerCase():a,e=m.getElementRule(d),f=e&&e.parentsRequired;return!(!f||!f.length)&&(c&&b.inArray(f,c)!==-1?c:f[0])}function h(a,c,d){var e,i,k,l=c.length>0&&c[0],m=l&&l.name;if(k=g(a,m))m==k?(i=c[0],c=c.slice(1)):i=k;else if(l)i=c[0],c=c.slice(1);else if(!d)return a;return i&&(e=f(i),e.appendChild(a)),d&&(e||(e=j.create("div"),e.appendChild(a)),b.each(d,function(b){var c=f(b);e.insertBefore(c,a)})),h(e,c,i&&i.siblings)}var i,k,l,m=d&&d.schema||new c({});return a&&a.length?(k=a[0],i=f(k),l=j.create("div"),l.appendChild(h(i,a.slice(1),k.siblings)),l):""}function e(a,b){return d(g(a),b)}function f(a){var c,d={classes:[],attrs:{}};return a=d.selector=b.trim(a),"*"!==a&&(c=a.replace(/(?:([#\.]|::?)([\w\-]+)|(\[)([^\]]+)\]?)/g,function(a,c,e,f,g){switch(c){case"#":d.attrs.id=e;break;case".":d.classes.push(e);break;case":":b.inArray("checked disabled enabled read-only required".split(" "),e)!==-1&&(d.attrs[e]=e)}if("["==f){var h=g.match(/([\w\-]+)(?:\=\"([^\"]+))?/);h&&(d.attrs[h[1]]=h[2])}return""})),d.name=c||"div",d}function g(a){return a&&"string"==typeof a?(a=a.split(/\s*,\s*/)[0],a=a.replace(/\s*(~\+|~|\+|>)\s*/g,"$1"),b.map(a.split(/(?:>|\s+(?![^\[\]]+\]))/),function(a){var c=b.map(a.split(/(?:~\+|~|\+)/),f),d=c.pop();return c.length&&(d.siblings=c),d}).reverse()):[]}function h(a,b){function c(a){return a.replace(/%(\w+)/g,"")}var e,f,h,k,l,m,n="";if(m=a.settings.preview_styles,m===!1)return"";if("string"!=typeof m&&(m="font-family font-size font-weight font-style text-decoration text-transform color background-color border border-radius outline text-shadow"),"string"==typeof b){if(b=a.formatter.get(b),!b)return;b=b[0]}return"preview"in b&&(m=b.preview,m===!1)?"":(e=b.block||b.inline||"span",k=g(b.selector),k.length?(k[0].name||(k[0].name=e),e=b.selector,f=d(k,a)):f=d([e],a),h=j.select(e,f)[0]||f.firstChild,i(b.styles,function(a,b){a=c(a),a&&j.setStyle(h,b,a)}),i(b.attributes,function(a,b){a=c(a),a&&j.setAttrib(h,b,a)}),i(b.classes,function(a){a=c(a),j.hasClass(h,a)||j.addClass(h,a)}),a.fire("PreviewFormats"),j.setStyles(f,{position:"absolute",left:-65535}),a.getBody().appendChild(f),l=j.getStyle(a.getBody(),"fontSize",!0),l=/px$/.test(l)?parseInt(l,10):0,i(m.split(" "),function(b){var c=j.getStyle(h,b,!0);if(!("background-color"==b&&/transparent|rgba\s*\([^)]+,\s*0\)/.test(c)&&(c=j.getStyle(a.getBody(),b,!0),"#ffffff"==j.toHex(c).toLowerCase())||"color"==b&&"#000000"==j.toHex(c).toLowerCase())){if("font-size"==b&&/em|%$/.test(c)){if(0===l)return;c=parseFloat(c,10)/(/%$/.test(c)?100:1),c=c*l+"px"}"border"==b&&c&&(n+="padding:0 2px;"),n+=b+":"+c+";"}}),a.fire("AfterPreviewFormats"),j.remove(f),n)}var i=b.each,j=a.DOM;return{getCssText:h,parseSelector:g,selectorToHtml:e}}),g("1y",["1g","1j","a"],function(a,b,c){function d(a,b){var c=f[a];c||(f[a]=c=[]),f[a].push(b)}function e(a,b){h(f[a],function(a){a(b)})}var f={},g=a.filter,h=a.each;return d("pre",function(d){function e(b){return i(b.previousSibling)&&a.indexOf(j,b.previousSibling)!=-1}function f(a,b){c(b).remove(),c(a).append("<br><br>").append(b.childNodes)}var i,j,k=d.selection.getRng();i=b.matchNodeNames("pre"),k.collapsed||(j=d.selection.getSelectedBlocks(),h(g(g(j,i),e),function(a){f(a.previousSibling,a)}))}),{postProcess:e}}),g("t",["c","h","r","1v","1j","1w","9","1x","1y"],function(a,b,c,d,e,f,g,h,i){return function(j){function k(a){return a.nodeType&&(a=a.nodeName),!!j.schema.getTextBlockElements()[a.toLowerCase()]}function l(a){return/^(TH|TD)$/.test(a.nodeName)}function m(a){return a&&/^(IMG)$/.test(a.nodeName)}function n(a,b){return da.getParents(a,b,da.getRoot())}function o(a){return 1===a.nodeType&&"_mce_caret"===a.id}function p(){s({valigntop:[{selector:"td,th",styles:{verticalAlign:"top"}}],valignmiddle:[{selector:"td,th",styles:{verticalAlign:"middle"}}],valignbottom:[{selector:"td,th",styles:{verticalAlign:"bottom"}}],alignleft:[{selector:"figure.image",collapsed:!1,classes:"align-left",ceFalseOverride:!0,preview:"font-family font-size"},{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"left"},inherit:!1,preview:!1,defaultBlock:"div"},{selector:"img,table",collapsed:!1,styles:{"float":"left"},preview:"font-family font-size"}],aligncenter:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"center"},inherit:!1,preview:!1,defaultBlock:"div"},{selector:"figure.image",collapsed:!1,classes:"align-center",ceFalseOverride:!0,preview:"font-family font-size"},{selector:"img",collapsed:!1,styles:{display:"block",marginLeft:"auto",marginRight:"auto"},preview:!1},{selector:"table",collapsed:!1,styles:{marginLeft:"auto",marginRight:"auto"},preview:"font-family font-size"}],alignright:[{selector:"figure.image",collapsed:!1,classes:"align-right",ceFalseOverride:!0,preview:"font-family font-size"},{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"right"},inherit:!1,preview:"font-family font-size",defaultBlock:"div"},{selector:"img,table",collapsed:!1,styles:{"float":"right"},preview:"font-family font-size"}],alignjustify:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"justify"},inherit:!1,defaultBlock:"div",preview:"font-family font-size"}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:!0},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:!0},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},links:!0,remove_similar:!0,clear_child_styles:!0},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},links:!0,remove_similar:!0,clear_child_styles:!0},fontname:{inline:"span",styles:{fontFamily:"%value"},clear_child_styles:!0},fontsize:{inline:"span",styles:{fontSize:"%value"},clear_child_styles:!0},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},code:{inline:"code"},link:{inline:"a",selector:"a",remove:"all",split:!0,deep:!0,onmatch:function(){return!0},onformat:function(a,b,c){qa(c,function(b,c){da.setAttrib(a,c,b)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins",remove:"all",split:!0,expand:!1,block_expand:!0,deep:!0},{selector:"span",attributes:["style","class"],remove:"empty",split:!0,expand:!1,deep:!0},{selector:"*",attributes:["style","class"],split:!1,expand:!1,deep:!0}]}),qa("p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp".split(/\s/),function(a){s(a,{block:a,remove:"all"})}),s(j.settings.formats)}function q(){j.addShortcut("meta+b","bold_desc","Bold"),j.addShortcut("meta+i","italic_desc","Italic"),j.addShortcut("meta+u","underline_desc","Underline");for(var a=1;a<=6;a++)j.addShortcut("access+"+a,"",["FormatBlock",!1,"h"+a]);j.addShortcut("access+7","",["FormatBlock",!1,"p"]),j.addShortcut("access+8","",["FormatBlock",!1,"div"]),j.addShortcut("access+9","",["FormatBlock",!1,"address"])}function r(a){return a?ca[a]:ca}function s(a,b){a&&("string"!=typeof a?qa(a,function(a,b){s(b,a)}):(b=b.length?b:[b],qa(b,function(a){a.deep===_&&(a.deep=!a.selector),a.split===_&&(a.split=!a.selector||a.inline),a.remove===_&&a.selector&&!a.inline&&(a.remove="none"),a.selector&&a.inline&&(a.mixed=!0,a.block_expand=!0),"string"==typeof a.classes&&(a.classes=a.classes.split(/\s+/))}),ca[a]=b))}function t(a){return a&&ca[a]&&delete ca[a],ca}function u(a,b){var c=r(b);if(c)for(var d=0;d<c.length;d++)if(c[d].inherit===!1&&da.is(a,c[d].selector))return!0;return!1}function v(a){var b;return j.dom.getParent(a,function(a){return b=j.dom.getStyle(a,"text-decoration"),b&&"none"!==b}),b}function w(a){var b;1===a.nodeType&&a.parentNode&&1===a.parentNode.nodeType&&(b=v(a.parentNode),j.dom.getStyle(a,"color")&&b?j.dom.setStyle(a,"text-decoration",b):j.dom.getStyle(a,"text-decoration")===b&&j.dom.setStyle(a,"text-decoration",null))}function x(b,c,d){function e(a,b){if(b=b||p,a){if(b.onformat&&b.onformat(a,b,c,d),qa(b.styles,function(b,d){da.setStyle(a,d,O(b,c))}),b.styles){var e=da.getAttrib(a,"style");e&&a.setAttribute("data-mce-style",e)}qa(b.attributes,function(b,d){da.setAttrib(a,d,O(b,c))}),qa(b.classes,function(b){b=O(b,c),da.hasClass(a,b)||da.addClass(a,b)})}}function f(a,b){var c=!1;return!!p.selector&&(qa(a,function(a){if(!("collapsed"in a&&a.collapsed!==q))return da.is(b,a.selector)&&!o(b)?(e(b,a),c=!0,!1):void 0}),c)}function g(){function b(b,c){var e=new a(c);for(d=e.prev2();d;d=e.prev2()){if(3==d.nodeType&&d.data.length>0)return d;if(d.childNodes.length>1||d==b||"BR"==d.tagName)return d}}var c=j.selection.getRng(),e=c.startContainer,f=c.endContainer;if(e!=f&&0===c.endOffset){var g=b(e,f),h=3==g.nodeType?g.data.length:g.childNodes.length;c.setEnd(g,h)}return c}function h(a,d,g){var h,i,j=[],l=!0;h=p.inline||p.block,i=da.create(h),e(i),fa.walk(a,function(a){function d(a){var q,r,s,t;if(t=l,q=a.nodeName.toLowerCase(),r=a.parentNode.nodeName.toLowerCase(),1===a.nodeType&&oa(a)&&(t=l,l="true"===oa(a),s=!0),H(q,"br"))return m=0,void(p.block&&da.remove(a));if(p.wrapper&&A(a,b,c))return void(m=0);if(l&&!s&&p.block&&!p.wrapper&&k(q)&&ga(r,h))return a=da.rename(a,h),e(a),j.push(a),void(m=0);if(p.selector){var u=f(n,a);if(!p.inline||u)return void(m=0)}!l||s||!ga(h,q)||!ga(r,h)||!g&&3===a.nodeType&&1===a.nodeValue.length&&65279===a.nodeValue.charCodeAt(0)||o(a)||p.inline&&ha(a)?(m=0,qa(ra(a.childNodes),d),s&&(l=t),m=0):(m||(m=da.clone(i,ma),a.parentNode.insertBefore(m,a),j.push(m)),m.appendChild(a))}var m;qa(a,d)}),p.links===!0&&qa(j,function(a){function b(a){"A"===a.nodeName&&e(a,p),qa(ra(a.childNodes),b)}b(a)}),qa(j,function(a){function d(a){var b=0;return qa(a.childNodes,function(a){P(a)||pa(a)||b++}),b}function f(a){var b=!1;return qa(a.childNodes,function(a){if(J(a))return b=a,!1}),b}function g(a,b){do{if(1!==d(a))break;if(a=f(a),!a)break;if(b(a))return a}while(a);return null}function h(a){var b,c;return b=f(a),b&&!pa(b)&&G(b,p)&&(c=da.clone(b,ma),e(c),da.replace(c,a,na),da.remove(b,1)),c||a}var i;if(i=d(a),(j.length>1||!ha(a))&&0===i)return void da.remove(a,1);if(p.inline||p.wrapper){if(p.exact||1!==i||(a=h(a)),qa(n,function(b){qa(da.select(b.inline,a),function(a){J(a)&&T(b,c,a,b.exact?a:null)}),ua(b,a)}),A(a.parentNode,b,c)&&T(p,c,a)&&(a=0),p.merge_with_parents&&da.getParent(a.parentNode,function(d){if(A(d,b,c))return T(p,c,a)&&(a=0),na}),a&&!ha(a)&&!M(a,"fontSize")){var k=g(a,K("fontSize"));k&&x("fontsize",{value:M(k,"fontSize")},a)}a&&p.merge_siblings!==!1&&(a=W(V(a),a),a=W(a,V(a,na)))}})}var l,m,n=r(b),p=n[0],q=!d&&ea.isCollapsed();if("false"!==oa(ea.getNode())){if(p){if(d)d.nodeType?f(n,d)||(m=da.createRng(),m.setStartBefore(d),m.setEndAfter(d),h(R(m,n),null,!0)):h(d,null,!0);else if(q&&p.inline&&!da.select("td[data-mce-selected],th[data-mce-selected]").length)Y("apply",b,c);else{var s=j.selection.getNode();ia||!n[0].defaultBlock||da.getParent(s,da.isBlock)||x(n[0].defaultBlock),j.selection.setRng(g()),l=ea.getBookmark(),h(R(ea.getRng(na),n),l),p.styles&&((p.styles.color||p.styles.textDecoration)&&(sa(s,w,"childNodes"),w(s)),p.styles.backgroundColor&&I(s,K("fontSize"),L("backgroundColor",O(p.styles.backgroundColor,c)))),ea.moveToBookmark(l),Z(ea.getRng(na)),j.nodeChanged()}i.postProcess(b,j)}}else{d=ea.getNode();for(var t=0,u=n.length;t<u;t++)if(n[t].ceFalseOverride&&da.is(d,n[t].selector))return void e(d,n[t])}}function y(a,b,c,d){function e(a){var c,d,f,g,h;if(1===a.nodeType&&oa(a)&&(g=s,s="true"===oa(a),h=!0),c=ra(a.childNodes),s&&!h)for(d=0,f=p.length;d<f&&!T(p[d],b,a,a);d++);if(q.deep&&c.length){for(d=0,f=c.length;d<f;d++)e(c[d]);h&&(s=g)}}function f(c){var e;return qa(n(c.parentNode).reverse(),function(c){var f;e||"_start"==c.id||"_end"==c.id||(f=A(c,a,b,d),f&&f.split!==!1&&(e=c))}),e}function g(a,c,d,e){var f,g,h,i,j,k;if(a){for(k=a.parentNode,f=c.parentNode;f&&f!=k;f=f.parentNode){for(g=da.clone(f,ma),j=0;j<p.length;j++)if(T(p[j],b,g,g)){g=0;break}g&&(h&&g.appendChild(h),i||(i=g),h=g)}!e||q.mixed&&ha(a)||(c=da.split(a,c)),h&&(d.parentNode.insertBefore(h,d),i.appendChild(d))}return c}function h(a){return g(f(a),a,a,!0)}function i(a){var b=da.get(a?"_start":"_end"),c=b[a?"firstChild":"lastChild"];return pa(c)&&(c=c[a?"firstChild":"lastChild"]),3==c.nodeType&&0===c.data.length&&(c=a?b.previousSibling||b.nextSibling:b.nextSibling||b.previousSibling),da.remove(b,!0),c}function k(a){var b,c,d=a.commonAncestorContainer;if(a=R(a,p,na),q.split){if(b=X(a,na),c=X(a),b!=c){if(/^(TR|TH|TD)$/.test(b.nodeName)&&b.firstChild&&(b="TR"==b.nodeName?b.firstChild.firstChild||b:b.firstChild||b),d&&/^T(HEAD|BODY|FOOT|R)$/.test(d.nodeName)&&l(c)&&c.firstChild&&(c=c.firstChild||c),da.isChildOf(b,c)&&!ha(c)&&!l(b)&&!l(c))return b=Q(b,"span",{id:"_start","data-mce-type":"bookmark"}),h(b),void(b=i(na));b=Q(b,"span",{ +id:"_start","data-mce-type":"bookmark"}),c=Q(c,"span",{id:"_end","data-mce-type":"bookmark"}),h(b),h(c),b=i(na),c=i()}else b=c=h(b);a.startContainer=b.parentNode?b.parentNode:b,a.startOffset=ja(b),a.endContainer=c.parentNode?c.parentNode:c,a.endOffset=ja(c)+1}fa.walk(a,function(a){qa(a,function(a){e(a),1===a.nodeType&&"underline"===j.dom.getStyle(a,"text-decoration")&&a.parentNode&&"underline"===v(a.parentNode)&&T({deep:!1,exact:!0,inline:"span",styles:{textDecoration:"underline"}},null,a)})})}var m,o,p=r(a),q=p[0],s=!0;if(c)return void(c.nodeType?(o=da.createRng(),o.setStartBefore(c),o.setEndAfter(c),k(o)):k(c));if("false"!==oa(ea.getNode()))ea.isCollapsed()&&q.inline&&!da.select("td[data-mce-selected],th[data-mce-selected]").length?Y("remove",a,b,d):(m=ea.getBookmark(),k(ea.getRng(na)),ea.moveToBookmark(m),q.inline&&B(a,b,ea.getStart())&&Z(ea.getRng(!0)),j.nodeChanged());else{c=ea.getNode();for(var t=0,u=p.length;t<u&&(!p[t].ceFalseOverride||!T(p[t],b,c,c));t++);}}function z(a,b,c){var d=r(a);!B(a,b,c)||"toggle"in d[0]&&!d[0].toggle?x(a,b,c):y(a,b,c)}function A(a,b,c,d){function e(a,b,e){var f,g,h,i=b[e];if(b.onmatch)return b.onmatch(a,b,e);if(i)if(i.length===_){for(f in i)if(i.hasOwnProperty(f)){if(g="attributes"===e?da.getAttrib(a,f):M(a,f),d&&!g&&!b.exact)return;if((!d||b.exact)&&!H(g,N(O(i[f],c),f)))return}}else for(h=0;h<i.length;h++)if("attributes"===e?da.getAttrib(a,i[h]):M(a,i[h]))return b;return b}var f,g,h,i=r(b);if(i&&a)for(g=0;g<i.length;g++)if(f=i[g],G(a,f)&&e(a,f,"attributes")&&e(a,f,"styles")){if(h=f.classes)for(g=0;g<h.length;g++)if(!da.hasClass(a,h[g]))return;return f}}function B(a,b,c){function d(c){var d=da.getRoot();return c!==d&&(c=da.getParent(c,function(c){return!!u(c,a)||(c.parentNode===d||!!A(c,a,b,!0))}),A(c,a,b))}var e;return c?d(c):(c=ea.getNode(),d(c)?na:(e=ea.getStart(),e!=c&&d(e)?na:ma))}function C(a,b){var c,d=[],e={};return c=ea.getStart(),da.getParent(c,function(c){var f,g;for(f=0;f<a.length;f++)g=a[f],!e[g]&&A(c,g,b)&&(e[g]=!0,d.push(g))},da.getRoot()),d}function D(a){var b,c,d,e,f,g=r(a);if(g)for(b=ea.getStart(),c=n(b),e=g.length-1;e>=0;e--){if(f=g[e].selector,!f||g[e].defaultBlock)return na;for(d=c.length-1;d>=0;d--)if(da.is(c[d],f))return na}return ma}function E(a,b,c){var d;return $||($={},d={},j.on("NodeChange",function(a){var b=n(a.element),c={};b=g.grep(b,function(a){return 1==a.nodeType&&!a.getAttribute("data-mce-bogus")}),qa($,function(a,e){qa(b,function(f){return A(f,e,{},a.similar)?(d[e]||(qa(a,function(a){a(!0,{node:f,format:e,parents:b})}),d[e]=a),c[e]=a,!1):!u(f,e)&&void 0})}),qa(d,function(e,f){c[f]||(delete d[f],qa(e,function(c){c(!1,{node:a.element,format:f,parents:b})}))})})),qa(a.split(","),function(a){$[a]||($[a]=[],$[a].similar=c),$[a].push(b)}),this}function F(a){return h.getCssText(j,a)}function G(a,b){return H(a,b.inline)?na:H(a,b.block)?na:b.selector?1==a.nodeType&&da.is(a,b.selector):void 0}function H(a,b){return a=a||"",b=b||"",a=""+(a.nodeName||a),b=""+(b.nodeName||b),a.toLowerCase()==b.toLowerCase()}function I(a,b,c){qa(a.childNodes,function(a){J(a)&&(b(a)&&c(a),a.hasChildNodes()&&I(a,b,c))})}function J(a){return a&&1===a.nodeType&&!pa(a)&&!o(a)&&!e.isBogus(a)}function K(a){return f.curry(function(a,b){return!(!b||!M(b,a))},a)}function L(a,b){return f.curry(function(a,b,c){da.setStyle(c,a,b)},a,b)}function M(a,b){return N(da.getStyle(a,b),b)}function N(a,b){return"color"!=b&&"backgroundColor"!=b||(a=da.toHex(a)),"fontWeight"==b&&700==a&&(a="bold"),"fontFamily"==b&&(a=a.replace(/[\'\"]/g,"").replace(/,\s+/g,",")),""+a}function O(a,b){return"string"!=typeof a?a=a(b):b&&(a=a.replace(/%(\w+)/g,function(a,c){return b[c]||a})),a}function P(a){return a&&3===a.nodeType&&/^([\t \r\n]+|)$/.test(a.nodeValue)}function Q(a,b,c){var d=da.create(b,c);return a.parentNode.insertBefore(d,a),d.appendChild(a),d}function R(b,c,d){function e(a){function b(a){return"BR"==a.nodeName&&a.getAttribute("data-mce-bogus")&&!a.nextSibling}var d,e,f,g,h;if(d=e=a?q:s,g=a?"previousSibling":"nextSibling",h=da.getRoot(),3==d.nodeType&&!P(d)&&(a?r>0:t<d.nodeValue.length))return d;for(;;){if(!c[0].block_expand&&ha(e))return e;for(f=e[g];f;f=f[g])if(!pa(f)&&!P(f)&&!b(f))return e;if(e==h||e.parentNode==h){d=e;break}e=e.parentNode}return d}function f(a,b){for(b===_&&(b=3===a.nodeType?a.length:a.childNodes.length);a&&a.hasChildNodes();)a=a.childNodes[b],a&&(b=3===a.nodeType?a.length:a.childNodes.length);return{node:a,offset:b}}function g(a){for(var b=a;b;){if(1===b.nodeType&&oa(b))return"false"===oa(b)?b:a;b=b.parentNode}return a}function h(b,c,e){function f(a,b){var c,f,g=a.nodeValue;return"undefined"==typeof b&&(b=e?g.length:0),e?(c=g.lastIndexOf(" ",b),f=g.lastIndexOf("\xa0",b),c=c>f?c:f,c===-1||d||c++):(c=g.indexOf(" ",b),f=g.indexOf("\xa0",b),c=c!==-1&&(f===-1||c<f)?c:f),c}var g,h,i,k;if(3===b.nodeType){if(i=f(b,c),i!==-1)return{container:b,offset:i};k=b}for(g=new a(b,da.getParent(b,ha)||j.getBody());h=g[e?"prev":"next"]();)if(3===h.nodeType){if(k=h,i=f(h),i!==-1)return{container:h,offset:i}}else if(ha(h))break;if(k)return c=e?0:k.length,{container:k,offset:c}}function i(a,d){var e,f,g,h;for(3==a.nodeType&&0===a.nodeValue.length&&a[d]&&(a=a[d]),e=n(a),f=0;f<e.length;f++)for(g=0;g<c.length;g++)if(h=c[g],!("collapsed"in h&&h.collapsed!==b.collapsed)&&da.is(e[f],h.selector))return e[f];return a}function l(a,b){var d,e=da.getRoot();if(c[0].wrapper||(d=da.getParent(a,c[0].block,e)),d||(d=da.getParent(3==a.nodeType?a.parentNode:a,function(a){return a!=e&&k(a)})),d&&c[0].wrapper&&(d=n(d,"ul,ol").reverse()[0]||d),!d)for(d=a;d[b]&&!ha(d[b])&&(d=d[b],!H(d,"br")););return d||a}var m,o,p,q=b.startContainer,r=b.startOffset,s=b.endContainer,t=b.endOffset;if(1==q.nodeType&&q.hasChildNodes()&&(m=q.childNodes.length-1,q=q.childNodes[r>m?m:r],3==q.nodeType&&(r=0)),1==s.nodeType&&s.hasChildNodes()&&(m=s.childNodes.length-1,s=s.childNodes[t>m?m:t-1],3==s.nodeType&&(t=s.nodeValue.length)),q=g(q),s=g(s),(pa(q.parentNode)||pa(q))&&(q=pa(q)?q:q.parentNode,q=q.nextSibling||q,3==q.nodeType&&(r=0)),(pa(s.parentNode)||pa(s))&&(s=pa(s)?s:s.parentNode,s=s.previousSibling||s,3==s.nodeType&&(t=s.length)),c[0].inline&&(b.collapsed&&(p=h(q,r,!0),p&&(q=p.container,r=p.offset),p=h(s,t),p&&(s=p.container,t=p.offset)),o=f(s,t),o.node)){for(;o.node&&0===o.offset&&o.node.previousSibling;)o=f(o.node.previousSibling);o.node&&o.offset>0&&3===o.node.nodeType&&" "===o.node.nodeValue.charAt(o.offset-1)&&o.offset>1&&(s=o.node,s.splitText(o.offset-1))}return(c[0].inline||c[0].block_expand)&&(c[0].inline&&3==q.nodeType&&0!==r||(q=e(!0)),c[0].inline&&3==s.nodeType&&t!==s.nodeValue.length||(s=e())),c[0].selector&&c[0].expand!==ma&&!c[0].inline&&(q=i(q,"previousSibling"),s=i(s,"nextSibling")),(c[0].block||c[0].selector)&&(q=l(q,"previousSibling"),s=l(s,"nextSibling"),c[0].block&&(ha(q)||(q=e(!0)),ha(s)||(s=e()))),1==q.nodeType&&(r=ja(q),q=q.parentNode),1==s.nodeType&&(t=ja(s)+1,s=s.parentNode),{startContainer:q,startOffset:r,endContainer:s,endOffset:t}}function S(a,b){return b.links&&"A"==a.tagName}function T(a,b,c,d){var e,f,g;if(!G(c,a)&&!S(c,a))return ma;if("all"!=a.remove)for(qa(a.styles,function(e,f){e=N(O(e,b),f),"number"==typeof f&&(f=e,d=0),(a.remove_similar||!d||H(M(d,f),e))&&da.setStyle(c,f,""),g=1}),g&&""===da.getAttrib(c,"style")&&(c.removeAttribute("style"),c.removeAttribute("data-mce-style")),qa(a.attributes,function(a,e){var f;if(a=O(a,b),"number"==typeof e&&(e=a,d=0),!d||H(da.getAttrib(d,e),a)){if("class"==e&&(a=da.getAttrib(c,e),a&&(f="",qa(a.split(/\s+/),function(a){/mce\-\w+/.test(a)&&(f+=(f?" ":"")+a)}),f)))return void da.setAttrib(c,e,f);"class"==e&&c.removeAttribute("className"),la.test(e)&&c.removeAttribute("data-mce-"+e),c.removeAttribute(e)}}),qa(a.classes,function(a){a=O(a,b),d&&!da.hasClass(d,a)||da.removeClass(c,a)}),f=da.getAttribs(c),e=0;e<f.length;e++){var h=f[e].nodeName;if(0!==h.indexOf("_")&&0!==h.indexOf("data-"))return ma}return"none"!=a.remove?(U(c,a),na):void 0}function U(a,b){function c(a,b,c){return a=V(a,b,c),!a||"BR"==a.nodeName||ha(a)}var d,e=a.parentNode;b.block&&(ia?e==da.getRoot()&&(b.list_block&&H(a,b.list_block)||qa(ra(a.childNodes),function(a){ga(ia,a.nodeName.toLowerCase())?d?d.appendChild(a):(d=Q(a,ia),da.setAttribs(d,j.settings.forced_root_block_attrs)):d=0})):ha(a)&&!ha(e)&&(c(a,ma)||c(a.firstChild,na,1)||a.insertBefore(da.create("br"),a.firstChild),c(a,na)||c(a.lastChild,ma,1)||a.appendChild(da.create("br")))),b.selector&&b.inline&&!H(b.inline,a)||da.remove(a,1)}function V(a,b,c){if(a)for(b=b?"nextSibling":"previousSibling",a=c?a:a[b];a;a=a[b])if(1==a.nodeType||!P(a))return a}function W(a,b){function c(a,b){for(e=a;e;e=e[b]){if(3==e.nodeType&&0!==e.nodeValue.length)return a;if(1==e.nodeType&&!pa(e))return e}return a}var e,f,g=new d(da);if(a&&b&&(a=c(a,"previousSibling"),b=c(b,"nextSibling"),g.compare(a,b))){for(e=a.nextSibling;e&&e!=b;)f=e,e=e.nextSibling,a.appendChild(f);return da.remove(b),qa(ra(b.childNodes),function(b){a.appendChild(b)}),a}return b}function X(b,c){var d,e,f;return d=b[c?"startContainer":"endContainer"],e=b[c?"startOffset":"endOffset"],1==d.nodeType&&(f=d.childNodes.length-1,!c&&e&&e--,d=d.childNodes[e>f?f:e]),3===d.nodeType&&c&&e>=d.nodeValue.length&&(d=new a(d,j.getBody()).next()||d),3!==d.nodeType||c||0!==e||(d=new a(d,j.getBody()).prev()||d),d}function Y(b,c,d,e){function f(a){var b=da.create("span",{id:p,"data-mce-bogus":!0,style:q?"color:red":""});return a&&b.appendChild(j.getDoc().createTextNode(ka)),b}function g(a,b){for(;a;){if(3===a.nodeType&&a.nodeValue!==ka||a.childNodes.length>1)return!1;b&&1===a.nodeType&&b.push(a),a=a.firstChild}return!0}function h(a){for(;a;){if(a.id===p)return a;a=a.parentNode}}function i(b){var c;if(b)for(c=new a(b,b),b=c.current();b;b=c.next())if(3===b.nodeType)return b}function l(a,b){var c,d;if(a)d=ea.getRng(!0),g(a)?(b!==!1&&(d.setStartBefore(a),d.setEndBefore(a)),da.remove(a)):(c=i(a),c.nodeValue.charAt(0)===ka&&(c.deleteData(0,1),d.startContainer==c&&d.startOffset>0&&d.setStart(c,d.startOffset-1),d.endContainer==c&&d.endOffset>0&&d.setEnd(c,d.endOffset-1)),da.remove(a,1)),ea.setRng(d);else if(a=h(ea.getStart()),!a)for(;a=da.get(p);)l(a,!1)}function m(){var a,b,e,g,j,k,l;a=ea.getRng(!0),g=a.startOffset,k=a.startContainer,l=k.nodeValue,b=h(ea.getStart()),b&&(e=i(b));var m=/[^\s\u00a0\u00ad\u200b\ufeff]/;l&&g>0&&g<l.length&&m.test(l.charAt(g))&&m.test(l.charAt(g-1))?(j=ea.getBookmark(),a.collapse(!0),a=R(a,r(c)),a=fa.split(a),x(c,d,a),ea.moveToBookmark(j)):(b&&e.nodeValue===ka?x(c,d,b):(b=f(!0),e=b.firstChild,a.insertNode(b),g=1,x(c,d,b)),ea.setCursorLocation(e,g))}function n(){var a,b,g,h,i,j,l,m,n=ea.getRng(!0),o=[];for(a=n.startContainer,b=n.startOffset,i=a,3==a.nodeType&&(b!=a.nodeValue.length&&(h=!0),i=i.parentNode);i;){if(A(i,c,d,e)){j=i;break}i.nextSibling&&(h=!0),o.push(i),i=i.parentNode}if(j)if(h)g=ea.getBookmark(),n.collapse(!0),n=R(n,r(c),!0),n=fa.split(n),y(c,d,n),ea.moveToBookmark(g);else{for(m=f(),i=m,l=o.length-1;l>=0;l--)i.appendChild(da.clone(o[l],!1)),i=i.firstChild;i.appendChild(da.doc.createTextNode(ka)),i=i.firstChild;var p=da.getParent(j,k);p&&da.isEmpty(p)?j.parentNode.replaceChild(m,j):da.insertAfter(m,j),ea.setCursorLocation(i,1),da.isEmpty(j)&&da.remove(j)}}function o(){var a;a=h(ea.getStart()),a&&!da.isEmpty(a)&&sa(a,function(a){1!=a.nodeType||a.id===p||da.isEmpty(a)||da.setAttrib(a,"data-mce-bogus",null)},"childNodes")}var p="_mce_caret",q=j.settings.caret_debug;j._hasCaretEvents||(ba=function(){var a,b=[];if(g(h(ea.getStart()),b))for(a=b.length;a--;)da.setAttrib(b[a],"data-mce-bogus","1")},aa=function(a){var b=a.keyCode;l(),8==b&&ea.isCollapsed()&&ea.getStart().innerHTML==ka&&l(h(ea.getStart())),37!=b&&39!=b||l(h(ea.getStart())),o()},j.on("SetContent",function(a){a.selection&&o()}),j._hasCaretEvents=!0),"apply"==b?m():n()}function Z(b){var c,d,e,f=b.startContainer,g=b.startOffset;if((b.startContainer!=b.endContainer||!m(b.startContainer.childNodes[b.startOffset]))&&(3==f.nodeType&&g>=f.nodeValue.length&&(g=ja(f),f=f.parentNode),1==f.nodeType))for(e=f.childNodes,g<e.length?(f=e[g],c=new a(f,da.getParent(f,da.isBlock))):(f=e[e.length-1],c=new a(f,da.getParent(f,da.isBlock)),c.next(!0)),d=c.current();d;d=c.next())if(3==d.nodeType&&!P(d))return b.setStart(d,0),void ea.setRng(b)}var $,_,aa,ba,ca={},da=j.dom,ea=j.selection,fa=new b(da),ga=j.schema.isValidChild,ha=da.isBlock,ia=j.settings.forced_root_block,ja=da.nodeIndex,ka="\ufeff",la=/^(src|href|style)$/,ma=!1,na=!0,oa=da.getContentEditable,pa=c.isBookmarkNode,qa=g.each,ra=g.grep,sa=g.walk,ta=g.extend,ua=function(a,b){a.clear_child_styles&&qa(da.select("*",b),function(b){qa(a.styles,function(a,c){da.setStyle(b,c,"")})})};ta(this,{get:r,register:s,unregister:t,apply:x,remove:y,toggle:z,match:B,matchAll:C,matchNode:A,canApply:D,formatChanged:E,getCssText:F}),p(),q(),j.on("BeforeGetContent",function(a){ba&&"raw"!=a.format&&ba()}),j.on("mouseup keydown",function(a){aa&&aa(a)})}}),g("5e",[],function(){var a=0,b=1,c=2,d=function(d,e){var f=d.length+e.length+2,g=new Array(f),h=new Array(f),i=function(a,b,c){return{start:a,end:b,diag:c}},j=function(f,g,h,i,k){var m=l(f,g,h,i);if(null===m||m.start===g&&m.diag===g-i||m.end===f&&m.diag===f-h)for(var n=f,o=h;n<g||o<i;)n<g&&o<i&&d[n]===e[o]?(k.push([a,d[n]]),++n,++o):g-f>i-h?(k.push([c,d[n]]),++n):(k.push([b,e[o]]),++o);else{j(f,m.start,h,m.start-m.diag,k);for(var p=m.start;p<m.end;++p)k.push([a,d[p]]);j(m.end,g,m.end-m.diag,i,k)}},k=function(a,b,c,f){for(var g=a;g-b<f&&g<c&&d[g]===e[g-b];)++g;return i(a,g,b)},l=function(a,b,c,f){var i=b-a,j=f-c;if(0===i||0===j)return null;var l=i-j,m=j+i,n=(m%2===0?m:m+1)/2;g[1+n]=a,h[1+n]=b+1;for(var o=0;o<=n;++o){for(var p=-o;p<=o;p+=2){var q=p+n;p===-o||p!=o&&g[q-1]<g[q+1]?g[q]=g[q+1]:g[q]=g[q-1]+1;for(var r=g[q],s=r-a+c-p;r<b&&s<f&&d[r]===e[s];)g[q]=++r,++s;if(l%2!=0&&l-o<=p&&p<=l+o&&h[q-l]<=g[q])return k(h[q-l],p+a-c,b,f)}for(p=l-o;p<=l+o;p+=2){for(q=p+n-l,p===l-o||p!=l+o&&h[q+1]<=h[q-1]?h[q]=h[q+1]-1:h[q]=h[q-1],r=h[q]-1,s=r-a+c-p;r>=a&&s>=c&&d[r]===e[s];)h[q]=r--,s--;if(l%2===0&&-o<=p&&p<=o&&h[q]<=g[q+l])return k(h[q],p+a-c,b,f)}}},m=[];return j(0,d.length,0,e.length,m),m};return{KEEP:a,DELETE:c,INSERT:b,diff:d}}),g("4h",["1g","d","5e"],function(a,b,c){var d=function(a){return 1===a.nodeType?a.outerHTML:3===a.nodeType?b.encodeRaw(a.data,!1):8===a.nodeType?"<!--"+a.data+"-->":""},e=function(a){var b,c,d;for(d=document.createElement("div"),b=document.createDocumentFragment(),a&&(d.innerHTML=a);c=d.firstChild;)b.appendChild(c);return b},f=function(a,b,c){var d=e(b);if(a.hasChildNodes()&&c<a.childNodes.length){var f=a.childNodes[c];f.parentNode.insertBefore(d,f)}else a.appendChild(d)},g=function(a,b){if(a.hasChildNodes()&&b<a.childNodes.length){var c=a.childNodes[b];c.parentNode.removeChild(c)}},h=function(b,d){var e=0;a.each(b,function(a){a[0]===c.KEEP?e++:a[0]===c.INSERT?(f(d,a[1],e),e++):a[0]===c.DELETE&&g(d,e)})},i=function(b){return a.filter(a.map(b.childNodes,d),function(a){return a.length>0})},j=function(b,e){var f=a.map(e.childNodes,d);return h(c.diff(f,b),e),e};return{read:i,write:j}}),g("1z",["1g","4h"],function(a,b){var c=function(a){return a.indexOf("</iframe>")!==-1},d=function(a){return{type:"fragmented",fragments:a,content:"",bookmark:null,beforeBookmark:null}},e=function(a){return{type:"complete",fragments:null,content:a,bookmark:null,beforeBookmark:null}},f=function(f){var g,h,i;return g=b.read(f.getBody()),i=a.map(g,function(a){return f.serializer.trimContent(a)}),h=i.join(""),c(h)?d(i):e(h)},g=function(a,c,d){"fragmented"===c.type?b.write(c.fragments,a.getBody()):a.setContent(c.content,{format:"raw"}),a.selection.moveToBookmark(d?c.beforeBookmark:c.bookmark)},h=function(a){return"fragmented"===a.type?a.fragments.join(""):a.content},i=function(a,b){return h(a)===h(b)};return{createFragmentedLevel:d,createCompleteLevel:e,createFromEditor:f,applyToEditor:g,isEq:i}}),g("u",["p","9","1z"],function(a,b,c){return function(a){function d(b){a.setDirty(b)}function e(a){n(!1),i.add({},a)}function f(){i.typing&&(n(!1),i.add())}var g,h,i=this,j=0,k=[],l=0,m=function(){return 0===l},n=function(a){m()&&(i.typing=a)};return a.on("init",function(){i.add()}),a.on("BeforeExecCommand",function(a){var b=a.command;"Undo"!==b&&"Redo"!==b&&"mceRepaint"!==b&&(f(),i.beforeChange())}),a.on("ExecCommand",function(a){var b=a.command;"Undo"!==b&&"Redo"!==b&&"mceRepaint"!==b&&e(a)}),a.on("ObjectResizeStart Cut",function(){i.beforeChange()}),a.on("SaveContent ObjectResized blur",e),a.on("DragEnd",e),a.on("KeyUp",function(b){var f=b.keyCode;b.isDefaultPrevented()||((f>=33&&f<=36||f>=37&&f<=40||45===f||b.ctrlKey)&&(e(),a.nodeChanged()),46!==f&&8!==f||a.nodeChanged(),h&&i.typing&&c.isEq(c.createFromEditor(a),k[0])===!1&&(a.isDirty()===!1&&(d(!0),a.fire("change",{level:k[0],lastLevel:null})),a.fire("TypingUndo"),h=!1,a.nodeChanged()))}),a.on("KeyDown",function(a){var b=a.keyCode;if(!a.isDefaultPrevented()){if(b>=33&&b<=36||b>=37&&b<=40||45===b)return void(i.typing&&e(a));var c=a.ctrlKey&&!a.altKey||a.metaKey;!(b<16||b>20)||224===b||91===b||i.typing||c||(i.beforeChange(),n(!0),i.add({},a),h=!0)}}),a.on("MouseDown",function(a){i.typing&&e(a)}),a.addShortcut("meta+z","","Undo"),a.addShortcut("meta+y,meta+shift+z","","Redo"),a.on("AddUndo Undo Redo ClearUndos",function(b){b.isDefaultPrevented()||a.nodeChanged()}),i={data:k,typing:!1,beforeChange:function(){m()&&(g=a.selection.getBookmark(2,!0))},add:function(e,f){var h,i,l,n=a.settings;if(l=c.createFromEditor(a),e=e||{},e=b.extend(e,l),m()===!1||a.removed)return null;if(i=k[j],a.fire("BeforeAddUndo",{level:e,lastLevel:i,originalEvent:f}).isDefaultPrevented())return null;if(i&&c.isEq(i,e))return null;if(k[j]&&(k[j].beforeBookmark=g),n.custom_undo_redo_levels&&k.length>n.custom_undo_redo_levels){for(h=0;h<k.length-1;h++)k[h]=k[h+1];k.length--,j=k.length}e.bookmark=a.selection.getBookmark(2,!0),j<k.length-1&&(k.length=j+1),k.push(e),j=k.length-1;var o={level:e,lastLevel:i,originalEvent:f};return a.fire("AddUndo",o),j>0&&(d(!0),a.fire("change",o)),e},undo:function(){var b;return i.typing&&(i.add(),i.typing=!1,n(!1)),j>0&&(b=k[--j],c.applyToEditor(a,b,!0),d(!0),a.fire("undo",{level:b})),b},redo:function(){var b;return j<k.length-1&&(b=k[++j],c.applyToEditor(a,b,!1),d(!0),a.fire("redo",{level:b})),b},clear:function(){k=[],j=0,i.typing=!1,i.data=k,a.fire("ClearUndos")},hasUndo:function(){return j>0||i.typing&&k[0]&&!c.isEq(c.createFromEditor(a),k[0])},hasRedo:function(){return j<k.length-1&&!i.typing},transact:function(a){return f(),i.beforeChange(),i.ignore(a),i.add()},ignore:function(a){try{l++,a()}finally{l--}},extra:function(b,d){var e,f;i.transact(b)&&(f=k[j].bookmark,e=k[j-1],c.applyToEditor(a,e,!0),i.transact(d)&&(k[j-1].beforeBookmark=f))}}}}),g("6o",["55","1r","4d","22"],function(a,b,c,d){var e=function(a){var b=c.isText(a)?a.dom().parentNode:a.dom();return void 0!==b&&null!==b&&b.ownerDocument.body.contains(b)},f=a.cached(function(){return g(b.fromDom(d))}),g=function(a){var c=a.dom().body;if(null===c||void 0===c)throw"Body is not available yet";return b.fromDom(c)};return{body:f,getBody:g,inBody:e}}),g("6p",["60","4z"],function(a,b){return function(c,d,e,f,g){return c(e,f)?b.some(e):a.isFunction(g)&&g(e)?b.none():d(e,f,g)}}),g("64",["60","3x","1m","4z","6o","1q","1r","6p"],function(a,b,c,d,e,f,g,h){var i=function(a){return n(e.body(),a)},j=function(b,e,f){for(var h=b.dom(),i=a.isFunction(f)?f:c.constant(!1);h.parentNode;){h=h.parentNode;var j=g.fromDom(h);if(e(j))return d.some(j);if(i(j))break}return d.none()},k=function(a,b,c){var d=function(a){return b(a)};return h(d,j,a,b,c)},l=function(a,b){var c=a.dom();return c.parentNode?m(g.fromDom(c.parentNode),function(c){return!f.eq(a,c)&&b(c)}):d.none()},m=function(a,d){var e=b.find(a.dom().childNodes,c.compose(d,g.fromDom));return e.map(g.fromDom)},n=function(a,b){var c=function(a){for(var e=0;e<a.childNodes.length;e++){if(b(g.fromDom(a.childNodes[e])))return d.some(g.fromDom(a.childNodes[e]));var f=c(a.childNodes[e]);if(f.isSome())return f}return d.none()};return c(a.dom())};return{first:i,ancestor:j,closest:k,sibling:l,child:m,descendant:n}}),g("5i",["1w","c","1j","1p","1k","44"],function(a,b,c,d,e,f){function g(a){return a>0}function h(a){return a<0}function i(a,b){for(var c;c=a(b);)if(!y(c))return c;return null}function j(a,c,d,e,f){var j=new b(a,e);if(h(c)){if((v(a)||y(a))&&(a=i(j.prev,!0),d(a)))return a;for(;a=i(j.prev,f);)if(d(a))return a}if(g(c)){if((v(a)||y(a))&&(a=i(j.next,!0),d(a)))return a;for(;a=i(j.next,f);)if(d(a))return a}return null}function k(a,b){for(a=a.parentNode;a&&a!=b;a=a.parentNode)if(u(a))return a;return b}function l(a,b){for(;a&&a!=b;){if(w(a))return a;a=a.parentNode}return null}function m(a,b,c){return l(a.container(),c)==l(b.container(),c)}function n(a,b,c){return k(a.container(),c)==k(b.container(),c)}function o(a,b){var c,d;return b?(c=b.container(),d=b.offset(),A(c)?c.childNodes[d+a]:null):null}function p(a,b){var c=b.ownerDocument.createRange();return a?(c.setStartBefore(b),c.setEndBefore(b)):(c.setStartAfter(b),c.setEndAfter(b)),c}function q(a,b,c){return l(b,a)==l(c,a)}function r(a,b,c){var d,e;for(e=a?"previousSibling":"nextSibling";c&&c!=b;){if(d=c[e],x(d)&&(d=d[e]),v(d)){if(q(b,d,c))return d;break}if(B(d))break;c=c.parentNode}return null}function s(a,b,d){var f,g,h,i,j=z(r,!0,b),k=z(r,!1,b);if(g=d.startContainer,h=d.startOffset,e.isCaretContainerBlock(g)){if(A(g)||(g=g.parentNode),i=g.getAttribute("data-mce-caret"),"before"==i&&(f=g.nextSibling,v(f)))return C(f);if("after"==i&&(f=g.previousSibling,v(f)))return D(f)}if(!d.collapsed)return d;if(c.isText(g)){if(x(g)){if(1===a){if(f=k(g))return C(f);if(f=j(g))return D(f)}if(a===-1){if(f=j(g))return D(f);if(f=k(g))return C(f)}return d}if(e.endsWithCaretContainer(g)&&h>=g.data.length-1)return 1===a&&(f=k(g))?C(f):d;if(e.startsWithCaretContainer(g)&&h<=1)return a===-1&&(f=j(g))?D(f):d;if(h===g.data.length)return f=k(g),f?C(f):d;if(0===h)return f=j(g),f?D(f):d}return d}function t(a,b){return v(o(a,b))}var u=c.isContentEditableTrue,v=c.isContentEditableFalse,w=c.matchStyleValues("display","block table table-cell table-caption list-item"),x=e.isCaretContainer,y=e.isCaretContainerBlock,z=a.curry,A=c.isElement,B=f.isCaretCandidate,C=z(p,!0),D=z(p,!1);return{isForwards:g,isBackwards:h,findNode:j,getEditingHost:k,getParentBlock:l,isInSameBlock:m,isInSameEditingHost:n,isBeforeContentEditableFalse:z(t,0),isAfterContentEditableFalse:z(t,-1),normalizeRange:s}}),g("4m",["1j","44","1p","5i","1g","1w"],function(a,b,c,d,e,f){function g(a,b){for(var c=[];a&&a!=b;)c.push(a),a=a.parentNode;return c}function h(a,b){return a.hasChildNodes()&&b<a.childNodes.length?a.childNodes[b]:null}function i(a,b){if(p(a)){if(r(b.previousSibling)&&!m(b.previousSibling))return c.before(b);if(m(b))return c(b,0)}if(q(a)){if(r(b.nextSibling)&&!m(b.nextSibling))return c.after(b);if(m(b))return c(b,b.data.length)}return q(a)?o(b)?c.before(b):c.after(b):c.before(b)}function j(b,e){var f;return!!a.isBr(b)&&(f=k(1,c.after(b),e),!!f&&!d.isInSameBlock(c.before(b),c.before(f),e))}function k(a,b,u){var v,w,x,y,z,A,B;if(!n(u)||!b)return null;if(b.isEqual(c.after(u))&&u.lastChild){if(B=c.after(u.lastChild),q(a)&&r(u.lastChild)&&n(u.lastChild))return o(u.lastChild)?c.before(u.lastChild):B}else B=b;if(v=B.container(),w=B.offset(),m(v)){if(q(a)&&w>0)return c(v,--w);if(p(a)&&w<v.length)return c(v,++w);x=v}else{if(q(a)&&w>0&&(y=h(v,w-1),r(y)))return!s(y)&&(z=d.findNode(y,a,t,y))?m(z)?c(z,z.data.length):c.after(z):m(y)?c(y,y.data.length):c.before(y);if(p(a)&&w<v.childNodes.length&&(y=h(v,w),r(y)))return j(y,u)?k(a,c.after(y),u):!s(y)&&(z=d.findNode(y,a,t,y))?m(z)?c(z,0):c.before(z):m(y)?c(y,0):c.after(y);x=B.getNode()}return(p(a)&&B.isAtEnd()||q(a)&&B.isAtStart())&&(x=d.findNode(x,a,f.constant(!0),u,!0),t(x))?i(a,x):(y=d.findNode(x,a,t,u),A=e.last(e.filter(g(v,u),l)),!A||y&&A.contains(y)?y?i(a,y):null:B=p(a)?c.after(A):c.before(A))}var l=a.isContentEditableFalse,m=a.isText,n=a.isElement,o=a.isBr,p=d.isForwards,q=d.isBackwards,r=b.isCaretCandidate,s=b.isAtomic,t=b.isEditableCaretCandidate;return function(a){return{next:function(b){return k(1,b,a)},prev:function(b){return k(-1,b,a)}}}}),g("5l",["1m","4z","44","1p","5i","4m","1j"],function(a,b,c,d,e,f,g){var h=function(a,b,c){var e=a?d.before(c):d.after(c);return o(a,b,e)},i=function(a){return g.isBr(a)?d.before(a):d.after(a)},j=function(a){return d.isTextPosition(a)?0===a.offset():c.isCaretCandidate(a.getNode())},k=function(a){return d.isTextPosition(a)?a.offset()===a.container().data.length:c.isCaretCandidate(a.getNode(!0))},l=function(a,b){return!d.isTextPosition(a)&&!d.isTextPosition(b)&&a.getNode()===b.getNode(!0)},m=function(a){return!d.isTextPosition(a)&&g.isBr(a.getNode())},n=function(a,b,c){return a?!l(b,c)&&!m(b)&&k(b)&&j(c):!l(c,b)&&j(b)&&k(c)},o=function(a,c,d){var e=new f(c);return b.from(a?e.next(d):e.prev(d))},p=function(a,c,d){return o(a,c,d).bind(function(f){return e.isInSameBlock(d,f,c)&&n(a,d,f)?o(a,c,f):b.some(f)})},q=function(a,e){var f=a?e.firstChild:e.lastChild;return g.isText(f)?b.some(new d(f,a?0:f.data.length)):f?c.isCaretCandidate(f)?b.some(a?d.before(f):i(f)):h(a,e,f):b.none()};return{fromPosition:o,navigate:p,positionIn:q}}),g("5h",["3x","4z","1q","1r","4d","64"],function(a,b,c,d,e,f){var g=function(b){var c=a.foldl(b,function(a,b){return a[b]=!0,a},{});return function(a){return c[e.name(a)]===!0}},h=g(["h1","h2","h3","h4","h5","h6","p","div","address","pre","form","blockquote","center","dir","fieldset","header","footer","article","section","hgroup","aside","nav","figure"]),i=function(a){return function(b){return c.eq(a,d.fromDom(b.dom().parentNode))}},j=function(a,d){return c.contains(a,d)?f.closest(d,h,i(a)):b.none()};return{getParentTextBlock:j}}),g("79",["64","48","6p"],function(a,b,c){var d=function(a){return b.one(a)},e=function(c,d,e){return a.ancestor(c,function(a){return b.is(a,d)},e)},f=function(c,d){return a.sibling(c,function(a){return b.is(a,d)})},g=function(c,d){return a.child(c,function(a){return b.is(a,d)})},h=function(a,c){return b.one(c,a)},i=function(a,d,f){return c(b.is,e,a,d,f)};return{first:d,ancestor:e,sibling:f,child:g,descendant:h,closest:i}}),g("6q",["79"],function(a){var b=function(b){return a.first(b).isSome()},c=function(b,c,d){return a.ancestor(b,c,d).isSome()},d=function(b,c){return a.sibling(b,c).isSome()},e=function(b,c){return a.child(b,c).isSome()},f=function(b,c){return a.descendant(b,c).isSome()},g=function(b,c,d){return a.closest(b,c,d).isSome()};return{any:b,ancestor:c,sibling:d,child:e,descendant:f,closest:g}}),g("65",["1m","1q","1r","6q","44","1j","c"],function(a,b,c,d,e,f,g){var h=function(e,f){var g=c.fromDom(e),h=c.fromDom(f);return d.ancestor(h,"pre,code",a.curry(b.eq,g))},i=function(a,b){return f.isText(b)&&/^[ \t\r\n]*$/.test(b.data)&&h(a,b)===!1},j=function(a){return f.isElement(a)&&"A"===a.nodeName&&a.hasAttribute("name")},k=function(a,b){return e.isCaretCandidate(b)&&i(a,b)===!1||j(b)||l(b)},l=f.hasAttribute("data-mce-bookmark"),m=f.hasAttribute("data-mce-bogus"),n=f.hasAttributeValue("data-mce-bogus","all"),o=function(a){var b,c,d=0;if(k(a,a))return!1;if(c=a.firstChild,!c)return!0;b=new g(c,a);do if(n(c))c=b.next(!0);else if(m(c))c=b.next();else if(f.isBr(c))d++,c=b.next();else{if(k(a,c))return!1;c=b.next()}while(c);return d<=1},p=function(a){return o(a.dom())};return{isEmpty:p}}),g("5f",["3x","1m","4z","5d","61","1q","1r","4d","64","59","5l","1p","5h","65","1j"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){var p=e.immutable("block","position"),q=e.immutable("from","to"),r=function(a,b){var c=g.fromDom(a),d=g.fromDom(b.container());return m.getParentTextBlock(c,d).map(function(a){return p(a,b)})},s=function(a){return f.eq(a.from().block(),a.to().block())===!1},t=function(a){return j.parent(a.from().block()).bind(function(b){return j.parent(a.to().block()).filter(function(a){return f.eq(b,a)})}).isSome()},u=function(a){return o.isContentEditableFalse(a.from().block())===!1&&o.isContentEditableFalse(a.to().block())===!1},v=function(a,b,d){return o.isBr(d.position().getNode())&&n.isEmpty(d.block())===!1?k.positionIn(!1,d.block().dom()).bind(function(e){return e.isEqual(d.position())?k.fromPosition(b,a,e).bind(function(b){return r(a,b)}):c.some(d)}).getOr(d):d},w=function(a,b,c){var e=r(a,l.fromRangeStart(c)),f=e.bind(function(c){return k.fromPosition(b,a,c.position()).bind(function(c){return r(a,c).map(function(c){return v(a,b,c)})})});return d.liftN([e,f],q).filter(function(a){return s(a)&&t(a)&&u(a)})},x=function(a,b,d){return d.collapsed?w(a,b,d):c.none()};return{read:x}}),g("5g",["3x","4z","4a","5c","1r","59","5l","1p","65","1j"],function(a,b,c,d,e,f,g,h,i,j){var k=function(h,k,l,m){var n=f.children(k);return j.isBr(m.getNode())&&(d.remove(e.fromDom(m.getNode())),m=g.positionIn(!1,l.dom()).getOr(m)),i.isEmpty(k)===!1&&a.each(n,function(a){c.append(l,a)}),i.isEmpty(k)&&d.remove(k),n.length>0?b.from(m):b.none()},l=function(a,b,c){return a?i.isEmpty(b)?(d.remove(b),g.positionIn(!0,c.dom())):g.positionIn(!1,b.dom()).bind(function(d){return k(a,c,b,d)}):i.isEmpty(c)?(d.remove(c),g.positionIn(!0,b.dom())):g.positionIn(!1,c.dom()).bind(function(d){return k(a,b,c,d)})};return{mergeBlocks:l}}),g("4i",["5f","5g"],function(a,b){var c=function(c,d){var e;return e=a.read(c.getBody(),d,c.selection.getRng()).bind(function(a){return b.mergeBlocks(d,a.from().block(),a.to().block())}),e.each(function(a){c.selection.setRng(a.toRange())}),e.isSome()};return{backspaceDelete:c}}),g("4j",["5d","1q","1r","5h","5g"],function(a,b,c,d,e){var f=function(f,g){var h=g.getRng();return a.liftN([d.getParentTextBlock(f,c.fromDom(h.startContainer)),d.getParentTextBlock(f,c.fromDom(h.endContainer))],function(a,c){return b.eq(a,c)===!1&&(h.deleteContents(),e.mergeBlocks(!0,a,c).each(function(a){g.setRng(a.toRange())}),!0)}).getOr(!1)},g=function(a,b){var d=c.fromDom(a.getBody());return a.selection.isCollapsed()===!1&&f(d,a.selection)};return{backspaceDelete:g}}),g("66",["3x","63","60","3y","3z","49"],function(a,b,c,d,e,f){var g=function(g){if(!c.isArray(g))throw new e("cases must be an array");if(0===g.length)throw new e("there must be at least one case");var h=[],i={};return a.each(g,function(j,k){var l=b.keys(j);if(1!==l.length)throw new e("one and only one name per case");var m=l[0],n=j[m];if(void 0!==i[m])throw new e("duplicate key detected:"+m);if("cata"===m)throw new e("cannot have a case named cata (sorry)");if(!c.isArray(n))throw new e("case arguments must be an array");h.push(m),i[m]=function(){var c=arguments.length;if(c!==n.length)throw new e("Wrong number of arguments to case "+m+". Expected "+n.length+" ("+n+"), got "+c);for(var i=new d(c),j=0;j<i.length;j++)i[j]=arguments[j];var l=function(c){var d=b.keys(c);if(h.length!==d.length)throw new e("Wrong number of arguments to match. Expected: "+h.join(",")+"\nActual: "+d.join(","));var f=a.forall(h,function(b){return a.contains(d,b)});if(!f)throw new e("Not all branches were specified when using match. Specified: "+d.join(", ")+"\nRequired: "+h.join(", "));return c[m].apply(null,i)};return{fold:function(){if(arguments.length!==g.length)throw new e("Wrong number of arguments to fold. Expected "+g.length+", got "+arguments.length);var a=arguments[k];return a.apply(null,i)},match:l,log:function(a){f.log(a,{constructors:h,constructor:m,params:i})}}}}),i};return{generate:g}}),g("5j",["66","4z","1r","5l","1p","5i","5h","65","1j"],function(a,b,c,d,e,f,g,h,i){var j=a.generate([{remove:["element"]},{moveToElement:["element"]},{moveToPosition:["position"]}]),k=function(a,b){var c=b.getNode(a===!1),d=a?"after":"before";return i.isElement(c)&&c.getAttribute("data-mce-caret")===d},l=function(a,d,e,f){var i=f.getNode(d===!1);return g.getParentTextBlock(c.fromDom(a),c.fromDom(e.getNode())).map(function(a){return h.isEmpty(a)?j.remove(a.dom()):j.moveToElement(i)}).orThunk(function(){return b.some(j.moveToElement(i))})},m=function(a,c,e){return d.fromPosition(c,a,e).bind(function(d){return c&&i.isContentEditableFalse(d.getNode())?l(a,c,e,d):c===!1&&i.isContentEditableFalse(d.getNode(!0))?l(a,c,e,d):c&&f.isAfterContentEditableFalse(e)?b.some(j.moveToPosition(d)):c===!1&&f.isBeforeContentEditableFalse(e)?b.some(j.moveToPosition(d)):b.none(); +})},n=function(a,c){return a&&i.isContentEditableFalse(c.nextSibling)?b.some(j.moveToElement(c.nextSibling)):a===!1&&i.isContentEditableFalse(c.previousSibling)?b.some(j.moveToElement(c.previousSibling)):b.none()},o=function(a,c,d){return k(c,d)?n(c,d.getNode(c===!1)).fold(function(){return m(a,c,d)},b.some):m(a,c,d)},p=function(a,c,d){var g=f.normalizeRange(c?1:-1,a,d),h=e.fromRangeStart(g);return c===!1&&f.isAfterContentEditableFalse(h)?b.some(j.remove(h.getNode(!0))):c&&f.isBeforeContentEditableFalse(h)?b.some(j.remove(h.getNode())):o(a,c,h)};return{read:p}}),g("67",[],function(){var a=/[\u0591-\u07FF\uFB1D-\uFDFF\uFE70-\uFEFC]/,b=function(b){return a.test(b)};return{hasStrongRtl:b}}),g("5p",["3x","1m","4z","5d","1k","5l","1p","5i","4m","e","1j","67"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m=function(a){return j.DOM.is(a,"a[href],code")},n=function(a){return"rtl"===j.DOM.getStyle(a,"direction",!0)||l.hasStrongRtl(a.textContent)},o=function(b,c){return a.filter(j.DOM.getParents(c.container(),"*",b),m)},p=function(a,b){var d=o(a,b);return c.from(d[0])},q=function(a,b){var d=o(a,b);return c.from(d[d.length-1])},r=function(a,b,c){var d=h.getParentBlock(b,a),e=h.getParentBlock(c,a);return d&&d===e},s=function(a,b){return!!b&&q(a,b).isSome()},t=function(a,b){return q(a,b).map(function(a){return w(a,!1,b).isNone()||w(a,!0,b).isNone()}).getOr(!1)},u=function(a){return e.isBeforeInline(a)||e.isAfterInline(a)},v=function(a,b){return f.positionIn(b,a)},w=function(a,b,c){return f.fromPosition(b,a,c)},x=function(a,b){var c=b.container(),d=b.offset();return a?e.isCaretContainerInline(c)?k.isText(c.nextSibling)?new g(c.nextSibling,0):g.after(c):e.isBeforeInline(b)?new g(c,d+1):b:e.isCaretContainerInline(c)?k.isText(c.previousSibling)?new g(c.previousSibling,c.previousSibling.data.length):g.before(c):e.isAfterInline(b)?new g(c,d-1):b},y=b.curry(x,!0),z=b.curry(x,!1);return{isInlineTarget:m,findInline:p,findRootInline:q,isInInline:s,isRtl:n,isAtInlineEndPoint:t,isAtZwsp:u,findCaretPositionIn:v,findCaretPosition:w,normalizePosition:x,normalizeForwards:y,normalizeBackwards:z,hasSameParentBlock:r}}),g("5k",["1m","4z","5d","4a","5c","1r","4d","64","59","44","1p","65","1j","5p"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){var o=function(a,b){var c=a.container(),d=a.offset();return k.isTextPosition(a)===!1&&c===b.parentNode&&d>k.before(b).offset()},p=function(a,b){return o(b,a)?new k(b.container(),b.offset()-1):b},q=function(a){return m.isText(a)?new k(a,0):k.before(a)},r=function(a){return m.isText(a)?new k(a,a.data.length):k.after(a)},s=function(a){return j.isCaretCandidate(a.previousSibling)?b.some(r(a.previousSibling)):a.previousSibling?n.findCaretPositionIn(a.previousSibling,!1):b.none()},t=function(a){return j.isCaretCandidate(a.nextSibling)?b.some(q(a.nextSibling)):a.nextSibling?n.findCaretPositionIn(a.nextSibling,!0):b.none()},u=function(a,c){var d=k.before(c.previousSibling?c.previousSibling:c.parentNode);return n.findCaretPosition(a,!1,d).fold(function(){return n.findCaretPosition(a,!0,k.after(c))},b.some)},v=function(a,c){return n.findCaretPosition(a,!0,k.after(c)).fold(function(){return n.findCaretPosition(a,!1,k.before(c))},b.some)},w=function(a,b){return s(b).orThunk(function(){return t(b)}).orThunk(function(){return u(a,b)})},x=function(a,b){return t(b).orThunk(function(){return s(b)}).orThunk(function(){return v(a,b)})},y=function(a,b,c){return a?x(b,c):w(b,c)},z=function(b,c,d){return y(b,c,d).map(a.curry(p,d))},A=function(a,b,c){c.fold(function(){a.focus()},function(c){a.selection.setRng(c.toRange(),b)})},B=function(a){return function(b){return b.dom()===a}},C=function(a,b){return b&&a.schema.getBlockElements().hasOwnProperty(g.name(b))},D=function(a){if(l.isEmpty(a)){var c=f.fromHtml('<br data-mce-bogus="1">');return e.empty(a),d.append(a,c),b.some(k.before(c.dom()))}return b.none()},E=function(a,b){return c.liftN([i.prevSibling(a),i.nextSibling(a),b],function(b,c,d){var f,g=b.dom(),h=c.dom();return m.isText(g)&&m.isText(h)?(f=g.data.length,g.appendData(h.data),e.remove(c),e.remove(a),d.container()===h?new k(g,f):d):(e.remove(a),d)}).orThunk(function(){return e.remove(a),b})},F=function(c,d,e){var f=z(d,c.getBody(),e.dom()),g=h.ancestor(e,a.curry(C,c),B(c.getBody())),i=E(e,f);g.bind(D).fold(function(){A(c,d,i)},function(a){A(c,d,b.some(a))})};return{deleteElement:F}}),g("4k",["1r","1p","5i","5f","5j","5k","5g","1j"],function(a,b,c,d,e,f,g,h){var i=function(b,c){return function(d){return f.deleteElement(b,c,a.fromDom(d)),!0}},j=function(a,c){return function(d){var e=c?b.before(d):b.after(d);return a.selection.setRng(e.toRange()),!0}},k=function(a){return function(b){return a.selection.setRng(b.toRange()),!0}},l=function(a,b){var c=e.read(a.getBody(),b,a.selection.getRng()).map(function(c){return c.fold(i(a,b),j(a,b),k(a))});return c.getOr(!1)},m=function(b,c){var d=b.selection.getNode();return!!h.isContentEditableFalse(d)&&(f.deleteElement(b,c,a.fromDom(b.selection.getNode())),!0)},n=function(a,b){for(;b&&b!==a;){if(h.isContentEditableTrue(b)||h.isContentEditableFalse(b))return b;b=b.parentNode}return null},o=function(a){var c,d=n(a.getBody(),a.selection.getNode());return h.isContentEditableTrue(d)&&a.dom.isBlock(d)&&a.dom.isEmpty(d)&&(c=a.dom.create("br",{"data-mce-bogus":"1"}),a.dom.setHTML(d,""),d.appendChild(c),a.selection.setRng(b.before(c).toRange())),!0},p=function(a,b){return a.selection.isCollapsed()?l(a,b):m(a,b)};return{backspaceDelete:p,paddEmptyElement:o}}),g("68",["1m","1j","1l"],function(a,b,c){var d=b.isText,e=function(a){return d(a)&&a.data[0]===c.ZWSP},f=function(a){return d(a)&&a.data[a.data.length-1]===c.ZWSP},g=function(a){return a.ownerDocument.createTextNode(c.ZWSP)},h=function(a){if(d(a.previousSibling))return f(a.previousSibling)?a.previousSibling:(a.previousSibling.appendData(c.ZWSP),a.previousSibling);if(d(a))return e(a)?a:(a.insertData(0,c.ZWSP),a);var b=g(a);return a.parentNode.insertBefore(b,a),b},i=function(a){if(d(a.nextSibling))return e(a.nextSibling)?a.nextSibling:(a.nextSibling.insertData(0,c.ZWSP),a.nextSibling);if(d(a))return f(a)?a:(a.appendData(c.ZWSP),a);var b=g(a);return a.nextSibling?a.parentNode.insertBefore(b,a.nextSibling):a.parentNode.appendChild(b),b},j=function(a,b){return a?h(b):i(b)};return{insertInline:j,insertInlineBefore:a.curry(j,!0),insertInlineAfter:a.curry(j,!1)}}),g("69",["3x","1k","1p","1j","1l","9"],function(a,b,c,d,e,f){var g=d.isElement,h=d.isText,i=function(a){var b=a.parentNode;b&&b.removeChild(a)},j=function(a){try{return a.nodeValue}catch(a){return""}},k=function(a,b){0===b.length?i(a):a.nodeValue=b},l=function(a){var b=e.trim(a);return{count:a.length-b.length,text:b}},m=function(a,b){return s(a),b},n=function(a,b){var d=l(a.data.substr(0,b.offset())),e=l(a.data.substr(b.offset())),f=d.text+e.text;return f.length>0?(k(a,f),new c(a,b.offset()-d.count)):b},o=function(b,d){var e=d.container(),f=a.indexOf(e.childNodes,b).map(function(a){return a<d.offset()?new c(e,d.offset()-1):d}).getOr(d);return s(b),f},p=function(a,b){return b.container()===a?n(a,b):m(a,b)},q=function(a,b){return b.container()===a.parentNode?o(a,b):m(a,b)},r=function(a,b){return c.isTextPosition(b)?p(a,b):q(a,b)},s=function(a){if(g(a)&&b.isCaretContainer(a)&&(b.hasContent(a)?a.removeAttribute("data-mce-caret"):i(a)),h(a)){var c=e.trim(j(a));k(a,c)}};return{removeAndReposition:r,remove:s}}),g("5m",["4z","1k","68","69","1p","1j","5p"],function(a,b,c,d,e,f,g){var h=function(a,b){return f.isText(a.container())?c.insertInline(b,a.container()):c.insertInline(b,a.getNode())},i=function(a,c){var d=c.get();return d&&a.container()===d&&b.isCaretContainerInline(d)},j=function(b,f){return f.fold(function(f){d.remove(b.get());var g=c.insertInlineBefore(f);return b.set(g),a.some(new e(g,g.length-1))},function(a){return g.findCaretPositionIn(a,!0).map(function(a){if(i(a,b))return new e(b.get(),1);d.remove(b.get());var c=h(a,!0);return b.set(c),new e(c,1)})},function(a){return g.findCaretPositionIn(a,!1).map(function(a){if(i(a,b))return new e(b.get(),b.get().length-1);d.remove(b.get());var c=h(a,!1);return b.set(c),new e(c,c.length-1)})},function(f){d.remove(b.get());var g=c.insertInlineAfter(f);return b.set(g),a.some(new e(g,1))})};return{renderCaret:j}}),g("6a",["4z"],function(a){var b=function(b,c){for(var d=0;d<b.length;d++){var e=b[d].apply(null,c);if(e.isSome())return e}return a.none()};return{evaluateUntil:b}}),g("5n",["66","1m","4z","5d","1k","1p","5i","1j","5p","6a"],function(a,b,c,d,e,f,g,h,i,j){var k=a.generate([{before:["element"]},{start:["element"]},{end:["element"]},{after:["element"]}]),l=function(a,b){var c=g.getParentBlock(b,a);return c?c:a},m=function(a,d){var e=i.normalizeForwards(d),f=l(a,e.container());return i.findRootInline(f,e).fold(function(){return i.findCaretPosition(f,!0,e).bind(b.curry(i.findRootInline,f)).map(function(a){return k.before(a)})},c.none)},n=function(a,b){var d=i.normalizeBackwards(b);return i.findRootInline(a,d).bind(function(a){var b=i.findCaretPosition(a,!1,d);return b.isNone()?c.some(k.start(a)):c.none()})},o=function(a,b){var d=i.normalizeForwards(b);return i.findRootInline(a,d).bind(function(a){var b=i.findCaretPosition(a,!0,d);return b.isNone()?c.some(k.end(a)):c.none()})},p=function(a,d){var e=i.normalizeBackwards(d),f=l(a,e.container());return i.findRootInline(f,e).fold(function(){return i.findCaretPosition(f,!1,e).bind(b.curry(i.findRootInline,f)).map(function(a){return k.after(a)})},c.none)},q=function(a){return i.isRtl(s(a))===!1},r=function(a,b){var c=j.evaluateUntil([m,n,o,p],[a,b]);return c.filter(q)},s=function(a){return a.fold(b.identity,b.identity,b.identity,b.identity)},t=function(a){return a.fold(b.constant("before"),b.constant("start"),b.constant("end"),b.constant("after"))},u=function(a){return a.fold(k.before,k.before,k.after,k.after)},v=function(a){return a.fold(k.start,k.start,k.end,k.end)},w=function(a,b){return t(a)===t(b)&&s(a)===s(b)},x=function(a,b,c,e,f){return d.liftN([i.findRootInline(b,c),i.findRootInline(b,e)],function(c,d){return c!==d&&i.hasSameParentBlock(b,c,d)?k.after(a?c:d):f}).getOr(f)},y=function(a,c){return a.fold(b.constant(!0),function(a){return!w(a,c)})},z=function(a,c,d,e){var f=i.normalizePosition(a,e),g=i.findCaretPosition(c,a,f).map(b.curry(i.normalizePosition,a)),h=g.fold(function(){return d.map(u)},function(e){return r(c,e).map(b.curry(x,a,c,f,e)).filter(b.curry(y,d))});return h.filter(q)},A=function(a,d){return a?d.fold(b.compose(c.some,k.start),c.none,b.compose(c.some,k.after),c.none):d.fold(c.none,b.compose(c.some,k.before),c.none,b.compose(c.some,k.end))},B=function(a,c,d){var e=i.normalizePosition(a,d),f=r(c,e);return r(c,e).bind(b.curry(A,a)).orThunk(function(){return z(a,c,f,d)})};return{readLocation:r,prevLocation:b.curry(B,!1),nextLocation:b.curry(B,!0),getElement:s,outside:u,inside:v}}),g("6b",[],function(){var a=function(b){var c=b,d=function(){return c},e=function(a){c=a},f=function(){return a(d())};return{get:d,set:e,clone:f}};return a}),g("5o",["3x","6b","1m","69","1p","5m","5n","5p"],function(a,b,c,d,e,f,g,h){var i=function(a,b){var c=a.dom.createRng();c.setStart(b.container(),b.offset()),c.setEnd(b.container(),b.offset()),a.selection.setRng(c)},j=function(a){return a.settings.inline_boundaries!==!1},k=function(a,b){a?b.setAttribute("data-mce-selected","1"):b.removeAttribute("data-mce-selected","1")},l=function(a,b,c){return f.renderCaret(b,c).map(function(b){return i(a,b),c})},m=function(a,b,c){var d=a.getBody(),f=e.fromRangeStart(a.selection.getRng()),h=c?g.nextLocation(d,f):g.prevLocation(d,f);return h.bind(function(c){return l(a,b,c)})},n=function(b,d){var e=b.select("a[href][data-mce-selected],code[data-mce-selected]"),f=a.filter(d,h.isInlineTarget);a.each(a.difference(e,f),c.curry(k,!1)),a.each(a.difference(f,e),c.curry(k,!0))},o=function(a,b){if(a.selection.isCollapsed()&&a.composing!==!0&&b.get()){var c=e.fromRangeStart(a.selection.getRng());e.isTextPosition(c)&&h.isAtZwsp(c)===!1&&(i(a,d.removeAndReposition(b.get(),c)),b.set(null))}},p=function(b,c,d){if(b.selection.isCollapsed()){var f=a.filter(d,h.isInlineTarget);a.each(f,function(a){var d=e.fromRangeStart(b.selection.getRng());g.readLocation(b.getBody(),d).bind(function(a){return l(b,c,a)})})}},q=function(a,b,c){return function(){return!!j(a)&&m(a,b,c).isSome()}},r=function(a){var c=new b(null);return a.on("NodeChange",function(b){j(a)&&(n(a.dom,b.parents),o(a,c),p(a,c,b.parents))}),c};return{move:q,setupSelectedState:r,setCaretPosition:i}}),g("4l",["1m","4z","5d","1r","1k","5l","1p","5i","5k","5m","5n","5o","5p"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){var n=function(a){return a.settings.inline_boundaries!==!1},o=function(a,b){var c=document.createRange();return c.setStart(a.container(),a.offset()),c.setEnd(b.container(),b.offset()),c},p=function(a){return c.liftN([m.findCaretPositionIn(a,!0),m.findCaretPositionIn(a,!1)],function(b,c){var d=m.normalizePosition(!0,b),e=m.normalizePosition(!1,c);return m.findCaretPosition(a,!0,d).map(function(a){return a.isEqual(e)}).getOr(!0)}).getOr(!0)},q=function(a,b){return function(c){return j.renderCaret(b,c).map(function(b){return l.setCaretPosition(a,b),!0}).getOr(!1)}},r=function(a,b,c,d){var e=a.getBody();a.undoManager.ignore(function(){a.selection.setRng(o(c,d)),a.execCommand("Delete"),k.readLocation(e,g.fromRangeStart(a.selection.getRng())).map(k.inside).map(q(a,b))}),a.nodeChanged()},s=function(a,b){var c=h.getParentBlock(b,a);return c?c:a},t=function(c,e,g,h){var j=s(c.getBody(),h.container()),l=k.readLocation(j,h);return l.bind(function(c){return g?c.fold(a.constant(b.some(k.inside(c))),b.none,a.constant(b.some(k.outside(c))),b.none):c.fold(b.none,a.constant(b.some(k.outside(c))),b.none,a.constant(b.some(k.inside(c))))}).map(q(c,e)).getOrThunk(function(){var a=f.navigate(g,j,h),b=a.bind(function(a){return k.readLocation(j,a)});return l.isSome()&&b.isSome()?m.findRootInline(j,h).map(function(a){return!!p(a)&&(i.deleteElement(c,g,d.fromDom(a)),!0)}).getOr(!1):b.bind(function(b){return a.map(function(a){return g?r(c,e,h,a):r(c,e,a,h),!0})}).getOr(!1)})},u=function(a,b,c){if(a.selection.isCollapsed()&&n(a)){var d=g.fromRangeStart(a.selection.getRng());return t(a,b,c,d)}return!1};return{backspaceDelete:u}}),g("20",["4i","4j","4k","4l"],function(a,b,c,d){var e=function(a,b){a.getDoc().execCommand(b,!1,null)},f=function(a){var b=a.dom,c=a.getBody();b.isEmpty(c)&&(a.setContent(""),c.firstChild&&b.isBlock(c.firstChild)?a.selection.setCursorLocation(c.firstChild,0):a.selection.setCursorLocation(c,0))},g=function(g){c.backspaceDelete(g,!1)||d.backspaceDelete(g,!1)||a.backspaceDelete(g,!1)||b.backspaceDelete(g,!1)||(e(g,"Delete"),f(g))},h=function(f){c.backspaceDelete(f,!0)||d.backspaceDelete(f,!0)||a.backspaceDelete(f,!0)||b.backspaceDelete(f,!0)||e(f,"ForwardDelete")};return{deleteCommand:g,forwardDeleteCommand:h}}),g("4n",["5l","1p","5i","1j"],function(a,b,c,d){var e=function(a){return d.isElement(a)&&/^(P|H[1-6]|DIV)$/.test(a.nodeName)},f=function(a,b){return b(a.endContainer)},g=function(a,b,c,d){var e=document.createRange();return e.setStart(a,b),e.setEnd(c,d),e},h=function(d){var h=b.fromRangeStart(d),i=b.fromRangeEnd(d),j=d.commonAncestorContainer;return d.collapsed===!1&&f(d,e)&&0===d.endOffset?a.fromPosition(!1,j,i).map(function(a){return!c.isInSameBlock(h,i,j)&&c.isInSameBlock(h,a,j)?g(h.container(),h.offset(),a.container(),a.offset()):d}).getOr(d):d},i=function(a){return h(a)};return{normalize:i}}),g("4o",["9","4m","1p"],function(a,b,c){var d=function(a){var b=a.firstChild,c=a.lastChild;return b&&"meta"===b.name&&(b=b.next),c&&"mce_marker"===c.attr("id")&&(c=c.prev),!(!b||b!==c)&&("ul"===b.name||"ol"===b.name)},e=function(a){var b=a.firstChild,c=a.lastChild;return b&&"META"===b.nodeName&&b.parentNode.removeChild(b),c&&"mce_marker"===c.id&&c.parentNode.removeChild(c),a},f=function(a,b,c){var d=b.serialize(c),f=a.createFragment(d);return e(f)},g=function(b){return a.grep(b.childNodes,function(a){return"LI"===a.nodeName})},h=function(a){return!a.firstChild},i=function(a){return a.length>0&&h(a[a.length-1])?a.slice(0,-1):a},j=function(a,b){var c=a.getParent(b,a.isBlock);return c&&"LI"===c.nodeName?c:null},k=function(a,b){return!!j(a,b)},l=function(a,b){var c=b.cloneRange(),d=b.cloneRange();return c.setStartBefore(a),d.setEndAfter(a),[c.cloneContents(),d.cloneContents()]},m=function(a,d){var e=c.before(a),f=new b(d),g=f.next(e);return g?g.toRange():null},n=function(a,d){var e=c.after(a),f=new b(d),g=f.prev(e);return g?g.toRange():null},o=function(b,c,d,e){var f=l(b,e),g=b.parentNode;return g.insertBefore(f[0],b),a.each(c,function(a){g.insertBefore(a,b)}),g.insertBefore(f[1],b),g.removeChild(b),n(c[c.length-1],d)},p=function(b,c,d){var e=b.parentNode;return a.each(c,function(a){e.insertBefore(a,b)}),m(b,d)},q=function(a,b,c,d){return d.insertAfter(b.reverse(),a),n(b[0],c)},r=function(a,d,e,h){var k=f(d,a,h),l=j(d,e.startContainer),m=i(g(k.firstChild)),n=1,r=2,s=d.getRoot(),t=function(a){var f=c.fromRangeStart(e),g=new b(d.getRoot()),h=a===n?g.prev(f):g.next(f);return!h||j(d,h.getNode())!==l};return t(n)?p(l,m,s):t(r)?q(l,m,s,d):o(l,m,s,e)};return{isListFragment:d,insertAtCaret:r,isParentBlockLi:k,trimListItems:i,listItems:g}}),g("21",["1p","4m","1v","1j","4n","6","n","4o","9"],function(a,b,c,d,e,f,g,h,i){var j=d.matchNodeNames("td th"),k=function(a,b,c){if("all"===c.getAttribute("data-mce-bogus"))c.parentNode.insertBefore(a.dom.createFragment(b),c);else{var d=c.firstChild,e=c.lastChild;!d||d===e&&"BR"===d.nodeName?a.dom.setHTML(c,b):a.selection.setContent(b)}},l=function(d,l,m){function n(a){function b(a){return d[a]&&3==d[a].nodeType}var c,d,e;return c=I.getRng(!0),d=c.startContainer,e=c.startOffset,3==d.nodeType&&(e>0?a=a.replace(/^ /," "):b("previousSibling")||(a=a.replace(/^ /," ")),e<d.length?a=a.replace(/ (<br>|)$/," "):b("nextSibling")||(a=a.replace(/( | )(<br>|)$/," "))),a}function o(){var a,b,c;a=I.getRng(!0),b=a.startContainer,c=a.startOffset,3==b.nodeType&&a.collapsed&&("\xa0"===b.data[c]?(b.deleteData(c,1),/[\u00a0| ]$/.test(l)||(l+=" ")):"\xa0"===b.data[c-1]&&(b.deleteData(c-1,1),/[\u00a0| ]$/.test(l)||(l=" "+l)))}function p(){if(G){var a=d.getBody(),b=new c(J);i.each(J.select("*[data-mce-fragment]"),function(c){for(var d=c.parentNode;d&&d!=a;d=d.parentNode)H[c.nodeName.toLowerCase()]&&b.compare(d,c)&&J.remove(c,!0)})}}function q(a){for(var b=a;b=b.walk();)1===b.type&&b.attr("data-mce-fragment","1")}function r(a){i.each(a.getElementsByTagName("*"),function(a){a.removeAttribute("data-mce-fragment")})}function s(a){return!!a.getAttribute("data-mce-fragment")}function t(a){return a&&!d.schema.getShortEndedElements()[a.nodeName]}function u(c){function e(a){for(var b=d.getBody();a&&a!==b;a=a.parentNode)if("false"===d.dom.getContentEditable(a))return a;return null}function g(c){var e=a.fromRangeStart(c),f=new b(d.getBody());if(e=f.next(e))return e.toRange()}var h,i,k;if(c){if(I.scrollIntoView(c),h=e(c))return J.remove(c),void I.select(h);C=J.createRng(),D=c.previousSibling,D&&3==D.nodeType?(C.setStart(D,D.nodeValue.length),f.ie||(E=c.nextSibling,E&&3==E.nodeType&&(D.appendData(E.data),E.parentNode.removeChild(E)))):(C.setStartBefore(c),C.setEndBefore(c)),i=J.getParent(c,J.isBlock),J.remove(c),i&&J.isEmpty(i)&&(d.$(i).empty(),C.setStart(i,0),C.setEnd(i,0),j(i)||s(i)||!(k=g(C))?J.add(i,J.create("br",{"data-mce-bogus":"1"})):(C=k,J.remove(i))),I.setRng(C)}}var v,w,x,y,z,A,B,C,D,E,F,G,H=d.schema.getTextInlineElements(),I=d.selection,J=d.dom;/^ | $/.test(l)&&(l=n(l)),v=d.parser,G=m.merge,w=new g({validate:d.settings.validate},d.schema),F='<span id="mce_marker" data-mce-type="bookmark">​</span>',A={content:l,format:"html",selection:!0},d.fire("BeforeSetContent",A),l=A.content,l.indexOf("{$caret}")==-1&&(l+="{$caret}"),l=l.replace(/\{\$caret\}/,F),C=I.getRng();var K=C.startContainer||(C.parentElement?C.parentElement():null),L=d.getBody();K===L&&I.isCollapsed()&&J.isBlock(L.firstChild)&&t(L.firstChild)&&J.isEmpty(L.firstChild)&&(C=J.createRng(),C.setStart(L.firstChild,0),C.setEnd(L.firstChild,0),I.setRng(C)),I.isCollapsed()||(d.selection.setRng(e.normalize(d.selection.getRng())),d.getDoc().execCommand("Delete",!1,null),o()),x=I.getNode();var M={context:x.nodeName.toLowerCase(),data:m.data};if(z=v.parse(l,M),m.paste===!0&&h.isListFragment(z)&&h.isParentBlockLi(J,x))return C=h.insertAtCaret(w,J,d.selection.getRng(!0),z),d.selection.setRng(C),void d.fire("SetContent",A);if(q(z),D=z.lastChild,"mce_marker"==D.attr("id"))for(B=D,D=D.prev;D;D=D.walk(!0))if(3==D.type||!J.isBlock(D.name)){d.schema.isValidChild(D.parent.name,"span")&&D.parent.insert(B,D,"br"===D.name);break}if(d._selectionOverrides.showBlockCaretContainer(x),M.invalid){for(I.setContent(F),x=I.getNode(),y=d.getBody(),9==x.nodeType?x=D=y:D=x;D!==y;)x=D,D=D.parentNode;l=x==y?y.innerHTML:J.getOuterHTML(x),l=w.serialize(v.parse(l.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i,function(){return w.serialize(z)}))),x==y?J.setHTML(y,l):J.setOuterHTML(x,l)}else l=w.serialize(z),k(d,l,x);p(),u(J.get("mce_marker")),r(d.getBody()),d.fire("SetContent",A),d.addVisual()},m=function(a){var b;return"string"!=typeof a?(b=i.extend({paste:a.paste,data:{paste:a.paste}},a),{content:a.content,details:b}):{content:a,details:{}}},n=function(a,b){var c=m(b);l(a,c.content,c.details)};return{insertAtCaret:n}}),g("v",["20","1j","h","c","6","21","9"],function(a,b,c,d,e,f,g){var h=g.each,i=g.extend,j=g.map,k=g.inArray,l=g.explode,m=e.ie&&e.ie<11,n=!0,o=!1;return function(g){function p(a,b,c,d){var e,f,i=0;if(!g.removed){if(/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(a)||d&&d.skip_focus||g.focus(),d=g.fire("BeforeExecCommand",{command:a,ui:b,value:c}),d.isDefaultPrevented())return!1;if(f=a.toLowerCase(),e=H.exec[f])return e(f,b,c),g.fire("ExecCommand",{command:a,ui:b,value:c}),!0;if(h(g.plugins,function(d){if(d.execCommand&&d.execCommand(a,b,c))return g.fire("ExecCommand",{command:a,ui:b,value:c}),i=!0,!1}),i)return i;if(g.theme&&g.theme.execCommand&&g.theme.execCommand(a,b,c))return g.fire("ExecCommand",{command:a,ui:b,value:c}),!0;try{i=g.getDoc().execCommand(a,b,c)}catch(a){}return!!i&&(g.fire("ExecCommand",{command:a,ui:b,value:c}),!0)}}function q(a){var b;if(!g.quirks.isHidden()&&!g.removed){if(a=a.toLowerCase(),b=H.state[a])return b(a);try{return g.getDoc().queryCommandState(a)}catch(a){}return!1}}function r(a){var b;if(!g.quirks.isHidden()&&!g.removed){if(a=a.toLowerCase(),b=H.value[a])return b(a);try{return g.getDoc().queryCommandValue(a)}catch(a){}}}function s(a,b){b=b||"exec",h(a,function(a,c){h(c.toLowerCase().split(","),function(c){H[b][c]=a})})}function t(a,b,c){a=a.toLowerCase(),H.exec[a]=function(a,d,e,f){return b.call(c||g,d,e,f)}}function u(a){if(a=a.toLowerCase(),H.exec[a])return!0;try{return g.getDoc().queryCommandSupported(a)}catch(a){}return!1}function v(a,b,c){a=a.toLowerCase(),H.state[a]=function(){return b.call(c||g)}}function w(a,b,c){a=a.toLowerCase(),H.value[a]=function(){return b.call(c||g)}}function x(a){return a=a.toLowerCase(),!!H.exec[a]}function y(a,b,c){return void 0===b&&(b=o),void 0===c&&(c=null),g.getDoc().execCommand(a,b,c)}function z(a){return F.match(a)}function A(a,b){F.toggle(a,b?{value:b}:void 0),g.nodeChanged()}function B(a){G=E.getBookmark(a)}function C(){E.moveToBookmark(G)}var D,E,F,G,H={state:{},exec:{},value:{}},I=g.settings;g.on("PreInit",function(){D=g.dom,E=g.selection,I=g.settings,F=g.formatter}),i(this,{execCommand:p,queryCommandState:q,queryCommandValue:r,queryCommandSupported:u,addCommands:s,addCommand:t,addQueryStateHandler:v,addQueryValueHandler:w,hasCustomCommand:x}),s({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){g.undoManager.add()},"Cut,Copy,Paste":function(a){var b,c=g.getDoc();try{y(a)}catch(a){b=n}if("paste"!==a||c.queryCommandEnabled(a)||(b=!0),b||!c.queryCommandSupported(a)){var d=g.translate("Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.");e.mac&&(d=d.replace(/Ctrl\+/g,"\u2318+")),g.notificationManager.open({text:d,type:"error"})}},unlink:function(){if(E.isCollapsed()){var a=g.dom.getParent(g.selection.getStart(),"a");return void(a&&g.dom.remove(a,!0))}F.remove("link")},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull,JustifyNone":function(a){var b=a.substring(7);"full"==b&&(b="justify"),h("left,center,right,justify".split(","),function(a){b!=a&&F.remove("align"+a)}),"none"!=b&&A("align"+b)},"InsertUnorderedList,InsertOrderedList":function(a){var b,c;y(a),b=D.getParent(E.getNode(),"ol,ul"),b&&(c=b.parentNode,/^(H[1-6]|P|ADDRESS|PRE)$/.test(c.nodeName)&&(B(),D.split(c,b),C()))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(a){A(a)},"ForeColor,HiliteColor,FontName":function(a,b,c){A(a,c)},FontSize:function(a,b,c){var d,e;c>=1&&c<=7&&(e=l(I.font_size_style_values),d=l(I.font_size_classes),c=d?d[c-1]||c:e[c-1]||c),A(a,c)},RemoveFormat:function(a){F.remove(a)},mceBlockQuote:function(){A("blockquote")},FormatBlock:function(a,b,c){return A(c||"p")},mceCleanup:function(){var a=E.getBookmark();g.setContent(g.getContent({cleanup:n}),{cleanup:n}),E.moveToBookmark(a)},mceRemoveNode:function(a,b,c){var d=c||E.getNode();d!=g.getBody()&&(B(),g.dom.remove(d,n),C())},mceSelectNodeDepth:function(a,b,c){var d=0;D.getParent(E.getNode(),function(a){if(1==a.nodeType&&d++==c)return E.select(a),o},g.getBody())},mceSelectNode:function(a,b,c){E.select(c)},mceInsertContent:function(a,b,c){f.insertAtCaret(g,c)},mceInsertRawHTML:function(a,b,c){E.setContent("tiny_mce_marker"),g.setContent(g.getContent().replace(/tiny_mce_marker/g,function(){return c}))},mceToggleFormat:function(a,b,c){A(c)},mceSetContent:function(a,b,c){g.setContent(c)},"Indent,Outdent":function(a){var b,c,d;b=I.indentation,c=/[a-z%]+$/i.exec(b),b=parseInt(b,10),q("InsertUnorderedList")||q("InsertOrderedList")?y(a):(I.forced_root_block||D.getParent(E.getNode(),D.isBlock)||F.apply("div"),h(E.getSelectedBlocks(),function(e){if("false"!==D.getContentEditable(e)&&"LI"!==e.nodeName){var f=g.getParam("indent_use_margin",!1)?"margin":"padding";f="TABLE"===e.nodeName?"margin":f,f+="rtl"==D.getStyle(e,"direction",!0)?"Right":"Left","outdent"==a?(d=Math.max(0,parseInt(e.style[f]||0,10)-b),D.setStyle(e,f,d?d+c:"")):(d=parseInt(e.style[f]||0,10)+b+c,D.setStyle(e,f,d))}}))},mceRepaint:function(){},InsertHorizontalRule:function(){g.execCommand("mceInsertContent",!1,"<hr />")},mceToggleVisualAid:function(){g.hasVisual=!g.hasVisual,g.addVisual()},mceReplaceContent:function(a,b,c){g.execCommand("mceInsertContent",!1,c.replace(/\{\$selection\}/g,E.getContent({format:"text"})))},mceInsertLink:function(a,b,c){var d;"string"==typeof c&&(c={href:c}),d=D.getParent(E.getNode(),"a"),c.href=c.href.replace(" ","%20"),d&&c.href||F.remove("link"),c.href&&F.apply("link",c,d)},selectAll:function(){var a,c=D.getRoot();if(E.getRng().setStart){var d=D.getParent(E.getStart(),b.isContentEditableTrue);d&&(a=D.createRng(),a.selectNodeContents(d),E.setRng(a))}else a=E.getRng(),a.item||(a.moveToElementText(c),a.select())},"delete":function(){a.deleteCommand(g)},forwardDelete:function(){a.forwardDeleteCommand(g)},mceNewDocument:function(){g.setContent("")},InsertLineBreak:function(a,b,e){function f(){for(var a,b=new d(p,r),c=g.schema.getNonEmptyElements();a=b.next();)if(c[a.nodeName.toLowerCase()]||a.length>0)return!0}var h,i,j,k=e,l=E.getRng(!0);new c(D).normalize(l);var o=l.startOffset,p=l.startContainer;if(1==p.nodeType&&p.hasChildNodes()){var q=o>p.childNodes.length-1;p=p.childNodes[Math.min(o,p.childNodes.length-1)]||p,o=q&&3==p.nodeType?p.nodeValue.length:0}var r=D.getParent(p,D.isBlock),s=r?r.nodeName.toUpperCase():"",t=r?D.getParent(r.parentNode,D.isBlock):null,u=t?t.nodeName.toUpperCase():"",v=k&&k.ctrlKey;"LI"!=u||v||(r=t,s=u),p&&3==p.nodeType&&o>=p.nodeValue.length&&(m||f()||(h=D.create("br"),l.insertNode(h),l.setStartAfter(h),l.setEndAfter(h),i=!0)),h=D.create("br"),l.insertNode(h);var w=D.doc.documentMode;return m&&"PRE"==s&&(!w||w<8)&&h.parentNode.insertBefore(D.doc.createTextNode("\r"),h),j=D.create("span",{}," "),h.parentNode.insertBefore(j,h),E.scrollIntoView(j),D.remove(j),i?(l.setStartBefore(h),l.setEndBefore(h)):(l.setStartAfter(h),l.setEndAfter(h)),E.setRng(l),g.undoManager.add(),n}}),s({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(a){var b="align"+a.substring(7),c=E.isCollapsed()?[D.getParent(E.getNode(),D.isBlock)]:E.getSelectedBlocks(),d=j(c,function(a){return!!F.matchNode(a,b)});return k(d,n)!==-1},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(a){return z(a)},mceBlockQuote:function(){return z("blockquote")},Outdent:function(){var a;if(I.inline_styles){if((a=D.getParent(E.getStart(),D.isBlock))&&parseInt(a.style.paddingLeft,10)>0)return n;if((a=D.getParent(E.getEnd(),D.isBlock))&&parseInt(a.style.paddingLeft,10)>0)return n}return q("InsertUnorderedList")||q("InsertOrderedList")||!I.inline_styles&&!!D.getParent(E.getNode(),"BLOCKQUOTE")},"InsertUnorderedList,InsertOrderedList":function(a){var b=D.getParent(E.getNode(),"ul,ol");return b&&("insertunorderedlist"===a&&"UL"===b.tagName||"insertorderedlist"===a&&"OL"===b.tagName)}},"state"),s({"FontSize,FontName":function(a){var b,c=0;return(b=D.getParent(E.getNode(),"span"))&&(c="fontsize"==a?b.style.fontSize:b.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()),c}},"value"),s({Undo:function(){g.undoManager.undo()},Redo:function(){g.undoManager.redo()}})}}),g("w",["22","9"],function(a,b){function c(b,g){var h,i,j=this;if(b=e(b),g=j.settings=g||{},h=g.base_uri,/^([\w\-]+):([^\/]{2})/i.test(b)||/^\s*#/.test(b))return void(j.source=b);var k=0===b.indexOf("//");0!==b.indexOf("/")||k||(b=(h?h.protocol||"http":"http")+"://mce_host"+b),/^[\w\-]*:?\/\//.test(b)||(i=g.base_uri?g.base_uri.path:new c(a.location.href).directory,""===g.base_uri.protocol?b="//mce_host"+j.toAbsPath(i,b):(b=/([^#?]*)([#?]?.*)/.exec(b),b=(h&&h.protocol||"http")+"://mce_host"+j.toAbsPath(i,b[1])+b[2])),b=b.replace(/@@/g,"(mce_at)"),b=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(b),d(f,function(a,c){var d=b[c];d&&(d=d.replace(/\(mce_at\)/g,"@@")),j[a]=d}),h&&(j.protocol||(j.protocol=h.protocol),j.userInfo||(j.userInfo=h.userInfo),j.port||"mce_host"!==j.host||(j.port=h.port),j.host&&"mce_host"!==j.host||(j.host=h.host),j.source=""),k&&(j.protocol="")}var d=b.each,e=b.trim,f="source protocol authority userInfo user password host port relative path directory file query anchor".split(" "),g={ftp:21,http:80,https:443,mailto:25};return c.prototype={setPath:function(a){var b=this;a=/^(.*?)\/?(\w+)?$/.exec(a),b.path=a[0],b.directory=a[1],b.file=a[2],b.source="",b.getURI()},toRelative:function(a){var b,d=this;if("./"===a)return a;if(a=new c(a,{base_uri:d}),"mce_host"!=a.host&&d.host!=a.host&&a.host||d.port!=a.port||d.protocol!=a.protocol&&""!==a.protocol)return a.getURI();var e=d.getURI(),f=a.getURI();return e==f||"/"==e.charAt(e.length-1)&&e.substr(0,e.length-1)==f?e:(b=d.toRelPath(d.path,a.path),a.query&&(b+="?"+a.query),a.anchor&&(b+="#"+a.anchor),b)},toAbsolute:function(a,b){return a=new c(a,{base_uri:this}),a.getURI(b&&this.isSameOrigin(a))},isSameOrigin:function(a){if(this.host==a.host&&this.protocol==a.protocol){if(this.port==a.port)return!0;var b=g[this.protocol];if(b&&(this.port||b)==(a.port||b))return!0}return!1},toRelPath:function(a,b){var c,d,e,f=0,g="";if(a=a.substring(0,a.lastIndexOf("/")),a=a.split("/"),c=b.split("/"),a.length>=c.length)for(d=0,e=a.length;d<e;d++)if(d>=c.length||a[d]!=c[d]){f=d+1;break}if(a.length<c.length)for(d=0,e=c.length;d<e;d++)if(d>=a.length||a[d]!=c[d]){f=d+1;break}if(1===f)return b;for(d=0,e=a.length-(f-1);d<e;d++)g+="../";for(d=f-1,e=c.length;d<e;d++)g+=d!=f-1?"/"+c[d]:c[d];return g},toAbsPath:function(a,b){var c,e,f,g=0,h=[];for(e=/\/$/.test(b)?"/":"",a=a.split("/"),b=b.split("/"), +d(a,function(a){a&&h.push(a)}),a=h,c=b.length-1,h=[];c>=0;c--)0!==b[c].length&&"."!==b[c]&&(".."!==b[c]?g>0?g--:h.push(b[c]):g++);return c=a.length-g,f=c<=0?h.reverse().join("/"):a.slice(0,c).join("/")+"/"+h.reverse().join("/"),0!==f.indexOf("/")&&(f="/"+f),e&&f.lastIndexOf("/")!==f.length-1&&(f+=e),f},getURI:function(a){var b,c=this;return c.source&&!a||(b="",a||(b+=c.protocol?c.protocol+"://":"//",c.userInfo&&(b+=c.userInfo+"@"),c.host&&(b+=c.host),c.port&&(b+=":"+c.port)),c.path&&(b+=c.path),c.query&&(b+="?"+c.query),c.anchor&&(b+="#"+c.anchor),c.source=b),c.source}},c.parseDataUri=function(a){var b,c;return a=decodeURIComponent(a).split(","),c=/data:([^;]+)/.exec(a[0]),c&&(b=c[1]),{type:b,data:a[1]}},c.getDocumentBaseUrl=function(a){var b;return b=0!==a.protocol.indexOf("http")&&"file:"!==a.protocol?a.href:a.protocol+"//"+a.host+a.pathname,/^[^:]+:\/\/\/?[^\/]+\//.test(b)&&(b=b.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,""),/[\/\\]$/.test(b)||(b+="/")),b},c}),g("x",["9"],function(a){function b(){}var c,d,e=a.each,f=a.extend;return b.extend=c=function(a){function b(){var a,b,c,e=this;if(!d&&(e.init&&e.init.apply(e,arguments),b=e.Mixins))for(a=b.length;a--;)c=b[a],c.init&&c.init.apply(e,arguments)}function g(){return this}function h(a,b){return function(){var c,d=this,e=d._super;return d._super=m[a],c=b.apply(d,arguments),d._super=e,c}}var i,j,k,l=this,m=l.prototype;d=!0,i=new l,d=!1,a.Mixins&&(e(a.Mixins,function(b){for(var c in b)"init"!==c&&(a[c]=b[c])}),m.Mixins&&(a.Mixins=m.Mixins.concat(a.Mixins))),a.Methods&&e(a.Methods.split(","),function(b){a[b]=g}),a.Properties&&e(a.Properties.split(","),function(b){var c="_"+b;a[b]=function(a){var b,d=this;return a!==b?(d[c]=a,d):d[c]}}),a.Statics&&e(a.Statics,function(a,c){b[c]=a}),a.Defaults&&m.Defaults&&(a.Defaults=f({},m.Defaults,a.Defaults));for(j in a)k=a[j],"function"==typeof k&&m[j]?i[j]=h(j,k):i[j]=k;return b.prototype=i,b.constructor=b,b.extend=c,b},b}),g("y",["9"],function(a){function b(b){function c(){return!1}function d(){return!0}function e(a,e){var f,h,i,k;if(a=a.toLowerCase(),e=e||{},e.type=a,e.target||(e.target=j),e.preventDefault||(e.preventDefault=function(){e.isDefaultPrevented=d},e.stopPropagation=function(){e.isPropagationStopped=d},e.stopImmediatePropagation=function(){e.isImmediatePropagationStopped=d},e.isDefaultPrevented=c,e.isPropagationStopped=c,e.isImmediatePropagationStopped=c),b.beforeFire&&b.beforeFire(e),f=m[a])for(h=0,i=f.length;h<i;h++){if(k=f[h],k.once&&g(a,k.func),e.isImmediatePropagationStopped())return e.stopPropagation(),e;if(k.func.call(j,e)===!1)return e.preventDefault(),e}return e}function f(b,d,e,f){var g,h,i;if(d===!1&&(d=c),d)for(d={func:d},f&&a.extend(d,f),h=b.toLowerCase().split(" "),i=h.length;i--;)b=h[i],g=m[b],g||(g=m[b]=[],k(b,!0)),e?g.unshift(d):g.push(d);return l}function g(a,b){var c,d,e,f,g;if(a)for(f=a.toLowerCase().split(" "),c=f.length;c--;){if(a=f[c],d=m[a],!a){for(e in m)k(e,!1),delete m[e];return l}if(d){if(b)for(g=d.length;g--;)d[g].func===b&&(d=d.slice(0,g).concat(d.slice(g+1)),m[a]=d);else d.length=0;d.length||(k(a,!1),delete m[a])}}else{for(a in m)k(a,!1);m={}}return l}function h(a,b,c){return f(a,b,c,{once:!0})}function i(a){return a=a.toLowerCase(),!(!m[a]||0===m[a].length)}var j,k,l=this,m={};b=b||{},j=b.scope||l,k=b.toggleEvent||c,l.fire=e,l.on=f,l.off=g,l.once=h,l.has=i}var c=a.makeMap("focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover draggesture dragdrop drop drag submit compositionstart compositionend compositionupdate touchstart touchmove touchend"," ");return b.isNative=function(a){return!!c[a.toLowerCase()]},b}),g("z",["y"],function(a){function b(b){return b._eventDispatcher||(b._eventDispatcher=new a({scope:b,toggleEvent:function(c,d){a.isNative(c)&&b.toggleNativeEvent&&b.toggleNativeEvent(c,d)}})),b._eventDispatcher}return{fire:function(a,c,d){var e=this;if(e.removed&&"remove"!==a)return c;if(c=b(e).fire(a,c,d),d!==!1&&e.parent)for(var f=e.parent();f&&!c.isPropagationStopped();)f.fire(a,c,!1),f=f.parent();return c},on:function(a,c,d){return b(this).on(a,c,d)},off:function(a,c){return b(this).off(a,c)},once:function(a,c){return b(this).once(a,c)},hasEventListeners:function(a){return b(this).has(a)}}}),g("5q",[],function(){function a(a){this.create=a.create}return a.create=function(b,c){return new a({create:function(a,d){function e(b){a.set(d,b.value)}function f(a){b.set(c,a.value)}var g;return a.on("change:"+d,f),b.on("change:"+c,e),g=a._bindings,g||(g=a._bindings=[],a.on("destroy",function(){for(var a=g.length;a--;)g[a]()})),g.push(function(){b.off("change:"+c,e)}),b.get(c)}})},a}),g("4p",["5q","x","z","9"],function(a,b,c,d){function e(a){return a.nodeType>0}function f(a,b){var c,g;if(a===b)return!0;if(null===a||null===b)return a===b;if("object"!=typeof a||"object"!=typeof b)return a===b;if(d.isArray(b)){if(a.length!==b.length)return!1;for(c=a.length;c--;)if(!f(a[c],b[c]))return!1}if(e(a)||e(b))return a===b;g={};for(c in b){if(!f(a[c],b[c]))return!1;g[c]=!0}for(c in a)if(!g[c]&&!f(a[c],b[c]))return!1;return!0}return b.extend({Mixins:[c],init:function(b){var c,d;b=b||{};for(c in b)d=b[c],d instanceof a&&(b[c]=d.create(this,c));this.data=b},set:function(b,c){var d,e,g=this.data[b];if(c instanceof a&&(c=c.create(this,b)),"object"==typeof b){for(d in b)this.set(d,b[d]);return this}return f(g,c)||(this.data[b]=c,e={target:this,name:b,value:c,oldValue:g},this.fire("change:"+b,e),this.fire("change",e)),this},get:function(a){return this.data[a]},has:function(a){return a in this.data},bind:function(b){return a.create(this,b)},destroy:function(){this.fire("destroy")}})}),g("2d",["x"],function(a){"use strict";function b(a){for(var b,c=[],d=a.length;d--;)b=a[d],b.__checked||(c.push(b),b.__checked=1);for(d=c.length;d--;)delete c[d].__checked;return c}var c,d=/^([\w\\*]+)?(?:#([\w\-\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i,e=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,f=/^\s*|\s*$/g,g=a.extend({init:function(a){function b(a){if(a)return a=a.toLowerCase(),function(b){return"*"===a||b.type===a}}function c(a){if(a)return function(b){return b._name===a}}function g(a){if(a)return a=a.split("."),function(b){for(var c=a.length;c--;)if(!b.classes.contains(a[c]))return!1;return!0}}function h(a,b,c){if(a)return function(d){var e=d[a]?d[a]():"";return b?"="===b?e===c:"*="===b?e.indexOf(c)>=0:"~="===b?(" "+e+" ").indexOf(" "+c+" ")>=0:"!="===b?e!=c:"^="===b?0===e.indexOf(c):"$="===b&&e.substr(e.length-c.length)===c:!!c}}function i(a){var b;if(a)return a=/(?:not\((.+)\))|(.+)/i.exec(a),a[1]?(b=k(a[1],[]),function(a){return!l(a,b)}):(a=a[2],function(b,c,d){return"first"===a?0===c:"last"===a?c===d-1:"even"===a?c%2===0:"odd"===a?c%2===1:!!b[a]&&b[a]()})}function j(a,e,j){function k(a){a&&e.push(a)}var l;return l=d.exec(a.replace(f,"")),k(b(l[1])),k(c(l[2])),k(g(l[3])),k(h(l[4],l[5],l[6])),k(i(l[7])),e.pseudo=!!l[7],e.direct=j,e}function k(a,b){var c,d,f,g=[];do if(e.exec(""),d=e.exec(a),d&&(a=d[3],g.push(d[1]),d[2])){c=d[3];break}while(d);for(c&&k(c,b),a=[],f=0;f<g.length;f++)">"!=g[f]&&a.push(j(g[f],[],">"===g[f-1]));return b.push(a),b}var l=this.match;this._selectors=k(a,[])},match:function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o;for(b=b||this._selectors,c=0,d=b.length;c<d;c++){for(g=b[c],f=g.length,o=a,n=0,e=f-1;e>=0;e--)for(j=g[e];o;){if(j.pseudo)for(m=o.parent().items(),k=l=m.length;k--&&m[k]!==o;);for(h=0,i=j.length;h<i;h++)if(!j[h](o,k,l)){h=i+1;break}if(h===i){n++;break}if(e===f-1)break;o=o.parent()}if(n===f)return!0}return!1},find:function(a){function d(a,b,c){var e,f,g,i,j,k=b[c];for(e=0,f=a.length;e<f;e++){for(j=a[e],g=0,i=k.length;g<i;g++)if(!k[g](j,e,f)){g=i+1;break}if(g===i)c==b.length-1?h.push(j):j.items&&d(j.items(),b,c+1);else if(k.direct)return;j.items&&d(j.items(),b,c)}}var e,f,h=[],i=this._selectors;if(a.items){for(e=0,f=i.length;e<f;e++)d(a.items(),i[e],0);f>1&&(h=b(h))}return c||(c=g.Collection),new c(h)}});return g}),g("2e",["9","2d","x"],function(a,b,c){"use strict";var d,e,f=Array.prototype.push,g=Array.prototype.slice;return e={length:0,init:function(a){a&&this.add(a)},add:function(b){var c=this;return a.isArray(b)?f.apply(c,b):b instanceof d?c.add(b.toArray()):f.call(c,b),c},set:function(a){var b,c=this,d=c.length;for(c.length=0,c.add(a),b=c.length;b<d;b++)delete c[b];return c},filter:function(a){var c,e,f,g,h=this,i=[];for("string"==typeof a?(a=new b(a),g=function(b){return a.match(b)}):g=a,c=0,e=h.length;c<e;c++)f=h[c],g(f)&&i.push(f);return new d(i)},slice:function(){return new d(g.apply(this,arguments))},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},each:function(b){return a.each(this,b),this},toArray:function(){return a.toArray(this)},indexOf:function(a){for(var b=this,c=b.length;c--&&b[c]!==a;);return c},reverse:function(){return new d(a.toArray(this).reverse())},hasClass:function(a){return!!this[0]&&this[0].classes.contains(a)},prop:function(a,b){var c,d,e=this;return b!==c?(e.each(function(c){c[a]&&c[a](b)}),e):(d=e[0],d&&d[a]?d[a]():void 0)},exec:function(b){var c=this,d=a.toArray(arguments).slice(1);return c.each(function(a){a[b]&&a[b].apply(a,d)}),c},remove:function(){for(var a=this.length;a--;)this[a].remove();return this},addClass:function(a){return this.each(function(b){b.classes.add(a)})},removeClass:function(a){return this.each(function(b){b.classes.remove(a)})}},a.each("fire on off show hide append prepend before after reflow".split(" "),function(b){e[b]=function(){var c=a.toArray(arguments);return this.each(function(a){b in a&&a[b].apply(a,c)}),this}}),a.each("text name disabled active selected checked visible parent value data".split(" "),function(a){e[a]=function(b){return this.prop(a,b)}}),d=c.extend(e),b.Collection=d,d}),g("4q",["6","9","e"],function(a,b,c){"use strict";var d=0,e={id:function(){return"mceu_"+d++},create:function(a,d,e){var f=document.createElement(a);return c.DOM.setAttribs(f,d),"string"==typeof e?f.innerHTML=e:b.each(e,function(a){a.nodeType&&f.appendChild(a)}),f},createFragment:function(a){return c.DOM.createFragment(a)},getWindowSize:function(){return c.DOM.getViewPort()},getSize:function(a){var b,c;if(a.getBoundingClientRect){var d=a.getBoundingClientRect();b=Math.max(d.width||d.right-d.left,a.offsetWidth),c=Math.max(d.height||d.bottom-d.bottom,a.offsetHeight)}else b=a.offsetWidth,c=a.offsetHeight;return{width:b,height:c}},getPos:function(a,b){return c.DOM.getPos(a,b||e.getContainer())},getContainer:function(){return a.container?a.container:document.body},getViewPort:function(a){return c.DOM.getViewPort(a)},get:function(a){return document.getElementById(a)},addClass:function(a,b){return c.DOM.addClass(a,b)},removeClass:function(a,b){return c.DOM.removeClass(a,b)},hasClass:function(a,b){return c.DOM.hasClass(a,b)},toggleClass:function(a,b,d){return c.DOM.toggleClass(a,b,d)},css:function(a,b,d){return c.DOM.setStyle(a,b,d)},getRuntimeStyle:function(a,b){return c.DOM.getStyle(a,b,!0)},on:function(a,b,d,e){return c.DOM.bind(a,b,d,e)},off:function(a,b,d){return c.DOM.unbind(a,b,d)},fire:function(a,b,d){return c.DOM.fire(a,b,d)},innerHtml:function(a,b){c.DOM.setHTML(a,b)}};return e}),g("4r",[],function(){"use strict";return{parseBox:function(a){var b,c=10;if(a)return"number"==typeof a?(a=a||0,{top:a,left:a,bottom:a,right:a}):(a=a.split(" "),b=a.length,1===b?a[1]=a[2]=a[3]=a[0]:2===b?(a[2]=a[0],a[3]=a[1]):3===b&&(a[3]=a[1]),{top:parseInt(a[0],c)||0,right:parseInt(a[1],c)||0,bottom:parseInt(a[2],c)||0,left:parseInt(a[3],c)||0})},measureBox:function(a,b){function c(b){var c=document.defaultView;return c?(b=b.replace(/[A-Z]/g,function(a){return"-"+a}),c.getComputedStyle(a,null).getPropertyValue(b)):a.currentStyle[b]}function d(a){var b=parseFloat(c(a),10);return isNaN(b)?0:b}return{top:d(b+"TopWidth"),right:d(b+"RightWidth"),bottom:d(b+"BottomWidth"),left:d(b+"LeftWidth")}}}}),g("4s",["9"],function(a){"use strict";function b(){}function c(a){this.cls=[],this.cls._map={},this.onchange=a||b,this.prefix=""}return a.extend(c.prototype,{add:function(a){return a&&!this.contains(a)&&(this.cls._map[a]=!0,this.cls.push(a),this._change()),this},remove:function(a){if(this.contains(a)){for(var b=0;b<this.cls.length&&this.cls[b]!==a;b++);this.cls.splice(b,1),delete this.cls._map[a],this._change()}return this},toggle:function(a,b){var c=this.contains(a);return c!==b&&(c?this.remove(a):this.add(a),this._change()),this},contains:function(a){return!!this.cls._map[a]},_change:function(){delete this.clsValue,this.onchange.call(this)}}),c.prototype.toString=function(){var a;if(this.clsValue)return this.clsValue;a="";for(var b=0;b<this.cls.length;b++)b>0&&(a+=" "),a+=this.prefix+this.cls[b];return a},c}),g("2f",["5"],function(a){var b,c={};return{add:function(d){var e=d.parent();if(e){if(!e._layout||e._layout.isNative())return;c[e._id]||(c[e._id]=e),b||(b=!0,a.requestAnimationFrame(function(){var a,d;b=!1;for(a in c)d=c[a],d.state.get("rendered")&&d.reflow();c={}},document.body))}},remove:function(a){c[a._id]&&delete c[a._id]}}}),g("2g",["x","9","y","4p","2e","4q","a","4r","4s","2f"],function(a,b,c,d,e,f,g,h,i,j){"use strict";function k(a){return a._eventDispatcher||(a._eventDispatcher=new c({scope:a,toggleEvent:function(b,d){d&&c.isNative(b)&&(a._nativeEvents||(a._nativeEvents={}),a._nativeEvents[b]=!0,a.state.get("rendered")&&l(a))}})),a._eventDispatcher}function l(a){function b(b){var c=a.getParentCtrl(b.target);c&&c.fire(b.type,b)}function c(){var a=j._lastHoverCtrl;a&&(a.fire("mouseleave",{target:a.getEl()}),a.parents().each(function(a){a.fire("mouseleave",{target:a.getEl()})}),j._lastHoverCtrl=null)}function d(b){var c,d,e,f=a.getParentCtrl(b.target),g=j._lastHoverCtrl,h=0;if(f!==g){if(j._lastHoverCtrl=f,d=f.parents().toArray().reverse(),d.push(f),g){for(e=g.parents().toArray().reverse(),e.push(g),h=0;h<e.length&&d[h]===e[h];h++);for(c=e.length-1;c>=h;c--)g=e[c],g.fire("mouseleave",{target:g.getEl()})}for(c=h;c<d.length;c++)f=d[c],f.fire("mouseenter",{target:f.getEl()})}}function e(b){b.preventDefault(),"mousewheel"==b.type?(b.deltaY=-.025*b.wheelDelta,b.wheelDeltaX&&(b.deltaX=-.025*b.wheelDeltaX)):(b.deltaX=0,b.deltaY=b.detail),b=a.fire("wheel",b)}var f,h,i,j,k,l;if(k=a._nativeEvents){for(i=a.parents().toArray(),i.unshift(a),f=0,h=i.length;!j&&f<h;f++)j=i[f]._eventsRoot;for(j||(j=i[i.length-1]||a),a._eventsRoot=j,h=f,f=0;f<h;f++)i[f]._eventsRoot=j;var m=j._delegates;m||(m=j._delegates={});for(l in k){if(!k)return!1;"wheel"!==l||o?("mouseenter"===l||"mouseleave"===l?j._hasMouseEnter||(g(j.getEl()).on("mouseleave",c).on("mouseover",d),j._hasMouseEnter=1):m[l]||(g(j.getEl()).on(l,b),m[l]=!0),k[l]=!1):n?g(a.getEl()).on("mousewheel",e):g(a.getEl()).on("DOMMouseScroll",e)}}}var m,n="onmousewheel"in document,o=!1,p="mce-",q=0,r={Statics:{classPrefix:p},isRtl:function(){return m.rtl},classPrefix:p,init:function(a){function c(a){var b;for(a=a.split(" "),b=0;b<a.length;b++)j.classes.add(a[b])}var e,f,j=this;j.settings=a=b.extend({},j.Defaults,a),j._id=a.id||"mceu_"+q++,j._aria={role:a.role},j._elmCache={},j.$=g,j.state=new d({visible:!0,active:!1,disabled:!1,value:""}),j.data=new d(a.data),j.classes=new i(function(){j.state.get("rendered")&&(j.getEl().className=this.toString())}),j.classes.prefix=j.classPrefix,e=a.classes,e&&(j.Defaults&&(f=j.Defaults.classes,f&&e!=f&&c(f)),c(e)),b.each("title text name visible disabled active value".split(" "),function(b){b in a&&j[b](a[b])}),j.on("click",function(){if(j.disabled())return!1}),j.settings=a,j.borderBox=h.parseBox(a.border),j.paddingBox=h.parseBox(a.padding),j.marginBox=h.parseBox(a.margin),a.hidden&&j.hide()},Properties:"parent,name",getContainerElm:function(){return f.getContainer()},getParentCtrl:function(a){for(var b,c=this.getRoot().controlIdLookup;a&&c&&!(b=c[a.id]);)a=a.parentNode;return b},initLayoutRect:function(){var a,b,c,d,e,g,i,j,k,l,m=this,n=m.settings,o=m.getEl();a=m.borderBox=m.borderBox||h.measureBox(o,"border"),m.paddingBox=m.paddingBox||h.measureBox(o,"padding"),m.marginBox=m.marginBox||h.measureBox(o,"margin"),l=f.getSize(o),j=n.minWidth,k=n.minHeight,e=j||l.width,g=k||l.height,c=n.width,d=n.height,i=n.autoResize,i="undefined"!=typeof i?i:!c&&!d,c=c||e,d=d||g;var p=a.left+a.right,q=a.top+a.bottom,r=n.maxWidth||65535,s=n.maxHeight||65535;return m._layoutRect=b={x:n.x||0,y:n.y||0,w:c,h:d,deltaW:p,deltaH:q,contentW:c-p,contentH:d-q,innerW:c-p,innerH:d-q,startMinWidth:j||0,startMinHeight:k||0,minW:Math.min(e,r),minH:Math.min(g,s),maxW:r,maxH:s,autoResize:i,scrollW:0},m._lastLayoutRect={},b},layoutRect:function(a){var b,c,d,e,f,g,h=this,i=h._layoutRect;return i||(i=h.initLayoutRect()),a?(d=i.deltaW,e=i.deltaH,a.x!==f&&(i.x=a.x),a.y!==f&&(i.y=a.y),a.minW!==f&&(i.minW=a.minW),a.minH!==f&&(i.minH=a.minH),c=a.w,c!==f&&(c=c<i.minW?i.minW:c,c=c>i.maxW?i.maxW:c,i.w=c,i.innerW=c-d),c=a.h,c!==f&&(c=c<i.minH?i.minH:c,c=c>i.maxH?i.maxH:c,i.h=c,i.innerH=c-e),c=a.innerW,c!==f&&(c=c<i.minW-d?i.minW-d:c,c=c>i.maxW-d?i.maxW-d:c,i.innerW=c,i.w=c+d),c=a.innerH,c!==f&&(c=c<i.minH-e?i.minH-e:c,c=c>i.maxH-e?i.maxH-e:c,i.innerH=c,i.h=c+e),a.contentW!==f&&(i.contentW=a.contentW),a.contentH!==f&&(i.contentH=a.contentH),b=h._lastLayoutRect,b.x===i.x&&b.y===i.y&&b.w===i.w&&b.h===i.h||(g=m.repaintControls,g&&g.map&&!g.map[h._id]&&(g.push(h),g.map[h._id]=!0),b.x=i.x,b.y=i.y,b.w=i.w,b.h=i.h),h):i},repaint:function(){var a,b,c,d,e,f,g,h,i,j,k=this;i=document.createRange?function(a){return a}:Math.round,a=k.getEl().style,d=k._layoutRect,h=k._lastRepaintRect||{},e=k.borderBox,f=e.left+e.right,g=e.top+e.bottom,d.x!==h.x&&(a.left=i(d.x)+"px",h.x=d.x),d.y!==h.y&&(a.top=i(d.y)+"px",h.y=d.y),d.w!==h.w&&(j=i(d.w-f),a.width=(j>=0?j:0)+"px",h.w=d.w),d.h!==h.h&&(j=i(d.h-g),a.height=(j>=0?j:0)+"px",h.h=d.h),k._hasBody&&d.innerW!==h.innerW&&(j=i(d.innerW),c=k.getEl("body"),c&&(b=c.style,b.width=(j>=0?j:0)+"px"),h.innerW=d.innerW),k._hasBody&&d.innerH!==h.innerH&&(j=i(d.innerH),c=c||k.getEl("body"),c&&(b=b||c.style,b.height=(j>=0?j:0)+"px"),h.innerH=d.innerH),k._lastRepaintRect=h,k.fire("repaint",{},!1)},updateLayoutRect:function(){var a=this;a.parent()._lastRect=null,f.css(a.getEl(),{width:"",height:""}),a._layoutRect=a._lastRepaintRect=a._lastLayoutRect=null,a.initLayoutRect()},on:function(a,b){function c(a){var b,c;return"string"!=typeof a?a:function(e){return b||d.parentsAndSelf().each(function(d){var e=d.settings.callbacks;if(e&&(b=e[a]))return c=d,!1}),b?b.call(c,e):(e.action=a,void this.fire("execute",e))}}var d=this;return k(d).on(a,c(b)),d},off:function(a,b){return k(this).off(a,b),this},fire:function(a,b,c){var d=this;if(b=b||{},b.control||(b.control=d),b=k(d).fire(a,b),c!==!1&&d.parent)for(var e=d.parent();e&&!b.isPropagationStopped();)e.fire(a,b,!1),e=e.parent();return b},hasEventListeners:function(a){return k(this).has(a)},parents:function(a){var b,c=this,d=new e;for(b=c.parent();b;b=b.parent())d.add(b);return a&&(d=d.filter(a)),d},parentsAndSelf:function(a){return new e(this).add(this.parents(a))},next:function(){var a=this.parent().items();return a[a.indexOf(this)+1]},prev:function(){var a=this.parent().items();return a[a.indexOf(this)-1]},innerHtml:function(a){return this.$el.html(a),this},getEl:function(a){var b=a?this._id+"-"+a:this._id;return this._elmCache[b]||(this._elmCache[b]=g("#"+b)[0]),this._elmCache[b]},show:function(){return this.visible(!0)},hide:function(){return this.visible(!1)},focus:function(){try{this.getEl().focus()}catch(a){}return this},blur:function(){return this.getEl().blur(),this},aria:function(a,b){var c=this,d=c.getEl(c.ariaTarget);return"undefined"==typeof b?c._aria[a]:(c._aria[a]=b,c.state.get("rendered")&&d.setAttribute("role"==a?a:"aria-"+a,b),c)},encode:function(a,b){return b!==!1&&(a=this.translate(a)),(a||"").replace(/[&<>"]/g,function(a){return"&#"+a.charCodeAt(0)+";"})},translate:function(a){return m.translate?m.translate(a):a},before:function(a){var b=this,c=b.parent();return c&&c.insert(a,c.items().indexOf(b),!0),b},after:function(a){var b=this,c=b.parent();return c&&c.insert(a,c.items().indexOf(b)),b},remove:function(){var a,b,c=this,d=c.getEl(),e=c.parent();if(c.items){var f=c.items().toArray();for(b=f.length;b--;)f[b].remove()}e&&e.items&&(a=[],e.items().each(function(b){b!==c&&a.push(b)}),e.items().set(a),e._lastRect=null),c._eventsRoot&&c._eventsRoot==c&&g(d).off();var h=c.getRoot().controlIdLookup;return h&&delete h[c._id],d&&d.parentNode&&d.parentNode.removeChild(d),c.state.set("rendered",!1),c.state.destroy(),c.fire("remove"),c},renderBefore:function(a){return g(a).before(this.renderHtml()),this.postRender(),this},renderTo:function(a){return g(a||this.getContainerElm()).append(this.renderHtml()),this.postRender(),this},preRender:function(){},render:function(){},renderHtml:function(){return'<div id="'+this._id+'" class="'+this.classes+'"></div>'},postRender:function(){var a,b,c,d,e,f=this,h=f.settings;f.$el=g(f.getEl()),f.state.set("rendered",!0);for(d in h)0===d.indexOf("on")&&f.on(d.substr(2),h[d]);if(f._eventsRoot){for(c=f.parent();!e&&c;c=c.parent())e=c._eventsRoot;if(e)for(d in e._nativeEvents)f._nativeEvents[d]=!0}l(f),h.style&&(a=f.getEl(),a&&(a.setAttribute("style",h.style),a.style.cssText=h.style)),f.settings.border&&(b=f.borderBox,f.$el.css({"border-top-width":b.top,"border-right-width":b.right,"border-bottom-width":b.bottom,"border-left-width":b.left}));var i=f.getRoot();i.controlIdLookup||(i.controlIdLookup={}),i.controlIdLookup[f._id]=f;for(var k in f._aria)f.aria(k,f._aria[k]);f.state.get("visible")===!1&&(f.getEl().style.display="none"),f.bindStates(),f.state.on("change:visible",function(a){var b,c=a.value;f.state.get("rendered")&&(f.getEl().style.display=c===!1?"none":"",f.getEl().getBoundingClientRect()),b=f.parent(),b&&(b._lastRect=null),f.fire(c?"show":"hide"),j.add(f)}),f.fire("postrender",{},!1)},bindStates:function(){},scrollIntoView:function(a){function b(a,b){var c,d,e=a;for(c=d=0;e&&e!=b&&e.nodeType;)c+=e.offsetLeft||0,d+=e.offsetTop||0,e=e.offsetParent;return{x:c,y:d}}var c,d,e,f,g,h,i=this.getEl(),j=i.parentNode,k=b(i,j);return c=k.x,d=k.y,e=i.offsetWidth,f=i.offsetHeight,g=j.clientWidth,h=j.clientHeight,"end"==a?(c-=g-e,d-=h-f):"center"==a&&(c-=g/2-e/2,d-=h/2-f/2),j.scrollLeft=c,j.scrollTop=d,this},getRoot:function(){for(var a,b=this,c=[];b;){if(b.rootControl){a=b.rootControl;break}c.push(b),a=b,b=b.parent()}a||(a=this);for(var d=c.length;d--;)c[d].rootControl=a;return a},reflow:function(){j.remove(this);var a=this.parent();return a&&a._layout&&!a._layout.isNative()&&a.reflow(),this}};return b.each("text title visible disabled active value".split(" "),function(a){r[a]=function(b){return 0===arguments.length?this.state.get(a):("undefined"!=typeof b&&this.state.set(a,b),this)}}),m=a.extend(r)}),g("2h",[],function(){"use strict";var a={};return{add:function(b,c){a[b.toLowerCase()]=c},has:function(b){return!!a[b.toLowerCase()]},create:function(b,c){var d;if("string"==typeof b?(c=c||{},c.type=b):(c=b,b=c.type),b=b.toLowerCase(),d=a[b],!d)throw new Error("Could not find control by type: "+b);return d=new d(c),d.type=b,d}}}),g("2i",[],function(){"use strict";var a=function(a){return!!a.getAttribute("data-mce-tabstop")};return function(b){function c(a){return a&&1===a.nodeType}function d(a){return a=a||u,c(a)?a.getAttribute("role"):null}function e(a){for(var b,c=a||u;c=c.parentNode;)if(b=d(c))return b}function f(a){var b=u;if(c(b))return b.getAttribute("aria-"+a)}function g(a){var b=a.tagName.toUpperCase();return"INPUT"==b||"TEXTAREA"==b||"SELECT"==b}function h(b){return!(!g(b)||b.hidden)||(!!a(b)||!!/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell|slider)$/.test(d(b)))}function i(a){function b(a){if(1==a.nodeType&&"none"!=a.style.display&&!a.disabled){h(a)&&c.push(a);for(var d=0;d<a.childNodes.length;d++)b(a.childNodes[d])}}var c=[];return b(a||w.getEl()),c}function j(a){var b,c;a=a||v,c=a.parents().toArray(),c.unshift(a);for(var d=0;d<c.length&&(b=c[d],!b.settings.ariaRoot);d++);return b}function k(a){var b=j(a),c=i(b.getEl());b.settings.ariaRemember&&"lastAriaIndex"in b?l(b.lastAriaIndex,c):l(0,c)}function l(a,b){return a<0?a=b.length-1:a>=b.length&&(a=0),b[a]&&b[a].focus(),a}function m(a,b){var c=-1,d=j();b=b||i(d.getEl());for(var e=0;e<b.length;e++)b[e]===u&&(c=e);c+=a,d.lastAriaIndex=l(c,b)}function n(){var a=e();"tablist"==a?m(-1,i(u.parentNode)):v.parent().submenu?s():m(-1)}function o(){var a=d(),b=e();"tablist"==b?m(1,i(u.parentNode)):"menuitem"==a&&"menu"==b&&f("haspopup")?t():m(1)}function p(){m(-1)}function q(){var a=d(),b=e();"menuitem"==a&&"menubar"==b?t():"button"==a&&f("haspopup")?t({key:"down"}):m(1)}function r(a){var b=e();if("tablist"==b){var c=i(v.getEl("body"))[0];c&&c.focus()}else m(a.shiftKey?-1:1)}function s(){v.fire("cancel")}function t(a){a=a||{},v.fire("click",{target:u,aria:a})}var u,v,w=b.root;try{u=document.activeElement}catch(a){u=document.body}return v=w.getParentCtrl(u),w.on("keydown",function(b){function c(b,c){g(u)||a(u)||"slider"!==d(u)&&c(b)!==!1&&b.preventDefault()}if(!b.isDefaultPrevented())switch(b.keyCode){case 37:c(b,n);break;case 39:c(b,o);break;case 38:c(b,p);break;case 40:c(b,q);break;case 27:s();break;case 14:case 13:case 32:c(b,t);break;case 9:r(b)!==!1&&b.preventDefault()}}),w.on("focusin",function(a){u=a.target,v=a.control}),{focusFirst:k}}}),g("2j",["2g","2e","2d","2h","2i","9","a","4s","2f"],function(a,b,c,d,e,f,g,h,i){"use strict";var j={};return a.extend({init:function(a){var c=this;c._super(a),a=c.settings,a.fixed&&c.state.set("fixed",!0),c._items=new b,c.isRtl()&&c.classes.add("rtl"),c.bodyClasses=new h(function(){c.state.get("rendered")&&(c.getEl("body").className=this.toString())}),c.bodyClasses.prefix=c.classPrefix,c.classes.add("container"),c.bodyClasses.add("container-body"),a.containerCls&&c.classes.add(a.containerCls),c._layout=d.create((a.layout||"")+"layout"),c.settings.items?c.add(c.settings.items):c.add(c.render()),c._hasBody=!0},items:function(){return this._items},find:function(a){return a=j[a]=j[a]||new c(a),a.find(this)},add:function(a){var b=this;return b.items().add(b.create(a)).parent(b),b},focus:function(a){var b,c,d,e=this;return a&&(c=e.keyboardNav||e.parents().eq(-1)[0].keyboardNav)?void c.focusFirst(e):(d=e.find("*"),e.statusbar&&d.add(e.statusbar.items()),d.each(function(a){return a.settings.autofocus?(b=null,!1):void(a.canFocus&&(b=b||a))}),b&&b.focus(),e)},replace:function(a,b){for(var c,d=this.items(),e=d.length;e--;)if(d[e]===a){d[e]=b;break}e>=0&&(c=b.getEl(),c&&c.parentNode.removeChild(c),c=a.getEl(),c&&c.parentNode.removeChild(c)),b.parent(this)},create:function(b){var c,e=this,g=[];return f.isArray(b)||(b=[b]),f.each(b,function(b){b&&(b instanceof a||("string"==typeof b&&(b={type:b}),c=f.extend({},e.settings.defaults,b),b.type=c.type=c.type||b.type||e.settings.defaultType||(c.defaults?c.defaults.type:null),b=d.create(c)),g.push(b))}),g},renderNew:function(){var a=this;return a.items().each(function(b,c){var d;b.parent(a),b.state.get("rendered")||(d=a.getEl("body"),d.hasChildNodes()&&c<=d.childNodes.length-1?g(d.childNodes[c]).before(b.renderHtml()):g(d).append(b.renderHtml()),b.postRender(),i.add(b))}),a._layout.applyClasses(a.items().filter(":visible")),a._lastRect=null,a},append:function(a){return this.add(a).renderNew()},prepend:function(a){var b=this;return b.items().set(b.create(a).concat(b.items().toArray())),b.renderNew()},insert:function(a,b,c){var d,e,f,g=this;return a=g.create(a),d=g.items(),!c&&b<d.length-1&&(b+=1),b>=0&&b<d.length&&(e=d.slice(0,b).toArray(),f=d.slice(b).toArray(),d.set(e.concat(a,f))),g.renderNew()},fromJSON:function(a){var b=this;for(var c in a)b.find("#"+c).value(a[c]);return b},toJSON:function(){var a=this,b={};return a.find("*").each(function(a){var c=a.name(),d=a.value();c&&"undefined"!=typeof d&&(b[c]=d)}),b},renderHtml:function(){var a=this,b=a._layout,c=this.settings.role;return a.preRender(),b.preRender(a),'<div id="'+a._id+'" class="'+a.classes+'"'+(c?' role="'+this.settings.role+'"':"")+'><div id="'+a._id+'-body" class="'+a.bodyClasses+'">'+(a.settings.html||"")+b.renderHtml(a)+"</div></div>"},postRender:function(){var a,b=this;return b.items().exec("postRender"),b._super(),b._layout.postRender(b),b.state.set("rendered",!0),b.settings.style&&b.$el.css(b.settings.style),b.settings.border&&(a=b.borderBox,b.$el.css({"border-top-width":a.top,"border-right-width":a.right,"border-bottom-width":a.bottom,"border-left-width":a.left})),b.parent()||(b.keyboardNav=new e({root:b})),b},initLayoutRect:function(){var a=this,b=a._super();return a._layout.recalc(a),b},recalc:function(){var a=this,b=a._layoutRect,c=a._lastRect;if(!c||c.w!=b.w||c.h!=b.h)return a._layout.recalc(a),b=a.layoutRect(),a._lastRect={x:b.x,y:b.y,w:b.w,h:b.h},!0},reflow:function(){var b;if(i.remove(this),this.visible()){for(a.repaintControls=[],a.repaintControls.map={},this.recalc(),b=a.repaintControls.length;b--;)a.repaintControls[b].repaint();"flow"!==this.settings.layout&&"stack"!==this.settings.layout&&this.repaint(),a.repaintControls=[]}return this}})}),g("2k",["a"],function(a){"use strict";function b(a){var b,c,d,e,f,g,h,i,j=Math.max;return b=a.documentElement,c=a.body,d=j(b.scrollWidth,c.scrollWidth),e=j(b.clientWidth,c.clientWidth),f=j(b.offsetWidth,c.offsetWidth),g=j(b.scrollHeight,c.scrollHeight),h=j(b.clientHeight,c.clientHeight),i=j(b.offsetHeight,c.offsetHeight),{width:d<f?e:d,height:g<i?h:g}}function c(a){var b,c;if(a.changedTouches)for(b="screenX screenY pageX pageY clientX clientY".split(" "),c=0;c<b.length;c++)a[b[c]]=a.changedTouches[0][b[c]]}return function(d,e){function f(){return n.getElementById(e.handle||d)}var g,h,i,j,k,l,m,n=e.document||document;e=e||{},i=function(d){var i,o,p=b(n);c(d),d.preventDefault(),h=d.button,i=f(),l=d.screenX,m=d.screenY,o=window.getComputedStyle?window.getComputedStyle(i,null).getPropertyValue("cursor"):i.runtimeStyle.cursor,g=a("<div></div>").css({position:"absolute",top:0,left:0,width:p.width,height:p.height,zIndex:2147483647,opacity:1e-4,cursor:o}).appendTo(n.body),a(n).on("mousemove touchmove",k).on("mouseup touchend",j),e.start(d)},k=function(a){return c(a),a.button!==h?j(a):(a.deltaX=a.screenX-l,a.deltaY=a.screenY-m,a.preventDefault(),void e.drag(a))},j=function(b){c(b),a(n).off("mousemove touchmove",k).off("mouseup touchend",j),g.remove(),e.stop&&e.stop(b)},this.destroy=function(){a(f()).off()},a(f()).on("mousedown touchstart",i)}}),g("2l",["a","2k"],function(a,b){"use strict";return{init:function(){var a=this;a.on("repaint",a.renderScroll)},renderScroll:function(){function c(){function b(b,g,h,i,j,k){var l,m,n,o,p,q,r,s,t;if(m=e.getEl("scroll"+b)){if(s=g.toLowerCase(),t=h.toLowerCase(),a(e.getEl("absend")).css(s,e.layoutRect()[i]-1),!j)return void a(m).css("display","none");a(m).css("display","block"),l=e.getEl("body"),n=e.getEl("scroll"+b+"t"),o=l["client"+h]-2*f,o-=c&&d?m["client"+k]:0,p=l["scroll"+h],q=o/p,r={},r[s]=l["offset"+g]+f,r[t]=o,a(m).css(r),r={},r[s]=l["scroll"+g]*q,r[t]=o*q,a(n).css(r)}}var c,d,g;g=e.getEl("body"),c=g.scrollWidth>g.clientWidth,d=g.scrollHeight>g.clientHeight,b("h","Left","Width","contentW",c,"Height"),b("v","Top","Height","contentH",d,"Width")}function d(){function c(c,d,g,h,i){var j,k=e._id+"-scroll"+c,l=e.classPrefix;a(e.getEl()).append('<div id="'+k+'" class="'+l+"scrollbar "+l+"scrollbar-"+c+'"><div id="'+k+'t" class="'+l+'scrollbar-thumb"></div></div>'),e.draghelper=new b(k+"t",{start:function(){j=e.getEl("body")["scroll"+d],a("#"+k).addClass(l+"active")},drag:function(a){var b,k,l,m,n=e.layoutRect();k=n.contentW>n.innerW,l=n.contentH>n.innerH,m=e.getEl("body")["client"+g]-2*f,m-=k&&l?e.getEl("scroll"+c)["client"+i]:0,b=m/e.getEl("body")["scroll"+g],e.getEl("body")["scroll"+d]=j+a["delta"+h]/b},stop:function(){a("#"+k).removeClass(l+"active")}})}e.classes.add("scroll"),c("v","Top","Height","Y","Width"),c("h","Left","Width","X","Height")}var e=this,f=2; +e.settings.autoScroll&&(e._hasScroll||(e._hasScroll=!0,d(),e.on("wheel",function(a){var b=e.getEl("body");b.scrollLeft+=10*(a.deltaX||0),b.scrollTop+=10*a.deltaY,c()}),a(e.getEl("body")).on("scroll",c)),c())}}}),g("2m",["2j","2l"],function(a,b){"use strict";return a.extend({Defaults:{layout:"fit",containerCls:"panel"},Mixins:[b],renderHtml:function(){var a=this,b=a._layout,c=a.settings.html;return a.preRender(),b.preRender(a),"undefined"==typeof c?c='<div id="'+a._id+'-body" class="'+a.bodyClasses+'">'+b.renderHtml(a)+"</div>":("function"==typeof c&&(c=c.call(a)),a._hasBody=!1),'<div id="'+a._id+'" class="'+a.classes+'" hidefocus="1" tabindex="-1" role="group">'+(a._preBodyHtml||"")+c+"</div>"}})}),g("2n",["4q"],function(a){"use strict";function b(b,c,d){var e,f,g,h,i,j,k,l,m,n;return m=a.getViewPort(),f=a.getPos(c),g=f.x,h=f.y,b.state.get("fixed")&&"static"==a.getRuntimeStyle(document.body,"position")&&(g-=m.x,h-=m.y),e=b.getEl(),n=a.getSize(e),i=n.width,j=n.height,n=a.getSize(c),k=n.width,l=n.height,d=(d||"").split(""),"b"===d[0]&&(h+=l),"r"===d[1]&&(g+=k),"c"===d[0]&&(h+=Math.round(l/2)),"c"===d[1]&&(g+=Math.round(k/2)),"b"===d[3]&&(h-=j),"r"===d[4]&&(g-=i),"c"===d[3]&&(h-=Math.round(j/2)),"c"===d[4]&&(g-=Math.round(i/2)),{x:g,y:h,w:i,h:j}}return{testMoveRel:function(c,d){for(var e=a.getViewPort(),f=0;f<d.length;f++){var g=b(this,c,d[f]);if(this.state.get("fixed")){if(g.x>0&&g.x+g.w<e.w&&g.y>0&&g.y+g.h<e.h)return d[f]}else if(g.x>e.x&&g.x+g.w<e.w+e.x&&g.y>e.y&&g.y+g.h<e.h+e.y)return d[f]}return d[0]},moveRel:function(a,c){"string"!=typeof c&&(c=this.testMoveRel(a,c));var d=b(this,a,c);return this.moveTo(d.x,d.y)},moveBy:function(a,b){var c=this,d=c.layoutRect();return c.moveTo(d.x+a,d.y+b),c},moveTo:function(b,c){function d(a,b,c){return a<0?0:a+c>b?(a=b-c,a<0?0:a):a}var e=this;if(e.settings.constrainToViewport){var f=a.getViewPort(window),g=e.layoutRect();b=d(b,f.w+f.x,g.w),c=d(c,f.h+f.y,g.h)}return e.state.get("rendered")?e.layoutRect({x:b,y:c}).repaint():(e.settings.x=b,e.settings.y=c),e.fire("move",{x:b,y:c}),e}}}),g("2o",["4q"],function(a){"use strict";return{resizeToContent:function(){this._layoutRect.autoResize=!0,this._lastRect=null,this.reflow()},resizeTo:function(b,c){if(b<=1||c<=1){var d=a.getWindowSize();b=b<=1?b*d.w:b,c=c<=1?c*d.h:c}return this._layoutRect.autoResize=!1,this.layoutRect({minW:b,minH:c,w:b,h:c}).reflow()},resizeBy:function(a,b){var c=this,d=c.layoutRect();return c.resizeTo(d.w+a,d.h+b)}}}),g("2p",["2m","2n","2o","4q","a","5"],function(a,b,c,d,e,f){"use strict";function g(a,b){for(;a;){if(a==b)return!0;a=a.parent()}}function h(a){for(var b=s.length;b--;){var c=s[b],d=c.getParentCtrl(a.target);if(c.settings.autohide){if(d&&(g(d,c)||c.parent()===d))continue;a=c.fire("autohide",{target:a.target}),a.isDefaultPrevented()||c.hide()}}}function i(){o||(o=function(a){2!=a.button&&h(a)},e(document).on("click touchstart",o))}function j(){p||(p=function(){var a;for(a=s.length;a--;)l(s[a])},e(window).on("scroll",p))}function k(){if(!q){var a=document.documentElement,b=a.clientWidth,c=a.clientHeight;q=function(){document.all&&b==a.clientWidth&&c==a.clientHeight||(b=a.clientWidth,c=a.clientHeight,u.hideAll())},e(window).on("resize",q)}}function l(a){function b(b,c){for(var d,e=0;e<s.length;e++)if(s[e]!=a)for(d=s[e].parent();d&&(d=d.parent());)d==a&&s[e].fixed(b).moveBy(0,c).repaint()}var c=d.getViewPort().y;a.settings.autofix&&(a.state.get("fixed")?a._autoFixY>c&&(a.fixed(!1).layoutRect({y:a._autoFixY}).repaint(),b(!1,a._autoFixY-c)):(a._autoFixY=a.layoutRect().y,a._autoFixY<c&&(a.fixed(!0).layoutRect({y:0}).repaint(),b(!0,c-a._autoFixY))))}function m(a,b){var c,d,f=u.zIndex||65535;if(a)t.push(b);else for(c=t.length;c--;)t[c]===b&&t.splice(c,1);if(t.length)for(c=0;c<t.length;c++)t[c].modal&&(f++,d=t[c]),t[c].getEl().style.zIndex=f,t[c].zIndex=f,f++;var g=e("#"+b.classPrefix+"modal-block",b.getContainerElm())[0];d?e(g).css("z-index",d.zIndex-1):g&&(g.parentNode.removeChild(g),r=!1),u.currentZIndex=f}function n(a){var b;for(b=s.length;b--;)s[b]===a&&s.splice(b,1);for(b=t.length;b--;)t[b]===a&&t.splice(b,1)}var o,p,q,r,s=[],t=[],u=a.extend({Mixins:[b,c],init:function(a){var b=this;b._super(a),b._eventsRoot=b,b.classes.add("floatpanel"),a.autohide&&(i(),k(),s.push(b)),a.autofix&&(j(),b.on("move",function(){l(this)})),b.on("postrender show",function(a){if(a.control==b){var c,d=b.classPrefix;b.modal&&!r&&(c=e("#"+d+"modal-block",b.getContainerElm()),c[0]||(c=e('<div id="'+d+'modal-block" class="'+d+"reset "+d+'fade"></div>').appendTo(b.getContainerElm())),f.setTimeout(function(){c.addClass(d+"in"),e(b.getEl()).addClass(d+"in")}),r=!0),m(!0,b)}}),b.on("show",function(){b.parents().each(function(a){if(a.state.get("fixed"))return b.fixed(!0),!1})}),a.popover&&(b._preBodyHtml='<div class="'+b.classPrefix+'arrow"></div>',b.classes.add("popover").add("bottom").add(b.isRtl()?"end":"start")),b.aria("label",a.ariaLabel),b.aria("labelledby",b._id),b.aria("describedby",b.describedBy||b._id+"-none")},fixed:function(a){var b=this;if(b.state.get("fixed")!=a){if(b.state.get("rendered")){var c=d.getViewPort();a?b.layoutRect().y-=c.y:b.layoutRect().y+=c.y}b.classes.toggle("fixed",a),b.state.set("fixed",a)}return b},show:function(){var a,b=this,c=b._super();for(a=s.length;a--&&s[a]!==b;);return a===-1&&s.push(b),c},hide:function(){return n(this),m(!1,this),this._super()},hideAll:function(){u.hideAll()},close:function(){var a=this;return a.fire("close").isDefaultPrevented()||(a.remove(),m(!1,a)),a},remove:function(){n(this),this._super()},postRender:function(){var a=this;return a.settings.bodyRole&&this.getEl("body").setAttribute("role",a.settings.bodyRole),a._super()}});return u.hideAll=function(){for(var a=s.length;a--;){var b=s[a];b&&b.settings.autohide&&(b.hide(),s.splice(a,1))}},u}),g("23",["2p","2m","4q","a","2k","4r","6","5"],function(a,b,c,d,e,f,g,h){"use strict";function i(a){var b,c="width=device-width,initial-scale=1.0,user-scalable=0,minimum-scale=1.0,maximum-scale=1.0",e=d("meta[name=viewport]")[0];g.overrideViewPort!==!1&&(e||(e=document.createElement("meta"),e.setAttribute("name","viewport"),document.getElementsByTagName("head")[0].appendChild(e)),b=e.getAttribute("content"),b&&"undefined"!=typeof n&&(n=b),e.setAttribute("content",a?c:n))}function j(a,b){k()&&b===!1&&d([document.documentElement,document.body]).removeClass(a+"fullscreen")}function k(){for(var a=0;a<m.length;a++)if(m[a]._fullscreen)return!0;return!1}function l(){function a(){var a,b,d=c.getWindowSize();for(a=0;a<m.length;a++)b=m[a].layoutRect(),m[a].moveTo(m[a].settings.x||Math.max(0,d.w/2-b.w/2),m[a].settings.y||Math.max(0,d.h/2-b.h/2))}if(!g.desktop){var b={w:window.innerWidth,h:window.innerHeight};h.setInterval(function(){var a=window.innerWidth,c=window.innerHeight;b.w==a&&b.h==c||(b={w:a,h:c},d(window).trigger("resize"))},100)}d(window).on("resize",a)}var m=[],n="",o=a.extend({modal:!0,Defaults:{border:1,layout:"flex",containerCls:"panel",role:"dialog",callbacks:{submit:function(){this.fire("submit",{data:this.toJSON()})},close:function(){this.close()}}},init:function(a){var d=this;d._super(a),d.isRtl()&&d.classes.add("rtl"),d.classes.add("window"),d.bodyClasses.add("window-body"),d.state.set("fixed",!0),a.buttons&&(d.statusbar=new b({layout:"flex",border:"1 0 0 0",spacing:3,padding:10,align:"center",pack:d.isRtl()?"start":"end",defaults:{type:"button"},items:a.buttons}),d.statusbar.classes.add("foot"),d.statusbar.parent(d)),d.on("click",function(a){var b=d.classPrefix+"close";(c.hasClass(a.target,b)||c.hasClass(a.target.parentNode,b))&&d.close()}),d.on("cancel",function(){d.close()}),d.aria("describedby",d.describedBy||d._id+"-none"),d.aria("label",a.title),d._fullscreen=!1},recalc:function(){var a,b,d,e,f=this,g=f.statusbar;f._fullscreen&&(f.layoutRect(c.getWindowSize()),f.layoutRect().contentH=f.layoutRect().innerH),f._super(),a=f.layoutRect(),f.settings.title&&!f._fullscreen&&(b=a.headerW,b>a.w&&(d=a.x-Math.max(0,b/2),f.layoutRect({w:b,x:d}),e=!0)),g&&(g.layoutRect({w:f.layoutRect().innerW}).recalc(),b=g.layoutRect().minW+a.deltaW,b>a.w&&(d=a.x-Math.max(0,b-a.w),f.layoutRect({w:b,x:d}),e=!0)),e&&f.recalc()},initLayoutRect:function(){var a,b=this,d=b._super(),e=0;if(b.settings.title&&!b._fullscreen){a=b.getEl("head");var f=c.getSize(a);d.headerW=f.width,d.headerH=f.height,e+=d.headerH}b.statusbar&&(e+=b.statusbar.layoutRect().h),d.deltaH+=e,d.minH+=e,d.h+=e;var g=c.getWindowSize();return d.x=b.settings.x||Math.max(0,g.w/2-d.w/2),d.y=b.settings.y||Math.max(0,g.h/2-d.h/2),d},renderHtml:function(){var a=this,b=a._layout,c=a._id,d=a.classPrefix,e=a.settings,f="",g="",h=e.html;return a.preRender(),b.preRender(a),e.title&&(f='<div id="'+c+'-head" class="'+d+'window-head"><div id="'+c+'-title" class="'+d+'title">'+a.encode(e.title)+'</div><div id="'+c+'-dragh" class="'+d+'dragh"></div><button type="button" class="'+d+'close" aria-hidden="true"><i class="mce-ico mce-i-remove"></i></button></div>'),e.url&&(h='<iframe src="'+e.url+'" tabindex="-1"></iframe>'),"undefined"==typeof h&&(h=b.renderHtml(a)),a.statusbar&&(g=a.statusbar.renderHtml()),'<div id="'+c+'" class="'+a.classes+'" hidefocus="1"><div class="'+a.classPrefix+'reset" role="application">'+f+'<div id="'+c+'-body" class="'+a.bodyClasses+'">'+h+"</div>"+g+"</div></div>"},fullscreen:function(a){var b,e,g=this,i=document.documentElement,j=g.classPrefix;if(a!=g._fullscreen)if(d(window).on("resize",function(){var a;if(g._fullscreen)if(b)g._timer||(g._timer=h.setTimeout(function(){var a=c.getWindowSize();g.moveTo(0,0).resizeTo(a.w,a.h),g._timer=0},50));else{a=(new Date).getTime();var d=c.getWindowSize();g.moveTo(0,0).resizeTo(d.w,d.h),(new Date).getTime()-a>50&&(b=!0)}}),e=g.layoutRect(),g._fullscreen=a,a){g._initial={x:e.x,y:e.y,w:e.w,h:e.h},g.borderBox=f.parseBox("0"),g.getEl("head").style.display="none",e.deltaH-=e.headerH+2,d([i,document.body]).addClass(j+"fullscreen"),g.classes.add("fullscreen");var k=c.getWindowSize();g.moveTo(0,0).resizeTo(k.w,k.h)}else g.borderBox=f.parseBox(g.settings.border),g.getEl("head").style.display="",e.deltaH+=e.headerH,d([i,document.body]).removeClass(j+"fullscreen"),g.classes.remove("fullscreen"),g.moveTo(g._initial.x,g._initial.y).resizeTo(g._initial.w,g._initial.h);return g.reflow()},postRender:function(){var a,b=this;setTimeout(function(){b.classes.add("in"),b.fire("open")},0),b._super(),b.statusbar&&b.statusbar.postRender(),b.focus(),this.dragHelper=new e(b._id+"-dragh",{start:function(){a={x:b.layoutRect().x,y:b.layoutRect().y}},drag:function(c){b.moveTo(a.x+c.deltaX,a.y+c.deltaY)}}),b.on("submit",function(a){a.isDefaultPrevented()||b.close()}),m.push(b),i(!0)},submit:function(){return this.fire("submit",{data:this.toJSON()})},remove:function(){var a,b=this;for(b.dragHelper.destroy(),b._super(),b.statusbar&&this.statusbar.remove(),j(b.classPrefix,!1),a=m.length;a--;)m[a]===b&&m.splice(a,1);i(m.length>0)},getContentWindow:function(){var a=this.getEl().getElementsByTagName("iframe")[0];return a?a.contentWindow:null}});return l(),o}),g("24",["23"],function(a){"use strict";var b=a.extend({init:function(a){a={border:1,padding:20,layout:"flex",pack:"center",align:"center",containerCls:"panel",autoScroll:!0,buttons:{type:"button",text:"Ok",action:"ok"},items:{type:"label",multiline:!0,maxWidth:500,maxHeight:200}},this._super(a)},Statics:{OK:1,OK_CANCEL:2,YES_NO:3,YES_NO_CANCEL:4,msgBox:function(c){function d(a,b,c){return{type:"button",text:a,subtype:c?"primary":"",onClick:function(a){a.control.parents()[1].close(),f(b)}}}var e,f=c.callback||function(){};switch(c.buttons){case b.OK_CANCEL:e=[d("Ok",!0,!0),d("Cancel",!1)];break;case b.YES_NO:case b.YES_NO_CANCEL:e=[d("Yes",1,!0),d("No",0)],c.buttons==b.YES_NO_CANCEL&&e.push(d("Cancel",-1));break;default:e=[d("Ok",!0,!0)]}return new a({padding:20,x:c.x,y:c.y,minWidth:300,minHeight:100,layout:"flex",pack:"center",align:"center",buttons:e,title:c.title,role:"alertdialog",items:{type:"label",multiline:!0,maxWidth:500,maxHeight:200,text:c.text},onPostRender:function(){this.aria("describedby",this.items()[0]._id)},onClose:c.onClose,onCancel:function(){f(!1)}}).renderTo(document.body).reflow()},alert:function(a,c){return"string"==typeof a&&(a={text:a}),a.callback=c,b.msgBox(a)},confirm:function(a,c){return"string"==typeof a&&(a={text:a}),a.callback=c,a.buttons=b.OK_CANCEL,b.msgBox(a)}}});return b}),g("10",["23","24"],function(a,b){return function(c){function d(){if(h.length)return h[h.length-1]}function e(a){c.fire("OpenWindow",{win:a})}function f(a){c.fire("CloseWindow",{win:a})}var g=this,h=[];g.windows=h,c.on("remove",function(){for(var a=h.length;a--;)h[a].close()}),g.open=function(b,d){var g;return c.editorManager.setActive(c),b.title=b.title||" ",b.url=b.url||b.file,b.url&&(b.width=parseInt(b.width||320,10),b.height=parseInt(b.height||240,10)),b.body&&(b.items={defaults:b.defaults,type:b.bodyType||"form",items:b.body,data:b.data,callbacks:b.commands}),b.url||b.buttons||(b.buttons=[{text:"Ok",subtype:"primary",onclick:function(){g.find("form")[0].submit()}},{text:"Cancel",onclick:function(){g.close()}}]),g=new a(b),h.push(g),g.on("close",function(){for(var a=h.length;a--;)h[a]===g&&h.splice(a,1);h.length||c.focus(),f(g)}),b.data&&g.on("postRender",function(){this.find("*").each(function(a){var c=a.name();c in b.data&&a.value(b.data[c])})}),g.features=b||{},g.params=d||{},1===h.length&&c.nodeChanged(),g=g.renderTo().reflow(),e(g),g},g.alert=function(a,d,g){var h;h=b.alert(a,function(){d?d.call(g||this):c.focus()}),h.on("close",function(){f(h)}),e(h)},g.confirm=function(a,c,d){var g;g=b.confirm(a,function(a){c.call(d||this,a)}),g.on("close",function(){f(g)}),e(g)},g.close=function(){d()&&d().close()},g.getParams=function(){return d()?d().params:null},g.setParams=function(a){d()&&(d().params=a)},g.getWindows=function(){return h}}}),g("2q",["2g","2n"],function(a,b){return a.extend({Mixins:[b],Defaults:{classes:"widget tooltip tooltip-n"},renderHtml:function(){var a=this,b=a.classPrefix;return'<div id="'+a._id+'" class="'+a.classes+'" role="presentation"><div class="'+b+'tooltip-arrow"></div><div class="'+b+'tooltip-inner">'+a.encode(a.state.get("text"))+"</div></div>"},bindStates:function(){var a=this;return a.state.on("change:text",function(b){a.getEl().lastChild.innerHTML=a.encode(b.value)}),a._super()},repaint:function(){var a,b,c=this;a=c.getEl().style,b=c._layoutRect,a.left=b.x+"px",a.top=b.y+"px",a.zIndex=131070}})}),g("2r",["2g","2q"],function(a,b){"use strict";var c,d=a.extend({init:function(a){var b=this;b._super(a),a=b.settings,b.canFocus=!0,a.tooltip&&d.tooltips!==!1&&(b.on("mouseenter",function(c){var d=b.tooltip().moveTo(-65535);if(c.control==b){var e=d.text(a.tooltip).show().testMoveRel(b.getEl(),["bc-tc","bc-tl","bc-tr"]);d.classes.toggle("tooltip-n","bc-tc"==e),d.classes.toggle("tooltip-nw","bc-tl"==e),d.classes.toggle("tooltip-ne","bc-tr"==e),d.moveRel(b.getEl(),e)}else d.hide()}),b.on("mouseleave mousedown click",function(){b.tooltip().hide()})),b.aria("label",a.ariaLabel||a.tooltip)},tooltip:function(){return c||(c=new b({type:"tooltip"}),c.renderTo()),c},postRender:function(){var a=this,b=a.settings;a._super(),a.parent()||!b.width&&!b.height||(a.initLayoutRect(),a.repaint()),b.autofocus&&a.focus()},bindStates:function(){function a(a){c.aria("disabled",a),c.classes.toggle("disabled",a)}function b(a){c.aria("pressed",a),c.classes.toggle("active",a)}var c=this;return c.state.on("change:disabled",function(b){a(b.value)}),c.state.on("change:active",function(a){b(a.value)}),c.state.get("disabled")&&a(!0),c.state.get("active")&&b(!0),c._super()},remove:function(){this._super(),c&&(c.remove(),c=null)}});return d}),g("2s",["2r"],function(a){"use strict";return a.extend({Defaults:{value:0},init:function(a){var b=this;b._super(a),b.classes.add("progress"),b.settings.filter||(b.settings.filter=function(a){return Math.round(a)})},renderHtml:function(){var a=this,b=a._id,c=this.classPrefix;return'<div id="'+b+'" class="'+a.classes+'"><div class="'+c+'bar-container"><div class="'+c+'bar"></div></div><div class="'+c+'text">0%</div></div>'},postRender:function(){var a=this;return a._super(),a.value(a.settings.value),a},bindStates:function(){function a(a){a=b.settings.filter(a),b.getEl().lastChild.innerHTML=a+"%",b.getEl().firstChild.firstChild.style.width=a+"%"}var b=this;return b.state.on("change:value",function(b){a(b.value)}),a(b.state.get("value")),b._super()}})}),g("25",["2g","2n","2s","5"],function(a,b,c,d){return a.extend({Mixins:[b],Defaults:{classes:"widget notification"},init:function(a){var b=this;b._super(a),a.text&&b.text(a.text),a.icon&&(b.icon=a.icon),a.color&&(b.color=a.color),a.type&&b.classes.add("notification-"+a.type),a.timeout&&(a.timeout<0||a.timeout>0)&&!a.closeButton?b.closeButton=!1:(b.classes.add("has-close"),b.closeButton=!0),a.progressBar&&(b.progressBar=new c),b.on("click",function(a){a.target.className.indexOf(b.classPrefix+"close")!=-1&&b.close()})},renderHtml:function(){var a=this,b=a.classPrefix,c="",d="",e="",f="";return a.icon&&(c='<i class="'+b+"ico "+b+"i-"+a.icon+'"></i>'),a.color&&(f=' style="background-color: '+a.color+'"'),a.closeButton&&(d='<button type="button" class="'+b+'close" aria-hidden="true">\xd7</button>'),a.progressBar&&(e=a.progressBar.renderHtml()),'<div id="'+a._id+'" class="'+a.classes+'"'+f+' role="presentation">'+c+'<div class="'+b+'notification-inner">'+a.state.get("text")+"</div>"+e+d+"</div>"},postRender:function(){var a=this;return d.setTimeout(function(){a.$el.addClass(a.classPrefix+"in")}),a._super()},bindStates:function(){var a=this;return a.state.on("change:text",function(b){a.getEl().childNodes[1].innerHTML=b.value}),a.progressBar&&a.progressBar.bindStates(),a._super()},close:function(){var a=this;return a.fire("close").isDefaultPrevented()||a.remove(),a},repaint:function(){var a,b,c=this;a=c.getEl().style,b=c._layoutRect,a.left=b.x+"px",a.top=b.y+"px",a.zIndex=65534}})}),g("11",["25","5","9"],function(a,b,c){return function(d){function e(){if(m.length)return m[m.length-1]}function f(){b.requestAnimationFrame(function(){g(),h()})}function g(){for(var a=0;a<m.length;a++)m[a].moveTo(0,0)}function h(){if(m.length>0){var a=m.slice(0,1)[0],b=d.inline?d.getElement():d.getContentAreaContainer();if(a.moveRel(b,"tc-tc"),m.length>1)for(var c=1;c<m.length;c++)m[c].moveRel(m[c-1].getEl(),"bc-tc")}}function i(a,b){if(!k(b))return null;var d=c.grep(a,function(a){return j(b,a)});return 0===d.length?null:d[0]}function j(a,b){return a.type===b.settings.type&&a.text===b.settings.text}function k(a){return!a.progressBar&&!a.timeout}var l=this,m=[];l.notifications=m,d.on("remove",function(){for(var a=m.length;a--;)m[a].close()}),d.on("ResizeEditor",h),d.on("ResizeWindow",f),l.open=function(b){if(!d.removed){var c;d.editorManager.setActive(d);var e=i(m,b);return null===e?(c=new a(b),m.push(c),b.timeout>0&&(c.timer=setTimeout(function(){c.close()},b.timeout)),c.on("close",function(){var a=m.length;for(c.timer&&d.getWin().clearTimeout(c.timer);a--;)m[a]===c&&m.splice(a,1);h()}),c.renderTo(),h()):c=e,c}},l.close=function(){e()&&e().close()},l.getNotifications=function(){return m},d.on("SkinLoaded",function(){var a=d.settings.service_message;a&&d.notificationManager.open({text:a,type:"warning",timeout:0,icon:""})})}}),g("12",["z","e","9"],function(a,b,c){function d(a,b){return"selectionchange"==b?a.getDoc():!a.inline&&/^mouse|touch|click|contextmenu|drop|dragover|dragend/.test(b)?a.getDoc().documentElement:a.settings.event_root?(a.eventRoot||(a.eventRoot=g.select(a.settings.event_root)[0]),a.eventRoot):a.getBody()}function e(a,b){function c(a){return!a.hidden&&!a.readonly}var e,h;if(a.delegates||(a.delegates={}),!a.delegates[b]&&!a.removed)if(e=d(a,b),a.settings.event_root){if(f||(f={},a.editorManager.on("removeEditor",function(){var b;if(!a.editorManager.activeEditor&&f){for(b in f)a.dom.unbind(d(a,b));f=null}})),f[b])return;h=function(d){for(var e=d.target,f=a.editorManager.editors,h=f.length;h--;){var i=f[h].getBody();(i===e||g.isChildOf(e,i))&&c(f[h])&&f[h].fire(b,d)}},f[b]=h,g.bind(e,b,h)}else h=function(d){c(a)&&a.fire(b,d)},g.bind(e,b,h),a.delegates[b]=h}var f,g=b.DOM,h={bindPendingEventDelegates:function(){var a=this;c.each(a._pendingNativeEvents,function(b){e(a,b)})},toggleNativeEvent:function(a,b){var c=this;"focus"!=a&&"blur"!=a&&(b?c.initialized?e(c,a):c._pendingNativeEvents?c._pendingNativeEvents.push(a):c._pendingNativeEvents=[a]:c.initialized&&(c.dom.unbind(d(c,a),a,c.delegates[a]),delete c.delegates[a]))},unbindAllNativeEvents:function(){var a,b=this;if(b.delegates){for(a in b.delegates)b.dom.unbind(d(b,a),a,b.delegates[a]);delete b.delegates}b.inline||(b.getBody().onload=null,b.dom.unbind(b.getWin()),b.dom.unbind(b.getDoc())),b.dom.unbind(b.getBody()),b.dom.unbind(b.getContainer())}};return h=c.extend({},a,h)}),g("13",["9","6"],function(a,b){var c=a.each,d=a.explode,e={f9:120,f10:121,f11:122},f=a.makeMap("alt,ctrl,shift,meta,access");return function(g){function h(a){var g,h,i={};c(d(a,"+"),function(a){a in f?i[a]=!0:/^[0-9]{2,}$/.test(a)?i.keyCode=parseInt(a,10):(i.charCode=a.charCodeAt(0),i.keyCode=e[a]||a.toUpperCase().charCodeAt(0))}),g=[i.keyCode];for(h in f)i[h]?g.push(h):i[h]=!1;return i.id=g.join(","),i.access&&(i.alt=!0,b.mac?i.ctrl=!0:i.shift=!0),i.meta&&(b.mac?i.meta=!0:(i.ctrl=!0,i.meta=!1)),i}function i(b,c,e,f){var i;return i=a.map(d(b,">"),h),i[i.length-1]=a.extend(i[i.length-1],{func:e,scope:f||g}),a.extend(i[0],{desc:g.translate(c),subpatterns:i.slice(1)})}function j(a){return a.altKey||a.ctrlKey||a.metaKey}function k(a){return"keydown"===a.type&&a.keyCode>=112&&a.keyCode<=123}function l(a,b){return!!b&&(b.ctrl==a.ctrlKey&&b.meta==a.metaKey&&(b.alt==a.altKey&&b.shift==a.shiftKey&&(!!(a.keyCode==b.keyCode||a.charCode&&a.charCode==b.charCode)&&(a.preventDefault(),!0))))}function m(a){return a.func?a.func.call(a.scope):null}var n=this,o={},p=[];g.on("keyup keypress keydown",function(a){!j(a)&&!k(a)||a.isDefaultPrevented()||(c(o,function(b){if(l(a,b))return p=b.subpatterns.slice(0),"keydown"==a.type&&m(b),!0}),l(a,p[0])&&(1===p.length&&"keydown"==a.type&&m(p[0]),p.shift()))}),n.add=function(b,e,f,h){var j;return j=f,"string"==typeof f?f=function(){g.execCommand(j,!1,null)}:a.isArray(j)&&(f=function(){g.execCommand(j[0],j[1],j[2])}),c(d(a.trim(b.toLowerCase())),function(a){var b=i(a,e,f,h);o[b.id]=b}),!0},n.remove=function(a){var b=i(a);return!!o[b.id]&&(delete o[b.id],!0)}}}),g("26",["9"],function(a){var b=function(b,c,d,e,f){return f=a.extend({id:c,theme:"modern",delta_width:0,delta_height:0,popup_css:"",plugins:"",document_base_url:d,add_form_submit_trigger:!0,submit_patch:!0,add_unload_trigger:!0,convert_urls:!0,relative_urls:!0,remove_script_host:!0,object_resizing:!0,doctype:"<!DOCTYPE html>",visual:!0,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",font_size_legacy_values:"xx-small,small,medium,large,x-large,xx-large,300%",forced_root_block:"p",hidden_input:!0,padd_empty_editor:!0,render_ui:!0,indentation:"30px",inline_styles:!0,convert_fonts_to_spans:!0,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist",entity_encoding:"named",url_converter:b.convertURL,url_converter_scope:b,ie7_compat:!0},e,f,{validate:!0,content_editable:f.inline}),e&&e.external_plugins&&f.external_plugins&&(f.external_plugins=a.extend({},e.external_plugins,f.external_plugins)),f};return{getEditorSettings:b}}),h("4t",window),g("2b",["g"],function(a){var b=a.PluginManager,c=function(a,c){for(var d in b.urls){var e=b.urls[d]+"/plugin"+c+".js";if(e===a)return d}return null},d=function(a,b){var d=c(b,a.suffix);return d?"Failed to load plugin: "+d+" from url "+b:"Failed to load plugin url: "+b},e=function(a,b){a.notificationManager.open({type:"error",text:b})},f=function(a,b){a._skinLoaded?e(a,b):a.on("SkinLoaded",function(){e(a,b)})},g=function(a,b){f(a,"Failed to upload image: "+b)},h=function(a,b){f(a,d(a,b))},i=function(a,b){f(a,"Failed to load content css: "+b[0])},j=function(a){var b=window.console;b&&!window.test&&(b.error?b.error.apply(b,arguments):b.log.apply(b,arguments))};return{pluginLoadError:h,uploadError:g,displayError:f,contentCssError:i,initError:j}}),g("6c",["1m","1k"],function(a,b){var c=function(a){return a.dom.select("*[data-mce-caret]")[0]},d=function(a){a.selection.setRng(a.selection.getRng())},e=function(a,c){c.hasAttribute("data-mce-caret")&&(b.showCaretContainerBlock(c),d(a),a.selection.scrollIntoView(c))},f=function(a,d){var f=c(a);if(f)return"compositionstart"===d.type?(d.preventDefault(),d.stopPropagation(),void e(f)):void(b.hasContent(f)&&e(a,f))},g=function(b){b.on("keyup compositionstart",a.curry(f,b))};return{setup:g}}),g("6r",["4","9","1w"],function(a,b,c){return function(c,d){function e(a,b){return a?a.replace(/\/$/,"")+"/"+b.replace(/^\//,""):b}function f(a,b,c,f){var g,h;g=new XMLHttpRequest,g.open("POST",d.url),g.withCredentials=d.credentials,g.upload.onprogress=function(a){f(a.loaded/a.total*100)},g.onerror=function(){c("Image upload failed due to a XHR Transport error. Code: "+g.status)},g.onload=function(){var a;return g.status<200||g.status>=300?void c("HTTP Error: "+g.status):(a=JSON.parse(g.responseText),a&&"string"==typeof a.location?void b(e(d.basePath,a.location)):void c("Invalid JSON: "+g.responseText))},h=new FormData,h.append("file",a.blob(),a.filename()),g.send(h)}function g(){return new a(function(a){a([])})}function h(a,b){return{url:b,blobInfo:a,status:!0}}function i(a,b){return{url:"",blobInfo:a,status:!1,error:b}}function j(a,c){b.each(p[a],function(a){a(c)}),delete p[a]}function k(b,d,e){return c.markPending(b.blobUri()),new a(function(a){var f,g,k=function(){};try{var l=function(){f&&(f.close(),g=k)},m=function(d){l(),c.markUploaded(b.blobUri(),d),j(b.blobUri(),h(b,d)),a(h(b,d))},n=function(d){l(),c.removeFailed(b.blobUri()),j(b.blobUri(),i(b,d)),a(i(b,d))};g=function(a){a<0||a>100||(f||(f=e()),f.progressBar.value(a))},d(b,m,n,g)}catch(c){a(i(b,c.message))}})}function l(a){return a===f}function m(b){var c=b.blobUri();return new a(function(a){p[c]=p[c]||[],p[c].push(a)})}function n(e,f){return e=b.grep(e,function(a){return!c.isUploaded(a.blobUri())}),a.all(b.map(e,function(a){return c.isPending(a.blobUri())?m(a):k(a,d.handler,f)}))}function o(a,b){return!d.url&&l(d.handler)?g():n(a,b)}var p={};return d=b.extend({credentials:!1,handler:f},d),{upload:o}}}),g("7a",["4"],function(a){function b(b){return new a(function(a,c){var d=function(){c("Cannot convert "+b+" to Blob. Resource might not exist or is inaccessible.")};try{var e=new XMLHttpRequest;e.open("GET",b,!0),e.responseType="blob",e.onload=function(){200==this.status?a(this.response):d()},e.onerror=d,e.send()}catch(a){d()}})}function c(a){var b,c;return a=decodeURIComponent(a).split(","),c=/data:([^;]+)/.exec(a[0]),c&&(b=c[1]),{type:b,data:a[1]}}function d(b){return new a(function(a){var d,e,f;b=c(b);try{d=atob(b.data)}catch(b){return void a(new Blob([]))}for(e=new Uint8Array(d.length),f=0;f<e.length;f++)e[f]=d.charCodeAt(f);a(new Blob([e],{type:b.type}))})}function e(a){return 0===a.indexOf("blob:")?b(a):0===a.indexOf("data:")?d(a):null}function f(b){return new a(function(a){var c=new FileReader;c.onloadend=function(){a(c.result)},c.readAsDataURL(b)})}return{uriToBlob:e,blobToDataUri:f,parseDataUri:c}}),g("6s",["4","1g","1w","7a","6"],function(a,b,c,d,e){var f=0,g=function(a){return(a||"blobid")+f++},h=function(a,b,c,e){var f,h;return 0===b.src.indexOf("blob:")?(h=a.getByUri(b.src),void(h?c({image:b,blobInfo:h}):d.uriToBlob(b.src).then(function(e){d.blobToDataUri(e).then(function(i){f=d.parseDataUri(i).data,h=a.create(g(),e,f),a.add(h),c({image:b,blobInfo:h})})},function(a){e(a)}))):(f=d.parseDataUri(b.src).data,h=a.findFirst(function(a){return a.base64()===f}),void(h?c({image:b,blobInfo:h}):d.uriToBlob(b.src).then(function(d){h=a.create(g(),d,f),a.add(h),c({image:b,blobInfo:h})},function(a){e(a)})))},i=function(a){return a?a.getElementsByTagName("img"):[]};return function(d,f){function g(g,k){var l,m;return k||(k=c.constant(!0)),l=b.filter(i(g),function(a){var b=a.src;return!!e.fileApi&&(!a.hasAttribute("data-mce-bogus")&&(!a.hasAttribute("data-mce-placeholder")&&(!(!b||b==e.transparentSrc)&&(0===b.indexOf("blob:")?!d.isUploaded(b):0===b.indexOf("data:")&&k(a)))))}),m=b.map(l,function(b){var c;return j[b.src]?new a(function(a){j[b.src].then(function(c){return"string"==typeof c?c:void a({image:b,blobInfo:c.blobInfo})})}):(c=new a(function(a,c){h(f,b,a,c)}).then(function(a){return delete j[a.image.src],a})["catch"](function(a){return delete j[b.src],a}),j[b.src]=c,c)}),a.all(m)}var j={};return{findAll:g}}}),g("2a",[],function(){var a=0,b=function(){var a=function(){return Math.round(4294967295*Math.random()).toString(36)},b=(new Date).getTime();return"s"+b.toString(36)+a()+a()+a()},c=function(c){return c+a++ +b()};return{uuid:c}}),h("7b",URL),g("6t",["1g","1w","2a","7b"],function(a,b,c,d){return function(){function e(a){var b={"image/jpeg":"jpg","image/jpg":"jpg","image/gif":"gif","image/png":"png"};return b[a.toLowerCase()]||"dat"}function f(a,b,c,d){return g("object"==typeof a?a:{id:a,name:d,blob:b,base64:c})}function g(a){var b,f;if(!a.blob||!a.base64)throw"blob and base64 representations of the image are required for BlobInfo to be created";return b=a.id||c.uuid("blobid"),f=a.name||b,{id:o(b),name:o(f),filename:o(f+"."+e(a.blob.type)),blob:o(a.blob),base64:o(a.base64),blobUri:o(a.blobUri||d.createObjectURL(a.blob)),uri:o(a.uri)}}function h(a){i(a.id())||n.push(a)}function i(a){return j(function(b){return b.id()===a})}function j(b){return a.filter(n,b)[0]}function k(a){return j(function(b){return b.blobUri()==a})}function l(b){n=a.filter(n,function(a){return a.blobUri()!==b||(d.revokeObjectURL(a.blobUri()),!1)})}function m(){a.each(n,function(a){d.revokeObjectURL(a.blobUri())}),n=[]}var n=[],o=b.constant;return{create:f,add:h,get:i,getByUri:k,findFirst:j,removeByUri:l,destroy:m}}}),g("6u",[],function(){return function(){function a(a,b){return{status:a,resultUri:b}}function b(a){return a in l}function c(a){var b=l[a];return b?b.resultUri:null}function d(a){return!!b(a)&&l[a].status===j}function e(a){return!!b(a)&&l[a].status===k}function f(b){l[b]=a(j,null)}function g(b,c){l[b]=a(k,c)}function h(a){delete l[a]}function i(){l={}}var j=1,k=2,l={};return{hasBlobUri:b,getResultUri:c,isPending:d,isUploaded:e,markPending:f,markUploaded:g,removeFailed:h,destroy:i}}}),g("6d",["1g","6r","6s","6t","6u","2b"],function(a,b,c,d,e,f){return function(g){function h(a){return function(b){return g.selection?a(b):[]}}function i(){return"?"+(new Date).getTime()}function j(a,b,c){var d=0;do d=a.indexOf(b,d),d!==-1&&(a=a.substring(0,d)+c+a.substr(d+b.length),d+=c.length-b.length+1);while(d!==-1);return a}function k(a,b,c){return a=j(a,'src="'+b+'"','src="'+c+'"'),a=j(a,'data-mce-src="'+b+'"','data-mce-src="'+c+'"')}function l(b,c){a.each(g.undoManager.data,function(d){"fragmented"===d.type?d.fragments=a.map(d.fragments,function(a){return k(a,b,c)}):d.content=k(d.content,b,c)})}function m(){return g.notificationManager.open({text:g.translate("Image uploading..."),type:"info",timeout:-1,progressBar:!0})}function n(a,b){w.removeByUri(a.src),l(a.src,b),g.$(a).attr({src:x.images_reuse_filename?b+i():b,"data-mce-src":g.convertURL(b,"src")})}function o(c){return u||(u=new b(y,{url:x.images_upload_url,basePath:x.images_upload_base_path,credentials:x.images_upload_credentials,handler:x.images_upload_handler})),r().then(h(function(b){var d;return d=a.map(b,function(a){return a.blobInfo}),u.upload(d,m).then(h(function(d){var e=a.map(d,function(a,c){var d=b[c].image;return a.status&&g.settings.images_replace_blob_uris!==!1?n(d,a.url):a.error&&f.uploadError(g,a.error),{element:d, +status:a.status}});return c&&c(e),e}))}))}function p(a){if(x.automatic_uploads!==!1)return o(a)}function q(a){return!x.images_dataimg_filter||x.images_dataimg_filter(a)}function r(){return v||(v=new c(y,w)),v.findAll(g.getBody(),q).then(h(function(b){return b=a.filter(b,function(a){return"string"!=typeof a||(f.displayError(g,a),!1)}),a.each(b,function(a){l(a.image.src,a.blobInfo.blobUri()),a.image.src=a.blobInfo.blobUri(),a.image.removeAttribute("data-mce-src")}),b}))}function s(){w.destroy(),y.destroy(),v=u=null}function t(b){return b.replace(/src="(blob:[^"]+)"/g,function(b,c){var d=y.getResultUri(c);if(d)return'src="'+d+'"';var e=w.getByUri(c);return e||(e=a.reduce(g.editorManager.editors,function(a,b){return a||b.editorUpload&&b.editorUpload.blobCache.getByUri(c)},null)),e?'src="data:'+e.blob().type+";base64,"+e.base64()+'"':b})}var u,v,w=new d,x=g.settings,y=new e;return g.on("setContent",function(){g.settings.automatic_uploads!==!1?p():r()}),g.on("RawSaveContent",function(a){a.content=t(a.content)}),g.on("getContent",function(a){a.source_view||"raw"==a.format||(a.content=t(a.content))}),g.on("PostRender",function(){g.parser.addNodeFilter("img",function(b){a.each(b,function(a){var b=a.attr("src");if(!w.getByUri(b)){var c=y.getResultUri(b);c&&a.attr("src",c)}})})}),{blobCache:w,uploadImages:o,uploadImagesAuto:p,scanForImages:r,destroy:s}}}),g("6e",["1m"],function(a){var b=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n=a.settings,o=a.dom,p=a.selection,q=a.schema,r=q.getBlockElements(),s=p.getStart(),t=a.getBody(),u=-16777215;if(m=n.forced_root_block,s&&1===s.nodeType&&m){for(;s&&s!==t;){if(r[s.nodeName])return;s=s.parentNode}if(b=p.getRng(),b.setStart){c=b.startContainer,d=b.startOffset,e=b.endContainer,f=b.endOffset;try{j=a.getDoc().activeElement===t}catch(a){}}else b.item&&(s=b.item(0),b=a.getDoc().body.createTextRange(),b.moveToElementText(s)),j=b.parentElement().ownerDocument===a.getDoc(),k=b.duplicate(),k.collapse(!0),d=k.move("character",u)*-1,k.collapsed||(k=b.duplicate(),k.collapse(!1),f=k.move("character",u)*-1-d);for(s=t.firstChild,l=t.nodeName.toLowerCase();s;)if((3===s.nodeType||1==s.nodeType&&!r[s.nodeName])&&q.isValidChild(l,m.toLowerCase())){if(3===s.nodeType&&0===s.nodeValue.length){h=s,s=s.nextSibling,o.remove(h);continue}g||(g=o.create(m,a.settings.forced_root_block_attrs),s.parentNode.insertBefore(g,s),i=!0),h=s,s=s.nextSibling,g.appendChild(h)}else g=null,s=s.nextSibling;if(i&&j){if(b.setStart)b.setStart(c,d),b.setEnd(e,f),p.setRng(b);else try{b=a.getDoc().body.createTextRange(),b.moveToElementText(t),b.collapse(!0),b.moveStart("character",d),f>0&&b.moveEnd("character",f),b.select()}catch(a){}a.nodeChanged()}}},c=function(c){c.settings.forced_root_block&&c.on("NodeChange",a.curry(b,c))};return{setup:c}}),g("7f",["1g","1j","43"],function(a,b,c){function d(e){function f(b){return a.map(b,function(a){return a=c.clone(a),a.node=e,a})}if(a.isArray(e))return a.reduce(e,function(a,b){return a.concat(d(b))},[]);if(b.isElement(e))return f(e.getClientRects());if(b.isText(e)){var g=e.ownerDocument.createRange();return g.setStart(e,0),g.setEnd(e,e.data.length),f(g.getClientRects())}}return{getClientRects:d}}),g("70",["1w","1g","1j","7f","43","5i","44"],function(a,b,c,d,e,f,g){function h(a,b){return Math.abs(a.left-b)}function i(a,b){return Math.abs(a.right-b)}function j(a,c){function d(a,b){return a>=b.left&&a<=b.right}return b.reduce(a,function(a,b){var e,f;return e=Math.min(h(a,c),i(a,c)),f=Math.min(h(b,c),i(b,c)),d(c,b)?b:d(c,a)?a:f==e&&p(b.node)?b:f<e?b:a})}function k(a,b,c,d){for(;d=q(d,a,g.isEditableCaretCandidate,b);)if(c(d))return}function l(a,c){function f(a,e){var f;return f=b.filter(d.getClientRects(e),function(b){return!a(b,c)}),g=g.concat(f),0===f.length}var g=[];return g.push(c),k(-1,a,r(f,e.isAbove),c.node),k(1,a,r(f,e.isBelow),c.node),g}function m(a){return b.filter(b.toArray(a.getElementsByTagName("*")),p)}function n(a,b){return{node:a.node,before:h(a,b)<i(a,b)}}function o(a,c,e){var f,g;return f=d.getClientRects(m(a)),f=b.filter(f,function(a){return e>=a.top&&e<=a.bottom}),g=j(f,c),g&&(g=j(l(a,g),c),g&&p(g.node))?n(g,c):null}var p=c.isContentEditableFalse,q=f.findNode,r=a.curry;return{findClosestClientRect:j,findLineNodeRects:l,closestCaret:o}}),g("7i",["1w","1g","7f","44","5i","4m","1p","43"],function(a,b,c,d,e,f,g,h){function i(a,b,c,f){for(;f=e.findNode(f,a,d.isEditableCaretCandidate,b);)if(c(f))return}function j(a,d,e,f,g,h){function j(f){var h,i,j;for(j=c.getClientRects(f),a==-1&&(j=j.reverse()),h=0;h<j.length;h++)if(i=j[h],!e(i,l)){if(n.length>0&&d(i,b.last(n))&&m++,i.line=m,g(i))return!0;n.push(i)}}var k,l,m=0,n=[];return(l=b.last(h.getClientRects()))?(k=h.getNode(),j(k),i(a,f,j,k),n):n}function k(a,b){return b.line>a}function l(a,b){return b.line===a}function m(a,c,d,e){function i(c){return 1==a?b.last(c.getClientRects()):b.last(c.getClientRects())}var j,k,l,m,n,o,p=new f(c),q=[],r=0;1==a?(j=p.next,k=h.isBelow,l=h.isAbove,m=g.after(e)):(j=p.prev,k=h.isAbove,l=h.isBelow,m=g.before(e)),o=i(m);do if(m.isVisible()&&(n=i(m),!l(n,o))){if(q.length>0&&k(n,b.last(q))&&r++,n=h.clone(n),n.position=m,n.line=r,d(n))return q;q.push(n)}while(m=j(m));return q}var n=a.curry,o=n(j,-1,h.isAbove,h.isBelow),p=n(j,1,h.isBelow,h.isAbove);return{upUntil:o,downUntil:p,positionsUntil:m,isAboveLine:n(k),isLine:n(l)}}),g("73",["1p","5i","1j","1w"],function(a,b,c,d){var e=c.isContentEditableTrue,f=c.isContentEditableFalse,g=function(a,b,c,d){return b._selectionOverrides.showCaret(a,c,d)},h=function(a){var b=a.ownerDocument.createRange();return b.selectNode(a),b},i=function(a,b){var c;return c=a.fire("BeforeObjectSelected",{target:b}),c.isDefaultPrevented()?null:h(b)},j=function(c,h){var i,j;return h=b.normalizeRange(1,c.getBody(),h),i=a.fromRangeStart(h),f(i.getNode())?g(1,c,i.getNode(),!i.isAtEnd()):f(i.getNode(!0))?g(1,c,i.getNode(!0),!1):(j=c.dom.getParent(i.getNode(),d.or(f,e)),f(j)?g(1,c,j,!1):null)},k=function(a,b){var c;return b&&b.collapsed?(c=j(a,b),c?c:b):b};return{showCaret:g,selectNode:i,renderCaretAtRange:j,renderRangeCaret:k}}),g("7c",["1k","1p","5i","4m","70","7i","1j","h","6","73","1g","1w"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m=g.isContentEditableFalse,n=h.getSelectedNode,o=c.isAfterContentEditableFalse,p=c.isBeforeContentEditableFalse,q=function(a,b){for(;b=a(b);)if(b.isVisible())return b;return b},r=function(a,b){var d=c.isInSameBlock(a,b);return!(d||!g.isBr(a.getNode()))||d},s=function(b){return a.isCaretContainerBlock(b.startContainer)},t=function(a,d,e){return e=c.normalizeRange(a,d,e),a===-1?b.fromRangeStart(e):b.fromRangeEnd(e)},u=function(a,b,c,d,e){var f,g,h,i;return!e.collapsed&&(f=n(e),m(f))?j.showCaret(a,b,f,a===-1):(i=s(e),g=t(a,b.getBody(),e),d(g)?j.selectNode(b,g.getNode(a===-1)):(g=c(g))?d(g)?j.showCaret(a,b,g.getNode(a===-1),1===a):(h=c(g),d(h)&&r(g,h)?j.showCaret(a,b,h.getNode(a===-1),1===a):i?j.renderRangeCaret(b,g.toRange()):null):i?e:null)},v=function(a,b,c,d){var g,h,i,l,q,r,s,u,v;if(v=n(d),g=t(a,b.getBody(),d),h=c(b.getBody(),f.isAboveLine(1),g),i=k.filter(h,f.isLine(1)),q=k.last(g.getClientRects()),p(g)&&(v=g.getNode()),o(g)&&(v=g.getNode(!0)),!q)return null;if(r=q.left,l=e.findClosestClientRect(i,r),l&&m(l.node))return s=Math.abs(r-l.left),u=Math.abs(r-l.right),j.showCaret(a,b,l.node,s<u);if(v){var w=f.positionsUntil(a,b.getBody(),f.isAboveLine(1),v);if(l=e.findClosestClientRect(k.filter(w,f.isLine(1)),r))return j.renderRangeCaret(b,l.position.toRange());if(l=k.last(k.filter(w,f.isLine(0))))return j.renderRangeCaret(b,l.position.toRange())}},w=function(a){var b=a.dom.create(a.settings.forced_root_block);return(!i.ie||i.ie>=11)&&(b.innerHTML='<br data-mce-bogus="1">'),b},x=function(a,c,e){var f,g,h,i=new d(a.getBody()),j=l.curry(q,i.next),k=l.curry(q,i.prev);if(e.collapsed&&a.settings.forced_root_block){if(f=a.dom.getParent(e.startContainer,"PRE"),!f)return;g=1===c?j(b.fromRangeStart(e)):k(b.fromRangeStart(e)),g||(h=w(a),1===c?a.$(f).after(h):a.$(f).before(h),a.selection.select(h,!0),a.selection.collapse())}},y=function(a,b){var c,e=new d(a.getBody()),f=l.curry(q,e.next),g=l.curry(q,e.prev),h=b?1:-1,i=b?f:g,j=b?p:o,k=a.selection.getRng();return(c=u(h,a,i,j,k))?c:(c=x(a,h,k),c?c:null)},z=function(a,b){var c,d=b?1:-1,e=b?f.downUntil:f.upUntil,g=a.selection.getRng();return(c=v(d,a,e,g))?c:(c=x(a,d,g),c?c:null)},A=function(a,b){return function(){var c=y(a,b);return!!c&&(a.selection.setRng(c),!0)}},B=function(a,b){return function(){var c=z(a,b);return!!c&&(a.selection.setRng(c),!0)}};return{moveH:A,moveV:B}}),g("7j",["60","3y","3z"],function(a,b,c){var d=function(a,b){return b},e=function(b,c){var d=a.isObject(b)&&a.isObject(c);return d?g(b,c):c},f=function(a){return function(){for(var d=new b(arguments.length),e=0;e<d.length;e++)d[e]=arguments[e];if(0===d.length)throw new c("Can't merge zero objects");for(var f={},g=0;g<d.length;g++){var h=d[g];for(var i in h)h.hasOwnProperty(i)&&(f[i]=a(f[i],h[i]))}return f}},g=f(e),h=f(d);return{deepMerge:g,merge:h}}),g("7d",["3x","1m","7j"],function(a,b,c){var d=function(d){return a.map(d,function(a){return c.merge({shiftKey:!1,altKey:!1,ctrlKey:!1,metaKey:!1,keyCode:0,action:b.noop},a)})},e=function(a,b){return b.keyCode===a.keyCode&&b.shiftKey===a.shiftKey&&b.altKey===a.altKey&&b.ctrlKey===a.ctrlKey&&b.metaKey===a.metaKey},f=function(b,c){return a.bind(d(b),function(a){return e(a,c)?[a]:[]})},g=function(a){var b=Array.prototype.slice.call(arguments,1);return function(){return a.apply(null,b)}};return{match:f,action:g}}),g("6v",["3x","6b","5o","7c","7d","p"],function(a,b,c,d,e,f){var g=function(b,g,h){var i=e.match([{keyCode:f.RIGHT,action:d.moveH(b,!0)},{keyCode:f.LEFT,action:d.moveH(b,!1)},{keyCode:f.UP,action:d.moveV(b,!1)},{keyCode:f.DOWN,action:d.moveV(b,!0)},{keyCode:f.RIGHT,action:c.move(b,g,!0)},{keyCode:f.LEFT,action:c.move(b,g,!1)}],h);a.find(i,function(a){return a.action()}).each(function(a){h.preventDefault()})},h=function(a,b){a.on("keydown",function(c){c.isDefaultPrevented()===!1&&g(a,b,c)})};return{setup:h}}),g("6w",["3x","4i","4j","4k","4l","7d","p"],function(a,b,c,d,e,f,g){var h=function(h,i,j){var k=f.match([{keyCode:g.BACKSPACE,action:f.action(d.backspaceDelete,h,!1)},{keyCode:g.DELETE,action:f.action(d.backspaceDelete,h,!0)},{keyCode:g.BACKSPACE,action:f.action(e.backspaceDelete,h,i,!1)},{keyCode:g.DELETE,action:f.action(e.backspaceDelete,h,i,!0)},{keyCode:g.BACKSPACE,action:f.action(c.backspaceDelete,h,!1)},{keyCode:g.DELETE,action:f.action(c.backspaceDelete,h,!0)},{keyCode:g.BACKSPACE,action:f.action(b.backspaceDelete,h,!1)},{keyCode:g.DELETE,action:f.action(b.backspaceDelete,h,!0)}],j);a.find(k,function(a){return a.action()}).each(function(a){j.preventDefault()})},i=function(b,c){var e=f.match([{keyCode:g.BACKSPACE,action:f.action(d.paddEmptyElement,b)},{keyCode:g.DELETE,action:f.action(d.paddEmptyElement,b)}],c);a.find(e,function(a){return a.action()})},j=function(a,b){a.on("keydown",function(c){c.isDefaultPrevented()===!1&&h(a,b,c)}),a.on("keyup",function(b){b.isDefaultPrevented()===!1&&i(a,b)})};return{setup:j}}),g("6x",["1k","1j","h","c","6","1l","9"],function(a,b,c,d,e,f,g){var h=e.ie&&e.ie<11,i=function(a){return a&&"A"===a.nodeName&&0===g.trim(f.trim(a.innerText||a.textContent)).length},j=function(a){return a&&/^(TD|TH|CAPTION)$/.test(a.nodeName)},k=function(a){a.innerHTML=h?"":'<br data-mce-bogus="1">'},l=function(a,b){return a.nodeName===b||a.previousSibling&&a.previousSibling.nodeName===b},m=function(a,b){return b&&a.isBlock(b)&&!/^(TD|TH|CAPTION|FORM)$/.test(b.nodeName)&&!/^(fixed|absolute)/i.test(b.style.position)&&"true"!==a.getContentEditable(b)},n=function(a,b,c){var d;a.isBlock(c)&&(d=b.getRng(),c.appendChild(a.create("span",null,"\xa0")),b.select(c),c.lastChild.outerHTML="",b.setRng(d))},o=function(a,b,c){var d,e=c,f=[];if(e){for(;e=e.firstChild;){if(a.isBlock(e))return;1!=e.nodeType||b[e.nodeName.toLowerCase()]||f.push(e)}for(d=f.length;d--;)e=f[d],!e.hasChildNodes()||e.firstChild==e.lastChild&&""===e.firstChild.nodeValue?a.remove(e):i(e)&&a.remove(e)}},p=function(a,c,d){return b.isText(c)===!1?d:a?1===d&&c.data.charAt(d-1)===f.ZWSP?0:d:d===c.data.length-1&&c.data.charAt(d)===f.ZWSP?c.data.length:d},q=function(a){var b=a.cloneRange();return b.setStart(a.startContainer,p(!0,a.startContainer,a.startOffset)),b.setEnd(a.endContainer,p(!1,a.endContainer,a.endOffset)),b},r=function(a){for(;a;){if(1===a.nodeType||3===a.nodeType&&a.data&&/[\r\n\s]/.test(a.data))return a;a=a.nextSibling}},s=function(b){function f(f){function x(a){var b,c,f,h,j=a;if(a){if(e.ie&&e.ie<9&&N&&N.firstChild&&N.firstChild==N.lastChild&&"BR"==N.firstChild.tagName&&g.remove(N.firstChild),/^(LI|DT|DD)$/.test(a.nodeName)){var k=r(a.firstChild);k&&/^(UL|OL|DL)$/.test(k.nodeName)&&a.insertBefore(g.doc.createTextNode("\xa0"),a.firstChild)}if(f=g.createRng(),e.ie||a.normalize(),a.hasChildNodes()){for(b=new d(a,a);c=b.current();){if(3==c.nodeType){f.setStart(c,0),f.setEnd(c,0);break}if(w[c.nodeName.toLowerCase()]){f.setStartBefore(c),f.setEndBefore(c);break}j=c,c=b.next()}c||(f.setStart(j,0),f.setEnd(j,0))}else"BR"==a.nodeName?a.nextSibling&&g.isBlock(a.nextSibling)?((!O||O<9)&&(h=g.create("br"),a.parentNode.insertBefore(h,a)),f.setStartBefore(a),f.setEndBefore(a)):(f.setStartAfter(a),f.setEndAfter(a)):(f.setStart(a,0),f.setEnd(a,0));i.setRng(f),g.remove(h),i.scrollIntoView(a)}}function y(a){var b=s.forced_root_block;b&&b.toLowerCase()===a.tagName.toLowerCase()&&g.setAttribs(a,s.forced_root_block_attrs)}function z(a){var b,c,d,e=L,f=u.getTextInlineElements();if(a||"TABLE"==T||"HR"==T?(b=g.create(a||V),y(b)):b=N.cloneNode(!1),d=b,s.keep_styles===!1)g.setAttrib(b,"style",null),g.setAttrib(b,"class",null);else do if(f[e.nodeName]){if("_mce_caret"==e.id)continue;c=e.cloneNode(!1),g.setAttrib(c,"id",""),b.hasChildNodes()?(c.appendChild(b.firstChild),b.appendChild(c)):(d=c,b.appendChild(c))}while((e=e.parentNode)&&e!=K);return h||(d.innerHTML='<br data-mce-bogus="1">'),b}function A(a){var b,c,e,f;if(f=p(a,L,M),3==L.nodeType&&(a?f>0:f<L.nodeValue.length))return!1;if(L.parentNode==N&&W&&!a)return!0;if(a&&1==L.nodeType&&L==N.firstChild)return!0;if(l(L,"TABLE")||l(L,"HR"))return W&&!a||!W&&a;for(b=new d(L,N),3==L.nodeType&&(a&&0===f?b.prev():a||f!=L.nodeValue.length||b.next());c=b.current();){if(1===c.nodeType){if(!c.getAttribute("data-mce-bogus")&&(e=c.nodeName.toLowerCase(),v[e]&&"br"!==e))return!1}else if(3===c.nodeType&&!/^[ \t\r\n]*$/.test(c.nodeValue))return!1;a?b.prev():b.next()}return!0}function B(a,c){var d,e,f,h,i,k,l=V||"P";if(e=g.getParent(a,g.isBlock),!e||!m(g,e)){if(e=e||K,k=e==b.getBody()||j(e)?e.nodeName.toLowerCase():e.parentNode.nodeName.toLowerCase(),!e.hasChildNodes())return d=g.create(l),y(d),e.appendChild(d),I.setStart(d,0),I.setEnd(d,0),d;for(h=a;h.parentNode!=e;)h=h.parentNode;for(;h&&!g.isBlock(h);)f=h,h=h.previousSibling;if(f&&u.isValidChild(k,l.toLowerCase())){for(d=g.create(l),y(d),f.parentNode.insertBefore(d,f),h=f;h&&!g.isBlock(h);)i=h.nextSibling,d.appendChild(h),h=i;I.setStart(a,c),I.setEnd(a,c)}}return a}function C(){function a(a){for(var b=S[a?"firstChild":"lastChild"];b&&1!=b.nodeType;)b=b[a?"nextSibling":"previousSibling"];return b===N}function c(){var a=S.parentNode;return/^(LI|DT|DD)$/.test(a.nodeName)?a:S}if(S!=b.getBody()){var d=S.parentNode.nodeName;/^(OL|UL|LI)$/.test(d)&&(V="LI"),Q=V?z(V):g.create("BR"),a(!0)&&a()?"LI"==d?g.insertAfter(Q,c()):g.replace(Q,S):a(!0)?"LI"==d?(g.insertAfter(Q,c()),Q.appendChild(g.doc.createTextNode(" ")),Q.appendChild(S)):S.parentNode.insertBefore(Q,S):a()?(g.insertAfter(Q,c()),n(g,i,Q)):(S=c(),J=I.cloneRange(),J.setStartAfter(N),J.setEndAfter(S),R=J.extractContents(),"LI"==V&&"LI"==R.firstChild.nodeName?(Q=R.firstChild,g.insertAfter(R,S)):(g.insertAfter(R,S),g.insertAfter(Q,S))),g.remove(N),x(Q),t.add()}}function D(){b.execCommand("InsertLineBreak",!1,f)}function E(a){do 3===a.nodeType&&(a.nodeValue=a.nodeValue.replace(/^[\r\n]+/,"")),a=a.firstChild;while(a)}function F(a){var b,c,d=g.getRoot();for(b=a;b!==d&&"false"!==g.getContentEditable(b);)"true"===g.getContentEditable(b)&&(c=b),b=b.parentNode;return b!==d?c:d}function G(a){var b;h||(a.normalize(),b=a.lastChild,b&&!/^(left|right)$/gi.test(g.getStyle(b,"float",!0))||g.add(a,"br"))}function H(){Q=/^(H[1-6]|PRE|FIGURE)$/.test(T)&&"HGROUP"!=U?z(V):z(),s.end_container_on_empty_block&&m(g,S)&&g.isEmpty(N)?Q=g.split(S,N):g.insertAfter(Q,N),x(Q)}var I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W;if(I=i.getRng(!0),!f.isDefaultPrevented()){if(!I.collapsed)return void b.execCommand("Delete");if(new c(g).normalize(I),L=I.startContainer,M=I.startOffset,V=(s.force_p_newlines?"p":"")||s.forced_root_block,V=V?V.toUpperCase():"",O=g.doc.documentMode,P=f.shiftKey,1==L.nodeType&&L.hasChildNodes()&&(W=M>L.childNodes.length-1,L=L.childNodes[Math.min(M,L.childNodes.length-1)]||L,M=W&&3==L.nodeType?L.nodeValue.length:0),K=F(L)){if(t.beforeChange(),!g.isBlock(K)&&K!=g.getRoot())return void(V&&!P||D());if((V&&!P||!V&&P)&&(L=B(L,M)),N=g.getParent(L,g.isBlock),S=N?g.getParent(N.parentNode,g.isBlock):null,T=N?N.nodeName.toUpperCase():"",U=S?S.nodeName.toUpperCase():"","LI"!=U||f.ctrlKey||(N=S,T=U),b.undoManager.typing&&(b.undoManager.typing=!1,b.undoManager.add()),/^(LI|DT|DD)$/.test(T)){if(!V&&P)return void D();if(g.isEmpty(N))return void C()}if("PRE"==T&&s.br_in_pre!==!1){if(!P)return void D()}else if(!V&&!P&&"LI"!=T||V&&P)return void D();V&&N===b.getBody()||(V=V||"P",a.isCaretContainerBlock(N)?(Q=a.showCaretContainerBlock(N),g.isEmpty(N)&&k(N),x(Q)):A()?H():A(!0)?(Q=N.parentNode.insertBefore(z(),N),n(g,i,Q),x(l(N,"HR")?Q:N)):(J=q(I).cloneRange(),J.setEndAfter(N),R=J.extractContents(),E(R),Q=R.firstChild,g.insertAfter(R,N),o(g,v,Q),G(N),g.isEmpty(N)&&k(N),Q.normalize(),g.isEmpty(Q)?(g.remove(Q),H()):x(Q)),g.setAttrib(Q,"id",""),b.fire("NewBlock",{newBlock:Q}),t.typing=!1,t.add())}}}var g=b.dom,i=b.selection,s=b.settings,t=b.undoManager,u=b.schema,v=u.getNonEmptyElements(),w=u.getMoveCaretBeforeOnEnterElements();b.on("keydown",function(a){13==a.keyCode&&f(a)!==!1&&a.preventDefault()})};return{setup:s}}),g("7e",["1m","1p","1j","5n"],function(a,b,c,d){var e=function(a,b){return i(a)&&c.isText(b.container())},f=function(a,b){var c=b.container(),d=b.offset();c.insertData(d,"\xa0"),a.selection.setCursorLocation(c,d+1)},g=function(a,b,c){return!!e(c,b)&&(f(a,b),!0)},h=function(c){var e=b.fromRangeStart(c.selection.getRng()),f=d.readLocation(c.getBody(),e);return f.map(a.curry(g,c,e)).getOr(!1)},i=function(b){return b.fold(a.constant(!1),a.constant(!0),a.constant(!0),a.constant(!1))},j=function(a){return!!a.selection.isCollapsed()&&h(a)};return{insertAtSelection:j}}),g("6y",["3x","7e","7d","p"],function(a,b,c,d){var e=function(e,f){var g=c.match([{keyCode:d.SPACEBAR,action:c.action(b.insertAtSelection,e)}],f);a.find(g,function(a){return a.action()}).each(function(a){f.preventDefault()})},f=function(a){a.on("keydown",function(b){b.isDefaultPrevented()===!1&&e(a,b)})};return{setup:f}}),g("6f",["6v","5o","6w","6x","6y"],function(a,b,c,d,e){var f=function(f){var g=b.setupSelectedState(f);a.setup(f,g),c.setup(f,g),d.setup(f),e.setup(f)};return{setup:f}}),g("6g",["h","6","5"],function(a,b,c){return function(d){function e(a){var b,c;if(c=d.$(a).parentsUntil(d.getBody()).add(a),c.length===g.length){for(b=c.length;b>=0&&c[b]===g[b];b--);if(b===-1)return g=c,!0}return g=c,!1}var f,g=[];"onselectionchange"in d.getDoc()||d.on("NodeChange Click MouseUp KeyUp Focus",function(b){var c,e;c=d.selection.getRng(),e={startContainer:c.startContainer,startOffset:c.startOffset,endContainer:c.endContainer,endOffset:c.endOffset},"nodechange"!=b.type&&a.compareRanges(e,f)||d.fire("SelectionChange"),f=e}),d.on("contextmenu",function(){d.fire("SelectionChange")}),d.on("SelectionChange",function(){var a=d.selection.getStart(!0);!b.range&&d.selection.isCollapsed()||!e(a)&&d.dom.isChildOf(a,d.getBody())&&d.nodeChanged({selectionChange:!0})}),d.on("MouseUp",function(a){a.isDefaultPrevented()||("IMG"==d.selection.getNode().nodeName?c.setEditorTimeout(d,function(){d.nodeChanged()}):d.nodeChanged())}),this.nodeChanged=function(a){var b,c,e,f=d.selection;d.initialized&&f&&!d.settings.disable_nodechange&&!d.readonly&&(e=d.getBody(),b=f.getStart(!0)||e,b.ownerDocument==d.getDoc()&&d.dom.isChildOf(b,e)||(b=e),c=[],d.dom.getParent(b,function(a){return a===e||void c.push(a)}),a=a||{},a.element=b,a.parents=c,d.fire("NodeChange",a))}}}),g("6z",["1k","69","1p","a","1j","h","43","5"],function(a,b,c,d,e,f,g,h){var i=e.isContentEditableFalse,j=function(a){return a&&/^(TD|TH)$/i.test(a.nodeName)};return function(c,e){function f(a,b){var d,e,f,h,i,j=g.collapse(a.getBoundingClientRect(),b);return"BODY"==c.tagName?(d=c.ownerDocument.documentElement,e=c.scrollLeft||d.scrollLeft,f=c.scrollTop||d.scrollTop):(i=c.getBoundingClientRect(),e=c.scrollLeft-i.left,f=c.scrollTop-i.top),j.left+=e,j.right+=e,j.top+=f,j.bottom+=f,j.width=1,h=a.offsetWidth-a.clientWidth,h>0&&(b&&(h*=-1),j.left+=h,j.right+=h),j}function k(){var b,e,f,g,h;for(b=d("*[contentEditable=false]",c),g=0;g<b.length;g++)e=b[g],f=e.previousSibling,a.endsWithCaretContainer(f)&&(h=f.data,1==h.length?f.parentNode.removeChild(f):f.deleteData(h.length-1,1)),f=e.nextSibling,a.startsWithCaretContainer(f)&&(h=f.data,1==h.length?f.parentNode.removeChild(f):f.deleteData(0,1));return null}function l(b,g){var h,k;return m(),j(g)?null:e(g)?(s=a.insertBlock("p",g,b),h=f(g,b),d(s).css("top",h.top),r=d('<div class="mce-visual-caret" data-mce-bogus="all"></div>').css(h).appendTo(c),b&&r.addClass("mce-visual-caret-before"),n(),k=g.ownerDocument.createRange(),k.setStart(s,0),k.setEnd(s,0),k):(s=a.insertInline(g,b),k=g.ownerDocument.createRange(),i(s.nextSibling)?(k.setStart(s,0),k.setEnd(s,0)):(k.setStart(s,1),k.setEnd(s,1)),k)}function m(){k(),s&&(b.remove(s),s=null),r&&(r.remove(),r=null),clearInterval(q)}function n(){q=h.setInterval(function(){d("div.mce-visual-caret",c).toggleClass("mce-visual-caret-hidden")},500)}function o(){h.clearInterval(q)}function p(){return".mce-visual-caret {position: absolute;background-color: black;background-color: currentcolor;}.mce-visual-caret-hidden {display: none;}*[data-mce-caret] {position: absolute;left: -1000px;right: auto;top: 0;margin: 0;padding: 0;}"}var q,r,s;return{show:l,hide:m,getCss:p,destroy:o}}});g("7g",[],function(){var a=function(a){var b,c,d,e;return e=a.getBoundingClientRect(),b=a.ownerDocument,c=b.documentElement,d=b.defaultView,{top:e.top+d.pageYOffset-c.clientTop,left:e.left+d.pageXOffset-c.clientLeft}},b=function(b){return b.inline?a(b.getBody()):{left:0,top:0}},c=function(a){var b=a.getBody();return a.inline?{left:b.scrollLeft,top:b.scrollTop}:{left:0,top:0}},d=function(a){var b=a.getBody(),c=a.getDoc().documentElement,d={left:b.scrollLeft,top:b.scrollTop},e={left:b.scrollLeft||c.scrollLeft,top:b.scrollTop||c.scrollTop};return a.inline?d:e},e=function(b,c){if(c.target.ownerDocument!==b.getDoc()){var e=a(b.getContentAreaContainer()),f=d(b);return{left:c.pageX-e.left+f.left,top:c.pageY-e.top+f.top}}return{left:c.pageX,top:c.pageY}},f=function(a,b,c){return{pageX:c.left-a.left+b.left,pageY:c.top-a.top+b.top}},g=function(a,d){return f(b(a),c(a),e(a,d))};return{calc:g}});g("71",["1j","1g","1w","5","e","7g"],function(a,b,c,d,e,f){var g=a.isContentEditableFalse,h=a.isContentEditableTrue,i=function(a,b){return g(b)&&b!==a},j=function(a,b,c){return b!==c&&!a.dom.isChildOf(b,c)&&!g(b)},k=function(a){var b=a.cloneNode(!0);return b.removeAttribute("data-mce-selected"),b},l=function(a,b,c,d){var e=b.cloneNode(!0);a.dom.setStyles(e,{width:c,height:d}),a.dom.setAttrib(e,"data-mce-selected",null);var f=a.dom.create("div",{"class":"mce-drag-container","data-mce-bogus":"all",unselectable:"on",contenteditable:"false"});return a.dom.setStyles(f,{position:"absolute",opacity:.5,overflow:"hidden",border:0,padding:0,margin:0,width:c,height:d}),a.dom.setStyles(e,{margin:0,boxSizing:"border-box"}),f.appendChild(e),f},m=function(a,b){a.parentNode!==b&&b.appendChild(a)},n=function(a,b,c,d,e,f){var g=0,h=0;a.style.left=b.pageX+"px",a.style.top=b.pageY+"px",b.pageX+c>e&&(g=b.pageX+c-e),b.pageY+d>f&&(h=b.pageY+d-f),a.style.width=c-g+"px",a.style.height=d-h+"px"},o=function(a){a&&a.parentNode&&a.parentNode.removeChild(a)},p=function(a){return 0===a.button},q=function(a){return a.element},r=function(a,b){return{pageX:b.pageX-a.relX,pageY:b.pageY+5}},s=function(a,d){return function(e){if(p(e)){var f=b.find(d.dom.getParents(e.target),c.or(g,h));if(i(d.getBody(),f)){var j=d.dom.getPos(f),k=d.getBody(),m=d.getDoc().documentElement;a.element=f,a.screenX=e.screenX,a.screenY=e.screenY,a.maxX=(d.inline?k.scrollWidth:m.offsetWidth)-2,a.maxY=(d.inline?k.scrollHeight:m.offsetHeight)-2,a.relX=e.pageX-j.x,a.relY=e.pageY-j.y,a.width=f.offsetWidth,a.height=f.offsetHeight,a.ghost=l(d,f,a.width,a.height)}}}},t=function(a,b){var c=d.throttle(function(a,c){b._selectionOverrides.hideFakeCaret(),b.selection.placeCaretAt(a,c)},0);return function(d){var e=Math.max(Math.abs(d.screenX-a.screenX),Math.abs(d.screenY-a.screenY));if(q(a)&&!a.dragging&&e>10){var g=b.fire("dragstart",{target:a.element});if(g.isDefaultPrevented())return;a.dragging=!0,b.focus()}if(a.dragging){var h=r(a,f.calc(b,d));m(a.ghost,b.getBody()),n(a.ghost,h,a.width,a.height,a.maxX,a.maxY),c(d.clientX,d.clientY)}}},u=function(a){var b=a.getSel().getRangeAt(0),c=b.startContainer;return 3===c.nodeType?c.parentNode:c},v=function(a,b){return function(c){if(a.dragging&&j(b,u(b.selection),a.element)){var d=k(a.element),e=b.fire("drop",{targetClone:d,clientX:c.clientX,clientY:c.clientY});e.isDefaultPrevented()||(d=e.targetClone,b.undoManager.transact(function(){o(a.element),b.insertContent(b.dom.getOuterHTML(d)),b._selectionOverrides.hideFakeCaret()}))}x(a)}},w=function(a,b){return function(){x(a),a.dragging&&b.fire("dragend")}},x=function(a){a.dragging=!1,a.element=null,o(a.ghost)},y=function(a){var b,c,d,f,g,h,i={};b=e.DOM,h=document,c=s(i,a),d=t(i,a),f=v(i,a),g=w(i,a),a.on("mousedown",c),a.on("mousemove",d),a.on("mouseup",f),b.bind(h,"mousemove",d),b.bind(h,"mouseup",g),a.on("remove",function(){b.unbind(h,"mousemove",d),b.unbind(h,"mouseup",g)})},z=function(a){a.on("drop",function(b){var c="undefined"!=typeof b.clientX?a.getDoc().elementFromPoint(b.clientX,b.clientY):null;(g(c)||g(a.dom.getContentEditableParent(c)))&&b.preventDefault()})},A=function(a){y(a),z(a)};return{init:A}}),g("7k",[],function(){var a=function(a){return void 0!==a.style};return{isSupported:a}}),g("7h",["60","3x","63","4z","5a","6o","1r","4d","7k","6l","3z","49","4t"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){var n=function(b,c,d){if(!a.isString(d))throw l.error("Invalid call to CSS.set. Property ",c,":: Value ",d,":: Element ",b),new k("CSS value must be a string: "+d);i.isSupported(b)&&b.style.setProperty(c,d)},o=function(a,b){i.isSupported(a)&&a.style.removeProperty(b)},p=function(a,b,c){var d=a.dom();n(d,b,c)},q=function(a,b){var d=a.dom();c.each(b,function(a,b){n(d,b,a)})},r=function(a,b){var d=a.dom();c.each(b,function(a,b){a.fold(function(){o(d,b)},function(a){n(d,b,a)})})},s=function(a,b){var c=a.dom(),d=m.getComputedStyle(c),e=d.getPropertyValue(b),g=""!==e||f.inBody(a)?e:t(c,b);return null===g?void 0:g},t=function(a,b){return i.isSupported(a)?a.style.getPropertyValue(b):""},u=function(a,b){var c=a.dom(),e=t(c,b);return d.from(e).filter(function(a){return a.length>0})},v=function(a,b,c){var d=g.fromTag(a);p(d,b,c);var e=u(d,b);return e.isSome()},w=function(a,b){var c=a.dom();o(c,b),e.has(a,"style")&&""===j.trim(e.get(a,"style"))&&e.remove(a,"style")},x=function(a,b){var c=e.get(a,"style"),d=b(a),f=void 0===c?e.remove:e.set;return f(a,"style",c),d},y=function(a,b){var c=a.dom(),d=b.dom();i.isSupported(c)&&i.isSupported(d)&&(d.style.cssText=c.style.cssText)},z=function(a){return a.dom().offsetWidth},A=function(a,b,c){u(a,c).each(function(a){u(b,c).isNone()&&p(b,c,a)})},B=function(a,c,d){h.isElement(a)&&h.isElement(c)&&b.each(d,function(b){A(a,c,b)})};return{copy:y,set:p,preserve:x,setAll:q,setOptions:r,remove:w,get:s,getRaw:u,isValidValue:v,reflow:z,transfer:B}}),g("72",["1m","1r","7h","59"],function(a,b,c,d){var e=function(a,b){var c=b.dom();return c[a]},f=function(a,b){return parseInt(c.get(b,a),10)},g=a.curry(e,"clientWidth"),h=a.curry(e,"clientHeight"),i=a.curry(f,"margin-top"),j=a.curry(f,"margin-left"),k=function(a){return a.dom().getBoundingClientRect()},l=function(a,b,c){var d=g(a),e=h(a);return b>=0&&c>=0&&b<=d&&c<=e},m=function(a,b,c,d){var e=k(b),f=a?e.left+b.dom().clientLeft+j(b):0,g=a?e.top+b.dom().clientTop+i(b):0,h=c-f,l=d-g;return{x:h,y:l}},n=function(a,c,e){var f=b.fromDom(a.getBody()),g=a.inline?f:d.documentElement(f),h=m(a.inline,g,c,e);return l(g,h.x,h.y)};return{isXYInContentArea:n}}),g("6h",["1k","1p","5i","4m","6z","70","1j","1n","71","72","6","73","5","p"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){function o(g){function o(a){return g.dom.hasClass(a,"mce-offscreen-selection")}function t(){var a=g.dom.get(M);return a?a.getElementsByTagName("*")[0]:a}function u(a){return g.dom.isBlock(a)}function v(a){a&&g.selection.setRng(a)}function w(){return g.selection.getRng()}function x(a,b){g.selection.scrollIntoView(a,b)}function y(a,b,c){var d;return d=g.fire("ShowCaret",{target:b,direction:a,before:c}),d.isDefaultPrevented()?null:(x(b,a===-1),L.show(c,b))}function z(a,d){return d=c.normalizeRange(a,K,d),a==-1?b.fromRangeStart(d):b.fromRangeEnd(d)}function A(b){b.hasAttribute("data-mce-caret")&&(a.showCaretContainerBlock(b),v(w()),x(b[0]))}function B(){function a(a){for(var b=g.getBody();a&&a!=b;){if(p(a)||q(a))return a;a=a.parentNode}return null}function c(b){var c=!1;b.on("touchstart",function(){c=!1}),b.on("touchmove",function(){c=!0}),b.on("touchend",function(d){var e=a(d.target);q(e)&&(c||(d.preventDefault(),F(l.selectNode(b,e))))})}g.on("mouseup",function(a){var b=w();b.collapsed&&j.isXYInContentArea(g,a.clientX,a.clientY)&&v(l.renderCaretAtRange(g,b))}),g.on("click",function(b){var c;c=a(b.target),c&&(q(c)&&(b.preventDefault(),g.focus()),p(c)&&g.dom.isChildOf(c,g.selection.getNode())&&G())}),g.on("blur NewBlock",function(){G(),I()});var e=function(a){var c=new d(a);if(!a.firstChild)return!1;var e=b.before(a.firstChild),f=c.next(e);return f&&!s(f)&&!r(f)},u=function(a,b){var c=g.dom.getParent(a,g.dom.isBlock),d=g.dom.getParent(b,g.dom.isBlock);return c===d},x=function(a,b){var c=g.dom.getParent(a,g.dom.isBlock),d=g.dom.getParent(b,g.dom.isBlock);return c&&!u(c,d)&&e(c)};c(g),g.on("mousedown",function(b){var c;if(j.isXYInContentArea(g,b.clientX,b.clientY)!==!1)if(c=a(b.target))q(c)?(b.preventDefault(),F(l.selectNode(g,c))):(G(),p(c)&&b.shiftKey||h.isXYWithinRange(b.clientX,b.clientY,g.selection.getRng())||g.selection.placeCaretAt(b.clientX,b.clientY));else{G(),I();var d=f.closestCaret(K,b.clientX,b.clientY);d&&(x(b.target,d.node)||(b.preventDefault(),g.getBody().focus(),v(y(1,d.node,d.before))))}}),g.on("keypress",function(a){if(!n.modifierPressed(a))switch(a.keyCode){default:q(g.selection.getNode())&&a.preventDefault()}}),g.on("getSelectionRange",function(a){var b=a.range;if(J){if(!J.parentNode)return void(J=null);b=b.cloneRange(),b.selectNode(J),a.range=b}}),g.on("setSelectionRange",function(a){var b;b=F(a.range,a.forward),b&&(a.range=b)}),g.on("AfterSetSelectionRange",function(a){var b=a.range;E(b)||I(),o(b.startContainer.parentNode)||G()}),g.on("focus",function(){m.setEditorTimeout(g,function(){g.selection.setRng(l.renderRangeCaret(g,g.selection.getRng()))},0)}),g.on("copy",function(a){var b=a.clipboardData;if(!a.isDefaultPrevented()&&a.clipboardData&&!k.ie){var c=t();c&&(a.preventDefault(),b.clearData(),b.setData("text/html",c.outerHTML),b.setData("text/plain",c.outerText))}}),i.init(g)}function C(){var a=g.contentStyles,b=".mce-content-body";a.push(L.getCss()),a.push(b+" .mce-offscreen-selection {position: absolute;left: -9999999999px;max-width: 1000000px;}"+b+" *[contentEditable=false] {cursor: default;}"+b+" *[contentEditable=true] {cursor: text;}")}function D(b){return a.isCaretContainer(b)||a.startsWithCaretContainer(b)||a.endsWithCaretContainer(b)}function E(a){return D(a.startContainer)||D(a.endContainer); +}function F(a,b){var c,d,e,f,h,i,j,l,m,n,o=g.$,p=g.dom;if(!a)return null;if(a.collapsed){if(!E(a))if(b===!1){if(l=z(-1,a),q(l.getNode(!0)))return y(-1,l.getNode(!0),!1);if(q(l.getNode()))return y(-1,l.getNode(),!l.isAtEnd())}else{if(l=z(1,a),q(l.getNode()))return y(1,l.getNode(),!l.isAtEnd());if(q(l.getNode(!0)))return y(1,l.getNode(!0),!1)}return null}return f=a.startContainer,h=a.startOffset,i=a.endOffset,3==f.nodeType&&0==h&&q(f.parentNode)&&(f=f.parentNode,h=p.nodeIndex(f),f=f.parentNode),1!=f.nodeType?null:(i==h+1&&(c=f.childNodes[h]),q(c)?(m=n=c.cloneNode(!0),j=g.fire("ObjectSelected",{target:c,targetClone:m}),j.isDefaultPrevented()?null:(m=j.targetClone,d=o("#"+M),0===d.length&&(d=o('<div data-mce-bogus="all" class="mce-offscreen-selection"></div>').attr("id",M),d.appendTo(g.getBody())),a=g.dom.createRng(),m===n&&k.ie?(d.empty().append('<p style="font-size: 0" data-mce-bogus="all">\xa0</p>').append(m),a.setStartAfter(d[0].firstChild.firstChild),a.setEndAfter(m)):(d.empty().append("\xa0").append(m).append("\xa0"),a.setStart(d[0].firstChild,1),a.setEnd(d[0].lastChild,0)),d.css({top:p.getPos(c,g.getBody()).y}),d[0].focus(),e=g.selection.getSel(),e.removeAllRanges(),e.addRange(a),g.$("*[data-mce-selected]").removeAttr("data-mce-selected"),c.setAttribute("data-mce-selected",1),J=c,I(),a)):null)}function G(){J&&(J.removeAttribute("data-mce-selected"),g.$("#"+M).remove(),J=null)}function H(){L.destroy(),J=null}function I(){L.hide()}var J,K=g.getBody(),L=new e(g.getBody(),u),M="sel-"+g.dom.uniqueId();return k.ceFalse&&(B(),C()),{showCaret:y,showBlockCaretContainer:A,hideFakeCaret:I,destroy:H}}var p=g.isContentEditableTrue,q=g.isContentEditableFalse,r=c.isAfterContentEditableFalse,s=c.isBeforeContentEditableFalse;return o}),g("74",["e"],function(a){function b(b,c,d){for(var e=[];c&&c!=b;c=c.parentNode)e.push(a.nodeIndex(c,d));return e}function c(a,b){var c,d,e;for(d=a,c=b.length-1;c>=0;c--){if(e=d.childNodes,b[c]>e.length-1)return null;d=e[b[c]]}return d}return{create:b,resolve:c}}),g("6i",["p","h","c","74","i","d","6","9","5","1k","1p","4m"],function(a,b,c,d,e,f,g,h,i,j,k,l){return function(c){function d(a,b){try{c.getDoc().execCommand(a,!1,b)}catch(a){}}function m(){var a=c.getDoc().documentMode;return a?a:6}function n(a){return a.isDefaultPrevented()}function o(a){var b,d;a.dataTransfer&&(c.selection.isCollapsed()&&"IMG"==a.target.tagName&&_.select(a.target),b=c.selection.getContent(),b.length>0&&(d=ga+escape(c.id)+","+escape(b),a.dataTransfer.setData(ha,d)))}function p(a){var b;return a.dataTransfer&&(b=a.dataTransfer.getData(ha),b&&b.indexOf(ga)>=0)?(b=b.substr(ga.length).split(","),{id:unescape(b[0]),html:unescape(b[1])}):null}function q(a,b){c.queryCommandSupported("mceInsertClipboardContent")?c.execCommand("mceInsertClipboardContent",!1,{content:a,internal:b}):c.execCommand("mceInsertContent",!1,a)}function r(){function a(a){var b=$.create("body"),c=a.cloneContents();return b.appendChild(c),_.serializer.serialize(b,{format:"html"})}function d(d){if(!d.setStart){if(d.item)return!1;var e=d.duplicate();return e.moveToElementText(c.getBody()),b.compareRanges(d,e)}var f=a(d),g=$.createRng();g.selectNode(c.getBody());var h=a(g);return f===h}c.on("keydown",function(a){var b,e,f=a.keyCode;if(!n(a)&&(f==Z||f==Y)){if(b=c.selection.isCollapsed(),e=c.getBody(),b&&!$.isEmpty(e))return;if(!b&&!d(c.selection.getRng()))return;a.preventDefault(),c.setContent(""),e.firstChild&&$.isBlock(e.firstChild)?c.selection.setCursorLocation(e.firstChild,0):c.selection.setCursorLocation(e,0),c.nodeChanged()}})}function s(){c.shortcuts.add("meta+a",null,"SelectAll")}function t(){c.settings.content_editable||$.bind(c.getDoc(),"mousedown mouseup",function(a){var b;if(a.target==c.getDoc().documentElement)if(b=_.getRng(),c.getBody().focus(),"mousedown"==a.type){if(j.isCaretContainer(b.startContainer))return;_.placeCaretAt(a.clientX,a.clientY)}else _.setRng(b)})}function u(){c.on("keydown",function(a){if(!n(a)&&a.keyCode===Y){if(!c.getBody().getElementsByTagName("hr").length)return;if(_.isCollapsed()&&0===_.getRng(!0).startOffset){var b=_.getNode(),d=b.previousSibling;if("HR"==b.nodeName)return $.remove(b),void a.preventDefault();d&&d.nodeName&&"hr"===d.nodeName.toLowerCase()&&($.remove(d),a.preventDefault())}}})}function v(){window.Range.prototype.getClientRects||c.on("mousedown",function(a){if(!n(a)&&"HTML"===a.target.nodeName){var b=c.getBody();b.blur(),i.setEditorTimeout(c,function(){b.focus()})}})}function w(){c.on("click",function(a){var b=a.target;/^(IMG|HR)$/.test(b.nodeName)&&"false"!==$.getContentEditableParent(b)&&(a.preventDefault(),c.selection.select(b),c.nodeChanged()),"A"==b.nodeName&&$.hasClass(b,"mce-item-anchor")&&(a.preventDefault(),_.select(b))})}function x(){function a(){var a=$.getAttribs(_.getStart().cloneNode(!1));return function(){var b=_.getStart();b!==c.getBody()&&($.setAttrib(b,"style",null),X(a,function(a){b.setAttributeNode(a.cloneNode(!0))}))}}function b(){return!_.isCollapsed()&&$.getParent(_.getStart(),$.isBlock)!=$.getParent(_.getEnd(),$.isBlock)}c.on("keypress",function(d){var e;if(!n(d)&&(8==d.keyCode||46==d.keyCode)&&b())return e=a(),c.getDoc().execCommand("delete",!1,null),e(),d.preventDefault(),!1}),$.bind(c.getDoc(),"cut",function(d){var e;!n(d)&&b()&&(e=a(),i.setEditorTimeout(c,function(){e()}))})}function y(){document.body.setAttribute("role","application")}function z(){c.on("keydown",function(a){if(!n(a)&&a.keyCode===Y&&_.isCollapsed()&&0===_.getRng(!0).startOffset){var b=_.getNode().previousSibling;if(b&&b.nodeName&&"table"===b.nodeName.toLowerCase())return a.preventDefault(),!1}})}function A(){m()>7||(d("RespectVisibilityInDesign",!0),c.contentStyles.push(".mceHideBrInPre pre br {display: none}"),$.addClass(c.getBody(),"mceHideBrInPre"),ba.addNodeFilter("pre",function(a){for(var b,c,d,f,g=a.length;g--;)for(b=a[g].getAll("br"),c=b.length;c--;)d=b[c],f=d.prev,f&&3===f.type&&"\n"!=f.value.charAt(f.value-1)?f.value+="\n":d.parent.insert(new e("#text",3),d,!0).value="\n"}),ca.addNodeFilter("pre",function(a){for(var b,c,d,e,f=a.length;f--;)for(b=a[f].getAll("br"),c=b.length;c--;)d=b[c],e=d.prev,e&&3==e.type&&(e.value=e.value.replace(/\r?\n$/,""))}))}function B(){$.bind(c.getBody(),"mouseup",function(){var a,b=_.getNode();"IMG"==b.nodeName&&((a=$.getStyle(b,"width"))&&($.setAttrib(b,"width",a.replace(/[^0-9%]+/g,"")),$.setStyle(b,"width","")),(a=$.getStyle(b,"height"))&&($.setAttrib(b,"height",a.replace(/[^0-9%]+/g,"")),$.setStyle(b,"height","")))})}function C(){c.on("keydown",function(b){var d,e,f,g,h;if(!n(b)&&b.keyCode==a.BACKSPACE&&(d=_.getRng(),e=d.startContainer,f=d.startOffset,g=$.getRoot(),h=e,d.collapsed&&0===f)){for(;h&&h.parentNode&&h.parentNode.firstChild==h&&h.parentNode!=g;)h=h.parentNode;"BLOCKQUOTE"===h.tagName&&(c.formatter.toggle("blockquote",null,h),d=$.createRng(),d.setStart(e,0),d.setEnd(e,0),_.setRng(d))}})}function D(){function a(){U(),d("StyleWithCSS",!1),d("enableInlineTableEditing",!1),aa.object_resizing||d("enableObjectResizing",!1)}aa.readonly||c.on("BeforeExecCommand MouseDown",a)}function E(){function a(){X($.select("a"),function(a){var b=a.parentNode,c=$.getRoot();if(b.lastChild===a){for(;b&&!$.isBlock(b);){if(b.parentNode.lastChild!==b||b===c)return;b=b.parentNode}$.add(b,"br",{"data-mce-bogus":1})}})}c.on("SetContent ExecCommand",function(b){"setcontent"!=b.type&&"mceInsertLink"!==b.command||a()})}function F(){aa.forced_root_block&&c.on("init",function(){d("DefaultParagraphSeparator",aa.forced_root_block)})}function G(){c.on("keydown",function(a){var b;n(a)||a.keyCode!=Y||(b=c.getDoc().selection.createRange(),b&&b.item&&(a.preventDefault(),c.undoManager.beforeChange(),$.remove(b.item(0)),c.undoManager.add()))})}function H(){var a;m()>=10&&(a="",X("p div h1 h2 h3 h4 h5 h6".split(" "),function(b,c){a+=(c>0?",":"")+b+":empty"}),c.contentStyles.push(a+"{padding-right: 1px !important}"))}function I(){m()<9&&(ba.addNodeFilter("noscript",function(a){for(var b,c,d=a.length;d--;)b=a[d],c=b.firstChild,c&&b.attr("data-mce-innertext",c.value)}),ca.addNodeFilter("noscript",function(a){for(var b,c,d,g=a.length;g--;)b=a[g],c=a[g].firstChild,c?c.value=f.decode(c.value):(d=b.attributes.map["data-mce-innertext"],d&&(b.attr("data-mce-innertext",null),c=new e("#text",3),c.value=d,c.raw=!0,b.append(c)))}))}function J(){function a(a,b){var c=h.createTextRange();try{c.moveToPoint(a,b)}catch(a){c=null}return c}function b(b){var d;b.button?(d=a(b.x,b.y),d&&(d.compareEndPoints("StartToStart",e)>0?d.setEndPoint("StartToStart",e):d.setEndPoint("EndToEnd",e),d.select())):c()}function c(){var a=g.selection.createRange();e&&!a.item&&0===a.compareEndPoints("StartToEnd",a)&&e.select(),$.unbind(g,"mouseup",c),$.unbind(g,"mousemove",b),e=d=0}var d,e,f,g=$.doc,h=g.body;g.documentElement.unselectable=!0,$.bind(g,"mousedown contextmenu",function(h){if("HTML"===h.target.nodeName){if(d&&c(),f=g.documentElement,f.scrollHeight>f.clientHeight)return;d=1,e=a(h.x,h.y),e&&($.bind(g,"mouseup",c),$.bind(g,"mousemove",b),$.getRoot().focus(),e.select())}})}function K(){c.on("keyup focusin mouseup",function(b){65==b.keyCode&&a.metaKeyPressed(b)||_.normalize()},!0)}function L(){c.contentStyles.push("img:-moz-broken {-moz-force-broken-image-icon:1;min-width:24px;min-height:24px}")}function M(){c.inline||c.on("keydown",function(){document.activeElement==document.body&&c.getWin().focus()})}function N(){c.inline||(c.contentStyles.push("body {min-height: 150px}"),c.on("click",function(a){var b;if("HTML"==a.target.nodeName){if(g.ie>11)return void c.getBody().focus();b=c.selection.getRng(),c.getBody().focus(),c.selection.setRng(b),c.selection.normalize(),c.nodeChanged()}}))}function O(){g.mac&&c.on("keydown",function(b){!a.metaKeyPressed(b)||b.shiftKey||37!=b.keyCode&&39!=b.keyCode||(b.preventDefault(),c.selection.getSel().modify("move",37==b.keyCode?"backward":"forward","lineboundary"))})}function P(){d("AutoUrlDetect",!1)}function Q(){c.on("click",function(a){var b=a.target;do if("A"===b.tagName)return void a.preventDefault();while(b=b.parentNode)}),c.contentStyles.push(".mce-content-body {-webkit-touch-callout: none}")}function R(){c.on("init",function(){c.dom.bind(c.getBody(),"submit",function(a){a.preventDefault()})})}function S(){ba.addNodeFilter("br",function(a){for(var b=a.length;b--;)"Apple-interchange-newline"==a[b].attr("class")&&a[b].remove()})}function T(){c.on("dragstart",function(a){o(a)}),c.on("drop",function(a){if(!n(a)){var d=p(a);if(d&&d.id!=c.id){a.preventDefault();var e=b.getCaretRangeFromPoint(a.x,a.y,c.getDoc());_.setRng(e),q(d.html,!0)}}})}function U(){}function V(){var a;return!da||c.removed?0:(a=c.selection.getSel(),!a||!a.rangeCount||0===a.rangeCount)}function W(){function b(a){var b=new l(a.getBody()),c=a.selection.getRng(),d=k.fromRangeStart(c),e=k.fromRangeEnd(c),f=b.prev(d),g=b.next(e);return!a.selection.isCollapsed()&&(!f||f.isAtStart()&&d.isEqual(f))&&(!g||g.isAtEnd()&&d.isEqual(g))}c.on("keypress",function(d){!n(d)&&!_.isCollapsed()&&d.charCode>31&&!a.metaKeyPressed(d)&&b(c)&&(d.preventDefault(),c.setContent(String.fromCharCode(d.charCode)),c.selection.select(c.getBody(),!0),c.selection.collapse(!1),c.nodeChanged())}),c.on("keydown",function(a){var d=a.keyCode;n(a)||d!=Z&&d!=Y||b(c)&&(a.preventDefault(),c.setContent(""),c.nodeChanged())})}var X=h.each,Y=a.BACKSPACE,Z=a.DELETE,$=c.dom,_=c.selection,aa=c.settings,ba=c.parser,ca=c.serializer,da=g.gecko,ea=g.ie,fa=g.webkit,ga="data:text/mce-internal,",ha=ea?"Text":"URL";return C(),r(),g.windowsPhone||K(),fa&&(W(),t(),w(),F(),R(),z(),S(),g.iOS?(M(),N(),Q()):s()),ea&&g.ie<11&&(u(),y(),A(),B(),G(),H(),I(),J()),g.ie>=11&&(N(),z()),g.ie&&(s(),P(),T()),da&&(W(),u(),v(),x(),D(),E(),L(),O(),z()),{refreshContentEditable:U,isHidden:V}}}),g("5r",["22","4t","6c","e","s","o","6d","2b","6e","t","l","i","j","6f","6g","6h","u","5","6i","9"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t){var u=d.DOM,v=function(a){var b=new k(a.settings,a.schema);return b.addAttributeFilter("src,href,style,tabindex",function(b,c){for(var d,e,f,g=b.length,h=a.dom;g--;)if(d=b[g],e=d.attr(c),f="data-mce-"+c,!d.attributes.map[f]){if(0===e.indexOf("data:")||0===e.indexOf("blob:"))continue;"style"===c?(e=h.serializeStyle(h.parseStyle(e),d.name),e.length||(e=null),d.attr(f,e),d.attr(c,e)):"tabindex"===c?(d.attr(f,e),d.attr(c,null)):d.attr(f,a.convertURL(e,c,d.name))}}),b.addNodeFilter("script",function(a){for(var b,c,d=a.length;d--;)b=a[d],c=b.attr("type")||"no/type",0!==c.indexOf("mce-")&&b.attr("type","mce-"+c)}),b.addNodeFilter("#cdata",function(a){for(var b,c=a.length;c--;)b=a[c],b.type=8,b.name="#comment",b.value="[CDATA["+b.value+"]]"}),b.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(b){for(var c,d=b.length,e=a.schema.getNonEmptyElements();d--;)c=b[d],c.isEmpty(e)&&0===c.getAll("br").length&&(c.append(new l("br",1)).shortEnded=!0)}),b},w=function(a){a.settings.auto_focus&&r.setEditorTimeout(a,function(){var b;b=a.settings.auto_focus===!0?a:a.editorManager.get(a.settings.auto_focus),b.destroyed||b.focus()},100)},x=function(a){a.bindPendingEventDelegates(),a.initialized=!0,a.fire("init"),a.focus(!0),a.nodeChanged({initial:!0}),a.execCallback("init_instance_callback",a),w(a)},y=function(a){return a.inline?u.styleSheetLoader:a.dom.styleSheetLoader},z=function(k,l){var r,w,z=k.settings,A=k.getElement(),B=k.getDoc();z.inline||(k.getElement().style.visibility=k.orgVisibility),l||z.content_editable||(B.open(),B.write(k.iframeHTML),B.close()),z.content_editable&&(k.on("remove",function(){var a=this.getBody();u.removeClass(a,"mce-content-body"),u.removeClass(a,"mce-edit-focus"),u.setAttrib(a,"contentEditable",null)}),u.addClass(A,"mce-content-body"),k.contentDocument=B=z.content_document||a,k.contentWindow=z.content_window||b,k.bodyElement=A,z.content_document=z.content_window=null,z.root_name=A.nodeName.toLowerCase()),r=k.getBody(),r.disabled=!0,k.readonly=z.readonly,k.readonly||(k.inline&&"static"===u.getStyle(r,"position",!0)&&(r.style.position="relative"),r.contentEditable=k.getParam("content_editable_state",!0)),r.disabled=!1,k.editorUpload=new g(k),k.schema=new m(z),k.dom=new d(B,{keep_values:!0,url_converter:k.convertURL,url_converter_scope:k,hex_colors:z.force_hex_style_colors,class_filter:z.class_filter,update_styles:!0,root_element:k.inline?k.getBody():null,collect:z.content_editable,schema:k.schema,onSetAttrib:function(a){k.fire("SetAttrib",a)}}),k.parser=v(k),k.serializer=new f(z,k),k.selection=new e(k.dom,k.getWin(),k.serializer,k),k.formatter=new j(k),k.undoManager=new q(k),k._nodeChangeDispatcher=new o(k),k._selectionOverrides=new p(k),c.setup(k),n.setup(k),i.setup(k),k.fire("PreInit"),z.browser_spellcheck||z.gecko_spellcheck||(B.body.spellcheck=!1,u.setAttrib(r,"spellcheck","false")),k.quirks=new s(k),k.fire("PostRender"),z.directionality&&(r.dir=z.directionality),z.nowrap&&(r.style.whiteSpace="nowrap"),z.protect&&k.on("BeforeSetContent",function(a){t.each(z.protect,function(b){a.content=a.content.replace(b,function(a){return"<!--mce:protected "+escape(a)+"-->"})})}),k.on("SetContent",function(){k.addVisual(k.getBody())}),z.padd_empty_editor&&k.on("PostProcess",function(a){a.content=a.content.replace(/^(<p[^>]*>( | |\s|\u00a0|<br \/>|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/,"")}),k.load({initial:!0,format:"html"}),k.startContent=k.getContent({format:"raw"}),k.on("compositionstart compositionend",function(a){k.composing="compositionstart"===a.type}),k.contentStyles.length>0&&(w="",t.each(k.contentStyles,function(a){w+=a+"\r\n"}),k.dom.addStyle(w)),y(k).loadAll(k.contentCSS,function(a){x(k)},function(a){x(k),h.contentCssError(k,a)})};return{initContentBody:z}}),g("4v",["g"],function(a){return a.PluginManager}),g("4w",["g"],function(a){return a.ThemeManager}),g("4u",["22","4t","e","6","5r","4v","4w","9","2a"],function(a,b,c,d,e,f,g,h,i){var j=c.DOM,k=function(a,b,c){var d,e,g=f.get(c);if(d=f.urls[c]||a.documentBaseUrl.replace(/\/$/,""),c=h.trim(c),g&&h.inArray(b,c)===-1){if(h.each(f.dependencies(c),function(c){k(a,b,c)}),a.plugins[c])return;e=new g(a,d,a.$),a.plugins[c]=e,e.init&&(e.init(a,d),b.push(c))}},l=function(a){var b=[];h.each(a.settings.plugins.replace(/\-/g,"").split(/[ ,]/),function(c){k(a,b,c)})},m=function(a){var b,c=a.settings;c.theme&&("function"!=typeof c.theme?(c.theme=c.theme.replace(/-/,""),b=g.get(c.theme),a.theme=new b(a,g.urls[c.theme]),a.theme.init&&a.theme.init(a,g.urls[c.theme]||a.documentBaseUrl.replace(/\/$/,""),a.$)):a.theme=c.theme)},n=function(a){var b,c,d,e,f,g=a.settings,h=a.getElement();return g.render_ui&&a.theme&&(a.orgDisplay=h.style.display,"function"!=typeof g.theme?(b=g.width||j.getStyle(h,"width")||"100%",c=g.height||j.getStyle(h,"height")||h.offsetHeight,d=g.min_height||100,e=/^[0-9\.]+(|px)$/i,e.test(""+b)&&(b=Math.max(parseInt(b,10),100)),e.test(""+c)&&(c=Math.max(parseInt(c,10),d)),f=a.theme.renderUI({targetNode:h,width:b,height:c,deltaWidth:g.delta_width,deltaHeight:g.delta_height}),g.content_editable||(c=(f.iframeHeight||c)+("number"==typeof c?f.deltaHeight||0:""),c<d&&(c=d))):(f=g.theme(a,h),f.editorContainer.nodeType&&(f.editorContainer.id=f.editorContainer.id||a.id+"_parent"),f.iframeContainer.nodeType&&(f.iframeContainer.id=f.iframeContainer.id||a.id+"_iframecontainer"),c=f.iframeHeight||h.offsetHeight),a.editorContainer=f.editorContainer,f.height=c),f},o=function(c,f){if(a.domain!==b.location.hostname&&d.ie&&d.ie<12){var g=i.uuid("mce");c[g]=function(){e.initContentBody(c)};var h='javascript:(function(){document.open();document.domain="'+a.domain+'";var ed = window.parent.tinymce.get("'+c.id+'");document.write(ed.iframeHTML);document.close();ed.'+g+"(true);})()";return j.setAttrib(f,"src",h),!0}return!1},p=function(a,b){var c,e,f=a.settings;a.iframeHTML=f.doctype+"<html><head>",f.document_base_url!=a.documentBaseUrl&&(a.iframeHTML+='<base href="'+a.documentBaseURI.getURI()+'" />'),!d.caretAfter&&f.ie7_compat&&(a.iframeHTML+='<meta http-equiv="X-UA-Compatible" content="IE=7" />'),a.iframeHTML+='<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />',c=f.body_id||"tinymce",c.indexOf("=")!=-1&&(c=a.getParam("body_id","","hash"),c=c[a.id]||c),e=f.body_class||"",e.indexOf("=")!=-1&&(e=a.getParam("body_class","","hash"),e=e[a.id]||""),f.content_security_policy&&(a.iframeHTML+='<meta http-equiv="Content-Security-Policy" content="'+f.content_security_policy+'" />'),a.iframeHTML+='</head><body id="'+c+'" class="mce-content-body '+e+'" data-id="'+a.id+'"><br></body></html>';var g=j.create("iframe",{id:a.id+"_ifr",frameBorder:"0",allowTransparency:"true",title:a.editorManager.translate("Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help"),style:{width:"100%",height:b.height,display:"block"}});g.onload=function(){g.onload=null,a.fire("load")};var h=o(a,g);return a.contentAreaContainer=b.iframeContainer,a.iframeElement=g,j.add(b.iframeContainer,g),h},q=function(a){var b,c=a.settings,d=a.getElement();if(a.rtl=c.rtl_ui||a.editorManager.i18n.rtl,a.editorManager.i18n.setCode(c.language),c.aria_label=c.aria_label||j.getAttrib(d,"aria-label",a.getLang("aria.rich_text_area")),a.fire("ScriptsLoaded"),m(a),l(a),b=n(a),c.content_css&&h.each(h.explode(c.content_css),function(b){a.contentCSS.push(a.documentBaseURI.toAbsolute(b))}),c.content_style&&a.contentStyles.push(c.content_style),c.content_editable)return e.initContentBody(a);var f=p(a,b);b.editorContainer&&(j.get(b.editorContainer).style.display=a.orgDisplay,a.hidden=j.isHidden(b.editorContainer)),a.getElement().style.display="none",j.setAttrib(a.id,"aria-hidden",!0),f||e.initContentBody(a)};return{init:q}}),g("27",["4t","e","7","f","6","2b","4u","11","4v","4w","9","10"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m=b.DOM,n=function(a,b){var c=a.settings,e=d.ScriptLoader;if(c.language&&"en"!=c.language&&!c.language_url&&(c.language_url=a.editorManager.baseURL+"/langs/"+c.language+".js"),c.language_url&&e.add(c.language_url),c.theme&&"function"!=typeof c.theme&&"-"!=c.theme.charAt(0)&&!j.urls[c.theme]){var h=c.theme_url;h=h?a.documentBaseURI.toAbsolute(h):"themes/"+c.theme+"/theme"+b+".js",j.load(c.theme,h)}k.isArray(c.plugins)&&(c.plugins=c.plugins.join(" ")),k.each(c.external_plugins,function(a,b){i.load(b,a),c.plugins+=" "+b}),k.each(c.plugins.split(/[ ,]/),function(a){if(a=k.trim(a),a&&!i.urls[a])if("-"===a.charAt(0)){a=a.substr(1,a.length);var c=i.dependencies(a);k.each(c,function(a){var c={prefix:"plugins/",resource:a,suffix:"/plugin"+b+".js"};a=i.createUrl(c,a),i.load(a.resource,a)})}else i.load(a,{prefix:"plugins/",resource:a,suffix:"/plugin"+b+".js"})}),e.loadQueue(function(){a.removed||g.init(a)},a,function(b){f.pluginLoadError(a,b[0]),a.removed||g.init(a)})},o=function(b){function d(){m.unbind(a,"ready",d),b.render()}var f=b.settings,g=b.id;if(!c.Event.domLoaded)return void m.bind(a,"ready",d);if(b.getElement()&&e.contentEditable){f.inline?b.inline=!0:(b.orgVisibility=b.getElement().style.visibility,b.getElement().style.visibility="hidden");var i=b.getElement().form||m.getParent(g,"form");i&&(b.formElement=i,f.hidden_input&&!/TEXTAREA|INPUT/i.test(b.getElement().nodeName)&&(m.insertAfter(m.create("input",{type:"hidden",name:g}),g),b.hasHiddenInput=!0),b.formEventDelegate=function(a){b.fire(a.type,a)},m.bind(i,"submit reset",b.formEventDelegate),b.on("reset",function(){b.setContent(b.startContent,{format:"raw"})}),!f.submit_patch||i.submit.nodeType||i.submit.length||i._mceOldSubmit||(i._mceOldSubmit=i.submit,i.submit=function(){return b.editorManager.triggerSave(),b.setDirty(!1),i._mceOldSubmit(i)})),b.windowManager=new l(b),b.notificationManager=new h(b),"xml"===f.encoding&&b.on("GetContent",function(a){a.save&&(a.content=m.encode(a.content))}),f.add_form_submit_trigger&&b.on("submit",function(){b.initialized&&b.save()}),f.add_unload_trigger&&(b._beforeUnload=function(){!b.initialized||b.destroyed||b.isHidden()||b.save({format:"raw",no_events:!0,set_dirty:!1})},b.editorManager.on("BeforeUnload",b._beforeUnload)),b.editorManager.add(b),n(b,b.suffix)}};return{render:o}}),g("28",[],function(){function a(a,b,c){try{a.getDoc().execCommand(b,!1,c)}catch(a){}}function b(a){var b,c;return b=a.getBody(),c=function(b){a.dom.getParents(b.target,"a").length>0&&b.preventDefault()},a.dom.bind(b,"click",c),{unbind:function(){a.dom.unbind(b,"click",c)}}}function c(c,d){c._clickBlocker&&(c._clickBlocker.unbind(),c._clickBlocker=null),d?(c._clickBlocker=b(c),c.selection.controlSelection.hideResizeRect(),c.readonly=!0,c.getBody().contentEditable=!1):(c.readonly=!1,c.getBody().contentEditable=!0,a(c,"StyleWithCSS",!1),a(c,"enableInlineTableEditing",!1),a(c,"enableObjectResizing",!1),c.focus(),c.nodeChanged())}function d(a,b){var d=a.readonly?"readonly":"design";b!=d&&(a.initialized?c(a,"readonly"==b):a.on("init",function(){c(a,"readonly"==b)}),a.fire("SwitchMode",{mode:b}))}return{setMode:d}}),g("29",[],function(){var a=function(a,b,c){var d=a.sidebars?a.sidebars:[];d.push({name:b,settings:c}),a.sidebars=d};return{add:a}}),g("14",["g","a","e","v","12","26","6","n","27","28","13","29","9","w","2a"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){function p(c,e,h){var i,j,l=this;i=l.documentBaseUrl=h.documentBaseURL,j=h.baseURI,e=f.getEditorSettings(l,c,i,h.defaultSettings,e),l.settings=e,a.language=e.language||"en",a.languageLoad=e.language_load,a.baseURL=h.baseURL,l.id=c,l.setDirty(!1),l.plugins={},l.documentBaseURI=new n(e.document_base_url,{base_uri:j}),l.baseURI=j,l.contentCSS=[],l.contentStyles=[],l.shortcuts=new k(l),l.loadedCSS={},l.editorCommands=new d(l),l.suffix=h.suffix,l.editorManager=h,l.inline=e.inline,e.cache_suffix&&(g.cacheSuffix=e.cache_suffix.replace(/^[\?\&]+/,"")),e.override_viewport===!1&&(g.overrideViewPort=!1),h.fire("SetupEditor",l),l.execCallback("setup",l),l.$=b.overrideDefaults(function(){return{context:l.inline?l.getBody():l.getDoc(),element:l.getBody()}})}var q=c.DOM,r=m.extend,s=m.each,t=m.trim,u=m.resolve,v=g.gecko,w=g.ie;return p.prototype={render:function(){i.render(this)},focus:function(a){function b(a){return f.dom.getParent(a,function(a){return"true"===f.dom.getContentEditable(a)})}var c,d,e,f=this,h=f.selection,i=f.settings.content_editable,j=f.getDoc(),k=f.getBody();if(!f.removed){if(!a){if(c=h.getRng(),c.item&&(d=c.item(0)),f.quirks.refreshContentEditable(),e=b(h.getNode()),f.$.contains(k,e))return e.focus(),h.normalize(),void f.editorManager.setActive(f);if(i||(g.opera||f.getBody().focus(),f.getWin().focus()),v||i){if(k.setActive)try{k.setActive()}catch(a){k.focus()}else f.selection.setRng(f.lastRng),k.focus();i&&h.normalize()}d&&d.ownerDocument==j&&(c=j.body.createControlRange(),c.addElement(d),c.select())}f.editorManager.setActive(f)}},execCallback:function(a){var b,c=this,d=c.settings[a];if(d)return c.callbackLookup&&(b=c.callbackLookup[a])&&(d=b.func,b=b.scope),"string"==typeof d&&(b=d.replace(/\.\w+$/,""),b=b?u(b):0,d=u(d),c.callbackLookup=c.callbackLookup||{},c.callbackLookup[a]={func:d,scope:b}),d.apply(b||c,Array.prototype.slice.call(arguments,1))},translate:function(a){if(a&&m.is(a,"string")){var b=this.settings.language||"en",c=this.editorManager.i18n;a=c.data[b+"."+a]||a.replace(/\{\#([^\}]+)\}/g,function(a,d){return c.data[b+"."+d]||"{#"+d+"}"})}return this.editorManager.translate(a)},getLang:function(a,b){return this.editorManager.i18n.data[(this.settings.language||"en")+"."+a]||(void 0!==b?b:"{#"+a+"}")},getParam:function(a,b,c){var d,e=a in this.settings?this.settings[a]:b;return"hash"===c?(d={},"string"==typeof e?s(e.indexOf("=")>0?e.split(/[;,](?![^=;,]*(?:[;,]|$))/):e.split(","),function(a){a=a.split("="),a.length>1?d[t(a[0])]=t(a[1]):d[t(a[0])]=t(a)}):d=e,d):e},nodeChanged:function(a){this._nodeChangeDispatcher.nodeChanged(a)},addButton:function(a,b){var c=this;b.cmd&&(b.onclick=function(){c.execCommand(b.cmd)}),b.text||b.icon||(b.icon=a),c.buttons=c.buttons||{},b.tooltip=b.tooltip||b.title,c.buttons[a]=b},addSidebar:function(a,b){return l.add(this,a,b)},addMenuItem:function(a,b){var c=this;b.cmd&&(b.onclick=function(){c.execCommand(b.cmd)}),c.menuItems=c.menuItems||{},c.menuItems[a]=b},addContextToolbar:function(a,b){var c,d=this;d.contextToolbars=d.contextToolbars||[],"string"==typeof a&&(c=a,a=function(a){return d.dom.is(a,c)}),d.contextToolbars.push({id:o.uuid("mcet"),predicate:a,items:b})},addCommand:function(a,b,c){this.editorCommands.addCommand(a,b,c)},addQueryStateHandler:function(a,b,c){this.editorCommands.addQueryStateHandler(a,b,c)},addQueryValueHandler:function(a,b,c){this.editorCommands.addQueryValueHandler(a,b,c)},addShortcut:function(a,b,c,d){this.shortcuts.add(a,b,c,d)},execCommand:function(a,b,c,d){return this.editorCommands.execCommand(a,b,c,d)},queryCommandState:function(a){return this.editorCommands.queryCommandState(a)},queryCommandValue:function(a){return this.editorCommands.queryCommandValue(a)},queryCommandSupported:function(a){return this.editorCommands.queryCommandSupported(a)},show:function(){var a=this;a.hidden&&(a.hidden=!1,a.inline?a.getBody().contentEditable=!0:(q.show(a.getContainer()),q.hide(a.id)),a.load(),a.fire("show"))},hide:function(){var a=this,b=a.getDoc();a.hidden||(w&&b&&!a.inline&&b.execCommand("SelectAll"),a.save(),a.inline?(a.getBody().contentEditable=!1,a==a.editorManager.focusedEditor&&(a.editorManager.focusedEditor=null)):(q.hide(a.getContainer()),q.setStyle(a.id,"display",a.orgDisplay)),a.hidden=!0,a.fire("hide"))},isHidden:function(){return!!this.hidden},setProgressState:function(a,b){this.fire("ProgressState",{state:a,time:b})},load:function(a){var b,c=this,d=c.getElement();return c.removed?"":d?(a=a||{},a.load=!0,b=c.setContent(void 0!==d.value?d.value:d.innerHTML,a),a.element=d,a.no_events||c.fire("LoadContent",a),a.element=d=null,b):void 0},save:function(a){var b,c,d=this,e=d.getElement();if(e&&d.initialized&&!d.removed)return a=a||{},a.save=!0,a.element=e,b=a.content=d.getContent(a),a.no_events||d.fire("SaveContent",a),"raw"==a.format&&d.fire("RawSaveContent",a),b=a.content,/TEXTAREA|INPUT/i.test(e.nodeName)?e.value=b:(d.inline||(e.innerHTML=b),(c=q.getParent(d.id,"form"))&&s(c.elements,function(a){if(a.name==d.id)return a.value=b,!1})),a.element=e=null,a.set_dirty!==!1&&d.setDirty(!1),b},setContent:function(a,b){var c,d,e=this,f=e.getBody();return b=b||{},b.format=b.format||"html",b.set=!0,b.content=a,b.no_events||e.fire("BeforeSetContent",b),a=b.content,0===a.length||/^\s+$/.test(a)?(d=w&&w<11?"":'<br data-mce-bogus="1">',"TABLE"==f.nodeName?a="<tr><td>"+d+"</td></tr>":/^(UL|OL)$/.test(f.nodeName)&&(a="<li>"+d+"</li>"),c=e.settings.forced_root_block,c&&e.schema.isValidChild(f.nodeName.toLowerCase(),c.toLowerCase())?(a=d,a=e.dom.createHTML(c,e.settings.forced_root_block_attrs,a)):w||a||(a='<br data-mce-bogus="1">'),e.dom.setHTML(f,a),e.fire("SetContent",b)):("raw"!==b.format&&(a=new h({validate:e.validate},e.schema).serialize(e.parser.parse(a,{isRootContent:!0}))),b.content=t(a),e.dom.setHTML(f,b.content),b.no_events||e.fire("SetContent",b)),b.content},getContent:function(a){var b,c=this,d=c.getBody();return c.removed?"":(a=a||{},a.format=a.format||"html",a.get=!0,a.getInner=!0,a.no_events||c.fire("BeforeGetContent",a),b="raw"==a.format?m.trim(c.serializer.getTrimmedContent()):"text"==a.format?d.innerText||d.textContent:c.serializer.serialize(d,a),"text"!=a.format?a.content=t(b):a.content=b,a.no_events||c.fire("GetContent",a),a.content)},insertContent:function(a,b){b&&(a=r({content:a},b)),this.execCommand("mceInsertContent",!1,a)},isDirty:function(){return!this.isNotDirty},setDirty:function(a){var b=!this.isNotDirty;this.isNotDirty=!a,a&&a!=b&&this.fire("dirty")},setMode:function(a){j.setMode(this,a)},getContainer:function(){var a=this;return a.container||(a.container=q.get(a.editorContainer||a.id+"_parent")),a.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return this.targetElm||(this.targetElm=q.get(this.id)),this.targetElm},getWin:function(){var a,b=this;return b.contentWindow||(a=b.iframeElement,a&&(b.contentWindow=a.contentWindow)),b.contentWindow},getDoc:function(){var a,b=this;return b.contentDocument||(a=b.getWin(),a&&(b.contentDocument=a.document)),b.contentDocument},getBody:function(){var a=this.getDoc();return this.bodyElement||(a?a.body:null)},convertURL:function(a,b,c){var d=this,e=d.settings;return e.urlconverter_callback?d.execCallback("urlconverter_callback",a,c,!0,b):!e.convert_urls||c&&"LINK"==c.nodeName||0===a.indexOf("file:")||0===a.length?a:e.relative_urls?d.documentBaseURI.toRelative(a):a=d.documentBaseURI.toAbsolute(a,e.remove_script_host)},addVisual:function(a){var b,c=this,d=c.settings,e=c.dom;a=a||c.getBody(),void 0===c.hasVisual&&(c.hasVisual=d.visual),s(e.select("table,a",a),function(a){var f;switch(a.nodeName){case"TABLE":return b=d.visual_table_class||"mce-item-table",f=e.getAttrib(a,"border"),void(f&&"0"!=f||!c.hasVisual?e.removeClass(a,b):e.addClass(a,b));case"A":return void(e.getAttrib(a,"href",!1)||(f=e.getAttrib(a,"name")||a.id,b=d.visual_anchor_class||"mce-item-anchor",f&&c.hasVisual?e.addClass(a,b):e.removeClass(a,b)))}}),c.fire("VisualAid",{element:a,hasVisual:c.hasVisual})},remove:function(){var a=this;a.removed||(a.save(),a.removed=1,a.unbindAllNativeEvents(),a.hasHiddenInput&&q.remove(a.getElement().nextSibling),a.inline||(w&&w<10&&a.getDoc().execCommand("SelectAll",!1,null),q.setStyle(a.id,"display",a.orgDisplay),a.getBody().onload=null),a.fire("remove"),a.editorManager.remove(a),q.remove(a.getContainer()),a._selectionOverrides.destroy(),a.editorUpload.destroy(),a.destroy())},destroy:function(a){var b,c=this;if(!c.destroyed){if(!a&&!c.removed)return void c.remove();a||(c.editorManager.off("beforeunload",c._beforeUnload),c.theme&&c.theme.destroy&&c.theme.destroy(),c.selection.destroy(),c.dom.destroy()),b=c.formElement,b&&(b._mceOldSubmit&&(b.submit=b._mceOldSubmit,b._mceOldSubmit=null),q.unbind(b,"submit reset",c.formEventDelegate)),c.contentAreaContainer=c.formElement=c.container=c.editorContainer=null,c.bodyElement=c.contentDocument=c.contentWindow=null,c.iframeElement=c.targetElm=null,c.selection&&(c.selection=c.selection.win=c.selection.dom=c.selection.dom.doc=null), +c.destroyed=1}},uploadImages:function(a){return this.editorUpload.uploadImages(a)},_scanForImages:function(){return this.editorUpload.scanForImages()}},r(p.prototype,e),p}),g("15",["9"],function(a){"use strict";var b={},c="en";return{setCode:function(a){a&&(c=a,this.rtl=!!this.data[a]&&"rtl"===this.data[a]._dir)},getCode:function(){return c},rtl:!1,add:function(a,c){var d=b[a];d||(b[a]=d={});for(var e in c)d[e]=c[e];this.setCode(a)},translate:function(d){function e(b){return a.is(b,"function")?Object.prototype.toString.call(b):f(b)?"":""+b}function f(b){return""===b||null===b||a.is(b,"undefined")}function g(b){return b=e(b),a.hasOwn(h,b)?e(h[b]):b}var h=b[c]||{};if(f(d))return"";if(a.is(d,"object")&&a.hasOwn(d,"raw"))return e(d.raw);if(a.is(d,"array")){var i=d.slice(1);d=g(d[0]).replace(/\{([0-9]+)\}/g,function(b,c){return a.hasOwn(i,c)?e(i[c]):b})}return g(d).replace(/{context:\w+}$/,"")},data:b}}),g("16",["e","5","6"],function(a,b,c){function d(a){function d(){try{return document.activeElement}catch(a){return document.body}}function j(a,b){if(b&&b.startContainer){if(!a.isChildOf(b.startContainer,a.getRoot())||!a.isChildOf(b.endContainer,a.getRoot()))return;return{startContainer:b.startContainer,startOffset:b.startOffset,endContainer:b.endContainer,endOffset:b.endOffset}}return b}function l(a,b){var c;return b.startContainer?(c=a.getDoc().createRange(),c.setStart(b.startContainer,b.startOffset),c.setEnd(b.endContainer,b.endOffset)):c=b,c}function m(m){var n=m.editor;n.on("init",function(){(n.inline||c.ie)&&("onbeforedeactivate"in document&&c.ie<9?n.dom.bind(n.getBody(),"beforedeactivate",function(a){if(a.target==n.getBody())try{n.lastRng=n.selection.getRng()}catch(a){}}):n.on("nodechange mouseup keyup",function(a){var b=d();"nodechange"==a.type&&a.selectionChange||(b&&b.id==n.id+"_ifr"&&(b=n.getBody()),n.dom.isChildOf(b,n.getBody())&&(n.lastRng=n.selection.getRng()))}),c.webkit&&!e&&(e=function(){var b=a.activeEditor;if(b&&b.selection){var c=b.selection.getRng();c&&!c.collapsed&&(n.lastRng=c)}},h.bind(document,"selectionchange",e)))}),n.on("setcontent",function(){n.lastRng=null}),n.on("mousedown",function(){n.selection.lastFocusBookmark=null}),n.on("focusin",function(){var b,c=a.focusedEditor;n.selection.lastFocusBookmark&&(b=l(n,n.selection.lastFocusBookmark),n.selection.lastFocusBookmark=null,n.selection.setRng(b)),c!=n&&(c&&c.fire("blur",{focusedEditor:n}),a.setActive(n),a.focusedEditor=n,n.fire("focus",{blurredEditor:c}),n.focus(!0)),n.lastRng=null}),n.on("focusout",function(){b.setEditorTimeout(n,function(){var b=a.focusedEditor;i(n,d())||b!=n||(n.fire("blur",{focusedEditor:null}),a.focusedEditor=null,n.selection&&(n.selection.lastFocusBookmark=null))})}),f||(f=function(b){var c,d=a.activeEditor;c=b.target,d&&c.ownerDocument===document&&(d.selection&&c!==d.getBody()&&k(n,c)&&(d.selection.lastFocusBookmark=j(d.dom,d.lastRng)),c===document.body||i(d,c)||a.focusedEditor!==d||(d.fire("blur",{focusedEditor:null}),a.focusedEditor=null))},h.bind(document,"focusin",f)),n.inline&&!g&&(g=function(b){var c=a.activeEditor,d=c.dom;if(c.inline&&d&&!d.isChildOf(b.target,c.getBody())){var e=c.selection.getRng();e.collapsed||(c.lastRng=e)}},h.bind(document,"mouseup",g))}function n(b){a.focusedEditor==b.editor&&(a.focusedEditor=null),a.activeEditor||(h.unbind(document,"selectionchange",e),h.unbind(document,"focusin",f),h.unbind(document,"mouseup",g),e=f=g=null)}a.on("AddEditor",m),a.on("RemoveEditor",n)}var e,f,g,h=a.DOM,i=function(a,b){var c=a?a.settings.custom_ui_selector:"",e=h.getParent(b,function(b){return d.isEditorUIElement(b)||!!c&&a.dom.is(b,c)});return null!==e},j=function(a){return a.inline===!0},k=function(a,b){return j(a)===!1||a.dom.isChildOf(b,a.getBody())===!1};return d.isEditorUIElement=function(a){return a.className.toString().indexOf("mce-")!==-1},d._isUIElement=i,d}),g("2c",["9"],function(a){var b=a.each,c=a.explode,d=function(a){a.on("AddEditor",function(a){var d=a.editor;d.on("preInit",function(){function a(a,c){b(c,function(b,c){b&&h.setStyle(a,c,b)}),h.rename(a,"span")}function e(a){h=d.dom,i.convert_fonts_to_spans&&b(h.select("font,u,strike",a.node),function(a){f[a.nodeName.toLowerCase()](h,a)})}var f,g,h,i=d.settings;i.inline_styles&&(g=c(i.font_size_legacy_values),f={font:function(b,c){a(c,{backgroundColor:c.style.backgroundColor,color:c.color,fontFamily:c.face,fontSize:g[parseInt(c.size,10)-1]})},u:function(b,c){"html4"===d.settings.schema&&a(c,{textDecoration:"underline"})},strike:function(b,c){a(c,{textDecoration:"line-through"})}},d.on("PreProcess SetContent",e))})})};return{register:d}}),g("17",["g","a","e","14","6","2b","16","2c","15","z","4","9","w"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){function n(a){v(s.editors,function(b){"scroll"===a.type?b.fire("ScrollWindow",a):b.fire("ResizeWindow",a)})}function o(a,c){c!==y&&(c?b(window).on("resize scroll",n):b(window).off("resize scroll",n),y=c)}function p(a){var b,c=s.editors;delete c[a.id];for(var d=0;d<c.length;d++)if(c[d]==a){c.splice(d,1),b=!0;break}return s.activeEditor==a&&(s.activeEditor=c[0]),s.focusedEditor==a&&(s.focusedEditor=null),b}function q(a){return a&&a.initialized&&!(a.getContainer()||a.getBody()).parentNode&&(p(a),a.unbindAllNativeEvents(),a.destroy(!0),a.removed=!0,a=null),a}var r,s,t=c.DOM,u=l.explode,v=l.each,w=l.extend,x=0,y=!1;return s={$:b,majorVersion:"4",minorVersion:"6.4",releaseDate:"2017-06-13",editors:[],i18n:i,activeEditor:null,setup:function(){var a,b,c,d,e=this,f="";if(b=m.getDocumentBaseUrl(document.location),/^[^:]+:\/\/\/?[^\/]+\//.test(b)&&(b=b.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,""),/[\/\\]$/.test(b)||(b+="/")),c=window.tinymce||window.tinyMCEPreInit)a=c.base||c.baseURL,f=c.suffix;else{for(var h=document.getElementsByTagName("script"),i=0;i<h.length;i++){d=h[i].src;var j=d.substring(d.lastIndexOf("/"));if(/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(d)){j.indexOf(".min")!=-1&&(f=".min"),a=d.substring(0,d.lastIndexOf("/"));break}}!a&&document.currentScript&&(d=document.currentScript.src,d.indexOf(".min")!=-1&&(f=".min"),a=d.substring(0,d.lastIndexOf("/")))}e.baseURL=new m(b).toAbsolute(a),e.documentBaseURL=b,e.baseURI=new m(e.baseURL),e.suffix=f,e.focusManager=new g(e)},overrideDefaults:function(b){var c,d;c=b.base_url,c&&(this.baseURL=new m(this.documentBaseURL).toAbsolute(c.replace(/\/+$/,"")),this.baseURI=new m(this.baseURL)),d=b.suffix,b.suffix&&(this.suffix=d),this.defaultSettings=b;var e=b.plugin_base_urls;for(var f in e)a.PluginManager.urls[f]=e[f]},init:function(a){function c(a,b){return a.inline&&b.tagName.toLowerCase()in o}function g(a){var b=a.id;return b||(b=a.name,b=b&&!t.get(b)?a.name:t.uniqueId(),a.setAttribute("id",b)),b}function h(b){var c=a[b];if(c)return c.apply(p,Array.prototype.slice.call(arguments,2))}function i(a,b){return b.constructor===RegExp?b.test(a.className):t.hasClass(a,b)}function j(a){var b,c=[];if(e.ie&&e.ie<11)return f.initError("TinyMCE does not support the browser you are using. For a list of supported browsers please see: https://www.tinymce.com/docs/get-started/system-requirements/"),[];if(a.types)return v(a.types,function(a){c=c.concat(t.select(a.selector))}),c;if(a.selector)return t.select(a.selector);if(a.target)return[a.target];switch(a.mode){case"exact":b=a.elements||"",b.length>0&&v(u(b),function(a){var b;(b=t.get(a))?c.push(b):v(document.forms,function(b){v(b.elements,function(b){b.name===a&&(a="mce_editor_"+x++,t.setAttrib(b,"id",a),c.push(b))})})});break;case"textareas":case"specific_textareas":v(t.select("textarea"),function(b){a.editor_deselector&&i(b,a.editor_deselector)||a.editor_selector&&!i(b,a.editor_selector)||c.push(b)})}return c}function m(){function e(a,b,c){var e=new d(a,b,p);n.push(e),e.on("init",function(){++k===i.length&&r(n)}),e.targetElm=e.targetElm||c,e.render()}var i,k=0,n=[];return t.unbind(window,"ready",m),h("onpageload"),i=b.unique(j(a)),a.types?void v(a.types,function(b){l.each(i,function(c){return!t.is(c,b.selector)||(e(g(c),w({},a,b),c),!1)})}):(l.each(i,function(a){q(p.get(a.id))}),i=l.grep(i,function(a){return!p.get(a.id)}),void(0===i.length?r([]):v(i,function(b){c(a,b)?f.initError("Could not initialize inline editor on invalid inline target element",b):e(g(b),a,b)})))}var n,o,p=this;o=l.makeMap("area base basefont br col frame hr img input isindex link meta param embed source wbr track colgroup option tbody tfoot thead tr script noscript style textarea video audio iframe object menu"," ");var r=function(a){n=a};return p.settings=a,t.bind(window,"ready",m),new k(function(a){n?a(n):r=function(b){a(b)}})},get:function(a){return arguments.length?a in this.editors?this.editors[a]:null:this.editors},add:function(a){var b=this,c=b.editors;return c[a.id]=a,c.push(a),o(c,!0),b.activeEditor=a,b.fire("AddEditor",{editor:a}),r||(r=function(){b.fire("BeforeUnload")},t.bind(window,"beforeunload",r)),a},createEditor:function(a,b){return this.add(new d(a,b,this))},remove:function(a){var b,c,d=this,e=d.editors;{if(a)return"string"==typeof a?(a=a.selector||a,void v(t.select(a),function(a){c=e[a.id],c&&d.remove(c)})):(c=a,e[c.id]?(p(c)&&d.fire("RemoveEditor",{editor:c}),e.length||t.unbind(window,"beforeunload",r),c.remove(),o(e,e.length>0),c):null);for(b=e.length-1;b>=0;b--)d.remove(e[b])}},execCommand:function(a,b,c){var e=this,f=e.get(c);switch(a){case"mceAddEditor":return e.get(c)||new d(c,e.settings,e).render(),!0;case"mceRemoveEditor":return f&&f.remove(),!0;case"mceToggleEditor":return f?(f.isHidden()?f.show():f.hide(),!0):(e.execCommand("mceAddEditor",0,c),!0)}return!!e.activeEditor&&e.activeEditor.execCommand(a,b,c)},triggerSave:function(){v(this.editors,function(a){a.save()})},addI18n:function(a,b){i.add(a,b)},translate:function(a){return i.translate(a)},setActive:function(a){var b=this.activeEditor;this.activeEditor!=a&&(b&&b.fire("deactivate",{relatedTarget:a}),a.fire("activate",{relatedTarget:b})),this.activeEditor=a}},w(s,j),s.setup(),h.register(s),s}),g("18",["z","9"],function(a,b){var c={send:function(a){function d(){!a.async||4==e.readyState||f++>1e4?(a.success&&f<1e4&&200==e.status?a.success.call(a.success_scope,""+e.responseText,e,a):a.error&&a.error.call(a.error_scope,f>1e4?"TIMED_OUT":"GENERAL",e,a),e=null):setTimeout(d,10)}var e,f=0;if(a.scope=a.scope||this,a.success_scope=a.success_scope||a.scope,a.error_scope=a.error_scope||a.scope,a.async=a.async!==!1,a.data=a.data||"",c.fire("beforeInitialize",{settings:a}),e=new XMLHttpRequest){if(e.overrideMimeType&&e.overrideMimeType(a.content_type),e.open(a.type||(a.data?"POST":"GET"),a.url,a.async),a.crossDomain&&(e.withCredentials=!0),a.content_type&&e.setRequestHeader("Content-Type",a.content_type),a.requestheaders&&b.each(a.requestheaders,function(a){e.setRequestHeader(a.key,a.value)}),e.setRequestHeader("X-Requested-With","XMLHttpRequest"),e=c.fire("beforeSend",{xhr:e,settings:a}).xhr,e.send(a.data),!a.async)return d();setTimeout(d,10)}}};return b.extend(c,a),c}),g("19",[],function(){function a(b,c){var d,e,f,g;if(c=c||'"',null===b)return"null";if(f=typeof b,"string"==f)return e="\bb\tt\nn\ff\rr\"\"''\\\\",c+b.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(a,b){return'"'===c&&"'"===a?a:(d=e.indexOf(b),d+1?"\\"+e.charAt(d+1):(a=b.charCodeAt().toString(16),"\\u"+"0000".substring(a.length)+a))})+c;if("object"==f){if(b.hasOwnProperty&&"[object Array]"===Object.prototype.toString.call(b)){for(d=0,e="[";d<b.length;d++)e+=(d>0?",":"")+a(b[d],c);return e+"]"}e="{";for(g in b)b.hasOwnProperty(g)&&(e+="function"!=typeof b[g]?(e.length>1?","+c:c)+g+c+":"+a(b[g],c):"");return e+"}"}return""+b}return{serialize:a,parse:function(a){try{return window[String.fromCharCode(101)+"val"]("("+a+")")}catch(a){}}}}),g("1a",["19","18","9"],function(a,b,c){function d(a){this.settings=e({},a),this.count=0}var e=c.extend;return d.sendRPC=function(a){return(new d).send(a)},d.prototype={send:function(c){var d=c.error,f=c.success;c=e(this.settings,c),c.success=function(b,e){b=a.parse(b),"undefined"==typeof b&&(b={error:"JSON Parse error."}),b.error?d.call(c.error_scope||c.scope,b.error,e):f.call(c.success_scope||c.scope,b.result)},c.error=function(a,b){d&&d.call(c.error_scope||c.scope,a,b)},c.data=a.serialize({id:c.id||"c"+this.count++,method:c.method,params:c.params}),c.content_type="application/json",b.send(c)}},d}),g("1b",["e"],function(a){return{callbacks:{},count:0,send:function(b){var c=this,d=a.DOM,e=void 0!==b.count?b.count:c.count,f="tinymce_jsonp_"+e;c.callbacks[e]=function(a){d.remove(f),delete c.callbacks[e],b.callback(a)},d.add(d.doc.body,"script",{id:f,src:b.url,type:"text/javascript"}),c.count++}}}),g("1c",[],function(){function a(){g=[];for(var a in f)g.push(a);d.length=g.length}function b(){function b(a){var b,c;return c=void 0!==a?j+a:d.indexOf(",",j),c===-1||c>d.length?null:(b=d.substring(j,c),j=c+1,b)}var c,d,g,j=0;if(f={},i){e.load(h),d=e.getAttribute(h)||"";do{var k=b();if(null===k)break;if(c=b(parseInt(k,32)||0),null!==c){if(k=b(),null===k)break;g=b(parseInt(k,32)||0),c&&(f[c]=g)}}while(null!==c);a()}}function c(){var b,c="";if(i){for(var d in f)b=f[d],c+=(c?",":"")+d.length.toString(32)+","+d+","+b.length.toString(32)+","+b;e.setAttribute(h,c);try{e.save(h)}catch(a){}a()}}var d,e,f,g,h,i;try{if(window.localStorage)return localStorage}catch(a){}return h="tinymce",e=document.documentElement,i=!!e.addBehavior,i&&e.addBehavior("#default#userData"),d={key:function(a){return g[a]},getItem:function(a){return a in f?f[a]:null},setItem:function(a,b){f[a]=""+b,c()},removeItem:function(a){delete f[a],c()},clear:function(){f={},c()}},b(),d}),g("1d",["e","7","f","g","9","6"],function(a,b,c,d,e,f){var g=function(g){g.DOM=a.DOM,g.ScriptLoader=c.ScriptLoader,g.PluginManager=d.PluginManager,g.ThemeManager=d.ThemeManager,g.dom=g.dom||{},g.dom.Event=b.Event,e.each("trim isArray is toArray makeMap each map grep inArray extend create walk createNS resolve explode _addCacheSuffix".split(" "),function(a){g[a]=e[a]}),e.each("isOpera isWebKit isIE isGecko isMac".split(" "),function(a){g[a]=f[a.substr(2).toLowerCase()]})};return{register:g}}),g("1e",[],function(){function a(a){function e(a,e,f){var g,h,i,j,k,l;return g=0,h=0,i=0,a/=255,e/=255,f/=255,k=b(a,b(e,f)),l=c(a,c(e,f)),k==l?(i=k,{h:0,s:0,v:100*i}):(j=a==k?e-f:f==k?a-e:f-a,g=a==k?3:f==k?1:5,g=60*(g-j/(l-k)),h=(l-k)/l,i=l,{h:d(g),s:d(100*h),v:d(100*i)})}function f(a,e,f){var g,h,i,j;if(a=(parseInt(a,10)||0)%360,e=parseInt(e,10)/100,f=parseInt(f,10)/100,e=c(0,b(e,1)),f=c(0,b(f,1)),0===e)return void(l=m=n=d(255*f));switch(g=a/60,h=f*e,i=h*(1-Math.abs(g%2-1)),j=f-h,Math.floor(g)){case 0:l=h,m=i,n=0;break;case 1:l=i,m=h,n=0;break;case 2:l=0,m=h,n=i;break;case 3:l=0,m=i,n=h;break;case 4:l=i,m=0,n=h;break;case 5:l=h,m=0,n=i;break;default:l=m=n=0}l=d(255*(l+j)),m=d(255*(m+j)),n=d(255*(n+j))}function g(){function a(a){return a=parseInt(a,10).toString(16),a.length>1?a:"0"+a}return"#"+a(l)+a(m)+a(n)}function h(){return{r:l,g:m,b:n}}function i(){return e(l,m,n)}function j(a){var b;return"object"==typeof a?"r"in a?(l=a.r,m=a.g,n=a.b):"v"in a&&f(a.h,a.s,a.v):(b=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)[^\)]*\)/gi.exec(a))?(l=parseInt(b[1],10),m=parseInt(b[2],10),n=parseInt(b[3],10)):(b=/#([0-F]{2})([0-F]{2})([0-F]{2})/gi.exec(a))?(l=parseInt(b[1],16),m=parseInt(b[2],16),n=parseInt(b[3],16)):(b=/#([0-F])([0-F])([0-F])/gi.exec(a))&&(l=parseInt(b[1]+b[1],16),m=parseInt(b[2]+b[2],16),n=parseInt(b[3]+b[3],16)),l=l<0?0:l>255?255:l,m=m<0?0:m>255?255:m,n=n<0?0:n>255?255:n,k}var k=this,l=0,m=0,n=0;a&&j(a),k.toRgb=h,k.toHsv=i,k.toHex=g,k.parse=j}var b=Math.min,c=Math.max,d=Math.round;return a}),g("2t",["x","9"],function(a,b){"use strict";return a.extend({Defaults:{firstControlClass:"first",lastControlClass:"last"},init:function(a){this.settings=b.extend({},this.Defaults,a)},preRender:function(a){a.bodyClasses.add(this.settings.containerClass)},applyClasses:function(a){var b,c,d,e,f=this,g=f.settings;b=g.firstControlClass,c=g.lastControlClass,a.each(function(a){a.classes.remove(b).remove(c).add(g.controlClass),a.visible()&&(d||(d=a),e=a)}),d&&d.classes.add(b),e&&e.classes.add(c)},renderHtml:function(a){var b=this,c="";return b.applyClasses(a.items()),a.items().each(function(a){c+=a.renderHtml()}),c},recalc:function(){},postRender:function(){},isNative:function(){return!1}})}),g("2u",["2t"],function(a){"use strict";return a.extend({Defaults:{containerClass:"abs-layout",controlClass:"abs-layout-item"},recalc:function(a){a.items().filter(":visible").each(function(a){var b=a.settings;a.layoutRect({x:b.x,y:b.y,w:b.w,h:b.h}),a.recalc&&a.recalc()})},renderHtml:function(a){return'<div id="'+a._id+'-absend" class="'+a.classPrefix+'abs-end"></div>'+this._super(a)}})}),g("2v",["2r"],function(a){"use strict";return a.extend({Defaults:{classes:"widget btn",role:"button"},init:function(a){var b,c=this;c._super(a),a=c.settings,b=c.settings.size,c.on("click mousedown",function(a){a.preventDefault()}),c.on("touchstart",function(a){c.fire("click",a),a.preventDefault()}),a.subtype&&c.classes.add(a.subtype),b&&c.classes.add("btn-"+b),a.icon&&c.icon(a.icon)},icon:function(a){return arguments.length?(this.state.set("icon",a),this):this.state.get("icon")},repaint:function(){var a,b=this.getEl().firstChild;b&&(a=b.style,a.width=a.height="100%"),this._super()},renderHtml:function(){var a,b=this,c=b._id,d=b.classPrefix,e=b.state.get("icon"),f=b.state.get("text"),g="";return a=b.settings.image,a?(e="none","string"!=typeof a&&(a=window.getSelection?a[0]:a[1]),a=" style=\"background-image: url('"+a+"')\""):a="",f&&(b.classes.add("btn-has-text"),g='<span class="'+d+'txt">'+b.encode(f)+"</span>"),e=e?d+"ico "+d+"i-"+e:"",'<div id="'+c+'" class="'+b.classes+'" tabindex="-1"><button role="presentation" type="button" tabindex="-1">'+(e?'<i class="'+e+'"'+a+"></i>":"")+g+"</button></div>"},bindStates:function(){function a(a){var e=c("span."+d,b.getEl());a?(e[0]||(c("button:first",b.getEl()).append('<span class="'+d+'"></span>'),e=c("span."+d,b.getEl())),e.html(b.encode(a))):e.remove(),b.classes.toggle("btn-has-text",!!a)}var b=this,c=b.$,d=b.classPrefix+"txt";return b.state.on("change:text",function(b){a(b.value)}),b.state.on("change:icon",function(c){var d=c.value,e=b.classPrefix;b.settings.icon=d,d=d?e+"ico "+e+"i-"+b.settings.icon:"";var f=b.getEl().firstChild,g=f.getElementsByTagName("i")[0];d?(g&&g==f.firstChild||(g=document.createElement("i"),f.insertBefore(g,f.firstChild)),g.className=d):g&&f.removeChild(g),a(b.state.get("text"))}),b._super()}})}),g("2w",["2j"],function(a){"use strict";return a.extend({Defaults:{defaultType:"button",role:"group"},renderHtml:function(){var a=this,b=a._layout;return a.classes.add("btn-group"),a.preRender(),b.preRender(a),'<div id="'+a._id+'" class="'+a.classes+'"><div id="'+a._id+'-body">'+(a.settings.html||"")+b.renderHtml(a)+"</div></div>"}})}),g("2x",["2r"],function(a){"use strict";return a.extend({Defaults:{classes:"checkbox",role:"checkbox",checked:!1},init:function(a){var b=this;b._super(a),b.on("click mousedown",function(a){a.preventDefault()}),b.on("click",function(a){a.preventDefault(),b.disabled()||b.checked(!b.checked())}),b.checked(b.settings.checked)},checked:function(a){return arguments.length?(this.state.set("checked",a),this):this.state.get("checked")},value:function(a){return arguments.length?this.checked(a):this.checked()},renderHtml:function(){var a=this,b=a._id,c=a.classPrefix;return'<div id="'+b+'" class="'+a.classes+'" unselectable="on" aria-labelledby="'+b+'-al" tabindex="-1"><i class="'+c+"ico "+c+'i-checkbox"></i><span id="'+b+'-al" class="'+c+'label">'+a.encode(a.state.get("text"))+"</span></div>"},bindStates:function(){function a(a){b.classes.toggle("checked",a),b.aria("checked",a)}var b=this;return b.state.on("change:text",function(a){b.getEl("al").firstChild.data=b.translate(a.value)}),b.state.on("change:checked change:value",function(c){b.fire("change"),a(c.value)}),b.state.on("change:icon",function(a){var c=a.value,d=b.classPrefix;if("undefined"==typeof c)return b.settings.icon;b.settings.icon=c,c=c?d+"ico "+d+"i-"+b.settings.icon:"";var e=b.getEl().firstChild,f=e.getElementsByTagName("i")[0];c?(f&&f==e.firstChild||(f=document.createElement("i"),e.insertBefore(f,e.firstChild)),f.className=c):f&&e.removeChild(f)}),b.state.get("checked")&&a(!0),b._super()}})}),g("2y",["2r","2h","4q","a","p","9"],function(a,b,c,d,e,f){"use strict";return a.extend({init:function(a){var b=this;b._super(a),a=b.settings,b.classes.add("combobox"),b.subinput=!0,b.ariaTarget="inp",a.menu=a.menu||a.values,a.menu&&(a.icon="caret"),b.on("click",function(c){var e=c.target,f=b.getEl();if(d.contains(f,e)||e==f)for(;e&&e!=f;)e.id&&e.id.indexOf("-open")!=-1&&(b.fire("action"),a.menu&&(b.showMenu(),c.aria&&b.menu.items()[0].focus())),e=e.parentNode}),b.on("keydown",function(a){var c;13==a.keyCode&&"INPUT"===a.target.nodeName&&(a.preventDefault(),b.parents().reverse().each(function(a){if(a.toJSON)return c=a,!1}),b.fire("submit",{data:c.toJSON()}))}),b.on("keyup",function(a){if("INPUT"==a.target.nodeName){var c=b.state.get("value"),d=a.target.value;d!==c&&(b.state.set("value",d),b.fire("autocomplete",a))}}),b.on("mouseover",function(a){var c=b.tooltip().moveTo(-65535);if(b.statusLevel()&&a.target.className.indexOf(b.classPrefix+"status")!==-1){var d=b.statusMessage()||"Ok",e=c.text(d).show().testMoveRel(a.target,["bc-tc","bc-tl","bc-tr"]);c.classes.toggle("tooltip-n","bc-tc"==e),c.classes.toggle("tooltip-nw","bc-tl"==e),c.classes.toggle("tooltip-ne","bc-tr"==e),c.moveRel(a.target,e)}})},statusLevel:function(a){return arguments.length>0&&this.state.set("statusLevel",a),this.state.get("statusLevel")},statusMessage:function(a){return arguments.length>0&&this.state.set("statusMessage",a),this.state.get("statusMessage")},showMenu:function(){var a,c=this,d=c.settings;c.menu||(a=d.menu||[],a.length?a={type:"menu",items:a}:a.type=a.type||"menu",c.menu=b.create(a).parent(c).renderTo(c.getContainerElm()),c.fire("createmenu"),c.menu.reflow(),c.menu.on("cancel",function(a){a.control===c.menu&&c.focus()}),c.menu.on("show hide",function(a){a.control.items().each(function(a){a.active(a.value()==c.value())})}).fire("show"),c.menu.on("select",function(a){c.value(a.control.value())}),c.on("focusin",function(a){"INPUT"==a.target.tagName.toUpperCase()&&c.menu.hide()}),c.aria("expanded",!0)),c.menu.show(),c.menu.layoutRect({w:c.layoutRect().w}),c.menu.moveRel(c.getEl(),c.isRtl()?["br-tr","tr-br"]:["bl-tl","tl-bl"])},focus:function(){this.getEl("inp").focus()},repaint:function(){var a,b,e=this,f=e.getEl(),g=e.getEl("open"),h=e.layoutRect(),i=0,j=f.firstChild;e.statusLevel()&&"none"!==e.statusLevel()&&(i=parseInt(c.getRuntimeStyle(j,"padding-right"),10)-parseInt(c.getRuntimeStyle(j,"padding-left"),10)),a=g?h.w-c.getSize(g).width-10:h.w-10;var k=document;return k.all&&(!k.documentMode||k.documentMode<=8)&&(b=e.layoutRect().h-2+"px"),d(j).css({width:a-i,lineHeight:b}),e._super(),e},postRender:function(){var a=this;return d(this.getEl("inp")).on("change",function(b){a.state.set("value",b.target.value),a.fire("change",b)}),a._super()},renderHtml:function(){var a,b,c=this,d=c._id,e=c.settings,f=c.classPrefix,g=c.state.get("value")||"",h="",i="",j="";return"spellcheck"in e&&(i+=' spellcheck="'+e.spellcheck+'"'),e.maxLength&&(i+=' maxlength="'+e.maxLength+'"'),e.size&&(i+=' size="'+e.size+'"'),e.subtype&&(i+=' type="'+e.subtype+'"'),j='<i id="'+d+'-status" class="mce-status mce-ico" style="display: none"></i>',c.disabled()&&(i+=' disabled="disabled"'),a=e.icon,a&&"caret"!=a&&(a=f+"ico "+f+"i-"+e.icon),b=c.state.get("text"),(a||b)&&(h='<div id="'+d+'-open" class="'+f+"btn "+f+'open" tabIndex="-1" role="button"><button id="'+d+'-action" type="button" hidefocus="1" tabindex="-1">'+("caret"!=a?'<i class="'+a+'"></i>':'<i class="'+f+'caret"></i>')+(b?(a?" ":"")+b:"")+"</button></div>",c.classes.add("has-open")),'<div id="'+d+'" class="'+c.classes+'"><input id="'+d+'-inp" class="'+f+'textbox" value="'+c.encode(g,!1)+'" hidefocus="1"'+i+' placeholder="'+c.encode(e.placeholder)+'" />'+j+h+"</div>"},value:function(a){return arguments.length?(this.state.set("value",a),this):(this.state.get("rendered")&&this.state.set("value",this.getEl("inp").value),this.state.get("value"))},showAutoComplete:function(a,c){var d=this;if(0===a.length)return void d.hideMenu();var e=function(a,b){return function(){d.fire("selectitem",{title:b,value:a})}};d.menu?d.menu.items().remove():d.menu=b.create({type:"menu",classes:"combobox-menu",layout:"flow"}).parent(d).renderTo(),f.each(a,function(a){d.menu.add({text:a.title,url:a.previewUrl,match:c,classes:"menu-item-ellipsis",onclick:e(a.value,a.title)})}),d.menu.renderNew(),d.hideMenu(),d.menu.on("cancel",function(a){a.control.parent()===d.menu&&(a.stopPropagation(),d.focus(),d.hideMenu())}),d.menu.on("select",function(){d.focus()});var g=d.layoutRect().w;d.menu.layoutRect({w:g,minW:0,maxW:g}),d.menu.reflow(),d.menu.show(),d.menu.moveRel(d.getEl(),d.isRtl()?["br-tr","tr-br"]:["bl-tl","tl-bl"])},hideMenu:function(){this.menu&&this.menu.hide()},bindStates:function(){var a=this;a.state.on("change:value",function(b){a.getEl("inp").value!=b.value&&(a.getEl("inp").value=b.value)}),a.state.on("change:disabled",function(b){a.getEl("inp").disabled=b.value}),a.state.on("change:statusLevel",function(b){var d=a.getEl("status"),e=a.classPrefix,f=b.value;c.css(d,"display","none"===f?"none":""),c.toggleClass(d,e+"i-checkmark","ok"===f),c.toggleClass(d,e+"i-warning","warn"===f),c.toggleClass(d,e+"i-error","error"===f),a.classes.toggle("has-status","none"!==f),a.repaint()}),c.on(a.getEl("status"),"mouseleave",function(){a.tooltip().hide()}),a.on("cancel",function(b){a.menu&&a.menu.visible()&&(b.stopPropagation(),a.hideMenu())});var b=function(a,b){b&&b.items().length>0&&b.items().eq(a)[0].focus()};return a.on("keydown",function(c){var d=c.keyCode;"INPUT"===c.target.nodeName&&(d===e.DOWN?(c.preventDefault(),a.fire("autocomplete"),b(0,a.menu)):d===e.UP&&(c.preventDefault(),b(-1,a.menu)))}),a._super()},remove:function(){d(this.getEl("inp")).off(),this.menu&&this.menu.remove(),this._super()}})}),g("2z",["2y"],function(a){"use strict";return a.extend({init:function(a){var b=this;a.spellcheck=!1,a.onaction&&(a.icon="none"),b._super(a),b.classes.add("colorbox"),b.on("change keyup postrender",function(){b.repaintColor(b.value())})},repaintColor:function(a){var b=this.getEl("open"),c=b?b.getElementsByTagName("i")[0]:null;if(c)try{c.style.background=a}catch(a){}},bindStates:function(){var a=this;return a.state.on("change:value",function(b){a.state.get("rendered")&&a.repaintColor(b.value)}),a._super()}})}),g("30",["2v","2p"],function(a,b){"use strict";return a.extend({showPanel:function(){var a=this,c=a.settings;if(a.active(!0),a.panel)a.panel.show();else{var d=c.panel;d.type&&(d={layout:"grid",items:d}),d.role=d.role||"dialog",d.popover=!0,d.autohide=!0,d.ariaRoot=!0,a.panel=new b(d).on("hide",function(){a.active(!1)}).on("cancel",function(b){b.stopPropagation(),a.focus(),a.hidePanel()}).parent(a).renderTo(a.getContainerElm()),a.panel.fire("show"),a.panel.reflow()}var e=a.panel.testMoveRel(a.getEl(),c.popoverAlign||(a.isRtl()?["bc-tc","bc-tl","bc-tr"]:["bc-tc","bc-tr","bc-tl"]));a.panel.classes.toggle("start","bc-tl"===e),a.panel.classes.toggle("end","bc-tr"===e),a.panel.moveRel(a.getEl(),e)},hidePanel:function(){var a=this;a.panel&&a.panel.hide()},postRender:function(){var a=this;return a.aria("haspopup",!0),a.on("click",function(b){b.control===a&&(a.panel&&a.panel.visible()?a.hidePanel():(a.showPanel(),a.panel.focus(!!b.aria)))}),a._super()},remove:function(){return this.panel&&(this.panel.remove(),this.panel=null),this._super()}})}),g("31",["30","e"],function(a,b){"use strict";var c=b.DOM;return a.extend({init:function(a){this._super(a),this.classes.add("colorbutton")},color:function(a){return a?(this._color=a,this.getEl("preview").style.backgroundColor=a,this):this._color},resetColor:function(){return this._color=null,this.getEl("preview").style.backgroundColor=null,this},renderHtml:function(){var a=this,b=a._id,c=a.classPrefix,d=a.state.get("text"),e=a.settings.icon?c+"ico "+c+"i-"+a.settings.icon:"",f=a.settings.image?" style=\"background-image: url('"+a.settings.image+"')\"":"",g="";return d&&(a.classes.add("btn-has-text"),g='<span class="'+c+'txt">'+a.encode(d)+"</span>"),'<div id="'+b+'" class="'+a.classes+'" role="button" tabindex="-1" aria-haspopup="true"><button role="presentation" hidefocus="1" type="button" tabindex="-1">'+(e?'<i class="'+e+'"'+f+"></i>":"")+'<span id="'+b+'-preview" class="'+c+'preview"></span>'+g+'</button><button type="button" class="'+c+'open" hidefocus="1" tabindex="-1"> <i class="'+c+'caret"></i></button></div>'},postRender:function(){var a=this,b=a.settings.onclick;return a.on("click",function(d){d.aria&&"down"==d.aria.key||d.control!=a||c.getParent(d.target,"."+a.classPrefix+"open")||(d.stopImmediatePropagation(),b.call(a,d))}),delete a.settings.onclick,a._super()}})}),g("32",["2r","2k","4q","1e"],function(a,b,c,d){"use strict";return a.extend({Defaults:{classes:"widget colorpicker"},init:function(a){this._super(a)},postRender:function(){function a(a,b){var d,e,f=c.getPos(a);return d=b.pageX-f.x,e=b.pageY-f.y,d=Math.max(0,Math.min(d/a.clientWidth,1)),e=Math.max(0,Math.min(e/a.clientHeight,1)),{x:d,y:e}}function e(a,b){var e=(360-a.h)/360;c.css(j,{top:100*e+"%"}),b||c.css(l,{left:a.s+"%",top:100-a.v+"%"}),k.style.background=new d({s:100,v:100,h:a.h}).toHex(),m.color().parse({s:a.s,v:a.v,h:a.h})}function f(b){var c;c=a(k,b),h.s=100*c.x,h.v=100*(1-c.y),e(h),m.fire("change")}function g(b){var c;c=a(i,b),h=n.toHsv(),h.h=360*(1-c.y),e(h,!0),m.fire("change")}var h,i,j,k,l,m=this,n=m.color();i=m.getEl("h"),j=m.getEl("hp"),k=m.getEl("sv"),l=m.getEl("svp"),m._repaint=function(){h=n.toHsv(),e(h)},m._super(),m._svdraghelper=new b(m._id+"-sv",{start:f,drag:f}),m._hdraghelper=new b(m._id+"-h",{start:g,drag:g}),m._repaint()},rgb:function(){return this.color().toRgb()},value:function(a){var b=this;return arguments.length?(b.color().parse(a),void(b._rendered&&b._repaint())):b.color().toHex()},color:function(){return this._color||(this._color=new d),this._color},renderHtml:function(){function a(){var a,b,c,d,g="";for(c="filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=",d=f.split(","),a=0,b=d.length-1;a<b;a++)g+='<div class="'+e+'colorpicker-h-chunk" style="height:'+100/b+"%;"+c+d[a]+",endColorstr="+d[a+1]+");-ms-"+c+d[a]+",endColorstr="+d[a+1]+')"></div>';return g}var b,c=this,d=c._id,e=c.classPrefix,f="#ff0000,#ff0080,#ff00ff,#8000ff,#0000ff,#0080ff,#00ffff,#00ff80,#00ff00,#80ff00,#ffff00,#ff8000,#ff0000",g="background: -ms-linear-gradient(top,"+f+");background: linear-gradient(to bottom,"+f+");";return b='<div id="'+d+'-h" class="'+e+'colorpicker-h" style="'+g+'">'+a()+'<div id="'+d+'-hp" class="'+e+'colorpicker-h-marker"></div></div>','<div id="'+d+'" class="'+c.classes+'"><div id="'+d+'-sv" class="'+e+'colorpicker-sv"><div class="'+e+'colorpicker-overlay1"><div class="'+e+'colorpicker-overlay2"><div id="'+d+'-svp" class="'+e+'colorpicker-selector1"><div class="'+e+'colorpicker-selector2"></div></div></div></div></div>'+b+"</div>"}})}),g("33",["2r"],function(a){"use strict";return a.extend({init:function(a){var b=this;a.delimiter||(a.delimiter="\xbb"),b._super(a),b.classes.add("path"),b.canFocus=!0,b.on("click",function(a){var c,d=a.target;(c=d.getAttribute("data-index"))&&b.fire("select",{value:b.row()[c],index:c})}),b.row(b.settings.row)},focus:function(){var a=this;return a.getEl().firstChild.focus(),a},row:function(a){return arguments.length?(this.state.set("row",a),this):this.state.get("row")},renderHtml:function(){var a=this;return'<div id="'+a._id+'" class="'+a.classes+'">'+a._getDataPathHtml(a.state.get("row"))+"</div>"},bindStates:function(){var a=this;return a.state.on("change:row",function(b){a.innerHtml(a._getDataPathHtml(b.value))}),a._super()},_getDataPathHtml:function(a){var b,c,d=this,e=a||[],f="",g=d.classPrefix;for(b=0,c=e.length;b<c;b++)f+=(b>0?'<div class="'+g+'divider" aria-hidden="true"> '+d.settings.delimiter+" </div>":"")+'<div role="button" class="'+g+"path-item"+(b==c-1?" "+g+"last":"")+'" data-index="'+b+'" tabindex="-1" id="'+d._id+"-"+b+'" aria-level="'+(b+1)+'">'+e[b].name+"</div>"; +return f||(f='<div class="'+g+'path-item">\xa0</div>'),f}})}),g("34",["33"],function(a){return a.extend({postRender:function(){function a(a){if(1===a.nodeType){if("BR"==a.nodeName||a.getAttribute("data-mce-bogus"))return!0;if("bookmark"===a.getAttribute("data-mce-type"))return!0}return!1}var b=this,c=b.settings.editor;return c.settings.elementpath!==!1&&(b.on("select",function(a){c.focus(),c.selection.select(this.row()[a.index].element),c.nodeChanged()}),c.on("nodeChange",function(d){for(var e=[],f=d.parents,g=f.length;g--;)if(1==f[g].nodeType&&!a(f[g])){var h=c.fire("ResolveName",{name:f[g].nodeName.toLowerCase(),target:f[g]});if(h.isDefaultPrevented()||e.push({name:h.name,element:f[g]}),h.isPropagationStopped())break}b.row(e)})),b._super()}})}),g("35",["2j"],function(a){"use strict";return a.extend({Defaults:{layout:"flex",align:"center",defaults:{flex:1}},renderHtml:function(){var a=this,b=a._layout,c=a.classPrefix;return a.classes.add("formitem"),b.preRender(a),'<div id="'+a._id+'" class="'+a.classes+'" hidefocus="1" tabindex="-1">'+(a.settings.title?'<div id="'+a._id+'-title" class="'+c+'title">'+a.settings.title+"</div>":"")+'<div id="'+a._id+'-body" class="'+a.bodyClasses+'">'+(a.settings.html||"")+b.renderHtml(a)+"</div></div>"}})}),g("36",["2j","35","9"],function(a,b,c){"use strict";return a.extend({Defaults:{containerCls:"form",layout:"flex",direction:"column",align:"stretch",flex:1,padding:20,labelGap:30,spacing:10,callbacks:{submit:function(){this.submit()}}},preRender:function(){var a=this,d=a.items();a.settings.formItemDefaults||(a.settings.formItemDefaults={layout:"flex",autoResize:"overflow",defaults:{flex:1}}),d.each(function(d){var e,f=d.settings.label;f&&(e=new b(c.extend({items:{type:"label",id:d._id+"-l",text:f,flex:0,forId:d._id,disabled:d.disabled()}},a.settings.formItemDefaults)),e.type="formitem",d.aria("labelledby",d._id+"-l"),"undefined"==typeof d.settings.flex&&(d.settings.flex=1),a.replace(d,e),e.add(d))})},submit:function(){return this.fire("submit",{data:this.toJSON()})},postRender:function(){var a=this;a._super(),a.fromJSON(a.settings.data)},bindStates:function(){function a(){var a,c,d,e=0,f=[];if(b.settings.labelGapCalc!==!1)for(d="children"==b.settings.labelGapCalc?b.find("formitem"):b.items(),d.filter("formitem").each(function(a){var b=a.items()[0],c=b.getEl().clientWidth;e=c>e?c:e,f.push(b)}),c=b.settings.labelGap||0,a=f.length;a--;)f[a].settings.minWidth=e+c}var b=this;b._super(),b.on("show",a),a()}})}),g("37",["36"],function(a){"use strict";return a.extend({Defaults:{containerCls:"fieldset",layout:"flex",direction:"column",align:"stretch",flex:1,padding:"25 15 5 15",labelGap:30,spacing:10,border:1},renderHtml:function(){var a=this,b=a._layout,c=a.classPrefix;return a.preRender(),b.preRender(a),'<fieldset id="'+a._id+'" class="'+a.classes+'" hidefocus="1" tabindex="-1">'+(a.settings.title?'<legend id="'+a._id+'-title" class="'+c+'fieldset-title">'+a.settings.title+"</legend>":"")+'<div id="'+a._id+'-body" class="'+a.bodyClasses+'">'+(a.settings.html||"")+b.renderHtml(a)+"</div></fieldset>"}})}),g("4x",["e","1j","1g","1w","9","2a"],function(a,b,c,d,e,f){var g=e.trim,h=function(a,b,c,d,e){return{type:a,title:b,url:c,level:d,attach:e}},i=function(a){for(;a=a.parentNode;){var c=a.contentEditable;if(c&&"inherit"!==c)return b.isContentEditableTrue(a)}return!1},j=function(b,c){return a.DOM.select(b,c)},k=function(a){return a.innerText||a.textContent},l=function(a){return a.id?a.id:f.uuid("h")},m=function(a){return a&&"A"===a.nodeName&&(a.id||a.name)},n=function(a){return m(a)&&p(a)},o=function(a){return a&&/^(H[1-6])$/.test(a.nodeName)},p=function(a){return i(a)&&!b.isContentEditableFalse(a)},q=function(a){return o(a)&&p(a)},r=function(a){return o(a)?parseInt(a.nodeName.substr(1),10):0},s=function(a){var b=l(a),c=function(){a.id=b};return h("header",k(a),"#"+b,r(a),c)},t=function(a){var b=a.id||a.name,c=k(a);return h("anchor",c?c:"#"+b,"#"+b,0,d.noop)},u=function(a){return c.map(c.filter(a,q),s)},v=function(a){return c.map(c.filter(a,n),t)},w=function(a){var b=j("h1,h2,h3,h4,h5,h6,a:not([href])",a);return b},x=function(a){return g(a.title).length>0},y=function(a){var b=w(a);return c.filter(u(b).concat(v(b)),x)};return{find:y}}),g("38",["4t","4x","17","2y","1g","1w","9"],function(a,b,c,d,e,f,g){"use strict";var h=function(){return a.tinymce?a.tinymce.activeEditor:c.activeEditor},i={},j=5,k=function(a){return{title:a.title,value:{title:{raw:a.title},url:a.url,attach:a.attach}}},l=function(a){return g.map(a,k)},m=function(a,b){return{title:a,value:{title:a,url:b,attach:f.noop}}},n=function(a,b){var c=e.find(b,function(b){return b.url===a});return!c},o=function(a,b,c){var d=b in a?a[b]:c;return d===!1?null:d},p=function(a,b,c,d){var h={title:"-"},j=function(a){var d=e.filter(a[c],function(a){return n(a,b)});return g.map(d,function(a){return{title:a,value:{title:a,url:a,attach:f.noop}}})},k=function(a){var c=e.filter(b,function(b){return b.type==a});return l(c)},p=function(){var a=k("anchor"),b=o(d,"anchor_top","#top"),c=o(d,"anchor_bottom","#bottom");return null!==b&&a.unshift(m("<top>",b)),null!==c&&a.push(m("<bottom>",c)),a},q=function(a){return e.reduce(a,function(a,b){var c=0===a.length||0===b.length;return c?a.concat(b):a.concat(h,b)},[])};return d.typeahead_urls===!1?[]:"file"===c?q([r(a,j(i)),r(a,k("header")),r(a,p())]):r(a,j(i))},q=function(a,b){var c=i[b];/^https?/.test(a)&&(c?e.indexOf(c,a)===-1&&(i[b]=c.slice(0,j).concat(a)):i[b]=[a])},r=function(a,b){var c=a.toLowerCase(),d=g.grep(b,function(a){return a.title.toLowerCase().indexOf(c)!==-1});return 1===d.length&&d[0].title===a?[]:d},s=function(a){var b=a.title;return b.raw?b.raw:b},t=function(a,c,d,e){var f=function(f){var g=b.find(d),h=p(f,g,e,c);a.showAutoComplete(h,f)};a.on("autocomplete",function(){f(a.value())}),a.on("selectitem",function(b){var c=b.value;a.value(c.url);var d=s(c);"image"===e?a.fire("change",{meta:{alt:d,attach:c.attach}}):a.fire("change",{meta:{text:d,attach:c.attach}}),a.focus()}),a.on("click",function(b){0===a.value().length&&"INPUT"===b.target.nodeName&&f("")}),a.on("PostRender",function(){a.getRoot().on("submit",function(b){b.isDefaultPrevented()||q(a.value(),e)})})},u=function(a){var b=a.status,c=a.message;return"valid"===b?{status:"ok",message:c}:"unknown"===b?{status:"warn",message:c}:"invalid"===b?{status:"warn",message:c}:{status:"none",message:""}},v=function(a,b,c){var d=b.filepicker_validator_handler;if(d){var e=function(b){return 0===b.length?void a.statusLevel("none"):void d({url:b,type:c},function(b){var c=u(b);a.statusMessage(c.message),a.statusLevel(c.status)})};a.state.on("change:value",function(a){e(a.value)})}};return d.extend({init:function(b){var c,d,e,f=this,i=h(),j=i.settings,k=b.filetype;b.spellcheck=!1,e=j.file_picker_types||j.file_browser_callback_types,e&&(e=g.makeMap(e,/[, ]/)),e&&!e[k]||(d=j.file_picker_callback,!d||e&&!e[k]?(d=j.file_browser_callback,!d||e&&!e[k]||(c=function(){d(f.getEl("inp").id,f.value(),k,a)})):c=function(){var a=f.fire("beforecall").meta;a=g.extend({filetype:k},a),d.call(i,function(a,b){f.value(a).fire("change",{meta:b})},f.value(),a)}),c&&(b.icon="browse",b.onaction=c),f._super(b),t(f,j,i.getBody(),k),v(f,j,k)}})}),g("39",["2u"],function(a){"use strict";return a.extend({recalc:function(a){var b=a.layoutRect(),c=a.paddingBox;a.items().filter(":visible").each(function(a){a.layoutRect({x:c.left,y:c.top,w:b.innerW-c.right-c.left,h:b.innerH-c.top-c.bottom}),a.recalc&&a.recalc()})}})}),g("3a",["2u"],function(a){"use strict";return a.extend({recalc:function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N=[],O=Math.max,P=Math.min;for(d=a.items().filter(":visible"),e=a.layoutRect(),f=a.paddingBox,g=a.settings,m=a.isRtl()?g.direction||"row-reversed":g.direction,h=g.align,i=a.isRtl()?g.pack||"end":g.pack,j=g.spacing||0,"row-reversed"!=m&&"column-reverse"!=m||(d=d.set(d.toArray().reverse()),m=m.split("-")[0]),"column"==m?(z="y",x="h",y="minH",A="maxH",C="innerH",B="top",D="deltaH",E="contentH",J="left",H="w",F="x",G="innerW",I="minW",K="right",L="deltaW",M="contentW"):(z="x",x="w",y="minW",A="maxW",C="innerW",B="left",D="deltaW",E="contentW",J="top",H="h",F="y",G="innerH",I="minH",K="bottom",L="deltaH",M="contentH"),l=e[C]-f[B]-f[B],w=k=0,b=0,c=d.length;b<c;b++)n=d[b],o=n.layoutRect(),p=n.settings,q=p.flex,l-=b<c-1?j:0,q>0&&(k+=q,o[A]&&N.push(n),o.flex=q),l-=o[y],r=f[J]+o[I]+f[K],r>w&&(w=r);if(u={},l<0?u[y]=e[y]-l+e[D]:u[y]=e[C]-l+e[D],u[I]=w+e[L],u[E]=e[C]-l,u[M]=w,u.minW=P(u.minW,e.maxW),u.minH=P(u.minH,e.maxH),u.minW=O(u.minW,e.startMinWidth),u.minH=O(u.minH,e.startMinHeight),!e.autoResize||u.minW==e.minW&&u.minH==e.minH){for(t=l/k,b=0,c=N.length;b<c;b++)n=N[b],o=n.layoutRect(),s=o[A],r=o[y]+o.flex*t,r>s?(l-=o[A]-o[y],k-=o.flex,o.flex=0,o.maxFlexSize=s):o.maxFlexSize=0;for(t=l/k,v=f[B],u={},0===k&&("end"==i?v=l+f[B]:"center"==i?(v=Math.round(e[C]/2-(e[C]-l)/2)+f[B],v<0&&(v=f[B])):"justify"==i&&(v=f[B],j=Math.floor(l/(d.length-1)))),u[F]=f[J],b=0,c=d.length;b<c;b++)n=d[b],o=n.layoutRect(),r=o.maxFlexSize||o[y],"center"===h?u[F]=Math.round(e[G]/2-o[H]/2):"stretch"===h?(u[H]=O(o[I]||0,e[G]-f[J]-f[K]),u[F]=f[J]):"end"===h&&(u[F]=e[G]-o[H]-f.top),o.flex>0&&(r+=o.flex*t),u[x]=r,u[z]=v,n.layoutRect(u),n.recalc&&n.recalc(),v+=r+j}else if(u.w=u.minW,u.h=u.minH,a.layoutRect(u),this.recalc(a),null===a._lastRect){var Q=a.parent();Q&&(Q._lastRect=null,Q.recalc())}}})}),g("3b",["2t"],function(a){return a.extend({Defaults:{containerClass:"flow-layout",controlClass:"flow-layout-item",endClass:"break"},recalc:function(a){a.items().filter(":visible").each(function(a){a.recalc&&a.recalc()})},isNative:function(){return!0}})}),g("4y",["1m","4z","1r","4d","e"],function(a,b,c,d,e){var f=function(a,c,d){for(;d!==c;){if(d.style[a]){var e=d.style[a];return""!==e?b.some(e):b.none()}d=d.parentNode}return b.none()},g=function(a){return/[0-9.]+px$/.test(a)?Math.round(72*parseInt(a,10)/96)+"pt":a},h=function(a){return a.replace(/[\'\"]/g,"").replace(/,\s+/g,",")},i=function(a,c){return b.from(e.DOM.getStyle(c,a,!0))},j=function(a){return function(e,g){return b.from(g).map(c.fromDom).filter(d.isElement).bind(function(b){return f(a,e,b.dom()).or(i(a,b.dom()))}).getOr("")}};return{getFontSize:j("fontSize"),getFontFamily:a.compose(h,j("fontFamily")),toPt:g}}),g("3c",["2g","2r","2p","9","1g","e","17","6","4y"],function(a,b,c,d,e,f,g,h,i){function j(a){a.settings.ui_container&&(h.container=f.DOM.select(a.settings.ui_container)[0])}function k(b){b.on("ScriptsLoaded",function(){b.rtl&&(a.rtl=!0)})}function l(a){function b(b,c){return function(){var d=this;a.on("nodeChange",function(e){var f=a.formatter,g=null;m(e.parents,function(a){if(m(b,function(b){if(c?f.matchNode(a,c,{value:b.value})&&(g=b.value):f.matchNode(a,b.value)&&(g=b.value),g)return!1}),g)return!1}),d.value(g)})}}function e(b){return function(){var c=this,d=function(a){return a?a.split(",")[0]:""};a.on("init nodeChange",function(e){var f,g=null;f=i.getFontFamily(a.getBody(),e.element),m(b,function(a){a.value.toLowerCase()===f.toLowerCase()&&(g=a.value)}),m(b,function(a){g||d(a.value).toLowerCase()!==d(f).toLowerCase()||(g=a.value)}),c.value(g),!g&&f&&c.text(d(f))})}}function f(b){return function(){var c=this;a.on("init nodeChange",function(d){var e,f,g=null;e=i.getFontSize(a.getBody(),d.element),f=i.toPt(e),m(b,function(a){a.value===e?g=e:a.value===f&&(g=f)}),c.value(g),g||c.text(f)})}}function g(a){a=a.replace(/;$/,"").split(";");for(var b=a.length;b--;)a[b]=a[b].split("=");return a}function h(){function b(a){var c=[];if(a)return m(a,function(a){var f={text:a.title,icon:a.icon};if(a.items)f.menu=b(a.items);else{var g=a.format||"custom"+d++;a.format||(a.name=g,e.push(a)),f.format=g,f.cmd=a.cmd}c.push(f)}),c}function c(){var c;return c=b(a.settings.style_formats_merge?a.settings.style_formats?f.concat(a.settings.style_formats):f:a.settings.style_formats||f)}var d=0,e=[],f=[{title:"Headings",items:[{title:"Heading 1",format:"h1"},{title:"Heading 2",format:"h2"},{title:"Heading 3",format:"h3"},{title:"Heading 4",format:"h4"},{title:"Heading 5",format:"h5"},{title:"Heading 6",format:"h6"}]},{title:"Inline",items:[{title:"Bold",icon:"bold",format:"bold"},{title:"Italic",icon:"italic",format:"italic"},{title:"Underline",icon:"underline",format:"underline"},{title:"Strikethrough",icon:"strikethrough",format:"strikethrough"},{title:"Superscript",icon:"superscript",format:"superscript"},{title:"Subscript",icon:"subscript",format:"subscript"},{title:"Code",icon:"code",format:"code"}]},{title:"Blocks",items:[{title:"Paragraph",format:"p"},{title:"Blockquote",format:"blockquote"},{title:"Div",format:"div"},{title:"Pre",format:"pre"}]},{title:"Alignment",items:[{title:"Left",icon:"alignleft",format:"alignleft"},{title:"Center",icon:"aligncenter",format:"aligncenter"},{title:"Right",icon:"alignright",format:"alignright"},{title:"Justify",icon:"alignjustify",format:"alignjustify"}]}];return a.on("init",function(){m(e,function(b){a.formatter.register(b.name,b)})}),{type:"menu",items:c(),onPostRender:function(b){a.fire("renderFormatsMenu",{control:b.control})},itemDefaults:{preview:!0,textStyle:function(){if(this.settings.format)return a.formatter.getCssText(this.settings.format)},onPostRender:function(){var b=this;b.parent().on("show",function(){var c,d;c=b.settings.format,c&&(b.disabled(!a.formatter.canApply(c)),b.active(a.formatter.match(c))),d=b.settings.cmd,d&&b.active(a.queryCommandState(d))})},onclick:function(){this.settings.format&&o(this.settings.format),this.settings.cmd&&a.execCommand(this.settings.cmd)}}}}function j(b){return function(){var c=this;a.formatter?a.formatter.formatChanged(b,function(a){c.active(a)}):a.on("init",function(){a.formatter.formatChanged(b,function(a){c.active(a)})})}}function k(b){return function(){function c(){var c="redo"==b?"hasRedo":"hasUndo";return!!a.undoManager&&a.undoManager[c]()}var d=this;d.disabled(!c()),a.on("Undo Redo AddUndo TypingUndo ClearUndos SwitchMode",function(){d.disabled(a.readonly||!c())})}}function l(){var b=this;a.on("VisualAid",function(a){b.active(a.hasVisual)}),b.active(a.hasVisual)}function o(b){b.control&&(b=b.control.value()),b&&a.execCommand("mceToggleFormat",!1,b)}function p(b){var c=b.length;return d.each(b,function(b){b.menu&&(b.hidden=0===p(b.menu));var d=b.format;d&&(b.hidden=!a.formatter.canApply(d)),b.hidden&&c--}),c}function q(b){var c=b.items().length;return b.items().each(function(b){b.menu&&b.visible(q(b.menu)>0),!b.menu&&b.settings.menu&&b.visible(p(b.settings.menu)>0);var d=b.settings.format;d&&b.visible(a.formatter.canApply(d)),b.visible()||c--}),c}var r;r=h(),m({bold:"Bold",italic:"Italic",underline:"Underline",strikethrough:"Strikethrough",subscript:"Subscript",superscript:"Superscript"},function(b,c){a.addButton(c,{tooltip:b,onPostRender:j(c),onclick:function(){o(c)}})}),m({outdent:["Decrease indent","Outdent"],indent:["Increase indent","Indent"],cut:["Cut","Cut"],copy:["Copy","Copy"],paste:["Paste","Paste"],help:["Help","mceHelp"],selectall:["Select all","SelectAll"],removeformat:["Clear formatting","RemoveFormat"],visualaid:["Visual aids","mceToggleVisualAid"],newdocument:["New document","mceNewDocument"]},function(b,c){a.addButton(c,{tooltip:b[0],cmd:b[1]})}),m({blockquote:["Blockquote","mceBlockQuote"],subscript:["Subscript","Subscript"],superscript:["Superscript","Superscript"],alignleft:["Align left","JustifyLeft"],aligncenter:["Align center","JustifyCenter"],alignright:["Align right","JustifyRight"],alignjustify:["Justify","JustifyFull"],alignnone:["No alignment","JustifyNone"]},function(b,c){a.addButton(c,{tooltip:b[0],cmd:b[1],onPostRender:j(c)})});var s=function(a){var b=a;return b.length>0&&"-"===b[0].text&&(b=b.slice(1)),b.length>0&&"-"===b[b.length-1].text&&(b=b.slice(0,b.length-1)),b},t=function(b){var c,e;if("string"==typeof b)e=b.split(" ");else if(d.isArray(b))return n(d.map(b,t));return c=d.grep(e,function(b){return"|"===b||b in a.menuItems}),d.map(c,function(b){return"|"===b?{text:"-"}:a.menuItems[b]})},u=function(b){var c=[{text:"-"}],e=d.grep(a.menuItems,function(a){return a.context===b});return d.each(e,function(a){"before"==a.separator&&c.push({text:"|"}),a.prependToContext?c.unshift(a):c.push(a),"after"==a.separator&&c.push({text:"|"})}),c},v=function(a){return s(a.insert_button_items?t(a.insert_button_items):u("insert"))};a.addButton("undo",{tooltip:"Undo",onPostRender:k("undo"),cmd:"undo"}),a.addButton("redo",{tooltip:"Redo",onPostRender:k("redo"),cmd:"redo"}),a.addMenuItem("newdocument",{text:"New document",icon:"newdocument",cmd:"mceNewDocument"}),a.addMenuItem("undo",{text:"Undo",icon:"undo",shortcut:"Meta+Z",onPostRender:k("undo"),cmd:"undo"}),a.addMenuItem("redo",{text:"Redo",icon:"redo",shortcut:"Meta+Y",onPostRender:k("redo"),cmd:"redo"}),a.addMenuItem("visualaid",{text:"Visual aids",selectable:!0,onPostRender:l,cmd:"mceToggleVisualAid"}),a.addButton("remove",{tooltip:"Remove",icon:"remove",cmd:"Delete"}),a.addButton("insert",{type:"menubutton",icon:"insert",menu:[],oncreatemenu:function(){this.menu.add(v(a.settings)),this.menu.renderNew()}}),m({cut:["Cut","Cut","Meta+X"],copy:["Copy","Copy","Meta+C"],paste:["Paste","Paste","Meta+V"],selectall:["Select all","SelectAll","Meta+A"],bold:["Bold","Bold","Meta+B"],italic:["Italic","Italic","Meta+I"],underline:["Underline","Underline","Meta+U"],strikethrough:["Strikethrough","Strikethrough"],subscript:["Subscript","Subscript"],superscript:["Superscript","Superscript"],removeformat:["Clear formatting","RemoveFormat"]},function(b,c){a.addMenuItem(c,{text:b[0],icon:c,shortcut:b[2],cmd:b[1]})}),a.on("mousedown",function(){c.hideAll()}),a.addButton("styleselect",{type:"menubutton",text:"Formats",menu:r,onShowMenu:function(){a.settings.style_formats_autohide&&q(this.menu)}}),a.addButton("formatselect",function(){var c=[],d=g(a.settings.block_formats||"Paragraph=p;Heading 1=h1;Heading 2=h2;Heading 3=h3;Heading 4=h4;Heading 5=h5;Heading 6=h6;Preformatted=pre");return m(d,function(b){c.push({text:b[0],value:b[1],textStyle:function(){return a.formatter.getCssText(b[1])}})}),{type:"listbox",text:d[0][0],values:c,fixedWidth:!0,onselect:o,onPostRender:b(c)}}),a.addButton("fontselect",function(){var b="Andale Mono=andale mono,monospace;Arial=arial,helvetica,sans-serif;Arial Black=arial black,sans-serif;Book Antiqua=book antiqua,palatino,serif;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier,monospace;Georgia=georgia,palatino,serif;Helvetica=helvetica,arial,sans-serif;Impact=impact,sans-serif;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco,monospace;Times New Roman=times new roman,times,serif;Trebuchet MS=trebuchet ms,geneva,sans-serif;Verdana=verdana,geneva,sans-serif;Webdings=webdings;Wingdings=wingdings,zapf dingbats",c=[],d=g(a.settings.font_formats||b);return m(d,function(a){c.push({text:{raw:a[0]},value:a[1],textStyle:a[1].indexOf("dings")==-1?"font-family:"+a[1]:""})}),{type:"listbox",text:"Font Family",tooltip:"Font Family",values:c,fixedWidth:!0,onPostRender:e(c),onselect:function(b){b.control.settings.value&&a.execCommand("FontName",!1,b.control.settings.value)}}}),a.addButton("fontsizeselect",function(){var b=[],c="8pt 10pt 12pt 14pt 18pt 24pt 36pt",d=a.settings.fontsize_formats||c;return m(d.split(" "),function(a){var c=a,d=a,e=a.split("=");e.length>1&&(c=e[0],d=e[1]),b.push({text:c,value:d})}),{type:"listbox",text:"Font Sizes",tooltip:"Font Sizes",values:b,fixedWidth:!0,onPostRender:f(b),onclick:function(b){b.control.settings.value&&a.execCommand("FontSize",!1,b.control.settings.value)}}}),a.addMenuItem("formats",{text:"Formats",menu:r})}var m=d.each,n=function(a){return e.reduce(a,function(a,b){return a.concat(b)},[])};return g.on("AddEditor",function(a){var b=a.editor;k(b),l(b),j(b)}),a.translate=function(a){return g.translate(a)},b.tooltips=!h.iOS,{}}),g("3d",["2u"],function(a){"use strict";return a.extend({recalc:function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E=[],F=[];b=a.settings,e=a.items().filter(":visible"),f=a.layoutRect(),d=b.columns||Math.ceil(Math.sqrt(e.length)),c=Math.ceil(e.length/d),s=b.spacingH||b.spacing||0,t=b.spacingV||b.spacing||0,u=b.alignH||b.align,v=b.alignV||b.align,q=a.paddingBox,C="reverseRows"in b?b.reverseRows:a.isRtl(),u&&"string"==typeof u&&(u=[u]),v&&"string"==typeof v&&(v=[v]);for(l=0;l<d;l++)E.push(0);for(m=0;m<c;m++)F.push(0);for(m=0;m<c;m++)for(l=0;l<d&&(k=e[m*d+l],k);l++)j=k.layoutRect(),y=j.minW,z=j.minH,E[l]=y>E[l]?y:E[l],F[m]=z>F[m]?z:F[m];for(A=f.innerW-q.left-q.right,w=0,l=0;l<d;l++)w+=E[l]+(l>0?s:0),A-=(l>0?s:0)+E[l];for(B=f.innerH-q.top-q.bottom,x=0,m=0;m<c;m++)x+=F[m]+(m>0?t:0),B-=(m>0?t:0)+F[m];if(w+=q.left+q.right,x+=q.top+q.bottom,i={},i.minW=w+(f.w-f.innerW),i.minH=x+(f.h-f.innerH),i.contentW=i.minW-f.deltaW,i.contentH=i.minH-f.deltaH,i.minW=Math.min(i.minW,f.maxW),i.minH=Math.min(i.minH,f.maxH),i.minW=Math.max(i.minW,f.startMinWidth),i.minH=Math.max(i.minH,f.startMinHeight),!f.autoResize||i.minW==f.minW&&i.minH==f.minH){f.autoResize&&(i=a.layoutRect(i),i.contentW=i.minW-f.deltaW,i.contentH=i.minH-f.deltaH);var G;G="start"==b.packV?0:B>0?Math.floor(B/c):0;var H=0,I=b.flexWidths;if(I)for(l=0;l<I.length;l++)H+=I[l];else H=d;var J=A/H;for(l=0;l<d;l++)E[l]+=I?I[l]*J:J;for(o=q.top,m=0;m<c;m++){for(n=q.left,h=F[m]+G,l=0;l<d&&(D=C?m*d+d-1-l:m*d+l,k=e[D],k);l++)p=k.settings,j=k.layoutRect(),g=Math.max(E[l],j.startMinWidth),j.x=n,j.y=o,r=p.alignH||(u?u[l]||u[0]:null),"center"==r?j.x=n+g/2-j.w/2:"right"==r?j.x=n+g-j.w:"stretch"==r&&(j.w=g),r=p.alignV||(v?v[l]||v[0]:null),"center"==r?j.y=o+h/2-j.h/2:"bottom"==r?j.y=o+h-j.h:"stretch"==r&&(j.h=h),k.layoutRect(j),n+=g+s,k.recalc&&k.recalc();o+=h+t}}else if(i.w=i.minW,i.h=i.minH,a.layoutRect(i),this.recalc(a),null===a._lastRect){var K=a.parent();K&&(K._lastRect=null,K.recalc())}}})}),g("3e",["2r","5"],function(a,b){"use strict";return a.extend({renderHtml:function(){var a=this;return a.classes.add("iframe"),a.canFocus=!1,'<iframe id="'+a._id+'" class="'+a.classes+'" tabindex="-1" src="'+(a.settings.url||"javascript:''")+'" frameborder="0"></iframe>'},src:function(a){this.getEl().src=a},html:function(a,c){var d=this,e=this.getEl().contentWindow.document.body;return e?(e.innerHTML=a,c&&c()):b.setTimeout(function(){d.html(a)}),this}})}),g("3f",["2r"],function(a){"use strict";return a.extend({init:function(a){var b=this;b._super(a),b.classes.add("widget").add("infobox"),b.canFocus=!1},severity:function(a){this.classes.remove("error"),this.classes.remove("warning"),this.classes.remove("success"),this.classes.add(a)},help:function(a){this.state.set("help",a)},renderHtml:function(){var a=this,b=a.classPrefix;return'<div id="'+a._id+'" class="'+a.classes+'"><div id="'+a._id+'-body">'+a.encode(a.state.get("text"))+'<button role="button" tabindex="-1"><i class="'+b+"ico "+b+'i-help"></i></button></div></div>'},bindStates:function(){var a=this;return a.state.on("change:text",function(b){a.getEl("body").firstChild.data=a.encode(b.value),a.state.get("rendered")&&a.updateLayoutRect()}),a.state.on("change:help",function(b){a.classes.toggle("has-help",b.value),a.state.get("rendered")&&a.updateLayoutRect()}),a._super()}})}),g("3g",["2r","4q"],function(a,b){"use strict";return a.extend({init:function(a){var b=this;b._super(a),b.classes.add("widget").add("label"),b.canFocus=!1,a.multiline&&b.classes.add("autoscroll"),a.strong&&b.classes.add("strong")},initLayoutRect:function(){var a=this,c=a._super();if(a.settings.multiline){var d=b.getSize(a.getEl());d.width>c.maxW&&(c.minW=c.maxW,a.classes.add("multiline")),a.getEl().style.width=c.minW+"px",c.startMinH=c.h=c.minH=Math.min(c.maxH,b.getSize(a.getEl()).height)}return c},repaint:function(){var a=this;return a.settings.multiline||(a.getEl().style.lineHeight=a.layoutRect().h+"px"),a._super()},severity:function(a){this.classes.remove("error"),this.classes.remove("warning"),this.classes.remove("success"),this.classes.add(a)},renderHtml:function(){var a,b,c=this,d=c.settings.forId,e=c.settings.html?c.settings.html:c.encode(c.state.get("text"));return!d&&(b=c.settings.forName)&&(a=c.getRoot().find("#"+b)[0],a&&(d=a._id)),d?'<label id="'+c._id+'" class="'+c.classes+'"'+(d?' for="'+d+'"':"")+">"+e+"</label>":'<span id="'+c._id+'" class="'+c.classes+'">'+e+"</span>"},bindStates:function(){var a=this;return a.state.on("change:text",function(b){a.innerHtml(a.encode(b.value)),a.state.get("rendered")&&a.updateLayoutRect()}),a._super()}})}),g("3h",["2j"],function(a){"use strict";return a.extend({Defaults:{role:"toolbar",layout:"flow"},init:function(a){var b=this;b._super(a),b.classes.add("toolbar")},postRender:function(){var a=this;return a.items().each(function(a){a.classes.add("toolbar-item")}),a._super()}})}),g("3i",["3h"],function(a){"use strict";return a.extend({Defaults:{role:"menubar",containerCls:"menubar",ariaRoot:!0,defaults:{type:"menubutton"}}})}),g("3j",["2v","2h","3i"],function(a,b,c){"use strict";function d(a,b){for(;a;){if(b===a)return!0;a=a.parentNode}return!1}var e=a.extend({init:function(a){var b=this;b._renderOpen=!0,b._super(a),a=b.settings,b.classes.add("menubtn"),a.fixedWidth&&b.classes.add("fixed-width"),b.aria("haspopup",!0),b.state.set("menu",a.menu||b.render())},showMenu:function(a){var c,d=this;return d.menu&&d.menu.visible()&&a!==!1?d.hideMenu():(d.menu||(c=d.state.get("menu")||[],c.length?c={type:"menu",items:c}:c.type=c.type||"menu",c.renderTo?d.menu=c.parent(d).show().renderTo():d.menu=b.create(c).parent(d).renderTo(),d.fire("createmenu"),d.menu.reflow(),d.menu.on("cancel",function(a){a.control.parent()===d.menu&&(a.stopPropagation(),d.focus(),d.hideMenu())}),d.menu.on("select",function(){d.focus()}),d.menu.on("show hide",function(a){a.control==d.menu&&d.activeMenu("show"==a.type),d.aria("expanded","show"==a.type)}).fire("show")),d.menu.show(),d.menu.layoutRect({w:d.layoutRect().w}),d.menu.moveRel(d.getEl(),d.isRtl()?["br-tr","tr-br"]:["bl-tl","tl-bl"]),void d.fire("showmenu"))},hideMenu:function(){var a=this;a.menu&&(a.menu.items().each(function(a){a.hideMenu&&a.hideMenu()}),a.menu.hide())},activeMenu:function(a){this.classes.toggle("active",a)},renderHtml:function(){var a,b=this,d=b._id,e=b.classPrefix,f=b.settings.icon,g=b.state.get("text"),h="";return a=b.settings.image,a?(f="none","string"!=typeof a&&(a=window.getSelection?a[0]:a[1]),a=" style=\"background-image: url('"+a+"')\""):a="",g&&(b.classes.add("btn-has-text"),h='<span class="'+e+'txt">'+b.encode(g)+"</span>"),f=b.settings.icon?e+"ico "+e+"i-"+f:"",b.aria("role",b.parent()instanceof c?"menuitem":"button"),'<div id="'+d+'" class="'+b.classes+'" tabindex="-1" aria-labelledby="'+d+'"><button id="'+d+'-open" role="presentation" type="button" tabindex="-1">'+(f?'<i class="'+f+'"'+a+"></i>":"")+h+' <i class="'+e+'caret"></i></button></div>'},postRender:function(){var a=this;return a.on("click",function(b){b.control===a&&d(b.target,a.getEl())&&(a.focus(),a.showMenu(!b.aria),b.aria&&a.menu.items().filter(":visible")[0].focus())}),a.on("mouseenter",function(b){var c,d=b.control,f=a.parent();d&&f&&d instanceof e&&d.parent()==f&&(f.items().filter("MenuButton").each(function(a){a.hideMenu&&a!=d&&(a.menu&&a.menu.visible()&&(c=!0),a.hideMenu())}),c&&(d.focus(),d.showMenu()))}),a._super()},bindStates:function(){var a=this;return a.state.on("change:menu",function(){a.menu&&a.menu.remove(),a.menu=null}),a._super()},remove:function(){this._super(),this.menu&&this.menu.remove()}});return e}),g("3k",["2r","2h","6","5"],function(a,b,c,d){"use strict";return a.extend({Defaults:{border:0,role:"menuitem"},init:function(a){var b,c=this;c._super(a),a=c.settings,c.classes.add("menu-item"),a.menu&&c.classes.add("menu-item-expand"),a.preview&&c.classes.add("menu-item-preview"),b=c.state.get("text"),"-"!==b&&"|"!==b||(c.classes.add("menu-item-sep"),c.aria("role","separator"),c.state.set("text","-")),a.selectable&&(c.aria("role","menuitemcheckbox"),c.classes.add("menu-item-checkbox"),a.icon="selected"),a.preview||a.selectable||c.classes.add("menu-item-normal"),c.on("mousedown",function(a){a.preventDefault()}),a.menu&&!a.ariaHideMenu&&c.aria("haspopup",!0)},hasMenus:function(){return!!this.settings.menu},showMenu:function(){var a,c=this,d=c.settings,e=c.parent();if(e.items().each(function(a){a!==c&&a.hideMenu()}),d.menu){a=c.menu,a?a.show():(a=d.menu,a.length?a={type:"menu",items:a}:a.type=a.type||"menu",e.settings.itemDefaults&&(a.itemDefaults=e.settings.itemDefaults),a=c.menu=b.create(a).parent(c).renderTo(),a.reflow(),a.on("cancel",function(b){b.stopPropagation(),c.focus(),a.hide()}),a.on("show hide",function(a){a.control.items&&a.control.items().each(function(a){a.active(a.settings.selected)})}).fire("show"),a.on("hide",function(b){b.control===a&&c.classes.remove("selected")}),a.submenu=!0),a._parentMenu=e,a.classes.add("menu-sub");var f=a.testMoveRel(c.getEl(),c.isRtl()?["tl-tr","bl-br","tr-tl","br-bl"]:["tr-tl","br-bl","tl-tr","bl-br"]);a.moveRel(c.getEl(),f),a.rel=f,f="menu-sub-"+f,a.classes.remove(a._lastRel).add(f),a._lastRel=f,c.classes.add("selected"),c.aria("expanded",!0)}},hideMenu:function(){var a=this;return a.menu&&(a.menu.items().each(function(a){a.hideMenu&&a.hideMenu()}),a.menu.hide(),a.aria("expanded",!1)),a},renderHtml:function(){function a(a){var b,d,e={};for(e=c.mac?{alt:"⌥",ctrl:"⌘",shift:"⇧",meta:"⌘"}:{meta:"Ctrl"},a=a.split("+"),b=0;b<a.length;b++)d=e[a[b].toLowerCase()],d&&(a[b]=d);return a.join("+")}function b(a){return a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function d(a){var c=h.match||"";return c?a.replace(new RegExp(b(c),"gi"),function(a){return"!mce~match["+a+"]mce~match!"}):a}function e(a){return a.replace(new RegExp(b("!mce~match["),"g"),"<b>").replace(new RegExp(b("]mce~match!"),"g"),"</b>")}var f=this,g=f._id,h=f.settings,i=f.classPrefix,j=f.state.get("text"),k=f.settings.icon,l="",m=h.shortcut,n=f.encode(h.url),o="";return k&&f.parent().classes.add("menu-has-icons"),h.image&&(l=" style=\"background-image: url('"+h.image+"')\""),m&&(m=a(m)),k=i+"ico "+i+"i-"+(f.settings.icon||"none"),o="-"!==j?'<i class="'+k+'"'+l+"></i>\xa0":"",j=e(f.encode(d(j))),n=e(f.encode(d(n))),'<div id="'+g+'" class="'+f.classes+'" tabindex="-1">'+o+("-"!==j?'<span id="'+g+'-text" class="'+i+'text">'+j+"</span>":"")+(m?'<div id="'+g+'-shortcut" class="'+i+'menu-shortcut">'+m+"</div>":"")+(h.menu?'<div class="'+i+'caret"></div>':"")+(n?'<div class="'+i+'menu-item-link">'+n+"</div>":"")+"</div>"},postRender:function(){var a=this,b=a.settings,c=b.textStyle;if("function"==typeof c&&(c=c.call(this)),c){var e=a.getEl("text");e&&e.setAttribute("style",c)}return a.on("mouseenter click",function(c){c.control===a&&(b.menu||"click"!==c.type?(a.showMenu(),c.aria&&a.menu.focus(!0)):(a.fire("select"),d.requestAnimationFrame(function(){a.parent().hideAll()})))}),a._super(),a},hover:function(){var a=this;return a.parent().items().each(function(a){a.classes.remove("selected")}),a.classes.toggle("selected",!0),a},active:function(a){return"undefined"!=typeof a&&this.aria("checked",a),this._super(a)},remove:function(){this._super(),this.menu&&this.menu.remove()}})}),g("3l",["a","2g","5"],function(a,b,c){"use strict";return function(d,e){var f,g,h=this,i=b.classPrefix;h.show=function(b,j){function k(){f&&(a(d).append('<div class="'+i+"throbber"+(e?" "+i+"throbber-inline":"")+'"></div>'),j&&j())}return h.hide(),f=!0,b?g=c.setTimeout(k,b):k(),h},h.hide=function(){var a=d.lastChild;return c.clearTimeout(g),a&&a.className.indexOf("throbber")!=-1&&a.parentNode.removeChild(a),f=!1,h}}}),g("3m",["2p","3k","3l","9"],function(a,b,c,d){"use strict";return a.extend({Defaults:{defaultType:"menuitem",border:1,layout:"stack",role:"application",bodyRole:"menu",ariaRoot:!0},init:function(a){var b=this;if(a.autohide=!0,a.constrainToViewport=!0,"function"==typeof a.items&&(a.itemsFactory=a.items,a.items=[]),a.itemDefaults)for(var c=a.items,e=c.length;e--;)c[e]=d.extend({},a.itemDefaults,c[e]);b._super(a),b.classes.add("menu")},repaint:function(){return this.classes.toggle("menu-align",!0), +this._super(),this.getEl().style.height="",this.getEl("body").style.height="",this},cancel:function(){var a=this;a.hideAll(),a.fire("select")},load:function(){function a(){e.throbber&&(e.throbber.hide(),e.throbber=null)}var b,d,e=this;d=e.settings.itemsFactory,d&&(e.throbber||(e.throbber=new c(e.getEl("body"),!0),0===e.items().length?(e.throbber.show(),e.fire("loading")):e.throbber.show(100,function(){e.items().remove(),e.fire("loading")}),e.on("hide close",a)),e.requestTime=b=(new Date).getTime(),e.settings.itemsFactory(function(c){return 0===c.length?void e.hide():void(e.requestTime===b&&(e.getEl().style.width="",e.getEl("body").style.width="",a(),e.items().remove(),e.getEl("body").innerHTML="",e.add(c),e.renderNew(),e.fire("loaded")))}))},hideAll:function(){var a=this;return this.find("menuitem").exec("hideMenu"),a._super()},preRender:function(){var a=this;return a.items().each(function(b){var c=b.settings;if(c.icon||c.image||c.selectable)return a._hasIcons=!0,!1}),a.settings.itemsFactory&&a.on("postrender",function(){a.settings.itemsFactory&&a.load()}),a._super()}})}),g("3n",["3j","3m"],function(a,b){"use strict";return a.extend({init:function(a){function b(c){for(var f=0;f<c.length;f++){if(d=c[f].selected||a.value===c[f].value)return e=e||c[f].text,g.state.set("value",c[f].value),!0;if(c[f].menu&&b(c[f].menu))return!0}}var c,d,e,f,g=this;g._super(a),a=g.settings,g._values=c=a.values,c&&("undefined"!=typeof a.value&&b(c),!d&&c.length>0&&(e=c[0].text,g.state.set("value",c[0].value)),g.state.set("menu",c)),g.state.set("text",a.text||e),g.classes.add("listbox"),g.on("select",function(b){var c=b.control;f&&(b.lastControl=f),a.multiple?c.active(!c.active()):g.value(b.control.value()),f=c})},bindStates:function(){function a(a,c){a instanceof b&&a.items().each(function(a){a.hasMenus()||a.active(a.value()===c)})}function c(a,b){var d;if(a)for(var e=0;e<a.length;e++){if(a[e].value===b)return a[e];if(a[e].menu&&(d=c(a[e].menu,b)))return d}}var d=this;return d.on("show",function(b){a(b.control,d.value())}),d.state.on("change:value",function(a){var b=c(d.state.get("menu"),a.value);b?d.text(b.text):d.text(d.settings.text)}),d._super()}})}),g("3o",["2x"],function(a){"use strict";return a.extend({Defaults:{classes:"radio",role:"radio"}})}),g("3p",["2r","2k"],function(a,b){"use strict";return a.extend({renderHtml:function(){var a=this,b=a.classPrefix;return a.classes.add("resizehandle"),"both"==a.settings.direction&&a.classes.add("resizehandle-both"),a.canFocus=!1,'<div id="'+a._id+'" class="'+a.classes+'"><i class="'+b+"ico "+b+'i-resize"></i></div>'},postRender:function(){var a=this;a._super(),a.resizeDragHelper=new b(this._id,{start:function(){a.fire("ResizeStart")},drag:function(b){"both"!=a.settings.direction&&(b.deltaX=0),a.fire("Resize",b)},stop:function(){a.fire("ResizeEnd")}})},remove:function(){return this.resizeDragHelper&&this.resizeDragHelper.destroy(),this._super()}})}),g("3q",["2r"],function(a){"use strict";function b(a){var b="";if(a)for(var c=0;c<a.length;c++)b+='<option value="'+a[c]+'">'+a[c]+"</option>";return b}return a.extend({Defaults:{classes:"selectbox",role:"selectbox",options:[]},init:function(a){var b=this;b._super(a),b.settings.size&&(b.size=b.settings.size),b.settings.options&&(b._options=b.settings.options),b.on("keydown",function(a){var c;13==a.keyCode&&(a.preventDefault(),b.parents().reverse().each(function(a){if(a.toJSON)return c=a,!1}),b.fire("submit",{data:c.toJSON()}))})},options:function(a){return arguments.length?(this.state.set("options",a),this):this.state.get("options")},renderHtml:function(){var a,c=this,d="";return a=b(c._options),c.size&&(d=' size = "'+c.size+'"'),'<select id="'+c._id+'" class="'+c.classes+'"'+d+">"+a+"</select>"},bindStates:function(){var a=this;return a.state.on("change:options",function(c){a.getEl().innerHTML=b(c.value)}),a._super()}})}),g("3r",["2r","2k","4q"],function(a,b,c){"use strict";function d(a,b,c){return a<b&&(a=b),a>c&&(a=c),a}function e(a,b,c){a.setAttribute("aria-"+b,c)}function f(a,b){var d,f,g,h,i,j;"v"==a.settings.orientation?(h="top",g="height",f="h"):(h="left",g="width",f="w"),j=a.getEl("handle"),d=(a.layoutRect()[f]||100)-c.getSize(j)[g],i=d*((b-a._minValue)/(a._maxValue-a._minValue))+"px",j.style[h]=i,j.style.height=a.layoutRect().h+"px",e(j,"valuenow",b),e(j,"valuetext",""+a.settings.previewFilter(b)),e(j,"valuemin",a._minValue),e(j,"valuemax",a._maxValue)}return a.extend({init:function(a){var b=this;a.previewFilter||(a.previewFilter=function(a){return Math.round(100*a)/100}),b._super(a),b.classes.add("slider"),"v"==a.orientation&&b.classes.add("vertical"),b._minValue=a.minValue||0,b._maxValue=a.maxValue||100,b._initValue=b.state.get("value")},renderHtml:function(){var a=this,b=a._id,c=a.classPrefix;return'<div id="'+b+'" class="'+a.classes+'"><div id="'+b+'-handle" class="'+c+'slider-handle" role="slider" tabindex="-1"></div></div>'},reset:function(){this.value(this._initValue).repaint()},postRender:function(){function a(a,b,c){return(c+a)/(b-a)}function e(a,b,c){return c*(b-a)-a}function f(b,c){function f(f){var g;g=n.value(),g=e(b,c,a(b,c,g)+.05*f),g=d(g,b,c),n.value(g),n.fire("dragstart",{value:g}),n.fire("drag",{value:g}),n.fire("dragend",{value:g})}n.on("keydown",function(a){switch(a.keyCode){case 37:case 38:f(-1);break;case 39:case 40:f(1)}})}function g(a,e,f){var g,h,i,o,p;n._dragHelper=new b(n._id,{handle:n._id+"-handle",start:function(a){g=a[j],h=parseInt(n.getEl("handle").style[k],10),i=(n.layoutRect()[m]||100)-c.getSize(f)[l],n.fire("dragstart",{value:p})},drag:function(b){var c=b[j]-g;o=d(h+c,0,i),f.style[k]=o+"px",p=a+o/i*(e-a),n.value(p),n.tooltip().text(""+n.settings.previewFilter(p)).show().moveRel(f,"bc tc"),n.fire("drag",{value:p})},stop:function(){n.tooltip().hide(),n.fire("dragend",{value:p})}})}var h,i,j,k,l,m,n=this;h=n._minValue,i=n._maxValue,"v"==n.settings.orientation?(j="screenY",k="top",l="height",m="h"):(j="screenX",k="left",l="width",m="w"),n._super(),f(h,i,n.getEl("handle")),g(h,i,n.getEl("handle"))},repaint:function(){this._super(),f(this,this.value())},bindStates:function(){var a=this;return a.state.on("change:value",function(b){f(a,b.value)}),a._super()}})}),g("3s",["2r"],function(a){"use strict";return a.extend({renderHtml:function(){var a=this;return a.classes.add("spacer"),a.canFocus=!1,'<div id="'+a._id+'" class="'+a.classes+'"></div>'}})}),g("3t",["3j","4q","a"],function(a,b,c){return a.extend({Defaults:{classes:"widget btn splitbtn",role:"button"},repaint:function(){var a,d,e=this,f=e.getEl(),g=e.layoutRect();return e._super(),a=f.firstChild,d=f.lastChild,c(a).css({width:g.w-b.getSize(d).width,height:g.h-2}),c(d).css({height:g.h-2}),e},activeMenu:function(a){var b=this;c(b.getEl().lastChild).toggleClass(b.classPrefix+"active",a)},renderHtml:function(){var a,b=this,c=b._id,d=b.classPrefix,e=b.state.get("icon"),f=b.state.get("text"),g="";return a=b.settings.image,a?(e="none","string"!=typeof a&&(a=window.getSelection?a[0]:a[1]),a=" style=\"background-image: url('"+a+"')\""):a="",e=b.settings.icon?d+"ico "+d+"i-"+e:"",f&&(b.classes.add("btn-has-text"),g='<span class="'+d+'txt">'+b.encode(f)+"</span>"),'<div id="'+c+'" class="'+b.classes+'" role="button" tabindex="-1"><button type="button" hidefocus="1" tabindex="-1">'+(e?'<i class="'+e+'"'+a+"></i>":"")+g+'</button><button type="button" class="'+d+'open" hidefocus="1" tabindex="-1">'+(b._menuBtnText?(e?"\xa0":"")+b._menuBtnText:"")+' <i class="'+d+'caret"></i></button></div>'},postRender:function(){var a=this,b=a.settings.onclick;return a.on("click",function(a){var c=a.target;if(a.control==this)for(;c;){if(a.aria&&"down"!=a.aria.key||"BUTTON"==c.nodeName&&c.className.indexOf("open")==-1)return a.stopImmediatePropagation(),void(b&&b.call(this,a));c=c.parentNode}}),delete a.settings.onclick,a._super()}})}),g("3u",["3b"],function(a){"use strict";return a.extend({Defaults:{containerClass:"stack-layout",controlClass:"stack-layout-item",endClass:"break"},isNative:function(){return!0}})}),g("3v",["2m","a","4q"],function(a,b,c){"use strict";return a.extend({Defaults:{layout:"absolute",defaults:{type:"panel"}},activateTab:function(a){var c;this.activeTabId&&(c=this.getEl(this.activeTabId),b(c).removeClass(this.classPrefix+"active"),c.setAttribute("aria-selected","false")),this.activeTabId="t"+a,c=this.getEl("t"+a),c.setAttribute("aria-selected","true"),b(c).addClass(this.classPrefix+"active"),this.items()[a].show().fire("showtab"),this.reflow(),this.items().each(function(b,c){a!=c&&b.hide()})},renderHtml:function(){var a=this,b=a._layout,c="",d=a.classPrefix;return a.preRender(),b.preRender(a),a.items().each(function(b,e){var f=a._id+"-t"+e;b.aria("role","tabpanel"),b.aria("labelledby",f),c+='<div id="'+f+'" class="'+d+'tab" unselectable="on" role="tab" aria-controls="'+b._id+'" aria-selected="false" tabIndex="-1">'+a.encode(b.settings.title)+"</div>"}),'<div id="'+a._id+'" class="'+a.classes+'" hidefocus="1" tabindex="-1"><div id="'+a._id+'-head" class="'+d+'tabs" role="tablist">'+c+'</div><div id="'+a._id+'-body" class="'+a.bodyClasses+'">'+b.renderHtml(a)+"</div></div>"},postRender:function(){var a=this;a._super(),a.settings.activeTab=a.settings.activeTab||0,a.activateTab(a.settings.activeTab),this.on("click",function(b){var c=b.target.parentNode;if(c&&c.id==a._id+"-head")for(var d=c.childNodes.length;d--;)c.childNodes[d]==b.target&&a.activateTab(d)})},initLayoutRect:function(){var a,b,d,e=this;b=c.getSize(e.getEl("head")).width,b=b<0?0:b,d=0,e.items().each(function(a){b=Math.max(b,a.layoutRect().minW),d=Math.max(d,a.layoutRect().minH)}),e.items().each(function(a){a.settings.x=0,a.settings.y=0,a.settings.w=b,a.settings.h=d,a.layoutRect({x:0,y:0,w:b,h:d})});var f=c.getSize(e.getEl("head")).height;return e.settings.minWidth=b,e.settings.minHeight=d+f,a=e._super(),a.deltaH+=f,a.innerH=a.h-a.deltaH,a}})}),g("3w",["2r","9","4q"],function(a,b,c){return a.extend({init:function(a){var b=this;b._super(a),b.classes.add("textbox"),a.multiline?b.classes.add("multiline"):(b.on("keydown",function(a){var c;13==a.keyCode&&(a.preventDefault(),b.parents().reverse().each(function(a){if(a.toJSON)return c=a,!1}),b.fire("submit",{data:c.toJSON()}))}),b.on("keyup",function(a){b.state.set("value",a.target.value)}))},repaint:function(){var a,b,c,d,e,f=this,g=0;a=f.getEl().style,b=f._layoutRect,e=f._lastRepaintRect||{};var h=document;return!f.settings.multiline&&h.all&&(!h.documentMode||h.documentMode<=8)&&(a.lineHeight=b.h-g+"px"),c=f.borderBox,d=c.left+c.right+8,g=c.top+c.bottom+(f.settings.multiline?8:0),b.x!==e.x&&(a.left=b.x+"px",e.x=b.x),b.y!==e.y&&(a.top=b.y+"px",e.y=b.y),b.w!==e.w&&(a.width=b.w-d+"px",e.w=b.w),b.h!==e.h&&(a.height=b.h-g+"px",e.h=b.h),f._lastRepaintRect=e,f.fire("repaint",{},!1),f},renderHtml:function(){var a,d,e=this,f=e.settings;return a={id:e._id,hidefocus:"1"},b.each(["rows","spellcheck","maxLength","size","readonly","min","max","step","list","pattern","placeholder","required","multiple"],function(b){a[b]=f[b]}),e.disabled()&&(a.disabled="disabled"),f.subtype&&(a.type=f.subtype),d=c.create(f.multiline?"textarea":"input",a),d.value=e.state.get("value"),d.className=e.classes,d.outerHTML},value:function(a){return arguments.length?(this.state.set("value",a),this):(this.state.get("rendered")&&this.state.set("value",this.getEl().value),this.state.get("value"))},postRender:function(){var a=this;a.getEl().value=a.state.get("value"),a._super(),a.$el.on("change",function(b){a.state.set("value",b.target.value),a.fire("change",b)})},bindStates:function(){var a=this;return a.state.on("change:value",function(b){a.getEl().value!=b.value&&(a.getEl().value=b.value)}),a.state.on("change:disabled",function(b){a.getEl().disabled=b.value}),a._super()},remove:function(){this.$el.off(),this._super()}})}),g("1f",["2d","2e","2f","2g","2h","2i","2j","2k","2l","2m","2n","2o","2p","23","24","2q","2r","2s","25","2t","2u","2v","2w","2x","2y","2z","30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f","3g","3h","3i","3j","3k","3l","3m","3n","3o","3p","3q","3r","3s","3t","3u","3v","3w"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,_,aa,ba,ca,da,ea){"use strict";var fa=function(a,b){e.add(a.split(".").pop(),b)},ga=function(a,b,c){var d,e;for(e=b.split(/[.\/]/),d=0;d<e.length-1;++d)void 0===a[e[d]]&&(a[e[d]]={}),a=a[e[d]];a[e[e.length-1]]=c,fa(b,c)},ha=function(fa){ga(fa,"ui.Selector",a),ga(fa,"ui.Collection",b),ga(fa,"ui.ReflowQueue",c),ga(fa,"ui.Control",d),ga(fa,"ui.Factory",e),ga(fa,"ui.KeyboardNavigation",f),ga(fa,"ui.Container",g),ga(fa,"ui.DragHelper",h),ga(fa,"ui.Scrollable",i),ga(fa,"ui.Panel",j),ga(fa,"ui.Movable",k),ga(fa,"ui.Resizable",l),ga(fa,"ui.FloatPanel",m),ga(fa,"ui.Window",n),ga(fa,"ui.MessageBox",o),ga(fa,"ui.Tooltip",p),ga(fa,"ui.Widget",q),ga(fa,"ui.Progress",r),ga(fa,"ui.Notification",s),ga(fa,"ui.Layout",t),ga(fa,"ui.AbsoluteLayout",u),ga(fa,"ui.Button",v),ga(fa,"ui.ButtonGroup",w),ga(fa,"ui.Checkbox",x),ga(fa,"ui.ComboBox",y),ga(fa,"ui.ColorBox",z),ga(fa,"ui.PanelButton",A),ga(fa,"ui.ColorButton",B),ga(fa,"ui.ColorPicker",C),ga(fa,"ui.Path",D),ga(fa,"ui.ElementPath",E),ga(fa,"ui.FormItem",F),ga(fa,"ui.Form",G),ga(fa,"ui.FieldSet",H),ga(fa,"ui.FilePicker",I),ga(fa,"ui.FitLayout",J),ga(fa,"ui.FlexLayout",K),ga(fa,"ui.FlowLayout",L),ga(fa,"ui.FormatControls",M),ga(fa,"ui.GridLayout",N),ga(fa,"ui.Iframe",O),ga(fa,"ui.InfoBox",P),ga(fa,"ui.Label",Q),ga(fa,"ui.Toolbar",R),ga(fa,"ui.MenuBar",S),ga(fa,"ui.MenuButton",T),ga(fa,"ui.MenuItem",U),ga(fa,"ui.Throbber",V),ga(fa,"ui.Menu",W),ga(fa,"ui.ListBox",X),ga(fa,"ui.Radio",Y),ga(fa,"ui.ResizeHandle",Z),ga(fa,"ui.SelectBox",$),ga(fa,"ui.Slider",_),ga(fa,"ui.Spacer",aa),ga(fa,"ui.SplitButton",ba),ga(fa,"ui.StackLayout",ca),ga(fa,"ui.TabPanel",da),ga(fa,"ui.TextBox",ea),ga(fa,"ui.Api",ia)},ia={appendTo:ha};return ia}),g("1",["3","4","5","6","7","8","9","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W){var X=O,Y=function(a,b,c){var d,e;for(e=b.split(/[.\/]/),d=0;d<e.length-1;++d)void 0===a[e[d]]&&(a[e[d]]={}),a=a[e[d]];a[e[e.length-1]]=c};return Y(X,"geom.Rect",a),Y(X,"util.Promise",b),Y(X,"util.Delay",c),Y(X,"Env",d),Y(X,"dom.EventUtils",e),Y(X,"dom.Sizzle",f),Y(X,"util.Tools",g),Y(X,"dom.DomQuery",h),Y(X,"html.Styles",i),Y(X,"dom.TreeWalker",j),Y(X,"html.Entities",k),Y(X,"dom.DOMUtils",l),Y(X,"dom.ScriptLoader",m),Y(X,"AddOnManager",n),Y(X,"dom.RangeUtils",o),Y(X,"html.Node",p),Y(X,"html.Schema",q),Y(X,"html.SaxParser",r),Y(X,"html.DomParser",s),Y(X,"html.Writer",t),Y(X,"html.Serializer",u),Y(X,"dom.Serializer",v),Y(X,"util.VK",w),Y(X,"dom.ControlSelection",x),Y(X,"dom.BookmarkManager",y),Y(X,"dom.Selection",z),Y(X,"Formatter",A),Y(X,"UndoManager",B),Y(X,"EditorCommands",C),Y(X,"util.URI",D),Y(X,"util.Class",E),Y(X,"util.EventDispatcher",F),Y(X,"util.Observable",G),Y(X,"WindowManager",H),Y(X,"NotificationManager",I),Y(X,"EditorObservable",J),Y(X,"Shortcuts",K),Y(X,"Editor",L),Y(X,"util.I18n",M),Y(X,"FocusManager",N),Y(X,"EditorManager",O),Y(X,"util.XHR",P),Y(X,"util.JSON",Q),Y(X,"util.JSONRequest",R),Y(X,"util.JSONP",S),Y(X,"util.LocalStorage",T),Y(X,"Compat",U),Y(X,"util.Color",V),W.appendTo(X),U.register(X),X}),g("2",[],function(){var a=this||window,b=function(b){"function"==typeof a.define&&(a.define.amd||(a.define("ephox/tinymce",[],function(){return b}),a.define("17",[],function(){return b}))),"object"==typeof module&&(module.exports=b)};return{exposeToModuleLoaders:b}}),g("0",["1","2"],function(a,b){return function(){return window.tinymce=a,window.tinyMCE=a,b.exposeToModuleLoaders(a),a}}),d("0")()}(); \ No newline at end of file From 19fa3adba8e0bacbbbac3dfc1430af06fe24e7c1 Mon Sep 17 00:00:00 2001 From: Wouter Samaey <wouter.samaey@storefront.be> Date: Wed, 25 Jul 2018 14:28:47 +0200 Subject: [PATCH 0674/1171] Added missing exception cause for better error handling Added missing exception cause for better error handling --- app/code/Magento/Translation/Model/Js/DataProvider.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Translation/Model/Js/DataProvider.php b/app/code/Magento/Translation/Model/Js/DataProvider.php index a73d4ee99b5d6..453035793a659 100644 --- a/app/code/Magento/Translation/Model/Js/DataProvider.php +++ b/app/code/Magento/Translation/Model/Js/DataProvider.php @@ -113,7 +113,8 @@ public function getData($themePath) } } catch (\Exception $e) { throw new LocalizedException( - __('Error while translating phrase "%s" in file %s.', $phrase, $filePath[0]) + __('Error while translating phrase "%s" in file %s.', $phrase, $filePath[0]), + $e ); } } From 32f8454120c287422b1b7bfdbe780cc29a0fe51a Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Thu, 26 Jul 2018 17:43:39 +0300 Subject: [PATCH 0675/1171] magento/magento2#17122 Added missing exception cause for better error handling Replace tabs with spaces --- app/code/Magento/Translation/Model/Js/DataProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Translation/Model/Js/DataProvider.php b/app/code/Magento/Translation/Model/Js/DataProvider.php index 453035793a659..7aad7c765bcd5 100644 --- a/app/code/Magento/Translation/Model/Js/DataProvider.php +++ b/app/code/Magento/Translation/Model/Js/DataProvider.php @@ -114,7 +114,7 @@ public function getData($themePath) } catch (\Exception $e) { throw new LocalizedException( __('Error while translating phrase "%s" in file %s.', $phrase, $filePath[0]), - $e + $e ); } } From a5f800c79d9528b76a1f1e7b68d3434964ae7401 Mon Sep 17 00:00:00 2001 From: Valerij Ivashchenko <likemusic@yandex.ru> Date: Mon, 11 Jun 2018 12:12:58 +0300 Subject: [PATCH 0676/1171] Don't disable "Use default" inputs according to comment above --- lib/web/mage/adminhtml/form.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/web/mage/adminhtml/form.js b/lib/web/mage/adminhtml/form.js index 487c71484e4c5..b363109d80695 100644 --- a/lib/web/mage/adminhtml/form.js +++ b/lib/web/mage/adminhtml/form.js @@ -494,7 +494,8 @@ define([ inputs.each(function (item) { // don't touch hidden inputs (and Use Default inputs too), bc they may have custom logic if ((!item.type || item.type != 'hidden') && !($(item.id + '_inherit') && $(item.id + '_inherit').checked) && //eslint-disable-line - !(currentConfig['can_edit_price'] != undefined && !currentConfig['can_edit_price']) //eslint-disable-line + !(currentConfig['can_edit_price'] != undefined && !currentConfig['can_edit_price']) && //eslint-disable-line + !item.id.endsWith('_inherit') ) { item.disabled = false; jQuery(item).removeClass('ignore-validate'); From 1b2b3228ad6b863947a1150c289d57accbc157f1 Mon Sep 17 00:00:00 2001 From: Anshu Mishra <mishra.anshu1710@gmail.com> Date: Tue, 3 Jul 2018 17:56:39 +0530 Subject: [PATCH 0677/1171] admin checkout agreement controllers refactor --- .../Controller/Adminhtml/Agreement.php | 27 +++++++++++++++++-- .../Controller/Adminhtml/Agreement/Delete.php | 6 ++--- .../Controller/Adminhtml/Agreement/Edit.php | 4 +-- .../Controller/Adminhtml/Agreement/Save.php | 4 +-- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php index 13130a4491eb2..92829feecace9 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php @@ -5,6 +5,9 @@ */ namespace Magento\CheckoutAgreements\Controller\Adminhtml; +use Magento\Framework\App\ObjectManager; +use Magento\CheckoutAgreements\Api\CheckoutAgreementsRepositoryInterface; + abstract class Agreement extends \Magento\Backend\App\Action { /** @@ -20,15 +23,35 @@ abstract class Agreement extends \Magento\Backend\App\Action * @var \Magento\Framework\Registry */ protected $_coreRegistry = null; + + /** + * @var CheckoutAgreementsRepositoryInterface + */ + protected $_agreementRepository; + + /** + * @var \Magento\CheckoutAgreements\Model\AgreementFactory + */ + protected $_agreementFactory; /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Framework\Registry $coreRegistry + * @param CheckoutAgreementsRepositoryInterface $agreementRepository + * @param \Magento\CheckoutAgreements\Model\AgreementFactory $agreementFactory * @codeCoverageIgnore */ - public function __construct(\Magento\Backend\App\Action\Context $context, \Magento\Framework\Registry $coreRegistry) - { + public function __construct( + \Magento\Backend\App\Action\Context $context, + \Magento\Framework\Registry $coreRegistry, + CheckoutAgreementsRepositoryInterface $agreementRepository = null, + \Magento\CheckoutAgreements\Model\AgreementFactory $agreementFactory = null + ) { $this->_coreRegistry = $coreRegistry; + $this->_agreementRepository = $agreementRepository ?: + ObjectManager::getInstance()->get(CheckoutAgreementsRepositoryInterface::class); + $this->_agreementFactory = $agreementFactory ?: + ObjectManager::getInstance()->get(\Magento\CheckoutAgreements\Model\AgreementFactory::class); parent::__construct($context); } diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php index 65aca6205caa4..169246e359b1e 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php @@ -14,15 +14,15 @@ class Delete extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement public function execute() { $id = (int)$this->getRequest()->getParam('id'); - $model = $this->_objectManager->get(\Magento\CheckoutAgreements\Model\Agreement::class)->load($id); - if (!$model->getId()) { + $repository = $this->_agreementRepository->get($id); + if (!$repository->getAgreementId()) { $this->messageManager->addError(__('This condition no longer exists.')); $this->_redirect('checkout/*/'); return; } try { - $model->delete(); + $repository->delete(); $this->messageManager->addSuccess(__('You deleted the condition.')); $this->_redirect('checkout/*/'); return; diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php index 73ac129bc993c..fb160f450f0dd 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php @@ -15,7 +15,7 @@ class Edit extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement public function execute() { $id = $this->getRequest()->getParam('id'); - $agreementModel = $this->_objectManager->create(\Magento\CheckoutAgreements\Model\Agreement::class); + $agreementModel = $this->_agreementFactory->create(); if ($id) { $agreementModel->load($id); @@ -26,7 +26,7 @@ public function execute() } } - $data = $this->_objectManager->get(\Magento\Backend\Model\Session::class)->getAgreementData(true); + $data = $this->_session->getAgreementData(true); if (!empty($data)) { $agreementModel->setData($data); } diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php index 25c034203620b..c8c2b7d0a40bf 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php @@ -15,7 +15,7 @@ public function execute() { $postData = $this->getRequest()->getPostValue(); if ($postData) { - $model = $this->_objectManager->get(\Magento\CheckoutAgreements\Model\Agreement::class); + $model = $this->_agreementFactory->create(); $model->setData($postData); try { @@ -36,7 +36,7 @@ public function execute() $this->messageManager->addError(__('Something went wrong while saving this condition.')); } - $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setAgreementData($postData); + $this->_session->setAgreementData($postData); $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl($this->getUrl('*'))); } } From 538e6816b097d75c47763ef58714b33ef2b56766 Mon Sep 17 00:00:00 2001 From: Anshu Mishra <mishra.anshu1710@gmail.com> Date: Fri, 13 Jul 2018 15:01:41 +0530 Subject: [PATCH 0678/1171] refactor code --- .../Controller/Adminhtml/Agreement.php | 33 +++++------------ .../Controller/Adminhtml/Agreement/Delete.php | 32 +++++++++++++++-- .../Controller/Adminhtml/Agreement/Edit.php | 33 +++++++++++++++-- .../Controller/Adminhtml/Agreement/Save.php | 36 ++++++++++++++++--- 4 files changed, 99 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php index 92829feecace9..aa6f461fc5ee2 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php @@ -5,10 +5,11 @@ */ namespace Magento\CheckoutAgreements\Controller\Adminhtml; -use Magento\Framework\App\ObjectManager; -use Magento\CheckoutAgreements\Api\CheckoutAgreementsRepositoryInterface; +use Magento\Backend\App\Action; +use Magento\Backend\App\Action\Context; +use Magento\Framework\Registry; -abstract class Agreement extends \Magento\Backend\App\Action +abstract class Agreement extends Action { /** * Authorization level of a basic admin session @@ -23,35 +24,17 @@ abstract class Agreement extends \Magento\Backend\App\Action * @var \Magento\Framework\Registry */ protected $_coreRegistry = null; - - /** - * @var CheckoutAgreementsRepositoryInterface - */ - protected $_agreementRepository; - - /** - * @var \Magento\CheckoutAgreements\Model\AgreementFactory - */ - protected $_agreementFactory; /** - * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\Framework\Registry $coreRegistry - * @param CheckoutAgreementsRepositoryInterface $agreementRepository - * @param \Magento\CheckoutAgreements\Model\AgreementFactory $agreementFactory + * @param Context $context + * @param Registry $coreRegistry * @codeCoverageIgnore */ public function __construct( - \Magento\Backend\App\Action\Context $context, - \Magento\Framework\Registry $coreRegistry, - CheckoutAgreementsRepositoryInterface $agreementRepository = null, - \Magento\CheckoutAgreements\Model\AgreementFactory $agreementFactory = null + Context $context, + Registry $coreRegistry ) { $this->_coreRegistry = $coreRegistry; - $this->_agreementRepository = $agreementRepository ?: - ObjectManager::getInstance()->get(CheckoutAgreementsRepositoryInterface::class); - $this->_agreementFactory = $agreementFactory ?: - ObjectManager::getInstance()->get(\Magento\CheckoutAgreements\Model\AgreementFactory::class); parent::__construct($context); } diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php index 169246e359b1e..144ce2b2f6bc5 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php @@ -6,15 +6,41 @@ */ namespace Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; -class Delete extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement +use Magento\CheckoutAgreements\Api\CheckoutAgreementsRepositoryInterface; +use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; +use Magento\Backend\App\Action\Context; +use Magento\Framework\Registry; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; + +class Delete extends Agreement { + /** + * @var CheckoutAgreementsRepositoryInterface + */ + private $agreementRepository; + + /** + * @param Context $context + * @param Registry $coreRegistry + * @param CheckoutAgreementsRepositoryInterface $agreementRepository + */ + public function __construct( + Context $context, + Registry $coreRegistry, + CheckoutAgreementsRepositoryInterface $agreementRepository = null + ) { + $this->agreementRepository = $agreementRepository ?: + ObjectManager::getInstance()->get(CheckoutAgreementsRepositoryInterface::class); + parent::__construct($context, $coreRegistry); + } /** * @return void */ public function execute() { $id = (int)$this->getRequest()->getParam('id'); - $repository = $this->_agreementRepository->get($id); + $repository = $this->agreementRepository->get($id); if (!$repository->getAgreementId()) { $this->messageManager->addError(__('This condition no longer exists.')); $this->_redirect('checkout/*/'); @@ -26,7 +52,7 @@ public function execute() $this->messageManager->addSuccess(__('You deleted the condition.')); $this->_redirect('checkout/*/'); return; - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $this->messageManager->addError($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addError(__('Something went wrong while deleting this condition.')); diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php index fb160f450f0dd..768ac6ee273f0 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php @@ -6,8 +6,35 @@ */ namespace Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; -class Edit extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement +use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; +use Magento\CheckoutAgreements\Model\AgreementFactory; +use Magento\Backend\App\Action\Context; +use Magento\Framework\Registry; +use Magento\Framework\App\ObjectManager; +use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; +use Magento\CheckoutAgreements\Block\Adminhtml\Agreement\Edit; + +class Edit extends Agreement { + /** + * @var AgreementFactory + */ + private $agreementFactory; + + /** + * @param Context $context + * @param Registry $coreRegistry + * @param AgreementFactory $agreementFactory + */ + public function __construct( + Context $context, + Registry $coreRegistry, + AgreementFactory $agreementFactory = null + ) { + $this->agreementFactory = $agreementFactory ?: + ObjectManager::getInstance()->get(AgreementFactory::class); + parent::__construct($context, $coreRegistry); + } /** * @return void * @SuppressWarnings(PHPMD.NPathComplexity) @@ -15,7 +42,7 @@ class Edit extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement public function execute() { $id = $this->getRequest()->getParam('id'); - $agreementModel = $this->_agreementFactory->create(); + $agreementModel = $this->agreementFactory->create(); if ($id) { $agreementModel->load($id); @@ -38,7 +65,7 @@ public function execute() $id ? __('Edit Condition') : __('New Condition') )->_addContent( $this->_view->getLayout()->createBlock( - \Magento\CheckoutAgreements\Block\Adminhtml\Agreement\Edit::class + Edit::class )->setData( 'action', $this->getUrl('checkout/*/save') diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php index c8c2b7d0a40bf..c0eef133a2a9a 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php @@ -6,8 +6,36 @@ */ namespace Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; -class Save extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement +use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; +use Magento\CheckoutAgreements\Model\AgreementFactory; +use Magento\Backend\App\Action\Context; +use Magento\Framework\Registry; +use Magento\Framework\App\ObjectManager; +use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; +use Magento\Framework\DataObject; +use Magento\Framework\Exception\LocalizedException; + +class Save extends Agreement { + /** + * @var AgreementFactory + */ + private $agreementFactory; + + /** + * @param Context $context + * @param Registry $coreRegistry + * @param AgreementFactory $agreementFactory + */ + public function __construct( + Context $context, + Registry $coreRegistry, + AgreementFactory $agreementFactory = null + ) { + $this->agreementFactory = $agreementFactory ?: + ObjectManager::getInstance()->get(AgreementFactory::class); + parent::__construct($context, $coreRegistry); + } /** * @return void */ @@ -15,11 +43,11 @@ public function execute() { $postData = $this->getRequest()->getPostValue(); if ($postData) { - $model = $this->_agreementFactory->create(); + $model = $this->agreementFactory->create(); $model->setData($postData); try { - $validationResult = $model->validateData(new \Magento\Framework\DataObject($postData)); + $validationResult = $model->validateData(new DataObject($postData)); if ($validationResult !== true) { foreach ($validationResult as $message) { $this->messageManager->addError($message); @@ -30,7 +58,7 @@ public function execute() $this->_redirect('checkout/*/'); return; } - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $this->messageManager->addError($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addError(__('Something went wrong while saving this condition.')); From f4d837d1548a2f8d964419f35e9572c7f31a36b0 Mon Sep 17 00:00:00 2001 From: Anshu Mishra <mishra.anshu1710@gmail.com> Date: Tue, 17 Jul 2018 17:31:13 +0530 Subject: [PATCH 0679/1171] fixes as CI issues --- .../Controller/Adminhtml/Agreement/Edit.php | 5 ++--- .../Controller/Adminhtml/Agreement/Save.php | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php index 768ac6ee273f0..8bec3b581cd54 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php @@ -11,8 +11,7 @@ use Magento\Backend\App\Action\Context; use Magento\Framework\Registry; use Magento\Framework\App\ObjectManager; -use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; -use Magento\CheckoutAgreements\Block\Adminhtml\Agreement\Edit; +use Magento\CheckoutAgreements\Block\Adminhtml\Agreement\Edit as BlockEdit; class Edit extends Agreement { @@ -65,7 +64,7 @@ public function execute() $id ? __('Edit Condition') : __('New Condition') )->_addContent( $this->_view->getLayout()->createBlock( - Edit::class + BlockEdit::class )->setData( 'action', $this->getUrl('checkout/*/save') diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php index c0eef133a2a9a..05a16d3dd4264 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php @@ -11,7 +11,6 @@ use Magento\Backend\App\Action\Context; use Magento\Framework\Registry; use Magento\Framework\App\ObjectManager; -use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; use Magento\Framework\DataObject; use Magento\Framework\Exception\LocalizedException; From d49227401db8f926c14b8ef88845a34bb6f08367 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Tue, 31 Jul 2018 09:41:47 +0300 Subject: [PATCH 0680/1171] Fixed variable name & repository usage --- .../Controller/Adminhtml/Agreement/Delete.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php index 144ce2b2f6bc5..f7b178df99624 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php @@ -40,15 +40,15 @@ public function __construct( public function execute() { $id = (int)$this->getRequest()->getParam('id'); - $repository = $this->agreementRepository->get($id); - if (!$repository->getAgreementId()) { + $agreement = $this->agreementRepository->get($id); + if (!$agreement->getAgreementId()) { $this->messageManager->addError(__('This condition no longer exists.')); $this->_redirect('checkout/*/'); return; } try { - $repository->delete(); + $this->agreementRepository->delete($agreement); $this->messageManager->addSuccess(__('You deleted the condition.')); $this->_redirect('checkout/*/'); return; From 53b1aa5e67b3884bb31e14e8b53d75098d0b3926 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Wed, 1 Aug 2018 13:34:29 -0500 Subject: [PATCH 0681/1171] MC-3496: Static block widget is broken in inline mode Wrap in try/catch --- lib/web/mage/adminhtml/wysiwyg/widget.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/widget.js b/lib/web/mage/adminhtml/wysiwyg/widget.js index d609b3f244f1e..759c6711fcbe7 100644 --- a/lib/web/mage/adminhtml/wysiwyg/widget.js +++ b/lib/web/mage/adminhtml/wysiwyg/widget.js @@ -275,7 +275,11 @@ define([ } if (e != undefined && e.id) { //eslint-disable-line eqeqeq - widgetCode = Base64.idDecode(e.id); + try { + widgetCode = Base64.idDecode(e.id); + } catch (ex) { + return false; + } if (widgetCode.indexOf('{{widget') !== -1) { this.optionValues = new Hash({}); From 6f9656f2aec8dd9130994af1700643d04c8fc16c Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Wed, 1 Aug 2018 14:53:47 -0500 Subject: [PATCH 0682/1171] MC-3305: Architectural Review for MC-1416 Move events out to separate module and use camel case --- lib/web/mage/adminhtml/wysiwyg/events.js | 22 ++++++ .../wysiwyg/tiny_mce/tinymce4Adapter.js | 70 ++++++++----------- 2 files changed, 50 insertions(+), 42 deletions(-) create mode 100644 lib/web/mage/adminhtml/wysiwyg/events.js diff --git a/lib/web/mage/adminhtml/wysiwyg/events.js b/lib/web/mage/adminhtml/wysiwyg/events.js new file mode 100644 index 0000000000000..36c680a7ffe46 --- /dev/null +++ b/lib/web/mage/adminhtml/wysiwyg/events.js @@ -0,0 +1,22 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([], function () { + 'use strict'; + + return { + afterInitialization: 'afterInitialization', + afterChangeContent: 'afterChangeContent', + afterUndo: 'afterUndo', + afterPaste: 'afterPaste', + beforeSetContent: 'beforeSetContent', + afterSetContent: 'afterSetContent', + afterSave: 'afterSave', + afterOpenFileBrowser: 'afterOpenFileBrowser', + afterFormSubmit: 'afterFormSubmit', + afterBlur: 'afterBlur', + afterFocus: 'afterFocus' + }; +}); diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index 320a0cc76bc23..6c952683aa1c9 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -10,28 +10,16 @@ define([ 'underscore', 'tinymce4', 'mage/adminhtml/events', + 'mage/adminhtml/wysiwyg/events', 'mage/translate', 'prototype', 'jquery/ui' -], function (jQuery, _, tinyMCE4, varienGlobalEvents) { +], function (jQuery, _, tinyMCE4, varienGlobalEvents, wysiwygEvents) { 'use strict'; var tinyMce4Wysiwyg = Class.create(); tinyMce4Wysiwyg.prototype = { - EVENT: { - AFTER_INITIALIZATION: 'wysiwygEditorInitialized', - AFTER_CONTENT_CHANGE: 'tinymceChange', - AFTER_UNDO: 'tinymceUndo', - AFTER_PASTE: 'tinymcePaste', - BEFORE_SET_CONTENT: 'tinymceBeforeSetContent', - AFTER_SET_CONTENT: 'tinymceSetContent', - AFTER_SAVE: 'tinymceSaveContent', - AFTER_OPEN_FILE_BROWSER: 'open_browser_callback', - AFTER_FORM_SUBMIT: 'formSubmit', - AFTER_BLUR: 'tinymceBlur', - AFTER_FOCUS: 'tinymceFocus' - }, mediaBrowserOpener: null, mediaBrowserTargetElementId: null, magentoVariablesPlugin: null, @@ -55,13 +43,12 @@ define([ 'onUndo' ); - varienGlobalEvents.attachEventHandler(this.EVENT.AFTER_CONTENT_CHANGE, this.onChangeContent); - varienGlobalEvents.attachEventHandler(this.EVENT.BEFORE_SET_CONTENT, this.beforeSetContent); - varienGlobalEvents.attachEventHandler(this.EVENT.AFTER_SET_CONTENT, this.updateTextArea); - varienGlobalEvents.attachEventHandler(this.EVENT.AFTER_SAVE, this.saveContent); - varienGlobalEvents.attachEventHandler(this.EVENT.AFTER_UNDO, this.onUndo); - varienGlobalEvents.attachEventHandler(this.EVENT.AFTER_OPEN_FILE_BROWSER, this.openFileBrowser); - varienGlobalEvents.attachEventHandler(this.EVENT.AFTER_FORM_SUBMIT, this.onFormValidation); + varienGlobalEvents.attachEventHandler('tinymceChange', this.onChangeContent); + varienGlobalEvents.attachEventHandler('tinymceBeforeSetContent', this.beforeSetContent); + varienGlobalEvents.attachEventHandler('tinymceSetContent', this.updateTextArea); + varienGlobalEvents.attachEventHandler('tinymceSaveContent', this.saveContent); + varienGlobalEvents.attachEventHandler('tinymceUndo', this.onUndo); + varienGlobalEvents.attachEventHandler('open_browser_callback', this.openFileBrowser); if (typeof tinyMceEditors === 'undefined') { window.tinyMceEditors = $H({}); @@ -124,7 +111,7 @@ define([ tinyMCE4.ui.FloatPanel.zIndex = settings.toolbarZIndex; } - varienGlobalEvents.removeEventHandler(this.EVENT.AFTER_CONTENT_CHANGE, this.onChangeContent); + varienGlobalEvents.removeEventHandler('tinymceChange', this.onChangeContent); } jQuery.when.apply(jQuery, deferreds).done(function () { @@ -186,8 +173,7 @@ define([ */ getSettings: function () { var settings, - eventBus = this.eventBus, - wysiwygAdapter = this; + eventBus = this.eventBus; settings = { selector: '#' + this.getId(), @@ -208,44 +194,44 @@ define([ var onChange; editor.on('BeforeSetContent', function (evt) { - varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.BEFORE_SET_CONTENT, evt); - eventBus.fireEvent(wysiwygAdapter.EVENT.BEFORE_SET_CONTENT); + varienGlobalEvents.fireEvent('tinymceBeforeSetContent', evt); + eventBus.fireEvent(wysiwygEvents.beforeSetContent); }); editor.on('SaveContent', function (evt) { - varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.AFTER_SAVE, evt); - eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_SAVE); + varienGlobalEvents.fireEvent('tinymceSaveContent', evt); + eventBus.fireEvent(wysiwygEvents.afterSave); }); editor.on('paste', function (evt) { - varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.AFTER_PASTE, evt); - eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_PASTE); + varienGlobalEvents.fireEvent('tinymcePaste', evt); + eventBus.fireEvent(wysiwygEvents.afterPaste); }); editor.on('PostProcess', function (evt) { - varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.AFTER_SAVE, evt); - eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_SAVE); + varienGlobalEvents.fireEvent('tinymceSaveContent', evt); + eventBus.fireEvent(wysiwygEvents.afterSave); }); editor.on('undo', function (evt) { - varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.AFTER_UNDO, evt); - eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_UNDO); + varienGlobalEvents.fireEvent('tinymceUndo', evt); + eventBus.fireEvent(wysiwygEvents.afterUndo); }); editor.on('focus', function () { - eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_FOCUS); + eventBus.fireEvent(wysiwygEvents.afterFocus); }); editor.on('blur', function () { - eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_BLUR); + eventBus.fireEvent(wysiwygEvents.afterBlur); }); /** * @param {*} evt */ onChange = function (evt) { - varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.AFTER_CONTENT_CHANGE, evt); - eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_CONTENT_CHANGE); + varienGlobalEvents.fireEvent('tinymceChange', evt); + eventBus.fireEvent(wysiwygEvents.afterChangeContent); }; editor.on('Change', onChange); @@ -256,8 +242,8 @@ define([ }); editor.on('init', function (args) { - varienGlobalEvents.fireEvent(wysiwygAdapter.EVENT.AFTER_INITIALIZATION, args.target); - eventBus.fireEvent(wysiwygAdapter.EVENT.AFTER_INITIALIZATION); + varienGlobalEvents.fireEvent('wysiwygEditorInitialized', args.target); + eventBus.fireEvent(wysiwygEvents.afterInitialization); }); } }; @@ -278,12 +264,12 @@ define([ * @param {*} w */ settings['file_browser_callback'] = function (fieldName, url, objectType, w) { - varienGlobalEvents.fireEvent(this.EVENT.AFTER_OPEN_FILE_BROWSER, { + varienGlobalEvents.fireEvent('open_browser_callback', { win: w, type: objectType, field: fieldName }); - }.bind(this); + }; } if (this.config.width) { From 217b0e2c64b11e4b4d6eed03d376d4d8ab3ea8f4 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Wed, 1 Aug 2018 15:03:08 -0500 Subject: [PATCH 0683/1171] MC-3496: Static block widget is broken in inline mode Explain try/catch wrap --- lib/web/mage/adminhtml/wysiwyg/widget.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/mage/adminhtml/wysiwyg/widget.js b/lib/web/mage/adminhtml/wysiwyg/widget.js index 759c6711fcbe7..68206fdec6201 100644 --- a/lib/web/mage/adminhtml/wysiwyg/widget.js +++ b/lib/web/mage/adminhtml/wysiwyg/widget.js @@ -275,6 +275,7 @@ define([ } if (e != undefined && e.id) { //eslint-disable-line eqeqeq + // attempt to Base64-decode id on selected node; exception is thrown if it is in fact not a widget node try { widgetCode = Base64.idDecode(e.id); } catch (ex) { From d0a21654a0a0652cec57d3eff1ff892c2d9f31cd Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Wed, 1 Aug 2018 17:02:11 -0500 Subject: [PATCH 0684/1171] MQE-1112: Bump MFTF version in Magento - fixing unstable tests --- .../Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml index e1e6cd4738757..2452e7b36be00 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml @@ -176,7 +176,6 @@ <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2" /> <click stepKey="changeShippingAddress" selector="{{CheckoutShippingMethodsSection.shipHereButton}}"/> - <waitForElementVisible stepKey="waitForShippingMethodLoaderVisible" selector="{{CheckoutShippingMethodsSection.shippingMethodLoader}}" time="30"/> <waitForElementNotVisible stepKey="waitForShippingMethodLoaderNotVisible" selector="{{CheckoutShippingMethodsSection.shippingMethodLoader}}" time="30"/> <click stepKey="selectFirstShippingMethod2" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> <waitForElement stepKey="waitForShippingMethodSelect2" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> From 5a2c46c3139cb60c16785de0b75c16a02c6dcf67 Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Thu, 2 Aug 2018 11:33:00 +0300 Subject: [PATCH 0685/1171] MAGETWO-60823: Cannot unset (set empty) category image on store view level if image defined on all store views - Fixed - Modified test --- .../Controller/Adminhtml/Category/Save.php | 2 +- .../Adminhtml/Category/SaveTest.php | 77 +++++++------------ .../css/source/components/_file-uploader.less | 8 ++ 3 files changed, 36 insertions(+), 51 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php index cc03ab870739b..aa9ae88754b6a 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php @@ -280,7 +280,7 @@ public function imagePreprocessing($data) continue; } - $data[$attributeCode] = false; + $data[$attributeCode] = ''; } return $data; 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 f6a586950afdc..53f7fa0ed7e63 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 @@ -5,8 +5,6 @@ */ namespace Magento\Catalog\Test\Unit\Controller\Adminhtml\Category; -use Magento\Catalog\Controller\Adminhtml\Category\Save as Model; - /** * Class SaveTest * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -463,9 +461,25 @@ public function dataProviderExecute() */ public function imagePreprocessingDataProvider() { + $dataWithImage = [ + 'image' => 'path.jpg', + 'name' => 'category', + 'description' => '', + 'parent' => 0 + ]; + $expectedSameAsDataWithImage = $dataWithImage; + + $dataWithoutImage = [ + 'name' => 'category', + 'description' => '', + 'parent' => 0 + ]; + $expectedIfDataWithoutImage = $dataWithoutImage; + $expectedIfDataWithoutImage['image'] = ''; + return [ - [['attribute1' => null, 'attribute2' => 123]], - [['attribute2' => 123]] + 'categoryPostData contains image' => [$dataWithImage, $expectedSameAsDataWithImage], + 'categoryPostData doesn\'t contain image' => [$dataWithoutImage, $expectedIfDataWithoutImage], ]; } @@ -473,8 +487,9 @@ public function imagePreprocessingDataProvider() * @dataProvider imagePreprocessingDataProvider * * @param array $data + * @param array $expected */ - public function testImagePreprocessingWithoutValue($data) + public function testImagePreprocessing($data, $expected) { $eavConfig = $this->createPartialMock(\Magento\Eav\Model\Config::class, ['getEntityType']); @@ -484,49 +499,17 @@ public function testImagePreprocessingWithoutValue($data) $collection = new \Magento\Framework\DataObject(['attribute_collection' => [ new \Magento\Framework\DataObject([ - 'attribute_code' => 'attribute1', + 'attribute_code' => 'image', 'backend' => $imageBackendModel ]), new \Magento\Framework\DataObject([ - 'attribute_code' => 'attribute2', + 'attribute_code' => 'name', '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->createPartialMock(\Magento\Eav\Model\Config::class, ['getEntityType']); - - $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', + 'attribute_code' => 'level', 'backend' => new \Magento\Framework\DataObject() - ]) + ]), ]]); $eavConfig->expects($this->once()) @@ -534,18 +517,12 @@ public function testImagePreprocessingWithValue() ->with(\Magento\Catalog\Api\Data\CategoryAttributeInterface::ENTITY_TYPE_CODE) ->will($this->returnValue($collection)); - $model = $this->objectManager->getObject(Model::class, [ + $model = $this->objectManager->getObject(\Magento\Catalog\Controller\Adminhtml\Category\Save::class, [ 'eavConfig' => $eavConfig ]); - $result = $model->imagePreprocessing([ - 'attribute1' => 'somevalue', - 'attribute2' => null - ]); + $result = $model->imagePreprocessing($data); - $this->assertEquals([ - 'attribute1' => 'somevalue', - 'attribute2' => null - ], $result); + $this->assertEquals($expected, $result); } } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_file-uploader.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_file-uploader.less index 79e3975a41bc6..9a88d5e3593b9 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_file-uploader.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_file-uploader.less @@ -74,6 +74,14 @@ box-shadow: 0 0 0 1px @file-uploader-preview-focus__color; } } + + &:disabled { + + .file-uploader-button { + cursor: default; + opacity: .5; + pointer-events: none; + } + } } } From cbcba2540909f2e298a9b098006742b3d57901af Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Thu, 2 Aug 2018 13:07:41 +0300 Subject: [PATCH 0686/1171] MAGETWO-93188: Product date attribute: An error displays for default date greater than 12 with local using dd/mm/yyyy - Fixed - Added test --- .../Magento/Eav/Model/Entity/Attribute.php | 9 +- .../Eav/Model/Entity/AttributeTest.php | 100 ++++++++++++++++++ 2 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php diff --git a/app/code/Magento/Eav/Model/Entity/Attribute.php b/app/code/Magento/Eav/Model/Entity/Attribute.php index 1b20858a0c424..c605f3ce17e30 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute.php @@ -8,6 +8,7 @@ use Magento\Framework\Api\AttributeValueFactory; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Stdlib\DateTime; use Magento\Framework\Stdlib\DateTime\DateTimeFormatterInterface; /** @@ -284,12 +285,10 @@ public function beforeSave() // save default date value as timestamp if ($hasDefaultValue) { - $format = $this->_localeDate->getDateFormat( - \IntlDateFormatter::SHORT - ); try { - $defaultValue = $this->dateTimeFormatter->formatObject(new \DateTime($defaultValue), $format); - $this->setDefaultValue($defaultValue); + $locale = $this->_localeResolver->getLocale(); + $defaultValue = $this->_localeDate->date($defaultValue, $locale, false, false); + $this->setDefaultValue($defaultValue->format(DateTime::DATETIME_PHP_FORMAT)); } catch (\Exception $e) { throw new LocalizedException(__('The default date is invalid. Verify the date and try again.')); } diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php new file mode 100644 index 0000000000000..07dea7249563a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Eav\Model\Entity; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Locale\ResolverInterface; + +class AttributeTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Attribute + */ + private $attribute; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var ResolverInterface + */ + private $_localeResolver; + + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->attribute = $this->objectManager->get(Attribute::class); + $this->_localeResolver = $this->objectManager->get(ResolverInterface::class); + } + + protected function tearDown() + { + $this->attribute = null; + $this->objectManager = null; + $this->_localeResolver = null; + } + + /** + * @param string $defaultValue + * @param string $backendType + * @param string $locale + * @param string $expected + * @dataProvider beforeSaveDataProvider + * @throws + */ + public function testBeforeSave($defaultValue, $backendType, $locale, $expected) + { + $this->attribute->setDefaultValue($defaultValue); + $this->attribute->setBackendType($backendType); + $this->_localeResolver->setLocale($locale); + $this->attribute->beforeSave(); + + $this->assertEquals($expected, $this->attribute->getDefaultValue()); + } + + public function beforeSaveDataProvider() + { + return [ + ['21/07/18', 'datetime', 'en_AU', '2018-07-21 00:00:00'], + ['07/21/18', 'datetime', 'en_US', '2018-07-21 00:00:00'], + ['21/07/18', 'datetime', 'fr_FR', '2018-07-21 00:00:00'], + ['21/07/18', 'datetime', 'de_DE', '2018-07-21 00:00:00'], + ['21/07/18', 'datetime', 'uk_UA', '2018-07-21 00:00:00'], + ['100.50', 'decimal', 'en_US', '100.50'], + ['100,50', 'decimal', 'uk_UA', '100.5'], + ]; + } + + /** + * @param string $defaultValue + * @param string $backendType + * @param string $locale + * @param string $expected + * @dataProvider beforeSaveErrorDataDataProvider + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testBeforeSaveErrorData($defaultValue, $backendType, $locale, $expected) + { + $this->attribute->setDefaultValue($defaultValue); + $this->attribute->setBackendType($backendType); + $this->_localeResolver->setLocale($locale); + $this->attribute->beforeSave(); + + $this->expectExceptionMessage($expected); + } + + public function beforeSaveErrorDataDataProvider() + { + return [ + 'wrong date for Australia' => ['32/38', 'datetime', 'en_AU', 'Invalid default date'], + 'wrong date for States' => ['32/38', 'datetime', 'en_US', 'Invalid default date'], + 'wrong decimal separator for US' => ['100,50', 'decimal', 'en_US', 'Invalid default decimal value'], + 'wrong decimal separator for UA' => ['100.50', 'decimal', 'uk_UA', 'Invalid default decimal value'], + ]; + } +} From 9b84332089239785a608a9a4bd034264a14b8497 Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Thu, 2 Aug 2018 13:31:47 +0300 Subject: [PATCH 0687/1171] MAGETWO-93188: Product date attribute: An error displays for default date greater than 12 with local using dd/mm/yyyy - Fixed static test strict type missing --- .../testsuite/Magento/Eav/Model/Entity/AttributeTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php index 07dea7249563a..11696aa1e0b98 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Eav\Model\Entity; use Magento\TestFramework\Helper\Bootstrap; From 66e72856d811172142d61f8b1d0767c2775e4ba7 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Thu, 2 Aug 2018 13:58:45 +0300 Subject: [PATCH 0688/1171] MAGETWO-91737: Customer Address attribute value length is still validated when min/max length fields are not displayed at the backend --- .../Customer/Controller/Address/FormPost.php | 9 ++- .../Customer/Model/Metadata/Form/Text.php | 65 ++++++++++------ .../Unit/Controller/Address/FormPostTest.php | 6 +- .../Unit/Model/Metadata/Form/TextTest.php | 18 ++++- .../Magento/Eav/Model/Attribute/Data/Text.php | 46 +++++++---- .../Model/Attribute/Data/MultilineTest.php | 54 +++++++++---- .../Unit/Model/Attribute/Data/TextTest.php | 77 +++++++++++++------ .../Ui/DataProvider/EavValidationRules.php | 43 +++++++++-- .../DataProvider/EavValidationRulesTest.php | 25 ++++-- .../Customer/Controller/AddressTest.php | 4 +- .../Controller/Adminhtml/IndexTest.php | 3 - 11 files changed, 250 insertions(+), 100 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Address/FormPost.php b/app/code/Magento/Customer/Controller/Address/FormPost.php index 21334f51b1752..60e1f6eb172f5 100644 --- a/app/code/Magento/Customer/Controller/Address/FormPost.php +++ b/app/code/Magento/Customer/Controller/Address/FormPost.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Customer\Controller\Address; use Magento\Customer\Api\AddressRepositoryInterface; @@ -197,17 +198,17 @@ public function execute() try { $address = $this->_extractAddress(); $this->_addressRepository->save($address); - $this->messageManager->addSuccess(__('You saved the address.')); + $this->messageManager->addSuccessMessage(__('You saved the address.')); $url = $this->_buildUrl('*/*/index', ['_secure' => true]); return $this->resultRedirectFactory->create()->setUrl($this->_redirect->success($url)); } catch (InputException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); foreach ($e->getErrors() as $error) { - $this->messageManager->addError($error->getMessage()); + $this->messageManager->addErrorMessage($error->getMessage()); } } catch (\Exception $e) { $redirectUrl = $this->_buildUrl('*/*/index'); - $this->messageManager->addException($e, __('We can\'t save the address.')); + $this->messageManager->addExceptionMessage($e, __('We can\'t save the address.')); } $url = $redirectUrl; diff --git a/app/code/Magento/Customer/Model/Metadata/Form/Text.php b/app/code/Magento/Customer/Model/Metadata/Form/Text.php index 9ef6df0a6d36e..7eedd3bec8dda 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/Text.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/Text.php @@ -5,8 +5,10 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Customer\Model\Metadata\Form; +use Magento\Customer\Api\Data\AttributeMetadataInterface; use Magento\Framework\Api\ArrayObjectSearch; class Text extends AbstractData @@ -19,7 +21,7 @@ class Text extends AbstractData /** * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute + * @param AttributeMetadataInterface $attribute * @param \Magento\Framework\Locale\ResolverInterface $localeResolver * @param string $value * @param string $entityTypeCode @@ -29,7 +31,7 @@ class Text extends AbstractData public function __construct( \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, \Psr\Log\LoggerInterface $logger, - \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute, + AttributeMetadataInterface $attribute, \Magento\Framework\Locale\ResolverInterface $localeResolver, $value, $entityTypeCode, @@ -72,26 +74,7 @@ public function validateValue($value) return true; } - // validate length - $length = $this->_string->strlen(trim($value)); - - $validateRules = $attribute->getValidationRules(); - - $minTextLength = ArrayObjectSearch::getArrayElementByName( - $validateRules, - 'min_text_length' - ); - if ($minTextLength !== null && $length < $minTextLength) { - $errors[] = __('"%1" length must be equal or greater than %2 characters.', $label, $minTextLength); - } - - $maxTextLength = ArrayObjectSearch::getArrayElementByName( - $validateRules, - 'max_text_length' - ); - if ($maxTextLength !== null && $length > $maxTextLength) { - $errors[] = __('"%1" length must be equal or less than %2 characters.', $label, $maxTextLength); - } + $errors = $this->validateLength($value, $attribute, $errors); $result = $this->_validateInputRule($value); if ($result !== true) { @@ -127,4 +110,42 @@ public function outputValue($format = \Magento\Customer\Model\Metadata\ElementFa { return $this->_applyOutputFilter($this->_value); } + + /** + * Length validation + * + * @param mixed $value + * @param AttributeMetadataInterface $attribute + * @param array $errors + * @return array + */ + private function validateLength($value, AttributeMetadataInterface $attribute, array $errors): array + { + // validate length + $label = __($attribute->getStoreLabel()); + + $length = $this->_string->strlen(trim($value)); + + $validateRules = $attribute->getValidationRules(); + + if (!empty(ArrayObjectSearch::getArrayElementByName($validateRules, 'input_validation'))) { + $minTextLength = ArrayObjectSearch::getArrayElementByName( + $validateRules, + 'min_text_length' + ); + if ($minTextLength !== null && $length < $minTextLength) { + $errors[] = __('"%1" length must be equal or greater than %2 characters.', $label, $minTextLength); + } + + $maxTextLength = ArrayObjectSearch::getArrayElementByName( + $validateRules, + 'max_text_length' + ); + if ($maxTextLength !== null && $length > $maxTextLength) { + $errors[] = __('"%1" length must be equal or less than %2 characters.', $label, $maxTextLength); + } + } + + return $errors; + } } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php index 4ad1b5cbc96bd..a2766d42403ba 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php @@ -549,7 +549,7 @@ public function testExecute( ->willReturnSelf(); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You saved the address.')) ->willReturnSelf(); @@ -640,7 +640,7 @@ public function testExecuteInputException() ->willThrowException(new InputException(__('InputException'))); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('InputException') ->willReturnSelf(); @@ -703,7 +703,7 @@ public function testExecuteException() ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addException') + ->method('addExceptionMessage') ->with($exception, __('We can\'t save the address.')) ->willReturnSelf(); diff --git a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/TextTest.php b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/TextTest.php index b95987cba1dcf..292d46a936092 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/TextTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/TextTest.php @@ -5,8 +5,10 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Customer\Test\Unit\Model\Metadata\Form; +use Magento\Customer\Api\Data\ValidationRuleInterface; use Magento\Customer\Model\Metadata\Form\Text; class TextTest extends AbstractFormTestCase @@ -111,7 +113,7 @@ public function validateValueRequiredDataProvider() */ public function testValidateValueLength($value, $expected) { - $minTextLengthRule = $this->getMockBuilder(\Magento\Customer\Api\Data\ValidationRuleInterface::class) + $minTextLengthRule = $this->getMockBuilder(ValidationRuleInterface::class) ->disableOriginalConstructor() ->setMethods(['getName', 'getValue']) ->getMockForAbstractClass(); @@ -122,7 +124,7 @@ public function testValidateValueLength($value, $expected) ->method('getValue') ->will($this->returnValue(4)); - $maxTextLengthRule = $this->getMockBuilder(\Magento\Customer\Api\Data\ValidationRuleInterface::class) + $maxTextLengthRule = $this->getMockBuilder(ValidationRuleInterface::class) ->disableOriginalConstructor() ->setMethods(['getName', 'getValue']) ->getMockForAbstractClass(); @@ -133,7 +135,19 @@ public function testValidateValueLength($value, $expected) ->method('getValue') ->will($this->returnValue(8)); + $inputValidationRule = $this->getMockBuilder(ValidationRuleInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getName', 'getValue']) + ->getMockForAbstractClass(); + $inputValidationRule->expects($this->any()) + ->method('getName') + ->will($this->returnValue('input_validation')); + $inputValidationRule->expects($this->any()) + ->method('getValue') + ->will($this->returnValue('other')); + $validationRules = [ + 'input_validation' => $inputValidationRule, 'min_text_length' => $minTextLengthRule, 'max_text_length' => $maxTextLengthRule, ]; diff --git a/app/code/Magento/Eav/Model/Attribute/Data/Text.php b/app/code/Magento/Eav/Model/Attribute/Data/Text.php index 242f4e98108c9..f81fb2affd3b3 100644 --- a/app/code/Magento/Eav/Model/Attribute/Data/Text.php +++ b/app/code/Magento/Eav/Model/Attribute/Data/Text.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Eav\Model\Attribute\Data; use Magento\Framework\App\RequestInterface; @@ -76,19 +77,9 @@ public function validateValue($value) return true; } - // validate length - $length = $this->_string->strlen(trim($value)); - - $validateRules = $attribute->getValidateRules(); - if (!empty($validateRules['min_text_length']) && $length < $validateRules['min_text_length']) { - $label = __($attribute->getStoreLabel()); - $v = $validateRules['min_text_length']; - $errors[] = __('"%1" length must be equal or greater than %2 characters.', $label, $v); - } - if (!empty($validateRules['max_text_length']) && $length > $validateRules['max_text_length']) { - $label = __($attribute->getStoreLabel()); - $v = $validateRules['max_text_length']; - $errors[] = __('"%1" length must be equal or less than %2 characters.', $label, $v); + $result = $this->validateLength($attribute, $value); + if (count($result) !== 0) { + $errors = array_merge($errors, $result); } $result = $this->_validateInputRule($value); @@ -142,4 +133,33 @@ public function outputValue($format = \Magento\Eav\Model\AttributeDataFactory::O return $value; } + + /** + * Validates value length by attribute rules + * + * @param \Magento\Eav\Model\Attribute $attribute + * @param string $value + * @return array errors + */ + private function validateLength(\Magento\Eav\Model\Attribute $attribute, $value): array + { + $errors = []; + $length = $this->_string->strlen(trim($value)); + $validateRules = $attribute->getValidateRules(); + + if (!empty($validateRules['input_validation'])) { + if (!empty($validateRules['min_text_length']) && $length < $validateRules['min_text_length']) { + $label = __($attribute->getStoreLabel()); + $v = $validateRules['min_text_length']; + $errors[] = __('"%1" length must be equal or greater than %2 characters.', $label, $v); + } + if (!empty($validateRules['max_text_length']) && $length > $validateRules['max_text_length']) { + $label = __($attribute->getStoreLabel()); + $v = $validateRules['max_text_length']; + $errors[] = __('"%1" length must be equal or less than %2 characters.', $label, $v); + } + } + + return $errors; + } } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/MultilineTest.php b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/MultilineTest.php index f628d52f6647f..5eeef79df6d2f 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/MultilineTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/MultilineTest.php @@ -3,8 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Eav\Test\Unit\Model\Attribute\Data; +use Magento\Framework\Locale\ResolverInterface; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; + class MultilineTest extends \PHPUnit\Framework\TestCase { /** @@ -13,15 +17,18 @@ class MultilineTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Stdlib\StringUtils */ protected $stringMock; protected function setUp() { - $timezoneMock = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); + /** @var TimezoneInterface $timezoneMock */ + $timezoneMock = $this->createMock(TimezoneInterface::class); + /** @var \Psr\Log\LoggerInterface $loggerMock */ $loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); - $localeResolverMock = $this->createMock(\Magento\Framework\Locale\ResolverInterface::class); + /** @var ResolverInterface $localeResolverMock */ + $localeResolverMock = $this->createMock(ResolverInterface::class); $this->stringMock = $this->createMock(\Magento\Framework\Stdlib\StringUtils::class); $this->model = new \Magento\Eav\Model\Attribute\Data\Multiline( @@ -33,7 +40,7 @@ protected function setUp() } /** - * @covers \Magento\Eav\Model\Attribute\Data\Multiline::extractValue + * @covers \Magento\Eav\Model\Attribute\Data\Multiline::extractValue * * @param mixed $param * @param mixed $expectedResult @@ -41,11 +48,15 @@ protected function setUp() */ public function testExtractValue($param, $expectedResult) { + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\App\RequestInterface $requestMock */ $requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Eav\Model\Attribute $attributeMock */ $attributeMock = $this->createMock(\Magento\Eav\Model\Attribute::class); $requestMock->expects($this->once())->method('getParam')->will($this->returnValue($param)); - $attributeMock->expects($this->once())->method('getAttributeCode')->will($this->returnValue('attributeCode')); + $attributeMock->expects($this->once()) + ->method('getAttributeCode') + ->will($this->returnValue('attributeCode')); $this->model->setAttribute($attributeMock); $this->assertEquals($expectedResult, $this->model->extractValue($requestMock)); @@ -69,7 +80,7 @@ public function extractValueDataProvider() } /** - * @covers \Magento\Eav\Model\Attribute\Data\Multiline::outputValue + * @covers \Magento\Eav\Model\Attribute\Data\Multiline::outputValue * * @param string $format * @param mixed $expectedResult @@ -77,9 +88,13 @@ public function extractValueDataProvider() */ public function testOutputValue($format, $expectedResult) { + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Model\AbstractModel $entityMock */ $entityMock = $this->createMock(\Magento\Framework\Model\AbstractModel::class); - $entityMock->expects($this->once())->method('getData')->will($this->returnValue("value1\nvalue2")); + $entityMock->expects($this->once()) + ->method('getData') + ->will($this->returnValue("value1\nvalue2")); + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Eav\Model\Attribute $attributeMock */ $attributeMock = $this->createMock(\Magento\Eav\Model\Attribute::class); $this->model->setEntity($entityMock); @@ -113,8 +128,8 @@ public function outputValueDataProvider() } /** - * @covers \Magento\Eav\Model\Attribute\Data\Multiline::validateValue - * @covers \Magento\Eav\Model\Attribute\Data\Text::validateValue + * @covers \Magento\Eav\Model\Attribute\Data\Multiline::validateValue + * @covers \Magento\Eav\Model\Attribute\Data\Text::validateValue * * @param mixed $value * @param bool $isAttributeRequired @@ -124,14 +139,23 @@ public function outputValueDataProvider() */ public function testValidateValue($value, $isAttributeRequired, $rules, $expectedResult) { + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Model\AbstractModel $entityMock */ $entityMock = $this->createMock(\Magento\Framework\Model\AbstractModel::class); - $entityMock->expects($this->any())->method('getDataUsingMethod')->will($this->returnValue("value1\nvalue2")); + $entityMock->expects($this->any()) + ->method('getDataUsingMethod') + ->will($this->returnValue("value1\nvalue2")); + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Eav\Model\Attribute $attributeMock */ $attributeMock = $this->createMock(\Magento\Eav\Model\Attribute::class); $attributeMock->expects($this->any())->method('getMultilineCount')->will($this->returnValue(2)); $attributeMock->expects($this->any())->method('getValidateRules')->will($this->returnValue($rules)); - $attributeMock->expects($this->any())->method('getStoreLabel')->will($this->returnValue('Label')); - $attributeMock->expects($this->any())->method('getIsRequired')->will($this->returnValue($isAttributeRequired)); + $attributeMock->expects($this->any()) + ->method('getStoreLabel') + ->will($this->returnValue('Label')); + + $attributeMock->expects($this->any()) + ->method('getIsRequired') + ->will($this->returnValue($isAttributeRequired)); $this->stringMock->expects($this->any())->method('strlen')->will($this->returnValue(5)); @@ -159,7 +183,7 @@ public function validateValueDataProvider() 'expectedResult' => true, ], [ - 'value' => ['value1', 'value2'], + 'value' => ['value1', 'value2'], 'isAttributeRequired' => false, 'rules' => [], 'expectedResult' => true, @@ -167,13 +191,13 @@ public function validateValueDataProvider() [ 'value' => 'value', 'isAttributeRequired' => false, - 'rules' => ['max_text_length' => 3], + 'rules' => ['input_validation' => 'other', 'max_text_length' => 3], 'expectedResult' => ['"Label" length must be equal or less than 3 characters.'], ], [ 'value' => 'value', 'isAttributeRequired' => false, - 'rules' => ['min_text_length' => 10], + 'rules' => ['input_validation' => 'other', 'min_text_length' => 10], 'expectedResult' => ['"Label" length must be equal or greater than 10 characters.'], ], [ diff --git a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php index 36eac0bfab27b..217a04045b939 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Eav\Test\Unit\Model\Attribute\Data; class TextTest extends \PHPUnit\Framework\TestCase @@ -19,30 +20,17 @@ protected function setUp() $logger = $this->createMock(\Psr\Log\LoggerInterface::class); $helper = $this->createMock(\Magento\Framework\Stdlib\StringUtils::class); - $attributeData = [ - 'store_label' => 'Test', - 'attribute_code' => 'test', - 'is_required' => 1, - 'validate_rules' => ['min_text_length' => 0, 'max_text_length' => 0, 'input_validation' => 0], - ]; - - $attributeClass = \Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class; - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $eavTypeFactory = $this->createMock(\Magento\Eav\Model\Entity\TypeFactory::class); - $arguments = $objectManagerHelper->getConstructArguments( - $attributeClass, - ['eavTypeFactory' => $eavTypeFactory, 'data' => $attributeData] - ); - - /** @var $attribute \Magento\Eav\Model\Entity\Attribute\AbstractAttribute| - * \PHPUnit_Framework_MockObject_MockObject - */ - $attribute = $this->getMockBuilder($attributeClass) - ->setMethods(['_init']) - ->setConstructorArgs($arguments) - ->getMock(); $this->_model = new \Magento\Eav\Model\Attribute\Data\Text($locale, $logger, $localeResolver, $helper); - $this->_model->setAttribute($attribute); + $this->_model->setAttribute( + $this->createAttribute( + [ + 'store_label' => 'Test', + 'attribute_code' => 'test', + 'is_required' => 1, + 'validate_rules' => ['min_text_length' => 0, 'max_text_length' => 0, 'input_validation' => 0], + ] + ) + ); } protected function tearDown() @@ -64,4 +52,47 @@ public function testValidateValueInteger() $result = $this->_model->validateValue($inputValue); $this->assertEquals($expectedResult, [(string)$result[0]]); } + + public function testWithoutLengthValidation() + { + $expectedResult = true; + $defaultAttributeData = [ + 'store_label' => 'Test', + 'attribute_code' => 'test', + 'is_required' => 1, + 'validate_rules' => ['min_text_length' => 0, 'max_text_length' => 0, 'input_validation' => 0], + ]; + + $defaultAttributeData['validate_rules']['min_text_length'] = 2; + $this->_model->setAttribute($this->createAttribute($defaultAttributeData)); + $this->assertEquals($expectedResult, $this->_model->validateValue('t')); + + $defaultAttributeData['validate_rules']['max_text_length'] = 3; + $this->_model->setAttribute($this->createAttribute($defaultAttributeData)); + $this->assertEquals($expectedResult, $this->_model->validateValue('test')); + } + + /** + * @param array $attributeData + * @return \Magento\Eav\Model\Attribute + */ + protected function createAttribute($attributeData): \Magento\Eav\Model\Entity\Attribute\AbstractAttribute + { + $attributeClass = \Magento\Eav\Model\Attribute::class; + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $eavTypeFactory = $this->createMock(\Magento\Eav\Model\Entity\TypeFactory::class); + $arguments = $objectManagerHelper->getConstructArguments( + $attributeClass, + ['eavTypeFactory' => $eavTypeFactory, 'data' => $attributeData] + ); + + /** @var $attribute \Magento\Eav\Model\Entity\Attribute\AbstractAttribute| + * \PHPUnit_Framework_MockObject_MockObject + */ + $attribute = $this->getMockBuilder($attributeClass) + ->setMethods(['_init']) + ->setConstructorArgs($arguments) + ->getMock(); + return $attribute; + } } diff --git a/app/code/Magento/Ui/DataProvider/EavValidationRules.php b/app/code/Magento/Ui/DataProvider/EavValidationRules.php index 12e345e1fa12c..6e2bb3866e947 100644 --- a/app/code/Magento/Ui/DataProvider/EavValidationRules.php +++ b/app/code/Magento/Ui/DataProvider/EavValidationRules.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Ui\DataProvider; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; @@ -31,25 +32,51 @@ class EavValidationRules */ public function build(AbstractAttribute $attribute, array $data) { - $validation = []; + $validations = []; if (isset($data['required']) && $data['required'] == 1) { - $validation = array_merge($validation, ['required-entry' => true]); + $validations = array_merge($validations, ['required-entry' => true]); } if ($attribute->getFrontendInput() === 'price') { - $validation = array_merge($validation, ['validate-zero-or-greater' => true]); + $validations = array_merge($validations, ['validate-zero-or-greater' => true]); } if ($attribute->getValidateRules()) { - $validation = array_merge($validation, $attribute->getValidateRules()); + $validations = array_merge($validations, $this->clipLengthRules($attribute->getValidateRules())); } + return $this->aggregateRules($validations); + } + + /** + * @param array $validations + * @return array + */ + private function aggregateRules(array $validations): array + { $rules = []; - foreach ($validation as $type => $ruleName) { - $rule = [$type => $ruleName]; + foreach ($validations as $type => $ruleValue) { + $rule = [$type => $ruleValue]; if ($type === 'input_validation') { - $rule = isset($this->validationRules[$ruleName]) ? $this->validationRules[$ruleName] : []; + $rule = $this->validationRules[$ruleValue] ?? []; + } + if (count($rule) !== 0) { + $key = key($rule); + $rules[$key] = $rule[$key]; } - $rules = array_merge($rules, $rule); } + return $rules; + } + /** + * @param array $rules + * @return array + */ + private function clipLengthRules(array $rules): array + { + if (empty($rules['input_validation'])) { + unset( + $rules['min_text_length'], + $rules['max_text_length'] + ); + } return $rules; } } diff --git a/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php b/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php index debcde4765fc7..b9a5262e64eb8 100644 --- a/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php +++ b/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Ui\Test\Unit\DataProvider; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; @@ -70,11 +71,25 @@ public function buildDataProvider() ['', ['input_validation' => 'email'], [], ['validate-email' => true]], ['', ['input_validation' => 'date'], [], ['validate-date' => true]], ['', ['input_validation' => 'other'], [], []], - ['', ['max_text_length' => '254'], ['required' => 1], ['max_text_length' => 254, 'required-entry' => true]], - ['', ['max_text_length' => '254', 'min_text_length' => 1], [], - ['max_text_length' => 254, 'min_text_length' => 1]], - ['', ['max_text_length' => '254', 'input_validation' => 'date'], [], - ['max_text_length' => 254, 'validate-date' => true]], + ['', ['max_text_length' => '254'], ['required' => 1], ['required-entry' => true]], + [ + '', + ['input_validation' => 'other', 'max_text_length' => '254'], + ['required' => 1], + ['max_text_length' => 254, 'required-entry' => true] + ], + [ + '', + ['input_validation' => 'other', 'max_text_length' => '254', 'min_text_length' => 1], + [], + ['max_text_length' => 254, 'min_text_length' => 1] + ], + [ + '', + ['max_text_length' => '254', 'input_validation' => 'date'], + [], + ['max_text_length' => 254, 'validate-date' => true] + ], ]; } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php index 4c30adb6894e2..5357a2b0eb0c2 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php @@ -150,8 +150,8 @@ public function testFailedFormPostAction() $this->equalTo( [ 'One or more input exceptions have occurred.', - '"street" is required. Enter and try again.', - '"city" is required. Enter and try again.', + '"street" is required. Enter and try again.', + '"city" is required. Enter and try again.', ] ), \Magento\Framework\Message\MessageInterface::TYPE_ERROR diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index b942365d64a75..02181ce6e2989 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -717,12 +717,9 @@ public function testValidateCustomerWithAddressFailure() $this->assertContains('{"error":true,"messages":', $body); $this->assertContains('\"First Name\" is a required value', $body); - $this->assertContains('\"First Name\" length must be equal or greater than 1 characters', $body); $this->assertContains('\"Last Name\" is a required value.', $body); - $this->assertContains('\"Last Name\" length must be equal or greater than 1 characters.', $body); $this->assertContains('\"Country\" is a required value.', $body); $this->assertContains('\"Phone Number\" is a required value.', $body); - $this->assertContains('\"Phone Number\" length must be equal or greater than 1 characters.', $body); } /** From 052471d724e3b8a182e57d755444573722040584 Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Thu, 2 Aug 2018 15:39:59 +0300 Subject: [PATCH 0689/1171] MAGETWO-93712: [2.3] Free Shipping Cart Price Rule not working when UPS shipping method is enabled and Free Shipping is set to "For matching items only" - Fixed integration test --- app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php | 2 ++ .../integration/testsuite/Magento/Ups/Model/CarrierTest.php | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php index 9d7bfff5e559c..fac733a3fc5fe 100644 --- a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php +++ b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php @@ -451,6 +451,8 @@ protected function _updateFreeMethodQuote($request) } } } + } else { + $price = 0; } /** diff --git a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php index 334cb336862a9..7cf58a1ff2daa 100644 --- a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php +++ b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Ups\Model; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Quote\Model\Quote\Address\RateRequestFactory; + class CarrierTest extends \PHPUnit\Framework\TestCase { /** @@ -14,7 +17,7 @@ class CarrierTest extends \PHPUnit\Framework\TestCase protected function setUp() { - $this->carrier = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + $this->carrier = Bootstrap::getObjectManager()->create( \Magento\Ups\Model\Carrier::class ); } From ab1e70d56e9dbd6e525e6598b370b099a0a4e113 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Thu, 2 Aug 2018 15:58:17 +0300 Subject: [PATCH 0690/1171] MAGETWO-91329: 'Use Default Value' checkboxes in Design section of a category are not checked by default --- .../app/code/Magento/Ui/base/js/lib/ko/bind/i18n.test.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/i18n.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/i18n.test.js index 0f50cc2b6b9b1..201959a2598fd 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/i18n.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/i18n.test.js @@ -38,14 +38,18 @@ define([ /** Stub */ turnOffInlineTranslation = function () { manageInlineTranslation(false); - }; + }, + + storedConfig; beforeEach(function () { + storedConfig = context.config.config; $(document.body).append(elWithStaticText); $(document.body).append(elWithVariable); }); afterEach(function () { + context.config.config = storedConfig; elWithStaticText.remove(); elWithVariable.remove(); }); From 65740bc452b2ff5d7b2ae8a6bff326954dadad24 Mon Sep 17 00:00:00 2001 From: Navarr Barnier <navarr@mediotype.com> Date: Wed, 25 Jul 2018 09:55:41 -0400 Subject: [PATCH 0691/1171] Allow 3rd party modules to perform actions after totals calculation by returning the storage.post promise, third party modules can perform additional actions by adding .done/.fail or .always tasks to the request promise by creating a Javascript mixin for the totals processor. --- .../frontend/web/js/model/cart/totals-processor/default.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/totals-processor/default.js b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/totals-processor/default.js index e269462047748..0e94232786c65 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/totals-processor/default.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/totals-processor/default.js @@ -38,7 +38,7 @@ define([ payload.addressInformation['shipping_carrier_code'] = quote.shippingMethod()['carrier_code']; } - storage.post( + return storage.post( serviceUrl, JSON.stringify(payload), false ).done(function (result) { var data = { @@ -96,7 +96,7 @@ define([ ) { quote.setTotals(cartCache.get('totals')); } else { - loadFromServer(address); + return loadFromServer(address); } } }; From 203749c4fa49fcd9fea028af3c46058d2e294537 Mon Sep 17 00:00:00 2001 From: Navarr Barnier <navarr@mediotype.com> Date: Thu, 26 Jul 2018 08:15:07 -0400 Subject: [PATCH 0692/1171] Update test to expect a deferral instead of undefined --- .../js/model/cart/totals-processor/default.test.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js index 47518bef19e56..952dbcf1955d7 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js @@ -119,14 +119,15 @@ define([ 'data_id': 1 }) ); + var deferral = new $.Deferred(); spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'get'); spyOn(mocks['mage/storage'], 'post').and.callFake(function () { data.shippingMethodCode = mocks['Magento_Checkout/js/model/quote'].shippingMethod()['method_code']; data.shippingCarrierCode = mocks['Magento_Checkout/js/model/quote'].shippingMethod()['carrier_code']; - return new $.Deferred().resolve(result); + return deferral.resolve(result); }); - expect(defaultProcessor.estimateTotals(address)).toBeUndefined(); + expect(defaultProcessor.estimateTotals(address)).toBe(deferral); expect(mocks['Magento_Checkout/js/model/quote'].setTotals).toHaveBeenCalledWith(totals); expect(mocks['Magento_Checkout/js/model/totals'].isLoading.calls.argsFor(0)[0]).toBe(true); expect(mocks['Magento_Checkout/js/model/totals'].isLoading.calls.argsFor(1)[0]).toBe(false); @@ -143,10 +144,11 @@ define([ }) ); spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'get'); + var deferral = new $.Deferred(); spyOn(mocks['mage/storage'], 'post').and.callFake(function () { - return new $.Deferred().reject('Error Message'); + return deferral.reject('Error Message'); }); - expect(defaultProcessor.estimateTotals(address)).toBeUndefined(); + expect(defaultProcessor.estimateTotals(address)).toBe(deferral); expect(mocks['Magento_Checkout/js/model/totals'].isLoading.calls.argsFor(0)[0]).toBe(true); expect(mocks['Magento_Checkout/js/model/totals'].isLoading.calls.argsFor(1)[0]).toBe(false); expect(mocks['mage/storage'].post).toHaveBeenCalled(); From 3a795e7e95135ff570a9afadc3b56ec95e05c76a Mon Sep 17 00:00:00 2001 From: Navarr Barnier <navarr@mediotype.com> Date: Fri, 27 Jul 2018 08:04:08 -0400 Subject: [PATCH 0693/1171] Move new variables to top of scope to fall in line with convention --- .../frontend/js/model/cart/totals-processor/default.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js index 952dbcf1955d7..af5d1ccf39eb0 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js @@ -113,13 +113,13 @@ define([ }); it('estimateTotals if data wasn\'t cached and request was successfully sent', function () { + var deferral = new $.Deferred(); spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'isChanged').and.returnValue(true); spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue( ko.observable({ 'data_id': 1 }) ); - var deferral = new $.Deferred(); spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'get'); spyOn(mocks['mage/storage'], 'post').and.callFake(function () { data.shippingMethodCode = mocks['Magento_Checkout/js/model/quote'].shippingMethod()['method_code']; @@ -137,6 +137,7 @@ define([ }); it('estimateTotals if data wasn\'t cached and request returns error', function () { + var deferral = new $.Deferred(); spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'isChanged').and.returnValue(true); spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue( ko.observable({ @@ -144,7 +145,6 @@ define([ }) ); spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'get'); - var deferral = new $.Deferred(); spyOn(mocks['mage/storage'], 'post').and.callFake(function () { return deferral.reject('Error Message'); }); From cde493837d66c5c9ac5784f87ff6cd209cb4e566 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Mon, 30 Jul 2018 11:42:56 +0300 Subject: [PATCH 0694/1171] Fixed code style issues --- .../frontend/js/model/cart/totals-processor/default.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js index af5d1ccf39eb0..9323024bc888c 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js @@ -114,6 +114,7 @@ define([ it('estimateTotals if data wasn\'t cached and request was successfully sent', function () { var deferral = new $.Deferred(); + spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'isChanged').and.returnValue(true); spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue( ko.observable({ @@ -138,6 +139,7 @@ define([ it('estimateTotals if data wasn\'t cached and request returns error', function () { var deferral = new $.Deferred(); + spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'isChanged').and.returnValue(true); spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue( ko.observable({ From fff874377e7c6c7ad9b0059754a4d7ff3a0ddf6a Mon Sep 17 00:00:00 2001 From: Navarr Barnier <me@navarr.me> Date: Mon, 30 Jul 2018 08:28:00 -0400 Subject: [PATCH 0695/1171] Fix styling issues --- .../frontend/js/model/cart/totals-processor/default.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js index 9323024bc888c..184118f1d02ec 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js @@ -114,7 +114,7 @@ define([ it('estimateTotals if data wasn\'t cached and request was successfully sent', function () { var deferral = new $.Deferred(); - + spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'isChanged').and.returnValue(true); spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue( ko.observable({ @@ -139,7 +139,7 @@ define([ it('estimateTotals if data wasn\'t cached and request returns error', function () { var deferral = new $.Deferred(); - + spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'isChanged').and.returnValue(true); spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue( ko.observable({ From 66158e463fe4e3f8025081409c63aa52d31aed74 Mon Sep 17 00:00:00 2001 From: Ji Lu <> Date: Thu, 2 Aug 2018 10:04:19 -0500 Subject: [PATCH 0696/1171] MC-111: Admin should be able to add default video for simple products MC-206: Admin should be able to remove default video for simple products - Updated mftf tests --- .../AdminAddDefaultVideoSimpleProductTest.xml | 2 +- ...minRemoveDefaultVideoSimpleProductTest.xml | 2 +- .../ConfigAdminAccountSharingActionGroup.xml | 26 ++++++++++++------- .../Test/Mftf/Data/SystemConfigData.xml | 23 ++++++++++++++++ .../Test/Mftf/Metadata/system_config-meta.xml | 21 +++++++++++++++ 5 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 app/code/Magento/Config/Test/Mftf/Data/SystemConfigData.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Metadata/system_config-meta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml index 623a2ebadbfec..13dab55cb0771 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="ConfigAdminAccountSharingActionGroup" stepKey="allowAdminShareAccount"/> + <actionGroup ref="EnableAdminAccountSharingActionGroup" stepKey="enableAdminAccountSharing"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml index fa564c4bbb474..63e7abd09c30d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="ConfigAdminAccountSharingActionGroup" stepKey="allowAdminShareAccount"/> + <actionGroup ref="EnableAdminAccountSharingActionGroup" stepKey="enableAdminAccountSharing"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml index 51155423e62bf..72d26ba0e6c19 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml @@ -8,15 +8,21 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> -<actionGroup name="ConfigAdminAccountSharingActionGroup"> - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/system_config/edit/section/admin/" stepKey="navigateToConfigurationPage" /> - <waitForPageLoad stepKey="wait1"/> - <conditionalClick stepKey="expandSecurityTab" selector="{{AdminSection.SecurityTab}}" dependentSelector="{{AdminSection.CheckIfTabExpand}}" visible="true" /> - <waitForElementVisible selector="{{AdminSection.AdminAccountSharing}}" stepKey="waitForAdminAccountSharingDrpDown" /> - <uncheckOption selector="{{AdminSection.EnableSystemValue}}" stepKey="uncheckUseSystemValue"/> - <selectOption selector="{{AdminSection.AdminAccountSharing}}" userInput="Yes" stepKey="selectYes"/> - <click selector="{{AdminSection.SecurityTab}}" stepKey="clollapseSecurityTab" /> - <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig" /> -</actionGroup> + <actionGroup name="ConfigAdminAccountSharingActionGroup"> + <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/system_config/edit/section/admin/" stepKey="navigateToConfigurationPage" /> + <waitForPageLoad stepKey="wait1"/> + <conditionalClick stepKey="expandSecurityTab" selector="{{AdminSection.SecurityTab}}" dependentSelector="{{AdminSection.CheckIfTabExpand}}" visible="true" /> + <waitForElementVisible selector="{{AdminSection.AdminAccountSharing}}" stepKey="waitForAdminAccountSharingDrpDown" /> + <uncheckOption selector="{{AdminSection.EnableSystemValue}}" stepKey="uncheckUseSystemValue"/> + <selectOption selector="{{AdminSection.AdminAccountSharing}}" userInput="Yes" stepKey="selectYes"/> + <click selector="{{AdminSection.SecurityTab}}" stepKey="clollapseSecurityTab" /> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig" /> + </actionGroup> + <actionGroup name="EnableAdminAccountSharingActionGroup"> + <createData stepKey="setConfig" entity="EnableAdminAccountSharing"/> + </actionGroup> + <actionGroup name="DisableAdminAccountSharingActionGroup"> + <createData stepKey="setConfig" entity="DisableAdminAccountSharing"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/Data/SystemConfigData.xml b/app/code/Magento/Config/Test/Mftf/Data/SystemConfigData.xml new file mode 100644 index 0000000000000..75dc19dc99c8e --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Data/SystemConfigData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="AdminAccountSharingYes" type="admin_account_sharing_value"> + <data key="value">Yes</data> + </entity> + <entity name="AdminAccountSharingNo" type="admin_account_sharing_value"> + <data key="value">No</data> + </entity> + <entity name="EnableAdminAccountSharing" type="admin_account_sharing_config"> + <requiredEntity type="admin_account_sharing_value">AdminAccountSharingYes</requiredEntity> + </entity> + <entity name="DisableAdminAccountSharing" type="admin_account_sharing_config"> + <requiredEntity type="admin_account_sharing_value">AdminAccountSharingNo</requiredEntity> + </entity> +</entities> diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/system_config-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/system_config-meta.xml new file mode 100644 index 0000000000000..37b8414d1f396 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Metadata/system_config-meta.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + <operation name="AdminAccountSharingConfig" dataType="admin_account_sharing_config" type="create" auth="adminFormKey" url="/admin/system_config/save/section/admin/" method="POST"> + <object key="groups" dataType="admin_account_sharing_config"> + <object key="security" dataType="admin_account_sharing_config"> + <object key="fields" dataType="admin_account_sharing_config"> + <object key="admin_account_sharing" dataType="admin_account_sharing_value"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> +</operations> From de295f709a44a56ce5c2a9c5c80826b6ee6730cf Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Thu, 2 Aug 2018 18:21:08 +0300 Subject: [PATCH 0697/1171] MAGETWO-93706: [2.3] Can't reorder sales order with configurable product - Added correct link to a parent product entity --- .../OrderedProductAvailabilityChecker.php | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/ConfigurableProductSales/Model/Order/Reorder/OrderedProductAvailabilityChecker.php b/app/code/Magento/ConfigurableProductSales/Model/Order/Reorder/OrderedProductAvailabilityChecker.php index 42d7d91fb90e8..8ef1e24125981 100644 --- a/app/code/Magento/ConfigurableProductSales/Model/Order/Reorder/OrderedProductAvailabilityChecker.php +++ b/app/code/Magento/ConfigurableProductSales/Model/Order/Reorder/OrderedProductAvailabilityChecker.php @@ -5,11 +5,12 @@ */ namespace Magento\ConfigurableProductSales\Model\Order\Reorder; -use Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityCheckerInterface; -use Magento\Sales\Model\Order\Item; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\App\ResourceConnection; use Magento\Framework\EntityManager\MetadataPool; +use Magento\Sales\Model\Order\Item; +use Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityCheckerInterface; use Magento\Store\Model\Store; /** @@ -27,16 +28,24 @@ class OrderedProductAvailabilityChecker implements OrderedProductAvailabilityChe */ private $metadataPool; + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + /** * @param ResourceConnection $resourceConnection * @param MetadataPool $metadataPool + * @param ProductRepositoryInterface $productRepository */ public function __construct( ResourceConnection $resourceConnection, - MetadataPool $metadataPool + MetadataPool $metadataPool, + ProductRepositoryInterface $productRepository ) { $this->resourceConnection = $resourceConnection; $this->metadataPool = $metadataPool; + $this->productRepository = $productRepository; } /** @@ -48,7 +57,9 @@ public function isAvailable(Item $item) $superAttribute = $buyRequest->getData()['super_attribute'] ?? []; $connection = $this->getConnection(); $select = $connection->select(); - $orderItemParentId = $item->getParentItem()->getProductId(); + $linkField = $this->getMetadata()->getLinkField(); + $parentItem = $this->productRepository->getById($item->getParentItem()->getProductId()); + $orderItemParentId = $parentItem->getData($linkField); $select->from( ['cpe' => $this->resourceConnection->getTableName('catalog_product_entity')], ['cpe.entity_id'] @@ -67,7 +78,7 @@ public function isAvailable(Item $item) ['cpid' . $attributeId => $this->resourceConnection->getTableName('catalog_product_entity_int')], sprintf( 'cpe.%1$s = cpid%2$d.%1$s AND cpid%2$d.attribute_id = %2$d AND cpid%2$d.store_id = %3$d', - $this->getMetadata()->getLinkField(), + $linkField, $attributeId, Store::DEFAULT_STORE_ID ), @@ -77,7 +88,7 @@ public function isAvailable(Item $item) ['cpis' . $attributeId => $this->resourceConnection->getTableName('catalog_product_entity_int')], sprintf( 'cpe.%1$s = cpis%2$d.%1$s AND cpis%2$d.attribute_id = %2$d AND cpis%2$d.store_id = %3$d', - $this->getMetadata()->getLinkField(), + $linkField, $attributeId, $item->getStoreId() ), From 3d535787f482a380fedf94049a855c2e078885b9 Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Thu, 2 Aug 2018 18:28:24 +0300 Subject: [PATCH 0698/1171] MAGETWO-91601: [2.3] Shipping Progress Dates are wrong for Tracking Popup - Specified full datetime for date formatting --- .../Shipping/view/frontend/templates/tracking/progress.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Shipping/view/frontend/templates/tracking/progress.phtml b/app/code/Magento/Shipping/view/frontend/templates/tracking/progress.phtml index 84126d8eeb636..237eba09ff802 100644 --- a/app/code/Magento/Shipping/view/frontend/templates/tracking/progress.phtml +++ b/app/code/Magento/Shipping/view/frontend/templates/tracking/progress.phtml @@ -23,7 +23,7 @@ $track = $block->getData('track'); </thead> <tbody> <?php foreach ($track->getProgressdetail() as $detail): ?> - <?php $detailDate = (!empty($detail['deliverydate']) ? $parentBlock->formatDeliveryDate($detail['deliverydate']) : ''); ?> + <?php $detailDate = (!empty($detail['deliverydate']) ? $parentBlock->formatDeliveryDate($detail['deliverydate'] . ' ' . $detail['deliverytime']) : ''); ?> <?php $detailTime = (!empty($detail['deliverytime']) ? $parentBlock->formatDeliveryTime($detail['deliverytime'], $detail['deliverydate']) : ''); ?> <tr> <td data-th="<?= $block->escapeHtml(__('Location')) ?>" class="col location"> From 63ac4bfe052fe885a7d98a98304932ee962befc7 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Fri, 15 Jun 2018 16:28:18 +0300 Subject: [PATCH 0699/1171] MAGETWO-91673: Not Visible Custom Address Attributes Showing on Checkout --- .../Checkout/Model/DefaultConfigProvider.php | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php index b5727bf8f365e..16f13511001e9 100644 --- a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php +++ b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php @@ -8,12 +8,14 @@ use Magento\Catalog\Helper\Product\ConfigurationPool; use Magento\Checkout\Helper\Data as CheckoutHelper; use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Customer\Api\AddressMetadataInterface; use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository; use Magento\Customer\Model\Context as CustomerContext; use Magento\Customer\Model\Session as CustomerSession; use Magento\Customer\Model\Url as CustomerUrlManager; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Http\Context as HttpContext; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Data\Form\FormKey; use Magento\Framework\Locale\FormatInterface as LocaleFormat; use Magento\Framework\UrlInterface; @@ -159,6 +161,11 @@ class DefaultConfigProvider implements ConfigProviderInterface */ protected $urlBuilder; + /** + * @var AddressMetadataInterface + */ + private $addressMetadata; + /** * @param CheckoutHelper $checkoutHelper * @param Session $checkoutSession @@ -186,6 +193,7 @@ class DefaultConfigProvider implements ConfigProviderInterface * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Quote\Api\PaymentMethodManagementInterface $paymentMethodManagement * @param UrlInterface $urlBuilder + * @param AddressMetadataInterface $addressMetadata * @codeCoverageIgnore * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -215,7 +223,8 @@ public function __construct( \Magento\Shipping\Model\Config $shippingMethodConfig, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Quote\Api\PaymentMethodManagementInterface $paymentMethodManagement, - UrlInterface $urlBuilder + UrlInterface $urlBuilder, + AddressMetadataInterface $addressMetadata = null ) { $this->checkoutHelper = $checkoutHelper; $this->checkoutSession = $checkoutSession; @@ -243,6 +252,7 @@ public function __construct( $this->storeManager = $storeManager; $this->paymentMethodManagement = $paymentMethodManagement; $this->urlBuilder = $urlBuilder; + $this->addressMetadata = $addressMetadata ?: ObjectManager::getInstance()->get(AddressMetadataInterface::class); } /** @@ -324,11 +334,34 @@ private function getCustomerData() $customerData = $customer->__toArray(); foreach ($customer->getAddresses() as $key => $address) { $customerData['addresses'][$key]['inline'] = $this->getCustomerAddressInline($address); + if ($address->getCustomAttributes()) { + $customerData['addresses'][$key]['custom_attributes'] = $this->filterNotVisibleAttributes( + $customerData['addresses'][$key]['custom_attributes'] + ); + } } } return $customerData; } + /** + * Filter not visible on storefront custom attributes. + * + * @param array $attributes + * @return array + */ + private function filterNotVisibleAttributes(array $attributes) + { + $attributesMetadata = $this->addressMetadata->getAllAttributesMetadata(); + foreach ($attributesMetadata as $attributeMetadata) { + if (!$attributeMetadata->isVisible()) { + unset($attributes[$attributeMetadata->getAttributeCode()]); + } + } + + return $attributes; + } + /** * Set additional customer address data * From 80fc4aa81a948ebf8031fc30a50612ca61f04fd5 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Tue, 12 Jun 2018 11:26:32 +0300 Subject: [PATCH 0700/1171] MAGETWO-93715: [2.3] Delete action in grid could be sent multiple times --- .../Adminhtml/Product/MassDelete.php | 2 +- .../Ui/Component/MassAction/Filter.php | 14 +- .../Adminhtml/Index/MassAssignGroupTest.php | 85 +++++++---- .../Adminhtml/Index/MassDeleteTest.php | 137 +++++++++++++++--- .../Adminhtml/Index/MassSubscribeTest.php | 74 +++++++--- .../_files/five_repository_customers.php | 49 +++++++ .../five_repository_customers_rollback.php | 29 ++++ 7 files changed, 315 insertions(+), 75 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/five_repository_customers.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/five_repository_customers_rollback.php diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php index f32c6edd57394..366356597fdfb 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php @@ -66,7 +66,7 @@ public function execute() } if ($productDeleted) { - $this->messageManager->addSuccessMessage( + $this->messageManager->addSuccess( __('A total of %1 record(s) have been deleted.', $productDeleted) ); } diff --git a/app/code/Magento/Ui/Component/MassAction/Filter.php b/app/code/Magento/Ui/Component/MassAction/Filter.php index 6877303f5c491..a8ed5d901d860 100644 --- a/app/code/Magento/Ui/Component/MassAction/Filter.php +++ b/app/code/Magento/Ui/Component/MassAction/Filter.php @@ -99,14 +99,12 @@ public function getCollection(AbstractDb $collection) throw new LocalizedException(__('An item needs to be selected. Select and try again.')); } } - /** @var \Magento\Customer\Model\ResourceModel\Customer\Collection $collection */ - $idsArray = $this->getFilterIds(); - if (!empty($idsArray)) { - $collection->addFieldToFilter( - $collection->getIdFieldName(), - ['in' => $idsArray] - ); - } + + $collection->addFieldToFilter( + $collection->getIdFieldName(), + ['in' => $this->getFilterIds()] + ); + return $collection; } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroupTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroupTest.php index ef5b4cae5ff16..8ee12fa1aa8d5 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroupTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroupTest.php @@ -3,16 +3,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Customer\Controller\Adminhtml\Index; -use Magento\TestFramework\Helper\Bootstrap; +use Magento\Backend\Model\Session; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\AbstractBackendController; /** * @magentoAppArea adminhtml */ -class MassAssignGroupTest extends \Magento\TestFramework\TestCase\AbstractBackendController +class MassAssignGroupTest extends AbstractBackendController { /** * Base controller URL @@ -29,9 +34,7 @@ class MassAssignGroupTest extends \Magento\TestFramework\TestCase\AbstractBacken protected function setUp() { parent::setUp(); - $this->customerRepository = Bootstrap::getObjectManager()->get( - \Magento\Customer\Api\CustomerRepositoryInterface::class - ); + $this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class); } protected function tearDown() @@ -39,75 +42,97 @@ protected function tearDown() /** * Unset customer data */ - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->setCustomerData(null); + Bootstrap::getObjectManager()->get(Session::class)->setCustomerData(null); /** * Unset messages */ - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getMessages(true); + Bootstrap::getObjectManager()->get(Session::class)->getMessages(true); } /** - * @magentoDataFixture Magento/Customer/_files/customer.php + * Tests os update a single customer record. + * + * @magentoDataFixture Magento/Customer/_files/five_repository_customers.php + * @magentoDbIsolation disabled */ public function testMassAssignGroupAction() { - $customer = $this->customerRepository->getById(1); + $customerEmail = 'customer1@example.com'; + /** @var CustomerInterface $customer */ + $customer = $this->customerRepository->get($customerEmail); $this->assertEquals(1, $customer->getGroupId()); - $this->getRequest() - ->setParam('group', 0) - ->setPostValue('namespace', 'customer_listing') - ->setPostValue('selected', [1]); + $params = [ + 'group' => 0, + 'namespace' => 'customer_listing', + 'selected' => [$customer->getId()] + ]; + + $this->getRequest()->setParams($params); $this->dispatch('backend/customer/index/massAssignGroup'); $this->assertSessionMessages( - $this->equalTo(['A total of 1 record(s) were updated.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + self::equalTo(['A total of 1 record(s) were updated.']), + MessageInterface::TYPE_SUCCESS ); $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl)); - $customer = $this->customerRepository->getById(1); + $customer = $this->customerRepository->get($customerEmail); $this->assertEquals(0, $customer->getGroupId()); } /** - * @magentoDataFixture Magento/Customer/_files/twenty_one_customers.php + * Tests os update a multiple customer records. + * + * @magentoDataFixture Magento/Customer/_files/five_repository_customers.php + * @magentoDbIsolation disabled */ public function testLargeGroupMassAssignGroupAction() { - - for ($i = 1; $i < 22; $i++) { - $customer = $this->customerRepository->getById($i); + $ids = []; + for ($i = 1; $i <= 5; $i++) { + /** @var CustomerInterface $customer */ + $customer = $this->customerRepository->get('customer' . $i . '@example.com'); $this->assertEquals(1, $customer->getGroupId()); + $ids[] = $customer->getId(); } - $this->getRequest() - ->setParam('group', 0) - ->setPostValue('namespace', 'customer_listing') - ->setPostValue('selected', [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]); + $params = [ + 'group' => 0, + 'namespace' => 'customer_listing', + 'selected' => $ids, + ]; + + $this->getRequest()->setParams($params); $this->dispatch('backend/customer/index/massAssignGroup'); $this->assertSessionMessages( - $this->equalTo(['A total of 21 record(s) were updated.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + self::equalTo(['A total of 5 record(s) were updated.']), + MessageInterface::TYPE_SUCCESS ); $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl)); - for ($i = 1; $i < 22; $i++) { - $customer = $this->customerRepository->getById($i); + for ($i = 1; $i < 5; $i++) { + /** @var CustomerInterface $customer */ + $customer = $this->customerRepository->get('customer' . $i . '@example.com'); $this->assertEquals(0, $customer->getGroupId()); } } /** * Valid group Id but no customer Ids specified + * * @magentoDbIsolation enabled */ public function testMassAssignGroupActionNoCustomerIds() { - $this->getRequest()->setParam('group', 0)->setPostValue('namespace', 'customer_listing'); + $params = [ + 'group' => 0, + 'namespace' => 'customer_listing', + ]; + $this->getRequest()->setParams($params); $this->dispatch('backend/customer/index/massAssignGroup'); $this->assertSessionMessages( $this->equalTo(['An item needs to be selected. Select and try again.']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR + MessageInterface::TYPE_ERROR ); } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php index b7aefe7c31707..2a916c1c00c66 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php @@ -3,61 +3,162 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Customer\Controller\Adminhtml\Index; +use Magento\Backend\Model\Session; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use PHPUnit\Framework\Constraint\Constraint; +use Magento\Framework\Message\MessageInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\AbstractBackendController; /** * @magentoAppArea adminhtml */ -class MassDeleteTest extends \Magento\TestFramework\TestCase\AbstractBackendController +class MassDeleteTest extends AbstractBackendController { + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + /** * Base controller URL * * @var string */ - protected $baseControllerUrl = 'http://localhost/index.php/backend/customer/index/index'; + private $baseControllerUrl = 'http://localhost/index.php/backend/customer/index/index'; + + protected function setUp() + { + parent::setUp(); + $this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class); + } protected function tearDown() { /** * Unset customer data */ - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->setCustomerData(null); + Bootstrap::getObjectManager()->get(Session::class)->setCustomerData(null); /** * Unset messages */ - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getMessages(true); + Bootstrap::getObjectManager()->get(Session::class)->getMessages(true); } /** - * @magentoDataFixture Magento/Customer/_files/customer.php + * Validates failure attempts to delete customers from grid. + * + * @param array|null $ids + * @param Constraint $constraint + * @param string|null $messageType + * @magentoDataFixture Magento/Customer/_files/five_repository_customers.php + * @magentoDbIsolation disabled + * @dataProvider failedRequestDataProvider */ - public function testMassDeleteAction() + public function testFailedMassDeleteAction($ids, Constraint $constraint, $messageType) { - $this->getRequest()->setPostValue('selected', [1])->setPostValue('namespace', 'customer_listing'); - $this->dispatch('backend/customer/index/massDelete'); - $this->assertSessionMessages( - $this->equalTo(['A total of 1 record(s) were deleted.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + $this->massDeleteAssertions($ids, $constraint, $messageType); + } + + /** + * Validates success attempt to delete customer from grid. + * + * @param array $emails + * @param Constraint $constraint + * @param string $messageType + * @magentoDataFixture Magento/Customer/_files/five_repository_customers.php + * @magentoDbIsolation disabled + * @dataProvider successRequestDataProvider + */ + public function testSuccessMassDeleteAction(array $emails, Constraint $constraint, string $messageType) + { + $ids = []; + foreach ($emails as $email) { + /** @var CustomerInterface $customer */ + $customer = $this->customerRepository->get($email); + $ids[] = $customer->getId(); + } + + $this->massDeleteAssertions( + $ids, + $constraint, + $messageType ); - $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl)); } /** - * Valid group Id but no customer Ids specified - * @magentoDbIsolation enabled + * Performs required request and assertions. + * + * @param array|null $ids + * @param Constraint $constraint + * @param string|null $messageType */ - public function testMassDeleteActionNoCustomerIds() + private function massDeleteAssertions($ids, Constraint $constraint, $messageType) { - $this->getRequest()->setPostValue('namespace', 'customer_listing'); + $requestData = [ + 'selected' => $ids, + 'namespace' => 'customer_listing', + ]; + + $this->getRequest()->setParams($requestData); $this->dispatch('backend/customer/index/massDelete'); $this->assertSessionMessages( - $this->equalTo(['An item needs to be selected. Select and try again.']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR + $constraint, + $messageType ); + $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl)); + } + + /** + * Provides sets of data for unsuccessful attempts. + * + * @return array + */ + public function failedRequestDataProvider(): array + { + return [ + [ + 'ids' => [], + 'constraint' => self::equalTo(['Please select item(s).']), + 'messageType' => MessageInterface::TYPE_ERROR, + ], + [ + 'ids' => [111], + 'constraint' => self::isEmpty(), + 'messageType' => null, + ], + [ + 'ids' => null, + 'constraint' => self::equalTo(['Please select item(s).']), + 'messageType' => MessageInterface::TYPE_ERROR, + ] + ]; + } + + /** + * Provides sets of data for successful attempts. + * + * @return array + */ + public function successRequestDataProvider(): array + { + return [ + [ + 'customerEmails' => ['customer1@example.com'], + 'constraint' => self::equalTo(['A total of 1 record(s) were deleted.']), + 'messageType' => MessageInterface::TYPE_SUCCESS, + ], + [ + 'customerEmails' => ['customer2@example.com', 'customer3@example.com'], + 'constraint' => self::equalTo(['A total of 2 record(s) were deleted.']), + 'messageType' => MessageInterface::TYPE_SUCCESS, + ], + ]; } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassSubscribeTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassSubscribeTest.php index d9880b2dc741a..c2fc7b1b58756 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassSubscribeTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassSubscribeTest.php @@ -3,11 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Customer\Controller\Adminhtml\Index; +use Magento\Backend\Model\Session; +use Magento\Framework\Message\MessageInterface; use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; /** * @magentoAppArea adminhtml @@ -26,58 +32,90 @@ protected function tearDown() /** * Unset customer data */ - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->setCustomerData(null); + Bootstrap::getObjectManager()->get(Session::class)->setCustomerData(null); /** * Unset messages */ - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getMessages(true); + Bootstrap::getObjectManager()->get(Session::class)->getMessages(true); } /** - * @magentoDataFixture Magento/Customer/_files/two_customers.php + * Tests subscriber status of customers. + * + * @magentoDataFixture Magento/Customer/_files/five_repository_customers.php + * @magentoDbIsolation disabled */ public function testMassSubscriberAction() { - // Pre-condition - /** @var \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory */ - $subscriberFactory = Bootstrap::getObjectManager()->get(\Magento\Newsletter\Model\SubscriberFactory::class); - $this->assertNull($subscriberFactory->create()->loadByCustomerId(1)->getSubscriberStatus()); - $this->assertNull($subscriberFactory->create()->loadByCustomerId(2)->getSubscriberStatus()); - // Setup - $this->getRequest()->setPostValue('selected', [1, 2])->setPostValue('namespace', 'customer_listing'); + /** @var SubscriberFactory $subscriberFactory */ + $subscriberFactory = Bootstrap::getObjectManager()->get(SubscriberFactory::class); + $customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class); + + $this->assertNull( + $subscriberFactory->create() + ->loadByEmail('customer1@example.com') + ->getSubscriberStatus() + ); + $this->assertNull( + $subscriberFactory->create() + ->loadByEmail('customer2@example.com') + ->getSubscriberStatus() + ); + + /** @var CustomerInterface $customer1 */ + $customer1 = $customerRepository->get('customer1@example.com'); + /** @var CustomerInterface $customer2 */ + $customer2 = $customerRepository->get('customer2@example.com'); + + $params = [ + 'selected' => [ + $customer1->getId(), + $customer2->getId(), + ], + 'namespace' => 'customer_listing', + ]; + $this->getRequest()->setParams($params); - // Test $this->dispatch('backend/customer/index/massSubscribe'); // Assertions $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl)); $this->assertSessionMessages( - $this->equalTo(['A total of 2 record(s) were updated.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + self::equalTo(['A total of 2 record(s) were updated.']), + MessageInterface::TYPE_SUCCESS ); $this->assertEquals( Subscriber::STATUS_SUBSCRIBED, - $subscriberFactory->create()->loadByCustomerId(1)->getSubscriberStatus() + $subscriberFactory->create() + ->loadByEmail('customer1@example.com') + ->getSubscriberStatus() ); $this->assertEquals( Subscriber::STATUS_SUBSCRIBED, - $subscriberFactory->create()->loadByCustomerId(2)->getSubscriberStatus() + $subscriberFactory->create() + ->loadByEmail('customer2@example.com') + ->getSubscriberStatus() ); } /** + * @magentoAppIsolation enabled * @magentoDbIsolation enabled */ public function testMassSubscriberActionNoSelection() { - $this->getRequest()->setPostValue('namespace', 'customer_listing'); + $params = [ + 'namespace' => 'customer_listing' + ]; + + $this->getRequest()->setParams($params); $this->dispatch('backend/customer/index/massSubscribe'); $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl)); $this->assertSessionMessages( - $this->equalTo(['An item needs to be selected. Select and try again.']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR + self::equalTo(['An item needs to be selected. Select and try again.']), + MessageInterface::TYPE_ERROR ); } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/five_repository_customers.php b/dev/tests/integration/testsuite/Magento/Customer/_files/five_repository_customers.php new file mode 100644 index 0000000000000..1722e471a5bbc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/five_repository_customers.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Customer\Model\Customer; +use Magento\Eav\Model\Config as EavModelConfig; +use Magento\Framework\Indexer\IndexerInterface; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->create(CustomerRepositoryInterface::class); +/** @var CustomerInterfaceFactory $customerFactory */ +$customerFactory = $objectManager->get(CustomerInterfaceFactory::class); + +for ($i = 1; $i <= 5; $i++) { + /** @var CustomerInterface $customer */ + $customer = $customerFactory->create(); + $customer->setFirstname('John') + ->setGroupId(1) + ->setLastname('Smith') + ->setWebsiteId(1) + ->setEmail('customer'.$i.'@example.com'); + try { + $customerRepository->save($customer, 'password'); + } catch (\Exception $e) { + } +} + +/** @var EavModelConfig $eavConfig */ +$eavConfig = $objectManager->get(EavModelConfig::class); +$eavConfig->clear(); + +/** @var IndexerRegistry $indexerRegistry */ +$indexerRegistry = $objectManager->create(IndexerRegistry::class); +/** @var IndexerInterface $indexer */ +$indexer = $indexerRegistry->get(Customer::CUSTOMER_GRID_INDEXER_ID); +try { + $indexer->reindexAll(); +} catch (\Exception $e) { +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/five_repository_customers_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/five_repository_customers_rollback.php new file mode 100644 index 0000000000000..5272d9cdbf06d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/five_repository_customers_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Eav\Model\Config as EavModelConfig; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var CustomerRepositoryInterface $repository */ +$customerRepository = $objectManager->create(CustomerRepositoryInterface::class); + +for ($i = 1; $i <= 5; $i++) { + try { + /** @var CustomerInterface $customer */ + $customer = $customerRepository->get('customer'.$i.'@example.com'); + $customerRepository->delete($customer); + } catch (\Exception $e) { + } +} + +/** @var EavModelConfig $eavConfig */ +$eavConfig = $objectManager->get(EavModelConfig::class); +$eavConfig->clear(); From c840ade149c58049ffbd01ebee623aef1bb9d743 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Thu, 2 Aug 2018 19:00:38 +0300 Subject: [PATCH 0701/1171] MAGETWO-93715: [2.3] Delete action in grid could be sent multiple times --- .../Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php index 366356597fdfb..f32c6edd57394 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php @@ -66,7 +66,7 @@ public function execute() } if ($productDeleted) { - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('A total of %1 record(s) have been deleted.', $productDeleted) ); } From c4eeaa9a827c533e46f35af5cf7e10f8eb4737b3 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Wed, 25 Jul 2018 00:37:24 +0530 Subject: [PATCH 0702/1171] Code cleanup of lib files --- .../Framework/Api/AbstractExtensibleObject.php | 4 ++-- .../Magento/Framework/Api/AbstractSimpleObject.php | 2 +- .../Magento/Framework/Api/ImageProcessor.php | 2 +- .../Magento/Framework/Api/Search/Document.php | 4 +--- .../CollectionProcessor/FilterProcessor.php | 2 +- .../CollectionProcessor/JoinProcessor.php | 2 +- .../CollectionProcessor/SortingProcessor.php | 2 +- lib/internal/Magento/Framework/App/ActionFlag.php | 4 +--- lib/internal/Magento/Framework/App/AreaList.php | 4 ++-- .../Magento/Framework/App/Config/Initial.php | 4 ++-- .../Framework/App/DefaultPath/DefaultPath.php | 2 +- .../Magento/Framework/App/DeploymentConfig.php | 2 +- .../Magento/Framework/App/Http/Context.php | 4 +--- .../Framework/Component/ComponentRegistrar.php | 2 +- lib/internal/Magento/Framework/Config/View.php | 14 ++++++-------- .../Magento/Framework/DB/Adapter/Pdo/Mysql.php | 2 +- .../Magento/Framework/Data/AbstractCriteria.php | 2 +- .../Magento/Framework/Data/AbstractDataObject.php | 2 +- lib/internal/Magento/Framework/Data/Collection.php | 2 +- lib/internal/Magento/Framework/Data/Structure.php | 8 +++----- lib/internal/Magento/Framework/Event.php | 2 +- .../Magento/Framework/Filesystem/Io/File.php | 2 +- .../Magento/Framework/Filter/AbstractFactory.php | 2 +- lib/internal/Magento/Framework/Filter/Input.php | 2 +- .../Interception/PluginList/PluginList.php | 2 +- .../Magento/Framework/Message/Collection.php | 2 +- .../Framework/Model/AbstractExtensibleModel.php | 4 +--- .../Magento/Framework/Module/FullModuleList.php | 2 +- .../Magento/Framework/Module/ModuleList.php | 2 +- .../Magento/Framework/Module/ModuleResource.php | 4 ++-- .../Magento/Framework/Module/PackageInfo.php | 2 +- .../Magento/Framework/Notification/MessageList.php | 2 +- .../Test/Unit/Factory/Fixture/Polymorphous.php | 2 +- .../Magento/Framework/Pricing/Amount/Base.php | 4 +--- .../Magento/Framework/Pricing/Price/Pool.php | 2 +- .../Framework/Search/Response/Aggregation.php | 2 +- .../Magento/Framework/Session/SessionManager.php | 2 +- lib/internal/Magento/Framework/Url.php | 2 +- .../Magento/Framework/Validator/Exception.php | 2 +- .../Magento/Framework/View/Asset/PropertyGroup.php | 2 +- lib/internal/Magento/Framework/View/BlockPool.php | 2 +- .../Magento/Framework/View/DataSourcePool.php | 4 ++-- .../Framework/View/Design/Theme/Validator.php | 2 +- .../Framework/View/Element/UiComponent/Context.php | 2 +- .../UiComponent/DataProvider/DataProvider.php | 10 ++++------ .../Magento/Framework/View/Layout/Generic.php | 2 +- .../Framework/View/Layout/ScheduledStructure.php | 8 ++++---- .../Magento/Framework/View/Page/Config.php | 4 ++-- lib/internal/Magento/Framework/Xml/Generator.php | 4 +--- 49 files changed, 68 insertions(+), 86 deletions(-) diff --git a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php index 7b2b94cbd0973..97c24167d47e1 100644 --- a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php +++ b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php @@ -76,7 +76,7 @@ public function getCustomAttribute($attributeCode) */ public function getCustomAttributes() { - return isset($this->_data[self::CUSTOM_ATTRIBUTES]) ? $this->_data[self::CUSTOM_ATTRIBUTES] : []; + return $this->_data[self::CUSTOM_ATTRIBUTES] ?? []; } /** @@ -131,7 +131,7 @@ public function setCustomAttribute($attributeCode, $attributeValue) */ protected function getCustomAttributesCodes() { - return isset($this->customAttributesCodes) ? $this->customAttributesCodes : []; + return $this->customAttributesCodes ?? []; } /** diff --git a/lib/internal/Magento/Framework/Api/AbstractSimpleObject.php b/lib/internal/Magento/Framework/Api/AbstractSimpleObject.php index b907246139d42..9cc0ddab3bfcb 100644 --- a/lib/internal/Magento/Framework/Api/AbstractSimpleObject.php +++ b/lib/internal/Magento/Framework/Api/AbstractSimpleObject.php @@ -34,7 +34,7 @@ public function __construct(array $data = []) */ protected function _get($key) { - return isset($this->_data[$key]) ? $this->_data[$key] : null; + return $this->_data[$key] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Api/ImageProcessor.php b/lib/internal/Magento/Framework/Api/ImageProcessor.php index beadb51bc7796..83390b3853212 100644 --- a/lib/internal/Magento/Framework/Api/ImageProcessor.php +++ b/lib/internal/Magento/Framework/Api/ImageProcessor.php @@ -172,7 +172,7 @@ public function processImageContent($entityType, $imageContent) */ protected function getMimeTypeExtension($mimeType) { - return isset($this->mimeTypeExtensionMap[$mimeType]) ? $this->mimeTypeExtensionMap[$mimeType] : ''; + return $this->mimeTypeExtensionMap[$mimeType] ?? ''; } /** diff --git a/lib/internal/Magento/Framework/Api/Search/Document.php b/lib/internal/Magento/Framework/Api/Search/Document.php index d60458a6e5585..7454fa7974ece 100644 --- a/lib/internal/Magento/Framework/Api/Search/Document.php +++ b/lib/internal/Magento/Framework/Api/Search/Document.php @@ -33,9 +33,7 @@ public function setId($id) */ public function getCustomAttribute($attributeCode) { - return isset($this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode]) - ? $this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode] - : null; + return $this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php index c9f10c183b50c..024a4d2af3847 100644 --- a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php +++ b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php @@ -123,6 +123,6 @@ private function getCustomFilterForField($field) */ private function getFieldMapping($field) { - return isset($this->fieldMapping[$field]) ? $this->fieldMapping[$field] : $field; + return $this->fieldMapping[$field] ?? $field; } } diff --git a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/JoinProcessor.php b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/JoinProcessor.php index b8e52334bee1f..207325042c737 100644 --- a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/JoinProcessor.php +++ b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/JoinProcessor.php @@ -121,6 +121,6 @@ private function getCustomJoin($field) */ private function getFieldMapping($field) { - return isset($this->fieldMapping[$field]) ? $this->fieldMapping[$field] : $field; + return $this->fieldMapping[$field] ?? $field; } } diff --git a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/SortingProcessor.php b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/SortingProcessor.php index 85a6d6f570d5e..9dda5cd682708 100644 --- a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/SortingProcessor.php +++ b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/SortingProcessor.php @@ -59,7 +59,7 @@ public function process(SearchCriteriaInterface $searchCriteria, AbstractDb $col */ private function getFieldMapping($field) { - return isset($this->fieldMapping[$field]) ? $this->fieldMapping[$field] : $field; + return $this->fieldMapping[$field] ?? $field; } /** diff --git a/lib/internal/Magento/Framework/App/ActionFlag.php b/lib/internal/Magento/Framework/App/ActionFlag.php index 657c2ede9262d..55201504c968f 100644 --- a/lib/internal/Magento/Framework/App/ActionFlag.php +++ b/lib/internal/Magento/Framework/App/ActionFlag.php @@ -65,9 +65,7 @@ public function get($action, $flag = '') $action = $this->_request->getActionName(); } if ('' === $flag) { - return isset( - $this->_flags[$this->_getControllerKey()] - ) ? $this->_flags[$this->_getControllerKey()] : []; + return $this->_flags[$this->_getControllerKey()] ?? []; } elseif (isset($this->_flags[$this->_getControllerKey()][$action][$flag])) { return $this->_flags[$this->_getControllerKey()][$action][$flag]; } else { diff --git a/lib/internal/Magento/Framework/App/AreaList.php b/lib/internal/Magento/Framework/App/AreaList.php index 7c123f7ff9d60..fb28d09d5fe09 100644 --- a/lib/internal/Magento/Framework/App/AreaList.php +++ b/lib/internal/Magento/Framework/App/AreaList.php @@ -88,7 +88,7 @@ public function getCodeByFrontName($frontName) */ public function getFrontName($areaCode) { - return isset($this->_areas[$areaCode]['frontName']) ? $this->_areas[$areaCode]['frontName'] : null; + return $this->_areas[$areaCode]['frontName'] ?? null; } /** @@ -111,7 +111,7 @@ public function getCodes() */ public function getDefaultRouter($areaCode) { - return isset($this->_areas[$areaCode]['router']) ? $this->_areas[$areaCode]['router'] : null; + return $this->_areas[$areaCode]['router'] ?? null; } /** diff --git a/lib/internal/Magento/Framework/App/Config/Initial.php b/lib/internal/Magento/Framework/App/Config/Initial.php index 1933682346ad3..b65c9a2f53489 100644 --- a/lib/internal/Magento/Framework/App/Config/Initial.php +++ b/lib/internal/Magento/Framework/App/Config/Initial.php @@ -72,9 +72,9 @@ public function getData($scope) list($scopeType, $scopeCode) = array_pad(explode('|', $scope), 2, null); if (ScopeConfigInterface::SCOPE_TYPE_DEFAULT == $scopeType) { - return isset($this->_data[$scopeType]) ? $this->_data[$scopeType] : []; + return $this->_data[$scopeType] ?? []; } elseif ($scopeCode) { - return isset($this->_data[$scopeType][$scopeCode]) ? $this->_data[$scopeType][$scopeCode] : []; + return $this->_data[$scopeType][$scopeCode] ?? []; } return []; } diff --git a/lib/internal/Magento/Framework/App/DefaultPath/DefaultPath.php b/lib/internal/Magento/Framework/App/DefaultPath/DefaultPath.php index 61d0aa138f9a4..8a4188aed9605 100644 --- a/lib/internal/Magento/Framework/App/DefaultPath/DefaultPath.php +++ b/lib/internal/Magento/Framework/App/DefaultPath/DefaultPath.php @@ -32,6 +32,6 @@ public function __construct(array $parts) */ public function getPart($code) { - return isset($this->_parts[$code]) ? $this->_parts[$code] : null; + return $this->_parts[$code] ?? null; } } diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig.php b/lib/internal/Magento/Framework/App/DeploymentConfig.php index 0fe7703ef81c0..f83f89ee4caeb 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig.php @@ -70,7 +70,7 @@ public function get($key = null, $defaultValue = null) if ($key === null) { return $this->flatData; } - return isset($this->flatData[$key]) ? $this->flatData[$key] : $defaultValue; + return $this->flatData[$key] ?? $defaultValue; } /** diff --git a/lib/internal/Magento/Framework/App/Http/Context.php b/lib/internal/Magento/Framework/App/Http/Context.php index a5eba2753b510..79a15110234cd 100644 --- a/lib/internal/Magento/Framework/App/Http/Context.php +++ b/lib/internal/Magento/Framework/App/Http/Context.php @@ -84,9 +84,7 @@ public function unsValue($name) */ public function getValue($name) { - return isset($this->data[$name]) - ? $this->data[$name] - : (isset($this->default[$name]) ? $this->default[$name] : null); + return $this->data[$name] ?? ($this->default[$name] ?? null); } /** diff --git a/lib/internal/Magento/Framework/Component/ComponentRegistrar.php b/lib/internal/Magento/Framework/Component/ComponentRegistrar.php index 0e85cf5260e9b..0a54d770300e8 100644 --- a/lib/internal/Magento/Framework/Component/ComponentRegistrar.php +++ b/lib/internal/Magento/Framework/Component/ComponentRegistrar.php @@ -70,7 +70,7 @@ public function getPaths($type) public function getPath($type, $componentName) { self::validateType($type); - return isset(self::$paths[$type][$componentName]) ? self::$paths[$type][$componentName] : null; + return self::$paths[$type][$componentName] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Config/View.php b/lib/internal/Magento/Framework/Config/View.php index ef9c39e221e86..05863caeec2b6 100644 --- a/lib/internal/Magento/Framework/Config/View.php +++ b/lib/internal/Magento/Framework/Config/View.php @@ -71,7 +71,7 @@ public function __construct( public function getVars($module) { $this->initData(); - return isset($this->data['vars'][$module]) ? $this->data['vars'][$module] : []; + return $this->data['vars'][$module] ?? []; } /** @@ -110,7 +110,7 @@ public function getVarValue($module, $var) public function getMediaEntities($module, $mediaType) { $this->initData(); - return isset($this->data['media'][$module][$mediaType]) ? $this->data['media'][$module][$mediaType] : []; + return $this->data['media'][$module][$mediaType] ?? []; } /** @@ -124,9 +124,7 @@ public function getMediaEntities($module, $mediaType) public function getMediaAttributes($module, $mediaType, $mediaId) { $this->initData(); - return isset($this->data['media'][$module][$mediaType][$mediaId]) - ? $this->data['media'][$module][$mediaType][$mediaId] - : []; + return $this->data['media'][$module][$mediaType][$mediaId] ?? []; } /** @@ -163,7 +161,7 @@ protected function getIdAttributes() public function getExcludedFiles() { $items = $this->getItems(); - return isset($items['file']) ? $items['file'] : []; + return $items['file'] ?? []; } /** @@ -174,7 +172,7 @@ public function getExcludedFiles() public function getExcludedDir() { $items = $this->getItems(); - return isset($items['directory']) ? $items['directory'] : []; + return $items['directory'] ?? []; } /** @@ -185,7 +183,7 @@ public function getExcludedDir() protected function getItems() { $this->initData(); - return isset($this->data['exclude']) ? $this->data['exclude'] : []; + return $this->data['exclude'] ?? []; } /** diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index e02c48222ebd4..441da10253a14 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -490,7 +490,7 @@ public function rawFetchRow($sql, $field = null) if (empty($field)) { return $row; } else { - return isset($row[$field]) ? $row[$field] : false; + return $row[$field] ?? false; } } diff --git a/lib/internal/Magento/Framework/Data/AbstractCriteria.php b/lib/internal/Magento/Framework/Data/AbstractCriteria.php index c90ed2b03bf3d..d4811b79980a9 100644 --- a/lib/internal/Magento/Framework/Data/AbstractCriteria.php +++ b/lib/internal/Magento/Framework/Data/AbstractCriteria.php @@ -274,7 +274,7 @@ public function getLimit() */ public function getPart($name, $default = null) { - return isset($this->data[$name]) ? $this->data[$name] : $default; + return $this->data[$name] ?? $default; } /** diff --git a/lib/internal/Magento/Framework/Data/AbstractDataObject.php b/lib/internal/Magento/Framework/Data/AbstractDataObject.php index 5916100ffbbfa..da04fecc447cc 100644 --- a/lib/internal/Magento/Framework/Data/AbstractDataObject.php +++ b/lib/internal/Magento/Framework/Data/AbstractDataObject.php @@ -50,6 +50,6 @@ public function toArray() */ protected function get($key) { - return isset($this->data[$key]) ? $this->data[$key] : null; + return $this->data[$key] ?? null; } } diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php index f8b82d3122234..71ec8c1aa8379 100644 --- a/lib/internal/Magento/Framework/Data/Collection.php +++ b/lib/internal/Magento/Framework/Data/Collection.php @@ -851,7 +851,7 @@ public function count() */ public function getFlag($flag) { - return isset($this->_flags[$flag]) ? $this->_flags[$flag] : null; + return $this->_flags[$flag] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Data/Structure.php b/lib/internal/Magento/Framework/Data/Structure.php index f2c45fa160cc2..b22395f1ba835 100644 --- a/lib/internal/Magento/Framework/Data/Structure.php +++ b/lib/internal/Magento/Framework/Data/Structure.php @@ -172,7 +172,7 @@ public function createElement($elementId, array $data) */ public function getElement($elementId) { - return isset($this->_elements[$elementId]) ? $this->_elements[$elementId] : false; + return $this->_elements[$elementId] ?? false; } /** @@ -466,9 +466,7 @@ public function getChildId($parentId, $alias) */ public function getChildren($parentId) { - return isset( - $this->_elements[$parentId][self::CHILDREN] - ) ? $this->_elements[$parentId][self::CHILDREN] : []; + return $this->_elements[$parentId][self::CHILDREN] ?? []; } /** @@ -479,7 +477,7 @@ public function getChildren($parentId) */ public function getParentId($childId) { - return isset($this->_elements[$childId][self::PARENT]) ? $this->_elements[$childId][self::PARENT] : false; + return $this->_elements[$childId][self::PARENT] ?? false; } /** diff --git a/lib/internal/Magento/Framework/Event.php b/lib/internal/Magento/Framework/Event.php index 4c116d0a33629..c7b15a8eb0722 100644 --- a/lib/internal/Magento/Framework/Event.php +++ b/lib/internal/Magento/Framework/Event.php @@ -88,7 +88,7 @@ public function dispatch() */ public function getName() { - return isset($this->_data['name']) ? $this->_data['name'] : null; + return $this->_data['name'] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Filesystem/Io/File.php b/lib/internal/Magento/Framework/Filesystem/Io/File.php index c1cfebc7a0ac1..8fec7f7630257 100644 --- a/lib/internal/Magento/Framework/Filesystem/Io/File.php +++ b/lib/internal/Magento/Framework/Filesystem/Io/File.php @@ -233,7 +233,7 @@ public function streamStat($part = null, $default = null) } $stat = @fstat($this->_streamHandler); if ($part !== null) { - return isset($stat[$part]) ? $stat[$part] : $default; + return $stat[$part] ?? $default; } return $stat; } diff --git a/lib/internal/Magento/Framework/Filter/AbstractFactory.php b/lib/internal/Magento/Framework/Filter/AbstractFactory.php index 2a0ae0ba15e42..c30b07aa09061 100644 --- a/lib/internal/Magento/Framework/Filter/AbstractFactory.php +++ b/lib/internal/Magento/Framework/Filter/AbstractFactory.php @@ -68,7 +68,7 @@ public function canCreateFilter($alias) */ public function isShared($class) { - return isset($this->shared[$class]) ? $this->shared[$class] : $this->shareByDefault; + return $this->shared[$class] ?? $this->shareByDefault; } /** diff --git a/lib/internal/Magento/Framework/Filter/Input.php b/lib/internal/Magento/Framework/Filter/Input.php index 39c7a54786edb..6da748fcbeb9f 100644 --- a/lib/internal/Magento/Framework/Filter/Input.php +++ b/lib/internal/Magento/Framework/Filter/Input.php @@ -183,7 +183,7 @@ public function getFilters($name = null) if (null === $name) { return $this->_filters; } else { - return isset($this->_filters[$name]) ? $this->_filters[$name] : null; + return $this->_filters[$name] ?? null; } } diff --git a/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php b/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php index 82738a30266d8..e21841b48bc13 100644 --- a/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php +++ b/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php @@ -268,7 +268,7 @@ public function getNext($type, $method, $code = '__self') $this->_inheritPlugins($type); } $key = $type . '_' . lcfirst($method) . '_' . $code; - return isset($this->_processed[$key]) ? $this->_processed[$key] : null; + return $this->_processed[$key] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Message/Collection.php b/lib/internal/Magento/Framework/Message/Collection.php index 8805d433f4ffb..32e84fc28f5a0 100644 --- a/lib/internal/Magento/Framework/Message/Collection.php +++ b/lib/internal/Magento/Framework/Message/Collection.php @@ -136,7 +136,7 @@ public function getItems() */ public function getItemsByType($type) { - return isset($this->messages[$type]) ? $this->messages[$type] : []; + return $this->messages[$type] ?? []; } /** diff --git a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php index e7ffcde03ff64..1cffba2543b0b 100644 --- a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php +++ b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php @@ -154,9 +154,7 @@ public function getCustomAttributes() public function getCustomAttribute($attributeCode) { $this->initializeCustomAttributes(); - return isset($this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode]) - ? $this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode] - : null; + return $this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Module/FullModuleList.php b/lib/internal/Magento/Framework/Module/FullModuleList.php index 5ad5b05a413ef..c6e403dee0898 100644 --- a/lib/internal/Magento/Framework/Module/FullModuleList.php +++ b/lib/internal/Magento/Framework/Module/FullModuleList.php @@ -55,7 +55,7 @@ public function getAll() public function getOne($name) { $data = $this->getAll(); - return isset($data[$name]) ? $data[$name] : null; + return $data[$name] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Module/ModuleList.php b/lib/internal/Magento/Framework/Module/ModuleList.php index 406aa9efd31a0..6ee061cffb3d0 100644 --- a/lib/internal/Magento/Framework/Module/ModuleList.php +++ b/lib/internal/Magento/Framework/Module/ModuleList.php @@ -90,7 +90,7 @@ public function getAll() public function getOne($name) { $enabled = $this->getAll(); - return isset($enabled[$name]) ? $enabled[$name] : null; + return $enabled[$name] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Module/ModuleResource.php b/lib/internal/Magento/Framework/Module/ModuleResource.php index 427f8a6f8e959..b453ea4cba095 100644 --- a/lib/internal/Magento/Framework/Module/ModuleResource.php +++ b/lib/internal/Magento/Framework/Module/ModuleResource.php @@ -87,7 +87,7 @@ public function getDbVersion($moduleName) return false; } $this->_loadVersion('db'); - return isset(self::$schemaVersions[$moduleName]) ? self::$schemaVersions[$moduleName] : false; + return self::$schemaVersions[$moduleName] ?? false; } /** @@ -119,7 +119,7 @@ public function getDataVersion($moduleName) return false; } $this->_loadVersion('data'); - return isset(self::$dataVersions[$moduleName]) ? self::$dataVersions[$moduleName] : false; + return self::$dataVersions[$moduleName] ?? false; } /** diff --git a/lib/internal/Magento/Framework/Module/PackageInfo.php b/lib/internal/Magento/Framework/Module/PackageInfo.php index 7edb0c04ebf98..0dce507ba26f4 100644 --- a/lib/internal/Magento/Framework/Module/PackageInfo.php +++ b/lib/internal/Magento/Framework/Module/PackageInfo.php @@ -283,6 +283,6 @@ public function getConflict($moduleName) public function getVersion($moduleName) { $this->load(); - return isset($this->modulePackageVersionMap[$moduleName]) ? $this->modulePackageVersionMap[$moduleName] : ''; + return $this->modulePackageVersionMap[$moduleName] ?? ''; } } diff --git a/lib/internal/Magento/Framework/Notification/MessageList.php b/lib/internal/Magento/Framework/Notification/MessageList.php index 8fb91890b2ff0..ac753b48c8944 100644 --- a/lib/internal/Magento/Framework/Notification/MessageList.php +++ b/lib/internal/Magento/Framework/Notification/MessageList.php @@ -72,7 +72,7 @@ protected function _loadMessages() public function getMessageByIdentity($identity) { $this->_loadMessages(); - return isset($this->_messages[$identity]) ? $this->_messages[$identity] : null; + return $this->_messages[$identity] ?? null; } /** diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/Polymorphous.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/Polymorphous.php index ebb7d76dcb63c..0c1a2128560f8 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/Polymorphous.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/Polymorphous.php @@ -26,6 +26,6 @@ public function __construct() */ public function getArg($key) { - return isset($this->args[$key]) ? $this->args[$key] : null; + return $this->args[$key] ?? null; } } diff --git a/lib/internal/Magento/Framework/Pricing/Amount/Base.php b/lib/internal/Magento/Framework/Pricing/Amount/Base.php index 2664ccc54944c..d055819a4a126 100644 --- a/lib/internal/Magento/Framework/Pricing/Amount/Base.php +++ b/lib/internal/Magento/Framework/Pricing/Amount/Base.php @@ -105,9 +105,7 @@ public function getBaseAmount() */ public function getAdjustmentAmount($adjustmentCode) { - return isset($this->adjustmentAmounts[$adjustmentCode]) - ? $this->adjustmentAmounts[$adjustmentCode] - : false; + return $this->adjustmentAmounts[$adjustmentCode] ?? false; } /** diff --git a/lib/internal/Magento/Framework/Pricing/Price/Pool.php b/lib/internal/Magento/Framework/Pricing/Price/Pool.php index b460113fc32c8..dfdd0c52681e1 100644 --- a/lib/internal/Magento/Framework/Pricing/Price/Pool.php +++ b/lib/internal/Magento/Framework/Pricing/Price/Pool.php @@ -141,6 +141,6 @@ public function offsetUnset($offset) */ public function offsetGet($offset) { - return isset($this->prices[$offset]) ? $this->prices[$offset] : null; + return $this->prices[$offset] ?? null; } } diff --git a/lib/internal/Magento/Framework/Search/Response/Aggregation.php b/lib/internal/Magento/Framework/Search/Response/Aggregation.php index 9cb7a364ff21c..ea72597c53034 100644 --- a/lib/internal/Magento/Framework/Search/Response/Aggregation.php +++ b/lib/internal/Magento/Framework/Search/Response/Aggregation.php @@ -47,7 +47,7 @@ public function getIterator() */ public function getBucket($bucketName) { - return isset($this->buckets[$bucketName]) ? $this->buckets[$bucketName] : null; + return $this->buckets[$bucketName] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Session/SessionManager.php b/lib/internal/Magento/Framework/Session/SessionManager.php index e4130147a5da0..662173ad4a09a 100644 --- a/lib/internal/Magento/Framework/Session/SessionManager.php +++ b/lib/internal/Magento/Framework/Session/SessionManager.php @@ -473,7 +473,7 @@ protected function _addHost() */ protected function _getHosts() { - return isset($_SESSION[self::HOST_KEY]) ? $_SESSION[self::HOST_KEY] : []; + return $_SESSION[self::HOST_KEY] ?? []; } /** diff --git a/lib/internal/Magento/Framework/Url.php b/lib/internal/Magento/Framework/Url.php index 11f828370d441..11aeb1c0c79b8 100644 --- a/lib/internal/Magento/Framework/Url.php +++ b/lib/internal/Magento/Framework/Url.php @@ -1074,7 +1074,7 @@ function ($match) { if ($match[1] == '?') { return isset($match[3]) ? '?' : ''; } elseif ($match[1] == '&' || $match[1] == '&') { - return isset($match[3]) ? $match[3] : ''; + return $match[3] ?? ''; } } }, diff --git a/lib/internal/Magento/Framework/Validator/Exception.php b/lib/internal/Magento/Framework/Validator/Exception.php index c70ecfabb52af..370f66c424b01 100644 --- a/lib/internal/Magento/Framework/Validator/Exception.php +++ b/lib/internal/Magento/Framework/Validator/Exception.php @@ -84,6 +84,6 @@ public function getMessages($type = '') } return $allMessages; } - return isset($this->messages[$type]) ? $this->messages[$type] : []; + return $this->messages[$type] ?? []; } } diff --git a/lib/internal/Magento/Framework/View/Asset/PropertyGroup.php b/lib/internal/Magento/Framework/View/Asset/PropertyGroup.php index ad86dfca47a25..4fe8f48d6b723 100644 --- a/lib/internal/Magento/Framework/View/Asset/PropertyGroup.php +++ b/lib/internal/Magento/Framework/View/Asset/PropertyGroup.php @@ -45,6 +45,6 @@ public function getProperties() */ public function getProperty($name) { - return isset($this->properties[$name]) ? $this->properties[$name] : null; + return $this->properties[$name] ?? null; } } diff --git a/lib/internal/Magento/Framework/View/BlockPool.php b/lib/internal/Magento/Framework/View/BlockPool.php index c50af9d5bbc5f..dff280057fdaf 100644 --- a/lib/internal/Magento/Framework/View/BlockPool.php +++ b/lib/internal/Magento/Framework/View/BlockPool.php @@ -72,6 +72,6 @@ public function get($name = null) return $this->blocks; } - return isset($this->blocks[$name]) ? $this->blocks[$name] : null; + return $this->blocks[$name] ?? null; } } diff --git a/lib/internal/Magento/Framework/View/DataSourcePool.php b/lib/internal/Magento/Framework/View/DataSourcePool.php index 24bdb6639981b..94f4f1dceae97 100644 --- a/lib/internal/Magento/Framework/View/DataSourcePool.php +++ b/lib/internal/Magento/Framework/View/DataSourcePool.php @@ -80,7 +80,7 @@ public function get($name = null) return $this->dataSources; } - return isset($this->dataSources[$name]) ? $this->dataSources[$name] : null; + return $this->dataSources[$name] ?? null; } /** @@ -107,6 +107,6 @@ public function assign($dataName, $namespace, $alias) */ public function getNamespaceData($namespace) { - return isset($this->assignments[$namespace]) ? $this->assignments[$namespace] : []; + return $this->assignments[$namespace] ?? []; } } diff --git a/lib/internal/Magento/Framework/View/Design/Theme/Validator.php b/lib/internal/Magento/Framework/View/Design/Theme/Validator.php index 06c78e7125040..04e775d730b56 100644 --- a/lib/internal/Magento/Framework/View/Design/Theme/Validator.php +++ b/lib/internal/Magento/Framework/View/Design/Theme/Validator.php @@ -118,7 +118,7 @@ public function addDataValidators($dataKey, $validators) public function getErrorMessages($dataKey = null) { if ($dataKey) { - return isset($this->_errorMessages[$dataKey]) ? $this->_errorMessages[$dataKey] : []; + return $this->_errorMessages[$dataKey] ?? []; } return $this->_errorMessages; } diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php index 73f125be77c79..48a66b429ebd5 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php @@ -213,7 +213,7 @@ public function getFiltersParams() public function getFilterParam($key, $defaultValue = null) { $filter = $this->getFiltersParams(); - return isset($filter[$key]) ? $filter[$key] : $defaultValue; + return $filter[$key] ?? $defaultValue; } /** diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/DataProvider.php b/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/DataProvider.php index b2288a47f8f83..baa4e94eed978 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/DataProvider.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/DataProvider.php @@ -181,7 +181,7 @@ public function getMeta() */ public function getFieldSetMetaInfo($fieldSetName) { - return isset($this->meta[$fieldSetName]) ? $this->meta[$fieldSetName] : []; + return $this->meta[$fieldSetName] ?? []; } /** @@ -190,7 +190,7 @@ public function getFieldSetMetaInfo($fieldSetName) */ public function getFieldsMetaInfo($fieldSetName) { - return isset($this->meta[$fieldSetName]['children']) ? $this->meta[$fieldSetName]['children'] : []; + return $this->meta[$fieldSetName]['children'] ?? []; } /** @@ -200,9 +200,7 @@ public function getFieldsMetaInfo($fieldSetName) */ public function getFieldMetaInfo($fieldSetName, $fieldName) { - return isset($this->meta[$fieldSetName]['children'][$fieldName]) - ? $this->meta[$fieldSetName]['children'][$fieldName] - : []; + return $this->meta[$fieldSetName]['children'][$fieldName] ?? []; } /** @@ -291,7 +289,7 @@ public function getData() */ public function getConfigData() { - return isset($this->data['config']) ? $this->data['config'] : []; + return $this->data['config'] ?? []; } /** diff --git a/lib/internal/Magento/Framework/View/Layout/Generic.php b/lib/internal/Magento/Framework/View/Layout/Generic.php index b83545ff3c439..b527d1a817a96 100644 --- a/lib/internal/Magento/Framework/View/Layout/Generic.php +++ b/lib/internal/Magento/Framework/View/Layout/Generic.php @@ -200,6 +200,6 @@ protected function createChildFormComponent(UiComponentInterface $childComponent */ protected function getConfig($name) { - return isset($this->data['config'][$name]) ? $this->data['config'][$name] : null; + return $this->data['config'][$name] ?? null; } } diff --git a/lib/internal/Magento/Framework/View/Layout/ScheduledStructure.php b/lib/internal/Magento/Framework/View/Layout/ScheduledStructure.php index 25a04845a0728..3193e10282fd4 100644 --- a/lib/internal/Magento/Framework/View/Layout/ScheduledStructure.php +++ b/lib/internal/Magento/Framework/View/Layout/ScheduledStructure.php @@ -146,7 +146,7 @@ public function unsetElementToSort($elementName) */ public function getElementToSort($elementName, array $default = []) { - return isset($this->elementsToSort[$elementName]) ? $this->elementsToSort[$elementName] : $default; + return $this->elementsToSort[$elementName] ?? $default; } /** @@ -257,7 +257,7 @@ public function unsetElement($elementName) */ public function getElementToMove($elementName, $default = null) { - return isset($this->scheduledMoves[$elementName]) ? $this->scheduledMoves[$elementName] : $default; + return $this->scheduledMoves[$elementName] ?? $default; } /** @@ -370,7 +370,7 @@ public function unsetStructureElement($elementName) */ public function getStructureElementData($elementName, $default = null) { - return isset($this->scheduledData[$elementName]) ? $this->scheduledData[$elementName] : $default; + return $this->scheduledData[$elementName] ?? $default; } /** @@ -524,6 +524,6 @@ public function populateWithArray(array $data) */ private function getArrayValueByKey($key, array $array) { - return isset($array[$key]) ? $array[$key] : []; + return $array[$key] ?? []; } } diff --git a/lib/internal/Magento/Framework/View/Page/Config.php b/lib/internal/Magento/Framework/View/Page/Config.php index d42d30e35cc5b..1a21e61f867d8 100644 --- a/lib/internal/Magento/Framework/View/Page/Config.php +++ b/lib/internal/Magento/Framework/View/Page/Config.php @@ -508,7 +508,7 @@ public function setElementAttribute($elementType, $attribute, $value) public function getElementAttribute($elementType, $attribute) { $this->build(); - return isset($this->elements[$elementType][$attribute]) ? $this->elements[$elementType][$attribute] : null; + return $this->elements[$elementType][$attribute] ?? null; } /** @@ -518,7 +518,7 @@ public function getElementAttribute($elementType, $attribute) public function getElementAttributes($elementType) { $this->build(); - return isset($this->elements[$elementType]) ? $this->elements[$elementType] : []; + return $this->elements[$elementType] ?? []; } /** diff --git a/lib/internal/Magento/Framework/Xml/Generator.php b/lib/internal/Magento/Framework/Xml/Generator.php index 975073e443d0e..f165793c2e2a4 100644 --- a/lib/internal/Magento/Framework/Xml/Generator.php +++ b/lib/internal/Magento/Framework/Xml/Generator.php @@ -146,8 +146,6 @@ public function setIndexedArrayItemName($name) */ protected function _getIndexedArrayItemName() { - return isset($this->_defaultIndexedArrayItemName) - ? $this->_defaultIndexedArrayItemName - : self::DEFAULT_ENTITY_ITEM_NAME; + return $this->_defaultIndexedArrayItemName ?? self::DEFAULT_ENTITY_ITEM_NAME; } } From 13a9c4367fbbc2c22363a72e72eb9bc2708de1ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= <lukasz.bajsarowicz@gmail.com> Date: Fri, 13 Jul 2018 17:04:54 +0200 Subject: [PATCH 0703/1171] Replace sort callback to spaceship --- app/code/Magento/Quote/Model/Quote/Address.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index 87c5feaba8f2e..3eb5d68885035 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -872,13 +872,7 @@ public function getGroupedAllShippingRates() */ protected function _sortRates($firstItem, $secondItem) { - if ((int)$firstItem[0]->carrier_sort_order < (int)$secondItem[0]->carrier_sort_order) { - return -1; - } elseif ((int)$firstItem[0]->carrier_sort_order > (int)$secondItem[0]->carrier_sort_order) { - return 1; - } else { - return 0; - } + return (int) $firstItem[0]->carrier_sort_order <=> (int) $secondItem[0]->carrier_sort_order; } /** From 622150ac14d4f30ad724810b90c4979736d024a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= <lukasz.bajsarowicz@gmail.com> Date: Fri, 13 Jul 2018 18:45:49 +0200 Subject: [PATCH 0704/1171] Additional usort callback replacements with spaceship operator --- .../Magento/Catalog/Model/Product/Type/AbstractType.php | 8 +------- app/code/Magento/Eav/Model/Entity/AbstractEntity.php | 8 +------- app/code/Magento/Sales/Model/Config/Ordered.php | 9 ++------- 3 files changed, 4 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php index d3f0c8be6f649..1b5cf37f6cbb8 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php +++ b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php @@ -292,13 +292,7 @@ public function attributesCompare($attributeOne, $attributeTwo) $sortOne = $attributeOne->getGroupSortPath() * 1000 + $attributeOne->getSortPath() * 0.0001; $sortTwo = $attributeTwo->getGroupSortPath() * 1000 + $attributeTwo->getSortPath() * 0.0001; - if ($sortOne > $sortTwo) { - return 1; - } elseif ($sortOne < $sortTwo) { - return -1; - } - - return 0; + return $sortOne <=> $sortTwo; } /** diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index 7159cedb61cee..0522ea0432176 100644 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -591,13 +591,7 @@ public function attributesCompare($firstAttribute, $secondAttribute) $firstSort = $firstAttribute->getSortWeight((int) $this->_sortingSetId); $secondSort = $secondAttribute->getSortWeight((int) $this->_sortingSetId); - if ($firstSort > $secondSort) { - return 1; - } elseif ($firstSort < $secondSort) { - return -1; - } - - return 0; + return $firstSort <=> $secondSort; } /** diff --git a/app/code/Magento/Sales/Model/Config/Ordered.php b/app/code/Magento/Sales/Model/Config/Ordered.php index 8c5ddb8e07df7..bae6223ee7d5e 100644 --- a/app/code/Magento/Sales/Model/Config/Ordered.php +++ b/app/code/Magento/Sales/Model/Config/Ordered.php @@ -167,13 +167,8 @@ function ($a, $b) { if (!isset($a['sort_order']) || !isset($b['sort_order'])) { return 0; } - if ($a['sort_order'] > $b['sort_order']) { - return 1; - } elseif ($a['sort_order'] < $b['sort_order']) { - return -1; - } else { - return 0; - } + + return $a['sort_order'] <=> $b['sort_order']; } ); } From afd16e91ade2c797ebbcee3c59eaa7ec556ea932 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Thu, 2 Aug 2018 13:00:00 -0500 Subject: [PATCH 0705/1171] MC-3311: Bug fixing for MC-1416 Localize open_browser_callback to prevent observable from being called for every instance --- .../adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index 6c952683aa1c9..46a09b28c4a7e 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -48,7 +48,6 @@ define([ varienGlobalEvents.attachEventHandler('tinymceSetContent', this.updateTextArea); varienGlobalEvents.attachEventHandler('tinymceSaveContent', this.saveContent); varienGlobalEvents.attachEventHandler('tinymceUndo', this.onUndo); - varienGlobalEvents.attachEventHandler('open_browser_callback', this.openFileBrowser); if (typeof tinyMceEditors === 'undefined') { window.tinyMceEditors = $H({}); @@ -117,6 +116,7 @@ define([ jQuery.when.apply(jQuery, deferreds).done(function () { tinyMCE4.init(settings); this.getPluginButtons().hide(); + this.eventBus.attachEventHandler('open_browser_callback', this.openFileBrowser); }.bind(this)); }, @@ -264,12 +264,15 @@ define([ * @param {*} w */ settings['file_browser_callback'] = function (fieldName, url, objectType, w) { - varienGlobalEvents.fireEvent('open_browser_callback', { + var payload = { win: w, type: objectType, field: fieldName - }); - }; + }; + + varienGlobalEvents.fireEvent('open_browser_callback', payload); + this.eventBus.fireEvent('open_browser_callback', payload); + }.bind(this); } if (this.config.width) { From b0de9b30919df45ca4314abb1d8b0c363a36f0e1 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel@daniel-ruf.de> Date: Thu, 2 Aug 2018 19:42:55 +0200 Subject: [PATCH 0706/1171] fix: remove unused ID --- .../Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml | 2 +- .../view/frontend/web/template/billing-address/form.html | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index 6d7533be7e9ea..866f5f5070940 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -12,7 +12,7 @@ <element name="isPaymentSection" type="text" selector="//*[@class='opc-progress-bar']/li[contains(@class, '_active') and span[contains(.,'Review & Payments')]]"/> <element name="availablePaymentSolutions" type="text" selector="#checkout-payment-method-load>div>div>div:nth-child(2)>div.payment-method-title.field.choice"/> <element name="notAvailablePaymentSolutions" type="text" selector="#checkout-payment-method-load>div>div>div.payment-method._active>div.payment-method-title.field.choice"/> - <element name="billingNewAddressForm" type="text" selector=".billing-new-address-form"/> + <element name="billingNewAddressForm" type="text" selector="[data-form='billing-new-address']"/> <element name="placeOrderDisabled" type="button" selector="#checkout-payment-method-load button.disabled"/> <element name="update" type="button" selector=".payment-method-billing-address .action.action-update"/> <element name="guestFirstName" type="input" selector=".billing-address-form input[name*='firstname']"/> diff --git a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html index dcd726076d900..54fe9a1f59394 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html @@ -9,8 +9,7 @@ <!-- ko template: getTemplate() --><!-- /ko --> <!--/ko--> <form data-bind="attr: {'data-hasrequired': $t('* Required Fields')}"> - <fieldset data-bind="attr: { id:'billing-new-address-form-'+index, value:index}" - class="billing-new-address-form fieldset address"> + <fieldset class="fieldset address" data-form="billing-new-address"> <!-- ko foreach: getRegion('additional-fieldsets') --> <!-- ko template: getTemplate() --><!-- /ko --> <!--/ko--> From bb3beea3b06590b8c6d37e4de80335915a25361d Mon Sep 17 00:00:00 2001 From: KevinBKozan <kkozan@magento.com> Date: Thu, 2 Aug 2018 13:33:49 -0500 Subject: [PATCH 0707/1171] MQE-1167: Remove composer.json from Mftf folder in app/code - removed composer.json from all app/code/<module>/Test/Mftf folders. --- .../AdminNotification/Test/Mftf/composer.json | 20 --------- .../Test/Mftf/composer.json | 23 ---------- .../AdvancedSearch/Test/Mftf/composer.json | 23 ---------- app/code/Magento/Amqp/Test/Mftf/composer.json | 18 -------- .../Magento/Analytics/Test/Mftf/composer.json | 20 --------- .../Test/Mftf/composer.json | 25 ----------- .../Authorization/Test/Mftf/composer.json | 17 ------- .../Authorizenet/Test/Mftf/composer.json | 26 ----------- .../Magento/Backend/Test/Mftf/composer.json | 35 --------------- .../Magento/Backup/Test/Mftf/composer.json | 19 -------- .../Magento/Braintree/Test/Mftf/composer.json | 34 -------------- .../Magento/Bundle/Test/Mftf/composer.json | 36 --------------- .../BundleGraphQl/Test/Mftf/composer.json | 20 --------- .../Test/Mftf/composer.json | 22 ---------- .../CacheInvalidate/Test/Mftf/composer.json | 17 ------- .../Magento/Captcha/Test/Mftf/composer.json | 20 --------- .../Magento/Catalog/Test/Mftf/composer.json | 44 ------------------- .../CatalogAnalytics/Test/Mftf/composer.json | 17 ------- .../CatalogGraphQl/Test/Mftf/composer.json | 26 ----------- .../Test/Mftf/composer.json | 25 ----------- .../CatalogInventory/Test/Mftf/composer.json | 23 ---------- .../CatalogRule/Test/Mftf/composer.json | 27 ------------ .../Test/Mftf/composer.json | 23 ---------- .../CatalogSearch/Test/Mftf/composer.json | 26 ----------- .../CatalogUrlRewrite/Test/Mftf/composer.json | 24 ---------- .../Test/Mftf/composer.json | 21 --------- .../CatalogWidget/Test/Mftf/composer.json | 24 ---------- .../Magento/Checkout/Test/Mftf/composer.json | 36 --------------- .../Test/Mftf/composer.json | 20 --------- app/code/Magento/Cms/Test/Mftf/composer.json | 28 ------------ .../CmsUrlRewrite/Test/Mftf/composer.json | 19 -------- .../Test/Mftf/composer.json | 23 ---------- .../Magento/Config/Test/Mftf/composer.json | 23 ---------- .../Test/Mftf/composer.json | 21 --------- .../Test/Mftf/composer.json | 34 -------------- .../Test/Mftf/composer.json | 19 -------- .../Test/Mftf/composer.json | 22 ---------- .../Magento/Contact/Test/Mftf/composer.json | 20 --------- .../Magento/Cookie/Test/Mftf/composer.json | 20 --------- app/code/Magento/Cron/Test/Mftf/composer.json | 20 --------- .../CurrencySymbol/Test/Mftf/composer.json | 21 --------- .../Magento/Customer/Test/Mftf/composer.json | 39 ---------------- .../CustomerAnalytics/Test/Mftf/composer.json | 17 ------- .../CustomerGraphQl/Test/Mftf/composer.json | 21 --------- .../Test/Mftf/composer.json | 22 ---------- .../Magento/Deploy/Test/Mftf/composer.json | 20 --------- .../Magento/Developer/Test/Mftf/composer.json | 18 -------- app/code/Magento/Dhl/Test/Mftf/composer.json | 28 ------------ .../Magento/Directory/Test/Mftf/composer.json | 19 -------- .../Downloadable/Test/Mftf/composer.json | 35 --------------- .../Test/Mftf/composer.json | 21 --------- .../Test/Mftf/composer.json | 22 ---------- app/code/Magento/Eav/Test/Mftf/composer.json | 21 --------- .../EavGraphQl/Test/Mftf/composer.json | 19 -------- .../Elasticsearch/Test/Mftf/composer.json | 27 ------------ .../Magento/Email/Test/Mftf/composer.json | 25 ----------- .../EncryptionKey/Test/Mftf/composer.json | 18 -------- .../Magento/Fedex/Test/Mftf/composer.json | 24 ---------- .../GiftMessage/Test/Mftf/composer.json | 27 ------------ .../GoogleAdwords/Test/Mftf/composer.json | 18 -------- .../GoogleAnalytics/Test/Mftf/composer.json | 22 ---------- .../GoogleOptimizer/Test/Mftf/composer.json | 22 ---------- .../Magento/GraphQl/Test/Mftf/composer.json | 22 ---------- .../Test/Mftf/composer.json | 21 --------- .../GroupedProduct/Test/Mftf/composer.json | 31 ------------- .../Test/Mftf/composer.json | 18 -------- .../ImportExport/Test/Mftf/composer.json | 21 --------- .../Magento/Indexer/Test/Mftf/composer.json | 17 ------- .../InstantPurchase/Test/Mftf/composer.json | 23 ---------- .../Integration/Test/Mftf/composer.json | 22 ---------- .../LayeredNavigation/Test/Mftf/composer.json | 18 -------- .../Marketplace/Test/Mftf/composer.json | 17 ------- .../MediaStorage/Test/Mftf/composer.json | 21 --------- .../MessageQueue/Test/Mftf/composer.json | 17 ------- app/code/Magento/Msrp/Test/Mftf/composer.json | 26 ----------- .../Multishipping/Test/Mftf/composer.json | 25 ----------- .../Magento/MysqlMq/Test/Mftf/composer.json | 18 -------- .../NewRelicReporting/Test/Mftf/composer.json | 23 ---------- .../Newsletter/Test/Mftf/composer.json | 24 ---------- .../OfflinePayments/Test/Mftf/composer.json | 21 --------- .../OfflineShipping/Test/Mftf/composer.json | 29 ------------ .../Magento/PageCache/Test/Mftf/composer.json | 19 -------- .../Magento/Payment/Test/Mftf/composer.json | 22 ---------- .../Magento/Paypal/Test/Mftf/composer.json | 35 --------------- .../Persistent/Test/Mftf/composer.json | 22 ---------- .../ProductAlert/Test/Mftf/composer.json | 23 ---------- .../ProductVideo/Test/Mftf/composer.json | 26 ----------- .../Magento/Quote/Test/Mftf/composer.json | 33 -------------- .../QuoteAnalytics/Test/Mftf/composer.json | 17 ------- .../Test/Mftf/composer.json | 22 ---------- .../Magento/Reports/Test/Mftf/composer.json | 32 -------------- .../Magento/RequireJs/Test/Mftf/composer.json | 16 ------- .../Magento/Review/Test/Mftf/composer.json | 28 ------------ .../ReviewAnalytics/Test/Mftf/composer.json | 17 ------- .../Magento/Robots/Test/Mftf/composer.json | 20 --------- app/code/Magento/Rss/Test/Mftf/composer.json | 19 -------- app/code/Magento/Rule/Test/Mftf/composer.json | 20 --------- .../Magento/Sales/Test/Mftf/composer.json | 43 ------------------ .../SalesAnalytics/Test/Mftf/composer.json | 17 ------- .../SalesInventory/Test/Mftf/composer.json | 20 --------- .../Magento/SalesRule/Test/Mftf/composer.json | 35 --------------- .../SalesSequence/Test/Mftf/composer.json | 16 ------- .../SampleData/Test/Mftf/composer.json | 19 -------- .../Magento/Search/Test/Mftf/composer.json | 21 --------- .../Magento/Security/Test/Mftf/composer.json | 22 ---------- .../SendFriend/Test/Mftf/composer.json | 19 -------- .../Magento/Shipping/Test/Mftf/composer.json | 34 -------------- .../Magento/Signifyd/Test/Mftf/composer.json | 27 ------------ .../Magento/Sitemap/Test/Mftf/composer.json | 28 ------------ .../Magento/Store/Test/Mftf/composer.json | 24 ---------- .../StoreGraphQl/Test/Mftf/composer.json | 20 --------- .../Magento/Swagger/Test/Mftf/composer.json | 16 ------- .../SwaggerWebapi/Test/Mftf/composer.json | 17 ------- .../Test/Mftf/composer.json | 20 --------- .../Magento/Swatches/Test/Mftf/composer.json | 29 ------------ .../SwatchesGraphQl/Test/Mftf/composer.json | 21 --------- .../Test/Mftf/composer.json | 17 ------- app/code/Magento/Tax/Test/Mftf/composer.json | 32 -------------- .../TaxGraphQl/Test/Mftf/composer.json | 20 --------- .../TaxImportExport/Test/Mftf/composer.json | 20 --------- .../Magento/Theme/Test/Mftf/composer.json | 32 -------------- .../Magento/Tinymce3/Test/Mftf/composer.json | 22 ---------- .../Translation/Test/Mftf/composer.json | 22 ---------- app/code/Magento/Ui/Test/Mftf/composer.json | 24 ---------- app/code/Magento/Ups/Test/Mftf/composer.json | 26 ----------- .../UrlRewrite/Test/Mftf/composer.json | 22 ---------- .../UrlRewriteGraphQl/Test/Mftf/composer.json | 21 --------- app/code/Magento/User/Test/Mftf/composer.json | 22 ---------- app/code/Magento/Usps/Test/Mftf/composer.json | 24 ---------- .../Magento/Variable/Test/Mftf/composer.json | 19 -------- .../Magento/Vault/Test/Mftf/composer.json | 22 ---------- .../Magento/Version/Test/Mftf/composer.json | 16 ------- .../Magento/Webapi/Test/Mftf/composer.json | 24 ---------- .../WebapiAsync/Test/Mftf/composer.json | 22 ---------- .../WebapiSecurity/Test/Mftf/composer.json | 17 ------- app/code/Magento/Weee/Test/Mftf/composer.json | 31 ------------- .../WeeeGraphQl/Test/Mftf/composer.json | 20 --------- .../Magento/Widget/Test/Mftf/composer.json | 25 ----------- .../Magento/Wishlist/Test/Mftf/composer.json | 33 -------------- .../WishlistAnalytics/Test/Mftf/composer.json | 17 ------- 140 files changed, 3249 deletions(-) delete mode 100644 app/code/Magento/AdminNotification/Test/Mftf/composer.json delete mode 100644 app/code/Magento/AdvancedPricingImportExport/Test/Mftf/composer.json delete mode 100644 app/code/Magento/AdvancedSearch/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Amqp/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Analytics/Test/Mftf/composer.json delete mode 100644 app/code/Magento/AsynchronousOperations/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Authorization/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Authorizenet/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Backend/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Backup/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Braintree/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Bundle/Test/Mftf/composer.json delete mode 100644 app/code/Magento/BundleGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/BundleImportExport/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CacheInvalidate/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Captcha/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Catalog/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CatalogAnalytics/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CatalogGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CatalogImportExport/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CatalogRule/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CatalogRuleConfigurable/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CatalogUrlRewriteGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CatalogWidget/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Checkout/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CheckoutAgreements/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Cms/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CmsUrlRewrite/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CmsUrlRewriteGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Config/Test/Mftf/composer.json delete mode 100644 app/code/Magento/ConfigurableImportExport/Test/Mftf/composer.json delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/composer.json delete mode 100644 app/code/Magento/ConfigurableProductGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/ConfigurableProductSales/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Contact/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Cookie/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Cron/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Customer/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CustomerAnalytics/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CustomerGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/CustomerImportExport/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Deploy/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Developer/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Dhl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Directory/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Downloadable/Test/Mftf/composer.json delete mode 100644 app/code/Magento/DownloadableGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/DownloadableImportExport/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Eav/Test/Mftf/composer.json delete mode 100644 app/code/Magento/EavGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Elasticsearch/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Email/Test/Mftf/composer.json delete mode 100644 app/code/Magento/EncryptionKey/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Fedex/Test/Mftf/composer.json delete mode 100644 app/code/Magento/GiftMessage/Test/Mftf/composer.json delete mode 100644 app/code/Magento/GoogleAdwords/Test/Mftf/composer.json delete mode 100644 app/code/Magento/GoogleAnalytics/Test/Mftf/composer.json delete mode 100644 app/code/Magento/GoogleOptimizer/Test/Mftf/composer.json delete mode 100644 app/code/Magento/GraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/GroupedImportExport/Test/Mftf/composer.json delete mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/composer.json delete mode 100644 app/code/Magento/GroupedProductGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/ImportExport/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Indexer/Test/Mftf/composer.json delete mode 100644 app/code/Magento/InstantPurchase/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Integration/Test/Mftf/composer.json delete mode 100644 app/code/Magento/LayeredNavigation/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Marketplace/Test/Mftf/composer.json delete mode 100644 app/code/Magento/MediaStorage/Test/Mftf/composer.json delete mode 100644 app/code/Magento/MessageQueue/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Msrp/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Multishipping/Test/Mftf/composer.json delete mode 100644 app/code/Magento/MysqlMq/Test/Mftf/composer.json delete mode 100644 app/code/Magento/NewRelicReporting/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Newsletter/Test/Mftf/composer.json delete mode 100644 app/code/Magento/OfflinePayments/Test/Mftf/composer.json delete mode 100644 app/code/Magento/OfflineShipping/Test/Mftf/composer.json delete mode 100644 app/code/Magento/PageCache/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Payment/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Paypal/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Persistent/Test/Mftf/composer.json delete mode 100644 app/code/Magento/ProductAlert/Test/Mftf/composer.json delete mode 100644 app/code/Magento/ProductVideo/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Quote/Test/Mftf/composer.json delete mode 100644 app/code/Magento/QuoteAnalytics/Test/Mftf/composer.json delete mode 100644 app/code/Magento/ReleaseNotification/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Reports/Test/Mftf/composer.json delete mode 100644 app/code/Magento/RequireJs/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Review/Test/Mftf/composer.json delete mode 100644 app/code/Magento/ReviewAnalytics/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Robots/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Rss/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Rule/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Sales/Test/Mftf/composer.json delete mode 100644 app/code/Magento/SalesAnalytics/Test/Mftf/composer.json delete mode 100644 app/code/Magento/SalesInventory/Test/Mftf/composer.json delete mode 100644 app/code/Magento/SalesRule/Test/Mftf/composer.json delete mode 100644 app/code/Magento/SalesSequence/Test/Mftf/composer.json delete mode 100644 app/code/Magento/SampleData/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Search/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Security/Test/Mftf/composer.json delete mode 100644 app/code/Magento/SendFriend/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Shipping/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Signifyd/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Sitemap/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Store/Test/Mftf/composer.json delete mode 100644 app/code/Magento/StoreGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Swagger/Test/Mftf/composer.json delete mode 100644 app/code/Magento/SwaggerWebapi/Test/Mftf/composer.json delete mode 100644 app/code/Magento/SwaggerWebapiAsync/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Swatches/Test/Mftf/composer.json delete mode 100644 app/code/Magento/SwatchesGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/SwatchesLayeredNavigation/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Tax/Test/Mftf/composer.json delete mode 100644 app/code/Magento/TaxGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/TaxImportExport/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Theme/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Tinymce3/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Translation/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Ui/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Ups/Test/Mftf/composer.json delete mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/composer.json delete mode 100644 app/code/Magento/UrlRewriteGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/User/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Usps/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Variable/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Vault/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Version/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Webapi/Test/Mftf/composer.json delete mode 100644 app/code/Magento/WebapiAsync/Test/Mftf/composer.json delete mode 100644 app/code/Magento/WebapiSecurity/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Weee/Test/Mftf/composer.json delete mode 100644 app/code/Magento/WeeeGraphQl/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Widget/Test/Mftf/composer.json delete mode 100644 app/code/Magento/Wishlist/Test/Mftf/composer.json delete mode 100644 app/code/Magento/WishlistAnalytics/Test/Mftf/composer.json diff --git a/app/code/Magento/AdminNotification/Test/Mftf/composer.json b/app/code/Magento/AdminNotification/Test/Mftf/composer.json deleted file mode 100644 index 6dcd053378c1b..0000000000000 --- a/app/code/Magento/AdminNotification/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-admin-notification", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/AdvancedPricingImportExport/Test/Mftf/composer.json b/app/code/Magento/AdvancedPricingImportExport/Test/Mftf/composer.json deleted file mode 100644 index df2817ad6a52f..0000000000000 --- a/app/code/Magento/AdvancedPricingImportExport/Test/Mftf/composer.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "magento/functional-test-module-advanced-pricing-import-export", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-import-export": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-import-export": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/AdvancedSearch/Test/Mftf/composer.json b/app/code/Magento/AdvancedSearch/Test/Mftf/composer.json deleted file mode 100644 index 7ae6dbb7eb709..0000000000000 --- a/app/code/Magento/AdvancedSearch/Test/Mftf/composer.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "magento/functional-test-module-advanced-search", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-search": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-search": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "php": "~7.1.3||~7.2.0" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Amqp/Test/Mftf/composer.json b/app/code/Magento/Amqp/Test/Mftf/composer.json deleted file mode 100644 index 3cb9637c9a7ba..0000000000000 --- a/app/code/Magento/Amqp/Test/Mftf/composer.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "magento/functional-test-module-amqp", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/framework-amqp": "100.0.0-dev", - "magento/framework-message-queue": "100.0.0-dev", - "php": "~7.1.3||~7.2.0" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Analytics/Test/Mftf/composer.json b/app/code/Magento/Analytics/Test/Mftf/composer.json deleted file mode 100644 index cc288fbbb130d..0000000000000 --- a/app/code/Magento/Analytics/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-analytics", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-integration": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/AsynchronousOperations/Test/Mftf/composer.json b/app/code/Magento/AsynchronousOperations/Test/Mftf/composer.json deleted file mode 100644 index e1ac71b46c546..0000000000000 --- a/app/code/Magento/AsynchronousOperations/Test/Mftf/composer.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "magento/functional-test-module-asynchronous-operations", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/framework-bulk": "100.0.0-dev", - "magento/functional-test-module-authorization": "100.0.0-dev", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/functional-test-module-user": "100.0.0-dev", - "php": "~7.1.3||~7.2.0" - }, - "suggest": { - "magento/functional-test-module-admin-notification": "100.0.0-dev", - "magento/functional-test-module-logging": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Authorization/Test/Mftf/composer.json b/app/code/Magento/Authorization/Test/Mftf/composer.json deleted file mode 100644 index fddc5acb034ed..0000000000000 --- a/app/code/Magento/Authorization/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-authorization", - "description": "Authorization module provides access to Magento ACL functionality.", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Authorizenet/Test/Mftf/composer.json b/app/code/Magento/Authorizenet/Test/Mftf/composer.json deleted file mode 100644 index 176f6207e1bac..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Mftf/composer.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "magento/functional-test-module-authorizenet", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-payment": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Backend/Test/Mftf/composer.json b/app/code/Magento/Backend/Test/Mftf/composer.json deleted file mode 100644 index adc3900cfe054..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/composer.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "magento/functional-test-module-backend", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backup": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-developer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-reports": "100.0.0-dev", - "magento/functional-test-module-require-js": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-security": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-translation": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/functional-test-module-user": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-theme": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Backup/Test/Mftf/composer.json b/app/code/Magento/Backup/Test/Mftf/composer.json deleted file mode 100644 index d7a6c804ab5b3..0000000000000 --- a/app/code/Magento/Backup/Test/Mftf/composer.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "magento/functional-test-module-backup", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-cron": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Braintree/Test/Mftf/composer.json b/app/code/Magento/Braintree/Test/Mftf/composer.json deleted file mode 100644 index b973b89602347..0000000000000 --- a/app/code/Magento/Braintree/Test/Mftf/composer.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "magento/functional-test-module-braintree", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/magento-composer-installer": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-instant-purchase": "100.0.0-dev", - "magento/functional-test-module-payment": "100.0.0-dev", - "magento/functional-test-module-paypal": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/functional-test-module-vault": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-checkout-agreements": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Bundle/Test/Mftf/composer.json b/app/code/Magento/Bundle/Test/Mftf/composer.json deleted file mode 100644 index 1e379d1bacd44..0000000000000 --- a/app/code/Magento/Bundle/Test/Mftf/composer.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "magento/functional-test-module-bundle", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-catalog-rule": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-gift-message": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-webapi": "100.0.0-dev", - "magento/functional-test-module-bundle-sample-data": "100.0.0-dev", - "magento/functional-test-module-sales-rule": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/BundleGraphQl/Test/Mftf/composer.json b/app/code/Magento/BundleGraphQl/Test/Mftf/composer.json deleted file mode 100644 index 33dffdc85a364..0000000000000 --- a/app/code/Magento/BundleGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-bundle-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-bundle": "100.0.0-dev", - "magento/functional-test-module-catalog-graph-ql": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/BundleImportExport/Test/Mftf/composer.json b/app/code/Magento/BundleImportExport/Test/Mftf/composer.json deleted file mode 100644 index 2210ef5248159..0000000000000 --- a/app/code/Magento/BundleImportExport/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-bundle-import-export", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-bundle": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-import-export": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-import-export": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CacheInvalidate/Test/Mftf/composer.json b/app/code/Magento/CacheInvalidate/Test/Mftf/composer.json deleted file mode 100644 index 0a6ffcf2f4661..0000000000000 --- a/app/code/Magento/CacheInvalidate/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-cache-invalidate", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-page-cache": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Captcha/Test/Mftf/composer.json b/app/code/Magento/Captcha/Test/Mftf/composer.json deleted file mode 100644 index 95c3aa5f76078..0000000000000 --- a/app/code/Magento/Captcha/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-captcha", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Catalog/Test/Mftf/composer.json b/app/code/Magento/Catalog/Test/Mftf/composer.json deleted file mode 100644 index 501fc0fa6c8db..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/composer.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "magento/functional-test-module-catalog", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-catalog-rule": "100.0.0-dev", - "magento/functional-test-module-catalog-url-rewrite": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-cms": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-indexer": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-msrp": "100.0.0-dev", - "magento/functional-test-module-page-cache": "100.0.0-dev", - "magento/functional-test-module-product-alert": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/functional-test-module-url-rewrite": "100.0.0-dev", - "magento/functional-test-module-widget": "100.0.0-dev", - "magento/functional-test-module-wishlist": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-cookie": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-catalog-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CatalogAnalytics/Test/Mftf/composer.json b/app/code/Magento/CatalogAnalytics/Test/Mftf/composer.json deleted file mode 100644 index c03b128d9ad83..0000000000000 --- a/app/code/Magento/CatalogAnalytics/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-catalog-analytics", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CatalogGraphQl/Test/Mftf/composer.json b/app/code/Magento/CatalogGraphQl/Test/Mftf/composer.json deleted file mode 100644 index 81a291e1527fc..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "magento/functional-test-module-catalog-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-search": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-eav-graph-ql": "100.0.0-dev", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "suggest": { - "magento/functional-test-module-graph-ql": "100.0.0-dev", - "magento/functional-test-module-store-graph-ql": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/composer.json b/app/code/Magento/CatalogImportExport/Test/Mftf/composer.json deleted file mode 100644 index 1f223fc5f46b5..0000000000000 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/composer.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "magento/functional-test-module-catalog-import-export", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-catalog-url-rewrite": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-import-export": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/composer.json b/app/code/Magento/CatalogInventory/Test/Mftf/composer.json deleted file mode 100644 index afd8d3ddb661a..0000000000000 --- a/app/code/Magento/CatalogInventory/Test/Mftf/composer.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "magento/functional-test-module-catalog-inventory", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CatalogRule/Test/Mftf/composer.json b/app/code/Magento/CatalogRule/Test/Mftf/composer.json deleted file mode 100644 index 0b57cf8dfaefa..0000000000000 --- a/app/code/Magento/CatalogRule/Test/Mftf/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "magento/functional-test-module-catalog-rule", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-rule": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-import-export": "100.0.0-dev", - "magento/functional-test-module-catalog-rule-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/composer.json b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/composer.json deleted file mode 100644 index 20b1a74c9416a..0000000000000 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/composer.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "magento/functional-test-module-catalog-rule-configurable", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/magento-composer-installer": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-rule": "100.0.0-dev", - "magento/functional-test-module-configurable-product": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-catalog-rule": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/composer.json b/app/code/Magento/CatalogSearch/Test/Mftf/composer.json deleted file mode 100644 index 7d54620eeac83..0000000000000 --- a/app/code/Magento/CatalogSearch/Test/Mftf/composer.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "magento/functional-test-module-catalog-search", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-search": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/composer.json b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/composer.json deleted file mode 100644 index 03c48982f5865..0000000000000 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/composer.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "magento/functional-test-module-catalog-url-rewrite", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-import-export": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-import-export": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/functional-test-module-url-rewrite": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CatalogUrlRewriteGraphQl/Test/Mftf/composer.json b/app/code/Magento/CatalogUrlRewriteGraphQl/Test/Mftf/composer.json deleted file mode 100644 index a66ca925561c1..0000000000000 --- a/app/code/Magento/CatalogUrlRewriteGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/functional-test-module-catalog-url-rewrite-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "suggest": { - "magento/functional-test-module-catalog-url-rewrite": "100.0.0-dev", - "magento/functional-test-module-catalog-graph-ql": "100.0.0-dev", - "magento/functional-test-module-url-rewrite-graph-ql": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/composer.json b/app/code/Magento/CatalogWidget/Test/Mftf/composer.json deleted file mode 100644 index 9b3437526718a..0000000000000 --- a/app/code/Magento/CatalogWidget/Test/Mftf/composer.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "magento/functional-test-module-catalog-widget", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-rule": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-widget": "100.0.0-dev", - "magento/functional-test-module-wishlist": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Checkout/Test/Mftf/composer.json b/app/code/Magento/Checkout/Test/Mftf/composer.json deleted file mode 100644 index 32c1a2c6bcd3f..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/composer.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "magento/functional-test-module-checkout", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-msrp": "100.0.0-dev", - "magento/functional-test-module-page-cache": "100.0.0-dev", - "magento/functional-test-module-payment": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-sales-rule": "100.0.0-dev", - "magento/functional-test-module-shipping": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-cookie": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/composer.json b/app/code/Magento/CheckoutAgreements/Test/Mftf/composer.json deleted file mode 100644 index 5ba702a5903a5..0000000000000 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-checkout-agreements", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Cms/Test/Mftf/composer.json b/app/code/Magento/Cms/Test/Mftf/composer.json deleted file mode 100644 index cdf6572dc1460..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/composer.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "magento/functional-test-module-cms", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-email": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/functional-test-module-variable": "100.0.0-dev", - "magento/functional-test-module-widget": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-cms-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CmsUrlRewrite/Test/Mftf/composer.json b/app/code/Magento/CmsUrlRewrite/Test/Mftf/composer.json deleted file mode 100644 index 9bed46134f87e..0000000000000 --- a/app/code/Magento/CmsUrlRewrite/Test/Mftf/composer.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "magento/functional-test-module-cms-url-rewrite", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-cms": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-url-rewrite": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CmsUrlRewriteGraphQl/Test/Mftf/composer.json b/app/code/Magento/CmsUrlRewriteGraphQl/Test/Mftf/composer.json deleted file mode 100644 index edd1c6b352623..0000000000000 --- a/app/code/Magento/CmsUrlRewriteGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "magento/functional-test-module-cms-url-rewrite-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-url-rewrite-graph-ql": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-cms": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-cms-url-rewrite": "100.0.0-dev", - "magento/functional-test-module-catalog-graph-ql": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Config/Test/Mftf/composer.json b/app/code/Magento/Config/Test/Mftf/composer.json deleted file mode 100644 index 0dacef66e5dce..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/composer.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "magento/functional-test-module-config", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-cron": "100.0.0-dev", - "magento/functional-test-module-deploy": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-email": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/ConfigurableImportExport/Test/Mftf/composer.json b/app/code/Magento/ConfigurableImportExport/Test/Mftf/composer.json deleted file mode 100644 index 74322a53512f9..0000000000000 --- a/app/code/Magento/ConfigurableImportExport/Test/Mftf/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/functional-test-module-configurable-import-export", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-import-export": "100.0.0-dev", - "magento/functional-test-module-configurable-product": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-import-export": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/composer.json b/app/code/Magento/ConfigurableProduct/Test/Mftf/composer.json deleted file mode 100644 index 64ef047693cf0..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/composer.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "magento/functional-test-module-configurable-product", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-msrp": "100.0.0-dev", - "magento/functional-test-module-webapi": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-product-video": "100.0.0-dev", - "magento/functional-test-module-configurable-sample-data": "100.0.0-dev", - "magento/functional-test-module-product-links-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/ConfigurableProductGraphQl/Test/Mftf/composer.json b/app/code/Magento/ConfigurableProductGraphQl/Test/Mftf/composer.json deleted file mode 100644 index 6d32ef35f6eb2..0000000000000 --- a/app/code/Magento/ConfigurableProductGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "magento/functional-test-module-configurable-product-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-configurable-product": "100.0.0-dev", - "magento/functional-test-module-catalog-graph-ql": "100.0.0-dev", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/ConfigurableProductSales/Test/Mftf/composer.json b/app/code/Magento/ConfigurableProductSales/Test/Mftf/composer.json deleted file mode 100644 index 416e6a0c45ce1..0000000000000 --- a/app/code/Magento/ConfigurableProductSales/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-configurable-product-sales", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-configurable-product": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Contact/Test/Mftf/composer.json b/app/code/Magento/Contact/Test/Mftf/composer.json deleted file mode 100644 index 824257ae8d0c4..0000000000000 --- a/app/code/Magento/Contact/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-contact", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-cms": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Cookie/Test/Mftf/composer.json b/app/code/Magento/Cookie/Test/Mftf/composer.json deleted file mode 100644 index 54578425c5516..0000000000000 --- a/app/code/Magento/Cookie/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-cookie", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-backend": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Cron/Test/Mftf/composer.json b/app/code/Magento/Cron/Test/Mftf/composer.json deleted file mode 100644 index a091882117d12..0000000000000 --- a/app/code/Magento/Cron/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-cron", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/composer.json b/app/code/Magento/CurrencySymbol/Test/Mftf/composer.json deleted file mode 100644 index ca14f3ebd0bf9..0000000000000 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/functional-test-module-currency-symbol", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-page-cache": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Customer/Test/Mftf/composer.json b/app/code/Magento/Customer/Test/Mftf/composer.json deleted file mode 100644 index 4bf83a4bf800a..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/composer.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "magento/functional-test-module-customer", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-authorization": "100.0.0-dev", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-integration": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-newsletter": "100.0.0-dev", - "magento/functional-test-module-page-cache": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-review": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/functional-test-module-wishlist": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-cookie": "100.0.0-dev", - "magento/functional-test-module-customer-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CustomerAnalytics/Test/Mftf/composer.json b/app/code/Magento/CustomerAnalytics/Test/Mftf/composer.json deleted file mode 100644 index 9b90f06de29c6..0000000000000 --- a/app/code/Magento/CustomerAnalytics/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-customer-analytics", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-customer": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CustomerGraphQl/Test/Mftf/composer.json b/app/code/Magento/CustomerGraphQl/Test/Mftf/composer.json deleted file mode 100644 index b173084a84d10..0000000000000 --- a/app/code/Magento/CustomerGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/functional-test-module-customer-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-authorization": "100.0.0-dev", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "suggest": { - "magento/functional-test-module-graph-ql": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/CustomerImportExport/Test/Mftf/composer.json b/app/code/Magento/CustomerImportExport/Test/Mftf/composer.json deleted file mode 100644 index adddda9ff7370..0000000000000 --- a/app/code/Magento/CustomerImportExport/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-customer-import-export", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-import-export": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Deploy/Test/Mftf/composer.json b/app/code/Magento/Deploy/Test/Mftf/composer.json deleted file mode 100644 index 7e18c4d1d1222..0000000000000 --- a/app/code/Magento/Deploy/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-deploy", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-require-js": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-user": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Developer/Test/Mftf/composer.json b/app/code/Magento/Developer/Test/Mftf/composer.json deleted file mode 100644 index ebd6230526271..0000000000000 --- a/app/code/Magento/Developer/Test/Mftf/composer.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "magento/functional-test-module-developer", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Dhl/Test/Mftf/composer.json b/app/code/Magento/Dhl/Test/Mftf/composer.json deleted file mode 100644 index 0da17d213c84e..0000000000000 --- a/app/code/Magento/Dhl/Test/Mftf/composer.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "magento/functional-test-module-dhl", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-shipping": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-checkout": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Directory/Test/Mftf/composer.json b/app/code/Magento/Directory/Test/Mftf/composer.json deleted file mode 100644 index c23156f6d923e..0000000000000 --- a/app/code/Magento/Directory/Test/Mftf/composer.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "magento/functional-test-module-directory", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Downloadable/Test/Mftf/composer.json b/app/code/Magento/Downloadable/Test/Mftf/composer.json deleted file mode 100644 index 6a2a5bb00f01e..0000000000000 --- a/app/code/Magento/Downloadable/Test/Mftf/composer.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "magento/functional-test-module-downloadable", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-gift-message": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-downloadable-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/DownloadableGraphQl/Test/Mftf/composer.json b/app/code/Magento/DownloadableGraphQl/Test/Mftf/composer.json deleted file mode 100644 index 725f17975e581..0000000000000 --- a/app/code/Magento/DownloadableGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/functional-test-module-downloadable-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-downloadable": "100.0.0-dev", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "suggest": { - "magento/functional-test-module-catalog-graph-ql": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/DownloadableImportExport/Test/Mftf/composer.json b/app/code/Magento/DownloadableImportExport/Test/Mftf/composer.json deleted file mode 100644 index da5caa42b87d5..0000000000000 --- a/app/code/Magento/DownloadableImportExport/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-downloadable-import-export", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-import-export": "100.0.0-dev", - "magento/functional-test-module-downloadable": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-import-export": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Eav/Test/Mftf/composer.json b/app/code/Magento/Eav/Test/Mftf/composer.json deleted file mode 100644 index 8ad451d607263..0000000000000 --- a/app/code/Magento/Eav/Test/Mftf/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/functional-test-module-eav", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/EavGraphQl/Test/Mftf/composer.json b/app/code/Magento/EavGraphQl/Test/Mftf/composer.json deleted file mode 100644 index f0390a211f3dc..0000000000000 --- a/app/code/Magento/EavGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "magento/functional-test-module-eav-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "suggest": { - "magento/functional-test-module-graph-ql": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/composer.json b/app/code/Magento/Elasticsearch/Test/Mftf/composer.json deleted file mode 100644 index b8c984c6e4ff6..0000000000000 --- a/app/code/Magento/Elasticsearch/Test/Mftf/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "magento/functional-test-module-elasticsearch", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/functional-test-module-advanced-search": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-search": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-search": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "suggest": { - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Email/Test/Mftf/composer.json b/app/code/Magento/Email/Test/Mftf/composer.json deleted file mode 100644 index 9f9b087c5fc15..0000000000000 --- a/app/code/Magento/Email/Test/Mftf/composer.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "magento/functional-test-module-email", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-cms": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev", - "magento/functional-test-module-variable": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-theme": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/composer.json b/app/code/Magento/EncryptionKey/Test/Mftf/composer.json deleted file mode 100644 index 5805c221e6e75..0000000000000 --- a/app/code/Magento/EncryptionKey/Test/Mftf/composer.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "magento/functional-test-module-encryption-key", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Fedex/Test/Mftf/composer.json b/app/code/Magento/Fedex/Test/Mftf/composer.json deleted file mode 100644 index 6152c679ec796..0000000000000 --- a/app/code/Magento/Fedex/Test/Mftf/composer.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "magento/functional-test-module-fedex", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-shipping": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/GiftMessage/Test/Mftf/composer.json b/app/code/Magento/GiftMessage/Test/Mftf/composer.json deleted file mode 100644 index 06f8c507d25c9..0000000000000 --- a/app/code/Magento/GiftMessage/Test/Mftf/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "magento/functional-test-module-gift-message", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-multishipping": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/GoogleAdwords/Test/Mftf/composer.json b/app/code/Magento/GoogleAdwords/Test/Mftf/composer.json deleted file mode 100644 index d40fb323175fe..0000000000000 --- a/app/code/Magento/GoogleAdwords/Test/Mftf/composer.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "magento/functional-test-module-google-adwords", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/GoogleAnalytics/Test/Mftf/composer.json b/app/code/Magento/GoogleAnalytics/Test/Mftf/composer.json deleted file mode 100644 index 3413ffcac6e42..0000000000000 --- a/app/code/Magento/GoogleAnalytics/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-google-analytics", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-cookie": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/GoogleOptimizer/Test/Mftf/composer.json b/app/code/Magento/GoogleOptimizer/Test/Mftf/composer.json deleted file mode 100644 index 2378140ac272a..0000000000000 --- a/app/code/Magento/GoogleOptimizer/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-google-optimizer", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-cms": "100.0.0-dev", - "magento/functional-test-module-google-analytics": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/GraphQl/Test/Mftf/composer.json b/app/code/Magento/GraphQl/Test/Mftf/composer.json deleted file mode 100644 index e86f458e9e6a4..0000000000000 --- a/app/code/Magento/GraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/functional-test-module-authorization": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "suggest": { - "magento/functional-test-module-webapi": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/GroupedImportExport/Test/Mftf/composer.json b/app/code/Magento/GroupedImportExport/Test/Mftf/composer.json deleted file mode 100644 index 37d7a9ffe9f52..0000000000000 --- a/app/code/Magento/GroupedImportExport/Test/Mftf/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/functional-test-module-grouped-import-export", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-import-export": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-grouped-product": "100.0.0-dev", - "magento/functional-test-module-import-export": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/composer.json b/app/code/Magento/GroupedProduct/Test/Mftf/composer.json deleted file mode 100644 index ebd0d3656952f..0000000000000 --- a/app/code/Magento/GroupedProduct/Test/Mftf/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "magento/functional-test-module-grouped-product", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-msrp": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-grouped-product-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/GroupedProductGraphQl/Test/Mftf/composer.json b/app/code/Magento/GroupedProductGraphQl/Test/Mftf/composer.json deleted file mode 100644 index 6335b45595db7..0000000000000 --- a/app/code/Magento/GroupedProductGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "magento/functional-test-module-grouped-product-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/functional-test-module-grouped-product": "100.0.0-dev", - "magento/functional-test-module-catalog-graph-ql": "100.0.0-dev", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/ImportExport/Test/Mftf/composer.json b/app/code/Magento/ImportExport/Test/Mftf/composer.json deleted file mode 100644 index fcce7ec7e84ff..0000000000000 --- a/app/code/Magento/ImportExport/Test/Mftf/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/functional-test-module-import-export", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Indexer/Test/Mftf/composer.json b/app/code/Magento/Indexer/Test/Mftf/composer.json deleted file mode 100644 index 487c2ac1abc85..0000000000000 --- a/app/code/Magento/Indexer/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-indexer", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/composer.json b/app/code/Magento/InstantPurchase/Test/Mftf/composer.json deleted file mode 100644 index 465002e5c4244..0000000000000 --- a/app/code/Magento/InstantPurchase/Test/Mftf/composer.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "magento/functional-test-module-instant-purchase", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-shipping": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-vault": "100.0.0-dev", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Integration/Test/Mftf/composer.json b/app/code/Magento/Integration/Test/Mftf/composer.json deleted file mode 100644 index 7168fc1945dab..0000000000000 --- a/app/code/Magento/Integration/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-integration", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-authorization": "100.0.0-dev", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-security": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-user": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/composer.json b/app/code/Magento/LayeredNavigation/Test/Mftf/composer.json deleted file mode 100644 index 9d046b8c2a105..0000000000000 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/composer.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "magento/functional-test-module-layered-navigation", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Marketplace/Test/Mftf/composer.json b/app/code/Magento/Marketplace/Test/Mftf/composer.json deleted file mode 100644 index 3d4fd741bc4ad..0000000000000 --- a/app/code/Magento/Marketplace/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-marketplace", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/MediaStorage/Test/Mftf/composer.json b/app/code/Magento/MediaStorage/Test/Mftf/composer.json deleted file mode 100644 index 7e7aeeef14517..0000000000000 --- a/app/code/Magento/MediaStorage/Test/Mftf/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/functional-test-module-media-storage", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/MessageQueue/Test/Mftf/composer.json b/app/code/Magento/MessageQueue/Test/Mftf/composer.json deleted file mode 100644 index 20783280811c7..0000000000000 --- a/app/code/Magento/MessageQueue/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-message-queue", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/magento-composer-installer": "100.0.0-dev", - "php": "~7.1.3||~7.2.0" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Msrp/Test/Mftf/composer.json b/app/code/Magento/Msrp/Test/Mftf/composer.json deleted file mode 100644 index cd85cb6cb268a..0000000000000 --- a/app/code/Magento/Msrp/Test/Mftf/composer.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "magento/functional-test-module-msrp", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-downloadable": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-grouped-product": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-bundle": "100.0.0-dev", - "magento/functional-test-module-msrp-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Multishipping/Test/Mftf/composer.json b/app/code/Magento/Multishipping/Test/Mftf/composer.json deleted file mode 100644 index 344b2b1ef6525..0000000000000 --- a/app/code/Magento/Multishipping/Test/Mftf/composer.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "magento/functional-test-module-multishipping", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-payment": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/MysqlMq/Test/Mftf/composer.json b/app/code/Magento/MysqlMq/Test/Mftf/composer.json deleted file mode 100644 index fec40ce087ab8..0000000000000 --- a/app/code/Magento/MysqlMq/Test/Mftf/composer.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "magento/functional-test-module-mysql-mq", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/magento-composer-installer": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "php": "~7.1.3||~7.2.0" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/composer.json b/app/code/Magento/NewRelicReporting/Test/Mftf/composer.json deleted file mode 100644 index 287475d097084..0000000000000 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/composer.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "magento/functional-test-module-new-relic-reporting", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/magento-composer-installer": "100.0.0-dev", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-configurable-product": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Newsletter/Test/Mftf/composer.json b/app/code/Magento/Newsletter/Test/Mftf/composer.json deleted file mode 100644 index 2a2ab444dd08a..0000000000000 --- a/app/code/Magento/Newsletter/Test/Mftf/composer.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "magento/functional-test-module-newsletter", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-cms": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-email": "100.0.0-dev", - "magento/functional-test-module-require-js": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-widget": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/OfflinePayments/Test/Mftf/composer.json b/app/code/Magento/OfflinePayments/Test/Mftf/composer.json deleted file mode 100644 index 7ce98340d75ee..0000000000000 --- a/app/code/Magento/OfflinePayments/Test/Mftf/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/functional-test-module-offline-payments", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-payment": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/OfflineShipping/Test/Mftf/composer.json b/app/code/Magento/OfflineShipping/Test/Mftf/composer.json deleted file mode 100644 index acca01f787402..0000000000000 --- a/app/code/Magento/OfflineShipping/Test/Mftf/composer.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "magento/functional-test-module-offline-shipping", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-sales-rule": "100.0.0-dev", - "magento/functional-test-module-shipping": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-offline-shipping-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/PageCache/Test/Mftf/composer.json b/app/code/Magento/PageCache/Test/Mftf/composer.json deleted file mode 100644 index 3bfe8f15ef64e..0000000000000 --- a/app/code/Magento/PageCache/Test/Mftf/composer.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "magento/functional-test-module-page-cache", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Payment/Test/Mftf/composer.json b/app/code/Magento/Payment/Test/Mftf/composer.json deleted file mode 100644 index 7311dae6fb074..0000000000000 --- a/app/code/Magento/Payment/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-payment", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Paypal/Test/Mftf/composer.json b/app/code/Magento/Paypal/Test/Mftf/composer.json deleted file mode 100644 index 1e2bc3c33a0c4..0000000000000 --- a/app/code/Magento/Paypal/Test/Mftf/composer.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "magento/functional-test-module-paypal", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-instant-purchase": "100.0.0-dev", - "magento/functional-test-module-payment": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/functional-test-module-vault": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-checkout-agreements": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Persistent/Test/Mftf/composer.json b/app/code/Magento/Persistent/Test/Mftf/composer.json deleted file mode 100644 index 0ea42b2755a0d..0000000000000 --- a/app/code/Magento/Persistent/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-persistent", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-cron": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-page-cache": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/ProductAlert/Test/Mftf/composer.json b/app/code/Magento/ProductAlert/Test/Mftf/composer.json deleted file mode 100644 index 37fda5965a0f8..0000000000000 --- a/app/code/Magento/ProductAlert/Test/Mftf/composer.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "magento/functional-test-module-product-alert", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/ProductVideo/Test/Mftf/composer.json b/app/code/Magento/ProductVideo/Test/Mftf/composer.json deleted file mode 100644 index df3a9afb3584f..0000000000000 --- a/app/code/Magento/ProductVideo/Test/Mftf/composer.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "magento/functional-test-module-product-video", - "description": "Add Video to Products", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/magento-composer-installer": "100.0.0-dev", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Quote/Test/Mftf/composer.json b/app/code/Magento/Quote/Test/Mftf/composer.json deleted file mode 100644 index 62932a84f3059..0000000000000 --- a/app/code/Magento/Quote/Test/Mftf/composer.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "magento/functional-test-module-quote", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-authorization": "100.0.0-dev", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-payment": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-sales-sequence": "100.0.0-dev", - "magento/functional-test-module-shipping": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-webapi": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/QuoteAnalytics/Test/Mftf/composer.json b/app/code/Magento/QuoteAnalytics/Test/Mftf/composer.json deleted file mode 100644 index 4f0f8f2c07344..0000000000000 --- a/app/code/Magento/QuoteAnalytics/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-quote-analytics", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-quote": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/ReleaseNotification/Test/Mftf/composer.json b/app/code/Magento/ReleaseNotification/Test/Mftf/composer.json deleted file mode 100644 index 9499e710aaed2..0000000000000 --- a/app/code/Magento/ReleaseNotification/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-release-notification", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/functional-test-module-user": "100.0.0-dev", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "suggest": { - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Reports/Test/Mftf/composer.json b/app/code/Magento/Reports/Test/Mftf/composer.json deleted file mode 100644 index 6801ad5adaf2e..0000000000000 --- a/app/code/Magento/Reports/Test/Mftf/composer.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "magento/functional-test-module-reports", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-cms": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-downloadable": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-review": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-sales-rule": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev", - "magento/functional-test-module-widget": "100.0.0-dev", - "magento/functional-test-module-wishlist": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/RequireJs/Test/Mftf/composer.json b/app/code/Magento/RequireJs/Test/Mftf/composer.json deleted file mode 100644 index 57bd4809b4140..0000000000000 --- a/app/code/Magento/RequireJs/Test/Mftf/composer.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "magento/functional-test-module-require-js", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Review/Test/Mftf/composer.json b/app/code/Magento/Review/Test/Mftf/composer.json deleted file mode 100644 index ae12d545ec244..0000000000000 --- a/app/code/Magento/Review/Test/Mftf/composer.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "magento/functional-test-module-review", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-newsletter": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-cookie": "100.0.0-dev", - "magento/functional-test-module-review-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/ReviewAnalytics/Test/Mftf/composer.json b/app/code/Magento/ReviewAnalytics/Test/Mftf/composer.json deleted file mode 100644 index 0164913753cb4..0000000000000 --- a/app/code/Magento/ReviewAnalytics/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-review-analytics", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-review": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Robots/Test/Mftf/composer.json b/app/code/Magento/Robots/Test/Mftf/composer.json deleted file mode 100644 index d3f2447e7970a..0000000000000 --- a/app/code/Magento/Robots/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-robots", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-theme": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Rss/Test/Mftf/composer.json b/app/code/Magento/Rss/Test/Mftf/composer.json deleted file mode 100644 index cf3a9dae8a054..0000000000000 --- a/app/code/Magento/Rss/Test/Mftf/composer.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "magento/functional-test-module-rss", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Rule/Test/Mftf/composer.json b/app/code/Magento/Rule/Test/Mftf/composer.json deleted file mode 100644 index 827d8603fdb99..0000000000000 --- a/app/code/Magento/Rule/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-rule", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Sales/Test/Mftf/composer.json b/app/code/Magento/Sales/Test/Mftf/composer.json deleted file mode 100644 index ac26bb1bf49f5..0000000000000 --- a/app/code/Magento/Sales/Test/Mftf/composer.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "magento/functional-test-module-sales", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-authorization": "100.0.0-dev", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-bundle": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-gift-message": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-payment": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-reports": "100.0.0-dev", - "magento/functional-test-module-sales-rule": "100.0.0-dev", - "magento/functional-test-module-sales-sequence": "100.0.0-dev", - "magento/functional-test-module-shipping": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/functional-test-module-widget": "100.0.0-dev", - "magento/functional-test-module-wishlist": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-sales-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/SalesAnalytics/Test/Mftf/composer.json b/app/code/Magento/SalesAnalytics/Test/Mftf/composer.json deleted file mode 100644 index 7382a7dcc8e5a..0000000000000 --- a/app/code/Magento/SalesAnalytics/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-sales-analytics", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-sales": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/SalesInventory/Test/Mftf/composer.json b/app/code/Magento/SalesInventory/Test/Mftf/composer.json deleted file mode 100644 index 7a72dfc649780..0000000000000 --- a/app/code/Magento/SalesInventory/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-sales-inventory", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/SalesRule/Test/Mftf/composer.json b/app/code/Magento/SalesRule/Test/Mftf/composer.json deleted file mode 100644 index 8839051a0848e..0000000000000 --- a/app/code/Magento/SalesRule/Test/Mftf/composer.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "magento/functional-test-module-sales-rule", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-rule": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-payment": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-reports": "100.0.0-dev", - "magento/functional-test-module-rule": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-shipping": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/functional-test-module-widget": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-sales-rule-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/SalesSequence/Test/Mftf/composer.json b/app/code/Magento/SalesSequence/Test/Mftf/composer.json deleted file mode 100644 index 5649df3b7e0ea..0000000000000 --- a/app/code/Magento/SalesSequence/Test/Mftf/composer.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "magento/functional-test-module-sales-sequence", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/SampleData/Test/Mftf/composer.json b/app/code/Magento/SampleData/Test/Mftf/composer.json deleted file mode 100644 index 039b87fa858b5..0000000000000 --- a/app/code/Magento/SampleData/Test/Mftf/composer.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "magento/functional-test-module-sample-data", - "description": "Sample Data fixtures", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "suggest": { - "magento/sample-data-media": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Search/Test/Mftf/composer.json b/app/code/Magento/Search/Test/Mftf/composer.json deleted file mode 100644 index 97cef910ffa02..0000000000000 --- a/app/code/Magento/Search/Test/Mftf/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/functional-test-module-search", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog-search": "100.0.0-dev", - "magento/functional-test-module-reports": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Security/Test/Mftf/composer.json b/app/code/Magento/Security/Test/Mftf/composer.json deleted file mode 100644 index 3429571c9b6d8..0000000000000 --- a/app/code/Magento/Security/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-security", - "description": "Security management module", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-user": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-customer": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/SendFriend/Test/Mftf/composer.json b/app/code/Magento/SendFriend/Test/Mftf/composer.json deleted file mode 100644 index 7e061e0159f02..0000000000000 --- a/app/code/Magento/SendFriend/Test/Mftf/composer.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "magento/functional-test-module-send-friend", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Shipping/Test/Mftf/composer.json b/app/code/Magento/Shipping/Test/Mftf/composer.json deleted file mode 100644 index 1b640d86f907d..0000000000000 --- a/app/code/Magento/Shipping/Test/Mftf/composer.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "magento/functional-test-module-shipping", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-contact": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-payment": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/functional-test-module-user": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-fedex": "100.0.0-dev", - "magento/functional-test-module-ups": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Signifyd/Test/Mftf/composer.json b/app/code/Magento/Signifyd/Test/Mftf/composer.json deleted file mode 100644 index 7ae52655d19f4..0000000000000 --- a/app/code/Magento/Signifyd/Test/Mftf/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "magento/functional-test-module-signifyd", - "description": "Submitting Case Entry to Signifyd on Order Creation", - "config": { - "sort-packages": true - }, - "require": { - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-payment": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "php": "~7.1.3||~7.2.0" - }, - "suggest": { - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Sitemap/Test/Mftf/composer.json b/app/code/Magento/Sitemap/Test/Mftf/composer.json deleted file mode 100644 index 46ad680c9fb1e..0000000000000 --- a/app/code/Magento/Sitemap/Test/Mftf/composer.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "magento/functional-test-module-sitemap", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-url-rewrite": "100.0.0-dev", - "magento/functional-test-module-cms": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-robots": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Store/Test/Mftf/composer.json b/app/code/Magento/Store/Test/Mftf/composer.json deleted file mode 100644 index 81305cf049e6c..0000000000000 --- a/app/code/Magento/Store/Test/Mftf/composer.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "magento/functional-test-module-store", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-deploy": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/StoreGraphQl/Test/Mftf/composer.json b/app/code/Magento/StoreGraphQl/Test/Mftf/composer.json deleted file mode 100644 index 10407d0dc179c..0000000000000 --- a/app/code/Magento/StoreGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-store-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "suggest": { - "magento/functional-test-module-graph-ql": "100.0.0-dev", - "magento/functional-test-module-catalog-graph-ql": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Swagger/Test/Mftf/composer.json b/app/code/Magento/Swagger/Test/Mftf/composer.json deleted file mode 100644 index a997257192fce..0000000000000 --- a/app/code/Magento/Swagger/Test/Mftf/composer.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "magento/functional-test-module-swagger", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/SwaggerWebapi/Test/Mftf/composer.json b/app/code/Magento/SwaggerWebapi/Test/Mftf/composer.json deleted file mode 100644 index 22ed346ef2081..0000000000000 --- a/app/code/Magento/SwaggerWebapi/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-swagger-webapi", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-swagger": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/SwaggerWebapiAsync/Test/Mftf/composer.json b/app/code/Magento/SwaggerWebapiAsync/Test/Mftf/composer.json deleted file mode 100644 index 58d3740a12021..0000000000000 --- a/app/code/Magento/SwaggerWebapiAsync/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-swagger-webapi-async", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-swagger": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Swatches/Test/Mftf/composer.json b/app/code/Magento/Swatches/Test/Mftf/composer.json deleted file mode 100644 index daac589907f2c..0000000000000 --- a/app/code/Magento/Swatches/Test/Mftf/composer.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "magento/functional-test-module-swatches", - "description": "Add Swatches to Products", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-configurable-product": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-layered-navigation": "100.0.0-dev", - "magento/functional-test-module-swatches-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/SwatchesGraphQl/Test/Mftf/composer.json b/app/code/Magento/SwatchesGraphQl/Test/Mftf/composer.json deleted file mode 100644 index 7a62d9bd33ab9..0000000000000 --- a/app/code/Magento/SwatchesGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/functional-test-module-swatches-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-swatches": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-catalog-graph-ql": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/SwatchesLayeredNavigation/Test/Mftf/composer.json b/app/code/Magento/SwatchesLayeredNavigation/Test/Mftf/composer.json deleted file mode 100644 index e37d2bfda247d..0000000000000 --- a/app/code/Magento/SwatchesLayeredNavigation/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-swatches-layered-navigation", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/magento-composer-installer": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Tax/Test/Mftf/composer.json b/app/code/Magento/Tax/Test/Mftf/composer.json deleted file mode 100644 index e2d9db9f8f76e..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/composer.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "magento/functional-test-module-tax", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-page-cache": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-reports": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-shipping": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-tax-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/TaxGraphQl/Test/Mftf/composer.json b/app/code/Magento/TaxGraphQl/Test/Mftf/composer.json deleted file mode 100644 index 144f778833cd4..0000000000000 --- a/app/code/Magento/TaxGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-tax-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "suggest": { - "magento/functional-test-module-tax": "100.0.0-dev", - "magento/functional-test-module-catalog-graph-ql": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/TaxImportExport/Test/Mftf/composer.json b/app/code/Magento/TaxImportExport/Test/Mftf/composer.json deleted file mode 100644 index 39bc90df32651..0000000000000 --- a/app/code/Magento/TaxImportExport/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-tax-import-export", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Theme/Test/Mftf/composer.json b/app/code/Magento/Theme/Test/Mftf/composer.json deleted file mode 100644 index f93dac15279a4..0000000000000 --- a/app/code/Magento/Theme/Test/Mftf/composer.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "magento/functional-test-module-theme", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-cms": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-media-storage": "100.0.0-dev", - "magento/functional-test-module-require-js": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/functional-test-module-widget": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-translation": "100.0.0-dev", - "magento/functional-test-module-theme-sample-data": "100.0.0-dev", - "magento/functional-test-module-deploy": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Tinymce3/Test/Mftf/composer.json b/app/code/Magento/Tinymce3/Test/Mftf/composer.json deleted file mode 100644 index 84c1f9376bf45..0000000000000 --- a/app/code/Magento/Tinymce3/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-tinymce-3", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-ui": "100.0.0-dev", - "magento/functional-test-module-variable": "100.0.0-dev", - "magento/functional-test-module-widget": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-cms": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Translation/Test/Mftf/composer.json b/app/code/Magento/Translation/Test/Mftf/composer.json deleted file mode 100644 index 36bfa66adda0f..0000000000000 --- a/app/code/Magento/Translation/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-translation", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-developer": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-deploy": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Ui/Test/Mftf/composer.json b/app/code/Magento/Ui/Test/Mftf/composer.json deleted file mode 100644 index 86a99d02bd959..0000000000000 --- a/app/code/Magento/Ui/Test/Mftf/composer.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "magento/functional-test-module-ui", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-authorization": "100.0.0-dev", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-user": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Ups/Test/Mftf/composer.json b/app/code/Magento/Ups/Test/Mftf/composer.json deleted file mode 100644 index 560eaab1a19b1..0000000000000 --- a/app/code/Magento/Ups/Test/Mftf/composer.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "magento/functional-test-module-ups", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-shipping": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/composer.json b/app/code/Magento/UrlRewrite/Test/Mftf/composer.json deleted file mode 100644 index 2d906c3499ac6..0000000000000 --- a/app/code/Magento/UrlRewrite/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-url-rewrite", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-url-rewrite": "100.0.0-dev", - "magento/functional-test-module-cms": "100.0.0-dev", - "magento/functional-test-module-cms-url-rewrite": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/UrlRewriteGraphQl/Test/Mftf/composer.json b/app/code/Magento/UrlRewriteGraphQl/Test/Mftf/composer.json deleted file mode 100644 index 06c58dbb59468..0000000000000 --- a/app/code/Magento/UrlRewriteGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/functional-test-module-url-rewrite-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-url-rewrite": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-graph-ql": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/User/Test/Mftf/composer.json b/app/code/Magento/User/Test/Mftf/composer.json deleted file mode 100644 index 6deceeb6b856f..0000000000000 --- a/app/code/Magento/User/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-user", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-authorization": "100.0.0-dev", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-email": "100.0.0-dev", - "magento/functional-test-module-integration": "100.0.0-dev", - "magento/functional-test-module-security": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Usps/Test/Mftf/composer.json b/app/code/Magento/Usps/Test/Mftf/composer.json deleted file mode 100644 index e3b487ad80d9f..0000000000000 --- a/app/code/Magento/Usps/Test/Mftf/composer.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "magento/functional-test-module-usps", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-shipping": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Variable/Test/Mftf/composer.json b/app/code/Magento/Variable/Test/Mftf/composer.json deleted file mode 100644 index 2360634369fed..0000000000000 --- a/app/code/Magento/Variable/Test/Mftf/composer.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "magento/functional-test-module-variable", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-config": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Vault/Test/Mftf/composer.json b/app/code/Magento/Vault/Test/Mftf/composer.json deleted file mode 100644 index 64fe51a3a1a3c..0000000000000 --- a/app/code/Magento/Vault/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-vault", - "description": "", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-payment": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Version/Test/Mftf/composer.json b/app/code/Magento/Version/Test/Mftf/composer.json deleted file mode 100644 index 24037307b7878..0000000000000 --- a/app/code/Magento/Version/Test/Mftf/composer.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "magento/functional-test-module-version", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Webapi/Test/Mftf/composer.json b/app/code/Magento/Webapi/Test/Mftf/composer.json deleted file mode 100644 index 28d5769034a39..0000000000000 --- a/app/code/Magento/Webapi/Test/Mftf/composer.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "magento/functional-test-module-webapi", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-authorization": "100.0.0-dev", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-integration": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-user": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/WebapiAsync/Test/Mftf/composer.json b/app/code/Magento/WebapiAsync/Test/Mftf/composer.json deleted file mode 100644 index 6bf5375b7c873..0000000000000 --- a/app/code/Magento/WebapiAsync/Test/Mftf/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "magento/functional-test-module-webapi-async", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-webapi": "100.0.0-dev", - "magento/functional-test-module-asynchronous-operations": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-user": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/WebapiSecurity/Test/Mftf/composer.json b/app/code/Magento/WebapiSecurity/Test/Mftf/composer.json deleted file mode 100644 index de96b3a3210f0..0000000000000 --- a/app/code/Magento/WebapiSecurity/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-webapi-security", - "description": "WebapiSecurity module provides option to loosen security on some webapi resources.", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-webapi": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Weee/Test/Mftf/composer.json b/app/code/Magento/Weee/Test/Mftf/composer.json deleted file mode 100644 index 9e5f7c5772a4e..0000000000000 --- a/app/code/Magento/Weee/Test/Mftf/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "magento/functional-test-module-weee", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-directory": "100.0.0-dev", - "magento/functional-test-module-eav": "100.0.0-dev", - "magento/functional-test-module-page-cache": "100.0.0-dev", - "magento/functional-test-module-quote": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-tax": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-bundle": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/WeeeGraphQl/Test/Mftf/composer.json b/app/code/Magento/WeeeGraphQl/Test/Mftf/composer.json deleted file mode 100644 index 09b1dc9afe551..0000000000000 --- a/app/code/Magento/WeeeGraphQl/Test/Mftf/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "magento/functional-test-module-weee-graph-ql", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0" - }, - "suggest": { - "magento/functional-test-module-weee": "100.0.0-dev", - "magento/functional-test-module-catalog-graph-ql": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Widget/Test/Mftf/composer.json b/app/code/Magento/Widget/Test/Mftf/composer.json deleted file mode 100644 index 693fa651f5974..0000000000000 --- a/app/code/Magento/Widget/Test/Mftf/composer.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "magento/functional-test-module-widget", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-cms": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-theme": "100.0.0-dev", - "magento/functional-test-module-variable": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-widget-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/Wishlist/Test/Mftf/composer.json b/app/code/Magento/Wishlist/Test/Mftf/composer.json deleted file mode 100644 index d016f9346f60c..0000000000000 --- a/app/code/Magento/Wishlist/Test/Mftf/composer.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "magento/functional-test-module-wishlist", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-backend": "100.0.0-dev", - "magento/functional-test-module-catalog": "100.0.0-dev", - "magento/functional-test-module-catalog-inventory": "100.0.0-dev", - "magento/functional-test-module-checkout": "100.0.0-dev", - "magento/functional-test-module-customer": "100.0.0-dev", - "magento/functional-test-module-rss": "100.0.0-dev", - "magento/functional-test-module-sales": "100.0.0-dev", - "magento/functional-test-module-store": "100.0.0-dev", - "magento/functional-test-module-ui": "100.0.0-dev" - }, - "suggest": { - "magento/functional-test-module-configurable-product": "100.0.0-dev", - "magento/functional-test-module-downloadable": "100.0.0-dev", - "magento/functional-test-module-bundle": "100.0.0-dev", - "magento/functional-test-module-cookie": "100.0.0-dev", - "magento/functional-test-module-grouped-product": "100.0.0-dev", - "magento/functional-test-module-wishlist-sample-data": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} diff --git a/app/code/Magento/WishlistAnalytics/Test/Mftf/composer.json b/app/code/Magento/WishlistAnalytics/Test/Mftf/composer.json deleted file mode 100644 index 6969178cfdaa6..0000000000000 --- a/app/code/Magento/WishlistAnalytics/Test/Mftf/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "magento/functional-test-module-wishlist-analytics", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "magento/magento2-functional-testing-framework": "2.2.0", - "magento/functional-test-module-wishlist": "100.0.0-dev" - }, - "type": "magento2-test", - "license": [ - "OSL-3.0", - "AFL-3.0" - ] -} From 105da22de539c521f770c5228d0d7156a9ebbfb1 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel@daniel-ruf.de> Date: Thu, 2 Aug 2018 22:57:18 +0200 Subject: [PATCH 0708/1171] fix: fix Less linter warnings in adminhtml --- .../backend/Magento_Analytics/web/css/source/_module.less | 3 +-- .../backend/Magento_Backend/web/css/source/_module-old.less | 3 +-- .../web/css/source/module/components/_currency-addon.less | 3 ++- .../web/css/source/module/steps/_bulk-images.less | 2 +- .../Magento_ReleaseNotification/web/css/source/_module.less | 3 +-- .../backend/Magento_Ui/web/css/source/_module-old.less | 4 ++-- .../Magento_VisualMerchandiser/web/css/source/_module.less | 2 +- .../adminhtml/Magento/backend/web/css/source/_reset.less | 4 ++++ .../backend/web/css/source/actions/_actions-dropdown.less | 1 - .../backend/web/css/source/actions/_actions-switcher.less | 1 - .../backend/web/css/source/components/_file-insertion.less | 1 + .../backend/web/css/source/components/_media-gallery.less | 1 + .../Magento/backend/web/css/source/components/_popups.less | 1 + .../adminhtml/Magento/backend/web/css/source/forms/_temp.less | 1 + app/design/adminhtml/Magento/backend/web/css/styles-old.less | 2 +- .../adminhtml/Magento/backend/web/mui/styles/_table.less | 1 - 16 files changed, 18 insertions(+), 15 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Analytics/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Analytics/web/css/source/_module.less index 45f1a835d18d8..dbc4e54762d7f 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Analytics/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Analytics/web/css/source/_module.less @@ -109,8 +109,7 @@ } .advanced-reports-subscription-close { - display: inline-block; - vertical-align: top; + display: block; float: right; } diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/_module-old.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/_module-old.less index 16db9092db368..c4bed53dcbe80 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/_module-old.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/_module-old.less @@ -54,7 +54,6 @@ .search-global-field .mage-suggest { position: static; display: block; - vertical-align: baseline; width: auto; background-color: transparent; border: none; @@ -177,7 +176,7 @@ } &:before { - display: inline-block; + display: block; float: right; margin-left: 4px; font-size: 13px; diff --git a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less index f3bb1fede2512..c491e4bdfc71e 100644 --- a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less +++ b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less @@ -30,7 +30,8 @@ width: 100%; .admin__control-text { - appearence: none; + -webkit-appearance: none; + appearance: none; -webkit-flex-grow: 1; flex-grow: 1; -ms-flex-order: 1; diff --git a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/steps/_bulk-images.less b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/steps/_bulk-images.less index b2dc94d9ffa74..25c7be1467bda 100644 --- a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/steps/_bulk-images.less +++ b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/steps/_bulk-images.less @@ -98,7 +98,7 @@ .admin__field-control { float: right; - display: inline-block; + display: block; } } } diff --git a/app/design/adminhtml/Magento/backend/Magento_ReleaseNotification/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_ReleaseNotification/web/css/source/_module.less index a7b6f553f1ff8..932699d337cd7 100644 --- a/app/design/adminhtml/Magento/backend/Magento_ReleaseNotification/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_ReleaseNotification/web/css/source/_module.less @@ -69,8 +69,7 @@ } .release-notification-button-next, .release-notification-button-back { - display: inline-block; - vertical-align: top; + display: block; float: right; position: absolute; bottom: 4rem; diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less index 11e0fcc7c8b84..3756fe678a3c9 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less @@ -180,14 +180,14 @@ .lib-clearfix(); [data-part=left] { - display: inline-block; + display: block; width: 45%; float: left; text-align: left; } [data-part=right] { - display: inline-block; + display: block; width: 45%; text-align: right; float: right; diff --git a/app/design/adminhtml/Magento/backend/Magento_VisualMerchandiser/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_VisualMerchandiser/web/css/source/_module.less index 909935e1f8ea8..1cd867efdd13b 100644 --- a/app/design/adminhtml/Magento/backend/Magento_VisualMerchandiser/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_VisualMerchandiser/web/css/source/_module.less @@ -68,7 +68,7 @@ a { color: @color-gray85; - display: inline-block; + display: block; float: left; text-decoration: none; } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/_reset.less b/app/design/adminhtml/Magento/backend/web/css/source/_reset.less index 3a2847c82b8ce..8d9927975edbe 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/_reset.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/_reset.less @@ -11,6 +11,7 @@ html { // Prevent iOS text size adjust after orientation change, without disabling user zoom. -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; + text-size-adjust: 100%; box-sizing: border-box; } @@ -234,6 +235,7 @@ html input[type='button'], input[type='reset'], input[type='submit'] { -webkit-appearance: button; + appearance: button; cursor: pointer; } @@ -275,6 +277,7 @@ input[type='number'] { // Address 'appearance' set to 'searchfield' in Safari and Chrome. input[type='search'] { -webkit-appearance: textfield; + appearance: textfield; } // Remove inner padding and search cancel button in Safari and Chrome on OS X. @@ -283,6 +286,7 @@ input[type='search'] { &::-webkit-search-cancel-button, &::-webkit-search-decoration { -webkit-appearance: none; + appearance: none; } } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less index a2e6a4485cc02..a50d0892d73cd 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less @@ -231,7 +231,6 @@ border: 0; display: inline; margin: 0; - width: 6rem; body._keyfocus &:focus { box-shadow: none; diff --git a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-switcher.less b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-switcher.less index fbef3f4b5f3a6..cf7a8dd56cb3b 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-switcher.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-switcher.less @@ -130,7 +130,6 @@ display: block; height: @actions-switcher__height; transition: background @actions-switcher-speed ease-in 0s; - vertical-align: middle; width: @actions-switcher__width; z-index: 0; } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_file-insertion.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_file-insertion.less index 96b8ce8df1219..cd17ed69008eb 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_file-insertion.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_file-insertion.less @@ -23,6 +23,7 @@ input { -moz-transform: none; + transform: none; border: none; opacity: 1; position: static; diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less index 85ccc235a3bdd..32dd4004f427e 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less @@ -323,6 +323,7 @@ .image-panel-preview { -ms-flex: 1; -webkit-box-flex: 1; + box-flex: 1; -webkit-flex: 1; flex: 1; } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less index 7bd10ad491f0c..5ac31a066d238 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less @@ -280,6 +280,7 @@ input { -moz-transform: none; + transform: none; border: none; opacity: 1; position: static; diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less index 0bfa454adbf0d..945f81292c19f 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less @@ -470,6 +470,7 @@ label.mage-error { .action-select-multiselect { -webkit-appearance: menulist-button; + appearance: menulist-button; height: 38px; left: -1rem; min-width: 0; diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index 8d314e06899b3..033df45582901 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -279,7 +279,6 @@ .nested .choice .label, .nested .choice .control { float: none; - width: auto; position: static; left: auto; text-align: left; @@ -1215,6 +1214,7 @@ -webkit-user-select: none; // use in 41 Chrome -moz-user-select: none; // use in 36 Firefox -ms-user-select: none; // use in 11 IE + user-select: none; min-height: 39px; } diff --git a/app/design/adminhtml/Magento/backend/web/mui/styles/_table.less b/app/design/adminhtml/Magento/backend/web/mui/styles/_table.less index 7565ee88714f6..f99deeacb3eab 100644 --- a/app/design/adminhtml/Magento/backend/web/mui/styles/_table.less +++ b/app/design/adminhtml/Magento/backend/web/mui/styles/_table.less @@ -455,7 +455,6 @@ td.col-type { .import { display: block; - vertical-align: top; } .action-reset { From 410027d7a484d0fd2fa74de569f3107ede2c6575 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Thu, 2 Aug 2018 17:10:23 -0500 Subject: [PATCH 0709/1171] MAGETWO-90632: DHL Shipping Method not available --- app/code/Magento/Dhl/Model/Carrier.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Dhl/Model/Carrier.php b/app/code/Magento/Dhl/Model/Carrier.php index 6262d71c4edcc..55df0748b1f06 100644 --- a/app/code/Magento/Dhl/Model/Carrier.php +++ b/app/code/Magento/Dhl/Model/Carrier.php @@ -1970,6 +1970,6 @@ protected function isDutiable($origCountryId, $destCountryId) return self::DHL_CONTENT_TYPE_NON_DOC == $this->getConfigData('content_type') - && !$this->_isDomestic; + || !$this->_isDomestic; } } From 95e671d3b29bb932f8255990d1701a50fbb02ab6 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Thu, 2 Aug 2018 18:21:22 -0500 Subject: [PATCH 0710/1171] MAGETWO-90863: Error handling responses from MBI --- .../Model/Connector/Http/ResponseResolver.php | 10 +- .../Analytics/Model/Connector/OTPRequest.php | 5 +- .../Model/Connector/SignUpCommand.php | 6 +- .../Model/Connector/UpdateCommand.php | 5 +- .../Connector/Http/ResponseResolverTest.php | 122 ++++++++++++++---- 5 files changed, 113 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/Analytics/Model/Connector/Http/ResponseResolver.php b/app/code/Magento/Analytics/Model/Connector/Http/ResponseResolver.php index ec198e4a3c40b..4cba11123873f 100644 --- a/app/code/Magento/Analytics/Model/Connector/Http/ResponseResolver.php +++ b/app/code/Magento/Analytics/Model/Connector/Http/ResponseResolver.php @@ -38,7 +38,15 @@ public function __construct(ConverterInterface $converter, array $responseHandle public function getResult(\Zend_Http_Response $response) { $result = false; - $responseBody = $this->converter->fromBody($response->getBody()); + preg_match('#(?:Content-Type:\s*)(\w\S+)#i', $this->converter->getContentTypeHeader(), $contentType); + $converterContentType = $contentType[1]; + + if ($response->getBody() && is_int(strripos($response->getHeader('Content-Type'), $converterContentType))) { + $responseBody = $this->converter->fromBody($response->getBody()); + } else { + $responseBody = []; + } + if (array_key_exists($response->getStatus(), $this->responseHandlers)) { $result = $this->responseHandlers[$response->getStatus()]->handleResponse($responseBody); } diff --git a/app/code/Magento/Analytics/Model/Connector/OTPRequest.php b/app/code/Magento/Analytics/Model/Connector/OTPRequest.php index dfa283e10d070..c05357400d075 100644 --- a/app/code/Magento/Analytics/Model/Connector/OTPRequest.php +++ b/app/code/Magento/Analytics/Model/Connector/OTPRequest.php @@ -103,8 +103,9 @@ public function call() if (!$result) { $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.' + 'Obtaining of an OTP from the MBI service has been failed: %s. Content-Type: %s', + !empty($response->getBody()) ? $response->getBody() : 'Response body is empty', + $response->getHeader('Content-Type') ) ); } diff --git a/app/code/Magento/Analytics/Model/Connector/SignUpCommand.php b/app/code/Magento/Analytics/Model/Connector/SignUpCommand.php index c1f8152f3134e..e35c9bb42bc43 100644 --- a/app/code/Magento/Analytics/Model/Connector/SignUpCommand.php +++ b/app/code/Magento/Analytics/Model/Connector/SignUpCommand.php @@ -110,8 +110,10 @@ public function execute() if (!$result) { $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.' + 'Subscription for MBI service has been failed. An error occurred during token exchange: %s.' + . ' Content-Type: %s', + !empty($response->getBody()) ? $response->getBody() : 'Response body is empty', + $response->getHeader('Content-Type') ) ); } diff --git a/app/code/Magento/Analytics/Model/Connector/UpdateCommand.php b/app/code/Magento/Analytics/Model/Connector/UpdateCommand.php index 7b4e452a7b451..59878ff9c0814 100644 --- a/app/code/Magento/Analytics/Model/Connector/UpdateCommand.php +++ b/app/code/Magento/Analytics/Model/Connector/UpdateCommand.php @@ -101,8 +101,9 @@ public function execute() 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.' + 'Update of the subscription for MBI service has been failed: %s. Content-Type: %s', + !empty($response->getBody()) ? $response->getBody() : 'Response body is empty', + $response->getHeader('Content-Type') ) ); } diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/ResponseResolverTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/ResponseResolverTest.php index 3d4c90bcd07f7..f9fce447ca381 100644 --- a/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/ResponseResolverTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/ResponseResolverTest.php @@ -3,49 +3,115 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Analytics\Test\Unit\Model\Connector\Http; -use Magento\Analytics\Model\Connector\Http\JsonConverter; +use Magento\Analytics\Model\Connector\Http\ConverterInterface; use Magento\Analytics\Model\Connector\Http\ResponseHandlerInterface; use Magento\Analytics\Model\Connector\Http\ResponseResolver; -use Magento\Framework\Serialize\Serializer\Json; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; class ResponseResolverTest extends \PHPUnit\Framework\TestCase { - public function testGetResultHandleResponseSuccess() + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var ConverterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $converterMock; + + /** + * @var ResponseHandlerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $successResponseHandlerMock; + + /** + * @var ResponseHandlerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $notFoundResponseHandlerMock; + + /** + * @var ResponseResolver + */ + private $responseResolver; + + /** + * @return void + */ + protected function setUp() { - $expectedBody = ['test' => 'testValue']; - $response = new \Zend_Http_Response(201, [], json_encode($expectedBody)); - $responseHandlerMock = $this->getMockBuilder(ResponseHandlerInterface::class) - ->getMockForAbstractClass(); - $responseHandlerMock->expects($this->once()) - ->method('handleResponse') - ->with($expectedBody) - ->willReturn(true); - $notFoundResponseHandlerMock = $this->getMockBuilder(ResponseHandlerInterface::class) - ->getMockForAbstractClass(); - $notFoundResponseHandlerMock->expects($this->never())->method('handleResponse'); - $serializerMock = $this->getMockBuilder(Json::class) + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->converterMock = $this->getMockBuilder(ConverterInterface::class) ->disableOriginalConstructor() ->getMock(); - $serializerMock->expects($this->once()) - ->method('unserialize') - ->willReturn($expectedBody); - $objectManager = new ObjectManager($this); - $responseResolver = $objectManager->getObject( + $this->successResponseHandlerMock = $this->getMockBuilder(ResponseHandlerInterface::class) + ->getMockForAbstractClass(); + $this->notFoundResponseHandlerMock = $this->getMockBuilder(ResponseHandlerInterface::class) + ->getMockForAbstractClass(); + $this->responseResolver = $this->objectManagerHelper->getObject( ResponseResolver::class, [ - 'converter' => $objectManager->getObject( - JsonConverter::class, - ['serializer' => $serializerMock] - ), + 'converter' => $this->converterMock, 'responseHandlers' => [ - 201 => $responseHandlerMock, - 404 => $notFoundResponseHandlerMock, + 201 => $this->successResponseHandlerMock, + 404 => $this->notFoundResponseHandlerMock, ] ] ); - $this->assertTrue($responseResolver->getResult($response)); + } + + /** + * @return void + * @throws \Zend_Http_Exception + */ + public function testGetResultHandleResponseSuccess() + { + $expectedBody = ['test' => 'testValue']; + $response = new \Zend_Http_Response(201, ['Content-Type' => 'application/json'], json_encode($expectedBody)); + $this->converterMock + ->method('getContentTypeHeader') + ->willReturn('Content-Type: application/json'); + + $this->successResponseHandlerMock + ->expects($this->once()) + ->method('handleResponse') + ->with($expectedBody) + ->willReturn(true); + $this->notFoundResponseHandlerMock + ->expects($this->never()) + ->method('handleResponse'); + $this->converterMock + ->method('fromBody') + ->willReturn($expectedBody); + $this->assertTrue($this->responseResolver->getResult($response)); + } + + /** + * @return void + * @throws \Zend_Http_Exception + */ + public function testGetResultHandleResponseUnexpectedContentType() + { + $expectedBody = 'testString'; + $response = new \Zend_Http_Response(201, ['Content-Type' => 'plain/text'], $expectedBody); + $this->converterMock + ->method('getContentTypeHeader') + ->willReturn('Content-Type: application/json'); + $this->converterMock + ->expects($this->never()) + ->method('fromBody'); + $this->successResponseHandlerMock + ->expects($this->once()) + ->method('handleResponse') + ->with([]) + ->willReturn(false); + $this->notFoundResponseHandlerMock + ->expects($this->never()) + ->method('handleResponse'); + $this->assertFalse($this->responseResolver->getResult($response)); } } From b49b5685b62b8847c691d6e47619f86e4e4afbbd Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Thu, 2 Aug 2018 18:27:37 -0500 Subject: [PATCH 0711/1171] MC-3308: Automate MFTF for MC-1416 --- .../Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml index 2f5a20b887369..6c4a42d64404e 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml @@ -8,8 +8,6 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> <actionGroup name="VerifyTinyMCEActionGroup"> - <!--<waitForElementVisible selector="{{TinyMCESection.TinyMCE4}}" stepKey="waitForTinyMCE" time="30" />--> - <!--<seeElement selector="{{TinyMCESection.TinyMCE4}}" stepKey="seeTinyMCE4" />--> <seeElement selector="{{TinyMCESection.Style}}" stepKey="assertInfo2"/> <seeElement selector="{{TinyMCESection.Bold}}" stepKey="assertInfo3"/> <seeElement selector="{{TinyMCESection.Italic}}" stepKey="assertInfo4"/> From 59e9aa84d02a47a768fd5bbd4ae1f8cc40041093 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Thu, 2 Aug 2018 19:35:41 -0500 Subject: [PATCH 0712/1171] MC-3308: Automate MFTF for MC-1416 --- ...URLForMediaContentInWYSIWYGActionGroup.xml | 20 +++++++++++++++++++ .../Test/Mftf/Section/GeneralSection.xml | 1 + 2 files changed, 21 insertions(+) create mode 100644 app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml new file mode 100644 index 0000000000000..c2bbbc365dfaf --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="useStaticURLForMediaContentInWYSIWYGActionGroup"> + <amOnPage url="{{ConfigurationStoresPage.url}}" stepKey="navigateToWYSIWYGConfigPage1"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick stepKey="expandWYSIWYGOptions" selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.CheckIfTabExpand}}" visible="true" /> + <waitForElementVisible selector="{{ContentManagementSection.EnableWYSIWYG}}" stepKey="waitForEnableWYSIWYGDropdown1" /> + <selectOption selector="{{ContentManagementSection.StaticURL}}" userInput="Yes" stepKey="selectOption1"/> + <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions" /> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml index b1454ff07ee9e..18e91dc017cfc 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml @@ -15,6 +15,7 @@ <element name="EnableWYSIWYG" type="button" selector="#cms_wysiwyg_enabled"/> <element name="SwitcherSystemValue" type="button" selector="#cms_wysiwyg_editor_inherit"/> <element name="Switcher" type="button" selector="#cms_wysiwyg_editor" /> + <element name="StaticURL" type="button" selector="#cms_wysiwyg_use_static_urls_in_catalog" /> <element name="Save" type="button" selector="#save"/> </section> <section name="WebSection"> From 6c88c5891867b1bb8f4bfb983d00ca50d41182a6 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Fri, 3 Aug 2018 09:37:25 +0530 Subject: [PATCH 0713/1171] Removed commented code --- .../adminhtml/templates/instance/edit/layout.phtml | 5 ----- .../GiftMessage/Api/GuestItemRepositoryTest.php | 1 - .../Magento/GiftMessage/Api/ItemRepositoryTest.php | 2 -- .../Magento/Sales/Service/V1/CreditmemoCreateTest.php | 1 - .../testsuite/Magento/Wishlist/_files/wishlist.php | 10 ---------- .../ObjectManager/Code/Generator/Repository.php | 3 --- 6 files changed, 22 deletions(-) diff --git a/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml b/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml index a0a0fc040a262..3441cf6b5d52a 100644 --- a/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml +++ b/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml @@ -304,11 +304,6 @@ var WidgetInstance = { }, displayPageGroup : function(container, additional) { container = $(container); - if (!container) { -// if (activePageGroupId = this.activePageGroups.get(container.up('div.page_group_container').id)) { -// this.hideBlockContainer(activePageGroupId); -// } - } if (!additional) { additional = {}; } diff --git a/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/GuestItemRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/GuestItemRepositoryTest.php index 8c30ef875c288..b7db294eedfbe 100644 --- a/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/GuestItemRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/GuestItemRepositoryTest.php @@ -117,7 +117,6 @@ public function testSave() ], ]; $this->assertTrue($this->_webApiCall($serviceInfo, $requestData)); -// $quote->load('test_order_item_with_message', 'reserved_order_id'); $messageId = $quote->getItemByProduct($product)->getGiftMessageId(); /** @var \Magento\GiftMessage\Model\Message $message */ $message = $this->objectManager->create(\Magento\GiftMessage\Model\Message::class)->load($messageId); diff --git a/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/ItemRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/ItemRepositoryTest.php index 4b821b118f999..d4df57fbff89e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/ItemRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/ItemRepositoryTest.php @@ -144,7 +144,6 @@ public function testSave() ], ]; $this->assertTrue($this->_webApiCall($serviceInfo, $requestData)); -// $quote->load('test_order_item_with_message', 'reserved_order_id'); $messageId = $quote->getItemByProduct($product)->getGiftMessageId(); /** @var \Magento\GiftMessage\Model\Message $message */ $message = $this->objectManager->create(\Magento\GiftMessage\Model\Message::class)->load($messageId); @@ -193,7 +192,6 @@ public function testSaveForMyCart() ], ]; $this->assertTrue($this->_webApiCall($serviceInfo, $requestData)); -// $quote->load('test_order_item_with_message', 'reserved_order_id'); $messageId = $quote->getItemByProduct($product)->getGiftMessageId(); /** @var \Magento\GiftMessage\Model\Message $message */ $message = $this->objectManager->create(\Magento\GiftMessage\Model\Message::class)->load($messageId); diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/CreditmemoCreateTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/CreditmemoCreateTest.php index aef162d92b1c1..45bae261bb617 100644 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/CreditmemoCreateTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/CreditmemoCreateTest.php @@ -38,7 +38,6 @@ public function testInvoke() $orderCollection = $this->objectManager->get(\Magento\Sales\Model\ResourceModel\Order\Collection::class); $order = $orderCollection->getFirstItem(); -// $order = $this->objectManager->create('Magento\Sales\Model\Order')->loadByIncrementId('100000001'); /** @var \Magento\Sales\Model\Order\Item $orderItem */ $orderItem = current($order->getAllItems()); $items = [ diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist.php index 4c183ad6368cc..73ced4d5462cc 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist.php @@ -12,14 +12,4 @@ ); $wishlist->loadByCustomerId($customer->getId(), true); $item = $wishlist->addNewItem($product, new \Magento\Framework\DataObject([])); -// 'product' => '1', -// 'related_product' => '', -// 'options' => array( -// 1 => '1-text', -// 2 => array('month' => 1, 'day' => 1, 'year' => 2001, 'hour' => 1, 'minute' => 1), -// 3 => '1', -// 4 => '1', -// ), -// 'validate_datetime_2' => '', -// 'qty' => '1', $wishlist->setSharingCode('fixture_unique_code')->save(); diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php index 362cbb2e887c7..be484f074342d 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php +++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php @@ -156,9 +156,6 @@ protected function _getCollectionFactoryClassName() protected function _getPersistorClassName() { $target = $this->getSourceClassName(); -// if (substr($target, -9) == 'Interface') { -// $target = substr($target, 1, strlen($target) -9); -// } return $target . 'Persistor'; } From 01c3c71933a318f2e572961fdfb3d1fc2b1a5dd9 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Fri, 3 Aug 2018 09:52:00 +0530 Subject: [PATCH 0714/1171] Removed commented code from lib files --- .../Magento/Framework/Code/Generator/EntityAbstract.php | 1 - .../Magento/Framework/ObjectManager/Code/Generator/Factory.php | 2 -- .../Magento/Framework/ObjectManager/Code/Generator/Proxy.php | 2 -- 3 files changed, 5 deletions(-) diff --git a/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php b/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php index 3efe110ccf193..b1a9b768f8c43 100644 --- a/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php +++ b/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php @@ -183,7 +183,6 @@ protected function _getDefaultResultClassName($modelClassName) */ protected function _getClassProperties() { - // protected $_objectManager = null; $objectManager = [ 'name' => '_objectManager', 'visibility' => 'protected', diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Factory.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Factory.php index 6186bffd4ca68..07987cd113c5c 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Factory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Factory.php @@ -21,7 +21,6 @@ protected function _getClassProperties() { $properties = parent::_getClassProperties(); - // protected $_instanceName = null; $properties[] = [ 'name' => '_instanceName', 'visibility' => 'protected', @@ -69,7 +68,6 @@ protected function _getClassMethods() { $construct = $this->_getDefaultConstructorDefinition(); - // public function create(array $data = array()) $create = [ 'name' => 'create', 'parameters' => [['name' => 'data', 'type' => 'array', 'defaultValue' => []]], diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php index 96595bc7a073b..7c12b4c82f12e 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php +++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php @@ -37,7 +37,6 @@ protected function _getClassProperties() { $properties = parent::_getClassProperties(); - // protected $_instanceName = null; $properties[] = [ 'name' => '_instanceName', 'visibility' => 'protected', @@ -56,7 +55,6 @@ protected function _getClassProperties() ], ]; - // protected $_shared = null; $properties[] = [ 'name' => '_isShared', 'visibility' => 'protected', From f9be0622781af767396836abe71714c756724a1c Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Fri, 3 Aug 2018 10:09:35 +0300 Subject: [PATCH 0715/1171] MAGETWO-91337: Admin global search preview works inconsistently. - Added fulltext search by title in the CMS pages grid --- .../Cms/Ui/Component/AddFilterInterface.php | 26 ++++++++ .../Magento/Cms/Ui/Component/DataProvider.php | 24 ++++++- .../Cms/Ui/Component/Page/FulltextFilter.php | 44 +++++++++++++ app/code/Magento/Cms/etc/di.xml | 8 +++ .../Adminhtml/FulltextGridSearchTest.php | 66 +++++++++++++++++++ .../Magento/Cms/Fixtures/page_list.php | 34 ++++++++++ .../Cms/Fixtures/page_list_rollback.php | 25 +++++++ 7 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Cms/Ui/Component/AddFilterInterface.php create mode 100644 app/code/Magento/Cms/Ui/Component/Page/FulltextFilter.php create mode 100644 dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/FulltextGridSearchTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list.php create mode 100644 dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list_rollback.php diff --git a/app/code/Magento/Cms/Ui/Component/AddFilterInterface.php b/app/code/Magento/Cms/Ui/Component/AddFilterInterface.php new file mode 100644 index 0000000000000..406b40fbc1647 --- /dev/null +++ b/app/code/Magento/Cms/Ui/Component/AddFilterInterface.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Cms\Ui\Component; + +use Magento\Framework\Api\Filter; +use Magento\Framework\Api\Search\SearchCriteriaBuilder; + +/** + * Provides extension point to add additional filters to search criteria. + */ +interface AddFilterInterface +{ + /** + * Adds custom filter to search criteria builder based on received filter. + * + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param Filter $filter + * @return void + */ + public function addFilter(SearchCriteriaBuilder $searchCriteriaBuilder, Filter $filter); +} diff --git a/app/code/Magento/Cms/Ui/Component/DataProvider.php b/app/code/Magento/Cms/Ui/Component/DataProvider.php index 3298d66b0b877..5fc9c5a896037 100644 --- a/app/code/Magento/Cms/Ui/Component/DataProvider.php +++ b/app/code/Magento/Cms/Ui/Component/DataProvider.php @@ -5,6 +5,7 @@ */ namespace Magento\Cms\Ui\Component; +use Magento\Framework\Api\Filter; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\Search\SearchCriteriaBuilder; use Magento\Framework\App\ObjectManager; @@ -19,6 +20,11 @@ class DataProvider extends \Magento\Framework\View\Element\UiComponent\DataProvi */ private $authorization; + /** + * @var AddFilterInterface[] + */ + private $additionalFilterPool; + /** * @param string $name * @param string $primaryFieldName @@ -29,6 +35,8 @@ class DataProvider extends \Magento\Framework\View\Element\UiComponent\DataProvi * @param FilterBuilder $filterBuilder * @param array $meta * @param array $data + * @param array $additionalFilterPool + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( $name, @@ -39,7 +47,8 @@ public function __construct( RequestInterface $request, FilterBuilder $filterBuilder, array $meta = [], - array $data = [] + array $data = [], + array $additionalFilterPool = [] ) { parent::__construct( $name, @@ -54,6 +63,7 @@ public function __construct( ); $this->meta = array_replace_recursive($meta, $this->prepareMetadata()); + $this->additionalFilterPool = $additionalFilterPool; } /** @@ -95,4 +105,16 @@ public function prepareMetadata() return $metadata; } + + /** + * @inheritdoc + */ + public function addFilter(Filter $filter) + { + if (!empty($this->additionalFilterPool[$filter->getField()])) { + $this->additionalFilterPool[$filter->getField()]->addFilter($this->searchCriteriaBuilder, $filter); + } else { + parent::addFilter($filter); + } + } } diff --git a/app/code/Magento/Cms/Ui/Component/Page/FulltextFilter.php b/app/code/Magento/Cms/Ui/Component/Page/FulltextFilter.php new file mode 100644 index 0000000000000..9b0c69a4f10c4 --- /dev/null +++ b/app/code/Magento/Cms/Ui/Component/Page/FulltextFilter.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Cms\Ui\Component\Page; + +use Magento\Cms\Ui\Component\AddFilterInterface; +use Magento\Framework\Api\Filter; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\Search\SearchCriteriaBuilder; + +/** + * Adds fulltext filter for CMS Page title attribute. + */ +class FulltextFilter implements AddFilterInterface +{ + /** + * @var FilterBuilder + */ + private $filterBuilder; + + /** + * @param FilterBuilder $filterBuilder + */ + public function __construct(FilterBuilder $filterBuilder) + { + $this->filterBuilder = $filterBuilder; + } + + /** + * @inheritdoc + */ + public function addFilter(SearchCriteriaBuilder $searchCriteriaBuilder, Filter $filter) + { + $titleFilter = $this->filterBuilder->setField('title') + ->setValue(sprintf('%%%s%%', $filter->getValue())) + ->setConditionType('like') + ->create(); + $searchCriteriaBuilder->addFilter($titleFilter); + } +} diff --git a/app/code/Magento/Cms/etc/di.xml b/app/code/Magento/Cms/etc/di.xml index 978d75c9b1e56..b6e13c63302cd 100644 --- a/app/code/Magento/Cms/etc/di.xml +++ b/app/code/Magento/Cms/etc/di.xml @@ -225,5 +225,13 @@ <argument name="collectionProcessor" xsi:type="object">Magento\Cms\Model\Api\SearchCriteria\BlockCollectionProcessor</argument> </arguments> </type> + + <type name="Magento\Cms\Ui\Component\DataProvider"> + <arguments> + <argument name="additionalFilterPool" xsi:type="array"> + <item name="fulltext" xsi:type="object">Magento\Cms\Ui\Component\Page\FulltextFilter</item> + </argument> + </arguments> + </type> </config> diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/FulltextGridSearchTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/FulltextGridSearchTest.php new file mode 100644 index 0000000000000..c740609773b90 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/FulltextGridSearchTest.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Cms\Controller\Adminhtml; + +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * @magentoDataFixture Magento/Cms/Fixtures/page_list.php + */ +class FulltextGridSearchTest extends AbstractBackendController +{ + /** + * Checks a fulltext grid search by CMS page title. + * + * @param string $query + * @param int $expectedRows + * @param array $expectedTitles + * @dataProvider queryDataProvider + */ + public function testSearchByTitle(string $query, int $expectedRows, array $expectedTitles) + { + $url = 'backend/mui/index/render/?namespace=cms_page_listing&search=' . $query; + + $this->getRequest() + ->getHeaders() + ->addHeaderLine('Accept', 'application/json'); + $this->dispatch($url); + $response = $this->getResponse(); + $data = json_decode($response->getBody(), true); + self::assertEquals($expectedRows, $data['totalRecords']); + + $titleList = array_column($data['items'], 'title'); + self::assertEquals($expectedTitles, $titleList); + } + + /** + * Gets list of variations with different search queries. + * + * @return array + */ + public function queryDataProvider(): array + { + return [ + [ + 'query' => 'simple', + 'expectedRows' => 3, + 'expectedTitles' => ['simplePage', 'simplePage01', '01simplePage'] + ], + [ + 'query' => 'page01', + 'expectedRows' => 1, + 'expectedTitles' => ['simplePage01'] + ], + [ + 'query' => '01simple', + 'expectedRows' => 1, + 'expectedTitles' => ['01simplePage'] + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list.php b/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list.php new file mode 100644 index 0000000000000..ae431f5c4cf1a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +$data = [ + [ + 'title' => 'simplePage', + 'is_active' => 1 + ], + [ + 'title' => 'simplePage01', + 'is_active' => 1 + ], + [ + 'title' => '01simplePage', + 'is_active' => 1 + ], +]; + +/** @var PageRepositoryInterface $pageRepository */ +$pageRepository = $objectManager->get(PageRepositoryInterface::class); +foreach ($data as $item) { + $page = $objectManager->create(PageInterface::class, ['data' => $item]); + $pageRepository->save($page); +} diff --git a/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list_rollback.php b/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list_rollback.php new file mode 100644 index 0000000000000..261cdba589653 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list_rollback.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var PageRepositoryInterface $pageRepository */ +$pageRepository = $objectManager->get(PageRepositoryInterface::class); + +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); +$searchCriteria = $searchCriteriaBuilder->addFilter('title', ['simplePage', 'simplePage01', '01simplePage'], 'in') + ->create(); +$result = $pageRepository->getList($searchCriteria); + +foreach ($result->getItems() as $item) { + $pageRepository->delete($item); +} From 92622905ba9de5b8aedc727904ead4c1177e81e1 Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Fri, 3 Aug 2018 10:12:38 +0300 Subject: [PATCH 0716/1171] MAGETWO-92951: [2.3][B2B] Unable to add to cart grouped product by SKU with enabled Shared Catalog - Added quote ID to request params --- .../Block/Adminhtml/Order/Create/Form.php | 1 + .../Block/Adminhtml/Order/Create/FormTest.php | 197 ++++++++++++ .../adminhtml/web/order/create/scripts.js | 1 + .../Block/Adminhtml/Order/Create/FormTest.php | 282 +++++++++++------- 4 files changed, 365 insertions(+), 116 deletions(-) create mode 100644 app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/FormTest.php diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form.php index 37e8d8e784744..12a8270acfff0 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form.php @@ -189,6 +189,7 @@ public function getOrderDataJson() $data['shipping_method_reseted'] = !(bool)$this->getQuote()->getShippingAddress()->getShippingMethod(); $data['payment_method'] = $this->getQuote()->getPayment()->getMethod(); } + $data['quote_id'] = $this->_sessionQuote->getQuoteId(); return $this->_jsonEncoder->encode($data); } diff --git a/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/FormTest.php b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/FormTest.php new file mode 100644 index 0000000000000..4cbd30b1392fa --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/FormTest.php @@ -0,0 +1,197 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Test\Unit\Block\Adminhtml\Order\Create; + +use Magento\Backend\Block\Template\Context; +use Magento\Backend\Model\Session\Quote as QuoteSession; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Address\Mapper; +use Magento\Customer\Model\Metadata\FormFactory; +use Magento\Framework\Currency; +use Magento\Framework\Json\EncoderInterface; +use Magento\Framework\Locale\CurrencyInterface; +use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\Quote\Payment; +use Magento\Sales\Block\Adminhtml\Order\Create\Form; +use Magento\Sales\Model\AdminOrder\Create; +use Magento\Store\Model\Store; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class FormTest extends TestCase +{ + /** + * @var QuoteSession|MockObject + */ + private $quoteSession; + + /** + * @var CustomerRepositoryInterface|MockObject + */ + private $customerRepository; + + /** + * @var CurrencyInterface|MockObject + */ + private $localeCurrency; + + /** + * @var Form + */ + private $block; + + /** + * @inheritdoc + */ + protected function setUp() + { + /** @var Context|MockObject $context */ + $context = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + $this->quoteSession = $this->getMockBuilder(QuoteSession::class) + ->disableOriginalConstructor() + ->setMethods(['getCustomerId', 'getQuoteId', 'getStoreId', 'getStore', 'getQuote']) + ->getMock(); + /** @var Create|MockObject $create */ + $create = $this->getMockBuilder(Create::class) + ->disableOriginalConstructor() + ->getMock(); + /** @var PriceCurrencyInterface|MockObject $priceCurrency */ + $priceCurrency = $this->getMockForAbstractClass(PriceCurrencyInterface::class); + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockForAbstractClass(EncoderInterface::class); + $encoder->method('encode') + ->willReturnCallback(function ($param) { + return json_encode($param); + }); + /** @var FormFactory|MockObject $formFactory */ + $formFactory = $this->getMockBuilder(FormFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->customerRepository = $this->getMockForAbstractClass(CustomerRepositoryInterface::class); + + $this->localeCurrency = $this->getMockForAbstractClass(CurrencyInterface::class); + /** @var Mapper|MockObject $addressMapper */ + $addressMapper = $this->getMockBuilder(Mapper::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->block = new Form( + $context, + $this->quoteSession, + $create, + $priceCurrency, + $encoder, + $formFactory, + $this->customerRepository, + $this->localeCurrency, + $addressMapper + ); + } + + /** + * Checks if order contains all needed data. + */ + public function testGetOrderDataJson() + { + $customerId = 1; + $storeId = 1; + $quoteId = 2; + $expected = [ + 'customer_id' => $customerId, + 'addresses' => [], + 'store_id' => $storeId, + 'currency_symbol' => '$', + 'shipping_method_reseted' => false, + 'payment_method' => 'free', + 'quote_id' => $quoteId + ]; + + $this->quoteSession->method('getCustomerId') + ->willReturn($customerId); + $this->quoteSession->method('getStoreId') + ->willReturn($storeId); + $this->quoteSession->method('getQuoteId') + ->willReturn($quoteId); + + $customer = $this->getMockBuilder(CustomerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $customer->method('getAddresses') + ->willReturn([]); + $this->customerRepository->method('getById') + ->with($customerId) + ->willReturn($customer); + + $this->withCurrencySymbol('$'); + + $this->withQuote(); + + self::assertEquals($expected, json_decode($this->block->getOrderDataJson(), true)); + } + + /** + * Configures mock object for currency. + * + * @param string $symbol + */ + private function withCurrencySymbol(string $symbol) + { + $store = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->getMock(); + $store->method('getCurrentCurrencyCode') + ->willReturn('USD'); + $this->quoteSession->method('getStore') + ->willReturn($store); + + $currency = $this->getMockBuilder(Currency::class) + ->disableOriginalConstructor() + ->getMock(); + $currency->method('getSymbol') + ->willReturn($symbol); + $this->localeCurrency->method('getCurrency') + ->with('USD') + ->willReturn($currency); + } + + /** + * Configures shipping and payment mock objects. + */ + private function withQuote() + { + $quote = $this->getMockBuilder(Quote::class) + ->disableOriginalConstructor() + ->getMock(); + $this->quoteSession->method('getQuote') + ->willReturn($quote); + + $address = $this->getMockBuilder(Address::class) + ->disableOriginalConstructor() + ->getMock(); + $address->method('getShippingMethod') + ->willReturn('free'); + $quote->method('getShippingAddress') + ->willReturn($address); + + $payment = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->getMock(); + $payment->method('getMethod') + ->willReturn('free'); + $quote->method('getPayment') + ->willReturn($payment); + } +} diff --git a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js index 62bd9e27c832d..fbf5f5c1023e3 100644 --- a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js +++ b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js @@ -21,6 +21,7 @@ define([ this.loadBaseUrl = false; this.customerId = data.customer_id ? data.customer_id : false; this.storeId = data.store_id ? data.store_id : false; + this.quoteId = data['quote_id'] ? data['quote_id'] : false; this.currencyId = false; this.currencySymbol = data.currency_symbol ? data.currency_symbol : ''; this.addresses = data.addresses ? data.addresses : $H({}); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/FormTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/FormTest.php index b6df7eb05c03d..abdbab2c24d16 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/FormTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/FormTest.php @@ -1,151 +1,201 @@ <?php /** - * Test class for \Magento\Sales\Block\Adminhtml\Order\Create\Form + * Test class for Form * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Sales\Block\Adminhtml\Order\Create; +use Magento\Backend\Model\Session\Quote as QuoteSession; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterfaceFactory; +use Magento\Customer\Api\Data\RegionInterfaceFactory; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + /** * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FormTest extends \PHPUnit\Framework\TestCase { - /** @var \Magento\Sales\Block\Adminhtml\Order\Create\Form */ - protected $_orderCreateBlock; + /** + * @var Form + */ + private $block; - /** @var \Magento\Framework\ObjectManagerInterface */ - protected $_objectManager; + /** + * @var ObjectManagerInterface + */ + private $objectManager; /** - * @magentoDataFixture Magento/Sales/_files/quote.php + * @var QuoteSession|MockObject */ + private $session; + protected function setUp() { - $this->_objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - $sessionMock = $this->getMockBuilder( - \Magento\Backend\Model\Session\Quote::class - )->disableOriginalConstructor()->setMethods( - ['getCustomerId', 'getQuote', 'getStoreId', 'getStore'] - )->getMock(); - $sessionMock->expects($this->any())->method('getCustomerId')->will($this->returnValue(1)); - - $quote = $this->_objectManager->create(\Magento\Quote\Model\Quote::class)->load(1); - $sessionMock->expects($this->any())->method('getQuote')->will($this->returnValue($quote)); - - $sessionMock->expects($this->any())->method('getStoreId')->will($this->returnValue(1)); - - $storeMock = $this->getMockBuilder( - \Magento\Store\Model\Store::class - )->disableOriginalConstructor()->setMethods( - ['getCurrentCurrencyCode'] - )->getMock(); - $storeMock->expects($this->any())->method('getCurrentCurrencyCode')->will($this->returnValue('USD')); - $sessionMock->expects($this->any())->method('getStore')->will($this->returnValue($storeMock)); - - /** @var \Magento\Framework\View\LayoutInterface $layout */ - $layout = $this->_objectManager->get(\Magento\Framework\View\LayoutInterface::class); - $this->_orderCreateBlock = $layout->createBlock( - \Magento\Sales\Block\Adminhtml\Order\Create\Form::class, - 'order_create_block' . rand(), - ['sessionQuote' => $sessionMock] + $this->objectManager = Bootstrap::getObjectManager(); + + $this->session = $this->getMockBuilder(QuoteSession::class) + ->disableOriginalConstructor() + ->setMethods(['getCustomerId', 'getQuote', 'getStoreId', 'getStore', 'getQuoteId']) + ->getMock(); + $this->session->method('getCustomerId') + ->willReturn(1); + + $this->session->method('getStoreId') + ->willReturn(1); + + $store = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->setMethods(['getCurrentCurrencyCode']) + ->getMock(); + $store->method('getCurrentCurrencyCode') + ->willReturn('USD'); + $this->session->method('getStore') + ->willReturn($store); + + /** @var LayoutInterface $layout */ + $layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $layout->createBlock( + Form::class, + 'order_create_block' . mt_rand(), + ['sessionQuote' => $this->session] ); parent::setUp(); } /** + * Checks if all needed order's data is correctly returned to the form. + * * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Sales/_files/quote.php */ public function testOrderDataJson() { - /** @var array $addressIds */ - $addressIds = $this->setUpMockAddress(); - $orderDataJson = $this->_orderCreateBlock->getOrderDataJson(); - $expectedOrderDataJson = <<<ORDER_DATA_JSON - { - "customer_id":1, - "addresses": - {"{$addressIds[0]}": - {"firstname":"John","lastname":"Smith","company":false,"street":"Green str, 67","city":"CityM", - "country_id":"US", - "region":"Alabama","region_id":1, - "postcode":"75477","telephone":"3468676","vat_id":false}, - "{$addressIds[1]}": - {"firstname":"John","lastname":"Smith","company":false,"street":"Black str, 48","city":"CityX", - "country_id":"US", - "region":"Alabama","region_id":1, - "postcode":"47676","telephone":"3234676","vat_id":false} - }, - "store_id":1,"currency_symbol":"$","shipping_method_reseted":true,"payment_method":null - } -ORDER_DATA_JSON; - - $this->assertEquals(json_decode($expectedOrderDataJson), json_decode($orderDataJson)); + $customerId = 1; + $quote = $this->getQuote('test01'); + $this->session->method('getQuote') + ->willReturn($quote); + $this->session->method('getQuoteId') + ->willReturn($quote->getId()); + $addressData = $this->getAddressData(); + $addressIds = $this->setUpMockAddress($customerId, $addressData); + $expected = [ + 'customer_id' => $customerId, + 'addresses' => [ + $addressIds[0] => $addressData[0], + $addressIds[1] => $addressData[1] + ], + 'store_id' => 1, + 'currency_symbol' => '$', + 'shipping_method_reseted' => true, + 'payment_method' => 'checkmo', + 'quote_id' => $quote->getId() + ]; + + self::assertEquals($expected, json_decode($this->block->getOrderDataJson(), true)); } - private function setUpMockAddress() + /** + * Saves customer's addresses. + * + * @param int $customerId + * @param array $addressData + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function setUpMockAddress(int $customerId, array $addressData) { - /** @var \Magento\Customer\Api\Data\RegionInterfaceFactory $regionFactory */ - $regionFactory = $this->_objectManager->create(\Magento\Customer\Api\Data\RegionInterfaceFactory::class); - - /** @var \Magento\Customer\Api\Data\AddressInterfaceFactory $addressFactory */ - $addressFactory = $this->_objectManager->create(\Magento\Customer\Api\Data\AddressInterfaceFactory::class); - /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ - $addressRepository = $this->_objectManager->create(\Magento\Customer\Api\AddressRepositoryInterface::class); - - $addressData1 = $addressFactory->create()->setCountryId( - 'US' - )->setCustomerId( - 1 - )->setIsDefaultBilling( - true - )->setIsDefaultShipping( - true - )->setPostcode( - '75477' - )->setRegion( - $regionFactory->create()->setRegionCode('AL')->setRegion('Alabama')->setRegionId(1) - )->setStreet( - ['Green str, 67'] - )->setTelephone( - '3468676' - )->setCity( - 'CityM' - )->setFirstname( - 'John' - )->setLastname( - 'Smith' - ); - - $addressData2 = $addressFactory->create()->setCountryId( - 'US' - )->setCustomerId( - 1 - )->setIsDefaultBilling( - false - )->setIsDefaultShipping( - false - )->setPostcode( - '47676' - )->setRegion( - $regionFactory->create()->setRegionCode('AL')->setRegion('Alabama')->setRegionId(1) - )->setStreet( - ['Black str, 48'] - )->setCity( - 'CityX' - )->setTelephone( - '3234676' - )->setFirstname( - 'John' - )->setLastname( - 'Smith' - ); + /** @var RegionInterfaceFactory $regionFactory */ + $regionFactory = $this->objectManager->create(RegionInterfaceFactory::class); + /** @var AddressInterfaceFactory $addressFactory */ + $addressFactory = $this->objectManager->create(AddressInterfaceFactory::class); + /** @var AddressRepositoryInterface $addressRepository */ + $addressRepository = $this->objectManager->create(AddressRepositoryInterface::class); + $region = $regionFactory->create() + ->setRegionCode('AL') + ->setRegion('Alabama') + ->setRegionId(1); + + $ids = []; + foreach ($addressData as $data) { + $address = $addressFactory->create(['data' => $data]); + $address->setRegion($region) + ->setCustomerId($customerId) + ->setIsDefaultBilling(true) + ->setIsDefaultShipping(true); + $address = $addressRepository->save($address); + $ids[] = $address->getId(); + } + + return $ids; + } - $savedAddress1 = $addressRepository->save($addressData1); - $savedAddress2 = $addressRepository->save($addressData2); + /** + * Gets test address data. + * + * @return array + */ + private function getAddressData(): array + { + return [ + [ + 'firstname' => 'John', + 'lastname' => 'Smith', + 'company' => false, + 'street' => 'Green str, 67', + 'city' => 'CityM', + 'country_id' => 'US', + 'region' => 'Alabama', + 'region_id' => 1, + 'postcode' => '75477', + 'telephone' => '3468676', + 'vat_id' => false + ], + [ + 'firstname' => 'John', + 'lastname' => 'Smith', + 'company' => false, + 'street' => 'Black str, 48', + 'city' => 'CityX', + 'country_id' => 'US', + 'region' => 'Alabama', + 'region_id' => 1, + 'postcode' => '47676', + 'telephone' => '3234676', + 'vat_id' => false, + ] + ]; + } - return [$savedAddress1->getId(), $savedAddress2->getId()]; + /** + * Gets quote by ID. + * + * @param string $reservedOrderId + * @return Quote + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getQuote(string $reservedOrderId): Quote + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + /** @var CartRepositoryInterface $repository */ + $repository = $this->objectManager->get(CartRepositoryInterface::class); + $items = $repository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); } } From 5a7a5e1b469fcaa2dd809dd8ba0e1f33d1be0c24 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Mon, 18 Jun 2018 12:24:57 +0300 Subject: [PATCH 0717/1171] MAGETWO-92953: Once Payflow Pro payment is Declined, customer cannot continue checkout --- .../view/frontend/templates/transparent/iframe.phtml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml b/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml index f1988f1ca86bb..c127371cb5f47 100644 --- a/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml +++ b/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml @@ -67,9 +67,10 @@ $params = $block->getParams(); 'jquery', 'Magento_Checkout/js/model/quote', 'Magento_Checkout/js/action/place-order', - 'Magento_Checkout/js/action/redirect-on-success' + 'Magento_Checkout/js/action/redirect-on-success', + 'Magento_Checkout/js/model/full-screen-loader' ], - function($, quote, placeOrderAction, redirectOnSuccessAction) { + function($, quote, placeOrderAction, redirectOnSuccessAction, fullScreenLoader) { var parent = window.top; $(parent).trigger('clearTimeout'); @@ -79,6 +80,12 @@ $params = $block->getParams(); function () { redirectOnSuccessAction.execute(); } + ).fail( + function () { + var parent = window.top; + $(parent).trigger('clearTimeout'); + fullScreenLoader.stopLoader(); + } ); } ); From f8aedef314c9e0633b4d2b48fa23fc13ef37fc36 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Wed, 1 Aug 2018 11:07:37 +0300 Subject: [PATCH 0718/1171] MAGETWO-91771: Comma special character in cart price rule condition value results in incorrect rule --- .../Product/View/Type/ConfigurableTest.php | 9 +- .../Model/Condition/AbstractCondition.php | 2 +- .../Model/Rule/Condition/Product.php | 29 +++++- .../Unit/Model/Rule/Condition/ProductTest.php | 97 +++++++++++++++++-- .../Magento/Framework/Locale/Format.php | 2 +- .../Framework/Locale/Test/Unit/FormatTest.php | 41 +++++++- 6 files changed, 161 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php index b45306d670bff..e117ab707a473 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\ConfigurableProduct\Test\Unit\Block\Product\View\Type; use Magento\Customer\Model\Session; @@ -174,7 +175,7 @@ protected function setUp() * * @return array */ - public function cacheKeyProvider() : array + public function cacheKeyProvider(): array { return [ 'without_currency_and_customer_group' => [ @@ -313,11 +314,7 @@ public function testGetJsonConfig() $this->localeFormat->expects($this->atLeastOnce())->method('getPriceFormat')->willReturn([]); $this->localeFormat->expects($this->any()) ->method('getNumber') - ->willReturnMap([ - [$amount, $amount], - [$priceQty, $priceQty], - [$percentage, $percentage], - ]); + ->willReturnArgument(0); $this->variationPricesMock->expects($this->once()) ->method('getFormattedPrices') diff --git a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php index e3bec2d9959b1..eac67015b104e 100644 --- a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php +++ b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php @@ -380,7 +380,7 @@ public function isArrayOperatorType() } /** - * @return array + * @return mixed */ public function getValue() { diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php index 499e35db9dfd6..d2e7cabe473f4 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\SalesRule\Model\Rule\Condition; /** @@ -51,10 +52,17 @@ public function validate(\Magento\Framework\Model\AbstractModel $model) $attrCode = $this->getAttribute(); - if ('category_ids' == $attrCode) { + if ($attrCode === 'category_ids') { return $this->validateAttribute($this->_getAvailableInCategories($product->getId())); } + if ($attrCode === 'quote_item_price') { + $numericOperations = $this->getDefaultOperatorInputByType()['numeric']; + if (in_array($this->getOperator(), $numericOperations)) { + $this->setData('value', $this->getFormattedPrice($this->getValue())); + } + } + return parent::validate($product); } @@ -79,4 +87,23 @@ public function getValueElementChooserUrl() } return $url !== false ? $this->_backendData->getUrl($url) : ''; } + + /** + * @param string $value + * @return float|null + */ + private function getFormattedPrice($value) + { + $value = preg_replace('/[^0-9^\^.,-]/m', '', $value); + + /** + * If the comma is the third symbol in the number, we consider it to be a decimal separator + */ + $separatorComa = strpos($value, ','); + $separatorDot = strpos($value, '.'); + if ($separatorComa !== false && $separatorDot === false && preg_match('/,\d{3}$/m', $value) === 1) { + $value .= '.00'; + } + return $this->_localeFormat->getNumber($value); + } } diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php index 0bce282747b16..97c7efab42583 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php @@ -3,11 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\SalesRule\Test\Unit\Model\Rule\Condition; +use Magento\Directory\Model\CurrencyFactory; +use Magento\Framework\App\ScopeResolverInterface; use \Magento\Framework\DB\Adapter\AdapterInterface; use \Magento\Framework\DB\Select; -use \Magento\Framework\Model\AbstractModel; +use Magento\Framework\Locale\Format; +use Magento\Framework\Locale\ResolverInterface; use Magento\Quote\Model\Quote\Item\AbstractItem; use \Magento\Rule\Model\Condition\Context; use \Magento\Backend\Helper\Data; @@ -50,8 +54,8 @@ class ProductTest extends \PHPUnit\Framework\TestCase /** @var Collection|\PHPUnit_Framework_MockObject_MockObject */ protected $collectionMock; - /** @var FormatInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $formatMock; + /** @var FormatInterface */ + protected $format; /** @var AttributeLoaderInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $attributeLoaderInterfaceMock; @@ -130,8 +134,12 @@ protected function setUp() $this->collectionMock = $this->getMockBuilder(Collection::class) ->disableOriginalConstructor() ->getMock(); - $this->formatMock = $this->getMockBuilder(FormatInterface::class) - ->getMockForAbstractClass(); + $this->format = new Format( + $this->getMockBuilder(ScopeResolverInterface::class)->disableOriginalConstructor()->getMock(), + $this->getMockBuilder(ResolverInterface::class)->disableOriginalConstructor()->getMock(), + $this->getMockBuilder(CurrencyFactory::class)->disableOriginalConstructor()->getMock() + ); + $this->model = new SalesRuleProduct( $this->contextMock, $this->backendHelperMock, @@ -140,7 +148,7 @@ protected function setUp() $this->productRepositoryMock, $this->productMock, $this->collectionMock, - $this->formatMock + $this->format ); } @@ -231,4 +239,81 @@ public function testValidateCategoriesIgnoresVisibility() $this->model->validate($item); } + + /** + * @param boolean $isValid + * @param string $conditionValue + * @param string $operator + * @param double $productPrice + * @dataProvider localisationProvider + */ + public function testQuoteLocaleFormatPrice($isValid, $conditionValue, $operator = '>=', $productPrice = 2000.00) + { + $attr = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class) + ->disableOriginalConstructor() + ->setMethods(['getAttribute']) + ->getMockForAbstractClass(); + + $attr->expects($this->any()) + ->method('getAttribute') + ->willReturn(''); + + /* @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $product */ + $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->setMethods(['setQuoteItemPrice', 'getResource', 'hasData', 'getData',]) + ->getMock(); + + $product->expects($this->any()) + ->method('setQuoteItemPrice') + ->willReturnSelf(); + + $product->expects($this->any()) + ->method('getResource') + ->willReturn($attr); + + $product->expects($this->any()) + ->method('hasData') + ->willReturn(true); + + $product->expects($this->any()) + ->method('getData') + ->with('quote_item_price') + ->willReturn($productPrice); + + /* @var AbstractItem|\PHPUnit_Framework_MockObject_MockObject $item */ + $item = $this->getMockBuilder(AbstractItem::class) + ->disableOriginalConstructor() + ->setMethods(['getPrice', 'getProduct',]) + ->getMockForAbstractClass(); + + $item->expects($this->any()) + ->method('getPrice') + ->willReturn($productPrice); + + $item->expects($this->any()) + ->method('getProduct') + ->willReturn($product); + + $this->model->setAttribute('quote_item_price') + ->setOperator($operator); + + $this->assertEquals($isValid, $this->model->setValue($conditionValue)->validate($item)); + } + + /** + * DataProvider for testQuoteLocaleFormatPrice + * + * @return array + */ + public function localisationProvider(): array + { + return [ + 'number' => [true, 500.01], + 'locale' => [true, '1,500.03'], + 'operation' => [true, '1,500.03', '!='], + 'stringOperation' => [false, '1,500.03', '{}'], + 'smallPrice' => [false, '1,500.03', '>=', 1000], + ]; + } } diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php index 89f6957011876..ca50cdb2440f4 100644 --- a/lib/internal/Magento/Framework/Locale/Format.php +++ b/lib/internal/Magento/Framework/Locale/Format.php @@ -65,7 +65,7 @@ public function getNumber($value) } //trim spaces and apostrophes - $value = str_replace(['\'', ' '], '', $value); + $value = preg_replace('/[^0-9^\^.,-]/m', '', $value); $separatorComa = strpos($value, ','); $separatorDot = strpos($value, '.'); diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php index 41583fd1383a5..418c5e927f5e7 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php @@ -41,9 +41,7 @@ protected function setUp() $this->scope = $this->getMockBuilder(\Magento\Framework\App\ScopeInterface::class) ->setMethods(['getCurrentCurrency']) ->getMockForAbstractClass(); - $this->scope->expects($this->once()) - ->method('getCurrentCurrency') - ->willReturn($this->currency); + $this->scopeResolver = $this->getMockBuilder(\Magento\Framework\App\ScopeResolverInterface::class) ->setMethods(['getScope']) ->getMockForAbstractClass(); @@ -52,6 +50,8 @@ protected function setUp() ->willReturn($this->scope); $this->localeResolver = $this->getMockBuilder(\Magento\Framework\Locale\ResolverInterface::class) ->getMock(); + + /** @var \Magento\Directory\Model\CurrencyFactory|\PHPUnit_Framework_MockObject_MockObject $currencyFactory */ $currencyFactory = $this->getMockBuilder(\Magento\Directory\Model\CurrencyFactory::class) ->getMock(); @@ -69,6 +69,10 @@ protected function setUp() */ public function testGetPriceFormat($localeCode, $expectedResult) { + $this->scope->expects($this->once()) + ->method('getCurrentCurrency') + ->willReturn($this->currency); + $result = $this->formatModel->getPriceFormat($localeCode); $intersection = array_intersect_assoc($result, $expectedResult); $this->assertCount(count($expectedResult), $intersection); @@ -83,7 +87,36 @@ public function getPriceFormatDataProvider() ['en_US', ['decimalSymbol' => '.', 'groupSymbol' => ',']], ['de_DE', ['decimalSymbol' => ',', 'groupSymbol' => '.']], ['de_CH', ['decimalSymbol' => '.', 'groupSymbol' => '\'']], - ['uk_UA', ['decimalSymbol' => ',', 'groupSymbol' => ' ']] + ['uk_UA', ['decimalSymbol' => ',', 'groupSymbol' => ' ']] + ]; + } + + /** + * @param float | null $expected + * @param string|float|int $value + * @dataProvider provideNumbers + */ + public function testGetNumber($value, $expected) + { + $this->assertEquals($expected, $this->formatModel->getNumber($value)); + } + + /** + * @return array + */ + public function provideNumbers(): array + { + return [ + [' 2345.4356,1234', 23454356.1234], + ['+23,3452.123', 233452.123], + ['12343', 12343], + ['-9456km', -9456], + ['0', 0], + ['2 054,10', 2054.1], + ['2046,45', 2046.45], + ['2 054.52', 2054.52], + ['2,46 GB', 2.46], + ['2,054.00', 2054], ]; } } From 32a31e42340d77680b6c363256d8a9c351c8c17d Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Fri, 3 Aug 2018 10:33:35 +0300 Subject: [PATCH 0719/1171] MAGETWO-93949: [2.3] The error icon does not appear on sections with required attributes that are empty when click on 'save' button --- .../base/web/js/form/components/fieldset.js | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js index b729dd3127d90..6d33386fa1f1c 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js @@ -22,7 +22,7 @@ define([ opened: false, level: 0, visible: true, - initializeFieldsetDataByDefault: false, /* Data in some fieldsets should be initialized before open */ + initializeFieldsetDataByDefault: false, /* Data in some fieldsets should be initialized before open */ disabled: false, listens: { 'opened': 'onVisibilityChange' @@ -77,9 +77,9 @@ define([ elem.initContainer(this); elem.on({ - 'update': this.onChildrenUpdate, - 'loading': this.onContentLoading, - 'error': this.onChildrenError + 'update': this.onChildrenUpdate, + 'loading': this.onContentLoading, + 'error': this.onChildrenError }); if (this.disabled) { @@ -155,11 +155,42 @@ define([ * @param {String} message - error message. */ onChildrenError: function (message) { - var hasErrors = this.elems.some('error'); + var hasErrors = false; + + if (!message) { + hasErrors = this._isChildrenHasErrors(hasErrors, this); + } this.error(hasErrors || message); }, + /** + * Returns errors of children if exist + * + * @param {Boolean} hasErrors + * @param {*} container + * @return {Boolean} + * @private + */ + _isChildrenHasErrors: function (hasErrors, container) { + var self = this; + + if (hasErrors === false && container.hasOwnProperty('elems')) { + hasErrors = container.elems.some('error'); + + if (hasErrors === false && container.hasOwnProperty('_elems')) { + container._elems.forEach(function (child) { + + if (hasErrors === false) { + hasErrors = self._isChildrenHasErrors(hasErrors, child); + } + }); + } + } + + return hasErrors; + }, + /** * Callback that sets loading property to true. */ From 5672712e97d1144a4582886096580d67d2b2617c Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Wed, 20 Jun 2018 15:41:00 +0300 Subject: [PATCH 0720/1171] MAGETWO-92950: [2.3] Extra XHR POST request made on every page --- .../web/js/product/storage/storage-service.js | 2 +- .../product/storage/storage-service.test.js | 77 +++++++++++++++---- 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/storage/storage-service.js b/app/code/Magento/Catalog/view/frontend/web/js/product/storage/storage-service.js index 014002bdc4af9..e571baa23e497 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/storage/storage-service.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/storage/storage-service.js @@ -47,7 +47,7 @@ define([ * @param {*} data */ add: function (data) { - if (!utils.compare(data, this.data()).equal) { + if (!_.isEmpty(data) && !utils.compare(data, this.data()).equal) { this.data(_.extend(utils.copy(this.data()), data)); } }, diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/frontend/js/product/storage/storage-service.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/frontend/js/product/storage/storage-service.test.js index 4ef1aa2a98976..1a3a5726080bc 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/frontend/js/product/storage/storage-service.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/frontend/js/product/storage/storage-service.test.js @@ -12,22 +12,11 @@ define([ var injector = new Squire(), mocks = { - 'Magento_Catalog/js/product/storage/ids-storage': { - name: 'IdsStorage', - initialize: jasmine.createSpy().and.returnValue({}) - }, 'Magento_Catalog/js/product/storage/data-storage': {}, 'Magento_Catalog/js/product/storage/ids-storage-compare': {} }, - obj; - - beforeEach(function (done) { - injector.mock(mocks); - injector.require(['Magento_Catalog/js/product/storage/storage-service'], function (insance) { - obj = insance; - done(); - }); - }); + obj, + utils; afterEach(function () { try { @@ -43,6 +32,19 @@ define([ }, storage; + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Catalog/js/product/storage/ids-storage', + 'Magento_Catalog/js/product/storage/storage-service', + 'mageUtils' + ], function (IdsStorage, instance, mageUtils) { + obj = instance; + utils = mageUtils; + done(); + }); + }); + describe('"createStorage" method', function () { it('create new storage', function () { obj.processSubscribers = jasmine.createSpy(); @@ -73,5 +75,54 @@ define([ expect(typeof obj.getStorage(config.namespace)).toBe('object'); }); }); + describe('"add" method', function () { + var storageValue; + + beforeEach(function () { + storage = new obj.createStorage(config); + storageValue = { + 'property1': 1 + }; + + storage.set(storageValue); + }); + + it('method exists', function () { + expect(storage.add).toBeDefined(); + expect(typeof storage.add).toEqual('function'); + }); + + it('update value', function () { + spyOn(utils, 'copy').and.callThrough(); + expect(storage.get()).toEqual(storageValue); + + storageValue = { + 'property2': 2 + }; + + storage.add(storageValue); + + expect(utils.copy).toHaveBeenCalled(); + expect(storage.get()).toEqual( + { + 'property1': 1, + 'property2': 2 + } + ); + }); + + it('add empty value', function () { + spyOn(utils, 'copy').and.callThrough(); + + storage.add({}); + + expect(utils.copy).not.toHaveBeenCalled(); + expect(storage.get()).toEqual( + { + 'property1': 1 + } + ); + }); + }); }); }); From 846f720851a58175e6c1dee7a3ba3619f9d63a83 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Thu, 2 Aug 2018 11:23:25 +0300 Subject: [PATCH 0721/1171] MAGETWO-91808: Categories of the Main menu in the different Store View not updated when varnish enabled --- .../Plugin/Store/Block/Switcher.php | 86 ------------- .../CatalogUrlRewrite/etc/frontend/di.xml | 12 -- app/code/Magento/Customer/Model/Session.php | 2 +- .../Customer/Test/Unit/Model/SessionTest.php | 13 +- app/code/Magento/PageCache/Model/Config.php | 3 - .../Magento/Store/App/Config/Type/Scopes.php | 1 + .../Magento/Store/App/Response/Redirect.php | 10 -- app/code/Magento/Store/Block/Switcher.php | 26 +++- .../Store/Controller/Store/Redirect.php | 113 +++++++++++++++++ .../Store/Controller/Store/SwitchAction.php | 64 +++++----- .../Store/Model/Plugin/StoreCookie.php | 7 - app/code/Magento/Store/Model/Store.php | 4 +- .../Magento/Store/Model/StoreSwitcher.php | 57 +++++++++ .../CannotSwitchStoreException.php | 26 ++++ .../Model/StoreSwitcher/CleanTargetUrl.php | 65 ++++++++++ .../StoreSwitcher/ManagePrivateContent.php | 65 ++++++++++ .../Model/StoreSwitcher/ManageStoreCookie.php | 71 +++++++++++ .../Store/Model/StoreSwitcherInterface.php | 25 ++++ .../Store/Test/Mftf/Data/StoreData.xml | 6 +- .../Store/Test/Unit/Block/SwitcherTest.php | 21 ++- .../Controller/Store/SwitchActionTest.php | 85 +++++-------- .../Unit/Model/Plugin/StoreCookieTest.php | 41 ------ .../Store/Test/Unit/Model/StoreTest.php | 59 +++++++-- app/code/Magento/Store/etc/di.xml | 10 ++ .../Plugin/Store/Switcher/SetRedirectUrl.php | 89 ------------- .../Magento/UrlRewrite/Controller/Router.php | 39 ------ .../Model/StoreSwitcher/RewriteUrl.php | 81 ++++++++++++ .../Store/Switcher/SetRedirectUrlTest.php | 96 -------------- .../Test/Unit/Controller/RouterTest.php | 72 ++++++++++- app/code/Magento/UrlRewrite/etc/di.xml | 7 + .../Magento/UrlRewrite/etc/frontend/di.xml | 3 - .../Controller/Product/CompareTest.php | 2 +- .../Plugin/Store/Block/SwitcherTest.php | 56 -------- .../two_categories_per_two_store_groups.php | 67 ---------- ...tegories_per_two_store_groups_rollback.php | 47 ------- .../Customer/Controller/AccountTest.php | 10 +- .../Magento/Store/Block/SwitcherTest.php | 27 +++- .../Magento/Store/Model/StoreSwitcherTest.php | 57 +++++++++ .../UrlRewrite/Controller/UrlRewriteTest.php | 6 - .../Model/StoreSwitcher/RewriteUrlTest.php | 120 ++++++++++++++++++ .../_files/url_rewrite_rollback.php | 28 ++++ 41 files changed, 987 insertions(+), 692 deletions(-) delete mode 100644 app/code/Magento/CatalogUrlRewrite/Plugin/Store/Block/Switcher.php delete mode 100644 app/code/Magento/CatalogUrlRewrite/etc/frontend/di.xml create mode 100644 app/code/Magento/Store/Controller/Store/Redirect.php create mode 100644 app/code/Magento/Store/Model/StoreSwitcher.php create mode 100644 app/code/Magento/Store/Model/StoreSwitcher/CannotSwitchStoreException.php create mode 100644 app/code/Magento/Store/Model/StoreSwitcher/CleanTargetUrl.php create mode 100644 app/code/Magento/Store/Model/StoreSwitcher/ManagePrivateContent.php create mode 100644 app/code/Magento/Store/Model/StoreSwitcher/ManageStoreCookie.php create mode 100644 app/code/Magento/Store/Model/StoreSwitcherInterface.php delete mode 100644 app/code/Magento/UrlRewrite/Block/Plugin/Store/Switcher/SetRedirectUrl.php create mode 100644 app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php delete mode 100644 app/code/Magento/UrlRewrite/Test/Unit/Block/Plugin/Store/Switcher/SetRedirectUrlTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Store/Block/SwitcherTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php delete mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Store/Model/StoreSwitcherTest.php create mode 100644 dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php create mode 100644 dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php diff --git a/app/code/Magento/CatalogUrlRewrite/Plugin/Store/Block/Switcher.php b/app/code/Magento/CatalogUrlRewrite/Plugin/Store/Block/Switcher.php deleted file mode 100644 index 44213c007551c..0000000000000 --- a/app/code/Magento/CatalogUrlRewrite/Plugin/Store/Block/Switcher.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogUrlRewrite\Plugin\Store\Block; - -use Magento\Framework\Data\Helper\PostHelper; -use Magento\Store\Api\StoreResolverInterface; -use Magento\Store\Model\Store; -use Magento\UrlRewrite\Model\UrlFinderInterface; -use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; -use Magento\Framework\App\Request\Http as HttpRequest; - -/** - * Plugin makes connection between Store and UrlRewrite modules - * because Magento\Store\Block\Switcher should not know about UrlRewrite module functionality. - */ -class Switcher -{ - /** - * @var PostHelper - */ - private $postHelper; - - /** - * @var UrlFinderInterface - */ - private $urlFinder; - - /** - * @var HttpRequest - */ - private $request; - - /** - * @param PostHelper $postHelper - * @param UrlFinderInterface $urlFinder - * @param HttpRequest $request - */ - public function __construct( - PostHelper $postHelper, - UrlFinderInterface $urlFinder, - HttpRequest $request - ) { - $this->postHelper = $postHelper; - $this->urlFinder = $urlFinder; - $this->request = $request; - } - - /** - * @param \Magento\Store\Block\Switcher $subject - * @param string $result - * @param Store $store - * @param array $data - * @return string - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function afterGetTargetStorePostData( - \Magento\Store\Block\Switcher $subject, - string $result, - Store $store, - array $data = [] - ): string { - $data[StoreResolverInterface::PARAM_NAME] = $store->getCode(); - $currentUrl = $store->getCurrentUrl(true); - $baseUrl = $store->getBaseUrl(); - $urlPath = parse_url($currentUrl, PHP_URL_PATH); - $urlToSwitch = $currentUrl; - - //check only catalog pages - if ($this->request->getFrontName() === 'catalog') { - $currentRewrite = $this->urlFinder->findOneByData([ - UrlRewrite::REQUEST_PATH => ltrim($urlPath, '/'), - UrlRewrite::STORE_ID => $store->getId(), - ]); - if (null === $currentRewrite) { - $urlToSwitch = $baseUrl; - } - } - - return $this->postHelper->getPostData($urlToSwitch, $data); - } -} diff --git a/app/code/Magento/CatalogUrlRewrite/etc/frontend/di.xml b/app/code/Magento/CatalogUrlRewrite/etc/frontend/di.xml deleted file mode 100644 index 3a9122b2f748d..0000000000000 --- a/app/code/Magento/CatalogUrlRewrite/etc/frontend/di.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <type name="\Magento\Store\Block\Switcher"> - <plugin name="store_switcher_plugin" type="Magento\CatalogUrlRewrite\Plugin\Store\Block\Switcher"/> - </type> -</config> diff --git a/app/code/Magento/Customer/Model/Session.php b/app/code/Magento/Customer/Model/Session.php index 680e68b5c4c0f..5900fed218edf 100644 --- a/app/code/Magento/Customer/Model/Session.php +++ b/app/code/Magento/Customer/Model/Session.php @@ -487,7 +487,7 @@ public function authenticate($loginUrl = null) $this->response->setRedirect($loginUrl); } else { $arguments = $this->_customerUrl->getLoginUrlParams(); - if ($this->_session->getCookieShouldBeReceived() && $this->_createUrl()->getUseSession()) { + if ($this->_createUrl()->getUseSession()) { $arguments += [ '_query' => [ $this->sidResolver->getSessionIdQueryParam($this->_session) => $this->_session->getSessionId(), diff --git a/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php b/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php index 7a6807562f906..858b7ec24324a 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php @@ -131,18 +131,21 @@ public function testAuthenticate() $urlMock = $this->createMock(\Magento\Framework\Url::class); $urlMock->expects($this->exactly(2)) ->method('getUrl') - ->will($this->returnValue('')); + ->willReturn(''); $urlMock->expects($this->once()) ->method('getRebuiltUrl') - ->will($this->returnValue('')); - $this->urlFactoryMock->expects($this->exactly(3)) + ->willReturn(''); + $this->urlFactoryMock->expects($this->exactly(4)) ->method('create') - ->will($this->returnValue($urlMock)); + ->willReturn($urlMock); + $urlMock->expects($this->once()) + ->method('getUseSession') + ->willReturn(false); $this->responseMock->expects($this->once()) ->method('setRedirect') ->with('') - ->will($this->returnValue('')); + ->willReturn(''); $this->assertFalse($this->_model->authenticate()); } diff --git a/app/code/Magento/PageCache/Model/Config.php b/app/code/Magento/PageCache/Model/Config.php index 6debbdf3e728a..83db8c0dec3b1 100644 --- a/app/code/Magento/PageCache/Model/Config.php +++ b/app/code/Magento/PageCache/Model/Config.php @@ -113,7 +113,6 @@ public function __construct( * * @return int * @api - * @deprecated 100.2.0 see \Magento\PageCache\Model\VclGeneratorInterface::generateVcl */ public function getType() { @@ -125,7 +124,6 @@ public function getType() * * @return int * @api - * @deprecated 100.2.0 see \Magento\PageCache\Model\VclGeneratorInterface::generateVcl */ public function getTtl() { @@ -255,7 +253,6 @@ protected function _getDesignExceptions() * * @return bool * @api - * @deprecated 100.2.0 see \Magento\PageCache\Model\VclGeneratorInterface::generateVcl */ public function isEnabled() { diff --git a/app/code/Magento/Store/App/Config/Type/Scopes.php b/app/code/Magento/Store/App/Config/Type/Scopes.php index 9fbecc4db303e..e6b9d0000e4a6 100644 --- a/app/code/Magento/Store/App/Config/Type/Scopes.php +++ b/app/code/Magento/Store/App/Config/Type/Scopes.php @@ -114,5 +114,6 @@ private function convertIdPathToCodePath(array $patchChunks) public function clean() { $this->data = null; + $this->idCodeMap = []; } } diff --git a/app/code/Magento/Store/App/Response/Redirect.php b/app/code/Magento/Store/App/Response/Redirect.php index b18f2cfc22613..534c6be41c937 100644 --- a/app/code/Magento/Store/App/Response/Redirect.php +++ b/app/code/Magento/Store/App/Response/Redirect.php @@ -171,16 +171,6 @@ public function success($defaultUrl) */ public function updatePathParams(array $arguments) { - if ($this->_session->getCookieShouldBeReceived() - && $this->_sidResolver->getUseSessionInUrl() - && $this->_canUseSessionIdInParam - ) { - $arguments += [ - '_query' => [ - $this->_sidResolver->getSessionIdQueryParam($this->_session) => $this->_session->getSessionId(), - ] - ]; - } return $arguments; } diff --git a/app/code/Magento/Store/Block/Switcher.php b/app/code/Magento/Store/Block/Switcher.php index b0659b7caf7e8..58a7ba3c1c283 100644 --- a/app/code/Magento/Store/Block/Switcher.php +++ b/app/code/Magento/Store/Block/Switcher.php @@ -13,6 +13,9 @@ use Magento\Store\Api\StoreResolverInterface; use Magento\Store\Model\Group; use Magento\Store\Model\Store; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Url\Helper\Data as UrlHelper; /** * @api @@ -30,20 +33,28 @@ class Switcher extends \Magento\Framework\View\Element\Template */ protected $_postDataHelper; + /** + * @var UrlHelper + */ + private $urlHelper; + /** * Constructs * * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Framework\Data\Helper\PostHelper $postDataHelper * @param array $data + * @param UrlHelper $urlHelper */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Magento\Framework\Data\Helper\PostHelper $postDataHelper, - array $data = [] + array $data = [], + UrlHelper $urlHelper = null ) { $this->_postDataHelper = $postDataHelper; parent::__construct($context, $data); + $this->urlHelper = $urlHelper ?: ObjectManager::getInstance()->get(UrlHelper::class); } /** @@ -222,15 +233,20 @@ public function getStoreName() * @param Store $store * @param array $data * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getTargetStorePostData(Store $store, $data = []) { - $data[StoreResolverInterface::PARAM_NAME] = $store->getCode(); + $data[\Magento\Store\Api\StoreResolverInterface::PARAM_NAME] = $store->getCode(); + $data['___from_store'] = $this->_storeManager->getStore()->getCode(); + + $urlOnTargetStore = $store->getCurrentUrl(false); + $data[ActionInterface::PARAM_NAME_URL_ENCODED] = $this->urlHelper->getEncodedUrl($urlOnTargetStore); + + $url = $this->getUrl('stores/store/redirect'); - //We need to set fromStore argument as true because - //it will enable proper URL rewriting during store switching. return $this->_postDataHelper->getPostData( - $store->getCurrentUrl(true), + $url, $data ); } diff --git a/app/code/Magento/Store/Controller/Store/Redirect.php b/app/code/Magento/Store/Controller/Store/Redirect.php new file mode 100644 index 0000000000000..04714b17a0cc0 --- /dev/null +++ b/app/code/Magento/Store/Controller/Store/Redirect.php @@ -0,0 +1,113 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Controller\Store; + +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Store\Api\StoreResolverInterface; +use Magento\Store\Model\StoreResolver; +use Magento\Framework\Session\SidResolverInterface; +use Magento\Framework\Session\Generic as Session; + +/** + * Builds correct url to target store and performs redirect. + */ +class Redirect extends \Magento\Framework\App\Action\Action +{ + /** + * @var StoreRepositoryInterface + */ + private $storeRepository; + + /** + * @var StoreResolverInterface + */ + private $storeResolver; + + /** + * @var SidResolverInterface + */ + private $sidResolver; + + /** + * @var Session + */ + private $session; + + /** + * @param Context $context + * @param StoreRepositoryInterface $storeRepository + * @param StoreResolverInterface $storeResolver + * @param Session $session + * @param SidResolverInterface $sidResolver + */ + public function __construct( + Context $context, + StoreRepositoryInterface $storeRepository, + StoreResolverInterface $storeResolver, + Session $session, + SidResolverInterface $sidResolver + ) { + parent::__construct($context); + $this->storeRepository = $storeRepository; + $this->storeResolver = $storeResolver; + $this->session = $session; + $this->sidResolver = $sidResolver; + } + + /** + * @return ResponseInterface|\Magento\Framework\Controller\ResultInterface + * @throws NoSuchEntityException + */ + public function execute() + { + /** @var \Magento\Store\Model\Store $currentStore */ + $currentStore = $this->storeRepository->getById($this->storeResolver->getCurrentStoreId()); + $targetStoreCode = $this->_request->getParam(StoreResolver::PARAM_NAME); + $fromStoreCode = $this->_request->getParam('___from_store'); + $error = null; + + if ($targetStoreCode === null) { + return $this->_redirect($currentStore->getBaseUrl()); + } + + try { + /** @var \Magento\Store\Model\Store $targetStore */ + $fromStore = $this->storeRepository->get($fromStoreCode); + } catch (NoSuchEntityException $e) { + $error = __('Requested store is not found'); + } + + if ($error !== null) { + $this->messageManager->addErrorMessage($error); + $this->_redirect->redirect($this->_response, $currentStore->getBaseUrl()); + } else { + $encodedUrl = $this->_request->getParam(\Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED); + + $query = [ + '___from_store' => $fromStore->getCode(), + StoreResolverInterface::PARAM_NAME => $targetStoreCode, + \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl, + ]; + + if ($this->sidResolver->getUseSessionInUrl()) { + // allow customers to stay logged in during store switching + $sidName = $this->sidResolver->getSessionIdQueryParam($this->session); + $query[$sidName] = $this->session->getSessionId(); + } + + $arguments = [ + '_nosid' => true, + '_query' => $query + ]; + $this->_redirect->redirect($this->_response, 'stores/store/switch', $arguments); + } + } +} diff --git a/app/code/Magento/Store/Controller/Store/SwitchAction.php b/app/code/Magento/Store/Controller/Store/SwitchAction.php index f2872a51db6f4..4727872834161 100644 --- a/app/code/Magento/Store/Controller/Store/SwitchAction.php +++ b/app/code/Magento/Store/Controller/Store/SwitchAction.php @@ -10,16 +10,19 @@ use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context as ActionContext; use Magento\Framework\App\Http\Context as HttpContext; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Store\Api\StoreCookieManagerInterface; use Magento\Store\Api\StoreRepositoryInterface; -use Magento\Store\Model\Store; use Magento\Store\Model\StoreIsInactiveException; use Magento\Store\Model\StoreResolver; use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\StoreSwitcher; +use Magento\Store\Model\StoreSwitcherInterface; /** - * Switch current store view. + * Handles store switching url and makes redirect. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SwitchAction extends Action { @@ -30,6 +33,7 @@ class SwitchAction extends Action /** * @var HttpContext + * @deprecated */ protected $httpContext; @@ -40,9 +44,15 @@ class SwitchAction extends Action /** * @var StoreManagerInterface + * @deprecated */ protected $storeManager; + /** + * @var StoreSwitcherInterface + */ + private $storeSwitcher; + /** * Initialize dependencies. * @@ -51,69 +61,55 @@ class SwitchAction extends Action * @param HttpContext $httpContext * @param StoreRepositoryInterface $storeRepository * @param StoreManagerInterface $storeManager + * @param StoreSwitcherInterface $storeSwitcher */ public function __construct( ActionContext $context, StoreCookieManagerInterface $storeCookieManager, HttpContext $httpContext, StoreRepositoryInterface $storeRepository, - StoreManagerInterface $storeManager + StoreManagerInterface $storeManager, + StoreSwitcherInterface $storeSwitcher = null ) { parent::__construct($context); $this->storeCookieManager = $storeCookieManager; $this->httpContext = $httpContext; $this->storeRepository = $storeRepository; $this->storeManager = $storeManager; + $this->messageManager = $context->getMessageManager(); + $this->storeSwitcher = $storeSwitcher ?: ObjectManager::getInstance()->get(StoreSwitcher::class); } /** * @return void + * @throws StoreSwitcher\CannotSwitchStoreException */ public function execute() { - $currentActiveStore = $this->storeManager->getStore(); - $storeCode = $this->_request->getParam( + $targetStoreCode = $this->_request->getParam( StoreResolver::PARAM_NAME, $this->storeCookieManager->getStoreCodeFromCookie() ); + $fromStoreCode = $this->_request->getParam('___from_store'); + $requestedUrlToRedirect = $this->_redirect->getRedirectUrl(); + $redirectUrl = $requestedUrlToRedirect; + + $error = null; try { - $store = $this->storeRepository->getActiveStoreByCode($storeCode); + $fromStore = $this->storeRepository->get($fromStoreCode); + $targetStore = $this->storeRepository->getActiveStoreByCode($targetStoreCode); } catch (StoreIsInactiveException $e) { $error = __('Requested store is inactive'); } catch (NoSuchEntityException $e) { $error = __("The store that was requested wasn't found. Verify the store and try again."); } - - if (isset($error)) { - $this->messageManager->addError($error); - $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl()); - return; - } - - $defaultStoreView = $this->storeManager->getDefaultStoreView(); - if ($defaultStoreView->getId() == $store->getId()) { - $this->storeCookieManager->deleteStoreCookie($store); + if ($error !== null) { + $this->messageManager->addErrorMessage($error); } else { - $this->httpContext->setValue(Store::ENTITY, $store->getCode(), $defaultStoreView->getCode()); - $this->storeCookieManager->setStoreCookie($store); + $redirectUrl = $this->storeSwitcher->switch($fromStore, $targetStore, $requestedUrlToRedirect); } - if ($store->isUseStoreInUrl()) { - // Change store code in redirect url - if (strpos($this->_redirect->getRedirectUrl(), $currentActiveStore->getBaseUrl()) !== false) { - $this->getResponse()->setRedirect( - str_replace( - $currentActiveStore->getBaseUrl(), - $store->getBaseUrl(), - $this->_redirect->getRedirectUrl() - ) - ); - } else { - $this->getResponse()->setRedirect($store->getBaseUrl()); - } - } else { - $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl()); - } + $this->getResponse()->setRedirect($redirectUrl); } } diff --git a/app/code/Magento/Store/Model/Plugin/StoreCookie.php b/app/code/Magento/Store/Model/Plugin/StoreCookie.php index 612d35e136d7e..9fca86ab71762 100644 --- a/app/code/Magento/Store/Model/Plugin/StoreCookie.php +++ b/app/code/Magento/Store/Model/Plugin/StoreCookie.php @@ -82,12 +82,5 @@ public function beforeDispatch( $this->storeCookieManager->deleteStoreCookie($this->storeManager->getDefaultStoreView()); } } - if ($this->storeCookieManager->getStoreCodeFromCookie() === null - || $request->getParam(StoreResolverInterface::PARAM_NAME) !== null - ) { - $storeId = $this->storeResolver->getCurrentStoreId(); - $store = $this->storeRepository->getActiveStoreById($storeId); - $this->storeCookieManager->setStoreCookie($store); - } } } diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index 6e749df6a768d..cb624f09be091 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -1163,7 +1163,7 @@ public function isDefault() /** * Retrieve current url for store * - * @param bool|string $fromStore + * @param bool $fromStore * @return string * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -1223,7 +1223,7 @@ public function getCurrentUrl($fromStore = true) . (isset($storeParsedUrl['port']) ? ':' . $storeParsedUrl['port'] : '') . $storeParsedUrl['path'] . $requestStringPath - . ($currentUrlQueryParams ? '?' . http_build_query($currentUrlQueryParams, '', '&') : ''); + . ($currentUrlQueryParams ? '?' . http_build_query($currentUrlQueryParams) : ''); return $currentUrl; } diff --git a/app/code/Magento/Store/Model/StoreSwitcher.php b/app/code/Magento/Store/Model/StoreSwitcher.php new file mode 100644 index 0000000000000..476196ad84f8a --- /dev/null +++ b/app/code/Magento/Store/Model/StoreSwitcher.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Model; + +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreSwitcher\CannotSwitchStoreException; + +/** + * Handles store switching procedure and detects url for final redirect after store switching. + */ +class StoreSwitcher implements StoreSwitcherInterface +{ + /** + * @var StoreSwitcherInterface[] + */ + private $storeSwitchers; + + /** + * @param StoreSwitcherInterface[] $storeSwitchers + * @throws \Exception + */ + public function __construct(array $storeSwitchers) + { + foreach ($storeSwitchers as $switcherName => $switcherInstance) { + if (!$switcherInstance instanceof StoreSwitcherInterface) { + throw new \InvalidArgumentException( + "Store switcher '{$switcherName}' is expected to implement interface " + . StoreSwitcherInterface::class + ); + } + } + $this->storeSwitchers = $storeSwitchers; + } + + /** + * @param StoreInterface $fromStore store where we came from + * @param StoreInterface $targetStore store where to go to + * @param string $redirectUrl original url requested for redirect after switching + * @return string url to be redirected after switching + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @throws CannotSwitchStoreException + */ + public function switch(StoreInterface $fromStore, StoreInterface $targetStore, string $redirectUrl): string + { + $targetUrl = $redirectUrl; + + foreach ($this->storeSwitchers as $storeSwitcher) { + $targetUrl = $storeSwitcher->switch($fromStore, $targetStore, $targetUrl); + } + + return $targetUrl; + } +} diff --git a/app/code/Magento/Store/Model/StoreSwitcher/CannotSwitchStoreException.php b/app/code/Magento/Store/Model/StoreSwitcher/CannotSwitchStoreException.php new file mode 100644 index 0000000000000..b19ae419e2f07 --- /dev/null +++ b/app/code/Magento/Store/Model/StoreSwitcher/CannotSwitchStoreException.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Model\StoreSwitcher; + +use Magento\Framework\Exception\RuntimeException; +use Magento\Framework\Phrase; + +/** + * Exception thrown if store cannot be switched. + */ +class CannotSwitchStoreException extends RuntimeException +{ + /** + * @param \Exception|null $cause + * @param Phrase|null $phrase + * @param int $code + */ + public function __construct(\Exception $cause = null, Phrase $phrase = null, int $code = 0) + { + parent::__construct($phrase ?: __('The store cannot be switched.'), $cause, $code); + } +} diff --git a/app/code/Magento/Store/Model/StoreSwitcher/CleanTargetUrl.php b/app/code/Magento/Store/Model/StoreSwitcher/CleanTargetUrl.php new file mode 100644 index 0000000000000..31b6eb62fb9e1 --- /dev/null +++ b/app/code/Magento/Store/Model/StoreSwitcher/CleanTargetUrl.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Model\StoreSwitcher; + +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Api\StoreResolverInterface; +use Magento\Store\Model\StoreSwitcherInterface; +use Magento\Framework\Url\Helper\Data as UrlHelper; + +/** + * Remove SID, from_store, store from target url. + */ +class CleanTargetUrl implements StoreSwitcherInterface +{ + /** + * @var UrlHelper + */ + private $urlHelper; + + /** + * @var \Magento\Framework\Session\Generic + */ + private $session; + + /** + * @var \Magento\Framework\Session\SidResolverInterface + */ + private $sidResolver; + + /** + * @param UrlHelper $urlHelper + * @param \Magento\Framework\Session\Generic $session + * @param \Magento\Framework\Session\SidResolverInterface $sidResolver + */ + public function __construct( + UrlHelper $urlHelper, + \Magento\Framework\Session\Generic $session, + \Magento\Framework\Session\SidResolverInterface $sidResolver + ) { + $this->urlHelper = $urlHelper; + $this->session = $session; + $this->sidResolver = $sidResolver; + } + + /** + * @param StoreInterface $fromStore store where we came from + * @param StoreInterface $targetStore store where to go to + * @param string $redirectUrl original url requested for redirect after switching + * @return string redirect url + */ + public function switch(StoreInterface $fromStore, StoreInterface $targetStore, string $redirectUrl): string + { + $targetUrl = $redirectUrl; + $sidName = $this->sidResolver->getSessionIdQueryParam($this->session); + $targetUrl = $this->urlHelper->removeRequestParam($targetUrl, $sidName); + $targetUrl = $this->urlHelper->removeRequestParam($targetUrl, '___from_store'); + $targetUrl = $this->urlHelper->removeRequestParam($targetUrl, StoreResolverInterface::PARAM_NAME); + + return $targetUrl; + } +} diff --git a/app/code/Magento/Store/Model/StoreSwitcher/ManagePrivateContent.php b/app/code/Magento/Store/Model/StoreSwitcher/ManagePrivateContent.php new file mode 100644 index 0000000000000..4ef5aad5d7988 --- /dev/null +++ b/app/code/Magento/Store/Model/StoreSwitcher/ManagePrivateContent.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Model\StoreSwitcher; + +use Magento\Store\Model\StoreSwitcherInterface; +use Magento\Store\Api\Data\StoreInterface; + +/** + * Set private content cookie to have actual local storage data on target store after store switching. + */ +class ManagePrivateContent implements StoreSwitcherInterface +{ + /** + * @var \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory + */ + private $cookieMetadataFactory; + + /** + * @var \Magento\Framework\Stdlib\CookieManagerInterface + */ + private $cookieManager; + + /** + * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory + * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager + */ + public function __construct( + \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory, + \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager + ) { + $this->cookieMetadataFactory = $cookieMetadataFactory; + $this->cookieManager = $cookieManager; + } + + /** + * @param StoreInterface $fromStore store where we came from + * @param StoreInterface $targetStore store where to go to + * @param string $redirectUrl original url requested for redirect after switching + * @return string redirect url + * @throws CannotSwitchStoreException + */ + public function switch(StoreInterface $fromStore, StoreInterface $targetStore, string $redirectUrl): string + { + try { + $publicCookieMetadata = $this->cookieMetadataFactory->createPublicCookieMetadata() + ->setDurationOneYear() + ->setPath('/') + ->setSecure(false) + ->setHttpOnly(false); + $this->cookieManager->setPublicCookie( + \Magento\Framework\App\PageCache\Version::COOKIE_NAME, + 'should_be_updated', + $publicCookieMetadata + ); + } catch (\Exception $e) { + throw new CannotSwitchStoreException($e); + } + + return $redirectUrl; + } +} diff --git a/app/code/Magento/Store/Model/StoreSwitcher/ManageStoreCookie.php b/app/code/Magento/Store/Model/StoreSwitcher/ManageStoreCookie.php new file mode 100644 index 0000000000000..23fda163e9662 --- /dev/null +++ b/app/code/Magento/Store/Model/StoreSwitcher/ManageStoreCookie.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Model\StoreSwitcher; + +use Magento\Store\Model\StoreSwitcherInterface; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Framework\App\Http\Context as HttpContext; +use Magento\Store\Api\StoreCookieManagerInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\Store; + +/** + * Manage store cookie depending on what store view customer is. + */ +class ManageStoreCookie implements StoreSwitcherInterface +{ + /** + * @var StoreCookieManagerInterface + */ + private $storeCookieManager; + + /** + * @var HttpContext + */ + private $httpContext; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @param StoreCookieManagerInterface $storeCookieManager + * @param HttpContext $httpContext + * @param StoreManagerInterface $storeManager + */ + public function __construct( + StoreCookieManagerInterface $storeCookieManager, + HttpContext $httpContext, + StoreManagerInterface $storeManager + ) { + $this->storeCookieManager = $storeCookieManager; + $this->httpContext = $httpContext; + $this->storeManager = $storeManager; + } + + /** + * @param StoreInterface $fromStore store where we came from + * @param StoreInterface $targetStore store where to go to + * @param string $redirectUrl original url requested for redirect after switching + * @return string redirect url + */ + public function switch(StoreInterface $fromStore, StoreInterface $targetStore, string $redirectUrl): string + { + $defaultStoreView = $this->storeManager->getDefaultStoreView(); + if ($defaultStoreView !== null) { + if ($defaultStoreView->getId() === $targetStore->getId()) { + $this->storeCookieManager->deleteStoreCookie($targetStore); + } else { + $this->httpContext->setValue(Store::ENTITY, $targetStore->getCode(), $defaultStoreView->getCode()); + $this->storeCookieManager->setStoreCookie($targetStore); + } + } + + return $redirectUrl; + } +} diff --git a/app/code/Magento/Store/Model/StoreSwitcherInterface.php b/app/code/Magento/Store/Model/StoreSwitcherInterface.php new file mode 100644 index 0000000000000..b6c5c38d23ef6 --- /dev/null +++ b/app/code/Magento/Store/Model/StoreSwitcherInterface.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Model; + +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreSwitcher\CannotSwitchStoreException; + +/** + * Handles store switching procedure and detects url for final redirect after store switching. + */ +interface StoreSwitcherInterface +{ + /** + * @param StoreInterface $fromStore store where we came from + * @param StoreInterface $targetStore store where to go to + * @param string $redirectUrl original url requested for redirect after switching + * @return string url to be redirected after switching + * @throws CannotSwitchStoreException + */ + public function switch(StoreInterface $fromStore, StoreInterface $targetStore, string $redirectUrl): string; +} diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index 4e3c724572e79..9c3330ba74140 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -18,7 +18,7 @@ <data key="is_active">1</data> <data key="store_id">null</data> <data key="store_action">add</data> - <data key="store_type">group</data> + <data key="store_type">store</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> <entity name="customStoreEN" type="store"> @@ -27,7 +27,7 @@ <data key="is_active">1</data> <data key="store_id">null</data> <data key="store_action">add</data> - <data key="store_type">group</data> + <data key="store_type">store</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> <entity name="customStoreFR" type="store"> @@ -46,7 +46,7 @@ <data key="is_active">1</data> <data key="store_id">null</data> <data key="store_action">add</data> - <data key="store_type">group</data> + <data key="store_type">store</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> </entities> diff --git a/app/code/Magento/Store/Test/Unit/Block/SwitcherTest.php b/app/code/Magento/Store/Test/Unit/Block/SwitcherTest.php index 8b4799d2b3437..57cb63e7c2744 100644 --- a/app/code/Magento/Store/Test/Unit/Block/SwitcherTest.php +++ b/app/code/Magento/Store/Test/Unit/Block/SwitcherTest.php @@ -25,6 +25,9 @@ class SwitcherTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $urlBuilder; + /** @var \Magento\Store\Api\Data\StoreInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $store; + protected function setUp() { $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class)->getMock(); @@ -33,6 +36,9 @@ protected function setUp() $this->context->expects($this->any())->method('getStoreManager')->will($this->returnValue($this->storeManager)); $this->context->expects($this->any())->method('getUrlBuilder')->will($this->returnValue($this->urlBuilder)); $this->corePostDataHelper = $this->createMock(\Magento\Framework\Data\Helper\PostHelper::class); + $this->store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); $this->switcher = (new ObjectManager($this))->getObject( \Magento\Store\Block\Switcher::class, [ @@ -50,14 +56,23 @@ public function testGetTargetStorePostData() $store->expects($this->any()) ->method('getCode') ->willReturn('new-store'); - $storeSwitchUrl = 'http://domain.com/stores/store/switch'; + $storeSwitchUrl = 'http://domain.com/stores/store/redirect'; $store->expects($this->atLeastOnce()) ->method('getCurrentUrl') - ->with(true) + ->with(false) + ->willReturn($storeSwitchUrl); + $this->storeManager->expects($this->once()) + ->method('getStore') + ->willReturn($this->store); + $this->store->expects($this->once()) + ->method('getCode') + ->willReturn('old-store'); + $this->urlBuilder->expects($this->once()) + ->method('getUrl') ->willReturn($storeSwitchUrl); $this->corePostDataHelper->expects($this->any()) ->method('getPostData') - ->with($storeSwitchUrl, ['___store' => 'new-store']); + ->with($storeSwitchUrl, ['___store' => 'new-store', 'uenc' => null, '___from_store' => 'old-store']); $this->switcher->getTargetStorePostData($store); } diff --git a/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchActionTest.php b/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchActionTest.php index 1e7b2691a0084..fa7c696bf53cd 100644 --- a/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchActionTest.php +++ b/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchActionTest.php @@ -8,13 +8,15 @@ use Magento\Framework\App\Http\Context as HttpContext; use Magento\Store\Api\StoreCookieManagerInterface; use Magento\Store\Api\StoreRepositoryInterface; -use Magento\Store\Model\Store; -use Magento\Store\Model\StoreResolver; use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\StoreResolver; +use Magento\Store\Model\StoreSwitcher; +use Magento\Store\Model\StoreSwitcherInterface; /** * Test class for \Magento\Store\Controller\Store\SwitchAction + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SwitchActionTest extends \PHPUnit\Framework\TestCase { @@ -58,6 +60,9 @@ class SwitchActionTest extends \PHPUnit\Framework\TestCase */ private $redirectMock; + /** @var StoreSwitcherInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $storeSwitcher; + protected function setUp() { $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class)->getMock(); @@ -73,6 +78,10 @@ protected function setUp() ->getMockForAbstractClass(); $this->redirectMock = $this->getMockBuilder(\Magento\Framework\App\Response\RedirectInterface::class)->getMock(); + $this->storeSwitcher = $this->getMockBuilder(StoreSwitcher::class) + ->disableOriginalConstructor() + ->setMethods(['switch']) + ->getMock(); $this->model = (new ObjectManager($this))->getObject( \Magento\Store\Controller\Store\SwitchAction::class, @@ -83,81 +92,45 @@ protected function setUp() 'storeManager' => $this->storeManagerMock, '_request' => $this->requestMock, '_response' => $this->responseMock, - '_redirect' => $this->redirectMock + '_redirect' => $this->redirectMock, + 'storeSwitcher' => $this->storeSwitcher ] ); } - public function testExecuteSuccessWithoutUseStoreInUrl() + public function testExecute() { $storeToSwitchToCode = 'sv2'; $defaultStoreViewCode = 'default'; $expectedRedirectUrl = "magento.com/{$storeToSwitchToCode}"; - $currentActiveStoreMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)->getMock(); $defaultStoreViewMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)->getMock(); $storeToSwitchToMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) ->disableOriginalConstructor() ->setMethods(['isUseStoreInUrl']) ->getMockForAbstractClass(); - $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($currentActiveStoreMock); - $this->requestMock->expects($this->once())->method('getParam')->willReturn($storeToSwitchToCode); + $this->requestMock->expects($this->any())->method('getParam')->willReturnMap( + [ + [StoreResolver::PARAM_NAME, null, $storeToSwitchToCode], + ['___from_store', null, $defaultStoreViewCode] + ] + ); $this->storeRepositoryMock ->expects($this->once()) - ->method('getActiveStoreByCode') - ->willReturn($storeToSwitchToMock); - $this->storeManagerMock - ->expects($this->once()) - ->method('getDefaultStoreView') + ->method('get') + ->with($defaultStoreViewCode) ->willReturn($defaultStoreViewMock); - $defaultStoreViewMock->expects($this->once())->method('getId')->willReturn($defaultStoreViewCode); - $storeToSwitchToMock->expects($this->once())->method('getId')->willReturn($storeToSwitchToCode); - $storeToSwitchToMock->expects($this->once())->method('isUseStoreInUrl')->willReturn(false); - $this->redirectMock->expects($this->once())->method('getRedirectUrl')->willReturn($expectedRedirectUrl); - $this->responseMock->expects($this->once())->method('setRedirect')->with($expectedRedirectUrl); - - $this->model->execute(); - } - - public function testExecuteSuccessWithUseStoreInUrl() - { - $currentActiveStoreCode = 'sv1'; - $storeToSwitchToCode = 'sv2'; - $defaultStoreViewCode = 'default'; - $originalRedirectUrl = "magento.com/{$currentActiveStoreCode}/test-page/test-sub-page"; - $expectedRedirectUrl = "magento.com/{$storeToSwitchToCode}/test-page/test-sub-page"; - $currentActiveStoreMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->setMethods(['isUseStoreInUrl', 'getBaseUrl']) - ->getMockForAbstractClass(); - $defaultStoreViewMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)->getMock(); - $storeToSwitchToMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->setMethods(['isUseStoreInUrl', 'getBaseUrl']) - ->getMockForAbstractClass(); - - $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($currentActiveStoreMock); - $this->requestMock->expects($this->once())->method('getParam')->willReturn($storeToSwitchToCode); $this->storeRepositoryMock ->expects($this->once()) ->method('getActiveStoreByCode') + ->with($storeToSwitchToCode) ->willReturn($storeToSwitchToMock); - $this->storeManagerMock - ->expects($this->once()) - ->method('getDefaultStoreView') - ->willReturn($defaultStoreViewMock); - $defaultStoreViewMock->expects($this->once())->method('getId')->willReturn($defaultStoreViewCode); - $storeToSwitchToMock->expects($this->once())->method('getId')->willReturn($storeToSwitchToCode); - $storeToSwitchToMock->expects($this->once())->method('isUseStoreInUrl')->willReturn(true); - $this->redirectMock->expects($this->any())->method('getRedirectUrl')->willReturn($originalRedirectUrl); - $currentActiveStoreMock - ->expects($this->any()) - ->method('getBaseUrl') - ->willReturn("magento.com/{$currentActiveStoreCode}"); - $storeToSwitchToMock - ->expects($this->once()) - ->method('getBaseUrl') - ->willReturn("magento.com/{$storeToSwitchToCode}"); + $this->storeSwitcher->expects($this->once()) + ->method('switch') + ->with($defaultStoreViewMock, $storeToSwitchToMock, $expectedRedirectUrl) + ->willReturn($expectedRedirectUrl); + + $this->redirectMock->expects($this->once())->method('getRedirectUrl')->willReturn($expectedRedirectUrl); $this->responseMock->expects($this->once())->method('setRedirect')->with($expectedRedirectUrl); $this->model->execute(); diff --git a/app/code/Magento/Store/Test/Unit/Model/Plugin/StoreCookieTest.php b/app/code/Magento/Store/Test/Unit/Model/Plugin/StoreCookieTest.php index e56b5c7fcaa19..7aa992064f794 100644 --- a/app/code/Magento/Store/Test/Unit/Model/Plugin/StoreCookieTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/Plugin/StoreCookieTest.php @@ -125,10 +125,6 @@ public function testBeforeDispatchNoSuchEntity() $this->storeCookieManagerMock->expects($this->once()) ->method('deleteStoreCookie') ->with($this->storeMock); - $this->requestMock->expects($this->atLeastOnce()) - ->method('getParam') - ->with(StoreResolverInterface::PARAM_NAME) - ->willReturn(null); $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock); } @@ -148,10 +144,6 @@ public function testBeforeDispatchStoreIsInactive() $this->storeCookieManagerMock->expects($this->once()) ->method('deleteStoreCookie') ->with($this->storeMock); - $this->requestMock->expects($this->atLeastOnce()) - ->method('getParam') - ->with(StoreResolverInterface::PARAM_NAME) - ->willReturn(null); $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock); } @@ -171,10 +163,6 @@ public function testBeforeDispatchInvalidArgument() $this->storeCookieManagerMock->expects($this->once()) ->method('deleteStoreCookie') ->with($this->storeMock); - $this->requestMock->expects($this->atLeastOnce()) - ->method('getParam') - ->with(StoreResolverInterface::PARAM_NAME) - ->willReturn(null); $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock); } @@ -194,18 +182,6 @@ public function testBeforeDispatchNoStoreCookie() ->method('deleteStoreCookie') ->with($this->storeMock); - $this->storeResolverMock->expects($this->atLeastOnce()) - ->method('getCurrentStoreId') - ->willReturn(1); - - $this->storeRepositoryMock->expects($this->atLeastOnce()) - ->method('getActiveStoreById') - ->willReturn($this->storeMock); - - $this->storeCookieManagerMock->expects($this->atLeastOnce()) - ->method('setStoreCookie') - ->with($this->storeMock); - $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock); } @@ -222,23 +198,6 @@ public function testBeforeDispatchWithStoreRequestParam() ->method('deleteStoreCookie') ->with($this->storeMock); - $this->requestMock->expects($this->atLeastOnce()) - ->method('getParam') - ->with(StoreResolverInterface::PARAM_NAME) - ->willReturn($storeCode); - - $this->storeResolverMock->expects($this->atLeastOnce()) - ->method('getCurrentStoreId') - ->willReturn(1); - - $this->storeRepositoryMock->expects($this->atLeastOnce()) - ->method('getActiveStoreById') - ->willReturn($this->storeMock); - - $this->storeCookieManagerMock->expects($this->atLeastOnce()) - ->method('setStoreCookie') - ->with($this->storeMock); - $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock); } } diff --git a/app/code/Magento/Store/Test/Unit/Model/StoreTest.php b/app/code/Magento/Store/Test/Unit/Model/StoreTest.php index 4cbf78d0d6471..b00194db4826e 100644 --- a/app/code/Magento/Store/Test/Unit/Model/StoreTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/StoreTest.php @@ -377,10 +377,11 @@ public function testGetBaseUrlWrongType() * @param boolean $secure * @param string $url * @param string $expected + * @param bool|string $fromStore */ - public function testGetCurrentUrl($secure, $url, $expected) + public function testGetCurrentUrl($secure, $url, $expected, $fromStore) { - $defaultStore = $this->createPartialMock(\Magento\Store\Model\Store::class, [ + $defaultStore = $this->createPartialMock(Store::class, [ 'getId', 'isCurrentlySecure', '__wakeup' @@ -393,15 +394,31 @@ public function testGetCurrentUrl($secure, $url, $expected) $config = $this->getMockForAbstractClass(\Magento\Framework\App\Config\ReinitableConfigInterface::class); - $this->requestMock->expects($this->atLeastOnce())->method('getRequestString')->will($this->returnValue('')); + $requestString = preg_replace( + '/http(s?)\:\/\/[a-z0-9\-]+\//i', + '', + $url + ); + $this->requestMock + ->expects($this->atLeastOnce()) + ->method('getRequestString') + ->willReturn($requestString); $this->requestMock->expects($this->atLeastOnce())->method('getQueryValue')->will($this->returnValue([ 'SID' => 'sid' ])); $urlMock = $this->getMockForAbstractClass(\Magento\Framework\UrlInterface::class); - $urlMock->expects($this->atLeastOnce())->method('setScope')->will($this->returnSelf()); - $urlMock->expects($this->any())->method('getUrl') - ->will($this->returnValue($url)); + $urlMock + ->expects($this->atLeastOnce()) + ->method('setScope') + ->will($this->returnSelf()); + $urlMock->expects($this->any()) + ->method('getUrl') + ->will($this->returnValue(str_replace($requestString, '', $url))); + $urlMock + ->expects($this->atLeastOnce()) + ->method('escape') + ->willReturnArgument(0); $storeManager = $this->getMockForAbstractClass(\Magento\Store\Model\StoreManagerInterface::class); $storeManager->expects($this->any()) @@ -416,7 +433,7 @@ public function testGetCurrentUrl($secure, $url, $expected) $model->setStoreId(2); $model->setCode('scope_code'); - $this->assertEquals($expected, $model->getCurrentUrl(false)); + $this->assertEquals($expected, $model->getCurrentUrl($fromStore)); } /** @@ -425,9 +442,31 @@ public function testGetCurrentUrl($secure, $url, $expected) public function getCurrentUrlDataProvider() { return [ - [true, 'http://test/url', 'http://test/url?SID=sid&___store=scope_code'], - [true, 'http://test/url?SID=sid1&___store=scope', 'http://test/url?SID=sid&___store=scope_code'], - [false, 'https://test/url', 'https://test/url?SID=sid&___store=scope_code'] + [ + true, + 'http://test/url', + 'http://test/url?SID=sid&___store=scope_code', + false + ], + [ + true, + 'http://test/url?SID=sid1&___store=scope', + 'http://test/url?SID=sid&___store=scope_code', + false + ], + [ + false, + 'https://test/url', + 'https://test/url?SID=sid&___store=scope_code', + false + ], + [ + true, + 'http://test/u/u.2?___store=scope_code', + 'http://test/u/u.2?' + . '___store=scope_code&SID=sid&___from_store=old-store', + 'old-store' + ] ]; } diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml index 27133de270e2f..836f19ae33a09 100644 --- a/app/code/Magento/Store/etc/di.xml +++ b/app/code/Magento/Store/etc/di.xml @@ -25,6 +25,7 @@ <preference for="Magento\Framework\App\ScopeFallbackResolverInterface" type="Magento\Store\Model\ScopeFallbackResolver"/> <preference for="Magento\Framework\App\ScopeTreeProviderInterface" type="Magento\Store\Model\ScopeTreeProvider"/> <preference for="Magento\Framework\App\ScopeValidatorInterface" type="Magento\Store\Model\ScopeValidator"/> + <preference for="Magento\Store\Model\StoreSwitcherInterface" type="Magento\Store\Model\StoreSwitcher" /> <type name="Magento\Framework\App\Response\Http"> <plugin name="genericHeaderPlugin" type="Magento\Framework\App\Response\HeaderManager"/> </type> @@ -417,4 +418,13 @@ </argument> </arguments> </type> + <type name="Magento\Store\Model\StoreSwitcher"> + <arguments> + <argument name="storeSwitchers" xsi:type="array"> + <item name="cleanTargetUrl" xsi:type="object">Magento\Store\Model\StoreSwitcher\CleanTargetUrl</item> + <item name="manageStoreCookie" xsi:type="object">Magento\Store\Model\StoreSwitcher\ManageStoreCookie</item> + <item name="managePrivateContent" xsi:type="object">Magento\Store\Model\StoreSwitcher\ManagePrivateContent</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/UrlRewrite/Block/Plugin/Store/Switcher/SetRedirectUrl.php b/app/code/Magento/UrlRewrite/Block/Plugin/Store/Switcher/SetRedirectUrl.php deleted file mode 100644 index 9bdff38e7aaa2..0000000000000 --- a/app/code/Magento/UrlRewrite/Block/Plugin/Store/Switcher/SetRedirectUrl.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\UrlRewrite\Block\Plugin\Store\Switcher; - -use Magento\Framework\Url\Helper\Data as UrlHelper; -use Magento\Framework\UrlInterface; -use Magento\UrlRewrite\Model\UrlFinderInterface; -use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; -use Magento\Framework\App\RequestInterface; -use Magento\Framework\App\ActionInterface; - -class SetRedirectUrl -{ - /** - * @var \Magento\Framework\Url\Helper\Data - */ - private $urlHelper; - - /** - * @var \Magento\Framework\UrlInterface - */ - private $urlBuilder; - - /** - * @var \Magento\UrlRewrite\Model\UrlFinderInterface - */ - private $urlFinder; - - /** - * @var \Magento\Framework\App\RequestInterface - */ - private $request; - - /** - * @param UrlHelper $urlHelper - * @param UrlInterface $urlBuilder - * @param UrlFinderInterface $urlFinder - * @param RequestInterface $request - */ - public function __construct( - UrlHelper $urlHelper, - UrlInterface $urlBuilder, - UrlFinderInterface $urlFinder, - RequestInterface $request - ) { - $this->urlHelper = $urlHelper; - $this->urlBuilder = $urlBuilder; - $this->urlFinder = $urlFinder; - $this->request = $request; - } - - /** - * Set redirect url for store view based on request path info - * - * @param \Magento\Store\Block\Switcher $switcher - * @param \Magento\Store\Model\Store $store - * @param array $data - * @return array - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function beforeGetTargetStorePostData( - \Magento\Store\Block\Switcher $switcher, - \Magento\Store\Model\Store $store, - $data = [] - ) { - $urlRewrite = $this->urlFinder->findOneByData([ - UrlRewrite::TARGET_PATH => $this->trimSlashInPath($this->request->getPathInfo()), - UrlRewrite::STORE_ID => $store->getId(), - ]); - if ($urlRewrite) { - $data[ActionInterface::PARAM_NAME_URL_ENCODED] = $this->urlHelper->getEncodedUrl( - $this->trimSlashInPath($this->urlBuilder->getUrl($urlRewrite->getRequestPath(), ['_scope' => $store])) - ); - } - return [$store, $data]; - } - - /** - * @param string $path - * @return string - */ - private function trimSlashInPath($path) - { - return trim($path, '/'); - } -} diff --git a/app/code/Magento/UrlRewrite/Controller/Router.php b/app/code/Magento/UrlRewrite/Controller/Router.php index 73002c10cf1b6..ce432f24365a6 100644 --- a/app/code/Magento/UrlRewrite/Controller/Router.php +++ b/app/code/Magento/UrlRewrite/Controller/Router.php @@ -7,7 +7,6 @@ use Magento\Framework\App\RequestInterface; use Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite; -use Magento\UrlRewrite\Model\OptionProvider; use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\Framework\App\Request\Http as HttpRequest; @@ -78,44 +77,6 @@ public function __construct( */ public function match(RequestInterface $request) { - if ($fromStore = $request->getParam('___from_store')) { - //If we're in the process of switching stores then matching rewrite - //rule from previous store because the URL was not changed yet from - //old store's format. - $oldStoreId = $this->storeManager->getStore($fromStore)->getId(); - $oldRewrite = $this->getRewrite( - $request->getPathInfo(), - $oldStoreId - ); - if ($oldRewrite && $oldRewrite->getRedirectType() === 0) { - //If there is a match and it's a correct URL then just - //redirecting to current store's URL equivalent, - //otherwise just continuing finding a rule within current store. - $currentRewrite = $this->urlFinder->findOneByData( - [ - UrlRewrite::ENTITY_TYPE => $oldRewrite->getEntityType(), - UrlRewrite::ENTITY_ID => $oldRewrite->getEntityId(), - UrlRewrite::STORE_ID => - $this->storeManager->getStore()->getId(), - UrlRewrite::REDIRECT_TYPE => 0, - ] - ); - if ($currentRewrite - && $currentRewrite->getRequestPath() - !== $oldRewrite->getRequestPath() - ) { - return $this->redirect( - $request, - $this->url->getUrl( - '', - ['_direct' => $currentRewrite->getRequestPath()] - ), - OptionProvider::TEMPORARY - ); - } - } - } - $rewrite = $this->getRewrite( $request->getPathInfo(), $this->storeManager->getStore()->getId() diff --git a/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php b/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php new file mode 100644 index 0000000000000..2b6f9e87e3de2 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\UrlRewrite\Model\StoreSwitcher; + +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreSwitcherInterface; +use Magento\UrlRewrite\Model\UrlFinderInterface; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; + +/** + * Handle url rewrites for redirect url + */ +class RewriteUrl implements StoreSwitcherInterface +{ + /** + * @var UrlFinderInterface + */ + private $urlFinder; + + /** + * @var \Magento\Framework\HTTP\PhpEnvironment\RequestFactory + */ + private $requestFactory; + + /** + * @param UrlFinderInterface $urlFinder + * @param \Magento\Framework\HTTP\PhpEnvironment\RequestFactory $requestFactory + */ + public function __construct( + UrlFinderInterface $urlFinder, + \Magento\Framework\HTTP\PhpEnvironment\RequestFactory $requestFactory + ) { + $this->urlFinder = $urlFinder; + $this->requestFactory = $requestFactory; + } + + /** + * @param StoreInterface $fromStore + * @param StoreInterface $targetStore + * @param string $redirectUrl + * @return string + */ + public function switch(StoreInterface $fromStore, StoreInterface $targetStore, string $redirectUrl): string + { + $targetUrl = $redirectUrl; + /** @var \Magento\Framework\HTTP\PhpEnvironment\Request $request */ + $request = $this->requestFactory->create(['uri' => $targetUrl]); + + $urlPath = ltrim($request->getPathInfo(), '/'); + + if ($targetStore->isUseStoreInUrl()) { + // Remove store code in redirect url for correct rewrite search + $storeCode = preg_quote($targetStore->getCode() . '/', '/'); + $pattern = "@^($storeCode)@"; + $urlPath = preg_replace($pattern, '', $urlPath); + } + + $oldStoreId = $fromStore->getId(); + $oldRewrite = $this->urlFinder->findOneByData([ + UrlRewrite::REQUEST_PATH => $urlPath, + UrlRewrite::STORE_ID => $oldStoreId, + ]); + if ($oldRewrite) { + // look for url rewrite match on the target store + $currentRewrite = $this->urlFinder->findOneByData([ + UrlRewrite::REQUEST_PATH => $urlPath, + UrlRewrite::STORE_ID => $targetStore->getId(), + ]); + if (null === $currentRewrite) { + /** @var \Magento\Framework\App\Response\Http $response */ + $targetUrl = $targetStore->getBaseUrl(); + } + } + + return $targetUrl; + } +} diff --git a/app/code/Magento/UrlRewrite/Test/Unit/Block/Plugin/Store/Switcher/SetRedirectUrlTest.php b/app/code/Magento/UrlRewrite/Test/Unit/Block/Plugin/Store/Switcher/SetRedirectUrlTest.php deleted file mode 100644 index 46bf90493004d..0000000000000 --- a/app/code/Magento/UrlRewrite/Test/Unit/Block/Plugin/Store/Switcher/SetRedirectUrlTest.php +++ /dev/null @@ -1,96 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\UrlRewrite\Test\Unit\Block\Plugin\Store\Switcher; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -class SetRedirectUrlTest extends \PHPUnit\Framework\TestCase -{ - /** @var \Magento\UrlRewrite\Block\Plugin\Store\Switcher\SetRedirectUrl */ - protected $unit; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $urlFinder; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $urlHelper; - - /** @var \Magento\Store\Block\Switcher|\PHPUnit_Framework_MockObject_MockObject */ - protected $switcher; - - /** @var \Magento\Store\Model\Store|\PHPUnit_Framework_MockObject_MockObject */ - protected $store; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $request; - - /** @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $urlBuilder; - - protected function setUp() - { - $this->store = $this->createMock(\Magento\Store\Model\Store::class); - $this->request = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->urlBuilder = $this->createMock(\Magento\Framework\UrlInterface::class); - $this->urlHelper = $this->createMock(\Magento\Framework\Url\Helper\Data::class); - $this->urlFinder = $this->createMock(\Magento\UrlRewrite\Model\UrlFinderInterface::class); - $this->switcher = $this->createMock(\Magento\Store\Block\Switcher::class); - - $this->unit = (new ObjectManager($this))->getObject( - \Magento\UrlRewrite\Block\Plugin\Store\Switcher\SetRedirectUrl::class, - [ - 'urlFinder' => $this->urlFinder, - 'urlHelper' => $this->urlHelper, - 'urlBuilder' => $this->urlBuilder, - 'request' => $this->request, - ] - ); - } - - public function testNoUrlRewriteForSpecificStoreOnGetTargetStorePostData() - { - $this->request->expects($this->once())->method('getPathInfo')->willReturn('path'); - $this->urlFinder->expects($this->once())->method('findOneByData')->willReturn(null); - $this->urlHelper->expects($this->never())->method('getEncodedUrl'); - $this->assertEquals( - [$this->store, []], - $this->unit->beforeGetTargetStorePostData($this->switcher, $this->store, []) - ); - } - - public function testTrimPathInfoForGetTargetStorePostData() - { - $this->request->expects($this->once())->method('getPathInfo')->willReturn('path/with/trim/'); - $this->store->expects($this->once())->method('getId')->willReturn(1); - $this->urlFinder->expects($this->once())->method('findOneByData') - ->with([ - \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::TARGET_PATH => 'path/with/trim', - \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::STORE_ID => 1, - ]) - ->willReturn(null); - $this->urlHelper->expects($this->never())->method('getEncodedUrl'); - $this->assertEquals( - [$this->store, []], - $this->unit->beforeGetTargetStorePostData($this->switcher, $this->store, []) - ); - } - - public function testGetTargetStorePostData() - { - $urlRewrite = $this->createMock(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class); - $urlRewrite->expects($this->once())->method('getRequestPath')->willReturn('path'); - - $this->request->expects($this->once())->method('getPathInfo')->willReturn('path'); - $this->urlFinder->expects($this->once())->method('findOneByData')->willReturn($urlRewrite); - $this->urlHelper->expects($this->once())->method('getEncodedUrl')->willReturn('encoded-path'); - $this->urlBuilder->expects($this->once())->method('getUrl')->willReturn('path'); - $this->assertEquals( - [$this->store, [\Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => 'encoded-path']], - $this->unit->beforeGetTargetStorePostData($this->switcher, $this->store, []) - ); - } -} diff --git a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php index 49300448146f2..3097a016fbbe8 100644 --- a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php +++ b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php @@ -6,9 +6,10 @@ namespace Magento\UrlRewrite\Test\Unit\Controller; +use Magento\Framework\App\Action\Forward; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\UrlRewrite\Model\OptionProvider; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use Magento\Store\Model\Store; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -76,6 +77,75 @@ public function testNoRewriteExist() $this->assertNull($this->router->match($this->request)); } + public function testRewriteAfterStoreSwitcher() + { + $initialRequestPath = 'request-path'; + $newRequestPath = 'new-request-path'; + $oldStoreAlias = 'old-store'; + $oldStoreId = 'old-store-id'; + $currentStoreId = 'current-store-id'; + $rewriteEntityType = 'entity-type'; + $rewriteEntityId = 42; + $this->request + ->expects($this->any()) + ->method('getParam') + ->with('___from_store') + ->willReturn($oldStoreAlias); + $this->request + ->expects($this->any()) + ->method('getPathInfo') + ->willReturn($initialRequestPath); + $oldStore = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->getMock(); + $oldStore->expects($this->any()) + ->method('getId') + ->willReturn($oldStoreId); + $this->store + ->expects($this->any()) + ->method('getId') + ->willReturn($currentStoreId); + $this->storeManager + ->expects($this->any()) + ->method('getStore') + ->willReturnMap([[$oldStoreAlias, $oldStore], [null, $this->store]]); + $oldUrlRewrite = $this->getMockBuilder(UrlRewrite::class) + ->disableOriginalConstructor() + ->getMock(); + $oldUrlRewrite->expects($this->any()) + ->method('getEntityType') + ->willReturn($rewriteEntityType); + $oldUrlRewrite->expects($this->any()) + ->method('getEntityId') + ->willReturn($rewriteEntityId); + $oldUrlRewrite->expects($this->any()) + ->method('getRedirectType') + ->willReturn(0); + $urlRewrite = $this->getMockBuilder(UrlRewrite::class) + ->disableOriginalConstructor() + ->getMock(); + $urlRewrite->expects($this->any()) + ->method('getRequestPath') + ->willReturn($newRequestPath); + $this->urlFinder + ->expects($this->any()) + ->method('findOneByData') + ->willReturnMap([ + [ + [ + UrlRewrite::REQUEST_PATH => $initialRequestPath, + UrlRewrite::STORE_ID => $currentStoreId, + ], + $urlRewrite, + ] + ]); + $this->actionFactory + ->expects($this->once()) + ->method('create') + ->with(Forward::class); + $this->router->match($this->request); + } + public function testNoRewriteAfterStoreSwitcherWhenNoOldRewrite() { $this->request->expects($this->any())->method('getPathInfo')->will($this->returnValue('request-path')); diff --git a/app/code/Magento/UrlRewrite/etc/di.xml b/app/code/Magento/UrlRewrite/etc/di.xml index dfeb5d026d508..26055efbd2ba8 100644 --- a/app/code/Magento/UrlRewrite/etc/di.xml +++ b/app/code/Magento/UrlRewrite/etc/di.xml @@ -9,4 +9,11 @@ <preference for="Magento\UrlRewrite\Model\StorageInterface" type="Magento\UrlRewrite\Model\Storage\DbStorage"/> <preference for="Magento\UrlRewrite\Model\UrlFinderInterface" type="Magento\UrlRewrite\Model\Storage\DbStorage"/> <preference for="Magento\UrlRewrite\Model\UrlPersistInterface" type="Magento\UrlRewrite\Model\Storage\DbStorage"/> + <type name="Magento\Store\Model\StoreSwitcher"> + <arguments> + <argument name="storeSwitchers" xsi:type="array"> + <item name="rewriteUrl" xsi:type="object">Magento\UrlRewrite\Model\StoreSwitcher\RewriteUrl</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/UrlRewrite/etc/frontend/di.xml b/app/code/Magento/UrlRewrite/etc/frontend/di.xml index d46bb1c5d14a2..bc5b6aa767fa8 100644 --- a/app/code/Magento/UrlRewrite/etc/frontend/di.xml +++ b/app/code/Magento/UrlRewrite/etc/frontend/di.xml @@ -17,7 +17,4 @@ </argument> </arguments> </type> - <type name="Magento\Store\Block\Switcher"> - <plugin name="setStoreSpecificRedirectUrl" type="Magento\UrlRewrite\Block\Plugin\Store\Switcher\SetRedirectUrl"/> - </type> </config> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php index cc04e48adb620..264e5d993ad2d 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php @@ -63,7 +63,7 @@ public function testIndexActionAddProducts() $product = $this->productRepository->get('simple_product_2'); $this->dispatch('catalog/product_compare/index/items/' . $product->getEntityId()); - $this->assertRedirect($this->equalTo('http://localhost/index.php/catalog/product_compare/index/')); + $this->assertRedirect($this->stringStartsWith('http://localhost/index.php/catalog/product_compare/index/')); $this->_assertCompareListEquals([$product->getEntityId()]); } diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Store/Block/SwitcherTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Store/Block/SwitcherTest.php deleted file mode 100644 index 5b3879b592245..0000000000000 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Store/Block/SwitcherTest.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogUrlRewrite\Plugin\Store\Block; - -/** - * Integration tests for Magento\CatalogUrlRewrite\Plugin\Store\Block\Switcher block. - */ -class SwitcherTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\TestFramework\ObjectManager - */ - private $objectManager; - - /** - * @var \Magento\Store\Block\Switcher - */ - private $model; - - /** - * @var \Magento\Store\Api\StoreRepositoryInterface - */ - private $storeRepository; - - /** - * {@inheritdoc} - */ - protected function setUp() - { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->model = $this->objectManager->create(\Magento\Store\Block\Switcher::class); - $this->storeRepository = $this->objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); - } - - /** - * Test that after switching from Store 1 to Store 2 with another root Category user gets correct store url. - * - * @magentoDataFixture Magento/Store/_files/store.php - * @magentoDataFixture Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php - * @magentoAppArea frontend - * @return void - */ - public function testGetTargetStorePostData(): void - { - $storeCode = 'test'; - $store = $this->storeRepository->get($storeCode); - $result = json_decode($this->model->getTargetStorePostData($store), true); - - $this->assertContains($storeCode, $result['action']); - } -} diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php deleted file mode 100644 index f77413e18f472..0000000000000 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -$defaultCategory = $objectManager->create(\Magento\Catalog\Helper\DefaultCategory::class); -/** @var \Magento\Catalog\Model\Category $category */ -$category = $objectManager->create(\Magento\Catalog\Model\Category::class); -$category->isObjectNew(true); -$category->setCreatedAt('2014-06-23 09:50:07') - ->setName('Category 1') - ->setParentId($defaultCategory->getId()) - ->setPath('1/2/3') - ->setLevel(2) - ->setAvailableSortBy('name') - ->setDefaultSortBy('name') - ->setIsActive(true) - ->setPosition(1) - ->setAvailableSortBy(['position']) - ->save(); - -/** @var \Magento\Store\Model\Store $store */ -$store = $objectManager->create(\Magento\Store\Model\Store::class); - -$category->setStoreId($store->load('default')->getId()) - ->setName('category-default-store') - ->setUrlKey('category-default-store') - ->save(); - -$rootCategoryForTestStoreGroup = $objectManager->create(\Magento\Catalog\Model\Category::class); -$rootCategoryForTestStoreGroup->isObjectNew(true); -$rootCategoryForTestStoreGroup->setCreatedAt('2014-06-23 09:50:07') - ->setName('Category 2') - ->setParentId(\Magento\Catalog\Model\Category::TREE_ROOT_ID) - ->setPath('1/2/334') - ->setLevel(2) - ->setAvailableSortBy('name') - ->setDefaultSortBy('name') - ->setIsActive(true) - ->setPosition(1) - ->setAvailableSortBy(['position']) - ->save(); - -$rootCategoryForTestStoreGroup->setStoreId($store->load('test')->getId()) - ->setName('category-test-store') - ->setUrlKey('category-test-store') - ->save(); - -$storeCode = 'test'; -/** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ -$storeRepository = $objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); -/** @var \Magento\Store\Api\Data\StoreInterface $store */ -$store = $storeRepository->get($storeCode); - -/** @var \Magento\Store\Model\Group $storeGroup */ -$storeGroup = $objectManager->create(\Magento\Store\Model\Group::class) - ->setWebsiteId('1') - ->setCode('test_store_group') - ->setName('Test Store Group') - ->setRootCategoryId($rootCategoryForTestStoreGroup->getId()) - ->setDefaultStoreId($store->getId()) - ->save(); - -$store->setGroupId($storeGroup->getId())->save(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups_rollback.php deleted file mode 100644 index 9592e9d0e69b4..0000000000000 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups_rollback.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\Registry; -use Magento\Store\Model\Group; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Catalog\Api\CategoryListInterface; -use Magento\Catalog\Api\CategoryRepositoryInterface; -use Magento\Framework\Api\SearchCriteriaBuilder; - -$objectManager = Bootstrap::getObjectManager(); - -/** @var Registry $registry */ -$registry = $objectManager->get(Registry::class); -$registry->unregister('isSecureArea'); -$registry->register('isSecureArea', true); -// Delete first category -/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ -$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); -$searchCriteria = $searchCriteriaBuilder->addFilter('name', 'Category 1')->create(); -/** @var CategoryListInterface $categoryList */ -$categoryList = $objectManager->get(CategoryListInterface::class); -$categories = $categoryList->getList($searchCriteria)->getItems(); -/** @var CategoryRepositoryInterface $categoryRepository */ -$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); -foreach ($categories as $category) { - $categoryRepository->delete($category); -} -// Delete second category -$searchCriteria = $searchCriteriaBuilder->addFilter('name', 'Category 2')->create(); -$categories = $categoryList->getList($searchCriteria)->getItems(); -foreach ($categories as $category) { - $categoryRepository->delete($category); -} -// Delete store group -/** @var Group $store */ -$storeGroup = $objectManager->get(Group::class); -$storeGroup->load('test_store_group', 'code'); -if ($storeGroup->getId()) { - $storeGroup->delete(); -} -$registry->unregister('isSecureArea'); -$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index 1cbdbd128bbf4..181275904c5cc 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -522,7 +522,7 @@ public function testEditPostAction() $this->dispatch('customer/account/editPost'); - $this->assertRedirect($this->stringEndsWith('customer/account/')); + $this->assertRedirect($this->stringContains('customer/account/')); $this->assertSessionMessages( $this->equalTo(['You saved the account information.']), MessageInterface::TYPE_SUCCESS @@ -570,7 +570,7 @@ public function testChangePasswordEditPostAction() $this->dispatch('customer/account/editPost'); - $this->assertRedirect($this->stringEndsWith('customer/account/')); + $this->assertRedirect($this->stringContains('customer/account/')); $this->assertSessionMessages( $this->equalTo(['You saved the account information.']), MessageInterface::TYPE_SUCCESS @@ -603,7 +603,7 @@ public function testMissingDataEditPostAction() $this->dispatch('customer/account/editPost'); - $this->assertRedirect($this->stringEndsWith('customer/account/edit/')); + $this->assertRedirect($this->stringContains('customer/account/edit/')); $this->assertSessionMessages( $this->equalTo(['"Email" is not a valid email address.']), MessageInterface::TYPE_ERROR @@ -633,7 +633,7 @@ public function testWrongPasswordEditPostAction() $this->dispatch('customer/account/editPost'); - $this->assertRedirect($this->stringEndsWith('customer/account/edit/')); + $this->assertRedirect($this->stringContains('customer/account/edit/')); // Not sure if its the most secure message. Not changing the behavior for now in the new AccountManagement APIs. $this->assertSessionMessages( $this->equalTo(["The password doesn't match this account. Verify the password and try again."]), @@ -662,7 +662,7 @@ public function testWrongConfirmationEditPostAction() $this->dispatch('customer/account/editPost'); - $this->assertRedirect($this->stringEndsWith('customer/account/edit/')); + $this->assertRedirect($this->stringContains('customer/account/edit/')); $this->assertSessionMessages( $this->equalTo(['Password confirmation doesn\'t match entered password.']), MessageInterface::TYPE_ERROR diff --git a/dev/tests/integration/testsuite/Magento/Store/Block/SwitcherTest.php b/dev/tests/integration/testsuite/Magento/Store/Block/SwitcherTest.php index b3707fc605017..9d1f584bfcd77 100644 --- a/dev/tests/integration/testsuite/Magento/Store/Block/SwitcherTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/Block/SwitcherTest.php @@ -5,6 +5,11 @@ */ namespace Magento\Store\Block; +use Magento\Framework\App\ActionInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Url\DecoderInterface; +use Magento\Framework\App\ScopeInterface; + /** * Integration tests for \Magento\Store\Block\Switcher block. */ @@ -15,6 +20,11 @@ class SwitcherTest extends \PHPUnit\Framework\TestCase */ private $_objectManager; + /** + * @var DecoderInterface + */ + private $decoder; + /** * Set up. * @@ -22,11 +32,12 @@ class SwitcherTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->_objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->_objectManager = Bootstrap::getObjectManager(); + $this->decoder = Bootstrap::getObjectManager()->create(DecoderInterface::class); } /** - * Test that GetTargetStorePostData() method return correct store URL. + * Test that GetTargetStorePostData() method returns correct data. * * @magentoDataFixture Magento/Store/_files/store.php * @return void @@ -39,8 +50,16 @@ public function testGetTargetStorePostData() /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ $storeRepository = $this->_objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); $store = $storeRepository->get($storeCode); + $result = json_decode($block->getTargetStorePostData($store), true); - - $this->assertContains($storeCode, $result['action']); + $url = parse_url($this->decoder->decode($result['data'][ActionInterface::PARAM_NAME_URL_ENCODED])); + $storeParsedQuery = []; + if (isset($url['query'])) { + parse_str($url['query'], $storeParsedQuery); + } + + $this->assertSame($storeCode, $result['data']['___store']); + $this->assertSame($storeCode, $storeParsedQuery['___store']); + $this->assertSame(ScopeInterface::SCOPE_DEFAULT, $result['data']['___from_store']); } } diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/StoreSwitcherTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/StoreSwitcherTest.php new file mode 100644 index 0000000000000..069505819da4f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/Model/StoreSwitcherTest.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Store\Model; + +use Magento\Framework\ObjectManagerInterface as ObjectManager; +use Magento\TestFramework\Helper\Bootstrap; + +class StoreSwitcherTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var StoreSwitcher + */ + private $storeSwitcher; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * Class dependencies initialization + * + * @return void + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->storeSwitcher = $this->objectManager->get(StoreSwitcher::class); + } + + /** + * @magentoDataFixture Magento/Store/_files/store.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @return void + * @throws StoreSwitcher\CannotSwitchStoreException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testSwitch() + { + $redirectUrl = "http://domain.com/?SID=e5h3e086dce3ckkqt9ia7avl27&___store=fixture_second_store"; + $expectedUrl = "http://domain.com/"; + $fromStoreCode = 'test'; + /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ + $storeRepository = $this->objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); + $fromStore = $storeRepository->get($fromStoreCode); + + $toStoreCode = 'fixture_second_store'; + /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ + $storeRepository = $this->objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); + $toStore = $storeRepository->get($toStoreCode); + + $this->assertEquals($expectedUrl, $this->storeSwitcher->switch($fromStore, $toStore, $redirectUrl)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php index 1ebbb35dd7dc4..21e029d5aab81 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php @@ -69,12 +69,6 @@ public function requestDataProvider() 'request' => '/page-similar/', 'redirect' => '/page-b', ], - 'Use Case #7: Rewrite during stores switching' => [ - 'request' => '/page-c-on-2nd-store' - . '?___store=default&___from_store=fixture_second_store', - 'redirect' => '/page-c-on-1st-store', - 'expectedCode' => 302 - ], ]; } } diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php new file mode 100644 index 0000000000000..d23389718e2c2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\UrlRewrite\Model\StoreSwitcher; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\App\Config\Value; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreSwitcher; +use Magento\Framework\ObjectManagerInterface as ObjectManager; +use Magento\TestFramework\Helper\Bootstrap; + +class RewriteUrlTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var StoreSwitcher + */ + private $storeSwitcher; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * Class dependencies initialization + * + * @return void + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->storeSwitcher = $this->objectManager->get(StoreSwitcher::class); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + } + + /** + * @magentoDataFixture Magento/UrlRewrite/_files/url_rewrite.php + * @magentoDataFixture Magento/Catalog/_files/category_product.php + * @return void + * @throws StoreSwitcher\CannotSwitchStoreException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testSwitchToNonExistingPage() + { + $fromStoreCode = 'default'; + /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ + $storeRepository = $this->objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); + $fromStore = $storeRepository->get($fromStoreCode); + + $toStoreCode = 'fixture_second_store'; + /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ + $storeRepository = $this->objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); + $toStore = $storeRepository->get($toStoreCode); + + $this->setBaseUrl($toStore); + + $product = $this->productRepository->get('simple333'); + + $redirectUrl = "http://domain.com/{$product->getUrlKey()}.html"; + $expectedUrl = $toStore->getBaseUrl(); + + $this->assertEquals($expectedUrl, $this->storeSwitcher->switch($fromStore, $toStore, $redirectUrl)); + } + + /** + * @magentoDataFixture Magento/UrlRewrite/_files/url_rewrite.php + * @return void + * @throws StoreSwitcher\CannotSwitchStoreException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testSwitchToExistingPage() + { + $fromStoreCode = 'default'; + /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ + $storeRepository = $this->objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); + $fromStore = $storeRepository->get($fromStoreCode); + + $toStoreCode = 'fixture_second_store'; + /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ + $storeRepository = $this->objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); + $toStore = $storeRepository->get($toStoreCode); + + $redirectUrl = $expectedUrl = "http://localhost/page-c"; + + $this->assertEquals($expectedUrl, $this->storeSwitcher->switch($fromStore, $toStore, $redirectUrl)); + } + + /** + * Set base url to store. + * + * @param StoreInterface $targetStore + * @return void + */ + private function setBaseUrl(StoreInterface $targetStore) + { + $configValue = $this->objectManager->create(Value::class); + $configValue->load('web/unsecure/base_url', 'path'); + $baseUrl = 'http://domain.com/'; + if (!$configValue->getPath()) { + $configValue->setPath('web/unsecure/base_url'); + } + $configValue->setValue($baseUrl); + $configValue->setScope(ScopeInterface::SCOPE_STORES); + $configValue->setScopeId($targetStore->getId()); + $configValue->save(); + + $reinitibleConfig = $this->objectManager->create(ReinitableConfigInterface::class); + $reinitibleConfig->reinit(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php new file mode 100644 index 0000000000000..6938cf9512de7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +/** @var \Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection $urlRewriteCollection */ +$urlRewriteCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection::class); +$collection = $urlRewriteCollection + ->addFieldToFilter('entity_type', 'custom') + ->addFieldToFilter('request_path', ['page-a', 'page-b', 'page-c']) + ->addFieldToFilter('entity_id', '333') + ->load() + ->walk('delete'); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From 6d92d49279d9d95dab4a7c90a9858863fa9ccff1 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Fri, 3 Aug 2018 11:55:30 +0300 Subject: [PATCH 0722/1171] MAGETWO-91771: Comma special character in cart price rule condition value results in incorrect rule fix unit tests --- lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php index 418c5e927f5e7..f6d7326f52764 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php @@ -53,6 +53,7 @@ protected function setUp() /** @var \Magento\Directory\Model\CurrencyFactory|\PHPUnit_Framework_MockObject_MockObject $currencyFactory */ $currencyFactory = $this->getMockBuilder(\Magento\Directory\Model\CurrencyFactory::class) + ->disableOriginalConstructor() ->getMock(); $this->formatModel = new \Magento\Framework\Locale\Format( @@ -87,7 +88,7 @@ public function getPriceFormatDataProvider() ['en_US', ['decimalSymbol' => '.', 'groupSymbol' => ',']], ['de_DE', ['decimalSymbol' => ',', 'groupSymbol' => '.']], ['de_CH', ['decimalSymbol' => '.', 'groupSymbol' => '\'']], - ['uk_UA', ['decimalSymbol' => ',', 'groupSymbol' => ' ']] + ['uk_UA', ['decimalSymbol' => ',', 'groupSymbol' => ' ']] ]; } From 494fa58be9c4be94abdd2b93df9a2fd2c6210961 Mon Sep 17 00:00:00 2001 From: Yuriy Tkachenko <y.tkachenko@atwix.com> Date: Wed, 13 Jun 2018 11:00:59 +0300 Subject: [PATCH 0723/1171] Fix false cache_lifetime usage in xml layouts --- lib/internal/Magento/Framework/View/Element/AbstractBlock.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php index 0ffa9dce7f730..b2f857bf29f45 100644 --- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php +++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php @@ -1074,7 +1074,7 @@ protected function getCacheLifetime() $cacheLifetime = $this->getData('cache_lifetime'); if (false === $cacheLifetime || null === $cacheLifetime) { - return $cacheLifetime; + return null; } return (int)$cacheLifetime; From 9d204cf265f7942ff3e08ec504efd4e70a1d346d Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Thu, 2 Aug 2018 19:17:06 +0400 Subject: [PATCH 0724/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Added automated test --- .../StorefrontAddProductToCardActionGroup.xml | 75 +++++++++++++++++++ .../StorefrontAddProductToCardSection.xml | 72 ++++++++++++++++++ .../AddingProductWithExpiredSessionTest.xml | 49 ++++++++++++ 3 files changed, 196 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml new file mode 100644 index 0000000000000..b38430391285d --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + + <!--Go To Products page--> + <actionGroup name="GoToProductPage"> + <click selector="{{GoToProductPageSection.catalog}}" stepKey="clickOnCatalog" /> + <waitForPageLoad stepKey="waitForPage"/> + <click selector="{{GoToProductPageSection.product}}" stepKey="clickToSelectProductsItem" /> + <waitForPageLoad stepKey="waitForPageProducts"/> + </actionGroup> + + <!--Create Simple product--> + <actionGroup name="AdminCreateSimpleProduct"> + <click selector="{{GoToProductPageSection.add}}" stepKey="clickToAddProduct"/> + <waitForPageLoad stepKey="WaitForProductPageIsLoaded"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProductOne.name}}" stepKey="setNameForProduct"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{SimpleProductOne.sku}}" stepKey="setSKUForProduct"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{SimpleProductOne.price}}" stepKey="setPriceForProduct"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{SimpleProductOne.quantity}}" stepKey="setQuantityForProduct"/> + <click selector="{{AdminProductFormSection.searchOptimization}}" stepKey="clickOnSearchEngineOptimization"/> + <fillField selector="{{AdminProductFormSection.urlKey}}" userInput="{{SimpleProductOne.urlKey}}" stepKey="setSearchUrlForProduct"/> + <click selector="{{AdminProductFormSection.saveButton}}" stepKey="clickSaveProduct"/> + <waitForPageLoad stepKey="WaitForProductSave"/> + <see userInput="You saved the product." stepKey="seeSaveConfirmation"/> + </actionGroup> + + + <actionGroup name="FindAndAddProductToCardActionGroup"> + <arguments> + <argument name="product" defaultValue="SimpleProductOne"/> + </arguments> + + <click selector="{{StorefrontAddProductToCartSection.addToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" time="30" stepKey="waitForProductAdded"/> + <click selector="{{StorefrontAddProductToCartSection.showCard}}" stepKey="clickToOpenCard"/> + <waitForPageLoad stepKey="WaitForFormOpened" time="3"/> + <click selector="{{StorefrontAddProductToCartSection.proceed}}" stepKey="clickToProceedToCheckout"/> + <waitForPageLoad time="5" stepKey="waitForTheFormIsOpened"/> + <see userInput="Shipping Address" stepKey="seeShippingAddress"/> + </actionGroup> + + + <actionGroup name="AgainGoToProductCategory"> + + <amOnPage url="/admin" stepKey="GoToDashboard"/> + + <click selector="{{GoToProductPageSection.catalog}}" stepKey="clickOnCatalog" /> + <waitForPageLoad stepKey="waitForPage"/> + <click selector="{{GoToProductPageSection.product}}" stepKey="clickToSelectProductsItem" /> + <waitForPageLoad stepKey="waitForPageProducts"/> + </actionGroup> + + <actionGroup name="DeleteCreatedProduct"> + + <click selector="{{DeleteCreatedProduct.createdProductID(SimpleProductOne.name)}}" stepKey="selectCreatedProduct"/> + <wait stepKey="waitSelectCreatedProduct" time="2"/> + <waitForElementVisible selector="{{DeleteCreatedProduct.actionSelectBox}}" stepKey="waitToSelectActionVisible" time="50"/> + <click stepKey="clickToSelectAction" selector="{{DeleteCreatedProduct.actionSelectBox}}"/> + <wait stepKey="waitClickToSelectAction" time="2"/> + <click selector="{{DeleteCreatedProduct.deleteButton}}" stepKey="clickToDeleteProduct"/> + <wait stepKey="waitClickToDeleteProduct" time="2"/> + <click selector="{{DeleteCreatedProduct.okButton}}" stepKey="clickToConfirm"/> + <wait stepKey="waitClickToConfirmButton" time="2"/> + <see userInput="A total of 1 record(s) have been deleted." stepKey="productDeletedSuccessfully"/> + + </actionGroup> + +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml new file mode 100644 index 0000000000000..3ae3e361d8264 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="StorefrontAddProductToCartSection"> + <element name="addToCartBtn" type="button" selector="button.action.tocart.primary"/> + <element name="successMsg" type="button" selector="div.message-success"/> + <element name="showCard" type="button" selector=".action.showcart"/> + <element name="proceed" type="button" selector="#top-cart-btn-checkout"/> + + </section> + + <section name="GoToProductPageSection"> + <!--Go to Catalog/Products--> + <element name="catalog" type="button" selector="#menu-magento-catalog-catalog"/> + <element name="product" type="button" selector="//span[contains(text(), 'Products')]"/> + <element name="add" type="button" selector="#add_new_product-button"/> + </section> + + <section name="AdminProductFormSection"> + <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> + <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> + <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> + <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> + <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> + <element name="productStatus" type="checkbox" selector="input[name='product[status]']"/> + <element name="enableProductLabel" type="checkbox" selector="input[name='product[status]']+label"/> + <element name="productStatusUseDefault" type="checkbox" selector="input[name='use_default[status]']"/> + <element name="productNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> + <element name="productPrice" type="input" selector=".admin__field[data-index=price] input"/> + <element name="productTaxClassUseDefault" type="checkbox" selector="input[name='use_default[tax_class_id]']"/> + <element name="advancedPricingLink" type="button" selector="button[data-index='advanced_pricing_button']"/> + <element name="categoriesDropdown" type="multiselect" selector="div[data-index='category_ids']"/> + <element name="productQuantity" type="input" selector=".admin__field[data-index=qty] input"/> + <element name="productStockStatus" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]']"/> + <element name="productWeight" type="input" selector=".admin__field[data-index=weight] input"/> + <element name="productWeightSelect" type="select" selector="select[name='product[product_has_weight]']"/> + <element name="contentTab" type="button" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Content']"/> + <element name="fieldError" type="text" selector="//input[@name='product[{{fieldName}}]']/following-sibling::label[@class='admin__field-error']" parameterized="true"/> + <element name="priceFieldError" type="text" selector="//input[@name='product[price]']/parent::div/parent::div/label[@class='admin__field-error']"/> + <element name="addAttributeBtn" type="button" selector="#addAttribute"/> + <element name="createNewAttributeBtn" type="button" selector="button[data-index='add_new_attribute_button']"/> + <element name="save" type="button" selector="#save"/> + <element name="attributeTab" type="button" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Attributes']"/> + <element name="attributeLabel" type="input" selector="//input[@name='frontend_label[0]']"/> + <element name="frontendInput" type="select" selector="select[name = 'frontend_input']"/> + <element name="productFormTab" type="button" selector="//strong[@class='admin__collapsible-title']/span[contains(text(), '{{tabName}}')]" parameterized="true"/> + <element name="productFormTabState" type="text" selector="//strong[@class='admin__collapsible-title']/span[contains(text(), '{{tabName}}')]/parent::*/parent::*[@data-state-collapsible='{{state}}']" parameterized="true"/> + <element name="visibility" type="select" selector="//select[@name='product[visibility]']"/> + <element name="visibilityUseDefault" type="checkbox" selector="//input[@name='use_default[visibility]']"/> + <element name="divByDataIndex" type="input" selector="div[data-index='{{var}}']" parameterized="true"/> + <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//span[text()='{{attributeLabel}}']" parameterized="true"/> + <element name="searchOptimization" type="button" selector="//*[contains(text(),'Search Engine Optimization')]"/> + <element name="urlKey" type="input" selector="//input[contains(@name,'url_key')]"/> + <element name="saveButton" type="button" selector="#save-button"/> + + </section> + + <section name="DeleteCreatedProduct"> + <element name="createdProductID" type="select" selector="//*[@id='container']//*[text()='{{arg1}}']/parent::td/parent::tr//label[contains(@class, 'data-grid-checkbox-cell-inner')]" parameterized="true"/> + <element name="actionSelectBox" type="button" selector="//*[@class='col-xs-2']"/> + <element name="deleteButton" type="button" selector="//*[@class='admin__data-grid-header-row row row-gutter']//*[text()='Delete']"/> + <element name="okButton" type="button" selector=".action-primary.action-accept"/> + </section> + +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml new file mode 100644 index 0000000000000..056fcb9f47dd1 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AddingProductWithExpiredSessionTest"> + <annotations> + <title value="Adding a product to cart from category page with an expired session"/> + <features value="Module/ Catalog"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-93289"/> + <stories value="MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added"/> + <group value="customer"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="GoToProductPage" stepKey="goToProductPage"/> + <actionGroup ref="AdminCreateSimpleProduct" stepKey="adminCreateSimpleProduct"/> + </before> + + <!--Navigate to a category page --> + <amOnPage url="/{{SimpleProductOne.name}}.html" stepKey="GoToProductPage"/> + + <waitForPageLoad stepKey="waitForPageLoad"/> + + <!-- Remove PHPSESSID and form_key to replicate an expired session--> + <executeJS function="var delete_cookie = function(name) { + document.cookie = name + '=;expires=Thu, 01 Jan 1970 00:00:01 UTC; path=/;';}; + delete_cookie('PHPSESSID'); + delete_cookie('form_key');" stepKey="removeCookies" after="waitForPageLoad"/> + + <!-- "Add to Cart" any product--> + <actionGroup ref="FindAndAddProductToCardActionGroup" stepKey="addProductToCard"/> + + <after> + <!--Delete created product--> + <actionGroup ref="AgainGoToProductCategory" stepKey="againGoToProductCategory"/> + <wait stepKey="waitForProductPageLoaded" time="3"/> + <actionGroup ref="DeleteCreatedProduct" stepKey="deleteCreatedProduct"/> + </after> + + </test> +</tests> From abae0e01db56bd1f1f15d152936a6d148eb9950a Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 3 Aug 2018 13:37:43 +0300 Subject: [PATCH 0725/1171] MAGETWO-93998: [Forwardport] Run Product Price reindex in multithread mode with Redis --- .../Framework/Session/SaveHandler/Redis.php | 122 +++++++++++++++++- 1 file changed, 115 insertions(+), 7 deletions(-) diff --git a/lib/internal/Magento/Framework/Session/SaveHandler/Redis.php b/lib/internal/Magento/Framework/Session/SaveHandler/Redis.php index 53af1847d1012..447523214cb78 100644 --- a/lib/internal/Magento/Framework/Session/SaveHandler/Redis.php +++ b/lib/internal/Magento/Framework/Session/SaveHandler/Redis.php @@ -14,13 +14,28 @@ use Magento\Framework\Filesystem; use Magento\Framework\App\Filesystem\DirectoryList; -class Redis extends \Cm\RedisSession\Handler +class Redis implements \SessionHandlerInterface { + /** + * @var ConfigInterface + */ + private $config; + + /** + * @var LoggerInterface + */ + private $logger; + /** * @var Filesystem */ private $filesystem; + /** + * @var \Cm\RedisSession\Handler[] + */ + private $connection; + /** * @param ConfigInterface $config * @param LoggerInterface $logger @@ -29,23 +44,116 @@ class Redis extends \Cm\RedisSession\Handler */ public function __construct(ConfigInterface $config, LoggerInterface $logger, Filesystem $filesystem) { + $this->config = $config; + $this->logger = $logger; $this->filesystem = $filesystem; - try { - parent::__construct($config, $logger); - } catch (ConnectionFailedException $e) { - throw new SessionException(new Phrase($e->getMessage())); + } + + /** + * Get connection + * + * @return \Cm\RedisSession\Handler + * @throws SessionException + */ + private function getConnection() + { + $pid = getmypid(); + if (!isset($this->connection[$pid])) { + try { + $this->connection[$pid] = new \Cm\RedisSession\Handler($this->config, $this->logger); + } catch (ConnectionFailedException $e) { + throw new SessionException(new Phrase($e->getMessage())); + } } + return $this->connection[$pid]; } /** - * {@inheritdoc} + * Open session + * + * @param string $savePath ignored + * @param string $sessionName ignored + * @return bool + * @throws SessionException + */ + public function open($savePath, $sessionName) + { + return $this->getConnection()->open($savePath, $sessionName); + } + + /** + * Fetch session data + * + * @param string $sessionId + * @return string + * @throws ConcurrentConnectionsExceededException + * @throws SessionException */ public function read($sessionId) { try { - return parent::read($sessionId); + return $this->getConnection()->read($sessionId); } catch (ConcurrentConnectionsExceededException $e) { require $this->filesystem->getDirectoryRead(DirectoryList::PUB)->getAbsolutePath('errors/503.php'); } } + + /** + * Update session + * + * @param string $sessionId + * @param string $sessionData + * @return boolean + * @throws SessionException + */ + public function write($sessionId, $sessionData) + { + return $this->getConnection()->write($sessionId, $sessionData); + } + + /** + * Destroy session + * + * @param string $sessionId + * @return boolean + * @throws SessionException + */ + public function destroy($sessionId) + { + return $this->getConnection()->destroy($sessionId); + } + + /** + * Overridden to prevent calling getLifeTime at shutdown + * + * @return bool + * @throws SessionException + */ + public function close() + { + return $this->getConnection()->close(); + } + + /** + * Garbage collection + * + * @param int $maxLifeTime ignored + * @return boolean + * @throws SessionException + */ + public function gc($maxLifeTime) + { + return $this->getConnection()->gc($maxLifeTime); + } + + /** + * Get the number of failed lock attempts + * + * @return int + * @throws SessionException + */ + public function getFailedLockAttempts() + { + return $this->getConnection()->getFailedLockAttempts(); + } } From 62520aff20f28fbc3a21844ea897dd37e051e048 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Fri, 3 Aug 2018 13:57:49 +0300 Subject: [PATCH 0726/1171] MAGETWO-93715: [2.3] Delete action in grid could be sent multiple times --- .../Customer/Controller/Adminhtml/Index/MassDeleteTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php index 2a916c1c00c66..940f7c84325f6 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php @@ -125,7 +125,7 @@ public function failedRequestDataProvider(): array return [ [ 'ids' => [], - 'constraint' => self::equalTo(['Please select item(s).']), + 'constraint' => self::equalTo(['An item needs to be selected. Select and try again.']), 'messageType' => MessageInterface::TYPE_ERROR, ], [ @@ -135,7 +135,7 @@ public function failedRequestDataProvider(): array ], [ 'ids' => null, - 'constraint' => self::equalTo(['Please select item(s).']), + 'constraint' => self::equalTo(['An item needs to be selected. Select and try again.']), 'messageType' => MessageInterface::TYPE_ERROR, ] ]; From abc6d0a76fa3988a9bf5f7df59d274ece533b054 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Fri, 3 Aug 2018 14:37:37 +0300 Subject: [PATCH 0727/1171] MAGETWO-91808: Categories of the Main menu in the different Store View not updated when varnish enabled --- app/code/Magento/Store/Controller/Store/Redirect.php | 2 +- app/code/Magento/Store/Controller/Store/SwitchAction.php | 2 +- app/code/Magento/Store/Model/StoreSwitcher.php | 1 + .../Model/StoreSwitcher/CannotSwitchStoreException.php | 1 + .../Magento/Store/Model/StoreSwitcher/CleanTargetUrl.php | 1 + .../Store/Model/StoreSwitcher/ManagePrivateContent.php | 1 + .../Store/Model/StoreSwitcher/ManageStoreCookie.php | 1 + app/code/Magento/Store/Model/StoreSwitcherInterface.php | 1 + .../Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php | 1 + .../testsuite/Magento/Store/Model/StoreSwitcherTest.php | 4 +++- .../UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php | 8 +++++--- .../Magento/UrlRewrite/_files/url_rewrite_rollback.php | 1 + 12 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Store/Controller/Store/Redirect.php b/app/code/Magento/Store/Controller/Store/Redirect.php index 04714b17a0cc0..21692e9d6dd1e 100644 --- a/app/code/Magento/Store/Controller/Store/Redirect.php +++ b/app/code/Magento/Store/Controller/Store/Redirect.php @@ -1,9 +1,9 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Store\Controller\Store; diff --git a/app/code/Magento/Store/Controller/Store/SwitchAction.php b/app/code/Magento/Store/Controller/Store/SwitchAction.php index 4727872834161..df946b45d60e5 100644 --- a/app/code/Magento/Store/Controller/Store/SwitchAction.php +++ b/app/code/Magento/Store/Controller/Store/SwitchAction.php @@ -77,7 +77,7 @@ public function __construct( $this->storeRepository = $storeRepository; $this->storeManager = $storeManager; $this->messageManager = $context->getMessageManager(); - $this->storeSwitcher = $storeSwitcher ?: ObjectManager::getInstance()->get(StoreSwitcher::class); + $this->storeSwitcher = $storeSwitcher ?: ObjectManager::getInstance()->get(StoreSwitcherInterface::class); } /** diff --git a/app/code/Magento/Store/Model/StoreSwitcher.php b/app/code/Magento/Store/Model/StoreSwitcher.php index 476196ad84f8a..a294b6a0d6b0a 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher.php +++ b/app/code/Magento/Store/Model/StoreSwitcher.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Store\Model; diff --git a/app/code/Magento/Store/Model/StoreSwitcher/CannotSwitchStoreException.php b/app/code/Magento/Store/Model/StoreSwitcher/CannotSwitchStoreException.php index b19ae419e2f07..c6afd71cb6f01 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/CannotSwitchStoreException.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/CannotSwitchStoreException.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Store\Model\StoreSwitcher; diff --git a/app/code/Magento/Store/Model/StoreSwitcher/CleanTargetUrl.php b/app/code/Magento/Store/Model/StoreSwitcher/CleanTargetUrl.php index 31b6eb62fb9e1..3328a21e8f5e1 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/CleanTargetUrl.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/CleanTargetUrl.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Store\Model\StoreSwitcher; diff --git a/app/code/Magento/Store/Model/StoreSwitcher/ManagePrivateContent.php b/app/code/Magento/Store/Model/StoreSwitcher/ManagePrivateContent.php index 4ef5aad5d7988..8aed785641efe 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/ManagePrivateContent.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/ManagePrivateContent.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Store\Model\StoreSwitcher; diff --git a/app/code/Magento/Store/Model/StoreSwitcher/ManageStoreCookie.php b/app/code/Magento/Store/Model/StoreSwitcher/ManageStoreCookie.php index 23fda163e9662..7a485be090af6 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/ManageStoreCookie.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/ManageStoreCookie.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Store\Model\StoreSwitcher; diff --git a/app/code/Magento/Store/Model/StoreSwitcherInterface.php b/app/code/Magento/Store/Model/StoreSwitcherInterface.php index b6c5c38d23ef6..ee6fb0b4763c6 100644 --- a/app/code/Magento/Store/Model/StoreSwitcherInterface.php +++ b/app/code/Magento/Store/Model/StoreSwitcherInterface.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Store\Model; diff --git a/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php b/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php index 2b6f9e87e3de2..2ce00d53588b3 100644 --- a/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php +++ b/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\UrlRewrite\Model\StoreSwitcher; diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/StoreSwitcherTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/StoreSwitcherTest.php index 069505819da4f..0e4d129d17aa6 100644 --- a/dev/tests/integration/testsuite/Magento/Store/Model/StoreSwitcherTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/Model/StoreSwitcherTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Store\Model; use Magento\Framework\ObjectManagerInterface as ObjectManager; @@ -38,7 +40,7 @@ protected function setUp() * @throws StoreSwitcher\CannotSwitchStoreException * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testSwitch() + public function testSwitch(): void { $redirectUrl = "http://domain.com/?SID=e5h3e086dce3ckkqt9ia7avl27&___store=fixture_second_store"; $expectedUrl = "http://domain.com/"; diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php index d23389718e2c2..2cb86358667c0 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\UrlRewrite\Model\StoreSwitcher; use Magento\Catalog\Api\ProductRepositoryInterface; @@ -50,7 +52,7 @@ protected function setUp() * @throws StoreSwitcher\CannotSwitchStoreException * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testSwitchToNonExistingPage() + public function testSwitchToNonExistingPage(): void { $fromStoreCode = 'default'; /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ @@ -78,7 +80,7 @@ public function testSwitchToNonExistingPage() * @throws StoreSwitcher\CannotSwitchStoreException * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testSwitchToExistingPage() + public function testSwitchToExistingPage(): void { $fromStoreCode = 'default'; /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ @@ -101,7 +103,7 @@ public function testSwitchToExistingPage() * @param StoreInterface $targetStore * @return void */ - private function setBaseUrl(StoreInterface $targetStore) + private function setBaseUrl(StoreInterface $targetStore): void { $configValue = $this->objectManager->create(Value::class); $configValue->load('web/unsecure/base_url', 'path'); diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php index 6938cf9512de7..19cce769b1c19 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); /** @var \Magento\Framework\Registry $registry */ $registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); From 6a63d067252aafcab0938c7509ae91ffb63f0124 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel.ruf@ueberbit.de> Date: Fri, 3 Aug 2018 09:15:59 +0200 Subject: [PATCH 0728/1171] chore: remove support for IE 9 and IE 10 --- .../web/css/source/module/_menu.less | 1 - .../web/css/source/module/pages/_login.less | 4 - .../web/css/source/_module-old.less | 4 - .../web/css/source/_module.less | 10 -- .../css/source/module/_staging-preview.less | 6 - .../web/css/source/module/_data-grid.less | 16 --- .../_data-grid-action-bookmarks.less | 4 - .../app/setup/styles/less/lib/_buttons.less | 114 ------------------ .../setup/styles/less/lib/forms/_selects.less | 13 -- .../backend/web/css/source/_actions.less | 37 ------ .../backend/web/css/source/_reset.less | 2 - .../web/css/source/components/_popups.less | 22 ---- .../web/css/source/components/_spinner.less | 8 -- .../web/css/source/forms/_controls.less | 37 ------ .../Magento/backend/web/css/styles-old.less | 111 ----------------- .../backend/web/mui/styles/_table.less | 10 -- .../Magento_Theme/web/css/source/_module.less | 1 - .../Magento/blank/web/css/source/_layout.less | 4 - .../Magento_Theme/web/css/source/_module.less | 14 --- .../Magento/luma/web/css/source/_forms.less | 5 - dev/tools/grunt/configs/autoprefixer.json | 2 +- lib/web/css/source/components/_modals.less | 8 +- lib/web/mage/gallery/gallery.less | 8 -- lib/web/mage/ie-class-fixer.js | 8 +- setup/view/layout/layout.phtml | 2 - 25 files changed, 4 insertions(+), 447 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less index 8825f4ea3f5a2..7e58d3bf44b0b 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less @@ -263,7 +263,6 @@ visibility: hidden; z-index: @submenu__z-index - 1; - .ie10 &, .ie11 & { height: 100%; } diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_login.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_login.less index 97ed1d8cdb1ec..2852fc1095576 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_login.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_login.less @@ -49,10 +49,6 @@ position: relative; width: 100%; z-index: 1; - - .ie9 & { - margin-top: 10%; - } } :-ms-input-placeholder { diff --git a/app/design/adminhtml/Magento/backend/Magento_GiftRegistry/web/css/source/_module-old.less b/app/design/adminhtml/Magento/backend/Magento_GiftRegistry/web/css/source/_module-old.less index 6b0d73299d903..3e1a1b9cf9be9 100644 --- a/app/design/adminhtml/Magento/backend/Magento_GiftRegistry/web/css/source/_module-old.less +++ b/app/design/adminhtml/Magento/backend/Magento_GiftRegistry/web/css/source/_module-old.less @@ -7,7 +7,3 @@ // Stores -> Gift Registry // --------------------------------------------- -.eq-ie9 [class^=' adminhtml-giftregistry-'] .custom-options .data-table { - table-layout: auto; - word-wrap: normal; -} diff --git a/app/design/adminhtml/Magento/backend/Magento_Marketplace/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Marketplace/web/css/source/_module.less index d4f918567e579..92a08eb7909c4 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Marketplace/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Marketplace/web/css/source/_module.less @@ -15,22 +15,12 @@ .lib-vendor-prefix-display(flex); .lib-vendor-prefix-flex-direction(row); .lib-vendor-prefix-flex-wrap(wrap); - - .ie9 & { - word-spacing: -.45rem; - } } .partner { margin-bottom: @indent__xl; padding: 0 @indent__m; width: 100% / 3; - - .ie9 & { - display: inline-block; - vertical-align: top; - word-spacing: normal; - } } } diff --git a/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less b/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less index a71731320c5ce..8dc22fd8e206b 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less +++ b/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less @@ -730,15 +730,9 @@ top: @indent__l; width: 1px; - .ie9 &, - .ie10 &, .ie11 & { height: 1000px; } - - .ie9 & { - border-right: 1px dashed @staging-preview-table-lighten__border-color; - } } } } diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less index 065f870dbfe01..d55608ade4a05 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less @@ -59,18 +59,6 @@ margin-top: -(@data-grid-spinner__size / 2); position: absolute; top: 50%; - - .ie9 & { - background: url('@{baseDir}images/loader-2.gif') 50% 50% no-repeat; - bottom: 0; - height: 149px; - left: 0; - margin: auto; - position: absolute; - right: 0; - top: 0; - width: 218px; - } } } @@ -999,10 +987,6 @@ body._in-resize { transform: rotate(45deg); width: 1.6rem; z-index: 3; - - .ie9 & { - display: none; - } } } } diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-action-bookmarks.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-action-bookmarks.less index dfaf340f90b6c..f9c93dce57002 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-action-bookmarks.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-action-bookmarks.less @@ -132,10 +132,6 @@ font-size: @action-dropdown-menu__font-size; min-width: 15rem; width: ~'calc(100% - 4rem)'; - - .ie9 & { - width: 15rem; - } } .action-dropdown-menu-item-actions { diff --git a/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/_buttons.less b/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/_buttons.less index ec7509cba6dda..2c60af8dfa178 100644 --- a/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/_buttons.less +++ b/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/_buttons.less @@ -73,12 +73,6 @@ cursor: default; opacity: @disabled__opacity; pointer-events: none; - - .ie9 & { - background-color: @btn__base__disabled__background-color; - opacity: 1; - text-shadow: none; - } } } @@ -135,22 +129,6 @@ ); color: @btn-prime__color; } - - // Disabled state for IE9 - &[disabled], - &.disabled { - .ie9 & { - background-color: @btn-prime__disabled__background-color; - } - - &:hover, - &:active { - .ie9 & { - background-color: @btn-prime__disabled__background-color; - filter: none; - } - } - } } .btn-secondary { @@ -167,21 +145,6 @@ background-color: @btn-secondary__active__background-color; color: @btn-secondary__color; } - - // Disabled state for IE9 - &[disabled], - &.disabled { - .ie9 & { - background-color: @btn-secondary__disabled__background-color; - } - - &:active { - .ie9 & { - background-color: @btn-secondary__disabled__background-color; - filter: none; - } - } - } } // @@ -239,26 +202,6 @@ left: 1px; } } - - // Disabled state for IE9 - &[disabled], - &.disabled { - &:after { - .ie9 & { - border-color: transparent transparent transparent @btn__base__disabled__background-color; - } - } - - &:focus, - &:hover, - &:active { - &:after { - .ie9 & { - border-left-color: @btn__base__disabled__background-color; - } - } - } - } } .btn-prime { @@ -285,25 +228,6 @@ left: 1px; } } - - // Disabled state for IE9 - &[disabled], - &.disabled { - &:after { - .ie9 & { - border-color: transparent transparent transparent @btn-prime__disabled__background-color; - } - } - - &:hover, - &:active { - &:after { - .ie9 & { - border-left-color: @btn-prime__disabled__background-color; - } - } - } - } } } @@ -342,25 +266,6 @@ right: 1px; } } - - // Disabled state for IE9 - &[disabled], - &.disabled { - &:after { - .ie9 & { - border-color: transparent @btn__base__disabled__background-color transparent transparent; - } - } - - &:hover, - &:active { - &:after { - .ie9 & { - border-right-color: @btn__base__disabled__background-color; - } - } - } - } } .btn-prime { @@ -387,25 +292,6 @@ right: 1px; } } - - // Disabled state for IE9 - &[disabled], - &.disabled { - &:after { - .ie9 & { - border-color: transparent @btn-prime__disabled__background-color transparent transparent; - } - } - - &:hover, - &:active { - &:after { - .ie9 & { - border-right-color: @btn-prime__disabled__background-color; - } - } - } - } } } diff --git a/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/forms/_selects.less b/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/forms/_selects.less index 7e5d34e48892a..76973802a3d2e 100644 --- a/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/forms/_selects.less +++ b/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/forms/_selects.less @@ -47,10 +47,6 @@ top: 0; width: @select-check__size; z-index: -2; - - .ie9 & { - display: none; - } } &:before { @@ -66,10 +62,6 @@ top: 50%; width: 0; z-index: -1; - - .ie9 & { - display: none; - } } .form-el-select { @@ -82,11 +74,6 @@ padding: @form-el__padding-top ~'calc(@{select-check__size} + 10%)' @form-el__padding-bottom @form-el__padding-side; width: 110%; - .ie9 & { - padding-right: @form-el__padding-side; - width: 100%; - } - &::-ms-expand { display: none; } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/_actions.less b/app/design/adminhtml/Magento/backend/web/css/source/_actions.less index 4377687fd3d39..4da070f84d666 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/_actions.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/_actions.less @@ -142,16 +142,6 @@ box-shadow: @button__hover__box-shadow; } } - - &[disabled], - &.disabled { - .ie9 &, - .ie10 & { - background-color: @button-triangle__base__disabled__background-color; - opacity: 1; - text-shadow: none; - } - } } } @@ -182,17 +172,6 @@ border-left-color: @button__hover__background-color; } } - - // Disabled state for IE9, IE10 - &.disabled, - &[disabled] { - &:after { - .ie9 &, - .ie10 & { - border-color: transparent transparent transparent @button-triangle__base__disabled__background-color; - } - } - } } .action-primary { @@ -238,17 +217,6 @@ border-right-color: @button__hover__background-color; } } - - // Disabled state for IE9, IE10 - &.disabled, - &[disabled] { - &:after { - .ie9 &, - .ie10 & { - border-color: transparent @button-triangle__base__disabled__background-color transparent transparent; - } - } - } } .action-primary { @@ -495,11 +463,6 @@ button { position: absolute; right: auto; top: auto; - - .ie9 & { - margin-left: 99%; - margin-top: -3.5rem; - } } a { diff --git a/app/design/adminhtml/Magento/backend/web/css/source/_reset.less b/app/design/adminhtml/Magento/backend/web/css/source/_reset.less index 3a2847c82b8ce..b1b1f6c005572 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/_reset.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/_reset.less @@ -87,8 +87,6 @@ template { // --------------------------------------------- a { - background-color: transparent; // Remove the gray background color from active links in IE 10. - // Improve readability when focused and also mouse hovered in all browsers. &:active, &:hover { diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less index 7bd10ad491f0c..2ef228ef6f570 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less @@ -462,25 +462,3 @@ } } } - -.ie9 { - .catalog-product-attribute-edit { - &.attribute-popup { - min-width: 0; - - .menu-wrapper { - display: none; - } - - .page-actions { - button { - float: none; - } - - .primary { - float: right; - } - } - } - } -} diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_spinner.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_spinner.less index c9e56a29f275f..f6f61c1efae91 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_spinner.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_spinner.less @@ -33,14 +33,6 @@ position: absolute; width: 1em; } - - .ie9 & { - background: url('@{baseDir}images/ajax-loader.gif') no-repeat center; - - > span { - display: none; - } - } } // ToDo UI: remove old loaders style while loaders redesign diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less index 5f1cee13b5b88..c28d3d045aa57 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less @@ -100,11 +100,6 @@ &::-ms-expand { display: none; } - - .ie9 & { - background-image: none; - padding-right: @field-control__padding-horizontal; - } } // ToDo UI: add month and date styles @@ -336,35 +331,3 @@ option:empty { .admin__addon-prefix { .lib-vendor-prefix-order(0); } - -.ie9 { - .admin__control-addon { - &:after { - clear: both; - content: ''; - display: block; - height: 0; - overflow: hidden; - } - } - - .admin__addon { - min-width: 0; - overflow: hidden; - text-align: right; - white-space: nowrap; - width: auto; - - [class*='admin__control-'] { - display: inline; - } - - &-prefix { - float: left; - } - - &-suffix { - float: right; - } - } -} diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index 8d314e06899b3..986f02e338d59 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -3905,117 +3905,6 @@ } } -// -// IE9 styles -// --------------------------------------------- - -.ie9 { - .admin__scope-old { - select { - &:not([multiple]) { - padding-right: 4px; - min-width: 0; - } - } - - // Table Filters - .filter select { - &:not([multiple]) { - padding-right: 0; - } - } - - .adminhtml-widget-instance-edit { - .grid-chooser .control { - margin-top: -18px; - } - } - .page-layout-admin-1column .page-columns, - .catalog-product-edit, - .catalog-product-new, - .catalog-category-edit { - table.data { - table-layout: fixed; - word-wrap: break-word; - - th { - word-wrap: normal; - overflow: hidden; - vertical-align: top; - - > span { - white-space: normal; - } - } - - th:not(.col-select):not(.col-id):not(.col-severity), - td:not(.col-select):not(.col-id):not(.col-severity) { - width: auto; - } - } - } - - #setGrid_table, - #attributeGrid_table, - .custom-options .data-table, - .ui-dialog .data, - .page-layout-admin-1column .page-columns .data, - .catalog-category-edit .data { - word-wrap: break-word; - table-layout: fixed; - } - - .fieldset-wrapper { - table.data { - table-layout: inherit; - word-wrap: normal; - } - } - - .sales-order-create-index table.data, - .sales-order-create-index .fieldset-wrapper table.data { - table-layout: fixed; - word-wrap: break-word; - - th { - word-wrap: normal; - overflow: hidden; - vertical-align: top; - - > span { - white-space: normal; - } - } - } - - .entry-edit .product-options .grouped-items-table { - table-layout: fixed; - word-wrap: break-word; - - th { - word-wrap: normal; - overflow: hidden; - vertical-align: top; - - > span { - white-space: normal; - } - } - } - - .catalog-category-edit, - .adminhtml-cache-index, - .adminhtml-process-list, - .indexer-indexer-list, - .adminhtml-notification-index { - table.data { - table-layout: inherit; - word-wrap: normal; - } - } - } -} - // // Pages styles // --------------------------------------------- diff --git a/app/design/adminhtml/Magento/backend/web/mui/styles/_table.less b/app/design/adminhtml/Magento/backend/web/mui/styles/_table.less index 7565ee88714f6..1f1ff5df3b80b 100644 --- a/app/design/adminhtml/Magento/backend/web/mui/styles/_table.less +++ b/app/design/adminhtml/Magento/backend/web/mui/styles/_table.less @@ -204,16 +204,6 @@ } } -.eq-ie9 { - .hor-scroll { - display: inline-block; - min-height: 0; - overflow-y: hidden; - overflow-x: auto; - width: 100%; - } -} - td.col-period, td.col-date, td.col-date_to, diff --git a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less index ed7af5872483b..f1e1529d9820f 100644 --- a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less @@ -297,7 +297,6 @@ width: 100%; } - .ie10 &, .ie11 & { height: 100%; } diff --git a/app/design/frontend/Magento/blank/web/css/source/_layout.less b/app/design/frontend/Magento/blank/web/css/source/_layout.less index 8d01ddc0a9bd3..a81a3fa8d1f17 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_layout.less +++ b/app/design/frontend/Magento/blank/web/css/source/_layout.less @@ -98,10 +98,6 @@ .lib-vendor-prefix-flex-grow(1); .lib-vendor-prefix-flex-shrink(0); .lib-vendor-prefix-flex-basis(auto); - - .ie9 & { - width: auto; - } } .columns { diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less index 4ee47be1e2dab..af6cfa8605f6d 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less @@ -62,13 +62,6 @@ // Common // _____________________________________________ -.page-wrapper { - .ie9 & { - .lib-css(background-color, @page__background-color); - min-height: 0; - } -} - & when (@media-common = true) { body { .lib-css(background-color, @page__background-color); @@ -454,12 +447,6 @@ height: 100%; // Stretch screen area for sticky footer } - body { - .ie9 & { - .lib-css(background-color, @copyright__background-color); - } - } - .navigation ul { padding: 0 8px; } @@ -641,7 +628,6 @@ width: 100%; } - .ie10 &, .ie11 & { height: 100%; } diff --git a/app/design/frontend/Magento/luma/web/css/source/_forms.less b/app/design/frontend/Magento/luma/web/css/source/_forms.less index 7c5027aef113b..0c7150c18550b 100644 --- a/app/design/frontend/Magento/luma/web/css/source/_forms.less +++ b/app/design/frontend/Magento/luma/web/css/source/_forms.less @@ -98,11 +98,6 @@ &::-ms-expand { display: none; } - - .lt-ie10 & { - background-image: none; - padding-right: 4px; - } } select { diff --git a/dev/tools/grunt/configs/autoprefixer.json b/dev/tools/grunt/configs/autoprefixer.json index 78283d030919f..28cd9c8a1255f 100644 --- a/dev/tools/grunt/configs/autoprefixer.json +++ b/dev/tools/grunt/configs/autoprefixer.json @@ -2,7 +2,7 @@ "options": { "browsers": [ "last 2 versions", - "ie 9" + "ie 11" ] }, "setup": { diff --git a/lib/web/css/source/components/_modals.less b/lib/web/css/source/components/_modals.less index 51ba451681df5..fb29a74c60585 100644 --- a/lib/web/css/source/components/_modals.less +++ b/lib/web/css/source/components/_modals.less @@ -198,18 +198,14 @@ &._inner-scroll { overflow-y: visible; - .ie11 &, - .ie10 &, - .ie9 & { + .ie11 & { overflow-y: auto; } .modal-inner-wrap { max-height: 90%; - .ie11 &, - .ie10 &, - .ie9 & { + .ie11 & { max-height: none; } } diff --git a/lib/web/mage/gallery/gallery.less b/lib/web/mage/gallery/gallery.less index 1f55c55bb009b..3c35a772253bc 100644 --- a/lib/web/mage/gallery/gallery.less +++ b/lib/web/mage/gallery/gallery.less @@ -400,10 +400,6 @@ .fotorama-abs-center(); height: @size-fotorama-block; width: @size-fotorama-block; - - .ie9 & { - margin: (-@size-fotorama-block/2) 0 0 (-@size-fotorama-block/2); - } } } @@ -873,10 +869,6 @@ .fotorama__thumb--icon { .fotorama-abs-center(); width: 100%; - - .ie9 & { - margin: (-@fotorama-thumb-arrow/2) 0 0 (-@fotorama-thumb-arrow/2); - } } } .fotorama__thumb__arr--left { diff --git a/lib/web/mage/ie-class-fixer.js b/lib/web/mage/ie-class-fixer.js index 6e89c0779f1a0..bab02123ac9b7 100644 --- a/lib/web/mage/ie-class-fixer.js +++ b/lib/web/mage/ie-class-fixer.js @@ -7,19 +7,13 @@ (function () { var userAgent = navigator.userAgent, // user agent identifier html = document.documentElement, // html tag - version = 9, // minimal supported version of IE + version = 11, // minimal supported version of IE gap = ''; // gap between classes if (html.className) { // check if neighbour class exist in html tag gap = ' '; } // end if - for (version; version <= 10; version++) { // loop from minimal to 10 version of IE - if (userAgent.indexOf('MSIE ' + version) > -1) { // match IE individual name - html.className += gap + 'ie' + version; - } // end if - } - if (userAgent.match(/Trident.*rv[ :]*11\./)) { // Special case for IE11 html.className += gap + 'ie11'; } // end if diff --git a/setup/view/layout/layout.phtml b/setup/view/layout/layout.phtml index 613ff391a8fff..0415aff91fc7b 100644 --- a/setup/view/layout/layout.phtml +++ b/setup/view/layout/layout.phtml @@ -5,8 +5,6 @@ */ ?> <?= $this->doctype() ?> -<!--[if IE 9]> -<html class="ie9" lang="en" ng-app="magentoSetup"><![endif]--> <!--[if !IE]><!--> <html lang="en" ng-app="magentoSetup"> <!--<![endif]--> From c406c54367e6346c0575220aa7aeae2a4cb244bf Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel.ruf@ueberbit.de> Date: Fri, 3 Aug 2018 14:04:53 +0200 Subject: [PATCH 0729/1171] fix: remove unused variable declaration --- lib/web/mage/ie-class-fixer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/web/mage/ie-class-fixer.js b/lib/web/mage/ie-class-fixer.js index bab02123ac9b7..683090b1d1386 100644 --- a/lib/web/mage/ie-class-fixer.js +++ b/lib/web/mage/ie-class-fixer.js @@ -7,7 +7,6 @@ (function () { var userAgent = navigator.userAgent, // user agent identifier html = document.documentElement, // html tag - version = 11, // minimal supported version of IE gap = ''; // gap between classes if (html.className) { // check if neighbour class exist in html tag From dc0827ade30819b4e06b24cdc6d691413b222f39 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Fri, 3 Aug 2018 15:51:12 +0300 Subject: [PATCH 0730/1171] MAGETWO-93998: [Forwardport] Run Product Price reindex in multithread mode with Redis --- lib/internal/Magento/Framework/Session/SaveHandler/Redis.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/Magento/Framework/Session/SaveHandler/Redis.php b/lib/internal/Magento/Framework/Session/SaveHandler/Redis.php index 447523214cb78..cd1cef5da6ddd 100644 --- a/lib/internal/Magento/Framework/Session/SaveHandler/Redis.php +++ b/lib/internal/Magento/Framework/Session/SaveHandler/Redis.php @@ -140,6 +140,7 @@ public function close() * @param int $maxLifeTime ignored * @return boolean * @throws SessionException + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function gc($maxLifeTime) { From d23f36f46811a333996d46bf43fcb438fbd7daf7 Mon Sep 17 00:00:00 2001 From: Ji Lu <> Date: Fri, 3 Aug 2018 10:02:48 -0500 Subject: [PATCH 0731/1171] MC-111: Admin should be able to add default video for simple products MC-206: Admin should be able to remove default video for simple products - Updated mftf tests --- .../Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml | 3 ++- .../Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml index 13dab55cb0771..5456cb02e74ca 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml @@ -19,8 +19,8 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup ref="EnableAdminAccountSharingActionGroup" stepKey="enableAdminAccountSharing"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> @@ -29,6 +29,7 @@ <!-- Create product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageAdd"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> <argument name="product" value="ApiSimpleProduct"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml index 63e7abd09c30d..1bd218d18c27d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml @@ -19,8 +19,8 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup ref="EnableAdminAccountSharingActionGroup" stepKey="enableAdminAccountSharing"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> @@ -29,6 +29,7 @@ <!-- Create product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageAdd"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="EnableAdminAccountSharingActionGroup" stepKey="enableAdminAccountSharing"/> <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> <argument name="product" value="ApiSimpleProduct"/> </actionGroup> From ca82ac338bd35f2fb045a34e16cf74414c8a4d33 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Fri, 3 Aug 2018 11:05:47 -0500 Subject: [PATCH 0732/1171] MQE-1167: Remove composer.json from Mftf folder in app/code --- .../composer.json | 33 ------------------- .../ConfigurableProductWishlist/composer.json | 33 ------------------- .../FunctionalTest/Framework/composer.json | 30 ----------------- .../SampleTemplates/composer.json | 30 ----------------- .../FunctionalTest/SampleTests/composer.json | 30 ----------------- 5 files changed, 156 deletions(-) delete mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/composer.json delete mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/composer.json delete mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Framework/composer.json delete mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTemplates/composer.json delete mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/composer.json diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/composer.json b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/composer.json deleted file mode 100644 index df8598a93a2d8..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/composer.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "magento/magento2-functional-test-module-configurable-product-catalog-search", - "description": "Magento 2 Functional Test Module Configurable Product Catalog Search", - "type": "magento2-test-module", - "version": "100.0.0-dev", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "config": { - "sort-packages": true - }, - "require": { - "magento/magento2-functional-testing-framework": "1.0.0", - "magento/magento2-functional-test-module-catalog": "100.0.0-dev", - "magento/magento2-functional-test-module-catalog-search": "100.0.0-dev", - "magento/magento2-functional-test-module-configurable-product": "100.0.0-dev", - "php": "~7.1.3||~7.2.0" - }, - "autoload": { - "psr-4": { - "Magento\\FunctionalTest\\ConfigurableProductCatalogSearch\\": "" - } - }, - "extra": { - "map": [ - [ - "*", - "tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch" - ] - ] - } -} diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/composer.json b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/composer.json deleted file mode 100644 index e7f61c1bc660d..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/composer.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "magento/magento2-functional-test-module-configurable-product-wishlist", - "description": "Magento 2 Functional Test Module Configurable Product Wishlist", - "type": "magento2-test-module", - "version": "100.0.0-dev", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "config": { - "sort-packages": true - }, - "require": { - "magento/magento2-functional-testing-framework": "1.0.0", - "magento/magento2-functional-test-module-catalog": "100.0.0-dev", - "magento/magento2-functional-test-module-configurable-product": "100.0.0-dev", - "magento/magento2-functional-test-module-wishlist": "100.0.0-dev", - "php": "~7.1.3||~7.2.0" - }, - "autoload": { - "psr-4": { - "Magento\\FunctionalTest\\ConfigurableProductWishlist\\": "" - } - }, - "extra": { - "map": [ - [ - "*", - "tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist" - ] - ] - } -} diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Framework/composer.json b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Framework/composer.json deleted file mode 100644 index 3a00a525a5478..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Framework/composer.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "magento/magento2-functional-test-framework", - "description": "Magento 2 Functional Test Framework", - "type": "magento2-test-module", - "version": "100.0.0-dev", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "config": { - "sort-packages": true - }, - "require": { - "magento/magento2-functional-testing-framework": "~2.0.0", - "php": "~7.1.3||~7.2.0" - }, - "autoload": { - "psr-4": { - "Magento\\FunctionalTest\\Framework\\": "" - } - }, - "extra": { - "map": [ - [ - "*", - "dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Framework" - ] - ] - } -} diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTemplates/composer.json b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTemplates/composer.json deleted file mode 100644 index e0ceb5bd23c1c..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTemplates/composer.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "magento/magento2-functional-test-module-sample-templates", - "description": "Magento 2 Functional Test Module Sample Templates", - "type": "magento2-test-module", - "version": "100.0.0-dev", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "config": { - "sort-packages": true - }, - "require": { - "magento/magento2-functional-testing-framework": "1.0.0", - "php": "~7.1.3||~7.2.0" - }, - "autoload": { - "psr-4": { - "Magento\\FunctionalTest\\SampleTemplates\\": "" - } - }, - "extra": { - "map": [ - [ - "*", - "tests/functional/Magento/FunctionalTest/SampleTemplates" - ] - ] - } -} diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/composer.json b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/composer.json deleted file mode 100644 index dc91e593e745b..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SampleTests/composer.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "magento/magento2-functional-test-module-sample-tests", - "description": "Magento 2 Functional Test Module Sample Tests", - "type": "magento2-test-module", - "version": "100.0.0-dev", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "config": { - "sort-packages": true - }, - "require": { - "magento/magento2-functional-testing-framework": "1.0.0", - "php": "~7.1.3||~7.2.0" - }, - "autoload": { - "psr-4": { - "Magento\\FunctionalTest\\SampleTests\\": "" - } - }, - "extra": { - "map": [ - [ - "*", - "tests/functional/Magento/FunctionalTest/SampleTests" - ] - ] - } -} From d9a3cf5606d7cf1666bdd0c8fbaec2b549ec653f Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Fri, 3 Aug 2018 19:16:04 +0300 Subject: [PATCH 0733/1171] MAGETWO-92953: [2.3] Once Payflow Pro payment is Declined, customer cannot continue checkout - allow POST request without form key on response controller --- .../Controller/Transparent/Response.php | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Paypal/Controller/Transparent/Response.php b/app/code/Magento/Paypal/Controller/Transparent/Response.php index c54dd529588b9..2764121232f5a 100644 --- a/app/code/Magento/Paypal/Controller/Transparent/Response.php +++ b/app/code/Magento/Paypal/Controller/Transparent/Response.php @@ -5,6 +5,7 @@ */ namespace Magento\Paypal\Controller\Transparent; +use Magento\Framework\App\CsrfAwareActionInterface; use Magento\Framework\Registry; use Magento\Framework\App\Action\Context; use Magento\Framework\View\Result\LayoutFactory; @@ -16,11 +17,13 @@ use Magento\Paypal\Model\Payflow\Transparent; use Magento\Sales\Api\PaymentFailuresInterface; use Magento\Framework\Session\Generic as Session; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\Request\InvalidRequestException; /** * Class Response */ -class Response extends \Magento\Framework\App\Action\Action +class Response extends \Magento\Framework\App\Action\Action implements CsrfAwareActionInterface { /** * Core registry @@ -91,6 +94,23 @@ public function __construct( $this->paymentFailures = $paymentFailures ?: $this->_objectManager->get(PaymentFailuresInterface::class); } + /** + * @inheritDoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } + /** * @return ResultInterface */ From 0d7182794c80b807e1a13ca728f9ddb73b858011 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Fri, 3 Aug 2018 21:06:33 +0300 Subject: [PATCH 0734/1171] MAGETWO-92953: [2.3] Once Payflow Pro payment is Declined, customer cannot continue checkout --- app/code/Magento/Paypal/Controller/Transparent/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Paypal/Controller/Transparent/Response.php b/app/code/Magento/Paypal/Controller/Transparent/Response.php index 2764121232f5a..e16d2289df845 100644 --- a/app/code/Magento/Paypal/Controller/Transparent/Response.php +++ b/app/code/Magento/Paypal/Controller/Transparent/Response.php @@ -21,7 +21,7 @@ use Magento\Framework\App\Request\InvalidRequestException; /** - * Class Response + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Response extends \Magento\Framework\App\Action\Action implements CsrfAwareActionInterface { From 18b8c0e557dbc54e80d5f1dd87cae986dc216931 Mon Sep 17 00:00:00 2001 From: KevinBKozan <kkozan@magento.com> Date: Fri, 3 Aug 2018 13:37:55 -0500 Subject: [PATCH 0735/1171] MQE-1172: Bump MFTF version and deliver Magento branches - updates composer and lock file - deleted command.php --- composer.json | 2 +- composer.lock | 356 +++++++++++-------------- dev/tests/acceptance/utils/command.php | 78 ------ 3 files changed, 155 insertions(+), 281 deletions(-) delete mode 100644 dev/tests/acceptance/utils/command.php diff --git a/composer.json b/composer.json index 375522b557f03..8293e17ab8acf 100644 --- a/composer.json +++ b/composer.json @@ -81,7 +81,7 @@ "zendframework/zend-view": "~2.10.0" }, "require-dev": { - "magento/magento2-functional-testing-framework": "2.3.0", + "magento/magento2-functional-testing-framework": "2.3.2", "friendsofphp/php-cs-fixer": "~2.12.0", "lusitanian/oauth": "~0.8.10", "pdepend/pdepend": "2.5.2", diff --git a/composer.lock b/composer.lock index 611411d61f60d..2bb2ff1ceb672 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "7537dab4a1593204387fb3f6adf4f33d", + "content-hash": "f7b2384ecfca07c84ed3b89f6f6d8c7b", "packages": [ { "name": "braintree/braintree_php", @@ -257,26 +257,26 @@ }, { "name": "composer/composer", - "version": "1.6.5", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "b184a92419cc9a9c4c6a09db555a94d441cb11c9" + "reference": "39edb2f375679a4eba19e69e9c9491e302976983" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/b184a92419cc9a9c4c6a09db555a94d441cb11c9", - "reference": "b184a92419cc9a9c4c6a09db555a94d441cb11c9", + "url": "https://api.github.com/repos/composer/composer/zipball/39edb2f375679a4eba19e69e9c9491e302976983", + "reference": "39edb2f375679a4eba19e69e9c9491e302976983", "shasum": "" }, "require": { "composer/ca-bundle": "^1.0", "composer/semver": "^1.0", "composer/spdx-licenses": "^1.2", + "composer/xdebug-handler": "^1.1", "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0", "php": "^5.3.2 || ^7.0", "psr/log": "^1.0", - "seld/cli-prompt": "^1.0", "seld/jsonlint": "^1.4", "seld/phar-utils": "^1.0", "symfony/console": "^2.7 || ^3.0 || ^4.0", @@ -302,7 +302,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6-dev" + "dev-master": "1.7-dev" } }, "autoload": { @@ -333,7 +333,7 @@ "dependency", "package" ], - "time": "2018-05-04T09:44:59+00:00" + "time": "2018-08-03T13:39:07+00:00" }, { "name": "composer/semver", @@ -458,6 +458,50 @@ ], "time": "2018-04-30T10:33:04+00:00" }, + { + "name": "composer/xdebug-handler", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "c919dc6c62e221fc6406f861ea13433c0aa24f08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/c919dc6c62e221fc6406f861ea13433c0aa24f08", + "reference": "c919dc6c62e221fc6406f861ea13433c0aa24f08", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0", + "psr/log": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "time": "2018-04-11T15:42:36+00:00" + }, { "name": "container-interop/container-interop", "version": "1.2.0", @@ -546,16 +590,16 @@ }, { "name": "guzzlehttp/ringphp", - "version": "1.1.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/guzzle/RingPHP.git", - "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b" + "reference": "5e2a174052995663dd68e6b5ad838afd47dd615b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", - "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", + "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/5e2a174052995663dd68e6b5ad838afd47dd615b", + "reference": "5e2a174052995663dd68e6b5ad838afd47dd615b", "shasum": "" }, "require": { @@ -593,7 +637,7 @@ } ], "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", - "time": "2015-05-20T03:37:09+00:00" + "time": "2018-07-31T13:22:33+00:00" }, { "name": "guzzlehttp/streams", @@ -1625,54 +1669,6 @@ ], "time": "2018-06-13T15:59:06+00:00" }, - { - "name": "seld/cli-prompt", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/cli-prompt.git", - "reference": "a19a7376a4689d4d94cab66ab4f3c816019ba8dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/cli-prompt/zipball/a19a7376a4689d4d94cab66ab4f3c816019ba8dd", - "reference": "a19a7376a4689d4d94cab66ab4f3c816019ba8dd", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Seld\\CliPrompt\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "Allows you to prompt for user input on the command line, and optionally hide the characters they type", - "keywords": [ - "cli", - "console", - "hidden", - "input", - "prompt" - ], - "time": "2017-03-18T11:32:45+00:00" - }, { "name": "seld/jsonlint", "version": "1.7.1", @@ -1768,16 +1764,16 @@ }, { "name": "symfony/console", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5c31f6a97c1c240707f6d786e7e59bfacdbc0219" + "reference": "ca80b8ced97cf07390078b29773dc384c39eee1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5c31f6a97c1c240707f6d786e7e59bfacdbc0219", - "reference": "5c31f6a97c1c240707f6d786e7e59bfacdbc0219", + "url": "https://api.github.com/repos/symfony/console/zipball/ca80b8ced97cf07390078b29773dc384c39eee1f", + "reference": "ca80b8ced97cf07390078b29773dc384c39eee1f", "shasum": "" }, "require": { @@ -1832,20 +1828,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-07-16T14:05:40+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "00d64638e4f0703a00ab7fc2c8ae5f75f3b4020f" + "reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/00d64638e4f0703a00ab7fc2c8ae5f75f3b4020f", - "reference": "00d64638e4f0703a00ab7fc2c8ae5f75f3b4020f", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/bfb30c2ad377615a463ebbc875eba64a99f6aa3e", + "reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e", "shasum": "" }, "require": { @@ -1895,20 +1891,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-07-10T11:02:47+00:00" + "time": "2018-07-26T09:10:45+00:00" }, { "name": "symfony/filesystem", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c" + "reference": "2e30335e0aafeaa86645555959572fe7cea22b43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", - "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/2e30335e0aafeaa86645555959572fe7cea22b43", + "reference": "2e30335e0aafeaa86645555959572fe7cea22b43", "shasum": "" }, "require": { @@ -1945,20 +1941,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-05-30T07:26:09+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { "name": "symfony/finder", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb" + "reference": "e162f1df3102d0b7472805a5a9d5db9fcf0a8068" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/84714b8417d19e4ba02ea78a41a975b3efaafddb", - "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb", + "url": "https://api.github.com/repos/symfony/finder/zipball/e162f1df3102d0b7472805a5a9d5db9fcf0a8068", + "reference": "e162f1df3102d0b7472805a5a9d5db9fcf0a8068", "shasum": "" }, "require": { @@ -1994,7 +1990,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-06-19T21:38:16+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2112,16 +2108,16 @@ }, { "name": "symfony/process", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a" + "reference": "f01fc7a4493572f7f506c49dcb50ad01fb3a2f56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a", - "reference": "1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a", + "url": "https://api.github.com/repos/symfony/process/zipball/f01fc7a4493572f7f506c49dcb50ad01fb3a2f56", + "reference": "f01fc7a4493572f7f506c49dcb50ad01fb3a2f56", "shasum": "" }, "require": { @@ -2157,7 +2153,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-05-31T10:17:53+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { "name": "tedivm/jshrink", @@ -2728,16 +2724,16 @@ }, { "name": "zendframework/zend-diactoros", - "version": "1.8.3", + "version": "1.8.4", "source": { "type": "git", "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "72c13834fb3db2a962e913758b384ff2e6425d6e" + "reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/72c13834fb3db2a962e913758b384ff2e6425d6e", - "reference": "72c13834fb3db2a962e913758b384ff2e6425d6e", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba", + "reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba", "shasum": "" }, "require": { @@ -2787,7 +2783,7 @@ "psr", "psr-7" ], - "time": "2018-07-24T21:54:38+00:00" + "time": "2018-08-01T13:47:49+00:00" }, { "name": "zendframework/zend-escaper", @@ -2883,16 +2879,16 @@ }, { "name": "zendframework/zend-feed", - "version": "2.10.2", + "version": "2.10.3", "source": { "type": "git", "url": "https://github.com/zendframework/zend-feed.git", - "reference": "5253f949f4ad999086ab9b408908b6c6776f24db" + "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/5253f949f4ad999086ab9b408908b6c6776f24db", - "reference": "5253f949f4ad999086ab9b408908b6c6776f24db", + "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", + "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888", "shasum": "" }, "require": { @@ -2940,7 +2936,7 @@ "feed", "zf" ], - "time": "2018-06-18T20:14:01+00:00" + "time": "2018-08-01T13:53:20+00:00" }, { "name": "zendframework/zend-filter", @@ -3085,16 +3081,16 @@ }, { "name": "zendframework/zend-http", - "version": "2.8.0", + "version": "2.8.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-http.git", - "reference": "f48b276ffa11b48dd1ae3c6bc306d6ed7958ef51" + "reference": "44197164a270259116162a442f639085ea24094a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/f48b276ffa11b48dd1ae3c6bc306d6ed7958ef51", - "reference": "f48b276ffa11b48dd1ae3c6bc306d6ed7958ef51", + "url": "https://api.github.com/repos/zendframework/zend-http/zipball/44197164a270259116162a442f639085ea24094a", + "reference": "44197164a270259116162a442f639085ea24094a", "shasum": "" }, "require": { @@ -3136,7 +3132,7 @@ "zend", "zf" ], - "time": "2018-04-26T21:04:50+00:00" + "time": "2018-08-01T13:50:48+00:00" }, { "name": "zendframework/zend-hydrator", @@ -4734,50 +4730,6 @@ "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", "time": "2018-05-17T09:31:08+00:00" }, - { - "name": "composer/xdebug-handler", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "c919dc6c62e221fc6406f861ea13433c0aa24f08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/c919dc6c62e221fc6406f861ea13433c0aa24f08", - "reference": "c919dc6c62e221fc6406f861ea13433c0aa24f08", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0", - "psr/log": "^1.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" - } - ], - "description": "Restarts a process without xdebug.", - "keywords": [ - "Xdebug", - "performance" - ], - "time": "2018-04-11T15:42:36+00:00" - }, { "name": "consolidation/annotated-command", "version": "2.8.4", @@ -6238,16 +6190,16 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.3.0", + "version": "2.3.2", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "174a9470f0b51152d34c5f374392ab9ea91f7519" + "reference": "aac499e5deb97bad3838ec7e2a1a4b38d579d0fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/174a9470f0b51152d34c5f374392ab9ea91f7519", - "reference": "174a9470f0b51152d34c5f374392ab9ea91f7519", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/aac499e5deb97bad3838ec7e2a1a4b38d579d0fe", + "reference": "aac499e5deb97bad3838ec7e2a1a4b38d579d0fe", "shasum": "" }, "require": { @@ -6305,7 +6257,7 @@ "magento", "testing" ], - "time": "2018-07-24T14:16:26+00:00" + "time": "2018-08-03T18:21:09+00:00" }, { "name": "moontoast/math", @@ -7273,16 +7225,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.9", + "version": "6.5.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "093ca5508174cd8ab8efe44fd1dde447adfdec8f" + "reference": "5744955af9c0a2de74a5eb5287c50bf025100d39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/093ca5508174cd8ab8efe44fd1dde447adfdec8f", - "reference": "093ca5508174cd8ab8efe44fd1dde447adfdec8f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5744955af9c0a2de74a5eb5287c50bf025100d39", + "reference": "5744955af9c0a2de74a5eb5287c50bf025100d39", "shasum": "" }, "require": { @@ -7300,7 +7252,7 @@ "phpunit/php-file-iterator": "^1.4.3", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.5", + "phpunit/phpunit-mock-objects": "^5.0.8", "sebastian/comparator": "^2.1", "sebastian/diff": "^2.0", "sebastian/environment": "^3.1", @@ -7353,7 +7305,7 @@ "testing", "xunit" ], - "time": "2018-07-03T06:40:40+00:00" + "time": "2018-08-03T05:27:14+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -8115,16 +8067,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62" + "reference": "c55fe9257003b2d95c0211b3f6941e8dfd26dffd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", - "reference": "ff9ac5d5808a530b2e7f6abcf3a2412d4f9bcd62", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c55fe9257003b2d95c0211b3f6941e8dfd26dffd", + "reference": "c55fe9257003b2d95c0211b3f6941e8dfd26dffd", "shasum": "" }, "require": { @@ -8168,20 +8120,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2018-06-04T17:31:56+00:00" + "time": "2018-07-26T09:10:45+00:00" }, { "name": "symfony/config", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "e57e7b573df9d0eaa8c0152768c708ee7ea2b8e5" + "reference": "c868972ac26e4e19860ce11b300bb74145246ff9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/e57e7b573df9d0eaa8c0152768c708ee7ea2b8e5", - "reference": "e57e7b573df9d0eaa8c0152768c708ee7ea2b8e5", + "url": "https://api.github.com/repos/symfony/config/zipball/c868972ac26e4e19860ce11b300bb74145246ff9", + "reference": "c868972ac26e4e19860ce11b300bb74145246ff9", "shasum": "" }, "require": { @@ -8231,20 +8183,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-06-20T11:15:17+00:00" + "time": "2018-07-26T11:24:31+00:00" }, { "name": "symfony/css-selector", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4" + "reference": "2a4df7618f869b456f9096781e78c57b509d76c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/03ac71606ecb0b0ce792faa17d74cc32c2949ef4", - "reference": "03ac71606ecb0b0ce792faa17d74cc32c2949ef4", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/2a4df7618f869b456f9096781e78c57b509d76c7", + "reference": "2a4df7618f869b456f9096781e78c57b509d76c7", "shasum": "" }, "require": { @@ -8284,20 +8236,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2018-05-30T07:26:09+00:00" + "time": "2018-07-26T09:10:45+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "62912ab79facdbdaa0849f6c2fe4734b7b60f5cc" + "reference": "f4f401fc2766eb8d766fc6043d9e6489b37a41e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/62912ab79facdbdaa0849f6c2fe4734b7b60f5cc", - "reference": "62912ab79facdbdaa0849f6c2fe4734b7b60f5cc", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f4f401fc2766eb8d766fc6043d9e6489b37a41e4", + "reference": "f4f401fc2766eb8d766fc6043d9e6489b37a41e4", "shasum": "" }, "require": { @@ -8355,20 +8307,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2018-07-16T14:05:40+00:00" + "time": "2018-08-01T08:24:03+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "eb501fa8aab8c8e2db790d8d0f945697769f6c41" + "reference": "1c4519d257e652404c3aa550207ccd8ada66b38e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/eb501fa8aab8c8e2db790d8d0f945697769f6c41", - "reference": "eb501fa8aab8c8e2db790d8d0f945697769f6c41", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/1c4519d257e652404c3aa550207ccd8ada66b38e", + "reference": "1c4519d257e652404c3aa550207ccd8ada66b38e", "shasum": "" }, "require": { @@ -8412,20 +8364,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-07-05T11:54:23+00:00" + "time": "2018-07-26T11:00:49+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "8da9ea68ab2d80dfabd41e0d14b9606bb47a10c0" + "reference": "7d93e3547660ec7ee3dad1428ba42e8076a0e5f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/8da9ea68ab2d80dfabd41e0d14b9606bb47a10c0", - "reference": "8da9ea68ab2d80dfabd41e0d14b9606bb47a10c0", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7d93e3547660ec7ee3dad1428ba42e8076a0e5f1", + "reference": "7d93e3547660ec7ee3dad1428ba42e8076a0e5f1", "shasum": "" }, "require": { @@ -8466,20 +8418,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-07-16T14:05:40+00:00" + "time": "2018-08-01T14:07:44+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "0aec9f9c5d2447ae7ea5ea31bc82f1d43f9a8a56" + "reference": "1913f1962477cdbb13df951f8147d5da1fe2412c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0aec9f9c5d2447ae7ea5ea31bc82f1d43f9a8a56", - "reference": "0aec9f9c5d2447ae7ea5ea31bc82f1d43f9a8a56", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1913f1962477cdbb13df951f8147d5da1fe2412c", + "reference": "1913f1962477cdbb13df951f8147d5da1fe2412c", "shasum": "" }, "require": { @@ -8520,7 +8472,7 @@ "configuration", "options" ], - "time": "2018-07-07T16:00:36+00:00" + "time": "2018-07-26T08:55:25+00:00" }, { "name": "symfony/polyfill-php70", @@ -8638,16 +8590,16 @@ }, { "name": "symfony/stopwatch", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "07463bbbbbfe119045a24c4a516f92ebd2752784" + "reference": "966c982df3cca41324253dc0c7ffe76b6076b705" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/07463bbbbbfe119045a24c4a516f92ebd2752784", - "reference": "07463bbbbbfe119045a24c4a516f92ebd2752784", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/966c982df3cca41324253dc0c7ffe76b6076b705", + "reference": "966c982df3cca41324253dc0c7ffe76b6076b705", "shasum": "" }, "require": { @@ -8683,20 +8635,20 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2018-02-19T16:51:42+00:00" + "time": "2018-07-26T11:00:49+00:00" }, { "name": "symfony/yaml", - "version": "v3.4.13", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0" + "reference": "810af2d35fc72b6cf5c01116806d2b65ccaaf2e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", - "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", + "url": "https://api.github.com/repos/symfony/yaml/zipball/810af2d35fc72b6cf5c01116806d2b65ccaaf2e2", + "reference": "810af2d35fc72b6cf5c01116806d2b65ccaaf2e2", "shasum": "" }, "require": { @@ -8742,7 +8694,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-05-03T23:18:14+00:00" + "time": "2018-07-26T11:19:56+00:00" }, { "name": "theseer/fdomdocument", diff --git a/dev/tests/acceptance/utils/command.php b/dev/tests/acceptance/utils/command.php deleted file mode 100644 index 6b8208f55d495..0000000000000 --- a/dev/tests/acceptance/utils/command.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -if (isset($_POST['command'])) { - $command = urldecode($_POST['command']); - if (array_key_exists("arguments", $_POST)) { - $arguments = urldecode($_POST['arguments']); - } else { - $arguments = null; - } - $php = PHP_BINARY ?: (PHP_BINDIR ? PHP_BINDIR . '/php' : 'php'); - $valid = validateCommand($command); - if ($valid) { - exec( - escapeCommand($php . ' -f ../../../../bin/magento ' . $command) . " $arguments" ." 2>&1", - $output, - $exitCode - ); - if ($exitCode == 0) { - http_response_code(202); - } else { - http_response_code(500); - } - echo implode("\n", $output); - } else { - http_response_code(403); - echo "Given command not found valid in Magento CLI Command list."; - } -} else { - http_response_code(412); - echo("Command parameter is not set."); -} - -/** - * Returns escaped command. - * - * @param string $command - * @return string - */ -function escapeCommand($command) -{ - $escapeExceptions = [ - '> /dev/null &' => '--dev-null-amp--' - ]; - - $command = escapeshellcmd( - str_replace(array_keys($escapeExceptions), array_values($escapeExceptions), $command) - ); - - return str_replace(array_values($escapeExceptions), array_keys($escapeExceptions), $command); -} - -/** - * Checks magento list of CLI commands for given $command. Does not check command parameters, just base command. - * @param string $command - * @return bool - */ -function validateCommand($command) -{ - $php = PHP_BINARY ?: (PHP_BINDIR ? PHP_BINDIR . '/php' : 'php'); - exec($php . ' -f ../../../../bin/magento list', $commandList); - // Trim list of commands after first whitespace - $commandList = array_map("trimAfterWhitespace", $commandList); - return in_array(trimAfterWhitespace($command), $commandList); -} - -/** - * Returns given string trimmed of everything after the first found whitespace. - * @param string $string - * @return string - */ -function trimAfterWhitespace($string) -{ - return strtok($string, ' '); -} From 78c0cac20c14136f2f33b8837796526b6404d50e Mon Sep 17 00:00:00 2001 From: Ji Lu <> Date: Fri, 3 Aug 2018 14:29:46 -0500 Subject: [PATCH 0736/1171] MC-111: Admin should be able to add default video for simple products MC-206: Admin should be able to remove default video for simple products - Updated mftf tests --- .../Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml | 7 ++++--- .../Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml | 7 ++++--- .../Test/AdminAddDefaultVideoDownloadableProductTest.xml | 3 ++- .../AdminRemoveDefaultVideoDownloadableProductTest.xml | 3 ++- .../Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml | 7 +++---- .../Test/AdminRemoveDefaultVideoGroupedProductTest.xml | 7 +++---- .../Test/Mftf/ActionGroup/AdminProductVideoActionGroup.xml | 4 ++++ 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml index 516f47ef8ac56..3c00344697699 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml @@ -29,15 +29,16 @@ <!-- Create a bundle product --> <!-- Replacing steps in base AdminAddDefaultVideoSimpleProductTest --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage" after="waitForProductIndexPageLoad"> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillMainProductForm" after="goToCreateProductPage"> + <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillMainProductForm"> <argument name="product" value="BundleProduct"/> </actionGroup> <!-- Add two bundle items --> - <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="openBundleSection" after="addProductVideo"/> + <scrollTo selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" x="0" y="-100" stepKey="scrollToSection" after="addProductVideo"/> + <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="openBundleSection" after="scrollToSection"/> <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption" after="openBundleSection"/> <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleTitle" after="clickAddOption"/> <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillBundleTitle" after="waitForBundleTitle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml index 51fa38ce421c8..e3cb68b6664e2 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml @@ -29,15 +29,16 @@ <!-- Create a bundle product --> <!-- Replacing steps in base AdminRemoveDefaultVideoSimpleProductTest --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage" after="waitForProductIndexPageLoad"> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillMainProductForm" after="goToCreateProductPage"> + <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillMainProductForm"> <argument name="product" value="BundleProduct"/> </actionGroup> <!-- Add two bundle items --> - <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="openBundleSection" after="addProductVideo"/> + <scrollTo selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" x="0" y="-100" stepKey="scrollToSection" after="addProductVideo"/> + <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="openBundleSection" after="scrollToSection"/> <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption" after="openBundleSection"/> <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleTitle" after="clickAddOption"/> <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillBundleTitle" after="waitForBundleTitle"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml index 63ed252360f00..88dcca0958719 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml @@ -29,7 +29,8 @@ </actionGroup> <!-- Add downloadable links --> - <click selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="openDownloadableSection" after="addProductVideo"/> + <scrollTo selector="{{AdminProductDownloadableSection.sectionHeader}}" x="0" y="-100" stepKey="scrollToSection" after="addProductVideo"/> + <conditionalClick selector="{{AdminProductDownloadableSection.sectionHeader}}" dependentSelector="{{AdminProductDownloadableSection.isDownloadableProduct}}" visible="false" stepKey="openDownloadableSection" after="scrollToSection"/> <checkOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkOptionIsDownloadable" after="openDownloadableSection"/> <fillField userInput="{{downloadableData.link_title}}" selector="{{AdminProductDownloadableSection.linksTitleInput}}" stepKey="fillLinkTitle" after="checkOptionIsDownloadable"/> <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately" after="fillLinkTitle"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml index 2210dd009318a..4a62a7a43bc60 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml @@ -29,7 +29,8 @@ </actionGroup> <!-- Add downloadable links --> - <click selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="openDownloadableSection" after="addProductVideo"/> + <scrollTo selector="{{AdminProductDownloadableSection.sectionHeader}}" x="0" y="-100" stepKey="scrollToSection" after="addProductVideo"/> + <conditionalClick selector="{{AdminProductDownloadableSection.sectionHeader}}" dependentSelector="{{AdminProductDownloadableSection.isDownloadableProduct}}" visible="false" stepKey="openDownloadableSection" after="scrollToSection"/> <checkOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkOptionIsDownloadable" after="openDownloadableSection"/> <fillField userInput="{{downloadableData.link_title}}" selector="{{AdminProductDownloadableSection.linksTitleInput}}" stepKey="fillLinkTitle" after="checkOptionIsDownloadable"/> <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately" after="fillLinkTitle"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml index d4c4655895051..8634fc3f6f9dc 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml @@ -37,10 +37,9 @@ </actionGroup> <!-- Add two simple products to grouped product --> - <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollTo" after="addProductVideo"/> - <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductSection" after="scrollTo"/> - <click selector="body" stepKey="clickBody" after="openGroupedProductSection"/> - <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup" after="clickBody"/> + <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollToSection" after="addProductVideo"/> + <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductSection" after="scrollToSection"/> + <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup" after="openGroupedProductSection"/> <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForFilter" after="clickAddProductsToGroup"/> <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku1" after="waitForFilter"> <argument name="product" value="$$simpleProduct1$$"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml index 577fe9644a6fc..25c45abdfe047 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml @@ -37,10 +37,9 @@ </actionGroup> <!-- Add two simple products to grouped product --> - <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollTo" after="addProductVideo"/> - <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductSection" after="scrollTo"/> - <click selector="body" stepKey="clickBody" after="openGroupedProductSection"/> - <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup" after="clickBody"/> + <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollToSection" after="addProductVideo"/> + <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductSection" after="scrollToSection"/> + <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup" after="openGroupedProductSection"/> <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForFilter" after="clickAddProductsToGroup"/> <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku1" after="waitForFilter"> <argument name="product" value="$$simpleProduct1$$"/> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminProductVideoActionGroup.xml b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminProductVideoActionGroup.xml index f28a9f9359def..f83abc01b6540 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminProductVideoActionGroup.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminProductVideoActionGroup.xml @@ -12,6 +12,7 @@ <arguments> <argument name="video" defaultValue="mftfTestProductVideo"/> </arguments> + <scrollTo selector="{{AdminProductImagesSection.productImagesToggle}}" x="0" y="-100" stepKey="scrollToArea"/> <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductVideoSection"/> <waitForElementVisible selector="{{AdminProductImagesSection.addVideoButton}}" stepKey="waitForAddVideoButtonVisible" time="30"/> <click selector="{{AdminProductImagesSection.addVideoButton}}" stepKey="addVideo"/> @@ -27,6 +28,7 @@ <!-- Remove video in Admin Product page --> <actionGroup name="removeProductVideo"> + <scrollTo selector="{{AdminProductImagesSection.productImagesToggle}}" x="0" y="-100" stepKey="scrollToArea"/> <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductVideoSection"/> <waitForElementVisible selector="{{AdminProductImagesSection.addVideoButton}}" stepKey="waitForAddVideoButtonVisible" time="30"/> <click selector="{{AdminProductImagesSection.removeVideoButton}}" stepKey="removeVideo"/> @@ -37,6 +39,7 @@ <arguments> <argument name="video" defaultValue="mftfTestProductVideo"/> </arguments> + <scrollTo selector="{{AdminProductImagesSection.productImagesToggle}}" x="0" y="-100" stepKey="scrollToArea"/> <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductVideoSection"/> <waitForPageLoad stepKey="waitForPageLoad"/> <seeElement selector="{{AdminProductImagesSection.videoTitleText(video.videoShortTitle)}}" stepKey="seeVideoTitle"/> @@ -48,6 +51,7 @@ <arguments> <argument name="video" defaultValue="mftfTestProductVideo"/> </arguments> + <scrollTo selector="{{AdminProductImagesSection.productImagesToggle}}" x="0" y="-100" stepKey="scrollToArea"/> <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductVideoSection"/> <waitForPageLoad stepKey="waitForPageLoad"/> <dontSeeElement selector="{{AdminProductImagesSection.videoTitleText(video.videoShortTitle)}}" stepKey="seeVideoTitle"/> From b78cf35c6be0fc16c50ff746dc8e0928026916f3 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Fri, 3 Aug 2018 15:37:21 -0500 Subject: [PATCH 0737/1171] MAGETWO-90591: Add Selected Button shouldn't be enabled if there are no images available --- app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Content.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Content.php b/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Content.php index 89f6be0525663..e94992ae26b60 100644 --- a/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Content.php +++ b/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Content.php @@ -78,7 +78,7 @@ protected function _construct() $this->buttonList->add( 'insert_files', - ['class' => 'save no-display primary', 'label' => __('Add Selected'), 'type' => 'button'], + ['class' => 'save no-display action-primary', 'label' => __('Add Selected'), 'type' => 'button'], 0, 0, 'header' From 890a02cf4c35a0242969cded3b17d1e8827ab61b Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Fri, 3 Aug 2018 15:51:23 -0500 Subject: [PATCH 0738/1171] MC-1416: Editing Text Content Block from the Stage with TinyMCE 4 turned on Add word-wrap: break-all to .magento-placeholder --- lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css index de634880f765a..af1bb8df4dbfa 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css @@ -16,6 +16,7 @@ font-size: 14px; color: #000; box-sizing: initial; + word-wrap: break-all; } .magento-placeholder-error { From b8d37861e908ff55327d76f1363da664a0d6a58f Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Fri, 3 Aug 2018 16:04:33 -0500 Subject: [PATCH 0739/1171] MC-1416: Editing Text Content Block from the Stage with TinyMCE 4 turned on Add word-break: break-all to .magento-placeholder --- lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css index af1bb8df4dbfa..27373b2a5147b 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css @@ -16,7 +16,7 @@ font-size: 14px; color: #000; box-sizing: initial; - word-wrap: break-all; + word-break: break-all; } .magento-placeholder-error { From 9524aeb06d2ab19cebae792f18f0ff3ded274189 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Fri, 27 Jul 2018 09:06:05 +0300 Subject: [PATCH 0740/1171] ENGCOM-2537: GoogleAnalytics: Added unit test for order success observer #17137 --- .../SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php b/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php index 34f71dd53391d..b3ed16e0c94a2 100644 --- a/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php +++ b/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\GoogleAnalytics\Test\Unit\Block; +namespace Magento\GoogleAnalytics\Test\Unit\Observer; use Magento\Framework\Event; use Magento\Framework\Event\Observer; From 76b1481c375a53fcfbb111e3fa3c6eb3deb74d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Mon, 30 Jul 2018 17:14:14 +0300 Subject: [PATCH 0741/1171] fixed translation issue --- app/code/Magento/Ui/view/base/web/js/modal/confirm.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/modal/confirm.js b/app/code/Magento/Ui/view/base/web/js/modal/confirm.js index 9cb14f7b2ef11..eceab940d1141 100644 --- a/app/code/Magento/Ui/view/base/web/js/modal/confirm.js +++ b/app/code/Magento/Ui/view/base/web/js/modal/confirm.js @@ -9,10 +9,10 @@ define([ 'jquery', 'underscore', + 'mage/translate', 'jquery/ui', - 'Magento_Ui/js/modal/modal', - 'mage/translate' -], function ($, _) { + 'Magento_Ui/js/modal/modal' +], function ($, _, $t) { 'use strict'; $.widget('mage.confirm', $.mage.modal, { @@ -38,7 +38,7 @@ define([ cancel: function () {} }, buttons: [{ - text: $.mage.__('Cancel'), + text: $t('Cancel'), class: 'action-secondary action-dismiss', /** @@ -48,7 +48,7 @@ define([ this.closeModal(event); } }, { - text: $.mage.__('OK'), + text: $t('OK'), class: 'action-primary action-accept', /** From ecca1f5f535d5dd2fa3b20734209b6194cb92468 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Sun, 5 Aug 2018 19:05:16 +0200 Subject: [PATCH 0742/1171] Removed unnecessary unsets from models --- .../Magento/Braintree/Gateway/Http/Client/TransactionRefund.php | 2 -- .../Gateway/Http/Client/TransactionSubmitForSettlement.php | 2 -- .../Magento/Braintree/Gateway/Http/Client/TransactionVoid.php | 2 -- 3 files changed, 6 deletions(-) diff --git a/app/code/Magento/Braintree/Gateway/Http/Client/TransactionRefund.php b/app/code/Magento/Braintree/Gateway/Http/Client/TransactionRefund.php index 4c3f1e179d378..6d43929db7675 100644 --- a/app/code/Magento/Braintree/Gateway/Http/Client/TransactionRefund.php +++ b/app/code/Magento/Braintree/Gateway/Http/Client/TransactionRefund.php @@ -17,8 +17,6 @@ class TransactionRefund extends AbstractTransaction protected function process(array $data) { $storeId = $data['store_id'] ?? null; - // sending store id and other additional keys are restricted by Braintree API - unset($data['store_id']); return $this->adapterFactory->create($storeId) ->refund($data['transaction_id'], $data[PaymentDataBuilder::AMOUNT]); diff --git a/app/code/Magento/Braintree/Gateway/Http/Client/TransactionSubmitForSettlement.php b/app/code/Magento/Braintree/Gateway/Http/Client/TransactionSubmitForSettlement.php index 16ebd7a7a00c5..6760e724fd3a6 100644 --- a/app/code/Magento/Braintree/Gateway/Http/Client/TransactionSubmitForSettlement.php +++ b/app/code/Magento/Braintree/Gateway/Http/Client/TransactionSubmitForSettlement.php @@ -19,8 +19,6 @@ class TransactionSubmitForSettlement extends AbstractTransaction protected function process(array $data) { $storeId = $data['store_id'] ?? null; - // sending store id and other additional keys are restricted by Braintree API - unset($data['store_id']); return $this->adapterFactory->create($storeId) ->submitForSettlement($data[CaptureDataBuilder::TRANSACTION_ID], $data[PaymentDataBuilder::AMOUNT]); diff --git a/app/code/Magento/Braintree/Gateway/Http/Client/TransactionVoid.php b/app/code/Magento/Braintree/Gateway/Http/Client/TransactionVoid.php index e9a478a8bac23..0a940839fc154 100644 --- a/app/code/Magento/Braintree/Gateway/Http/Client/TransactionVoid.php +++ b/app/code/Magento/Braintree/Gateway/Http/Client/TransactionVoid.php @@ -15,8 +15,6 @@ class TransactionVoid extends AbstractTransaction protected function process(array $data) { $storeId = $data['store_id'] ?? null; - // sending store id and other additional keys are restricted by Braintree API - unset($data['store_id']); return $this->adapterFactory->create($storeId)->void($data['transaction_id']); } From a8ae74ea71b89b2b52930051a3bd022d1ecfe8f7 Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Sun, 5 Aug 2018 19:08:38 +0400 Subject: [PATCH 0743/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Add automated test --- .../StorefrontAddProductToCardActionGroup.xml | 29 +++++-------------- .../Test/Mftf/Data/SimpleProductData.xml | 18 ++++++++++++ .../StorefrontAddProductToCardSection.xml | 10 +++---- .../AddingProductWithExpiredSessionTest.xml | 4 +-- 4 files changed, 32 insertions(+), 29 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/Data/SimpleProductData.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml index b38430391285d..f4d65e22abe84 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml @@ -31,12 +31,7 @@ <see userInput="You saved the product." stepKey="seeSaveConfirmation"/> </actionGroup> - <actionGroup name="FindAndAddProductToCardActionGroup"> - <arguments> - <argument name="product" defaultValue="SimpleProductOne"/> - </arguments> - <click selector="{{StorefrontAddProductToCartSection.addToCartBtn}}" stepKey="addToCart"/> <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" time="30" stepKey="waitForProductAdded"/> <click selector="{{StorefrontAddProductToCartSection.showCard}}" stepKey="clickToOpenCard"/> @@ -46,30 +41,20 @@ <see userInput="Shipping Address" stepKey="seeShippingAddress"/> </actionGroup> - - <actionGroup name="AgainGoToProductCategory"> - - <amOnPage url="/admin" stepKey="GoToDashboard"/> - - <click selector="{{GoToProductPageSection.catalog}}" stepKey="clickOnCatalog" /> - <waitForPageLoad stepKey="waitForPage"/> - <click selector="{{GoToProductPageSection.product}}" stepKey="clickToSelectProductsItem" /> - <waitForPageLoad stepKey="waitForPageProducts"/> - </actionGroup> - <actionGroup name="DeleteCreatedProduct"> - - <click selector="{{DeleteCreatedProduct.createdProductID(SimpleProductOne.name)}}" stepKey="selectCreatedProduct"/> + <fillField stepKey="searchToKeyword" selector="{{DeleteCreatedProduct.searchToKeyword}}" userInput="{{SimpleProductOne.name}}"/> + <click stepKey="clickSearchButton" selector="{{DeleteCreatedProduct.searchButton}}"/> + <wait stepKey="waitForFiltering" time="2"/> + <click selector="{{DeleteCreatedProduct.createdProductID}}" stepKey="selectCreatedProduct"/> <wait stepKey="waitSelectCreatedProduct" time="2"/> <waitForElementVisible selector="{{DeleteCreatedProduct.actionSelectBox}}" stepKey="waitToSelectActionVisible" time="50"/> <click stepKey="clickToSelectAction" selector="{{DeleteCreatedProduct.actionSelectBox}}"/> - <wait stepKey="waitClickToSelectAction" time="2"/> + <waitForElementVisible selector="{{DeleteCreatedProduct.deleteButton}}" stepKey="waitForDeleteButtonAppeared" time="2"/> <click selector="{{DeleteCreatedProduct.deleteButton}}" stepKey="clickToDeleteProduct"/> - <wait stepKey="waitClickToDeleteProduct" time="2"/> + <waitForElementVisible selector="{{DeleteCreatedProduct.okButton}}" stepKey="waitForOkButtonAppeared" time="2"/> <click selector="{{DeleteCreatedProduct.okButton}}" stepKey="clickToConfirm"/> - <wait stepKey="waitClickToConfirmButton" time="2"/> + <wait stepKey="waitForRecordIsDeleted" time="2"/> <see userInput="A total of 1 record(s) have been deleted." stepKey="productDeletedSuccessfully"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/SimpleProductData.xml b/app/code/Magento/Customer/Test/Mftf/Data/SimpleProductData.xml new file mode 100644 index 0000000000000..e9eacb6e37a11 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Data/SimpleProductData.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SimpleProductOne" type="product"> + <data key="name" unique="suffix">testProduct</data> + <data key="sku" unique="suffix">testSku</data> + <data key="price">200</data> + <data key="quantity">100</data> + <data key="urlKey" unique="suffix">testProduct</data> + </entity> +</entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml index 3ae3e361d8264..29315aeeb2355 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml @@ -13,7 +13,6 @@ <element name="successMsg" type="button" selector="div.message-success"/> <element name="showCard" type="button" selector=".action.showcart"/> <element name="proceed" type="button" selector="#top-cart-btn-checkout"/> - </section> <section name="GoToProductPageSection"> @@ -59,13 +58,14 @@ <element name="searchOptimization" type="button" selector="//*[contains(text(),'Search Engine Optimization')]"/> <element name="urlKey" type="input" selector="//input[contains(@name,'url_key')]"/> <element name="saveButton" type="button" selector="#save-button"/> - </section> <section name="DeleteCreatedProduct"> - <element name="createdProductID" type="select" selector="//*[@id='container']//*[text()='{{arg1}}']/parent::td/parent::tr//label[contains(@class, 'data-grid-checkbox-cell-inner')]" parameterized="true"/> - <element name="actionSelectBox" type="button" selector="//*[@class='col-xs-2']"/> - <element name="deleteButton" type="button" selector="//*[@class='admin__data-grid-header-row row row-gutter']//*[text()='Delete']"/> + <element name="searchToKeyword" type="input" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/input"/> + <element name="searchButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/button"/> + <element name="createdProductID" type="select" selector="//*[@class='data-grid-checkbox-cell-inner']/input"/> + <element name="actionSelectBox" type="button" selector="//*[@class='col-xs-2']//span[text()='Actions']"/> + <element name="deleteButton" type="button" selector="//div[@class='col-xs-2']//*[text()='Delete']"/> <element name="okButton" type="button" selector=".action-primary.action-accept"/> </section> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml index 056fcb9f47dd1..058b5b5c18bfd 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml @@ -40,8 +40,8 @@ <after> <!--Delete created product--> - <actionGroup ref="AgainGoToProductCategory" stepKey="againGoToProductCategory"/> - <wait stepKey="waitForProductPageLoaded" time="3"/> + <amOnPage url="/admin" stepKey="GoToDashboard"/> + <actionGroup ref="GoToProductPage" stepKey="againGoToProductPage"/> <actionGroup ref="DeleteCreatedProduct" stepKey="deleteCreatedProduct"/> </after> From 6c5d6f091e0771746abfbd630a4e05afe117f3a5 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 6 Aug 2018 12:19:14 +0300 Subject: [PATCH 0744/1171] MAGETWO-93786: Payment APIs webhooks are now expecting a form key --- .../Magento/Framework/App/FrontController.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/FrontController.php b/lib/internal/Magento/Framework/App/FrontController.php index 03d6ad7ab3f02..b00cb3ed6090f 100644 --- a/lib/internal/Magento/Framework/App/FrontController.php +++ b/lib/internal/Magento/Framework/App/FrontController.php @@ -13,6 +13,7 @@ use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Message\ManagerInterface as MessageManager; use Magento\Framework\App\Action\AbstractAction; +use Psr\Log\LoggerInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -39,17 +40,24 @@ class FrontController implements FrontControllerInterface */ private $messages; + /** + * @var LoggerInterface + */ + private $logger; + /** * @param RouterListInterface $routerList * @param ResponseInterface $response * @param RequestValidator|null $requestValidator * @param MessageManager|null $messageManager + * @param LoggerInterface|null $logger */ public function __construct( RouterListInterface $routerList, ResponseInterface $response, ?RequestValidator $requestValidator = null, - ?MessageManager $messageManager = null + ?MessageManager $messageManager = null, + ?LoggerInterface $logger = null ) { $this->_routerList = $routerList; $this->response = $response; @@ -57,6 +65,8 @@ public function __construct( ?? ObjectManager::getInstance()->get(RequestValidator::class); $this->messages = $messageManager ?? ObjectManager::getInstance()->get(MessageManager::class); + $this->logger = $logger + ?? ObjectManager::getInstance()->get(LoggerInterface::class); } /** @@ -125,6 +135,10 @@ private function processRequest( } } catch (InvalidRequestException $exception) { //Validation failed - processing validation results. + $this->logger->debug( + 'Request validation failed for action "' + .get_class($actionInstance) .'"' + ); $result = $exception->getReplaceResult(); if ($messages = $exception->getMessages()) { foreach ($messages as $message) { From ceaa506effa06c0451970094ec2c19ca0449b6f1 Mon Sep 17 00:00:00 2001 From: Karen_Mkhitaryan <Karen_Mkhitaryan@epam.com> Date: Mon, 6 Aug 2018 14:31:48 +0400 Subject: [PATCH 0745/1171] MAGETWO-91500: Custom role backend user cannot place an admin order using Braintree payment - for 2.2.x - Add automated test --- .../Mftf/ActionGroup/AdminRoleActionGroup.xml | 51 +++++++++++ .../Mftf/ActionGroup/AdminUserActionGroup.xml | 56 +++++++++++++ .../ConfigureBraintreeActionGroup.xml | 49 +++++++++++ .../ActionGroup/CreateCustomerActionGroup.xml | 44 ++++++++++ .../ActionGroup/CreateNewOrderActionGroup.xml | 66 +++++++++++++++ .../CreateNewProductActionGroup.xml | 25 ++++++ .../ActionGroup/DeleteCustomerActionGroup.xml | 28 +++++++ .../ActionGroup/DeleteProductActionGroup.xml | 26 ++++++ .../ActionGroup/SwitchAccountActionGroup.xml | 28 +++++++ .../Test/Mftf/Data/BraintreeData.xml | 36 ++++++++ .../Test/Mftf/Data/NewCustomerData.xml | 23 +++++ .../Test/Mftf/Data/NewProductData.xml | 17 ++++ .../Mftf/Section/AdminCreateRoleSection.xml | 24 ++++++ .../Mftf/Section/AdminCreateUserSection.xml | 23 +++++ .../Mftf/Section/AdminDeleteRoleSection.xml | 15 ++++ .../Mftf/Section/AdminDeleteUserSection.xml | 15 ++++ .../Mftf/Section/AdminEditRoleInfoSection.xml | 21 +++++ .../Mftf/Section/AdminEditUserRoleSection.xml | 17 ++++ .../Mftf/Section/AdminEditUserSection.xml | 28 +++++++ .../Test/Mftf/Section/AdminMenuSection.xml | 23 +++++ .../Mftf/Section/AdminRoleGridSection.xml | 17 ++++ .../Mftf/Section/AdminUserGridSection.xml | 17 ++++ .../Section/BraintreeConfiguraionSection.xml | 34 ++++++++ .../Mftf/Section/CatalogSubmenuSection.xml | 14 ++++ .../Mftf/Section/ConfigurationListSection.xml | 15 ++++ .../Section/ConfigurationPaymentSection.xml | 14 ++++ .../Mftf/Section/CustomersPageSection.xml | 19 +++++ .../Mftf/Section/CustomersSubmenuSection.xml | 14 ++++ .../Mftf/Section/NewCustomerPageSection.xml | 33 ++++++++ .../Test/Mftf/Section/NewOrderSection.xml | 35 ++++++++ .../Mftf/Section/NewProductPageSection.xml | 19 +++++ .../Test/Mftf/Section/ProductsPageSection.xml | 20 +++++ .../Mftf/Section/StoresSubmenuSection.xml | 14 ++++ .../Mftf/Section/SwitchAccountSection.xml | 24 ++++++ ...AnAdminOrderUsingBraintreePaymentTest1.xml | 84 +++++++++++++++++++ 35 files changed, 988 insertions(+) create mode 100644 app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminRoleActionGroup.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminUserActionGroup.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/ActionGroup/ConfigureBraintreeActionGroup.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateCustomerActionGroup.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateNewOrderActionGroup.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateNewProductActionGroup.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/ActionGroup/DeleteProductActionGroup.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/ActionGroup/SwitchAccountActionGroup.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Data/NewCustomerData.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Data/NewProductData.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/AdminCreateRoleSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/AdminCreateUserSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/AdminDeleteRoleSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/AdminDeleteUserSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/AdminEditRoleInfoSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/AdminEditUserRoleSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/AdminEditUserSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/AdminMenuSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/AdminRoleGridSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/AdminUserGridSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/BraintreeConfiguraionSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/CatalogSubmenuSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/ConfigurationListSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/ConfigurationPaymentSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/CustomersPageSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/CustomersSubmenuSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/NewCustomerPageSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/NewOrderSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/NewProductPageSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/ProductsPageSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/StoresSubmenuSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/SwitchAccountSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminRoleActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminRoleActionGroup.xml new file mode 100644 index 0000000000000..e86d5403e11eb --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminRoleActionGroup.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + + <actionGroup name="GoToUserRoles"> + <click selector="#menu-magento-backend-system" stepKey="clickOnSystemIcon"/> + <waitForPageLoad stepKey="waitForSystemsPageToOpen"/> + <click selector="//span[contains(text(), 'User Roles')]" stepKey="clickToSelectUserRoles"/> + <waitForPageLoad stepKey="waitForUserRolesPageToOpen"/> + </actionGroup> + + <!--Create new role--> + <actionGroup name="AdminCreateRole"> + <arguments> + <argument name="role" type="string" defaultValue=""/> + <argument name="resource" type="string" defaultValue="All"/> + <argument name="scope" type="string" defaultValue="Custom"/> + <argument name="websites" type="string" defaultValue="Main Website"/> + </arguments> + <click selector="{{AdminCreateRoleSection.create}}" stepKey="clickToAddNewRole"/> + <fillField selector="{{AdminCreateRoleSection.name}}" userInput="{{role.name}}" stepKey="setRoleName"/> + <fillField stepKey="setPassword" selector="{{AdminCreateRoleSection.password}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + <click selector="{{AdminCreateRoleSection.roleResources}}" stepKey="clickToOpenRoleResources"/> + <waitForPageLoad stepKey="waitForRoleResourcePage" time="5"/> + <click stepKey="checkSales" selector="//a[text()='Sales']"/> + <click selector="{{AdminCreateRoleSection.save}}" stepKey="clickToSaveRole"/> + <waitForPageLoad stepKey="waitForPageLoad" time="10"/> + <see userInput="You saved the role." stepKey="seeSuccessMessage" /> + </actionGroup> + + + <!--Delete role--> + <actionGroup name="AdminDeleteRoleActionGroup"> + <arguments> + <argument name="role" defaultValue=""/> + </arguments> + <click stepKey="clickOnRole" selector="{{AdminDeleteRoleSection.theRole}}"/> + <fillField stepKey="TypeCurrentPassword" selector="{{AdminDeleteRoleSection.current_pass}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + <click stepKey="clickToDeleteRole" selector="{{AdminDeleteRoleSection.delete}}"/> + <waitForAjaxLoad stepKey="waitForDeleteConfirmationPopup" time="5"/> + <click stepKey="clickToConfirm" selector="{{AdminDeleteRoleSection.confirm}}"/> + <waitForPageLoad stepKey="waitForPageLoad" time="10"/> + <see stepKey="seeSuccessMessage" userInput="You deleted the role."/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminUserActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminUserActionGroup.xml new file mode 100644 index 0000000000000..30ca3f38fe736 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminUserActionGroup.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + + <!--Go to all users--> + <actionGroup name="GoToAllUsers"> + <click selector="{{AdminCreateUserSection.system}}" stepKey="clickOnSystemIcon"/> + <waitForPageLoad stepKey="waitForSystemsPageToOpen"/> + <click selector="{{AdminCreateUserSection.allUsers}}" stepKey="clickToSelectUserRoles"/> + <waitForPageLoad stepKey="waitForUserRolesPageToOpen"/> + </actionGroup> + + <!--Create new user with specified role--> + <actionGroup name="AdminCreateUser"> + <click selector="{{AdminCreateUserSection.create}}" stepKey="clickToCreateNewUser"/> + <waitForPageLoad stepKey="waitForNewUserPageLoad" time="10"/> + <fillField selector="{{AdminCreateUserSection.usernameTextField}}" userInput="{{NewAdmin.username}}" stepKey="enterUserName" /> + <fillField selector="{{AdminCreateUserSection.firstNameTextField}}" userInput="{{NewAdmin.firstName}}" stepKey="enterFirstName" /> + <fillField selector="{{AdminCreateUserSection.lastNameTextField}}" userInput="{{NewAdmin.lastName}}" stepKey="enterLastName" /> + <fillField selector="{{AdminCreateUserSection.emailTextField}}" userInput="{{NewAdmin.email}}" stepKey="enterEmail" /> + <fillField selector="{{AdminCreateUserSection.passwordTextField}}" userInput="{{NewAdmin.password}}" stepKey="enterPassword" /> + <fillField selector="{{AdminCreateUserSection.pwConfirmationTextField}}" userInput="{{NewAdmin.password}}" stepKey="confirmPassword" /> + <fillField selector="{{AdminCreateUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterCurrentPassword" /> + <scrollToTopOfPage stepKey="scrollToTopOfPage" /> + <click selector="{{AdminCreateUserSection.userRoleTab}}" stepKey="clickUserRole" /> + <waitForAjaxLoad stepKey="waitForRoles" time="5"/> + <fillField selector="{{AdminCreateRoleSection.roleNameFilterTextField}}" userInput="{{role.name}}" stepKey="filterRole" /> + <click selector="{{AdminCreateRoleSection.searchButton}}" stepKey="clickSearch" /> + <waitForPageLoad stepKey="waitForSearch" time="10"/> + <click selector="{{AdminCreateRoleSection.searchResultFirstRow}}" stepKey="selectRole" /> + <click selector="{{AdminCreateUserSection.saveButton}}" stepKey="clickSaveUser" /> + <waitForPageLoad stepKey="waitForSaveUser" time="10"/> + <see userInput="You saved the user." stepKey="seeSuccessMessage" /> + </actionGroup> + + + <!--Delete User--> + <actionGroup name="AdminDeleteUserActionGroup"> + + <click stepKey="clickOnUser" selector="{{AdminDeleteUserSection.theUser}}"/> + <fillField stepKey="TypeCurrentPassword" selector="{{AdminDeleteUserSection.password}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click stepKey="clickToDeleteUser" selector="{{AdminDeleteUserSection.delete}}"/> + <waitForPageLoad stepKey="waitForDeletePopupOpen" time="5"/> + <click stepKey="clickToConfirm" selector="{{AdminDeleteUserSection.confirm}}"/> + <waitForPageLoad stepKey="waitForPageLoad" time="10"/> + <see userInput="You deleted the user." stepKey="seeSuccessMessage" /> + </actionGroup> + +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/ConfigureBraintreeActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/ConfigureBraintreeActionGroup.xml new file mode 100644 index 0000000000000..27e2039fe526e --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/ConfigureBraintreeActionGroup.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + + <actionGroup name="ConfigureBraintree"> + <!-- GoTo ConfigureBraintree fields --> + <click stepKey="clickOnSTORES" selector="{{AdminMenuSection.stores}}"/> + <waitForPageLoad stepKey="waitForConfiguration" time="2"/> + <click stepKey="clickOnConfigurations" selector="{{StoresSubmenuSection.configuration}}" /> + <waitForPageLoad stepKey="waitForSales" time="2"/> + <click stepKey="clickOnSales" selector="{{ConfigurationListSection.sales}}" /> + <waitForPageLoad stepKey="waitForPaymentMethods" time="2"/> + <click stepKey="clickOnPaymentMethods" selector="{{ConfigurationListSection.salesPaymentMethods}}" /> + <waitForPageLoad stepKey="waitForConfigureButton" time="2"/> + <click stepKey="clickOnConfigureButtonForBraintree" selector="{{ConfigurationPaymentSection.configureButton}}" /> + <waitForPageLoad stepKey="BraintreeSettings" time="2"/> + + <!-- Fill Braintree fields --> + <fillField stepKey="fillTitleForBraintreeSettings" selector="{{BraintreeConfiguraionSection.titleForBraintreeSettings}}" userInput="{{BraintreeConfigurationData.title}}"/> + <click stepKey="openEnvironmentSelect" selector="{{BraintreeConfiguraionSection.environment}}"/> + <click stepKey="chooseEnvironment" selector="{{BraintreeConfiguraionSection.sandbox}}"/> + <click stepKey="openPaymentActionSelect" selector="{{BraintreeConfiguraionSection.paymentActionSelect}}"/> + <click stepKey="choosePaymentAction" selector="{{BraintreeConfiguraionSection.paymentAction}}"/> + <fillField stepKey="fillMerchantID" selector="{{BraintreeConfiguraionSection.merchantID}}" userInput="{{BraintreeConfigurationData.merchantID}}"/> + <fillField stepKey="fillPublicKey" selector="{{BraintreeConfiguraionSection.publicKey}}" userInput="{{BraintreeConfigurationData.publicKey}}"/> + <fillField stepKey="fillPrivateKey" selector="{{BraintreeConfiguraionSection.privateKey}}" userInput="{{BraintreeConfigurationData.privateKey}}"/> + <click stepKey="expandEnableThisSolution" selector="{{BraintreeConfiguraionSection.enableThisSolution}}"/> + <click stepKey="chooseYesForEnableThisSolution" selector="{{BraintreeConfiguraionSection.yesForEnable}}"/> + <click stepKey="expandEnablePayPalThroughBraintree" selector="{{BraintreeConfiguraionSection.payPalThroughBraintree}}"/> + <click stepKey="chooseYesForEnablePayPalThroughBraintree" selector="{{BraintreeConfiguraionSection.yesForPayPalThroughBraintree}}"/> + <click stepKey="expandAdvancedBraintreeSettings" selector="{{BraintreeConfiguraionSection.advancedBraintreeSettings}}"/> + <fillField stepKey="fillMerchantAccountID" selector="{{BraintreeConfiguraionSection.merchantAccountID}}" userInput="{{BraintreeConfigurationData.merchantAccountID}}"/> + <click stepKey="expandCVVVerification" selector="{{BraintreeConfiguraionSection.CVVVerification}}"/> + <click stepKey="chooseYes" selector="{{BraintreeConfiguraionSection.yesForCVV}}"/> + <click stepKey="expandPayPalThroughBraintree" selector="{{BraintreeConfiguraionSection.payPalThroughBraintreeSelector}}"/> + <fillField stepKey="fillTitleForPayPalThroughBraintree" selector="{{BraintreeConfiguraionSection.titleForPayPalThroughBraintree}}" userInput="{{BraintreeConfigurationData.titleForPayPalThroughBraintree}}"/> + <click stepKey="expandPaymentAction" selector="{{BraintreeConfiguraionSection.paymentActionInPayPal}}"/> + <click stepKey="chooseAuthorize" selector="{{BraintreeConfiguraionSection.actionAuthorize}}"/> + <click stepKey="save" selector="{{BraintreeConfiguraionSection.save}}"/> + <waitForElementVisible selector="{{BraintreeConfiguraionSection.successfulMessage}}" stepKey="waitForSuccessfullyConfigured" time="10"/> + </actionGroup> + +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateCustomerActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateCustomerActionGroup.xml new file mode 100644 index 0000000000000..a68042127ec48 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateCustomerActionGroup.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateCustomerActionGroup"> + <click stepKey="openCustomers" selector="{{AdminMenuSection.customers}}"/> + <waitForAjaxLoad stepKey="waitForCatalogSubmenu" time="5"/> + <click stepKey="clickOnAllCustomers" selector="{{CustomersSubmenuSection.allCustomers}}"/> + <waitForPageLoad stepKey="waitForProductsPage" time="10"/> + <click stepKey="addNewCustomer" selector="{{CustomersPageSection.addNewCustomerButton}}"/> + <waitForPageLoad stepKey="waitForNewProductPage" time="10"/> + <click stepKey="AssociateToWebsite" selector="{{NewCustomerPageSection.associateToWebsite}}"/> + <click stepKey="Group" selector="{{NewCustomerPageSection.group}}"/> + <fillField stepKey="FillFirstName" selector="{{NewCustomerPageSection.firstName}}" userInput="{{NewCustomerData.FirstName}}"/> + <fillField stepKey="FillLastName" selector="{{NewCustomerPageSection.lastName}}" userInput="{{NewCustomerData.LastName}}"/> + <fillField stepKey="FillEmail" selector="{{NewCustomerPageSection.email}}" userInput="{{NewCustomerData.Email}}"/> + <scrollToTopOfPage stepKey="scrollToAddresses"/> + <click stepKey="goToAddresses" selector="{{NewCustomerPageSection.addresses}}"/> + <waitForAjaxLoad stepKey="waitForAddresses" time="5"/> + <click stepKey="AddNewAddress" selector="{{NewCustomerPageSection.addNewAddress}}"/> + <waitForPageLoad stepKey="waitForAddressFields" time="5"/> + <click stepKey="thickBillingAddress" selector="{{NewCustomerPageSection.defaultBillingAddress}}"/> + <click stepKey="thickShippingAddress" selector="{{NewCustomerPageSection.defaultShippingAddress}}"/> + <fillField stepKey="fillFirstNameForAddress" selector="{{NewCustomerPageSection.firstNameForAddress}}" userInput="{{NewCustomerData.AddressFirstName}}"/> + <fillField stepKey="fillLastNameForAddress" selector="{{NewCustomerPageSection.lastNameForAddress}}" userInput="{{NewCustomerData.AddressLastName}}"/> + <fillField stepKey="fillStreetAddress" selector="{{NewCustomerPageSection.streetAddress}}" userInput="{{NewCustomerData.StreetAddress}}"/> + <fillField stepKey="fillCity" selector="{{NewCustomerPageSection.city}}" userInput="{{NewCustomerData.City}}"/> + <click stepKey="openCountry" selector="{{NewCustomerPageSection.country}}"/> + <waitForAjaxLoad stepKey="waitForCountryList" time="5"/> + <click stepKey="chooseCountry" selector="{{NewCustomerPageSection.countryArmenia}}"/> + <fillField stepKey="fillZip" selector="{{NewCustomerPageSection.zip}}" userInput="{{NewCustomerData.Zip}}"/> + <fillField stepKey="fillPhoneNumber" selector="{{NewCustomerPageSection.phoneNumber}}" userInput="{{NewCustomerData.PhoneNumber}}"/> + <waitForPageLoad stepKey="wait" time="10"/> + <click stepKey="save" selector="{{NewCustomerPageSection.saveCustomer}}"/> + <waitForPageLoad stepKey="waitForCustomersPage" time="10"/> + <waitForElementVisible selector="{{NewCustomerPageSection.createdSuccessMessage}}" stepKey="waitForSuccessfullyCreatedMessage" time="20"/> + + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateNewOrderActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateNewOrderActionGroup.xml new file mode 100644 index 0000000000000..ee7158c2b63f7 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateNewOrderActionGroup.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + + <actionGroup name="CreateNewOrderActionGroup"> + <click stepKey="createNewOrder" selector="{{NewOrderSection.createNewOrder}}"/> + <waitForPageLoad stepKey="waitForCustomersList" time="3"/> + <click stepKey="chooseCustomer" selector="{{NewOrderSection.customer}}"/> + <waitForPageLoad stepKey="waitForOrderPage" time="3"/> + <click stepKey="addProducts" selector="{{NewOrderSection.addProducts}}"/> + <waitForPageLoad stepKey="waitForProducts" time="3"/> + <click stepKey="chooseProducts" selector="{{NewOrderSection.chooseProduct}}"/> + <waitForPageLoad stepKey="waitForProductChoose" time="3"/> + <click stepKey="addSelectedProduct" selector="{{NewOrderSection.addSelectedProduct}}"/> + <waitForAjaxLoad stepKey="waitForChoose" time="3"/> + <click stepKey="openAddresses" selector="{{NewOrderSection.openAddresses}}"/> + <click stepKey="chooseAddress" selector="{{NewOrderSection.chooseAddress}}"/> + <fillField stepKey="fillState" selector="{{NewOrderSection.state}}" userInput="Yerevan"/> + <scrollTo stepKey="scrollTo" selector="#order-methods"/> + <waitForPageLoad stepKey="waitForMethods" time="3"/> + <click stepKey="startJSMethodExecution" selector="{{NewOrderSection.openShippingMethods}}"/> + <waitForPageLoad stepKey="waitForJSMethodExecution" time="3"/> + <click stepKey="openShippingMethods" selector="{{NewOrderSection.openShippingMethods}}"/> + <waitForPageLoad stepKey="waitForShippingMethods" time="3"/> + <click stepKey="chooseShippingMethods" selector="{{NewOrderSection.shippingMethod}}"/> + <waitForPageLoad stepKey="waitForShippingMethodChoose" time="4"/> + <click stepKey="chooseBraintree" selector="{{NewOrderSection.creditCardBraintree}}"/> + <waitForPageLoad stepKey="waitForBraintreeConfigs" time="5"/> + <click stepKey="openCardTypes" selector="{{NewOrderSection.openCardTypes}}"/> + <waitForPageLoad stepKey="waitForCardTypes" time="3"/> + <click stepKey="chooseCardType" selector="{{NewOrderSection.masterCard}}"/> + <waitForPageLoad stepKey="waitForCardSelected" time="3"/> + + <switchToIFrame stepKey="switchToCardNumber" selector="{{NewOrderSection.cardFrame}}"/> + <fillField stepKey="fillCardNumber" selector="{{NewOrderSection.creditCardNumber}}" userInput="{{PaymentAndShippingInfo.cardNumber}}"/> + <waitForPageLoad stepKey="waitForFillCardNumber" time="1"/> + <switchToIFrame stepKey="switchBackFromCard"/> + + <switchToIFrame stepKey="switchToExpirationMonth" selector="{{NewOrderSection.monthFrame}}"/> + <fillField stepKey="fillMonth" selector="{{NewOrderSection.expirationMonth}}" userInput="{{PaymentAndShippingInfo.month}}"/> + <waitForPageLoad stepKey="waitForFillMonth" time="1"/> + <switchToIFrame stepKey="switchBackFromMonth"/> + + <switchToIFrame stepKey="switchToExpirationYear" selector="{{NewOrderSection.yearFrame}}"/> + <fillField stepKey="fillYear" selector="{{NewOrderSection.expirationYear}}" userInput="{{PaymentAndShippingInfo.year}}"/> + <waitForPageLoad stepKey="waitForFillYear" time="1"/> + <switchToIFrame stepKey="switchBackFromYear"/> + + <switchToIFrame stepKey="switchToCVV" selector="{{NewOrderSection.cvvFrame}}"/> + <fillField stepKey="fillCVV" selector="{{NewOrderSection.cvv}}" userInput="{{PaymentAndShippingInfo.cvv}}"/> + <wait stepKey="waitForFillCVV" time="1"/> + <switchToIFrame stepKey="switchBackFromCVV"/> + + <click stepKey="submitOrder" selector="{{NewOrderSection.submitOrder}}"/> + <waitForPageLoad stepKey="waitForSaveConfig" time="5"/> + <waitForElementVisible selector="{{NewOrderSection.successMessage}}" stepKey="waitForSuccessMessage" time="1"/> + + </actionGroup> + +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateNewProductActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateNewProductActionGroup.xml new file mode 100644 index 0000000000000..19de3e859ae9a --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateNewProductActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateNewProductActionGroup"> + + <click stepKey="openCatalog" selector="{{AdminMenuSection.catalog}}"/> + <waitForPageLoad stepKey="waitForCatalogSubmenu" time="5"/> + <click stepKey="clickOnProducts" selector="{{CatalogSubmenuSection.products}}"/> + <waitForPageLoad stepKey="waitForProductsPage" time="10"/> + <click stepKey="addProduct" selector="{{ProductsPageSection.addProductButton}}"/> + <waitForPageLoad stepKey="waitForNewProductPage" time="10"/> + <fillField stepKey="FillProductName" selector="{{NewProductPageSection.productName}}" userInput="{{NewProductData.ProductName}}"/> + <fillField stepKey="FillPrice" selector="{{NewProductPageSection.price}}" userInput="{{NewProductData.Price}}"/> + <fillField stepKey="FillQuantity" selector="{{NewProductPageSection.quantity}}" userInput="{{NewProductData.Quantity}}"/> + <click stepKey="Save" selector="{{NewProductPageSection.saveButton}}"/> + <waitForElementVisible stepKey="waitForSuccessfullyCreatedMessage" selector="{{NewProductPageSection.createdSuccessMessage}}" time="10"/> + <waitForPageLoad stepKey="waitForPageLoad" time="10"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml new file mode 100644 index 0000000000000..65eddc0d9e51a --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteCustomerActionGroup"> + <arguments> + <argument name="lastName" defaultValue=""/> + </arguments> + <click stepKey="openCustomers" selector="{{AdminMenuSection.customers}}"/> + <waitForPageLoad stepKey="waitForCatalogSubmenu" time="10"/> + <click stepKey="clickOnAllCustomers" selector="{{CustomersSubmenuSection.allCustomers}}"/> + <waitForPageLoad stepKey="waitForProductsPage" time="10"/> + <click stepKey="chooseCustomer" selector="{{CustomersPageSection.customerCheckbox(lastName)}}"/> + <waitForAjaxLoad stepKey="waitForThick" time="2"/> + <click stepKey="OpenActions" selector="{{CustomersPageSection.actions}}"/> + <waitForAjaxLoad stepKey="waitForDelete" time="5"/> + <click stepKey="ChooseDelete" selector="{{CustomersPageSection.delete}}"/> + <waitForPageLoad stepKey="waitForDeleteItemPopup" time="10"/> + <click stepKey="clickOnOk" selector="{{CustomersPageSection.ok}}"/> + <waitForElementVisible stepKey="waitForSuccessfullyDeletedMessage" selector="{{CustomersPageSection.deletedSuccessMessage}}" time="10"/> + + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/DeleteProductActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/DeleteProductActionGroup.xml new file mode 100644 index 0000000000000..724c6d92846c4 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/DeleteProductActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteProductActionGroup"> + <arguments> + <argument name="productName" defaultValue=""/> + </arguments> + <click stepKey="openCatalog" selector="{{AdminMenuSection.catalog}}"/> + <waitForPageLoad stepKey="waitForCatalogSubmenu" time="5"/> + <click stepKey="clickOnProducts" selector="{{CatalogSubmenuSection.products}}"/> + <waitForPageLoad stepKey="waitForProductsPage" time="10"/> + <click stepKey="TickCheckbox" selector="{{ProductsPageSection.checkboxForProduct(productName)}}"/> + <click stepKey="OpenActions" selector="{{ProductsPageSection.actions}}"/> + <waitForAjaxLoad stepKey="waitForDelete" time="5"/> + <click stepKey="ChooseDelete" selector="{{ProductsPageSection.delete}}"/> + <waitForPageLoad stepKey="waitForDeleteItemPopup" time="10"/> + <click stepKey="clickOnOk" selector="{{ProductsPageSection.ok}}"/> + <waitForElementVisible stepKey="waitForSuccessfullyDeletedMessage" selector="{{ProductsPageSection.deletedSuccessMessage}}" time="10"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/SwitchAccountActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/SwitchAccountActionGroup.xml new file mode 100644 index 0000000000000..7c774a634b369 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/SwitchAccountActionGroup.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + + <!--Sign out--> + <actionGroup name="SignOut"> + <click selector="{{SignOutSection.admin}}" stepKey="clickToAdminProfile"/> + <click selector="{{SignOutSection.logout}}" stepKey="clickToLogOut"/> + <waitForPageLoad stepKey="waitForPageLoad" /> + <see userInput="You have logged out." stepKey="seeSuccessMessage" /> + <waitForElementVisible selector="//*[@data-ui-id='messages-message-success']" stepKey="waitForSuccessMessageLoggedOut" time="5"/> + </actionGroup> + + <!--Login New User--> + <actionGroup name="LoginNewUser"> + <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}" stepKey="navigateToAdmin"/> + <fillField userInput="{{NewAdmin.username}}" selector="{{LoginFormSection.username}}" stepKey="fillUsername"/> + <fillField userInput="{{NewAdmin.password}}" selector="{{LoginFormSection.password}}" stepKey="fillPassword"/> + <click selector="{{LoginFormSection.signIn}}" stepKey="clickLogin"/> + </actionGroup> + +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml index 6e669a1b8bf4b..208c8b25d8d64 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml @@ -62,4 +62,40 @@ <entity name="DefaultPrivateKey" type="private_key"> <data key="value"/> </entity> + + <entity name="testData" type="data"> + <data key="websiteName" unique="suffix">Website</data> + <data key="websiteCode" unique="suffix">new_website</data> + <data key="name" unique="suffix">Store</data> + <data key="storeCode" unique="suffix">new_store</data> + <data key="block" unique="suffix">Block</data> + </entity> + + <entity name="role" type="data"> + <data key="name" unique="suffix">Role</data> + </entity> + <entity name="NewAdmin" type="user"> + <data key="username" unique="suffix">admin</data> + <data key="firstName">John</data> + <data key="lastName">Smith</data> + <data key="password">admin123</data> + <data key="email">mail@mail.com</data> + </entity> + + <entity name="PaymentAndShippingInfo" type="data"> + <data key="cardNumber">5105105105105100</data> + <data key="month">12</data> + <data key="year">20</data> + <data key="cvv">113</data> + </entity> + + <entity name="BraintreeConfigurationData" type="data"> + <data key="title">Credit Card (Braintree)</data> + <data key="merchantID">d4pdjhxgjfrsmzbf</data> + <data key="publicKey">m7q4wmh43xrgyrst</data> + <data key="privateKey">67de364080b1b4e2492d7a3de413a572</data> + <data key="merchantAccountID">Magneto</data> + <data key="titleForPayPalThroughBraintree">PayPal (Braintree)</data> + </entity> + </entities> diff --git a/app/code/Magento/Braintree/Test/Mftf/Data/NewCustomerData.xml b/app/code/Magento/Braintree/Test/Mftf/Data/NewCustomerData.xml new file mode 100644 index 0000000000000..772c1c39a04ca --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Data/NewCustomerData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="NewCustomerData" type="braintree_config_state"> + <data key="FirstName">Abgar</data> + <data key="LastName">Abgaryan</data> + <data key="Email">m@m.com</data> + <data key="AddressFirstName">Abgar</data> + <data key="AddressLastName">Abgaryan</data> + <data key="StreetAddress">Street</data> + <data key="City">Yerevan</data> + <data key="Zip">9999</data> + <data key="PhoneNumber">9999</data> + </entity> + +</entities> diff --git a/app/code/Magento/Braintree/Test/Mftf/Data/NewProductData.xml b/app/code/Magento/Braintree/Test/Mftf/Data/NewProductData.xml new file mode 100644 index 0000000000000..72661ae94076f --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Data/NewProductData.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="NewProductData" type="braintree_config_state"> + <data key="ProductName">ProductTest</data> + <data key="Price">100</data> + <data key="Quantity">100</data> + </entity> + +</entities> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/AdminCreateRoleSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/AdminCreateRoleSection.xml new file mode 100644 index 0000000000000..1158f471d51f0 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/AdminCreateRoleSection.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminCreateRoleSection"> + <element name="create" type="button" selector="#add"/> + <element name="name" type="button" selector="#role_name"/> + <element name="password" type="input" selector="#current_password"/> + <element name="roleResources" type="button" selector="#role_info_tabs_account"/> + <element name="roleResource" type="button" selector="#gws_is_all"/> + <element name="resourceValue" type="button" selector="//*[text()='{{arg1}}']" parameterized="true"/> + <element name="roleScope" type="button" selector="#all"/> + <element name="scopeValue" type="button" selector="//select[@id='all']/*[text()='{{arg2}}']" parameterized="true"/> + <element name="website" type="checkbox" selector="//*[contains(text(), '{{arg3}}')]" parameterized="true"/> + <element name="save" type="button" selector="//button[@title='Save Role']"/> + <element name="roleNameFilterTextField" type="input" selector="#permissionsUserRolesGrid_filter_role_name"/> + <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]"/> + <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/AdminCreateUserSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/AdminCreateUserSection.xml new file mode 100644 index 0000000000000..98d748b5a30ea --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/AdminCreateUserSection.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminCreateUserSection"> + <element name="system" type="input" selector="#menu-magento-backend-system"/> + <element name="allUsers" type="input" selector="//span[contains(text(), 'All Users')]"/> + <element name="create" type="input" selector="#add"/> + <element name="usernameTextField" type="input" selector="#user_username"/> + <element name="firstNameTextField" type="input" selector="#user_firstname"/> + <element name="lastNameTextField" type="input" selector="#user_lastname"/> + <element name="emailTextField" type="input" selector="#user_email"/> + <element name="passwordTextField" type="input" selector="#user_password"/> + <element name="pwConfirmationTextField" type="input" selector="#user_confirmation"/> + <element name="currentPasswordField" type="input" selector="#user_current_password"/> + <element name="userRoleTab" type="button" selector="#page_tabs_roles_section"/> + <element name="saveButton" type="button" selector="#save"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/AdminDeleteRoleSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/AdminDeleteRoleSection.xml new file mode 100644 index 0000000000000..220c9a444b02f --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/AdminDeleteRoleSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminDeleteRoleSection"> + <element name="theRole" selector="//td[contains(text(), 'Role')]" type="button"/> + <element name="current_pass" type="button" selector="#current_password"/> + <element name="delete" selector="//button/span[contains(text(), 'Delete Role')]" type="button"/> + <element name="confirm" selector="//*[@class='action-primary action-accept']" type="button"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/AdminDeleteUserSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/AdminDeleteUserSection.xml new file mode 100644 index 0000000000000..bf2e2b44eb602 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/AdminDeleteUserSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminDeleteUserSection"> + <element name="theUser" selector="//td[contains(text(), 'John')]" type="button"/> + <element name="password" selector="#user_current_password" type="input"/> + <element name="delete" selector="//button/span[contains(text(), 'Delete User')]" type="button"/> + <element name="confirm" selector="//*[@class='action-primary action-accept']" type="button"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/AdminEditRoleInfoSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/AdminEditRoleInfoSection.xml new file mode 100644 index 0000000000000..e37ce8f4738b3 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/AdminEditRoleInfoSection.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminEditRoleInfoSection"> + <element name="roleName" type="input" selector="#role_name"/> + <element name="password" type="input" selector="#current_password"/> + <element name="roleResourcesTab" type="button" selector="#role_info_tabs_account"/> + <element name="backButton" type="button" selector="button[title='Back']"/> + <element name="resetButton" type="button" selector="button[title='Reset']"/> + <element name="deleteButton" type="button" selector="button[title='Delete Role']"/> + <element name="saveButton" type="button" selector="button[title='Save Role']"/> + <element name="message" type="text" selector=".modal-popup.confirm div.modal-content"/> + <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> + <element name="ok" type="button" selector=".modal-popup.confirm button.action-accept" timeout="60"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/AdminEditUserRoleSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/AdminEditUserRoleSection.xml new file mode 100644 index 0000000000000..e999413c96d74 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/AdminEditUserRoleSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminEditUserRoleSection"> + <element name="usernameTextField" type="input" selector="#user_username"/> + <element name="roleNameFilterTextField" type="input" selector="#permissionsUserRolesGrid_filter_role_name"/> + <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]"/> + <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> + <element name="roleNameInFirstRow" type="text" selector=".col-role_name"/> + <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/AdminEditUserSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/AdminEditUserSection.xml new file mode 100644 index 0000000000000..2e5fcfb7b5c8d --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/AdminEditUserSection.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminEditUserSection"> + <element name="system" type="input" selector="#menu-magento-backend-system"/> + <element name="allUsers" type="input" selector="//span[contains(text(), 'All Users')]"/> + <element name="create" type="input" selector="#add"/> + <element name="usernameTextField" type="input" selector="#user_username"/> + <element name="firstNameTextField" type="input" selector="#user_firstname"/> + <element name="lastNameTextField" type="input" selector="#user_lastname"/> + <element name="emailTextField" type="input" selector="#user_email"/> + <element name="passwordTextField" type="input" selector="#user_password"/> + <element name="pwConfirmationTextField" type="input" selector="#user_confirmation"/> + <element name="currentPasswordField" type="input" selector="#user_current_password"/> + <element name="userRoleTab" type="button" selector="#page_tabs_roles_section"/> + <element name="roleNameFilterTextField" type="input" selector="#permissionsUserRolesGrid_filter_role_name"/> + <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]"/> + <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> + <element name="roleNameInFirstRow" type="text" selector=".col-role_name"/> + <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> + <element name="saveButton" type="button" selector="#save"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/AdminMenuSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/AdminMenuSection.xml new file mode 100644 index 0000000000000..660c7393b4061 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/AdminMenuSection.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminMenuSection"> + <element name="dashboard" type="button" selector="//li[@id='menu-magento-backend-dashboard']"/> + <element name="sales" type="button" selector="//li[@id='menu-magento-sales-sales']"/> + <element name="catalog" type="button" selector="//li[@id='menu-magento-catalog-catalog']"/> + <element name="customers" type="button" selector="//li[@id='menu-magento-customer-customer']"/> + <element name="marketing" type="button" selector="//li[@id='//li[@id='menu-magento-backend-marketing']']"/> + <element name="content" type="button" selector="//li[@id='menu-magento-backend-content']"/> + <element name="reports" type="button" selector="//li[@id='menu-magento-reports-report']"/> + <element name="stores" type="button" selector="//li[@id='menu-magento-backend-stores']"/> + <element name="system" type="button" selector="//li[@id='menu-magento-backend-system']"/> + <element name="findPartners" type="button" selector="//li[@id='menu-magento-marketplace-partners']"/> + </section> +</sections> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/AdminRoleGridSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/AdminRoleGridSection.xml new file mode 100644 index 0000000000000..63cbadc71d3d3 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/AdminRoleGridSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminRoleGridSection"> + <element name="idFilterTextField" type="input" selector="#roleGrid_filter_role_id"/> + <element name="roleNameFilterTextField" type="input" selector="#roleGrid_filter_role_name"/> + <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]"/> + <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> + <element name="roleNameInFirstRow" type="text" selector=".col-role_name"/> + <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/AdminUserGridSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/AdminUserGridSection.xml new file mode 100644 index 0000000000000..9564bc61f799c --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/AdminUserGridSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminUserGridSection"> + <element name="usernameFilterTextField" type="input" selector="#permissionsUserGrid_filter_username"/> + <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]"/> + <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> + <element name="usernameInFirstRow" type="text" selector=".col-username"/> + <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> + <element name="successMessage" type="text" selector=".message-success"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/BraintreeConfiguraionSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/BraintreeConfiguraionSection.xml new file mode 100644 index 0000000000000..016af2e102744 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/BraintreeConfiguraionSection.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="BraintreeConfiguraionSection"> + <element name="titleForBraintreeSettings" type="input" selector="//input[@id='payment_us_braintree_section_braintree_braintree_required_title']"/> + <element name="environment" type="select" selector="//select[@id='payment_us_braintree_section_braintree_braintree_required_environment']"/> + <element name="sandbox" type="select" selector="//select[@id='payment_us_braintree_section_braintree_braintree_required_environment']/option[text()='Sandbox']"/> + <element name="paymentActionSelect" type="select" selector="//select[@id='payment_us_braintree_section_braintree_braintree_required_payment_action']"/> + <element name="paymentAction" type="button" selector="//select[@id='payment_us_braintree_section_braintree_braintree_required_payment_action']/option[text()='Authorize']"/> + <element name="merchantID" type="input" selector="//input[@id='payment_us_braintree_section_braintree_braintree_required_merchant_id']"/> + <element name="publicKey" type="input" selector="//input[@id='payment_us_braintree_section_braintree_braintree_required_public_key']"/> + <element name="privateKey" type="input" selector="//input[@id='payment_us_braintree_section_braintree_braintree_required_private_key']"/> + <element name="enableThisSolution" type="select" selector="//select[@id='payment_us_braintree_section_braintree_active']"/> + <element name="yesForEnable" type="button" selector="//select[@id='payment_us_braintree_section_braintree_active']/option[text()='Yes']"/> + <element name="payPalThroughBraintree" type="select" selector="//select[@id='payment_us_braintree_section_braintree_active_braintree_paypal']"/> + <element name="yesForPayPalThroughBraintree" type="input" selector="//select[@id='payment_us_braintree_section_braintree_active_braintree_paypal']/option[text()='Yes']"/> + <element name="advancedBraintreeSettings" type="button" selector="//a[@id='payment_us_braintree_section_braintree_braintree_advanced-head']"/> + <element name="merchantAccountID" type="text" selector="//input[@id='payment_us_braintree_section_braintree_braintree_advanced_merchant_account_id']"/> + <element name="CVVVerification" type="text" selector="//select[@id='payment_us_braintree_section_braintree_braintree_advanced_useccv']"/> + <element name="yesForCVV" type="text" selector="//select[@id='payment_us_braintree_section_braintree_braintree_advanced_useccv']/option[text()='Yes']"/> + <element name="payPalThroughBraintreeSelector" type="text" selector="//a[@id='payment_us_braintree_section_braintree_braintree_paypal-head']"/> + <element name="titleForPayPalThroughBraintree" type="text" selector="//input[@id='payment_us_braintree_section_braintree_braintree_paypal_title']"/> + <element name="paymentActionInPayPal" type="text" selector="//select[@id='payment_us_braintree_section_braintree_braintree_paypal_payment_action']"/> + <element name="actionAuthorize" type="text" selector="//select[@id='payment_us_braintree_section_braintree_braintree_paypal_payment_action']/option[text()='Authorize']"/> + <element name="save" type="button" selector="//span[text()='Save Config']"/> + <element name="successfulMessage" type="text" selector="//*[@data-ui-id='messages-message-success']"/> + + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/CatalogSubmenuSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/CatalogSubmenuSection.xml new file mode 100644 index 0000000000000..32f02a69f817e --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/CatalogSubmenuSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="CatalogSubmenuSection"> + <element name="products" type="button" selector="//li[@id='menu-magento-catalog-catalog']//li[@data-ui-id='menu-magento-catalog-catalog-products']"/> + </section> +</sections> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/ConfigurationListSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/ConfigurationListSection.xml new file mode 100644 index 0000000000000..100407438eaae --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/ConfigurationListSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="ConfigurationListSection"> + <element name="sales" type="button" selector="//div[contains(@class, 'admin__page-nav-title title _collapsible')]/strong[text()='Sales']"/> + <element name="salesPaymentMethods" type="button" selector="//span[text()='Payment Methods']"/> + </section> +</sections> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/ConfigurationPaymentSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/ConfigurationPaymentSection.xml new file mode 100644 index 0000000000000..885a45be721f1 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/ConfigurationPaymentSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="ConfigurationPaymentSection"> + <element name="configureButton" type="button" selector="//button[@id='payment_us_braintree_section_braintree-head']"/> + </section> +</sections> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/CustomersPageSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/CustomersPageSection.xml new file mode 100644 index 0000000000000..e4a75b1b6a842 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/CustomersPageSection.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="CustomersPageSection"> + <element name="addNewCustomerButton" type="button" selector="//*[@id='add']"/> + <element name="customerCheckbox" type="button" selector="//*[contains(text(),'{{args}}')]/parent::td/preceding-sibling::td/label[@class='data-grid-checkbox-cell-inner']" parameterized="true"/> + <element name="actions" type="button" selector="//div[@class='col-xs-2']/div[@class='action-select-wrap']/button[@class='action-select']"/> + <element name="delete" type="button" selector="//*[contains(@class,'admin__data-grid-header-row row row-gutter')]//*[text()='Delete']"/> + <element name="ok" type="button" selector="//button[@data-role='action']//span[text()='OK']"/> + <element name="deletedSuccessMessage" type="button" selector="//*[@class='message message-success success']"/> + </section> +</sections> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/CustomersSubmenuSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/CustomersSubmenuSection.xml new file mode 100644 index 0000000000000..937afb83da96f --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/CustomersSubmenuSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="CustomersSubmenuSection"> + <element name="allCustomers" type="button" selector="//li[@id='menu-magento-customer-customer']//li[@data-ui-id='menu-magento-customer-customer-manage']"/> + </section> +</sections> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/NewCustomerPageSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/NewCustomerPageSection.xml new file mode 100644 index 0000000000000..d302f9c7d0cba --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/NewCustomerPageSection.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="NewCustomerPageSection"> + <element name="associateToWebsite" type="select" selector="//*[@class='admin__field-control _with-tooltip']//*[@class='admin__control-select']"/> + <element name="group" type="select" selector="//div[@class='admin__field-control admin__control-fields required']//div[@class='admin__field-control']//select[@class='admin__control-select']"/> + <element name="firstName" type="input" selector="//input[@name='customer[firstname]']"/> + <element name="lastName" type="input" selector="//input[@name='customer[lastname]']"/> + <element name="email" type="input" selector="//input[@name='customer[email]']"/> + <element name="addresses" type="button" selector="//a[@id='tab_address']"/> + <element name="addNewAddress" type="button" selector="//span[text()='Add New Addresses']"/> + <element name="defaultBillingAddress" type="button" selector="//label[text()='Default Billing Address']"/> + <element name="defaultShippingAddress" type="button" selector="//label[text()='Default Shipping Address']"/> + <element name="firstNameForAddress" type="button" selector="//input[contains(@name, 'address')][contains(@name, 'firstname')]"/> + <element name="lastNameForAddress" type="button" selector="//input[contains(@name, 'address')][contains(@name, 'lastname')]"/> + <element name="streetAddress" type="button" selector="//input[contains(@name, 'street')]"/> + <element name="city" type="input" selector="//input[contains(@name, 'city')]"/> + <element name="country" type="select" selector="//select[contains(@name, 'country_id')]"/> + <element name="countryArmenia" type="select" selector="//select[contains(@name, 'country_id')]//option[@data-title='Armenia']"/> + <element name="zip" type="input" selector="//input[contains(@name, 'postcode')]"/> + <element name="phoneNumber" type="input" selector="//input[contains(@name, 'telephone')]"/> + <element name="saveCustomer" type="button" selector="//button[@title='Save Customer']"/> + <element name="createdSuccessMessage" type="button" selector="//div[@data-ui-id='messages-message-success']"/> + + </section> +</sections> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/NewOrderSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/NewOrderSection.xml new file mode 100644 index 0000000000000..13f59ad2cf18e --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/NewOrderSection.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="NewOrderSection"> + <element name="createNewOrder" type="button" selector="#add"/> + <element name="customer" type="button" selector="//td[contains(text(), 'Abgar')]"/> + <element name="addProducts" type="button" selector="#add_products"/> + <element name="chooseProduct" type="button" selector="//td[contains(text(),'ProductTest')]"/> + <element name="addSelectedProduct" type="button" selector="//button[@title='Add Selected Product(s) to Order']"/> + <element name="openAddresses" type="button" selector="#order-billing_address_customer_address_id"/> + <element name="chooseAddress" type="button" selector="//select[@id='order-billing_address_customer_address_id']//option[contains(text(), 'Abgar')]"/> + <element name="state" type="button" selector="#order-billing_address_region"/> + <element name="openShippingMethods" type="button" selector="//a[@class='action-default']"/> + <element name="shippingMethod" type="button" selector="//label[@class='admin__field-label' and @for='s_method_flatrate_flatrate']"/> + <element name="creditCardBraintree" type="button" selector="#p_method_braintree"/> + <element name="openCardTypes" type="button" selector="#braintree_cc_type"/> + <element name="masterCard" type="button" selector="//option[contains(text(), 'MasterCard')]"/> + <element name="cardFrame" type="iframe" selector="braintree-hosted-field-number"/> + <element name="monthFrame" type="iframe" selector="braintree-hosted-field-expirationMonth"/> + <element name="yearFrame" type="iframe" selector="braintree-hosted-field-expirationYear"/> + <element name="cvvFrame" type="iframe" selector="braintree-hosted-field-cvv"/> + <element name="creditCardNumber" type="input" selector="#credit-card-number"/> + <element name="expirationMonth" type="input" selector="#expiration-month"/> + <element name="expirationYear" type="input" selector="#expiration-year"/> + <element name="cvv" type="input" selector="#cvv"/> + <element name="submitOrder" type="input" selector="#submit_order_top_button"/> + <element name="successMessage" type="input" selector="#messages"/> + + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/NewProductPageSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/NewProductPageSection.xml new file mode 100644 index 0000000000000..42e451940c91b --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/NewProductPageSection.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="NewProductPageSection"> + <element name="productName" type="input" selector="//input[@name='product[name]']"/> + <element name="sku" type="input" selector="//input[@name='product[sku]']"/> + <element name="price" type="input" selector="//input[@name='product[price]']"/> + <element name="quantity" type="input" selector="//input[@name='product[quantity_and_stock_status][qty]']"/> + <element name="saveButton" type="button" selector="//button[@id='save-button']"/> + <element name="createdSuccessMessage" type="button" selector="//div[@data-ui-id='messages-message-success']"/> + </section> +</sections> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/ProductsPageSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/ProductsPageSection.xml new file mode 100644 index 0000000000000..267efdf3d0e5e --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/ProductsPageSection.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="ProductsPageSection"> + <element name="addProductButton" type="button" selector="//button[@id='add_new_product-button']"/> + <element name="checkboxForProduct" type="button" selector="//*[contains(text(),'{{args}}')]/parent::td/preceding-sibling::td/label[@class='data-grid-checkbox-cell-inner']" parameterized="true"/> + <element name="actions" type="button" selector="//div[@class='col-xs-2']/div[@class='action-select-wrap']/button[@class='action-select']"/> + <element name="delete" type="button" selector="//*[contains(@class,'admin__data-grid-header-row row row-gutter')]//*[text()='Delete']"/> + <element name="ok" type="button" selector="//button[@data-role='action']//span[text()='OK']"/> + <element name="deletedSuccessMessage" type="button" selector="//*[@class='message message-success success']"/> + + </section> +</sections> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/StoresSubmenuSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/StoresSubmenuSection.xml new file mode 100644 index 0000000000000..f094baa9f3446 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/StoresSubmenuSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="StoresSubmenuSection"> + <element name="configuration" type="button" selector="//li[@id='menu-magento-backend-stores']//li[@data-ui-id='menu-magento-config-system-config']"/> + </section> +</sections> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/SwitchAccountSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/SwitchAccountSection.xml new file mode 100644 index 0000000000000..3a07cbc6dd145 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/SwitchAccountSection.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + + <section name="LoginFormSection"> + <element name="username" type="input" selector="#username"/> + <element name="password" type="input" selector="#login"/> + <element name="signIn" type="button" selector=".actions .action-primary" timeout="30"/> + </section> + + <section name="SignOutSection"> + <element name="admin" type="button" selector=".admin__action-dropdown-text"/> + <element name="logout" type="button" selector="//*[contains(text(), 'Sign Out')]"/> + </section> + +</sections> + diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml new file mode 100644 index 0000000000000..4836c49d354b4 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="CreateAnAdminOrderUsingBraintreePaymentTest1"> + <annotations> + <features value="Backend"/> + <stories value="Creation an admin order using Braintree payment"/> + <title value="Create order using Braintree payment"/> + <description value="Admin should be able to create order using Braintree payment"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-93677"/> + <group value="braintree"/> + </annotations> + + + <before> + <!--Login As Admin--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!--CreateNewProduct--> + <actionGroup ref="CreateNewProductActionGroup" stepKey="CreateNewProduct"/> + + <!--Create New Customer--> + <actionGroup ref="CreateCustomerActionGroup" stepKey="CreateCustomer"/> + + </before> + + + <!--Configure Braintree--> + <actionGroup ref="ConfigureBraintree" stepKey="configureBraintree"/> + + <!--Create New Role--> + <actionGroup ref="GoToUserRoles" stepKey="GoToUserRoles"/> + <actionGroup ref="AdminCreateRole" stepKey="AdminCreateNewRole"/> + + <!--Create New User With Specific Role--> + <actionGroup ref="GoToAllUsers" stepKey="GoToAllUsers"/> + <actionGroup ref="AdminCreateUser" stepKey="AdminCreateNewUser"/> + + <!--SignOut--> + <actionGroup ref="SignOut" stepKey="signOutFromAdmin"/> + + <!--SignIn New User--> + <actionGroup ref="LoginNewUser" stepKey="signInNewUser"/> + <waitForPageLoad stepKey="waitForLogin" time="3"/> + + <!--Create New Order--> + <actionGroup ref="CreateNewOrderActionGroup" stepKey="createNewOrder"/> + + + <after> + <!--SignOut--> + <actionGroup ref="SignOut" stepKey="signOutFromNewUser"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!--Delete Product--> + <actionGroup ref="DeleteProductActionGroup" stepKey="DeleteAllProducts"> + <argument name="productName" value="NewProductData.ProductName"/> + </actionGroup> + + <!--Delete Customer--> + <actionGroup ref="DeleteCustomerActionGroup" stepKey="DeleteCustomer"> + <argument name="lastName" value="NewCustomerData.LastName"/> + </actionGroup> + + <!--Delete User --> + <actionGroup ref="GoToAllUsers" stepKey="GoBackToAllUsers"/> + <actionGroup ref="AdminDeleteUserActionGroup" stepKey="AdminDeleteUserActionGroup"/> + + <!--Delete Role--> + <actionGroup ref="GoToUserRoles" stepKey="GoBackToUserRoles"/> + <actionGroup ref="AdminDeleteRoleActionGroup" stepKey="AdminDeleteRoleActionGroup"/> + + </after> + + </test> +</tests> From 8b50b90b55f6954fda58cfe3c25b584c981b47ea Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 6 Aug 2018 15:52:36 +0300 Subject: [PATCH 0746/1171] MAGETWO-67633: Advanced Pricing import validation results show 0 value for checked entities --- .../ImportExport/Model/Import/Entity/AbstractEntity.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php index e7883693fbe74..e965e8ad207fd 100644 --- a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php +++ b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php @@ -391,6 +391,7 @@ protected function _saveValidatedBunches() $nextRowBackup = []; $maxDataSize = $this->_resourceHelper->getMaxDataSize(); $bunchSize = $this->_importExportData->getBunchSize(); + $skuSet = []; $source->rewind(); $this->_dataSourceModel->cleanBunches(); @@ -407,6 +408,7 @@ protected function _saveValidatedBunches() if ($source->valid()) { try { $rowData = $source->current(); + $skuSet[$rowData['sku']] = true; } catch (\InvalidArgumentException $e) { $this->addRowError($e->getMessage(), $this->_processedRowsCount); $this->_processedRowsCount++; @@ -434,6 +436,8 @@ protected function _saveValidatedBunches() $source->next(); } } + $this->_processedEntitiesCount = count($skuSet); + return $this; } From 5f7095461583f239fc0bb55c044a8e307d45398a Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Mon, 6 Aug 2018 16:05:36 +0300 Subject: [PATCH 0747/1171] MAGETWO-91008: Layered navigation has incompatibility with ElasticSearch --- .../Model/Client/Elasticsearch.php | 1 + .../SearchAdapter/Aggregation/Interval.php | 251 ++++++++++++++ .../Model/Client/ElasticsearchTest.php | 16 +- .../Aggregation/IntervalTest.php | 323 ++++++++++++++++++ app/code/Magento/Elasticsearch/etc/di.xml | 2 +- 5 files changed, 585 insertions(+), 8 deletions(-) create mode 100644 app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Aggregation/Interval.php create mode 100644 app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/SearchAdapter/Aggregation/IntervalTest.php diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php index c0ecaadaea504..66f16183ff2c0 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php @@ -244,6 +244,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'float', + 'store' => true, ], ], ], diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Aggregation/Interval.php b/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Aggregation/Interval.php new file mode 100644 index 0000000000000..a1fcbeb061481 --- /dev/null +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Aggregation/Interval.php @@ -0,0 +1,251 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Aggregation; + +use Magento\Framework\Search\Dynamic\IntervalInterface; +use Magento\Elasticsearch\SearchAdapter\ConnectionManager; +use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; +use Magento\Elasticsearch\Model\Config; +use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; +use Magento\CatalogSearch\Model\Indexer\Fulltext; + +/** + * Aggregate price intervals for search query result. + */ +class Interval implements IntervalInterface +{ + /** + * Minimal possible value + */ + const DELTA = 0.005; + + /** + * @var ConnectionManager + */ + private $connectionManager; + + /** + * @var FieldMapperInterface + */ + private $fieldMapper; + + /** + * @var Config + */ + private $clientConfig; + + /** + * @var string + */ + private $fieldName; + + /** + * @var string + */ + private $storeId; + + /** + * @var array + */ + private $entityIds; + + /** + * @var SearchIndexNameResolver + */ + private $searchIndexNameResolver; + + /** + * @param ConnectionManager $connectionManager + * @param FieldMapperInterface $fieldMapper + * @param Config $clientConfig + * @param SearchIndexNameResolver $searchIndexNameResolver + * @param string $fieldName + * @param string $storeId + * @param array $entityIds + */ + public function __construct( + ConnectionManager $connectionManager, + FieldMapperInterface $fieldMapper, + Config $clientConfig, + SearchIndexNameResolver $searchIndexNameResolver, + string $fieldName, + string $storeId, + array $entityIds + ) { + $this->connectionManager = $connectionManager; + $this->fieldMapper = $fieldMapper; + $this->clientConfig = $clientConfig; + $this->fieldName = $fieldName; + $this->storeId = $storeId; + $this->entityIds = $entityIds; + $this->searchIndexNameResolver = $searchIndexNameResolver; + } + + /** + * {@inheritdoc} + */ + public function load($limit, $offset = null, $lower = null, $upper = null) + { + $from = $to = []; + if ($lower) { + $from = ['gte' => $lower - self::DELTA]; + } + if ($upper) { + $to = ['lt' => $upper - self::DELTA]; + } + + $requestQuery = $this->prepareBaseRequestQuery($from, $to); + $requestQuery = array_merge_recursive( + $requestQuery, + ['body' => ['stored_fields' => [$this->fieldName], 'size' => $limit]] + ); + + if ($offset) { + $requestQuery['body']['from'] = $offset; + } + + $queryResult = $this->connectionManager->getConnection() + ->query($requestQuery); + + return $this->arrayValuesToFloat($queryResult['hits']['hits'], $this->fieldName); + } + + /** + * {@inheritdoc} + */ + public function loadPrevious($data, $index, $lower = null) + { + if ($lower) { + $from = ['gte' => $lower - self::DELTA]; + } + if ($data) { + $to = ['lt' => $data - self::DELTA]; + } + + $requestQuery = $this->prepareBaseRequestQuery($from, $to); + $requestQuery = array_merge_recursive( + $requestQuery, + ['size' => 0] + ); + + $queryResult = $this->connectionManager->getConnection() + ->query($requestQuery); + + $offset = $queryResult['hits']['total']; + if (!$offset) { + return false; + } + + return $this->load($index - $offset + 1, $offset - 1, $lower); + } + + /** + * {@inheritdoc} + */ + public function loadNext($data, $rightIndex, $upper = null) + { + $from = ['gt' => $data + self::DELTA]; + $to = ['lt' => $data - self::DELTA]; + + $requestCountQuery = $this->prepareBaseRequestQuery($from, $to); + $requestCountQuery = array_merge_recursive( + $requestCountQuery, + ['size' => 0] + ); + + $queryCountResult = $this->connectionManager->getConnection() + ->query($requestCountQuery); + + $offset = $queryCountResult['hits']['total']; + if (!$offset) { + return false; + } + + $from = ['gte' => $data - self::DELTA]; + if ($upper !== null) { + $to = ['lt' => $data - self::DELTA]; + } + + $requestQuery = $requestCountQuery; + + $requestCountQuery['body']['query']['bool']['filter']['bool']['must']['range'] = + [$this->fieldName => array_merge($from, $to)]; + $requestCountQuery['body']['from'] = $offset - 1; + $requestCountQuery['body']['size'] = $rightIndex - $offset + 1; + $queryResult = $this->connectionManager->getConnection() + ->query($requestQuery); + + return array_reverse($this->arrayValuesToFloat($queryResult['hits']['hits'], $this->fieldName)); + } + + /** + * Conver array values to float type. + * + * @param array $hits + * @param string $fieldName + * + * @return float[] + */ + private function arrayValuesToFloat(array $hits, string $fieldName): array + { + $returnPrices = []; + foreach ($hits as $hit) { + $returnPrices[] = (float)$hit['fields'][$fieldName][0]; + } + + return $returnPrices; + } + + /** + * Prepare base query for search. + * + * @param array|null $from + * @param array|null $to + * @return array + */ + private function prepareBaseRequestQuery($from = null, $to = null): array + { + $requestQuery = [ + 'index' => $this->searchIndexNameResolver->getIndexName($this->storeId, Fulltext::INDEXER_ID), + 'type' => $this->clientConfig->getEntityType(), + 'body' => [ + 'stored_fields' => [ + '_id', + ], + 'query' => [ + 'bool' => [ + 'must' => [ + 'match_all' => new \stdClass(), + ], + 'filter' => [ + 'bool' => [ + 'must' => [ + [ + 'terms' => [ + '_id' => $this->entityIds, + ], + ], + [ + 'range' => [ + $this->fieldName => array_merge($from, $to), + ], + ], + ], + ], + ], + ], + ], + 'sort' => [ + $this->fieldName, + ], + ], + ]; + + return $requestQuery; + } +} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php index 415c8b61ac6ca..026d385da34da 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php @@ -343,7 +343,7 @@ public function testAddFieldsMapping() 'product' => [ '_all' => [ 'enabled' => true, - 'type' => 'text' + 'type' => 'text', ], 'properties' => [ 'name' => [ @@ -356,7 +356,8 @@ public function testAddFieldsMapping() 'match' => 'price_*', 'match_mapping_type' => 'string', 'mapping' => [ - 'type' => 'float' + 'type' => 'float', + 'store' => true, ], ], ], @@ -366,7 +367,7 @@ public function testAddFieldsMapping() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'text', - 'index' => 'no' + 'index' => 'no', ], ], ], @@ -375,7 +376,7 @@ public function testAddFieldsMapping() 'match' => 'position_*', 'match_mapping_type' => 'string', 'mapping' => [ - 'type' => 'int' + 'type' => 'int', ], ], ], @@ -409,7 +410,7 @@ public function testAddFieldsMappingFailure() 'product' => [ '_all' => [ 'enabled' => true, - 'type' => 'text' + 'type' => 'text', ], 'properties' => [ 'name' => [ @@ -422,7 +423,8 @@ public function testAddFieldsMappingFailure() 'match' => 'price_*', 'match_mapping_type' => 'string', 'mapping' => [ - 'type' => 'float' + 'type' => 'float', + 'store' => true, ], ], ], @@ -441,7 +443,7 @@ public function testAddFieldsMappingFailure() 'match' => 'position_*', 'match_mapping_type' => 'string', 'mapping' => [ - 'type' => 'int' + 'type' => 'int', ], ], ], diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/SearchAdapter/Aggregation/IntervalTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/SearchAdapter/Aggregation/IntervalTest.php new file mode 100644 index 0000000000000..4030d2dfaf0c5 --- /dev/null +++ b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/SearchAdapter/Aggregation/IntervalTest.php @@ -0,0 +1,323 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\Test\Unit\Elasticsearch5\SearchAdapter\Aggregation; + +use Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Aggregation\Interval; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Elasticsearch\SearchAdapter\ConnectionManager; +use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Elasticsearch\Model\Config; +use Magento\Elasticsearch\Elasticsearch5\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; + +/** + * Test for Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Aggregation\Interval class. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class IntervalTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Interval + */ + private $model; + + /** + * @var ConnectionManager|\PHPUnit_Framework_MockObject_MockObject + */ + private $connectionManager; + + /** + * @var FieldMapperInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $fieldMapper; + + /** + * @var Config|\PHPUnit_Framework_MockObject_MockObject + */ + private $clientConfig; + + /** + * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeManager; + + /** + * @var CustomerSession|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerSession; + + /** + * @var ElasticsearchClient|\PHPUnit_Framework_MockObject_MockObject + */ + private $clientMock; + + /** + * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeMock; + + /** + * @var SearchIndexNameResolver|\PHPUnit_Framework_MockObject_MockObject + */ + private $searchIndexNameResolver; + + /** + * {@inheritdoc} + */ + protected function setUp() + { + $this->connectionManager = $this->getMockBuilder(ConnectionManager::class) + ->setMethods(['getConnection']) + ->disableOriginalConstructor() + ->getMock(); + $this->fieldMapper = $this->getMockBuilder(FieldMapperInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->clientConfig = $this->getMockBuilder(Config::class) + ->setMethods([ + 'getIndexName', + 'getEntityType', + ]) + ->disableOriginalConstructor() + ->getMock(); + $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->customerSession = $this->getMockBuilder(CustomerSession::class) + ->setMethods(['getCustomerGroupId']) + ->disableOriginalConstructor() + ->getMock(); + $this->customerSession->expects($this->any()) + ->method('getCustomerGroupId') + ->willReturn(1); + $this->storeMock = $this->getMockBuilder(StoreInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->searchIndexNameResolver = $this + ->getMockBuilder(SearchIndexNameResolver::class) + ->disableOriginalConstructor() + ->getMock(); + $this->storeMock->expects($this->any()) + ->method('getWebsiteId') + ->willReturn(1); + $this->storeMock->expects($this->any()) + ->method('getId') + ->willReturn(1); + $this->clientConfig->expects($this->any()) + ->method('getIndexName') + ->willReturn('indexName'); + $this->clientConfig->expects($this->any()) + ->method('getEntityType') + ->willReturn('product'); + $this->clientMock = $this->getMockBuilder(ElasticsearchClient::class) + ->setMethods(['query']) + ->disableOriginalConstructor() + ->getMock(); + $this->connectionManager->expects($this->any()) + ->method('getConnection') + ->willReturn($this->clientMock); + + $objectManagerHelper = new ObjectManagerHelper($this); + $this->model = $objectManagerHelper->getObject( + Interval::class, + [ + 'connectionManager' => $this->connectionManager, + 'fieldMapper' => $this->fieldMapper, + 'clientConfig' => $this->clientConfig, + 'searchIndexNameResolver' => $this->searchIndexNameResolver, + 'fieldName' => 'price_0_1', + 'storeId' => 1, + 'entityIds' => [265, 313, 281], + ] + ); + } + + /** + * @dataProvider loadParamsProvider + * @param string $limit + * @param string $offset + * @param string $lower + * @param string $upper + * @param array $queryResult + * @param array $expected + * @return void + */ + public function testLoad( + string $limit, + string $offset, + string $lower, + string $upper, + array $queryResult, + array $expected + ): void { + $this->processQuery($queryResult); + + $this->assertEquals( + $expected, + $this->model->load($limit, $offset, $lower, $upper) + ); + } + + /** + * @dataProvider loadPrevParamsProvider + * @param string $data + * @param string $index + * @param string $lower + * @param array $queryResult + * @param array|bool $expected + * @return void + */ + public function testLoadPrev(string $data, string $index, string $lower, array $queryResult, $expected): void + { + $this->processQuery($queryResult); + + $this->assertEquals( + $expected, + $this->model->loadPrevious($data, $index, $lower) + ); + } + + /** + * @dataProvider loadNextParamsProvider + * @param string $data + * @param string $rightIndex + * @param string $upper + * @param array $queryResult + * @param array|bool $expected + * @return void + */ + public function testLoadNext(string $data, string $rightIndex, string $upper, array $queryResult, $expected): void + { + $this->processQuery($queryResult); + + $this->assertEquals( + $expected, + $this->model->loadNext($data, $rightIndex, $upper) + ); + } + + /** + * @param array $queryResult + * @return void + */ + private function processQuery(array $queryResult): void + { + $this->searchIndexNameResolver->expects($this->any()) + ->method('getIndexName') + ->willReturn('magento2_product_1'); + $this->clientConfig->expects($this->any()) + ->method('getEntityType') + ->willReturn('document'); + $this->clientMock->expects($this->any()) + ->method('query') + ->willReturn($queryResult); + } + + /** + * @return array + */ + public function loadParamsProvider(): array + { + return [ + [ + 'limit' => '6', + 'offset' => '2', + 'lower' => '24', + 'upper' => '42', + 'queryResult' => [ + 'hits' => [ + 'hits' => [ + [ + 'fields' => [ + 'price_0_1' => [25], + ], + ], + ], + ], + ], + 'expected' => [25], + ], + ]; + } + + /** + * @return array + */ + public function loadPrevParamsProvider(): array + { + return [ + [ + 'data' => '24', + 'rightIndex' => '1', + 'upper' => '24', + 'queryResult' => [ + 'hits' => [ + 'total'=> '1', + 'hits' => [ + [ + 'fields' => [ + 'price_0_1' => ['25'], + ], + ], + ], + ], + ], + 'expected' => ['25.0'], + ], + [ + 'data' => '24', + 'rightIndex' => '1', + 'upper' => '24', + 'queryResult' => [ + 'hits' => ['total'=> '0'], + ], + 'expected' => false, + ], + ]; + } + + /** + * @return array + */ + public function loadNextParamsProvider(): array + { + return [ + [ + 'data' => '24', + 'rightIndex' => '2', + 'upper' => '42', + 'queryResult' => [ + 'hits' => [ + 'total'=> '1', + 'hits' => [ + [ + 'fields' => [ + 'price_0_1' => ['25'], + ], + ], + ], + ], + ], + 'expected' => ['25.0'], + ], + [ + 'data' => '24', + 'rightIndex' => '2', + 'upper' => '42', + 'queryResult' => [ + 'hits' => ['total'=> '0'], + ], + 'expected' => false, + ], + ]; + } +} diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml index 2d569eecfee58..0cfaba845fd6a 100644 --- a/app/code/Magento/Elasticsearch/etc/di.xml +++ b/app/code/Magento/Elasticsearch/etc/di.xml @@ -166,7 +166,7 @@ <arguments> <argument name="intervals" xsi:type="array"> <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Aggregation\Interval</item> - <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Aggregation\Interval</item> + <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Aggregation\Interval</item> </argument> </arguments> </type> From fbabf2c061662f4243d01b20aec872ba55c430bd Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Mon, 6 Aug 2018 16:25:21 +0400 Subject: [PATCH 0748/1171] MAGETWO-91524: "element.disabled is not a function"error is thrown when configurable products are generated with an attribute named "design" - Update automated test according to review --- ...eProductAttributeNameDesignActionGroup.xml | 106 ++++++++++-------- ...igurableProductAttributeNameDesignData.xml | 22 ++-- ...ableProductAttributeNameDesignSection.xml} | 38 ++++--- ...igurableProductAttributeNameDesignTest.xml | 19 +++- 4 files changed, 104 insertions(+), 81 deletions(-) rename app/code/Magento/ConfigurableProduct/Test/Mftf/Section/{ConfigurableProductsaAttributeNameDesignSection.xml => ConfigurableProductAttributeNameDesignSection.xml} (57%) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml index 9c0f8d4ce9692..b9c594ea89a35 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml @@ -13,13 +13,11 @@ <!--Click on Catalog item--> <click stepKey="clickOnCatalogItem" selector="{{CatalogProductsSection.catalogItem}}"/> - - <waitForPageLoad stepKey="waitForCatalogLoad" time="3"/> + <waitForPageLoad stepKey="waitForCatalogLoad"/> <!--Click on Products item--> <click stepKey="clickOnProductItem" selector="{{CatalogProductsSection.productItem}}"/> - - <waitForPageLoad stepKey="waitForCatalogProductPageLoad" time="3"/> + <waitForPageLoad stepKey="waitForCatalogProductPageLoad"/> <!--Assert we have gone desired page successfully--> <seeInCurrentUrl stepKey="assertWeAreOnTheCatalogProductPage" url="{{assertionData.catalogProduct}}"/> @@ -33,8 +31,7 @@ <!--Click on Configuration Product item--> <click stepKey="clickOnConfigurationProductItem" selector="{{ConfigurableProductSection.configProductItem}}"/> - - <waitForPageLoad stepKey="waitForConfigurableProductPageLoad" time="3"/> + <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> <!--Assert we have gone desired page successfully--> <seeInCurrentUrl stepKey="assertWeAreOnTheConfigurableProductPage" url="{{assertionData.configurableProduct}}"/> @@ -54,17 +51,16 @@ <!--Click "Create Configurations" button in configurations field--> <click stepKey="clickOnCreateConfigurationsButton" selector="{{NewProduct.createConfigurationButton}}"/> - - <wait stepKey="waitForCreateProductConfigurationsPageLoad" time="3"/> + <waitForPageLoad stepKey="waitForCreateProductConfigurationsPageLoad"/> <!--Click "Create New Attribute" button--> <click stepKey="clickOnCreateNewAttributeButton" selector="{{NewProduct.createNewAttributeButton}}"/> - - <wait stepKey="waitForNewAttributePageLoad" time="3"/> + <waitForPageLoad stepKey="waitForNewAttributePageLoad"/> </actionGroup> - <actionGroup name="FillNewAttributeFields"> + + <actionGroup name="CreateNewAttribute"> <switchToIFrame stepKey="NewAttributePage" selector="{{NewProduct.newAttributeIFrame}}"/> @@ -73,71 +69,92 @@ <!--Add option 1 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption1"/> - <fillField stepKey="fillInAdminField1" selector="{{NewProduct.adminField1}}" userInput="{{NewProductsData.adminField1}}"/> - <fillField stepKey="fillInDefaultStoreViewField1" selector="{{NewProduct.defaultStoreViewField1}}" userInput="{{NewProductsData.defaultStoreViewField1}}"/> + <fillField stepKey="fillInAdminFieldRed" selector="{{NewProduct.adminFieldRed}}" userInput="{{NewProductsData.adminFieldRed}}"/> + <fillField stepKey="fillInDefaultStoreViewFieldRed" selector="{{NewProduct.defaultStoreViewFieldRed}}" userInput="{{NewProductsData.defaultStoreViewFieldRed}}"/> <!--Add option 2 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption2"/> - <fillField stepKey="fillInAdminField2" selector="{{NewProduct.adminField2}}" userInput="{{NewProductsData.adminField2}}"/> - <fillField stepKey="fillInDefaultStoreViewField2" selector="{{NewProduct.defaultStoreViewField2}}" userInput="{{NewProductsData.defaultStoreViewField2}}"/> + <fillField stepKey="fillInAdminFieldBlue" selector="{{NewProduct.adminFieldBlue}}" userInput="{{NewProductsData.adminFieldBlue}}"/> + <fillField stepKey="fillInDefaultStoreViewFieldBlue" selector="{{NewProduct.defaultStoreViewFieldBlue}}" userInput="{{NewProductsData.defaultStoreViewFieldBlue}}"/> <!--Add option 3 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption3"/> - <fillField stepKey="fillInAdminField3" selector="{{NewProduct.adminField3}}" userInput="{{NewProductsData.adminField3}}"/> - <fillField stepKey="fillInDefaultStoreViewField3" selector="{{NewProduct.defaultStoreViewField3}}" userInput="{{NewProductsData.defaultStoreViewField3}}"/> + <fillField stepKey="fillInAdminFieldYellow" selector="{{NewProduct.adminFieldYellow}}" userInput="{{NewProductsData.adminFieldYellow}}"/> + <fillField stepKey="fillInDefaultStoreViewFieldYellow" selector="{{NewProduct.defaultStoreViewFieldYellow}}" userInput="{{NewProductsData.defaultStoreViewFieldYellow}}"/> <!--Add option 4 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption4"/> - <fillField stepKey="fillInAdminField4" selector="{{NewProduct.adminField4}}" userInput="{{NewProductsData.adminField4}}"/> - <fillField stepKey="fillInDefaultStoreViewField4" selector="{{NewProduct.defaultStoreViewField4}}" userInput="{{NewProductsData.defaultStoreViewField4}}"/> + <fillField stepKey="fillInAdminFieldGreen" selector="{{NewProduct.adminFieldGreen}}" userInput="{{NewProductsData.adminFieldGreen}}"/> + <fillField stepKey="fillInDefaultStoreViewFieldGreen" selector="{{NewProduct.defaultStoreViewFieldGreen}}" userInput="{{NewProductsData.defaultStoreViewFieldGreen}}"/> <!--Add option 5 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption5"/> - <fillField stepKey="fillInAdminField5" selector="{{NewProduct.adminField5}}" userInput="{{NewProductsData.adminField5}}"/> - <fillField stepKey="fillInDefaultStoreViewField5" selector="{{NewProduct.defaultStoreViewField5}}" userInput="{{NewProductsData.defaultStoreViewField5}}"/> + <fillField stepKey="fillInAdminFieldBlack" selector="{{NewProduct.adminFieldBlack}}" userInput="{{NewProductsData.adminFieldBlack}}"/> + <fillField stepKey="fillInDefaultStoreViewFieldBlack" selector="{{NewProduct.defaultStoreViewFieldBlack}}" userInput="{{NewProductsData.defaultStoreViewFieldBlack}}"/> <!--Click Save Attribute button--> <click selector="{{NewProduct.saveAttributeButton}}" stepKey="clickSaveAttributeButton"/> - - <wait stepKey="waitForSavingSettings" time="3"/> + <waitForPageLoad stepKey="waitForSavingSettings"/> <!--Select created Attribute --> <click selector="{{ConfigurableProductSection.selectCreatedAttribute}}" stepKey="selectCreatedAttribute"/> <!--Click Next button--> <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton"/> - - <wait stepKey="waitForNextPageLoaded" time="3"/> + <waitForPageLoad stepKey="waitForNextPageLoaded"/> <!--Select all the options of all the attributes button--> - <click selector="{{CreateProductConfigurations.item1}}" stepKey="selectItem1"/> - <click selector="{{CreateProductConfigurations.item2}}" stepKey="selectItem2"/> - <click selector="{{CreateProductConfigurations.item3}}" stepKey="selectItem3"/> - <click selector="{{CreateProductConfigurations.item4}}" stepKey="selectItem4"/> - <click selector="{{CreateProductConfigurations.item5}}" stepKey="selectItem5"/> + <click selector="{{CreateProductConfigurations.checkboxRed}}" stepKey="selectCheckboxRed"/> + <click selector="{{CreateProductConfigurations.checkboxBlue}}" stepKey="selectCheckboxBlue"/> + <click selector="{{CreateProductConfigurations.checkboxYellow}}" stepKey="selectCheckboxYellow"/> + <click selector="{{CreateProductConfigurations.checkboxGreen}}" stepKey="selectCheckboxGreen"/> + <click selector="{{CreateProductConfigurations.checkboxBlack}}" stepKey="selectCheckboxBlack"/> <!--Click Next button--> <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton2"/> - - <wait stepKey="waitForBulkImagesPricePageLoaded" time="3"/> + <waitForPageLoad stepKey="waitForBulkImagesPricePageLoaded"/> <!--Click Next button--> <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton3"/> - - <wait stepKey="waitForSummaryPageLoaded" time="3"/> + <waitForPageLoad stepKey="waitForSummaryPageLoaded"/> <!--Click Generate Configure button--> <click selector="{{ConfigurableProductSection.generateConfigure}}" stepKey="generateConfigure"/> - - <wait stepKey="waitForGenerateConfigure" time="3"/> + <waitForPageLoad stepKey="waitForGenerateConfigure"/> <!-- This Error message shouldn't appear: Test will pass when bug will be fixed--> <dontSee selector="{{CreateProductConfigurations.errorMessage}}" userInput="{{assertionData.errorMessage}}" stepKey="dontSeeError"/> <!--Close frame--> <conditionalClick selector="{{ConfigurableProductSection.closeFrame}}" dependentSelector="{{ConfigurableProductSection.closeFrame}}" visible="1" stepKey="closeFrame"/> + <waitForPageLoad stepKey="waitForClosingFrame"/> - <wait stepKey="waitForClosingFrame" time="3"/> + </actionGroup> + + <actionGroup name="DeleteCreatedAttribute"> + + <!--Click on Stores item--> + <click stepKey="clickOnStoresItem" selector="{{CatalogProductsSection.storesItem}}"/> + + <!--Click on Products item--> + <waitForElementVisible selector="{{CatalogProductsSection.storesProductItem}}" stepKey="waitForCatalogLoad"/> + <click stepKey="clickOnStoresProductItem" selector="{{CatalogProductsSection.storesProductItem}}"/> + <waitForPageLoad stepKey="waitForStoresProductPageLoad"/> + + <!--Click on created Attribute --> + <fillField stepKey="searchProductDefaultLabel" selector="{{CatalogProductsSection.searchDefaultLabelField}}" userInput="{{NewProductsData.defaultLabel}}"/> + <click stepKey="clickSearchButton" selector="{{CatalogProductsSection.searchButton}}"/> + <waitForPageLoad stepKey="waitForCreatedAttributeLoad"/> + <click stepKey="clickOnCreatedAttributeItem" selector="{{CatalogProductsSection.createdAttributeItem}}"/> + <waitForPageLoad stepKey="waitForAttributePropertiesPageLoad"/> + + <!--Click on Delete Attribute item--> + <click stepKey="clickOnDeleteAttributeItem" selector="{{CatalogProductsSection.deleteAttributeItem}}"/> + <waitForPageLoad stepKey="waitForDeletedDialogOpened"/> + + <!--Click on OK button--> + <click stepKey="clickOnOKButton" selector="{{CatalogProductsSection.okButton}}"/> + <waitForPageLoad stepKey="waitFordAttributeDeleted"/> </actionGroup> @@ -145,28 +162,23 @@ <!--Click on Stores item--> <click stepKey="clickOnStoresItem" selector="{{CatalogProductsSection.storesItem}}"/> - - <waitForPageLoad stepKey="waitForCatalogLoad" time="3"/> + <waitForPageLoad stepKey="waitForCatalogLoad"/> <!--Click on Products item--> <click stepKey="clickOnStoresProductItem" selector="{{CatalogProductsSection.storesProductItem}}"/> - - <waitForPageLoad stepKey="waitForStoresProductPageLoad" time="3"/> + <waitForPageLoad stepKey="waitForStoresProductPageLoad"/> <!--Click on created Attribute item if it exist--> <conditionalClick selector="{{CatalogProductsSection.createdAttributeItem}}" dependentSelector="{{CatalogProductsSection.createdAttributeItem}}" visible="1" stepKey="clickOnCreatedAttributeItem"/> - - <waitForPageLoad stepKey="waitForCreatedAttributeLoad" time="3"/> + <waitForPageLoad stepKey="waitForCreatedAttributeLoad"/> <!--Click on Delete Attribute item--> <conditionalClick stepKey="clickOnDeleteAttributeItem" selector="{{CatalogProductsSection.deleteAttributeItem}}" dependentSelector="{{CatalogProductsSection.deleteAttributeItem}}" visible="1"/> - - <waitForPageLoad stepKey="waitForDeletedDialogOpened" time="3"/> + <waitForPageLoad stepKey="waitForDeletedDialogOpened"/> <!--Click on OK button--> <conditionalClick stepKey="clickOnOKButton" selector="{{CatalogProductsSection.okButton}}" dependentSelector="{{CatalogProductsSection.okButton}}" visible="1"/> - - <waitForPageLoad stepKey="waitFordAttributeDeleted" time="3"/> + <waitForPageLoad stepKey="waitFordAttributeDeleted"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml index 685c913679a99..73a668fd2fefd 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml @@ -9,20 +9,20 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> <entity name="NewProductsData" type="user"> - <data key="productName">Shoes</data> + <data key="productName" unique="prefix">Shoes</data> <data key="price">60</data> <data key="weight">100</data> <data key="defaultLabel">design</data> - <data key="adminField1">red</data> - <data key="defaultStoreViewField1">red123</data> - <data key="adminField2">blue</data> - <data key="defaultStoreViewField2">blue123</data> - <data key="adminField3">yellow</data> - <data key="defaultStoreViewField3">yellow123</data> - <data key="adminField4">green</data> - <data key="defaultStoreViewField4">green123</data> - <data key="adminField5">black</data> - <data key="defaultStoreViewField5">black123</data> + <data key="adminFieldRed">red</data> + <data key="defaultStoreViewFieldRed">red123</data> + <data key="adminFieldBlue">blue</data> + <data key="defaultStoreViewFieldBlue">blue123</data> + <data key="adminFieldYellow">yellow</data> + <data key="defaultStoreViewFieldYellow">yellow123</data> + <data key="adminFieldGreen">green</data> + <data key="defaultStoreViewFieldGreen">green123</data> + <data key="adminFieldBlack">black</data> + <data key="defaultStoreViewFieldBlack">black123</data> <data key="attributeCodeField">bug91524</data> </entity> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductsaAttributeNameDesignSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml similarity index 57% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductsaAttributeNameDesignSection.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml index 6a7b428d8649c..182ed7c42a401 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductsaAttributeNameDesignSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml @@ -12,12 +12,14 @@ <element name="catalogItem" type="button" selector="//*[@id='menu-magento-catalog-catalog']/a/span"/> <element name="productItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-products']/a"/> <element name="storesItem" type="button" selector="//*[@id='menu-magento-backend-stores']/a/span"/> + <element name="searchDefaultLabelField" type="input" selector="//*[@id='attributeGrid_filter_frontend_label']"/> + <element name="searchButton" type="button" selector="//div[@class='admin__filter-actions']//*[contains(text(), 'Search')]"/> <element name="storesProductItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-attributes-attributes']/a"/> - <element name="createdAttributeItem" type="button" selector="//td[contains(@class, 'col-attr-code') and normalize-space()='design']"/> + <element name="createdAttributeItem" type="button" selector="//td[contains(@class, 'col-label') and normalize-space()='design']"/> <element name="deleteAttributeItem" type="button" selector="//*[@id='delete']"/> - <element name="okButton" type="button" selector=" //footer[@class='modal-footer']//*[contains(text(),'OK')]"/> + <element name="okButton" type="button" selector="//footer[@class='modal-footer']//*[contains(text(),'OK')]"/> - <element name="messageSuccessSavedProduct" type="button" selector=" //div[@data-ui-id='messages-message-success']"/> + <element name="messageSuccessSavedProduct" type="button" selector="//div[@data-ui-id='messages-message-success']"/> </section> <section name="ConfigurableProductSection"> @@ -38,16 +40,16 @@ <element name="newAttributeIFrame" type="iframe" selector="create_new_attribute_container"/> <element name="defaultLabel" type="input" selector="//*[@id='attribute_label']"/> <element name="addOptionButton" type="button" selector="//*[@id='add_new_option_button']"/> - <element name="adminField1" type="input" selector="//input[@name='option[value][option_0][0]']"/> - <element name="defaultStoreViewField1" type="input" selector="//input[@name='option[value][option_0][1]']"/> - <element name="adminField2" type="input" selector="//input[@name='option[value][option_1][0]']"/> - <element name="defaultStoreViewField2" type="input" selector="//input[@name='option[value][option_1][1]']"/> - <element name="adminField3" type="input" selector="//input[@name='option[value][option_2][0]']"/> - <element name="defaultStoreViewField3" type="input" selector="//input[@name='option[value][option_2][1]']"/> - <element name="adminField4" type="input" selector="//input[@name='option[value][option_3][0]']"/> - <element name="defaultStoreViewField4" type="input" selector="//input[@name='option[value][option_3][1]']"/> - <element name="adminField5" type="input" selector="//input[@name='option[value][option_4][0]']"/> - <element name="defaultStoreViewField5" type="input" selector="//input[@name='option[value][option_4][1]']"/> + <element name="adminFieldRed" type="input" selector="//input[@name='option[value][option_0][0]']"/> + <element name="defaultStoreViewFieldRed" type="input" selector="//input[@name='option[value][option_0][1]']"/> + <element name="adminFieldBlue" type="input" selector="//input[@name='option[value][option_1][0]']"/> + <element name="defaultStoreViewFieldBlue" type="input" selector="//input[@name='option[value][option_1][1]']"/> + <element name="adminFieldYellow" type="input" selector="//input[@name='option[value][option_2][0]']"/> + <element name="defaultStoreViewFieldYellow" type="input" selector="//input[@name='option[value][option_2][1]']"/> + <element name="adminFieldGreen" type="input" selector="//input[@name='option[value][option_3][0]']"/> + <element name="defaultStoreViewFieldGreen" type="input" selector="//input[@name='option[value][option_3][1]']"/> + <element name="adminFieldBlack" type="input" selector="//input[@name='option[value][option_4][0]']"/> + <element name="defaultStoreViewFieldBlack" type="input" selector="//input[@name='option[value][option_4][1]']"/> <element name="saveAttributeButton" type="button" selector="//*[@id='save']"/> <element name="advancedAttributeProperties" type="button" selector="//*[@id='advanced_fieldset-wrapper']//*[contains(text(),'Advanced Attribute Properties')]"/> <element name="attributeCodeField" type="input" selector="//*[@id='attribute_code']"/> @@ -55,11 +57,11 @@ </section> <section name="CreateProductConfigurations"> - <element name="item1" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'red')]/preceding-sibling::input"/> - <element name="item2" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'blue')]/preceding-sibling::input"/> - <element name="item3" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'yellow')]/preceding-sibling::input"/> - <element name="item4" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'green')]/preceding-sibling::input"/> - <element name="item5" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'black')]/preceding-sibling::input"/> + <element name="checkboxRed" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'red')]/preceding-sibling::input"/> + <element name="checkboxBlue" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'blue')]/preceding-sibling::input"/> + <element name="checkboxYellow" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'yellow')]/preceding-sibling::input"/> + <element name="checkboxGreen" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'green')]/preceding-sibling::input"/> + <element name="checkboxBlack" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'black')]/preceding-sibling::input"/> <element name="errorMessage" type="input" selector="//div[@data-ui-id='messages-message-error']"/> </section> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml index c051a1b29d0ce..3ff0589230687 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml @@ -18,17 +18,26 @@ <group value="product"/> </annotations> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <before> + <!-- Log in to Dashboard page --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <!-- This actionGroup should be deleted and Test will pass after fixing the bug.--> + <actionGroup ref="DeleteCreatedAttributeIfExist" stepKey="deleteCreatedAttributeIfExist"/> + </before> - <actionGroup ref="DeleteCreatedAttributeIfExist" stepKey="deleteCreatedAttributeIfExist"/> + <!-- Navigate to Catalog-> Products --> <actionGroup ref="GotoCatalogProductsPage" stepKey="goToCatalogProductsPage"/> - + <!-- Fill the fields on Configurable Product--> <actionGroup ref="GotoConfigurableProductPage" stepKey="goToConfigurableProductPage"/> - <actionGroup ref="FillAllRequiredFields" stepKey="fillInAllRequiredFields"/> + <!-- Create New Attribute (Default Label= design) --> + <actionGroup ref="CreateNewAttribute" stepKey="createNewAttribute"/> - <actionGroup ref="FillNewAttributeFields" stepKey="fillInNewAttributeFields"/> + <after> + <!-- Delete Created Attribute --> + <actionGroup ref="DeleteCreatedAttribute" stepKey="deleteCreatedAttribute"/> + </after> </test> </tests> From c33478beec949847158ddca4e10ccf9e286d37be Mon Sep 17 00:00:00 2001 From: IvanPletnyov <ivan.pletnyov@transoftgroup.com> Date: Mon, 6 Aug 2018 17:44:07 +0300 Subject: [PATCH 0749/1171] MSI-1542: Provide MSI support for Shipment Web API endpoint --- .../Model/Order/ShipmentDocumentFactory.php | 15 +- .../ExtensionAttributesProcessor.php | 76 ++++++++++ .../ExtensionAttributesProcessorTest.php | 132 ++++++++++++++++++ .../Order/ShipmentDocumentFactoryTest.php | 16 ++- 4 files changed, 237 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessor.php create mode 100644 app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessorTest.php diff --git a/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php index c0a3f84e8846d..f0998a18966bd 100644 --- a/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php +++ b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php @@ -5,6 +5,7 @@ */ namespace Magento\Sales\Model\Order; +use Magento\Framework\App\ObjectManager; use Magento\Sales\Api\Data\ShipmentInterface; use Magento\Sales\Api\Data\ShipmentItemCreationInterface; use Magento\Sales\Api\Data\ShipmentPackageCreationInterface; @@ -15,6 +16,7 @@ use Magento\Sales\Api\Data\ShipmentCommentCreationInterface; use Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface; use Magento\Sales\Api\Data\OrderItemInterface; +use Magento\Sales\Model\Order\ShipmentDocumentFactory\ExtensionAttributesProcessor; /** * Class ShipmentDocumentFactory @@ -39,21 +41,30 @@ class ShipmentDocumentFactory */ private $hydratorPool; + /** + * @var ExtensionAttributesProcessor|null + */ + private $extensionAttributesProcessor; + /** * ShipmentDocumentFactory constructor. * * @param ShipmentFactory $shipmentFactory * @param HydratorPool $hydratorPool * @param TrackFactory $trackFactory + * @param ExtensionAttributesProcessor $extensionAttributesProcessor */ public function __construct( ShipmentFactory $shipmentFactory, HydratorPool $hydratorPool, - TrackFactory $trackFactory + TrackFactory $trackFactory, + ExtensionAttributesProcessor $extensionAttributesProcessor = null ) { $this->shipmentFactory = $shipmentFactory; $this->trackFactory = $trackFactory; $this->hydratorPool = $hydratorPool; + $this->extensionAttributesProcessor = $extensionAttributesProcessor ?: ObjectManager::getInstance() + ->get(ExtensionAttributesProcessor::class); } /** @@ -88,6 +99,8 @@ public function create( $shipmentItems ); + $this->extensionAttributesProcessor->execute($shipment, $arguments); + foreach ($tracks as $track) { $hydrator = $this->hydratorPool->getHydrator( \Magento\Sales\Api\Data\ShipmentTrackCreationInterface::class diff --git a/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessor.php b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessor.php new file mode 100644 index 0000000000000..3950f714626aa --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessor.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Model\Order\ShipmentDocumentFactory; + +use Magento\Framework\Api\SimpleDataObjectConverter; +use Magento\Framework\Reflection\ExtensionAttributesProcessor as AttributesProcessor; +use Magento\Sales\Api\Data\ShipmentCreationArgumentsExtensionInterface; +use Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface; +use Magento\Sales\Api\Data\ShipmentExtensionFactory; +use Magento\Sales\Api\Data\ShipmentInterface; + +/** + * Build and set extension attributes for shipment. + */ +class ExtensionAttributesProcessor +{ + /** + * @var ShipmentExtensionFactory + */ + private $shipmentExtensionFactory; + + /** + * @var ExtensionAttributesProcessor + */ + private $extensionAttributesProcessor; + + /** + * @param ShipmentExtensionFactory $shipmentExtensionFactory + * @param AttributesProcessor $extensionAttributesProcessor + */ + public function __construct( + ShipmentExtensionFactory $shipmentExtensionFactory, + AttributesProcessor $extensionAttributesProcessor + ) { + $this->shipmentExtensionFactory = $shipmentExtensionFactory; + $this->extensionAttributesProcessor = $extensionAttributesProcessor; + } + + /** + * @param ShipmentInterface $shipment + * @param ShipmentCreationArgumentsInterface $arguments + * @return void + */ + public function execute( + ShipmentInterface $shipment, + ShipmentCreationArgumentsInterface $arguments = null + ): void { + if (null === $arguments) { + return; + } + + $shipmentExtensionAttributes = $shipment->getExtensionAttributes(); + if (null === $shipmentExtensionAttributes) { + $shipmentExtensionAttributes = $this->shipmentExtensionFactory->create(); + } + + $attributes = $arguments->getExtensionAttributes(); + $extensionAttributes = $this->extensionAttributesProcessor->buildOutputDataArray( + $attributes, + ShipmentCreationArgumentsExtensionInterface::class + ); + + foreach ($extensionAttributes as $code => $value) { + $setMethod = 'set' . SimpleDataObjectConverter::snakeCaseToUpperCamelCase($code); + + if (method_exists($shipmentExtensionAttributes, $setMethod)) { + $shipmentExtensionAttributes->$setMethod($value); + } + } + } +} diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessorTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessorTest.php new file mode 100644 index 0000000000000..70237373a8d90 --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessorTest.php @@ -0,0 +1,132 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Test\Unit\Model\Order\ShipmentDocumentFactory; + +use Magento\Framework\Reflection\ExtensionAttributesProcessor as AttributesProcessor; +use Magento\Sales\Api\Data\ShipmentCreationArgumentsExtensionInterface; +use Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface; +use Magento\Sales\Api\Data\ShipmentExtensionFactory; +use Magento\Sales\Api\Data\ShipmentExtensionInterface; +use Magento\Sales\Api\Data\ShipmentInterface; +use Magento\Sales\Model\Order\ShipmentDocumentFactory\ExtensionAttributesProcessor; +use PHPUnit\Framework\TestCase; + +/** + * Provide tests for shipment document factory extension attributes processor. + */ +class ExtensionAttributesProcessorTest extends TestCase +{ + /** + * Test subject. + * + * @var ExtensionAttributesProcessor + */ + private $extensionAttributesProcessor; + + /** + * @var AttributesProcessor|\PHPUnit_Framework_MockObject_MockObject + */ + private $extensionAttributesProcessorMock; + + /** + * @var ShipmentExtensionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $shipmentExtensionFactoryMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->shipmentExtensionFactoryMock = $this->getMockBuilder(ShipmentExtensionFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->extensionAttributesProcessorMock = $this->getMockBuilder(AttributesProcessor::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->extensionAttributesProcessor = new ExtensionAttributesProcessor( + $this->shipmentExtensionFactoryMock, + $this->extensionAttributesProcessorMock + ); + } + + /** + * Build and set extension attributes for shipment with shipment creation arguments. + * + * @return void + */ + public function testExecuteWithParameter(): void + { + /** @var ShipmentInterface|\PHPUnit_Framework_MockObject_MockObject $shipmentMock */ + $shipmentMock = $this->getMockBuilder(ShipmentInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $shipmentMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn(null); + + $attributes = $this->getMockBuilder(ShipmentCreationArgumentsExtensionInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + /** @var ShipmentCreationArgumentsInterface|\PHPUnit_Framework_MockObject_MockObject $argumentsMock */ + $argumentsMock = $this->getMockBuilder(ShipmentCreationArgumentsInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getExtensionAttributes']) + ->getMockForAbstractClass(); + $argumentsMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($attributes); + + $shipmentExtensionAttributes = $this->getMockBuilder(ShipmentExtensionInterface::class) + ->disableOriginalConstructor() + ->setMethods(['setTestAttribute']) + ->getMockForAbstractClass(); + $shipmentExtensionAttributes->expects($this->once()) + ->method('setTestAttribute') + ->with('test_value') + ->willReturnSelf(); + + $this->shipmentExtensionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($shipmentExtensionAttributes); + + $this->extensionAttributesProcessorMock->expects($this->once()) + ->method('buildOutputDataArray') + ->with($attributes, ShipmentCreationArgumentsExtensionInterface::class) + ->willReturn(['test_attribute' => 'test_value']); + + $this->extensionAttributesProcessor->execute($shipmentMock, $argumentsMock); + } + + /** + * Build and set extension attributes for shipment without shipment creation arguments. + * + * @return void + */ + public function testExecuteWithoutParameter(): void + { + /** @var ShipmentInterface|\PHPUnit_Framework_MockObject_MockObject $shipmentMock */ + $shipmentMock = $this->getMockBuilder(ShipmentInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $shipmentMock->expects($this->never()) + ->method('getExtensionAttributes') + ->willReturn(null); + + $this->shipmentExtensionFactoryMock->expects($this->never()) + ->method('create'); + + $this->extensionAttributesProcessorMock->expects($this->never()) + ->method('buildOutputDataArray'); + + $this->extensionAttributesProcessor->execute($shipmentMock, null); + } +} diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactoryTest.php index bf9b3a67f9640..6075bcfb615b2 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactoryTest.php @@ -16,6 +16,7 @@ use Magento\Sales\Model\Order\Shipment\TrackFactory; use Magento\Sales\Model\Order\Shipment\Track; use Magento\Framework\EntityManager\HydratorInterface; +use Magento\Sales\Model\Order\ShipmentDocumentFactory\ExtensionAttributesProcessor; /** * Class ShipmentDocumentFactoryTest @@ -68,6 +69,11 @@ class ShipmentDocumentFactoryTest extends \PHPUnit\Framework\TestCase */ private $hydratorMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject|ExtensionAttributesProcessor + */ + private $extensionAttributeProcessorMock; + /** * @var \PHPUnit_Framework_MockObject_MockObject|Track */ @@ -113,10 +119,15 @@ protected function setUp() ->disableOriginalConstructor() ->getMockForAbstractClass(); + $this->extensionAttributeProcessorMock = $this->getMockBuilder(ExtensionAttributesProcessor::class) + ->disableOriginalConstructor() + ->getMock(); + $this->shipmentDocumentFactory = new ShipmentDocumentFactory( $this->shipmentFactoryMock, $this->hydratorPoolMock, - $this->trackFactoryMock + $this->trackFactoryMock, + $this->extensionAttributeProcessorMock ); } @@ -129,6 +140,9 @@ public function testCreate() $packages = []; $items = [1 => 10]; + $this->extensionAttributeProcessorMock->expects($this->once()) + ->method('execute') + ->with($this->shipmentMock, null); $this->itemMock->expects($this->once())->method('getOrderItemId')->willReturn(1); $this->itemMock->expects($this->once())->method('getQty')->willReturn(10); $this->itemMock->expects($this->once()) From 04ad1e9d169404f9ed694c756268f9d095f19377 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Mon, 6 Aug 2018 22:29:58 +0530 Subject: [PATCH 0750/1171] Remove unused use statement --- lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php index 098782b4e814e..d64f15058393d 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php @@ -7,7 +7,6 @@ use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Ddl\Table; -use Magento\Framework\DB\Select; use Magento\Framework\Search\Adapter\Mysql\Aggregation\Builder as AggregationBuilder; use Magento\Framework\Search\AdapterInterface; use Magento\Framework\Search\RequestInterface; From 5af16a162bd6f7a411abeeab8b49acf2d85df351 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Mon, 6 Aug 2018 23:14:14 +0530 Subject: [PATCH 0751/1171] Declare module namespace before template path name --- .../Magento/Backend/Block/System/Store/Delete/Group.php | 2 +- .../Magento/Backend/Block/System/Store/Delete/Website.php | 2 +- .../Catalog/Block/Adminhtml/Category/Checkboxes/Tree.php | 2 +- .../Block/Adminhtml/Product/Edit/Tab/Ajax/Serializer.php | 2 +- app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Cart.php | 2 +- .../Customer/Block/Adminhtml/System/Config/Validatevat.php | 2 +- app/code/Magento/Customer/Block/Widget/Company.php | 2 +- app/code/Magento/Customer/Block/Widget/Dob.php | 2 +- app/code/Magento/Customer/Block/Widget/Fax.php | 2 +- app/code/Magento/Customer/Block/Widget/Gender.php | 2 +- app/code/Magento/Customer/Block/Widget/Name.php | 2 +- app/code/Magento/Customer/Block/Widget/Taxvat.php | 2 +- app/code/Magento/Customer/Block/Widget/Telephone.php | 2 +- app/code/Magento/Dhl/Block/Adminhtml/Unitofmeasure.php | 2 +- app/code/Magento/Paypal/Block/Iframe.php | 6 +++--- app/code/Magento/Review/Block/Form.php | 2 +- app/code/Magento/Review/Block/Rating/Entity/Detailed.php | 2 +- app/code/Magento/UrlRewrite/Block/Edit.php | 2 +- app/code/Magento/User/Block/Role/Tab/Users.php | 2 +- 19 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Backend/Block/System/Store/Delete/Group.php b/app/code/Magento/Backend/Block/System/Store/Delete/Group.php index ae80b56066a6d..e95f3bbf9f8c1 100644 --- a/app/code/Magento/Backend/Block/System/Store/Delete/Group.php +++ b/app/code/Magento/Backend/Block/System/Store/Delete/Group.php @@ -19,7 +19,7 @@ protected function _prepareLayout() { $itemId = $this->getRequest()->getParam('group_id'); - $this->setTemplate('system/store/delete_group.phtml'); + $this->setTemplate('Magento_Backend::system/store/delete_group.phtml'); $this->setAction($this->getUrl('adminhtml/*/deleteGroupPost', ['group_id' => $itemId])); $this->addChild( 'confirm_deletion_button', diff --git a/app/code/Magento/Backend/Block/System/Store/Delete/Website.php b/app/code/Magento/Backend/Block/System/Store/Delete/Website.php index da28a471130cc..82cbb780137b8 100644 --- a/app/code/Magento/Backend/Block/System/Store/Delete/Website.php +++ b/app/code/Magento/Backend/Block/System/Store/Delete/Website.php @@ -19,7 +19,7 @@ protected function _prepareLayout() { $itemId = $this->getRequest()->getParam('website_id'); - $this->setTemplate('system/store/delete_website.phtml'); + $this->setTemplate('Magento_Backend::system/store/delete_website.phtml'); $this->setAction($this->getUrl('adminhtml/*/deleteWebsitePost', ['website_id' => $itemId])); $this->addChild( 'confirm_deletion_button', diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Category/Checkboxes/Tree.php b/app/code/Magento/Catalog/Block/Adminhtml/Category/Checkboxes/Tree.php index ad1ccea6f2c5a..1e0015d2be2c6 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Category/Checkboxes/Tree.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Category/Checkboxes/Tree.php @@ -30,7 +30,7 @@ class Tree extends \Magento\Catalog\Block\Adminhtml\Category\Tree */ protected function _prepareLayout() { - $this->setTemplate('catalog/category/checkboxes/tree.phtml'); + $this->setTemplate('Magento_Catalog::catalog/category/checkboxes/tree.phtml'); } /** diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Ajax/Serializer.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Ajax/Serializer.php index a7129f509316b..3d131a6e08810 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Ajax/Serializer.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Ajax/Serializer.php @@ -41,7 +41,7 @@ public function __construct( public function _construct() { parent::_construct(); - $this->setTemplate('catalog/product/edit/serializer.phtml'); + $this->setTemplate('Magento_Catalog::catalog/product/edit/serializer.phtml'); return $this; } diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Cart.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Cart.php index 9ee152078d960..9cd1989980fac 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Cart.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Cart.php @@ -82,7 +82,7 @@ protected function _construct() parent::_construct(); $this->setUseAjax(true); $this->_parentTemplate = $this->getTemplate(); - $this->setTemplate('tab/cart.phtml'); + $this->setTemplate('Magento_Customer::tab/cart.phtml'); } /** diff --git a/app/code/Magento/Customer/Block/Adminhtml/System/Config/Validatevat.php b/app/code/Magento/Customer/Block/Adminhtml/System/Config/Validatevat.php index 83ce7bef26d60..8cbe5c0680bd3 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/System/Config/Validatevat.php +++ b/app/code/Magento/Customer/Block/Adminhtml/System/Config/Validatevat.php @@ -99,7 +99,7 @@ protected function _prepareLayout() { parent::_prepareLayout(); if (!$this->getTemplate()) { - $this->setTemplate('system/config/validatevat.phtml'); + $this->setTemplate('Magento_Customer::system/config/validatevat.phtml'); } return $this; } diff --git a/app/code/Magento/Customer/Block/Widget/Company.php b/app/code/Magento/Customer/Block/Widget/Company.php index 4b928805fa543..d1c86d19a9849 100644 --- a/app/code/Magento/Customer/Block/Widget/Company.php +++ b/app/code/Magento/Customer/Block/Widget/Company.php @@ -69,7 +69,7 @@ public function _construct() parent::_construct(); // default template location - $this->setTemplate('widget/company.phtml'); + $this->setTemplate('Magento_Customer::widget/company.phtml'); } /** diff --git a/app/code/Magento/Customer/Block/Widget/Dob.php b/app/code/Magento/Customer/Block/Widget/Dob.php index 1a1d5d81bf13d..936563d519823 100644 --- a/app/code/Magento/Customer/Block/Widget/Dob.php +++ b/app/code/Magento/Customer/Block/Widget/Dob.php @@ -66,7 +66,7 @@ public function __construct( public function _construct() { parent::_construct(); - $this->setTemplate('widget/dob.phtml'); + $this->setTemplate('Magento_Customer::widget/dob.phtml'); } /** diff --git a/app/code/Magento/Customer/Block/Widget/Fax.php b/app/code/Magento/Customer/Block/Widget/Fax.php index 2ede51f0eec96..a775c447142df 100644 --- a/app/code/Magento/Customer/Block/Widget/Fax.php +++ b/app/code/Magento/Customer/Block/Widget/Fax.php @@ -69,7 +69,7 @@ public function _construct() parent::_construct(); // default template location - $this->setTemplate('widget/fax.phtml'); + $this->setTemplate('Magento_Customer::widget/fax.phtml'); } /** diff --git a/app/code/Magento/Customer/Block/Widget/Gender.php b/app/code/Magento/Customer/Block/Widget/Gender.php index 1d3730e6c6d58..d03c64a54fb94 100644 --- a/app/code/Magento/Customer/Block/Widget/Gender.php +++ b/app/code/Magento/Customer/Block/Widget/Gender.php @@ -59,7 +59,7 @@ public function __construct( public function _construct() { parent::_construct(); - $this->setTemplate('widget/gender.phtml'); + $this->setTemplate('Magento_Customer::widget/gender.phtml'); } /** diff --git a/app/code/Magento/Customer/Block/Widget/Name.php b/app/code/Magento/Customer/Block/Widget/Name.php index 35f3bbefb8f00..f70ddcd67af3a 100644 --- a/app/code/Magento/Customer/Block/Widget/Name.php +++ b/app/code/Magento/Customer/Block/Widget/Name.php @@ -62,7 +62,7 @@ public function _construct() parent::_construct(); // default template location - $this->setTemplate('widget/name.phtml'); + $this->setTemplate('Magento_Customer::widget/name.phtml'); } /** diff --git a/app/code/Magento/Customer/Block/Widget/Taxvat.php b/app/code/Magento/Customer/Block/Widget/Taxvat.php index e695681ad4a6c..e5c9c01dc3ac5 100644 --- a/app/code/Magento/Customer/Block/Widget/Taxvat.php +++ b/app/code/Magento/Customer/Block/Widget/Taxvat.php @@ -41,7 +41,7 @@ public function __construct( public function _construct() { parent::_construct(); - $this->setTemplate('widget/taxvat.phtml'); + $this->setTemplate('Magento_Customer::widget/taxvat.phtml'); } /** diff --git a/app/code/Magento/Customer/Block/Widget/Telephone.php b/app/code/Magento/Customer/Block/Widget/Telephone.php index b0d155eff6db5..86608ec729b70 100644 --- a/app/code/Magento/Customer/Block/Widget/Telephone.php +++ b/app/code/Magento/Customer/Block/Widget/Telephone.php @@ -69,7 +69,7 @@ public function _construct() parent::_construct(); // default template location - $this->setTemplate('widget/telephone.phtml'); + $this->setTemplate('Magento_Customer::widget/telephone.phtml'); } /** diff --git a/app/code/Magento/Dhl/Block/Adminhtml/Unitofmeasure.php b/app/code/Magento/Dhl/Block/Adminhtml/Unitofmeasure.php index deeb769c808fa..bf94a3b5c89ad 100644 --- a/app/code/Magento/Dhl/Block/Adminhtml/Unitofmeasure.php +++ b/app/code/Magento/Dhl/Block/Adminhtml/Unitofmeasure.php @@ -86,7 +86,7 @@ public function _construct() ) ); - $this->setTemplate('unitofmeasure.phtml'); + $this->setTemplate('Magento_Dhl::unitofmeasure.phtml'); } /** diff --git a/app/code/Magento/Paypal/Block/Iframe.php b/app/code/Magento/Paypal/Block/Iframe.php index 98fc05d0d2f60..bcdfae6b86dbe 100644 --- a/app/code/Magento/Paypal/Block/Iframe.php +++ b/app/code/Magento/Paypal/Block/Iframe.php @@ -110,13 +110,13 @@ protected function _construct() if (in_array($paymentCode, $this->_hssHelper->getHssMethods())) { $this->_paymentMethodCode = $paymentCode; $templatePath = str_replace('_', '', $paymentCode); - $templateFile = "{$templatePath}/iframe.phtml"; + $templateFile = "Magento_Paypal::{$templatePath}/iframe.phtml"; $directory = $this->readFactory->create($this->reader->getModuleDir('', 'Magento_Paypal')); $file = $this->resolver->getTemplateFileName($templateFile, ['module' => 'Magento_Paypal']); if ($file && $directory->isExist($directory->getRelativePath($file))) { $this->setTemplate($templateFile); } else { - $this->setTemplate('hss/iframe.phtml'); + $this->setTemplate('Magento_Paypal::hss/iframe.phtml'); } } } @@ -198,7 +198,7 @@ protected function _beforeToHtml() protected function _toHtml() { if ($this->_isAfterPaymentSave()) { - $this->setTemplate('hss/js.phtml'); + $this->setTemplate('Magento_Paypal::hss/js.phtml'); return parent::_toHtml(); } if (!$this->_shouldRender) { diff --git a/app/code/Magento/Review/Block/Form.php b/app/code/Magento/Review/Block/Form.php index 440e13deb5839..c6dfad8265ac7 100644 --- a/app/code/Magento/Review/Block/Form.php +++ b/app/code/Magento/Review/Block/Form.php @@ -139,7 +139,7 @@ protected function _construct() ); } - $this->setTemplate('form.phtml'); + $this->setTemplate('Magento_Review::form.phtml'); } /** diff --git a/app/code/Magento/Review/Block/Rating/Entity/Detailed.php b/app/code/Magento/Review/Block/Rating/Entity/Detailed.php index 0ce4f436ae704..eee00483ace66 100644 --- a/app/code/Magento/Review/Block/Rating/Entity/Detailed.php +++ b/app/code/Magento/Review/Block/Rating/Entity/Detailed.php @@ -49,7 +49,7 @@ protected function _toHtml() $reviewsCount = $this->_ratingFactory->create()->getTotalReviews($entityId, true); if ($reviewsCount == 0) { #return __('Be the first to review this product'); - $this->setTemplate('empty.phtml'); + $this->setTemplate('Magento_Review::empty.phtml'); return parent::_toHtml(); } diff --git a/app/code/Magento/UrlRewrite/Block/Edit.php b/app/code/Magento/UrlRewrite/Block/Edit.php index baee8af893083..210ed5189eb4c 100644 --- a/app/code/Magento/UrlRewrite/Block/Edit.php +++ b/app/code/Magento/UrlRewrite/Block/Edit.php @@ -65,7 +65,7 @@ public function __construct( */ protected function _prepareLayout() { - $this->setTemplate('edit.phtml'); + $this->setTemplate('Magento_UrlRewrite::edit.phtml'); $this->_addBackButton(); $this->_prepareLayoutFeatures(); diff --git a/app/code/Magento/User/Block/Role/Tab/Users.php b/app/code/Magento/User/Block/Role/Tab/Users.php index d93e7ad63e354..a95a68cbe14f3 100644 --- a/app/code/Magento/User/Block/Role/Tab/Users.php +++ b/app/code/Magento/User/Block/Role/Tab/Users.php @@ -45,7 +45,7 @@ protected function _construct() $roleId = $this->getRequest()->getParam('rid', false); /** @var \Magento\User\Model\ResourceModel\User\Collection $users */ $users = $this->_userCollectionFactory->create()->load(); - $this->setTemplate('role/users.phtml') + $this->setTemplate('Magento_User::role/users.phtml') ->assign('users', $users->getItems()) ->assign('roleId', $roleId); } From ba0d2480469fe39b2bfaf88a29ecab7d57d4859a Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Mon, 6 Aug 2018 13:46:31 -0500 Subject: [PATCH 0752/1171] MQE-1172: Bump MFTF version and deliver Magento branches - update mftf version to 2.3.3 in composer and lock file --- composer.json | 2 +- composer.lock | 99 ++++++++++++++++++++++++--------------------------- 2 files changed, 47 insertions(+), 54 deletions(-) diff --git a/composer.json b/composer.json index 8293e17ab8acf..2eba717464e68 100644 --- a/composer.json +++ b/composer.json @@ -81,7 +81,7 @@ "zendframework/zend-view": "~2.10.0" }, "require-dev": { - "magento/magento2-functional-testing-framework": "2.3.2", + "magento/magento2-functional-testing-framework": "2.3.3", "friendsofphp/php-cs-fixer": "~2.12.0", "lusitanian/oauth": "~0.8.10", "pdepend/pdepend": "2.5.2", diff --git a/composer.lock b/composer.lock index 2bb2ff1ceb672..430b233d72fc6 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f7b2384ecfca07c84ed3b89f6f6d8c7b", + "content-hash": "57ae7ad75c4d8d50eb40e4ffe0fd2740", "packages": [ { "name": "braintree/braintree_php", @@ -884,13 +884,6 @@ "reference": "68522e5768edc8e829d1f64b620a3de3753f1141", "shasum": "" }, - "archive": { - "exclude": [ - "/demos", - "/documentation", - "/tests" - ] - }, "require": { "php": ">=5.2.11" }, @@ -909,6 +902,7 @@ "Zend_": "library/" } }, + "notification-url": "https://packagist.org/downloads/", "include-path": [ "library/" ], @@ -918,14 +912,10 @@ "description": "Magento Zend Framework 1", "homepage": "http://framework.zend.com/", "keywords": [ - "framework", - "zf1" + "ZF1", + "framework" ], - "support": { - "source": "https://github.com/magento-engcom/zf1-php-7.2-support/tree/master", - "issues": "https://github.com/magento-engcom/zf1-php-7.2-support/issues" - }, - "time": "2018-04-06T17:12:22+00:00" + "time": "2018-04-06T18:49:03+00:00" }, { "name": "monolog/monolog", @@ -1994,25 +1984,28 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae" + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/7cc359f1b7b80fc25ed7796be7d96adc9b354bae", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", "shasum": "" }, "require": { "php": ">=5.3.3" }, + "suggest": { + "ext-ctype": "For best performance" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -2045,20 +2038,20 @@ "polyfill", "portable" ], - "time": "2018-04-30T19:57:29+00:00" + "time": "2018-08-06T14:22:27+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "3296adf6a6454a050679cde90f95350ad604b171" + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", - "reference": "3296adf6a6454a050679cde90f95350ad604b171", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8", + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8", "shasum": "" }, "require": { @@ -2070,7 +2063,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -2104,7 +2097,7 @@ "portable", "shim" ], - "time": "2018-04-26T10:06:28+00:00" + "time": "2018-08-06T14:22:27+00:00" }, { "name": "symfony/process", @@ -6190,16 +6183,16 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.3.2", + "version": "2.3.3", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "aac499e5deb97bad3838ec7e2a1a4b38d579d0fe" + "reference": "885c5a0562cd2e428ba6bdafaff86fce78ad6b08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/aac499e5deb97bad3838ec7e2a1a4b38d579d0fe", - "reference": "aac499e5deb97bad3838ec7e2a1a4b38d579d0fe", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/885c5a0562cd2e428ba6bdafaff86fce78ad6b08", + "reference": "885c5a0562cd2e428ba6bdafaff86fce78ad6b08", "shasum": "" }, "require": { @@ -6257,7 +6250,7 @@ "magento", "testing" ], - "time": "2018-08-03T18:21:09+00:00" + "time": "2018-08-06T18:35:58+00:00" }, { "name": "moontoast/math", @@ -6913,16 +6906,16 @@ }, { "name": "phpspec/prophecy", - "version": "1.7.6", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712" + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", "shasum": "" }, "require": { @@ -6934,12 +6927,12 @@ }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.8.x-dev" } }, "autoload": { @@ -6972,7 +6965,7 @@ "spy", "stub" ], - "time": "2018-04-18T13:57:24+00:00" + "time": "2018-08-05T17:53:17+00:00" }, { "name": "phpunit/php-code-coverage", @@ -8476,26 +8469,26 @@ }, { "name": "symfony/polyfill-php70", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "77454693d8f10dd23bb24955cffd2d82db1007a6" + "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/77454693d8f10dd23bb24955cffd2d82db1007a6", - "reference": "77454693d8f10dd23bb24955cffd2d82db1007a6", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/1e24b0c4a56d55aaf368763a06c6d1c7d3194934", + "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934", "shasum": "" }, "require": { - "paragonie/random_compat": "~1.0|~2.0", + "paragonie/random_compat": "~1.0|~2.0|~9.99", "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -8531,20 +8524,20 @@ "portable", "shim" ], - "time": "2018-04-26T10:06:28+00:00" + "time": "2018-08-06T14:22:27+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "a4576e282d782ad82397f3e4ec1df8e0f0cafb46" + "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/a4576e282d782ad82397f3e4ec1df8e0f0cafb46", - "reference": "a4576e282d782ad82397f3e4ec1df8e0f0cafb46", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/95c50420b0baed23852452a7f0c7b527303ed5ae", + "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae", "shasum": "" }, "require": { @@ -8553,7 +8546,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -8586,7 +8579,7 @@ "portable", "shim" ], - "time": "2018-04-26T10:06:28+00:00" + "time": "2018-08-06T14:22:27+00:00" }, { "name": "symfony/stopwatch", From 0b5d2af7ddf0e68a119c1ba6a94f1652837f5d0b Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Mon, 6 Aug 2018 14:32:13 -0500 Subject: [PATCH 0753/1171] MQE-1174: Deliver weekly regression enablement tests --- .../SetBundleProductAttributesActionGroup.xml | 82 ++++++++ .../Bundle/Test/Mftf/Data/ProductData.xml | 1 + .../Section/AdminProductFormBundleSection.xml | 32 ++- .../AdminBasicBundleProductAttributesTest.xml | 194 ++++++++++++++++++ .../AdminBundleProductSetEditContentTest.xml | 39 ++++ .../AdminEditRelatedBundleProductTest.xml | 78 +++++++ ...NewProductsListWidgetBundleProductTest.xml | 75 +++++++ .../ActionGroup/AdminProductActionGroup.xml | 18 ++ .../AdminProductAttributeSetActionGroup.xml | 11 + .../AdminCreateProductAttributeSection.xml | 2 + .../Section/AdminProductContentSection.xml | 1 + .../Mftf/Section/AdminProductFormSection.xml | 4 +- .../Section/AdminProductGridActionSection.xml | 3 +- ...inProductRelatedUpSellCrossSellSection.xml | 4 + .../AdminSimpleProductSetEditContentTest.xml | 78 +++++++ .../AdminSimpleSetEditRelatedProductsTest.xml | 77 +++++++ .../AdminVirtualProductSetEditContentTest.xml | 39 ++++ ...AdminVirtualSetEditRelatedProductsTest.xml | 40 ++++ ...NewProductsListWidgetSimpleProductTest.xml | 43 ++++ ...ewProductsListWidgetVirtualProductTest.xml | 46 +++++ .../Test/Mftf/Data/CatalogRuleData.xml | 4 +- .../Test/AdminCreateCatalogPriceRuleTest.xml | 116 +---------- .../Cms/Test/Mftf/Section/TinyMCESection.xml | 6 +- .../AdminConfigurableProductActionGroup.xml | 20 ++ ...nConfigurableProductSetEditContentTest.xml | 39 ++++ ...ConfigurableSetEditRelatedProductsTest.xml | 42 ++++ ...ductsListWidgetConfigurableProductTest.xml | 90 ++++++++ ...nDownloadableProductSetEditContentTest.xml | 39 ++++ ...DownloadableSetEditRelatedProductsTest.xml | 40 ++++ ...ductsListWidgetDownloadableProductTest.xml | 54 +++++ .../AdminGroupedProductSetEditContentTest.xml | 39 ++++ ...AdminGroupedSetEditRelatedProductsTest.xml | 40 ++++ ...ewProductsListWidgetGroupedProductTest.xml | 70 +++++++ .../ActionGroup/ClearCacheActionGroup.xml | 22 +- .../Mftf/Page/AdminCacheManagementPage.xml | 14 ++ .../Section/AdminCacheManagementSection.xml | 18 +- .../Mftf/Test/NewProductsListWidgetTest.xml | 29 +++ .../Mftf/Section/AdminManageSwatchSection.xml | 1 + .../StorefrontCategorySidebarSection.xml | 15 ++ .../Mftf/Test/AdminCreateImageSwatchTest.xml | 138 +++++++++++++ .../StorefrontFilterByImageSwatchTest.xml | 117 +++++++++++ .../Test/StorefrontFilterByTextSwatchTest.xml | 100 +++++++++ .../StorefrontFilterByVisualSwatchTest.xml | 120 +++++++++++ .../Mftf/Test/NewProductsListWidgetTest.xml | 42 ++++ .../lib/Magento/Mtf/Util/Command/Cli.php | 10 +- 45 files changed, 1961 insertions(+), 131 deletions(-) create mode 100644 app/code/Magento/Bundle/Test/Mftf/ActionGroup/SetBundleProductAttributesActionGroup.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualSetEditRelatedProductsTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetSimpleProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetVirtualProductTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/NewProductsListWidgetGroupedProductTest.xml create mode 100644 app/code/Magento/PageCache/Test/Mftf/Page/AdminCacheManagementPage.xml create mode 100644 app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Section/StorefrontCategorySidebarSection.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml create mode 100644 app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/SetBundleProductAttributesActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/SetBundleProductAttributesActionGroup.xml new file mode 100644 index 0000000000000..2f6e38df97e84 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/SetBundleProductAttributesActionGroup.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <actionGroup name="SetBundleProductAttributes"> + <arguments> + <argument name="attributeSet" defaultValue="Default" type="string"/> + <argument name="bundleProductName" defaultValue="defaultBundleProductNameAndSku" type="string"/> + <argument name="bundleProductSku" defaultValue="defaultBundleProductNameAndSku" type="string"/> + <argument name="price" defaultValue="10" type="string"/> + <argument name="stock" defaultValue="In Stock" type="string"/> + <argument name="weight" defaultValue="10" type="string"/> + <argument name="visibility" defaultValue="Catalog, Search" type="string"/> + <argument name="fromDate" defaultValue="10/10/2018" type="string"/> + <argument name="toDate" defaultValue="10/10/2018" type="string"/> + <argument name="country" defaultValue="Italy" type="string"/> + </arguments> + + <!-- + Pre-Reqs: + 1) Go to bundle product creation page + 2) Will not Enable/Disable + --> + + <!--Apply Attribute Set--> + <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttrSet"/> + <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{attributeSet}}" stepKey="searchForAttrSet"/> + <click selector="{{AdminProductFormSection.attributeSetFilterResult}}" stepKey="selectAttrSet"/> + + <!--Product name and SKU--> + <fillField selector="{{AdminProductFormBundleSection.productName}}" userInput="{{bundleProductName}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormBundleSection.productSku}}" userInput="{{bundleProductSku}}" stepKey="fillProductSku"/> + <click selector="{{AdminProductFormBundleSection.productName}}" stepKey="clickUnselectField"/> + + <!--Dynamic SKU Toggle--> + <checkOption selector="{{AdminProductFormBundleSection.dynamicSkuToggle}}" stepKey="clickOnToggle"/> + <click selector="{{AdminProductFormBundleSection.productName}}" stepKey="clickUnselectFieldAgain"/> + + <!--Dynamic Price Toggle--> + <checkOption selector="{{AdminProductFormBundleSection.dynamicPriceToggle}}" stepKey="clickOnDynamicPriceToggle"/> + + <!--Tax Class--> + <selectOption selector="{{AdminProductFormBundleSection.taxClassDropDown}}" userInput="Taxable Goods" stepKey="taxClassDropDown"/> + + <!--Fill out price--> + <fillField selector="{{AdminProductFormBundleSection.priceField}}" userInput="{{price}}" stepKey="fillOutPrice"/> + + <!--Stock status--> + <selectOption selector="{{AdminProductFormBundleSection.stockStatusField}}" userInput="{{stock}}" stepKey="stockStatus"/> + + <!--Dynamic weight--> + <checkOption selector="{{AdminProductFormBundleSection.dynamicWeightToggle}}" stepKey="dynamicWeight"/> + + <!--Weight--> + <fillField selector="{{AdminProductFormBundleSection.weightField}}" userInput="{{weight}}" stepKey="fillIn"/> + + <!--Visibilty--> + <selectOption selector="{{AdminProductFormBundleSection.visibilityDropDown}}" userInput="{{visibility}}" stepKey="openVisibility"/> + + <!--Categories--> + <click selector="{{AdminProductFormBundleSection.categoriesDropDown}}" stepKey="clickOnCategoriesDropDown"/> + <click selector="{{AdminProductFormBundleSection.defaultCategory}}" stepKey="selectDefaultCategory"/> + <click selector="{{AdminProductFormBundleSection.categoryDone}}" stepKey="clickOnCategoriesDoneToCloseOptions"/> + + <!--New from - to--> + <fillField selector="{{AdminProductFormBundleSection.fromDate}}" userInput="{{fromDate}}" stepKey="fillInFirstDate"/> + <fillField selector="{{AdminProductFormBundleSection.toDate}}" userInput="{{toDate}}" stepKey="fillInSecondDate"/> + + <!--Country of manufacture--> + <selectOption selector="{{AdminProductFormBundleSection.countryOfManufactureDropDown}}" userInput="{{country}}" stepKey="countryOfManufactureDropDown"/> + + <!--Save the product--> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml index f48810e0534fe..9c558861dc61d 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml @@ -23,6 +23,7 @@ <data key="urlKey2" unique="suffix">bundleproduct2</data> <data key="default_quantity1">10</data> <data key="default_quantity2">20</data> + <data key="quantity">30</data> <data key="set">4</data> <data key="type">bundle</data> <data key="price">10</data> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml index 25f1d95dc863c..2013ac7b10bd6 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml @@ -49,7 +49,9 @@ <!--NameOfProductOnProductPage--> <element name="bundleProductName" type="text" selector="//*[@id='maincontent']//span[@itemprop='name']"/> <!--EnableDisableToggle--> - <element name="enableDisableToggle" type="button" selector="//*[@id='container']//input[@name='product[status]']/.." timeout="30"/> + <element name="enableDisableToggle" type="checkbox" selector="//*[@id='container']//input[@name='product[status]']/.." timeout="30"/> + <element name="enableDisableToggleOn" type="checkbox" selector="//*[@id='container']//input[@name='product[status]' and @value='1']/.."/> + <element name="enableDisableToggleOff" type="checkbox" selector="//*[@id='container']//input[@name='product[status]' and @value='2']/.."/> <!--SearchButton--> <element name="searchButton" type="button" selector="//div[@class='data-grid-search-control-wrap']//*[@type='button']" timeout="30"/> <!--ClickOnFirstProductInCatalog--> @@ -61,9 +63,33 @@ <element name="listedBundleItem2" type="text" selector="//tr[@data-repeat-index='2']//div"/> <!--FirstProductOption--> <element name="firstProductOption" type="checkbox" selector="//div[@class='admin__data-grid-outer-wrap']//tr[@data-repeat-index='0']//input[@type='checkbox']"/> + <element name="dynamicSkuToggle" type="checkbox" selector="div[data-index='sku_type'] .admin__actions-switch-label" timeout="30"/> + <element name="dynamicPriceToggle" type="checkbox" selector="//div[@data-index='price_type']//div[@data-role='switcher']"/> + <element name="taxClassDropDown" type="select" selector="//select[@name='product[tax_class_id]']" timeout="30"/> + <element name="taxableGoodsOption" type="text" selector="//select[@name='product[tax_class_id]']//option[@data-title='Taxable Goods']"/> + <element name="stockStatusField" type="select" selector="//select[@name='product[quantity_and_stock_status][is_in_stock]']"/> + <element name="inStockOption" type="text" selector="//select[@name='product[quantity_and_stock_status][is_in_stock]']//option[@data-title='{{stock}}']" parameterized="true" timeout="30"/> + <element name="dynamicWeightToggle" type="checkbox" selector="//div[@data-index='weight_type']//div[@data-role='switcher']" timeout="30"/> + <element name="weightField" type="input" selector="//div[@data-index='weight']//input"/> + <element name="categoriesDropDown" type="block" selector="//div[@data-index='category_ids']//div" timeout="30"/> + <element name="defaultCategory" type="text" selector="//div[@data-index='category_ids']//span[contains(text(), 'Default Category')]"/> + <element name="visibilityDropDown" type="select" selector="//select[@name='product[visibility]']"/> + <element name="catalogOption1" type="text" selector="//select[@name='product[visibility]']//option[1]"/> + <element name="catalogOption2" type="text" selector="//select[@name='product[visibility]']//option[2]"/> + <element name="fromDate" type="input" selector="//div[@data-index='news_from_date']//input"/> + <element name="toDate" type="input" selector="//div[@data-index='news_to_date']//input"/> + <element name="countryOfManufactureDropDown" type="select" selector="//select[@name='product[country_of_manufacture]']"/> + <element name="selectCountryOfManufacture" type="text" selector="//select[@name='product[country_of_manufacture]']//option[@data-title='{{country}}']" parameterized="true"/> + <element name="dynamicSkuToggleOn" type="checkbox" selector="//div[@data-index='sku_type']//div[@data-role='switcher']//input[@value='0']"/> + <element name="dynamicSkuToggleOff" type="checkbox" selector="//div[@data-index='sku_type']//div[@data-role='switcher']//input[@value='1']"/> + <element name="dynamicWeightToggleOn" type="checkbox" selector="//div[@data-index='weight_type']//div[@data-role='switcher']//input[@value='0']"/> + <element name="dynamicWeightToggleOff" type="checkbox" selector="//div[@data-index='weight_type']//div[@data-role='switcher']//input[@value='1']"/> + <element name="categoryFieldName" type="text" selector="//fieldset[@data-index='container_category_ids']//label//span" timeout="30"/> + <element name="categoryDone" type="button" selector=".admin__action-multiselect-actions-wrap [type='button'] span" timeout="30"/> + <element name="dynamicPriceToggleOff" type="checkbox" selector="//div[@data-index='price_type']//div[@data-role='switcher']//input[@value='1']"/> <!--Category Selection--> - <element name="categoriesDropDown" type="multiselect" selector="//div[@data-index='category_ids']//div" timeout="30"/> - <element name="defaultCategory" type="multiselect" selector="//div[@data-index='category_ids']//span[contains(text(), 'Default Category')]"/> + <!-- <element name="categoriesDropDown" type="multiselect" selector="//div[@data-index='category_ids']//div" timeout="30"/> --> + <!-- <element name="defaultCategory" type="multiselect" selector="//div[@data-index='category_ids']//span[contains(text(), 'Default Category')]"/> --> <element name="categoryByName" type="multiselect" selector="//div[@data-index='category_ids']//span[contains(text(), '{{category}}')]" parameterized="true"/> <element name="searchForCategory" type="input" selector="div.action-menu._active > div.admin__action-multiselect-search-wrap input" timeout="30"/> <element name="selectCategory" type="multiselect" selector="//div[@class='action-menu _active']//label[@class='admin__action-multiselect-label']"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml new file mode 100644 index 0000000000000..eec21dc3551d9 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml @@ -0,0 +1,194 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminBasicBundleProductAttributesTest"> + <annotations> + <features value="Bundle"/> + <stories value="Create/Edit bundle product in Admin"/> + <title value="Admin should be able to set/edit all the basic product attributes when creating/editing a bundle product"/> + <description value="Admin should be able to set/edit all the basic product attributes when creating/editing a bundle product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-222"/> + <group value="Bundle"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + <!--Create attribute set--> + <actionGroup ref="CreateDefaultAttributeSet" stepKey="createDefaultAttributeSet"> + <argument name="label" value="{{ProductAttributeFrontendLabel.label}}"/> + </actionGroup> + + <!--Go to product creation page--> + <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage"/> + <waitForPageLoad stepKey="waitForBundleProductCreationPage"/> + + <!--Enable/Disable Toggle--> + <checkOption selector="{{AdminProductFormBundleSection.enableDisableToggle}}" stepKey="clickOnEnableDisableToggle"/> + + <!--Fill out product attributes--> + <actionGroup ref="SetBundleProductAttributes" stepKey="fillOutAllAttributes"> + <!--primarily uses default values--> + <argument name="attributeSet" value="{{ProductAttributeFrontendLabel.label}}"/> + <argument name="bundleProductName" value="{{BundleProduct.name}}"/> + <argument name="bundleProductSku" value="{{BundleProduct.sku}}"/> + <argument name="visibilty" value="catalog"/> + </actionGroup> + + <!--Verify form was filled out correctly--> + + <!--Enable/Disable Toggle check--> + <dontSeeCheckboxIsChecked selector="{{AdminProductFormBundleSection.enableDisableToggle}}" stepKey="seeToggleIsOff"/> + + <!--Apply Attribute Set--> + <seeOptionIsSelected selector="{{AdminProductFormSection.attributeSet}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="seeAttributeSet"/> + + <!--Product name and SKU--> + <seeInField selector="{{AdminProductFormBundleSection.productName}}" userInput="{{BundleProduct.name}}" stepKey="seeProductName"/> + <seeInField selector="{{AdminProductFormBundleSection.productSku}}" userInput="{{BundleProduct.sku}}" stepKey="seeProductSku"/> + + <!--Dynamic SKU Toggle--> + <dontSeeCheckboxIsChecked selector="{{AdminProductFormBundleSection.dynamicSkuToggle}}" stepKey="seeDynamicSkuToggleOff"/> + + <!--Dynamic Price Toggle--> + <dontSeeCheckboxIsChecked selector="{{AdminProductFormBundleSection.dynamicPriceToggle}}" stepKey="seeDynamicPriceToggleOff"/> + + <!--Tax Class--> + <seeOptionIsSelected selector="{{AdminProductFormBundleSection.taxClassDropDown}}" userInput="Taxable Goods" stepKey="seeCorrectTaxClass"/> + + <!--Fill out price--> + <seeInField selector="{{AdminProductFormBundleSection.priceField}}" userInput="10" stepKey="seePrice"/> + + <!--Stock status--> + <seeOptionIsSelected selector="{{AdminProductFormBundleSection.stockStatusField}}" userInput="In Stock" stepKey="seeStockStatus"/> + + <!--Dynamic weight--> + <dontSeeCheckboxIsChecked selector="{{AdminProductFormBundleSection.dynamicWeightToggle}}" stepKey="seeDynamicWeightOff"/> + + <!--Weight--> + <seeInField selector="{{AdminProductFormBundleSection.weightField}}" userInput="10" stepKey="seeWeight"/> + + <!--Visibilty--> + <seeOptionIsSelected selector="{{AdminProductFormBundleSection.visibilityDropDown}}" userInput="Catalog" stepKey="seeVisibility"/> + + <!--Categories--> + <seeElement selector="{{AdminProductFormBundleSection.defaultCategory}}" stepKey="seeDefaultCategory"/> + + <!--New from - to--> + <seeInField selector="{{AdminProductFormBundleSection.fromDate}}" userInput="10/10/2018" stepKey="seeFirstDate"/> + <seeInField selector="{{AdminProductFormBundleSection.toDate}}" userInput="10/10/2018" stepKey="seeSecondDate"/> + + <!--Country of manufacture--> + <seeOptionIsSelected selector="{{AdminProductFormBundleSection.countryOfManufactureDropDown}}" userInput="Italy" stepKey="seeCountryOfManufacture"/> + + <!--Create second attribute set for edit--> + <actionGroup ref="CreateDefaultAttributeSet" stepKey="createSecondAttributeSet"> + <argument name="label" value="{{ProductAttributeFrontendLabel.label}}2"/> + </actionGroup> + + <!--Filter catalog--> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> + <waitForPageLoad stepKey="WaitForPageToLoad"/> + <actionGroup ref="filterProductGridByName" stepKey="filterBundleProductOptionsDownToName"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <click selector="{{AdminProductFiltersSection.attributeSetOfFirstRow(ProductAttributeFrontendLabel.label)}}" stepKey="clickAttributeSet2"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + + <!--Edit fields--> + + <!--Enable/Disable Toggle--> + <checkOption selector="{{AdminProductFormBundleSection.enableDisableToggle}}" stepKey="clickOnEnableDisableToggleAgain"/> + + <!--Apply Attribute Set--> + <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttrSet"/> + <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{ProductAttributeFrontendLabel.label}}2" stepKey="searchForAttrSet"/> + <click selector="{{AdminProductFormSection.attributeSetFilterResult}}" stepKey="selectAttrSet"/> + + <!--Product name and SKU--> + <fillField selector="{{AdminProductFormBundleSection.productName}}" userInput="{{BundleProduct.name2}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormBundleSection.productSku}}" userInput="{{BundleProduct.sku2}}" stepKey="fillProductSku"/> + <click selector="{{AdminProductFormBundleSection.productName}}" stepKey="clickUnselectField"/> + + <!--Dynamic SKU Toggle--> + <checkOption selector="{{AdminProductFormBundleSection.dynamicSkuToggle}}" stepKey="clickOnToggle"/> + <click selector="{{AdminProductFormBundleSection.productName}}" stepKey="clickUnselectFieldAgain"/> + + <!--Fill out price--> + <fillField selector="{{AdminProductFormBundleSection.priceField}}" userInput="20" stepKey="fillOutPrice"/> + + <!--Stock status--> + <selectOption selector="{{AdminProductFormBundleSection.stockStatusField}}" userInput="Out of Stock" stepKey="stockStatus"/> + + <!--Dynamic weight--> + <checkOption selector="{{AdminProductFormBundleSection.dynamicWeightToggle}}" stepKey="dynamicWeight"/> + + <!--Visibilty--> + <selectOption selector="{{AdminProductFormBundleSection.visibilityDropDown}}" userInput="Not Visible Individually" stepKey="openVisibility"/> + + <!--Categories--> + <click selector="{{AdminProductFormBundleSection.categoriesDropDown}}" stepKey="clickOnCategoriesDropDown"/> + <click selector="{{AdminProductFormBundleSection.categoryFieldName}}" stepKey="clickOnCategoriesFieldName"/> + + <!--New from - to--> + <fillField selector="{{AdminProductFormBundleSection.fromDate}}" userInput="10/20/2018" stepKey="fillInFirstDate"/> + <fillField selector="{{AdminProductFormBundleSection.toDate}}" userInput="10/20/2018" stepKey="fillInSecondDate"/> + + <!--Country of manufacture--> + <selectOption selector="{{AdminProductFormBundleSection.countryOfManufactureDropDown}}" userInput="France" stepKey="countryOfManufactureDropDown"/> + + <!--Save the product--> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> + + <!--Verify form was filled out correctly after edit--> + + <!--Enable/Disable Toggle--> + <seeElement selector="{{AdminProductFormBundleSection.enableDisableToggleOn}}" stepKey="seeToggleIsOn2"/> + + <!--Attribute Set--> + <seeOptionIsSelected selector="{{AdminProductFormSection.attributeSet}}" userInput="{{ProductAttributeFrontendLabel.label}}2" stepKey="seeAttributeSet2"/> + + <!--Product name and SKU--> + <seeInField selector="{{AdminProductFormBundleSection.productName}}" userInput="{{BundleProduct.name2}}" stepKey="seeProductName2"/> + <seeInField selector="{{AdminProductFormBundleSection.productSku}}" userInput="{{BundleProduct.sku2}}" stepKey="seeProductSku2"/> + + <!--Dynamic SKU Toggle--> + <seeElement selector="{{AdminProductFormBundleSection.dynamicSkuToggleOn}}" stepKey="seeDynamicSkuToggleOn2"/> + + <!--Tax Class--> + <seeOptionIsSelected selector="{{AdminProductFormBundleSection.taxClassDropDown}}" userInput="Taxable Goods" stepKey="seeCorrectTaxClass2"/> + + <!--Price--> + <seeInField selector="{{AdminProductFormBundleSection.priceField}}" userInput="20" stepKey="seePrice2"/> + + <!--Stock status--> + <seeOptionIsSelected selector="{{AdminProductFormBundleSection.stockStatusField}}" userInput="Out of Stock" stepKey="seeStockStatus2"/> + + <!--Dynamic weight--> + <seeElement selector="{{AdminProductFormBundleSection.dynamicWeightToggleOn}}" stepKey="seeDynamicWeightOn2"/> + + <!--Visibilty--> + <seeOptionIsSelected selector="{{AdminProductFormBundleSection.visibilityDropDown}}" userInput="Not Visible Individually" stepKey="seeVisibility2"/> + + <!--Categories--> + <seeElement selector="{{AdminProductFormBundleSection.categoriesDropDown}}" stepKey="seeDefaultCategory2"/> + + <!--New from - to--> + <seeInField selector="{{AdminProductFormBundleSection.fromDate}}" userInput="10/20/2018" stepKey="seeFirstDate2"/> + <seeInField selector="{{AdminProductFormBundleSection.toDate}}" userInput="10/20/2018" stepKey="seeSecondDate2"/> + + <!--Country of manufacture--> + <seeOptionIsSelected selector="{{AdminProductFormBundleSection.countryOfManufactureDropDown}}" userInput="France" stepKey="seeCountryOfManufacture2"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml new file mode 100644 index 0000000000000..02eaeba8d7e78 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminBundleProductSetEditContentTest" extends="AdminSimpleProductSetEditContentTest"> + <annotations> + <features value="Bundle"/> + <stories value="Create/Edit bundle product in Admin"/> + <title value="Admin should be able to set/edit product Content when editing a bundle product"/> + <description value="Admin should be able to set/edit product Content when editing a bundle product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3343"/> + <group value="Bundle"/> + </annotations> + <after> + <!-- Delete bundle product --> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + </after> + + <!-- Create product --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillProductForm"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <!--Checking content storefront--> + <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml new file mode 100644 index 0000000000000..4bbb01ceae305 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminEditRelatedBundleProductTest"> + <annotations> + <features value="Bundle"/> + <stories value="Create/Edit bundle product in Admin"/> + <title value="Admin should be able to set/edit Related Products information when editing a bundle product"/> + <description value="Admin should be able to set/edit Related Products information when editing a bundle product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3342"/> + <group value="Bundle"/> + </annotations> + <before> + <!--Admin login--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct0"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + </before> + <after> + <!-- Delete the bundled product --> + <actionGroup stepKey="deleteBundle" ref="deleteProductUsingProductGrid"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <!--Logging out--> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct0" stepKey="deleteSimpleProduct0"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + </after> + + <!-- Create a bundle product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageBundle"/> + <waitForPageLoad stepKey="waitForProductPageLoadBundle"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateBundleProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillBundleProductNameAndSku"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct0"> + <argument name="sku" value="$$simpleProduct0.sku$$"/> + </actionGroup> + + <!--Save the product--> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> + + <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct1"> + <argument name="sku" value="$$simpleProduct1.sku$$"/> + </actionGroup> + + <!--Remove previous related product--> + <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.removeRelatedProduct($$simpleProduct0.sku$$)}}" stepKey="removeRelatedProduct"/> + + <!--Save the product--> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButtonAfterEdit"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShownAgain"/> + + <!--See related product in admin--> + <scrollTo selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" stepKey="scrollTo"/> + <conditionalClick selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" dependentSelector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDependent}}" visible="false" stepKey="openDropDownIfClosedRelatedSee"/> + <see selector="{{AdminProductFormRelatedUpSellCrossSellSection.selectedRelatedProduct}}" userInput="$$simpleProduct1.sku$$" stepKey="seeRelatedProduct"/> + + <!--See related product in storefront--> + <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> + <waitForPageLoad stepKey="waitForStorefront"/> + <see userInput="$$simpleProduct1.sku$$" stepKey="seeRelatedProductInStorefront"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml new file mode 100644 index 0000000000000..8c330ef9184a0 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewProductsListWidgetBundleProductTest" extends="NewProductsListWidgetTest"> + <annotations> + <features value="Bundle"/> + <stories value="New products list widget"/> + <title value="Admin should be able to set Bundle Product as new so that it shows up in the Catalog New Products List Widget"/> + <description value="Admin should be able to set Bundle Product as new so that it shows up in the Catalog New Products List Widget"/> + <severity value="MAJOR"/> + <testCaseId value="MC-123"/> + <group value="Bundle"/> + </annotations> + + <before> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + </before> + + <after> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!-- A Cms page containing the New Products Widget gets created here via extends --> + + <!-- Create a product to appear in the widget, fill in basic info first --> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductList"/> + <waitForPageLoad stepKey="waitForProductList"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductToggle"/> + <click selector="{{AdminProductGridActionSection.addBundleProduct}}" stepKey="clickAddBundleProduct"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{_defaultProduct.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{_defaultProduct.sku}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.setProductAsNewFrom}}" userInput="01/1/2000" stepKey="fillProductNewFrom"/> + <fillField selector="{{AdminProductFormSection.setProductAsNewTo}}" userInput="01/1/2099" stepKey="fillProductNewTo"/> + + <!-- and then configure bundled items for this product --> + + <scrollTo selector="{{AdminProductFormBundleSection.addOption}}" stepKey="scrollToAddOptionButton"/> + <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption"/> + <waitForPageLoad stepKey="waitForOptions"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="MFTF Test Bundle 1" stepKey="fillOptionTitle"/> + <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="checkbox" stepKey="selectInputType"/> + <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> + <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> + <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddSelectedBundleProducts"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="1" stepKey="fillProductDefaultQty1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="1" stepKey="fillProductDefaultQty2"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + + <!-- If PageCache is enabled, Cache clearing happens here, via merge --> + + <!-- Check for product on the CMS page with the New Products widget --> + + <amOnPage url="{{_newDefaultCmsPage.identifier}}" stepKey="amOnCmsPage"/> + <waitForPageLoad stepKey="waitForCmsPage"/> + <see selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="{{_defaultProduct.name}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index bca6ae2b60bf3..3d0c1288271eb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -184,6 +184,24 @@ <see selector="{{element}}" userInput="{{expectedText}}" stepKey="assertText"/> </actionGroup> + <!--Related products--> + <actionGroup name="addRelatedProductBySku"> + <arguments> + <argument name="sku"/> + </arguments> + <!--Scroll up to avoid error--> + <scrollTo selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" x="0" y="-100" stepKey="scrollTo"/> + <conditionalClick selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" dependentSelector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDependent}}" visible="false" stepKey="openDropDownIfClosedRelatedUpSellCrossSell"/> + <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.AddRelatedProductsButton}}" stepKey="clickAddRelatedProductButton"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminProductModalSlideGridSection.productGridXRowYColumnButton('1', '1')}}" stepKey="selectProduct"/> + <click selector="{{AdminAddRelatedProductsModalSection.AddSelectedProductsButton}}" stepKey="addRelatedProductSelected"/> + </actionGroup> + <!--Add special price to product in Admin product page--> <actionGroup name="AddSpecialPriceToProductActionGroup"> <arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml index 33f4ccac2b98f..e943227903884 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml @@ -34,4 +34,15 @@ <click selector="{{AdminProductAttributeSetActionSection.save}}" stepKey="clickSave"/> <see userInput="You saved the attribute set" selector="{{AdminMessagesSection.success}}" stepKey="successMessage"/> </actionGroup> + <actionGroup name="CreateDefaultAttributeSet"> + <!--Generic atrribute set creation--> + <arguments> + <argument name="label" type="string"/> + </arguments> + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{AdminProductAttributeSetGridSection.addAttributeSetBtn}}" stepKey="clickAddAttributeSet"/> + <fillField selector="{{AdminProductAttributeSetSection.name}}" userInput="{{label}}" stepKey="fillName"/> + <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickSave1"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 377bc18d64764..24bb0a69c15d3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -9,6 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AttributePropertiesSection"> + <element name="propertiesTab" type="button" selector="#product_attribute_tabs_main"/> <element name="DefaultLabel" type="input" selector="#attribute_label"/> <element name="InputType" type="select" selector="#frontend_input"/> <element name="ValueRequired" type="select" selector="#is_required"/> @@ -19,6 +20,7 @@ <element name="SaveAndEdit" type="button" selector="#save_and_edit_button" timeout="30"/> <element name="TinyMCE4" type="button" selector="//span[text()='Default Value']/parent::label/following-sibling::div//div[@class='mce-branding-powered-by']"/> <element name="checkIfTabOpen" selector="//div[@id='advanced_fieldset-wrapper' and not(contains(@class,'opened'))]" type="button"/> + <element name="useInLayeredNavigation" type="select" selector="#is_filterable"/> </section> <section name="StorefrontPropertiesSection"> <element name="StoreFrontPropertiesTab" selector="#product_attribute_tabs_front" type="button"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml index b73c630d6d963..fc8666ec1c797 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml @@ -12,5 +12,6 @@ <element name="sectionHeader" type="button" selector="div[data-index='content']" timeout="30"/> <element name="descriptionTextArea" type="textarea" selector="#product_form_description"/> <element name="shortDescriptionTextArea" type="textarea" selector="#product_form_short_description"/> + <element name="sectionHeaderIfNotShowing" type="button" selector="//div[@data-index='content']//div[contains(@class, '_hide')]"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 604eba61a05af..c056a4975efde 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormSection"> <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> @@ -42,6 +42,8 @@ <element name="visibilityUseDefault" type="checkbox" selector="//input[@name='use_default[visibility]']"/> <element name="divByDataIndex" type="input" selector="div[data-index='{{var}}']" parameterized="true"/> <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//label[text()='{{attributeLabel}}']" parameterized="true"/> + <element name="setProductAsNewFrom" type="input" selector="input[name='product[news_from_date]']"/> + <element name="setProductAsNewTo" type="input" selector="input[name='product[news_to_date]']"/> </section> <section name="ProductInWebsitesSection"> <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridActionSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridActionSection.xml index 4ce9580405a97..3b74041684017 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridActionSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridActionSection.xml @@ -7,8 +7,9 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductGridActionSection"> + <element name="addProductBtn" type="button" selector="#add_new_product-button" timeout="30"/> <element name="addProductToggle" type="button" selector=".action-toggle.primary.add"/> <element name="addSimpleProduct" type="button" selector=".item[data-ui-id='products-list-add-new-product-button-item-simple']" timeout="30"/> <element name="addGroupedProduct" type="button" selector=".item[data-ui-id='products-list-add-new-product-button-item-grouped']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml index 636a7b5c85e8d..36b0623f28695 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml @@ -13,5 +13,9 @@ <element name="relatedProductSectionText" type="text" selector=".fieldset-wrapper.admin__fieldset-section[data-index='related']"/> <element name="upSellProductSectionText" type="text" selector=".fieldset-wrapper.admin__fieldset-section[data-index='upsell']"/> <element name="crossSellProductSectionText" type="text" selector=".fieldset-wrapper.admin__fieldset-section[data-index='crosssell']"/> + <element name="relatedDropdown" type="block" selector="//div[@data-index='related']" timeout="30"/> + <element name="relatedDependent" type="block" selector="//div[@data-index='related']//div[contains(@class, '_show')]"/> + <element name="selectedRelatedProduct" type="block" selector="//span[@data-index='name']"/> + <element name="removeRelatedProduct" type="button" selector="//span[text()='Related Products']//..//..//..//span[text()='{{productName}}']//..//..//..//..//..//button[@class='action-delete']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml new file mode 100644 index 0000000000000..98c708b137657 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminSimpleProductSetEditContentTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create/edit simple product"/> + <title value="Admin should be able to set/edit product Content when editing a simple product"/> + <description value="Admin should be able to set/edit product Content when editing a simple product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3422"/> + <group value="Catalog"/> + </annotations> + <before> + <!--Admin Login--> + <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + </before> + <after> + <!-- Delete simple product --> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="SimpleProduct"/> + </actionGroup> + <!--Admin Logout--> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="SimpleProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <argument name="product" value="SimpleProduct"/> + </actionGroup> + + <!--Add content--> + <!--A generic scroll scrolls past this element, in doing this it fails to execute certain actions on the element and others below it. By scrolling slightly above it it resolves this issue.--> + <scrollTo selector="{{AdminProductContentSection.sectionHeader}}" x="0" y="-100" stepKey="scrollTo"/> + <click selector="{{AdminProductContentSection.sectionHeader}}" stepKey="openDescriptionDropDown"/> + <fillField selector="{{AdminProductContentSection.descriptionTextArea}}" userInput="This is the long description" stepKey="fillLongDescription"/> + <fillField selector="{{AdminProductContentSection.shortDescriptionTextArea}}" userInput="This is the short description" stepKey="fillShortDescription"/> + + <!--save the product--> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> + + <!--Edit content--> + <click selector="{{AdminProductContentSection.sectionHeader}}" stepKey="openDescriptionDropDownEdit"/> + <scrollTo selector="{{AdminProductContentSection.sectionHeader}}" stepKey="scrollToEdit"/> + <fillField selector="{{AdminProductContentSection.descriptionTextArea}}" userInput="EDIT ~ This is the long description ~ EDIT" stepKey="editLongDescription"/> + <fillField selector="{{AdminProductContentSection.shortDescriptionTextArea}}" userInput="EDIT ~ This is the short description ~ EDIT" stepKey="editShortDescription"/> + + <!--save the product--> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButtonAfterEdit"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShownAgain"/> + + <!--Checking content admin--> + <click selector="{{AdminProductContentSection.sectionHeader}}" stepKey="openDescriptionDropDownAgain"/> + <scrollTo selector="{{AdminProductContentSection.sectionHeader}}" stepKey="scrollToAgain"/> + <seeInField selector="{{AdminProductContentSection.descriptionTextArea}}" userInput="EDIT ~ This is the long description ~ EDIT" stepKey="seeLongDescriptionAdmin"/> + <seeInField selector="{{AdminProductContentSection.shortDescriptionTextArea}}" userInput="EDIT ~ This is the short description ~ EDIT" stepKey="seeShortDescriptionAdmin"/> + + <!--Checking content storefront--> + <amOnPage url="{{SimpleProduct.sku}}.html" stepKey="goToStorefront"/> + <waitForPageLoad stepKey="waitForStorefront"/> + <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="EDIT ~ This is the long description ~ EDIT" stepKey="seeLongDescriptionStorefront"/> + <see selector="{{StorefrontProductInfoMainSection.productShortDescription}}" userInput="EDIT ~ This is the short description ~ EDIT" stepKey="seeShortDescriptionStorefront"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml new file mode 100644 index 0000000000000..48b9094d6d791 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminSimpleSetEditRelatedProductsTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create/edit simple product"/> + <title value="Admin should be able to set/edit Related Products information when editing a simple product"/> + <description value="Admin should be able to set/edit Related Products information when editing a simple product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3411"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct0"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + </before> + <after> + <!-- Delete simple product --> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="SimpleProduct3"/> + </actionGroup> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct0" stepKey="deleteSimpleProduct0"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + </after> + + <!--Create product--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="SimpleProduct3"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <argument name="product" value="SimpleProduct3"/> + </actionGroup> + + <!--Add related product--> + <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct0"> + <argument name="sku" value="$$simpleProduct0.sku$$"/> + </actionGroup> + + <!--Save the product--> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> + + <!--Add another related product--> + <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct1"> + <argument name="sku" value="$$simpleProduct1.sku$$"/> + </actionGroup> + + <!--Remove previous related product--> + <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.removeRelatedProduct($$simpleProduct0.sku$$)}}" stepKey="removeRelatedProduct"/> + + <!--Save the product--> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButtonAfterEdit"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShownAgain"/> + + <!--See related product in admin--> + <scrollTo selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" stepKey="scrollTo"/> + <conditionalClick selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" dependentSelector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDependent}}" visible="false" stepKey="openDropDownIfClosedRelatedSee"/> + <see selector="{{AdminProductFormRelatedUpSellCrossSellSection.selectedRelatedProduct}}" userInput="$$simpleProduct1.sku$$" stepKey="seeRelatedProduct"/> + + <!--See related product in storefront--> + <amOnPage url="{{SimpleProduct3.sku}}.html" stepKey="goToStorefront"/> + <waitForPageLoad stepKey="waitForStorefront"/> + <seeElement selector="{{StorefrontProductRelatedProductsSection.relatedProductName($$simpleProduct1.sku$$)}}" stepKey="seeRelatedProductInStorefront"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml new file mode 100644 index 0000000000000..bb8f76ebe7bf6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminVirtualProductSetEditContentTest" extends="AdminSimpleProductSetEditContentTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create/edit virtual product"/> + <title value="Admin should be able to set/edit product Content when editing a virtual product"/> + <description value="Admin should be able to set/edit product Content when editing a virtual product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3425"/> + <group value="Catalog"/> + </annotations> + <after> + <!-- Delete virtual product --> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + </after> + + <!-- Create product --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + + <!--Checking content storefront--> + <amOnPage url="{{defaultVirtualProduct.sku}}.html" stepKey="goToStorefront"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualSetEditRelatedProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualSetEditRelatedProductsTest.xml new file mode 100644 index 0000000000000..f3d0f023913ff --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualSetEditRelatedProductsTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminVirtualSetEditRelatedProductsTest" extends="AdminSimpleSetEditRelatedProductsTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create/edit virtual product"/> + <title value="Admin should be able to set/edit Related Products information when editing a virtual product"/> + <description value="Admin should be able to set/edit Related Products information when editing a virtual product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3415"/> + <group value="Catalog"/> + </annotations> + <before></before> + <after> + <!-- Delete virtual product --> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + </after> + + <!-- Create product --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + + <!--See related product in storefront--> + <amOnPage url="{{defaultVirtualProduct.sku}}.html" stepKey="goToStorefront"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetSimpleProductTest.xml new file mode 100644 index 0000000000000..66732454c2332 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetSimpleProductTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewProductsListWidgetSimpleProductTest" extends="NewProductsListWidgetTest"> + <annotations> + <features value="Catalog"/> + <stories value="New products list widget"/> + <title value="Admin should be able to set Simple Product as new so that it shows up in the Catalog New Products List Widget"/> + <description value="Admin should be able to set Simple Product as new so that it shows up in the Catalog New Products List Widget"/> + <severity value="MAJOR"/> + <testCaseId value="MC-104"/> + <group value="Catalog"/> + </annotations> + + <!-- A Cms page containing the New Products Widget gets created here via extends --> + + <!-- Create a Simple Product to appear in the widget --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductList"/> + <waitForPageLoad stepKey="waitForProductList"/> + <click selector="{{AdminProductGridActionSection.addProductBtn}}" stepKey="clickAddProduct"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{_defaultProduct.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{_defaultProduct.sku}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{_defaultProduct.price}}" stepKey="fillProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="100" stepKey="fillProductQuantity"/> + <fillField selector="{{AdminProductFormSection.setProductAsNewFrom}}" userInput="01/1/2000" stepKey="fillProductNewFrom"/> + <fillField selector="{{AdminProductFormSection.setProductAsNewTo}}" userInput="01/1/2099" stepKey="fillProductNewTo"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + + <!-- If PageCache is enabled, Cache clearing happens here via merge --> + + <!-- Check for product on the CMS page with the New Products widget --> + <amOnPage url="{{_newDefaultCmsPage.identifier}}" stepKey="amOnCmsPage"/> + <waitForPageLoad stepKey="waitForCmsPage"/> + <see selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="{{_defaultProduct.name}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetVirtualProductTest.xml new file mode 100644 index 0000000000000..f17981404f349 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetVirtualProductTest.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewProductsListWidgetVirtualProductTest" extends="NewProductsListWidgetTest"> + <annotations> + <features value="Catalog"/> + <stories value="New products list widget"/> + <title value="Admin should be able to set Virtual Product as new so that it shows up in the Catalog New Products List Widget"/> + <description value="Admin should be able to set Virtual Product as new so that it shows up in the Catalog New Products List Widget"/> + <severity value="MAJOR"/> + <testCaseId value="MC-122"/> + <group value="Catalog"/> + </annotations> + + <!-- A Cms page containing the New Products Widget gets created here via extends --> + + <!-- Create a Virtual Product to appear in the widget --> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductList"/> + <waitForPageLoad stepKey="waitForProductList"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="toggleAddProductButton"/> + <click selector="{{AdminProductGridActionSection.addVirtualProduct}}" stepKey="clickAddVirtualProduct"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{_defaultProduct.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{_defaultProduct.sku}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="123.45" stepKey="fillProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="100" stepKey="fillProductQuantity"/> + <fillField selector="{{AdminProductFormSection.setProductAsNewFrom}}" userInput="01/1/2000" stepKey="fillProductNewFrom"/> + <fillField selector="{{AdminProductFormSection.setProductAsNewTo}}" userInput="01/1/2099" stepKey="fillProductNewTo"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + + <!-- If PageCache is enabled, Cache clearing happens here, via merge --> + + <!-- Check for product on the CMS page with the New Products widget --> + + <amOnPage url="{{_newDefaultCmsPage.identifier}}" stepKey="amOnCmsPage"/> + <waitForPageLoad stepKey="waitForCmsPage"/> + <see selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="{{_defaultProduct.name}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml index 3199a53026a92..a996e3ea958b1 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml @@ -33,7 +33,7 @@ <item>1</item> </array> <data key="simple_action">by_fixed</data> - <data key="discount_amount">10</data> + <data key="discount_amount">12.3</data> </entity> <entity name="CatalogRuleToPercent" type="catalogRule"> @@ -61,6 +61,6 @@ <item>1</item> </array> <data key="simple_action">to_fixed</data> - <data key="discount_amount">100</data> + <data key="discount_amount">110.7</data> </entity> </entities> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml index 70fd745986567..1e7aac745d748 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml @@ -28,7 +28,7 @@ <!-- log in and create the price rule --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"/> - <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> + <actionGroup stepKey="selectNotLoggedInCustomerGroup" ref="selectNotLoggedInCustomerGroup"/> <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> </before> @@ -65,7 +65,7 @@ <see stepKey="seeNewPriceInCart" selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$110.70"/> </test> - <test name="AdminCreateCatalogPriceRuleByFixedTest"> + <test name="AdminCreateCatalogPriceRuleByFixedTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> <annotations> <features value="CatalogRule"/> <stories value="Create catalog price rule"/> @@ -76,55 +76,19 @@ <group value="CatalogRule"/> </annotations> <before> - <!-- Create the simple product and category that it will be in --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- log in and create the price rule --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> <argument name="catalogRule" value="CatalogRuleByFixed"/> </actionGroup> - <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> - <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> - <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> </before> <after> - <!-- delete the simple product and catalog price rule and logout --> - <amOnPage stepKey="goToPriceRulePage" url="admin/catalog_rule/promo_catalog/"/> <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> <argument name="name" value="{{CatalogRuleByFixed.name}}"/> <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> </actionGroup> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> </after> - - <!-- Go to category page and make sure that all of the prices are correct --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryPage"/> - <waitForPageLoad stepKey="waitForCategory"/> - <see stepKey="seeOldPrice" selector="{{StorefrontCategoryProductSection.ProductOldPriceByNumber('1')}}" userInput="$$createProduct.price$$"/> - <see stepKey="seeNewPrice" selector="{{StorefrontCategoryProductSection.ProductSpecialPriceByNumber('1')}}" userInput="$113.00"/> - - <!-- Go to the simple product page and check that the prices are correct --> - <amOnPage stepKey="goToProductPage" url="$$createProduct.sku$$.html"/> - <waitForPageLoad stepKey="waitForProductPage"/> - <see stepKey="seeOldPriceTag" selector="{{StorefrontProductInfoMainSection.oldPriceTag}}" userInput="Regular Price"/> - <see stepKey="seeOldPrice2" selector="{{StorefrontProductInfoMainSection.oldPriceAmount}}" userInput="$$createProduct.price$$"/> - <see stepKey="seeNewPrice2" selector="{{StorefrontProductInfoMainSection.updatedPrice}}" userInput="$113.00"/> - - <!-- Add the product to cart and check that the price is correct there --> - <click stepKey="addToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForAddedToCart"/> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCart"/> - <see stepKey="seeNewPriceInCart" selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$113.00"/> </test> - <test name="AdminCreateCatalogPriceRuleToPercentTest"> + <test name="AdminCreateCatalogPriceRuleToPercentTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> <annotations> <features value="CatalogRule"/> <stories value="Create catalog price rule"/> @@ -135,55 +99,19 @@ <group value="CatalogRule"/> </annotations> <before> - <!-- Create the simple product and category that it will be in --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- log in and create the price rule --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> <argument name="catalogRule" value="CatalogRuleToPercent"/> </actionGroup> - <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> - <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> - <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> </before> <after> - <!-- delete the simple product and catalog price rule and logout --> - <amOnPage stepKey="goToPriceRulePage" url="admin/catalog_rule/promo_catalog/"/> <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> <argument name="name" value="{{CatalogRuleToPercent.name}}"/> <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> </actionGroup> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> </after> - - <!-- Go to category page and make sure that all of the prices are correct --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryPage"/> - <waitForPageLoad stepKey="waitForCategory"/> - <see stepKey="seeOldPrice" selector="{{StorefrontCategoryProductSection.ProductOldPriceByNumber('1')}}" userInput="$$createProduct.price$$"/> - <see stepKey="seeNewPrice" selector="{{StorefrontCategoryProductSection.ProductSpecialPriceByNumber('1')}}" userInput="$110.70"/> - - <!-- Go to the simple product page and check that the prices are correct --> - <amOnPage stepKey="goToProductPage" url="$$createProduct.sku$$.html"/> - <waitForPageLoad stepKey="waitForProductPage"/> - <see stepKey="seeOldPriceTag" selector="{{StorefrontProductInfoMainSection.oldPriceTag}}" userInput="Regular Price"/> - <see stepKey="seeOldPrice2" selector="{{StorefrontProductInfoMainSection.oldPriceAmount}}" userInput="$$createProduct.price$$"/> - <see stepKey="seeNewPrice2" selector="{{StorefrontProductInfoMainSection.updatedPrice}}" userInput="$110.70"/> - - <!-- Add the product to cart and check that the price is correct there --> - <click stepKey="addToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForAddedToCart"/> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCart"/> - <see stepKey="seeNewPriceInCart" selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$110.70"/> </test> - <test name="AdminCreateCatalogPriceRuleToFixedTest"> + <test name="AdminCreateCatalogPriceRuleToFixedTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> <annotations> <features value="CatalogRule"/> <stories value="Create catalog price rule"/> @@ -194,51 +122,15 @@ <group value="CatalogRule"/> </annotations> <before> - <!-- Create the simple product and category that it will be in --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- log in and create the price rule --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> <argument name="catalogRule" value="CatalogRuleToFixed"/> </actionGroup> - <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> - <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> - <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> </before> <after> - <!-- delete the simple product and catalog price rule and logout --> - <amOnPage stepKey="goToPriceRulePage" url="admin/catalog_rule/promo_catalog/"/> <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> <argument name="name" value="{{CatalogRuleToFixed.name}}"/> <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> </actionGroup> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> </after> - - <!-- Go to category page and make sure that all of the prices are correct --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryPage"/> - <waitForPageLoad stepKey="waitForCategory"/> - <see stepKey="seeOldPrice" selector="{{StorefrontCategoryProductSection.ProductOldPriceByNumber('1')}}" userInput="$$createProduct.price$$"/> - <see stepKey="seeNewPrice" selector="{{StorefrontCategoryProductSection.ProductSpecialPriceByNumber('1')}}" userInput="$100.00"/> - - <!-- Go to the simple product page and check that the prices are correct --> - <amOnPage stepKey="goToProductPage" url="$$createProduct.sku$$.html"/> - <waitForPageLoad stepKey="waitForProductPage"/> - <see stepKey="seeOldPriceTag" selector="{{StorefrontProductInfoMainSection.oldPriceTag}}" userInput="Regular Price"/> - <see stepKey="seeOldPrice2" selector="{{StorefrontProductInfoMainSection.oldPriceAmount}}" userInput="$$createProduct.price$$"/> - <see stepKey="seeNewPrice2" selector="{{StorefrontProductInfoMainSection.updatedPrice}}" userInput="$100.00"/> - - <!-- Add the product to cart and check that the price is correct there --> - <click stepKey="addToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForAddedToCart"/> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCart"/> - <see stepKey="seeNewPriceInCart" selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$100.00"/> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml index fef9e2d851652..63643757ef438 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="TinyMCESection"> <element name="checkIfContentTabOpen" type="button" selector="//span[text()='Content']/parent::strong/parent::*[@data-state-collapsible='closed']"/> <element name="CheckIfTabExpand" type="button" selector="//div[@data-state-collapsible='closed']//span[text()='Content']"/> @@ -74,11 +74,12 @@ </section> <section name="WidgetSection"> <element name="InsertWidgetTitle" type="text" selector="//h1[contains(text(),'Insert Widget')]"/> + <element name="DisplayType" type="select" selector="select[name='parameters[display_type]']"/> <element name="SelectCategoryTitle" type="text" selector="//h1[contains(text(),'Select Category')]"/> <element name="SelectProductTitle" type="text" selector="//h1[contains(text(),'Select Product')]"/> <element name="SelectPageTitle" type="text" selector="//h1[contains(text(),'Select Page')]"/> <element name="SelectBlockTitle" type="text" selector="//h1[contains(text(),'Select Block')]"/> - <element name="InsertWidget" type="button" selector="#insert_button"/> + <element name="InsertWidget" type="button" selector="#insert_button" timeout="30"/> <element name="InsertWidgetBtnDisabled" type="button" selector="//button[@id='insert_button' and contains(@class,'disabled')]"/> <element name="InsertWidgetBtnEnabled" type="button" selector="//button[@id='insert_button' and not(contains(@class,'disabled'))]"/> <element name="CancelBtnEnabled" type="button" selector="//button[@id='reset' and not(contains(@class,'disabled'))]"/> @@ -106,6 +107,5 @@ <element name="CompareBtn" type="button" selector=".action.tocompare"/> <element name="ClearCompare" type="button" selector="#compare-clear-all"/> <element name="AcceptClear" type="button" selector=".action-primary.action-accept" /> - </section> </sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml index 63ef6cb99f8c1..246e5be75889c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml @@ -103,4 +103,24 @@ <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> <seeInTitle userInput="{{product.name}}" stepKey="seeProductNameInTitle"/> </actionGroup> + + <actionGroup name="createConfigurationsForAttribute"> + <arguments> + <argument name="attributeCode" type="string" defaultValue="SomeString"/> + </arguments> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickCreateConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickFilters"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" userInput="{{attributeCode}}" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="99" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml new file mode 100644 index 0000000000000..280d5c3cdb02f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductSetEditContentTest" extends="AdminSimpleProductSetEditContentTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create/edit configurable product"/> + <title value="Admin should be able to set/edit product Content when editing a configurable product"/> + <description value="Admin should be able to set/edit product Content when editing a configurable product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3424"/> + <group value="ConfigurableProduct"/> + </annotations> + <after> + <!-- Delete configurable product --> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + </after> + + <!-- Create product --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + + <!--Checking content storefront--> + <amOnPage url="{{BaseConfigurableProduct.name}}.html" stepKey="goToStorefront"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml new file mode 100644 index 0000000000000..f8ac5bbd4781b --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminConfigurableSetEditRelatedProductsTest" extends="AdminSimpleSetEditRelatedProductsTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create/Edit configurable product"/> + <title value="Admin should be able to set/edit Related Products information when editing a configurable product"/> + <description value="Admin should be able to set/edit Related Products information when editing a configurable product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3414"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + </before> + <after> + <!-- Delete configurable product --> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Create product --> + <remove keyForRemoval="goToCreateProduct"/> + <actionGroup ref="createConfigurableProduct" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <!--See related product in storefront--> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="goToStorefront"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml new file mode 100644 index 0000000000000..8a00fe2413fe4 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewProductsListWidgetConfigurableProductTest" extends="NewProductsListWidgetTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="New products list widget"/> + <title value="Admin should be able to set Configurable Product as new so that it shows up in the Catalog New Products List Widget"/> + <description value="Admin should be able to set Configurable Product as new so that it shows up in the Catalog New Products List Widget"/> + <severity value="MAJOR"/> + <testCaseId value="MC-120"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + </after> + + <!-- A Cms page containing the New Products Widget gets created here via extends --> + + <!-- Modify the Configurable Product that we created in the before block --> + <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="amOnEditPage"/> + <waitForPageLoad stepKey="waitForEditPage"/> + <fillField selector="{{AdminProductFormSection.setProductAsNewFrom}}" userInput="01/1/2000" stepKey="fillProductNewFrom"/> + <fillField selector="{{AdminProductFormSection.setProductAsNewTo}}" userInput="01/1/2099" stepKey="fillProductNewTo"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + + <!-- If PageCache is enabled, Cache clearing happens here, via merge --> + + <!-- Check for product on the CMS page with the New Products widget --> + <amOnPage url="{{_newDefaultCmsPage.identifier}}" stepKey="amOnCmsPage"/> + <waitForPageLoad stepKey="waitForCmsPage"/> + <see selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="$$createConfigProduct.name$$" stepKey="seeProductName"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml new file mode 100644 index 0000000000000..8153f16274de7 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminDownloadableProductSetEditContentTest" extends="AdminSimpleProductSetEditContentTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Create/edit downloadable product"/> + <title value="Admin should be able to set/edit product Content when editing a downloadable product"/> + <description value="Admin should be able to set/edit product Content when editing a downloadable product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3426"/> + <group value="Downloadable"/> + </annotations> + <after> + <!-- Delete downloadable product --> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + </after> + + <!-- Create product --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + + <!--Checking content storefront--> + <amOnPage url="{{DownloadableProduct.sku}}.html" stepKey="goToStorefront"/> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml new file mode 100644 index 0000000000000..dadc60a90a27a --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminDownloadableSetEditRelatedProductsTest" extends="AdminSimpleSetEditRelatedProductsTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Create/edit downloadable product"/> + <title value="Admin should be able to set/edit Related Products information when editing a downloadable product"/> + <description value="Admin should be able to set/edit Related Products information when editing a downloadable product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3416"/> + <group value="Downloadable"/> + </annotations> + <before></before> + <after> + <!-- Delete downloadable product --> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + </after> + + <!-- Create product --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + + <!--See related product in storefront--> + <amOnPage url="{{DownloadableProduct.sku}}.html" stepKey="goToStorefront"/> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml new file mode 100644 index 0000000000000..f9a8a3bd135a6 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewProductsListWidgetDownloadableProductTest" extends="NewProductsListWidgetTest"> + <annotations> + <features value="Downloadable"/> + <stories value="New products list widget"/> + <title value="Admin should be able to set Downloadable Product as new so that it shows up in the Catalog New Products List Widget"/> + <description value="Admin should be able to set Downloadable Product as new so that it shows up in the Catalog New Products List Widget"/> + <severity value="MAJOR"/> + <testCaseId value="MC-124"/> + <group value="Downloadable"/> + </annotations> + + <!-- A Cms page containing the New Products Widget gets created here via extends --> + + <!-- Create a Downloadable product to appear in the widget --> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductList"/> + <waitForPageLoad stepKey="waitForProductList"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProduct"/> + <click selector="{{AdminProductGridActionSection.addDownloadableProduct}}" stepKey="clickAddDownloadableProduct"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{_defaultProduct.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{_defaultProduct.sku}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{_defaultProduct.price}}" stepKey="fillProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="100" stepKey="fillProductQuantity"/> + <fillField selector="{{AdminProductFormSection.setProductAsNewFrom}}" userInput="01/1/2000" stepKey="fillProductNewFrom"/> + <fillField selector="{{AdminProductFormSection.setProductAsNewTo}}" userInput="01/1/2099" stepKey="fillProductNewTo"/> + <click selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="openDownloadableSection"/> + <checkOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable"/> + <fillField userInput="This Is A Title" selector="{{AdminProductDownloadableSection.linksTitleInput}}" stepKey="fillDownloadableLinkTitle"/> + <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkLinksPurchasedSeparately"/> + <fillField userInput="This Is Another Title" selector="{{AdminProductDownloadableSection.samplesTitleInput}}" stepKey="fillDownloadableSampleTitle"/> + <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableLinkWithMaxDownloads"> + <argument name="link" value="downloadableLinkWithMaxDownloads"/> + </actionGroup> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + + <!-- If PageCache is enabled, Cache clearing happens here, via merge --> + + <!-- Check for product on the CMS page with the New Products widget --> + + <amOnPage url="{{_newDefaultCmsPage.identifier}}" stepKey="amOnCmsPage"/> + <waitForPageLoad stepKey="waitForCmsPage"/> + <see selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="{{_defaultProduct.name}}" stepKey="seeProductName"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml new file mode 100644 index 0000000000000..0193e88e9597b --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminGroupedProductSetEditContentTest" extends="AdminSimpleProductSetEditContentTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Create/edit grouped product"/> + <title value="Admin should be able to set/edit product Content when editing a grouped product"/> + <description value="Admin should be able to set/edit product Content when editing a grouped product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3423"/> + <group value="GroupedProduct"/> + </annotations> + <after> + <!-- Delete grouped product --> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + </after> + + <!-- Create product --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillProductForm"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + + <!--Checking content storefront--> + <amOnPage url="{{GroupedProduct.sku}}.html" stepKey="goToStorefront"/> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml new file mode 100644 index 0000000000000..2e7e8e161f92c --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminGroupedSetEditRelatedProductsTest" extends="AdminSimpleSetEditRelatedProductsTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Create/edit grouped product"/> + <title value="Admin should be able to set/edit Related Products information when editing a grouped product"/> + <description value="Admin should be able to set/edit Related Products information when editing a grouped product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3755"/> + <group value="GroupedProduct"/> + </annotations> + <before></before> + <after> + <!-- Delete grouped product --> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + </after> + + <!-- Create product --> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + <actionGroup ref="fillGroupedProductForm" stepKey="fillProductForm"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + + <!--See related product in storefront--> + <amOnPage url="{{GroupedProduct.sku}}.html" stepKey="goToStorefront"/> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/NewProductsListWidgetGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/NewProductsListWidgetGroupedProductTest.xml new file mode 100644 index 0000000000000..96d64aa798265 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/NewProductsListWidgetGroupedProductTest.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewProductsListWidgetGroupedProductTest" extends="NewProductsListWidgetTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="New products list widget"/> + <title value="Admin should be able to set Grouped Product as new so that it shows up in the Catalog New Products List Widget"/> + <description value="Admin should be able to set Grouped Product as new so that it shows up in the Catalog New Products List Widget"/> + <severity value="MAJOR"/> + <testCaseId value="MC-121"/> + <group value="GroupedProduct"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProductOne"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProductTwo"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- A Cms page containing the New Products Widget gets created here via extends --> + + <!-- Create a grouped product to appear in the widget --> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductList"/> + <waitForPageLoad stepKey="waitForProductList"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="toggleAddProductButton"/> + <click selector="{{AdminProductGridActionSection.addGroupedProduct}}" stepKey="clickAddGroupedProduct"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{_defaultProduct.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{_defaultProduct.sku}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.setProductAsNewFrom}}" userInput="01/1/2000" stepKey="fillProductNewFrom"/> + <fillField selector="{{AdminProductFormSection.setProductAsNewTo}}" userInput="01/1/2099" stepKey="fillProductNewTo"/> + <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductsSection"/> + <scrollTo selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="scrollToAddProductsToGroup"/> + <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup"/> + <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForGroupedProductModal"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterGroupedProducts"> + <argument name="sku" value="api-simple-product"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToGroupPanel.nThCheckbox('0')}}" stepKey="checkFilterResult1"/> + <checkOption selector="{{AdminAddProductsToGroupPanel.nThCheckbox('1')}}" stepKey="checkFilterResult2"/> + <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="clickAddSelectedGroupProducts"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + + <!-- If PageCache is enabled, Cache clearing happens here, via merge --> + + <!-- Check for product on the CMS page with the New Products widget --> + + <amOnPage url="{{_newDefaultCmsPage.identifier}}" stepKey="amOnCmsPage"/> + <waitForPageLoad stepKey="waitForCmsPage"/> + <see selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="{{_defaultProduct.name}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearCacheActionGroup.xml b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearCacheActionGroup.xml index cf4bd4ff43c8e..510dfc3554ce5 100644 --- a/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearCacheActionGroup.xml +++ b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearCacheActionGroup.xml @@ -7,11 +7,19 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> -<actionGroup name="ClearCacheActionGroup"> - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/cache/" stepKey="goToNewCustomVarialePage" /> - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{AdminCacheManagementSection.FlushMagentoCache}}" stepKey="clickFlushMagentoCache" /> - <waitForPageLoad stepKey="waitForCacheFlush"/> -</actionGroup> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ClearCacheActionGroup"> + <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/cache/" stepKey="goToNewCustomVarialePage" /> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{AdminCacheManagementSection.FlushMagentoCache}}" stepKey="clickFlushMagentoCache" /> + <waitForPageLoad stepKey="waitForCacheFlush"/> + </actionGroup> + <actionGroup name="clearPageCache"> + <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/cache/" stepKey="amOnCacheManagementPage"/> + <waitForPageLoad stepKey="waitForCacheManagement"/> + <selectOption selector="{{AdminCacheManagementSection.massActionSelect}}" userInput="refresh" stepKey="selectRefresh"/> + <click selector="{{AdminCacheManagementSection.pageCacheCheckbox}}" stepKey="selectPageCache"/> + <click selector="{{AdminCacheManagementSection.massActionSubmit}}" stepKey="submitCacheForm"/> + <waitForPageLoad stepKey="waitForCacheFlush"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/PageCache/Test/Mftf/Page/AdminCacheManagementPage.xml b/app/code/Magento/PageCache/Test/Mftf/Page/AdminCacheManagementPage.xml new file mode 100644 index 0000000000000..24fc5ffb04cb7 --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Page/AdminCacheManagementPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminCacheManagementPage" url="/admin/cache" area="admin" module="PageCache"> + <section name="AdminCacheManagementSection"/> + </page> +</pages> diff --git a/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml b/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml index c91f021b5719b..34a77095d524d 100644 --- a/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml +++ b/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml @@ -7,12 +7,28 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCacheManagementSection"> <element name="FlushMagentoCache" type="button" selector="#flush_magento"/> <element name="actionDropDown" type="multiselect" selector="//*[@id='cache_grid_massaction-select']//option[contains(., 'Action')]" timeout="30"/> <element name="refreshOption" type="multiselect" selector="//*[@id='cache_grid_massaction-select']//option[@value='refresh']" timeout="30"/> <element name="pageCacheRowCheckbox" type="checkbox" selector="//td[contains(., 'Page Cache')]/..//input[@type='checkbox']"/> <element name="submit" type="button" selector="//button[@title='Submit']" timeout="30"/> + <element name="massActionSelect" type="select" selector="#cache_grid_massaction-form #cache_grid_massaction-select"/> + <element name="massActionSubmit" type="button" selector="#cache_grid_massaction-form button"/> + <element name="configurationCheckbox" type="checkbox" selector="input[value='config']"/> + <element name="layoutsCheckbox" type="checkbox" selector="input[value='layout']"/> + <element name="blocksHtmlCheckbox" type="checkbox" selector="input[value='block_html']"/> + <element name="collectionsDataCheckbox" type="checkbox" selector="input[value='collections']"/> + <element name="reflectionDataCheckbox" type="checkbox" selector="input[value='reflection']"/> + <element name="databaseDdlCheckbox" type="checkbox" selector="input[value='db_ddl']"/> + <element name="compiledConfigCheckbox" type="checkbox" selector="input[value='compiled_config']"/> + <element name="eavTypesAndAttrsCheckbox" type="checkbox" selector="input[value='eav']"/> + <element name="customerNotificationCheckbox" type="checkbox" selector="input[value='customer_notification']"/> + <element name="integrationsConfigCheckbox" type="checkbox" selector="input[value='config_integration']"/> + <element name="integrationsApiCheckbox" type="checkbox" selector="input[value='config_integration_api']"/> + <element name="pageCacheCheckbox" type="checkbox" selector="input[value='full_page']"/> + <element name="webServicesConfigCheckbox" type="checkbox" selector="input[value='config_webservice']"/> + <element name="translationsCheckbox" type="checkbox" selector="input[value='translate']"/> </section> </sections> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml new file mode 100644 index 0000000000000..c5871ddc3a373 --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewProductsListWidgetSimpleProductTest"> + <actionGroup ref="clearPageCache" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> + <test name="NewProductsListWidgetConfigurableProductTest"> + <actionGroup ref="clearPageCache" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> + <test name="NewProductsListWidgetGroupedProductTest"> + <actionGroup ref="clearPageCache" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> + <test name="NewProductsListWidgetVirtualProductTest"> + <actionGroup ref="clearPageCache" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> + <test name="NewProductsListWidgetBundleProductTest"> + <actionGroup ref="clearPageCache" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> + <test name="NewProductsListWidgetDownloadableProductTest"> + <actionGroup ref="clearPageCache" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml index 39db9f92837e3..6e430dd30a512 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml @@ -19,6 +19,7 @@ <!-- Selector for Admin Description input where the index is zero-based --> <element name="swatchAdminDescriptionByIndex" type="input" selector="input[name='optiontext[value][option_{{index}}][0]']" parameterized="true"/> <element name="nthChooseColor" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_row_name.colorpicker_handler" parameterized="true"/> + <element name="nthUploadFile" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_row_name.btn_choose_file_upload" parameterized="true"/> <element name="nthDelete" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) button.delete-option" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontCategorySidebarSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontCategorySidebarSection.xml new file mode 100644 index 0000000000000..750191f19cf13 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontCategorySidebarSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="StorefrontCategorySidebarSection"> + <element name="layeredFilterBlock" type="block" selector="#layered-filter-block"/> + <element name="filterOptionTitle" type="button" selector="//div[@class='filter-options-title'][text() = '{{var}}']" parameterized="true" timeout="30"/> + <element name="attributeNthOption" type="button" selector="div.{{attributeLabel}} a:nth-of-type({{n}}) div" parameterized="true" timeout="30"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml new file mode 100644 index 0000000000000..5ec6c0c7332a6 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminCreateImageSwatchTest"> + <annotations> + <features value="Swatches"/> + <stories value="Create/configure swatches"/> + <title value="Admin can create product attribute with image swatch"/> + <description value="Admin can create product attribute with image swatch"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3077"/> + <group value="Swatches"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Begin creating a new product attribute of type "Image Swatch" --> + <amOnPage url="{{ProductAttributePage.url}}" stepKey="goToNewProductAttributePage"/> + <waitForPageLoad stepKey="waitForNewProductAttributePage"/> + <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillDefaultLabel"/> + + <!-- Select visual swatch --> + <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="swatch_visual" stepKey="selectInputType"/> + + <!-- This hack is because the same <input type="file"> is re-purposed used for all uploads. --> + <executeJS function="HTMLInputElement.prototype.click = function() { if(this.type !== 'file') HTMLElement.prototype.click.call(this); };" stepKey="disableClick"/> + + <!-- Set swatch image #1 --> + <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch1"/> + <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch1"> + <argument name="index" value="0"/> + </actionGroup> + <click selector="{{AdminManageSwatchSection.nthUploadFile('1')}}" stepKey="clickUploadFile1"/> + <attachFile selector="input[name='datafile']" userInput="adobe-thumb.jpg" stepKey="attachFile1"/> + <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('0')}}" userInput="adobe-thumb" stepKey="fillAdmin1"/> + + <!-- Set swatch image #2 --> + <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch2"/> + <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch2"> + <argument name="index" value="1"/> + </actionGroup> + <click selector="{{AdminManageSwatchSection.nthUploadFile('2')}}" stepKey="clickUploadFile2"/> + <attachFile selector="input[name='datafile']" userInput="adobe-small.jpg" stepKey="attachFile2"/> + <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('1')}}" userInput="adobe-small" stepKey="fillAdmin2"/> + + <!-- Set swatch image #3 --> + <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch3"/> + <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch3"> + <argument name="index" value="2"/> + </actionGroup> + <click selector="{{AdminManageSwatchSection.nthUploadFile('3')}}" stepKey="clickUploadFile3"/> + <attachFile selector="input[name='datafile']" userInput="adobe-base.jpg" stepKey="attachFile3"/> + <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('2')}}" userInput="adobe-base" stepKey="fillAdmin3"/> + + <!-- Set scope --> + <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> + <selectOption selector="{{AttributePropertiesSection.Scope}}" userInput="1" stepKey="selectGlobalScope"/> + + <!-- Save the new product attribute --> + <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit1"/> + <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccess"/> + + <!-- Verify after round trip to the server --> + <grabAttributeFrom selector="{{AdminManageSwatchSection.nthSwatch('1')}}" userInput="style" stepKey="grabSwatch1"/> + <assertContains stepKey="assertSwatch1"> + <expectedResult type="string">adobe-thumb</expectedResult> + <actualResult type="string">{$grabSwatch1}</actualResult> + </assertContains> + <grabAttributeFrom selector="{{AdminManageSwatchSection.nthSwatch('2')}}" userInput="style" stepKey="grabSwatch2"/> + <assertContains stepKey="assertSwatch2"> + <expectedResult type="string">adobe-small</expectedResult> + <actualResult type="string">{$grabSwatch2}</actualResult> + </assertContains> + <grabAttributeFrom selector="{{AdminManageSwatchSection.nthSwatch('3')}}" userInput="style" stepKey="grabSwatch3"/> + <assertContains stepKey="assertSwatch3"> + <expectedResult type="string">adobe-base</expectedResult> + <actualResult type="string">{$grabSwatch3}</actualResult> + </assertContains> + + <!-- Create a configurable product to verify the storefront with --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad time="30" stepKey="waitForProductGrid"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + + <!-- Create configurations based off the Image Swatch we created earlier --> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickCreateConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickFilters"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="1" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> + + <!-- Go to the product page and see text swatch options --> + <amOnPage url="{{BaseConfigurableProduct.sku}}.html" stepKey="amOnProductPage"/> + <waitForPageLoad stepKey="waitForProductPage"/> + + <!-- Verify the storefront --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOption('1')}}" userInput="style" stepKey="grabSwatch4"/> + <assertContains stepKey="assertSwatch4"> + <expectedResult type="string">adobe-thumb</expectedResult> + <actualResult type="string">{$grabSwatch4}</actualResult> + </assertContains> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOption('2')}}" userInput="style" stepKey="grabSwatch5"/> + <assertContains stepKey="assertSwatch5"> + <expectedResult type="string">adobe-small</expectedResult> + <actualResult type="string">{$grabSwatch5}</actualResult> + </assertContains> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOption('3')}}" userInput="style" stepKey="grabSwatch6"/> + <assertContains stepKey="assertSwatch6"> + <expectedResult type="string">adobe-base</expectedResult> + <actualResult type="string">{$grabSwatch6}</actualResult> + </assertContains> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml new file mode 100644 index 0000000000000..a7e975fe41975 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontFilterByImageSwatchTest"> + <annotations> + <features value="Swatches"/> + <stories value="View swatches in product listing"/> + <title value="Customers can filter products using image swatches"/> + <description value="Customers can filter products using image swatches"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3461"/> + <group value="Swatches"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Begin creating a new product attribute --> + <amOnPage url="{{ProductAttributePage.url}}" stepKey="goToNewProductAttributePage"/> + <waitForPageLoad stepKey="waitForNewProductAttributePage"/> + <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillDefaultLabel"/> + + <!-- Select visual swatch --> + <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="swatch_visual" stepKey="selectInputType"/> + + <!-- This hack is because the same <input type="file"> is re-purposed used for all uploads. --> + <executeJS function="HTMLInputElement.prototype.click = function() { if(this.type !== 'file') HTMLElement.prototype.click.call(this); };" stepKey="disableClick"/> + + <!-- Set swatch #1 image using file upload --> + <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch1"/> + <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch1"> + <argument name="index" value="0"/> + </actionGroup> + <click selector="{{AdminManageSwatchSection.nthUploadFile('1')}}" stepKey="clickUploadFile1"/> + <attachFile selector="input[name='datafile']" userInput="adobe-thumb.jpg" stepKey="attachFile1"/> + <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('0')}}" userInput="adobe-thumb" stepKey="fillAdmin1"/> + + <!-- Set swatch #2 image using the file upload --> + <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch2"/> + <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch2"> + <argument name="index" value="1"/> + </actionGroup> + <click selector="{{AdminManageSwatchSection.nthUploadFile('2')}}" stepKey="clickUploadFile2"/> + <attachFile selector="input[name='datafile']" userInput="adobe-small.jpg" stepKey="attachFile2"/> + <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('1')}}" userInput="adobe-small" stepKey="fillAdmin2"/> + + <!-- Set scope to global --> + <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> + <selectOption selector="{{AttributePropertiesSection.Scope}}" userInput="1" stepKey="selectGlobalScope"/> + + <!-- Set Use In Layered Navigation --> + <scrollToTopOfPage stepKey="scrollToTop1"/> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="goToStorefrontProperties"/> + <selectOption selector="{{AttributePropertiesSection.useInLayeredNavigation}}" userInput="1" stepKey="selectUseInLayeredNavigation"/> + + <!-- Save the new attribute --> + <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit1"/> + <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccess"/> + + <!-- Create a configurable product to verify the storefront with --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad time="30" stepKey="waitForProductGrid"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> + + <!-- Create configurations based off the visual swatch we created earlier --> + <actionGroup ref="createConfigurationsForAttribute" stepKey="createConfigurations"> + <argument name="attributeCode" value="{{ProductAttributeFrontendLabel.label}}"/> + </actionGroup> + + <!-- Go to the category page --> + <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPage"/> + + <!-- Verify swatches are present in the layered navigation --> + <see selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="seeAttributeInLayeredNav"/> + <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle(ProductAttributeFrontendLabel.label)}}" stepKey="expandAttribute"/> + <grabAttributeFrom selector="{{StorefrontCategorySidebarSection.attributeNthOption(ProductAttributeFrontendLabel.label, '1')}}" userInput="style" stepKey="grabSwatch1"/> + <grabAttributeFrom selector="{{StorefrontCategorySidebarSection.attributeNthOption(ProductAttributeFrontendLabel.label, '2')}}" userInput="style" stepKey="grabSwatch2"/> + <assertContains stepKey="assertSwatch1"> + <expectedResult type="string">adobe-thumb</expectedResult> + <actualResult type="string">{$grabSwatch1}</actualResult> + </assertContains> + <assertContains stepKey="assertSwatch2"> + <expectedResult type="string">adobe-small</expectedResult> + <actualResult type="string">{$grabSwatch2}</actualResult> + </assertContains> + + <!-- Click a swatch and expect to see the configurable product, not see the simple product --> + <click selector="{{StorefrontCategorySidebarSection.attributeNthOption(ProductAttributeFrontendLabel.label, '1')}}" stepKey="filterBySwatch1"/> + <see selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="{{BaseConfigurableProduct.name}}" stepKey="seeConfigurableProduct"/> + <dontSee selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="$$createSimpleProduct.name$$" stepKey="dontSeeSimpleProduct"/> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml new file mode 100644 index 0000000000000..28df5ffd53436 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontFilterByTextSwatchTest"> + <annotations> + <features value="Swatches"/> + <stories value="View swatches in product listing"/> + <title value="Customers can filter products using text swatches"/> + <description value="Customers can filter products using text swatches"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3462"/> + <group value="Swatches"/> + </annotations> + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Begin creating a new product attribute --> + <amOnPage url="{{ProductAttributePage.url}}" stepKey="goToNewProductAttributePage"/> + <waitForPageLoad stepKey="waitForNewProductAttributePage"/> + <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillDefaultLabel"/> + + <!-- Select text swatch --> + <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="swatch_text" stepKey="selectInputType"/> + + <!-- Create swatch #1 --> + <click selector="{{AdminManageSwatchSection.addSwatchText}}" stepKey="clickAddSwatch0"/> + <fillField selector="{{AdminManageSwatchSection.swatchTextByIndex('0')}}" userInput="red" stepKey="fillSwatch0"/> + <fillField selector="{{AdminManageSwatchSection.swatchAdminDescriptionByIndex('0')}}" userInput="Something red." stepKey="fillDescription0"/> + + <!-- Create swatch #2 --> + <click selector="{{AdminManageSwatchSection.addSwatchText}}" stepKey="clickAddSwatch1"/> + <fillField selector="{{AdminManageSwatchSection.swatchTextByIndex('1')}}" userInput="blue" stepKey="fillSwatch1"/> + <fillField selector="{{AdminManageSwatchSection.swatchAdminDescriptionByIndex('1')}}" userInput="Something blue." stepKey="fillDescription1"/> + + <!-- Set scope to global --> + <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> + <selectOption selector="{{AttributePropertiesSection.Scope}}" userInput="1" stepKey="selectGlobalScope"/> + + <!-- Set Use In Layered Navigation --> + <scrollToTopOfPage stepKey="scrollToTop1"/> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="goToStorefrontProperties"/> + <selectOption selector="{{AttributePropertiesSection.useInLayeredNavigation}}" userInput="1" stepKey="selectUseInLayeredNavigation"/> + + <!-- Workaround: click on the main tab again to ensure the values are saved, otherwise the swatches do not get saved --> + <scrollToTopOfPage stepKey="scrollToTop2"/> + <click selector="{{AttributePropertiesSection.propertiesTab}}" stepKey="goBackToPropertiesTab"/> + + <!-- Save the new attribute --> + <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit1"/> + <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccess"/> + + <!-- Create a configurable product to verify the storefront with --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad stepKey="waitForProductGrid"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> + + <!-- Create configurations based off the text swatch we created earlier --> + <actionGroup ref="createConfigurationsForAttribute" stepKey="createConfigurations"> + <argument name="attributeCode" value="{{ProductAttributeFrontendLabel.label}}"/> + </actionGroup> + + <!-- Go to the category page --> + <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPage"/> + + <!-- Verify swatches are present in the layered navigation --> + <see selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="seeAttributeInLayeredNav"/> + <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle(ProductAttributeFrontendLabel.label)}}" stepKey="expandAttribute"/> + <see selector="{{StorefrontCategorySidebarSection.attributeNthOption(ProductAttributeFrontendLabel.label, '1')}}" userInput="red" stepKey="seeRed"/> + <see selector="{{StorefrontCategorySidebarSection.attributeNthOption(ProductAttributeFrontendLabel.label, '2')}}" userInput="blue" stepKey="seeBlue"/> + + <!-- Click a swatch and expect to see the configurable product, not see the simple product --> + <click selector="{{StorefrontCategorySidebarSection.attributeNthOption(ProductAttributeFrontendLabel.label, '1')}}" stepKey="filterBySwatch1"/> + <see selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="{{BaseConfigurableProduct.name}}" stepKey="seeConfigurableProduct"/> + <dontSee selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="$$createSimpleProduct.name$$" stepKey="dontSeeSimpleProduct"/> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml new file mode 100644 index 0000000000000..a59b4b1208668 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontFilterByVisualSwatchTest"> + <annotations> + <features value="Swatches"/> + <stories value="View swatches in product listing"/> + <title value="Customers can filter products using visual swatches"/> + <description value="Customers can filter products using visual swatches "/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3082"/> + <group value="Swatches"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Begin creating a new product attribute --> + <amOnPage url="{{ProductAttributePage.url}}" stepKey="goToNewProductAttributePage"/> + <waitForPageLoad stepKey="waitForNewProductAttributePage"/> + <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillDefaultLabel"/> + + <!-- Select visual swatch --> + <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="swatch_visual" stepKey="selectInputType"/> + + <!-- Set swatch #1 using the color picker --> + <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch1"/> + <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch1"> + <argument name="index" value="0"/> + </actionGroup> + <click selector="{{AdminManageSwatchSection.nthChooseColor('1')}}" stepKey="clickChooseColor1"/> + <actionGroup ref="setColorPickerByHex" stepKey="fillHex1"> + <argument name="nthColorPicker" value="1"/> + <argument name="hexColor" value="e74c3c"/> + </actionGroup> + <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('0')}}" userInput="red" stepKey="fillAdmin1"/> + + <!-- Set swatch #2 using the color picker --> + <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch2"/> + <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch2"> + <argument name="index" value="1"/> + </actionGroup> + <click selector="{{AdminManageSwatchSection.nthChooseColor('2')}}" stepKey="clickChooseColor2"/> + <actionGroup ref="setColorPickerByHex" stepKey="fillHex2"> + <argument name="nthColorPicker" value="2"/> + <argument name="hexColor" value="3498db"/> + </actionGroup> + <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('1')}}" userInput="blue" stepKey="fillAdmin2"/> + + <!-- Set scope to global --> + <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> + <selectOption selector="{{AttributePropertiesSection.Scope}}" userInput="1" stepKey="selectGlobalScope"/> + + <!-- Set Use In Layered Navigation --> + <scrollToTopOfPage stepKey="scrollToTop1"/> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="goToStorefrontProperties"/> + <selectOption selector="{{AttributePropertiesSection.useInLayeredNavigation}}" userInput="1" stepKey="selectUseInLayeredNavigation"/> + + <!-- Save the new attribute --> + <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit1"/> + <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccess"/> + + <!-- Create a configurable product to verify the storefront with --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad time="30" stepKey="waitForProductGrid"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> + + <!-- Create configurations based off the visual watch we created earlier --> + <actionGroup ref="createConfigurationsForAttribute" stepKey="createConfigurations"> + <argument name="attributeCode" value="{{ProductAttributeFrontendLabel.label}}"/> + </actionGroup> + + <!-- Go to the category page --> + <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPage"/> + + <!-- Verify swatches are present in the layered navigation --> + <see selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="seeAttributeInLayeredNav"/> + <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle(ProductAttributeFrontendLabel.label)}}" stepKey="expandAttribute"/> + <grabAttributeFrom selector="{{StorefrontCategorySidebarSection.attributeNthOption(ProductAttributeFrontendLabel.label, '1')}}" userInput="style" stepKey="grabSwatch1"/> + <grabAttributeFrom selector="{{StorefrontCategorySidebarSection.attributeNthOption(ProductAttributeFrontendLabel.label, '2')}}" userInput="style" stepKey="grabSwatch2"/> + <assertContains stepKey="assertSwatch1"> + <expectedResult type="string">rgb(231, 77, 60)</expectedResult> + <actualResult type="string">{$grabSwatch1}</actualResult> + </assertContains> + <assertContains stepKey="assertSwatch2"> + <expectedResult type="string">rgb(52, 152, 219)</expectedResult> + <actualResult type="string">{$grabSwatch2}</actualResult> + </assertContains> + + <!-- Click a swatch and expect to see the configurable product, not see the simple product --> + <click selector="{{StorefrontCategorySidebarSection.attributeNthOption(ProductAttributeFrontendLabel.label, '1')}}" stepKey="filterBySwatch1"/> + <see selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="{{BaseConfigurableProduct.name}}" stepKey="seeConfigurableProduct"/> + <dontSee selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="$$createSimpleProduct.name$$" stepKey="dontSeeSimpleProduct"/> + </test> +</tests> diff --git a/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml new file mode 100644 index 0000000000000..6a8de1ca5f0a0 --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + + <!-- This test exists to serve as a base for extension for other tests --> + <test name="NewProductsListWidgetTest"> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + </before> + + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="logout"/> + </after> + + <!-- Create a CMS page containing the New Products widget --> + + <amOnPage url="{{CmsPagesPage.url}}" stepKey="amOnCmsList"/> + <waitForPageLoad stepKey="waitForCmsList"/> + <click selector="{{CmsPagesPageActionsSection.addNewPageButton}}" stepKey="clickAddNewPageButton"/> + <fillField selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{_newDefaultCmsPage.title}}" stepKey="fillPageTitle"/> + <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="expandContentSection"/> + <waitForPageLoad stepKey="waitForContentSection"/> + <click selector="{{CmsWYSIWYGSection.InsertWidgetBtn}}" stepKey="clickInsertWidgetButton"/> + <waitForPageLoad stepKey="waitForSlideOut"/> + <selectOption selector="{{WidgetSection.WidgetType}}" userInput="Catalog New Products List" stepKey="selectWidgetType"/> + <waitForPageLoad stepKey="waitForWidgetOptions"/> + <selectOption selector="{{WidgetSection.DisplayType}}" userInput="New products" stepKey="selectDisplayType"/> + <fillField selector="{{WidgetSection.NoOfProductToDisplay}}" userInput="100" stepKey="fillNoOfProductToDisplay"/> + <click selector="{{WidgetSection.InsertWidget}}" stepKey="clickInsertWidget"/> + <click selector="{{CmsNewPagePageSeoSection.header}}" stepKey="expandSeoSection"/> + <fillField selector="{{CmsNewPagePageSeoSection.urlKey}}" userInput="{{_newDefaultCmsPage.identifier}}" stepKey="fillPageUrlKey"/> + <click selector="{{CmsNewPagePageActionsSection.saveAndContinueEdit}}" stepKey="clickSaveCmsPage"/> + </test> +</tests> \ No newline at end of file diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php index 8fa22122cce89..e22f27b91ccc5 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php @@ -59,6 +59,14 @@ public function execute($command, $options = []) private function prepareUrl($command, $options = []) { $command .= ' ' . implode(' ', $options); - return $_ENV['app_frontend_url'] . self::URL . '?command=' . urlencode($command); + // replacing index.php if it presents + $count = 1; + $trimmedAppFrontendUrl = str_replace( + 'index.php', + '', + rtrim($_ENV['app_frontend_url'], '/'), + $count + ); + return $trimmedAppFrontendUrl . self::URL . '?command=' . urlencode($command); } } From e2b7b39cdef0dc556f574ca3d03e4547ad7a36a2 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Mon, 6 Aug 2018 14:35:38 -0500 Subject: [PATCH 0754/1171] MAGETWO-90974: HTML showing in minicart with custom option file upload Fix span tag to display html link instead of text --- .../view/frontend/web/template/minicart/item/default.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html b/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html index 41d442a76d510..357b0e550af0f 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html @@ -45,7 +45,7 @@ <span data-bind="html: option.value.join('<br>')"></span> <!-- /ko --> <!-- ko ifnot: Array.isArray(option.value) --> - <span data-bind="text: option.value"></span> + <span data-bind="html: option.value"></span> <!-- /ko --> </dd> <!-- /ko --> From d964200aaffdc3e52678b1d2613b2d8d5ef7f434 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Mon, 6 Aug 2018 16:33:27 -0500 Subject: [PATCH 0755/1171] MAGETWO-90862: PayPal Credit Learn More link is broken --- .../etc/adminhtml/system/express_checkout.xml | 8 ++--- .../etc/adminhtml/system/payflow_advanced.xml | 2 +- .../etc/adminhtml/system/payflow_link.xml | 6 ++-- .../system/payments_pro_hosted_solution.xml | 6 ++-- ...aypal_payflowpro_with_express_checkout.xml | 6 ++-- app/code/Magento/Paypal/i18n/en_US.csv | 36 +++++++++---------- .../Reader/_files/expected/config.xml | 28 +++++++-------- 7 files changed, 46 insertions(+), 46 deletions(-) diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml b/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml index a726439331cb0..bff076aad9cb5 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml @@ -162,7 +162,7 @@ <label>Enable PayPal Credit</label> <comment><![CDATA[PayPal Express Checkout lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> + <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> </comment> <config_path>payment/paypal_express_bml/active</config_path> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -184,12 +184,12 @@ <group id="advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="30"> <label>Advertise PayPal Credit</label> <comment> - <![CDATA[<a href="https:/financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> + <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https:/financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> + or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> </comment> <field id="bml_publisher_id" translate="label comment tooltip" showInDefault="1" showInWebsite="1" sortOrder="10"> <label>Publisher ID</label> @@ -199,7 +199,7 @@ </field> <field id="bml_wizard" translate="button_label" sortOrder="15" showInDefault="1" showInWebsite="1"> <button_label>Get Publisher ID from PayPal</button_label> - <button_url><![CDATA[https:/financing.paypal.com/ppfinportal/cart/index?dcp=4eff8563b9cc505e0b9afaff3256705081553c79]]></button_url> + <button_url><![CDATA[https://financing.paypal.com/ppfinportal/cart/index?dcp=4eff8563b9cc505e0b9afaff3256705081553c79]]></button_url> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\BmlApiWizard</frontend_model> </field> <group id="settings_bml_homepage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/payflow_advanced.xml b/app/code/Magento/Paypal/etc/adminhtml/system/payflow_advanced.xml index 3012ff4361483..5eb596c9c4f45 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system/payflow_advanced.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system/payflow_advanced.xml @@ -100,7 +100,7 @@ <field id="enable_express_checkout_bml" sortOrder="42" extends="payment_all_paypal/express_checkout/express_checkout_required/enable_express_checkout_bml"> <comment><![CDATA[PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> + <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> </comment> <config_path>payment/payflow_express_bml/active</config_path> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Bml</frontend_model> diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/payflow_link.xml b/app/code/Magento/Paypal/etc/adminhtml/system/payflow_link.xml index 60a4d670e8a18..d27dde02c579e 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system/payflow_link.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system/payflow_link.xml @@ -109,7 +109,7 @@ <field id="enable_express_checkout_bml" extends="payment_all_paypal/express_checkout/express_checkout_required/enable_express_checkout_bml" sortOrder="41"> <comment><![CDATA[Payflow Link lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> + <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> </comment> <config_path>payment/payflow_express_bml/active</config_path> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Bml</frontend_model> @@ -130,12 +130,12 @@ <group id="payflow_link_advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="60"> <label>Advertise PayPal Credit</label> <comment> - <![CDATA[<a href="https:/financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> + <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https:/financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> + or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> </comment> <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/payments_pro_hosted_solution.xml b/app/code/Magento/Paypal/etc/adminhtml/system/payments_pro_hosted_solution.xml index 80ba1c3ac03a2..77acff48c247e 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system/payments_pro_hosted_solution.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system/payments_pro_hosted_solution.xml @@ -49,7 +49,7 @@ <field id="enable_express_checkout_bml" extends="payment_all_paypal/express_checkout/express_checkout_required/enable_express_checkout_bml" sortOrder="21"> <comment><![CDATA[Payments Pro Hosted Solution lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> + <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> </comment> <requires> <field id="pphs_enable"/> @@ -58,12 +58,12 @@ <group id="pphs_advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="22"> <label>Advertise PayPal Credit</label> <comment> - <![CDATA[<a href="https:/financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> + <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https:/financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> + or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> </comment> <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml index 90ffeddbb812e..6090025024dd7 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml @@ -34,7 +34,7 @@ <label>Enable PayPal Credit</label> <comment><![CDATA[PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> + <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> </comment> <config_path>payment/payflow_express_bml/active</config_path> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -53,12 +53,12 @@ <group id="paypal_payflow_advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="40"> <label>Advertise PayPal Credit</label> <comment> - <![CDATA[<a href="https:/financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> + <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https:/financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> + or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> </comment> <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> diff --git a/app/code/Magento/Paypal/i18n/en_US.csv b/app/code/Magento/Paypal/i18n/en_US.csv index 797edbf8bfa1e..e47264878f16e 100644 --- a/app/code/Magento/Paypal/i18n/en_US.csv +++ b/app/code/Magento/Paypal/i18n/en_US.csv @@ -1,17 +1,17 @@ " - <a href=""https:/financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> + <a href=""https://financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’ s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href=""https:/financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. + or more. <a href=""https://financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. "," - <a href=""https:/financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> + <a href=""https://financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href=""https:/financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. + or more. <a href=""https://financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. " payflowpro,"Payflow Pro" "Billing Agreements","Billing Agreements" @@ -484,27 +484,27 @@ Manage,Manage "Enable PayPal Credit","Enable PayPal Credit" "PayPal Express Checkout lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href=""https:/www.paypal.com/webapps/mpp/promotional-financing"" target=""_blank"">Learn More</a> + <a href=""https://www.paypal.com/webapps/mpp/promotional-financing"" target=""_blank"">Learn More</a> ","PayPal Express Checkout lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href=""https:/www.paypal.com/webapps/mpp/promotional-financing"" target=""_blank"">Learn More</a> + <a href=""https://www.paypal.com/webapps/mpp/promotional-financing"" target=""_blank"">Learn More</a> " "Sort Order PayPal Credit","Sort Order PayPal Credit" "Advertise PayPal Credit","Advertise PayPal Credit" " - <a href=""https:/financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> + <a href=""https://financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href=""https:/financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. + or more. <a href=""https://financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. "," - <a href=""https:/financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> + <a href=""https://financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href=""https:/financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. + or more. <a href=""https://financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. " "Publisher ID","Publisher ID" "Required to display a banner","Required to display a banner" @@ -645,19 +645,19 @@ User,User "Accept payments with a PCI compliant checkout that keeps customers on your site. (<u>Includes Express Checkout</u>)","Accept payments with a PCI compliant checkout that keeps customers on your site. (<u>Includes Express Checkout</u>)" "Payments Pro Hosted Solution","Payments Pro Hosted Solution" " - <a href=""https:/financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> + <a href=""https://financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href=""https:/financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. + or more. <a href=""https://financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. "," - <a href=""https:/financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> + <a href=""https://financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href=""https:/financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. + or more. <a href=""https://financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. " "Basic Settings - PayPal Payments Pro Hosted Solution","Basic Settings - PayPal Payments Pro Hosted Solution" "Display Express Checkout in the Payment Information step","Display Express Checkout in the Payment Information step" @@ -683,17 +683,17 @@ User,User "Card Security Code Does Not Match","Card Security Code Does Not Match" "Payflow Pro and Express Checkout","Payflow Pro and Express Checkout" " - <a href=""https:/financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> + <a href=""https://financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href=""https:/financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. + or more. <a href=""https://financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. "," - <a href=""https:/financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> + <a href=""https://financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href=""https:/financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. + or more. <a href=""https://financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. " diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml b/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml index 2552d383bbcc3..2bd346a6e8f7b 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml @@ -350,7 +350,7 @@ <field id="enable_express_checkout_bml" extends="payment_all_paypal/express_checkout/express_checkout_required/enable_express_checkout_bml" sortOrder="41"> <comment><![CDATA[Payflow Link lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> + <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> </comment> <config_path>payment/payflow_express_bml/active</config_path> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Bml</frontend_model> @@ -371,12 +371,12 @@ <group id="payflow_link_advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="60"> <label>Advertise PayPal Credit</label> <comment> - <![CDATA[<a href="https:/financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> + <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https:/financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> + or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> </comment> <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> @@ -731,7 +731,7 @@ <label>Enable PayPal Credit</label> <comment><![CDATA[PayPal Express Checkout lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> + <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> </comment> <config_path>payment/paypal_express_bml/active</config_path> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -753,12 +753,12 @@ <group id="advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="30"> <label>Advertise PayPal Credit</label> <comment> - <![CDATA[<a href="https:/financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> + <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https:/financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> + or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> </comment> <field id="bml_publisher_id" translate="label comment tooltip" showInDefault="1" showInWebsite="1" sortOrder="10"> <label>Publisher ID</label> @@ -768,7 +768,7 @@ </field> <field id="bml_wizard" translate="button_label" sortOrder="15" showInDefault="1" showInWebsite="1"> <button_label>Get Publisher ID from PayPal</button_label> - <button_url><![CDATA[https:/financing.paypal.com/ppfinportal/cart/index?dcp=4eff8563b9cc505e0b9afaff3256705081553c79]]></button_url> + <button_url><![CDATA[https://financing.paypal.com/ppfinportal/cart/index?dcp=4eff8563b9cc505e0b9afaff3256705081553c79]]></button_url> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\BmlApiWizard</frontend_model> </field> <group id="settings_bml_homepage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> @@ -1261,7 +1261,7 @@ <field id="enable_express_checkout_bml" extends="payment_all_paypal/express_checkout/express_checkout_required/enable_express_checkout_bml" sortOrder="21"> <comment><![CDATA[Payments Pro Hosted Solution lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> + <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> </comment> <requires> <field id="pphs_enable"/> @@ -1270,12 +1270,12 @@ <group id="pphs_advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="22"> <label>Advertise PayPal Credit</label> <comment> - <![CDATA[<a href="https:/financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> + <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https:/financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> + or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> </comment> <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> @@ -1536,7 +1536,7 @@ <field id="enable_express_checkout_bml" sortOrder="42" extends="payment_all_paypal/express_checkout/express_checkout_required/enable_express_checkout_bml"> <comment><![CDATA[PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> + <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> </comment> <config_path>payment/payflow_express_bml/active</config_path> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Bml</frontend_model> @@ -1836,7 +1836,7 @@ <label>Enable PayPal Credit</label> <comment><![CDATA[PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> + <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> </comment> <config_path>payment/payflow_express_bml/active</config_path> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -1855,12 +1855,12 @@ <group id="paypal_payflow_advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="40"> <label>Advertise PayPal Credit</label> <comment> - <![CDATA[<a href="https:/financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> + <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https:/financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> + or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> </comment> <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> From 1283596c28366e21cc14e720d84962421045c726 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Mon, 6 Aug 2018 19:00:12 -0500 Subject: [PATCH 0756/1171] MC-3053: Automate MFTF for MC-2294 --- ...UseStaticURLForMediaContentInWYSIWYGActionGroup.xml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml index c2bbbc365dfaf..4782944ea867b 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml @@ -8,13 +8,17 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> - <actionGroup name="useStaticURLForMediaContentInWYSIWYGActionGroup"> + <actionGroup name="UseStaticURLForMediaContentInWYSIWYG"> + <arguments> + <argument name="value" defaultValue="Yes" type="string"/> + </arguments> <amOnPage url="{{ConfigurationStoresPage.url}}" stepKey="navigateToWYSIWYGConfigPage1"/> - <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> <conditionalClick stepKey="expandWYSIWYGOptions" selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.CheckIfTabExpand}}" visible="true" /> <waitForElementVisible selector="{{ContentManagementSection.EnableWYSIWYG}}" stepKey="waitForEnableWYSIWYGDropdown1" /> - <selectOption selector="{{ContentManagementSection.StaticURL}}" userInput="Yes" stepKey="selectOption1"/> + <selectOption selector="{{ContentManagementSection.StaticURL}}" userInput="{{value}}" stepKey="selectOption1"/> <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions" /> <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig" /> + <waitForPageLoad stepKey="waitForPageLoad2" /> </actionGroup> </actionGroups> From cfc77b08986dc1707476e17a762f40f83013f6dc Mon Sep 17 00:00:00 2001 From: Jason Evans <evans022@gmail.com> Date: Mon, 6 Aug 2018 22:15:34 -0500 Subject: [PATCH 0757/1171] Issue 10411 - Change account management to check if store is in website. --- .../Customer/Model/AccountManagement.php | 5 ++ .../Test/Unit/Model/AccountManagementTest.php | 76 ++++++++++++++++++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 63eb1efa64be6..150938223e684 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -794,6 +794,11 @@ public function createAccountWithPasswordHash(CustomerInterface $customer, $hash // Update 'created_in' value with actual store name if ($customer->getId() === null) { + $websiteId = $customer->getWebsiteId(); + if ($websiteId && !$this->isCustomerInStore($websiteId, $customer->getStoreId())) { + throw new LocalizedException(__('The store view is not in the associated website.')); + } + $storeName = $this->storeManager->getStore($customer->getStoreId())->getName(); $customer->setCreatedIn($storeName); } diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php index 9e3a16a307923..bbac58ac837b4 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php @@ -318,7 +318,7 @@ public function testCreateAccountWithPasswordHashWithCustomerWithoutStoreId() ->method('getId') ->willReturn($defaultStoreId); $website = $this->getMockBuilder(\Magento\Store\Model\Website::class)->disableOriginalConstructor()->getMock(); - $website->expects($this->once()) + $website->expects($this->atLeastOnce()) ->method('getStoreIds') ->willReturn([1, 2, 3]); $website->expects($this->once()) @@ -354,7 +354,7 @@ public function testCreateAccountWithPasswordHashWithCustomerWithoutStoreId() ->with($customerEmail) ->willReturn($customer); $this->share - ->expects($this->once()) + ->expects($this->atLeastOnce()) ->method('isWebsiteScope') ->willReturn(true); $this->storeManager @@ -545,6 +545,7 @@ public function testCreateAccountWithPasswordHashWithNewCustomerAndLocalizedExce { $storeId = 1; $storeName = 'store_name'; + $websiteId = 1; $hash = '4nj54lkj5jfi03j49f8bgujfgsd'; $customerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) @@ -556,6 +557,9 @@ public function testCreateAccountWithPasswordHashWithNewCustomerAndLocalizedExce $customerMock->expects($this->atLeastOnce()) ->method('getStoreId') ->willReturn($storeId); + $customerMock->expects($this->atLeastOnce()) + ->method('getWebsiteId') + ->willReturn($websiteId); $customerMock->expects($this->once()) ->method('setCreatedIn') ->with($storeName) @@ -567,6 +571,19 @@ public function testCreateAccountWithPasswordHashWithNewCustomerAndLocalizedExce ->method('setAddresses') ->with(null) ->willReturnSelf(); + $this->share + ->expects($this->once()) + ->method('isWebsiteScope') + ->willReturn(true); + $website = $this->getMockBuilder(\Magento\Store\Model\Website::class)->disableOriginalConstructor()->getMock(); + $website->expects($this->once()) + ->method('getStoreIds') + ->willReturn([1, 2, 3]); + $this->storeManager + ->expects($this->atLeastOnce()) + ->method('getWebsite') + ->with($websiteId) + ->willReturn($website); $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) ->disableOriginalConstructor() @@ -576,7 +593,7 @@ public function testCreateAccountWithPasswordHashWithNewCustomerAndLocalizedExce ->method('getName') ->willReturn($storeName); - $this->storeManager->expects($this->exactly(2)) + $this->storeManager->expects($this->exactly(1)) ->method('getStore') ->with($storeId) ->willReturn($storeMock); @@ -1720,7 +1737,7 @@ public function testCreateAccountWithPasswordHashForGuest() $customerMock->expects($this->exactly(3)) ->method('getStoreId') ->willReturn(null); - $customerMock->expects($this->exactly(2)) + $customerMock->expects($this->exactly(3)) ->method('getWebsiteId') ->willReturn(null); $customerMock->expects($this->once()) @@ -1752,6 +1769,9 @@ public function testCreateAccountWithPasswordHashForGuest() $this->accountManagement->createAccountWithPasswordHash($customerMock, $hash); } + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ public function testCreateAccountWithPasswordHashWithCustomerAddresses() { $websiteId = 1; @@ -1846,6 +1866,19 @@ public function testCreateAccountWithPasswordHashWithCustomerAddresses() ->expects($this->atLeastOnce()) ->method('getStore') ->willReturn($store); + $this->share + ->expects($this->once()) + ->method('isWebsiteScope') + ->willReturn(true); + $website = $this->getMockBuilder(\Magento\Store\Model\Website::class)->disableOriginalConstructor()->getMock(); + $website->expects($this->once()) + ->method('getStoreIds') + ->willReturn([1, 2, 3]); + $this->storeManager + ->expects($this->atLeastOnce()) + ->method('getWebsite') + ->with($websiteId) + ->willReturn($website); $this->assertSame($customer, $this->accountManagement->createAccountWithPasswordHash($customer, $hash)); } @@ -1976,4 +2009,39 @@ public function testCreateAccountUnexpectedValueException(): void $this->accountManagement->createAccount($customer); } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testCreateAccountWithStoreNotInWebsite() + { + $storeId = 1; + $websiteId = 1; + $hash = '4nj54lkj5jfi03j49f8bgujfgsd'; + $customerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) + ->getMockForAbstractClass(); + $customerMock->expects($this->atLeastOnce()) + ->method('getId') + ->willReturn(null); + $customerMock->expects($this->atLeastOnce()) + ->method('getStoreId') + ->willReturn($storeId); + $customerMock->expects($this->atLeastOnce()) + ->method('getWebsiteId') + ->willReturn($websiteId); + $this->share + ->expects($this->once()) + ->method('isWebsiteScope') + ->willReturn(true); + $website = $this->getMockBuilder(\Magento\Store\Model\Website::class)->disableOriginalConstructor()->getMock(); + $website->expects($this->once()) + ->method('getStoreIds') + ->willReturn([2, 3]); + $this->storeManager + ->expects($this->atLeastOnce()) + ->method('getWebsite') + ->with($websiteId) + ->willReturn($website); + $this->accountManagement->createAccountWithPasswordHash($customerMock, $hash); + } } From 456de2703a1f83790b8196322d0b0007e3d1eb7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Hochd=C3=B6rfer?= <S.Hochdoerfer@bitExpert.de> Date: Tue, 7 Aug 2018 08:41:01 +0200 Subject: [PATCH 0758/1171] Remove dead code \Magento\Cms\Model\Page::load() returns $this thus the if condition will never evaluate as false. And since there is an id check afterwards anyways we should be save. --- app/code/Magento/Cms/Helper/Page.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Cms/Helper/Page.php b/app/code/Magento/Cms/Helper/Page.php index bf51c5f0210e9..a421f89e07d6a 100644 --- a/app/code/Magento/Cms/Helper/Page.php +++ b/app/code/Magento/Cms/Helper/Page.php @@ -189,9 +189,7 @@ public function getPageUrl($pageId = null) $page = $this->_pageFactory->create(); if ($pageId !== null && $pageId !== $page->getId()) { $page->setStoreId($this->_storeManager->getStore()->getId()); - if (!$page->load($pageId)) { - return null; - } + $page->load($pageId); } if (!$page->getId()) { From 574e1e7c43ccf74e9dfd23cace75abb1bfc0def8 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Tue, 7 Aug 2018 12:26:39 +0300 Subject: [PATCH 0759/1171] MAGETWO-92953: [2.3] Once Payflow Pro payment is Declined, customer cannot continue checkout - fix static --- app/code/Magento/Paypal/Controller/Transparent/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Paypal/Controller/Transparent/Response.php b/app/code/Magento/Paypal/Controller/Transparent/Response.php index e16d2289df845..67ec9afff2c44 100644 --- a/app/code/Magento/Paypal/Controller/Transparent/Response.php +++ b/app/code/Magento/Paypal/Controller/Transparent/Response.php @@ -21,7 +21,7 @@ use Magento\Framework\App\Request\InvalidRequestException; /** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Response extends \Magento\Framework\App\Action\Action implements CsrfAwareActionInterface { From 6e5bc079850002ac96909465a352ba3eb5140e34 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel.ruf@ueberbit.de> Date: Thu, 2 Aug 2018 14:11:33 +0200 Subject: [PATCH 0760/1171] fix: add missing data-th selector for tables --- .../blank/Magento_Checkout/web/css/source/module/_cart.less | 2 +- app/design/frontend/Magento/blank/web/css/source/_extends.less | 2 +- .../luma/Magento_Checkout/web/css/source/module/_cart.less | 2 +- .../Magento/luma/Magento_Sales/web/css/source/_module.less | 2 +- app/design/frontend/Magento/luma/web/css/source/_extends.less | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less index bb14a3c2521b0..d3d15019f0e87 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less @@ -305,7 +305,7 @@ white-space: nowrap; width: 33%; - &:before { + &[data-th]:before { content: attr(data-th) ':'; display: block; font-weight: @font-weight__bold; diff --git a/app/design/frontend/Magento/blank/web/css/source/_extends.less b/app/design/frontend/Magento/blank/web/css/source/_extends.less index c177f91e9e7e8..6df78859a1a80 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_extends.less +++ b/app/design/frontend/Magento/blank/web/css/source/_extends.less @@ -846,7 +846,7 @@ white-space: nowrap; width: 33%; - &:before { + &[data-th]:before { content: attr(data-th) ':'; display: block; font-weight: @font-weight__bold; diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less index 43b0351f0ff77..9e3a28be4c90e 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less @@ -460,7 +460,7 @@ white-space: nowrap; width: 33%; - &:before { + &[data-th]:before { content: attr(data-th); display: block; font-weight: @font-weight__semibold; diff --git a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less index 692f91ef463b1..a0f734b05cbd1 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less @@ -482,7 +482,7 @@ .options-label + .item-options-container, .item-options-container + .item-options-container { - &:before { + &[data-th]:before { content: attr(data-th) ':'; display: block; font-weight: @font-weight__bold; diff --git a/app/design/frontend/Magento/luma/web/css/source/_extends.less b/app/design/frontend/Magento/luma/web/css/source/_extends.less index 760ec9ed861a9..1292188102868 100644 --- a/app/design/frontend/Magento/luma/web/css/source/_extends.less +++ b/app/design/frontend/Magento/luma/web/css/source/_extends.less @@ -760,7 +760,7 @@ white-space: nowrap; width: 33%; - &:before { + &[data-th]:before { content: attr(data-th) ':'; display: block; font-weight: @font-weight__bold; From 1124fb5a5272baf1572a7acbf80bdf43218b53fb Mon Sep 17 00:00:00 2001 From: Jignesh Baldha <iamjignesh.b@gmail.com> Date: Tue, 7 Aug 2018 16:18:08 +0530 Subject: [PATCH 0761/1171] Fixed unit test according to changes --- .../View/Test/Unit/Element/AbstractBlockTest.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php index 70233c0196dc5..ced26f082fe27 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php @@ -285,15 +285,6 @@ public function getCacheLifetimeDataProvider() 'expectsCacheSave' => $this->never(), 'expectedResult' => '', ], - [ - 'cacheLifetime' => false, - 'dataFromCache' => 'dataFromCache', - 'dataForSaveCache' => '', - 'expectsDispatchEvent' => $this->exactly(2), - 'expectsCacheLoad' => $this->once(), - 'expectsCacheSave' => $this->never(), - 'expectedResult' => 'dataFromCache', - ], [ 'cacheLifetime' => 120, 'dataFromCache' => 'dataFromCache', From a87969a3d7e558b780f33d3cbd5cd29bf2e4dd37 Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Fri, 27 Jul 2018 13:58:09 +0200 Subject: [PATCH 0762/1171] Don't add empty method to the cart summary In some cases the method html is empty, this will result in an empty list item, which in the end results in an extra margin of 20px because of default styling. --- .../Checkout/view/frontend/templates/cart/methods.phtml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml index 5530f7661bb1b..d329e2e8c1770 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml @@ -14,7 +14,8 @@ <?php $methods = $block->getMethods('methods') ?: $block->getMethods('top_methods') ?> <ul class="checkout methods items checkout-methods-items"> <?php foreach ($methods as $method): ?> - <?php if ($methodHtml = $block->getMethodHtml($method)): ?> + <?php $methodHtml = $block->getMethodHtml($method); ?> + <?php if (trim($methodHtml) !== ''): ?> <li class="item"><?= /* @escapeNotVerified */ $methodHtml ?></li> <?php endif; ?> <?php endforeach; ?> From b5bf9d223adebc7c48d5039efd9eacd5693fb66c Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@magento.com> Date: Fri, 27 Jul 2018 14:26:16 +0300 Subject: [PATCH 0763/1171] MAGETWO-93733: [Forwardport] Bundle Product --- .../Model/ResourceModel/Indexer/Price.php | 427 +++++++++++++----- .../Model/ResourceModel/Indexer/Price.php | 260 +++++++++-- .../Product/Indexer/Price/Grouped.php | 250 ++++++---- app/code/Magento/GroupedProduct/etc/di.xml | 2 - .../GroupedProduct/etc/product_types.xml | 2 +- composer.json | 3 + .../Annotation/IndexerDimensionMode.php | 146 ++++++ .../TestFramework/Bootstrap/DocBlock.php | 1 + .../Catalog/Product/View/Type/BundleTest.php | 1 + .../Magento/Bundle/Controller/ProductTest.php | 1 + ...BundlePriceCalculatorWithDimensionTest.php | 349 ++++++++++++++ ...BundlePriceCalculatorWithDimensionTest.php | 416 +++++++++++++++++ .../Bundle/Model/Product/OptionListTest.php | 1 + .../Model/Product/PriceWithDimensionTest.php | 43 ++ .../product_with_tier_pricing_rollback.php | 28 ++ .../Model/Export/RowCustomizerTest.php | 1 + ...eWithOptionsTierPriceWithDimensionTest.php | 81 ++++ .../Product/Type/PriceWithDimensionTest.php | 156 +++++++ .../Model/ProductPriceWithDimensionTest.php | 124 +++++ .../Quote/Item/QuantityValidatorTest.php | 4 +- .../Magento/Checkout/Controller/CartTest.php | 1 + .../_files/quote_with_bundle_product.php | 1 + .../quote_with_bundle_product_rollback.php | 35 ++ .../SpecialPriceIndexerWithDimensionTest.php | 107 +++++ ...edOnIsProductListFlagWithDimensionTest.php | 149 ++++++ 25 files changed, 2322 insertions(+), 267 deletions(-) create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_tier_pricing_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_bundle_product_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php index 0b6e97cfb9299..dd01b8ae5351b 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php @@ -6,20 +6,170 @@ namespace Magento\Bundle\Model\ResourceModel\Indexer; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier; +use Magento\Framework\Indexer\DimensionalIndexerInterface; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructureFactory; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Query\JoinAttributeProcessor; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; +use Magento\Catalog\Model\Product\Attribute\Source\Status; /** * Bundle products Price indexer resource model * - * @author Magento Core Team <core@magentocommerce.com> + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Price extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice +class Price implements DimensionalIndexerInterface { /** - * @inheritdoc + * @var IndexTableStructureFactory */ - protected function reindex($entityIds = null) + private $indexTableStructureFactory; + + /** + * @var TableMaintainer + */ + private $tableMaintainer; + + /** + * @var MetadataPool + */ + private $metadataPool; + + /** + * @var \Magento\Framework\App\ResourceConnection + */ + private $resource; + + /** + * @var bool + */ + private $fullReindexAction; + + /** + * @var string + */ + private $connectionName; + + /** + * @var \Magento\Framework\DB\Adapter\AdapterInterface + */ + private $connection; + + /** + * Mapping between dimensions and field in database + * + * @var array + */ + private $dimensionToFieldMapper = [ + WebsiteDimensionProvider::DIMENSION_NAME => 'pw.website_id', + CustomerGroupDimensionProvider::DIMENSION_NAME => 'cg.customer_group_id', + ]; + + /** + * @var BasePriceModifier + */ + private $basePriceModifier; + + /** + * @var JoinAttributeProcessor + */ + private $joinAttributeProcessor; + + /** + * @var \Magento\Framework\Event\ManagerInterface + */ + private $eventManager; + + /** + * @var \Magento\Framework\Module\Manager + */ + private $moduleManager; + + /** + * @param IndexTableStructureFactory $indexTableStructureFactory + * @param TableMaintainer $tableMaintainer + * @param MetadataPool $metadataPool + * @param \Magento\Framework\App\ResourceConnection $resource + * @param BasePriceModifier $basePriceModifier + * @param JoinAttributeProcessor $joinAttributeProcessor + * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @param \Magento\Framework\Module\Manager $moduleManager + * @param bool $fullReindexAction + * @param string $connectionName + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __construct( + IndexTableStructureFactory $indexTableStructureFactory, + TableMaintainer $tableMaintainer, + MetadataPool $metadataPool, + \Magento\Framework\App\ResourceConnection $resource, + BasePriceModifier $basePriceModifier, + JoinAttributeProcessor $joinAttributeProcessor, + \Magento\Framework\Event\ManagerInterface $eventManager, + \Magento\Framework\Module\Manager $moduleManager, + $fullReindexAction = false, + $connectionName = 'indexer' + ) { + $this->indexTableStructureFactory = $indexTableStructureFactory; + $this->tableMaintainer = $tableMaintainer; + $this->connectionName = $connectionName; + $this->metadataPool = $metadataPool; + $this->resource = $resource; + $this->fullReindexAction = $fullReindexAction; + $this->basePriceModifier = $basePriceModifier; + $this->joinAttributeProcessor = $joinAttributeProcessor; + $this->eventManager = $eventManager; + $this->moduleManager = $moduleManager; + } + + /** + * {@inheritdoc} + * + * @throws \Exception + */ + public function executeByDimensions(array $dimensions, \Traversable $entityIds) { - $this->_prepareBundlePrice($entityIds); + $this->tableMaintainer->createMainTmpTable($dimensions); + + $temporaryPriceTable = $this->indexTableStructureFactory->create([ + 'tableName' => $this->tableMaintainer->getMainTmpTable($dimensions), + 'entityField' => 'entity_id', + 'customerGroupField' => 'customer_group_id', + 'websiteField' => 'website_id', + 'taxClassField' => 'tax_class_id', + 'originalPriceField' => 'price', + 'finalPriceField' => 'final_price', + 'minPriceField' => 'min_price', + 'maxPriceField' => 'max_price', + 'tierPriceField' => 'tier_price', + ]); + + $entityIds = iterator_to_array($entityIds); + + $this->prepareTierPriceIndex($dimensions, $entityIds); + + $this->prepareBundlePriceTable(); + + $this->prepareBundlePriceByType( + \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED, + $dimensions, + $entityIds + ); + + $this->prepareBundlePriceByType( + \Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC, + $dimensions, + $entityIds + ); + + $this->calculateBundleOptionPrice($temporaryPriceTable, $dimensions); + + $this->basePriceModifier->modifyPrice($temporaryPriceTable, $entityIds); } /** @@ -27,9 +177,9 @@ protected function reindex($entityIds = null) * * @return string */ - protected function _getBundlePriceTable() + private function getBundlePriceTable() { - return $this->tableStrategy->getTableName('catalog_product_index_price_bundle'); + return $this->getTable('catalog_product_index_price_bundle_tmp'); } /** @@ -37,9 +187,9 @@ protected function _getBundlePriceTable() * * @return string */ - protected function _getBundleSelectionTable() + private function getBundleSelectionTable() { - return $this->tableStrategy->getTableName('catalog_product_index_price_bundle_sel'); + return $this->getTable('catalog_product_index_price_bundle_sel_tmp'); } /** @@ -47,9 +197,9 @@ protected function _getBundleSelectionTable() * * @return string */ - protected function _getBundleOptionTable() + private function getBundleOptionTable() { - return $this->tableStrategy->getTableName('catalog_product_index_price_bundle_opt'); + return $this->getTable('catalog_product_index_price_bundle_opt_tmp'); } /** @@ -57,9 +207,9 @@ protected function _getBundleOptionTable() * * @return $this */ - protected function _prepareBundlePriceTable() + private function prepareBundlePriceTable() { - $this->getConnection()->delete($this->_getBundlePriceTable()); + $this->getConnection()->delete($this->getBundlePriceTable()); return $this; } @@ -68,9 +218,9 @@ protected function _prepareBundlePriceTable() * * @return $this */ - protected function _prepareBundleSelectionTable() + private function prepareBundleSelectionTable() { - $this->getConnection()->delete($this->_getBundleSelectionTable()); + $this->getConnection()->delete($this->getBundleSelectionTable()); return $this; } @@ -79,61 +229,68 @@ protected function _prepareBundleSelectionTable() * * @return $this */ - protected function _prepareBundleOptionTable() + private function prepareBundleOptionTable() { - $this->getConnection()->delete($this->_getBundleOptionTable()); + $this->getConnection()->delete($this->getBundleOptionTable()); return $this; } /** * Prepare temporary price index data for bundle products by price type * + * @param array $dimensions * @param int $priceType * @param int|array $entityIds the entity ids limitation - * @return $this + * @return void + * @throws \Exception * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - protected function _prepareBundlePriceByType($priceType, $entityIds = null) + private function prepareBundlePriceByType($priceType, array $dimensions, $entityIds = null) { $connection = $this->getConnection(); - $table = $this->_getBundlePriceTable(); - $select = $connection->select()->from( ['e' => $this->getTable('catalog_product_entity')], ['entity_id'] - )->join( + )->joinInner( ['cg' => $this->getTable('customer_group')], - '', + array_key_exists(CustomerGroupDimensionProvider::DIMENSION_NAME, $dimensions) + ? sprintf( + '%s = %s', + $this->dimensionToFieldMapper[CustomerGroupDimensionProvider::DIMENSION_NAME], + $dimensions[CustomerGroupDimensionProvider::DIMENSION_NAME]->getValue() + ) : '', ['customer_group_id'] - ); - $this->_addWebsiteJoinToSelect($select, true); - $this->_addProductWebsiteJoinToSelect($select, 'cw.website_id', "e.entity_id"); - $select->columns( - 'website_id', - 'cw' - )->join( - ['cwd' => $this->_getWebsiteDateTable()], - 'cw.website_id = cwd.website_id', + )->joinInner( + ['pw' => $this->getTable('catalog_product_website')], + 'pw.product_id = e.entity_id', + ['pw.website_id'] + )->joinInner( + ['cwd' => $this->getTable('catalog_product_index_website')], + 'pw.website_id = cwd.website_id', [] - )->joinLeft( - ['tp' => $this->_getTierPriceIndexTable()], - 'tp.entity_id = e.entity_id AND tp.website_id = cw.website_id' . + ); + $select->joinLeft( + ['tp' => $this->getTable('catalog_product_index_tier_price')], + 'tp.entity_id = e.entity_id AND tp.website_id = pw.website_id' . ' AND tp.customer_group_id = cg.customer_group_id', [] )->where( 'e.type_id=?', - $this->getTypeId() + \Magento\Bundle\Ui\DataProvider\Product\Listing\Collector\BundlePrice::PRODUCT_TYPE ); - // add enable products limitation - $statusCond = $connection->quoteInto( - '=?', - \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED - ); - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); - $this->_addAttributeToSelect($select, 'status', "e.$linkField", 'cs.store_id', $statusCond, true); + foreach ($dimensions as $dimension) { + if (!isset($this->dimensionToFieldMapper[$dimension->getName()])) { + throw new \LogicException( + 'Provided dimension is not valid for Price indexer: ' . $dimension->getName() + ); + } + $select->where($this->dimensionToFieldMapper[$dimension->getName()] . ' = ?', $dimension->getValue()); + } + + $this->joinAttributeProcessor->process($select, 'status', Status::STATUS_ENABLED); if ($this->moduleManager->isEnabled('Magento_Tax')) { - $taxClassId = $this->_addAttributeToSelect($select, 'tax_class_id', "e.$linkField", 'cs.store_id'); + $taxClassId = $this->joinAttributeProcessor->process($select, 'tax_class_id'); } else { $taxClassId = new \Zend_Db_Expr('0'); } @@ -146,13 +303,12 @@ protected function _prepareBundlePriceByType($priceType, $entityIds = null) ); } - $priceTypeCond = $connection->quoteInto('=?', $priceType); - $this->_addAttributeToSelect($select, 'price_type', "e.$linkField", 'cs.store_id', $priceTypeCond); + $this->joinAttributeProcessor->process($select, 'price_type', $priceType); - $price = $this->_addAttributeToSelect($select, 'price', "e.$linkField", 'cs.store_id'); - $specialPrice = $this->_addAttributeToSelect($select, 'special_price', "e.$linkField", 'cs.store_id'); - $specialFrom = $this->_addAttributeToSelect($select, 'special_from_date', "e.$linkField", 'cs.store_id'); - $specialTo = $this->_addAttributeToSelect($select, 'special_to_date', "e.$linkField", 'cs.store_id'); + $price = $this->joinAttributeProcessor->process($select, 'price'); + $specialPrice = $this->joinAttributeProcessor->process($select, 'special_price'); + $specialFrom = $this->joinAttributeProcessor->process($select, 'special_from_date'); + $specialTo = $this->joinAttributeProcessor->process($select, 'special_to_date'); $currentDate = new \Zend_Db_Expr('cwd.website_date'); $specialFromDate = $connection->getDatePartSql($specialFrom); @@ -205,39 +361,41 @@ protected function _prepareBundlePriceByType($priceType, $entityIds = null) /** * Add additional external limitation */ - $this->_eventManager->dispatch( + $this->eventManager->dispatch( 'catalog_product_prepare_index_select', [ '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') + 'website_field' => new \Zend_Db_Expr('pw.website_id'), + 'store_field' => new \Zend_Db_Expr('cwd.default_store_id') ] ); - $query = $select->insertFromSelect($table); + $query = $select->insertFromSelect($this->getBundlePriceTable()); $connection->query($query); - - return $this; } /** * Calculate fixed bundle product selections price * - * @return $this + * @param IndexTableStructure $priceTable + * @param array $dimensions + * + * @return void + * @throws \Exception */ - protected function _calculateBundleOptionPrice() + private function calculateBundleOptionPrice($priceTable, $dimensions) { $connection = $this->getConnection(); - $this->_prepareBundleSelectionTable(); - $this->_calculateBundleSelectionPrice(\Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED); - $this->_calculateBundleSelectionPrice(\Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC); + $this->prepareBundleSelectionTable(); + $this->calculateBundleSelectionPrice($dimensions, \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED); + $this->calculateBundleSelectionPrice($dimensions, \Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC); - $this->_prepareBundleOptionTable(); + $this->prepareBundleOptionTable(); $select = $connection->select()->from( - $this->_getBundleSelectionTable(), + $this->getBundleSelectionTable(), ['entity_id', 'customer_group_id', 'website_id', 'option_id'] )->group( ['entity_id', 'customer_group_id', 'website_id', 'option_id'] @@ -254,24 +412,24 @@ protected function _calculateBundleOptionPrice() ] ); - $query = $select->insertFromSelect($this->_getBundleOptionTable()); + $query = $select->insertFromSelect($this->getBundleOptionTable()); $connection->query($query); - $this->_prepareDefaultFinalPriceTable(); - $this->applyBundlePrice(); - $this->applyBundleOptionPrice(); - - return $this; + $this->getConnection()->delete($priceTable->getTableName()); + $this->applyBundlePrice($priceTable); + $this->applyBundleOptionPrice($priceTable); } /** * Calculate bundle product selections price by product type * + * @param array $dimensions * @param int $priceType - * @return $this + * @return void + * @throws \Exception * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - protected function _calculateBundleSelectionPrice($priceType) + private function calculateBundleSelectionPrice($dimensions, $priceType) { $connection = $this->getConnection(); @@ -334,9 +492,10 @@ protected function _calculateBundleSelectionPrice($priceType) ]); } - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + $linkField = $metadata->getLinkField(); $select = $connection->select()->from( - ['i' => $this->_getBundlePriceTable()], + ['i' => $this->getBundlePriceTable()], ['entity_id', 'customer_group_id', 'website_id'] )->join( ['parent_product' => $this->getTable('catalog_product_entity')], @@ -355,7 +514,7 @@ protected function _calculateBundleSelectionPrice($priceType) 'bs.selection_id = bsp.selection_id AND bsp.website_id = i.website_id', [''] )->join( - ['idx' => $this->getIdxTable()], + ['idx' => $this->getMainTable($dimensions)], 'bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id' . ' AND i.website_id = idx.website_id', [] @@ -375,49 +534,26 @@ protected function _calculateBundleSelectionPrice($priceType) ] ); - $query = $select->insertFromSelect($this->_getBundleSelectionTable()); + $query = $select->insertFromSelect($this->getBundleSelectionTable()); $connection->query($query); - - return $this; - } - - /** - * Prepare temporary index price for bundle products - * - * @param int|array $entityIds the entity ids limitation - * @return $this - */ - protected function _prepareBundlePrice($entityIds = null) - { - if (!$this->hasEntity() && empty($entityIds)) { - return $this; - } - $this->_prepareTierPriceIndex($entityIds); - $this->_prepareBundlePriceTable(); - $this->_prepareBundlePriceByType(\Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED, $entityIds); - $this->_prepareBundlePriceByType(\Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC, $entityIds); - - $this->_calculateBundleOptionPrice(); - $this->_applyCustomOption(); - - $this->_movePriceDataToIndexTable(); - - return $this; } /** * Prepare percentage tier price for bundle products * - * @param int|array $entityIds - * @return $this + * @param array $dimensions + * @param array $entityIds + * @return void + * @throws \Exception */ - protected function _prepareTierPriceIndex($entityIds = null) + private function prepareTierPriceIndex($dimensions, $entityIds) { $connection = $this->getConnection(); - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + $linkField = $metadata->getLinkField(); // remove index by bundle products $select = $connection->select()->from( - ['i' => $this->_getTierPriceIndexTable()], + ['i' => $this->getTable('catalog_product_index_tier_price')], null )->join( ['e' => $this->getTable('catalog_product_entity')], @@ -425,7 +561,7 @@ protected function _prepareTierPriceIndex($entityIds = null) [] )->where( 'e.type_id=?', - $this->getTypeId() + \Magento\Bundle\Ui\DataProvider\Product\Listing\Collector\BundlePrice::PRODUCT_TYPE ); $query = $select->deleteFromSelect('i'); $connection->query($query); @@ -442,40 +578,47 @@ protected function _prepareTierPriceIndex($entityIds = null) 'tp.all_groups = 1 OR (tp.all_groups = 0 AND tp.customer_group_id = cg.customer_group_id)', ['customer_group_id'] )->join( - ['cw' => $this->getTable('store_website')], - 'tp.website_id = 0 OR tp.website_id = cw.website_id', + ['pw' => $this->getTable('store_website')], + 'tp.website_id = 0 OR tp.website_id = pw.website_id', ['website_id'] )->where( - 'cw.website_id != 0' + 'pw.website_id != 0' )->where( 'e.type_id=?', - $this->getTypeId() + \Magento\Bundle\Ui\DataProvider\Product\Listing\Collector\BundlePrice::PRODUCT_TYPE )->columns( new \Zend_Db_Expr('MIN(tp.value)') )->group( - ['e.entity_id', 'cg.customer_group_id', 'cw.website_id'] + ['e.entity_id', 'cg.customer_group_id', 'pw.website_id'] ); if (!empty($entityIds)) { $select->where('e.entity_id IN(?)', $entityIds); } + foreach ($dimensions as $dimension) { + if (!isset($this->dimensionToFieldMapper[$dimension->getName()])) { + throw new \LogicException( + 'Provided dimension is not valid for Price indexer: ' . $dimension->getName() + ); + } + $select->where($this->dimensionToFieldMapper[$dimension->getName()] . ' = ?', $dimension->getValue()); + } - $query = $select->insertFromSelect($this->_getTierPriceIndexTable()); + $query = $select->insertFromSelect($this->getTable('catalog_product_index_tier_price')); $connection->query($query); - - return $this; } /** * Create bundle price. * + * @param IndexTableStructure $priceTable * @return void */ - private function applyBundlePrice(): void + private function applyBundlePrice($priceTable): void { $select = $this->getConnection()->select(); $select->from( - $this->_getBundlePriceTable(), + $this->getBundlePriceTable(), [ 'entity_id', 'customer_group_id', @@ -486,11 +629,10 @@ private function applyBundlePrice(): void 'min_price', 'max_price', 'tier_price', - 'base_tier', ] ); - $query = $select->insertFromSelect($this->_getDefaultFinalPriceTable()); + $query = $select->insertFromSelect($priceTable->getTableName()); $this->getConnection()->query($query); } @@ -498,13 +640,14 @@ private function applyBundlePrice(): void * Make insert/update bundle option price. * * @return void + * @param IndexTableStructure $priceTable */ - private function applyBundleOptionPrice(): void + private function applyBundleOptionPrice($priceTable): void { $connection = $this->getConnection(); $subSelect = $connection->select()->from( - $this->_getBundleOptionTable(), + $this->getBundleOptionTable(), [ 'entity_id', 'customer_group_id', @@ -534,7 +677,47 @@ private function applyBundleOptionPrice(): void ] ); - $query = $select->crossUpdateFromSelect(['i' => $this->_getDefaultFinalPriceTable()]); + $query = $select->crossUpdateFromSelect(['i' => $priceTable->getTableName()]); $connection->query($query); } + + /** + * Get main table + * + * @param array $dimensions + * @return string + */ + private function getMainTable($dimensions) + { + if ($this->fullReindexAction) { + return $this->tableMaintainer->getMainReplicaTable($dimensions); + } + return $this->tableMaintainer->getMainTable($dimensions); + } + + /** + * Get connection + * + * return \Magento\Framework\DB\Adapter\AdapterInterface + * @throws \DomainException + */ + private function getConnection(): \Magento\Framework\DB\Adapter\AdapterInterface + { + if ($this->connection === null) { + $this->connection = $this->resource->getConnection($this->connectionName); + } + + return $this->connection; + } + + /** + * Get table + * + * @param string $tableName + * @return string + */ + private function getTable($tableName) + { + return $this->resource->getTableName($tableName, $this->connectionName); + } } diff --git a/app/code/Magento/Downloadable/Model/ResourceModel/Indexer/Price.php b/app/code/Magento/Downloadable/Model/ResourceModel/Indexer/Price.php index 855fac5041b21..732f1e70bcb3f 100644 --- a/app/code/Magento/Downloadable/Model/ResourceModel/Indexer/Price.php +++ b/app/code/Magento/Downloadable/Model/ResourceModel/Indexer/Price.php @@ -6,73 +6,176 @@ namespace Magento\Downloadable\Model\ResourceModel\Indexer; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier; +use Magento\Downloadable\Model\Product\Type; +use Magento\Eav\Model\Config; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Indexer\DimensionalIndexerInterface; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Query\BaseFinalPrice; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructureFactory; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure; /** - * Downloadable products Price indexer resource model - * - * @author Magento Core Team <core@magentocommerce.com> + * Downloadable Product Price Indexer Resource model + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Price extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice +class Price implements DimensionalIndexerInterface { /** - * @param null|int|array $entityIds - * @return \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice + * @var BaseFinalPrice */ - protected function reindex($entityIds = null) - { - if ($this->hasEntity() || !empty($entityIds)) { - $this->_prepareFinalPriceData($entityIds); - $this->_applyCustomOption(); - $this->_applyDownloadableLink(); - $this->_movePriceDataToIndexTable(); - } + private $baseFinalPrice; - return $this; + /** + * @var IndexTableStructureFactory + */ + private $indexTableStructureFactory; + + /** + * @var TableMaintainer + */ + private $tableMaintainer; + + /** + * @var MetadataPool + */ + private $metadataPool; + + /** + * @var ResourceConnection + */ + private $resource; + + /** + * @var string + */ + private $connectionName; + + /** + * @var \Magento\Framework\DB\Adapter\AdapterInterface + */ + private $connection; + + /** + * @var Config + */ + private $eavConfig; + + /** + * @var BasePriceModifier + */ + private $basePriceModifier; + + /** + * @param BaseFinalPrice $baseFinalPrice + * @param IndexTableStructureFactory $indexTableStructureFactory + * @param TableMaintainer $tableMaintainer + * @param MetadataPool $metadataPool + * @param Config $eavConfig + * @param ResourceConnection $resource + * @param BasePriceModifier $basePriceModifier + * @param string $connectionName + */ + public function __construct( + BaseFinalPrice $baseFinalPrice, + IndexTableStructureFactory $indexTableStructureFactory, + TableMaintainer $tableMaintainer, + MetadataPool $metadataPool, + Config $eavConfig, + ResourceConnection $resource, + BasePriceModifier $basePriceModifier, + $connectionName = 'indexer' + ) { + $this->baseFinalPrice = $baseFinalPrice; + $this->indexTableStructureFactory = $indexTableStructureFactory; + $this->tableMaintainer = $tableMaintainer; + $this->connectionName = $connectionName; + $this->metadataPool = $metadataPool; + $this->resource = $resource; + $this->eavConfig = $eavConfig; + $this->basePriceModifier = $basePriceModifier; } /** - * Retrieve downloadable links price temporary index table name + * {@inheritdoc} * - * @see _prepareDefaultFinalPriceTable() - * - * @return string + * @throws \Exception */ - protected function _getDownloadableLinkPriceTable() + public function executeByDimensions(array $dimensions, \Traversable $entityIds) { - return $this->tableStrategy->getTableName('catalog_product_index_price_downlod'); + $temporaryPriceTable = $this->indexTableStructureFactory->create([ + 'tableName' => $this->tableMaintainer->getMainTmpTable($dimensions), + 'entityField' => 'entity_id', + 'customerGroupField' => 'customer_group_id', + 'websiteField' => 'website_id', + 'taxClassField' => 'tax_class_id', + 'originalPriceField' => 'price', + 'finalPriceField' => 'final_price', + 'minPriceField' => 'min_price', + 'maxPriceField' => 'max_price', + 'tierPriceField' => 'tier_price', + ]); + $this->fillFinalPrice($dimensions, $entityIds, $temporaryPriceTable); + $this->basePriceModifier->modifyPrice($temporaryPriceTable, iterator_to_array($entityIds)); + $this->applyDownloadableLink($temporaryPriceTable, $dimensions); } /** - * Prepare downloadable links price temporary index table + * Calculate and apply Downloadable links price to index * + * @param IndexTableStructure $temporaryPriceTable + * @param array $dimensions * @return $this + * @throws \Exception */ - protected function _prepareDownloadableLinkPriceTable() - { - $this->getConnection()->delete($this->_getDownloadableLinkPriceTable()); + private function applyDownloadableLink( + IndexTableStructure $temporaryPriceTable, + array $dimensions + ) { + $temporaryDownloadableTableName = 'catalog_product_index_price_downlod_temp'; + $this->getConnection()->createTemporaryTableLike( + $temporaryDownloadableTableName, + $this->getTable('catalog_product_index_price_downlod_tmp'), + true + ); + $this->fillTemporaryTable($temporaryDownloadableTableName, $dimensions); + $this->updateTemporaryDownloadableTable($temporaryPriceTable->getTableName(), $temporaryDownloadableTableName); + $this->getConnection()->delete($temporaryDownloadableTableName); return $this; } /** - * Calculate and apply Downloadable links price to index + * Retrieve catalog_product attribute instance by attribute code * - * @return $this + * @param string $attributeCode + * @return \Magento\Eav\Model\Entity\Attribute\AbstractAttribute + * @throws \Magento\Framework\Exception\LocalizedException */ - protected function _applyDownloadableLink() + protected function getAttribute($attributeCode) { - $connection = $this->getConnection(); - $table = $this->_getDownloadableLinkPriceTable(); - $finalPriceTable = $this->_getDefaultFinalPriceTable(); - - $this->_prepareDownloadableLinkPriceTable(); - - $dlType = $this->_getAttribute('links_purchased_separately'); - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); + return $this->eavConfig->getAttribute(Product::ENTITY, $attributeCode); + } - $ifPrice = $connection->getIfNullSql('dlpw.price_id', 'dlpd.price'); + /** + * Put data into catalog product price indexer Downloadable links price temp table + * + * @param string $temporaryDownloadableTableName + * @param array $dimensions + * @return void + * @throws \Exception + */ + private function fillTemporaryTable(string $temporaryDownloadableTableName, array $dimensions) + { + $dlType = $this->getAttribute('links_purchased_separately'); + $ifPrice = $this->getConnection()->getIfNullSql('dlpw.price_id', 'dlpd.price'); + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + $linkField = $metadata->getLinkField(); - $select = $connection->select()->from( - ['i' => $finalPriceTable], + $select = $this->getConnection()->select()->from( + ['i' => $this->tableMaintainer->getMainTmpTable($dimensions)], ['entity_id', 'customer_group_id', 'website_id'] )->join( ['dl' => $dlType->getBackend()->getTable()], @@ -101,30 +204,87 @@ protected function _applyDownloadableLink() 'max_price' => new \Zend_Db_Expr('SUM(' . $ifPrice . ')'), ] ); + $query = $select->insertFromSelect($temporaryDownloadableTableName); + $this->getConnection()->query($query); + } - $query = $select->insertFromSelect($table); - $connection->query($query); - - $ifTierPrice = $connection->getCheckSql('i.tier_price IS NOT NULL', '(i.tier_price + id.min_price)', 'NULL'); + /** + * Update data in the catalog product price indexer temp table + * + * @param string $temporaryPriceTableName + * @param string $temporaryDownloadableTableName + * @return void + */ + private function updateTemporaryDownloadableTable( + string $temporaryPriceTableName, + string $temporaryDownloadableTableName + ) { + $ifTierPrice = $this->getConnection()->getCheckSql( + 'i.tier_price IS NOT NULL', + '(i.tier_price + id.min_price)', + 'NULL' + ); - $select = $connection->select()->join( - ['id' => $table], + $selectForCrossUpdate = $this->getConnection()->select()->join( + ['id' => $temporaryDownloadableTableName], 'i.entity_id = id.entity_id AND i.customer_group_id = id.customer_group_id' . ' AND i.website_id = id.website_id', [] - )->columns( + ); + // adds price of custom option, that was applied in DefaultPrice::_applyCustomOption + $selectForCrossUpdate->columns( [ 'min_price' => new \Zend_Db_Expr('i.min_price + id.min_price'), 'max_price' => new \Zend_Db_Expr('i.max_price + id.max_price'), 'tier_price' => new \Zend_Db_Expr($ifTierPrice), ] ); + $query = $selectForCrossUpdate->crossUpdateFromSelect(['i' => $temporaryPriceTableName]); + $this->getConnection()->query($query); + } - $query = $select->crossUpdateFromSelect(['i' => $finalPriceTable]); - $connection->query($query); + /** + * Fill final price + * + * @param array $dimensions + * @param \Traversable $entityIds + * @param IndexTableStructure $temporaryPriceTable + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Zend_Db_Select_Exception + */ + private function fillFinalPrice( + array $dimensions, + \Traversable $entityIds, + IndexTableStructure $temporaryPriceTable + ) { + $select = $this->baseFinalPrice->getQuery($dimensions, Type::TYPE_DOWNLOADABLE, iterator_to_array($entityIds)); + $query = $select->insertFromSelect($temporaryPriceTable->getTableName(), [], false); + $this->tableMaintainer->getConnection()->query($query); + } - $connection->delete($table); + /** + * Get connection + * + * return \Magento\Framework\DB\Adapter\AdapterInterface + * @throws \DomainException + */ + private function getConnection(): \Magento\Framework\DB\Adapter\AdapterInterface + { + if ($this->connection === null) { + $this->connection = $this->resource->getConnection($this->connectionName); + } - return $this; + return $this->connection; + } + + /** + * Get table + * + * @param string $tableName + * @return string + */ + private function getTable($tableName) + { + return $this->resource->getTableName($tableName, $this->connectionName); } } diff --git a/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Indexer/Price/Grouped.php b/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Indexer/Price/Grouped.php index cbbb58d3c24c8..2861c574532ff 100644 --- a/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Indexer/Price/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Indexer/Price/Grouped.php @@ -7,140 +7,210 @@ */ namespace Magento\GroupedProduct\Model\ResourceModel\Product\Indexer\Price; -use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Indexer\DimensionalIndexerInterface; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructureFactory; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure; +use Magento\GroupedProduct\Model\ResourceModel\Product\Link; +use Magento\GroupedProduct\Model\Product\Type\Grouped as GroupedType; -class Grouped extends DefaultPrice implements GroupedInterface +/** + * Calculate minimal and maximal prices for Grouped products + * Use calculated price for relation products + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class Grouped implements DimensionalIndexerInterface { /** - * Prefix for temporary table support. + * @var IndexTableStructureFactory */ - const TRANSIT_PREFIX = 'transit_'; + private $indexTableStructureFactory; /** - * @inheritdoc + * @var TableMaintainer */ - protected function reindex($entityIds = null) - { - $this->_prepareGroupedProductPriceData($entityIds); + private $tableMaintainer; + + /** + * @var MetadataPool + */ + private $metadataPool; + + /** + * @var ResourceConnection + */ + private $resource; + + /** + * @var string + */ + private $connectionName; + + /** + * @var AdapterInterface + */ + private $connection; + + /** + * @var bool + */ + private $fullReindexAction; + + /** + * @param IndexTableStructureFactory $indexTableStructureFactory + * @param TableMaintainer $tableMaintainer + * @param MetadataPool $metadataPool + * @param ResourceConnection $resource + * @param string $connectionName + * @param bool $fullReindexAction + */ + public function __construct( + IndexTableStructureFactory $indexTableStructureFactory, + TableMaintainer $tableMaintainer, + MetadataPool $metadataPool, + ResourceConnection $resource, + $connectionName = 'indexer', + $fullReindexAction = false + ) { + $this->indexTableStructureFactory = $indexTableStructureFactory; + $this->tableMaintainer = $tableMaintainer; + $this->connectionName = $connectionName; + $this->metadataPool = $metadataPool; + $this->resource = $resource; + $this->fullReindexAction = $fullReindexAction; } /** - * Calculate minimal and maximal prices for Grouped products - * Use calculated price for relation products + * {@inheritdoc} * - * @param int|array $entityIds the parent entity ids limitation - * @return $this + * @throws \Exception */ - protected function _prepareGroupedProductPriceData($entityIds = null) + public function executeByDimensions(array $dimensions, \Traversable $entityIds) { - if (!$this->hasEntity() && empty($entityIds)) { - return $this; - } - - $connection = $this->getConnection(); - $table = $this->getIdxTable(); - - if (!$this->tableStrategy->getUseIdxTable()) { - $additionalIdxTable = $connection->getTableName(self::TRANSIT_PREFIX . $this->getIdxTable()); - $connection->createTemporaryTableLike($additionalIdxTable, $table); - $query = $connection->insertFromSelect( - $this->_prepareGroupedProductPriceDataSelect($entityIds), - $additionalIdxTable, - [] - ); - $connection->query($query); - - $select = $connection->select()->from($additionalIdxTable); - $query = $connection->insertFromSelect( - $select, - $table, - [], - \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE - ); - $connection->query($query); - $connection->dropTemporaryTable($additionalIdxTable); - } else { - $query = $this->_prepareGroupedProductPriceDataSelect($entityIds)->insertFromSelect($table); - $connection->query($query); - } - return $this; + /** @var IndexTableStructure $temporaryPriceTable */ + $temporaryPriceTable = $this->indexTableStructureFactory->create([ + 'tableName' => $this->tableMaintainer->getMainTmpTable($dimensions), + 'entityField' => 'entity_id', + 'customerGroupField' => 'customer_group_id', + 'websiteField' => 'website_id', + 'taxClassField' => 'tax_class_id', + 'originalPriceField' => 'price', + 'finalPriceField' => 'final_price', + 'minPriceField' => 'min_price', + 'maxPriceField' => 'max_price', + 'tierPriceField' => 'tier_price', + ]); + $query = $this->prepareGroupedProductPriceDataSelect($dimensions, iterator_to_array($entityIds)) + ->insertFromSelect($temporaryPriceTable->getTableName()); + $this->getConnection()->query($query); } /** * Prepare data index select for Grouped products prices * - * @param int|array $entityIds the parent entity ids limitation + * @param array $dimensions + * @param array $entityIds the parent entity ids limitation * @return \Magento\Framework\DB\Select + * @throws \Exception */ - protected function _prepareGroupedProductPriceDataSelect($entityIds = null) + private function prepareGroupedProductPriceDataSelect(array $dimensions, array $entityIds) { - $connection = $this->getConnection(); - $table = $this->getIdxTable(); - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); - $select = $connection->select()->from( + $select = $this->getConnection()->select(); + + $select->from( ['e' => $this->getTable('catalog_product_entity')], 'entity_id' - )->joinLeft( + ); + + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + $select->joinLeft( ['l' => $this->getTable('catalog_product_link')], - 'e.' . $linkField . ' = l.product_id AND l.link_type_id=' . - \Magento\GroupedProduct\Model\ResourceModel\Product\Link::LINK_TYPE_GROUPED, + 'e.' . $linkField . ' = l.product_id AND l.link_type_id=' . Link::LINK_TYPE_GROUPED, [] - )->join( - ['cg' => $this->getTable('customer_group')], - '', - ['customer_group_id'] ); - $this->_addWebsiteJoinToSelect($select, true); - $this->_addProductWebsiteJoinToSelect($select, 'cw.website_id', 'e.entity_id'); - $minCheckSql = $connection->getCheckSql('le.required_options = 0', 'i.min_price', 0); - $maxCheckSql = $connection->getCheckSql('le.required_options = 0', 'i.max_price', 0); - $select->columns( - 'website_id', - 'cw' - )->joinLeft( + //additional information about inner products + $select->joinLeft( ['le' => $this->getTable('catalog_product_entity')], 'le.entity_id = l.linked_product_id', [] - )->joinLeft( - ['i' => $table], - 'i.entity_id = l.linked_product_id AND i.website_id = cw.website_id' . - ' AND i.customer_group_id = cg.customer_group_id', + ); + $select->columns( [ - 'tax_class_id' => $this->getConnection()->getCheckSql( - 'MIN(i.tax_class_id) IS NULL', - '0', - 'MIN(i.tax_class_id)' - ), + 'i.customer_group_id', + 'i.website_id', + ] + ); + $taxClassId = $this->getConnection()->getCheckSql('MIN(i.tax_class_id) IS NULL', '0', 'MIN(i.tax_class_id)'); + $minCheckSql = $this->getConnection()->getCheckSql('le.required_options = 0', 'i.min_price', 0); + $maxCheckSql = $this->getConnection()->getCheckSql('le.required_options = 0', 'i.max_price', 0); + $select->join( + ['i' => $this->getMainTable($dimensions)], + 'i.entity_id = l.linked_product_id', + [ + 'tax_class_id' => $taxClassId, 'price' => new \Zend_Db_Expr('NULL'), 'final_price' => new \Zend_Db_Expr('NULL'), 'min_price' => new \Zend_Db_Expr('MIN(' . $minCheckSql . ')'), 'max_price' => new \Zend_Db_Expr('MAX(' . $maxCheckSql . ')'), 'tier_price' => new \Zend_Db_Expr('NULL'), ] - )->group( - ['e.entity_id', 'cg.customer_group_id', 'cw.website_id'] - )->where( + ); + $select->group( + ['e.entity_id', 'i.customer_group_id', 'i.website_id'] + ); + $select->where( 'e.type_id=?', - $this->getTypeId() + GroupedType::TYPE_CODE ); if ($entityIds !== null) { $select->where('e.entity_id IN(?)', $entityIds); } - /** - * Add additional external limitation - */ - $this->_eventManager->dispatch( - 'catalog_product_prepare_index_select', - [ - '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') - ] - ); return $select; } + + /** + * Get main table + * + * @param array $dimensions + * @return string + */ + private function getMainTable($dimensions) + { + if ($this->fullReindexAction) { + return $this->tableMaintainer->getMainReplicaTable($dimensions); + } + return $this->tableMaintainer->getMainTable($dimensions); + } + + /** + * Get connection + * + * return \Magento\Framework\DB\Adapter\AdapterInterface + * @throws \DomainException + */ + private function getConnection(): \Magento\Framework\DB\Adapter\AdapterInterface + { + if ($this->connection === null) { + $this->connection = $this->resource->getConnection($this->connectionName); + } + + return $this->connection; + } + + /** + * Get table + * + * @param string $tableName + * @return string + */ + private function getTable($tableName) + { + return $this->resource->getTableName($tableName, $this->connectionName); + } } diff --git a/app/code/Magento/GroupedProduct/etc/di.xml b/app/code/Magento/GroupedProduct/etc/di.xml index 0371ef2386eb7..43678d0ad7a82 100644 --- a/app/code/Magento/GroupedProduct/etc/di.xml +++ b/app/code/Magento/GroupedProduct/etc/di.xml @@ -88,8 +88,6 @@ </argument> </arguments> </type> - <preference for="Magento\GroupedProduct\Model\ResourceModel\Product\Indexer\Price\GroupedInterface" - type="Magento\GroupedProduct\Model\ResourceModel\Product\Indexer\Price\Grouped"/> <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator"> <arguments> <argument name="estimators" xsi:type="array"> diff --git a/app/code/Magento/GroupedProduct/etc/product_types.xml b/app/code/Magento/GroupedProduct/etc/product_types.xml index 8f41611353a32..9f8f0534ff34f 100644 --- a/app/code/Magento/GroupedProduct/etc/product_types.xml +++ b/app/code/Magento/GroupedProduct/etc/product_types.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/product_types.xsd"> <type name="grouped" label="Grouped Product" modelInstance="Magento\GroupedProduct\Model\Product\Type\Grouped" composite='true' indexPriority="50" sortOrder="30"> <priceModel instance="Magento\GroupedProduct\Model\Product\Type\Grouped\Price" /> - <indexerModel instance="Magento\GroupedProduct\Model\ResourceModel\Product\Indexer\Price\GroupedInterface" /> + <indexerModel instance="Magento\GroupedProduct\Model\ResourceModel\Product\Indexer\Price\Grouped" /> <stockIndexerModel instance="Magento\GroupedProduct\Model\ResourceModel\Indexer\Stock\Grouped" /> <customAttributes> <attribute name="is_real_product" value="false"/> diff --git a/composer.json b/composer.json index 375522b557f03..8ef297490e734 100644 --- a/composer.json +++ b/composer.json @@ -90,6 +90,9 @@ "sebastian/phpcpd": "~3.0.0", "squizlabs/php_codesniffer": "3.3.0" }, + "suggest": { + "ext-pcntl": "Need for run processes in parallel mode" + }, "replace": { "magento/module-marketplace": "*", "magento/module-admin-notification": "*", diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php new file mode 100644 index 0000000000000..86d2d8ee589a7 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php @@ -0,0 +1,146 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\TestFramework\Annotation; + +use Magento\Catalog\Model\Indexer\Product\Price\ModeSwitcher; +use Magento\Catalog\Model\Indexer\Product\Price\Processor; +use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Framework\App\Config\ConfigResource\ConfigInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\App\Config; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; +use PHPUnit\Framework\TestCase; + +/** + * Implementation of the @magentoIndexerDimensionMode DocBlock annotation + */ +class IndexerDimensionMode +{ + /** @var TypeListInterface */ + private $cacheTypeList; + + /** @var ScopeConfigInterface */ + private $configReader; + + /** @var ModeSwitcher */ + private $modeSwitcher; + + /** @var ConfigInterface */ + private $configWriter; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var \Magento\TestFramework\Db\Mysql */ + private $db; + + /** @var bool */ + private $isDimensionMode = false; + + private function restoreDb() + { + $this->db = Bootstrap::getInstance()->getBootstrap()->getApplication()->getDbInstance(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->db->restoreFromDbDump(); + $this->cacheTypeList = $this->objectManager->get(TypeListInterface::class); + $this->cacheTypeList->cleanType('config'); + $this->objectManager->get(Config::class)->clean(); + } + + /** + * @param string $mode + */ + private function setDimensionMode($mode, $test) + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->modeSwitcher = $this->objectManager->get(ModeSwitcher::class); + $this->configWriter = $this->objectManager->get(ConfigInterface::class); + $this->configReader = $this->objectManager->get(ScopeConfigInterface::class); + $this->cacheTypeList = $this->objectManager->get(TypeListInterface::class); + + $this->configReader->clean(); + $previousMode = $this->configReader->getValue(ModeSwitcher::XML_PATH_PRICE_DIMENSIONS_MODE) ?: + DimensionModeConfiguration::DIMENSION_NONE; + + if ($previousMode !== $mode) { + //Create new tables and move data + $this->modeSwitcher->createTables($mode); + $this->modeSwitcher->moveData($mode, $previousMode); + + //Change config options + $this->configWriter->saveConfig(ModeSwitcher::XML_PATH_PRICE_DIMENSIONS_MODE, $mode); + $this->cacheTypeList->cleanType('config'); + $this->objectManager->get(Config::class)->clean(); + + //Delete old tables + $this->modeSwitcher->dropTables($previousMode); + } else { + $this->fail('Dimensions mode for indexer has not been changed', $test); + } + } + + /** + * Handler for 'startTest' event + * + * @param TestCase $test + * @return void + */ + public function startTest(TestCase $test) + { + $source = $test->getAnnotations(); + + if (isset($source['method']['magentoIndexerDimensionMode'])) { + $annotations = $source['method']['magentoIndexerDimensionMode']; + } elseif (isset($source['class']['magentoIndexerDimensionMode'])) { + $annotations = $source['class']['magentoIndexerDimensionMode']; + } else { + return; + } + + $dbIsolation = $source['method']['magentoDbIsolation'] + ?? $source['class']['magentoDbIsolation'] + ?? ['disabled']; + + if ($dbIsolation[0] != 'disabled') { + $this->fail("Invalid @magentoDbIsolation declaration: $dbIsolation[0]", $test); + } + + list($indexerType, $indexerMode) = explode(' ', $annotations[0]); + + if ($indexerType == Processor::INDEXER_ID) { + $this->isDimensionMode = true; + $this->setDimensionMode($indexerMode, $test); + } + } + + /** + * Handler for 'endTest' event + * + * @return void + */ + public function endTest() + { + if ($this->isDimensionMode) { + $this->restoreDb(); + $this->isDimensionMode = false; + } + } + + /** + * Fails the test with specified error message + * + * @param string $message + * @param TestCase $test + * @throws \Exception + */ + private function fail($message, TestCase $test) + { + $test->fail("{$message} in the test '{$test->toString()}'"); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php b/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php index d0b6d0427bbd3..b92fd6da077d2 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php @@ -54,6 +54,7 @@ protected function _getSubscribers(\Magento\TestFramework\Application $applicati new \Magento\TestFramework\Isolation\WorkingDirectory(), new \Magento\TestFramework\Isolation\DeploymentConfig(), new \Magento\TestFramework\Annotation\AppIsolation($application), + new \Magento\TestFramework\Annotation\IndexerDimensionMode($application), new \Magento\TestFramework\Isolation\AppConfig(), new \Magento\TestFramework\Annotation\ConfigFixture(), new \Magento\TestFramework\Annotation\DataFixtureBeforeTransaction($this->_fixturesBaseDir), diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php index 35c470282937f..f9947d562d34d 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php @@ -11,6 +11,7 @@ * Test for Magento\Bundle\Block\Catalog\Product\View\Type\Bundle * * @magentoDataFixture Magento/Bundle/_files/product.php + * @magentoDbIsolation disabled * @magentoAppArea frontend */ class BundleTest extends \PHPUnit\Framework\TestCase diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Controller/ProductTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Controller/ProductTest.php index 1f2f08f345099..65a9b1234ef7c 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Controller/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Controller/ProductTest.php @@ -13,6 +13,7 @@ class ProductTest extends \Magento\TestFramework\TestCase\AbstractController { /** * @magentoDataFixture Magento/Bundle/_files/product.php + * @magentoDbIsolation disabled */ public function testViewAction() { diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php new file mode 100644 index 0000000000000..6794a686146f9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php @@ -0,0 +1,349 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Bundle\Model\Product; + +/** + * @magentoDbIsolation disabled + * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @group indexer_dimension + * @magentoAppArea frontend + */ +class DynamicBundlePriceCalculatorWithDimensionTest extends BundlePriceAbstract +{ + /** + * @param array $strategyModifiers + * @param array $expectedResults + * @dataProvider getTestCases + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product.php + * @magentoDbIsolation disabled + */ + public function testPriceForDynamicBundle(array $strategyModifiers, array $expectedResults) + { + $this->prepareFixture($strategyModifiers, 'bundle_product'); + $bundleProduct = $this->productRepository->get('bundle_product', false, null, true); + + /** @var \Magento\Framework\Pricing\PriceInfo\Base $priceInfo */ + $priceInfo = $bundleProduct->getPriceInfo(); + $priceCode = \Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE; + + $this->assertEquals( + $expectedResults['minimalPrice'], + $priceInfo->getPrice($priceCode)->getMinimalPrice()->getValue(), + 'Failed to check minimal price on product' + ); + $this->assertEquals( + $expectedResults['maximalPrice'], + $priceInfo->getPrice($priceCode)->getMaximalPrice()->getValue(), + 'Failed to check maximal price on product' + ); + + $priceInfoFromIndexer = $this->productCollectionFactory->create() + ->addFieldToFilter('sku', 'bundle_product') + ->addPriceData() + ->load() + ->getFirstItem(); + + $this->assertEquals($expectedResults['minimalPrice'], $priceInfoFromIndexer->getMinimalPrice()); + $this->assertEquals($expectedResults['maximalPrice'], $priceInfoFromIndexer->getMaxPrice()); + } + + /** + * @param array $strategyModifiers + * @param array $expectedResults + * @dataProvider getTestCases + * @magentoAppIsolation enabled + * @magentoConfigFixture current_store catalog/price/scope 1 + * @magentoDataFixture Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product.php + * @magentoDbIsolation disabled + */ + public function testPriceForDynamicBundleInWebsiteScope(array $strategyModifiers, array $expectedResults) + { + $this->prepareFixture($strategyModifiers, 'bundle_product'); + $bundleProduct = $this->productRepository->get('bundle_product', false, null, true); + + /** @var \Magento\Framework\Pricing\PriceInfo\Base $priceInfo */ + $priceInfo = $bundleProduct->getPriceInfo(); + $priceCode = \Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE; + + $this->assertEquals( + $expectedResults['minimalPrice'], + $priceInfo->getPrice($priceCode)->getMinimalPrice()->getValue(), + 'Failed to check minimal price on product' + ); + $this->assertEquals( + $expectedResults['maximalPrice'], + $priceInfo->getPrice($priceCode)->getMaximalPrice()->getValue(), + 'Failed to check maximal price on product' + ); + + $priceInfoFromIndexer = $this->productCollectionFactory->create() + ->addFieldToFilter('sku', 'bundle_product') + ->addPriceData() + ->load() + ->getFirstItem(); + + $this->assertEquals($expectedResults['minimalPrice'], $priceInfoFromIndexer->getMinimalPrice()); + $this->assertEquals($expectedResults['maximalPrice'], $priceInfoFromIndexer->getMaxPrice()); + } + + /** + * Test cases for current test + * @return array + */ + public function getTestCases() + { + return [ + '#1 Testing price for dynamic bundle product with one simple' => [ + 'strategy' => $this->getBundleConfiguration1(), + 'expectedResults' => [ + // just price from simple1 + 'minimalPrice' => 10, + // just price from simple1 + 'maximalPrice' => 10 + ] + ], + + '#2 Testing price for dynamic bundle product with three simples and different qty' => [ + 'strategy' => $this->getBundleConfiguration2(), + 'expectedResults' => [ + // min price from simples 3*10 or 30 + 'minimalPrice' => 30, + // (3 * 10) + (2 * 20) + 30 + 'maximalPrice' => 100 + ] + ], + + '#3 Testing price for dynamic bundle product with four simples and different price' => [ + 'strategy' => $this->getBundleConfiguration3(), + 'expectedResults' => [ + // 10 + 'minimalPrice' => 10, + // 10 + 20 + 30 + 'maximalPrice' => 60 + ] + ], + + '#4 Testing price for dynamic bundle with two non required options' => [ + 'strategy' => $this->getBundleConfiguration4(), + 'expectedResults' => [ + // 1 * 10 + 'minimalPrice' => 10, + // 3 * 20 + 1 * 10 + 3 * 20 + 'maximalPrice' => 130 + ] + ], + + '#5 Testing price for dynamic bundle with two required options' => [ + 'strategy' => $this->getBundleConfiguration5(), + 'expectedResults' => [ + // 1 * 10 + 1 * 10 + 'minimalPrice' => 20, + // 3 * 20 + 1 * 10 + 3 * 20 + 'maximalPrice' => 130 + ] + ], + ]; + } + + /** + * Dynamic bundle product with one simple + * + * @return array + */ + private function getBundleConfiguration1() + { + $optionsData = [ + [ + 'title' => 'op1', + 'required' => true, + 'type' => 'checkbox', + 'links' => [ + [ + 'sku' => 'simple1', + 'qty' => 1, + ], + ] + ], + ]; + + return [ + [ + 'modifierName' => 'addSimpleProduct', + 'data' => [$optionsData] + ], + ]; + } + + /** + * Dynamic bundle product with three simples and different qty + * + * @return array + */ + private function getBundleConfiguration2() + { + $optionsData = [ + [ + 'title' => 'op1', + 'required' => true, + 'type' => 'checkbox', + 'links' => [ + [ + 'sku' => 'simple1', + 'qty' => 3, + ], + [ + 'sku' => 'simple2', + 'qty' => 2, + ], + [ + 'sku' => 'simple3', + 'qty' => 1, + ], + ] + ] + ]; + + return [ + [ + 'modifierName' => 'addSimpleProduct', + 'data' => [$optionsData] + ], + ]; + } + + /** + * Dynamic bundle product with three simples and different price + * + * @return array + */ + private function getBundleConfiguration3() + { + $optionsData = [ + [ + 'title' => 'op1', + 'required' => true, + 'type' => 'checkbox', + 'links' => [ + [ + 'sku' => 'simple1', + 'qty' => 1, + ], + [ + 'sku' => 'simple2', + 'qty' => 1, + ], + [ + 'sku' => 'simple3', + 'qty' => 1, + ] + ] + ] + ]; + + return [ + [ + 'modifierName' => 'addSimpleProduct', + 'data' => [$optionsData] + ], + ]; + } + + /** + * Dynamic bundle with two non required options and special price + * @return array + */ + private function getBundleConfiguration4() + { + $optionsData = [ + [ + 'title' => 'Op1', + 'required' => false, + 'type' => 'radio', + 'links' => [ + [ + 'sku' => 'simple1', + 'qty' => 1, + ], + [ + 'sku' => 'simple2', + 'qty' => 3, + ], + ] + ], + [ + 'title' => 'Op2', + 'required' => false, + 'type' => 'checkbox', + 'links' => [ + [ + 'sku' => 'simple1', + 'qty' => 1, + ], + [ + 'sku' => 'simple2', + 'qty' => 3, + ], + ] + ] + ]; + + return [ + [ + 'modifierName' => 'addSimpleProduct', + 'data' => [$optionsData] + ], + ]; + } + + /** + * Dynamic bundle with two required options + * @return array + */ + private function getBundleConfiguration5() + { + $optionsData = [ + [ + 'title' => 'Op1', + 'required' => true, + 'type' => 'radio', + 'links' => [ + [ + 'sku' => 'simple1', + 'qty' => 1, + ], + [ + 'sku' => 'simple2', + 'qty' => 3, + ], + ] + ], + [ + 'title' => 'Op2', + 'required' => true, + 'type' => 'checkbox', + 'links' => [ + [ + 'sku' => 'simple1', + 'qty' => 1, + ], + [ + 'sku' => 'simple2', + 'qty' => 3, + ], + ] + ] + ]; + + return [ + [ + 'modifierName' => 'addSimpleProduct', + 'data' => [$optionsData] + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php new file mode 100644 index 0000000000000..ffc24b2f45d5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php @@ -0,0 +1,416 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Bundle\Model\Product; + +use \Magento\Bundle\Api\Data\LinkInterface; + +/** + * @magentoDbIsolation disabled + * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @group indexer_dimension + * @magentoAppArea frontend + */ +class FixedBundlePriceCalculatorWithDimensionTest extends BundlePriceAbstract +{ + /** + * @param array $strategyModifiers + * @param array $expectedResults + * @dataProvider getTestCases + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Bundle/_files/PriceCalculator/fixed_bundle_product.php + * @magentoDbIsolation disabled + */ + public function testPriceForFixedBundle(array $strategyModifiers, array $expectedResults) + { + $this->prepareFixture($strategyModifiers, 'bundle_product'); + $bundleProduct = $this->productRepository->get('bundle_product', false, null, true); + + /** @var \Magento\Framework\Pricing\PriceInfo\Base $priceInfo */ + $priceInfo = $bundleProduct->getPriceInfo(); + $priceCode = \Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE; + + $this->assertEquals( + $expectedResults['minimalPrice'], + $priceInfo->getPrice($priceCode)->getMinimalPrice()->getValue(), + 'Failed to check minimal price on product' + ); + $this->assertEquals( + $expectedResults['maximalPrice'], + $priceInfo->getPrice($priceCode)->getMaximalPrice()->getValue(), + 'Failed to check maximal price on product' + ); + + $priceInfoFromIndexer = $this->productCollectionFactory->create() + ->addIdFilter([42]) + ->addPriceData() + ->load() + ->getFirstItem(); + + $this->assertEquals($expectedResults['minimalPrice'], $priceInfoFromIndexer->getMinimalPrice()); + $this->assertEquals($expectedResults['maximalPrice'], $priceInfoFromIndexer->getMaxPrice()); + } + + /** + * @param array $strategyModifiers + * @param array $expectedResults + * @dataProvider getTestCases + * @magentoAppIsolation enabled + * @magentoConfigFixture current_store catalog/price/scope 1 + * @magentoDataFixture Magento/Bundle/_files/PriceCalculator/fixed_bundle_product.php + * @magentoDbIsolation disabled + */ + public function testPriceForFixedBundleInWebsiteScope(array $strategyModifiers, array $expectedResults) + { + $this->prepareFixture($strategyModifiers, 'bundle_product'); + $bundleProduct = $this->productRepository->get('bundle_product', false, null, true); + + /** @var \Magento\Framework\Pricing\PriceInfo\Base $priceInfo */ + $priceInfo = $bundleProduct->getPriceInfo(); + $priceCode = \Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE; + + $this->assertEquals( + $expectedResults['minimalPrice'], + $priceInfo->getPrice($priceCode)->getMinimalPrice()->getValue(), + 'Failed to check minimal price on product' + ); + $this->assertEquals( + $expectedResults['maximalPrice'], + $priceInfo->getPrice($priceCode)->getMaximalPrice()->getValue(), + 'Failed to check maximal price on product' + ); + + $priceInfoFromIndexer = $this->productCollectionFactory->create() + ->addFieldToFilter('sku', 'bundle_product') + ->addPriceData() + ->load() + ->getFirstItem(); + + $this->assertEquals($expectedResults['minimalPrice'], $priceInfoFromIndexer->getMinimalPrice()); + $this->assertEquals($expectedResults['maximalPrice'], $priceInfoFromIndexer->getMaxPrice()); + } + + /** + * Test cases for current test + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function getTestCases() + { + return [ + '#1 Testing price for fixed bundle product with one simple' => [ + 'strategy' => $this->getProductWithOneSimple(), + 'expectedResults' => [ + // 110 + 10 (price from simple1) + 'minimalPrice' => 120, + // 110 + 10 (sum of simple price) + 'maximalPrice' => 120, + ] + ], + + '#2 Testing price for fixed bundle product with three simples and different qty' => [ + 'strategy' => $this->getProductWithDifferentQty(), + 'expectedResults' => [ + // 110 + 10 (min price from simples) + 'minimalPrice' => 120, + // 110 + (3 * 10) + (2 * 10) + 10 + 'maximalPrice' => 170, + ] + ], + + '#3 Testing price for fixed bundle product with three simples and different price' => [ + 'strategy' => $this->getProductWithDifferentPrice(), + 'expectedResults' => [ + // 110 + 10 + 'minimalPrice' => 120, + // 110 + 60 + 'maximalPrice' => 170, + ] + ], + + '#4 Testing price for fixed bundle product with three simples' => [ + 'strategy' => $this->getProductWithSamePrice(), + 'expectedResults' => [ + // 110 + 10 + 'minimalPrice' => 120, + // 110 + 30 + 'maximalPrice' => 140, + ] + ], + + ' + #5 Testing price for fixed bundle product + with fixed sub items, fixed options and without any discounts + ' => [ + 'strategy' => $this->getBundleConfiguration3( + LinkInterface::PRICE_TYPE_FIXED, + self::CUSTOM_OPTION_PRICE_TYPE_FIXED + ), + 'expectedResults' => [ + // 110 + 1 * 20 + 100 + 'minimalPrice' => 230, + + // 110 + 1 * 20 + 100 + 'maximalPrice' => 230, + ] + ], + + ' + #6 Testing price for fixed bundle product + with percent sub items, percent options and without any discounts + ' => [ + 'strategy' => $this->getBundleConfiguration3( + LinkInterface::PRICE_TYPE_PERCENT, + self::CUSTOM_OPTION_PRICE_TYPE_PERCENT + ), + 'expectedResults' => [ + // 110 + 110 * 0.2 + 110 * 1 + 'minimalPrice' => 242, + + // 110 + 110 * 0.2 + 110 * 1 + 'maximalPrice' => 242, + ] + ], + + ' + #7 Testing price for fixed bundle product + with fixed sub items, percent options and without any discounts + ' => [ + 'strategy' => $this->getBundleConfiguration3( + LinkInterface::PRICE_TYPE_FIXED, + self::CUSTOM_OPTION_PRICE_TYPE_PERCENT + ), + 'expectedResults' => [ + // 110 + 1 * 20 + 110 * 1 + 'minimalPrice' => 240, + + // 110 + 1 * 20 + 110 * 1 + 'maximalPrice' => 240, + ] + ], + + ' + #8 Testing price for fixed bundle product + with percent sub items, fixed options and without any discounts + ' => [ + 'strategy' => $this->getBundleConfiguration3( + LinkInterface::PRICE_TYPE_PERCENT, + self::CUSTOM_OPTION_PRICE_TYPE_FIXED + ), + 'expectedResults' => [ + // 110 + 110 * 0.2 + 100 + 'minimalPrice' => 232, + + // 110 + 110 * 0.2 + 100 + 'maximalPrice' => 232, + ] + ], + ]; + } + + /** + * Fixed bundle product with one simple + * @return array + */ + private function getProductWithOneSimple() + { + $optionsData = [ + [ + 'title' => 'Op1', + 'required' => true, + 'type' => 'checkbox', + 'links' => [ + [ + 'sku' => 'simple1', + 'price' => 10, + 'qty' => 1, + 'price_type' => LinkInterface::PRICE_TYPE_FIXED, + ], + ] + ], + ]; + + return [ + [ + 'modifierName' => 'addSimpleProduct', + 'data' => [$optionsData] + ], + ]; + } + + /** + * Fixed bundle product with three simples and different qty + * @return array + */ + private function getProductWithDifferentQty() + { + $optionsData = [ + [ + 'title' => 'Op1', + 'required' => true, + 'type' => 'checkbox', + 'links' => [ + [ + 'sku' => 'simple1', + 'price' => 10, + 'qty' => 3, + 'price_type' => LinkInterface::PRICE_TYPE_FIXED, + ], + [ + 'sku' => 'simple2', + 'price' => 10, + 'qty' => 2, + 'price_type' => LinkInterface::PRICE_TYPE_FIXED, + ], + [ + 'sku' => 'simple3', + 'price' => 10, + 'qty' => 1, + 'price_type' => LinkInterface::PRICE_TYPE_FIXED, + ], + ] + ] + ]; + + return [ + [ + 'modifierName' => 'addSimpleProduct', + 'data' => [$optionsData] + ], + ]; + } + + /** + * Fixed bundle product with three simples and different price + * @return array + */ + private function getProductWithSamePrice() + { + $optionsData = [ + [ + 'title' => 'Op1', + 'required' => true, + 'type' => 'checkbox', + 'links' => [ + [ + 'sku' => 'simple1', + 'price' => 10, + 'qty' => 1, + 'price_type' => LinkInterface::PRICE_TYPE_FIXED, + ], + [ + 'sku' => 'simple2', + 'price' => 10, + 'qty' => 1, + 'price_type' => LinkInterface::PRICE_TYPE_FIXED, + ], + [ + 'sku' => 'simple3', + 'price' => 10, + 'qty' => 1, + 'price_type' => LinkInterface::PRICE_TYPE_FIXED, + ] + ] + ] + ]; + + return [ + [ + 'modifierName' => 'addSimpleProduct', + 'data' => [$optionsData] + ], + ]; + } + + /** + * Fixed bundle product with three simples + * @return array + */ + private function getProductWithDifferentPrice() + { + $optionsData = [ + [ + 'title' => 'Op1', + 'required' => true, + 'type' => 'checkbox', + 'links' => [ + [ + 'sku' => 'simple1', + 'price' => 10, + 'qty' => 1, + 'price_type' => LinkInterface::PRICE_TYPE_FIXED, + ], + [ + 'sku' => 'simple2', + 'price' => 20, + 'qty' => 1, + 'price_type' => LinkInterface::PRICE_TYPE_FIXED, + ], + [ + 'sku' => 'simple3', + 'price' => 30, + 'qty' => 1, + 'price_type' => LinkInterface::PRICE_TYPE_FIXED, + ] + ] + ] + ]; + + return [ + [ + 'modifierName' => 'addSimpleProduct', + 'data' => [$optionsData] + ], + ]; + } + + /** + * Fixed bundle product with required option, custom option and without any discounts + * @param $selectionsPriceType + * @param $customOptionsPriceType + * @return array + */ + private function getBundleConfiguration3($selectionsPriceType, $customOptionsPriceType) + { + $optionsData = [ + [ + 'title' => 'Op1', + 'required' => true, + 'type' => 'checkbox', + 'links' => [ + [ + 'sku' => 'simple1', + 'qty' => 1, + 'price' => 20, + 'price_type' => $selectionsPriceType + ], + ] + ], + ]; + + $customOptionsData = [ + [ + 'price_type' => $customOptionsPriceType, + 'title' => 'Test Field', + 'type' => 'field', + 'is_require' => 1, + 'price' => 100, + 'sku' => '1-text', + ] + ]; + + return [ + [ + 'modifierName' => 'addSimpleProduct', + 'data' => [$optionsData] + ], + [ + 'modifierName' => 'addCustomOption', + 'data' => [$customOptionsData] + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/OptionListTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/OptionListTest.php index 55f8821d7087b..d14d5255eacad 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/OptionListTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/OptionListTest.php @@ -28,6 +28,7 @@ protected function setUp() /** * @magentoDataFixture Magento/Bundle/_files/product.php + * @magentoDbIsolation disabled */ public function testGetItems() { diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php new file mode 100644 index 0000000000000..3b3b1ed5cbd07 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Bundle\Model\Product; + +/** + * @magentoDbIsolation disabled + * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @group indexer_dimension + * @magentoDataFixture Magento/Bundle/_files/product_with_tier_pricing.php + */ +class PriceWithDimensionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Bundle\Model\Product\Price + */ + protected $_model; + + protected function setUp() + { + $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Bundle\Model\Product\Price::class + ); + } + + public function testGetTierPrice() + { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $product = $productRepository->get('bundle-product'); + // fixture + + // Note that this is really not the "tier price" but the "tier discount percentage" + // so it is expected to be increasing instead of decreasing + $this->assertEquals(8.0, $this->_model->getTierPrice(2, $product)); + $this->assertEquals(20.0, $this->_model->getTierPrice(3, $product)); + $this->assertEquals(20.0, $this->_model->getTierPrice(4, $product)); + $this->assertEquals(30.0, $this->_model->getTierPrice(5, $product)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_tier_pricing_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_tier_pricing_rollback.php new file mode 100644 index 0000000000000..130d08bae23c7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_tier_pricing_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/* + * Since the bundle product creation GUI doesn't allow to choose values for bundled products' custom options, + * bundled items should not contain products with required custom options. + * However, if to create such a bundle product, it will be always out of stock. + */ +require __DIR__ . '/../../../Magento/Catalog/_files/products_rollback.php'; + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $product = $productRepository->get('bundle-product'); + $productRepository->delete($product); +} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/Export/RowCustomizerTest.php b/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/Export/RowCustomizerTest.php index 53e8281ffbdf1..c26f5860f2375 100644 --- a/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/Export/RowCustomizerTest.php +++ b/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/Export/RowCustomizerTest.php @@ -33,6 +33,7 @@ protected function setUp() /** * @magentoDataFixture Magento/Bundle/_files/product.php + * @magentoDbIsolation disabled * * @return void */ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php new file mode 100644 index 0000000000000..40607cd85b3b4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Model\Indexer\Product\Price; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Api\ScopedProductTierPriceManagementInterface; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\Catalog\Pricing\Price\TierPrice; +use Magento\Customer\Model\Group; + +/** + * @group indexer_dimension + */ +class SimpleWithOptionsTierPriceWithDimensionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var CollectionFactory + */ + private $productCollectionFactory; + + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->productCollectionFactory = $this->objectManager->create(CollectionFactory::class); + } + + /** + * @magentoDbIsolation disabled + * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @magentoDataFixture Magento/Catalog/_files/category_product.php + */ + public function testTierPrice() + { + $tierPriceValue = 9.00; + + $tierPrice = $this->objectManager->create(ProductTierPriceInterfaceFactory::class) + ->create(); + $tierPrice->setCustomerGroupId(Group::CUST_GROUP_ALL); + $tierPrice->setQty(1.00); + $tierPrice->setValue($tierPriceValue); + $tierPriceManagement = $this->objectManager->create(ScopedProductTierPriceManagementInterface::class); + $tierPriceManagement->add('simple333', $tierPrice); + + $productCollection = $this->productCollectionFactory->create(); + $productCollection->addIdFilter(333); + $productCollection->addPriceData(); + $productCollection->load(); + /** @var \Magento\Catalog\Model\Product $product */ + $product = $productCollection->getFirstItem(); + $tierPrice = $product->getPriceInfo() + ->getPrice(TierPrice::PRICE_CODE) + ->getValue(); + + $this->assertEquals($tierPriceValue, $tierPrice); + + $tierPrice = $product->getTierPrice(1); + $this->assertEquals($tierPriceValue, $tierPrice); + + $tierPrices = $product->getData('tier_price'); + $this->assertEquals($tierPriceValue, $tierPrices[0]['price']); + + $minPrice = $product->getData('min_price'); + $this->assertEquals($tierPriceValue, $minPrice); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php new file mode 100644 index 0000000000000..12b7da2bd6e35 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php @@ -0,0 +1,156 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Model\Product\Type; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ProductRepository; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DataObject; +use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; +use Magento\Framework\Indexer\DimensionFactory; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * @magentoDbIsolation disabled + * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @group indexer_dimension + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + */ +class PriceWithDimensionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Catalog\Model\Product\Type\Price + */ + protected $_model; + + protected function setUp() + { + $this->_model = Bootstrap::getObjectManager()->create( + \Magento\Catalog\Model\Product\Type\Price::class + ); + } + + public function testGetPriceFromIndexer() + { + /** @var PriceTableResolver $tableResolver */ + $tableResolver = Bootstrap::getObjectManager()->create(PriceTableResolver::class); + + /** @var ResourceConnection $resourceConnection */ + $resourceConnection = Bootstrap::getObjectManager()->create(ResourceConnection::class); + + /** @var DimensionFactory $dimensionFactory */ + $dimensionFactory = Bootstrap::getObjectManager()->create(DimensionFactory::class); + $dimension = [ + $dimensionFactory->create(CustomerGroupDimensionProvider::DIMENSION_NAME, (string)0), + $dimensionFactory->create(WebsiteDimensionProvider::DIMENSION_NAME, (string)1) + ]; + $connection = $resourceConnection->getConnection(); + $priceTable = $connection->getTableName( + $tableResolver->resolve('catalog_product_index_price', $dimension) + ); + + $select = $connection->select()->from($priceTable)->where('entity_id = 1'); + + $return = $connection->fetchAll($select); + + $this->assertEquals('10', $return[0]['price']); + $this->assertEquals('10', $return[0]['final_price']); + $this->assertEquals('19', $return[0]['min_price']); + $this->assertEquals('19', $return[0]['max_price']); + } + + public function testGetPrice() + { + $this->assertEquals('test', $this->_model->getPrice(new DataObject(['price' => 'test']))); + } + + public function testGetFinalPrice() + { + $repository = Bootstrap::getObjectManager()->create( + ProductRepository::class + ); + $product = $repository->get('simple'); + // fixture + + // regular & tier prices + $this->assertEquals(10.0, $this->_model->getFinalPrice(1, $product)); + $this->assertEquals(8.0, $this->_model->getFinalPrice(2, $product)); + $this->assertEquals(5.0, $this->_model->getFinalPrice(5, $product)); + + // with options + $buyRequest = $this->prepareBuyRequest($product); + $product->getTypeInstance()->prepareForCart($buyRequest, $product); + + //product price + options price(10+1+2+3+3) + $this->assertEquals(19.0, $this->_model->getFinalPrice(1, $product)); + + //product tier price + options price(5+1+2+3+3) + $this->assertEquals(14.0, $this->_model->getFinalPrice(5, $product)); + } + + public function testGetFormatedPrice() + { + $repository = Bootstrap::getObjectManager()->create( + ProductRepository::class + ); + $product = $repository->get('simple'); + // fixture + $this->assertEquals('<span class="price">$10.00</span>', $this->_model->getFormatedPrice($product)); + } + + public function testCalculatePrice() + { + $this->assertEquals(10, $this->_model->calculatePrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01')); + $this->assertEquals(8, $this->_model->calculatePrice(10, 8, '1970-12-12 23:59:59', '2034-01-01 01:01:01')); + } + + public function testCalculateSpecialPrice() + { + $this->assertEquals( + 10, + $this->_model->calculateSpecialPrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01') + ); + $this->assertEquals( + 8, + $this->_model->calculateSpecialPrice(10, 8, '1970-12-12 23:59:59', '2034-01-01 01:01:01') + ); + } + + public function testIsTierPriceFixed() + { + $this->assertTrue($this->_model->isTierPriceFixed()); + } + + /** + * Build buy request based on product custom options + * + * @param Product $product + * @return DataObject + */ + private function prepareBuyRequest(Product $product) + { + $options = []; + /** @var $option \Magento\Catalog\Model\Product\Option */ + foreach ($product->getOptions() as $option) { + switch ($option->getGroupByType()) { + case \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_DATE: + $value = ['year' => 2013, 'month' => 8, 'day' => 9, 'hour' => 13, 'minute' => 35]; + break; + case \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_SELECT: + $value = key($option->getValues()); + break; + default: + $value = 'test'; + break; + } + $options[$option->getId()] = $value; + } + + return new DataObject(['qty' => 1, 'options' => $options]); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php new file mode 100644 index 0000000000000..f1b6eba653eff --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Model; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\CatalogInventory\Api\StockRegistryInterface; + +/** + * Tests product model: + * - pricing behaviour is tested + * @group indexer_dimension + * @magentoDbIsolation disabled + * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @see \Magento\Catalog\Model\ProductTest + * @see \Magento\Catalog\Model\ProductExternalTest + */ +class ProductPriceWithDimensionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Catalog\Model\Product + */ + protected $_model; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + protected function setUp() + { + $this->_model = Bootstrap::getObjectManager()->create(Product::class); + $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); + } + + public function testGetPrice() + { + $this->assertEmpty($this->_model->getPrice()); + $this->_model->setPrice(10.0); + $this->assertEquals(10.0, $this->_model->getPrice()); + } + + public function testGetPriceModel() + { + $default = $this->_model->getPriceModel(); + $this->assertInstanceOf(\Magento\Catalog\Model\Product\Type\Price::class, $default); + $this->assertSame($default, $this->_model->getPriceModel()); + } + + /** + * See detailed tests at \Magento\Catalog\Model\Product\Type*_PriceTest + */ + public function testGetTierPrice() + { + $this->assertEquals([], $this->_model->getTierPrice()); + } + + /** + * See detailed tests at \Magento\Catalog\Model\Product\Type*_PriceTest + */ + public function testGetTierPriceCount() + { + $this->assertEquals(0, $this->_model->getTierPriceCount()); + } + + /** + * See detailed tests at \Magento\Catalog\Model\Product\Type*_PriceTest + */ + public function testGetFormatedPrice() + { + $this->assertEquals('<span class="price">$0.00</span>', $this->_model->getFormatedPrice()); + } + + public function testSetGetFinalPrice() + { + $this->assertEquals(0, $this->_model->getFinalPrice()); + $this->_model->setPrice(10); + $this->_model->setFinalPrice(10); + $this->assertEquals(10, $this->_model->getFinalPrice()); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_with_options.php + * @return void + */ + public function testGetMinPrice(): void + { + $product = $this->productRepository->get('simple'); + $collection = Bootstrap::getObjectManager()->create(Collection::class); + $collection->addIdFilter($product->getId()); + $collection->addPriceData(); + $collection->load(); + /** @var \Magento\Catalog\Model\Product $product */ + $product = $collection->getFirstItem(); + $this->assertEquals(333, $product->getData('min_price')); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php + */ + public function testGetMinPriceForComposite() + { + $confProduct = $this->productRepository->get('configurable'); + $collection = Bootstrap::getObjectManager()->create(Collection::class); + $collection->addIdFilter($confProduct->getId()); + $collection->addPriceData(); + $collection->load(); + $product = $collection->getFirstItem(); + $this->assertEquals(10, $product->getData('min_price')); + + $childProduct = $this->productRepository->get('simple_10'); + $stockRegistry = Bootstrap::getObjectManager()->get(StockRegistryInterface::class); + $stockItem = $stockRegistry->getStockItem($childProduct->getId()); + $stockItem->setIsInStock(false); + $stockRegistry->updateStockItemBySku($childProduct->getSku(), $stockItem); + $collection->clear()->load(); + $product = $collection->getFirstItem(); + $this->assertEquals(20, $product->getData('min_price')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Quote/Item/QuantityValidatorTest.php b/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Quote/Item/QuantityValidatorTest.php index 873c5db9ab782..598370f4f4366 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Quote/Item/QuantityValidatorTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Quote/Item/QuantityValidatorTest.php @@ -83,7 +83,7 @@ protected function setUp() /** * @magentoDataFixture Magento/Checkout/_files/quote_with_bundle_product.php - * @magentoDbIsolation enabled + * @magentoDbIsolation disabled * @magentoAppIsolation enabled */ public function testQuoteWithOptions() @@ -108,7 +108,7 @@ public function testQuoteWithOptions() /** * @magentoDataFixture Magento/Checkout/_files/quote_with_bundle_product.php - * @magentoDbIsolation enabled + * @magentoDbIsolation disabled * @magentoAppIsolation enabled */ public function testQuoteWithOptionsWithErrors() diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php index 5ffaa789cf2eb..a7268e3e846c7 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php @@ -108,6 +108,7 @@ public function testConfigureActionWithSimpleProductAndCustomOption() * Test for \Magento\Checkout\Controller\Cart::configureAction() with bundle product * * @magentoDataFixture Magento/Checkout/_files/quote_with_bundle_product.php + * @magentoDbIsolation disabled */ public function testConfigureActionWithBundleProduct() { diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_bundle_product.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_bundle_product.php index ddc7d20631566..c88c87d74dfda 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_bundle_product.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_bundle_product.php @@ -35,6 +35,7 @@ /** @var $cart \Magento\Checkout\Model\Cart */ $cart = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Checkout\Model\Cart::class); $cart->addProduct($product, $requestInfo); +$cart->getQuote()->setReservedOrderId('test_cart_with_bundle'); $cart->save(); /** @var $objectManager \Magento\TestFramework\ObjectManager */ diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_bundle_product_rollback.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_bundle_product_rollback.php new file mode 100644 index 0000000000000..ed350e34b74dd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_bundle_product_rollback.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/* + * Since the bundle product creation GUI doesn't allow to choose values for bundled products' custom options, + * bundled items should not contain products with required custom options. + * However, if to create such a bundle product, it will be always out of stock. + */ +require __DIR__ . '/../../../Magento/Catalog/_files/products_rollback.php'; + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('bundle-product', false, null, true); + $productRepository->delete($product); +} catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //Product already removed +} + +/** @var $objectManager \Magento\TestFramework\ObjectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$quote = $objectManager->create(\Magento\Quote\Model\Quote::class); +$quote->load('test_cart_with_bundle', 'reserved_order_id'); +$quote->delete(); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php new file mode 100644 index 0000000000000..f08f0a4543ea3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php @@ -0,0 +1,107 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Pricing\Price; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Indexer\Product\Price\Processor; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * @magentoDbIsolation disabled + * @group indexer_dimension + * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + */ +class SpecialPriceIndexerWithDimensionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var CollectionFactory + */ + private $productCollectionFactory; + + /** + * @var Processor + */ + private $indexerProcessor; + + protected function setUp() + { + $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); + $this->productCollectionFactory = Bootstrap::getObjectManager()->get(CollectionFactory::class); + $this->indexerProcessor = Bootstrap::getObjectManager()->get(Processor::class); + } + + /** + * Use collection to check data in index + * Do not use magentoDbIsolation because index statement changing "tears" transaction (triggers creating) + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoDataFixture Magento/Catalog/_files/enable_price_index_schedule.php + */ + public function testFullReindexIfChildHasSpecialPrice() + { + $specialPrice = 2; + /** @var Product $childProduct */ + $childProduct = $this->productRepository->get('simple_10', true); + $childProduct->setData('special_price', $specialPrice); + $this->productRepository->save($childProduct); + + /** @var ProductCollection $collection */ + $collection = $this->productCollectionFactory->create(); + $collection + ->addPriceData() + ->addFieldToFilter(ProductInterface::SKU, 'configurable'); + + /** @var Product[] $items */ + $items = array_values($collection->getItems()); + self::assertEquals(10, $items[0]->getData('min_price')); + + $this->indexerProcessor->reindexAll(); + + /** @var ProductCollection $collection */ + $collection = $this->productCollectionFactory->create(); + $collection + ->addPriceData() + ->addFieldToFilter(ProductInterface::SKU, 'configurable'); + + /** @var Product $item */ + $item = $collection->getFirstItem(); + self::assertEquals($specialPrice, $item->getData('min_price')); + } + + /** + * Use collection to check data in index + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoDbIsolation disabled + */ + public function testOnSaveIndexationIfChildHasSpecialPrice() + { + $specialPrice = 2; + /** @var Product $childProduct */ + $childProduct = $this->productRepository->get('simple_10', true); + $childProduct->setData('special_price', $specialPrice); + $this->productRepository->save($childProduct); + + /** @var ProductCollection $collection */ + $collection = $this->productCollectionFactory->create(); + $collection + ->addPriceData() + ->addFieldToFilter(ProductInterface::SKU, 'configurable'); + + /** @var Product $item */ + $item = $collection->getFirstItem(); + self::assertEquals($specialPrice, $item->getData('min_price')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php new file mode 100644 index 0000000000000..bddbb38e9f019 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php @@ -0,0 +1,149 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Pricing\Render\FinalPriceBox; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Pricing\Price\FinalPrice; +use Magento\Catalog\Pricing\Render\FinalPriceBox; +use Magento\Framework\Pricing\Render\Amount; +use Magento\Framework\Pricing\Render\RendererPool; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * @magentoDbIsolation disabled + * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @group indexer_dimension + * Test price rendering according to is_product_list flag for Configurable product + */ +class RenderingBasedOnIsProductListFlagWithDimensionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ProductInterface + */ + private $product; + + /** + * @var FinalPrice + */ + private $finalPrice; + + /** + * @var RendererPool + */ + private $rendererPool; + + /** + * @var FinalPriceBox + */ + private $finalPriceBox; + + protected function setUp() + { + $productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); + $this->product = $productRepository->get('configurable'); + $this->finalPrice = Bootstrap::getObjectManager()->create(FinalPrice::class, [ + 'saleableItem' => $this->product, + 'quantity' => null + ]); + $this->rendererPool = Bootstrap::getObjectManager()->create(RendererPool::class); + $this->rendererPool->setData( + [ + 'default' => + [ + 'default_amount_render_class' => Amount::class, + 'default_amount_render_template' => 'Magento_Catalog::product/price/amount/default.phtml', + ], + ] + ); + $this->finalPriceBox = Bootstrap::getObjectManager()->create(FinalPriceBox::class, [ + 'saleableItem' => $this->product, + 'price' => $this->finalPrice, + 'rendererPool' => $this->rendererPool, + ]); + $this->finalPriceBox->setTemplate('Magento_ConfigurableProduct::product/price/final_price.phtml'); + + /** @var Product $childProduct */ + $childProduct = $productRepository->get('simple_10', true); + $childProduct->setData('special_price', 5.99); + $productRepository->save($childProduct); + } + + /** + * Test when is_product_list flag is not specified. Regular and Special price should be rendered + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoAppArea frontend + * @magentoDbIsolation disabled + */ + public function testRenderingByDefault() + { + $html = $this->finalPriceBox->toHtml(); + self::assertContains('5.99', $html); + $this->assertGreaterThanOrEqual( + 1, + \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath( + '//*[contains(@class,"normal-price")]', + $html + ) + ); + $this->assertGreaterThanOrEqual( + 1, + \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath( + '//*[contains(@class,"old-price")]', + $html + ) + ); + } + + /** + * Test when is_product_list flag is specified + * + * Special price should be valid + * FinalPriceBox::hasSpecialPrice should not be call + * Regular price for Configurable product should be rendered for is_product_list = false (product page), but not be + * for for is_product_list = true (list of products) + * + * @param bool $flag + * @param int|bool $count + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoAppArea frontend + * @dataProvider isProductListDataProvider + * @magentoDbIsolation disabled + */ + public function testRenderingAccordingToIsProductListFlag($flag, $count) + { + $this->finalPriceBox->setData('is_product_list', $flag); + $html = $this->finalPriceBox->toHtml(); + self::assertContains('5.99', $html); + $this->assertEquals( + 1, + \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath( + '//*[contains(@class,"normal-price")]', + $html + ) + ); + $this->assertEquals( + $count, + \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath( + '//*[contains(@class,"old-price")]', + $html + ) + ); + } + + /** + * @return array + */ + public function isProductListDataProvider() + { + return [ + 'is_not_product_list' => [false, 1], + 'is_product_list' => [true, 0], + ]; + } +} From 0e5d0e66ba4787d000f1e215614595709a32b79c Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Tue, 7 Aug 2018 09:25:33 +0300 Subject: [PATCH 0764/1171] MAGETWO-93997: [Forwardport] Create commands to set/show dimension modes for indexers --- .../PriceIndexerDimensionsModeSetCommand.php | 198 ---------------- .../Price/DimensionModeConfiguration.php | 15 +- .../Indexer/Product/Price/ModeSwitcher.php | 61 ++++- .../Price/ModeSwitcherConfiguration.php | 70 ++++++ ...stallDefaultPriceIndexerDimensionsMode.php | 79 ------- app/code/Magento/Catalog/etc/config.xml | 5 + app/code/Magento/Catalog/etc/di.xml | 15 +- .../IndexerSetDimensionsModeCommand.php | 203 ++++++++++++++++ .../IndexerShowDimensionsModeCommand.php | 152 ++++++++++++ .../Magento/Indexer/Model/DimensionMode.php | 56 +++++ .../Magento/Indexer/Model/DimensionModes.php | 43 ++++ .../Indexer/Model/ModeSwitcherInterface.php | 32 +++ .../IndexerSetDimensionsModeCommandTest.php | 222 ++++++++++++++++++ .../IndexerShowDimensionsModeCommandTest.php | 144 ++++++++++++ app/code/Magento/Indexer/etc/di.xml | 2 + .../Annotation/IndexerDimensionMode.php | 24 +- ...iceIndexerDimensionsModeSetCommandTest.php | 33 +-- 17 files changed, 1028 insertions(+), 326 deletions(-) delete mode 100644 app/code/Magento/Catalog/Console/Command/PriceIndexerDimensionsModeSetCommand.php create mode 100644 app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcherConfiguration.php delete mode 100644 app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultPriceIndexerDimensionsMode.php create mode 100644 app/code/Magento/Indexer/Console/Command/IndexerSetDimensionsModeCommand.php create mode 100644 app/code/Magento/Indexer/Console/Command/IndexerShowDimensionsModeCommand.php create mode 100644 app/code/Magento/Indexer/Model/DimensionMode.php create mode 100644 app/code/Magento/Indexer/Model/DimensionModes.php create mode 100644 app/code/Magento/Indexer/Model/ModeSwitcherInterface.php create mode 100644 app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerSetDimensionsModeCommandTest.php create mode 100644 app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowDimensionsModeCommandTest.php diff --git a/app/code/Magento/Catalog/Console/Command/PriceIndexerDimensionsModeSetCommand.php b/app/code/Magento/Catalog/Console/Command/PriceIndexerDimensionsModeSetCommand.php deleted file mode 100644 index e331f4d2a2fc4..0000000000000 --- a/app/code/Magento/Catalog/Console/Command/PriceIndexerDimensionsModeSetCommand.php +++ /dev/null @@ -1,198 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Catalog\Console\Command; - -use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Input\InputArgument; -use Magento\Framework\Exception\LocalizedException; -use Magento\Indexer\Console\Command\AbstractIndexerCommand; -use Magento\Framework\App\ObjectManagerFactory; -use Magento\Catalog\Model\Indexer\Product\Price\ModeSwitcher; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\Config\ConfigResource\ConfigInterface; -use Magento\Framework\App\Cache\TypeListInterface; - -/** - * Command to change price indexer dimensions mode - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class PriceIndexerDimensionsModeSetCommand extends AbstractIndexerCommand -{ - const INPUT_KEY_MODE = 'mode'; - - /** - * ScopeConfigInterface - * - * @var ScopeConfigInterface - */ - private $configReader; - - /** - * ConfigInterface - * - * @var ConfigInterface - */ - private $configWriter; - - /** - * TypeListInterface - * - * @var TypeListInterface - */ - private $cacheTypeList; - - /** - * ModeSwitcher - * - * @var ModeSwitcher - */ - private $modeSwitcher; - - /** - * @param ObjectManagerFactory $objectManagerFactory - * @param ScopeConfigInterface $configReader - * @param ConfigInterface $configWriter - * @param TypeListInterface $cacheTypeList - * @param ModeSwitcher $modeSwitcher - */ - public function __construct( - ObjectManagerFactory $objectManagerFactory, - ScopeConfigInterface $configReader, - ConfigInterface $configWriter, - TypeListInterface $cacheTypeList, - ModeSwitcher $modeSwitcher - ) { - $this->configReader = $configReader; - $this->configWriter = $configWriter; - $this->cacheTypeList = $cacheTypeList; - $this->modeSwitcher = $modeSwitcher; - parent::__construct($objectManagerFactory); - } - - /** - * {@inheritdoc} - */ - protected function configure() - { - $this->setName('indexer:set-dimensions-mode:catalog_product_price') - ->setDescription('Set Indexer Dimensions Mode') - ->setDefinition($this->getInputList()); - - parent::configure(); - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $errors = $this->validate($input); - - if ($errors) { - throw new \InvalidArgumentException(implode(PHP_EOL, $errors)); - } - - $returnValue = \Magento\Framework\Console\Cli::RETURN_SUCCESS; - - $indexer = $this->getObjectManager()->get(\Magento\Indexer\Model\Indexer::class); - $indexer->load(\Magento\Catalog\Model\Indexer\Product\Price\Processor::INDEXER_ID); - - try { - $currentMode = $input->getArgument(self::INPUT_KEY_MODE); - $previousMode = $this->configReader->getValue(ModeSwitcher::XML_PATH_PRICE_DIMENSIONS_MODE) ?: - DimensionModeConfiguration::DIMENSION_NONE; - - if ($previousMode !== $currentMode) { - //Create new tables and move data - $this->modeSwitcher->createTables($currentMode); - $this->modeSwitcher->moveData($currentMode, $previousMode); - - //Change config options - $this->configWriter->saveConfig(ModeSwitcher::XML_PATH_PRICE_DIMENSIONS_MODE, $currentMode); - $this->cacheTypeList->cleanType('config'); - $indexer->invalidate(); - - //Delete old tables - $this->modeSwitcher->dropTables($previousMode); - - $output->writeln( - 'Dimensions mode for indexer ' . $indexer->getTitle() . ' was changed from \'' - . $previousMode . '\' to \'' . $currentMode . '\'' - ); - } else { - $output->writeln('Dimensions mode for indexer ' . $indexer->getTitle() . ' has not been changed'); - } - } catch (LocalizedException $e) { - $output->writeln($e->getMessage() . PHP_EOL); - // we must have an exit code higher than zero to indicate something was wrong - $returnValue = \Magento\Framework\Console\Cli::RETURN_FAILURE; - } catch (\Exception $e) { - $output->writeln($indexer->getTitle() . " indexer process unknown error:" . PHP_EOL); - $output->writeln($e->getMessage() . PHP_EOL); - // we must have an exit code higher than zero to indicate something was wrong - $returnValue = \Magento\Framework\Console\Cli::RETURN_FAILURE; - } - - return $returnValue; - } - - /** - * Get list of arguments for the command - * - * @return InputOption[] - */ - public function getInputList(): array - { - $modeOptions[] = new InputArgument( - self::INPUT_KEY_MODE, - InputArgument::REQUIRED, - 'Indexer dimensions mode ['. DimensionModeConfiguration::DIMENSION_NONE - . '|' . DimensionModeConfiguration::DIMENSION_WEBSITE - . '|' . DimensionModeConfiguration::DIMENSION_CUSTOMER_GROUP - . '|' . DimensionModeConfiguration::DIMENSION_WEBSITE_AND_CUSTOMER_GROUP .']' - ); - return $modeOptions; - } - - /** - * Check if all admin options are provided - * - * @param InputInterface $input - * @return string[] - */ - public function validate(InputInterface $input): array - { - $errors = []; - - $acceptedModeValues = ' Accepted values for ' . self::INPUT_KEY_MODE . ' are \'' - . DimensionModeConfiguration::DIMENSION_NONE . '\', \'' - . DimensionModeConfiguration::DIMENSION_WEBSITE . '\', \'' - . DimensionModeConfiguration::DIMENSION_CUSTOMER_GROUP . '\', \'' - . DimensionModeConfiguration::DIMENSION_WEBSITE_AND_CUSTOMER_GROUP . '\''; - - $inputMode = $input->getArgument(self::INPUT_KEY_MODE); - if (!$inputMode) { - $errors[] = 'Missing argument \'' . self::INPUT_KEY_MODE .'\'.' . $acceptedModeValues; - } elseif (!in_array( - $inputMode, - [ - DimensionModeConfiguration::DIMENSION_NONE, - DimensionModeConfiguration::DIMENSION_WEBSITE, - DimensionModeConfiguration::DIMENSION_CUSTOMER_GROUP, - DimensionModeConfiguration::DIMENSION_WEBSITE_AND_CUSTOMER_GROUP - ] - )) { - $errors[] = $acceptedModeValues; - } - return $errors; - } -} diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionModeConfiguration.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionModeConfiguration.php index 9b8eb55b7aac5..7a4d8e313462d 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionModeConfiguration.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/DimensionModeConfiguration.php @@ -4,7 +4,6 @@ * See COPYING.txt for license details. */ declare(strict_types=1); - namespace Magento\Catalog\Model\Indexer\Product\Price; use Magento\Framework\App\Config\ScopeConfigInterface; @@ -41,6 +40,7 @@ class DimensionModeConfiguration CustomerGroupDimensionProvider::DIMENSION_NAME ], ]; + /** * @var ScopeConfigInterface */ @@ -59,12 +59,23 @@ public function __construct(ScopeConfigInterface $scopeConfig) $this->scopeConfig = $scopeConfig; } + /** + * Return dimension modes configuration. + * + * @return array + */ + public function getDimensionModes(): array + { + return $this->modesMapping; + } + /** * Get names of dimensions which used for provided mode. * By default return dimensions for current enabled mode * * @param string|null $mode * @return string[] + * @throws \InvalidArgumentException */ public function getDimensionConfiguration(string $mode = null): array { @@ -82,7 +93,7 @@ public function getDimensionConfiguration(string $mode = null): array private function getCurrentMode(): string { if (null === $this->currentMode) { - $this->currentMode = $this->scopeConfig->getValue(ModeSwitcher::XML_PATH_PRICE_DIMENSIONS_MODE) + $this->currentMode = $this->scopeConfig->getValue(ModeSwitcherConfiguration::XML_PATH_PRICE_DIMENSIONS_MODE) ?: self::DIMENSION_NONE; } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php index 81a170fc0a3d7..e71031489fa0e 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php @@ -10,14 +10,14 @@ use Magento\Framework\Search\Request\Dimension; use Magento\Store\Model\Indexer\WebsiteDimensionProvider; use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Indexer\Model\DimensionModes; +use Magento\Indexer\Model\DimensionMode; /** * Class to prepare new tables for new indexer mode */ -class ModeSwitcher +class ModeSwitcher implements \Magento\Indexer\Model\ModeSwitcherInterface { - const XML_PATH_PRICE_DIMENSIONS_MODE = 'indexer/catalog_product_price/dimensions_mode'; - /** * TableMaintainer * @@ -38,15 +38,60 @@ class ModeSwitcher private $dimensionsArray; /** - * @param \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer $tableMaintainer - * @param \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory $dimensionCollectionFactory + * @var \Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration + */ + private $dimensionModeConfiguration; + + /** + * @var ModeSwitcherConfiguration + */ + private $modeSwitcherConfiguration; + + /** + * @param TableMaintainer $tableMaintainer + * @param DimensionCollectionFactory $dimensionCollectionFactory + * @param DimensionModeConfiguration $dimensionModeConfiguration + * @param ModeSwitcherConfiguration $modeSwitcherConfiguration */ public function __construct( - \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer $tableMaintainer, - \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory $dimensionCollectionFactory + TableMaintainer $tableMaintainer, + DimensionCollectionFactory $dimensionCollectionFactory, + DimensionModeConfiguration $dimensionModeConfiguration, + ModeSwitcherConfiguration $modeSwitcherConfiguration ) { $this->tableMaintainer = $tableMaintainer; $this->dimensionCollectionFactory = $dimensionCollectionFactory; + $this->dimensionModeConfiguration = $dimensionModeConfiguration; + $this->modeSwitcherConfiguration = $modeSwitcherConfiguration; + } + + /** + * @inheritdoc + */ + public function getDimensionModes(): DimensionModes + { + $dimensionsList = []; + foreach ($this->dimensionModeConfiguration->getDimensionModes() as $dimension => $modes) { + $dimensionsList[] = new DimensionMode($dimension, $modes); + } + + return new DimensionModes($dimensionsList); + } + + /** + * @inheritdoc + */ + public function switchMode(string $currentMode, string $previousMode) + { + //Create new tables and move data + $this->createTables($currentMode); + $this->moveData($currentMode, $previousMode); + + //Change config options + $this->modeSwitcherConfiguration->saveMode($currentMode); + + //Delete old tables + $this->dropTables($previousMode); } /** @@ -120,7 +165,7 @@ public function dropTables(string $previousMode) * * @param string $mode * - * @return array + * @return \Magento\Framework\Indexer\MultiDimensionProvider */ private function getDimensionsArray(string $mode): \Magento\Framework\Indexer\MultiDimensionProvider { diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcherConfiguration.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcherConfiguration.php new file mode 100644 index 0000000000000..66b7147a8db76 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcherConfiguration.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Indexer\Product\Price; + +use Magento\Framework\App\Config\ConfigResource\ConfigInterface; +use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Indexer\Model\Indexer; + +/** + * Class to configure indexers and system config after modes has been switched + */ +class ModeSwitcherConfiguration +{ + const XML_PATH_PRICE_DIMENSIONS_MODE = 'indexer/catalog_product_price/dimensions_mode'; + + /** + * ConfigInterface + * + * @var ConfigInterface + */ + private $configWriter; + + /** + * TypeListInterface + * + * @var TypeListInterface + */ + private $cacheTypeList; + + /** + * @var Indexer $indexer + */ + private $indexer; + + /** + * @param ConfigInterface $configWriter + * @param TypeListInterface $cacheTypeList + * @param Indexer $indexer + */ + public function __construct( + ConfigInterface $configWriter, + TypeListInterface $cacheTypeList, + Indexer $indexer + ) { + $this->configWriter = $configWriter; + $this->cacheTypeList = $cacheTypeList; + $this->indexer = $indexer; + } + + /** + * Save switcher mode and invalidate reindex. + * + * @param string $mode + * @return void + * @throws \InvalidArgumentException + */ + public function saveMode(string $mode) + { + //Change config options + $this->configWriter->saveConfig(self::XML_PATH_PRICE_DIMENSIONS_MODE, $mode); + $this->cacheTypeList->cleanType('config'); + $this->indexer->load(\Magento\Catalog\Model\Indexer\Product\Price\Processor::INDEXER_ID); + $this->indexer->invalidate(); + } +} diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultPriceIndexerDimensionsMode.php b/app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultPriceIndexerDimensionsMode.php deleted file mode 100644 index 9cb417383255e..0000000000000 --- a/app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultPriceIndexerDimensionsMode.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Catalog\Setup\Patch\Data; - -use Magento\Catalog\Model\Indexer\Product\Price\ModeSwitcher; -use Magento\Framework\Setup\ModuleDataSetupInterface; -use Magento\Framework\Setup\Patch\DataPatchInterface; -use Magento\Framework\Setup\Patch\PatchVersionInterface; -use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; - -/** - * Class InstallDefaultCategories data patch. - * - * @package Magento\Catalog\Setup\Patch - */ -class InstallDefaultPriceIndexerDimensionsMode implements DataPatchInterface, PatchVersionInterface -{ - /** - * @var ModuleDataSetupInterface - */ - private $moduleDataSetup; - - /** - * PatchInitial constructor. - * @param ModuleDataSetupInterface $moduleDataSetup - */ - public function __construct( - ModuleDataSetupInterface $moduleDataSetup - ) { - $this->moduleDataSetup = $moduleDataSetup; - } - - /** - * {@inheritdoc} - */ - public function apply() - { - $configTable = $this->moduleDataSetup->getTable('core_config_data'); - - $this->moduleDataSetup->getConnection()->insert( - $configTable, - [ - 'scope' => 'default', - 'scope_id' => 0, - 'value' => DimensionModeConfiguration::DIMENSION_NONE, - 'path' => ModeSwitcher::XML_PATH_PRICE_DIMENSIONS_MODE - ] - ); - } - - /** - * {@inheritdoc} - */ - public static function getDependencies() - { - return []; - } - - /** - * {@inheritdoc} - */ - public function getAliases() - { - return []; - } - - /** - * {@inheritdoc} - */ - public static function getVersion() - { - return '2.2.6'; - } -} diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index 1d92197e390a1..f52760aa50743 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -52,6 +52,11 @@ <forbidden_extensions>php,exe</forbidden_extensions> </custom_options> </catalog> + <indexer> + <catalog_product_price> + <dimensions_mode>none</dimensions_mode> + </catalog_product_price> + </indexer> <system> <media_storage_configuration> <allowed_resources> diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 44564da388fc9..86dcbee196a87 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -563,7 +563,6 @@ <arguments> <argument name="commands" xsi:type="array"> <item name="productAttributesCleanUp" xsi:type="object">Magento\Catalog\Console\Command\ProductAttributesCleanUp</item> - <item name="setPriceDimensionsMode" xsi:type="object">Magento\Catalog\Console\Command\PriceIndexerDimensionsModeSetCommand</item> </argument> </arguments> </type> @@ -1142,4 +1141,18 @@ <argument name="productType" xsi:type="string">virtual</argument> </arguments> </virtualType> + <type name="Magento\Indexer\Console\Command\IndexerSetDimensionsModeCommand"> + <arguments> + <argument name="dimensionSwitchers" xsi:type="array"> + <item name="catalog_product_price" xsi:type="object">Magento\Catalog\Model\Indexer\Product\Price\ModeSwitcher</item> + </argument> + </arguments> + </type> + <type name="Magento\Indexer\Console\Command\IndexerShowDimensionsModeCommand"> + <arguments> + <argument name="indexers" xsi:type="array"> + <item name="catalog_product_price" xsi:type="string">catalog_product_price</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Indexer/Console/Command/IndexerSetDimensionsModeCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerSetDimensionsModeCommand.php new file mode 100644 index 0000000000000..30f57d09f508b --- /dev/null +++ b/app/code/Magento/Indexer/Console/Command/IndexerSetDimensionsModeCommand.php @@ -0,0 +1,203 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Indexer\Console\Command; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Magento\Framework\App\ObjectManagerFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Console\Cli; +use Magento\Indexer\Model\ModeSwitcherInterface; + +/** + * Command to set indexer dimensions mode + */ +class IndexerSetDimensionsModeCommand extends AbstractIndexerCommand +{ + const INPUT_KEY_MODE = 'mode'; + const INPUT_KEY_INDEXER = 'indexer'; + const DIMENSION_MODE_NONE = 'none'; + const XML_PATH_DIMENSIONS_MODE_MASK = 'indexer/%s/dimensions_mode'; + + /** + * @var string + */ + private $commandName = 'indexer:set-dimensions-mode'; + + /** + * ScopeConfigInterface + * + * @var ScopeConfigInterface + */ + private $configReader; + + /** + * @var ModeSwitcherInterface[] + */ + private $dimensionProviders; + + /** + * @param ObjectManagerFactory $objectManagerFactory + * @param ScopeConfigInterface $configReader + * @param ModeSwitcherInterface[] $dimensionSwitchers + */ + public function __construct( + ObjectManagerFactory $objectManagerFactory, + ScopeConfigInterface $configReader, + array $dimensionSwitchers + ) { + $this->configReader = $configReader; + $this->dimensionProviders = $dimensionSwitchers; + parent::__construct($objectManagerFactory); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->setName($this->commandName) + ->setDescription('Set Indexer Dimensions Mode') + ->setDefinition($this->getInputList()); + parent::configure(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $errors = $this->validate($input); + if ($errors) { + throw new \InvalidArgumentException(implode(PHP_EOL, $errors)); + } + $returnValue = Cli::RETURN_SUCCESS; + /** @var \Magento\Indexer\Model\Indexer $indexer */ + $indexer = $this->getObjectManager()->get(\Magento\Indexer\Model\Indexer::class); + try { + $selectedIndexer = (string)$input->getArgument(self::INPUT_KEY_INDEXER); + if (!$selectedIndexer) { + $this->showAvailableModes($output); + } else { + $indexer->load($selectedIndexer); + $currentMode = $input->getArgument(self::INPUT_KEY_MODE); + $configPath = sprintf(self::XML_PATH_DIMENSIONS_MODE_MASK, $selectedIndexer); + $previousMode = $this->configReader->getValue($configPath) ?: self::DIMENSION_MODE_NONE; + if ($previousMode !== $currentMode) { + /** @var ModeSwitcherInterface $modeSwitcher */ + $modeSwitcher = $this->dimensionProviders[$selectedIndexer]; + // Switch dimensions mode + $modeSwitcher->switchMode($currentMode, $previousMode); + $output->writeln( + 'Dimensions mode for indexer "' . $indexer->getTitle() . '" was changed from \'' + . $previousMode . '\' to \'' . $currentMode . '\'' + ); + } else { + $output->writeln('Dimensions mode for indexer "' . $indexer->getTitle() . '" has not been changed'); + } + } + } catch (\Exception $e) { + $output->writeln('"' . $indexer->getTitle() . '" indexer process unknown error:' . PHP_EOL); + $output->writeln($e->getMessage() . PHP_EOL); + // we must have an exit code higher than zero to indicate something was wrong + $returnValue = Cli::RETURN_FAILURE; + } + + return $returnValue; + } + + /** + * Display all available indexers and modes + * + * @param OutputInterface $output + * @return void + */ + private function showAvailableModes(OutputInterface $output) + { + $output->writeln(sprintf('%-50s', 'Indexer') . 'Available modes'); + foreach ($this->dimensionProviders as $indexer => $provider) { + $availableModes = implode(',', array_keys($provider->getDimensionModes()->getDimensions())); + $output->writeln(sprintf('%-50s', $indexer) . $availableModes); + } + } + + /** + * Get list of arguments for the command + * + * @return InputArgument[] + */ + private function getInputList(): array + { + $dimensionProvidersList = array_keys($this->dimensionProviders); + $indexerOptionDescription = 'Indexer name [' . implode('|', $dimensionProvidersList) . ']'; + $arguments[] = new InputArgument( + self::INPUT_KEY_INDEXER, + InputArgument::OPTIONAL, + $indexerOptionDescription + ); + $modeOptionDescription = 'Indexer dimension modes' . PHP_EOL; + foreach ($this->dimensionProviders as $indexer => $provider) { + $availableModes = implode(',', array_keys($provider->getDimensionModes()->getDimensions())); + $modeOptionDescription .= sprintf('%-30s ', $indexer) . $availableModes . PHP_EOL; + } + $arguments[] = new InputArgument( + self::INPUT_KEY_MODE, + InputArgument::OPTIONAL, + $modeOptionDescription + ); + + return $arguments; + } + + /** + * Check if all arguments are provided + * + * @param InputInterface $input + * @return string[] + */ + private function validate(InputInterface $input): array + { + $errors = []; + $inputIndexer = (string)$input->getArgument(self::INPUT_KEY_INDEXER); + if ($inputIndexer) { + $acceptedValues = array_keys($this->dimensionProviders); + $errors = $this->validateArgument(self::INPUT_KEY_INDEXER, $inputIndexer, $acceptedValues); + if (!$errors) { + $inputIndexerDimensionMode = (string)$input->getArgument(self::INPUT_KEY_MODE); + /** @var ModeSwitcherInterface $modeSwitcher */ + $modeSwitcher = $this->dimensionProviders[$inputIndexer]; + $acceptedValues = array_keys($modeSwitcher->getDimensionModes()->getDimensions()); + $errors = $this->validateArgument(self::INPUT_KEY_MODE, $inputIndexerDimensionMode, $acceptedValues); + } + } + + return $errors; + } + + /** + * Validate command argument and return errors in case if argument is invalid + * + * @param string $inputKey + * @param string $inputIndexer + * @param array $acceptedValues + * @return string[] + */ + private function validateArgument(string $inputKey, string $inputIndexer, array $acceptedValues): array + { + $errors = []; + $acceptedIndexerValues = ' Accepted values for "<' . $inputKey . '>" are \'' . + implode(',', $acceptedValues) . '\''; + if (!$inputIndexer) { + $errors[] = 'Missing argument "<' . $inputKey . '>".' . $acceptedIndexerValues; + } elseif (!\in_array($inputIndexer, $acceptedValues)) { + $errors[] = 'Invalid value for "<' . $inputKey . '>" argument.' . $acceptedIndexerValues; + } + + return $errors; + } +} diff --git a/app/code/Magento/Indexer/Console/Command/IndexerShowDimensionsModeCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerShowDimensionsModeCommand.php new file mode 100644 index 0000000000000..f5553c3fb3546 --- /dev/null +++ b/app/code/Magento/Indexer/Console/Command/IndexerShowDimensionsModeCommand.php @@ -0,0 +1,152 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Indexer\Console\Command; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Magento\Framework\App\ObjectManagerFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Console\Cli; +use Symfony\Component\Console\Input\InputArgument; + +/** + * Command to show indexers dimension modes + */ +class IndexerShowDimensionsModeCommand extends AbstractIndexerCommand +{ + const INPUT_KEY_INDEXER = 'indexer'; + const DIMENSION_MODE_NONE = 'none'; + const XML_PATH_DIMENSIONS_MODE_MASK = 'indexer/%s/dimensions_mode'; + /** + * @var string + */ + private $commandName = 'indexer:show-dimensions-mode'; + /** + * ScopeConfigInterface + * + * @var ScopeConfigInterface + */ + private $configReader; + /** + * @var string[] + */ + private $indexers; + + /** + * @param ObjectManagerFactory $objectManagerFactory + * @param ScopeConfigInterface $configReader + * @param array $indexers + */ + public function __construct( + ObjectManagerFactory $objectManagerFactory, + ScopeConfigInterface $configReader, + array $indexers + ) { + $this->configReader = $configReader; + $this->indexers = $indexers; + parent::__construct($objectManagerFactory); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->setName($this->commandName) + ->setDescription('Shows Indexer Dimension Mode') + ->setDefinition($this->getInputList()); + parent::configure(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $errors = $this->validate($input); + if ($errors) { + throw new \InvalidArgumentException(implode(PHP_EOL, $errors)); + } + $returnValue = Cli::RETURN_SUCCESS; + /** @var \Magento\Indexer\Model\Indexer $indexer */ + $indexer = $this->getObjectManager()->get(\Magento\Indexer\Model\Indexer::class); + try { + $selectedIndexers = $input->getArgument(self::INPUT_KEY_INDEXER); + if ($selectedIndexers) { + $indexersList = (array)$selectedIndexers; + } else { + $indexersList = $this->indexers; + } + foreach ($indexersList as $indexerId) { + $indexer->load($indexerId); + $configPath = sprintf(self::XML_PATH_DIMENSIONS_MODE_MASK, $indexerId); + $mode = $this->configReader->getValue($configPath) ?: self::DIMENSION_MODE_NONE; + $output->writeln(sprintf('%-50s ', $indexer->getTitle() . ':') . $mode); + } + } catch (\Exception $e) { + $output->writeln('"' . $indexer->getTitle() . '" indexer process unknown error:' . PHP_EOL); + $output->writeln($e->getMessage() . PHP_EOL); + // we must have an exit code higher than zero to indicate something was wrong + $returnValue = Cli::RETURN_FAILURE; + } + + return $returnValue; + } + + /** + * Get list of arguments for the command + * + * @return InputArgument[] + */ + private function getInputList(): array + { + $optionDescription = 'Space-separated list of index types or omit to apply to all indexes'; + $arguments[] = new InputArgument( + self::INPUT_KEY_INDEXER, + InputArgument::OPTIONAL | InputArgument::IS_ARRAY, + $optionDescription . ' (' . implode($this->indexers) . ')' + ); + + return $arguments; + } + + /** + * Check if all arguments are provided + * + * @param InputInterface $input + * @return string[] + */ + private function validate(InputInterface $input): array + { + $inputIndexer = (array)$input->getArgument(self::INPUT_KEY_INDEXER); + $acceptedValues = array_keys($this->indexers); + $errors = $this->validateArgument(self::INPUT_KEY_INDEXER, $inputIndexer, $acceptedValues); + + return $errors; + } + + /** + * Validate command argument and return errors in case if argument is invalid + * + * @param string $inputKey + * @param array $inputIndexer + * @param array $acceptedValues + * @return array + */ + private function validateArgument(string $inputKey, array $inputIndexer, array $acceptedValues): array + { + $errors = []; + $acceptedIndexerValues = ' Accepted values for "<' . $inputKey . '>" are \'' . + implode(',', $acceptedValues) . '\''; + if (!empty($inputIndexer) && !\array_intersect($inputIndexer, $acceptedValues)) { + $errors[] = 'Invalid value for "<' . $inputKey . '>" argument.' . $acceptedIndexerValues; + } + + return $errors; + } +} diff --git a/app/code/Magento/Indexer/Model/DimensionMode.php b/app/code/Magento/Indexer/Model/DimensionMode.php new file mode 100644 index 0000000000000..74fb85e5420ff --- /dev/null +++ b/app/code/Magento/Indexer/Model/DimensionMode.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Indexer\Model; + +/** + * DTO to work with dimension mode + */ +class DimensionMode +{ + /** + * @var array + */ + private $name; + + /** + * @var array + */ + private $dimensions; + + /** + * @param string $name + * @param array $dimensions + */ + public function __construct(string $name, array $dimensions) + { + $this->dimensions = (function (string ...$dimensions) { + return $dimensions; + })(...$dimensions); + $this->name = $name; + } + + /** + * Returns dimension name + * + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * Returns dimension modes + * + * @return string[] + */ + public function getDimensions(): array + { + return $this->dimensions; + } +} diff --git a/app/code/Magento/Indexer/Model/DimensionModes.php b/app/code/Magento/Indexer/Model/DimensionModes.php new file mode 100644 index 0000000000000..bbdee0ced8662 --- /dev/null +++ b/app/code/Magento/Indexer/Model/DimensionModes.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Indexer\Model; + +/** + * DTO to work with dimension modes + */ +class DimensionModes +{ + /** + * @var DimensionMode[] + */ + private $dimensions; + + /** + * @param DimensionMode[] $dimensions + */ + public function __construct(array $dimensions) + { + $this->dimensions = (function (DimensionMode ...$dimensions) { + $result = []; + foreach ($dimensions as $dimension) { + $result[$dimension->getName()] = $dimension; + }; + return $result; + })(...$dimensions); + } + + /** + * Returns dimensions and their modes + * + * @return array + */ + public function getDimensions(): array + { + return $this->dimensions; + } +} diff --git a/app/code/Magento/Indexer/Model/ModeSwitcherInterface.php b/app/code/Magento/Indexer/Model/ModeSwitcherInterface.php new file mode 100644 index 0000000000000..8984b05365fb9 --- /dev/null +++ b/app/code/Magento/Indexer/Model/ModeSwitcherInterface.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Indexer\Model; + +/** + * Interface to switch indexer mode + */ +interface ModeSwitcherInterface +{ + /** + * Returns data object that contains dimension modes + * + * @return DimensionModes + */ + public function getDimensionModes(): DimensionModes; + + /** + * Switch dimension mode + * + * @param string $currentMode + * @param string $previousMode + * @throws \InvalidArgumentException + * @throws \Zend_Db_Exception + * @return void + */ + public function switchMode(string $currentMode, string $previousMode); +} diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerSetDimensionsModeCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerSetDimensionsModeCommandTest.php new file mode 100644 index 0000000000000..a5521325e589c --- /dev/null +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerSetDimensionsModeCommandTest.php @@ -0,0 +1,222 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Indexer\Test\Unit\Console\Command; + +use Magento\Indexer\Console\Command\IndexerSetDimensionsModeCommand; +use Symfony\Component\Console\Tester\CommandTester; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Test for class \Magento\Indexer\Model\ModeSwitcherInterface. + */ +class IndexerSetDimensionsModeCommandTest extends AbstractIndexerCommandCommonSetup +{ + /** + * Command being tested + * + * @var IndexerSetDimensionsModeCommand|\PHPUnit_Framework_MockObject_MockObject + */ + private $command; + + /** + * ScopeConfigInterface + * + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $configReaderMock; + + /** + * @var \Magento\Indexer\Model\ModeSwitcherInterface[] + */ + private $dimensionProviders; + + /** + * @var \Magento\Indexer\Model\ModeSwitcherInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $dimensionModeSwitcherMock; + + /** + * @var \Magento\Indexer\Model\Indexer|\PHPUnit_Framework_MockObject_MockObject + */ + private $indexerMock; + + /** + * @var \Magento\Indexer\Model\DimensionModes|\PHPUnit_Framework_MockObject_MockObject + */ + private $dimensionModes; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $objectManagerHelper = new ObjectManagerHelper($this); + $this->configReaderMock = $this->createMock(ScopeConfigInterface::class); + $this->dimensionModeSwitcherMock = + $this->createMock(\Magento\Indexer\Model\ModeSwitcherInterface::class); + $this->dimensionProviders = [ + 'indexer_title' => $this->dimensionModeSwitcherMock, + ]; + $this->dimensionModes = $this->createMock(\Magento\Indexer\Model\DimensionModes::class); + $this->command = $objectManagerHelper->getObject( + IndexerSetDimensionsModeCommand::class, + [ + 'objectManagerFactory' => $this->objectManagerFactory, + 'configReader' => $this->configReaderMock, + 'dimensionSwitchers' => $this->dimensionProviders, + ] + ); + } + + /** + * Get return value map for object manager + * + * @return array + */ + protected function getObjectManagerReturnValueMap() + { + $result = parent::getObjectManagerReturnValueMap(); + $this->indexerMock = $this->createMock(\Magento\Indexer\Model\Indexer::class); + $result[] = [\Magento\Indexer\Model\Indexer::class, $this->indexerMock]; + + return $result; + } + + /** + * Tests method \Magento\Indexer\Console\Command\IndexerDimensionsModeCommand::execute + * + * @param $indexerTitle + * @param $previousMode + * @param $command + * @param $consoleOutput + * @dataProvider dimensionModesDataProvider + * @return void + */ + public function testExecuteWithAttributes($indexerTitle, $previousMode, $command, $consoleOutput) + { + $this->configureAdminArea(); + $commandTester = new CommandTester($this->command); + $this->dimensionModes->method('getDimensions')->willReturn([ + $previousMode => 'dimension1', + $command['mode'] => 'dimension2', + ]); + $this->dimensionModeSwitcherMock->method('getDimensionModes')->willReturn($this->dimensionModes); + $this->indexerMock->method('load')->willReturnSelf(); + $this->indexerMock->method('getTitle')->willReturn($indexerTitle); + $commandTester->execute($command); + $actualValue = $commandTester->getDisplay(); + $this->assertEquals( + $consoleOutput, + $actualValue + ); + } + + /** + * @return array + */ + public function dimensionModesDataProvider(): array + { + return [ + 'was_changed' => [ + 'indexer_title' => 'indexer_title', + 'previousMode' => 'none', + 'command' => [ + 'indexer' => 'indexer_title', + 'mode' => 'store', + ], + 'output' => + sprintf( + 'Dimensions mode for indexer "%s" was changed from \'%s\' to \'%s\'', + 'indexer_title', + 'none', + 'store' + ) . PHP_EOL + , + ], + 'was_not_changed' => [ + 'indexer_title' => 'indexer_title', + 'previousMode' => 'none', + 'command' => [ + 'indexer' => 'indexer_title', + 'mode' => 'none', + ], + 'output' => + sprintf( + 'Dimensions mode for indexer "%s" has not been changed', + 'indexer_title' + ) . PHP_EOL + , + ], + ]; + } + + /** + * Tests indexer exception of method \Magento\Indexer\Console\Command\IndexerDimensionsModeCommand::execute + * + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage + * Invalid value for "<indexer>" argument. Accepted values for "<indexer>" are 'indexer_title' + * @return void + */ + public function testExecuteWithIndxerException() + { + $commandTester = new CommandTester($this->command); + $this->indexerMock->method('getTitle')->willReturn('indexer_title'); + $commandTester->execute(['indexer' => 'non_existing_title']); + } + + /** + * Tests indexer exception of method \Magento\Indexer\Console\Command\IndexerDimensionsModeCommand::execute + * + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Missing argument "<mode>". Accepted values for "<mode>" are 'store,website' + * @return void + */ + public function testExecuteWithModeException() + { + $commandTester = new CommandTester($this->command); + $this->dimensionModes->method('getDimensions')->willReturn([ + 'store' => 'dimension1', + 'website' => 'dimension2', + ]); + $this->dimensionModeSwitcherMock->method('getDimensionModes')->willReturn($this->dimensionModes); + $this->indexerMock->method('getTitle')->willReturn('indexer_title'); + $commandTester->execute([ + 'indexer' => 'indexer_title', + ]); + } + + /** + * Test execution of command without any arguments + * + * @return void + */ + public function testExecuteWithNoArguments() + { + $indexerTitle = 'indexer_title'; + $modesConfig = [ + 'store' => 'dimension1', + 'website' => 'dimension2', + ]; + $this->configureAdminArea(); + $commandTester = new CommandTester($this->command); + $this->indexerMock->method('getTitle')->willReturn($indexerTitle); + $this->dimensionModes->method('getDimensions')->willReturn($modesConfig); + $this->dimensionModeSwitcherMock->method('getDimensionModes')->willReturn($this->dimensionModes); + $commandTester->execute([]); + $actualValue = $commandTester->getDisplay(); + $consoleOutput = sprintf('%-50s', 'Indexer') . 'Available modes' . PHP_EOL + . sprintf('%-50s', $indexerTitle) . 'store,website' . PHP_EOL; + $this->assertEquals( + $consoleOutput, + $actualValue + ); + } +} diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowDimensionsModeCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowDimensionsModeCommandTest.php new file mode 100644 index 0000000000000..f5487f268ac01 --- /dev/null +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowDimensionsModeCommandTest.php @@ -0,0 +1,144 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Indexer\Test\Unit\Console\Command; + +use Magento\Indexer\Console\Command\IndexerShowDimensionsModeCommand; +use Symfony\Component\Console\Tester\CommandTester; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class IndexerShowDimensionsModeCommandTest extends AbstractIndexerCommandCommonSetup +{ + /** + * Command being tested + * + * @var IndexerShowDimensionsModeCommand|\PHPUnit_Framework_MockObject_MockObject + */ + private $command; + + /** + * ScopeConfigInterface + * + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $configReaderMock; + + /** + * @var \Magento\Indexer\Model\ModeSwitcherInterface[] + */ + private $indexers; + + /** + * @var \Magento\Indexer\Model\Indexer|\PHPUnit_Framework_MockObject_MockObject + */ + private $indexerMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $objectManagerHelper = new ObjectManagerHelper($this); + $this->configReaderMock = $this->createMock(ScopeConfigInterface::class); + $this->indexers = ['indexer_1' => 'indexer_1', 'indexer_2' => 'indexer_2']; + $this->command = $objectManagerHelper->getObject( + IndexerShowDimensionsModeCommand::class, + [ + 'objectManagerFactory' => $this->objectManagerFactory, + 'configReader' => $this->configReaderMock, + 'indexers' => $this->indexers, + ] + ); + } + + /** + * Get return value map for object manager + * + * @return array + */ + protected function getObjectManagerReturnValueMap(): array + { + $result = parent::getObjectManagerReturnValueMap(); + $this->indexerMock = $this->createMock(\Magento\Indexer\Model\Indexer::class); + $result[] = [\Magento\Indexer\Model\Indexer::class, $this->indexerMock]; + + return $result; + } + + /** + * Tests method \Magento\Indexer\Console\Command\IndexerDimensionsModeCommand::execute + * + * @param $command + * @param $consoleOutput + * @dataProvider dimensionModesDataProvider + */ + public function testExecuteWithAttributes($command, $consoleOutput) + { + $indexers = [['indexer_1'], ['indexer_2']]; + $indexerTitles = ['indexer_title1', 'indexer_title2']; + $this->configureAdminArea(); + /** @var CommandTester $commandTester */ + $commandTester = new CommandTester($this->command); + $this->indexerMock->method('load')->withConsecutive(...$indexers); + $this->indexerMock->method('getTitle')->willReturnOnConsecutiveCalls(...$indexerTitles); + $commandTester->execute($command); + $actualValue = $commandTester->getDisplay(); + $this->assertEquals( + $consoleOutput, + $actualValue + ); + } + + /** + * @return array + */ + public function dimensionModesDataProvider(): array + { + return [ + 'get_all' => [ + 'command' => [], + 'output' => + sprintf( + '%-50s ', + 'indexer_title1' . ':' + ) . 'none' . PHP_EOL . + sprintf( + '%-50s ', + 'indexer_title2' . ':' + ) . 'none' . PHP_EOL + , + ], + 'get_by_index' => [ + 'command' => [ + 'indexer' => ['indexer_1'], + ], + 'output' => + sprintf( + '%-50s ', + 'indexer_title1' . ':' + ) . 'none' . PHP_EOL + , + ], + 'get_by_several_indexes' => [ + 'command' => [ + 'indexer' => ['indexer_1', 'indexer_2'], + ], + 'output' => + sprintf( + '%-50s ', + 'indexer_title1' . ':' + ) . 'none' . PHP_EOL . + sprintf( + '%-50s ', + 'indexer_title2' . ':' + ) . 'none' . PHP_EOL + , + ], + ]; + } +} diff --git a/app/code/Magento/Indexer/etc/di.xml b/app/code/Magento/Indexer/etc/di.xml index 6abaaf625e108..c7603191e8606 100644 --- a/app/code/Magento/Indexer/etc/di.xml +++ b/app/code/Magento/Indexer/etc/di.xml @@ -56,6 +56,8 @@ <item name="show-mode" xsi:type="object">Magento\Indexer\Console\Command\IndexerShowModeCommand</item> <item name="status" xsi:type="object">Magento\Indexer\Console\Command\IndexerStatusCommand</item> <item name="reset" xsi:type="object">Magento\Indexer\Console\Command\IndexerResetStateCommand</item> + <item name="set-dimensions-mode" xsi:type="object">Magento\Indexer\Console\Command\IndexerSetDimensionsModeCommand</item> + <item name="show-dimensions-mode" xsi:type="object">Magento\Indexer\Console\Command\IndexerShowDimensionsModeCommand</item> </argument> </arguments> </type> diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php index 86d2d8ee589a7..179babdb7e184 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php @@ -3,13 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\TestFramework\Annotation; use Magento\Catalog\Model\Indexer\Product\Price\ModeSwitcher; +use Magento\Catalog\Model\Indexer\Product\Price\ModeSwitcherConfiguration; use Magento\Catalog\Model\Indexer\Product\Price\Processor; use Magento\Framework\App\Cache\TypeListInterface; -use Magento\Framework\App\Config\ConfigResource\ConfigInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\ObjectManagerInterface; use Magento\TestFramework\App\Config; @@ -31,9 +32,6 @@ class IndexerDimensionMode /** @var ModeSwitcher */ private $modeSwitcher; - /** @var ConfigInterface */ - private $configWriter; - /** @var ObjectManagerInterface */ private $objectManager; @@ -55,31 +53,24 @@ private function restoreDb() /** * @param string $mode + * @param TestCase $test + * @throws \Exception */ - private function setDimensionMode($mode, $test) + private function setDimensionMode(string $mode, TestCase $test) { $this->objectManager = Bootstrap::getObjectManager(); $this->modeSwitcher = $this->objectManager->get(ModeSwitcher::class); - $this->configWriter = $this->objectManager->get(ConfigInterface::class); $this->configReader = $this->objectManager->get(ScopeConfigInterface::class); $this->cacheTypeList = $this->objectManager->get(TypeListInterface::class); $this->configReader->clean(); - $previousMode = $this->configReader->getValue(ModeSwitcher::XML_PATH_PRICE_DIMENSIONS_MODE) ?: + $previousMode = $this->configReader->getValue(ModeSwitcherConfiguration::XML_PATH_PRICE_DIMENSIONS_MODE) ?: DimensionModeConfiguration::DIMENSION_NONE; if ($previousMode !== $mode) { //Create new tables and move data - $this->modeSwitcher->createTables($mode); - $this->modeSwitcher->moveData($mode, $previousMode); - - //Change config options - $this->configWriter->saveConfig(ModeSwitcher::XML_PATH_PRICE_DIMENSIONS_MODE, $mode); - $this->cacheTypeList->cleanType('config'); + $this->modeSwitcher->switchMode($mode, $previousMode); $this->objectManager->get(Config::class)->clean(); - - //Delete old tables - $this->modeSwitcher->dropTables($previousMode); } else { $this->fail('Dimensions mode for indexer has not been changed', $test); } @@ -90,6 +81,7 @@ private function setDimensionMode($mode, $test) * * @param TestCase $test * @return void + * @throws \Exception */ public function startTest(TestCase $test) { diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/PriceIndexerDimensionsModeSetCommandTest.php b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/PriceIndexerDimensionsModeSetCommandTest.php index 79028a159d1aa..b1407f4266c13 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/PriceIndexerDimensionsModeSetCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/PriceIndexerDimensionsModeSetCommandTest.php @@ -3,8 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); - namespace Magento\Setup\Console\Command; use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; @@ -12,18 +10,16 @@ use Magento\Framework\Console\Cli; use Magento\Framework\ObjectManagerInterface; use Magento\TestFramework\Helper\Bootstrap; -use Magento\Catalog\Console\Command\PriceIndexerDimensionsModeSetCommand; /** - * Class PriceIndexerDimensionsModeSetCommand - * @package Magento\Setup\Console\Command + * Test command that sets indexer mode for catalog_product_price indexer */ class PriceIndexerDimensionsModeSetCommandTest extends \Magento\TestFramework\Indexer\TestCase { /** @var ObjectManagerInterface */ private $objectManager; - /** @var GenerateFixturesCommand */ + /** @var \Magento\Indexer\Console\Command\IndexerSetDimensionsModeCommand */ private $command; /** @var CommandTester */ @@ -39,7 +35,7 @@ public function setUp() $this->objectManager->get(\Magento\TestFramework\App\Config::class)->clean(); $this->command = $this->objectManager->create( - \Magento\Catalog\Console\Command\PriceIndexerDimensionsModeSetCommand::class + \Magento\Indexer\Console\Command\IndexerSetDimensionsModeCommand::class ); $this->commandTester = new CommandTester($this->command); @@ -47,14 +43,6 @@ public function setUp() parent::setUp(); } - /** - * tearDown - */ - public function tearDown() - { - parent::tearDown(); - } - /** * setUpBeforeClass */ @@ -83,11 +71,12 @@ public function testSwitchMode($previousMode, $currentMode) { $this->commandTester->execute( [ - PriceIndexerDimensionsModeSetCommand::INPUT_KEY_MODE => $currentMode + 'indexer' => 'catalog_product_price', + 'mode' => $currentMode, ] ); - $expectedOutput = 'Dimensions mode for indexer Product Price was changed from \'' - . $previousMode . '\' to \'' . $currentMode . '\''; + $expectedOutput = 'Dimensions mode for indexer "Product Price" was changed from \'' + . $previousMode . '\' to \'' . $currentMode . '\'' . PHP_EOL; $actualOutput = $this->commandTester->getDisplay(); @@ -134,10 +123,11 @@ public function testSwitchModeForSameMode() { $this->commandTester->execute( [ - PriceIndexerDimensionsModeSetCommand::INPUT_KEY_MODE => DimensionModeConfiguration::DIMENSION_NONE + 'indexer' => 'catalog_product_price', + 'mode' => DimensionModeConfiguration::DIMENSION_NONE ] ); - $expectedOutput = 'Dimensions mode for indexer Product Price has not been changed'; + $expectedOutput = 'Dimensions mode for indexer "Product Price" has not been changed' . PHP_EOL; $actualOutput = $this->commandTester->getDisplay(); @@ -160,8 +150,7 @@ public function testSwitchModeWithInvalidArgument() { $this->commandTester->execute( [ - PriceIndexerDimensionsModeSetCommand::INPUT_KEY_MODE => DimensionModeConfiguration::DIMENSION_NONE . - '_not_valid' + 'indexer' => 'indexer_not_valid' ] ); } From a02e5ccbd26bf683ee5260668633e20bf72989c5 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Tue, 7 Aug 2018 01:00:03 -0300 Subject: [PATCH 0765/1171] Removing deprecated code. --- .../SalesRule/Controller/Adminhtml/Promo/Quote/Delete.php | 8 ++++---- .../SalesRule/Controller/Adminhtml/Promo/Quote/Edit.php | 2 +- .../Controller/Adminhtml/Promo/Quote/Generate.php | 2 +- .../SalesRule/Controller/Adminhtml/Promo/Quote/Save.php | 8 ++++---- .../SalesRule/Observer/CheckSalesRulesAvailability.php | 2 +- .../Controller/Adminhtml/Promo/Quote/GenerateTest.php | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Delete.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Delete.php index 9adb62583985d..28993e159499b 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Delete.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Delete.php @@ -21,13 +21,13 @@ public function execute() $model = $this->_objectManager->create(\Magento\SalesRule\Model\Rule::class); $model->load($id); $model->delete(); - $this->messageManager->addSuccess(__('You deleted the rule.')); + $this->messageManager->addSuccessMessage(__('You deleted the rule.')); $this->_redirect('sales_rule/*/'); return; } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('We can\'t delete the rule right now. Please review the log and try again.') ); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); @@ -35,7 +35,7 @@ public function execute() return; } } - $this->messageManager->addError(__('We can\'t find a rule to delete.')); + $this->messageManager->addErrorMessage(__('We can\'t find a rule to delete.')); $this->_redirect('sales_rule/*/'); } } diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Edit.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Edit.php index 7221f49b852d3..717c71fde4837 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Edit.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Edit.php @@ -51,7 +51,7 @@ public function execute() if ($id) { $model->load($id); if (!$model->getRuleId()) { - $this->messageManager->addError(__('This rule no longer exists.')); + $this->messageManager->addErrorMessage(__('This rule no longer exists.')); $this->_redirect('sales_rule/*'); return; } diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Generate.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Generate.php index 297db7d96939d..12d34b8320d07 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Generate.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Generate.php @@ -64,7 +64,7 @@ public function execute() $couponCodes = $this->couponGenerator->generateCodes($data); $generated = count($couponCodes); - $this->messageManager->addSuccess(__('%1 coupon(s) have been generated.', $generated)); + $this->messageManager->addSuccessMessage(__('%1 coupon(s) have been generated.', $generated)); $this->_view->getLayout()->initMessages(); $result['messages'] = $this->_view->getLayout()->getMessagesBlock()->getGroupedHtml(); } catch (\Magento\Framework\Exception\InputException $inputException) { diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Save.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Save.php index f28b245ee04ab..a5b71130e70eb 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Save.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Save.php @@ -50,7 +50,7 @@ public function execute() $validateResult = $model->validateData(new \Magento\Framework\DataObject($data)); if ($validateResult !== true) { foreach ($validateResult as $errorMessage) { - $this->messageManager->addError($errorMessage); + $this->messageManager->addErrorMessage($errorMessage); } $session->setPageData($data); $this->_redirect('sales_rule/*/edit', ['id' => $model->getId()]); @@ -82,7 +82,7 @@ public function execute() $session->setPageData($model->getData()); $model->save(); - $this->messageManager->addSuccess(__('You saved the rule.')); + $this->messageManager->addSuccessMessage(__('You saved the rule.')); $session->setPageData(false); if ($this->getRequest()->getParam('back')) { $this->_redirect('sales_rule/*/edit', ['id' => $model->getId()]); @@ -91,7 +91,7 @@ public function execute() $this->_redirect('sales_rule/*/'); return; } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $id = (int)$this->getRequest()->getParam('rule_id'); if (!empty($id)) { $this->_redirect('sales_rule/*/edit', ['id' => $id]); @@ -100,7 +100,7 @@ public function execute() } return; } catch (\Exception $e) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('Something went wrong while saving the rule data. Please review the error log.') ); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); diff --git a/app/code/Magento/SalesRule/Observer/CheckSalesRulesAvailability.php b/app/code/Magento/SalesRule/Observer/CheckSalesRulesAvailability.php index 4b66d2b98642c..f2819a06fe1bf 100644 --- a/app/code/Magento/SalesRule/Observer/CheckSalesRulesAvailability.php +++ b/app/code/Magento/SalesRule/Observer/CheckSalesRulesAvailability.php @@ -54,7 +54,7 @@ public function checkSalesRulesAvailability($attributeCode) } if ($disabledRulesCount) { - $this->messageManager->addWarning( + $this->messageManager->addWarningMessage( __( '%1 Cart Price Rules based on "%2" attribute have been disabled.', $disabledRulesCount, diff --git a/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/GenerateTest.php b/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/GenerateTest.php index 8bcffcab9ca0a..219f342cdb949 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/GenerateTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/GenerateTest.php @@ -143,7 +143,7 @@ public function testExecute() ->with($requestData) ->willReturn(['some_data', 'some_data_2']); $this->messageManager->expects($this->once()) - ->method('addSuccess'); + ->method('addSuccessMessage'); $this->responseMock->expects($this->once()) ->method('representJson') ->with(); From 105b7872320c837857789202c30be3a9ca33f70a Mon Sep 17 00:00:00 2001 From: Jignesh Baldha <iamjignesh.b@gmail.com> Date: Tue, 7 Aug 2018 18:39:02 +0530 Subject: [PATCH 0766/1171] Changed unit test according --- .../View/Test/Unit/Element/AbstractBlockTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php index ced26f082fe27..5f7508438a6ed 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php @@ -285,6 +285,15 @@ public function getCacheLifetimeDataProvider() 'expectsCacheSave' => $this->never(), 'expectedResult' => '', ], + [ + 'cacheLifetime' => false, + 'dataFromCache' => 'dataFromCache', + 'dataForSaveCache' => '', + 'expectsDispatchEvent' => $this->exactly(2), + 'expectsCacheLoad' => $this->never(), + 'expectsCacheSave' => $this->never(), + 'expectedResult' => '', + ], [ 'cacheLifetime' => 120, 'dataFromCache' => 'dataFromCache', From 20069c4846043d90755d978c799b1e358ef2fe4f Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Tue, 7 Aug 2018 10:09:28 -0300 Subject: [PATCH 0767/1171] Replacing deprecated methods from Magento_Sales module. --- .../Controller/AbstractController/Reorder.php | 9 ++++++--- .../Creditmemo/AbstractCreditmemo/Email.php | 2 +- .../Adminhtml/Invoice/AbstractInvoice/Email.php | 2 +- .../Adminhtml/Invoice/AbstractInvoice/View.php | 2 +- .../Sales/Controller/Adminhtml/Order.php | 4 ++-- .../Adminhtml/Order/AbstractMassAction.php | 2 +- .../Controller/Adminhtml/Order/AddressSave.php | 6 +++--- .../Sales/Controller/Adminhtml/Order/Cancel.php | 8 ++++---- .../Sales/Controller/Adminhtml/Order/Create.php | 6 +++--- .../Adminhtml/Order/Create/LoadBlock.php | 4 ++-- .../Controller/Adminhtml/Order/Create/Save.php | 8 ++++---- .../Adminhtml/Order/Creditmemo/Cancel.php | 6 +++--- .../Adminhtml/Order/Creditmemo/Save.php | 6 +++--- .../Adminhtml/Order/Creditmemo/VoidAction.php | 6 +++--- .../Adminhtml/Order/CreditmemoLoader.php | 4 ++-- .../Controller/Adminhtml/Order/Edit/Start.php | 4 ++-- .../Sales/Controller/Adminhtml/Order/Email.php | 6 +++--- .../Sales/Controller/Adminhtml/Order/Hold.php | 8 ++++---- .../Adminhtml/Order/Invoice/Cancel.php | 6 +++--- .../Adminhtml/Order/Invoice/Capture.php | 6 +++--- .../Adminhtml/Order/Invoice/NewAction.php | 4 ++-- .../Controller/Adminhtml/Order/Invoice/Save.php | 17 ++++++++++------- .../Adminhtml/Order/Invoice/VoidAction.php | 6 +++--- .../Controller/Adminhtml/Order/MassCancel.php | 6 +++--- .../Controller/Adminhtml/Order/MassHold.php | 6 +++--- .../Controller/Adminhtml/Order/MassUnhold.php | 6 +++--- .../Adminhtml/Order/PdfDocumentsMassAction.php | 2 +- .../Adminhtml/Order/Pdfcreditmemos.php | 2 +- .../Controller/Adminhtml/Order/Pdfdocs.php | 2 +- .../Controller/Adminhtml/Order/Pdfinvoices.php | 2 +- .../Controller/Adminhtml/Order/Pdfshipments.php | 2 +- .../Adminhtml/Order/ReviewPayment.php | 6 +++--- .../Adminhtml/Order/Status/AssignPost.php | 8 ++++---- .../Controller/Adminhtml/Order/Status/Edit.php | 2 +- .../Controller/Adminhtml/Order/Status/Save.php | 9 +++++---- .../Adminhtml/Order/Status/Unassign.php | 8 ++++---- .../Sales/Controller/Adminhtml/Order/Unhold.php | 8 ++++---- .../Sales/Controller/Adminhtml/Order/View.php | 2 +- .../Adminhtml/Order/View/Giftmessage/Save.php | 4 ++-- .../Controller/Adminhtml/Order/VoidPayment.php | 6 +++--- .../Sales/Controller/Adminhtml/Transactions.php | 2 +- .../Controller/Adminhtml/Transactions/Fetch.php | 6 +++--- app/code/Magento/Sales/Helper/Guest.php | 2 +- .../Magento/Sales/Model/AdminOrder/Create.php | 4 ++-- .../Sales/Model/AdminOrder/EmailSender.php | 2 +- .../Creditmemo/AbstractCreditmemo/EmailTest.php | 7 +++++-- .../Invoice/AbstractInvoice/EmailTest.php | 2 +- .../Controller/Adminhtml/Order/CancelTest.php | 4 ++-- .../Adminhtml/Order/Create/ProcessDataTest.php | 6 +++++- .../Adminhtml/Order/Create/ReorderTest.php | 2 +- .../Adminhtml/Order/Creditmemo/CancelTest.php | 2 +- .../Adminhtml/Order/Creditmemo/SaveTest.php | 2 +- .../Order/Creditmemo/VoidActionTest.php | 2 +- .../Controller/Adminhtml/Order/EmailTest.php | 6 +++--- .../Controller/Adminhtml/Order/HoldTest.php | 4 ++-- .../Adminhtml/Order/Invoice/CancelTest.php | 6 +++--- .../Adminhtml/Order/Invoice/CaptureTest.php | 6 +++--- .../Adminhtml/Order/Invoice/SaveTest.php | 2 +- .../Adminhtml/Order/Invoice/VoidActionTest.php | 8 ++++---- .../Adminhtml/Order/MassCancelTest.php | 8 ++++---- .../Controller/Adminhtml/Order/MassHoldTest.php | 6 +++--- .../Adminhtml/Order/MassUnholdTest.php | 6 +++--- .../Adminhtml/Order/ReviewPaymentTest.php | 4 ++-- .../Controller/Adminhtml/Order/UnholdTest.php | 4 ++-- .../Controller/Adminhtml/Order/ViewTest.php | 4 ++-- .../Adminhtml/PdfDocumentsMassActionTest.php | 4 ++-- .../Unit/Model/AdminOrder/EmailSenderTest.php | 2 +- 67 files changed, 170 insertions(+), 156 deletions(-) diff --git a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php index d7ab99377e1b5..dd4d73930cc8f 100644 --- a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php +++ b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php @@ -59,13 +59,16 @@ public function execute() $cart->addOrderItem($item); } catch (\Magento\Framework\Exception\LocalizedException $e) { if ($this->_objectManager->get(\Magento\Checkout\Model\Session::class)->getUseNotice(true)) { - $this->messageManager->addNotice($e->getMessage()); + $this->messageManager->addNoticeMessage($e->getMessage()); } else { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } return $resultRedirect->setPath('*/*/history'); } catch (\Exception $e) { - $this->messageManager->addException($e, __('We can\'t add this item to your shopping cart right now.')); + $this->messageManager->addExceptionMessage( + $e, + __('We can\'t add this item to your shopping cart right now.') + ); return $resultRedirect->setPath('checkout/cart'); } } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/Email.php b/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/Email.php index ac34af6f4ce6e..e3571f3cc02bc 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/Email.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/Email.php @@ -33,7 +33,7 @@ public function execute() $this->_objectManager->create(\Magento\Sales\Api\CreditmemoManagementInterface::class) ->notify($creditmemoId); - $this->messageManager->addSuccess(__('You sent the message.')); + $this->messageManager->addSuccessMessage(__('You sent the message.')); $resultRedirect = $this->resultRedirectFactory->create(); $resultRedirect->setPath('sales/order_creditmemo/view', ['creditmemo_id' => $creditmemoId]); return $resultRedirect; diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/Email.php b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/Email.php index e491dd78db617..c645df152ba8f 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/Email.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/Email.php @@ -57,7 +57,7 @@ public function execute() \Magento\Sales\Api\InvoiceManagementInterface::class )->notify($invoice->getEntityId()); - $this->messageManager->addSuccess(__('You sent the message.')); + $this->messageManager->addSuccessMessage(__('You sent the message.')); return $this->resultRedirectFactory->create()->setPath( 'sales/invoice/view', ['order_id' => $invoice->getOrder()->getId(), 'invoice_id' => $invoiceId] diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/View.php b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/View.php index 3615f3033ca81..300b7ee37f2ef 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/View.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/View.php @@ -79,7 +79,7 @@ protected function getInvoice() ->get($this->getRequest()->getParam('invoice_id')); $this->registry->register('current_invoice', $invoice); } catch (\Exception $e) { - $this->messageManager->addError(__('Invoice capturing error')); + $this->messageManager->addErrorMessage(__('Invoice capturing error')); return false; } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order.php b/app/code/Magento/Sales/Controller/Adminhtml/Order.php index c2299374c798c..0066c5f4c828a 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order.php @@ -154,11 +154,11 @@ protected function _initOrder() try { $order = $this->orderRepository->get($id); } catch (NoSuchEntityException $e) { - $this->messageManager->addError(__('This order no longer exists.')); + $this->messageManager->addErrorMessage(__('This order no longer exists.')); $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); return false; } catch (InputException $e) { - $this->messageManager->addError(__('This order no longer exists.')); + $this->messageManager->addErrorMessage(__('This order no longer exists.')); $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); return false; } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/AbstractMassAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/AbstractMassAction.php index 05066fe5b125e..c6b45f282debc 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/AbstractMassAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/AbstractMassAction.php @@ -61,7 +61,7 @@ public function execute() $collection = $this->filter->getCollection($this->collectionFactory->create()); return $this->massAction($collection); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); return $resultRedirect->setPath($this->redirectUrl); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php index dde2192c3b82c..c71de8cb0252d 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php @@ -114,12 +114,12 @@ public function execute() 'order_id' => $address->getParentId() ] ); - $this->messageManager->addSuccess(__('You updated the order address.')); + $this->messageManager->addSuccessMessage(__('You updated the order address.')); return $resultRedirect->setPath('sales/*/view', ['order_id' => $address->getParentId()]); } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('We can\'t update the order address right now.')); + $this->messageManager->addExceptionMessage($e, __('We can\'t update the order address right now.')); } return $resultRedirect->setPath('sales/*/address', ['address_id' => $address->getId()]); } else { diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Cancel.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Cancel.php index de41c3c737968..7e41c7417b38d 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Cancel.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Cancel.php @@ -24,18 +24,18 @@ public function execute() { $resultRedirect = $this->resultRedirectFactory->create(); if (!$this->isValidPostRequest()) { - $this->messageManager->addError(__('You have not canceled the item.')); + $this->messageManager->addErrorMessage(__('You have not canceled the item.')); return $resultRedirect->setPath('sales/*/'); } $order = $this->_initOrder(); if ($order) { try { $this->orderManagement->cancel($order->getEntityId()); - $this->messageManager->addSuccess(__('You canceled the order.')); + $this->messageManager->addSuccessMessage(__('You canceled the order.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('You have not canceled the item.')); + $this->messageManager->addErrorMessage(__('You have not canceled the item.')); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); } return $resultRedirect->setPath('sales/order/view', ['order_id' => $order->getId()]); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create.php index a7b41d0a780f3..dcf5e617d055c 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create.php @@ -317,7 +317,7 @@ protected function _processActionData($action = null) } } if (!$isApplyDiscount) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __( '"%1" coupon code was not applied. Do not apply discount is selected for item(s)', $this->escaper->escapeHtml($couponCode) @@ -325,14 +325,14 @@ protected function _processActionData($action = null) ); } else { if ($this->_getQuote()->getCouponCode() !== $couponCode) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __( 'The "%1" coupon code isn\'t valid. Verify the code and try again.', $this->escaper->escapeHtml($couponCode) ) ); } else { - $this->messageManager->addSuccess(__('The coupon code has been accepted.')); + $this->messageManager->addSuccessMessage(__('The coupon code has been accepted.')); } } } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlock.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlock.php index 6a9d0a5dcb8ed..bc4eb3cfba423 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlock.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlock.php @@ -55,10 +55,10 @@ public function execute() $this->_initSession()->_processData(); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->_reloadQuote(); - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { $this->_reloadQuote(); - $this->messageManager->addException($e, $e->getMessage()); + $this->messageManager->addExceptionMessage($e, $e->getMessage()); } $asJson = $request->getParam('json'); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Save.php index 621705c7937cb..256bfe2fbb64f 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Save.php @@ -49,7 +49,7 @@ public function execute() ->createOrder(); $this->_getSession()->clearStorage(); - $this->messageManager->addSuccess(__('You created the order.')); + $this->messageManager->addSuccessMessage(__('You created the order.')); if ($this->_authorization->isAllowed('Magento_Sales::actions_view')) { $resultRedirect->setPath('sales/order/view', ['order_id' => $order->getId()]); } else { @@ -59,7 +59,7 @@ public function execute() $this->_getOrderCreateModel()->saveQuote(); $message = $e->getMessage(); if (!empty($message)) { - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); } $resultRedirect->setPath('sales/*/'); } catch (\Magento\Framework\Exception\LocalizedException $e) { @@ -67,11 +67,11 @@ public function execute() $this->_getSession()->setCustomerId($this->_getSession()->getQuote()->getCustomerId()); $message = $e->getMessage(); if (!empty($message)) { - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); } $resultRedirect->setPath('sales/*/'); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Order saving error: %1', $e->getMessage())); + $this->messageManager->addExceptionMessage($e, __('Order saving error: %1', $e->getMessage())); $resultRedirect->setPath('sales/*/'); } return $resultRedirect; diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/Cancel.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/Cancel.php index eedf49a6cae9a..1ca0b53ee8784 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/Cancel.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/Cancel.php @@ -47,11 +47,11 @@ public function execute() \Magento\Sales\Api\CreditmemoManagementInterface::class ); $creditmemoManagement->cancel($creditmemoId); - $this->messageManager->addSuccess(__('The credit memo has been canceled.')); + $this->messageManager->addSuccessMessage(__('The credit memo has been canceled.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('Credit memo has not been canceled.')); + $this->messageManager->addErrorMessage(__('Credit memo has not been canceled.')); } $resultRedirect = $this->resultRedirectFactory->create(); $resultRedirect->setPath('sales/*/view', ['creditmemo_id' => $creditmemoId]); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/Save.php index 826a2a2a8b6c1..2a2445d2b2ae0 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/Save.php @@ -109,7 +109,7 @@ public function execute() $this->creditmemoSender->send($creditmemo); } - $this->messageManager->addSuccess(__('You created the credit memo.')); + $this->messageManager->addSuccessMessage(__('You created the credit memo.')); $this->_getSession()->getCommentText(true); $resultRedirect->setPath('sales/order/view', ['order_id' => $creditmemo->getOrderId()]); return $resultRedirect; @@ -119,11 +119,11 @@ public function execute() return $resultForward; } } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $this->_getSession()->setFormData($data); } catch (\Exception $e) { $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $this->messageManager->addError(__('We can\'t save the credit memo right now.')); + $this->messageManager->addErrorMessage(__('We can\'t save the credit memo right now.')); } $resultRedirect->setPath('sales/*/new', ['_current' => true]); return $resultRedirect; diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/VoidAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/VoidAction.php index e7bd891fbfbff..146514265517a 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/VoidAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/VoidAction.php @@ -64,11 +64,11 @@ public function execute() $transactionSave->addObject($creditmemo->getInvoice()); } $transactionSave->save(); - $this->messageManager->addSuccess(__('You voided the credit memo.')); + $this->messageManager->addSuccessMessage(__('You voided the credit memo.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('We can\'t void the credit memo.')); + $this->messageManager->addErrorMessage(__('We can\'t void the credit memo.')); } $resultRedirect = $this->resultRedirectFactory->create(); $resultRedirect->setPath('sales/*/view', ['creditmemo_id' => $creditmemo->getId()]); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/CreditmemoLoader.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/CreditmemoLoader.php index 057e72b6176d3..0c5864e954a4f 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/CreditmemoLoader.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/CreditmemoLoader.php @@ -138,7 +138,7 @@ protected function _canCreditmemo($order) * Check order existing */ if (!$order->getId()) { - $this->messageManager->addError(__('The order no longer exists.')); + $this->messageManager->addErrorMessage(__('The order no longer exists.')); return false; } @@ -146,7 +146,7 @@ protected function _canCreditmemo($order) * Check creditmemo create availability */ if (!$order->canCreditmemo()) { - $this->messageManager->addError(__('We can\'t create credit memo for the order.')); + $this->messageManager->addErrorMessage(__('We can\'t create credit memo for the order.')); return false; } return true; diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Edit/Start.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Edit/Start.php index 14c630542dbde..b1eba5f661c2e 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Edit/Start.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Edit/Start.php @@ -36,10 +36,10 @@ public function execute() $resultRedirect->setPath('sales/order/'); } } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $resultRedirect->setPath('sales/order/view', ['order_id' => $orderId]); } catch (\Exception $e) { - $this->messageManager->addException($e, $e->getMessage()); + $this->messageManager->addExceptionMessage($e, $e->getMessage()); $resultRedirect->setPath('sales/order/view', ['order_id' => $orderId]); } return $resultRedirect; diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Email.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Email.php index 1573a07710b65..3500de798979a 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Email.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Email.php @@ -25,11 +25,11 @@ public function execute() if ($order) { try { $this->orderManagement->notify($order->getEntityId()); - $this->messageManager->addSuccess(__('You sent the order email.')); + $this->messageManager->addSuccessMessage(__('You sent the order email.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('We can\'t send the email order right now.')); + $this->messageManager->addErrorMessage(__('We can\'t send the email order right now.')); $this->logger->critical($e); } return $this->resultRedirectFactory->create()->setPath( diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Hold.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Hold.php index feb307ca19c31..7d2c713eece48 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Hold.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Hold.php @@ -23,18 +23,18 @@ public function execute() { $resultRedirect = $this->resultRedirectFactory->create(); if (!$this->isValidPostRequest()) { - $this->messageManager->addError(__('You have not put the order on hold.')); + $this->messageManager->addErrorMessage(__('You have not put the order on hold.')); return $resultRedirect->setPath('sales/*/'); } $order = $this->_initOrder(); if ($order) { try { $this->orderManagement->hold($order->getEntityId()); - $this->messageManager->addSuccess(__('You put the order on hold.')); + $this->messageManager->addSuccessMessage(__('You put the order on hold.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('You have not put the order on hold.')); + $this->messageManager->addErrorMessage(__('You have not put the order on hold.')); } $resultRedirect->setPath('sales/order/view', ['order_id' => $order->getId()]); return $resultRedirect; diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Cancel.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Cancel.php index a031591b37fae..e4a74329502f1 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Cancel.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Cancel.php @@ -31,11 +31,11 @@ public function execute() )->addObject( $invoice->getOrder() )->save(); - $this->messageManager->addSuccess(__('You canceled the invoice.')); + $this->messageManager->addSuccessMessage(__('You canceled the invoice.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('Invoice canceling error')); + $this->messageManager->addErrorMessage(__('Invoice canceling error')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Capture.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Capture.php index 030ccc9d9d3f6..43270264ecbda 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Capture.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Capture.php @@ -33,11 +33,11 @@ public function execute() )->addObject( $invoice->getOrder() )->save(); - $this->messageManager->addSuccess(__('The invoice has been captured.')); + $this->messageManager->addSuccessMessage(__('The invoice has been captured.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('Invoice capturing error')); + $this->messageManager->addErrorMessage(__('Invoice capturing error')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewAction.php index 359bbafd45105..2d7826807a6c3 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewAction.php @@ -112,10 +112,10 @@ public function execute() $resultPage->getConfig()->getTitle()->prepend(__('New Invoice')); return $resultPage; } catch (\Magento\Framework\Exception\LocalizedException $exception) { - $this->messageManager->addError($exception->getMessage()); + $this->messageManager->addErrorMessage($exception->getMessage()); return $this->_redirectToOrder($orderId); } catch (\Exception $exception) { - $this->messageManager->addException($exception, 'Cannot create an invoice.'); + $this->messageManager->addExceptionMessage($exception, 'Cannot create an invoice.'); return $this->_redirectToOrder($orderId); } } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php index d804dff5d48a0..71338c818c418 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php @@ -118,7 +118,8 @@ public function execute() $formKeyIsValid = $this->_formKeyValidator->validate($this->getRequest()); $isPost = $this->getRequest()->isPost(); if (!$formKeyIsValid || !$isPost) { - $this->messageManager->addError(__("The invoice can't be saved at this time. Please try again later.")); + $this->messageManager + ->addErrorMessage(__("The invoice can't be saved at this time. Please try again later.")); return $resultRedirect->setPath('sales/order/index'); } @@ -193,9 +194,9 @@ public function execute() $transactionSave->save(); if (!empty($data['do_shipment'])) { - $this->messageManager->addSuccess(__('You created the invoice and shipment.')); + $this->messageManager->addSuccessMessage(__('You created the invoice and shipment.')); } else { - $this->messageManager->addSuccess(__('The invoice has been created.')); + $this->messageManager->addSuccessMessage(__('The invoice has been created.')); } // send invoice/shipment emails @@ -205,7 +206,7 @@ public function execute() } } catch (\Exception $e) { $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $this->messageManager->addError(__('We can\'t send the invoice email right now.')); + $this->messageManager->addErrorMessage(__('We can\'t send the invoice email right now.')); } if ($shipment) { try { @@ -214,15 +215,17 @@ public function execute() } } catch (\Exception $e) { $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $this->messageManager->addError(__('We can\'t send the shipment right now.')); + $this->messageManager->addErrorMessage(__('We can\'t send the shipment right now.')); } } $this->_objectManager->get(\Magento\Backend\Model\Session::class)->getCommentText(true); return $resultRedirect->setPath('sales/order/view', ['order_id' => $orderId]); } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__("The invoice can't be saved at this time. Please try again later.")); + $this->messageManager->addErrorMessage( + __("The invoice can't be saved at this time. Please try again later.") + ); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); } return $resultRedirect->setPath('sales/*/new', ['order_id' => $orderId]); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/VoidAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/VoidAction.php index 6c91001cbb3da..4888ed555234c 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/VoidAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/VoidAction.php @@ -34,11 +34,11 @@ public function execute() )->addObject( $invoice->getOrder() )->save(); - $this->messageManager->addSuccess(__('The invoice has been voided.')); + $this->messageManager->addSuccessMessage(__('The invoice has been voided.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('Invoice voiding error')); + $this->messageManager->addErrorMessage(__('Invoice voiding error')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/MassCancel.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/MassCancel.php index 8488e402caf69..32cf6dbfc7c35 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/MassCancel.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/MassCancel.php @@ -61,13 +61,13 @@ protected function massAction(AbstractCollection $collection) $countNonCancelOrder = $collection->count() - $countCancelOrder; if ($countNonCancelOrder && $countCancelOrder) { - $this->messageManager->addError(__('%1 order(s) cannot be canceled.', $countNonCancelOrder)); + $this->messageManager->addErrorMessage(__('%1 order(s) cannot be canceled.', $countNonCancelOrder)); } elseif ($countNonCancelOrder) { - $this->messageManager->addError(__('You cannot cancel the order(s).')); + $this->messageManager->addErrorMessage(__('You cannot cancel the order(s).')); } if ($countCancelOrder) { - $this->messageManager->addSuccess(__('We canceled %1 order(s).', $countCancelOrder)); + $this->messageManager->addSuccessMessage(__('We canceled %1 order(s).', $countCancelOrder)); } $resultRedirect = $this->resultRedirectFactory->create(); $resultRedirect->setPath($this->getComponentRefererUrl()); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/MassHold.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/MassHold.php index e894957dc8b6c..241255f68e39f 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/MassHold.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/MassHold.php @@ -62,13 +62,13 @@ protected function massAction(AbstractCollection $collection) $countNonHoldOrder = $collection->count() - $countHoldOrder; if ($countNonHoldOrder && $countHoldOrder) { - $this->messageManager->addError(__('%1 order(s) were not put on hold.', $countNonHoldOrder)); + $this->messageManager->addErrorMessage(__('%1 order(s) were not put on hold.', $countNonHoldOrder)); } elseif ($countNonHoldOrder) { - $this->messageManager->addError(__('No order(s) were put on hold.')); + $this->messageManager->addErrorMessage(__('No order(s) were put on hold.')); } if ($countHoldOrder) { - $this->messageManager->addSuccess(__('You have put %1 order(s) on hold.', $countHoldOrder)); + $this->messageManager->addSuccessMessage(__('You have put %1 order(s) on hold.', $countHoldOrder)); } $resultRedirect = $this->resultRedirectFactory->create(); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/MassUnhold.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/MassUnhold.php index 2eb54c9814ef7..5e8ea9c4ad071 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/MassUnhold.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/MassUnhold.php @@ -64,15 +64,15 @@ protected function massAction(AbstractCollection $collection) $countNonUnHoldOrder = $collection->count() - $countUnHoldOrder; if ($countNonUnHoldOrder && $countUnHoldOrder) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('%1 order(s) were not released from on hold status.', $countNonUnHoldOrder) ); } elseif ($countNonUnHoldOrder) { - $this->messageManager->addError(__('No order(s) were released from on hold status.')); + $this->messageManager->addErrorMessage(__('No order(s) were released from on hold status.')); } if ($countUnHoldOrder) { - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('%1 order(s) have been released from on hold status.', $countUnHoldOrder) ); } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/PdfDocumentsMassAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/PdfDocumentsMassAction.php index c53e4d48925c0..eeda8699a8b10 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/PdfDocumentsMassAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/PdfDocumentsMassAction.php @@ -27,7 +27,7 @@ public function execute() $collection = $this->filter->getCollection($this->getOrderCollection()->create()); return $this->massAction($collection); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); return $resultRedirect->setPath($this->redirectUrl); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php index f96e2fd09c2b0..d53ff790f4171 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php @@ -82,7 +82,7 @@ protected function massAction(AbstractCollection $collection) $creditmemoCollection = $this->collectionFactory->create()->setOrderFilter(['in' => $collection->getAllIds()]); if (!$creditmemoCollection->getSize()) { - $this->messageManager->addError(__('There are no printable documents related to selected orders.')); + $this->messageManager->addErrorMessage(__('There are no printable documents related to selected orders.')); return $this->resultRedirectFactory->create()->setPath($this->getComponentRefererUrl()); } return $this->fileFactory->create( diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php index d68cfe696b0ef..06e027308a87c 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php @@ -134,7 +134,7 @@ protected function massAction(AbstractCollection $collection) } if (empty($documents)) { - $this->messageManager->addError(__('There are no printable documents related to selected orders.')); + $this->messageManager->addErrorMessage(__('There are no printable documents related to selected orders.')); return $this->resultRedirectFactory->create()->setPath($this->getComponentRefererUrl()); } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfinvoices.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfinvoices.php index fee124a91410d..1b4af8f9764d0 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfinvoices.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfinvoices.php @@ -80,7 +80,7 @@ protected function massAction(AbstractCollection $collection) { $invoicesCollection = $this->collectionFactory->create()->setOrderFilter(['in' => $collection->getAllIds()]); if (!$invoicesCollection->getSize()) { - $this->messageManager->addError(__('There are no printable documents related to selected orders.')); + $this->messageManager->addErrorMessage(__('There are no printable documents related to selected orders.')); return $this->resultRedirectFactory->create()->setPath($this->getComponentRefererUrl()); } return $this->fileFactory->create( diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfshipments.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfshipments.php index 1aa5bfdb83878..e13962ae9cc19 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfshipments.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfshipments.php @@ -84,7 +84,7 @@ protected function massAction(AbstractCollection $collection) ->create() ->setOrderFilter(['in' => $collection->getAllIds()]); if (!$shipmentsCollection->getSize()) { - $this->messageManager->addError(__('There are no printable documents related to selected orders.')); + $this->messageManager->addErrorMessage(__('There are no printable documents related to selected orders.')); return $this->resultRedirectFactory->create()->setPath($this->getComponentRefererUrl()); } return $this->fileFactory->create( diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/ReviewPayment.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/ReviewPayment.php index 88abdb76d26f7..09a52a113617a 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/ReviewPayment.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/ReviewPayment.php @@ -53,15 +53,15 @@ public function execute() throw new \Exception(sprintf('Action "%s" is not supported.', $action)); } $this->orderRepository->save($order); - $this->messageManager->addSuccess($message); + $this->messageManager->addSuccessMessage($message); } else { $resultRedirect->setPath('sales/*/'); return $resultRedirect; } } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('We can\'t update the payment right now.')); + $this->messageManager->addErrorMessage(__('We can\'t update the payment right now.')); $this->logger->critical($e); } $resultRedirect->setPath('sales/order/view', ['order_id' => $order->getEntityId()]); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/AssignPost.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/AssignPost.php index 89820b41a68da..3b98d206d5f66 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/AssignPost.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/AssignPost.php @@ -26,18 +26,18 @@ public function execute() if ($status && $status->getStatus()) { try { $status->assignState($state, $isDefault, $visibleOnFront); - $this->messageManager->addSuccess(__('You assigned the order status.')); + $this->messageManager->addSuccessMessage(__('You assigned the order status.')); return $resultRedirect->setPath('sales/*/'); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('Something went wrong while assigning the order status.') ); } } else { - $this->messageManager->addError(__('We can\'t find this order status.')); + $this->messageManager->addErrorMessage(__('We can\'t find this order status.')); } return $resultRedirect->setPath('sales/*/assign'); } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Edit.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Edit.php index 69e051b20c4dd..7a4a1918bc1a2 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Edit.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Edit.php @@ -48,7 +48,7 @@ public function execute() $resultPage->getConfig()->getTitle()->prepend(__('Edit Order Status')); return $resultPage; } else { - $this->messageManager->addError(__('We can\'t find this order status.')); + $this->messageManager->addErrorMessage(__('We can\'t find this order status.')); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('sales/'); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php index 849a7e2d0c817..57ac3776fc3e9 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php @@ -40,7 +40,8 @@ public function execute() $status = $this->_objectManager->create(\Magento\Sales\Model\Order\Status::class)->load($statusCode); // check if status exist if ($isNew && $status->getStatus()) { - $this->messageManager->addError(__('We found another order status with the same order status code.')); + $this->messageManager + ->addErrorMessage(__('We found another order status with the same order status code.')); $this->_getSession()->setFormData($data); return $resultRedirect->setPath('sales/*/new'); } @@ -49,12 +50,12 @@ public function execute() try { $status->save(); - $this->messageManager->addSuccess(__('You saved the order status.')); + $this->messageManager->addSuccessMessage(__('You saved the order status.')); return $resultRedirect->setPath('sales/*/'); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('We can\'t add the order status right now.') ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Unassign.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Unassign.php index 04db430e1ffa4..2723d483dd38b 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Unassign.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Unassign.php @@ -18,17 +18,17 @@ public function execute() if ($status) { try { $status->unassignState($state); - $this->messageManager->addSuccess(__('You have unassigned the order status.')); + $this->messageManager->addSuccessMessage(__('You have unassigned the order status.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('Something went wrong while unassigning the order.') ); } } else { - $this->messageManager->addError(__('We can\'t find this order status.')); + $this->messageManager->addErrorMessage(__('We can\'t find this order status.')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Unhold.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Unhold.php index fa9676856a442..8d7f27069eeb7 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Unhold.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Unhold.php @@ -23,7 +23,7 @@ public function execute() { $resultRedirect = $this->resultRedirectFactory->create(); if (!$this->isValidPostRequest()) { - $this->messageManager->addError(__('Can\'t unhold order.')); + $this->messageManager->addErrorMessage(__('Can\'t unhold order.')); return $resultRedirect->setPath('sales/*/'); } $order = $this->_initOrder(); @@ -33,11 +33,11 @@ public function execute() throw new \Magento\Framework\Exception\LocalizedException(__('Can\'t unhold order.')); } $this->orderManagement->unHold($order->getEntityId()); - $this->messageManager->addSuccess(__('You released the order from holding status.')); + $this->messageManager->addSuccessMessage(__('You released the order from holding status.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('The order was not on hold.')); + $this->messageManager->addErrorMessage(__('The order was not on hold.')); } $resultRedirect->setPath('sales/order/view', ['order_id' => $order->getId()]); return $resultRedirect; diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/View.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/View.php index 7be17f865312e..8906d11dd776f 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/View.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/View.php @@ -31,7 +31,7 @@ public function execute() $resultPage->getConfig()->getTitle()->prepend(__('Orders')); } catch (\Exception $e) { $this->logger->critical($e); - $this->messageManager->addError(__('Exception occurred during order load')); + $this->messageManager->addErrorMessage(__('Exception occurred during order load')); $resultRedirect->setPath('sales/order/index'); return $resultRedirect; } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/View/Giftmessage/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/View/Giftmessage/Save.php index 61dc325c05c91..88fdbe58271f6 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/View/Giftmessage/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/View/Giftmessage/Save.php @@ -18,9 +18,9 @@ public function execute() $this->getRequest()->getParam('giftmessage') )->saveAllInOrder(); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('Something went wrong while saving the gift message.')); + $this->messageManager->addErrorMessage(__('Something went wrong while saving the gift message.')); } if ($this->getRequest()->getParam('type') == 'order_item') { diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/VoidPayment.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/VoidPayment.php index 3e7ce50f9a6f1..4ead8885087c3 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/VoidPayment.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/VoidPayment.php @@ -21,11 +21,11 @@ public function execute() // workaround for backwards compatibility $order->getPayment()->void(new \Magento\Framework\DataObject()); $order->save(); - $this->messageManager->addSuccess(__('The payment has been voided.')); + $this->messageManager->addSuccessMessage(__('The payment has been voided.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('We can\'t void the payment right now.')); + $this->messageManager->addErrorMessage(__('We can\'t void the payment right now.')); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); } $resultRedirect->setPath('sales/*/view', ['order_id' => $order->getId()]); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Transactions.php b/app/code/Magento/Sales/Controller/Adminhtml/Transactions.php index d5c1186964076..e344b258c5198 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Transactions.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Transactions.php @@ -82,7 +82,7 @@ protected function _initTransaction() ); if (!$txn->getId()) { - $this->messageManager->addError(__('Please correct the transaction ID and try again.')); + $this->messageManager->addErrorMessage(__('Please correct the transaction ID and try again.')); $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); return false; } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Transactions/Fetch.php b/app/code/Magento/Sales/Controller/Adminhtml/Transactions/Fetch.php index e77c65716fdd7..1b72ce3f13e33 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Transactions/Fetch.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Transactions/Fetch.php @@ -38,11 +38,11 @@ public function execute() ->setOrder($txn->getOrder()) ->importTransactionInfo($txn); $txn->save(); - $this->messageManager->addSuccess(__('The transaction details have been updated.')); + $this->messageManager->addSuccessMessage(__('The transaction details have been updated.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('We can\'t update the transaction details.')); + $this->messageManager->addErrorMessage(__('We can\'t update the transaction details.')); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); } diff --git a/app/code/Magento/Sales/Helper/Guest.php b/app/code/Magento/Sales/Helper/Guest.php index 8407ce5a8d7cb..a3f2ac6ba3556 100644 --- a/app/code/Magento/Sales/Helper/Guest.php +++ b/app/code/Magento/Sales/Helper/Guest.php @@ -165,7 +165,7 @@ public function loadValidOrder(App\RequestInterface $request) $this->coreRegistry->register('current_order', $order); return true; } catch (InputException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); return $this->resultRedirectFactory->create()->setPath('sales/guest/form'); } } diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 07716ff8274c6..0fb9ab5833055 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -1075,7 +1075,7 @@ public function addProducts(array $products) try { $this->addProduct($productId, $config); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { return $e; } @@ -2002,7 +2002,7 @@ protected function _validate() $logger = ObjectManager::getInstance()->get(LoggerInterface::class); foreach ($this->_errors as $error) { $logger->error($error); - $this->messageManager->addError($error); + $this->messageManager->addErrorMessage($error); } throw new \Magento\Framework\Exception\LocalizedException(__('Validation is failed.')); diff --git a/app/code/Magento/Sales/Model/AdminOrder/EmailSender.php b/app/code/Magento/Sales/Model/AdminOrder/EmailSender.php index 8ba0b5b071395..4e068eb571deb 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/EmailSender.php +++ b/app/code/Magento/Sales/Model/AdminOrder/EmailSender.php @@ -55,7 +55,7 @@ public function send(Order $order) $this->orderSender->send($order); } catch (\Magento\Framework\Exception\MailException $exception) { $this->logger->critical($exception); - $this->messageManager->addWarning( + $this->messageManager->addWarningMessage( __('You did not email your customer. Please check your email settings.') ); return false; diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/EmailTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/EmailTest.php index c6f1be2a88540..cece68c544779 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/EmailTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/EmailTest.php @@ -98,7 +98,10 @@ protected function setUp() \Magento\Framework\ObjectManager\ObjectManager::class, ['create'] ); - $this->messageManager = $this->createPartialMock(\Magento\Framework\Message\Manager::class, ['addSuccess']); + $this->messageManager = $this->createPartialMock( + \Magento\Framework\Message\Manager::class, + ['addSuccessMessage'] + ); $this->session = $this->createPartialMock(\Magento\Backend\Model\Session::class, ['setIsUrlNotice']); $this->actionFlag = $this->createPartialMock(\Magento\Framework\App\ActionFlag::class, ['get']); $this->helper = $this->createPartialMock(\Magento\Backend\Helper\Data::class, ['getUrl']); @@ -147,7 +150,7 @@ public function testEmail() ->method('notify') ->willReturn(true); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('You sent the message.'); $this->assertInstanceOf( diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Invoice/AbstractInvoice/EmailTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Invoice/AbstractInvoice/EmailTest.php index f71d98f94a82a..e7182878b4a4a 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Invoice/AbstractInvoice/EmailTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Invoice/AbstractInvoice/EmailTest.php @@ -196,7 +196,7 @@ public function testEmail() ->with($invoiceId) ->willReturn(true); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('You sent the message.'); $this->resultRedirectFactory->expects($this->atLeastOnce()) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php index 363f78e738f12..9f8c3a60bacc5 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php @@ -77,7 +77,7 @@ protected function setUp() ->disableOriginalConstructor()->getMock(); $this->messageManager = $this->createPartialMock( \Magento\Framework\Message\Manager::class, - ['addSuccess', 'addError'] + ['addSuccessMessage', 'addErrorMessage'] ); $this->orderRepositoryMock = $this->getMockBuilder(\Magento\Sales\Api\OrderRepositoryInterface::class) ->disableOriginalConstructor() @@ -117,7 +117,7 @@ public function testExecuteNotPost() ->method('isPost') ->willReturn(false); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('You have not canceled the item.'); $this->resultRedirect->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ProcessDataTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ProcessDataTest.php index e818b1edbc1d7..2bc33b3bad6de 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ProcessDataTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ProcessDataTest.php @@ -226,7 +226,11 @@ public function testExecute($noDiscount, $couponCode, $errorMessage, $actualCoup ); $this->escaper->expects($this->once())->method('escapeHtml')->with($couponCode)->willReturn($couponCode); - $this->messageManager->expects($this->once())->method('addError')->with($errorMessageManager)->willReturnSelf(); + $this->messageManager + ->expects($this->once()) + ->method('addErrorMessage') + ->with($errorMessageManager) + ->willReturnSelf(); $this->resultForward->expects($this->once()) ->method('forward') diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php index 1fdd9759f5045..f2ec969eb83fe 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php @@ -189,7 +189,7 @@ public function testExecuteRedirectBack() $this->createRedirect(); $this->getOrderId($this->orderId); $this->getUnavailableProducts([1, 3]); - $this->messageManagerMock->expects($this->any())->method('addErrorMessage'); + $this->messageManagerMock->expects($this->any())->method('addErrorMessageMessage'); $this->setPath('sales/order/view', ['order_id' => $this->orderId]); $this->assertInstanceOf(Redirect::class, $this->reorder->execute()); diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/CancelTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/CancelTest.php index af218903a3868..3e9e6af0b4181 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/CancelTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/CancelTest.php @@ -292,7 +292,7 @@ public function testExecute() ->method('cancel') ->with($creditmemoId); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('The credit memo has been canceled.'); $this->resultRedirectFactoryMock->expects($this->once()) ->method('create') diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/SaveTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/SaveTest.php index 490203563bb36..0112d09eb735f 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/SaveTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/SaveTest.php @@ -235,7 +235,7 @@ public function testSaveActionWithNegativeCreditmemo() */ protected function _setSaveActionExpectationForMageCoreException($data, $errorMessage) { - $this->_messageManager->expects($this->once())->method('addError')->with($this->equalTo($errorMessage)); + $this->_messageManager->expects($this->once())->method('addErrorMessage')->with($this->equalTo($errorMessage)); $this->_sessionMock->expects($this->once())->method('setFormData')->with($this->equalTo($data)); } } diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/VoidActionTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/VoidActionTest.php index 7e0163002b437..04adb63bc88ac 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/VoidActionTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/VoidActionTest.php @@ -335,7 +335,7 @@ public function testExecute() ->method('getInvoice') ->willReturn($invoiceMock); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('You voided the credit memo.'); $this->resultRedirectFactoryMock->expects($this->once()) ->method('create') diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/EmailTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/EmailTest.php index bb826be85850c..6286cd521d9db 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/EmailTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/EmailTest.php @@ -119,7 +119,7 @@ protected function setUp() ->disableOriginalConstructor()->getMock(); $this->messageManager = $this->createPartialMock( \Magento\Framework\Message\Manager::class, - ['addSuccess', 'addError'] + ['addSuccessMessage', 'addErrorMessage'] ); $this->orderMock = $this->getMockBuilder(\Magento\Sales\Api\Data\OrderInterface::class) @@ -171,7 +171,7 @@ public function testEmail() ->with($orderId) ->willReturn(true); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('You sent the order email.'); $this->resultRedirect->expects($this->once()) ->method('setPath') @@ -200,7 +200,7 @@ public function testEmailNoOrderId() ) ); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('This order no longer exists.'); $this->actionFlag->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/HoldTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/HoldTest.php index 68ce75781db78..e7245016c0d74 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/HoldTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/HoldTest.php @@ -77,7 +77,7 @@ protected function setUp() ->disableOriginalConstructor()->getMock(); $this->messageManager = $this->createPartialMock( \Magento\Framework\Message\Manager::class, - ['addSuccess', 'addError'] + ['addSuccessMessage', 'addErrorMessage'] ); $this->orderRepositoryMock = $this->getMockBuilder(\Magento\Sales\Api\OrderRepositoryInterface::class) ->disableOriginalConstructor() @@ -117,7 +117,7 @@ public function testExecuteNotPost() ->method('isPost') ->willReturn(false); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('You have not put the order on hold.'); $this->resultRedirect->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/CancelTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/CancelTest.php index 83b897656d185..667b6775384cf 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/CancelTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/CancelTest.php @@ -213,7 +213,7 @@ public function testExecute() ->method('save'); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('You canceled the invoice.'); $this->invoiceRepository->expects($this->once()) @@ -295,7 +295,7 @@ public function testExecuteModelException() ->will($this->throwException($e)); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($message); $invoiceMock->expects($this->once()) @@ -343,7 +343,7 @@ public function testExecuteException() ->will($this->throwException($e)); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($message); $invoiceMock->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/CaptureTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/CaptureTest.php index 0fabc24dd4421..fd53d91b9d9ea 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/CaptureTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/CaptureTest.php @@ -223,7 +223,7 @@ public function testExecute() ->method('save'); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('The invoice has been captured.'); $invoiceMock->expects($this->once()) @@ -304,7 +304,7 @@ public function testExecuteModelException() ->getMock(); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($message); $invoiceMock->expects($this->once()) @@ -355,7 +355,7 @@ public function testExecuteException() ->getMock(); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($message); $invoiceMock->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/SaveTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/SaveTest.php index 3ffa0971770b1..17dc3f42a2fe2 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/SaveTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/SaveTest.php @@ -122,7 +122,7 @@ public function testExecuteNotValidPost() ->method('isPost') ->willReturn(false); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with("The invoice can't be saved at this time. Please try again later."); $redirectMock->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/VoidActionTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/VoidActionTest.php index d7766d79e9c30..0fbff061650f8 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/VoidActionTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/VoidActionTest.php @@ -250,7 +250,7 @@ public function testExecute() ->will($this->returnValue($transactionMock)); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('The invoice has been voided.'); $resultRedirect = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) @@ -283,9 +283,9 @@ public function testExecuteNoInvoice() ->willReturn(null); $this->messageManagerMock->expects($this->never()) - ->method('addError'); + ->method('addErrorMessage'); $this->messageManagerMock->expects($this->never()) - ->method('addSuccess'); + ->method('addSuccessMessage'); $resultForward = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Forward::class) ->disableOriginalConstructor() @@ -334,7 +334,7 @@ public function testExecuteModelException() ->willReturn($invoiceMock); $this->messageManagerMock->expects($this->once()) - ->method('addError'); + ->method('addErrorMessage'); $resultRedirect = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) ->disableOriginalConstructor() diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassCancelTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassCancelTest.php index 756bade3c83c9..8e2620135255b 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassCancelTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassCancelTest.php @@ -201,11 +201,11 @@ public function testExecuteCanCancelOneOrder() $this->orderManagementMock->expects($this->at(1))->method('cancel')->with($order2id)->willReturn(false); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('1 order(s) cannot be canceled.'); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('We canceled 1 order(s).'); $this->resultRedirectMock->expects($this->once()) @@ -251,7 +251,7 @@ public function testExcludedCannotCancelOrders() $this->orderManagementMock->expects($this->atLeastOnce())->method('cancel')->willReturn(false); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('You cannot cancel the order(s).'); $this->resultRedirectMock->expects($this->once()) @@ -283,7 +283,7 @@ public function testException() $this->orderManagementMock->expects($this->atLeastOnce())->method('cancel')->willThrowException($exception); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Can not cancel'); $this->massAction->execute(); diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassHoldTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassHoldTest.php index 02ff208445596..6bfbdf24b45a9 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassHoldTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassHoldTest.php @@ -196,11 +196,11 @@ public function testExecuteOneOrderPutOnHold() ->willReturn(false); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('1 order(s) were not put on hold.'); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('You have put 1 order(s) on hold.'); $this->resultRedirectMock->expects($this->once()) @@ -240,7 +240,7 @@ public function testExecuteNoOrdersPutOnHold() ->willReturn(false); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('No order(s) were put on hold.'); $this->resultRedirectMock->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassUnholdTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassUnholdTest.php index cddb503925987..096abdce6d2a9 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassUnholdTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassUnholdTest.php @@ -196,11 +196,11 @@ public function testExecuteOneOrdersReleasedFromHold() $this->orderManagementMock->expects($this->atLeastOnce())->method('unHold')->willReturn(true); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('1 order(s) were not released from on hold status.'); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('1 order(s) have been released from on hold status.'); $this->resultRedirectMock->expects($this->once()) @@ -239,7 +239,7 @@ public function testExecuteNoReleasedOrderFromHold() ->willReturn(false); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('No order(s) were released from on hold status.'); $this->resultRedirectMock->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ReviewPaymentTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ReviewPaymentTest.php index 27bbbd4eb0043..9d5b33b4a0b59 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ReviewPaymentTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ReviewPaymentTest.php @@ -75,7 +75,7 @@ protected function setUp() ->getMockForAbstractClass(); $this->messageManagerMock = $this->createPartialMock( \Magento\Framework\Message\Manager::class, - ['addSuccess', 'addError'] + ['addSuccessMessage', 'addErrorMessage'] ); $this->resultRedirectFactoryMock = $this->createPartialMock( @@ -137,7 +137,7 @@ public function testExecuteUpdateAction() $this->paymentMock->expects($this->once())->method('update'); $this->paymentMock->expects($this->any())->method('getIsTransactionApproved')->willReturn(true); - $this->messageManagerMock->expects($this->once())->method('addSuccess'); + $this->messageManagerMock->expects($this->once())->method('addSuccessMessage'); $this->resultRedirectMock->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/UnholdTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/UnholdTest.php index 7720b7615c3fa..efa9235dc42f5 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/UnholdTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/UnholdTest.php @@ -77,7 +77,7 @@ protected function setUp() ->disableOriginalConstructor()->getMock(); $this->messageManager = $this->createPartialMock( \Magento\Framework\Message\Manager::class, - ['addSuccess', 'addError'] + ['addSuccessMessage', 'addErrorMessage'] ); $this->orderRepositoryMock = $this->getMockBuilder(\Magento\Sales\Api\OrderRepositoryInterface::class) ->disableOriginalConstructor() @@ -117,7 +117,7 @@ public function testExecuteNotPost() ->method('isPost') ->willReturn(false); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Can\'t unhold order.'); $this->resultRedirect->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ViewTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ViewTest.php index 97b3fe630aa52..9884f1604c756 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ViewTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ViewTest.php @@ -251,7 +251,7 @@ public function testGlobalException() ->method('critical') ->with($exception); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Exception occurred during order load') ->willReturnSelf(); $this->setPath('sales/order/index'); @@ -292,7 +292,7 @@ protected function initOrderSuccess() protected function initOrderFail() { $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('This order no longer exists.') ->willReturnSelf(); $this->actionFlagMock->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/PdfDocumentsMassActionTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/PdfDocumentsMassActionTest.php index 0841e239429a5..b201ed0753554 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/PdfDocumentsMassActionTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/PdfDocumentsMassActionTest.php @@ -46,7 +46,7 @@ protected function setUp() $this->messageManager = $this->createPartialMock( \Magento\Framework\Message\Manager::class, - ['addSuccess', 'addError'] + ['addSuccessMessage', 'addErrorMessage'] ); $this->orderCollectionMock = $this->createMock(\Magento\Sales\Model\ResourceModel\Order\Collection::class); @@ -88,7 +88,7 @@ public function testExecute() ->method('getCollection') ->with($this->orderCollectionMock) ->willThrowException($exception); - $this->messageManager->expects($this->once())->method('addError'); + $this->messageManager->expects($this->once())->method('addErrorMessage'); $this->resultRedirect->expects($this->once())->method('setPath')->willReturnSelf(); $this->controller->execute($exception); diff --git a/app/code/Magento/Sales/Test/Unit/Model/AdminOrder/EmailSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/AdminOrder/EmailSenderTest.php index ccc0bfe309f0c..87dd8b2be5a30 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/AdminOrder/EmailSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/AdminOrder/EmailSenderTest.php @@ -57,7 +57,7 @@ public function testSendFailure() ->method('send') ->willThrowException(new \Magento\Framework\Exception\MailException(__('test message'))); $this->messageManagerMock->expects($this->once()) - ->method('addWarning'); + ->method('addWarningMessage'); $this->loggerMock->expects($this->once()) ->method('critical'); From 8237f4fab291e78dacee7e2370d716fd18dd9003 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Tue, 7 Aug 2018 10:21:16 -0300 Subject: [PATCH 0768/1171] Updated the annotations and added translation for Magento_Search module. --- app/code/Magento/Search/Block/Adminhtml/Dashboard/Last.php | 2 ++ app/code/Magento/Search/Block/Adminhtml/Term/Edit/Form.php | 2 ++ app/code/Magento/Search/Block/Term.php | 2 ++ .../Magento/Search/Controller/Adminhtml/Synonyms/Edit.php | 7 +++---- .../Search/Controller/Adminhtml/Term/ExportSearchCsv.php | 1 + .../Search/Controller/Adminhtml/Term/ExportSearchExcel.php | 1 + app/code/Magento/Search/Controller/Term/Popular.php | 1 + app/code/Magento/Search/i18n/pt_BR.csv | 2 +- 8 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Search/Block/Adminhtml/Dashboard/Last.php b/app/code/Magento/Search/Block/Adminhtml/Dashboard/Last.php index 03c298b23a43e..01b9dbcd67836 100644 --- a/app/code/Magento/Search/Block/Adminhtml/Dashboard/Last.php +++ b/app/code/Magento/Search/Block/Adminhtml/Dashboard/Last.php @@ -63,6 +63,7 @@ protected function _construct() /** * @return $this + * @throws \Magento\Framework\Exception\LocalizedException */ protected function _prepareCollection() { @@ -86,6 +87,7 @@ protected function _prepareCollection() /** * @return $this + * @throws \Exception */ protected function _prepareColumns() { diff --git a/app/code/Magento/Search/Block/Adminhtml/Term/Edit/Form.php b/app/code/Magento/Search/Block/Adminhtml/Term/Edit/Form.php index 9019a127c5b64..18d6d2cf54c3a 100644 --- a/app/code/Magento/Search/Block/Adminhtml/Term/Edit/Form.php +++ b/app/code/Magento/Search/Block/Adminhtml/Term/Edit/Form.php @@ -51,6 +51,8 @@ protected function _construct() * Prepare form fields * * @return $this + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function _prepareForm() diff --git a/app/code/Magento/Search/Block/Term.php b/app/code/Magento/Search/Block/Term.php index e6c5c2b82ab5e..b27ef6b01fda2 100644 --- a/app/code/Magento/Search/Block/Term.php +++ b/app/code/Magento/Search/Block/Term.php @@ -71,6 +71,7 @@ public function __construct( * Load terms and try to sort it by names * * @return $this + * @throws \Magento\Framework\Exception\NoSuchEntityException */ protected function _loadTerms() { @@ -109,6 +110,7 @@ protected function _loadTerms() /** * @return array + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getTerms() { diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Edit.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Edit.php index 506976247327f..ff81ac3671903 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Edit.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Edit.php @@ -33,11 +33,10 @@ class Edit extends \Magento\Backend\App\Action /** * Edit constructor. * - * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\Backend\Model\Session $session - * @param \Magento\Framework\Registry $registry + * @param \Magento\Backend\App\Action\Context $context + * @param \Magento\Framework\Registry $registry * @param \Magento\Search\Controller\Adminhtml\Synonyms\ResultPageBuilder $pageBuilder - * @param \Magento\Search\Api\SynonymGroupRepositoryInterface $synGroupRepository + * @param \Magento\Search\Api\SynonymGroupRepositoryInterface $synGroupRepository */ public function __construct( \Magento\Backend\App\Action\Context $context, diff --git a/app/code/Magento/Search/Controller/Adminhtml/Term/ExportSearchCsv.php b/app/code/Magento/Search/Controller/Adminhtml/Term/ExportSearchCsv.php index 75173381b5239..128fa1c6c687c 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Term/ExportSearchCsv.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Term/ExportSearchCsv.php @@ -34,6 +34,7 @@ public function __construct( * Export search report grid to CSV format * * @return \Magento\Framework\App\ResponseInterface + * @throws \Exception */ public function execute() { diff --git a/app/code/Magento/Search/Controller/Adminhtml/Term/ExportSearchExcel.php b/app/code/Magento/Search/Controller/Adminhtml/Term/ExportSearchExcel.php index e3859bc5aefd2..d7e2b02bc5249 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Term/ExportSearchExcel.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Term/ExportSearchExcel.php @@ -34,6 +34,7 @@ public function __construct( * Export search report to Excel XML format * * @return \Magento\Framework\App\ResponseInterface + * @throws \Exception */ public function execute() { diff --git a/app/code/Magento/Search/Controller/Term/Popular.php b/app/code/Magento/Search/Controller/Term/Popular.php index acc1ce324567e..0573fe6a81a12 100644 --- a/app/code/Magento/Search/Controller/Term/Popular.php +++ b/app/code/Magento/Search/Controller/Term/Popular.php @@ -34,6 +34,7 @@ public function __construct(Context $context, ScopeConfigInterface $scopeConfig) * * @param \Magento\Framework\App\RequestInterface $request * @return \Magento\Framework\App\ResponseInterface + * @throws \Magento\Framework\Exception\NotFoundException */ public function dispatch(RequestInterface $request) { diff --git a/app/code/Magento/Search/i18n/pt_BR.csv b/app/code/Magento/Search/i18n/pt_BR.csv index 8b4b04aa3b9ec..c10566a7c9800 100644 --- a/app/code/Magento/Search/i18n/pt_BR.csv +++ b/app/code/Magento/Search/i18n/pt_BR.csv @@ -1 +1 @@ -"Search Engine","Search Engine" +"Search Engine","Mecanismo de Busca" From 1fe125161c3e9a4b94b8b9ae8c1dd46ed10b4ed4 Mon Sep 17 00:00:00 2001 From: Danny Verkade <danny@cream.nl> Date: Mon, 9 Jul 2018 11:50:47 -0400 Subject: [PATCH 0769/1171] - Removed the unused Totalbar block from the invoice create and credit memo create layouts. - Deprecated Totalbar class - Deprecated totalbar template file --- app/code/Magento/Sales/Block/Adminhtml/Order/Totalbar.php | 1 + .../Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml | 1 - .../view/adminhtml/layout/sales_order_creditmemo_updateqty.xml | 1 - .../Sales/view/adminhtml/layout/sales_order_invoice_new.xml | 1 - .../view/adminhtml/layout/sales_order_invoice_updateqty.xml | 1 - .../Magento/Sales/view/adminhtml/templates/order/totalbar.phtml | 1 + 6 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Totalbar.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Totalbar.php index 648a281228908..c4ce48d162c2c 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Totalbar.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Totalbar.php @@ -8,6 +8,7 @@ /** * Adminhtml creditmemo bar * + * @deprecated * @api * @author Magento Core Team <core@magentocommerce.com> * @since 100.0.2 diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml index 0c1b395b5116d..71490553aff17 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml @@ -22,7 +22,6 @@ <block class="Magento\Sales\Block\Adminhtml\Items\Column\Qty" name="column_qty" template="Magento_Sales::items/column/qty.phtml" group="column"/> <block class="Magento\Sales\Block\Adminhtml\Items\Column\Name" name="column_name" template="Magento_Sales::items/column/name.phtml" group="column"/> <block class="Magento\Framework\View\Element\Text\ListText" name="order_item_extra_info"/> - <block class="Magento\Sales\Block\Adminhtml\Order\Totalbar" name="order_totalbar" template="Magento_Sales::order/totalbar.phtml"/> <block class="Magento\Sales\Block\Adminhtml\Order\Creditmemo\Totals" name="creditmemo_totals" template="Magento_Sales::order/totals.phtml"> <block class="Magento\Sales\Block\Adminhtml\Order\Creditmemo\Create\Adjustments" name="adjustments" template="Magento_Sales::order/creditmemo/create/totals/adjustments.phtml"/> <block class="Magento\Sales\Block\Adminhtml\Order\Totals\Tax" name="tax" template="Magento_Sales::order/totals/tax.phtml"/> diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml index 29a61308391c6..8375bec965794 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml @@ -13,7 +13,6 @@ <block class="Magento\Sales\Block\Adminhtml\Items\Column\Qty" name="column_qty" template="Magento_Sales::items/column/qty.phtml" group="column"/> <block class="Magento\Sales\Block\Adminhtml\Items\Column\Name" name="column_name" template="Magento_Sales::items/column/name.phtml" group="column"/> <block class="Magento\Framework\View\Element\Text\ListText" name="order_item_extra_info"/> - <block class="Magento\Sales\Block\Adminhtml\Order\Totalbar" name="order_totalbar" template="Magento_Sales::order/totalbar.phtml"/> <block class="Magento\Sales\Block\Adminhtml\Order\Creditmemo\Totals" name="creditmemo_totals" template="Magento_Sales::order/totals.phtml"> <block class="Magento\Sales\Block\Adminhtml\Order\Creditmemo\Create\Adjustments" name="adjustments" template="Magento_Sales::order/creditmemo/create/totals/adjustments.phtml"/> <block class="Magento\Sales\Block\Adminhtml\Order\Totals\Tax" name="tax" template="Magento_Sales::order/totals/tax.phtml"/> diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_new.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_new.xml index def5ebaf546cd..b589ac0ac793d 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_new.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_new.xml @@ -25,7 +25,6 @@ <block class="Magento\Sales\Block\Adminhtml\Items\Column\Qty" name="column_qty" template="Magento_Sales::items/column/qty.phtml" group="column"/> <block class="Magento\Sales\Block\Adminhtml\Items\Column\Name" name="column_name" template="Magento_Sales::items/column/name.phtml" group="column"/> <block class="Magento\Framework\View\Element\Text\ListText" name="order_item_extra_info"/> - <block class="Magento\Sales\Block\Adminhtml\Order\Totalbar" name="order_totalbar" template="Magento_Sales::order/totalbar.phtml"/> <block class="Magento\Sales\Block\Adminhtml\Order\Invoice\Totals" name="invoice_totals" template="Magento_Sales::order/totals.phtml"> <block class="Magento\Sales\Block\Adminhtml\Order\Totals\Tax" name="tax" template="Magento_Sales::order/totals/tax.phtml"/> </block> diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_updateqty.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_updateqty.xml index 4df3f057f6a58..38e4cb50f4343 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_updateqty.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_updateqty.xml @@ -13,7 +13,6 @@ <block class="Magento\Sales\Block\Adminhtml\Items\Column\Qty" name="column_qty" template="Magento_Sales::items/column/qty.phtml" group="column"/> <block class="Magento\Sales\Block\Adminhtml\Items\Column\Name" name="column_name" template="Magento_Sales::items/column/name.phtml" group="column"/> <block class="Magento\Framework\View\Element\Text\ListText" name="order_item_extra_info"/> - <block class="Magento\Sales\Block\Adminhtml\Order\Totalbar" name="order_totalbar" template="Magento_Sales::order/totalbar.phtml"/> <block class="Magento\Sales\Block\Adminhtml\Order\Invoice\Totals" name="invoice_totals" template="Magento_Sales::order/totals.phtml"> <block class="Magento\Sales\Block\Adminhtml\Order\Totals\Tax" name="tax" template="Magento_Sales::order/totals/tax.phtml"/> </block> diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/totalbar.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/totalbar.phtml index af7dfbc4e2ab3..14025eb2dd51a 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/totalbar.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/totalbar.phtml @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ +// @deprecated // @codingStandardsIgnoreFile $totals = $block->getTotals(); From 2e2771d0a4e21360e94bc1fab9a8efd7c10d87e4 Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@magento.com> Date: Tue, 7 Aug 2018 17:25:56 +0300 Subject: [PATCH 0770/1171] MAGETWO-93735: [Forwardport] Grouped Product --- .../Indexer/Console/Command/IndexerSetDimensionsModeCommand.php | 1 + .../Console/Command/IndexerShowDimensionsModeCommandTest.php | 1 + .../Product/DynamicBundlePriceCalculatorWithDimensionTest.php | 1 + .../Product/FixedBundlePriceCalculatorWithDimensionTest.php | 1 + .../Magento/Bundle/Model/Product/PriceWithDimensionTest.php | 2 ++ .../Bundle/_files/product_with_tier_pricing_rollback.php | 1 + .../Price/SimpleWithOptionsTierPriceWithDimensionTest.php | 2 ++ .../Catalog/Model/Product/Type/PriceWithDimensionTest.php | 2 ++ .../Magento/Catalog/Model/ProductPriceWithDimensionTest.php | 2 ++ .../Checkout/_files/quote_with_bundle_product_rollback.php | 1 + .../Pricing/Price/SpecialPriceIndexerWithDimensionTest.php | 2 ++ .../RenderingBasedOnIsProductListFlagWithDimensionTest.php | 2 ++ 12 files changed, 18 insertions(+) diff --git a/app/code/Magento/Indexer/Console/Command/IndexerSetDimensionsModeCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerSetDimensionsModeCommand.php index 30f57d09f508b..56dcfbc061ea8 100644 --- a/app/code/Magento/Indexer/Console/Command/IndexerSetDimensionsModeCommand.php +++ b/app/code/Magento/Indexer/Console/Command/IndexerSetDimensionsModeCommand.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Indexer\Console\Command; diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowDimensionsModeCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowDimensionsModeCommandTest.php index f5487f268ac01..ad1cf9b5738ba 100644 --- a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowDimensionsModeCommandTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowDimensionsModeCommandTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Indexer\Test\Unit\Console\Command; diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php index 6794a686146f9..2123968d64892 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Model\Product; diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php index ffc24b2f45d5c..9fa6d2cb705b9 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Model\Product; diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php index 3b3b1ed5cbd07..8430a3324892d 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\Product; /** diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_tier_pricing_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_tier_pricing_rollback.php index 130d08bae23c7..7e4c5593f18bb 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_tier_pricing_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_tier_pricing_rollback.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); /* * Since the bundle product creation GUI doesn't allow to choose values for bundled products' custom options, diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php index 40607cd85b3b4..8aff52672a337 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Product\Price; use Magento\TestFramework\Helper\Bootstrap; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php index 12b7da2bd6e35..cfdbb237e9c1d 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Product\Type; use Magento\Catalog\Model\Product; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php index f1b6eba653eff..e80f5b1e4db70 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model; use Magento\TestFramework\Helper\Bootstrap; diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_bundle_product_rollback.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_bundle_product_rollback.php index ed350e34b74dd..090248820e246 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_bundle_product_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_bundle_product_rollback.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); /* * Since the bundle product creation GUI doesn't allow to choose values for bundled products' custom options, diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php index f08f0a4543ea3..f533e751d210a 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\ConfigurableProduct\Pricing\Price; use Magento\Catalog\Api\Data\ProductInterface; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php index bddbb38e9f019..249c1cd2f028f 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\ConfigurableProduct\Pricing\Render\FinalPriceBox; use Magento\Catalog\Api\Data\ProductInterface; From e521aa2c55d015f972362d78a5424124923ca974 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Tue, 7 Aug 2018 09:48:33 -0500 Subject: [PATCH 0771/1171] MC-222: Admin should be able to set/edit all the basic product attributes when creating/editing a bundle product - Remove no-op test actions that were causing a failure in Jenkins --- .../Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml index eec21dc3551d9..eeb04aeadb555 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml @@ -136,10 +136,6 @@ <!--Visibilty--> <selectOption selector="{{AdminProductFormBundleSection.visibilityDropDown}}" userInput="Not Visible Individually" stepKey="openVisibility"/> - <!--Categories--> - <click selector="{{AdminProductFormBundleSection.categoriesDropDown}}" stepKey="clickOnCategoriesDropDown"/> - <click selector="{{AdminProductFormBundleSection.categoryFieldName}}" stepKey="clickOnCategoriesFieldName"/> - <!--New from - to--> <fillField selector="{{AdminProductFormBundleSection.fromDate}}" userInput="10/20/2018" stepKey="fillInFirstDate"/> <fillField selector="{{AdminProductFormBundleSection.toDate}}" userInput="10/20/2018" stepKey="fillInSecondDate"/> From cbe9dddcf3be173b9442949378556622f980d373 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Tue, 7 Aug 2018 10:14:38 -0500 Subject: [PATCH 0772/1171] MC-3308: Automate MFTF for MC-1416 --- ...URLForMediaContentInWYSIWYGActionGroup.xml | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml deleted file mode 100644 index 4782944ea867b..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> - <actionGroup name="UseStaticURLForMediaContentInWYSIWYG"> - <arguments> - <argument name="value" defaultValue="Yes" type="string"/> - </arguments> - <amOnPage url="{{ConfigurationStoresPage.url}}" stepKey="navigateToWYSIWYGConfigPage1"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <conditionalClick stepKey="expandWYSIWYGOptions" selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.CheckIfTabExpand}}" visible="true" /> - <waitForElementVisible selector="{{ContentManagementSection.EnableWYSIWYG}}" stepKey="waitForEnableWYSIWYGDropdown1" /> - <selectOption selector="{{ContentManagementSection.StaticURL}}" userInput="{{value}}" stepKey="selectOption1"/> - <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions" /> - <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig" /> - <waitForPageLoad stepKey="waitForPageLoad2" /> - </actionGroup> -</actionGroups> From 6b6fe787ffed285b8d7b6c2588621869d1d25e42 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Tue, 7 Aug 2018 10:15:17 -0500 Subject: [PATCH 0773/1171] MC-3308: Automate MFTF for MC-1416 --- .../Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml index 52adb0b1f50a5..80351c9d97520 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml @@ -29,4 +29,17 @@ <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions" /> <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig" /> </actionGroup> + <actionGroup name="UseStaticURLForMediaContentInWYSIWYG"> + <arguments> + <argument name="value" defaultValue="Yes" type="string"/> + </arguments> + <amOnPage url="{{ConfigurationStoresPage.url}}" stepKey="navigateToWYSIWYGConfigPage1"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <conditionalClick stepKey="expandWYSIWYGOptions" selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.CheckIfTabExpand}}" visible="true" /> + <waitForElementVisible selector="{{ContentManagementSection.EnableWYSIWYG}}" stepKey="waitForEnableWYSIWYGDropdown1" /> + <selectOption selector="{{ContentManagementSection.StaticURL}}" userInput="{{value}}" stepKey="selectOption1"/> + <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions" /> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig" /> + <waitForPageLoad stepKey="waitForPageLoad2" /> + </actionGroup> </actionGroups> From 516608d7fb6ed9c311c4927a3243feffb73ef7e3 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Sat, 28 Jul 2018 10:56:50 -0300 Subject: [PATCH 0774/1171] Reverting some chenges because of the contributor guide. --- .../Backend/Helper/Dashboard/Order.php | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Backend/Helper/Dashboard/Order.php b/app/code/Magento/Backend/Helper/Dashboard/Order.php index f63017f292c5d..67aa1ae8c34ba 100644 --- a/app/code/Magento/Backend/Helper/Dashboard/Order.php +++ b/app/code/Magento/Backend/Helper/Dashboard/Order.php @@ -5,36 +5,41 @@ */ namespace Magento\Backend\Helper\Dashboard; +use Magento\Framework\App\ObjectManager; + /** * Adminhtml dashboard helper for orders * * @api * @since 100.0.2 */ -class Order extends \Magento\Backend\Helper\Dashboard\AbstractDashboard +class Order extends AbstractDashboard { /** * @var \Magento\Reports\Model\ResourceModel\Order\Collection */ - private $orderCollection; + private $_orderCollection; /** * @var \Magento\Store\Model\StoreManagerInterface * @since 100.0.6 */ - private $storeManager; + private $_storeManager; /** * @param \Magento\Framework\App\Helper\Context $context * @param \Magento\Reports\Model\ResourceModel\Order\Collection $orderCollection + * @param \Magento\Store\Model\StoreManagerInterface $storeManager */ public function __construct( \Magento\Framework\App\Helper\Context $context, \Magento\Reports\Model\ResourceModel\Order\Collection $orderCollection, - \Magento\Store\Model\StoreManagerInterface $storeManager + \Magento\Store\Model\StoreManagerInterface $storeManager = null ) { - $this->orderCollection = $orderCollection; - $this->storeManager = $storeManager; + $this->_orderCollection = $orderCollection; + $this->_storeManager = $storeManager ?: ObjectManager::getInstance() + ->get(\Magento\Store\Model\StoreManagerInterface::class); + parent::__construct($context); } @@ -48,20 +53,20 @@ protected function _initCollection() { $isFilter = $this->getParam('store') || $this->getParam('website') || $this->getParam('group'); - $this->_collection = $this->orderCollection->prepareSummary($this->getParam('period'), 0, 0, $isFilter); + $this->_collection = $this->_orderCollection->prepareSummary($this->getParam('period'), 0, 0, $isFilter); if ($this->getParam('store')) { $this->_collection->addFieldToFilter('store_id', $this->getParam('store')); } elseif ($this->getParam('website')) { - $storeIds = $this->storeManager->getWebsite($this->getParam('website'))->getStoreIds(); + $storeIds = $this->_storeManager->getWebsite($this->getParam('website'))->getStoreIds(); $this->_collection->addFieldToFilter('store_id', ['in' => implode(',', $storeIds)]); } elseif ($this->getParam('group')) { - $storeIds = $this->storeManager->getGroup($this->getParam('group'))->getStoreIds(); + $storeIds = $this->_storeManager->getGroup($this->getParam('group'))->getStoreIds(); $this->_collection->addFieldToFilter('store_id', ['in' => implode(',', $storeIds)]); } elseif (!$this->_collection->isLive()) { $this->_collection->addFieldToFilter( 'store_id', - ['eq' => $this->storeManager->getStore(\Magento\Store\Model\Store::ADMIN_CODE)->getId()] + ['eq' => $this->_storeManager->getStore(\Magento\Store\Model\Store::ADMIN_CODE)->getId()] ); } $this->_collection->load(); From fb65e2f8aef8ba0a1238f5012c2655a91381f174 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Sun, 29 Jul 2018 00:29:06 +0300 Subject: [PATCH 0775/1171] magento/magento2#17101 Refactory to Magento_Backend module class Revert backward incompatible changes --- app/code/Magento/Backend/Helper/Dashboard/Order.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Backend/Helper/Dashboard/Order.php b/app/code/Magento/Backend/Helper/Dashboard/Order.php index 67aa1ae8c34ba..c19c28b6e33eb 100644 --- a/app/code/Magento/Backend/Helper/Dashboard/Order.php +++ b/app/code/Magento/Backend/Helper/Dashboard/Order.php @@ -18,13 +18,13 @@ class Order extends AbstractDashboard /** * @var \Magento\Reports\Model\ResourceModel\Order\Collection */ - private $_orderCollection; + protected $_orderCollection; /** * @var \Magento\Store\Model\StoreManagerInterface * @since 100.0.6 */ - private $_storeManager; + protected $_storeManager; /** * @param \Magento\Framework\App\Helper\Context $context From a2feeb330f81b02609eee8f11a525924777ee114 Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv <rkostiv@magento.com> Date: Tue, 7 Aug 2018 19:10:49 +0300 Subject: [PATCH 0776/1171] MAGETWO-94060: [2.3.x] Unlink CatalogWidget from EAV indexer --- .../Model/Rule/Condition/Product.php | 11 ++++++---- .../Unit/Model/Rule/Condition/ProductTest.php | 21 +++++++++++++++---- .../Model/Condition/AbstractCondition.php | 6 +++--- .../Rule/Model/Condition/Sql/Builder.php | 16 +++++++++++++- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php index f22879df0ae0c..70e2dc9dc4f36 100644 --- a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php +++ b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php @@ -163,8 +163,6 @@ protected function addGlobalAttribute( \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute, \Magento\Catalog\Model\ResourceModel\Product\Collection $collection ) { - $storeId = $this->storeManager->getStore()->getId(); - switch ($attribute->getBackendType()) { case 'decimal': case 'datetime': @@ -174,9 +172,14 @@ protected function addGlobalAttribute( break; default: $alias = 'at_' . md5($this->getId()) . $attribute->getAttributeCode(); + + $connection = $this->_productResource->getConnection(); + $storeId = $connection->getIfNullSql($alias . '.store_id', $this->storeManager->getStore()->getId()); + $linkField = $attribute->getEntity()->getLinkField(); + $collection->getSelect()->join( - [$alias => $collection->getTable('catalog_product_index_eav')], - "($alias.entity_id = e.entity_id) AND ($alias.store_id = $storeId)" . + [$alias => $collection->getTable('catalog_product_entity_varchar')], + "($alias.$linkField = e.$linkField) AND ($alias.store_id = $storeId)" . " AND ($alias.attribute_id = {$attribute->getId()})", [] ); diff --git a/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php b/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php index 09270b6b41fc7..d255a9940ad9f 100644 --- a/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php +++ b/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php @@ -17,6 +17,11 @@ class ProductTest extends \PHPUnit\Framework\TestCase */ private $model; + /** + * @var \Magento\Catalog\Model\ResourceModel\Product|\PHPUnit_Framework_MockObject_MockObject + */ + private $productResource; + /** * @var \PHPUnit_Framework_MockObject_MockObject */ @@ -33,9 +38,9 @@ protected function setUp() $storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); $storeManager->expects($this->any())->method('getStore')->willReturn($storeMock); - $productResource = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product::class); - $productResource->expects($this->once())->method('loadAllAttributes')->willReturnSelf(); - $productResource->expects($this->once())->method('getAttributesByCode')->willReturn([]); + $this->productResource = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product::class); + $this->productResource->expects($this->once())->method('loadAllAttributes')->willReturnSelf(); + $this->productResource->expects($this->once())->method('getAttributesByCode')->willReturn([]); $productCategoryList = $this->getMockBuilder(\Magento\Catalog\Model\ProductCategoryList::class) ->disableOriginalConstructor() ->getMock(); @@ -45,7 +50,7 @@ protected function setUp() [ 'config' => $eavConfig, 'storeManager' => $storeManager, - 'productResource' => $productResource, + 'productResource' => $this->productResource, 'productCategoryList' => $productCategoryList, 'data' => [ 'rule' => $ruleMock, @@ -67,6 +72,14 @@ public function testAddToCollection() $this->attributeMock->expects($this->once())->method('isScopeGlobal')->willReturn(true); $this->attributeMock->expects($this->once())->method('isScopeGlobal')->willReturn(true); $this->attributeMock->expects($this->once())->method('getBackendType')->willReturn('multiselect'); + + $entityMock = $this->createMock(\Magento\Eav\Model\Entity\AbstractEntity::class); + $entityMock->expects($this->once())->method('getLinkField')->willReturn('entitiy_id'); + $this->attributeMock->expects($this->once())->method('getEntity')->willReturn($entityMock); + $connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); + + $this->productResource->expects($this->atLeastOnce())->method('getConnection')->willReturn($connection); + $this->model->addToCollection($collectionMock); } diff --git a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php index e3bec2d9959b1..f5ca1443a8c10 100644 --- a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php +++ b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php @@ -355,10 +355,10 @@ public function getValueParsed() { if (!$this->hasValueParsed()) { $value = $this->getData('value'); - if (is_array($value) && isset($value[0]) && is_string($value[0])) { - $value = $value[0]; + if (is_array($value) && count($value) === 1) { + $value = reset($value); } - if ($this->isArrayOperatorType() && $value) { + if (!is_array($value) && $this->isArrayOperatorType() && $value) { $value = preg_split('#\s*[,;]\s*#', $value, null, PREG_SPLIT_NO_EMPTY); } $this->setValueParsed($value); diff --git a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php index e1c9bf99f2675..7ea70d5478e10 100644 --- a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php +++ b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php @@ -163,8 +163,22 @@ protected function _getMappedSqlCondition( $this->_conditionOperatorMap[$conditionOperator] ); + $bindValue = $condition->getBindArgumentValue(); + $expression = $value . $this->_connection->quoteInto($sql, $bindValue); + + // values for multiselect attributes can be saved in comma separated format + // below is a solution for matching such conditions with selected values + if (in_array($conditionOperator, ['()', '{}']) && is_array($bindValue)) { + foreach ($bindValue as $item) { + $expression .= $this->_connection->quoteInto( + " OR (FIND_IN_SET (?, {$this->_connection->quoteIdentifier($argument)}) > 0)", + $item + ); + } + } + return $this->_expressionFactory->create( - ['expression' => $value . $this->_connection->quoteInto($sql, $condition->getBindArgumentValue())] + ['expression' => $expression] ); } From 5c310fc55d3efa860e9e7b09cf91cb0a79259873 Mon Sep 17 00:00:00 2001 From: Andrea Rivadossi <riva.andrea.web@gmail.com> Date: Fri, 1 Jun 2018 08:38:57 +0200 Subject: [PATCH 0777/1171] Fix #13445 Added the class for fix the issue #13445 --- .../view/frontend/layout/catalogsearch_result_index.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/LayeredNavigation/view/frontend/layout/catalogsearch_result_index.xml b/app/code/Magento/LayeredNavigation/view/frontend/layout/catalogsearch_result_index.xml index ab9aa485aeea2..3bb21ed09a01c 100644 --- a/app/code/Magento/LayeredNavigation/view/frontend/layout/catalogsearch_result_index.xml +++ b/app/code/Magento/LayeredNavigation/view/frontend/layout/catalogsearch_result_index.xml @@ -7,6 +7,7 @@ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> + <attribute name="class" value="page-with-filter"/> <referenceContainer name="sidebar.main"> <block class="Magento\LayeredNavigation\Block\Navigation\Search" name="catalogsearch.leftnav" before="-" template="Magento_LayeredNavigation::layer/view.phtml"> <block class="Magento\LayeredNavigation\Block\Navigation\State" name="catalogsearch.navigation.state" as="state" /> From 1296071b8c3cdb37636bffbdc8ecea170e706c78 Mon Sep 17 00:00:00 2001 From: DmitryChukhnov <d@bumba.ru> Date: Tue, 3 Jul 2018 20:22:57 +0300 Subject: [PATCH 0778/1171] Fix the special price expression. Without this fix, catalog_product_price generate incorrect price data. The priority of "OR" is lower then "AND". That's why we have to add brackets around OR-expresstion. --- .../Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 849c12238db5a..7ea85cd3f6f10 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 @@ -456,7 +456,7 @@ protected function getSelect($entityIds = null, $type = null) $specialFromExpr = "{$specialFrom} IS NULL OR {$specialFromDate} <= {$currentDate}"; $specialToExpr = "{$specialTo} IS NULL OR {$specialToDate} >= {$currentDate}"; $specialPriceExpr = $connection->getCheckSql( - "{$specialPrice} IS NOT NULL AND {$specialFromExpr} AND {$specialToExpr}", + "{$specialPrice} IS NOT NULL AND ({$specialFromExpr}) AND ({$specialToExpr})", $specialPrice, $maxUnsignedBigint ); From acf8f9ff281a0fb097ed40b8e58e16abbfec48c9 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Tue, 7 Aug 2018 11:15:27 -0500 Subject: [PATCH 0779/1171] MQE-1175: Run upgrade script to change all schema paths to urns --- app/code/Magento/Analytics/Test/Mftf/Data/UserData.xml | 2 +- app/code/Magento/Analytics/Test/Mftf/Data/UserRoleData.xml | 2 +- app/code/Magento/Analytics/Test/Mftf/Metadata/user-meta.xml | 2 +- .../Magento/Analytics/Test/Mftf/Metadata/user_role-meta.xml | 2 +- .../Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml | 2 +- .../Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml | 2 +- .../Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml | 2 +- .../Test/Mftf/Test/AdminConfigurationPermissionTest.xml | 2 +- .../Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml | 2 +- .../Magento/Backend/Test/Mftf/ActionGroup/LoginActionGroup.xml | 2 +- .../Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml | 2 +- .../Magento/Backend/Test/Mftf/ActionGroup/LogoutActionGroup.xml | 2 +- .../Backend/Test/Mftf/ActionGroup/SecondaryGridActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/SortByIdDescendingActionGroup.xml | 2 +- app/code/Magento/Backend/Test/Mftf/Data/BackenedData.xml | 2 +- .../Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml | 2 +- app/code/Magento/Backend/Test/Mftf/Page/AdminLoginPage.xml | 2 +- app/code/Magento/Backend/Test/Mftf/Page/AdminLogoutPage.xml | 2 +- .../Backend/Test/Mftf/Section/AdminConfirmationModalSection.xml | 2 +- .../Magento/Backend/Test/Mftf/Section/AdminGridTableSection.xml | 2 +- .../Magento/Backend/Test/Mftf/Section/AdminHeaderSection.xml | 2 +- .../Magento/Backend/Test/Mftf/Section/AdminLoginFormSection.xml | 2 +- .../Backend/Test/Mftf/Section/AdminMainActionsSection.xml | 2 +- .../Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml | 2 +- .../Backend/Test/Mftf/Section/AdminSecondaryGridSection.xml | 2 +- app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml | 2 +- app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml | 2 +- .../Braintree/Test/Mftf/Metadata/braintree_config-meta.xml | 2 +- .../Test/Mftf/ActionGroup/AdminBundleProductActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AdminClearFiltersActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/BundleProductFilterActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/CreateBundleProductActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/EnableDisableProductActionGroup.xml | 2 +- .../Mftf/ActionGroup/SetBundleProductAttributesActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml | 2 +- app/code/Magento/Bundle/Test/Mftf/Data/BundleLinkData.xml | 2 +- app/code/Magento/Bundle/Test/Mftf/Data/BundleOptionData.xml | 2 +- app/code/Magento/Bundle/Test/Mftf/Data/CustomAttributeData.xml | 2 +- app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml | 2 +- app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_link-meta.xml | 2 +- .../Magento/Bundle/Test/Mftf/Metadata/bundle_option-meta.xml | 2 +- .../Magento/Bundle/Test/Mftf/Metadata/bundle_options-meta.xml | 2 +- .../Magento/Bundle/Test/Mftf/Page/AdminCatalogProductPage.xml | 2 +- .../Magento/Bundle/Test/Mftf/Page/AdminProductCreatePage.xml | 2 +- .../Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml | 2 +- .../Bundle/Test/Mftf/Section/BundleStorefrontSection.xml | 2 +- .../Bundle/Test/Mftf/Section/StorefrontBundledSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCategoryProductSection.xml | 2 +- .../Bundle/Test/Mftf/Section/StorefrontProductActionSection.xml | 2 +- .../Test/Mftf/Section/StorefrontProductInfoMainSection.xml | 2 +- .../Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml | 2 +- .../Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml | 2 +- .../Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml | 2 +- .../Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml | 2 +- .../Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml | 2 +- .../Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml | 2 +- .../Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml | 2 +- .../Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml | 2 +- .../Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml | 2 +- .../Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml | 2 +- .../Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml | 2 +- .../Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml | 2 +- .../Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml | 2 +- app/code/Magento/Bundle/Test/Mftf/Test/EndToEndB2CAdminTest.xml | 2 +- .../Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml | 2 +- .../Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml | 2 +- .../Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml | 2 +- .../Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml | 2 +- .../Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml | 2 +- .../Test/StorefrontBundleProductShownInCategoryListAndGrid.xml | 2 +- .../Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml | 2 +- .../Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml | 2 +- .../Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml | 2 +- ...ifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml | 2 +- .../Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml | 2 +- .../Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml | 2 +- .../Mftf/ActionGroup/AdminCreateRootCategoryActionGroup.xml | 2 +- .../Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml | 2 +- .../Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml | 2 +- .../AssertProductInStorefrontCategoryPageActionGroup.xml | 2 +- .../AssertProductInStorefrontProductPageActionGroup.xml | 2 +- .../ActionGroup/CheckItemInLayeredNavigationActionGroup.xml | 2 +- .../Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml | 2 +- .../Catalog/Test/Mftf/ActionGroup/MoveCategoryActionGroup.xml | 2 +- .../Mftf/ActionGroup/OpenEditProductOnBackendActionGroup.xml | 2 +- .../Mftf/ActionGroup/OpenProductFromCategoryPageActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/SearchAndMultiselectActionGroup.xml | 2 +- .../Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml | 2 +- .../StorefrontAddToCartCustomOptionsProductPageActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/StorefrontCompareActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Data/ConstData.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Data/ImageContentData.xml | 2 +- .../Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml | 2 +- .../Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml | 2 +- .../Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml | 2 +- .../Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml | 2 +- .../Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Data/ProductGridData.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml | 2 +- .../Magento/Catalog/Test/Mftf/Data/ProductOptionValueData.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Metadata/category-meta.xml | 2 +- .../Catalog/Test/Mftf/Metadata/custom_attribute-meta.xml | 2 +- .../Test/Mftf/Metadata/empty_extension_attribute-meta.xml | 2 +- .../Magento/Catalog/Test/Mftf/Metadata/frontend_label-meta.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml | 2 +- .../Catalog/Test/Mftf/Metadata/product_attribute-meta.xml | 2 +- .../Metadata/product_attribute_media_gallery_entry-meta.xml | 2 +- .../Test/Mftf/Metadata/product_attribute_option-meta.xml | 2 +- .../Catalog/Test/Mftf/Metadata/product_attribute_set-meta.xml | 2 +- .../Test/Mftf/Metadata/product_extension_attribute-meta.xml | 2 +- .../Magento/Catalog/Test/Mftf/Metadata/product_link-meta.xml | 2 +- .../Mftf/Metadata/product_link_extension_attribute-meta.xml | 2 +- .../Magento/Catalog/Test/Mftf/Metadata/product_links-meta.xml | 2 +- .../Magento/Catalog/Test/Mftf/Metadata/product_option-meta.xml | 2 +- .../Catalog/Test/Mftf/Metadata/product_option_value-meta.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Metadata/stock_item-meta.xml | 2 +- .../Magento/Catalog/Test/Mftf/Metadata/store_label-meta.xml | 2 +- .../Magento/Catalog/Test/Mftf/Metadata/validation_rule-meta.xml | 2 +- .../Magento/Catalog/Test/Mftf/Page/AdminCategoryEditPage.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml | 2 +- .../Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml | 2 +- .../Catalog/Test/Mftf/Page/AdminProductAttributeGridPage.xml | 2 +- .../Catalog/Test/Mftf/Page/AdminProductAttributeSetEditPage.xml | 2 +- .../Catalog/Test/Mftf/Page/AdminProductAttributeSetGridPage.xml | 2 +- .../Catalog/Test/Mftf/Page/AdminProductAttributesEditPage.xml | 2 +- .../Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml | 2 +- .../Magento/Catalog/Test/Mftf/Page/AdminProductEditPage.xml | 2 +- .../Magento/Catalog/Test/Mftf/Page/AdminProductIndexPage.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Page/ProductCatalogPage.xml | 2 +- .../Magento/Catalog/Test/Mftf/Page/StorefrontCategoryPage.xml | 2 +- .../Catalog/Test/Mftf/Page/StorefrontProductComparePage.xml | 2 +- .../Magento/Catalog/Test/Mftf/Page/StorefrontProductPage.xml | 2 +- .../Test/Mftf/Section/AdminAddProductsToOptionPanelSection.xml | 2 +- .../Test/Mftf/Section/AdminCategoryBasicFieldSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml | 2 +- .../Test/Mftf/Section/AdminCategoryMainActionsSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminCategoryMessagesSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminCategoryModalSection.xml | 2 +- .../Test/Mftf/Section/AdminCategoryProductsGridSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminCategorySEOSection.xml | 2 +- .../Test/Mftf/Section/AdminCategorySidebarActionSection.xml | 2 +- .../Test/Mftf/Section/AdminCategorySidebarTreeSection.xml | 2 +- .../Mftf/Section/AdminCategoryWarningMessagesPopupSection.xml | 2 +- .../Test/Mftf/Section/AdminCreateProductAttributeSection.xml | 2 +- .../Test/Mftf/Section/AdminEditProductAttributesSection.xml | 2 +- .../Test/Mftf/Section/AdminProductAttributeGridSection.xml | 2 +- .../Test/Mftf/Section/AdminProductAttributeSetActionSection.xml | 2 +- .../Test/Mftf/Section/AdminProductAttributeSetEditSection.xml | 2 +- .../Test/Mftf/Section/AdminProductAttributeSetGridSection.xml | 2 +- .../Test/Mftf/Section/AdminProductAttributeSetSection.xml | 2 +- .../Test/Mftf/Section/AdminProductCategoryCreationSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminProductContentSection.xml | 2 +- .../Mftf/Section/AdminProductCustomizableOptionsSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml | 2 +- .../Mftf/Section/AdminProductFormAdvancedPricingSection.xml | 2 +- .../Test/Mftf/Section/AdminProductFormChangeStoreSection.xml | 2 +- .../Test/Mftf/Section/AdminProductGridConfirmActionSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml | 2 +- .../Test/Mftf/Section/AdminProductGridPaginationSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminProductGridSection.xml | 2 +- .../Test/Mftf/Section/AdminProductGridTableHeaderSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminProductImagesSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminProductMessagesSection.xml | 2 +- .../Test/Mftf/Section/AdminProductModalSlideGridSection.xml | 2 +- .../Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminProductSEOSection.xml | 2 +- .../Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCategoryFilterSection.xml | 2 +- .../Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCategoryProductSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCategorySidebarSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCategoryTopToolbarSection.xml | 2 +- .../Test/Mftf/Section/StorefrontComparisonSidebarSection.xml | 2 +- .../Catalog/Test/Mftf/Section/StorefrontFooterSection.xml | 2 +- .../Catalog/Test/Mftf/Section/StorefrontHeaderSection.xml | 2 +- .../Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml | 2 +- .../Catalog/Test/Mftf/Section/StorefrontNavigationSection.xml | 2 +- .../Mftf/Section/StorefrontProducRelatedProductsSection.xml | 2 +- .../Test/Mftf/Section/StorefrontProductActionSection.xml | 2 +- .../Test/Mftf/Section/StorefrontProductCompareMainSection.xml | 2 +- .../Test/Mftf/Section/StorefrontProductInfoDetailsSection.xml | 2 +- .../Test/Mftf/Section/StorefrontProductInfoMainSection.xml | 2 +- .../Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml | 2 +- .../Mftf/Section/StorefrontProductMoreInformationSection.xml | 2 +- .../Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml | 2 +- .../Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml | 2 +- .../Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml | 2 +- .../Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml | 2 +- .../Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml | 2 +- .../Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml | 2 +- .../Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml | 2 +- .../Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml | 2 +- .../Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml | 2 +- .../Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml | 2 +- .../Test/Mftf/Test/AdminCreateProductCustomAttributeSet.xml | 2 +- .../Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml | 2 +- .../Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml | 2 +- .../Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml | 2 +- .../Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml | 2 +- .../Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml | 2 +- .../Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml | 2 +- .../Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml | 2 +- ...AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml | 2 +- .../Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml | 2 +- .../Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml | 2 +- .../Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml | 2 +- .../Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml | 2 +- .../Test/AdminProductStatusAttributeDisabledByDefaultTest.xml | 2 +- .../Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml | 2 +- .../Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml | 2 +- .../Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml | 2 +- .../Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml | 2 +- .../Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml | 2 +- .../Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml | 2 +- .../Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml | 2 +- .../Test/AdminUnassignProductAttributeFromAttributeSetTest.xml | 2 +- .../Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml | 2 +- .../Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml | 2 +- .../Test/Mftf/Test/AdminVirtualSetEditRelatedProductsTest.xml | 2 +- .../Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml | 2 +- .../Test/ConfigurableOptionTextInputLengthValidationHint.xml | 2 +- .../Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml | 2 +- .../Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml | 2 +- .../Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml | 2 +- .../Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 2 +- .../Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml | 2 +- .../Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml | 2 +- .../Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml | 2 +- .../Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml | 2 +- .../Test/StorefrontProductsCompareWithEmptyAttributeTest.xml | 2 +- ...torefrontPurchaseProductCustomOptionsDifferentStoreViews.xml | 2 +- .../Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml | 2 +- ...frontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml | 2 +- .../Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml | 2 +- .../Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml | 2 +- .../Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest.xml | 2 +- .../Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml | 2 +- .../Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml | 2 +- .../Test/Mftf/Page/InventoryConfigurationPage.xml | 2 +- .../CatalogInventory/Test/Mftf/Section/InventorySection.xml | 2 +- .../Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml | 2 +- app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml | 2 +- .../CatalogRule/Test/Mftf/Metadata/catalog-rule-meta.xml | 2 +- app/code/Magento/CatalogRule/Test/Mftf/Page/CatalogRulePage.xml | 2 +- .../Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml | 2 +- .../Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml | 2 +- .../Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml | 2 +- .../Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml | 2 +- .../Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml | 2 +- app/code/Magento/CatalogSearch/Test/Mftf/Data/ConstData.xml | 2 +- .../Test/Mftf/Page/StorefrontCatalogSearchAdvancedFormPage.xml | 2 +- .../Mftf/Page/StorefrontCatalogSearchAdvancedResultPage.xml | 2 +- .../Test/Mftf/Page/StorefrontCatalogSearchPage.xml | 2 +- .../Mftf/Section/StorefrontCatalogSearchAdvancedFormSection.xml | 2 +- .../StorefrontCatalogSearchAdvancedResultMainSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCatalogSearchMainSection.xml | 2 +- .../CatalogSearch/Test/Mftf/Section/StorefrontFooterSection.xml | 2 +- .../Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml | 2 +- .../CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml | 2 +- .../Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 2 +- .../Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml | 2 +- .../GuestCheckoutFillNewBillingAddressActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml | 2 +- app/code/Magento/Checkout/Test/Mftf/Data/ConstData.xml | 2 +- app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml | 2 +- app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml | 2 +- app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml | 2 +- .../Magento/Checkout/Test/Mftf/Page/CheckoutShippingPage.xml | 2 +- .../Magento/Checkout/Test/Mftf/Page/CheckoutSuccessPage.xml | 2 +- .../Test/Mftf/Page/GuestCheckoutReviewAndPaymentsPage.xml | 2 +- .../Checkout/Test/Mftf/Section/AdminDataGridHeaderSection.xml | 2 +- .../Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml | 2 +- .../Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml | 2 +- .../Checkout/Test/Mftf/Section/CheckoutHeaderSection.xml | 2 +- .../Checkout/Test/Mftf/Section/CheckoutOrderSummarySection.xml | 2 +- .../Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml | 2 +- .../Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml | 2 +- .../Test/Mftf/Section/CheckoutShippingMethodsSection.xml | 2 +- .../Checkout/Test/Mftf/Section/CheckoutShippingSection.xml | 2 +- .../Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml | 2 +- .../Test/Mftf/Section/CheckoutSuccessRegisterSection.xml | 2 +- .../Test/Mftf/Section/StoreFrontRemoveItemModalSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCategoryProductSection.xml | 2 +- .../Checkout/Test/Mftf/Section/StorefrontMessagesSection.xml | 2 +- .../Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml | 2 +- .../Test/Mftf/Section/StorefrontProductCompareMainSection.xml | 2 +- .../Test/Mftf/Section/StorefrontProductInfoMainSection.xml | 2 +- ...dressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml | 2 +- .../AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml | 2 +- .../Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml | 2 +- .../Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml | 2 +- .../Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 2 +- .../NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml | 2 +- .../Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml | 2 +- .../Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml | 2 +- .../Test/Mftf/ActionGroup/AssertCMSBlockContentActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AssertCMSPageContentActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AssignBlockToCMSPageActionGroup.xml | 2 +- app/code/Magento/Cms/Test/Mftf/ActionGroup/CMSActionGroup.xml | 2 +- .../Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/DeleteImageFromStorageActionGroup.xml | 2 +- .../Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/FillOutBlockContentActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/FillOutCMSPageContentActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/NavigateToMediaFolderActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup.xml | 2 +- .../Mftf/ActionGroup/SelectImageFromMediaStorageActionGroup.xml | 2 +- .../Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml | 2 +- app/code/Magento/Cms/Test/Mftf/Data/BlockPageData.xml | 2 +- app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml | 2 +- app/code/Magento/Cms/Test/Mftf/Metadata/block-meta.xml | 2 +- app/code/Magento/Cms/Test/Mftf/Metadata/cms-meta.xml | 2 +- app/code/Magento/Cms/Test/Mftf/Page/CmsBlocksPage.xml | 2 +- app/code/Magento/Cms/Test/Mftf/Page/CmsNewBlockPage.xml | 2 +- app/code/Magento/Cms/Test/Mftf/Page/CmsNewPagePage.xml | 2 +- app/code/Magento/Cms/Test/Mftf/Page/CmsPagesPage.xml | 2 +- app/code/Magento/Cms/Test/Mftf/Page/StorefrontHomePage.xml | 2 +- .../Magento/Cms/Test/Mftf/Section/BlockPageActionsSection.xml | 2 +- .../Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml | 2 +- .../Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml | 2 +- .../Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml | 2 +- .../Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml | 2 +- .../Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml | 2 +- .../Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml | 2 +- .../Magento/Cms/Test/Mftf/Section/CmsNewPagePageSeoSection.xml | 2 +- app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml | 2 +- .../Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml | 2 +- .../Magento/Cms/Test/Mftf/Section/CustomVariableSection.xml | 2 +- .../Magento/Cms/Test/Mftf/Section/StorefrontBlockSection.xml | 2 +- .../Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml | 2 +- .../Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml | 2 +- .../Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml | 2 +- .../Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml | 2 +- .../Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml | 2 +- .../Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml | 2 +- .../Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml | 2 +- .../Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml | 2 +- .../Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml | 2 +- .../AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml | 2 +- .../AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml | 2 +- .../AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml | 2 +- ...inAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml | 2 +- ...dminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml | 2 +- app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockTest.xml | 2 +- app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml | 2 +- .../Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest.xml | 2 +- .../Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest.xml | 2 +- .../Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml | 2 +- .../Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/ConfigWebUrlOptionsActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml | 2 +- .../Config/Test/Mftf/ActionGroup/SwitcherActionGroup.xml | 2 +- app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml | 2 +- app/code/Magento/Config/Test/Mftf/Page/AdminSalesConfigPage.xml | 2 +- .../Magento/Config/Test/Mftf/Section/AdminConfigSection.xml | 2 +- .../Config/Test/Mftf/Section/AdminSalesConfigSection.xml | 2 +- app/code/Magento/Config/Test/Mftf/Section/AdminSection.xml | 2 +- app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml | 2 +- app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml | 2 +- .../Magento/Config/Test/Mftf/Section/SalesConfigSection.xml | 2 +- .../Magento/Config/Test/Mftf/Section/StoreConfigSection.xml | 2 +- app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml | 2 +- .../Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml | 2 +- .../Mftf/ActionGroup/ConfigurableProductCheckoutActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/StorefrontCompareActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml | 2 +- .../Test/Mftf/Data/ConfigurableProductData.xml | 2 +- .../Test/Mftf/Data/ConfigurableProductOptionData.xml | 2 +- .../Magento/ConfigurableProduct/Test/Mftf/Data/ConstData.xml | 2 +- .../Test/Mftf/Data/ProductConfigurableAttributeData.xml | 2 +- .../ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml | 2 +- .../Test/Mftf/Metadata/configurable_product_add_child-meta.xml | 2 +- .../Test/Mftf/Metadata/configurable_product_options-meta.xml | 2 +- .../extension_attribute_configurable_product_options-meta.xml | 2 +- .../ConfigurableProduct/Test/Mftf/Metadata/valueIndex-meta.xml | 2 +- .../Test/Mftf/Page/AdminProductCreatePage.xml | 2 +- .../Mftf/Section/AdminChooseAffectedAttributeSetSection.xml | 2 +- .../Section/AdminCreateProductConfigurationsPanelSection.xml | 2 +- .../Test/Mftf/Section/AdminNewAttributePanelSection.xml | 2 +- .../Test/Mftf/Section/AdminProductFormConfigurationsSection.xml | 2 +- .../Test/Mftf/Section/AdminProductGridActionSection.xml | 2 +- .../Test/Mftf/Section/StorefrontProductInfoMainSection.xml | 2 +- .../Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml | 2 +- .../Test/Mftf/Test/AdminConfigurableProductCreateTest.xml | 2 +- .../Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml | 2 +- .../Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml | 2 +- .../Test/Mftf/Test/AdminConfigurableProductSearchTest.xml | 2 +- .../Mftf/Test/AdminConfigurableProductSetEditContentTest.xml | 2 +- .../Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml | 2 +- .../Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml | 2 +- .../Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml | 2 +- .../Test/Mftf/Test/AdminRelatedProductsTest.xml | 2 +- .../Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml | 2 +- .../Test/ConfigurableProductPriceAdditionalStoreViewTest.xml | 2 +- .../ConfigurableProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml | 2 +- .../Test/Mftf/Test/EndToEndB2CGuestUserTest.xml | 2 +- .../Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 2 +- .../Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml | 2 +- .../Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml | 2 +- .../Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml | 2 +- .../StorefrontConfigurableProductWithFileCustomOptionTest.xml | 2 +- .../Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml | 2 +- .../Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml | 2 +- .../Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml | 2 +- app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml | 2 +- app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml | 2 +- app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml | 2 +- .../Customer/Test/Mftf/Data/ExtensionAttributeSimple.xml | 2 +- app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml | 2 +- app/code/Magento/Customer/Test/Mftf/Metadata/address-meta.xml | 2 +- app/code/Magento/Customer/Test/Mftf/Metadata/customer-meta.xml | 2 +- .../Test/Mftf/Metadata/customer_extension_attribute-meta.xml | 2 +- .../Mftf/Metadata/customer_nested_extension_attribute-meta.xml | 2 +- app/code/Magento/Customer/Test/Mftf/Metadata/region-meta.xml | 2 +- app/code/Magento/Customer/Test/Mftf/Page/AdminCustomerPage.xml | 2 +- .../Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml | 2 +- .../Magento/Customer/Test/Mftf/Page/AdminNewCustomerPage.xml | 2 +- .../Customer/Test/Mftf/Page/StorefrontCustomerCreatePage.xml | 2 +- .../Customer/Test/Mftf/Page/StorefrontCustomerDashboardPage.xml | 2 +- .../Customer/Test/Mftf/Page/StorefrontCustomerOrderPage.xml | 2 +- .../Customer/Test/Mftf/Page/StorefrontCustomerOrderViewPage.xml | 2 +- .../Customer/Test/Mftf/Page/StorefrontCustomerSignInPage.xml | 2 +- app/code/Magento/Customer/Test/Mftf/Page/StorefrontHomePage.xml | 2 +- .../Mftf/Section/AdminCustomerAccountInformationSection.xml | 2 +- .../Customer/Test/Mftf/Section/AdminCustomerFiltersSection.xml | 2 +- .../Test/Mftf/Section/AdminCustomerGridMainActionsSection.xml | 2 +- .../Customer/Test/Mftf/Section/AdminCustomerGridSection.xml | 2 +- .../Test/Mftf/Section/AdminCustomerMainActionsSection.xml | 2 +- .../Customer/Test/Mftf/Section/AdminCustomerMessagesSection.xml | 2 +- .../Test/Mftf/Section/AdminEditCustomerInformationSection.xml | 2 +- .../Test/Mftf/Section/AdminEditCustomerOrdersSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml | 2 +- .../StorefrontCustomerDashboardAccountInformationSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCustomerOrderSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCustomerOrderViewSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml | 2 +- .../Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml | 2 +- .../Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml | 2 +- .../Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 2 +- .../Customer/Test/Mftf/Test/StorefrontCreateCustomerTest.xml | 2 +- .../Test/Mftf/Test/StorefrontPersistedCustomerLoginTest.xml | 2 +- .../Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml | 2 +- app/code/Magento/Downloadable/Test/Mftf/Data/LinkData.xml | 2 +- app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml | 2 +- .../Downloadable/Test/Mftf/Metadata/downloadable_link-meta.xml | 2 +- .../Downloadable/Test/Mftf/Metadata/link_file_content-meta.xml | 2 +- .../Test/Mftf/Metadata/sample_file_content-meta.xml | 2 +- .../Downloadable/Test/Mftf/Page/AdminProductCreatePage.xml | 2 +- .../Test/Mftf/Section/AdminProductDownloadableSection.xml | 2 +- .../Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml | 2 +- .../Mftf/Test/AdminDownloadableProductSetEditContentTest.xml | 2 +- .../Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml | 2 +- .../Test/AdminRemoveDefaultImageDownloadableProductTest.xml | 2 +- .../Downloadable/Test/Mftf/Test/EndToEndB2CAdminTest.xml | 2 +- .../Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml | 2 +- .../GroupedProduct/Test/Mftf/Data/GroupedProductData.xml | 2 +- .../Magento/GroupedProduct/Test/Mftf/Data/ProductLinkData.xml | 2 +- .../Test/Mftf/Data/ProductLinkExtensionAttributeData.xml | 2 +- .../Magento/GroupedProduct/Test/Mftf/Data/ProductLinksData.xml | 2 +- .../GroupedProduct/Test/Mftf/Page/AdminProductCreatePage.xml | 2 +- .../Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml | 2 +- .../Mftf/Section/AdminProductFormGroupedProductsSection.xml | 2 +- .../Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml | 2 +- .../Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml | 2 +- .../Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml | 2 +- .../Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml | 2 +- .../GroupedProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml | 2 +- .../Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml | 2 +- .../Newsletter/Test/Mftf/Page/NewsletterTemplatePage.xml | 2 +- .../Newsletter/Test/Mftf/Section/NewsletterTemplateSection.xml | 2 +- .../Test/Mftf/Section/StorefrontNewsletterSection.xml | 2 +- .../Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml | 2 +- .../Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml | 2 +- .../Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml | 2 +- .../Test/VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest.xml | 2 +- .../Test/Mftf/ActionGroup/ClearPageCacheActionGroup.xml | 2 +- app/code/Magento/Payment/Test/Mftf/Data/PaymentMethodData.xml | 2 +- .../Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml | 2 +- app/code/Magento/Paypal/Test/Mftf/Data/PaypalData.xml | 2 +- .../Magento/Paypal/Test/Mftf/Metadata/paypal_config-meta.xml | 2 +- .../Paypal/Test/Mftf/Page/AdminConfigPaymentMethodsPage.xml | 2 +- .../Paypal/Test/Mftf/Section/OtherPaymentsConfigSection.xml | 2 +- .../Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionState.xml | 2 +- app/code/Magento/Persistent/Test/Mftf/Data/PersistentData.xml | 2 +- .../Persistent/Test/Mftf/Metadata/persistent_config-meta.xml | 2 +- .../Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml | 2 +- .../Test/Mftf/ActionGroup/AdminProductVideoActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/StorefrontProductVideoActionGroup.xml | 2 +- .../ProductVideo/Test/Mftf/Data/ProductVideoConfigData.xml | 2 +- .../Magento/ProductVideo/Test/Mftf/Data/ProductVideoData.xml | 2 +- .../Test/Mftf/Metadata/product_video_config-meta.xml | 2 +- .../Test/Mftf/Section/AdminProductImagesSection.xml | 2 +- .../Test/Mftf/Section/AdminProductNewVideoSection.xml | 2 +- .../Test/Mftf/Section/StorefrontProductInfoMainSection.xml | 2 +- app/code/Magento/Quote/Test/Mftf/Data/CartItemData.xml | 2 +- app/code/Magento/Quote/Test/Mftf/Data/GuestCartData.xml | 2 +- .../Magento/Quote/Test/Mftf/Metadata/billing_address-meta.xml | 2 +- app/code/Magento/Quote/Test/Mftf/Metadata/guest_cart-meta.xml | 2 +- .../Magento/Quote/Test/Mftf/Metadata/shipping_address-meta.xml | 2 +- .../Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml | 2 +- .../Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml | 2 +- .../Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml | 2 +- .../Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml | 2 +- app/code/Magento/Sales/Test/Mftf/Data/AddressData.xml | 2 +- app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml | 2 +- app/code/Magento/Sales/Test/Mftf/Data/OrderData.xml | 2 +- .../Magento/Sales/Test/Mftf/Page/AdminCreditMemoNewPage.xml | 2 +- .../Magento/Sales/Test/Mftf/Page/AdminInvoiceDetailsPage.xml | 2 +- app/code/Magento/Sales/Test/Mftf/Page/AdminInvoiceNewPage.xml | 2 +- app/code/Magento/Sales/Test/Mftf/Page/AdminInvoicesPage.xml | 2 +- app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml | 2 +- app/code/Magento/Sales/Test/Mftf/Page/AdminOrderDetailsPage.xml | 2 +- app/code/Magento/Sales/Test/Mftf/Page/AdminOrdersPage.xml | 2 +- .../Mftf/Section/AdminCreditMemoAddressInformationSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminCreditMemoItemsSection.xml | 2 +- .../Mftf/Section/AdminCreditMemoOrderInformationSection.xml | 2 +- .../Test/Mftf/Section/AdminCreditMemoPaymentShippingSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml | 2 +- .../Test/Mftf/Section/AdminInvoiceAddressInformationSection.xml | 2 +- .../Test/Mftf/Section/AdminInvoiceDetailsInformationSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminInvoiceMainActionsSection.xml | 2 +- .../Test/Mftf/Section/AdminInvoiceOrderInformationSection.xml | 2 +- .../Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminInvoiceTotalSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminInvoicesFiltersSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminInvoicesGridSection.xml | 2 +- .../Test/Mftf/Section/AdminOrderAddressInformationSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminOrderCommentsTabSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminOrderCreditMemosTabSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminOrderCustomersGridSection.xml | 2 +- .../Test/Mftf/Section/AdminOrderDetailsInformationSection.xml | 2 +- .../Test/Mftf/Section/AdminOrderDetailsInvoicesSection.xml | 2 +- .../Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml | 2 +- .../Test/Mftf/Section/AdminOrderDetailsMessagesSection.xml | 2 +- .../Test/Mftf/Section/AdminOrderDetailsOrderViewSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminOrderFormActionSection.xml | 2 +- .../Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml | 2 +- .../Test/Mftf/Section/AdminOrderFormBundleProductSection.xml | 2 +- .../Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml | 2 +- .../Mftf/Section/AdminOrderFormDownloadableProductSection.xml | 2 +- .../Test/Mftf/Section/AdminOrderFormGroupedProductSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminOrderFormItemsSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml | 2 +- .../Test/Mftf/Section/AdminOrderFormShippingAddressSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminOrderFormTotalSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminOrderInvoicesTabSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml | 2 +- .../Test/Mftf/Section/AdminOrderPaymentInformationSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminOrderShipmentsTabSection.xml | 2 +- .../Test/Mftf/Section/AdminOrderShippingInformationSection.xml | 2 +- .../Sales/Test/Mftf/Section/AdminOrderStoreScopeTreeSection.xml | 2 +- .../Magento/Sales/Test/Mftf/Section/AdminOrderTotalSection.xml | 2 +- .../Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml | 2 +- app/code/Magento/Sales/Test/Mftf/Section/OrdersGridSection.xml | 2 +- .../Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml | 2 +- .../Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml | 2 +- .../Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml | 2 +- app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml | 2 +- .../Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AdminSalesRuleActionGroup.xml | 2 +- .../Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml | 2 +- app/code/Magento/SalesRule/Test/Mftf/Data/QuoteData.xml | 2 +- app/code/Magento/SalesRule/Test/Mftf/Data/SalesCouponData.xml | 2 +- .../Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml | 2 +- app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml | 2 +- .../Magento/SalesRule/Test/Mftf/Data/SalesRuleLabelData.xml | 2 +- .../SalesRule/Test/Mftf/Metadata/sales_rule-condition-meta.xml | 2 +- .../SalesRule/Test/Mftf/Metadata/sales_rule-coupon-meta.xml | 2 +- .../SalesRule/Test/Mftf/Metadata/sales_rule-label-meta.xml | 2 +- .../Magento/SalesRule/Test/Mftf/Metadata/sales_rule-meta.xml | 2 +- .../SalesRule/Test/Mftf/Page/AdminCartPriceRulesPage.xml | 2 +- app/code/Magento/SalesRule/Test/Mftf/Page/PriceRuleNewPage.xml | 2 +- .../Test/Mftf/Section/AdminCartPriceRulesFormSection.xml | 2 +- .../SalesRule/Test/Mftf/Section/AdminCartPriceRulesSection.xml | 2 +- .../SalesRule/Test/Mftf/Section/CheckoutCartSummarySection.xml | 2 +- .../Magento/SalesRule/Test/Mftf/Section/DiscountSection.xml | 2 +- .../SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml | 2 +- .../Test/Mftf/Section/StorefrontSalesRuleCartCouponSection.xml | 2 +- .../SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml | 2 +- .../Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml | 2 +- .../Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml | 2 +- .../Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml | 2 +- .../Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml | 2 +- .../Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml | 2 +- .../SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml | 2 +- .../SalesRule/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 2 +- .../SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml | 2 +- .../SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml | 2 +- .../Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml | 2 +- .../Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml | 2 +- .../SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml | 2 +- .../Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml | 2 +- .../Search/Test/Mftf/Section/StorefrontQuickSearchSection.xml | 2 +- .../Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml | 2 +- .../Shipping/Test/Mftf/Data/FlatRateShippingMethodData.xml | 2 +- .../Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml | 2 +- app/code/Magento/Shipping/Test/Mftf/Data/ShippingMethodData.xml | 2 +- .../Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml | 2 +- .../Magento/Shipping/Test/Mftf/Page/AdminShipmentNewPage.xml | 2 +- .../Mftf/Section/AdminShipmentAddressInformationSection.xml | 2 +- .../Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml | 2 +- .../Test/Mftf/Section/AdminShipmentMainActionsSection.xml | 2 +- .../Test/Mftf/Section/AdminShipmentOrderInformationSection.xml | 2 +- .../Test/Mftf/Section/AdminShipmentPaymentShippingSection.xml | 2 +- .../Shipping/Test/Mftf/Section/AdminShipmentTotalSection.xml | 2 +- .../Magento/Shipping/Test/Mftf/Test/EndToEndB2CAdminTest.xml | 2 +- .../Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/CreateCustomStoreViewActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/DeleteCustomStoreActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml | 2 +- .../Mftf/ActionGroup/StorefrontSwitchStoreViewActionGroup.xml | 2 +- app/code/Magento/Store/Test/Mftf/Data/StoreData.xml | 2 +- app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml | 2 +- app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml | 2 +- app/code/Magento/Store/Test/Mftf/Metadata/store-meta.xml | 2 +- app/code/Magento/Store/Test/Mftf/Metadata/store_group-meta.xml | 2 +- app/code/Magento/Store/Test/Mftf/Metadata/website-meta.xml | 2 +- .../Magento/Store/Test/Mftf/Page/AdminSystemStoreDeletePage.xml | 2 +- .../Magento/Store/Test/Mftf/Page/AdminSystemStoreEditPage.xml | 2 +- .../Store/Test/Mftf/Page/AdminSystemStoreGroupEditPage.xml | 2 +- .../Magento/Store/Test/Mftf/Page/AdminSystemStoreGroupPage.xml | 2 +- app/code/Magento/Store/Test/Mftf/Page/AdminSystemStorePage.xml | 2 +- .../Magento/Store/Test/Mftf/Page/AdminSystemStoreViewPage.xml | 2 +- .../Store/Test/Mftf/Page/AdminSystemStoreWebsitePage.xml | 2 +- .../Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml | 2 +- .../Test/Mftf/Section/AdminNewStoreGroupActionsSection.xml | 2 +- .../Store/Test/Mftf/Section/AdminNewStoreGroupSection.xml | 2 +- .../Magento/Store/Test/Mftf/Section/AdminNewStoreSection.xml | 2 +- .../Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml | 2 +- .../Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml | 2 +- .../Magento/Store/Test/Mftf/Section/AdminNewWebsiteSection.xml | 2 +- .../Store/Test/Mftf/Section/AdminStoreBackupOptionsSection.xml | 2 +- .../Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml | 2 +- .../Test/Mftf/Section/AdminStoresDeleteStoreGroupSection.xml | 2 +- .../Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml | 2 +- .../Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml | 2 +- .../Store/Test/Mftf/Section/AdminStoresMainActionsSection.xml | 2 +- .../Magento/Store/Test/Mftf/Section/StorefrontHeaderSection.xml | 2 +- .../Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml | 2 +- .../Magento/Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml | 2 +- .../Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml | 2 +- .../Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml | 2 +- .../Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml | 2 +- app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml | 2 +- .../Swatches/Test/Mftf/Section/AdminColorPickerSection.xml | 2 +- .../Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml | 2 +- .../Test/Mftf/Section/AdminNewAttributePanelSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCategorySidebarSection.xml | 2 +- .../Test/Mftf/Section/StorefrontProductInfoMainSection.xml | 2 +- .../Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml | 2 +- .../Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml | 2 +- .../Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml | 2 +- .../Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml | 2 +- .../Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml | 2 +- .../Test/StorefrontSwatchProductWithFileCustomOptionTest.xml | 2 +- .../Magento/Tax/Test/Mftf/ActionGroup/AdminTaxActionGroup.xml | 2 +- app/code/Magento/Tax/Test/Mftf/Data/TaxCodeData.xml | 2 +- app/code/Magento/Tax/Test/Mftf/Data/TaxConfigData.xml | 2 +- app/code/Magento/Tax/Test/Mftf/Data/TaxRegionData.xml | 2 +- app/code/Magento/Tax/Test/Mftf/Data/TaxRuleData.xml | 2 +- app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml | 2 +- app/code/Magento/Tax/Test/Mftf/Metadata/tax_rule-meta.xml | 2 +- app/code/Magento/Tax/Test/Mftf/Page/AdminNewTaxRulePage.xml | 2 +- .../Magento/Tax/Test/Mftf/Page/AdminTaxConfigurationPage.xml | 2 +- app/code/Magento/Tax/Test/Mftf/Page/AdminTaxRateGridPage.xml | 2 +- app/code/Magento/Tax/Test/Mftf/Page/AdminTaxRuleGridPage.xml | 2 +- .../Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml | 2 +- app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRulesSection.xml | 2 +- .../Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml | 2 +- ...TaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml | 2 +- ...tTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml | 2 +- ...ontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml | 2 +- ...rontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml | 2 +- .../Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml | 2 +- .../Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml | 2 +- app/code/Magento/Theme/Test/Mftf/Data/DesignData.xml | 2 +- app/code/Magento/Theme/Test/Mftf/Page/ThemesPage.xml | 2 +- app/code/Magento/Theme/Test/Mftf/Section/AdminThemeSection.xml | 2 +- .../Magento/Theme/Test/Mftf/Section/StorefrontFooterSection.xml | 2 +- .../Theme/Test/Mftf/Section/StorefrontMessagesSection.xml | 2 +- app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml | 2 +- .../Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection.xml | 2 +- .../Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml | 2 +- .../Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml | 2 +- .../Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml | 2 +- .../ActionGroup/AdminGridFilterSearchResultsActionGroup.xml | 2 +- .../Ui/Test/Mftf/ActionGroup/AdminSaveAndCloseActionGroup.xml | 2 +- .../Magento/Ui/Test/Mftf/Section/AdminDataGridHeaderSection.xml | 2 +- .../Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml | 2 +- .../Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml | 2 +- .../Magento/Ui/Test/Mftf/Section/AdminGridControlsSection.xml | 2 +- app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml | 2 +- .../Magento/Ui/Test/Mftf/Section/ModalConfirmationSection.xml | 2 +- .../User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml | 2 +- app/code/Magento/User/Test/Mftf/Data/UserData.xml | 2 +- app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml | 2 +- app/code/Magento/User/Test/Mftf/Page/AdminEditRolePage.xml | 2 +- app/code/Magento/User/Test/Mftf/Page/AdminEditUserPage.xml | 2 +- app/code/Magento/User/Test/Mftf/Page/AdminRolesPage.xml | 2 +- app/code/Magento/User/Test/Mftf/Page/AdminUsersPage.xml | 2 +- .../Magento/User/Test/Mftf/Section/AdminEditRoleInfoSection.xml | 2 +- .../Magento/User/Test/Mftf/Section/AdminEditUserRoleSection.xml | 2 +- .../Magento/User/Test/Mftf/Section/AdminEditUserSection.xml | 2 +- .../Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml | 2 +- .../Magento/User/Test/Mftf/Section/AdminUserGridSection.xml | 2 +- .../Test/Mftf/ActionGroup/CreateCustomVariableActionGroup.xml | 2 +- app/code/Magento/Variable/Test/Mftf/Data/VariableData.xml | 2 +- .../Mftf/ActionGroup/AdminProductAddFPTValueActionGroup.xml | 2 +- .../Magento/Weee/Test/Mftf/Data/FixedProductAttributeData.xml | 2 +- app/code/Magento/Weee/Test/Mftf/Data/WeeeConfigData.xml | 2 +- app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml | 2 +- app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml | 2 +- .../Weee/Test/Mftf/Section/AdminProductAddFPTValueSection.xml | 2 +- .../Weee/Test/Mftf/Section/CheckoutCartSummarySection.xml | 2 +- .../Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml | 2 +- app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml | 2 +- app/code/Magento/Wishlist/Test/Mftf/Metadata/wishlist-meta.xml | 2 +- .../Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml | 2 +- .../Test/Mftf/Section/StorefrontCategoryProductSection.xml | 2 +- .../Mftf/Section/StorefrontCustomerWishlistProductSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCustomerWishlistSection.xml | 2 +- .../Mftf/Section/StorefrontCustomerWishlistSidebarSection.xml | 2 +- .../Test/Mftf/Section/StorefrontProductInfoMainSection.xml | 2 +- ...ConfigurableProductChildImageShouldBeShownOnWishListTest.xml | 2 +- .../Wishlist/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml | 2 +- .../Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml | 2 +- .../StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml | 2 +- .../Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml | 2 +- .../StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml | 2 +- 758 files changed, 758 insertions(+), 758 deletions(-) diff --git a/app/code/Magento/Analytics/Test/Mftf/Data/UserData.xml b/app/code/Magento/Analytics/Test/Mftf/Data/UserData.xml index 8324ad5ba995a..f6e5b955816e2 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Data/UserData.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Data/UserData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="adminNoReport" type="user"> <data key="username" unique="suffix">noreport</data> <data key="firstname">No</data> diff --git a/app/code/Magento/Analytics/Test/Mftf/Data/UserRoleData.xml b/app/code/Magento/Analytics/Test/Mftf/Data/UserRoleData.xml index 71d8bdcd5994b..099cc71321b84 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Data/UserRoleData.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Data/UserRoleData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="adminNoReportRole" type="user_role"> <data key="rolename" unique="suffix">noreport</data> <data key="current_password">123123q</data> diff --git a/app/code/Magento/Analytics/Test/Mftf/Metadata/user-meta.xml b/app/code/Magento/Analytics/Test/Mftf/Metadata/user-meta.xml index 06186d2d10402..2e1e5f6f5a97d 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Metadata/user-meta.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Metadata/user-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateUser" dataType="user" type="create" auth="adminFormKey" url="/admin/user/save/" method="POST" successRegex="/messages-message-success/" returnRegex="" > <contentType>application/x-www-form-urlencoded</contentType> diff --git a/app/code/Magento/Analytics/Test/Mftf/Metadata/user_role-meta.xml b/app/code/Magento/Analytics/Test/Mftf/Metadata/user_role-meta.xml index f52468807928e..9d0132453c798 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Metadata/user_role-meta.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Metadata/user_role-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateUserRole" dataType="user_role" type="create" auth="adminFormKey" url="/admin/user_role/saverole/" method="POST" successRegex="/messages-message-success/" returnRegex="" > <contentType>application/x-www-form-urlencoded</contentType> diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml index fefb7874ef736..ff89ca9b663ee 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurationBlankIndustryTest"> <annotations> <features value="Analytics"/> diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml index 15c9727cc8c79..d9617209dcdff 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml @@ -6,7 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurationEnableDisableAnalyticsTest"> <annotations> <features value="Analytics"/> diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml index d4f30737bae3e..2d5594a43b1a7 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurationIndustryTest"> <annotations> <features value="Analytics"/> diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml index b3ccd3afd1bf7..d8aed1250d82a 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurationPermissionTest"> <annotations> <features value="Analytics"/> diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml index fc1ff7d18b51e..3f17df108b50b 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurationTimeToSendDataTest"> <annotations> <features value="Analytics"/> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginActionGroup.xml index bcff329d79dad..9ba4430bafe35 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="LoginActionGroup"> <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}" stepKey="navigateToAdmin"/> <fillField userInput="{{_ENV.MAGENTO_ADMIN_USERNAME}}" selector="{{AdminLoginFormSection.username}}" stepKey="fillUsername"/> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml index 8a24ab2a2f185..a7ef237a232b8 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="LoginAsAdmin"> <arguments> <argument name="adminUser" defaultValue="_ENV"/> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LogoutActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LogoutActionGroup.xml index cdaf231e9dda0..a4d922086df34 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LogoutActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LogoutActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="logout"> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> </actionGroup> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/SecondaryGridActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/SecondaryGridActionGroup.xml index 9fe5f54f1db3c..6f27b03e4df30 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/SecondaryGridActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/SecondaryGridActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Action group to delete an item given that items name --> <!-- Must already be on the admin page containing the grid --> <actionGroup name="deleteEntitySecondaryGrid"> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/SortByIdDescendingActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/SortByIdDescendingActionGroup.xml index b7b63c5d9a62e..fd353964bae9a 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/SortByIdDescendingActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/SortByIdDescendingActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="SortByIdDescendingActionGroup"> <conditionalClick selector="//div[contains(@data-role, 'grid-wrapper')]/table/thead/tr/th/span[contains(text(), 'ID')]" dependentSelector="//span[contains(text(), 'ID')]/parent::th[not(contains(@class, '_descend'))]/parent::tr/parent::thead/parent::table/parent::div[contains(@data-role, 'grid-wrapper')]" stepKey="clickToAttemptSortByIdDescending" visible="true"/> <waitForLoadingMaskToDisappear stepKey="waitForFirstIdSortDescendingToFinish" /> diff --git a/app/code/Magento/Backend/Test/Mftf/Data/BackenedData.xml b/app/code/Magento/Backend/Test/Mftf/Data/BackenedData.xml index 286685315a7bc..016e936977cd0 100644 --- a/app/code/Magento/Backend/Test/Mftf/Data/BackenedData.xml +++ b/app/code/Magento/Backend/Test/Mftf/Data/BackenedData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="backendDataOne" type="backend"> <data key="backendConfigName">data</data> </entity> diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml index a53938d534644..d1bf3c2cb2ed6 100644 --- a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="ConfigurationStoresPage" url="admin/system_config/edit/section/cms/" area="admin" module="Catalog"> <section name="WYSIWYGOptionsSection"/> </page> diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminLoginPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminLoginPage.xml index ca0797f7ded26..b68b9914186f6 100644 --- a/app/code/Magento/Backend/Test/Mftf/Page/AdminLoginPage.xml +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminLoginPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminLoginPage" url="admin" area="admin" module="Magento_Backend"> <section name="AdminLoginFormSection"/> </page> diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminLogoutPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminLogoutPage.xml index 75ef114ec64b6..713199771e824 100644 --- a/app/code/Magento/Backend/Test/Mftf/Page/AdminLogoutPage.xml +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminLogoutPage.xml @@ -7,6 +7,6 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminLogoutPage" url="admin/auth/logout/" area="admin" module="Magento_Backend"/> </pages> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminConfirmationModalSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminConfirmationModalSection.xml index dc512e66528ac..2ec25da461908 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminConfirmationModalSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminConfirmationModalSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminConfirmationModalSection"> <element name="title" type="text" selector="aside.confirm .modal-title"/> <element name="message" type="text" selector="aside.confirm .modal-content"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminGridTableSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminGridTableSection.xml index 3e8f8a8f2e412..cc92e530cf3d4 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminGridTableSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminGridTableSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminGridTableSection"> <element name="row" type="text" selector="table.data-grid tbody tr[data-role=row]:nth-of-type({{row}})" parameterized="true"/> </section> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminHeaderSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminHeaderSection.xml index 92b06878ab87f..441ce886f117b 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminHeaderSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminHeaderSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminHeaderSection"> <element name="pageTitle" type="text" selector=".page-header h1.page-title"/> </section> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginFormSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginFormSection.xml index b65a969e334c4..3b10fac7bb9dc 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginFormSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginFormSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminLoginFormSection"> <element name="username" type="input" selector="#username"/> <element name="password" type="input" selector="#login"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml index f8d259cc8e490..cb164d43a49ff 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMainActionsSection"> <element name="save" type="button" selector="#save"/> <element name="delete" type="button" selector="#delete"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml index ff5e02397cbff..b1350d5dcc1d7 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMessagesSection"> <element name="success" type="text" selector="#messages div.message-success"/> <element name="nthSuccess" type="text" selector=".message.message-success.success:nth-of-type({{n}})>div" parameterized="true"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminSecondaryGridSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminSecondaryGridSection.xml index ea84ce7ea0c4f..9051eb747a7a6 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminSecondaryGridSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminSecondaryGridSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminSecondaryGridSection"> <element name="resetFilters" type="button" selector="[title='Reset Filter']"/> <element name="taxIdentifierSearch" type="input" selector=".col-code .admin__control-text"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml index 99d0f6654738a..7f0194b7dc347 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminLoginTest"> <annotations> <features value="Backend"/> diff --git a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml index 6e669a1b8bf4b..f291eb0e4b986 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="SampleBraintreeConfig" type="braintree_config_state"> <requiredEntity type="title">SampleTitle</requiredEntity> <requiredEntity type="payment_action">SamplePaymentAction</requiredEntity> diff --git a/app/code/Magento/Braintree/Test/Mftf/Metadata/braintree_config-meta.xml b/app/code/Magento/Braintree/Test/Mftf/Metadata/braintree_config-meta.xml index e4d02a58b5bf4..83018852bfeb5 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Metadata/braintree_config-meta.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Metadata/braintree_config-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateBraintreeConfigState" dataType="braintree_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/payment/" method="POST"> <object key="groups" dataType="braintree_config_state"> <object key="braintree_section" dataType="braintree_config_state"> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminBundleProductActionGroup.xml index a5e62fca9483c..836826734f02d 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminBundleProductActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminBundleProductActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Fill main fields in create product form--> <actionGroup name="fillMainBundleProductForm"> <arguments> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminClearFiltersActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminClearFiltersActionGroup.xml index f3e5eff3834e3..b3ac72d3f416e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminClearFiltersActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminClearFiltersActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminClearFiltersActionGroup"> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="GoToCatalogProductPage"/> <waitForPageLoad stepKey="WaitForPageToLoad"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductFilterActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductFilterActionGroup.xml index 8ab7af1d0318e..177f9203ed146 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductFilterActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductFilterActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="BundleProductFilter"> <!--Setting filter--> <!--Prereq: go to admin product catalog page--> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBundleProductActionGroup.xml index af8fc1459d9e3..6e889ea2432e7 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBundleProductActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBundleProductActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CreateBasicBundleProduct"> <!--Prereq: Go to bundle product creation page--> <!--Product name and SKU--> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/EnableDisableProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/EnableDisableProductActionGroup.xml index 2ae9748c773e8..e3ac6483bc7bd 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/EnableDisableProductActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/EnableDisableProductActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AncillaryPrepBundleProduct"> <!--Prereq: go to bundle product creation page--> <fillField selector="{{AdminProductFormBundleSection.productName}}" userInput="{{BundleProduct.name}}" stepKey="fillProductName"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/SetBundleProductAttributesActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/SetBundleProductAttributesActionGroup.xml index 2f6e38df97e84..01f8d0de4b706 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/SetBundleProductAttributesActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/SetBundleProductAttributesActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <actionGroup name="SetBundleProductAttributes"> <arguments> <argument name="attributeSet" defaultValue="Default" type="string"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml index 48697d43ec824..f28ffbdc40acc 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Add Bundle Product to Cart from the category page with specified quantity to cart --> <actionGroup name="StorefrontAddCategoryBundleProductToCartActionGroup"> <arguments> diff --git a/app/code/Magento/Bundle/Test/Mftf/Data/BundleLinkData.xml b/app/code/Magento/Bundle/Test/Mftf/Data/BundleLinkData.xml index 7123a573bc2e1..60d11345731c1 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Data/BundleLinkData.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Data/BundleLinkData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ApiBundleLink" type="bundle_link"> <var key="option_id" entityKey="return" entityType="bundle_option"/> <var key="sku" entityKey="sku" entityType="product"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Data/BundleOptionData.xml b/app/code/Magento/Bundle/Test/Mftf/Data/BundleOptionData.xml index e10fe4e33c208..a53ae9be4b75b 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Data/BundleOptionData.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Data/BundleOptionData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="DropDownBundleOption" type="bundle_option"> <data key="title" unique="suffix">bundle-option-dropdown</data> <data key="required">true</data> diff --git a/app/code/Magento/Bundle/Test/Mftf/Data/CustomAttributeData.xml b/app/code/Magento/Bundle/Test/Mftf/Data/CustomAttributeData.xml index 380b5b8959025..e6866ae74a7e1 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Data/CustomAttributeData.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Data/CustomAttributeData.xml @@ -6,7 +6,7 @@ */ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="CustomAttributeDynamicPrice" type="custom_attribute"> <data key="attribute_code">price_type</data> <data key="value">0</data> diff --git a/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml index 9c558861dc61d..af93200f946d2 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="BundleProduct" type="product"> <data key="name" unique="suffix">BundleProduct</data> <data key="name2" unique="suffix">BundleProduct2</data> diff --git a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_link-meta.xml b/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_link-meta.xml index ca39253aa54a0..254f542316d10 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_link-meta.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_link-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateBundleLink" dataType="bundle_link" type="create" auth="adminOauth" url="/V1/bundle-products/{sku}/links/{return}" method="POST"> <contentType>application/json</contentType> <object dataType="bundle_link" key="linkedProduct"> diff --git a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_option-meta.xml b/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_option-meta.xml index c912ea5eac41a..4e1dc7ac9cb50 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_option-meta.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_option-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateBundleOption" dataType="bundle_option" type="create" auth="adminOauth" url="/V1/bundle-products/options/add" method="POST"> <contentType>application/json</contentType> <object dataType="bundle_option" key="option"> diff --git a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_options-meta.xml b/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_options-meta.xml index 12cba3fc179fe..df931c74191f9 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_options-meta.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_options-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="GetAllBundleOptions" dataType="bundle_options" type="get" auth="adminOauth" url="/V1/bundle-products/{sku}/options/all" method="GET"> <contentType>application/json</contentType> </operation> diff --git a/app/code/Magento/Bundle/Test/Mftf/Page/AdminCatalogProductPage.xml b/app/code/Magento/Bundle/Test/Mftf/Page/AdminCatalogProductPage.xml index cb97521499e23..782c97aab1a29 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Page/AdminCatalogProductPage.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Page/AdminCatalogProductPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminCatalogProductPage" url="catalog/product/" area="admin" module="Magento_Bundle"> <section name="AdminCatalogProductSection"/> </page> diff --git a/app/code/Magento/Bundle/Test/Mftf/Page/AdminProductCreatePage.xml b/app/code/Magento/Bundle/Test/Mftf/Page/AdminProductCreatePage.xml index f0048e2fc95d4..562ded6c8e40f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Page/AdminProductCreatePage.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Page/AdminProductCreatePage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminProductCreatePage" url="catalog/product/new/set/{{set}}/type/{{type}}/" area="admin" module="Magento_Catalog" parameterized="true"> <section name="AdminProductFormBundleSection"/> </page> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml index 2013ac7b10bd6..147b70c44f9e7 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormBundleSection"> <element name="bundleItemsToggle" type="button" selector="//span[text()='Bundle Items']"/> <element name="shipmentType" type="select" selector=".admin__control-select[name='product[shipment_type]']"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/BundleStorefrontSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/BundleStorefrontSection.xml index 1a8709bd84e9e..7a188fd58e1af 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/BundleStorefrontSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/BundleStorefrontSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="BundleStorefrontSection"> <!--TestingForLocationOfOptions--> <element name="bundleOptionSelector" type="checkbox" selector="//*[@id='bundle-slide']/span" timeout="30"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml index f724f9bbfe1bd..8d9f29814f762 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontBundledSection"> <element name="nthBundledOption" type="input" selector=".option:nth-of-type({{numOption}}) .choice:nth-of-type({{numOptionSelect}}) input" parameterized="true"/> <element name="addToCart" type="button" selector="#bundle-slide" timeout="30"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontCategoryProductSection.xml index c76f822a0913f..3d5dc61d88a87 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontCategoryProductSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategoryProductSection"> <element name="priceToByProductId" type="text" selector="div[data-product-id='{{id}}'] .price-to" parameterized="true"/> <element name="priceFromByProductId" type="text" selector="div[data-product-id='{{id}}'] .price-from" parameterized="true"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductActionSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductActionSection.xml index abc9bc6dab540..9dc4aed26bef0 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductActionSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductActionSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontBundleProductActionSection"> <element name="customizeAndAddToCartButton" type="button" selector="#bundle-slide"/> <element name="quantityField" type="input" selector="#qty"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 41c00b5eda184..735571375866e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoMainSection"> <element name="priceFrom" type="text" selector=".product-info-price .price-from"/> <element name="priceTo" type="text" selector=".product-info-price .price-to"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml index d94e196ea5ad1..401d360a34c64 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddBundleItemsTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml index e1f90790b30a9..21e6be98b3169 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddDefaultImageBundleProductTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml index 795982eb4b939..1d2f21b7d15f9 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAttributeSetSelectionTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml index eeb04aeadb555..6b310d49ff23d 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminBasicBundleProductAttributesTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml index 02eaeba8d7e78..448e9d7dffb2e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminBundleProductSetEditContentTest" extends="AdminSimpleProductSetEditContentTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml index bf62212babd43..86db6f372b5f8 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminDeleteABundleProduct"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml index 4bbb01ceae305..08faa9d2444df 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminEditRelatedBundleProductTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml index 9faf9e69bc873..40a6e1b75c60a 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminFilterProductListByBundleProduct"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml index 6cb86d8028352..c0edbf14e894b 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMassDeleteBundleProductsTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml index 643f13dfd61ed..f87897bd579a3 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminProductBundleCreationTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml index ccd729ac841cd..1438958b92b61 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminRemoveDefaultImageBundleProductTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml index a579460906d0e..574c0dccdb07f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="BundleProductFixedPricingTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml index 7a7b4673eda6d..0cfd1f99a8ce0 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EnableDisableBundleProductStatusTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 9402d1d48012f..9040d675be34f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CAdminTest"> <!--Create Bundle Product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageBundle" after="seeSimpleProductInGrid"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml index 89867341e96d3..0fb8a7b88250c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="MassEnableDisableBundleProductsTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml index 8a0a1ceaf52c7..e0a6a9afd648e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="NewBundleProductSelectionTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml index c0d659f1665a8..40132ea956584 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontAdminEditDataTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml index 655081df61073..695c3a8bf7dbb 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontBundleCartTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml index a475ef16ed5c5..285503465a011 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontBundleProductDetailsTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml index 577079965cabb..9ad4b6828d6e4 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontBundleProductShownInCategoryListAndGrid"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml index 26e5e436ed567..5e6e891541420 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontCustomerSelectAndSetBundleOptionsTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml index a50a73c7f6bb4..f94cd83f4e7d7 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontEditBundleProductTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml index 6c476183a35b3..ccd6a58223b3c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontGoToDetailsPageWhenAddingToCart"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml index 1b1a46d1c8ba4..0d81e364ae4ba 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest"> <annotations> <features value="Bundle"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml index 44c960dc37641..692487c1d60cd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AddSimpleProductToCart"> <arguments> <argument name="product" defaultValue="product"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index 7c04e9bd83d56..76f65381f43fa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Create a new category--> <actionGroup name="CreateCategory"> <arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateRootCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateRootCategoryActionGroup.xml index e7d9a63484bc6..a99420bcf95bb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateRootCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateRootCategoryActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Create a new root category--> <actionGroup name="AdminCreateRootCategory"> <arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 3d0c1288271eb..84231473b685d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Navigate to create product page from product grid page--> <actionGroup name="goToCreateProductPage"> <arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index 3f4ee180fc65f..fd80838692065 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="navigateToCreatedProductAttribute"> <arguments> <argument name="ProductAttribute"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml index e943227903884..5948ca12dcf0f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssignAttributeToGroup"> <arguments> <argument name="group" type="string"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index c2620bc5a3672..1bd9bb4a09c86 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Reset the product grid to the default view--> <actionGroup name="resetProductGridToDefaultView"> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontCategoryPageActionGroup.xml index 4eca49dc28b57..8b657fa1b8aab 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontCategoryPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontCategoryPageActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertProductInStorefrontCategoryPage"> <arguments> <argument name="category"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontProductPageActionGroup.xml index 59c874b8481d3..391a1a7d670de 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontProductPageActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertProductInStorefrontProductPage"> <arguments> <argument name="product"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckItemInLayeredNavigationActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckItemInLayeredNavigationActionGroup.xml index 304f38e227960..f2a7a0acffefa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckItemInLayeredNavigationActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckItemInLayeredNavigationActionGroup.xml @@ -6,7 +6,7 @@ */ --> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CheckItemInLayeredNavigationActionGroup"> <arguments> <argument name="itemType"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml index 6b47479d41cb7..7373d5baea0c5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml @@ -6,7 +6,7 @@ */ --> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CreateCustomRadioOptions"> <!-- ActionGroup will add a single custom option to a product --> <!-- You must already be on the product creation page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/MoveCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/MoveCategoryActionGroup.xml index ae9dc0557a9bd..7bb9aa60ca628 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/MoveCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/MoveCategoryActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="MoveCategoryActionGroup"> <arguments> <argument name="childCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenEditProductOnBackendActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenEditProductOnBackendActionGroup.xml index 07fba7cc6be06..8f89a85e14892 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenEditProductOnBackendActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenEditProductOnBackendActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="OpenEditProductOnBackendActionGroup"> <arguments> <argument name="product" defaultValue="product"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductFromCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductFromCategoryPageActionGroup.xml index e8794ab895c6b..c460dcbfbec91 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductFromCategoryPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductFromCategoryPageActionGroup.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. --> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="OpenProductFromCategoryPageActionGroup"> <arguments> <argument name="category"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml index 53acfe2b4372d..2f9d38516bd05 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="RestoreLayoutSetting"> <selectOption selector="{{DefaultLayoutsSection.categoryLayout}}" userInput="No layout updates" stepKey="selectNoLayoutUpdates1" after="expandDefaultLayouts"/> <selectOption selector="{{DefaultLayoutsSection.productLayout}}" userInput="No layout updates" stepKey="selectNoLayoutUpdates2" before="clickSaveConfig"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchAndMultiselectActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchAndMultiselectActionGroup.xml index 943fe803232e6..53e7ea3589d1e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchAndMultiselectActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchAndMultiselectActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="searchAndMultiSelectActionGroup"> <arguments> <argument name="dropDownSelector" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml index 5fbc9c5d7fcad..113e108577aa8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="SearchForProductOnBackendActionGroup"> <arguments> <argument name="product" defaultValue="product"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddToCartCustomOptionsProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddToCartCustomOptionsProductPageActionGroup.xml index 105a5c58788de..c7ae52d2b37c3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddToCartCustomOptionsProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddToCartCustomOptionsProductPageActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Click Add to Cart button in storefront product page--> <actionGroup name="StorefrontAddToCartCustomOptionsProductPageActionGroup"> <arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml index 4376e78242fbd..c980c43b8f3af 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Go to storefront category product page by given parameters --> <actionGroup name="GoToStorefrontCategoryPageByParameters"> <arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCompareActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCompareActionGroup.xml index 7af1cacfb3da8..04e15da91777c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCompareActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCompareActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Add Product to Compare from the category page and check message --> <actionGroup name="StorefrontAddCategoryProductToCompareActionGroup"> <arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml index eb672cd162e82..5f0d03597dab1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Check the simple product on the product page --> <actionGroup name="StorefrontCheckSimpleProduct"> <arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml index d46b895044531..82042975d5fb8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Click Add to Cart button in storefront product page--> <actionGroup name="addToCartFromStorefrontProductPage"> <arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml index 42351741d9fa8..5c79c321c9431 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultCategory" type="category"> <data key="name" unique="suffix">simpleCategory</data> <data key="name_lwr" unique="suffix">simplecategory</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ConstData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ConstData.xml index 8ae57f9239902..8a26b6babdbbc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ConstData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ConstData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <!-- @TODO: Get rid off this workaround and its usages after MQE-498 is implemented --> <entity name="CONST" type="CONST"> <data key="one">1</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml index e93138fecfd47..389c41abf0bd1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml @@ -6,7 +6,7 @@ */ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="CustomAttributeCategoryUrlKey" type="custom_attribute"> <data key="attribute_code">url_key</data> <data key="value" unique="suffix">category</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml index 2423383bc19f7..7a6a22abacb00 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ProductAttributeFrontendLabel" type="FrontendLabel"> <data key="store_id">0</data> <data key="label" unique="suffix">attribute</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ImageContentData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ImageContentData.xml index c674a8fc144ce..1f4b1470098e2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ImageContentData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ImageContentData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="TestImageContent" type="ImageContent"> <data key="base64_encoded_data">/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDIBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/AABEIAGAAYAMBIgACEQEDEQH/xACXAAEBAAMBAQEBAAAAAAAAAAAABgMEBQgCAQcQAAEDAQUFBgQDCQAAAAAAAAABAgMEBQYRFpESMTZV0QchcnOzwhMUIkEygaE1QlFSYXGCsbIBAAEFAQAAAAAAAAAAAAAAAAACAwQGBwERAAECAwMLBAMBAAAAAAAAAAEAAgMEERMhkRQxMzRBUVJTcXKxBRJhoSKBwUL/2gAMAwEAAhEDEQA/AP7+AYKysp7Po5aurlbFBEmL3u3NQ6ASaBdArcFnBN5/urzqn0d0Gf7q86p9HdCRkUzy3YFOWEXhOCpATef7q86p9HdBn+6vOqfR3QMimeW7AosIvCcFSAm8/wB1edU+jugz/dXnVPo7oGRTPLdgUWEXhOCpATef7q86p9HdBn+6vOqfR3QMimeW7AosIvCcFSA1bPtGktWiZWUM7Z6d6qjZG7lwXBf1Q2iO5paaOFCmyCDQoTd/uBLX8n3IUhN3+4EtfyfchIk9Zh9w8pyBpW9QvN4Bwbcsujis+pq2Q4Tq5HbW0u9XJj3Y4fc0ibjPgQjEY0GgJNTS4brj/FaIz3Q2FwFafNP4V3gc1aWz7FY+rjhVrsNjBrlcrsV3Iir/ABPxtqzRyM+boJKeJ7kakm2jkRV3Yom4TlbYf4xrnfFSBuqaCn7ouWwbc+4/FT90XTBz57RlbVvpqWjdUSRoiyfWjUbju71MUlqSyWdVPjpnsqIUVJI3ORFZ3fix+4OnoLSRU3V2HZnANKEjcEGOwVG74OxdUGjZM1RNQROqIlYuw3Zcr9pXpgn1f0xN4kQYgiww8bU4xwe0OG1eg+y7gCg8cvqOLEjuy7gCg8cvqOLEzT1HXIvcfKq0zpn9ShN3+4EtfyfchSE3f7gS1/J9yCJPWYfcPKTA0reoXm85l4P2HUf4/wDSHTPmSOOZiskY17F3tcmKKaXMwjGgvhj/AECMQrTFZ72ObvC5lvxq+gjeivRsUzXvVn4kb34qmpozxWc+NjVtWtqPiOREjbMj1Vf7YFHvMMdLTxP244ImP/maxEUhzMhaxC8UvABrXZuoR9pmLL+9xddfvXNrfkVtJyPqJaOpRiL8VHbKPT8+5THFVS1FnWnE+VKhsUbmsmamG3i1e78jsSwQzoiTRRyIm5HtRf8AZ9MjZGxGMY1rU/damCHTJPMQuDgAa5q31G0VpdnrnuRYO9xNaA1+/r9rUsmeGazqdscrHuZExHo1cVauH30U3THFBDBtfBijj2t+w1Ex0MhMgMcyG1r843J+GC1oDs69B9l3AFB45fUcWJHdl3AFB45fUcWJm3qOuRe4+VV5nTP6lCbv9wJa/k+5CkJu/wBwJa/k+5BEnrMPuHlJgaVvULzeADUlbUAAIQAAhAACF6D7LuAKDxy+o4sSO7LuAKDxy+o4sTMPUdci9x8qqTOmf1KE3f7gS1/J9yFITd/uBLX8n3IIk9Zh9w8pMDSt6hebwAakragABCAAEIAAQvQfZdwBQeOX1HFiR3ZdwBQeOX1HFiZh6jrkXuPlVSZ0z+pQwVlHT2hRy0lXE2WCVMHsduchnBEBINQmQaXhTeQLq8lp9XdRkC6vJafV3UpASMtmeY7Epy3i8RxU3kC6vJafV3UZAuryWn1d1KQBlszzHYlFvF4jipvIF1eS0+ruoyBdXktPq7qUgDLZnmOxKLeLxHFTeQLq8lp9XdRkC6vJafV3UpAGWzPMdiUW8XiOK1bPs6ksqiZR0MDYKdiqrY27kxXFf1U2gCO5xcauNSmySTUr/9k=</data> <data key="type">image/jpeg</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index f67370dcff296..b367cdcab9d8b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="productAttributeWysiwyg" type="ProductAttribute"> <data key="attribute_code" unique="suffix">attribute</data> <data key="frontend_input">textarea</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml index 60b38812e4ced..98c9a70e6aad4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ApiProductAttributeMediaGalleryEntryTestImage" type="ProductAttributeMediaGalleryEntry"> <data key="media_type">image</data> <data key="label" unique="suffix">Test Image </data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml index 15c2dc8bbebca..c575f1a5db82f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="productAttributeOption1" type="ProductAttributeOption"> <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> <data key="label" unique="suffix">option1</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml index 68c0a54ff88fc..68f51559a9f31 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="AddToDefaultSet" type="ProductAttributeSet"> <var key="attributeCode" entityKey="attribute_code" entityType="ProductAttribute"/> <data key="attributeSetId">4</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 0df091eb5f8ef..9ae551b69d388 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultProduct" type="product"> <data key="sku" unique="suffix">testSku</data> <data key="type_id">simple</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml index 88ff2bbace47a..6e532637fb6d3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="EavStockItem" type="product_extension_attribute"> <requiredEntity type="stock_item">Qty_1000</requiredEntity> </entity> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductGridData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductGridData.xml index b123800a6cc84..ea0bcafe56c48 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductGridData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductGridData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="PriceFilterRange" type="filter"> <data key="from">10</data> <data key="to">100</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml index 903bf03535a37..ca5024920ad40 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ProductOptionField" type="product_option"> <var key="product_sku" entityType="product" entityKey="sku" /> <data key="title">OptionField</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionValueData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionValueData.xml index 28dd255321844..d16a201cd9ecc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionValueData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionValueData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ProductOptionValueDropdown1" type="product_option_value"> <data key="title">OptionValueDropDown1</data> <data key="sort_order">1</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml index 4fae51de86c45..39ecc2d440fc2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="Qty_1000" type="stock_item"> <data key="qty">1000</data> <data key="is_in_stock">true</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml index a703e56beda01..ce964e2d71503 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="Option1Store0" type="StoreLabel"> <data key="store_id">0</data> <data key="label">option1</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/category-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/category-meta.xml index 0880315db5d6b..ae491aefc10cf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/category-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/category-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateCategory" dataType="category" type="create" auth="adminOauth" url="/V1/categories" method="POST"> <contentType>application/json</contentType> <object key="category" dataType="category"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/custom_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/custom_attribute-meta.xml index aed9b7a979836..a37bb36eb6597 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/custom_attribute-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/custom_attribute-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateCustomAttribute" dataType="custom_attribute" type="create"> <field key="attribute_code">string</field> <field key="value">string</field> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/empty_extension_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/empty_extension_attribute-meta.xml index d8410593cb5b4..7faac6c3b6d3d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/empty_extension_attribute-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/empty_extension_attribute-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateEmptyExtensionAttribute" dataType="empty_extension_attribute" type="create"> </operation> <operation name="UpdateEmptyExtensionAttribute" dataType="empty_extension_attribute" type="update"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/frontend_label-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/frontend_label-meta.xml index d0bcbd3e5db97..063b8c2e5ac63 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/frontend_label-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/frontend_label-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateFrontendLabel" dataType="FrontendLabel" type="create"> <field key="store_id">integer</field> <field key="label">string</field> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml index 212de2b39d363..9ece47c01fca3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateProduct" dataType="product" type="create" auth="adminOauth" url="/V1/products" method="POST"> <contentType>application/json</contentType> <object dataType="product" key="product"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute-meta.xml index 93396352ba506..1e9aa3bc219e9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateProductAttribute" dataType="ProductAttribute" type="create" auth="adminOauth" url="/V1/products/attributes" method="POST"> <contentType>application/json</contentType> <object dataType="ProductAttribute" key="attribute"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_media_gallery_entry-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_media_gallery_entry-meta.xml index 8033e8c33a349..521e864702e57 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_media_gallery_entry-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_media_gallery_entry-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateProductAttributeMediaGalleryEntry" dataType="ProductAttributeMediaGalleryEntry" type="create" auth="adminOauth" url="/V1/products/{sku}/media" method="POST"> <contentType>application/json</contentType> <object key="entry" dataType="ProductAttributeMediaGalleryEntry"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_option-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_option-meta.xml index 176afa8d58d7c..467ff9a48eb77 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_option-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_option-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateProductAttributeOption" dataType="ProductAttributeOption" type="create" auth="adminOauth" url="/V1/products/attributes/{attribute_code}/options" method="POST"> <contentType>application/json</contentType> <object dataType="ProductAttributeOption" key="option"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_set-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_set-meta.xml index eef82b07aaf4f..6f04c48e79254 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_set-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_set-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="AddProductAttributeToAttributeSet" dataType="ProductAttributeSet" type="create" auth="adminOauth" url="/V1/products/attribute-sets/attributes" method="POST"> <contentType>application/json</contentType> <field key="attributeSetId">integer</field> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_extension_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_extension_attribute-meta.xml index 8d0d1e66c81e3..127a754c88808 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_extension_attribute-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_extension_attribute-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateProductExtensionAttribute" dataType="product_extension_attribute" type="create"> <field key="stock_item">stock_item</field> </operation> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link-meta.xml index 5e631b2ea3a28..a2fcbb1417d6f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateProductLink" dataType="product_link" type="create"> <field key="sku">string</field> <field key="link_type">string</field> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link_extension_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link_extension_attribute-meta.xml index 07ea02f5b7aee..90888463ef8a2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link_extension_attribute-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link_extension_attribute-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateProductLinkExtensionAttribute" dataType="product_link_extension_attribute" type="create"> <contentType>application/json</contentType> <field key="qty">integer</field> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_links-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_links-meta.xml index 56b3ee25ef735..450ea99b9d016 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_links-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_links-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateProductLinks" dataType="product_links" type="create" auth="adminOauth" url="/V1/products/{sku}/links" method="POST"> <contentType>application/json</contentType> <array key="items"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_option-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_option-meta.xml index adc5a33507af6..6464c2988ad25 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_option-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_option-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateProductOption" dataType="product_option" type="create"> <field key="product_sku">string</field> <field key="option_id">integer</field> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_option_value-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_option_value-meta.xml index f4273f5796830..bce77bc3a2612 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_option_value-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_option_value-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateProductOptionValue" dataType="product_option_value" type="create"> <field key="title">string</field> <field key="sort_order">integer</field> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/stock_item-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/stock_item-meta.xml index e7e79d69055c6..6ec5f2c8051ea 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/stock_item-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/stock_item-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateStockItem" dataType="stock_item" type="create"> <field key="qty">integer</field> <field key="is_in_stock">boolean</field> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/store_label-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/store_label-meta.xml index abb9b003dc59e..584ba5eebb551 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/store_label-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/store_label-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateStoreLabel" dataType="StoreLabel" type="create"> <field key="store_id">integer</field> <field key="label">string</field> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/validation_rule-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/validation_rule-meta.xml index c568e52b2ab3c..aa120491ece5d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/validation_rule-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/validation_rule-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateValidationRule" dataType="validation_rule" type="create"> <field key="key">string</field> <field key="value">string</field> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryEditPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryEditPage.xml index cfefa8cb2c4bc..e1c8e5c75e9ac 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryEditPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryEditPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminCategoryEditPage" url="catalog/category/edit/id/{{categoryId}}/" area="admin" module="Catalog" parameterized="true"> <section name="AdminCategorySidebarActionSection"/> <section name="AdminCategoryMainActionsSection"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml index 7cabe0e18f0b6..9349e188430f4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminCategoryPage" url="catalog/category/" area="admin" module="Catalog"> <section name="AdminCategorySidebarActionSection"/> <section name="AdminCategoryMainActionsSection"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml index b04aff5f161da..fab87f90f86dd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml @@ -6,7 +6,7 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="ProductAttributePage" url="catalog/product_attribute/new/" area="admin" module="Catalog"> <section name="AdminCreateProductAttributeSection"/> </page> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeGridPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeGridPage.xml index a5de7453d9c23..e6aafa53601a6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeGridPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeGridPage.xml @@ -6,7 +6,7 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminProductAttributeGridPage" url="catalog/product_attribute" area="admin" module="Catalog"> <section name="AdminProductAttributeGridSection"/> </page> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeSetEditPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeSetEditPage.xml index 4034f2ab075d4..3e89cbc8262ce 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeSetEditPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeSetEditPage.xml @@ -6,7 +6,7 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminProductAttributeSetEditPage" url="catalog/product_set/edit/id" area="admin" module="Catalog"> <section name="AdminProductAttributeSetEditSection"/> </page> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeSetGridPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeSetGridPage.xml index 0d879768eb494..d55e71adca24b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeSetGridPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeSetGridPage.xml @@ -6,7 +6,7 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminProductAttributeSetGridPage" url="catalog/product_set/" area="admin" module="ProductAttributeSet"> <section name="AdminProductAttributeSetGridSection"/> </page> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributesEditPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributesEditPage.xml index 4918041d2cd88..66475a93b75b1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributesEditPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributesEditPage.xml @@ -6,7 +6,7 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="ProductAttributesEditPage" url="catalog/product_action_attribute/edit/" area="admin" module="Catalog"> <section name="AdminEditProductAttributesSection"/> </page> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml index be7c44e378f08..fc776b49ba213 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminProductCreatePage" url="catalog/product/new/set/{{set}}/type/{{type}}/" area="admin" module="Magento_Catalog" parameterized="true"> <section name="AdminProductFormSection"/> <section name="AdminProductFormActionSection"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductEditPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductEditPage.xml index 9312d4dfcfbe9..c9debf8bf3b3d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductEditPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductEditPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminProductEditPage" url="catalog/product/edit/id/{{productId}}/" area="admin" module="Magento_Catalog" parameterized="true"> <!-- This page object only exists for the url. Use the AdminProductCreatePage for selectors. --> </page> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductIndexPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductIndexPage.xml index 66cd691176268..a6edf06f2c1b7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductIndexPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductIndexPage.xml @@ -6,7 +6,7 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminProductIndexPage" url="catalog/product/index" area="admin" module="Magento_Catalog"> <section name="AdminProductGridActionSection" /> <section name="AdminProductGridFilterSection" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/ProductCatalogPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/ProductCatalogPage.xml index 742b46fcaf7ed..012aeaaf14e70 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/ProductCatalogPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/ProductCatalogPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="ProductCatalogPage" url="/catalog/product/" area="admin" module="Magento_Catalog"> <section name="ProductCatalogPageSection"/> </page> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontCategoryPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontCategoryPage.xml index c5b9fe869558e..469c153d38b88 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontCategoryPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontCategoryPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCategoryPage" url="/{{var1}}.html" area="storefront" module="Catalog" parameterized="true"> <section name="StorefrontCategoryMainSection"/> <section name="WYSIWYGToolbarSection"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontProductComparePage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontProductComparePage.xml index f0599a021d4c4..5451d92022496 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontProductComparePage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontProductComparePage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontProductComparePage" url="catalog/product_compare/index" module="Magento_Catalog" area="storefront"> <section name="StorefrontProductCompareMainSection" /> </page> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontProductPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontProductPage.xml index 8fd59585938be..5aaa78822af08 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontProductPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontProductPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontProductPage" url="/{{var1}}.html" area="storefront" module="Catalog" parameterized="true"> <section name="StorefrontProductInfoMainSection" /> <section name="StorefrontProductInfoDetailsSection" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminAddProductsToOptionPanelSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminAddProductsToOptionPanelSection.xml index 4541ad25af231..069a8b28698d1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminAddProductsToOptionPanelSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminAddProductsToOptionPanelSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminAddProductsToOptionPanel"> <element name="addSelectedProducts" type="button" selector=".product_form_product_form_bundle-items_modal button.action-primary" timeout="30"/> <element name="filters" type="button" selector=".product_form_product_form_bundle-items_modal button[data-action='grid-filter-expand']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml index 3ed3763da19d6..ee38fb3ff68e5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategoryBasicFieldSection"> <element name="IncludeInMenu" type="checkbox" selector="input[name='include_in_menu']"/> <element name="includeInMenuLabel" type="text" selector="input[name='include_in_menu']+label"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml index 59537274f23c9..ed0325394d591 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategoryContentSection"> <element name="sectionHeader" type="button" selector="div[data-index='content']" timeout="30"/> <element name="uploadButton" type="button" selector="//*[@class='file-uploader-area']/label[text()='Upload']"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMainActionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMainActionsSection.xml index 60a6d852bf6ef..009110a729bde 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMainActionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMainActionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategoryMainActionsSection"> <element name="SaveButton" type="button" selector=".page-actions-inner #save" timeout="30"/> <element name="DeleteButton" type="button" selector=".page-actions-inner #delete" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMessagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMessagesSection.xml index 1214cfd2eb224..fee86ca1caa29 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMessagesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMessagesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategoryMessagesSection"> <element name="SuccessMessage" type="text" selector=".message-success"/> </section> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryModalSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryModalSection.xml index 03b9d76778555..85b8dc894a139 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryModalSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryModalSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategoryModalSection"> <element name="message" type="text" selector="aside.confirm div.modal-content"/> <element name="title" type="text" selector="aside.confirm .modal-header .modal-title"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsGridSection.xml index 540a97fd04e36..6b754dcc5d482 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsGridSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategoryProductsGridSection"> <element name="rowProductId" type="text" selector="#catalog_category_products_table tbody tr:nth-of-type({{row}}) .col-id" parameterized="true"/> <element name="rowProductName" type="text" selector="#catalog_category_products_table tbody tr:nth-of-type({{row}}) .col-name" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml index dc254bdf15982..3c05f72ff1597 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategoryProductsSection"> <element name="sectionHeader" type="button" selector="div[data-index='assign_products']" timeout="30"/> </section> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySEOSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySEOSection.xml index 35852abe3505e..b5d5d61f6468b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySEOSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySEOSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategorySEOSection"> <element name="SectionHeader" type="button" selector="div[data-index='search_engine_optimization']" timeout="30"/> <element name="UrlKeyInput" type="input" selector="input[name='url_key']"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarActionSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarActionSection.xml index e53a9989d661c..0a1901f1fdaf8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarActionSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarActionSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategorySidebarActionSection"> <element name="AddRootCategoryButton" type="button" selector="#add_root_category_button" timeout="30"/> <element name="AddSubcategoryButton" type="button" selector="#add_subcategory_button" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml index 524fac78bc1c1..ef6fb99e88eed 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategorySidebarTreeSection"> <element name="collapseAll" type="button" selector=".tree-actions a:first-child"/> <element name="expandAll" type="button" selector=".tree-actions a:last-child"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryWarningMessagesPopupSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryWarningMessagesPopupSection.xml index 82b3b76df3d2e..4c16e9081f4c6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryWarningMessagesPopupSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryWarningMessagesPopupSection.xml @@ -6,7 +6,7 @@ */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategoryWarningMessagesPopupSection"> <element name="warningMessage" type="text" selector=".modal-inner-wrap .modal-content .message.message-notice"/> <element name="cancelButton" type="button" selector=".modal-inner-wrap .action-secondary"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 24bb0a69c15d3..e7825afa049db 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AttributePropertiesSection"> <element name="propertiesTab" type="button" selector="#product_attribute_tabs_main"/> <element name="DefaultLabel" type="input" selector="#attribute_label"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml index 703e9e7ec70ac..99dfddcc776d3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminEditProductAttributesSection"> <element name="AttributeName" type="text" selector="#name"/> <element name="ChangeAttributeNameToggle" type="checkbox" selector="#toggle_name"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml index caf34a9f355a0..9e0a8ddc17217 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductAttributeGridSection"> <element name="AttributeCode" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true" timeout="30"/> <element name="createNewAttributeBtn" type="button" selector="button[data-index='add_new_attribute_button']"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetActionSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetActionSection.xml index 4c309584d4d56..e165b51ef180e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetActionSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetActionSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductAttributeSetActionSection"> <element name="save" type="button" selector="button[title='Save']" timeout="30"/> <element name="reset" type="button" selector="button[title='Reset']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetEditSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetEditSection.xml index a2193bcafbb01..0814c7ea7dc3e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetEditSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetEditSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductAttributeSetEditSection"> <!-- Groups Column --> <element name="groupTree" type="block" selector="#tree-div1"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetGridSection.xml index 08724222a3885..b906e2fa9084b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetGridSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductAttributeSetGridSection"> <element name="filter" type="input" selector="#setGrid_filter_set_name"/> <element name="searchBtn" type="button" selector="#container button[title='Search']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml index 9e320d9e8b08d..8c15526f03555 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductAttributeSetSection"> <element name="name" type="input" selector="#attribute_set_name"/> <element name="basedOn" type="select" selector="#skeleton_set"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCategoryCreationSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCategoryCreationSection.xml index 81290bf281a56..337cf0527dd4e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCategoryCreationSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCategoryCreationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductCategoryCreationSection"> <element name="firstExampleProduct" type="button" selector=".data-row:nth-of-type(1)"/> <element name="newCategory" type="button" selector="//button/span[text()='New Category']"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml index fc8666ec1c797..a2ad155672a1a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductContentSection"> <element name="sectionHeader" type="button" selector="div[data-index='content']" timeout="30"/> <element name="descriptionTextArea" type="textarea" selector="#product_form_description"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml index 80ed5cb2d7e9d..603428ede8f28 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductCustomizableOptionsSection"> <element name="checkIfCustomizableOptionsTabOpen" type="text" selector="//span[text()='Customizable Options']/parent::strong/parent::*[@data-state-collapsible='closed']"/> <element name="customizableOptions" type="text" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Customizable Options']"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml index 4c6c1020e190d..ed08c84cdb6f8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFiltersSection"> <element name="filtersButton" type="button" selector="#container > div > div.admin__data-grid-header > div:nth-child(1) > div.data-grid-filters-actions-wrap > div > button"/> <element name="clearFiltersButton" type="button" selector="//div[@class='admin__data-grid-header']//button[@class='action-tertiary action-clear']" timeout="10"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml index a314f9cd0ddfd..afbaba41a9bb7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormActionSection"> <element name="backButton" type="button" selector="#back" timeout="30"/> <element name="saveButton" type="button" selector="#save-button" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml index 1042b1e5a5464..0a1804aa284dc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormAdvancedPricingSection"> <element name="customerGroupPriceAddButton" type="button" selector="[data-action='add_new_row']" timeout="30"/> <element name="customerGroupPriceDeleteButton" type="button" selector="[data-action='remove_row']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormChangeStoreSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormChangeStoreSection.xml index 8c9e92d912bf3..04e5445c8ab63 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormChangeStoreSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormChangeStoreSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormChangeStoreSection"> <element name="storeSelector" type="button" selector="//a[contains(text(),'{{var1}}')]" parameterized="true"/> <element name="acceptButton" type="button" selector="button[class='action-primary action-accept']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridConfirmActionSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridConfirmActionSection.xml index d8567df81b6b3..5bf73076e14de 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridConfirmActionSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridConfirmActionSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductGridConfirmActionSection"> <element name="title" type="text" selector=".modal-popup.confirm h1.modal-title"/> <element name="message" type="text" selector=".modal-popup.confirm div.modal-content"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml index 4683576bf9516..611f12a39b510 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductGridFilterSection"> <element name="filters" type="button" selector="button[data-action='grid-filter-expand']"/> <element name="clearAll" type="button" selector=".admin__data-grid-header .admin__data-grid-filters-current._show .action-clear" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridPaginationSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridPaginationSection.xml index 9ef89e1260fa4..fbcfabfa02fa1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridPaginationSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridPaginationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductGridPaginationSection"> <element name="perPageDropdown" type="select" selector=".admin__data-grid-pager-wrap .selectmenu"/> <element name="perPageOption" type="button" selector="//div[@class='admin__data-grid-pager-wrap']//div[@class='selectmenu-items _active']//li//button[text()='{{label}}']" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml index 98afed124c698..a7e20e22f1ddc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductGridSection"> <element name="loadingMask" type="text" selector=".admin__data-grid-loading-mask[data-component*='product_listing']"/> <element name="columnHeader" type="button" selector="//div[@data-role='grid-wrapper']//table[contains(@class, 'data-grid')]/thead/tr/th[contains(@class, 'data-grid-th')]/span[text() = '{{label}}']" parameterized="true" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridTableHeaderSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridTableHeaderSection.xml index fc6ccea20d3c2..7341a6ded7a09 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridTableHeaderSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridTableHeaderSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductGridTableHeaderSection"> <element name="id" type="button" selector=".//*[@class='sticky-header']/following-sibling::*//th[@class='data-grid-th _sortable _draggable _{{order}}']/span[text()='ID']" parameterized="true"/> </section> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml index ce10b1e52aeb0..eca0cb6f02ea1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductImagesSection"> <element name="productImagesToggle" type="button" selector="div[data-index=gallery] .admin__collapsible-title"/> <element name="imageFileUpload" type="input" selector="#fileupload"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMessagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMessagesSection.xml index 5f2e6bd6cf721..59fbeee142dfe 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMessagesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMessagesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductMessagesSection"> <element name="successMessage" type="text" selector=".message-success"/> <element name="errorMessage" type="text" selector=".message.message-error.error"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductModalSlideGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductModalSlideGridSection.xml index bef213e6cdae0..adc3a753f06f5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductModalSlideGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductModalSlideGridSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductModalSlideGridSection"> <element name="productGridXRowYColumnButton" type="input" selector=".modal-slide table.data-grid tr.data-row:nth-child({{row}}) td:nth-child({{column}})" parameterized="true" timeout="30"/> </section> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml index 36b0623f28695..e90d806805f7c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormRelatedUpSellCrossSellSection"> <element name="AddRelatedProductsButton" type="button" selector="button[data-index='button_related']" timeout="30"/> <element name="relatedProductSectionText" type="text" selector=".fieldset-wrapper.admin__fieldset-section[data-index='related']"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductSEOSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductSEOSection.xml index 1d49d05363612..c545fcd408831 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductSEOSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductSEOSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductSEOSection"> <element name="sectionHeader" type="button" selector="div[data-index='search-engine-optimization']" timeout="30"/> <element name="urlKeyInput" type="input" selector="input[name='product[url_key]']"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml index 3048f0e3f5669..bf8812b3acef5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminUpdateAttributesSection"> <element name="saveButton" type="button" selector="button[title='Save']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml index 631cb36e16817..ddec4428f90e2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategoryFilterSection"> <element name="CategoryFilter" type="button" selector="//main//div[@class='filter-options']//div[contains(text(), 'Category')]"/> <element name="CategoryByName" type="button" selector="//main//div[@class='filter-options']//li[@class='item']//a[contains(text(), '{{var1}}')]" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml index 2a6003d837b2e..d484abf2069ff 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategoryMainSection"> <element name="modeListButton" type="button" selector="#mode-list"/> <element name="CategoryTitle" type="text" selector="#page-title-heading span"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml index 19b3a5cc127a7..7ea74d7913758 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategoryProductSection"> <element name="ProductTitleByNumber" type="button" selector="//main//li[{{var1}}]//a[@class='product-item-link']" parameterized="true"/> <element name="ProductPriceByNumber" type="text" selector="//main//li[{{var1}}]//span[@class='price']" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml index 9cd35f65c297a..0599a42bfd7a9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml @@ -6,7 +6,7 @@ */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategorySidebarSection"> <element name="filterOptionsTitle" type="text" selector="//div[@class='filter-options-title' and contains(text(), '{{var1}}')]" parameterized="true"/> <element name="filterOptions" type="text" selector=".filter-options-content .items"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryTopToolbarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryTopToolbarSection.xml index 2b44bf1db7efd..68cc9e0204912 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryTopToolbarSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryTopToolbarSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategoryTopToolbarSection"> <element name="gridMode" type="button" selector=".//*[@class='toolbar toolbar-products'][1]//*[@id='mode-grid']" timeout="30"/> <element name="listMode" type="button" selector=".//*[@class='toolbar toolbar-products'][1]//*[@id='mode-list']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontComparisonSidebarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontComparisonSidebarSection.xml index 0fdda3eaae952..d097d6bbc4626 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontComparisonSidebarSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontComparisonSidebarSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontComparisonSidebarSection"> <element name="Compare" type="button" selector="//main//div[contains(@class, 'block-compare')]//a[contains(@class, 'action compare')]"/> <element name="ClearAll" type="button" selector="//main//div[contains(@class, 'block-compare')]//a[contains(@class, 'action clear')]"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontFooterSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontFooterSection.xml index cf956004ae498..1c937637ad823 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontFooterSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontFooterSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontFooterSection"> <element name="switchStoreButton" type="button" selector="#switcher-store-trigger"/> <element name="storeLink" type="button" selector="//ul[@class='dropdown switcher-dropdown']//a[contains(text(),'{{var1}}')]" parameterized="true" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontHeaderSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontHeaderSection.xml index 6b0130eefc39b..509ad2b8f849c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontHeaderSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontHeaderSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontHeaderSection"> <element name="NavigationCategoryByName" type="button" selector="//nav//a[span[contains(., '{{var1}}')]]" parameterized="true"/> </section> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml index 1a9406b9975e6..dea1b2a5af752 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontMessagesSection"> <element name="success" type="text" selector="div.message-success.success.message"/> </section> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontNavigationSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontNavigationSection.xml index ad575b640bd20..e8f35fc6787b7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontNavigationSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontNavigationSection.xml @@ -6,7 +6,7 @@ */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontNavigationSection"> <element name="topCategory" type="button" selector="//a[contains(@class,'level-top')]/span[contains(text(),'{{var1}}')]" parameterized="true"/> <element name="subCategory" type="button" selector="//ul[contains(@class,'submenu')]//span[contains(text(),'{{var1}}')]" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProducRelatedProductsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProducRelatedProductsSection.xml index e15723582dbf0..f4db37b677584 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProducRelatedProductsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProducRelatedProductsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductRelatedProductsSection"> <element name="relatedProductsActionsHeaderText" type="text" selector=".block.related .block-actions" /> <element name="relatedProductsListSectionText" type="text" selector=".block.related .products.wrapper.grid.products-grid.products-related" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductActionSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductActionSection.xml index 65d6b7c5f61cb..98dc5e764fd77 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductActionSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductActionSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductActionSection"> <element name="quantity" type="input" selector="#qty"/> <element name="addToCart" type="button" selector="#product-addtocart-button"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductCompareMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductCompareMainSection.xml index 728f9a5a174cd..ad31be6b277ee 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductCompareMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductCompareMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductCompareMainSection"> <element name="PageName" type="text" selector="//*[@id='maincontent']//h1//span"/> <element name="ProductLinkByName" type="button" selector="//*[@id='product-comparison']//tr//strong[@class='product-item-name']/a[contains(text(), '{{var1}}')]" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoDetailsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoDetailsSection.xml index 5688811cb96a6..0745c0d0819a0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoDetailsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoDetailsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoDetailsSection"> <element name="productNameForReview" type="text" selector=".legend.review-legend>strong" /> <element name="detailsTab" type="button" selector="#tab-label-description-title" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 42ca3836e6c1c..b93a70559fc4a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoMainSection"> <element name="stock" type="input" selector=".stock.available"/> <element name="productName" type="text" selector=".base"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml index 0273b39f48aba..83c3ca5348606 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductMediaSection"> <element name="imageFile" type="text" selector="//*[@class='product media']//img[contains(@src, '{{filename}}')]" parameterized="true"/> </section> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMoreInformationSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMoreInformationSection.xml index fc2102e073de3..ee687fa62da93 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMoreInformationSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMoreInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductMoreInformationSection"> <element name="moreInformation" type="button" selector="#tab-label-additional-title" timeout="30"/> <element name="moreInformationTextArea" type="textarea" selector="#additional"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml index acda5c40af8a3..0435d8cf3df47 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductPageSection"> <element name="qtyInput" type="button" selector="input.input-text.qty"/> <element name="addToCartBtn" type="button" selector="button.action.tocart.primary" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml index c9b6e033a2fd8..88a39a9087bb3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddDefaultImageSimpleProductTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml index add917199e2eb..3f857c258924f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddRemoveProductImageVirtualProductTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml index 6ee72877a0da0..8ac0cfa512b03 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddImageForCategoryTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml index 479247ade8cb2..50d192a27e46d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddImageToWYSIWYGCatalogTest"> <before> <actionGroup ref="LoginActionGroup" stepKey="login"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml index d6e055c43322a..9973194679b51 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddImageToWYSIWYGProductTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml index 902f51c4a15a7..7731a055f7e14 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminApplyTierPriceToProductTest"> <annotations> <features value="Apply tier price to a product"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml index aed667db1f7b2..4261721d36064 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAssignProductAttributeToAttributeSetTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml index d5483f772f028..dc4e6ad3bf036 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateCategoryFromProductPageTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml index 7e3e2cd918f5e..bfb9557910642 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateCategoryTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSet.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSet.xml index 038c8fd2263f0..713e1b7d6dfd1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSet.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSet.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateProductCustomAttributeSet"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml index 0340eea852a4c..95d74b9653113 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateProductDuplicateUrlkeyTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml index dbe5a90d592da..61b0e8083175c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateRootCategoryAndSubcategoriesTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml index b2e6119ef4e13..6096ee1fa3996 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateSimpleProductTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml index 486fea9d91f9e..896a28d0298e6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateSimpleProductWithUnicodeTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml index 71f2ffecb4652..7603400ba8dcd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminEditTextEditorProductAttributeTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml index 2edca19deeb00..8d5121cf21461 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMassChangeProductsStatusTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml index 7c3e31e90c015..c0eebd1512d6d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMassUpdateProductAttributesGlobalScopeTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml index be0bc2bf52a0b..fe0e46369c5e6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMassUpdateProductAttributesMissingRequiredFieldTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml index f7a04709b76d9..845c47c0e4c20 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMassUpdateProductAttributesStoreViewScopeTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml index d29fde8590c9d..1785cc5b3ea57 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml @@ -6,7 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMoveAnchoredCategoryTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml index 012c956c2dbef..af69a7da7ba4f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMultipleWebsitesUseDefaultValuesTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml index b079d35296e43..10af015912ad2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminProductGridFilteringByDateAttributeTest"> <annotations> <title value="Verify Set Product as new Filter input on Product Grid doesn't getreset to currentDate"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml index c8be44eb73ca9..f2dfb1083cf89 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminProductStatusAttributeDisabledByDefaultTest"> <annotations> <title value="Verify the default option value for product Status attribute is set correctly during product creation"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml index 4a3b370c7da08..9760dc579b10b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminRemoveDefaultImageSimpleProductTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml index e249c8e8ceabc..a740b700c3026 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminRemoveDefaultImageVirtualProductTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml index c68a848fb0fb6..fb33e18379982 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminRemoveImageFromCategoryTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml index 2b9733fd2fa5c..fcbc6b3e5503a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminSimpleProductUiValidationTest"> <annotations> <title value="UI elements on the simple product edit screen should be organized as expected"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml index b51f6a6e43249..1cd0e15780c11 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminSimpleProductImagesTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml index 98c708b137657..1fe78cfe75344 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminSimpleProductSetEditContentTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml index 48b9094d6d791..b06502ce94c65 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminSimpleSetEditRelatedProductsTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml index 5a07b11a204af..dc6cf012840eb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminUnassignProductAttributeFromAttributeSetTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml index 013b1b6d38123..2ff83afa15e5e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminUpdateCategoryStoreUrlKeyTest"> <annotations> <features value="SEO-friendly URL Key Update"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml index bb8f76ebe7bf6..7b0d063ba822e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminVirtualProductSetEditContentTest" extends="AdminSimpleProductSetEditContentTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualSetEditRelatedProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualSetEditRelatedProductsTest.xml index f3d0f023913ff..e630545fff8fb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualSetEditRelatedProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualSetEditRelatedProductsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminVirtualSetEditRelatedProductsTest" extends="AdminSimpleSetEditRelatedProductsTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml index 03d919e329115..a4c8b492d9d84 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdvanceCatalogSearchSimpleProductByNameTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml index c97d2c45be951..899f3e61b5b86 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="ConfigurableOptionTextinputLengthValidationHintTest"> <annotations> <features value="Product Customizable Option"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml index 4ec775f7ea2d8..a5db0776feee9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="DeleteCategoriesTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml index d5d9de2648ecf..1419ca4cb42ef 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CAdminTest"> <annotations> <features value="End to End scenarios"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index e80de8122e810..7c0de6da18caf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CGuestUserTest"> <annotations> <features value="End to End scenarios"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index b8827616e3ec5..3f48c3ca811e3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CLoggedInUserTest"> <before> <createData entity="ApiCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml index 26f6eb7910fa2..058261a468292 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="SaveProductWithCustomOptionsAdditionalWebsiteTest"> <annotations> <features value="Save a product with Custom Options and assign to a different website"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml index 084ae62788d41..0049dcb504335 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="SimpleProductTwoCustomOptionsTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml index 05964c5cce264..23d24c70bb682 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontProductNameWithDoubleQuote"> <annotations> <title value="Product with double quote in name"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml index 92013f6f9d0f0..92ccfc5d6b338 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontProductWithEmptyAttributeTest"> <annotations> <title value="Product attribute is not visible on storefront if it is empty"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml index c03241348e807..2527363140e16 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontProductsCompareWithEmptyAttributeTest"> <annotations> <title value="Product attribute is not visible on product compare page if it is empty"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViews.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViews.xml index 7d843012d1d1d..9eae7d28e6f69 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViews.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViews.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml index 8628ae9b99d3a..5d7928aee1118 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontPurchaseProductWithCustomOptions"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index 9e0f80eb9ea5d..bc89739f00c13 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle"> <annotations> <group value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml index 51290ba12f2c7..ed0962260650b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="VerifyChildCategoriesShouldNotIncludeInMenuTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml index 234148830bd43..53bcac5b1d5f0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="VerifyDefaultWYSIWYGToolbarOnProductTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest.xml index 5fc0b6478ffb8..aad83cd0c2aff 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml index e2077f8676706..26dd94b19de64 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest"> <annotations> <features value="Catalog"/> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml index 1bec4cc99c0e8..c7c9126f46803 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="displayOutOfStockProduct"> <amOnPage url="{{InventoryConfigurationPage.url}}" stepKey="navigateToInventoryConfigurationPage"/> <waitForPageLoad stepKey="waitForConfigPageToLoad"/> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Page/InventoryConfigurationPage.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Page/InventoryConfigurationPage.xml index 95e873a3b164d..ba8a3c300b2e8 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Page/InventoryConfigurationPage.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Page/InventoryConfigurationPage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="InventoryConfigurationPage" url="admin/system_config/edit/section/cataloginventory/" area="admin" module="Magento_Config"> <section name="InventorySection"/> </page> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Section/InventorySection.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Section/InventorySection.xml index 55fbc84ead96a..929f43467b947 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Section/InventorySection.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Section/InventorySection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="InventoryConfigSection"> <element name="ProductStockOptionsTab" type="button" selector="#cataloginventory_options-head"/> <element name="CheckIfProductStockOptionsTabExpanded" type="button" selector="#cataloginventory_options-head:not(.open)"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index f82974b7bc82a..bfc059ccb247b 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- action group to create a new catalog price rule giving a catalogRule entity --> <actionGroup name="newCatalogPriceRuleByUI"> <arguments> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml index a996e3ea958b1..b1cb1920f39f4 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultCatalogRule" type="catalogRule"> <data key="name" unique="suffix">CatalogPriceRule</data> <data key="description">Catalog Price Rule Description</data> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Metadata/catalog-rule-meta.xml b/app/code/Magento/CatalogRule/Test/Mftf/Metadata/catalog-rule-meta.xml index 6f5bd2decc6ce..0d89c7970b852 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Metadata/catalog-rule-meta.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Metadata/catalog-rule-meta.xml @@ -8,7 +8,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="createCatalogRule" dataType="catalogRule" type="create" auth="adminFormKey" url="/catalog_rule/promo_catalog/save/" method="POST"> <contentType>application/x-www-form-urlencoded</contentType> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Page/CatalogRulePage.xml b/app/code/Magento/CatalogRule/Test/Mftf/Page/CatalogRulePage.xml index e080a252e7855..511a9ac0615d5 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Page/CatalogRulePage.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Page/CatalogRulePage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CatalogRulePage" url="catalog_rule/promo_catalog/" module="Magento_CatalogRule" area="admin"> <section name="AdminSecondaryGridSection"/> </page> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml index d212aac1c0d4a..bab9842caaa42 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCatalogPriceRuleStagingSection"> <element name="status" type="select" selector=".modal-component [data-index='is_active'] select"/> </section> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml index 4773fd8224fe3..071b96c06b544 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewCatalogPriceRule"> <element name="saveAndApply" type="button" selector="#save_and_apply" timeout="30"/> <element name="save" type="button" selector="#save" timeout="30"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml index 1e7aac745d748..e4aed558ccea0 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateCatalogPriceRuleByPercentTest"> <annotations> <features value="CatalogRule"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml index 64f19cb3ca881..e7be6e8443a36 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontInactiveCatalogRuleTest"> <annotations> <features value="CatalogRule"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml index c4bb5ff4b6dc7..51dd8a80fcb41 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Quick search the phrase and check if the result page contains correct information --> <actionGroup name="StorefrontCheckQuickSearchActionGroup"> <arguments> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Data/ConstData.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Data/ConstData.xml index 08fc1ce00e5e4..52fd61301c3b3 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Data/ConstData.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Data/ConstData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <!-- @TODO: Get rid off this workaround and its usages after MQE-498 is implemented --> <entity name="CONST" type="CONST"> <data key="apiSimpleProduct">Api Simple Product</data> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Page/StorefrontCatalogSearchAdvancedFormPage.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Page/StorefrontCatalogSearchAdvancedFormPage.xml index c52d816f0f30a..28515c8186a23 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Page/StorefrontCatalogSearchAdvancedFormPage.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Page/StorefrontCatalogSearchAdvancedFormPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCatalogSearchAdvancedFormPage" url="/catalogsearch/advanced/" area="storefront" module="Magento_CatalogSearch"> <section name="StorefrontCatalogSearchAdvancedFormSection" /> <section name="StorefrontQuickSearchSection" /> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Page/StorefrontCatalogSearchAdvancedResultPage.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Page/StorefrontCatalogSearchAdvancedResultPage.xml index 422ccc652b793..0584f5e338035 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Page/StorefrontCatalogSearchAdvancedResultPage.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Page/StorefrontCatalogSearchAdvancedResultPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCatalogSearchAdvancedResultPage" url="/catalogsearch/advanced/result" area="storefront" module="Magento_CatalogSearch"> <section name="StorefrontCatalogSearchAdvancedResultMainSection" /> <section name="StorefrontQuickSearchSection" /> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Page/StorefrontCatalogSearchPage.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Page/StorefrontCatalogSearchPage.xml index 6141aa96226c0..0700adb6d30e0 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Page/StorefrontCatalogSearchPage.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Page/StorefrontCatalogSearchPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCatalogSearchPage" url="/catalogsearch/result/" area="storefront" module="Magento_CatalogSearch"> <section name="StorefrontCatalogSearchMainSection" /> <section name="StorefrontQuickSearchSection" /> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchAdvancedFormSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchAdvancedFormSection.xml index d7c63ca1af2de..6889025530098 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchAdvancedFormSection.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchAdvancedFormSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCatalogSearchAdvancedFormSection"> <element name="SearchTitle" type="text" selector=".page-title span"/> <element name="ProductName" type="input" selector="#name"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchAdvancedResultMainSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchAdvancedResultMainSection.xml index d0634754eeed8..6b28b4f36c6a7 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchAdvancedResultMainSection.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchAdvancedResultMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCatalogSearchAdvancedResultMainSection"> <element name="SearchTitle" type="text" selector=".page-title span"/> <element name="ProductItemInfo" type="button" selector=".product-item-info"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchMainSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchMainSection.xml index 165629f1cea81..8b35ef3336175 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchMainSection.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCatalogSearchMainSection"> <element name="SearchTitle" type="text" selector=".page-title span"/> <element name="ProductItemInfo" type="button" selector=".product-item-info"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontFooterSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontFooterSection.xml index dae21aeefc1f9..dbecf55a0104b 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontFooterSection.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontFooterSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontFooterSection"> <element name="AdvancedSearch" type="button" selector="//footer//ul//li//a[text()='Advanced Search']"/> </section> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml index f33f3db14b6cc..13665100f79af 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdvanceCatalogSearchSimpleProductByNameTest"> <annotations> <features value="CatalogSearch"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index b19c00eaf325b..99f3fc00a7401 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CGuestUserTest"> <!-- Step 2: User searches for product --> <comment userInput="Start of searching products" stepKey="startOfSearchingProducts" after="endOfBrowsingCatalog"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 5669a788105fa..367cb6a6e214e 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CLoggedInUserTest"> <!-- Step 2: User searches for product --> <comment userInput="Start of searching products" stepKey="startOfSearchingProducts" after="endOfBrowsingCatalog"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml index e70bccbfdfe2b..031387a6cbba7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Go to checkout from minicart --> <actionGroup name="GoToCheckoutFromMinicartActionGroup"> <wait stepKey="wait" time="10" /> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml index e7a6e219d28b1..5a71b82c15cdb 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Guest checkout filling billing section --> <actionGroup name="GuestCheckoutFillNewBillingAddressActionGroup"> <arguments> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml index b2402d94723cd..cb53a2a4f381f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="clickViewAndEditCartFromMiniCart"> <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml index 33b83fe63fdc1..5bb06326ec803 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Add Product to Cart from the category page and check message and product count in Minicart --> <actionGroup name="StorefrontAddCategoryProductToCartActionGroup"> <arguments> diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/ConstData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/ConstData.xml index 1703d7255e1f8..f946d04fc9f95 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Data/ConstData.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Data/ConstData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <!-- @TODO: Get rid off this workaround and its usages after MQE-498 is implemented --> <entity name="CONST" type="CONST"> <data key="successGuestCheckoutOrderNumberMessage">Your order # is:</data> diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml index 51fb264a16a1c..530157851191f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <!-- @TODO: Get rid off this workaround and its usages after MQE-498 is implemented --> <entity name="E2EB2CQuote" type="Quote"> <data key="subtotal">480.00</data> diff --git a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml index 433d397ee81a9..b0acc64c77727 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CheckoutCartPage" url="/checkout/cart" module="Magento_Checkout" area="storefront"> <section name="CheckoutCartProductSection"/> <section name="CheckoutCartSummarySection"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml index aa11d42275a38..d3fa045e4654f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CheckoutPage" url="/checkout" area="storefront" module="Magento_Checkout"> <section name="CheckoutShippingSection"/> <section name="CheckoutShippingMethodsSection"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutShippingPage.xml b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutShippingPage.xml index 59c3b20aca674..07f9e9e6481f7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutShippingPage.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutShippingPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CheckoutShippingPage" url="/checkout/#shipping" module="Checkout" area="storefront"> <section name="CheckoutShippingGuestInfoSection"/> <section name="CheckoutShippingSection"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutSuccessPage.xml b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutSuccessPage.xml index 1c3293267e2ab..ebca0651b457d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutSuccessPage.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutSuccessPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CheckoutSuccessPage" url="/checkout/onepage/success/" area="storefront" module="Magento_Checkout"> <section name="CheckoutSuccessMainSection"/> <section name="CheckoutSuccessRegisterSection"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Page/GuestCheckoutReviewAndPaymentsPage.xml b/app/code/Magento/Checkout/Test/Mftf/Page/GuestCheckoutReviewAndPaymentsPage.xml index cac9c934cd662..90f8a914b4f42 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Page/GuestCheckoutReviewAndPaymentsPage.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Page/GuestCheckoutReviewAndPaymentsPage.xml @@ -6,7 +6,7 @@ */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="GuestCheckoutReviewAndPaymentsPage" url="/checkout/#payment" area="storefront" module="Magento_Checkout"> <section name="CheckoutPaymentSection"/> </page> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/AdminDataGridHeaderSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/AdminDataGridHeaderSection.xml index 56062f96152c2..3e1f902f6c3be 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/AdminDataGridHeaderSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/AdminDataGridHeaderSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminDataGridHeaderSection"> <element name="attributeCodeFilterInput" type="input" selector=".admin__data-grid-filters input[name='attribute_code']"/> </section> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml index b3e3f082319fb..ab82d9fdd93b5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutCartProductSection"> <element name="ProductLinkByName" type="button" selector="//main//table[@id='shopping-cart-table']//tbody//tr//strong[contains(@class, 'product-item-name')]//a[contains(text(), '{{var1}}')]" diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml index 01b483c8ecf0b..4eb396f4d10eb 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutCartSummarySection"> <element name="subtotal" type="text" selector="//*[@id='cart-totals']//tr[@class='totals sub']//td//span[@class='price']"/> <element name="shippingMethod" type="text" selector="//*[@id='cart-totals']//tr[@class='totals shipping excl']//th//span[@class='value']"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutHeaderSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutHeaderSection.xml index ca42eff89cf26..babbe51746df8 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutHeaderSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutHeaderSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutHeaderSection"> <element name="shippingMethodStep" type="text" selector=".opc-progress-bar-item:nth-of-type(1)"/> <element name="reviewAndPaymentsStep" type="text" selector=".opc-progress-bar-item:nth-of-type(2)"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutOrderSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutOrderSummarySection.xml index cb079d2f0361e..3074f390306b1 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutOrderSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutOrderSummarySection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutOrderSummarySection"> <element name="miniCartTab" type="button" selector=".title[role='tab']"/> <element name="productItemName" type="text" selector=".product-item-name"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index 866f5f5070940..38eea1c6e7971 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutPaymentSection"> <element name="isPaymentSection" type="text" selector="//*[@class='opc-progress-bar']/li[contains(@class, '_active') and span[contains(.,'Review & Payments')]]"/> <element name="availablePaymentSolutions" type="text" selector="#checkout-payment-method-load>div>div>div:nth-child(2)>div.payment-method-title.field.choice"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml index ca13af52c1ed5..ad2a43eb90c8c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutShippingGuestInfoSection"> <element name="email" type="input" selector="#customer-email"/> <element name="firstName" type="input" selector="input[name=firstname]"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingMethodsSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingMethodsSection.xml index 2d07cdd9bd08d..ceb4505c79693 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingMethodsSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingMethodsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutShippingMethodsSection"> <element name="next" type="button" selector="button.button.action.continue.primary" timeout="30"/> <element name="firstShippingMethod" type="radio" selector="//*[@id='checkout-shipping-method-load']//input[@class='radio']"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml index 136658cc59106..494a365ffd507 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutShippingSection"> <element name="isShippingStep" type="text" selector="//*[@class='opc-progress-bar']/li[contains(@class, '_active') and span[contains(.,'Shipping')]]"/> <element name="shippingTab" type="text" selector="//li[contains(@class,'opc-progress-bar-item')]//*[text()='Shipping']" timeout="30"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml index 8a55015f9d244..34819f641cbc9 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutSuccessMainSection"> <element name="successTitle" type="text" selector=".page-title"/> <element name="success" type="text" selector="div.checkout-success"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessRegisterSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessRegisterSection.xml index 271ccec450510..d0ef8d347efb5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessRegisterSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessRegisterSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutSuccessRegisterSection"> <element name="registerMessage" type="text" selector="#registration p:nth-child(1)"/> <element name="customerEmail" type="text" selector="#registration p:nth-child(2)"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StoreFrontRemoveItemModalSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StoreFrontRemoveItemModalSection.xml index 4e2a08e94bd9f..e8001af6f0344 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StoreFrontRemoveItemModalSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StoreFrontRemoveItemModalSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StoreFrontRemoveItemModalSection"> <element name="message" type="text" selector="aside.confirm div.modal-content"/> <element name="ok" type="button" selector="aside.confirm .modal-footer .action-primary"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCategoryProductSection.xml index 0edbb21bc6f5d..0427938a02df1 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCategoryProductSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategoryProductSection"> <element name="ProductAddToCartByNumber" type="button" selector="//main//li[{{var1}}]//button[contains(@class, 'tocart')]" parameterized="true"/> <element name="ProductAddToCartByName" type="button" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//button[contains(@class, 'tocart')]" parameterized="true"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMessagesSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMessagesSection.xml index 4341d99c3fb30..e70ff2b445194 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMessagesSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMessagesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontMessagesSection"> <!-- @TODO: Use general message selector after MQE-694 is fixed --> <element name="messageProductAddedToCart" type="text" diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml index 2c556f25f2064..1c46b8677503b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontMinicartSection"> <element name="productCount" type="text" selector="//header//div[contains(@class, 'minicart-wrapper')]//a[contains(@class, 'showcart')]//span[@class='counter-number']"/> <element name="productLinkByName" type="button" selector="//header//ol[@id='mini-cart']//div[@class='product-item-details']//a[contains(text(), '{{var1}}')]" parameterized="true"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontProductCompareMainSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontProductCompareMainSection.xml index 823260be42f2a..200a742e58f14 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontProductCompareMainSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontProductCompareMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductCompareMainSection"> <element name="ProductAddToCartByName" type="button" selector="//*[@id='product-comparison']//td[.//strong[@class='product-item-name']/a[contains(text(), '{{var1}}')]]//button[contains(@class, 'tocart')]" parameterized="true"/> </section> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 1ff5d2c874459..95929401620b8 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoMainSection"> <element name="AddToCart" type="button" selector="#product-addtocart-button"/> </section> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml index 66146137d0122..c60eb79f92de1 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest"> <annotations> <features value="Checkout"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml index add1a1b1cf9be..71a0c7f7fbdb3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AddressStateFieldShouldNotAcceptJustIntegerValuesTest"> <annotations> <features value="Checkout"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml index d718222283586..a41b1afc74368 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="CheckCheckoutSuccessPageAsRegisterCustomer"> <annotations> <features value="Checkout"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index e386698092aa4..00d80cc2a94d9 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CGuestUserTest"> <!-- Step 3: User adds products to cart --> <comment userInput="Start of adding products to cart" stepKey="startOfAddingProductsToCart" after="endOfBrowsingCatalog"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 6effeec685106..05a6939941f3e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CLoggedInUserTest"> <!-- Step 3: User adds products to cart --> <comment userInput="Start of adding products to cart" stepKey="startOfAddingProductsToCart" after="endOfBrowsingCatalog"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml index efa8b4ca75147..1f3d9db5ca524 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="NoErrorCartCheckoutForProductsDeletedFromMiniCartTest"> <annotations> <features value="Checkout"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml index 2452e7b36be00..6f86121e53166 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontCustomerCheckoutTest"> <annotations> <features value="Checkout"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml index 9d88e42447cb6..a7b82d54afb30 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontGuestCheckoutTest"> <annotations> <features value="Checkout"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSBlockContentActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSBlockContentActionGroup.xml index 553d851707b96..d2f81c1c24c35 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSBlockContentActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSBlockContentActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertBlockContent"> <grabValueFrom selector="{{BlockNewPageBasicFieldsSection.blockTitle}}" stepKey="grabTextFromTitle"/> <assertEquals stepKey="assertTitle" message="pass"> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageContentActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageContentActionGroup.xml index f286c9159c6d8..58318660d2c42 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageContentActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageContentActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertCMSPageContent"> <grabValueFrom selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" stepKey="grabTextFromTitle"/> <assertEquals stepKey="assertTitle" message="pass"> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssignBlockToCMSPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssignBlockToCMSPageActionGroup.xml index 3fa72c2d6b561..5720e79e95abd 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssignBlockToCMSPageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssignBlockToCMSPageActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssignBlockToCMSPage"> <arguments> <argument name="Block" defaultValue=""/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CMSActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CMSActionGroup.xml index 06419356d8e84..75e059f620c2d 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CMSActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CMSActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="navigateToCreatedCMSPage"> <arguments> <argument name="CMSPage" defaultValue=""/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml index 2225d3d34a655..c51e673139af9 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CreateNewPageWithAllValues"> <arguments> <argument name="PageTitle" type="string"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteImageFromStorageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteImageFromStorageActionGroup.xml index cbd239cde80fe..6de6f27e1069f 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteImageFromStorageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteImageFromStorageActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="DeleteImageFromStorageActionGroup"> <arguments> <argument name="Image"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml index 690ad9881c7fc..2a2b2ff15d375 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="DeletePageByUrlKeyActionGroup"> <arguments> <argument name="UrlKey" type="string"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutBlockContentActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutBlockContentActionGroup.xml index ef7c925c3f8f7..3ffc999b41abb 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutBlockContentActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutBlockContentActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="FillOutBlockContent"> <fillField selector="{{BlockNewPageBasicFieldsSection.blockTitle}}" userInput="{{_defaultBlock.title}}" stepKey="fillFieldTitle1"/> <fillField selector="{{BlockNewPageBasicFieldsSection.identifier}}" userInput="{{_defaultBlock.identifier}}" stepKey="fillFieldIdentifier"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutCMSPageContentActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutCMSPageContentActionGroup.xml index 5caeadcea282d..e47ff472ccdcb 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutCMSPageContentActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutCMSPageContentActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="FillOutCMSPageContent"> <fillField selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{_duplicatedCMSPage.title}}" stepKey="fillFieldTitle"/> <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickExpandContentTabForPage"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/NavigateToMediaFolderActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/NavigateToMediaFolderActionGroup.xml index 031481d90d1bd..3c447f808e721 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/NavigateToMediaFolderActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/NavigateToMediaFolderActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="NavigateToMediaFolderActionGroup"> <arguments> <argument name="FolderName" type="string"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml index 54c4164749152..3016fba6caba8 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="RestoreLayoutSetting"> <waitForElementVisible selector="{{DefaultLayoutsSection.pageLayout}}" stepKey="waittForDefaultCMSLayout" after="expandDefaultLayouts" /> <selectOption selector="{{DefaultLayoutsSection.pageLayout}}" userInput="1 column" stepKey="selectOneColumn" before="clickSaveConfig"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup.xml index 8656f4e03a21e..e31eb44146af4 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="searchBlockOnGridPage"> <arguments> <argument name="Block" defaultValue=""/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SelectImageFromMediaStorageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SelectImageFromMediaStorageActionGroup.xml index 8c1d17c8d9bed..6060ba6091139 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SelectImageFromMediaStorageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SelectImageFromMediaStorageActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="clickBrowseBtnOnUploadPopup"> <click selector="{{MediaGallerySection.Browse}}" stepKey="clickBrowse" /> <waitForPageLoad stepKey="waitForPageLoad1" /> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml index 24900ad33b560..53706b223a991 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="VerifyTinyMCEActionGroup"> <waitForElementVisible selector="{{TinyMCESection.TinyMCE4}}" stepKey="waitForTinyMCE" time="30" /> <seeElement selector="{{TinyMCESection.TinyMCE4}}" stepKey="seeTinyMCE4" /> diff --git a/app/code/Magento/Cms/Test/Mftf/Data/BlockPageData.xml b/app/code/Magento/Cms/Test/Mftf/Data/BlockPageData.xml index 9e0db2ada4a2f..368df3baa561f 100644 --- a/app/code/Magento/Cms/Test/Mftf/Data/BlockPageData.xml +++ b/app/code/Magento/Cms/Test/Mftf/Data/BlockPageData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultBlock" type="block"> <data key="title">Default Block</data> <data key="identifier" unique="suffix" >block</data> diff --git a/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml b/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml index 5df8ebab2d380..7b8140c7afbd1 100644 --- a/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml +++ b/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultCmsPage" type="cms_page"> <data key="title">Test CMS Page</data> <data key="content_heading">Test Content Heading</data> diff --git a/app/code/Magento/Cms/Test/Mftf/Metadata/block-meta.xml b/app/code/Magento/Cms/Test/Mftf/Metadata/block-meta.xml index d764275f7c44b..c007c89b313ae 100644 --- a/app/code/Magento/Cms/Test/Mftf/Metadata/block-meta.xml +++ b/app/code/Magento/Cms/Test/Mftf/Metadata/block-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateBlock" dataType="block" type="create" auth="adminOauth" url="/V1/cmsBlock" method="POST"> <contentType>application/json</contentType> <object key="block" dataType="block"> diff --git a/app/code/Magento/Cms/Test/Mftf/Metadata/cms-meta.xml b/app/code/Magento/Cms/Test/Mftf/Metadata/cms-meta.xml index 495ca2ee0c2fe..44a9d9452e8fe 100644 --- a/app/code/Magento/Cms/Test/Mftf/Metadata/cms-meta.xml +++ b/app/code/Magento/Cms/Test/Mftf/Metadata/cms-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateCMSPage" dataType="cms_page" type="create" auth="adminOauth" url="/V1/cmsPage" method="POST"> <contentType>application/json</contentType> <object key="page" dataType="cms_page"> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/CmsBlocksPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/CmsBlocksPage.xml index 790c2feafcad0..1d9564fee8680 100644 --- a/app/code/Magento/Cms/Test/Mftf/Page/CmsBlocksPage.xml +++ b/app/code/Magento/Cms/Test/Mftf/Page/CmsBlocksPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CmsBlocksPage" url="/cms/block/" area="admin" module="Magento_Cms"> <section name="BlockPageActionsSection"/> </page> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/CmsNewBlockPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/CmsNewBlockPage.xml index d607c1ccf39af..0a2b7a7ed37b4 100644 --- a/app/code/Magento/Cms/Test/Mftf/Page/CmsNewBlockPage.xml +++ b/app/code/Magento/Cms/Test/Mftf/Page/CmsNewBlockPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CmsNewBlock" area="admin" url="/cms/block/new" module="Magento_Cms"> <section name="CmsNewBlockBlockActionsSection"/> <section name="CmsNewBlockBlockBasicFieldsSection"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/CmsNewPagePage.xml b/app/code/Magento/Cms/Test/Mftf/Page/CmsNewPagePage.xml index b165d6c044c2b..c844dc55ea156 100644 --- a/app/code/Magento/Cms/Test/Mftf/Page/CmsNewPagePage.xml +++ b/app/code/Magento/Cms/Test/Mftf/Page/CmsNewPagePage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CmsNewPagePage" url="/cms/page/new" area="admin" module="Magento_Cms"> <section name="CmsNewPagePageActionsSection"/> <section name="CmsNewPagePageBasicFieldsSection"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/CmsPagesPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/CmsPagesPage.xml index 9dcb3d608d04e..45ba6eb6cf00c 100644 --- a/app/code/Magento/Cms/Test/Mftf/Page/CmsPagesPage.xml +++ b/app/code/Magento/Cms/Test/Mftf/Page/CmsPagesPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CmsPagesPage" url="/cms/page" area="admin" module="Magento_Cms"> <section name="CmsPagesPageActionsSection"/> </page> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/StorefrontHomePage.xml b/app/code/Magento/Cms/Test/Mftf/Page/StorefrontHomePage.xml index 289d872aad804..07deacfaaef88 100644 --- a/app/code/Magento/Cms/Test/Mftf/Page/StorefrontHomePage.xml +++ b/app/code/Magento/Cms/Test/Mftf/Page/StorefrontHomePage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontHomePage" url="/" module="Magento_Cms" area="storefront"> <section name="StorefrontHeaderSection"/> </page> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/BlockPageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/BlockPageActionsSection.xml index 3fb56e0b179dd..d487517269c01 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/BlockPageActionsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/BlockPageActionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="BlockPageActionsSection"> <element name="addNewBlock" type="button" selector="#add" timeout="30"/> <element name="select" type="button" selector="//div[text()='{{var1}}']//parent::td//following-sibling::td//button[text()='Select']" parameterized="true"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml index 65ea1226772cf..2e783af6bfc6a 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CmsNewBlockBlockActionsSection"> <element name="savePage" type="button" selector="#save-button" timeout="30"/> </section> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml index 00b81686f7167..79fc3bac0fb25 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CmsNewBlockBlockBasicFieldsSection"> <element name="title" type="input" selector="input[name=title]"/> <element name="identifier" type="input" selector="input[name=identifier]"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml index e2c4f48f4ff9b..a2e4aecf8db2d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CmsNewPageHierarchySection"> <element name="header" type="button" selector="div[data-index=hierarchy]" timeout="30"/> <element name="selectHierarchy" type="button" selector="//a/span[contains(text(),'{{var1}}')]" parameterized="true" timeout="30"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml index 810c482dffd1a..42f8f4d00ee9f 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CmsNewPagePageActionsSection"> <element name="savePage" type="button" selector="#save_and_close" timeout="10"/> <element name="reset" type="button" selector="#reset"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml index 468dbecb20e02..7c1e2d0b1b0a5 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CmsNewPagePageBasicFieldsSection"> <element name="pageTitle" type="input" selector="input[name=title]"/> <element name="isActive" type="button" selector="//input[@name='is_active' and @value='{{var1}}']" parameterized="true"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml index 8015134c90f9c..65feddfd4a258 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CmsNewPagePageContentSection"> <element name="header" type="button" selector="div[data-index=content]"/> <element name="contentHeading" type="input" selector="input[name=content_heading]"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageSeoSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageSeoSection.xml index 0fe9c01d36fcb..dfd7386e09aba 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageSeoSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageSeoSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CmsNewPagePageSeoSection"> <element name="header" type="button" selector="div[data-index=search_engine_optimisation]" timeout="30"/> <element name="urlKey" type="input" selector="input[name=identifier]"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml index 456de55b49171..bd487e3b2c03c 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CmsNewPagePiwSection"> <element name="header" type="button" selector="div[data-index=websites]" timeout="30"/> <element name="selectStoreView" type="select" selector="//option[contains(text(),'{{var1}}')]" parameterized="true"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml index 2f28aa46af65b..de8a2adccc360 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CmsPagesPageActionsSection"> <element name="filterButton" type="input" selector="//button[text()='Filters']"/> <element name="URLKey" type="input" selector="//div[@class='admin__form-field-control']/input[@name='identifier']"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CustomVariableSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CustomVariableSection.xml index 354c86cfc4b3f..1488134ea511d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CustomVariableSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CustomVariableSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CustomVariableSection"> <element name="GridCustomVariableCode" type="text" selector=".//*[@id='customVariablesGrid_table']/tbody//tr//td[contains(text(), '{{var1}}')]" parameterized="true"/> <element name="variableCode" type="input" selector="#code"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontBlockSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontBlockSection.xml index fb4abe30b37af..bd2f9e5a646d5 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontBlockSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontBlockSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontBlockSection"> <element name="mediaDescription" type="text" selector=".widget.block.block-static-block>p>img"/> <element name="imageSource" type="text" selector="//img[contains(@src,'{{var1}}')]" parameterized="true"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml index d7c0a41464d21..9c83ce5565d98 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCMSPageSection"> <element name="mediaDescription" type="text" selector=".column.main>p>img"/> <element name="imageSource" type="text" selector="//img[contains(@src,'{{var1}}')]" parameterized="true"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml index 154bf33ac5661..d26f7d83616d5 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontHeaderSection"> </section> </sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml index b37c9e97a78fc..03edc69e6d625 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddImageToWYSIWYGBlockTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml index 995f52e42b3a6..205850f888797 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddImageToWYSIWYGCMSTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml index d0d8edc6abc91..bf17c277c1c5f 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddVariableToWYSIWYGBlockTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml index a7627b5492d72..9e5eb2558d6f2 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddVariableToWYSIWYGCMSTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml index 4d93980da9a33..ad5e769c61be4 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddWidgetToWYSIWYGBlockTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml index 90caf89c6a0ca..ded94eab92042 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml index 89030034dde12..f37038435e109 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml index 5993c7e2b82f3..5b3679bed77e0 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml index 6d626b3a91734..123d25f92b6b7 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml index 69938147444fe..705f2883f5839 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml index 50441574d7a0d..c89aa27c10e77 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml index 1574e6bd3b469..9cdbccd1f8c32 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockTest.xml index 3b80204f5c3d3..e2c74823dfffb 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateDuplicatedCmsBlockTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml index 73e38fcdad558..fccc5b5980f2b 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateCmsPageTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest.xml index 3d45d4baae748..9ee2055aae650 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest.xml index 2bfdc5f503720..caad1cabe78c5 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml index 51155423e62bf..19e689c794a43 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="ConfigAdminAccountSharingActionGroup"> <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/system_config/edit/section/admin/" stepKey="navigateToConfigurationPage" /> <waitForPageLoad stepKey="wait1"/> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml index 7bb2441a6a529..06c041fabeb35 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="SetTaxClassForShipping"> <amOnPage url="{{AdminSalesTaxClassPage.url}}" stepKey="navigateToSalesTaxPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml index 52adb0b1f50a5..dd52aaeeaff08 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="EnabledWYSIWYG"> <amOnPage url="admin/admin/system_config/edit/section/cms/" stepKey="navigateToConfigurationPage" /> <waitForPageLoad stepKey="wait1"/> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWebUrlOptionsActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWebUrlOptionsActionGroup.xml index 056b89624a2f5..f1e0ea6b7e175 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWebUrlOptionsActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWebUrlOptionsActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="EnableWebUrlOptions"> <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml index c3c0430a3d58c..fc5b5580d617c 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="NavigateToDefaultLayoutsSetting"> <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml index 670cd236be8be..e9e899a68c33e 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/RestoreLayoutSettingActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="RestoreLayoutSetting"> <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/SwitcherActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/SwitcherActionGroup.xml index 172ace8b18c11..f29ee1a407203 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/SwitcherActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/SwitcherActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="SwitchToVersion4ActionGroup"> <amOnPage url="{{ConfigurationStoresPage.url}}" stepKey="navigateToWYSIWYGConfigPage1"/> <waitForPageLoad stepKey="waitForConfigPageToLoad"/> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml index 8d5aaa5830b92..1e60158e4ac96 100644 --- a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminConfigPage" url="admin/system_config/" area="admin" module="Magento_Config"> <section name="AdminConfigSection"/> </page> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminSalesConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminSalesConfigPage.xml index 1a99ff6533dbb..7897a181ff405 100644 --- a/app/code/Magento/Config/Test/Mftf/Page/AdminSalesConfigPage.xml +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminSalesConfigPage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminSalesConfigPage" url="admin/system_config/edit/section/sales/{{var1}}" area="admin" parameterized="true" module="Magento_Config"> <section name="AdminSalesConfigSection"/> </page> diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml index a1b8c2f62f7da..dbede5491e011 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminConfigSection"> <element name="advancedReportingMenuItem" type="text" selector="//a[contains(concat(' ',normalize-space(@class),' '),'item-nav')]/span[text()='Advanced Reporting']"/> <element name="advancedReportingService" type="select" selector="#analytics_general_enabled"/> diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml index 4897e8415c1b8..55679fdb1524d 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminSalesConfigSection"> <element name="enableMAPUseSystemValue" type="checkbox" selector="#sales_msrp_enabled_inherit"/> <element name="enableMAPSelect" type="select" selector="#sales_msrp_enabled"/> diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminSection.xml index 8278c6366b68a..7b6c9f8ab3b79 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/AdminSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminSection"> <element name="CheckIfTabExpand" type="button" selector="#admin_security-head:not(.open)"/> <element name="SecurityTab" type="button" selector="#admin_security-head"/> diff --git a/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml index 78b9d1f72f66d..3b1de96abc8a2 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CatalogSection"> <element name="storefront" type="select" selector="#catalog_frontend-head"/> <element name="CheckIfTabExpand" type="button" selector="#catalog_frontend-head:not(.open)"/> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml index b1454ff07ee9e..e6f66cabff4b1 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="ContentManagementSection"> <element name="WYSIWYGOptions" type="button" selector="#cms_wysiwyg-head"/> <element name="CheckIfTabExpand" type="button" selector="#cms_wysiwyg-head:not(.open)"/> diff --git a/app/code/Magento/Config/Test/Mftf/Section/SalesConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/SalesConfigSection.xml index f1520f5813e6d..fbe1fd77eaa46 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/SalesConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/SalesConfigSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="SalesConfigSection"> <element name="TaxClassesTab" type="button" selector="#tax_classes-head"/> <element name="CheckIfTaxClassesTabExpand" type="button" selector="#tax_classes-head:not(.open)"/> diff --git a/app/code/Magento/Config/Test/Mftf/Section/StoreConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/StoreConfigSection.xml index 0ff3f3ca55d22..52fc0018a949d 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/StoreConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/StoreConfigSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StoreConfigSection"> <element name="CheckIfTabExpand" type="button" selector="#general_store_information-head:not(.open)"/> <element name="StoreInformation" type="button" selector="#general_store_information-head"/> diff --git a/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml b/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml index 0e1a6f4717c6b..66aacf706b039 100644 --- a/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml +++ b/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="VerifyAllowDynamicMediaURLsSettingIsRemoved"> <annotations> <features value="Backend"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml index 246e5be75889c..280ab95f73321 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Filter the product grid and view expected products--> <actionGroup name="viewConfigurableProductInAdminGrid"> <arguments> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductCheckoutActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductCheckoutActionGroup.xml index f272fa8ea733c..f88ed5e1b02f3 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductCheckoutActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductCheckoutActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Check configurable product in checkout cart items --> <actionGroup name="CheckConfigurableProductInCheckoutCartItemsActionGroup"> <arguments> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml index 9c160d72acc8d..39c206e365a2d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Check configurable product on the category page --> <actionGroup name="StorefrontCheckCategoryConfigurableProduct"> <arguments> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCompareActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCompareActionGroup.xml index 62e03b62151a5..a0c82ae356e22 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCompareActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCompareActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Check the configurable product in comparison page --> <actionGroup name="StorefrontCheckCompareConfigurableProductActionGroup"> <arguments> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml index 968f8c490af8d..0a8d8e56426ba 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Check the configurable product on the product page --> <actionGroup name="StorefrontCheckConfigurableProduct"> <arguments> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml index cc88a2c6147ef..e07b97c63a92b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Check Configurable Product in the Cart --> <actionGroup name="StorefrontCheckCartConfigurableProductActionGroup"> <arguments> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml index f92b388b46649..286c11ad5f30a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="BaseConfigurableProduct" type="product"> <data key="sku" unique="suffix">configurable</data> <data key="type_id">configurable</data> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductOptionData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductOptionData.xml index 21dcf998a6399..7555337db8e02 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductOptionData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductOptionData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ConfigurableProductTwoOptions" type="ConfigurableProductOption"> <var key="attribute_id" entityKey="attribute_id" entityType="ProductAttribute" /> <data key="label">option</data> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConstData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConstData.xml index 974be1e14a389..7e21729ba15c4 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConstData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConstData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <!-- @TODO: Get rid off this workaround and its usages after MQE-498 is implemented --> <entity name="CONST" type="CONST"> <data key="three">3</data> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ProductConfigurableAttributeData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ProductConfigurableAttributeData.xml index f99f960f6a945..9342172f7d4df 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ProductConfigurableAttributeData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ProductConfigurableAttributeData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="colorProductAttribute" type="product_attribute"> <data key="default_label" unique="suffix">Color</data> <data key="input_type">Dropdown</data> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml index 54d489a446fd7..2e4788823a28b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ValueIndex1" type="ValueIndex"> <var key="value_index" entityKey="value" entityType="ProductAttributeOption"/> </entity> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_add_child-meta.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_add_child-meta.xml index 6a77e97d8f276..ec4da787541f9 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_add_child-meta.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_add_child-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="ConfigurableProductAddChild" dataType="ConfigurableProductAddChild" type="create" auth="adminOauth" url="/V1/configurable-products/{sku}/child" method="POST"> <contentType>application/json</contentType> <field key="childSku">string</field> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_options-meta.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_options-meta.xml index 37e6be683c2fe..4d894f780092d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_options-meta.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_options-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateConfigurableProductOption" dataType="ConfigurableProductOption" type="create" auth="adminOauth" url="/V1/configurable-products/{sku}/options" method="POST"> <contentType>application/json</contentType> <object dataType="ConfigurableProductOption" key="option"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/extension_attribute_configurable_product_options-meta.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/extension_attribute_configurable_product_options-meta.xml index 2f1db19a1fd64..4b12abd3053fe 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/extension_attribute_configurable_product_options-meta.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/extension_attribute_configurable_product_options-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateExtensionAttributeConfigProductOption" dataType="ExtensionAttributeConfigProductOption" type="create"> <contentType>application/json</contentType> <array key="configurable_product_options"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/valueIndex-meta.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/valueIndex-meta.xml index 8d955fcc94431..e83faddf0e332 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/valueIndex-meta.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/valueIndex-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="ValueIndex" dataType="ValueIndex" type="create"> <field key="value_index">integer</field> </operation> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Page/AdminProductCreatePage.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Page/AdminProductCreatePage.xml index 01a494afd10e0..7705b34f0af03 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Page/AdminProductCreatePage.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Page/AdminProductCreatePage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminProductCreatePage" url="catalog/product/new/set/{{set}}/type/{{type}}/" area="admin" module="Magento_Catalog" parameterized="true"> <section name="AdminProductFormConfigurationsSection"/> <section name="AdminCreateProductConfigurationsPanel"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminChooseAffectedAttributeSetSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminChooseAffectedAttributeSetSection.xml index dac7027b73951..4289638352990 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminChooseAffectedAttributeSetSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminChooseAffectedAttributeSetSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminChooseAffectedAttributeSetPopup"> <element name="confirm" type="button" selector="button[data-index='confirm_button']" timeout="30"/> </section> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml index 77759043a0500..99e47baac37d5 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCreateProductConfigurationsPanel"> <element name="next" type="button" selector=".steps-wizard-navigation .action-next-step" timeout="30"/> <element name="createNewAttribute" type="button" selector=".select-attributes-actions button[title='Create New Attribute']" timeout="30"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml index 31787ca75f199..44077888f8bc0 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewAttributePanel"> <element name="container" type="text" selector="#create_new_attribute"/> <element name="saveAttribute" type="button" selector="#save"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml index 8a2cd192a20e3..0de7bc00044c8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormConfigurationsSection"> <element name="sectionHeader" type="text" selector=".admin__collapsible-block-wrapper[data-index='configurable']"/> <element name="createConfigurations" type="button" selector="button[data-index='create_configurable_products_button']" timeout="30"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductGridActionSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductGridActionSection.xml index 1d4ec9270ef8f..e3403ce71acaa 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductGridActionSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductGridActionSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductGridActionSection"> <element name="addConfigurableProduct" type="button" selector=".item[data-ui-id='products-list-add-new-product-button-item-configurable']" timeout="30"/> </section> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 4f96ea41eac5a..b195c19f7bedd 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoMainSection"> <element name="optionByAttributeId" type="input" selector="#attribute{{var1}}" parameterized="true"/> <element name="productAttributeTitle1" type="text" selector="#product-options-wrapper div[tabindex='0'] label"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml index 7dbacfa2ce612..92928c9384672 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddDefaultImageConfigurableTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml index 17ace8419c034..48f46a1205ec3 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurableProductCreateTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml index 17f17323a9edd..20201627f500b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurableProductDeleteTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml index c612431ec7044..5633c3675ca85 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurableProductChildrenOutOfStockTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml index 77ccf7bc6900b..23b8fc537cef8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurableProductSearchTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml index 280d5c3cdb02f..ddb9b0a76bdf5 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurableProductSetEditContentTest" extends="AdminSimpleProductSetEditContentTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml index 2282da467a967..027c2ce729162 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurableProductUpdateAttributeTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml index 154ce019f8c16..06de0e2ba5ce3 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurableProductBulkUpdateTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml index f8ac5bbd4781b..fdb6bfdd7b2a8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurableSetEditRelatedProductsTest" extends="AdminSimpleSetEditRelatedProductsTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml index 4461c06ed6b51..aa34693ed82f0 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminRelatedProductsTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml index 65e1300a4e6b3..e7492f4eeaecf 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminRemoveDefaultImageConfigurableTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml index 676c23f1cfb88..2b460a51ee5d1 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="ConfigurableProductPriceAdditionalStoreViewTest"> <annotations> <features value="ConfigurableProductPriceStoreFront"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml index a00ce52f442d7..6bbb97c66cdd8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CAdminTest"> <!--Create configurable product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageConfigurable" after="seeSimpleProductInGrid"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 1f7b2bff0a7de..47ee09e4b2086 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CGuestUserTest"> <before> <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 07e2ecf86ca9e..6f9ad93a56dc5 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CLoggedInUserTest"> <before> <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml index c0e7c886d0cc1..231ef553d2d42 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontConfigurableProductChildSearchTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml index 3b0f5752ebf5d..836bc2cdca970 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontConfigurableProductBasicInfoTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml index b8e894ccf3606..cc8291a83eb40 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontConfigurableProductGridViewTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml index e7091dbfae215..0255ef2dd6303 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontConfigurableProductWithFileCustomOptionTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml index 1a4e9071d306b..7be36ffbd9bc4 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="LoginToStorefrontActionGroup"> <arguments> <argument name="Customer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml index 3d6e0fb54b054..639c64b773a2d 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="OpenEditCustomerFromAdminActionGroup"> <arguments> <argument name="customer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml index de8418774f794..79cc00d4474b1 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="SignUpNewUserFromStorefrontActionGroup"> <arguments> <argument name="Customer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index d84dcef5b9229..6ed75861fd420 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="CustomerAddressSimple" type="address"> <data key="id">0</data> <data key="customer_id">12</data> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index 1f6a01ea815d1..baf6772843894 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="CustomerEntityOne" type="customer"> <data key="group_id">0</data> <data key="default_billing">defaultBillingValue</data> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml index cc8e16f017f8e..c1f11c9e9c390 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="GeneralCustomerGroup" type="customerGroup"> <data key="code">General</data> <data key="tax_class_id">3</data> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/ExtensionAttributeSimple.xml b/app/code/Magento/Customer/Test/Mftf/Data/ExtensionAttributeSimple.xml index fee4463709dd5..90540251877d5 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/ExtensionAttributeSimple.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/ExtensionAttributeSimple.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ExtensionAttributeSimple" type="extension_attribute"> <data key="is_subscribed">true</data> </entity> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml b/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml index 99741a357109e..523a463e5dea2 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="CustomerRegionOne" type="region"> <data key="region_code">100</data> <data key="region_id">12</data> diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/address-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/address-meta.xml index deb911f244f11..10f63a5a2a820 100644 --- a/app/code/Magento/Customer/Test/Mftf/Metadata/address-meta.xml +++ b/app/code/Magento/Customer/Test/Mftf/Metadata/address-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateAddress" dataType="address" type="create"> <field key="region">region</field> <field key="country_id">string</field> diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/customer-meta.xml index ab2ee2aeddb54..0d8aeb6614bf4 100644 --- a/app/code/Magento/Customer/Test/Mftf/Metadata/customer-meta.xml +++ b/app/code/Magento/Customer/Test/Mftf/Metadata/customer-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateCustomer" dataType="customer" type="create" auth="adminOauth" url="/V1/customers" method="POST"> <contentType>application/json</contentType> <object dataType="customer" key="customer"> diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_extension_attribute-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/customer_extension_attribute-meta.xml index 8561e937221a9..06c7b74aef002 100644 --- a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_extension_attribute-meta.xml +++ b/app/code/Magento/Customer/Test/Mftf/Metadata/customer_extension_attribute-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateCustomerExtensionAttribute" dataType="customer_extension_attribute" type="create"> <field key="is_subscribed">boolean</field> <field key="extension_attribute">customer_nested_extension_attribute</field> diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_nested_extension_attribute-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/customer_nested_extension_attribute-meta.xml index eb9829cca4981..a2741b7817b16 100644 --- a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_nested_extension_attribute-meta.xml +++ b/app/code/Magento/Customer/Test/Mftf/Metadata/customer_nested_extension_attribute-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateNestedExtensionAttribute" dataType="customer_nested_extension_attribute" type="create"> <field key="id">integer</field> <field key="customer_id">integer</field> diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/region-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/region-meta.xml index 3dd019462c846..5c21c5318e58b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Metadata/region-meta.xml +++ b/app/code/Magento/Customer/Test/Mftf/Metadata/region-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateRegion" dataType="region" type="create"> <field key="region_code">string</field> <field key="region">string</field> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/AdminCustomerPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/AdminCustomerPage.xml index 06ab646aa4c75..114c737e361ed 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/AdminCustomerPage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/AdminCustomerPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminCustomerPage" url="/customer/index/" area="admin" module="Magento_Customer"> <section name="AdminCustomerGridMainActionsSection"/> <section name="AdminCustomerMessagesSection"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml index 9a28bad4e0d6a..31dad24ba8372 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml @@ -6,7 +6,7 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminEditCustomerPage" url="/customer/index/edit/id/{{var1}}" area="admin" module="Magento_Customer" parameterized="true"> <section name="AdminCustomerAccountInformationSection"/> <section name="AdminCustomerMainActionsSection"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/AdminNewCustomerPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/AdminNewCustomerPage.xml index 646f03181d8fa..57a30d6f98921 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/AdminNewCustomerPage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/AdminNewCustomerPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminNewCustomerPage" url="/customer/index/new" area="admin" module="Magento_Customer"> <section name="AdminCustomerAccountInformationSection"/> <section name="AdminCustomerMainActionsSection"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerCreatePage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerCreatePage.xml index ba61cbb0bca42..e2ebf638934c6 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerCreatePage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerCreatePage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCustomerCreatePage" url="/customer/account/create/" area="storefront" module="Magento_Customer"> <section name="StorefrontCustomerCreateFormSection" /> </page> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerDashboardPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerDashboardPage.xml index 941e247e18b8c..c4f03659c12af 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerDashboardPage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerDashboardPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCustomerDashboardPage" url="/customer/account/" area="storefront" module="Magento_Customer"> <section name="StorefrontCustomerDashboardAccountInformationSection" /> </page> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerOrderPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerOrderPage.xml index bd25c67c8c907..05c4c71a61e94 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerOrderPage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerOrderPage.xml @@ -6,7 +6,7 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCustomerOrderPage" url="sales/order/view/order_id/" area="storefront" module="Magento_Customer"> <section name="StorefrontCustomerOrderViewSection"/> </page> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerOrderViewPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerOrderViewPage.xml index 7e6cebe6f3c78..2305bd3a9b82f 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerOrderViewPage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerOrderViewPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCustomerOrderViewPage" url="sales/order/view/order_id/{{var1}}" area="storefront" module="Magento_Customer" parameterized="true"> <section name="StorefrontCustomerOrderSection" /> </page> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerSignInPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerSignInPage.xml index f6673227beada..0d4fef8f6e967 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerSignInPage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerSignInPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCustomerSignInPage" url="/customer/account/login/" area="storefront" module="Magento_Customer"> <section name="StorefrontCustomerSignInFormSection" /> </page> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontHomePage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontHomePage.xml index 6b65bd97e8cb3..a466ceab2f7ed 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontHomePage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontHomePage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontHomePage" url="/" area="storefront" module="Magento_Customer"> <section name="StorefrontPanelHeader" /> </page> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml index 647cc6e3ee11f..a485069341a03 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCustomerAccountInformationSection"> <element name="accountInformationTitle" type="text" selector=".admin__page-nav-title"/> <element name="firstName" type="input" selector="input[name='customer[firstname]']"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerFiltersSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerFiltersSection.xml index 7d106a35f0e13..bc69621e9f22f 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerFiltersSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerFiltersSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCustomerFiltersSection"> <element name="filtersButton" type="button" selector="#container > div > div.admin__data-grid-header > div:nth-child(1) > div.data-grid-filters-actions-wrap > div > button" timeout="30"/> <element name="nameInput" type="input" selector="input[name=name]"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridMainActionsSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridMainActionsSection.xml index 760b2c3663322..e3616b0d59d90 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridMainActionsSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridMainActionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCustomerGridMainActionsSection"> <element name="addNewCustomer" type="button" selector="#add" timeout="30"/> </section> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml index 515d5eed1124b..d9d3bfe7f737c 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCustomerGridSection"> <element name="customerGrid" type="text" selector="table[data-role='grid']"/> <element name="firstRowEditLink" type="text" selector="tr[data-repeat-index='0'] .action-menu-item" timeout="30"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMainActionsSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMainActionsSection.xml index 1aadcb2fa469f..37ea99d652fb9 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMainActionsSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMainActionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCustomerMainActionsSection"> <element name="saveButton" type="button" selector="#save" timeout="30"/> </section> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMessagesSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMessagesSection.xml index 08c29473a7ee6..b11142fd1ce2e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMessagesSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMessagesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCustomerMessagesSection"> <element name="successMessage" type="text" selector=".message-success"/> </section> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerInformationSection.xml index 76feb2624b5ed..a28c6d5ff5e2d 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerInformationSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminEditCustomerInformationSection"> <element name="orders" type="button" selector="#tab_orders_content" timeout="30"/> </section> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerOrdersSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerOrdersSection.xml index bce4a7e848c13..89fed43184b84 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerOrdersSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerOrdersSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminEditCustomerOrdersSection"> <element name="orderGrid" type="text" selector="#customer_orders_grid_table"/> </section> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml index adf898a65f212..2b5662cdd623e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCustomerCreateFormSection"> <element name="firstnameField" type="input" selector="#firstname"/> <element name="lastnameField" type="input" selector="#lastname"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml index 21205c6d5d91e..70d1bb6675db5 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCustomerDashboardAccountInformationSection"> <element name="ContactInformation" type="textarea" selector=".box.box-information .box-content"/> </section> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderSection.xml index c39dfef5f74e7..141c2fbbb4a07 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCustomerOrderSection"> <element name="productCustomOptions" type="text" selector="//strong[contains(@class, 'product-item-name') and normalize-space(.)='{{var1}}']/following-sibling::*[contains(@class, 'item-options')]/dt[normalize-space(.)='{{var2}}']/following-sibling::dd[normalize-space(.)='{{var3}}']" parameterized="true"/> <element name="productCustomOptionsFile" type="text" selector="//strong[contains(@class, 'product-item-name') and normalize-space(.)='{{var1}}']/following-sibling::*[contains(@class, 'item-options')]/dt[normalize-space(.)='{{var2}}']/following-sibling::dd[contains(.,'{{var3}}')]" parameterized="true"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderViewSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderViewSection.xml index 9ea271dad7b21..53b07b2b6a51c 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderViewSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderViewSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCustomerOrderViewSection"> <element name="reorder" type="text" selector="a.action.order" timeout="30"/> <element name="orderTitle" type="text" selector=".page-title span"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml index 9cc4a43d31bc6..8480dc6e9d2a6 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCustomerSignInFormSection"> <element name="emailField" type="input" selector="#email"/> <element name="passwordField" type="input" selector="#pass"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml index 65e7aa7a12113..06b82db767ab5 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontPanelHeaderSection"> <element name="WelcomeMessage" type="text" selector=".greet.welcome span"/> <element name="createAnAccountLink" type="select" selector=".panel.header li:nth-child(3)" timeout="30"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml index bde15b31ff1e6..9dd2127fa28b2 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateCustomerTest"> <annotations> <features value="Customer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index a9563c4cc93d8..901018c2fd074 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CLoggedInUserTest"> <annotations> <features value="End to End scenarios"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerTest.xml index 3670cdba3872d..97c932f0cb28a 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontCreateCustomerTest"> <annotations> <features value="Customer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontPersistedCustomerLoginTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontPersistedCustomerLoginTest.xml index ec669be165e68..250da68786688 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontPersistedCustomerLoginTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontPersistedCustomerLoginTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontPersistedCustomerLoginTest"> <annotations> <features value="Customer"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml index 2e1392eb0d2a8..363911daa41ed 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Fill main fields in product form--> <actionGroup name="fillMainDownloadableProductForm"> <arguments> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Data/LinkData.xml b/app/code/Magento/Downloadable/Test/Mftf/Data/LinkData.xml index 16f32942a375f..38ac2c99e4756 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Data/LinkData.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Data/LinkData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="downloadableData" type="downloadable_data"> <data key="link_title">Downloadable Links</data> <data key="sample_title">Downloadable Samples</data> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml index 2c25c2c9b822b..6a91b60dcb588 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="DownloadableProduct" type="product"> <data key="sku" unique="suffix">downloadableproduct</data> <data key="type_id">downloadable</data> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link-meta.xml index 1138b56189137..2511244d445c1 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link-meta.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateDownloadableLink" dataType="downloadable_link" type="create" auth="adminOauth" url="/V1/products/{sku}/downloadable-links" method="POST"> <contentType>application/json</contentType> <object dataType="downloadable_link" key="link"> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/link_file_content-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/link_file_content-meta.xml index de899a9051022..d5d6c16c71736 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Metadata/link_file_content-meta.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Metadata/link_file_content-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateLinkFileContent" dataType="link_file_content" type="create"> <field key="file_data">string</field> <field key="name">string</field> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/sample_file_content-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/sample_file_content-meta.xml index 5109263cfc242..3da91807ceb48 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Metadata/sample_file_content-meta.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Metadata/sample_file_content-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateSampleFileContent" dataType="sample_file_content" type="create"> <field key="file_data">string</field> <field key="name">string</field> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Page/AdminProductCreatePage.xml b/app/code/Magento/Downloadable/Test/Mftf/Page/AdminProductCreatePage.xml index 7d5cc562bfb3c..049d6d084530f 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Page/AdminProductCreatePage.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Page/AdminProductCreatePage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminProductCreatePage" url="catalog/product/new/set/{{set}}/type/{{type}}/" area="admin" module="Magento_Catalog" parameterized="true"> <section name="AdminProductDownloadableSection"/> </page> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Section/AdminProductDownloadableSection.xml b/app/code/Magento/Downloadable/Test/Mftf/Section/AdminProductDownloadableSection.xml index 1c1ce0343c94d..274f26498958b 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Section/AdminProductDownloadableSection.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Section/AdminProductDownloadableSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductDownloadableSection"> <element name="sectionHeader" type="button" selector="div[data-index='downloadable']" timeout="30" /> <element name="isDownloadableProduct" type="input" selector="input[name='is_downloadable']" /> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml index f2c7f6a61388f..3d779740849c5 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddDefaultImageDownloadableProductTest"> <annotations> <features value="Downloadable"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml index 8153f16274de7..70bd4bda49761 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminDownloadableProductSetEditContentTest" extends="AdminSimpleProductSetEditContentTest"> <annotations> <features value="Downloadable"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml index dadc60a90a27a..f70769cdfe834 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminDownloadableSetEditRelatedProductsTest" extends="AdminSimpleSetEditRelatedProductsTest"> <annotations> <features value="Downloadable"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml index 3b10f60c97340..3ee6cef47738b 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminRemoveDefaultImageDownloadableProductTest"> <annotations> <features value="Downloadable"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 4a4242811a39c..75a66cec91692 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CAdminTest"> <!--Create Downloadable Product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitProductPageDownloadable" after="seeSimpleProductInGrid"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml index 720f5a2c2b949..9360ed6e42792 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Check that required fields are actually required--> <actionGroup name="checkRequiredFieldsInGroupedProductForm"> <clearField selector="{{AdminProductFormSection.productName}}" stepKey="clearProductSku"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Data/GroupedProductData.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Data/GroupedProductData.xml index 1f9f1594f8fcb..4d979953a934e 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Data/GroupedProductData.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Data/GroupedProductData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="GroupedProduct" type="product"> <data key="sku" unique="suffix">groupedproduct</data> <data key="type_id">grouped</data> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkData.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkData.xml index 2c7cc254c855d..882275d0060b4 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkData.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ProductLinkSimple1" type="product_link"> <var key="sku" entityKey="sku" entityType="product3"/> <var key="linked_product_sku" entityKey="sku" entityType="product"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkExtensionAttributeData.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkExtensionAttributeData.xml index 433dc920502d4..b580c876a6f30 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkExtensionAttributeData.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkExtensionAttributeData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="Qty1000" type="product_link_extension_attribute"> <data key="qty">1000</data> </entity> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinksData.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinksData.xml index b712e3f40afd1..68c95e856e2f8 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinksData.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinksData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="OneSimpleProductLink" type="product_links"> <requiredEntity type="product_link">ProductLinkSimple1</requiredEntity> </entity> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Page/AdminProductCreatePage.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Page/AdminProductCreatePage.xml index d8edc37160bab..7cf998cff3eea 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Page/AdminProductCreatePage.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Page/AdminProductCreatePage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminProductCreatePage" url="catalog/product/new/set/{{set}}/type/{{type}}/" area="admin" module="Magento_Catalog" parameterized="true"> <section name="AdminProductFormGroupedProductsSection"/> <section name="AdminAddProductsToGroupPanel"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml index d21a067d34e20..e2c4286135d2e 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminAddProductsToGroupPanel"> <element name="addSelectedProducts" type="button" selector=".product_form_product_form_grouped_grouped_products_modal button.action-primary" timeout="30"/> <element name="filters" type="button" selector=".product_form_product_form_grouped_grouped_products_modal [data-action='grid-filter-expand']" timeout="30"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml index adb0ac5a984a1..64dcd9566d890 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormGroupedProductsSection"> <element name="toggleGroupedProduct" type="button" selector="div[data-index=grouped] .admin__collapsible-title"/> <element name="addProductsToGroup" type="button" selector="button[data-index='grouped_products_button']" timeout="30"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml index 1bee1846ac0f5..5d65f82690235 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddDefaultImageGroupedProductTest"> <annotations> <features value="GroupedProduct"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml index 0193e88e9597b..b70e61e2ec0aa 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminGroupedProductSetEditContentTest" extends="AdminSimpleProductSetEditContentTest"> <annotations> <features value="GroupedProduct"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml index 2e7e8e161f92c..8117d627a370c 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminGroupedSetEditRelatedProductsTest" extends="AdminSimpleSetEditRelatedProductsTest"> <annotations> <features value="GroupedProduct"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml index 54062d97f7c8f..da7cfaeb71566 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminRemoveDefaultImageGroupedProductTest"> <annotations> <features value="GroupedProduct"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 60aa666224550..dbe3dddfca81b 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CAdminTest"> <!--Create Grouped Product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageGrouped" after="seeSimpleProductInGrid"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml b/app/code/Magento/Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml index 9719a892aa702..fe2deed9a279f 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Data/NewsletterTemplateData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultNewsletter" type="cms_page"> <data key="name" unique="suffix">Test Newsletter Template</data> <data key="subject">Test Newsletter Subject</data> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage.xml b/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage.xml index b27bc5eee9f4d..fa655fadab551 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage.xml @@ -6,7 +6,7 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="NewsletterTemplateForm" url="/newsletter/template/new/" area="admin" module="Magento_Cms"> <section name="StorefrontNewsletterSection"/> <section name="StorefrontNewsletterSection"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection.xml index ef897ef6421bb..d0dfd21cc2e1f 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="BasicFieldNewsletterSection"> <element name="templateName" type="input" selector="#code"/> <element name="templateSubject" type="input" selector="#subject"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontNewsletterSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontNewsletterSection.xml index 01d3a36bf29c5..ed2f5c316055c 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontNewsletterSection.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontNewsletterSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontNewsletterSection"> <element name="mediaDescription" type="text" selector="body>p>img" /> <element name="imageSource" type="text" selector="//img[contains(@src,'{{var1}}')]" parameterized="true"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml index f397b84dfaeb1..f69f94dbd79e3 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddImageToWYSIWYGNewsletterTest"> <annotations> <features value="Newsletter"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml index 353d33439f079..6e5370927e9de 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddVariableToWYSIWYGNewsletterTest"> <annotations> <features value="Newsletter"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml index a447acc1b0fca..50a6b74a67233 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddWidgetToWYSIWYGNewsletterTest"> <annotations> <features value="Newsletter"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest.xml index 69ea5677a09e8..3c19a3fa99d3c 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest"> <annotations> <features value="Newsletter"/> diff --git a/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearPageCacheActionGroup.xml b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearPageCacheActionGroup.xml index 0293b1e10b8ce..35d5234e47c3a 100644 --- a/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearPageCacheActionGroup.xml +++ b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearPageCacheActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="ClearPageCacheActionGroup"> <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/cache/" stepKey="goToCacheManagementPage" /> <waitForPageLoad stepKey="waitForPageLoad"/> diff --git a/app/code/Magento/Payment/Test/Mftf/Data/PaymentMethodData.xml b/app/code/Magento/Payment/Test/Mftf/Data/PaymentMethodData.xml index feaff74a3a985..14c8bd0fecde7 100644 --- a/app/code/Magento/Payment/Test/Mftf/Data/PaymentMethodData.xml +++ b/app/code/Magento/Payment/Test/Mftf/Data/PaymentMethodData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="PaymentMethodCheckMoneyOrder" type="payment_method"> <data key="method">checkmo</data> </entity> diff --git a/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml b/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml index eb5f272e25e25..39506a682038f 100644 --- a/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml +++ b/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreatePaymentMethod" dataType="payment_method" type="create"> <field key="method">string</field> </operation> diff --git a/app/code/Magento/Paypal/Test/Mftf/Data/PaypalData.xml b/app/code/Magento/Paypal/Test/Mftf/Data/PaypalData.xml index 4cb6173a0273e..6d5f80e30dc7f 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Data/PaypalData.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Data/PaypalData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="SamplePaypalConfig" type="paypal_config_state"> <requiredEntity type="business_account">SampleBusinessAccount</requiredEntity> <requiredEntity type="api_username">SampleApiUsername</requiredEntity> diff --git a/app/code/Magento/Paypal/Test/Mftf/Metadata/paypal_config-meta.xml b/app/code/Magento/Paypal/Test/Mftf/Metadata/paypal_config-meta.xml index 37009297ddb30..7457a90150a0f 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Metadata/paypal_config-meta.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Metadata/paypal_config-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreatePaypalConfigState" dataType="paypal_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/payment/" method="POST"> <object key="groups" dataType="paypal_config_state"> <object key="paypal_alternative_payment_methods" dataType="paypal_config_state"> diff --git a/app/code/Magento/Paypal/Test/Mftf/Page/AdminConfigPaymentMethodsPage.xml b/app/code/Magento/Paypal/Test/Mftf/Page/AdminConfigPaymentMethodsPage.xml index 111e0d8b01a93..28e77e82d91f1 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Page/AdminConfigPaymentMethodsPage.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Page/AdminConfigPaymentMethodsPage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminConfigPaymentMethodsPage" url="admin/system_config/edit/section/payment/" area="admin" module="Magento_Config"> <section name="OtherPaymentsConfigSection"/> </page> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPaymentsConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPaymentsConfigSection.xml index ec4f4f4ae09e8..bca6f963058e4 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPaymentsConfigSection.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPaymentsConfigSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="OtherPaymentsConfigSection"> <element name="expandedTab" type="button" selector="#payment_us_other_payment_methods-head.open"/> </section> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionState.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionState.xml index 283bc32412646..f9e2c2589e3ab 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionState.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionState.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigPaymentsSectionState"> <annotations> <description value="Other Payment Methods section in Admin expanded by default"/> diff --git a/app/code/Magento/Persistent/Test/Mftf/Data/PersistentData.xml b/app/code/Magento/Persistent/Test/Mftf/Data/PersistentData.xml index a23d1169b6865..f4e2fa198e7ff 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Data/PersistentData.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Data/PersistentData.xml @@ -6,7 +6,7 @@ */ --> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="PersistentConfigDefault" type="persistent_config_state"> <requiredEntity type="persistent_options_enabled">persistentDefaultState</requiredEntity> </entity> diff --git a/app/code/Magento/Persistent/Test/Mftf/Metadata/persistent_config-meta.xml b/app/code/Magento/Persistent/Test/Mftf/Metadata/persistent_config-meta.xml index 42aae658b2e27..d165ca5f929b0 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Metadata/persistent_config-meta.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Metadata/persistent_config-meta.xml @@ -6,7 +6,7 @@ */ --> -<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreatePersistentConfigState" dataType="persistent_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/persistent/" method="POST"> <object key="groups" dataType="persistent_config_state"> <object key="options" dataType="persistent_config_state"> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml index 49d07992694d4..c32b371566277 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="GuestCheckoutWithEnabledPersistentTest"> <annotations> <features value="Persistent"/> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminProductVideoActionGroup.xml b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminProductVideoActionGroup.xml index f28a9f9359def..fbda025f76758 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminProductVideoActionGroup.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminProductVideoActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Add video in Admin Product page --> <actionGroup name="addProductVideo"> <arguments> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/StorefrontProductVideoActionGroup.xml b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/StorefrontProductVideoActionGroup.xml index 1f7750ab1f2cf..8f0d41f8c2153 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/StorefrontProductVideoActionGroup.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/StorefrontProductVideoActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Assert product video in Storefront Product page --> <actionGroup name="assertProductVideoStorefrontProductPage"> <arguments> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoConfigData.xml b/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoConfigData.xml index b21ac404711f7..8fe5899e91ef8 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoConfigData.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoConfigData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <!-- mftf test youtube api key configuration --> <entity name="ProductVideoYoutubeApiKeyConfig" type="product_video_config"> <requiredEntity type="youtube_api_key_config">YouTubeApiKey</requiredEntity> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoData.xml b/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoData.xml index 93d3f93ff429f..5bc4ad86e0f06 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoData.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="mftfTestProductVideo" type="product_video"> <data key="videoUrl">https://youtu.be/bpOSxM0rNPM</data> <data key="videoTitle">Arctic Monkeys - Do I Wanna Know? (Official Video)</data> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Metadata/product_video_config-meta.xml b/app/code/Magento/ProductVideo/Test/Mftf/Metadata/product_video_config-meta.xml index 07d91bc0a1e84..2525c3a3d0ff6 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Metadata/product_video_config-meta.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Metadata/product_video_config-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateProductVideoYouTubeApiKeyConfig" dataType="product_video_config" type="create" auth="adminFormKey" url="admin/system_config/save/section/catalog/" method="POST"> <object key="groups" dataType="product_video_config"> <object key="product_video" dataType="product_video_config"> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductImagesSection.xml b/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductImagesSection.xml index b232d24c51ee1..5db86267f7d7b 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductImagesSection.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductImagesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductImagesSection"> <element name="addVideoButton" type="button" selector="#add_video_button" timeout="60"/> <element name="removeVideoButton" type="button" selector="//*[@id='media_gallery_content']//button[@class='action-remove']" timeout="60"/> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductNewVideoSection.xml b/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductNewVideoSection.xml index 89e6fd37b171b..71dad6a24f148 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductNewVideoSection.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductNewVideoSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductNewVideoSection"> <element name="saveButton" type="button" selector=".action-primary.video-create-button" timeout="30"/> <element name="saveButtonDisabled" type="text" selector="//button[@class='action-primary video-create-button' and @disabled='disabled']"/> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 03e1d91df4d3e..564122f71b9f4 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoMainSection"> <element name="productVideo" type="text" selector="//*[@class='product-video' and @data-type='{{videoType}}']" parameterized="true"/> </section> diff --git a/app/code/Magento/Quote/Test/Mftf/Data/CartItemData.xml b/app/code/Magento/Quote/Test/Mftf/Data/CartItemData.xml index d4032b5f1ac56..4cc2c4f392419 100644 --- a/app/code/Magento/Quote/Test/Mftf/Data/CartItemData.xml +++ b/app/code/Magento/Quote/Test/Mftf/Data/CartItemData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="SimpleCartItem" type="CartItem"> <data key="qty">1</data> <var key="quote_id" entityKey="return" entityType="GuestCart"/> diff --git a/app/code/Magento/Quote/Test/Mftf/Data/GuestCartData.xml b/app/code/Magento/Quote/Test/Mftf/Data/GuestCartData.xml index 062c4ebbad333..1d63a8a0d9f87 100644 --- a/app/code/Magento/Quote/Test/Mftf/Data/GuestCartData.xml +++ b/app/code/Magento/Quote/Test/Mftf/Data/GuestCartData.xml @@ -8,7 +8,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="GuestCart" type="GuestCart"> </entity> diff --git a/app/code/Magento/Quote/Test/Mftf/Metadata/billing_address-meta.xml b/app/code/Magento/Quote/Test/Mftf/Metadata/billing_address-meta.xml index a21c4a30e8077..d4a4c4345d738 100644 --- a/app/code/Magento/Quote/Test/Mftf/Metadata/billing_address-meta.xml +++ b/app/code/Magento/Quote/Test/Mftf/Metadata/billing_address-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateBillingAddress" dataType="billing_address" type="create"> <field key="city">string</field> <field key="region">string</field> diff --git a/app/code/Magento/Quote/Test/Mftf/Metadata/guest_cart-meta.xml b/app/code/Magento/Quote/Test/Mftf/Metadata/guest_cart-meta.xml index e08a30407889f..27c7af95d0f2c 100644 --- a/app/code/Magento/Quote/Test/Mftf/Metadata/guest_cart-meta.xml +++ b/app/code/Magento/Quote/Test/Mftf/Metadata/guest_cart-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateGuestCart" dataType="GuestCart" type="create" auth="anonymous" url="/V1/guest-carts" method="POST"> <contentType>application/json</contentType> </operation> diff --git a/app/code/Magento/Quote/Test/Mftf/Metadata/shipping_address-meta.xml b/app/code/Magento/Quote/Test/Mftf/Metadata/shipping_address-meta.xml index 537f6c2a2f87d..803681252a9e9 100644 --- a/app/code/Magento/Quote/Test/Mftf/Metadata/shipping_address-meta.xml +++ b/app/code/Magento/Quote/Test/Mftf/Metadata/shipping_address-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateShippingAddress" dataType="shipping_address" type="create"> <field key="city">string</field> <field key="region">string</field> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml index 2a9ef0c948392..58c7752626c8a 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Check customer information is correct in credit memo--> <actionGroup name="verifyBasicCreditMemoInformation"> <arguments> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml index f17d3462d06f3..15aff7c751a11 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Check customer information is correct in invoice--> <actionGroup name="verifyBasicInvoiceInformation"> <arguments> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index 7ce10d5e5424e..5b6c7fb70b809 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Navigate to create order page (New Order -> Create New Customer)--> <actionGroup name="navigateToNewOrderPageNewCustomer"> <arguments> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml index df0f56dbf7866..eed9f80c251c8 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Filter order grid by order id field--> <actionGroup name="filterOrderGridById"> <arguments> diff --git a/app/code/Magento/Sales/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Sales/Test/Mftf/Data/AddressData.xml index 800bbfca2f869..920618a70dfb8 100644 --- a/app/code/Magento/Sales/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Sales/Test/Mftf/Data/AddressData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ShippingAddressTX" type="shipping_address"> <data key="firstname">Joe</data> <data key="lastname">Buyer</data> diff --git a/app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml b/app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml index f6855ed09c263..10de6681d1b57 100644 --- a/app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml +++ b/app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="Const" type="constant"> <data key="one">1</data> <data key="two">2</data> diff --git a/app/code/Magento/Sales/Test/Mftf/Data/OrderData.xml b/app/code/Magento/Sales/Test/Mftf/Data/OrderData.xml index 566fb7d44528e..eb5600f112ea1 100644 --- a/app/code/Magento/Sales/Test/Mftf/Data/OrderData.xml +++ b/app/code/Magento/Sales/Test/Mftf/Data/OrderData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <!--Data for order created through UI with simple and configurable order--> <entity name="AdminOrderSimpleConfigurableProduct" type="order"> <data key="subtotal">246.00</data> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminCreditMemoNewPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminCreditMemoNewPage.xml index 5a50c807628c6..2d020caa0d107 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminCreditMemoNewPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminCreditMemoNewPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminCreditMemoNewPage" url="sales/order_creditmemo/new/order_id/" area="admin" module="Magento_Sales"> <section name="AdminCreditMemoOrderInformationSection"/> <section name="AdminCreditMemoAddressInformationSection"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminInvoiceDetailsPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminInvoiceDetailsPage.xml index a60e44247fe9c..bf48bc6763348 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminInvoiceDetailsPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminInvoiceDetailsPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminInvoiceDetailsPage" url="sales/invoice/view/invoice_id/" area="admin" module="Magento_Sales"> <section name="AdminInvoiceDetailsInformationSection"/> </page> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminInvoiceNewPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminInvoiceNewPage.xml index e782fe5194720..d547c9f1eb05f 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminInvoiceNewPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminInvoiceNewPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminInvoiceNewPage" url="sales/order_invoice/new/order_id/" area="admin" module="Magento_Sales"> <section name="AdminInvoiceMainActionsSection"/> <section name="AdminInvoiceOrderInformationSection"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminInvoicesPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminInvoicesPage.xml index 7f7289b2e64fd..3dda74adb9c0f 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminInvoicesPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminInvoicesPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminInvoicesPage" url="sales/invoice/" area="admin" module="Magento_Sales"> <section name="AdminInvoicesGridSection"/> <section name="AdminInvoicesFiltersSection"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml index 49520261d857f..333be23dbf346 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminOrderCreatePage" url="sales/order_create/index" area="admin" module="Magento_Sales"> <section name="AdminOrderFormActionSection"/> <section name="AdminOrderFormItemsSection"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderDetailsPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderDetailsPage.xml index 690eade8b0c8f..c62144b84aa63 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderDetailsPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderDetailsPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminOrderDetailsPage" url="sales/order/view/order_id/" area="admin" module="Magento_Sales"> <section name="AdminOrderDetailsMainActionsSection"/> <section name="AdminOrderDetailsOrderViewSection"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrdersPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrdersPage.xml index d4d5e78631885..7a9414e3f9aab 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrdersPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrdersPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminOrdersPage" url="sales/order/" area="admin" module="Magento_Sales"> <section name="AdminOrdersGridSection"/> </page> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoAddressInformationSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoAddressInformationSection.xml index 85e5faca3a48c..178cd37e6b8d5 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoAddressInformationSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoAddressInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCreditMemoAddressInformationSection"> <element name="billingAddress" type="text" selector=".order-billing-address address"/> <element name="billingAddressEdit" type="button" selector=".order-billing-address .actions a"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoItemsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoItemsSection.xml index 5e3e8fc6e22ec..13f351c064437 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoItemsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoItemsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCreditMemoItemsSection"> <element name="header" type="text" selector="#creditmemo_item_container span.title"/> <element name="itemName" type="text" selector=".order-creditmemo-tables tbody:nth-of-type({{row}}) .col-product .product-title" parameterized="true"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoOrderInformationSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoOrderInformationSection.xml index b0be8c6cd6575..5b7f829626587 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoOrderInformationSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoOrderInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCreditMemoOrderInformationSection"> <element name="orderId" type="text" selector="div.order-information span.title > a" timeout="60"/> <element name="orderDate" type="text" selector=".order-information table.order-information-table tr:first-of-type > td"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoPaymentShippingSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoPaymentShippingSection.xml index b307a5d1cf4bc..1bfe28b9f045d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoPaymentShippingSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoPaymentShippingSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCreditMemoPaymentShippingSection"> <element name="PaymentMethod" type="text" selector=".order-payment-method .order-payment-method-title"/> <element name="CurrencyInformation" type="text" selector=".order-payment-method .order-payment-currency"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml index ca5e297b72ffb..00eb93452edd5 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCreditMemoTotalSection"> <element name="subtotalRow" type="text" selector=".order-subtotal-table tbody > tr:nth-of-type({{row}}) td span.price" parameterized="true"/> <element name="total" type="text" selector="//table[contains(@class,'order-subtotal-table')]/tbody/tr/td[contains(text(), '{{total}}')]/following-sibling::td//span[contains(@class, 'price')]" parameterized="true"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceAddressInformationSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceAddressInformationSection.xml index 63712f24a5de8..14a0d4b8488ea 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceAddressInformationSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceAddressInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminInvoiceAddressInformationSection"> <element name="billingAddress" type="text" selector=".order-billing-address address"/> <element name="billingAddressEdit" type="button" selector=".order-billing-address .actions a"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceDetailsInformationSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceDetailsInformationSection.xml index 41b36310ebeb1..39071a9eb5899 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceDetailsInformationSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceDetailsInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminInvoiceDetailsInformationSection"> <element name="orderStatus" type="text" selector="#order_status"/> </section> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml index 56c165bada500..bc0d1cffd5d3e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminInvoiceItemsSection"> <element name="itemName" type="text" selector=".order-invoice-tables tbody:nth-of-type({{row}}) .product-title" parameterized="true"/> <element name="itemSku" type="text" selector=".order-invoice-tables tbody:nth-of-type({{row}}) .product-sku-block" parameterized="true"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceMainActionsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceMainActionsSection.xml index c1a9718b29b1c..2a241708517bf 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceMainActionsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceMainActionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminInvoiceMainActionsSection"> <element name="submitInvoice" type="button" selector=".action-default.scalable.save.submit-button.primary"/> </section> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceOrderInformationSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceOrderInformationSection.xml index 198a087bffc45..38ca7e683fe56 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceOrderInformationSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceOrderInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminInvoiceOrderInformationSection"> <element name="orderId" type="text" selector="div.order-information span.title > a" timeout="30"/> <element name="orderDate" type="text" selector=".order-information table.order-information-table tr:first-of-type > td"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml index 3bb381047bb97..918a8e814b555 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminInvoicePaymentShippingSection"> <element name="PaymentMethod" type="text" selector=".order-payment-method .order-payment-method-title"/> <element name="CurrencyInformation" type="text" selector=".order-payment-method .order-payment-currency"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceTotalSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceTotalSection.xml index db5b12f622b64..f66412c876709 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceTotalSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceTotalSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminInvoiceTotalSection"> <element name="subtotalRow" type="text" selector=".order-subtotal-table tbody > tr:nth-of-type({{row}}) td span.price" parameterized="true"/> <element name="total" type="text" selector="//table[contains(@class,'order-subtotal-table')]/tbody/tr/td[contains(text(), '{{total}}')]/following-sibling::td/span/span[contains(@class, 'price')]" parameterized="true"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicesFiltersSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicesFiltersSection.xml index 5b55dd3cc1aae..8fb45295bd26e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicesFiltersSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicesFiltersSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminInvoicesFiltersSection"> <element name="orderNum" type="input" selector="input[name='order_increment_id']"/> </section> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicesGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicesGridSection.xml index 8d2d8750e5045..b8cc79a84db1a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicesGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicesGridSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminInvoicesGridSection"> <element name="spinner" type="button" selector=".spinner"/> <element name="filter" type="button" selector="#container > div > div.admin__data-grid-header > div:nth-child(1) > div.data-grid-filters-actions-wrap > div > button"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderAddressInformationSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderAddressInformationSection.xml index 2900cb60391be..2632d5f2815e7 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderAddressInformationSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderAddressInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderAddressInformationSection"> <element name="billingAddress" type="text" selector=".order-billing-address address"/> <element name="shippingAddress" type="text" selector=".order-shipping-address address"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCommentsTabSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCommentsTabSection.xml index 9a784049e081a..19f447117959a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCommentsTabSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCommentsTabSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderCommentsTabSection"> <element name="orderNotesList" type="text" selector="#Order_History .edit-order-comments .note-list"/> <element name="orderComments" type="text" selector="#Order_History .edit-order-comments-block"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCreditMemosTabSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCreditMemosTabSection.xml index bb0e1618c66e3..e285f8700a1a7 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCreditMemosTabSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCreditMemosTabSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderCreditMemosTabSection"> <element name="spinner" type="text" selector="[data-role='spinner'][data-component*='sales_order_view_creditmemo']"/> <element name="gridRow" type="text" selector="#sales_order_view_tabs_order_creditmemos_content .data-grid tbody > tr:nth-of-type({{row}})" parameterized="true"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCustomersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCustomersGridSection.xml index c91a1e2aef693..c02a36432851d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCustomersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCustomersGridSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderCustomersGridSection"> <element name="spinner" type="button" selector=".spinner"/> <element name="apply" type="button" selector=".action-secondary[title='Search']"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsInformationSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsInformationSection.xml index 39feba4694019..a531f423d134c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsInformationSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderDetailsInformationSection"> <element name="orderDate" type="text" selector=".order-information table.order-information-table tr:first-of-type > td"/> <element name="orderStatus" type="text" selector=".order-information table.order-information-table #order_status"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsInvoicesSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsInvoicesSection.xml index b6b32184841c4..bcf8bdcae7c59 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsInvoicesSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsInvoicesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderDetailsInvoicesSection"> <element name="spinner" type="button" selector=".spinner"/> <element name="content" type="text" selector="#sales_order_view_tabs_order_invoices_content"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml index eac238584b030..578022217f358 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderDetailsMainActionsSection"> <element name="back" type="button" selector="#back" timeout="30"/> <element name="cancel" type="button" selector="#order-view-cancel-button" timeout="30"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMessagesSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMessagesSection.xml index 31f78db11f90b..1bc3718467102 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMessagesSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMessagesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderDetailsMessagesSection"> <element name="successMessage" type="text" selector="div.message-success"/> </section> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsOrderViewSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsOrderViewSection.xml index 6623c68099fe7..7f98256fa732a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsOrderViewSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsOrderViewSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderDetailsOrderViewSection"> <element name="information" type="button" selector="#sales_order_view_tabs_order_info"/> <element name="invoices" type="button" selector="#sales_order_view_tabs_order_invoices"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml index 3c920bc6ba0e7..4ab1e3327960c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormAccountSection"> <element name="group" type="select" selector="#group_id"/> <element name="email" type="input" selector="#email"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormActionSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormActionSection.xml index 09b2012841b8b..2f6149dfa1cb7 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormActionSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormActionSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormActionSection"> <element name="SubmitOrder" type="button" selector="#submit_order_top_button" timeout="30"/> <element name="Cancel" type="button" selector="#reset_order_top_button" timeout="30"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml index b0f7eb21d4dd7..2d1a4d5a4cbae 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormBillingAddressSection"> <element name="NamePrefix" type="input" selector="#order-billing_address_prefix" timeout="30"/> <element name="FirstName" type="input" selector="#order-billing_address_firstname" timeout="30"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBundleProductSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBundleProductSection.xml index a035e47394d5b..562d17f2e8739 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBundleProductSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBundleProductSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormBundleProductSection"> <element name="quantity" type="input" selector="#product_composite_configure_input_qty"/> <element name="ok" type="button" selector=".modal-header .page-actions button[data-role='action']" timeout="30"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml index 87948c38e4328..83d417f6f8555 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormConfigureProductSection"> <element name="optionSelect" type="select" selector="//div[@class='product-options']/div/div/select[../../label[text() = '{{option}}']]" parameterized="true"/> <element name="quantity" type="input" selector="#product_composite_configure_input_qty"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormDownloadableProductSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormDownloadableProductSection.xml index b77b01d54f950..94cb0c57d4ee2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormDownloadableProductSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormDownloadableProductSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormDownloadableProductSection"> <element name="optionSelect" type="select" selector="//div[contains(@class,'link')]/div/div/input[./../label[contains(text(),{{linkTitle}})]]" parameterized="true"/> <element name="quantity" type="input" selector="#product_composite_configure_input_qty"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormGroupedProductSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormGroupedProductSection.xml index ceba11f74ae83..5a25a1bd880f4 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormGroupedProductSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormGroupedProductSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormGroupedProductSection"> <element name="optionQty" type="input" selector="//td[@class='col-sku'][text()='{{productSku}}']/..//input[contains(@class, 'qty')]" parameterized="true"/> <element name="ok" type="button" selector=".modal-header .page-actions button[data-role='action']" timeout="30"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsSection.xml index 86288ec7d7ec9..65f9a41c269c9 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormItemsSection"> <element name="skuNumber" type="input" selector="#sku_{{row}}" parameterized="true"/> <element name="qty" type="input" selector="#sku_qty_{{row}}" parameterized="true"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml index bb7c89dd39b6c..e4d329bc85057 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormPaymentSection"> <element name="header" type="text" selector="#order-methods span.title"/> <element name="getShippingMethods" type="text" selector="#order-shipping_method a.action-default" timeout="30"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormShippingAddressSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormShippingAddressSection.xml index 82ecb023198c7..b79d933268769 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormShippingAddressSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormShippingAddressSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormShippingAddressSection"> <element name="SameAsBilling" type="checkbox" selector="#order-shipping_same_as_billing"/> <element name="NamePrefix" type="input" selector="#order-shipping_address_prefix"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormTotalSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormTotalSection.xml index e44a97b678f89..6f62ce199ecbb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormTotalSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormTotalSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormTotalSection"> <element name="subtotalRow" type="text" selector="#order-totals>table tr.row-totals:nth-of-type({{row}}) span.price" parameterized="true"/> <element name="total" type="text" selector="//tr[contains(@class,'row-totals')]/td[contains(text(), '{{total}}')]/following-sibling::td/span[contains(@class, 'price')]" parameterized="true"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderInvoicesTabSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderInvoicesTabSection.xml index e00ab7e66b99a..b33276bed527e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderInvoicesTabSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderInvoicesTabSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderInvoicesTabSection"> <element name="spinner" type="text" selector="[data-role='spinner'][data-component*='sales_order_view_invoice']"/> <element name="gridRow" type="text" selector="#sales_order_view_tabs_order_invoices_content .data-grid tbody > tr:nth-of-type({{row}})" parameterized="true"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml index 9807d7364c7c6..c4fcfd1095a33 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderItemsOrderedSection"> <element name="itemProductName" type="text" selector=".edit-order-table tr:nth-of-type({{row}}) .col-product .product-title" parameterized="true"/> <element name="itemProductSku" type="text" selector=".edit-order-table tr:nth-of-type({{row}}) .col-product .product-sku-block" parameterized="true"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderPaymentInformationSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderPaymentInformationSection.xml index 15b468d1dfa9b..9299222fd3236 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderPaymentInformationSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderPaymentInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderPaymentInformationSection"> <element name="paymentMethod" type="text" selector=".order-payment-method .order-payment-method-title"/> <element name="paymentCurrency" type="text" selector=".order-payment-method .order-payment-currency"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderShipmentsTabSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderShipmentsTabSection.xml index 65dbf01aad2e0..70d413d733b8e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderShipmentsTabSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderShipmentsTabSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderShipmentsTabSection"> <element name="spinner" type="text" selector="[data-role='spinner'][data-component*='sales_order_view_shipment']"/> <element name="gridRow" type="text" selector="#sales_order_view_tabs_order_shipments_content .data-grid tbody > tr:nth-of-type({{row}})" parameterized="true"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderShippingInformationSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderShippingInformationSection.xml index f29e8a2a7f970..83e5512ef0d27 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderShippingInformationSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderShippingInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderShippingInformationSection"> <element name="shippingMethod" type="text" selector=".order-shipping-method .admin__page-section-item-content"/> <element name="shippingPrice" type="text" selector=".order-shipping-method .admin__page-section-item-content .price"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStoreScopeTreeSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStoreScopeTreeSection.xml index 8f31dd1b8e96c..050e1ba8b2df4 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStoreScopeTreeSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStoreScopeTreeSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderStoreScopeTreeSection"> <element name="storeTree" type="text" selector="div.tree-store-scope"/> <element name="storeOption" type="radio" selector="//div[contains(@class, 'tree-store-scope')]//label[contains(text(), '{{name}}')]/preceding-sibling::input" parameterized="true" timeout="30"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderTotalSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderTotalSection.xml index 5d60886a96a51..9b7356127df69 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderTotalSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderTotalSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderTotalSection"> <element name="subTotal" type="text" selector=".order-subtotal-table tbody tr.col-0>td span.price"/> <element name="grandTotal" type="text" selector=".order-subtotal-table tfoot tr.col-0>td span.price"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml index 49aae467b7e09..7ece18fb863b7 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrdersGridSection"> <element name="spinner" type="button" selector=".spinner"/> <element name="gridLoadingMask" type="button" selector=".admin__data-grid-loading-mask"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/OrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/OrdersGridSection.xml index f54fbf4cf4d53..55daae7692040 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/OrdersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/OrdersGridSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="OrdersGridSection"> <element name="spinner" type="button" selector=".spinner"/> <element name="gridLoadingMask" type="button" selector=".admin__data-grid-loading-mask"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index 317a325693c65..05f20371851bc 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateInvoiceTest"> <annotations> <features value="Sales"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml index 23e4ae676b35b..f4a228c72250f 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminSubmitsOrderWithAndWithoutEmailTest"> <annotations> <title value="Email is required to create an order from Admin Panel"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index 4cb72972b4ce2..ab067ea45222f 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="CreditMemoTotalAfterShippingDiscountTest"> <annotations> <features value="Credit memo"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 4dc3c7a6ae788..0fdd8d8c35b32 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CAdminTest"> <before> <!--Create order via API--> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml index 997b3b4b9ff06..bae7069859937 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="selectNotLoggedInCustomerGroup"> <!-- This actionGroup was created to be merged from B2B because B2B has a very different form control here --> <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminSalesRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminSalesRuleActionGroup.xml index 4026c3d65cfaf..800621ac70ff1 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminSalesRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminSalesRuleActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="DeleteCartPriceRuleByName"> <arguments> <argument name="ruleName" type="string"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml index e8c520f1f985e..55b2e9c10fd64 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="ApplyCartRuleOnStorefrontActionGroup"> <arguments> <argument name="Product" defaultValue="_defaultProduct"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml index 5d1cc877aa775..70d1fc56d2cea 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Apply Sales Rule Coupon to the cart --> <actionGroup name="StorefrontApplyCouponActionGroup"> <arguments> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/QuoteData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/QuoteData.xml index 7e929266b89ef..4cd5513080f73 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/QuoteData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/QuoteData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <!-- @TODO: Get rid off this workaround and its usages after MQE-498 is implemented --> <entity name="E2EB2CQuoteWith10PercentDiscount" type="Quote"> <data key="subtotal">480.00</data> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesCouponData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesCouponData.xml index 917b4ca179b8a..bab82fa20463b 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesCouponData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesCouponData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ApiSalesRuleCoupon" type="SalesRuleCoupon"> <data key="code" unique="suffix">salesCoupon</data> <data key="times_used">0</data> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml index 6cca73dff5724..10b198b53f389 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml @@ -6,7 +6,7 @@ */ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="SimpleSalesRuleCoupon" type="SalesRuleCoupon"> <var key="rule_id" entityKey="rule_id" entityType="SalesRule"/> <data key="code" unique="suffix">Code</data> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml index efd21405a7cc1..5b7585b8b2a3f 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ApiSalesRule" type="SalesRule"> <data key="name" unique="suffix">salesRule</data> <data key="description">Sales Rule Descritpion</data> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleLabelData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleLabelData.xml index f1916e81e0e52..90fe36fd7bdba 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleLabelData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleLabelData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="SalesRuleLabelDefault" type="SalesRuleLabel"> <data key="store_id">0</data> <data key="store_label" unique="suffix">Sales Rule (Default) </data> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-condition-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-condition-meta.xml index bf4809a7d3f8d..bd50be31e01b8 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-condition-meta.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-condition-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateSalesRuleCondition" dataType="SalesRuleCondition" type="create"> <field key="condition_type" required="true">string</field> <array key="conditions" required="true"> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-coupon-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-coupon-meta.xml index 03b87a96e6ce6..a2025add0b629 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-coupon-meta.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-coupon-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateSalesRuleCoupon" dataType="SalesRuleCoupon" type="create" auth="adminOauth" url="/V1/coupons" method="POST"> <contentType>application/json</contentType> <object key="coupon" dataType="SalesRuleCoupon"> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-label-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-label-meta.xml index 676cb7026c2fa..c462824a47f97 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-label-meta.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-label-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateSalesRuleLabel" dataType="SalesRuleLabel" type="create"> <field key="store_id" required="true">integer</field> <field key="store_label" required="true">string</field> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-meta.xml index 6e6a203a4e6c5..3b3f7f39a65a0 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-meta.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateSalesRule" dataType="SalesRule" type="create" auth="adminOauth" url="/V1/salesRules" method="POST"> <contentType>application/json</contentType> <object key="rule" dataType="SalesRule"> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Page/AdminCartPriceRulesPage.xml b/app/code/Magento/SalesRule/Test/Mftf/Page/AdminCartPriceRulesPage.xml index 2c260540ae79f..78e10904411c3 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Page/AdminCartPriceRulesPage.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Page/AdminCartPriceRulesPage.xml @@ -6,7 +6,7 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminCartPriceRulesPage" url="sales_rule/promo_quote/" area="admin" module="SalesRule"> <section name="AdminCartPriceRulesSection"/> <section name="AdminCartPriceRulesFormSection"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Page/PriceRuleNewPage.xml b/app/code/Magento/SalesRule/Test/Mftf/Page/PriceRuleNewPage.xml index 4bedeb88effc8..94967fedf8f01 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Page/PriceRuleNewPage.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Page/PriceRuleNewPage.xml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ --> -<pages xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> +<pages xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <page name="PriceRuleNewPage" url="sales_rule/promo_quote/new/" area="admin" module="Magento_SalesRule"> <section name="PriceRuleConditionsSection"/> </page> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml index f31ff1a456898..fcacb3a3a37bc 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCartPriceRulesFormSection"> <element name="save" type="button" selector="#save" timeout="30"/> <element name="saveAndContinue" type="button" selector="#save_and_continue" timeout="30"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesSection.xml index 5fe5fc1e34687..a32c50d9d8617 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCartPriceRulesSection"> <element name="addNewRuleButton" type="button" selector="#add" timeout="30"/> <element name="messages" type="text" selector=".messages"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/CheckoutCartSummarySection.xml index 5327d53032d48..a5fb96afcc972 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutCartSummarySection"> <element name="discountLabel" type="text" selector="//*[@id='cart-totals']//tr[.//th//span[contains(@class, 'discount coupon')]]"/> <element name="discountTotal" type="text" selector="//*[@id='cart-totals']//tr[.//th//span[contains(@class, 'discount coupon')]]//td//span//span[@class='price']"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/DiscountSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/DiscountSection.xml index cc9ab6724528c..cbab097c5291b 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/DiscountSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/DiscountSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="DiscountSection"> <element name="DiscountTab" type="button" selector="//strong[text()='Apply Discount Code']"/> <element name="CouponInput" type="input" selector="#coupon_code"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml index 1b05ec838dc98..9a74ced2a2c17 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ --> -<sections xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> +<sections xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <section name="PriceRuleConditionsSection"> <element name="conditionsTab" type="text" selector="//div[@data-index='conditions']//span[contains(.,'Conditions')][1]"/> <element name="createNewRule" type="text" selector="span.rule-param.rule-param-new-child"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontSalesRuleCartCouponSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontSalesRuleCartCouponSection.xml index 39d85ae5fa69b..be52aa05f5af1 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontSalesRuleCartCouponSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontSalesRuleCartCouponSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontSalesRuleCartCouponSection"> <element name="couponHeader" type="button" selector="//*[@id='block-discount-heading']"/> <element name="couponField" type="text" selector="//*[@id='coupon_code']"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml index d21034bc9248f..c69fa97efc034 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateBuyXGetYFreeTest"> <annotations> <features value="SalesRule"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml index a33a4b819b530..88d8f1945ce68 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateCartPriceRuleForCouponCodeTest"> <annotations> <features value="SalesRule"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml index cf8879514f5a5..c1aeebfca520e 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateCartPriceRuleForGeneratedCouponTest"> <annotations> <features value="SalesRule"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml index adf8b391a68bf..30aa26b26ed39 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateFixedAmountDiscountTest"> <annotations> <features value="SalesRule"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml index c482d25828397..7ac69f82f79da 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateFixedAmountWholeCartDiscountTest"> <annotations> <features value="SalesRule"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml index 2de7952cd1208..eb04ce04f0718 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreatePercentOfProductPriceTest"> <annotations> <features value="SalesRule"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 05ea5a32574c8..da9eb8e19790e 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CGuestUserTest"> <before> <createData entity="ApiSalesRule" stepKey="createSalesRule"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index c0ef70dbd048a..d735d5a73f0f5 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CLoggedInUserTest"> <before> <createData entity="ApiSalesRule" stepKey="createSalesRule"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml index a46fc19a51cc8..de5f480ac6d7e 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ --> -<tests xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> +<tests xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <test name="PriceRuleCategoryNestingTest"> <annotations> <description value="Category nesting level must be the same as were created in categories."/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml index 508b16721b2df..ca897bffe8b79 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontCartPriceRuleCountry"> <annotations> <features value="SalesRule"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml index e025d8b2a3b68..83854c4a767ca 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontCartPriceRulePostcode"> <annotations> <features value="SalesRule"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml index 3e54620d24937..60a19074317fc 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontCartPriceRuleQuantity"> <annotations> <features value="SalesRule"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml index 98c4b1144b475..f98f7b408436f 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontCartPriceRuleState"> <annotations> <features value="SalesRule"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml index 93c64903a337e..6567beba198eb 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontCartPriceRuleSubtotal"> <annotations> <features value="SalesRule"/> diff --git a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchSection.xml b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchSection.xml index 543725fc5fa1c..2b08e9b4b85ec 100644 --- a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchSection.xml +++ b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontQuickSearchSection"> <element name="searchPhrase" type="input" selector="#search"/> <element name="searchButton" type="button" selector="button.action.search" timeout="30"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml index 80db1fe9469a3..85430aeaa4168 100644 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="verifyBasicShipmentInformation"> <arguments> diff --git a/app/code/Magento/Shipping/Test/Mftf/Data/FlatRateShippingMethodData.xml b/app/code/Magento/Shipping/Test/Mftf/Data/FlatRateShippingMethodData.xml index 120517bffde8f..6c7e5cf1b18e0 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Data/FlatRateShippingMethodData.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Data/FlatRateShippingMethodData.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <!-- Enable Flat Rate Shipping method config --> <entity name="FlatRateShippingMethodConfig" type="flat_rate_shipping_method"> <requiredEntity type="active">flatRateActiveEnable</requiredEntity> diff --git a/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml b/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml index fba1970dba294..110795533468e 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <!-- Enable Free Shipping method --> <entity name="FreeShippinMethodConfig" type="free_shipping_method"> <requiredEntity type="active">freeActiveEnable</requiredEntity> diff --git a/app/code/Magento/Shipping/Test/Mftf/Data/ShippingMethodData.xml b/app/code/Magento/Shipping/Test/Mftf/Data/ShippingMethodData.xml index 3e8613ec2e43f..1151e55c4378f 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Data/ShippingMethodData.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Data/ShippingMethodData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="DefaultFlatRateMethod" type="shipping"> <data key="enabled">Yes</data> <data key="title">Flat Rate</data> diff --git a/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml b/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml index 8b6a2aab74580..5781b886386f6 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="FlatRateShippingMethodSetup" dataType="flat_rate_shipping_method" type="create" auth="adminFormKey" url="/admin/system_config/save/section/carriers/" method="POST"> <object key="groups" dataType="flat_rate_shipping_method"> <object key="flatrate" dataType="flat_rate_shipping_method"> diff --git a/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentNewPage.xml b/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentNewPage.xml index e6b3f1100dd88..597abb5694e30 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentNewPage.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentNewPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminShipmentNewPage" url="order_shipment/new/order_id/" area="admin" module="Shipping"> <section name="AdminShipmentMainActionsSection"/> <section name="AdminShipmentOrderInformationSection"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml index ea4dde8190bc7..14fefd981e4ed 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminShipmentAddressInformationSection"> <element name="billingAddress" type="text" selector=".order-billing-address address"/> <element name="billingAddressEdit" type="button" selector=".order-billing-address .actions a"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml index 30f508beb57ab..a7bf82588f7c7 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminShipmentItemsSection"> <element name="itemName" type="text" selector=".order-shipment-table tbody:nth-of-type({{var1}}) .col-product .product-title" parameterized="true"/> <element name="itemSku" type="text" selector=".order-shipment-table tbody:nth-of-type({{var1}}) .col-product .product-sku-block" parameterized="true"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentMainActionsSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentMainActionsSection.xml index 506a7a496d8d8..9f66b269b96ac 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentMainActionsSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentMainActionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminShipmentMainActionsSection"> <element name="submitShipment" type="button" selector="button.action-default.save.submit-button"/> </section> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentOrderInformationSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentOrderInformationSection.xml index 56f5a3221535b..ca2b867bc1f4c 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentOrderInformationSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentOrderInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminShipmentOrderInformationSection"> <element name="orderId" type="text" selector="div.order-information span.title > a" timeout="30"/> <element name="orderDate" type="text" selector=".order-information table.order-information-table tr:first-of-type > td"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentPaymentShippingSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentPaymentShippingSection.xml index e6004e8c59454..48c7106c2d65e 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentPaymentShippingSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentPaymentShippingSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminShipmentPaymentShippingSection"> <element name="PaymentMethod" type="text" selector=".order-payment-method .order-payment-method-title"/> <element name="CurrencyInformation" type="text" selector=".order-payment-method .order-payment-currency"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentTotalSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentTotalSection.xml index 2a4150b19a454..f2f39d77d8d79 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentTotalSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentTotalSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminShipmentTotalSection"> <element name="CommentText" type="textarea" selector="#shipment_comment_text"/> <element name="AppendComments" type="checkbox" selector=".order-totals input#notify_customer"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/EndToEndB2CAdminTest.xml index e14fed443ac0c..a0edf4e13a80f 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -6,7 +6,7 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CAdminTest"> <!--Ship Order--> <comment userInput="Admin creates shipment" stepKey="adminCreatesShipmentComment" before="clickShipAction"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml index e67e21e4c1e67..91fe4fccddb91 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml @@ -7,7 +7,7 @@ --> <!-- Admin creates new Store group --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCreateNewStoreGroupActionGroup"> <arguments> <argument name="website" type="string"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml index 0819a74ea8996..b21c79692a7cf 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCreateStoreGroupActionGroup"> <arguments> <argument name="Website" defaultValue="_defaultWebsite"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml index b35b36bc667a7..99ca7991e5e90 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml @@ -7,7 +7,7 @@ --> <!-- Test XML Example --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCreateStoreViewActionGroup"> <arguments> <argument name="StoreGroup" defaultValue="_defaultStoreGroup"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml index 709cfe8ec9c40..a87356303c6e8 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml @@ -7,7 +7,7 @@ --> <!-- Admin creates new custom website --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCreateWebsiteActionGroup"> <arguments> <argument name="newWebsiteName" type="string"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml index 1267ebf8f440c..849dc91efedb7 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml @@ -7,7 +7,7 @@ --> <!-- Test XML Example --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminDeleteStoreViewActionGroup"> <arguments> <argument name="customStore" defaultValue="customStore"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml index 0512a1f6fbc3f..1400fbf12c16c 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminDeleteWebsiteActionGroup"> <arguments> <argument name="websiteName" type="string"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml index bd004f1fc7de3..860f094a48ecc 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml @@ -7,7 +7,7 @@ --> <!-- Test XML Example --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminSwitchStoreViewActionGroup"> <arguments> <argument name="storeView" defaultValue="customStore.name"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreViewActionGroup.xml index 1e23a85a78935..31bbe7550e5a1 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreViewActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CreateCustomStoreViewActionGroup"> <arguments> <argument name="storeGroupName" defaultValue="customStoreGroup.name"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreActionGroup.xml index 0cac9bbd9954b..8e32b819aa954 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="DeleteCustomStoreActionGroup"> <arguments> <argument name="storeGroupName" defaultValue="customStoreGroup.name"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml index 0f8673eb2f4aa..cc6a1fb62ea5f 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="DeleteCustomWebsiteActionGroup"> <arguments> <argument name="websiteName" defaultValue="customWebsite.name"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchStoreViewActionGroup.xml index d14de9af9c14a..cfcd25086e067 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchStoreViewActionGroup.xml @@ -7,7 +7,7 @@ --> <!-- Test XML Example --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontSwitchStoreViewActionGroup"> <arguments> <argument name="storeView" defaultValue="customStore"/> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index 4e3c724572e79..5d73cbe7d7fcd 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultStore" type="store"> <data key="name">Default Store View</data> <data key="code">default</data> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml index 8c293bc22f2e8..83ca12875d099 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultStoreGroup" type="group"> <data key="name">Main Website Store</data> <data key="code">main_website_store</data> diff --git a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml index e8528fba1ae29..ee137d78d7fd2 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultWebsite" type="website"> <data key="name">Main Website</data> <data key="code">base</data> diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/store-meta.xml index e0263b2c88869..a1cfc69ecd705 100644 --- a/app/code/Magento/Store/Test/Mftf/Metadata/store-meta.xml +++ b/app/code/Magento/Store/Test/Mftf/Metadata/store-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateStore" dataType="store" type="create" auth="adminFormKey" url="/admin/system_store/save" method="POST" successRegex="/messages-message-success/" returnRegex="" > <object dataType="store" key="store"> diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store_group-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/store_group-meta.xml index bc117756a542b..a8d8ff7b68323 100644 --- a/app/code/Magento/Store/Test/Mftf/Metadata/store_group-meta.xml +++ b/app/code/Magento/Store/Test/Mftf/Metadata/store_group-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateStoreGroup" dataType="group" type="create" auth="adminFormKey" url="/admin/system_store/save" method="POST" successRegex="/messages-message-success/" returnRegex="" > <contentType>application/x-www-form-urlencoded</contentType> diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/website-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/website-meta.xml index 4e314396ab046..bad274501f710 100644 --- a/app/code/Magento/Store/Test/Mftf/Metadata/website-meta.xml +++ b/app/code/Magento/Store/Test/Mftf/Metadata/website-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateWebsite" dataType="website" type="create" auth="adminFormKey" url="/admin/system_store/save" method="POST" successRegex="/messages-message-success/" returnRegex=""> <object dataType="website" key="website"> diff --git a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreDeletePage.xml b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreDeletePage.xml index 3ec22d3135137..79472f1cc2899 100644 --- a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreDeletePage.xml +++ b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreDeletePage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminSystemStoreDeletePage" url="system_store/deleteStore" module="Magento_Store" area="admin"> <section name="AdminStoreBackupOptionsSection"/> </page> diff --git a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreEditPage.xml b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreEditPage.xml index fe2bfcab39ae4..6b020a1bffd95 100644 --- a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreEditPage.xml +++ b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreEditPage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminSystemStoreEditPage" url="system_store/editStore" module="Magento_Store" area="admin"> <section name="AdminNewStoreViewMainActionsSection"/> <section name="AdminNewStoreSection"/> diff --git a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreGroupEditPage.xml b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreGroupEditPage.xml index 634ee6d651af1..386869a8fa19b 100644 --- a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreGroupEditPage.xml +++ b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreGroupEditPage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminSystemStoreGroupEditPage" url="admin/system_store/editGroup" area="admin" module="Magento_Store"> <section name="AdminStoreGroupActionsSection"/> </page> diff --git a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreGroupPage.xml b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreGroupPage.xml index 3c73d019aa540..8d48f3fd80417 100644 --- a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreGroupPage.xml +++ b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreGroupPage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminSystemStoreGroupPage" url="admin/system_store/newGroup" module="Magento_Store" area="admin"> <section name="AdminNewStoreGroupSection"/> </page> diff --git a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStorePage.xml b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStorePage.xml index 9eed4f6557a59..c309c47035bba 100644 --- a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStorePage.xml +++ b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStorePage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminSystemStorePage" url="/admin/system_store/" area="admin" module="Magento_Store"> <section name="AdminStoresMainActionsSection"/> <section name="AdminStoresGridSection"/> diff --git a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreViewPage.xml b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreViewPage.xml index 15ed31c19f996..4a7de70fb3c35 100644 --- a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreViewPage.xml +++ b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreViewPage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminSystemStoreViewPage" url="admin/system_store/newStore" module="Magento_Store" area="admin"> <section name="AdminNewStoreViewMainActionsSection"/> <section name="AdminNewStoreSection"/> diff --git a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreWebsitePage.xml b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreWebsitePage.xml index 6f99e4340a070..9296d2667b93d 100644 --- a/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreWebsitePage.xml +++ b/app/code/Magento/Store/Test/Mftf/Page/AdminSystemStoreWebsitePage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminSystemStoreWebsitePage" url="admin/system_store/newWebsite" module="Magento_Store" area="admin"> <section name="AdminNewWebsiteSection"/> </page> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml index 0927a1ffc950b..fda182246db4a 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMainActionsSection"> <element name="storeSwitcher" type="text" selector=".store-switcher"/> <element name="storeViewDropdown" type="button" selector="#store-change-button"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreGroupActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreGroupActionsSection.xml index f026c7765b6d6..66e1407f17b26 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreGroupActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreGroupActionsSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewStoreGroupActionsSection"> <element name="backButton" type="button" selector="#back" timeout="30"/> <element name="delete" type="button" selector="#delete" timeout="30"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreGroupSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreGroupSection.xml index 106a0f4de5e8b..ea5d9aab8b26d 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreGroupSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreGroupSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewStoreGroupSection"> <element name="storeGrpWebsiteDropdown" type="select" selector="#group_website_id"/> <element name="storeGrpNameTextField" type="input" selector="#group_name"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreSection.xml index cec7a1f4f81e1..5a7d9bba7ebec 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewStoreSection"> <element name="storeNameTextField" type="input" selector="#store_name"/> <element name="storeCodeTextField" type="input" selector="#store_code"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml index a3b5d1e616319..faffc69dc6975 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewStoreViewActionsSection"> <element name="backButton" type="button" selector="#back" timeout="30"/> <element name="delete" type="button" selector="#delete" timeout="30"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml index 703abea8cfd0d..89c0ad15e7dab 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewWebsiteActionsSection"> <element name="saveWebsite" type="button" selector="#save" timeout="60"/> </section> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteSection.xml index 21dee5f6b6e0d..ea67cf71ccd68 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewWebsiteSection"> <element name="name" type="input" selector="#website_name"/> <element name="code" type="input" selector="#website_code"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoreBackupOptionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoreBackupOptionsSection.xml index 58248b1943714..82ec219f541a3 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoreBackupOptionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoreBackupOptionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminStoreBackupOptionsSection"> <element name="createBackupSelect" type="select" selector="select#store_create_backup"/> </section> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml index 6dc766c0c02da..f79ea080ed1ca 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminStoreGroupActionsSection"> <element name="saveButton" type="button" selector="#save" timeout="60" /> </section> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteStoreGroupSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteStoreGroupSection.xml index ba3d9660b44b3..8ac48dae364b1 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteStoreGroupSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteStoreGroupSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminStoresDeleteStoreGroupSection"> <element name="createDbBackup" type="select" selector="#store_create_backup"/> <element name="deleteStoreGroupButton" type="button" selector="#delete" timeout="30"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml index 50c536dcfe809..fea7dc07c8287 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminStoresDeleteWebsiteSection"> <element name="createDbBackup" type="select" selector="#store_create_backup"/> <element name="deleteButton" type="button" selector="#delete" timeout="30"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml index 7630f316d8095..04cbeb5bc596e 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminStoresGridControlsSection"> <element name="createStoreView" type="button" selector="#add_store"/> <element name="createStore" type="button" selector="#add_group"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresMainActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresMainActionsSection.xml index d86a68d0f7b99..98ad1db46732b 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresMainActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresMainActionsSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminStoresMainActionsSection"> <element name="createStoreViewButton" type="button" selector="#add_store" timeout="30"/> <element name="createStoreButton" type="button" selector="#add_group" timeout="30"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/StorefrontHeaderSection.xml b/app/code/Magento/Store/Test/Mftf/Section/StorefrontHeaderSection.xml index 4bb62a5a7f6b9..af18e858e1057 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/StorefrontHeaderSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/StorefrontHeaderSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontHeaderSection"> <element name="storeViewSwitcher" type="button" selector="#switcher-language-trigger"/> <element name="storeViewDropdown" type="button" selector="ul.switcher-dropdown"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml index e3345c1f2f094..4e5dfed70d36e 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml @@ -6,7 +6,7 @@ */ --> <!-- Test XML Example --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateStoreGroupTest"> <annotations> <features value="Store"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml index d38d4dc992a2b..288245066b84d 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml @@ -6,7 +6,7 @@ */ --> <!-- Test XML Example --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateStoreViewTest"> <annotations> <features value="Store"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml index 09137a7003b94..60a8035dedeca 100644 --- a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AddVisualSwatchToProductActionGroup"> <arguments> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml index f8cfa3071ce0f..6f991274a015b 100644 --- a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="setColorPickerByHex"> <arguments> <argument name="nthColorPicker" type="string" defaultValue="1"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml index 0e70bdcc70249..08e24cfeb38fe 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="visualSwatchAttribute" type="SwatchAttribute"> <data key="default_label" unique="suffix">VisualSwatchAttr</data> <data key="input_type">Visual Swatch</data> diff --git a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml index 76bfbe8e1b870..4608d1a9190a9 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="visualSwatchOption1" type="SwatchOption"> <data key="admin_label" unique="suffix">VisualOpt1</data> <data key="default_label" unique="suffix">VisualOpt1</data> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml index 772b724b6648d..50d56d64bb67b 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminColorPickerSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminColorPickerSection"> <element name="hexByIndex" type="input" selector="//div[@class='colorpicker'][{{var}}]/div[@class='colorpicker_hex']/input" parameterized="true"/> <element name="submitByIndex" type="button" selector="//div[@class='colorpicker'][{{var}}]/div[@class='colorpicker_submit']" parameterized="true"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml index 6e430dd30a512..25e03676f6870 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminManageSwatchSection"> <element name="adminInputByIndex" type="input" selector="optionvisual[value][option_{{var}}][0]" parameterized="true"/> <element name="addSwatch" type="button" selector="#add_new_swatch_visual_option_button" timeout="30"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminNewAttributePanelSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminNewAttributePanelSection.xml index 36c2056a45771..adefce9182724 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/AdminNewAttributePanelSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminNewAttributePanelSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewAttributePanel"> <element name="addVisualSwatchOption" type="button" selector="button#add_new_swatch_visual_option_button"/> <element name="addTextSwatchOption" type="button" selector="button#add_new_swatch_text_option_button"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontCategorySidebarSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontCategorySidebarSection.xml index 750191f19cf13..43746fc08a0da 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontCategorySidebarSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontCategorySidebarSection.xml @@ -6,7 +6,7 @@ */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategorySidebarSection"> <element name="layeredFilterBlock" type="block" selector="#layered-filter-block"/> <element name="filterOptionTitle" type="button" selector="//div[@class='filter-options-title'][text() = '{{var}}']" parameterized="true" timeout="30"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 68c1d29258727..415ae88fceb52 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoMainSection"> <element name="swatchOptionByLabel" type="button" selector="div.swatch-option[option-label={{opt}}]" parameterized="true"/> <element name="nthSwatchOption" type="button" selector="div.swatch-option:nth-of-type({{var}})" parameterized="true"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml index 5ec6c0c7332a6..a763bda2e494f 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateImageSwatchTest"> <annotations> <features value="Swatches"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml index 0c26c6a8174af..3ef347b7aca12 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateTextSwatchTest"> <annotations> <features value="Swatches"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml index 67750ac931f0b..90e94466351b6 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateVisualSwatchTest"> <annotations> <features value="Swatches"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml index a7e975fe41975..e4c96ab3a2ba7 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontFilterByImageSwatchTest"> <annotations> <features value="Swatches"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml index a59b4b1208668..d12cb0433fed1 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontFilterByVisualSwatchTest"> <annotations> <features value="Swatches"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml index cc6699e989101..7ef030ef8dfa8 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontSwatchProductWithFileCustomOptionTest"> <annotations> <features value="ConfigurableProduct"/> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminTaxActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminTaxActionGroup.xml index 6d6e5cea0fd34..6c535e3004e69 100644 --- a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminTaxActionGroup.xml +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminTaxActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Change the tax configuration to display in cart and checkout flow --> <actionGroup name="editTaxConfigurationByUI"> <!-- navigate to the tax configuration page --> diff --git a/app/code/Magento/Tax/Test/Mftf/Data/TaxCodeData.xml b/app/code/Magento/Tax/Test/Mftf/Data/TaxCodeData.xml index e786616119c92..42fd01357375e 100644 --- a/app/code/Magento/Tax/Test/Mftf/Data/TaxCodeData.xml +++ b/app/code/Magento/Tax/Test/Mftf/Data/TaxCodeData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="SimpleTaxNY" type="tax"> <data key="state">New York</data> <data key="country">United States</data> diff --git a/app/code/Magento/Tax/Test/Mftf/Data/TaxConfigData.xml b/app/code/Magento/Tax/Test/Mftf/Data/TaxConfigData.xml index d7c88c1d282e2..4edf005c2fc2b 100644 --- a/app/code/Magento/Tax/Test/Mftf/Data/TaxConfigData.xml +++ b/app/code/Magento/Tax/Test/Mftf/Data/TaxConfigData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <!-- Default Tax Destination Calculation --> <entity name="CountryUS" type="country"> <data key="value">US</data> diff --git a/app/code/Magento/Tax/Test/Mftf/Data/TaxRegionData.xml b/app/code/Magento/Tax/Test/Mftf/Data/TaxRegionData.xml index c27225a339831..353bc0a489443 100644 --- a/app/code/Magento/Tax/Test/Mftf/Data/TaxRegionData.xml +++ b/app/code/Magento/Tax/Test/Mftf/Data/TaxRegionData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="Region_NY" type="region"> <data key="value">43</data> </entity> diff --git a/app/code/Magento/Tax/Test/Mftf/Data/TaxRuleData.xml b/app/code/Magento/Tax/Test/Mftf/Data/TaxRuleData.xml index 16c891745426d..b3f341b687ba7 100644 --- a/app/code/Magento/Tax/Test/Mftf/Data/TaxRuleData.xml +++ b/app/code/Magento/Tax/Test/Mftf/Data/TaxRuleData.xml @@ -6,7 +6,7 @@ */ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="SimpleTaxRule" type="taxRule"> <data key="code" unique="suffix">TaxRule</data> <data key="position">0</data> diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml index 137c2e48c111e..7383e9c580283 100644 --- a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml +++ b/app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateTaxConfigDefaultsTaxDestination" dataType="tax_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> <object key="groups" dataType="tax_config_state"> <object key="defaults" dataType="tax_config_state"> diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rule-meta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rule-meta.xml index f9886303fd3a3..c5b781519912d 100644 --- a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rule-meta.xml +++ b/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rule-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateTaxRule" dataType="taxRule" type="create" auth="adminOauth" url="/V1/taxRules" method="POST"> <contentType>application/json</contentType> <object key="rule" dataType="taxRule"> diff --git a/app/code/Magento/Tax/Test/Mftf/Page/AdminNewTaxRulePage.xml b/app/code/Magento/Tax/Test/Mftf/Page/AdminNewTaxRulePage.xml index cf7fcf041c1b1..6aedc0014280c 100644 --- a/app/code/Magento/Tax/Test/Mftf/Page/AdminNewTaxRulePage.xml +++ b/app/code/Magento/Tax/Test/Mftf/Page/AdminNewTaxRulePage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminNewTaxRulePage" url="tax/rule/new/" module="Magento_Tax" area="admin"> <section name="AdminTaxRulesSection"/> </page> diff --git a/app/code/Magento/Tax/Test/Mftf/Page/AdminTaxConfigurationPage.xml b/app/code/Magento/Tax/Test/Mftf/Page/AdminTaxConfigurationPage.xml index d18e300983b5f..a9d14de18de80 100644 --- a/app/code/Magento/Tax/Test/Mftf/Page/AdminTaxConfigurationPage.xml +++ b/app/code/Magento/Tax/Test/Mftf/Page/AdminTaxConfigurationPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminTaxConfigurationPage" url="admin/system_config/edit/section/tax/" area="admin" module="Magento_Tax"> <section name="AdminConfigureTaxSection"/> </page> diff --git a/app/code/Magento/Tax/Test/Mftf/Page/AdminTaxRateGridPage.xml b/app/code/Magento/Tax/Test/Mftf/Page/AdminTaxRateGridPage.xml index 6073766a81692..716c399110470 100644 --- a/app/code/Magento/Tax/Test/Mftf/Page/AdminTaxRateGridPage.xml +++ b/app/code/Magento/Tax/Test/Mftf/Page/AdminTaxRateGridPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminTaxRateGridPage" url="tax/rate/" area="admin" module="Magento_Tax"> <section name="AdminSecondaryGridSection"/> </page> diff --git a/app/code/Magento/Tax/Test/Mftf/Page/AdminTaxRuleGridPage.xml b/app/code/Magento/Tax/Test/Mftf/Page/AdminTaxRuleGridPage.xml index 8c75237a203a6..3c8326e95d930 100644 --- a/app/code/Magento/Tax/Test/Mftf/Page/AdminTaxRuleGridPage.xml +++ b/app/code/Magento/Tax/Test/Mftf/Page/AdminTaxRuleGridPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminTaxRuleGridPage" url="tax/rule" area="admin" module="Magento_Tax"> <section name="AdminSecondaryGridSection"/> <section name="AdminGridMainControls"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml index af33b6b4ad539..896d719a436ca 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminConfigureTaxSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminConfigureTaxSection"> <!-- on page /admin/admin/system_config/edit/section/tax/ --> <element name="taxClasses" type="block" selector="#tax_classes-head" timeout="30"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRulesSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRulesSection.xml index f7b72263c1255..9727c649d7e66 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRulesSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRulesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminTaxRulesSection"> <!-- on page /admin/tax/rule/new/ --> <element name="ruleName" type="input" selector="#anchor-content #code"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml index b47e8b85e5231..b89a77b8ad2ca 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutCartSummarySection"> <element name="taxAmount" type="text" selector="[data-th='Tax']>span"/> <element name="taxSummary" type="text" selector=".totals-tax-summary"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml index fdd6d18dae9c4..c89b75c229341 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest"> <annotations> <features value="Tax information in shopping cart for Customer with default addresses (physical quote)"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml index c27af9a6f63f5..cbe09059c26cd 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest"> <annotations> <features value="Tax information in shopping cart for Customer with default addresses (virtual quote)"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml index 891e8f4e7968b..5e3594b62b500 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest"> <annotations> <features value="Tax information in shopping cart for Guest (physical quote)"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml index 9f33c1a8b155a..036686050db75 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest"> <annotations> <features value="Tax information in shopping cart for Guest (virtual quote)"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml index c0b32e4bc71e7..633eab4df47b4 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontTaxQuoteCartLoggedInSimple"> <annotations> <features value="Tax"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml index f051d4f8c3b82..1aa87725a1a41 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontTaxQuoteCheckoutGuestVirtual"> <annotations> <features value="Tax"/> diff --git a/app/code/Magento/Theme/Test/Mftf/Data/DesignData.xml b/app/code/Magento/Theme/Test/Mftf/Data/DesignData.xml index 988d71bc4086b..ec28e8ed7a999 100644 --- a/app/code/Magento/Theme/Test/Mftf/Data/DesignData.xml +++ b/app/code/Magento/Theme/Test/Mftf/Data/DesignData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="Layout" type="page_layout"> <data key="1column">1 column</data> </entity> diff --git a/app/code/Magento/Theme/Test/Mftf/Page/ThemesPage.xml b/app/code/Magento/Theme/Test/Mftf/Page/ThemesPage.xml index e0f0d2db70602..ab7b436cb3ae3 100644 --- a/app/code/Magento/Theme/Test/Mftf/Page/ThemesPage.xml +++ b/app/code/Magento/Theme/Test/Mftf/Page/ThemesPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="ThemesPageIndex" url="admin/system_design_theme/" area="admin" module="Magento_Theme"> <section name="AdminThemeSection"/> </page> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/AdminThemeSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/AdminThemeSection.xml index 67a5146fd8b3a..219ca7420361c 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/AdminThemeSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/AdminThemeSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminThemeSection"> <!--All rows in a specific Column e.g. {{Section.rowsInColumn('columnName')}}--> <element name="allRowsInColumn" type="text" selector="//tr/td[contains(@class, '{{column}}')]" parameterized="true"/> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/StorefrontFooterSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/StorefrontFooterSection.xml index a9db1cbb9c776..d9854b7a80b9b 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/StorefrontFooterSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/StorefrontFooterSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontFooterSection"> </section> </sections> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/StorefrontMessagesSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/StorefrontMessagesSection.xml index 9b0b9c7a053bc..a7bc36f1e629b 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/StorefrontMessagesSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/StorefrontMessagesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontMessagesSection"> <element name="message" type="text" selector="//main//div[contains(@class, 'messages')]//div[contains(@class, 'message')]/div[contains(text(), '{{var1}}')]" diff --git a/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml index 2ae24934a48df..5844c77ef9ade 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="ThemeTest"> <annotations> <features value="Theme Test"/> diff --git a/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection.xml b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection.xml index 83296abf61dc2..1730996937ca2 100644 --- a/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection.xml +++ b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="ProductWYSIWYGSection"> <element name="Tinymce3MSG" type="button" selector=".admin__field-error"/> </section> diff --git a/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml b/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml index 0806011e8ffbc..ed3eea30c8b45 100644 --- a/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml +++ b/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminSwitchWYSIWYGOptionsTest"> <annotations> <features value="Cms"/> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml index 127647e6e9b48..1942c02ace51b 100644 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Search grid with keyword search--> <actionGroup name="searchAdminDataGridByKeyword"> <arguments> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml index 9239f296aafad..9148c22976c19 100644 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="adminDataGridSelectPerPage"> <arguments> <argument name="perPage" type="string"/> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterSearchResultsActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterSearchResultsActionGroup.xml index 023e20b7025c1..73d441dd96d1e 100644 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterSearchResultsActionGroup.xml +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterSearchResultsActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminGridFilterSearchResultsByInput"> <arguments> <argument name="selector"/> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminSaveAndCloseActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminSaveAndCloseActionGroup.xml index 133b81a5d6044..9a9458ab34d2b 100644 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminSaveAndCloseActionGroup.xml +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminSaveAndCloseActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminFormSaveAndClose"> <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> <click selector="{{AdminProductFormActionSection.saveAndClose}}" stepKey="clickOnSaveAndClose"/> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridHeaderSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridHeaderSection.xml index e5766300b0e87..3e917a5944f95 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridHeaderSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridHeaderSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminDataGridHeaderSection"> <!--Search by keyword element--> <element name="search" type="input" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] input[placeholder='Search by keyword']"/> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml index ff4097aa76265..0f54f51549e7a 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminDataGridPaginationSection"> <element name="perPageDropdown" type="select" selector=".admin__data-grid-pager-wrap .selectmenu"/> <element name="perPageOption" type="button" selector="//div[@class='admin__data-grid-pager-wrap']//div[@class='selectmenu-items _active']//li//button[text()='{{var1}}']" parameterized="true"/> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml index ea0f7e64a8448..edcc70a82396d 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminDataGridTableSection"> <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)"/> <element name="columnHeader" type="button" selector="//div[@data-role='grid-wrapper']//table[contains(@class, 'data-grid')]/thead/tr/th[contains(@class, 'data-grid-th')]/span[text() = '{{label}}']" parameterized="true" timeout="30"/> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection.xml index 2bf65a682d21c..978a09db82b16 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <!-- TODO: Search, Notifications, Admin Menu --> <section name="AdminGridMainControls"> <element name="add" type="button" selector="#add" timeout="30"/> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml index d3e94eb24dfd2..cb1fdd716b174 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMessagesSection"> <element name="successMessage" type="text" selector=".message-success"/> <element name="errorMessage" type="text" selector=".message.message-error.error"/> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/ModalConfirmationSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/ModalConfirmationSection.xml index 8c3dd505f66be..35ec242f05c52 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/ModalConfirmationSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/ModalConfirmationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="ModalConfirmationSection"> <element name="CancelButton" type="button" selector="//footer[@class='modal-footer']/button[contains(@class, 'action-dismiss')]"/> <element name="OkButton" type="button" selector="//footer[@class='modal-footer']/button[contains(@class, 'action-accept')]"/> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml index e8aff30ff8d67..de887d2de6704 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCreateUserActionGroup"> <arguments> <argument name="role"/> diff --git a/app/code/Magento/User/Test/Mftf/Data/UserData.xml b/app/code/Magento/User/Test/Mftf/Data/UserData.xml index 93bf0ccfa43d6..03ae3dba21840 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="admin" type="user"> <data key="email">admin@magento.com</data> <data key="password">admin123</data> diff --git a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml index 39eea63356b7e..641b692adea5c 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="adminRole" type="role"> <data key="name" unique="suffix">adminRole</data> <data key="scope">1</data> diff --git a/app/code/Magento/User/Test/Mftf/Page/AdminEditRolePage.xml b/app/code/Magento/User/Test/Mftf/Page/AdminEditRolePage.xml index 8c71a815a3346..5b94553e398c5 100644 --- a/app/code/Magento/User/Test/Mftf/Page/AdminEditRolePage.xml +++ b/app/code/Magento/User/Test/Mftf/Page/AdminEditRolePage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminEditRolePage" url="admin/user_role/editrole" module="Magento_User" area="admin"> <section name="AdminEditRoleInfoSection"/> </page> diff --git a/app/code/Magento/User/Test/Mftf/Page/AdminEditUserPage.xml b/app/code/Magento/User/Test/Mftf/Page/AdminEditUserPage.xml index 7f6751c6f9e9b..ae965fa1c48e7 100644 --- a/app/code/Magento/User/Test/Mftf/Page/AdminEditUserPage.xml +++ b/app/code/Magento/User/Test/Mftf/Page/AdminEditUserPage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminEditUserPage" url="admin/user/new" area="admin" module="Magento_User"> <section name="AdminEditUserSection"/> <section name="AdminEditUserRoleSection"/> diff --git a/app/code/Magento/User/Test/Mftf/Page/AdminRolesPage.xml b/app/code/Magento/User/Test/Mftf/Page/AdminRolesPage.xml index 87cf0625670e0..e3b0c55f99cc1 100644 --- a/app/code/Magento/User/Test/Mftf/Page/AdminRolesPage.xml +++ b/app/code/Magento/User/Test/Mftf/Page/AdminRolesPage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminRolesPage" url="admin/user_role/" module="Magento_User" area="admin"> <section name="AdminRoleGridSection"/> </page> diff --git a/app/code/Magento/User/Test/Mftf/Page/AdminUsersPage.xml b/app/code/Magento/User/Test/Mftf/Page/AdminUsersPage.xml index 2075cba2bdccb..ceb05ec7bd9c8 100644 --- a/app/code/Magento/User/Test/Mftf/Page/AdminUsersPage.xml +++ b/app/code/Magento/User/Test/Mftf/Page/AdminUsersPage.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminUsersPage" url="admin/user/" area="admin" module="Magento_User"> <section name="AdminUserGridSection"/> </page> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminEditRoleInfoSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminEditRoleInfoSection.xml index feb7b3e3bba8b..e30a545649d12 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminEditRoleInfoSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminEditRoleInfoSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminEditRoleInfoSection"> <element name="roleName" type="input" selector="#role_name"/> <element name="password" type="input" selector="#current_password"/> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminEditUserRoleSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminEditUserRoleSection.xml index dc12205a84d9a..8f6f2352ff01b 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminEditUserRoleSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminEditUserRoleSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminEditUserRoleSection"> <element name="usernameTextField" type="input" selector="#user_username"/> <element name="roleNameFilterTextField" type="input" selector="#permissionsUserRolesGrid_filter_role_name"/> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminEditUserSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminEditUserSection.xml index 1406758b86118..5b866b45e2fbe 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminEditUserSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminEditUserSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminEditUserSection"> <element name="usernameTextField" type="input" selector="#user_username"/> <element name="firstNameTextField" type="input" selector="#user_firstname"/> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml index 1e1f3457995fe..6db6858500342 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminRoleGridSection"> <element name="idFilterTextField" type="input" selector="#roleGrid_filter_role_id"/> <element name="roleNameFilterTextField" type="input" selector="#roleGrid_filter_role_name"/> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml index b6d2645ac7384..f429c390efe6b 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminUserGridSection"> <element name="usernameFilterTextField" type="input" selector="#permissionsUserGrid_filter_username"/> <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]"/> diff --git a/app/code/Magento/Variable/Test/Mftf/ActionGroup/CreateCustomVariableActionGroup.xml b/app/code/Magento/Variable/Test/Mftf/ActionGroup/CreateCustomVariableActionGroup.xml index 0df617c876d8e..610676b350455 100644 --- a/app/code/Magento/Variable/Test/Mftf/ActionGroup/CreateCustomVariableActionGroup.xml +++ b/app/code/Magento/Variable/Test/Mftf/ActionGroup/CreateCustomVariableActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CreateCustomVariableActionGroup"> <amOnPage url="admin/admin/system_variable/new/" stepKey="goToNewCustomVarialePage" /> <waitForPageLoad stepKey="waitForPageLoad" /> diff --git a/app/code/Magento/Variable/Test/Mftf/Data/VariableData.xml b/app/code/Magento/Variable/Test/Mftf/Data/VariableData.xml index 9038030b30cbf..7b7fd768f0ab1 100644 --- a/app/code/Magento/Variable/Test/Mftf/Data/VariableData.xml +++ b/app/code/Magento/Variable/Test/Mftf/Data/VariableData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultVariable" type="cms_page"> <data key="city"> Austin </data> </entity> diff --git a/app/code/Magento/Weee/Test/Mftf/ActionGroup/AdminProductAddFPTValueActionGroup.xml b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AdminProductAddFPTValueActionGroup.xml index c41eb7c02a557..39deec3d81bac 100644 --- a/app/code/Magento/Weee/Test/Mftf/ActionGroup/AdminProductAddFPTValueActionGroup.xml +++ b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AdminProductAddFPTValueActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Navigate to create product page from product grid page--> <actionGroup name="AdminProductAddFPTValueActionGroup"> <arguments> diff --git a/app/code/Magento/Weee/Test/Mftf/Data/FixedProductAttributeData.xml b/app/code/Magento/Weee/Test/Mftf/Data/FixedProductAttributeData.xml index e981dae483f32..b8b45d84242c9 100644 --- a/app/code/Magento/Weee/Test/Mftf/Data/FixedProductAttributeData.xml +++ b/app/code/Magento/Weee/Test/Mftf/Data/FixedProductAttributeData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="productFPTAttribute" type="ProductAttribute"> <data key="attribute_code" unique="suffix">attribute</data> <data key="is_unique">true</data> diff --git a/app/code/Magento/Weee/Test/Mftf/Data/WeeeConfigData.xml b/app/code/Magento/Weee/Test/Mftf/Data/WeeeConfigData.xml index 120dd10eee359..e44c1bb51e41b 100644 --- a/app/code/Magento/Weee/Test/Mftf/Data/WeeeConfigData.xml +++ b/app/code/Magento/Weee/Test/Mftf/Data/WeeeConfigData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <!-- Fixed Product Taxes Enable--> <entity name="WeeeConfigEnable" type="weee_config"> <requiredEntity type="enableFPT">EnableFPT</requiredEntity> diff --git a/app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml b/app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml index 2e2b71c30ef47..56153067658d2 100644 --- a/app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml +++ b/app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml @@ -6,7 +6,7 @@ */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="WeeeConfigEnable" dataType="weee_config" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> <object key="groups" dataType="weee_config"> <object key="weee" dataType="weee_config"> diff --git a/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml b/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml index fa3663ee719e1..793b763f0fc15 100644 --- a/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml +++ b/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminProductEditPage" url="catalog/product/edit/id/{{product_id}}/" area="admin" module="Magento_Catalog"> <section name="AdminProductAddFPTValueSection"/> </page> diff --git a/app/code/Magento/Weee/Test/Mftf/Section/AdminProductAddFPTValueSection.xml b/app/code/Magento/Weee/Test/Mftf/Section/AdminProductAddFPTValueSection.xml index 40a9a97f31425..eee3f421910e1 100644 --- a/app/code/Magento/Weee/Test/Mftf/Section/AdminProductAddFPTValueSection.xml +++ b/app/code/Magento/Weee/Test/Mftf/Section/AdminProductAddFPTValueSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductAddFPTValueSection"> <element name="addFPT" type="button" selector="[data-index='{{FPTAttributeCode}}'] [data-action='add_new_row']" parameterized="true"/> <element name="selectCountryForFPT" type="select" selector="(//select[contains(@name, 'product[{{FPTAttributeCode}}]') and contains(@name, '[country]')])[last()]" parameterized="true"/> diff --git a/app/code/Magento/Weee/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Weee/Test/Mftf/Section/CheckoutCartSummarySection.xml index 9b6541b93541d..2f8ed312f15cf 100644 --- a/app/code/Magento/Weee/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Weee/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutCartSummarySection"> <element name="amountFPT" type="text" selector=".totals td[data-th='FPT'] .price"/> </section> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml index edd1af41964a2..fff1f44c17c45 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Add Product to wishlist from the category page and check message --> <actionGroup name="StorefrontCustomerAddCategoryProductToWishlistActionGroup"> <arguments> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml b/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml index 93fb9a5689e17..811871bf685ae 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml @@ -6,7 +6,7 @@ */ --> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="Wishlist" type="wishlist"> <data key="id">null</data> <var key="product" entityType="product" entityKey="id"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Metadata/wishlist-meta.xml b/app/code/Magento/Wishlist/Test/Mftf/Metadata/wishlist-meta.xml index 37f2bbe6a29d5..423367c9a3b9d 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Metadata/wishlist-meta.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Metadata/wishlist-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateWishlist" dataType="wishlist" type="create" auth="customerFormKey" url="/wishlist/index/add/" method="POST" successRegex="" returnRegex="~\/wishlist_id\/(\d*?)\/~" > <contentType>application/x-www-form-urlencoded</contentType> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml b/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml index cf2db7efab6c6..986d1e59ad066 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCustomerWishlistPage" url="/wishlist/" area="storefront" module="Magento_Wishlist"> <section name="StorefrontCustomerWishlistSection" /> </page> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml index 20d72a0704699..07f8b91661d2e 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategoryProductSection"> <element name="ProductAddToWishlistByNumber" type="text" selector="//main//li[{{var1}}]//a[contains(@class, 'towishlist')]" parameterized="true"/> <element name="ProductAddToWishlistByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//a[contains(@class, 'towishlist')]" parameterized="true"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml index 8115e591aa9f1..4bc4b3f1b9274 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCustomerWishlistProductSection"> <element name="ProductTitleByName" type="button" selector="//main//li//a[contains(text(), '{{var1}}')]" parameterized="true"/> <element name="ProductPriceByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//span[@class='price']" parameterized="true"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSection.xml index 747ad958adbe0..c208bfc41dcdc 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCustomerWishlistSection"> <element name="pageTitle" type="text" selector="h1.page-title"/> <element name="successMsg" type="text" selector="div.message-success.success.message"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSidebarSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSidebarSection.xml index dfff8b91e895b..ba226837c5fe7 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSidebarSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSidebarSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCustomerWishlistSidebarSection"> <element name="ProductTitleByName" type="button" selector="//main//ol[@id='wishlist-sidebar']//a[@class='product-item-link']/span[text()='{{var1}}']" parameterized="true"/> <element name="ProductPriceByName" type="text" selector="//main//ol[@id='wishlist-sidebar']//a[@class='product-item-link']/span[text()='{{var1}}']//ancestor::ol//span[@class='price']" parameterized="true"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index ea2dfcbedaa27..e77c489074069 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoMainSection"> <element name="productAddToWishlist" type="button" selector="a.action.towishlist"/> </section> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml index 95beae991384f..42d4203999a44 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml @@ -6,7 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="ConfigurableProductChildImageShouldBeShownOnWishListTest"> <annotations> <features value="Wishlist"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 194737788eb7c..7eb42d1fbfed9 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CLoggedInUserTest"> <!-- Step 5: Add products to wishlist --> <comment userInput="Start of adding products to wishlist" stepKey="startOfAddingProductsToWishlist" after="endOfComparingProducts" /> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml index 0a4d241b0e4ab..0c7f5fb4963cf 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml @@ -6,7 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontAddMultipleStoreProductsToWishlistTest"> <annotations> <features value="Wishlist"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml index b20ba3a153fc5..a542c9d552324 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml @@ -6,7 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontAddProductsToCartFromWishlistUsingSidebarTest"> <annotations> <title value="Add products from the wishlist to the cart using the sidebar."/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml index cbed63db36d49..01de5f39527b0 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml @@ -6,7 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontDeletePersistedWishlistTest"> <annotations> <features value="Wishlist"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml index f6deb803967ba..4aec6e4703e98 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml @@ -6,7 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontRemoveProductsFromWishlistUsingSidebarTest"> <annotations> <title value="Remove products from the wishlist using the sidebar."/> From 8b19892237baf4face8ceafc88562c411d347752 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi <volodymyr@hryvinskyi.com> Date: Tue, 24 Jul 2018 15:38:06 +0300 Subject: [PATCH 0780/1171] Update CMS Page Index --- .../Magento/Cms/Controller/Index/Index.php | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Index/Index.php b/app/code/Magento/Cms/Controller/Index/Index.php index 8e20feb0f058f..3326749727c0f 100644 --- a/app/code/Magento/Cms/Controller/Index/Index.php +++ b/app/code/Magento/Cms/Controller/Index/Index.php @@ -1,11 +1,20 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Cms\Controller\Index; +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Controller\Result\Forward; +use Magento\Framework\Controller\Result\ForwardFactory; +use Magento\Framework\View\Result\Page as ResultPage; +use Magento\Cms\Helper\Page; +use Magento\Store\Model\ScopeInterface; + class Index extends \Magento\Framework\App\Action\Action { /** @@ -14,14 +23,30 @@ class Index extends \Magento\Framework\App\Action\Action protected $resultForwardFactory; /** - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + protected $scopeConfig; + + /** + * @var Page + */ + protected $page; + + /** + * @param Context $context + * @param ScopeConfigInterface $scopeConfig + * @param ForwardFactory $resultForwardFactory + * @param Page $page */ public function __construct( - \Magento\Framework\App\Action\Context $context, - \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory + Context $context, + ScopeConfigInterface $scopeConfig, + ForwardFactory $resultForwardFactory, + Page $page ) { + $this->scopeConfig = $scopeConfig; $this->resultForwardFactory = $resultForwardFactory; + $this->page = $page; parent::__construct($context); } @@ -29,18 +54,15 @@ public function __construct( * Renders CMS Home page * * @param string|null $coreRoute - * @return \Magento\Framework\Controller\Result\Forward + * + * @return bool|ResponseInterface|Forward|ResultInterface|ResultPage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function execute($coreRoute = null) { - $pageId = $this->_objectManager->get( - \Magento\Framework\App\Config\ScopeConfigInterface::class - )->getValue( - \Magento\Cms\Helper\Page::XML_PATH_HOME_PAGE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - $resultPage = $this->_objectManager->get(\Magento\Cms\Helper\Page::class)->prepareResultPage($this, $pageId); + $pageId = $this->scopeConfig->getValue(Page::XML_PATH_HOME_PAGE, ScopeInterface::SCOPE_STORE); + $resultPage = $this->page->prepareResultPage($this, $pageId); if (!$resultPage) { /** @var \Magento\Framework\Controller\Result\Forward $resultForward */ $resultForward = $this->resultForwardFactory->create(); From 08bbf9acaa7688c621f512f5ac93a97e58d83597 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi <volodymyr@hryvinskyi.com> Date: Wed, 25 Jul 2018 12:03:39 +0300 Subject: [PATCH 0781/1171] Update Backward Compatible and change protected to private properties --- .../Magento/Cms/Controller/Index/Index.php | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Index/Index.php b/app/code/Magento/Cms/Controller/Index/Index.php index 3326749727c0f..236023373a735 100644 --- a/app/code/Magento/Cms/Controller/Index/Index.php +++ b/app/code/Magento/Cms/Controller/Index/Index.php @@ -18,35 +18,37 @@ class Index extends \Magento\Framework\App\Action\Action { /** - * @var \Magento\Framework\Controller\Result\ForwardFactory + * @var ForwardFactory */ - protected $resultForwardFactory; + private $resultForwardFactory; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ - protected $scopeConfig; + private $scopeConfig; /** * @var Page */ - protected $page; + private $page; /** + * Index constructor. + * * @param Context $context - * @param ScopeConfigInterface $scopeConfig * @param ForwardFactory $resultForwardFactory - * @param Page $page + * @param ScopeConfigInterface|null $scopeConfig + * @param Page|null $page */ public function __construct( Context $context, - ScopeConfigInterface $scopeConfig, ForwardFactory $resultForwardFactory, - Page $page + ScopeConfigInterface $scopeConfig = null, + Page $page = null ) { - $this->scopeConfig = $scopeConfig; $this->resultForwardFactory = $resultForwardFactory; - $this->page = $page; + $this->scopeConfig = $scopeConfig ? : $this->_objectManager->get(ScopeConfigInterface::class); + $this->page = $page ? : $this->_objectManager->get(Page::class); parent::__construct($context); } @@ -64,7 +66,7 @@ public function execute($coreRoute = null) $pageId = $this->scopeConfig->getValue(Page::XML_PATH_HOME_PAGE, ScopeInterface::SCOPE_STORE); $resultPage = $this->page->prepareResultPage($this, $pageId); if (!$resultPage) { - /** @var \Magento\Framework\Controller\Result\Forward $resultForward */ + /** @var Forward $resultForward */ $resultForward = $this->resultForwardFactory->create(); $resultForward->forward('defaultIndex'); return $resultForward; From d45965306fa150bfc0d7a5d25833709df1df34e8 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi <volodymyr@hryvinskyi.com> Date: Wed, 25 Jul 2018 13:42:46 +0300 Subject: [PATCH 0782/1171] Fix Object Manager --- app/code/Magento/Cms/Controller/Index/Index.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Index/Index.php b/app/code/Magento/Cms/Controller/Index/Index.php index 236023373a735..3197bac650e96 100644 --- a/app/code/Magento/Cms/Controller/Index/Index.php +++ b/app/code/Magento/Cms/Controller/Index/Index.php @@ -7,6 +7,7 @@ use Magento\Framework\App\Action\Context; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Controller\Result\Forward; @@ -47,8 +48,8 @@ public function __construct( Page $page = null ) { $this->resultForwardFactory = $resultForwardFactory; - $this->scopeConfig = $scopeConfig ? : $this->_objectManager->get(ScopeConfigInterface::class); - $this->page = $page ? : $this->_objectManager->get(Page::class); + $this->scopeConfig = $scopeConfig ? : ObjectManager::getInstance()->get(ScopeConfigInterface::class); + $this->page = $page ? : ObjectManager::getInstance()->get(Page::class); parent::__construct($context); } From ac62ab087c2566d024710584e25bec86b0504b57 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi <volodymyr@hryvinskyi.com> Date: Thu, 26 Jul 2018 14:22:47 +0300 Subject: [PATCH 0783/1171] Update CMS IndexTest --- app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php index 8ff206e8a80fc..06c7a03d57d1d 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php @@ -84,7 +84,9 @@ protected function setUp() 'response' => $responseMock, 'objectManager' => $objectManagerMock, 'request' => $this->requestMock, - 'resultForwardFactory' => $this->forwardFactoryMock + 'resultForwardFactory' => $this->forwardFactoryMock, + 'scopeConfig' => $scopeConfigMock, + 'page' => $this->cmsHelperMock ] ); } From d941639e78faf14abc81cf2fd34ffad212089f63 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi <volodymyr@hryvinskyi.com> Date: Thu, 26 Jul 2018 14:23:50 +0300 Subject: [PATCH 0784/1171] Update tab to space --- app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php index 06c7a03d57d1d..d8453bc6ecbce 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php @@ -85,8 +85,8 @@ protected function setUp() 'objectManager' => $objectManagerMock, 'request' => $this->requestMock, 'resultForwardFactory' => $this->forwardFactoryMock, - 'scopeConfig' => $scopeConfigMock, - 'page' => $this->cmsHelperMock + 'scopeConfig' => $scopeConfigMock, + 'page' => $this->cmsHelperMock ] ); } From 89bf5e8e1c5dce197f6d8a0017af274d660bfa3a Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Sun, 5 Aug 2018 13:49:03 +0300 Subject: [PATCH 0785/1171] Fixed breaking incompatible change --- app/code/Magento/Cms/Controller/Index/Index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Controller/Index/Index.php b/app/code/Magento/Cms/Controller/Index/Index.php index 3197bac650e96..c027bd1a2b717 100644 --- a/app/code/Magento/Cms/Controller/Index/Index.php +++ b/app/code/Magento/Cms/Controller/Index/Index.php @@ -21,7 +21,7 @@ class Index extends \Magento\Framework\App\Action\Action /** * @var ForwardFactory */ - private $resultForwardFactory; + protected $resultForwardFactory; /** * @var ScopeConfigInterface From 3cf4d8d66b04ee8dcd6a750d31255a910db0f190 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@magento.com> Date: Tue, 7 Aug 2018 09:21:54 -0500 Subject: [PATCH 0786/1171] MAGETWO-91314: Validate method arguments annotations - added annotation sniff and made PHPCS run on diff --- .../Annotation/MethodArgumentsSniff.php | 587 ++++++++++++++++++ .../static/framework/Magento/ruleset.xml | 3 + dev/tests/static/phpunit-all.xml.dist | 2 +- dev/tests/static/phpunit.xml | 30 + dev/tests/static/phpunit.xml.dist | 2 +- 5 files changed, 622 insertions(+), 2 deletions(-) create mode 100644 dev/tests/static/framework/Magento/Sniffs/Annotation/MethodArgumentsSniff.php create mode 100644 dev/tests/static/phpunit.xml diff --git a/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodArgumentsSniff.php b/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodArgumentsSniff.php new file mode 100644 index 0000000000000..879334d8c553b --- /dev/null +++ b/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodArgumentsSniff.php @@ -0,0 +1,587 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Sniffs\Annotation; + +use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Files\File; + +/** + * Sniff to validate method arguments annotations + */ +class MethodArgumentsSniff implements Sniff +{ + /** + * @var array + */ + private $validTokensBeforeClosingCommentTag = [ + 'T_WHITESPACE', + 'T_PUBLIC', + 'T_PRIVATE', + 'T_PROTECTED', + 'T_STATIC', + 'T_ABSTRACT', + 'T_FINAL' + ]; + + /** + * @var array + */ + private $invalidTypes = [ + 'null', + 'false', + 'true', + 'self' + ]; + + /** + * @inheritdoc + */ + public function register() : array + { + return [ + T_FUNCTION + ]; + } + + /** + * Validates whether valid token exists before closing comment tag + * + * @param string $type + * @return bool + */ + private function isTokenBeforeClosingCommentTagValid(string $type) : bool + { + return in_array($type, $this->validTokensBeforeClosingCommentTag); + } + + /** + * Validates whether comment block exists + * + * @param File $phpcsFile + * @param int $previousCommentClosePtr + * @param int $stackPtr + * @return bool + */ + private function validateCommentBlockExists(File $phpcsFile, int $previousCommentClosePtr, int $stackPtr) : bool + { + $tokens = $phpcsFile->getTokens(); + for ($tempPtr = $previousCommentClosePtr + 1; $tempPtr < $stackPtr; $tempPtr++) { + if (!$this->isTokenBeforeClosingCommentTagValid($tokens[$tempPtr]['type'])) { + return false; + } + } + return true; + } + + /** + * Checks whether the parameter type is invalid + * + * @param string $type + * @return bool + */ + private function isInvalidType(string $type) : bool + { + return in_array(strtolower($type), $this->invalidTypes); + } + + /** + * Get arguments from method signature + * + * @param File $phpcsFile + * @param int $openParenthesisPtr + * @param int $closedParenthesisPtr + * @return array + */ + private function getMethodArguments(File $phpcsFile, int $openParenthesisPtr, int $closedParenthesisPtr) : array + { + $tokens = $phpcsFile->getTokens(); + $methodArguments = []; + for ($i = $openParenthesisPtr; $i < $closedParenthesisPtr; $i++) { + $argumentsPtr = $phpcsFile->findNext(T_VARIABLE, $i + 1, $closedParenthesisPtr); + if ($argumentsPtr === false) { + break; + } elseif ($argumentsPtr < $closedParenthesisPtr) { + $arguments = $tokens[$argumentsPtr]['content']; + $methodArguments[] = $arguments; + $i = $argumentsPtr - 1; + } + } + return $methodArguments; + } + + /** + * Get parameters from method annotation + * + * @param array $paramDefinitions + * @return array + */ + private function getMethodParameters(array $paramDefinitions) : array + { + $paramName = []; + for ($i = 0; $i < count($paramDefinitions); $i++) { + if (isset($paramDefinitions[$i]['paramName'])) { + $paramName[] = $paramDefinitions[$i]['paramName']; + } + } + return $paramName; + } + + /** + * Validates whether @inheritdoc without braces [@inheritdoc] exists or not + * + * @param File $phpcsFile + * @param int $previousCommentOpenPtr + * @param int $previousCommentClosePtr + */ + private function validateInheritdocAnnotationWithoutBracesExists( + File $phpcsFile, + int $previousCommentOpenPtr, + int $previousCommentClosePtr + ) : bool { + return $this->validateInheritdocAnnotationExists( + $phpcsFile, + $previousCommentOpenPtr, + $previousCommentClosePtr, + '@inheritdoc' + ); + } + + /** + * Validates whether @inheritdoc with braces [{@inheritdoc}] exists or not + * + * @param File $phpcsFile + * @param int $previousCommentOpenPtr + * @param int $previousCommentClosePtr + */ + private function validateInheritdocAnnotationWithBracesExists( + File $phpcsFile, + int $previousCommentOpenPtr, + int $previousCommentClosePtr + ) : bool { + return $this->validateInheritdocAnnotationExists( + $phpcsFile, + $previousCommentOpenPtr, + $previousCommentClosePtr, + '{@inheritdoc}' + ); + } + + /** + * Validates inheritdoc annotation exists + * + * @param File $phpcsFile + * @param int $previousCommentOpenPtr + * @param int $previousCommentClosePtr + * @param string $inheritdocAnnotation + * @return bool + */ + private function validateInheritdocAnnotationExists( + File $phpcsFile, + int $previousCommentOpenPtr, + int $previousCommentClosePtr, + string $inheritdocAnnotation + ) : bool { + $tokens = $phpcsFile->getTokens(); + for ($ptr = $previousCommentOpenPtr; $ptr < $previousCommentClosePtr; $ptr++) { + if (strtolower($tokens[$ptr]['content']) === $inheritdocAnnotation) { + return true; + } + } + return false; + } + + /** + * Validates if annotation exists for parameter in method annotation + * + * @param File $phpcsFile + * @param int $argumentsCount + * @param int $parametersCount + * @param int $previousCommentOpenPtr + * @param int $previousCommentClosePtr + * @param int $stackPtr + */ + private function validateParameterAnnotationForArgumentExists( + File $phpcsFile, + int $argumentsCount, + int $parametersCount, + int $previousCommentOpenPtr, + int $previousCommentClosePtr, + int $stackPtr + ) : void { + if ($argumentsCount > 0 && $parametersCount === 0) { + $inheritdocAnnotationWithoutBracesExists = $this->validateInheritdocAnnotationWithoutBracesExists( + $phpcsFile, + $previousCommentOpenPtr, + $previousCommentClosePtr + ); + $inheritdocAnnotationWithBracesExists = $this->validateInheritdocAnnotationWithBracesExists( + $phpcsFile, + $previousCommentOpenPtr, + $previousCommentClosePtr + ); + if ($inheritdocAnnotationWithBracesExists) { + $phpcsFile->addFixableError( + '{@inheritdoc} does not import parameter annotation', + $stackPtr, + 'MethodArguments' + ); + } elseif ($this->validateCommentBlockExists($phpcsFile, $previousCommentClosePtr, $stackPtr) + && !$inheritdocAnnotationWithoutBracesExists + ) { + $phpcsFile->addFixableError( + 'Missing @param for argument in method annotation', + $stackPtr, + 'MethodArguments' + ); + } + } + } + + /** + * Validates whether comment block have extra the parameters listed in method annotation + * + * @param File $phpcsFile + * @param int $argumentsCount + * @param int $parametersCount + * @param int $stackPtr + */ + private function validateCommentBlockDoesnotHaveExtraParameterAnnotation( + File $phpcsFile, + int $argumentsCount, + int $parametersCount, + int $stackPtr + ) : void { + if ($argumentsCount < $parametersCount && $argumentsCount > 0) { + $phpcsFile->addFixableError( + 'Extra @param found in method annotation', + $stackPtr, + 'MethodArguments' + ); + } elseif ($argumentsCount > 0 && $argumentsCount != $parametersCount && $parametersCount != 0) { + $phpcsFile->addFixableError( + '@param is not found for one or more params in method annotation', + $stackPtr, + 'MethodArguments' + ); + } + } + + /** + * Validates whether the argument name exists in method parameter annotation + * + * @param int $stackPtr + * @param int $ptr + * @param File $phpcsFile + * @param array $methodArguments + * @param array $paramDefinitions + */ + private function validateArgumentNameInParameterAnnotationExists( + int $stackPtr, + int $ptr, + File $phpcsFile, + array $methodArguments, + array $paramDefinitions + ) : void { + $parameterNames = $this->getMethodParameters($paramDefinitions); + if (!in_array($methodArguments[$ptr], $parameterNames)) { + $error = $methodArguments[$ptr]. ' parameter is missing in method annotation'; + $phpcsFile->addFixableError($error, $stackPtr, 'MethodArguments'); + } + } + + /** + * Validates whether parameter present in method signature + * + * @param int $ptr + * @param int $paramDefinitionsArguments + * @param array $methodArguments + * @param File $phpcsFile + * @param array $paramPointers + */ + private function validateParameterPresentInMethodSignature( + int $ptr, + string $paramDefinitionsArguments, + array $methodArguments, + File $phpcsFile, + array $paramPointers + ) : void { + if (!in_array($paramDefinitionsArguments, $methodArguments)) { + $phpcsFile->addFixableError( + $paramDefinitionsArguments . ' parameter is missing in method arguments signature', + $paramPointers[$ptr], + 'MethodArguments' + ); + } + } + + /** + * Validates whether the parameters are in order or not in method annotation + * + * @param array $paramDefinitions + * @param array $methodArguments + * @param File $phpcsFile + * @param array $paramPointers + */ + private function validateParameterOrderIsCorrect( + array $paramDefinitions, + array $methodArguments, + File $phpcsFile, + array $paramPointers + ) : void { + $parameterNames = $this->getMethodParameters($paramDefinitions); + $paramDefinitionsCount = count($paramDefinitions); + for ($ptr = 0; $ptr < $paramDefinitionsCount; $ptr++) { + if (isset($methodArguments[$ptr]) && isset($parameterNames[$ptr]) + && in_array($methodArguments[$ptr], $parameterNames) + ) { + if ($methodArguments[$ptr] != $parameterNames[$ptr]) { + $phpcsFile->addFixableError( + $methodArguments[$ptr].' parameter is not in order', + $paramPointers[$ptr], + 'MethodArguments' + ); + } + } + } + } + + /** + * Validate whether duplicate annotation present in method annotation + * + * @param int $stackPtr + * @param array $paramDefinitions + * @param array $paramPointers + * @param File $phpcsFile + * @param array $methodArguments + */ + private function validateDuplicateAnnotationDoesnotExists( + int $stackPtr, + array $paramDefinitions, + array $paramPointers, + File $phpcsFile, + array $methodArguments + ) : void { + $argumentsCount = count($methodArguments); + $parametersCount = count($paramPointers); + if ($argumentsCount <= $parametersCount && $argumentsCount > 0) { + $duplicateParameters = []; + for ($i = 0; $i < sizeof($paramDefinitions); $i++) { + if (isset($paramDefinitions[$i]['paramName'])) { + $parameterContent = $paramDefinitions[$i]['paramName']; + for ($j = $i + 1; $j < count($paramDefinitions); $j++) { + if (isset($paramDefinitions[$j]['paramName']) + && $parameterContent === $paramDefinitions[$j]['paramName'] + ) { + $duplicateParameters[] = $parameterContent; + } + } + } + } + foreach ($duplicateParameters as $value) { + $phpcsFile->addFixableError( + $value . ' duplicate found in method annotation', + $stackPtr, + 'MethodArguments' + ); + } + } + } + + /** + * Validate parameter annotation format is correct or not + * + * @param int $ptr + * @param File $phpcsFile + * @param array $methodArguments + * @param array $paramDefinitions + * @param array $paramPointers + */ + private function validateParameterAnnotationFormatIsCorrect( + int $ptr, + File $phpcsFile, + array $methodArguments, + array $paramDefinitions, + array $paramPointers + ) : void { + switch (count($paramDefinitions)) { + case 0: + $phpcsFile->addFixableError( + 'Missing both type and parameter', + $paramPointers[$ptr], + 'MethodArguments' + ); + break; + case 1: + if (preg_match('/^\$.*/', $paramDefinitions[0])) { + $phpcsFile->addError( + 'Type is not specified', + $paramPointers[$ptr], + 'MethodArguments' + ); + } + break; + case 2: + if ($this->isInvalidType($paramDefinitions[0])) { + $phpcsFile->addFixableError( + $paramDefinitions[0].' is not a valid PHP type', + $paramPointers[$ptr], + 'MethodArguments' + ); + } + $this->validateParameterPresentInMethodSignature( + $ptr, + ltrim($paramDefinitions[1], '&'), + $methodArguments, + $phpcsFile, + $paramPointers + ); + break; + default: + if (preg_match('/^\$.*/', $paramDefinitions[0])) { + $phpcsFile->addError( + 'Type is not specified', + $paramPointers[$ptr], + 'MethodArguments' + ); + if ($this->isInvalidType($paramDefinitions[0])) { + $phpcsFile->addFixableError( + $paramDefinitions[0].' is not a valid PHP type', + $paramPointers[$ptr], + 'MethodArguments' + ); + } + } + break; + } + } + + /** + * Validate method parameter annotations + * + * @param int $stackPtr + * @param array $paramDefinitions + * @param array $paramPointers + * @param File $phpcsFile + * @param array $methodArguments + * @param int $previousCommentOpenPtr + * @param int $previousCommentClosePtr + */ + private function validateMethodParameterAnnotations( + int $stackPtr, + array $paramDefinitions, + array $paramPointers, + File $phpcsFile, + array $methodArguments, + int $previousCommentOpenPtr, + int $previousCommentClosePtr + ) : void { + $argumentCount = count($methodArguments); + $paramCount = count($paramPointers); + $this->validateParameterAnnotationForArgumentExists( + $phpcsFile, + $argumentCount, + $paramCount, + $previousCommentOpenPtr, + $previousCommentClosePtr, + $stackPtr + ); + $this->validateCommentBlockDoesnotHaveExtraParameterAnnotation( + $phpcsFile, + $argumentCount, + $paramCount, + $stackPtr + ); + $this->validateDuplicateAnnotationDoesnotExists( + $stackPtr, + $paramDefinitions, + $paramPointers, + $phpcsFile, + $methodArguments + ); + $this->validateParameterOrderIsCorrect( + $paramDefinitions, + $methodArguments, + $phpcsFile, + $paramPointers + ); + for ($ptr = 0; $ptr < count($methodArguments); $ptr++) { + $tokens = $phpcsFile->getTokens(); + if (isset($paramPointers[$ptr])) { + $this->validateArgumentNameInParameterAnnotationExists( + $stackPtr, + $ptr, + $phpcsFile, + $methodArguments, + $paramDefinitions + ); + $paramContent = $tokens[$paramPointers[$ptr]+2]['content']; + $paramContentExplode = explode(' ', $paramContent); + $this->validateParameterAnnotationFormatIsCorrect( + $ptr, + $phpcsFile, + $methodArguments, + $paramContentExplode, + $paramPointers + ); + } + } + } + + /** + * @inheritdoc + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $numTokens = count($tokens); + $previousCommentOpenPtr = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr-1, 0); + $previousCommentClosePtr = $phpcsFile->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $stackPtr-1, 0); + if (!$this->validateCommentBlockExists($phpcsFile, $previousCommentClosePtr, $stackPtr)) { + $phpcsFile->addError('Comment block is missing', $stackPtr, 'MethodArguments'); + return; + } + $openParenthesisPtr = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr+1, $numTokens); + $closedParenthesisPtr = $phpcsFile->findNext(T_CLOSE_PARENTHESIS, $stackPtr+1, $numTokens); + $methodArguments = $this->getMethodArguments($phpcsFile, $openParenthesisPtr, $closedParenthesisPtr); + $paramPointers = $paramDefinitions = []; + for ($tempPtr = $previousCommentOpenPtr; $tempPtr < $previousCommentClosePtr; $tempPtr++) { + if (strtolower($tokens[$tempPtr]['content']) === '@param') { + $paramPointers[] = $tempPtr; + $paramAnnotationParts = explode(' ', $tokens[$tempPtr+2]['content']); + if (count($paramAnnotationParts) === 1) { + if ((preg_match('/^\$.*/', $paramAnnotationParts[0]))) { + $paramDefinitions[] = [ + 'type' => null, + 'paramName' => rtrim(ltrim($tokens[$tempPtr+2]['content'], '&'), ',') + ]; + } else { + $paramDefinitions[] = [ + 'type' => $tokens[$tempPtr+2]['content'], + 'paramName' => null + ]; + } + } else { + $paramDefinitions[] = [ + 'type' => $paramAnnotationParts[0], + 'paramName' => rtrim(ltrim($paramAnnotationParts[1], '&'), ',') + ]; + } + } + } + $this->validateMethodParameterAnnotations( + $stackPtr, + $paramDefinitions, + $paramPointers, + $phpcsFile, + $methodArguments, + $previousCommentOpenPtr, + $previousCommentClosePtr + ); + } +} diff --git a/dev/tests/static/framework/Magento/ruleset.xml b/dev/tests/static/framework/Magento/ruleset.xml index 14d05b466b407..f01715618223b 100644 --- a/dev/tests/static/framework/Magento/ruleset.xml +++ b/dev/tests/static/framework/Magento/ruleset.xml @@ -22,6 +22,9 @@ <rule ref="Magento.LiteralNamespaces.LiteralNamespaces"> <exclude-pattern>*/_files/*</exclude-pattern> </rule> + <rule ref="Magento.Annotation.MethodArguments"> + <exclude-pattern>*/_files/*</exclude-pattern> + </rule> <rule ref="Magento.Functions.OutputBuffering"> <include-pattern>*/(app/code|vendor|setup/src|lib/internal/Magento)/*</include-pattern> <exclude-pattern>*/lib/internal/Magento/Framework/Image/Adapter/Gd2.php</exclude-pattern> diff --git a/dev/tests/static/phpunit-all.xml.dist b/dev/tests/static/phpunit-all.xml.dist index a0d1f2fe75dc9..3020889e6066b 100644 --- a/dev/tests/static/phpunit-all.xml.dist +++ b/dev/tests/static/phpunit-all.xml.dist @@ -21,6 +21,6 @@ <php> <ini name="date.timezone" value="America/Los_Angeles"/> <!-- TESTCODESTYLE_IS_FULL_SCAN - specify if full scan should be performed for test code style test --> - <const name="TESTCODESTYLE_IS_FULL_SCAN" value="1"/> + <const name="TESTCODESTYLE_IS_FULL_SCAN" value="0"/> </php> </phpunit> diff --git a/dev/tests/static/phpunit.xml b/dev/tests/static/phpunit.xml new file mode 100644 index 0000000000000..c7754e7637174 --- /dev/null +++ b/dev/tests/static/phpunit.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Default test suites declaration: run verification of coding standards and code integrity test suites + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.2/phpunit.xsd" + colors="true" + beStrictAboutTestsThatDoNotTestAnything="false" + bootstrap="./framework/bootstrap.php" +> + <testsuites> + <testsuite name="PHP Coding Standard Verification"> + <file>testsuite/Magento/Test/Php/LiveCodeTest.php</file> + </testsuite> + <testsuite name="Xss Unsafe Output Test"> + <file>testsuite/Magento/Test/Php/XssPhtmlTemplateTest.php</file> + </testsuite>--> + </testsuites> + <php> + <ini name="date.timezone" value="America/Los_Angeles"/> + <const name="TESTS_COMPOSER_PATH" value="/usr/local/bin/composer"/> + <!-- TESTCODESTYLE_IS_FULL_SCAN - specify if full scan should be performed for test code style test --> + <const name="TESTCODESTYLE_IS_FULL_SCAN" value="0"/> + </php> +</phpunit> diff --git a/dev/tests/static/phpunit.xml.dist b/dev/tests/static/phpunit.xml.dist index d9f9dbdfe54cd..ec581ec992f00 100644 --- a/dev/tests/static/phpunit.xml.dist +++ b/dev/tests/static/phpunit.xml.dist @@ -31,7 +31,7 @@ <php> <ini name="date.timezone" value="America/Los_Angeles"/> <!-- TESTCODESTYLE_IS_FULL_SCAN - specify if full scan should be performed for test code style test --> - <const name="TESTCODESTYLE_IS_FULL_SCAN" value="1"/> + <const name="TESTCODESTYLE_IS_FULL_SCAN" value="0"/> <!-- TESTS_COMPOSER_PATH - specify the path to composer binary, if a relative reference cannot be resolved --> <!--<const name="TESTS_COMPOSER_PATH" value="/usr/local/bin/composer"/>--> </php> From 6a3500bbca64f629fec65779a749ce0c3dbb1944 Mon Sep 17 00:00:00 2001 From: "Deep A. Joshi" <deep.ajoshi@EIC.EINFOCHIPS.COM> Date: Tue, 24 Jul 2018 19:29:44 +0530 Subject: [PATCH 0787/1171] Resolved special character issue for sidebar --- app/code/Magento/Wishlist/view/frontend/templates/sidebar.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/view/frontend/templates/sidebar.phtml b/app/code/Magento/Wishlist/view/frontend/templates/sidebar.phtml index 00fb87b411fe0..84b607adb6362 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/sidebar.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/sidebar.phtml @@ -31,7 +31,7 @@ $wishlistHelper = $this->helper('Magento\Wishlist\Helper\Data'); <div class="product-item-details"> <strong class="product-item-name"> <a data-bind="attr: { href: product_url }" class="product-item-link"> - <span data-bind="text: product_name"></span> + <span data-bind="html: product_name"></span> </a> </strong> <div data-bind="html: product_price"></div> From 5659fd0cdb817edad9736dfe70980daecfb3cc13 Mon Sep 17 00:00:00 2001 From: "Deep A. Joshi" <deep.ajoshi@EIC.EINFOCHIPS.COM> Date: Tue, 24 Jul 2018 19:55:47 +0530 Subject: [PATCH 0788/1171] Resolved special character issue order summary --- .../view/frontend/web/template/summary/item/details.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html b/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html index 94f94ec878151..730ceadbd914c 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html @@ -12,7 +12,7 @@ <div class="product-item-inner"> <div class="product-item-name-block"> - <strong class="product-item-name" data-bind="text: $parent.name"></strong> + <strong class="product-item-name" data-bind="html: $parent.name"></strong> <div class="details-qty"> <span class="label"><!-- ko i18n: 'Qty' --><!-- /ko --></span> <span class="value" data-bind="text: $parent.qty"></span> From 07402bb8d2f539915a40caa8fa2d6d51fc5fd08a Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Tue, 7 Aug 2018 22:52:05 +0530 Subject: [PATCH 0789/1171] Replace sort callbacks to spaceship operator --- app/code/Magento/Payment/Helper/Data.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/app/code/Magento/Payment/Helper/Data.php b/app/code/Magento/Payment/Helper/Data.php index 5fd23c195f0c4..0a4990313fa82 100644 --- a/app/code/Magento/Payment/Helper/Data.php +++ b/app/code/Magento/Payment/Helper/Data.php @@ -151,15 +151,7 @@ public function getStoreMethods($store = null, $quote = null) @uasort( $res, function (MethodInterface $a, MethodInterface $b) { - if ((int)$a->getConfigData('sort_order') < (int)$b->getConfigData('sort_order')) { - return -1; - } - - if ((int)$a->getConfigData('sort_order') > (int)$b->getConfigData('sort_order')) { - return 1; - } - - return 0; + return (int)$a->getConfigData('sort_order') <=> (int)$b->getConfigData('sort_order'); } ); From 4c96d0edb3f8d803e1ec7b82f4fc2f362575df8c Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Tue, 7 Aug 2018 13:43:39 -0500 Subject: [PATCH 0790/1171] MC-3309: Build stabilization Prevent calling _setActive multiple times on active modal --- app/code/Magento/Ui/view/base/web/js/modal/modal.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/modal/modal.js b/app/code/Magento/Ui/view/base/web/js/modal/modal.js index 147a18c08c54c..f016b0dbefd87 100644 --- a/app/code/Magento/Ui/view/base/web/js/modal/modal.js +++ b/app/code/Magento/Ui/view/base/web/js/modal/modal.js @@ -340,6 +340,12 @@ define([ var zIndex = this.modal.zIndex(), baseIndex = zIndex + this._getVisibleCount(); + if (this.modal.data('active')) { + return; + } + + this.modal.data('active', true); + this.overlay.zIndex(++baseIndex); this.prevOverlayIndex = this.overlay.zIndex(); this.modal.zIndex(this.overlay.zIndex() + 1); @@ -354,6 +360,7 @@ define([ */ _unsetActive: function () { this.modal.removeAttr('style'); + this.modal.data('active', false); if (this.overlay) { // In cases when one modal is closed but there is another modal open (e.g. admin notifications) From 5de2ec7a6c0b37bd4716f3fb9f97625aaa9d790f Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Tue, 7 Aug 2018 14:37:52 -0500 Subject: [PATCH 0791/1171] MC-3434: Build Stabilization for MC-2294 - Removed extra replace --- .../Magento/Ui/view/base/web/js/form/element/file-uploader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js index eb91f47e936ad..6492d0821fc21 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js @@ -170,7 +170,7 @@ define([ file.previewType = this.getFilePreviewType(file); if (!file.id && file.name) { - file.id = Base64.mageEncode(file.name).replace(/g/); + file.id = Base64.mageEncode(file.name); } this.observe.call(file, true, [ From bc442fb995a10c0197d907befa425ea039ee3868 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Tue, 7 Aug 2018 14:45:49 -0500 Subject: [PATCH 0792/1171] MQE-1176: Fix all deprecation warnings --- ...micBundleProductPricesForCombinationOfOptionsTest.xml | 1 + .../Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml | 3 ++- .../Mftf/Test/AdminCreateCategoryFromProductPageTest.xml | 1 + .../Test/AdminCreateRootCategoryAndSubcategoriesTest.xml | 1 + .../Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml | 1 + .../Test/AdminMultipleWebsitesUseDefaultValuesTest.xml | 1 + .../AdminProductGridFilteringByDateAttributeTest.xml | 2 ++ .../AdminProductStatusAttributeDisabledByDefaultTest.xml | 2 ++ .../Test/Mftf/Test/AdminSimpleProductEditUiTest.xml | 3 +++ .../Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml | 1 + .../Mftf/Test/StorefrontProductNameWithDoubleQuote.xml | 7 ++++++- .../Test/StorefrontProductWithEmptyAttributeTest.xml | 2 ++ .../StorefrontProductsCompareWithEmptyAttributeTest.xml | 2 ++ ...ntPurchaseProductCustomOptionsDifferentStoreViews.xml | 1 + ...rchaseProductWithCustomOptionsWithLongValuesTitle.xml | 5 +++++ .../VerifyChildCategoriesShouldNotIncludeInMenuTest.xml | 1 + ...ateFieldForUKCustomerRemainOptionAfterRefreshTest.xml | 8 +++++--- .../Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml | 3 +++ .../Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml | 1 + .../Mftf/Test/AdminConfigurableProductDeleteTest.xml | 2 ++ .../Mftf/Test/AdminConfigurableProductSearchTest.xml | 2 ++ .../Mftf/Test/AdminConfigurableProductUpdateTest.xml | 3 +++ .../ConfigurableProductPriceAdditionalStoreViewTest.xml | 1 + .../Test/AdminAddVariableToWYSIWYGNewsletterTest.xml | 1 + .../Test/Mftf/Test/AdminConfigPaymentsSectionState.xml | 3 +++ .../Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml | 1 + .../Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml | 3 ++- .../Test/CreditMemoTotalAfterShippingDiscountTest.xml | 3 ++- .../Test/Mftf/Test/PriceRuleCategoryNestingTest.xml | 5 ++++- .../Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml | 1 + .../Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml | 1 + ...rmationInShoppingCartForCustomerPhysicalQuoteTest.xml | 3 ++- ...ormationInShoppingCartForCustomerVirtualQuoteTest.xml | 3 ++- ...nformationInShoppingCartForGuestPhysicalQuoteTest.xml | 3 ++- ...InformationInShoppingCartForGuestVirtualQuoteTest.xml | 3 ++- app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml | 1 + .../Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml | 9 +++++++++ .../StorefrontAddMultipleStoreProductsToWishlistTest.xml | 2 ++ ...rontAddProductsToCartFromWishlistUsingSidebarTest.xml | 3 +++ .../Mftf/Test/StorefrontDeletePersistedWishlistTest.xml | 5 ++++- ...refrontRemoveProductsFromWishlistUsingSidebarTest.xml | 3 +++ 41 files changed, 94 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml index 0d81e364ae4ba..31a5f9bab7758 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml @@ -11,6 +11,7 @@ <test name="StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest"> <annotations> <features value="Bundle"/> + <stories value="View bundle products"/> <title value="Verify dynamic bundle product prices for combination of options"/> <description value="Verify prices for various configurations of Dynamic Bundle product"/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml index 7731a055f7e14..72c0a4a51901a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml @@ -9,7 +9,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminApplyTierPriceToProductTest"> <annotations> - <features value="Apply tier price to a product"/> + <features value="Catalog"/> + <stories value="Apply tier price to a product"/> <title value="You should be able to apply tier price to a product."/> <description value="You should be able to apply tier price to a product."/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml index dc4e6ad3bf036..a5150a0fb7f24 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml @@ -12,6 +12,7 @@ <annotations> <features value="Catalog"/> <stories value="Create/Edit Category in Admin"/> + <title value="Admin should be able to create category from the product page"/> <description value="Admin should be able to create category from the product page" /> <severity value="AVERAGE"/> <testCaseId value="MC-234"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml index 61b0e8083175c..11d919ddefa2c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml @@ -11,6 +11,7 @@ <test name="AdminCreateRootCategoryAndSubcategoriesTest"> <annotations> <features value="Catalog"/> + <stories value="Create categories"/> <title value="Admin should be able to create a Root Category and a Subcategory"/> <description value="Admin should be able to create a Root Category and a Subcategory"/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml index 1785cc5b3ea57..551b3437cb856 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml @@ -10,6 +10,7 @@ <test name="AdminMoveAnchoredCategoryTest"> <annotations> <features value="Catalog"/> + <stories value="Edit categories"/> <title value="Admin should be able to move a category via categories tree and changes should be applied on frontend without a forced cache cleaning"/> <description value="Admin should be able to move a category via categories tree and changes should be applied on frontend without a forced cache cleaning"/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml index af69a7da7ba4f..264615ff6736f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml @@ -11,6 +11,7 @@ <test name="AdminMultipleWebsitesUseDefaultValuesTest"> <annotations> <features value="Catalog"/> + <stories value="Create websites"/> <title value="Use Default Value checkboxes should be checked for new website scope"/> <description value="Use Default Value checkboxes for product attribute should be checked for new website scope"/> <severity value="MAJOR"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml index 10af015912ad2..2884cb26cf813 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml @@ -10,6 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminProductGridFilteringByDateAttributeTest"> <annotations> + <features value="Catalog"/> + <stories value="Filter products"/> <title value="Verify Set Product as new Filter input on Product Grid doesn't getreset to currentDate"/> <description value="Data input in the new from date filter field should not change"/> <severity value="MAJOR"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml index f2dfb1083cf89..a882c6e7817ce 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml @@ -10,6 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminProductStatusAttributeDisabledByDefaultTest"> <annotations> + <features value="Catalog"/> + <stories value="Create products"/> <title value="Verify the default option value for product Status attribute is set correctly during product creation"/> <description value="The default option value for product Status attribute is set correctly during product creation"/> <severity value="MAJOR"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml index fcbc6b3e5503a..bc5a0319bae7a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml @@ -10,10 +10,13 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminSimpleProductUiValidationTest"> <annotations> + <features value="Catalog"/> + <stories value="Edit products"/> <title value="UI elements on the simple product edit screen should be organized as expected"/> <description value="Admin should be able to use simple product UI in expected manner"/> <testCaseId value="MAGETWO-92835"/> <group value="Catalog"/> + <severity value="AVERAGE"/> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml index a5db0776feee9..5cae81b36a323 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml @@ -11,6 +11,7 @@ <test name="DeleteCategoriesTest"> <annotations> <features value="Catalog"/> + <stories value="Delete categories"/> <title value="Admin should be able to delete the default root category and subcategories and still see products in the storefront"/> <description value="Admin should be able to delete the default root category and subcategories and still see products in the storefront"/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml index 23d24c70bb682..569eb290bae3c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml @@ -10,13 +10,17 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontProductNameWithDoubleQuote"> <annotations> + <features value="Catalog"/> + <stories value="Create products"/> <title value="Product with double quote in name"/> <description value="Product with a double quote in the name should appear correctly on the storefront"/> <severity value="CRITICAL"/> <group value="product"/> <testCaseId value="MAGETWO-92384"/> - <!-- Skipped due to MAGETWO-93261 --> <group value="skip"/> + <skip> + <issueId value="MAGETWO-93261"/> + </skip> </annotations> <before> <createData entity="_defaultCategory" stepKey="createCategory"/> @@ -66,6 +70,7 @@ <test name="StorefrontProductNameWithHTMLEntities"> <annotations> <features value="Catalog"/> + <stories value="Create product"/> <title value=":Proudct with html special characters in name"/> <description value="Product with html entities in the name should appear correctly on the PDP breadcrumbs on storefront"/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml index 92ccfc5d6b338..1c1b47a6bded9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml @@ -10,6 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontProductWithEmptyAttributeTest"> <annotations> + <features value="Catalog"/> + <stories value="Create products"/> <title value="Product attribute is not visible on storefront if it is empty"/> <description value="Product attribute should not be visible on storefront if it is empty"/> <severity value="MAJOR"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml index 2527363140e16..d7f98c4cdd307 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml @@ -10,6 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontProductsCompareWithEmptyAttributeTest"> <annotations> + <features value="Catalog"/> + <stories value="Product attributes"/> <title value="Product attribute is not visible on product compare page if it is empty"/> <description value="Product attribute should not be visible on the product compare page if it is empty for all products that are being compared, not even displayed as N/A"/> <severity value="MAJOR"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViews.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViews.xml index 9eae7d28e6f69..1df6ae654001f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViews.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViews.xml @@ -11,6 +11,7 @@ <test name="StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest"> <annotations> <features value="Catalog"/> + <stories value="Custom options different storeviews"/> <title value="Admin should be able to sell products with different variants of their own"/> <description value="Admin should be able to sell products with different variants of their own"/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index bc89739f00c13..9d7c616238451 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -10,6 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle"> <annotations> + <features value="Catalog"/> + <stories value="Custom options"/> <group value="Catalog"/> <title value="Admin should be able to see the full title of the selected custom option value in the order"/> <description value="Admin should be able to see the full title of the selected custom option value in the order"/> @@ -17,6 +19,9 @@ <testCaseId value="MC-3043"/> <group value="skip"/> <!-- Skip due to MQE-1128 --> + <skip> + <issueId value="MQE-1128"/> + </skip> </annotations> <before> <!--Create Simple Product with Custom Options--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml index ed0962260650b..455e9b58156eb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml @@ -11,6 +11,7 @@ <test name="VerifyChildCategoriesShouldNotIncludeInMenuTest"> <annotations> <features value="Catalog"/> + <stories value="Create categories"/> <title value="Customer should not see categories that are not included in the menu"/> <description value="Customer should not see categories that are not included in the menu"/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml index c60eb79f92de1..52a69307550c5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest.xml @@ -11,13 +11,15 @@ <test name="AddressStateFieldForUKCustomerRemainOptionAfterRefreshTest"> <annotations> <features value="Checkout"/> - <title value="Guest Checkout"/> + <stories value="Guest checkout"/> + <title value="Address State Field For UK Customers Remain Option even After Browser Refresh"/> <description value="Address State Field For UK Customers Remain Option even After Browser Refresh"/> <severity value="MAJOR"/> <testCaseId value="MAGETWO-93329"/> <group value="checkout"/> - <!-- Skipped because of MAGETWO-93726 --> - <group value="skip"/> + <skip> + <issueId value="MAGETWO-93726"/> + </skip> </annotations> <before> <createData entity="_defaultCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml index 6f86121e53166..14676e8b0e4c2 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml @@ -85,9 +85,12 @@ </test> <test name="StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRates"> <annotations> + <features value="Checkout"/> + <stories value="Customer checkout"/> <title value="Customer Checkout with multiple addresses and tax rates"/> <description value="Should be able to place an order as a customer with multiple addresses and tax rates."/> <testCaseId value="MAGETWO-93109"/> + <severity value="AVERAGE"/> </annotations> <before> <createData entity="SimpleSubCategory" stepKey="simplecategory"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml index bf17c277c1c5f..8fea72764f280 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml @@ -15,6 +15,7 @@ <title value="Admin should be able to add variable to WYSIWYG content of Block"/> <description value="You should be able to add variable to WYSIWYG content Block"/> <testCaseId value="MAGETWO-84378"/> + <severity value="AVERAGE"/> </annotations> <before> <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml index 20201627f500b..1a694b8adf17e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml @@ -16,6 +16,7 @@ <description value="admin should be able to delete a configurable product"/> <testCaseId value="MC-87"/> <group value="ConfigurableProduct"/> + <severity value="AVERAGE"/> </annotations> <before> @@ -106,6 +107,7 @@ <description value="admin should be able to mass delete configurable products"/> <testCaseId value="MC-99"/> <group value="ConfigurableProduct"/> + <severity value="AVERAGE"/> </annotations> <before> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml index 23b8fc537cef8..059a18200e90c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml @@ -16,6 +16,7 @@ <description value="admin should be able to search for a configurable product"/> <testCaseId value="MC-100"/> <group value="ConfigurableProduct"/> + <severity value="AVERAGE"/> </annotations> <before> @@ -94,6 +95,7 @@ <description value="admin should be able to filter by type configurable product"/> <testCaseId value="MC-66"/> <group value="ConfigurableProduct"/> + <severity value="AVERAGE"/> </annotations> <before> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml index 06de0e2ba5ce3..af12f49bf86ea 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml @@ -16,6 +16,7 @@ <description value="admin should be able to bulk update attributes of configurable products"/> <testCaseId value="MC-88"/> <group value="ConfigurableProduct"/> + <severity value="AVERAGE"/> </annotations> <before> @@ -78,6 +79,7 @@ <description value="Admin should be able to remove a product configuration"/> <testCaseId value="MC-63"/> <group value="ConfigurableProduct"/> + <severity value="AVERAGE"/> </annotations> <before> @@ -170,6 +172,7 @@ <description value="Admin should be able to disable a product configuration"/> <testCaseId value="MC-119"/> <group value="ConfigurableProduct"/> + <severity value="AVERAGE"/> </annotations> <before> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml index 2b460a51ee5d1..b06067a6d43e4 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml @@ -11,6 +11,7 @@ <test name="ConfigurableProductPriceAdditionalStoreViewTest"> <annotations> <features value="ConfigurableProductPriceStoreFront"/> + <stories value="View products"/> <title value="Configurable product prices should not disappear on storefront for additional store"/> <description value="Configurable product price should not disappear for additional stores on frontEnd if disabled for default store"/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml index 6e5370927e9de..e3d73fb57333e 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml @@ -15,6 +15,7 @@ <title value="Admin should be able to add variable to WYSIWYG Editor of Newsletter"/> <description value="Admin should be able to add variable to WYSIWYG Editor Newsletter"/> <testCaseId value="MAGETWO-84379"/> + <severity value="AVERAGE"/> </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionState.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionState.xml index f9e2c2589e3ab..ac752e8412ff9 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionState.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionState.xml @@ -10,6 +10,9 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigPaymentsSectionState"> <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Other Payment Methods section in Admin expanded by default"/> <description value="Other Payment Methods section in Admin expanded by default"/> <severity value="AVERAGE"/> <testCaseId value="MAGETWO-92043"/> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml index c32b371566277..289ba541e3c18 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml @@ -11,6 +11,7 @@ <test name="GuestCheckoutWithEnabledPersistentTest"> <annotations> <features value="Persistent"/> + <stories value="Guest checkout"/> <title value="Guest Checkout with Enabled Persistent"/> <description value="Checkout data must be restored after page checkout reload."/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml index f4a228c72250f..cc69b6dfb7d41 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml @@ -9,12 +9,13 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminSubmitsOrderWithAndWithoutEmailTest"> <annotations> + <features value="Sales"/> + <stories value="Create orders"/> <title value="Email is required to create an order from Admin Panel"/> <description value="Admin should not be able to submit orders without an email address"/> <severity value="MAJOR"/> <testCaseId value="MAGETWO-92980"/> <group value="sales"/> - </annotations> <before> <createData entity="_defaultCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index ab067ea45222f..60df3f27fd65b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -10,7 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="CreditMemoTotalAfterShippingDiscountTest"> <annotations> - <features value="Credit memo"/> + <features value="Sales"/> + <stories value="Credit memos"/> <title value="Verify credit memo grand total after shipping discount is applied via Cart Price Rule"/> <description value="Verify credit memo grand total after shipping discount is applied via Cart Price Rule"/> <severity value="MAJOR"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml index de5f480ac6d7e..091e09e32f1e6 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml @@ -7,10 +7,13 @@ <tests xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <test name="PriceRuleCategoryNestingTest"> <annotations> + <features value="SalesRule"/> + <stories value="Create categories"/> + <title value="Category nesting level must be the same as were created in categories."/> <description value="Category nesting level must be the same as were created in categories."/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-91101"/> - <group value="sale_rules"/> + <group value="SalesRule"/> </annotations> <before> <createData entity="_defaultCategory" stepKey="subcategory1"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml index 4e5dfed70d36e..25e93f8f6ff4c 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml @@ -14,6 +14,7 @@ <title value="Admin should be able to create a store group"/> <description value="Admin should be able to create a store group"/> <group value="store"/> + <severity value="AVERAGE"/> </annotations> <before> <createData stepKey="b1" entity="customStoreGroup"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml index 288245066b84d..54d392d0c06f1 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml @@ -14,6 +14,7 @@ <title value="Admin should be able to create a store view"/> <description value="Admin should be able to create a store view"/> <group value="storeView"/> + <severity value="AVERAGE"/> </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="login"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml index c89b75c229341..1b3422011a9a7 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml @@ -10,7 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest"> <annotations> - <features value="Tax information in shopping cart for Customer with default addresses (physical quote)"/> + <features value="Tax"/> + <stories value="Shopping cart taxes"/> <title value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (physical quote)"/> <description value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (physical quote)"/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml index cbe09059c26cd..3fa9826512934 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml @@ -10,7 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest"> <annotations> - <features value="Tax information in shopping cart for Customer with default addresses (virtual quote)"/> + <features value="Tax"/> + <stories value="Shopping cart taxes"/> <title value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (virtual quote)"/> <description value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (virtual quote)"/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml index 5e3594b62b500..47161001219e8 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml @@ -10,7 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest"> <annotations> - <features value="Tax information in shopping cart for Guest (physical quote)"/> + <features value="Tax"/> + <stories value="Shopping cart taxes"/> <title value="Tax information are updating/recalculating on fly in shopping cart for Guest (physical quote)"/> <description value="Tax information are updating/recalculating on fly in shopping cart for Guest (physical quote)"/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml index 036686050db75..88496b8e2cd27 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml @@ -10,7 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest"> <annotations> - <features value="Tax information in shopping cart for Guest (virtual quote)"/> + <features value="Tax"/> + <stories value="Shopping cart taxes"/> <title value="Tax information are updating/recalculating on fly in shopping cart for Guest (virtual quote)"/> <description value="Tax information are updating/recalculating on fly in shopping cart for Guest (virtual quote)"/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml index 5844c77ef9ade..3f8d87cc15db5 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml @@ -11,6 +11,7 @@ <test name="ThemeTest"> <annotations> <features value="Theme Test"/> + <stories value="Themes"/> <title value="Magento Rush theme should not be available in Themes grid"/> <description value="Magento Rush theme should not be available in Themes grid"/> <severity value="MAJOR"/> diff --git a/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml index 6a8de1ca5f0a0..02e16f9921d62 100644 --- a/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml +++ b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml @@ -11,6 +11,15 @@ <!-- This test exists to serve as a base for extension for other tests --> <test name="NewProductsListWidgetTest"> + <annotations> + <features value="Widget"/> + <stories value="New products list widget"/> + <title value="Admin should be able to set products as new so that they show up in the Catalog New Products List Widget"/> + <description value="Admin should be able to set products as new so that they show up in the Catalog New Products List Widget"/> + <severity value="MAJOR"/> + <group value="Widget"/> + </annotations> + <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml index 0c7f5fb4963cf..3d2d6d8781be0 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml @@ -10,9 +10,11 @@ <test name="StorefrontAddMultipleStoreProductsToWishlistTest"> <annotations> <features value="Wishlist"/> + <stories value="Adding to wishlist"/> <title value="Customer should be able to add products to wishlist from different stores"/> <description value="All products added to wishlist should be visible on any store. Even if product visibility was set to 'Not Visible Individually' for this store"/> <group value="wishlist"/> + <severity value="AVERAGE"/> </annotations> <before> <createData entity="customStoreGroup" stepKey="storeGroup"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml index a542c9d552324..16a18dd27b123 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml @@ -9,9 +9,12 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontAddProductsToCartFromWishlistUsingSidebarTest"> <annotations> + <features value="Wishlist"/> + <stories value="Add to wishlist"/> <title value="Add products from the wishlist to the cart using the sidebar."/> <description value="Products added to the cart from wishlist and a customer remains on the same page."/> <group value="wishlist"/> + <severity value="AVERAGE"/> </annotations> <before> <createData entity="SimpleSubCategory" stepKey="categoryFirst"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml index 01de5f39527b0..0ae2b6af804bd 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml @@ -14,8 +14,11 @@ <title value="Customer should be able to delete a persistent wishlist"/> <description value="Customer should be able to delete a persistent wishlist"/> <group value="wishlist"/> - <!-- MQE-1145 --> <group value="skip"/> + <skip> + <issueId value="MQE-1145"/> + </skip> + <severity value="AVERAGE"/> </annotations> <before> <createData stepKey="category" entity="SimpleSubCategory"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml index 4aec6e4703e98..e3382dc41d27e 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml @@ -9,9 +9,12 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontRemoveProductsFromWishlistUsingSidebarTest"> <annotations> + <features value="Wishlist"/> + <stories value="Remove from wishlist"/> <title value="Remove products from the wishlist using the sidebar."/> <description value="Products removed from wishlist and a customer remains on the same page."/> <group value="wishlist"/> + <severity value="AVERAGE"/> </annotations> <before> <createData entity="SimpleSubCategory" stepKey="categoryFirst"/> From 46ab7d95b496fb9f96445050970c01f59674fcb5 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Tue, 7 Aug 2018 22:46:23 +0300 Subject: [PATCH 0793/1171] Fixed tests failures --- .../Magento/Cms/Test/Unit/Helper/PageTest.php | 2 +- .../web/css/source/_module.less | 183 +++++++++--------- .../web/css/source/module/_menu.less | 4 +- .../web/css/source/module/pages/_login.less | 2 +- .../module/components/_currency-addon.less | 18 +- .../css/source/module/steps/_bulk-images.less | 2 +- .../web/css/source/_module.less | 154 +++++++-------- .../css/source/module/_staging-preview.less | 2 +- 8 files changed, 184 insertions(+), 183 deletions(-) diff --git a/app/code/Magento/Cms/Test/Unit/Helper/PageTest.php b/app/code/Magento/Cms/Test/Unit/Helper/PageTest.php index c50f33caa6bc2..6034ec890f938 100644 --- a/app/code/Magento/Cms/Test/Unit/Helper/PageTest.php +++ b/app/code/Magento/Cms/Test/Unit/Helper/PageTest.php @@ -478,7 +478,7 @@ public function getPageUrlDataProvider() return [ 'ids NOT EQUAL BUT page->load() NOT SUCCESSFUL' => [ 'pageId' => 123, - 'internalPageId' => 234, + 'internalPageId' => null, 'pageLoadResultIndex' => 0, 'expectedResult' => null, ], diff --git a/app/design/adminhtml/Magento/backend/Magento_Analytics/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Analytics/web/css/source/_module.less index dbc4e54762d7f..cf1275c477c2f 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Analytics/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Analytics/web/css/source/_module.less @@ -18,65 +18,65 @@ // _____________________________________________ .dashboard-advanced-reports { - .lib-vendor-prefix-display(flex); - border-color: @color-gray89; - border-style: solid; - border-width: 1px 0; - margin-bottom: @indent__l; - padding: @indent__m 0; + .lib-vendor-prefix-display(flex); + border-color: @color-gray89; + border-style: solid; + border-width: 1px 0; + margin-bottom: @indent__l; + padding: @indent__m 0; } .dashboard-advanced-reports-title { - &:extend(.dashboard-item-title all); - margin-bottom: @indent__s; + &:extend(.dashboard-item-title all); + margin-bottom: @indent__s; } .dashboard-advanced-reports-content { - line-height: @line-height__xl; + line-height: @line-height__xl; } .dashboard-advanced-reports-actions { - .lib-vendor-prefix-flex-basis(auto); - .lib-vendor-prefix-flex-grow(1); - .lib-vendor-prefix-flex-shrink(1); - align-self: center; - margin-left: @indent__m; - margin-right: @page-main-actions__padding; - text-align: right; + .lib-vendor-prefix-flex-basis(auto); + .lib-vendor-prefix-flex-grow(1); + .lib-vendor-prefix-flex-shrink(1); + align-self: center; + margin-left: @indent__m; + margin-right: @page-main-actions__padding; + text-align: right; } .action-advanced-reports { - &:extend(.abs-action-l all); - &:extend(.abs-action-pattern all); - background-color: @button-advanced-reports__background-color; - border-color: @button-advanced-reports__background-color; - color: @button-advanced-reports__color; - text-shadow: 1px 1px 0 rgba(0, 0, 0, .25); - white-space: nowrap; - - &:after { - &:extend(.abs-icon all); - content: @icon-external-link__content; - font-size: @font-size__xs; - vertical-align: super; - } - - &:hover, - &:active, - &:focus { - background-color: @button-advanced-reports__hover__background-color; - border-color: @button-advanced-reports__hover__border-color; - box-shadow: @button__hover__box-shadow; + &:extend(.abs-action-l all); + &:extend(.abs-action-pattern all); + background-color: @button-advanced-reports__background-color; + border-color: @button-advanced-reports__background-color; color: @button-advanced-reports__color; - text-decoration: none; - } - - &.disabled, - &[disabled] { - cursor: default; - opacity: @disabled__opacity; - pointer-events: none; - } + text-shadow: 1px 1px 0 rgba(0, 0, 0, .25); + white-space: nowrap; + + &:after { + &:extend(.abs-icon all); + content: @icon-external-link__content; + font-size: @font-size__xs; + vertical-align: super; + } + + &:hover, + &:active, + &:focus { + background-color: @button-advanced-reports__hover__background-color; + border-color: @button-advanced-reports__hover__border-color; + box-shadow: @button__hover__box-shadow; + color: @button-advanced-reports__color; + text-decoration: none; + } + + &.disabled, + &[disabled] { + cursor: default; + opacity: @disabled__opacity; + pointer-events: none; + } } // @@ -84,41 +84,42 @@ // --------------------------------------------- .advanced-reports-subscription-modal { - .modal-inner-wrap { - max-width: 75rem; - margin-top: 13rem; - - .modal-content, .modal-header { - padding-left: 4rem; - padding-right: 4rem; - - .action-close { - display: none; - } + .modal-inner-wrap { + margin-top: 13rem; + max-width: 75rem; + + .modal-content, + .modal-header { + padding-left: 4rem; + padding-right: 4rem; + + .action-close { + display: none; + } + } } - } - .admin__fieldset { - padding: 0; - } + .admin__fieldset { + padding: 0; + } } .advanced-reports-subscription-text { - line-height: @line-height__xl; - padding-bottom: 8rem; + line-height: @line-height__xl; + padding-bottom: 8rem; } .advanced-reports-subscription-close { - display: block; - float: right; + display: block; + float: right; } .advanced-reports-subscription-modal { - h1:first-of-type { - background: url("Magento_Analytics::images/analytics-icon.svg") no-repeat; - background-size: 55px 49.08px; - padding: 1.5rem 0 2rem 7rem; - } + h1:first-of-type { + background: url("Magento_Analytics::images/analytics-icon.svg") no-repeat; + background-size: 55px 49.08px; + padding: 1.5rem 0 2rem 7rem; + } } // @@ -126,41 +127,41 @@ // _____________________________________________ .config-additional-comment { - border-color: @color-gray80; - border-style: solid; - border-width: 1px 0; - margin: @indent__l 0; - padding: @indent__m; + border-color: @color-gray80; + border-style: solid; + border-width: 1px 0; + margin: @indent__l 0; + padding: @indent__m; } .config-additional-comment-title { - margin-bottom: @indent__xs; + margin-bottom: @indent__xs; } .config-additional-comment-content { - line-height: @line-height__l; + line-height: @line-height__l; } .config-vertical-title { - clear: both; - color: #303030; - font-size: 1.7rem; - font-weight: 600; - letter-spacing: .025em; - padding: 1.9rem 2.8rem 1.9rem 0; - position: relative; + clear: both; + color: #303030; + font-size: 1.7rem; + font-weight: 600; + letter-spacing: .025em; + padding: 1.9rem 2.8rem 1.9rem 0; + position: relative; } .config-vertical-comment { - line-height: 1.5; - margin-bottom: .5em; - margin-top: 1rem; + line-height: 1.5; + margin-bottom: .5em; + margin-top: 1rem; } #row_analytics_general_vertical { - >td.config-vertical-label { - >label.admin__field-label { - padding-right: 0; + >td.config-vertical-label { + >label.admin__field-label { + padding-right: 0; + } } - } } diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less index 7e58d3bf44b0b..ba28108326006 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less @@ -187,12 +187,12 @@ display: block; } - // External link marker + // External link marker [target='_blank'] { &:after { &:extend(.abs-icon all); content: @icon-external-link__content; - font-size: 0.5rem; + font-size: .5rem; margin-left: @indent__xs; vertical-align: super; } diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_login.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_login.less index 2852fc1095576..e7f4c41de9750 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_login.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_login.less @@ -80,7 +80,7 @@ } .messages { - margin-top: 0.5rem; + margin-top: .5rem; + form .admin__legend { display: none; diff --git a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less index c491e4bdfc71e..d6fd3ed06599f 100644 --- a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less +++ b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less @@ -18,8 +18,8 @@ // _____________________________________________ .currency-addon { - position: relative; border: 1px solid #adadad; + position: relative; display: -webkit-inline-flex; display: -ms-inline-flexbox; -webkit-flex-direction: row; @@ -30,18 +30,18 @@ width: 100%; .admin__control-text { + -ms-flex-order: 1; -webkit-appearance: none; - appearance: none; -webkit-flex-grow: 1; - flex-grow: 1; - -ms-flex-order: 1; - -webkit-order: 1; - order: 1; -webkit-flex-shrink: 1; - flex-shrink: 1; + -webkit-order: 1; + appearance: none; background-color: transparent; border-color: transparent; box-shadow: none; + flex-grow: 1; + flex-shrink: 1; + order: 1; vertical-align: top; &:focus { @@ -53,8 +53,8 @@ label.error { position: absolute; - left: 0; top: 33px; + left: 0; } .currency-symbol { @@ -63,6 +63,7 @@ box-sizing: border-box; color: @currency-addon-symbol__color; height: @currency-addon-symbol__height; + order: 0; padding: 7px 0 0 @indent__xs; position: static; transition: @smooth__border-color; @@ -73,7 +74,6 @@ -webkit-flex-shrink: 0; flex-shrink: 0; z-index: 1; - order: 0; } ._error & { diff --git a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/steps/_bulk-images.less b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/steps/_bulk-images.less index 25c7be1467bda..35dae432a6c4f 100644 --- a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/steps/_bulk-images.less +++ b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/steps/_bulk-images.less @@ -97,8 +97,8 @@ margin-left: 0; .admin__field-control { - float: right; display: block; + float: right; } } } diff --git a/app/design/adminhtml/Magento/backend/Magento_ReleaseNotification/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_ReleaseNotification/web/css/source/_module.less index 932699d337cd7..3e2e264f40740 100644 --- a/app/design/adminhtml/Magento/backend/Magento_ReleaseNotification/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_ReleaseNotification/web/css/source/_module.less @@ -8,110 +8,110 @@ // --------------------------------------------- .release-notification-modal { - -webkit-transition: visibility 0s .5s, opacity .5s ease; - transition: visibility 0s .5s, opacity .5s ease; - - &._show { - visibility: visible; - opacity: 1; - -webkit-transition: opacity .5s ease; - transition: opacity .5s ease; - } - - .modal-inner-wrap { - -webkit-transform: translateX(0); - transform: translateX(0); - -webkit-transition: -webkit-transform 0s; - transition: transform 0s; - height: 50rem; - max-width: 75rem; - margin-top: 13rem; - - .modal-content, .modal-header { - padding-left: 4rem; - padding-right: 4rem; - - .action-close { - display: none; - } + -webkit-transition: visibility 0s .5s, opacity .5s ease; + transition: visibility 0s .5s, opacity .5s ease; + + &._show { + -webkit-transition: opacity .5s ease; + opacity: 1; + transition: opacity .5s ease; + visibility: visible; } - } - .admin__fieldset { - padding: 0; - } + .modal-inner-wrap { + -webkit-transform: translateX(0); + transform: translateX(0); + -webkit-transition: -webkit-transform 0s; + transition: transform 0s; + height: 50rem; + max-width: 75rem; + margin-top: 13rem; + + .modal-content, .modal-header { + padding-left: 4rem; + padding-right: 4rem; + + .action-close { + display: none; + } + } + } + + .admin__fieldset { + padding: 0; + } } .release-notification-title-with-image { - padding: 1.5rem 0 2rem 7rem; - background-size: 55px 49.08px; - background-repeat: no-repeat; + padding: 1.5rem 0 2rem 7rem; + background-size: 55px 49.08px; + background-repeat: no-repeat; } .release-notification-text { - line-height: @line-height__l; - - ul { - margin: 2rem 0 2rem 0; - - li { - font-size: @font-size__xs; - margin: 1.5rem 0 1.5rem 2rem; - - span { - font-size: @font-size__base; - vertical-align: middle; - position: relative; - left: 1rem; - } + line-height: @line-height__l; + + ul { + margin: 2rem 0 2rem 0; + + li { + font-size: @font-size__xs; + margin: 1.5rem 0 1.5rem 2rem; + + span { + font-size: @font-size__base; + vertical-align: middle; + position: relative; + left: 1rem; + } + } } - } } .release-notification-button-next, .release-notification-button-back { - display: block; - float: right; - position: absolute; - bottom: 4rem; + display: block; + float: right; + position: absolute; + bottom: 4rem; } .release-notification-button-next { - .lib-button-as-link(); - right: 4rem; - font-weight: 400; + .lib-button-as-link(); + right: 4rem; + font-weight: 400; } .release-notification-button-back { - .lib-button-as-link(); - left: 4rem; - font-weight: 400; + .lib-button-as-link(); + left: 4rem; + font-weight: 400; } .highlight-item { - padding: 0 0 2rem 8.5rem; - margin-left: 1rem; - background-size: 65px 58px; - background-repeat: no-repeat; - - h3 { - margin: 0; - - span { - font-style: @font-style__emphasis; - font-size: @font-size__s; - font-weight: @font-weight__light; + padding: 0 0 2rem 8.5rem; + margin-left: 1rem; + background-size: 65px 58px; + background-repeat: no-repeat; + + h3 { + margin: 0; + + span { + font-style: @font-style__emphasis; + font-size: @font-size__s; + font-weight: @font-weight__light; + } } - } } .highlight-item-no-image { - padding: 0 0 2rem 0; + padding: 0 0 2rem 0; - h3 { - margin: 0; - } + h3 { + margin: 0; + } } .hide-release-notification { - display: none; + display: none; } diff --git a/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less b/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less index 8dc22fd8e206b..99aa5fdc04e71 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less +++ b/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less @@ -1,5 +1,5 @@ // /** -// * Copyright © 2015 Magento. All rights reserved. +// * Copyright © Magento, Inc. All rights reserved. // * See COPYING.txt for license details. // */ From 5994832f6c1e5cbfc228390c846f7b6ec7beca21 Mon Sep 17 00:00:00 2001 From: Danny Nimmo <d@nny.nz> Date: Tue, 7 Aug 2018 21:36:03 +0100 Subject: [PATCH 0794/1171] Fix incorrect image magnifier size bug in Safari --- lib/web/magnifier/magnify.js | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/lib/web/magnifier/magnify.js b/lib/web/magnifier/magnify.js index 5776c666b6815..1fb73ea28bff1 100644 --- a/lib/web/magnifier/magnify.js +++ b/lib/web/magnifier/magnify.js @@ -53,21 +53,14 @@ define([ /** * Return width and height of original image - * @param src path for original image + * @param img original image node * @returns {{rw: number, rh: number}} */ - function getImageSize(src) { - var img = new Image(), - imgSize = { - rw: 0, - rh: 0 - }; - - img.src = src; - imgSize.rw = img.width; - imgSize.rh = img.height; - - return imgSize; + function getImageSize(img) { + return { + rw: img.naturalWidth, + rh: img.naturalHeight + }; } /** @@ -192,7 +185,7 @@ define([ if (!e.data.$image || !e.data.$image.length) return; - imageSize = getImageSize($(fullscreenImageSelector)[0].src); + imageSize = getImageSize($(fullscreenImageSelector)[0]); parentWidth = e.data.$image.parent().width(); parentHeight = e.data.$image.parent().height(); isImageSmall = parentWidth >= imageSize.rw && parentHeight >= imageSize.rh; @@ -331,7 +324,7 @@ define([ if (allowZoomIn && (!transitionEnabled || !transitionActive) && (isTouchEnabled || !$(zoomInButtonSelector).hasClass(zoomInDisabled))) { $image = $(fullscreenImageSelector); - imgOriginalSize = getImageSize($image[0].src); + imgOriginalSize = getImageSize($image[0]); imageWidth = $image.width(); imageHeight = $image.height(); ratio = imageWidth / imageHeight; @@ -630,7 +623,7 @@ define([ * @param e - event object */ function dblClickHandler(e) { - var imgOriginalSize = getImageSize($image[0].src), + var imgOriginalSize = getImageSize($image[0]), proportions; if (imgOriginalSize.rh < $image.parent().height() && imgOriginalSize.rw < $image.parent().width()) { From 684f78e6f121cc04a208664ff5f009889cfa0913 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 7 Aug 2018 15:49:11 -0500 Subject: [PATCH 0795/1171] MAGETWO-91504: Mobile PDP accordion widget hides accordion content on phones with iOS - only scroll to element when element gets out of the viewport --- lib/web/mage/collapsible.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/web/mage/collapsible.js b/lib/web/mage/collapsible.js index ce4c5325aebd4..ceb5f0a37baea 100644 --- a/lib/web/mage/collapsible.js +++ b/lib/web/mage/collapsible.js @@ -448,7 +448,9 @@ define([ if (this.options.animate) { this._animate(showProps); } else { - this.content.get(0).parentElement.scrollIntoView(); + if (this._isElementOutOfViewport(this.content.get(0).parentElement)) { + this.content.get(0).parentElement.scrollIntoView(); + } this.content.show(); } this._open(); @@ -554,6 +556,14 @@ define([ }, 1); }); } + }, + + /** + * @private + */ + _isElementOutOfViewport: function (el) { + var rect = el.getBoundingClientRect(); + return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight; } }); From 45cb03a478ceb0c5764ecb738dd5aa22063daa26 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 7 Aug 2018 16:10:31 -0500 Subject: [PATCH 0796/1171] MAGETWO-91504: Mobile PDP accordion widget hides accordion content on phones with iOS - only scroll to element when element gets out of the viewport --- lib/web/mage/collapsible.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/web/mage/collapsible.js b/lib/web/mage/collapsible.js index ceb5f0a37baea..e432994705178 100644 --- a/lib/web/mage/collapsible.js +++ b/lib/web/mage/collapsible.js @@ -559,10 +559,11 @@ define([ }, /** + * @param {HTMLElement} elem * @private */ - _isElementOutOfViewport: function (el) { - var rect = el.getBoundingClientRect(); + _isElementOutOfViewport: function (elem) { + var rect = elem.getBoundingClientRect(); return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight; } }); From 4d619e67651ac5cfe13b561707a8b6db034acc88 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@magento.com> Date: Tue, 7 Aug 2018 17:37:26 -0500 Subject: [PATCH 0797/1171] MAGETWO-91314: Validate method arguments annotations - remove unnecessary files --- dev/tests/static/phpunit.xml | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100644 dev/tests/static/phpunit.xml diff --git a/dev/tests/static/phpunit.xml b/dev/tests/static/phpunit.xml deleted file mode 100644 index c7754e7637174..0000000000000 --- a/dev/tests/static/phpunit.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/** - * Default test suites declaration: run verification of coding standards and code integrity test suites - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.2/phpunit.xsd" - colors="true" - beStrictAboutTestsThatDoNotTestAnything="false" - bootstrap="./framework/bootstrap.php" -> - <testsuites> - <testsuite name="PHP Coding Standard Verification"> - <file>testsuite/Magento/Test/Php/LiveCodeTest.php</file> - </testsuite> - <testsuite name="Xss Unsafe Output Test"> - <file>testsuite/Magento/Test/Php/XssPhtmlTemplateTest.php</file> - </testsuite>--> - </testsuites> - <php> - <ini name="date.timezone" value="America/Los_Angeles"/> - <const name="TESTS_COMPOSER_PATH" value="/usr/local/bin/composer"/> - <!-- TESTCODESTYLE_IS_FULL_SCAN - specify if full scan should be performed for test code style test --> - <const name="TESTCODESTYLE_IS_FULL_SCAN" value="0"/> - </php> -</phpunit> From 61576a1ecf3edad8e325cbd6e83620018d0f53d7 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Tue, 7 Aug 2018 23:12:54 -0300 Subject: [PATCH 0798/1171] Updated the annotations and added translation for Magento_Search module. --- app/code/Magento/Search/Model/Query.php | 7 +++++++ .../Magento/Search/Model/SynonymGroupRepository.php | 11 +++++++---- .../Search/Ui/Component/Listing/Column/StoreView.php | 2 ++ .../Search/Ui/Component/Listing/Column/Website.php | 2 ++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Search/Model/Query.php b/app/code/Magento/Search/Model/Query.php index 404d93f76da6d..365e03b3648a7 100644 --- a/app/code/Magento/Search/Model/Query.php +++ b/app/code/Magento/Search/Model/Query.php @@ -147,6 +147,7 @@ public function getSearchCollection() * Retrieve collection of suggest queries * * @return QueryCollection + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getSuggestCollection() { @@ -167,6 +168,7 @@ public function getSuggestCollection() * * @param string $text * @return $this + * @throws \Magento\Framework\Exception\LocalizedException * @deprecated 100.1.0 "synonym for" feature has been removed */ public function loadByQuery($text) @@ -180,6 +182,7 @@ public function loadByQuery($text) * * @param string $text * @return $this + * @throws \Magento\Framework\Exception\LocalizedException */ public function loadByQueryText($text) { @@ -204,6 +207,7 @@ public function setStoreId($storeId) * Retrieve store Id * * @return int + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getStoreId() { @@ -217,6 +221,7 @@ public function getStoreId() * Prepare save query for result * * @return $this + * @throws \Exception */ public function prepare() { @@ -264,6 +269,7 @@ public function saveNumResults($numResults) * Retrieve minimum query length * * @return int + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getMinQueryLength() { @@ -278,6 +284,7 @@ public function getMinQueryLength() * Retrieve maximum query length * * @return int + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getMaxQueryLength() { diff --git a/app/code/Magento/Search/Model/SynonymGroupRepository.php b/app/code/Magento/Search/Model/SynonymGroupRepository.php index 6c5bdfb597487..167a39f5ac657 100644 --- a/app/code/Magento/Search/Model/SynonymGroupRepository.php +++ b/app/code/Magento/Search/Model/SynonymGroupRepository.php @@ -103,9 +103,10 @@ public function get($synonymGroupId) * Private helper to create a synonym group, throw exception on merge conflict * * @param SynonymGroupInterface $synonymGroup - * @param bool $errorOnMergeConflict + * @param bool $errorOnMergeConflict * @return SynonymGroupInterface * @throws Synonym\MergeConflictException + * @throws \Magento\Framework\Exception\AlreadyExistsException */ private function create(SynonymGroupInterface $synonymGroup, $errorOnMergeConflict) { @@ -142,8 +143,9 @@ private function create(SynonymGroupInterface $synonymGroup, $errorOnMergeConfli * Perform synonyms merge * * @param SynonymGroupInterface $synonymGroupToMerge - * @param array $matchingGroupIds + * @param array $matchingGroupIds * @return array + * @throws \Exception */ private function merge(SynonymGroupInterface $synonymGroupToMerge, array $matchingGroupIds) { @@ -177,11 +179,12 @@ private function populateSynonymGroupModel(SynonymGroup $modelToPopulate, Synony /** * Private helper to update a synonym group, throw exception on merge conflict * - * @param SynonymGroup $oldSynonymGroup + * @param SynonymGroup $oldSynonymGroup * @param SynonymGroupInterface $newSynonymGroup - * @param bool $errorOnMergeConflict + * @param bool $errorOnMergeConflict * @return SynonymGroupInterface * @throws Synonym\MergeConflictException + * @throws \Magento\Framework\Exception\AlreadyExistsException */ private function update( SynonymGroup $oldSynonymGroup, diff --git a/app/code/Magento/Search/Ui/Component/Listing/Column/StoreView.php b/app/code/Magento/Search/Ui/Component/Listing/Column/StoreView.php index 1a66d9242b343..405042f2e7825 100644 --- a/app/code/Magento/Search/Ui/Component/Listing/Column/StoreView.php +++ b/app/code/Magento/Search/Ui/Component/Listing/Column/StoreView.php @@ -46,6 +46,7 @@ public function __construct( * * @param array $dataSource * @return array + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function prepareDataSource(array $dataSource) { @@ -62,6 +63,7 @@ public function prepareDataSource(array $dataSource) * * @param array $item * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException */ protected function prepareItem(array $item) { diff --git a/app/code/Magento/Search/Ui/Component/Listing/Column/Website.php b/app/code/Magento/Search/Ui/Component/Listing/Column/Website.php index 216e0b3877ddb..78fd4b9c36a80 100644 --- a/app/code/Magento/Search/Ui/Component/Listing/Column/Website.php +++ b/app/code/Magento/Search/Ui/Component/Listing/Column/Website.php @@ -47,6 +47,7 @@ public function __construct( * * @param array $dataSource * @return array + * @throws \Magento\Framework\Exception\LocalizedException */ public function prepareDataSource(array $dataSource) { @@ -63,6 +64,7 @@ public function prepareDataSource(array $dataSource) * * @param array $item * @return string + * @throws \Magento\Framework\Exception\LocalizedException */ protected function prepareItem(array $item) { From 33c711f5eea3518b6f4d5929f74b0538ca1b69b8 Mon Sep 17 00:00:00 2001 From: Floriaan <15719294+Floctopus@users.noreply.github.com> Date: Wed, 8 Aug 2018 08:30:36 +0200 Subject: [PATCH 0799/1171] Fix documentation typos in registry.js Fixed multiple typos in php docblocks. --- .../Ui/view/base/web/js/lib/registry/registry.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/registry/registry.js b/app/code/Magento/Ui/view/base/web/js/lib/registry/registry.js index 5c3c71e31823d..826e8ec8c33b4 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/registry/registry.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/registry/registry.js @@ -17,7 +17,7 @@ define([ var privateData = new WeakMap(); /** - * Extarcts private items storage associated + * Extracts private item storage associated * with a provided registry instance. * * @param {Object} container @@ -39,7 +39,7 @@ define([ } /** - * Wrapper function used for convinient access to the elements. + * Wrapper function used for convenient access to the elements. * See 'async' method for examples of usage and comparison * with a regular 'get' method. * @@ -139,7 +139,7 @@ define([ * which matches specified search criteria. * * @param {Object} data - Data object where to perform a lookup. - * @param {(String|Object|Function)} query - Seach criteria. + * @param {(String|Object|Function)} query - Search criteria. * @param {Boolean} findAll - Flag that defines whether to * search for all applicable items or to stop on a first found entry. * @returns {Array|Object|*} @@ -322,8 +322,8 @@ define([ /** * Creates a wrapper function over the provided search query - * in order to provide somehow more convinient access to the - * registrie's items. + * in order to provide somehow more convenient access to the + * registry's items. * * @param {(String|Object|Function)} query - Search criteria. * See 'get' method for the syntax examples. From 656191d97a238e7f5c4dec9165858f439349bfa7 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Fri, 22 Jun 2018 19:04:10 +0530 Subject: [PATCH 0800/1171] Remove unnecessary translation of HTML tags --- .../Block/Adminhtml/Form/Renderer/Config/YearRange.php | 9 ++++----- app/code/Magento/Catalog/i18n/en_US.csv | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Form/Renderer/Config/YearRange.php b/app/code/Magento/Catalog/Block/Adminhtml/Form/Renderer/Config/YearRange.php index 0026e52e039ef..cd6c5021f0cc9 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Form/Renderer/Config/YearRange.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Form/Renderer/Config/YearRange.php @@ -32,10 +32,9 @@ protected function _getElementHtml(AbstractElement $element) $from = $element->setValue(isset($values[0]) ? $values[0] : null)->getElementHtml(); $to = $element->setValue(isset($values[1]) ? $values[1] : null)->getElementHtml(); - return __( - '<label class="label"><span>from</span></label>' - ) . $from . __( - '<label class="label"><span>to</span></label>' - ) . $to; + return '<label class="label"><span>' . __('from') . '</span></label>' + . $from . + '<label class="label"><span>' . __('to') . '</span></label>' + . $to; } } diff --git a/app/code/Magento/Catalog/i18n/en_US.csv b/app/code/Magento/Catalog/i18n/en_US.csv index a399f67e3130f..f2a3ab8f83f24 100644 --- a/app/code/Magento/Catalog/i18n/en_US.csv +++ b/app/code/Magento/Catalog/i18n/en_US.csv @@ -13,8 +13,8 @@ Position,Position Day,Day Month,Month Year,Year -"<label class=""label""><span>from</span></label>","<label class=""label""><span>from</span></label>" -"<label class=""label""><span>to</span></label>","<label class=""label""><span>to</span></label>" +from,from +to,to [GLOBAL],[GLOBAL] [WEBSITE],[WEBSITE] "[STORE VIEW]","[STORE VIEW]" From c0899ae7f2f0466dd89beafd4da6392ef4bdb572 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 8 Aug 2018 10:05:51 +0300 Subject: [PATCH 0801/1171] Fixed less code style issues --- .../web/css/source/_module.less | 5 +- .../module/components/_currency-addon.less | 16 +++---- .../web/css/source/_module.less | 46 ++++++++++--------- .../css/source/module/_staging-preview.less | 2 +- .../backend/web/css/source/_actions.less | 20 ++++---- .../backend/web/css/source/_reset.less | 12 ++--- .../css/source/actions/_actions-dropdown.less | 3 ++ .../source/components/_file-insertion.less | 2 +- .../css/source/components/_media-gallery.less | 4 +- .../web/css/source/components/_popups.less | 2 +- .../web/css/source/forms/_controls.less | 30 ++++-------- .../backend/web/css/source/forms/_temp.less | 4 ++ .../Magento/luma/web/css/source/_extends.less | 4 +- 13 files changed, 77 insertions(+), 73 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Analytics/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Analytics/web/css/source/_module.less index cf1275c477c2f..6d21462d753ca 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Analytics/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Analytics/web/css/source/_module.less @@ -144,7 +144,7 @@ .config-vertical-title { clear: both; - color: #303030; + color: rgb(48, 48, 48); font-size: 1.7rem; font-weight: 600; letter-spacing: .025em; @@ -158,6 +158,9 @@ margin-top: 1rem; } +/** + * @codingStandardsIgnoreStart + */ #row_analytics_general_vertical { >td.config-vertical-label { >label.admin__field-label { diff --git a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less index d6fd3ed06599f..659b1fa811db1 100644 --- a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less +++ b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less @@ -18,7 +18,7 @@ // _____________________________________________ .currency-addon { - border: 1px solid #adadad; + border: 1px solid rgb(173,173,173); position: relative; display: -webkit-inline-flex; display: -ms-inline-flexbox; @@ -52,27 +52,27 @@ } label.error { + left: 0; position: absolute; top: 33px; - left: 0; } .currency-symbol { + -webkit-flex-basis: auto; + -webkit-flex-grow: 0; + -webkit-flex-shrink: 0; border: solid @currency-addon-symbol__border-color; border-width: 0; box-sizing: border-box; color: @currency-addon-symbol__color; + flex-basis: auto; + flex-grow: 0; + flex-shrink: 0; height: @currency-addon-symbol__height; order: 0; padding: 7px 0 0 @indent__xs; position: static; transition: @smooth__border-color; - -webkit-flex-basis: auto; - flex-basis: auto; - -webkit-flex-grow: 0; - flex-grow: 0; - -webkit-flex-shrink: 0; - flex-shrink: 0; z-index: 1; } diff --git a/app/design/adminhtml/Magento/backend/Magento_ReleaseNotification/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_ReleaseNotification/web/css/source/_module.less index 3e2e264f40740..6f1e4224fa7a5 100644 --- a/app/design/adminhtml/Magento/backend/Magento_ReleaseNotification/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_ReleaseNotification/web/css/source/_module.less @@ -19,15 +19,8 @@ } .modal-inner-wrap { - -webkit-transform: translateX(0); - transform: translateX(0); - -webkit-transition: -webkit-transform 0s; - transition: transform 0s; - height: 50rem; - max-width: 75rem; - margin-top: 13rem; - - .modal-content, .modal-header { + .modal-content, + .modal-header { padding-left: 4rem; padding-right: 4rem; @@ -35,17 +28,25 @@ display: none; } } + + -webkit-transform: translateX(0); + -webkit-transition: -webkit-transform 0s; + height: 50rem; + transition: transform 0s; + transform: translateX(0); + margin-top: 13rem; + max-width: 75rem; } .admin__fieldset { - padding: 0; + padding: 0; } } .release-notification-title-with-image { - padding: 1.5rem 0 2rem 7rem; - background-size: 55px 49.08px; background-repeat: no-repeat; + background-size: 55px 49.08px; + padding: 1.5rem 0 2rem 7rem; } .release-notification-text { @@ -68,40 +69,41 @@ } } -.release-notification-button-next, .release-notification-button-back { +.release-notification-button-next, +.release-notification-button-back { + bottom: 4rem; display: block; float: right; position: absolute; - bottom: 4rem; } .release-notification-button-next { .lib-button-as-link(); - right: 4rem; font-weight: 400; + right: 4rem; } .release-notification-button-back { .lib-button-as-link(); - left: 4rem; font-weight: 400; + left: 4rem; } .highlight-item { - padding: 0 0 2rem 8.5rem; - margin-left: 1rem; - background-size: 65px 58px; - background-repeat: no-repeat; - h3 { margin: 0; span { - font-style: @font-style__emphasis; font-size: @font-size__s; + font-style: @font-style__emphasis; font-weight: @font-weight__light; } } + + background-size: 65px 58px; + background-repeat: no-repeat; + padding: 0 0 2rem 8.5rem; + margin-left: 1rem; } .highlight-item-no-image { diff --git a/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less b/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less index 99aa5fdc04e71..3e1f4e75031d2 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less +++ b/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less @@ -366,7 +366,7 @@ // Generic data grid .admin__data-grid-outer-wrap { border-top: 1px solid @staging-preview-table-dark__border-color; - max-height: 400px; // ToDO: remove after JS adjustment implemented + max-height: 400px; // ToDO remove after JS adjustment implemented overflow-y: auto; padding: 15px @indent__s 0 0; } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/_actions.less b/app/design/adminhtml/Magento/backend/web/css/source/_actions.less index 4da070f84d666..886bbcc29a3b9 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/_actions.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/_actions.less @@ -23,8 +23,15 @@ } .abs-action-pattern { + &[disabled], + &.disabled { + cursor: default; + opacity: @disabled__opacity; + pointer-events: none; + } + border: @button__border-size @button__border-style; - border-radius: 0; // ToDo UI: Delete with admin scope + border-radius: 0; // ToDo UI Delete with admin scope display: inline-block; font-family: @button__font-family; font-size: @button__font-size; @@ -33,13 +40,6 @@ padding: @button__padding-top @button__padding-horizontal @button__padding-bottom; text-align: center; vertical-align: baseline; - - &[disabled], - &.disabled { - cursor: default; - opacity: @disabled__opacity; - pointer-events: none; - } } .abs-action-l { @@ -401,7 +401,7 @@ button { left: 0; list-style: none; margin: 2px 0 0; // Action box-shadow + 1px indent - min-width: 0; // ToDo UI: Should be deleted with old styles + min-width: 0; // ToDo UI Should be deleted with old styles padding: 0; position: absolute; right: 0; @@ -412,7 +412,7 @@ button { } > li { - border: none; // ToDo UI: Should be deleted with old styles + border: none; // ToDo UI Should be deleted with old styles display: block; padding: 0; transition: background-color .1s linear; diff --git a/app/design/adminhtml/Magento/backend/web/css/source/_reset.less b/app/design/adminhtml/Magento/backend/web/css/source/_reset.less index 4ac9a1d82a063..51de756bff16c 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/_reset.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/_reset.less @@ -11,8 +11,8 @@ html { // Prevent iOS text size adjust after orientation change, without disabling user zoom. -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; - text-size-adjust: 100%; box-sizing: border-box; + text-size-adjust: 100%; } * { @@ -106,7 +106,7 @@ abbr { } } -// Address style set to 'bolder' in Firefox 4+, Safari, and Chrome. +// Address style set to 'bolder' in Firefox 4 and later, Safari, and Chrome. b, strong { font-weight: bold; @@ -205,9 +205,9 @@ input, optgroup, select, textarea { - color: inherit; // Correct color not being inherited. Known issue: affects color of disabled elements. + color: inherit; // Correct color not being inherited. Known issue affects color of disabled elements. font: inherit; // Correct font properties not being inherited. - margin: 0; // Address margins set differently in Firefox 4+, Safari, and Chrome. + margin: 0; // Address margins set differently in Firefox 4 and later, Safari, and Chrome. } // Address 'overflow' set to 'hidden' in IE 8/9/10/11. @@ -243,7 +243,7 @@ html input[disabled] { cursor: default; } -// Remove inner padding and border in Firefox 4+. +// Remove inner padding and border in Firefox 4 and later. button, input { &::-moz-focus-inner { @@ -252,7 +252,7 @@ input { } } -// Address Firefox 4+ setting 'line-height' on 'input' using '!important' in the UA stylesheet. +// Address Firefox 4 and later setting 'line-height' on 'input' using '!important' in the UA stylesheet. input { line-height: normal; } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less index a50d0892d73cd..cd089232412dc 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less @@ -168,6 +168,9 @@ width: auto; z-index: 1; + /** + * @codingStandardsIgnoreStart + */ &:hover { &:extend(.abs-form-control-pattern:hover); } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_file-insertion.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_file-insertion.less index cd17ed69008eb..88962a1019a19 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_file-insertion.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_file-insertion.less @@ -23,10 +23,10 @@ input { -moz-transform: none; - transform: none; border: none; opacity: 1; position: static; + transform: none; } } } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less index 32dd4004f427e..9a5f35e4ede90 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less @@ -159,7 +159,7 @@ } .file-row { - background: @color-white url("@{baseDir}mui/images/ajax-loader-big.gif") no-repeat 50% 50%; // TODO UI: remove after new uploader implemented + background: @color-white url("@{baseDir}mui/images/ajax-loader-big.gif") no-repeat 50% 50%; bottom: 0; height: 100%; left: 0; @@ -323,8 +323,8 @@ .image-panel-preview { -ms-flex: 1; -webkit-box-flex: 1; - box-flex: 1; -webkit-flex: 1; + box-flex: 1; flex: 1; } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less index ac64eae5dcf3e..d1d1ff9891634 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less @@ -280,10 +280,10 @@ input { -moz-transform: none; - transform: none; border: none; opacity: 1; position: static; + transform: none; } } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less index c28d3d045aa57..bb51abaa0f156 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less @@ -102,19 +102,6 @@ } } -// ToDo UI: add month and date styles -// .admin__control-select-month { -// width: 140px; -// } - -// .admin__control-select-year { -// width: 103px; -// } - -// .admin__control-cvn { -// width: 3em; -// } - option:empty { display: none; } @@ -147,21 +134,24 @@ option:empty { &:before { &:extend(.abs-form-control-pattern); - content:''; - left: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 0; - .admin__control-file:active + &, .admin__control-file:focus + & { + /** + * @codingStandardsIgnoreStart + */ &:extend(.abs-form-control-pattern:focus); } .admin__control-file[disabled] + & { &:extend(.abs-form-control-pattern[disabled]); } + + content: ''; + left: 0; + position: absolute; + top: 0; + width: 100%; + z-index: 0; } } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less index 945f81292c19f..df031bebeb24a 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less @@ -408,6 +408,9 @@ label.mage-error { width: 16px; z-index: 1; + /** + * @codingStandardsIgnoreStart + */ &:before { &:extend(.admin__control-checkbox + label:before); left: 0; @@ -434,6 +437,7 @@ label.mage-error { &:before { &:extend(.admin__control-checkbox:checked + label:before); } + // @codingStandardsIgnoreEnd } &._indeterminate { diff --git a/app/design/frontend/Magento/luma/web/css/source/_extends.less b/app/design/frontend/Magento/luma/web/css/source/_extends.less index 1292188102868..7c9f5b7a65ab4 100644 --- a/app/design/frontend/Magento/luma/web/css/source/_extends.less +++ b/app/design/frontend/Magento/luma/web/css/source/_extends.less @@ -7,6 +7,8 @@ // List default styles reset // --------------------------------------------- +@_column-number: 1; + & when (@media-common = true) { .abs-reset-list { .lib-list-reset-styles(); @@ -705,7 +707,7 @@ // --------------------------------------------- @abs-form-field-revert-column-1: { - .lib-form-field-column-number(@_column-number: 1); + .lib-form-field-column-number(@_column-number); }; .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { From af43bdf7b3ee6d718b98b778689d2cd1bc0aac5e Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 8 Aug 2018 10:49:05 +0300 Subject: [PATCH 0802/1171] Fixed code style issues --- app/code/Magento/Cms/Helper/Page.php | 2 +- app/code/Magento/Cms/Test/Unit/Helper/PageTest.php | 3 +++ app/code/Magento/Directory/Model/AllowedCountries.php | 2 +- .../Directory/Test/Unit/Model/AllowedCountriesTest.php | 9 +++++++++ ...GoogleAnalyticsOnOrderSuccessPageViewObserverTest.php | 9 +++++++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Cms/Helper/Page.php b/app/code/Magento/Cms/Helper/Page.php index a421f89e07d6a..abd260b260b93 100644 --- a/app/code/Magento/Cms/Helper/Page.php +++ b/app/code/Magento/Cms/Helper/Page.php @@ -116,7 +116,7 @@ public function __construct( * Return result CMS page * * @param Action $action - * @param null $pageId + * @param int $pageId * @return \Magento\Framework\View\Result\Page|bool */ public function prepareResultPage(Action $action, $pageId = null) diff --git a/app/code/Magento/Cms/Test/Unit/Helper/PageTest.php b/app/code/Magento/Cms/Test/Unit/Helper/PageTest.php index 6034ec890f938..19f3b113c5e2c 100644 --- a/app/code/Magento/Cms/Test/Unit/Helper/PageTest.php +++ b/app/code/Magento/Cms/Test/Unit/Helper/PageTest.php @@ -118,6 +118,9 @@ class PageTest extends \PHPUnit\Framework\TestCase */ private $httpRequestMock; + /** + * Test Setup + */ protected function setUp() { $this->actionMock = $this->getMockBuilder(\Magento\Framework\App\Action\Action::class) diff --git a/app/code/Magento/Directory/Model/AllowedCountries.php b/app/code/Magento/Directory/Model/AllowedCountries.php index db83e4e8920c5..b9f2d829dd1a6 100644 --- a/app/code/Magento/Directory/Model/AllowedCountries.php +++ b/app/code/Magento/Directory/Model/AllowedCountries.php @@ -46,8 +46,8 @@ public function __construct( /** * Retrieve all allowed countries for scope or scopes * - * @param string | null $scopeCode * @param string $scope + * @param string|null $scopeCode * @return array * @since 100.1.2 */ diff --git a/app/code/Magento/Directory/Test/Unit/Model/AllowedCountriesTest.php b/app/code/Magento/Directory/Test/Unit/Model/AllowedCountriesTest.php index 97a49aa50dc10..95a429c1d7597 100644 --- a/app/code/Magento/Directory/Test/Unit/Model/AllowedCountriesTest.php +++ b/app/code/Magento/Directory/Test/Unit/Model/AllowedCountriesTest.php @@ -41,6 +41,9 @@ public function setUp() ); } + /** + * Test for getAllowedCountries + */ public function testGetAllowedCountriesWithEmptyFilter() { $website1 = $this->createMock(WebsiteInterface::class); @@ -58,6 +61,9 @@ public function testGetAllowedCountriesWithEmptyFilter() $this->assertEquals(['AM' => 'AM'], $this->allowedCountriesReader->getAllowedCountries()); } + /** + * Test for getAllowedCountries + */ public function testGetAllowedCountries() { $this->scopeConfigMock->expects($this->once()) @@ -71,6 +77,9 @@ public function testGetAllowedCountries() ); } + /** + * Test for getAllowedCountries + */ public function testGetAllowedCountriesDefaultScope() { $this->storeManagerMock->expects($this->never()) diff --git a/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php b/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php index b3ed16e0c94a2..a883b0dab8c3b 100644 --- a/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php +++ b/app/code/Magento/GoogleAnalytics/Test/Unit/Observer/SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest.php @@ -49,6 +49,9 @@ class SetGoogleAnalyticsOnOrderSuccessPageViewObserverTest extends TestCase */ private $orderSuccessObserver; + /** + * Test setUp + */ protected function setUp() { $this->googleAnalyticsDataMock = $this->getMockBuilder(GaDataHelper::class) @@ -75,6 +78,9 @@ protected function setUp() ); } + /** + * Observer test + */ public function testExecuteWithNoOrderIds() { $this->observerMock->expects($this->once()) @@ -92,6 +98,9 @@ public function testExecuteWithNoOrderIds() $this->orderSuccessObserver->execute($this->observerMock); } + /** + * Observer test + */ public function testExecuteWithOrderIds() { $blockMock = $this->getMockBuilder(AbstractBlock::class) From ad286026c5dde49f6f62d63e99032b43b00d2944 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 8 Aug 2018 11:19:13 +0300 Subject: [PATCH 0803/1171] Fixed typo issue --- .../Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php index f2ec969eb83fe..1fdd9759f5045 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php @@ -189,7 +189,7 @@ public function testExecuteRedirectBack() $this->createRedirect(); $this->getOrderId($this->orderId); $this->getUnavailableProducts([1, 3]); - $this->messageManagerMock->expects($this->any())->method('addErrorMessageMessage'); + $this->messageManagerMock->expects($this->any())->method('addErrorMessage'); $this->setPath('sales/order/view', ['order_id' => $this->orderId]); $this->assertInstanceOf(Redirect::class, $this->reorder->execute()); From 9a701c78e5e1ebbaf1a3dd127b3dd915679972c9 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 8 Aug 2018 11:35:43 +0300 Subject: [PATCH 0804/1171] Fixed cyclomatic complexity --- .../Adminhtml/Order/Status/Save.php | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php index 57ac3776fc3e9..6cedae096f2f8 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php @@ -61,12 +61,22 @@ public function execute() ); } $this->_getSession()->setFormData($data); - if ($isNew) { - return $resultRedirect->setPath('sales/*/new'); - } else { - return $resultRedirect->setPath('sales/*/edit', ['status' => $this->getRequest()->getParam('status')]); - } + return $this->getRedirect($resultRedirect, $isNew); } return $resultRedirect->setPath('sales/*/'); } + + /** + * @param \Magento\Backend\Model\View\Result\Redirect $resultRedirect + * @param $isNew + * @return \Magento\Backend\Model\View\Result\Redirect + */ + private function getRedirect(\Magento\Backend\Model\View\Result\Redirect $resultRedirect, $isNew) + { + if ($isNew) { + return $resultRedirect->setPath('sales/*/new'); + } else { + return $resultRedirect->setPath('sales/*/edit', ['status' => $this->getRequest()->getParam('status')]); + } + } } From a07835ac68c4cfb8af53ae8f14c2362c89f4513d Mon Sep 17 00:00:00 2001 From: olysenko <olysenko@magento.com> Date: Wed, 8 Aug 2018 11:48:26 +0300 Subject: [PATCH 0805/1171] MAGETWO-93188: Product date attribute: An error displays for default date greater than 12 with local using dd/mm/yyyy --- .../Model/Customer/Plugin/AjaxLoginTest.php | 9 +++++++++ .../Product/Initialization/Helper.php | 2 +- .../Product/Initialization/HelperTest.php | 3 +++ .../Category/Attribute/Backend/ImageTest.php | 18 ++++++++++++++++++ .../Magento/Eav/Model/Entity/AttributeTest.php | 16 ++++++++++++++++ 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php b/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php index bda0d9705d3df..ec2a49f3fc566 100644 --- a/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php @@ -58,6 +58,9 @@ class AjaxLoginTest extends \PHPUnit\Framework\TestCase */ protected $model; + /** + * @inheritdoc + */ protected function setUp() { $this->sessionManagerMock = $this->createPartialMock(\Magento\Checkout\Model\Session::class, ['setUsername']); @@ -91,6 +94,9 @@ protected function setUp() ); } + /** + * Test aroundExecute. + */ public function testAroundExecute() { $username = 'name'; @@ -123,6 +129,9 @@ public function testAroundExecute() $this->assertEquals('result', $this->model->aroundExecute($this->loginControllerMock, $closure)); } + /** + * Test aroundExecuteIncorrectCaptcha. + */ public function testAroundExecuteIncorrectCaptcha() { $username = 'name'; diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index 7153f9fd0f3f9..d82f4a04fb252 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -104,7 +104,7 @@ class Helper * @param \Magento\Backend\Helper\Js $jsHelper * @param \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter * @param CustomOptionFactory|null $customOptionFactory - * @param ProductLinkFactory |null $productLinkFactory + * @param ProductLinkFactory|null $productLinkFactory * @param ProductRepositoryInterface|null $productRepository * @param LinkTypeProvider|null $linkTypeProvider * @param AttributeFilter|null $attributeFilter diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php index aed87f918ebb8..ff44a91a64998 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php @@ -95,6 +95,9 @@ class HelperTest extends \PHPUnit\Framework\TestCase */ protected $attributeFilterMock; + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = new ObjectManager($this); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php index 3fbe0c198a027..f1672d842de4e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php @@ -34,6 +34,9 @@ class ImageTest extends \PHPUnit\Framework\TestCase */ private $filesystem; + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -138,6 +141,9 @@ public function testBeforeSaveValueInvalid($value) $this->assertEquals('', $object->getTestAttribute()); } + /** + * Test beforeSaveAttributeFileName. + */ public function testBeforeSaveAttributeFileName() { $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class); @@ -154,6 +160,9 @@ public function testBeforeSaveAttributeFileName() $this->assertEquals('test123.jpg', $object->getTestAttribute()); } + /** + * Test beforeSaveAttributeFileNameOutsideOfCategoryDir. + */ public function testBeforeSaveAttributeFileNameOutsideOfCategoryDir() { $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class, [ @@ -186,6 +195,9 @@ public function testBeforeSaveAttributeFileNameOutsideOfCategoryDir() ); } + /** + * Test beforeSaveTemporaryAttribute. + */ public function testBeforeSaveTemporaryAttribute() { $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class); @@ -204,6 +216,9 @@ public function testBeforeSaveTemporaryAttribute() ], $object->getData('_additional_data_test_attribute')); } + /** + * Test beforeSaveAttributeStringValue. + */ public function testBeforeSaveAttributeStringValue() { $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class); @@ -304,6 +319,9 @@ public function testAfterSaveWithoutAdditionalData($value) $model->afterSave($object); } + /** + * Test afterSaveWithExceptions. + */ public function testAfterSaveWithExceptions() { $model = $this->setUpModelForAfterSave(); diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php index 11696aa1e0b98..2750e2a768aab 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php @@ -28,6 +28,9 @@ class AttributeTest extends \PHPUnit\Framework\TestCase */ private $_localeResolver; + /** + * {@inheritdoc} + */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); @@ -35,6 +38,9 @@ protected function setUp() $this->_localeResolver = $this->objectManager->get(ResolverInterface::class); } + /** + * {@inheritdoc} + */ protected function tearDown() { $this->attribute = null; @@ -60,6 +66,11 @@ public function testBeforeSave($defaultValue, $backendType, $locale, $expected) $this->assertEquals($expected, $this->attribute->getDefaultValue()); } + /** + * Data provider for beforeSaveData. + * + * @return array + */ public function beforeSaveDataProvider() { return [ @@ -91,6 +102,11 @@ public function testBeforeSaveErrorData($defaultValue, $backendType, $locale, $e $this->expectExceptionMessage($expected); } + /** + * Data provider for beforeSaveData with error result. + * + * @return array + */ public function beforeSaveErrorDataDataProvider() { return [ From ab2fe010eadf4ddab5adbb487047f90db7eebeaa Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 8 Aug 2018 12:26:25 +0300 Subject: [PATCH 0806/1171] Fixed code style issue --- .../Directory/Test/Unit/Model/AllowedCountriesTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Directory/Test/Unit/Model/AllowedCountriesTest.php b/app/code/Magento/Directory/Test/Unit/Model/AllowedCountriesTest.php index 95a429c1d7597..c0d549ee11d6d 100644 --- a/app/code/Magento/Directory/Test/Unit/Model/AllowedCountriesTest.php +++ b/app/code/Magento/Directory/Test/Unit/Model/AllowedCountriesTest.php @@ -8,7 +8,6 @@ use Magento\Directory\Model\AllowedCountries; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\Data\Collection\AbstractDb; use Magento\Store\Api\Data\WebsiteInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; @@ -30,6 +29,9 @@ class AllowedCountriesTest extends \PHPUnit\Framework\TestCase */ private $allowedCountriesReader; + /** + * Test setUp + */ public function setUp() { $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); From 007a45bb0965042f89b757e819660de353c630c3 Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv <rkostiv@magento.com> Date: Wed, 8 Aug 2018 12:30:01 +0300 Subject: [PATCH 0807/1171] MAGETWO-94060: [2.3.x] Unlink CatalogWidget from EAV indexer --- app/code/Magento/Rule/Model/Condition/Sql/Builder.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php index 7ea70d5478e10..c32469c0f4691 100644 --- a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php +++ b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php @@ -166,9 +166,9 @@ protected function _getMappedSqlCondition( $bindValue = $condition->getBindArgumentValue(); $expression = $value . $this->_connection->quoteInto($sql, $bindValue); - // values for multiselect attributes can be saved in comma separated format + // values for multiselect attributes can be saved in comma-separated format // below is a solution for matching such conditions with selected values - if (in_array($conditionOperator, ['()', '{}']) && is_array($bindValue)) { + if (is_array($bindValue) && \in_array($conditionOperator, ['()', '{}'], true)) { foreach ($bindValue as $item) { $expression .= $this->_connection->quoteInto( " OR (FIND_IN_SET (?, {$this->_connection->quoteIdentifier($argument)}) > 0)", @@ -184,10 +184,11 @@ protected function _getMappedSqlCondition( /** * @param Combine $combine - * @param string $value - * @param bool $isDefaultStoreUsed + * @param string $value + * @param bool $isDefaultStoreUsed * @return string * @SuppressWarnings(PHPMD.NPathComplexity) + * @throws \Magento\Framework\Exception\LocalizedException */ protected function _getMappedSqlCombination( Combine $combine, From d1406d212f3a1ab39e60f71dbe8080eab908a1a1 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Wed, 8 Aug 2018 13:21:18 +0300 Subject: [PATCH 0808/1171] MAGETWO-93692: [2.3] Wildcard values for coupon codes --- .../Model/ResourceModel/Rule/Collection.php | 7 ++- .../Rule/Action/Discount/CartFixedTest.php | 30 ++++++++++++ .../_files/coupon_code_with_wildcard.php | 45 ++++++++++++++++++ .../coupon_code_with_wildcard_rollback.php | 46 +++++++++++++++++++ 4 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard.php create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard_rollback.php diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index 54b50dbdf38db..59f24fa8b6e03 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -6,6 +6,7 @@ namespace Magento\SalesRule\Model\ResourceModel\Rule; +use Magento\Framework\DB\Select; use Magento\Framework\Serialize\Serializer\Json; use Magento\Quote\Model\Quote\Address; @@ -209,7 +210,9 @@ public function setValidationFilter( $andWhereCondition = implode(' AND ', $andWhereConditions); $select->where( - $noCouponWhereCondition . ' OR ((' . $orWhereCondition . ') AND ' . $andWhereCondition . ')' + $noCouponWhereCondition . ' OR ((' . $orWhereCondition . ') AND ' . $andWhereCondition . ')', + null, + Select::TYPE_CONDITION ); } else { $this->addFieldToFilter( @@ -320,7 +323,7 @@ public function addAttributeInConditionFilter($attributeCode) $this->getSelect()->where( sprintf('(%s OR %s)', $cCond, $aCond), null, - \Magento\Framework\DB\Select::TYPE_CONDITION + Select::TYPE_CONDITION ); return $this; diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php index e601e2dd59232..694310f2cbc89 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php @@ -93,6 +93,36 @@ public function applyFixedDiscountDataProvider(): array ]; } + /** + * Tests that coupon with wildcard symbols in code can be successfully applied. + * + * @magentoDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php + */ + public function testCouponCodeWithWildcard() + { + $expectedDiscount = '-5.00'; + $couponCode = '2?ds5!2d'; + $cartId = $this->cartManagement->createEmptyCart(); + $productPrice = 10; + + $product = $this->createProduct($productPrice); + + /** @var CartItemInterface $quoteItem */ + $quoteItem = Bootstrap::getObjectManager()->create(CartItemInterface::class); + $quoteItem->setQuoteId($cartId); + $quoteItem->setProduct($product); + $quoteItem->setQty(1); + $this->cartItemRepository->save($quoteItem); + + $this->couponManagement->set($cartId, $couponCode); + + /** @var GuestCartTotalRepositoryInterface $cartTotalRepository */ + $cartTotalRepository = Bootstrap::getObjectManager()->get(GuestCartTotalRepositoryInterface::class); + $total = $cartTotalRepository->get($cartId); + + $this->assertEquals($expectedDiscount, $total->getBaseDiscountAmount()); + } + /** * Returns simple product with given price. * diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard.php new file mode 100644 index 0000000000000..9005284f984cf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Model\GroupManagement; +use Magento\SalesRule\Api\CouponRepositoryInterface; +use Magento\SalesRule\Model\Coupon; +use Magento\SalesRule\Model\Rule; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Rule $salesRule */ +$salesRule = $objectManager->create(Rule::class); +$salesRule->setData( + [ + 'name' => '5$ fixed discount on whole cart', + 'is_active' => 1, + 'customer_group_ids' => [GroupManagement::NOT_LOGGED_IN_ID], + 'coupon_type' => Rule::COUPON_TYPE_SPECIFIC, + 'conditions' => [], + 'simple_action' => Rule::CART_FIXED_ACTION, + 'discount_amount' => 5, + 'discount_step' => 0, + 'stop_rules_processing' => 1, + 'website_ids' => [ + $objectManager->get(StoreManagerInterface::class)->getWebsite()->getId(), + ], + ] +); +$objectManager->get(\Magento\SalesRule\Model\ResourceModel\Rule::class)->save($salesRule); + +// Create coupon and assign "15$ fixed discount" rule to this coupon. +$coupon = $objectManager->create(Coupon::class); +$coupon->setRuleId($salesRule->getId()) + ->setCode('2?ds5!2d') + ->setType(0); + +/** @var CouponRepositoryInterface $couponRepository */ +$couponRepository = $objectManager->get(CouponRepositoryInterface::class); +$couponRepository->save($coupon); diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard_rollback.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard_rollback.php new file mode 100644 index 0000000000000..776c302210351 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard_rollback.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\SalesRule\Api\CouponRepositoryInterface; +use Magento\SalesRule\Api\RuleRepositoryInterface; +use Magento\SalesRule\Model\Coupon; +use Magento\SalesRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Rule $salesRule */ +$salesRule = getSalesRule('5$ fixed discount on whole cart'); +if ($salesRule !== null) { + /** @var RuleRepositoryInterface $ruleRepository */ + $ruleRepository = $objectManager->get(RuleRepositoryInterface::class); + $ruleRepository->deleteById($salesRule->getRuleId()); +} + +$coupon = $objectManager->create(Coupon::class); +$coupon->loadByCode('2?ds5!2d'); +if ($coupon->getCouponId()) { + /** @var CouponRepositoryInterface $couponRepository */ + $couponRepository = $objectManager->get(CouponRepositoryInterface::class); + $couponRepository->deleteById($coupon->getCouponId()); +} + +function getSalesRule(string $name) +{ + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('name', $name) + ->create(); + + /** @var RuleRepositoryInterface $ruleRepository */ + $ruleRepository = Bootstrap::getObjectManager()->get(RuleRepositoryInterface::class); + $items = $ruleRepository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); +} From 99ee3ca9b308e76efed4a5d6b9ce393cf563e4b1 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel.ruf@ueberbit.de> Date: Fri, 20 Jul 2018 09:00:51 +0200 Subject: [PATCH 0809/1171] fix: remove disabled attribute on region list --- .../Magento/Checkout/view/frontend/web/js/region-updater.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/region-updater.js b/app/code/Magento/Checkout/view/frontend/web/js/region-updater.js index dacebd75c3c68..cf2a59cdba427 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/region-updater.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/region-updater.js @@ -188,6 +188,8 @@ define([ if (!this.options.optionalRegionAllowed) { //eslint-disable-line max-depth regionList.attr('disabled', 'disabled'); + } else { + regionList.removeAttr('disabled'); } } From f227808a3f0079db7e57aafc1cd088205affa9ba Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Wed, 8 Aug 2018 14:31:53 +0300 Subject: [PATCH 0810/1171] MAGETWO-94029: Issues with Multi website YouTube Videos --- .../Block/Product/View/Gallery.php | 5 +- .../Catalog/Product/Gallery/CreateHandler.php | 67 ++++++++++++++----- .../Catalog/Product/Gallery/ReadHandler.php | 16 ++--- 3 files changed, 62 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/ProductVideo/Block/Product/View/Gallery.php b/app/code/Magento/ProductVideo/Block/Product/View/Gallery.php index 2277aa980f66c..45c4925640a0c 100644 --- a/app/code/Magento/ProductVideo/Block/Product/View/Gallery.php +++ b/app/code/Magento/ProductVideo/Block/Product/View/Gallery.php @@ -9,9 +9,8 @@ * * @author Magento Core Team <core@magentocommerce.com> */ -namespace Magento\ProductVideo\Block\Product\View; -use Magento\Catalog\Model\Product\Image\UrlBuilder; +namespace Magento\ProductVideo\Block\Product\View; /** * @api @@ -93,6 +92,6 @@ public function getVideoSettingsJson() */ public function getOptionsMediaGalleryDataJson() { - return $this->jsonEncoder->encode([]); + return $this->jsonEncoder->encode([]); } } diff --git a/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php b/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php index ab3c8f3c5269a..db67b20c98cc2 100644 --- a/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\ProductVideo\Model\Plugin\Catalog\Product\Gallery; use Magento\ProductVideo\Model\Product\Attribute\Media\ExternalVideoEntryConverter; @@ -29,6 +30,7 @@ public function beforeExecute( \Magento\Catalog\Model\Product $product, array $arguments = [] ) { + /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */ $attribute = $mediaGalleryCreateHandler->getAttribute(); $mediaCollection = $this->getMediaEntriesDataCollection($product, $attribute); if (!empty($mediaCollection)) { @@ -36,7 +38,7 @@ public function beforeExecute( $mediaCollection = $this->addAdditionalStoreData($mediaCollection, $storeDataCollection); $product->setData( $attribute->getAttributeCode(), - $mediaCollection + $product->getData($attribute->getAttributeCode()) + $mediaCollection ); } } @@ -56,6 +58,10 @@ public function afterExecute( ); if (!empty($mediaCollection)) { + $newVideoCollection = $this->collectNewVideos($mediaCollection); + $this->saveVideoData($newVideoCollection, 0); + $this->saveAdditionalStoreData($newVideoCollection); + $videoDataCollection = $this->collectVideoData($mediaCollection); $this->saveVideoData($videoDataCollection, $product->getStoreId()); $this->saveAdditionalStoreData($videoDataCollection); @@ -167,10 +173,7 @@ protected function collectVideoData(array $mediaCollection) { $videoDataCollection = []; foreach ($mediaCollection as $item) { - if (!empty($item['media_type']) - && empty($item['removed']) - && $item['media_type'] == ExternalVideoEntryConverter::MEDIA_TYPE_CODE - ) { + if ($this->isVideoItem($item)) { $videoData = $this->extractVideoDataFromRowData($item); $videoDataCollection[] = $videoData; } @@ -199,11 +202,7 @@ protected function collectVideoEntriesIdsToAdditionalLoad(array $mediaCollection { $ids = []; foreach ($mediaCollection as $item) { - if (!empty($item['media_type']) - && empty($item['removed']) - && $item['media_type'] == ExternalVideoEntryConverter::MEDIA_TYPE_CODE - && isset($item['save_data_from']) - ) { + if ($this->isVideoItem($item) && isset($item['save_data_from'])) { $ids[] = $item['save_data_from']; } } @@ -215,18 +214,19 @@ protected function collectVideoEntriesIdsToAdditionalLoad(array $mediaCollection * @param array $data * @return array */ - protected function addAdditionalStoreData(array $mediaCollection, array $data) + protected function addAdditionalStoreData(array $mediaCollection, array $data): array { - foreach ($mediaCollection as &$mediaItem) { + $return = []; + foreach ($mediaCollection as $key => $mediaItem) { if (!empty($mediaItem['save_data_from'])) { $additionalData = $this->createAdditionalStoreDataCollection($data, $mediaItem['save_data_from']); if (!empty($additionalData)) { $mediaItem[self::ADDITIONAL_STORE_DATA_KEY] = $additionalData; } } + $return[$key] = $mediaItem; } - - return ['images' => $mediaCollection]; + return ['images' => $return]; } /** @@ -234,7 +234,7 @@ protected function addAdditionalStoreData(array $mediaCollection, array $data) * @param int $valueId * @return array */ - protected function createAdditionalStoreDataCollection(array $storeData, $valueId) + protected function createAdditionalStoreDataCollection(array $storeData, $valueId): array { $result = []; foreach ($storeData as $item) { @@ -246,4 +246,41 @@ protected function createAdditionalStoreDataCollection(array $storeData, $valueI return $result; } + + /** + * @param array $mediaCollection + * @return array + */ + private function collectNewVideos(array $mediaCollection): array + { + $return = []; + foreach ($mediaCollection as $item) { + if ($this->isVideoItem($item) && $this->isNewVideo($item)) { + $return[] = $this->extractVideoDataFromRowData($item); + } + } + return $return; + } + + /** + * @param $item + * @return bool + */ + private function isVideoItem($item): bool + { + return !empty($item['media_type']) + && empty($item['removed']) + && $item['media_type'] == ExternalVideoEntryConverter::MEDIA_TYPE_CODE; + } + + /** + * @param $item + * @return bool + */ + private function isNewVideo($item): bool + { + return !isset($item['video_url_default'], $item['video_title_default']) + || empty($item['video_url_default']) + || empty($item['video_title_default']); + } } diff --git a/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/ReadHandler.php b/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/ReadHandler.php index 6c534580c39d9..31d63efcf8cb0 100644 --- a/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/ReadHandler.php +++ b/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/ReadHandler.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\ProductVideo\Model\Plugin\Catalog\Product\Gallery; use Magento\ProductVideo\Model\Product\Attribute\Media\ExternalVideoEntryConverter; @@ -55,8 +56,8 @@ protected function collectVideoEntriesIds(array $mediaCollection) { $ids = []; foreach ($mediaCollection as $item) { - if ($item['media_type'] == ExternalVideoEntryConverter::MEDIA_TYPE_CODE - && !array_key_exists('video_url', $item) + if ($item['media_type'] === ExternalVideoEntryConverter::MEDIA_TYPE_CODE + && !isset($item['video_url']) ) { $ids[] = $item['value_id']; } @@ -72,7 +73,7 @@ protected function collectVideoEntriesIds(array $mediaCollection) protected function loadVideoDataById(array $ids, $storeId = null) { $mainTableAlias = $this->resourceModel->getMainTableAlias(); - $joinConditions = $mainTableAlias.'.value_id = store_value.value_id'; + $joinConditions = $mainTableAlias . '.value_id = store_value.value_id'; if (null !== $storeId) { $joinConditions = implode( ' AND ', @@ -138,10 +139,10 @@ protected function addVideoDataToMediaEntries(array $mediaCollection, array $dat protected function substituteNullsWithDefaultValues(array $rowData) { foreach ($this->getVideoProperties(false) as $key) { - if (empty($rowData[$key]) && !empty($rowData[$key.'_default'])) { - $rowData[$key] = $rowData[$key.'_default']; + if (empty($rowData[$key]) && !empty($rowData[$key . '_default'])) { + $rowData[$key] = $rowData[$key . '_default']; } - unset($rowData[$key.'_default']); + unset($rowData[$key . '_default']); } return $rowData; @@ -154,8 +155,7 @@ protected function substituteNullsWithDefaultValues(array $rowData) protected function getVideoProperties($withDbMapping = true) { $properties = $this->videoPropertiesDbMapping; - unset($properties['value_id']); - unset($properties['store_id']); + unset($properties['value_id'], $properties['store_id']); return $withDbMapping ? $properties : array_keys($properties); } From c101726cd7a49367d7d9def678753fab8a292536 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Wed, 8 Aug 2018 14:31:58 +0300 Subject: [PATCH 0811/1171] MAGETWO-93961: [2.3] Product URL rewrites get deleted in multi store views --- .../Observer/UrlRewriteHandler.php | 240 +++++++++++------- .../Fixtures/product_custom_url_key.php | 30 +++ .../product_custom_url_key_rollback.php | 21 ++ .../Observer/UrlRewriteHandlerTest.php | 111 ++++++++ .../_files/product_with_category.php | 2 +- 5 files changed, 314 insertions(+), 90 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandlerTest.php diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php index 9892465d1538a..18360dedf0693 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php @@ -3,27 +3,48 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogUrlRewrite\Observer; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider; +use Magento\CatalogUrlRewrite\Model\CategoryProductUrlPathGenerator; +use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; +use Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator; +use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\UrlRewrite\Model\MergeDataProvider; +use Magento\UrlRewrite\Model\MergeDataProviderFactory; +use Magento\UrlRewrite\Model\UrlPersistInterface; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class UrlRewriteHandler { /** - * @var \Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider + * @var ChildrenCategoriesProvider */ protected $childrenCategoriesProvider; /** - * @var \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator + * @var CategoryUrlRewriteGenerator */ protected $categoryUrlRewriteGenerator; /** - * @var \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator + * @var ProductUrlRewriteGenerator */ protected $productUrlRewriteGenerator; /** - * @var \Magento\UrlRewrite\Model\UrlPersistInterface + * @var UrlPersistInterface */ protected $urlPersist; @@ -33,44 +54,51 @@ class UrlRewriteHandler protected $isSkippedProduct; /** - * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory + * @var CollectionFactory */ protected $productCollectionFactory; /** - * @var \Magento\CatalogUrlRewrite\Model\CategoryProductUrlPathGenerator + * @var CategoryProductUrlPathGenerator */ private $categoryBasedProductRewriteGenerator; /** - * @var \Magento\UrlRewrite\Model\MergeDataProvider + * @var MergeDataProvider */ private $mergeDataProviderPrototype; /** - * @var \Magento\Framework\Serialize\Serializer\Json + * @var Json */ private $serializer; /** - * @param \Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider $childrenCategoriesProvider - * @param \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator - * @param \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator $productUrlRewriteGenerator - * @param \Magento\UrlRewrite\Model\UrlPersistInterface $urlPersist - * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory - * @param \Magento\CatalogUrlRewrite\Model\CategoryProductUrlPathGenerator $categoryBasedProductRewriteGenerator - * @param \Magento\UrlRewrite\Model\MergeDataProviderFactory|null $mergeDataProviderFactory - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @var ProductScopeRewriteGenerator + */ + private $productScopeRewriteGenerator; + + /** + * @param ChildrenCategoriesProvider $childrenCategoriesProvider + * @param CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator + * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator + * @param UrlPersistInterface $urlPersist + * @param CollectionFactory $productCollectionFactory + * @param CategoryProductUrlPathGenerator $categoryBasedProductRewriteGenerator + * @param MergeDataProviderFactory|null $mergeDataProviderFactory + * @param Json|null $serializer + * @param ProductScopeRewriteGenerator|null $productScopeRewriteGenerator */ public function __construct( - \Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider $childrenCategoriesProvider, - \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator, - \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator $productUrlRewriteGenerator, - \Magento\UrlRewrite\Model\UrlPersistInterface $urlPersist, - \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory, - \Magento\CatalogUrlRewrite\Model\CategoryProductUrlPathGenerator $categoryBasedProductRewriteGenerator, - \Magento\UrlRewrite\Model\MergeDataProviderFactory $mergeDataProviderFactory = null, - \Magento\Framework\Serialize\Serializer\Json $serializer = null + ChildrenCategoriesProvider $childrenCategoriesProvider, + CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator, + ProductUrlRewriteGenerator $productUrlRewriteGenerator, + UrlPersistInterface $urlPersist, + CollectionFactory $productCollectionFactory, + CategoryProductUrlPathGenerator $categoryBasedProductRewriteGenerator, + MergeDataProviderFactory $mergeDataProviderFactory = null, + Json $serializer = null, + ProductScopeRewriteGenerator $productScopeRewriteGenerator = null ) { $this->childrenCategoriesProvider = $childrenCategoriesProvider; $this->categoryUrlRewriteGenerator = $categoryUrlRewriteGenerator; @@ -79,58 +107,29 @@ public function __construct( $this->productCollectionFactory = $productCollectionFactory; $this->categoryBasedProductRewriteGenerator = $categoryBasedProductRewriteGenerator; - if (!isset($mergeDataProviderFactory)) { - $mergeDataProviderFactory = \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\UrlRewrite\Model\MergeDataProviderFactory::class - ); - } - + $objectManager = ObjectManager::getInstance(); + $mergeDataProviderFactory = $mergeDataProviderFactory ?: $objectManager->get(MergeDataProviderFactory::class); $this->mergeDataProviderPrototype = $mergeDataProviderFactory->create(); - - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\Serialize\Serializer\Json::class - ); + $this->serializer = $serializer ?: $objectManager->get(Json::class); + $this->productScopeRewriteGenerator = $productScopeRewriteGenerator + ?: $objectManager->get(ProductScopeRewriteGenerator::class); } /** - * Generate url rewrites for products assigned to category + * Generates URL rewrites for products assigned to category. * - * @param \Magento\Catalog\Model\Category $category + * @param Category $category * @return array */ - public function generateProductUrlRewrites(\Magento\Catalog\Model\Category $category) + public function generateProductUrlRewrites(Category $category): array { $mergeDataProvider = clone $this->mergeDataProviderPrototype; $this->isSkippedProduct[$category->getEntityId()] = []; $saveRewriteHistory = $category->getData('save_rewrites_history'); - $storeId = $category->getStoreId(); + $storeId = (int)$category->getStoreId(); if ($category->getChangedProductIds()) { - $this->isSkippedProduct[$category->getEntityId()] = $category->getAffectedProductIds(); - /* @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */ - $collection = $this->productCollectionFactory->create() - ->setStoreId($storeId) - ->addIdFilter($category->getAffectedProductIds()) - ->addAttributeToSelect('visibility') - ->addAttributeToSelect('name') - ->addAttributeToSelect('url_key') - ->addAttributeToSelect('url_path'); - - $collection->setPageSize(1000); - $pageCount = $collection->getLastPageNumber(); - $currentPage = 1; - while ($currentPage <= $pageCount) { - $collection->setCurPage($currentPage); - foreach ($collection as $product) { - $product->setStoreId($storeId); - $product->setData('save_rewrites_history', $saveRewriteHistory); - $mergeDataProvider->merge( - $this->productUrlRewriteGenerator->generate($product, $category->getEntityId()) - ); - } - $collection->clear(); - $currentPage++; - } + $this->generateChangedProductUrls($mergeDataProvider, $category, $storeId, $saveRewriteHistory); } else { $mergeDataProvider->merge( $this->getCategoryProductsUrlRewrites( @@ -157,21 +156,49 @@ public function generateProductUrlRewrites(\Magento\Catalog\Model\Category $cate } /** - * @param \Magento\Catalog\Model\Category $category + * @param Category $category + * @return void + */ + public function deleteCategoryRewritesForChildren(Category $category) + { + $categoryIds = $this->childrenCategoriesProvider->getChildrenIds($category, true); + $categoryIds[] = $category->getId(); + foreach ($categoryIds as $categoryId) { + $this->urlPersist->deleteByData( + [ + UrlRewrite::ENTITY_ID => + $categoryId, + UrlRewrite::ENTITY_TYPE => + CategoryUrlRewriteGenerator::ENTITY_TYPE, + ] + ); + $this->urlPersist->deleteByData( + [ + UrlRewrite::METADATA => + $this->serializer->serialize(['category_id' => $categoryId]), + UrlRewrite::ENTITY_TYPE => + ProductUrlRewriteGenerator::ENTITY_TYPE, + ] + ); + } + } + + /** + * @param Category $category * @param int $storeId * @param bool $saveRewriteHistory * @param int|null $rootCategoryId * @return array */ private function getCategoryProductsUrlRewrites( - \Magento\Catalog\Model\Category $category, + Category $category, $storeId, $saveRewriteHistory, $rootCategoryId = null ) { $mergeDataProvider = clone $this->mergeDataProviderPrototype; - /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection */ + /** @var Collection $productCollection */ $productCollection = $this->productCollectionFactory->create(); $productCollection->addCategoriesFilter(['eq' => [$category->getEntityId()]]) @@ -199,30 +226,65 @@ private function getCategoryProductsUrlRewrites( } /** - * @param \Magento\Catalog\Model\Category $category - * @return void + * Generates product URL rewrites. + * + * @param MergeDataProvider $mergeDataProvider + * @param Category $category + * @param Product $product + * @param int $storeId + * @param $saveRewriteHistory */ - public function deleteCategoryRewritesForChildren(\Magento\Catalog\Model\Category $category) - { - $categoryIds = $this->childrenCategoriesProvider->getChildrenIds($category, true); - $categoryIds[] = $category->getId(); - foreach ($categoryIds as $categoryId) { - $this->urlPersist->deleteByData( - [ - \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::ENTITY_ID => - $categoryId, - \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::ENTITY_TYPE => - \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator::ENTITY_TYPE, - ] - ); - $this->urlPersist->deleteByData( - [ - \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::METADATA => - $this->serializer->serialize(['category_id' => $categoryId]), - \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::ENTITY_TYPE => - \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::ENTITY_TYPE, - ] - ); + private function generateChangedProductUrls( + MergeDataProvider $mergeDataProvider, + Category $category, + int $storeId, + $saveRewriteHistory + ) { + $this->isSkippedProduct[$category->getEntityId()] = $category->getAffectedProductIds(); + + $categoryStoreIds = [$storeId]; + // If category is changed in the Global scope when need to regenerate product URL rewrites for all other scopes. + if ($this->productScopeRewriteGenerator->isGlobalScope($storeId)) { + $categoryStoreIds = $this->getCategoryStoreIds($category); + } + + foreach ($categoryStoreIds as $categoryStoreId) { + /* @var Collection $collection */ + $collection = $this->productCollectionFactory->create() + ->setStoreId($categoryStoreId) + ->addIdFilter($category->getAffectedProductIds()) + ->addAttributeToSelect('visibility') + ->addAttributeToSelect('name') + ->addAttributeToSelect('url_key') + ->addAttributeToSelect('url_path'); + + $collection->setPageSize(1000); + $pageCount = $collection->getLastPageNumber(); + $currentPage = 1; + while ($currentPage <= $pageCount) { + $collection->setCurPage($currentPage); + foreach ($collection as $product) { + $product->setData('save_rewrites_history', $saveRewriteHistory); + $product->setStoreId($categoryStoreId); + $mergeDataProvider->merge( + $this->productUrlRewriteGenerator->generate($product, $category->getEntityId()) + ); + } + $collection->clear(); + $currentPage++; + } } } + + /** + * Gets category store IDs without Global Store. + * + * @param Category $category + * @return array + */ + private function getCategoryStoreIds(Category $category): array + { + $ids = $category->getStoreIds(); + return array_filter($ids); + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key.php new file mode 100644 index 0000000000000..cae23dcc9c674 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +require __DIR__ . '/../_files/product_with_category.php'; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$product->setStoreId(1); +$product->setUrlKey('store-1-key'); +$product = $productRepository->save($product); +$linkManagement->assignProductToCategories($product->getSku(), [Category::TREE_ROOT_ID, $category->getEntityId()]); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key_rollback.php new file mode 100644 index 0000000000000..517e9121a6d1e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key_rollback.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +require __DIR__ . '/../_files/product_with_category_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandlerTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandlerTest.php new file mode 100644 index 0000000000000..402db06a0bbc9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandlerTest.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Observer; + +use Magento\Catalog\Api\CategoryListInterface; +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use PHPUnit\Framework\TestCase; + +/** + * @magentoAppArea adminhtml + */ +class UrlRewriteHandlerTest extends TestCase +{ + /** + * @var UrlRewriteHandler + */ + private $handler; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->handler = $this->objectManager->get(UrlRewriteHandler::class); + } + + /** + * Checks category URLs rewrites generation with enabled `Use Categories Path for Product URLs` option and + * store's specific product URL key. + * + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key.php + * @magentoConfigFixture admin_store catalog/seo/product_use_categories 1 + */ + public function testGenerateProductUrlRewrites() + { + $product = $this->getProduct('p002'); + $category = $this->getCategory('category 1'); + // change the category scope to the global + $category->setStoreId(0) + ->setChangedProductIds([$product->getId()]) + ->setAffectedProductIds([$product->getId()]) + ->setAnchorsAbove(false); + + $generatedUrls = $this->handler->generateProductUrlRewrites($category); + $actual = array_values(array_map(function (UrlRewrite $urlRewrite) { + return $urlRewrite->getRequestPath(); + }, $generatedUrls)); + + $expected = [ + 'store-1-key.html', // the Default store + 'cat-1/store-1-key.html', // the Default store with Category URL key + '/store-1-key.html', // an anchor URL the Default store + 'p002.html', // the Secondary store + 'cat-1-2/p002.html', // the Secondary store with Category URL key + '/p002.html', // an anchor URL the Secondary store + ]; + self::assertEquals($expected, $actual, 'Generated URLs rewrites do not match.'); + } + + /** + * Gets category by name. + * + * @param string $name + * @return CategoryInterface + */ + private function getCategory(string $name): CategoryInterface + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('name', $name) + ->create(); + /** @var CategoryListInterface $repository */ + $repository = $this->objectManager->get(CategoryListInterface::class); + $items = $repository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } + + /** + * Gets product by SKU. + * + * @param string $sku + * @return ProductInterface + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getProduct(string $sku): ProductInterface + { + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + return $productRepository->get($sku); + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category.php index aa753a3509987..2f3d4ea4c3e7f 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category.php @@ -75,7 +75,7 @@ /** @var ProductRepositoryInterface $productRepository */ $productRepository = $objectManager->get(ProductRepositoryInterface::class); -$productRepository->save($product); +$product = $productRepository->save($product); /** @var CategoryLinkManagementInterface $linkManagement */ $linkManagement = $objectManager->get(CategoryLinkManagementInterface::class); From 93d7761798385a6273da6d51659eee0ae82412b1 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Wed, 8 Aug 2018 15:03:25 +0300 Subject: [PATCH 0812/1171] MAGETWO-91558: Enable Add to Cart on bundle products when bundle item qty is not User Defined while backorders are allowed --- .../ResourceModel/Selection/Collection.php | 129 ++++++++++++--- .../Selection/CollectionTest.php | 156 ------------------ .../Magento/Bundle/Model/ProductTest.php | 122 ++++++++++++++ 3 files changed, 231 insertions(+), 176 deletions(-) delete mode 100644 app/code/Magento/Bundle/Test/Unit/Model/ResourceModel/Selection/CollectionTest.php diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Selection/Collection.php b/app/code/Magento/Bundle/Model/ResourceModel/Selection/Collection.php index e9295b22674bd..ca6d7f1030121 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Selection/Collection.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Selection/Collection.php @@ -5,10 +5,8 @@ */ namespace Magento\Bundle\Model\ResourceModel\Selection; -use Magento\Customer\Api\GroupManagementInterface; use Magento\Framework\DataObject; use Magento\Framework\DB\Select; -use Magento\Framework\EntityManager\MetadataPool; use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; use Magento\Framework\App\ObjectManager; @@ -45,6 +43,95 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection */ private $websiteScopePriceJoined = false; + /** + * @var \Magento\CatalogInventory\Model\ResourceModel\Stock\Item + */ + private $stockItem; + + /** + * Collection constructor. + * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory + * @param \Psr\Log\LoggerInterface $logger + * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy + * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @param \Magento\Eav\Model\Config $eavConfig + * @param \Magento\Framework\App\ResourceConnection $resource + * @param \Magento\Eav\Model\EntityFactory $eavEntityFactory + * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper + * @param \Magento\Framework\Validator\UniversalFactory $universalFactory + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Framework\Module\Manager $moduleManager + * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState + * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory + * @param \Magento\Catalog\Model\ResourceModel\Url $catalogUrl + * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate + * @param \Magento\Customer\Model\Session $customerSession + * @param \Magento\Framework\Stdlib\DateTime $dateTime + * @param \Magento\Customer\Api\GroupManagementInterface $groupManagement + * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection + * @param ProductLimitationFactory|null $productLimitationFactory + * @param \Magento\Framework\EntityManager\MetadataPool|null $metadataPool + * @param \Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer|null $tableMaintainer + * @param \Magento\CatalogInventory\Model\ResourceModel\Stock\Item|null $stockItem + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __construct( + \Magento\Framework\Data\Collection\EntityFactory $entityFactory, + \Psr\Log\LoggerInterface $logger, + \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, + \Magento\Framework\Event\ManagerInterface $eventManager, + \Magento\Eav\Model\Config $eavConfig, + \Magento\Framework\App\ResourceConnection $resource, + \Magento\Eav\Model\EntityFactory $eavEntityFactory, + \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, + \Magento\Framework\Validator\UniversalFactory $universalFactory, + \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Framework\Module\Manager $moduleManager, + \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, + \Magento\Catalog\Model\ResourceModel\Url $catalogUrl, + \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, + \Magento\Customer\Model\Session $customerSession, + \Magento\Framework\Stdlib\DateTime $dateTime, + \Magento\Customer\Api\GroupManagementInterface $groupManagement, + \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, + ProductLimitationFactory $productLimitationFactory = null, + \Magento\Framework\EntityManager\MetadataPool $metadataPool = null, + \Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer $tableMaintainer = null, + \Magento\CatalogInventory\Model\ResourceModel\Stock\Item $stockItem = null + ) { + parent::__construct( + $entityFactory, + $logger, + $fetchStrategy, + $eventManager, + $eavConfig, + $resource, + $eavEntityFactory, + $resourceHelper, + $universalFactory, + $storeManager, + $moduleManager, + $catalogProductFlatState, + $scopeConfig, + $productOptionFactory, + $catalogUrl, + $localeDate, + $customerSession, + $dateTime, + $groupManagement, + $connection, + $productLimitationFactory, + $metadataPool, + $tableMaintainer + ); + + $this->stockItem = $stockItem + ?? ObjectManager::getInstance()->get(\Magento\CatalogInventory\Model\ResourceModel\Stock\Item::class); + } + /** * Initialize collection * @@ -170,28 +257,30 @@ public function setPositionOrder() */ public function addQuantityFilter() { - $stockItemTable = $this->getTable('cataloginventory_stock_item'); - $stockStatusTable = $this->getTable('cataloginventory_stock_status'); + $manageStockExpr = $this->stockItem->getManageStockExpr('stock_item'); + $backordersExpr = $this->stockItem->getBackordersExpr('stock_item'); + $minQtyExpr = $this->getConnection()->getCheckSql( + 'selection.selection_can_change_qty', + $this->stockItem->getMinSaleQtyExpr('stock_item'), + 'selection.selection_qty' + ); + + $where = $manageStockExpr . ' = 0'; + $where .= ' OR (' + . 'stock_item.is_in_stock = ' . \Magento\CatalogInventory\Model\Stock::STOCK_IN_STOCK + . ' AND (' + . $backordersExpr . ' != ' . \Magento\CatalogInventory\Model\Stock::BACKORDERS_NO + . ' OR ' + . $minQtyExpr . ' <= stock_item.qty' + . ')' + . ')'; + $this->getSelect() ->joinInner( - ['stock' => $stockStatusTable], - 'selection.product_id = stock.product_id', - [] - )->joinInner( - ['stock_item' => $stockItemTable], + ['stock_item' => $this->stockItem->getMainTable()], 'selection.product_id = stock_item.product_id', [] - ) - ->where( - '(' - . 'selection.selection_can_change_qty > 0' - . ' or ' - . 'selection.selection_qty <= stock.qty' - . ' or ' - .'stock_item.manage_stock = 0' - . ')' - ) - ->where('stock.stock_status = 1'); + )->where($where); return $this; } diff --git a/app/code/Magento/Bundle/Test/Unit/Model/ResourceModel/Selection/CollectionTest.php b/app/code/Magento/Bundle/Test/Unit/Model/ResourceModel/Selection/CollectionTest.php deleted file mode 100644 index e595f9a47f060..0000000000000 --- a/app/code/Magento/Bundle/Test/Unit/Model/ResourceModel/Selection/CollectionTest.php +++ /dev/null @@ -1,156 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Bundle\Test\Unit\Model\ResourceModel\Selection; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Store\Api\Data\StoreInterface; -use Magento\Framework\Validator\UniversalFactory; -use Magento\Eav\Model\Entity\AbstractEntity; -use Magento\Framework\DB\Adapter\AdapterInterface; -use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; -use Magento\Framework\DB\Select; - -/** - * Class CollectionTest. - */ -class CollectionTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $storeManager; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $store; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $universalFactory; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $entity; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $adapter; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $select; - - /** - * @var \Magento\Bundle\Model\ResourceModel\Selection\Collection - */ - private $model; - - protected function setUp() - { - $objectManager = new ObjectManager($this); - $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->store = $this->getMockBuilder(StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->universalFactory = $this->getMockBuilder(UniversalFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->entity = $this->getMockBuilder(AbstractEntity::class) - ->disableOriginalConstructor() - ->getMock(); - $this->adapter = $this->getMockBuilder(AdapterInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->select = $this->getMockBuilder(Select::class) - ->disableOriginalConstructor() - ->getMock(); - $factory = $this->getMockBuilder(ProductLimitationFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $this->storeManager->expects($this->any()) - ->method('getStore') - ->willReturn($this->store); - $this->store->expects($this->any()) - ->method('getId') - ->willReturn(1); - $this->universalFactory->expects($this->any()) - ->method('create') - ->willReturn($this->entity); - $this->entity->expects($this->any()) - ->method('getConnection') - ->willReturn($this->adapter); - $this->entity->expects($this->any()) - ->method('getDefaultAttributes') - ->willReturn([]); - $this->adapter->expects($this->any()) - ->method('select') - ->willReturn($this->select); - - $this->model = $objectManager->getObject( - \Magento\Bundle\Model\ResourceModel\Selection\Collection::class, - [ - 'storeManager' => $this->storeManager, - 'universalFactory' => $this->universalFactory, - 'productLimitationFactory' => $factory - ] - ); - } - - public function testAddQuantityFilter() - { - $statusTableName = 'cataloginventory_stock_status'; - $itemTableName = 'cataloginventory_stock_item'; - $this->entity->expects($this->exactly(2)) - ->method('getTable') - ->willReturnMap([ - ['cataloginventory_stock_item', $itemTableName], - ['cataloginventory_stock_status', $statusTableName], - ]); - $this->select->expects($this->exactly(2)) - ->method('joinInner') - ->withConsecutive( - [ - ['stock' => $statusTableName], - 'selection.product_id = stock.product_id', - [], - ], - [ - ['stock_item' => $itemTableName], - 'selection.product_id = stock_item.product_id', - [], - ] - )->willReturnSelf(); - $this->select - ->expects($this->exactly(2)) - ->method('where') - ->withConsecutive( - [ - '(' - . 'selection.selection_can_change_qty > 0' - . ' or ' - . 'selection.selection_qty <= stock.qty' - . ' or ' - .'stock_item.manage_stock = 0' - . ')', - ], - [ - 'stock.stock_status = 1', - ] - )->willReturnSelf(); - - $this->assertEquals($this->model, $this->model->addQuantityFilter()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/ProductTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/ProductTest.php index 9654df29bcb46..caf1d256e53e9 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/ProductTest.php @@ -17,12 +17,16 @@ use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Catalog\Model\Product\Type; use Magento\Catalog\Model\Product\Visibility; +use Magento\CatalogInventory\Model\Stock; use Magento\Framework\ObjectManagerInterface; use Magento\Store\Api\StoreRepositoryInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Entity; use Magento\TestFramework\Helper\Bootstrap; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class ProductTest extends \PHPUnit\Framework\TestCase { /** @@ -124,4 +128,122 @@ public function testMultipleStores() self::assertEquals($store->getId(), $updatedBundle->getStoreId()); } + + /** + * @param float $selectionQty + * @param float $qty + * @param int $isInStock + * @param bool $manageStock + * @param int $backorders + * @param bool $isSalable + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Bundle/_files/product.php + * @dataProvider stockConfigDataProvider + * @covers \Magento\Catalog\Model\Product::isSalable + */ + public function testIsSalable( + float $selectionQty, + float $qty, + int $isInStock, + bool $manageStock, + int $backorders, + bool $isSalable + ) { + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + + $child = $productRepository->get('simple'); + $childStockItem = $child->getExtensionAttributes()->getStockItem(); + $childStockItem->setQty($qty); + $childStockItem->setIsInStock($isInStock); + $childStockItem->setUseConfigManageStock(false); + $childStockItem->setManageStock($manageStock); + $childStockItem->setUseConfigBackorders(false); + $childStockItem->setBackorders($backorders); + $productRepository->save($child); + + /** @var \Magento\Catalog\Model\Product $bundle */ + $bundle = $productRepository->get('bundle-product'); + foreach ($bundle->getExtensionAttributes()->getBundleProductOptions() as $productOption) { + foreach ($productOption->getProductLinks() as $productLink) { + $productLink->setCanChangeQuantity(0); + $productLink->setQty($selectionQty); + } + } + $productRepository->save($bundle); + + $this->assertEquals($isSalable, $bundle->isSalable()); + } + + /** + * @return array + */ + public function stockConfigDataProvider(): array + { + $qtyVars = [0, 10]; + $isInStockVars = [ + Stock::STOCK_OUT_OF_STOCK, + Stock::STOCK_IN_STOCK, + ]; + $manageStockVars = [false, true]; + $backordersVars = [ + Stock::BACKORDERS_NO, + Stock::BACKORDERS_YES_NONOTIFY, + Stock::BACKORDERS_YES_NOTIFY, + ]; + $selectionQtyVars = [5, 10, 15]; + + $variations = []; + foreach ($qtyVars as $qty) { + foreach ($isInStockVars as $isInStock) { + foreach ($manageStockVars as $manageStock) { + foreach ($backordersVars as $backorders) { + foreach ($selectionQtyVars as $selectionQty) { + $variationName = "selectionQty: {$selectionQty}" + . " qty: {$qty}" + . " isInStock: {$isInStock}" + . " manageStock: {$manageStock}" + . " backorders: {$backorders}"; + $isSalable = $this->checkIsSalable( + $selectionQty, + $qty, + $isInStock, + $manageStock, + $backorders + ); + + $variations[$variationName] = [ + $selectionQty, + $qty, + $isInStock, + $manageStock, + $backorders, + $isSalable + ]; + } + } + } + } + } + + return $variations; + } + + /** + * @param float $selectionQty + * @param float $qty + * @param int $isInStock + * @param bool $manageStock + * @param int $backorders + * @return bool + * @see \Magento\Bundle\Model\ResourceModel\Selection\Collection::addQuantityFilter + */ + private function checkIsSalable( + float $selectionQty, + float $qty, + int $isInStock, + bool $manageStock, + int $backorders + ): bool { + return !$manageStock || ($isInStock && ($backorders || $selectionQty <= $qty)); + } } From 26829d140559326c1b29c83f22c83452d086818d Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 8 Aug 2018 15:11:49 +0300 Subject: [PATCH 0813/1171] Fixed code style issues --- .../Magento/Cms/Test/Unit/Controller/Index/IndexTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php index d8453bc6ecbce..53064e87c2755 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php @@ -42,6 +42,9 @@ class IndexTest extends \PHPUnit\Framework\TestCase */ protected $pageId = 'home'; + /** + * Test setUp + */ protected function setUp() { $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -91,6 +94,9 @@ protected function setUp() ); } + /** + * Controller test + */ public function testExecuteResultPage() { $this->cmsHelperMock->expects($this->once()) @@ -100,6 +106,9 @@ public function testExecuteResultPage() $this->assertSame($this->resultPageMock, $this->controller->execute()); } + /** + * Controller test + */ public function testExecuteResultForward() { $this->forwardMock->expects($this->once()) From f94e142919f05a951c8aaa3557e4ff6bd9192263 Mon Sep 17 00:00:00 2001 From: IvanPletnyov <ivan.pletnyov@transoftgroup.com> Date: Wed, 8 Aug 2018 15:55:21 +0300 Subject: [PATCH 0814/1171] Add new element in product page section --- .../Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml index acda5c40af8a3..2de616819b2f7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml @@ -12,6 +12,7 @@ <element name="qtyInput" type="button" selector="input.input-text.qty"/> <element name="addToCartBtn" type="button" selector="button.action.tocart.primary" timeout="30"/> <element name="successMsg" type="button" selector="div.message-success"/> + <element name="errorMsg" type="button" selector="div.message-error"/> <element name="alertMessage" type="text" selector=".page.messages [role=alert]"/> <element name="messagesBlock" type="text" selector=".page.messages"/> <element name="addToWishlist" type="button" selector="//a[@class='action towishlist']" timeout="30"/> From edc37fd6d495d1303fad1676e891d982043a894e Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Wed, 8 Aug 2018 16:10:47 +0300 Subject: [PATCH 0815/1171] MAGETWO-94070: [2.3.x] Enable/disable EAV indexer from configuration --- .../Indexer/Product/Eav/AbstractAction.php | 37 ++++- .../Model/Indexer/Product/Eav/Action/Full.php | 35 +++- .../Product/Eav/AbstractActionTest.php | 30 +++- .../Indexer/Product/Eav/Action/FullTest.php | 157 +++++++++--------- .../CatalogSearch/Plugin/EnableEavIndexer.php | 31 ++++ .../Test/Unit/Plugin/EnableEavIndexerTest.php | 51 ++++++ app/code/Magento/CatalogSearch/composer.json | 5 +- .../CatalogSearch/etc/adminhtml/system.xml | 8 + app/code/Magento/CatalogSearch/etc/config.xml | 1 + app/code/Magento/CatalogSearch/etc/di.xml | 3 + 10 files changed, 277 insertions(+), 81 deletions(-) create mode 100644 app/code/Magento/CatalogSearch/Plugin/EnableEavIndexer.php create mode 100644 app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php index b6206f96b91e0..6101e5cd362e4 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Product\Eav; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\AbstractEav; @@ -12,6 +14,11 @@ */ abstract class AbstractAction { + /** + * Config path for enable EAV indexer + */ + const ENABLE_EAV_INDEXER = 'catalog/search/enable_eav_indexer'; + /** * EAV Indexers by type * @@ -29,17 +36,27 @@ abstract class AbstractAction */ protected $_eavDecimalFactory; + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $scopeConfig; + /** * AbstractAction constructor. * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory + * @param \Magento\Framework\App\Config\ScopeConfigInterface|null $scopeConfig */ public function __construct( \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory + \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory, + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig = null ) { $this->_eavDecimalFactory = $eavDecimalFactory; $this->_eavSourceFactory = $eavSourceFactory; + $this->scopeConfig = $scopeConfig ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\App\Config\ScopeConfigInterface::class + ); } /** @@ -92,6 +109,9 @@ public function getIndexer($type) */ public function reindex($ids = null) { + if (!$this->isEavIndexerEnabled()) { + return; + } foreach ($this->getIndexers() as $indexer) { if ($ids === null) { $indexer->reindexAll(); @@ -149,4 +169,19 @@ protected function processRelations(AbstractEav $indexer, array $ids, bool $only return array_unique(array_merge($ids, $childIds, $parentIds)); } + + /** + * Get EAV indexer status + * + * @return bool + */ + private function isEavIndexerEnabled(): bool + { + $eavIndexerStatus = $this->scopeConfig->getValue( + self::ENABLE_EAV_INDEXER, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + return (bool)$eavIndexerStatus; + } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php index bc747e62f641e..802176092d147 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php @@ -3,12 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Product\Eav\Action; use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; /** * Class Full reindex action + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Full extends \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction { @@ -32,6 +35,11 @@ class Full extends \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction */ private $activeTableSwitcher; + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $scopeConfig; + /** * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory @@ -39,6 +47,7 @@ class Full extends \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction * @param \Magento\Framework\Indexer\BatchProviderInterface|null $batchProvider * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator $batchSizeCalculator * @param ActiveTableSwitcher|null $activeTableSwitcher + * @param \Magento\Framework\App\Config\ScopeConfigInterface|null $scopeConfig */ public function __construct( \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory, @@ -46,9 +55,13 @@ public function __construct( \Magento\Framework\EntityManager\MetadataPool $metadataPool = null, \Magento\Framework\Indexer\BatchProviderInterface $batchProvider = null, \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator $batchSizeCalculator = null, - ActiveTableSwitcher $activeTableSwitcher = null + ActiveTableSwitcher $activeTableSwitcher = null, + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig = null ) { - parent::__construct($eavDecimalFactory, $eavSourceFactory); + $this->scopeConfig = $scopeConfig ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\App\Config\ScopeConfigInterface::class + ); + parent::__construct($eavDecimalFactory, $eavSourceFactory, $scopeConfig); $this->metadataPool = $metadataPool ?: \Magento\Framework\App\ObjectManager::getInstance()->get( \Magento\Framework\EntityManager\MetadataPool::class ); @@ -73,6 +86,9 @@ public function __construct( */ public function execute($ids = null) { + if (!$this->isEavIndexerEnabled()) { + return; + } try { foreach ($this->getIndexers() as $indexerName => $indexer) { $connection = $indexer->getConnection(); @@ -129,4 +145,19 @@ protected function syncData($indexer, $destinationTable, $ids = null) throw $e; } } + + /** + * Get EAV indexer status + * + * @return bool + */ + private function isEavIndexerEnabled(): bool + { + $eavIndexerStatus = $this->scopeConfig->getValue( + self::ENABLE_EAV_INDEXER, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + return (bool)$eavIndexerStatus; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php index 9d58822fb6073..d946eab695054 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php @@ -22,6 +22,11 @@ class AbstractActionTest extends \PHPUnit\Framework\TestCase */ protected $_eavSourceFactoryMock; + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfig; + protected function setUp() { $this->_eavDecimalFactoryMock = $this->createPartialMock( @@ -32,9 +37,16 @@ protected function setUp() \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory::class, ['create'] ); + $this->scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); $this->_model = $this->getMockForAbstractClass( \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction::class, - [$this->_eavDecimalFactoryMock, $this->_eavSourceFactoryMock, []] + [ + $this->_eavDecimalFactoryMock, + $this->_eavSourceFactoryMock, + $this->scopeConfig + ] ); } @@ -110,6 +122,10 @@ public function testReindexWithoutArgumentsExecutesReindexAll() ->method('create') ->will($this->returnValue($eavDecimal)); + $this->scopeConfig->expects($this->once()) + ->method('getValue') + ->willReturn(1); + $this->_model->reindex(); } @@ -174,9 +190,21 @@ public function testReindexWithNotNullArgumentExecutesReindexEntities( ->method('create') ->will($this->returnValue($eavDecimal)); + $this->scopeConfig->expects($this->once()) + ->method('getValue') + ->willReturn(1); + $this->_model->reindex($ids); } + public function testReindexWithDisabledEavIndexer() + { + $this->scopeConfig->expects($this->once())->method('getValue')->willReturn(0); + $this->_eavSourceFactoryMock->expects($this->never())->method('create'); + $this->_eavDecimalFactoryMock->expects($this->never())->method('create'); + $this->_model->reindex(); + } + /** * @return array */ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php index c254557904da1..cf9e83ed39650 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php @@ -5,53 +5,84 @@ */ namespace Magento\Catalog\Test\Unit\Model\Indexer\Product\Eav\Action; +use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Indexer\BatchProviderInterface; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator; + /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FullTest extends \PHPUnit\Framework\TestCase { - public function testExecuteWithAdapterErrorThrowsException() - { - $eavDecimalFactory = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory::class, - ['create'] - ); - $eavSourceFactory = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory::class, - ['create'] - ); + /** + * @var \Magento\Catalog\Model\Indexer\Product\Eav\Action\Full|\PHPUnit_Framework_MockObject_MockObject + */ + private $model; - $exceptionMessage = 'exception message'; - $exception = new \Exception($exceptionMessage); + /** + * @var DecimalFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $eavDecimalFactory; - $eavDecimalFactory->expects($this->once()) - ->method('create') - ->will($this->throwException($exception)); + /** + * @var SourceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $eavSourceFactory; - $metadataMock = $this->createMock(\Magento\Framework\EntityManager\MetadataPool::class); - $batchProviderMock = $this->createMock(\Magento\Framework\Indexer\BatchProviderInterface::class); + /** + * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject + */ + private $metadataPool; - $batchManagementMock = $this->createMock( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator::class - ); + /** + * @var BatchProviderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $batchProvider; - $tableSwitcherMock = $this->getMockBuilder( - \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class - )->disableOriginalConstructor()->getMock(); - - $model = new \Magento\Catalog\Model\Indexer\Product\Eav\Action\Full( - $eavDecimalFactory, - $eavSourceFactory, - $metadataMock, - $batchProviderMock, - $batchManagementMock, - $tableSwitcherMock - ); + /** + * @var BatchSizeCalculator|\PHPUnit_Framework_MockObject_MockObject + */ + private $batchSizeCalculator; - $this->expectException(\Magento\Framework\Exception\LocalizedException::class); - $this->expectExceptionMessage($exceptionMessage); + /** + * @var ActiveTableSwitcher|\PHPUnit_Framework_MockObject_MockObject + */ + private $activeTableSwitcher; - $model->execute(); + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfig; + + protected function setUp() + { + $this->eavDecimalFactory = $this->createPartialMock(DecimalFactory::class, ['create']); + $this->eavSourceFactory = $this->createPartialMock(SourceFactory::class, ['create']); + $this->metadataPool = $this->createMock(MetadataPool::class); + $this->batchProvider = $this->getMockForAbstractClass(BatchProviderInterface::class); + $this->batchSizeCalculator = $this->createMock(BatchSizeCalculator::class); + $this->activeTableSwitcher = $this->createMock(ActiveTableSwitcher::class); + $this->scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\Indexer\Product\Eav\Action\Full::class, + [ + 'eavDecimalFactory' => $this->eavDecimalFactory, + 'eavSourceFactory' => $this->eavSourceFactory, + 'metadataPool' => $this->metadataPool, + 'batchProvider' => $this->batchProvider, + 'batchSizeCalculator' => $this->batchSizeCalculator, + 'activeTableSwitcher' => $this->activeTableSwitcher, + 'scopeConfig' => $this->scopeConfig + ] + ); } /** @@ -59,14 +90,7 @@ public function testExecuteWithAdapterErrorThrowsException() */ public function testExecute() { - $eavDecimalFactory = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory::class, - ['create'] - ); - $eavSourceFactory = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory::class, - ['create'] - ); + $this->scopeConfig->expects($this->once())->method('getValue')->willReturn(1); $ids = [1, 2, 3]; $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) @@ -90,42 +114,29 @@ public function testExecute() $eavSource->expects($this->atLeastOnce())->method('getConnection')->willReturn($connectionMock); $eavDecimal->expects($this->atLeastOnce())->method('getConnection')->willReturn($connectionMock); - $eavDecimal->expects($this->once()) - ->method('reindexEntities') - ->with($ids); + $eavDecimal->expects($this->once())->method('reindexEntities')->with($ids); - $eavSource->expects($this->once()) - ->method('reindexEntities') - ->with($ids); + $eavSource->expects($this->once())->method('reindexEntities')->with($ids); - $eavDecimalFactory->expects($this->once()) - ->method('create') - ->will($this->returnValue($eavSource)); + $this->eavDecimalFactory->expects($this->once())->method('create')->will($this->returnValue($eavSource)); - $eavSourceFactory->expects($this->once()) - ->method('create') - ->will($this->returnValue($eavDecimal)); + $this->eavSourceFactory->expects($this->once())->method('create')->will($this->returnValue($eavDecimal)); - $metadataMock = $this->createMock(\Magento\Framework\EntityManager\MetadataPool::class); $entityMetadataMock = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadataInterface::class) ->getMockForAbstractClass(); - $metadataMock->expects($this->atLeastOnce()) + $this->metadataPool->expects($this->atLeastOnce()) ->method('getMetadata') ->with(\Magento\Catalog\Api\Data\ProductInterface::class) ->willReturn($entityMetadataMock); - $batchProviderMock = $this->createMock(\Magento\Framework\Indexer\BatchProviderInterface::class); - $batchProviderMock->expects($this->atLeastOnce()) + $this->batchProvider->expects($this->atLeastOnce()) ->method('getBatches') ->willReturn([['from' => 10, 'to' => 100]]); - $batchProviderMock->expects($this->atLeastOnce()) + $this->batchProvider->expects($this->atLeastOnce()) ->method('getBatchIds') ->willReturn($ids); - $batchManagementMock = $this->createMock( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator::class - ); $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) ->disableOriginalConstructor() ->getMock(); @@ -134,19 +145,13 @@ public function testExecute() $selectMock->expects($this->atLeastOnce())->method('distinct')->willReturnSelf(); $selectMock->expects($this->atLeastOnce())->method('from')->willReturnSelf(); - $tableSwitcherMock = $this->getMockBuilder( - \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class - )->disableOriginalConstructor()->getMock(); - - $model = new \Magento\Catalog\Model\Indexer\Product\Eav\Action\Full( - $eavDecimalFactory, - $eavSourceFactory, - $metadataMock, - $batchProviderMock, - $batchManagementMock, - $tableSwitcherMock - ); + $this->model->execute(); + } - $model->execute(); + public function testExecuteWithDisabledEavIndexer() + { + $this->scopeConfig->expects($this->once())->method('getValue')->willReturn(0); + $this->metadataPool->expects($this->never())->method('getMetadata'); + $this->model->execute(); } } diff --git a/app/code/Magento/CatalogSearch/Plugin/EnableEavIndexer.php b/app/code/Magento/CatalogSearch/Plugin/EnableEavIndexer.php new file mode 100644 index 0000000000000..a2aa327ca5d41 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Plugin/EnableEavIndexer.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogSearch\Plugin; + +/** + * Enable Product EAV indexer in configuration for MySQL search engine + */ +class EnableEavIndexer +{ + /** + * Config search engine path + */ + const SEARCH_ENGINE_VALUE_PATH = 'groups/search/fields/engine/value'; + + /** + * @param \Magento\Config\Model\Config $subject + */ + public function beforeSave(\Magento\Config\Model\Config $subject) + { + $searchEngine = $subject->getData(self::SEARCH_ENGINE_VALUE_PATH); + if ($searchEngine === 'mysql') { + $data = $subject->getData(); + $data['groups']['search']['fields']['enable_eav_indexer']['value'] = 1; + + $subject->setData($data); + } + } +} diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php new file mode 100644 index 0000000000000..0b07b7af875ce --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogSearch\Test\Unit\Plugin; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class EnableEavIndexerTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\CatalogSearch\Plugin\EnableEavIndexer + */ + private $model; + + /** + * @var \Magento\Config\Model\Config|\PHPUnit_Framework_MockObject_MockObject + */ + private $config; + + protected function setUp() + { + $this->config = $this->getMockBuilder(\Magento\Config\Model\Config::class) + ->disableOriginalConstructor() + ->setMethods(['getData', 'setData']) + ->getMock(); + + $objectManagerHelper = new ObjectManagerHelper($this); + $this->model = $objectManagerHelper->getObject( + \Magento\CatalogSearch\Plugin\EnableEavIndexer::class + ); + } + + public function testBeforeSave() + { + $this->config->expects($this->once())->method('getData')->willReturn('elasticsearch'); + $this->config->expects($this->never())->method('setData')->willReturnSelf(); + + $this->model->beforeSave($this->config); + } + + public function testBeforeSaveMysqlSearchEngine() + { + $this->config->expects($this->at(0))->method('getData')->willReturn('mysql'); + $this->config->expects($this->at(1))->method('getData')->willReturn([]); + $this->config->expects($this->once())->method('setData')->willReturnSelf(); + + $this->model->beforeSave($this->config); + } +} diff --git a/app/code/Magento/CatalogSearch/composer.json b/app/code/Magento/CatalogSearch/composer.json index 298511ef428a9..1000e349b6f9a 100644 --- a/app/code/Magento/CatalogSearch/composer.json +++ b/app/code/Magento/CatalogSearch/composer.json @@ -9,7 +9,7 @@ "magento/framework": "*", "magento/module-backend": "*", "magento/module-catalog": "*", - "magento/module-indexer": "100.2.*", + "magento/module-indexer": "*", "magento/module-catalog-inventory": "*", "magento/module-customer": "*", "magento/module-directory": "*", @@ -19,6 +19,9 @@ "magento/module-theme": "*", "magento/module-ui": "*" }, + "suggest": { + "magento/module-config": "*" + }, "type": "magento2-module", "license": [ "OSL-3.0", diff --git a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml index 18d2cdf542799..39235511eaeec 100644 --- a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml @@ -36,6 +36,14 @@ <label>Autocomplete Limit</label> <validate>validate-digits</validate> </field> + <field id="enable_eav_indexer" translate="label" type="select" sortOrder="18" showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Enable EAV Indexer</label> + <comment>Enable/Disable Product EAV indexer to improve indexation speed. Make sure that indexer is not used by 3rd party extensions.</comment> + <depends> + <field id="engine" separator="," negative="1">mysql</field> + </depends> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> </group> </section> </system> diff --git a/app/code/Magento/CatalogSearch/etc/config.xml b/app/code/Magento/CatalogSearch/etc/config.xml index d2b50fe9f5336..66b79226c9f34 100644 --- a/app/code/Magento/CatalogSearch/etc/config.xml +++ b/app/code/Magento/CatalogSearch/etc/config.xml @@ -17,6 +17,7 @@ <max_query_length>128</max_query_length> <max_count_cacheable_search_terms>100</max_count_cacheable_search_terms> <autocomplete_limit>8</autocomplete_limit> + <enable_eav_indexer>1</enable_eav_indexer> </search> </catalog> </default> diff --git a/app/code/Magento/CatalogSearch/etc/di.xml b/app/code/Magento/CatalogSearch/etc/di.xml index acec17f48211e..cc07384d4c525 100644 --- a/app/code/Magento/CatalogSearch/etc/di.xml +++ b/app/code/Magento/CatalogSearch/etc/di.xml @@ -337,4 +337,7 @@ </argument> </arguments> </type> + <type name="Magento\Config\Model\Config"> + <plugin name="config_enable_eav_indexer" type="Magento\CatalogSearch\Plugin\EnableEavIndexer" /> + </type> </config> From d93e5c7d1925b468a53b16b262ea6b6bbaae2050 Mon Sep 17 00:00:00 2001 From: Oksana_Kremen <Oksana_Kremen@epam.com> Date: Wed, 8 Aug 2018 16:11:30 +0300 Subject: [PATCH 0816/1171] MAGETWO-91620: It is not possible to use function setInAllAttributeSetsFilter() together with getAllIds() for class Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection - Modified having clause to use aggregation function instead of alias --- .../Eav/Model/ResourceModel/Entity/Attribute/Collection.php | 2 +- .../Model/ResourceModel/Entity/Attribute/CollectionTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Collection.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Collection.php index 492da0b72c122..cec415e513677 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Collection.php +++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Collection.php @@ -222,7 +222,7 @@ public function setInAllAttributeSetsFilter(array $setIds) $setIds ) ->group('entity_attribute.attribute_id') - ->having('count = ' . count($setIds)); + ->having(new \Zend_Db_Expr('COUNT(*)') . ' = ' . count($setIds)); } //$this->getSelect()->distinct(true); diff --git a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/Attribute/CollectionTest.php b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/Attribute/CollectionTest.php index 138e1363bb6dd..7263637a081d5 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/Attribute/CollectionTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/Attribute/CollectionTest.php @@ -150,7 +150,7 @@ public function testSetInAllAttributeSetsFilter() $this->selectMock->expects($this->atLeastOnce())->method('group')->with('entity_attribute.attribute_id') ->willReturnSelf(); - $this->selectMock->expects($this->atLeastOnce())->method('having')->with('count = ' . count($setIds)) + $this->selectMock->expects($this->atLeastOnce())->method('having')->with(new \Zend_Db_Expr('COUNT(*)') . ' = ' . count($setIds)) ->willReturnSelf(); $this->model->setInAllAttributeSetsFilter($setIds); From fa97c91146d36cdb17d68c83ad46f1464c72e87f Mon Sep 17 00:00:00 2001 From: Jose Ortega <joc.hhop@gmail.com> Date: Wed, 8 Aug 2018 15:31:31 +0200 Subject: [PATCH 0817/1171] Changed property position at directory country source class --- .../Directory/Model/Config/Source/Country.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Directory/Model/Config/Source/Country.php b/app/code/Magento/Directory/Model/Config/Source/Country.php index e807f34456e24..a38ac4b62e1ee 100644 --- a/app/code/Magento/Directory/Model/Config/Source/Country.php +++ b/app/code/Magento/Directory/Model/Config/Source/Country.php @@ -20,6 +20,13 @@ class Country implements \Magento\Framework\Option\ArrayInterface */ protected $_countryCollection; + /** + * Options array + * + * @var array + */ + protected $_options; + /** * @param \Magento\Directory\Model\ResourceModel\Country\Collection $countryCollection */ @@ -28,13 +35,6 @@ public function __construct(\Magento\Directory\Model\ResourceModel\Country\Colle $this->_countryCollection = $countryCollection; } - /** - * Options array - * - * @var array - */ - protected $_options; - /** * Return options array * From d1021a3da1c16ecf02772d8a17832b1876007cab Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Wed, 8 Aug 2018 16:35:07 +0300 Subject: [PATCH 0818/1171] MAGETWO-94070: [2.3.x] Enable/disable EAV indexer from configuration - Add declare strict types; --- app/code/Magento/CatalogSearch/Plugin/EnableEavIndexer.php | 2 ++ .../Test/Unit/Plugin/EnableEavIndexerTest.php | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/app/code/Magento/CatalogSearch/Plugin/EnableEavIndexer.php b/app/code/Magento/CatalogSearch/Plugin/EnableEavIndexer.php index a2aa327ca5d41..c624f9d1c2e52 100644 --- a/app/code/Magento/CatalogSearch/Plugin/EnableEavIndexer.php +++ b/app/code/Magento/CatalogSearch/Plugin/EnableEavIndexer.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogSearch\Plugin; /** diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php index 0b07b7af875ce..8ba7d6713b514 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogSearch\Test\Unit\Plugin; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; @@ -19,6 +21,11 @@ class EnableEavIndexerTest extends \PHPUnit\Framework\TestCase */ private $config; + /** + * Set up + * + * @return void + */ protected function setUp() { $this->config = $this->getMockBuilder(\Magento\Config\Model\Config::class) From 016477f576a180444b2bb34e0c74b066ec730651 Mon Sep 17 00:00:00 2001 From: Ji Lu <> Date: Wed, 8 Aug 2018 08:43:22 -0500 Subject: [PATCH 0819/1171] MC-139: Guest customer should be able to advance search Bundle product with product name - updated mftf tests --- .../AdvanceCatalogSearchBundleProductTest.xml | 45 +++++++------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml index 44ae4b7476aeb..0b220efaad49f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml @@ -22,20 +22,17 @@ <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropdownBundleOption" stepKey="bundleOption"> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> <requiredEntity createDataKey="product"/> </createData> - <getData entity="AllBundleOptions" index="0" stepKey="getBundleOption"> - <requiredEntity createDataKey="product"/> - </getData> <createData entity="ApiBundleLink" stepKey="createBundleLink1"> <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple1"/> </createData> <createData entity="ApiBundleLink" stepKey="createBundleLink2"> <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple2"/> </createData> <magentoCLI command="indexer:reindex" stepKey="reindex"/> @@ -60,20 +57,17 @@ <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropdownBundleOption" stepKey="bundleOption"> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> <requiredEntity createDataKey="product"/> </createData> - <getData entity="AllBundleOptions" index="0" stepKey="getBundleOption"> - <requiredEntity createDataKey="product"/> - </getData> <createData entity="ApiBundleLink" stepKey="createBundleLink1"> <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple1"/> </createData> <createData entity="ApiBundleLink" stepKey="createBundleLink2"> <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple2"/> </createData> <magentoCLI command="indexer:reindex" stepKey="reindex"/> @@ -98,20 +92,17 @@ <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropdownBundleOption" stepKey="bundleOption"> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> <requiredEntity createDataKey="product"/> </createData> - <getData entity="AllBundleOptions" index="0" stepKey="getBundleOption"> - <requiredEntity createDataKey="product"/> - </getData> <createData entity="ApiBundleLink" stepKey="createBundleLink1"> <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple1"/> </createData> <createData entity="ApiBundleLink" stepKey="createBundleLink2"> <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple2"/> </createData> <magentoCLI command="indexer:reindex" stepKey="reindex"/> @@ -136,20 +127,17 @@ <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropdownBundleOption" stepKey="bundleOption"> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> <requiredEntity createDataKey="product"/> </createData> - <getData entity="AllBundleOptions" index="0" stepKey="getBundleOption"> - <requiredEntity createDataKey="product"/> - </getData> <createData entity="ApiBundleLink" stepKey="createBundleLink1"> <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple1"/> </createData> <createData entity="ApiBundleLink" stepKey="createBundleLink2"> <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple2"/> </createData> <magentoCLI command="indexer:reindex" stepKey="reindex"/> @@ -174,20 +162,17 @@ <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropdownBundleOption" stepKey="bundleOption"> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> <requiredEntity createDataKey="product"/> </createData> - <getData entity="AllBundleOptions" index="0" stepKey="getBundleOption"> - <requiredEntity createDataKey="product"/> - </getData> <createData entity="ApiBundleLink" stepKey="createBundleLink1"> <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple1"/> </createData> <createData entity="ApiBundleLink" stepKey="createBundleLink2"> <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="getBundleOption"/> + <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple2"/> </createData> <getData entity="GetProduct" stepKey="arg1"> From b5e761bae13f28ef902cb8af992c3d97d06d1c41 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Wed, 8 Aug 2018 17:14:41 +0300 Subject: [PATCH 0820/1171] MAGETWO-94070: [2.3.x] Enable/disable EAV indexer from configuration - Add comment blocks to tests; --- .../Indexer/Product/Eav/AbstractActionTest.php | 18 ++++++++++++++++++ .../Indexer/Product/Eav/Action/FullTest.php | 7 +++++++ .../Test/Unit/Plugin/EnableEavIndexerTest.php | 10 ++++++++++ 3 files changed, 35 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php index d946eab695054..6ad14af44304e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php @@ -27,6 +27,9 @@ class AbstractActionTest extends \PHPUnit\Framework\TestCase */ private $scopeConfig; + /** + * @return void + */ protected function setUp() { $this->_eavDecimalFactoryMock = $this->createPartialMock( @@ -50,6 +53,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testGetIndexers() { $expectedIndexers = [ @@ -85,6 +91,10 @@ public function testGetIndexerWithUnknownTypeThrowsException() $this->_model->getIndexer('unknown_type'); } + /** + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testGetIndexer() { $this->_eavSourceFactoryMock->expects($this->once()) @@ -98,6 +108,10 @@ public function testGetIndexer() $this->assertEquals('source_return_value', $this->_model->getIndexer('source')); } + /** + * @return void + * @throws \Exception + */ public function testReindexWithoutArgumentsExecutesReindexAll() { $eavSource = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source::class) @@ -197,6 +211,10 @@ public function testReindexWithNotNullArgumentExecutesReindexEntities( $this->_model->reindex($ids); } + /** + * @return void + * @throws \Exception + */ public function testReindexWithDisabledEavIndexer() { $this->scopeConfig->expects($this->once())->method('getValue')->willReturn(0); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php index cf9e83ed39650..90c3f999a6a8b 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php @@ -58,6 +58,9 @@ class FullTest extends \PHPUnit\Framework\TestCase */ private $scopeConfig; + /** + * @return void + */ protected function setUp() { $this->eavDecimalFactory = $this->createPartialMock(DecimalFactory::class, ['create']); @@ -148,6 +151,10 @@ public function testExecute() $this->model->execute(); } + /** + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testExecuteWithDisabledEavIndexer() { $this->scopeConfig->expects($this->once())->method('getValue')->willReturn(0); diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php index 8ba7d6713b514..0eac2e3309aec 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php @@ -39,6 +39,11 @@ protected function setUp() ); } + /** + * Test with other search engine (not MySQL) selected in config + * + * @return void + */ public function testBeforeSave() { $this->config->expects($this->once())->method('getData')->willReturn('elasticsearch'); @@ -47,6 +52,11 @@ public function testBeforeSave() $this->model->beforeSave($this->config); } + /** + * Test with MySQL search engine selected in config + * + * @return void + */ public function testBeforeSaveMysqlSearchEngine() { $this->config->expects($this->at(0))->method('getData')->willReturn('mysql'); From f7773b17e1d2d881e7da4b8b0f7685057e5390fb Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Wed, 8 Aug 2018 17:17:04 +0300 Subject: [PATCH 0821/1171] MAGETWO-91512: Free Shipping Cart Price Rule not working when UPS shipping method is enabled --- .../Product/Indexer/Price/DefaultPrice.php | 5 +++++ .../Model/Plugin/PriceIndexUpdater.php | 4 ++-- .../Model/Adapter/FieldMapper/ProductFieldMapper.php | 7 +++++-- .../Model/Adapter/FieldMapper/ProductFieldMapper.php | 7 +++++-- .../Adapter/FieldMapper/ProductFieldMapperTest.php | 4 +++- .../Magento/Catalog/Model/ProductPriceTest.php | 12 ++++++++++++ .../testsuite/Magento/Ups/Model/CarrierTest.php | 9 +++++++++ 7 files changed, 41 insertions(+), 7 deletions(-) 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 60c8507f325e6..7a88e40d90ee1 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 @@ -862,6 +862,11 @@ private function getTotalTierPriceExpression(\Zend_Db_Expr $priceExpression) ); } + /** + * @param string $tableAlias + * @param \Zend_Db_Expr $priceExpression + * @return \Zend_Db_Expr + */ private function getTierPriceExpressionForTable($tableAlias, \Zend_Db_Expr $priceExpression) { return $this->getConnection()->getCheckSql( diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php index f5cb43afb8e36..c061c459bfb49 100644 --- a/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php +++ b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php @@ -55,7 +55,7 @@ public function afterSave(Item $subject, Item $result, AbstractModel $model): It /** * @param Item $subject - * @param $result + * @param mixed $result * @param int $websiteId * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) @@ -67,7 +67,7 @@ public function afterUpdateSetOutOfStock(Item $subject, $result, int $websiteId) /** * @param Item $subject - * @param $result + * @param mixed $result * @param int $websiteId * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php index dc8d73a7d556c..7ba28d557c9bc 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php @@ -69,7 +69,9 @@ public function __construct( } /** - * {@inheritdoc} + * @param string $attributeCode + * @param array $context + * @return string */ public function getFieldName($attributeCode, $context = []) { @@ -105,7 +107,8 @@ public function getFieldName($attributeCode, $context = []) } /** - * {@inheritdoc} + * @param array $context + * @return array * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function getAllAttributesTypes($context = []) diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php index 582eea9c6d4ad..2a8ccdb5c72ac 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php @@ -42,7 +42,9 @@ public function __construct( } /** - * {@inheritdoc} + * @param string $attributeCode + * @param array $context + * @return string */ public function getFieldName($attributeCode, $context = []) { @@ -78,7 +80,8 @@ public function getFieldName($attributeCode, $context = []) } /** - * {@inheritdoc} + * @param array $context + * @return array */ public function getAllAttributesTypes($context = []) { diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapperTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapperTest.php index d13e8237efb79..d531d15cb393d 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapperTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapperTest.php @@ -180,7 +180,9 @@ public function testGetFieldNameWithoutAttribute() /** * @dataProvider attributeProvider * @param string $attributeCode - * + * @param $inputType + * @param $searchAttributes + * @param $expected * @return void */ public function testGetAllAttributesTypes($attributeCode, $inputType, $searchAttributes, $expected) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php index 2653e40c85d7d..c712918d6bd05 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php @@ -29,12 +29,18 @@ class ProductPriceTest extends \PHPUnit\Framework\TestCase */ private $productRepository; + /** + * @return void + */ protected function setUp() { $this->_model = Bootstrap::getObjectManager()->create(Product::class); $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); } + /** + * @return void + */ public function testGetPrice() { $this->assertEmpty($this->_model->getPrice()); @@ -42,6 +48,9 @@ public function testGetPrice() $this->assertEquals(10.0, $this->_model->getPrice()); } + /** + * @return void + */ public function testGetPriceModel() { $default = $this->_model->getPriceModel(); @@ -73,6 +82,9 @@ public function testGetFormatedPrice() $this->assertEquals('<span class="price">$0.00</span>', $this->_model->getFormatedPrice()); } + /** + * @return void + */ public function testSetGetFinalPrice() { $this->assertEquals(0, $this->_model->getFinalPrice()); diff --git a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php index 66df6b0f7e60c..02f4225f836b1 100644 --- a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php +++ b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php @@ -15,11 +15,17 @@ class CarrierTest extends \PHPUnit\Framework\TestCase */ private $carrier; + /** + * @return void + */ protected function setUp() { $this->carrier = Bootstrap::getObjectManager()->create(Carrier::class); } + /** + * @return void + */ public function testGetShipAcceptUrl() { $this->assertEquals('https://wwwcie.ups.com/ups.app/xml/ShipAccept', $this->carrier->getShipAcceptUrl()); @@ -35,6 +41,9 @@ public function testGetShipAcceptUrlLive() $this->assertEquals('https://onlinetools.ups.com/ups.app/xml/ShipAccept', $this->carrier->getShipAcceptUrl()); } + /** + * @return void + */ public function testGetShipConfirmUrl() { $this->assertEquals('https://wwwcie.ups.com/ups.app/xml/ShipConfirm', $this->carrier->getShipConfirmUrl()); From 9dd52e13a43819b37ada6cf4060c88a0fb43fdb9 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Wed, 8 Aug 2018 09:46:36 -0500 Subject: [PATCH 0822/1171] MAGETWO-90591: Add Selected Button shouldn't be enabled if there are no images available --- .../Mftf/ActionGroup/SelectImageFromMediaStorageActionGroup.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SelectImageFromMediaStorageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SelectImageFromMediaStorageActionGroup.xml index 8c1d17c8d9bed..2daa1cbeca800 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SelectImageFromMediaStorageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SelectImageFromMediaStorageActionGroup.xml @@ -17,7 +17,6 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoading2" /> <see selector="{{MediaGallerySection.CancelBtn}}" userInput="Cancel" stepKey="seeCancelBtn" /> <see selector="{{MediaGallerySection.CreateFolder}}" userInput="Create Folder" stepKey="seeCreateFolderBtn" /> - <see selector="{{MediaGallerySection.InsertFile}}" userInput="Add Selected" stepKey="seeAddSelectedBtn" /> </actionGroup> <actionGroup name="CreateImageFolder"> <arguments> From 7dac24541c92b7d01914d08eb8eabace6986ee18 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Wed, 8 Aug 2018 17:48:02 +0300 Subject: [PATCH 0823/1171] MAGETWO-91512: Free Shipping Cart Price Rule not working when UPS shipping method is enabled --- .../integration/testsuite/Magento/Ups/Model/CarrierTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php index 02f4225f836b1..aa301e8d3e423 100644 --- a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php +++ b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php @@ -67,7 +67,7 @@ public function testGetShipConfirmUrlLive() * @magentoConfigFixture current_store carriers/ups/allowed_methods 1DA,GND * @magentoConfigFixture current_store carriers/ups/free_method GND */ - public function testCollectRates() + public function testCollectFreeRates() { $rateRequest = Bootstrap::getObjectManager()->get(RateRequestFactory::class)->create(); $rateRequest->setDestCountryId('US'); @@ -77,7 +77,7 @@ public function testCollectRates() $rateRequest->setPackageWeight(1); $rateRequest->setFreeMethodWeight(0); $rateRequest->setLimitCarrier($this->carrier::CODE); - + $rateRequest->setFreeShipping(true); $rateResult = $this->carrier->collectRates($rateRequest); $result = $rateResult->asArray(); $methods = $result[$this->carrier::CODE]['methods']; From 832bbaee9e264ae6a0d6ed8be1aec3b968ce4497 Mon Sep 17 00:00:00 2001 From: Alexey Arendarenko <alexeya@ven.com> Date: Tue, 7 Aug 2018 15:53:27 +0300 Subject: [PATCH 0824/1171] Ui module fixes: - Update confirm message method, add number of selected records to confirm message --- app/code/Magento/Ui/view/base/web/js/grid/massactions.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/massactions.js b/app/code/Magento/Ui/view/base/web/js/grid/massactions.js index 046ff12bfaa6f..758a8341e1736 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/massactions.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/massactions.js @@ -176,10 +176,13 @@ define([ */ _confirm: function (action, callback) { var confirmData = action.confirm; + var data = this.getSelections(); + var total = data.total ? data.total : 0; + var confirmMessage = confirmData.message + ' (' + total + ' record' + (total > 1 ? 's' : '') + ')'; confirm({ title: confirmData.title, - content: confirmData.message, + content: confirmMessage, actions: { confirm: callback } From 55ec6a30e710b713f96bf218ce27b98c9bcc9ae4 Mon Sep 17 00:00:00 2001 From: Alexey Arendarenko <alexeya@ven.com> Date: Tue, 7 Aug 2018 15:59:12 +0300 Subject: [PATCH 0825/1171] Ui module fixes: - Add multiselect.js new method that toggles page records and set it using in template and set up it in template file --- .../Ui/view/base/web/js/grid/columns/multiselect.js | 9 +++++++++ .../base/web/templates/grid/columns/multiselect.html | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js index 46dd865ffdde8..ba0f4d25c25a4 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js @@ -229,6 +229,15 @@ define([ return this; }, + /** + * Selects or deselects all records on the current page. + * + * @returns {Multiselect} Chainable. + */ + togglePage: function () { + return this.isPageSelected() ? this.deselectPage() : this.selectPage(); + }, + /** * Clears the array of not selected records. * diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/columns/multiselect.html b/app/code/Magento/Ui/view/base/web/templates/grid/columns/multiselect.html index 18dd49bd56ee3..e6e14d5aa335b 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/columns/multiselect.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/columns/multiselect.html @@ -11,7 +11,7 @@ data-bind=" checked: allSelected(), attr: {id: ++ko.uid}, - event: { change: toggleSelectAll }, + event: { change: togglePage }, css: { '_indeterminate': indetermine }, enable: totalRecords"> <label attr="for: ko.uid"/> From fd2575ecc42680660d7b383dfff7b0292053d31f Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Wed, 8 Aug 2018 18:48:12 +0300 Subject: [PATCH 0826/1171] MAGETWO-91512: Free Shipping Cart Price Rule not working when UPS shipping method is enabled --- .../Model/Adapter/FieldMapper/ProductFieldMapper.php | 1 + .../Model/Adapter/FieldMapper/ProductFieldMapperTest.php | 6 +++--- .../Model/Adapter/FieldMapper/ProductFieldMapperTest.php | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php index 7ba28d557c9bc..09357216d2067 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php @@ -110,6 +110,7 @@ public function getFieldName($attributeCode, $context = []) * @param array $context * @return array * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getAllAttributesTypes($context = []) { diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapperTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapperTest.php index d531d15cb393d..6ee32a58698c6 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapperTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapperTest.php @@ -180,9 +180,9 @@ public function testGetFieldNameWithoutAttribute() /** * @dataProvider attributeProvider * @param string $attributeCode - * @param $inputType - * @param $searchAttributes - * @param $expected + * @param string $inputType + * @param array $searchAttributes + * @param array $expected * @return void */ public function testGetAllAttributesTypes($attributeCode, $inputType, $searchAttributes, $expected) diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/ProductFieldMapperTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/ProductFieldMapperTest.php index 2a57dfc65f47b..8b7ac6abbb190 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/ProductFieldMapperTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/ProductFieldMapperTest.php @@ -182,7 +182,9 @@ public function testGetFieldNameWithoutAttribute() /** * @dataProvider attributeProvider * @param string $attributeCode - * + * @param string $inputType + * @param array $searchAttributes + * @param array $expected * @return void */ public function testGetAllAttributesTypes($attributeCode, $inputType, $searchAttributes, $expected) From 0c3e3976331f9a64f5b9648e849c37919d2b5177 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Wed, 8 Aug 2018 18:45:21 +0300 Subject: [PATCH 0827/1171] MAGETWO-91771: Comma special character in cart price rule condition value results in incorrect rule --- .../Product/View/Type/ConfigurableTest.php | 13 +++++++----- .../Unit/Model/Rule/Condition/ProductTest.php | 5 ++++- .../Framework/Locale/Test/Unit/FormatTest.php | 20 ++++++++++++------- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php index e117ab707a473..25d8412c91056 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php @@ -84,6 +84,9 @@ class ConfigurableTest extends \PHPUnit\Framework\TestCase */ private $variationPricesMock; + /** + * {@inheritDoc} + */ protected function setUp() { $this->mockContextObject(); @@ -346,13 +349,13 @@ public function testGetJsonConfig() /** * Retrieve array with expected parameters for method getJsonConfig() * - * @param $productId - * @param $amount - * @param $priceQty - * @param $percentage + * @param int $productId + * @param double $amount + * @param int $priceQty + * @param int $percentage * @return array */ - private function getExpectedArray($productId, $amount, $priceQty, $percentage) + private function getExpectedArray($productId, $amount, $priceQty, $percentage): array { $expectedArray = [ 'attributes' => [], diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php index 97c7efab42583..51c7b9ede5aa2 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php @@ -207,7 +207,10 @@ public function testGetValueElementChooserUrl($attribute, $url, $jsObject = '') $this->assertEquals($url, $this->model->getValueElementChooserUrl()); } - public function testValidateCategoriesIgnoresVisibility() + /** + * test ValidateCategoriesIgnoresVisibility + */ + public function testValidateCategoriesIgnoresVisibility(): void { /* @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $product */ $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php index f6d7326f52764..1141f451c13a5 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php @@ -33,6 +33,9 @@ class FormatTest extends \PHPUnit\Framework\TestCase */ protected $currency; + /** + * {@inheritDoc} + */ protected function setUp() { $this->currency = $this->getMockBuilder(\Magento\Directory\Model\Currency::class) @@ -64,11 +67,11 @@ protected function setUp() } /** - * @param $localeCode - * @param $expectedResult + * @param string $localeCode + * @param array $expectedResult * @dataProvider getPriceFormatDataProvider */ - public function testGetPriceFormat($localeCode, $expectedResult) + public function testGetPriceFormat($localeCode, array $expectedResult): void { $this->scope->expects($this->once()) ->method('getCurrentCurrency') @@ -80,9 +83,10 @@ public function testGetPriceFormat($localeCode, $expectedResult) } /** + * * @return array */ - public function getPriceFormatDataProvider() + public function getPriceFormatDataProvider(): array { return [ ['en_US', ['decimalSymbol' => '.', 'groupSymbol' => ',']], @@ -93,16 +97,18 @@ public function getPriceFormatDataProvider() } /** - * @param float | null $expected - * @param string|float|int $value + * + * @param mixed $value + * @param float $expected * @dataProvider provideNumbers */ - public function testGetNumber($value, $expected) + public function testGetNumber($value, $expected): void { $this->assertEquals($expected, $this->formatModel->getNumber($value)); } /** + * * @return array */ public function provideNumbers(): array From c007bdab682ba08b88b30c380b7081c65f016bb4 Mon Sep 17 00:00:00 2001 From: Ben Tideswell <ben@fishpig.co.uk> Date: Tue, 27 Mar 2018 16:24:35 +0100 Subject: [PATCH 0828/1171] XSD File Change --- app/code/Magento/Config/etc/system_file.xsd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Config/etc/system_file.xsd b/app/code/Magento/Config/etc/system_file.xsd index 5a2b915262a9a..f1688b2e35371 100644 --- a/app/code/Magento/Config/etc/system_file.xsd +++ b/app/code/Magento/Config/etc/system_file.xsd @@ -474,7 +474,7 @@ </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[A-Za-z0-9\\:]+" /> + <xs:pattern value="[A-Za-z0-9_\\:]+" /> <xs:minLength value="5" /> </xs:restriction> </xs:simpleType> From bb0a9a4c9a41d7d8f67ec5343d33de85fd1b4a82 Mon Sep 17 00:00:00 2001 From: Guillaume GIORDANA <guillaume.giordana@gmail.com> Date: Tue, 24 Jul 2018 17:56:52 +0200 Subject: [PATCH 0829/1171] MAGETWO-84608: Cannot perform setup:install if Redis needs a password and Redis page cache is enabled in env.php --- .../Setup/Model/ConfigOptionsList/Cache.php | 19 ++++++++++++++++- .../Model/ConfigOptionsList/PageCache.php | 21 +++++++++++++++++-- .../Model/ConfigOptionsList/CacheTest.php | 10 +++++++-- .../Model/ConfigOptionsList/PageCacheTest.php | 10 +++++++-- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php index 1ec9d486a5a22..04ec83a3d0ca2 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php @@ -26,11 +26,13 @@ class Cache implements ConfigOptionsListInterface const INPUT_KEY_CACHE_BACKEND_REDIS_SERVER = 'cache-backend-redis-server'; const INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE = 'cache-backend-redis-db'; const INPUT_KEY_CACHE_BACKEND_REDIS_PORT = 'cache-backend-redis-port'; + const INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD = 'cache-backend-redis-password'; const CONFIG_PATH_CACHE_BACKEND = 'cache/frontend/default/backend'; const CONFIG_PATH_CACHE_BACKEND_SERVER = 'cache/frontend/default/backend_options/server'; const CONFIG_PATH_CACHE_BACKEND_DATABASE = 'cache/frontend/default/backend_options/database'; const CONFIG_PATH_CACHE_BACKEND_PORT = 'cache/frontend/default/backend_options/port'; + const CONFIG_PATH_CACHE_BACKEND_PASSWORD = 'cache/frontend/default/backend_options/password'; /** * @var array @@ -38,7 +40,8 @@ class Cache implements ConfigOptionsListInterface private $defaultConfigValues = [ self::INPUT_KEY_CACHE_BACKEND_REDIS_SERVER => '127.0.0.1', self::INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE => '0', - self::INPUT_KEY_CACHE_BACKEND_REDIS_PORT => '6379' + self::INPUT_KEY_CACHE_BACKEND_REDIS_PORT => '6379', + self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD => '' ]; /** @@ -55,6 +58,7 @@ class Cache implements ConfigOptionsListInterface self::INPUT_KEY_CACHE_BACKEND_REDIS_SERVER => self::CONFIG_PATH_CACHE_BACKEND_SERVER, self::INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE => self::CONFIG_PATH_CACHE_BACKEND_DATABASE, self::INPUT_KEY_CACHE_BACKEND_REDIS_PORT => self::CONFIG_PATH_CACHE_BACKEND_PORT, + self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD => self::CONFIG_PATH_CACHE_BACKEND_PASSWORD ]; /** @@ -102,6 +106,12 @@ public function getOptions() TextConfigOption::FRONTEND_WIZARD_TEXT, self::CONFIG_PATH_CACHE_BACKEND_PORT, 'Redis server listen port' + ), + new TextConfigOption( + self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_CACHE_BACKEND_PASSWORD, + 'Redis server password' ) ]; } @@ -190,6 +200,13 @@ private function validateRedisConfig(array $options, DeploymentConfig $deploymen self::CONFIG_PATH_CACHE_BACKEND_DATABASE, $this->getDefaultConfigValue(self::INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE) ); + + $config['password'] = isset($options[self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD]) + ? $options[self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD] + : $deploymentConfig->get( + self::CONFIG_PATH_CACHE_BACKEND_PASSWORD, + $this->getDefaultConfigValue(self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD) + ); return $this->redisValidator->isValidConnection($config); } diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php index be1cc5b010185..944c543495751 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php @@ -27,12 +27,14 @@ class PageCache implements ConfigOptionsListInterface const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_DATABASE = 'page-cache-redis-db'; const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PORT = 'page-cache-redis-port'; const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA = 'page-cache-redis-compress-data'; + const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD = 'page-cache-redis-password'; const CONFIG_PATH_PAGE_CACHE_BACKEND = 'cache/frontend/page_cache/backend'; const CONFIG_PATH_PAGE_CACHE_BACKEND_SERVER = 'cache/frontend/page_cache/backend_options/server'; const CONFIG_PATH_PAGE_CACHE_BACKEND_DATABASE = 'cache/frontend/page_cache/backend_options/database'; const CONFIG_PATH_PAGE_CACHE_BACKEND_PORT = 'cache/frontend/page_cache/backend_options/port'; const CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESS_DATA = 'cache/frontend/page_cache/backend_options/compress_data'; + const CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD = 'cache/frontend/page_cache/backend_options/password'; /** * @var array @@ -41,7 +43,8 @@ class PageCache implements ConfigOptionsListInterface self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER => '127.0.0.1', self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_DATABASE => '1', self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PORT => '6379', - self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA => '0' + self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA => '0', + self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD => '' ]; /** @@ -58,7 +61,8 @@ class PageCache implements ConfigOptionsListInterface self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER => self::CONFIG_PATH_PAGE_CACHE_BACKEND_SERVER, self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_DATABASE => self::CONFIG_PATH_PAGE_CACHE_BACKEND_DATABASE, self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PORT => self::CONFIG_PATH_PAGE_CACHE_BACKEND_PORT, - self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA => self::CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESS_DATA + self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA => self::CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESS_DATA, + self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD => self::CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD ]; /** @@ -112,6 +116,12 @@ public function getOptions() TextConfigOption::FRONTEND_WIZARD_TEXT, self::CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESS_DATA, 'Set to 1 to compress the full page cache (use 0 to disable)' + ), + new TextConfigOption( + self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD, + 'Redis server password' ) ]; } @@ -201,6 +211,13 @@ private function validateRedisConfig(array $options, DeploymentConfig $deploymen self::CONFIG_PATH_PAGE_CACHE_BACKEND_DATABASE, $this->getDefaultConfigValue(self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_DATABASE) ); + + $config['password'] = isset($options[self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD]) + ? $options[self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD] + : $deploymentConfig->get( + self::CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD, + $this->getDefaultConfigValue(self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD) + ); return $this->redisValidator->isValidConnection($config); } diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php index ef0ea3e988364..abc2a6fdc5ae2 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php @@ -56,6 +56,10 @@ public function testGetOptions() $this->assertArrayHasKey(3, $options); $this->assertInstanceOf(TextConfigOption::class, $options[3]); $this->assertEquals('cache-backend-redis-port', $options[3]->getName()); + + $this->assertArrayHasKey(4, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[4]); + $this->assertEquals('cache-backend-redis-password', $options[4]->getName()); } public function testCreateConfigCacheRedis() @@ -70,7 +74,8 @@ public function testCreateConfigCacheRedis() 'backend_options' => [ 'server' => '', 'port' => '', - 'database' => '' + 'database' => '', + 'password' => '' ] ] ] @@ -92,7 +97,8 @@ public function testCreateConfigWithRedisConfig() 'backend_options' => [ 'server' => 'localhost', 'port' => '1234', - 'database' => '5' + 'database' => '5', + 'password' => '' ] ] ] diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php index e654bea9ac1c5..b9cd137530aa9 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php @@ -60,6 +60,10 @@ public function testGetOptions() $this->assertArrayHasKey(4, $options); $this->assertInstanceOf(TextConfigOption::class, $options[4]); $this->assertEquals('page-cache-redis-compress-data', $options[4]->getName()); + + $this->assertArrayHasKey(5, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[5]); + $this->assertEquals('page-cache-redis-password', $options[5]->getName()); } public function testCreateConfigWithRedis() @@ -75,7 +79,8 @@ public function testCreateConfigWithRedis() 'server'=> '', 'port' => '', 'database' => '', - 'compress_data' => '' + 'compress_data' => '', + 'password' => '' ] ] ] @@ -98,7 +103,8 @@ public function testCreateConfigWithRedisConfiguration() 'server' => 'foo.bar', 'port' => '9000', 'database' => '6', - 'compress_data' => '1' + 'compress_data' => '1', + 'password' => '' ] ] ] From 4c2d6f5c61fc42c15880f243f9a28a94459d676a Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Wed, 8 Aug 2018 11:41:17 -0500 Subject: [PATCH 0830/1171] MAGETWO-90863: Error handling responses from MBI --- .../Connector/Http/ConverterInterface.php | 5 +++++ .../Model/Connector/Http/JsonConverter.php | 19 ++++++++++++++++++- .../Model/Connector/Http/ResponseResolver.php | 6 +++--- .../Model/Connector/Http/Client/CurlTest.php | 18 ++++++++---------- .../Connector/Http/JsonConverterTest.php | 5 ++++- .../Connector/Http/ResponseResolverTest.php | 8 ++++---- .../Model/Connector/SignUpCommandTest.php | 1 - 7 files changed, 42 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/Analytics/Model/Connector/Http/ConverterInterface.php b/app/code/Magento/Analytics/Model/Connector/Http/ConverterInterface.php index 474398fd34e26..5f92e1104b313 100644 --- a/app/code/Magento/Analytics/Model/Connector/Http/ConverterInterface.php +++ b/app/code/Magento/Analytics/Model/Connector/Http/ConverterInterface.php @@ -30,4 +30,9 @@ public function toBody(array $data); * @return string */ public function getContentTypeHeader(); + + /** + * @return string + */ + public function getContentMediaType(): string ; } diff --git a/app/code/Magento/Analytics/Model/Connector/Http/JsonConverter.php b/app/code/Magento/Analytics/Model/Connector/Http/JsonConverter.php index 44c54f67da759..059dab554bd92 100644 --- a/app/code/Magento/Analytics/Model/Connector/Http/JsonConverter.php +++ b/app/code/Magento/Analytics/Model/Connector/Http/JsonConverter.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Analytics\Model\Connector\Http; use Magento\Framework\Serialize\Serializer\Json; @@ -14,9 +16,16 @@ class JsonConverter implements ConverterInterface { /** * Content-Type HTTP header for json. + * @deprecated + * @see CONTENT_MEDIA_TYPE */ const CONTENT_TYPE_HEADER = 'Content-Type: application/json'; + /** + * Media-Type corresponding to this converter. + */ + const CONTENT_MEDIA_TYPE = 'application/json'; + /** * @var Json */ @@ -56,6 +65,14 @@ public function toBody(array $data) */ public function getContentTypeHeader() { - return self::CONTENT_TYPE_HEADER; + return sprintf('Content-Type: %s', self::CONTENT_MEDIA_TYPE); + } + + /** + * @inheritdoc + */ + public function getContentMediaType(): string + { + return self::CONTENT_MEDIA_TYPE; } } diff --git a/app/code/Magento/Analytics/Model/Connector/Http/ResponseResolver.php b/app/code/Magento/Analytics/Model/Connector/Http/ResponseResolver.php index 4cba11123873f..57b61c1b5562a 100644 --- a/app/code/Magento/Analytics/Model/Connector/Http/ResponseResolver.php +++ b/app/code/Magento/Analytics/Model/Connector/Http/ResponseResolver.php @@ -38,10 +38,10 @@ public function __construct(ConverterInterface $converter, array $responseHandle public function getResult(\Zend_Http_Response $response) { $result = false; - preg_match('#(?:Content-Type:\s*)(\w\S+)#i', $this->converter->getContentTypeHeader(), $contentType); - $converterContentType = $contentType[1]; + $converterMediaType = $this->converter->getContentMediaType(); - if ($response->getBody() && is_int(strripos($response->getHeader('Content-Type'), $converterContentType))) { + /** Content-Type header may not only contain media-type declaration */ + if ($response->getBody() && is_int(strripos($response->getHeader('Content-Type'), $converterMediaType))) { $responseBody = $this->converter->fromBody($response->getBody()); } else { $responseBody = []; 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 index 5ee59a7913a61..cc432e338741b 100644 --- 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 @@ -97,7 +97,6 @@ public function getTestData() 'version' => '1.1', 'body'=> ['name' => 'value'], 'url' => 'http://www.mystore.com', - 'headers' => [JsonConverter::CONTENT_TYPE_HEADER], 'method' => \Magento\Framework\HTTP\ZendClient::POST, ] ] @@ -118,7 +117,7 @@ public function testRequestSuccess(array $data) $data['method'], $data['url'], $data['version'], - $data['headers'], + [$this->converterMock->getContentTypeHeader()], json_encode($data['body']) ); $this->curlAdapterMock->expects($this->once()) @@ -139,7 +138,7 @@ public function testRequestSuccess(array $data) $data['method'], $data['url'], $data['body'], - $data['headers'], + [$this->converterMock->getContentTypeHeader()], $data['version'] ) ); @@ -158,7 +157,7 @@ public function testRequestError(array $data) $data['method'], $data['url'], $data['version'], - $data['headers'], + [$this->converterMock->getContentTypeHeader()], json_encode($data['body']) ); $this->curlAdapterMock->expects($this->once()) @@ -184,7 +183,7 @@ public function testRequestError(array $data) $data['method'], $data['url'], $data['body'], - $data['headers'], + [$this->converterMock->getContentTypeHeader()], $data['version'] ) ); @@ -195,14 +194,13 @@ public function testRequestError(array $data) */ private function createJsonConverter() { - $converterMock = $this->getMockBuilder(ConverterInterface::class) - ->getMockForAbstractClass(); + $converterMock = $this->getMockBuilder(JsonConverter::class) + ->setMethodsExcept(['getContentTypeHeader']) + ->disableOriginalConstructor() + ->getMock(); $converterMock->expects($this->any())->method('toBody')->willReturnCallback(function ($value) { return json_encode($value); }); - $converterMock->expects($this->any()) - ->method('getContentTypeHeader') - ->willReturn(JsonConverter::CONTENT_TYPE_HEADER); return $converterMock; } } diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/JsonConverterTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/JsonConverterTest.php index 251f0d1474083..dad016a92d2f2 100644 --- a/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/JsonConverterTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/JsonConverterTest.php @@ -39,7 +39,10 @@ protected function setUp() public function testConverterContainsHeader() { - $this->assertEquals(JsonConverter::CONTENT_TYPE_HEADER, $this->converter->getContentTypeHeader()); + $this->assertEquals( + 'Content-Type: ' . JsonConverter::CONTENT_MEDIA_TYPE, + $this->converter->getContentTypeHeader() + ); } /** diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/ResponseResolverTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/ResponseResolverTest.php index f9fce447ca381..2564240c4fa11 100644 --- a/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/ResponseResolverTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/ResponseResolverTest.php @@ -73,8 +73,8 @@ public function testGetResultHandleResponseSuccess() $expectedBody = ['test' => 'testValue']; $response = new \Zend_Http_Response(201, ['Content-Type' => 'application/json'], json_encode($expectedBody)); $this->converterMock - ->method('getContentTypeHeader') - ->willReturn('Content-Type: application/json'); + ->method('getContentMediaType') + ->willReturn('application/json'); $this->successResponseHandlerMock ->expects($this->once()) @@ -99,8 +99,8 @@ public function testGetResultHandleResponseUnexpectedContentType() $expectedBody = 'testString'; $response = new \Zend_Http_Response(201, ['Content-Type' => 'plain/text'], $expectedBody); $this->converterMock - ->method('getContentTypeHeader') - ->willReturn('Content-Type: application/json'); + ->method('getContentMediaType') + ->willReturn('application/json'); $this->converterMock ->expects($this->never()) ->method('fromBody'); diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpCommandTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpCommandTest.php index db6cda7153c1a..ab79453a34048 100644 --- a/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpCommandTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpCommandTest.php @@ -163,7 +163,6 @@ private function getTestData() 'url' => 'http://www.mystore.com', 'access-token' => 'thisisaccesstoken', 'integration-token' => 'thisisintegrationtoken', - 'headers' => [JsonConverter::CONTENT_TYPE_HEADER], 'method' => \Magento\Framework\HTTP\ZendClient::POST, 'body'=> ['token' => 'thisisintegrationtoken','url' => 'http://www.mystore.com'], ]; From 7918eb45b47f9e006e576aa77343483b89a2d529 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Wed, 8 Aug 2018 11:47:08 -0500 Subject: [PATCH 0831/1171] MAGETWO-90863: Error handling responses from MBI --- .../Analytics/Model/Connector/Http/ConverterInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Analytics/Model/Connector/Http/ConverterInterface.php b/app/code/Magento/Analytics/Model/Connector/Http/ConverterInterface.php index 5f92e1104b313..ddd9fcba21109 100644 --- a/app/code/Magento/Analytics/Model/Connector/Http/ConverterInterface.php +++ b/app/code/Magento/Analytics/Model/Connector/Http/ConverterInterface.php @@ -34,5 +34,5 @@ public function getContentTypeHeader(); /** * @return string */ - public function getContentMediaType(): string ; + public function getContentMediaType(): string; } From d297ea24b0dce192c95678d0c1ccbd6097e69e13 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Wed, 8 Aug 2018 13:15:47 -0500 Subject: [PATCH 0832/1171] MC-3299: Build Stabilization for Sprint 13 PR - MC-3297 --- .../Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml index 283e90b688f20..8ec0c8349c104 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml @@ -20,6 +20,7 @@ <element name="InsertVariableBtn" type="button" selector=".scalable.add-variable.plugin"/> <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> <element name="imageSource" type="text" selector="//img[contains(@src,'{{var1}}')]" parameterized="true"/> + <element name="ImageAlt" type="text" selector="//img[contains(@alt,'{{var1}}')]" parameterized="true"/> </section> <section name="CmsDesignSection"> <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> From 9bdb9e0e8d7c4428112e470c2db5960e337f5b91 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Wed, 8 Aug 2018 14:35:55 -0500 Subject: [PATCH 0833/1171] MC-160: Admin should be able to delete catalog price rule --- .../Test/AdminDeleteCatalogPriceRuleTest.xml | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml new file mode 100644 index 0000000000000..93d19de131369 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCatalogPriceRuleTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Delete Catalog Price Rule"/> + <title value="Admin should be able to delete catalog price rule"/> + <description value="Admin should be able to delete catalog price rule"/> + <severity value="MAJOR"/> + <testCaseId value="MC-160"/> + <group value="CatalogRule"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="ApiCategory" stepKey="createCategory"/> + + <!-- Create a simple product --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Create a configurable product --> + <actionGroup ref="createConfigurableProduct" stepKey="createConfigurableProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + </after> + + <!-- Create a catalog price rule --> + <actionGroup ref="newCatalogPriceRuleByUI" stepKey="createNewPriceRule"/> + <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccess"/> + + <!-- Verify that category page shows the discount --> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryPage1"/> + <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(ApiSimpleProduct.name)}}" userInput="$$createSimpleProduct.name$$" stepKey="seeSimpleProduct1"/> + <see selector="{{StorefrontCategoryProductSection.ProductPriceByName(ApiSimpleProduct.name)}}" userInput="$110.70" stepKey="seeSimpleProductDiscount1"/> + <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(_defaultProduct.name)}}" userInput="{{_defaultProduct.name}}" stepKey="seeConfigurableProduct1"/> + <see selector="{{StorefrontCategoryProductSection.ProductPriceByName(_defaultProduct.name)}}" userInput="$0.90" stepKey="seeConfigurableProductDiscount1"/> + + <!-- Verify that the simple product page shows the discount --> + <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="goToSimpleProductPage1"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeCorrectName1"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="$$createSimpleProduct.sku$$" stepKey="seeCorrectSku1"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$110.70" stepKey="seeCorrectPrice1"/> + + <!-- Verify that the configurable product page the catalog price rule discount --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="goToConfigurableProductPage1"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{_defaultProduct.name}}" stepKey="seeCorrectName2"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{_defaultProduct.sku}}" stepKey="seeCorrectSku2"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$0.90" stepKey="seeCorrectPrice2"/> + + <!-- Delete the rule --> + <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule"> + <argument name="name" value="{{_defaultCatalogRule.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + + <!-- Apply and flush the cache --> + <click selector="{{AdminCatalogPriceRuleGrid.applyRules}}" stepKey="clickApplyRules"/> + <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache"/> + + <!-- Verify that category page shows the original prices --> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryPage2"/> + <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(ApiSimpleProduct.name)}}" userInput="$$createSimpleProduct.name$$" stepKey="seeSimpleProduct2"/> + <see selector="{{StorefrontCategoryProductSection.ProductPriceByName(ApiSimpleProduct.name)}}" userInput="$123.00" stepKey="seeSimpleProductDiscount2"/> + <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(_defaultProduct.name)}}" userInput="{{_defaultProduct.name}}" stepKey="seeConfigurableProduct2"/> + <see selector="{{StorefrontCategoryProductSection.ProductPriceByName(_defaultProduct.name)}}" userInput="$1.00" stepKey="seeConfigurableProductDiscount2"/> + + <!-- Verify that the simple product page shows the original price --> + <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="goToSimpleProductPage2"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeCorrectName3"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="$$createSimpleProduct.sku$$" stepKey="seeCorrectSku3"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$123.00" stepKey="seeCorrectPrice3"/> + + <!-- Verify that the configurable product page shows the original price --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="goToConfigurableProductPage2"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{_defaultProduct.name}}" stepKey="seeCorrectName4"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{_defaultProduct.sku}}" stepKey="seeCorrectSku4"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$1.00" stepKey="seeCorrectPrice4"/> + </test> +</tests> From b0695ce5abe341ee45593a69f047b4f47590438e Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Wed, 8 Aug 2018 15:14:30 -0500 Subject: [PATCH 0834/1171] MC-3299: Build Stabilization for Sprint 13 PR - MC-3297 --- app/code/Magento/Cms/Api/BlockRepositoryInterface.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Cms/Api/BlockRepositoryInterface.php b/app/code/Magento/Cms/Api/BlockRepositoryInterface.php index 9b285051fc44b..b713ca91ea852 100644 --- a/app/code/Magento/Cms/Api/BlockRepositoryInterface.php +++ b/app/code/Magento/Cms/Api/BlockRepositoryInterface.php @@ -5,8 +5,6 @@ */ namespace Magento\Cms\Api; -use Magento\Framework\Api\SearchCriteriaInterface; - /** * CMS block CRUD interface. * @api From 89dc4b708748b68cd185feee5ccd15a297a99a4f Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Wed, 8 Aug 2018 15:18:38 -0500 Subject: [PATCH 0835/1171] MAGETWO-90591: Add Selected Button shouldn't be enabled if there are no images available --- .../Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml index d6e055c43322a..74d8f9962540a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml @@ -39,7 +39,6 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoading2" /> <see selector="{{ProductDescriptionWYSIWYGToolbarSection.CancelBtn}}" userInput="Cancel" stepKey="seeCancelBtn1" /> <see selector="{{ProductDescriptionWYSIWYGToolbarSection.CreateFolder}}" userInput="Create Folder" stepKey="seeCreateFolderBtn1" /> - <see selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertFile}}" userInput="Add Selected" stepKey="seeAddSelectedBtn1" /> <click selector="{{ProductDescriptionWYSIWYGToolbarSection.CreateFolder}}" stepKey="createFolder1"/> <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.FolderName}}" stepKey="waitForPopUp1" /> <fillField selector="{{ProductDescriptionWYSIWYGToolbarSection.FolderName}}" userInput="{{ImageFolder.name}}" stepKey="fillFolderName1" /> @@ -78,7 +77,6 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoading8" /> <see selector="{{ProductShortDescriptionWYSIWYGToolbarSection.CancelBtn}}" userInput="Cancel" stepKey="seeCancelBtn2" /> <see selector="{{ProductShortDescriptionWYSIWYGToolbarSection.CreateFolder}}" userInput="Create Folder" stepKey="seeCreateFolderBtn2" /> - <see selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertFile}}" userInput="Add Selected" stepKey="seeAddSelectedBtn2" /> <attachFile selector="{{ProductShortDescriptionWYSIWYGToolbarSection.BrowseUploadImage}}" userInput="{{ImageUpload3.value}}" stepKey="uploadImage3"/> <waitForElementVisible selector="{{ProductShortDescriptionWYSIWYGToolbarSection.image(ImageUpload3.value)}}" stepKey="waitForUploadImage3" /> <waitForLoadingMaskToDisappear stepKey="waitForLoading9" /> From 9c8b2e21047446bde902bd3da91425a0241a83db Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Wed, 8 Aug 2018 15:31:08 -0500 Subject: [PATCH 0836/1171] MAGETWO-90863: Error handling responses from MBI --- .../Unit/Model/Connector/Http/Client/CurlTest.php | 4 ++++ .../Model/Connector/Http/JsonConverterTest.php | 9 +++++++++ .../Unit/Model/Connector/SignUpCommandTest.php | 14 ++++++++++++++ .../Unit/Model/Paypal/Helper/QuoteUpdaterTest.php | 2 +- .../Product/Listing/Collector/MsrpPriceTest.php | 2 +- .../Adminhtml/Order/Create/ReorderTest.php | 4 +++- .../Sales/Test/Unit/Model/Order/ConfigTest.php | 1 + .../Product/Listing/Collector/TaxTest.php | 2 ++ .../Product/Listing/Collector/WeeeTest.php | 4 +++- 9 files changed, 38 insertions(+), 4 deletions(-) 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 index cc432e338741b..b84ce53247f98 100644 --- 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 @@ -104,7 +104,9 @@ public function getTestData() } /** + * @param array $data * @return void + * @throws \Zend_Http_Exception * @dataProvider getTestData */ public function testRequestSuccess(array $data) @@ -145,7 +147,9 @@ public function testRequestSuccess(array $data) } /** + * @param array $data * @return void + * @throws \Zend_Http_Exception * @dataProvider getTestData */ public function testRequestError(array $data) diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/JsonConverterTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/JsonConverterTest.php index dad016a92d2f2..d3258c8ae9caa 100644 --- a/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/JsonConverterTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/JsonConverterTest.php @@ -25,6 +25,9 @@ class JsonConverterTest extends \PHPUnit\Framework\TestCase */ private $converter; + /** + * @return void + */ protected function setUp() { $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -37,6 +40,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testConverterContainsHeader() { $this->assertEquals( @@ -69,6 +75,9 @@ public function convertBodyDataProvider() ]; } + /** + * return void + */ public function testConvertData() { $this->serializerMock->expects($this->once()) diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpCommandTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpCommandTest.php index ab79453a34048..c113b2dc275dd 100644 --- a/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpCommandTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpCommandTest.php @@ -57,6 +57,9 @@ class SignUpCommandTest extends \PHPUnit\Framework\TestCase */ private $responseResolverMock; + /** + * @return void + */ protected function setUp() { $this->analyticsTokenMock = $this->getMockBuilder(AnalyticsToken::class) @@ -91,6 +94,10 @@ protected function setUp() ); } + /** + * @throws \Zend_Http_Exception + * @return void + */ public function testExecuteSuccess() { $this->integrationManagerMock->expects($this->once()) @@ -124,6 +131,9 @@ public function testExecuteSuccess() $this->assertTrue($this->signUpCommand->execute()); } + /** + * @return void + */ public function testExecuteFailureCannotGenerateToken() { $this->integrationManagerMock->expects($this->once()) @@ -134,6 +144,10 @@ public function testExecuteFailureCannotGenerateToken() $this->assertFalse($this->signUpCommand->execute()); } + /** + * @throws \Zend_Http_Exception + * @return void + */ public function testExecuteFailureResponseIsEmpty() { $this->integrationManagerMock->expects($this->once()) diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php index 76bf5b659bda3..50dd371ac3ad8 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php @@ -300,7 +300,7 @@ private function getQuoteMock() $cartExtensionMock = $this->getMockBuilder(CartExtensionInterface::class) ->setMethods(['setShippingAssignments']) ->disableOriginalConstructor() - ->getMock(); + ->getMockForAbstractClass(); $quoteMock->expects(self::any()) ->method('getExtensionAttributes') diff --git a/app/code/Magento/Msrp/Test/Unit/Ui/DataProvider/Product/Listing/Collector/MsrpPriceTest.php b/app/code/Magento/Msrp/Test/Unit/Ui/DataProvider/Product/Listing/Collector/MsrpPriceTest.php index 90b060f51073f..f99d48285d7af 100644 --- a/app/code/Magento/Msrp/Test/Unit/Ui/DataProvider/Product/Listing/Collector/MsrpPriceTest.php +++ b/app/code/Magento/Msrp/Test/Unit/Ui/DataProvider/Product/Listing/Collector/MsrpPriceTest.php @@ -101,7 +101,7 @@ public function testCollect() \Magento\Catalog\Api\Data\ProductRender\PriceInfoExtensionInterface::class ) ->setMethods(['setMsrp']) - ->getMock(); + ->getMockForAbstractClass(); $priceInfo = $this->getMockBuilder(MsrpPriceInfoInterface::class) ->setMethods(['getPrice', 'getExtensionAttributes']) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php index 1fdd9759f5045..8203b4486d193 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php @@ -118,7 +118,9 @@ protected function setUp() ->getMock(); $this->requestMock = $this->getMockBuilder(RequestInterface::class)->getMockForAbstractClass(); $this->objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class)->getMockForAbstractClass(); - $this->resultForwardFactoryMock = $this->getMockBuilder(ForwardFactory::class)->getMock(); + $this->resultForwardFactoryMock = $this->getMockBuilder(ForwardFactory::class) + ->disableOriginalConstructor() + ->getMock(); $this->resultRedirectFactoryMock = $this->getMockBuilder(RedirectFactory::class) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php index 86419c0c905b6..decce409b3331 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php @@ -47,6 +47,7 @@ protected function setUp() 'storeManager' => $this->storeManagerMock, ]); $this->statusFactoryMock = $this->getMockBuilder(\Magento\Sales\Model\Order\StatusFactory::class) + ->disableOriginalConstructor() ->setMethods(['load', 'create']) ->getMock(); $this->orderStatusCollectionFactoryMock = $this->createPartialMock( diff --git a/app/code/Magento/Tax/Test/Unit/Ui/DataProvider/Product/Listing/Collector/TaxTest.php b/app/code/Magento/Tax/Test/Unit/Ui/DataProvider/Product/Listing/Collector/TaxTest.php index 4cf3f4d339e18..908aeb5e3f405 100644 --- a/app/code/Magento/Tax/Test/Unit/Ui/DataProvider/Product/Listing/Collector/TaxTest.php +++ b/app/code/Magento/Tax/Test/Unit/Ui/DataProvider/Product/Listing/Collector/TaxTest.php @@ -68,10 +68,12 @@ protected function setUp() ->getMockForAbstractClass(); $this->priceInfoFactory = $this->getMockBuilder(PriceInfoInterfaceFactory::class) + ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); $this->priceInfoExtensionFactory = $this->getMockBuilder(PriceInfoExtensionInterfaceFactory::class) + ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); $this->formattedPriceInfoBuilder = $this->getMockBuilder(FormattedPriceInfoBuilder::class) diff --git a/app/code/Magento/Weee/Test/Unit/Ui/DataProvider/Product/Listing/Collector/WeeeTest.php b/app/code/Magento/Weee/Test/Unit/Ui/DataProvider/Product/Listing/Collector/WeeeTest.php index 6be1713c99004..e6353a2b4b76a 100644 --- a/app/code/Magento/Weee/Test/Unit/Ui/DataProvider/Product/Listing/Collector/WeeeTest.php +++ b/app/code/Magento/Weee/Test/Unit/Ui/DataProvider/Product/Listing/Collector/WeeeTest.php @@ -50,14 +50,16 @@ protected function setUp() ->getMockForAbstractClass(); $this->weeeAdjustmentAttributeFactory = $this->getMockBuilder(WeeeAdjustmentAttributeInterfaceFactory::class) + ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); $this->extensionAttributes = $this->getMockBuilder(PriceInfoExtensionInterface::class) ->setMethods(['setWeeeAttributes', 'setWeeeAdjustment']) - ->getMock(); + ->getMockForAbstractClass(); $this->priceInfoExtensionFactory = $this->getMockBuilder(PriceInfoExtensionInterfaceFactory::class) + ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); From 586737e804d09ea779697ddc1afef030b7c173c6 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Wed, 8 Aug 2018 15:55:36 -0500 Subject: [PATCH 0837/1171] MAGETWO-90863: Error handling responses from MBI --- .../Model/Connector/Http/Client/CurlTest.php | 1 + .../Model/Paypal/Helper/QuoteUpdaterTest.php | 10 ++++++ .../Listing/Collector/MsrpPriceTest.php | 6 ++++ .../Adminhtml/Order/Create/ReorderTest.php | 36 +++++++++++++++++-- .../Test/Unit/Model/Order/ConfigTest.php | 9 +++++ .../Product/Listing/Collector/TaxTest.php | 6 ++++ .../Product/Listing/Collector/WeeeTest.php | 6 ++++ 7 files changed, 71 insertions(+), 3 deletions(-) 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 index b84ce53247f98..92f79c2bf6dee 100644 --- 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 @@ -12,6 +12,7 @@ /** * A unit test for testing of the CURL HTTP client. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CurlTest extends \PHPUnit\Framework\TestCase { diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php index 50dd371ac3ad8..62452228b6186 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php @@ -51,6 +51,9 @@ class QuoteUpdaterTest extends \PHPUnit\Framework\TestCase */ private $quoteUpdater; + /** + * @return void + */ protected function setUp() { $this->configMock = $this->getMockBuilder(Config::class) @@ -99,6 +102,10 @@ protected function setUp() ); } + /** + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testExecute() { $details = $this->getDetails(); @@ -121,6 +128,9 @@ public function testExecute() $this->quoteUpdater->execute(self::TEST_NONCE, $details, $quoteMock); } + /** + * @return void + */ private function disabledQuoteAddressValidationStep() { $this->billingAddressMock->expects(self::once()) diff --git a/app/code/Magento/Msrp/Test/Unit/Ui/DataProvider/Product/Listing/Collector/MsrpPriceTest.php b/app/code/Magento/Msrp/Test/Unit/Ui/DataProvider/Product/Listing/Collector/MsrpPriceTest.php index f99d48285d7af..adf2a5b4a0e9e 100644 --- a/app/code/Magento/Msrp/Test/Unit/Ui/DataProvider/Product/Listing/Collector/MsrpPriceTest.php +++ b/app/code/Magento/Msrp/Test/Unit/Ui/DataProvider/Product/Listing/Collector/MsrpPriceTest.php @@ -50,6 +50,9 @@ class MsrpPriceTest extends \PHPUnit\Framework\TestCase */ private $priceInfoExtensionFactory; + /** + * @return void + */ protected function setUp() { $this->priceCurrencyMock = $this->getMockBuilder(\Magento\Framework\Pricing\PriceCurrencyInterface::class) @@ -86,6 +89,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testCollect() { $product = $this->getMockBuilder(Product::class) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php index 8203b4486d193..b11d73de736d4 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php @@ -108,6 +108,9 @@ class ReorderTest extends \PHPUnit\Framework\TestCase */ private $orderId; + /** + * @return void + */ protected function setUp() { $this->orderId = 111; @@ -161,6 +164,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testExecuteForward() { $this->clearStorage(); @@ -171,6 +177,9 @@ public function testExecuteForward() $this->assertInstanceOf(Forward::class, $this->reorder->execute()); } + /** + * @return void + */ public function testExecuteRedirectOrderGrid() { $this->clearStorage(); @@ -183,6 +192,9 @@ public function testExecuteRedirectOrderGrid() $this->assertInstanceOf(Redirect::class, $this->reorder->execute()); } + /** + * @return void + */ public function testExecuteRedirectBack() { $this->clearStorage(); @@ -197,6 +209,9 @@ public function testExecuteRedirectBack() $this->assertInstanceOf(Redirect::class, $this->reorder->execute()); } + /** + * @return void + */ public function testExecuteRedirectNewOrder() { $this->clearStorage(); @@ -211,6 +226,9 @@ public function testExecuteRedirectNewOrder() $this->assertInstanceOf(Redirect::class, $this->reorder->execute()); } + /** + * @return void + */ private function clearStorage() { $this->objectManagerMock->expects($this->at(0)) @@ -220,6 +238,9 @@ private function clearStorage() $this->quoteSessionMock->expects($this->once())->method('clearStorage')->will($this->returnSelf()); } + /** + * @return void + */ private function getOrder() { $this->requestMock->expects($this->once()) @@ -237,20 +258,26 @@ private function getOrder() */ private function canReorder($result) { - $entity_id = 1; - $this->orderMock->expects($this->once())->method('getEntityId')->willReturn($entity_id); + $entityId = 1; + $this->orderMock->expects($this->once())->method('getEntityId')->willReturn($entityId); $this->reorderHelperMock->expects($this->once()) ->method('canReorder') - ->with($entity_id) + ->with($entityId) ->willReturn($result); } + /** + * @return void + */ private function prepareForward() { $this->resultForwardFactoryMock->expects($this->once())->method('create')->willReturn($this->resultForwardMock); $this->resultForwardMock->expects($this->once())->method('forward')->with('noroute')->willReturnSelf(); } + /** + * @return void + */ private function createRedirect() { $this->resultRedirectFactoryMock->expects($this->once()) @@ -286,6 +313,9 @@ private function getUnavailableProducts(array $unavailableProducts) ->willReturn($unavailableProducts); } + /** + * @return void + */ private function initFromOrder() { $this->orderMock->expects($this->once())->method('setReordered')->with(true)->willReturnSelf(); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php index decce409b3331..feee2816b2cd4 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php @@ -38,6 +38,9 @@ class ConfigTest extends \PHPUnit\Framework\TestCase */ protected $storeManagerMock; + /** + * @return void + */ protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -64,6 +67,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testGetInvisibleOnFrontStatuses() { $statuses = [ @@ -110,6 +116,9 @@ public function testGetInvisibleOnFrontStatuses() $this->assertSame($expectedResult, $result); } + /** + * @return void + */ public function testGetStateLabelByStateAndStatus() { $statuses = [ diff --git a/app/code/Magento/Tax/Test/Unit/Ui/DataProvider/Product/Listing/Collector/TaxTest.php b/app/code/Magento/Tax/Test/Unit/Ui/DataProvider/Product/Listing/Collector/TaxTest.php index 908aeb5e3f405..3f80d97ea921b 100644 --- a/app/code/Magento/Tax/Test/Unit/Ui/DataProvider/Product/Listing/Collector/TaxTest.php +++ b/app/code/Magento/Tax/Test/Unit/Ui/DataProvider/Product/Listing/Collector/TaxTest.php @@ -54,6 +54,9 @@ class TaxTest extends \PHPUnit\Framework\TestCase */ private $formattedPriceInfoBuilder; + /** + * @return void + */ protected function setUp() { $this->priceCurrencyMock = $this->getMockBuilder(PriceCurrencyInterface::class) @@ -88,6 +91,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testCollect() { $amountValue = 10; diff --git a/app/code/Magento/Weee/Test/Unit/Ui/DataProvider/Product/Listing/Collector/WeeeTest.php b/app/code/Magento/Weee/Test/Unit/Ui/DataProvider/Product/Listing/Collector/WeeeTest.php index e6353a2b4b76a..266737089cd21 100644 --- a/app/code/Magento/Weee/Test/Unit/Ui/DataProvider/Product/Listing/Collector/WeeeTest.php +++ b/app/code/Magento/Weee/Test/Unit/Ui/DataProvider/Product/Listing/Collector/WeeeTest.php @@ -41,6 +41,9 @@ class WeeeTest extends \PHPUnit\Framework\TestCase /** @var FormattedPriceInfoBuilder|\PHPUnit_Framework_MockObject_MockObject */ private $formattedPriceInfoBuilder; + /** + * @return void + */ protected function setUp() { $this->weeeHelperMock = $this->getMockBuilder(\Magento\Weee\Helper\Data::class) @@ -76,6 +79,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testCollect() { $productMock = $this->getMockBuilder(Product::class) From 0345e5418ee2ac33768473015a397464cf2a0c48 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Wed, 8 Aug 2018 16:55:50 -0500 Subject: [PATCH 0838/1171] MC-3299: Build Stabilization for Sprint 13 PR - MC-3297 --- .../Test/TestCase/OnePageCheckoutOfflinePaymentMethodsTest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutOfflinePaymentMethodsTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutOfflinePaymentMethodsTest.xml index b999900042c37..387e6418ee572 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutOfflinePaymentMethodsTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutOfflinePaymentMethodsTest.xml @@ -102,7 +102,7 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCorrect" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderButtonsAvailable" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> - <data name="issue" xsi:type="string">MAGETWO-66737: Magento\Checkout\Test\TestCase\OnePageCheckoutTest with OnePageCheckoutTestVariation3 is not stable</data> + <data name="issue" xsi:type="string">MAGETWO-66737: Magento\Checkout\Test\TestCase\OnePageCheckoutTest with OnePageCheckoutTestVariation3 and 4 is not stable</data> </variation> <variation name="OnePageCheckoutTestVariation4" summary="One Page Checkout Products with Special Prices" ticketId="MAGETWO-12429"> <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test, severity:S0</data> @@ -125,6 +125,7 @@ <constraint name="Magento\Shipping\Test\Constraint\AssertShipmentSuccessCreateMessage" /> <constraint name="Magento\Checkout\Test\Constraint\AssertMinicartEmpty" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal"/> + <data name="issue" xsi:type="string">MAGETWO-66737: Magento\Checkout\Test\TestCase\OnePageCheckoutTest with OnePageCheckoutTestVariation3 and 4 is not stable</data> </variation> <variation name="OnePageCheckoutTestVariation5" summary="Guest Checkout using Check/Money Order and Free Shipping with Prices/Taxes Verifications" ticketId="MAGETWO-12412"> <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test, severity:S0</data> From 2f13c4497cf08f92281b265dfb8a44f997eaf961 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 8 Aug 2018 17:06:19 -0500 Subject: [PATCH 0839/1171] MAGETWO-91504: Mobile PDP accordion widget hides accordion content on phones with iOS - fix js static --- lib/web/mage/collapsible.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/web/mage/collapsible.js b/lib/web/mage/collapsible.js index e432994705178..245c4a38a6aa9 100644 --- a/lib/web/mage/collapsible.js +++ b/lib/web/mage/collapsible.js @@ -448,9 +448,7 @@ define([ if (this.options.animate) { this._animate(showProps); } else { - if (this._isElementOutOfViewport(this.content.get(0).parentElement)) { - this.content.get(0).parentElement.scrollIntoView(); - } + this._scrollToTopIfVisible(this.content.get(0).parentElement); this.content.show(); } this._open(); @@ -562,8 +560,20 @@ define([ * @param {HTMLElement} elem * @private */ + _scrollToTopIfVisible: function (elem) { + if (this._isElementOutOfViewport(elem)) { + elem.scrollIntoView(); + } + }, + + /** + * @param {HTMLElement} elem + * @private + * @return {Boolean} + */ _isElementOutOfViewport: function (elem) { var rect = elem.getBoundingClientRect(); + return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight; } }); From 0826f349632be9101f7429c8770ad4619317e21e Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Thu, 9 Aug 2018 10:02:12 +0300 Subject: [PATCH 0840/1171] MAGETWO-94086: [Forwardport] Run Catalog Search reindex in multithread mode with AdvancedSearch --- .../Model/ResourceModel/Index.php | 28 ++++++++++++++----- .../Magento/Elasticsearch/etc/indexer.xml | 1 + .../Pricing/Render/FinalPriceBoxTest.php | 4 +-- .../Model/Order/Address/RendererTest.php | 2 +- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/AdvancedSearch/Model/ResourceModel/Index.php b/app/code/Magento/AdvancedSearch/Model/ResourceModel/Index.php index 7751a3b75092d..1835ef32f5677 100644 --- a/app/code/Magento/AdvancedSearch/Model/ResourceModel/Index.php +++ b/app/code/Magento/AdvancedSearch/Model/ResourceModel/Index.php @@ -16,6 +16,7 @@ use Magento\Catalog\Model\Indexer\Category\Product\AbstractAction; use Magento\Framework\Search\Request\IndexScopeResolverInterface as TableResolver; use Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; /** * @api @@ -47,6 +48,11 @@ class Index extends AbstractDb */ private $dimensionCollectionFactory; + /** + * @var int|null + */ + private $websiteId; + /** * Index constructor. * @param Context $context @@ -94,12 +100,17 @@ protected function _getCatalogProductPriceData($productIds = null) $catalogProductIndexPriceSelect = []; foreach ($this->dimensionCollectionFactory->create() as $dimensions) { - $catalogProductIndexPriceSelect[] = $connection->select()->from( - $this->tableResolver->resolve('catalog_product_index_price', $dimensions), - ['entity_id', 'customer_group_id', 'website_id', 'min_price'] - ); - if ($productIds) { - current($catalogProductIndexPriceSelect)->where('entity_id IN (?)', $productIds); + if (!isset($dimensions[WebsiteDimensionProvider::DIMENSION_NAME]) || + $this->websiteId === null || + $dimensions[WebsiteDimensionProvider::DIMENSION_NAME]->getValue() === $this->websiteId) { + $select = $connection->select()->from( + $this->tableResolver->resolve('catalog_product_index_price', $dimensions), + ['entity_id', 'customer_group_id', 'website_id', 'min_price'] + ); + if ($productIds) { + $select->where('entity_id IN (?)', $productIds); + } + $catalogProductIndexPriceSelect[] = $select; } } @@ -123,9 +134,12 @@ protected function _getCatalogProductPriceData($productIds = null) */ public function getPriceIndexData($productIds, $storeId) { + $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId(); + + $this->websiteId = $websiteId; $priceProductsIndexData = $this->_getCatalogProductPriceData($productIds); + $this->websiteId = null; - $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId(); if (!isset($priceProductsIndexData[$websiteId])) { return []; } diff --git a/app/code/Magento/Elasticsearch/etc/indexer.xml b/app/code/Magento/Elasticsearch/etc/indexer.xml index 245829396a67b..8d75a59f84048 100644 --- a/app/code/Magento/Elasticsearch/etc/indexer.xml +++ b/app/code/Magento/Elasticsearch/etc/indexer.xml @@ -9,6 +9,7 @@ <indexer id="catalogsearch_fulltext"> <dependencies> <indexer id="cataloginventory_stock" /> + <indexer id="catalog_product_price" /> </dependencies> </indexer> </config> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Pricing/Render/FinalPriceBoxTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Pricing/Render/FinalPriceBoxTest.php index c6d6c3cf42214..88d3c3a6c9b1a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Pricing/Render/FinalPriceBoxTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Pricing/Render/FinalPriceBoxTest.php @@ -125,7 +125,7 @@ protected function setUp() /** * @magentoDataFixture Magento/Catalog/_files/product_has_tier_price_show_as_low_as.php - * @magentoDbIsolation enabled + * @magentoDbIsolation disabled * @magentoAppIsolation enabled */ public function testRenderAmountMinimalProductWithTierPricesShouldShowMinTierPrice() @@ -136,7 +136,7 @@ public function testRenderAmountMinimalProductWithTierPricesShouldShowMinTierPri /** * @magentoDataFixture Magento/Catalog/_files/product_different_store_prices.php - * @magentoDbIsolation enabled + * @magentoDbIsolation disabled * @magentoAppIsolation enabled * @magentoConfigFixture current_store catalog/frontend/flat_catalog_product 1 */ diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Address/RendererTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Address/RendererTest.php index 1bad0eec7d1d0..1df0864e1874d 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Address/RendererTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Address/RendererTest.php @@ -46,7 +46,7 @@ protected function setUp() /** * @magentoDataFixture Magento/Sales/_files/order_fixture_store.php - * @magentoDbIsolation enabled + * @magentoDbIsolation disabled * @magentoAppIsolation enabled */ public function testFormat() From 6a964ff5569e76972e07c61d04f1d4c3c181726b Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <strpwebstudio@gmail.com> Date: Thu, 9 Aug 2018 10:06:05 +0300 Subject: [PATCH 0841/1171] [Forwardport] Improvements in UI component - Code refactoring according to Magento code standards --- app/code/Magento/Ui/view/base/web/js/grid/massactions.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/massactions.js b/app/code/Magento/Ui/view/base/web/js/grid/massactions.js index 758a8341e1736..48c04458ff49a 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/massactions.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/massactions.js @@ -175,10 +175,10 @@ define([ * invoked if action is confirmed. */ _confirm: function (action, callback) { - var confirmData = action.confirm; - var data = this.getSelections(); - var total = data.total ? data.total : 0; - var confirmMessage = confirmData.message + ' (' + total + ' record' + (total > 1 ? 's' : '') + ')'; + var confirmData = action.confirm, + data = this.getSelections(), + total = data.total ? data.total : 0, + confirmMessage = confirmData.message + ' (' + total + ' record' + (total > 1 ? 's' : '') + ')'; confirm({ title: confirmData.title, From 73b794b5a17e4f1c318837615d04563b0d808621 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Thu, 9 Aug 2018 10:17:06 +0300 Subject: [PATCH 0842/1171] MAGETWO-93963: [2.3] Double click (not too fast) on proceed to check out after view edit mini cart returns empty shopping cart --- app/code/Magento/Checkout/view/frontend/web/js/sidebar.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js index debb1acf90d4f..3b5168453e11a 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js @@ -61,13 +61,15 @@ define([ }; events['click ' + this.options.button.checkout] = $.proxy(function () { var cart = customerData.get('cart'), - customer = customerData.get('customer'); + customer = customerData.get('customer'), + element = $(this.options.button.checkout); if (!customer().firstname && cart().isGuestCheckoutAllowed === false) { // set URL for redirect on successful login/registration. It's postprocessed on backend. $.cookie('login_redirect', this.options.url.checkout); if (this.options.url.isRedirectRequired) { + element.prop('disabled', true); location.href = this.options.url.loginUrl; } else { authenticationPopup.showModal(); @@ -75,6 +77,7 @@ define([ return false; } + element.prop('disabled', true); location.href = this.options.url.checkout; }, this); From 736c768afad5e2d3600c760438b74facc5e98ae3 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Wed, 8 Aug 2018 19:35:20 +0300 Subject: [PATCH 0843/1171] MAGETWO-91737: Customer Address attribute value length is still validated when min/max length fields are not displayed at the backend --- .../Customer/Model/Metadata/Form/Text.php | 10 ++-- .../Unit/Controller/Address/FormPostTest.php | 54 ++++++++++++++----- .../Unit/Model/Metadata/Form/TextTest.php | 3 ++ .../Model/Attribute/Data/MultilineTest.php | 3 ++ .../Unit/Model/Attribute/Data/TextTest.php | 21 ++++++-- .../DataProvider/EavValidationRulesTest.php | 7 ++- .../Customer/Controller/AddressTest.php | 3 ++ .../Controller/Adminhtml/IndexTest.php | 11 +++- 8 files changed, 90 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/Customer/Model/Metadata/Form/Text.php b/app/code/Magento/Customer/Model/Metadata/Form/Text.php index 7eedd3bec8dda..c8b9a1e46a127 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/Text.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/Text.php @@ -43,7 +43,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function extractValue(\Magento\Framework\App\RequestInterface $request) { @@ -51,7 +51,7 @@ public function extractValue(\Magento\Framework\App\RequestInterface $request) } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -88,7 +88,7 @@ public function validateValue($value) } /** - * {@inheritdoc} + * @inheritdoc */ public function compactValue($value) { @@ -96,7 +96,7 @@ public function compactValue($value) } /** - * {@inheritdoc} + * @inheritdoc */ public function restoreValue($value) { @@ -104,7 +104,7 @@ public function restoreValue($value) } /** - * {@inheritdoc} + * @inheritdoc */ public function outputValue($format = \Magento\Customer\Model\Metadata\ElementFactory::OUTPUT_FORMAT_TEXT) { diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php index a2766d42403ba..c2a795fc95016 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Customer\Test\Unit\Controller\Address; use Magento\Customer\Api\AddressRepositoryInterface; @@ -162,6 +163,9 @@ class FormPostTest extends \PHPUnit\Framework\TestCase */ private $customerAddressMapper; + /** + * {@inheritDoc} + */ protected function setUp() { $this->prepareContext(); @@ -230,7 +234,10 @@ protected function setUp() ); } - protected function prepareContext() + /** + * Prepares context + */ + protected function prepareContext(): void { $this->context = $this->getMockBuilder(\Magento\Framework\App\Action\Context::class) ->disableOriginalConstructor() @@ -284,7 +291,10 @@ protected function prepareContext() ->willReturn($this->messageManager); } - protected function prepareAddress() + /** + * Prepare address + */ + protected function prepareAddress(): void { $this->addressRepository = $this->getMockBuilder(\Magento\Customer\Api\AddressRepositoryInterface::class) ->getMockForAbstractClass(); @@ -303,7 +313,10 @@ protected function prepareAddress() ->willReturn($this->addressData); } - protected function prepareRegion() + /** + * Prepare region + */ + protected function prepareRegion(): void { $this->region = $this->getMockBuilder(\Magento\Directory\Model\Region::class) ->disableOriginalConstructor() @@ -335,7 +348,10 @@ protected function prepareRegion() ->willReturn($this->regionData); } - protected function prepareForm() + /** + * Prepare form + */ + protected function prepareForm(): void { $this->form = $this->getMockBuilder(\Magento\Customer\Model\Metadata\Form::class) ->disableOriginalConstructor() @@ -346,7 +362,10 @@ protected function prepareForm() ->getMock(); } - public function testExecuteNoFormKey() + /** + * Test form without formKey + */ + public function testExecuteNoFormKey(): void { $this->formKeyValidator->expects($this->once()) ->method('validate') @@ -361,7 +380,10 @@ public function testExecuteNoFormKey() $this->assertEquals($this->resultRedirect, $this->model->execute()); } - public function testExecuteNoPostData() + /** + * Test executing without post data + */ + public function testExecuteNoPostData(): void { $postValue = 'post_value'; $url = 'url'; @@ -409,10 +431,11 @@ public function testExecuteNoPostData() } /** + * Tests executing + * * @param int $addressId * @param int $countryId * @param int $customerId - * @param bool $isRegionRequired * @param int $regionId * @param string $region * @param string $regionCode @@ -433,7 +456,7 @@ public function testExecute( $newRegionId, $newRegion, $newRegionCode - ) { + ): void { $existingAddressData = [ 'country_id' => $countryId, 'region_id' => $regionId, @@ -517,7 +540,8 @@ public function testExecute( ->willReturnMap([ [ $this->regionData, - $regionData, \Magento\Customer\Api\Data\RegionInterface::class, + $regionData, + \Magento\Customer\Api\Data\RegionInterface::class, $this->dataObjectHelper, ], [ @@ -581,7 +605,7 @@ public function testExecute( /** * @return array */ - public function dataProviderTestExecute() + public function dataProviderTestExecute(): array { return [ [1, 1, 1, null, '', null, '', null, ''], @@ -612,7 +636,10 @@ public function dataProviderTestExecute() ]; } - public function testExecuteInputException() + /** + * Tests input exception + */ + public function testExecuteInputException(): void { $addressId = 1; $postValue = 'post_value'; @@ -674,7 +701,10 @@ public function testExecuteInputException() $this->assertEquals($this->resultRedirect, $this->model->execute()); } - public function testExecuteException() + /** + * Tests exception + */ + public function testExecuteException(): void { $addressId = 1; $postValue = 'post_value'; diff --git a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/TextTest.php b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/TextTest.php index 292d46a936092..7987bdc79ed98 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/TextTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/TextTest.php @@ -16,6 +16,9 @@ class TextTest extends AbstractFormTestCase /** @var \Magento\Framework\Stdlib\StringUtils */ protected $stringHelper; + /** + * {@inheritDoc} + */ protected function setUp() { parent::setUp(); diff --git a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/MultilineTest.php b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/MultilineTest.php index 5eeef79df6d2f..bde4a3adb9de5 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/MultilineTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/MultilineTest.php @@ -21,6 +21,9 @@ class MultilineTest extends \PHPUnit\Framework\TestCase */ protected $stringMock; + /** + * {@inheritDoc} + */ protected function setUp() { /** @var TimezoneInterface $timezoneMock */ diff --git a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php index 217a04045b939..bbbe712b2bb42 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php @@ -13,6 +13,9 @@ class TextTest extends \PHPUnit\Framework\TestCase */ protected $_model; + /** + * {@inheritDoc} + */ protected function setUp() { $locale = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); @@ -33,19 +36,28 @@ protected function setUp() ); } + /** + * {@inheritDoc} + */ protected function tearDown() { $this->_model = null; } - public function testValidateValueString() + /** + * Test for string validation + */ + public function testValidateValueString(): void { $inputValue = '0'; $expectedResult = true; $this->assertEquals($expectedResult, $this->_model->validateValue($inputValue)); } - public function testValidateValueInteger() + /** + * Test for integer validation + */ + public function testValidateValueInteger(): void { $inputValue = 0; $expectedResult = ['"Test" is a required value.']; @@ -53,7 +65,10 @@ public function testValidateValueInteger() $this->assertEquals($expectedResult, [(string)$result[0]]); } - public function testWithoutLengthValidation() + /** + * Test without length validation + */ + public function testWithoutLengthValidation(): void { $expectedResult = true; $defaultAttributeData = [ diff --git a/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php b/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php index b9a5262e64eb8..cdb62ce1db68d 100644 --- a/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php +++ b/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php @@ -30,6 +30,9 @@ class EavValidationRulesTest extends \PHPUnit\Framework\TestCase */ protected $attributeMock; + /** + * {@inheritDoc} + */ protected function setUp() { $this->objectManager = new ObjectManager($this); @@ -43,11 +46,13 @@ protected function setUp() } /** + * @param string $attributeInputType + * @param mixed $validateRules * @param array $data * @param array $expected * @dataProvider buildDataProvider */ - public function testBuild($attributeInputType, $validateRules, $data, $expected) + public function testBuild($attributeInputType, $validateRules, $data, $expected): void { $this->attributeMock->expects($this->once())->method('getFrontendInput')->willReturn($attributeInputType); $this->attributeMock->expects($this->any())->method('getValidateRules')->willReturn($validateRules); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php index 5357a2b0eb0c2..32a622d4aa654 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php @@ -18,6 +18,9 @@ class AddressTest extends \Magento\TestFramework\TestCase\AbstractController /** @var FormKey */ private $formKey; + /** + * {@inheritDoc} + */ protected function setUp() { parent::setUp(); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index 02181ce6e2989..6448816c9345c 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -44,6 +44,9 @@ class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendControlle /** @var \Magento\TestFramework\ObjectManager */ protected $objectManager; + /** + * {@inheritDoc} + */ protected function setUp() { parent::setUp(); @@ -67,6 +70,9 @@ protected function setUp() ); } + /** + * {@inheritDoc} + */ protected function tearDown() { /** @@ -522,7 +528,10 @@ public function testEditAction() $this->assertContains('<h1 class="page-title">test firstname test lastname</h1>', $body); } - public function testNewAction() + /** + * Tests new action + */ + public function testNewAction(): void { $this->dispatch('backend/customer/index/edit'); $body = $this->getResponse()->getBody(); From 1786cad03be79d772a08ecd022c0e9ffcef5392f Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Wed, 8 Aug 2018 16:55:12 +0300 Subject: [PATCH 0844/1171] MAGETWO-94029: Issues with Multi website YouTube Videos fix tests --- .../Catalog/Product/Gallery/CreateHandler.php | 9 +- .../Product/Gallery/CreateHandlerTest.php | 223 ++++++++++++------ 2 files changed, 159 insertions(+), 73 deletions(-) diff --git a/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php b/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php index db67b20c98cc2..ce1493b349a85 100644 --- a/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php @@ -60,7 +60,6 @@ public function afterExecute( if (!empty($mediaCollection)) { $newVideoCollection = $this->collectNewVideos($mediaCollection); $this->saveVideoData($newVideoCollection, 0); - $this->saveAdditionalStoreData($newVideoCollection); $videoDataCollection = $this->collectVideoData($mediaCollection); $this->saveVideoData($videoDataCollection, $product->getStoreId()); @@ -263,10 +262,10 @@ private function collectNewVideos(array $mediaCollection): array } /** - * @param $item + * @param array $item * @return bool */ - private function isVideoItem($item): bool + private function isVideoItem(array $item): bool { return !empty($item['media_type']) && empty($item['removed']) @@ -274,10 +273,10 @@ private function isVideoItem($item): bool } /** - * @param $item + * @param array $item * @return bool */ - private function isNewVideo($item): bool + private function isNewVideo(array $item): bool { return !isset($item['video_url_default'], $item['video_title_default']) || empty($item['video_url_default']) diff --git a/app/code/Magento/ProductVideo/Test/Unit/Model/Plugin/Catalog/Product/Gallery/CreateHandlerTest.php b/app/code/Magento/ProductVideo/Test/Unit/Model/Plugin/Catalog/Product/Gallery/CreateHandlerTest.php index 5770ea8b5689d..57ad71997dad7 100644 --- a/app/code/Magento/ProductVideo/Test/Unit/Model/Plugin/Catalog/Product/Gallery/CreateHandlerTest.php +++ b/app/code/Magento/ProductVideo/Test/Unit/Model/Plugin/Catalog/Product/Gallery/CreateHandlerTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\ProductVideo\Test\Unit\Model\Plugin\Catalog\Product\Gallery; /** @@ -37,6 +38,9 @@ class CreateHandlerTest extends \PHPUnit\Framework\TestCase */ protected $mediaGalleryCreateHandler; + /** + * {@inheritDoc} + */ protected function setUp() { $this->product = $this->createMock(\Magento\Catalog\Model\Product::class); @@ -62,72 +66,18 @@ protected function setUp() ); } - public function testAfterExecute() + /** + * @dataProvider provideImageForAfterExecute + * @param array $image + * @param array $expectedSave + * @param int $rowSaved + */ + public function testAfterExecute($image, $expectedSave, $rowSaved): void { - $mediaData = [ - 'images' => [ - '72mljfhmasfilp9cuq' => [ - 'position' => '3', - 'media_type' => 'external-video', - 'file' => '/i/n/index111111.jpg', - 'value_id' => '4', - 'label' => '', - 'disabled' => '0', - 'removed' => '', - 'video_provider' => 'youtube', - 'video_url' => 'https://www.youtube.com/watch?v=ab123456', - 'video_title' => 'Some second title', - 'video_description' => 'Description second', - 'video_metadata' => 'meta two', - 'role' => '', - ], - 'w596fi79hv1p6wj21u' => [ - 'position' => '4', - 'media_type' => 'image', - 'video_provider' => '', - 'file' => '/h/d/hd_image.jpg', - 'value_id' => '7', - 'label' => '', - 'disabled' => '0', - 'removed' => '', - 'video_url' => '', - 'video_title' => '', - 'video_description' => '', - 'video_metadata' => '', - 'role' => '', - ], - 'tcodwd7e0dirifr64j' => [ - 'position' => '4', - 'media_type' => 'external-video', - 'file' => '/s/a/sample_3.jpg', - 'value_id' => '5', - 'label' => '', - 'disabled' => '0', - 'removed' => '', - 'video_provider' => 'youtube', - 'video_url' => 'https://www.youtube.com/watch?v=ab123456', - 'video_title' => 'Some second title', - 'video_description' => 'Description second', - 'video_metadata' => 'meta two', - 'role' => '', - 'additional_store_data' => [ - 0 => [ - 'store_id' => '0', - 'video_provider' => null, - 'video_url' => 'https://www.youtube.com/watch?v=ab123456', - 'video_title' => 'New Title', - 'video_description' => 'New Description', - 'video_metadata' => 'New metadata', - ], - ] - ], - ], - ]; - $this->product->expects($this->once()) ->method('getData') ->with('media_gallery') - ->willReturn($mediaData); + ->willReturn(['images' => $image]); $this->product->expects($this->once()) ->method('getStoreId') ->willReturn(0); @@ -136,13 +86,150 @@ public function testAfterExecute() ->method('getAttribute') ->willReturn($this->attribute); - $this->subject->afterExecute( - $this->mediaGalleryCreateHandler, - $this->product - ); + $this->resourceModel->expects($this->exactly($rowSaved)) + ->method('saveDataRow') + ->with('catalog_product_entity_media_gallery_value_video', $expectedSave) + ->willReturn(1); + + $this->subject->afterExecute($this->mediaGalleryCreateHandler, $this->product); + } + + /** + * DataProvider for testAfterExecute + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return array + */ + public function provideImageForAfterExecute(): array + { + return [ + 'new_video' => [ + [ + '72mljfhmasfilp9cuq' => [ + 'position' => '3', + 'media_type' => 'external-video', + 'file' => '/i/n/index111111.jpg', + 'value_id' => '4', + 'label' => '', + 'disabled' => '0', + 'removed' => '', + 'video_provider' => 'youtube', + 'video_url' => 'https://www.youtube.com/watch?v=ab123456', + 'video_title' => 'Some second title', + 'video_description' => 'Description second', + 'video_metadata' => 'meta two', + 'role' => '', + ], + ], + [ + 'value_id' => '4', + 'store_id' => 0, + 'provider' => 'youtube', + 'url' => 'https://www.youtube.com/watch?v=ab123456', + 'title' => 'Some second title', + 'description' => 'Description second', + 'metadata' => 'meta two', + ], + 2 + ], + 'image' => [ + [ + 'w596fi79hv1p6wj21u' => [ + 'position' => '4', + 'media_type' => 'image', + 'video_provider' => '', + 'file' => '/h/d/hd_image.jpg', + 'value_id' => '7', + 'label' => '', + 'disabled' => '0', + 'removed' => '', + 'video_url' => '', + 'video_title' => '', + 'video_description' => '', + 'video_metadata' => '', + 'role' => '', + ], + ], + [], + 0 + ], + 'new_video_with_additional_data' => [ + [ + 'tcodwd7e0dirifr64j' => [ + 'position' => '4', + 'media_type' => 'external-video', + 'file' => '/s/a/sample_3.jpg', + 'value_id' => '5', + 'label' => '', + 'disabled' => '0', + 'removed' => '', + 'video_provider' => 'youtube', + 'video_url' => 'https://www.youtube.com/watch?v=ab123456', + 'video_title' => 'Some second title', + 'video_description' => 'Description second', + 'video_metadata' => 'meta two', + 'role' => '', + 'additional_store_data' => [ + 0 => [ + 'store_id' => 0, + 'video_provider' => 'youtube', + 'video_url' => 'https://www.youtube.com/watch?v=ab123456', + 'video_title' => 'Some second title', + 'video_description' => 'Description second', + 'video_metadata' => 'meta two', + ], + ] + ], + ], + [ + 'value_id' => '5', + 'store_id' => 0, + 'provider' => 'youtube', + 'url' => 'https://www.youtube.com/watch?v=ab123456', + 'title' => 'Some second title', + 'description' => 'Description second', + 'metadata' => 'meta two', + ], + 3 + ], + 'not_new_video' => [ + [ + '72mljfhmasfilp9cuq' => [ + 'position' => '3', + 'media_type' => 'external-video', + 'file' => '/i/n/index111111.jpg', + 'value_id' => '4', + 'label' => '', + 'disabled' => '0', + 'removed' => '', + 'video_provider' => 'youtube', + 'video_url' => 'https://www.youtube.com/watch?v=ab123456', + 'video_url_default' => 'https://www.youtube.com/watch?v=ab123456', + 'video_title' => 'Some second title', + 'video_title_default' => 'Some second title', + 'video_description' => 'Description second', + 'video_metadata' => 'meta two', + 'role' => '', + ], + ], + [ + 'value_id' => '4', + 'store_id' => 0, + 'provider' => 'youtube', + 'url' => 'https://www.youtube.com/watch?v=ab123456', + 'title' => 'Some second title', + 'description' => 'Description second', + 'metadata' => 'meta two', + ], + 1 + ], + ]; } - public function testAfterExecuteEmpty() + /** + * Tests empty media gallery + */ + public function testAfterExecuteEmpty(): void { $this->product->expects($this->once()) ->method('getData') @@ -162,7 +249,7 @@ public function testAfterExecuteEmpty() /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testBeforeExecute() + public function testBeforeExecute(): void { $mediaData = [ 'images' => [ From 5891c8c6d99a8b883e3d0d40c7d7bc686185b790 Mon Sep 17 00:00:00 2001 From: NazarKlovanych <nazar.klovanich@transoftgroup.com> Date: Thu, 9 Aug 2018 10:39:41 +0300 Subject: [PATCH 0845/1171] Fix failed CacheTEst & PageCacheTest --- .../Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php | 4 ++-- .../Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php index abc2a6fdc5ae2..d2ff7b2f35523 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php @@ -39,7 +39,7 @@ protected function setUp() public function testGetOptions() { $options = $this->configOptionsList->getOptions(); - $this->assertCount(4, $options); + $this->assertCount(5, $options); $this->assertArrayHasKey(0, $options); $this->assertInstanceOf(SelectConfigOption::class, $options[0]); @@ -124,7 +124,7 @@ public function testValidateWithValidInput() ]; $this->validatorMock->expects($this->once()) ->method('isValidConnection') - ->with(['host'=>'localhost', 'db'=>'', 'port'=>'']) + ->with(['host'=>'localhost', 'db'=>'', 'port'=>'', 'password'=>'']) ->willReturn(true); $errors = $this->configOptionsList->validate($options, $this->deploymentConfigMock); diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php index b9cd137530aa9..ed0e567820ad1 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php @@ -39,7 +39,7 @@ protected function setUp() public function testGetOptions() { $options = $this->configList->getOptions(); - $this->assertCount(5, $options); + $this->assertCount(6, $options); $this->assertArrayHasKey(0, $options); $this->assertInstanceOf(SelectConfigOption::class, $options[0]); From b81548352d25d8f0ac6950a7b362374dbeb013e7 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Thu, 9 Aug 2018 11:58:58 +0300 Subject: [PATCH 0846/1171] Reverted breaking changes --- app/code/Magento/Customer/Block/Account/Navigation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Block/Account/Navigation.php b/app/code/Magento/Customer/Block/Account/Navigation.php index f963c8074c8a3..bc51c46aee206 100644 --- a/app/code/Magento/Customer/Block/Account/Navigation.php +++ b/app/code/Magento/Customer/Block/Account/Navigation.php @@ -22,7 +22,7 @@ class Navigation extends Links * {@inheritdoc} * @since 100.2.0 */ - public function getLinks(): array + public function getLinks() { $links = $this->_layout->getChildBlocks($this->getNameInLayout()); $sortableLink = []; From 0dfcda77467ed6e557ec82f7e6fe8cee89e23980 Mon Sep 17 00:00:00 2001 From: Deefco Research Laboratory <wietze.mink@gmail.com> Date: Thu, 9 Aug 2018 12:14:20 +0200 Subject: [PATCH 0847/1171] Fix typos and grammar errors in actions.js --- .../view/base/web/js/grid/columns/actions.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js index a4c56958911a8..f25106662a357 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js @@ -78,8 +78,8 @@ define([ }, /** - * Adds new action. If action with a specfied identifier - * already exists, than the original will be overrided. + * Adds new action. If an action with the specified identifier + * already exists, then the original will be overriden. * * @param {String} index - Actions' identifier. * @param {Object} action - Actions' data. @@ -108,7 +108,7 @@ define([ /** * Processes actions, setting additional information to them and - * evaluating ther properties as a string templates. + * evaluating their properties as string templates. * * @private * @param {Object} row - Row object. @@ -204,11 +204,11 @@ define([ }, /** - * Creates action callback based on its' data. If action doesn't spicify + * Creates action callback based on it's data. If the action doesn't specify * a callback function than the default one will be used. * * @private - * @param {Object} action - Actions' object. + * @param {Object} action - Action's object. * @returns {Function} Callback function. */ _getCallback: function (action) { @@ -234,7 +234,7 @@ define([ * Creates action callback for multiple actions. * * @private - * @param {Object} action - Actions' object. + * @param {Object} action - Action's object. * @returns {Function} Callback function. */ _getCallbacks: function (action) { @@ -259,12 +259,12 @@ define([ /** * Default action callback. Redirects to - * the specified in actions' data url. + * the specified in action's data url. * - * @param {String} actionIndex - Actions' identifier. - * @param {(Number|String)} recordId - Id of the record accociated - * with a specfied action. - * @param {Object} action - Actions' data. + * @param {String} actionIndex - Action's identifier. + * @param {(Number|String)} recordId - Id of the record associated + * with a specified action. + * @param {Object} action - Action's data. */ defaultCallback: function (actionIndex, recordId, action) { window.location.href = action.href; @@ -273,7 +273,7 @@ define([ /** * Shows actions' confirmation window. * - * @param {Object} action - Actions' data. + * @param {Object} action - Action's data. * @param {Function} callback - Callback that will be * invoked if action is confirmed. */ From 3026e1ac28d90351a66f8d75f41a17d2eb9de19d Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Thu, 9 Aug 2018 13:31:22 +0300 Subject: [PATCH 0848/1171] MAGETWO-94086: [Forwardport] Run Catalog Search reindex in multithread mode with AdvancedSearch --- .../Observer/ProductProcessUrlRewriteSavingObserverTest.php | 1 + .../testsuite/Magento/Quote/Model/QuoteRepositoryTest.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php index 34f873df71abb..d1c4a561e4bd9 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -11,6 +11,7 @@ /** * @magentoAppArea adminhtml + * @magentoDbIsolation disabled */ class ProductProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php index 3e093876349d0..3cc1f9880f204 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php @@ -20,6 +20,7 @@ /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @magentoDbIsolation disabled */ class QuoteRepositoryTest extends \PHPUnit\Framework\TestCase { @@ -104,7 +105,6 @@ public function testGetListDoubleCall() } /** - * @magentoDbIsolation enabled * @magentoAppIsolation enabled */ public function testSaveWithNotExistingCustomerAddress() From 053e74629c5e2f214c54fc6b8a8c780c819395a3 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Thu, 9 Aug 2018 13:47:55 +0300 Subject: [PATCH 0849/1171] MAGETWO-94086: [Forwardport] Run Catalog Search reindex in multithread mode with AdvancedSearch --- .../product_rewrite_multistore_rollback.php | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_rewrite_multistore_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_rewrite_multistore_rollback.php index 1ac9fbf63b118..bcf399cb5e552 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_rewrite_multistore_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_rewrite_multistore_rollback.php @@ -4,9 +4,30 @@ * See COPYING.txt for license details. */ -use Magento\TestFramework\Helper\Bootstrap; +declare(strict_types=1); -$objectManager = Bootstrap::getObjectManager(); +use Magento\Framework\Exception\NoSuchEntityException; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('product1', true); + if ($product->getId()) { + $productRepository->delete($product); + } +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); require __DIR__ . '/../../Store/_files/store_rollback.php'; require __DIR__ . '/../../Store/_files/second_store_rollback.php'; From 5d93f82af230f3d2ca7bdd59432b26148898ce56 Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@magento.com> Date: Thu, 9 Aug 2018 13:49:41 +0300 Subject: [PATCH 0850/1171] MAGETWO-93753: [Forwardport] Implement sharding and parallelization for Price Indexer - part 2 --- .../Model/ResourceModel/Index.php | 2 +- .../Model/ResourceModel/Indexer/Price.php | 5 ++-- .../Indexer/Product/Price/ModeSwitcher.php | 4 ++-- .../Price/ModeSwitcherConfiguration.php | 4 ++-- .../Model/ResourceModel/Indexer/Price.php | 3 ++- .../Product/Indexer/Price/Grouped.php | 3 ++- .../IndexerSetDimensionsModeCommand.php | 9 ++++--- .../IndexerShowDimensionsModeCommand.php | 10 +++++--- .../Magento/Indexer/Model/DimensionMode.php | 2 +- .../IndexerSetDimensionsModeCommandTest.php | 8 +++---- .../IndexerShowDimensionsModeCommandTest.php | 4 ++-- .../Annotation/IndexerDimensionMode.php | 3 +++ .../TestFramework/Bootstrap/DocBlock.php | 9 ++++--- .../Catalog/Product/View/Type/BundleTest.php | 3 +++ ...BundlePriceCalculatorWithDimensionTest.php | 4 ++-- .../Bundle/Model/Product/OptionListTest.php | 3 +++ .../Model/Product/PriceWithDimensionTest.php | 6 +++++ ...eWithOptionsTierPriceWithDimensionTest.php | 3 +++ .../Product/Type/PriceWithDimensionTest.php | 24 +++++++++++++++++++ .../Model/ProductPriceWithDimensionTest.php | 12 ++++++++++ .../Pricing/Render/FinalPriceBoxTest.php | 3 +++ .../Quote/Item/QuantityValidatorTest.php | 5 +++- .../Magento/Checkout/Controller/CartTest.php | 2 +- .../SpecialPriceIndexerWithDimensionTest.php | 3 +++ ...edOnIsProductListFlagWithDimensionTest.php | 3 +++ .../Model/Order/Address/RendererTest.php | 3 +++ ...iceIndexerDimensionsModeSetCommandTest.php | 8 +++++-- 27 files changed, 117 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/AdvancedSearch/Model/ResourceModel/Index.php b/app/code/Magento/AdvancedSearch/Model/ResourceModel/Index.php index 1835ef32f5677..b20872da2f8e7 100644 --- a/app/code/Magento/AdvancedSearch/Model/ResourceModel/Index.php +++ b/app/code/Magento/AdvancedSearch/Model/ResourceModel/Index.php @@ -58,7 +58,7 @@ class Index extends AbstractDb * @param Context $context * @param StoreManagerInterface $storeManager * @param MetadataPool $metadataPool - * @param null $connectionName + * @param string|null $connectionName * @param TableResolver|null $tableResolver * @param DimensionCollectionFactory|null $dimensionCollectionFactory */ diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php index dd01b8ae5351b..b5dfd312cd0c4 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php @@ -129,7 +129,8 @@ public function __construct( /** * {@inheritdoc} - * + * @param array $dimensions + * @param \Traversable $entityIds * @throws \Exception */ public function executeByDimensions(array $dimensions, \Traversable $entityIds) @@ -238,8 +239,8 @@ private function prepareBundleOptionTable() /** * Prepare temporary price index data for bundle products by price type * - * @param array $dimensions * @param int $priceType + * @param array $dimensions * @param int|array $entityIds the entity ids limitation * @return void * @throws \Exception diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php index e71031489fa0e..c418f2e1f253b 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcher.php @@ -48,10 +48,10 @@ class ModeSwitcher implements \Magento\Indexer\Model\ModeSwitcherInterface private $modeSwitcherConfiguration; /** - * @param TableMaintainer $tableMaintainer + * @param TableMaintainer $tableMaintainer * @param DimensionCollectionFactory $dimensionCollectionFactory * @param DimensionModeConfiguration $dimensionModeConfiguration - * @param ModeSwitcherConfiguration $modeSwitcherConfiguration + * @param ModeSwitcherConfiguration $modeSwitcherConfiguration */ public function __construct( TableMaintainer $tableMaintainer, diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcherConfiguration.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcherConfiguration.php index 66b7147a8db76..ae00ec51f2960 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcherConfiguration.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/ModeSwitcherConfiguration.php @@ -38,9 +38,9 @@ class ModeSwitcherConfiguration private $indexer; /** - * @param ConfigInterface $configWriter + * @param ConfigInterface $configWriter * @param TypeListInterface $cacheTypeList - * @param Indexer $indexer + * @param Indexer $indexer */ public function __construct( ConfigInterface $configWriter, diff --git a/app/code/Magento/Downloadable/Model/ResourceModel/Indexer/Price.php b/app/code/Magento/Downloadable/Model/ResourceModel/Indexer/Price.php index 732f1e70bcb3f..90b458ff6348e 100644 --- a/app/code/Magento/Downloadable/Model/ResourceModel/Indexer/Price.php +++ b/app/code/Magento/Downloadable/Model/ResourceModel/Indexer/Price.php @@ -101,7 +101,8 @@ public function __construct( /** * {@inheritdoc} - * + * @param array $dimensions + * @param \Traversable $entityIds * @throws \Exception */ public function executeByDimensions(array $dimensions, \Traversable $entityIds) diff --git a/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Indexer/Price/Grouped.php b/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Indexer/Price/Grouped.php index 2861c574532ff..e1599dc772c2c 100644 --- a/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Indexer/Price/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Indexer/Price/Grouped.php @@ -86,7 +86,8 @@ public function __construct( /** * {@inheritdoc} - * + * @param array $dimensions + * @param \Traversable $entityIds * @throws \Exception */ public function executeByDimensions(array $dimensions, \Traversable $entityIds) diff --git a/app/code/Magento/Indexer/Console/Command/IndexerSetDimensionsModeCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerSetDimensionsModeCommand.php index 56dcfbc061ea8..51d67e2116a06 100644 --- a/app/code/Magento/Indexer/Console/Command/IndexerSetDimensionsModeCommand.php +++ b/app/code/Magento/Indexer/Console/Command/IndexerSetDimensionsModeCommand.php @@ -43,8 +43,8 @@ class IndexerSetDimensionsModeCommand extends AbstractIndexerCommand private $dimensionProviders; /** - * @param ObjectManagerFactory $objectManagerFactory - * @param ScopeConfigInterface $configReader + * @param ObjectManagerFactory $objectManagerFactory + * @param ScopeConfigInterface $configReader * @param ModeSwitcherInterface[] $dimensionSwitchers */ public function __construct( @@ -70,6 +70,9 @@ protected function configure() /** * {@inheritdoc} + * @param InputInterface $input + * @param OutputInterface $output + * @return int */ protected function execute(InputInterface $input, OutputInterface $output) { @@ -185,7 +188,7 @@ private function validate(InputInterface $input): array * * @param string $inputKey * @param string $inputIndexer - * @param array $acceptedValues + * @param array $acceptedValues * @return string[] */ private function validateArgument(string $inputKey, string $inputIndexer, array $acceptedValues): array diff --git a/app/code/Magento/Indexer/Console/Command/IndexerShowDimensionsModeCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerShowDimensionsModeCommand.php index f5553c3fb3546..0c1477d6146aa 100644 --- a/app/code/Magento/Indexer/Console/Command/IndexerShowDimensionsModeCommand.php +++ b/app/code/Magento/Indexer/Console/Command/IndexerShowDimensionsModeCommand.php @@ -40,7 +40,7 @@ class IndexerShowDimensionsModeCommand extends AbstractIndexerCommand /** * @param ObjectManagerFactory $objectManagerFactory * @param ScopeConfigInterface $configReader - * @param array $indexers + * @param array $indexers */ public function __construct( ObjectManagerFactory $objectManagerFactory, @@ -63,8 +63,12 @@ protected function configure() parent::configure(); } + /** * {@inheritdoc} + * @param InputInterface $input + * @param OutputInterface $output + * @return int */ protected function execute(InputInterface $input, OutputInterface $output) { @@ -134,8 +138,8 @@ private function validate(InputInterface $input): array * Validate command argument and return errors in case if argument is invalid * * @param string $inputKey - * @param array $inputIndexer - * @param array $acceptedValues + * @param array $inputIndexer + * @param array $acceptedValues * @return array */ private function validateArgument(string $inputKey, array $inputIndexer, array $acceptedValues): array diff --git a/app/code/Magento/Indexer/Model/DimensionMode.php b/app/code/Magento/Indexer/Model/DimensionMode.php index 74fb85e5420ff..5e67f70663bac 100644 --- a/app/code/Magento/Indexer/Model/DimensionMode.php +++ b/app/code/Magento/Indexer/Model/DimensionMode.php @@ -24,7 +24,7 @@ class DimensionMode /** * @param string $name - * @param array $dimensions + * @param array $dimensions */ public function __construct(string $name, array $dimensions) { diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerSetDimensionsModeCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerSetDimensionsModeCommandTest.php index a5521325e589c..3d913ee2860d2 100644 --- a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerSetDimensionsModeCommandTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerSetDimensionsModeCommandTest.php @@ -92,10 +92,10 @@ protected function getObjectManagerReturnValueMap() /** * Tests method \Magento\Indexer\Console\Command\IndexerDimensionsModeCommand::execute * - * @param $indexerTitle - * @param $previousMode - * @param $command - * @param $consoleOutput + * @param string $indexerTitle + * @param string $previousMode + * @param string $command + * @param string $consoleOutput * @dataProvider dimensionModesDataProvider * @return void */ diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowDimensionsModeCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowDimensionsModeCommandTest.php index ad1cf9b5738ba..53a84b96b9713 100644 --- a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowDimensionsModeCommandTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowDimensionsModeCommandTest.php @@ -74,8 +74,8 @@ protected function getObjectManagerReturnValueMap(): array /** * Tests method \Magento\Indexer\Console\Command\IndexerDimensionsModeCommand::execute * - * @param $command - * @param $consoleOutput + * @param string $command + * @param string $consoleOutput * @dataProvider dimensionModesDataProvider */ public function testExecuteWithAttributes($command, $consoleOutput) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php index 179babdb7e184..9f8518239cce5 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php @@ -41,6 +41,9 @@ class IndexerDimensionMode /** @var bool */ private $isDimensionMode = false; + /** + * Restore db + */ private function restoreDb() { $this->db = Bootstrap::getInstance()->getBootstrap()->getApplication()->getDbInstance(); diff --git a/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php b/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php index b92fd6da077d2..9a6b808ef7d61 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php @@ -5,6 +5,8 @@ */ namespace Magento\TestFramework\Bootstrap; +use Magento\TestFramework\Application; + /** * Bootstrap of the custom DocBlock annotations * @@ -27,8 +29,9 @@ public function __construct($fixturesBaseDir) /** * Activate custom DocBlock annotations along with more-or-less permanent workarounds + * @param Application $application */ - public function registerAnnotations(\Magento\TestFramework\Application $application) + public function registerAnnotations(Application $application) { $eventManager = new \Magento\TestFramework\EventManager($this->_getSubscribers($application)); \Magento\TestFramework\Event\PhpUnit::setDefaultEventManager($eventManager); @@ -42,10 +45,10 @@ public function registerAnnotations(\Magento\TestFramework\Application $applicat * To allow config fixtures to deal with fixture stores, data fixtures should be processed first. * ConfigFixture applied twice because data fixtures could clean config and clean custom settings * - * @param \Magento\TestFramework\Application $application + * @param Application $application * @return array */ - protected function _getSubscribers(\Magento\TestFramework\Application $application) + protected function _getSubscribers(Application $application) { return [ new \Magento\TestFramework\Workaround\Segfault(), diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php index f9947d562d34d..ce324ed774dc4 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php @@ -74,6 +74,9 @@ public function testGetJsonConfig() $this->assertEquals(5, $selection['prices']['finalPrice']['amount']); } + /** + * Tear Down + */ protected function tearDown() { $this->objectManager->get(\Magento\Framework\Registry::class)->unregister('product'); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php index 9fa6d2cb705b9..b97bd9f822666 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php @@ -370,8 +370,8 @@ private function getProductWithDifferentPrice() /** * Fixed bundle product with required option, custom option and without any discounts - * @param $selectionsPriceType - * @param $customOptionsPriceType + * @param string $selectionsPriceType + * @param string $customOptionsPriceType * @return array */ private function getBundleConfiguration3($selectionsPriceType, $customOptionsPriceType) diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/OptionListTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/OptionListTest.php index d14d5255eacad..47f50dc6d991e 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/OptionListTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/OptionListTest.php @@ -21,6 +21,9 @@ class OptionListTest extends \PHPUnit\Framework\TestCase */ protected $objectManager; + /** + * Set up + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php index 8430a3324892d..bc25c3fa29381 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php @@ -20,6 +20,9 @@ class PriceWithDimensionTest extends \PHPUnit\Framework\TestCase */ protected $_model; + /** + * Set up + */ protected function setUp() { $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( @@ -27,6 +30,9 @@ protected function setUp() ); } + /** + * Get tier price + */ public function testGetTierPrice() { /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php index 8aff52672a337..691ae78de71af 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php @@ -35,6 +35,9 @@ class SimpleWithOptionsTierPriceWithDimensionTest extends \PHPUnit\Framework\Tes */ private $productCollectionFactory; + /** + * set up + */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php index cfdbb237e9c1d..280e83a863325 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php @@ -30,6 +30,9 @@ class PriceWithDimensionTest extends \PHPUnit\Framework\TestCase */ protected $_model; + /** + * Set up + */ protected function setUp() { $this->_model = Bootstrap::getObjectManager()->create( @@ -37,6 +40,9 @@ protected function setUp() ); } + /** + * Get price from indexer + */ public function testGetPriceFromIndexer() { /** @var PriceTableResolver $tableResolver */ @@ -66,11 +72,17 @@ public function testGetPriceFromIndexer() $this->assertEquals('19', $return[0]['max_price']); } + /** + * Get price + */ public function testGetPrice() { $this->assertEquals('test', $this->_model->getPrice(new DataObject(['price' => 'test']))); } + /** + * Get final price + */ public function testGetFinalPrice() { $repository = Bootstrap::getObjectManager()->create( @@ -95,6 +107,9 @@ public function testGetFinalPrice() $this->assertEquals(14.0, $this->_model->getFinalPrice(5, $product)); } + /** + * Get formated price + */ public function testGetFormatedPrice() { $repository = Bootstrap::getObjectManager()->create( @@ -105,12 +120,18 @@ public function testGetFormatedPrice() $this->assertEquals('<span class="price">$10.00</span>', $this->_model->getFormatedPrice($product)); } + /** + * Calculate price + */ public function testCalculatePrice() { $this->assertEquals(10, $this->_model->calculatePrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01')); $this->assertEquals(8, $this->_model->calculatePrice(10, 8, '1970-12-12 23:59:59', '2034-01-01 01:01:01')); } + /** + * Calculate special price + */ public function testCalculateSpecialPrice() { $this->assertEquals( @@ -123,6 +144,9 @@ public function testCalculateSpecialPrice() ); } + /** + * Is tier price fixed + */ public function testIsTierPriceFixed() { $this->assertTrue($this->_model->isTierPriceFixed()); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php index e80f5b1e4db70..c4b22b37d00e9 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php @@ -33,12 +33,18 @@ class ProductPriceWithDimensionTest extends \PHPUnit\Framework\TestCase */ private $productRepository; + /** + * Set up + */ protected function setUp() { $this->_model = Bootstrap::getObjectManager()->create(Product::class); $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); } + /** + * Get price + */ public function testGetPrice() { $this->assertEmpty($this->_model->getPrice()); @@ -46,6 +52,9 @@ public function testGetPrice() $this->assertEquals(10.0, $this->_model->getPrice()); } + /** + * Get price model + */ public function testGetPriceModel() { $default = $this->_model->getPriceModel(); @@ -77,6 +86,9 @@ public function testGetFormatedPrice() $this->assertEquals('<span class="price">$0.00</span>', $this->_model->getFormatedPrice()); } + /** + * Set get final price + */ public function testSetGetFinalPrice() { $this->assertEquals(0, $this->_model->getFinalPrice()); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Pricing/Render/FinalPriceBoxTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Pricing/Render/FinalPriceBoxTest.php index 88d3c3a6c9b1a..8e5b38a4f8802 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Pricing/Render/FinalPriceBoxTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Pricing/Render/FinalPriceBoxTest.php @@ -68,6 +68,9 @@ class FinalPriceBoxTest extends \PHPUnit\Framework\TestCase */ private $templateEnginePool; + /** + * Set up + */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Quote/Item/QuantityValidatorTest.php b/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Quote/Item/QuantityValidatorTest.php index 598370f4f4366..a849e412675ea 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Quote/Item/QuantityValidatorTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Quote/Item/QuantityValidatorTest.php @@ -58,6 +58,9 @@ class QuantityValidatorTest extends \PHPUnit\Framework\TestCase */ private $observer; + /** + * Set up + */ protected function setUp() { /** @var \Magento\Framework\ObjectManagerInterface objectManager */ @@ -159,7 +162,7 @@ private function setMockStockStateResultToQuoteItemOptions($quoteItem, $resultMo * Gets \Magento\Quote\Model\Quote\Item from \Magento\Quote\Model\Quote by product id * * @param \Magento\Quote\Model\Quote $quote - * @param $productId + * @param int $productId * @return \Magento\Quote\Model\Quote\Item */ private function _getQuoteItemIdByProductId($quote, $productId) diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php index a7268e3e846c7..52156040b2800 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php @@ -239,7 +239,7 @@ public function testUpdatePostAction() * Gets \Magento\Quote\Model\Quote\Item from \Magento\Quote\Model\Quote by product id * * @param \Magento\Quote\Model\Quote $quote - * @param $productId + * @param int $productId * @return \Magento\Quote\Model\Quote\Item|null */ private function _getQuoteItemIdByProductId($quote, $productId) diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php index f533e751d210a..578cb47d14676 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php @@ -37,6 +37,9 @@ class SpecialPriceIndexerWithDimensionTest extends \PHPUnit\Framework\TestCase */ private $indexerProcessor; + /** + * Set up + */ protected function setUp() { $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php index 249c1cd2f028f..b2e4fe6af3243 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php @@ -44,6 +44,9 @@ class RenderingBasedOnIsProductListFlagWithDimensionTest extends \PHPUnit\Framew */ private $finalPriceBox; + /** + * Set up + */ protected function setUp() { $productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Address/RendererTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Address/RendererTest.php index 1df0864e1874d..d9c38d5697d96 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Address/RendererTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Address/RendererTest.php @@ -36,6 +36,9 @@ class RendererTest extends \PHPUnit\Framework\TestCase */ private $config; + /** + * Set up + */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/PriceIndexerDimensionsModeSetCommandTest.php b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/PriceIndexerDimensionsModeSetCommandTest.php index b1407f4266c13..c25dc65ccd73c 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/PriceIndexerDimensionsModeSetCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/PriceIndexerDimensionsModeSetCommandTest.php @@ -63,8 +63,8 @@ public static function setUpBeforeClass() * @magentoAppArea adminhtml * @magentoAppIsolation enabled * - * @param $previousMode - * @param $currentMode + * @param string $previousMode + * @param string $currentMode * @dataProvider modesDataProvider */ public function testSwitchMode($previousMode, $currentMode) @@ -89,6 +89,10 @@ public function testSwitchMode($previousMode, $currentMode) ); } + /** + * Modes data provider + * @return array + */ public function modesDataProvider() { return [ From 761396d58164ab23a94244aff48cddf0773142b1 Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv <rkostiv@magento.com> Date: Thu, 9 Aug 2018 14:09:22 +0300 Subject: [PATCH 0851/1171] MAGETWO-94060: [2.3.x] Unlink CatalogWidget from EAV indexer --- .../Model/Rule/Condition/Product.php | 8 ++++++++ .../Unit/Model/Rule/Condition/ProductTest.php | 15 +++++++++++++++ .../Magento/Rule/Model/Condition/Sql/Builder.php | 4 ++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php index 70e2dc9dc4f36..27d027ce6ca43 100644 --- a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php +++ b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php @@ -101,6 +101,9 @@ public function loadAttributeOptions() /** * {@inheritdoc} + * + * @param array &$attributes + * @return void */ protected function _addSpecialAttributes(array &$attributes) { @@ -228,6 +231,8 @@ protected function addNotGlobalAttribute( /** * {@inheritdoc} + * + * @return string */ public function getMappedSqlField() { @@ -247,6 +252,9 @@ public function getMappedSqlField() /** * {@inheritdoc} + * + * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection + * @return $this */ public function collectValidatedAttributes($productCollection) { diff --git a/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php b/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php index d255a9940ad9f..219cae6829299 100644 --- a/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php +++ b/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php @@ -27,6 +27,11 @@ class ProductTest extends \PHPUnit\Framework\TestCase */ private $attributeMock; + /** + * @inheritdoc + * + * @return void + */ protected function setUp() { $objectManagerHelper = new ObjectManager($this); @@ -60,6 +65,11 @@ protected function setUp() ); } + /** + * Test addToCollection method. + * + * @return void + */ public function testAddToCollection() { $collectionMock = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product\Collection::class); @@ -83,6 +93,11 @@ public function testAddToCollection() $this->model->addToCollection($collectionMock); } + /** + * Test getMappedSqlField method. + * + * @return void + */ public function testGetMappedSqlFieldSku() { $this->model->setAttribute('sku'); diff --git a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php index c32469c0f4691..2894de0f19b87 100644 --- a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php +++ b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php @@ -184,8 +184,8 @@ protected function _getMappedSqlCondition( /** * @param Combine $combine - * @param string $value - * @param bool $isDefaultStoreUsed + * @param string $value + * @param bool $isDefaultStoreUsed * @return string * @SuppressWarnings(PHPMD.NPathComplexity) * @throws \Magento\Framework\Exception\LocalizedException From 0c5065989cfd9160847d5909e78c4a344212b771 Mon Sep 17 00:00:00 2001 From: nikita <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 9 Aug 2018 14:44:31 +0300 Subject: [PATCH 0852/1171] MAGETWO-73774: [2.3] Custom option price field disallows negative values #7333 --- .../Option/Validator/DefaultValidator.php | 2 +- .../Model/Product/Option/Validator/Select.php | 2 +- .../Option/Validator/DefaultValidatorTest.php | 52 +++---------------- .../Product/Option/Validator/FileTest.php | 18 ++++--- .../Product/Option/Validator/SelectTest.php | 3 +- .../Product/Option/Validator/TextTest.php | 12 +++-- .../Product/Form/Modifier/CustomOptions.php | 2 +- .../Catalog/Api/_files/product_options.php | 2 +- .../Api/_files/product_options_negative.php | 11 ---- .../Catalog/Model/ProductPriceTest.php | 2 +- .../Magento/Catalog/Model/ProductTest.php | 6 ++- .../product_simple_with_custom_options.php | 2 +- .../Catalog/_files/product_with_options.php | 2 +- 13 files changed, 40 insertions(+), 76 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php index 1e5c7f76d829b..d1fe4c570dc75 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php @@ -132,7 +132,7 @@ protected function validateOptionType(Option $option) */ protected function validateOptionValue(Option $option) { - return $this->isInRange($option->getPriceType(), $this->priceTypes) && !$this->isNegative($option->getPrice()); + return $this->isInRange($option->getPriceType(), $this->priceTypes); } /** diff --git a/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php b/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php index f04ab497e1d4f..44756890b6ed7 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php @@ -83,7 +83,7 @@ protected function isValidOptionPrice($priceType, $price, $storeId) if (!$priceType && !$price) { return true; } - if (!$this->isInRange($priceType, $this->priceTypes) || $this->isNegative($price)) { + if (!$this->isInRange($priceType, $this->priceTypes)) { return false; } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php index 1eb5f1a2dacd2..5a8dba5c8c2b2 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php @@ -60,10 +60,10 @@ public function isValidTitleDataProvider() { $mess = ['option required fields' => 'Missed values for option required fields']; return [ - ['option_title', 'name 1.1', 'fixed', 10, new \Magento\Framework\DataObject(['store_id' => 1]), [], true], - ['option_title', 'name 1.1', 'fixed', 10, new \Magento\Framework\DataObject(['store_id' => 0]), [], true], - [null, 'name 1.1', 'fixed', 10, new \Magento\Framework\DataObject(['store_id' => 1]), [], true], - [null, 'name 1.1', 'fixed', 10, new \Magento\Framework\DataObject(['store_id' => 0]), $mess, false], + ['option_title', 'name 1.1', 'fixed', new \Magento\Framework\DataObject(['store_id' => 1]), [], true], + ['option_title', 'name 1.1', 'fixed', new \Magento\Framework\DataObject(['store_id' => 0]), [], true], + [null, 'name 1.1', 'fixed', new \Magento\Framework\DataObject(['store_id' => 1]), [], true], + [null, 'name 1.1', 'fixed', new \Magento\Framework\DataObject(['store_id' => 0]), $mess, false], ]; } @@ -71,20 +71,19 @@ public function isValidTitleDataProvider() * @param $title * @param $type * @param $priceType - * @param $price * @param $product * @param $messages * @param $result * @dataProvider isValidTitleDataProvider */ - public function testIsValidTitle($title, $type, $priceType, $price, $product, $messages, $result) + public function testIsValidTitle($title, $type, $priceType, $product, $messages, $result) { - $methods = ['getTitle', 'getType', 'getPriceType', 'getPrice', '__wakeup', 'getProduct']; + $methods = ['getTitle', 'getType', 'getPriceType', '__wakeup', 'getProduct']; $valueMock = $this->createPartialMock(\Magento\Catalog\Model\Product\Option::class, $methods); $valueMock->expects($this->once())->method('getTitle')->will($this->returnValue($title)); $valueMock->expects($this->any())->method('getType')->will($this->returnValue($type)); $valueMock->expects($this->once())->method('getPriceType')->will($this->returnValue($priceType)); - $valueMock->expects($this->once())->method('getPrice')->will($this->returnValue($price)); + // $valueMock->expects($this->once())->method('getPrice')->will($this->returnValue($price)); $valueMock->expects($this->once())->method('getProduct')->will($this->returnValue($product)); $this->assertEquals($result, $this->validator->isValid($valueMock)); $this->assertEquals($messages, $this->validator->getMessages()); @@ -124,41 +123,4 @@ public function testIsValidFail($product) $this->assertFalse($this->validator->isValid($valueMock)); $this->assertEquals($messages, $this->validator->getMessages()); } - - /** - * Data provider for testValidationNegativePrice - * @return array - */ - public function validationNegativePriceDataProvider() - { - return [ - ['option_title', 'name 1.1', 'fixed', -12, new \Magento\Framework\DataObject(['store_id' => 1])], - ['option_title', 'name 1.1', 'fixed', -12, new \Magento\Framework\DataObject(['store_id' => 0])], - ]; - } - - /** - * @param $title - * @param $type - * @param $priceType - * @param $price - * @param $product - * @dataProvider validationNegativePriceDataProvider - */ - public function testValidationNegativePrice($title, $type, $priceType, $price, $product) - { - $methods = ['getTitle', 'getType', 'getPriceType', 'getPrice', '__wakeup', 'getProduct']; - $valueMock = $this->createPartialMock(\Magento\Catalog\Model\Product\Option::class, $methods); - $valueMock->expects($this->once())->method('getTitle')->will($this->returnValue($title)); - $valueMock->expects($this->exactly(2))->method('getType')->will($this->returnValue($type)); - $valueMock->expects($this->once())->method('getPriceType')->will($this->returnValue($priceType)); - $valueMock->expects($this->once())->method('getPrice')->will($this->returnValue($price)); - $valueMock->expects($this->once())->method('getProduct')->will($this->returnValue($product)); - - $messages = [ - 'option values' => 'Invalid option value', - ]; - $this->assertFalse($this->validator->isValid($valueMock)); - $this->assertEquals($messages, $this->validator->getMessages()); - } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php index 3c06db0e7ce5f..d8b48d0cc984e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php @@ -58,8 +58,10 @@ public function testIsValidSuccess() { $this->valueMock->expects($this->once())->method('getTitle')->will($this->returnValue('option_title')); $this->valueMock->expects($this->exactly(2))->method('getType')->will($this->returnValue('name 1.1')); - $this->valueMock->expects($this->once())->method('getPriceType')->will($this->returnValue('fixed')); - $this->valueMock->expects($this->once())->method('getPrice')->will($this->returnValue(10)); + $this->valueMock->method('getPriceType') + ->willReturn('fixed'); + $this->valueMock->method('getPrice') + ->willReturn(10); $this->valueMock->expects($this->once())->method('getImageSizeX')->will($this->returnValue(10)); $this->valueMock->expects($this->once())->method('getImageSizeY')->will($this->returnValue(15)); $this->assertEmpty($this->validator->getMessages()); @@ -70,8 +72,10 @@ public function testIsValidWithNegativeImageSize() { $this->valueMock->expects($this->once())->method('getTitle')->will($this->returnValue('option_title')); $this->valueMock->expects($this->exactly(2))->method('getType')->will($this->returnValue('name 1.1')); - $this->valueMock->expects($this->once())->method('getPriceType')->will($this->returnValue('fixed')); - $this->valueMock->expects($this->once())->method('getPrice')->will($this->returnValue(10)); + $this->valueMock->method('getPriceType') + ->willReturn('fixed'); + $this->valueMock->method('getPrice') + ->willReturn(10); $this->valueMock->expects($this->once())->method('getImageSizeX')->will($this->returnValue(-10)); $this->valueMock->expects($this->never())->method('getImageSizeY'); $messages = [ @@ -85,8 +89,10 @@ public function testIsValidWithNegativeImageSizeY() { $this->valueMock->expects($this->once())->method('getTitle')->will($this->returnValue('option_title')); $this->valueMock->expects($this->exactly(2))->method('getType')->will($this->returnValue('name 1.1')); - $this->valueMock->expects($this->once())->method('getPriceType')->will($this->returnValue('fixed')); - $this->valueMock->expects($this->once())->method('getPrice')->will($this->returnValue(10)); + $this->valueMock->method('getPriceType') + ->willReturn('fixed'); + $this->valueMock->method('getPrice') + ->willReturn(10); $this->valueMock->expects($this->once())->method('getImageSizeX')->will($this->returnValue(10)); $this->valueMock->expects($this->once())->method('getImageSizeY')->will($this->returnValue(-10)); $messages = [ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php index 95a9b961c8d81..675821fcda111 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php @@ -90,7 +90,7 @@ public function isValidSuccessDataProvider() ] ], [ - false, + true, [ 'title' => 'Some Title', 'price_type' => 'fixed', @@ -163,7 +163,6 @@ public function testIsValidateWithInvalidData($priceType, $price, $title) public function isValidateWithInvalidDataDataProvider() { return [ - 'invalid_price' => ['fixed', -10, 'Title'], 'invalid_price_type' => ['some_value', '10', 'Title'], 'empty_title' => ['fixed', 10, null] ]; diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php index cf31d67817684..ffd858c3d433e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php @@ -58,8 +58,10 @@ public function testIsValidSuccess() { $this->valueMock->expects($this->once())->method('getTitle')->will($this->returnValue('option_title')); $this->valueMock->expects($this->exactly(2))->method('getType')->will($this->returnValue('name 1.1')); - $this->valueMock->expects($this->once())->method('getPriceType')->will($this->returnValue('fixed')); - $this->valueMock->expects($this->once())->method('getPrice')->will($this->returnValue(10)); + $this->valueMock->method('getPriceType') + ->willReturn('fixed'); + $this->valueMock->method('getPrice') + ->willReturn(10); $this->valueMock->expects($this->once())->method('getMaxCharacters')->will($this->returnValue(10)); $this->assertTrue($this->validator->isValid($this->valueMock)); $this->assertEmpty($this->validator->getMessages()); @@ -69,8 +71,10 @@ public function testIsValidWithNegativeMaxCharacters() { $this->valueMock->expects($this->once())->method('getTitle')->will($this->returnValue('option_title')); $this->valueMock->expects($this->exactly(2))->method('getType')->will($this->returnValue('name 1.1')); - $this->valueMock->expects($this->once())->method('getPriceType')->will($this->returnValue('fixed')); - $this->valueMock->expects($this->once())->method('getPrice')->will($this->returnValue(10)); + $this->valueMock->method('getPriceType') + ->willReturn('fixed'); + $this->valueMock->method('getPrice') + ->willReturn(10); $this->valueMock->expects($this->once())->method('getMaxCharacters')->will($this->returnValue(-10)); $messages = [ 'option values' => 'Invalid option value', 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 7196a721f1d02..e557c8a377681 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 @@ -923,7 +923,7 @@ protected function getPriceFieldConfig($sortOrder) 'addbeforePool' => $this->productOptionsPrice->prefixesToOptionArray(), 'sortOrder' => $sortOrder, 'validation' => [ - 'validate-zero-or-greater' => true + 'validate-number' => true ], ], ], diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php index 144f3a9926fe3..8a00de1be094f 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php @@ -10,7 +10,7 @@ 'type' => 'field', 'sort_order' => 1, 'is_require' => 1, - 'price' => 10, + 'price' => -10, 'price_type' => 'fixed', 'sku' => 'sku1', 'max_characters' => 10, diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_negative.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_negative.php index 5d2737b3aa532..50b68a2653ed3 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_negative.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_negative.php @@ -15,17 +15,6 @@ 'sku' => 'sku1', 'max_characters' => 10, ], - 'negative_price' => [ - 'title' => 'area option', - 'type' => 'area', - 'sort_order' => 2, - 'is_require' => 0, - 'price' => -20, - 'price_type' => 'percent', - 'sku' => 'sku2', - 'max_characters' => 20, - - ], 'negative_value_of_image_size' => [ 'title' => 'file option', 'type' => 'file', diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php index f7ae17e06a33b..793275ae5a18d 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php @@ -96,7 +96,7 @@ public function testGetMinPrice(): void $collection->load(); /** @var \Magento\Catalog\Model\Product $product */ $product = $collection->getFirstItem(); - $this->assertEquals(333, $product->getData('min_price')); + $this->assertEquals(323, $product->getData('min_price')); } /** diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php index b00090850e09b..7f1d37689e042 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php @@ -20,6 +20,8 @@ * @magentoDbIsolation enabled * @magentoAppIsolation enabled * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyMethods) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) */ class ProductTest extends \PHPUnit\Framework\TestCase { @@ -534,6 +536,8 @@ public function testValidateUniqueInputAttributeOnTheSameProduct() } /** + * Tests Customizable Options price values including negative value. + * * @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_options.php * @magentoAppIsolation enabled */ @@ -543,7 +547,7 @@ public function testGetOptions() $options = $this->_model->getOptions(); $this->assertNotEmpty($options); $expectedValue = [ - '3-1-select' => 3000.00, + '3-1-select' => -3000.00, '3-2-select' => 5000.00, '4-1-radio' => 600.234, '4-2-radio' => 40000.00 diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options.php index c3abd2dfcdae7..059b784978a22 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options.php @@ -51,7 +51,7 @@ [ 'option_type_id' => null, 'title' => 'Option 1', - 'price' => '3,000.00', + 'price' => '-3,000.00', 'price_type' => 'fixed', 'sku' => '3-1-select', ], diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php index 9b8a629d24cad..a9bc557fd9b72 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_options.php @@ -47,7 +47,7 @@ 'type' => 'field', 'is_require' => true, 'sort_order' => 1, - 'price' => 10.0, + 'price' => -10.0, 'price_type' => 'fixed', 'sku' => 'sku1', 'max_characters' => 10, From bf17136143b1e062d5bbf62256c95f5346be85af Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@magento.com> Date: Thu, 9 Aug 2018 14:49:12 +0300 Subject: [PATCH 0853/1171] MAGETWO-93753: [Forwardport] Implement sharding and parallelization for Price Indexer - part 2 --- .../Console/Command/IndexerShowDimensionsModeCommand.php | 1 - .../Observer/ProductProcessUrlRewriteSavingObserverTest.php | 3 +++ .../testsuite/Magento/Quote/Model/QuoteRepositoryTest.php | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Indexer/Console/Command/IndexerShowDimensionsModeCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerShowDimensionsModeCommand.php index 0c1477d6146aa..44fff0bca6802 100644 --- a/app/code/Magento/Indexer/Console/Command/IndexerShowDimensionsModeCommand.php +++ b/app/code/Magento/Indexer/Console/Command/IndexerShowDimensionsModeCommand.php @@ -63,7 +63,6 @@ protected function configure() parent::configure(); } - /** * {@inheritdoc} * @param InputInterface $input diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php index d1c4a561e4bd9..9de0588356c43 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -18,6 +18,9 @@ class ProductProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\Test /** @var \Magento\Framework\ObjectManagerInterface */ protected $objectManager; + /** + * Set up + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php index 3cc1f9880f204..397591918129c 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php @@ -44,6 +44,9 @@ class QuoteRepositoryTest extends \PHPUnit\Framework\TestCase */ private $filterBuilder; + /** + * Set up + */ protected function setUp() { $this->objectManager = BootstrapHelper::getObjectManager(); From 6a749b29eb48d68473615439067a11379adfe210 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Thu, 9 Aug 2018 14:52:57 +0300 Subject: [PATCH 0854/1171] MAGETWO-94086: [Forwardport] Run Catalog Search reindex in multithread mode with AdvancedSearch --- .../Paypal/Model/Payflow/Service/Request/SecureTokenTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php index 2318edbb80b25..f865c9ededd3d 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php @@ -22,6 +22,7 @@ /** * @magentoAppIsolation enabled + * @magentoDbIsolation disabled */ class SecureTokenTest extends TestCase { From 98caaa4864da9812869008340a2fc8906b3129f7 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Thu, 9 Aug 2018 15:12:03 +0300 Subject: [PATCH 0855/1171] Minor fixes for builds stabilization --- .../Product/Indexer/Price/DefaultPrice.php | 5 +++ .../Test/Unit/Controller/Index/PostTest.php | 9 +++++ .../Customer/Block/Account/Navigation.php | 2 +- .../Customer/Model/AccountManagement.php | 34 +++++++++---------- .../Test/Unit/Model/AccountManagementTest.php | 22 +++++++++--- .../Eav/Api/Data/AttributeInterface.php | 3 +- .../Order/Creditmemo/PrintActionTest.php | 3 ++ .../Search/Block/Adminhtml/Dashboard/Last.php | 2 +- .../Controller/Adminhtml/Synonyms/Edit.php | 6 ++-- .../Search/Model/SynonymGroupRepository.php | 10 +++--- .../AbstractAggregateCalculator.php | 4 +-- .../Controller/Product/CompareTest.php | 33 ++++++++++++++++++ .../Magento/Contact/Controller/IndexTest.php | 10 ++++-- .../App/Filesystem/CreatePdfFileTest.php | 3 ++ .../Newsletter/Controller/ManageTest.php | 6 ++++ .../Tax/Model/Sales/Total/Quote/TaxTest.php | 3 ++ .../Magento/Framework/App/Request/Http.php | 2 +- 17 files changed, 119 insertions(+), 38 deletions(-) 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 7ea85cd3f6f10..ed25c17ff0513 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 @@ -862,6 +862,11 @@ private function getTotalTierPriceExpression(\Zend_Db_Expr $priceExpression) ); } + /** + * @param string $tableAlias + * @param \Zend_Db_Expr $priceExpression + * @return \Zend_Db_Expr + */ private function getTierPriceExpressionForTable($tableAlias, \Zend_Db_Expr $priceExpression) { return $this->getConnection()->getCheckSql( diff --git a/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php b/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php index f01922f42f40c..b78edcb0731ff 100644 --- a/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php +++ b/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php @@ -65,6 +65,9 @@ class PostTest extends \PHPUnit\Framework\TestCase */ private $mailMock; + /** + * test setup + */ protected function setUp() { $this->mailMock = $this->getMockBuilder(MailInterface::class)->getMockForAbstractClass(); @@ -120,6 +123,9 @@ protected function setUp() ); } + /** + * testExecuteEmptyPost + */ public function testExecuteEmptyPost() { $this->stubRequestPostData([]); @@ -159,6 +165,9 @@ public function postDataProvider() ]; } + /** + * testExecuteValidPost + */ public function testExecuteValidPost() { $post = ['name' => 'Name', 'comment' => 'Comment', 'email' => 'valid@mail.com', 'hideit' => null]; diff --git a/app/code/Magento/Customer/Block/Account/Navigation.php b/app/code/Magento/Customer/Block/Account/Navigation.php index bc51c46aee206..705acbcda4c6a 100644 --- a/app/code/Magento/Customer/Block/Account/Navigation.php +++ b/app/code/Magento/Customer/Block/Account/Navigation.php @@ -47,6 +47,6 @@ public function getLinks() */ private function compare(SortLinkInterface $firstLink, SortLinkInterface $secondLink): int { - return $firstLink->getSortOrder() <=> $secondLink->getSortOrder(); + return $secondLink->getSortOrder() <=> $firstLink->getSortOrder(); } } diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 150938223e684..3aba7d1da89fb 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -442,7 +442,7 @@ private function getAuthentication() } /** - * {@inheritdoc} + * @inheritdoc */ public function resendConfirmation($email, $websiteId = null, $redirectUrl = '') { @@ -465,7 +465,7 @@ public function resendConfirmation($email, $websiteId = null, $redirectUrl = '') } /** - * {@inheritdoc} + * @inheritdoc */ public function activate($email, $confirmationKey) { @@ -474,7 +474,7 @@ public function activate($email, $confirmationKey) } /** - * {@inheritdoc} + * @inheritdoc */ public function activateById($customerId, $confirmationKey) { @@ -514,7 +514,7 @@ private function activateCustomer($customer, $confirmationKey) } /** - * {@inheritdoc} + * @inheritdoc */ public function authenticate($username, $password) { @@ -549,7 +549,7 @@ public function authenticate($username, $password) } /** - * {@inheritdoc} + * @inheritdoc */ public function validateResetPasswordLinkToken($customerId, $resetPasswordLinkToken) { @@ -558,7 +558,7 @@ public function validateResetPasswordLinkToken($customerId, $resetPasswordLinkTo } /** - * {@inheritdoc} + * @inheritdoc */ public function initiatePasswordReset($email, $template, $websiteId = null) { @@ -611,7 +611,7 @@ private function handleUnknownTemplate($template) } /** - * {@inheritdoc} + * @inheritdoc */ public function resetPassword($email, $resetToken, $newPassword) { @@ -720,7 +720,7 @@ protected function getMinPasswordLength() } /** - * {@inheritdoc} + * @inheritdoc */ public function getConfirmationStatus($customerId) { @@ -736,7 +736,7 @@ public function getConfirmationStatus($customerId) } /** - * {@inheritdoc} + * @inheritdoc */ public function createAccount(CustomerInterface $customer, $password = null, $redirectUrl = '') { @@ -758,7 +758,7 @@ public function createAccount(CustomerInterface $customer, $password = null, $re } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -841,7 +841,7 @@ public function createAccountWithPasswordHash(CustomerInterface $customer, $hash } /** - * {@inheritdoc} + * @inheritdoc */ public function getDefaultBillingAddress($customerId) { @@ -850,7 +850,7 @@ public function getDefaultBillingAddress($customerId) } /** - * {@inheritdoc} + * @inheritdoc */ public function getDefaultShippingAddress($customerId) { @@ -885,7 +885,7 @@ protected function sendEmailConfirmation(CustomerInterface $customer, $redirectU } /** - * {@inheritdoc} + * @inheritdoc */ public function changePassword($email, $currentPassword, $newPassword) { @@ -898,7 +898,7 @@ public function changePassword($email, $currentPassword, $newPassword) } /** - * {@inheritdoc} + * @inheritdoc */ public function changePasswordById($customerId, $currentPassword, $newPassword) { @@ -966,7 +966,7 @@ private function getEavValidator() } /** - * {@inheritdoc} + * @inheritdoc */ public function validate(CustomerInterface $customer) { @@ -991,7 +991,7 @@ public function validate(CustomerInterface $customer) } /** - * {@inheritdoc} + * @inheritdoc */ public function isEmailAvailable($customerEmail, $websiteId = null) { @@ -1007,7 +1007,7 @@ public function isEmailAvailable($customerEmail, $websiteId = null) } /** - * {@inheritDoc} + * @inheritDoc */ public function isCustomerInStore($customerWebsiteId, $storeId) { diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php index bbac58ac837b4..af36e20e14204 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php @@ -789,6 +789,9 @@ public function testCreateAccountWithPasswordInputException( $this->accountManagement->createAccount($customer, $password); } + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testCreateAccountInputExceptionExtraLongPassword() { $password = '257*chars*************************************************************************************' @@ -1146,11 +1149,11 @@ protected function prepareInitiatePasswordReset($email, $templateIdentifier, $se } /** - * @param $email - * @param $templateIdentifier - * @param $sender - * @param $storeId - * @param $customerName + * @param string $email + * @param int $templateIdentifier + * @param string $sender + * @param int $storeId + * @param string $customerName */ protected function prepareEmailSend($email, $templateIdentifier, $sender, $storeId, $customerName) { @@ -1185,6 +1188,9 @@ protected function prepareEmailSend($email, $templateIdentifier, $sender, $store ->method('sendMessage'); } + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testInitiatePasswordResetEmailReminder() { $customerId = 1; @@ -1208,6 +1214,9 @@ public function testInitiatePasswordResetEmailReminder() $this->assertTrue($this->accountManagement->initiatePasswordReset($email, $template)); } + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testInitiatePasswordResetEmailReset() { $storeId = 1; @@ -1230,6 +1239,9 @@ public function testInitiatePasswordResetEmailReset() $this->assertTrue($this->accountManagement->initiatePasswordReset($email, $template)); } + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testInitiatePasswordResetNoTemplate() { $storeId = 1; diff --git a/app/code/Magento/Eav/Api/Data/AttributeInterface.php b/app/code/Magento/Eav/Api/Data/AttributeInterface.php index 8386ab33ab34f..1b2fdde005356 100644 --- a/app/code/Magento/Eav/Api/Data/AttributeInterface.php +++ b/app/code/Magento/Eav/Api/Data/AttributeInterface.php @@ -11,7 +11,8 @@ * @api * @since 100.0.2 */ -interface AttributeInterface extends \Magento\Framework\Api\CustomAttributesDataInterface, +interface AttributeInterface + extends \Magento\Framework\Api\CustomAttributesDataInterface, \Magento\Framework\Api\MetadataObjectInterface { const ATTRIBUTE_ID = 'attribute_id'; diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/PrintActionTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/PrintActionTest.php index e12a4195db4c6..11ccdd87a5664 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/PrintActionTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/PrintActionTest.php @@ -81,6 +81,9 @@ class PrintActionTest extends \PHPUnit\Framework\TestCase */ protected $resultForwardMock; + /** + * test setup + */ protected function setUp() { $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) diff --git a/app/code/Magento/Search/Block/Adminhtml/Dashboard/Last.php b/app/code/Magento/Search/Block/Adminhtml/Dashboard/Last.php index 01b9dbcd67836..5ead8437f943a 100644 --- a/app/code/Magento/Search/Block/Adminhtml/Dashboard/Last.php +++ b/app/code/Magento/Search/Block/Adminhtml/Dashboard/Last.php @@ -120,7 +120,7 @@ protected function _prepareColumns() } /** - * {@inheritdoc} + * @inheritdoc */ public function getRowUrl($row) { diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Edit.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Edit.php index ff81ac3671903..8eefc956e8aa5 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Edit.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Edit.php @@ -33,10 +33,10 @@ class Edit extends \Magento\Backend\App\Action /** * Edit constructor. * - * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\Framework\Registry $registry + * @param \Magento\Backend\App\Action\Context $context + * @param \Magento\Framework\Registry $registry * @param \Magento\Search\Controller\Adminhtml\Synonyms\ResultPageBuilder $pageBuilder - * @param \Magento\Search\Api\SynonymGroupRepositoryInterface $synGroupRepository + * @param \Magento\Search\Api\SynonymGroupRepositoryInterface $synGroupRepository */ public function __construct( \Magento\Backend\App\Action\Context $context, diff --git a/app/code/Magento/Search/Model/SynonymGroupRepository.php b/app/code/Magento/Search/Model/SynonymGroupRepository.php index 167a39f5ac657..75d7049afd949 100644 --- a/app/code/Magento/Search/Model/SynonymGroupRepository.php +++ b/app/code/Magento/Search/Model/SynonymGroupRepository.php @@ -45,7 +45,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function save(SynonymGroupInterface $synonymGroup, $errorOnMergeConflict = false) { @@ -103,7 +103,7 @@ public function get($synonymGroupId) * Private helper to create a synonym group, throw exception on merge conflict * * @param SynonymGroupInterface $synonymGroup - * @param bool $errorOnMergeConflict + * @param bool $errorOnMergeConflict * @return SynonymGroupInterface * @throws Synonym\MergeConflictException * @throws \Magento\Framework\Exception\AlreadyExistsException @@ -143,7 +143,7 @@ private function create(SynonymGroupInterface $synonymGroup, $errorOnMergeConfli * Perform synonyms merge * * @param SynonymGroupInterface $synonymGroupToMerge - * @param array $matchingGroupIds + * @param array $matchingGroupIds * @return array * @throws \Exception */ @@ -179,9 +179,9 @@ private function populateSynonymGroupModel(SynonymGroup $modelToPopulate, Synony /** * Private helper to update a synonym group, throw exception on merge conflict * - * @param SynonymGroup $oldSynonymGroup + * @param SynonymGroup $oldSynonymGroup * @param SynonymGroupInterface $newSynonymGroup - * @param bool $errorOnMergeConflict + * @param bool $errorOnMergeConflict * @return SynonymGroupInterface * @throws Synonym\MergeConflictException * @throws \Magento\Framework\Exception\AlreadyExistsException diff --git a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php index 77302bba82c67..bad64260cf58a 100644 --- a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php +++ b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php @@ -10,7 +10,7 @@ abstract class AbstractAggregateCalculator extends AbstractCalculator { /** - * {@inheritdoc} + * @inheritdoc */ protected function calculateWithTaxInPrice(QuoteDetailsItemInterface $item, $quantity, $round = true) { @@ -86,7 +86,7 @@ protected function calculateWithTaxInPrice(QuoteDetailsItemInterface $item, $qua } /** - * {@inheritdoc} + * @inheritdoc */ protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $quantity, $round = true) { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php index 9c23b28462e67..9857657fc4897 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php @@ -22,6 +22,9 @@ class CompareTest extends \Magento\TestFramework\TestCase\AbstractController */ protected $productRepository; + /** + * Test setup + */ protected function setUp() { parent::setUp(); @@ -32,6 +35,9 @@ protected function setUp() $this->productRepository = $objectManager->create(\Magento\Catalog\Model\ProductRepository::class); } + /** + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function testAddAction() { $this->_requireVisitorWithNoProducts(); @@ -62,6 +68,9 @@ public function testAddAction() $this->_assertCompareListEquals([$product->getEntityId()]); } + /** + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function testIndexActionAddProducts() { $this->_requireVisitorWithNoProducts(); @@ -73,6 +82,9 @@ public function testIndexActionAddProducts() $this->_assertCompareListEquals([$product->getEntityId()]); } + /** + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function testRemoveAction() { $this->_requireVisitorWithTwoProducts(); @@ -89,6 +101,9 @@ public function testRemoveAction() $this->_assertCompareListEquals([$restProduct->getEntityId()]); } + /** + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function testRemoveActionWithSession() { $this->_requireCustomerWithTwoProducts(); @@ -106,6 +121,9 @@ public function testRemoveActionWithSession() $this->_assertCompareListEquals([$secondProduct->getEntityId()]); } + /** + * testIndexActionDisplay + */ public function testIndexActionDisplay() { $this->_requireVisitorWithTwoProducts(); @@ -132,6 +150,9 @@ public function testIndexActionDisplay() $this->assertContains('$987.65', $responseBody); } + /** + * testClearAction + */ public function testClearAction() { $this->_requireVisitorWithTwoProducts(); @@ -165,6 +186,9 @@ public function testRemoveActionProductNameXss() ); } + /** + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ protected function _prepareCompareListWithProductNameXss() { /** @var $visitor \Magento\Customer\Model\Visitor */ @@ -187,6 +211,9 @@ protected function _prepareCompareListWithProductNameXss() ); } + /** + * _requireVisitorWithNoProducts + */ protected function _requireVisitorWithNoProducts() { /** @var $visitor \Magento\Customer\Model\Visitor */ @@ -206,6 +233,9 @@ protected function _requireVisitorWithNoProducts() $this->_assertCompareListEquals([]); } + /** + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ protected function _requireVisitorWithTwoProducts() { /** @var $visitor \Magento\Customer\Model\Visitor */ @@ -238,6 +268,9 @@ protected function _requireVisitorWithTwoProducts() $this->_assertCompareListEquals([$firstProductEntityId, $secondProductEntityId]); } + /** + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ protected function _requireCustomerWithTwoProducts() { $customer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() diff --git a/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php b/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php index 8a94e64cf625a..2f89cb72b0cd9 100644 --- a/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php @@ -13,6 +13,9 @@ */ class IndexTest extends \Magento\TestFramework\TestCase\AbstractController { + /** + * testPostAction + */ public function testPostAction() { $params = [ @@ -36,8 +39,8 @@ public function testPostAction() /** * @dataProvider dataInvalidPostAction - * @param $params - * @param $expectedMessage + * @param array $params + * @param string $expectedMessage */ public function testInvalidPostAction($params, $expectedMessage) { @@ -52,6 +55,9 @@ public function testInvalidPostAction($params, $expectedMessage) ); } + /** + * @return array + */ public static function dataInvalidPostAction() { return [ diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php index d97a57589bc59..9ac778da91f29 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php @@ -21,6 +21,9 @@ */ class CreatePdfFileTest extends \PHPUnit\Framework\TestCase { + /** + * @throws \Exception + */ public function testGenerateFileFromString() { $objectManager = Bootstrap::getObjectManager(); diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php index 90892be1327c9..5a094eb05c774 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php @@ -21,6 +21,9 @@ class ManageTest extends \Magento\TestFramework\TestCase\AbstractController */ protected $coreSession; + /** + * Test setup + */ protected function setUp() { parent::setUp(); @@ -31,6 +34,9 @@ protected function setUp() $this->coreSession->setData('_form_key', 'formKey'); } + /** + * test tearDown + */ protected function tearDown() { $this->customerSession->setCustomerId(null); diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php index ebf2c2eea9553..19343e49192ac 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php @@ -31,6 +31,9 @@ class TaxTest extends \Magento\TestFramework\Indexer\TestCase */ private $totalsCollector; + /** + * test setup + */ public function setUp() { /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ diff --git a/lib/internal/Magento/Framework/App/Request/Http.php b/lib/internal/Magento/Framework/App/Request/Http.php index 7e8ceacc63520..03a7200fbe203 100644 --- a/lib/internal/Magento/Framework/App/Request/Http.php +++ b/lib/internal/Magento/Framework/App/Request/Http.php @@ -100,7 +100,7 @@ class Http extends Request implements RequestContentInterface, RequestSafetyInte * @param StringUtils $converter * @param ConfigInterface $routeConfig * @param PathInfoProcessorInterface $pathInfoProcessor - * @param ObjectManagerInterface $objectManager + * @param ObjectManagerInterface $objectManager * @param \Zend\Uri\UriInterface|string|null $uri * @param array $directFrontNames */ From b681953fb3af986fe2e55f3c47bc0f82260d886e Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@magento.com> Date: Thu, 9 Aug 2018 15:26:53 +0300 Subject: [PATCH 0856/1171] MAGETWO-93753: [Forwardport] Implement sharding and parallelization for Price Indexer - part 2 --- .../Paypal/Model/Payflow/Service/Request/SecureTokenTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php index f865c9ededd3d..04fb36312f300 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php @@ -23,6 +23,7 @@ /** * @magentoAppIsolation enabled * @magentoDbIsolation disabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SecureTokenTest extends TestCase { From ed28110ff1505c5950506cd3835b8112dcdbd084 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Thu, 9 Aug 2018 16:58:18 +0300 Subject: [PATCH 0857/1171] MAGETWO-91558: Enable Add to Cart on bundle products when bundle item qty is not User Defined while backorders are allowed --- .../Magento/Bundle/Model/ProductTest.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/ProductTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/ProductTest.php index caf1d256e53e9..4303577e6c435 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/ProductTest.php @@ -39,6 +39,9 @@ class ProductTest extends \PHPUnit\Framework\TestCase */ private $objectManager; + /** + * @return void + */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); @@ -47,6 +50,13 @@ protected function setUp() $this->model->setTypeId(Type::TYPE_BUNDLE); } + /** + * Tests Retrieve ans set type instance of the product + * + * @see \Magento\Catalog\Model\Product::getTypeInstance + * @see \Magento\Catalog\Model\Product::setTypeInstance + * @return void + */ public function testGetSetTypeInstance() { // model getter @@ -86,6 +96,12 @@ public function testCRUD() $crud->testCrud(); } + /** + * Tests Get product price model + * + * @see \Magento\Catalog\Model\Product::getPriceModel + * @return void + */ public function testGetPriceModel() { $this->model->setTypeId(Type::TYPE_BUNDLE); @@ -94,6 +110,12 @@ public function testGetPriceModel() $this->assertSame($type, $this->model->getPriceModel()); } + /** + * Tests Check is product composite + * + * @see \Magento\Catalog\Model\Product::isComposite + * @return void + */ public function testIsComposite() { $this->assertTrue($this->model->isComposite()); From df7ecf71c50dd4b856d8b1b610656509b6af26d7 Mon Sep 17 00:00:00 2001 From: nikita <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 9 Aug 2018 17:06:57 +0300 Subject: [PATCH 0858/1171] MAGETWO-73774: [2.3] Custom option price field disallows negative values #7333 --- .../Option/Validator/DefaultValidatorTest.php | 19 +++++++----- .../Product/Option/Validator/FileTest.php | 17 +++++++++++ .../Product/Option/Validator/SelectTest.php | 13 ++++++++ .../Product/Option/Validator/TextTest.php | 13 ++++++++ .../Product/Form/Modifier/CustomOptions.php | 4 +-- .../Catalog/Model/ProductPriceTest.php | 14 +++++++++ .../Magento/Catalog/Model/ProductTest.php | 30 +++++++++++++++++++ 7 files changed, 101 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php index 5a8dba5c8c2b2..73a27eb189187 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php @@ -18,6 +18,11 @@ class DefaultValidatorTest extends \PHPUnit\Framework\TestCase */ protected $valueMock; + /** + * Class dependencies initialization + * + * @return void + */ protected function setUp() { $configMock = $this->createMock(\Magento\Catalog\Model\ProductOptions\ConfigInterface::class); @@ -68,12 +73,12 @@ public function isValidTitleDataProvider() } /** - * @param $title - * @param $type - * @param $priceType - * @param $product - * @param $messages - * @param $result + * @param string $title + * @param string $type + * @param string $priceType + * @param \Magento\Framework\DataObject $product + * @param array $messages + * @param bool $result * @dataProvider isValidTitleDataProvider */ public function testIsValidTitle($title, $type, $priceType, $product, $messages, $result) @@ -103,7 +108,7 @@ public function isValidFailDataProvider() } /** - * @param $product + * @param \Magento\Framework\DataObject $product * @dataProvider isValidFailDataProvider */ public function testIsValidFail($product) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php index d8b48d0cc984e..ec84aa8e7b964 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php @@ -18,6 +18,11 @@ class FileTest extends \PHPUnit\Framework\TestCase */ protected $valueMock; + /** + * Class dependencies initialization + * + * @return void + */ protected function setUp() { $configMock = $this->createMock(\Magento\Catalog\Model\ProductOptions\ConfigInterface::class); @@ -54,6 +59,10 @@ protected function setUp() ); } + /** + * @throws \Zend_Validate_Exception + * @return void + */ public function testIsValidSuccess() { $this->valueMock->expects($this->once())->method('getTitle')->will($this->returnValue('option_title')); @@ -68,6 +77,10 @@ public function testIsValidSuccess() $this->assertTrue($this->validator->isValid($this->valueMock)); } + /** + * @throws \Zend_Validate_Exception + * @return void + */ public function testIsValidWithNegativeImageSize() { $this->valueMock->expects($this->once())->method('getTitle')->will($this->returnValue('option_title')); @@ -85,6 +98,10 @@ public function testIsValidWithNegativeImageSize() $this->assertEquals($messages, $this->validator->getMessages()); } + /** + * @throws \Zend_Validate_Exception + * @return void + */ public function testIsValidWithNegativeImageSizeY() { $this->valueMock->expects($this->once())->method('getTitle')->will($this->returnValue('option_title')); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php index 675821fcda111..94b5a05e48168 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php @@ -18,6 +18,11 @@ class SelectTest extends \PHPUnit\Framework\TestCase */ protected $valueMock; + /** + * Class dependencies initialization + * + * @return void + */ protected function setUp() { $configMock = $this->createMock(\Magento\Catalog\Model\ProductOptions\ConfigInterface::class); @@ -100,6 +105,10 @@ public function isValidSuccessDataProvider() ]; } + /** + * @throws \Zend_Validate_Exception + * @return void + */ public function testIsValidateWithInvalidOptionValues() { $this->valueMock->expects($this->once())->method('getTitle')->will($this->returnValue('option_title')); @@ -118,6 +127,10 @@ public function testIsValidateWithInvalidOptionValues() $this->assertEquals($messages, $this->validator->getMessages()); } + /** + * @throws \Zend_Validate_Exception + * @return void + */ public function testIsValidateWithEmptyValues() { $this->valueMock->expects($this->once())->method('getTitle')->will($this->returnValue('option_title')); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php index ffd858c3d433e..9232630a7c03f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php @@ -18,6 +18,11 @@ class TextTest extends \PHPUnit\Framework\TestCase */ protected $valueMock; + /** + * Class dependencies initialization + * + * @return void + */ protected function setUp() { $configMock = $this->createMock(\Magento\Catalog\Model\ProductOptions\ConfigInterface::class); @@ -54,6 +59,10 @@ protected function setUp() ); } + /** + * @throws \Zend_Validate_Exception + * @return void + */ public function testIsValidSuccess() { $this->valueMock->expects($this->once())->method('getTitle')->will($this->returnValue('option_title')); @@ -67,6 +76,10 @@ public function testIsValidSuccess() $this->assertEmpty($this->validator->getMessages()); } + /** + * @throws \Zend_Validate_Exception + * @return void + */ public function testIsValidWithNegativeMaxCharacters() { $this->valueMock->expects($this->once())->method('getTitle')->will($this->returnValue('option_title')); 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 e557c8a377681..86f1db2022cc9 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 @@ -166,7 +166,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc * @since 101.0.0 */ public function modifyData(array $data) @@ -226,7 +226,7 @@ protected function formatPriceByPath($path, array $data) } /** - * {@inheritdoc} + * @inheritdoc * @since 101.0.0 */ public function modifyMeta(array $meta) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php index 793275ae5a18d..c31ca7e3ee01d 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php @@ -29,12 +29,20 @@ class ProductPriceTest extends \PHPUnit\Framework\TestCase */ private $productRepository; + /** + * Class dependencies initialization + * + * @return void + */ protected function setUp() { $this->_model = Bootstrap::getObjectManager()->create(Product::class); $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); } + /** + * @return void + */ public function testGetPrice() { $this->assertEmpty($this->_model->getPrice()); @@ -42,6 +50,9 @@ public function testGetPrice() $this->assertEquals(10.0, $this->_model->getPrice()); } + /** + * @return void + */ public function testGetPriceModel() { $default = $this->_model->getPriceModel(); @@ -73,6 +84,9 @@ public function testGetFormatedPrice() $this->assertEquals('<span class="price">$0.00</span>', $this->_model->getFormatedPrice()); } + /** + * @return void + */ public function testSetGetFinalPrice() { $this->assertEquals(0, $this->_model->getFinalPrice()); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php index 7f1d37689e042..3d65d1869dda7 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php @@ -35,6 +35,11 @@ class ProductTest extends \PHPUnit\Framework\TestCase */ protected $_model; + /** + * Class dependencies initialization + * + * @return void + */ protected function setUp() { $this->productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() @@ -45,6 +50,10 @@ protected function setUp() ); } + /** + * @throws \Magento\Framework\Exception\FileSystemException + * @return void + */ public static function tearDownAfterClass() { $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -66,6 +75,9 @@ public static function tearDownAfterClass() } } + /** + * @return void + */ public function testCanAffectOptions() { $this->assertFalse($this->_model->canAffectOptions()); @@ -105,6 +117,9 @@ public function testCRUD() $crud->testCrud(); } + /** + * @return void + */ public function testCleanCache() { \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( @@ -125,6 +140,9 @@ public function testCleanCache() ); } + /** + * @return void + */ public function testAddImageToMediaGallery() { // Model accepts only files in tmp media path, we need to copy fixture file there @@ -332,6 +350,9 @@ public function testIsVirtual() $this->assertTrue($model->getIsVirtual()); } + /** + * @return void + */ public function testToArray() { $this->assertEquals([], $this->_model->toArray()); @@ -339,6 +360,9 @@ public function testToArray() $this->assertEquals(['sku' => 'sku', 'name' => 'name'], $this->_model->toArray()); } + /** + * @return void + */ public function testFromArray() { $this->_model->fromArray(['sku' => 'sku', 'name' => 'name', 'stock_item' => ['key' => 'value']]); @@ -410,6 +434,9 @@ public function testIsProductsHasSku() ); } + /** + * @return void + */ public function testProcessBuyRequest() { $request = new \Magento\Framework\DataObject(); @@ -418,6 +445,9 @@ public function testProcessBuyRequest() $this->assertArrayHasKey('errors', $result->getData()); } + /** + * @return void + */ public function testValidate() { $this->_model->setTypeId( From 8b184b4107110fefa7bde7623533086a3c473d03 Mon Sep 17 00:00:00 2001 From: vprohorov <vitaliy_prokharau@epam.com> Date: Thu, 26 Jul 2018 17:33:04 +0300 Subject: [PATCH 0859/1171] MAGETWO-91701: Newsletter subscription is not correctly updated when user is registered on 2 stores - Adding additional checking for guest customers --- .../Model/ResourceModel/Subscriber.php | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php b/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php index c7ce4b2f2f11b..4e059f9fe79a0 100644 --- a/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php @@ -48,6 +48,13 @@ class Subscriber extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb */ protected $mathRandom; + /** + * Guest customer id + * + * @var int + */ + private $guestCustomerId = 0; + /** * Construct * @@ -136,22 +143,24 @@ public function loadByCustomerData(\Magento\Customer\Api\Data\CustomerInterface return $result; } - $select = $this->connection - ->select() - ->from($this->getMainTable()) - ->where('subscriber_email=:subscriber_email and store_id=:store_id'); - - $result = $this->connection - ->fetchRow( - $select, - [ - 'subscriber_email' => $customer->getEmail(), - 'store_id' => $customer->getStoreId() - ] - ); - - if ($result) { - return $result; + if ($customer->getId() === $this->guestCustomerId) { + $select = $this->connection + ->select() + ->from($this->getMainTable()) + ->where('subscriber_email=:subscriber_email and store_id=:store_id'); + + $result = $this->connection + ->fetchRow( + $select, + [ + 'subscriber_email' => $customer->getEmail(), + 'store_id' => $customer->getStoreId() + ] + ); + + if ($result) { + return $result; + } } return []; From 2cad06db521d411a31d9a33b7e57187fdf45fd65 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Thu, 9 Aug 2018 10:01:06 -0500 Subject: [PATCH 0860/1171] MC-160: Admin should be able to delete catalog price rule --- .../Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml index 93d19de131369..9d31b2e0e403c 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml @@ -73,6 +73,8 @@ <!-- Apply and flush the cache --> <click selector="{{AdminCatalogPriceRuleGrid.applyRules}}" stepKey="clickApplyRules"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache"/> <!-- Verify that category page shows the original prices --> From 012d67c2a2b7adee67844dc8dc10862477ad65b4 Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv <rkostiv@magento.com> Date: Thu, 9 Aug 2018 18:46:49 +0300 Subject: [PATCH 0861/1171] MAGETWO-94060: [2.3.x] Unlink CatalogWidget from EAV indexer --- app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php index 27d027ce6ca43..29adc1816d337 100644 --- a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php +++ b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php @@ -174,7 +174,7 @@ protected function addGlobalAttribute( $collection->addAttributeToSelect($attribute->getAttributeCode(), 'inner'); break; default: - $alias = 'at_' . md5($this->getId()) . $attribute->getAttributeCode(); + $alias = 'at_' . sha1($this->getId()) . $attribute->getAttributeCode(); $connection = $this->_productResource->getConnection(); $storeId = $connection->getIfNullSql($alias . '.store_id', $this->storeManager->getStore()->getId()); From e2d4aa7200227634ae244517cac98dd5839fde7e Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Thu, 9 Aug 2018 19:16:25 +0300 Subject: [PATCH 0862/1171] Code style fixes --- .../Contact/Test/Unit/Controller/Index/PostTest.php | 2 ++ .../Customer/Test/Unit/Model/AccountManagementTest.php | 3 +++ app/code/Magento/Eav/Api/Data/AttributeInterface.php | 7 ++++--- .../Magento/Ui/view/base/web/js/grid/columns/actions.js | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php b/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php index b78edcb0731ff..e5b7f53ecb26b 100644 --- a/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php +++ b/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php @@ -133,6 +133,8 @@ public function testExecuteEmptyPost() } /** + * @param array $postData + * @param bool $exceptionExpected * @dataProvider postDataProvider */ public function testExecutePostValidation($postData, $exceptionExpected) diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php index af36e20e14204..86e7683545fa2 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php @@ -1521,6 +1521,9 @@ public function testChangePassword() $this->assertTrue($this->accountManagement->changePassword($email, $currentPassword, $newPassword)); } + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testResetPassword() { $customerEmail = 'customer@example.com'; diff --git a/app/code/Magento/Eav/Api/Data/AttributeInterface.php b/app/code/Magento/Eav/Api/Data/AttributeInterface.php index 1b2fdde005356..55d6e58b64b71 100644 --- a/app/code/Magento/Eav/Api/Data/AttributeInterface.php +++ b/app/code/Magento/Eav/Api/Data/AttributeInterface.php @@ -6,14 +6,15 @@ */ namespace Magento\Eav\Api\Data; +use Magento\Framework\Api\CustomAttributesDataInterface; +use Magento\Framework\Api\MetadataObjectInterface; + /** * Interface AttributeInterface * @api * @since 100.0.2 */ -interface AttributeInterface - extends \Magento\Framework\Api\CustomAttributesDataInterface, - \Magento\Framework\Api\MetadataObjectInterface +interface AttributeInterface extends CustomAttributesDataInterface, MetadataObjectInterface { const ATTRIBUTE_ID = 'attribute_id'; diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js index f25106662a357..be7a1a13fbd61 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js @@ -79,7 +79,7 @@ define([ /** * Adds new action. If an action with the specified identifier - * already exists, then the original will be overriden. + * already exists, then the original will be overridden. * * @param {String} index - Actions' identifier. * @param {Object} action - Actions' data. From 2c517d80c9f8733fd5e8ce5b03170c1e5ee68de1 Mon Sep 17 00:00:00 2001 From: Emily Pepperman <emily@prowebconcepts.com> Date: Thu, 9 Aug 2018 12:32:36 -0400 Subject: [PATCH 0863/1171] small misspelling fixed --- .../Model/ShippingMethodChoose/CarrierFinder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/InstantPurchase/Model/ShippingMethodChoose/CarrierFinder.php b/app/code/Magento/InstantPurchase/Model/ShippingMethodChoose/CarrierFinder.php index 0eb2afd04140a..d3e84083e5b42 100644 --- a/app/code/Magento/InstantPurchase/Model/ShippingMethodChoose/CarrierFinder.php +++ b/app/code/Magento/InstantPurchase/Model/ShippingMethodChoose/CarrierFinder.php @@ -11,7 +11,7 @@ use Magento\Store\Model\StoreManagerInterface; /** - * Collect shipping rates for customer address without packaging estiamtion. + * Collect shipping rates for customer address without packaging estimation. */ class CarrierFinder { From 9f644200fa0bcb2c185280721785b8e72b8e5d9b Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Thu, 9 Aug 2018 11:39:44 -0500 Subject: [PATCH 0864/1171] MQE-1172: Bump MFTF version and deliver Magento branches - MFTF version bump to use MQE-1181 (release candidate branch) - RoboFile returning exit code correctly --- composer.json | 8 ++- composer.lock | 97 +++++++++++++++++-------------- dev/tests/acceptance/RoboFile.php | 34 ++++++----- 3 files changed, 78 insertions(+), 61 deletions(-) diff --git a/composer.json b/composer.json index 2eba717464e68..209ccacaedb8c 100644 --- a/composer.json +++ b/composer.json @@ -80,8 +80,14 @@ "zendframework/zend-validator": "^2.6.0", "zendframework/zend-view": "~2.10.0" }, + "repositories": [ + { + "type": "git", + "url": "git@github.com:magento/magento2-functional-testing-framework.git" + } + ], "require-dev": { - "magento/magento2-functional-testing-framework": "2.3.3", + "magento/magento2-functional-testing-framework": "dev-develop", "friendsofphp/php-cs-fixer": "~2.12.0", "lusitanian/oauth": "~0.8.10", "pdepend/pdepend": "2.5.2", diff --git a/composer.lock b/composer.lock index 430b233d72fc6..1e5d6929e30c2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "57ae7ad75c4d8d50eb40e4ffe0fd2740", + "content-hash": "144b16cfcd074bac2b151d14b9a61c0d", "packages": [ { "name": "braintree/braintree_php", @@ -201,16 +201,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "d2c0a83b7533d6912e8d516756ebd34f893e9169" + "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/d2c0a83b7533d6912e8d516756ebd34f893e9169", - "reference": "d2c0a83b7533d6912e8d516756ebd34f893e9169", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/46afded9720f40b9dc63542af4e3e43a1177acb0", + "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0", "shasum": "" }, "require": { @@ -253,20 +253,20 @@ "ssl", "tls" ], - "time": "2018-03-29T19:57:20+00:00" + "time": "2018-08-08T08:57:40+00:00" }, { "name": "composer/composer", - "version": "1.7.0", + "version": "1.7.1", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "39edb2f375679a4eba19e69e9c9491e302976983" + "reference": "5d9311d4555787c8a57fea15f82471499aedf712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/39edb2f375679a4eba19e69e9c9491e302976983", - "reference": "39edb2f375679a4eba19e69e9c9491e302976983", + "url": "https://api.github.com/repos/composer/composer/zipball/5d9311d4555787c8a57fea15f82471499aedf712", + "reference": "5d9311d4555787c8a57fea15f82471499aedf712", "shasum": "" }, "require": { @@ -333,7 +333,7 @@ "dependency", "package" ], - "time": "2018-08-03T13:39:07+00:00" + "time": "2018-08-07T07:39:23+00:00" }, { "name": "composer/semver", @@ -872,16 +872,16 @@ }, { "name": "magento/zendframework1", - "version": "1.14.0", + "version": "1.14.1", "source": { "type": "git", "url": "https://github.com/magento/zf1.git", - "reference": "68522e5768edc8e829d1f64b620a3de3753f1141" + "reference": "4df018254c70b5b998b00a8cb1a30760f831ff0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/zf1/zipball/68522e5768edc8e829d1f64b620a3de3753f1141", - "reference": "68522e5768edc8e829d1f64b620a3de3753f1141", + "url": "https://api.github.com/repos/magento/zf1/zipball/4df018254c70b5b998b00a8cb1a30760f831ff0d", + "reference": "4df018254c70b5b998b00a8cb1a30760f831ff0d", "shasum": "" }, "require": { @@ -915,7 +915,7 @@ "ZF1", "framework" ], - "time": "2018-04-06T18:49:03+00:00" + "time": "2018-08-09T15:03:40+00:00" }, { "name": "monolog/monolog", @@ -4777,16 +4777,16 @@ }, { "name": "consolidation/config", - "version": "1.0.11", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/consolidation/config.git", - "reference": "ede41d946078e97e7a9513aadc3352f1c26817af" + "reference": "c9fc25e9088a708637e18a256321addc0670e578" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/ede41d946078e97e7a9513aadc3352f1c26817af", - "reference": "ede41d946078e97e7a9513aadc3352f1c26817af", + "url": "https://api.github.com/repos/consolidation/config/zipball/c9fc25e9088a708637e18a256321addc0670e578", + "reference": "c9fc25e9088a708637e18a256321addc0670e578", "shasum": "" }, "require": { @@ -4796,7 +4796,7 @@ }, "require-dev": { "g1a/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4", + "phpunit/phpunit": "^5", "satooshi/php-coveralls": "^1.0", "squizlabs/php_codesniffer": "2.*", "symfony/console": "^2.5|^3|^4", @@ -4827,7 +4827,7 @@ } ], "description": "Provide configuration services for a commandline tool.", - "time": "2018-05-27T01:17:02+00:00" + "time": "2018-08-07T22:57:00+00:00" }, { "name": "consolidation/log", @@ -6183,17 +6183,11 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.3.3", + "version": "dev-develop", "source": { "type": "git", - "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "885c5a0562cd2e428ba6bdafaff86fce78ad6b08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/885c5a0562cd2e428ba6bdafaff86fce78ad6b08", - "reference": "885c5a0562cd2e428ba6bdafaff86fce78ad6b08", - "shasum": "" + "url": "git@github.com:magento/magento2-functional-testing-framework.git", + "reference": "228291162439cb6fba11733114c9b2993a040df0" }, "require": { "allure-framework/allure-codeception": "~1.2.6", @@ -6239,7 +6233,19 @@ "MFTF\\": "dev/tests/functional/MFTF" } }, - "notification-url": "https://packagist.org/downloads/", + "autoload-dev": { + "psr-4": { + "tests\\unit\\": "dev/tests/unit" + } + }, + "scripts": { + "tests": [ + "bin/phpunit-checks" + ], + "static": [ + "bin/static-checks" + ] + }, "license": [ "AGPL-3.0" ], @@ -6250,7 +6256,7 @@ "magento", "testing" ], - "time": "2018-08-06T18:35:58+00:00" + "time": "2018-08-06T18:28:29+00:00" }, { "name": "moontoast/math", @@ -7218,16 +7224,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.10", + "version": "6.5.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "5744955af9c0a2de74a5eb5287c50bf025100d39" + "reference": "7bab54cb366076023bbf457a2a0d513332cd40f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5744955af9c0a2de74a5eb5287c50bf025100d39", - "reference": "5744955af9c0a2de74a5eb5287c50bf025100d39", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7bab54cb366076023bbf457a2a0d513332cd40f2", + "reference": "7bab54cb366076023bbf457a2a0d513332cd40f2", "shasum": "" }, "require": { @@ -7245,7 +7251,7 @@ "phpunit/php-file-iterator": "^1.4.3", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.8", + "phpunit/phpunit-mock-objects": "^5.0.9", "sebastian/comparator": "^2.1", "sebastian/diff": "^2.0", "sebastian/environment": "^3.1", @@ -7298,20 +7304,20 @@ "testing", "xunit" ], - "time": "2018-08-03T05:27:14+00:00" + "time": "2018-08-07T07:05:35+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "5.0.8", + "version": "5.0.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "6f9a3c8bf34188a2b53ce2ae7a126089c53e0a9f" + "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/6f9a3c8bf34188a2b53ce2ae7a126089c53e0a9f", - "reference": "6f9a3c8bf34188a2b53ce2ae7a126089c53e0a9f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f", + "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f", "shasum": "" }, "require": { @@ -7324,7 +7330,7 @@ "phpunit/phpunit": "<6.0" }, "require-dev": { - "phpunit/phpunit": "^6.5" + "phpunit/phpunit": "^6.5.11" }, "suggest": { "ext-soap": "*" @@ -7357,7 +7363,7 @@ "mock", "xunit" ], - "time": "2018-07-13T03:27:23+00:00" + "time": "2018-08-09T05:50:03+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -8873,6 +8879,7 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { + "magento/magento2-functional-testing-framework": 20, "phpmd/phpmd": 0 }, "prefer-stable": true, diff --git a/dev/tests/acceptance/RoboFile.php b/dev/tests/acceptance/RoboFile.php index f36150ad254b5..e6e9e591bbd8b 100644 --- a/dev/tests/acceptance/RoboFile.php +++ b/dev/tests/acceptance/RoboFile.php @@ -31,7 +31,7 @@ function buildProject() * * @param array $tests * @param array $opts - * @return void + * @return \Robo\Result */ function generateTests(array $tests, $opts = [ 'config' => null, @@ -56,7 +56,7 @@ function generateTests(array $tests, $opts = [ $baseCmd .= ' --force'; } - $this->taskExec($baseCmd)->args($tests)->run(); + return $this->taskExec($baseCmd)->args($tests)->run(); } /** @@ -64,7 +64,7 @@ function generateTests(array $tests, $opts = [ * * @param array $args * @throws Exception - * @return void + * @return \Robo\Result */ function generateSuite(array $args) { @@ -72,20 +72,20 @@ function generateSuite(array $args) throw new Exception("Please provide suite name(s) after generate:suite command"); } $baseCmd = $this->getBaseCmd("generate:suite"); - $this->taskExec($baseCmd)->args($args)->run(); + return $this->taskExec($baseCmd)->args($args)->run(); } /** * Run all Tests with the specified @group tag'. * * @param array $args - * @return void + * @return \Robo\Result */ function group(array $args) { $args = array_merge($args, ['-k']); $baseCmd = $this->getBaseCmd("run:group"); - $this->taskExec($baseCmd)->args($args)->run(); + return $this->taskExec($baseCmd)->args($args)->run(); } /** @@ -111,48 +111,52 @@ function allure2Generate() /** * Open the HTML Allure report - Allure v1.4.X * - * @return void + * @return \Robo\Result */ function allure1Open() { - $this->_exec('allure report open --report-dir tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); + return $this->_exec('allure report open --report-dir tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); } /** * Open the HTML Allure report - Allure v2.3.X * - * @return void + * @return \Robo\Result */ function allure2Open() { - $this->_exec('allure open --port 0 tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); + return $this->_exec('allure open --port 0 tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); } /** * Generate and open the HTML Allure report - Allure v1.4.X * - * @return void + * @return \Robo\Result */ function allure1Report() { $result1 = $this->allure1Generate(); if ($result1->wasSuccessful()) { - $this->allure1Open(); + return $this->allure1Open(); + } else { + return $result1; } } /** * Generate and open the HTML Allure report - Allure v2.3.X * - * @return void + * @return \Robo\Result */ function allure2Report() { $result1 = $this->allure2Generate(); if ($result1->wasSuccessful()) { - $this->allure2Open(); + return $this->allure2Open(); + } else { + return $result1; } } @@ -168,4 +172,4 @@ private function getBaseCmd($command) chdir(__DIR__); return realpath('../../../vendor/bin/mftf') . " $command"; } -} +} \ No newline at end of file From efdc2e39a07828c505611ceb8f537ee35eb8aeae Mon Sep 17 00:00:00 2001 From: Dzmitry Tabusheu <dzmitry_tabusheu@epam.com> Date: Thu, 9 Aug 2018 19:49:30 +0300 Subject: [PATCH 0865/1171] MAGETWO-91552: [github] CAPTCHA doesn't show when check out as guest - Removed guest checkout/register on checkout captcha configurations --- .../Observer/CheckGuestCheckoutObserver.php | 82 ------- .../CheckRegisterCheckoutObserver.php | 82 ------- .../Captcha/Test/Unit/Model/DefaultTest.php | 7 +- .../CheckGuestCheckoutObserverTest.php | 211 ------------------ .../CheckRegisterCheckoutObserverTest.php | 211 ------------------ app/code/Magento/Captcha/etc/config.xml | 8 - app/code/Magento/Captcha/etc/di.xml | 1 - app/code/Magento/Captcha/etc/events.xml | 4 - app/code/Magento/Captcha/etc/frontend/di.xml | 1 - .../frontend/layout/checkout_index_index.xml | 24 +- .../Captcha/view/frontend/web/onepage.js | 22 -- 11 files changed, 3 insertions(+), 650 deletions(-) delete mode 100644 app/code/Magento/Captcha/Observer/CheckGuestCheckoutObserver.php delete mode 100644 app/code/Magento/Captcha/Observer/CheckRegisterCheckoutObserver.php delete mode 100644 app/code/Magento/Captcha/Test/Unit/Observer/CheckGuestCheckoutObserverTest.php delete mode 100644 app/code/Magento/Captcha/Test/Unit/Observer/CheckRegisterCheckoutObserverTest.php delete mode 100644 app/code/Magento/Captcha/view/frontend/web/onepage.js diff --git a/app/code/Magento/Captcha/Observer/CheckGuestCheckoutObserver.php b/app/code/Magento/Captcha/Observer/CheckGuestCheckoutObserver.php deleted file mode 100644 index 7ccaa76b6c7c8..0000000000000 --- a/app/code/Magento/Captcha/Observer/CheckGuestCheckoutObserver.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Captcha\Observer; - -use Magento\Framework\Event\ObserverInterface; - -class CheckGuestCheckoutObserver implements ObserverInterface -{ - /** - * @var \Magento\Captcha\Helper\Data - */ - protected $_helper; - - /** - * @var \Magento\Framework\App\ActionFlag - */ - protected $_actionFlag; - - /** - * @var CaptchaStringResolver - */ - protected $captchaStringResolver; - - /** - * @var \Magento\Checkout\Model\Type\Onepage - */ - protected $_typeOnepage; - - /** - * @var \Magento\Framework\Json\Helper\Data - */ - protected $jsonHelper; - - /** - * @param \Magento\Captcha\Helper\Data $helper - * @param \Magento\Framework\App\ActionFlag $actionFlag - * @param CaptchaStringResolver $captchaStringResolver - * @param \Magento\Checkout\Model\Type\Onepage $typeOnepage - * @param \Magento\Framework\Json\Helper\Data $jsonHelper - */ - public function __construct( - \Magento\Captcha\Helper\Data $helper, - \Magento\Framework\App\ActionFlag $actionFlag, - CaptchaStringResolver $captchaStringResolver, - \Magento\Checkout\Model\Type\Onepage $typeOnepage, - \Magento\Framework\Json\Helper\Data $jsonHelper - ) { - $this->_helper = $helper; - $this->_actionFlag = $actionFlag; - $this->captchaStringResolver = $captchaStringResolver; - $this->_typeOnepage = $typeOnepage; - $this->jsonHelper = $jsonHelper; - } - - /** - * Check Captcha On Checkout as Guest Page - * - * @param \Magento\Framework\Event\Observer $observer - * @return $this - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - $formId = 'guest_checkout'; - $captchaModel = $this->_helper->getCaptcha($formId); - $checkoutMethod = $this->_typeOnepage->getQuote()->getCheckoutMethod(); - if ($checkoutMethod == \Magento\Checkout\Model\Type\Onepage::METHOD_GUEST - && $captchaModel->isRequired() - ) { - $controller = $observer->getControllerAction(); - if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId))) { - $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); - $result = ['error' => 1, 'message' => __('Incorrect CAPTCHA')]; - $controller->getResponse()->representJson($this->jsonHelper->jsonEncode($result)); - } - } - - return $this; - } -} diff --git a/app/code/Magento/Captcha/Observer/CheckRegisterCheckoutObserver.php b/app/code/Magento/Captcha/Observer/CheckRegisterCheckoutObserver.php deleted file mode 100644 index 8e110a9f4653d..0000000000000 --- a/app/code/Magento/Captcha/Observer/CheckRegisterCheckoutObserver.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Captcha\Observer; - -use Magento\Framework\Event\ObserverInterface; - -class CheckRegisterCheckoutObserver implements ObserverInterface -{ - /** - * @var \Magento\Captcha\Helper\Data - */ - protected $_helper; - - /** - * @var \Magento\Framework\App\ActionFlag - */ - protected $_actionFlag; - - /** - * @var CaptchaStringResolver - */ - protected $captchaStringResolver; - - /** - * @var \Magento\Checkout\Model\Type\Onepage - */ - protected $_typeOnepage; - - /** - * @var \Magento\Framework\Json\Helper\Data - */ - protected $jsonHelper; - - /** - * @param \Magento\Captcha\Helper\Data $helper - * @param \Magento\Framework\App\ActionFlag $actionFlag - * @param CaptchaStringResolver $captchaStringResolver - * @param \Magento\Checkout\Model\Type\Onepage $typeOnepage - * @param \Magento\Framework\Json\Helper\Data $jsonHelper - */ - public function __construct( - \Magento\Captcha\Helper\Data $helper, - \Magento\Framework\App\ActionFlag $actionFlag, - CaptchaStringResolver $captchaStringResolver, - \Magento\Checkout\Model\Type\Onepage $typeOnepage, - \Magento\Framework\Json\Helper\Data $jsonHelper - ) { - $this->_helper = $helper; - $this->_actionFlag = $actionFlag; - $this->captchaStringResolver = $captchaStringResolver; - $this->_typeOnepage = $typeOnepage; - $this->jsonHelper = $jsonHelper; - } - - /** - * Check Captcha On Checkout Register Page - * - * @param \Magento\Framework\Event\Observer $observer - * @return $this - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - $formId = 'register_during_checkout'; - $captchaModel = $this->_helper->getCaptcha($formId); - $checkoutMethod = $this->_typeOnepage->getQuote()->getCheckoutMethod(); - if ($checkoutMethod == \Magento\Checkout\Model\Type\Onepage::METHOD_REGISTER - && $captchaModel->isRequired() - ) { - $controller = $observer->getControllerAction(); - if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId))) { - $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); - $result = ['error' => 1, 'message' => __('Incorrect CAPTCHA')]; - $controller->getResponse()->representJson($this->jsonHelper->jsonEncode($result)); - } - } - - return $this; - } -} diff --git a/app/code/Magento/Captcha/Test/Unit/Model/DefaultTest.php b/app/code/Magento/Captcha/Test/Unit/Model/DefaultTest.php index 0500b29f787c2..1edbcc029e4ce 100644 --- a/app/code/Magento/Captcha/Test/Unit/Model/DefaultTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Model/DefaultTest.php @@ -24,7 +24,7 @@ class DefaultTest extends \PHPUnit\Framework\TestCase 'enable' => '1', 'font' => 'linlibertine', 'mode' => 'after_fail', - 'forms' => 'user_forgotpassword,user_create,guest_checkout,register_during_checkout', + 'forms' => 'user_forgotpassword,user_create', 'failed_attempts_login' => '3', 'failed_attempts_ip' => '1000', 'timeout' => '7', @@ -35,8 +35,6 @@ class DefaultTest extends \PHPUnit\Framework\TestCase 'always_for' => [ 'user_create', 'user_forgotpassword', - 'guest_checkout', - 'register_during_checkout', 'contact_us', ], ]; @@ -362,8 +360,7 @@ public function isShownToLoggedInUserDataProvider() return [ [true, 'contact_us'], [false, 'user_create'], - [false, 'user_forgotpassword'], - [false, 'guest_checkout'] + [false, 'user_forgotpassword'] ]; } } diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckGuestCheckoutObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckGuestCheckoutObserverTest.php deleted file mode 100644 index d3f29fae8a592..0000000000000 --- a/app/code/Magento/Captcha/Test/Unit/Observer/CheckGuestCheckoutObserverTest.php +++ /dev/null @@ -1,211 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Captcha\Test\Unit\Observer; - -use Magento\Captcha\Model\DefaultModel as CaptchaModel; -use Magento\Captcha\Observer\CheckGuestCheckoutObserver; -use Magento\Captcha\Helper\Data as CaptchaDataHelper; -use Magento\Framework\App\Action\Action; -use Magento\Framework\App\ActionFlag; -use Magento\Captcha\Observer\CaptchaStringResolver; -use Magento\Checkout\Model\Type\Onepage; -use Magento\Framework\App\Request\Http; -use Magento\Framework\App\Response\Http as HttpResponse; -use Magento\Framework\Event\Observer; -use Magento\Framework\Json\Helper\Data as JsonHelper; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Quote\Model\Quote; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CheckGuestCheckoutObserverTest extends \PHPUnit\Framework\TestCase -{ - const FORM_ID = 'guest_checkout'; - - /** - * @var CheckGuestCheckoutObserver - */ - private $checkGuestCheckoutObserver; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var Observer - */ - private $observer; - - /** - * @var HttpResponse|\PHPUnit_Framework_MockObject_MockObject - */ - private $responseMock; - - /** - * @var HttpResponse|\PHPUnit_Framework_MockObject_MockObject - */ - private $requestMock; - - /** - * @var ActionFlag|\PHPUnit_Framework_MockObject_MockObject - */ - private $actionFlagMock; - - /** - * @var CaptchaStringResolver|\PHPUnit_Framework_MockObject_MockObject - */ - private $captchaStringResolverMock; - - /** - * @var JsonHelper|\PHPUnit_Framework_MockObject_MockObject - */ - private $jsonHelperMock; - - /** - * @var CaptchaModel|\PHPUnit_Framework_MockObject_MockObject - */ - private $captchaModelMock; - - /** - * @var Quote|\PHPUnit_Framework_MockObject_MockObject - */ - private $quoteModelMock; - - /** - * @var Action|\PHPUnit_Framework_MockObject_MockObject - */ - private $controllerMock; - - protected function setUp() - { - $onepageModelTypeMock = $this->createMock(Onepage::class); - $captchaHelperMock = $this->createMock(CaptchaDataHelper::class); - $this->objectManager = new ObjectManager($this); - $this->actionFlagMock = $this->createMock(ActionFlag::class); - $this->captchaStringResolverMock = $this->createMock(CaptchaStringResolver::class); - $this->captchaModelMock = $this->createMock(CaptchaModel::class); - $this->quoteModelMock = $this->createMock(Quote::class); - $this->controllerMock = $this->createMock(Action::class); - $this->requestMock = $this->createMock(Http::class); - $this->responseMock = $this->createMock(HttpResponse::class); - $this->observer = new Observer(['controller_action' => $this->controllerMock]); - $this->jsonHelperMock = $this->createMock(JsonHelper::class); - - $this->checkGuestCheckoutObserver = $this->objectManager->getObject( - CheckGuestCheckoutObserver::class, - [ - 'helper' => $captchaHelperMock, - 'actionFlag' => $this->actionFlagMock, - 'captchaStringResolver' => $this->captchaStringResolverMock, - 'typeOnepage' => $onepageModelTypeMock, - 'jsonHelper' => $this->jsonHelperMock - ] - ); - - $captchaHelperMock->expects($this->once()) - ->method('getCaptcha') - ->with(self::FORM_ID) - ->willReturn($this->captchaModelMock); - $onepageModelTypeMock->expects($this->once()) - ->method('getQuote') - ->willReturn($this->quoteModelMock); - } - - public function testCheckGuestCheckoutForRegister() - { - $this->quoteModelMock->expects($this->once()) - ->method('getCheckoutMethod') - ->willReturn(Onepage::METHOD_REGISTER); - $this->captchaModelMock->expects($this->never()) - ->method('isRequired'); - - $this->checkGuestCheckoutObserver->execute($this->observer); - } - - public function testCheckGuestCheckoutWithNoCaptchaRequired() - { - $this->quoteModelMock->expects($this->once()) - ->method('getCheckoutMethod') - ->willReturn(Onepage::METHOD_GUEST); - $this->captchaModelMock->expects($this->once()) - ->method('isRequired') - ->willReturn(false); - $this->captchaModelMock->expects($this->never()) - ->method('isCorrect'); - - $this->checkGuestCheckoutObserver->execute($this->observer); - } - - public function testCheckGuestCheckoutWithIncorrectCaptcha() - { - $captchaValue = 'some_word'; - $encodedJsonValue = '{}'; - - $this->quoteModelMock->expects($this->once()) - ->method('getCheckoutMethod') - ->willReturn(Onepage::METHOD_GUEST); - $this->captchaModelMock->expects($this->once()) - ->method('isRequired') - ->willReturn(true); - $this->controllerMock->expects($this->once()) - ->method('getRequest') - ->willReturn($this->requestMock); - $this->controllerMock->expects($this->once()) - ->method('getResponse') - ->willReturn($this->responseMock); - $this->controllerMock->expects($this->once()) - ->method('getResponse') - ->willReturn($this->responseMock); - $this->captchaStringResolverMock->expects($this->once()) - ->method('resolve') - ->with($this->requestMock, self::FORM_ID) - ->willReturn($captchaValue); - $this->captchaModelMock->expects($this->once()) - ->method('isCorrect') - ->with($captchaValue) - ->willReturn(false); - $this->actionFlagMock->expects($this->once()) - ->method('set') - ->with('', Action::FLAG_NO_DISPATCH, true); - $this->jsonHelperMock->expects($this->once()) - ->method('jsonEncode') - ->willReturn($encodedJsonValue); - $this->responseMock->expects($this->once()) - ->method('representJson') - ->with($encodedJsonValue); - - $this->checkGuestCheckoutObserver->execute($this->observer); - } - - public function testCheckGuestCheckoutWithCorrectCaptcha() - { - $this->quoteModelMock->expects($this->once()) - ->method('getCheckoutMethod') - ->willReturn(Onepage::METHOD_GUEST); - $this->captchaModelMock->expects($this->once()) - ->method('isRequired') - ->willReturn(true); - $this->controllerMock->expects($this->once()) - ->method('getRequest') - ->willReturn($this->requestMock); - $this->captchaStringResolverMock->expects($this->once()) - ->method('resolve') - ->with($this->requestMock, self::FORM_ID) - ->willReturn('some_word'); - $this->captchaModelMock->expects($this->once()) - ->method('isCorrect') - ->with('some_word') - ->willReturn(true); - $this->actionFlagMock->expects($this->never()) - ->method('set'); - - $this->checkGuestCheckoutObserver->execute($this->observer); - } -} diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckRegisterCheckoutObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckRegisterCheckoutObserverTest.php deleted file mode 100644 index 89012ef653838..0000000000000 --- a/app/code/Magento/Captcha/Test/Unit/Observer/CheckRegisterCheckoutObserverTest.php +++ /dev/null @@ -1,211 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Captcha\Test\Unit\Observer; - -use Magento\Captcha\Model\DefaultModel as CaptchaModel; -use Magento\Captcha\Observer\CheckRegisterCheckoutObserver; -use Magento\Captcha\Helper\Data as CaptchaDataHelper; -use Magento\Framework\App\Action\Action; -use Magento\Framework\App\ActionFlag; -use Magento\Captcha\Observer\CaptchaStringResolver; -use Magento\Checkout\Model\Type\Onepage; -use Magento\Framework\App\Request\Http; -use Magento\Framework\App\Response\Http as HttpResponse; -use Magento\Framework\Event\Observer; -use Magento\Framework\Json\Helper\Data as JsonHelper; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Quote\Model\Quote; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CheckRegisterCheckoutObserverTest extends \PHPUnit\Framework\TestCase -{ - const FORM_ID = 'register_during_checkout'; - - /** - * @var CheckRegisterCheckoutObserver - */ - private $checkRegisterCheckoutObserver; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var Observer - */ - private $observer; - - /** - * @var HttpResponse|\PHPUnit_Framework_MockObject_MockObject - */ - private $responseMock; - - /** - * @var HttpResponse|\PHPUnit_Framework_MockObject_MockObject - */ - private $requestMock; - - /** - * @var ActionFlag|\PHPUnit_Framework_MockObject_MockObject - */ - private $actionFlagMock; - - /** - * @var CaptchaStringResolver|\PHPUnit_Framework_MockObject_MockObject - */ - private $captchaStringResolverMock; - - /** - * @var JsonHelper|\PHPUnit_Framework_MockObject_MockObject - */ - private $jsonHelperMock; - - /** - * @var CaptchaModel|\PHPUnit_Framework_MockObject_MockObject - */ - private $captchaModelMock; - - /** - * @var Quote|\PHPUnit_Framework_MockObject_MockObject - */ - private $quoteModelMock; - - /** - * @var Action|\PHPUnit_Framework_MockObject_MockObject - */ - private $controllerMock; - - protected function setUp() - { - $onepageModelTypeMock = $this->createMock(Onepage::class); - $captchaHelperMock = $this->createMock(CaptchaDataHelper::class); - $this->objectManager = new ObjectManager($this); - $this->actionFlagMock = $this->createMock(ActionFlag::class); - $this->captchaStringResolverMock = $this->createMock(CaptchaStringResolver::class); - $this->captchaModelMock = $this->createMock(CaptchaModel::class); - $this->quoteModelMock = $this->createMock(Quote::class); - $this->controllerMock = $this->createMock(Action::class); - $this->requestMock = $this->createMock(Http::class); - $this->responseMock = $this->createMock(HttpResponse::class); - $this->observer = new Observer(['controller_action' => $this->controllerMock]); - $this->jsonHelperMock = $this->createMock(JsonHelper::class); - - $this->checkRegisterCheckoutObserver = $this->objectManager->getObject( - CheckRegisterCheckoutObserver::class, - [ - 'helper' => $captchaHelperMock, - 'actionFlag' => $this->actionFlagMock, - 'captchaStringResolver' => $this->captchaStringResolverMock, - 'typeOnepage' => $onepageModelTypeMock, - 'jsonHelper' => $this->jsonHelperMock - ] - ); - - $captchaHelperMock->expects($this->once()) - ->method('getCaptcha') - ->with(self::FORM_ID) - ->willReturn($this->captchaModelMock); - $onepageModelTypeMock->expects($this->once()) - ->method('getQuote') - ->willReturn($this->quoteModelMock); - } - - public function testCheckRegisterCheckoutForGuest() - { - $this->quoteModelMock->expects($this->once()) - ->method('getCheckoutMethod') - ->willReturn(Onepage::METHOD_GUEST); - $this->captchaModelMock->expects($this->never()) - ->method('isRequired'); - - $this->checkRegisterCheckoutObserver->execute($this->observer); - } - - public function testCheckRegisterCheckoutWithNoCaptchaRequired() - { - $this->quoteModelMock->expects($this->once()) - ->method('getCheckoutMethod') - ->willReturn(Onepage::METHOD_REGISTER); - $this->captchaModelMock->expects($this->once()) - ->method('isRequired') - ->willReturn(false); - $this->captchaModelMock->expects($this->never()) - ->method('isCorrect'); - - $this->checkRegisterCheckoutObserver->execute($this->observer); - } - - public function testCheckRegisterCheckoutWithIncorrectCaptcha() - { - $captchaValue = 'some_word'; - $encodedJsonValue = '{}'; - - $this->quoteModelMock->expects($this->once()) - ->method('getCheckoutMethod') - ->willReturn(Onepage::METHOD_REGISTER); - $this->captchaModelMock->expects($this->once()) - ->method('isRequired') - ->willReturn(true); - $this->controllerMock->expects($this->once()) - ->method('getRequest') - ->willReturn($this->requestMock); - $this->controllerMock->expects($this->once()) - ->method('getResponse') - ->willReturn($this->responseMock); - $this->controllerMock->expects($this->once()) - ->method('getResponse') - ->willReturn($this->responseMock); - $this->captchaStringResolverMock->expects($this->once()) - ->method('resolve') - ->with($this->requestMock, self::FORM_ID) - ->willReturn($captchaValue); - $this->captchaModelMock->expects($this->once()) - ->method('isCorrect') - ->with($captchaValue) - ->willReturn(false); - $this->actionFlagMock->expects($this->once()) - ->method('set') - ->with('', Action::FLAG_NO_DISPATCH, true); - $this->jsonHelperMock->expects($this->once()) - ->method('jsonEncode') - ->willReturn($encodedJsonValue); - $this->responseMock->expects($this->once()) - ->method('representJson') - ->with($encodedJsonValue); - - $this->checkRegisterCheckoutObserver->execute($this->observer); - } - - public function testCheckRegisterCheckoutWithCorrectCaptcha() - { - $this->quoteModelMock->expects($this->once()) - ->method('getCheckoutMethod') - ->willReturn(Onepage::METHOD_REGISTER); - $this->captchaModelMock->expects($this->once()) - ->method('isRequired') - ->willReturn(true); - $this->controllerMock->expects($this->once()) - ->method('getRequest') - ->willReturn($this->requestMock); - $this->captchaStringResolverMock->expects($this->once()) - ->method('resolve') - ->with($this->requestMock, self::FORM_ID) - ->willReturn('some_word'); - $this->captchaModelMock->expects($this->once()) - ->method('isCorrect') - ->with('some_word') - ->willReturn(true); - $this->actionFlagMock->expects($this->never()) - ->method('set'); - - $this->checkRegisterCheckoutObserver->execute($this->observer); - } -} diff --git a/app/code/Magento/Captcha/etc/config.xml b/app/code/Magento/Captcha/etc/config.xml index 71c474de90ff4..dd748dd05ccda 100644 --- a/app/code/Magento/Captcha/etc/config.xml +++ b/app/code/Magento/Captcha/etc/config.xml @@ -53,8 +53,6 @@ <always_for> <user_create>1</user_create> <user_forgotpassword>1</user_forgotpassword> - <guest_checkout>1</guest_checkout> - <register_during_checkout>1</register_during_checkout> <contact_us>1</contact_us> </always_for> </captcha> @@ -77,12 +75,6 @@ <user_forgotpassword> <label>Forgot password</label> </user_forgotpassword> - <guest_checkout> - <label>Check Out as Guest</label> - </guest_checkout> - <register_during_checkout> - <label>Register during Checkout</label> - </register_during_checkout> <contact_us> <label>Contact Us</label> </contact_us> diff --git a/app/code/Magento/Captcha/etc/di.xml b/app/code/Magento/Captcha/etc/di.xml index 955896eb12744..3a929f5e6cc00 100644 --- a/app/code/Magento/Captcha/etc/di.xml +++ b/app/code/Magento/Captcha/etc/di.xml @@ -33,7 +33,6 @@ <arguments> <argument name="formIds" xsi:type="array"> <item name="user_login" xsi:type="string">user_login</item> - <item name="guest_checkout" xsi:type="string">guest_checkout</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Captcha/etc/events.xml b/app/code/Magento/Captcha/etc/events.xml index e3ddd19de2d12..970c0d077260c 100644 --- a/app/code/Magento/Captcha/etc/events.xml +++ b/app/code/Magento/Captcha/etc/events.xml @@ -18,10 +18,6 @@ <event name="admin_user_authenticate_before"> <observer name="captcha" instance="Magento\Captcha\Observer\CheckUserLoginBackendObserver" /> </event> - <event name="controller_action_predispatch_checkout_onepage_saveBilling"> - <observer name="captcha_guest" instance="Magento\Captcha\Observer\CheckGuestCheckoutObserver" /> - <observer name="captcha_register" instance="Magento\Captcha\Observer\CheckRegisterCheckoutObserver" /> - </event> <event name="customer_customer_authenticated"> <observer name="captcha_reset_attempt" instance="Magento\Captcha\Observer\ResetAttemptForFrontendObserver" /> </event> diff --git a/app/code/Magento/Captcha/etc/frontend/di.xml b/app/code/Magento/Captcha/etc/frontend/di.xml index 225e62c8e8203..0c4ab0cda0735 100644 --- a/app/code/Magento/Captcha/etc/frontend/di.xml +++ b/app/code/Magento/Captcha/etc/frontend/di.xml @@ -17,7 +17,6 @@ <arguments> <argument name="formIds" xsi:type="array"> <item name="user_login" xsi:type="string">user_login</item> - <item name="guest_checkout" xsi:type="string">guest_checkout</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Captcha/view/frontend/layout/checkout_index_index.xml b/app/code/Magento/Captcha/view/frontend/layout/checkout_index_index.xml index 4ed56fd56cc3a..7180372f004e5 100644 --- a/app/code/Magento/Captcha/view/frontend/layout/checkout_index_index.xml +++ b/app/code/Magento/Captcha/view/frontend/layout/checkout_index_index.xml @@ -36,29 +36,7 @@ <item name="captcha" xsi:type="array"> <item name="component" xsi:type="string">Magento_Captcha/js/view/checkout/loginCaptcha</item> <item name="displayArea" xsi:type="string">additional-login-form-fields</item> - <item name="formId" xsi:type="string">guest_checkout</item> - <item name="configSource" xsi:type="string">checkoutConfig</item> - </item> - </item> - </item> - </item> - </item> - </item> - </item> - </item> - </item> - <item name="billing-step" xsi:type="array"> - <item name="children" xsi:type="array"> - <item name="payment" xsi:type="array"> - <item name="children" xsi:type="array"> - <item name="customer-email" xsi:type="array"> - <item name="children" xsi:type="array"> - <item name="additional-login-form-fields" xsi:type="array"> - <item name="children" xsi:type="array"> - <item name="captcha" xsi:type="array"> - <item name="component" xsi:type="string">Magento_Captcha/js/view/checkout/loginCaptcha</item> - <item name="displayArea" xsi:type="string">additional-login-form-fields</item> - <item name="formId" xsi:type="string">guest_checkout</item> + <item name="formId" xsi:type="string">user_login</item> <item name="configSource" xsi:type="string">checkoutConfig</item> </item> </item> diff --git a/app/code/Magento/Captcha/view/frontend/web/onepage.js b/app/code/Magento/Captcha/view/frontend/web/onepage.js deleted file mode 100644 index 7f5f11d20572b..0000000000000 --- a/app/code/Magento/Captcha/view/frontend/web/onepage.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -/** - * @deprecated since version 2.2.0 - */ -define(['jquery'], function ($) { - 'use strict'; - - $(document).on('login', function () { - var type; - - $('[data-captcha="guest_checkout"], [data-captcha="register_during_checkout"]').hide(); - $('[role="guest_checkout"], [role="register_during_checkout"]').hide(); - type = $('#login\\:guest').is(':checked') ? 'guest_checkout' : 'register_during_checkout'; - $('[role="' + type + '"], [data-captcha="' + type + '"]').show(); - }).on('billingSave', function () { - $('.captcha-reload:visible').trigger('click'); - }); -}); From 63a1058907b82f65596d9e955fb9925fb1c43fc3 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 7 Aug 2018 11:23:12 +0200 Subject: [PATCH 0866/1171] Added unit test for instant purchase paypal token formatter --- .../PayPal/TokenFormatterTest.php | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php diff --git a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php new file mode 100644 index 0000000000000..ebb9a3e8bfb59 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Test\Unit\Model\InstantPurchase\Paypal; + +use Magento\Braintree\Model\InstantPurchase\CreditCard\TokenFormatter as PaypalTokenFormatter; +use Magento\Vault\Api\Data\PaymentTokenInterface; + +class TokenFormatterTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var PaymentTokenInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $paymentTokenMock; + + /** + * @var PaypalTokenFormatter + */ + private $paypalTokenFormatter; + + /** + * @var array + */ + private $tokenDetails = [ + 'type' => 'visa', + 'maskedCC' => '4444************9999', + 'expirationDate' => '07-07-2025' + ]; + + protected function setUp() + { + $this->paymentTokenMock = $this->getMockBuilder(PaymentTokenInterface::class) + ->getMockForAbstractClass(); + + $this->paypalTokenFormatter = new PaypalTokenFormatter(); + } + + public function testFormatPaymentTokenWithKnownCardType() + { + $this->tokenDetails['type'] = key(PaypalTokenFormatter::$baseCardTypes); + $this->paymentTokenMock->expects($this->once()) + ->method('getTokenDetails') + ->willReturn(json_encode($this->tokenDetails)); + + $formattedString = sprintf( + '%s: %s, %s: %s (%s: %s)', + __('Credit Card'), + reset(PaypalTokenFormatter::$baseCardTypes), + __('ending'), + $this->tokenDetails['maskedCC'], + __('expires'), + $this->tokenDetails['expirationDate'] + ); + + self::assertEquals($formattedString, $this->paypalTokenFormatter->formatPaymentToken($this->paymentTokenMock)); + } + + public function testFormatPaymentTokenWithUnknownCardType() + { + $this->paymentTokenMock->expects($this->once()) + ->method('getTokenDetails') + ->willReturn(json_encode($this->tokenDetails)); + + $formattedString = sprintf( + '%s: %s, %s: %s (%s: %s)', + __('Credit Card'), + $this->tokenDetails['type'], + __('ending'), + $this->tokenDetails['maskedCC'], + __('expires'), + $this->tokenDetails['expirationDate'] + ); + + self::assertEquals($formattedString, $this->paypalTokenFormatter->formatPaymentToken($this->paymentTokenMock)); + } + + public function testFormatPaymentTokenWithWrongData() + { + unset($this->tokenDetails['type']); + + $this->paymentTokenMock->expects($this->once()) + ->method('getTokenDetails') + ->willReturn(json_encode($this->tokenDetails)); + self::expectException('\InvalidArgumentException'); + + $this->paypalTokenFormatter->formatPaymentToken($this->paymentTokenMock); + } +} From 51456f73b359fb519ece91c17449c50aa085dd23 Mon Sep 17 00:00:00 2001 From: Claudio Zambon <claudio.zambon@webformat.com> Date: Sun, 27 May 2018 11:47:33 +0200 Subject: [PATCH 0867/1171] Convert to string $option->getValue, in order to be compared with other saved options in previous cart items --- .../ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php b/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php index 4450bbd75e574..85493b81bc6d4 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php +++ b/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php @@ -69,7 +69,7 @@ public function convertToBuyRequest(CartItemInterface $cartItem) if (is_array($options)) { $requestData = []; foreach ($options as $option) { - $requestData['super_attribute'][$option->getOptionId()] = $option->getOptionValue(); + $requestData['super_attribute'][$option->getOptionId()] = (string) $option->getOptionValue(); } return $this->objectFactory->create($requestData); } From 307923447c5669fec0e13860227ea56daac17000 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Thu, 9 Aug 2018 13:37:05 -0500 Subject: [PATCH 0868/1171] MQE-1172: Bump MFTF version and deliver Magento branches - MFTF version bump to use MQE-1181 (release candidate branch) - RoboFile returning exit code correctly --- composer.json | 2 +- composer.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 209ccacaedb8c..e22cb325a4d40 100644 --- a/composer.json +++ b/composer.json @@ -87,7 +87,7 @@ } ], "require-dev": { - "magento/magento2-functional-testing-framework": "dev-develop", + "magento/magento2-functional-testing-framework": "dev-MQE-1181", "friendsofphp/php-cs-fixer": "~2.12.0", "lusitanian/oauth": "~0.8.10", "pdepend/pdepend": "2.5.2", diff --git a/composer.lock b/composer.lock index 1e5d6929e30c2..ab354c595259d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "144b16cfcd074bac2b151d14b9a61c0d", + "content-hash": "33d00be920e1a33f354757d8956be27c", "packages": [ { "name": "braintree/braintree_php", @@ -6183,11 +6183,11 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "dev-develop", + "version": "dev-MQE-1181", "source": { "type": "git", "url": "git@github.com:magento/magento2-functional-testing-framework.git", - "reference": "228291162439cb6fba11733114c9b2993a040df0" + "reference": "7a77f17db79ff5101a6fc431ec0615b323510002" }, "require": { "allure-framework/allure-codeception": "~1.2.6", @@ -6256,7 +6256,7 @@ "magento", "testing" ], - "time": "2018-08-06T18:28:29+00:00" + "time": "2018-08-09T15:55:57+00:00" }, { "name": "moontoast/math", From e328cdd3014d5286de886e7e33c33d8cbfb2ea68 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Thu, 9 Aug 2018 13:50:19 -0500 Subject: [PATCH 0869/1171] MQE-1176: Fix all deprecation warnings - Addressed code review feedback --- app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml index 3f8d87cc15db5..67213934a6c41 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml @@ -10,13 +10,13 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="ThemeTest"> <annotations> - <features value="Theme Test"/> + <features value="Theme"/> <stories value="Themes"/> <title value="Magento Rush theme should not be available in Themes grid"/> <description value="Magento Rush theme should not be available in Themes grid"/> <severity value="MAJOR"/> <testCaseId value="MAGETWO-91409"/> - <group value="theme"/> + <group value="Theme"/> </annotations> <after> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> From 1aeb0cbb512737acc6cf5f16663b94ce5d1c5d90 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Thu, 9 Aug 2018 14:48:35 -0500 Subject: [PATCH 0870/1171] MC-222: Admin should be able to set/edit all the basic product attributes when creating/editing a bundle product - Attempt to fix flakiness --- .../ActionGroup/SetBundleProductAttributesActionGroup.xml | 2 +- .../Mftf/Test/AdminBasicBundleProductAttributesTest.xml | 8 ++++---- .../Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml | 4 ++++ .../Catalog/Test/Mftf/Section/AdminProductFormSection.xml | 1 + 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/SetBundleProductAttributesActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/SetBundleProductAttributesActionGroup.xml index 01f8d0de4b706..50af5993af5bc 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/SetBundleProductAttributesActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/SetBundleProductAttributesActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="SetBundleProductAttributes"> <arguments> <argument name="attributeSet" defaultValue="Default" type="string"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml index 6b310d49ff23d..828c163bf81fa 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml @@ -93,7 +93,7 @@ <!--Create second attribute set for edit--> <actionGroup ref="CreateDefaultAttributeSet" stepKey="createSecondAttributeSet"> - <argument name="label" value="{{ProductAttributeFrontendLabel.label}}2"/> + <argument name="label" value="{{ProductAttributeFrontendLabelTwo.label}}"/> </actionGroup> <!--Filter catalog--> @@ -112,8 +112,8 @@ <!--Apply Attribute Set--> <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttrSet"/> - <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{ProductAttributeFrontendLabel.label}}2" stepKey="searchForAttrSet"/> - <click selector="{{AdminProductFormSection.attributeSetFilterResult}}" stepKey="selectAttrSet"/> + <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{ProductAttributeFrontendLabelTwo.label}}" stepKey="searchForAttrSet"/> + <click selector="{{AdminProductFormSection.attributeSetFilterResultByName(ProductAttributeFrontendLabelTwo.label)}}" stepKey="selectAttrSet"/> <!--Product name and SKU--> <fillField selector="{{AdminProductFormBundleSection.productName}}" userInput="{{BundleProduct.name2}}" stepKey="fillProductName"/> @@ -153,7 +153,7 @@ <seeElement selector="{{AdminProductFormBundleSection.enableDisableToggleOn}}" stepKey="seeToggleIsOn2"/> <!--Attribute Set--> - <seeOptionIsSelected selector="{{AdminProductFormSection.attributeSet}}" userInput="{{ProductAttributeFrontendLabel.label}}2" stepKey="seeAttributeSet2"/> + <seeOptionIsSelected selector="{{AdminProductFormSection.attributeSet}}" userInput="{{ProductAttributeFrontendLabelTwo.label}}" stepKey="seeAttributeSet2"/> <!--Product name and SKU--> <seeInField selector="{{AdminProductFormBundleSection.productName}}" userInput="{{BundleProduct.name2}}" stepKey="seeProductName2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml index 7a6a22abacb00..a46d40c62c76e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml @@ -12,4 +12,8 @@ <data key="store_id">0</data> <data key="label" unique="suffix">attribute</data> </entity> + <entity name="ProductAttributeFrontendLabelTwo" type="FrontendLabel"> + <data key="store_id">0</data> + <data key="label" unique="suffix">attributeTwo</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index c056a4975efde..e26720e3c0673 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -11,6 +11,7 @@ <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> + <element name="attributeSetFilterResultByName" type="text" selector="//label/span[text() = '{{var}}']" timeout="30" parameterized="true"/> <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> <element name="enableProductAttributeLabel" type="text" selector="//label[text()='Enable Product']"/> From 6bf82aef5d83b69f9bc63863a84998b296ffa3f7 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Thu, 9 Aug 2018 16:00:13 -0500 Subject: [PATCH 0871/1171] =?UTF-8?q?MAGETWO-90719=EF=BC=9AException=20is?= =?UTF-8?q?=20thrown=20when=20you=20re-order=20a=20product=20with=20custom?= =?UTF-8?q?=20options=20from=20Admin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Upgrade magento/zendframework1 to v.1.14.1 --- composer.json | 2 +- composer.lock | 68 +++++++++++++++++++++++++-------------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/composer.json b/composer.json index 2eba717464e68..b70d9d8d56582 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "elasticsearch/elasticsearch": "~2.0|~5.1", "magento/composer": "~1.4.0", "magento/magento-composer-installer": ">=0.1.11", - "magento/zendframework1": "~1.14.0", + "magento/zendframework1": "~1.14.1", "monolog/monolog": "^1.17", "oyejorge/less.php": "~1.7.0", "pelago/emogrifier": "^2.0.0", diff --git a/composer.lock b/composer.lock index 430b233d72fc6..c640f67fd31a0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "57ae7ad75c4d8d50eb40e4ffe0fd2740", + "content-hash": "e0e56e29331f2903481bf9f620941e27", "packages": [ { "name": "braintree/braintree_php", @@ -201,16 +201,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "d2c0a83b7533d6912e8d516756ebd34f893e9169" + "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/d2c0a83b7533d6912e8d516756ebd34f893e9169", - "reference": "d2c0a83b7533d6912e8d516756ebd34f893e9169", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/46afded9720f40b9dc63542af4e3e43a1177acb0", + "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0", "shasum": "" }, "require": { @@ -253,20 +253,20 @@ "ssl", "tls" ], - "time": "2018-03-29T19:57:20+00:00" + "time": "2018-08-08T08:57:40+00:00" }, { "name": "composer/composer", - "version": "1.7.0", + "version": "1.7.1", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "39edb2f375679a4eba19e69e9c9491e302976983" + "reference": "5d9311d4555787c8a57fea15f82471499aedf712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/39edb2f375679a4eba19e69e9c9491e302976983", - "reference": "39edb2f375679a4eba19e69e9c9491e302976983", + "url": "https://api.github.com/repos/composer/composer/zipball/5d9311d4555787c8a57fea15f82471499aedf712", + "reference": "5d9311d4555787c8a57fea15f82471499aedf712", "shasum": "" }, "require": { @@ -333,7 +333,7 @@ "dependency", "package" ], - "time": "2018-08-03T13:39:07+00:00" + "time": "2018-08-07T07:39:23+00:00" }, { "name": "composer/semver", @@ -872,16 +872,16 @@ }, { "name": "magento/zendframework1", - "version": "1.14.0", + "version": "1.14.1", "source": { "type": "git", "url": "https://github.com/magento/zf1.git", - "reference": "68522e5768edc8e829d1f64b620a3de3753f1141" + "reference": "4df018254c70b5b998b00a8cb1a30760f831ff0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/zf1/zipball/68522e5768edc8e829d1f64b620a3de3753f1141", - "reference": "68522e5768edc8e829d1f64b620a3de3753f1141", + "url": "https://api.github.com/repos/magento/zf1/zipball/4df018254c70b5b998b00a8cb1a30760f831ff0d", + "reference": "4df018254c70b5b998b00a8cb1a30760f831ff0d", "shasum": "" }, "require": { @@ -915,7 +915,7 @@ "ZF1", "framework" ], - "time": "2018-04-06T18:49:03+00:00" + "time": "2018-08-09T15:03:40+00:00" }, { "name": "monolog/monolog", @@ -4777,16 +4777,16 @@ }, { "name": "consolidation/config", - "version": "1.0.11", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/consolidation/config.git", - "reference": "ede41d946078e97e7a9513aadc3352f1c26817af" + "reference": "c9fc25e9088a708637e18a256321addc0670e578" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/ede41d946078e97e7a9513aadc3352f1c26817af", - "reference": "ede41d946078e97e7a9513aadc3352f1c26817af", + "url": "https://api.github.com/repos/consolidation/config/zipball/c9fc25e9088a708637e18a256321addc0670e578", + "reference": "c9fc25e9088a708637e18a256321addc0670e578", "shasum": "" }, "require": { @@ -4796,7 +4796,7 @@ }, "require-dev": { "g1a/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4", + "phpunit/phpunit": "^5", "satooshi/php-coveralls": "^1.0", "squizlabs/php_codesniffer": "2.*", "symfony/console": "^2.5|^3|^4", @@ -4827,7 +4827,7 @@ } ], "description": "Provide configuration services for a commandline tool.", - "time": "2018-05-27T01:17:02+00:00" + "time": "2018-08-07T22:57:00+00:00" }, { "name": "consolidation/log", @@ -7218,16 +7218,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.10", + "version": "6.5.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "5744955af9c0a2de74a5eb5287c50bf025100d39" + "reference": "7bab54cb366076023bbf457a2a0d513332cd40f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5744955af9c0a2de74a5eb5287c50bf025100d39", - "reference": "5744955af9c0a2de74a5eb5287c50bf025100d39", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7bab54cb366076023bbf457a2a0d513332cd40f2", + "reference": "7bab54cb366076023bbf457a2a0d513332cd40f2", "shasum": "" }, "require": { @@ -7245,7 +7245,7 @@ "phpunit/php-file-iterator": "^1.4.3", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.8", + "phpunit/phpunit-mock-objects": "^5.0.9", "sebastian/comparator": "^2.1", "sebastian/diff": "^2.0", "sebastian/environment": "^3.1", @@ -7298,20 +7298,20 @@ "testing", "xunit" ], - "time": "2018-08-03T05:27:14+00:00" + "time": "2018-08-07T07:05:35+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "5.0.8", + "version": "5.0.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "6f9a3c8bf34188a2b53ce2ae7a126089c53e0a9f" + "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/6f9a3c8bf34188a2b53ce2ae7a126089c53e0a9f", - "reference": "6f9a3c8bf34188a2b53ce2ae7a126089c53e0a9f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f", + "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f", "shasum": "" }, "require": { @@ -7324,7 +7324,7 @@ "phpunit/phpunit": "<6.0" }, "require-dev": { - "phpunit/phpunit": "^6.5" + "phpunit/phpunit": "^6.5.11" }, "suggest": { "ext-soap": "*" @@ -7357,7 +7357,7 @@ "mock", "xunit" ], - "time": "2018-07-13T03:27:23+00:00" + "time": "2018-08-09T05:50:03+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", From 835e48a77363ac236de82550624a4de66d19b160 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Thu, 9 Aug 2018 17:03:55 -0500 Subject: [PATCH 0872/1171] =?UTF-8?q?MAGETWO-90719=EF=BC=9AException=20is?= =?UTF-8?q?=20thrown=20when=20you=20re-order=20a=20product=20with=20custom?= =?UTF-8?q?=20options=20from=20Admin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix static test failure --- .../Model/Product/Option/Type/File/ValidatorInfoTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfoTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfoTest.php index 7c340c37ba9a9..d4214a32f31dc 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfoTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfoTest.php @@ -30,6 +30,9 @@ class ValidatorInfoTest extends \PHPUnit\Framework\TestCase */ protected $validateFactoryMock; + /** + * {@inheritdoc} + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); From 1632ba906185ccce346ddb6367adb6649f3aad7b Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Thu, 9 Aug 2018 19:46:54 -0500 Subject: [PATCH 0873/1171] MAGETWO-90539: Fixed Tax doesn't show in any total or tax field of cart during checkout. --- .../view/frontend/web/js/model/new-customer-address.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js b/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js index 3bc5911946fda..a880bd423ab2e 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js @@ -17,17 +17,23 @@ define([ */ return function (addressData) { var identifier = Date.now(), + countryId = addressData['country_id'] || addressData.countryId || window.checkoutConfig.defaultCountryId, regionId; if (addressData.region && addressData.region['region_id']) { regionId = addressData.region['region_id']; - } else if (addressData['country_id'] && addressData['country_id'] == window.checkoutConfig.defaultCountryId) { //eslint-disable-line + } else if ( + /* eslint-disable */ + addressData['country_id'] && addressData['country_id'] == window.checkoutConfig.defaultCountryId || + !addressData['country_id'] && countryId == window.checkoutConfig.defaultCountryId + /* eslint-enable */ + ) { regionId = window.checkoutConfig.defaultRegionId || undefined; } return { email: addressData.email, - countryId: addressData['country_id'] || addressData.countryId || window.checkoutConfig.defaultCountryId, + countryId: countryId, regionId: regionId || addressData.regionId, regionCode: addressData.region ? addressData.region['region_code'] : null, region: addressData.region ? addressData.region.region : null, From 2d9b35a0480d23fee669aa88a8630ba5eca4a44b Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Fri, 10 Aug 2018 09:31:59 +0300 Subject: [PATCH 0874/1171] MAGETWO-93961: [2.3] Product URL rewrites get deleted in multi store views --- .../Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php index 18360dedf0693..1268e864194bd 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php @@ -230,9 +230,9 @@ private function getCategoryProductsUrlRewrites( * * @param MergeDataProvider $mergeDataProvider * @param Category $category - * @param Product $product * @param int $storeId * @param $saveRewriteHistory + * @return void */ private function generateChangedProductUrls( MergeDataProvider $mergeDataProvider, From e3a9ac2ea24ba7c2ab83ee4d1253397ce7ebf61b Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Thu, 9 Aug 2018 10:57:15 +0400 Subject: [PATCH 0875/1171] MAGETWO-91701: Newsletter subscription is not correctly updated when user is registered on 2 stores - Add automated test --- ...bscribedNewsletterDisplayedActionGroup.xml | 162 ++++++++++++++++++ ...erifySubscribedNewsletterDisplayedData.xml | 23 +++ ...fySubscribedNewsLetterDisplayedSection.xml | 80 +++++++++ ...erifySubscribedNewsletterDisplayedTest.xml | 65 +++++++ 4 files changed, 330 insertions(+) create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Data/VerifySubscribedNewsletterDisplayedData.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml new file mode 100644 index 0000000000000..1f61e1dab9813 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml @@ -0,0 +1,162 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + + <!--Go To Stores/All Stores--> + <actionGroup name="GoToAllStores"> + <click stepKey="clickStoreItem" selector="{{Dashboard.storesItem}}"/> + <waitForElementVisible selector="{{Dashboard.storesAllStoresItem}}" stepKey="waitForAllStoresItemBecomeAvailable"/> + <click stepKey="clickAllStoreItem" selector="{{Dashboard.storesAllStoresItem}}"/> + <waitForPageLoad stepKey="waitForStoresPageLoaded"/> + </actionGroup> + + <!--Create new website--> + <actionGroup name="AdminCreateWebsite"> + <!--Fill required fields--> + <click selector="{{AdminNewWebsiteSection.addWebSite}}" stepKey="clickOnCreateWebsiteButton"/> + <waitForPageLoad stepKey="waitFormToBeOpened"/> + <fillField selector="{{AdminNewWebsiteSection.name}}" userInput="{{AdminTestData.testData}}" stepKey="enterWebsiteName" /> + <fillField selector="{{AdminNewWebsiteSection.code}}" userInput="{{AdminTestData.testData}}" stepKey="enterWebsiteCode" /> + <click selector="{{AdminNewWebsiteActionsSection.saveWebsite}}" stepKey="clickSaveWebsite" /> + <waitForElementVisible selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="waitForStoreGridToReload"/> + <see userInput="You saved the website." stepKey="seeSavedMessage" /> + </actionGroup> + + <!--Create new store--> + <actionGroup name="AdminCreateNewStore"> + <!--Fill required fields--> + <click selector="{{AdminNewStoreGroupSection.create}}" stepKey="clickOnCreateStore"/> + <waitForPageLoad stepKey="waitFormToBeOpened"/> + <selectOption selector="{{AdminNewStoreGroupSection.storeGrpWebsiteDropdown}}" userInput="{{AdminTestData.testData}}" stepKey="selectWebsite" /> + <fillField selector="{{AdminNewStoreGroupSection.storeGrpNameTextField}}" userInput="{{AdminTestData.testData}}" stepKey="enterStoreGroupName" /> + <fillField selector="{{AdminNewStoreGroupSection.storeGrpCodeTextField}}" userInput="{{AdminTestData.testData}}" stepKey="enterStoreGroupCode" /> + <selectOption selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" userInput="Default Category" stepKey="chooseRootCategory" /> + <click selector="{{AdminStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreGroup" /> + <waitForElementVisible selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="waitForStoreGridReload"/> + <see userInput="You saved the store." stepKey="seeSavedMessage" /> + </actionGroup> + + <!--Create store view--> + <actionGroup name="AdminCreateStoreView"> + <!--Fill required fields--> + <click selector="{{AdminNewStoreSection.create}}" stepKey="clickOnCreateStoreView"/> + <waitForPageLoad stepKey="waitFormToBeOpened"/> + <selectOption selector="{{AdminNewStoreSection.storeGrpDropdown}}" userInput="{{AdminTestData.testData}}" stepKey="selectStore" /> + <fillField selector="{{AdminNewStoreSection.storeNameTextField}}" userInput="{{AdminTestData.testData}}" stepKey="enterStoreViewName" /> + <fillField selector="{{AdminNewStoreSection.storeCodeTextField}}" userInput="{{AdminTestData.testData}}" stepKey="enterStoreViewCode" /> + <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="Enabled" stepKey="setStatus" /> + <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView" /> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal" /> + <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning" /> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal" /> + <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReolad"/> + <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReload"/> + <see userInput="You saved the store view." stepKey="seeSavedMessage" /> + </actionGroup> + + <!--Go to Stores -> Configuration -> Web.--> + <actionGroup name="GoToStoresConfigurationWeb"> + <click stepKey="againClickStoreItem" selector="{{Dashboard.storesItem}}"/> + <waitForElementVisible selector="{{Dashboard.storesConfigurationItem}}" stepKey="waitForAllStoreItemExtends"/> + <click stepKey="clickConfigurationItem" selector="{{Dashboard.storesConfigurationItem}}"/> + <waitForPageLoad stepKey="waitForStoresConfigurationPageLoaded"/> + <click stepKey="clickWebItem" selector="{{AdminStoresConfigurationSection.webItem}}"/> + <waitForPageLoad stepKey="waitForStoresConfigurationWebPageLoaded"/> + </actionGroup> + + <!--Select Yes in Add Store Code to Urls field.--> + <actionGroup name="SelectYesInAddStoreCodeToUrlsField"> + <click stepKey="clickOpenUrlOptions" selector="{{AdminStoresConfigurationSection.openUrlOptions}}"/> + <click stepKey="clickUseSystemValueCheckbox" selector="{{AdminStoresConfigurationSection.useSystemValueCheckbox}}"/> + <selectOption selector="{{AdminStoresConfigurationSection.addStoreCodeToUrlsDropDown}}" userInput="Yes" stepKey="setAddStoreCodeToUrlsYes" /> + <click stepKey="clickSaveConfigButton" selector="{{AdminStoresConfigurationSection.saveConfigButton}}"/> + <waitForPageLoad stepKey="waitForSaveConfig"/> + <see stepKey="seeSavedConfigurationMessage" userInput="You saved the configuration."/> + </actionGroup> + + <!--Create an Account. Check Sign Up for Newsletter checkbox --> + <actionGroup name="StorefrontCreateNewAccount"> + <fillField selector="{{StorefrontCustomerCreateFormSection.firstNameField}}" userInput="{{CreateUserData.firstName}}" stepKey="enterFirstName" /> + <fillField selector="{{StorefrontCustomerCreateFormSection.lastNameField}}" userInput="{{CreateUserData.lastName}}" stepKey="enterLastName" /> + <click selector="{{StorefrontCustomerCreateFormSection.signUpForNewsletter}}" stepKey="selectSignUpForNewsletterCheckbox"/> + <fillField selector="{{StorefrontCustomerCreateFormSection.emailField}}" userInput="{{CreateUserData.firstName}}@magento.com" stepKey="enterEmail" /> + <fillField selector="{{StorefrontCustomerCreateFormSection.passwordField}}" userInput="{{CreateUserData.password}}" stepKey="enterPassword" /> + <fillField selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}" userInput="{{CreateUserData.password}}" stepKey="confirmPassword" /> + <click stepKey="clickCreateAccountButton" selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}"/> + <waitForPageLoad stepKey="waitForMyAccountPageLoad"/> + </actionGroup> + + <!-- Sign out --> + <actionGroup name="StorefrontSignOut"> + <click stepKey="clickCustomerNameItem" selector="{{CustomerMyAccountPage.customerName}}"/> + <click stepKey="clickSignOutButton" selector="{{CustomerMyAccountPage.customerSignOut}}"/> + <waitForPageLoad stepKey="waitForSignOut"/> + </actionGroup> + + <!--Create an Account. Uncheck Sign Up for Newsletter checkbox --> + <actionGroup name="StorefrontCreateNewAccountNewsletterUnchecked"> + <click stepKey="clickCreateNewAccountButton" selector="{{CustomerMyAccountPage.createNewAccount}}"/> + <waitForPageLoad stepKey="waitForCreateNewAccountPageLoaded"/> + <fillField selector="{{StorefrontCustomerCreateFormSection.firstNameField}}" userInput="{{CreateUserData.firstName}}" stepKey="enterFirstName" /> + <fillField selector="{{StorefrontCustomerCreateFormSection.lastNameField}}" userInput="{{CreateUserData.lastName}}" stepKey="enterLastName" /> + <fillField selector="{{StorefrontCustomerCreateFormSection.emailField}}" userInput="{{CreateUserData.firstName}}@magento.com" stepKey="enterEmail" /> + <fillField selector="{{StorefrontCustomerCreateFormSection.passwordField}}" userInput="{{CreateUserData.password}}" stepKey="enterPassword" /> + <fillField selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}" userInput="{{CreateUserData.password}}" stepKey="confirmPassword" /> + <click stepKey="clickCreateAccountButton" selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}"/> + <waitForPageLoad stepKey="waitForMyAccountPageLoad"/> + <see userInput="Thank you for registering with" stepKey="seeValidRegistrationMessage"/> + </actionGroup> + + <!--Delete created Website --> + <actionGroup name="AdminDeleteWebsite"> + <fillField stepKey="fillSearchWebsiteField" selector="{{AdminStoresGridSection.websiteFilterTextField}}" userInput="{{AdminTestData.testData}}"/> + <click stepKey="clickSearchButton" selector="{{AdminStoresGridSection.searchButton}}"/> + <see stepKey="verifyThatCorrectWebsiteFound" selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" userInput="{{AdminTestData.testData}}"/> + <click stepKey="clickEditExistingStoreRow" selector="{{AdminStoresGridSection.websiteNameInFirstRow}}"/> + <waitForPageLoad stepKey="waitForStoreToLoad"/> + <click stepKey="clickDeleteWebsiteButtonOnEditWebsitePage" selector="{{AdminStoresMainActionsSection.deleteButton}}"/> + <selectOption stepKey="setCreateDbBackupToNo" selector="{{AdminStoresDeleteStoreGroupSection.createDbBackup}}" userInput="No"/> + <click stepKey="clickDeleteWebsiteButton" selector="{{AdminStoresDeleteStoreGroupSection.deleteStoreGroupButton}}"/> + <waitForElementVisible stepKey="waitForStoreGridToReload" selector="{{AdminStoresGridSection.websiteFilterTextField}}"/> + <see stepKey="seeSavedMessage" userInput="You deleted the website."/> + </actionGroup> + + <!--Set Default config --> + <actionGroup name="AdminSetDefaultConfig"> + <selectOption selector="{{AdminStoresConfigurationSection.addStoreCodeToUrlsDropDown}}" userInput="No" stepKey="setAddStoreCodeToUrlsNo" /> + <click stepKey="disableUseSystemValueCheckbox" selector="{{AdminStoresConfigurationSection.useSystemValueCheckbox}}"/> + <click stepKey="clickDefaultConfigSaveButton" selector="{{AdminStoresConfigurationSection.saveConfigButton}}"/> + <waitForPageLoad stepKey="waitForSaveConfig"/> + <see stepKey="saveDefaultConfig" userInput="You saved the configuration."/> + <click stepKey="clickOpenUrlOptions" selector="{{AdminStoresConfigurationSection.openUrlOptions}}"/> + </actionGroup> + + <!--Delete created Customer --> + <actionGroup name="AdminDeleteCreatedCustomer"> + <click stepKey="clickCustomerItem" selector="{{Dashboard.customer}}"/> + <wait stepKey="WaitForCustomerViewOpened" time="2"/> + <click stepKey="clickCustomerAllCustomerItem" selector="{{Dashboard.customerAllCustomer}}"/> + <waitForPageLoad stepKey="WaitForCustomerPageIsLoaded"/> + <fillField stepKey="searchToKeyword" selector="{{AdminCustomerAccountInformationSection.searchToKeyword}}" userInput="{{CreateUserData.firstName}}"/> + <click stepKey="clickSearchButton" selector="{{AdminCustomerAccountInformationSection.searchButton}}"/> + <waitForElementVisible stepKey="waitForFiltering" selector="{{AdminCustomerAccountInformationSection.selectCustomer}}"/> + <click selector="{{AdminCustomerAccountInformationSection.selectCustomer}}" stepKey="ClickOnCustomer"/> + <click selector="{{AdminCustomerAccountInformationSection.actions}}" stepKey="ClickOnActions"/> + <waitForElementVisible selector="{{AdminCustomerAccountInformationSection.delete}}" stepKey="waitForDeleteButtonAppeared"/> + <click selector="{{AdminCustomerAccountInformationSection.delete}}" stepKey="ClickOnDelete"/> + <waitForElementVisible selector="{{AdminCustomerAccountInformationSection.confirm}}" stepKey="waitForConfirmButtonAppeared"/> + <click selector="{{AdminCustomerAccountInformationSection.confirm}}" stepKey="ClickToConfirm"/> + <waitForPageLoad stepKey="waitClickToConfirmButton"/> + <see stepKey="seeRecordsWereDeletedMessage" userInput="A total of 2 record(s) were deleted."/> + <click stepKey="clickClearAllFilterButton" selector="{{AdminCustomerAccountInformationSection.clearAll}}"/> + <!-- We need this wait to make sure that Active filters is clear (waitForElementNotVisible tag doesn't wait until clearing filters)--> + <wait stepKey="waitToClearAllFilters" time="2"/> + </actionGroup> + +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Data/VerifySubscribedNewsletterDisplayedData.xml b/app/code/Magento/Newsletter/Test/Mftf/Data/VerifySubscribedNewsletterDisplayedData.xml new file mode 100644 index 0000000000000..4082626cef5a7 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Data/VerifySubscribedNewsletterDisplayedData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + + <!-- For this Test, all data is the same. --> + <entity name="AdminTestData" type="data"> + <data key="testData" unique="suffix">store2</data> + </entity> + + <entity name="CreateUserData" type="user"> + <data key="firstName" unique="suffix">John</data> + <data key="lastName">Smith</data> + <data key="password">Admin@123</data> + </entity> + +</entities> \ No newline at end of file diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml new file mode 100644 index 0000000000000..1af5c142e2cd7 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + + <section name="Dashboard"> + <element name="storesItem" type="button" selector="#menu-magento-backend-stores"/> + <element name="storesAllStoresItem" type="button" selector="//*[@data-ui-id='menu-magento-backend-system-store']/a/span"/> + <element name="storesConfigurationItem" type="button" selector="//*[@data-ui-id='menu-magento-config-system-config']/a/span"/> + <element name="customerAllCustomer" type="button" selector="//*[@data-ui-id='menu-magento-customer-customer-manage']"/> + <element name="customer" type="button" selector="#menu-magento-customer-customer"/> + </section> + + <section name="AdminNewWebsiteSection"> + <element name="stores" type="button" selector="#menu-magento-backend-stores"/> + <element name="allStores" type="button" selector="//span[contains(text(), 'All Stores')]"/> + <element name="addWebSite" type="button" selector="#add"/> + <element name="name" type="input" selector="#website_name"/> + <element name="code" type="input" selector="#website_code"/> + </section> + + <section name="AdminNewStoreGroupSection"> + <element name="create" type="button" selector="#add_group"/> + <element name="storeGrpWebsiteDropdown" type="select" selector="#group_website_id"/> + <element name="storeGrpNameTextField" type="input" selector="#group_name"/> + <element name="storeGrpCodeTextField" type="input" selector="#group_code"/> + <element name="storeRootCategoryDropdown" type="select" selector="#group_root_category_id"/> + </section> + + <section name="AdminNewStoreSection"> + <element name="create" type="button" selector="#add_store"/> + <element name="storeNameTextField" type="input" selector="#store_name"/> + <element name="storeCodeTextField" type="input" selector="#store_code"/> + <element name="statusDropdown" type="select" selector="#store_is_active"/> + <element name="storeGrpDropdown" type="select" selector="#store_group_id"/> + <element name="sortOrderTextField" type="input" selector="#store_sort_order"/> + <element name="acceptNewStoreViewCreation" type="button" selector=".action-primary.action-accept" /> + </section> + + <section name="AdminStoresConfigurationSection"> + <element name="openUrlOptions" type="button" selector="#web_url-head"/> + <element name="webItem" type="button" selector="//*[@id='system_config_tabs']/div[1]//span[contains(text(), 'Web')]"/> + <element name="useSystemValueCheckbox" type="checkbox" selector="#web_url_use_store_inherit"/> + <element name="addStoreCodeToUrlsDropDown" type="select" selector="#web_url_use_store"/> + <element name="saveConfigButton" type="button" selector="#save"/> + </section> + + <section name="StorefrontCustomerCreateFormSection"> + <element name="firstNameField" type="input" selector="#firstname"/> + <element name="lastNameField" type="input" selector="#lastname"/> + <element name="emailField" type="input" selector="#email_address"/> + <element name="passwordField" type="input" selector="#password"/> + <element name="confirmPasswordField" type="input" selector="#password-confirmation"/> + <element name="createAccountButton" type="button" selector="button.action.submit.primary" timeout="30"/> + <element name="signUpForNewsletter" type="checkbox" selector="//span[contains(text(), 'Sign Up for Newsletter')]"/> + </section> + + <section name="CustomerMyAccountPage"> + <element name="createNewAccount" type="button" selector="//*[@class='page-header']//a[contains(text(),'Create an Account')]"/> + <element name="customerName" type="button" selector="//*[@class='customer-welcome']//span[@class='customer-name']/button[@data-action='customer-menu-toggle']"/> + <element name="customerSignOut" type="button" selector="//*[@class='customer-menu']//*[normalize-space()='Sign Out']"/> + </section> + + <section name="AdminCustomerAccountInformationSection"> + <element name="searchToKeyword" type="input" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/input"/> + <element name="searchButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/button"/> + <element name="selectCustomer" type="checkbox" selector="//*[@class='admin__data-grid-wrap' and @data-role='grid-wrapper']//*[@class='data-grid-multicheck-cell']/div/label"/> + <element name="actions" type="button" selector="//div[@class='admin__data-grid-header']//div[@class='col-xs-2']//span[text()='Actions']"/> + <element name="delete" type="button" selector="//div[@class='col-xs-2']//span[text()='Delete']"/> + <element name="confirm" type="button" selector=".action-primary.action-accept"/> + <element name="clearAll" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[contains(text(), 'Clear all')]"/> + </section> + +</sections> \ No newline at end of file diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml new file mode 100644 index 0000000000000..78c4e2085cc86 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="VerifySubscribedNewsletterDisplayedTest"> + <annotations> + <features value="Newsletter"/> + <stories value="MAGETWO-91701: Newsletter subscription is not correctly updated when user is registered on 2 stores"/> + <group value="Newsletter"/> + <title value="Newsletter subscription when user is registered on 2 stores"/> + <description value="Newsletter subscription when user is registered on 2 stores"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-93836"/> + </annotations> + + <before> + <!--Log in to Magento as admin.--> + <actionGroup ref="LoginActionGroup" stepKey="login"/> + <!--Go to Stores.--> + <actionGroup ref="GoToAllStores" stepKey="goToAllStores"/> + <!--Create Website.--> + <actionGroup ref="AdminCreateWebsite" stepKey="adminCreateWebsite"/> + <!--Create Store.--> + <actionGroup ref="AdminCreateNewStore" stepKey="adminCreateNewStore"/> + <!--Create Store View.--> + <actionGroup ref="AdminCreateStoreView" stepKey="adminCreateStoreView"/> + <!--Go to Stores -> Configuration -> Web.--> + <actionGroup ref="GoToStoresConfigurationWeb" stepKey="goToStoresConfigurationWeb"/> + <actionGroup ref="SelectYesInAddStoreCodeToUrlsField" stepKey="selectYesInAddStoreCodeToUrlsField"/> + </before> + + <!--Go to store front (default) and click Create an Account.--> + <amOnPage url="{{StorefrontCustomerCreatePage.url}}" stepKey="navigateToCustomers"/> + <actionGroup ref="StorefrontCreateNewAccount" stepKey="createNewAccount"/> + + <!--Sign Out--> + <actionGroup ref="StorefrontSignOut" stepKey="storefrontSignOut"/> + + <!--Change 'default' to 'url_name' in url--> + <amOnPage url="{{AdminTestData.testData}}" stepKey="goToCreatedWebPage"/> + <waitForPageLoad stepKey="waitForCreatedWebPageLoaded"/> + + <!--Create new Account with the same email address. (unchecked Sign Up for Newsletter checkbox)--> + <actionGroup ref="StorefrontCreateNewAccountNewsletterUnchecked" stepKey="createNewAccountNewsletterUnchecked"/> + <dontSee stepKey="verifySubscribedNewsletters" userInput="You aren't subscribed to our newsletter displayed"/> + + <after> + <!--Delete created data and set Default Configuration--> + <amOnPage url="admin" stepKey="goToAdminPage"/> + <waitForPageLoad stepKey="waitForAdminPageLoaded"/> + <actionGroup ref="AdminDeleteCreatedCustomer" stepKey="adminDeleteCustomer"/> + <actionGroup ref="GoToAllStores" stepKey="goToAllStores"/> + <actionGroup ref="AdminDeleteWebsite" stepKey="adminDeleteWebsite"/> + <actionGroup ref="GoToStoresConfigurationWeb" stepKey="goToStoresConfigurationWeb"/> + <actionGroup ref="AdminSetDefaultConfig" stepKey="adminSetDefaultConfig"/> + </after> + + </test> +</tests> + From 2026ce8e3afee60226a9c3f49a87a78dd435d419 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Fri, 10 Aug 2018 11:42:34 +0300 Subject: [PATCH 0876/1171] MAGETWO-91808: Categories of the Main menu in the different Store View not updated when varnish enabled --- .../Customer/Test/Unit/Model/SessionTest.php | 20 ++++++++++- app/code/Magento/Store/Model/Store.php | 3 +- .../Store/Test/Unit/Block/SwitcherTest.php | 7 ++++ .../Controller/Store/SwitchActionTest.php | 6 ++++ .../Unit/Model/Plugin/StoreCookieTest.php | 15 +++++++++ .../Store/Test/Unit/Model/StoreTest.php | 33 +++++++++++++++++++ .../Test/Unit/Controller/RouterTest.php | 24 ++++++++++++++ .../Controller/Product/CompareTest.php | 33 +++++++++++++++++++ .../Customer/Controller/AccountTest.php | 3 ++ .../UrlRewrite/Controller/UrlRewriteTest.php | 3 ++ 10 files changed, 145 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php b/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php index 858b7ec24324a..7efc61af800d3 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php @@ -52,6 +52,9 @@ class SessionTest extends \PHPUnit\Framework\TestCase */ protected $_model; + /** + * @return void + */ protected function setUp() { $this->_storageMock = $this->createPartialMock( @@ -82,6 +85,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testSetCustomerAsLoggedIn() { $customer = $this->createMock(\Magento\Customer\Model\Customer::class); @@ -102,6 +108,9 @@ public function testSetCustomerAsLoggedIn() $this->assertSame($customer, $this->_model->getCustomer()); } + /** + * @return void + */ public function testSetCustomerDataAsLoggedIn() { $customer = $this->createMock(\Magento\Customer\Model\Customer::class); @@ -126,6 +135,9 @@ public function testSetCustomerDataAsLoggedIn() $this->assertSame($customer, $this->_model->getCustomer()); } + /** + * @return void + */ public function testAuthenticate() { $urlMock = $this->createMock(\Magento\Framework\Url::class); @@ -150,6 +162,9 @@ public function testAuthenticate() $this->assertFalse($this->_model->authenticate()); } + /** + * @return void + */ public function testLoginById() { $customerId = 1; @@ -165,7 +180,7 @@ public function testLoginById() } /** - * @param $customerId + * @param int $customerId * @return \PHPUnit_Framework_MockObject_MockObject */ protected function prepareLoginDataMock($customerId) @@ -242,6 +257,9 @@ public function getIsLoggedInDataProvider() ]; } + /** + * @return void + */ public function testSetCustomerRemovesFlagThatShowsIfCustomerIsEmulated() { $customerMock = $this->createMock(\Magento\Customer\Model\Customer::class); diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index cb624f09be091..af25957257421 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -1379,7 +1379,8 @@ public function getExtensionAttributes() } /** - * {@inheritdoc} + * @param \Magento\Store\Api\Data\StoreExtensionInterface $extensionAttributes + * @return $this */ public function setExtensionAttributes( \Magento\Store\Api\Data\StoreExtensionInterface $extensionAttributes diff --git a/app/code/Magento/Store/Test/Unit/Block/SwitcherTest.php b/app/code/Magento/Store/Test/Unit/Block/SwitcherTest.php index 57cb63e7c2744..aca3525a4400e 100644 --- a/app/code/Magento/Store/Test/Unit/Block/SwitcherTest.php +++ b/app/code/Magento/Store/Test/Unit/Block/SwitcherTest.php @@ -28,6 +28,9 @@ class SwitcherTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Store\Api\Data\StoreInterface|\PHPUnit_Framework_MockObject_MockObject */ private $store; + /** + * @return void + */ protected function setUp() { $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class)->getMock(); @@ -48,6 +51,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testGetTargetStorePostData() { $store = $this->getMockBuilder(\Magento\Store\Model\Store::class) @@ -79,6 +85,7 @@ public function testGetTargetStorePostData() /** * @dataProvider isStoreInUrlDataProvider + * @param bool $isUseStoreInUrl */ public function testIsStoreInUrl($isUseStoreInUrl) { diff --git a/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchActionTest.php b/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchActionTest.php index fa7c696bf53cd..0d337b91c192a 100644 --- a/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchActionTest.php +++ b/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchActionTest.php @@ -63,6 +63,9 @@ class SwitchActionTest extends \PHPUnit\Framework\TestCase /** @var StoreSwitcherInterface|\PHPUnit_Framework_MockObject_MockObject */ private $storeSwitcher; + /** + * @return void + */ protected function setUp() { $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class)->getMock(); @@ -98,6 +101,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testExecute() { $storeToSwitchToCode = 'sv2'; diff --git a/app/code/Magento/Store/Test/Unit/Model/Plugin/StoreCookieTest.php b/app/code/Magento/Store/Test/Unit/Model/Plugin/StoreCookieTest.php index 7aa992064f794..0454c0096a6ab 100644 --- a/app/code/Magento/Store/Test/Unit/Model/Plugin/StoreCookieTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/Plugin/StoreCookieTest.php @@ -110,6 +110,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testBeforeDispatchNoSuchEntity() { $storeCode = 'store'; @@ -129,6 +132,9 @@ public function testBeforeDispatchNoSuchEntity() $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock); } + /** + * @return void + */ public function testBeforeDispatchStoreIsInactive() { $storeCode = 'store'; @@ -148,6 +154,9 @@ public function testBeforeDispatchStoreIsInactive() $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock); } + /** + * @return void + */ public function testBeforeDispatchInvalidArgument() { $storeCode = 'store'; @@ -167,6 +176,9 @@ public function testBeforeDispatchInvalidArgument() $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock); } + /** + * @return void + */ public function testBeforeDispatchNoStoreCookie() { $storeCode = null; @@ -185,6 +197,9 @@ public function testBeforeDispatchNoStoreCookie() $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock); } + /** + * @return void + */ public function testBeforeDispatchWithStoreRequestParam() { $storeCode = 'store'; diff --git a/app/code/Magento/Store/Test/Unit/Model/StoreTest.php b/app/code/Magento/Store/Test/Unit/Model/StoreTest.php index b00194db4826e..f98cf5d892e07 100644 --- a/app/code/Magento/Store/Test/Unit/Model/StoreTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/StoreTest.php @@ -43,6 +43,9 @@ class StoreTest extends \PHPUnit\Framework\TestCase */ private $urlModifierMock; + /** + * @return void + */ protected function setUp() { $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -105,6 +108,9 @@ public function loadDataProvider() ]; } + /** + * @return void + */ public function testSetWebsite() { $website = $this->createPartialMock(\Magento\Store\Model\Website::class, ['getId', '__wakeup']); @@ -115,6 +121,9 @@ public function testSetWebsite() $this->assertEquals(2, $model->getWebsiteId()); } + /** + * @return void + */ public function testGetWebsite() { $websiteId = 2; @@ -138,6 +147,9 @@ public function testGetWebsite() $this->assertEquals($website, $model->getWebsite()); } + /** + * @return void + */ public function testGetWebsiteIfWebsiteIsNotExist() { $websiteRepository = $this->getMockBuilder(\Magento\Store\Api\WebsiteRepositoryInterface::class) @@ -156,6 +168,9 @@ public function testGetWebsiteIfWebsiteIsNotExist() $this->assertFalse($model->getWebsite()); } + /** + * @return void + */ public function testGetGroup() { $groupId = 2; @@ -179,6 +194,9 @@ public function testGetGroup() $this->assertEquals($group, $model->getGroup()); } + /** + * @return void + */ public function testGetGroupIfGroupIsNotExist() { $groupRepository = $this->getMockBuilder(\Magento\Store\Api\GroupRepositoryInterface::class) @@ -197,6 +215,9 @@ public function testGetGroupIfGroupIsNotExist() $this->assertFalse($model->getGroup()); } + /** + * @return void + */ public function testGetUrl() { $params = ['_scope_to_url' => true]; @@ -325,6 +346,9 @@ public function getBaseUrlDataProvider() ]; } + /** + * @return void + */ public function testGetBaseUrlEntryPoint() { $expectedPath = 'web/unsecure/base_link_url'; @@ -526,6 +550,9 @@ public function getBaseCurrencyDataProvider() ]; } + /** + * @return void + */ public function testGetAllowedCurrencies() { $currencyPath = 'cur/ren/cy/path'; @@ -651,11 +678,17 @@ public function testGetBaseStaticDir() $this->assertEquals($expectedResult, $this->store->getBaseStaticDir()); } + /** + * @return void + */ public function testGetScopeType() { $this->assertEquals(ScopeInterface::SCOPE_STORE, $this->store->getScopeType()); } + /** + * @return void + */ public function testGetScopeTypeName() { $this->assertEquals('Store View', $this->store->getScopeTypeName()); diff --git a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php index 3097a016fbbe8..642ca0f9af6d1 100644 --- a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php +++ b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php @@ -40,6 +40,9 @@ class RouterTest extends \PHPUnit\Framework\TestCase /** @var \Magento\UrlRewrite\Model\UrlFinderInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $urlFinder; + /** + * @return void + */ protected function setUp() { $this->actionFactory = $this->createMock(\Magento\Framework\App\ActionFactory::class); @@ -68,6 +71,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testNoRewriteExist() { $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue(null)); @@ -77,6 +83,9 @@ public function testNoRewriteExist() $this->assertNull($this->router->match($this->request)); } + /** + * @return void + */ public function testRewriteAfterStoreSwitcher() { $initialRequestPath = 'request-path'; @@ -146,6 +155,9 @@ public function testRewriteAfterStoreSwitcher() $this->router->match($this->request); } + /** + * @return void + */ public function testNoRewriteAfterStoreSwitcherWhenNoOldRewrite() { $this->request->expects($this->any())->method('getPathInfo')->will($this->returnValue('request-path')); @@ -168,6 +180,9 @@ public function testNoRewriteAfterStoreSwitcherWhenNoOldRewrite() $this->assertNull($this->router->match($this->request)); } + /** + * @return void + */ public function testNoRewriteAfterStoreSwitcherWhenOldRewriteEqualsToNewOne() { $this->request->expects($this->any())->method('getPathInfo')->will($this->returnValue('request-path')); @@ -208,6 +223,9 @@ public function testNoRewriteAfterStoreSwitcherWhenOldRewriteEqualsToNewOne() $this->assertNull($this->router->match($this->request)); } + /** + * @return void + */ public function testMatchWithRedirect() { $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); @@ -227,6 +245,9 @@ public function testMatchWithRedirect() $this->router->match($this->request); } + /** + * @return void + */ public function testMatchWithCustomInternalRedirect() { $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); @@ -278,6 +299,9 @@ public function externalRedirectTargetPathDataProvider() ]; } + /** + * @return void + */ public function testMatch() { $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php index 264e5d993ad2d..f6b3de1059821 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php @@ -22,6 +22,9 @@ class CompareTest extends \Magento\TestFramework\TestCase\AbstractController */ protected $productRepository; + /** + * @return void + */ protected function setUp() { parent::setUp(); @@ -32,6 +35,9 @@ protected function setUp() $this->productRepository = $objectManager->create(\Magento\Catalog\Model\ProductRepository::class); } + /** + * @return void + */ public function testAddAction() { $this->_requireVisitorWithNoProducts(); @@ -57,6 +63,9 @@ public function testAddAction() $this->_assertCompareListEquals([$product->getEntityId()]); } + /** + * @return void + */ public function testIndexActionAddProducts() { $this->_requireVisitorWithNoProducts(); @@ -68,6 +77,9 @@ public function testIndexActionAddProducts() $this->_assertCompareListEquals([$product->getEntityId()]); } + /** + * @return void + */ public function testRemoveAction() { $this->_requireVisitorWithTwoProducts(); @@ -84,6 +96,9 @@ public function testRemoveAction() $this->_assertCompareListEquals([$restProduct->getEntityId()]); } + /** + * @return void + */ public function testRemoveActionWithSession() { $this->_requireCustomerWithTwoProducts(); @@ -101,6 +116,9 @@ public function testRemoveActionWithSession() $this->_assertCompareListEquals([$secondProduct->getEntityId()]); } + /** + * @return void + */ public function testIndexActionDisplay() { $this->_requireVisitorWithTwoProducts(); @@ -127,6 +145,9 @@ public function testIndexActionDisplay() $this->assertContains('$987.65', $responseBody); } + /** + * @return void + */ public function testClearAction() { $this->_requireVisitorWithTwoProducts(); @@ -160,6 +181,9 @@ public function testRemoveActionProductNameXss() ); } + /** + * @return void + */ protected function _prepareCompareListWithProductNameXss() { /** @var $visitor \Magento\Customer\Model\Visitor */ @@ -182,6 +206,9 @@ protected function _prepareCompareListWithProductNameXss() ); } + /** + * @return void + */ protected function _requireVisitorWithNoProducts() { /** @var $visitor \Magento\Customer\Model\Visitor */ @@ -201,6 +228,9 @@ protected function _requireVisitorWithNoProducts() $this->_assertCompareListEquals([]); } + /** + * @return void + */ protected function _requireVisitorWithTwoProducts() { /** @var $visitor \Magento\Customer\Model\Visitor */ @@ -233,6 +263,9 @@ protected function _requireVisitorWithTwoProducts() $this->_assertCompareListEquals([$firstProductEntityId, $secondProductEntityId]); } + /** + * @return void + */ protected function _requireCustomerWithTwoProducts() { $customer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index 181275904c5cc..186d9c8f87dc2 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -55,6 +55,9 @@ public function testIndexAction() $this->assertContains('Green str, 67', $body); } + /** + * @return void + */ public function testCreateAction() { $this->dispatch('customer/account/create'); diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php index 21e029d5aab81..5593cecb10d91 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php @@ -42,6 +42,9 @@ public function testMatchUrlRewrite( ); } + /** + * @return array + */ public function requestDataProvider() { return [ From f57f4b8845ef4484f655064a536ee38747ae0800 Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Fri, 10 Aug 2018 11:50:12 +0400 Subject: [PATCH 0877/1171] MAGETWO-91524: "element.disabled is not a function"error is thrown when configurable products are generated with an attribute named "design" - Update automated test --- ...eProductAttributeNameDesignActionGroup.xml | 27 +++---------------- ...rableProductAttributeNameDesignSection.xml | 2 +- ...igurableProductAttributeNameDesignTest.xml | 2 -- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml index b9c594ea89a35..eec1fd6273ee3 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml @@ -155,30 +155,11 @@ <!--Click on OK button--> <click stepKey="clickOnOKButton" selector="{{CatalogProductsSection.okButton}}"/> <waitForPageLoad stepKey="waitFordAttributeDeleted"/> + <see userInput="You deleted the product attribute." stepKey="seeDeletedTheProductAttributeMessage"/> - </actionGroup> - - <actionGroup name="DeleteCreatedAttributeIfExist"> - - <!--Click on Stores item--> - <click stepKey="clickOnStoresItem" selector="{{CatalogProductsSection.storesItem}}"/> - <waitForPageLoad stepKey="waitForCatalogLoad"/> - - <!--Click on Products item--> - <click stepKey="clickOnStoresProductItem" selector="{{CatalogProductsSection.storesProductItem}}"/> - <waitForPageLoad stepKey="waitForStoresProductPageLoad"/> - - <!--Click on created Attribute item if it exist--> - <conditionalClick selector="{{CatalogProductsSection.createdAttributeItem}}" dependentSelector="{{CatalogProductsSection.createdAttributeItem}}" visible="1" stepKey="clickOnCreatedAttributeItem"/> - <waitForPageLoad stepKey="waitForCreatedAttributeLoad"/> - - <!--Click on Delete Attribute item--> - <conditionalClick stepKey="clickOnDeleteAttributeItem" selector="{{CatalogProductsSection.deleteAttributeItem}}" dependentSelector="{{CatalogProductsSection.deleteAttributeItem}}" visible="1"/> - <waitForPageLoad stepKey="waitForDeletedDialogOpened"/> - - <!--Click on OK button--> - <conditionalClick stepKey="clickOnOKButton" selector="{{CatalogProductsSection.okButton}}" dependentSelector="{{CatalogProductsSection.okButton}}" visible="1"/> - <waitForPageLoad stepKey="waitFordAttributeDeleted"/> + <!-- Click Reset Filter button--> + <click stepKey="clickResetFilterButton" selector="{{CatalogProductsSection.resetFilter}}"/> + <waitForPageLoad stepKey="waitForAllFilterReset"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml index 182ed7c42a401..b3077d9d5d566 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml @@ -18,8 +18,8 @@ <element name="createdAttributeItem" type="button" selector="//td[contains(@class, 'col-label') and normalize-space()='design']"/> <element name="deleteAttributeItem" type="button" selector="//*[@id='delete']"/> <element name="okButton" type="button" selector="//footer[@class='modal-footer']//*[contains(text(),'OK')]"/> - <element name="messageSuccessSavedProduct" type="button" selector="//div[@data-ui-id='messages-message-success']"/> + <element name="resetFilter" type="button" selector="//span[contains(text(), 'Reset Filter')]"/> </section> <section name="ConfigurableProductSection"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml index 3ff0589230687..bb7792b9d375e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml @@ -21,8 +21,6 @@ <before> <!-- Log in to Dashboard page --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <!-- This actionGroup should be deleted and Test will pass after fixing the bug.--> - <actionGroup ref="DeleteCreatedAttributeIfExist" stepKey="deleteCreatedAttributeIfExist"/> </before> From 4711dad5b8cb5dae2ab12a73e350f695db258664 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Fri, 10 Aug 2018 11:48:54 +0300 Subject: [PATCH 0878/1171] MAGETWO-93286: [2.3] Gift Message lost at Checkout when logging in --- .../Observer/SalesEventQuoteMerge.php | 35 +++++ .../GiftMessage/etc/frontend/events.xml | 3 + .../Magento/Quote/Model/QuoteTest.php | 138 ++++++++++++------ 3 files changed, 133 insertions(+), 43 deletions(-) create mode 100644 app/code/Magento/GiftMessage/Observer/SalesEventQuoteMerge.php diff --git a/app/code/Magento/GiftMessage/Observer/SalesEventQuoteMerge.php b/app/code/Magento/GiftMessage/Observer/SalesEventQuoteMerge.php new file mode 100644 index 0000000000000..0d2280d29fed4 --- /dev/null +++ b/app/code/Magento/GiftMessage/Observer/SalesEventQuoteMerge.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GiftMessage\Observer; + +use Magento\Framework\Event\ObserverInterface; +use Magento\Quote\Model\Quote; + +/** + * Gift Message Observer Model + */ +class SalesEventQuoteMerge implements ObserverInterface +{ + /** + * Sets gift message to customer quote from guest quote. + * + * @param \Magento\Framework\Event\Observer $observer + * @return $this + */ + public function execute(\Magento\Framework\Event\Observer $observer) + { + /** @var Quote $targetQuote */ + $targetQuote = $observer->getData('quote'); + /** @var Quote $sourceQuote */ + $sourceQuote = $observer->getData('source'); + + $targetQuote->setGiftMessageId($sourceQuote->getGiftMessageId()); + + return $this; + } +} diff --git a/app/code/Magento/GiftMessage/etc/frontend/events.xml b/app/code/Magento/GiftMessage/etc/frontend/events.xml index 3af69730838ad..c786373b2094f 100644 --- a/app/code/Magento/GiftMessage/etc/frontend/events.xml +++ b/app/code/Magento/GiftMessage/etc/frontend/events.xml @@ -12,6 +12,9 @@ <event name="sales_convert_order_to_quote"> <observer name="giftmessage" instance="Magento\GiftMessage\Observer\SalesEventOrderToQuoteObserver" shared="false" /> </event> + <event name="sales_quote_merge_after"> + <observer name="giftmessage" instance="Magento\GiftMessage\Observer\SalesEventQuoteMerge" shared="false" /> + </event> <event name="checkout_type_multishipping_create_orders_single"> <observer name="giftmessage" instance="Magento\GiftMessage\Observer\MultishippingEventCreateOrdersObserver" shared="false" /> </event> diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php index 39db5b1572cb7..2af8969f11372 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php @@ -8,16 +8,33 @@ use Magento\Catalog\Model\ProductRepository; use Magento\Customer\Api\Data\CustomerInterfaceFactory; use Magento\Framework\Exception\LocalizedException; +use Magento\Quote\Api\Data\CartInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Quote\Api\CartRepositoryInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class QuoteTest extends \PHPUnit\Framework\TestCase { + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + } + private function convertToArray($entity) { - return Bootstrap::getObjectManager() + return $this->objectManager ->create(\Magento\Framework\Api\ExtensibleDataObjectConverter::class) ->toFlatArray($entity); } @@ -28,10 +45,10 @@ private function convertToArray($entity) */ public function testCollectTotalsWithVirtual() { - $quote = Bootstrap::getObjectManager()->create(\Magento\Quote\Model\Quote::class); + $quote = $this->objectManager->create(Quote::class); $quote->load('test01', 'reserved_order_id'); - $productRepository = Bootstrap::getObjectManager()->create( + $productRepository = $this->objectManager->create( \Magento\Catalog\Api\ProductRepositoryInterface::class ); $product = $productRepository->get('virtual-product', false, null, true); @@ -46,14 +63,14 @@ public function testCollectTotalsWithVirtual() public function testSetCustomerData() { - /** @var \Magento\Quote\Model\Quote $quote */ - $quote = Bootstrap::getObjectManager()->create(\Magento\Quote\Model\Quote::class); + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); /** @var CustomerInterfaceFactory $customerFactory */ - $customerFactory = Bootstrap::getObjectManager()->create( + $customerFactory = $this->objectManager->create( CustomerInterfaceFactory::class ); /** @var \Magento\Framework\Api\DataObjectHelper $dataObjectHelper */ - $dataObjectHelper = Bootstrap::getObjectManager()->create(\Magento\Framework\Api\DataObjectHelper::class); + $dataObjectHelper = $this->objectManager->create(\Magento\Framework\Api\DataObjectHelper::class); $expected = $this->_getCustomerDataArray(); $customer = $customerFactory->create(); $dataObjectHelper->populateWithArray( @@ -72,13 +89,13 @@ public function testSetCustomerData() public function testUpdateCustomerData() { - /** @var \Magento\Quote\Model\Quote $quote */ - $quote = Bootstrap::getObjectManager()->create(\Magento\Quote\Model\Quote::class); - $customerFactory = Bootstrap::getObjectManager()->create( + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); + $customerFactory = $this->objectManager->create( CustomerInterfaceFactory::class ); /** @var \Magento\Framework\Api\DataObjectHelper $dataObjectHelper */ - $dataObjectHelper = Bootstrap::getObjectManager()->create(\Magento\Framework\Api\DataObjectHelper::class); + $dataObjectHelper = $this->objectManager->create(\Magento\Framework\Api\DataObjectHelper::class); $expected = $this->_getCustomerDataArray(); //For save in repository $expected = $this->removeIdFromCustomerData($expected); @@ -92,7 +109,7 @@ public function testUpdateCustomerData() /** * @var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */ - $customerRepository = Bootstrap::getObjectManager() + $customerRepository = $this->objectManager ->create(\Magento\Customer\Api\CustomerRepositoryInterface::class); $customerRepository->save($customerDataSet); $quote->setCustomer($customerDataSet); @@ -121,13 +138,13 @@ public function testGetCustomerGroupFromCustomer() { /** Preconditions */ /** @var CustomerInterfaceFactory $customerFactory */ - $customerFactory = Bootstrap::getObjectManager()->create( + $customerFactory = $this->objectManager->create( CustomerInterfaceFactory::class ); $customerGroupId = 3; $customerData = $customerFactory->create()->setId(1)->setGroupId($customerGroupId); - /** @var \Magento\Quote\Model\Quote $quote */ - $quote = Bootstrap::getObjectManager()->create(\Magento\Quote\Model\Quote::class); + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); $quote->setCustomer($customerData); $quote->unsetData('customer_group_id'); @@ -146,10 +163,10 @@ public function testGetCustomerTaxClassId() $fixtureGroupCode = 'custom_group'; $fixtureTaxClassId = 3; /** @var \Magento\Customer\Model\Group $group */ - $group = Bootstrap::getObjectManager()->create(\Magento\Customer\Model\Group::class); + $group = $this->objectManager->create(\Magento\Customer\Model\Group::class); $fixtureGroupId = $group->load($fixtureGroupCode, 'customer_group_code')->getId(); - /** @var \Magento\Quote\Model\Quote $quote */ - $quote = Bootstrap::getObjectManager()->create(\Magento\Quote\Model\Quote::class); + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); $quote->setCustomerGroupId($fixtureGroupId); /** Execute SUT */ @@ -169,8 +186,8 @@ public function testAssignCustomerWithAddressChangeAddressesNotSpecified() * Customer with two addresses created * First address is default billing, second is default shipping. */ - /** @var \Magento\Quote\Model\Quote $quote */ - $quote = Bootstrap::getObjectManager()->create(\Magento\Quote\Model\Quote::class); + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); $customerData = $this->_prepareQuoteForTestAssignCustomerWithAddressChange($quote); /** Execute SUT */ @@ -234,9 +251,8 @@ public function testAssignCustomerWithAddressChange() * Customer with two addresses created * First address is default billing, second is default shipping. */ - /** @var \Magento\Quote\Model\Quote $quote */ - $objectManager = Bootstrap::getObjectManager(); - $quote = $objectManager->create(\Magento\Quote\Model\Quote::class); + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); $customerData = $this->_prepareQuoteForTestAssignCustomerWithAddressChange($quote); /** @var \Magento\Quote\Model\Quote\Address $quoteBillingAddress */ $expectedBillingAddressData = [ @@ -249,7 +265,7 @@ public function testAssignCustomerWithAddressChange() 'firstname' => 'FirstBilling', 'region_id' => 1 ]; - $quoteBillingAddress = $objectManager->create(\Magento\Quote\Model\Quote\Address::class); + $quoteBillingAddress = $this->objectManager->create(\Magento\Quote\Model\Quote\Address::class); $quoteBillingAddress->setData($expectedBillingAddressData); $expectedShippingAddressData = [ @@ -262,7 +278,7 @@ public function testAssignCustomerWithAddressChange() 'firstname' => 'FirstShipping', 'region_id' => 1 ]; - $quoteShippingAddress = $objectManager->create(\Magento\Quote\Model\Quote\Address::class); + $quoteShippingAddress = $this->objectManager->create(\Magento\Quote\Model\Quote\Address::class); $quoteShippingAddress->setData($expectedShippingAddressData); /** Execute SUT */ @@ -295,13 +311,13 @@ public function testAssignCustomerWithAddressChange() */ public function testAddProductUpdateItem() { - /** @var \Magento\Quote\Model\Quote $quote */ - $quote = Bootstrap::getObjectManager()->create(\Magento\Quote\Model\Quote::class); + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); $quote->load('test01', 'reserved_order_id'); $productStockQty = 100; - $productRepository = Bootstrap::getObjectManager()->create( + $productRepository = $this->objectManager->create( \Magento\Catalog\Api\ProductRepositoryInterface::class ); $product = $productRepository->get('simple-1', false, null, true); @@ -334,17 +350,16 @@ public function testAddProductUpdateItem() * * Customer with two addresses created. First address is default billing, second is default shipping. * - * @param \Magento\Quote\Model\Quote $quote + * @param Quote $quote * @return \Magento\Customer\Api\Data\CustomerInterface */ protected function _prepareQuoteForTestAssignCustomerWithAddressChange($quote) { - $objectManager = Bootstrap::getObjectManager(); /** @var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */ - $customerRepository = $objectManager->create(\Magento\Customer\Api\CustomerRepositoryInterface::class); + $customerRepository = $this->objectManager->create(\Magento\Customer\Api\CustomerRepositoryInterface::class); $fixtureCustomerId = 1; /** @var \Magento\Customer\Model\Customer $customer */ - $customer = $objectManager->create(\Magento\Customer\Model\Customer::class); + $customer = $this->objectManager->create(\Magento\Customer\Model\Customer::class); $fixtureSecondAddressId = 2; $customer->load($fixtureCustomerId)->setDefaultShipping($fixtureSecondAddressId)->save(); $customerData = $customerRepository->getById($fixtureCustomerId); @@ -413,9 +428,8 @@ protected function _getCustomerDataArray() */ public function testReserveOrderId() { - $objectManager = Bootstrap::getObjectManager(); - /** @var \Magento\Quote\Model\Quote $quote */ - $quote = $objectManager->create(\Magento\Quote\Model\Quote::class); + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); $quote->load('reserved_order_id', 'reserved_order_id'); $quote->reserveOrderId(); $this->assertEquals('reserved_order_id', $quote->getReservedOrderId()); @@ -431,18 +445,17 @@ public function testReserveOrderId() public function testAddedProductToQuoteIsSalable() { $productId = 99; - $objectManager = Bootstrap::getObjectManager(); /** @var ProductRepository $productRepository */ - $productRepository = $objectManager->get(ProductRepository::class); + $productRepository = $this->objectManager->get(ProductRepository::class); - /** @var \Magento\Quote\Model\Quote $quote */ + /** @var Quote $quote */ $product = $productRepository->getById($productId, false, null, true); $this->expectException(LocalizedException::class); $this->expectExceptionMessage('Product that you are trying to add is not available.'); - $quote = $objectManager->create(\Magento\Quote\Model\Quote::class); + $quote = $this->objectManager->create(Quote::class); $quote->addProduct($product); } @@ -452,13 +465,12 @@ public function testAddedProductToQuoteIsSalable() */ public function testGetItemById() { - $objectManager = Bootstrap::getObjectManager(); - $quote = $objectManager->create(\Magento\Quote\Model\Quote::class); + $quote = $this->objectManager->create(Quote::class); $quote->load('test01', 'reserved_order_id'); - $quoteItem = $objectManager->create(\Magento\Quote\Model\Quote\Item::class); + $quoteItem = $this->objectManager->create(\Magento\Quote\Model\Quote\Item::class); - $productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); $product = $productRepository->get('simple'); $quoteItem->setProduct($product); @@ -468,4 +480,44 @@ public function testGetItemById() $this->assertInstanceOf(\Magento\Quote\Model\Quote\Item::class, $quote->getItemById($quoteItem->getId())); $this->assertEquals($quoteItem->getId(), $quote->getItemById($quoteItem->getId())->getId()); } + + /** + * Tests of quotes merging. + * + * @magentoDataFixture Magento/Sales/_files/quote.php + */ + public function testMerge() + { + $giftMessageId = 1; + + /** @var Quote $quote */ + $guestQuote = $this->getQuote('test01'); + $guestQuote->setGiftMessageId($giftMessageId); + + /** @var Quote $customerQuote */ + $customerQuote = $this->objectManager->create(Quote::class); + $customerQuote->merge($guestQuote); + + self::assertEquals($giftMessageId, $customerQuote->getGiftMessageId()); + } + + /** + * Gets quote by reserved order id. + * + * @param string $reservedOrderId + * @return Quote + */ + private function getQuote($reservedOrderId) + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria)->getItems(); + + return array_pop($items); + } } From 601b2f641edd8dcbf55e0344a0bf70b2b1ceddf6 Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Fri, 10 Aug 2018 13:55:23 +0400 Subject: [PATCH 0879/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Updated automated test to clear search field --- .../Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml | 3 +++ .../Test/Mftf/Section/StorefrontAddProductToCardSection.xml | 1 + 2 files changed, 4 insertions(+) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml index f4d65e22abe84..71919d9da37de 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml @@ -55,6 +55,9 @@ <click selector="{{DeleteCreatedProduct.okButton}}" stepKey="clickToConfirm"/> <wait stepKey="waitForRecordIsDeleted" time="2"/> <see userInput="A total of 1 record(s) have been deleted." stepKey="productDeletedSuccessfully"/> + <click stepKey="clickClearAllFilterButton" selector="{{DeleteCreatedProduct.clearAll}}"/> + <!-- We need this wait to make sure that Active filters is clear (waitForElementNotVisible tag doesn't wait until clearing filters)--> + <wait stepKey="waitToClearAllFilters" time="2"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml index 29315aeeb2355..868a47a26f42b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml @@ -67,6 +67,7 @@ <element name="actionSelectBox" type="button" selector="//*[@class='col-xs-2']//span[text()='Actions']"/> <element name="deleteButton" type="button" selector="//div[@class='col-xs-2']//*[text()='Delete']"/> <element name="okButton" type="button" selector=".action-primary.action-accept"/> + <element name="clearAll" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[contains(text(), 'Clear all')]"/> </section> </sections> From e4861d39c89f1fc0c4e0f63bbe6a940598c94386 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Fri, 10 Aug 2018 13:21:21 +0300 Subject: [PATCH 0880/1171] Code style fixes --- .../PayPal/TokenFormatterTest.php | 14 +++++++++++++- .../Model/Quote/Item/CartItemProcessor.php | 4 ++-- .../Customer/Block/Adminhtml/Edit/Tab/Cart.php | 2 +- .../Magento/Customer/Block/Widget/Company.php | 12 ++++++------ app/code/Magento/Customer/Block/Widget/Fax.php | 12 ++++++------ .../Magento/Customer/Block/Widget/Name.php | 2 +- .../Customer/Block/Widget/Telephone.php | 12 ++++++------ .../Controller/Adminhtml/Order/Status/Save.php | 2 +- .../AbstractCreditmemo/EmailTest.php | 9 +++++++++ .../Invoice/AbstractInvoice/EmailTest.php | 12 ++++++++++++ .../Controller/Adminhtml/Order/CancelTest.php | 6 ++++++ .../Adminhtml/Order/Create/ProcessDataTest.php | 3 +++ .../Controller/Adminhtml/Order/EmailTest.php | 9 +++++++++ .../Controller/Adminhtml/Order/HoldTest.php | 6 ++++++ .../Adminhtml/Order/MassCancelTest.php | 3 +++ .../Adminhtml/Order/MassHoldTest.php | 9 +++++++++ .../Adminhtml/Order/MassUnholdTest.php | 9 +++++++++ .../Adminhtml/Order/ReviewPaymentTest.php | 6 ++++++ .../Controller/Adminhtml/Order/UnholdTest.php | 6 ++++++ .../Controller/Adminhtml/Order/ViewTest.php | 15 +++++++++++++++ .../Adminhtml/PdfDocumentsMassActionTest.php | 6 ++++++ .../Unit/Model/AdminOrder/EmailSenderTest.php | 9 +++++++++ .../Adminhtml/Promo/Quote/GenerateTest.php | 6 ++++++ app/code/Magento/UrlRewrite/Block/Edit.php | 2 +- .../Framework/Search/Adapter/Mysql/Adapter.php | 2 +- .../Setup/Model/ConfigOptionsList/Cache.php | 4 ++-- .../Model/ConfigOptionsList/PageCache.php | 4 ++-- .../Unit/Model/ConfigOptionsList/CacheTest.php | 18 ++++++++++++++++++ .../Model/ConfigOptionsList/PageCacheTest.php | 18 ++++++++++++++++++ 29 files changed, 192 insertions(+), 30 deletions(-) diff --git a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php index ebb9a3e8bfb59..e4cd8fd58043b 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\Braintree\Test\Unit\Model\InstantPurchase\Paypal; +namespace Magento\Braintree\Test\Unit\Model\InstantPurchase\PayPal; use Magento\Braintree\Model\InstantPurchase\CreditCard\TokenFormatter as PaypalTokenFormatter; use Magento\Vault\Api\Data\PaymentTokenInterface; @@ -31,6 +31,9 @@ class TokenFormatterTest extends \PHPUnit\Framework\TestCase 'expirationDate' => '07-07-2025' ]; + /** + * Test setup + */ protected function setUp() { $this->paymentTokenMock = $this->getMockBuilder(PaymentTokenInterface::class) @@ -39,6 +42,9 @@ protected function setUp() $this->paypalTokenFormatter = new PaypalTokenFormatter(); } + /** + * testFormatPaymentTokenWithKnownCardType + */ public function testFormatPaymentTokenWithKnownCardType() { $this->tokenDetails['type'] = key(PaypalTokenFormatter::$baseCardTypes); @@ -59,6 +65,9 @@ public function testFormatPaymentTokenWithKnownCardType() self::assertEquals($formattedString, $this->paypalTokenFormatter->formatPaymentToken($this->paymentTokenMock)); } + /** + * testFormatPaymentTokenWithUnknownCardType + */ public function testFormatPaymentTokenWithUnknownCardType() { $this->paymentTokenMock->expects($this->once()) @@ -78,6 +87,9 @@ public function testFormatPaymentTokenWithUnknownCardType() self::assertEquals($formattedString, $this->paypalTokenFormatter->formatPaymentToken($this->paymentTokenMock)); } + /** + * testFormatPaymentTokenWithWrongData + */ public function testFormatPaymentTokenWithWrongData() { unset($this->tokenDetails['type']); diff --git a/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php b/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php index 85493b81bc6d4..56993ecec1fbf 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php +++ b/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php @@ -59,7 +59,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function convertToBuyRequest(CartItemInterface $cartItem) { @@ -78,7 +78,7 @@ public function convertToBuyRequest(CartItemInterface $cartItem) } /** - * {@inheritdoc} + * @inheritdoc */ public function processOptions(CartItemInterface $cartItem) { diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Cart.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Cart.php index 9cd1989980fac..db560f7de3ecb 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Cart.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Cart.php @@ -220,7 +220,7 @@ public function getGridParentHtml() } /** - * {@inheritdoc} + * @inheritdoc */ public function getRowUrl($row) { diff --git a/app/code/Magento/Customer/Block/Widget/Company.php b/app/code/Magento/Customer/Block/Widget/Company.php index d1c86d19a9849..8052e396f7dda 100644 --- a/app/code/Magento/Customer/Block/Widget/Company.php +++ b/app/code/Magento/Customer/Block/Widget/Company.php @@ -40,12 +40,12 @@ class Company extends AbstractWidget protected $options; /** - * @param Context $context - * @param AddressHelper $addressHelper + * @param Context $context + * @param AddressHelper $addressHelper * @param CustomerMetadataInterface $customerMetadata - * @param Options $options - * @param AddressMetadataInterface $addressMetadata - * @param array $data + * @param Options $options + * @param AddressMetadataInterface $addressMetadata + * @param array $data */ public function __construct( Context $context, @@ -95,7 +95,7 @@ public function showCompany() } /** - * {@inheritdoc} + * @inheritdoc */ protected function _getAttribute($attributeCode) { diff --git a/app/code/Magento/Customer/Block/Widget/Fax.php b/app/code/Magento/Customer/Block/Widget/Fax.php index a775c447142df..aa1cff632abd3 100644 --- a/app/code/Magento/Customer/Block/Widget/Fax.php +++ b/app/code/Magento/Customer/Block/Widget/Fax.php @@ -40,12 +40,12 @@ class Fax extends AbstractWidget protected $options; /** - * @param Context $context - * @param AddressHelper $addressHelper + * @param Context $context + * @param AddressHelper $addressHelper * @param CustomerMetadataInterface $customerMetadata - * @param Options $options - * @param AddressMetadataInterface $addressMetadata - * @param array $data + * @param Options $options + * @param AddressMetadataInterface $addressMetadata + * @param array $data */ public function __construct( Context $context, @@ -95,7 +95,7 @@ public function showFax() } /** - * {@inheritdoc} + * @inheritdoc */ protected function _getAttribute($attributeCode) { diff --git a/app/code/Magento/Customer/Block/Widget/Name.php b/app/code/Magento/Customer/Block/Widget/Name.php index f70ddcd67af3a..d50045f4a4092 100644 --- a/app/code/Magento/Customer/Block/Widget/Name.php +++ b/app/code/Magento/Customer/Block/Widget/Name.php @@ -201,7 +201,7 @@ public function getContainerClassName() } /** - * {@inheritdoc} + * @inheritdoc */ protected function _getAttribute($attributeCode) { diff --git a/app/code/Magento/Customer/Block/Widget/Telephone.php b/app/code/Magento/Customer/Block/Widget/Telephone.php index 86608ec729b70..b67e92ed7d29f 100644 --- a/app/code/Magento/Customer/Block/Widget/Telephone.php +++ b/app/code/Magento/Customer/Block/Widget/Telephone.php @@ -40,12 +40,12 @@ class Telephone extends AbstractWidget protected $options; /** - * @param Context $context - * @param AddressHelper $addressHelper + * @param Context $context + * @param AddressHelper $addressHelper * @param CustomerMetadataInterface $customerMetadata - * @param Options $options - * @param AddressMetadataInterface $addressMetadata - * @param array $data + * @param Options $options + * @param AddressMetadataInterface $addressMetadata + * @param array $data */ public function __construct( Context $context, @@ -95,7 +95,7 @@ public function showTelephone() } /** - * {@inheritdoc} + * @inheritdoc */ protected function _getAttribute($attributeCode) { diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php index 6cedae096f2f8..bd0ad771815b1 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php @@ -68,7 +68,7 @@ public function execute() /** * @param \Magento\Backend\Model\View\Result\Redirect $resultRedirect - * @param $isNew + * @param bool $isNew * @return \Magento\Backend\Model\View\Result\Redirect */ private function getRedirect(\Magento\Backend\Model\View\Result\Redirect $resultRedirect, $isNew) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/EmailTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/EmailTest.php index cece68c544779..910164f029f1c 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/EmailTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/EmailTest.php @@ -73,6 +73,9 @@ class EmailTest extends \PHPUnit\Framework\TestCase */ protected $resultRedirectMock; + /** + * Test setup + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); @@ -131,6 +134,9 @@ protected function setUp() ); } + /** + * testEmail + */ public function testEmail() { $cmId = 10000031; @@ -160,6 +166,9 @@ public function testEmail() $this->assertEquals($this->response, $this->creditmemoEmail->getResponse()); } + /** + * testEmailNoCreditmemoId + */ public function testEmailNoCreditmemoId() { $this->request->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Invoice/AbstractInvoice/EmailTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Invoice/AbstractInvoice/EmailTest.php index e7182878b4a4a..8407cc251db93 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Invoice/AbstractInvoice/EmailTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Invoice/AbstractInvoice/EmailTest.php @@ -89,6 +89,9 @@ class EmailTest extends \PHPUnit\Framework\TestCase */ protected $invoiceManagement; + /** + * Test setup + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); @@ -153,6 +156,9 @@ protected function setUp() ); } + /** + * testEmail + */ public function testEmail() { $invoiceId = 10000031; @@ -209,6 +215,9 @@ public function testEmail() $this->assertInstanceOf(\Magento\Backend\Model\View\Result\Redirect::class, $this->invoiceEmail->execute()); } + /** + * testEmailNoInvoiceId + */ public function testEmailNoInvoiceId() { $this->request->expects($this->once()) @@ -226,6 +235,9 @@ public function testEmailNoInvoiceId() $this->assertInstanceOf(\Magento\Backend\Model\View\Result\Forward::class, $this->invoiceEmail->execute()); } + /** + * testEmailNoInvoice + */ public function testEmailNoInvoice() { $invoiceId = 10000031; diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php index 9f8c3a60bacc5..a368bf779398f 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php @@ -61,6 +61,9 @@ class CancelTest extends \PHPUnit\Framework\TestCase */ protected $objectManager; + /** + * Test setup + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); @@ -108,6 +111,9 @@ protected function setUp() ); } + /** + * testExecuteNotPost + */ public function testExecuteNotPost() { $this->validatorMock->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ProcessDataTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ProcessDataTest.php index 2bc33b3bad6de..3e22867ce1f65 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ProcessDataTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ProcessDataTest.php @@ -63,6 +63,9 @@ class ProcessDataTest extends \PHPUnit\Framework\TestCase */ protected $resultForwardFactory; + /** + * Test setup + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/EmailTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/EmailTest.php index 6286cd521d9db..e9a0e573e3f9a 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/EmailTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/EmailTest.php @@ -87,6 +87,9 @@ class EmailTest extends \PHPUnit\Framework\TestCase */ protected $orderMock; + /** + * Test setup + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); @@ -152,6 +155,9 @@ protected function setUp() ); } + /** + * testEmail + */ public function testEmail() { $orderId = 10000031; @@ -185,6 +191,9 @@ public function testEmail() $this->assertEquals($this->response, $this->orderEmail->getResponse()); } + /** + * testEmailNoOrderId + */ public function testEmailNoOrderId() { $this->request->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/HoldTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/HoldTest.php index e7245016c0d74..30e25605f0b94 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/HoldTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/HoldTest.php @@ -61,6 +61,9 @@ class HoldTest extends \PHPUnit\Framework\TestCase */ protected $objectManager; + /** + * Test setup + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); @@ -108,6 +111,9 @@ protected function setUp() ); } + /** + * testExecuteNotPost + */ public function testExecuteNotPost() { $this->validatorMock->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassCancelTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassCancelTest.php index 8e2620135255b..38449c5686631 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassCancelTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassCancelTest.php @@ -90,6 +90,9 @@ class MassCancelTest extends \PHPUnit\Framework\TestCase */ private $orderManagementMock; + /** + * Test setup + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassHoldTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassHoldTest.php index 6bfbdf24b45a9..bfd1d1e825f89 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassHoldTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassHoldTest.php @@ -90,6 +90,9 @@ class MassHoldTest extends \PHPUnit\Framework\TestCase */ protected $orderManagementMock; + /** + * Test setup + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); @@ -166,6 +169,9 @@ protected function setUp() ); } + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testExecuteOneOrderPutOnHold() { $order1 = $this->getMockBuilder(\Magento\Sales\Model\Order::class) @@ -211,6 +217,9 @@ public function testExecuteOneOrderPutOnHold() $this->massAction->execute(); } + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testExecuteNoOrdersPutOnHold() { $order1 = $this->getMockBuilder(\Magento\Sales\Model\Order::class) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassUnholdTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassUnholdTest.php index 096abdce6d2a9..56473ec948f27 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassUnholdTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassUnholdTest.php @@ -90,6 +90,9 @@ class MassUnholdTest extends \PHPUnit\Framework\TestCase */ private $orderManagementMock; + /** + * Test setup + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); @@ -164,6 +167,9 @@ protected function setUp() ); } + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testExecuteOneOrdersReleasedFromHold() { $order1 = $this->getMockBuilder(\Magento\Sales\Model\Order::class) @@ -211,6 +217,9 @@ public function testExecuteOneOrdersReleasedFromHold() $this->massAction->execute(); } + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testExecuteNoReleasedOrderFromHold() { $order1 = $this->getMockBuilder(\Magento\Sales\Model\Order::class) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ReviewPaymentTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ReviewPaymentTest.php index 9d5b33b4a0b59..452bde7023f9d 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ReviewPaymentTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ReviewPaymentTest.php @@ -51,6 +51,9 @@ class ReviewPaymentTest extends \PHPUnit\Framework\TestCase */ protected $loggerMock; + /** + * Test setup + */ protected function setUp() { $this->contextMock = $this->createPartialMock(\Magento\Backend\App\Action\Context::class, [ @@ -112,6 +115,9 @@ protected function setUp() ); } + /** + * testExecuteUpdateAction + */ public function testExecuteUpdateAction() { $orderId = 30; diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/UnholdTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/UnholdTest.php index efa9235dc42f5..cc4720f1c6b44 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/UnholdTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/UnholdTest.php @@ -61,6 +61,9 @@ class UnholdTest extends \PHPUnit\Framework\TestCase */ protected $objectManager; + /** + * Test setup + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); @@ -108,6 +111,9 @@ protected function setUp() ); } + /** + * testExecuteNotPost + */ public function testExecuteNotPost() { $this->validatorMock->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ViewTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ViewTest.php index 9884f1604c756..7d39aca35d2c1 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ViewTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/ViewTest.php @@ -98,6 +98,9 @@ class ViewTest extends \PHPUnit\Framework\TestCase */ protected $orderRepositoryMock; + /** + * Test setup + */ protected function setUp() { $this->orderManagementMock = $this->getMockBuilder(\Magento\Sales\Api\OrderManagementInterface::class) @@ -262,6 +265,9 @@ public function testGlobalException() ); } + /** + * initOrder + */ protected function initOrder() { $orderIdParam = 111; @@ -289,6 +295,9 @@ protected function initOrderSuccess() ); } + /** + * initOrderFail + */ protected function initOrderFail() { $this->messageManagerMock->expects($this->once()) @@ -300,6 +309,9 @@ protected function initOrderFail() ->with('', \Magento\Sales\Controller\Adminhtml\Order::FLAG_NO_DISPATCH, true); } + /** + * initAction + */ protected function initAction() { $this->resultPageFactoryMock->expects($this->once()) @@ -318,6 +330,9 @@ protected function initAction() ->willReturnSelf(); } + /** + * prepareRedirect + */ protected function prepareRedirect() { $this->resultRedirectFactoryMock->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/PdfDocumentsMassActionTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/PdfDocumentsMassActionTest.php index b201ed0753554..bb9662b7e02d8 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/PdfDocumentsMassActionTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/PdfDocumentsMassActionTest.php @@ -40,6 +40,9 @@ class PdfDocumentsMassActionTest extends \PHPUnit\Framework\TestCase */ private $filterMock; + /** + * Test setup + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); @@ -80,6 +83,9 @@ protected function setUp() ); } + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testExecute() { $exception = new \Exception(); diff --git a/app/code/Magento/Sales/Test/Unit/Model/AdminOrder/EmailSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/AdminOrder/EmailSenderTest.php index 87dd8b2be5a30..37f0f754c7074 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/AdminOrder/EmailSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/AdminOrder/EmailSenderTest.php @@ -34,6 +34,9 @@ class EmailSenderTest extends \PHPUnit\Framework\TestCase */ protected $orderSenderMock; + /** + * Test setup + */ protected function setUp() { $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\Manager::class); @@ -44,6 +47,9 @@ protected function setUp() $this->emailSender = new EmailSender($this->messageManagerMock, $this->loggerMock, $this->orderSenderMock); } + /** + * testSendSuccess + */ public function testSendSuccess() { $this->orderSenderMock->expects($this->once()) @@ -51,6 +57,9 @@ public function testSendSuccess() $this->assertTrue($this->emailSender->send($this->orderMock)); } + /** + * testSendFailure + */ public function testSendFailure() { $this->orderSenderMock->expects($this->once()) diff --git a/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/GenerateTest.php b/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/GenerateTest.php index 219f342cdb949..2ef77d72a8af5 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/GenerateTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/GenerateTest.php @@ -50,6 +50,9 @@ class GenerateTest extends \PHPUnit\Framework\TestCase /** @var CouponGenerator | \PHPUnit_Framework_MockObject_MockObject */ private $couponGenerator; + /** + * Test setup + */ protected function setUp() { $this->contextMock = $this->getMockBuilder(\Magento\Backend\App\Action\Context::class) @@ -109,6 +112,9 @@ protected function setUp() ); } + /** + * testExecute + */ public function testExecute() { $helperData = $this->getMockBuilder(\Magento\Framework\Json\Helper\Data::class) diff --git a/app/code/Magento/UrlRewrite/Block/Edit.php b/app/code/Magento/UrlRewrite/Block/Edit.php index 210ed5189eb4c..115c5db43a70a 100644 --- a/app/code/Magento/UrlRewrite/Block/Edit.php +++ b/app/code/Magento/UrlRewrite/Block/Edit.php @@ -243,7 +243,7 @@ private function _getSelectorBlock() * Since buttons are set as children, we remove them as children after generating them * not to duplicate them in future * - * @param null $area + * @param string $area * @return string * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php index d64f15058393d..11bd746eb71e5 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php @@ -69,7 +69,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc * @throws \LogicException */ public function query(RequestInterface $request) diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php index 04ec83a3d0ca2..2f19f010c1704 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php @@ -117,7 +117,7 @@ public function getOptions() } /** - * {@inheritdoc} + * @inheritdoc */ public function createConfig(array $options, DeploymentConfig $deploymentConfig) { @@ -141,7 +141,7 @@ public function createConfig(array $options, DeploymentConfig $deploymentConfig) } /** - * {@inheritdoc} + * @inheritdoc */ public function validate(array $options, DeploymentConfig $deploymentConfig) { diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php index 944c543495751..c288b4dd51d65 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php @@ -127,7 +127,7 @@ public function getOptions() } /** - * {@inheritdoc} + * @inheritdoc */ public function createConfig(array $options, DeploymentConfig $deploymentConfig) { @@ -152,7 +152,7 @@ public function createConfig(array $options, DeploymentConfig $deploymentConfig) } /** - * {@inheritdoc} + * @inheritdoc */ public function validate(array $options, DeploymentConfig $deploymentConfig) { diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php index d2ff7b2f35523..1bbd2e671e59a 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php @@ -28,6 +28,9 @@ class CacheTest extends \PHPUnit\Framework\TestCase */ private $deploymentConfigMock; + /** + * Tests setup + */ protected function setUp() { $this->validatorMock = $this->createMock(RedisConnectionValidator::class); @@ -36,6 +39,9 @@ protected function setUp() $this->configOptionsList = new CacheConfigOptionsList($this->validatorMock); } + /** + * testGetOptions + */ public function testGetOptions() { $options = $this->configOptionsList->getOptions(); @@ -62,6 +68,9 @@ public function testGetOptions() $this->assertEquals('cache-backend-redis-password', $options[4]->getName()); } + /** + * testCreateConfigCacheRedis + */ public function testCreateConfigCacheRedis() { $this->deploymentConfigMock->method('get')->willReturn(''); @@ -87,6 +96,9 @@ public function testCreateConfigCacheRedis() $this->assertEquals($expectedConfigData, $configData->getData()); } + /** + * testCreateConfigWithRedisConfig + */ public function testCreateConfigWithRedisConfig() { $expectedConfigData = [ @@ -116,6 +128,9 @@ public function testCreateConfigWithRedisConfig() $this->assertEquals($expectedConfigData, $configData->getData()); } + /** + * testValidateWithValidInput + */ public function testValidateWithValidInput() { $options = [ @@ -132,6 +147,9 @@ public function testValidateWithValidInput() $this->assertEmpty($errors); } + /** + * testValidateWithInvalidInput + */ public function testValidateWithInvalidInput() { $invalidCacheOption = 'clay-tablet'; diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php index ed0e567820ad1..6f8e97cd5dd8d 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php @@ -28,6 +28,9 @@ class PageCacheTest extends \PHPUnit\Framework\TestCase */ private $deploymentConfigMock; + /** + * Test setup + */ protected function setUp() { $this->validatorMock = $this->createMock(RedisConnectionValidator::class, [], [], '', false); @@ -36,6 +39,9 @@ protected function setUp() $this->configList = new PageCache($this->validatorMock); } + /** + * testGetOptions + */ public function testGetOptions() { $options = $this->configList->getOptions(); @@ -66,6 +72,9 @@ public function testGetOptions() $this->assertEquals('page-cache-redis-password', $options[5]->getName()); } + /** + * testCreateConfigWithRedis + */ public function testCreateConfigWithRedis() { $this->deploymentConfigMock->method('get')->willReturn(''); @@ -92,6 +101,9 @@ public function testCreateConfigWithRedis() $this->assertEquals($expectedConfigData, $configData->getData()); } + /** + * testCreateConfigWithRedisConfiguration + */ public function testCreateConfigWithRedisConfiguration() { $expectedConfigData = [ @@ -124,6 +136,9 @@ public function testCreateConfigWithRedisConfiguration() $this->assertEquals($expectedConfigData, $configData->getData()); } + /** + * testValidationWithValidData + */ public function testValidationWithValidData() { $this->validatorMock->expects($this->once()) @@ -140,6 +155,9 @@ public function testValidationWithValidData() $this->assertEmpty($errors); } + /** + * testValidationWithInvalidData + */ public function testValidationWithInvalidData() { $options = [ From 6ce423507e1886fa72f9acd507b047edad80d554 Mon Sep 17 00:00:00 2001 From: Lusine Hakobyan <lusine_hakobyan@epam.com> Date: Fri, 10 Aug 2018 14:22:27 +0400 Subject: [PATCH 0881/1171] MAGETWO-91552: [github] CAPTCHA doesn't show when check out as guest - Add automated test --- .../CaptchaFormsDisplayingActionGroup.xml | 23 +++++++ .../Mftf/Data/CaptchaFormsDisplayingData.xml | 20 ++++++ .../Section/CaptchaFormsDisplayingSection.xml | 27 ++++++++ .../Mftf/Test/CaptchaFormsDisplayingTest.xml | 69 +++++++++++++++++++ 4 files changed, 139 insertions(+) create mode 100644 app/code/Magento/Captcha/Test/Mftf/ActionGroup/CaptchaFormsDisplayingActionGroup.xml create mode 100644 app/code/Magento/Captcha/Test/Mftf/Data/CaptchaFormsDisplayingData.xml create mode 100644 app/code/Magento/Captcha/Test/Mftf/Section/CaptchaFormsDisplayingSection.xml create mode 100644 app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/CaptchaFormsDisplayingActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/CaptchaFormsDisplayingActionGroup.xml new file mode 100644 index 0000000000000..71a876bbbcdbf --- /dev/null +++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/CaptchaFormsDisplayingActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CaptchaFormsDisplayingActionGroup"> + <click selector="{{CaptchaFormsDisplayingSection.store}}" stepKey="ClickToGoStores"/> + <waitForPageLoad stepKey="waitForStoresLoaded"/> + <click selector="{{CaptchaFormsDisplayingSection.config}}" stepKey="ClickToGoConfiguration"/> + <waitForPageLoad stepKey="waitForConfigurationsLoaded"/> + <scrollTo selector="{{CaptchaFormsDisplayingSection.customer}}" stepKey="ScrollToCustomers"/> + <click selector="{{CaptchaFormsDisplayingSection.customer}}" stepKey="ClickToCustomers"/> + <waitForPageLoad stepKey="waitForCustomerConfigurationsLoaded"/> + <click selector="{{CaptchaFormsDisplayingSection.customerConfig}}" stepKey="ClickToGoCustomerConfiguration"/> + <scrollTo selector="{{CaptchaFormsDisplayingSection.captcha}}" stepKey="scrollToCaptcha"/> + <conditionalClick selector="{{CaptchaFormsDisplayingSection.captcha}}" dependentSelector="{{CaptchaFormsDisplayingSection.dependent}}" visible="false" stepKey="ClickToOpenCaptcha"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Captcha/Test/Mftf/Data/CaptchaFormsDisplayingData.xml b/app/code/Magento/Captcha/Test/Mftf/Data/CaptchaFormsDisplayingData.xml new file mode 100644 index 0000000000000..9db8110c0f64b --- /dev/null +++ b/app/code/Magento/Captcha/Test/Mftf/Data/CaptchaFormsDisplayingData.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="CaptchaData"> + <data key="createUser">Create user</data> + <data key="login">Login</data> + <data key="passwd">Forgot password</data> + <data key="contactUs">Contact Us</data> + <data key="changePasswd">Change password</data> + <data key="register">Register during Checkout</data> + <data key="checkoutAsGuest">Check Out as Guest</data> + </entity> +</entities> diff --git a/app/code/Magento/Captcha/Test/Mftf/Section/CaptchaFormsDisplayingSection.xml b/app/code/Magento/Captcha/Test/Mftf/Section/CaptchaFormsDisplayingSection.xml new file mode 100644 index 0000000000000..4c974e6fced05 --- /dev/null +++ b/app/code/Magento/Captcha/Test/Mftf/Section/CaptchaFormsDisplayingSection.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="CaptchaFormsDisplayingSection"> + <element name="store" type="button" selector="#menu-magento-backend-stores"/> + <element name="config" type="button" selector="//li[@data-ui-id='menu-magento-config-system-config']//span"/> + <element name="customer" type="button" selector="//div[@class='admin__page-nav-title title _collapsible']//strong[text()='Customers']"/> + <element name="customerConfig" type="text" selector="//span[text()='Customer Configuration']"/> + <element name="captcha" type="button" selector="#customer_captcha-head"/> + <element name="dependent" type="button" selector="//a[@id='customer_captcha-head' and @class='open']"/> + <element name="forms" type="multiselect" selector="#customer_captcha_forms"/> + <element name="createUser" type="multiselect" selector="//select[@id='customer_captcha_forms']/option[@value='user_create']"/> + <element name="forgotpassword" type="multiselect" selector="//select[@id='customer_captcha_forms']/option[@value='user_forgotpassword']"/> + <element name="userLogin" type="multiselect" selector="//select[@id='customer_captcha_forms']/option[@value='user_login']"/> + <element name="guestCheckout" type="multiselect" selector="//select[@id='customer_captcha_forms']/option[@value='guest_checkout']"/> + <element name="register" type="multiselect" selector="//select[@id='customer_captcha_forms']/option[@value='register_during_checkout']"/> + <element name="userEdit" type="multiselect" selector="//select[@id='customer_captcha_forms']/option[@value='user_edit']"/> + <element name="contactUs" type="multiselect" selector="//select[@id='customer_captcha_forms']/option[@value='contact_us']"/> + </section> +</sections> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml new file mode 100644 index 0000000000000..8f764899706ac --- /dev/null +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="CaptchaFormsDisplayingTest"> + <annotations> + <features value="Captcha"/> + <stories value="MAGETWO-91552 - [github] CAPTCHA doesn't show when check out as guest"/> + <title value="Captcha forms displaying"/> + <description value="Captcha forms displaying"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-93941"/> + <group value="captcha"/> + </annotations> + + <!--Login as admin--> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!--Go to Captcha--> + <actionGroup ref="CaptchaFormsDisplayingActionGroup" stepKey="CaptchaFormsDisplayingActionGroup"/> + <waitForPageLoad stepKey="WaitForPageLoaded"/> + <!--Verify fields removed--> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.forms}}" stepKey="formItems"/> + <assertNotContains stepKey="checkoutAsGuest"> + <expectedResult type="string">{{CaptchaData.checkoutAsGuest}}</expectedResult> + <actualResult type="variable">$formItems</actualResult> + </assertNotContains> + <assertNotContains stepKey="register"> + <expectedResult type="string">{{CaptchaData.register}}</expectedResult> + <actualResult type="variable">$formItems</actualResult> + </assertNotContains> + <!--Verify fields existence--> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.createUser}}" stepKey="createUser"/> + <assertEquals stepKey="CreateUserFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.createUser}}</expectedResult> + <actualResult type="variable">$createUser</actualResult> + </assertEquals> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.userLogin}}" stepKey="login"/> + <assertEquals stepKey="LoginFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.login}}</expectedResult> + <actualResult type="variable">login</actualResult> + </assertEquals> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.forgotpassword}}" stepKey="forgotpassword"/> + <assertEquals stepKey="PasswordFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.passwd}}</expectedResult> + <actualResult type="variable">$forgotpassword</actualResult> + </assertEquals> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.contactUs}}" stepKey="contactUs"/> + <assertEquals stepKey="contactUsFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.contactUs}}</expectedResult> + <actualResult type="variable">$contactUs</actualResult> + </assertEquals> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.userEdit}}" stepKey="userEdit"/> + <assertEquals stepKey="userEditFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.changePasswd}}</expectedResult> + <actualResult type="variable">$userEdit</actualResult> + </assertEquals> + + <!--Roll back configuration--> + <scrollToTopOfPage stepKey="ScrollToTop"/> + <click selector="{{CaptchaFormsDisplayingSection.captcha}}" stepKey="ClickToCloseCaptcha"/> + + </test> +</tests> From 1ebe5f18472421aaec7d18ad53ae0e732ac4e18c Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Fri, 10 Aug 2018 13:58:41 +0300 Subject: [PATCH 0882/1171] MAGETWO-93057: [2.3] salesShipmentRepositoryV1 throws error when adding tracking --- .../Magento/Sales/Model/Order/Shipment.php | 48 ++++--- .../Test/Unit/Model/Order/ShipmentTest.php | 44 ++---- .../Sales/Model/Order/ShipmentTest.php | 130 +++++++++++++----- 3 files changed, 140 insertions(+), 82 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Shipment.php b/app/code/Magento/Sales/Model/Order/Shipment.php index e23d7eaef2f0a..7be7a79dcf004 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment.php +++ b/app/code/Magento/Sales/Model/Order/Shipment.php @@ -9,6 +9,7 @@ use Magento\Sales\Api\Data\ShipmentInterface; use Magento\Sales\Model\AbstractModel; use Magento\Sales\Model\EntityInterface; +use Magento\Sales\Model\ResourceModel\Order\Shipment\Comment\Collection as CommentsCollection; /** * Sales order shipment model @@ -94,9 +95,14 @@ class Shipment extends AbstractModel implements EntityInterface, ShipmentInterfa protected $orderRepository; /** - * @var \Magento\Sales\Model\ResourceModel\Order\Shipment\Track\Collection|null + * @var \Magento\Sales\Model\ResourceModel\Order\Shipment\Track\Collection */ - private $tracksCollection = null; + private $tracksCollection; + + /** + * @var CommentsCollection + */ + private $commentsCollection; /** * @param \Magento\Framework\Model\Context $context @@ -414,43 +420,45 @@ public function addTrack(\Magento\Sales\Model\Order\Shipment\Track $track) public function addComment($comment, $notify = false, $visibleOnFront = false) { if (!$comment instanceof \Magento\Sales\Model\Order\Shipment\Comment) { - $comment = $this->_commentFactory->create()->setComment( - $comment - )->setIsCustomerNotified( - $notify - )->setIsVisibleOnFront( - $visibleOnFront - ); + $comment = $this->_commentFactory->create() + ->setComment($comment) + ->setIsCustomerNotified($notify) + ->setIsVisibleOnFront($visibleOnFront); } - $comment->setShipment($this)->setParentId($this->getId())->setStoreId($this->getStoreId()); + $comment->setShipment($this) + ->setParentId($this->getId()) + ->setStoreId($this->getStoreId()); if (!$comment->getId()) { $this->getCommentsCollection()->addItem($comment); } + $comments = $this->getComments(); + $comments[] = $comment; + $this->setComments($comments); $this->_hasDataChanges = true; return $this; } /** - * Retrieve comments collection. + * Retrieves comments collection. * * @param bool $reload - * @return \Magento\Sales\Model\ResourceModel\Order\Shipment\Comment\Collection + * @return CommentsCollection */ public function getCommentsCollection($reload = false) { - if (!$this->hasData(ShipmentInterface::COMMENTS) || $reload) { - $comments = $this->_commentCollectionFactory->create() - ->setShipmentFilter($this->getId()) - ->setCreatedAtOrder(); - $this->setComments($comments); - + if ($this->commentsCollection === null || $reload) { + $this->commentsCollection = $this->_commentCollectionFactory->create(); if ($this->getId()) { - foreach ($this->getComments() as $comment) { + $this->commentsCollection->setShipmentFilter($this->getId()) + ->setCreatedAtOrder(); + + foreach ($this->commentsCollection as $comment) { $comment->setShipment($this); } } } - return $this->getComments(); + + return $this->commentsCollection; } /** diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentTest.php index 5204073454345..f1724899f22f5 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentTest.php @@ -25,7 +25,7 @@ class ShipmentTest extends \PHPUnit\Framework\TestCase private $commentCollection; /** - * @var \Magento\Sales\Model\Order\shipment + * @var Shipment */ private $shipmentModel; @@ -46,9 +46,6 @@ public function testGetIncrementId() $this->assertEquals('test_increment_id', $this->shipmentModel->getIncrementId()); } - /** - * @covers \Magento\Sales\Model\Order\Shipment::getCommentsCollection - */ public function testGetCommentsCollection() { $shipmentId = 1; @@ -58,36 +55,29 @@ public function testGetCommentsCollection() ->disableOriginalConstructor() ->setMethods(['setShipment']) ->getMock(); - $shipmentItem->expects(static::once()) - ->method('setShipment') + $shipmentItem->method('setShipment') ->with($this->shipmentModel); $collection = [$shipmentItem]; - $this->commentCollection->expects(static::once()) + $this->commentCollection->expects(self::once()) ->method('setShipmentFilter') ->with($shipmentId) ->willReturnSelf(); - $this->commentCollection->expects(static::once()) + $this->commentCollection->expects(self::once()) ->method('setCreatedAtOrder') ->willReturnSelf(); - $this->commentCollection->expects(static::once()) - ->method('load') - ->willReturnSelf(); - $reflection = new \ReflectionClass(Collection::class); $reflectionProperty = $reflection->getProperty('_items'); $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($this->commentCollection, $collection); - $expected = $this->shipmentModel->getCommentsCollection(); + $actual = $this->shipmentModel->getCommentsCollection(); - static::assertEquals($expected, $this->commentCollection); + self::assertTrue(is_object($actual)); + self::assertEquals($this->commentCollection, $actual); } - /** - * @covers \Magento\Sales\Model\Order\Shipment::getComments - */ public function testGetComments() { $shipmentId = 1; @@ -97,30 +87,27 @@ public function testGetComments() ->disableOriginalConstructor() ->setMethods(['setShipment']) ->getMock(); - $shipmentItem->expects(static::once()) + $shipmentItem->expects(self::once()) ->method('setShipment') ->with($this->shipmentModel); $collection = [$shipmentItem]; - $this->commentCollection->expects(static::once()) - ->method('setShipmentFilter') + $this->commentCollection->method('setShipmentFilter') ->with($shipmentId) ->willReturnSelf(); - $this->commentCollection->expects(static::once()) - ->method('load') - ->willReturnSelf(); - $reflection = new \ReflectionClass(Collection::class); $reflectionProperty = $reflection->getProperty('_items'); $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($this->commentCollection, $collection); - $this->commentCollection->expects(static::once()) + $this->commentCollection->expects(self::once()) ->method('getItems') ->willReturn($collection); - static::assertEquals($this->shipmentModel->getComments(), $collection); + $actual = $this->shipmentModel->getComments(); + self::assertTrue(is_array($actual)); + self::assertEquals($collection, $actual); } /** @@ -131,7 +118,7 @@ private function initCommentsCollectionFactoryMock() { $this->commentCollection = $this->getMockBuilder(Collection::class) ->disableOriginalConstructor() - ->setMethods(['setShipmentFilter', 'setCreatedAtOrder', 'getItems', 'load', '__wakeup']) + ->setMethods(['setShipmentFilter', 'setCreatedAtOrder', 'getItems', 'load']) ->getMock(); $this->commentCollectionFactory = $this->getMockBuilder(CollectionFactory::class) @@ -139,8 +126,7 @@ private function initCommentsCollectionFactoryMock() ->setMethods(['create']) ->getMock(); - $this->commentCollectionFactory->expects(static::any()) - ->method('create') + $this->commentCollectionFactory->method('create') ->willReturn($this->commentCollection); } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php index 18097cf12def4..0a12ffa81139e 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php @@ -3,35 +3,59 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Sales\Model\Order; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Payment\Helper\Data; +use Magento\Sales\Api\Data\CommentInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\Data\ShipmentInterface; +use Magento\Sales\Api\Data\ShipmentItemInterface; +use Magento\Sales\Api\Data\ShipmentTrackInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Api\ShipmentRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + /** - * Class ShipmentTest * @magentoAppIsolation enabled - * @package Magento\Sales\Model\Order + * @magentoDataFixture Magento/Sales/_files/order.php */ class ShipmentTest extends \PHPUnit\Framework\TestCase { + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ShipmentRepositoryInterface + */ + private $shipmentRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->shipmentRepository = $this->objectManager->get(ShipmentRepositoryInterface::class); + } + /** * Check the correctness and stability of set/get packages of shipment * - * @magentoDataFixture Magento/Sales/_files/order.php + * @magentoAppArea frontend */ public function testPackages() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $objectManager->get(\Magento\Framework\App\State::class)->setAreaCode('frontend'); - $order = $objectManager->create(\Magento\Sales\Model\Order::class); - $order->loadByIncrementId('100000001'); - $order->setCustomerEmail('customer@example.com'); + $order = $this->getOrder('100000001'); $payment = $order->getPayment(); - $paymentInfoBlock = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Payment\Helper\Data::class - )->getInfoBlock( - $payment - ); + $paymentInfoBlock = $this->objectManager->get(Data::class) + ->getInfoBlock($payment); $payment->setBlockMock($paymentInfoBlock); $items = []; @@ -39,47 +63,87 @@ public function testPackages() $items[$item->getId()] = $item->getQtyOrdered(); } /** @var \Magento\Sales\Model\Order\Shipment $shipment */ - $shipment = $objectManager->get(ShipmentFactory::class)->create($order, $items); + $shipment = $this->objectManager->get(ShipmentFactory::class)->create($order, $items); $packages = [['1'], ['2']]; $shipment->setPackages($packages); - $this->assertEquals($packages, $shipment->getPackages()); - $shipment->save(); - $shipment->save(); - $shipment->load($shipment->getId()); - $this->assertEquals($packages, $shipment->getPackages()); + $saved = $this->shipmentRepository->save($shipment); + self::assertEquals($packages, $saved->getPackages()); } /** * Check that getTracksCollection() always return collection instance. - * - * @magentoDataFixture Magento/Sales/_files/order.php */ public function testAddTrack() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $order = $this->getOrder('100000001'); - $order = $objectManager->create(\Magento\Sales\Model\Order::class); - $order->loadByIncrementId('100000001'); + /** @var ShipmentInterface $shipment */ + $shipment = $this->objectManager->create(ShipmentInterface::class); + + /** @var ShipmentTrackInterface $track */ + $track = $this->objectManager->create(ShipmentTrackInterface::class); + $track->setNumber('Test Number') + ->setTitle('Test Title') + ->setCarrierCode('Test CODE'); $items = []; foreach ($order->getItems() as $item) { $items[$item->getId()] = $item->getQtyOrdered(); } /** @var \Magento\Sales\Model\Order\Shipment $shipment */ - $shipment = $objectManager->get(ShipmentFactory::class)->create($order, $items); + $shipment = $this->objectManager->get(ShipmentFactory::class)->create($order, $items); + $shipment->addTrack($track); $shipment->save(); + $saved = $this->shipmentRepository->save($shipment); + self::assertNotEmpty($saved->getTracks()); + } + + /** + * Checks adding comment to the shipment entity. + */ + public function testAddComment() + { + $message1 = 'Test Comment 1'; + $message2 = 'Test Comment 2'; + $order = $this->getOrder('100000001'); + + /** @var ShipmentInterface $shipment */ + $shipment = $this->objectManager->create(ShipmentInterface::class); + $shipment->setOrder($order) + ->addItem($this->objectManager->create(ShipmentItemInterface::class)) + ->addComment($message1) + ->addComment($message2); - /** @var $track \Magento\Sales\Model\Order\Shipment\Track */ - $track = $objectManager->get(\Magento\Sales\Model\Order\Shipment\Track::class); - $track->setNumber('Test Number')->setTitle('Test Title')->setCarrierCode('Test CODE'); + $saved = $this->shipmentRepository->save($shipment); + + $comments = $saved->getComments(); + $actual = array_map(function (CommentInterface $comment) { + return $comment->getComment(); + }, $comments); + self::assertEquals(2, count($actual)); + self::assertEquals([$message1, $message2], $actual); + } + + /** + * Gets order entity by increment id. + * + * @param string $incrementId + * @return OrderInterface + */ + private function getOrder(string $incrementId): OrderInterface + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('increment_id', $incrementId) + ->create(); - $this->assertEmpty($shipment->getTracks()); - $shipment->addTrack($track)->save(); + /** @var OrderRepositoryInterface $repository */ + $repository = $this->objectManager->get(OrderRepositoryInterface::class); + $items = $repository->getList($searchCriteria) + ->getItems(); - //to empty cache - $shipment->setTracks(null); - $this->assertNotEmpty($shipment->getTracks()); + return array_pop($items); } } From 80933e036d971ad6cfb9cd3cb29a77c0bf4f9a61 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Fri, 10 Aug 2018 14:56:23 +0300 Subject: [PATCH 0883/1171] MAGETWO-93057: [2.3] salesShipmentRepositoryV1 throws error when adding tracking --- .../Magento/Sales/Model/Order/Shipment.php | 30 +++++++++---------- .../Test/Unit/Model/Order/ShipmentTest.php | 20 +++++++++++++ .../Sales/Model/Order/ShipmentTest.php | 3 -- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Shipment.php b/app/code/Magento/Sales/Model/Order/Shipment.php index 7be7a79dcf004..8be12ec6ad57f 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment.php +++ b/app/code/Magento/Sales/Model/Order/Shipment.php @@ -522,7 +522,7 @@ public function getPackages() } /** - * {@inheritdoc} + * @inheritdoc * @codeCoverageIgnore */ public function setPackages(array $packages = null) @@ -619,7 +619,7 @@ public function getCreatedAt() } /** - * {@inheritdoc} + * @inheritdoc */ public function setCreatedAt($createdAt) { @@ -751,7 +751,7 @@ public function setComments($comments = null) } /** - * {@inheritdoc} + * @inheritdoc */ public function setStoreId($id) { @@ -759,7 +759,7 @@ public function setStoreId($id) } /** - * {@inheritdoc} + * @inheritdoc */ public function setTotalWeight($totalWeight) { @@ -767,7 +767,7 @@ public function setTotalWeight($totalWeight) } /** - * {@inheritdoc} + * @inheritdoc */ public function setTotalQty($qty) { @@ -775,7 +775,7 @@ public function setTotalQty($qty) } /** - * {@inheritdoc} + * @inheritdoc */ public function setEmailSent($emailSent) { @@ -783,7 +783,7 @@ public function setEmailSent($emailSent) } /** - * {@inheritdoc} + * @inheritdoc */ public function setOrderId($id) { @@ -791,7 +791,7 @@ public function setOrderId($id) } /** - * {@inheritdoc} + * @inheritdoc */ public function setCustomerId($id) { @@ -799,7 +799,7 @@ public function setCustomerId($id) } /** - * {@inheritdoc} + * @inheritdoc */ public function setShippingAddressId($id) { @@ -807,7 +807,7 @@ public function setShippingAddressId($id) } /** - * {@inheritdoc} + * @inheritdoc */ public function setBillingAddressId($id) { @@ -815,7 +815,7 @@ public function setBillingAddressId($id) } /** - * {@inheritdoc} + * @inheritdoc */ public function setShipmentStatus($shipmentStatus) { @@ -823,7 +823,7 @@ public function setShipmentStatus($shipmentStatus) } /** - * {@inheritdoc} + * @inheritdoc */ public function setIncrementId($id) { @@ -831,7 +831,7 @@ public function setIncrementId($id) } /** - * {@inheritdoc} + * @inheritdoc */ public function setUpdatedAt($timestamp) { @@ -839,7 +839,7 @@ public function setUpdatedAt($timestamp) } /** - * {@inheritdoc} + * @inheritdoc * * @return \Magento\Sales\Api\Data\ShipmentExtensionInterface|null */ @@ -849,7 +849,7 @@ public function getExtensionAttributes() } /** - * {@inheritdoc} + * @inheritdoc * * @param \Magento\Sales\Api\Data\ShipmentExtensionInterface $extensionAttributes * @return $this diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentTest.php index f1724899f22f5..a7649b5387cbe 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentTest.php @@ -29,6 +29,9 @@ class ShipmentTest extends \PHPUnit\Framework\TestCase */ private $shipmentModel; + /** + * @return void + */ protected function setUp() { $helperManager = new ObjectManager($this); @@ -40,12 +43,23 @@ protected function setUp() ]); } + /** + * Test to Returns increment id + * + * @return void + */ public function testGetIncrementId() { $this->shipmentModel->setIncrementId('test_increment_id'); $this->assertEquals('test_increment_id', $this->shipmentModel->getIncrementId()); } + /** + * Test to Retrieves comments collection + * + * @return void + * @throws \ReflectionException + */ public function testGetCommentsCollection() { $shipmentId = 1; @@ -78,6 +92,12 @@ public function testGetCommentsCollection() self::assertEquals($this->commentCollection, $actual); } + /** + * Test to Returns comments + * + * @return void + * @throws \ReflectionException + */ public function testGetComments() { $shipmentId = 1; diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php index 0a12ffa81139e..0679fc6ffe6cb 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php @@ -79,9 +79,6 @@ public function testAddTrack() { $order = $this->getOrder('100000001'); - /** @var ShipmentInterface $shipment */ - $shipment = $this->objectManager->create(ShipmentInterface::class); - /** @var ShipmentTrackInterface $track */ $track = $this->objectManager->create(ShipmentTrackInterface::class); $track->setNumber('Test Number') From 1e6f2e6fa81306f3d7b61fc031862fdc235f49c0 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Fri, 10 Aug 2018 15:15:05 +0300 Subject: [PATCH 0884/1171] MAGETWO-93286: [2.3] Gift Message lost at Checkout when logging in --- .../Magento/Quote/Model/QuoteTest.php | 71 +++++++++++++------ 1 file changed, 49 insertions(+), 22 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php index 2af8969f11372..5d6438dda063b 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php @@ -8,11 +8,11 @@ use Magento\Catalog\Model\ProductRepository; use Magento\Customer\Api\Data\CustomerInterfaceFactory; use Magento\Framework\Exception\LocalizedException; -use Magento\Quote\Api\Data\CartInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Framework\Api\ExtensibleDataInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -32,7 +32,11 @@ protected function setUp() $this->objectManager = Bootstrap::getObjectManager(); } - private function convertToArray($entity) + /** + * @param ExtensibleDataInterface $entity + * @return array + */ + private function convertToArray(ExtensibleDataInterface $entity): array { return $this->objectManager ->create(\Magento\Framework\Api\ExtensibleDataObjectConverter::class) @@ -42,8 +46,9 @@ private function convertToArray($entity) /** * @magentoDataFixture Magento/Catalog/_files/product_virtual.php * @magentoDataFixture Magento/Sales/_files/quote.php + * @return void */ - public function testCollectTotalsWithVirtual() + public function testCollectTotalsWithVirtual(): void { $quote = $this->objectManager->create(Quote::class); $quote->load('test01', 'reserved_order_id'); @@ -61,7 +66,10 @@ public function testCollectTotalsWithVirtual() $this->assertEquals(20, $quote->getBaseGrandTotal()); } - public function testSetCustomerData() + /** + * @return void + */ + public function testSetCustomerData(): void { /** @var Quote $quote */ $quote = $this->objectManager->create(Quote::class); @@ -87,7 +95,10 @@ public function testSetCustomerData() $this->assertEquals('qa@example.com', $quote->getCustomerEmail()); } - public function testUpdateCustomerData() + /** + * @return void + */ + public function testUpdateCustomerData(): void { /** @var Quote $quote */ $quote = $this->objectManager->create(Quote::class); @@ -133,8 +144,10 @@ public function testUpdateCustomerData() /** * Customer data is set to quote (which contains valid group ID). + * + * @return void */ - public function testGetCustomerGroupFromCustomer() + public function testGetCustomerGroupFromCustomer(): void { /** Preconditions */ /** @var CustomerInterfaceFactory $customerFactory */ @@ -154,8 +167,9 @@ public function testGetCustomerGroupFromCustomer() /** * @magentoDataFixture Magento/Customer/_files/customer_group.php + * @return void */ - public function testGetCustomerTaxClassId() + public function testGetCustomerTaxClassId(): void { /** * Preconditions: create quote and assign ID of customer group created in fixture to it. @@ -179,8 +193,9 @@ public function testGetCustomerTaxClassId() * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php + * @return void */ - public function testAssignCustomerWithAddressChangeAddressesNotSpecified() + public function testAssignCustomerWithAddressChangeAddressesNotSpecified(): void { /** Preconditions: * Customer with two addresses created @@ -244,8 +259,9 @@ public function testAssignCustomerWithAddressChangeAddressesNotSpecified() * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php + * @return void */ - public function testAssignCustomerWithAddressChange() + public function testAssignCustomerWithAddressChange(): void { /** Preconditions: * Customer with two addresses created @@ -308,8 +324,9 @@ public function testAssignCustomerWithAddressChange() /** * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php + * @return void */ - public function testAddProductUpdateItem() + public function testAddProductUpdateItem(): void { /** @var Quote $quote */ $quote = $this->objectManager->create(Quote::class); @@ -353,10 +370,13 @@ public function testAddProductUpdateItem() * @param Quote $quote * @return \Magento\Customer\Api\Data\CustomerInterface */ - protected function _prepareQuoteForTestAssignCustomerWithAddressChange($quote) - { + protected function _prepareQuoteForTestAssignCustomerWithAddressChange( + Quote $quote + ): \Magento\Customer\Api\Data\CustomerInterface { /** @var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */ - $customerRepository = $this->objectManager->create(\Magento\Customer\Api\CustomerRepositoryInterface::class); + $customerRepository = $this->objectManager->create( + \Magento\Customer\Api\CustomerRepositoryInterface::class + ); $fixtureCustomerId = 1; /** @var \Magento\Customer\Model\Customer $customer */ $customer = $this->objectManager->create(\Magento\Customer\Model\Customer::class); @@ -375,11 +395,11 @@ protected function _prepareQuoteForTestAssignCustomerWithAddressChange($quote) } /** - * @param $email + * @param string $email * @param array $customerData * @return array */ - protected function changeEmailInCustomerData($email, array $customerData) + protected function changeEmailInCustomerData(string $email, array $customerData): array { $customerData[\Magento\Customer\Model\Data\Customer::EMAIL] = $email; return $customerData; @@ -389,13 +409,16 @@ protected function changeEmailInCustomerData($email, array $customerData) * @param array $customerData * @return array */ - protected function removeIdFromCustomerData(array $customerData) + protected function removeIdFromCustomerData(array $customerData): array { unset($customerData[\Magento\Customer\Model\Data\Customer::ID]); return $customerData; } - protected function _getCustomerDataArray() + /** + * @return array + */ + protected function _getCustomerDataArray(): array { return [ \Magento\Customer\Model\Data\Customer::CONFIRMATION => 'test', @@ -425,8 +448,9 @@ protected function _getCustomerDataArray() * * @magentoDataFixture Magento/Sales/_files/order.php * @magentoDataFixture Magento/Quote/_files/empty_quote.php + * @return void */ - public function testReserveOrderId() + public function testReserveOrderId(): void { /** @var Quote $quote */ $quote = $this->objectManager->create(Quote::class); @@ -441,8 +465,9 @@ public function testReserveOrderId() /** * Test to verify that disabled product cannot be added to cart * @magentoDataFixture Magento/Quote/_files/is_not_salable_product.php + * @return void */ - public function testAddedProductToQuoteIsSalable() + public function testAddedProductToQuoteIsSalable(): void { $productId = 99; @@ -462,8 +487,9 @@ public function testAddedProductToQuoteIsSalable() /** * @magentoDataFixture Magento/Sales/_files/quote.php * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void */ - public function testGetItemById() + public function testGetItemById(): void { $quote = $this->objectManager->create(Quote::class); $quote->load('test01', 'reserved_order_id'); @@ -485,8 +511,9 @@ public function testGetItemById() * Tests of quotes merging. * * @magentoDataFixture Magento/Sales/_files/quote.php + * @return void */ - public function testMerge() + public function testMerge(): void { $giftMessageId = 1; @@ -507,7 +534,7 @@ public function testMerge() * @param string $reservedOrderId * @return Quote */ - private function getQuote($reservedOrderId) + private function getQuote(string $reservedOrderId): Quote { /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); From 8544ae5fb4bfce6d1bb754cf0f84dc3f12f4e861 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Fri, 10 Aug 2018 16:12:27 +0300 Subject: [PATCH 0885/1171] Static test failures fix --- dev/tests/static/phpunit-all.xml.dist | 2 +- dev/tests/static/phpunit.xml.dist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/static/phpunit-all.xml.dist b/dev/tests/static/phpunit-all.xml.dist index 3020889e6066b..a0d1f2fe75dc9 100644 --- a/dev/tests/static/phpunit-all.xml.dist +++ b/dev/tests/static/phpunit-all.xml.dist @@ -21,6 +21,6 @@ <php> <ini name="date.timezone" value="America/Los_Angeles"/> <!-- TESTCODESTYLE_IS_FULL_SCAN - specify if full scan should be performed for test code style test --> - <const name="TESTCODESTYLE_IS_FULL_SCAN" value="0"/> + <const name="TESTCODESTYLE_IS_FULL_SCAN" value="1"/> </php> </phpunit> diff --git a/dev/tests/static/phpunit.xml.dist b/dev/tests/static/phpunit.xml.dist index ec581ec992f00..d9f9dbdfe54cd 100644 --- a/dev/tests/static/phpunit.xml.dist +++ b/dev/tests/static/phpunit.xml.dist @@ -31,7 +31,7 @@ <php> <ini name="date.timezone" value="America/Los_Angeles"/> <!-- TESTCODESTYLE_IS_FULL_SCAN - specify if full scan should be performed for test code style test --> - <const name="TESTCODESTYLE_IS_FULL_SCAN" value="0"/> + <const name="TESTCODESTYLE_IS_FULL_SCAN" value="1"/> <!-- TESTS_COMPOSER_PATH - specify the path to composer binary, if a relative reference cannot be resolved --> <!--<const name="TESTS_COMPOSER_PATH" value="/usr/local/bin/composer"/>--> </php> From e1365484041cadc39028b1a01d3a06731b42c2ab Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Fri, 10 Aug 2018 17:11:13 +0300 Subject: [PATCH 0886/1171] MAGETWO-93961: [2.3] Product URL rewrites get deleted in multi store views --- .../CatalogUrlRewrite/Observer/UrlRewriteHandler.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php index 1268e864194bd..c4ec0bb3a74b2 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php @@ -125,7 +125,7 @@ public function generateProductUrlRewrites(Category $category): array { $mergeDataProvider = clone $this->mergeDataProviderPrototype; $this->isSkippedProduct[$category->getEntityId()] = []; - $saveRewriteHistory = $category->getData('save_rewrites_history'); + $saveRewriteHistory = (bool)$category->getData('save_rewrites_history'); $storeId = (int)$category->getStoreId(); if ($category->getChangedProductIds()) { @@ -231,14 +231,14 @@ private function getCategoryProductsUrlRewrites( * @param MergeDataProvider $mergeDataProvider * @param Category $category * @param int $storeId - * @param $saveRewriteHistory + * @param bool $saveRewriteHistory * @return void */ private function generateChangedProductUrls( MergeDataProvider $mergeDataProvider, Category $category, int $storeId, - $saveRewriteHistory + bool $saveRewriteHistory ) { $this->isSkippedProduct[$category->getEntityId()] = $category->getAffectedProductIds(); From 279e1f5d496ebaca2ad95c1e11b19b832e183fc4 Mon Sep 17 00:00:00 2001 From: Oksana_Kremen <Oksana_Kremen@epam.com> Date: Fri, 10 Aug 2018 17:15:27 +0300 Subject: [PATCH 0887/1171] MAGETWO-91620: It is not possible to use function setInAllAttributeSetsFilter() together with getAllIds() for class Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection - Added integration test --- .../Entity/Attribute/CollectionTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/ResourceModel/Entity/Attribute/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/ResourceModel/Entity/Attribute/CollectionTest.php index e39f1e2fd8390..c273e87f6d738 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Model/ResourceModel/Entity/Attribute/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Model/ResourceModel/Entity/Attribute/CollectionTest.php @@ -52,6 +52,20 @@ public function testSetAttributeGroupFilter() $this->assertEquals([$includeGroupId], $groups); } + /** + * Test if getAllIds method return results after using setInAllAttributeSetsFilter method + * + * @covers \Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection::setInAllAttributeSetsFilter() + * @covers \Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection::getAllIds() + */ + public function testSetInAllAttributeSetsFilterWithGetAllIds() + { + $sets = [1]; + $this->_model->setInAllAttributeSetsFilter($sets); + $attributeIds = $this->_model->getAllIds(); + $this->assertGreaterThan(0, count($attributeIds)); + } + /** * Returns array of group ids, present in collection attributes * From a756e0656207014beaac7daab89df5a36dce10a4 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Fri, 10 Aug 2018 09:40:20 -0500 Subject: [PATCH 0888/1171] MQE-1172: Bump MFTF version and deliver Magento branches - composer version update --- composer.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.lock b/composer.lock index ab354c595259d..04bee0ae7be60 100644 --- a/composer.lock +++ b/composer.lock @@ -2717,16 +2717,16 @@ }, { "name": "zendframework/zend-diactoros", - "version": "1.8.4", + "version": "1.8.5", "source": { "type": "git", "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba" + "reference": "3e4edb822c942f37ade0d09579cfbab11e2fee87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba", - "reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/3e4edb822c942f37ade0d09579cfbab11e2fee87", + "reference": "3e4edb822c942f37ade0d09579cfbab11e2fee87", "shasum": "" }, "require": { @@ -2776,7 +2776,7 @@ "psr", "psr-7" ], - "time": "2018-08-01T13:47:49+00:00" + "time": "2018-08-10T14:16:32+00:00" }, { "name": "zendframework/zend-escaper", @@ -6187,7 +6187,7 @@ "source": { "type": "git", "url": "git@github.com:magento/magento2-functional-testing-framework.git", - "reference": "7a77f17db79ff5101a6fc431ec0615b323510002" + "reference": "a75cc902b101f7540c52dee413b42404d8b6edc4" }, "require": { "allure-framework/allure-codeception": "~1.2.6", @@ -6256,7 +6256,7 @@ "magento", "testing" ], - "time": "2018-08-09T15:55:57+00:00" + "time": "2018-08-10T14:36:01+00:00" }, { "name": "moontoast/math", From b133f312a4e0265f1985197032917128d9bbb5b3 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 10 Aug 2018 10:11:03 -0500 Subject: [PATCH 0889/1171] MAGETWO-90539: Fixed Tax doesn't show in any total or tax field of cart during checkout. --- .../Mftf/ActionGroup/StorefrontProductCartActionGroup.xml | 2 +- .../Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml index 33b83fe63fdc1..3842b3962c12d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -102,7 +102,7 @@ </arguments> <conditionalClick stepKey="openShippingDetails" selector="{{CheckoutCartSummarySection.shippingHeading}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false"/> <selectOption stepKey="selectCountry" selector="{{CheckoutCartSummarySection.country}}" userInput="{{taxCode.country}}"/> - <selectOption stepKey="selectStateProvince" selector="{{CheckoutCartSummarySection.country}}" userInput="{{taxCode.country}}"/> + <selectOption stepKey="selectStateProvince" selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="{{taxCode.state}}"/> <fillField stepKey="fillZip" selector="{{CheckoutCartSummarySection.postcode}}" userInput="{{taxCode.zip}}"/> <waitForPageLoad stepKey="waitForFormUpdate"/> </actionGroup> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml index c0b32e4bc71e7..362af79d8de2d 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml @@ -248,9 +248,6 @@ <severity value="CRITICAL"/> <testCaseId value="MC-297"/> <group value="Tax"/> - <skip> - <issueId value="MAGETWO-90539"/> - </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -357,9 +354,6 @@ <severity value="CRITICAL"/> <testCaseId value="MC-298"/> <group value="Tax"/> - <skip> - <issueId value="MAGETWO-90539"/> - </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> From f32c71935865bd1c7488ebe0f561f4a087c202f9 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Fri, 10 Aug 2018 18:40:40 +0300 Subject: [PATCH 0890/1171] Disabled Magento.Annotation.MethodArguments sniff due to MAGETWO-94083 --- dev/tests/static/framework/Magento/ruleset.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/static/framework/Magento/ruleset.xml b/dev/tests/static/framework/Magento/ruleset.xml index f01715618223b..f3e4893ba752f 100644 --- a/dev/tests/static/framework/Magento/ruleset.xml +++ b/dev/tests/static/framework/Magento/ruleset.xml @@ -23,7 +23,8 @@ <exclude-pattern>*/_files/*</exclude-pattern> </rule> <rule ref="Magento.Annotation.MethodArguments"> - <exclude-pattern>*/_files/*</exclude-pattern> + <!--Disabled due to MAGETWO-94083--> + <exclude-pattern>*</exclude-pattern> </rule> <rule ref="Magento.Functions.OutputBuffering"> <include-pattern>*/(app/code|vendor|setup/src|lib/internal/Magento)/*</include-pattern> From 3947aa2bb8561600f37895af1d98349c447340f7 Mon Sep 17 00:00:00 2001 From: IvanPletnyov <ivan.pletnyov@transoftgroup.com> Date: Fri, 10 Aug 2018 18:54:11 +0300 Subject: [PATCH 0891/1171] MSI-1542: Provide MSI support for Shipment Web API endpoint --- .../Model/Order/ShipmentDocumentFactory.php | 4 +- .../ExtensionAttributesProcessor.php | 35 ++--- .../ExtensionAttributesProcessorTest.php | 132 ------------------ .../Order/ShipmentDocumentFactoryTest.php | 80 +++++++++++ .../Sales/etc/extension_attributes.xml | 16 +++ 5 files changed, 115 insertions(+), 152 deletions(-) delete mode 100644 app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentDocumentFactoryTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/etc/extension_attributes.xml diff --git a/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php index f0998a18966bd..73844a899e262 100644 --- a/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php +++ b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php @@ -42,7 +42,7 @@ class ShipmentDocumentFactory private $hydratorPool; /** - * @var ExtensionAttributesProcessor|null + * @var ExtensionAttributesProcessor */ private $extensionAttributesProcessor; @@ -68,8 +68,6 @@ public function __construct( } /** - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * * @param OrderInterface $order * @param ShipmentItemCreationInterface[] $items * @param ShipmentTrackCreationInterface[] $tracks diff --git a/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessor.php b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessor.php index 3950f714626aa..c39cb44552db1 100644 --- a/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessor.php +++ b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessor.php @@ -7,11 +7,11 @@ namespace Magento\Sales\Model\Order\ShipmentDocumentFactory; -use Magento\Framework\Api\SimpleDataObjectConverter; use Magento\Framework\Reflection\ExtensionAttributesProcessor as AttributesProcessor; use Magento\Sales\Api\Data\ShipmentCreationArgumentsExtensionInterface; use Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface; use Magento\Sales\Api\Data\ShipmentExtensionFactory; +use Magento\Sales\Api\Data\ShipmentExtensionInterface; use Magento\Sales\Api\Data\ShipmentInterface; /** @@ -53,24 +53,25 @@ public function execute( if (null === $arguments) { return; } - - $shipmentExtensionAttributes = $shipment->getExtensionAttributes(); - if (null === $shipmentExtensionAttributes) { - $shipmentExtensionAttributes = $this->shipmentExtensionFactory->create(); + $shipmentExtensionAttributes = []; + if (null !== $shipment->getExtensionAttributes()) { + $shipmentExtensionAttributes = $this->extensionAttributesProcessor->buildOutputDataArray( + $shipment->getExtensionAttributes(), + ShipmentExtensionInterface::class + ); + } + $argumentsExtensionAttributes = []; + if (null !== $arguments->getExtensionAttributes()) { + $argumentsExtensionAttributes = $this->extensionAttributesProcessor->buildOutputDataArray( + $arguments->getExtensionAttributes(), + ShipmentCreationArgumentsExtensionInterface::class + ); } - $attributes = $arguments->getExtensionAttributes(); - $extensionAttributes = $this->extensionAttributesProcessor->buildOutputDataArray( - $attributes, - ShipmentCreationArgumentsExtensionInterface::class - ); - - foreach ($extensionAttributes as $code => $value) { - $setMethod = 'set' . SimpleDataObjectConverter::snakeCaseToUpperCamelCase($code); + $mergedExtensionAttributes = $this->shipmentExtensionFactory->create([ + 'data' => array_merge($shipmentExtensionAttributes, $argumentsExtensionAttributes) + ]); - if (method_exists($shipmentExtensionAttributes, $setMethod)) { - $shipmentExtensionAttributes->$setMethod($value); - } - } + $shipment->setExtensionAttributes($mergedExtensionAttributes); } } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessorTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessorTest.php deleted file mode 100644 index 70237373a8d90..0000000000000 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessorTest.php +++ /dev/null @@ -1,132 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Sales\Test\Unit\Model\Order\ShipmentDocumentFactory; - -use Magento\Framework\Reflection\ExtensionAttributesProcessor as AttributesProcessor; -use Magento\Sales\Api\Data\ShipmentCreationArgumentsExtensionInterface; -use Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface; -use Magento\Sales\Api\Data\ShipmentExtensionFactory; -use Magento\Sales\Api\Data\ShipmentExtensionInterface; -use Magento\Sales\Api\Data\ShipmentInterface; -use Magento\Sales\Model\Order\ShipmentDocumentFactory\ExtensionAttributesProcessor; -use PHPUnit\Framework\TestCase; - -/** - * Provide tests for shipment document factory extension attributes processor. - */ -class ExtensionAttributesProcessorTest extends TestCase -{ - /** - * Test subject. - * - * @var ExtensionAttributesProcessor - */ - private $extensionAttributesProcessor; - - /** - * @var AttributesProcessor|\PHPUnit_Framework_MockObject_MockObject - */ - private $extensionAttributesProcessorMock; - - /** - * @var ShipmentExtensionFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $shipmentExtensionFactoryMock; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->shipmentExtensionFactoryMock = $this->getMockBuilder(ShipmentExtensionFactory::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->extensionAttributesProcessorMock = $this->getMockBuilder(AttributesProcessor::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->extensionAttributesProcessor = new ExtensionAttributesProcessor( - $this->shipmentExtensionFactoryMock, - $this->extensionAttributesProcessorMock - ); - } - - /** - * Build and set extension attributes for shipment with shipment creation arguments. - * - * @return void - */ - public function testExecuteWithParameter(): void - { - /** @var ShipmentInterface|\PHPUnit_Framework_MockObject_MockObject $shipmentMock */ - $shipmentMock = $this->getMockBuilder(ShipmentInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $shipmentMock->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn(null); - - $attributes = $this->getMockBuilder(ShipmentCreationArgumentsExtensionInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - - /** @var ShipmentCreationArgumentsInterface|\PHPUnit_Framework_MockObject_MockObject $argumentsMock */ - $argumentsMock = $this->getMockBuilder(ShipmentCreationArgumentsInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getExtensionAttributes']) - ->getMockForAbstractClass(); - $argumentsMock->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($attributes); - - $shipmentExtensionAttributes = $this->getMockBuilder(ShipmentExtensionInterface::class) - ->disableOriginalConstructor() - ->setMethods(['setTestAttribute']) - ->getMockForAbstractClass(); - $shipmentExtensionAttributes->expects($this->once()) - ->method('setTestAttribute') - ->with('test_value') - ->willReturnSelf(); - - $this->shipmentExtensionFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($shipmentExtensionAttributes); - - $this->extensionAttributesProcessorMock->expects($this->once()) - ->method('buildOutputDataArray') - ->with($attributes, ShipmentCreationArgumentsExtensionInterface::class) - ->willReturn(['test_attribute' => 'test_value']); - - $this->extensionAttributesProcessor->execute($shipmentMock, $argumentsMock); - } - - /** - * Build and set extension attributes for shipment without shipment creation arguments. - * - * @return void - */ - public function testExecuteWithoutParameter(): void - { - /** @var ShipmentInterface|\PHPUnit_Framework_MockObject_MockObject $shipmentMock */ - $shipmentMock = $this->getMockBuilder(ShipmentInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $shipmentMock->expects($this->never()) - ->method('getExtensionAttributes') - ->willReturn(null); - - $this->shipmentExtensionFactoryMock->expects($this->never()) - ->method('create'); - - $this->extensionAttributesProcessorMock->expects($this->never()) - ->method('buildOutputDataArray'); - - $this->extensionAttributesProcessor->execute($shipmentMock, null); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentDocumentFactoryTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentDocumentFactoryTest.php new file mode 100644 index 0000000000000..7f0ec2ae92f89 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentDocumentFactoryTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Model\Order; + +use Magento\Sales\Api\Data\ShipmentCreationArgumentsExtensionInterfaceFactory; +use Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface; +use Magento\Sales\Model\Order; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Provide tests for shipment document factory. + */ +class ShipmentDocumentFactoryTest extends TestCase +{ + /** + * @var Order + */ + private $order; + + /** + * @var ShipmentDocumentFactory + */ + private $shipmentDocumentFactory; + + /** + * @var ShipmentCreationArgumentsInterface + */ + private $shipmentCreationArgumentsInterface; + + /** + * @var ShipmentCreationArgumentsExtensionInterfaceFactory + */ + private $shipmentCreationArgumentsExtensionInterfaceFactory; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $objectManager = Bootstrap::getObjectManager(); + + $this->order = $objectManager->create(Order::class); + $this->shipmentDocumentFactory = $objectManager->create(ShipmentDocumentFactory::class); + $this->shipmentCreationArgumentsInterface = $objectManager + ->create(ShipmentCreationArgumentsInterface::class); + $this->shipmentCreationArgumentsExtensionInterfaceFactory = $objectManager + ->create(ShipmentCreationArgumentsExtensionInterfaceFactory::class); + } + + /** + * Create shipment with shipment creation arguments. + * + * @magentoDataFixture Magento/Sales/_files/order.php + */ + public function testCreate(): void + { + $order = $this->order->loadByIncrementId('100000001'); + $argumentsExtensionAttributes = $this->shipmentCreationArgumentsExtensionInterfaceFactory->create([ + 'data' => ['test_attribute_value' => 'test_value'] + ]); + $this->shipmentCreationArgumentsInterface->setExtensionAttributes($argumentsExtensionAttributes); + $shipment = $this->shipmentDocumentFactory->create( + $order, + [], + [], + null, + false, + [], + $this->shipmentCreationArgumentsInterface + ); + $shipmentExtensionAttributes = $shipment->getExtensionAttributes(); + self::assertEquals('test_value', $shipmentExtensionAttributes->getTestAttributeValue()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/etc/extension_attributes.xml b/dev/tests/integration/testsuite/Magento/Sales/etc/extension_attributes.xml new file mode 100644 index 0000000000000..abefb087024ef --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/etc/extension_attributes.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd"> + <extension_attributes for="Magento\Sales\Api\Data\ShipmentInterface"> + <attribute code="test_attribute_value" type="string"/> + </extension_attributes> + <extension_attributes for="Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface"> + <attribute code="test_attribute_value" type="string"/> + </extension_attributes> +</config> From 4094173f7645cac49731de7870402da046a01844 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 10 Aug 2018 11:24:58 -0500 Subject: [PATCH 0892/1171] MAGETWO-90531: WYSIWYG shows special character button in toolbar on product page - when test module is enabled - corecting docbloc - removing un-necesary Di entry --- .../_files/Magento/TestModuleWysiwygConfig/Model/Config.php | 2 +- .../Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/Model/Config.php b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/Model/Config.php index b80caeed6beed..aceac618f48b9 100644 --- a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/Model/Config.php +++ b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/Model/Config.php @@ -61,7 +61,7 @@ private function modifyHeightAndContentCss(\Magento\Framework\DataObject $config } /** - * Modify height and content_css in the config + * Remove the special character from the toolbar configuration * * @param \Magento\Framework\DataObject $config * @return \Magento\Framework\DataObject diff --git a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml index 1001b40d3bbd4..afbc603caa8b5 100644 --- a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml +++ b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml @@ -14,9 +14,6 @@ <argument name="wysiwygConfigPostProcessor" xsi:type="array"> <item name="mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter" xsi:type="string">Magento\TestModuleWysiwygConfig\Model\Config</item> </argument> - <argument name="wysiwygConfigPostProcessor" xsi:type="array"> - <item name="default" xsi:type="string">Magento\TestModuleWysiwygConfig\Model\Config</item> - </argument> <argument name="variablePluginConfigProvider" xsi:type="array"> <item name="testAdapter" xsi:type="string">Magento\Variable\Model\Variable\ConfigProvider</item> </argument> From cea62ecf191f706b6cdef82973ac794989bb80de Mon Sep 17 00:00:00 2001 From: teddysie <godbless_tedy@yahoo.co.id> Date: Mon, 30 Jul 2018 16:14:54 +0700 Subject: [PATCH 0893/1171] Magento 2.2.5: Year-to-date dropdown in Stores>Configuration>General>Reports>Dashboard #17289 --- .../Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php b/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php index 5a27b4dc7666f..b3d6be5e6ebdb 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php @@ -23,9 +23,8 @@ protected function _getElementHtml(AbstractElement $element) { $_months = []; for ($i = 1; $i <= 12; $i++) { - $_months[$i] = $this->_localeDate->date(mktime(null, null, null, $i))->format('m'); + $_months[$i] = $this->_localeDate->date(mktime(null, null, null, $i,1))->format('m'); } - $_days = []; for ($i = 1; $i <= 31; $i++) { $_days[$i] = $i < 10 ? '0' . $i : $i; From 1c70659dc409ba6a369a23542dd2a9fe2be7b683 Mon Sep 17 00:00:00 2001 From: Ronak Patel <11473750+ronak2ram@users.noreply.github.com> Date: Tue, 7 Aug 2018 19:20:00 +0530 Subject: [PATCH 0894/1171] Update YtdStart.php --- .../Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php b/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php index b3d6be5e6ebdb..4ac12501aa90f 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php @@ -23,7 +23,7 @@ protected function _getElementHtml(AbstractElement $element) { $_months = []; for ($i = 1; $i <= 12; $i++) { - $_months[$i] = $this->_localeDate->date(mktime(null, null, null, $i,1))->format('m'); + $_months[$i] = $this->_localeDate->date(mktime(null, null, null, $i, 1))->format('m'); } $_days = []; for ($i = 1; $i <= 31; $i++) { From 811c6cabc5d25f5e33b1830e4b8ca3f9ea989757 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Fri, 10 Aug 2018 11:55:47 -0500 Subject: [PATCH 0895/1171] MQE-1172: Bump MFTF version and deliver Magento branches - mftf bump --- composer.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.lock b/composer.lock index 04bee0ae7be60..97a8da0e5934b 100644 --- a/composer.lock +++ b/composer.lock @@ -6187,7 +6187,7 @@ "source": { "type": "git", "url": "git@github.com:magento/magento2-functional-testing-framework.git", - "reference": "a75cc902b101f7540c52dee413b42404d8b6edc4" + "reference": "57882535a697c3cb9f259a396ad6f5bf04d5f15a" }, "require": { "allure-framework/allure-codeception": "~1.2.6", @@ -6256,7 +6256,7 @@ "magento", "testing" ], - "time": "2018-08-10T14:36:01+00:00" + "time": "2018-08-10T16:53:07+00:00" }, { "name": "moontoast/math", From 3ab543e61ad5118f57d827ad617fdcd66697cf0e Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Thu, 9 Aug 2018 00:28:44 +0300 Subject: [PATCH 0896/1171] Added unit test for AvailabilityChecker --- .../CreditCard/AvailabilityCheckerTest.php | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/AvailabilityCheckerTest.php diff --git a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/AvailabilityCheckerTest.php b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/AvailabilityCheckerTest.php new file mode 100644 index 0000000000000..9e3f58f2ffe5c --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/AvailabilityCheckerTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Test\Unit\Model\InstantPurchase\CreditCard; + +use Magento\Braintree\Gateway\Config\Config; +use Magento\Braintree\Model\InstantPurchase\CreditCard\AvailabilityChecker; + +/** + * @covers \Magento\Braintree\Model\InstantPurchase\CreditCard\AvailabilityChecker + */ +class AvailabilityCheckerTest extends \PHPUnit\Framework\TestCase +{ + /** + * Testable Object + * + * @var AvailabilityChecker + */ + private $availabilityChecker; + + /** + * @var Config|\PHPUnit_Framework_MockObject_MockObject + */ + private $configMock; + + /** + * Se Up + * + * @return void + */ + protected function setUp() + { + $this->configMock = $this->createMock(Config::class); + $this->availabilityChecker = new AvailabilityChecker($this->configMock); + } + + /** + * Test isAvailable method + * + * @dataProvider isAvailableDataProvider + * + * @param bool $isVerify3DSecure + * @param bool $expected + * + * @return void + */ + public function testIsAvailable(bool $isVerify3DSecure, bool $expected) + { + $this->configMock->expects($this->once())->method('isVerify3DSecure')->willReturn($isVerify3DSecure); + $actual = $this->availabilityChecker->isAvailable(); + self::assertEquals($expected, $actual); + } + + /** + * Data provider for isAvailable method test + * + * @return array + */ + public function isAvailableDataProvider() + { + return [ + [true, false], + [false, true], + ]; + } +} From 2d85db35a88da101dcbc0318c6c711d8fd3e50a6 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Thu, 9 Aug 2018 09:25:47 +0300 Subject: [PATCH 0897/1171] Braintree: Add unit test for PaymentAdditionalInformationProvider --- ...ymentAdditionalInformationProviderTest.php | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PaymentAdditionalInformationProviderTest.php diff --git a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PaymentAdditionalInformationProviderTest.php b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PaymentAdditionalInformationProviderTest.php new file mode 100644 index 0000000000000..1d2aa9df5bcf3 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PaymentAdditionalInformationProviderTest.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Test\Unit\Model\InstantPurchase\CreditCard; + +use Magento\Braintree\Gateway\Command\GetPaymentNonceCommand; +use Magento\Braintree\Model\InstantPurchase\PaymentAdditionalInformationProvider; +use Magento\Payment\Gateway\Command\Result\ArrayResult; +use Magento\Vault\Api\Data\PaymentTokenInterface; + +/** + * @covers \Magento\Braintree\Model\InstantPurchase\PaymentAdditionalInformationProvider + */ +class PaymentAdditionalInformationProviderTest extends \PHPUnit\Framework\TestCase +{ + /** + * Testable Object + * + * @var PaymentAdditionalInformationProvider + */ + private $paymentAdditionalInformationProvider; + + /** + * @var GetPaymentNonceCommand|\PHPUnit_Framework_MockObject_MockObject + */ + private $getPaymentNonceCommandMock; + + /** + * @var PaymentTokenInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $paymentTokenMock; + + /** + * @var ArrayResult|\PHPUnit_Framework_MockObject_MockObject + */ + private $arrayResultMock; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->getPaymentNonceCommandMock = $this->createMock(GetPaymentNonceCommand::class); + $this->paymentTokenMock = $this->createMock(PaymentTokenInterface::class); + $this->arrayResultMock = $this->createMock(ArrayResult::class); + $this->paymentAdditionalInformationProvider = new PaymentAdditionalInformationProvider( + $this->getPaymentNonceCommandMock + ); + } + + /** + * Test getAdditionalInformation method + * + * @return void + */ + public function testGetAdditionalInformation() + { + $customerId = 15; + $publicHash = '3n4b7sn48g'; + $paymentMethodNonce = 'test'; + + $this->paymentTokenMock->expects($this->once())->method('getCustomerId')->willReturn($customerId); + $this->paymentTokenMock->expects($this->once())->method('getPublicHash')->willReturn($publicHash); + $this->getPaymentNonceCommandMock->expects($this->once())->method('execute')->with([ + PaymentTokenInterface::CUSTOMER_ID => $customerId, + PaymentTokenInterface::PUBLIC_HASH => $publicHash, + ])->willReturn($this->arrayResultMock); + $this->arrayResultMock->expects($this->once())->method('get') + ->willReturn(['paymentMethodNonce' => $paymentMethodNonce]); + + $expected = [ + 'payment_method_nonce' => $paymentMethodNonce, + ]; + $actual = $this->paymentAdditionalInformationProvider->getAdditionalInformation($this->paymentTokenMock); + self::assertEquals($expected, $actual); + } +} From 13fc94be83d249ebaa903f8887f743a263a14e28 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Thu, 9 Aug 2018 09:31:23 +0300 Subject: [PATCH 0898/1171] Fix typo --- .../InstantPurchase/CreditCard/AvailabilityCheckerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/AvailabilityCheckerTest.php b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/AvailabilityCheckerTest.php index 9e3f58f2ffe5c..2248aab1aad2e 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/AvailabilityCheckerTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/AvailabilityCheckerTest.php @@ -28,7 +28,7 @@ class AvailabilityCheckerTest extends \PHPUnit\Framework\TestCase private $configMock; /** - * Se Up + * Set Up * * @return void */ From 23f12b960f8ae728c4d5ce6bdf7232a4822dd2ea Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Thu, 9 Aug 2018 09:56:33 +0300 Subject: [PATCH 0899/1171] Braintree: Add unit test for LocaleResolver --- .../Test/Unit/Model/LocaleResolverTest.php | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 app/code/Magento/Braintree/Test/Unit/Model/LocaleResolverTest.php diff --git a/app/code/Magento/Braintree/Test/Unit/Model/LocaleResolverTest.php b/app/code/Magento/Braintree/Test/Unit/Model/LocaleResolverTest.php new file mode 100644 index 0000000000000..b6ef534c55c29 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Model/LocaleResolverTest.php @@ -0,0 +1,138 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Test\Unit\Model; + +use Magento\Braintree\Gateway\Config\PayPal\Config; +use Magento\Braintree\Model\LocaleResolver; +use Magento\Framework\Locale\ResolverInterface; + +/** + * @covers \Magento\Braintree\Model\LocaleResolver + */ +class LocaleResolverTest extends \PHPUnit\Framework\TestCase +{ + /** + * Testable Object + * + * @var LocaleResolver + */ + private $localeResolver; + + /** + * @var Config|\PHPUnit_Framework_MockObject_MockObject + */ + private $configMock; + + /** + * @var ResolverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $resolverMock; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->configMock = $this->createMock(Config::class); + $this->resolverMock = $this->createMock(ResolverInterface::class); + $this->localeResolver = new LocaleResolver($this->resolverMock, $this->configMock); + } + + /** + * Test getDefaultLocalePath method + * + * @return void + */ + public function testGetDefaultLocalePath() + { + $expected = 'general/locale/code'; + $this->resolverMock->expects($this->once())->method('getDefaultLocalePath')->willReturn($expected); + $actual = $this->localeResolver->getDefaultLocalePath(); + self::assertEquals($expected, $actual); + } + + /** + * Test setDefaultLocale method + * + * @return void + */ + public function testSetDefaultLocale() + { + $defaultLocale = 'en_US'; + $this->resolverMock->expects($this->once())->method('setDefaultLocale')->with($defaultLocale); + $this->localeResolver->setDefaultLocale($defaultLocale); + } + + /** + * Test getDefaultLocale method + * + * @return void + */ + public function testGetDefaultLocale() + { + $expected = 'fr_FR'; + $this->resolverMock->expects($this->once())->method('getDefaultLocale')->willReturn($expected); + $actual = $this->localeResolver->getDefaultLocale(); + self::assertEquals($expected, $actual); + } + + /** + * Test setLocale method + * + * @return void + */ + public function testSetLocale() + { + $locale = 'en_GB'; + $this->resolverMock->expects($this->once())->method('setLocale')->with($locale); + $this->localeResolver->setLocale($locale); + } + + /** + * Test getLocale method + * + * @return void + */ + public function testGetLocale() + { + $locale = 'en_TEST'; + $allowedLocales = 'en_US,en_GB,en_AU,da_DK,fr_FR,fr_CA,de_DE,zh_HK,it_IT,nl_NL'; + $this->resolverMock->expects($this->once())->method('getLocale')->willReturn($locale); + $this->configMock->expects($this->once())->method('getValue')->with('supported_locales') + ->willReturn($allowedLocales); + + $expected = 'en_US'; + $actual = $this->localeResolver->getLocale(); + self::assertEquals($expected, $actual); + } + + /** + * Test emulate method + * + * @return void + */ + public function testEmulate() + { + $scopeId = 12; + $this->resolverMock->expects($this->once())->method('emulate')->with($scopeId); + $this->localeResolver->emulate($scopeId); + } + + /** + * Test revert method + * + * @return void + */ + public function testRevert() + { + $this->resolverMock->expects($this->once())->method('revert'); + $this->localeResolver->revert(); + } +} From 2892b05146f1e63d5db43df7afea41fded934db4 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Thu, 9 Aug 2018 09:59:25 +0300 Subject: [PATCH 0900/1171] Update namespace --- .../PaymentAdditionalInformationProviderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PaymentAdditionalInformationProviderTest.php b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PaymentAdditionalInformationProviderTest.php index 1d2aa9df5bcf3..2631fcbe5f5b5 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PaymentAdditionalInformationProviderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PaymentAdditionalInformationProviderTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\Braintree\Test\Unit\Model\InstantPurchase\CreditCard; +namespace Magento\Braintree\Test\Unit\Model\InstantPurchase; use Magento\Braintree\Gateway\Command\GetPaymentNonceCommand; use Magento\Braintree\Model\InstantPurchase\PaymentAdditionalInformationProvider; From da81fccdf19fe8419e15dcadb618ce23c12dce24 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Fri, 10 Aug 2018 12:23:12 -0500 Subject: [PATCH 0901/1171] MQE-1172: Bump MFTF version and deliver Magento branches - mftf bump --- composer.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.lock b/composer.lock index 97a8da0e5934b..0f1ab65e9d9a5 100644 --- a/composer.lock +++ b/composer.lock @@ -6187,7 +6187,7 @@ "source": { "type": "git", "url": "git@github.com:magento/magento2-functional-testing-framework.git", - "reference": "57882535a697c3cb9f259a396ad6f5bf04d5f15a" + "reference": "2353428fedf1d0883d17df1679cdc83295c8157d" }, "require": { "allure-framework/allure-codeception": "~1.2.6", @@ -6256,7 +6256,7 @@ "magento", "testing" ], - "time": "2018-08-10T16:53:07+00:00" + "time": "2018-08-10T17:19:41+00:00" }, { "name": "moontoast/math", From 9ecc8ea1b3a60c3c5781a2db5166c63008304736 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Fri, 10 Aug 2018 12:26:51 -0500 Subject: [PATCH 0902/1171] MAGETWO-90591: Add Selected Button shouldn't be enabled if there are no images available - fix case that "Add Selected" button still appears after deleted selected image --- lib/web/mage/adminhtml/browser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 26d6679bb3ce5..13e7edac2a0c5 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -434,7 +434,7 @@ define([ showLoader: true }).done($.proxy(function () { self.reload(); - self.element.find('#delete_files').toggleClass(self.options.hidden, true); + self.element.find('#delete_files, #insert_files').toggleClass(self.options.hidden, true); }, this)); }, From a8e5de8f824a85df90464959c90b581baed99bd8 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Fri, 10 Aug 2018 12:34:21 -0500 Subject: [PATCH 0903/1171] MAGETWO-90632: DHL Shipping Method not available - Fix static test failure --- app/code/Magento/Dhl/Model/Carrier.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Dhl/Model/Carrier.php b/app/code/Magento/Dhl/Model/Carrier.php index 55df0748b1f06..d997db6ac1a3e 100644 --- a/app/code/Magento/Dhl/Model/Carrier.php +++ b/app/code/Magento/Dhl/Model/Carrier.php @@ -232,7 +232,7 @@ class Carrier extends \Magento\Dhl\Model\AbstractDhl implements \Magento\Shippin * @param \Magento\Framework\Stdlib\DateTime $dateTime * @param \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory * @param array $data - * @param \Magento\Dhl\Model\Validator\XmlValidatorFactory $xmlValidatorFactory + * @param \Magento\Dhl\Model\Validator\XmlValidator $xmlValidator * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( From 6603a243fed1f6c0ce8eace51083fdae2108c00c Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Sat, 28 Jul 2018 16:17:22 +0200 Subject: [PATCH 0904/1171] Improve code quality for Subscriber/NewAction + Refactor `else` block for returning success message to private function + Use import statements for classes + Because imports are being used an `if` statement can be written on one line + Use `addExceptionMessage` and `addSuccessMessage` instead of their deprecated counterparts + Include with DI the `Magento\Framework\Validator\EmailAddress` class which can be used to validate the email address. Therefore the static function call to the `is` method on the `Zend_Validate` class is not needed anymore. --- .../Controller/Subscriber/NewAction.php | 72 ++++++++++++------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php b/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php index 13ab40665e591..fd2a61702e909 100644 --- a/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php +++ b/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php @@ -10,19 +10,32 @@ use Magento\Customer\Model\Session; use Magento\Customer\Model\Url as CustomerUrl; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Phrase; +use Magento\Framework\Validator\EmailAddress as EmailValidator; +use Magento\Newsletter\Controller\Subscriber as SubscriberController; +use Magento\Newsletter\Model\Subscriber; +use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Newsletter\Model\SubscriberFactory; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class NewAction extends \Magento\Newsletter\Controller\Subscriber +class NewAction extends SubscriberController { /** * @var CustomerAccountManagement */ protected $customerAccountManagement; + /** + * @var EmailValidator + */ + private $emailValidator; + /** * Initialize dependencies. * @@ -32,6 +45,7 @@ class NewAction extends \Magento\Newsletter\Controller\Subscriber * @param StoreManagerInterface $storeManager * @param CustomerUrl $customerUrl * @param CustomerAccountManagement $customerAccountManagement + * @param EmailValidator $emailValidator */ public function __construct( Context $context, @@ -39,9 +53,11 @@ public function __construct( Session $customerSession, StoreManagerInterface $storeManager, CustomerUrl $customerUrl, - CustomerAccountManagement $customerAccountManagement + CustomerAccountManagement $customerAccountManagement, + EmailValidator $emailValidator = null ) { $this->customerAccountManagement = $customerAccountManagement; + $this->emailValidator = $emailValidator ?: ObjectManager::getInstance()->get(EmailValidator::class); parent::__construct( $context, $subscriberFactory, @@ -55,7 +71,7 @@ public function __construct( * Validates that the email address isn't being used by a different account. * * @param string $email - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException * @return void */ protected function validateEmailAvailable($email) @@ -64,7 +80,7 @@ protected function validateEmailAvailable($email) if ($this->_customerSession->getCustomerDataObject()->getEmail() !== $email && !$this->customerAccountManagement->isEmailAvailable($email, $websiteId) ) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __('This email address is already assigned to another user.') ); } @@ -73,19 +89,19 @@ protected function validateEmailAvailable($email) /** * Validates that if the current user is a guest, that they can subscribe to a newsletter. * - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException * @return void */ protected function validateGuestSubscription() { - if ($this->_objectManager->get(\Magento\Framework\App\Config\ScopeConfigInterface::class) + if ($this->_objectManager->get(ScopeConfigInterface::class) ->getValue( - \Magento\Newsletter\Model\Subscriber::XML_PATH_ALLOW_GUEST_SUBSCRIBE_FLAG, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + Subscriber::XML_PATH_ALLOW_GUEST_SUBSCRIBE_FLAG, + ScopeInterface::SCOPE_STORE ) != 1 && !$this->_customerSession->isLoggedIn() ) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __( 'Sorry, but the administrator denied subscription for guests. Please <a href="%1">register</a>.', $this->_customerUrl->getRegisterUrl() @@ -98,20 +114,19 @@ protected function validateGuestSubscription() * Validates the format of the email address * * @param string $email - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException * @return void */ protected function validateEmailFormat($email) { - if (!\Zend_Validate::is($email, \Magento\Framework\Validator\EmailAddress::class)) { - throw new \Magento\Framework\Exception\LocalizedException(__('Please enter a valid email address.')); + if (!$this->emailValidator->isValid($email)) { + throw new LocalizedException(__('Please enter a valid email address.')); } } /** * New subscription action * - * @throws \Magento\Framework\Exception\LocalizedException * @return void */ public function execute() @@ -126,28 +141,37 @@ public function execute() $subscriber = $this->_subscriberFactory->create()->loadByEmail($email); if ($subscriber->getId() - && $subscriber->getSubscriberStatus() == \Magento\Newsletter\Model\Subscriber::STATUS_SUBSCRIBED + && (int) $subscriber->getSubscriberStatus() === Subscriber::STATUS_SUBSCRIBED ) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __('This email address is already subscribed.') ); } - $status = $this->_subscriberFactory->create()->subscribe($email); - if ($status == \Magento\Newsletter\Model\Subscriber::STATUS_NOT_ACTIVE) { - $this->messageManager->addSuccess(__('The confirmation request has been sent.')); - } else { - $this->messageManager->addSuccess(__('Thank you for your subscription.')); - } - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addException( + $status = (int) $this->_subscriberFactory->create()->subscribe($email); + $this->messageManager->addSuccessMessage($this->getSuccessMessage($status)); + } catch (LocalizedException $e) { + $this->messageManager->addExceptionMessage( $e, __('There was a problem with the subscription: %1', $e->getMessage()) ); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Something went wrong with the subscription.')); + $this->messageManager->addExceptionMessage($e, __('Something went wrong with the subscription.')); } } $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl()); } + + /** + * @param int $status + * @return Phrase + */ + private function getSuccessMessage(int $status): Phrase + { + if ($status === Subscriber::STATUS_NOT_ACTIVE) { + return __('The confirmation request has been sent.'); + } + + return __('Thank you for your subscription.'); + } } From 5c41898712935b205ba66df17b9f19b070f82717 Mon Sep 17 00:00:00 2001 From: Vital_Pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Fri, 10 Aug 2018 21:48:41 +0300 Subject: [PATCH 0905/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Add unit test in case Expired session --- .../Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php b/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php index d2878a7c8dc67..a9c9ecd23d97b 100644 --- a/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php @@ -34,13 +34,18 @@ class AddTest extends \PHPUnit\Framework\TestCase */ private $messageManager; + /** + * Init mocks for tests. + * + * @return void + */ public function setUp() { $this->formKeyValidator = $this->getMockBuilder(\Magento\Framework\Data\Form\FormKey\Validator::class) ->disableOriginalConstructor()->getMock(); $this->resultRedirectFactory = $this->getMockBuilder(\Magento\Framework\Controller\Result\RedirectFactory::class) - ->disableOriginalConstructor()->getMock(); + ->disableOriginalConstructor()->getMock(); $this->request = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) ->disableOriginalConstructor()->getmock(); $this->messageManager = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) From eb614455d13ea9a2d8d35d84d40d8fb50487470b Mon Sep 17 00:00:00 2001 From: Vital_Pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Fri, 10 Aug 2018 22:07:34 +0300 Subject: [PATCH 0906/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Add unit test in case Expired session --- .../Checkout/Test/Unit/Controller/Cart/AddTest.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php b/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php index a9c9ecd23d97b..9fcfdf438f8ea 100644 --- a/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php @@ -34,6 +34,11 @@ class AddTest extends \PHPUnit\Framework\TestCase */ private $messageManager; + /** + * @var \Magento\Checkout\Controller\Cart\Add | \PHPUnit_Framework_MockObject_MockObject + */ + private $cartAdd; + /** * Init mocks for tests. * @@ -52,7 +57,7 @@ public function setUp() ->disableOriginalConstructor()->getMock(); $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->add = $this->objectManagerHelper->getObject( + $this->cartAdd = $this->objectManagerHelper->getObject( \Magento\Checkout\Controller\Cart\Add::class, [ '_formKeyValidator' => $this->formKeyValidator, @@ -79,6 +84,6 @@ public function testExecute() $this->messageManager->expects($this->once())->method('addErrorMessage'); $this->resultRedirectFactory->expects($this->once())->method('create')->willReturn($redirect); $redirect->expects($this->once())->method('setPath')->with($path)->willReturnSelf(); - $this->assertEquals($redirect, $this->add->execute()); + $this->assertEquals($redirect, $this->cartAdd->execute()); } } From 2f189c0323369ebc6847cd3d88dbbc7e7cc53c59 Mon Sep 17 00:00:00 2001 From: Igor Melnykov <melnykov@adobe.com> Date: Fri, 10 Aug 2018 14:42:56 -0500 Subject: [PATCH 0907/1171] MAGETWO-94083: Impossible get information about static errors on PR - create empty report file for the case when there no php and phtml files --- dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php index fa62179ed34f5..371b8d237d6b9 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php @@ -260,8 +260,11 @@ public function testCodeStyle() { $isFullScan = defined('TESTCODESTYLE_IS_FULL_SCAN') && TESTCODESTYLE_IS_FULL_SCAN === '1'; $reportFile = self::$reportDir . '/phpcs_report.txt'; + if (!file_exists($reportFile)) { + touch($reportFile); + } $codeSniffer = new CodeSniffer('Magento', $reportFile, new Wrapper()); - $result = $codeSniffer->run($isFullScan ? $this->getFullWhitelist() : self::getWhitelist(['php', 'phtml'])); + $result = $codeSniffer->run($isFullScan ? $this->getFullWhitelist() : []); $report = file_get_contents($reportFile); $this->assertEquals( 0, From ddf25093d4bef2b522f118279d45caf02b4d0faa Mon Sep 17 00:00:00 2001 From: Glenn <jetaimemia@gmail.com> Date: Mon, 16 Jul 2018 16:40:05 +0800 Subject: [PATCH 0908/1171] fix: Doesn't work if use date as condition for Catalog Price Rules --- app/code/Magento/CatalogRule/Model/Rule/Condition/Product.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/CatalogRule/Model/Rule/Condition/Product.php b/app/code/Magento/CatalogRule/Model/Rule/Condition/Product.php index 51cb6638af48b..ab650c94a0f08 100644 --- a/app/code/Magento/CatalogRule/Model/Rule/Condition/Product.php +++ b/app/code/Magento/CatalogRule/Model/Rule/Condition/Product.php @@ -99,6 +99,10 @@ protected function _prepareDatetimeValue($value, \Magento\Framework\Model\Abstra { $attribute = $model->getResource()->getAttribute($this->getAttribute()); if ($attribute && $attribute->getBackendType() == 'datetime') { + if (!$value) { + return null; + } + $this->setValue(strtotime($this->getValue())); $value = strtotime($value); } From 68da866e530d074bd337d7d34970f6f99aa2c7c1 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Fri, 10 Aug 2018 15:22:04 -0500 Subject: [PATCH 0909/1171] MQE-1172: Bump MFTF version and deliver Magento branches - Updated MFTF to 2.3.4, lock file update --- composer.json | 8 +------- composer.lock | 31 ++++++++++++------------------- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/composer.json b/composer.json index e22cb325a4d40..3f0c765bfaef5 100644 --- a/composer.json +++ b/composer.json @@ -80,14 +80,8 @@ "zendframework/zend-validator": "^2.6.0", "zendframework/zend-view": "~2.10.0" }, - "repositories": [ - { - "type": "git", - "url": "git@github.com:magento/magento2-functional-testing-framework.git" - } - ], "require-dev": { - "magento/magento2-functional-testing-framework": "dev-MQE-1181", + "magento/magento2-functional-testing-framework": "2.3.4", "friendsofphp/php-cs-fixer": "~2.12.0", "lusitanian/oauth": "~0.8.10", "pdepend/pdepend": "2.5.2", diff --git a/composer.lock b/composer.lock index 0f1ab65e9d9a5..39c9e37ce833d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "33d00be920e1a33f354757d8956be27c", + "content-hash": "2dd0a131df2b0f816b7dea047b3c136e", "packages": [ { "name": "braintree/braintree_php", @@ -6183,11 +6183,17 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "dev-MQE-1181", + "version": "2.3.4", "source": { "type": "git", - "url": "git@github.com:magento/magento2-functional-testing-framework.git", - "reference": "2353428fedf1d0883d17df1679cdc83295c8157d" + "url": "https://github.com/magento/magento2-functional-testing-framework.git", + "reference": "ac56e5a6520dd580658034ae53d3724985c2a901" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/ac56e5a6520dd580658034ae53d3724985c2a901", + "reference": "ac56e5a6520dd580658034ae53d3724985c2a901", + "shasum": "" }, "require": { "allure-framework/allure-codeception": "~1.2.6", @@ -6233,19 +6239,7 @@ "MFTF\\": "dev/tests/functional/MFTF" } }, - "autoload-dev": { - "psr-4": { - "tests\\unit\\": "dev/tests/unit" - } - }, - "scripts": { - "tests": [ - "bin/phpunit-checks" - ], - "static": [ - "bin/static-checks" - ] - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "AGPL-3.0" ], @@ -6256,7 +6250,7 @@ "magento", "testing" ], - "time": "2018-08-10T17:19:41+00:00" + "time": "2018-08-10T20:16:42+00:00" }, { "name": "moontoast/math", @@ -8879,7 +8873,6 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "magento/magento2-functional-testing-framework": 20, "phpmd/phpmd": 0 }, "prefer-stable": true, From 39c18483b630e1aa5ca4afbf6938ec2b2a416a89 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 10 Aug 2018 16:29:49 -0500 Subject: [PATCH 0910/1171] MAGETWO-90531: WYSIWYG shows special character button in toolbar on product page - when test module is enabled - fix php static --- .../testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php index df33afa214bc3..498fc044faacf 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php @@ -18,6 +18,9 @@ class ConfigTest extends \PHPUnit\Framework\TestCase */ private $model; + /** + * {@inheritdoc} + */ protected function setUp() { $objectManager = Bootstrap::getObjectManager(); @@ -26,6 +29,8 @@ protected function setUp() /** * Tests that config returns valid config array in it + * + * @return void */ public function testGetConfig() { @@ -35,6 +40,8 @@ public function testGetConfig() /** * Tests that config returns right urls going to the published library path + * + * @return void */ public function testGetConfigCssUrls() { @@ -53,6 +60,7 @@ public function testGetConfigCssUrls() /** * Test enabled module is able to modify WYSIWYG config + * * @return void * * @magentoConfigFixture default/cms/wysiwyg/editor testAdapter From 46c300ed57e995e6923f72d66dc30ede04b8546e Mon Sep 17 00:00:00 2001 From: Igor Melnykov <melnykov@adobe.com> Date: Fri, 10 Aug 2018 17:55:13 -0500 Subject: [PATCH 0911/1171] MAGETWO-94083: Impossible get information about static errors on PR - create empty report file for the case when there no php and phtml files --- dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php index 371b8d237d6b9..1bcb99dd48bd7 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php @@ -264,7 +264,7 @@ public function testCodeStyle() touch($reportFile); } $codeSniffer = new CodeSniffer('Magento', $reportFile, new Wrapper()); - $result = $codeSniffer->run($isFullScan ? $this->getFullWhitelist() : []); + $result = $codeSniffer->run($isFullScan ? $this->getFullWhitelist() : self::getWhitelist(['php', 'phtml'])); $report = file_get_contents($reportFile); $this->assertEquals( 0, From 9db2bd05dbf0a1a7a5b4986bdb9091f4f2d2c1f8 Mon Sep 17 00:00:00 2001 From: Igor Melnykov <melnykov@adobe.com> Date: Fri, 10 Aug 2018 19:42:00 -0500 Subject: [PATCH 0912/1171] MAGETWO-94083: Impossible get information about static errors on PR - create empty report file for the case when there no php and phtml files --- .../static/testsuite/Magento/Test/Php/LiveCodeTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php index 1bcb99dd48bd7..21ca0a495dd19 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php @@ -256,6 +256,9 @@ private function getFullWhitelist() } } + /** + * Test code quality using phpcs + */ public function testCodeStyle() { $isFullScan = defined('TESTCODESTYLE_IS_FULL_SCAN') && TESTCODESTYLE_IS_FULL_SCAN === '1'; @@ -273,6 +276,9 @@ public function testCodeStyle() ); } + /** + * Test code quality using phpmd + */ public function testCodeMess() { $reportFile = self::$reportDir . '/phpmd_report.txt'; @@ -301,6 +307,9 @@ public function testCodeMess() } } + /** + * Test code quality using phpcpd + */ public function testCopyPaste() { $reportFile = self::$reportDir . '/phpcpd_report.xml'; From b9b94765c2750ac101bab90c093a8b0d98e960c5 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Sat, 11 Aug 2018 11:43:45 +0530 Subject: [PATCH 0913/1171] Added translation for comment tag --- app/code/Magento/Ui/etc/adminhtml/system.xml | 4 +-- app/code/Magento/Ui/i18n/en_US.csv | 26 +++++-------------- .../base/ui_component/etc/definition.map.xml | 17 ++++-------- .../WebapiSecurity/etc/adminhtml/system.xml | 4 +-- .../Magento/WebapiSecurity/i18n/en_US.csv | 1 + .../Magento/Wishlist/etc/adminhtml/system.xml | 4 +-- app/code/Magento/Wishlist/i18n/en_US.csv | 2 ++ 7 files changed, 21 insertions(+), 37 deletions(-) diff --git a/app/code/Magento/Ui/etc/adminhtml/system.xml b/app/code/Magento/Ui/etc/adminhtml/system.xml index 74e2e076b93c8..ab4272f8d2a34 100644 --- a/app/code/Magento/Ui/etc/adminhtml/system.xml +++ b/app/code/Magento/Ui/etc/adminhtml/system.xml @@ -9,12 +9,12 @@ <system> <section id="dev"> <group id="js"> - <field id="session_storage_logging" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="session_storage_logging" translate="label comment" type="select" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Log JS Errors to Session Storage</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>If enabled, can be used by functional tests for extended reporting</comment> </field> - <field id="session_storage_key" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="session_storage_key" translate="label comment" type="text" sortOrder="110" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Log JS Errors to Session Storage Key</label> <comment>Use this key to retrieve collected js errors</comment> </field> diff --git a/app/code/Magento/Ui/i18n/en_US.csv b/app/code/Magento/Ui/i18n/en_US.csv index ed4386f6000c3..106526f66e708 100644 --- a/app/code/Magento/Ui/i18n/en_US.csv +++ b/app/code/Magento/Ui/i18n/en_US.csv @@ -125,30 +125,18 @@ Ok,Ok "Label Actions Two","Label Actions Two" "Some Dynamic Actions","Some Dynamic Actions" "Log JS Errors to Session Storage","Log JS Errors to Session Storage" +"If enabled, can be used by functional tests for extended reporting","If enabled, can be used by functional tests for extended reporting" "Log JS Errors to Session Storage Key","Log JS Errors to Session Storage Key" +"Use this key to retrieve collected js errors","Use this key to retrieve collected js errors" settings/label,settings/label settings/confirm/title,settings/confirm/title -"settings/confirm/message - ","settings/confirm/message - " -" - settings/callback/provider - "," - settings/callback/provider - " -"settings/callback/target - ","settings/callback/target - " -"settings/newViewLabel - ","settings/newViewLabel - " +"settings/confirm/message","settings/confirm/message" +"settings/callback/provider","settings/callback/provider" +"settings/callback/target","settings/callback/target" +"settings/newViewLabel","settings/newViewLabel" settings/title,settings/title settings/description,settings/description -" - settings/addButtonLabel - "," - settings/addButtonLabel - " +"settings/addButtonLabel","settings/addButtonLabel" " formElements/*[name(.)=../../@formElement]/settings/description "," diff --git a/app/code/Magento/Ui/view/base/ui_component/etc/definition.map.xml b/app/code/Magento/Ui/view/base/ui_component/etc/definition.map.xml index 94272f723ec77..ccd702c23ea65 100644 --- a/app/code/Magento/Ui/view/base/ui_component/etc/definition.map.xml +++ b/app/code/Magento/Ui/view/base/ui_component/etc/definition.map.xml @@ -16,15 +16,11 @@ <item name="url" type="url" xsi:type="converter">settings/url</item> <item name="confirm" xsi:type="array"> <item name="title" type="string" translate="true" xsi:type="xpath">settings/confirm/title</item> - <item name="message" type="string" translate="true" xsi:type="xpath">settings/confirm/message - </item> + <item name="message" type="string" translate="true" xsi:type="xpath">settings/confirm/message</item> </item> <item name="callback" xsi:type="array"> - <item name="provider" type="string" translate="true" xsi:type="xpath"> - settings/callback/provider - </item> - <item name="target" type="string" translate="true" xsi:type="xpath">settings/callback/target - </item> + <item name="provider" type="string" translate="true" xsi:type="xpath">settings/callback/provider</item> + <item name="target" type="string" translate="true" xsi:type="xpath">settings/callback/target</item> </item> </item> </argument> @@ -59,8 +55,7 @@ <item name="config" xsi:type="array"> <item name="childDefaults" type="item" xsi:type="converter">settings/childDefaults</item> <item name="viewTmpl" type="string" xsi:type="xpath">settings/viewTmpl</item> - <item name="newViewLabel" translate="true" type="string" xsi:type="xpath">settings/newViewLabel - </item> + <item name="newViewLabel" translate="true" type="string" xsi:type="xpath">settings/newViewLabel</item> </item> </argument> </schema> @@ -265,9 +260,7 @@ <item name="columnsHeaderClasses" type="string" xsi:type="xpath">settings/columnsHeaderClasses</item> <item name="recordTemplate" type="string" xsi:type="xpath">settings/recordTemplate</item> <item name="collapsibleHeader" type="boolean" xsi:type="xpath">settings/collapsibleHeader</item> - <item name="addButtonLabel" type="string" translate="true" xsi:type="xpath"> - settings/addButtonLabel - </item> + <item name="addButtonLabel" type="string" translate="true" xsi:type="xpath">settings/addButtonLabel</item> <item name="addButton" type="boolean" xsi:type="xpath">settings/addButton</item> <item name="deleteProperty" type="string" xsi:type="xpath">settings/deleteProperty</item> <item name="identificationProperty" type="string" xsi:type="xpath">settings/identificationProperty</item> diff --git a/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml b/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml index 425a2047cfd51..c38ea402718e3 100644 --- a/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml +++ b/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml @@ -5,10 +5,10 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <section id="webapi" translate="label" type="text" sortOrder="102" showInDefault="1" showInWebsite="1" showInStore="1"> + <section id="webapi" type="text" sortOrder="102" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="webapisecurity" translate="label" type="text" sortOrder="250" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Web API Security</label> - <field id="allow_insecure" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="allow_insecure" translate="label" type="select comment" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Allow Anonymous Guest Access</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>This feature applies only to CMS, Catalog and Store APIs. Please consult your developers for details on potential security risks.</comment> diff --git a/app/code/Magento/WebapiSecurity/i18n/en_US.csv b/app/code/Magento/WebapiSecurity/i18n/en_US.csv index 94a4e8706bcd8..5e5e821e63e7f 100644 --- a/app/code/Magento/WebapiSecurity/i18n/en_US.csv +++ b/app/code/Magento/WebapiSecurity/i18n/en_US.csv @@ -1,2 +1,3 @@ "Web API Security","Web API Security" "Allow Anonymous Guest Access","Allow Anonymous Guest Access" +"This feature applies only to CMS, Catalog and Store APIs. Please consult your developers for details on potential security risks.","This feature applies only to CMS, Catalog and Store APIs. Please consult your developers for details on potential security risks." diff --git a/app/code/Magento/Wishlist/etc/adminhtml/system.xml b/app/code/Magento/Wishlist/etc/adminhtml/system.xml index 86c24bab11ffb..50ea4b01fe12b 100644 --- a/app/code/Magento/Wishlist/etc/adminhtml/system.xml +++ b/app/code/Magento/Wishlist/etc/adminhtml/system.xml @@ -22,12 +22,12 @@ <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> </field> - <field id="number_limit" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="number_limit" translate="label comment" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Max Emails Allowed to be Sent</label> <comment>10 by default. Max - 10000</comment> <validate>validate-digits validate-digits-range digits-range-1-10000</validate> </field> - <field id="text_limit" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="text_limit" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Email Text Length Limit</label> <comment>255 by default</comment> <validate>validate-digits validate-digits-range digits-range-1-10000</validate> diff --git a/app/code/Magento/Wishlist/i18n/en_US.csv b/app/code/Magento/Wishlist/i18n/en_US.csv index 75d5a2e840102..a1d33cbd574f0 100644 --- a/app/code/Magento/Wishlist/i18n/en_US.csv +++ b/app/code/Magento/Wishlist/i18n/en_US.csv @@ -99,7 +99,9 @@ Back,Back "Email Template","Email Template" "Email template chosen based on theme fallback when ""Default"" option is selected.","Email template chosen based on theme fallback when ""Default"" option is selected." "Max Emails Allowed to be Sent","Max Emails Allowed to be Sent" +"10 by default. Max - 10000","10 by default. Max - 10000" "Email Text Length Limit","Email Text Length Limit" +"255 by default","255 by default" "General Options","General Options" Enabled,Enabled "My Wish List Link","My Wish List Link" From b95b1a5d811a7a58b26129a853963960e53ce96e Mon Sep 17 00:00:00 2001 From: Andrew Howden <hello@andrewhowden.com> Date: Sat, 12 May 2018 14:10:36 +0200 Subject: [PATCH 0914/1171] AD-HOC feat (Profiler): Allow supplying complex profiler configuration --- app/bootstrap.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/bootstrap.php b/app/bootstrap.php index ba62b296bd49c..b3f2c71f0f85b 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -54,8 +54,18 @@ && isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'text/html') !== false ) { - \Magento\Framework\Profiler::applyConfig( - (isset($_SERVER['MAGE_PROFILER']) && strlen($_SERVER['MAGE_PROFILER'])) ? $_SERVER['MAGE_PROFILER'] : trim(file_get_contents(BP . '/var/profiler.flag')), + $profilerString = isset($_SERVER['MAGE_PROFILER']) && strlen($_SERVER['MAGE_PROFILER']) + ? $_SERVER['MAGE_PROFILER'] + : trim(file_get_contents(BP . '/var/profiler.flag')); + + if ($profilerString && $profilerArray = json_decode($profilerString, true)) { + $profilerConfig = $profilerArray; + } else { + $profilerConfig = $profilerString; + } + + Magento\Framework\Profiler::applyConfig( + $profilerConfig, BP, !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' ); From 2a515ef48146aaa5306a1cdfd2ff17728cf99ba8 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Thu, 9 Aug 2018 11:06:03 +0300 Subject: [PATCH 0915/1171] Small refactoring for better readability --- app/bootstrap.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/bootstrap.php b/app/bootstrap.php index b3f2c71f0f85b..70b632537a75b 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -54,14 +54,12 @@ && isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'text/html') !== false ) { - $profilerString = isset($_SERVER['MAGE_PROFILER']) && strlen($_SERVER['MAGE_PROFILER']) + $profilerConfig = isset($_SERVER['MAGE_PROFILER']) && strlen($_SERVER['MAGE_PROFILER']) ? $_SERVER['MAGE_PROFILER'] : trim(file_get_contents(BP . '/var/profiler.flag')); - if ($profilerString && $profilerArray = json_decode($profilerString, true)) { - $profilerConfig = $profilerArray; - } else { - $profilerConfig = $profilerString; + if ($profilerConfig) { + $profilerConfig = json_decode($profilerConfig, true) ?: $profilerConfig; } Magento\Framework\Profiler::applyConfig( From a119df4e9ab5ffe3f3097255cc6a02cfe2e23376 Mon Sep 17 00:00:00 2001 From: Igor Melnykov <melnykov@adobe.com> Date: Sat, 11 Aug 2018 08:48:55 -0500 Subject: [PATCH 0916/1171] MAGETWO-94083: Impossible get information about static errors on PR - create empty report file for the case when there no php and phtml files --- dev/tests/static/phpunit-all.xml.dist | 2 +- dev/tests/static/phpunit.xml.dist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/static/phpunit-all.xml.dist b/dev/tests/static/phpunit-all.xml.dist index a0d1f2fe75dc9..3020889e6066b 100644 --- a/dev/tests/static/phpunit-all.xml.dist +++ b/dev/tests/static/phpunit-all.xml.dist @@ -21,6 +21,6 @@ <php> <ini name="date.timezone" value="America/Los_Angeles"/> <!-- TESTCODESTYLE_IS_FULL_SCAN - specify if full scan should be performed for test code style test --> - <const name="TESTCODESTYLE_IS_FULL_SCAN" value="1"/> + <const name="TESTCODESTYLE_IS_FULL_SCAN" value="0"/> </php> </phpunit> diff --git a/dev/tests/static/phpunit.xml.dist b/dev/tests/static/phpunit.xml.dist index d9f9dbdfe54cd..ec581ec992f00 100644 --- a/dev/tests/static/phpunit.xml.dist +++ b/dev/tests/static/phpunit.xml.dist @@ -31,7 +31,7 @@ <php> <ini name="date.timezone" value="America/Los_Angeles"/> <!-- TESTCODESTYLE_IS_FULL_SCAN - specify if full scan should be performed for test code style test --> - <const name="TESTCODESTYLE_IS_FULL_SCAN" value="1"/> + <const name="TESTCODESTYLE_IS_FULL_SCAN" value="0"/> <!-- TESTS_COMPOSER_PATH - specify the path to composer binary, if a relative reference cannot be resolved --> <!--<const name="TESTS_COMPOSER_PATH" value="/usr/local/bin/composer"/>--> </php> From 969985fc20f097529ef640b826a04999fd2e0059 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Sat, 11 Aug 2018 12:00:07 -0500 Subject: [PATCH 0917/1171] MQE-1172: Bump MFTF version and deliver Magento branches - Fix an issue with the test --- .../Braintree/Test/Mftf/ActionGroup/AdminUserActionGroup.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminUserActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminUserActionGroup.xml index 30ca3f38fe736..23c322083773c 100644 --- a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminUserActionGroup.xml +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminUserActionGroup.xml @@ -17,7 +17,7 @@ </actionGroup> <!--Create new user with specified role--> - <actionGroup name="AdminCreateUser"> + <actionGroup name="AdminCreateUserAction"> <click selector="{{AdminCreateUserSection.create}}" stepKey="clickToCreateNewUser"/> <waitForPageLoad stepKey="waitForNewUserPageLoad" time="10"/> <fillField selector="{{AdminCreateUserSection.usernameTextField}}" userInput="{{NewAdmin.username}}" stepKey="enterUserName" /> @@ -53,4 +53,4 @@ <see userInput="You deleted the user." stepKey="seeSuccessMessage" /> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> From 7dcc6ae6238f98327a1fc00c72653ac0fede396e Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Sat, 11 Aug 2018 12:01:27 -0500 Subject: [PATCH 0918/1171] MQE-1172: Bump MFTF version and deliver Magento branches - fix an issue with test --- .../Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml index 4836c49d354b4..db105e1acb122 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml @@ -42,7 +42,7 @@ <!--Create New User With Specific Role--> <actionGroup ref="GoToAllUsers" stepKey="GoToAllUsers"/> - <actionGroup ref="AdminCreateUser" stepKey="AdminCreateNewUser"/> + <actionGroup ref="AdminCreateUserAction" stepKey="AdminCreateNewUser"/> <!--SignOut--> <actionGroup ref="SignOut" stepKey="signOutFromAdmin"/> From 7a95de147abe5f2496828b608681b658f2b27bd7 Mon Sep 17 00:00:00 2001 From: Vital_Pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Sat, 11 Aug 2018 15:19:03 +0300 Subject: [PATCH 0919/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Add unit test in case Expired session --- .../Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php b/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php index 9fcfdf438f8ea..7c0e542dd6705 100644 --- a/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Checkout\Test\Unit\Controller\Cart; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; @@ -35,7 +37,7 @@ class AddTest extends \PHPUnit\Framework\TestCase private $messageManager; /** - * @var \Magento\Checkout\Controller\Cart\Add | \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Checkout\Controller\Cart\Add|\PHPUnit_Framework_MockObject_MockObject */ private $cartAdd; From 9868428966220ddc23fe04f3af985eab2d66ff59 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sun, 12 Aug 2018 10:16:19 +0300 Subject: [PATCH 0920/1171] Fix the issue with "Shipping address is not set" exception, Fix the integrity constraint violation error when trying to access Shopping Cart --- .../Multishipping/Controller/Checkout.php | 11 ++++++++++- app/code/Magento/Multishipping/Helper/Url.php | 10 ++++++++++ .../Quote/Model/ShippingMethodManagement.php | 18 ++++++++++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Multishipping/Controller/Checkout.php b/app/code/Magento/Multishipping/Controller/Checkout.php index 161021768ce19..92417c7cb3a18 100644 --- a/app/code/Magento/Multishipping/Controller/Checkout.php +++ b/app/code/Magento/Multishipping/Controller/Checkout.php @@ -8,6 +8,7 @@ use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Framework\App\RequestInterface; +use Magento\Framework\Exception\StateException; /** * Multishipping checkout controller @@ -153,7 +154,15 @@ public function dispatch(RequestInterface $request) return parent::dispatch($request); } - $quote = $this->_getCheckout()->getQuote(); + try { + $checkout = $this->_getCheckout(); + } catch (StateException $e) { + $this->getResponse()->setRedirect($this->_getHelper()->getMSNewShippingUrl()); + $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); + return parent::dispatch($request); + } + + $quote = $checkout->getQuote(); if (!$quote->hasItems() || $quote->getHasError() || $quote->isVirtual()) { $this->getResponse()->setRedirect($this->_getHelper()->getCartUrl()); $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); diff --git a/app/code/Magento/Multishipping/Helper/Url.php b/app/code/Magento/Multishipping/Helper/Url.php index e293e3d4d7121..eaefa8fe8bee3 100644 --- a/app/code/Magento/Multishipping/Helper/Url.php +++ b/app/code/Magento/Multishipping/Helper/Url.php @@ -63,6 +63,16 @@ public function getMSShippingAddressSavedUrl() return $this->_getUrl('multishipping/checkout_address/shippingSaved'); } + /** + * Retrieve register url + * + * @return string + */ + public function getMSNewShippingUrl() + { + return $this->_getUrl('multishipping/checkout_address/newShipping'); + } + /** * Retrieve register url * diff --git a/app/code/Magento/Quote/Model/ShippingMethodManagement.php b/app/code/Magento/Quote/Model/ShippingMethodManagement.php index ac609e7f435ea..f62866539c6cc 100644 --- a/app/code/Magento/Quote/Model/ShippingMethodManagement.php +++ b/app/code/Magento/Quote/Model/ShippingMethodManagement.php @@ -16,6 +16,7 @@ use Magento\Quote\Api\Data\AddressInterface; use Magento\Quote\Api\Data\EstimateAddressInterface; use Magento\Quote\Api\ShipmentEstimationInterface; +use Magento\Quote\Model\ResourceModel\Quote\Address as QuoteAddressResource; /** * Shipping method read service @@ -63,6 +64,11 @@ class ShippingMethodManagement implements */ private $addressFactory; + /** + * @var QuoteAddressResource + */ + private $quoteAddressResource; + /** * Constructor * @@ -71,13 +77,15 @@ class ShippingMethodManagement implements * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository * @param Quote\TotalsCollector $totalsCollector * @param AddressInterfaceFactory|null $addressFactory + * @param QuoteAddressResource|null $quoteAddressResource */ public function __construct( \Magento\Quote\Api\CartRepositoryInterface $quoteRepository, Cart\ShippingMethodConverter $converter, \Magento\Customer\Api\AddressRepositoryInterface $addressRepository, \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector, - AddressInterfaceFactory $addressFactory = null + AddressInterfaceFactory $addressFactory = null, + QuoteAddressResource $quoteAddressResource = null ) { $this->quoteRepository = $quoteRepository; $this->converter = $converter; @@ -85,6 +93,8 @@ public function __construct( $this->totalsCollector = $totalsCollector; $this->addressFactory = $addressFactory ?: ObjectManager::getInstance() ->get(AddressInterfaceFactory::class); + $this->quoteAddressResource = $quoteAddressResource ?: ObjectManager::getInstance() + ->get(QuoteAddressResource::class); } /** @@ -172,6 +182,8 @@ public function set($cartId, $carrierCode, $methodCode) * @return void * @throws InputException The shipping method is not valid for an empty cart. * @throws NoSuchEntityException CThe Cart includes virtual product(s) only, so a shipping address is not used. + * @throws StateException The billing or shipping address is not set. + * @throws \Exception */ public function apply($cartId, $carrierCode, $methodCode) { @@ -189,7 +201,9 @@ public function apply($cartId, $carrierCode, $methodCode) } $shippingAddress = $quote->getShippingAddress(); if (!$shippingAddress->getCountryId()) { - return; + // Remove empty quote address + $this->quoteAddressResource->delete($shippingAddress); + throw new StateException(__('The shipping address is missing. Set the address and try again.')); } $shippingAddress->setShippingMethod($carrierCode . '_' . $methodCode); } From 2c546c4d67d1b8434405874f80c02eb898c3dced Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sun, 12 Aug 2018 10:44:14 +0300 Subject: [PATCH 0921/1171] Update PHPUnit tests --- .../Model/ShippingMethodManagementTest.php | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php index 6042ab25eef7f..34d7707d31666 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php @@ -15,7 +15,9 @@ use Magento\Quote\Model\Quote\Address\Rate; use Magento\Quote\Model\Quote\TotalsCollector; use Magento\Quote\Model\QuoteRepository; +use Magento\Quote\Model\ResourceModel\Quote\Address as QuoteAddressResource; use Magento\Quote\Model\ShippingMethodManagement; +use Magento\Store\Model\Store; use PHPUnit_Framework_MockObject_MockObject as MockObject; /** @@ -83,6 +85,16 @@ class ShippingMethodManagementTest extends \PHPUnit\Framework\TestCase */ private $totalsCollector; + /** + * @var Store|MockObject + */ + private $storeMock; + + /** + * @var QuoteAddressResource|MockObject + */ + private $quoteAddressResource; + protected function setUp() { $this->objectManager = new ObjectManager($this); @@ -98,7 +110,8 @@ protected function setUp() $className = \Magento\Framework\Reflection\DataObjectProcessor::class; $this->dataProcessor = $this->createMock($className); - $this->storeMock = $this->createMock(\Magento\Store\Model\Store::class); + $this->quoteAddressResource = $this->createMock(QuoteAddressResource::class); + $this->storeMock = $this->createMock(Store::class); $this->quote = $this->getMockBuilder(Quote::class) ->disableOriginalConstructor() ->setMethods([ @@ -150,6 +163,7 @@ protected function setUp() 'converter' => $this->converter, 'totalsCollector' => $this->totalsCollector, 'addressRepository' => $this->addressRepository, + 'quoteAddressResource' => $this->quoteAddressResource, ] ); @@ -344,6 +358,10 @@ public function testSetMethodWithVirtualProduct() $this->model->set($cartId, $carrierCode, $methodCode); } + /** + * @expectedException \Magento\Framework\Exception\StateException + * @expectedExceptionMessage The shipping address is missing. Set the address and try again. + */ public function testSetMethodWithoutShippingAddress() { $cartId = 12; @@ -357,8 +375,8 @@ public function testSetMethodWithoutShippingAddress() $this->quote->expects($this->once())->method('isVirtual')->will($this->returnValue(false)); $this->quote->expects($this->once()) ->method('getShippingAddress')->will($this->returnValue($this->shippingAddress)); - $this->quote->expects($this->once())->method('collectTotals')->willReturnSelf(); $this->shippingAddress->expects($this->once())->method('getCountryId')->will($this->returnValue(null)); + $this->quoteAddressResource->expects($this->once())->method('delete')->with($this->shippingAddress); $this->model->set($cartId, $carrierCode, $methodCode); } @@ -399,6 +417,10 @@ public function testSetMethodWithCouldNotSaveException() $this->model->set($cartId, $carrierCode, $methodCode); } + /** + * @expectedException \Magento\Framework\Exception\StateException + * @expectedExceptionMessage The shipping address is missing. Set the address and try again. + */ public function testSetMethodWithoutAddress() { $cartId = 12; @@ -413,8 +435,8 @@ public function testSetMethodWithoutAddress() $this->quote->expects($this->once()) ->method('getShippingAddress') ->willReturn($this->shippingAddress); - $this->quote->expects($this->once())->method('collectTotals')->willReturnSelf(); $this->shippingAddress->expects($this->once())->method('getCountryId'); + $this->quoteAddressResource->expects($this->once())->method('delete')->with($this->shippingAddress); $this->model->set($cartId, $carrierCode, $methodCode); } From d46d36a0331f7818147dec636f5cf981089ff1d6 Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Sun, 12 Aug 2018 16:45:48 +0200 Subject: [PATCH 0922/1171] Fixed typo in module AdminNotification --- .../Observer/PredispatchAdminActionControllerObserver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/AdminNotification/Observer/PredispatchAdminActionControllerObserver.php b/app/code/Magento/AdminNotification/Observer/PredispatchAdminActionControllerObserver.php index 3275de2a82fb7..24ef712c0f61f 100644 --- a/app/code/Magento/AdminNotification/Observer/PredispatchAdminActionControllerObserver.php +++ b/app/code/Magento/AdminNotification/Observer/PredispatchAdminActionControllerObserver.php @@ -37,7 +37,7 @@ public function __construct( } /** - * Predispath admin action controller + * Predispatch admin action controller * * @param \Magento\Framework\Event\Observer $observer * @return void From 088a91001e6b39b08188e65e50218ad16852b196 Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Sun, 12 Aug 2018 16:58:55 +0200 Subject: [PATCH 0923/1171] Fixed typos in module CatalogRule Fixed typos in module CatalogRule --- .../Block/Adminhtml/Promo/Catalog/Edit/Tab/Conditions.php | 2 +- .../CatalogRule/Block/Adminhtml/Promo/Widget/Chooser/Sku.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Catalog/Edit/Tab/Conditions.php b/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Catalog/Edit/Tab/Conditions.php index 0b4748c933a3c..9d1a23611dc27 100644 --- a/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Catalog/Edit/Tab/Conditions.php +++ b/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Catalog/Edit/Tab/Conditions.php @@ -66,7 +66,7 @@ public function getTabTitle() } /** - * Returns status flag about this tab can be showen or not + * Returns status flag about this tab can be shown or not * * @return bool * @codeCoverageIgnore diff --git a/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Widget/Chooser/Sku.php b/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Widget/Chooser/Sku.php index 306d3b9a347b4..87cb18253a107 100644 --- a/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Widget/Chooser/Sku.php +++ b/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Widget/Chooser/Sku.php @@ -139,7 +139,7 @@ protected function _getCpCollectionInstance() } /** - * Define Cooser Grid Columns and filters + * Define Chooser Grid Columns and filters * * @return $this */ From f49084f161dbcac3fc6e9ed9165a02bf520a117b Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Sun, 12 Aug 2018 17:13:09 +0200 Subject: [PATCH 0924/1171] Fixed typo 'Infomation' All occurrences of 'Infomation' have been changed to 'Information'. --- .../Mftf/ActionGroup/CheckoutActionGroup.xml | 16 ++++++++-------- .../Test/Mftf/Section/CheckoutPaymentSection.xml | 4 ++-- .../GuestCheckoutWithEnabledPersistentTest.xml | 14 +++++++------- .../Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml | 4 ++-- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml index e70bccbfdfe2b..4c9ef93c2c4d3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml @@ -93,13 +93,13 @@ <argument name="customerAddressVar"/> </arguments> <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> - <see userInput="{{customerVar.firstname}}" selector="{{CheckoutPaymentSection.shipToInfomation}}" stepKey="assertShipToInformationFirstName"/> - <see userInput="{{customerVar.lastname}}" selector="{{CheckoutPaymentSection.shipToInfomation}}" stepKey="assertShipToInformationLastName"/> - <see userInput="{{customerAddressVar.street[0]}}" selector="{{CheckoutPaymentSection.shipToInfomation}}" stepKey="assertShipToInformationStreet"/> - <see userInput="{{customerAddressVar.city}}" selector="{{CheckoutPaymentSection.shipToInfomation}}" stepKey="assertShipToInformationCity"/> - <see userInput="{{customerAddressVar.state}}" selector="{{CheckoutPaymentSection.shipToInfomation}}" stepKey="assertShipToInformationState"/> - <see userInput="{{customerAddressVar.postcode}}" selector="{{CheckoutPaymentSection.shipToInfomation}}" stepKey="assertShipToInformationPostcode"/> - <see userInput="{{customerAddressVar.telephone}}" selector="{{CheckoutPaymentSection.shipToInfomation}}" stepKey="assertShipToInformationTelephone"/> + <see userInput="{{customerVar.firstname}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationFirstName"/> + <see userInput="{{customerVar.lastname}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationLastName"/> + <see userInput="{{customerAddressVar.street[0]}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationStreet"/> + <see userInput="{{customerAddressVar.city}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationCity"/> + <see userInput="{{customerAddressVar.state}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationState"/> + <see userInput="{{customerAddressVar.postcode}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationPostcode"/> + <see userInput="{{customerAddressVar.telephone}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationTelephone"/> </actionGroup> <!-- Check shipping method in checkout --> @@ -108,7 +108,7 @@ <argument name="shippingMethod"/> </arguments> <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> - <see userInput="{{shippingMethod}}" selector="{{CheckoutPaymentSection.shippingMethodInfomation}}" stepKey="assertshippingMethodInfomation"/> + <see userInput="{{shippingMethod}}" selector="{{CheckoutPaymentSection.shippingMethodInformation}}" stepKey="assertshippingMethodInformation"/> </actionGroup> <!-- Checkout select Check/Money Order payment --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index 866f5f5070940..a12852ea1ef25 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -36,8 +36,8 @@ <element name="ProductItemByName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']" parameterized="true" /> <element name="ProductOptionsByProductItemName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']//ancestor::div[@class='product-item-details']//div[@class='product options']" parameterized="true" /> <element name="ProductOptionsActiveByProductItemName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']//ancestor::div[@class='product-item-details']//div[@class='product options active']" parameterized="true" /> - <element name="shipToInfomation" type="text" selector="//div[@class='ship-to']//div[@class='shipping-information-content']" /> - <element name="shippingMethodInfomation" type="text" selector="//div[@class='ship-via']//div[@class='shipping-information-content']" /> + <element name="shipToInformation" type="text" selector="//div[@class='ship-to']//div[@class='shipping-information-content']" /> + <element name="shippingMethodInformation" type="text" selector="//div[@class='ship-via']//div[@class='shipping-information-content']" /> <element name="paymentMethodTitle" type="text" selector=".payment-method-title span" /> <element name="productOptionsByProductItemPrice" type="text" selector="//div[@class='product-item-inner']//div[@class='subtotal']//span[@class='price'][contains(.,'{{var1}}')]//ancestor::div[@class='product-item-details']//div[@class='product options']" parameterized="true"/> <element name="productOptionsActiveByProductItemPrice" type="text" selector="//div[@class='subtotal']//span[@class='price'][contains(.,'{{var1}}')]//ancestor::div[@class='product-item-details']//div[@class='product options active']" parameterized="true"/> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml index 49d07992694d4..03509268751ed 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml @@ -72,12 +72,12 @@ <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{CustomerAddressSimple.postcode}}" stepKey="seeBilllingPostcode" /> <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{CustomerAddressSimple.telephone}}" stepKey="seeBilllingTelephone" /> <!-- Check that "Ship To" block contains correct information --> - <see selector="{{CheckoutPaymentSection.shipToInfomation}}" userInput="{{CustomerAddressSimple.firstName}}" stepKey="seeShipToFirstName" /> - <see selector="{{CheckoutPaymentSection.shipToInfomation}}" userInput="{{CustomerAddressSimple.lastName}}" stepKey="seeShipToLastName" /> - <see selector="{{CheckoutPaymentSection.shipToInfomation}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="seeShipToStreet" /> - <see selector="{{CheckoutPaymentSection.shipToInfomation}}" userInput="{{CustomerAddressSimple.city}}" stepKey="seeShipToCity" /> - <see selector="{{CheckoutPaymentSection.shipToInfomation}}" userInput="{{CustomerAddressSimple.state}}" stepKey="seeShipToState" /> - <see selector="{{CheckoutPaymentSection.shipToInfomation}}" userInput="{{CustomerAddressSimple.postcode}}" stepKey="seeShipToPostcode" /> - <see selector="{{CheckoutPaymentSection.shipToInfomation}}" userInput="{{CustomerAddressSimple.telephone}}" stepKey="seeShipToTelephone" /> + <see selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{CustomerAddressSimple.firstName}}" stepKey="seeShipToFirstName" /> + <see selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{CustomerAddressSimple.lastName}}" stepKey="seeShipToLastName" /> + <see selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="seeShipToStreet" /> + <see selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{CustomerAddressSimple.city}}" stepKey="seeShipToCity" /> + <see selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{CustomerAddressSimple.state}}" stepKey="seeShipToState" /> + <see selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{CustomerAddressSimple.postcode}}" stepKey="seeShipToPostcode" /> + <see selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{CustomerAddressSimple.telephone}}" stepKey="seeShipToTelephone" /> </test> </tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml index f051d4f8c3b82..3e85ecc783253 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml @@ -313,7 +313,7 @@ </actionGroup> <click stepKey="clickNext" selector="{{CheckoutShippingSection.next}}"/> <see stepKey="seeAddress" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxCA.state}}"/> - <see stepKey="seeShipTo" selector="{{CheckoutPaymentSection.shipToInfomation}}" userInput="{{SimpleTaxCA.state}}"/> + <see stepKey="seeShipTo" selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{SimpleTaxCA.state}}"/> <!-- Assert that taxes are applied correctly for CA --> <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> @@ -329,7 +329,7 @@ <argument name="Address" value="US_Address_NY"/> </actionGroup> <click stepKey="clickNext2" selector="{{CheckoutShippingSection.next}}"/> - <see stepKey="seeShipTo2" selector="{{CheckoutPaymentSection.shipToInfomation}}" userInput="{{SimpleTaxNY.state}}"/> + <see stepKey="seeShipTo2" selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{SimpleTaxNY.state}}"/> <!-- Assert that taxes are applied correctly for NY --> <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> From df54a2694092ac50f1a68eebe3731d21f74c6b11 Mon Sep 17 00:00:00 2001 From: Marcel <marcel.moldovan@zoho.com> Date: Wed, 14 Feb 2018 11:54:54 +0200 Subject: [PATCH 0925/1171] bug: Fix possible undefined index when caching config data --- app/code/Magento/Config/App/Config/Type/System.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Config/App/Config/Type/System.php b/app/code/Magento/Config/App/Config/Type/System.php index 4f6d9c14346f0..479b4f5c21732 100644 --- a/app/code/Magento/Config/App/Config/Type/System.php +++ b/app/code/Magento/Config/App/Config/Type/System.php @@ -245,7 +245,7 @@ private function cacheData(array $data) ); $scopes = []; foreach (['websites', 'stores'] as $curScopeType) { - foreach ($data[$curScopeType] as $curScopeId => $curScopeData) { + foreach ($data[$curScopeType] ?? [] as $curScopeId => $curScopeData) { $scopes[$curScopeType][$curScopeId] = 1; $this->cache->save( $this->serializer->serialize($curScopeData), From 1c74f5576011cd442dc086e52935c9fe68e7aa3e Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sun, 12 Aug 2018 22:40:08 +0300 Subject: [PATCH 0926/1171] Adding roles to images array --- .../Catalog/Model/ProductRepository.php | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index 4c0122694285d..d7fbcbcd523f8 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -514,8 +514,13 @@ protected function processMediaGallery(ProductInterface $product, $mediaGalleryE $newEntries = $mediaGalleryEntries; } - $this->getMediaGalleryProcessor()->clearMediaAttribute($product, array_keys($product->getMediaAttributes())); $images = $product->getMediaGallery('images'); + if ($images) { + $images = $this->determineImageRoles($product, $images); + } + + $this->getMediaGalleryProcessor()->clearMediaAttribute($product, array_keys($product->getMediaAttributes())); + if ($images) { foreach ($images as $image) { if (!isset($image['removed']) && !empty($image['types'])) { @@ -758,6 +763,32 @@ public function cleanCache() $this->instancesById = null; } + /** + * Ascertain image roles, if they are not set against the gallery entries + * + * @param ProductInterface $product + * @param array $images + * @return array + */ + private function determineImageRoles(ProductInterface $product, array $images) + { + $imagesWithRoles = []; + foreach ($images as $image) { + if (!isset($image['types'])) { + $image['types'] = []; + if (isset($image['file'])) { + foreach (array_keys($product->getMediaAttributes()) as $attribute) { + if ($image['file'] == $product->getData($attribute)) { + $image['types'][] = $attribute; + } + } + } + } + $imagesWithRoles[] = $image; + } + return $imagesWithRoles; + } + /** * @return Product\Gallery\Processor */ From fd1995517aee014695b5f0a4e2594285192a074c Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Sun, 12 Aug 2018 17:13:22 -0300 Subject: [PATCH 0927/1171] Replacing deprecated methods. --- .../Sitemap/Controller/Adminhtml/Sitemap/Delete.php | 6 +++--- .../Magento/Sitemap/Controller/Adminhtml/Sitemap/Edit.php | 2 +- .../Sitemap/Controller/Adminhtml/Sitemap/Generate.php | 8 ++++---- .../Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php | 6 +++--- .../Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php index 422fed9d9a688..29d50ea8408fd 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php @@ -65,20 +65,20 @@ public function execute() } $sitemap->delete(); // display success message - $this->messageManager->addSuccess(__('You deleted the sitemap.')); + $this->messageManager->addSuccessMessage(__('You deleted the sitemap.')); // go to grid $this->_redirect('adminhtml/*/'); return; } catch (\Exception $e) { // display error message - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); // go back to edit form $this->_redirect('adminhtml/*/edit', ['sitemap_id' => $id]); return; } } // display error message - $this->messageManager->addError(__('We can\'t find a sitemap to delete.')); + $this->messageManager->addErrorMessage(__('We can\'t find a sitemap to delete.')); // go to grid $this->_redirect('adminhtml/*/'); } diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Edit.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Edit.php index 04ab4f8725e0e..111353550b9cd 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Edit.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Edit.php @@ -41,7 +41,7 @@ public function execute() if ($id) { $model->load($id); if (!$model->getId()) { - $this->messageManager->addError(__('This sitemap no longer exists.')); + $this->messageManager->addErrorMessage(__('This sitemap no longer exists.')); $this->_redirect('adminhtml/*/'); return; } diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index d19b248c8008f..9592ab6f57c55 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -52,18 +52,18 @@ public function execute() ); $sitemap->generateXml(); - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('The sitemap "%1" has been generated.', $sitemap->getSitemapFilename()) ); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('We can\'t generate the sitemap right now.')); + $this->messageManager->addExceptionMessage($e, __('We can\'t generate the sitemap right now.')); } finally { $this->appEmulation->stopEnvironmentEmulation(); } } else { - $this->messageManager->addError(__('We can\'t find a sitemap to generate.')); + $this->messageManager->addErrorMessage(__('We can\'t find a sitemap to generate.')); } // go to grid diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php index 5c38cc68e6ef7..1e0d1cb248f00 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php @@ -30,7 +30,7 @@ protected function validatePath(array $data) $validator->setPaths($helper->getValidPaths()); if (!$validator->isValid($path)) { foreach ($validator->getMessages() as $message) { - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); } // save data in session $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setFormData($data); @@ -83,13 +83,13 @@ protected function saveData($data) // save the data $model->save(); // display success message - $this->messageManager->addSuccess(__('You saved the sitemap.')); + $this->messageManager->addSuccessMessage(__('You saved the sitemap.')); // clear previously saved data from session $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setFormData(false); return $model->getId(); } catch (\Exception $e) { // display error message - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); // save data in session $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setFormData($data); } diff --git a/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php b/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php index 36e3aa0312627..f77954101df7c 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php @@ -154,7 +154,7 @@ public function testTryToSaveInvalidDataShouldFailWithErrors() ->willReturnMap([[$helperClass, $helper], [$sessionClass, $session]]); $this->messageManagerMock->expects($this->at(0)) - ->method('addError') + ->method('addErrorMessage') ->withConsecutive( [$messages[0]], [$messages[1]] From c7d22db258b5c6d5130c116d0bf5061ec9f358d2 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Sun, 12 Aug 2018 17:19:13 -0300 Subject: [PATCH 0928/1171] Replacing deprecated methods for Magento_Security module. --- .../Controller/Adminhtml/Session/LogoutAll.php | 6 +++--- .../Magento/Security/Model/Plugin/AuthSession.php | 2 +- .../Security/Model/Plugin/LoginController.php | 2 +- .../Controller/Adminhtml/Session/LogoutAllTest.php | 12 ++++++------ .../Test/Unit/Model/Plugin/AuthSessionTest.php | 2 +- .../Test/Unit/Model/Plugin/LoginControllerTest.php | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Security/Controller/Adminhtml/Session/LogoutAll.php b/app/code/Magento/Security/Controller/Adminhtml/Session/LogoutAll.php index c533e740b2251..35d8f22d84d51 100644 --- a/app/code/Magento/Security/Controller/Adminhtml/Session/LogoutAll.php +++ b/app/code/Magento/Security/Controller/Adminhtml/Session/LogoutAll.php @@ -38,11 +38,11 @@ public function execute() { try { $this->sessionsManager->logoutOtherUserSessions(); - $this->messageManager->addSuccess(__('All other open sessions for this account were terminated.')); + $this->messageManager->addSuccessMessage(__('All other open sessions for this account were terminated.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __("We couldn't logout because of an error.")); + $this->messageManager->addExceptionMessage($e, __("We couldn't logout because of an error.")); } $this->_redirect('*/*/activity'); } diff --git a/app/code/Magento/Security/Model/Plugin/AuthSession.php b/app/code/Magento/Security/Model/Plugin/AuthSession.php index abec4dc7c29ef..01203caaa31cd 100644 --- a/app/code/Magento/Security/Model/Plugin/AuthSession.php +++ b/app/code/Magento/Security/Model/Plugin/AuthSession.php @@ -82,7 +82,7 @@ private function addUserLogoutNotification() $this->sessionsManager->getCurrentSession()->getStatus() ); } elseif ($message = $this->sessionsManager->getLogoutReasonMessage()) { - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); } return $this; diff --git a/app/code/Magento/Security/Model/Plugin/LoginController.php b/app/code/Magento/Security/Model/Plugin/LoginController.php index ab9c6e2857bce..ba1a18c4f0c06 100644 --- a/app/code/Magento/Security/Model/Plugin/LoginController.php +++ b/app/code/Magento/Security/Model/Plugin/LoginController.php @@ -53,7 +53,7 @@ public function beforeExecute(Login $login) { $logoutReasonCode = $this->securityCookie->getLogoutReasonCookie(); if ($this->isLoginForm($login) && $logoutReasonCode >= 0) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( $this->sessionsManager->getLogoutReasonMessageByStatus($logoutReasonCode) ); $this->securityCookie->deleteLogoutReasonCookie(); diff --git a/app/code/Magento/Security/Test/Unit/Controller/Adminhtml/Session/LogoutAllTest.php b/app/code/Magento/Security/Test/Unit/Controller/Adminhtml/Session/LogoutAllTest.php index 8c2119bde667e..fd991f46f958d 100644 --- a/app/code/Magento/Security/Test/Unit/Controller/Adminhtml/Session/LogoutAllTest.php +++ b/app/code/Magento/Security/Test/Unit/Controller/Adminhtml/Session/LogoutAllTest.php @@ -74,7 +74,7 @@ public function setUp() $this->messageManager = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) ->disableOriginalConstructor() - ->setMethods(['addSuccess', 'addError', 'addException']) + ->setMethods(['addSuccessMessage', 'addErrorMessage', 'addExceptionMessage']) ->getMockForAbstractClass(); $this->contextMock->expects($this->any()) ->method('getMessageManager') @@ -132,12 +132,12 @@ public function testExecute() $this->sessionsManager->expects($this->once()) ->method('logoutOtherUserSessions'); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($successMessage); $this->messageManager->expects($this->never()) - ->method('addError'); + ->method('addErrorMessage'); $this->messageManager->expects($this->never()) - ->method('addException'); + ->method('addExceptionMessage'); $this->responseMock->expects($this->once()) ->method('setRedirect'); $this->actionFlagMock->expects($this->once()) @@ -158,7 +158,7 @@ public function testExecuteLocalizedException() ->method('logoutOtherUserSessions') ->willThrowException(new LocalizedException($phrase)); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($phrase); $this->controller->execute(); } @@ -173,7 +173,7 @@ public function testExecuteException() ->method('logoutOtherUserSessions') ->willThrowException(new \Exception()); $this->messageManager->expects($this->once()) - ->method('addException') + ->method('addSuccessMessageptionMessage') ->with(new \Exception(), $phrase); $this->controller->execute(); } diff --git a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php index 5cb06d6143023..0f7f590b71de4 100644 --- a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php @@ -112,7 +112,7 @@ public function testAroundProlongSessionIsNotActiveAndIsNotAjaxRequest() ->willReturn($errorMessage); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($errorMessage); $this->model->aroundProlong($this->authSessionMock, $proceed); diff --git a/app/code/Magento/Security/Test/Unit/Model/Plugin/LoginControllerTest.php b/app/code/Magento/Security/Test/Unit/Model/Plugin/LoginControllerTest.php index 2bb2bc3cafac7..aa066e23f67cb 100644 --- a/app/code/Magento/Security/Test/Unit/Model/Plugin/LoginControllerTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/Plugin/LoginControllerTest.php @@ -103,7 +103,7 @@ public function testBeforeExecute() ->willReturn($errorMessage); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($errorMessage); $this->securityCookieMock->expects($this->once()) From 164943def6a909623b86c86b987e0095ba817a7e Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Sun, 12 Aug 2018 17:25:38 -0300 Subject: [PATCH 0929/1171] Replacing deprecated methods for Magento_Search module. --- .../Search/Controller/Adminhtml/Synonyms/Delete.php | 10 ++++++---- .../Search/Controller/Adminhtml/Synonyms/Edit.php | 2 +- .../Controller/Adminhtml/Synonyms/MassDelete.php | 6 +++--- .../Search/Controller/Adminhtml/Synonyms/Save.php | 2 +- .../Search/Controller/Adminhtml/Term/Delete.php | 6 +++--- .../Search/Controller/Adminhtml/Term/Edit.php | 2 +- .../Search/Controller/Adminhtml/Term/MassDelete.php | 6 +++--- .../Search/Controller/Adminhtml/Term/Save.php | 9 ++++++--- .../Controller/Adminhtml/Synonyms/DeleteTest.php | 8 ++++---- .../Controller/Adminhtml/Term/MassDeleteTest.php | 4 ++-- .../Test/Unit/Controller/Adminhtml/Term/SaveTest.php | 12 ++++++------ 11 files changed, 36 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php index bb476423692d1..9d8b612cefadf 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php @@ -60,16 +60,18 @@ public function execute() /** @var \Magento\Search\Model\SynonymGroup $synGroupModel */ $synGroupModel = $this->synGroupRepository->get($id); $this->synGroupRepository->delete($synGroupModel); - $this->messageManager->addSuccess(__('The synonym group has been deleted.')); + $this->messageManager->addSuccessMessage(__('The synonym group has been deleted.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $this->logger->error($e); } catch (\Exception $e) { - $this->messageManager->addError(__('An error was encountered while performing delete operation.')); + $this->messageManager->addErrorMessage( + __('An error was encountered while performing delete operation.') + ); $this->logger->error($e); } } else { - $this->messageManager->addError(__('We can\'t find a synonym group to delete.')); + $this->messageManager->addErrorMessage(__('We can\'t find a synonym group to delete.')); } return $resultRedirect->setPath('*/*/'); diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Edit.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Edit.php index 8eefc956e8aa5..adc4ecc52030a 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Edit.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Edit.php @@ -65,7 +65,7 @@ public function execute() // 2. Initial checking if ($groupId && (!$synGroup->getGroupId())) { - $this->messageManager->addError(__('This synonyms group no longer exists.')); + $this->messageManager->addErrorMessage(__('This synonyms group no longer exists.')); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('*/*/'); diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php index 4add418d95325..f2770f77cc533 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php @@ -72,17 +72,17 @@ public function execute() $this->synGroupRepository->delete($synonymGroup); $deletedItems++; } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } } if ($deletedItems != 0) { if ($collectionSize != $deletedItems) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('Failed to delete %1 synonym group(s).', $collectionSize - $deletedItems) ); } - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('A total of %1 synonym group(s) have been deleted.', $deletedItems) ); } diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Save.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Save.php index ffa97ceb3e0e1..0ed73fd0cee32 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Save.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Save.php @@ -59,7 +59,7 @@ public function execute() $synGroup = $this->synGroupRepository->get($synGroupId); if (!$synGroup->getGroupId() && $synGroupId) { - $this->messageManager->addError(__('This synonym group no longer exists.')); + $this->messageManager->addErrorMessage(__('This synonym group no longer exists.')); return $resultRedirect->setPath('*/*/'); } diff --git a/app/code/Magento/Search/Controller/Adminhtml/Term/Delete.php b/app/code/Magento/Search/Controller/Adminhtml/Term/Delete.php index 3a1b80df2ea7e..c7adf32da0fb0 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Term/Delete.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Term/Delete.php @@ -23,16 +23,16 @@ public function execute() $model = $this->_objectManager->create(\Magento\Search\Model\Query::class); $model->setId($id); $model->delete(); - $this->messageManager->addSuccess(__('You deleted the search.')); + $this->messageManager->addSuccessMessage(__('You deleted the search.')); $resultRedirect->setPath('search/*/'); return $resultRedirect; } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $resultRedirect->setPath('search/*/edit', ['id' => $this->getRequest()->getParam('id')]); return $resultRedirect; } } - $this->messageManager->addError(__('We can\'t find a search term to delete.')); + $this->messageManager->addErrorMessage(__('We can\'t find a search term to delete.')); $resultRedirect->setPath('search/*/'); return $resultRedirect; } diff --git a/app/code/Magento/Search/Controller/Adminhtml/Term/Edit.php b/app/code/Magento/Search/Controller/Adminhtml/Term/Edit.php index 85e14ae9fe0b0..3ee0ea240377f 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Term/Edit.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Term/Edit.php @@ -43,7 +43,7 @@ public function execute() if ($id) { $model->load($id); if (!$model->getId()) { - $this->messageManager->addError(__('This search no longer exists.')); + $this->messageManager->addErrorMessage(__('This search no longer exists.')); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); $resultRedirect->setPath('search/*'); diff --git a/app/code/Magento/Search/Controller/Adminhtml/Term/MassDelete.php b/app/code/Magento/Search/Controller/Adminhtml/Term/MassDelete.php index b38d883b8faae..f6874078f2f64 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Term/MassDelete.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Term/MassDelete.php @@ -17,16 +17,16 @@ public function execute() { $searchIds = $this->getRequest()->getParam('search'); if (!is_array($searchIds)) { - $this->messageManager->addError(__('Please select searches.')); + $this->messageManager->addErrorMessage(__('Please select searches.')); } else { try { foreach ($searchIds as $searchId) { $model = $this->_objectManager->create(\Magento\Search\Model\Query::class)->load($searchId); $model->delete(); } - $this->messageManager->addSuccess(__('Total of %1 record(s) were deleted.', count($searchIds))); + $this->messageManager->addSuccessMessage(__('Total of %1 record(s) were deleted.', count($searchIds))); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Search/Controller/Adminhtml/Term/Save.php b/app/code/Magento/Search/Controller/Adminhtml/Term/Save.php index 42e9373a20fe2..cd9b1347ed1ed 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Term/Save.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Term/Save.php @@ -45,12 +45,15 @@ public function execute() $model->addData($data); $model->setIsProcessed(0); $model->save(); - $this->messageManager->addSuccess(__('You saved the search term.')); + $this->messageManager->addSuccessMessage(__('You saved the search term.')); } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); return $this->proceedToEdit($data); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Something went wrong while saving the search query.')); + $this->messageManager->addExceptionMessage( + $e, + __('Something went wrong while saving the search query.') + ); return $this->proceedToEdit($data); } } diff --git a/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/DeleteTest.php b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/DeleteTest.php index a7f71941dc6b2..38c78b986faf4 100644 --- a/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/DeleteTest.php +++ b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/DeleteTest.php @@ -107,10 +107,10 @@ public function testDeleteAction() $this->repository->expects($this->once())->method('get')->with(10)->willReturn($this->synonymGroupMock); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('The synonym group has been deleted.')); - $this->messageManagerMock->expects($this->never())->method('addError'); + $this->messageManagerMock->expects($this->never())->method('addErrorMessage'); $this->resultRedirectMock->expects($this->once())->method('setPath')->with('*/*/')->willReturnSelf(); @@ -124,10 +124,10 @@ public function testDeleteActionNoId() ->willReturn(null); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('We can\'t find a synonym group to delete.')); $this->messageManagerMock->expects($this->never()) - ->method('addSuccess'); + ->method('addSuccessMessage'); $this->resultRedirectMock->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/MassDeleteTest.php b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/MassDeleteTest.php index efda8f52fcfe9..60cc958a6187c 100644 --- a/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/MassDeleteTest.php +++ b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/MassDeleteTest.php @@ -54,7 +54,7 @@ protected function setUp() ->getMockForAbstractClass(); $this->messageManager = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) ->disableOriginalConstructor() - ->setMethods(['addSuccess', 'addError']) + ->setMethods(['addSuccessMessage', 'addErrorMessage']) ->getMockForAbstractClass(); $this->pageFactory = $this->getMockBuilder(\Magento\Framework\View\Result\PageFactory::class) ->setMethods([]) @@ -107,7 +107,7 @@ public function testExecute() $this->createQuery(0, 1); $this->createQuery(1, 2); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->will($this->returnSelf()); $this->resultRedirectMock->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/SaveTest.php b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/SaveTest.php index 09ae2c38fe525..28f4b65cd412f 100644 --- a/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/SaveTest.php +++ b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/SaveTest.php @@ -75,7 +75,7 @@ protected function setUp() $this->messageManager = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) ->disableOriginalConstructor() - ->setMethods(['addSuccess', 'addError', 'addException']) + ->setMethods(['addSuccessMessage', 'addErrorMessage', 'addExceptionMessage']) ->getMockForAbstractClass(); $this->context->expects($this->any()) ->method('getMessageManager') @@ -143,7 +143,7 @@ public function testExecuteLoadQueryQueryId() $this->query->expects($this->once())->method('getId')->willReturn(false); $this->query->expects($this->once())->method('load')->with($queryId); - $this->messageManager->expects($this->once())->method('addSuccess'); + $this->messageManager->expects($this->once())->method('addSuccessMessage'); $this->redirect->expects($this->once())->method('setPath')->willReturnSelf(); $this->assertSame($this->redirect, $this->controller->execute()); @@ -161,7 +161,7 @@ public function testExecuteLoadQueryQueryIdQueryText() $this->query->expects($this->once())->method('loadByQueryText')->with($queryText); $this->query->expects($this->any())->method('getId')->willReturn($queryId); - $this->messageManager->expects($this->once())->method('addSuccess'); + $this->messageManager->expects($this->once())->method('addSuccessMessage'); $this->redirect->expects($this->once())->method('setPath')->willReturnSelf(); $this->assertSame($this->redirect, $this->controller->execute()); @@ -180,7 +180,7 @@ public function testExecuteLoadQueryQueryIdQueryText2() $this->query->expects($this->any())->method('getId')->willReturn(false); $this->query->expects($this->once())->method('load')->with($queryId); - $this->messageManager->expects($this->once())->method('addSuccess'); + $this->messageManager->expects($this->once())->method('addSuccessMessage'); $this->redirect->expects($this->once())->method('setPath')->willReturnSelf(); $this->assertSame($this->redirect, $this->controller->execute()); @@ -199,7 +199,7 @@ public function testExecuteLoadQueryQueryIdQueryTextException() $this->query->expects($this->once())->method('loadByQueryText')->with($queryText); $this->query->expects($this->any())->method('getId')->willReturn($anotherQueryId); - $this->messageManager->expects($this->once())->method('addError'); + $this->messageManager->expects($this->once())->method('addErrorMessage'); $this->session->expects($this->once())->method('setPageData'); $this->redirect->expects($this->once())->method('setPath')->willReturnSelf(); $this->assertSame($this->redirect, $this->controller->execute()); @@ -216,7 +216,7 @@ public function testExecuteException() $this->query->expects($this->once())->method('setStoreId'); $this->query->expects($this->once())->method('loadByQueryText')->willThrowException(new \Exception()); - $this->messageManager->expects($this->once())->method('addException'); + $this->messageManager->expects($this->once())->method('addExceptionMessage'); $this->session->expects($this->once())->method('setPageData'); $this->redirect->expects($this->once())->method('setPath')->willReturnSelf(); $this->assertSame($this->redirect, $this->controller->execute()); From 587db1913bc2bc3449a7a7ba3b26b14e061536e5 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Sun, 12 Aug 2018 21:04:04 -0500 Subject: [PATCH 0930/1171] MQE-1172: Bump MFTF version and deliver Magento branches - skipping failing tests --- .../Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml | 5 ++++- .../Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml index db105e1acb122..df2e98816f0d3 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="CreateAnAdminOrderUsingBraintreePaymentTest1"> <annotations> <features value="Backend"/> @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-93677"/> <group value="braintree"/> + <skip> + <issueId value="MQE-1187" /> + </skip> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml index 2452e7b36be00..1efff9783b90c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontCustomerCheckoutTest"> <annotations> <features value="Checkout"/> @@ -88,6 +88,9 @@ <title value="Customer Checkout with multiple addresses and tax rates"/> <description value="Should be able to place an order as a customer with multiple addresses and tax rates."/> <testCaseId value="MAGETWO-93109"/> + <skip> + <issueId value="MQE-1187" /> + </skip> </annotations> <before> <createData entity="SimpleSubCategory" stepKey="simplecategory"/> From 897e7df9441d121af626cace56cbc2a53e35c38d Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Mon, 13 Aug 2018 09:42:40 +0300 Subject: [PATCH 0931/1171] Fix wrong return type in StockRegistryInterface API --- .../Magento/CatalogInventory/Api/StockRegistryInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogInventory/Api/StockRegistryInterface.php b/app/code/Magento/CatalogInventory/Api/StockRegistryInterface.php index c9b6abeb72e23..2d011e24f4105 100644 --- a/app/code/Magento/CatalogInventory/Api/StockRegistryInterface.php +++ b/app/code/Magento/CatalogInventory/Api/StockRegistryInterface.php @@ -72,7 +72,7 @@ public function getProductStockStatusBySku($productSku, $scopeId = null); * @param float $qty * @param int $currentPage * @param int $pageSize - * @return \Magento\CatalogInventory\Api\Data\StockStatusCollectionInterface + * @return \Magento\CatalogInventory\Api\Data\StockItemCollectionInterface */ public function getLowStockItems($scopeId, $qty, $currentPage = 1, $pageSize = 0); From 0f108dcde2cc103aea6b4a69235af0f3ee409d24 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 13 Aug 2018 10:36:59 +0300 Subject: [PATCH 0932/1171] MAGETWO-67633: Advanced Pricing import validation results show 0 value for checked entities --- .../Import/Entity/EntityAbstractTest.php | 66 +++++++++++++++++++ .../advanced_price_for_validation_test.csv | 3 + 2 files changed, 69 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/EntityAbstractTest.php create mode 100644 dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/_files/advanced_price_for_validation_test.csv diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/EntityAbstractTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/EntityAbstractTest.php new file mode 100644 index 0000000000000..718a81cfbc3f1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/EntityAbstractTest.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ImportExport\Model\Import\Entity; + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\ImportExport\Model\Import\Source\Csv; +use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; + +/** + * Test class for \Magento\ImportExport\Model\Import\AbstractEntity + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class EntityAbstractTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test for method _saveValidatedBunches() + */ + public function testSaveValidatedBunches() : void + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $filesystem = $objectManager->create(\Magento\Framework\Filesystem::class); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = new Csv(__DIR__ . '/_files/advanced_price_for_validation_test.csv', $directory); + $source->rewind(); + + $eavConfig = $this->createMock(\Magento\Eav\Model\Config::class); + $entityTypeMock = $this->createMock(\Magento\Eav\Model\Entity\Type::class); + $eavConfig->expects($this->any())->method('getEntityType')->willReturn($entityTypeMock); + + /** @var $model AbstractEntity|\PHPUnit_Framework_MockObject_MockObject */ + $model = $this->getMockForAbstractClass( + AbstractEntity::class, + [ + $objectManager->get(\Magento\Framework\Json\Helper\Data::class), + $objectManager->get(\Magento\ImportExport\Helper\Data::class), + $objectManager->get(\Magento\ImportExport\Model\ResourceModel\Import\Data::class), + $eavConfig, + $objectManager->get(\Magento\Framework\App\ResourceConnection::class), + $objectManager->get(\Magento\ImportExport\Model\ResourceModel\Helper::class), + $objectManager->get(\Magento\Framework\Stdlib\StringUtils::class), + $objectManager->get(ProcessingErrorAggregatorInterface::class), + ], + '', + true, + false, + true, + ['validateRow', 'getEntityTypeCode'] + ); + $model->expects($this->any())->method('validateRow')->willReturn(true); + $model->expects($this->any())->method('getEntityTypeCode')->willReturn('catalog_product'); + + $model->setSource($source); + + $method = new \ReflectionMethod($model, '_saveValidatedBunches'); + $method->setAccessible(true); + $method->invoke($model); + + $this->assertEquals(1, $model->getProcessedEntitiesCount()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/_files/advanced_price_for_validation_test.csv b/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/_files/advanced_price_for_validation_test.csv new file mode 100644 index 0000000000000..fb42afb901880 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/_files/advanced_price_for_validation_test.csv @@ -0,0 +1,3 @@ +sku,tier_price_website,tier_price_customer_group,tier_price_qty,tier_price,tier_price_value_type +SimpleProduct,All Websites [USD],ALL GROUPS,100,50,Fixed +SimpleProduct,All Websites [USD],ALL GROUPS,33,55,Fixed From 0088fd4a5fb9842291a8d9e0fb0371bb21ab4ba6 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Mon, 13 Aug 2018 10:37:41 +0300 Subject: [PATCH 0933/1171] MAGETWO-93172: [2.3] 'Could not save credit memo' when creating a 2nd credit memo --- .../Sales/Model/Order/CreditmemoRepository.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/CreditmemoRepository.php b/app/code/Magento/Sales/Model/Order/CreditmemoRepository.php index 1d82c17549140..ce4a0aa5b3e3a 100644 --- a/app/code/Magento/Sales/Model/Order/CreditmemoRepository.php +++ b/app/code/Magento/Sales/Model/Order/CreditmemoRepository.php @@ -7,12 +7,13 @@ namespace Magento\Sales\Model\Order; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; -use Magento\Sales\Model\ResourceModel\Metadata; -use Magento\Sales\Api\Data\CreditmemoSearchResultInterfaceFactory as SearchResultFactory; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\CouldNotDeleteException; use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Sales\Api\Data\CreditmemoSearchResultInterfaceFactory as SearchResultFactory; +use Magento\Sales\Model\ResourceModel\Metadata; /** * Repository class for @see \Magento\Sales\Api\Data\CreditmemoInterface @@ -139,6 +140,8 @@ public function save(\Magento\Sales\Api\Data\CreditmemoInterface $entity) try { $this->metadata->getMapper()->save($entity); $this->registry[$entity->getEntityId()] = $entity; + } catch (LocalizedException $e) { + throw new CouldNotSaveException(__($e->getMessage()), $e); } catch (\Exception $e) { throw new CouldNotSaveException(__("The credit memo couldn't be saved."), $e); } From 7d73804c2e341fb315ef0d2920085652517f25c4 Mon Sep 17 00:00:00 2001 From: nikita <nikita.shcherbatykh@transoftgroup.com> Date: Mon, 13 Aug 2018 10:47:12 +0300 Subject: [PATCH 0934/1171] MAGETWO-73774: [2.3] Custom option price field disallows negative values #7333 --- .../Product/Option/Validator/DefaultValidatorTest.php | 4 +--- .../Test/Unit/Model/Product/Option/Validator/FileTest.php | 7 +------ .../Unit/Model/Product/Option/Validator/SelectTest.php | 6 +----- .../Test/Unit/Model/Product/Option/Validator/TextTest.php | 6 +----- .../testsuite/Magento/Catalog/Model/ProductTest.php | 4 +--- 5 files changed, 5 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php index 73a27eb189187..da6b790fedfa6 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php @@ -19,9 +19,7 @@ class DefaultValidatorTest extends \PHPUnit\Framework\TestCase protected $valueMock; /** - * Class dependencies initialization - * - * @return void + * @inheritdoc */ protected function setUp() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php index ec84aa8e7b964..2de993c075514 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php @@ -19,9 +19,7 @@ class FileTest extends \PHPUnit\Framework\TestCase protected $valueMock; /** - * Class dependencies initialization - * - * @return void + * @inheritdoc */ protected function setUp() { @@ -60,7 +58,6 @@ protected function setUp() } /** - * @throws \Zend_Validate_Exception * @return void */ public function testIsValidSuccess() @@ -78,7 +75,6 @@ public function testIsValidSuccess() } /** - * @throws \Zend_Validate_Exception * @return void */ public function testIsValidWithNegativeImageSize() @@ -99,7 +95,6 @@ public function testIsValidWithNegativeImageSize() } /** - * @throws \Zend_Validate_Exception * @return void */ public function testIsValidWithNegativeImageSizeY() diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php index 94b5a05e48168..b97783edf856c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php @@ -19,9 +19,7 @@ class SelectTest extends \PHPUnit\Framework\TestCase protected $valueMock; /** - * Class dependencies initialization - * - * @return void + * @inheritdoc */ protected function setUp() { @@ -106,7 +104,6 @@ public function isValidSuccessDataProvider() } /** - * @throws \Zend_Validate_Exception * @return void */ public function testIsValidateWithInvalidOptionValues() @@ -128,7 +125,6 @@ public function testIsValidateWithInvalidOptionValues() } /** - * @throws \Zend_Validate_Exception * @return void */ public function testIsValidateWithEmptyValues() diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php index 9232630a7c03f..4881154728ddc 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php @@ -19,9 +19,7 @@ class TextTest extends \PHPUnit\Framework\TestCase protected $valueMock; /** - * Class dependencies initialization - * - * @return void + * @inheritdoc */ protected function setUp() { @@ -60,7 +58,6 @@ protected function setUp() } /** - * @throws \Zend_Validate_Exception * @return void */ public function testIsValidSuccess() @@ -77,7 +74,6 @@ public function testIsValidSuccess() } /** - * @throws \Zend_Validate_Exception * @return void */ public function testIsValidWithNegativeMaxCharacters() diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php index 3d65d1869dda7..82d39bbb7066a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php @@ -36,9 +36,7 @@ class ProductTest extends \PHPUnit\Framework\TestCase protected $_model; /** - * Class dependencies initialization - * - * @return void + * @inheritdoc */ protected function setUp() { From 40e4ae66196c49e09f5b60fa256539c814c04561 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Mon, 13 Aug 2018 11:31:49 +0300 Subject: [PATCH 0935/1171] ENGCOM-2719: Add Currency Converter API connecting feature #15542 --- .../Model/Currency/Import/CurrencyConverterApi.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php b/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php index c32fb36eb5b2e..7f323ba3200cd 100644 --- a/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php +++ b/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php @@ -1,4 +1,10 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + namespace Magento\Directory\Model\Currency\Import; class CurrencyConverterApi extends AbstractImport @@ -6,14 +12,14 @@ class CurrencyConverterApi extends AbstractImport /** * @var string */ - const CURRENCY_CONVERTER_URL = 'http://free.currencyconverterapi.com/api/v3/convert?q={{CURRENCY_FROM}}_{{CURRENCY_TO}}&compact=ultra'; + const CURRENCY_CONVERTER_URL = 'http://free.currencyconverterapi.com/api/v3/convert?q={{CURRENCY_FROM}}_{{CURRENCY_TO}}&compact=ultra'; //@codingStandardsIgnoreLine /** * Http Client Factory * * @var \Magento\Framework\HTTP\ZendClientFactory */ - protected $httpClientFactory; + private $httpClientFactory; /** * Core scope config @@ -68,7 +74,7 @@ public function fetchRates() */ private function convertBatch($data, $currencyFrom, $currenciesTo) { - foreach($currenciesTo as $to) { + foreach ($currenciesTo as $to) { set_time_limit(0); try { $url = str_replace('{{CURRENCY_FROM}}', $currencyFrom, self::CURRENCY_CONVERTER_URL); From b6ccebd2a7ae37a00a35cfd3252aee99933fcdce Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 13 Aug 2018 11:53:21 +0300 Subject: [PATCH 0936/1171] MAGETWO-67633: Advanced Pricing import validation results show 0 value for checked entities --- .../ImportExport/Model/Import/Entity/EntityAbstractTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/EntityAbstractTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/EntityAbstractTest.php index 718a81cfbc3f1..db4d9c5468640 100644 --- a/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/EntityAbstractTest.php +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/EntityAbstractTest.php @@ -20,6 +20,8 @@ class EntityAbstractTest extends \PHPUnit\Framework\TestCase { /** * Test for method _saveValidatedBunches() + * + * @return void */ public function testSaveValidatedBunches() : void { From abd9516b8bf509959e74d7dfcc00420d13af2c67 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 13 Aug 2018 12:00:33 +0300 Subject: [PATCH 0937/1171] MAGETWO-93763: [2.3] Error occurs when entering a new shipping address on admin order paid with Braintree --- .../view/adminhtml/web/js/braintree.js | 10 ++++++++- .../adminhtml/web/order/create/scripts.js | 22 +++++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) 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 9c95b79196e9d..ab01565d7f1e5 100644 --- a/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js +++ b/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js @@ -24,6 +24,7 @@ define([ scriptLoaded: false, braintree: null, selectedCardType: null, + checkout: null, imports: { onActiveChange: 'active' } @@ -147,6 +148,12 @@ define([ this.disableEventListeners(); + if (self.checkout) { + self.checkout.teardown(function () { + self.checkout = null; + }); + } + self.braintree.setup(self.clientToken, 'custom', { id: self.selector, hostedFields: self.getHostedFields(), @@ -154,7 +161,8 @@ define([ /** * Triggered when sdk was loaded */ - onReady: function () { + onReady: function (checkout) { + self.checkout = checkout; $('body').trigger('processStop'); self.enableEventListeners(); }, diff --git a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js index fbf5f5c1023e3..53ab6c204fa5b 100644 --- a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js +++ b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js @@ -39,6 +39,7 @@ define([ this.isOnlyVirtualProduct = false; this.excludedPaymentMethods = []; this.summarizePrice = true; + this.timerId = null; jQuery.async('#order-items', (function(){ this.dataArea = new OrderFormArea('data', $(this.getAreaId('data')), this); this.itemsArea = Object.extend(new OrderFormArea('items', $(this.getAreaId('items')), this), { @@ -189,14 +190,27 @@ define([ bindAddressFields : function(container) { var fields = $(container).select('input', 'select', 'textarea'); for(var i=0;i<fields.length;i++){ - Event.observe(fields[i], 'change', this.changeAddressField.bind(this)); + Event.observe(fields[i], 'change', this.triggerChangeEvent.bind(this)); } }, + /** + * Calls changing address field handler after timeout to prevent multiple simultaneous calls. + * + * @param {Event} event + */ + triggerChangeEvent: function (event) { + if (this.timerId) { + window.clearTimeout(this.timerId); + } + + this.timerId = window.setTimeout(this.changeAddressField.bind(this), 500, event); + }, + /** * Triggers on each form's element changes. * - * @param {Object} event + * @param {Event} event */ changeAddressField: function (event) { var field = Event.element(event), @@ -619,7 +633,7 @@ define([ } else if (((elms[i].type == 'checkbox' || elms[i].type == 'radio') && elms[i].checked) || ((elms[i].type == 'file' || elms[i].type == 'text' || elms[i].type == 'textarea' || elms[i].type == 'hidden') - && Form.Element.getValue(elms[i])) + && Form.Element.getValue(elms[i])) ) { if (this._isSummarizePrice(elms[i])) { productPrice += getPrice(elms[i]); @@ -1131,7 +1145,7 @@ define([ */ isPaymentValidationAvailable : function(){ return ((typeof this.paymentMethod) == 'undefined' - || this.excludedPaymentMethods.indexOf(this.paymentMethod) == -1); + || this.excludedPaymentMethods.indexOf(this.paymentMethod) == -1); }, serializeData : function(container){ From 61d8bc31cc8a023a21c5c0faedaedd7aee7294b4 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Mon, 13 Aug 2018 12:33:11 +0300 Subject: [PATCH 0938/1171] ENGCOM-2719: Add Currency Converter API connecting feature #15542 --- .../Directory/Model/Currency/Import/CurrencyConverterApi.php | 1 + .../Test/Unit/Controller/Adminhtml/Session/LogoutAllTest.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php b/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php index 7f323ba3200cd..f52886a14264d 100644 --- a/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php +++ b/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php @@ -141,5 +141,6 @@ private function getServiceResponse($url, $retry = 0) */ protected function _convert($currencyFrom, $currencyTo) { + return 1; } } diff --git a/app/code/Magento/Security/Test/Unit/Controller/Adminhtml/Session/LogoutAllTest.php b/app/code/Magento/Security/Test/Unit/Controller/Adminhtml/Session/LogoutAllTest.php index fd991f46f958d..02335ef55aa93 100644 --- a/app/code/Magento/Security/Test/Unit/Controller/Adminhtml/Session/LogoutAllTest.php +++ b/app/code/Magento/Security/Test/Unit/Controller/Adminhtml/Session/LogoutAllTest.php @@ -173,7 +173,7 @@ public function testExecuteException() ->method('logoutOtherUserSessions') ->willThrowException(new \Exception()); $this->messageManager->expects($this->once()) - ->method('addSuccessMessageptionMessage') + ->method('addExceptionMessage') ->with(new \Exception(), $phrase); $this->controller->execute(); } From df9902723ab669dd261c5d520d64f8a2aceb9b16 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 13 Aug 2018 13:33:46 +0300 Subject: [PATCH 0939/1171] MAGETWO-66745: Magento\Integration\Test\TestCase\ActivateIntegrationEntityTest with ActivateIntegrationEntityTestVariation1 fails randomly --- .../app/Magento/Integration/Test/Repository/Integration.xml | 1 + .../Integration/Test/TestCase/ActivateIntegrationEntityTest.xml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/Repository/Integration.xml b/dev/tests/functional/tests/app/Magento/Integration/Test/Repository/Integration.xml index abfc9e09cb439..7d4811c4cb558 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/Repository/Integration.xml +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/Repository/Integration.xml @@ -40,6 +40,7 @@ <item name="9" xsi:type="string">Stores</item> <item name="10" xsi:type="string">System</item> <item name="11" xsi:type="string">Global Search</item> + <item name="12" xsi:type="string">Catalog</item> </field> </dataset> </repository> diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/ActivateIntegrationEntityTest.xml b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/ActivateIntegrationEntityTest.xml index cae8dec6edcf1..11a8ed006af30 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/ActivateIntegrationEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/ActivateIntegrationEntityTest.xml @@ -14,7 +14,6 @@ <constraint name="Magento\Integration\Test\Constraint\AssertIntegrationResourcesPopup" /> <constraint name="Magento\Integration\Test\Constraint\AssertIntegrationTokensPopup" /> <constraint name="Magento\Integration\Test\Constraint\AssertIntegrationSuccessActivationMessage" /> - <data name="issue" xsi:type="string">MAGETWO-66745: Magento\Integration\Test\TestCase\ActivateIntegrationEntityTest with ActivateIntegrationEntityTestVariation1 fails randomly</data> </variation> </testCase> </config> From bcdd5986606b06ab93127ec11f6f09b27d0a2a45 Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Fri, 10 Aug 2018 22:04:25 +0200 Subject: [PATCH 0940/1171] Refactor: remove some code duplication + Complex statement that was executed three times moved to separate method so the logic is only on one place + The calculation for the index of the last record moved to a variable so the calculation is only being done once. This also reduces the amount of times `this.getChildItems()` is being called. --- .../base/web/js/dynamic-rows/dynamic-rows.js | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js index 7537107560cb4..870ad96732014 100644 --- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js +++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js @@ -330,9 +330,7 @@ define([ } if (this.defaultPagesState[this.currentPage()]) { - this.pagesChanged[this.currentPage()] = - !compareArrays(this.defaultPagesState[this.currentPage()], this.arrayFilter(this.getChildItems())); - this.changed(_.some(this.pagesChanged)); + this.setChangedForCurrentPage(); } }, @@ -442,13 +440,9 @@ define([ return initialize; })); - this.pagesChanged[this.currentPage()] = - !compareArrays(this.defaultPagesState[this.currentPage()], this.arrayFilter(this.getChildItems())); - this.changed(_.some(this.pagesChanged)); + this.setChangedForCurrentPage(); } else if (this.hasInitialPagesState[this.currentPage()]) { - this.pagesChanged[this.currentPage()] = - !compareArrays(this.defaultPagesState[this.currentPage()], this.arrayFilter(this.getChildItems())); - this.changed(_.some(this.pagesChanged)); + this.setChangedForCurrentPage(); } }, @@ -848,7 +842,8 @@ define([ deleteRecord: function (index, recordId) { var recordInstance, lastRecord, - recordsData; + recordsData, + lastRecordIndex; if (this.deleteProperty) { recordsData = this.recordData(); @@ -867,12 +862,13 @@ define([ this.update = true; if (~~this.currentPage() === this.pages()) { + lastRecordIndex = (this.startIndex + this.getChildItems().length - 1); lastRecord = _.findWhere(this.elems(), { - index: this.startIndex + this.getChildItems().length - 1 + index: lastRecordIndex }) || _.findWhere(this.elems(), { - index: (this.startIndex + this.getChildItems().length - 1).toString() + index: lastRecordIndex.toString() }); lastRecord.destroy(); @@ -1133,6 +1129,18 @@ define([ }); this.isDifferedFromDefault(!_.isEqual(recordData, this.default)); + }, + + /** + * Set the changed property if the current page is different + * than the default state + * + * @return void + */ + setChangedForCurrentPage: function () { + this.pagesChanged[this.currentPage()] = + !compareArrays(this.defaultPagesState[this.currentPage()], this.arrayFilter(this.getChildItems())); + this.changed(_.some(this.pagesChanged)); } }); }); From 50e74cd04b835760ea5b53cad47f61ec5f83892a Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Mon, 13 Aug 2018 08:52:48 +0300 Subject: [PATCH 0941/1171] Catalog: Add unit tests for Cron classes --- .../DeleteAbandonedStoreFlatTablesTest.php | 51 +++++++ .../Cron/DeleteOutdatedPriceValuesTest.php | 125 ++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Unit/Cron/DeleteAbandonedStoreFlatTablesTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php diff --git a/app/code/Magento/Catalog/Test/Unit/Cron/DeleteAbandonedStoreFlatTablesTest.php b/app/code/Magento/Catalog/Test/Unit/Cron/DeleteAbandonedStoreFlatTablesTest.php new file mode 100644 index 0000000000000..cd017dbcafbc2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Cron/DeleteAbandonedStoreFlatTablesTest.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\Cron; + +use Magento\Catalog\Cron\DeleteAbandonedStoreFlatTables; +use Magento\Catalog\Helper\Product\Flat\Indexer; + +/** + * @covers \Magento\Catalog\Cron\DeleteAbandonedStoreFlatTables + */ +class AvailabilityCheckerTest extends \PHPUnit\Framework\TestCase +{ + /** + * Testable Object + * + * @var DeleteAbandonedStoreFlatTables + */ + private $deleteAbandonedStoreFlatTables; + + /** + * @var Indexer|\PHPUnit_Framework_MockObject_MockObject + */ + private $indexerMock; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->indexerMock = $this->createMock(Indexer::class); + $this->deleteAbandonedStoreFlatTables = new DeleteAbandonedStoreFlatTables($this->indexerMock); + } + + /** + * Test execute method + * + * @return void + */ + public function testExecute() + { + $this->indexerMock->expects($this->once())->method('deleteAbandonedStoreFlatTables'); + $this->deleteAbandonedStoreFlatTables->execute(); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php b/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php new file mode 100644 index 0000000000000..304e203f228af --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php @@ -0,0 +1,125 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\Cron; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Cron\DeleteOutdatedPriceValues; +use Magento\Eav\Api\AttributeRepositoryInterface as AttributeRepository; +use Magento\Eav\Model\Entity\Attribute; +use Magento\Eav\Model\Entity\Attribute\Backend\BackendInterface; +use Magento\Framework\App\Config\MutableScopeConfigInterface as ScopeConfig; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Store\Model\Store; + +/** + * @covers \Magento\Catalog\Cron\DeleteOutdatedPriceValues + */ +class DeleteOutdatedPriceValuesTest extends \PHPUnit\Framework\TestCase +{ + /** + * Testable Object + * + * @var DeleteOutdatedPriceValues + */ + private $deleteOutdatedPriceValues; + + /** + * @var AttributeRepository|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeRepositoryMock; + + /** + * @var ResourceConnection|\PHPUnit_Framework_MockObject_MockObject + */ + private $resourceConnectionMock; + + /** + * @var ScopeConfig|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + + /** + * @var Attribute|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeMock; + + /** + * @var AdapterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $dbAdapterMock; + + /** + * @var BackendInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeBackendMock; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->resourceConnectionMock = $this->createMock(ResourceConnection::class); + $this->attributeRepositoryMock = $this->createMock(AttributeRepository::class); + $this->attributeMock = $this->createMock(Attribute::class); + $this->scopeConfigMock = $this->createMock(ScopeConfig::class); + $this->dbAdapterMock = $this->createMock(AdapterInterface::class); + $this->attributeBackendMock = $this->createMock(BackendInterface::class); + $this->deleteOutdatedPriceValues = new DeleteOutdatedPriceValues( + $this->resourceConnectionMock, + $this->attributeRepositoryMock, + $this->scopeConfigMock + ); + } + + /** + * Test execute method + * + * @return void + */ + public function testExecute() + { + $table = 'catalog_product_entity_decimal'; + $attributeId = 15; + $conditions = ['first', 'second']; + + $this->scopeConfigMock->expects($this->once())->method('getValue')->with(Store::XML_PATH_PRICE_SCOPE) + ->willReturn(Store::XML_PATH_PRICE_SCOPE); + $this->attributeRepositoryMock->expects($this->once())->method('get') + ->with(ProductAttributeInterface::ENTITY_TYPE_CODE, ProductAttributeInterface::CODE_PRICE) + ->willReturn($this->attributeMock); + $this->attributeMock->expects($this->once())->method('getId')->willReturn($attributeId); + $this->attributeMock->expects($this->once())->method('getBackend')->willReturn($this->attributeBackendMock); + $this->attributeBackendMock->expects($this->once())->method('getTable')->willReturn($table); + $this->resourceConnectionMock->expects($this->once())->method('getConnection')->willReturn($this->dbAdapterMock); + $this->dbAdapterMock->expects($this->exactly(2))->method('quoteInto')->willReturnMap([ + ['attribute_id = ?', $attributeId, null, null, $conditions[0]], + ['store_id != ?', Store::DEFAULT_STORE_ID, null, null, $conditions[1]], + ]); + $this->dbAdapterMock->expects($this->once())->method('delete')->with($table, $conditions); + $this->deleteOutdatedPriceValues->execute(); + } + + /** + * Test execute method + * The price scope config option is not equal to global value + * + * @return void + */ + public function testExecutePriceConfigIsNotSetToGlobal() + { + $this->scopeConfigMock->expects($this->once())->method('getValue')->with(Store::XML_PATH_PRICE_SCOPE) + ->willReturn(null); + $this->attributeRepositoryMock->expects($this->never())->method('get'); + $this->dbAdapterMock->expects($this->never())->method('delete'); + + $this->deleteOutdatedPriceValues->execute(); + } +} From c982b6221b826ae6401ede037575213a91aa74d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:30:30 +0300 Subject: [PATCH 0942/1171] removed lib-url-check --- lib/web/css/docs/source/_variables.less | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/lib/web/css/docs/source/_variables.less b/lib/web/css/docs/source/_variables.less index 326580ead813f..e1845786067c8 100644 --- a/lib/web/css/docs/source/_variables.less +++ b/lib/web/css/docs/source/_variables.less @@ -7375,21 +7375,3 @@ // </tr> // </table> // </pre> -// -// #### <code>.lib-url-check()</code> mixin variables -// <pre> -// <table> -// <tr> -// <th class="vars_head">Mixin variable</th> -// <th class="vars_head">Allowed values</th> -// <th class="vars_head">Output variable</th> -// <th class="vars_head">Comment</th> -// </tr> -// <tr> -// <th>@_path</th> -// <td class="vars_value">'' | false | value</td> -// <td class="vars_value">@lib-url-check-output</td> -// <td>Passed url to wrap in 'url( ... )'. If the 'false' value passed mixin will return 'false'</td> -// </tr> -// </table> -// </pre> From 15fbeb676c503002837c464ecce21a8f37e50839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:35:17 +0300 Subject: [PATCH 0943/1171] removed lib-url-check --- lib/web/css/docs/docs.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/docs.html b/lib/web/css/docs/docs.html index 164e5a1dfdc21..68e9d98eb2def 100644 --- a/lib/web/css/docs/docs.html +++ b/lib/web/css/docs/docs.html @@ -40,4 +40,4 @@ body { padding: 15px; background-image: none; -}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From ae9d62a6f84feda7662a69b79ae13160702cdcb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:37:56 +0300 Subject: [PATCH 0944/1171] remove lib-url-check --- lib/web/css/docs/actions-toolbar.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/actions-toolbar.html b/lib/web/css/docs/actions-toolbar.html index 4a3fbe515ca95..0c2186bf0458c 100644 --- a/lib/web/css/docs/actions-toolbar.html +++ b/lib/web/css/docs/actions-toolbar.html @@ -301,4 +301,4 @@ .example-actions-toolbar-12 { .lib-actions-toolbar-clear-floats(); } -}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 074bc271490e81d51826d6b60a6989792ef18d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:38:31 +0300 Subject: [PATCH 0945/1171] remove lib-url-check --- lib/web/css/docs/breadcrumbs.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/breadcrumbs.html b/lib/web/css/docs/breadcrumbs.html index 9ada940b4c0de..e4bfc3a973aa1 100644 --- a/lib/web/css/docs/breadcrumbs.html +++ b/lib/web/css/docs/breadcrumbs.html @@ -502,4 +502,4 @@ border-color: transparent transparent transparent #ccc; } } -}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From a7ddbb7062705b3f8a0fed20e871723addf2e670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:39:14 +0300 Subject: [PATCH 0946/1171] remove lib-url-check --- lib/web/css/docs/buttons.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/buttons.html b/lib/web/css/docs/buttons.html index 0890701226d3c..502d35da59a99 100644 --- a/lib/web/css/docs/buttons.html +++ b/lib/web/css/docs/buttons.html @@ -875,4 +875,4 @@ <h2 id="primary-button-big">Primary button big</h2> </tr> </table> </pre> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 9872e741a9f78dc5ffa81018529d93bf850d8ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:39:53 +0300 Subject: [PATCH 0947/1171] remove lib-url-check --- lib/web/css/docs/components.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/components.html b/lib/web/css/docs/components.html index 609bd4d3ffa00..9e0b149c989a9 100644 --- a/lib/web/css/docs/components.html +++ b/lib/web/css/docs/components.html @@ -147,4 +147,4 @@ <h1 class="modal-title" data-role="title">Modal Slide</h1> </tr> </table> </pre> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 358cbdfc76667f1fa2ed9411efcd977471e96c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:40:32 +0300 Subject: [PATCH 0948/1171] remove lib-url-check --- lib/web/css/docs/dropdowns.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/dropdowns.html b/lib/web/css/docs/dropdowns.html index 6f0c95a723beb..eb9efc9a65078 100644 --- a/lib/web/css/docs/dropdowns.html +++ b/lib/web/css/docs/dropdowns.html @@ -876,4 +876,4 @@ <h2 id="split-button-buttonbutton">Split button: button+button</h2> @_dropdown-split-list-shadow: none, @_dropdown-split-button-border-radius-fix: true ); -}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 96af19cd0f52d1bc014e45c18effe601b32477fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:41:10 +0300 Subject: [PATCH 0949/1171] remove lib-url-check --- lib/web/css/docs/forms.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/forms.html b/lib/web/css/docs/forms.html index ca3e2e6184677..211a9bd650ba0 100644 --- a/lib/web/css/docs/forms.html +++ b/lib/web/css/docs/forms.html @@ -1133,4 +1133,4 @@ <h2 id="simple-form-with-required-fields-message">Simple form with "require </tr> </table> </pre> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 917394cc11a3637a1f4b0f5afac79cc42f12dd19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:41:49 +0300 Subject: [PATCH 0950/1171] remove lib-url-check --- lib/web/css/docs/icons.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/icons.html b/lib/web/css/docs/icons.html index 1dca9797fc560..d7eeff7f802ca 100644 --- a/lib/web/css/docs/icons.html +++ b/lib/web/css/docs/icons.html @@ -847,4 +847,4 @@ <h2 id="icons-using-sprite">Icons using sprite</h2> } } } -}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 3d8d5224bfd90bf834e4f8d6401878888d124429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:43:42 +0300 Subject: [PATCH 0951/1171] remove lib-url-check --- lib/web/css/docs/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/index.html b/lib/web/css/docs/index.html index cbf3de48cdfc7..07cd72e6f823f 100644 --- a/lib/web/css/docs/index.html +++ b/lib/web/css/docs/index.html @@ -608,4 +608,4 @@ <h3 id="location">Location</h3> Extends that used in more than one theme should be saved in lib <strong>lib/source/_abstract.less</strong> (will be renamed to _extend.less)</p> <h3 id="naming">Naming</h3> <p>Extend class names should have prefix <strong>.abs-</strong> (from abstract)</p> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 290fd99bcef8819b30fd092bbd239df8051e1e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:44:10 +0300 Subject: [PATCH 0952/1171] remove lib-url-check --- lib/web/css/docs/layout.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/layout.html b/lib/web/css/docs/layout.html index a75e366f621ad..77ed0597f0748 100644 --- a/lib/web/css/docs/layout.html +++ b/lib/web/css/docs/layout.html @@ -340,4 +340,4 @@ <h2 id="three-columns-page-layout">Three columns page layout</h2> </tr> </table> </pre> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From da798ae926a3e0e21b2710a776432cfca51dc24d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:44:39 +0300 Subject: [PATCH 0953/1171] remove lib-url-check --- lib/web/css/docs/lib.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/lib.html b/lib/web/css/docs/lib.html index ccd5873e94303..001e8aeecd308 100644 --- a/lib/web/css/docs/lib.html +++ b/lib/web/css/docs/lib.html @@ -10,4 +10,4 @@ <p> The _lib.less file contains the includes of all Magento UI library files. To use Magento UI library in your theme add the following directive to the theme’s styles.less:</p> <pre><code class="lang-css"> @import 'source/lib/_lib';</code></pre> <p> The lib.less file is designed to avoid manual adding of each Magento UI library file import instruction to your theme.</p> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From ddfb194a71f3a96529f9e67bbc4b670ca3bd2190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:45:36 +0300 Subject: [PATCH 0954/1171] remove lib-url-check --- lib/web/css/docs/loaders.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/loaders.html b/lib/web/css/docs/loaders.html index 6d63110cfafea..884b71c782905 100644 --- a/lib/web/css/docs/loaders.html +++ b/lib/web/css/docs/loaders.html @@ -182,4 +182,4 @@ </tr> </table> </pre> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 183c998e555312b075cc776e2fc4ec96f611e936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:46:04 +0300 Subject: [PATCH 0955/1171] remove lib-url-check --- lib/web/css/docs/messages.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/messages.html b/lib/web/css/docs/messages.html index 305266906e3d0..287bf1b0d3aa4 100644 --- a/lib/web/css/docs/messages.html +++ b/lib/web/css/docs/messages.html @@ -711,4 +711,4 @@ </tr> </table> </pre> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 373cbae1aede4027d2ba494d43c5d8ffd642b2af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:47:01 +0300 Subject: [PATCH 0956/1171] remove lib-url-check --- lib/web/css/docs/pages.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/pages.html b/lib/web/css/docs/pages.html index 8b7335d86b8da..1bbcac94c56f8 100644 --- a/lib/web/css/docs/pages.html +++ b/lib/web/css/docs/pages.html @@ -826,4 +826,4 @@ @_pager-action-color-hover: #fff, @_pager-action-color-active: #fff ); -}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From f550c9b25810df37a8678bf4e4a63bc54c0d8db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:47:51 +0300 Subject: [PATCH 0957/1171] remove lib-url-check --- lib/web/css/docs/popups.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/popups.html b/lib/web/css/docs/popups.html index d31d50f6ed7c6..9f7aff4c6569b 100644 --- a/lib/web/css/docs/popups.html +++ b/lib/web/css/docs/popups.html @@ -750,4 +750,4 @@ <h2 id="simple-popup">Simple popup</h2> @_overlay-opacity: .8, @_overlay-opacity-old: 80 ); -}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From e886063dff3858e8bf6907348525062f49bb280f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:48:43 +0300 Subject: [PATCH 0958/1171] remove lib-url-check --- lib/web/css/docs/rating.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/rating.html b/lib/web/css/docs/rating.html index bf74c47c22f11..3cfe1a864dd98 100644 --- a/lib/web/css/docs/rating.html +++ b/lib/web/css/docs/rating.html @@ -343,4 +343,4 @@ </div><div class="code"><pre><code>.example-rating-summary-7 { .lib-rating-summary(); .lib-rating-summary-label-hide(); -}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 5e7b9e710c906c286f6cbd6cfdef081d7692da61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:49:19 +0300 Subject: [PATCH 0959/1171] remove lib-url-check --- lib/web/css/docs/resets.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/resets.html b/lib/web/css/docs/resets.html index f2eb5ee9b48db..fc1cb092422c0 100644 --- a/lib/web/css/docs/resets.html +++ b/lib/web/css/docs/resets.html @@ -34,4 +34,4 @@ <h2 id="global-borderbox">Global border-box</h2> <p> To set <code>box-sizing: border-box</code> globally, use mixin:</p> <pre><code class="lang-CSS"> .lib-set-default-border-box();</code></pre> <p>  </p> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 7f173f6707b31832902f1b1fb102c5d677e15290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:49:43 +0300 Subject: [PATCH 0960/1171] remove lib-url-check --- lib/web/css/docs/responsive.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/responsive.html b/lib/web/css/docs/responsive.html index 5e9d3b38eddd0..48d0bd551bd92 100644 --- a/lib/web/css/docs/responsive.html +++ b/lib/web/css/docs/responsive.html @@ -80,4 +80,4 @@ <h2 id="gathering">Gathering</h2> @screen__l: 1024px; @screen__xl: 1440px;</code></pre> <p>  </p> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 988820bc60842ec2f9b3706e0b75c4d2378bccbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:50:20 +0300 Subject: [PATCH 0961/1171] remove lib-url-check --- lib/web/css/docs/sections.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/sections.html b/lib/web/css/docs/sections.html index c433216775ef4..8ed121578757a 100644 --- a/lib/web/css/docs/sections.html +++ b/lib/web/css/docs/sections.html @@ -643,4 +643,4 @@ </dl></textarea> </div><div class="code"><pre><code>.example-sections-6 { .lib-data-accordion__base(); -}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +}</code></pre></div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 3fe56763fc8798933678704fa2cfc9a2fd175a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:50:56 +0300 Subject: [PATCH 0962/1171] remove lib-url-check --- lib/web/css/docs/tables.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/tables.html b/lib/web/css/docs/tables.html index 5a5067ebd0e4b..280f32135de62 100644 --- a/lib/web/css/docs/tables.html +++ b/lib/web/css/docs/tables.html @@ -1498,4 +1498,4 @@ </tr> </table> </pre> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 475ee79dd365e5d4f14aea50c760d5cb1c965733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:51:55 +0300 Subject: [PATCH 0963/1171] remove lib-url-check --- lib/web/css/docs/tooltips.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/tooltips.html b/lib/web/css/docs/tooltips.html index 20158046859f6..2b38aa0a55aef 100644 --- a/lib/web/css/docs/tooltips.html +++ b/lib/web/css/docs/tooltips.html @@ -186,4 +186,4 @@ </tr> </table> </pre> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 904b16819171fdc654215742f15bd08b6a51b8a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:53:06 +0300 Subject: [PATCH 0964/1171] remove lib-url-check --- lib/web/css/docs/typography.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/typography.html b/lib/web/css/docs/typography.html index 76f704c17c1bd..a933bcb46bf64 100644 --- a/lib/web/css/docs/typography.html +++ b/lib/web/css/docs/typography.html @@ -1686,4 +1686,4 @@ </tr> </table> </pre> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From f34491b2e4e5b223697575f820fbb37facc76778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:54:05 +0300 Subject: [PATCH 0965/1171] remove lib-url-check --- lib/web/css/docs/variables.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/docs/variables.html b/lib/web/css/docs/variables.html index 4423d682d0f80..4f353dc1554a4 100644 --- a/lib/web/css/docs/variables.html +++ b/lib/web/css/docs/variables.html @@ -7391,4 +7391,4 @@ <h4 id="codeliburlcheckcode-mixin-variables"><code>.lib-url-check()</code> mixin </tr> </table> </pre> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 49ddf600e66988f1d0eafb4c24f7fbd5f9db8400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 9 Aug 2018 21:59:22 +0300 Subject: [PATCH 0966/1171] remove lib-url-check --- lib/web/css/docs/utilities.html | 48 +-------------------------------- 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/lib/web/css/docs/utilities.html b/lib/web/css/docs/utilities.html index 29ecce2414e1e..8ab4a1136967e 100644 --- a/lib/web/css/docs/utilities.html +++ b/lib/web/css/docs/utilities.html @@ -261,50 +261,4 @@ </tr> </table> </pre> -</div></article><article id="liburlcheck" class="section"><div class="docs"><a href="#liburlcheck" class="permalink"><svg viewBox="0 0 512 512" height="32" width="32" class="icon"><path d="M156.2,199.7c7.5-7.5,15.9-13.8,24.8-18.7c49.6-27.3,113.1-12.8,145,35.5l-38.5,38.5c-11.1-25.2-38.5-39.6-65.8-33.5c-10.3,2.3-20.1,7.4-28,15.4l-73.9,73.9c-22.4,22.4-22.4,58.9,0,81.4c22.4,22.4,58.9,22.4,81.4,0l22.8-22.8c20.7,8.2,42.9,11.5,64.9,9.9l-50.3,50.3c-43.1,43.1-113,43.1-156.1,0c-43.1-43.1-43.1-113-0-156.1L156.2,199.7z M273.6,82.3l-50.3,50.3c21.9-1.6,44.2,1.6,64.9,9.9l22.8-22.8c22.4-22.4,58.9-22.4,81.4,0c22.4,22.4,22.4,58.9,0,81.4l-73.9,73.9c-22.5,22.5-59.1,22.3-81.4,0c-5.2-5.2-9.7-11.7-12.5-18l-38.5,38.5c4,6.1,8.3,11.5,13.7,16.9c13.9,13.9,31.7,24.3,52.1,29.3c26.5,6.4,54.8,2.8,79.2-10.6c8.9-4.9,17.3-11.1,24.8-18.7l73.9-73.9c43.1-43.1,43.1-113,0-156.1C386.6,39.2,316.7,39.2,273.6,82.3z"></path></svg></a><h1 id="liburlcheck">.lib-url-check()</h1> -<p> The <code>.lib-url-check()</code> mixin wraps passed value with 'url( ... )' and returns <code>@lib-url-check-output</code> variable. Can be used with <code>.lib-css()</code> mixin.</p> -<p> If the variable is set to <code>false</code>, the <code>.lib-url-check()</code> will return false.</p> -<textarea class="preview-code" spellcheck="false"> <div class="example-url-check"> - Block with background. - </div></textarea><textarea class="preview-code" spellcheck="false"> <div class="example-url-check-false"> - Block with no background. - </div></textarea> -</div><div class="code"><pre><code>.example-url-check { - // Set image path variable - @_icon-image: '/images/test.png'; - - // "Call" the mixin - .lib-url-check(@_icon-image); - - // Will return url('/images/test.png') - .lib-css(background, #eee @lib-url-check-output no-repeat 0 0); -} - - -.example-url-check-false { - // Set usage image path to false - @_icon-image: false; - - // "Call" the mixin - .lib-url-check(@_icon-image); - - // Will return 'false' and outputs nothing - .lib-css(background, #eee @lib-url-check-output no-repeat 0 0); -}</code></pre></div></article><article id="liburlcheck-variables" class="section"><div class="docs"><a href="#liburlcheck-variables" class="permalink"><svg viewBox="0 0 512 512" height="32" width="32" class="icon"><path d="M156.2,199.7c7.5-7.5,15.9-13.8,24.8-18.7c49.6-27.3,113.1-12.8,145,35.5l-38.5,38.5c-11.1-25.2-38.5-39.6-65.8-33.5c-10.3,2.3-20.1,7.4-28,15.4l-73.9,73.9c-22.4,22.4-22.4,58.9,0,81.4c22.4,22.4,58.9,22.4,81.4,0l22.8-22.8c20.7,8.2,42.9,11.5,64.9,9.9l-50.3,50.3c-43.1,43.1-113,43.1-156.1,0c-43.1-43.1-43.1-113-0-156.1L156.2,199.7z M273.6,82.3l-50.3,50.3c21.9-1.6,44.2,1.6,64.9,9.9l22.8-22.8c22.4-22.4,58.9-22.4,81.4,0c22.4,22.4,22.4,58.9,0,81.4l-73.9,73.9c-22.5,22.5-59.1,22.3-81.4,0c-5.2-5.2-9.7-11.7-12.5-18l-38.5,38.5c4,6.1,8.3,11.5,13.7,16.9c13.9,13.9,31.7,24.3,52.1,29.3c26.5,6.4,54.8,2.8,79.2-10.6c8.9-4.9,17.3-11.1,24.8-18.7l73.9-73.9c43.1-43.1,43.1-113,0-156.1C386.6,39.2,316.7,39.2,273.6,82.3z"></path></svg></a><h1 id="liburlcheck-variables">.lib-url-check() variables</h1> - <pre> - <table> - <tr> - <th class="vars_head">Mixin variable</th> - <th class="vars_head">Allowed values</th> - <th class="vars_head">Output variable</th> - <th class="vars_head">Comment</th> - </tr> - <tr> - <th>@_path</th> - <td class="vars_value">'' | false | value</td> - <td class="vars_value">@lib-url-check-output</td> - <td>Passed url to wrap in 'url( ... )'. If the 'false' value passed mixin will return 'false'</td> - </tr> - </table> - </pre> -</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:".lib-url-check()",filename:"utilities",url:"utilities.html#liburlcheck"},{title:".lib-url-check() variables",filename:"utilities",url:"utilities.html#liburlcheck-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> +</div></article></section><div class="bar bottom"><div hidden class="settings container"><!-- Icons from http://iconmonstr.com--><button title="Desktop (1280)" data-width='1280'><svg viewBox="0 0 412 386" height="24" width="26" class="icon"><path d="m147.6,343.9c-4.5,15.9-26.2,37.6-42.1,42.1h201c-15.3,-4-38.1,-26.8-42.1,-42.1H147.6zM387,0.5H25c-13.8,0-25,11.2-25,25V294c0,13.8 11.2,25 25,25h362c13.8,0 25,-11.2 25,-25V25.5C412,11.7 400.8,0.5 387,0.5zM369.9,238.2H42.1L42.1,42.6 369.9,42.6V238.2z"></path></svg></button><button title="Laptop (1024)" data-width='1024'><svg viewBox="0 0 384 312" height="23" width="28" class="icon"><path d="m349.2,20.5c0,-11-9,-20-20,-20H53.6c-11,0-20,9-20,20v194H349.2v-194zm-27,167H60.6V27.5H322.2v160zm28,42H32.6L2.6,282.1c-3.5,6.2-3.5,13.8 0.1,19.9 3.6,6.2 10.2,9.9 17.3,9.9H363.1c7.1,0 13.7,-3.8 17.3,-10 3.6,-6.2 3.6,-13.8 0,-20l-30.2,-52.5zm-196.9,54 8,-23.5h60.5l8,23.5h-76.5z"></path></svg></button><button title="Tablet (768)" data-width='768'><svg viewBox="0 0 317 412" height="24" width="18" class="icon"><path d="M 316.5,380 V 32 c 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,0 0,14.3 0,32 v 348 c 0,17.7 14.3,32 32,32 h 252.5 c 17.7,0 32,-14.3 32,-32 z M 40,367 V 45 H 276.5 V 367 H 40 z m 109.8,22.7 c 0,-4.7 3.8,-8.5 8.5,-8.5 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 -4.7,0 -8.5,-3.8 -8.5,-8.5 z"></path></svg></button><button title="Smart phone (320)" data-width='320'><svg viewBox="0 0 224 412" height="24" width="13" class="icon"><path d="M 190.7,0 H 33 C 14.8,0 0,14.8 0,33 v 346 c 0,18.2 14.8,33 33,33 h 157.7 c 18.2,0 33,-14.8 33,-33 V 33 c 0,-18.2 -14.8,-33 -33,-33 z M 94.3,30.2 h 37 c 2.2,0 4,1.8 4,4 0,2.2 -1.8,4 -4,4 h -37 c -2.2,0 -4,-1.8 -4,-4 0,-2.2 1.8,-4 4,-4 z m 18.5,362.8 c -8.8,0 -16,-7.2 -16,-16 0,-8.8 7.2,-16 16,-16 8.8,0 16,7.2 16,16 0,8.8 -7.2,16 -16,16 z M 198.6,343.8 H 25.1 V 68.2 h 173.5 v 275.5 z"></path></svg></button><button title="Feature phone (240)" data-width='240'><svg viewBox="0 0 201 412" height="24" width="12" class="icon"><path d="M 165.5,0.2 V 45 H 25 c -13.8,0 -25,11.2 -25,25 V 387 c 0,13.8 11.2,25 25,25 h 150.5 c 13.8,0 25,-11.2 25,-25 V 0.2 h -35 z M 65.2,366.5 H 34.2 v -24.5 h 31 v 24.5 z m 0,-44.3 H 34.2 v -24.5 h 31 v 24.5 z m 50.5,44.3 H 84.7 v -24.5 h 31 v 24.5 z m 0,-44.3 H 84.7 v -24.5 h 31 v 24.5 z m 50.5,44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-44.3 h -31 v -24.5 h 31 v 24.5 z m 0,-59.3 h -132 V 95.4 h 132 V 262.9 z"></path></svg></button><button title="Auto (100%)" data-width="auto" class="auto is-active">Auto</button></div></div><script>(function(){var a=[{title:"actions-toolbar",filename:"actions-toolbar",url:"actions-toolbar.html"},{title:"Actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar"},{title:"Actions toolbar mixin variables",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-mixin-variables"},{title:"Actions toolbar alignment",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-alignment"},{title:"Reverse primary and secondary blocks",filename:"actions-toolbar",url:"actions-toolbar.html#reverse-primary-and-secondary-blocks"},{title:"Actions toolbar indents customizations",filename:"actions-toolbar",url:"actions-toolbar.html#actions-toolbar-indents-customizations"},{title:"Responsive actions toolbar",filename:"actions-toolbar",url:"actions-toolbar.html#responsive-actions-toolbar"},{title:"breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html"},{title:"Breadcrumbs",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs"},{title:"Breadcrumbs variables",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-variables"},{title:"Button-styled breadcrumbs with gradient background, border, and no separating symbol",filename:"breadcrumbs",url:"breadcrumbs.html#buttonstyled-breadcrumbs-with-gradient-background-border-and-no-separating-symbol"},{title:"Breadcrumbs with solid background",filename:"breadcrumbs",url:"breadcrumbs.html#breadcrumbs-with-solid-background"},{title:"buttons",filename:"buttons",url:"buttons.html"},{title:"Default button",filename:"buttons",url:"buttons.html#default-button"},{title:"Button variables",filename:"buttons",url:"buttons.html#button-variables"},{title:"Button as an icon",filename:"buttons",url:"buttons.html#button-as-an-icon"},{title:"Button with an icon on the left or right side of the text",filename:"buttons",url:"buttons.html#button-with-an-icon-on-the-left-or-right-side-of-the-text"},{title:"Button with fixed width",filename:"buttons",url:"buttons.html#button-with-fixed-width"},{title:"Primary button",filename:"buttons",url:"buttons.html#primary-button"},{title:"Primary button variables",filename:"buttons",url:"buttons.html#primary-button-variables"},{title:"Button with gradient background",filename:"buttons",url:"buttons.html#button-with-gradient-background"},{title:"Button as a link",filename:"buttons",url:"buttons.html#button-as-a-link"},{title:"Button as a link variables",filename:"buttons",url:"buttons.html#button-as-a-link-variables"},{title:"Link as a button",filename:"buttons",url:"buttons.html#link-as-a-button"},{title:"Button reset",filename:"buttons",url:"buttons.html#button-reset"},{title:"Button revert secondary color",filename:"buttons",url:"buttons.html#button-revert-secondary-color"},{title:"Button revert secondary color variables",filename:"buttons",url:"buttons.html#button-revert-secondary-color-variables"},{title:"Button revert secondary size",filename:"buttons",url:"buttons.html#button-revert-secondary-size"},{title:"Button revert secondary size variables",filename:"buttons",url:"buttons.html#button-revert-secondary-size-variables"},{title:"components",filename:"components",url:"components.html"},{title:"Components",filename:"components",url:"components.html#components"},{title:"Components Variables",filename:"components",url:"components.html#components-variables"},{title:"dropdowns",filename:"dropdowns",url:"dropdowns.html"},{title:"Drop-down and split buttons mixins",filename:"dropdowns",url:"dropdowns.html#dropdown-and-split-buttons-mixins"},{title:"Drop-down",filename:"dropdowns",url:"dropdowns.html#dropdown"},{title:"Drop-down variables",filename:"dropdowns",url:"dropdowns.html#dropdown-variables"},{title:"Drop-down with icon customization",filename:"dropdowns",url:"dropdowns.html#dropdown-with-icon-customization"},{title:"Modify dropdown list styles",filename:"dropdowns",url:"dropdowns.html#modify-dropdown-list-styles"},{title:"Split button",filename:"dropdowns",url:"dropdowns.html#split-button"},{title:"Split button variables",filename:"dropdowns",url:"dropdowns.html#split-button-variables"},{title:"Split button - button styling",filename:"dropdowns",url:"dropdowns.html#split-button-button-styling"},{title:"Split button icon customization",filename:"dropdowns",url:"dropdowns.html#split-button-icon-customization"},{title:"Split button drop-down list customization",filename:"dropdowns",url:"dropdowns.html#split-button-dropdown-list-customization"},{title:"forms",filename:"forms",url:"forms.html"},{title:"Forms mixins",filename:"forms",url:"forms.html#forms-mixins"},{title:"Global forms elements customization",filename:"forms",url:"forms.html#global-forms-elements-customization"},{title:"Fieldsets & fields customization",filename:"forms",url:"forms.html#fieldsets-fields-customization"},{title:"Fieldset and legend customization variables",filename:"forms",url:"forms.html#fieldset-and-legend-customization-variables"},{title:"Fields customization variables",filename:"forms",url:"forms.html#fields-customization-variables"},{title:"Required fields message customization variables",filename:"forms",url:"forms.html#required-fields-message-customization-variables"},{title:"Form element inputs customization",filename:"forms",url:"forms.html#form-element-inputs-customization"},{title:"Form element inputs customization variables",filename:"forms",url:"forms.html#form-element-inputs-customization-variables"},{title:"Form element choice",filename:"forms",url:"forms.html#form-element-choice"},{title:"Form element choice variables",filename:"forms",url:"forms.html#form-element-choice-variables"},{title:"Custom color",filename:"forms",url:"forms.html#custom-color"},{title:"Input number - input-text view",filename:"forms",url:"forms.html#input-number-inputtext-view"},{title:"Input search - input-text view",filename:"forms",url:"forms.html#input-search-inputtext-view"},{title:"Form validation",filename:"forms",url:"forms.html#form-validation"},{title:"Form validation variables",filename:"forms",url:"forms.html#form-validation-variables"},{title:"icons",filename:"icons",url:"icons.html"},{title:"Icons",filename:"icons",url:"icons.html#icons"},{title:"Icon with image or sprite",filename:"icons",url:"icons.html#icon-with-image-or-sprite"},{title:"Icon with image or sprite variables",filename:"icons",url:"icons.html#icon-with-image-or-sprite-variables"},{title:"Icon position for an icon with image or sprite",filename:"icons",url:"icons.html#icon-position-for-an-icon-with-image-or-sprite"},{title:"Position for icon with image or sprite mixin variables",filename:"icons",url:"icons.html#position-for-icon-with-image-or-sprite-mixin-variables"},{title:"Icon sprite position (with grid)",filename:"icons",url:"icons.html#icon-sprite-position-with-grid"},{title:"Icon sprite position variables",filename:"icons",url:"icons.html#icon-sprite-position-variables"},{title:"Image/sprite icon size",filename:"icons",url:"icons.html#imagesprite-icon-size"},{title:"Image/sprite icon size variables",filename:"icons",url:"icons.html#imagesprite-icon-size-variables"},{title:"Font icon",filename:"icons",url:"icons.html#font-icon"},{title:"Font icon variables",filename:"icons",url:"icons.html#font-icon-variables"},{title:"Change the size of font icon",filename:"icons",url:"icons.html#change-the-size-of-font-icon"},{title:"Change the size of font icon variables",filename:"icons",url:"icons.html#change-the-size-of-font-icon-variables"},{title:"Hide icon text",filename:"icons",url:"icons.html#hide-icon-text"},{title:"Sprite and font icons for Blank theme",filename:"icons",url:"icons.html#sprite-and-font-icons-for-blank-theme"},{title:"layout",filename:"layout",url:"layout.html"},{title:"Layout",filename:"layout",url:"layout.html#layout"},{title:"Layout global variables",filename:"layout",url:"layout.html#layout-global-variables"},{title:"Page layouts",filename:"layout",url:"layout.html#page-layouts"},{title:"Layout column",filename:"layout",url:"layout.html#layout-column"},{title:"Layout column variables",filename:"layout",url:"layout.html#layout-column-variables"},{title:"Layout width",filename:"layout",url:"layout.html#layout-width"},{title:"Layout width variables",filename:"layout",url:"layout.html#layout-width-variables"},{title:"lib",filename:"lib",url:"lib.html"},{title:"Including Magento UI library to your theme",filename:"lib",url:"lib.html#including-magento-ui-library-to-your-theme"},{title:"loaders",filename:"loaders",url:"loaders.html"},{title:"Loaders",filename:"loaders",url:"loaders.html#loaders"},{title:"Default loader variables",filename:"loaders",url:"loaders.html#default-loader-variables"},{title:"Loading",filename:"loaders",url:"loaders.html#loading"},{title:"Loading default variables",filename:"loaders",url:"loaders.html#loading-default-variables"},{title:"messages",filename:"messages",url:"messages.html"},{title:"Messages",filename:"messages",url:"messages.html#messages"},{title:"Information message",filename:"messages",url:"messages.html#information-message"},{title:"Warning message",filename:"messages",url:"messages.html#warning-message"},{title:"Error message",filename:"messages",url:"messages.html#error-message"},{title:"Success message",filename:"messages",url:"messages.html#success-message"},{title:"Notice message",filename:"messages",url:"messages.html#notice-message"},{title:"Message with inner icon",filename:"messages",url:"messages.html#message-with-inner-icon"},{title:"Message with lateral icon",filename:"messages",url:"messages.html#message-with-lateral-icon"},{title:"Custom message style",filename:"messages",url:"messages.html#custom-message-style"},{title:"Messages global variables",filename:"messages",url:"messages.html#messages-global-variables"},{title:"pages",filename:"pages",url:"pages.html"},{title:"Pagination HTML markup",filename:"pages",url:"pages.html#pagination-html-markup"},{title:"Pagination variables",filename:"pages",url:"pages.html#pagination-variables"},{title:"Pagination with label and gradient background on links",filename:"pages",url:"pages.html#pagination-with-label-and-gradient-background-on-links"},{title:'Pagination with "previous"..."next" text links and label',filename:"pages",url:"pages.html#pagination-with-previousnext-text-links-and-label"},{title:"Pagination without label, with solid background",filename:"pages",url:"pages.html#pagination-without-label-with-solid-background"},{title:"popups",filename:"popups",url:"popups.html"},{title:"Popups",filename:"popups",url:"popups.html#popups"},{title:"Popup variables",filename:"popups",url:"popups.html#popup-variables"},{title:"Window overlay mixin variables",filename:"popups",url:"popups.html#window-overlay-mixin-variables"},{title:"Fixed height popup",filename:"popups",url:"popups.html#fixed-height-popup"},{title:"Fixed content height popup",filename:"popups",url:"popups.html#fixed-content-height-popup"},{title:"Margins for header, content and footer block in popup",filename:"popups",url:"popups.html#margins-for-header-content-and-footer-block-in-popup"},{title:"Popup titles styled as theme headings",filename:"popups",url:"popups.html#popup-titles-styled-as-theme-headings"},{title:"Popup action toolbar",filename:"popups",url:"popups.html#popup-action-toolbar"},{title:"Popup Close button without an icon",filename:"popups",url:"popups.html#popup-close-button-without-an-icon"},{title:"Modify the icon of popup Close button",filename:"popups",url:"popups.html#modify-the-icon-of-popup-close-button"},{title:"Modify overlay styles",filename:"popups",url:"popups.html#modify-overlay-styles"},{title:"rating",filename:"rating",url:"rating.html"},{title:"Ratings",filename:"rating",url:"rating.html#ratings"},{title:"Global rating variables",filename:"rating",url:"rating.html#global-rating-variables"},{title:"Rating with vote",filename:"rating",url:"rating.html#rating-with-vote"},{title:"Rating with vote icons number customization",filename:"rating",url:"rating.html#rating-with-vote-icons-number-customization"},{title:"Rating with vote icons colors customization",filename:"rating",url:"rating.html#rating-with-vote-icons-colors-customization"},{title:"Rating with vote icons symbol customization",filename:"rating",url:"rating.html#rating-with-vote-icons-symbol-customization"},{title:"Accessible rating with vote",filename:"rating",url:"rating.html#accessible-rating-with-vote"},{title:"Rating summary",filename:"rating",url:"rating.html#rating-summary"},{title:"Rating summary icons number customization",filename:"rating",url:"rating.html#rating-summary-icons-number-customization"},{title:"Rating summary icons color customization",filename:"rating",url:"rating.html#rating-summary-icons-color-customization"},{title:"Rating summary icons symbol customization",filename:"rating",url:"rating.html#rating-summary-icons-symbol-customization"},{title:"Rating summary hide label",filename:"rating",url:"rating.html#rating-summary-hide-label"},{title:"Rating summary multiple ratings",filename:"rating",url:"rating.html#rating-summary-multiple-ratings"},{title:"Rating hide label mixin",filename:"rating",url:"rating.html#rating-hide-label-mixin"},{title:"resets",filename:"resets",url:"resets.html"},{title:"Resets",filename:"resets",url:"resets.html#resets"},{title:"responsive",filename:"responsive",url:"responsive.html"},{title:"Responsive",filename:"responsive",url:"responsive.html#responsive"},{title:"Responsive mixins usage",filename:"responsive",url:"responsive.html#responsive-mixins-usage"},{title:"Media query style groups separation variables",filename:"responsive",url:"responsive.html#media-query-style-groups-separation-variables"},{title:"Responsive breakpoints",filename:"responsive",url:"responsive.html#responsive-breakpoints"},{title:"sections",filename:"sections",url:"sections.html"},{title:"Tabs and accordions",filename:"sections",url:"sections.html#tabs-and-accordions"},{title:"Tabs",filename:"sections",url:"sections.html#tabs"},{title:"Tabs mixin variables",filename:"sections",url:"sections.html#tabs-mixin-variables"},{title:"Tabs with content top border",filename:"sections",url:"sections.html#tabs-with-content-top-border"},{title:"Accordion",filename:"sections",url:"sections.html#accordion"},{title:"Accordion mixin variables",filename:"sections",url:"sections.html#accordion-mixin-variables"},{title:"Responsive tabs",filename:"sections",url:"sections.html#responsive-tabs"},{title:"Tabs Base",filename:"sections",url:"sections.html#tabs-base"},{title:"Accordion Base",filename:"sections",url:"sections.html#accordion-base"},{title:"tables",filename:"tables",url:"tables.html"},{title:"Tables",filename:"tables",url:"tables.html#tables"},{title:"Table mixin variables",filename:"tables",url:"tables.html#table-mixin-variables"},{title:"Table typography",filename:"tables",url:"tables.html#table-typography"},{title:"Table typography mixin variables",filename:"tables",url:"tables.html#table-typography-mixin-variables"},{title:"Table caption",filename:"tables",url:"tables.html#table-caption"},{title:"Table caption mixin variables",filename:"tables",url:"tables.html#table-caption-mixin-variables"},{title:"Table cells resize",filename:"tables",url:"tables.html#table-cells-resize"},{title:"Table cells resize variables",filename:"tables",url:"tables.html#table-cells-resize-variables"},{title:"Table background customization",filename:"tables",url:"tables.html#table-background-customization"},{title:"Table background mixin variables",filename:"tables",url:"tables.html#table-background-mixin-variables"},{title:"Table borders customization",filename:"tables",url:"tables.html#table-borders-customization"},{title:"Table borders mixin variables",filename:"tables",url:"tables.html#table-borders-mixin-variables"},{title:"Table with horizontal borders",filename:"tables",url:"tables.html#table-with-horizontal-borders"},{title:"Table with vertical borders",filename:"tables",url:"tables.html#table-with-vertical-borders"},{title:"Table with light borders",filename:"tables",url:"tables.html#table-with-light-borders"},{title:"Table without borders",filename:"tables",url:"tables.html#table-without-borders"},{title:"Striped table",filename:"tables",url:"tables.html#striped-table"},{title:"Striped table mixin variables",filename:"tables",url:"tables.html#striped-table-mixin-variables"},{title:"Table with rows hover",filename:"tables",url:"tables.html#table-with-rows-hover"},{title:"Table with rows hover mixin variables",filename:"tables",url:"tables.html#table-with-rows-hover-mixin-variables"},{title:"Responsive table technics #1",filename:"tables",url:"tables.html#responsive-table-technics-1"},{title:"Responsive table technics #2",filename:"tables",url:"tables.html#responsive-table-technics-2"},{title:"Responsive table technics #2 mixin variables",filename:"tables",url:"tables.html#responsive-table-technics-2-mixin-variables"},{title:"tooltips",filename:"tooltips",url:"tooltips.html"},{title:"Tooltips",filename:"tooltips",url:"tooltips.html#tooltips"},{title:"Tooltips variables",filename:"tooltips",url:"tooltips.html#tooltips-variables"},{title:"typography",filename:"typography",url:"typography.html"},{title:"Typogrphy",filename:"typography",url:"typography.html#typogrphy"},{title:"Typography variables",filename:"typography",url:"typography.html#typography-variables"},{title:"Font-size mixin",filename:"typography",url:"typography.html#fontsize-mixin"},{title:"Line-height mixin",filename:"typography",url:"typography.html#lineheight-mixin"},{title:"Word breaking mixin",filename:"typography",url:"typography.html#word-breaking-mixin"},{title:"Font face mixin",filename:"typography",url:"typography.html#font-face-mixin"},{title:"Text overflow mixin",filename:"typography",url:"typography.html#text-overflow-mixin"},{title:"Text hide",filename:"typography",url:"typography.html#text-hide"},{title:"Hyphens",filename:"typography",url:"typography.html#hyphens"},{title:"Font style and color",filename:"typography",url:"typography.html#font-style-and-color"},{title:"Font style mixin variables",filename:"typography",url:"typography.html#font-style-mixin-variables"},{title:"Reset list styles",filename:"typography",url:"typography.html#reset-list-styles"},{title:"Reset list styles variables",filename:"typography",url:"typography.html#reset-list-styles-variables"},{title:"Inline-block list item styling",filename:"typography",url:"typography.html#inlineblock-list-item-styling"},{title:"Link styling mixin",filename:"typography",url:"typography.html#link-styling-mixin"},{title:"Link styling mixin variables",filename:"typography",url:"typography.html#link-styling-mixin-variables"},{title:"Heading styling mixin",filename:"typography",url:"typography.html#heading-styling-mixin"},{title:"Base typography mixins",filename:"typography",url:"typography.html#base-typography-mixins"},{title:"Base typography mixin variables",filename:"typography",url:"typography.html#base-typography-mixin-variables"},{title:"Headings typography mixin",filename:"typography",url:"typography.html#headings-typography-mixin"},{title:"Headings typography mixin variables",filename:"typography",url:"typography.html#headings-typography-mixin-variables"},{title:"Typography links mixin",filename:"typography",url:"typography.html#typography-links-mixin"},{title:"Typography lists mixin",filename:"typography",url:"typography.html#typography-lists-mixin"},{title:"Typography lists mixin variables",filename:"typography",url:"typography.html#typography-lists-mixin-variables"},{title:"Typography code elements mixin",filename:"typography",url:"typography.html#typography-code-elements-mixin"},{title:"Typography code mixin variables",filename:"typography",url:"typography.html#typography-code-mixin-variables"},{title:"Typography blockquote",filename:"typography",url:"typography.html#typography-blockquote"},{title:"Typography blockquote mixin variables",filename:"typography",url:"typography.html#typography-blockquote-mixin-variables"},{title:"utilities",filename:"utilities",url:"utilities.html"},{title:"Utilities",filename:"utilities",url:"utilities.html#utilities"},{title:".lib-clearfix()",filename:"utilities",url:"utilities.html#libclearfix"},{title:".lib-visibility-hidden()",filename:"utilities",url:"utilities.html#libvisibilityhidden"},{title:".lib-visually-hidden()",filename:"utilities",url:"utilities.html#libvisuallyhidden"},{title:".lib-visually-hidden-reset()",filename:"utilities",url:"utilities.html#libvisuallyhiddenreset"},{title:".lib-css()",filename:"utilities",url:"utilities.html#libcss"},{title:".lib-css() variables",filename:"utilities",url:"utilities.html#libcss-variables"},{title:".lib-rotate()",filename:"utilities",url:"utilities.html#librotate"},{title:".lib-rotate() variables",filename:"utilities",url:"utilities.html#librotate-variables"},{title:".lib-input-placeholder()",filename:"utilities",url:"utilities.html#libinputplaceholder"},{title:".lib-input-placeholder() variables",filename:"utilities",url:"utilities.html#libinputplaceholder-variables"},{title:".lib-background-gradient()",filename:"utilities",url:"utilities.html#libbackgroundgradient"},{title:".lib-background-gradient() variables",filename:"utilities",url:"utilities.html#libbackgroundgradient-variables"},{title:"variables",filename:"variables",url:"variables.html"},{title:"List of Global Variables",filename:"variables",url:"variables.html#list-of-global-variables"},{title:"Table with rows hover mixin variables",filename:"variables",url:"variables.html#table-with-rows-hover-mixin-variables"},{title:"docs",filename:"docs",url:"docs.html"},{title:"Documentation",filename:"docs",url:"docs.html#documentation"}];(function(){"use strict";var b=function(a,b){return Array.prototype.indexOf.call(a,b)!==-1},c=function(a,b){return Array.prototype.filter.call(a,b)},d=function(a,b){return Array.prototype.forEach.call(a,b)},e=document.getElementsByTagName("body")[0];e.addEventListener("click",function(a){var b=a.target;b.tagName.toLowerCase()==="svg"&&(b=b.parentNode);var c=!1;b.dataset.toggle!=null&&(a.preventDefault(),b.classList.contains("is-active")||(c=!0)),d(e.querySelectorAll("[data-toggle]"),function(a){a.classList.remove("is-active"),document.getElementById(a.dataset.toggle).hidden=!0}),c&&(b.classList.add("is-active"),document.getElementById(b.dataset.toggle).hidden=!1)}),function(){var f=e.getElementsByClassName("nav")[0];if(!f)return;var g=document.createElement("ul");g.className="nav-results",g.id="nav-search",g.hidden=!0,d(a,function(a){var b,c,d;b=document.createElement("li"),b._title=a.title.toLowerCase(),b.hidden=!0,b.appendChild(c=document.createElement("a")),c.href=a.url,c.innerHTML=a.title,c.appendChild(d=document.createElement("span")),d.innerHTML=a.filename,d.className="nav-results-filename",g.appendChild(b)}),f.appendChild(g);var h=g.children,i=function(a){d(h,function(a){a.hidden=!0});var b=this.value.toLowerCase(),e=[];b!==""&&(e=c(h,function(a){return a._title.indexOf(b)!==-1})),e.length>0?(d(e,function(a){a.hidden=!1}),g.hidden=!1):g.hidden=!0},j=f.querySelector('input[type="search"]');j.addEventListener("keyup",i),j.addEventListener("focus",i),e.addEventListener("click",function(a){if(a.target.classList&&a.target.classList.contains("search"))return;g.hidden=!0}),g.addEventListener("click",function(a){j.value=""});var k=document.createElement("ul");k.id="nav-toc",k.hidden=!0,k.className="nav-results toc-list",c(e.getElementsByTagName("*"),function(a){return b(["h1","h2","h3"],a.tagName.toLowerCase())}).map(function(a){var b=document.createElement("li"),c=document.createElement("a"),d=a.tagName.toLowerCase()[1];c.classList.add("level-"+d),b.appendChild(c),c.href="#"+a.id,c.innerHTML=a.innerHTML,k.appendChild(b)}),f.appendChild(k)}()})(),function(){"use strict";if(location.hash==="#__preview__"||location.protocol==="data:")return;var a=function(a,b){return Array.prototype.forEach.call(a,b)},b=function(a,b){var e=Array.prototype.slice.call(arguments,2);return d(a,function(a){return(c(b)?b||a:a[b]).apply(a,e)})},c=function(a){return Object.prototype.toString.call(a)==="[object Function]"},d=function(a,b){return Array.prototype.map.call(a,b)},e=function(a,b){return d(a,function(a){return a[b]})},f=function(a){var b={},c=a.split(";");for(var d=0;c.length>d;d++){var e=c[d].trim().split("=");b[e[0]]=e[1]}return b},g=function(a,c){return b(e(a,"classList"),"remove",c)},h=function(a,b){a.contentDocument.defaultView.postMessage(b,"*")},i=document.getElementsByTagName("head")[0],j=document.getElementsByTagName("body")[0],k=e(i.querySelectorAll('style[type="text/preview"]'),"innerHTML").join(""),l=e(i.querySelectorAll('script[type="text/preview"]'),"innerHTML").join(""),m=location.href.split("#")[0]+"#__preview__",n=document.createElement("iframe");n.src="data:text/html,",j.appendChild(n),n.addEventListener("load",function(){var b={sameOriginDataUri:!0};try{this.contentDocument,this.contentDocument||(b.sameOriginDataUri=!1)}catch(c){b.sameOriginDataUri=!1}this.parentNode.removeChild(this),a(j.getElementsByTagName("textarea"),function(a,c){o(a,b,c),q(),p(a)})});var o=function(a,b,c){var d,e,f;d=document.createElement("div"),d.appendChild(e=document.createElement("div")),d.className="preview",e.appendChild(f=document.createElement("iframe")),e.className="resizeable",f.setAttribute("scrolling","no"),f.name="iframe"+c++,f.addEventListener("load",function(){var c,d,e,f,g,i,j;j=this.contentDocument;if(!b.sameOriginDataUri&&this.src!==m)return;this.src===m&&(c=j.createElement("html"),c.appendChild(j.createElement("head")),c.appendChild(d=j.createElement("body")),d.innerHTML=a.textContent,j.replaceChild(c,j.documentElement)),g=j.createElement("head"),g.appendChild(f=j.createElement("style")),g.appendChild(e=j.createElement("script")),e.textContent=l,f.textContent=k,i=j.getElementsByTagName("head")[0],i.parentNode.replaceChild(g,i),h(this,"getHeight")});var g;b.sameOriginDataUri?g="data:text/html;charset=utf-8,"+encodeURIComponent("<!doctype html><html><head></head></body>"+a.textContent):g=m,f.setAttribute("src",g);var i=function(){f.contentDocument.body.innerHTML=this.value,h(f,"getHeight")};a.addEventListener("keypress",i),a.addEventListener("keyup",i),a.parentNode.insertBefore(d,a)},p=function(a){var b=document.createElement("div");b.className="preview-code",b.style.position="absolute",b.style.left="-9999px",j.appendChild(b);var c=parseInt(window.getComputedStyle(a).getPropertyValue("max-height"),10),d=function(a){b.textContent=this.value+"\n";var d=b.offsetHeight+2;d>=c?this.style.overflow="auto":this.style.overflow="hidden",this.style.height=b.offsetHeight+2+"px"};a.addEventListener("keypress",d),a.addEventListener("keyup",d),d.call(a)},q=function(){var b=j.getElementsByClassName("settings")[0],c=j.getElementsByClassName("resizeable"),d=30,e=function(b){document.cookie="preview-width="+b,a(c,function(a){b==="auto"&&(b=a.parentNode.offsetWidth),a.style.width=b+"px",h(a.getElementsByTagName("iframe")[0],"getHeight")})},i=f(document.cookie)["preview-width"];if(i){e(i),g(b.getElementsByClassName("is-active"),"is-active");var k=b.querySelector('button[data-width="'+i+'"]');k&&k.classList.add("is-active")}window.addEventListener("message",function(a){if(a.data==null||!a.source)return;var b=a.data,c=document.getElementsByName(a.source.name)[0];b.height!=null&&c&&(c.parentNode.style.height=b.height+d+"px")},!1),b&&c.length>0&&(b.hidden=!1,b.addEventListener("click",function(a){var c=a.target.tagName.toLowerCase(),d;if(c==="button")d=a.target;else{if(c!=="svg")return;d=a.target.parentNode}a.preventDefault(),g(b.getElementsByClassName("is-active"),"is-active"),d.classList.add("is-active");var f=d.dataset.width;e(f)}))}}()})()</script></body></html><!-- Generated with StyleDocco (http://jacobrask.github.com/styledocco). --> From 0d7dc6cb6c4fb25839b73fcbf861a09a646379b3 Mon Sep 17 00:00:00 2001 From: Emipro <git@emiprotechnologies.com> Date: Sat, 11 Aug 2018 09:36:26 +0530 Subject: [PATCH 0967/1171] Solution for User role issue with customer group --- app/code/Magento/Customer/etc/acl.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/etc/acl.xml b/app/code/Magento/Customer/etc/acl.xml index a500608e1cdf5..e8e6219bef4fe 100644 --- a/app/code/Magento/Customer/etc/acl.xml +++ b/app/code/Magento/Customer/etc/acl.xml @@ -12,6 +12,7 @@ <resource id="Magento_Customer::customer" title="Customers" translate="title" sortOrder="40"> <resource id="Magento_Customer::manage" title="All Customers" translate="title" sortOrder="10" /> <resource id="Magento_Customer::online" title="Now Online" translate="title" sortOrder="20" /> + <resource id="Magento_Customer::group" title="Customer Groups" translate="title" sortOrder="30" /> </resource> <resource id="Magento_Backend::stores"> <resource id="Magento_Backend::stores_settings"> @@ -19,10 +20,7 @@ <resource id="Magento_Customer::config_customer" title="Customers Section" translate="title" sortOrder="50" /> </resource> </resource> - <resource id="Magento_Backend::stores_other_settings"> - <resource id="Magento_Customer::group" title="Customer Groups" translate="title" sortOrder="10" /> - </resource> - </resource> + </resource> </resource> </resources> </acl> From b07ec006661a07b395af418d4ce8f37fd66c61ed Mon Sep 17 00:00:00 2001 From: nikita <nikita.shcherbatykh@transoftgroup.com> Date: Mon, 13 Aug 2018 15:18:53 +0300 Subject: [PATCH 0968/1171] MAGETWO-93173: [2.3] Widget selection by Enabled Products causes a fatal on Storefront in case of "Flat Product" configuration --- .../Model/Rule/Condition/Product.php | 20 ++++++++++++------- .../Model/Rule/Condition/ProductTest.php | 19 ++++++++++++++++-- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php index f22879df0ae0c..3d583375977b6 100644 --- a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php +++ b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php @@ -9,6 +9,7 @@ */ namespace Magento\CatalogWidget\Model\Rule\Condition; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ProductCategoryList; /** @@ -77,17 +78,22 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function loadAttributeOptions() { $productAttributes = $this->_productResource->loadAllAttributes()->getAttributesByCode(); + $productAttributes = array_filter( + $productAttributes, + function ($attribute) { + return $attribute->getFrontendLabel() && + $attribute->getFrontendInput() !== 'text' && + $attribute->getAttributeCode() !== ProductInterface::STATUS; + } + ); $attributes = []; foreach ($productAttributes as $attribute) { - if (!$attribute->getFrontendLabel() || $attribute->getFrontendInput() == 'text') { - continue; - } $attributes[$attribute->getAttributeCode()] = $attribute->getFrontendLabel(); } @@ -100,7 +106,7 @@ public function loadAttributeOptions() } /** - * {@inheritdoc} + * @inheritdoc */ protected function _addSpecialAttributes(array &$attributes) { @@ -224,7 +230,7 @@ protected function addNotGlobalAttribute( } /** - * {@inheritdoc} + * @inheritdoc */ public function getMappedSqlField() { @@ -243,7 +249,7 @@ public function getMappedSqlField() } /** - * {@inheritdoc} + * @inheritdoc */ public function collectValidatedAttributes($productCollection) { diff --git a/dev/tests/integration/testsuite/Magento/CatalogWidget/Model/Rule/Condition/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogWidget/Model/Rule/Condition/ProductTest.php index 61ce3315fd9b5..f7967eee8b247 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogWidget/Model/Rule/Condition/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogWidget/Model/Rule/Condition/ProductTest.php @@ -6,6 +6,8 @@ namespace Magento\CatalogWidget\Model\Rule\Condition; +use Magento\Catalog\Api\Data\ProductInterface; + class ProductTest extends \PHPUnit\Framework\TestCase { /** @@ -18,6 +20,9 @@ class ProductTest extends \PHPUnit\Framework\TestCase */ protected $objectManager; + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -28,19 +33,26 @@ protected function setUp() $this->conditionProduct->setRule($rule); } + /** + * @return void + */ public function testLoadAttributeOptions() { $this->conditionProduct->loadAttributeOptions(); $options = $this->conditionProduct->getAttributeOption(); - $this->assertArrayHasKey('sku', $options); - $this->assertArrayHasKey('attribute_set_id', $options); + $this->assertArrayHasKey(ProductInterface::SKU, $options); + $this->assertArrayHasKey(ProductInterface::ATTRIBUTE_SET_ID, $options); $this->assertArrayHasKey('category_ids', $options); + $this->assertArrayNotHasKey(ProductInterface::STATUS, $options); foreach ($options as $code => $label) { $this->assertNotEmpty($label); $this->assertNotEmpty($code); } } + /** + * @return void + */ public function testAddGlobalAttributeToCollection() { $collection = $this->objectManager->create(\Magento\Catalog\Model\ResourceModel\Product\Collection::class); @@ -53,6 +65,9 @@ public function testAddGlobalAttributeToCollection() $this->assertEquals('at_special_price.value', $this->conditionProduct->getMappedSqlField()); } + /** + * @return void + */ public function testAddNonGlobalAttributeToCollectionNoProducts() { $collection = $this->objectManager->create(\Magento\Catalog\Model\ResourceModel\Product\Collection::class); From a28847f760bf1a583d2ff654ff199b1a315358cf Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Mon, 13 Aug 2018 15:44:55 +0300 Subject: [PATCH 0969/1171] MAGETWO-62891: New address is not marked as "Default Billing" - Fix added --- app/code/Magento/Quote/Model/QuoteManagement.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index 13021ee412754..a718d9df9d923 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -564,6 +564,10 @@ protected function _prepareCustomerQuote($quote) //Make provided address as default shipping address $shippingAddress->setIsDefaultShipping(true); $hasDefaultShipping = true; + if (!$hasDefaultBilling && !$billing->getSaveInAddressBook()) { + $shippingAddress->setIsDefaultBilling(true); + $hasDefaultBilling = true; + } } //save here new customer address $shippingAddress->setCustomerId($quote->getCustomerId()); From d2ad2b99fc273cbc896e321b925ca9537ec8e9f6 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 13 Aug 2018 16:19:24 +0300 Subject: [PATCH 0970/1171] GraphQL-127: Fixed return value for getGraphQlClient in API-functional tests -- Fix static tests --- .../Magento/TestFramework/TestCase/GraphQlAbstract.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php index 936cbfdb5106f..84ac6360643c4 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php @@ -92,9 +92,10 @@ private function getAppCache() private function getGraphQlClient() { if ($this->graphQlClient === null) { - $this->graphQlClient = Bootstrap::getObjectManager()->get(\Magento\TestFramework\TestCase\GraphQl\Client::class); + $this->graphQlClient = Bootstrap::getObjectManager()->get( + \Magento\TestFramework\TestCase\GraphQl\Client::class + ); } - return $this->graphQlClient; } } From 1511c7585322f6749f5319b2673de1b20b2dc434 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 13 Aug 2018 16:22:28 +0300 Subject: [PATCH 0971/1171] MAGETWO-93167: [2.3] Order Summary does not show free shipping after applying coupon during checkout --- .../Model/Quote/Address/Total/Shipping.php | 228 ++++++++++-------- .../Quote/Address/Total/ShippingTest.php | 39 +-- .../Usps/Api/GuestCouponManagementTest.php | 163 +++++++++++++ .../cart_rule_coupon_free_shipping.php | 36 +++ ...art_rule_coupon_free_shipping_rollback.php | 8 + .../Magento/Usps/Fixtures/rates_response.xml | 98 ++++++++ 6 files changed, 458 insertions(+), 114 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Usps/Api/GuestCouponManagementTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Usps/Fixtures/cart_rule_coupon_free_shipping.php create mode 100644 dev/tests/integration/testsuite/Magento/Usps/Fixtures/cart_rule_coupon_free_shipping_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Usps/Fixtures/rates_response.xml diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total/Shipping.php b/app/code/Magento/Quote/Model/Quote/Address/Total/Shipping.php index 6e6aa27ec5f30..84f1fc1c35adf 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total/Shipping.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total/Shipping.php @@ -6,6 +6,7 @@ namespace Magento\Quote\Model\Quote\Address\Total; use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Quote\Api\Data\AddressInterface; use Magento\Quote\Model\Quote\Address\FreeShippingInterface; class Shipping extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal @@ -40,7 +41,6 @@ public function __construct( * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment * @param \Magento\Quote\Model\Quote\Address\Total $total * @return $this - * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -54,13 +54,6 @@ public function collect( $address = $shippingAssignment->getShipping()->getAddress(); $method = $shippingAssignment->getShipping()->getMethod(); - $address->setWeight(0); - $address->setFreeMethodWeight(0); - - $addressWeight = $address->getWeight(); - $freeMethodWeight = $address->getFreeMethodWeight(); - $addressFreeShipping = $address->getFreeShipping(); - $total->setTotalAmount($this->getCode(), 0); $total->setBaseTotalAmount($this->getCode(), 0); @@ -68,97 +61,20 @@ public function collect( return $this; } - $addressQty = 0; - foreach ($shippingAssignment->getItems() as $item) { - /** - * Skip if this item is virtual - */ - if ($item->getProduct()->isVirtual()) { - continue; - } - - /** - * Children weight we calculate for parent - */ - if ($item->getParentItem()) { - continue; - } - - if ($item->getHasChildren() && $item->isShipSeparately()) { - foreach ($item->getChildren() as $child) { - if ($child->getProduct()->isVirtual()) { - continue; - } - $addressQty += $child->getTotalQty(); - - if (!$item->getProduct()->getWeightType()) { - $itemWeight = $child->getWeight(); - $itemQty = $child->getTotalQty(); - $rowWeight = $itemWeight * $itemQty; - $addressWeight += $rowWeight; - if ($addressFreeShipping || $child->getFreeShipping() === true) { - $rowWeight = 0; - } elseif (is_numeric($child->getFreeShipping())) { - $freeQty = $child->getFreeShipping(); - if ($itemQty > $freeQty) { - $rowWeight = $itemWeight * ($itemQty - $freeQty); - } else { - $rowWeight = 0; - } - } - $freeMethodWeight += $rowWeight; - $item->setRowWeight($rowWeight); - } - } - if ($item->getProduct()->getWeightType()) { - $itemWeight = $item->getWeight(); - $rowWeight = $itemWeight * $item->getQty(); - $addressWeight += $rowWeight; - if ($addressFreeShipping || $item->getFreeShipping() === true) { - $rowWeight = 0; - } elseif (is_numeric($item->getFreeShipping())) { - $freeQty = $item->getFreeShipping(); - if ($item->getQty() > $freeQty) { - $rowWeight = $itemWeight * ($item->getQty() - $freeQty); - } else { - $rowWeight = 0; - } - } - $freeMethodWeight += $rowWeight; - $item->setRowWeight($rowWeight); - } - } else { - if (!$item->getProduct()->isVirtual()) { - $addressQty += $item->getQty(); - } - $itemWeight = $item->getWeight(); - $rowWeight = $itemWeight * $item->getQty(); - $addressWeight += $rowWeight; - if ($addressFreeShipping || $item->getFreeShipping() === true) { - $rowWeight = 0; - } elseif (is_numeric($item->getFreeShipping())) { - $freeQty = $item->getFreeShipping(); - if ($item->getQty() > $freeQty) { - $rowWeight = $itemWeight * ($item->getQty() - $freeQty); - } else { - $rowWeight = 0; - } - } - $freeMethodWeight += $rowWeight; - $item->setRowWeight($rowWeight); - } + $data = $this->getAssignmentWeightData($address, $shippingAssignment->getItems()); + $address->setItemQty($data['addressQty']); + $address->setWeight($data['addressWeight']); + $address->setFreeMethodWeight($data['freeMethodWeight']); + $addressFreeShipping = (bool)$address->getFreeShipping(); + $isFreeShipping = $this->freeShipping->isFreeShipping($quote, $shippingAssignment->getItems()); + $address->setFreeShipping($isFreeShipping); + if (!$addressFreeShipping && $isFreeShipping) { + $data = $this->getAssignmentWeightData($address, $shippingAssignment->getItems()); + $address->setItemQty($data['addressQty']); + $address->setWeight($data['addressWeight']); + $address->setFreeMethodWeight($data['freeMethodWeight']); } - if (isset($addressQty)) { - $address->setItemQty($addressQty); - } - - $address->setWeight($addressWeight); - $address->setFreeMethodWeight($freeMethodWeight); - $address->setFreeShipping( - $this->freeShipping->isFreeShipping($quote, $shippingAssignment->getItems()) - ); - $address->collectShippingRates(); if ($method) { @@ -215,4 +131,122 @@ public function getLabel() { return __('Shipping'); } + + /** + * Gets shipping assignments data like items weight, address weight, items quantity. + * + * @param AddressInterface $address + * @param array $items + * @return array + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + private function getAssignmentWeightData(AddressInterface $address, array $items): array + { + $address->setWeight(0); + $address->setFreeMethodWeight(0); + $addressWeight = $address->getWeight(); + $freeMethodWeight = $address->getFreeMethodWeight(); + $addressFreeShipping = (bool)$address->getFreeShipping(); + $addressQty = 0; + foreach ($items as $item) { + /** + * Skip if this item is virtual + */ + if ($item->getProduct()->isVirtual()) { + continue; + } + + /** + * Children weight we calculate for parent + */ + if ($item->getParentItem()) { + continue; + } + + $itemQty = (float)$item->getQty(); + $itemWeight = (float)$item->getWeight(); + + if ($item->getHasChildren() && $item->isShipSeparately()) { + foreach ($item->getChildren() as $child) { + if ($child->getProduct()->isVirtual()) { + continue; + } + $addressQty += $child->getTotalQty(); + + if (!$item->getProduct()->getWeightType()) { + $itemWeight = (float)$child->getWeight(); + $itemQty = (float)$child->getTotalQty(); + $addressWeight += ($itemWeight * $itemQty); + $rowWeight = $this->getItemRowWeight( + $addressFreeShipping, + $itemWeight, + $itemQty, + $child->getFreeShipping() + ); + $freeMethodWeight += $rowWeight; + $item->setRowWeight($rowWeight); + } + } + if ($item->getProduct()->getWeightType()) { + $addressWeight += ($itemWeight * $itemQty); + $rowWeight = $this->getItemRowWeight( + $addressFreeShipping, + $itemWeight, + $itemQty, + $item->getFreeShipping() + ); + $freeMethodWeight += $rowWeight; + $item->setRowWeight($rowWeight); + } + } else { + if (!$item->getProduct()->isVirtual()) { + $addressQty += $itemQty; + } + $addressWeight += ($itemWeight * $itemQty); + $rowWeight = $this->getItemRowWeight( + $addressFreeShipping, + $itemWeight, + $itemQty, + $item->getFreeShipping() + ); + $freeMethodWeight += $rowWeight; + $item->setRowWeight($rowWeight); + } + } + + return [ + 'addressQty' => $addressQty, + 'addressWeight' => $addressWeight, + 'freeMethodWeight' => $freeMethodWeight + ]; + } + + /** + * Calculates item row weight. + * + * @param bool $addressFreeShipping + * @param float $itemWeight + * @param float $itemQty + * @param $freeShipping + * @return float + */ + private function getItemRowWeight( + bool $addressFreeShipping, + float $itemWeight, + float $itemQty, + $freeShipping + ): float { + $rowWeight = $itemWeight * $itemQty; + if ($addressFreeShipping || $freeShipping === true) { + $rowWeight = 0; + } elseif (is_numeric($freeShipping)) { + $freeQty = $freeShipping; + if ($itemQty > $freeQty) { + $rowWeight = $itemWeight * ($itemQty - $freeQty); + } else { + $rowWeight = 0; + } + } + return (float)$rowWeight; + } } diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php index 6c572b1e86d08..0a06236111042 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php @@ -42,6 +42,9 @@ class ShippingTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\Pricing\PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $priceCurrency; + /** + * @inheritdoc + */ protected function setUp() { $this->freeShipping = $this->getMockForAbstractClass( @@ -123,7 +126,10 @@ protected function setUp() $this->store = $this->createMock(\Magento\Store\Model\Store::class); } - public function testFetch() + /** + * @return void + */ + public function testFetch(): void { $shippingAmount = 100; $shippingDescription = 100; @@ -144,7 +150,10 @@ public function testFetch() $this->assertEquals($expectedResult, $this->shippingModel->fetch($quoteMock, $totalMock)); } - public function testCollect() + /** + * @return void + */ + public function testCollect(): void { $this->shippingAssignment->expects($this->exactly(3)) ->method('getShipping') @@ -158,12 +167,10 @@ public function testCollect() $this->shippingAssignment->expects($this->atLeastOnce()) ->method('getItems') ->willReturn([$this->cartItem]); - $this->freeShipping->expects($this->once()) - ->method('isFreeShipping') + $this->freeShipping->method('isFreeShipping') ->with($this->quote, [$this->cartItem]) ->willReturn(true); - $this->address->expects($this->once()) - ->method('setFreeShipping') + $this->address->method('setFreeShipping') ->with(true); $this->total->expects($this->atLeastOnce()) ->method('setTotalAmount'); @@ -175,24 +182,19 @@ public function testCollect() $this->cartItem->expects($this->atLeastOnce()) ->method('isVirtual') ->willReturn(false); - $this->cartItem->expects($this->once()) - ->method('getParentItem') + $this->cartItem->method('getParentItem') ->willReturn(false); - $this->cartItem->expects($this->once()) - ->method('getHasChildren') + $this->cartItem->method('getHasChildren') ->willReturn(false); - $this->cartItem->expects($this->once()) - ->method('getWeight') + $this->cartItem->method('getWeight') ->willReturn(2); $this->cartItem->expects($this->atLeastOnce()) ->method('getQty') ->willReturn(2); $this->freeShippingAssertions(); - $this->cartItem->expects($this->once()) - ->method('setRowWeight') + $this->cartItem->method('setRowWeight') ->with(0); - $this->address->expects($this->once()) - ->method('setItemQty') + $this->address->method('setItemQty') ->with(2); $this->address->expects($this->atLeastOnce()) ->method('setWeight'); @@ -241,7 +243,10 @@ public function testCollect() $this->shippingModel->collect($this->quote, $this->shippingAssignment, $this->total); } - protected function freeShippingAssertions() + /** + * @return void + */ + protected function freeShippingAssertions(): void { $this->address->expects($this->at(0)) ->method('getFreeShipping') diff --git a/dev/tests/integration/testsuite/Magento/Usps/Api/GuestCouponManagementTest.php b/dev/tests/integration/testsuite/Magento/Usps/Api/GuestCouponManagementTest.php new file mode 100644 index 0000000000000..aeaa174ee54df --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Usps/Api/GuestCouponManagementTest.php @@ -0,0 +1,163 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Usps\Api; + +use Magento\Catalog\Model\Product\Type; +use Magento\Framework\DataObject; +use Magento\Framework\HTTP\ZendClient; +use Magento\Framework\HTTP\ZendClientFactory; +use Magento\Quote\Api\Data\AddressInterface; +use Magento\Quote\Api\Data\AddressInterfaceFactory; +use Magento\Quote\Api\Data\CartItemInterface; +use Magento\Quote\Api\Data\CartItemInterfaceFactory; +use Magento\Quote\Api\Data\ShippingMethodInterface; +use Magento\Quote\Api\GuestCartItemRepositoryInterface; +use Magento\Quote\Api\GuestCartManagementInterface; +use Magento\Quote\Api\GuestCouponManagementInterface; +use Magento\Quote\Api\GuestShipmentEstimationInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +class GuestCouponManagementTest extends TestCase +{ + /** + * @var GuestCouponManagementInterface + */ + private $management; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ZendClient|MockObject + */ + private $httpClient; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->management = $this->objectManager->get(GuestCouponManagementInterface::class); + $clientFactory = $this->getMockBuilder(ZendClientFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->httpClient = $this->getMockBuilder(ZendClient::class) + ->disableOriginalConstructor() + ->getMock(); + $clientFactory->method('create') + ->willReturn($this->httpClient); + + $this->objectManager->addSharedInstance($clientFactory, ZendClientFactory::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->objectManager->removeSharedInstance(ZendClientFactory::class); + } + + /** + * Checks a case when coupon is applied for a guest cart and USPS Priority Mail 1-Day configured as free method. + * + * @magentoConfigFixture default_store carriers/usps/active 1 + * @magentoConfigFixture default_store carriers/usps/free_method 1 + * @magentoDataFixture Magento/Usps/Fixtures/cart_rule_coupon_free_shipping.php + * @magentoDataFixture Magento/Quote/_files/is_salable_product.php + * @return void + */ + public function testFreeShippingWithCoupon(): void + { + $couponCode = 'IMPHBR852R61'; + $cartId = $this->createGuestCart(); + + $request = new DataObject(['body' => file_get_contents(__DIR__ . '/../Fixtures/rates_response.xml')]); + $this->httpClient->method('request') + ->willReturn($request); + + self::assertTrue($this->management->set($cartId, $couponCode)); + + $methods = $this->estimateShipping($cartId); + $methods = $this->filterFreeShippingMethods($methods); + self::assertEquals(['Fixed', 'Priority Mail 1-Day'], $methods); + } + + /** + * Creates guest shopping cart. + * + * @return string + */ + private function createGuestCart(): string + { + /** @var GuestCartManagementInterface $cartManagement */ + $cartManagement = $this->objectManager->get(GuestCartManagementInterface::class); + $cartId = $cartManagement->createEmptyCart(); + + /** @var CartItemInterfaceFactory $cartItemFactory */ + $cartItemFactory = $this->objectManager->get(CartItemInterfaceFactory::class); + + /** @var CartItemInterface $cartItem */ + $cartItem = $cartItemFactory->create(); + $cartItem->setQuoteId($cartId); + $cartItem->setQty(1); + $cartItem->setSku('simple-99'); + $cartItem->setProductType(Type::TYPE_SIMPLE); + + /** @var GuestCartItemRepositoryInterface $itemRepository */ + $itemRepository = $this->objectManager->get(GuestCartItemRepositoryInterface::class); + $itemRepository->save($cartItem); + + return $cartId; + } + + /** + * Estimates shipment for guest cart. + * + * @param string $cartId + * @return array ShippingMethodInterface[] + */ + private function estimateShipping(string $cartId): array + { + $addressFactory = $this->objectManager->get(AddressInterfaceFactory::class); + /** @var AddressInterface $address */ + $address = $addressFactory->create(); + $address->setCountryId('US'); + $address->setRegionId(12); + $address->setPostcode(90230); + + /** @var GuestShipmentEstimationInterface $estimation */ + $estimation = $this->objectManager->get(GuestShipmentEstimationInterface::class); + return $estimation->estimateByExtendedAddress($cartId, $address); + } + + /** + * Filters free shipping methods. + * + * @param array $methods + * @return array + */ + private function filterFreeShippingMethods(array $methods): array + { + $result = []; + /** @var ShippingMethodInterface $method */ + foreach ($methods as $method) { + if ($method->getAmount() == 0) { + $result[] = $method->getMethodTitle(); + } + } + return $result; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Usps/Fixtures/cart_rule_coupon_free_shipping.php b/dev/tests/integration/testsuite/Magento/Usps/Fixtures/cart_rule_coupon_free_shipping.php new file mode 100644 index 0000000000000..8d140593677ec --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Usps/Fixtures/cart_rule_coupon_free_shipping.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Registry; +use Magento\SalesRule\Api\CouponRepositoryInterface; +use Magento\SalesRule\Model\Coupon; +use Magento\SalesRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** + * @var Rule $salesRule + * @var Registry $registry + */ +require __DIR__ . '/../../SalesRule/_files/cart_rule_free_shipping.php'; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +$salesRule->setCouponType(Rule::COUPON_TYPE_SPECIFIC)->setUseAutoGeneration(0); +$salesRule->save(); + +$couponCode = 'IMPHBR852R61'; +$coupon = $objectManager->create(Coupon::class); +$coupon->setRuleId($salesRule->getId()) + ->setCode($couponCode) + ->setType(0); +$objectManager->get(CouponRepositoryInterface::class) + ->save($coupon); + +$registry->unregister('cart_rule_free_shipping'); +$registry->register('cart_rule_free_shipping', $salesRule); diff --git a/dev/tests/integration/testsuite/Magento/Usps/Fixtures/cart_rule_coupon_free_shipping_rollback.php b/dev/tests/integration/testsuite/Magento/Usps/Fixtures/cart_rule_coupon_free_shipping_rollback.php new file mode 100644 index 0000000000000..17b37e3d024fd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Usps/Fixtures/cart_rule_coupon_free_shipping_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../SalesRule/_files/cart_rule_free_shipping_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Usps/Fixtures/rates_response.xml b/dev/tests/integration/testsuite/Magento/Usps/Fixtures/rates_response.xml new file mode 100644 index 0000000000000..22cf7035e4eae --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Usps/Fixtures/rates_response.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<RateV4Response> + <Package ID="0"> + <ZipOrigination>90034</ZipOrigination> + <ZipDestination>90230</ZipDestination> + <Pounds>1</Pounds> + <Ounces>0.00000000</Ounces> + <Size>REGULAR</Size> + <Machinable>TRUE</Machinable> + <Zone>1</Zone> + <Postage CLASSID="3"> + <MailService>Priority Mail Express 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt;</MailService> + <Rate>24.70</Rate> + </Postage> + <Postage CLASSID="2"> + <MailService>Priority Mail Express 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Hold For Pickup</MailService> + <Rate>24.70</Rate> + </Postage> + <Postage CLASSID="13"> + <MailService>Priority Mail Express 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Flat Rate Envelope</MailService> + <Rate>24.70</Rate> + </Postage> + <Postage CLASSID="27"> + <MailService>Priority Mail Express 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Flat Rate Envelope Hold For Pickup</MailService> + <Rate>24.70</Rate> + </Postage> + <Postage CLASSID="30"> + <MailService>Priority Mail Express 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Legal Flat Rate Envelope</MailService> + <Rate>24.90</Rate> + </Postage> + <Postage CLASSID="31"> + <MailService>Priority Mail Express 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Legal Flat Rate Envelope Hold For Pickup</MailService> + <Rate>24.90</Rate> + </Postage> + <Postage CLASSID="62"> + <MailService>Priority Mail Express 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Padded Flat Rate Envelope</MailService> + <Rate>25.40</Rate> + </Postage> + <Postage CLASSID="63"> + <MailService>Priority Mail Express 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Padded Flat Rate Envelope Hold For Pickup</MailService> + <Rate>25.40</Rate> + </Postage> + <Postage CLASSID="1"> + <MailService>Priority Mail 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt;</MailService> + <Rate>6.70</Rate> + </Postage> + <Postage CLASSID="22"> + <MailService>Priority Mail 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Large Flat Rate Box</MailService> + <Rate>18.90</Rate> + </Postage> + <Postage CLASSID="17"> + <MailService>Priority Mail 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Medium Flat Rate Box</MailService> + <Rate>13.65</Rate> + </Postage> + <Postage CLASSID="28"> + <MailService>Priority Mail 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Small Flat Rate Box</MailService> + <Rate>7.20</Rate> + </Postage> + <Postage CLASSID="16"> + <MailService>Priority Mail 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Flat Rate Envelope</MailService> + <Rate>6.70</Rate> + </Postage> + <Postage CLASSID="44"> + <MailService>Priority Mail 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Legal Flat Rate Envelope</MailService> + <Rate>7.00</Rate> + </Postage> + <Postage CLASSID="29"> + <MailService>Priority Mail 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Padded Flat Rate Envelope</MailService> + <Rate>7.25</Rate> + </Postage> + <Postage CLASSID="38"> + <MailService>Priority Mail 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Gift Card Flat Rate Envelope</MailService> + <Rate>6.70</Rate> + </Postage> + <Postage CLASSID="42"> + <MailService>Priority Mail 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Small Flat Rate Envelope</MailService> + <Rate>6.70</Rate> + </Postage> + <Postage CLASSID="40"> + <MailService>Priority Mail 1-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Window Flat Rate Envelope</MailService> + <Rate>6.70</Rate> + </Postage> + <Postage CLASSID="6"> + <MailService>Media Mail Parcel</MailService> + <Rate>2.66</Rate> + </Postage> + <Postage CLASSID="7"> + <MailService>Library Mail Parcel</MailService> + <Rate>2.53</Rate> + </Postage> + </Package> +</RateV4Response> \ No newline at end of file From 5194e232d17ac36ace4d1862c729057ef27b3d48 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 13 Aug 2018 17:35:35 +0300 Subject: [PATCH 0972/1171] MAGETWO-93167: [2.3] Order Summary does not show free shipping after applying coupon during checkout --- .../Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php | 3 +++ .../testsuite/Magento/Usps/Api/GuestCouponManagementTest.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php index 0a06236111042..999595357c05a 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Quote\Test\Unit\Model\Quote\Address\Total; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class ShippingTest extends \PHPUnit\Framework\TestCase { /** diff --git a/dev/tests/integration/testsuite/Magento/Usps/Api/GuestCouponManagementTest.php b/dev/tests/integration/testsuite/Magento/Usps/Api/GuestCouponManagementTest.php index aeaa174ee54df..c4656a4801eaa 100644 --- a/dev/tests/integration/testsuite/Magento/Usps/Api/GuestCouponManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Usps/Api/GuestCouponManagementTest.php @@ -25,6 +25,9 @@ use PHPUnit\Framework\TestCase; use PHPUnit_Framework_MockObject_MockObject as MockObject; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class GuestCouponManagementTest extends TestCase { /** From 5dfe5e38a2b408325d4bce77568b9c9b08a00e64 Mon Sep 17 00:00:00 2001 From: IvanPletnyov <ivan.pletnyov@transoftgroup.com> Date: Mon, 13 Aug 2018 17:50:21 +0300 Subject: [PATCH 0973/1171] MSI-1542: Provide MSI support for Shipment Web API endpoint --- .../Magento/Sales/Model/Order/ShipmentDocumentFactory.php | 6 +++++- .../ExtensionAttributesProcessor.php | 5 +---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php index 73844a899e262..4a3e834a69a3d 100644 --- a/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php +++ b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php @@ -68,6 +68,8 @@ public function __construct( } /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * * @param OrderInterface $order * @param ShipmentItemCreationInterface[] $items * @param ShipmentTrackCreationInterface[] $tracks @@ -97,7 +99,9 @@ public function create( $shipmentItems ); - $this->extensionAttributesProcessor->execute($shipment, $arguments); + if (null !== $arguments) { + $this->extensionAttributesProcessor->execute($shipment, $arguments); + } foreach ($tracks as $track) { $hydrator = $this->hydratorPool->getHydrator( diff --git a/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessor.php b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessor.php index c39cb44552db1..c4c38a234dab1 100644 --- a/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessor.php +++ b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory/ExtensionAttributesProcessor.php @@ -48,11 +48,8 @@ public function __construct( */ public function execute( ShipmentInterface $shipment, - ShipmentCreationArgumentsInterface $arguments = null + ShipmentCreationArgumentsInterface $arguments ): void { - if (null === $arguments) { - return; - } $shipmentExtensionAttributes = []; if (null !== $shipment->getExtensionAttributes()) { $shipmentExtensionAttributes = $this->extensionAttributesProcessor->buildOutputDataArray( From 69d0c01ee487cee11e827d511932503200d31364 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <okolesnyk@magento.com> Date: Mon, 13 Aug 2018 10:01:54 -0500 Subject: [PATCH 0974/1171] MQE-1172: Bump MFTF version and deliver Magento branches - skip failing tests --- .../Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml index b37c9e97a78fc..46aa4ac22e03c 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml @@ -16,6 +16,9 @@ <description value="Admin should be able to add image to WYSIWYG content of Block"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-84376"/> + <skip> + <issueId value="MQE-1187" /> + </skip> </annotations> <before> <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> From f8f1a61a4ad29789b170c4ac7e247c7518fcfdb7 Mon Sep 17 00:00:00 2001 From: Vital_Pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Mon, 13 Aug 2018 18:24:00 +0300 Subject: [PATCH 0975/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Add reload and error message --- .../Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js index a06dc47794773..3fb641733eb22 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js @@ -163,7 +163,7 @@ define([ /** @inheritdoc */ complete: function (res) { - if (res.state() == 'rejected') { + if (res.state() === 'rejected') { location.reload(); } } From f952fc1a21fd2c6a74bd3902480de03760c9f423 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Mon, 13 Aug 2018 11:59:05 -0500 Subject: [PATCH 0976/1171] MC-182: Customer should be able to sort by price with catalog rules applied to configurable product --- .../AdminCreateProductAttributeSection.xml | 1 + .../AdminProductAttributeGridSection.xml | 2 +- .../StorefrontCategoryTopToolbarSection.xml | 2 + .../AdminNewCatalogPriceRuleSection.xml | 8 + .../Mftf/Test/StorefrontSortByPriceTest.xml | 177 ++++++++++++++++++ 5 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontSortByPriceTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index e7825afa049db..b83676c2e1033 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -25,6 +25,7 @@ <section name="StorefrontPropertiesSection"> <element name="StoreFrontPropertiesTab" selector="#product_attribute_tabs_front" type="button"/> <element name="EnableWYSIWYG" type="select" selector="#enabled"/> + <element name="useForPromoRuleConditions" type="select" selector="#is_used_for_promo_rules"/> </section> <section name="WYSIWYGProductAttributeSection"> <element name="ShowHideBtn" type="button" selector="#toggledefault_value_texteditor"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml index 9e0a8ddc17217..160948f8f1f2c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml @@ -14,7 +14,7 @@ <element name="GridFilterFrontEndLabel" type="input" selector="#attributeGrid_filter_frontend_label"/> <element name="Search" type="button" selector="button[data-action=grid-filter-apply]" timeout="30"/> <element name="ResetFilter" type="button" selector="button[data-action='grid-filter-reset']" timeout="30"/> - <element name="FirstRow" type="button" selector="//*[@id='attributeGrid_table']/tbody/tr[1]"/> + <element name="FirstRow" type="button" selector="//*[@id='attributeGrid_table']/tbody/tr[1]" timeout="30"/> <element name="FilterByAttributeCode" type="input" selector="#attributeGrid_filter_attribute_code"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryTopToolbarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryTopToolbarSection.xml index 68cc9e0204912..e063b5fc8c1b7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryTopToolbarSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryTopToolbarSection.xml @@ -12,5 +12,7 @@ <element name="gridMode" type="button" selector=".//*[@class='toolbar toolbar-products'][1]//*[@id='mode-grid']" timeout="30"/> <element name="listMode" type="button" selector=".//*[@class='toolbar toolbar-products'][1]//*[@id='mode-list']" timeout="30"/> <element name="sortByDropdown" type="select" selector=".//*[@class='toolbar toolbar-products'][1]//*[@id='sorter']" timeout="30"/> + <element name="sortDirectionAsc" type="button" selector=".//*[@class='toolbar toolbar-products'][1]//a[contains(@class, 'sort-asc')]" timeout="30"/> + <element name="sortDirectionDesc" type="button" selector=".//*[@class='toolbar toolbar-products'][1]//a[contains(@class, 'sort-desc')]" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml index 071b96c06b544..ab2c6eb89d266 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml @@ -37,6 +37,14 @@ <element name="disregardRules" type="select" selector="[name='stop_rules_processing']"/> </section> + <section name="AdminNewCatalogPriceRuleConditions"> + <element name="newCondition" type="button" selector="span.rule-param-new-child"/> + <element name="conditionSelect" type="select" selector="select#conditions__{{var}}__new_child" parameterized="true"/> + <element name="targetEllipsis" type="button" selector="//li[{{var}}]//a[@class='label'][text() = '...']" parameterized="true"/> + <element name="targetInput" type="input" selector="input#conditions__{{var1}}--{{var2}}__value" parameterized="true"/> + <element name="applyButton" type="button" selector="#conditions__{{var1}}__children li:nth-of-type({{var2}}) a.rule-param-apply" parameterized="true"/> + </section> + <section name="AdminCatalogPriceRuleGrid"> <element name="applyRules" type="button" selector="#apply_rules" timeout="30"/> </section> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontSortByPriceTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontSortByPriceTest.xml new file mode 100644 index 0000000000000..5e1cc854c0f5f --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontSortByPriceTest.xml @@ -0,0 +1,177 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontSortByPriceTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Apply catalog price rule"/> + <title value="Customer should be able to sort by price with catalog rules applied to configurable product"/> + <description value="Customer should be able to sort by price with catalog rules applied to configurable product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-182"/> + <group value="CatalogRule"/> + </annotations> + <before> + <!-- Create category and two simple products --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct5"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">5</field> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct10"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">10</field> + </createData> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Enable SKU for use in promo rule conditions --> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + <waitForPageLoad stepKey="waitForProductAttributes"/> + <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid"/> + <fillField selector="{{AdminProductAttributeGridSection.GridFilterFrontEndLabel}}" userInput="SKU" stepKey="setAttributeLabel"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromGrid"/> + <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="goToStorefrontProperties"/> + <selectOption selector="{{StorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="Yes" stepKey="selectUseForPromoRuleCondition"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSaveAttribute"/> + + <!-- Create a configurable product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad time="30" stepKey="waitForProductGrid"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> + <click selector="{{AdminProductGridActionSection.addConfigurableProduct}}" stepKey="clickOnAddConfigurableProduct"/> + <fillField userInput="{{_defaultProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{_defaultProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="{{_defaultProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{_defaultProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{_defaultProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="clickOnNewAttribute"/> + <waitForPageLoad stepKey="waitForIFrame"/> + <switchToIFrame selector="{{AdminNewAttributePanel.newAttributeIFrame}}" stepKey="switchToNewAttributeIFrame"/> + <fillField selector="{{AdminNewAttributePanel.defaultLabel}}" userInput="{{colorProductAttribute.default_label}}" stepKey="fillDefaultLabel"/> + <click selector="{{AdminNewAttributePanel.saveAttribute}}" stepKey="clickOnNewAttributePanel"/> + <waitForPageLoad stepKey="waitForSaveAttribute"/> + <switchToIFrame stepKey="switchOutOfIFrame"/> + <waitForPageLoad stepKey="waitForFilters"/> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickOnFilters"/> + <fillField userInput="{{colorProductAttribute.default_label}}" selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue1"/> + <fillField userInput="{{colorProductAttribute1.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue2"/> + <fillField userInput="{{colorProductAttribute2.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue3"/> + <fillField userInput="{{colorProductAttribute3.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyUniquePricesByAttributeToEachSku}}" stepKey="clickOnApplyUniquePricesByAttributeToEachSku"/> + <selectOption selector="{{AdminCreateProductConfigurationsPanel.selectAttribute}}" userInput="{{colorProductAttribute.default_label}}" stepKey="selectAttributes"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute1}}" userInput="15" stepKey="fillAttributePrice1"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute2}}" userInput="20" stepKey="fillAttributePrice2"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute3}}" userInput="25" stepKey="fillAttributePrice3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="1" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> + </before> + <after> + <!-- Delete category and two simple products --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct5" stepKey="deleteSimpleProduct5"/> + <deleteData createDataKey="createSimpleProduct10" stepKey="deleteSimpleProduct10"/> + + <!-- Delete the catalog price rule --> + <amOnPage stepKey="goToPriceRulePage" url="{{CatalogRulePage.url}}"/> + <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> + <argument name="name" value="{{_defaultCatalogRule.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!-- 1. Check category page price sorting BEFORE catalog rule is created --> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory1"/> + <selectOption selector="{{StorefrontCategoryTopToolbarSection.sortByDropdown}}" userInput="price" stepKey="sortByPrice1"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createSimpleProduct5.name$$" stepKey="seeOrder1"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$5.00" stepKey="seePrice1"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$$createSimpleProduct10.name$$" stepKey="seeOrder2"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$10.00" stepKey="seePrice2"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="{{_defaultProduct.name}}" stepKey="seeOrder3"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="$15.00" stepKey="seePrice3"/> + <click selector="{{StorefrontCategoryTopToolbarSection.sortDirectionAsc}}" stepKey="clickSortByArrow1"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="{{_defaultProduct.name}}" stepKey="seeOrder4"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$15.00" stepKey="seePrice4"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$$createSimpleProduct10.name$$" stepKey="seeOrder5"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$10.00" stepKey="seePrice5"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="$$createSimpleProduct5.name$$" stepKey="seeOrder6"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="$5.00" stepKey="seePrice6"/> + + <!-- 2. Create a new catalog rule that adjusts one of the configurable products down to $1 --> + <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage"/> + <waitForPageLoad stepKey="waitForPriceRulePage"/> + <click selector="{{AdminGridMainControls.add}}" stepKey="addNewRule"/> + <waitForPageLoad stepKey="waitForIndividualRulePage"/> + <fillField selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{_defaultCatalogRule.name}}" stepKey="fillName"/> + <fillField selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{_defaultCatalogRule.description}}" stepKey="fillDescription"/> + <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" userInput="{{_defaultCatalogRule.website_ids[0]}}" stepKey="selectSite"/> + <click selector="{{AdminNewCatalogPriceRule.fromDateButton}}" stepKey="clickFromCalender"/> + <click selector="{{AdminNewCatalogPriceRule.todayDate}}" stepKey="clickFromToday"/> + <click selector="{{AdminNewCatalogPriceRule.toDateButton}}" stepKey="clickToCalender"/> + <click selector="{{AdminNewCatalogPriceRule.todayDate}}" stepKey="clickToToday"/> + <click selector="{{AdminNewCatalogPriceRule.conditionsTab}}" stepKey="openConditions"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" stepKey="clickNewRule"/> + <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionSelect('1')}}" userInput="SKU" stepKey="selectSKU"/> + <waitForPageLoad stepKey="waitForEllipsis"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.targetEllipsis('1')}}" stepKey="clickEllipsis"/> + <waitForPageLoad stepKey="waitForInput"/> + <fillField selector="{{AdminNewCatalogPriceRuleConditions.targetInput('1', '1')}}" userInput="{{_defaultProduct.sku}}-{{colorProductAttribute3.name}}" stepKey="fillSku"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.applyButton('1', '1')}}" stepKey="clickApply"/> + <click selector="{{AdminNewCatalogPriceRule.actionsTab}}" stepKey="openActionDropdown"/> + <selectOption selector="{{AdminNewCatalogPriceRuleActions.apply}}" userInput="{{_defaultCatalogRule.simple_action}}" stepKey="discountType"/> + <fillField selector="{{AdminNewCatalogPriceRuleActions.discountAmount}}" userInput="96" stepKey="fillDiscountValue"/> + <selectOption selector="{{AdminNewCatalogPriceRuleActions.disregardRules}}" userInput="Yes" stepKey="discardSubsequentRules"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <waitForPageLoad stepKey="waitForApplied"/> + <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + + <!-- 3. Save the catalog rule, reindex, and flush cache--> + <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <!-- 4. Check category page price sorting AFTER catalog rule is created --> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory2"/> + <selectOption selector="{{StorefrontCategoryTopToolbarSection.sortByDropdown}}" userInput="price" stepKey="sortByPrice2"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="{{_defaultProduct.name}}" stepKey="seeOrder7"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$1.00" stepKey="seePrice7"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$$createSimpleProduct5.name$$" stepKey="seeOrder8"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$5.00" stepKey="seePrice8"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="$$createSimpleProduct10.name$$" stepKey="seeOrder9"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="$10.00" stepKey="seePrice9"/> + <click selector="{{StorefrontCategoryTopToolbarSection.sortDirectionAsc}}" stepKey="clickSortByArrow2"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createSimpleProduct10.name$$" stepKey="seeOrder10"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$10.00" stepKey="seePrice10"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$$createSimpleProduct5.name$$" stepKey="seeOrder11"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$5.00" stepKey="seePrice11"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="{{_defaultProduct.name}}" stepKey="seeOrder12"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="$1.00" stepKey="seePrice12"/> + </test> +</tests> From 1af0957ccadc61ec731e5366088b1c6795acc603 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Mon, 13 Aug 2018 13:19:39 -0500 Subject: [PATCH 0977/1171] MC-160: Admin should be able to delete catalog price rule - Remove flush cache via ui --- .../Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml index 9d31b2e0e403c..d3546d06492be 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml @@ -75,7 +75,6 @@ <click selector="{{AdminCatalogPriceRuleGrid.applyRules}}" stepKey="clickApplyRules"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache"/> <!-- Verify that category page shows the original prices --> <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryPage2"/> From fe505390c03b167788565756f15dab2648d86bb3 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Mon, 13 Aug 2018 15:40:40 -0500 Subject: [PATCH 0978/1171] MC-74: Admin should be able to apply the catalog rule by category --- .../AdminApplyCatalogRuleByCategoryTest.xml | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml new file mode 100644 index 0000000000000..741da96179b8c --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminApplyCatalogRuleByCategoryTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Apply catalog price rule"/> + <title value="Admin should be able to apply the catalog rule by category"/> + <description value="Admin should be able to apply the catalog rule by category"/> + <severity value="MAJOR"/> + <testCaseId value="MC-74"/> + <group value="CatalogRule"/> + </annotations> + <before> + <createData entity="ApiCategory" stepKey="createCategoryOne"/> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProductOne"> + <requiredEntity createDataKey="createCategoryOne"/> + </createData> + <createData entity="ApiCategory" stepKey="createCategoryTwo"/> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProductTwo"> + <requiredEntity createDataKey="createCategoryTwo"/> + </createData> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createCategoryOne" stepKey="deleteCategoryOne"/> + <deleteData createDataKey="createSimpleProductOne" stepKey="deleteSimpleProductOne"/> + <deleteData createDataKey="createCategoryTwo" stepKey="deleteCategoryTwo"/> + <deleteData createDataKey="createSimpleProductTwo" stepKey="deleteSimpleProductTwo"/> + + <!-- Delete the catalog price rule --> + <amOnPage stepKey="goToPriceRulePage" url="{{CatalogRulePage.url}}"/> + <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> + <argument name="name" value="{{_defaultCatalogRule.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!-- 1. Begin creating a new catalog price rule --> + <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage"/> + <waitForPageLoad stepKey="waitForPriceRulePage"/> + <click selector="{{AdminGridMainControls.add}}" stepKey="addNewRule"/> + <waitForPageLoad stepKey="waitForIndividualRulePage"/> + <fillField selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{_defaultCatalogRule.name}}" stepKey="fillName"/> + <fillField selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{_defaultCatalogRule.description}}" stepKey="fillDescription"/> + <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" userInput="{{_defaultCatalogRule.website_ids[0]}}" stepKey="selectSite"/> + <click selector="{{AdminNewCatalogPriceRule.fromDateButton}}" stepKey="clickFromCalender"/> + <click selector="{{AdminNewCatalogPriceRule.todayDate}}" stepKey="clickFromToday"/> + <click selector="{{AdminNewCatalogPriceRule.toDateButton}}" stepKey="clickToCalender"/> + <click selector="{{AdminNewCatalogPriceRule.todayDate}}" stepKey="clickToToday"/> + <click selector="{{AdminNewCatalogPriceRule.conditionsTab}}" stepKey="openConditions"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" stepKey="clickNewRule"/> + <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionSelect('1')}}" userInput="Category" stepKey="selectCategory"/> + <waitForPageLoad stepKey="waitForEllipsis"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.targetEllipsis('1')}}" stepKey="clickEllipsis"/> + <waitForPageLoad stepKey="waitForInput"/> + + <!-- 2. Fill condition of category = createCategoryOne --> + <fillField selector="{{AdminNewCatalogPriceRuleConditions.targetInput('1', '1')}}" userInput="$$createCategoryOne.id$$" stepKey="fillCategory"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.applyButton('1', '1')}}" stepKey="clickApply"/> + <click selector="{{AdminNewCatalogPriceRule.actionsTab}}" stepKey="openActionDropdown"/> + <selectOption selector="{{AdminNewCatalogPriceRuleActions.apply}}" userInput="{{_defaultCatalogRule.simple_action}}" stepKey="discountType"/> + <fillField selector="{{AdminNewCatalogPriceRuleActions.discountAmount}}" userInput="50" stepKey="fillDiscountValue"/> + <selectOption selector="{{AdminNewCatalogPriceRuleActions.disregardRules}}" userInput="Yes" stepKey="discardSubsequentRules"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <waitForPageLoad stepKey="waitForApplied"/> + <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + + <!-- 3. Save and apply the new catalog price rule --> + <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <!-- 4. Verify the storefront --> + <amOnPage url="$$createCategoryOne.name$$.html" stepKey="goToCategoryOne"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createSimpleProductOne.name$$" stepKey="seeProductOne"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$61.50" stepKey="seeProductOnePrice"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="Regular Price $123.00" stepKey="seeProductOneRegularPrice"/> + <amOnPage url="$$createCategoryTwo.name$$.html" stepKey="goToCategoryTwo"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createSimpleProductTwo.name$$" stepKey="seeProductTwo"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$123.00" stepKey="seeProductTwoPrice"/> + <dontSee selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$61.50" stepKey="dontSeeDiscount"/> + </test> +</tests> From 1b2da8d81e447c583ad5052cd4184cf56eeab2b1 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Mon, 13 Aug 2018 23:50:25 +0300 Subject: [PATCH 0979/1171] MAGETWO-94047: [FT] Magento\Checkout\Test\TestCase\OnePageCheckoutTest::OnePageCheckoutBraintreeTestVariation5 failed on Bamboo --- .../Constraint/AssertDeviceDataIsPresentInBraintreeRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Constraint/AssertDeviceDataIsPresentInBraintreeRequest.php b/dev/tests/functional/tests/app/Magento/Braintree/Test/Constraint/AssertDeviceDataIsPresentInBraintreeRequest.php index 8320514520b03..b238302e8f47f 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/Constraint/AssertDeviceDataIsPresentInBraintreeRequest.php +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Constraint/AssertDeviceDataIsPresentInBraintreeRequest.php @@ -17,7 +17,7 @@ class AssertDeviceDataIsPresentInBraintreeRequest extends AbstractConstraint /** * Log file name. */ - const FILE_NAME = 'debug.log'; + const FILE_NAME = 'payment.log'; /** * Device data pattern for regular expression. From 5da5e4ea537cabae5c4e176a1bc90647437926c0 Mon Sep 17 00:00:00 2001 From: Vital_Pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Tue, 14 Aug 2018 00:49:12 +0300 Subject: [PATCH 0980/1171] MAGETWO-91701: Newsletter subscription is not correctly updated when user is registered on 2 stores - Adding additional checking for guest customers --- .../Magento/Newsletter/Model/Plugin/PluginTest.php | 4 ++-- .../Newsletter/Model/ResourceModel/SubscriberTest.php | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Model/Plugin/PluginTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Model/Plugin/PluginTest.php index 39db400d2d637..ab38dcf158c56 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Model/Plugin/PluginTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Model/Plugin/PluginTest.php @@ -63,14 +63,14 @@ public function testCustomerCreated() ->setFirstname('Firstname') ->setLastname('Lastname') ->setEmail('customer_two@example.com'); - $createdCustomer = $this->customerRepository->save( + $this->customerRepository->save( $customerDataObject, $this->accountManagement->getPasswordHash('password') ); $subscriber->loadByEmail('customer_two@example.com'); $this->assertTrue($subscriber->isSubscribed()); - $this->assertEquals((int)$createdCustomer->getId(), (int)$subscriber->getCustomerId()); + $this->assertEquals(0, (int)$subscriber->getCustomerId()); } /** diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Model/ResourceModel/SubscriberTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Model/ResourceModel/SubscriberTest.php index 356cedde57772..b6f9a22ff5273 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Model/ResourceModel/SubscriberTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Model/ResourceModel/SubscriberTest.php @@ -40,15 +40,13 @@ public function testLoadByCustomerDataWithCustomerId() * @magentoDataFixture Magento/Newsletter/_files/subscribers.php * @magentoDataFixture Magento/Customer/_files/two_customers.php */ - public function testLoadByCustomerDataWithoutCustomerId() + public function testTryLoadByCustomerDataWithoutCustomerId() { /** @var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */ $customerRepository = Bootstrap::getObjectManager() ->create(\Magento\Customer\Api\CustomerRepositoryInterface::class); $customerData = $customerRepository->getById(2); $result = $this->_resourceModel->loadByCustomerData($customerData); - - $this->assertEquals(0, $result['customer_id']); - $this->assertEquals('customer_two@example.com', $result['subscriber_email']); + $this->assertEmpty($result); } } From b58eb3686cd9fc9a12299dbc300176aefe0758de Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Mon, 13 Aug 2018 17:07:23 -0500 Subject: [PATCH 0981/1171] MAGETWO-91678: Minimum Order amount required in Admin orders --- .../Magento/Quote/Model/QuoteValidator.php | 59 ++++++------------- .../AllowedCountryValidationRule.php | 55 +++++++++++++++++ .../BillingAddressValidationRule.php | 43 ++++++++++++++ .../MinimumAmountValidationRule.php | 52 ++++++++++++++++ .../PaymentMethodValidationRule.php | 40 +++++++++++++ .../QuoteValidationComposite.php | 38 ++++++++++++ .../QuoteValidationRuleInterface.php | 32 ++++++++++ .../ShippingAddressValidationRule.php | 46 +++++++++++++++ .../ShippingMethodValidationRule.php | 45 ++++++++++++++ app/code/Magento/Quote/etc/adminhtml/di.xml | 21 +++++++ app/code/Magento/Quote/etc/di.xml | 37 ++++++++++++ 11 files changed, 426 insertions(+), 42 deletions(-) create mode 100644 app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php create mode 100644 app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php create mode 100644 app/code/Magento/Quote/Model/ValidationRules/MinimumAmountValidationRule.php create mode 100644 app/code/Magento/Quote/Model/ValidationRules/PaymentMethodValidationRule.php create mode 100644 app/code/Magento/Quote/Model/ValidationRules/QuoteValidationComposite.php create mode 100644 app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php create mode 100644 app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php create mode 100644 app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php create mode 100644 app/code/Magento/Quote/etc/adminhtml/di.xml diff --git a/app/code/Magento/Quote/Model/QuoteValidator.php b/app/code/Magento/Quote/Model/QuoteValidator.php index 1f6deca4c1d74..59e7334d30ff3 100644 --- a/app/code/Magento/Quote/Model/QuoteValidator.php +++ b/app/code/Magento/Quote/Model/QuoteValidator.php @@ -11,6 +11,7 @@ use Magento\Directory\Model\AllowedCountries; use Magento\Framework\App\ObjectManager; use Magento\Quote\Model\Quote\Validator\MinimumOrderAmount\ValidationMessage as OrderAmountValidationMessage; +use Magento\Quote\Model\ValidationRules\QuoteValidationRuleInterface; /** * @api @@ -33,20 +34,29 @@ class QuoteValidator */ private $minimumAmountMessage; + /** + * @var QuoteValidationRuleInterface + */ + private $quoteValidationRule; + /** * QuoteValidator constructor. * * @param AllowedCountries|null $allowedCountryReader * @param OrderAmountValidationMessage|null $minimumAmountMessage + * @param QuoteValidationRuleInterface|null $quoteValidationRule */ public function __construct( AllowedCountries $allowedCountryReader = null, - OrderAmountValidationMessage $minimumAmountMessage = null + OrderAmountValidationMessage $minimumAmountMessage = null, + QuoteValidationRuleInterface $quoteValidationRule = null ) { $this->allowedCountryReader = $allowedCountryReader ?: ObjectManager::getInstance() ->get(AllowedCountries::class); $this->minimumAmountMessage = $minimumAmountMessage ?: ObjectManager::getInstance() ->get(OrderAmountValidationMessage::class); + $this->quoteValidationRule = $quoteValidationRule ?: ObjectManager::getInstance() + ->get(QuoteValidationRuleInterface::class); } /** @@ -74,49 +84,14 @@ public function validateQuoteAmount(QuoteEntity $quote, $amount) */ public function validateBeforeSubmit(QuoteEntity $quote) { - if (!$quote->isVirtual()) { - if ($quote->getShippingAddress()->validate() !== true) { - throw new \Magento\Framework\Exception\LocalizedException( - __( - 'Please check the shipping address information. %1', - implode(' ', $quote->getShippingAddress()->validate()) - ) - ); + foreach ($this->quoteValidationRule->validate($quote) as $messages) { + $defaultMessage = array_shift($messages); + if ($defaultMessage && !empty($messages)) { + $defaultMessage .= ' %1'; } - - // Checks if country id present in the allowed countries list. - if (!in_array( - $quote->getShippingAddress()->getCountryId(), - $this->allowedCountryReader->getAllowedCountries() - )) { - throw new \Magento\Framework\Exception\LocalizedException( - __("Some addresses can't be used due to the configurations for specific countries.") - ); + if ($defaultMessage) { + throw new LocalizedException(__($defaultMessage, implode(' ', $messages))); } - - $method = $quote->getShippingAddress()->getShippingMethod(); - $rate = $quote->getShippingAddress()->getShippingRateByCode($method); - if (!$method || !$rate) { - throw new \Magento\Framework\Exception\LocalizedException( - __('The shipping method is missing. Select the shipping method and try again.') - ); - } - } - if ($quote->getBillingAddress()->validate() !== true) { - throw new \Magento\Framework\Exception\LocalizedException( - __( - 'Please check the billing address information. %1', - implode(' ', $quote->getBillingAddress()->validate()) - ) - ); - } - if (!$quote->getPayment()->getMethod()) { - throw new \Magento\Framework\Exception\LocalizedException( - __('Enter a valid payment method and try again. ') - ); - } - if (!$quote->validateMinimumAmount($quote->getIsMultiShipping())) { - throw new LocalizedException($this->minimumAmountMessage->getMessage()); } return $this; diff --git a/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php new file mode 100644 index 0000000000000..72627936daaf4 --- /dev/null +++ b/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Model\ValidationRules; + +use Magento\Directory\Model\AllowedCountries; +use Magento\Quote\Model\Quote; + +class AllowedCountryValidationRule implements QuoteValidationRuleInterface +{ + /** + * @var string + */ + private $defaultMessage; + + /** + * @var AllowedCountries + */ + private $allowedCountryReader; + + /** + * @param AllowedCountries $allowedCountryReader + * @param string $defaultMessage + */ + public function __construct(AllowedCountries $allowedCountryReader, string $defaultMessage = '') + { + $this->defaultMessage = $defaultMessage; + $this->allowedCountryReader = $allowedCountryReader; + } + + /** + * @inheritdoc + */ + public function validate(Quote $quote): array + { + $validationErrors = []; + + if (!$quote->isVirtual()) { + $validationResult = + in_array( + $quote->getShippingAddress()->getCountryId(), + $this->allowedCountryReader->getAllowedCountries() + ); + if (!$validationResult) { + $validationErrors = [$this->defaultMessage]; + } + } + + return $validationErrors ? [get_class($this) => $validationErrors] : []; + } +} diff --git a/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php new file mode 100644 index 0000000000000..0dd6d472e761d --- /dev/null +++ b/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Model\ValidationRules; + +use Magento\Quote\Model\Quote; + +class BillingAddressValidationRule implements QuoteValidationRuleInterface +{ + /** + * @var string + */ + private $defaultMessage; + + /** + * @param string $defaultMessage + */ + public function __construct(string $defaultMessage = '') + { + $this->defaultMessage = $defaultMessage; + } + + /** + * @inheritdoc + */ + public function validate(Quote $quote): array + { + $validationErrors = []; + $validationResult = $quote->getBillingAddress()->validate(); + if ($validationResult !== true) { + $validationErrors = [$this->defaultMessage]; + } + if (is_array($validationResult)) { + $validationErrors = array_merge($validationErrors, $validationResult); + } + + return $validationErrors ? [get_class($this) => $validationErrors] : []; + } +} diff --git a/app/code/Magento/Quote/Model/ValidationRules/MinimumAmountValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/MinimumAmountValidationRule.php new file mode 100644 index 0000000000000..9230f370c7ca7 --- /dev/null +++ b/app/code/Magento/Quote/Model/ValidationRules/MinimumAmountValidationRule.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Model\ValidationRules; + +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Validator\MinimumOrderAmount\ValidationMessage; + +class MinimumAmountValidationRule implements QuoteValidationRuleInterface +{ + /** + * @var string + */ + private $defaultMessage; + + /** + * @var ValidationMessage + */ + private $amountValidationMessage; + + /** + * @param ValidationMessage $amountValidationMessage + * @param string $defaultMessage + */ + public function __construct(ValidationMessage $amountValidationMessage, string $defaultMessage = '') + { + $this->amountValidationMessage = $amountValidationMessage; + $this->defaultMessage = $defaultMessage; + } + + /** + * @inheritdoc + * @throws \Zend_Currency_Exception + */ + public function validate(Quote $quote): array + { + $validationErrors = []; + $validationResult = $quote->validateMinimumAmount($quote->getIsMultiShipping()); + if (!$validationResult) { + if (!$this->defaultMessage) { + $this->defaultMessage = $this->amountValidationMessage->getMessage(); + } + $validationErrors = [$this->defaultMessage]; + } + + return $validationErrors ? [get_class($this) => $validationErrors] : []; + } +} diff --git a/app/code/Magento/Quote/Model/ValidationRules/PaymentMethodValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/PaymentMethodValidationRule.php new file mode 100644 index 0000000000000..7bb35096a5878 --- /dev/null +++ b/app/code/Magento/Quote/Model/ValidationRules/PaymentMethodValidationRule.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Model\ValidationRules; + +use Magento\Quote\Model\Quote; + +class PaymentMethodValidationRule implements QuoteValidationRuleInterface +{ + /** + * @var string + */ + private $defaultMessage; + + /** + * @param string $defaultMessage + */ + public function __construct(string $defaultMessage = '') + { + $this->defaultMessage = $defaultMessage; + } + + /** + * @inheritdoc + */ + public function validate(Quote $quote): array + { + $validationErrors = []; + $validationResult = $quote->getPayment()->getMethod(); + if (!$validationResult) { + $validationErrors = [$this->defaultMessage]; + } + + return $validationErrors ? [get_class($this) => $validationErrors] : []; + } +} diff --git a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationComposite.php b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationComposite.php new file mode 100644 index 0000000000000..cfce07048064a --- /dev/null +++ b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationComposite.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Model\ValidationRules; + +use Magento\Quote\Model\Quote; + +class QuoteValidationComposite implements QuoteValidationRuleInterface +{ + /** + * @var QuoteValidationRuleInterface[] + */ + private $validationRules = []; + + public function __construct(array $validationRules) + { + $this->validationRules = $validationRules; + } + + /** + * @inheritdoc + */ + public function validate(Quote $quote): array + { + $aggregateResult = []; + + foreach ($this->validationRules as $validationRule) { + $ruleValidationResult = $validationRule->validate($quote); + $aggregateResult += $ruleValidationResult; + } + + return $aggregateResult; + } +} diff --git a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php new file mode 100644 index 0000000000000..31da5b731aa4b --- /dev/null +++ b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Quote\Model\ValidationRules; + +use Magento\Quote\Model\Quote; + +interface QuoteValidationRuleInterface +{ + /** + * Validate quote model. + * + * @param Quote $quote + * @return array + * [ + * 'ruleId_1' => [ + * 'Base error message', + * 'Additional error message #1', + * 'Additional error message #2', + * 'Additional error message #3', + * 'Additional error message #4', + * ], + * 'ruleId_2' => [ + * 'Base error message', + * ] + * ] + */ + public function validate(Quote $quote): array; +} \ No newline at end of file diff --git a/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php new file mode 100644 index 0000000000000..71fc2503e9a32 --- /dev/null +++ b/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Model\ValidationRules; + +use Magento\Quote\Model\Quote; + +class ShippingAddressValidationRule implements QuoteValidationRuleInterface +{ + /** + * @var string + */ + private $defaultMessage; + + /** + * @param string $defaultMessage + */ + public function __construct(string $defaultMessage = '') + { + $this->defaultMessage = $defaultMessage; + } + + /** + * @inheritdoc + */ + public function validate(Quote $quote): array + { + $validationErrors = []; + + if (!$quote->isVirtual()) { + $validationResult = $quote->getShippingAddress()->validate(); + if ($validationResult !== true) { + $validationErrors = [$this->defaultMessage]; + } + if (is_array($validationResult)) { + $validationErrors = array_merge($validationErrors, $validationResult); + } + } + + return $validationErrors ? [get_class($this) => $validationErrors] : []; + } +} diff --git a/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php new file mode 100644 index 0000000000000..1f4f613dd5a12 --- /dev/null +++ b/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Model\ValidationRules; + +use Magento\Quote\Model\Quote; + +class ShippingMethodValidationRule implements QuoteValidationRuleInterface +{ + /** + * @var string + */ + private $defaultMessage; + + /** + * @param string $defaultMessage + */ + public function __construct(string $defaultMessage = '') + { + $this->defaultMessage = $defaultMessage; + } + + /** + * @inheritdoc + */ + public function validate(Quote $quote): array + { + $validationErrors = []; + + if (!$quote->isVirtual()) { + $shippingMethod = $quote->getShippingAddress()->getShippingMethod(); + $shippingRate = $quote->getShippingAddress()->getShippingRateByCode($shippingMethod); + $validationResult = $shippingMethod && $shippingRate; + if (!$validationResult) { + $validationErrors = [$this->defaultMessage]; + } + } + + return $validationErrors ? [get_class($this) => $validationErrors] : []; + } +} diff --git a/app/code/Magento/Quote/etc/adminhtml/di.xml b/app/code/Magento/Quote/etc/adminhtml/di.xml new file mode 100644 index 0000000000000..df21430cf06ad --- /dev/null +++ b/app/code/Magento/Quote/etc/adminhtml/di.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Quote\Model\ValidationRules\QuoteValidationComposite"> + <arguments> + <argument name="validationRules" xsi:type="array"> + <item name="ShippingAddressValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\ShippingAddressValidationRule</item> + <item name="AllowedCountryValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\AllowedCountryValidationRule</item> + <item name="ShippingMethodValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\ShippingMethodValidationRule</item> + <item name="BillingAddressValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\BillingAddressValidationRule</item> + <item name="PaymentMethodValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\PaymentMethodValidationRule</item> + <item name="MinimumAmountValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\MinimumAmountValidationRule</item> + </argument> + </arguments> + </type> +</config> \ No newline at end of file diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml index aa6fafb5dd051..b7a15fe138c33 100644 --- a/app/code/Magento/Quote/etc/di.xml +++ b/app/code/Magento/Quote/etc/di.xml @@ -41,6 +41,7 @@ <preference for="Magento\Quote\Api\GuestCartTotalManagementInterface" type="Magento\Quote\Model\GuestCart\GuestCartTotalManagement" /> <preference for="Magento\Quote\Api\Data\EstimateAddressInterface" type="Magento\Quote\Model\EstimateAddress" /> <preference for="Magento\Quote\Api\Data\ProductOptionInterface" type="Magento\Quote\Model\Quote\ProductOption" /> + <preference for="Magento\Quote\Model\ValidationRules\QuoteValidationRuleInterface" type="Magento\Quote\Model\ValidationRules\QuoteValidationComposite"/> <type name="Magento\Webapi\Controller\Rest\ParamsOverrider"> <arguments> <argument name="paramOverriders" xsi:type="array"> @@ -95,4 +96,40 @@ <plugin name="clean_quote_items_after_product_delete" type="Magento\Quote\Model\Product\Plugin\RemoveQuoteItems"/> <plugin name="update_quote_items_after_product_save" type="Magento\Quote\Model\Product\Plugin\UpdateQuoteItems"/> </type> + <type name="Magento\Quote\Model\ValidationRules\QuoteValidationComposite"> + <arguments> + <argument name="validationRules" xsi:type="array"> + <item name="ShippingAddressValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\ShippingAddressValidationRule</item> + <item name="AllowedCountryValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\AllowedCountryValidationRule</item> + <item name="ShippingMethodValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\ShippingMethodValidationRule</item> + <item name="BillingAddressValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\BillingAddressValidationRule</item> + <item name="PaymentMethodValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\PaymentMethodValidationRule</item> + </argument> + </arguments> + </type> + <type name="Magento\Quote\Model\ValidationRules\ShippingAddressValidationRule"> + <arguments> + <argument name="defaultMessage" xsi:type="string">Please check the shipping address information.</argument> + </arguments> + </type> + <type name="Magento\Quote\Model\ValidationRules\AllowedCountryValidationRule"> + <arguments> + <argument name="defaultMessage" xsi:type="string">Some addresses can't be used due to the configurations for specific countries.</argument> + </arguments> + </type> + <type name="Magento\Quote\Model\ValidationRules\ShippingMethodValidationRule"> + <arguments> + <argument name="defaultMessage" xsi:type="string">The shipping method is missing. Select the shipping method and try again.</argument> + </arguments> + </type> + <type name="Magento\Quote\Model\ValidationRules\BillingAddressValidationRule"> + <arguments> + <argument name="defaultMessage" xsi:type="string">Please check the billing address information.</argument> + </arguments> + </type> + <type name="Magento\Quote\Model\ValidationRules\PaymentMethodValidationRule"> + <arguments> + <argument name="defaultMessage" xsi:type="string">Enter a valid payment method and try again.</argument> + </arguments> + </type> </config> From 6d2f92bec5219a2162a5410d2d752dbfc6dded55 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Tue, 14 Aug 2018 09:21:29 +0300 Subject: [PATCH 0982/1171] MAGETWO-91682: [2.3] Incorrect amount is sent to PayPal when discount is applied to an order --- .../Magento/Paypal/Model/Api/AbstractApi.php | 3 + app/code/Magento/Paypal/Model/Cart.php | 4 +- .../Magento/Paypal/Model/Express/Checkout.php | 5 +- .../Paypal/Test/Unit/Model/CartTest.php | 12 +- .../Tax/Model/Sales/Total/Quote/Shipping.php | 15 +- .../Tax/Model/Sales/Total/Quote/SetupUtil.php | 1 + ...including_tax_apply_tax_after_discount.php | 132 ++++++++++++++++++ .../tax_calculation_data_aggregated.php | 1 + 8 files changed, 163 insertions(+), 10 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_apply_tax_after_discount.php diff --git a/app/code/Magento/Paypal/Model/Api/AbstractApi.php b/app/code/Magento/Paypal/Model/Api/AbstractApi.php index 0d1cd44639e93..6f578e44eae4f 100644 --- a/app/code/Magento/Paypal/Model/Api/AbstractApi.php +++ b/app/code/Magento/Paypal/Model/Api/AbstractApi.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Paypal\Model\Api; use Magento\Payment\Helper\Formatter; @@ -427,6 +429,7 @@ protected function _exportLineItems(array &$request, $i = 0) if (isset($this->_lineItemTotalExportMap[$key])) { // !empty($total) $privateKey = $this->_lineItemTotalExportMap[$key]; + $total = round($total, 2); $request[$privateKey] = $this->formatPrice($total); } } diff --git a/app/code/Magento/Paypal/Model/Cart.php b/app/code/Magento/Paypal/Model/Cart.php index c8e4a6acf4649..cef17fab8d916 100644 --- a/app/code/Magento/Paypal/Model/Cart.php +++ b/app/code/Magento/Paypal/Model/Cart.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Paypal\Model; /** @@ -177,7 +179,7 @@ protected function _applyDiscountTaxCompensationWorkaround( ) { $dataContainer = $salesEntity->getTaxContainer(); $this->addTax((double)$dataContainer->getBaseDiscountTaxCompensationAmount()); - $this->addTax((double)$dataContainer->getBaseShippingDiscountTaxCompensationAmnt()); + $this->addTax((double)$dataContainer->getBaseShippingDiscountTaxCompensationAmount()); } /** diff --git a/app/code/Magento/Paypal/Model/Express/Checkout.php b/app/code/Magento/Paypal/Model/Express/Checkout.php index 9c9b4dc3e87a7..0388d58910d0a 100644 --- a/app/code/Magento/Paypal/Model/Express/Checkout.php +++ b/app/code/Magento/Paypal/Model/Express/Checkout.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Paypal\Model\Express; use Magento\Customer\Api\Data\CustomerInterface as CustomerDataObject; @@ -498,7 +500,8 @@ public function start($returnUrl, $cancelUrl, $button = null) $solutionType = $this->_config->getMerchantCountry() == 'DE' ? \Magento\Paypal\Model\Config::EC_SOLUTION_TYPE_MARK : $this->_config->getValue('solutionType'); - $this->_getApi()->setAmount($this->_quote->getBaseGrandTotal()) + $totalAmount = round($this->_quote->getBaseGrandTotal(), 2); + $this->_getApi()->setAmount($totalAmount) ->setCurrencyCode($this->_quote->getBaseCurrencyCode()) ->setInvNum($this->_quote->getReservedOrderId()) ->setReturnUrl($returnUrl) diff --git a/app/code/Magento/Paypal/Test/Unit/Model/CartTest.php b/app/code/Magento/Paypal/Test/Unit/Model/CartTest.php index 0f787f0f513a1..28837727533d2 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/CartTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/CartTest.php @@ -70,7 +70,7 @@ protected function setUp() public function testInvalidGetAllItems($items) { $taxContainer = new \Magento\Framework\DataObject( - ['base_discount_tax_compensation_amount' => 0.2, 'base_shipping_discount_tax_compensation_amnt' => 0.1] + ['base_discount_tax_compensation_amount' => 0.2, 'base_shipping_discount_tax_compensation_amount' => 0.1] ); $this->_salesModel->expects($this->once())->method('getTaxContainer')->will($this->returnValue($taxContainer)); $this->_salesModel->expects($this->once())->method('getAllItems')->will($this->returnValue($items)); @@ -146,7 +146,7 @@ public function testInvalidTotalsGetAllItems($values, $transferDiscount) $this->assertEquals( $values['base_tax_amount'] + $values['base_discount_tax_compensation_amount'] + - $values['base_shipping_discount_tax_compensation_amnt'], + $values['base_shipping_discount_tax_compensation_amount'], $this->_model->getTax() ); $this->assertEquals($values['base_shipping_amount'], $this->_model->getShipping()); @@ -162,7 +162,7 @@ public function invalidTotalsGetAllItemsDataProvider() [ [ 'base_discount_tax_compensation_amount' => 0, - 'base_shipping_discount_tax_compensation_amnt' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, 'base_subtotal' => 0, 'base_tax_amount' => 0, 'base_shipping_amount' => 0, @@ -174,7 +174,7 @@ public function invalidTotalsGetAllItemsDataProvider() [ [ 'base_discount_tax_compensation_amount' => 1, - 'base_shipping_discount_tax_compensation_amnt' => 2, + 'base_shipping_discount_tax_compensation_amount' => 2, 'base_subtotal' => 3, 'base_tax_amount' => 4, 'base_shipping_amount' => 5, @@ -255,8 +255,8 @@ protected function _prepareInvalidModelData($values, $transferDiscount) [ 'base_discount_tax_compensation_amount' => $values['base_discount_tax_compensation_amount'], - 'base_shipping_discount_tax_compensation_amnt' => - $values['base_shipping_discount_tax_compensation_amnt'], + 'base_shipping_discount_tax_compensation_amount' => + $values['base_shipping_discount_tax_compensation_amount'], ] ); $expectedSubtotal = $values['base_subtotal']; diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php index 16a55dbfac3e2..ddfb6f9fd5073 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Tax\Model\Sales\Total\Quote; use Magento\Quote\Model\Quote\Address; @@ -39,16 +41,23 @@ public function collect( $quoteDetails = $this->prepareQuoteDetails($shippingAssignment, [$shippingDataObject]); $taxDetails = $this->taxCalculationService ->calculateTax($quoteDetails, $storeId); + $taxDetailsItems = $taxDetails->getItems()[self::ITEM_CODE_SHIPPING]; $baseQuoteDetails = $this->prepareQuoteDetails($shippingAssignment, [$baseShippingDataObject]); $baseTaxDetails = $this->taxCalculationService ->calculateTax($baseQuoteDetails, $storeId); + $baseTaxDetailsItems = $baseTaxDetails->getItems()[self::ITEM_CODE_SHIPPING]; + + $quote->getShippingAddress() + ->setShippingAmount($taxDetailsItems->getRowTotal()); + $quote->getShippingAddress() + ->setBaseShippingAmount($baseTaxDetailsItems->getRowTotal()); $this->processShippingTaxInfo( $shippingAssignment, $total, - $taxDetails->getItems()[self::ITEM_CODE_SHIPPING], - $baseTaxDetails->getItems()[self::ITEM_CODE_SHIPPING] + $taxDetailsItems, + $baseTaxDetailsItems ); return $this; @@ -58,6 +67,8 @@ public function collect( * @param \Magento\Quote\Model\Quote $quote * @param Address\Total $total * @return array|null + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) { diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php index a9a5c922c3d25..bd505fd4db035 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php @@ -141,6 +141,7 @@ class SetupUtil 'discount_amount' => 40, 'discount_step' => 0, 'stop_rules_processing' => 1, + 'apply_to_shipping' => 0, 'website_ids' => [1], ]; diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_apply_tax_after_discount.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_apply_tax_after_discount.php new file mode 100644 index 0000000000000..41b1aec5a67f3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_apply_tax_after_discount.php @@ -0,0 +1,132 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Tax\Model\Calculation; +use Magento\Tax\Model\Config; +use Magento\Tax\Model\Sales\Total\Quote\SetupUtil; + +$taxCalculationData['including_tax_apply_tax_after_discount'] = [ + 'config_data' => [ + SetupUtil::CONFIG_OVERRIDES => [ + Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 1, + Config::CONFIG_XML_PATH_PRICE_INCLUDES_TAX => 1, + Config::CONFIG_XML_PATH_SHIPPING_INCLUDES_TAX => 1, + Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, + Config::XML_PATH_ALGORITHM => Calculation::CALC_ROW_BASE, + ], + SetupUtil::TAX_RATE_OVERRIDES => [ + SetupUtil::TAX_RATE_TX => 10, + SetupUtil::TAX_STORE_RATE => 10, + SetupUtil::TAX_RATE_SHIPPING => 10 + ], + SetupUtil::TAX_RULE_OVERRIDES => [ + [ + //tax rule for product + 'code' => 'Product Tax Rule', + 'product_tax_class_ids' => [SetupUtil::PRODUCT_TAX_CLASS_1], + ], + [ + //tax rule for shipping + 'code' => 'Shipping Tax Rule', + 'product_tax_class_ids' => [SetupUtil::SHIPPING_TAX_CLASS], + 'tax_rate_ids' => [SetupUtil::TAX_RATE_SHIPPING], + ], + ], + ], + 'quote_data' => [ + 'billing_address' => [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'shipping_address' => [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'items' => [ + [ + 'sku' => 'simple1', + 'price' => 29, + 'qty' => 1, + ], + ], + 'shipping_method' => 'flatrate_flatrate', + 'shopping_cart_rules' => [ + [ + 'discount_amount' => 50, + 'apply_to_shipping' => 1, + ], + ], + ], + 'expected_results' => [ + 'address_data' => [ + 'subtotal' => 26.36, + 'base_subtotal' => 26.36, + 'subtotal_incl_tax' => 29, + 'base_subtotal_incl_tax' => 29, + 'tax_amount' => 1.69, + 'base_tax_amount' => 1.69, + 'shipping_amount' => 4.55, + 'base_shipping_amount' => 4.55, + 'shipping_incl_tax' => 5, + 'base_shipping_incl_tax' => 5, + 'shipping_tax_amount' => 0.25, + 'base_shipping_tax_amount' => 0.25, + 'discount_amount' => -15.455, + 'base_discount_amount' => -15.455, + 'discount_tax_compensation_amount' => 1.2, + 'base_discount_tax_compensation_amount' => 1.2, + 'shipping_discount_tax_compensation_amount' => 0.2, + 'base_shipping_discount_tax_compensation_amount' => 0.2, + 'grand_total' => 18.545, + 'base_grand_total' => 18.545, + 'applied_taxes' => [ + SetupUtil::TAX_RATE_TX => [ + 'percent' => 10, + 'amount' => 1.44, + 'base_amount' => 1.44, + 'rates' => [ + [ + 'code' => SetupUtil::TAX_RATE_TX, + 'title' => SetupUtil::TAX_RATE_TX, + 'percent' => 10, + ], + ], + ], + SetupUtil::TAX_RATE_SHIPPING => [ + 'percent' => 10, + 'amount' => 0.25, + 'base_amount' => 0.25, + 'rates' => [ + [ + 'code' => SetupUtil::TAX_RATE_SHIPPING, + 'title' => SetupUtil::TAX_RATE_SHIPPING, + 'percent' => 10, + ], + ], + ], + ], + ], + 'items_data' => [ + 'simple1' => [ + 'row_total' => 26.36, + 'base_row_total' => 26.36, + 'tax_percent' => 10, + 'price' => 26.36, + 'base_price' => 26.36, + 'price_incl_tax' => 29, + 'base_price_incl_tax' => 29, + 'row_total_incl_tax' => 29, + 'base_row_total_incl_tax' => 29, + 'tax_amount' => 1.44, + 'base_tax_amount' => 1.44, + 'discount_amount' => 13.18, + 'base_discount_amount' => 13.18, + 'discount_percent' => 50, + 'discount_tax_compensation_amount' => 1.2, + 'base_discount_tax_compensation_amount' => 1.2, + ], + ], + ], +]; diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php index c47d348cedda5..f22b48a259685 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php @@ -30,3 +30,4 @@ require_once __DIR__ . '/scenarios/multi_tax_rule_total_calculate_subtotal_yes.php'; require_once __DIR__ . '/scenarios/multi_tax_rule_two_row_calculate_subtotal_yes_row.php'; require_once __DIR__ . '/scenarios/multi_tax_rule_two_row_calculate_subtotal_yes_total.php'; +require_once __DIR__ . '/scenarios/including_tax_apply_tax_after_discount.php'; From 06e9ad872482fddfb471a091f625e7389905742d Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Tue, 14 Aug 2018 10:13:38 +0300 Subject: [PATCH 0983/1171] MAGETWO-91812: [Magento Cloud] - Issue with polluted database when updating product attributes through API --- .../Model/ProductUrlPathGenerator.php | 7 +- .../ProductUrlKeyAutogeneratorObserver.php | 5 +- .../Model/ProductUrlPathGeneratorTest.php | 51 +++++++--- ...ProductUrlKeyAutogeneratorObserverTest.php | 99 +++++++++++++++++++ .../view/base/web/js/form/element/abstract.js | 4 + .../element/single-checkbox-use-config.js | 4 + .../form/element/helper/service.html | 1 - .../Ui/base/js/form/element/abstract.test.js | 51 ++++++++-- .../Ui/base/js/form/element/boolean.test.js | 43 ++++++-- .../Ui/base/js/form/element/date-time.test.js | 59 ++++++++--- .../Ui/base/js/form/element/date.test.js | 49 +++++++-- .../js/form/element/file-uploader.test.js | 53 ++++++++-- .../Ui/base/js/form/element/post-code.test.js | 48 ++++++--- .../Ui/base/js/form/element/region.test.js | 49 ++++++--- .../Ui/base/js/form/element/select.test.js | 53 +++++++--- .../single-checkbox-use-config.test.js | 78 +++++++++++++++ .../Ui/base/js/form/element/textarea.test.js | 44 +++++++-- .../Magento/Ui/base/js/form/ui-select.test.js | 56 ++++++++--- 18 files changed, 628 insertions(+), 126 deletions(-) create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductUrlKeyAutogeneratorObserverTest.php create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/single-checkbox-use-config.test.js diff --git a/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php index 2e192d895c6d5..4fdb9a3e2138d 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php @@ -5,8 +5,6 @@ */ namespace Magento\CatalogUrlRewrite\Model; -use Magento\Store\Model\Store; - class ProductUrlPathGenerator { const XML_PATH_PRODUCT_URL_SUFFIX = 'catalog/seo/product_url_suffix'; @@ -120,11 +118,12 @@ public function getCanonicalUrlPath($product, $category = null) * Generate product url key based on url_key entered by merchant or product name * * @param \Magento\Catalog\Model\Product $product - * @return string + * @return string|null */ public function getUrlKey($product) { - return $product->getUrlKey() === false ? false : $this->prepareProductUrlKey($product); + $generatedProductUrlKey = $this->prepareProductUrlKey($product); + return ($product->getUrlKey() === false || empty($generatedProductUrlKey)) ? null : $generatedProductUrlKey; } /** diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductUrlKeyAutogeneratorObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductUrlKeyAutogeneratorObserver.php index b201ae31b680a..28afff56c019f 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductUrlKeyAutogeneratorObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductUrlKeyAutogeneratorObserver.php @@ -33,6 +33,9 @@ public function execute(\Magento\Framework\Event\Observer $observer) { /** @var Product $product */ $product = $observer->getEvent()->getProduct(); - $product->setUrlKey($this->productUrlPathGenerator->getUrlKey($product)); + $urlKey = $this->productUrlPathGenerator->getUrlKey($product); + if (null !== $urlKey) { + $product->setUrlKey($urlKey); + } } } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php index b32b0216b9bdf..7435096642de2 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogUrlRewrite\Test\Unit\Model; use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; @@ -32,7 +34,10 @@ class ProductUrlPathGeneratorTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Catalog\Model\Category|\PHPUnit_Framework_MockObject_MockObject */ protected $category; - protected function setUp() + /** + * @inheritdoc + */ + protected function setUp(): void { $this->category = $this->createMock(\Magento\Catalog\Model\Category::class); $productMethods = [ @@ -69,7 +74,7 @@ protected function setUp() /** * @return array */ - public function getUrlPathDataProvider() + public function getUrlPathDataProvider(): array { return [ 'path based on url key' => ['url-key', null, 'url-key'], @@ -84,8 +89,9 @@ public function getUrlPathDataProvider() * @param string|null|bool $urlKey * @param string|null|bool $productName * @param string $result + * @return void */ - public function testGetUrlPath($urlKey, $productName, $result) + public function testGetUrlPath($urlKey, $productName, $result): void { $this->product->expects($this->once())->method('getData')->with('url_path') ->will($this->returnValue(null)); @@ -99,22 +105,23 @@ public function testGetUrlPath($urlKey, $productName, $result) /** * @param string|bool $productUrlKey * @param string|bool $expectedUrlKey + * @return void * @dataProvider getUrlKeyDataProvider */ - public function testGetUrlKey($productUrlKey, $expectedUrlKey) + public function testGetUrlKey($productUrlKey, $expectedUrlKey): void { $this->product->expects($this->any())->method('getUrlKey')->will($this->returnValue($productUrlKey)); $this->product->expects($this->any())->method('formatUrlKey')->will($this->returnValue($productUrlKey)); - $this->assertEquals($expectedUrlKey, $this->productUrlPathGenerator->getUrlKey($this->product)); + $this->assertSame($expectedUrlKey, $this->productUrlPathGenerator->getUrlKey($this->product)); } /** * @return array */ - public function getUrlKeyDataProvider() + public function getUrlKeyDataProvider(): array { return [ - 'URL Key use default' => [false, false], + 'URL Key use default' => [false, null], 'URL Key empty' => ['product-url', 'product-url'], ]; } @@ -123,9 +130,10 @@ public function getUrlKeyDataProvider() * @param string|null|bool $storedUrlKey * @param string|null|bool $productName * @param string $expectedUrlKey + * @return void * @dataProvider getUrlPathDefaultUrlKeyDataProvider */ - public function testGetUrlPathDefaultUrlKey($storedUrlKey, $productName, $expectedUrlKey) + public function testGetUrlPathDefaultUrlKey($storedUrlKey, $productName, $expectedUrlKey): void { $this->product->expects($this->once())->method('getData')->with('url_path') ->will($this->returnValue(null)); @@ -138,7 +146,7 @@ public function testGetUrlPathDefaultUrlKey($storedUrlKey, $productName, $expect /** * @return array */ - public function getUrlPathDefaultUrlKeyDataProvider() + public function getUrlPathDefaultUrlKeyDataProvider(): array { return [ ['default-store-view-url-key', null, 'default-store-view-url-key'], @@ -146,7 +154,10 @@ public function getUrlPathDefaultUrlKeyDataProvider() ]; } - public function testGetUrlPathWithCategory() + /** + * @return void + */ + public function testGetUrlPathWithCategory(): void { $this->product->expects($this->once())->method('getData')->with('url_path') ->will($this->returnValue('product-path')); @@ -159,7 +170,10 @@ public function testGetUrlPathWithCategory() ); } - public function testGetUrlPathWithSuffix() + /** + * @return void + */ + public function testGetUrlPathWithSuffix(): void { $storeId = 1; $this->product->expects($this->once())->method('getData')->with('url_path') @@ -177,7 +191,10 @@ public function testGetUrlPathWithSuffix() ); } - public function testGetUrlPathWithSuffixAndCategoryAndStore() + /** + * @return void + */ + public function testGetUrlPathWithSuffixAndCategoryAndStore(): void { $storeId = 1; $this->product->expects($this->once())->method('getData')->with('url_path') @@ -195,7 +212,10 @@ public function testGetUrlPathWithSuffixAndCategoryAndStore() ); } - public function testGetCanonicalUrlPath() + /** + * @return void + */ + public function testGetCanonicalUrlPath(): void { $this->product->expects($this->once())->method('getId')->will($this->returnValue(1)); @@ -205,7 +225,10 @@ public function testGetCanonicalUrlPath() ); } - public function testGetCanonicalUrlPathWithCategory() + /** + * @return void + */ + public function testGetCanonicalUrlPathWithCategory(): void { $this->product->expects($this->once())->method('getId')->will($this->returnValue(1)); $this->category->expects($this->once())->method('getId')->will($this->returnValue(1)); diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductUrlKeyAutogeneratorObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductUrlKeyAutogeneratorObserverTest.php new file mode 100644 index 0000000000000..b9628caff8400 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductUrlKeyAutogeneratorObserverTest.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Test\Unit\Observer; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; + +/** + * Unit tests for \Magento\CatalogUrlRewrite\Observer\ProductUrlKeyAutogeneratorObserver class + */ +class ProductUrlKeyAutogeneratorObserverTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator|\PHPUnit_Framework_MockObject_MockObject + */ + private $productUrlPathGenerator; + + /** @var \Magento\CatalogUrlRewrite\Observer\ProductUrlKeyAutogeneratorObserver */ + private $productUrlKeyAutogeneratorObserver; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->productUrlPathGenerator = $this->getMockBuilder(ProductUrlPathGenerator::class) + ->disableOriginalConstructor() + ->setMethods(['getUrlKey']) + ->getMock(); + + $this->productUrlKeyAutogeneratorObserver = (new ObjectManagerHelper($this))->getObject( + \Magento\CatalogUrlRewrite\Observer\ProductUrlKeyAutogeneratorObserver::class, + [ + 'productUrlPathGenerator' => $this->productUrlPathGenerator + ] + ); + } + + /** + * @return void + */ + public function testExecuteWithUrlKey(): void + { + $urlKey = 'product_url_key'; + + $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->setMethods(['setUrlKey']) + ->getMock(); + $product->expects($this->atLeastOnce())->method('setUrlKey')->with($urlKey); + $event = $this->getMockBuilder(\Magento\Framework\Event::class) + ->disableOriginalConstructor() + ->setMethods(['getProduct']) + ->getMock(); + $event->expects($this->atLeastOnce())->method('getProduct')->willReturn($product); + /** @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject $observer */ + $observer = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) + ->disableOriginalConstructor() + ->setMethods(['getEvent']) + ->getMock(); + $observer->expects($this->atLeastOnce())->method('getEvent')->willReturn($event); + $this->productUrlPathGenerator->expects($this->atLeastOnce())->method('getUrlKey')->with($product) + ->willReturn($urlKey); + + $this->productUrlKeyAutogeneratorObserver->execute($observer); + } + + /** + * @return void + */ + public function testExecuteWithEmptyUrlKey(): void + { + $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->setMethods(['setUrlKey']) + ->getMock(); + $product->expects($this->never())->method('setUrlKey'); + $event = $this->getMockBuilder(\Magento\Framework\Event::class) + ->disableOriginalConstructor() + ->setMethods(['getProduct']) + ->getMock(); + $event->expects($this->atLeastOnce())->method('getProduct')->willReturn($product); + /** @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject $observer */ + $observer = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) + ->disableOriginalConstructor() + ->setMethods(['getEvent']) + ->getMock(); + $observer->expects($this->atLeastOnce())->method('getEvent')->willReturn($event); + $this->productUrlPathGenerator->expects($this->atLeastOnce())->method('getUrlKey')->with($product) + ->willReturn(null); + + $this->productUrlKeyAutogeneratorObserver->execute($observer); + } +} diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js index 5d79ac77218f5..8f1a75d2be0d4 100755 --- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js @@ -450,6 +450,10 @@ define([ */ toggleUseDefault: function (state) { this.disabled(state); + + if (this.source && this.hasService()) { + this.source.set('data.use_default.' + this.index, Number(state)); + } }, /** diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/single-checkbox-use-config.js b/app/code/Magento/Ui/view/base/web/js/form/element/single-checkbox-use-config.js index b20b2c31ee1fb..3ee7ddd1e738f 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/single-checkbox-use-config.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/single-checkbox-use-config.js @@ -36,6 +36,10 @@ define([ */ toggleElement: function () { this.disabled(this.isUseDefault() || this.isUseConfig()); + + if (this.source) { + this.source.set('data.use_default.' + this.index, Number(this.isUseDefault())); + } } }); }); diff --git a/app/code/Magento/Ui/view/base/web/templates/form/element/helper/service.html b/app/code/Magento/Ui/view/base/web/templates/form/element/helper/service.html index 46f0a2df52441..195104b36721e 100644 --- a/app/code/Magento/Ui/view/base/web/templates/form/element/helper/service.html +++ b/app/code/Magento/Ui/view/base/web/templates/form/element/helper/service.html @@ -10,7 +10,6 @@ attr=" id: $data.uid + '_default', name: 'use_default[' + $data.index + ']', - 'data-form-part': $data.ns " ko-checked="isUseDefault" ko-disabled="$data.serviceDisabled"> diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/abstract.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/abstract.test.js index 390c5aa89fcc7..e1d853e0b1b55 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/abstract.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/abstract.test.js @@ -5,19 +5,50 @@ /*eslint max-nested-callbacks: 0*/ define([ - 'Magento_Ui/js/form/element/abstract' -], function (Abstract) { + 'squire' +], function (Squire) { 'use strict'; describe('Magento_Ui/js/form/element/abstract', function () { - var params, model; - - beforeEach(function () { + var injector = new Squire(), + providerMock = { + get: jasmine.createSpy(), + set: jasmine.createSpy() + }, + mocks = { + 'Magento_Ui/js/lib/registry/registry': { + /** Method stub. */ + get: function () { + return providerMock; + }, + create: jasmine.createSpy(), + set: jasmine.createSpy(), + async: jasmine.createSpy() + }, + '/mage/utils/wrapper': jasmine.createSpy() + }, + dataScope = 'abstract', params = { - dataScope: 'abstract' - }; - model = new Abstract(params); - model.source = jasmine.createSpyObj('model.source', ['set']); + provider: 'provName', + name: '', + index: 'testIndex', + dataScope: dataScope, + service: { + template: 'ui/form/element/helper/service' + } + }, + model; + + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Ui/js/form/element/abstract', + 'knockoutjs/knockout-es5' + ], function (Constr) { + model = new Constr(params); + + done(); + }); }); describe('initialize method', function () { @@ -50,8 +81,10 @@ define([ var expectedValue = 1; spyOn(model, 'getInitialValue').and.returnValue(expectedValue); + model.service = true; expect(model.setInitialValue()).toEqual(model); expect(model.getInitialValue).toHaveBeenCalled(); + expect(model.source.set).toHaveBeenCalledWith('data.use_default.' + model.index, 0); expect(model.value()).toEqual(expectedValue); }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/boolean.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/boolean.test.js index 4f64d1f53aa21..282badea0889f 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/boolean.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/boolean.test.js @@ -6,18 +6,45 @@ /*eslint max-nested-callbacks: 0*/ define([ - 'Magento_Ui/js/form/element/boolean' -], function (BooleanElement) { + 'squire' +], function (Squire) { 'use strict'; describe('Magento_Ui/js/form/element/boolean', function () { - var params, model; + var injector = new Squire(), + mocks = { + 'Magento_Ui/js/lib/registry/registry': { + /** Method stub. */ + get: function () { + return { + get: jasmine.createSpy(), + set: jasmine.createSpy() + }; + }, + create: jasmine.createSpy(), + set: jasmine.createSpy(), + async: jasmine.createSpy() + }, + '/mage/utils/wrapper': jasmine.createSpy() + }, + model, + dataScope = 'dataScope'; - beforeEach(function () { - params = { - dataScope: 'abstract' - }; - model = new BooleanElement(params); + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Ui/js/form/element/boolean', + 'knockoutjs/knockout-es5' + ], function (Constr) { + model = new Constr({ + provider: 'provName', + name: '', + index: '', + dataScope: dataScope + }); + + done(); + }); }); describe('getInitialValue method', function () { diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/date-time.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/date-time.test.js index bd314bcc85736..01208a083a696 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/date-time.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/date-time.test.js @@ -6,23 +6,52 @@ /*eslint max-nested-callbacks: 0*/ define([ - 'Magento_Ui/js/form/element/date', - 'mageUtils', - 'moment' -], function (DateElement, utils, moment) { + 'squire' +], function (Squire) { 'use strict'; - describe('Magento_Ui/js/form/element/date', function () { - var params, model; - - beforeEach(function () { - params = { - dataScope: 'abstract', - options: { - showsTime: true - } - }; - model = new DateElement(params); + describe('Magento_Ui/js/form/element/date-time', function () { + var injector = new Squire(), + mocks = { + 'Magento_Ui/js/lib/registry/registry': { + /** Method stub. */ + get: function () { + return { + get: jasmine.createSpy(), + set: jasmine.createSpy() + }; + }, + create: jasmine.createSpy(), + set: jasmine.createSpy(), + async: jasmine.createSpy() + }, + '/mage/utils/wrapper': jasmine.createSpy() + }, + model, utils, moment, + dataScope = 'abstract'; + + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Ui/js/form/element/date', + 'mageUtils', + 'moment', + 'knockoutjs/knockout-es5' + ], function (Constr, mageUtils, m) { + model = new Constr({ + provider: 'provName', + name: '', + index: '', + dataScope: dataScope, + options: { + showsTime: true + } + }); + utils = mageUtils; + moment = m; + + done(); + }); }); it('Check prepareDateTimeFormats function', function () { diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/date.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/date.test.js index 0b2290ba7a831..f9d379407fcbd 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/date.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/date.test.js @@ -4,19 +4,50 @@ */ define([ - 'Magento_Ui/js/form/element/date', - 'mageUtils' -], function (DateElement, utils) { + 'squire' +], function (Squire) { 'use strict'; describe('Magento_Ui/js/form/element/date', function () { - var params, model; + var injector = new Squire(), + mocks = { + 'Magento_Ui/js/lib/registry/registry': { + /** Method stub. */ + get: function () { + return { + get: jasmine.createSpy(), + set: jasmine.createSpy() + }; + }, + create: jasmine.createSpy(), + set: jasmine.createSpy(), + async: jasmine.createSpy() + }, + '/mage/utils/wrapper': jasmine.createSpy() + }, + model, utils, + dataScope = 'abstract'; - beforeEach(function () { - params = { - dataScope: 'abstract' - }; - model = new DateElement(params); + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Ui/js/form/element/date', + 'mageUtils', + 'knockoutjs/knockout-es5' + ], function (Constr, mageUtils) { + model = new Constr({ + provider: 'provName', + name: '', + index: '', + dataScope: dataScope, + options: { + showsTime: true + } + }); + utils = mageUtils; + + done(); + }); }); it('Check prepareDateTimeFormats function', function () { diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/file-uploader.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/file-uploader.test.js index 916733fd2b0a7..46916054a29be 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/file-uploader.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/file-uploader.test.js @@ -7,28 +7,65 @@ define([ 'jquery', - 'Magento_Ui/js/form/element/file-uploader' -], function ($, FileUploader) { + 'squire' +], function ($, Squire) { 'use strict'; describe('Magento_Ui/js/form/element/file-uploader', function () { - var component; + var injector = new Squire(), + mocks = { + 'Magento_Ui/js/lib/core/events': { + on: jasmine.createSpy() + }, + 'Magento_Ui/js/lib/registry/registry': { + /** Method stub. */ + get: function () { + return { + get: jasmine.createSpy(), + set: jasmine.createSpy() + }; + }, + create: jasmine.createSpy(), + set: jasmine.createSpy(), + async: jasmine.createSpy() + }, + '/mage/utils/wrapper': jasmine.createSpy() + }, + component, + dataScope = 'dataScope', + originalJQuery = jQuery.fn; + + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Ui/js/form/element/file-uploader', + 'knockoutjs/knockout-es5' + ], function (Constr) { + component = new Constr({ + provider: 'provName', + name: '', + index: '', + dataScope: dataScope + }); - beforeEach(function () { - component = new FileUploader({ - dataScope: 'abstract' + done(); }); }); + afterEach(function () { + jQuery.fn = originalJQuery; + }); + describe('initUploader method', function () { it('creates instance of file uploader', function () { var elem = document.createElement('input'); - spyOn($.fn, 'fileupload'); + spyOn(jQuery.fn, 'fileupload'); component.initUploader(elem); - expect($.fn.fileupload).toHaveBeenCalled(); + expect(jQuery.fn.fileupload).toHaveBeenCalled(); + }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/post-code.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/post-code.test.js index 9bddfcd35642a..96bca1bbd8c6b 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/post-code.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/post-code.test.js @@ -5,19 +5,45 @@ /*eslint max-nested-callbacks: 0*/ define([ - 'uiRegistry', - 'Magento_Ui/js/form/element/post-code' -], function (registry, PostCodeElement) { + 'squire' +], function (Squire) { 'use strict'; describe('Magento_Ui/js/form/element/post-code', function () { - var params, model; + var injector = new Squire(), + mocks = { + 'Magento_Ui/js/lib/registry/registry': { + /** Method stub. */ + get: function () { + return { + get: jasmine.createSpy(), + set: jasmine.createSpy() + }; + }, + create: jasmine.createSpy(), + set: jasmine.createSpy(), + async: jasmine.createSpy() + }, + '/mage/utils/wrapper': jasmine.createSpy() + }, + model, + dataScope = 'post-code'; - beforeEach(function () { - params = { - dataScope: 'post-code' - }; - model = new PostCodeElement(params); + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Ui/js/form/element/post-code', + 'knockoutjs/knockout-es5' + ], function (Constr) { + model = new Constr({ + provider: 'provName', + name: '', + index: '', + dataScope: dataScope + }); + + done(); + }); }); describe('update method', function () { @@ -31,9 +57,9 @@ define([ } }; - spyOn(registry, 'get').and.returnValue(country); + spyOn(mocks['Magento_Ui/js/lib/registry/registry'], 'get').and.returnValue(country); model.update(value); - expect(registry.get).toHaveBeenCalled(); + expect(mocks['Magento_Ui/js/lib/registry/registry'].get).toHaveBeenCalled(); expect(model.error()).toEqual(false); expect(model.required()).toEqual(false); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/region.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/region.test.js index 8e76d8e90729d..a36d3b0b7fefa 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/region.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/region.test.js @@ -5,19 +5,46 @@ /*eslint max-nested-callbacks: 0*/ define([ - 'uiRegistry', - 'Magento_Ui/js/form/element/region' -], function (registry, RegionElement) { + 'squire' +], function (Squire) { 'use strict'; describe('Magento_Ui/js/form/element/region', function () { - var params, model; + var injector = new Squire(), + mocks = { + 'Magento_Ui/js/lib/registry/registry': { + /** Method stub. */ + get: function () { + return { + get: jasmine.createSpy(), + set: jasmine.createSpy() + }; + }, + create: jasmine.createSpy(), + set: jasmine.createSpy(), + async: jasmine.createSpy() + }, + '/mage/utils/wrapper': jasmine.createSpy() + }, + model, + dataScope = 'dataScope'; - beforeEach(function () { - params = { - dataScope: 'region' - }; - model = new RegionElement(params); + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Ui/js/form/element/region', + 'knockoutjs/knockout-es5', + 'Magento_Ui/js/lib/knockout/extender/observable_array' + ], function (Constr) { + model = new Constr({ + provider: 'provName', + name: '', + index: '', + dataScope: dataScope + }); + + done(); + }); }); describe('update method', function () { @@ -31,9 +58,9 @@ define([ } }; - spyOn(registry, 'get').and.returnValue(country); + spyOn(mocks['Magento_Ui/js/lib/registry/registry'], 'get').and.returnValue(country); model.update(value); - expect(registry.get).toHaveBeenCalled(); + expect(mocks['Magento_Ui/js/lib/registry/registry'].get).toHaveBeenCalled(); expect(model.error()).toEqual(false); expect(model.required()).toEqual(false); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/select.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/select.test.js index 6eac936974595..fffb045e8ce41 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/select.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/select.test.js @@ -5,18 +5,49 @@ /*eslint max-nested-callbacks: 0*/ define([ - 'Magento_Ui/js/form/element/select' -], function (SelectElement) { + 'squire' +], function (Squire) { 'use strict'; describe('Magento_Ui/js/form/element/select', function () { - var params, model; - - beforeEach(function () { + var injector = new Squire(), + mocks = { + 'Magento_Ui/js/lib/registry/registry': { + /** Method stub. */ + get: function () { + return { + get: jasmine.createSpy(), + set: jasmine.createSpy() + }; + }, + options: jasmine.createSpy(), + create: jasmine.createSpy(), + set: jasmine.createSpy(), + async: jasmine.createSpy() + }, + '/mage/utils/wrapper': jasmine.createSpy(), + 'Magento_Ui/js/core/renderer/layout': jasmine.createSpy() + }, + dataScope = 'select', params = { - dataScope: 'select' - }; - model = new SelectElement(params); + provider: 'provName', + name: '', + index: '', + dataScope: dataScope + }, + model; + + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Ui/js/form/element/select', + 'knockoutjs/knockout-es5', + 'Magento_Ui/js/lib/knockout/extender/observable_array' + ], function (Constr) { + model = new Constr(params); + + done(); + }); }); describe('initialize method', function () { @@ -24,26 +55,20 @@ define([ expect(model).toBeDefined(); }); it('check for chainable', function () { - spyOn(model, 'initInput'); spyOn(model, 'initFilter'); expect(model.initialize(params)).toEqual(model); - expect(model.initInput).not.toHaveBeenCalled(); expect(model.initFilter).not.toHaveBeenCalled(); }); it('check for call initInput', function () { - spyOn(model, 'initInput'); spyOn(model, 'initFilter'); model.customEntry = true; expect(model.initialize(params)).toEqual(model); - expect(model.initInput).toHaveBeenCalled(); expect(model.initFilter).not.toHaveBeenCalled(); }); it('check for call initFilter', function () { - spyOn(model, 'initInput'); spyOn(model, 'initFilter'); model.filterBy = true; expect(model.initialize(params)).toEqual(model); - expect(model.initInput).not.toHaveBeenCalled(); expect(model.initFilter).toHaveBeenCalled(); }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/single-checkbox-use-config.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/single-checkbox-use-config.test.js new file mode 100644 index 0000000000000..f63e0adda1e17 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/single-checkbox-use-config.test.js @@ -0,0 +1,78 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/*eslint max-nested-callbacks: 0*/ + +define([ + 'squire' +], function (Squire) { + 'use strict'; + + describe('Magento_Ui/js/form/element/single-checkbox-use-config', function () { + var injector = new Squire(), + mocks = { + 'Magento_Ui/js/lib/registry/registry': { + /** Method stub. */ + get: function () { + return { + get: jasmine.createSpy(), + set: jasmine.createSpy() + }; + }, + create: jasmine.createSpy(), + set: jasmine.createSpy(), + async: jasmine.createSpy() + }, + '/mage/utils/wrapper': jasmine.createSpy() + }, + dataScope = 'dataScope', + params = { + provider: 'provName', + name: '', + index: '', + dataScope: dataScope + }, + model; + + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Ui/js/form/element/single-checkbox-use-config', + 'knockoutjs/knockout-es5' + ], function (Constr) { + model = new Constr(params); + done(); + }); + }); + + describe('initObservable method', function () { + it('check for chainable', function () { + expect(model.initObservable({})).toEqual(model); + }); + it('check for validation', function () { + spyOn(model, 'observe').and.returnValue(model); + expect(model.initObservable()).toEqual(model); + expect(model.validation).toEqual({}); + }); + }); + + describe('toggleElement method', function () { + it('check with isUseDefault false', function () { + spyOn(model, 'isUseDefault').and.returnValue(false); + spyOn(model, 'isUseConfig').and.returnValue(false); + expect(model.toggleElement()).toEqual(undefined); + expect(model.disabled()).toEqual(false); + expect(model.source.set).toHaveBeenCalledWith('data.use_default.' + model.index, 0); + }); + it('check with isUseDefault true', function () { + spyOn(model, 'isUseDefault').and.returnValue(true); + spyOn(model, 'isUseConfig').and.returnValue(false); + expect(model.toggleElement()).toEqual(undefined); + expect(model.disabled()).toEqual(true); + expect(model.source.set).toHaveBeenCalledWith('data.use_default.' + model.index, 1); + }); + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/textarea.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/textarea.test.js index 03b00f0c3a842..60beff8d9b628 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/textarea.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/textarea.test.js @@ -4,18 +4,46 @@ */ define([ - 'Magento_Ui/js/form/element/textarea' -], function (TextareaElement) { + 'squire' +], function (Squire) { 'use strict'; describe('Magento_Ui/js/form/element/textarea', function () { - var params, model; + var injector = new Squire(), + mocks = { + 'Magento_Ui/js/lib/registry/registry': { + /** Method stub. */ + get: function () { + return { + get: jasmine.createSpy(), + set: jasmine.createSpy() + }; + }, + options: jasmine.createSpy(), + create: jasmine.createSpy(), + set: jasmine.createSpy(), + async: jasmine.createSpy() + }, + '/mage/utils/wrapper': jasmine.createSpy() + }, + model, + dataScope = 'dataScope'; - beforeEach(function () { - params = { - dataScope: 'textarea' - }; - model = new TextareaElement(params); + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Ui/js/form/element/textarea', + 'knockoutjs/knockout-es5' + ], function (Constr) { + model = new Constr({ + provider: 'provName', + name: '', + index: '', + dataScope: dataScope + }); + + done(); + }); }); it('check if component defined', function () { diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js index 52a410ffbf6c2..1bc05bf503b95 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js @@ -7,29 +7,59 @@ define([ 'underscore', 'uiRegistry', - 'Magento_Ui/js/form/element/ui-select', + 'squire', 'ko', - 'jquery' -], function (_, registry, Constr, ko, $) { + 'jquery', +], function (_, registry, Squire, ko, $) { 'use strict'; describe('Magento_Ui/js/form/element/ui-select', function () { - var obj, originaljQueryAjax; + var obj, originaljQueryAjax, + injector = new Squire(), + mocks = { + 'Magento_Ui/js/lib/registry/registry': { + /** Method stub. */ + get: function () { + return { + get: jasmine.createSpy(), + set: jasmine.createSpy() + }; + }, + create: jasmine.createSpy(), + set: jasmine.createSpy(), + async: jasmine.createSpy() + }, + '/mage/utils/wrapper': jasmine.createSpy() + }, + dataScope = 'abstract'; + + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Ui/js/form/element/ui-select', + 'knockoutjs/knockout-es5' + ], function (Constr) { + obj = new Constr({ + provider: 'provName', + name: '', + index: '', + dataScope: dataScope, + options: { + showsTime: true + } + }); - beforeEach(function () { - obj = new Constr({ - name: 'uiSelect', - dataScope: '', - provider: 'provider' - }); + obj.value = ko.observableArray([]); + obj.cacheOptions.plain = []; + originaljQueryAjax = $.ajax; - obj.value = ko.observableArray([]); - obj.cacheOptions.plain = []; - originaljQueryAjax = $.ajax; + done(); + }); }); afterEach(function () { $.ajax = originaljQueryAjax; + injector.clean(); }); describe('"initialize" method', function () { From d11bc5147e670b257db16a7e2361dfa9e9975c03 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 14 Aug 2018 10:15:57 +0300 Subject: [PATCH 0984/1171] MAGETWO-93282: [2.3] Customer logout on change password. --- app/code/Magento/Customer/Model/AccountManagement.php | 2 -- .../Customer/Test/Unit/Model/AccountManagementTest.php | 5 ----- 2 files changed, 7 deletions(-) diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 3aba7d1da89fb..fe17adcb09c0d 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -1468,9 +1468,7 @@ private function destroyCustomerSessions($customerId) /** @var \Magento\Customer\Model\Visitor $visitor */ foreach ($visitorCollection->getItems() as $visitor) { $sessionId = $visitor->getSessionId(); - $this->sessionManager->start(); $this->saveHandler->destroy($sessionId); - $this->sessionManager->writeClose(); } } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php index 86e7683545fa2..aad20f757e91e 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php @@ -1361,7 +1361,6 @@ private function reInitModel() $this->prepareDateTimeFactory(); $this->sessionManager = $this->getMockBuilder(\Magento\Framework\Session\SessionManagerInterface::class) ->disableOriginalConstructor() - ->setMethods(['destroy', 'start', 'writeClose']) ->getMockForAbstractClass(); $this->visitorCollectionFactory = $this->getMockBuilder( \Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory::class @@ -1494,8 +1493,6 @@ public function testChangePassword() ->method('save') ->with($customer); - $this->sessionManager->expects($this->atLeastOnce())->method('start'); - $this->sessionManager->expects($this->atLeastOnce())->method('writeClose'); $this->sessionManager->expects($this->atLeastOnce())->method('getSessionId'); $visitor = $this->getMockBuilder(\Magento\Customer\Model\Visitor::class) @@ -1551,8 +1548,6 @@ function ($string) { $this->customerSecure->expects($this->any())->method('setPasswordHash')->willReturn(null); $this->sessionManager->expects($this->atLeastOnce())->method('destroy'); - $this->sessionManager->expects($this->atLeastOnce())->method('start'); - $this->sessionManager->expects($this->atLeastOnce())->method('writeClose'); $this->sessionManager->expects($this->atLeastOnce())->method('getSessionId'); $visitor = $this->getMockBuilder(\Magento\Customer\Model\Visitor::class) ->disableOriginalConstructor() From 6bf8b330387c07b62908826228e58d7912a140db Mon Sep 17 00:00:00 2001 From: NazarKlovanych <nazar.klovanich@transoftgroup.com> Date: Tue, 14 Aug 2018 10:42:32 +0300 Subject: [PATCH 0985/1171] Fix wrong class name for Unit test --- .../Test/Unit/Cron/DeleteAbandonedStoreFlatTablesTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Cron/DeleteAbandonedStoreFlatTablesTest.php b/app/code/Magento/Catalog/Test/Unit/Cron/DeleteAbandonedStoreFlatTablesTest.php index cd017dbcafbc2..1a9d7959dda9c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Cron/DeleteAbandonedStoreFlatTablesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Cron/DeleteAbandonedStoreFlatTablesTest.php @@ -13,7 +13,7 @@ /** * @covers \Magento\Catalog\Cron\DeleteAbandonedStoreFlatTables */ -class AvailabilityCheckerTest extends \PHPUnit\Framework\TestCase +class DeleteAbandonedStoreFlatTablesTest extends \PHPUnit\Framework\TestCase { /** * Testable Object From 3b299196260c46127961487f0f8dc9c29506e13d Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Tue, 14 Aug 2018 13:02:41 +0300 Subject: [PATCH 0986/1171] MAGETWO-91682: [2.3] Incorrect amount is sent to PayPal when discount is applied to an order --- .../_files/scenarios/including_tax_apply_tax_after_discount.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_apply_tax_after_discount.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_apply_tax_after_discount.php index 41b1aec5a67f3..e0cb82e50cbd4 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_apply_tax_after_discount.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_apply_tax_after_discount.php @@ -21,7 +21,7 @@ SetupUtil::TAX_RATE_OVERRIDES => [ SetupUtil::TAX_RATE_TX => 10, SetupUtil::TAX_STORE_RATE => 10, - SetupUtil::TAX_RATE_SHIPPING => 10 + SetupUtil::TAX_RATE_SHIPPING => 10, ], SetupUtil::TAX_RULE_OVERRIDES => [ [ From 9bc3fd43de7b22f68647ccae0b6b630054764da1 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 14 Aug 2018 13:50:08 +0300 Subject: [PATCH 0987/1171] MAGETWO-93764: [2.3] Zero value in the email filter field returns all email address --- .../Ui/Component/Filters/Type/Input.php | 6 ++--- .../Unit/Component/Filters/Type/InputTest.php | 25 +++++++++++++------ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Ui/Component/Filters/Type/Input.php b/app/code/Magento/Ui/Component/Filters/Type/Input.php index 9cc060ae58172..1fc132700c8a9 100644 --- a/app/code/Magento/Ui/Component/Filters/Type/Input.php +++ b/app/code/Magento/Ui/Component/Filters/Type/Input.php @@ -29,7 +29,7 @@ class Input extends AbstractFilter * * @return void */ - public function prepare() + public function prepare(): void { $this->wrappedComponent = $this->uiComponentFactory->create( $this->getName(), @@ -62,12 +62,12 @@ public function prepare() * * @return void */ - protected function applyFilter() + protected function applyFilter(): void { if (isset($this->filterData[$this->getName()])) { $value = str_replace(['%', '_'], ['\%', '\_'], $this->filterData[$this->getName()]); - if (!empty($value)) { + if ($value || $value === '0') { $filter = $this->filterBuilder->setConditionType('like') ->setField($this->getName()) ->setValue(sprintf('%%%s%%', $value)) diff --git a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/InputTest.php b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/InputTest.php index d814fdcd153da..32524e2331ba7 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/InputTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/InputTest.php @@ -6,7 +6,6 @@ namespace Magento\Ui\Test\Unit\Component\Filters\Type; use Magento\Framework\View\Element\UiComponent\ContextInterface; -use Magento\Framework\View\Element\UiComponent\ContextInterface as UiContext; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Framework\View\Element\UiComponentInterface; use Magento\Ui\Component\Filters\Type\Input; @@ -37,18 +36,18 @@ class InputTest extends \PHPUnit\Framework\TestCase protected $filterModifierMock; /** - * Set up + * @inheritdoc */ protected function setUp() { $this->contextMock = $this->getMockForAbstractClass( - \Magento\Framework\View\Element\UiComponent\ContextInterface::class, + ContextInterface::class, [], '', false ); $this->uiComponentFactory = $this->createPartialMock( - \Magento\Framework\View\Element\UiComponentFactory::class, + UiComponentFactory::class, ['create'] ); $this->filterBuilderMock = $this->createMock(\Magento\Framework\Api\FilterBuilder::class); @@ -63,7 +62,7 @@ protected function setUp() * * @return void */ - public function testGetComponentName() + public function testGetComponentName(): void { $this->contextMock->expects($this->never())->method('getProcessor'); $date = new Input( @@ -86,7 +85,7 @@ public function testGetComponentName() * @dataProvider getPrepareDataProvider * @return void */ - public function testPrepare($name, $filterData, $expectedCondition) + public function testPrepare(string $name, array $filterData, ?array $expectedCondition): void { $processor = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\Processor::class) ->disableOriginalConstructor() @@ -94,7 +93,7 @@ public function testPrepare($name, $filterData, $expectedCondition) $this->contextMock->expects($this->atLeastOnce())->method('getProcessor')->willReturn($processor); /** @var UiComponentInterface $uiComponent */ $uiComponent = $this->getMockForAbstractClass( - \Magento\Framework\View\Element\UiComponentInterface::class, + UiComponentInterface::class, [], '', false @@ -169,7 +168,7 @@ public function testPrepare($name, $filterData, $expectedCondition) /** * @return array */ - public function getPrepareDataProvider() + public function getPrepareDataProvider(): array { return [ [ @@ -177,6 +176,16 @@ public function getPrepareDataProvider() ['test_date' => ''], null, ], + [ + 'test_date', + ['test_date' => null], + null, + ], + [ + 'test_date', + ['test_date' => '0'], + ['like' => '%0%'], + ], [ 'test_date', ['test_date' => 'some_value'], From 51b29a5db1abbdf50153f961fb74f273c0665878 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Tue, 14 Aug 2018 15:13:36 +0300 Subject: [PATCH 0988/1171] MAGETWO-91493: MDC Framework Issues. Message passed as a service to TransportBuilder - Add message factory --- .../Mail/Template/TransportBuilder.php | 18 +++++++++++++--- .../Unit/Template/TransportBuilderTest.php | 21 +++++++++++++------ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index a8374be59ff65..cd8cf94ab50ee 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -11,6 +11,7 @@ use Magento\Framework\App\TemplateTypesInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Mail\MessageInterface; +use Magento\Framework\Mail\MessageInterfaceFactory; use Magento\Framework\Mail\TransportInterfaceFactory; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Phrase; @@ -88,25 +89,36 @@ class TransportBuilder */ protected $mailTransportFactory; + /** + * @var \Magento\Framework\Mail\MessageInterfaceFactory + */ + private $messageFactory; + /** * @param FactoryInterface $templateFactory * @param MessageInterface $message * @param SenderResolverInterface $senderResolver * @param ObjectManagerInterface $objectManager * @param TransportInterfaceFactory $mailTransportFactory + * @param MessageInterfaceFactory $messageFactory + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( FactoryInterface $templateFactory, MessageInterface $message, SenderResolverInterface $senderResolver, ObjectManagerInterface $objectManager, - TransportInterfaceFactory $mailTransportFactory + TransportInterfaceFactory $mailTransportFactory, + MessageInterfaceFactory $messageFactory = null ) { $this->templateFactory = $templateFactory; - $this->message = $message; $this->objectManager = $objectManager; $this->_senderResolver = $senderResolver; $this->mailTransportFactory = $mailTransportFactory; + $this->messageFactory = $messageFactory ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(MessageInterfaceFactory::class); + $this->message = $this->messageFactory->create(); } /** @@ -242,7 +254,7 @@ public function getTransport() */ protected function reset() { - $this->message = $this->objectManager->create(\Magento\Framework\Mail\Message::class); + $this->message = $this->messageFactory->create(); $this->templateIdentifier = null; $this->templateVars = null; $this->templateOptions = null; diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index e79d12310436c..e1ebbb421468a 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -8,6 +8,7 @@ use Magento\Framework\App\TemplateTypesInterface; use Magento\Framework\Mail\MessageInterface; +use Magento\Framework\Mail\MessageInterfaceFactory; class TransportBuilderTest extends \PHPUnit\Framework\TestCase { @@ -41,6 +42,11 @@ class TransportBuilderTest extends \PHPUnit\Framework\TestCase */ protected $senderResolverMock; + /** + * @var \Magento\Framework\Mail\MessageInterfaceFactory| \PHPUnit_Framework_MockObject_MockObject + */ + private $messageFactoryMock; + /** * @var \PHPUnit_Framework_MockObject_MockObject */ @@ -60,7 +66,12 @@ protected function setUp() \Magento\Framework\Mail\TransportInterfaceFactory::class )->disableOriginalConstructor() ->setMethods(['create']) - ->getMock(); + ->getMockForAbstractClass(); + $this->messageFactoryMock = $this->getMockBuilder(\Magento\Framework\Mail\MessageInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMockForAbstractClass(); + $this->messageFactoryMock->expects($this->atLeastOnce())->method('create')->willReturn($this->messageMock); $this->builder = $objectManagerHelper->getObject( $this->builderClassName, [ @@ -68,7 +79,8 @@ protected function setUp() 'message' => $this->messageMock, 'objectManager' => $this->objectManagerMock, 'senderResolver' => $this->senderResolverMock, - 'mailTransportFactory' => $this->mailTransportFactoryMock + 'mailTransportFactory' => $this->mailTransportFactoryMock, + 'messageFactory' => $this->messageFactoryMock ] ); } @@ -122,10 +134,7 @@ public function testGetTransport($templateType, $messageType, $bodyText, $templa ->with($this->equalTo(['message' => $this->messageMock])) ->willReturn($transport); - $this->objectManagerMock->expects($this->at(0)) - ->method('create') - ->with($this->equalTo(\Magento\Framework\Mail\Message::class)) - ->willReturn($transport); + $this->messageFactoryMock->expects($this->once())->method('create')->willReturn($transport); $this->builder->setTemplateIdentifier('identifier')->setTemplateVars($vars)->setTemplateOptions($options); $this->assertInstanceOf(\Magento\Framework\Mail\TransportInterface::class, $this->builder->getTransport()); From 9e1caf991e3c3aa6fd36cba56703c4a4e45f0674 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Tue, 14 Aug 2018 15:54:49 +0300 Subject: [PATCH 0989/1171] Minor code style fixes --- .../Magento/Catalog/Model/ProductRepository.php | 16 ++++++---------- .../Unit/Cron/DeleteOutdatedPriceValuesTest.php | 4 +++- .../base/web/js/dynamic-rows/dynamic-rows.js | 2 +- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index d7fbcbcd523f8..ef2c99c5cb40e 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -514,18 +514,14 @@ protected function processMediaGallery(ProductInterface $product, $mediaGalleryE $newEntries = $mediaGalleryEntries; } - $images = $product->getMediaGallery('images'); - if ($images) { - $images = $this->determineImageRoles($product, $images); - } + $images = (array)$product->getMediaGallery('images'); + $images = $this->determineImageRoles($product, $images); $this->getMediaGalleryProcessor()->clearMediaAttribute($product, array_keys($product->getMediaAttributes())); - if ($images) { - foreach ($images as $image) { - if (!isset($image['removed']) && !empty($image['types'])) { - $this->getMediaGalleryProcessor()->setMediaAttribute($product, $image['types'], $image['file']); - } + foreach ($images as $image) { + if (!isset($image['removed']) && !empty($image['types'])) { + $this->getMediaGalleryProcessor()->setMediaAttribute($product, $image['types'], $image['file']); } } @@ -770,7 +766,7 @@ public function cleanCache() * @param array $images * @return array */ - private function determineImageRoles(ProductInterface $product, array $images) + private function determineImageRoles(ProductInterface $product, array $images) : array { $imagesWithRoles = []; foreach ($images as $image) { diff --git a/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php b/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php index 304e203f228af..c59d86aa3d5f1 100644 --- a/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php @@ -98,7 +98,9 @@ public function testExecute() $this->attributeMock->expects($this->once())->method('getId')->willReturn($attributeId); $this->attributeMock->expects($this->once())->method('getBackend')->willReturn($this->attributeBackendMock); $this->attributeBackendMock->expects($this->once())->method('getTable')->willReturn($table); - $this->resourceConnectionMock->expects($this->once())->method('getConnection')->willReturn($this->dbAdapterMock); + $this->resourceConnectionMock->expects($this->once()) + ->method('getConnection') + ->willReturn($this->dbAdapterMock); $this->dbAdapterMock->expects($this->exactly(2))->method('quoteInto')->willReturnMap([ ['attribute_id = ?', $attributeId, null, null, $conditions[0]], ['store_id != ?', Store::DEFAULT_STORE_ID, null, null, $conditions[1]], diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js index 870ad96732014..1d52fc78d7a85 100644 --- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js +++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js @@ -862,7 +862,7 @@ define([ this.update = true; if (~~this.currentPage() === this.pages()) { - lastRecordIndex = (this.startIndex + this.getChildItems().length - 1); + lastRecordIndex = this.startIndex + this.getChildItems().length - 1; lastRecord = _.findWhere(this.elems(), { index: lastRecordIndex From c3fa5b39fdb00279d7e48a3845fffbb372a5edea Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Tue, 14 Aug 2018 15:57:53 +0300 Subject: [PATCH 0990/1171] MAGETWO-91812: [Magento Cloud] - Issue with polluted database when updating product attributes through API --- .../tests/app/code/Magento/Ui/base/js/form/ui-select.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js index 1bc05bf503b95..11ddfc724b29a 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js @@ -9,7 +9,7 @@ define([ 'uiRegistry', 'squire', 'ko', - 'jquery', + 'jquery' ], function (_, registry, Squire, ko, $) { 'use strict'; From 9a5bfd3f38f09f94553ef79da140291943a5e57a Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 14 Aug 2018 16:07:33 +0300 Subject: [PATCH 0991/1171] Fixed typo --- app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php index 9e095fb6cdec4..dfd55f5346e59 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php @@ -567,10 +567,10 @@ public function getThumbnailUrl($filePath, $checkFile = false) * Create thumbnail for image and save it to thumbnails directory * * @param string $source Image path to be resized - * @param bool $keepRation Keep aspect ratio or not + * @param bool $keepRatio Keep aspect ratio or not * @return bool|string Resized filepath or false if errors were occurred */ - public function resizeFile($source, $keepRation = true) + public function resizeFile($source, $keepRatio = true) { $realPath = $this->_directory->getRelativePath($source); if (!$this->_directory->isFile($realPath) || !$this->_directory->isExist($realPath)) { @@ -587,7 +587,7 @@ public function resizeFile($source, $keepRation = true) } $image = $this->_imageFactory->create(); $image->open($source); - $image->keepAspectRatio($keepRation); + $image->keepAspectRatio($keepRatio); $image->resize($this->_resizeParameters['width'], $this->_resizeParameters['height']); $dest = $targetDir . '/' . pathinfo($source, PATHINFO_BASENAME); $image->save($dest); From e735178bdfeaa1eb5071361fca9feac5aaa52047 Mon Sep 17 00:00:00 2001 From: Thomas Klein <thomas@bird.eu> Date: Mon, 11 Jun 2018 17:41:24 +0200 Subject: [PATCH 0992/1171] add block config source --- .../Magento/Cms/Model/Config/Source/Block.php | 46 +++++++++++++++++++ .../ResourceModel/AbstractCollection.php | 28 +++++++++++ .../Model/ResourceModel/Page/Collection.php | 28 ----------- 3 files changed, 74 insertions(+), 28 deletions(-) create mode 100644 app/code/Magento/Cms/Model/Config/Source/Block.php diff --git a/app/code/Magento/Cms/Model/Config/Source/Block.php b/app/code/Magento/Cms/Model/Config/Source/Block.php new file mode 100644 index 0000000000000..e89daf381d94a --- /dev/null +++ b/app/code/Magento/Cms/Model/Config/Source/Block.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Cms\Model\Config\Source; + +use Magento\Cms\Model\ResourceModel\Block\CollectionFactory; + +/** + * Class Block + */ +class Block implements \Magento\Framework\Option\ArrayInterface +{ + /** + * @var array + */ + private $options; + + /** + * @var CollectionFactory + */ + private $collectionFactory; + + /** + * @param CollectionFactory $collectionFactory + */ + public function __construct( + CollectionFactory $collectionFactory + ) { + $this->collectionFactory = $collectionFactory; + } + + /** + * To option array + * + * @return array + */ + public function toOptionArray() + { + if (!$this->options) { + $this->options = $this->collectionFactory->create()->toOptionIdArray(); + } + return $this->options; + } +} diff --git a/app/code/Magento/Cms/Model/ResourceModel/AbstractCollection.php b/app/code/Magento/Cms/Model/ResourceModel/AbstractCollection.php index cd5945c2f47cb..2a67378614445 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/AbstractCollection.php +++ b/app/code/Magento/Cms/Model/ResourceModel/AbstractCollection.php @@ -176,4 +176,32 @@ public function getSelectCountSql() return $countSelect; } + + /** + * Returns pairs identifier - title for unique identifiers + * and pairs identifier|entity_id - title for non-unique after first + * + * @return array + */ + public function toOptionIdArray() + { + $res = []; + $existingIdentifiers = []; + foreach ($this as $item) { + $identifier = $item->getData('identifier'); + + $data['value'] = $identifier; + $data['label'] = $item->getData('title'); + + if (in_array($identifier, $existingIdentifiers)) { + $data['value'] .= '|' . $item->getData($this->getIdFieldName()); + } else { + $existingIdentifiers[] = $identifier; + } + + $res[] = $data; + } + + return $res; + } } diff --git a/app/code/Magento/Cms/Model/ResourceModel/Page/Collection.php b/app/code/Magento/Cms/Model/ResourceModel/Page/Collection.php index 98c071890be46..4ccc2c8f6e77d 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Page/Collection.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Page/Collection.php @@ -51,34 +51,6 @@ protected function _construct() $this->_map['fields']['store'] = 'store_table.store_id'; } - /** - * Returns pairs identifier - title for unique identifiers - * and pairs identifier|page_id - title for non-unique after first - * - * @return array - */ - public function toOptionIdArray() - { - $res = []; - $existingIdentifiers = []; - foreach ($this as $item) { - $identifier = $item->getData('identifier'); - - $data['value'] = $identifier; - $data['label'] = $item->getData('title'); - - if (in_array($identifier, $existingIdentifiers)) { - $data['value'] .= '|' . $item->getData('page_id'); - } else { - $existingIdentifiers[] = $identifier; - } - - $res[] = $data; - } - - return $res; - } - /** * Set first store flag * From b00d4b274f0e4030cd97c9b9f961380fe159f17d Mon Sep 17 00:00:00 2001 From: Thomas Klein <thomas@bird.eu> Date: Mon, 11 Jun 2018 18:21:02 +0200 Subject: [PATCH 0993/1171] add coverage test --- .../Unit/Model/Config/Source/BlockTest.php | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 app/code/Magento/Cms/Test/Unit/Model/Config/Source/BlockTest.php diff --git a/app/code/Magento/Cms/Test/Unit/Model/Config/Source/BlockTest.php b/app/code/Magento/Cms/Test/Unit/Model/Config/Source/BlockTest.php new file mode 100644 index 0000000000000..7d9162bd56776 --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/Model/Config/Source/BlockTest.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Cms\Test\Unit\Model\Config\Source; + +/** + * Class BlockTest + */ +class BlockTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Cms\Model\ResourceModel\Block\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $collectionFactory; + + /** + * @var \Magento\Cms\Model\Config\Source\Block + */ + protected $block; + + /** + * Set up + * + * @return void + */ + protected function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->collectionFactory = $this->createPartialMock( + \Magento\Cms\Model\ResourceModel\Block\CollectionFactory::class, + ['create'] + ); + + $this->block = $objectManager->getObject( + \Magento\Cms\Model\Config\Source\Block::class, + [ + 'collectionFactory' => $this->collectionFactory, + ] + ); + } + + /** + * Run test toOptionArray method + * + * @return void + */ + public function testToOptionArray() + { + $blockCollectionMock = $this->createMock(\Magento\Cms\Model\ResourceModel\Block\Collection::class); + + $this->collectionFactory->expects($this->once()) + ->method('create') + ->will($this->returnValue($blockCollectionMock)); + + $blockCollectionMock->expects($this->once()) + ->method('toOptionIdArray') + ->will($this->returnValue('return-value')); + + $this->assertEquals('return-value', $this->block->toOptionArray()); + } +} From d3711762c4deb9f3d510cf97a9ad9897467f4c8f Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv <rkostiv@magento.com> Date: Tue, 14 Aug 2018 17:34:16 +0300 Subject: [PATCH 0994/1171] MAGETWO-94060: [2.3.x] Unlink CatalogWidget from EAV indexer --- .../StorefrontConfigurableProductWithFileCustomOptionTest.xml | 1 + .../Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml index e7091dbfae215..602e04b138ea3 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-93059"/> <group value="ConfigurableProduct"/> + <group value="skip"/><!-- MAGETWO-94170 --> </annotations> <before> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml index 9e20bbdaac1d9..5c05d4a840009 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml @@ -49,6 +49,10 @@ <constraint name="Magento\Checkout\Test\Constraint\AssertOrderSuccessPlacedMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderAddresses" /> + <!-- MAGETWO-94169 --> + <data name="tag" xsi:type="string">stable:no</data> + <data name="issue" xsi:type="string">MAGETWO-94169: [MTF] - OnePageCheckoutUsingNonDefaultAddress_0 fails on 2.3-develop</data> + <!-- MAGETWO-94169 --> </variation> <variation name="OnePageCheckoutUsingNewAddress" summary="Checkout as Customer using New address" ticketId="MAGETWO-42601"> <data name="tag" xsi:type="string">severity:S1</data> From fab1ffa0a9986aa4de6d7ece27547ae30a55dc03 Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Tue, 14 Aug 2018 18:44:11 +0400 Subject: [PATCH 0995/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Updated automated test --- .../StorefrontAddProductToCardActionGroup.xml | 11 +++++++---- .../Section/StorefrontAddProductToCardSection.xml | 3 +++ .../Mftf/Test/AddingProductWithExpiredSessionTest.xml | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml index 71919d9da37de..49edf529a188d 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml @@ -41,10 +41,13 @@ <see userInput="Shipping Address" stepKey="seeShippingAddress"/> </actionGroup> - <actionGroup name="DeleteCreatedProduct"> - <fillField stepKey="searchToKeyword" selector="{{DeleteCreatedProduct.searchToKeyword}}" userInput="{{SimpleProductOne.name}}"/> - <click stepKey="clickSearchButton" selector="{{DeleteCreatedProduct.searchButton}}"/> - <wait stepKey="waitForFiltering" time="2"/> + <actionGroup name="DeleteCreatedProductActionGroup"> + <conditionalClick selector="{{DeleteCreatedProduct.clearAll}}" dependentSelector="{{DeleteCreatedProduct.clearAll}}" visible="1" stepKey="clickClearAllIfThereIsAnyValue"/> + <click stepKey="clickFilterButton" selector="{{DeleteCreatedProduct.filterButton}}"/> + <waitForElementVisible selector="{{DeleteCreatedProduct.filterSKUField}}" stepKey="waitForFilterDataLoaded"/> + <fillField stepKey="searchProductUsingSKUField" selector="{{DeleteCreatedProduct.filterSKUField}}" userInput="{{SimpleProductOne.sku}}"/> + <click stepKey="clickFiltersApplyButton" selector="{{DeleteCreatedProduct.filtersApplyButton}}"/> + <waitForElementNotVisible selector="{{DeleteCreatedProduct.filterSKUField}}" stepKey="waitForFilterBecomeNotVisible"/> <click selector="{{DeleteCreatedProduct.createdProductID}}" stepKey="selectCreatedProduct"/> <wait stepKey="waitSelectCreatedProduct" time="2"/> <waitForElementVisible selector="{{DeleteCreatedProduct.actionSelectBox}}" stepKey="waitToSelectActionVisible" time="50"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml index 868a47a26f42b..cc0cf8e07008e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml @@ -68,6 +68,9 @@ <element name="deleteButton" type="button" selector="//div[@class='col-xs-2']//*[text()='Delete']"/> <element name="okButton" type="button" selector=".action-primary.action-accept"/> <element name="clearAll" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[contains(text(), 'Clear all')]"/> + <element name="filterButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-filters-action-wrap']/button"/> + <element name="filterSKUField" type="input" selector="//*[@name='sku']"/> + <element name="filtersApplyButton" type="button" selector="//*[contains(text(),'Apply Filters')]"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml index 058b5b5c18bfd..b8abda3b37de6 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml @@ -42,7 +42,7 @@ <!--Delete created product--> <amOnPage url="/admin" stepKey="GoToDashboard"/> <actionGroup ref="GoToProductPage" stepKey="againGoToProductPage"/> - <actionGroup ref="DeleteCreatedProduct" stepKey="deleteCreatedProduct"/> + <actionGroup ref="DeleteCreatedProductActionGroup" stepKey="deleteCreatedProduct"/> </after> </test> From 3ebbcad54e6ff11ff91304454e985456de5b0a03 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Tue, 14 Aug 2018 18:00:04 +0300 Subject: [PATCH 0996/1171] MAGETWO-60377: [FT]MoveProductFromShoppingCartToWishlistTest randomly failed on assertProductDetails step on CICD --- .../Test/Constraint/AbstractAssertWishlistProductDetails.php | 5 +++++ .../TestCase/MoveProductFromShoppingCartToWishlistTest.xml | 2 -- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AbstractAssertWishlistProductDetails.php b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AbstractAssertWishlistProductDetails.php index c006516386274..803bee65fcdf4 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AbstractAssertWishlistProductDetails.php +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AbstractAssertWishlistProductDetails.php @@ -16,6 +16,11 @@ */ abstract class AbstractAssertWishlistProductDetails extends AbstractAssertForm { + /** + * @inheritdoc + */ + protected $skippedFields = ['sku']; + /** * Assert product details. * diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml index fd80d385e4853..95e6a854ed266 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml @@ -37,7 +37,6 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertProductDetailsInWishlist" /> </variation> <variation name="MoveProductFromShoppingCartToWishlistTestVariation5"> - <data name="tag" xsi:type="string">stable:no</data> <data name="product/0" xsi:type="string">bundleProduct::bundle_dynamic_product</data> <constraint name="Magento\Wishlist\Test\Constraint\AssertMoveProductToWishlistSuccessMessage" /> <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInWishlist" /> @@ -45,7 +44,6 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertProductDetailsInWishlist" /> </variation> <variation name="MoveProductFromShoppingCartToWishlistTestVariation6"> - <data name="tag" xsi:type="string">stable:no</data> <data name="product/0" xsi:type="string">bundleProduct::bundle_fixed_product</data> <constraint name="Magento\Wishlist\Test\Constraint\AssertMoveProductToWishlistSuccessMessage" /> <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInWishlist" /> From 24d2b3c1d583a43ecdb215b31d93f0484b0b04de Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Tue, 14 Aug 2018 11:13:44 -0500 Subject: [PATCH 0997/1171] MQE-1174: Deliver weekly regression enablement tests - Added a WYSIWYGDisabledSuite to ensure pagebuilder is not enabled for some tests --- .../AdminBundleProductSetEditContentTest.xml | 1 + ...NewProductsListWidgetBundleProductTest.xml | 1 + .../AdminSimpleProductSetEditContentTest.xml | 1 + .../AdminVirtualProductSetEditContentTest.xml | 1 + ...NewProductsListWidgetSimpleProductTest.xml | 1 + ...ewProductsListWidgetVirtualProductTest.xml | 2 ++ ...nConfigurableProductSetEditContentTest.xml | 1 + ...ConfigurableSetEditRelatedProductsTest.xml | 1 + ...ductsListWidgetConfigurableProductTest.xml | 1 + ...nDownloadableProductSetEditContentTest.xml | 1 + ...ductsListWidgetDownloadableProductTest.xml | 1 + .../AdminGroupedProductSetEditContentTest.xml | 1 + ...ewProductsListWidgetGroupedProductTest.xml | 1 + .../tests/_suite/WYSIWYGDisabledSuite.xml | 23 +++++++++++++++++++ 14 files changed, 37 insertions(+) create mode 100644 dev/tests/acceptance/tests/_suite/WYSIWYGDisabledSuite.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml index 02eaeba8d7e78..3ff203839cbf8 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-3343"/> <group value="Bundle"/> + <group value="WYSIWYGDisabled"/> </annotations> <after> <!-- Delete bundle product --> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml index 8c330ef9184a0..8efe32a7d84c0 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml @@ -17,6 +17,7 @@ <severity value="MAJOR"/> <testCaseId value="MC-123"/> <group value="Bundle"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml index 98c708b137657..b62a9e448f9c8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-3422"/> <group value="Catalog"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> <!--Admin Login--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml index bb8f76ebe7bf6..4131f5a1f128c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-3425"/> <group value="Catalog"/> + <group value="WYSIWYGDisabled"/> </annotations> <after> <!-- Delete virtual product --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetSimpleProductTest.xml index 66732454c2332..9ee56c02c7710 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetSimpleProductTest.xml @@ -17,6 +17,7 @@ <severity value="MAJOR"/> <testCaseId value="MC-104"/> <group value="Catalog"/> + <group value="WYSIWYGDisabled"/> </annotations> <!-- A Cms page containing the New Products Widget gets created here via extends --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetVirtualProductTest.xml index f17981404f349..a4e0d8708eb49 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsListWidgetVirtualProductTest.xml @@ -17,6 +17,8 @@ <severity value="MAJOR"/> <testCaseId value="MC-122"/> <group value="Catalog"/> + <group value="WYSIWYGDisabled"/> + </annotations> <!-- A Cms page containing the New Products Widget gets created here via extends --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml index 280d5c3cdb02f..07bed9e6e14de 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-3424"/> <group value="ConfigurableProduct"/> + <group value="WYSIWYGDisabled"/> </annotations> <after> <!-- Delete configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml index f8ac5bbd4781b..8f10848020e68 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-3414"/> <group value="ConfigurableProduct"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> <createData entity="ApiCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml index 8a00fe2413fe4..eadc7dadaf708 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml @@ -16,6 +16,7 @@ <severity value="MAJOR"/> <testCaseId value="MC-120"/> <group value="ConfigurableProduct"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml index 8153f16274de7..d82757cf48047 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-3426"/> <group value="Downloadable"/> + <group value="WYSIWYGDisabled"/> </annotations> <after> <!-- Delete downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml index f9a8a3bd135a6..4864d11c884bc 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml @@ -17,6 +17,7 @@ <severity value="MAJOR"/> <testCaseId value="MC-124"/> <group value="Downloadable"/> + <group value="WYSIWYGDisabled"/> </annotations> <!-- A Cms page containing the New Products Widget gets created here via extends --> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml index 0193e88e9597b..400a995b47eae 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-3423"/> <group value="GroupedProduct"/> + <group value="WYSIWYGDisabled"/> </annotations> <after> <!-- Delete grouped product --> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/NewProductsListWidgetGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/NewProductsListWidgetGroupedProductTest.xml index 96d64aa798265..a56512f84a9b8 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/NewProductsListWidgetGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/NewProductsListWidgetGroupedProductTest.xml @@ -17,6 +17,7 @@ <severity value="MAJOR"/> <testCaseId value="MC-121"/> <group value="GroupedProduct"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> diff --git a/dev/tests/acceptance/tests/_suite/WYSIWYGDisabledSuite.xml b/dev/tests/acceptance/tests/_suite/WYSIWYGDisabledSuite.xml new file mode 100644 index 0000000000000..65c2bb7004503 --- /dev/null +++ b/dev/tests/acceptance/tests/_suite/WYSIWYGDisabledSuite.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> + <suite name="WYSIWYGDisabledSuite"> + <before> + <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled disabled" /> + </before> + <include> + <group name="WYSIWYGDisabled"/> + </include> + <exclude> + <group name="skip"/> + </exclude> + <after> + <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled enabled" /> + </after> + </suite> +</suites> \ No newline at end of file From 382313475339543b5035e137e67ad0caa5eca439 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 14 Aug 2018 11:39:44 +0300 Subject: [PATCH 0998/1171] Covering Magento\Braintree\Model\InstantPurchase\CreditCard\TokenFormatter by UnitTest --- .../CreditCard/TokenFormatterTest.php | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/TokenFormatterTest.php diff --git a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/TokenFormatterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/TokenFormatterTest.php new file mode 100644 index 0000000000000..2a9cf2516dd2b --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/TokenFormatterTest.php @@ -0,0 +1,122 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Test\Unit\Model\InstantPurchase\CreditCard; + +use Magento\Braintree\Model\InstantPurchase\CreditCard\TokenFormatter as CreditCardTokenFormatter; +use Magento\Vault\Api\Data\PaymentTokenInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject; + +/** + * @covers CreditCardTokenFormatter + */ +class TokenFormatterTest extends TestCase +{ + /** + * @var PaymentTokenInterface|PHPUnit_Framework_MockObject_MockObject + */ + private $paymentTokenMock; + + /** + * @var CreditCardTokenFormatter + */ + private $creditCardTokenFormatter; + + /** + * @var array + */ + private $tokenDetails = [ + 'type' => 'visa', + 'maskedCC' => '1111************9999', + 'expirationDate' => '01-01-2020' + ]; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->paymentTokenMock = $this->getMockBuilder(PaymentTokenInterface::class) + ->getMockForAbstractClass(); + + $this->creditCardTokenFormatter = new CreditCardTokenFormatter(); + } + + /** + * Testing the payment format with a known credit card type + * + * @return void + */ + public function testFormatPaymentTokenWithKnownCardType() + { + $this->tokenDetails['type'] = key(CreditCardTokenFormatter::$baseCardTypes); + $this->paymentTokenMock->expects($this->once()) + ->method('getTokenDetails') + ->willReturn(json_encode($this->tokenDetails)); + + $formattedString = sprintf( + '%s: %s, %s: %s (%s: %s)', + __('Credit Card'), + reset(CreditCardTokenFormatter::$baseCardTypes), + __('ending'), + $this->tokenDetails['maskedCC'], + __('expires'), + $this->tokenDetails['expirationDate'] + ); + + self::assertEquals( + $formattedString, + $this->creditCardTokenFormatter->formatPaymentToken($this->paymentTokenMock) + ); + } + + /** + * Testing the payment format with a unknown credit card type + * + * @return void + */ + public function testFormatPaymentTokenWithUnknownCardType() + { + $this->paymentTokenMock->expects($this->once()) + ->method('getTokenDetails') + ->willReturn(json_encode($this->tokenDetails)); + + $formattedString = sprintf( + '%s: %s, %s: %s (%s: %s)', + __('Credit Card'), + $this->tokenDetails['type'], + __('ending'), + $this->tokenDetails['maskedCC'], + __('expires'), + $this->tokenDetails['expirationDate'] + ); + + self::assertEquals( + $formattedString, + $this->creditCardTokenFormatter->formatPaymentToken($this->paymentTokenMock) + ); + } + + /** + * Testing the payment format with wrong card data + * + * @return void + */ + public function testFormatPaymentTokenWithWrongData() + { + unset($this->tokenDetails['type']); + $this->paymentTokenMock->expects($this->once()) + ->method('getTokenDetails') + ->willReturn(json_encode($this->tokenDetails)); + self::expectException('\InvalidArgumentException'); + + $this->creditCardTokenFormatter->formatPaymentToken($this->paymentTokenMock); + } +} From b6df8e7c9046b0125fb3211217572dd26c7cbee7 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Sat, 11 Aug 2018 17:18:16 +0300 Subject: [PATCH 0999/1171] UI Component category_form - set default sort order for General fieldset Without the default sort order this fieldset goes to the very bottom after custom fieldset is added to the form --- .../Catalog/view/adminhtml/ui_component/category_form.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml index 537932a2d4daa..dafea71f872d0 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml @@ -42,7 +42,7 @@ </settings> </dataProvider> </dataSource> - <fieldset name="general"> + <fieldset name="general" sortOrder="5"> <settings> <collapsible>false</collapsible> <label/> From d60b3cfbd8172afcccadcdef5da1774bf8628d36 Mon Sep 17 00:00:00 2001 From: Madhu <madhumala.krishnan@leanswift.com> Date: Sat, 19 May 2018 15:06:11 +0530 Subject: [PATCH 1000/1171] 6305 Resolved product custom option title save issue --- .../Product/Option/Validator/DefaultValidator.php | 15 ++++++++++++++- .../Model/ResourceModel/Product/Option/Value.php | 3 ++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php index 1e5c7f76d829b..c6fb2243bed10 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php @@ -106,7 +106,9 @@ protected function isValidOptionTitle($title, $storeId) if ($storeId > \Magento\Store\Model\Store::DEFAULT_STORE_ID && $title === null) { return true; } - if ($this->isEmpty($title)) { + + // checking whether title is null and also changed is_empty to is_null + if ($this->isNull($title)) { return false; } @@ -168,4 +170,15 @@ protected function isNegative($value) { return intval($value) < 0; } + + /** + * check whether title is null + * + * @param $title + * @return bool + */ + protected function isNull($title) + { + return is_null($title); + } } 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 4ebcd1f4b9ae4..ce0a9b6e461ce 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php @@ -256,7 +256,8 @@ protected function _saveValueTitles(AbstractModel $object) $object->unsetData('title'); } - if ($object->getTitle()) { + /*** Checking whether title is not null ***/ + if ($object->getTitle()!= null) { if ($existInCurrentStore) { if ($storeId == $object->getStoreId()) { $where = [ From 71ce3fa2ee3487c6845286340c35937ec5083b47 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Sun, 20 May 2018 07:29:42 +0300 Subject: [PATCH 1001/1171] Update DefaultValidator.php --- .../Product/Option/Validator/DefaultValidator.php | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php index c6fb2243bed10..78a52f0b27e28 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php @@ -108,7 +108,7 @@ protected function isValidOptionTitle($title, $storeId) } // checking whether title is null and also changed is_empty to is_null - if ($this->isNull($title)) { + if ($title === null) { return false; } @@ -170,15 +170,4 @@ protected function isNegative($value) { return intval($value) < 0; } - - /** - * check whether title is null - * - * @param $title - * @return bool - */ - protected function isNull($title) - { - return is_null($title); - } } From 170d02962d126e314980bd2382efee3c5ffa653b Mon Sep 17 00:00:00 2001 From: Madhu <madhumala.krishnan@leanswift.com> Date: Thu, 24 May 2018 17:28:16 +0530 Subject: [PATCH 1002/1171] 2.2.6-dev-6305 fixed issue on displaying option title in invoice and shipment pdf --- .../Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php | 3 ++- .../Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php b/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php index bb6078e425900..69bec26768da0 100644 --- a/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php +++ b/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php @@ -127,7 +127,8 @@ public function draw() 'feed' => 35, ]; - if ($option['value']) { + // Checking if option value is null not as empty + if ($option['value']!= null) { if (isset($option['print_value'])) { $printValue = $option['print_value']; } else { diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php b/app/code/Magento/Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php index 0e6f345e19bc3..eb1bcc869ab74 100644 --- a/app/code/Magento/Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php +++ b/app/code/Magento/Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php @@ -89,7 +89,8 @@ public function draw() ]; // draw options value - if ($option['value']) { + // Checking if option value is null not as empty + if ($option['value']!= null) { $printValue = isset( $option['print_value'] ) ? $option['print_value'] : $this->filterManager->stripTags( From 6aebfe8c6007a71a5b2601e8050c1405b2685231 Mon Sep 17 00:00:00 2001 From: Madhu <madhumala.krishnan@leanswift.com> Date: Fri, 25 May 2018 10:49:50 +0530 Subject: [PATCH 1003/1171] 2.2.6-dev-6305 changes comment and repushed again for pdf issue --- .../Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php | 2 +- .../Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php b/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php index 69bec26768da0..7f5d880915cfc 100644 --- a/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php +++ b/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php @@ -127,7 +127,7 @@ public function draw() 'feed' => 35, ]; - // Checking if option value is null not as empty + // Checking whether option value is not null if ($option['value']!= null) { if (isset($option['print_value'])) { $printValue = $option['print_value']; diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php b/app/code/Magento/Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php index eb1bcc869ab74..433a4b7e314f4 100644 --- a/app/code/Magento/Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php +++ b/app/code/Magento/Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php @@ -89,7 +89,6 @@ public function draw() ]; // draw options value - // Checking if option value is null not as empty if ($option['value']!= null) { $printValue = isset( $option['print_value'] From c7bd9096e1160fe54fa90905c643bb1633cdb7bc Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Tue, 14 Aug 2018 13:51:02 -0500 Subject: [PATCH 1004/1171] MAGETWO-91678: Minimum Order amount required in Admin orders --- app/code/Magento/Quote/etc/adminhtml/di.xml | 1 - app/code/Magento/Quote/etc/di.xml | 1 + .../ActionGroup/AdminOrderActionGroup.xml | 13 +++ .../Sales/Test/Mftf/Data/SalesConfigData.xml | 27 +++++++ .../Test/Mftf/Metadata/sales_config-meta.xml | 23 ++++++ ...reateOrderWithMinimumAmountEnabledTest.xml | 79 +++++++++++++++++++ 6 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Sales/Test/Mftf/Data/SalesConfigData.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Metadata/sales_config-meta.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml diff --git a/app/code/Magento/Quote/etc/adminhtml/di.xml b/app/code/Magento/Quote/etc/adminhtml/di.xml index df21430cf06ad..08e87c7db9ec7 100644 --- a/app/code/Magento/Quote/etc/adminhtml/di.xml +++ b/app/code/Magento/Quote/etc/adminhtml/di.xml @@ -14,7 +14,6 @@ <item name="ShippingMethodValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\ShippingMethodValidationRule</item> <item name="BillingAddressValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\BillingAddressValidationRule</item> <item name="PaymentMethodValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\PaymentMethodValidationRule</item> - <item name="MinimumAmountValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\MinimumAmountValidationRule</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml index b7a15fe138c33..b8da73b91d043 100644 --- a/app/code/Magento/Quote/etc/di.xml +++ b/app/code/Magento/Quote/etc/di.xml @@ -104,6 +104,7 @@ <item name="ShippingMethodValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\ShippingMethodValidationRule</item> <item name="BillingAddressValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\BillingAddressValidationRule</item> <item name="PaymentMethodValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\PaymentMethodValidationRule</item> + <item name="MinimumAmountValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\MinimumAmountValidationRule</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index 7ce10d5e5424e..aa94b56e83245 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -22,6 +22,19 @@ <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> </actionGroup> + <!--Navigate to create order page (New Order -> Create New Customer)--> + <actionGroup name="navigateToNewOrderPageNewCustomerSingleStore"> + <arguments> + <argument name="storeView" defaultValue="_defaultStore"/> + </arguments> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> + <waitForPageLoad stepKey="waitForIndexPageLoad"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> + <click selector="{{AdminOrdersGridSection.createNewOrder}}" stepKey="clickCreateNewOrder"/> + <click selector="{{AdminOrderFormActionSection.CreateNewCustomer}}" stepKey="clickCreateCustomer"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> + </actionGroup> + <!--Navigate to create order page (New Order -> Select Customer)--> <actionGroup name="navigateToNewOrderPageExistingCustomer"> <arguments> diff --git a/app/code/Magento/Sales/Test/Mftf/Data/SalesConfigData.xml b/app/code/Magento/Sales/Test/Mftf/Data/SalesConfigData.xml new file mode 100644 index 0000000000000..c9c6d19173689 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Data/SalesConfigData.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="EnabledMinimumOrderAmount" type="sales_minimum_order"> + <requiredEntity type="active">EnableMinimumOrderCheck</requiredEntity> + <requiredEntity type="amount">MinimumOrderAmount</requiredEntity> + </entity> + <entity name="EnableMinimumOrderCheck" type="active"> + <data key="value">1</data> + </entity> + <entity name="MinimumOrderAmount" type="amount"> + <data key="value">500</data> + </entity> + + <entity name="DisabledMinimumOrderAmount" type="sales_minimum_order"> + <requiredEntity type="active">DisableMinimumOrderCheck</requiredEntity> + </entity> + <entity name="DisableMinimumOrderCheck" type="active"> + <data key="value">0</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Metadata/sales_config-meta.xml b/app/code/Magento/Sales/Test/Mftf/Metadata/sales_config-meta.xml new file mode 100644 index 0000000000000..2bd084b5457b8 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Metadata/sales_config-meta.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="SetSalesMinimumOrder" dataType="sales_minimum_order" type="create" auth="adminFormKey" url="/admin/system_config/save/section/sales/" method="POST"> + <object key="groups" dataType="sales_minimum_order"> + <object key="minimum_order" dataType="sales_minimum_order"> + <object key="fields" dataType="sales_minimum_order"> + <object key="active" dataType="active"> + <field key="value">string</field> + </object> + <object key="amount" dataType="amount"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> +</operations> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml new file mode 100644 index 0000000000000..420c54eca63a0 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml @@ -0,0 +1,79 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateOrderWithMinimumAmountEnabledTest"> + <annotations> + <features value="Sales"/> + <stories value="Admin create order"/> + <title value="Admin order is not restricted by 'Minimum Order Amount' configuration."/> + <description value="Admin should be able to create an order regardless of the minimum order amount."/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-92925"/> + <group value="sales"/> + </annotations> + <before> + <createData entity="EnabledMinimumOrderAmount" stepKey="enableMinimumOrderAmount"/> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="ClearCacheActionGroup" stepKey="clearCacheBefore"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <createData entity="DisabledMinimumOrderAmount" stepKey="disableMinimumOrderAmount"/> + <actionGroup ref="ClearCacheActionGroup" stepKey="clearCacheAfter"/> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!--Admin creates order--> + <comment userInput="Admin creates order" stepKey="adminCreateOrder"/> + <actionGroup ref="navigateToNewOrderPageNewCustomerSingleStore" stepKey="navigateToNewOrderPage"/> + + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder"> + <argument name="product" value="SimpleProduct"/> + </actionGroup> + + <!--Fill customer group information--> + <selectOption selector="{{AdminOrderFormAccountSection.group}}" userInput="{{GeneralCustomerGroup.code}}" stepKey="selectGroup"/> + <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillEmail"/> + + <!--Fill customer address information--> + <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress"> + <argument name="customer" value="Simple_US_Customer"/> + <argument name="address" value="US_Address_TX"/> + </actionGroup> + + <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping"/> + + <!--Verify totals on Order page--> + <see selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" userInput="${{AdminOrderSimpleProduct.subtotal}}" stepKey="seeOrderSubTotal"/> + <see selector="{{AdminOrderFormTotalSection.total('Shipping')}}" userInput="${{AdminOrderSimpleProduct.shipping}}" stepKey="seeOrderShipping"/> + <see selector="{{AdminOrderFormTotalSection.grandTotal}}" userInput="${{AdminOrderSimpleProduct.grandTotal}}" stepKey="seeCorrectGrandTotal"/> + + <!--Submit Order and verify information--> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder"/> + <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPage"/> + + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeOrderPendingStatus"/> + <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> + <assertNotEmpty actual="$orderId" stepKey="assertOrderIdIsNotEmpty"/> + <actionGroup ref="verifyBasicOrderInformation" stepKey="verifyOrderInformation"> + <argument name="customer" value="Simple_US_Customer"/> + <argument name="shippingAddress" value="US_Address_TX"/> + <argument name="billingAddress" value="US_Address_TX"/> + </actionGroup> + <actionGroup ref="seeProductInItemsOrdered" stepKey="seeSimpleProductInItemsOrdered"> + <argument name="product" value="SimpleProduct"/> + </actionGroup> + </test> +</tests> \ No newline at end of file From 43bbdb5730adbe56d161e38e236035c4af0a2c2f Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Sat, 11 Aug 2018 23:41:19 +0200 Subject: [PATCH 1005/1171] Link logo in web setup wizard to back-end base URL When you get to the web setup wizard from the Magento back-end it was not possible to get back to the back-end from this page. This commit makes the logo clickable and when you click it you will return the Magento back-end. --- .../web/app/setup/styles/less/pages/_common.less | 6 ------ setup/pub/styles/setup.css | 2 +- setup/view/magento/setup/navigation/side-menu.phtml | 13 ++++++++++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/pages/_common.less b/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/pages/_common.less index b71c94f2917c8..2b33d1fa542b6 100644 --- a/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/pages/_common.less +++ b/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/pages/_common.less @@ -19,12 +19,6 @@ padding-top: @main__indent-top; } -.menu-wrapper { - .logo-static { - pointer-events: none; - } -} - // // Header // _____________________________________________ diff --git a/setup/pub/styles/setup.css b/setup/pub/styles/setup.css index 13dc7b2a043d2..f290b155fb553 100644 --- a/setup/pub/styles/setup.css +++ b/setup/pub/styles/setup.css @@ -3,4 +3,4 @@ * 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:'\e612'}.admin__menu .item-module>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}} +.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:'\e612'}.admin__menu .item-module>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}.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/view/magento/setup/navigation/side-menu.phtml b/setup/view/magento/setup/navigation/side-menu.phtml index 58d12a4de448a..6354af875ae8c 100644 --- a/setup/view/magento/setup/navigation/side-menu.phtml +++ b/setup/view/magento/setup/navigation/side-menu.phtml @@ -6,6 +6,13 @@ // @codingStandardsIgnoreFile +use Magento\Backend\Model\UrlInterface; +use Magento\Framework\App\ObjectManager; + +$objectManager = ObjectManager::getInstance(); +/** @var Magento\Backend\Model\UrlInterface $backendUrl */ +$backendUrl = $objectManager->get(UrlInterface::class); + ?> <?php $expressions = []; foreach ( $this->main as $item ): ?> <?php $expressions[] = '!$state.is(\'' . $item['id'] . '\')'; @@ -20,13 +27,13 @@ ng-show="<?= implode( '&&', $expressions) ?>" > <nav class="admin__menu" ng-controller="mainController"> - <span - class="logo logo-static" + <a href="<?= $backendUrl->getBaseUrl() . $backendUrl->getAreaFrontName(); ?>" + class="logo" data-edition="Community Edition"> <img class="logo-img" src="./pub/images/logo.svg" alt="Magento Admin Panel"> - </span> + </a> <ul id="nav" role="menubar"> <li class="item-home level-0" ng-class="{_active: $state.current.name === 'root.home'}"> <a href="" ui-sref="root.home"> From d621327756d3f9cc37275194410673fb84a006f9 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 14 Aug 2018 15:44:33 -0500 Subject: [PATCH 1006/1171] MAGETWO-90531: WYSIWYG shows special character button in toolbar on product page - when test module is enabled - add dummy js module --- .../TestModuleWysiwygConfig/etc/adminhtml/di.xml | 4 ++-- .../Magento/TestModuleWysiwygConfig/etc/di.xml | 2 +- .../web/wysiwyg/tiny_mce/tinymce4TestAdapter.js | 14 ++++++++++++++ .../Magento/Cms/Model/Wysiwyg/ConfigTest.php | 2 +- 4 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/view/adminhtml/web/wysiwyg/tiny_mce/tinymce4TestAdapter.js diff --git a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml index afbc603caa8b5..cd5b0e4df7fe9 100644 --- a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml +++ b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml @@ -12,7 +12,7 @@ <item name="testAdapter" xsi:type="string">Magento\TestModuleWysiwygConfig\Model\Config</item> </argument> <argument name="wysiwygConfigPostProcessor" xsi:type="array"> - <item name="mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter" xsi:type="string">Magento\TestModuleWysiwygConfig\Model\Config</item> + <item name="Magento_TestModuleWysiwygConfig/wysiwyg/tiny_mce/tinymce4TestAdapter" xsi:type="string">Magento\TestModuleWysiwygConfig\Model\Config</item> </argument> <argument name="variablePluginConfigProvider" xsi:type="array"> <item name="testAdapter" xsi:type="string">Magento\Variable\Model\Variable\ConfigProvider</item> @@ -29,7 +29,7 @@ <arguments> <argument name="adapterOptions" xsi:type="array"> <item name="testAdapter" xsi:type="array"> - <item name="value" xsi:type="string">mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter</item> + <item name="value" xsi:type="string">Magento_TestModuleWysiwygConfig/wysiwyg/tiny_mce/tinymce4TestAdapter</item> <item name="label" xsi:type="string" translatable="true">Test Adapter</item> </item> </argument> diff --git a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/di.xml b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/di.xml index cd9710db45b45..db21145491d83 100644 --- a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/di.xml +++ b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/di.xml @@ -9,7 +9,7 @@ <type name="Magento\Ui\Block\Wysiwyg\ActiveEditor"> <arguments> <argument name="availableAdapterPaths" xsi:type="array"> - <item name="testAdapter" xsi:type="string"/> + <item name="Magento_TestModuleWysiwygConfig/wysiwyg/tiny_mce/tinymce4TestAdapter" xsi:type="string"/> </argument> </arguments> </type> diff --git a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/view/adminhtml/web/wysiwyg/tiny_mce/tinymce4TestAdapter.js b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/view/adminhtml/web/wysiwyg/tiny_mce/tinymce4TestAdapter.js new file mode 100644 index 0000000000000..005e6437c2d78 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/view/adminhtml/web/wysiwyg/tiny_mce/tinymce4TestAdapter.js @@ -0,0 +1,14 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/* global popups, tinyMceEditors, MediabrowserUtility, Base64 */ +/* eslint-disable strict */ +define([ + 'mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter' +], function (tinyMCE4) { + 'use strict'; + + return tinyMCE4; +}); diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php index 498fc044faacf..431c9383a8c74 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php @@ -63,7 +63,7 @@ public function testGetConfigCssUrls() * * @return void * - * @magentoConfigFixture default/cms/wysiwyg/editor testAdapter + * @magentoConfigFixture default/cms/wysiwyg/editor Magento_TestModuleWysiwygConfig/wysiwyg/tiny_mce/tinymce4TestAdapter */ public function testTestModuleEnabledModuleIsAbleToModifyConfig() { From 840af3a7036de4962522d5130bb05dfc9af96e68 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Tue, 14 Aug 2018 16:58:46 -0500 Subject: [PATCH 1007/1171] MAGETWO-93666: Wrong backup file is getting deleted when user has DB, DB/Media and System backup files and tries to delete System. --- .../Magento/Backup/Model/BackupFactory.php | 4 +- .../Magento/Backup/Model/Fs/Collection.php | 3 +- .../ActionGroup/CreateBackupActionGroup.xml | 54 +++++++++++++++++++ .../ActionGroup/DeleteBackupActionGroup.xml | 25 +++++++++ .../Backup/Test/Mftf/Data/BackupData.xml | 23 ++++++++ .../Backup/Test/Mftf/Page/BackupIndexPage.xml | 16 ++++++ .../Section/AdminCreateBackupFormSection.xml | 18 +++++++ .../Mftf/Section/AdminGridActionSection.xml | 15 ++++++ .../Mftf/Section/AdminGridTableSection.xml | 16 ++++++ .../Mftf/Section/AdminMainActionsSection.xml | 16 ++++++ .../Test/AdminCreateAndDeleteBackupsTest.xml | 54 +++++++++++++++++++ .../Test/Unit/Model/BackupFactoryTest.php | 4 +- 12 files changed, 243 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateBackupActionGroup.xml create mode 100644 app/code/Magento/Backup/Test/Mftf/ActionGroup/DeleteBackupActionGroup.xml create mode 100644 app/code/Magento/Backup/Test/Mftf/Data/BackupData.xml create mode 100644 app/code/Magento/Backup/Test/Mftf/Page/BackupIndexPage.xml create mode 100644 app/code/Magento/Backup/Test/Mftf/Section/AdminCreateBackupFormSection.xml create mode 100644 app/code/Magento/Backup/Test/Mftf/Section/AdminGridActionSection.xml create mode 100644 app/code/Magento/Backup/Test/Mftf/Section/AdminGridTableSection.xml create mode 100644 app/code/Magento/Backup/Test/Mftf/Section/AdminMainActionsSection.xml create mode 100644 app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml diff --git a/app/code/Magento/Backup/Model/BackupFactory.php b/app/code/Magento/Backup/Model/BackupFactory.php index 28b0a7baf43cb..f88b410a2371b 100644 --- a/app/code/Magento/Backup/Model/BackupFactory.php +++ b/app/code/Magento/Backup/Model/BackupFactory.php @@ -39,8 +39,8 @@ public function __construct(\Magento\Framework\ObjectManagerInterface $objectMan */ public function create($timestamp, $type) { - $fsCollection = $this->_objectManager->get(\Magento\Backup\Model\Fs\Collection::class); - $backupInstance = $this->_objectManager->get(\Magento\Backup\Model\Backup::class); + $fsCollection = $this->_objectManager->create(\Magento\Backup\Model\Fs\Collection::class); + $backupInstance = $this->_objectManager->create(\Magento\Backup\Model\Backup::class); foreach ($fsCollection as $backup) { if ($backup->getTime() === (int) $timestamp && $backup->getType() === $type) { diff --git a/app/code/Magento/Backup/Model/Fs/Collection.php b/app/code/Magento/Backup/Model/Fs/Collection.php index 2d08ac04528ac..b17c17f7074fb 100644 --- a/app/code/Magento/Backup/Model/Fs/Collection.php +++ b/app/code/Magento/Backup/Model/Fs/Collection.php @@ -115,7 +115,8 @@ protected function _generateRow($filename) if (isset($row['display_name']) && $row['display_name'] == '') { $row['display_name'] = 'WebSetupWizard'; } - $row['id'] = $row['time'] . '_' . $row['type'] . (isset($row['display_name']) ? $row['display_name'] : ''); + $row['id'] = $row['time'] . '_' . $row['type'] + . (isset($row['display_name']) ? '_' . $row['display_name'] : ''); return $row; } } diff --git a/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateBackupActionGroup.xml b/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateBackupActionGroup.xml new file mode 100644 index 0000000000000..89381112e6c66 --- /dev/null +++ b/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateBackupActionGroup.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + + <actionGroup name="createSystemBackup"> + <arguments> + <argument name="backup" defaultValue="SystemBackup"/> + </arguments> + <click selector="{{AdminMainActionsSection.systemBackup}}" stepKey="clickCreateBackupButton"/> + <waitForElementVisible selector="{{AdminCreateBackupFormSection.backupNameField}}" stepKey="waitForForm"/> + <fillField selector="{{AdminCreateBackupFormSection.backupNameField}}" userInput="{{backup.name}}" stepKey="fillBackupName"/> + <click selector="{{AdminCreateBackupFormSection.ok}}" stepKey="clickOk"/> + <waitForElementNotVisible selector=".loading-mask" time="300" stepKey="waitForBackupProcess"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You created the system backup." stepKey="seeSuccessMessage"/> + <see selector="{{AdminGridTableSection.backupNameColumn}}" userInput="{{backup.name}}" stepKey="seeBackupInGrid"/> + <see selector="{{AdminGridTableSection.backupTypeByName(backup.name)}}" userInput="{{backup.type}}" stepKey="seeBackupType"/> + </actionGroup> + + <actionGroup name="createMediaBackup"> + <arguments> + <argument name="backup" defaultValue="MediaBackup"/> + </arguments> + <click selector="{{AdminMainActionsSection.mediaBackup}}" stepKey="clickCreateBackupButton"/> + <waitForElementVisible selector="{{AdminCreateBackupFormSection.backupNameField}}" stepKey="waitForForm"/> + <fillField selector="{{AdminCreateBackupFormSection.backupNameField}}" userInput="{{backup.name}}" stepKey="fillBackupName"/> + <click selector="{{AdminCreateBackupFormSection.ok}}" stepKey="clickOk"/> + <waitForPageLoad time="120" stepKey="waitForBackupProcess"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You created the database and media backup." stepKey="seeSuccessMessage"/> + <see selector="{{AdminGridTableSection.backupNameColumn}}" userInput="{{backup.name}}" stepKey="seeBackupInGrid"/> + <see selector="{{AdminGridTableSection.backupTypeByName(backup.name)}}" userInput="{{backup.type}}" stepKey="seeBackupType"/> + </actionGroup> + + <actionGroup name="createDatabaseBackup"> + <arguments> + <argument name="backup" defaultValue="DatabaseBackup"/> + </arguments> + <click selector="{{AdminMainActionsSection.databaseBackup}}" stepKey="clickCreateBackupButton"/> + <waitForElementVisible selector="{{AdminCreateBackupFormSection.backupNameField}}" stepKey="waitForForm"/> + <fillField selector="{{AdminCreateBackupFormSection.backupNameField}}" userInput="{{backup.name}}" stepKey="fillBackupName"/> + <click selector="{{AdminCreateBackupFormSection.ok}}" stepKey="clickOk"/> + <waitForPageLoad time="120" stepKey="waitForBackupProcess"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You created the database backup." stepKey="seeSuccessMessage"/> + <see selector="{{AdminGridTableSection.backupNameColumn}}" userInput="{{backup.name}}" stepKey="seeBackupInGrid"/> + <see selector="{{AdminGridTableSection.backupTypeByName(backup.name)}}" userInput="{{backup.type}}" stepKey="seeBackupType"/> + </actionGroup> + +</actionGroups> diff --git a/app/code/Magento/Backup/Test/Mftf/ActionGroup/DeleteBackupActionGroup.xml b/app/code/Magento/Backup/Test/Mftf/ActionGroup/DeleteBackupActionGroup.xml new file mode 100644 index 0000000000000..4f34f24c3a806 --- /dev/null +++ b/app/code/Magento/Backup/Test/Mftf/ActionGroup/DeleteBackupActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + + <actionGroup name="deleteBackup"> + <arguments> + <argument name="backup"/> + </arguments> + <see selector="{{AdminGridTableSection.backupNameColumn}}" userInput="{{backup.name}}" stepKey="seeBackupInGrid"/> + <click selector="{{AdminGridTableSection.backupRowCheckbox(backup.name)}}" stepKey="selectBackupRow"/> + <selectOption selector="{{AdminGridActionSection.actionSelect}}" userInput="Delete" stepKey="selectDeleteAction"/> + <click selector="{{AdminGridActionSection.submitButton}}" stepKey="clickSubmit"/> + <see selector="{{AdminConfirmationModalSection.message}}" userInput="Are you sure you want to delete the selected backup(s)?" stepKey="seeConfirmationModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="clickOkConfirmDelete"/> + <dontSee selector="{{AdminGridTableSection.backupNameColumn}}" userInput="{{backup.name}}" stepKey="dontSeeBackupInGrid"/> + </actionGroup> + +</actionGroups> diff --git a/app/code/Magento/Backup/Test/Mftf/Data/BackupData.xml b/app/code/Magento/Backup/Test/Mftf/Data/BackupData.xml new file mode 100644 index 0000000000000..ae97351cafcaf --- /dev/null +++ b/app/code/Magento/Backup/Test/Mftf/Data/BackupData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SystemBackup" type="backup"> + <data key="name" unique="suffix">systemBackup</data> + <data key="type">System</data> + </entity> + <entity name="MediaBackup" type="backup"> + <data key="name" unique="suffix">mediaBackup</data> + <data key="type">Database and Media</data> + </entity> + <entity name="DatabaseBackup" type="backup"> + <data key="name" unique="suffix">databaseBackup</data> + <data key="type">Database</data> + </entity> +</entities> diff --git a/app/code/Magento/Backup/Test/Mftf/Page/BackupIndexPage.xml b/app/code/Magento/Backup/Test/Mftf/Page/BackupIndexPage.xml new file mode 100644 index 0000000000000..aad29e6e6d566 --- /dev/null +++ b/app/code/Magento/Backup/Test/Mftf/Page/BackupIndexPage.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="BackupIndexPage" url="/backup/index/" area="admin" module="Magento_Backup"> + <section name="AdminMainActionsSection"/> + <section name="AdminGridTableSection"/> + <section name="AdminCreateBackupFormSection"/> + </page> +</pages> diff --git a/app/code/Magento/Backup/Test/Mftf/Section/AdminCreateBackupFormSection.xml b/app/code/Magento/Backup/Test/Mftf/Section/AdminCreateBackupFormSection.xml new file mode 100644 index 0000000000000..af88146bcfb4b --- /dev/null +++ b/app/code/Magento/Backup/Test/Mftf/Section/AdminCreateBackupFormSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCreateBackupFormSection"> + <element name="backupNameField" type="input" selector="input[name='backup_name']"/> + <element name="maintenanceModeCheckbox" type="checkbox" selector="input[name='maintenance_mode']"/> + <element name="excludeMediaCheckbox" type="checkbox" selector="input[name='exclude_media']"/> + <element name="ok" type="button" selector=".modal-header button.primary"/> + <element name="cancel" type="button" selector=".modal-header button.cancel"/> + </section> +</sections> diff --git a/app/code/Magento/Backup/Test/Mftf/Section/AdminGridActionSection.xml b/app/code/Magento/Backup/Test/Mftf/Section/AdminGridActionSection.xml new file mode 100644 index 0000000000000..cca6b428a2820 --- /dev/null +++ b/app/code/Magento/Backup/Test/Mftf/Section/AdminGridActionSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridActionSection"> + <element name="actionSelect" type="select" selector="#backupsGrid_massaction-select"/> + <element name="submitButton" type="button" selector="#backupsGrid_massaction button[title='Submit']"/> + </section> +</sections> diff --git a/app/code/Magento/Backup/Test/Mftf/Section/AdminGridTableSection.xml b/app/code/Magento/Backup/Test/Mftf/Section/AdminGridTableSection.xml new file mode 100644 index 0000000000000..72fb51684931f --- /dev/null +++ b/app/code/Magento/Backup/Test/Mftf/Section/AdminGridTableSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridTableSection"> + <element name="backupNameColumn" type="text" selector="table.data-grid td[data-column='display_name']"/> + <element name="backupTypeByName" type="text" selector="//table//td[contains(., '{{name}}')]/parent::tr/td[@data-column='type']" parameterized="true"/> + <element name="backupRowCheckbox" type="checkbox" selector="//table//td[contains(., '{{name}}')]/parent::tr/td[@data-column='massaction']//input" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Backup/Test/Mftf/Section/AdminMainActionsSection.xml b/app/code/Magento/Backup/Test/Mftf/Section/AdminMainActionsSection.xml new file mode 100644 index 0000000000000..11ba79f32b5db --- /dev/null +++ b/app/code/Magento/Backup/Test/Mftf/Section/AdminMainActionsSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminMainActionsSection"> + <element name="systemBackup" type="button" selector="button[data-ui-id*='createsnapshotbutton']"/> + <element name="mediaBackup" type="button" selector="button[data-ui-id*='createmediabackupbutton']"/> + <element name="databaseBackup" type="button" selector="button.database-backup[data-ui-id*='createbutton']"/> + </section> +</sections> diff --git a/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml b/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml new file mode 100644 index 0000000000000..a1b0f5197eeb9 --- /dev/null +++ b/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateAndDeleteBackupsTest"> + <annotations> + <features value="Backup"/> + <stories value="Create and delete backups"/> + <title value="Create and delete backups"/> + <description value="An admin user can create a backup of each type and delete each backup."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-94176"/> + <group value="backup"/> + </annotations> + + <!--Login to admin area--> + <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + + <!--Go to backup index page--> + <amOnPage url="{{BackupIndexPage.url}}" stepKey="goToBackupPage"/> + <waitForPageLoad stepKey="waitForBackupPage"/> + + <!--Create system backup--> + <actionGroup ref="createSystemBackup" stepKey="createSystemBackup"/> + + <!--Create database/media backup--> + <actionGroup ref="createMediaBackup" stepKey="createMediaBackup"/> + + <!--Create database backup--> + <actionGroup ref="createDatabaseBackup" stepKey="createDatabaseBackup"/> + + <!--Delete system backup--> + <actionGroup ref="deleteBackup" stepKey="deleteSystemBackup"> + <argument name="backup" value="SystemBackup"/> + </actionGroup> + + <!--Delete database/media backup--> + <actionGroup ref="deleteBackup" stepKey="deleteMediaBackup"> + <argument name="backup" value="MediaBackup"/> + </actionGroup> + + <!--Delete database backup--> + <actionGroup ref="deleteBackup" stepKey="deleteDatabaseBackup"> + <argument name="backup" value="DatabaseBackup"/> + </actionGroup> + + </test> +</tests> diff --git a/app/code/Magento/Backup/Test/Unit/Model/BackupFactoryTest.php b/app/code/Magento/Backup/Test/Unit/Model/BackupFactoryTest.php index 629028bfd6f15..abf5e63276afa 100644 --- a/app/code/Magento/Backup/Test/Unit/Model/BackupFactoryTest.php +++ b/app/code/Magento/Backup/Test/Unit/Model/BackupFactoryTest.php @@ -56,7 +56,7 @@ protected function setUp() $this->_objectManager->expects( $this->at(0) )->method( - 'get' + 'create' )->with( \Magento\Backup\Model\Fs\Collection::class )->will( @@ -65,7 +65,7 @@ protected function setUp() $this->_objectManager->expects( $this->at(1) )->method( - 'get' + 'create' )->with( \Magento\Backup\Model\Backup::class )->will( From 9697e0d39e41f1ab35a60c6bfc9b7fdc4e582b8f Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 15 Aug 2018 09:39:24 +0300 Subject: [PATCH 1008/1171] Fixed static test failure --- .../Model/InstantPurchase/CreditCard/TokenFormatterTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/TokenFormatterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/TokenFormatterTest.php index 2a9cf2516dd2b..a5c7cd743d85f 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/TokenFormatterTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/CreditCard/TokenFormatterTest.php @@ -12,9 +12,6 @@ use PHPUnit\Framework\TestCase; use PHPUnit_Framework_MockObject_MockObject; -/** - * @covers CreditCardTokenFormatter - */ class TokenFormatterTest extends TestCase { /** From fce92ae9d06c2ff73a6e9dc4a37c704930522018 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Wed, 15 Aug 2018 11:04:56 +0300 Subject: [PATCH 1009/1171] MAGETWO-93276: [2.3] Downloadable product links removal on update via API --- .../Catalog/Model/ProductRepository.php | 123 +-- .../Test/Unit/Model/ProductRepositoryTest.php | 713 +++++++++--------- .../Api/ProductRepositoryInterfaceTest.php | 28 + 3 files changed, 469 insertions(+), 395 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index 4c0122694285d..a94a803031120 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -4,12 +4,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Model; +use Magento\Catalog\Api\Data\ProductExtension; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product\Gallery\MimeTypeExtensionMap; use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Eav\Model\Entity\Attribute\Exception as AttributeException; use Magento\Framework\Api\Data\ImageContentInterface; use Magento\Framework\Api\Data\ImageContentInterfaceFactory; use Magento\Framework\Api\ImageContentValidatorInterface; @@ -22,6 +25,7 @@ use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\TemporaryState\CouldNotSaveException as TemporaryCouldNotSaveException; use Magento\Framework\Exception\StateException; use Magento\Framework\Exception\ValidatorException; @@ -306,10 +310,10 @@ protected function getCacheKey($data) * Add product to internal cache and truncate cache if it has more than cacheLimit elements. * * @param string $cacheKey - * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param ProductInterface $product * @return void */ - private function cacheProduct($cacheKey, \Magento\Catalog\Api\Data\ProductInterface $product) + private function cacheProduct($cacheKey, ProductInterface $product) { $this->instancesById[$product->getId()][$cacheKey] = $product; $this->saveProductInLocalCache($product, $cacheKey); @@ -326,7 +330,7 @@ private function cacheProduct($cacheKey, \Magento\Catalog\Api\Data\ProductInterf * * @param array $productData * @param bool $createNew - * @return \Magento\Catalog\Api\Data\ProductInterface|Product + * @return ProductInterface|Product * @throws NoSuchEntityException */ protected function initializeProductData(array $productData, $createNew) @@ -414,12 +418,12 @@ protected function processNewMediaGalleryEntry( /** * Process product links, creating new links, updating and deleting existing links * - * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param ProductInterface $product * @param \Magento\Catalog\Api\Data\ProductLinkInterface[] $newLinks * @return $this * @throws NoSuchEntityException */ - private function processLinks(\Magento\Catalog\Api\Data\ProductInterface $product, $newLinks) + private function processLinks(ProductInterface $product, $newLinks) { if ($newLinks === null) { // If product links were not specified, don't do anything @@ -547,11 +551,11 @@ protected function processMediaGallery(ProductInterface $product, $mediaGalleryE } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ - public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveOptions = false) + public function save(ProductInterface $product, $saveOptions = false) { $tierPrices = $product->getData('tier_price'); @@ -565,12 +569,18 @@ public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveO if (!$product->hasData(Product::STATUS)) { $product->setStatus($existingProduct->getStatus()); } + + /** @var ProductExtension $extensionAttributes */ + $extensionAttributes = $product->getExtensionAttributes(); + if (empty($extensionAttributes->__toArray())) { + $product->setExtensionAttributes($existingProduct->getExtensionAttributes()); + } } catch (NoSuchEntityException $e) { $existingProduct = null; } $productDataArray = $this->extensibleDataObjectConverter - ->toNestedArray($product, [], \Magento\Catalog\Api\Data\ProductInterface::class); + ->toNestedArray($product, [], ProductInterface::class); $productDataArray = array_replace($productDataArray, $product->getData()); $ignoreLinksFlag = $product->getData('ignore_links_flag'); $productLinks = null; @@ -596,47 +606,11 @@ public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveO ); } - try { - if ($tierPrices !== null) { - $product->setData('tier_price', $tierPrices); - } - $this->removeProductFromLocalCache($product->getSku()); - unset($this->instancesById[$product->getId()]); - $this->resourceModel->save($product); - } catch (ConnectionException $exception) { - throw new \Magento\Framework\Exception\TemporaryState\CouldNotSaveException( - __('Database connection error'), - $exception, - $exception->getCode() - ); - } catch (DeadlockException $exception) { - throw new \Magento\Framework\Exception\TemporaryState\CouldNotSaveException( - __('Database deadlock found when trying to get lock'), - $exception, - $exception->getCode() - ); - } catch (LockWaitException $exception) { - throw new \Magento\Framework\Exception\TemporaryState\CouldNotSaveException( - __('Database lock wait timeout exceeded'), - $exception, - $exception->getCode() - ); - } catch (\Magento\Eav\Model\Entity\Attribute\Exception $exception) { - throw \Magento\Framework\Exception\InputException::invalidFieldValue( - $exception->getAttributeCode(), - $product->getData($exception->getAttributeCode()), - $exception - ); - } catch (ValidatorException $e) { - throw new CouldNotSaveException(__($e->getMessage())); - } catch (LocalizedException $e) { - throw $e; - } catch (\Exception $e) { - throw new \Magento\Framework\Exception\CouldNotSaveException( - __('The product was unable to be saved. Please try again.'), - $e - ); + if ($tierPrices !== null) { + $product->setData('tier_price', $tierPrices); } + + $this->saveProduct($product); $this->removeProductFromLocalCache($product->getSku()); unset($this->instancesById[$product->getId()]); @@ -646,7 +620,7 @@ public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveO /** * {@inheritdoc} */ - public function delete(\Magento\Catalog\Api\Data\ProductInterface $product) + public function delete(ProductInterface $product) { $sku = $product->getSku(); $productId = $product->getId(); @@ -835,4 +809,55 @@ private function prepareSku(string $sku): string { return mb_strtolower(trim($sku)); } + + /** + * Save product resource model. + * + * @param ProductInterface|Product $product + * @throws TemporaryCouldNotSaveException + * @throws InputException + * @throws CouldNotSaveException + * @throws LocalizedException + */ + private function saveProduct($product): void + { + try { + $this->removeProductFromLocalCache($product->getSku()); + unset($this->instancesById[$product->getId()]); + $this->resourceModel->save($product); + } catch (ConnectionException $exception) { + throw new TemporaryCouldNotSaveException( + __('Database connection error'), + $exception, + $exception->getCode() + ); + } catch (DeadlockException $exception) { + throw new TemporaryCouldNotSaveException( + __('Database deadlock found when trying to get lock'), + $exception, + $exception->getCode() + ); + } catch (LockWaitException $exception) { + throw new TemporaryCouldNotSaveException( + __('Database lock wait timeout exceeded'), + $exception, + $exception->getCode() + ); + } catch (AttributeException $exception) { + throw InputException::invalidFieldValue( + $exception->getAttributeCode(), + $product->getData($exception->getAttributeCode()), + $exception + ); + } catch (ValidatorException $e) { + throw new CouldNotSaveException(__($e->getMessage())); + } catch (LocalizedException $e) { + throw $e; + } catch (\Exception $e) { + throw new CouldNotSaveException( + __('The product was unable to be saved. Please try again.'), + $e + ); + } + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php index 3fc3587637dad..c729a0c58e1ec 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php @@ -4,16 +4,36 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Test\Unit\Model; -use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\Data\ProductExtensionInterface; +use Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Gallery\MimeTypeExtensionMap; +use Magento\Catalog\Model\Product\LinkTypeProvider; +use Magento\Catalog\Model\ProductFactory; +use Magento\Catalog\Model\ProductRepository; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\Framework\Api\Data\ImageContentInterfaceFactory; +use Magento\Framework\Api\ExtensibleDataObjectConverter; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\ImageContentValidator; +use Magento\Framework\Api\ImageContentValidatorInterface; +use Magento\Framework\Api\ImageProcessorInterface; use Magento\Framework\Api\Data\ImageContentInterface; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\DB\Adapter\ConnectionException; +use Magento\Framework\Filesystem; use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Class ProductRepositoryTest @@ -25,127 +45,127 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ - protected $productMock; + protected $product; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ - protected $initializedProductMock; + private $initializedProduct; /** - * @var \Magento\Catalog\Model\ProductRepository + * @var ProductRepository */ - protected $model; + private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Helper|MockObject */ - protected $initializationHelperMock; + private $initializationHelper; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ - protected $resourceModelMock; + private $resourceModel; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ProductFactory|MockObject */ - protected $productFactoryMock; + private $productFactory; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ - protected $collectionFactoryMock; + private $collectionFactory; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var SearchCriteriaBuilder|MockObject */ - protected $searchCriteriaBuilderMock; + private $searchCriteriaBuilder; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var FilterBuilder|MockObject */ - protected $filterBuilderMock; + private $filterBuilder; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ProductAttributeRepositoryInterface|MockObject */ - protected $metadataServiceMock; + private $metadataService; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ProductSearchResultsInterfaceFactory|MockObject */ - protected $searchResultsFactoryMock; + private $searchResultsFactory; /** - * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject + * @var ExtensibleDataObjectConverter|MockObject */ - protected $eavConfigMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $extensibleDataObjectConverterMock; + private $extensibleDataObjectConverter; /** * @var array data to create product */ - protected $productData = [ + private $productData = [ 'sku' => 'exisiting', 'name' => 'existing product', ]; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Filesystem + * @var Filesystem|MockObject */ - protected $fileSystemMock; + private $fileSystem; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Catalog\Model\Product\Gallery\MimeTypeExtensionMap + * @var MimeTypeExtensionMap|MockObject */ - protected $mimeTypeExtensionMapMock; + private $mimeTypeExtensionMap; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ImageContentInterfaceFactory|MockObject */ - protected $contentFactoryMock; + private $contentFactory; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Api\ImageContentValidator + * @var ImageContentValidator|MockObject */ - protected $contentValidatorMock; + private $contentValidator; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var LinkTypeProvider|MockObject */ - protected $linkTypeProviderMock; + private $linkTypeProvider; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Api\ImageProcessorInterface + * @var ImageProcessorInterface|MockObject */ - protected $imageProcessorMock; + private $imageProcessor; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var ObjectManager */ - protected $objectManager; + private $objectManager; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ - protected $storeManagerMock; + private $storeManager; /** * @var \Magento\Catalog\Model\Product\Gallery\Processor|\PHPUnit_Framework_MockObject_MockObject */ - protected $mediaGalleryProcessor; + private $mediaGalleryProcessor; /** * @var CollectionProcessorInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $collectionProcessorMock; + private $collectionProcessor; + + /** + * @var ProductExtensionInterface|MockObject + */ + private $productExtension; /** * @var Json|\PHPUnit_Framework_MockObject_MockObject @@ -164,12 +184,12 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->productFactoryMock = $this->createPartialMock( + $this->productFactory = $this->createPartialMock( \Magento\Catalog\Model\ProductFactory::class, ['create', 'setData'] ); - $this->productMock = $this->createPartialMock( + $this->product = $this->createPartialMock( \Magento\Catalog\Model\Product::class, [ 'getId', @@ -179,11 +199,12 @@ protected function setUp() 'load', 'setData', 'getStoreId', - 'getMediaGalleryEntries' + 'getMediaGalleryEntries', + 'getExtensionAttributes' ] ); - $this->initializedProductMock = $this->createPartialMock( + $this->initializedProduct = $this->createPartialMock( \Magento\Catalog\Model\Product::class, [ 'getWebsiteIds', @@ -199,66 +220,66 @@ protected function setUp() 'validate', 'save', 'getMediaGalleryEntries', + 'getExtensionAttributes' ] ); - $this->initializedProductMock->expects($this->any()) + $this->initializedProduct->expects($this->any()) ->method('hasGalleryAttribute') ->willReturn(true); - $this->filterBuilderMock = $this->createMock(\Magento\Framework\Api\FilterBuilder::class); - $this->initializationHelperMock = $this->createMock( - \Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper::class - ); - $this->collectionFactoryMock = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class, - ['create'] - ); - $this->searchCriteriaBuilderMock = $this->createMock(\Magento\Framework\Api\SearchCriteriaBuilder::class); - $this->metadataServiceMock = $this->createMock(\Magento\Catalog\Api\ProductAttributeRepositoryInterface::class); - $this->searchResultsFactoryMock = $this->createPartialMock( + $this->filterBuilder = $this->createMock(FilterBuilder::class); + $this->initializationHelper = $this->createMock(Helper::class); + $this->collectionFactory = $this->createPartialMock(CollectionFactory::class, ['create']); + $this->searchCriteriaBuilder = $this->createMock(SearchCriteriaBuilder::class); + $this->metadataService = $this->createMock(ProductAttributeRepositoryInterface::class); + $this->searchResultsFactory = $this->createPartialMock( \Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory::class, ['create'] ); - $this->resourceModelMock = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product::class); + $this->resourceModel = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product::class); $this->objectManager = new ObjectManager($this); - $this->extensibleDataObjectConverterMock = $this - ->getMockBuilder(\Magento\Framework\Api\ExtensibleDataObjectConverter::class) + $this->extensibleDataObjectConverter = $this + ->getMockBuilder(ExtensibleDataObjectConverter::class) ->setMethods(['toNestedArray']) ->disableOriginalConstructor() ->getMock(); - $this->fileSystemMock = $this->getMockBuilder(\Magento\Framework\Filesystem::class) + $this->fileSystem = $this->getMockBuilder(Filesystem::class) ->disableOriginalConstructor()->getMock(); - $this->mimeTypeExtensionMapMock = - $this->getMockBuilder(\Magento\Catalog\Model\Product\Gallery\MimeTypeExtensionMap::class)->getMock(); - $this->contentFactoryMock = $this->createPartialMock( - \Magento\Framework\Api\Data\ImageContentInterfaceFactory::class, - ['create'] - ); - $this->contentValidatorMock = $this->getMockBuilder( - \Magento\Framework\Api\ImageContentValidatorInterface::class - ) + $this->mimeTypeExtensionMap = $this->getMockBuilder(MimeTypeExtensionMap::class)->getMock(); + $this->contentFactory = $this->createPartialMock(ImageContentInterfaceFactory::class, ['create']); + $this->contentValidator = $this->getMockBuilder(ImageContentValidatorInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->linkTypeProviderMock = $this->createPartialMock( - \Magento\Catalog\Model\Product\LinkTypeProvider::class, - ['getLinkTypes'] - ); - $this->imageProcessorMock = $this->createMock(\Magento\Framework\Api\ImageProcessorInterface::class); + $this->linkTypeProvider = $this->createPartialMock(LinkTypeProvider::class, ['getLinkTypes']); + $this->imageProcessor = $this->createMock(ImageProcessorInterface::class); - $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class) ->disableOriginalConstructor() ->setMethods([]) ->getMockForAbstractClass(); - $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) + $this->productExtension = $this->getMockBuilder(ProductExtensionInterface::class) + ->setMethods(['__toArray']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->productExtension + ->method('__toArray') + ->willReturn([]); + $this->product + ->method('getExtensionAttributes') + ->willReturn($this->productExtension); + $this->initializedProduct + ->method('getExtensionAttributes') + ->willReturn($this->productExtension); + $storeMock = $this->getMockBuilder(StoreInterface::class) ->disableOriginalConstructor() ->setMethods([]) ->getMockForAbstractClass(); $storeMock->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->storeManager->expects($this->any())->method('getStore')->willReturn($storeMock); $this->mediaGalleryProcessor = $this->createMock(\Magento\Catalog\Model\Product\Gallery\Processor::class); - $this->collectionProcessorMock = $this->getMockBuilder(CollectionProcessorInterface::class) + $this->collectionProcessor = $this->getMockBuilder(CollectionProcessorInterface::class) ->getMock(); $this->serializerMock = $this->getMockBuilder(Json::class)->getMock(); @@ -273,26 +294,26 @@ function ($value) { ); $this->model = $this->objectManager->getObject( - \Magento\Catalog\Model\ProductRepository::class, + ProductRepository::class, [ - 'productFactory' => $this->productFactoryMock, - 'initializationHelper' => $this->initializationHelperMock, - 'resourceModel' => $this->resourceModelMock, - 'filterBuilder' => $this->filterBuilderMock, - 'collectionFactory' => $this->collectionFactoryMock, - 'searchCriteriaBuilder' => $this->searchCriteriaBuilderMock, - 'metadataServiceInterface' => $this->metadataServiceMock, - 'searchResultsFactory' => $this->searchResultsFactoryMock, - 'extensibleDataObjectConverter' => $this->extensibleDataObjectConverterMock, - 'contentValidator' => $this->contentValidatorMock, - 'fileSystem' => $this->fileSystemMock, - 'contentFactory' => $this->contentFactoryMock, - 'mimeTypeExtensionMap' => $this->mimeTypeExtensionMapMock, - 'linkTypeProvider' => $this->linkTypeProviderMock, - 'imageProcessor' => $this->imageProcessorMock, - 'storeManager' => $this->storeManagerMock, + 'productFactory' => $this->productFactory, + 'initializationHelper' => $this->initializationHelper, + 'resourceModel' => $this->resourceModel, + 'filterBuilder' => $this->filterBuilder, + 'collectionFactory' => $this->collectionFactory, + 'searchCriteriaBuilder' => $this->searchCriteriaBuilder, + 'metadataServiceInterface' => $this->metadataService, + 'searchResultsFactory' => $this->searchResultsFactory, + 'extensibleDataObjectConverter' => $this->extensibleDataObjectConverter, + 'contentValidator' => $this->contentValidator, + 'fileSystem' => $this->fileSystem, + 'contentFactory' => $this->contentFactory, + 'mimeTypeExtensionMap' => $this->mimeTypeExtensionMap, + 'linkTypeProvider' => $this->linkTypeProvider, + 'imageProcessor' => $this->imageProcessor, + 'storeManager' => $this->storeManager, 'mediaGalleryProcessor' => $this->mediaGalleryProcessor, - 'collectionProcessor' => $this->collectionProcessorMock, + 'collectionProcessor' => $this->collectionProcessor, 'serializer' => $this->serializerMock, 'cacheLimit' => $this->cacheLimit ] @@ -305,50 +326,50 @@ function ($value) { */ public function testGetAbsentProduct() { - $this->productFactoryMock->expects($this->once())->method('create') - ->will($this->returnValue($this->productMock)); - $this->resourceModelMock->expects($this->once())->method('getIdBySku')->with('test_sku') + $this->productFactory->expects($this->once())->method('create') + ->will($this->returnValue($this->product)); + $this->resourceModel->expects($this->once())->method('getIdBySku')->with('test_sku') ->will($this->returnValue(null)); - $this->productFactoryMock->expects($this->never())->method('setData'); + $this->productFactory->expects($this->never())->method('setData'); $this->model->get('test_sku'); } public function testCreateCreatesProduct() { $sku = 'test_sku'; - $this->productFactoryMock->expects($this->once())->method('create') - ->will($this->returnValue($this->productMock)); - $this->resourceModelMock->expects($this->once())->method('getIdBySku')->with($sku) + $this->productFactory->expects($this->once())->method('create') + ->will($this->returnValue($this->product)); + $this->resourceModel->expects($this->once())->method('getIdBySku')->with($sku) ->will($this->returnValue('test_id')); - $this->productMock->expects($this->once())->method('load')->with('test_id'); - $this->productMock->expects($this->once())->method('getSku')->willReturn($sku); - $this->assertEquals($this->productMock, $this->model->get($sku)); + $this->product->expects($this->once())->method('load')->with('test_id'); + $this->product->expects($this->once())->method('getSku')->willReturn($sku); + $this->assertEquals($this->product, $this->model->get($sku)); } public function testGetProductInEditMode() { $sku = 'test_sku'; - $this->productFactoryMock->expects($this->once())->method('create') - ->will($this->returnValue($this->productMock)); - $this->resourceModelMock->expects($this->once())->method('getIdBySku')->with($sku) + $this->productFactory->expects($this->once())->method('create') + ->will($this->returnValue($this->product)); + $this->resourceModel->expects($this->once())->method('getIdBySku')->with($sku) ->will($this->returnValue('test_id')); - $this->productMock->expects($this->once())->method('setData')->with('_edit_mode', true); - $this->productMock->expects($this->once())->method('load')->with('test_id'); - $this->productMock->expects($this->once())->method('getSku')->willReturn($sku); - $this->assertEquals($this->productMock, $this->model->get($sku, true)); + $this->product->expects($this->once())->method('setData')->with('_edit_mode', true); + $this->product->expects($this->once())->method('load')->with('test_id'); + $this->product->expects($this->once())->method('getSku')->willReturn($sku); + $this->assertEquals($this->product, $this->model->get($sku, true)); } public function testGetBySkuWithSpace() { $trimmedSku = 'test_sku'; $sku = 'test_sku '; - $this->productFactoryMock->expects($this->once())->method('create') - ->will($this->returnValue($this->productMock)); - $this->resourceModelMock->expects($this->once())->method('getIdBySku')->with($sku) + $this->productFactory->expects($this->once())->method('create') + ->will($this->returnValue($this->product)); + $this->resourceModel->expects($this->once())->method('getIdBySku')->with($sku) ->will($this->returnValue('test_id')); - $this->productMock->expects($this->once())->method('load')->with('test_id'); - $this->productMock->expects($this->once())->method('getSku')->willReturn($trimmedSku); - $this->assertEquals($this->productMock, $this->model->get($sku)); + $this->product->expects($this->once())->method('load')->with('test_id'); + $this->product->expects($this->once())->method('getSku')->willReturn($trimmedSku); + $this->assertEquals($this->product, $this->model->get($sku)); } public function testGetWithSetStoreId() @@ -356,13 +377,13 @@ public function testGetWithSetStoreId() $productId = 123; $sku = 'test-sku'; $storeId = 7; - $this->productFactoryMock->expects($this->once())->method('create')->willReturn($this->productMock); - $this->resourceModelMock->expects($this->once())->method('getIdBySku')->with($sku)->willReturn($productId); - $this->productMock->expects($this->once())->method('setData')->with('store_id', $storeId); - $this->productMock->expects($this->once())->method('load')->with($productId); - $this->productMock->expects($this->once())->method('getId')->willReturn($productId); - $this->productMock->expects($this->once())->method('getSku')->willReturn($sku); - $this->assertSame($this->productMock, $this->model->get($sku, false, $storeId)); + $this->productFactory->expects($this->once())->method('create')->willReturn($this->product); + $this->resourceModel->expects($this->once())->method('getIdBySku')->with($sku)->willReturn($productId); + $this->product->expects($this->once())->method('setData')->with('store_id', $storeId); + $this->product->expects($this->once())->method('load')->with($productId); + $this->product->expects($this->once())->method('getId')->willReturn($productId); + $this->product->expects($this->once())->method('getSku')->willReturn($sku); + $this->assertSame($this->product, $this->model->get($sku, false, $storeId)); } /** @@ -371,22 +392,22 @@ public function testGetWithSetStoreId() */ public function testGetByIdAbsentProduct() { - $this->productFactoryMock->expects($this->once())->method('create') - ->will($this->returnValue($this->productMock)); - $this->productMock->expects($this->once())->method('load')->with('product_id'); - $this->productMock->expects($this->once())->method('getId')->willReturn(null); + $this->productFactory->expects($this->once())->method('create') + ->will($this->returnValue($this->product)); + $this->product->expects($this->once())->method('load')->with('product_id'); + $this->product->expects($this->once())->method('getId')->willReturn(null); $this->model->getById('product_id'); } public function testGetByIdProductInEditMode() { $productId = 123; - $this->productFactoryMock->method('create')->willReturn($this->productMock); - $this->productMock->method('setData')->with('_edit_mode', true); - $this->productMock->method('load')->with($productId); - $this->productMock->expects($this->atLeastOnce())->method('getId')->willReturn($productId); - $this->productMock->method('getSku')->willReturn('simple'); - $this->assertEquals($this->productMock, $this->model->getById($productId, true)); + $this->productFactory->method('create')->willReturn($this->product); + $this->product->method('setData')->with('_edit_mode', true); + $this->product->method('load')->with($productId); + $this->product->expects($this->atLeastOnce())->method('getId')->willReturn($productId); + $this->product->method('getSku')->willReturn('simple'); + $this->assertEquals($this->product, $this->model->getById($productId, true)); } /** @@ -400,21 +421,21 @@ public function testGetByIdProductInEditMode() public function testGetByIdForCacheKeyGenerate($identifier, $editMode, $storeId) { $callIndex = 0; - $this->productFactoryMock->expects($this->once())->method('create') - ->will($this->returnValue($this->productMock)); + $this->productFactory->expects($this->once())->method('create') + ->will($this->returnValue($this->product)); if ($editMode) { - $this->productMock->expects($this->at($callIndex))->method('setData')->with('_edit_mode', $editMode); + $this->product->expects($this->at($callIndex))->method('setData')->with('_edit_mode', $editMode); ++$callIndex; } if ($storeId !== null) { - $this->productMock->expects($this->at($callIndex))->method('setData')->with('store_id', $storeId); + $this->product->expects($this->at($callIndex))->method('setData')->with('store_id', $storeId); } - $this->productMock->expects($this->once())->method('load')->with($identifier); - $this->productMock->expects($this->atLeastOnce())->method('getId')->willReturn($identifier); - $this->productMock->method('getSku')->willReturn('simple'); - $this->assertEquals($this->productMock, $this->model->getById($identifier, $editMode, $storeId)); + $this->product->expects($this->once())->method('load')->with($identifier); + $this->product->expects($this->atLeastOnce())->method('getId')->willReturn($identifier); + $this->product->method('getSku')->willReturn('simple'); + $this->assertEquals($this->product, $this->model->getById($identifier, $editMode, $storeId)); //Second invocation should just return from cache - $this->assertEquals($this->productMock, $this->model->getById($identifier, $editMode, $storeId)); + $this->assertEquals($this->product, $this->model->getById($identifier, $editMode, $storeId)); } /** @@ -428,18 +449,18 @@ public function testGetByIdForcedReload() $editMode = false; $storeId = 0; - $this->productFactoryMock->expects($this->exactly(2))->method('create') - ->will($this->returnValue($this->productMock)); - $this->productMock->expects($this->exactly(2))->method('load'); + $this->productFactory->expects($this->exactly(2))->method('create') + ->will($this->returnValue($this->product)); + $this->product->expects($this->exactly(2))->method('load'); $this->serializerMock->expects($this->exactly(3))->method('serialize'); - $this->productMock->expects($this->exactly(4))->method('getId')->willReturn($identifier); - $this->productMock->method('getSku')->willReturn('simple'); - $this->assertEquals($this->productMock, $this->model->getById($identifier, $editMode, $storeId)); + $this->product->expects($this->exactly(4))->method('getId')->willReturn($identifier); + $this->product->method('getSku')->willReturn('simple'); + $this->assertEquals($this->product, $this->model->getById($identifier, $editMode, $storeId)); //second invocation should just return from cache - $this->assertEquals($this->productMock, $this->model->getById($identifier, $editMode, $storeId)); + $this->assertEquals($this->product, $this->model->getById($identifier, $editMode, $storeId)); //force reload - $this->assertEquals($this->productMock, $this->model->getById($identifier, $editMode, $storeId, true)); + $this->assertEquals($this->product, $this->model->getById($identifier, $editMode, $storeId, true)); } /** @@ -454,7 +475,7 @@ public function testGetByIdWhenCacheReduced() $productsCount = $this->cacheLimit * 2; $productMocks = $this->getProductMocksForReducedCache($productsCount); - $productFactoryInvMock = $this->productFactoryMock->expects($this->exactly($productsCount)) + $productFactoryInvMock = $this->productFactory->expects($this->exactly($productsCount)) ->method('create'); call_user_func_array([$productFactoryInvMock, 'willReturnOnConsecutiveCalls'], $productMocks); $this->serializerMock->expects($this->atLeastOnce())->method('serialize'); @@ -509,86 +530,86 @@ public function testGetForcedReload() $editMode = false; $storeId = 0; - $this->productFactoryMock->expects($this->exactly(2))->method('create') - ->will($this->returnValue($this->productMock)); - $this->productMock->expects($this->exactly(2))->method('load'); - $this->productMock->expects($this->exactly(2))->method('getId')->willReturn($sku); - $this->resourceModelMock->expects($this->exactly(2))->method('getIdBySku') + $this->productFactory->expects($this->exactly(2))->method('create') + ->will($this->returnValue($this->product)); + $this->product->expects($this->exactly(2))->method('load'); + $this->product->expects($this->exactly(2))->method('getId')->willReturn($sku); + $this->resourceModel->expects($this->exactly(2))->method('getIdBySku') ->with($sku)->willReturn($id); - $this->productMock->expects($this->exactly(2))->method('getSku')->willReturn($sku); + $this->product->expects($this->exactly(2))->method('getSku')->willReturn($sku); $this->serializerMock->expects($this->exactly(3))->method('serialize'); - $this->assertEquals($this->productMock, $this->model->get($sku, $editMode, $storeId)); + $this->assertEquals($this->product, $this->model->get($sku, $editMode, $storeId)); //second invocation should just return from cache - $this->assertEquals($this->productMock, $this->model->get($sku, $editMode, $storeId)); + $this->assertEquals($this->product, $this->model->get($sku, $editMode, $storeId)); //force reload - $this->assertEquals($this->productMock, $this->model->get($sku, $editMode, $storeId, true)); + $this->assertEquals($this->product, $this->model->get($sku, $editMode, $storeId, true)); } public function testGetByIdWithSetStoreId() { $productId = 123; $storeId = 1; - $this->productFactoryMock->expects($this->atLeastOnce())->method('create') - ->will($this->returnValue($this->productMock)); - $this->productMock->expects($this->once())->method('setData')->with('store_id', $storeId); - $this->productMock->expects($this->once())->method('load')->with($productId); - $this->productMock->expects($this->atLeastOnce())->method('getId')->willReturn($productId); - $this->productMock->method('getSku')->willReturn('simple'); - $this->assertEquals($this->productMock, $this->model->getById($productId, false, $storeId)); + $this->productFactory->expects($this->atLeastOnce())->method('create') + ->will($this->returnValue($this->product)); + $this->product->expects($this->once())->method('setData')->with('store_id', $storeId); + $this->product->expects($this->once())->method('load')->with($productId); + $this->product->expects($this->atLeastOnce())->method('getId')->willReturn($productId); + $this->product->method('getSku')->willReturn('simple'); + $this->assertEquals($this->product, $this->model->getById($productId, false, $storeId)); } public function testGetBySkuFromCacheInitializedInGetById() { $productId = 123; $productSku = 'product_123'; - $this->productFactoryMock->expects($this->once())->method('create') - ->will($this->returnValue($this->productMock)); - $this->productMock->expects($this->once())->method('load')->with($productId); - $this->productMock->expects($this->atLeastOnce())->method('getId')->willReturn($productId); - $this->productMock->expects($this->once())->method('getSku')->willReturn($productSku); - $this->assertEquals($this->productMock, $this->model->getById($productId)); - $this->assertEquals($this->productMock, $this->model->get($productSku)); + $this->productFactory->expects($this->once())->method('create') + ->will($this->returnValue($this->product)); + $this->product->expects($this->once())->method('load')->with($productId); + $this->product->expects($this->atLeastOnce())->method('getId')->willReturn($productId); + $this->product->expects($this->once())->method('getSku')->willReturn($productSku); + $this->assertEquals($this->product, $this->model->getById($productId)); + $this->assertEquals($this->product, $this->model->get($productSku)); } public function testSaveExisting() { - $this->resourceModelMock->expects($this->any())->method('getIdBySku')->will($this->returnValue(100)); - $this->productFactoryMock->expects($this->any()) + $this->resourceModel->expects($this->any())->method('getIdBySku')->will($this->returnValue(100)); + $this->productFactory->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) + ->will($this->returnValue($this->product)); + $this->initializationHelper->expects($this->never())->method('initialize'); + $this->resourceModel->expects($this->once())->method('validate')->with($this->product) ->willReturn(true); - $this->resourceModelMock->expects($this->once())->method('save')->with($this->productMock)->willReturn(true); - $this->extensibleDataObjectConverterMock + $this->resourceModel->expects($this->once())->method('save')->with($this->product)->willReturn(true); + $this->extensibleDataObjectConverter ->expects($this->once()) ->method('toNestedArray') ->will($this->returnValue($this->productData)); - $this->productMock->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']); + $this->product->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']); - $this->assertEquals($this->productMock, $this->model->save($this->productMock)); + $this->assertEquals($this->product, $this->model->save($this->product)); } 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()) + $this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); + $this->resourceModel->expects($this->at(0))->method('getIdBySku')->will($this->returnValue(null)); + $this->resourceModel->expects($this->at(3))->method('getIdBySku')->will($this->returnValue(100)); + $this->productFactory->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) + ->will($this->returnValue($this->product)); + $this->initializationHelper->expects($this->never())->method('initialize'); + $this->resourceModel->expects($this->once())->method('validate')->with($this->product) ->willReturn(true); - $this->resourceModelMock->expects($this->once())->method('save')->with($this->productMock)->willReturn(true); - $this->extensibleDataObjectConverterMock + $this->resourceModel->expects($this->once())->method('save')->with($this->product)->willReturn(true); + $this->extensibleDataObjectConverter ->expects($this->once()) ->method('toNestedArray') ->will($this->returnValue($this->productData)); - $this->productMock->method('getSku')->willReturn('simple'); + $this->product->method('getSku')->willReturn('simple'); - $this->assertEquals($this->productMock, $this->model->save($this->productMock)); + $this->assertEquals($this->product, $this->model->save($this->product)); } /** @@ -597,24 +618,24 @@ public function testSaveNew() */ public function testSaveUnableToSaveException() { - $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); - $this->resourceModelMock->expects($this->exactly(1)) + $this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); + $this->resourceModel->expects($this->exactly(1)) ->method('getIdBySku')->willReturn(null); - $this->productFactoryMock->expects($this->exactly(2)) + $this->productFactory->expects($this->exactly(2)) ->method('create') - ->will($this->returnValue($this->productMock)); - $this->initializationHelperMock->expects($this->never())->method('initialize'); - $this->resourceModelMock->expects($this->once())->method('validate')->with($this->productMock) + ->will($this->returnValue($this->product)); + $this->initializationHelper->expects($this->never())->method('initialize'); + $this->resourceModel->expects($this->once())->method('validate')->with($this->product) ->willReturn(true); - $this->resourceModelMock->expects($this->once())->method('save')->with($this->productMock) + $this->resourceModel->expects($this->once())->method('save')->with($this->product) ->willThrowException(new \Exception()); - $this->extensibleDataObjectConverterMock + $this->extensibleDataObjectConverter ->expects($this->once()) ->method('toNestedArray') ->will($this->returnValue($this->productData)); - $this->productMock->method('getSku')->willReturn('simple'); + $this->product->method('getSku')->willReturn('simple'); - $this->model->save($this->productMock); + $this->model->save($this->product); } /** @@ -623,24 +644,24 @@ 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)) + $this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); + $this->resourceModel->expects($this->exactly(1))->method('getIdBySku')->will($this->returnValue(null)); + $this->productFactory->expects($this->exactly(2)) ->method('create') - ->will($this->returnValue($this->productMock)); - $this->initializationHelperMock->expects($this->never())->method('initialize'); - $this->resourceModelMock->expects($this->once())->method('validate')->with($this->productMock) + ->will($this->returnValue($this->product)); + $this->initializationHelper->expects($this->never())->method('initialize'); + $this->resourceModel->expects($this->once())->method('validate')->with($this->product) ->willReturn(true); - $this->resourceModelMock->expects($this->once())->method('save')->with($this->productMock) + $this->resourceModel->expects($this->once())->method('save')->with($this->product) ->willThrowException(new \Magento\Eav\Model\Entity\Attribute\Exception(__('123'))); - $this->productMock->expects($this->once())->method('getId')->willReturn(null); - $this->extensibleDataObjectConverterMock + $this->product->expects($this->once())->method('getId')->willReturn(null); + $this->extensibleDataObjectConverter ->expects($this->once()) ->method('toNestedArray') ->will($this->returnValue($this->productData)); - $this->productMock->method('getSku')->willReturn('simple'); + $this->product->method('getSku')->willReturn('simple'); - $this->model->save($this->productMock); + $this->model->save($this->product); } /** @@ -649,22 +670,22 @@ 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)) + $this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); + $this->resourceModel->expects($this->exactly(1))->method('getIdBySku')->will($this->returnValue(null)); + $this->productFactory->expects($this->exactly(2)) ->method('create') - ->will($this->returnValue($this->productMock)); - $this->initializationHelperMock->expects($this->never())->method('initialize'); - $this->resourceModelMock->expects($this->once())->method('validate')->with($this->productMock) + ->will($this->returnValue($this->product)); + $this->initializationHelper->expects($this->never())->method('initialize'); + $this->resourceModel->expects($this->once())->method('validate')->with($this->product) ->willReturn(['error1', 'error2']); - $this->productMock->expects($this->never())->method('getId'); - $this->extensibleDataObjectConverterMock + $this->product->expects($this->never())->method('getId'); + $this->extensibleDataObjectConverter ->expects($this->once()) ->method('toNestedArray') ->will($this->returnValue($this->productData)); - $this->productMock->method('getSku')->willReturn('simple'); + $this->product->method('getSku')->willReturn('simple'); - $this->model->save($this->productMock); + $this->model->save($this->product); } /** @@ -673,36 +694,36 @@ public function testSaveInvalidProductException() */ public function testSaveThrowsTemporaryStateExceptionIfDatabaseConnectionErrorOccurred() { - $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); - $this->productFactoryMock->expects($this->any()) + $this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); + $this->productFactory->expects($this->any()) ->method('create') - ->will($this->returnValue($this->productMock)); - $this->initializationHelperMock->expects($this->never()) + ->will($this->returnValue($this->product)); + $this->initializationHelper->expects($this->never()) ->method('initialize'); - $this->resourceModelMock->expects($this->once()) + $this->resourceModel->expects($this->once()) ->method('validate') - ->with($this->productMock) + ->with($this->product) ->willReturn(true); - $this->resourceModelMock->expects($this->once()) + $this->resourceModel->expects($this->once()) ->method('save') - ->with($this->productMock) + ->with($this->product) ->willThrowException(new ConnectionException('Connection lost')); - $this->extensibleDataObjectConverterMock + $this->extensibleDataObjectConverter ->expects($this->once()) ->method('toNestedArray') ->will($this->returnValue($this->productData)); - $this->productMock->method('getSku')->willReturn('simple'); + $this->product->method('getSku')->willReturn('simple'); - $this->model->save($this->productMock); + $this->model->save($this->product); } public function testDelete() { - $this->productMock->expects($this->exactly(2))->method('getSku')->willReturn('product-42'); - $this->productMock->expects($this->exactly(2))->method('getId')->willReturn(42); - $this->resourceModelMock->expects($this->once())->method('delete')->with($this->productMock) + $this->product->expects($this->exactly(2))->method('getSku')->willReturn('product-42'); + $this->product->expects($this->exactly(2))->method('getId')->willReturn(42); + $this->resourceModel->expects($this->once())->method('delete')->with($this->product) ->willReturn(true); - $this->assertTrue($this->model->delete($this->productMock)); + $this->assertTrue($this->model->delete($this->product)); } /** @@ -711,22 +732,22 @@ public function testDelete() */ public function testDeleteException() { - $this->productMock->expects($this->exactly(2))->method('getSku')->willReturn('product-42'); - $this->productMock->expects($this->exactly(2))->method('getId')->willReturn(42); - $this->resourceModelMock->expects($this->once())->method('delete')->with($this->productMock) + $this->product->expects($this->exactly(2))->method('getSku')->willReturn('product-42'); + $this->product->expects($this->exactly(2))->method('getId')->willReturn(42); + $this->resourceModel->expects($this->once())->method('delete')->with($this->product) ->willThrowException(new \Exception()); - $this->model->delete($this->productMock); + $this->model->delete($this->product); } public function testDeleteById() { $sku = 'product-42'; - $this->productFactoryMock->expects($this->once())->method('create') - ->will($this->returnValue($this->productMock)); - $this->resourceModelMock->expects($this->once())->method('getIdBySku')->with($sku) + $this->productFactory->expects($this->once())->method('create') + ->will($this->returnValue($this->product)); + $this->resourceModel->expects($this->once())->method('getIdBySku')->with($sku) ->will($this->returnValue('42')); - $this->productMock->expects($this->once())->method('load')->with('42'); - $this->productMock->expects($this->atLeastOnce())->method('getSku')->willReturn($sku); + $this->product->expects($this->once())->method('load')->with('42'); + $this->product->expects($this->atLeastOnce())->method('getSku')->willReturn($sku); $this->assertTrue($this->model->deleteById($sku)); } @@ -734,24 +755,24 @@ public function testGetList() { $searchCriteriaMock = $this->createMock(\Magento\Framework\Api\SearchCriteriaInterface::class); $collectionMock = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product\Collection::class); - $this->collectionFactoryMock->expects($this->once())->method('create')->willReturn($collectionMock); - $this->productMock->method('getSku')->willReturn('simple'); + $this->collectionFactory->expects($this->once())->method('create')->willReturn($collectionMock); + $this->product->method('getSku')->willReturn('simple'); $collectionMock->expects($this->once())->method('addAttributeToSelect')->with('*'); $collectionMock->expects($this->exactly(2))->method('joinAttribute')->withConsecutive( ['status', 'catalog_product/status', 'entity_id', null, 'inner'], ['visibility', 'catalog_product/visibility', 'entity_id', null, 'inner'] ); - $this->collectionProcessorMock->expects($this->once()) + $this->collectionProcessor->expects($this->once()) ->method('process') ->with($searchCriteriaMock, $collectionMock); $collectionMock->expects($this->once())->method('load'); $collectionMock->expects($this->once())->method('addCategoryIds'); - $collectionMock->expects($this->atLeastOnce())->method('getItems')->willReturn([$this->productMock]); + $collectionMock->expects($this->atLeastOnce())->method('getItems')->willReturn([$this->product]); $collectionMock->expects($this->once())->method('getSize')->willReturn(128); $searchResultsMock = $this->createMock(\Magento\Catalog\Api\Data\ProductSearchResultsInterface::class); $searchResultsMock->expects($this->once())->method('setSearchCriteria')->with($searchCriteriaMock); - $searchResultsMock->expects($this->once())->method('setItems')->with([$this->productMock]); - $this->searchResultsFactoryMock->expects($this->once())->method('create')->willReturn($searchResultsMock); + $searchResultsMock->expects($this->once())->method('setItems')->with([$this->product]); + $this->searchResultsFactory->expects($this->once())->method('create')->willReturn($searchResultsMock); $this->assertEquals($searchResultsMock, $this->model->getList($searchCriteriaMock)); } @@ -821,28 +842,28 @@ 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()) + $this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); + $this->resourceModel->expects($this->any())->method('getIdBySku')->will($this->returnValue(100)); + $this->productFactory->expects($this->any()) ->method('create') - ->will($this->returnValue($this->initializedProductMock)); - $this->initializationHelperMock->expects($this->never())->method('initialize'); - $this->resourceModelMock->expects($this->once())->method('validate')->with($this->initializedProductMock) + ->will($this->returnValue($this->initializedProduct)); + $this->initializationHelper->expects($this->never())->method('initialize'); + $this->resourceModel->expects($this->once())->method('validate')->with($this->initializedProduct) ->willReturn(true); - $this->resourceModelMock->expects($this->once())->method('save') - ->with($this->initializedProductMock)->willReturn(true); + $this->resourceModel->expects($this->once())->method('save') + ->with($this->initializedProduct)->willReturn(true); //option data $this->productData['options'] = $newOptions; - $this->extensibleDataObjectConverterMock + $this->extensibleDataObjectConverter ->expects($this->once()) ->method('toNestedArray') ->will($this->returnValue($this->productData)); - $this->initializedProductMock->expects($this->atLeastOnce()) + $this->initializedProduct->expects($this->atLeastOnce()) ->method('getSku')->willReturn($this->productData['sku']); - $this->productMock->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']); + $this->product->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']); - $this->assertEquals($this->initializedProductMock, $this->model->save($this->productMock)); + $this->assertEquals($this->initializedProduct, $this->model->save($this->product)); } /** @@ -992,27 +1013,27 @@ 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()) + $this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); + $this->resourceModel->expects($this->any())->method('getIdBySku')->will($this->returnValue(100)); + $this->productFactory->expects($this->any()) ->method('create') - ->will($this->returnValue($this->initializedProductMock)); - $this->initializationHelperMock->expects($this->never())->method('initialize'); - $this->resourceModelMock->expects($this->once())->method('validate')->with($this->initializedProductMock) + ->will($this->returnValue($this->initializedProduct)); + $this->initializationHelper->expects($this->never())->method('initialize'); + $this->resourceModel->expects($this->once())->method('validate')->with($this->initializedProduct) ->willReturn(true); - $this->resourceModelMock->expects($this->once())->method('save') - ->with($this->initializedProductMock)->willReturn(true); + $this->resourceModel->expects($this->once())->method('save') + ->with($this->initializedProduct)->willReturn(true); - $this->initializedProductMock->setData("product_links", $existingLinks); + $this->initializedProduct->setData("product_links", $existingLinks); if (!empty($newLinks)) { $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; - $this->linkTypeProviderMock->expects($this->once()) + $this->linkTypeProvider->expects($this->once()) ->method('getLinkTypes') ->willReturn($linkTypes); - $this->initializedProductMock->setData("ignore_links_flag", false); - $this->resourceModelMock + $this->initializedProduct->setData("ignore_links_flag", false); + $this->resourceModel ->expects($this->any())->method('getProductsIdsBySkus') ->willReturn([$newLinks['linked_product_sku'] => $newLinks['linked_product_sku']]); @@ -1029,29 +1050,29 @@ public function testSaveWithLinks(array $newLinks, array $existingLinks, array $ $this->productData['product_links'] = [$inputLink]; - $this->initializedProductMock->expects($this->any()) + $this->initializedProduct->expects($this->any()) ->method('getProductLinks') ->willReturn([$inputLink]); } else { - $this->resourceModelMock + $this->resourceModel ->expects($this->any())->method('getProductsIdsBySkus') ->willReturn([]); $this->productData['product_links'] = []; - $this->initializedProductMock->setData('ignore_links_flag', true); - $this->initializedProductMock->expects($this->never()) + $this->initializedProduct->setData('ignore_links_flag', true); + $this->initializedProduct->expects($this->never()) ->method('getProductLinks') ->willReturn([]); } - $this->extensibleDataObjectConverterMock + $this->extensibleDataObjectConverter ->expects($this->at(0)) ->method('toNestedArray') ->will($this->returnValue($this->productData)); if (!empty($newLinks)) { - $this->extensibleDataObjectConverterMock + $this->extensibleDataObjectConverter ->expects($this->at(1)) ->method('toNestedArray') ->will($this->returnValue($newLinks)); @@ -1075,18 +1096,18 @@ public function testSaveWithLinks(array $newLinks, array $existingLinks, array $ } if (!empty($outputLinks)) { - $this->initializedProductMock->expects($this->once()) + $this->initializedProduct->expects($this->once()) ->method('setProductLinks') ->with($outputLinks); } else { - $this->initializedProductMock->expects($this->never()) + $this->initializedProduct->expects($this->never()) ->method('setProductLinks'); } - $this->initializedProductMock->expects($this->atLeastOnce()) + $this->initializedProduct->expects($this->atLeastOnce()) ->method('getSku')->willReturn($this->productData['sku']); - $results = $this->model->save($this->initializedProductMock); - $this->assertEquals($this->initializedProductMock, $results); + $results = $this->model->save($this->initializedProduct); + $this->assertEquals($this->initializedProduct, $results); } /** @@ -1163,20 +1184,20 @@ public function saveWithLinksDataProvider() protected function setupProductMocksForSave() { - $this->resourceModelMock->expects($this->any())->method('getIdBySku')->will($this->returnValue(100)); - $this->productFactoryMock->expects($this->any()) + $this->resourceModel->expects($this->any())->method('getIdBySku')->will($this->returnValue(100)); + $this->productFactory->expects($this->any()) ->method('create') - ->will($this->returnValue($this->initializedProductMock)); - $this->initializationHelperMock->expects($this->never())->method('initialize'); - $this->resourceModelMock->expects($this->once())->method('validate')->with($this->initializedProductMock) + ->will($this->returnValue($this->initializedProduct)); + $this->initializationHelper->expects($this->never())->method('initialize'); + $this->resourceModel->expects($this->once())->method('validate')->with($this->initializedProduct) ->willReturn(true); - $this->resourceModelMock->expects($this->once())->method('save') - ->with($this->initializedProductMock)->willReturn(true); + $this->resourceModel->expects($this->once())->method('save') + ->with($this->initializedProduct)->willReturn(true); } public function testSaveExistingWithNewMediaGalleryEntries() { - $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); + $this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $newEntriesData = [ 'images' => [ [ @@ -1200,13 +1221,13 @@ public function testSaveExistingWithNewMediaGalleryEntries() $this->setupProductMocksForSave(); //media gallery data $this->productData['media_gallery'] = $newEntriesData; - $this->extensibleDataObjectConverterMock + $this->extensibleDataObjectConverter ->expects($this->once()) ->method('toNestedArray') ->will($this->returnValue($this->productData)); - $this->initializedProductMock->setData('media_gallery', $newEntriesData); - $this->initializedProductMock->expects($this->any()) + $this->initializedProduct->setData('media_gallery', $newEntriesData); + $this->initializedProduct->expects($this->any()) ->method('getMediaAttributes') ->willReturn(["image" => "imageAttribute", "small_image" => "small_image_attribute"]); @@ -1215,7 +1236,7 @@ public function testSaveExistingWithNewMediaGalleryEntries() $absolutePath = '/a/b/filename.jpg'; $this->mediaGalleryProcessor->expects($this->once())->method('clearMediaAttribute') - ->with($this->initializedProductMock, ['image', 'small_image']); + ->with($this->initializedProduct, ['image', 'small_image']); $mediaConfigMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Media\Config::class) ->disableOriginalConstructor() @@ -1224,7 +1245,7 @@ public function testSaveExistingWithNewMediaGalleryEntries() ->method('getTmpMediaShortUrl') ->with($absolutePath) ->willReturn($mediaTmpPath . $absolutePath); - $this->initializedProductMock->expects($this->once()) + $this->initializedProduct->expects($this->once()) ->method('getMediaConfig') ->willReturn($mediaConfigMock); @@ -1233,21 +1254,21 @@ public function testSaveExistingWithNewMediaGalleryEntries() ->disableOriginalConstructor() ->setMethods(null) ->getMock(); - $this->contentFactoryMock->expects($this->once()) + $this->contentFactory->expects($this->once()) ->method('create') ->willReturn($contentDataObject); - $this->imageProcessorMock->expects($this->once()) + $this->imageProcessor->expects($this->once()) ->method('processImageContent') ->willReturn($absolutePath); $imageFileUri = "imageFileUri"; $this->mediaGalleryProcessor->expects($this->once())->method('addImage') - ->with($this->initializedProductMock, $mediaTmpPath . $absolutePath, ['image', 'small_image'], true, false) + ->with($this->initializedProduct, $mediaTmpPath . $absolutePath, ['image', 'small_image'], true, false) ->willReturn($imageFileUri); $this->mediaGalleryProcessor->expects($this->once())->method('updateImage') ->with( - $this->initializedProductMock, + $this->initializedProduct, $imageFileUri, [ 'label' => 'label_text', @@ -1256,11 +1277,11 @@ public function testSaveExistingWithNewMediaGalleryEntries() 'media_type' => 'media_type', ] ); - $this->initializedProductMock->expects($this->atLeastOnce()) + $this->initializedProduct->expects($this->atLeastOnce()) ->method('getSku')->willReturn($this->productData['sku']); - $this->productMock->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']); + $this->product->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']); - $this->model->save($this->productMock); + $this->model->save($this->product); } /** @@ -1276,38 +1297,38 @@ public function websitesProvider() public function testSaveWithDifferentWebsites() { $storeMock = $this->createMock(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()) + $this->resourceModel->expects($this->at(0))->method('getIdBySku')->will($this->returnValue(null)); + $this->resourceModel->expects($this->at(3))->method('getIdBySku')->will($this->returnValue(100)); + $this->productFactory->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) + ->will($this->returnValue($this->product)); + $this->initializationHelper->expects($this->never())->method('initialize'); + $this->resourceModel->expects($this->once())->method('validate')->with($this->product) ->willReturn(true); - $this->resourceModelMock->expects($this->once())->method('save')->with($this->productMock)->willReturn(true); - $this->extensibleDataObjectConverterMock + $this->resourceModel->expects($this->once())->method('save')->with($this->product)->willReturn(true); + $this->extensibleDataObjectConverter ->expects($this->once()) ->method('toNestedArray') ->will($this->returnValue($this->productData)); - $this->storeManagerMock->expects($this->any()) + $this->storeManager->expects($this->any()) ->method('getStore') ->willReturn($storeMock); - $this->storeManagerMock->expects($this->once()) + $this->storeManager->expects($this->once()) ->method('getWebsites') ->willReturn([ 1 => ['first'], 2 => ['second'], 3 => ['third'] ]); - $this->productMock->expects($this->once())->method('setWebsiteIds')->willReturn([2,3]); - $this->productMock->method('getSku')->willReturn('simple'); + $this->product->expects($this->once())->method('setWebsiteIds')->willReturn([2,3]); + $this->product->method('getSku')->willReturn('simple'); - $this->assertEquals($this->productMock, $this->model->save($this->productMock)); + $this->assertEquals($this->product, $this->model->save($this->product)); } public function testSaveExistingWithMediaGalleryEntries() { - $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); + $this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); //update one entry, delete one entry $newEntries = [ [ @@ -1355,26 +1376,26 @@ public function testSaveExistingWithMediaGalleryEntries() $this->setupProductMocksForSave(); //media gallery data $this->productData['media_gallery']['images'] = $newEntries; - $this->extensibleDataObjectConverterMock + $this->extensibleDataObjectConverter ->expects($this->once()) ->method('toNestedArray') ->will($this->returnValue($this->productData)); - $this->initializedProductMock->setData('media_gallery', $existingMediaGallery); - $this->initializedProductMock->expects($this->any()) + $this->initializedProduct->setData('media_gallery', $existingMediaGallery); + $this->initializedProduct->expects($this->any()) ->method('getMediaAttributes') ->willReturn(["image" => "filename1", "small_image" => "filename2"]); $this->mediaGalleryProcessor->expects($this->once())->method('clearMediaAttribute') - ->with($this->initializedProductMock, ['image', 'small_image']); + ->with($this->initializedProduct, ['image', 'small_image']); $this->mediaGalleryProcessor->expects($this->once()) ->method('setMediaAttribute') - ->with($this->initializedProductMock, ['image', 'small_image'], 'filename1'); - $this->initializedProductMock->expects($this->atLeastOnce()) + ->with($this->initializedProduct, ['image', 'small_image'], 'filename1'); + $this->initializedProduct->expects($this->atLeastOnce()) ->method('getSku')->willReturn($this->productData['sku']); - $this->productMock->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']); - $this->productMock->expects($this->any())->method('getMediaGalleryEntries')->willReturn(null); - $this->model->save($this->productMock); - $this->assertEquals($expectedResult, $this->initializedProductMock->getMediaGallery('images')); + $this->product->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']); + $this->product->expects($this->any())->method('getMediaGalleryEntries')->willReturn(null); + $this->model->save($this->product); + $this->assertEquals($expectedResult, $this->initializedProduct->getMediaGallery('images')); } } diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index e140305db4dcd..5f276cfcf074c 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -3,16 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Api; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Downloadable\Model\Link; use Magento\Store\Model\Store; use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\Store\Model\Website; use Magento\Store\Model\WebsiteRepository; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; +use Magento\Framework\Api\ExtensibleDataInterface; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrder; @@ -696,6 +699,31 @@ public function testUpdate() $this->assertEquals($productData[ProductInterface::SKU], $response[ProductInterface::SKU]); } + /** + * Update product with extension attributes. + * + * @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable.php + */ + public function testUpdateWithExtensionAttributes(): void + { + $sku = 'downloadable-product'; + $linksKey = 'downloadable_product_links'; + $productData = [ + ProductInterface::NAME => 'Downloadable (updated)', + ProductInterface::SKU => $sku, + ]; + $response = $this->updateProduct($productData); + + self::assertArrayHasKey(ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY, $response); + self::assertArrayHasKey($linksKey, $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]); + self::assertCount(1, $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY][$linksKey]); + + $linkData = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY][$linksKey][0]; + + self::assertArrayHasKey(Link::KEY_LINK_URL, $linkData); + self::assertEquals('http://example.com/downloadable.txt', $linkData[Link::KEY_LINK_URL]); + } + /** * @param array $product * @return array|bool|float|int|string From 3a6b77fa677b0014f6d9d5ade3ad1dac34c897de Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Wed, 15 Aug 2018 11:46:13 +0300 Subject: [PATCH 1010/1171] MAGETWO-93285: [2.3] Edit to Customer Address attribute 'State/Territory' will not show on customer address forms --- .../DataProviders/AddressAttributeData.php | 62 +++++++++++++++++++ .../layout/customer_account_create.xml | 3 + .../frontend/layout/customer_address_form.xml | 6 +- .../frontend/templates/address/edit.phtml | 22 +++---- .../frontend/templates/form/register.phtml | 20 +++--- ...ultishipping_checkout_customer_address.xml | 6 +- .../Customer/Block/Form/RegisterTest.php | 57 +++++++++++------ 7 files changed, 135 insertions(+), 41 deletions(-) create mode 100644 app/code/Magento/Customer/Block/DataProviders/AddressAttributeData.php diff --git a/app/code/Magento/Customer/Block/DataProviders/AddressAttributeData.php b/app/code/Magento/Customer/Block/DataProviders/AddressAttributeData.php new file mode 100644 index 0000000000000..2be340c8ccca4 --- /dev/null +++ b/app/code/Magento/Customer/Block/DataProviders/AddressAttributeData.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Block\DataProviders; + +use Magento\Framework\Escaper; +use Magento\Framework\View\Element\Block\ArgumentInterface; +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; + +/** + * Provides address attribute data into template. + */ +class AddressAttributeData implements ArgumentInterface +{ + /** + * @var AddressMetadataInterface + */ + private $addressMetadata; + + /** + * @var Escaper + */ + private $escaper; + + /** + * @param AddressMetadataInterface $addressMetadata + * @param Escaper $escaper + */ + public function __construct( + AddressMetadataInterface $addressMetadata, + Escaper $escaper + ) { + + $this->addressMetadata = $addressMetadata; + $this->escaper = $escaper; + } + + /** + * Returns frontend label for attribute. + * + * @param string $attributeCode + * @return string + * @throws LocalizedException + */ + public function getFrontendLabel(string $attributeCode): string + { + try { + $attribute = $this->addressMetadata->getAttributeMetadata($attributeCode); + $frontendLabel = $attribute->getFrontendLabel(); + } catch (NoSuchEntityException $e) { + $frontendLabel = ''; + } + + return $this->escaper->escapeHtml(__($frontendLabel)); + } +} diff --git a/app/code/Magento/Customer/view/frontend/layout/customer_account_create.xml b/app/code/Magento/Customer/view/frontend/layout/customer_account_create.xml index 8f65d54f458bc..fd5ecbfa7f277 100644 --- a/app/code/Magento/Customer/view/frontend/layout/customer_account_create.xml +++ b/app/code/Magento/Customer/view/frontend/layout/customer_account_create.xml @@ -12,6 +12,9 @@ </referenceBlock> <referenceContainer name="content"> <block class="Magento\Customer\Block\Form\Register" name="customer_form_register" template="Magento_Customer::form/register.phtml"> + <arguments> + <argument name="attribute_data" xsi:type="object">Magento\Customer\Block\DataProviders\AddressAttributeData</argument> + </arguments> <container name="form.additional.info" as="form_additional_info"/> <container name="customer.form.register.fields.before" as="form_fields_before" label="Form Fields Before" htmlTag="div" htmlClass="customer-form-before"/> </block> diff --git a/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml b/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml index 194dfd1bc7d2b..f053805409fe5 100644 --- a/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml +++ b/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml @@ -17,7 +17,11 @@ </arguments> </referenceBlock> <referenceContainer name="content"> - <block class="Magento\Customer\Block\Address\Edit" name="customer_address_edit" template="Magento_Customer::address/edit.phtml" cacheable="false"/> + <block class="Magento\Customer\Block\Address\Edit" name="customer_address_edit" template="Magento_Customer::address/edit.phtml" cacheable="false"> + <arguments> + <argument name="attribute_data" xsi:type="object">Magento\Customer\Block\DataProviders\AddressAttributeData</argument> + </arguments> + </block> </referenceContainer> </body> </page> diff --git a/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml b/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml index 6a129a3aa4b44..086b7423befec 100644 --- a/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml @@ -42,13 +42,13 @@ <?php $_streetValidationClass = $this->helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('street'); ?> <div class="field street required"> <label for="street_1" class="label"> - <span><?= $block->escapeHtml(__('Street Address')) ?></span> + <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('street') ?></span> </label> <div class="control"> <input type="text" name="street[]" value="<?= $block->escapeHtmlAttr($block->getStreetLine(1)) ?>" - title="<?= $block->escapeHtmlAttr(__('Street Address')) ?>" + title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('street') ?>" id="street_1" class="input-text <?= $block->escapeHtmlAttr($_streetValidationClass) ?>"/> <div class="nested"> @@ -74,20 +74,20 @@ <?php if ($this->helper('Magento\Customer\Helper\Address')->isVatAttributeVisible()) : ?> <div class="field taxvat"> <label class="label" for="vat_id"> - <span><?= $block->escapeHtml(__('VAT Number')) ?></span> + <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('vat_id') ?></span> </label> <div class="control"> <input type="text" name="vat_id" value="<?= $block->escapeHtmlAttr($block->getAddress()->getVatId()) ?>" - title="<?= $block->escapeHtmlAttr(__('VAT Number')) ?>" + title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('vat_id') ?>" class="input-text <?= $block->escapeHtmlAttr($this->helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('vat_id')) ?>" id="vat_id"> </div> </div> <?php endif; ?> <div class="field city required"> - <label class="label" for="city"><span><?= $block->escapeHtml(__('City')) ?></span></label> + <label class="label" for="city"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('city') ?></span></label> <div class="control"> <input type="text" name="city" @@ -99,11 +99,11 @@ </div> <div class="field region required"> <label class="label" for="region_id"> - <span><?= $block->escapeHtml(__('State/Province')) ?></span> + <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?></span> </label> <div class="control"> <select id="region_id" name="region_id" - title="<?= $block->escapeHtmlAttr(__('State/Province')) ?>" + title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?>" class="validate-select" <?= /* @noEscape */ !$block->getConfig('general/region/display_all') ? ' disabled="disabled"' : '' ?>> <option value=""><?= $block->escapeHtml(__('Please select a region, state or province.')) ?></option> </select> @@ -111,25 +111,25 @@ id="region" name="region" value="<?= $block->escapeHtmlAttr($block->getRegion()) ?>" - title="<?= $block->escapeHtmlAttr(__('State/Province')) ?>" + title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?>" class="input-text validate-not-number-first <?= $block->escapeHtmlAttr($this->helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('region')) ?>"<?= !$block->getConfig('general/region/display_all') ? ' disabled="disabled"' : '' ?>/> </div> </div> <div class="field zip required"> <label class="label" for="zip"> - <span><?= $block->escapeHtml(__('Zip/Postal Code')) ?></span> + <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('postcode') ?></span> </label> <div class="control"> <input type="text" name="postcode" value="<?= $block->escapeHtmlAttr($block->getAddress()->getPostcode()) ?>" - title="<?= $block->escapeHtmlAttr(__('Zip/Postal Code')) ?>" + title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('postcode') ?>" id="zip" class="input-text validate-zip-international <?= $block->escapeHtmlAttr($this->helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('postcode')) ?>"> </div> </div> <div class="field country required"> - <label class="label" for="country"><span><?= $block->escapeHtml(__('Country')) ?></span></label> + <label class="label" for="country"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('country_id') ?></span></label> <div class="control"> <?= $block->getCountryHtmlSelect() ?> </div> diff --git a/app/code/Magento/Customer/view/frontend/templates/form/register.phtml b/app/code/Magento/Customer/view/frontend/templates/form/register.phtml index 6cdb8fc44f665..bb86c9f453dfb 100644 --- a/app/code/Magento/Customer/view/frontend/templates/form/register.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/form/register.phtml @@ -65,9 +65,9 @@ <?php $_streetValidationClass = $this->helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('street'); ?> <div class="field street required"> - <label for="street_1" class="label"><span><?= $block->escapeHtml(__('Street Address')) ?></span></label> + <label for="street_1" class="label"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('street') ?></span></label> <div class="control"> - <input type="text" name="street[]" value="<?= $block->escapeHtmlAttr($block->getFormData()->getStreet(0)) ?>" title="<?= $block->escapeHtmlAttr(__('Street Address')) ?>" id="street_1" class="input-text <?= $block->escapeHtmlAttr($_streetValidationClass) ?>"> + <input type="text" name="street[]" value="<?= $block->escapeHtmlAttr($block->getFormData()->getStreet(0)) ?>" title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('street') ?>" id="street_1" class="input-text <?= $block->escapeHtmlAttr($_streetValidationClass) ?>"> <div class="nested"> <?php $_streetValidationClass = trim(str_replace('required-entry', '', $_streetValidationClass)); ?> <?php for ($_i = 2, $_n = $this->helper('Magento\Customer\Helper\Address')->getStreetLines(); $_i <= $_n; $_i++): ?> @@ -85,31 +85,31 @@ </div> <div class="field required"> - <label for="city" class="label"><span><?= $block->escapeHtml(__('City')) ?></span></label> + <label for="city" class="label"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('city') ?></span></label> <div class="control"> - <input type="text" name="city" value="<?= $block->escapeHtmlAttr($block->getFormData()->getCity()) ?>" title="<?= $block->escapeHtmlAttr(__('City')) ?>" class="input-text <?= $block->escapeHtmlAttr($this->helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('city')) ?>" id="city"> + <input type="text" name="city" value="<?= $block->escapeHtmlAttr($block->getFormData()->getCity()) ?>" title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('city') ?>" class="input-text <?= $block->escapeHtmlAttr($this->helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('city')) ?>" id="city"> </div> </div> <div class="field region required"> - <label for="region_id" class="label"><span><?= $block->escapeHtml(__('State/Province')) ?></span></label> + <label for="region_id" class="label"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?></span></label> <div class="control"> - <select id="region_id" name="region_id" title="<?= $block->escapeHtmlAttr(__('State/Province')) ?>" class="validate-select" style="display:none;"> + <select id="region_id" name="region_id" title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?>" class="validate-select" style="display:none;"> <option value=""><?= $block->escapeHtml(__('Please select a region, state or province.')) ?></option> </select> - <input type="text" id="region" name="region" value="<?= $block->escapeHtml($block->getRegion()) ?>" title="<?= $block->escapeHtmlAttr(__('State/Province')) ?>" class="input-text <?= $block->escapeHtmlAttr($this->helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('region')) ?>" style="display:none;"> + <input type="text" id="region" name="region" value="<?= $block->escapeHtml($block->getRegion()) ?>" title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?>" class="input-text <?= $block->escapeHtmlAttr($this->helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('region')) ?>" style="display:none;"> </div> </div> <div class="field zip required"> - <label for="zip" class="label"><span><?= $block->escapeHtml(__('Zip/Postal Code')) ?></span></label> + <label for="zip" class="label"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('postcode') ?></span></label> <div class="control"> - <input type="text" name="postcode" value="<?= $block->escapeHtmlAttr($block->getFormData()->getPostcode()) ?>" title="<?= $block->escapeHtmlAttr(__('Zip/Postal Code')) ?>" id="zip" class="input-text validate-zip-international <?= $block->escapeHtmlAttr($this->helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('postcode')) ?>"> + <input type="text" name="postcode" value="<?= $block->escapeHtmlAttr($block->getFormData()->getPostcode()) ?>" title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('postcode') ?>" id="zip" class="input-text validate-zip-international <?= $block->escapeHtmlAttr($this->helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('postcode')) ?>"> </div> </div> <div class="field country required"> - <label for="country" class="label"><span><?= $block->escapeHtml(__('Country')) ?></span></label> + <label for="country" class="label"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('country_id') ?></span></label> <div class="control"> <?= $block->getCountryHtmlSelect() ?> </div> diff --git a/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml b/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml index 51e9e6352cfd5..c6bcdeb7b0413 100644 --- a/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml +++ b/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml @@ -9,7 +9,11 @@ <update handle="customer_form_template_handle"/> <body> <referenceContainer name="content"> - <block class="Magento\Customer\Block\Address\Edit" name="customer_address_edit" template="Magento_Customer::address/edit.phtml" cacheable="false"/> + <block class="Magento\Customer\Block\Address\Edit" name="customer_address_edit" template="Magento_Customer::address/edit.phtml" cacheable="false"> + <arguments> + <argument name="attribute_data" xsi:type="object">Magento\Customer\Block\DataProviders\AddressAttributeData</argument> + </arguments> + </block> </referenceContainer> </body> </page> diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Form/RegisterTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Form/RegisterTest.php index 874811d159050..7f01508a9f821 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Block/Form/RegisterTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Form/RegisterTest.php @@ -5,6 +5,10 @@ */ namespace Magento\Customer\Block\Form; +use Magento\Customer\Block\DataProviders\AddressAttributeData; +use Magento\Framework\View\Element\Template; +use Magento\TestFramework\Helper\Bootstrap; + /** * Test class for \Magento\Customer\Block\Form\Register * @@ -19,10 +23,10 @@ class RegisterTest extends \PHPUnit\Framework\TestCase public function testCompanyDefault() { /** @var \Magento\Customer\Block\Widget\Company $block */ - $block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Block\Form\Register::class - )->setTemplate('Magento_Customer::form/register.phtml') - ->setShowAddressFields(true); + $block = Bootstrap::getObjectManager()->create(Register::class) + ->setTemplate('Magento_Customer::form/register.phtml') + ->setShowAddressFields(true); + $this->setAttributeDataProvider($block); $this->assertContains('title="Company"', $block->toHtml()); } @@ -34,10 +38,11 @@ public function testCompanyDefault() public function testTelephoneDefault() { /** @var \Magento\Customer\Block\Widget\Company $block */ - $block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Block\Form\Register::class + $block = Bootstrap::getObjectManager()->create( + Register::class )->setTemplate('Magento_Customer::form/register.phtml') ->setShowAddressFields(true); + $this->setAttributeDataProvider($block); $this->assertContains('title="Phone Number"', $block->toHtml()); } @@ -49,10 +54,11 @@ public function testTelephoneDefault() public function testFaxDefault() { /** @var \Magento\Customer\Block\Widget\Company $block */ - $block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Block\Form\Register::class + $block = Bootstrap::getObjectManager()->create( + Register::class )->setTemplate('Magento_Customer::form/register.phtml') ->setShowAddressFields(true); + $this->setAttributeDataProvider($block); $this->assertNotContains('title="Fax"', $block->toHtml()); } @@ -64,17 +70,18 @@ public function testFaxDefault() public function testCompanyDisabled() { /** @var \Magento\Customer\Model\Attribute $model */ - $model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + $model = Bootstrap::getObjectManager()->create( \Magento\Customer\Model\Attribute::class ); $model->loadByCode('customer_address', 'company')->setIsVisible('0'); $model->save(); /** @var \Magento\Customer\Block\Widget\Company $block */ - $block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Block\Form\Register::class + $block = Bootstrap::getObjectManager()->create( + Register::class )->setTemplate('Magento_Customer::form/register.phtml') ->setShowAddressFields(true); + $this->setAttributeDataProvider($block); $this->assertNotContains('title="Company"', $block->toHtml()); } @@ -86,17 +93,18 @@ public function testCompanyDisabled() public function testTelephoneDisabled() { /** @var \Magento\Customer\Model\Attribute $model */ - $model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + $model = Bootstrap::getObjectManager()->create( \Magento\Customer\Model\Attribute::class ); $model->loadByCode('customer_address', 'telephone')->setIsVisible('0'); $model->save(); /** @var \Magento\Customer\Block\Widget\Company $block */ - $block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Block\Form\Register::class + $block = Bootstrap::getObjectManager()->create( + Register::class )->setTemplate('Magento_Customer::form/register.phtml') ->setShowAddressFields(true); + $this->setAttributeDataProvider($block); $this->assertNotContains('title="Phone Number"', $block->toHtml()); } @@ -108,17 +116,18 @@ public function testTelephoneDisabled() public function testFaxEnabled() { /** @var \Magento\Customer\Model\Attribute $model */ - $model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + $model = Bootstrap::getObjectManager()->create( \Magento\Customer\Model\Attribute::class ); $model->loadByCode('customer_address', 'fax')->setIsVisible('1'); $model->save(); /** @var \Magento\Customer\Block\Widget\Company $block */ - $block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Block\Form\Register::class + $block = Bootstrap::getObjectManager()->create( + Register::class )->setTemplate('Magento_Customer::form/register.phtml') ->setShowAddressFields(true); + $this->setAttributeDataProvider($block); $this->assertContains('title="Fax"', $block->toHtml()); } @@ -126,7 +135,19 @@ public function testFaxEnabled() protected function tearDown() { /** @var \Magento\Eav\Model\Config $eavConfig */ - $eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class); + $eavConfig = Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class); $eavConfig->clear(); } + + /** + * Set attribute data provider. + * + * @param Template $block + * @return void + */ + private function setAttributeDataProvider(Template $block) + { + $attributeData = Bootstrap::getObjectManager()->get(AddressAttributeData::class); + $block->setAttributeData($attributeData); + } } From e60965adb0824ce1215ce04f0c0f62f33525c0fa Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Wed, 15 Aug 2018 13:31:55 +0400 Subject: [PATCH 1011/1171] MAGETWO-62891: New address is not marked as "Default Billing" - Add automated test --- ...ltBillingAndShippingAddressActionGroup.xml | 146 ++++++++++++++++++ ...OfDefaultBillingAndShippingAddressData.xml | 26 ++++ ...efaultBillingAndShippingAddressSection.xml | 94 +++++++++++ ...OfDefaultBillingAndShippingAddressTest.xml | 62 ++++++++ 4 files changed, 328 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Data/IdentityOfDefaultBillingAndShippingAddressData.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml new file mode 100644 index 0000000000000..e126bd4b5f743 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml @@ -0,0 +1,146 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + + <!-- Go To Product Page --> + <actionGroup name="GoToProductPage"> + <click selector="{{GoToProductPageSection.catalog}}" stepKey="clickOnCatalog" /> + <waitForPageLoad stepKey="waitForPage"/> + <click selector="{{GoToProductPageSection.product}}" stepKey="clickToSelectProductsItem" /> + <waitForPageLoad stepKey="waitForPageProducts"/> + </actionGroup> + + <!-- Create Simple Product --> + <actionGroup name="CreateSimpleProduct"> + <arguments> + <argument name="product" defaultValue="SimpleProduct"/> + </arguments> + <click selector="{{GoToProductPageSection.add}}" stepKey="clickToAddProduct"/> + <waitForPageLoad stepKey="WaitForProductPageIsLoaded"/> + <fillField selector="{{AdminAddSimpleProductSection.productName}}" userInput="{{product.name}}" stepKey="setNameForProduct"/> + <fillField selector="{{AdminAddSimpleProductSection.productSku}}" userInput="{{product.sku}}" stepKey="setSKUForProduct"/> + <fillField selector="{{AdminAddSimpleProductSection.productPrice}}" userInput="{{product.price}}" stepKey="setPriceForProduct"/> + <fillField selector="{{AdminAddSimpleProductSection.productQuantity}}" userInput="{{product.quantity}}" stepKey="setQuantityForProduct"/> + <click selector="{{AdminAddSimpleProductSection.searchOptimization}}" stepKey="clickOnSearchEngineOptimization"/> + <fillField selector="{{AdminAddSimpleProductSection.urlKey}}" userInput="{{product.urlKey}}" stepKey="setSearchUrlForProduct"/> + <click selector="{{AdminAddSimpleProductSection.saveButton}}" stepKey="clickSaveProduct"/> + <waitForPageLoad stepKey="WaitForProductSave"/> + <see userInput="You saved the product." stepKey="seeSaveConfirmation"/> + </actionGroup> + + <!--Create Account --> + <actionGroup name="StorefrontCreateAccountActionGroup"> + <click selector="{{StorefrontCreateAccountSection.createAccount}}" stepKey="ClickToCreateAccount"/> + <waitForPageLoad stepKey="waitForAccountFormIsOpened"/> + <fillField selector="{{StorefrontCreateAccountSection.firstName}}" userInput="{{CreateUserData.firstName}}" stepKey="TypeFirstName"/> + <fillField selector="{{StorefrontCreateAccountSection.lastName}}" userInput="{{CreateUserData.lastName}}" stepKey="TypeLastName"/> + <fillField selector="{{StorefrontCreateAccountSection.email}}" userInput="{{CreateUserData.firstName}}@magento.com" stepKey="TypeEmail"/> + <fillField selector="{{StorefrontCreateAccountSection.password}}" userInput="{{CreateUserData.password}}" stepKey="TypePassword"/> + <waitForPageLoad stepKey="waitToConfirmPassword"/> + <fillField selector="{{StorefrontCreateAccountSection.confirmPass}}" userInput="{{CreateUserData.password}}" stepKey="confirmPassword"/> + <click selector="{{StorefrontCreateAccountSection.create}}" stepKey="ClickToSaveAccount"/> + <waitForPageLoad stepKey="waitForAccountPageLoaded"/> + </actionGroup> + + <!--Find and add product to cart--> + <actionGroup name="FindAndAddProductToCardActionGroup"> + <!--Navigate to a category page --> + <amOnPage url="/{{SimpleProduct.name}}.html" stepKey="goToCreatedProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoaded"/> + <click selector="{{StorefrontAddCreatedProductToCartSection.addToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontAddCreatedProductToCartSection.successMessage}}" time="30" stepKey="waitForProductAdded"/> + <click selector="{{StorefrontAddCreatedProductToCartSection.showCard}}" stepKey="clickToOpenCard"/> + <waitForPageLoad stepKey="WaitForFormOpened"/> + <click selector="{{StorefrontAddCreatedProductToCartSection.proceed}}" stepKey="clickToProceedToCheckout"/> + <waitForPageLoad stepKey="waitForTheFormIsOpened"/> + <see userInput="Shipping Address" stepKey="seeShippingAddress"/> + </actionGroup> + + <!--Fill shipment form--> + <actionGroup name="FillShipmentFormActionGroup"> + <fillField selector="{{ShipmentFormSection.street}}" userInput="{{Account.street}}" stepKey="SetCustomerStreetAddress"/> + <fillField selector="{{ShipmentFormSection.city}}" userInput="{{Account.city}}" stepKey="SetCustomerCity"/> + <fillField selector="{{ShipmentFormSection.postcode}}" userInput="{{Account.postcode}}" stepKey="SetCustomerZipCode"/> + <fillField selector="{{ShipmentFormSection.telephone}}" userInput="{{Account.telephone}}" stepKey="SetCustomerPhoneNumber"/> + <click selector="{{ShipmentFormSection.region}}" stepKey="clickToSetState"/> + <click selector="{{ShipmentFormSection.state}}" stepKey="clickToChooseState"/> + <click selector="{{ShipmentFormSection.stateAlabama}}" stepKey="chooseStateAlabama"/> + <click selector="{{ShipmentFormSection.next}}" stepKey="clickToSaveShippingInfo"/> + <waitForPageLoad stepKey="waitForReviewAndPaymentsPageIsLoaded"/> + </actionGroup> + + <!--Mark "My billing and shipping address are the same"--> + <actionGroup name="MarkMyBillingAndShippingAddressAreTheSame"> + <conditionalClick selector="{{ShipmentFormSection.billingShippingAddressTheSameCheckbox}}" dependentSelector="{{ShipmentFormSection.placeOrderButton}}" visible="0" stepKey="selectkMyBillingAndShippingAddressAreTheSameCheckbox"/> + <click stepKey="clickPlaceOrderButton" selector="{{ShipmentFormSection.placeOrderButton}}"/> + <waitForPageLoad stepKey="waitForCheckoutPageLoaded"/> + <see stepKey="seeSuccessfulMessage" userInput="Thank you for your purchase!"/> + </actionGroup> + + <!--Go To My Account Page--> + <actionGroup name="GoToMyAccountPage"> + <click stepKey="clickCustomerNameItem" selector="{{GoToMyAccountSection.customerName}}"/> + <click stepKey="clickMyAccountItem" selector="{{GoToMyAccountSection.myAccountItem}}"/> + <waitForPageLoad stepKey="waitForMyAccountPageLoaded"/> + </actionGroup> + + <!--Assert That Shipping And Billing Address are the same--> + <actionGroup name="AssertThatShippingAndBillingAddressTheSame"> + <!--Get shipping and billing addresses--> + <grabTextFrom selector="{{ShipmentFormSection.shippingAddress}}" stepKey="shippingAddr"/> + <grabTextFrom selector="{{ShipmentFormSection.billingAddress}}" stepKey="billingAddr"/> + <!--Make sure that shipping and billing addresses are different--> + <see userInput="Shipping Address" stepKey="seeShippingAddress"/> + <see userInput="Billing Address" stepKey="seeBillingAddress"/> + <assertEquals stepKey="assert" actual="$billingAddr" expected="$shippingAddr"/> + </actionGroup> + + <!-- Delete Created Product --> + <actionGroup name="DeleteCreatedProduct"> + <conditionalClick selector="{{DeleteCreatedProductSection.clearAll}}" dependentSelector="{{DeleteCreatedProductSection.clearAll}}" visible="1" stepKey="clickClearAllIfThereIsAnyValue"/> + <click stepKey="clickFilterButton" selector="{{DeleteCreatedProductSection.filterButton}}"/> + <waitForElementVisible selector="{{DeleteCreatedProductSection.filterSKUField}}" stepKey="waitForFilterDataLoaded"/> + <fillField stepKey="searchProductUsingSKUField" selector="{{DeleteCreatedProductSection.filterSKUField}}" userInput="{{SimpleProduct.sku}}"/> + <click stepKey="clickFiltersApplyButton" selector="{{DeleteCreatedProductSection.filtersApplyButton}}"/> + <waitForElementNotVisible selector="{{DeleteCreatedProductSection.filterSKUField}}" stepKey="waitForFilterBecomeNotVisible"/> + <click selector="{{DeleteCreatedProductSection.createdProductID}}" stepKey="selectCreatedProduct"/> + <wait stepKey="waitSelectCreatedProduct" time="2"/> + <waitForElementVisible selector="{{DeleteCreatedProductSection.actionSelectBox}}" stepKey="waitToSelectActionVisible"/> + <click stepKey="clickToSelectAction" selector="{{DeleteCreatedProductSection.actionSelectBox}}"/> + <waitForElementVisible selector="{{DeleteCreatedProductSection.deleteButton}}" stepKey="waitForDeleteButtonAppeared"/> + <click selector="{{DeleteCreatedProductSection.deleteButton}}" stepKey="clickToDeleteProduct"/> + <waitForElementVisible selector="{{DeleteCreatedProductSection.okButton}}" stepKey="waitForOkButtonAppeared"/> + <click selector="{{DeleteCreatedProductSection.okButton}}" stepKey="clickToConfirm"/> + <wait stepKey="waitForRecordIsDeleted" time="2"/> + <see userInput="A total of 1 record(s) have been deleted." stepKey="productDeletedSuccessfully"/> + <click stepKey="clickClearAllFilterButton" selector="{{DeleteCreatedProductSection.clearAll}}"/> + <wait stepKey="waitToClearAllFilters" time="2"/> + </actionGroup> + + <!--Delete created Customer --> + <actionGroup name="DeleteCreatedCustomerActionGroup"> + <click stepKey="clickCustomerItem" selector="{{DashboardSection.customer}}"/> + <wait stepKey="WaitForCustomerViewOpened" time="2"/> + <click stepKey="clickCustomerAllCustomerItem" selector="{{DashboardSection.customerAllCustomer}}"/> + <waitForPageLoad stepKey="WaitForCustomerPageIsLoaded"/> + <fillField stepKey="searchToKeyword" selector="{{AdminCustomerAccInformationSection.searchToKeyword}}" userInput="{{CreateUserData.firstName}}"/> + <click stepKey="clickSearchButton" selector="{{AdminCustomerAccInformationSection.searchButton}}"/> + <waitForElementVisible stepKey="waitForFiltering" selector="{{AdminCustomerAccInformationSection.selectCustomer}}"/> + <click selector="{{AdminCustomerAccInformationSection.selectCustomer}}" stepKey="ClickOnCustomer"/> + <click selector="{{AdminCustomerAccInformationSection.actions}}" stepKey="ClickOnActions"/> + <waitForElementVisible selector="{{AdminCustomerAccInformationSection.delete}}" stepKey="waitForDeleteButtonAppeared"/> + <click selector="{{AdminCustomerAccInformationSection.delete}}" stepKey="ClickOnDelete"/> + <waitForElementVisible selector="{{AdminCustomerAccInformationSection.confirm}}" stepKey="waitForConfirmButtonAppeared"/> + <click selector="{{AdminCustomerAccInformationSection.confirm}}" stepKey="ClickToConfirm"/> + <waitForPageLoad stepKey="waitClickToConfirmButton"/> + <click stepKey="clickClearAllFilterButton" selector="{{DeleteCreatedProductSection.clearAll}}"/> + <wait stepKey="waitToClearAllFilters" time="2"/> + </actionGroup> + +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/IdentityOfDefaultBillingAndShippingAddressData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/IdentityOfDefaultBillingAndShippingAddressData.xml new file mode 100644 index 0000000000000..a627eb3102bf6 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Data/IdentityOfDefaultBillingAndShippingAddressData.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SimpleProduct" type="product"> + <data key="name" unique="suffix">testProduct</data> + <data key="sku" unique="suffix">testSku</data> + <data key="price">210</data> + <data key="quantity">10</data> + <data key="urlKey" unique="suffix">testProduct</data> + </entity> + + <entity name="Account" type="product"> + <data key="street">BirminghamStreet</data> + <data key="city">Birmingham</data> + <data key="postcode">35005</data> + <data key="telephone">222222222</data> + </entity> + +</entities> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml new file mode 100644 index 0000000000000..8e8183a2be946 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="GoToProductPageSection"> + <!--Go to Catalog/Products--> + <element name="catalog" type="button" selector="#menu-magento-catalog-catalog"/> + <element name="product" type="button" selector="//span[contains(text(), 'Products')]"/> + <element name="add" type="button" selector="#add_new_product-button"/> + </section> + + <section name="AdminAddSimpleProductSection"> + <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> + <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> + <element name="productPrice" type="input" selector=".admin__field[data-index=price] input"/> + <element name="productQuantity" type="input" selector=".admin__field[data-index=qty] input"/> + <element name="searchOptimization" type="button" selector="//*[contains(text(),'Search Engine Optimization')]"/> + <element name="urlKey" type="input" selector="//input[contains(@name,'url_key')]"/> + <element name="saveButton" type="button" selector="#save-button"/> + </section> + + <section name="StorefrontCreateAccountSection"> + <element name="createAccount" type="button" selector="//div[contains(@class, 'panel wrapper')]//a[text()='Create an Account']"/> + <element name="firstName" type="input" selector="#firstname"/> + <element name="lastName" type="input" selector="#lastname"/> + <element name="email" type="input" selector="#email_address"/> + <element name="password" type="input" selector="#password"/> + <element name="confirmPass" type="input" selector="#password-confirmation"/> + <element name="create" type="button" selector="//button[@type='submit' and @title='Create an Account']"/> + </section> + + <section name="StorefrontAddCreatedProductToCartSection"> + <element name="addToCartBtn" type="button" selector="button.action.tocart.primary"/> + <element name="successMsg" type="button" selector="div.message-success"/> + <element name="showCard" type="button" selector=".action.showcart"/> + <element name="proceed" type="button" selector="#top-cart-btn-checkout"/> + <element name="successMessage" type="button" selector="div.message-success"/> + </section> + + <section name="ShipmentFormSection"> + <element name="street" type="input" selector="//*[@name='street[0]']"/> + <element name="city" type="input" selector="//*[@name='city']"/> + <element name="postcode" type="input" selector="//*[@name='postcode']"/> + <element name="telephone" type="input" selector="//*[@name='telephone']"/> + <element name="region" type="input" selector="//*[@name='region_id']"/> + <element name="state" type="input" selector="//*[@name='country_id']"/> + <element name="stateAlabama" type="select" selector="//*[@name='region_id']/option[contains(text(),'Alabama')]"/> + <element name="next" type="input" selector="//span[contains(text(), 'Next')]"/> + + <element name="billingShippingAddressTheSameCheckbox" type="select" selector="#billing-address-same-as-shipping-checkmo"/> + <element name="placeOrderButton" type="button" selector="//span[contains(text(),'Place Order')]"/> + <element name="shippingAddress" type="textarea" selector="//*[@class='box box-billing-address']//address"/> + <element name="billingAddress" type="textarea" selector="//*[@class='box box-shipping-address']//address"/> + </section> + + <section name="GoToMyAccountSection"> + <element name="customerName" type="input" selector="//*[@class='page-header']//*[@data-bind='text: customer().fullname']"/> + <element name="myAccountItem" type="input" selector="//*[@class='page-header']//*[contains(text(),'My Account')]"/> + </section> + + <section name="DeleteCreatedProductSection"> + <element name="searchToKeyword" type="input" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/input"/> + <element name="searchButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/button"/> + <element name="createdProductID" type="select" selector="//*[@class='data-grid-checkbox-cell-inner']/input"/> + <element name="actionSelectBox" type="button" selector="//*[@class='col-xs-2']//span[text()='Actions']"/> + <element name="deleteButton" type="button" selector="//div[@class='col-xs-2']//*[text()='Delete']"/> + <element name="okButton" type="button" selector=".action-primary.action-accept"/> + <element name="clearAll" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[contains(text(), 'Clear all')]"/> + <element name="filterButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-filters-action-wrap']/button"/> + <element name="filterSKUField" type="input" selector="//*[@name='sku']"/> + <element name="filtersApplyButton" type="button" selector="//*[contains(text(),'Apply Filters')]"/> + </section> + + <section name="DashboardSection"> + <element name="customerAllCustomer" type="button" selector="//*[@data-ui-id='menu-magento-customer-customer-manage']"/> + <element name="customer" type="button" selector="#menu-magento-customer-customer"/> + </section> + + <section name="AdminCustomerAccInformationSection"> + <element name="searchToKeyword" type="input" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/input"/> + <element name="searchButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/button"/> + <element name="selectCustomer" type="checkbox" selector="//*[@class='admin__data-grid-wrap' and @data-role='grid-wrapper']//*[@class='data-grid-multicheck-cell']/div/label"/> + <element name="actions" type="button" selector="//div[@class='admin__data-grid-header']//div[@class='col-xs-2']//span[text()='Actions']"/> + <element name="delete" type="button" selector="//div[@class='col-xs-2']//span[text()='Delete']"/> + <element name="confirm" type="button" selector=".action-primary.action-accept"/> + </section> + +</sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml new file mode 100644 index 0000000000000..48e4cc631e175 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="IdentityOfDefaultBillingAndShippingAddressTest"> + <annotations> + <features value="Customer"/> + <title value="Checking assignment of default billing address after placing an orde"/> + <description value="In 'Address book' field 'Default Billing Address' should be the same as 'Default Shipping Address'"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-94108"/> + <stories value="MAGETWO-62891: New address is not marked as 'Default Billing'"/> + <group value="customer"/> + </annotations> + + <before> + <!--Login as admin--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!--Create product--> + <actionGroup ref="GoToProductPage" stepKey="goToProductPage"/> + <actionGroup ref="CreateSimpleProduct" stepKey="createSimpleProduct"/> + </before> + + <!--Go to Storefront--> + <amOnPage url="" stepKey="DoToStorefront"/> + + <!--Create account--> + <actionGroup ref="StorefrontCreateAccountActionGroup" stepKey="storefrontCreateAccountActionGroup"/> + + <!--Add product to cart--> + <actionGroup ref="FindAndAddProductToCardActionGroup" stepKey="FindAndAddProductToCard"/> + + <!--Fill shipment form--> + <actionGroup ref="FillShipmentFormActionGroup" stepKey="ShipmentFormActionGroup"/> + + <!--Fill cart data--> + <actionGroup ref="MarkMyBillingAndShippingAddressAreTheSame" stepKey="markMyBillingAndShippingAddressAreTheSame"/> + + <!--Go To My Account--> + <actionGroup ref="GoToMyAccountPage" stepKey="goToMyAccountPage"/> + + <!--Assert That Shipping And Billing Address are the same--> + <actionGroup ref="AssertThatShippingAndBillingAddressTheSame" stepKey="assertThatShippingAndBillingAddressTheSame"/> + + <after> + <!--Delete created Product--> + <amOnPage url="/admin" stepKey="GoToDashboard"/> + <actionGroup ref="GoToProductPage" stepKey="againGoToProductPage"/> + <actionGroup ref="DeleteCreatedProduct" stepKey="deleteCreatedProduct"/> + + <!--Delete created Customer--> + <actionGroup ref="DeleteCreatedCustomerActionGroup" stepKey="deleteCreatedCustomer"/> + </after> + </test> +</tests> From 370dc8492dc2b00d21e74dccf24e0abd6cca6d97 Mon Sep 17 00:00:00 2001 From: nmalevanec <feaec9b174ab7404a5814cd67315bd99cf0917c2> Date: Wed, 15 Aug 2018 13:32:05 +0300 Subject: [PATCH 1012/1171] [Forwardport] Fix type error in Cart/Totals. --- app/code/Magento/Quote/Model/Cart/CartTotalRepository.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php b/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php index 9fe69b691424d..e18ab8587fc71 100644 --- a/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php +++ b/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php @@ -10,6 +10,7 @@ use Magento\Quote\Api\CartTotalRepositoryInterface; use Magento\Catalog\Helper\Product\ConfigurationPool; use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\Api\ExtensibleDataInterface; use Magento\Quote\Model\Cart\Totals\ItemConverter; use Magento\Quote\Api\CouponManagementInterface; @@ -94,6 +95,7 @@ public function get($cartId) $addressTotalsData = $quote->getShippingAddress()->getData(); $addressTotals = $quote->getShippingAddress()->getTotals(); } + unset($addressTotalsData[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]); /** @var \Magento\Quote\Api\Data\TotalsInterface $quoteTotals */ $quoteTotals = $this->totalsFactory->create(); From 4ec730cc0cabc4a55e5e7bf1ab99496178deeae1 Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Tue, 14 Aug 2018 18:44:11 +0400 Subject: [PATCH 1013/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Updated automated test --- .../StorefrontAddProductToCardActionGroup.xml | 11 +++++++---- .../Section/StorefrontAddProductToCardSection.xml | 3 +++ .../Mftf/Test/AddingProductWithExpiredSessionTest.xml | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml index 71919d9da37de..49edf529a188d 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml @@ -41,10 +41,13 @@ <see userInput="Shipping Address" stepKey="seeShippingAddress"/> </actionGroup> - <actionGroup name="DeleteCreatedProduct"> - <fillField stepKey="searchToKeyword" selector="{{DeleteCreatedProduct.searchToKeyword}}" userInput="{{SimpleProductOne.name}}"/> - <click stepKey="clickSearchButton" selector="{{DeleteCreatedProduct.searchButton}}"/> - <wait stepKey="waitForFiltering" time="2"/> + <actionGroup name="DeleteCreatedProductActionGroup"> + <conditionalClick selector="{{DeleteCreatedProduct.clearAll}}" dependentSelector="{{DeleteCreatedProduct.clearAll}}" visible="1" stepKey="clickClearAllIfThereIsAnyValue"/> + <click stepKey="clickFilterButton" selector="{{DeleteCreatedProduct.filterButton}}"/> + <waitForElementVisible selector="{{DeleteCreatedProduct.filterSKUField}}" stepKey="waitForFilterDataLoaded"/> + <fillField stepKey="searchProductUsingSKUField" selector="{{DeleteCreatedProduct.filterSKUField}}" userInput="{{SimpleProductOne.sku}}"/> + <click stepKey="clickFiltersApplyButton" selector="{{DeleteCreatedProduct.filtersApplyButton}}"/> + <waitForElementNotVisible selector="{{DeleteCreatedProduct.filterSKUField}}" stepKey="waitForFilterBecomeNotVisible"/> <click selector="{{DeleteCreatedProduct.createdProductID}}" stepKey="selectCreatedProduct"/> <wait stepKey="waitSelectCreatedProduct" time="2"/> <waitForElementVisible selector="{{DeleteCreatedProduct.actionSelectBox}}" stepKey="waitToSelectActionVisible" time="50"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml index 868a47a26f42b..cc0cf8e07008e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml @@ -68,6 +68,9 @@ <element name="deleteButton" type="button" selector="//div[@class='col-xs-2']//*[text()='Delete']"/> <element name="okButton" type="button" selector=".action-primary.action-accept"/> <element name="clearAll" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[contains(text(), 'Clear all')]"/> + <element name="filterButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-filters-action-wrap']/button"/> + <element name="filterSKUField" type="input" selector="//*[@name='sku']"/> + <element name="filtersApplyButton" type="button" selector="//*[contains(text(),'Apply Filters')]"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml index 058b5b5c18bfd..b8abda3b37de6 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml @@ -42,7 +42,7 @@ <!--Delete created product--> <amOnPage url="/admin" stepKey="GoToDashboard"/> <actionGroup ref="GoToProductPage" stepKey="againGoToProductPage"/> - <actionGroup ref="DeleteCreatedProduct" stepKey="deleteCreatedProduct"/> + <actionGroup ref="DeleteCreatedProductActionGroup" stepKey="deleteCreatedProduct"/> </after> </test> From d7706e34471ce187cfd6e09426211bcefcad2296 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Wed, 15 Aug 2018 14:43:57 +0300 Subject: [PATCH 1014/1171] MAGETWO-93285: [2.3] Edit to Customer Address attribute 'State/Territory' will not show on customer address forms --- .../Customer/Block/Form/RegisterTest.php | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Form/RegisterTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Form/RegisterTest.php index 7f01508a9f821..cb07b1a401fdf 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Block/Form/RegisterTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Form/RegisterTest.php @@ -19,8 +19,9 @@ class RegisterTest extends \PHPUnit\Framework\TestCase /** * @magentoAppIsolation enabled * @magentoDbIsolation enabled + * @return void */ - public function testCompanyDefault() + public function testCompanyDefault(): void { /** @var \Magento\Customer\Block\Widget\Company $block */ $block = Bootstrap::getObjectManager()->create(Register::class) @@ -34,8 +35,9 @@ public function testCompanyDefault() /** * @magentoAppIsolation enabled * @magentoDbIsolation enabled + * @return void */ - public function testTelephoneDefault() + public function testTelephoneDefault(): void { /** @var \Magento\Customer\Block\Widget\Company $block */ $block = Bootstrap::getObjectManager()->create( @@ -50,8 +52,9 @@ public function testTelephoneDefault() /** * @magentoAppIsolation enabled * @magentoDbIsolation enabled + * @return void */ - public function testFaxDefault() + public function testFaxDefault(): void { /** @var \Magento\Customer\Block\Widget\Company $block */ $block = Bootstrap::getObjectManager()->create( @@ -66,8 +69,9 @@ public function testFaxDefault() /** * @magentoAppIsolation enabled * @magentoDbIsolation enabled + * @return void */ - public function testCompanyDisabled() + public function testCompanyDisabled(): void { /** @var \Magento\Customer\Model\Attribute $model */ $model = Bootstrap::getObjectManager()->create( @@ -89,8 +93,9 @@ public function testCompanyDisabled() /** * @magentoAppIsolation enabled * @magentoDbIsolation enabled + * @return void */ - public function testTelephoneDisabled() + public function testTelephoneDisabled(): void { /** @var \Magento\Customer\Model\Attribute $model */ $model = Bootstrap::getObjectManager()->create( @@ -112,8 +117,9 @@ public function testTelephoneDisabled() /** * @magentoAppIsolation enabled * @magentoDbIsolation enabled + * @return void */ - public function testFaxEnabled() + public function testFaxEnabled(): void { /** @var \Magento\Customer\Model\Attribute $model */ $model = Bootstrap::getObjectManager()->create( @@ -132,6 +138,9 @@ public function testFaxEnabled() $this->assertContains('title="Fax"', $block->toHtml()); } + /** + * @inheritdoc + */ protected function tearDown() { /** @var \Magento\Eav\Model\Config $eavConfig */ @@ -145,7 +154,7 @@ protected function tearDown() * @param Template $block * @return void */ - private function setAttributeDataProvider(Template $block) + private function setAttributeDataProvider(Template $block): void { $attributeData = Bootstrap::getObjectManager()->get(AddressAttributeData::class); $block->setAttributeData($attributeData); From 3ec14e7ed0a3aca8c3df08130432f8dfd5f9deef Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 15 Aug 2018 15:00:06 +0300 Subject: [PATCH 1015/1171] ENGCOM-2793: [Forwardport] 6305 - Resolved product custom option title save issue #17607 --- .../Model/Product/Option/Validator/DefaultValidator.php | 4 ++-- .../Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php | 2 +- .../Magento/Catalog/Api/_files/product_options_negative.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php index 78a52f0b27e28..5057f385d3a59 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php @@ -107,8 +107,8 @@ protected function isValidOptionTitle($title, $storeId) return true; } - // checking whether title is null and also changed is_empty to is_null - if ($title === null) { + // checking whether title is null and is empty string + if ($title === null || $title === '') { return false; } diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php index 1cd9a5c2b9c86..e83811042fd8b 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php @@ -208,7 +208,7 @@ public function testAddNegative($optionData) ]; if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) { - if (isset($optionDataPost['title']) && empty($optionDataPost['title'])) { + if ($optionDataPost['title'] === null || $optionDataPost['title'] === '') { $this->expectException('SoapFault'); $this->expectExceptionMessage('Missed values for option required fields'); } else { diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_negative.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_negative.php index 5d2737b3aa532..fdb09227bee69 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_negative.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_negative.php @@ -6,7 +6,7 @@ return [ 'empty_required_field' => [ - 'title' => '', + 'title' => null, 'type' => 'field', 'sort_order' => 1, 'is_require' => 1, @@ -54,7 +54,7 @@ 'price' => 10.0, 'price_type' => 'fixed', 'sku' => 'radio option 1 sku', - 'title' => '', + 'title' => null, 'sort_order' => 1, ], ], From 077bf2bba31d4f3236fba6fd8693245cd7be87a4 Mon Sep 17 00:00:00 2001 From: Jignesh Baldha <iamjignesh.b@gmail.com> Date: Wed, 15 Aug 2018 17:39:42 +0530 Subject: [PATCH 1016/1171] Changed according PHPUnit version. --- .../Cron/DeleteOutdatedPriceValuesTest.php | 59 ++++++++++++++----- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php b/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php index 304e203f228af..1e71e6f667c70 100644 --- a/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php @@ -90,20 +90,44 @@ public function testExecute() $attributeId = 15; $conditions = ['first', 'second']; - $this->scopeConfigMock->expects($this->once())->method('getValue')->with(Store::XML_PATH_PRICE_SCOPE) + $this->scopeConfigMock + ->expects($this->once()) + ->method('getValue') + ->with(Store::XML_PATH_PRICE_SCOPE) ->willReturn(Store::XML_PATH_PRICE_SCOPE); - $this->attributeRepositoryMock->expects($this->once())->method('get') + $this->attributeRepositoryMock + ->expects($this->once()) + ->method('get') ->with(ProductAttributeInterface::ENTITY_TYPE_CODE, ProductAttributeInterface::CODE_PRICE) ->willReturn($this->attributeMock); - $this->attributeMock->expects($this->once())->method('getId')->willReturn($attributeId); - $this->attributeMock->expects($this->once())->method('getBackend')->willReturn($this->attributeBackendMock); - $this->attributeBackendMock->expects($this->once())->method('getTable')->willReturn($table); - $this->resourceConnectionMock->expects($this->once())->method('getConnection')->willReturn($this->dbAdapterMock); - $this->dbAdapterMock->expects($this->exactly(2))->method('quoteInto')->willReturnMap([ - ['attribute_id = ?', $attributeId, null, null, $conditions[0]], - ['store_id != ?', Store::DEFAULT_STORE_ID, null, null, $conditions[1]], - ]); - $this->dbAdapterMock->expects($this->once())->method('delete')->with($table, $conditions); + $this->attributeMock + ->expects($this->once()) + ->method('getId') + ->willReturn($attributeId); + $this->attributeMock + ->expects($this->once()) + ->method('getBackend') + ->willReturn($this->attributeBackendMock); + $this->attributeBackendMock + ->expects($this->once()) + ->method('getTable') + ->willReturn($table); + $this->resourceConnectionMock + ->expects($this->once()) + ->method('getConnection') + ->willReturn($this->dbAdapterMock); + $this->dbAdapterMock + ->expects($this->exactly(2)) + ->method('quoteInto') + ->willReturnMap([ + ['attribute_id = ?', $attributeId, null, null, $conditions[0]], + ['store_id != ?', Store::DEFAULT_STORE_ID, null, null, $conditions[1]], + ]); + $this->dbAdapterMock + ->expects($this->once()) + ->method('delete') + ->with($table, $conditions); + $this->deleteOutdatedPriceValues->execute(); } @@ -115,10 +139,17 @@ public function testExecute() */ public function testExecutePriceConfigIsNotSetToGlobal() { - $this->scopeConfigMock->expects($this->once())->method('getValue')->with(Store::XML_PATH_PRICE_SCOPE) + $this->scopeConfigMock + ->expects($this->once()) + ->method('getValue') + ->with(Store::XML_PATH_PRICE_SCOPE) ->willReturn(null); - $this->attributeRepositoryMock->expects($this->never())->method('get'); - $this->dbAdapterMock->expects($this->never())->method('delete'); + $this->attributeRepositoryMock + ->expects($this->never()) + ->method('get'); + $this->dbAdapterMock + ->expects($this->never()) + ->method('delete'); $this->deleteOutdatedPriceValues->execute(); } From ab217245f21043c2c9d1378617351cf473d3abc0 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Wed, 15 Aug 2018 15:10:41 +0300 Subject: [PATCH 1017/1171] MAGETWO-91812: [Magento Cloud] - Issue with polluted database when updating product attributes through API --- .../code/Magento/Ui/base/js/form/ui-select.test.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js index 11ddfc724b29a..ac6e230e7ed1c 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js @@ -8,13 +8,12 @@ define([ 'underscore', 'uiRegistry', 'squire', - 'ko', - 'jquery' -], function (_, registry, Squire, ko, $) { + 'ko' +], function (_, registry, Squire, ko) { 'use strict'; describe('Magento_Ui/js/form/element/ui-select', function () { - var obj, originaljQueryAjax, + var obj, jq, originaljQueryAjax, injector = new Squire(), mocks = { 'Magento_Ui/js/lib/registry/registry': { @@ -37,8 +36,9 @@ define([ injector.mock(mocks); injector.require([ 'Magento_Ui/js/form/element/ui-select', + 'jquery', 'knockoutjs/knockout-es5' - ], function (Constr) { + ], function (Constr, $) { obj = new Constr({ provider: 'provName', name: '', @@ -52,13 +52,13 @@ define([ obj.value = ko.observableArray([]); obj.cacheOptions.plain = []; originaljQueryAjax = $.ajax; - + jq = $; done(); }); }); afterEach(function () { - $.ajax = originaljQueryAjax; + jq.ajax = originaljQueryAjax; injector.clean(); }); From c1dda198dcb2868552dceb5f15f0dc17f6a70e64 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Wed, 15 Aug 2018 11:45:40 +0300 Subject: [PATCH 1018/1171] MAGETWO-93761: [2.3] Currency conversion rate services do not work in admin panel --- .../Model/Currency/Import/FixerIo.php | 95 +++++++--- .../Model/Currency/Import/Webservicex.php | 92 --------- .../Model/Currency/Import/YahooFinance.php | 177 ------------------ .../Model/Currency/Import/FixerIoTest.php | 118 +++++++----- .../Currency/Import/YahooFinanceTest.php | 94 ---------- .../Test/Unit/Model/ObserverTest.php | 153 --------------- .../Directory/etc/adminhtml/system.xml | 17 +- app/code/Magento/Directory/etc/config.xml | 7 +- app/code/Magento/Directory/etc/di.xml | 8 - app/code/Magento/Directory/i18n/en_US.csv | 7 +- .../System/Currency/FetchRatesTest.php | 92 +-------- .../Magento/Directory/Model/ObserverTest.php | 90 --------- 12 files changed, 170 insertions(+), 780 deletions(-) delete mode 100644 app/code/Magento/Directory/Model/Currency/Import/Webservicex.php delete mode 100644 app/code/Magento/Directory/Model/Currency/Import/YahooFinance.php delete mode 100644 app/code/Magento/Directory/Test/Unit/Model/Currency/Import/YahooFinanceTest.php delete mode 100644 app/code/Magento/Directory/Test/Unit/Model/ObserverTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Directory/Model/ObserverTest.php diff --git a/app/code/Magento/Directory/Model/Currency/Import/FixerIo.php b/app/code/Magento/Directory/Model/Currency/Import/FixerIo.php index 3e0bd5368509e..af49d6daaf379 100644 --- a/app/code/Magento/Directory/Model/Currency/Import/FixerIo.php +++ b/app/code/Magento/Directory/Model/Currency/Import/FixerIo.php @@ -5,15 +5,18 @@ */ namespace Magento\Directory\Model\Currency\Import; +use Magento\Store\Model\ScopeInterface; + /** * Currency rate import model (From http://fixer.io/) */ -class FixerIo extends \Magento\Directory\Model\Currency\Import\AbstractImport +class FixerIo extends AbstractImport { /** * @var string */ - const CURRENCY_CONVERTER_URL = 'http://api.fixer.io/latest?base={{CURRENCY_FROM}}&symbols={{CURRENCY_TO}}'; + const CURRENCY_CONVERTER_URL = 'http://data.fixer.io/api/latest?access_key={{ACCESS_KEY}}' + . '&base={{CURRENCY_FROM}}&symbols={{CURRENCY_TO}}'; /** * Http Client Factory @@ -47,7 +50,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function fetchRates() { @@ -65,6 +68,13 @@ public function fetchRates() return $data; } + /** + * @inheritdoc + */ + protected function _convert($currencyFrom, $currencyTo) + { + } + /** * Return currencies convert rates in batch mode * @@ -73,11 +83,21 @@ public function fetchRates() * @param array $currenciesTo * @return array */ - private function convertBatch($data, $currencyFrom, $currenciesTo) + private function convertBatch(array $data, string $currencyFrom, array $currenciesTo): array { + $accessKey = $this->scopeConfig->getValue('currency/fixerio/api_key', ScopeInterface::SCOPE_STORE); + if (empty($accessKey)) { + $this->_messages[] = __('No API Key was specified or an invalid API Key was specified.'); + $data[$currencyFrom] = $this->makeEmptyResponse($currenciesTo); + return $data; + } + $currenciesStr = implode(',', $currenciesTo); - $url = str_replace('{{CURRENCY_FROM}}', $currencyFrom, self::CURRENCY_CONVERTER_URL); - $url = str_replace('{{CURRENCY_TO}}', $currenciesStr, $url); + $url = str_replace( + ['{{ACCESS_KEY}}', '{{CURRENCY_FROM}}', '{{CURRENCY_TO}}'], + [$accessKey, $currencyFrom, $currenciesStr], + self::CURRENCY_CONVERTER_URL + ); set_time_limit(0); try { @@ -86,6 +106,11 @@ private function convertBatch($data, $currencyFrom, $currenciesTo) ini_restore('max_execution_time'); } + if (!$this->validateResponse($response, $currencyFrom)) { + $data[$currencyFrom] = $this->makeEmptyResponse($currenciesTo); + return $data; + } + foreach ($currenciesTo as $currencyTo) { if ($currencyFrom == $currencyTo) { $data[$currencyFrom][$currencyTo] = $this->_numberFormat(1); @@ -110,25 +135,24 @@ private function convertBatch($data, $currencyFrom, $currenciesTo) * @param int $retry * @return array */ - private function getServiceResponse($url, $retry = 0) + private function getServiceResponse(string $url, int $retry = 0): array { /** @var \Magento\Framework\HTTP\ZendClient $httpClient */ $httpClient = $this->httpClientFactory->create(); $response = []; try { - $jsonResponse = $httpClient->setUri( - $url - )->setConfig( - [ - 'timeout' => $this->scopeConfig->getValue( - 'currency/fixerio/timeout', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ), - ] - )->request( - 'GET' - )->getBody(); + $jsonResponse = $httpClient->setUri($url) + ->setConfig( + [ + 'timeout' => $this->scopeConfig->getValue( + 'currency/fixerio/timeout', + ScopeInterface::SCOPE_STORE + ), + ] + ) + ->request('GET') + ->getBody(); $response = json_decode($jsonResponse, true); } catch (\Exception $e) { @@ -140,9 +164,38 @@ private function getServiceResponse($url, $retry = 0) } /** - * {@inheritdoc} + * Validates rates response. + * + * @param array $response + * @param string $baseCurrency + * @return bool */ - protected function _convert($currencyFrom, $currencyTo) + private function validateResponse(array $response, string $baseCurrency): bool + { + if ($response['success']) { + return true; + } + + $errorCodes = [ + 101 => __('No API Key was specified or an invalid API Key was specified.'), + 102 => __('The account this API request is coming from is inactive.'), + 105 => __('The "%1" is not allowed as base currency for your subscription plan.', $baseCurrency), + 201 => __('An invalid base currency has been entered.'), + ]; + + $this->_messages[] = $errorCodes[$response['error']['code']] ?? __('Currency rates can\'t be retrieved.'); + + return false; + } + + /** + * Creates array for provided currencies with empty rates. + * + * @param array $currenciesTo + * @return array + */ + private function makeEmptyResponse(array $currenciesTo): array { + return array_fill_keys($currenciesTo, null); } } diff --git a/app/code/Magento/Directory/Model/Currency/Import/Webservicex.php b/app/code/Magento/Directory/Model/Currency/Import/Webservicex.php deleted file mode 100644 index fa3c3a9018dbc..0000000000000 --- a/app/code/Magento/Directory/Model/Currency/Import/Webservicex.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Directory\Model\Currency\Import; - -/** - * Currency rate import model (From www.webservicex.net) - */ -class Webservicex extends \Magento\Directory\Model\Currency\Import\AbstractImport -{ - /** - * Currency converter url - */ - const CURRENCY_CONVERTER_URL = 'http://www.webservicex.net/CurrencyConvertor.asmx/ConversionRate' - . '?FromCurrency={{CURRENCY_FROM}}&ToCurrency={{CURRENCY_TO}}'; - - /** - * Http Client Factory - * - * @var \Magento\Framework\HTTP\ZendClientFactory - */ - protected $httpClientFactory; - - /** - * Core scope config - * - * @var \Magento\Framework\App\Config\ScopeConfigInterface - */ - private $scopeConfig; - - /** - * Constructor - * - * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Framework\HTTP\ZendClientFactory|null $zendClientFactory - */ - public function __construct( - \Magento\Directory\Model\CurrencyFactory $currencyFactory, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Framework\HTTP\ZendClientFactory $zendClientFactory = null - ) { - parent::__construct($currencyFactory); - $this->scopeConfig = $scopeConfig; - $this->httpClientFactory = $zendClientFactory ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\HTTP\ZendClientFactory::class); - } - - /** - * @param string $currencyFrom - * @param string $currencyTo - * @param int $retry - * @return float|null - */ - protected function _convert($currencyFrom, $currencyTo, $retry = 0) - { - $url = str_replace('{{CURRENCY_FROM}}', $currencyFrom, self::CURRENCY_CONVERTER_URL); - $url = str_replace('{{CURRENCY_TO}}', $currencyTo, $url); - /** @var \Magento\Framework\HTTP\ZendClient $httpClient */ - $httpClient = $this->httpClientFactory->create(); - - try { - $response = $httpClient->setUri( - $url - )->setConfig( - [ - 'timeout' => $this->scopeConfig->getValue( - 'currency/webservicex/timeout', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ), - ] - )->request( - 'GET' - )->getBody(); - - $xml = simplexml_load_string($response, null, LIBXML_NOERROR); - if (!$xml || (isset($xml[0]) && $xml[0] == -1)) { - $this->_messages[] = __('We can\'t retrieve a rate from %1.', $url); - return null; - } - return (double)$xml; - } catch (\Exception $e) { - if ($retry == 0) { - $this->_convert($currencyFrom, $currencyTo, 1); - } else { - $this->_messages[] = __('We can\'t retrieve a rate from %1.', $url); - } - } - } -} diff --git a/app/code/Magento/Directory/Model/Currency/Import/YahooFinance.php b/app/code/Magento/Directory/Model/Currency/Import/YahooFinance.php deleted file mode 100644 index 5f96b8627af65..0000000000000 --- a/app/code/Magento/Directory/Model/Currency/Import/YahooFinance.php +++ /dev/null @@ -1,177 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Directory\Model\Currency\Import; - -/** - * Currency rate import model (From http://query.yahooapis.com/) - */ -class YahooFinance extends \Magento\Directory\Model\Currency\Import\AbstractImport -{ - // @codingStandardsIgnoreStart - - // @codingStandardsIgnoreStart - private $currencyConverterUrl = 'http://query.yahooapis.com/v1/public/yql?format=json&q={{YQL_STRING}}&env=store://datatables.org/alltableswithkeys'; - // @codingStandardsIgnoreEnd - - // @codingStandardsIgnoreEnd - private $timeoutConfigPath = 'currency/yahoofinance/timeout'; - - /** - * Http Client Factory - * - * @var \Magento\Framework\HTTP\ZendClientFactory - */ - protected $httpClientFactory; - - /** - * Core scope config - * - * @var \Magento\Framework\App\Config\ScopeConfigInterface - */ - private $scopeConfig; - - /** - * Initialize dependencies - * - * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory - */ - public function __construct( - \Magento\Directory\Model\CurrencyFactory $currencyFactory, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory - ) { - parent::__construct($currencyFactory); - $this->scopeConfig = $scopeConfig; - $this->httpClientFactory = $httpClientFactory; - } - - /** - * {@inheritdoc} - */ - public function fetchRates() - { - $data = []; - $currencies = $this->_getCurrencyCodes(); - $defaultCurrencies = $this->_getDefaultCurrencyCodes(); - - foreach ($defaultCurrencies as $currencyFrom) { - if (!isset($data[$currencyFrom])) { - $data[$currencyFrom] = []; - } - $data = $this->convertBatch($data, $currencyFrom, $currencies); - ksort($data[$currencyFrom]); - } - return $data; - } - - /** - * Return currencies convert rates in batch mode - * - * @param array $data - * @param string $currencyFrom - * @param array $currenciesTo - * @return array - */ - private function convertBatch($data, $currencyFrom, $currenciesTo) - { - $url = $this->buildUrl($currencyFrom, $currenciesTo); - set_time_limit(0); - try { - $response = $this->getServiceResponse($url); - } finally { - ini_restore('max_execution_time'); - } - - foreach ($currenciesTo as $currencyTo) { - if ($currencyFrom == $currencyTo) { - $data[$currencyFrom][$currencyTo] = $this->_numberFormat(1); - } else { - if (empty($response[$currencyFrom . $currencyTo])) { - $this->_messages[] = __('We can\'t retrieve a rate from %1 for %2.', $url, $currencyTo); - $data[$currencyFrom][$currencyTo] = null; - } else { - $data[$currencyFrom][$currencyTo] = $this->_numberFormat( - (double)$response[$currencyFrom . $currencyTo] - ); - } - } - } - return $data; - } - - /** - * Get Fixer.io service response - * - * @param string $url - * @param int $retry - * @return array - */ - private function getServiceResponse($url, $retry = 0) - { - /** @var \Magento\Framework\HTTP\ZendClient $httpClient */ - $httpClient = $this->httpClientFactory->create(); - $response = []; - try { - $jsonResponse = $httpClient->setUri( - $url - )->setConfig( - [ - 'timeout' => $this->scopeConfig->getValue( - $this->timeoutConfigPath, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ), - ] - )->request( - 'GET' - )->getBody(); - - $jsonResponse = json_decode($jsonResponse, true); - if (!empty($jsonResponse['query']['results']['rate'])) { - $response = array_column($jsonResponse['query']['results']['rate'], 'Rate', 'id'); - } - } catch (\Exception $e) { - if ($retry == 0) { - $response = $this->getServiceResponse($url, 1); - } - } - return $response; - } - - /** - * {@inheritdoc} - */ - protected function _convert($currencyFrom, $currencyTo) - { - } - - /** - * Build url for Currency Service - * - * @param string $currencyFrom - * @param string[] $currenciesTo - * @return string - */ - private function buildUrl($currencyFrom, $currenciesTo) - { - $query = urlencode('select ') . '*' . urlencode(' from yahoo.finance.xchange where pair in ('); - $query .= - urlencode( - implode( - ',', - array_map( - function ($currencyTo) use ($currencyFrom) { - return '"' . $currencyFrom . $currencyTo . '"'; - }, - $currenciesTo - ) - ) - ); - $query .= ')'; - return str_replace('{{YQL_STRING}}', $query, $this->currencyConverterUrl); - } -} diff --git a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/FixerIoTest.php b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/FixerIoTest.php index c1c3f2fbacb03..7efcf0d62712a 100644 --- a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/FixerIoTest.php +++ b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/FixerIoTest.php @@ -3,89 +3,123 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Directory\Test\Unit\Model\Currency\Import; +use Magento\Directory\Model\Currency; +use Magento\Directory\Model\Currency\Import\FixerIo; +use Magento\Directory\Model\CurrencyFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\DataObject; +use Magento\Framework\HTTP\ZendClient; +use Magento\Framework\HTTP\ZendClientFactory; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * FixerIo Test + */ class FixerIoTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Directory\Model\Currency\Import\FixerIo + * @var FixerIo */ private $model; /** - * @var \Magento\Directory\Model\CurrencyFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CurrencyFactory|MockObject */ - private $currencyFactoryMock; + private $currencyFactory; /** - * @var \Magento\Framework\HTTP\ZendClientFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ZendClientFactory|MockObject */ - private $httpClientFactoryMock; + private $httpClientFactory; + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfig; + + /** + * @inheritdoc + */ protected function setUp() { - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $this->currencyFactoryMock = $this->getMockBuilder(\Magento\Directory\Model\CurrencyFactory::class) + $this->currencyFactory = $this->getMockBuilder(CurrencyFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->httpClientFactoryMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClientFactory::class) + $this->httpClientFactory = $this->getMockBuilder(ZendClientFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $scopeMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); - $this->model = $objectManagerHelper->getObject( - \Magento\Directory\Model\Currency\Import\FixerIo::class, - [ - 'currencyFactory' => $this->currencyFactoryMock, - 'scopeConfig' => $scopeMock, - 'httpClientFactory' => $this->httpClientFactoryMock - ] - ); + $this->model = new FixerIo($this->currencyFactory, $this->scopeConfig, $this->httpClientFactory); } - public function testFetchRates() + /** + * Test Fetch Rates + * + * @return void + */ + public function testFetchRates(): void { $currencyFromList = ['USD']; $currencyToList = ['EUR', 'UAH']; - $responseBody = '{"base":"USD","date":"2015-10-07","rates":{"EUR":0.9022}}'; + $responseBody = '{"success":"true","base":"USD","date":"2015-10-07","rates":{"EUR":0.9022}}'; $expectedCurrencyRateList = ['USD' => ['EUR' => 0.9022, 'UAH' => null]]; - $message = "We can't retrieve a rate from http://api.fixer.io/latest?base=USD&symbols=EUR,UAH for UAH."; + $message = "We can't retrieve a rate from " + . "http://data.fixer.io/api/latest?access_key=api_key&base=USD&symbols=EUR,UAH for UAH."; + + $this->scopeConfig->method('getValue') + ->withConsecutive( + ['currency/fixerio/api_key', 'store'], + ['currency/fixerio/timeout', 'store'] + ) + ->willReturnOnConsecutiveCalls('api_key', 100); - /** @var \Magento\Directory\Model\Currency|\PHPUnit_Framework_MockObject_MockObject $currencyMock */ - $currencyMock = $this->getMockBuilder(\Magento\Directory\Model\Currency::class) + /** @var Currency|MockObject $currency */ + $currency = $this->getMockBuilder(Currency::class) ->disableOriginalConstructor() - ->setMethods([]) ->getMock(); - /** @var \Magento\Framework\HTTP\ZendClient|\PHPUnit_Framework_MockObject_MockObject $currencyMock */ - $httpClientMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClient::class) + /** @var ZendClient|MockObject $httpClient */ + $httpClient = $this->getMockBuilder(ZendClient::class) ->disableOriginalConstructor() - ->setMethods([]) ->getMock(); - /** @var \Zend_Http_Response|\PHPUnit_Framework_MockObject_MockObject $currencyMock */ - $httpResponseMock = $this->getMockBuilder(\Zend_Http_Response::class) + /** @var DataObject|MockObject $currencyMock */ + $httpResponse = $this->getMockBuilder(DataObject::class) ->disableOriginalConstructor() - ->setMethods([]) + ->setMethods(['getBody']) ->getMock(); - $this->currencyFactoryMock->expects($this->any())->method('create')->willReturn($currencyMock); - $currencyMock->expects($this->once())->method('getConfigBaseCurrencies')->willReturn($currencyFromList); - $currencyMock->expects($this->once())->method('getConfigAllowCurrencies')->willReturn($currencyToList); - $this->httpClientFactoryMock->expects($this->any())->method('create')->willReturn($httpClientMock); - $httpClientMock->expects($this->atLeastOnce())->method('setUri')->willReturnSelf(); - $httpClientMock->expects($this->atLeastOnce())->method('setConfig')->willReturnSelf(); - $httpClientMock->expects($this->atLeastOnce())->method('request')->willReturn($httpResponseMock); - $httpResponseMock->expects($this->any())->method('getBody')->willReturn($responseBody); - $this->assertEquals($expectedCurrencyRateList, $this->model->fetchRates()); + $this->currencyFactory->method('create') + ->willReturn($currency); + $currency->method('getConfigBaseCurrencies') + ->willReturn($currencyFromList); + $currency->method('getConfigAllowCurrencies') + ->willReturn($currencyToList); + + $this->httpClientFactory->method('create') + ->willReturn($httpClient); + $httpClient->method('setUri') + ->willReturnSelf(); + $httpClient->method('setConfig') + ->willReturnSelf(); + $httpClient->method('request') + ->willReturn($httpResponse); + $httpResponse->method('getBody') + ->willReturn($responseBody); + + self::assertEquals($expectedCurrencyRateList, $this->model->fetchRates()); + $messages = $this->model->getMessages(); - $this->assertNotEmpty($messages); - $this->assertTrue(is_array($messages)); - $this->assertEquals($message, (string)$messages[0]); + self::assertNotEmpty($messages); + self::assertTrue(is_array($messages)); + self::assertEquals($message, (string)$messages[0]); } } diff --git a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/YahooFinanceTest.php b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/YahooFinanceTest.php deleted file mode 100644 index b7b9015ffdfe6..0000000000000 --- a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/YahooFinanceTest.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Directory\Test\Unit\Model\Currency\Import; - -class YahooFinanceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Directory\Model\Currency\Import\YahooFinance - */ - private $model; - - /** - * @var \Magento\Directory\Model\CurrencyFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $currencyFactoryMock; - - /** - * @var \Magento\Framework\HTTP\ZendClientFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $httpClientFactoryMock; - - protected function setUp() - { - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $this->currencyFactoryMock = $this->getMockBuilder(\Magento\Directory\Model\CurrencyFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->httpClientFactoryMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClientFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $scopeMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) - ->disableOriginalConstructor() - ->setMethods([]) - ->getMock(); - - $this->model = $objectManagerHelper->getObject( - \Magento\Directory\Model\Currency\Import\YahooFinance::class, - [ - 'currencyFactory' => $this->currencyFactoryMock, - 'scopeConfig' => $scopeMock, - 'httpClientFactory' => $this->httpClientFactoryMock - ] - ); - } - - public function testFetchRates() - { - $currencyFromList = ['USD']; - $currencyToList = ['EUR', 'UAH']; - $responseBody = '{"query":{"count":7,"created":"2016-04-05T16:46:55Z","lang":"en-US","results":{"rate":' - . '[{"id":"USDEUR","Name":"USD/EUR","Rate":"0.9022","Date":"4/5/2016"}]}}}'; - $expectedCurrencyRateList = ['USD' => ['EUR' => 0.9022, 'UAH' => null]]; - $message = "We can't retrieve a rate from http://query.yahooapis.com/v1/public/yql?format=json" - . "&q=select+*+from+yahoo.finance.xchange+where+pair+in+%28%22USDEUR%22%2C%22USDUAH%22)" - . "&env=store://datatables.org/alltableswithkeys for UAH."; - - /** @var \Magento\Directory\Model\Currency|\PHPUnit_Framework_MockObject_MockObject $currencyMock */ - $currencyMock = $this->getMockBuilder(\Magento\Directory\Model\Currency::class) - ->disableOriginalConstructor() - ->setMethods([]) - ->getMock(); - /** @var \Magento\Framework\HTTP\ZendClient|\PHPUnit_Framework_MockObject_MockObject $currencyMock */ - $httpClientMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClient::class) - ->disableOriginalConstructor() - ->setMethods([]) - ->getMock(); - /** @var \Zend_Http_Response|\PHPUnit_Framework_MockObject_MockObject $currencyMock */ - $httpResponseMock = $this->getMockBuilder('Zend_Http_Response') - ->disableOriginalConstructor() - ->setMethods([]) - ->getMock(); - $this->currencyFactoryMock->expects($this->any())->method('create')->willReturn($currencyMock); - $currencyMock->expects($this->once())->method('getConfigBaseCurrencies')->willReturn($currencyFromList); - $currencyMock->expects($this->once())->method('getConfigAllowCurrencies')->willReturn($currencyToList); - $this->httpClientFactoryMock->expects($this->any())->method('create')->willReturn($httpClientMock); - $httpClientMock->expects($this->atLeastOnce())->method('setUri')->willReturnSelf(); - $httpClientMock->expects($this->atLeastOnce())->method('setConfig')->willReturnSelf(); - $httpClientMock->expects($this->atLeastOnce())->method('request')->willReturn($httpResponseMock); - $httpResponseMock->expects($this->any())->method('getBody')->willReturn($responseBody); - - $this->assertEquals($expectedCurrencyRateList, $this->model->fetchRates()); - $messages = $this->model->getMessages(); - $this->assertNotEmpty($messages); - $this->assertTrue(is_array($messages)); - $this->assertEquals($message, (string)$messages[0]); - } -} diff --git a/app/code/Magento/Directory/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Directory/Test/Unit/Model/ObserverTest.php deleted file mode 100644 index 5fb7271a411ed..0000000000000 --- a/app/code/Magento/Directory/Test/Unit/Model/ObserverTest.php +++ /dev/null @@ -1,153 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Directory\Test\Unit\Model; - -use Magento\Store\Model\ScopeInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Directory\Model\Observer; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class ObserverTest extends \PHPUnit\Framework\TestCase -{ - /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ - protected $objectManager; - - /** @var Observer */ - protected $observer; - - /** @var \Magento\Directory\Model\Currency\Import\Factory|\PHPUnit_Framework_MockObject_MockObject */ - protected $importFactory; - - /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $scopeConfig; - - /** @var \Magento\Framework\Mail\Template\TransportBuilder|\PHPUnit_Framework_MockObject_MockObject */ - protected $transportBuilder; - - /** @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $storeManager; - - /** @var \Magento\Directory\Model\CurrencyFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected $currencyFactory; - - /** @var \Magento\Framework\Translate\Inline\StateInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $inlineTranslation; - - protected function setUp() - { - $this->objectManager = new ObjectManager($this); - - $this->importFactory = $this->getMockBuilder(\Magento\Directory\Model\Currency\Import\Factory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->scopeConfig = $this->getMockBuilder(\Magento\Framework\App\MutableScopeConfig::class) - ->disableOriginalConstructor() - ->setMethods(['getValue']) - ->getMock(); - $this->transportBuilder = $this->getMockBuilder(\Magento\Framework\Mail\Template\TransportBuilder::class) - ->disableOriginalConstructor() - ->getMock(); - $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManager::class) - ->disableOriginalConstructor() - ->getMock(); - $this->currencyFactory = $this->getMockBuilder(\Magento\Directory\Model\CurrencyFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->inlineTranslation = $this->getMockBuilder(\Magento\Framework\Translate\Inline\StateInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->observer = $this->objectManager->getObject( - \Magento\Directory\Model\Observer::class, - [ - 'importFactory' => $this->importFactory, - 'scopeConfig' => $this->scopeConfig, - 'transportBuilder' => $this->transportBuilder, - 'storeManager' => $this->storeManager, - 'currencyFactory' => $this->currencyFactory, - 'inlineTranslation' => $this->inlineTranslation - ] - ); - } - - public function testScheduledUpdateCurrencyRates() - { - $this->scopeConfig - ->expects($this->at(0)) - ->method('getValue') - ->with(Observer::IMPORT_ENABLE, ScopeInterface::SCOPE_STORE) - ->will($this->returnValue(1)); - $this->scopeConfig - ->expects($this->at(1)) - ->method('getValue') - ->with(Observer::CRON_STRING_PATH, ScopeInterface::SCOPE_STORE) - ->will($this->returnValue('cron-path')); - $this->scopeConfig - ->expects($this->at(2)) - ->method('getValue') - ->with(Observer::IMPORT_SERVICE, ScopeInterface::SCOPE_STORE) - ->will($this->returnValue('import-service')); - $importInterfaceMock = $this->getMockBuilder(\Magento\Directory\Model\Currency\Import\Webservicex::class) - ->disableOriginalConstructor() - ->setMethods(['fetchRates', 'getMessages']) - ->getMock(); - $importInterfaceMock->expects($this->once()) - ->method('fetchRates') - ->will($this->returnValue([])); - $importInterfaceMock->expects($this->once()) - ->method('getMessages') - ->will($this->returnValue([])); - - $this->importFactory - ->expects($this->once()) - ->method('create') - ->with('import-service') - ->will($this->returnValue($importInterfaceMock)); - - $currencyMock = $this->getMockBuilder(\Magento\Directory\Model\Currency::class) - ->disableOriginalConstructor() - ->setMethods(['saveRates', '__wakeup', '__sleep']) - ->getMock(); - $currencyMock->expects($this->once()) - ->method('saveRates') - ->will($this->returnValue(null)); - $this->currencyFactory - ->expects($this->once()) - ->method('create') - ->will($this->returnValue($currencyMock)); - - $this->observer->scheduledUpdateCurrencyRates(null); - } - - /** - * @expectedException \Exception - */ - public function testScheduledUpdateCurrencyRatesThrowsException() - { - $this->scopeConfig->expects($this->exactly(3)) - ->method('getValue') - ->willReturnMap( - [ - [Observer::IMPORT_ENABLE, ScopeInterface::SCOPE_STORE, null, 1], - [Observer::CRON_STRING_PATH, ScopeInterface::SCOPE_STORE, null, 'cron-path'], - [Observer::IMPORT_SERVICE, ScopeInterface::SCOPE_STORE, null, 'import-service'] - ] - ); - - $this->importFactory - ->expects($this->once()) - ->method('create') - ->with('import-service') - ->willThrowException(new \Exception()); - - $this->observer->scheduledUpdateCurrencyRates(null); - } -} diff --git a/app/code/Magento/Directory/etc/adminhtml/system.xml b/app/code/Magento/Directory/etc/adminhtml/system.xml index cae3b1c41db36..ec5fa35b6a152 100644 --- a/app/code/Magento/Directory/etc/adminhtml/system.xml +++ b/app/code/Magento/Directory/etc/adminhtml/system.xml @@ -34,21 +34,14 @@ <can_be_empty>1</can_be_empty> </field> </group> - <group id="yahoofinance" translate="label" sortOrder="33" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Yahoo Finance Exchange</label> - <field id="timeout" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Connection Timeout in Seconds</label> - </field> - </group> <group id="fixerio" translate="label" sortOrder="35" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Fixer.io</label> - <field id="timeout" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Connection Timeout in Seconds</label> + <field id="api_key" translate="label" type="obscure" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0"> + <label>API Key</label> + <config_path>currency/fixerio/api_key</config_path> + <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - </group> - <group id="webservicex" translate="label" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Webservicex</label> - <field id="timeout" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="timeout" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Connection Timeout in Seconds</label> </field> </group> diff --git a/app/code/Magento/Directory/etc/config.xml b/app/code/Magento/Directory/etc/config.xml index de3ff626bc12c..276d7088cc2ea 100644 --- a/app/code/Magento/Directory/etc/config.xml +++ b/app/code/Magento/Directory/etc/config.xml @@ -18,15 +18,10 @@ <base>USD</base> <default>USD</default> </options> - <yahoofinance> - <timeout>100</timeout> - </yahoofinance> <fixerio> <timeout>100</timeout> + <api_key backend_model="Magento\Config\Model\Config\Backend\Encrypted" /> </fixerio> - <webservicex> - <timeout>100</timeout> - </webservicex> <currencyconverterapi> <timeout>100</timeout> </currencyconverterapi> diff --git a/app/code/Magento/Directory/etc/di.xml b/app/code/Magento/Directory/etc/di.xml index 4d0e51ab9f45a..50cd65cc5045c 100644 --- a/app/code/Magento/Directory/etc/di.xml +++ b/app/code/Magento/Directory/etc/di.xml @@ -10,14 +10,6 @@ <type name="Magento\Directory\Model\Currency\Import\Config"> <arguments> <argument name="servicesConfig" xsi:type="array"> - <item name="yahoofinance" xsi:type="array"> - <item name="label" xsi:type="string" translatable="true">Yahoo Finance Exchange</item> - <item name="class" xsi:type="string">Magento\Directory\Model\Currency\Import\YahooFinance</item> - </item> - <item name="webservicex" xsi:type="array"> - <item name="label" xsi:type="string" translatable="true">Webservicex</item> - <item name="class" xsi:type="string">Magento\Directory\Model\Currency\Import\Webservicex</item> - </item> <item name="fixerio" xsi:type="array"> <item name="label" xsi:type="string" translatable="true">Fixer.io</item> <item name="class" xsi:type="string">Magento\Directory\Model\Currency\Import\FixerIo</item> diff --git a/app/code/Magento/Directory/i18n/en_US.csv b/app/code/Magento/Directory/i18n/en_US.csv index 00e32ab8c8501..3dcd2ceebf134 100644 --- a/app/code/Magento/Directory/i18n/en_US.csv +++ b/app/code/Magento/Directory/i18n/en_US.csv @@ -30,10 +30,8 @@ Continue,Continue " "Default Display Currency","Default Display Currency" "Allowed Currencies","Allowed Currencies" -"Yahoo Finance Exchange","Yahoo Finance Exchange" "Connection Timeout in Seconds","Connection Timeout in Seconds" Fixer.io,Fixer.io -Webservicex,Webservicex "Scheduled Import Settings","Scheduled Import Settings" Enabled,Enabled "Error Email Recipient","Error Email Recipient" @@ -49,3 +47,8 @@ Service,Service "State is Required for","State is Required for" "Allow to Choose State if It is Optional for Country","Allow to Choose State if It is Optional for Country" "Weight Unit","Weight Unit" +"No API Key was specified or an invalid API Key was specified.","No API Key was specified or an invalid API Key was specified." +"The account this API request is coming from is inactive.","The account this API request is coming from is inactive." +"The """%1"" is not allowed as base currency for your subscription plan.","The """%1"" is not allowed as base currency for your subscription plan." +"An invalid base currency has been entered.","An invalid base currency has been entered." +"Currency rates can't be retrieved.","Currency rates can't be retrieved." diff --git a/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/FetchRatesTest.php b/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/FetchRatesTest.php index ce83b7d3a6e06..8e25e5960a4b5 100644 --- a/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/FetchRatesTest.php +++ b/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/FetchRatesTest.php @@ -6,12 +6,17 @@ namespace Magento\CurrencySymbol\Controller\Adminhtml\System\Currency; +/** + * Fetch Rates Test + */ class FetchRatesTest extends \Magento\TestFramework\TestCase\AbstractBackendController { /** * Test fetch action without service + * + * @return void */ - public function testFetchRatesActionWithoutService() + public function testFetchRatesActionWithoutService(): void { $request = $this->getRequest(); $request->setParam( @@ -28,8 +33,10 @@ public function testFetchRatesActionWithoutService() /** * Test save action with nonexistent service + * + * @return void */ - public function testFetchRatesActionWithNonexistentService() + public function testFetchRatesActionWithNonexistentService(): void { $request = $this->getRequest(); $request->setParam( @@ -43,85 +50,4 @@ public function testFetchRatesActionWithNonexistentService() \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); } - - /** - * Test save action with nonexistent service - */ - public function testFetchRatesActionWithServiceErrors() - { - $this->runActionWithMockedImportService(['We can\'t retrieve a rate from url']); - - $this->assertSessionMessages( - $this->contains('Click "Save" to apply the rates we found.'), - \Magento\Framework\Message\MessageInterface::TYPE_WARNING - ); - } - - /** - * Test save action with nonexistent service - */ - public function testFetchRatesActionWithoutServiceErrors() - { - $this->runActionWithMockedImportService(); - - $this->assertSessionMessages( - $this->contains('Click "Save" to apply the rates we found.'), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - } - - /** - * Run fetchRates with mocked external import service - * - * @param array $messages messages from external import service - */ - protected function runActionWithMockedImportService(array $messages = []) - { - $importServiceMock = $this->getMockBuilder(\Magento\Directory\Model\Currency\Import\Webservicex::class) - ->disableOriginalConstructor() - ->getMock(); - - $importServiceMock->method('fetchRates') - ->willReturn(['USD' => ['USD' => 1]]); - - $importServiceMock->method('getMessages') - ->willReturn($messages); - - $backendSessionMock = $this->getMockBuilder(\Magento\Backend\Model\Session::class) - ->disableOriginalConstructor() - ->getMock(); - - $importServiceFactoryMock = $this->getMockBuilder(\Magento\Directory\Model\Currency\Import\Factory::class) - ->disableOriginalConstructor() - ->getMock(); - - $importServiceFactoryMock->method('create') - ->willReturn($importServiceMock); - - $objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $objectManagerMap = [ - [\Magento\Directory\Model\Currency\Import\Factory::class, $importServiceFactoryMock], - [\Magento\Backend\Model\Session::class, $backendSessionMock] - ]; - - $objectManagerMock->method('get') - ->will($this->returnValueMap($objectManagerMap)); - - $context = $this->_objectManager->create( - \Magento\Backend\App\Action\Context::class, - ["objectManager" => $objectManagerMock] - ); - $registry = $this->_objectManager->get(\Magento\Framework\Registry::class); - - $this->getRequest()->setParam('rate_services', 'webservicex'); - - $action = new \Magento\CurrencySymbol\Controller\Adminhtml\System\Currency\FetchRates( - $context, - $registry - ); - $action->execute(); - } } diff --git a/dev/tests/integration/testsuite/Magento/Directory/Model/ObserverTest.php b/dev/tests/integration/testsuite/Magento/Directory/Model/ObserverTest.php deleted file mode 100644 index c3549a60e2444..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Directory/Model/ObserverTest.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Directory\Model; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Store\Model\ScopeInterface; -use Magento\TestFramework\Helper\Bootstrap; - -/** - * Integration test for \Magento\Directory\Model\Observer - */ -class ObserverTest extends \PHPUnit\Framework\TestCase -{ - /** @var ObjectManagerInterface */ - protected $objectManager; - - /** @var Observer */ - protected $observer; - - /** @var \Magento\Framework\App\MutableScopeConfig */ - protected $scopeConfig; - - /** @var string */ - protected $baseCurrency = 'USD'; - - /** @var string */ - protected $baseCurrencyPath = 'currency/options/base'; - - /** @var string */ - protected $allowedCurrenciesPath = 'currency/options/allow'; - - /** @var \Magento\Config\Model\ResourceModel\Config */ - protected $configResource; - - public function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->scopeConfig = $this->objectManager->create(\Magento\Framework\App\MutableScopeConfig::class); - $this->scopeConfig->setValue(Observer::IMPORT_ENABLE, 1, ScopeInterface::SCOPE_STORE); - $this->scopeConfig->setValue(Observer::CRON_STRING_PATH, 'cron-string-path', ScopeInterface::SCOPE_STORE); - $this->scopeConfig->setValue(Observer::IMPORT_SERVICE, 'webservicex', ScopeInterface::SCOPE_STORE); - - $this->configResource = $this->objectManager->get(\Magento\Config\Model\ResourceModel\Config::class); - $this->configResource->saveConfig( - $this->baseCurrencyPath, - $this->baseCurrency, - ScopeInterface::SCOPE_STORE, - 0 - ); - - $this->observer = $this->objectManager->create(\Magento\Directory\Model\Observer::class); - } - - public function testScheduledUpdateCurrencyRates() - { - //skipping test if service is unavailable - $url = str_replace( - '{{CURRENCY_FROM}}', - 'USD', - \Magento\Directory\Model\Currency\Import\Webservicex::CURRENCY_CONVERTER_URL - ); - $url = str_replace('{{CURRENCY_TO}}', 'GBP', $url); - try { - file_get_contents($url); - } catch (\PHPUnit\Framework\Exception $e) { - $this->markTestSkipped('http://www.webservicex.net is unavailable '); - } - - $allowedCurrencies = 'USD,GBP,EUR'; - $this->configResource->saveConfig( - $this->allowedCurrenciesPath, - $allowedCurrencies, - ScopeInterface::SCOPE_STORE, - 0 - ); - $this->observer->scheduledUpdateCurrencyRates(null); - /** @var Currency $currencyResource */ - $currencyResource = $this->objectManager - ->create(\Magento\Directory\Model\CurrencyFactory::class) - ->create() - ->getResource(); - $rates = $currencyResource->getCurrencyRates($this->baseCurrency, explode(',', $allowedCurrencies)); - $this->assertNotEmpty($rates); - } -} From f833fdab77cacef9b734e424c8301ebcc6061c16 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Wed, 15 Aug 2018 18:39:00 +0530 Subject: [PATCH 1019/1171] Removed commented code from lib files --- .../Magento/Framework/Code/Generator/EntityAbstract.php | 1 + .../Magento/Framework/ObjectManager/Code/Generator/Factory.php | 2 ++ .../Magento/Framework/ObjectManager/Code/Generator/Proxy.php | 2 ++ 3 files changed, 5 insertions(+) diff --git a/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php b/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php index b1a9b768f8c43..3efe110ccf193 100644 --- a/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php +++ b/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php @@ -183,6 +183,7 @@ protected function _getDefaultResultClassName($modelClassName) */ protected function _getClassProperties() { + // protected $_objectManager = null; $objectManager = [ 'name' => '_objectManager', 'visibility' => 'protected', diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Factory.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Factory.php index 07987cd113c5c..6186bffd4ca68 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Factory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Factory.php @@ -21,6 +21,7 @@ protected function _getClassProperties() { $properties = parent::_getClassProperties(); + // protected $_instanceName = null; $properties[] = [ 'name' => '_instanceName', 'visibility' => 'protected', @@ -68,6 +69,7 @@ protected function _getClassMethods() { $construct = $this->_getDefaultConstructorDefinition(); + // public function create(array $data = array()) $create = [ 'name' => 'create', 'parameters' => [['name' => 'data', 'type' => 'array', 'defaultValue' => []]], diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php index 7c12b4c82f12e..96595bc7a073b 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php +++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php @@ -37,6 +37,7 @@ protected function _getClassProperties() { $properties = parent::_getClassProperties(); + // protected $_instanceName = null; $properties[] = [ 'name' => '_instanceName', 'visibility' => 'protected', @@ -55,6 +56,7 @@ protected function _getClassProperties() ], ]; + // protected $_shared = null; $properties[] = [ 'name' => '_isShared', 'visibility' => 'protected', From eb173dd1b90cf3c729ec10dd115652b56586108a Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Wed, 15 Aug 2018 09:10:41 -0500 Subject: [PATCH 1020/1171] MAGETWO-93979: Required Field Indicators (Asterisks) Are Gone From Magento UI --- .../Magento/Ui/view/base/web/templates/form/field.html | 8 +++++--- .../Magento/backend/web/css/source/forms/_fields.less | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/templates/form/field.html b/app/code/Magento/Ui/view/base/web/templates/form/field.html index 24ed194d666ee..ed84e158819a2 100644 --- a/app/code/Magento/Ui/view/base/web/templates/form/field.html +++ b/app/code/Magento/Ui/view/base/web/templates/form/field.html @@ -8,9 +8,11 @@ visible="visible" css="$data.additionalClasses" attr="'data-index': index"> - <span class="admin__field-label" if="$data.label" visible="$data.labelVisible"> - <label translate="label" attr="'data-config-scope': $data.scopeLabel, for: uid" /> - </span> + <div class="admin__field-label"> + <label if="$data.label" visible="$data.labelVisible" attr="for: uid"> + <span translate="label" attr="'data-config-scope': $data.scopeLabel" /> + </label> + </div> <div class="admin__field-control" css="'_with-tooltip': $data.tooltip, '_with-reset': $data.showFallbackReset && $data.isDifferedFromDefault"> <render args="elementTmpl" ifnot="hasAddons()"/> diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less index 8f0d4c33a8ee7..c87b8f2ca5cc0 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less @@ -210,7 +210,7 @@ overflow: hidden; } - label { + span { display: inline-block; line-height: @field-label__line-height; vertical-align: middle; @@ -228,7 +228,7 @@ .required > &, // ToDo UI: change classes 'required' to '_required'. ._required > & { - > span { + span { &:after { color: @validation__color; content: '*'; @@ -515,7 +515,7 @@ position: absolute; top: 0; - label { + span { &:before { display: block; } @@ -530,7 +530,7 @@ } & > .admin__field-label { - label { + span { &:before { display: none; } From a1eb8a6cb58d1b0f5c55c83110ff1a6d73092785 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Wed, 15 Aug 2018 12:43:53 -0500 Subject: [PATCH 1021/1171] MAGETWO-93666: Wrong backup file is getting deleted when user has DB, DB/Media and System backup files and tries to delete System. - Skip test due to build limitation --- .../Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml b/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml index a1b0f5197eeb9..b83434f7ad1de 100644 --- a/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml +++ b/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-94176"/> <group value="backup"/> + <group value="skip"/> </annotations> <!--Login to admin area--> From 8d68a8cbfd0827a5d3b288a293e640153572528f Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Wed, 15 Aug 2018 15:57:47 -0500 Subject: [PATCH 1022/1171] MAGETWO-93979: Required Field Indicators (Asterisks) Are Gone From Magento UI - fix function test failures --- .../Product/Edit/Section/ProductDetails/NewCategoryIds.xml | 2 +- .../Catalog/Test/Block/Adminhtml/Product/ProductForm.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Section/ProductDetails/NewCategoryIds.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Section/ProductDetails/NewCategoryIds.xml index f318287720c38..87618777d7907 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Section/ProductDetails/NewCategoryIds.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Section/ProductDetails/NewCategoryIds.xml @@ -11,7 +11,7 @@ <selector>[name="name"]</selector> </name> <parent_category> - <selector>div[data-index="parent"]>div</selector> + <selector>div[data-index="parent"]>div[class="admin__field-control"]</selector> <class>Magento\Catalog\Test\Block\Adminhtml\Product\Edit\Section\ProductDetails\CategoryIds</class> </parent_category> </fields> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php index 61142adc8f9b3..fa2b66d3e9698 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php @@ -28,7 +28,7 @@ class ProductForm extends FormSections * * @var string */ - protected $attribute = './/*[contains(@class,"label")]/label[text()="%s"]'; + protected $attribute = './/*[contains(@class,"label")]//span[text()="%s"]'; /** * Product new from date field on the product form From fcd6f2771172ab7b3561c4ed1aca173670dc7435 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Wed, 15 Aug 2018 14:57:55 -0500 Subject: [PATCH 1023/1171] MAGETWO-93978: Footer Overlaps Storefront Page Content In Mobile View --- .../Magento_Theme/web/css/source/_module.less | 20 +++++++++---------- .../Magento/blank/web/css/source/_layout.less | 4 ---- .../Magento_Theme/web/css/source/_module.less | 20 +++++++++---------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less index f1e1529d9820f..8df5556e97721 100644 --- a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less @@ -52,6 +52,16 @@ .lib-css(background-color, @page__background-color); } + .page-wrapper { + .lib-vendor-prefix-display(flex); + .lib-vendor-prefix-flex-direction(column); + min-height: 100vh; // Stretch content area for sticky footer + } + + .page-main { + .lib-vendor-prefix-flex-grow(1); + } + // // Header // --------------------------------------------- @@ -279,17 +289,7 @@ // _____________________________________________ .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { - - html, - body { - height: 100%; // Stretch screen area for sticky footer - } - .page-wrapper { - .lib-vendor-prefix-display(flex); - .lib-vendor-prefix-flex-direction(column); - min-height: 100%; // Stretch content area for sticky footer - > .breadcrumbs, > .top-container, > .widget { diff --git a/app/design/frontend/Magento/blank/web/css/source/_layout.less b/app/design/frontend/Magento/blank/web/css/source/_layout.less index a81a3fa8d1f17..24743c665b460 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_layout.less +++ b/app/design/frontend/Magento/blank/web/css/source/_layout.less @@ -94,10 +94,6 @@ .page-main { width: 100%; - - .lib-vendor-prefix-flex-grow(1); - .lib-vendor-prefix-flex-shrink(0); - .lib-vendor-prefix-flex-basis(auto); } .columns { diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less index af6cfa8605f6d..bafe1be57ee35 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less @@ -67,6 +67,16 @@ .lib-css(background-color, @page__background-color); } + .page-wrapper { + .lib-vendor-prefix-display(flex); + .lib-vendor-prefix-flex-direction(column); + min-height: 100vh; // Stretch content area for sticky footer + } + + .page-main { + .lib-vendor-prefix-flex-grow(1); + } + // // Header // --------------------------------------------- @@ -306,7 +316,6 @@ // // Widgets // --------------------------------------------- - .sidebar { .widget.block:not(:last-child), .widget:not(:last-child) { @@ -411,12 +420,6 @@ } } } - .page-footer, - .copyright { - bottom: 0; - position: absolute; - width: 100%; - } } .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) { @@ -614,10 +617,7 @@ } .page-wrapper { - .lib-vendor-prefix-display(flex); - .lib-vendor-prefix-flex-direction(column); margin: 0; - min-height: 100%; // Stretch content area for sticky footer position: relative; transition: margin .3s ease-out 0s; From cba40fae6bffa0637f4b46052b9c9a2a3a81eac6 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Wed, 15 Aug 2018 16:07:15 -0500 Subject: [PATCH 1024/1171] MAGETWO-91678: Minimum Order amount required in Admin orders --- app/code/Magento/Quote/etc/di.xml | 10 +- .../Quote/Model/QuoteValidatorTest.php | 174 ++++++++++++++++++ 2 files changed, 179 insertions(+), 5 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml index b8da73b91d043..3ad6c9c40e359 100644 --- a/app/code/Magento/Quote/etc/di.xml +++ b/app/code/Magento/Quote/etc/di.xml @@ -99,8 +99,8 @@ <type name="Magento\Quote\Model\ValidationRules\QuoteValidationComposite"> <arguments> <argument name="validationRules" xsi:type="array"> - <item name="ShippingAddressValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\ShippingAddressValidationRule</item> <item name="AllowedCountryValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\AllowedCountryValidationRule</item> + <item name="ShippingAddressValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\ShippingAddressValidationRule</item> <item name="ShippingMethodValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\ShippingMethodValidationRule</item> <item name="BillingAddressValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\BillingAddressValidationRule</item> <item name="PaymentMethodValidationRule" xsi:type="object">Magento\Quote\Model\ValidationRules\PaymentMethodValidationRule</item> @@ -108,14 +108,14 @@ </argument> </arguments> </type> - <type name="Magento\Quote\Model\ValidationRules\ShippingAddressValidationRule"> + <type name="Magento\Quote\Model\ValidationRules\AllowedCountryValidationRule"> <arguments> - <argument name="defaultMessage" xsi:type="string">Please check the shipping address information.</argument> + <argument name="defaultMessage" xsi:type="string">Some addresses can't be used due to the configurations for specific countries.</argument> </arguments> </type> - <type name="Magento\Quote\Model\ValidationRules\AllowedCountryValidationRule"> + <type name="Magento\Quote\Model\ValidationRules\ShippingAddressValidationRule"> <arguments> - <argument name="defaultMessage" xsi:type="string">Some addresses can't be used due to the configurations for specific countries.</argument> + <argument name="defaultMessage" xsi:type="string">Please check the shipping address information.</argument> </arguments> </type> <type name="Magento\Quote\Model\ValidationRules\ShippingMethodValidationRule"> diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php new file mode 100644 index 0000000000000..981cd106853ca --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php @@ -0,0 +1,174 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Quote\Model; + +use Magento\Quote\Api\Data\AddressInterface; +use Magento\Quote\Model\Quote\Address\Rate; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Class QuoteValidatorTest + * + * @magentoDbIsolation enabled + */ +class QuoteValidatorTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var QuoteValidator + */ + private $quoteValidator; + + /** + * @inheritdoc + */ + public function setUp() + { + $this->quoteValidator = Bootstrap::getObjectManager()->create(QuoteValidator::class); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Please check the shipping address information. + */ + public function testValidateBeforeSubmitShippingAddressInvalid() + { + $quote = $this->getQuote(); + $quote->getShippingAddress()->setPostcode(''); + + $this->quoteValidator->validateBeforeSubmit($quote); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Some addresses can't be used due to the configurations for specific countries. + */ + public function testValidateBeforeSubmitCountryIsNotAllowed() + { + /** @magentoConfigFixture does not allow to change the value for the website scope */ + Bootstrap::getObjectManager()->get( + \Magento\Framework\App\Config\MutableScopeConfigInterface::class + )->setValue( + 'general/country/allow', + 'US', + \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITES + ); + $quote = $this->getQuote(); + $quote->getShippingAddress()->setCountryId('AF'); + + $this->quoteValidator->validateBeforeSubmit($quote); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage The shipping method is missing. Select the shipping method and try again. + */ + public function testValidateBeforeSubmitShippingMethodInvalid() + { + $quote = $this->getQuote(); + $quote->getShippingAddress()->setShippingMethod('NONE'); + + $this->quoteValidator->validateBeforeSubmit($quote); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Please check the billing address information. + */ + public function testValidateBeforeSubmitBillingAddressInvalid() + { + $quote = $this->getQuote(); + $quote->getBillingAddress()->setTelephone(''); + + $this->quoteValidator->validateBeforeSubmit($quote); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Enter a valid payment method and try again. + */ + public function testValidateBeforeSubmitPaymentMethodInvalid() + { + $quote = $this->getQuote(); + $quote->getPayment()->setMethod(''); + + $this->quoteValidator->validateBeforeSubmit($quote); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @magentoConfigFixture current_store sales/minimum_order/active 1 + * @magentoConfigFixture current_store sales/minimum_order/amount 100 + */ + public function testValidateBeforeSubmitMinimumAmountInvalid() + { + $quote = $this->getQuote(); + + $this->quoteValidator->validateBeforeSubmit($quote); + } + + /** + * @return void + */ + public function testValidateBeforeSubmitWithoutMinimumOrderAmount() + { + $this->quoteValidator->validateBeforeSubmit($this->getQuote()); + } + + /** + * @return Quote + */ + private function getQuote(): Quote + { + /** @var Quote $quote */ + $quote = Bootstrap::getObjectManager()->create(Quote::class); + + /** @var AddressInterface $billingAddress */ + $billingAddress = Bootstrap::getObjectManager()->create(AddressInterface::class); + $billingAddress->setFirstname('Joe') + ->setLastname('Doe') + ->setCountryId('US') + ->setRegion('TX') + ->setCity('Austin') + ->setStreet('1000 West Parmer Line') + ->setPostcode('11501') + ->setTelephone('123456789'); + $quote->setBillingAddress($billingAddress); + + /** @var AddressInterface $shippingAddress */ + $shippingAddress = Bootstrap::getObjectManager()->create(AddressInterface::class); + $shippingAddress->setFirstname('Joe') + ->setLastname('Doe') + ->setCountryId('US') + ->setRegion('TX') + ->setCity('Austin') + ->setStreet('1000 West Parmer Line') + ->setPostcode('11501') + ->setTelephone('123456789'); + $quote->setShippingAddress($shippingAddress); + + $quote->getShippingAddress() + ->setShippingMethod('flatrate_flatrate') + ->setCollectShippingRates(true); + /** @var Rate $shippingRate */ + $shippingRate = Bootstrap::getObjectManager()->create(Rate::class); + $shippingRate->setMethod('flatrate') + ->setCarrier('flatrate') + ->setPrice('5') + ->setCarrierTitle('Flat Rate') + ->setCode('flatrate_flatrate'); + $quote->getShippingAddress() + ->addShippingRate($shippingRate); + + $quote->getPayment()->setMethod('CC'); + + /** @var QuoteRepository $quoteRepository */ + $quoteRepository = Bootstrap::getObjectManager()->create(QuoteRepository::class); + $quoteRepository->save($quote); + + return $quote; + } +} \ No newline at end of file From f8a27742d912ec07b5e2d45ba5798c2374e59c69 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Thu, 16 Aug 2018 09:31:56 +0300 Subject: [PATCH 1025/1171] MAGETWO-93276: [2.3] Downloadable product links removal on update via API --- app/code/Magento/Catalog/Model/ProductRepository.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index a94a803031120..ed8d1d2e09186 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -4,7 +4,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); namespace Magento\Catalog\Model; From 02e4bf319017c075613fd09656c54ceeebf7a64c Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Thu, 16 Aug 2018 12:05:38 +0300 Subject: [PATCH 1026/1171] Fixed code style issue --- .../Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php b/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php index d17dfe2108341..f1d0034c29580 100644 --- a/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Cron/DeleteOutdatedPriceValuesTest.php @@ -103,7 +103,9 @@ public function testExecute() $this->attributeMock->expects($this->once())->method('getId')->willReturn($attributeId); $this->attributeMock->expects($this->once())->method('getBackend')->willReturn($this->attributeBackendMock); $this->attributeBackendMock->expects($this->once())->method('getTable')->willReturn($table); - $this->resourceConnectionMock->expects($this->once())->method('getConnection')->willReturn($this->dbAdapterMock); + $this->resourceConnectionMock->expects($this->once()) + ->method('getConnection') + ->willReturn($this->dbAdapterMock); $this->dbAdapterMock->expects($this->exactly(2))->method('quoteInto')->willReturnMap([ ['attribute_id = ?', $attributeId, null, null, $conditions[0]], ['store_id != ?', Store::DEFAULT_STORE_ID, null, null, $conditions[1]], From 5c2c6ff1013412e65a19df587e8a4d812a6f2416 Mon Sep 17 00:00:00 2001 From: Mikalai Shostka <Mikalai_Shostka@epam.com> Date: Thu, 16 Aug 2018 14:26:55 +0300 Subject: [PATCH 1027/1171] MAGETWO-91620: It is not possible to use function setInAllAttributeSetsFilter() together with getAllIds() for class Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection - Fix unit test --- .../Model/ResourceModel/Entity/Attribute/CollectionTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/Attribute/CollectionTest.php b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/Attribute/CollectionTest.php index 7263637a081d5..4a0fd5469767e 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/Attribute/CollectionTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/Attribute/CollectionTest.php @@ -150,8 +150,8 @@ public function testSetInAllAttributeSetsFilter() $this->selectMock->expects($this->atLeastOnce())->method('group')->with('entity_attribute.attribute_id') ->willReturnSelf(); - $this->selectMock->expects($this->atLeastOnce())->method('having')->with(new \Zend_Db_Expr('COUNT(*)') . ' = ' . count($setIds)) - ->willReturnSelf(); + $this->selectMock->expects($this->atLeastOnce())->method('having') + ->with(new \Zend_Db_Expr('COUNT(*)') . ' = ' . count($setIds))->willReturnSelf(); $this->model->setInAllAttributeSetsFilter($setIds); } From 7563605f8ab69ea09b5f017e8472670842f4bfc9 Mon Sep 17 00:00:00 2001 From: Thomas Klein <thomas@bird.eu> Date: Thu, 16 Aug 2018 13:48:21 +0200 Subject: [PATCH 1028/1171] add declare strict type tag --- app/code/Magento/Cms/Model/Config/Source/Block.php | 14 ++++++++------ .../Test/Unit/Model/Config/Source/BlockTest.php | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Cms/Model/Config/Source/Block.php b/app/code/Magento/Cms/Model/Config/Source/Block.php index e89daf381d94a..85dc4e4dd24bf 100644 --- a/app/code/Magento/Cms/Model/Config/Source/Block.php +++ b/app/code/Magento/Cms/Model/Config/Source/Block.php @@ -3,14 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Cms\Model\Config\Source; use Magento\Cms\Model\ResourceModel\Block\CollectionFactory; +use Magento\Framework\Data\OptionSourceInterface; /** * Class Block */ -class Block implements \Magento\Framework\Option\ArrayInterface +class Block implements OptionSourceInterface { /** * @var array @@ -18,12 +21,12 @@ class Block implements \Magento\Framework\Option\ArrayInterface private $options; /** - * @var CollectionFactory + * @var \Magento\Cms\Model\ResourceModel\Block\CollectionFactory */ private $collectionFactory; /** - * @param CollectionFactory $collectionFactory + * @param \Magento\Cms\Model\ResourceModel\Block\CollectionFactory $collectionFactory */ public function __construct( CollectionFactory $collectionFactory @@ -32,15 +35,14 @@ public function __construct( } /** - * To option array - * - * @return array + * {@inheritdoc} */ public function toOptionArray() { if (!$this->options) { $this->options = $this->collectionFactory->create()->toOptionIdArray(); } + return $this->options; } } diff --git a/app/code/Magento/Cms/Test/Unit/Model/Config/Source/BlockTest.php b/app/code/Magento/Cms/Test/Unit/Model/Config/Source/BlockTest.php index 7d9162bd56776..b6a91e9f56b30 100644 --- a/app/code/Magento/Cms/Test/Unit/Model/Config/Source/BlockTest.php +++ b/app/code/Magento/Cms/Test/Unit/Model/Config/Source/BlockTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Cms\Test\Unit\Model\Config\Source; /** From c0cc6e10337dc519403f6e74b6d881619f36c0e2 Mon Sep 17 00:00:00 2001 From: Volodymyr Kublytskyi <vkublytskyi@magento.com> Date: Thu, 16 Aug 2018 15:49:22 +0300 Subject: [PATCH 1029/1171] Fix exception message to avoid coupling of stock management with catalog --- .../Magento/CatalogInventory/Model/StockStateProvider.php | 4 ++-- .../Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php | 2 +- .../Multishipping/Controller/Checkout/CheckItemsTest.php | 2 +- .../integration/testsuite/Magento/Quote/Model/QuoteTest.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/StockStateProvider.php b/app/code/Magento/CatalogInventory/Model/StockStateProvider.php index ea4fdc994d682..fb6fc3be61375 100644 --- a/app/code/Magento/CatalogInventory/Model/StockStateProvider.php +++ b/app/code/Magento/CatalogInventory/Model/StockStateProvider.php @@ -160,7 +160,7 @@ public function checkQuoteItemQty(StockItemInterface $stockItem, $qty, $summaryQ } if (!$this->checkQty($stockItem, $summaryQty) || !$this->checkQty($stockItem, $qty)) { - $message = __('We don\'t have as many "%1" as you requested.', $stockItem->getProductName()); + $message = __('The requested qty is not available'); $result->setHasError(true)->setMessage($message)->setQuoteMessage($message)->setQuoteMessageIndex('qty'); return $result; } else { @@ -212,7 +212,7 @@ public function checkQuoteItemQty(StockItemInterface $stockItem, $qty, $summaryQ } } elseif ($stockItem->getShowDefaultNotificationMessage()) { $result->setMessage( - __('We don\'t have as many "%1" as you requested.', $stockItem->getProductName()) + __('The requested qty is not available') ); } } diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php index 44bbd90cc6a2d..4c653ab9ae33f 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php @@ -113,7 +113,7 @@ public function requestDataProvider(): array 'request' => ['qty' => 230], 'response' => [ 'success' => false, - 'error_message' => 'We don\'t have as many "Simple Product" as you requested.'] + 'error_message' => 'The requested qty is not available'] ], ]; } diff --git a/dev/tests/integration/testsuite/Magento/Multishipping/Controller/Checkout/CheckItemsTest.php b/dev/tests/integration/testsuite/Magento/Multishipping/Controller/Checkout/CheckItemsTest.php index 636239ee7a52e..02f031fae336a 100644 --- a/dev/tests/integration/testsuite/Magento/Multishipping/Controller/Checkout/CheckItemsTest.php +++ b/dev/tests/integration/testsuite/Magento/Multishipping/Controller/Checkout/CheckItemsTest.php @@ -163,7 +163,7 @@ public function requestDataProvider(): array 'request' => ['qty' => 101], 'response' => [ 'success' => false, - 'error_message' => 'We don\'t have as many "Simple Product" as you requested.'] + 'error_message' => 'The requested qty is not available'] ], [ 'request' => ['qty' => 230], diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php index 39db5b1572cb7..23df0acdc7822 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php @@ -324,7 +324,7 @@ public function testAddProductUpdateItem() $this->assertEquals(1, $quote->getItemsQty()); $this->expectException(\Magento\Framework\Exception\LocalizedException::class); - $this->expectExceptionMessage('We don\'t have as many "Simple Product" as you requested.'); + $this->expectExceptionMessage('The requested qty is not available'); $updateParams['qty'] = $productStockQty + 1; $quote->updateItem($updateParams['id'], $updateParams); } From bd1653369332544465452a4f261bc81c9ccc9e95 Mon Sep 17 00:00:00 2001 From: Volodymyr Kublytskyi <vkublytskyi@magento.com> Date: Thu, 16 Aug 2018 15:52:08 +0300 Subject: [PATCH 1030/1171] Fixture correction to have correctly loaded invoice for creditmemo --- .../integration/testsuite/Magento/Sales/_files/order_info.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_info.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_info.php index 31aa8aafd94d9..f3e528b9c0d28 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_info.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_info.php @@ -85,6 +85,9 @@ $invoice = $invoiceFactory->prepareInvoice($order, [$item->getId() => 10]); $invoice->register(); $invoice->save(); +$order->save(); + +$invoice = $objectManager->get(\Magento\Sales\Api\InvoiceRepositoryInterface::class)->get($invoice->getId()); /** @var \Magento\Sales\Model\Order\CreditmemoFactory $creditmemoFactory */ $creditmemoFactory = $objectManager->get(\Magento\Sales\Model\Order\CreditmemoFactory::class); From 2cd1fafed5e014ba566a7db9401bdff874e34a14 Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Thu, 16 Aug 2018 18:53:55 +0400 Subject: [PATCH 1031/1171] MAGETWO-91701: Newsletter subscription is not correctly updated when user is registered on 2 stores - Updated automated test. Updated ActionGroup names. --- .../VerifySubscribedNewsletterDisplayedActionGroup.xml | 8 ++++---- .../Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml index 1f61e1dab9813..941bcbd2d357a 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml @@ -17,7 +17,7 @@ </actionGroup> <!--Create new website--> - <actionGroup name="AdminCreateWebsite"> + <actionGroup name="AdminCreateWebsiteActGroup"> <!--Fill required fields--> <click selector="{{AdminNewWebsiteSection.addWebSite}}" stepKey="clickOnCreateWebsiteButton"/> <waitForPageLoad stepKey="waitFormToBeOpened"/> @@ -29,7 +29,7 @@ </actionGroup> <!--Create new store--> - <actionGroup name="AdminCreateNewStore"> + <actionGroup name="AdminCreateNewStoreActGroup"> <!--Fill required fields--> <click selector="{{AdminNewStoreGroupSection.create}}" stepKey="clickOnCreateStore"/> <waitForPageLoad stepKey="waitFormToBeOpened"/> @@ -43,7 +43,7 @@ </actionGroup> <!--Create store view--> - <actionGroup name="AdminCreateStoreView"> + <actionGroup name="AdminCreateStoreViewActGroup"> <!--Fill required fields--> <click selector="{{AdminNewStoreSection.create}}" stepKey="clickOnCreateStoreView"/> <waitForPageLoad stepKey="waitFormToBeOpened"/> @@ -114,7 +114,7 @@ </actionGroup> <!--Delete created Website --> - <actionGroup name="AdminDeleteWebsite"> + <actionGroup name="AdminDeleteWebsiteActGroup"> <fillField stepKey="fillSearchWebsiteField" selector="{{AdminStoresGridSection.websiteFilterTextField}}" userInput="{{AdminTestData.testData}}"/> <click stepKey="clickSearchButton" selector="{{AdminStoresGridSection.searchButton}}"/> <see stepKey="verifyThatCorrectWebsiteFound" selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" userInput="{{AdminTestData.testData}}"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml index 78c4e2085cc86..ad223381deadc 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml @@ -24,11 +24,11 @@ <!--Go to Stores.--> <actionGroup ref="GoToAllStores" stepKey="goToAllStores"/> <!--Create Website.--> - <actionGroup ref="AdminCreateWebsite" stepKey="adminCreateWebsite"/> + <actionGroup ref="AdminCreateWebsiteActGroup" stepKey="adminCreateWebsite"/> <!--Create Store.--> - <actionGroup ref="AdminCreateNewStore" stepKey="adminCreateNewStore"/> + <actionGroup ref="AdminCreateNewStoreActGroup" stepKey="adminCreateNewStore"/> <!--Create Store View.--> - <actionGroup ref="AdminCreateStoreView" stepKey="adminCreateStoreView"/> + <actionGroup ref="AdminCreateStoreViewActGroup" stepKey="adminCreateStoreView"/> <!--Go to Stores -> Configuration -> Web.--> <actionGroup ref="GoToStoresConfigurationWeb" stepKey="goToStoresConfigurationWeb"/> <actionGroup ref="SelectYesInAddStoreCodeToUrlsField" stepKey="selectYesInAddStoreCodeToUrlsField"/> @@ -55,7 +55,7 @@ <waitForPageLoad stepKey="waitForAdminPageLoaded"/> <actionGroup ref="AdminDeleteCreatedCustomer" stepKey="adminDeleteCustomer"/> <actionGroup ref="GoToAllStores" stepKey="goToAllStores"/> - <actionGroup ref="AdminDeleteWebsite" stepKey="adminDeleteWebsite"/> + <actionGroup ref="AdminDeleteWebsiteActGroup" stepKey="adminDeleteWebsite"/> <actionGroup ref="GoToStoresConfigurationWeb" stepKey="goToStoresConfigurationWeb"/> <actionGroup ref="AdminSetDefaultConfig" stepKey="adminSetDefaultConfig"/> </after> From b37a9896b359279ebe96a67eec06f62264f68a03 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Thu, 16 Aug 2018 12:39:32 -0300 Subject: [PATCH 1032/1171] Replacing deprecated methods for tests. --- .../Test/Unit/Controller/Adminhtml/Category/DeleteTest.php | 2 +- .../Test/Unit/Controller/Adminhtml/Category/SaveTest.php | 4 ++-- .../Adminhtml/Product/Action/Attribute/EditTest.php | 2 +- .../Adminhtml/Product/Action/Attribute/SaveTest.php | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/DeleteTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/DeleteTest.php index af1ded6987196..196b4df5b47c0 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/DeleteTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/DeleteTest.php @@ -71,7 +71,7 @@ protected function setUp() false, true, true, - ['addSuccess'] + ['addSuccessMessage'] ); $this->categoryRepository = $this->createMock(\Magento\Catalog\Api\CategoryRepositoryInterface::class); $context->expects($this->any()) 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 53f7fa0ed7e63..74173dc926d97 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 @@ -102,7 +102,7 @@ protected function setUp() false, true, true, - ['addSuccess', 'getMessages'] + ['addSuccessMessage', 'getMessages'] ); $this->save = $this->objectManager->getObject( @@ -392,7 +392,7 @@ public function testExecute($categoryId, $storeId, $parentId) $categoryMock->expects($this->once()) ->method('save'); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You saved the category.')); $categoryMock->expects($this->at(1)) ->method('getId') diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/EditTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/EditTest.php index 5a977b7934670..0ddd89afeac22 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/EditTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/EditTest.php @@ -94,7 +94,7 @@ private function prepareContext() $messageManager = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) ->setMethods([]) ->disableOriginalConstructor()->getMock(); - $messageManager->expects($this->any())->method('addError')->willReturn(true); + $messageManager->expects($this->any())->method('addErrorMessage')->willReturn(true); $this->context = $this->getMockBuilder(\Magento\Backend\App\Action\Context::class) ->setMethods(['getRequest', 'getObjectManager', 'getMessageManager', 'getResultRedirectFactory']) ->disableOriginalConstructor()->getMock(); diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php index c88a008efb19b..de44af7f58afc 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php @@ -250,8 +250,8 @@ public function testExecuteThatProductIdsAreObtainedFromAttributeHelper() ['inventory', [], [7]], ])); - $this->messageManager->expects($this->never())->method('addError'); - $this->messageManager->expects($this->never())->method('addException'); + $this->messageManager->expects($this->never())->method('addErrorMessage'); + $this->messageManager->expects($this->never())->method('addExceptionMessage'); $this->object->execute(); } From cbd470b14a538114c43abff9d9a8f0317632a25c Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Thu, 16 Aug 2018 18:42:33 +0300 Subject: [PATCH 1033/1171] GraphQL-122: API-functional tests: assertResponseFields moved to the abstract class --- .../Catalog/CategoryProductsVariantsTest.php | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php index d9a46a4451212..6b57f84e4b9c4 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php @@ -74,28 +74,4 @@ private function assertSimpleProductFields($product, $actualResponse) $this->assertResponseFields($actualResponse, $assertionMap); } - - /** - * @param array $actualResponse - * @param array $assertionMap - */ - private function assertResponseFields($actualResponse, $assertionMap) - { - foreach ($assertionMap as $key => $assertionData) { - $expectedValue = isset($assertionData['expected_value']) - ? $assertionData['expected_value'] - : $assertionData; - $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; - self::assertNotNull( - $expectedValue, - "Value of '{$responseField}' field must not be NULL" - ); - self::assertEquals( - $expectedValue, - $actualResponse[$responseField], - "Value of '{$responseField}' field in response does not match expected value: " - . var_export($expectedValue, true) - ); - } - } } From 432975dfc948619f8d38be7ed35aed140fb62fd4 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Thu, 16 Aug 2018 12:44:49 -0300 Subject: [PATCH 1034/1171] Replacing deprecated methods for Magento_Email module. --- .../Controller/Adminhtml/Email/Template/Delete.php | 10 +++++----- .../Controller/Adminhtml/Email/Template/Preview.php | 4 +++- .../Email/Controller/Adminhtml/Email/Template/Save.php | 6 +++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Delete.php b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Delete.php index 9f248f2cebc3d..eedcf5009ccfa 100644 --- a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Delete.php +++ b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Delete.php @@ -22,21 +22,21 @@ public function execute() if (count($template->getSystemConfigPathsWhereCurrentlyUsed()) == 0) { $template->delete(); // display success message - $this->messageManager->addSuccess(__('You deleted the email template.')); + $this->messageManager->addSuccessMessage(__('You deleted the email template.')); $this->_objectManager->get(\Magento\Framework\App\ReinitableConfig::class)->reinit(); // go to grid $this->_redirect('adminhtml/*/'); return; } // display error message - $this->messageManager->addError(__('The email template is currently being used.')); + $this->messageManager->addErrorMessage(__('The email template is currently being used.')); // redirect to edit form $this->_redirect('adminhtml/*/edit', ['id' => $template->getId()]); return; } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('We can\'t delete email template data right now. Please review log and try again.') ); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); @@ -52,7 +52,7 @@ public function execute() } } // display error message - $this->messageManager->addError(__('We can\'t find an email template to delete.')); + $this->messageManager->addErrorMessage(__('We can\'t find an email template to delete.')); // go to grid $this->_redirect('adminhtml/*/'); } diff --git a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Preview.php b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Preview.php index e23cd6cf17eb2..404f97c937167 100644 --- a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Preview.php +++ b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Preview.php @@ -21,7 +21,9 @@ public function execute() $this->_view->renderLayout(); $this->getResponse()->setHeader('Content-Security-Policy', "script-src 'none'"); } catch (\Exception $e) { - $this->messageManager->addError(__('An error occurred. The email template can not be opened for preview.')); + $this->messageManager->addErrorMessage( + __('An error occurred. The email template can not be opened for preview.') + ); $this->_redirect('adminhtml/*/'); } } diff --git a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Save.php b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Save.php index cf31259b7885d..135506f4068a8 100644 --- a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Save.php +++ b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Save.php @@ -22,7 +22,7 @@ public function execute() $template = $this->_initTemplate('id'); if (!$template->getId() && $id) { - $this->messageManager->addError(__('This email template no longer exists.')); + $this->messageManager->addErrorMessage(__('This email template no longer exists.')); $this->_redirect('adminhtml/*/'); return; } @@ -55,7 +55,7 @@ public function execute() $template->save(); $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setFormData(false); - $this->messageManager->addSuccess(__('You saved the email template.')); + $this->messageManager->addSuccessMessage(__('You saved the email template.')); $this->_redirect('adminhtml/*'); } catch (\Exception $e) { $this->_objectManager->get( @@ -64,7 +64,7 @@ public function execute() 'email_template_form_data', $request->getParams() ); - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $this->_forward('new'); } } From 6aeec4710d0decd5161bb3855e83540df81e6a14 Mon Sep 17 00:00:00 2001 From: Pieter Hoste <hoste.pieter@gmail.com> Date: Wed, 15 Aug 2018 13:30:46 +0200 Subject: [PATCH 1035/1171] Use '.min' in filenames of already minified js files in the Swagger module so they aren't getting minified again in production, fixes #16927 - for Magento 2.3 --- .../Swagger/view/frontend/layout/swagger_index_index.xml | 4 ++-- .../js/{swagger-ui-bundle.js => swagger-ui-bundle.min.js} | 0 ...andalone-preset.js => swagger-ui-standalone-preset.min.js} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/{swagger-ui-bundle.js => swagger-ui-bundle.min.js} (100%) rename app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/{swagger-ui-standalone-preset.js => swagger-ui-standalone-preset.min.js} (100%) diff --git a/app/code/Magento/Swagger/view/frontend/layout/swagger_index_index.xml b/app/code/Magento/Swagger/view/frontend/layout/swagger_index_index.xml index f14df1c70a790..5a592b9b7c987 100644 --- a/app/code/Magento/Swagger/view/frontend/layout/swagger_index_index.xml +++ b/app/code/Magento/Swagger/view/frontend/layout/swagger_index_index.xml @@ -15,8 +15,8 @@ <link src='Magento_Swagger::swagger-ui/js/lang/translator.js' type='text/javascript' defer="defer"/> <link src='Magento_Swagger::swagger-ui/js/lang/ru.js' type='text/javascript' defer="defer"/> <link src='Magento_Swagger::swagger-ui/js/lang/en.js' type='text/javascript' defer="defer"/> - <link src='Magento_Swagger::swagger-ui/js/swagger-ui-bundle.js' type='text/javascript' defer="defer"/> - <link src='Magento_Swagger::swagger-ui/js/swagger-ui-standalone-preset.js' type='text/javascript' defer="defer"/> + <link src='Magento_Swagger::swagger-ui/js/swagger-ui-bundle.min.js' type='text/javascript' defer="defer"/> + <link src='Magento_Swagger::swagger-ui/js/swagger-ui-standalone-preset.min.js' type='text/javascript' defer="defer"/> <link src='Magento_Swagger::swagger-ui/js/magento-swagger.js' type='text/javascript' defer="defer"/> <!--Remove require-js assets--> diff --git a/app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-bundle.js b/app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-bundle.min.js similarity index 100% rename from app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-bundle.js rename to app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-bundle.min.js diff --git a/app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-standalone-preset.js b/app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-standalone-preset.min.js similarity index 100% rename from app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-standalone-preset.js rename to app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-standalone-preset.min.js From 2a3fee4e74b1cbd7b28b7909c5d630dee50bc66b Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Thu, 16 Aug 2018 12:56:39 -0500 Subject: [PATCH 1036/1171] MAGETWO-93979: Required Field Indicators (Asterisks) Are Gone From Magento UI - fix function test failures --- .../Mftf/Section/AdminProductFormBundleSection.xml | 2 +- .../Mftf/Section/AdminProductAttributeSetSection.xml | 2 +- .../AdminProductCustomizableOptionsSection.xml | 12 ++++++------ .../Test/Mftf/Section/AdminProductFormSection.xml | 6 +++--- ...SaveProductWithCustomOptionsSecondWebsiteTest.xml | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml index 25f1d95dc863c..e0112c49f6b93 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml @@ -62,7 +62,7 @@ <!--FirstProductOption--> <element name="firstProductOption" type="checkbox" selector="//div[@class='admin__data-grid-outer-wrap']//tr[@data-repeat-index='0']//input[@type='checkbox']"/> <!--Category Selection--> - <element name="categoriesDropDown" type="multiselect" selector="//div[@data-index='category_ids']//div" timeout="30"/> + <element name="categoriesDropDown" type="multiselect" selector="//div[@data-index='category_ids']//div[@class='admin__field-control']" timeout="30"/> <element name="defaultCategory" type="multiselect" selector="//div[@data-index='category_ids']//span[contains(text(), 'Default Category')]"/> <element name="categoryByName" type="multiselect" selector="//div[@data-index='category_ids']//span[contains(text(), '{{category}}')]" parameterized="true"/> <element name="searchForCategory" type="input" selector="div.action-menu._active > div.admin__action-multiselect-search-wrap input" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml index 9e320d9e8b08d..ea1e031743fc1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml @@ -29,6 +29,6 @@ </section> <section name="ModifyAttributes"> <!-- Parameter is the attribute name --> - <element name="nthExistingAttribute" type="select" selector="//*[text()='{{attributeName}}']/../..//select" parameterized="true"/> + <element name="nthExistingAttribute" type="select" selector="//*[text()='{{attributeName}}']/../../..//select" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml index 80ed5cb2d7e9d..920ec053f6cef 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml @@ -14,19 +14,19 @@ <element name="useDefaultOptionTitle" type="text" selector="[data-index='options'] tr.data-row [data-index='title'] [name^='options_use_default']"/> <element name="useDefaultOptionTitleByIndex" type="text" selector="[data-index='options'] [data-index='values'] tr[data-repeat-index='{{var1}}'] [name^='options_use_default']" parameterized="true"/> <element name="addOptionBtn" type="button" selector="button[data-index='button_add']"/> - <element name="fillOptionTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//label[text()='Option Title']/parent::span/parent::div//input[@class='admin__control-text']" parameterized="true"/> + <element name="fillOptionTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//span[text()='Option Title']/parent::label/parent::div/parent::div//input[@class='admin__control-text']" parameterized="true"/> <element name="optionTitleInput" type="input" selector="input[name='product[options][0][title]']"/> <element name="optionTypeOpenDropDown" type="button" selector=".admin__dynamic-rows[data-index='options'] .action-select"/> <element name="optionTypeTextField" type="button" selector=".admin__dynamic-rows[data-index='options'] .action-menu._active li li"/> <element name="maxCharactersInput" type="input" selector="input[name='product[options][0][max_characters]']"/> - <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//label[text()='Option Type']/parent::span/parent::div//div[@data-role='selected-option']" parameterized="true"/> - <element name="checkDropDown" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//parent::label/parent::span/parent::div//li[@class='admin__action-multiselect-menu-inner-item']//label[text()='Drop-down']" parameterized="true"/> + <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//span[text()='Option Type']/parent::label/parent::div/parent::div//div[@data-role='selected-option']" parameterized="true"/> + <element name="checkDropDown" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//parent::label/parent::div/parent::div//li[@class='admin__action-multiselect-menu-inner-item']//label[text()='Drop-down']" parameterized="true"/> <element name="clickAddValue" type="button" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tfoot//button" parameterized="true"/> - <element name="fillOptionValueTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//label[text()='Title']/parent::span/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> + <element name="fillOptionValueTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//span[text()='Title']/parent::label/parent::div/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> <element name="fillOptionValuePrice" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//span[text()='Price']/parent::label/parent::div//div[@class='admin__control-addon']/input" parameterized="true"/> - <element name="clickSelectPriceType" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody//tr[@data-repeat-index='{{var2}}']//label[text()='Price Type']/parent::span/parent::div//select" parameterized="true"/> - <element name="checkboxUseDefaultTitle" type="checkbox" selector="//span[text()='Option Title']/parent::label/parent::div/div//input[@type='checkbox']"/> + <element name="clickSelectPriceType" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody//tr[@data-repeat-index='{{var2}}']//span[text()='Price Type']/parent::label/parent::div/parent::div//select" parameterized="true"/> + <element name="checkboxUseDefaultTitle" type="checkbox" selector="//span[text()='Option Title']/parent::label/parent::div/parent::div/div//input[@type='checkbox']"/> <element name="checkboxUseDefaultOption" type="checkbox" selector="//table[@data-index='values']//tbody//tr[@data-repeat-index='{{var1}}']//div[@class='admin__field-control']//input[@type='checkbox']" parameterized="true"/> <!-- Elements that make it easier to select the most recently added element --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 604eba61a05af..9b11e2a874811 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -13,8 +13,8 @@ <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> - <element name="enableProductAttributeLabel" type="text" selector="//label[text()='Enable Product']"/> - <element name="enableProductAttributeLabelWrapper" type="text" selector="//label[text()='Enable Product']/parent::span"/> + <element name="enableProductAttributeLabel" type="text" selector="//span[text()='Enable Product']/parent::label"/> + <element name="enableProductAttributeLabelWrapper" type="text" selector="//span[text()='Enable Product']/parent::label/parent::div"/> <element name="productStatus" type="checkbox" selector="input[name='product[status]']"/> <element name="enableProductLabel" type="checkbox" selector="input[name='product[status]']+label"/> <element name="productStatusUseDefault" type="checkbox" selector="input[name='use_default[status]']"/> @@ -41,7 +41,7 @@ <element name="visibility" type="select" selector="//select[@name='product[visibility]']"/> <element name="visibilityUseDefault" type="checkbox" selector="//input[@name='use_default[visibility]']"/> <element name="divByDataIndex" type="input" selector="div[data-index='{{var}}']" parameterized="true"/> - <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//label[text()='{{attributeLabel}}']" parameterized="true"/> + <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//span[text()='{{attributeLabel}}']" parameterized="true"/> </section> <section name="ProductInWebsitesSection"> <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml index 26f6eb7910fa2..5517b0fe43710 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml @@ -67,7 +67,7 @@ <fillField userInput="{{_defaultProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> <fillField userInput="{{_defaultProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> - <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses2"/> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses2"/> <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> <waitForPageLoad stepKey="waitAfterAddOption"/> <fillField selector="input[name='product[options][0][title]']" userInput="Radio Option" stepKey="fillOptionTitle"/> From fcbf4b7cc9d1778b376c8c0a8aba3ac08f829041 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 16 Aug 2018 15:19:16 -0500 Subject: [PATCH 1037/1171] MAGETWO-92279: An incorrect result of declaration:generate:whitelist execution - make generator exclude tables from app/etc/db_schema.xml - fix unit tests - create setup integration test for declaration:generate:whitelist --- .../TablesWhitelistGenerateCommand.php | 55 ++++- .../TablesWhitelistGenerateCommandTest.php | 101 ++++++++- .../TablesWhitelistGenerateCommandTest.php | 202 ++++++++++++++++++ .../Magento/Framework/Setup/JsonPersistor.php | 2 +- 4 files changed, 346 insertions(+), 14 deletions(-) create mode 100644 dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php diff --git a/app/code/Magento/Developer/Console/Command/TablesWhitelistGenerateCommand.php b/app/code/Magento/Developer/Console/Command/TablesWhitelistGenerateCommand.php index 776bef99e7960..b8ef557735db0 100644 --- a/app/code/Magento/Developer/Console/Command/TablesWhitelistGenerateCommand.php +++ b/app/code/Magento/Developer/Console/Command/TablesWhitelistGenerateCommand.php @@ -42,11 +42,16 @@ class TablesWhitelistGenerateCommand extends Command */ private $jsonPersistor; + /** + * @var array + */ + private $primaryDbSchema; + /** * @param ComponentRegistrar $componentRegistrar * @param ReaderComposite $readerComposite * @param JsonPersistor $jsonPersistor - * @param null $name + * @param string|null $name */ public function __construct( ComponentRegistrar $componentRegistrar, @@ -104,18 +109,21 @@ private function persistModule($moduleName) if (file_exists($whiteListFileName)) { $content = json_decode(file_get_contents($whiteListFileName), true); } - $newContent = $this->readerComposite->read($moduleName); - //Do merge between what we have before, and what we have now. + $newContent = $this->filterPrimaryTables($this->readerComposite->read($moduleName)); + + //Do merge between what we have before, and what we have now and filter to only certain attributes. $content = array_replace_recursive( $content, - $this->selectNamesFromContent($newContent) + $this->filterAttributeNames($newContent) ); - $this->jsonPersistor->persist($content, $whiteListFileName); + if (!empty($content)) { + $this->jsonPersistor->persist($content, $whiteListFileName); + } } /** - * {@inheritdoc} + * @inheritdoc */ protected function execute(InputInterface $input, OutputInterface $output) { @@ -144,7 +152,7 @@ protected function execute(InputInterface $input, OutputInterface $output) * @param array $content * @return array */ - private function selectNamesFromContent(array $content) + private function filterAttributeNames(array $content) { $names = []; $types = ['column', 'index', 'constraint']; @@ -163,4 +171,37 @@ private function selectNamesFromContent(array $content) return $names; } + + /** + * Load db_schema content from the primary scope app/etc/db_schema.xml. + * + * @return array + */ + private function getPrimaryDbSchema() + { + if (!$this->primaryDbSchema) { + $this->primaryDbSchema = $this->readerComposite->read('primary'); + } + return $this->primaryDbSchema; + } + + /** + * Filter tables from module db_schema.xml as they should not contain the primary system tables. + * + * @param array $moduleDbSchema + * @return array + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + private function filterPrimaryTables(array $moduleDbSchema) + { + $primaryDbSchema = $this->getPrimaryDbSchema(); + if (isset($moduleDbSchema['table']) && isset($primaryDbSchema['table'])) { + foreach ($primaryDbSchema['table'] as $tableNameKey => $tableContents) { + if (isset($moduleDbSchema['table'][$tableNameKey])) { + unset($moduleDbSchema['table'][$tableNameKey]); + } + } + } + return $moduleDbSchema; + } } diff --git a/app/code/Magento/Developer/Test/Unit/Console/Command/TablesWhitelistGenerateCommandTest.php b/app/code/Magento/Developer/Test/Unit/Console/Command/TablesWhitelistGenerateCommandTest.php index 66fd7e3eec638..5bfc5686b05fb 100644 --- a/app/code/Magento/Developer/Test/Unit/Console/Command/TablesWhitelistGenerateCommandTest.php +++ b/app/code/Magento/Developer/Test/Unit/Console/Command/TablesWhitelistGenerateCommandTest.php @@ -45,6 +45,9 @@ class TablesWhitelistGenerateCommandTest extends \PHPUnit\Framework\TestCase */ private $jsonPersistorMock; + /** + * {@inheritdoc} + */ protected function setUp() { $this->componentRegistrarMock = $this->getMockBuilder(ComponentRegistrar::class) @@ -76,6 +79,47 @@ public function whitelistTableProvider() [ 'moduleName' => 'SomeModule', 'whitelist' => [ + 'primary' => [ + 'table' => + [ + 'patch_list' => + [ + 'column' => + [ + 'patch_id' => + [ + 'type' => 'int', + 'name' => 'patch_id', + 'identity' => 'true', + 'comment' => 'Patch Auto Increment', + ], + 'patch_name' => + [ + 'type' => 'varchar', + 'name' => 'patch_name', + 'length' => '1024', + 'nullable' => 'false', + 'comment' => 'Patch Class Name', + ], + ], + 'constraint' => + [ + 'PRIMARY' => + [ + 'column' => + [ + 'patch_id' => 'patch_id', + ], + 'type' => 'primary', + 'name' => 'PRIMARY', + ], + ], + 'name' => 'patch_list', + 'resource' => 'default', + 'comment' => 'List of data/schema patches', + ], + ], + ], 'SomeModule' => [ 'table' => [ 'first_table' => [ @@ -149,6 +193,47 @@ public function whitelistTableProvider() [ 'moduleName' => false, 'whitelist' => [ + 'primary' => [ + 'table' => + [ + 'patch_list' => + [ + 'column' => + [ + 'patch_id' => + [ + 'type' => 'int', + 'name' => 'patch_id', + 'identity' => 'true', + 'comment' => 'Patch Auto Increment', + ], + 'patch_name' => + [ + 'type' => 'varchar', + 'name' => 'patch_name', + 'length' => '1024', + 'nullable' => 'false', + 'comment' => 'Patch Class Name', + ], + ], + 'constraint' => + [ + 'PRIMARY' => + [ + 'column' => + [ + 'patch_id' => 'patch_id', + ], + 'type' => 'primary', + 'name' => 'PRIMARY', + ], + ], + 'name' => 'patch_list', + 'resource' => 'default', + 'comment' => 'List of data/schema patches', + ], + ], + ], 'SomeModule' => [ 'table' => [ 'first_table' => [ @@ -303,10 +388,14 @@ public function testCommand($moduleName, array $whiteListTables, array $expected $this->componentRegistrarMock->expects(self::once()) ->method('getPaths') ->willReturn(['SomeModule' => 1, 'Module2' => 2]); - $this->readerCompositeMock->expects(self::exactly(2)) + $this->readerCompositeMock->expects(self::exactly(3)) ->method('read') - ->withConsecutive(['SomeModule'], ['Module2']) - ->willReturnOnConsecutiveCalls($whiteListTables['SomeModule'], $whiteListTables['Module2']); + ->withConsecutive(['SomeModule'], ['primary'], ['Module2']) + ->willReturnOnConsecutiveCalls( + $whiteListTables['SomeModule'], + $whiteListTables['primary'], + $whiteListTables['Module2'] + ); $this->jsonPersistorMock->expects(self::exactly(2)) ->method('persist') ->withConsecutive( @@ -320,10 +409,10 @@ public function testCommand($moduleName, array $whiteListTables, array $expected ] ); } else { - $this->readerCompositeMock->expects(self::once()) + $this->readerCompositeMock->expects(self::exactly(2)) ->method('read') - ->with($moduleName) - ->willReturn($whiteListTables['SomeModule']); + ->withConsecutive([$moduleName], ['primary']) + ->willReturnOnConsecutiveCalls($whiteListTables['SomeModule'], $whiteListTables['primary']); $this->jsonPersistorMock->expects(self::once()) ->method('persist') ->with( diff --git a/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php b/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php new file mode 100644 index 0000000000000..81ad56b793224 --- /dev/null +++ b/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php @@ -0,0 +1,202 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Developer\Console\Command; + +use Symfony\Component\Console\Tester\CommandTester; +use Magento\TestFramework\TestCase\SetupTestCase; +use Magento\Framework\Console\Cli; +use Magento\TestFramework\Deploy\CliCommand; + +/** + * The purpose of this test is to verify the declaration:generate:whitelist command. + */ +class TablesWhitelistGenerateCommandTest extends SetupTestCase +{ + /** + * @var CommandTester + */ + private $tester; + + /** + * @var \Magento\Developer\Console\Command\TablesWhitelistGenerateCommand + */ + private $command; + + /** + * @var \Magento\Framework\Component\ComponentRegistrar + */ + private $componentRegistrar; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var CliCommand + */ + private $cliCommand; + + /** + * {@inheritdoc} + */ + public function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->command = $this->objectManager->create( + \Magento\Developer\Console\Command\TablesWhitelistGenerateCommand::class + ); + $this->componentRegistrar = $this->objectManager->create( + \Magento\Framework\Component\ComponentRegistrar::class + ); + $this->cliCommand = $this->objectManager->get(CliCommand::class); + $this->tester = new CommandTester($this->command); + } + + /** + * Execute generate command for whitelist on module Magento_TestSetupDeclarationModule1. + * + * @param array $expectedWhitelistContent + * + * @moduleName Magento_TestSetupDeclarationModule1 + * @dataProvider contentsDataProvider + */ + public function testExecute(array $expectedWhitelistContent) + { + $this->cliCommand->install(['Magento_TestSetupDeclarationModule1']); + + $moduleName = 'Magento_TestSetupDeclarationModule1'; + $modulePath = $this->componentRegistrar->getPath('module', $moduleName); + $whiteListFileName = $modulePath + . DIRECTORY_SEPARATOR + . \Magento\Framework\Module\Dir::MODULE_ETC_DIR + . DIRECTORY_SEPARATOR + . \Magento\Framework\Setup\Declaration\Schema\Diff\Diff::GENERATED_WHITELIST_FILE_NAME; + + //run bin/magento declaration:generate:whitelist Magento_TestSetupDeclarationModule1 command. + $this->tester->execute(['--module-name' => $moduleName], ['interactive' => false]); + + $this->assertSame(Cli::RETURN_SUCCESS, $this->tester->getStatusCode()); + + $this->assertFileExists($whiteListFileName); + $this->assertContains('', $this->tester->getDisplay()); + + $whitelistContent = json_decode(file_get_contents($whiteListFileName), true); + $this->assertEquals($expectedWhitelistContent, $whitelistContent); + } + + /** + * Data provider for whitelist contents. + * + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function contentsDataProvider() + { + return [ + [ + 'content' => [ + 'reference_table' => + [ + 'column' => + [ + 'tinyint_ref' => true, + 'tinyint_without_padding' => true, + 'bigint_without_padding' => true, + 'integer_without_padding' => true, + 'smallint_with_big_padding' => true, + 'smallint_without_default' => true, + 'int_without_unsigned' => true, + 'int_unsigned' => true, + 'bigint_default_nullable' => true, + 'bigint_not_default_not_nullable' => true, + 'smallint_without_padding' => true, + ], + 'constraint' => + [ + 'tinyint_primary' => true, + ], + ], + 'auto_increment_test' => + [ + 'column' => + [ + 'int_auto_increment_with_nullable' => true, + 'int_disabled_auto_increment' => true, + ], + 'constraint' => + [ + 'AUTO_INCREMENT_TEST_INT_AUTO_INCREMENT_WITH_NULLABLE' => true, + ], + ], + 'test_table' => + [ + 'column' => + [ + 'smallint' => true, + 'tinyint' => true, + 'bigint' => true, + 'float' => true, + 'double' => true, + 'decimal' => true, + 'date' => true, + 'timestamp' => true, + 'datetime' => true, + 'longtext' => true, + 'mediumtext' => true, + 'varchar' => true, + 'mediumblob' => true, + 'blob' => true, + 'boolean' => true, + 'varbinary_rename' => true, + ], + 'index' => + [ + 'TEST_TABLE_TINYINT_BIGINT' => true, + ], + 'constraint' => + [ + 'TEST_TABLE_SMALLINT_BIGINT' => true, + 'TEST_TABLE_TINYINT_REFERENCE_TABLE_TINYINT_REF' => true, + ], + ], + 'store' => + [ + 'column' => + [ + 'store_owner_id' => true, + 'store_owner' => true, + ], + 'constraint' => + [ + 'STORE_STORE_OWNER_ID_STORE_OWNER_OWNER_ID' => true, + ], + ], + 'store_owner' => + [ + 'column' => + [ + 'owner_id' => true, + 'label' => true, + ], + 'constraint' => + [ + '' => true, + ], + ], + 'some_table' => + [ + 'column' => + [ + 'some_column' => true, + ], + ], + ], + ], + ]; + } +} diff --git a/lib/internal/Magento/Framework/Setup/JsonPersistor.php b/lib/internal/Magento/Framework/Setup/JsonPersistor.php index 4094f9e0d090d..b479ffe20e460 100644 --- a/lib/internal/Magento/Framework/Setup/JsonPersistor.php +++ b/lib/internal/Magento/Framework/Setup/JsonPersistor.php @@ -19,6 +19,6 @@ class JsonPersistor */ public function persist(array $data, $path) { - return file_put_contents($path, json_encode($data)); + return file_put_contents($path, json_encode($data, JSON_PRETTY_PRINT)); } } From 1b9325247c7b202844f781e026fbba47982a0346 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 16 Aug 2018 15:26:18 -0500 Subject: [PATCH 1038/1171] MAGETWO-92279: An incorrect result of declaration:generate:whitelist execution - modifying db_schema_whitelist.json for ce --- .../etc/db_schema_whitelist.json | 56 +- .../etc/db_schema_whitelist.json | 22 +- .../etc/db_schema_whitelist.json | 84 +- .../etc/db_schema_whitelist.json | 68 +- .../Bundle/etc/db_schema_whitelist.json | 418 +-- .../Captcha/etc/db_schema_whitelist.json | 20 +- .../Catalog/etc/db_schema_whitelist.json | 2243 +++++++-------- .../etc/db_schema_whitelist.json | 236 +- .../CatalogRule/etc/db_schema_whitelist.json | 386 +-- .../etc/db_schema_whitelist.json | 8 +- .../etc/db_schema_whitelist.json | 32 +- .../etc/db_schema_whitelist.json | 48 +- .../Magento/Cms/etc/db_schema_whitelist.json | 142 +- .../Config/etc/db_schema_whitelist.json | 24 +- .../etc/db_schema_whitelist.json | 90 +- .../Magento/Cron/etc/db_schema_whitelist.json | 36 +- .../Customer/etc/db_schema_whitelist.json | 692 ++--- .../Directory/etc/db_schema_whitelist.json | 116 +- .../Downloadable/etc/db_schema_whitelist.json | 334 +-- .../Magento/Eav/etc/db_schema_whitelist.json | 776 ++--- .../Email/etc/db_schema_whitelist.json | 46 +- .../GiftMessage/etc/db_schema_whitelist.json | 82 +- .../etc/db_schema_whitelist.json | 26 +- .../ImportExport/etc/db_schema_whitelist.json | 46 +- .../Indexer/etc/db_schema_whitelist.json | 60 +- .../Integration/etc/db_schema_whitelist.json | 180 +- .../MessageQueue/etc/db_schema_whitelist.json | 20 +- .../MysqlMq/etc/db_schema_whitelist.json | 78 +- .../etc/db_schema_whitelist.json | 108 +- .../Newsletter/etc/db_schema_whitelist.json | 218 +- .../etc/db_schema_whitelist.json | 80 +- .../Paypal/etc/db_schema_whitelist.json | 220 +- .../Persistent/etc/db_schema_whitelist.json | 46 +- .../ProductAlert/etc/db_schema_whitelist.json | 94 +- .../ProductVideo/etc/db_schema_whitelist.json | 30 +- .../Quote/etc/db_schema_whitelist.json | 638 ++--- .../etc/db_schema_whitelist.json | 22 +- .../Reports/etc/db_schema_whitelist.json | 286 +- .../Review/etc/db_schema_whitelist.json | 408 +-- .../Sales/etc/db_schema_whitelist.json | 2490 ++++++++--------- .../SalesRule/etc/db_schema_whitelist.json | 454 +-- .../etc/db_schema_whitelist.json | 56 +- .../Search/etc/db_schema_whitelist.json | 84 +- .../Security/etc/db_schema_whitelist.json | 66 +- .../SendFriend/etc/db_schema_whitelist.json | 28 +- .../Signifyd/etc/db_schema_whitelist.json | 48 +- .../Sitemap/etc/db_schema_whitelist.json | 32 +- .../Store/etc/db_schema_whitelist.json | 112 +- .../Swatches/etc/db_schema_whitelist.json | 42 +- .../Magento/Tax/etc/db_schema_whitelist.json | 238 +- .../Theme/etc/db_schema_whitelist.json | 88 +- .../Translation/etc/db_schema_whitelist.json | 28 +- .../Magento/Ui/etc/db_schema_whitelist.json | 38 +- .../UrlRewrite/etc/db_schema_whitelist.json | 42 +- .../Magento/User/etc/db_schema_whitelist.json | 82 +- .../Variable/etc/db_schema_whitelist.json | 54 +- .../Vault/etc/db_schema_whitelist.json | 66 +- .../Magento/Weee/etc/db_schema_whitelist.json | 148 +- .../Widget/etc/db_schema_whitelist.json | 180 +- .../Wishlist/etc/db_schema_whitelist.json | 100 +- 60 files changed, 6498 insertions(+), 6497 deletions(-) diff --git a/app/code/Magento/AdminNotification/etc/db_schema_whitelist.json b/app/code/Magento/AdminNotification/etc/db_schema_whitelist.json index df5e14e066636..b068ffffe9219 100644 --- a/app/code/Magento/AdminNotification/etc/db_schema_whitelist.json +++ b/app/code/Magento/AdminNotification/etc/db_schema_whitelist.json @@ -1,32 +1,32 @@ { - "adminnotification_inbox": { - "column": { - "notification_id": true, - "severity": true, - "date_added": true, - "title": true, - "description": true, - "url": true, - "is_read": true, - "is_remove": true + "adminnotification_inbox": { + "column": { + "notification_id": true, + "severity": true, + "date_added": true, + "title": true, + "description": true, + "url": true, + "is_read": true, + "is_remove": true + }, + "index": { + "ADMINNOTIFICATION_INBOX_SEVERITY": true, + "ADMINNOTIFICATION_INBOX_IS_READ": true, + "ADMINNOTIFICATION_INBOX_IS_REMOVE": true + }, + "constraint": { + "PRIMARY": true + } }, - "index": { - "ADMINNOTIFICATION_INBOX_SEVERITY": true, - "ADMINNOTIFICATION_INBOX_IS_READ": true, - "ADMINNOTIFICATION_INBOX_IS_REMOVE": true - }, - "constraint": { - "PRIMARY": true - } - }, - "admin_system_messages": { - "column": { - "identity": true, - "severity": true, - "created_at": true - }, - "constraint": { - "PRIMARY": true + "admin_system_messages": { + "column": { + "identity": true, + "severity": true, + "created_at": true + }, + "constraint": { + "PRIMARY": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/AdvancedSearch/etc/db_schema_whitelist.json b/app/code/Magento/AdvancedSearch/etc/db_schema_whitelist.json index eaf7f3d616736..8addf187744fd 100644 --- a/app/code/Magento/AdvancedSearch/etc/db_schema_whitelist.json +++ b/app/code/Magento/AdvancedSearch/etc/db_schema_whitelist.json @@ -1,14 +1,14 @@ { - "catalogsearch_recommendations": { - "column": { - "id": true, - "query_id": true, - "relation_id": true - }, - "constraint": { - "PRIMARY": true, - "CATALOGSEARCH_RECOMMENDATIONS_QUERY_ID_SEARCH_QUERY_QUERY_ID": true, - "CATALOGSEARCH_RECOMMENDATIONS_RELATION_ID_SEARCH_QUERY_QUERY_ID": true + "catalogsearch_recommendations": { + "column": { + "id": true, + "query_id": true, + "relation_id": true + }, + "constraint": { + "PRIMARY": true, + "CATALOGSEARCH_RECOMMENDATIONS_QUERY_ID_SEARCH_QUERY_QUERY_ID": true, + "CATALOGSEARCH_RECOMMENDATIONS_RELATION_ID_SEARCH_QUERY_QUERY_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/AsynchronousOperations/etc/db_schema_whitelist.json b/app/code/Magento/AsynchronousOperations/etc/db_schema_whitelist.json index 396e443355d8f..d62f16ffca8cd 100644 --- a/app/code/Magento/AsynchronousOperations/etc/db_schema_whitelist.json +++ b/app/code/Magento/AsynchronousOperations/etc/db_schema_whitelist.json @@ -1,47 +1,47 @@ { - "magento_bulk": { - "column": { - "id": true, - "uuid": true, - "user_id": true, - "description": true, - "operation_count": true, - "start_time": true + "magento_bulk": { + "column": { + "id": true, + "uuid": true, + "user_id": true, + "description": true, + "operation_count": true, + "start_time": true + }, + "constraint": { + "PRIMARY": true, + "MAGENTO_BULK_USER_ID_ADMIN_USER_USER_ID": true, + "MAGENTO_BULK_UUID": true + } }, - "constraint": { - "PRIMARY": true, - "MAGENTO_BULK_USER_ID_ADMIN_USER_USER_ID": true, - "MAGENTO_BULK_UUID": true - } - }, - "magento_operation": { - "column": { - "id": true, - "bulk_uuid": true, - "topic_name": true, - "serialized_data": true, - "result_serialized_data": true, - "status": true, - "error_code": true, - "result_message": true - }, - "index": { - "MAGENTO_OPERATION_BULK_UUID_ERROR_CODE": true - }, - "constraint": { - "PRIMARY": true, - "MAGENTO_OPERATION_BULK_UUID_MAGENTO_BULK_UUID": true - } - }, - "magento_acknowledged_bulk": { - "column": { - "id": true, - "bulk_uuid": true + "magento_operation": { + "column": { + "id": true, + "bulk_uuid": true, + "topic_name": true, + "serialized_data": true, + "result_serialized_data": true, + "status": true, + "error_code": true, + "result_message": true + }, + "index": { + "MAGENTO_OPERATION_BULK_UUID_ERROR_CODE": true + }, + "constraint": { + "PRIMARY": true, + "MAGENTO_OPERATION_BULK_UUID_MAGENTO_BULK_UUID": true + } }, - "constraint": { - "PRIMARY": true, - "MAGENTO_ACKNOWLEDGED_BULK_BULK_UUID_MAGENTO_BULK_UUID": true, - "MAGENTO_ACKNOWLEDGED_BULK_BULK_UUID": true + "magento_acknowledged_bulk": { + "column": { + "id": true, + "bulk_uuid": true + }, + "constraint": { + "PRIMARY": true, + "MAGENTO_ACKNOWLEDGED_BULK_BULK_UUID_MAGENTO_BULK_UUID": true, + "MAGENTO_ACKNOWLEDGED_BULK_BULK_UUID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Authorization/etc/db_schema_whitelist.json b/app/code/Magento/Authorization/etc/db_schema_whitelist.json index eb9256e415593..8c416d2a8b42c 100644 --- a/app/code/Magento/Authorization/etc/db_schema_whitelist.json +++ b/app/code/Magento/Authorization/etc/db_schema_whitelist.json @@ -1,38 +1,38 @@ { - "authorization_role": { - "column": { - "role_id": true, - "parent_id": true, - "tree_level": true, - "sort_order": true, - "role_type": true, - "user_id": true, - "user_type": true, - "role_name": true + "authorization_role": { + "column": { + "role_id": true, + "parent_id": true, + "tree_level": true, + "sort_order": true, + "role_type": true, + "user_id": true, + "user_type": true, + "role_name": true + }, + "index": { + "AUTHORIZATION_ROLE_PARENT_ID_SORT_ORDER": true, + "AUTHORIZATION_ROLE_TREE_LEVEL": true + }, + "constraint": { + "PRIMARY": true + } }, - "index": { - "AUTHORIZATION_ROLE_PARENT_ID_SORT_ORDER": true, - "AUTHORIZATION_ROLE_TREE_LEVEL": true - }, - "constraint": { - "PRIMARY": true - } - }, - "authorization_rule": { - "column": { - "rule_id": true, - "role_id": true, - "resource_id": true, - "privileges": true, - "permission": true - }, - "index": { - "AUTHORIZATION_RULE_RESOURCE_ID_ROLE_ID": true, - "AUTHORIZATION_RULE_ROLE_ID_RESOURCE_ID": true - }, - "constraint": { - "PRIMARY": true, - "AUTHORIZATION_RULE_ROLE_ID_AUTHORIZATION_ROLE_ROLE_ID": true + "authorization_rule": { + "column": { + "rule_id": true, + "role_id": true, + "resource_id": true, + "privileges": true, + "permission": true + }, + "index": { + "AUTHORIZATION_RULE_RESOURCE_ID_ROLE_ID": true, + "AUTHORIZATION_RULE_ROLE_ID_RESOURCE_ID": true + }, + "constraint": { + "PRIMARY": true, + "AUTHORIZATION_RULE_ROLE_ID_AUTHORIZATION_ROLE_ROLE_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Bundle/etc/db_schema_whitelist.json b/app/code/Magento/Bundle/etc/db_schema_whitelist.json index efb535d50caa3..2834d707cae0f 100644 --- a/app/code/Magento/Bundle/etc/db_schema_whitelist.json +++ b/app/code/Magento/Bundle/etc/db_schema_whitelist.json @@ -1,212 +1,212 @@ { - "catalog_product_bundle_option": { - "column": { - "option_id": true, - "parent_id": true, - "required": true, - "position": true, - "type": true - }, - "index": { - "CATALOG_PRODUCT_BUNDLE_OPTION_PARENT_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_BNDL_OPT_PARENT_ID_CAT_PRD_ENTT_ENTT_ID": true - } - }, - "catalog_product_bundle_option_value": { - "column": { - "value_id": true, - "option_id": true, - "store_id": true, - "title": true, - "parent_product_id": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_BNDL_OPT_VAL_OPT_ID_CAT_PRD_BNDL_OPT_OPT_ID": true, - "CAT_PRD_BNDL_OPT_VAL_OPT_ID_PARENT_PRD_ID_STORE_ID": true, - "CATALOG_PRODUCT_BUNDLE_OPTION_VALUE_OPTION_ID_STORE_ID": true - } - }, - "catalog_product_bundle_selection": { - "column": { - "selection_id": true, - "option_id": true, - "parent_product_id": true, - "product_id": true, - "position": true, - "is_default": true, - "selection_price_type": true, - "selection_price_value": true, - "selection_qty": true, - "selection_can_change_qty": true - }, - "index": { - "CATALOG_PRODUCT_BUNDLE_SELECTION_OPTION_ID": true, - "CATALOG_PRODUCT_BUNDLE_SELECTION_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_BNDL_SELECTION_OPT_ID_CAT_PRD_BNDL_OPT_OPT_ID": true, - "CAT_PRD_BNDL_SELECTION_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CAT_PRD_BNDL_SELECTION_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true - } - }, - "catalog_product_bundle_selection_price": { - "column": { - "selection_id": true, - "website_id": true, - "selection_price_type": true, - "selection_price_value": true, - "parent_product_id": true - }, - "index": { - "CATALOG_PRODUCT_BUNDLE_SELECTION_PRICE_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true, - "PK_CATALOG_PRODUCT_BUNDLE_SELECTION_PRICE": true, - "CAT_PRD_BNDL_SELECTION_PRICE_WS_ID_STORE_WS_WS_ID": true, - "FK_DCF37523AA05D770A70AA4ED7C2616E4": true, - "DCF37523AA05D770A70AA4ED7C2616E4": true - } - }, - "catalog_product_bundle_price_index": { - "column": { - "entity_id": true, - "website_id": true, - "customer_group_id": true, - "min_price": true, - "max_price": true - }, - "index": { - "CATALOG_PRODUCT_BUNDLE_PRICE_INDEX_WEBSITE_ID": true, - "CATALOG_PRODUCT_BUNDLE_PRICE_INDEX_CUSTOMER_GROUP_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_BNDL_PRICE_IDX_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true, - "CAT_PRD_BNDL_PRICE_IDX_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CAT_PRD_BNDL_PRICE_IDX_WS_ID_STORE_WS_WS_ID": true, - "CAT_PRD_BNDL_PRICE_IDX_ENTT_ID_SEQUENCE_PRD_SEQUENCE_VAL": true - } - }, - "catalog_product_bundle_stock_index": { - "column": { - "entity_id": true, - "website_id": true, - "stock_id": true, - "option_id": true, - "stock_status": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_bundle_idx": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "tax_class_id": true, - "price_type": true, - "special_price": true, - "tier_percent": true, - "orig_price": true, - "price": true, - "min_price": true, - "max_price": true, - "tier_price": true, - "base_tier": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_bundle_tmp": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "tax_class_id": true, - "price_type": true, - "special_price": true, - "tier_percent": true, - "orig_price": true, - "price": true, - "min_price": true, - "max_price": true, - "tier_price": true, - "base_tier": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_bundle_sel_idx": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "option_id": true, - "selection_id": true, - "group_type": true, - "is_required": true, - "price": true, - "tier_price": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_bundle_sel_tmp": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "option_id": true, - "selection_id": true, - "group_type": true, - "is_required": true, - "price": true, - "tier_price": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_bundle_opt_idx": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "option_id": true, - "min_price": true, - "alt_price": true, - "max_price": true, - "tier_price": true, - "alt_tier_price": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_bundle_opt_tmp": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "option_id": true, - "min_price": true, - "alt_price": true, - "max_price": true, - "tier_price": true, - "alt_tier_price": true - }, - "constraint": { - "PRIMARY": true + "catalog_product_bundle_option": { + "column": { + "option_id": true, + "parent_id": true, + "required": true, + "position": true, + "type": true + }, + "index": { + "CATALOG_PRODUCT_BUNDLE_OPTION_PARENT_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_BNDL_OPT_PARENT_ID_CAT_PRD_ENTT_ENTT_ID": true + } + }, + "catalog_product_bundle_option_value": { + "column": { + "value_id": true, + "option_id": true, + "store_id": true, + "title": true, + "parent_product_id": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_BNDL_OPT_VAL_OPT_ID_CAT_PRD_BNDL_OPT_OPT_ID": true, + "CAT_PRD_BNDL_OPT_VAL_OPT_ID_PARENT_PRD_ID_STORE_ID": true, + "CATALOG_PRODUCT_BUNDLE_OPTION_VALUE_OPTION_ID_STORE_ID": true + } + }, + "catalog_product_bundle_selection": { + "column": { + "selection_id": true, + "option_id": true, + "parent_product_id": true, + "product_id": true, + "position": true, + "is_default": true, + "selection_price_type": true, + "selection_price_value": true, + "selection_qty": true, + "selection_can_change_qty": true + }, + "index": { + "CATALOG_PRODUCT_BUNDLE_SELECTION_OPTION_ID": true, + "CATALOG_PRODUCT_BUNDLE_SELECTION_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_BNDL_SELECTION_OPT_ID_CAT_PRD_BNDL_OPT_OPT_ID": true, + "CAT_PRD_BNDL_SELECTION_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CAT_PRD_BNDL_SELECTION_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true + } + }, + "catalog_product_bundle_selection_price": { + "column": { + "selection_id": true, + "website_id": true, + "selection_price_type": true, + "selection_price_value": true, + "parent_product_id": true + }, + "index": { + "CATALOG_PRODUCT_BUNDLE_SELECTION_PRICE_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true, + "PK_CATALOG_PRODUCT_BUNDLE_SELECTION_PRICE": true, + "CAT_PRD_BNDL_SELECTION_PRICE_WS_ID_STORE_WS_WS_ID": true, + "FK_DCF37523AA05D770A70AA4ED7C2616E4": true, + "DCF37523AA05D770A70AA4ED7C2616E4": true + } + }, + "catalog_product_bundle_price_index": { + "column": { + "entity_id": true, + "website_id": true, + "customer_group_id": true, + "min_price": true, + "max_price": true + }, + "index": { + "CATALOG_PRODUCT_BUNDLE_PRICE_INDEX_WEBSITE_ID": true, + "CATALOG_PRODUCT_BUNDLE_PRICE_INDEX_CUSTOMER_GROUP_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_BNDL_PRICE_IDX_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true, + "CAT_PRD_BNDL_PRICE_IDX_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CAT_PRD_BNDL_PRICE_IDX_WS_ID_STORE_WS_WS_ID": true, + "CAT_PRD_BNDL_PRICE_IDX_ENTT_ID_SEQUENCE_PRD_SEQUENCE_VAL": true + } + }, + "catalog_product_bundle_stock_index": { + "column": { + "entity_id": true, + "website_id": true, + "stock_id": true, + "option_id": true, + "stock_status": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_bundle_idx": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "tax_class_id": true, + "price_type": true, + "special_price": true, + "tier_percent": true, + "orig_price": true, + "price": true, + "min_price": true, + "max_price": true, + "tier_price": true, + "base_tier": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_bundle_tmp": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "tax_class_id": true, + "price_type": true, + "special_price": true, + "tier_percent": true, + "orig_price": true, + "price": true, + "min_price": true, + "max_price": true, + "tier_price": true, + "base_tier": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_bundle_sel_idx": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "option_id": true, + "selection_id": true, + "group_type": true, + "is_required": true, + "price": true, + "tier_price": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_bundle_sel_tmp": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "option_id": true, + "selection_id": true, + "group_type": true, + "is_required": true, + "price": true, + "tier_price": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_bundle_opt_idx": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "option_id": true, + "min_price": true, + "alt_price": true, + "max_price": true, + "tier_price": true, + "alt_tier_price": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_bundle_opt_tmp": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "option_id": true, + "min_price": true, + "alt_price": true, + "max_price": true, + "tier_price": true, + "alt_tier_price": true + }, + "constraint": { + "PRIMARY": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Captcha/etc/db_schema_whitelist.json b/app/code/Magento/Captcha/etc/db_schema_whitelist.json index 95fd6411b44dd..1f5b1b624e48b 100644 --- a/app/code/Magento/Captcha/etc/db_schema_whitelist.json +++ b/app/code/Magento/Captcha/etc/db_schema_whitelist.json @@ -1,13 +1,13 @@ { - "captcha_log": { - "column": { - "type": true, - "value": true, - "count": true, - "updated_at": true - }, - "constraint": { - "PRIMARY": true + "captcha_log": { + "column": { + "type": true, + "value": true, + "count": true, + "updated_at": true + }, + "constraint": { + "PRIMARY": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Catalog/etc/db_schema_whitelist.json b/app/code/Magento/Catalog/etc/db_schema_whitelist.json index 1c2c660ca9b00..b1543a6a007f9 100644 --- a/app/code/Magento/Catalog/etc/db_schema_whitelist.json +++ b/app/code/Magento/Catalog/etc/db_schema_whitelist.json @@ -1,1123 +1,1124 @@ { - "catalog_product_entity": { - "column": { - "entity_id": true, - "attribute_set_id": true, - "type_id": true, - "sku": true, - "has_options": true, - "required_options": true, - "created_at": true, - "updated_at": true - }, - "index": { - "CATALOG_PRODUCT_ENTITY_ATTRIBUTE_SET_ID": true, - "CATALOG_PRODUCT_ENTITY_SKU": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_ENTT_ATTR_SET_ID_EAV_ATTR_SET_ATTR_SET_ID": true - } - }, - "catalog_product_entity_datetime": { - "column": { - "value_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CATALOG_PRODUCT_ENTITY_DATETIME_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_ENTITY_DATETIME_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_ENTT_DTIME_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CAT_PRD_ENTT_DTIME_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CATALOG_PRODUCT_ENTITY_DATETIME_STORE_ID_STORE_STORE_ID": true, - "CATALOG_PRODUCT_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "catalog_product_entity_decimal": { - "column": { - "value_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CATALOG_PRODUCT_ENTITY_DECIMAL_STORE_ID": true, - "CATALOG_PRODUCT_ENTITY_DECIMAL_ATTRIBUTE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_ENTT_DEC_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CAT_PRD_ENTT_DEC_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CATALOG_PRODUCT_ENTITY_DECIMAL_STORE_ID_STORE_STORE_ID": true, - "CATALOG_PRODUCT_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "catalog_product_entity_int": { - "column": { - "value_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CATALOG_PRODUCT_ENTITY_INT_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_ENTITY_INT_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_ENTT_INT_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CAT_PRD_ENTT_INT_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CATALOG_PRODUCT_ENTITY_INT_STORE_ID_STORE_STORE_ID": true, - "CATALOG_PRODUCT_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "catalog_product_entity_text": { - "column": { - "value_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CATALOG_PRODUCT_ENTITY_TEXT_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_ENTITY_TEXT_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_ENTT_TEXT_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CAT_PRD_ENTT_TEXT_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CATALOG_PRODUCT_ENTITY_TEXT_STORE_ID_STORE_STORE_ID": true, - "CATALOG_PRODUCT_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "catalog_product_entity_varchar": { - "column": { - "value_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CATALOG_PRODUCT_ENTITY_VARCHAR_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_ENTITY_VARCHAR_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_ENTT_VCHR_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CAT_PRD_ENTT_VCHR_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CATALOG_PRODUCT_ENTITY_VARCHAR_STORE_ID_STORE_STORE_ID": true, - "CATALOG_PRODUCT_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "catalog_product_entity_gallery": { - "column": { - "value_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "position": true, - "value": true - }, - "index": { - "CATALOG_PRODUCT_ENTITY_GALLERY_ENTITY_ID": true, - "CATALOG_PRODUCT_ENTITY_GALLERY_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_ENTITY_GALLERY_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_ENTT_GLR_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CAT_PRD_ENTT_GLR_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CATALOG_PRODUCT_ENTITY_GALLERY_STORE_ID_STORE_STORE_ID": true, - "CATALOG_PRODUCT_ENTITY_GALLERY_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "catalog_category_entity": { - "column": { - "entity_id": true, - "attribute_set_id": true, - "parent_id": true, - "created_at": true, - "updated_at": true, - "path": true, - "position": true, - "level": true, - "children_count": true - }, - "index": { - "CATALOG_CATEGORY_ENTITY_LEVEL": true, - "CATALOG_CATEGORY_ENTITY_PATH": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_category_entity_datetime": { - "column": { - "value_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CATALOG_CATEGORY_ENTITY_DATETIME_ENTITY_ID": true, - "CATALOG_CATEGORY_ENTITY_DATETIME_ATTRIBUTE_ID": true, - "CATALOG_CATEGORY_ENTITY_DATETIME_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_CTGR_ENTT_DTIME_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CAT_CTGR_ENTT_DTIME_ENTT_ID_CAT_CTGR_ENTT_ENTT_ID": true, - "CATALOG_CATEGORY_ENTITY_DATETIME_STORE_ID_STORE_STORE_ID": true, - "CATALOG_CATEGORY_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "catalog_category_entity_decimal": { - "column": { - "value_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CATALOG_CATEGORY_ENTITY_DECIMAL_ENTITY_ID": true, - "CATALOG_CATEGORY_ENTITY_DECIMAL_ATTRIBUTE_ID": true, - "CATALOG_CATEGORY_ENTITY_DECIMAL_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_CTGR_ENTT_DEC_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CAT_CTGR_ENTT_DEC_ENTT_ID_CAT_CTGR_ENTT_ENTT_ID": true, - "CATALOG_CATEGORY_ENTITY_DECIMAL_STORE_ID_STORE_STORE_ID": true, - "CATALOG_CATEGORY_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "catalog_category_entity_int": { - "column": { - "value_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CATALOG_CATEGORY_ENTITY_INT_ENTITY_ID": true, - "CATALOG_CATEGORY_ENTITY_INT_ATTRIBUTE_ID": true, - "CATALOG_CATEGORY_ENTITY_INT_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_CTGR_ENTT_INT_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CAT_CTGR_ENTT_INT_ENTT_ID_CAT_CTGR_ENTT_ENTT_ID": true, - "CATALOG_CATEGORY_ENTITY_INT_STORE_ID_STORE_STORE_ID": true, - "CATALOG_CATEGORY_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "catalog_category_entity_text": { - "column": { - "value_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CATALOG_CATEGORY_ENTITY_TEXT_ENTITY_ID": true, - "CATALOG_CATEGORY_ENTITY_TEXT_ATTRIBUTE_ID": true, - "CATALOG_CATEGORY_ENTITY_TEXT_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_CTGR_ENTT_TEXT_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CAT_CTGR_ENTT_TEXT_ENTT_ID_CAT_CTGR_ENTT_ENTT_ID": true, - "CATALOG_CATEGORY_ENTITY_TEXT_STORE_ID_STORE_STORE_ID": true, - "CATALOG_CATEGORY_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "catalog_category_entity_varchar": { - "column": { - "value_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CATALOG_CATEGORY_ENTITY_VARCHAR_ENTITY_ID": true, - "CATALOG_CATEGORY_ENTITY_VARCHAR_ATTRIBUTE_ID": true, - "CATALOG_CATEGORY_ENTITY_VARCHAR_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_CTGR_ENTT_VCHR_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CAT_CTGR_ENTT_VCHR_ENTT_ID_CAT_CTGR_ENTT_ENTT_ID": true, - "CATALOG_CATEGORY_ENTITY_VARCHAR_STORE_ID_STORE_STORE_ID": true, - "CATALOG_CATEGORY_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "catalog_category_product": { - "column": { - "entity_id": true, - "category_id": true, - "product_id": true, - "position": true - }, - "index": { - "CATALOG_CATEGORY_PRODUCT_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_CTGR_PRD_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CAT_CTGR_PRD_CTGR_ID_CAT_CTGR_ENTT_ENTT_ID": true, - "CATALOG_CATEGORY_PRODUCT_CATEGORY_ID_PRODUCT_ID": true, - "CAT_CTGR_PRD_CTGR_ID_SEQUENCE_CAT_CTGR_SEQUENCE_VAL": true - } - }, - "catalog_category_product_index": { - "column": { - "category_id": true, - "product_id": true, - "position": true, - "is_parent": true, - "store_id": true, - "visibility": true - }, - "index": { - "CAT_CTGR_PRD_IDX_PRD_ID_STORE_ID_CTGR_ID_VISIBILITY": true, - "CAT_CTGR_PRD_IDX_STORE_ID_CTGR_ID_VISIBILITY_IS_PARENT_POSITION": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_compare_item": { - "column": { - "catalog_compare_item_id": true, - "visitor_id": true, - "customer_id": true, - "product_id": true, - "store_id": true - }, - "index": { - "CATALOG_COMPARE_ITEM_PRODUCT_ID": true, - "CATALOG_COMPARE_ITEM_VISITOR_ID_PRODUCT_ID": true, - "CATALOG_COMPARE_ITEM_CUSTOMER_ID_PRODUCT_ID": true, - "CATALOG_COMPARE_ITEM_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CATALOG_COMPARE_ITEM_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "CATALOG_COMPARE_ITEM_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, - "CATALOG_COMPARE_ITEM_STORE_ID_STORE_STORE_ID": true - } - }, - "catalog_product_website": { - "column": { - "product_id": true, - "website_id": true - }, - "index": { - "CATALOG_PRODUCT_WEBSITE_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CATALOG_PRODUCT_WEBSITE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, - "CAT_PRD_WS_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true - } - }, - "catalog_product_link_type": { - "column": { - "link_type_id": true, - "code": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_link": { - "column": { - "link_id": true, - "product_id": true, - "linked_product_id": true, - "link_type_id": true - }, - "index": { - "CATALOG_PRODUCT_LINK_PRODUCT_ID": true, - "CATALOG_PRODUCT_LINK_LINKED_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_LNK_LNKED_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CATALOG_PRODUCT_LINK_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, - "CAT_PRD_LNK_LNK_TYPE_ID_CAT_PRD_LNK_TYPE_LNK_TYPE_ID": true, - "CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID": true - } - }, - "catalog_product_link_attribute": { - "column": { - "product_link_attribute_id": true, - "link_type_id": true, - "product_link_attribute_code": true, - "data_type": true - }, - "index": { - "CATALOG_PRODUCT_LINK_ATTRIBUTE_LINK_TYPE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_LNK_ATTR_LNK_TYPE_ID_CAT_PRD_LNK_TYPE_LNK_TYPE_ID": true - } - }, - "catalog_product_link_attribute_decimal": { - "column": { - "value_id": true, - "product_link_attribute_id": true, - "link_id": true, - "value": true - }, - "index": { - "CATALOG_PRODUCT_LINK_ATTRIBUTE_DECIMAL_LINK_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_LNK_ATTR_DEC_LNK_ID_CAT_PRD_LNK_LNK_ID": true, - "FK_AB2EFA9A14F7BCF1D5400056203D14B6": true, - "CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID": true - } - }, - "catalog_product_link_attribute_int": { - "column": { - "value_id": true, - "product_link_attribute_id": true, - "link_id": true, - "value": true - }, - "index": { - "CATALOG_PRODUCT_LINK_ATTRIBUTE_INT_LINK_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_LNK_ATTR_INT_LNK_ID_CAT_PRD_LNK_LNK_ID": true, - "FK_D6D878F8BA2A4282F8DDED7E6E3DE35C": true, - "CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID": true - } - }, - "catalog_product_link_attribute_varchar": { - "column": { - "value_id": true, - "product_link_attribute_id": true, - "link_id": true, - "value": true - }, - "index": { - "CATALOG_PRODUCT_LINK_ATTRIBUTE_VARCHAR_LINK_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_LNK_ATTR_VCHR_LNK_ID_CAT_PRD_LNK_LNK_ID": true, - "FK_DEE9C4DA61CFCC01DFCF50F0D79CEA51": true, - "CAT_PRD_LNK_ATTR_VCHR_PRD_LNK_ATTR_ID_LNK_ID": true - } - }, - "catalog_product_entity_tier_price": { - "column": { - "value_id": true, - "entity_id": true, - "all_groups": true, - "customer_group_id": true, - "qty": true, - "value": true, - "website_id": true, - "percentage_value": true - }, - "index": { - "CATALOG_PRODUCT_ENTITY_TIER_PRICE_CUSTOMER_GROUP_ID": true, - "CATALOG_PRODUCT_ENTITY_TIER_PRICE_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_ENTT_TIER_PRICE_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true, - "CAT_PRD_ENTT_TIER_PRICE_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CAT_PRD_ENTT_TIER_PRICE_WS_ID_STORE_WS_WS_ID": true, - "UNQ_E8AB433B9ACB00343ABB312AD2FAB087": true - } - }, - "catalog_product_entity_media_gallery": { - "column": { - "value_id": true, - "attribute_id": true, - "entity_id": true, - "value": true, - "media_type": true, - "disabled": true - }, - "index": { - "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_ENTITY_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_ENTT_MDA_GLR_ATTR_ID_EAV_ATTR_ATTR_ID": true - } - }, - "catalog_product_entity_media_gallery_value": { - "column": { - "value_id": true, - "store_id": true, - "entity_id": true, - "label": true, - "position": true, - "disabled": true, - "record_id": true - }, - "index": { - "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_STORE_ID": true, - "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_ENTITY_ID": true, - "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_VALUE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_ENTT_MDA_GLR_VAL_VAL_ID_CAT_PRD_ENTT_MDA_GLR_VAL_ID": true, - "CAT_PRD_ENTT_MDA_GLR_VAL_STORE_ID_STORE_STORE_ID": true, - "CAT_PRD_ENTT_MDA_GLR_VAL_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true - } - }, - "catalog_product_option": { - "column": { - "option_id": true, - "product_id": true, - "type": true, - "is_require": true, - "sku": true, - "max_characters": true, - "file_extension": true, - "image_size_x": true, - "image_size_y": true, - "sort_order": true - }, - "index": { - "CATALOG_PRODUCT_OPTION_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_OPT_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true - } - }, - "catalog_product_option_price": { - "column": { - "option_price_id": true, - "option_id": true, - "store_id": true, - "price": true, - "price_type": true - }, - "index": { - "CATALOG_PRODUCT_OPTION_PRICE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_OPT_PRICE_OPT_ID_CAT_PRD_OPT_OPT_ID": true, - "CATALOG_PRODUCT_OPTION_PRICE_STORE_ID_STORE_STORE_ID": true, - "CATALOG_PRODUCT_OPTION_PRICE_OPTION_ID_STORE_ID": true - } - }, - "catalog_product_option_title": { - "column": { - "option_title_id": true, - "option_id": true, - "store_id": true, - "title": true - }, - "index": { - "CATALOG_PRODUCT_OPTION_TITLE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_OPT_TTL_OPT_ID_CAT_PRD_OPT_OPT_ID": true, - "CATALOG_PRODUCT_OPTION_TITLE_STORE_ID_STORE_STORE_ID": true, - "CATALOG_PRODUCT_OPTION_TITLE_OPTION_ID_STORE_ID": true - } - }, - "catalog_product_option_type_value": { - "column": { - "option_type_id": true, - "option_id": true, - "sku": true, - "sort_order": true - }, - "index": { - "CATALOG_PRODUCT_OPTION_TYPE_VALUE_OPTION_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_OPT_TYPE_VAL_OPT_ID_CAT_PRD_OPT_OPT_ID": true - } - }, - "catalog_product_option_type_price": { - "column": { - "option_type_price_id": true, - "option_type_id": true, - "store_id": true, - "price": true, - "price_type": true - }, - "index": { - "CATALOG_PRODUCT_OPTION_TYPE_PRICE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "FK_B523E3378E8602F376CC415825576B7F": true, - "CATALOG_PRODUCT_OPTION_TYPE_PRICE_STORE_ID_STORE_STORE_ID": true, - "CATALOG_PRODUCT_OPTION_TYPE_PRICE_OPTION_TYPE_ID_STORE_ID": true - } - }, - "catalog_product_option_type_title": { - "column": { - "option_type_title_id": true, - "option_type_id": true, - "store_id": true, - "title": true - }, - "index": { - "CATALOG_PRODUCT_OPTION_TYPE_TITLE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "FK_C085B9CF2C2A302E8043FDEA1937D6A2": true, - "CATALOG_PRODUCT_OPTION_TYPE_TITLE_STORE_ID_STORE_STORE_ID": true, - "CATALOG_PRODUCT_OPTION_TYPE_TITLE_OPTION_TYPE_ID_STORE_ID": true - } - }, - "catalog_eav_attribute": { - "column": { - "attribute_id": true, - "frontend_input_renderer": true, - "is_global": true, - "is_visible": true, - "is_searchable": true, - "is_filterable": true, - "is_comparable": true, - "is_visible_on_front": true, - "is_html_allowed_on_front": true, - "is_used_for_price_rules": true, - "is_filterable_in_search": true, - "used_in_product_listing": true, - "used_for_sort_by": true, - "apply_to": true, - "is_visible_in_advanced_search": true, - "position": true, - "is_wysiwyg_enabled": true, - "is_used_for_promo_rules": true, - "is_required_in_admin_store": true, - "is_used_in_grid": true, - "is_visible_in_grid": true, - "is_filterable_in_grid": true - }, - "index": { - "CATALOG_EAV_ATTRIBUTE_USED_FOR_SORT_BY": true, - "CATALOG_EAV_ATTRIBUTE_USED_IN_PRODUCT_LISTING": true - }, - "constraint": { - "PRIMARY": true, - "CATALOG_EAV_ATTRIBUTE_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true - } - }, - "catalog_product_relation": { - "column": { - "parent_id": true, - "child_id": true - }, - "index": { - "CATALOG_PRODUCT_RELATION_CHILD_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_RELATION_CHILD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CAT_PRD_RELATION_PARENT_ID_CAT_PRD_ENTT_ENTT_ID": true - } - }, - "catalog_product_index_eav": { - "column": { - "entity_id": true, - "attribute_id": true, - "store_id": true, - "value": true, - "source_id": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_EAV_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_STORE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_IDX_EAV_ENTT_ID_ATTR_ID_STORE_ID_VAL_SOURCE_ID": true - } - }, - "catalog_product_index_eav_decimal": { - "column": { - "entity_id": true, - "attribute_id": true, - "store_id": true, - "value": true, - "source_id": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_STORE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_IDX_EAV_DEC_ENTT_ID_ATTR_ID_STORE_ID_VAL_SOURCE_ID": true - } - }, - "catalog_product_index_price": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "tax_class_id": true, - "price": true, - "final_price": true, - "min_price": true, - "max_price": true, - "tier_price": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_PRICE_CUSTOMER_GROUP_ID": true, - "CATALOG_PRODUCT_INDEX_PRICE_MIN_PRICE": true, - "CAT_PRD_IDX_PRICE_WS_ID_CSTR_GROUP_ID_MIN_PRICE": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_IDX_PRICE_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true, - "CAT_PRD_IDX_PRICE_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CATALOG_PRODUCT_INDEX_PRICE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true - } - }, - "catalog_product_index_tier_price": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "min_price": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_TIER_PRICE_CUSTOMER_GROUP_ID": true, - "CATALOG_PRODUCT_INDEX_TIER_PRICE_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_IDX_TIER_PRICE_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true, - "CAT_PRD_IDX_TIER_PRICE_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CAT_PRD_IDX_TIER_PRICE_WS_ID_STORE_WS_WS_ID": true - } - }, - "catalog_product_index_website": { - "column": { - "website_id": true, - "website_date": true, - "rate": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_WEBSITE_WEBSITE_DATE": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_IDX_WS_WS_ID_STORE_WS_WS_ID": true - } - }, - "catalog_product_index_price_cfg_opt_agr_idx": { - "column": { - "parent_id": true, - "child_id": true, - "customer_group_id": true, - "website_id": true, - "price": true, - "tier_price": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_cfg_opt_agr_tmp": { - "column": { - "parent_id": true, - "child_id": true, - "customer_group_id": true, - "website_id": true, - "price": true, - "tier_price": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_cfg_opt_idx": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "min_price": true, - "max_price": true, - "tier_price": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_cfg_opt_tmp": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "min_price": true, - "max_price": true, - "tier_price": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_final_idx": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "tax_class_id": true, - "orig_price": true, - "price": true, - "min_price": true, - "max_price": true, - "tier_price": true, - "base_tier": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_final_tmp": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "tax_class_id": true, - "orig_price": true, - "price": true, - "min_price": true, - "max_price": true, - "tier_price": true, - "base_tier": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_opt_idx": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "min_price": true, - "max_price": true, - "tier_price": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_opt_tmp": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "min_price": true, - "max_price": true, - "tier_price": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_opt_agr_idx": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "option_id": true, - "min_price": true, - "max_price": true, - "tier_price": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_opt_agr_tmp": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "option_id": true, - "min_price": true, - "max_price": true, - "tier_price": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_eav_idx": { - "column": { - "entity_id": true, - "attribute_id": true, - "store_id": true, - "value": true, - "source_id": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_EAV_IDX_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_IDX_STORE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_IDX_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_IDX_EAV_IDX_ENTT_ID_ATTR_ID_STORE_ID_VAL_SOURCE_ID": true - } - }, - "catalog_product_index_eav_tmp": { - "column": { - "entity_id": true, - "attribute_id": true, - "store_id": true, - "value": true, - "source_id": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_EAV_TMP_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_TMP_STORE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_TMP_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_IDX_EAV_TMP_ENTT_ID_ATTR_ID_STORE_ID_VAL_SOURCE_ID": true - } - }, - "catalog_product_index_eav_decimal_idx": { - "column": { - "entity_id": true, - "attribute_id": true, - "store_id": true, - "value": true, - "source_id": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_IDX_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_IDX_STORE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_IDX_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_IDX_EAV_DEC_IDX_ENTT_ID_ATTR_ID_STORE_ID_VAL_SOURCE_ID": true - } - }, - "catalog_product_index_eav_decimal_tmp": { - "column": { - "entity_id": true, - "attribute_id": true, - "store_id": true, - "value": true, - "source_id": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_TMP_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_TMP_STORE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_TMP_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_IDX_EAV_DEC_TMP_ENTT_ID_ATTR_ID_STORE_ID_VAL_SOURCE_ID": true - } - }, - "catalog_product_index_price_idx": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "tax_class_id": true, - "price": true, - "final_price": true, - "min_price": true, - "max_price": true, - "tier_price": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_PRICE_IDX_CUSTOMER_GROUP_ID": true, - "CATALOG_PRODUCT_INDEX_PRICE_IDX_WEBSITE_ID": true, - "CATALOG_PRODUCT_INDEX_PRICE_IDX_MIN_PRICE": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_tmp": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "tax_class_id": true, - "price": true, - "final_price": true, - "min_price": true, - "max_price": true, - "tier_price": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_PRICE_TMP_CUSTOMER_GROUP_ID": true, - "CATALOG_PRODUCT_INDEX_PRICE_TMP_WEBSITE_ID": true, - "CATALOG_PRODUCT_INDEX_PRICE_TMP_MIN_PRICE": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_category_product_index_tmp": { - "column": { - "category_id": true, - "product_id": true, - "position": true, - "is_parent": true, - "store_id": true, - "visibility": true - }, - "constraint": { - "PRIMARY": true - }, - "index": { - "CAT_CTGR_PRD_IDX_TMP_PRD_ID_CTGR_ID_STORE_ID": true - } - }, - "catalog_product_entity_media_gallery_value_to_entity": { - "column": { - "value_id": true, - "entity_id": true - }, - "constraint": { - "FK_A6C6C8FAA386736921D3A7C4B50B1185": true, - "CAT_PRD_ENTT_MDA_GLR_VAL_TO_ENTT_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CAT_PRD_ENTT_MDA_GLR_VAL_TO_ENTT_VAL_ID_ENTT_ID": true - } - }, - "catalog_product_index_eav_replica": { - "column": { - "entity_id": true, - "attribute_id": true, - "store_id": true, - "value": true, - "source_id": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_EAV_REPLICA_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_REPLICA_STORE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_REPLICA_VALUE": true, - "CATALOG_PRODUCT_INDEX_EAV_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_STORE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_VALUE": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_eav_decimal_replica": { - "column": { - "entity_id": true, - "attribute_id": true, - "store_id": true, - "value": true, - "source_id": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_REPLICA_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_REPLICA_STORE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_REPLICA_VALUE": true, - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_ATTRIBUTE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_STORE_ID": true, - "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_VALUE": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_replica": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "tax_class_id": true, - "price": true, - "final_price": true, - "min_price": true, - "max_price": true, - "tier_price": true - }, - "index": { - "CATALOG_PRODUCT_INDEX_PRICE_REPLICA_CUSTOMER_GROUP_ID": true, - "CATALOG_PRODUCT_INDEX_PRICE_REPLICA_MIN_PRICE": true, - "CAT_PRD_IDX_PRICE_REPLICA_WS_ID_CSTR_GROUP_ID_MIN_PRICE": true, - "CATALOG_PRODUCT_INDEX_PRICE_CUSTOMER_GROUP_ID": true, - "CATALOG_PRODUCT_INDEX_PRICE_MIN_PRICE": true, - "CAT_PRD_IDX_PRICE_WS_ID_CSTR_GROUP_ID_MIN_PRICE": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_category_product_index_replica": { - "column": { - "category_id": true, - "product_id": true, - "position": true, - "is_parent": true, - "store_id": true, - "visibility": true - }, - "index": { - "CAT_CTGR_PRD_IDX_REPLICA_PRD_ID_STORE_ID_CTGR_ID_VISIBILITY": true, - "IDX_87EB2E3059853CF89A75B4C55074810B": true, - "CAT_CTGR_PRD_IDX_PRD_ID_STORE_ID_CTGR_ID_VISIBILITY": true, - "CAT_CTGR_PRD_IDX_STORE_ID_CTGR_ID_VISIBILITY_IS_PARENT_POSITION": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_frontend_action": { - "column": { - "action_id": true, - "type_id": true, - "visitor_id": true, - "customer_id": true, - "product_id": true, - "added_at": true - }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_FRONTEND_ACTION_CSTR_ID_CSTR_ENTT_ENTT_ID": true, - "CAT_PRD_FRONTEND_ACTION_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CATALOG_PRODUCT_FRONTEND_ACTION_VISITOR_ID_PRODUCT_ID_TYPE_ID": true, - "CATALOG_PRODUCT_FRONTEND_ACTION_CUSTOMER_ID_PRODUCT_ID_TYPE_ID": true + "catalog_product_entity": { + "column": { + "entity_id": true, + "attribute_set_id": true, + "type_id": true, + "sku": true, + "has_options": true, + "required_options": true, + "created_at": true, + "updated_at": true + }, + "index": { + "CATALOG_PRODUCT_ENTITY_ATTRIBUTE_SET_ID": true, + "CATALOG_PRODUCT_ENTITY_SKU": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_ENTT_ATTR_SET_ID_EAV_ATTR_SET_ATTR_SET_ID": true + } + }, + "catalog_product_entity_datetime": { + "column": { + "value_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CATALOG_PRODUCT_ENTITY_DATETIME_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_ENTITY_DATETIME_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_ENTT_DTIME_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CAT_PRD_ENTT_DTIME_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CATALOG_PRODUCT_ENTITY_DATETIME_STORE_ID_STORE_STORE_ID": true, + "CATALOG_PRODUCT_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "catalog_product_entity_decimal": { + "column": { + "value_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CATALOG_PRODUCT_ENTITY_DECIMAL_STORE_ID": true, + "CATALOG_PRODUCT_ENTITY_DECIMAL_ATTRIBUTE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_ENTT_DEC_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CAT_PRD_ENTT_DEC_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CATALOG_PRODUCT_ENTITY_DECIMAL_STORE_ID_STORE_STORE_ID": true, + "CATALOG_PRODUCT_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "catalog_product_entity_int": { + "column": { + "value_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CATALOG_PRODUCT_ENTITY_INT_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_ENTITY_INT_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_ENTT_INT_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CAT_PRD_ENTT_INT_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CATALOG_PRODUCT_ENTITY_INT_STORE_ID_STORE_STORE_ID": true, + "CATALOG_PRODUCT_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "catalog_product_entity_text": { + "column": { + "value_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CATALOG_PRODUCT_ENTITY_TEXT_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_ENTITY_TEXT_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_ENTT_TEXT_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CAT_PRD_ENTT_TEXT_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CATALOG_PRODUCT_ENTITY_TEXT_STORE_ID_STORE_STORE_ID": true, + "CATALOG_PRODUCT_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "catalog_product_entity_varchar": { + "column": { + "value_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CATALOG_PRODUCT_ENTITY_VARCHAR_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_ENTITY_VARCHAR_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_ENTT_VCHR_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CAT_PRD_ENTT_VCHR_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CATALOG_PRODUCT_ENTITY_VARCHAR_STORE_ID_STORE_STORE_ID": true, + "CATALOG_PRODUCT_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "catalog_product_entity_gallery": { + "column": { + "value_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "position": true, + "value": true + }, + "index": { + "CATALOG_PRODUCT_ENTITY_GALLERY_ENTITY_ID": true, + "CATALOG_PRODUCT_ENTITY_GALLERY_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_ENTITY_GALLERY_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_ENTT_GLR_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CAT_PRD_ENTT_GLR_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CATALOG_PRODUCT_ENTITY_GALLERY_STORE_ID_STORE_STORE_ID": true, + "CATALOG_PRODUCT_ENTITY_GALLERY_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "catalog_category_entity": { + "column": { + "entity_id": true, + "attribute_set_id": true, + "parent_id": true, + "created_at": true, + "updated_at": true, + "path": true, + "position": true, + "level": true, + "children_count": true + }, + "index": { + "CATALOG_CATEGORY_ENTITY_LEVEL": true, + "CATALOG_CATEGORY_ENTITY_PATH": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_category_entity_datetime": { + "column": { + "value_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CATALOG_CATEGORY_ENTITY_DATETIME_ENTITY_ID": true, + "CATALOG_CATEGORY_ENTITY_DATETIME_ATTRIBUTE_ID": true, + "CATALOG_CATEGORY_ENTITY_DATETIME_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_CTGR_ENTT_DTIME_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CAT_CTGR_ENTT_DTIME_ENTT_ID_CAT_CTGR_ENTT_ENTT_ID": true, + "CATALOG_CATEGORY_ENTITY_DATETIME_STORE_ID_STORE_STORE_ID": true, + "CATALOG_CATEGORY_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "catalog_category_entity_decimal": { + "column": { + "value_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CATALOG_CATEGORY_ENTITY_DECIMAL_ENTITY_ID": true, + "CATALOG_CATEGORY_ENTITY_DECIMAL_ATTRIBUTE_ID": true, + "CATALOG_CATEGORY_ENTITY_DECIMAL_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_CTGR_ENTT_DEC_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CAT_CTGR_ENTT_DEC_ENTT_ID_CAT_CTGR_ENTT_ENTT_ID": true, + "CATALOG_CATEGORY_ENTITY_DECIMAL_STORE_ID_STORE_STORE_ID": true, + "CATALOG_CATEGORY_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "catalog_category_entity_int": { + "column": { + "value_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CATALOG_CATEGORY_ENTITY_INT_ENTITY_ID": true, + "CATALOG_CATEGORY_ENTITY_INT_ATTRIBUTE_ID": true, + "CATALOG_CATEGORY_ENTITY_INT_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_CTGR_ENTT_INT_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CAT_CTGR_ENTT_INT_ENTT_ID_CAT_CTGR_ENTT_ENTT_ID": true, + "CATALOG_CATEGORY_ENTITY_INT_STORE_ID_STORE_STORE_ID": true, + "CATALOG_CATEGORY_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "catalog_category_entity_text": { + "column": { + "value_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CATALOG_CATEGORY_ENTITY_TEXT_ENTITY_ID": true, + "CATALOG_CATEGORY_ENTITY_TEXT_ATTRIBUTE_ID": true, + "CATALOG_CATEGORY_ENTITY_TEXT_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_CTGR_ENTT_TEXT_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CAT_CTGR_ENTT_TEXT_ENTT_ID_CAT_CTGR_ENTT_ENTT_ID": true, + "CATALOG_CATEGORY_ENTITY_TEXT_STORE_ID_STORE_STORE_ID": true, + "CATALOG_CATEGORY_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "catalog_category_entity_varchar": { + "column": { + "value_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CATALOG_CATEGORY_ENTITY_VARCHAR_ENTITY_ID": true, + "CATALOG_CATEGORY_ENTITY_VARCHAR_ATTRIBUTE_ID": true, + "CATALOG_CATEGORY_ENTITY_VARCHAR_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_CTGR_ENTT_VCHR_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CAT_CTGR_ENTT_VCHR_ENTT_ID_CAT_CTGR_ENTT_ENTT_ID": true, + "CATALOG_CATEGORY_ENTITY_VARCHAR_STORE_ID_STORE_STORE_ID": true, + "CATALOG_CATEGORY_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "catalog_category_product": { + "column": { + "entity_id": true, + "category_id": true, + "product_id": true, + "position": true + }, + "index": { + "CATALOG_CATEGORY_PRODUCT_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_CTGR_PRD_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CAT_CTGR_PRD_CTGR_ID_CAT_CTGR_ENTT_ENTT_ID": true, + "CATALOG_CATEGORY_PRODUCT_CATEGORY_ID_PRODUCT_ID": true, + "CAT_CTGR_PRD_CTGR_ID_SEQUENCE_CAT_CTGR_SEQUENCE_VAL": true + } + }, + "catalog_category_product_index": { + "column": { + "category_id": true, + "product_id": true, + "position": true, + "is_parent": true, + "store_id": true, + "visibility": true + }, + "index": { + "CAT_CTGR_PRD_IDX_PRD_ID_STORE_ID_CTGR_ID_VISIBILITY": true, + "CAT_CTGR_PRD_IDX_STORE_ID_CTGR_ID_VISIBILITY_IS_PARENT_POSITION": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_compare_item": { + "column": { + "catalog_compare_item_id": true, + "visitor_id": true, + "customer_id": true, + "product_id": true, + "store_id": true + }, + "index": { + "CATALOG_COMPARE_ITEM_PRODUCT_ID": true, + "CATALOG_COMPARE_ITEM_VISITOR_ID_PRODUCT_ID": true, + "CATALOG_COMPARE_ITEM_CUSTOMER_ID_PRODUCT_ID": true, + "CATALOG_COMPARE_ITEM_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CATALOG_COMPARE_ITEM_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "CATALOG_COMPARE_ITEM_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, + "CATALOG_COMPARE_ITEM_STORE_ID_STORE_STORE_ID": true + } + }, + "catalog_product_website": { + "column": { + "product_id": true, + "website_id": true + }, + "index": { + "CATALOG_PRODUCT_WEBSITE_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CATALOG_PRODUCT_WEBSITE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, + "CAT_PRD_WS_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true + } + }, + "catalog_product_link_type": { + "column": { + "link_type_id": true, + "code": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_link": { + "column": { + "link_id": true, + "product_id": true, + "linked_product_id": true, + "link_type_id": true + }, + "index": { + "CATALOG_PRODUCT_LINK_PRODUCT_ID": true, + "CATALOG_PRODUCT_LINK_LINKED_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_LNK_LNKED_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CATALOG_PRODUCT_LINK_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, + "CAT_PRD_LNK_LNK_TYPE_ID_CAT_PRD_LNK_TYPE_LNK_TYPE_ID": true, + "CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID": true + } + }, + "catalog_product_link_attribute": { + "column": { + "product_link_attribute_id": true, + "link_type_id": true, + "product_link_attribute_code": true, + "data_type": true + }, + "index": { + "CATALOG_PRODUCT_LINK_ATTRIBUTE_LINK_TYPE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_LNK_ATTR_LNK_TYPE_ID_CAT_PRD_LNK_TYPE_LNK_TYPE_ID": true + } + }, + "catalog_product_link_attribute_decimal": { + "column": { + "value_id": true, + "product_link_attribute_id": true, + "link_id": true, + "value": true + }, + "index": { + "CATALOG_PRODUCT_LINK_ATTRIBUTE_DECIMAL_LINK_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_LNK_ATTR_DEC_LNK_ID_CAT_PRD_LNK_LNK_ID": true, + "FK_AB2EFA9A14F7BCF1D5400056203D14B6": true, + "CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID": true + } + }, + "catalog_product_link_attribute_int": { + "column": { + "value_id": true, + "product_link_attribute_id": true, + "link_id": true, + "value": true + }, + "index": { + "CATALOG_PRODUCT_LINK_ATTRIBUTE_INT_LINK_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_LNK_ATTR_INT_LNK_ID_CAT_PRD_LNK_LNK_ID": true, + "FK_D6D878F8BA2A4282F8DDED7E6E3DE35C": true, + "CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID": true + } + }, + "catalog_product_link_attribute_varchar": { + "column": { + "value_id": true, + "product_link_attribute_id": true, + "link_id": true, + "value": true + }, + "index": { + "CATALOG_PRODUCT_LINK_ATTRIBUTE_VARCHAR_LINK_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_LNK_ATTR_VCHR_LNK_ID_CAT_PRD_LNK_LNK_ID": true, + "FK_DEE9C4DA61CFCC01DFCF50F0D79CEA51": true, + "CAT_PRD_LNK_ATTR_VCHR_PRD_LNK_ATTR_ID_LNK_ID": true + } + }, + "catalog_product_entity_tier_price": { + "column": { + "value_id": true, + "entity_id": true, + "all_groups": true, + "customer_group_id": true, + "qty": true, + "value": true, + "website_id": true, + "percentage_value": true + }, + "index": { + "CATALOG_PRODUCT_ENTITY_TIER_PRICE_CUSTOMER_GROUP_ID": true, + "CATALOG_PRODUCT_ENTITY_TIER_PRICE_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_ENTT_TIER_PRICE_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true, + "CAT_PRD_ENTT_TIER_PRICE_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CAT_PRD_ENTT_TIER_PRICE_WS_ID_STORE_WS_WS_ID": true, + "UNQ_E8AB433B9ACB00343ABB312AD2FAB087": true + } + }, + "catalog_product_entity_media_gallery": { + "column": { + "value_id": true, + "attribute_id": true, + "entity_id": true, + "value": true, + "media_type": true, + "disabled": true + }, + "index": { + "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_ENTITY_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_ENTT_MDA_GLR_ATTR_ID_EAV_ATTR_ATTR_ID": true + } + }, + "catalog_product_entity_media_gallery_value": { + "column": { + "value_id": true, + "store_id": true, + "entity_id": true, + "label": true, + "position": true, + "disabled": true, + "record_id": true + }, + "index": { + "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_STORE_ID": true, + "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_ENTITY_ID": true, + "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_VALUE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_ENTT_MDA_GLR_VAL_VAL_ID_CAT_PRD_ENTT_MDA_GLR_VAL_ID": true, + "CAT_PRD_ENTT_MDA_GLR_VAL_STORE_ID_STORE_STORE_ID": true, + "CAT_PRD_ENTT_MDA_GLR_VAL_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true + } + }, + "catalog_product_option": { + "column": { + "option_id": true, + "product_id": true, + "type": true, + "is_require": true, + "sku": true, + "max_characters": true, + "file_extension": true, + "image_size_x": true, + "image_size_y": true, + "sort_order": true + }, + "index": { + "CATALOG_PRODUCT_OPTION_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_OPT_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true + } + }, + "catalog_product_option_price": { + "column": { + "option_price_id": true, + "option_id": true, + "store_id": true, + "price": true, + "price_type": true + }, + "index": { + "CATALOG_PRODUCT_OPTION_PRICE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_OPT_PRICE_OPT_ID_CAT_PRD_OPT_OPT_ID": true, + "CATALOG_PRODUCT_OPTION_PRICE_STORE_ID_STORE_STORE_ID": true, + "CATALOG_PRODUCT_OPTION_PRICE_OPTION_ID_STORE_ID": true + } + }, + "catalog_product_option_title": { + "column": { + "option_title_id": true, + "option_id": true, + "store_id": true, + "title": true + }, + "index": { + "CATALOG_PRODUCT_OPTION_TITLE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_OPT_TTL_OPT_ID_CAT_PRD_OPT_OPT_ID": true, + "CATALOG_PRODUCT_OPTION_TITLE_STORE_ID_STORE_STORE_ID": true, + "CATALOG_PRODUCT_OPTION_TITLE_OPTION_ID_STORE_ID": true + } + }, + "catalog_product_option_type_value": { + "column": { + "option_type_id": true, + "option_id": true, + "sku": true, + "sort_order": true + }, + "index": { + "CATALOG_PRODUCT_OPTION_TYPE_VALUE_OPTION_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_OPT_TYPE_VAL_OPT_ID_CAT_PRD_OPT_OPT_ID": true + } + }, + "catalog_product_option_type_price": { + "column": { + "option_type_price_id": true, + "option_type_id": true, + "store_id": true, + "price": true, + "price_type": true + }, + "index": { + "CATALOG_PRODUCT_OPTION_TYPE_PRICE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "FK_B523E3378E8602F376CC415825576B7F": true, + "CATALOG_PRODUCT_OPTION_TYPE_PRICE_STORE_ID_STORE_STORE_ID": true, + "CATALOG_PRODUCT_OPTION_TYPE_PRICE_OPTION_TYPE_ID_STORE_ID": true + } + }, + "catalog_product_option_type_title": { + "column": { + "option_type_title_id": true, + "option_type_id": true, + "store_id": true, + "title": true + }, + "index": { + "CATALOG_PRODUCT_OPTION_TYPE_TITLE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "FK_C085B9CF2C2A302E8043FDEA1937D6A2": true, + "CATALOG_PRODUCT_OPTION_TYPE_TITLE_STORE_ID_STORE_STORE_ID": true, + "CATALOG_PRODUCT_OPTION_TYPE_TITLE_OPTION_TYPE_ID_STORE_ID": true + } + }, + "catalog_eav_attribute": { + "column": { + "attribute_id": true, + "frontend_input_renderer": true, + "is_global": true, + "is_visible": true, + "is_searchable": true, + "is_filterable": true, + "is_comparable": true, + "is_visible_on_front": true, + "is_html_allowed_on_front": true, + "is_used_for_price_rules": true, + "is_filterable_in_search": true, + "used_in_product_listing": true, + "used_for_sort_by": true, + "apply_to": true, + "is_visible_in_advanced_search": true, + "position": true, + "is_wysiwyg_enabled": true, + "is_used_for_promo_rules": true, + "is_required_in_admin_store": true, + "is_used_in_grid": true, + "is_visible_in_grid": true, + "is_filterable_in_grid": true + }, + "index": { + "CATALOG_EAV_ATTRIBUTE_USED_FOR_SORT_BY": true, + "CATALOG_EAV_ATTRIBUTE_USED_IN_PRODUCT_LISTING": true + }, + "constraint": { + "PRIMARY": true, + "CATALOG_EAV_ATTRIBUTE_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true + } + }, + "catalog_product_relation": { + "column": { + "parent_id": true, + "child_id": true + }, + "index": { + "CATALOG_PRODUCT_RELATION_CHILD_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_RELATION_CHILD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CAT_PRD_RELATION_PARENT_ID_CAT_PRD_ENTT_ENTT_ID": true + } + }, + "catalog_product_index_eav": { + "column": { + "entity_id": true, + "attribute_id": true, + "store_id": true, + "value": true, + "source_id": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_EAV_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_STORE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_IDX_EAV_ENTT_ID_ATTR_ID_STORE_ID_VAL_SOURCE_ID": true + } + }, + "catalog_product_index_eav_decimal": { + "column": { + "entity_id": true, + "attribute_id": true, + "store_id": true, + "value": true, + "source_id": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_STORE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_IDX_EAV_DEC_ENTT_ID_ATTR_ID_STORE_ID_VAL_SOURCE_ID": true + } + }, + "catalog_product_index_price": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "tax_class_id": true, + "price": true, + "final_price": true, + "min_price": true, + "max_price": true, + "tier_price": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_PRICE_CUSTOMER_GROUP_ID": true, + "CATALOG_PRODUCT_INDEX_PRICE_MIN_PRICE": true, + "CAT_PRD_IDX_PRICE_WS_ID_CSTR_GROUP_ID_MIN_PRICE": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_IDX_PRICE_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true, + "CAT_PRD_IDX_PRICE_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CATALOG_PRODUCT_INDEX_PRICE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true + } + }, + "catalog_product_index_tier_price": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "min_price": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_TIER_PRICE_CUSTOMER_GROUP_ID": true, + "CATALOG_PRODUCT_INDEX_TIER_PRICE_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_IDX_TIER_PRICE_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true, + "CAT_PRD_IDX_TIER_PRICE_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CAT_PRD_IDX_TIER_PRICE_WS_ID_STORE_WS_WS_ID": true + } + }, + "catalog_product_index_website": { + "column": { + "website_id": true, + "website_date": true, + "rate": true, + "default_store_id": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_WEBSITE_WEBSITE_DATE": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_IDX_WS_WS_ID_STORE_WS_WS_ID": true + } + }, + "catalog_product_index_price_cfg_opt_agr_idx": { + "column": { + "parent_id": true, + "child_id": true, + "customer_group_id": true, + "website_id": true, + "price": true, + "tier_price": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_cfg_opt_agr_tmp": { + "column": { + "parent_id": true, + "child_id": true, + "customer_group_id": true, + "website_id": true, + "price": true, + "tier_price": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_cfg_opt_idx": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "min_price": true, + "max_price": true, + "tier_price": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_cfg_opt_tmp": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "min_price": true, + "max_price": true, + "tier_price": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_final_idx": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "tax_class_id": true, + "orig_price": true, + "price": true, + "min_price": true, + "max_price": true, + "tier_price": true, + "base_tier": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_final_tmp": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "tax_class_id": true, + "orig_price": true, + "price": true, + "min_price": true, + "max_price": true, + "tier_price": true, + "base_tier": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_opt_idx": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "min_price": true, + "max_price": true, + "tier_price": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_opt_tmp": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "min_price": true, + "max_price": true, + "tier_price": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_opt_agr_idx": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "option_id": true, + "min_price": true, + "max_price": true, + "tier_price": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_opt_agr_tmp": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "option_id": true, + "min_price": true, + "max_price": true, + "tier_price": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_eav_idx": { + "column": { + "entity_id": true, + "attribute_id": true, + "store_id": true, + "value": true, + "source_id": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_EAV_IDX_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_IDX_STORE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_IDX_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_IDX_EAV_IDX_ENTT_ID_ATTR_ID_STORE_ID_VAL_SOURCE_ID": true + } + }, + "catalog_product_index_eav_tmp": { + "column": { + "entity_id": true, + "attribute_id": true, + "store_id": true, + "value": true, + "source_id": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_EAV_TMP_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_TMP_STORE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_TMP_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_IDX_EAV_TMP_ENTT_ID_ATTR_ID_STORE_ID_VAL_SOURCE_ID": true + } + }, + "catalog_product_index_eav_decimal_idx": { + "column": { + "entity_id": true, + "attribute_id": true, + "store_id": true, + "value": true, + "source_id": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_IDX_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_IDX_STORE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_IDX_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_IDX_EAV_DEC_IDX_ENTT_ID_ATTR_ID_STORE_ID_VAL_SOURCE_ID": true + } + }, + "catalog_product_index_eav_decimal_tmp": { + "column": { + "entity_id": true, + "attribute_id": true, + "store_id": true, + "value": true, + "source_id": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_TMP_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_TMP_STORE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_TMP_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_IDX_EAV_DEC_TMP_ENTT_ID_ATTR_ID_STORE_ID_VAL_SOURCE_ID": true + } + }, + "catalog_product_index_price_idx": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "tax_class_id": true, + "price": true, + "final_price": true, + "min_price": true, + "max_price": true, + "tier_price": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_PRICE_IDX_CUSTOMER_GROUP_ID": true, + "CATALOG_PRODUCT_INDEX_PRICE_IDX_WEBSITE_ID": true, + "CATALOG_PRODUCT_INDEX_PRICE_IDX_MIN_PRICE": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_tmp": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "tax_class_id": true, + "price": true, + "final_price": true, + "min_price": true, + "max_price": true, + "tier_price": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_PRICE_TMP_CUSTOMER_GROUP_ID": true, + "CATALOG_PRODUCT_INDEX_PRICE_TMP_WEBSITE_ID": true, + "CATALOG_PRODUCT_INDEX_PRICE_TMP_MIN_PRICE": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_category_product_index_tmp": { + "column": { + "category_id": true, + "product_id": true, + "position": true, + "is_parent": true, + "store_id": true, + "visibility": true + }, + "constraint": { + "PRIMARY": true + }, + "index": { + "CAT_CTGR_PRD_IDX_TMP_PRD_ID_CTGR_ID_STORE_ID": true + } + }, + "catalog_product_entity_media_gallery_value_to_entity": { + "column": { + "value_id": true, + "entity_id": true + }, + "constraint": { + "FK_A6C6C8FAA386736921D3A7C4B50B1185": true, + "CAT_PRD_ENTT_MDA_GLR_VAL_TO_ENTT_ENTT_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CAT_PRD_ENTT_MDA_GLR_VAL_TO_ENTT_VAL_ID_ENTT_ID": true + } + }, + "catalog_product_index_eav_replica": { + "column": { + "entity_id": true, + "attribute_id": true, + "store_id": true, + "value": true, + "source_id": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_EAV_REPLICA_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_REPLICA_STORE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_REPLICA_VALUE": true, + "CATALOG_PRODUCT_INDEX_EAV_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_STORE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_VALUE": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_eav_decimal_replica": { + "column": { + "entity_id": true, + "attribute_id": true, + "store_id": true, + "value": true, + "source_id": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_REPLICA_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_REPLICA_STORE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_REPLICA_VALUE": true, + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_ATTRIBUTE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_STORE_ID": true, + "CATALOG_PRODUCT_INDEX_EAV_DECIMAL_VALUE": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_replica": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "tax_class_id": true, + "price": true, + "final_price": true, + "min_price": true, + "max_price": true, + "tier_price": true + }, + "index": { + "CATALOG_PRODUCT_INDEX_PRICE_REPLICA_CUSTOMER_GROUP_ID": true, + "CATALOG_PRODUCT_INDEX_PRICE_REPLICA_MIN_PRICE": true, + "CAT_PRD_IDX_PRICE_REPLICA_WS_ID_CSTR_GROUP_ID_MIN_PRICE": true, + "CATALOG_PRODUCT_INDEX_PRICE_CUSTOMER_GROUP_ID": true, + "CATALOG_PRODUCT_INDEX_PRICE_MIN_PRICE": true, + "CAT_PRD_IDX_PRICE_WS_ID_CSTR_GROUP_ID_MIN_PRICE": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_category_product_index_replica": { + "column": { + "category_id": true, + "product_id": true, + "position": true, + "is_parent": true, + "store_id": true, + "visibility": true + }, + "index": { + "CAT_CTGR_PRD_IDX_REPLICA_PRD_ID_STORE_ID_CTGR_ID_VISIBILITY": true, + "IDX_87EB2E3059853CF89A75B4C55074810B": true, + "CAT_CTGR_PRD_IDX_PRD_ID_STORE_ID_CTGR_ID_VISIBILITY": true, + "CAT_CTGR_PRD_IDX_STORE_ID_CTGR_ID_VISIBILITY_IS_PARENT_POSITION": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_frontend_action": { + "column": { + "action_id": true, + "type_id": true, + "visitor_id": true, + "customer_id": true, + "product_id": true, + "added_at": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_FRONTEND_ACTION_CSTR_ID_CSTR_ENTT_ENTT_ID": true, + "CAT_PRD_FRONTEND_ACTION_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CATALOG_PRODUCT_FRONTEND_ACTION_VISITOR_ID_PRODUCT_ID_TYPE_ID": true, + "CATALOG_PRODUCT_FRONTEND_ACTION_CUSTOMER_ID_PRODUCT_ID_TYPE_ID": true + } } - } -} +} \ No newline at end of file diff --git a/app/code/Magento/CatalogInventory/etc/db_schema_whitelist.json b/app/code/Magento/CatalogInventory/etc/db_schema_whitelist.json index 6ae1851d7e94e..2580ec1e336f1 100644 --- a/app/code/Magento/CatalogInventory/etc/db_schema_whitelist.json +++ b/app/code/Magento/CatalogInventory/etc/db_schema_whitelist.json @@ -1,126 +1,126 @@ { - "cataloginventory_stock": { - "column": { - "stock_id": true, - "website_id": true, - "stock_name": true + "cataloginventory_stock": { + "column": { + "stock_id": true, + "website_id": true, + "stock_name": true + }, + "index": { + "CATALOGINVENTORY_STOCK_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true + } }, - "index": { - "CATALOGINVENTORY_STOCK_WEBSITE_ID": true + "cataloginventory_stock_item": { + "column": { + "item_id": true, + "product_id": true, + "stock_id": true, + "qty": true, + "min_qty": true, + "use_config_min_qty": true, + "is_qty_decimal": true, + "backorders": true, + "use_config_backorders": true, + "min_sale_qty": true, + "use_config_min_sale_qty": true, + "max_sale_qty": true, + "use_config_max_sale_qty": true, + "is_in_stock": true, + "low_stock_date": true, + "notify_stock_qty": true, + "use_config_notify_stock_qty": true, + "manage_stock": true, + "use_config_manage_stock": true, + "stock_status_changed_auto": true, + "use_config_qty_increments": true, + "qty_increments": true, + "use_config_enable_qty_inc": true, + "enable_qty_increments": true, + "is_decimal_divided": true, + "website_id": true + }, + "index": { + "CATALOGINVENTORY_STOCK_ITEM_WEBSITE_ID": true, + "CATALOGINVENTORY_STOCK_ITEM_STOCK_ID": true + }, + "constraint": { + "PRIMARY": true, + "CATINV_STOCK_ITEM_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CATINV_STOCK_ITEM_STOCK_ID_CATINV_STOCK_STOCK_ID": true, + "CATALOGINVENTORY_STOCK_ITEM_PRODUCT_ID_STOCK_ID": true, + "CATALOGINVENTORY_STOCK_ITEM_PRODUCT_ID_WEBSITE_ID": true, + "CATINV_STOCK_ITEM_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "cataloginventory_stock_item": { - "column": { - "item_id": true, - "product_id": true, - "stock_id": true, - "qty": true, - "min_qty": true, - "use_config_min_qty": true, - "is_qty_decimal": true, - "backorders": true, - "use_config_backorders": true, - "min_sale_qty": true, - "use_config_min_sale_qty": true, - "max_sale_qty": true, - "use_config_max_sale_qty": true, - "is_in_stock": true, - "low_stock_date": true, - "notify_stock_qty": true, - "use_config_notify_stock_qty": true, - "manage_stock": true, - "use_config_manage_stock": true, - "stock_status_changed_auto": true, - "use_config_qty_increments": true, - "qty_increments": true, - "use_config_enable_qty_inc": true, - "enable_qty_increments": true, - "is_decimal_divided": true, - "website_id": true - }, - "index": { - "CATALOGINVENTORY_STOCK_ITEM_WEBSITE_ID": true, - "CATALOGINVENTORY_STOCK_ITEM_STOCK_ID": true - }, - "constraint": { - "PRIMARY": true, - "CATINV_STOCK_ITEM_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CATINV_STOCK_ITEM_STOCK_ID_CATINV_STOCK_STOCK_ID": true, - "CATALOGINVENTORY_STOCK_ITEM_PRODUCT_ID_STOCK_ID": true, - "CATALOGINVENTORY_STOCK_ITEM_PRODUCT_ID_WEBSITE_ID": true, - "CATINV_STOCK_ITEM_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true - } - }, - "cataloginventory_stock_status": { - "column": { - "product_id": true, - "website_id": true, - "stock_id": true, - "qty": true, - "stock_status": true - }, - "index": { - "CATALOGINVENTORY_STOCK_STATUS_STOCK_ID": true, - "CATALOGINVENTORY_STOCK_STATUS_WEBSITE_ID": true, - "CATALOGINVENTORY_STOCK_STATUS_STOCK_STATUS": true - }, - "constraint": { - "PRIMARY": true - } - }, - "cataloginventory_stock_status_idx": { - "column": { - "product_id": true, - "website_id": true, - "stock_id": true, - "qty": true, - "stock_status": true - }, - "index": { - "CATALOGINVENTORY_STOCK_STATUS_IDX_STOCK_ID": true, - "CATALOGINVENTORY_STOCK_STATUS_IDX_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true - } - }, - "cataloginventory_stock_status_tmp": { - "column": { - "product_id": true, - "website_id": true, - "stock_id": true, - "qty": true, - "stock_status": true - }, - "index": { - "CATALOGINVENTORY_STOCK_STATUS_TMP_STOCK_ID": true, - "CATALOGINVENTORY_STOCK_STATUS_TMP_WEBSITE_ID": true + "cataloginventory_stock_status": { + "column": { + "product_id": true, + "website_id": true, + "stock_id": true, + "qty": true, + "stock_status": true + }, + "index": { + "CATALOGINVENTORY_STOCK_STATUS_STOCK_ID": true, + "CATALOGINVENTORY_STOCK_STATUS_WEBSITE_ID": true, + "CATALOGINVENTORY_STOCK_STATUS_STOCK_STATUS": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "cataloginventory_stock_status_replica": { - "column": { - "product_id": true, - "website_id": true, - "stock_id": true, - "qty": true, - "stock_status": true + "cataloginventory_stock_status_idx": { + "column": { + "product_id": true, + "website_id": true, + "stock_id": true, + "qty": true, + "stock_status": true + }, + "index": { + "CATALOGINVENTORY_STOCK_STATUS_IDX_STOCK_ID": true, + "CATALOGINVENTORY_STOCK_STATUS_IDX_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true + } }, - "index": { - "CATALOGINVENTORY_STOCK_STATUS_REPLICA_STOCK_ID": true, - "CATALOGINVENTORY_STOCK_STATUS_REPLICA_WEBSITE_ID": true, - "CATALOGINVENTORY_STOCK_STATUS_REPLICA_STOCK_STATUS": true, - "CATALOGINVENTORY_STOCK_STATUS_STOCK_ID": true, - "CATALOGINVENTORY_STOCK_STATUS_WEBSITE_ID": true, - "CATALOGINVENTORY_STOCK_STATUS_STOCK_STATUS": true + "cataloginventory_stock_status_tmp": { + "column": { + "product_id": true, + "website_id": true, + "stock_id": true, + "qty": true, + "stock_status": true + }, + "index": { + "CATALOGINVENTORY_STOCK_STATUS_TMP_STOCK_ID": true, + "CATALOGINVENTORY_STOCK_STATUS_TMP_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true + "cataloginventory_stock_status_replica": { + "column": { + "product_id": true, + "website_id": true, + "stock_id": true, + "qty": true, + "stock_status": true + }, + "index": { + "CATALOGINVENTORY_STOCK_STATUS_REPLICA_STOCK_ID": true, + "CATALOGINVENTORY_STOCK_STATUS_REPLICA_WEBSITE_ID": true, + "CATALOGINVENTORY_STOCK_STATUS_REPLICA_STOCK_STATUS": true, + "CATALOGINVENTORY_STOCK_STATUS_STOCK_ID": true, + "CATALOGINVENTORY_STOCK_STATUS_WEBSITE_ID": true, + "CATALOGINVENTORY_STOCK_STATUS_STOCK_STATUS": true + }, + "constraint": { + "PRIMARY": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/CatalogRule/etc/db_schema_whitelist.json b/app/code/Magento/CatalogRule/etc/db_schema_whitelist.json index f5aaece43f179..b6c1391ce67fb 100644 --- a/app/code/Magento/CatalogRule/etc/db_schema_whitelist.json +++ b/app/code/Magento/CatalogRule/etc/db_schema_whitelist.json @@ -1,195 +1,195 @@ { - "catalogrule": { - "column": { - "rule_id": true, - "name": true, - "description": true, - "from_date": true, - "to_date": true, - "is_active": true, - "conditions_serialized": true, - "actions_serialized": true, - "stop_rules_processing": true, - "sort_order": true, - "simple_action": true, - "discount_amount": true, - "sub_is_enable": true, - "sub_simple_action": true, - "sub_discount_amount": true - }, - "index": { - "CATALOGRULE_IS_ACTIVE_SORT_ORDER_TO_DATE_FROM_DATE": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalogrule_product": { - "column": { - "rule_product_id": true, - "rule_id": true, - "from_time": true, - "to_time": true, - "customer_group_id": true, - "product_id": true, - "action_operator": true, - "action_amount": true, - "action_stop": true, - "sort_order": true, - "website_id": true, - "sub_simple_action": true, - "sub_discount_amount": true - }, - "index": { - "CATALOGRULE_PRODUCT_CUSTOMER_GROUP_ID": true, - "CATALOGRULE_PRODUCT_WEBSITE_ID": true, - "CATALOGRULE_PRODUCT_FROM_TIME": true, - "CATALOGRULE_PRODUCT_TO_TIME": true, - "CATALOGRULE_PRODUCT_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "IDX_EAA51B56FF092A0DCB795D1CEF812B7B": true, - "UNQ_EAA51B56FF092A0DCB795D1CEF812B7B": true - } - }, - "catalogrule_product_price": { - "column": { - "rule_product_price_id": true, - "rule_date": true, - "customer_group_id": true, - "product_id": true, - "rule_price": true, - "website_id": true, - "latest_start_date": true, - "earliest_end_date": true - }, - "index": { - "CATALOGRULE_PRODUCT_PRICE_CUSTOMER_GROUP_ID": true, - "CATALOGRULE_PRODUCT_PRICE_WEBSITE_ID": true, - "CATALOGRULE_PRODUCT_PRICE_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "CATRULE_PRD_PRICE_RULE_DATE_WS_ID_CSTR_GROUP_ID_PRD_ID": true - } - }, - "catalogrule_group_website": { - "column": { - "rule_id": true, - "customer_group_id": true, - "website_id": true - }, - "index": { - "CATALOGRULE_GROUP_WEBSITE_CUSTOMER_GROUP_ID": true, - "CATALOGRULE_GROUP_WEBSITE_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CATRULE_GROUP_WS_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true, - "CATALOGRULE_GROUP_WEBSITE_RULE_ID_CATALOGRULE_RULE_ID": true, - "CATALOGRULE_GROUP_WEBSITE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true - } - }, - "catalogrule_website": { - "column": { - "rule_id": true, - "website_id": true - }, - "index": { - "CATALOGRULE_WEBSITE_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CATALOGRULE_WEBSITE_RULE_ID_CATALOGRULE_RULE_ID": true, - "CATALOGRULE_WEBSITE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true - } - }, - "catalogrule_customer_group": { - "column": { - "rule_id": true, - "customer_group_id": true - }, - "index": { - "CATALOGRULE_CUSTOMER_GROUP_CUSTOMER_GROUP_ID": true - }, - "constraint": { - "PRIMARY": true, - "CATALOGRULE_CUSTOMER_GROUP_RULE_ID_CATALOGRULE_RULE_ID": true, - "CATRULE_CSTR_GROUP_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true - } - }, - "catalogrule_product_replica": { - "column": { - "rule_product_id": true, - "rule_id": true, - "from_time": true, - "to_time": true, - "customer_group_id": true, - "product_id": true, - "action_operator": true, - "action_amount": true, - "action_stop": true, - "sort_order": true, - "website_id": true - }, - "index": { - "CATALOGRULE_PRODUCT_REPLICA_CUSTOMER_GROUP_ID": true, - "CATALOGRULE_PRODUCT_REPLICA_WEBSITE_ID": true, - "CATALOGRULE_PRODUCT_REPLICA_FROM_TIME": true, - "CATALOGRULE_PRODUCT_REPLICA_TO_TIME": true, - "CATALOGRULE_PRODUCT_REPLICA_PRODUCT_ID": true, - "CATALOGRULE_PRODUCT_CUSTOMER_GROUP_ID": true, - "CATALOGRULE_PRODUCT_WEBSITE_ID": true, - "CATALOGRULE_PRODUCT_FROM_TIME": true, - "CATALOGRULE_PRODUCT_TO_TIME": true, - "CATALOGRULE_PRODUCT_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "IDX_EAA51B56FF092A0DCB795D1CEF812B7B": true, - "UNQ_EAA51B56FF092A0DCB795D1CEF812B7B": true - } - }, - "catalogrule_product_price_replica": { - "column": { - "rule_product_price_id": true, - "rule_date": true, - "customer_group_id": true, - "product_id": true, - "rule_price": true, - "website_id": true, - "latest_start_date": true, - "earliest_end_date": true - }, - "index": { - "CATALOGRULE_PRODUCT_PRICE_REPLICA_CUSTOMER_GROUP_ID": true, - "CATALOGRULE_PRODUCT_PRICE_REPLICA_WEBSITE_ID": true, - "CATALOGRULE_PRODUCT_PRICE_REPLICA_PRODUCT_ID": true, - "CATALOGRULE_PRODUCT_PRICE_CUSTOMER_GROUP_ID": true, - "CATALOGRULE_PRODUCT_PRICE_WEBSITE_ID": true, - "CATALOGRULE_PRODUCT_PRICE_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "CATRULE_PRD_PRICE_REPLICA_RULE_DATE_WS_ID_CSTR_GROUP_ID_PRD_ID": true, - "CATRULE_PRD_PRICE_RULE_DATE_WS_ID_CSTR_GROUP_ID_PRD_ID": true - } - }, - "catalogrule_group_website_replica": { - "column": { - "rule_id": true, - "customer_group_id": true, - "website_id": true - }, - "index": { - "CATALOGRULE_GROUP_WEBSITE_REPLICA_CUSTOMER_GROUP_ID": true, - "CATALOGRULE_GROUP_WEBSITE_REPLICA_WEBSITE_ID": true, - "CATALOGRULE_GROUP_WEBSITE_CUSTOMER_GROUP_ID": true, - "CATALOGRULE_GROUP_WEBSITE_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true + "catalogrule": { + "column": { + "rule_id": true, + "name": true, + "description": true, + "from_date": true, + "to_date": true, + "is_active": true, + "conditions_serialized": true, + "actions_serialized": true, + "stop_rules_processing": true, + "sort_order": true, + "simple_action": true, + "discount_amount": true, + "sub_is_enable": true, + "sub_simple_action": true, + "sub_discount_amount": true + }, + "index": { + "CATALOGRULE_IS_ACTIVE_SORT_ORDER_TO_DATE_FROM_DATE": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalogrule_product": { + "column": { + "rule_product_id": true, + "rule_id": true, + "from_time": true, + "to_time": true, + "customer_group_id": true, + "product_id": true, + "action_operator": true, + "action_amount": true, + "action_stop": true, + "sort_order": true, + "website_id": true, + "sub_simple_action": true, + "sub_discount_amount": true + }, + "index": { + "CATALOGRULE_PRODUCT_CUSTOMER_GROUP_ID": true, + "CATALOGRULE_PRODUCT_WEBSITE_ID": true, + "CATALOGRULE_PRODUCT_FROM_TIME": true, + "CATALOGRULE_PRODUCT_TO_TIME": true, + "CATALOGRULE_PRODUCT_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "IDX_EAA51B56FF092A0DCB795D1CEF812B7B": true, + "UNQ_EAA51B56FF092A0DCB795D1CEF812B7B": true + } + }, + "catalogrule_product_price": { + "column": { + "rule_product_price_id": true, + "rule_date": true, + "customer_group_id": true, + "product_id": true, + "rule_price": true, + "website_id": true, + "latest_start_date": true, + "earliest_end_date": true + }, + "index": { + "CATALOGRULE_PRODUCT_PRICE_CUSTOMER_GROUP_ID": true, + "CATALOGRULE_PRODUCT_PRICE_WEBSITE_ID": true, + "CATALOGRULE_PRODUCT_PRICE_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "CATRULE_PRD_PRICE_RULE_DATE_WS_ID_CSTR_GROUP_ID_PRD_ID": true + } + }, + "catalogrule_group_website": { + "column": { + "rule_id": true, + "customer_group_id": true, + "website_id": true + }, + "index": { + "CATALOGRULE_GROUP_WEBSITE_CUSTOMER_GROUP_ID": true, + "CATALOGRULE_GROUP_WEBSITE_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CATRULE_GROUP_WS_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true, + "CATALOGRULE_GROUP_WEBSITE_RULE_ID_CATALOGRULE_RULE_ID": true, + "CATALOGRULE_GROUP_WEBSITE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true + } + }, + "catalogrule_website": { + "column": { + "rule_id": true, + "website_id": true + }, + "index": { + "CATALOGRULE_WEBSITE_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CATALOGRULE_WEBSITE_RULE_ID_CATALOGRULE_RULE_ID": true, + "CATALOGRULE_WEBSITE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true + } + }, + "catalogrule_customer_group": { + "column": { + "rule_id": true, + "customer_group_id": true + }, + "index": { + "CATALOGRULE_CUSTOMER_GROUP_CUSTOMER_GROUP_ID": true + }, + "constraint": { + "PRIMARY": true, + "CATALOGRULE_CUSTOMER_GROUP_RULE_ID_CATALOGRULE_RULE_ID": true, + "CATRULE_CSTR_GROUP_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true + } + }, + "catalogrule_product_replica": { + "column": { + "rule_product_id": true, + "rule_id": true, + "from_time": true, + "to_time": true, + "customer_group_id": true, + "product_id": true, + "action_operator": true, + "action_amount": true, + "action_stop": true, + "sort_order": true, + "website_id": true + }, + "index": { + "CATALOGRULE_PRODUCT_REPLICA_CUSTOMER_GROUP_ID": true, + "CATALOGRULE_PRODUCT_REPLICA_WEBSITE_ID": true, + "CATALOGRULE_PRODUCT_REPLICA_FROM_TIME": true, + "CATALOGRULE_PRODUCT_REPLICA_TO_TIME": true, + "CATALOGRULE_PRODUCT_REPLICA_PRODUCT_ID": true, + "CATALOGRULE_PRODUCT_CUSTOMER_GROUP_ID": true, + "CATALOGRULE_PRODUCT_WEBSITE_ID": true, + "CATALOGRULE_PRODUCT_FROM_TIME": true, + "CATALOGRULE_PRODUCT_TO_TIME": true, + "CATALOGRULE_PRODUCT_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "IDX_EAA51B56FF092A0DCB795D1CEF812B7B": true, + "UNQ_EAA51B56FF092A0DCB795D1CEF812B7B": true + } + }, + "catalogrule_product_price_replica": { + "column": { + "rule_product_price_id": true, + "rule_date": true, + "customer_group_id": true, + "product_id": true, + "rule_price": true, + "website_id": true, + "latest_start_date": true, + "earliest_end_date": true + }, + "index": { + "CATALOGRULE_PRODUCT_PRICE_REPLICA_CUSTOMER_GROUP_ID": true, + "CATALOGRULE_PRODUCT_PRICE_REPLICA_WEBSITE_ID": true, + "CATALOGRULE_PRODUCT_PRICE_REPLICA_PRODUCT_ID": true, + "CATALOGRULE_PRODUCT_PRICE_CUSTOMER_GROUP_ID": true, + "CATALOGRULE_PRODUCT_PRICE_WEBSITE_ID": true, + "CATALOGRULE_PRODUCT_PRICE_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "CATRULE_PRD_PRICE_REPLICA_RULE_DATE_WS_ID_CSTR_GROUP_ID_PRD_ID": true, + "CATRULE_PRD_PRICE_RULE_DATE_WS_ID_CSTR_GROUP_ID_PRD_ID": true + } + }, + "catalogrule_group_website_replica": { + "column": { + "rule_id": true, + "customer_group_id": true, + "website_id": true + }, + "index": { + "CATALOGRULE_GROUP_WEBSITE_REPLICA_CUSTOMER_GROUP_ID": true, + "CATALOGRULE_GROUP_WEBSITE_REPLICA_WEBSITE_ID": true, + "CATALOGRULE_GROUP_WEBSITE_CUSTOMER_GROUP_ID": true, + "CATALOGRULE_GROUP_WEBSITE_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true + } } - } -} +} \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/etc/db_schema_whitelist.json b/app/code/Magento/CatalogSearch/etc/db_schema_whitelist.json index 453c0c9c90743..0d94c3a63a850 100644 --- a/app/code/Magento/CatalogSearch/etc/db_schema_whitelist.json +++ b/app/code/Magento/CatalogSearch/etc/db_schema_whitelist.json @@ -1,7 +1,7 @@ { - "catalog_eav_attribute": { - "column": { - "search_weight": true + "catalog_eav_attribute": { + "column": { + "search_weight": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/CatalogUrlRewrite/etc/db_schema_whitelist.json b/app/code/Magento/CatalogUrlRewrite/etc/db_schema_whitelist.json index f2e486e32eda1..5562ae5d48cc9 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/db_schema_whitelist.json +++ b/app/code/Magento/CatalogUrlRewrite/etc/db_schema_whitelist.json @@ -1,19 +1,19 @@ { - "catalog_url_rewrite_product_category": { - "column": { - "url_rewrite_id": true, - "category_id": true, - "product_id": true - }, - "index": { - "CATALOG_URL_REWRITE_PRODUCT_CATEGORY_CATEGORY_ID_PRODUCT_ID": true - }, - "constraint": { - "CAT_URL_REWRITE_PRD_CTGR_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "FK_BB79E64705D7F17FE181F23144528FC8": true, - "CAT_URL_REWRITE_PRD_CTGR_CTGR_ID_CAT_CTGR_ENTT_ENTT_ID": true, - "CAT_URL_REWRITE_PRD_CTGR_CTGR_ID_SEQUENCE_CAT_CTGR_SEQUENCE_VAL": true, - "CAT_URL_REWRITE_PRD_CTGR_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true + "catalog_url_rewrite_product_category": { + "column": { + "url_rewrite_id": true, + "category_id": true, + "product_id": true + }, + "index": { + "CATALOG_URL_REWRITE_PRODUCT_CATEGORY_CATEGORY_ID_PRODUCT_ID": true + }, + "constraint": { + "CAT_URL_REWRITE_PRD_CTGR_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "FK_BB79E64705D7F17FE181F23144528FC8": true, + "CAT_URL_REWRITE_PRD_CTGR_CTGR_ID_CAT_CTGR_ENTT_ENTT_ID": true, + "CAT_URL_REWRITE_PRD_CTGR_CTGR_ID_SEQUENCE_CAT_CTGR_SEQUENCE_VAL": true, + "CAT_URL_REWRITE_PRD_CTGR_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/CheckoutAgreements/etc/db_schema_whitelist.json b/app/code/Magento/CheckoutAgreements/etc/db_schema_whitelist.json index 039bcaade5205..f0be46a47e063 100644 --- a/app/code/Magento/CheckoutAgreements/etc/db_schema_whitelist.json +++ b/app/code/Magento/CheckoutAgreements/etc/db_schema_whitelist.json @@ -1,28 +1,28 @@ { - "checkout_agreement": { - "column": { - "agreement_id": true, - "name": true, - "content": true, - "content_height": true, - "checkbox_text": true, - "is_active": true, - "is_html": true, - "mode": true + "checkout_agreement": { + "column": { + "agreement_id": true, + "name": true, + "content": true, + "content_height": true, + "checkbox_text": true, + "is_active": true, + "is_html": true, + "mode": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true + "checkout_agreement_store": { + "column": { + "agreement_id": true, + "store_id": true + }, + "constraint": { + "PRIMARY": true, + "CHKT_AGRT_STORE_AGRT_ID_CHKT_AGRT_AGRT_ID": true, + "CHECKOUT_AGREEMENT_STORE_STORE_ID_STORE_STORE_ID": true + } } - }, - "checkout_agreement_store": { - "column": { - "agreement_id": true, - "store_id": true - }, - "constraint": { - "PRIMARY": true, - "CHKT_AGRT_STORE_AGRT_ID_CHKT_AGRT_AGRT_ID": true, - "CHECKOUT_AGREEMENT_STORE_STORE_ID_STORE_STORE_ID": true - } - } } \ No newline at end of file diff --git a/app/code/Magento/Cms/etc/db_schema_whitelist.json b/app/code/Magento/Cms/etc/db_schema_whitelist.json index 4ab3966798b2f..8da44baf71719 100644 --- a/app/code/Magento/Cms/etc/db_schema_whitelist.json +++ b/app/code/Magento/Cms/etc/db_schema_whitelist.json @@ -1,77 +1,77 @@ { - "cms_block": { - "column": { - "block_id": true, - "title": true, - "identifier": true, - "content": true, - "creation_time": true, - "update_time": true, - "is_active": true + "cms_block": { + "column": { + "block_id": true, + "title": true, + "identifier": true, + "content": true, + "creation_time": true, + "update_time": true, + "is_active": true + }, + "index": { + "CMS_BLOCK_TITLE_IDENTIFIER_CONTENT": true + }, + "constraint": { + "PRIMARY": true + } }, - "index": { - "CMS_BLOCK_TITLE_IDENTIFIER_CONTENT": true + "cms_block_store": { + "column": { + "block_id": true, + "store_id": true + }, + "index": { + "CMS_BLOCK_STORE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CMS_BLOCK_STORE_BLOCK_ID_CMS_BLOCK_BLOCK_ID": true, + "CMS_BLOCK_STORE_STORE_ID_STORE_STORE_ID": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "cms_block_store": { - "column": { - "block_id": true, - "store_id": true - }, - "index": { - "CMS_BLOCK_STORE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CMS_BLOCK_STORE_BLOCK_ID_CMS_BLOCK_BLOCK_ID": true, - "CMS_BLOCK_STORE_STORE_ID_STORE_STORE_ID": true - } - }, - "cms_page": { - "column": { - "page_id": true, - "title": true, - "page_layout": true, - "meta_keywords": true, - "meta_description": true, - "identifier": true, - "content_heading": true, - "content": true, - "creation_time": true, - "update_time": true, - "is_active": true, - "sort_order": true, - "layout_update_xml": true, - "custom_theme": true, - "custom_root_template": true, - "custom_layout_update_xml": true, - "custom_theme_from": true, - "custom_theme_to": true, - "meta_title": true - }, - "index": { - "CMS_PAGE_IDENTIFIER": true, - "CMS_PAGE_TITLE_META_KEYWORDS_META_DESCRIPTION_IDENTIFIER_CONTENT": true - }, - "constraint": { - "PRIMARY": true - } - }, - "cms_page_store": { - "column": { - "page_id": true, - "store_id": true - }, - "index": { - "CMS_PAGE_STORE_STORE_ID": true + "cms_page": { + "column": { + "page_id": true, + "title": true, + "page_layout": true, + "meta_keywords": true, + "meta_description": true, + "identifier": true, + "content_heading": true, + "content": true, + "creation_time": true, + "update_time": true, + "is_active": true, + "sort_order": true, + "layout_update_xml": true, + "custom_theme": true, + "custom_root_template": true, + "custom_layout_update_xml": true, + "custom_theme_from": true, + "custom_theme_to": true, + "meta_title": true + }, + "index": { + "CMS_PAGE_IDENTIFIER": true, + "CMS_PAGE_TITLE_META_KEYWORDS_META_DESCRIPTION_IDENTIFIER_CONTENT": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true, - "CMS_PAGE_STORE_PAGE_ID_CMS_PAGE_PAGE_ID": true, - "CMS_PAGE_STORE_STORE_ID_STORE_STORE_ID": true + "cms_page_store": { + "column": { + "page_id": true, + "store_id": true + }, + "index": { + "CMS_PAGE_STORE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CMS_PAGE_STORE_PAGE_ID_CMS_PAGE_PAGE_ID": true, + "CMS_PAGE_STORE_STORE_ID_STORE_STORE_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Config/etc/db_schema_whitelist.json b/app/code/Magento/Config/etc/db_schema_whitelist.json index 7383843c3bf5d..850e160bc732f 100644 --- a/app/code/Magento/Config/etc/db_schema_whitelist.json +++ b/app/code/Magento/Config/etc/db_schema_whitelist.json @@ -1,15 +1,15 @@ { - "core_config_data": { - "column": { - "config_id": true, - "scope": true, - "scope_id": true, - "path": true, - "value": true - }, - "constraint": { - "PRIMARY": true, - "CORE_CONFIG_DATA_SCOPE_SCOPE_ID_PATH": true + "core_config_data": { + "column": { + "config_id": true, + "scope": true, + "scope_id": true, + "path": true, + "value": true + }, + "constraint": { + "PRIMARY": true, + "CORE_CONFIG_DATA_SCOPE_SCOPE_ID_PATH": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/ConfigurableProduct/etc/db_schema_whitelist.json b/app/code/Magento/ConfigurableProduct/etc/db_schema_whitelist.json index c5c83dd31ca23..70ce362e02c71 100644 --- a/app/code/Magento/ConfigurableProduct/etc/db_schema_whitelist.json +++ b/app/code/Magento/ConfigurableProduct/etc/db_schema_whitelist.json @@ -1,50 +1,50 @@ { - "catalog_product_super_attribute": { - "column": { - "product_super_attribute_id": true, - "product_id": true, - "attribute_id": true, - "position": true + "catalog_product_super_attribute": { + "column": { + "product_super_attribute_id": true, + "product_id": true, + "attribute_id": true, + "position": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_SPR_ATTR_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CATALOG_PRODUCT_SUPER_ATTRIBUTE_PRODUCT_ID_ATTRIBUTE_ID": true + } }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_SPR_ATTR_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CATALOG_PRODUCT_SUPER_ATTRIBUTE_PRODUCT_ID_ATTRIBUTE_ID": true - } - }, - "catalog_product_super_attribute_label": { - "column": { - "value_id": true, - "product_super_attribute_id": true, - "store_id": true, - "use_default": true, - "value": true - }, - "index": { - "CATALOG_PRODUCT_SUPER_ATTRIBUTE_LABEL_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "FK_309442281DF7784210ED82B2CC51E5D5": true, - "CATALOG_PRODUCT_SUPER_ATTRIBUTE_LABEL_STORE_ID_STORE_STORE_ID": true, - "CAT_PRD_SPR_ATTR_LBL_PRD_SPR_ATTR_ID_STORE_ID": true - } - }, - "catalog_product_super_link": { - "column": { - "link_id": true, - "product_id": true, - "parent_id": true - }, - "index": { - "CATALOG_PRODUCT_SUPER_LINK_PARENT_ID": true + "catalog_product_super_attribute_label": { + "column": { + "value_id": true, + "product_super_attribute_id": true, + "store_id": true, + "use_default": true, + "value": true + }, + "index": { + "CATALOG_PRODUCT_SUPER_ATTRIBUTE_LABEL_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "FK_309442281DF7784210ED82B2CC51E5D5": true, + "CATALOG_PRODUCT_SUPER_ATTRIBUTE_LABEL_STORE_ID_STORE_STORE_ID": true, + "CAT_PRD_SPR_ATTR_LBL_PRD_SPR_ATTR_ID_STORE_ID": true + } }, - "constraint": { - "PRIMARY": true, - "CAT_PRD_SPR_LNK_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CAT_PRD_SPR_LNK_PARENT_ID_CAT_PRD_ENTT_ENTT_ID": true, - "CATALOG_PRODUCT_SUPER_LINK_PRODUCT_ID_PARENT_ID": true, - "CAT_PRD_SPR_LNK_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true + "catalog_product_super_link": { + "column": { + "link_id": true, + "product_id": true, + "parent_id": true + }, + "index": { + "CATALOG_PRODUCT_SUPER_LINK_PARENT_ID": true + }, + "constraint": { + "PRIMARY": true, + "CAT_PRD_SPR_LNK_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CAT_PRD_SPR_LNK_PARENT_ID_CAT_PRD_ENTT_ENTT_ID": true, + "CATALOG_PRODUCT_SUPER_LINK_PRODUCT_ID_PARENT_ID": true, + "CAT_PRD_SPR_LNK_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Cron/etc/db_schema_whitelist.json b/app/code/Magento/Cron/etc/db_schema_whitelist.json index 7c98f1d69ba59..c8666896627e2 100644 --- a/app/code/Magento/Cron/etc/db_schema_whitelist.json +++ b/app/code/Magento/Cron/etc/db_schema_whitelist.json @@ -1,21 +1,21 @@ { - "cron_schedule": { - "column": { - "schedule_id": true, - "job_code": true, - "status": true, - "messages": true, - "created_at": true, - "scheduled_at": true, - "executed_at": true, - "finished_at": true - }, - "index": { - "CRON_SCHEDULE_JOB_CODE": true, - "CRON_SCHEDULE_SCHEDULED_AT_STATUS": true - }, - "constraint": { - "PRIMARY": true + "cron_schedule": { + "column": { + "schedule_id": true, + "job_code": true, + "status": true, + "messages": true, + "created_at": true, + "scheduled_at": true, + "executed_at": true, + "finished_at": true + }, + "index": { + "CRON_SCHEDULE_JOB_CODE": true, + "CRON_SCHEDULE_SCHEDULED_AT_STATUS": true + }, + "constraint": { + "PRIMARY": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Customer/etc/db_schema_whitelist.json b/app/code/Magento/Customer/etc/db_schema_whitelist.json index f0150bccfe84e..4aada8f0d81fe 100644 --- a/app/code/Magento/Customer/etc/db_schema_whitelist.json +++ b/app/code/Magento/Customer/etc/db_schema_whitelist.json @@ -1,349 +1,349 @@ { - "customer_entity": { - "column": { - "entity_id": true, - "website_id": true, - "email": true, - "group_id": true, - "increment_id": true, - "store_id": true, - "created_at": true, - "updated_at": true, - "is_active": true, - "disable_auto_group_change": true, - "created_in": true, - "prefix": true, - "firstname": true, - "middlename": true, - "lastname": true, - "suffix": true, - "dob": true, - "password_hash": true, - "rp_token": true, - "rp_token_created_at": true, - "default_billing": true, - "default_shipping": true, - "taxvat": true, - "confirmation": true, - "gender": true, - "failures_num": true, - "first_failure": true, - "lock_expired": true, - "lock_expires": true - }, - "index": { - "CUSTOMER_ENTITY_STORE_ID": true, - "CUSTOMER_ENTITY_WEBSITE_ID": true, - "CUSTOMER_ENTITY_FIRSTNAME": true, - "CUSTOMER_ENTITY_LASTNAME": true - }, - "constraint": { - "PRIMARY": true, - "CUSTOMER_ENTITY_STORE_ID_STORE_STORE_ID": true, - "CUSTOMER_ENTITY_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, - "CUSTOMER_ENTITY_EMAIL_WEBSITE_ID": true - } - }, - "customer_address_entity": { - "column": { - "entity_id": true, - "increment_id": true, - "parent_id": true, - "created_at": true, - "updated_at": true, - "is_active": true, - "city": true, - "company": true, - "country_id": true, - "fax": true, - "firstname": true, - "lastname": true, - "middlename": true, - "postcode": true, - "prefix": true, - "region": true, - "region_id": true, - "street": true, - "suffix": true, - "telephone": true, - "vat_id": true, - "vat_is_valid": true, - "vat_request_date": true, - "vat_request_id": true, - "vat_request_success": true - }, - "index": { - "CUSTOMER_ADDRESS_ENTITY_PARENT_ID": true - }, - "constraint": { - "PRIMARY": true, - "CUSTOMER_ADDRESS_ENTITY_PARENT_ID_CUSTOMER_ENTITY_ENTITY_ID": true - } - }, - "customer_address_entity_datetime": { - "column": { - "value_id": true, - "attribute_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CUSTOMER_ADDRESS_ENTITY_DATETIME_ATTRIBUTE_ID": true, - "CUSTOMER_ADDRESS_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CSTR_ADDR_ENTT_DTIME_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CSTR_ADDR_ENTT_DTIME_ENTT_ID_CSTR_ADDR_ENTT_ENTT_ID": true, - "CUSTOMER_ADDRESS_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID": true - } - }, - "customer_address_entity_decimal": { - "column": { - "value_id": true, - "attribute_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CUSTOMER_ADDRESS_ENTITY_DECIMAL_ATTRIBUTE_ID": true, - "CUSTOMER_ADDRESS_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CSTR_ADDR_ENTT_DEC_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CSTR_ADDR_ENTT_DEC_ENTT_ID_CSTR_ADDR_ENTT_ENTT_ID": true, - "CUSTOMER_ADDRESS_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID": true - } - }, - "customer_address_entity_int": { - "column": { - "value_id": true, - "attribute_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CUSTOMER_ADDRESS_ENTITY_INT_ATTRIBUTE_ID": true, - "CUSTOMER_ADDRESS_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CSTR_ADDR_ENTT_INT_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CSTR_ADDR_ENTT_INT_ENTT_ID_CSTR_ADDR_ENTT_ENTT_ID": true, - "CUSTOMER_ADDRESS_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID": true - } - }, - "customer_address_entity_text": { - "column": { - "value_id": true, - "attribute_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CUSTOMER_ADDRESS_ENTITY_TEXT_ATTRIBUTE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CSTR_ADDR_ENTT_TEXT_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CSTR_ADDR_ENTT_TEXT_ENTT_ID_CSTR_ADDR_ENTT_ENTT_ID": true, - "CUSTOMER_ADDRESS_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID": true - } - }, - "customer_address_entity_varchar": { - "column": { - "value_id": true, - "attribute_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CUSTOMER_ADDRESS_ENTITY_VARCHAR_ATTRIBUTE_ID": true, - "CUSTOMER_ADDRESS_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CSTR_ADDR_ENTT_VCHR_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CSTR_ADDR_ENTT_VCHR_ENTT_ID_CSTR_ADDR_ENTT_ENTT_ID": true, - "CUSTOMER_ADDRESS_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID": true - } - }, - "customer_entity_datetime": { - "column": { - "value_id": true, - "attribute_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CUSTOMER_ENTITY_DATETIME_ATTRIBUTE_ID": true, - "CUSTOMER_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CUSTOMER_ENTITY_DATETIME_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, - "CUSTOMER_ENTITY_DATETIME_ENTITY_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "CUSTOMER_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID": true - } - }, - "customer_entity_decimal": { - "column": { - "value_id": true, - "attribute_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CUSTOMER_ENTITY_DECIMAL_ATTRIBUTE_ID": true, - "CUSTOMER_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CUSTOMER_ENTITY_DECIMAL_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, - "CUSTOMER_ENTITY_DECIMAL_ENTITY_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "CUSTOMER_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID": true - } - }, - "customer_entity_int": { - "column": { - "value_id": true, - "attribute_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CUSTOMER_ENTITY_INT_ATTRIBUTE_ID": true, - "CUSTOMER_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CUSTOMER_ENTITY_INT_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, - "CUSTOMER_ENTITY_INT_ENTITY_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "CUSTOMER_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID": true - } - }, - "customer_entity_text": { - "column": { - "value_id": true, - "attribute_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CUSTOMER_ENTITY_TEXT_ATTRIBUTE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CUSTOMER_ENTITY_TEXT_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, - "CUSTOMER_ENTITY_TEXT_ENTITY_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "CUSTOMER_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID": true - } - }, - "customer_entity_varchar": { - "column": { - "value_id": true, - "attribute_id": true, - "entity_id": true, - "value": true - }, - "index": { - "CUSTOMER_ENTITY_VARCHAR_ATTRIBUTE_ID": true, - "CUSTOMER_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "CUSTOMER_ENTITY_VARCHAR_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, - "CUSTOMER_ENTITY_VARCHAR_ENTITY_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "CUSTOMER_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID": true - } - }, - "customer_group": { - "column": { - "customer_group_id": true, - "customer_group_code": true, - "tax_class_id": true - }, - "constraint": { - "PRIMARY": true - } - }, - "customer_eav_attribute": { - "column": { - "attribute_id": true, - "is_visible": true, - "input_filter": true, - "multiline_count": true, - "validate_rules": true, - "is_system": true, - "sort_order": true, - "data_model": true, - "is_used_in_grid": true, - "is_visible_in_grid": true, - "is_filterable_in_grid": true, - "is_searchable_in_grid": true - }, - "constraint": { - "PRIMARY": true, - "CUSTOMER_EAV_ATTRIBUTE_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true - } - }, - "customer_form_attribute": { - "column": { - "form_code": true, - "attribute_id": true - }, - "index": { - "CUSTOMER_FORM_ATTRIBUTE_ATTRIBUTE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CUSTOMER_FORM_ATTRIBUTE_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true - } - }, - "customer_eav_attribute_website": { - "column": { - "attribute_id": true, - "website_id": true, - "is_visible": true, - "is_required": true, - "default_value": true, - "multiline_count": true - }, - "index": { - "CUSTOMER_EAV_ATTRIBUTE_WEBSITE_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true, - "CSTR_EAV_ATTR_WS_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "CSTR_EAV_ATTR_WS_WS_ID_STORE_WS_WS_ID": true - } - }, - "customer_visitor": { - "column": { - "visitor_id": true, - "customer_id": true, - "session_id": true, - "last_visit_at": true - }, - "index": { - "CUSTOMER_VISITOR_CUSTOMER_ID": true, - "CUSTOMER_VISITOR_LAST_VISIT_AT": true - }, - "constraint": { - "PRIMARY": true - } - }, - "customer_log": { - "column": { - "log_id": true, - "customer_id": true, - "last_login_at": true, - "last_logout_at": true - }, - "constraint": { - "PRIMARY": true, - "CUSTOMER_LOG_CUSTOMER_ID": true + "customer_entity": { + "column": { + "entity_id": true, + "website_id": true, + "email": true, + "group_id": true, + "increment_id": true, + "store_id": true, + "created_at": true, + "updated_at": true, + "is_active": true, + "disable_auto_group_change": true, + "created_in": true, + "prefix": true, + "firstname": true, + "middlename": true, + "lastname": true, + "suffix": true, + "dob": true, + "password_hash": true, + "rp_token": true, + "rp_token_created_at": true, + "default_billing": true, + "default_shipping": true, + "taxvat": true, + "confirmation": true, + "gender": true, + "failures_num": true, + "first_failure": true, + "lock_expired": true, + "lock_expires": true + }, + "index": { + "CUSTOMER_ENTITY_STORE_ID": true, + "CUSTOMER_ENTITY_WEBSITE_ID": true, + "CUSTOMER_ENTITY_FIRSTNAME": true, + "CUSTOMER_ENTITY_LASTNAME": true + }, + "constraint": { + "PRIMARY": true, + "CUSTOMER_ENTITY_STORE_ID_STORE_STORE_ID": true, + "CUSTOMER_ENTITY_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, + "CUSTOMER_ENTITY_EMAIL_WEBSITE_ID": true + } + }, + "customer_address_entity": { + "column": { + "entity_id": true, + "increment_id": true, + "parent_id": true, + "created_at": true, + "updated_at": true, + "is_active": true, + "city": true, + "company": true, + "country_id": true, + "fax": true, + "firstname": true, + "lastname": true, + "middlename": true, + "postcode": true, + "prefix": true, + "region": true, + "region_id": true, + "street": true, + "suffix": true, + "telephone": true, + "vat_id": true, + "vat_is_valid": true, + "vat_request_date": true, + "vat_request_id": true, + "vat_request_success": true + }, + "index": { + "CUSTOMER_ADDRESS_ENTITY_PARENT_ID": true + }, + "constraint": { + "PRIMARY": true, + "CUSTOMER_ADDRESS_ENTITY_PARENT_ID_CUSTOMER_ENTITY_ENTITY_ID": true + } + }, + "customer_address_entity_datetime": { + "column": { + "value_id": true, + "attribute_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CUSTOMER_ADDRESS_ENTITY_DATETIME_ATTRIBUTE_ID": true, + "CUSTOMER_ADDRESS_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CSTR_ADDR_ENTT_DTIME_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CSTR_ADDR_ENTT_DTIME_ENTT_ID_CSTR_ADDR_ENTT_ENTT_ID": true, + "CUSTOMER_ADDRESS_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID": true + } + }, + "customer_address_entity_decimal": { + "column": { + "value_id": true, + "attribute_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CUSTOMER_ADDRESS_ENTITY_DECIMAL_ATTRIBUTE_ID": true, + "CUSTOMER_ADDRESS_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CSTR_ADDR_ENTT_DEC_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CSTR_ADDR_ENTT_DEC_ENTT_ID_CSTR_ADDR_ENTT_ENTT_ID": true, + "CUSTOMER_ADDRESS_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID": true + } + }, + "customer_address_entity_int": { + "column": { + "value_id": true, + "attribute_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CUSTOMER_ADDRESS_ENTITY_INT_ATTRIBUTE_ID": true, + "CUSTOMER_ADDRESS_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CSTR_ADDR_ENTT_INT_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CSTR_ADDR_ENTT_INT_ENTT_ID_CSTR_ADDR_ENTT_ENTT_ID": true, + "CUSTOMER_ADDRESS_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID": true + } + }, + "customer_address_entity_text": { + "column": { + "value_id": true, + "attribute_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CUSTOMER_ADDRESS_ENTITY_TEXT_ATTRIBUTE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CSTR_ADDR_ENTT_TEXT_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CSTR_ADDR_ENTT_TEXT_ENTT_ID_CSTR_ADDR_ENTT_ENTT_ID": true, + "CUSTOMER_ADDRESS_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID": true + } + }, + "customer_address_entity_varchar": { + "column": { + "value_id": true, + "attribute_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CUSTOMER_ADDRESS_ENTITY_VARCHAR_ATTRIBUTE_ID": true, + "CUSTOMER_ADDRESS_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CSTR_ADDR_ENTT_VCHR_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CSTR_ADDR_ENTT_VCHR_ENTT_ID_CSTR_ADDR_ENTT_ENTT_ID": true, + "CUSTOMER_ADDRESS_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID": true + } + }, + "customer_entity_datetime": { + "column": { + "value_id": true, + "attribute_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CUSTOMER_ENTITY_DATETIME_ATTRIBUTE_ID": true, + "CUSTOMER_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CUSTOMER_ENTITY_DATETIME_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, + "CUSTOMER_ENTITY_DATETIME_ENTITY_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "CUSTOMER_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID": true + } + }, + "customer_entity_decimal": { + "column": { + "value_id": true, + "attribute_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CUSTOMER_ENTITY_DECIMAL_ATTRIBUTE_ID": true, + "CUSTOMER_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CUSTOMER_ENTITY_DECIMAL_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, + "CUSTOMER_ENTITY_DECIMAL_ENTITY_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "CUSTOMER_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID": true + } + }, + "customer_entity_int": { + "column": { + "value_id": true, + "attribute_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CUSTOMER_ENTITY_INT_ATTRIBUTE_ID": true, + "CUSTOMER_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CUSTOMER_ENTITY_INT_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, + "CUSTOMER_ENTITY_INT_ENTITY_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "CUSTOMER_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID": true + } + }, + "customer_entity_text": { + "column": { + "value_id": true, + "attribute_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CUSTOMER_ENTITY_TEXT_ATTRIBUTE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CUSTOMER_ENTITY_TEXT_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, + "CUSTOMER_ENTITY_TEXT_ENTITY_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "CUSTOMER_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID": true + } + }, + "customer_entity_varchar": { + "column": { + "value_id": true, + "attribute_id": true, + "entity_id": true, + "value": true + }, + "index": { + "CUSTOMER_ENTITY_VARCHAR_ATTRIBUTE_ID": true, + "CUSTOMER_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "CUSTOMER_ENTITY_VARCHAR_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, + "CUSTOMER_ENTITY_VARCHAR_ENTITY_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "CUSTOMER_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID": true + } + }, + "customer_group": { + "column": { + "customer_group_id": true, + "customer_group_code": true, + "tax_class_id": true + }, + "constraint": { + "PRIMARY": true + } + }, + "customer_eav_attribute": { + "column": { + "attribute_id": true, + "is_visible": true, + "input_filter": true, + "multiline_count": true, + "validate_rules": true, + "is_system": true, + "sort_order": true, + "data_model": true, + "is_used_in_grid": true, + "is_visible_in_grid": true, + "is_filterable_in_grid": true, + "is_searchable_in_grid": true + }, + "constraint": { + "PRIMARY": true, + "CUSTOMER_EAV_ATTRIBUTE_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true + } + }, + "customer_form_attribute": { + "column": { + "form_code": true, + "attribute_id": true + }, + "index": { + "CUSTOMER_FORM_ATTRIBUTE_ATTRIBUTE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CUSTOMER_FORM_ATTRIBUTE_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true + } + }, + "customer_eav_attribute_website": { + "column": { + "attribute_id": true, + "website_id": true, + "is_visible": true, + "is_required": true, + "default_value": true, + "multiline_count": true + }, + "index": { + "CUSTOMER_EAV_ATTRIBUTE_WEBSITE_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true, + "CSTR_EAV_ATTR_WS_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "CSTR_EAV_ATTR_WS_WS_ID_STORE_WS_WS_ID": true + } + }, + "customer_visitor": { + "column": { + "visitor_id": true, + "customer_id": true, + "session_id": true, + "last_visit_at": true + }, + "index": { + "CUSTOMER_VISITOR_CUSTOMER_ID": true, + "CUSTOMER_VISITOR_LAST_VISIT_AT": true + }, + "constraint": { + "PRIMARY": true + } + }, + "customer_log": { + "column": { + "log_id": true, + "customer_id": true, + "last_login_at": true, + "last_logout_at": true + }, + "constraint": { + "PRIMARY": true, + "CUSTOMER_LOG_CUSTOMER_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Directory/etc/db_schema_whitelist.json b/app/code/Magento/Directory/etc/db_schema_whitelist.json index a71261851e361..ab2facb17428a 100644 --- a/app/code/Magento/Directory/etc/db_schema_whitelist.json +++ b/app/code/Magento/Directory/etc/db_schema_whitelist.json @@ -1,65 +1,65 @@ { - "directory_country": { - "column": { - "country_id": true, - "iso2_code": true, - "iso3_code": true + "directory_country": { + "column": { + "country_id": true, + "iso2_code": true, + "iso3_code": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "directory_country_format": { - "column": { - "country_format_id": true, - "country_id": true, - "type": true, - "format": true - }, - "constraint": { - "PRIMARY": true, - "DIRECTORY_COUNTRY_FORMAT_COUNTRY_ID_TYPE": true - } - }, - "directory_country_region": { - "column": { - "region_id": true, - "country_id": true, - "code": true, - "default_name": true - }, - "index": { - "DIRECTORY_COUNTRY_REGION_COUNTRY_ID": true + "directory_country_format": { + "column": { + "country_format_id": true, + "country_id": true, + "type": true, + "format": true + }, + "constraint": { + "PRIMARY": true, + "DIRECTORY_COUNTRY_FORMAT_COUNTRY_ID_TYPE": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "directory_country_region_name": { - "column": { - "locale": true, - "region_id": true, - "name": true - }, - "index": { - "DIRECTORY_COUNTRY_REGION_NAME_REGION_ID": true - }, - "constraint": { - "PRIMARY": true, - "DIR_COUNTRY_REGION_NAME_REGION_ID_DIR_COUNTRY_REGION_REGION_ID": true - } - }, - "directory_currency_rate": { - "column": { - "currency_from": true, - "currency_to": true, - "rate": true + "directory_country_region": { + "column": { + "region_id": true, + "country_id": true, + "code": true, + "default_name": true + }, + "index": { + "DIRECTORY_COUNTRY_REGION_COUNTRY_ID": true + }, + "constraint": { + "PRIMARY": true + } }, - "index": { - "DIRECTORY_CURRENCY_RATE_CURRENCY_TO": true + "directory_country_region_name": { + "column": { + "locale": true, + "region_id": true, + "name": true + }, + "index": { + "DIRECTORY_COUNTRY_REGION_NAME_REGION_ID": true + }, + "constraint": { + "PRIMARY": true, + "DIR_COUNTRY_REGION_NAME_REGION_ID_DIR_COUNTRY_REGION_REGION_ID": true + } }, - "constraint": { - "PRIMARY": true + "directory_currency_rate": { + "column": { + "currency_from": true, + "currency_to": true, + "rate": true + }, + "index": { + "DIRECTORY_CURRENCY_RATE_CURRENCY_TO": true + }, + "constraint": { + "PRIMARY": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Downloadable/etc/db_schema_whitelist.json b/app/code/Magento/Downloadable/etc/db_schema_whitelist.json index 29cc8830746d7..61b7d439a4cd4 100644 --- a/app/code/Magento/Downloadable/etc/db_schema_whitelist.json +++ b/app/code/Magento/Downloadable/etc/db_schema_whitelist.json @@ -1,170 +1,170 @@ { - "downloadable_link": { - "column": { - "link_id": true, - "product_id": true, - "sort_order": true, - "number_of_downloads": true, - "is_shareable": true, - "link_url": true, - "link_file": true, - "link_type": true, - "sample_url": true, - "sample_file": true, - "sample_type": true - }, - "index": { - "DOWNLOADABLE_LINK_PRODUCT_ID_SORT_ORDER": true - }, - "constraint": { - "PRIMARY": true, - "DOWNLOADABLE_LINK_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true - } - }, - "downloadable_link_price": { - "column": { - "price_id": true, - "link_id": true, - "website_id": true, - "price": true - }, - "index": { - "DOWNLOADABLE_LINK_PRICE_LINK_ID": true, - "DOWNLOADABLE_LINK_PRICE_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true, - "DOWNLOADABLE_LINK_PRICE_LINK_ID_DOWNLOADABLE_LINK_LINK_ID": true, - "DOWNLOADABLE_LINK_PRICE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true - } - }, - "downloadable_link_purchased": { - "column": { - "purchased_id": true, - "order_id": true, - "order_increment_id": true, - "order_item_id": true, - "created_at": true, - "updated_at": true, - "customer_id": true, - "product_name": true, - "product_sku": true, - "link_section_title": true - }, - "index": { - "DOWNLOADABLE_LINK_PURCHASED_ORDER_ID": true, - "DOWNLOADABLE_LINK_PURCHASED_ORDER_ITEM_ID": true, - "DOWNLOADABLE_LINK_PURCHASED_CUSTOMER_ID": true - }, - "constraint": { - "PRIMARY": true, - "DL_LNK_PURCHASED_CSTR_ID_CSTR_ENTT_ENTT_ID": true, - "DOWNLOADABLE_LINK_PURCHASED_ORDER_ID_SALES_ORDER_ENTITY_ID": true - } - }, - "downloadable_link_purchased_item": { - "column": { - "item_id": true, - "purchased_id": true, - "order_item_id": true, - "product_id": true, - "link_hash": true, - "number_of_downloads_bought": true, - "number_of_downloads_used": true, - "link_id": true, - "link_title": true, - "is_shareable": true, - "link_url": true, - "link_file": true, - "link_type": true, - "status": true, - "created_at": true, - "updated_at": true - }, - "index": { - "DOWNLOADABLE_LINK_PURCHASED_ITEM_LINK_HASH": true, - "DOWNLOADABLE_LINK_PURCHASED_ITEM_ORDER_ITEM_ID": true, - "DOWNLOADABLE_LINK_PURCHASED_ITEM_PURCHASED_ID": true - }, - "constraint": { - "PRIMARY": true, - "DL_LNK_PURCHASED_ITEM_PURCHASED_ID_DL_LNK_PURCHASED_PURCHASED_ID": true, - "DL_LNK_PURCHASED_ITEM_ORDER_ITEM_ID_SALES_ORDER_ITEM_ITEM_ID": true - } - }, - "downloadable_link_title": { - "column": { - "title_id": true, - "link_id": true, - "store_id": true, - "title": true - }, - "index": { - "DOWNLOADABLE_LINK_TITLE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "DOWNLOADABLE_LINK_TITLE_LINK_ID_DOWNLOADABLE_LINK_LINK_ID": true, - "DOWNLOADABLE_LINK_TITLE_STORE_ID_STORE_STORE_ID": true, - "DOWNLOADABLE_LINK_TITLE_LINK_ID_STORE_ID": true - } - }, - "downloadable_sample": { - "column": { - "sample_id": true, - "product_id": true, - "sample_url": true, - "sample_file": true, - "sample_type": true, - "sort_order": true - }, - "index": { - "DOWNLOADABLE_SAMPLE_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "DOWNLOADABLE_SAMPLE_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true - } - }, - "downloadable_sample_title": { - "column": { - "title_id": true, - "sample_id": true, - "store_id": true, - "title": true - }, - "index": { - "DOWNLOADABLE_SAMPLE_TITLE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "DL_SAMPLE_TTL_SAMPLE_ID_DL_SAMPLE_SAMPLE_ID": true, - "DOWNLOADABLE_SAMPLE_TITLE_STORE_ID_STORE_STORE_ID": true, - "DOWNLOADABLE_SAMPLE_TITLE_SAMPLE_ID_STORE_ID": true - } - }, - "catalog_product_index_price_downlod_idx": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "min_price": true, - "max_price": true - }, - "constraint": { - "PRIMARY": true - } - }, - "catalog_product_index_price_downlod_tmp": { - "column": { - "entity_id": true, - "customer_group_id": true, - "website_id": true, - "min_price": true, - "max_price": true - }, - "constraint": { - "PRIMARY": true + "downloadable_link": { + "column": { + "link_id": true, + "product_id": true, + "sort_order": true, + "number_of_downloads": true, + "is_shareable": true, + "link_url": true, + "link_file": true, + "link_type": true, + "sample_url": true, + "sample_file": true, + "sample_type": true + }, + "index": { + "DOWNLOADABLE_LINK_PRODUCT_ID_SORT_ORDER": true + }, + "constraint": { + "PRIMARY": true, + "DOWNLOADABLE_LINK_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true + } + }, + "downloadable_link_price": { + "column": { + "price_id": true, + "link_id": true, + "website_id": true, + "price": true + }, + "index": { + "DOWNLOADABLE_LINK_PRICE_LINK_ID": true, + "DOWNLOADABLE_LINK_PRICE_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true, + "DOWNLOADABLE_LINK_PRICE_LINK_ID_DOWNLOADABLE_LINK_LINK_ID": true, + "DOWNLOADABLE_LINK_PRICE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true + } + }, + "downloadable_link_purchased": { + "column": { + "purchased_id": true, + "order_id": true, + "order_increment_id": true, + "order_item_id": true, + "created_at": true, + "updated_at": true, + "customer_id": true, + "product_name": true, + "product_sku": true, + "link_section_title": true + }, + "index": { + "DOWNLOADABLE_LINK_PURCHASED_ORDER_ID": true, + "DOWNLOADABLE_LINK_PURCHASED_ORDER_ITEM_ID": true, + "DOWNLOADABLE_LINK_PURCHASED_CUSTOMER_ID": true + }, + "constraint": { + "PRIMARY": true, + "DL_LNK_PURCHASED_CSTR_ID_CSTR_ENTT_ENTT_ID": true, + "DOWNLOADABLE_LINK_PURCHASED_ORDER_ID_SALES_ORDER_ENTITY_ID": true + } + }, + "downloadable_link_purchased_item": { + "column": { + "item_id": true, + "purchased_id": true, + "order_item_id": true, + "product_id": true, + "link_hash": true, + "number_of_downloads_bought": true, + "number_of_downloads_used": true, + "link_id": true, + "link_title": true, + "is_shareable": true, + "link_url": true, + "link_file": true, + "link_type": true, + "status": true, + "created_at": true, + "updated_at": true + }, + "index": { + "DOWNLOADABLE_LINK_PURCHASED_ITEM_LINK_HASH": true, + "DOWNLOADABLE_LINK_PURCHASED_ITEM_ORDER_ITEM_ID": true, + "DOWNLOADABLE_LINK_PURCHASED_ITEM_PURCHASED_ID": true + }, + "constraint": { + "PRIMARY": true, + "DL_LNK_PURCHASED_ITEM_PURCHASED_ID_DL_LNK_PURCHASED_PURCHASED_ID": true, + "DL_LNK_PURCHASED_ITEM_ORDER_ITEM_ID_SALES_ORDER_ITEM_ITEM_ID": true + } + }, + "downloadable_link_title": { + "column": { + "title_id": true, + "link_id": true, + "store_id": true, + "title": true + }, + "index": { + "DOWNLOADABLE_LINK_TITLE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "DOWNLOADABLE_LINK_TITLE_LINK_ID_DOWNLOADABLE_LINK_LINK_ID": true, + "DOWNLOADABLE_LINK_TITLE_STORE_ID_STORE_STORE_ID": true, + "DOWNLOADABLE_LINK_TITLE_LINK_ID_STORE_ID": true + } + }, + "downloadable_sample": { + "column": { + "sample_id": true, + "product_id": true, + "sample_url": true, + "sample_file": true, + "sample_type": true, + "sort_order": true + }, + "index": { + "DOWNLOADABLE_SAMPLE_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "DOWNLOADABLE_SAMPLE_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true + } + }, + "downloadable_sample_title": { + "column": { + "title_id": true, + "sample_id": true, + "store_id": true, + "title": true + }, + "index": { + "DOWNLOADABLE_SAMPLE_TITLE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "DL_SAMPLE_TTL_SAMPLE_ID_DL_SAMPLE_SAMPLE_ID": true, + "DOWNLOADABLE_SAMPLE_TITLE_STORE_ID_STORE_STORE_ID": true, + "DOWNLOADABLE_SAMPLE_TITLE_SAMPLE_ID_STORE_ID": true + } + }, + "catalog_product_index_price_downlod_idx": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "min_price": true, + "max_price": true + }, + "constraint": { + "PRIMARY": true + } + }, + "catalog_product_index_price_downlod_tmp": { + "column": { + "entity_id": true, + "customer_group_id": true, + "website_id": true, + "min_price": true, + "max_price": true + }, + "constraint": { + "PRIMARY": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Eav/etc/db_schema_whitelist.json b/app/code/Magento/Eav/etc/db_schema_whitelist.json index cf06b06d833d5..b3f1aca50df01 100644 --- a/app/code/Magento/Eav/etc/db_schema_whitelist.json +++ b/app/code/Magento/Eav/etc/db_schema_whitelist.json @@ -1,390 +1,390 @@ { - "eav_entity_type": { - "column": { - "entity_type_id": true, - "entity_type_code": true, - "entity_model": true, - "attribute_model": true, - "entity_table": true, - "value_table_prefix": true, - "entity_id_field": true, - "is_data_sharing": true, - "data_sharing_key": true, - "default_attribute_set_id": true, - "increment_model": true, - "increment_per_store": true, - "increment_pad_length": true, - "increment_pad_char": true, - "additional_attribute_table": true, - "entity_attribute_collection": true - }, - "index": { - "EAV_ENTITY_TYPE_ENTITY_TYPE_CODE": true - }, - "constraint": { - "PRIMARY": true - } - }, - "eav_entity": { - "column": { - "entity_id": true, - "entity_type_id": true, - "attribute_set_id": true, - "increment_id": true, - "parent_id": true, - "store_id": true, - "created_at": true, - "updated_at": true, - "is_active": true - }, - "index": { - "EAV_ENTITY_ENTITY_TYPE_ID": true, - "EAV_ENTITY_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ENTITY_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, - "EAV_ENTITY_STORE_ID_STORE_STORE_ID": true - } - }, - "eav_entity_datetime": { - "column": { - "value_id": true, - "entity_type_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "EAV_ENTITY_DATETIME_STORE_ID": true, - "EAV_ENTITY_DATETIME_ATTRIBUTE_ID_VALUE": true, - "EAV_ENTITY_DATETIME_ENTITY_TYPE_ID_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ENTITY_DATETIME_ENTITY_ID_EAV_ENTITY_ENTITY_ID": true, - "EAV_ENTT_DTIME_ENTT_TYPE_ID_EAV_ENTT_TYPE_ENTT_TYPE_ID": true, - "EAV_ENTITY_DATETIME_STORE_ID_STORE_STORE_ID": true, - "EAV_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "eav_entity_decimal": { - "column": { - "value_id": true, - "entity_type_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "EAV_ENTITY_DECIMAL_STORE_ID": true, - "EAV_ENTITY_DECIMAL_ATTRIBUTE_ID_VALUE": true, - "EAV_ENTITY_DECIMAL_ENTITY_TYPE_ID_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ENTITY_DECIMAL_ENTITY_ID_EAV_ENTITY_ENTITY_ID": true, - "EAV_ENTITY_DECIMAL_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, - "EAV_ENTITY_DECIMAL_STORE_ID_STORE_STORE_ID": true, - "EAV_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "eav_entity_int": { - "column": { - "value_id": true, - "entity_type_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "EAV_ENTITY_INT_STORE_ID": true, - "EAV_ENTITY_INT_ATTRIBUTE_ID_VALUE": true, - "EAV_ENTITY_INT_ENTITY_TYPE_ID_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ENTITY_INT_ENTITY_ID_EAV_ENTITY_ENTITY_ID": true, - "EAV_ENTITY_INT_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, - "EAV_ENTITY_INT_STORE_ID_STORE_STORE_ID": true, - "EAV_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "eav_entity_text": { - "column": { - "value_id": true, - "entity_type_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "EAV_ENTITY_TEXT_ENTITY_TYPE_ID": true, - "EAV_ENTITY_TEXT_ATTRIBUTE_ID": true, - "EAV_ENTITY_TEXT_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ENTITY_TEXT_ENTITY_ID_EAV_ENTITY_ENTITY_ID": true, - "EAV_ENTITY_TEXT_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, - "EAV_ENTITY_TEXT_STORE_ID_STORE_STORE_ID": true, - "EAV_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "eav_entity_varchar": { - "column": { - "value_id": true, - "entity_type_id": true, - "attribute_id": true, - "store_id": true, - "entity_id": true, - "value": true - }, - "index": { - "EAV_ENTITY_VARCHAR_STORE_ID": true, - "EAV_ENTITY_VARCHAR_ATTRIBUTE_ID_VALUE": true, - "EAV_ENTITY_VARCHAR_ENTITY_TYPE_ID_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ENTITY_VARCHAR_ENTITY_ID_EAV_ENTITY_ENTITY_ID": true, - "EAV_ENTITY_VARCHAR_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, - "EAV_ENTITY_VARCHAR_STORE_ID_STORE_STORE_ID": true, - "EAV_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true - } - }, - "eav_attribute": { - "column": { - "attribute_id": true, - "entity_type_id": true, - "attribute_code": true, - "attribute_model": true, - "backend_model": true, - "backend_type": true, - "backend_table": true, - "frontend_model": true, - "frontend_input": true, - "frontend_label": true, - "frontend_class": true, - "source_model": true, - "is_required": true, - "is_user_defined": true, - "default_value": true, - "is_unique": true, - "note": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ATTRIBUTE_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, - "EAV_ATTRIBUTE_ENTITY_TYPE_ID_ATTRIBUTE_CODE": true - } - }, - "eav_entity_store": { - "column": { - "entity_store_id": true, - "entity_type_id": true, - "store_id": true, - "increment_prefix": true, - "increment_last_id": true - }, - "index": { - "EAV_ENTITY_STORE_ENTITY_TYPE_ID": true, - "EAV_ENTITY_STORE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ENTITY_STORE_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, - "EAV_ENTITY_STORE_STORE_ID_STORE_STORE_ID": true - } - }, - "eav_attribute_set": { - "column": { - "attribute_set_id": true, - "entity_type_id": true, - "attribute_set_name": true, - "sort_order": true - }, - "index": { - "EAV_ATTRIBUTE_SET_ENTITY_TYPE_ID_SORT_ORDER": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ATTRIBUTE_SET_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, - "EAV_ATTRIBUTE_SET_ENTITY_TYPE_ID_ATTRIBUTE_SET_NAME": true - } - }, - "eav_attribute_group": { - "column": { - "attribute_group_id": true, - "attribute_set_id": true, - "attribute_group_name": true, - "sort_order": true, - "default_id": true, - "attribute_group_code": true, - "tab_group_code": true - }, - "index": { - "EAV_ATTRIBUTE_GROUP_ATTRIBUTE_SET_ID_SORT_ORDER": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ATTR_GROUP_ATTR_SET_ID_EAV_ATTR_SET_ATTR_SET_ID": true, - "EAV_ATTRIBUTE_GROUP_ATTRIBUTE_SET_ID_ATTRIBUTE_GROUP_NAME": true, - "EAV_ATTRIBUTE_GROUP_ATTRIBUTE_SET_ID_ATTRIBUTE_GROUP_CODE": true, - "CATALOG_CATEGORY_PRODUCT_ATTRIBUTE_SET_ID_ATTRIBUTE_GROUP_CODE": true - } - }, - "eav_entity_attribute": { - "column": { - "entity_attribute_id": true, - "entity_type_id": true, - "attribute_set_id": true, - "attribute_group_id": true, - "attribute_id": true, - "sort_order": true - }, - "index": { - "EAV_ENTITY_ATTRIBUTE_ATTRIBUTE_SET_ID_SORT_ORDER": true, - "EAV_ENTITY_ATTRIBUTE_ATTRIBUTE_ID": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ENTITY_ATTRIBUTE_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, - "EAV_ENTT_ATTR_ATTR_GROUP_ID_EAV_ATTR_GROUP_ATTR_GROUP_ID": true, - "EAV_ENTITY_ATTRIBUTE_ATTRIBUTE_SET_ID_ATTRIBUTE_ID": true, - "EAV_ENTITY_ATTRIBUTE_ATTRIBUTE_GROUP_ID_ATTRIBUTE_ID": true - } - }, - "eav_attribute_option": { - "column": { - "option_id": true, - "attribute_id": true, - "sort_order": true - }, - "index": { - "EAV_ATTRIBUTE_OPTION_ATTRIBUTE_ID": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ATTRIBUTE_OPTION_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true - } - }, - "eav_attribute_option_value": { - "column": { - "value_id": true, - "option_id": true, - "store_id": true, - "value": true - }, - "index": { - "EAV_ATTRIBUTE_OPTION_VALUE_OPTION_ID": true, - "EAV_ATTRIBUTE_OPTION_VALUE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ATTR_OPT_VAL_OPT_ID_EAV_ATTR_OPT_OPT_ID": true, - "EAV_ATTRIBUTE_OPTION_VALUE_STORE_ID_STORE_STORE_ID": true - } - }, - "eav_attribute_label": { - "column": { - "attribute_label_id": true, - "attribute_id": true, - "store_id": true, - "value": true - }, - "index": { - "EAV_ATTRIBUTE_LABEL_STORE_ID": true, - "EAV_ATTRIBUTE_LABEL_ATTRIBUTE_ID_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "EAV_ATTRIBUTE_LABEL_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, - "EAV_ATTRIBUTE_LABEL_STORE_ID_STORE_STORE_ID": true - } - }, - "eav_form_type": { - "column": { - "type_id": true, - "code": true, - "label": true, - "is_system": true, - "theme": true, - "store_id": true - }, - "index": { - "EAV_FORM_TYPE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "EAV_FORM_TYPE_STORE_ID_STORE_STORE_ID": true, - "EAV_FORM_TYPE_CODE_THEME_STORE_ID": true - } - }, - "eav_form_type_entity": { - "column": { - "type_id": true, - "entity_type_id": true - }, - "index": { - "EAV_FORM_TYPE_ENTITY_ENTITY_TYPE_ID": true - }, - "constraint": { - "PRIMARY": true, - "EAV_FORM_TYPE_ENTT_ENTT_TYPE_ID_EAV_ENTT_TYPE_ENTT_TYPE_ID": true, - "EAV_FORM_TYPE_ENTITY_TYPE_ID_EAV_FORM_TYPE_TYPE_ID": true - } - }, - "eav_form_fieldset": { - "column": { - "fieldset_id": true, - "type_id": true, - "code": true, - "sort_order": true - }, - "constraint": { - "PRIMARY": true, - "EAV_FORM_FIELDSET_TYPE_ID_EAV_FORM_TYPE_TYPE_ID": true, - "EAV_FORM_FIELDSET_TYPE_ID_CODE": true - } - }, - "eav_form_fieldset_label": { - "column": { - "fieldset_id": true, - "store_id": true, - "label": true - }, - "index": { - "EAV_FORM_FIELDSET_LABEL_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "EAV_FORM_FSET_LBL_FSET_ID_EAV_FORM_FSET_FSET_ID": true, - "EAV_FORM_FIELDSET_LABEL_STORE_ID_STORE_STORE_ID": true - } - }, - "eav_form_element": { - "column": { - "element_id": true, - "type_id": true, - "fieldset_id": true, - "attribute_id": true, - "sort_order": true - }, - "index": { - "EAV_FORM_ELEMENT_FIELDSET_ID": true, - "EAV_FORM_ELEMENT_ATTRIBUTE_ID": true - }, - "constraint": { - "PRIMARY": true, - "EAV_FORM_ELEMENT_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, - "EAV_FORM_ELEMENT_FIELDSET_ID_EAV_FORM_FIELDSET_FIELDSET_ID": true, - "EAV_FORM_ELEMENT_TYPE_ID_EAV_FORM_TYPE_TYPE_ID": true, - "EAV_FORM_ELEMENT_TYPE_ID_ATTRIBUTE_ID": true + "eav_entity_type": { + "column": { + "entity_type_id": true, + "entity_type_code": true, + "entity_model": true, + "attribute_model": true, + "entity_table": true, + "value_table_prefix": true, + "entity_id_field": true, + "is_data_sharing": true, + "data_sharing_key": true, + "default_attribute_set_id": true, + "increment_model": true, + "increment_per_store": true, + "increment_pad_length": true, + "increment_pad_char": true, + "additional_attribute_table": true, + "entity_attribute_collection": true + }, + "index": { + "EAV_ENTITY_TYPE_ENTITY_TYPE_CODE": true + }, + "constraint": { + "PRIMARY": true + } + }, + "eav_entity": { + "column": { + "entity_id": true, + "entity_type_id": true, + "attribute_set_id": true, + "increment_id": true, + "parent_id": true, + "store_id": true, + "created_at": true, + "updated_at": true, + "is_active": true + }, + "index": { + "EAV_ENTITY_ENTITY_TYPE_ID": true, + "EAV_ENTITY_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ENTITY_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, + "EAV_ENTITY_STORE_ID_STORE_STORE_ID": true + } + }, + "eav_entity_datetime": { + "column": { + "value_id": true, + "entity_type_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "EAV_ENTITY_DATETIME_STORE_ID": true, + "EAV_ENTITY_DATETIME_ATTRIBUTE_ID_VALUE": true, + "EAV_ENTITY_DATETIME_ENTITY_TYPE_ID_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ENTITY_DATETIME_ENTITY_ID_EAV_ENTITY_ENTITY_ID": true, + "EAV_ENTT_DTIME_ENTT_TYPE_ID_EAV_ENTT_TYPE_ENTT_TYPE_ID": true, + "EAV_ENTITY_DATETIME_STORE_ID_STORE_STORE_ID": true, + "EAV_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "eav_entity_decimal": { + "column": { + "value_id": true, + "entity_type_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "EAV_ENTITY_DECIMAL_STORE_ID": true, + "EAV_ENTITY_DECIMAL_ATTRIBUTE_ID_VALUE": true, + "EAV_ENTITY_DECIMAL_ENTITY_TYPE_ID_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ENTITY_DECIMAL_ENTITY_ID_EAV_ENTITY_ENTITY_ID": true, + "EAV_ENTITY_DECIMAL_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, + "EAV_ENTITY_DECIMAL_STORE_ID_STORE_STORE_ID": true, + "EAV_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "eav_entity_int": { + "column": { + "value_id": true, + "entity_type_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "EAV_ENTITY_INT_STORE_ID": true, + "EAV_ENTITY_INT_ATTRIBUTE_ID_VALUE": true, + "EAV_ENTITY_INT_ENTITY_TYPE_ID_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ENTITY_INT_ENTITY_ID_EAV_ENTITY_ENTITY_ID": true, + "EAV_ENTITY_INT_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, + "EAV_ENTITY_INT_STORE_ID_STORE_STORE_ID": true, + "EAV_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "eav_entity_text": { + "column": { + "value_id": true, + "entity_type_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "EAV_ENTITY_TEXT_ENTITY_TYPE_ID": true, + "EAV_ENTITY_TEXT_ATTRIBUTE_ID": true, + "EAV_ENTITY_TEXT_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ENTITY_TEXT_ENTITY_ID_EAV_ENTITY_ENTITY_ID": true, + "EAV_ENTITY_TEXT_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, + "EAV_ENTITY_TEXT_STORE_ID_STORE_STORE_ID": true, + "EAV_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "eav_entity_varchar": { + "column": { + "value_id": true, + "entity_type_id": true, + "attribute_id": true, + "store_id": true, + "entity_id": true, + "value": true + }, + "index": { + "EAV_ENTITY_VARCHAR_STORE_ID": true, + "EAV_ENTITY_VARCHAR_ATTRIBUTE_ID_VALUE": true, + "EAV_ENTITY_VARCHAR_ENTITY_TYPE_ID_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ENTITY_VARCHAR_ENTITY_ID_EAV_ENTITY_ENTITY_ID": true, + "EAV_ENTITY_VARCHAR_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, + "EAV_ENTITY_VARCHAR_STORE_ID_STORE_STORE_ID": true, + "EAV_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true + } + }, + "eav_attribute": { + "column": { + "attribute_id": true, + "entity_type_id": true, + "attribute_code": true, + "attribute_model": true, + "backend_model": true, + "backend_type": true, + "backend_table": true, + "frontend_model": true, + "frontend_input": true, + "frontend_label": true, + "frontend_class": true, + "source_model": true, + "is_required": true, + "is_user_defined": true, + "default_value": true, + "is_unique": true, + "note": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ATTRIBUTE_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, + "EAV_ATTRIBUTE_ENTITY_TYPE_ID_ATTRIBUTE_CODE": true + } + }, + "eav_entity_store": { + "column": { + "entity_store_id": true, + "entity_type_id": true, + "store_id": true, + "increment_prefix": true, + "increment_last_id": true + }, + "index": { + "EAV_ENTITY_STORE_ENTITY_TYPE_ID": true, + "EAV_ENTITY_STORE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ENTITY_STORE_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, + "EAV_ENTITY_STORE_STORE_ID_STORE_STORE_ID": true + } + }, + "eav_attribute_set": { + "column": { + "attribute_set_id": true, + "entity_type_id": true, + "attribute_set_name": true, + "sort_order": true + }, + "index": { + "EAV_ATTRIBUTE_SET_ENTITY_TYPE_ID_SORT_ORDER": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ATTRIBUTE_SET_ENTITY_TYPE_ID_EAV_ENTITY_TYPE_ENTITY_TYPE_ID": true, + "EAV_ATTRIBUTE_SET_ENTITY_TYPE_ID_ATTRIBUTE_SET_NAME": true + } + }, + "eav_attribute_group": { + "column": { + "attribute_group_id": true, + "attribute_set_id": true, + "attribute_group_name": true, + "sort_order": true, + "default_id": true, + "attribute_group_code": true, + "tab_group_code": true + }, + "index": { + "EAV_ATTRIBUTE_GROUP_ATTRIBUTE_SET_ID_SORT_ORDER": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ATTR_GROUP_ATTR_SET_ID_EAV_ATTR_SET_ATTR_SET_ID": true, + "EAV_ATTRIBUTE_GROUP_ATTRIBUTE_SET_ID_ATTRIBUTE_GROUP_NAME": true, + "EAV_ATTRIBUTE_GROUP_ATTRIBUTE_SET_ID_ATTRIBUTE_GROUP_CODE": true, + "CATALOG_CATEGORY_PRODUCT_ATTRIBUTE_SET_ID_ATTRIBUTE_GROUP_CODE": true + } + }, + "eav_entity_attribute": { + "column": { + "entity_attribute_id": true, + "entity_type_id": true, + "attribute_set_id": true, + "attribute_group_id": true, + "attribute_id": true, + "sort_order": true + }, + "index": { + "EAV_ENTITY_ATTRIBUTE_ATTRIBUTE_SET_ID_SORT_ORDER": true, + "EAV_ENTITY_ATTRIBUTE_ATTRIBUTE_ID": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ENTITY_ATTRIBUTE_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, + "EAV_ENTT_ATTR_ATTR_GROUP_ID_EAV_ATTR_GROUP_ATTR_GROUP_ID": true, + "EAV_ENTITY_ATTRIBUTE_ATTRIBUTE_SET_ID_ATTRIBUTE_ID": true, + "EAV_ENTITY_ATTRIBUTE_ATTRIBUTE_GROUP_ID_ATTRIBUTE_ID": true + } + }, + "eav_attribute_option": { + "column": { + "option_id": true, + "attribute_id": true, + "sort_order": true + }, + "index": { + "EAV_ATTRIBUTE_OPTION_ATTRIBUTE_ID": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ATTRIBUTE_OPTION_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true + } + }, + "eav_attribute_option_value": { + "column": { + "value_id": true, + "option_id": true, + "store_id": true, + "value": true + }, + "index": { + "EAV_ATTRIBUTE_OPTION_VALUE_OPTION_ID": true, + "EAV_ATTRIBUTE_OPTION_VALUE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ATTR_OPT_VAL_OPT_ID_EAV_ATTR_OPT_OPT_ID": true, + "EAV_ATTRIBUTE_OPTION_VALUE_STORE_ID_STORE_STORE_ID": true + } + }, + "eav_attribute_label": { + "column": { + "attribute_label_id": true, + "attribute_id": true, + "store_id": true, + "value": true + }, + "index": { + "EAV_ATTRIBUTE_LABEL_STORE_ID": true, + "EAV_ATTRIBUTE_LABEL_ATTRIBUTE_ID_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ATTRIBUTE_LABEL_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, + "EAV_ATTRIBUTE_LABEL_STORE_ID_STORE_STORE_ID": true + } + }, + "eav_form_type": { + "column": { + "type_id": true, + "code": true, + "label": true, + "is_system": true, + "theme": true, + "store_id": true + }, + "index": { + "EAV_FORM_TYPE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "EAV_FORM_TYPE_STORE_ID_STORE_STORE_ID": true, + "EAV_FORM_TYPE_CODE_THEME_STORE_ID": true + } + }, + "eav_form_type_entity": { + "column": { + "type_id": true, + "entity_type_id": true + }, + "index": { + "EAV_FORM_TYPE_ENTITY_ENTITY_TYPE_ID": true + }, + "constraint": { + "PRIMARY": true, + "EAV_FORM_TYPE_ENTT_ENTT_TYPE_ID_EAV_ENTT_TYPE_ENTT_TYPE_ID": true, + "EAV_FORM_TYPE_ENTITY_TYPE_ID_EAV_FORM_TYPE_TYPE_ID": true + } + }, + "eav_form_fieldset": { + "column": { + "fieldset_id": true, + "type_id": true, + "code": true, + "sort_order": true + }, + "constraint": { + "PRIMARY": true, + "EAV_FORM_FIELDSET_TYPE_ID_EAV_FORM_TYPE_TYPE_ID": true, + "EAV_FORM_FIELDSET_TYPE_ID_CODE": true + } + }, + "eav_form_fieldset_label": { + "column": { + "fieldset_id": true, + "store_id": true, + "label": true + }, + "index": { + "EAV_FORM_FIELDSET_LABEL_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "EAV_FORM_FSET_LBL_FSET_ID_EAV_FORM_FSET_FSET_ID": true, + "EAV_FORM_FIELDSET_LABEL_STORE_ID_STORE_STORE_ID": true + } + }, + "eav_form_element": { + "column": { + "element_id": true, + "type_id": true, + "fieldset_id": true, + "attribute_id": true, + "sort_order": true + }, + "index": { + "EAV_FORM_ELEMENT_FIELDSET_ID": true, + "EAV_FORM_ELEMENT_ATTRIBUTE_ID": true + }, + "constraint": { + "PRIMARY": true, + "EAV_FORM_ELEMENT_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, + "EAV_FORM_ELEMENT_FIELDSET_ID_EAV_FORM_FIELDSET_FIELDSET_ID": true, + "EAV_FORM_ELEMENT_TYPE_ID_EAV_FORM_TYPE_TYPE_ID": true, + "EAV_FORM_ELEMENT_TYPE_ID_ATTRIBUTE_ID": true + } } - } -} +} \ No newline at end of file diff --git a/app/code/Magento/Email/etc/db_schema_whitelist.json b/app/code/Magento/Email/etc/db_schema_whitelist.json index 2ad9cddc70f96..471a51771b854 100644 --- a/app/code/Magento/Email/etc/db_schema_whitelist.json +++ b/app/code/Magento/Email/etc/db_schema_whitelist.json @@ -1,26 +1,26 @@ { - "email_template": { - "column": { - "template_id": true, - "template_code": true, - "template_text": true, - "template_styles": true, - "template_type": true, - "template_subject": true, - "template_sender_name": true, - "template_sender_email": true, - "added_at": true, - "modified_at": true, - "orig_template_code": true, - "orig_template_variables": true - }, - "index": { - "EMAIL_TEMPLATE_ADDED_AT": true, - "EMAIL_TEMPLATE_MODIFIED_AT": true - }, - "constraint": { - "PRIMARY": true, - "EMAIL_TEMPLATE_TEMPLATE_CODE": true + "email_template": { + "column": { + "template_id": true, + "template_code": true, + "template_text": true, + "template_styles": true, + "template_type": true, + "template_subject": true, + "template_sender_name": true, + "template_sender_email": true, + "added_at": true, + "modified_at": true, + "orig_template_code": true, + "orig_template_variables": true + }, + "index": { + "EMAIL_TEMPLATE_ADDED_AT": true, + "EMAIL_TEMPLATE_MODIFIED_AT": true + }, + "constraint": { + "PRIMARY": true, + "EMAIL_TEMPLATE_TEMPLATE_CODE": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/GiftMessage/etc/db_schema_whitelist.json b/app/code/Magento/GiftMessage/etc/db_schema_whitelist.json index 959049be1930d..e3fed1cadd1da 100644 --- a/app/code/Magento/GiftMessage/etc/db_schema_whitelist.json +++ b/app/code/Magento/GiftMessage/etc/db_schema_whitelist.json @@ -1,45 +1,45 @@ { - "gift_message": { - "column": { - "gift_message_id": true, - "customer_id": true, - "sender": true, - "recipient": true, - "message": true + "gift_message": { + "column": { + "gift_message_id": true, + "customer_id": true, + "sender": true, + "recipient": true, + "message": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "quote": { - "column": { - "gift_message_id": true - } - }, - "quote_address": { - "column": { - "gift_message_id": true - } - }, - "quote_item": { - "column": { - "gift_message_id": true - } - }, - "quote_address_item": { - "column": { - "gift_message_id": true - } - }, - "sales_order": { - "column": { - "gift_message_id": true - } - }, - "sales_order_item": { - "column": { - "gift_message_id": true, - "gift_message_available": true + "quote": { + "column": { + "gift_message_id": true + } + }, + "quote_address": { + "column": { + "gift_message_id": true + } + }, + "quote_item": { + "column": { + "gift_message_id": true + } + }, + "quote_address_item": { + "column": { + "gift_message_id": true + } + }, + "sales_order": { + "column": { + "gift_message_id": true + } + }, + "sales_order_item": { + "column": { + "gift_message_id": true, + "gift_message_available": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/GoogleOptimizer/etc/db_schema_whitelist.json b/app/code/Magento/GoogleOptimizer/etc/db_schema_whitelist.json index e112a591cd1c8..20d2d1404a047 100644 --- a/app/code/Magento/GoogleOptimizer/etc/db_schema_whitelist.json +++ b/app/code/Magento/GoogleOptimizer/etc/db_schema_whitelist.json @@ -1,16 +1,16 @@ { - "googleoptimizer_code": { - "column": { - "code_id": true, - "entity_id": true, - "entity_type": true, - "store_id": true, - "experiment_script": true - }, - "constraint": { - "PRIMARY": true, - "GOOGLEOPTIMIZER_CODE_STORE_ID_STORE_STORE_ID": true, - "GOOGLEOPTIMIZER_CODE_STORE_ID_ENTITY_ID_ENTITY_TYPE": true + "googleoptimizer_code": { + "column": { + "code_id": true, + "entity_id": true, + "entity_type": true, + "store_id": true, + "experiment_script": true + }, + "constraint": { + "PRIMARY": true, + "GOOGLEOPTIMIZER_CODE_STORE_ID_STORE_STORE_ID": true, + "GOOGLEOPTIMIZER_CODE_STORE_ID_ENTITY_ID_ENTITY_TYPE": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/ImportExport/etc/db_schema_whitelist.json b/app/code/Magento/ImportExport/etc/db_schema_whitelist.json index d8f51dd4ef387..e78535d2c7585 100644 --- a/app/code/Magento/ImportExport/etc/db_schema_whitelist.json +++ b/app/code/Magento/ImportExport/etc/db_schema_whitelist.json @@ -1,27 +1,27 @@ { - "importexport_importdata": { - "column": { - "id": true, - "entity": true, - "behavior": true, - "data": true + "importexport_importdata": { + "column": { + "id": true, + "entity": true, + "behavior": true, + "data": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true + "import_history": { + "column": { + "history_id": true, + "started_at": true, + "user_id": true, + "imported_file": true, + "execution_time": true, + "summary": true, + "error_file": true + }, + "constraint": { + "PRIMARY": true + } } - }, - "import_history": { - "column": { - "history_id": true, - "started_at": true, - "user_id": true, - "imported_file": true, - "execution_time": true, - "summary": true, - "error_file": true - }, - "constraint": { - "PRIMARY": true - } - } } \ No newline at end of file diff --git a/app/code/Magento/Indexer/etc/db_schema_whitelist.json b/app/code/Magento/Indexer/etc/db_schema_whitelist.json index e1b78c94b230a..8c9b55729a737 100644 --- a/app/code/Magento/Indexer/etc/db_schema_whitelist.json +++ b/app/code/Magento/Indexer/etc/db_schema_whitelist.json @@ -1,34 +1,34 @@ { - "indexer_state": { - "column": { - "state_id": true, - "indexer_id": true, - "status": true, - "updated": true, - "hash_config": true + "indexer_state": { + "column": { + "state_id": true, + "indexer_id": true, + "status": true, + "updated": true, + "hash_config": true + }, + "index": { + "INDEXER_STATE_INDEXER_ID": true + }, + "constraint": { + "PRIMARY": true + } }, - "index": { - "INDEXER_STATE_INDEXER_ID": true - }, - "constraint": { - "PRIMARY": true - } - }, - "mview_state": { - "column": { - "state_id": true, - "view_id": true, - "mode": true, - "status": true, - "updated": true, - "version_id": true - }, - "index": { - "MVIEW_STATE_VIEW_ID": true, - "MVIEW_STATE_MODE": true - }, - "constraint": { - "PRIMARY": true + "mview_state": { + "column": { + "state_id": true, + "view_id": true, + "mode": true, + "status": true, + "updated": true, + "version_id": true + }, + "index": { + "MVIEW_STATE_VIEW_ID": true, + "MVIEW_STATE_MODE": true + }, + "constraint": { + "PRIMARY": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Integration/etc/db_schema_whitelist.json b/app/code/Magento/Integration/etc/db_schema_whitelist.json index 6513778060288..a7649eeb4ee1e 100644 --- a/app/code/Magento/Integration/etc/db_schema_whitelist.json +++ b/app/code/Magento/Integration/etc/db_schema_whitelist.json @@ -1,97 +1,97 @@ { - "oauth_consumer": { - "column": { - "entity_id": true, - "created_at": true, - "updated_at": true, - "name": true, - "key": true, - "secret": true, - "callback_url": true, - "rejected_callback_url": true + "oauth_consumer": { + "column": { + "entity_id": true, + "created_at": true, + "updated_at": true, + "name": true, + "key": true, + "secret": true, + "callback_url": true, + "rejected_callback_url": true + }, + "index": { + "OAUTH_CONSUMER_CREATED_AT": true, + "OAUTH_CONSUMER_UPDATED_AT": true + }, + "constraint": { + "PRIMARY": true, + "OAUTH_CONSUMER_KEY": true, + "OAUTH_CONSUMER_SECRET": true + } }, - "index": { - "OAUTH_CONSUMER_CREATED_AT": true, - "OAUTH_CONSUMER_UPDATED_AT": true + "oauth_token": { + "column": { + "entity_id": true, + "consumer_id": true, + "admin_id": true, + "customer_id": true, + "type": true, + "token": true, + "secret": true, + "verifier": true, + "callback_url": true, + "revoked": true, + "authorized": true, + "user_type": true, + "created_at": true + }, + "index": { + "OAUTH_TOKEN_CONSUMER_ID": true + }, + "constraint": { + "PRIMARY": true, + "OAUTH_TOKEN_ADMIN_ID_ADMIN_USER_USER_ID": true, + "OAUTH_TOKEN_CONSUMER_ID_OAUTH_CONSUMER_ENTITY_ID": true, + "OAUTH_TOKEN_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "OAUTH_TOKEN_TOKEN": true + } }, - "constraint": { - "PRIMARY": true, - "OAUTH_CONSUMER_KEY": true, - "OAUTH_CONSUMER_SECRET": true - } - }, - "oauth_token": { - "column": { - "entity_id": true, - "consumer_id": true, - "admin_id": true, - "customer_id": true, - "type": true, - "token": true, - "secret": true, - "verifier": true, - "callback_url": true, - "revoked": true, - "authorized": true, - "user_type": true, - "created_at": true - }, - "index": { - "OAUTH_TOKEN_CONSUMER_ID": true - }, - "constraint": { - "PRIMARY": true, - "OAUTH_TOKEN_ADMIN_ID_ADMIN_USER_USER_ID": true, - "OAUTH_TOKEN_CONSUMER_ID_OAUTH_CONSUMER_ENTITY_ID": true, - "OAUTH_TOKEN_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "OAUTH_TOKEN_TOKEN": true - } - }, - "oauth_nonce": { - "column": { - "nonce": true, - "timestamp": true, - "consumer_id": true + "oauth_nonce": { + "column": { + "nonce": true, + "timestamp": true, + "consumer_id": true + }, + "constraint": { + "OAUTH_NONCE_CONSUMER_ID_OAUTH_CONSUMER_ENTITY_ID": true, + "OAUTH_NONCE_NONCE_CONSUMER_ID": true + }, + "index": { + "OAUTH_NONCE_TIMESTAMP": true + } }, - "constraint": { - "OAUTH_NONCE_CONSUMER_ID_OAUTH_CONSUMER_ENTITY_ID": true, - "OAUTH_NONCE_NONCE_CONSUMER_ID": true - }, - "index": { - "OAUTH_NONCE_TIMESTAMP": true - } - }, - "integration": { - "column": { - "integration_id": true, - "name": true, - "email": true, - "endpoint": true, - "status": true, - "consumer_id": true, - "created_at": true, - "updated_at": true, - "setup_type": true, - "identity_link_url": true - }, - "constraint": { - "PRIMARY": true, - "INTEGRATION_CONSUMER_ID_OAUTH_CONSUMER_ENTITY_ID": true, - "INTEGRATION_NAME": true, - "INTEGRATION_CONSUMER_ID": true - } - }, - "oauth_token_request_log": { - "column": { - "log_id": true, - "user_name": true, - "user_type": true, - "failures_count": true, - "lock_expires_at": true + "integration": { + "column": { + "integration_id": true, + "name": true, + "email": true, + "endpoint": true, + "status": true, + "consumer_id": true, + "created_at": true, + "updated_at": true, + "setup_type": true, + "identity_link_url": true + }, + "constraint": { + "PRIMARY": true, + "INTEGRATION_CONSUMER_ID_OAUTH_CONSUMER_ENTITY_ID": true, + "INTEGRATION_NAME": true, + "INTEGRATION_CONSUMER_ID": true + } }, - "constraint": { - "PRIMARY": true, - "OAUTH_TOKEN_REQUEST_LOG_USER_NAME_USER_TYPE": true + "oauth_token_request_log": { + "column": { + "log_id": true, + "user_name": true, + "user_type": true, + "failures_count": true, + "lock_expires_at": true + }, + "constraint": { + "PRIMARY": true, + "OAUTH_TOKEN_REQUEST_LOG_USER_NAME_USER_TYPE": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json b/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json index 14c30198e87e4..f31981d2ec40f 100644 --- a/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json +++ b/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json @@ -1,13 +1,13 @@ { - "queue_lock": { - "column": { - "id": true, - "message_code": true, - "created_at": true - }, - "constraint": { - "PRIMARY": true, - "QUEUE_LOCK_MESSAGE_CODE": true + "queue_lock": { + "column": { + "id": true, + "message_code": true, + "created_at": true + }, + "constraint": { + "PRIMARY": true, + "QUEUE_LOCK_MESSAGE_CODE": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/MysqlMq/etc/db_schema_whitelist.json b/app/code/Magento/MysqlMq/etc/db_schema_whitelist.json index 4d5a919320fe9..711d19345f408 100644 --- a/app/code/Magento/MysqlMq/etc/db_schema_whitelist.json +++ b/app/code/Magento/MysqlMq/etc/db_schema_whitelist.json @@ -1,43 +1,43 @@ { - "queue": { - "column": { - "id": true, - "name": true + "queue": { + "column": { + "id": true, + "name": true + }, + "constraint": { + "PRIMARY": true, + "QUEUE_NAME": true + } }, - "constraint": { - "PRIMARY": true, - "QUEUE_NAME": true - } - }, - "queue_message": { - "column": { - "id": true, - "topic_name": true, - "body": true - }, - "constraint": { - "PRIMARY": true - } - }, - "queue_message_status": { - "column": { - "id": true, - "queue_id": true, - "message_id": true, - "updated_at": true, - "status": true, - "number_of_trials": true - }, - "index": { - "QUEUE_MESSAGE_STATUS_STATUS_UPDATED_AT": true + "queue_message": { + "column": { + "id": true, + "topic_name": true, + "body": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true, - "QUEUE_MESSAGE_STATUS_MESSAGE_ID_QUEUE_MESSAGE_ID": true, - "QUEUE_MESSAGE_ID_QUEUE_MESSAGE_STATUS_MESSAGE_ID": true, - "QUEUE_MESSAGE_STATUS_QUEUE_ID_QUEUE_ID": true, - "QUEUE_ID_QUEUE_MESSAGE_STATUS_QUEUE_ID": true, - "QUEUE_MESSAGE_STATUS_QUEUE_ID_MESSAGE_ID": true + "queue_message_status": { + "column": { + "id": true, + "queue_id": true, + "message_id": true, + "updated_at": true, + "status": true, + "number_of_trials": true + }, + "index": { + "QUEUE_MESSAGE_STATUS_STATUS_UPDATED_AT": true + }, + "constraint": { + "PRIMARY": true, + "QUEUE_MESSAGE_STATUS_MESSAGE_ID_QUEUE_MESSAGE_ID": true, + "QUEUE_MESSAGE_ID_QUEUE_MESSAGE_STATUS_MESSAGE_ID": true, + "QUEUE_MESSAGE_STATUS_QUEUE_ID_QUEUE_ID": true, + "QUEUE_ID_QUEUE_MESSAGE_STATUS_QUEUE_ID": true, + "QUEUE_MESSAGE_STATUS_QUEUE_ID_MESSAGE_ID": true + } } - } -} +} \ No newline at end of file diff --git a/app/code/Magento/NewRelicReporting/etc/db_schema_whitelist.json b/app/code/Magento/NewRelicReporting/etc/db_schema_whitelist.json index f5bf0afbb3e61..aa59e6cf831ea 100644 --- a/app/code/Magento/NewRelicReporting/etc/db_schema_whitelist.json +++ b/app/code/Magento/NewRelicReporting/etc/db_schema_whitelist.json @@ -1,61 +1,61 @@ { - "reporting_counts": { - "column": { - "entity_id": true, - "type": true, - "count": true, - "updated_at": true + "reporting_counts": { + "column": { + "entity_id": true, + "type": true, + "count": true, + "updated_at": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "reporting_module_status": { - "column": { - "entity_id": true, - "name": true, - "active": true, - "setup_version": true, - "state": true, - "updated_at": true + "reporting_module_status": { + "column": { + "entity_id": true, + "name": true, + "active": true, + "setup_version": true, + "state": true, + "updated_at": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "reporting_orders": { - "column": { - "entity_id": true, - "customer_id": true, - "total": true, - "total_base": true, - "item_count": true, - "updated_at": true + "reporting_orders": { + "column": { + "entity_id": true, + "customer_id": true, + "total": true, + "total_base": true, + "item_count": true, + "updated_at": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "reporting_users": { - "column": { - "entity_id": true, - "type": true, - "action": true, - "updated_at": true - }, - "constraint": { - "PRIMARY": true - } - }, - "reporting_system_updates": { - "column": { - "entity_id": true, - "type": true, - "action": true, - "updated_at": true + "reporting_users": { + "column": { + "entity_id": true, + "type": true, + "action": true, + "updated_at": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true + "reporting_system_updates": { + "column": { + "entity_id": true, + "type": true, + "action": true, + "updated_at": true + }, + "constraint": { + "PRIMARY": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Newsletter/etc/db_schema_whitelist.json b/app/code/Magento/Newsletter/etc/db_schema_whitelist.json index 27a42a55a174c..27666096e426e 100644 --- a/app/code/Magento/Newsletter/etc/db_schema_whitelist.json +++ b/app/code/Magento/Newsletter/etc/db_schema_whitelist.json @@ -1,116 +1,116 @@ { - "newsletter_subscriber": { - "column": { - "subscriber_id": true, - "store_id": true, - "change_status_at": true, - "customer_id": true, - "subscriber_email": true, - "subscriber_status": true, - "subscriber_confirm_code": true + "newsletter_subscriber": { + "column": { + "subscriber_id": true, + "store_id": true, + "change_status_at": true, + "customer_id": true, + "subscriber_email": true, + "subscriber_status": true, + "subscriber_confirm_code": true + }, + "index": { + "NEWSLETTER_SUBSCRIBER_CUSTOMER_ID": true, + "NEWSLETTER_SUBSCRIBER_STORE_ID": true, + "NEWSLETTER_SUBSCRIBER_SUBSCRIBER_EMAIL": true + }, + "constraint": { + "PRIMARY": true, + "NEWSLETTER_SUBSCRIBER_STORE_ID_STORE_STORE_ID": true + } }, - "index": { - "NEWSLETTER_SUBSCRIBER_CUSTOMER_ID": true, - "NEWSLETTER_SUBSCRIBER_STORE_ID": true, - "NEWSLETTER_SUBSCRIBER_SUBSCRIBER_EMAIL": true + "newsletter_template": { + "column": { + "template_id": true, + "template_code": true, + "template_text": true, + "template_styles": true, + "template_type": true, + "template_subject": true, + "template_sender_name": true, + "template_sender_email": true, + "template_actual": true, + "added_at": true, + "modified_at": true + }, + "index": { + "NEWSLETTER_TEMPLATE_TEMPLATE_ACTUAL": true, + "NEWSLETTER_TEMPLATE_ADDED_AT": true, + "NEWSLETTER_TEMPLATE_MODIFIED_AT": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true, - "NEWSLETTER_SUBSCRIBER_STORE_ID_STORE_STORE_ID": true - } - }, - "newsletter_template": { - "column": { - "template_id": true, - "template_code": true, - "template_text": true, - "template_styles": true, - "template_type": true, - "template_subject": true, - "template_sender_name": true, - "template_sender_email": true, - "template_actual": true, - "added_at": true, - "modified_at": true - }, - "index": { - "NEWSLETTER_TEMPLATE_TEMPLATE_ACTUAL": true, - "NEWSLETTER_TEMPLATE_ADDED_AT": true, - "NEWSLETTER_TEMPLATE_MODIFIED_AT": true - }, - "constraint": { - "PRIMARY": true - } - }, - "newsletter_queue": { - "column": { - "queue_id": true, - "template_id": true, - "newsletter_type": true, - "newsletter_text": true, - "newsletter_styles": true, - "newsletter_subject": true, - "newsletter_sender_name": true, - "newsletter_sender_email": true, - "queue_status": true, - "queue_start_at": true, - "queue_finish_at": true - }, - "index": { - "NEWSLETTER_QUEUE_TEMPLATE_ID": true - }, - "constraint": { - "PRIMARY": true, - "NEWSLETTER_QUEUE_TEMPLATE_ID_NEWSLETTER_TEMPLATE_TEMPLATE_ID": true - } - }, - "newsletter_queue_link": { - "column": { - "queue_link_id": true, - "queue_id": true, - "subscriber_id": true, - "letter_sent_at": true - }, - "index": { - "NEWSLETTER_QUEUE_LINK_SUBSCRIBER_ID": true, - "NEWSLETTER_QUEUE_LINK_QUEUE_ID_LETTER_SENT_AT": true - }, - "constraint": { - "PRIMARY": true, - "NEWSLETTER_QUEUE_LINK_QUEUE_ID_NEWSLETTER_QUEUE_QUEUE_ID": true, - "NLTTR_QUEUE_LNK_SUBSCRIBER_ID_NLTTR_SUBSCRIBER_SUBSCRIBER_ID": true - } - }, - "newsletter_queue_store_link": { - "column": { - "queue_id": true, - "store_id": true - }, - "index": { - "NEWSLETTER_QUEUE_STORE_LINK_STORE_ID": true + "newsletter_queue": { + "column": { + "queue_id": true, + "template_id": true, + "newsletter_type": true, + "newsletter_text": true, + "newsletter_styles": true, + "newsletter_subject": true, + "newsletter_sender_name": true, + "newsletter_sender_email": true, + "queue_status": true, + "queue_start_at": true, + "queue_finish_at": true + }, + "index": { + "NEWSLETTER_QUEUE_TEMPLATE_ID": true + }, + "constraint": { + "PRIMARY": true, + "NEWSLETTER_QUEUE_TEMPLATE_ID_NEWSLETTER_TEMPLATE_TEMPLATE_ID": true + } }, - "constraint": { - "PRIMARY": true, - "NEWSLETTER_QUEUE_STORE_LINK_QUEUE_ID_NEWSLETTER_QUEUE_QUEUE_ID": true, - "NEWSLETTER_QUEUE_STORE_LINK_STORE_ID_STORE_STORE_ID": true - } - }, - "newsletter_problem": { - "column": { - "problem_id": true, - "subscriber_id": true, - "queue_id": true, - "problem_error_code": true, - "problem_error_text": true + "newsletter_queue_link": { + "column": { + "queue_link_id": true, + "queue_id": true, + "subscriber_id": true, + "letter_sent_at": true + }, + "index": { + "NEWSLETTER_QUEUE_LINK_SUBSCRIBER_ID": true, + "NEWSLETTER_QUEUE_LINK_QUEUE_ID_LETTER_SENT_AT": true + }, + "constraint": { + "PRIMARY": true, + "NEWSLETTER_QUEUE_LINK_QUEUE_ID_NEWSLETTER_QUEUE_QUEUE_ID": true, + "NLTTR_QUEUE_LNK_SUBSCRIBER_ID_NLTTR_SUBSCRIBER_SUBSCRIBER_ID": true + } }, - "index": { - "NEWSLETTER_PROBLEM_SUBSCRIBER_ID": true, - "NEWSLETTER_PROBLEM_QUEUE_ID": true + "newsletter_queue_store_link": { + "column": { + "queue_id": true, + "store_id": true + }, + "index": { + "NEWSLETTER_QUEUE_STORE_LINK_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "NEWSLETTER_QUEUE_STORE_LINK_QUEUE_ID_NEWSLETTER_QUEUE_QUEUE_ID": true, + "NEWSLETTER_QUEUE_STORE_LINK_STORE_ID_STORE_STORE_ID": true + } }, - "constraint": { - "PRIMARY": true, - "NEWSLETTER_PROBLEM_QUEUE_ID_NEWSLETTER_QUEUE_QUEUE_ID": true, - "NLTTR_PROBLEM_SUBSCRIBER_ID_NLTTR_SUBSCRIBER_SUBSCRIBER_ID": true + "newsletter_problem": { + "column": { + "problem_id": true, + "subscriber_id": true, + "queue_id": true, + "problem_error_code": true, + "problem_error_text": true + }, + "index": { + "NEWSLETTER_PROBLEM_SUBSCRIBER_ID": true, + "NEWSLETTER_PROBLEM_QUEUE_ID": true + }, + "constraint": { + "PRIMARY": true, + "NEWSLETTER_PROBLEM_QUEUE_ID_NEWSLETTER_QUEUE_QUEUE_ID": true, + "NLTTR_PROBLEM_SUBSCRIBER_ID_NLTTR_SUBSCRIBER_SUBSCRIBER_ID": true + } } - } -} +} \ No newline at end of file diff --git a/app/code/Magento/OfflineShipping/etc/db_schema_whitelist.json b/app/code/Magento/OfflineShipping/etc/db_schema_whitelist.json index 8a189e711c222..72da62b8159c6 100644 --- a/app/code/Magento/OfflineShipping/etc/db_schema_whitelist.json +++ b/app/code/Magento/OfflineShipping/etc/db_schema_whitelist.json @@ -1,44 +1,44 @@ { - "shipping_tablerate": { - "column": { - "pk": true, - "website_id": true, - "dest_country_id": true, - "dest_region_id": true, - "dest_zip": true, - "condition_name": true, - "condition_value": true, - "price": true, - "cost": true + "shipping_tablerate": { + "column": { + "pk": true, + "website_id": true, + "dest_country_id": true, + "dest_region_id": true, + "dest_zip": true, + "condition_name": true, + "condition_value": true, + "price": true, + "cost": true + }, + "constraint": { + "PRIMARY": true, + "UNQ_D60821CDB2AFACEE1566CFC02D0D4CAA": true + } }, - "constraint": { - "PRIMARY": true, - "UNQ_D60821CDB2AFACEE1566CFC02D0D4CAA": true - } - }, - "salesrule": { - "column": { - "simple_free_shipping": true - } - }, - "sales_order_item": { - "column": { - "free_shipping": true - } - }, - "quote_address": { - "column": { - "free_shipping": true - } - }, - "quote_item": { - "column": { - "free_shipping": true - } - }, - "quote_address_item": { - "column": { - "free_shipping": true + "salesrule": { + "column": { + "simple_free_shipping": true + } + }, + "sales_order_item": { + "column": { + "free_shipping": true + } + }, + "quote_address": { + "column": { + "free_shipping": true + } + }, + "quote_item": { + "column": { + "free_shipping": true + } + }, + "quote_address_item": { + "column": { + "free_shipping": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Paypal/etc/db_schema_whitelist.json b/app/code/Magento/Paypal/etc/db_schema_whitelist.json index 64aa8456b5432..8caacbf308232 100644 --- a/app/code/Magento/Paypal/etc/db_schema_whitelist.json +++ b/app/code/Magento/Paypal/etc/db_schema_whitelist.json @@ -1,120 +1,120 @@ { - "paypal_billing_agreement": { - "column": { - "agreement_id": true, - "customer_id": true, - "method_code": true, - "reference_id": true, - "status": true, - "created_at": true, - "updated_at": true, - "store_id": true, - "agreement_label": true + "paypal_billing_agreement": { + "column": { + "agreement_id": true, + "customer_id": true, + "method_code": true, + "reference_id": true, + "status": true, + "created_at": true, + "updated_at": true, + "store_id": true, + "agreement_label": true + }, + "index": { + "PAYPAL_BILLING_AGREEMENT_CUSTOMER_ID": true, + "PAYPAL_BILLING_AGREEMENT_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "PAYPAL_BILLING_AGREEMENT_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "PAYPAL_BILLING_AGREEMENT_STORE_ID_STORE_STORE_ID": true + } }, - "index": { - "PAYPAL_BILLING_AGREEMENT_CUSTOMER_ID": true, - "PAYPAL_BILLING_AGREEMENT_STORE_ID": true + "paypal_billing_agreement_order": { + "column": { + "agreement_id": true, + "order_id": true + }, + "index": { + "PAYPAL_BILLING_AGREEMENT_ORDER_ORDER_ID": true + }, + "constraint": { + "PRIMARY": true, + "PAYPAL_BILLING_AGRT_ORDER_AGRT_ID_PAYPAL_BILLING_AGRT_AGRT_ID": true, + "PAYPAL_BILLING_AGREEMENT_ORDER_ORDER_ID_SALES_ORDER_ENTITY_ID": true + } }, - "constraint": { - "PRIMARY": true, - "PAYPAL_BILLING_AGREEMENT_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "PAYPAL_BILLING_AGREEMENT_STORE_ID_STORE_STORE_ID": true - } - }, - "paypal_billing_agreement_order": { - "column": { - "agreement_id": true, - "order_id": true - }, - "index": { - "PAYPAL_BILLING_AGREEMENT_ORDER_ORDER_ID": true - }, - "constraint": { - "PRIMARY": true, - "PAYPAL_BILLING_AGRT_ORDER_AGRT_ID_PAYPAL_BILLING_AGRT_AGRT_ID": true, - "PAYPAL_BILLING_AGREEMENT_ORDER_ORDER_ID_SALES_ORDER_ENTITY_ID": true - } - }, - "paypal_settlement_report": { - "column": { - "report_id": true, - "report_date": true, - "account_id": true, - "filename": true, - "last_modified": true - }, - "constraint": { - "PRIMARY": true, - "PAYPAL_SETTLEMENT_REPORT_REPORT_DATE_ACCOUNT_ID": true - } - }, - "paypal_settlement_report_row": { - "column": { - "row_id": true, - "report_id": true, - "transaction_id": true, - "invoice_id": true, - "paypal_reference_id": true, - "paypal_reference_id_type": true, - "transaction_event_code": true, - "transaction_initiation_date": true, - "transaction_completion_date": true, - "transaction_debit_or_credit": true, - "gross_transaction_amount": true, - "gross_transaction_currency": true, - "fee_debit_or_credit": true, - "fee_amount": true, - "fee_currency": true, - "custom_field": true, - "consumer_id": true, - "payment_tracking_id": true, - "store_id": true + "paypal_settlement_report": { + "column": { + "report_id": true, + "report_date": true, + "account_id": true, + "filename": true, + "last_modified": true + }, + "constraint": { + "PRIMARY": true, + "PAYPAL_SETTLEMENT_REPORT_REPORT_DATE_ACCOUNT_ID": true + } }, - "index": { - "PAYPAL_SETTLEMENT_REPORT_ROW_REPORT_ID": true + "paypal_settlement_report_row": { + "column": { + "row_id": true, + "report_id": true, + "transaction_id": true, + "invoice_id": true, + "paypal_reference_id": true, + "paypal_reference_id_type": true, + "transaction_event_code": true, + "transaction_initiation_date": true, + "transaction_completion_date": true, + "transaction_debit_or_credit": true, + "gross_transaction_amount": true, + "gross_transaction_currency": true, + "fee_debit_or_credit": true, + "fee_amount": true, + "fee_currency": true, + "custom_field": true, + "consumer_id": true, + "payment_tracking_id": true, + "store_id": true + }, + "index": { + "PAYPAL_SETTLEMENT_REPORT_ROW_REPORT_ID": true + }, + "constraint": { + "PRIMARY": true, + "FK_E183E488F593E0DE10C6EBFFEBAC9B55": true + } }, - "constraint": { - "PRIMARY": true, - "FK_E183E488F593E0DE10C6EBFFEBAC9B55": true - } - }, - "paypal_cert": { - "column": { - "cert_id": true, - "website_id": true, - "content": true, - "updated_at": true + "paypal_cert": { + "column": { + "cert_id": true, + "website_id": true, + "content": true, + "updated_at": true + }, + "index": { + "PAYPAL_CERT_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true, + "PAYPAL_CERT_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true + } }, - "index": { - "PAYPAL_CERT_WEBSITE_ID": true + "paypal_payment_transaction": { + "column": { + "transaction_id": true, + "txn_id": true, + "additional_information": true, + "created_at": true + }, + "constraint": { + "PRIMARY": true, + "PAYPAL_PAYMENT_TRANSACTION_TXN_ID": true + } }, - "constraint": { - "PRIMARY": true, - "PAYPAL_CERT_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true - } - }, - "paypal_payment_transaction": { - "column": { - "transaction_id": true, - "txn_id": true, - "additional_information": true, - "created_at": true + "quote_payment": { + "column": { + "paypal_payer_id": true, + "paypal_payer_status": true, + "paypal_correlation_id": true + } }, - "constraint": { - "PRIMARY": true, - "PAYPAL_PAYMENT_TRANSACTION_TXN_ID": true - } - }, - "quote_payment": { - "column": { - "paypal_payer_id": true, - "paypal_payer_status": true, - "paypal_correlation_id": true - } - }, - "sales_order": { - "column": { - "paypal_ipn_customer_notified": true + "sales_order": { + "column": { + "paypal_ipn_customer_notified": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Persistent/etc/db_schema_whitelist.json b/app/code/Magento/Persistent/etc/db_schema_whitelist.json index 93da6b440faa6..50a4567d4a6f1 100644 --- a/app/code/Magento/Persistent/etc/db_schema_whitelist.json +++ b/app/code/Magento/Persistent/etc/db_schema_whitelist.json @@ -1,27 +1,27 @@ { - "persistent_session": { - "column": { - "persistent_id": true, - "key": true, - "customer_id": true, - "website_id": true, - "info": true, - "updated_at": true + "persistent_session": { + "column": { + "persistent_id": true, + "key": true, + "customer_id": true, + "website_id": true, + "info": true, + "updated_at": true + }, + "index": { + "PERSISTENT_SESSION_UPDATED_AT": true + }, + "constraint": { + "PRIMARY": true, + "PERSISTENT_SESSION_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "PERSISTENT_SESSION_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, + "PERSISTENT_SESSION_KEY": true, + "PERSISTENT_SESSION_CUSTOMER_ID": true + } }, - "index": { - "PERSISTENT_SESSION_UPDATED_AT": true - }, - "constraint": { - "PRIMARY": true, - "PERSISTENT_SESSION_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "PERSISTENT_SESSION_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, - "PERSISTENT_SESSION_KEY": true, - "PERSISTENT_SESSION_CUSTOMER_ID": true - } - }, - "quote": { - "column": { - "is_persistent": true + "quote": { + "column": { + "is_persistent": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json b/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json index 31dd3857dff4e..266d00e04c5bc 100644 --- a/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json +++ b/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json @@ -1,51 +1,51 @@ { - "product_alert_price": { - "column": { - "alert_price_id": true, - "customer_id": true, - "product_id": true, - "price": true, - "website_id": true, - "add_date": true, - "last_send_date": true, - "send_count": true, - "status": true + "product_alert_price": { + "column": { + "alert_price_id": true, + "customer_id": true, + "product_id": true, + "price": true, + "website_id": true, + "add_date": true, + "last_send_date": true, + "send_count": true, + "status": true + }, + "index": { + "PRODUCT_ALERT_PRICE_CUSTOMER_ID": true, + "PRODUCT_ALERT_PRICE_PRODUCT_ID": true, + "PRODUCT_ALERT_PRICE_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true, + "PRODUCT_ALERT_PRICE_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "PRODUCT_ALERT_PRICE_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, + "PRODUCT_ALERT_PRICE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, + "PRODUCT_ALERT_PRICE_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true + } }, - "index": { - "PRODUCT_ALERT_PRICE_CUSTOMER_ID": true, - "PRODUCT_ALERT_PRICE_PRODUCT_ID": true, - "PRODUCT_ALERT_PRICE_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true, - "PRODUCT_ALERT_PRICE_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "PRODUCT_ALERT_PRICE_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, - "PRODUCT_ALERT_PRICE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, - "PRODUCT_ALERT_PRICE_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true - } - }, - "product_alert_stock": { - "column": { - "alert_stock_id": true, - "customer_id": true, - "product_id": true, - "website_id": true, - "add_date": true, - "send_date": true, - "send_count": true, - "status": true - }, - "index": { - "PRODUCT_ALERT_STOCK_CUSTOMER_ID": true, - "PRODUCT_ALERT_STOCK_PRODUCT_ID": true, - "PRODUCT_ALERT_STOCK_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true, - "PRODUCT_ALERT_STOCK_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, - "PRODUCT_ALERT_STOCK_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "PRODUCT_ALERT_STOCK_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, - "PRODUCT_ALERT_STOCK_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true + "product_alert_stock": { + "column": { + "alert_stock_id": true, + "customer_id": true, + "product_id": true, + "website_id": true, + "add_date": true, + "send_date": true, + "send_count": true, + "status": true + }, + "index": { + "PRODUCT_ALERT_STOCK_CUSTOMER_ID": true, + "PRODUCT_ALERT_STOCK_PRODUCT_ID": true, + "PRODUCT_ALERT_STOCK_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true, + "PRODUCT_ALERT_STOCK_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, + "PRODUCT_ALERT_STOCK_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "PRODUCT_ALERT_STOCK_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, + "PRODUCT_ALERT_STOCK_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/ProductVideo/etc/db_schema_whitelist.json b/app/code/Magento/ProductVideo/etc/db_schema_whitelist.json index b0c73baecd077..78fa6538da07c 100644 --- a/app/code/Magento/ProductVideo/etc/db_schema_whitelist.json +++ b/app/code/Magento/ProductVideo/etc/db_schema_whitelist.json @@ -1,18 +1,18 @@ { - "catalog_product_entity_media_gallery_value_video": { - "column": { - "value_id": true, - "store_id": true, - "provider": true, - "url": true, - "title": true, - "description": true, - "metadata": true - }, - "constraint": { - "FK_6FDF205946906B0E653E60AA769899F8": true, - "CAT_PRD_ENTT_MDA_GLR_VAL_VIDEO_STORE_ID_STORE_STORE_ID": true, - "CAT_PRD_ENTT_MDA_GLR_VAL_VIDEO_VAL_ID_STORE_ID": true + "catalog_product_entity_media_gallery_value_video": { + "column": { + "value_id": true, + "store_id": true, + "provider": true, + "url": true, + "title": true, + "description": true, + "metadata": true + }, + "constraint": { + "FK_6FDF205946906B0E653E60AA769899F8": true, + "CAT_PRD_ENTT_MDA_GLR_VAL_VIDEO_STORE_ID_STORE_STORE_ID": true, + "CAT_PRD_ENTT_MDA_GLR_VAL_VIDEO_VAL_ID_STORE_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Quote/etc/db_schema_whitelist.json b/app/code/Magento/Quote/etc/db_schema_whitelist.json index f7898c9d15dc0..c2cc34293dcb5 100644 --- a/app/code/Magento/Quote/etc/db_schema_whitelist.json +++ b/app/code/Magento/Quote/etc/db_schema_whitelist.json @@ -1,329 +1,329 @@ { - "quote": { - "column": { - "entity_id": true, - "store_id": true, - "created_at": true, - "updated_at": true, - "converted_at": true, - "is_active": true, - "is_virtual": true, - "is_multi_shipping": true, - "items_count": true, - "items_qty": true, - "orig_order_id": true, - "store_to_base_rate": true, - "store_to_quote_rate": true, - "base_currency_code": true, - "store_currency_code": true, - "quote_currency_code": true, - "grand_total": true, - "base_grand_total": true, - "checkout_method": true, - "customer_id": true, - "customer_tax_class_id": true, - "customer_group_id": true, - "customer_email": true, - "customer_prefix": true, - "customer_firstname": true, - "customer_middlename": true, - "customer_lastname": true, - "customer_suffix": true, - "customer_dob": true, - "customer_note": true, - "customer_note_notify": true, - "customer_is_guest": true, - "remote_ip": true, - "applied_rule_ids": true, - "reserved_order_id": true, - "password_hash": true, - "coupon_code": true, - "global_currency_code": true, - "base_to_global_rate": true, - "base_to_quote_rate": true, - "customer_taxvat": true, - "customer_gender": true, - "subtotal": true, - "base_subtotal": true, - "subtotal_with_discount": true, - "base_subtotal_with_discount": true, - "is_changed": true, - "trigger_recollect": true, - "ext_shipping_info": true + "quote": { + "column": { + "entity_id": true, + "store_id": true, + "created_at": true, + "updated_at": true, + "converted_at": true, + "is_active": true, + "is_virtual": true, + "is_multi_shipping": true, + "items_count": true, + "items_qty": true, + "orig_order_id": true, + "store_to_base_rate": true, + "store_to_quote_rate": true, + "base_currency_code": true, + "store_currency_code": true, + "quote_currency_code": true, + "grand_total": true, + "base_grand_total": true, + "checkout_method": true, + "customer_id": true, + "customer_tax_class_id": true, + "customer_group_id": true, + "customer_email": true, + "customer_prefix": true, + "customer_firstname": true, + "customer_middlename": true, + "customer_lastname": true, + "customer_suffix": true, + "customer_dob": true, + "customer_note": true, + "customer_note_notify": true, + "customer_is_guest": true, + "remote_ip": true, + "applied_rule_ids": true, + "reserved_order_id": true, + "password_hash": true, + "coupon_code": true, + "global_currency_code": true, + "base_to_global_rate": true, + "base_to_quote_rate": true, + "customer_taxvat": true, + "customer_gender": true, + "subtotal": true, + "base_subtotal": true, + "subtotal_with_discount": true, + "base_subtotal_with_discount": true, + "is_changed": true, + "trigger_recollect": true, + "ext_shipping_info": true + }, + "index": { + "QUOTE_CUSTOMER_ID_STORE_ID_IS_ACTIVE": true, + "QUOTE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "QUOTE_STORE_ID_STORE_STORE_ID": true + } }, - "index": { - "QUOTE_CUSTOMER_ID_STORE_ID_IS_ACTIVE": true, - "QUOTE_STORE_ID": true + "quote_address": { + "column": { + "address_id": true, + "quote_id": true, + "created_at": true, + "updated_at": true, + "customer_id": true, + "save_in_address_book": true, + "customer_address_id": true, + "address_type": true, + "email": true, + "prefix": true, + "firstname": true, + "middlename": true, + "lastname": true, + "suffix": true, + "company": true, + "street": true, + "city": true, + "region": true, + "region_id": true, + "postcode": true, + "country_id": true, + "telephone": true, + "fax": true, + "same_as_billing": true, + "collect_shipping_rates": true, + "shipping_method": true, + "shipping_description": true, + "weight": true, + "subtotal": true, + "base_subtotal": true, + "subtotal_with_discount": true, + "base_subtotal_with_discount": true, + "tax_amount": true, + "base_tax_amount": true, + "shipping_amount": true, + "base_shipping_amount": true, + "shipping_tax_amount": true, + "base_shipping_tax_amount": true, + "discount_amount": true, + "base_discount_amount": true, + "grand_total": true, + "base_grand_total": true, + "customer_notes": true, + "applied_taxes": true, + "discount_description": true, + "shipping_discount_amount": true, + "base_shipping_discount_amount": true, + "subtotal_incl_tax": true, + "base_subtotal_total_incl_tax": true, + "discount_tax_compensation_amount": true, + "base_discount_tax_compensation_amount": true, + "shipping_discount_tax_compensation_amount": true, + "base_shipping_discount_tax_compensation_amnt": true, + "shipping_incl_tax": true, + "base_shipping_incl_tax": true, + "vat_id": true, + "vat_is_valid": true, + "vat_request_id": true, + "vat_request_date": true, + "vat_request_success": true + }, + "index": { + "QUOTE_ADDRESS_QUOTE_ID": true + }, + "constraint": { + "PRIMARY": true, + "QUOTE_ADDRESS_QUOTE_ID_QUOTE_ENTITY_ID": true + } }, - "constraint": { - "PRIMARY": true, - "QUOTE_STORE_ID_STORE_STORE_ID": true - } - }, - "quote_address": { - "column": { - "address_id": true, - "quote_id": true, - "created_at": true, - "updated_at": true, - "customer_id": true, - "save_in_address_book": true, - "customer_address_id": true, - "address_type": true, - "email": true, - "prefix": true, - "firstname": true, - "middlename": true, - "lastname": true, - "suffix": true, - "company": true, - "street": true, - "city": true, - "region": true, - "region_id": true, - "postcode": true, - "country_id": true, - "telephone": true, - "fax": true, - "same_as_billing": true, - "collect_shipping_rates": true, - "shipping_method": true, - "shipping_description": true, - "weight": true, - "subtotal": true, - "base_subtotal": true, - "subtotal_with_discount": true, - "base_subtotal_with_discount": true, - "tax_amount": true, - "base_tax_amount": true, - "shipping_amount": true, - "base_shipping_amount": true, - "shipping_tax_amount": true, - "base_shipping_tax_amount": true, - "discount_amount": true, - "base_discount_amount": true, - "grand_total": true, - "base_grand_total": true, - "customer_notes": true, - "applied_taxes": true, - "discount_description": true, - "shipping_discount_amount": true, - "base_shipping_discount_amount": true, - "subtotal_incl_tax": true, - "base_subtotal_total_incl_tax": true, - "discount_tax_compensation_amount": true, - "base_discount_tax_compensation_amount": true, - "shipping_discount_tax_compensation_amount": true, - "base_shipping_discount_tax_compensation_amnt": true, - "shipping_incl_tax": true, - "base_shipping_incl_tax": true, - "vat_id": true, - "vat_is_valid": true, - "vat_request_id": true, - "vat_request_date": true, - "vat_request_success": true - }, - "index": { - "QUOTE_ADDRESS_QUOTE_ID": true - }, - "constraint": { - "PRIMARY": true, - "QUOTE_ADDRESS_QUOTE_ID_QUOTE_ENTITY_ID": true - } - }, - "quote_item": { - "column": { - "item_id": true, - "quote_id": true, - "created_at": true, - "updated_at": true, - "product_id": true, - "store_id": true, - "parent_item_id": true, - "is_virtual": true, - "sku": true, - "name": true, - "description": true, - "applied_rule_ids": true, - "additional_data": true, - "is_qty_decimal": true, - "no_discount": true, - "weight": true, - "qty": true, - "price": true, - "base_price": true, - "custom_price": true, - "discount_percent": true, - "discount_amount": true, - "base_discount_amount": true, - "tax_percent": true, - "tax_amount": true, - "base_tax_amount": true, - "row_total": true, - "base_row_total": true, - "row_total_with_discount": true, - "row_weight": true, - "product_type": true, - "base_tax_before_discount": true, - "tax_before_discount": true, - "original_custom_price": true, - "redirect_url": true, - "base_cost": true, - "price_incl_tax": true, - "base_price_incl_tax": true, - "row_total_incl_tax": true, - "base_row_total_incl_tax": true, - "discount_tax_compensation_amount": true, - "base_discount_tax_compensation_amount": true - }, - "index": { - "QUOTE_ITEM_PARENT_ITEM_ID": true, - "QUOTE_ITEM_PRODUCT_ID": true, - "QUOTE_ITEM_QUOTE_ID": true, - "QUOTE_ITEM_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "QUOTE_ITEM_PARENT_ITEM_ID_QUOTE_ITEM_ITEM_ID": true, - "QUOTE_ITEM_QUOTE_ID_QUOTE_ENTITY_ID": true, - "QUOTE_ITEM_STORE_ID_STORE_STORE_ID": true - } - }, - "quote_address_item": { - "column": { - "address_item_id": true, - "parent_item_id": true, - "quote_address_id": true, - "quote_item_id": true, - "created_at": true, - "updated_at": true, - "applied_rule_ids": true, - "additional_data": true, - "weight": true, - "qty": true, - "discount_amount": true, - "tax_amount": true, - "row_total": true, - "base_row_total": true, - "row_total_with_discount": true, - "base_discount_amount": true, - "base_tax_amount": true, - "row_weight": true, - "product_id": true, - "super_product_id": true, - "parent_product_id": true, - "sku": true, - "image": true, - "name": true, - "description": true, - "is_qty_decimal": true, - "price": true, - "discount_percent": true, - "no_discount": true, - "tax_percent": true, - "base_price": true, - "base_cost": true, - "price_incl_tax": true, - "base_price_incl_tax": true, - "row_total_incl_tax": true, - "base_row_total_incl_tax": true, - "discount_tax_compensation_amount": true, - "base_discount_tax_compensation_amount": true - }, - "index": { - "QUOTE_ADDRESS_ITEM_QUOTE_ADDRESS_ID": true, - "QUOTE_ADDRESS_ITEM_PARENT_ITEM_ID": true, - "QUOTE_ADDRESS_ITEM_QUOTE_ITEM_ID": true + "quote_item": { + "column": { + "item_id": true, + "quote_id": true, + "created_at": true, + "updated_at": true, + "product_id": true, + "store_id": true, + "parent_item_id": true, + "is_virtual": true, + "sku": true, + "name": true, + "description": true, + "applied_rule_ids": true, + "additional_data": true, + "is_qty_decimal": true, + "no_discount": true, + "weight": true, + "qty": true, + "price": true, + "base_price": true, + "custom_price": true, + "discount_percent": true, + "discount_amount": true, + "base_discount_amount": true, + "tax_percent": true, + "tax_amount": true, + "base_tax_amount": true, + "row_total": true, + "base_row_total": true, + "row_total_with_discount": true, + "row_weight": true, + "product_type": true, + "base_tax_before_discount": true, + "tax_before_discount": true, + "original_custom_price": true, + "redirect_url": true, + "base_cost": true, + "price_incl_tax": true, + "base_price_incl_tax": true, + "row_total_incl_tax": true, + "base_row_total_incl_tax": true, + "discount_tax_compensation_amount": true, + "base_discount_tax_compensation_amount": true + }, + "index": { + "QUOTE_ITEM_PARENT_ITEM_ID": true, + "QUOTE_ITEM_PRODUCT_ID": true, + "QUOTE_ITEM_QUOTE_ID": true, + "QUOTE_ITEM_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "QUOTE_ITEM_PARENT_ITEM_ID_QUOTE_ITEM_ITEM_ID": true, + "QUOTE_ITEM_QUOTE_ID_QUOTE_ENTITY_ID": true, + "QUOTE_ITEM_STORE_ID_STORE_STORE_ID": true + } }, - "constraint": { - "PRIMARY": true, - "QUOTE_ADDRESS_ITEM_QUOTE_ADDRESS_ID_QUOTE_ADDRESS_ADDRESS_ID": true, - "QUOTE_ADDR_ITEM_PARENT_ITEM_ID_QUOTE_ADDR_ITEM_ADDR_ITEM_ID": true, - "QUOTE_ADDRESS_ITEM_QUOTE_ITEM_ID_QUOTE_ITEM_ITEM_ID": true - } - }, - "quote_item_option": { - "column": { - "option_id": true, - "item_id": true, - "product_id": true, - "code": true, - "value": true - }, - "index": { - "QUOTE_ITEM_OPTION_ITEM_ID": true - }, - "constraint": { - "PRIMARY": true, - "QUOTE_ITEM_OPTION_ITEM_ID_QUOTE_ITEM_ITEM_ID": true - } - }, - "quote_payment": { - "column": { - "payment_id": true, - "quote_id": true, - "created_at": true, - "updated_at": true, - "method": true, - "cc_type": true, - "cc_number_enc": true, - "cc_last_4": true, - "cc_cid_enc": true, - "cc_owner": true, - "cc_exp_month": true, - "cc_exp_year": true, - "cc_ss_owner": true, - "cc_ss_start_month": true, - "cc_ss_start_year": true, - "po_number": true, - "additional_data": true, - "cc_ss_issue": true, - "additional_information": true + "quote_address_item": { + "column": { + "address_item_id": true, + "parent_item_id": true, + "quote_address_id": true, + "quote_item_id": true, + "created_at": true, + "updated_at": true, + "applied_rule_ids": true, + "additional_data": true, + "weight": true, + "qty": true, + "discount_amount": true, + "tax_amount": true, + "row_total": true, + "base_row_total": true, + "row_total_with_discount": true, + "base_discount_amount": true, + "base_tax_amount": true, + "row_weight": true, + "product_id": true, + "super_product_id": true, + "parent_product_id": true, + "sku": true, + "image": true, + "name": true, + "description": true, + "is_qty_decimal": true, + "price": true, + "discount_percent": true, + "no_discount": true, + "tax_percent": true, + "base_price": true, + "base_cost": true, + "price_incl_tax": true, + "base_price_incl_tax": true, + "row_total_incl_tax": true, + "base_row_total_incl_tax": true, + "discount_tax_compensation_amount": true, + "base_discount_tax_compensation_amount": true + }, + "index": { + "QUOTE_ADDRESS_ITEM_QUOTE_ADDRESS_ID": true, + "QUOTE_ADDRESS_ITEM_PARENT_ITEM_ID": true, + "QUOTE_ADDRESS_ITEM_QUOTE_ITEM_ID": true + }, + "constraint": { + "PRIMARY": true, + "QUOTE_ADDRESS_ITEM_QUOTE_ADDRESS_ID_QUOTE_ADDRESS_ADDRESS_ID": true, + "QUOTE_ADDR_ITEM_PARENT_ITEM_ID_QUOTE_ADDR_ITEM_ADDR_ITEM_ID": true, + "QUOTE_ADDRESS_ITEM_QUOTE_ITEM_ID_QUOTE_ITEM_ITEM_ID": true + } }, - "index": { - "QUOTE_PAYMENT_QUOTE_ID": true + "quote_item_option": { + "column": { + "option_id": true, + "item_id": true, + "product_id": true, + "code": true, + "value": true + }, + "index": { + "QUOTE_ITEM_OPTION_ITEM_ID": true + }, + "constraint": { + "PRIMARY": true, + "QUOTE_ITEM_OPTION_ITEM_ID_QUOTE_ITEM_ITEM_ID": true + } }, - "constraint": { - "PRIMARY": true, - "QUOTE_PAYMENT_QUOTE_ID_QUOTE_ENTITY_ID": true - } - }, - "quote_shipping_rate": { - "column": { - "rate_id": true, - "address_id": true, - "created_at": true, - "updated_at": true, - "carrier": true, - "carrier_title": true, - "code": true, - "method": true, - "method_description": true, - "price": true, - "error_message": true, - "method_title": true - }, - "index": { - "QUOTE_SHIPPING_RATE_ADDRESS_ID": true - }, - "constraint": { - "PRIMARY": true, - "QUOTE_SHIPPING_RATE_ADDRESS_ID_QUOTE_ADDRESS_ADDRESS_ID": true - } - }, - "quote_id_mask": { - "column": { - "entity_id": true, - "quote_id": true, - "masked_id": true + "quote_payment": { + "column": { + "payment_id": true, + "quote_id": true, + "created_at": true, + "updated_at": true, + "method": true, + "cc_type": true, + "cc_number_enc": true, + "cc_last_4": true, + "cc_cid_enc": true, + "cc_owner": true, + "cc_exp_month": true, + "cc_exp_year": true, + "cc_ss_owner": true, + "cc_ss_start_month": true, + "cc_ss_start_year": true, + "po_number": true, + "additional_data": true, + "cc_ss_issue": true, + "additional_information": true + }, + "index": { + "QUOTE_PAYMENT_QUOTE_ID": true + }, + "constraint": { + "PRIMARY": true, + "QUOTE_PAYMENT_QUOTE_ID_QUOTE_ENTITY_ID": true + } }, - "index": { - "QUOTE_ID_MASK_QUOTE_ID": true, - "QUOTE_ID_MASK_MASKED_ID": true + "quote_shipping_rate": { + "column": { + "rate_id": true, + "address_id": true, + "created_at": true, + "updated_at": true, + "carrier": true, + "carrier_title": true, + "code": true, + "method": true, + "method_description": true, + "price": true, + "error_message": true, + "method_title": true + }, + "index": { + "QUOTE_SHIPPING_RATE_ADDRESS_ID": true + }, + "constraint": { + "PRIMARY": true, + "QUOTE_SHIPPING_RATE_ADDRESS_ID_QUOTE_ADDRESS_ADDRESS_ID": true + } }, - "constraint": { - "PRIMARY": true, - "QUOTE_ID_MASK_QUOTE_ID_QUOTE_ENTITY_ID": true + "quote_id_mask": { + "column": { + "entity_id": true, + "quote_id": true, + "masked_id": true + }, + "index": { + "QUOTE_ID_MASK_QUOTE_ID": true, + "QUOTE_ID_MASK_MASKED_ID": true + }, + "constraint": { + "PRIMARY": true, + "QUOTE_ID_MASK_QUOTE_ID_QUOTE_ENTITY_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/ReleaseNotification/etc/db_schema_whitelist.json b/app/code/Magento/ReleaseNotification/etc/db_schema_whitelist.json index d44e071b00c7c..5edb2b7010be6 100644 --- a/app/code/Magento/ReleaseNotification/etc/db_schema_whitelist.json +++ b/app/code/Magento/ReleaseNotification/etc/db_schema_whitelist.json @@ -1,14 +1,14 @@ { - "release_notification_viewer_log": { - "column": { - "id": true, - "viewer_id": true, - "last_view_version": true - }, - "constraint": { - "PRIMARY": true, - "RELEASE_NOTIFICATION_VIEWER_LOG_VIEWER_ID_ADMIN_USER_USER_ID": true, - "RELEASE_NOTIFICATION_VIEWER_LOG_VIEWER_ID": true + "release_notification_viewer_log": { + "column": { + "id": true, + "viewer_id": true, + "last_view_version": true + }, + "constraint": { + "PRIMARY": true, + "RELEASE_NOTIFICATION_VIEWER_LOG_VIEWER_ID_ADMIN_USER_USER_ID": true, + "RELEASE_NOTIFICATION_VIEWER_LOG_VIEWER_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Reports/etc/db_schema_whitelist.json b/app/code/Magento/Reports/etc/db_schema_whitelist.json index 458b2ea8fa803..8f55ef4fdbee6 100644 --- a/app/code/Magento/Reports/etc/db_schema_whitelist.json +++ b/app/code/Magento/Reports/etc/db_schema_whitelist.json @@ -1,152 +1,152 @@ { - "report_compared_product_index": { - "column": { - "index_id": true, - "visitor_id": true, - "customer_id": true, - "product_id": true, - "store_id": true, - "added_at": true + "report_compared_product_index": { + "column": { + "index_id": true, + "visitor_id": true, + "customer_id": true, + "product_id": true, + "store_id": true, + "added_at": true + }, + "index": { + "REPORT_COMPARED_PRODUCT_INDEX_STORE_ID": true, + "REPORT_COMPARED_PRODUCT_INDEX_ADDED_AT": true, + "REPORT_COMPARED_PRODUCT_INDEX_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "REPORT_CMPD_PRD_IDX_CSTR_ID_CSTR_ENTT_ENTT_ID": true, + "REPORT_CMPD_PRD_IDX_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "REPORT_COMPARED_PRODUCT_INDEX_STORE_ID_STORE_STORE_ID": true, + "REPORT_COMPARED_PRODUCT_INDEX_VISITOR_ID_PRODUCT_ID": true, + "REPORT_COMPARED_PRODUCT_INDEX_CUSTOMER_ID_PRODUCT_ID": true, + "REPORT_CMPD_PRD_IDX_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true + } }, - "index": { - "REPORT_COMPARED_PRODUCT_INDEX_STORE_ID": true, - "REPORT_COMPARED_PRODUCT_INDEX_ADDED_AT": true, - "REPORT_COMPARED_PRODUCT_INDEX_PRODUCT_ID": true + "report_viewed_product_index": { + "column": { + "index_id": true, + "visitor_id": true, + "customer_id": true, + "product_id": true, + "store_id": true, + "added_at": true + }, + "index": { + "REPORT_VIEWED_PRODUCT_INDEX_STORE_ID": true, + "REPORT_VIEWED_PRODUCT_INDEX_ADDED_AT": true, + "REPORT_VIEWED_PRODUCT_INDEX_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "REPORT_VIEWED_PRD_IDX_CSTR_ID_CSTR_ENTT_ENTT_ID": true, + "REPORT_VIEWED_PRD_IDX_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "REPORT_VIEWED_PRODUCT_INDEX_STORE_ID_STORE_STORE_ID": true, + "REPORT_VIEWED_PRODUCT_INDEX_VISITOR_ID_PRODUCT_ID": true, + "REPORT_VIEWED_PRODUCT_INDEX_CUSTOMER_ID_PRODUCT_ID": true, + "REPORT_VIEWED_PRD_IDX_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true + } }, - "constraint": { - "PRIMARY": true, - "REPORT_CMPD_PRD_IDX_CSTR_ID_CSTR_ENTT_ENTT_ID": true, - "REPORT_CMPD_PRD_IDX_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "REPORT_COMPARED_PRODUCT_INDEX_STORE_ID_STORE_STORE_ID": true, - "REPORT_COMPARED_PRODUCT_INDEX_VISITOR_ID_PRODUCT_ID": true, - "REPORT_COMPARED_PRODUCT_INDEX_CUSTOMER_ID_PRODUCT_ID": true, - "REPORT_CMPD_PRD_IDX_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true - } - }, - "report_viewed_product_index": { - "column": { - "index_id": true, - "visitor_id": true, - "customer_id": true, - "product_id": true, - "store_id": true, - "added_at": true - }, - "index": { - "REPORT_VIEWED_PRODUCT_INDEX_STORE_ID": true, - "REPORT_VIEWED_PRODUCT_INDEX_ADDED_AT": true, - "REPORT_VIEWED_PRODUCT_INDEX_PRODUCT_ID": true + "report_event_types": { + "column": { + "event_type_id": true, + "event_name": true, + "customer_login": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true, - "REPORT_VIEWED_PRD_IDX_CSTR_ID_CSTR_ENTT_ENTT_ID": true, - "REPORT_VIEWED_PRD_IDX_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "REPORT_VIEWED_PRODUCT_INDEX_STORE_ID_STORE_STORE_ID": true, - "REPORT_VIEWED_PRODUCT_INDEX_VISITOR_ID_PRODUCT_ID": true, - "REPORT_VIEWED_PRODUCT_INDEX_CUSTOMER_ID_PRODUCT_ID": true, - "REPORT_VIEWED_PRD_IDX_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true - } - }, - "report_event_types": { - "column": { - "event_type_id": true, - "event_name": true, - "customer_login": true + "report_event": { + "column": { + "event_id": true, + "logged_at": true, + "event_type_id": true, + "object_id": true, + "subject_id": true, + "subtype": true, + "store_id": true + }, + "index": { + "REPORT_EVENT_EVENT_TYPE_ID": true, + "REPORT_EVENT_SUBJECT_ID": true, + "REPORT_EVENT_OBJECT_ID": true, + "REPORT_EVENT_SUBTYPE": true, + "REPORT_EVENT_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "REPORT_EVENT_STORE_ID_STORE_STORE_ID": true, + "REPORT_EVENT_EVENT_TYPE_ID_REPORT_EVENT_TYPES_EVENT_TYPE_ID": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "report_event": { - "column": { - "event_id": true, - "logged_at": true, - "event_type_id": true, - "object_id": true, - "subject_id": true, - "subtype": true, - "store_id": true - }, - "index": { - "REPORT_EVENT_EVENT_TYPE_ID": true, - "REPORT_EVENT_SUBJECT_ID": true, - "REPORT_EVENT_OBJECT_ID": true, - "REPORT_EVENT_SUBTYPE": true, - "REPORT_EVENT_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "REPORT_EVENT_STORE_ID_STORE_STORE_ID": true, - "REPORT_EVENT_EVENT_TYPE_ID_REPORT_EVENT_TYPES_EVENT_TYPE_ID": true - } - }, - "report_viewed_product_aggregated_daily": { - "column": { - "id": true, - "period": true, - "store_id": true, - "product_id": true, - "product_name": true, - "product_price": true, - "views_num": true, - "rating_pos": true - }, - "index": { - "REPORT_VIEWED_PRODUCT_AGGREGATED_DAILY_STORE_ID": true, - "REPORT_VIEWED_PRODUCT_AGGREGATED_DAILY_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "REPORT_VIEWED_PRODUCT_AGGREGATED_DAILY_STORE_ID_STORE_STORE_ID": true, - "REPORT_VIEWED_PRD_AGGRED_DAILY_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "REPORT_VIEWED_PRD_AGGRED_DAILY_PERIOD_STORE_ID_PRD_ID": true, - "REPORT_VIEWED_PRD_AGGRED_DAILY_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true - } - }, - "report_viewed_product_aggregated_monthly": { - "column": { - "id": true, - "period": true, - "store_id": true, - "product_id": true, - "product_name": true, - "product_price": true, - "views_num": true, - "rating_pos": true - }, - "index": { - "REPORT_VIEWED_PRODUCT_AGGREGATED_MONTHLY_STORE_ID": true, - "REPORT_VIEWED_PRODUCT_AGGREGATED_MONTHLY_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "REPORT_VIEWED_PRODUCT_AGGREGATED_MONTHLY_STORE_ID_STORE_STORE_ID": true, - "REPORT_VIEWED_PRD_AGGRED_MONTHLY_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "REPORT_VIEWED_PRD_AGGRED_MONTHLY_PERIOD_STORE_ID_PRD_ID": true, - "FK_0140003A30AFC1A9188D723C4634BA5D": true - } - }, - "report_viewed_product_aggregated_yearly": { - "column": { - "id": true, - "period": true, - "store_id": true, - "product_id": true, - "product_name": true, - "product_price": true, - "views_num": true, - "rating_pos": true + "report_viewed_product_aggregated_daily": { + "column": { + "id": true, + "period": true, + "store_id": true, + "product_id": true, + "product_name": true, + "product_price": true, + "views_num": true, + "rating_pos": true + }, + "index": { + "REPORT_VIEWED_PRODUCT_AGGREGATED_DAILY_STORE_ID": true, + "REPORT_VIEWED_PRODUCT_AGGREGATED_DAILY_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "REPORT_VIEWED_PRODUCT_AGGREGATED_DAILY_STORE_ID_STORE_STORE_ID": true, + "REPORT_VIEWED_PRD_AGGRED_DAILY_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "REPORT_VIEWED_PRD_AGGRED_DAILY_PERIOD_STORE_ID_PRD_ID": true, + "REPORT_VIEWED_PRD_AGGRED_DAILY_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true + } }, - "index": { - "REPORT_VIEWED_PRODUCT_AGGREGATED_YEARLY_STORE_ID": true, - "REPORT_VIEWED_PRODUCT_AGGREGATED_YEARLY_PRODUCT_ID": true + "report_viewed_product_aggregated_monthly": { + "column": { + "id": true, + "period": true, + "store_id": true, + "product_id": true, + "product_name": true, + "product_price": true, + "views_num": true, + "rating_pos": true + }, + "index": { + "REPORT_VIEWED_PRODUCT_AGGREGATED_MONTHLY_STORE_ID": true, + "REPORT_VIEWED_PRODUCT_AGGREGATED_MONTHLY_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "REPORT_VIEWED_PRODUCT_AGGREGATED_MONTHLY_STORE_ID_STORE_STORE_ID": true, + "REPORT_VIEWED_PRD_AGGRED_MONTHLY_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "REPORT_VIEWED_PRD_AGGRED_MONTHLY_PERIOD_STORE_ID_PRD_ID": true, + "FK_0140003A30AFC1A9188D723C4634BA5D": true + } }, - "constraint": { - "PRIMARY": true, - "REPORT_VIEWED_PRODUCT_AGGREGATED_YEARLY_STORE_ID_STORE_STORE_ID": true, - "REPORT_VIEWED_PRD_AGGRED_YEARLY_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, - "REPORT_VIEWED_PRD_AGGRED_YEARLY_PERIOD_STORE_ID_PRD_ID": true, - "REPORT_VIEWED_PRD_AGGRED_YEARLY_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true + "report_viewed_product_aggregated_yearly": { + "column": { + "id": true, + "period": true, + "store_id": true, + "product_id": true, + "product_name": true, + "product_price": true, + "views_num": true, + "rating_pos": true + }, + "index": { + "REPORT_VIEWED_PRODUCT_AGGREGATED_YEARLY_STORE_ID": true, + "REPORT_VIEWED_PRODUCT_AGGREGATED_YEARLY_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "REPORT_VIEWED_PRODUCT_AGGREGATED_YEARLY_STORE_ID_STORE_STORE_ID": true, + "REPORT_VIEWED_PRD_AGGRED_YEARLY_PRD_ID_CAT_PRD_ENTT_ENTT_ID": true, + "REPORT_VIEWED_PRD_AGGRED_YEARLY_PERIOD_STORE_ID_PRD_ID": true, + "REPORT_VIEWED_PRD_AGGRED_YEARLY_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Review/etc/db_schema_whitelist.json b/app/code/Magento/Review/etc/db_schema_whitelist.json index 93ffab116b4da..b7781570c4a3d 100644 --- a/app/code/Magento/Review/etc/db_schema_whitelist.json +++ b/app/code/Magento/Review/etc/db_schema_whitelist.json @@ -1,207 +1,207 @@ { - "review_entity": { - "column": { - "entity_id": true, - "entity_code": true - }, - "constraint": { - "PRIMARY": true - } - }, - "review_status": { - "column": { - "status_id": true, - "status_code": true - }, - "constraint": { - "PRIMARY": true - } - }, - "review": { - "column": { - "review_id": true, - "created_at": true, - "entity_id": true, - "entity_pk_value": true, - "status_id": true - }, - "index": { - "REVIEW_ENTITY_ID": true, - "REVIEW_STATUS_ID": true, - "REVIEW_ENTITY_PK_VALUE": true - }, - "constraint": { - "PRIMARY": true, - "REVIEW_ENTITY_ID_REVIEW_ENTITY_ENTITY_ID": true, - "REVIEW_STATUS_ID_REVIEW_STATUS_STATUS_ID": true - } - }, - "review_detail": { - "column": { - "detail_id": true, - "review_id": true, - "store_id": true, - "title": true, - "detail": true, - "nickname": true, - "customer_id": true - }, - "index": { - "REVIEW_DETAIL_REVIEW_ID": true, - "REVIEW_DETAIL_STORE_ID": true, - "REVIEW_DETAIL_CUSTOMER_ID": true - }, - "constraint": { - "PRIMARY": true, - "REVIEW_DETAIL_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "REVIEW_DETAIL_REVIEW_ID_REVIEW_REVIEW_ID": true, - "REVIEW_DETAIL_STORE_ID_STORE_STORE_ID": true - } - }, - "review_entity_summary": { - "column": { - "primary_id": true, - "entity_pk_value": true, - "entity_type": true, - "reviews_count": true, - "rating_summary": true, - "store_id": true - }, - "index": { - "REVIEW_ENTITY_SUMMARY_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "REVIEW_ENTITY_SUMMARY_STORE_ID_STORE_STORE_ID": true - } - }, - "review_store": { - "column": { - "review_id": true, - "store_id": true - }, - "index": { - "REVIEW_STORE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "REVIEW_STORE_REVIEW_ID_REVIEW_REVIEW_ID": true, - "REVIEW_STORE_STORE_ID_STORE_STORE_ID": true - } - }, - "rating_entity": { - "column": { - "entity_id": true, - "entity_code": true - }, - "constraint": { - "PRIMARY": true, - "RATING_ENTITY_ENTITY_CODE": true - } - }, - "rating": { - "column": { - "rating_id": true, - "entity_id": true, - "rating_code": true, - "position": true, - "is_active": true - }, - "index": { - "RATING_ENTITY_ID": true - }, - "constraint": { - "PRIMARY": true, - "RATING_ENTITY_ID_RATING_ENTITY_ENTITY_ID": true, - "RATING_RATING_CODE": true - } - }, - "rating_option": { - "column": { - "option_id": true, - "rating_id": true, - "code": true, - "value": true, - "position": true - }, - "index": { - "RATING_OPTION_RATING_ID": true - }, - "constraint": { - "PRIMARY": true, - "RATING_OPTION_RATING_ID_RATING_RATING_ID": true - } - }, - "rating_option_vote": { - "column": { - "vote_id": true, - "option_id": true, - "remote_ip": true, - "remote_ip_long": true, - "customer_id": true, - "entity_pk_value": true, - "rating_id": true, - "review_id": true, - "percent": true, - "value": true - }, - "index": { - "RATING_OPTION_VOTE_OPTION_ID": true - }, - "constraint": { - "PRIMARY": true, - "RATING_OPTION_VOTE_OPTION_ID_RATING_OPTION_OPTION_ID": true, - "RATING_OPTION_VOTE_REVIEW_ID_REVIEW_REVIEW_ID": true - } - }, - "rating_option_vote_aggregated": { - "column": { - "primary_id": true, - "rating_id": true, - "entity_pk_value": true, - "vote_count": true, - "vote_value_sum": true, - "percent": true, - "percent_approved": true, - "store_id": true - }, - "index": { - "RATING_OPTION_VOTE_AGGREGATED_RATING_ID": true, - "RATING_OPTION_VOTE_AGGREGATED_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "RATING_OPTION_VOTE_AGGREGATED_RATING_ID_RATING_RATING_ID": true, - "RATING_OPTION_VOTE_AGGREGATED_STORE_ID_STORE_STORE_ID": true - } - }, - "rating_store": { - "column": { - "rating_id": true, - "store_id": true - }, - "index": { - "RATING_STORE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "RATING_STORE_STORE_ID_STORE_STORE_ID": true, - "RATING_STORE_RATING_ID_RATING_RATING_ID": true - } - }, - "rating_title": { - "column": { - "rating_id": true, - "store_id": true, - "value": true - }, - "index": { - "RATING_TITLE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "RATING_TITLE_RATING_ID_RATING_RATING_ID": true, - "RATING_TITLE_STORE_ID_STORE_STORE_ID": true + "review_entity": { + "column": { + "entity_id": true, + "entity_code": true + }, + "constraint": { + "PRIMARY": true + } + }, + "review_status": { + "column": { + "status_id": true, + "status_code": true + }, + "constraint": { + "PRIMARY": true + } + }, + "review": { + "column": { + "review_id": true, + "created_at": true, + "entity_id": true, + "entity_pk_value": true, + "status_id": true + }, + "index": { + "REVIEW_ENTITY_ID": true, + "REVIEW_STATUS_ID": true, + "REVIEW_ENTITY_PK_VALUE": true + }, + "constraint": { + "PRIMARY": true, + "REVIEW_ENTITY_ID_REVIEW_ENTITY_ENTITY_ID": true, + "REVIEW_STATUS_ID_REVIEW_STATUS_STATUS_ID": true + } + }, + "review_detail": { + "column": { + "detail_id": true, + "review_id": true, + "store_id": true, + "title": true, + "detail": true, + "nickname": true, + "customer_id": true + }, + "index": { + "REVIEW_DETAIL_REVIEW_ID": true, + "REVIEW_DETAIL_STORE_ID": true, + "REVIEW_DETAIL_CUSTOMER_ID": true + }, + "constraint": { + "PRIMARY": true, + "REVIEW_DETAIL_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "REVIEW_DETAIL_REVIEW_ID_REVIEW_REVIEW_ID": true, + "REVIEW_DETAIL_STORE_ID_STORE_STORE_ID": true + } + }, + "review_entity_summary": { + "column": { + "primary_id": true, + "entity_pk_value": true, + "entity_type": true, + "reviews_count": true, + "rating_summary": true, + "store_id": true + }, + "index": { + "REVIEW_ENTITY_SUMMARY_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "REVIEW_ENTITY_SUMMARY_STORE_ID_STORE_STORE_ID": true + } + }, + "review_store": { + "column": { + "review_id": true, + "store_id": true + }, + "index": { + "REVIEW_STORE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "REVIEW_STORE_REVIEW_ID_REVIEW_REVIEW_ID": true, + "REVIEW_STORE_STORE_ID_STORE_STORE_ID": true + } + }, + "rating_entity": { + "column": { + "entity_id": true, + "entity_code": true + }, + "constraint": { + "PRIMARY": true, + "RATING_ENTITY_ENTITY_CODE": true + } + }, + "rating": { + "column": { + "rating_id": true, + "entity_id": true, + "rating_code": true, + "position": true, + "is_active": true + }, + "index": { + "RATING_ENTITY_ID": true + }, + "constraint": { + "PRIMARY": true, + "RATING_ENTITY_ID_RATING_ENTITY_ENTITY_ID": true, + "RATING_RATING_CODE": true + } + }, + "rating_option": { + "column": { + "option_id": true, + "rating_id": true, + "code": true, + "value": true, + "position": true + }, + "index": { + "RATING_OPTION_RATING_ID": true + }, + "constraint": { + "PRIMARY": true, + "RATING_OPTION_RATING_ID_RATING_RATING_ID": true + } + }, + "rating_option_vote": { + "column": { + "vote_id": true, + "option_id": true, + "remote_ip": true, + "remote_ip_long": true, + "customer_id": true, + "entity_pk_value": true, + "rating_id": true, + "review_id": true, + "percent": true, + "value": true + }, + "index": { + "RATING_OPTION_VOTE_OPTION_ID": true + }, + "constraint": { + "PRIMARY": true, + "RATING_OPTION_VOTE_OPTION_ID_RATING_OPTION_OPTION_ID": true, + "RATING_OPTION_VOTE_REVIEW_ID_REVIEW_REVIEW_ID": true + } + }, + "rating_option_vote_aggregated": { + "column": { + "primary_id": true, + "rating_id": true, + "entity_pk_value": true, + "vote_count": true, + "vote_value_sum": true, + "percent": true, + "percent_approved": true, + "store_id": true + }, + "index": { + "RATING_OPTION_VOTE_AGGREGATED_RATING_ID": true, + "RATING_OPTION_VOTE_AGGREGATED_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "RATING_OPTION_VOTE_AGGREGATED_RATING_ID_RATING_RATING_ID": true, + "RATING_OPTION_VOTE_AGGREGATED_STORE_ID_STORE_STORE_ID": true + } + }, + "rating_store": { + "column": { + "rating_id": true, + "store_id": true + }, + "index": { + "RATING_STORE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "RATING_STORE_STORE_ID_STORE_STORE_ID": true, + "RATING_STORE_RATING_ID_RATING_RATING_ID": true + } + }, + "rating_title": { + "column": { + "rating_id": true, + "store_id": true, + "value": true + }, + "index": { + "RATING_TITLE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "RATING_TITLE_RATING_ID_RATING_RATING_ID": true, + "RATING_TITLE_STORE_ID_STORE_STORE_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Sales/etc/db_schema_whitelist.json b/app/code/Magento/Sales/etc/db_schema_whitelist.json index 415ee7f0b26c1..8790523c97c21 100644 --- a/app/code/Magento/Sales/etc/db_schema_whitelist.json +++ b/app/code/Magento/Sales/etc/db_schema_whitelist.json @@ -1,1248 +1,1248 @@ { - "sales_order": { - "column": { - "entity_id": true, - "state": true, - "status": true, - "coupon_code": true, - "protect_code": true, - "shipping_description": true, - "is_virtual": true, - "store_id": true, - "customer_id": true, - "base_discount_amount": true, - "base_discount_canceled": true, - "base_discount_invoiced": true, - "base_discount_refunded": true, - "base_grand_total": true, - "base_shipping_amount": true, - "base_shipping_canceled": true, - "base_shipping_invoiced": true, - "base_shipping_refunded": true, - "base_shipping_tax_amount": true, - "base_shipping_tax_refunded": true, - "base_subtotal": true, - "base_subtotal_canceled": true, - "base_subtotal_invoiced": true, - "base_subtotal_refunded": true, - "base_tax_amount": true, - "base_tax_canceled": true, - "base_tax_invoiced": true, - "base_tax_refunded": true, - "base_to_global_rate": true, - "base_to_order_rate": true, - "base_total_canceled": true, - "base_total_invoiced": true, - "base_total_invoiced_cost": true, - "base_total_offline_refunded": true, - "base_total_online_refunded": true, - "base_total_paid": true, - "base_total_qty_ordered": true, - "base_total_refunded": true, - "discount_amount": true, - "discount_canceled": true, - "discount_invoiced": true, - "discount_refunded": true, - "grand_total": true, - "shipping_amount": true, - "shipping_canceled": true, - "shipping_invoiced": true, - "shipping_refunded": true, - "shipping_tax_amount": true, - "shipping_tax_refunded": true, - "store_to_base_rate": true, - "store_to_order_rate": true, - "subtotal": true, - "subtotal_canceled": true, - "subtotal_invoiced": true, - "subtotal_refunded": true, - "tax_amount": true, - "tax_canceled": true, - "tax_invoiced": true, - "tax_refunded": true, - "total_canceled": true, - "total_invoiced": true, - "total_offline_refunded": true, - "total_online_refunded": true, - "total_paid": true, - "total_qty_ordered": true, - "total_refunded": true, - "can_ship_partially": true, - "can_ship_partially_item": true, - "customer_is_guest": true, - "customer_note_notify": true, - "billing_address_id": true, - "customer_group_id": true, - "edit_increment": true, - "email_sent": true, - "send_email": true, - "forced_shipment_with_invoice": true, - "payment_auth_expiration": true, - "quote_address_id": true, - "quote_id": true, - "shipping_address_id": true, - "adjustment_negative": true, - "adjustment_positive": true, - "base_adjustment_negative": true, - "base_adjustment_positive": true, - "base_shipping_discount_amount": true, - "base_subtotal_incl_tax": true, - "base_total_due": true, - "payment_authorization_amount": true, - "shipping_discount_amount": true, - "subtotal_incl_tax": true, - "total_due": true, - "weight": true, - "customer_dob": true, - "increment_id": true, - "applied_rule_ids": true, - "base_currency_code": true, - "customer_email": true, - "customer_firstname": true, - "customer_lastname": true, - "customer_middlename": true, - "customer_prefix": true, - "customer_suffix": true, - "customer_taxvat": true, - "discount_description": true, - "ext_customer_id": true, - "ext_order_id": true, - "global_currency_code": true, - "hold_before_state": true, - "hold_before_status": true, - "order_currency_code": true, - "original_increment_id": true, - "relation_child_id": true, - "relation_child_real_id": true, - "relation_parent_id": true, - "relation_parent_real_id": true, - "remote_ip": true, - "shipping_method": true, - "store_currency_code": true, - "store_name": true, - "x_forwarded_for": true, - "customer_note": true, - "created_at": true, - "updated_at": true, - "total_item_count": true, - "customer_gender": true, - "discount_tax_compensation_amount": true, - "base_discount_tax_compensation_amount": true, - "shipping_discount_tax_compensation_amount": true, - "base_shipping_discount_tax_compensation_amnt": true, - "discount_tax_compensation_invoiced": true, - "base_discount_tax_compensation_invoiced": true, - "discount_tax_compensation_refunded": true, - "base_discount_tax_compensation_refunded": true, - "shipping_incl_tax": true, - "base_shipping_incl_tax": true, - "coupon_rule_name": true - }, - "index": { - "SALES_ORDER_STATUS": true, - "SALES_ORDER_STATE": true, - "SALES_ORDER_STORE_ID": true, - "SALES_ORDER_CREATED_AT": true, - "SALES_ORDER_CUSTOMER_ID": true, - "SALES_ORDER_EXT_ORDER_ID": true, - "SALES_ORDER_QUOTE_ID": true, - "SALES_ORDER_UPDATED_AT": true, - "SALES_ORDER_SEND_EMAIL": true, - "SALES_ORDER_EMAIL_SENT": true - }, - "constraint": { - "PRIMARY": true, - "SALES_ORDER_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "SALES_ORDER_STORE_ID_STORE_STORE_ID": true, - "SALES_ORDER_INCREMENT_ID_STORE_ID": true - } - }, - "sales_order_grid": { - "column": { - "entity_id": true, - "status": true, - "store_id": true, - "store_name": true, - "customer_id": true, - "base_grand_total": true, - "base_total_paid": true, - "grand_total": true, - "total_paid": true, - "increment_id": true, - "base_currency_code": true, - "order_currency_code": true, - "shipping_name": true, - "billing_name": true, - "created_at": true, - "updated_at": true, - "billing_address": true, - "shipping_address": true, - "shipping_information": true, - "customer_email": true, - "customer_group": true, - "subtotal": true, - "shipping_and_handling": true, - "customer_name": true, - "payment_method": true, - "total_refunded": true - }, - "index": { - "SALES_ORDER_GRID_STATUS": true, - "SALES_ORDER_GRID_STORE_ID": true, - "SALES_ORDER_GRID_BASE_GRAND_TOTAL": true, - "SALES_ORDER_GRID_BASE_TOTAL_PAID": true, - "SALES_ORDER_GRID_GRAND_TOTAL": true, - "SALES_ORDER_GRID_TOTAL_PAID": true, - "SALES_ORDER_GRID_SHIPPING_NAME": true, - "SALES_ORDER_GRID_BILLING_NAME": true, - "SALES_ORDER_GRID_CREATED_AT": true, - "SALES_ORDER_GRID_CUSTOMER_ID": true, - "SALES_ORDER_GRID_UPDATED_AT": true, - "FTI_65B9E9925EC58F0C7C2E2F6379C233E7": true - }, - "constraint": { - "PRIMARY": true, - "SALES_ORDER_GRID_INCREMENT_ID_STORE_ID": true - } - }, - "sales_order_address": { - "column": { - "entity_id": true, - "parent_id": true, - "customer_address_id": true, - "quote_address_id": true, - "region_id": true, - "customer_id": true, - "fax": true, - "region": true, - "postcode": true, - "lastname": true, - "street": true, - "city": true, - "email": true, - "telephone": true, - "country_id": true, - "firstname": true, - "address_type": true, - "prefix": true, - "middlename": true, - "suffix": true, - "company": true, - "vat_id": true, - "vat_is_valid": true, - "vat_request_id": true, - "vat_request_date": true, - "vat_request_success": true - }, - "index": { - "SALES_ORDER_ADDRESS_PARENT_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_ORDER_ADDRESS_PARENT_ID_SALES_ORDER_ENTITY_ID": true - } - }, - "sales_order_status_history": { - "column": { - "entity_id": true, - "parent_id": true, - "is_customer_notified": true, - "is_visible_on_front": true, - "comment": true, - "status": true, - "created_at": true, - "entity_name": true - }, - "index": { - "SALES_ORDER_STATUS_HISTORY_PARENT_ID": true, - "SALES_ORDER_STATUS_HISTORY_CREATED_AT": true - }, - "constraint": { - "PRIMARY": true, - "SALES_ORDER_STATUS_HISTORY_PARENT_ID_SALES_ORDER_ENTITY_ID": true - } - }, - "sales_order_item": { - "column": { - "item_id": true, - "order_id": true, - "parent_item_id": true, - "quote_item_id": true, - "store_id": true, - "created_at": true, - "updated_at": true, - "product_id": true, - "product_type": true, - "product_options": true, - "weight": true, - "is_virtual": true, - "sku": true, - "name": true, - "description": true, - "applied_rule_ids": true, - "additional_data": true, - "is_qty_decimal": true, - "no_discount": true, - "qty_backordered": true, - "qty_canceled": true, - "qty_invoiced": true, - "qty_ordered": true, - "qty_refunded": true, - "qty_shipped": true, - "base_cost": true, - "price": true, - "base_price": true, - "original_price": true, - "base_original_price": true, - "tax_percent": true, - "tax_amount": true, - "base_tax_amount": true, - "tax_invoiced": true, - "base_tax_invoiced": true, - "discount_percent": true, - "discount_amount": true, - "base_discount_amount": true, - "discount_invoiced": true, - "base_discount_invoiced": true, - "amount_refunded": true, - "base_amount_refunded": true, - "row_total": true, - "base_row_total": true, - "row_invoiced": true, - "base_row_invoiced": true, - "row_weight": true, - "base_tax_before_discount": true, - "tax_before_discount": true, - "ext_order_item_id": true, - "locked_do_invoice": true, - "locked_do_ship": true, - "price_incl_tax": true, - "base_price_incl_tax": true, - "row_total_incl_tax": true, - "base_row_total_incl_tax": true, - "discount_tax_compensation_amount": true, - "base_discount_tax_compensation_amount": true, - "discount_tax_compensation_invoiced": true, - "base_discount_tax_compensation_invoiced": true, - "discount_tax_compensation_refunded": true, - "base_discount_tax_compensation_refunded": true, - "tax_canceled": true, - "discount_tax_compensation_canceled": true, - "tax_refunded": true, - "base_tax_refunded": true, - "discount_refunded": true, - "base_discount_refunded": true - }, - "index": { - "SALES_ORDER_ITEM_ORDER_ID": true, - "SALES_ORDER_ITEM_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_ORDER_ITEM_ORDER_ID_SALES_ORDER_ENTITY_ID": true, - "SALES_ORDER_ITEM_STORE_ID_STORE_STORE_ID": true - } - }, - "sales_order_payment": { - "column": { - "entity_id": true, - "parent_id": true, - "base_shipping_captured": true, - "shipping_captured": true, - "amount_refunded": true, - "base_amount_paid": true, - "amount_canceled": true, - "base_amount_authorized": true, - "base_amount_paid_online": true, - "base_amount_refunded_online": true, - "base_shipping_amount": true, - "shipping_amount": true, - "amount_paid": true, - "amount_authorized": true, - "base_amount_ordered": true, - "base_shipping_refunded": true, - "shipping_refunded": true, - "base_amount_refunded": true, - "amount_ordered": true, - "base_amount_canceled": true, - "quote_payment_id": true, - "additional_data": true, - "cc_exp_month": true, - "cc_ss_start_year": true, - "echeck_bank_name": true, - "method": true, - "cc_debug_request_body": true, - "cc_secure_verify": true, - "protection_eligibility": true, - "cc_approval": true, - "cc_last_4": true, - "cc_status_description": true, - "echeck_type": true, - "cc_debug_response_serialized": true, - "cc_ss_start_month": true, - "echeck_account_type": true, - "last_trans_id": true, - "cc_cid_status": true, - "cc_owner": true, - "cc_type": true, - "po_number": true, - "cc_exp_year": true, - "cc_status": true, - "echeck_routing_number": true, - "account_status": true, - "anet_trans_method": true, - "cc_debug_response_body": true, - "cc_ss_issue": true, - "echeck_account_name": true, - "cc_avs_status": true, - "cc_number_enc": true, - "cc_trans_id": true, - "address_status": true, - "additional_information": true - }, - "index": { - "SALES_ORDER_PAYMENT_PARENT_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_ORDER_PAYMENT_PARENT_ID_SALES_ORDER_ENTITY_ID": true - } - }, - "sales_shipment": { - "column": { - "entity_id": true, - "store_id": true, - "total_weight": true, - "total_qty": true, - "email_sent": true, - "send_email": true, - "order_id": true, - "customer_id": true, - "shipping_address_id": true, - "billing_address_id": true, - "shipment_status": true, - "increment_id": true, - "created_at": true, - "updated_at": true, - "packages": true, - "shipping_label": true, - "customer_note": true, - "customer_note_notify": true - }, - "index": { - "SALES_SHIPMENT_STORE_ID": true, - "SALES_SHIPMENT_TOTAL_QTY": true, - "SALES_SHIPMENT_ORDER_ID": true, - "SALES_SHIPMENT_CREATED_AT": true, - "SALES_SHIPMENT_UPDATED_AT": true, - "SALES_SHIPMENT_SEND_EMAIL": true, - "SALES_SHIPMENT_EMAIL_SENT": true - }, - "constraint": { - "PRIMARY": true, - "SALES_SHIPMENT_ORDER_ID_SALES_ORDER_ENTITY_ID": true, - "SALES_SHIPMENT_STORE_ID_STORE_STORE_ID": true, - "SALES_SHIPMENT_INCREMENT_ID_STORE_ID": true - } - }, - "sales_shipment_grid": { - "column": { - "entity_id": true, - "increment_id": true, - "store_id": true, - "order_increment_id": true, - "order_id": true, - "order_created_at": true, - "customer_name": true, - "total_qty": true, - "shipment_status": true, - "order_status": true, - "billing_address": true, - "shipping_address": true, - "billing_name": true, - "shipping_name": true, - "customer_email": true, - "customer_group_id": true, - "payment_method": true, - "shipping_information": true, - "created_at": true, - "updated_at": true - }, - "index": { - "SALES_SHIPMENT_GRID_STORE_ID": true, - "SALES_SHIPMENT_GRID_TOTAL_QTY": true, - "SALES_SHIPMENT_GRID_ORDER_INCREMENT_ID": true, - "SALES_SHIPMENT_GRID_SHIPMENT_STATUS": true, - "SALES_SHIPMENT_GRID_ORDER_STATUS": true, - "SALES_SHIPMENT_GRID_CREATED_AT": true, - "SALES_SHIPMENT_GRID_UPDATED_AT": true, - "SALES_SHIPMENT_GRID_ORDER_CREATED_AT": true, - "SALES_SHIPMENT_GRID_SHIPPING_NAME": true, - "SALES_SHIPMENT_GRID_BILLING_NAME": true, - "FTI_086B40C8955F167B8EA76653437879B4": true - }, - "constraint": { - "PRIMARY": true, - "SALES_SHIPMENT_GRID_INCREMENT_ID_STORE_ID": true - } - }, - "sales_shipment_item": { - "column": { - "entity_id": true, - "parent_id": true, - "row_total": true, - "price": true, - "weight": true, - "qty": true, - "product_id": true, - "order_item_id": true, - "additional_data": true, - "description": true, - "name": true, - "sku": true - }, - "index": { - "SALES_SHIPMENT_ITEM_PARENT_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_SHIPMENT_ITEM_PARENT_ID_SALES_SHIPMENT_ENTITY_ID": true - } - }, - "sales_shipment_track": { - "column": { - "entity_id": true, - "parent_id": true, - "weight": true, - "qty": true, - "order_id": true, - "track_number": true, - "description": true, - "title": true, - "carrier_code": true, - "created_at": true, - "updated_at": true - }, - "index": { - "SALES_SHIPMENT_TRACK_PARENT_ID": true, - "SALES_SHIPMENT_TRACK_ORDER_ID": true, - "SALES_SHIPMENT_TRACK_CREATED_AT": true - }, - "constraint": { - "PRIMARY": true, - "SALES_SHIPMENT_TRACK_PARENT_ID_SALES_SHIPMENT_ENTITY_ID": true - } - }, - "sales_shipment_comment": { - "column": { - "entity_id": true, - "parent_id": true, - "is_customer_notified": true, - "is_visible_on_front": true, - "comment": true, - "created_at": true - }, - "index": { - "SALES_SHIPMENT_COMMENT_CREATED_AT": true, - "SALES_SHIPMENT_COMMENT_PARENT_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_SHIPMENT_COMMENT_PARENT_ID_SALES_SHIPMENT_ENTITY_ID": true - } - }, - "sales_invoice": { - "column": { - "entity_id": true, - "store_id": true, - "base_grand_total": true, - "shipping_tax_amount": true, - "tax_amount": true, - "base_tax_amount": true, - "store_to_order_rate": true, - "base_shipping_tax_amount": true, - "base_discount_amount": true, - "base_to_order_rate": true, - "grand_total": true, - "shipping_amount": true, - "subtotal_incl_tax": true, - "base_subtotal_incl_tax": true, - "store_to_base_rate": true, - "base_shipping_amount": true, - "total_qty": true, - "base_to_global_rate": true, - "subtotal": true, - "base_subtotal": true, - "discount_amount": true, - "billing_address_id": true, - "is_used_for_refund": true, - "order_id": true, - "email_sent": true, - "send_email": true, - "can_void_flag": true, - "state": true, - "shipping_address_id": true, - "store_currency_code": true, - "transaction_id": true, - "order_currency_code": true, - "base_currency_code": true, - "global_currency_code": true, - "increment_id": true, - "created_at": true, - "updated_at": true, - "discount_tax_compensation_amount": true, - "base_discount_tax_compensation_amount": true, - "shipping_discount_tax_compensation_amount": true, - "base_shipping_discount_tax_compensation_amnt": true, - "shipping_incl_tax": true, - "base_shipping_incl_tax": true, - "base_total_refunded": true, - "discount_description": true, - "customer_note": true, - "customer_note_notify": true - }, - "index": { - "SALES_INVOICE_STORE_ID": true, - "SALES_INVOICE_GRAND_TOTAL": true, - "SALES_INVOICE_ORDER_ID": true, - "SALES_INVOICE_STATE": true, - "SALES_INVOICE_CREATED_AT": true, - "SALES_INVOICE_UPDATED_AT": true, - "SALES_INVOICE_SEND_EMAIL": true, - "SALES_INVOICE_EMAIL_SENT": true - }, - "constraint": { - "PRIMARY": true, - "SALES_INVOICE_ORDER_ID_SALES_ORDER_ENTITY_ID": true, - "SALES_INVOICE_STORE_ID_STORE_STORE_ID": true, - "SALES_INVOICE_INCREMENT_ID_STORE_ID": true - } - }, - "sales_invoice_grid": { - "column": { - "entity_id": true, - "increment_id": true, - "state": true, - "store_id": true, - "store_name": true, - "order_id": true, - "order_increment_id": true, - "order_created_at": true, - "customer_name": true, - "customer_email": true, - "customer_group_id": true, - "payment_method": true, - "store_currency_code": true, - "order_currency_code": true, - "base_currency_code": true, - "global_currency_code": true, - "billing_name": true, - "billing_address": true, - "shipping_address": true, - "shipping_information": true, - "subtotal": true, - "shipping_and_handling": true, - "grand_total": true, - "created_at": true, - "updated_at": true, - "base_grand_total": true - }, - "index": { - "SALES_INVOICE_GRID_STORE_ID": true, - "SALES_INVOICE_GRID_GRAND_TOTAL": true, - "SALES_INVOICE_GRID_ORDER_ID": true, - "SALES_INVOICE_GRID_STATE": true, - "SALES_INVOICE_GRID_ORDER_INCREMENT_ID": true, - "SALES_INVOICE_GRID_CREATED_AT": true, - "SALES_INVOICE_GRID_UPDATED_AT": true, - "SALES_INVOICE_GRID_ORDER_CREATED_AT": true, - "SALES_INVOICE_GRID_BILLING_NAME": true, - "FTI_95D9C924DD6A8734EB8B5D01D60F90DE": true, - "SALES_INVOICE_GRID_BASE_GRAND_TOTAL": true - }, - "constraint": { - "PRIMARY": true, - "SALES_INVOICE_GRID_INCREMENT_ID_STORE_ID": true - } - }, - "sales_invoice_item": { - "column": { - "entity_id": true, - "parent_id": true, - "base_price": true, - "tax_amount": true, - "base_row_total": true, - "discount_amount": true, - "row_total": true, - "base_discount_amount": true, - "price_incl_tax": true, - "base_tax_amount": true, - "base_price_incl_tax": true, - "qty": true, - "base_cost": true, - "price": true, - "base_row_total_incl_tax": true, - "row_total_incl_tax": true, - "product_id": true, - "order_item_id": true, - "additional_data": true, - "description": true, - "sku": true, - "name": true, - "discount_tax_compensation_amount": true, - "base_discount_tax_compensation_amount": true, - "tax_ratio": true - }, - "index": { - "SALES_INVOICE_ITEM_PARENT_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_INVOICE_ITEM_PARENT_ID_SALES_INVOICE_ENTITY_ID": true - } - }, - "sales_invoice_comment": { - "column": { - "entity_id": true, - "parent_id": true, - "is_customer_notified": true, - "is_visible_on_front": true, - "comment": true, - "created_at": true - }, - "index": { - "SALES_INVOICE_COMMENT_CREATED_AT": true, - "SALES_INVOICE_COMMENT_PARENT_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_INVOICE_COMMENT_PARENT_ID_SALES_INVOICE_ENTITY_ID": true - } - }, - "sales_creditmemo": { - "column": { - "entity_id": true, - "store_id": true, - "adjustment_positive": true, - "base_shipping_tax_amount": true, - "store_to_order_rate": true, - "base_discount_amount": true, - "base_to_order_rate": true, - "grand_total": true, - "base_adjustment_negative": true, - "base_subtotal_incl_tax": true, - "shipping_amount": true, - "subtotal_incl_tax": true, - "adjustment_negative": true, - "base_shipping_amount": true, - "store_to_base_rate": true, - "base_to_global_rate": true, - "base_adjustment": true, - "base_subtotal": true, - "discount_amount": true, - "subtotal": true, - "adjustment": true, - "base_grand_total": true, - "base_adjustment_positive": true, - "base_tax_amount": true, - "shipping_tax_amount": true, - "tax_amount": true, - "order_id": true, - "email_sent": true, - "send_email": true, - "creditmemo_status": true, - "state": true, - "shipping_address_id": true, - "billing_address_id": true, - "invoice_id": true, - "store_currency_code": true, - "order_currency_code": true, - "base_currency_code": true, - "global_currency_code": true, - "transaction_id": true, - "increment_id": true, - "created_at": true, - "updated_at": true, - "discount_tax_compensation_amount": true, - "base_discount_tax_compensation_amount": true, - "shipping_discount_tax_compensation_amount": true, - "base_shipping_discount_tax_compensation_amnt": true, - "shipping_incl_tax": true, - "base_shipping_incl_tax": true, - "discount_description": true, - "customer_note": true, - "customer_note_notify": true - }, - "index": { - "SALES_CREDITMEMO_STORE_ID": true, - "SALES_CREDITMEMO_ORDER_ID": true, - "SALES_CREDITMEMO_CREDITMEMO_STATUS": true, - "SALES_CREDITMEMO_STATE": true, - "SALES_CREDITMEMO_CREATED_AT": true, - "SALES_CREDITMEMO_UPDATED_AT": true, - "SALES_CREDITMEMO_SEND_EMAIL": true, - "SALES_CREDITMEMO_EMAIL_SENT": true - }, - "constraint": { - "PRIMARY": true, - "SALES_CREDITMEMO_ORDER_ID_SALES_ORDER_ENTITY_ID": true, - "SALES_CREDITMEMO_STORE_ID_STORE_STORE_ID": true, - "SALES_CREDITMEMO_INCREMENT_ID_STORE_ID": true - } - }, - "sales_creditmemo_grid": { - "column": { - "entity_id": true, - "increment_id": true, - "created_at": true, - "updated_at": true, - "order_id": true, - "order_increment_id": true, - "order_created_at": true, - "billing_name": true, - "state": true, - "base_grand_total": true, - "order_status": true, - "store_id": true, - "billing_address": true, - "shipping_address": true, - "customer_name": true, - "customer_email": true, - "customer_group_id": true, - "payment_method": true, - "shipping_information": true, - "subtotal": true, - "shipping_and_handling": true, - "adjustment_positive": true, - "adjustment_negative": true, - "order_base_grand_total": true - }, - "index": { - "SALES_CREDITMEMO_GRID_ORDER_INCREMENT_ID": true, - "SALES_CREDITMEMO_GRID_CREATED_AT": true, - "SALES_CREDITMEMO_GRID_UPDATED_AT": true, - "SALES_CREDITMEMO_GRID_ORDER_CREATED_AT": true, - "SALES_CREDITMEMO_GRID_STATE": true, - "SALES_CREDITMEMO_GRID_BILLING_NAME": true, - "SALES_CREDITMEMO_GRID_ORDER_STATUS": true, - "SALES_CREDITMEMO_GRID_BASE_GRAND_TOTAL": true, - "SALES_CREDITMEMO_GRID_STORE_ID": true, - "SALES_CREDITMEMO_GRID_ORDER_BASE_GRAND_TOTAL": true, - "SALES_CREDITMEMO_GRID_ORDER_ID": true, - "FTI_32B7BA885941A8254EE84AE650ABDC86": true - }, - "constraint": { - "PRIMARY": true, - "SALES_CREDITMEMO_GRID_INCREMENT_ID_STORE_ID": true - } - }, - "sales_creditmemo_item": { - "column": { - "entity_id": true, - "parent_id": true, - "base_price": true, - "tax_amount": true, - "base_row_total": true, - "discount_amount": true, - "row_total": true, - "base_discount_amount": true, - "price_incl_tax": true, - "base_tax_amount": true, - "base_price_incl_tax": true, - "qty": true, - "base_cost": true, - "price": true, - "base_row_total_incl_tax": true, - "row_total_incl_tax": true, - "product_id": true, - "order_item_id": true, - "additional_data": true, - "description": true, - "sku": true, - "name": true, - "discount_tax_compensation_amount": true, - "base_discount_tax_compensation_amount": true, - "tax_ratio": true - }, - "index": { - "SALES_CREDITMEMO_ITEM_PARENT_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_CREDITMEMO_ITEM_PARENT_ID_SALES_CREDITMEMO_ENTITY_ID": true - } - }, - "sales_creditmemo_comment": { - "column": { - "entity_id": true, - "parent_id": true, - "is_customer_notified": true, - "is_visible_on_front": true, - "comment": true, - "created_at": true - }, - "index": { - "SALES_CREDITMEMO_COMMENT_CREATED_AT": true, - "SALES_CREDITMEMO_COMMENT_PARENT_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_CREDITMEMO_COMMENT_PARENT_ID_SALES_CREDITMEMO_ENTITY_ID": true - } - }, - "sales_invoiced_aggregated": { - "column": { - "id": true, - "period": true, - "store_id": true, - "order_status": true, - "orders_count": true, - "orders_invoiced": true, - "invoiced": true, - "invoiced_captured": true, - "invoiced_not_captured": true - }, - "index": { - "SALES_INVOICED_AGGREGATED_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_INVOICED_AGGREGATED_STORE_ID_STORE_STORE_ID": true, - "SALES_INVOICED_AGGREGATED_PERIOD_STORE_ID_ORDER_STATUS": true - } - }, - "sales_invoiced_aggregated_order": { - "column": { - "id": true, - "period": true, - "store_id": true, - "order_status": true, - "orders_count": true, - "orders_invoiced": true, - "invoiced": true, - "invoiced_captured": true, - "invoiced_not_captured": true - }, - "index": { - "SALES_INVOICED_AGGREGATED_ORDER_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_INVOICED_AGGREGATED_ORDER_STORE_ID_STORE_STORE_ID": true, - "SALES_INVOICED_AGGREGATED_ORDER_PERIOD_STORE_ID_ORDER_STATUS": true - } - }, - "sales_order_aggregated_created": { - "column": { - "id": true, - "period": true, - "store_id": true, - "order_status": true, - "orders_count": true, - "total_qty_ordered": true, - "total_qty_invoiced": true, - "total_income_amount": true, - "total_revenue_amount": true, - "total_profit_amount": true, - "total_invoiced_amount": true, - "total_canceled_amount": true, - "total_paid_amount": true, - "total_refunded_amount": true, - "total_tax_amount": true, - "total_tax_amount_actual": true, - "total_shipping_amount": true, - "total_shipping_amount_actual": true, - "total_discount_amount": true, - "total_discount_amount_actual": true - }, - "index": { - "SALES_ORDER_AGGREGATED_CREATED_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_ORDER_AGGREGATED_CREATED_STORE_ID_STORE_STORE_ID": true, - "SALES_ORDER_AGGREGATED_CREATED_PERIOD_STORE_ID_ORDER_STATUS": true - } - }, - "sales_order_aggregated_updated": { - "column": { - "id": true, - "period": true, - "store_id": true, - "order_status": true, - "orders_count": true, - "total_qty_ordered": true, - "total_qty_invoiced": true, - "total_income_amount": true, - "total_revenue_amount": true, - "total_profit_amount": true, - "total_invoiced_amount": true, - "total_canceled_amount": true, - "total_paid_amount": true, - "total_refunded_amount": true, - "total_tax_amount": true, - "total_tax_amount_actual": true, - "total_shipping_amount": true, - "total_shipping_amount_actual": true, - "total_discount_amount": true, - "total_discount_amount_actual": true - }, - "index": { - "SALES_ORDER_AGGREGATED_UPDATED_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_ORDER_AGGREGATED_UPDATED_STORE_ID_STORE_STORE_ID": true, - "SALES_ORDER_AGGREGATED_UPDATED_PERIOD_STORE_ID_ORDER_STATUS": true - } - }, - "sales_payment_transaction": { - "column": { - "transaction_id": true, - "parent_id": true, - "order_id": true, - "payment_id": true, - "txn_id": true, - "parent_txn_id": true, - "txn_type": true, - "is_closed": true, - "additional_information": true, - "created_at": true - }, - "index": { - "SALES_PAYMENT_TRANSACTION_PARENT_ID": true, - "SALES_PAYMENT_TRANSACTION_PAYMENT_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_PAYMENT_TRANSACTION_ORDER_ID_SALES_ORDER_ENTITY_ID": true, - "FK_B99FF1A06402D725EBDB0F3A7ECD47A2": true, - "SALES_PAYMENT_TRANSACTION_PAYMENT_ID_SALES_ORDER_PAYMENT_ENTT_ID": true, - "SALES_PAYMENT_TRANSACTION_ORDER_ID_PAYMENT_ID_TXN_ID": true - } - }, - "sales_refunded_aggregated": { - "column": { - "id": true, - "period": true, - "store_id": true, - "order_status": true, - "orders_count": true, - "refunded": true, - "online_refunded": true, - "offline_refunded": true - }, - "index": { - "SALES_REFUNDED_AGGREGATED_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_REFUNDED_AGGREGATED_STORE_ID_STORE_STORE_ID": true, - "SALES_REFUNDED_AGGREGATED_PERIOD_STORE_ID_ORDER_STATUS": true - } - }, - "sales_refunded_aggregated_order": { - "column": { - "id": true, - "period": true, - "store_id": true, - "order_status": true, - "orders_count": true, - "refunded": true, - "online_refunded": true, - "offline_refunded": true - }, - "index": { - "SALES_REFUNDED_AGGREGATED_ORDER_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_REFUNDED_AGGREGATED_ORDER_STORE_ID_STORE_STORE_ID": true, - "SALES_REFUNDED_AGGREGATED_ORDER_PERIOD_STORE_ID_ORDER_STATUS": true - } - }, - "sales_shipping_aggregated": { - "column": { - "id": true, - "period": true, - "store_id": true, - "order_status": true, - "shipping_description": true, - "orders_count": true, - "total_shipping": true, - "total_shipping_actual": true - }, - "index": { - "SALES_SHIPPING_AGGREGATED_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_SHIPPING_AGGREGATED_STORE_ID_STORE_STORE_ID": true, - "SALES_SHPP_AGGRED_PERIOD_STORE_ID_ORDER_STS_SHPP_DESCRIPTION": true - } - }, - "sales_shipping_aggregated_order": { - "column": { - "id": true, - "period": true, - "store_id": true, - "order_status": true, - "shipping_description": true, - "orders_count": true, - "total_shipping": true, - "total_shipping_actual": true - }, - "index": { - "SALES_SHIPPING_AGGREGATED_ORDER_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_SHIPPING_AGGREGATED_ORDER_STORE_ID_STORE_STORE_ID": true, - "UNQ_C05FAE47282EEA68654D0924E946761F": true - } - }, - "sales_bestsellers_aggregated_daily": { - "column": { - "id": true, - "period": true, - "store_id": true, - "product_id": true, - "product_name": true, - "product_price": true, - "qty_ordered": true, - "rating_pos": true - }, - "index": { - "SALES_BESTSELLERS_AGGREGATED_DAILY_STORE_ID": true, - "SALES_BESTSELLERS_AGGREGATED_DAILY_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_BESTSELLERS_AGGREGATED_DAILY_STORE_ID_STORE_STORE_ID": true, - "SALES_BESTSELLERS_AGGREGATED_DAILY_PERIOD_STORE_ID_PRODUCT_ID": true - } - }, - "sales_bestsellers_aggregated_monthly": { - "column": { - "id": true, - "period": true, - "store_id": true, - "product_id": true, - "product_name": true, - "product_price": true, - "qty_ordered": true, - "rating_pos": true - }, - "index": { - "SALES_BESTSELLERS_AGGREGATED_MONTHLY_STORE_ID": true, - "SALES_BESTSELLERS_AGGREGATED_MONTHLY_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_BESTSELLERS_AGGREGATED_MONTHLY_STORE_ID_STORE_STORE_ID": true, - "SALES_BESTSELLERS_AGGREGATED_MONTHLY_PERIOD_STORE_ID_PRODUCT_ID": true - } - }, - "sales_bestsellers_aggregated_yearly": { - "column": { - "id": true, - "period": true, - "store_id": true, - "product_id": true, - "product_name": true, - "product_price": true, - "qty_ordered": true, - "rating_pos": true - }, - "index": { - "SALES_BESTSELLERS_AGGREGATED_YEARLY_STORE_ID": true, - "SALES_BESTSELLERS_AGGREGATED_YEARLY_PRODUCT_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_BESTSELLERS_AGGREGATED_YEARLY_STORE_ID_STORE_STORE_ID": true, - "SALES_BESTSELLERS_AGGREGATED_YEARLY_PERIOD_STORE_ID_PRODUCT_ID": true - } - }, - "sales_order_tax": { - "column": { - "tax_id": true, - "order_id": true, - "code": true, - "title": true, - "percent": true, - "amount": true, - "priority": true, - "position": true, - "base_amount": true, - "process": true, - "base_real_amount": true - }, - "index": { - "SALES_ORDER_TAX_ORDER_ID_PRIORITY_POSITION": true - }, - "constraint": { - "PRIMARY": true - } - }, - "sales_order_tax_item": { - "column": { - "tax_item_id": true, - "tax_id": true, - "item_id": true, - "tax_percent": true, - "amount": true, - "base_amount": true, - "real_amount": true, - "real_base_amount": true, - "associated_item_id": true, - "taxable_item_type": true - }, - "index": { - "SALES_ORDER_TAX_ITEM_ITEM_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_ORDER_TAX_ITEM_ASSOCIATED_ITEM_ID_SALES_ORDER_ITEM_ITEM_ID": true, - "SALES_ORDER_TAX_ITEM_TAX_ID_SALES_ORDER_TAX_TAX_ID": true, - "SALES_ORDER_TAX_ITEM_ITEM_ID_SALES_ORDER_ITEM_ITEM_ID": true, - "SALES_ORDER_TAX_ITEM_TAX_ID_ITEM_ID": true - } - }, - "sales_order_status": { - "column": { - "status": true, - "label": true - }, - "constraint": { - "PRIMARY": true - } - }, - "sales_order_status_state": { - "column": { - "status": true, - "state": true, - "is_default": true, - "visible_on_front": true - }, - "constraint": { - "PRIMARY": true, - "SALES_ORDER_STATUS_STATE_STATUS_SALES_ORDER_STATUS_STATUS": true - } - }, - "sales_order_status_label": { - "column": { - "status": true, - "store_id": true, - "label": true - }, - "index": { - "SALES_ORDER_STATUS_LABEL_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALES_ORDER_STATUS_LABEL_STATUS_SALES_ORDER_STATUS_STATUS": true, - "SALES_ORDER_STATUS_LABEL_STORE_ID_STORE_STORE_ID": true + "sales_order": { + "column": { + "entity_id": true, + "state": true, + "status": true, + "coupon_code": true, + "protect_code": true, + "shipping_description": true, + "is_virtual": true, + "store_id": true, + "customer_id": true, + "base_discount_amount": true, + "base_discount_canceled": true, + "base_discount_invoiced": true, + "base_discount_refunded": true, + "base_grand_total": true, + "base_shipping_amount": true, + "base_shipping_canceled": true, + "base_shipping_invoiced": true, + "base_shipping_refunded": true, + "base_shipping_tax_amount": true, + "base_shipping_tax_refunded": true, + "base_subtotal": true, + "base_subtotal_canceled": true, + "base_subtotal_invoiced": true, + "base_subtotal_refunded": true, + "base_tax_amount": true, + "base_tax_canceled": true, + "base_tax_invoiced": true, + "base_tax_refunded": true, + "base_to_global_rate": true, + "base_to_order_rate": true, + "base_total_canceled": true, + "base_total_invoiced": true, + "base_total_invoiced_cost": true, + "base_total_offline_refunded": true, + "base_total_online_refunded": true, + "base_total_paid": true, + "base_total_qty_ordered": true, + "base_total_refunded": true, + "discount_amount": true, + "discount_canceled": true, + "discount_invoiced": true, + "discount_refunded": true, + "grand_total": true, + "shipping_amount": true, + "shipping_canceled": true, + "shipping_invoiced": true, + "shipping_refunded": true, + "shipping_tax_amount": true, + "shipping_tax_refunded": true, + "store_to_base_rate": true, + "store_to_order_rate": true, + "subtotal": true, + "subtotal_canceled": true, + "subtotal_invoiced": true, + "subtotal_refunded": true, + "tax_amount": true, + "tax_canceled": true, + "tax_invoiced": true, + "tax_refunded": true, + "total_canceled": true, + "total_invoiced": true, + "total_offline_refunded": true, + "total_online_refunded": true, + "total_paid": true, + "total_qty_ordered": true, + "total_refunded": true, + "can_ship_partially": true, + "can_ship_partially_item": true, + "customer_is_guest": true, + "customer_note_notify": true, + "billing_address_id": true, + "customer_group_id": true, + "edit_increment": true, + "email_sent": true, + "send_email": true, + "forced_shipment_with_invoice": true, + "payment_auth_expiration": true, + "quote_address_id": true, + "quote_id": true, + "shipping_address_id": true, + "adjustment_negative": true, + "adjustment_positive": true, + "base_adjustment_negative": true, + "base_adjustment_positive": true, + "base_shipping_discount_amount": true, + "base_subtotal_incl_tax": true, + "base_total_due": true, + "payment_authorization_amount": true, + "shipping_discount_amount": true, + "subtotal_incl_tax": true, + "total_due": true, + "weight": true, + "customer_dob": true, + "increment_id": true, + "applied_rule_ids": true, + "base_currency_code": true, + "customer_email": true, + "customer_firstname": true, + "customer_lastname": true, + "customer_middlename": true, + "customer_prefix": true, + "customer_suffix": true, + "customer_taxvat": true, + "discount_description": true, + "ext_customer_id": true, + "ext_order_id": true, + "global_currency_code": true, + "hold_before_state": true, + "hold_before_status": true, + "order_currency_code": true, + "original_increment_id": true, + "relation_child_id": true, + "relation_child_real_id": true, + "relation_parent_id": true, + "relation_parent_real_id": true, + "remote_ip": true, + "shipping_method": true, + "store_currency_code": true, + "store_name": true, + "x_forwarded_for": true, + "customer_note": true, + "created_at": true, + "updated_at": true, + "total_item_count": true, + "customer_gender": true, + "discount_tax_compensation_amount": true, + "base_discount_tax_compensation_amount": true, + "shipping_discount_tax_compensation_amount": true, + "base_shipping_discount_tax_compensation_amnt": true, + "discount_tax_compensation_invoiced": true, + "base_discount_tax_compensation_invoiced": true, + "discount_tax_compensation_refunded": true, + "base_discount_tax_compensation_refunded": true, + "shipping_incl_tax": true, + "base_shipping_incl_tax": true, + "coupon_rule_name": true + }, + "index": { + "SALES_ORDER_STATUS": true, + "SALES_ORDER_STATE": true, + "SALES_ORDER_STORE_ID": true, + "SALES_ORDER_CREATED_AT": true, + "SALES_ORDER_CUSTOMER_ID": true, + "SALES_ORDER_EXT_ORDER_ID": true, + "SALES_ORDER_QUOTE_ID": true, + "SALES_ORDER_UPDATED_AT": true, + "SALES_ORDER_SEND_EMAIL": true, + "SALES_ORDER_EMAIL_SENT": true + }, + "constraint": { + "PRIMARY": true, + "SALES_ORDER_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "SALES_ORDER_STORE_ID_STORE_STORE_ID": true, + "SALES_ORDER_INCREMENT_ID_STORE_ID": true + } + }, + "sales_order_grid": { + "column": { + "entity_id": true, + "status": true, + "store_id": true, + "store_name": true, + "customer_id": true, + "base_grand_total": true, + "base_total_paid": true, + "grand_total": true, + "total_paid": true, + "increment_id": true, + "base_currency_code": true, + "order_currency_code": true, + "shipping_name": true, + "billing_name": true, + "created_at": true, + "updated_at": true, + "billing_address": true, + "shipping_address": true, + "shipping_information": true, + "customer_email": true, + "customer_group": true, + "subtotal": true, + "shipping_and_handling": true, + "customer_name": true, + "payment_method": true, + "total_refunded": true + }, + "index": { + "SALES_ORDER_GRID_STATUS": true, + "SALES_ORDER_GRID_STORE_ID": true, + "SALES_ORDER_GRID_BASE_GRAND_TOTAL": true, + "SALES_ORDER_GRID_BASE_TOTAL_PAID": true, + "SALES_ORDER_GRID_GRAND_TOTAL": true, + "SALES_ORDER_GRID_TOTAL_PAID": true, + "SALES_ORDER_GRID_SHIPPING_NAME": true, + "SALES_ORDER_GRID_BILLING_NAME": true, + "SALES_ORDER_GRID_CREATED_AT": true, + "SALES_ORDER_GRID_CUSTOMER_ID": true, + "SALES_ORDER_GRID_UPDATED_AT": true, + "FTI_65B9E9925EC58F0C7C2E2F6379C233E7": true + }, + "constraint": { + "PRIMARY": true, + "SALES_ORDER_GRID_INCREMENT_ID_STORE_ID": true + } + }, + "sales_order_address": { + "column": { + "entity_id": true, + "parent_id": true, + "customer_address_id": true, + "quote_address_id": true, + "region_id": true, + "customer_id": true, + "fax": true, + "region": true, + "postcode": true, + "lastname": true, + "street": true, + "city": true, + "email": true, + "telephone": true, + "country_id": true, + "firstname": true, + "address_type": true, + "prefix": true, + "middlename": true, + "suffix": true, + "company": true, + "vat_id": true, + "vat_is_valid": true, + "vat_request_id": true, + "vat_request_date": true, + "vat_request_success": true + }, + "index": { + "SALES_ORDER_ADDRESS_PARENT_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_ORDER_ADDRESS_PARENT_ID_SALES_ORDER_ENTITY_ID": true + } + }, + "sales_order_status_history": { + "column": { + "entity_id": true, + "parent_id": true, + "is_customer_notified": true, + "is_visible_on_front": true, + "comment": true, + "status": true, + "created_at": true, + "entity_name": true + }, + "index": { + "SALES_ORDER_STATUS_HISTORY_PARENT_ID": true, + "SALES_ORDER_STATUS_HISTORY_CREATED_AT": true + }, + "constraint": { + "PRIMARY": true, + "SALES_ORDER_STATUS_HISTORY_PARENT_ID_SALES_ORDER_ENTITY_ID": true + } + }, + "sales_order_item": { + "column": { + "item_id": true, + "order_id": true, + "parent_item_id": true, + "quote_item_id": true, + "store_id": true, + "created_at": true, + "updated_at": true, + "product_id": true, + "product_type": true, + "product_options": true, + "weight": true, + "is_virtual": true, + "sku": true, + "name": true, + "description": true, + "applied_rule_ids": true, + "additional_data": true, + "is_qty_decimal": true, + "no_discount": true, + "qty_backordered": true, + "qty_canceled": true, + "qty_invoiced": true, + "qty_ordered": true, + "qty_refunded": true, + "qty_shipped": true, + "base_cost": true, + "price": true, + "base_price": true, + "original_price": true, + "base_original_price": true, + "tax_percent": true, + "tax_amount": true, + "base_tax_amount": true, + "tax_invoiced": true, + "base_tax_invoiced": true, + "discount_percent": true, + "discount_amount": true, + "base_discount_amount": true, + "discount_invoiced": true, + "base_discount_invoiced": true, + "amount_refunded": true, + "base_amount_refunded": true, + "row_total": true, + "base_row_total": true, + "row_invoiced": true, + "base_row_invoiced": true, + "row_weight": true, + "base_tax_before_discount": true, + "tax_before_discount": true, + "ext_order_item_id": true, + "locked_do_invoice": true, + "locked_do_ship": true, + "price_incl_tax": true, + "base_price_incl_tax": true, + "row_total_incl_tax": true, + "base_row_total_incl_tax": true, + "discount_tax_compensation_amount": true, + "base_discount_tax_compensation_amount": true, + "discount_tax_compensation_invoiced": true, + "base_discount_tax_compensation_invoiced": true, + "discount_tax_compensation_refunded": true, + "base_discount_tax_compensation_refunded": true, + "tax_canceled": true, + "discount_tax_compensation_canceled": true, + "tax_refunded": true, + "base_tax_refunded": true, + "discount_refunded": true, + "base_discount_refunded": true + }, + "index": { + "SALES_ORDER_ITEM_ORDER_ID": true, + "SALES_ORDER_ITEM_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_ORDER_ITEM_ORDER_ID_SALES_ORDER_ENTITY_ID": true, + "SALES_ORDER_ITEM_STORE_ID_STORE_STORE_ID": true + } + }, + "sales_order_payment": { + "column": { + "entity_id": true, + "parent_id": true, + "base_shipping_captured": true, + "shipping_captured": true, + "amount_refunded": true, + "base_amount_paid": true, + "amount_canceled": true, + "base_amount_authorized": true, + "base_amount_paid_online": true, + "base_amount_refunded_online": true, + "base_shipping_amount": true, + "shipping_amount": true, + "amount_paid": true, + "amount_authorized": true, + "base_amount_ordered": true, + "base_shipping_refunded": true, + "shipping_refunded": true, + "base_amount_refunded": true, + "amount_ordered": true, + "base_amount_canceled": true, + "quote_payment_id": true, + "additional_data": true, + "cc_exp_month": true, + "cc_ss_start_year": true, + "echeck_bank_name": true, + "method": true, + "cc_debug_request_body": true, + "cc_secure_verify": true, + "protection_eligibility": true, + "cc_approval": true, + "cc_last_4": true, + "cc_status_description": true, + "echeck_type": true, + "cc_debug_response_serialized": true, + "cc_ss_start_month": true, + "echeck_account_type": true, + "last_trans_id": true, + "cc_cid_status": true, + "cc_owner": true, + "cc_type": true, + "po_number": true, + "cc_exp_year": true, + "cc_status": true, + "echeck_routing_number": true, + "account_status": true, + "anet_trans_method": true, + "cc_debug_response_body": true, + "cc_ss_issue": true, + "echeck_account_name": true, + "cc_avs_status": true, + "cc_number_enc": true, + "cc_trans_id": true, + "address_status": true, + "additional_information": true + }, + "index": { + "SALES_ORDER_PAYMENT_PARENT_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_ORDER_PAYMENT_PARENT_ID_SALES_ORDER_ENTITY_ID": true + } + }, + "sales_shipment": { + "column": { + "entity_id": true, + "store_id": true, + "total_weight": true, + "total_qty": true, + "email_sent": true, + "send_email": true, + "order_id": true, + "customer_id": true, + "shipping_address_id": true, + "billing_address_id": true, + "shipment_status": true, + "increment_id": true, + "created_at": true, + "updated_at": true, + "packages": true, + "shipping_label": true, + "customer_note": true, + "customer_note_notify": true + }, + "index": { + "SALES_SHIPMENT_STORE_ID": true, + "SALES_SHIPMENT_TOTAL_QTY": true, + "SALES_SHIPMENT_ORDER_ID": true, + "SALES_SHIPMENT_CREATED_AT": true, + "SALES_SHIPMENT_UPDATED_AT": true, + "SALES_SHIPMENT_SEND_EMAIL": true, + "SALES_SHIPMENT_EMAIL_SENT": true + }, + "constraint": { + "PRIMARY": true, + "SALES_SHIPMENT_ORDER_ID_SALES_ORDER_ENTITY_ID": true, + "SALES_SHIPMENT_STORE_ID_STORE_STORE_ID": true, + "SALES_SHIPMENT_INCREMENT_ID_STORE_ID": true + } + }, + "sales_shipment_grid": { + "column": { + "entity_id": true, + "increment_id": true, + "store_id": true, + "order_increment_id": true, + "order_id": true, + "order_created_at": true, + "customer_name": true, + "total_qty": true, + "shipment_status": true, + "order_status": true, + "billing_address": true, + "shipping_address": true, + "billing_name": true, + "shipping_name": true, + "customer_email": true, + "customer_group_id": true, + "payment_method": true, + "shipping_information": true, + "created_at": true, + "updated_at": true + }, + "index": { + "SALES_SHIPMENT_GRID_STORE_ID": true, + "SALES_SHIPMENT_GRID_TOTAL_QTY": true, + "SALES_SHIPMENT_GRID_ORDER_INCREMENT_ID": true, + "SALES_SHIPMENT_GRID_SHIPMENT_STATUS": true, + "SALES_SHIPMENT_GRID_ORDER_STATUS": true, + "SALES_SHIPMENT_GRID_CREATED_AT": true, + "SALES_SHIPMENT_GRID_UPDATED_AT": true, + "SALES_SHIPMENT_GRID_ORDER_CREATED_AT": true, + "SALES_SHIPMENT_GRID_SHIPPING_NAME": true, + "SALES_SHIPMENT_GRID_BILLING_NAME": true, + "FTI_086B40C8955F167B8EA76653437879B4": true + }, + "constraint": { + "PRIMARY": true, + "SALES_SHIPMENT_GRID_INCREMENT_ID_STORE_ID": true + } + }, + "sales_shipment_item": { + "column": { + "entity_id": true, + "parent_id": true, + "row_total": true, + "price": true, + "weight": true, + "qty": true, + "product_id": true, + "order_item_id": true, + "additional_data": true, + "description": true, + "name": true, + "sku": true + }, + "index": { + "SALES_SHIPMENT_ITEM_PARENT_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_SHIPMENT_ITEM_PARENT_ID_SALES_SHIPMENT_ENTITY_ID": true + } + }, + "sales_shipment_track": { + "column": { + "entity_id": true, + "parent_id": true, + "weight": true, + "qty": true, + "order_id": true, + "track_number": true, + "description": true, + "title": true, + "carrier_code": true, + "created_at": true, + "updated_at": true + }, + "index": { + "SALES_SHIPMENT_TRACK_PARENT_ID": true, + "SALES_SHIPMENT_TRACK_ORDER_ID": true, + "SALES_SHIPMENT_TRACK_CREATED_AT": true + }, + "constraint": { + "PRIMARY": true, + "SALES_SHIPMENT_TRACK_PARENT_ID_SALES_SHIPMENT_ENTITY_ID": true + } + }, + "sales_shipment_comment": { + "column": { + "entity_id": true, + "parent_id": true, + "is_customer_notified": true, + "is_visible_on_front": true, + "comment": true, + "created_at": true + }, + "index": { + "SALES_SHIPMENT_COMMENT_CREATED_AT": true, + "SALES_SHIPMENT_COMMENT_PARENT_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_SHIPMENT_COMMENT_PARENT_ID_SALES_SHIPMENT_ENTITY_ID": true + } + }, + "sales_invoice": { + "column": { + "entity_id": true, + "store_id": true, + "base_grand_total": true, + "shipping_tax_amount": true, + "tax_amount": true, + "base_tax_amount": true, + "store_to_order_rate": true, + "base_shipping_tax_amount": true, + "base_discount_amount": true, + "base_to_order_rate": true, + "grand_total": true, + "shipping_amount": true, + "subtotal_incl_tax": true, + "base_subtotal_incl_tax": true, + "store_to_base_rate": true, + "base_shipping_amount": true, + "total_qty": true, + "base_to_global_rate": true, + "subtotal": true, + "base_subtotal": true, + "discount_amount": true, + "billing_address_id": true, + "is_used_for_refund": true, + "order_id": true, + "email_sent": true, + "send_email": true, + "can_void_flag": true, + "state": true, + "shipping_address_id": true, + "store_currency_code": true, + "transaction_id": true, + "order_currency_code": true, + "base_currency_code": true, + "global_currency_code": true, + "increment_id": true, + "created_at": true, + "updated_at": true, + "discount_tax_compensation_amount": true, + "base_discount_tax_compensation_amount": true, + "shipping_discount_tax_compensation_amount": true, + "base_shipping_discount_tax_compensation_amnt": true, + "shipping_incl_tax": true, + "base_shipping_incl_tax": true, + "base_total_refunded": true, + "discount_description": true, + "customer_note": true, + "customer_note_notify": true + }, + "index": { + "SALES_INVOICE_STORE_ID": true, + "SALES_INVOICE_GRAND_TOTAL": true, + "SALES_INVOICE_ORDER_ID": true, + "SALES_INVOICE_STATE": true, + "SALES_INVOICE_CREATED_AT": true, + "SALES_INVOICE_UPDATED_AT": true, + "SALES_INVOICE_SEND_EMAIL": true, + "SALES_INVOICE_EMAIL_SENT": true + }, + "constraint": { + "PRIMARY": true, + "SALES_INVOICE_ORDER_ID_SALES_ORDER_ENTITY_ID": true, + "SALES_INVOICE_STORE_ID_STORE_STORE_ID": true, + "SALES_INVOICE_INCREMENT_ID_STORE_ID": true + } + }, + "sales_invoice_grid": { + "column": { + "entity_id": true, + "increment_id": true, + "state": true, + "store_id": true, + "store_name": true, + "order_id": true, + "order_increment_id": true, + "order_created_at": true, + "customer_name": true, + "customer_email": true, + "customer_group_id": true, + "payment_method": true, + "store_currency_code": true, + "order_currency_code": true, + "base_currency_code": true, + "global_currency_code": true, + "billing_name": true, + "billing_address": true, + "shipping_address": true, + "shipping_information": true, + "subtotal": true, + "shipping_and_handling": true, + "grand_total": true, + "created_at": true, + "updated_at": true, + "base_grand_total": true + }, + "index": { + "SALES_INVOICE_GRID_STORE_ID": true, + "SALES_INVOICE_GRID_GRAND_TOTAL": true, + "SALES_INVOICE_GRID_ORDER_ID": true, + "SALES_INVOICE_GRID_STATE": true, + "SALES_INVOICE_GRID_ORDER_INCREMENT_ID": true, + "SALES_INVOICE_GRID_CREATED_AT": true, + "SALES_INVOICE_GRID_UPDATED_AT": true, + "SALES_INVOICE_GRID_ORDER_CREATED_AT": true, + "SALES_INVOICE_GRID_BILLING_NAME": true, + "FTI_95D9C924DD6A8734EB8B5D01D60F90DE": true, + "SALES_INVOICE_GRID_BASE_GRAND_TOTAL": true + }, + "constraint": { + "PRIMARY": true, + "SALES_INVOICE_GRID_INCREMENT_ID_STORE_ID": true + } + }, + "sales_invoice_item": { + "column": { + "entity_id": true, + "parent_id": true, + "base_price": true, + "tax_amount": true, + "base_row_total": true, + "discount_amount": true, + "row_total": true, + "base_discount_amount": true, + "price_incl_tax": true, + "base_tax_amount": true, + "base_price_incl_tax": true, + "qty": true, + "base_cost": true, + "price": true, + "base_row_total_incl_tax": true, + "row_total_incl_tax": true, + "product_id": true, + "order_item_id": true, + "additional_data": true, + "description": true, + "sku": true, + "name": true, + "discount_tax_compensation_amount": true, + "base_discount_tax_compensation_amount": true, + "tax_ratio": true + }, + "index": { + "SALES_INVOICE_ITEM_PARENT_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_INVOICE_ITEM_PARENT_ID_SALES_INVOICE_ENTITY_ID": true + } + }, + "sales_invoice_comment": { + "column": { + "entity_id": true, + "parent_id": true, + "is_customer_notified": true, + "is_visible_on_front": true, + "comment": true, + "created_at": true + }, + "index": { + "SALES_INVOICE_COMMENT_CREATED_AT": true, + "SALES_INVOICE_COMMENT_PARENT_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_INVOICE_COMMENT_PARENT_ID_SALES_INVOICE_ENTITY_ID": true + } + }, + "sales_creditmemo": { + "column": { + "entity_id": true, + "store_id": true, + "adjustment_positive": true, + "base_shipping_tax_amount": true, + "store_to_order_rate": true, + "base_discount_amount": true, + "base_to_order_rate": true, + "grand_total": true, + "base_adjustment_negative": true, + "base_subtotal_incl_tax": true, + "shipping_amount": true, + "subtotal_incl_tax": true, + "adjustment_negative": true, + "base_shipping_amount": true, + "store_to_base_rate": true, + "base_to_global_rate": true, + "base_adjustment": true, + "base_subtotal": true, + "discount_amount": true, + "subtotal": true, + "adjustment": true, + "base_grand_total": true, + "base_adjustment_positive": true, + "base_tax_amount": true, + "shipping_tax_amount": true, + "tax_amount": true, + "order_id": true, + "email_sent": true, + "send_email": true, + "creditmemo_status": true, + "state": true, + "shipping_address_id": true, + "billing_address_id": true, + "invoice_id": true, + "store_currency_code": true, + "order_currency_code": true, + "base_currency_code": true, + "global_currency_code": true, + "transaction_id": true, + "increment_id": true, + "created_at": true, + "updated_at": true, + "discount_tax_compensation_amount": true, + "base_discount_tax_compensation_amount": true, + "shipping_discount_tax_compensation_amount": true, + "base_shipping_discount_tax_compensation_amnt": true, + "shipping_incl_tax": true, + "base_shipping_incl_tax": true, + "discount_description": true, + "customer_note": true, + "customer_note_notify": true + }, + "index": { + "SALES_CREDITMEMO_STORE_ID": true, + "SALES_CREDITMEMO_ORDER_ID": true, + "SALES_CREDITMEMO_CREDITMEMO_STATUS": true, + "SALES_CREDITMEMO_STATE": true, + "SALES_CREDITMEMO_CREATED_AT": true, + "SALES_CREDITMEMO_UPDATED_AT": true, + "SALES_CREDITMEMO_SEND_EMAIL": true, + "SALES_CREDITMEMO_EMAIL_SENT": true + }, + "constraint": { + "PRIMARY": true, + "SALES_CREDITMEMO_ORDER_ID_SALES_ORDER_ENTITY_ID": true, + "SALES_CREDITMEMO_STORE_ID_STORE_STORE_ID": true, + "SALES_CREDITMEMO_INCREMENT_ID_STORE_ID": true + } + }, + "sales_creditmemo_grid": { + "column": { + "entity_id": true, + "increment_id": true, + "created_at": true, + "updated_at": true, + "order_id": true, + "order_increment_id": true, + "order_created_at": true, + "billing_name": true, + "state": true, + "base_grand_total": true, + "order_status": true, + "store_id": true, + "billing_address": true, + "shipping_address": true, + "customer_name": true, + "customer_email": true, + "customer_group_id": true, + "payment_method": true, + "shipping_information": true, + "subtotal": true, + "shipping_and_handling": true, + "adjustment_positive": true, + "adjustment_negative": true, + "order_base_grand_total": true + }, + "index": { + "SALES_CREDITMEMO_GRID_ORDER_INCREMENT_ID": true, + "SALES_CREDITMEMO_GRID_CREATED_AT": true, + "SALES_CREDITMEMO_GRID_UPDATED_AT": true, + "SALES_CREDITMEMO_GRID_ORDER_CREATED_AT": true, + "SALES_CREDITMEMO_GRID_STATE": true, + "SALES_CREDITMEMO_GRID_BILLING_NAME": true, + "SALES_CREDITMEMO_GRID_ORDER_STATUS": true, + "SALES_CREDITMEMO_GRID_BASE_GRAND_TOTAL": true, + "SALES_CREDITMEMO_GRID_STORE_ID": true, + "SALES_CREDITMEMO_GRID_ORDER_BASE_GRAND_TOTAL": true, + "SALES_CREDITMEMO_GRID_ORDER_ID": true, + "FTI_32B7BA885941A8254EE84AE650ABDC86": true + }, + "constraint": { + "PRIMARY": true, + "SALES_CREDITMEMO_GRID_INCREMENT_ID_STORE_ID": true + } + }, + "sales_creditmemo_item": { + "column": { + "entity_id": true, + "parent_id": true, + "base_price": true, + "tax_amount": true, + "base_row_total": true, + "discount_amount": true, + "row_total": true, + "base_discount_amount": true, + "price_incl_tax": true, + "base_tax_amount": true, + "base_price_incl_tax": true, + "qty": true, + "base_cost": true, + "price": true, + "base_row_total_incl_tax": true, + "row_total_incl_tax": true, + "product_id": true, + "order_item_id": true, + "additional_data": true, + "description": true, + "sku": true, + "name": true, + "discount_tax_compensation_amount": true, + "base_discount_tax_compensation_amount": true, + "tax_ratio": true + }, + "index": { + "SALES_CREDITMEMO_ITEM_PARENT_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_CREDITMEMO_ITEM_PARENT_ID_SALES_CREDITMEMO_ENTITY_ID": true + } + }, + "sales_creditmemo_comment": { + "column": { + "entity_id": true, + "parent_id": true, + "is_customer_notified": true, + "is_visible_on_front": true, + "comment": true, + "created_at": true + }, + "index": { + "SALES_CREDITMEMO_COMMENT_CREATED_AT": true, + "SALES_CREDITMEMO_COMMENT_PARENT_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_CREDITMEMO_COMMENT_PARENT_ID_SALES_CREDITMEMO_ENTITY_ID": true + } + }, + "sales_invoiced_aggregated": { + "column": { + "id": true, + "period": true, + "store_id": true, + "order_status": true, + "orders_count": true, + "orders_invoiced": true, + "invoiced": true, + "invoiced_captured": true, + "invoiced_not_captured": true + }, + "index": { + "SALES_INVOICED_AGGREGATED_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_INVOICED_AGGREGATED_STORE_ID_STORE_STORE_ID": true, + "SALES_INVOICED_AGGREGATED_PERIOD_STORE_ID_ORDER_STATUS": true + } + }, + "sales_invoiced_aggregated_order": { + "column": { + "id": true, + "period": true, + "store_id": true, + "order_status": true, + "orders_count": true, + "orders_invoiced": true, + "invoiced": true, + "invoiced_captured": true, + "invoiced_not_captured": true + }, + "index": { + "SALES_INVOICED_AGGREGATED_ORDER_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_INVOICED_AGGREGATED_ORDER_STORE_ID_STORE_STORE_ID": true, + "SALES_INVOICED_AGGREGATED_ORDER_PERIOD_STORE_ID_ORDER_STATUS": true + } + }, + "sales_order_aggregated_created": { + "column": { + "id": true, + "period": true, + "store_id": true, + "order_status": true, + "orders_count": true, + "total_qty_ordered": true, + "total_qty_invoiced": true, + "total_income_amount": true, + "total_revenue_amount": true, + "total_profit_amount": true, + "total_invoiced_amount": true, + "total_canceled_amount": true, + "total_paid_amount": true, + "total_refunded_amount": true, + "total_tax_amount": true, + "total_tax_amount_actual": true, + "total_shipping_amount": true, + "total_shipping_amount_actual": true, + "total_discount_amount": true, + "total_discount_amount_actual": true + }, + "index": { + "SALES_ORDER_AGGREGATED_CREATED_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_ORDER_AGGREGATED_CREATED_STORE_ID_STORE_STORE_ID": true, + "SALES_ORDER_AGGREGATED_CREATED_PERIOD_STORE_ID_ORDER_STATUS": true + } + }, + "sales_order_aggregated_updated": { + "column": { + "id": true, + "period": true, + "store_id": true, + "order_status": true, + "orders_count": true, + "total_qty_ordered": true, + "total_qty_invoiced": true, + "total_income_amount": true, + "total_revenue_amount": true, + "total_profit_amount": true, + "total_invoiced_amount": true, + "total_canceled_amount": true, + "total_paid_amount": true, + "total_refunded_amount": true, + "total_tax_amount": true, + "total_tax_amount_actual": true, + "total_shipping_amount": true, + "total_shipping_amount_actual": true, + "total_discount_amount": true, + "total_discount_amount_actual": true + }, + "index": { + "SALES_ORDER_AGGREGATED_UPDATED_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_ORDER_AGGREGATED_UPDATED_STORE_ID_STORE_STORE_ID": true, + "SALES_ORDER_AGGREGATED_UPDATED_PERIOD_STORE_ID_ORDER_STATUS": true + } + }, + "sales_payment_transaction": { + "column": { + "transaction_id": true, + "parent_id": true, + "order_id": true, + "payment_id": true, + "txn_id": true, + "parent_txn_id": true, + "txn_type": true, + "is_closed": true, + "additional_information": true, + "created_at": true + }, + "index": { + "SALES_PAYMENT_TRANSACTION_PARENT_ID": true, + "SALES_PAYMENT_TRANSACTION_PAYMENT_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_PAYMENT_TRANSACTION_ORDER_ID_SALES_ORDER_ENTITY_ID": true, + "FK_B99FF1A06402D725EBDB0F3A7ECD47A2": true, + "SALES_PAYMENT_TRANSACTION_PAYMENT_ID_SALES_ORDER_PAYMENT_ENTT_ID": true, + "SALES_PAYMENT_TRANSACTION_ORDER_ID_PAYMENT_ID_TXN_ID": true + } + }, + "sales_refunded_aggregated": { + "column": { + "id": true, + "period": true, + "store_id": true, + "order_status": true, + "orders_count": true, + "refunded": true, + "online_refunded": true, + "offline_refunded": true + }, + "index": { + "SALES_REFUNDED_AGGREGATED_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_REFUNDED_AGGREGATED_STORE_ID_STORE_STORE_ID": true, + "SALES_REFUNDED_AGGREGATED_PERIOD_STORE_ID_ORDER_STATUS": true + } + }, + "sales_refunded_aggregated_order": { + "column": { + "id": true, + "period": true, + "store_id": true, + "order_status": true, + "orders_count": true, + "refunded": true, + "online_refunded": true, + "offline_refunded": true + }, + "index": { + "SALES_REFUNDED_AGGREGATED_ORDER_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_REFUNDED_AGGREGATED_ORDER_STORE_ID_STORE_STORE_ID": true, + "SALES_REFUNDED_AGGREGATED_ORDER_PERIOD_STORE_ID_ORDER_STATUS": true + } + }, + "sales_shipping_aggregated": { + "column": { + "id": true, + "period": true, + "store_id": true, + "order_status": true, + "shipping_description": true, + "orders_count": true, + "total_shipping": true, + "total_shipping_actual": true + }, + "index": { + "SALES_SHIPPING_AGGREGATED_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_SHIPPING_AGGREGATED_STORE_ID_STORE_STORE_ID": true, + "SALES_SHPP_AGGRED_PERIOD_STORE_ID_ORDER_STS_SHPP_DESCRIPTION": true + } + }, + "sales_shipping_aggregated_order": { + "column": { + "id": true, + "period": true, + "store_id": true, + "order_status": true, + "shipping_description": true, + "orders_count": true, + "total_shipping": true, + "total_shipping_actual": true + }, + "index": { + "SALES_SHIPPING_AGGREGATED_ORDER_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_SHIPPING_AGGREGATED_ORDER_STORE_ID_STORE_STORE_ID": true, + "UNQ_C05FAE47282EEA68654D0924E946761F": true + } + }, + "sales_bestsellers_aggregated_daily": { + "column": { + "id": true, + "period": true, + "store_id": true, + "product_id": true, + "product_name": true, + "product_price": true, + "qty_ordered": true, + "rating_pos": true + }, + "index": { + "SALES_BESTSELLERS_AGGREGATED_DAILY_STORE_ID": true, + "SALES_BESTSELLERS_AGGREGATED_DAILY_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_BESTSELLERS_AGGREGATED_DAILY_STORE_ID_STORE_STORE_ID": true, + "SALES_BESTSELLERS_AGGREGATED_DAILY_PERIOD_STORE_ID_PRODUCT_ID": true + } + }, + "sales_bestsellers_aggregated_monthly": { + "column": { + "id": true, + "period": true, + "store_id": true, + "product_id": true, + "product_name": true, + "product_price": true, + "qty_ordered": true, + "rating_pos": true + }, + "index": { + "SALES_BESTSELLERS_AGGREGATED_MONTHLY_STORE_ID": true, + "SALES_BESTSELLERS_AGGREGATED_MONTHLY_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_BESTSELLERS_AGGREGATED_MONTHLY_STORE_ID_STORE_STORE_ID": true, + "SALES_BESTSELLERS_AGGREGATED_MONTHLY_PERIOD_STORE_ID_PRODUCT_ID": true + } + }, + "sales_bestsellers_aggregated_yearly": { + "column": { + "id": true, + "period": true, + "store_id": true, + "product_id": true, + "product_name": true, + "product_price": true, + "qty_ordered": true, + "rating_pos": true + }, + "index": { + "SALES_BESTSELLERS_AGGREGATED_YEARLY_STORE_ID": true, + "SALES_BESTSELLERS_AGGREGATED_YEARLY_PRODUCT_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_BESTSELLERS_AGGREGATED_YEARLY_STORE_ID_STORE_STORE_ID": true, + "SALES_BESTSELLERS_AGGREGATED_YEARLY_PERIOD_STORE_ID_PRODUCT_ID": true + } + }, + "sales_order_tax": { + "column": { + "tax_id": true, + "order_id": true, + "code": true, + "title": true, + "percent": true, + "amount": true, + "priority": true, + "position": true, + "base_amount": true, + "process": true, + "base_real_amount": true + }, + "index": { + "SALES_ORDER_TAX_ORDER_ID_PRIORITY_POSITION": true + }, + "constraint": { + "PRIMARY": true + } + }, + "sales_order_tax_item": { + "column": { + "tax_item_id": true, + "tax_id": true, + "item_id": true, + "tax_percent": true, + "amount": true, + "base_amount": true, + "real_amount": true, + "real_base_amount": true, + "associated_item_id": true, + "taxable_item_type": true + }, + "index": { + "SALES_ORDER_TAX_ITEM_ITEM_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_ORDER_TAX_ITEM_ASSOCIATED_ITEM_ID_SALES_ORDER_ITEM_ITEM_ID": true, + "SALES_ORDER_TAX_ITEM_TAX_ID_SALES_ORDER_TAX_TAX_ID": true, + "SALES_ORDER_TAX_ITEM_ITEM_ID_SALES_ORDER_ITEM_ITEM_ID": true, + "SALES_ORDER_TAX_ITEM_TAX_ID_ITEM_ID": true + } + }, + "sales_order_status": { + "column": { + "status": true, + "label": true + }, + "constraint": { + "PRIMARY": true + } + }, + "sales_order_status_state": { + "column": { + "status": true, + "state": true, + "is_default": true, + "visible_on_front": true + }, + "constraint": { + "PRIMARY": true, + "SALES_ORDER_STATUS_STATE_STATUS_SALES_ORDER_STATUS_STATUS": true + } + }, + "sales_order_status_label": { + "column": { + "status": true, + "store_id": true, + "label": true + }, + "index": { + "SALES_ORDER_STATUS_LABEL_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALES_ORDER_STATUS_LABEL_STATUS_SALES_ORDER_STATUS_STATUS": true, + "SALES_ORDER_STATUS_LABEL_STORE_ID_STORE_STORE_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/SalesRule/etc/db_schema_whitelist.json b/app/code/Magento/SalesRule/etc/db_schema_whitelist.json index e72a0c687331a..f05591b276231 100644 --- a/app/code/Magento/SalesRule/etc/db_schema_whitelist.json +++ b/app/code/Magento/SalesRule/etc/db_schema_whitelist.json @@ -1,230 +1,230 @@ { - "salesrule": { - "column": { - "rule_id": true, - "name": true, - "description": true, - "from_date": true, - "to_date": true, - "uses_per_customer": true, - "is_active": true, - "conditions_serialized": true, - "actions_serialized": true, - "stop_rules_processing": true, - "is_advanced": true, - "product_ids": true, - "sort_order": true, - "simple_action": true, - "discount_amount": true, - "discount_qty": true, - "discount_step": true, - "apply_to_shipping": true, - "times_used": true, - "is_rss": true, - "coupon_type": true, - "use_auto_generation": true, - "uses_per_coupon": true - }, - "index": { - "SALESRULE_IS_ACTIVE_SORT_ORDER_TO_DATE_FROM_DATE": true - }, - "constraint": { - "PRIMARY": true - } - }, - "salesrule_coupon": { - "column": { - "coupon_id": true, - "rule_id": true, - "code": true, - "usage_limit": true, - "usage_per_customer": true, - "times_used": true, - "expiration_date": true, - "is_primary": true, - "created_at": true, - "type": true - }, - "index": { - "SALESRULE_COUPON_RULE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALESRULE_COUPON_RULE_ID_SALESRULE_RULE_ID": true, - "SALESRULE_COUPON_CODE": true, - "SALESRULE_COUPON_RULE_ID_IS_PRIMARY": true - } - }, - "salesrule_coupon_usage": { - "column": { - "coupon_id": true, - "customer_id": true, - "times_used": true - }, - "index": { - "SALESRULE_COUPON_USAGE_CUSTOMER_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALESRULE_COUPON_USAGE_COUPON_ID_SALESRULE_COUPON_COUPON_ID": true, - "SALESRULE_COUPON_USAGE_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true - } - }, - "salesrule_customer": { - "column": { - "rule_customer_id": true, - "rule_id": true, - "customer_id": true, - "times_used": true - }, - "index": { - "SALESRULE_CUSTOMER_RULE_ID_CUSTOMER_ID": true, - "SALESRULE_CUSTOMER_CUSTOMER_ID_RULE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALESRULE_CUSTOMER_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "SALESRULE_CUSTOMER_RULE_ID_SALESRULE_RULE_ID": true - } - }, - "salesrule_label": { - "column": { - "label_id": true, - "rule_id": true, - "store_id": true, - "label": true - }, - "index": { - "SALESRULE_LABEL_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALESRULE_LABEL_RULE_ID_SALESRULE_RULE_ID": true, - "SALESRULE_LABEL_STORE_ID_STORE_STORE_ID": true, - "SALESRULE_LABEL_RULE_ID_STORE_ID": true - } - }, - "salesrule_product_attribute": { - "column": { - "rule_id": true, - "website_id": true, - "customer_group_id": true, - "attribute_id": true - }, - "index": { - "SALESRULE_PRODUCT_ATTRIBUTE_WEBSITE_ID": true, - "SALESRULE_PRODUCT_ATTRIBUTE_CUSTOMER_GROUP_ID": true, - "SALESRULE_PRODUCT_ATTRIBUTE_ATTRIBUTE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALESRULE_PRD_ATTR_ATTR_ID_EAV_ATTR_ATTR_ID": true, - "SALESRULE_PRD_ATTR_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true, - "SALESRULE_PRODUCT_ATTRIBUTE_RULE_ID_SALESRULE_RULE_ID": true, - "SALESRULE_PRODUCT_ATTRIBUTE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true - } - }, - "salesrule_coupon_aggregated": { - "column": { - "id": true, - "period": true, - "store_id": true, - "order_status": true, - "coupon_code": true, - "coupon_uses": true, - "subtotal_amount": true, - "discount_amount": true, - "total_amount": true, - "subtotal_amount_actual": true, - "discount_amount_actual": true, - "total_amount_actual": true, - "rule_name": true - }, - "index": { - "SALESRULE_COUPON_AGGREGATED_STORE_ID": true, - "SALESRULE_COUPON_AGGREGATED_RULE_NAME": true - }, - "constraint": { - "PRIMARY": true, - "SALESRULE_COUPON_AGGREGATED_STORE_ID_STORE_STORE_ID": true, - "SALESRULE_COUPON_AGGRED_PERIOD_STORE_ID_ORDER_STS_COUPON_CODE": true - } - }, - "salesrule_coupon_aggregated_updated": { - "column": { - "id": true, - "period": true, - "store_id": true, - "order_status": true, - "coupon_code": true, - "coupon_uses": true, - "subtotal_amount": true, - "discount_amount": true, - "total_amount": true, - "subtotal_amount_actual": true, - "discount_amount_actual": true, - "total_amount_actual": true, - "rule_name": true - }, - "index": { - "SALESRULE_COUPON_AGGREGATED_UPDATED_STORE_ID": true, - "SALESRULE_COUPON_AGGREGATED_UPDATED_RULE_NAME": true - }, - "constraint": { - "PRIMARY": true, - "SALESRULE_COUPON_AGGREGATED_UPDATED_STORE_ID_STORE_STORE_ID": true, - "UNQ_7196FA120A4F0F84E1B66605E87E213E": true - } - }, - "salesrule_coupon_aggregated_order": { - "column": { - "id": true, - "period": true, - "store_id": true, - "order_status": true, - "coupon_code": true, - "coupon_uses": true, - "subtotal_amount": true, - "discount_amount": true, - "total_amount": true, - "rule_name": true - }, - "index": { - "SALESRULE_COUPON_AGGREGATED_ORDER_STORE_ID": true, - "SALESRULE_COUPON_AGGREGATED_ORDER_RULE_NAME": true - }, - "constraint": { - "PRIMARY": true, - "SALESRULE_COUPON_AGGREGATED_ORDER_STORE_ID_STORE_STORE_ID": true, - "UNQ_1094D1FBBCBB11704A29DEF3ACC37D2B": true - } - }, - "salesrule_website": { - "column": { - "rule_id": true, - "website_id": true - }, - "index": { - "SALESRULE_WEBSITE_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALESRULE_WEBSITE_RULE_ID_SALESRULE_RULE_ID": true, - "SALESRULE_WEBSITE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true - } - }, - "salesrule_customer_group": { - "column": { - "rule_id": true, - "customer_group_id": true - }, - "index": { - "SALESRULE_CUSTOMER_GROUP_CUSTOMER_GROUP_ID": true - }, - "constraint": { - "PRIMARY": true, - "SALESRULE_CUSTOMER_GROUP_RULE_ID_SALESRULE_RULE_ID": true, - "SALESRULE_CSTR_GROUP_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true + "salesrule": { + "column": { + "rule_id": true, + "name": true, + "description": true, + "from_date": true, + "to_date": true, + "uses_per_customer": true, + "is_active": true, + "conditions_serialized": true, + "actions_serialized": true, + "stop_rules_processing": true, + "is_advanced": true, + "product_ids": true, + "sort_order": true, + "simple_action": true, + "discount_amount": true, + "discount_qty": true, + "discount_step": true, + "apply_to_shipping": true, + "times_used": true, + "is_rss": true, + "coupon_type": true, + "use_auto_generation": true, + "uses_per_coupon": true + }, + "index": { + "SALESRULE_IS_ACTIVE_SORT_ORDER_TO_DATE_FROM_DATE": true + }, + "constraint": { + "PRIMARY": true + } + }, + "salesrule_coupon": { + "column": { + "coupon_id": true, + "rule_id": true, + "code": true, + "usage_limit": true, + "usage_per_customer": true, + "times_used": true, + "expiration_date": true, + "is_primary": true, + "created_at": true, + "type": true + }, + "index": { + "SALESRULE_COUPON_RULE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALESRULE_COUPON_RULE_ID_SALESRULE_RULE_ID": true, + "SALESRULE_COUPON_CODE": true, + "SALESRULE_COUPON_RULE_ID_IS_PRIMARY": true + } + }, + "salesrule_coupon_usage": { + "column": { + "coupon_id": true, + "customer_id": true, + "times_used": true + }, + "index": { + "SALESRULE_COUPON_USAGE_CUSTOMER_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALESRULE_COUPON_USAGE_COUPON_ID_SALESRULE_COUPON_COUPON_ID": true, + "SALESRULE_COUPON_USAGE_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true + } + }, + "salesrule_customer": { + "column": { + "rule_customer_id": true, + "rule_id": true, + "customer_id": true, + "times_used": true + }, + "index": { + "SALESRULE_CUSTOMER_RULE_ID_CUSTOMER_ID": true, + "SALESRULE_CUSTOMER_CUSTOMER_ID_RULE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALESRULE_CUSTOMER_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "SALESRULE_CUSTOMER_RULE_ID_SALESRULE_RULE_ID": true + } + }, + "salesrule_label": { + "column": { + "label_id": true, + "rule_id": true, + "store_id": true, + "label": true + }, + "index": { + "SALESRULE_LABEL_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALESRULE_LABEL_RULE_ID_SALESRULE_RULE_ID": true, + "SALESRULE_LABEL_STORE_ID_STORE_STORE_ID": true, + "SALESRULE_LABEL_RULE_ID_STORE_ID": true + } + }, + "salesrule_product_attribute": { + "column": { + "rule_id": true, + "website_id": true, + "customer_group_id": true, + "attribute_id": true + }, + "index": { + "SALESRULE_PRODUCT_ATTRIBUTE_WEBSITE_ID": true, + "SALESRULE_PRODUCT_ATTRIBUTE_CUSTOMER_GROUP_ID": true, + "SALESRULE_PRODUCT_ATTRIBUTE_ATTRIBUTE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALESRULE_PRD_ATTR_ATTR_ID_EAV_ATTR_ATTR_ID": true, + "SALESRULE_PRD_ATTR_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true, + "SALESRULE_PRODUCT_ATTRIBUTE_RULE_ID_SALESRULE_RULE_ID": true, + "SALESRULE_PRODUCT_ATTRIBUTE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true + } + }, + "salesrule_coupon_aggregated": { + "column": { + "id": true, + "period": true, + "store_id": true, + "order_status": true, + "coupon_code": true, + "coupon_uses": true, + "subtotal_amount": true, + "discount_amount": true, + "total_amount": true, + "subtotal_amount_actual": true, + "discount_amount_actual": true, + "total_amount_actual": true, + "rule_name": true + }, + "index": { + "SALESRULE_COUPON_AGGREGATED_STORE_ID": true, + "SALESRULE_COUPON_AGGREGATED_RULE_NAME": true + }, + "constraint": { + "PRIMARY": true, + "SALESRULE_COUPON_AGGREGATED_STORE_ID_STORE_STORE_ID": true, + "SALESRULE_COUPON_AGGRED_PERIOD_STORE_ID_ORDER_STS_COUPON_CODE": true + } + }, + "salesrule_coupon_aggregated_updated": { + "column": { + "id": true, + "period": true, + "store_id": true, + "order_status": true, + "coupon_code": true, + "coupon_uses": true, + "subtotal_amount": true, + "discount_amount": true, + "total_amount": true, + "subtotal_amount_actual": true, + "discount_amount_actual": true, + "total_amount_actual": true, + "rule_name": true + }, + "index": { + "SALESRULE_COUPON_AGGREGATED_UPDATED_STORE_ID": true, + "SALESRULE_COUPON_AGGREGATED_UPDATED_RULE_NAME": true + }, + "constraint": { + "PRIMARY": true, + "SALESRULE_COUPON_AGGREGATED_UPDATED_STORE_ID_STORE_STORE_ID": true, + "UNQ_7196FA120A4F0F84E1B66605E87E213E": true + } + }, + "salesrule_coupon_aggregated_order": { + "column": { + "id": true, + "period": true, + "store_id": true, + "order_status": true, + "coupon_code": true, + "coupon_uses": true, + "subtotal_amount": true, + "discount_amount": true, + "total_amount": true, + "rule_name": true + }, + "index": { + "SALESRULE_COUPON_AGGREGATED_ORDER_STORE_ID": true, + "SALESRULE_COUPON_AGGREGATED_ORDER_RULE_NAME": true + }, + "constraint": { + "PRIMARY": true, + "SALESRULE_COUPON_AGGREGATED_ORDER_STORE_ID_STORE_STORE_ID": true, + "UNQ_1094D1FBBCBB11704A29DEF3ACC37D2B": true + } + }, + "salesrule_website": { + "column": { + "rule_id": true, + "website_id": true + }, + "index": { + "SALESRULE_WEBSITE_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALESRULE_WEBSITE_RULE_ID_SALESRULE_RULE_ID": true, + "SALESRULE_WEBSITE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true + } + }, + "salesrule_customer_group": { + "column": { + "rule_id": true, + "customer_group_id": true + }, + "index": { + "SALESRULE_CUSTOMER_GROUP_CUSTOMER_GROUP_ID": true + }, + "constraint": { + "PRIMARY": true, + "SALESRULE_CUSTOMER_GROUP_RULE_ID_SALESRULE_RULE_ID": true, + "SALESRULE_CSTR_GROUP_CSTR_GROUP_ID_CSTR_GROUP_CSTR_GROUP_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/SalesSequence/etc/db_schema_whitelist.json b/app/code/Magento/SalesSequence/etc/db_schema_whitelist.json index 37a5d715ab024..17fec04e24467 100644 --- a/app/code/Magento/SalesSequence/etc/db_schema_whitelist.json +++ b/app/code/Magento/SalesSequence/etc/db_schema_whitelist.json @@ -1,32 +1,32 @@ { - "sales_sequence_profile": { - "column": { - "profile_id": true, - "meta_id": true, - "prefix": true, - "suffix": true, - "start_value": true, - "step": true, - "max_value": true, - "warning_value": true, - "is_active": true + "sales_sequence_profile": { + "column": { + "profile_id": true, + "meta_id": true, + "prefix": true, + "suffix": true, + "start_value": true, + "step": true, + "max_value": true, + "warning_value": true, + "is_active": true + }, + "constraint": { + "PRIMARY": true, + "SALES_SEQUENCE_PROFILE_META_ID_SALES_SEQUENCE_META_META_ID": true, + "SALES_SEQUENCE_PROFILE_META_ID_PREFIX_SUFFIX": true + } }, - "constraint": { - "PRIMARY": true, - "SALES_SEQUENCE_PROFILE_META_ID_SALES_SEQUENCE_META_META_ID": true, - "SALES_SEQUENCE_PROFILE_META_ID_PREFIX_SUFFIX": true + "sales_sequence_meta": { + "column": { + "meta_id": true, + "entity_type": true, + "store_id": true, + "sequence_table": true + }, + "constraint": { + "PRIMARY": true, + "SALES_SEQUENCE_META_ENTITY_TYPE_STORE_ID": true + } } - }, - "sales_sequence_meta": { - "column": { - "meta_id": true, - "entity_type": true, - "store_id": true, - "sequence_table": true - }, - "constraint": { - "PRIMARY": true, - "SALES_SEQUENCE_META_ENTITY_TYPE_STORE_ID": true - } - } } \ No newline at end of file diff --git a/app/code/Magento/Search/etc/db_schema_whitelist.json b/app/code/Magento/Search/etc/db_schema_whitelist.json index 72b417b976862..71adbc68887d0 100644 --- a/app/code/Magento/Search/etc/db_schema_whitelist.json +++ b/app/code/Magento/Search/etc/db_schema_whitelist.json @@ -1,46 +1,46 @@ { - "search_query": { - "column": { - "query_id": true, - "query_text": true, - "num_results": true, - "popularity": true, - "redirect": true, - "synonym_for": true, - "store_id": true, - "display_in_terms": true, - "is_active": true, - "is_processed": true, - "updated_at": true + "search_query": { + "column": { + "query_id": true, + "query_text": true, + "num_results": true, + "popularity": true, + "redirect": true, + "synonym_for": true, + "store_id": true, + "display_in_terms": true, + "is_active": true, + "is_processed": true, + "updated_at": true + }, + "index": { + "SEARCH_QUERY_QUERY_TEXT_STORE_ID_POPULARITY": true, + "SEARCH_QUERY_STORE_ID": true, + "SEARCH_QUERY_IS_PROCESSED": true, + "SEARCH_QUERY_SYNONYM_FOR": true + }, + "constraint": { + "PRIMARY": true, + "SEARCH_QUERY_STORE_ID_STORE_STORE_ID": true, + "SEARCH_QUERY_QUERY_TEXT_STORE_ID": true + } }, - "index": { - "SEARCH_QUERY_QUERY_TEXT_STORE_ID_POPULARITY": true, - "SEARCH_QUERY_STORE_ID": true, - "SEARCH_QUERY_IS_PROCESSED": true, - "SEARCH_QUERY_SYNONYM_FOR": true - }, - "constraint": { - "PRIMARY": true, - "SEARCH_QUERY_STORE_ID_STORE_STORE_ID": true, - "SEARCH_QUERY_QUERY_TEXT_STORE_ID": true - } - }, - "search_synonyms": { - "column": { - "group_id": true, - "synonyms": true, - "store_id": true, - "website_id": true - }, - "index": { - "SEARCH_SYNONYMS_SYNONYMS": true, - "SEARCH_SYNONYMS_STORE_ID": true, - "SEARCH_SYNONYMS_WEBSITE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SEARCH_SYNONYMS_STORE_ID_STORE_STORE_ID": true, - "SEARCH_SYNONYMS_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true + "search_synonyms": { + "column": { + "group_id": true, + "synonyms": true, + "store_id": true, + "website_id": true + }, + "index": { + "SEARCH_SYNONYMS_SYNONYMS": true, + "SEARCH_SYNONYMS_STORE_ID": true, + "SEARCH_SYNONYMS_WEBSITE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SEARCH_SYNONYMS_STORE_ID_STORE_STORE_ID": true, + "SEARCH_SYNONYMS_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Security/etc/db_schema_whitelist.json b/app/code/Magento/Security/etc/db_schema_whitelist.json index c9115aed6fb82..c387b7591c7a5 100644 --- a/app/code/Magento/Security/etc/db_schema_whitelist.json +++ b/app/code/Magento/Security/etc/db_schema_whitelist.json @@ -1,37 +1,37 @@ { - "admin_user_session": { - "column": { - "id": true, - "session_id": true, - "user_id": true, - "status": true, - "created_at": true, - "updated_at": true, - "ip": true + "admin_user_session": { + "column": { + "id": true, + "session_id": true, + "user_id": true, + "status": true, + "created_at": true, + "updated_at": true, + "ip": true + }, + "index": { + "ADMIN_USER_SESSION_SESSION_ID": true, + "ADMIN_USER_SESSION_USER_ID": true + }, + "constraint": { + "PRIMARY": true, + "ADMIN_USER_SESSION_USER_ID_ADMIN_USER_USER_ID": true + } }, - "index": { - "ADMIN_USER_SESSION_SESSION_ID": true, - "ADMIN_USER_SESSION_USER_ID": true - }, - "constraint": { - "PRIMARY": true, - "ADMIN_USER_SESSION_USER_ID_ADMIN_USER_USER_ID": true - } - }, - "password_reset_request_event": { - "column": { - "id": true, - "request_type": true, - "account_reference": true, - "created_at": true, - "ip": true - }, - "index": { - "PASSWORD_RESET_REQUEST_EVENT_ACCOUNT_REFERENCE": true, - "PASSWORD_RESET_REQUEST_EVENT_CREATED_AT": true - }, - "constraint": { - "PRIMARY": true + "password_reset_request_event": { + "column": { + "id": true, + "request_type": true, + "account_reference": true, + "created_at": true, + "ip": true + }, + "index": { + "PASSWORD_RESET_REQUEST_EVENT_ACCOUNT_REFERENCE": true, + "PASSWORD_RESET_REQUEST_EVENT_CREATED_AT": true + }, + "constraint": { + "PRIMARY": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/SendFriend/etc/db_schema_whitelist.json b/app/code/Magento/SendFriend/etc/db_schema_whitelist.json index eebb79747432c..5cab97e6dfb1c 100644 --- a/app/code/Magento/SendFriend/etc/db_schema_whitelist.json +++ b/app/code/Magento/SendFriend/etc/db_schema_whitelist.json @@ -1,17 +1,17 @@ { - "sendfriend_log": { - "column": { - "log_id": true, - "ip": true, - "time": true, - "website_id": true - }, - "index": { - "SENDFRIEND_LOG_IP": true, - "SENDFRIEND_LOG_TIME": true - }, - "constraint": { - "PRIMARY": true + "sendfriend_log": { + "column": { + "log_id": true, + "ip": true, + "time": true, + "website_id": true + }, + "index": { + "SENDFRIEND_LOG_IP": true, + "SENDFRIEND_LOG_TIME": true + }, + "constraint": { + "PRIMARY": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Signifyd/etc/db_schema_whitelist.json b/app/code/Magento/Signifyd/etc/db_schema_whitelist.json index 31f753dbb3b39..69d164b23d9e7 100644 --- a/app/code/Magento/Signifyd/etc/db_schema_whitelist.json +++ b/app/code/Magento/Signifyd/etc/db_schema_whitelist.json @@ -1,28 +1,28 @@ { - "signifyd_case": { - "column": { - "entity_id": true, - "order_id": true, - "case_id": true, - "guarantee_eligible": true, - "guarantee_disposition": true, - "status": true, - "score": true, - "associated_team": true, - "review_disposition": true, - "created_at": true, - "updated_at": true + "signifyd_case": { + "column": { + "entity_id": true, + "order_id": true, + "case_id": true, + "guarantee_eligible": true, + "guarantee_disposition": true, + "status": true, + "score": true, + "associated_team": true, + "review_disposition": true, + "created_at": true, + "updated_at": true + }, + "constraint": { + "PRIMARY": true, + "SIGNIFYD_CASE_ORDER_ID_SALES_ORDER_ENTITY_ID": true, + "SIGNIFYD_CASE_ORDER_ID": true, + "SIGNIFYD_CASE_CASE_ID": true + } }, - "constraint": { - "PRIMARY": true, - "SIGNIFYD_CASE_ORDER_ID_SALES_ORDER_ENTITY_ID": true, - "SIGNIFYD_CASE_ORDER_ID": true, - "SIGNIFYD_CASE_CASE_ID": true + "sales_order_grid": { + "column": { + "signifyd_guarantee_status": true + } } - }, - "sales_order_grid": { - "column": { - "signifyd_guarantee_status": true - } - } } \ No newline at end of file diff --git a/app/code/Magento/Sitemap/etc/db_schema_whitelist.json b/app/code/Magento/Sitemap/etc/db_schema_whitelist.json index b7067f773b93f..b6359e7526a0f 100644 --- a/app/code/Magento/Sitemap/etc/db_schema_whitelist.json +++ b/app/code/Magento/Sitemap/etc/db_schema_whitelist.json @@ -1,19 +1,19 @@ { - "sitemap": { - "column": { - "sitemap_id": true, - "sitemap_type": true, - "sitemap_filename": true, - "sitemap_path": true, - "sitemap_time": true, - "store_id": true - }, - "index": { - "SITEMAP_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "SITEMAP_STORE_ID_STORE_STORE_ID": true + "sitemap": { + "column": { + "sitemap_id": true, + "sitemap_type": true, + "sitemap_filename": true, + "sitemap_path": true, + "sitemap_time": true, + "store_id": true + }, + "index": { + "SITEMAP_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "SITEMAP_STORE_ID_STORE_STORE_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Store/etc/db_schema_whitelist.json b/app/code/Magento/Store/etc/db_schema_whitelist.json index 5fa120333ff12..24ed35c361efb 100644 --- a/app/code/Magento/Store/etc/db_schema_whitelist.json +++ b/app/code/Magento/Store/etc/db_schema_whitelist.json @@ -1,61 +1,61 @@ { - "store_website": { - "column": { - "website_id": true, - "code": true, - "name": true, - "sort_order": true, - "default_group_id": true, - "is_default": true + "store_website": { + "column": { + "website_id": true, + "code": true, + "name": true, + "sort_order": true, + "default_group_id": true, + "is_default": true + }, + "index": { + "STORE_WEBSITE_SORT_ORDER": true, + "STORE_WEBSITE_DEFAULT_GROUP_ID": true + }, + "constraint": { + "PRIMARY": true, + "STORE_WEBSITE_CODE": true + } }, - "index": { - "STORE_WEBSITE_SORT_ORDER": true, - "STORE_WEBSITE_DEFAULT_GROUP_ID": true + "store_group": { + "column": { + "group_id": true, + "website_id": true, + "name": true, + "root_category_id": true, + "default_store_id": true, + "code": true + }, + "index": { + "STORE_GROUP_WEBSITE_ID": true, + "STORE_GROUP_DEFAULT_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "STORE_GROUP_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, + "STORE_GROUP_CODE": true + } }, - "constraint": { - "PRIMARY": true, - "STORE_WEBSITE_CODE": true + "store": { + "column": { + "store_id": true, + "code": true, + "website_id": true, + "group_id": true, + "name": true, + "sort_order": true, + "is_active": true + }, + "index": { + "STORE_WEBSITE_ID": true, + "STORE_IS_ACTIVE_SORT_ORDER": true, + "STORE_GROUP_ID": true + }, + "constraint": { + "PRIMARY": true, + "STORE_GROUP_ID_STORE_GROUP_GROUP_ID": true, + "STORE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, + "STORE_CODE": true + } } - }, - "store_group": { - "column": { - "group_id": true, - "website_id": true, - "name": true, - "root_category_id": true, - "default_store_id": true, - "code": true - }, - "index": { - "STORE_GROUP_WEBSITE_ID": true, - "STORE_GROUP_DEFAULT_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "STORE_GROUP_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, - "STORE_GROUP_CODE": true - } - }, - "store": { - "column": { - "store_id": true, - "code": true, - "website_id": true, - "group_id": true, - "name": true, - "sort_order": true, - "is_active": true - }, - "index": { - "STORE_WEBSITE_ID": true, - "STORE_IS_ACTIVE_SORT_ORDER": true, - "STORE_GROUP_ID": true - }, - "constraint": { - "PRIMARY": true, - "STORE_GROUP_ID_STORE_GROUP_GROUP_ID": true, - "STORE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, - "STORE_CODE": true - } - } } \ No newline at end of file diff --git a/app/code/Magento/Swatches/etc/db_schema_whitelist.json b/app/code/Magento/Swatches/etc/db_schema_whitelist.json index 8f335442def21..e1b8e4c7e1798 100644 --- a/app/code/Magento/Swatches/etc/db_schema_whitelist.json +++ b/app/code/Magento/Swatches/etc/db_schema_whitelist.json @@ -1,25 +1,25 @@ { - "catalog_eav_attribute": { - "column": { - "additional_data": true - } - }, - "eav_attribute_option_swatch": { - "column": { - "swatch_id": true, - "option_id": true, - "store_id": true, - "type": true, - "value": true - }, - "index": { - "EAV_ATTRIBUTE_OPTION_SWATCH_SWATCH_ID": true + "catalog_eav_attribute": { + "column": { + "additional_data": true + } }, - "constraint": { - "PRIMARY": true, - "EAV_ATTRIBUTE_OPTION_SWATCH_STORE_ID_STORE_STORE_ID": true, - "EAV_ATTR_OPT_SWATCH_OPT_ID_EAV_ATTR_OPT_OPT_ID": true, - "EAV_ATTRIBUTE_OPTION_SWATCH_STORE_ID_OPTION_ID": true + "eav_attribute_option_swatch": { + "column": { + "swatch_id": true, + "option_id": true, + "store_id": true, + "type": true, + "value": true + }, + "index": { + "EAV_ATTRIBUTE_OPTION_SWATCH_SWATCH_ID": true + }, + "constraint": { + "PRIMARY": true, + "EAV_ATTRIBUTE_OPTION_SWATCH_STORE_ID_STORE_STORE_ID": true, + "EAV_ATTR_OPT_SWATCH_OPT_ID_EAV_ATTR_OPT_OPT_ID": true, + "EAV_ATTRIBUTE_OPTION_SWATCH_STORE_ID_OPTION_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Tax/etc/db_schema_whitelist.json b/app/code/Magento/Tax/etc/db_schema_whitelist.json index 4e9731abce738..36a1dee1b455a 100644 --- a/app/code/Magento/Tax/etc/db_schema_whitelist.json +++ b/app/code/Magento/Tax/etc/db_schema_whitelist.json @@ -1,128 +1,128 @@ { - "tax_class": { - "column": { - "class_id": true, - "class_name": true, - "class_type": true + "tax_class": { + "column": { + "class_id": true, + "class_name": true, + "class_type": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "tax_calculation_rule": { - "column": { - "tax_calculation_rule_id": true, - "code": true, - "priority": true, - "position": true, - "calculate_subtotal": true - }, - "index": { - "TAX_CALCULATION_RULE_PRIORITY_POSITION": true, - "TAX_CALCULATION_RULE_CODE": true + "tax_calculation_rule": { + "column": { + "tax_calculation_rule_id": true, + "code": true, + "priority": true, + "position": true, + "calculate_subtotal": true + }, + "index": { + "TAX_CALCULATION_RULE_PRIORITY_POSITION": true, + "TAX_CALCULATION_RULE_CODE": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "tax_calculation_rate": { - "column": { - "tax_calculation_rate_id": true, - "tax_country_id": true, - "tax_region_id": true, - "tax_postcode": true, - "code": true, - "rate": true, - "zip_is_range": true, - "zip_from": true, - "zip_to": true + "tax_calculation_rate": { + "column": { + "tax_calculation_rate_id": true, + "tax_country_id": true, + "tax_region_id": true, + "tax_postcode": true, + "code": true, + "rate": true, + "zip_is_range": true, + "zip_from": true, + "zip_to": true + }, + "index": { + "TAX_CALCULATION_RATE_TAX_COUNTRY_ID_TAX_REGION_ID_TAX_POSTCODE": true, + "TAX_CALCULATION_RATE_CODE": true, + "IDX_CA799F1E2CB843495F601E56C84A626D": true + }, + "constraint": { + "PRIMARY": true + } }, - "index": { - "TAX_CALCULATION_RATE_TAX_COUNTRY_ID_TAX_REGION_ID_TAX_POSTCODE": true, - "TAX_CALCULATION_RATE_CODE": true, - "IDX_CA799F1E2CB843495F601E56C84A626D": true + "tax_calculation": { + "column": { + "tax_calculation_id": true, + "tax_calculation_rate_id": true, + "tax_calculation_rule_id": true, + "customer_tax_class_id": true, + "product_tax_class_id": true + }, + "index": { + "TAX_CALCULATION_TAX_CALCULATION_RULE_ID": true, + "TAX_CALCULATION_CUSTOMER_TAX_CLASS_ID": true, + "TAX_CALCULATION_PRODUCT_TAX_CLASS_ID": true, + "TAX_CALC_TAX_CALC_RATE_ID_CSTR_TAX_CLASS_ID_PRD_TAX_CLASS_ID": true + }, + "constraint": { + "PRIMARY": true, + "TAX_CALCULATION_PRODUCT_TAX_CLASS_ID_TAX_CLASS_CLASS_ID": true, + "TAX_CALCULATION_CUSTOMER_TAX_CLASS_ID_TAX_CLASS_CLASS_ID": true, + "TAX_CALC_TAX_CALC_RATE_ID_TAX_CALC_RATE_TAX_CALC_RATE_ID": true, + "TAX_CALC_TAX_CALC_RULE_ID_TAX_CALC_RULE_TAX_CALC_RULE_ID": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "tax_calculation": { - "column": { - "tax_calculation_id": true, - "tax_calculation_rate_id": true, - "tax_calculation_rule_id": true, - "customer_tax_class_id": true, - "product_tax_class_id": true - }, - "index": { - "TAX_CALCULATION_TAX_CALCULATION_RULE_ID": true, - "TAX_CALCULATION_CUSTOMER_TAX_CLASS_ID": true, - "TAX_CALCULATION_PRODUCT_TAX_CLASS_ID": true, - "TAX_CALC_TAX_CALC_RATE_ID_CSTR_TAX_CLASS_ID_PRD_TAX_CLASS_ID": true - }, - "constraint": { - "PRIMARY": true, - "TAX_CALCULATION_PRODUCT_TAX_CLASS_ID_TAX_CLASS_CLASS_ID": true, - "TAX_CALCULATION_CUSTOMER_TAX_CLASS_ID_TAX_CLASS_CLASS_ID": true, - "TAX_CALC_TAX_CALC_RATE_ID_TAX_CALC_RATE_TAX_CALC_RATE_ID": true, - "TAX_CALC_TAX_CALC_RULE_ID_TAX_CALC_RULE_TAX_CALC_RULE_ID": true - } - }, - "tax_calculation_rate_title": { - "column": { - "tax_calculation_rate_title_id": true, - "tax_calculation_rate_id": true, - "store_id": true, - "value": true - }, - "index": { - "TAX_CALCULATION_RATE_TITLE_TAX_CALCULATION_RATE_ID_STORE_ID": true, - "TAX_CALCULATION_RATE_TITLE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "TAX_CALCULATION_RATE_TITLE_STORE_ID_STORE_STORE_ID": true, - "FK_37FB965F786AD5897BB3AE90470C42AB": true - } - }, - "tax_order_aggregated_created": { - "column": { - "id": true, - "period": true, - "store_id": true, - "code": true, - "order_status": true, - "percent": true, - "orders_count": true, - "tax_base_amount_sum": true - }, - "index": { - "TAX_ORDER_AGGREGATED_CREATED_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "TAX_ORDER_AGGREGATED_CREATED_STORE_ID_STORE_STORE_ID": true, - "TAX_ORDER_AGGRED_CREATED_PERIOD_STORE_ID_CODE_PERCENT_ORDER_STS": true - } - }, - "tax_order_aggregated_updated": { - "column": { - "id": true, - "period": true, - "store_id": true, - "code": true, - "order_status": true, - "percent": true, - "orders_count": true, - "tax_base_amount_sum": true + "tax_calculation_rate_title": { + "column": { + "tax_calculation_rate_title_id": true, + "tax_calculation_rate_id": true, + "store_id": true, + "value": true + }, + "index": { + "TAX_CALCULATION_RATE_TITLE_TAX_CALCULATION_RATE_ID_STORE_ID": true, + "TAX_CALCULATION_RATE_TITLE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "TAX_CALCULATION_RATE_TITLE_STORE_ID_STORE_STORE_ID": true, + "FK_37FB965F786AD5897BB3AE90470C42AB": true + } }, - "index": { - "TAX_ORDER_AGGREGATED_UPDATED_STORE_ID": true + "tax_order_aggregated_created": { + "column": { + "id": true, + "period": true, + "store_id": true, + "code": true, + "order_status": true, + "percent": true, + "orders_count": true, + "tax_base_amount_sum": true + }, + "index": { + "TAX_ORDER_AGGREGATED_CREATED_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "TAX_ORDER_AGGREGATED_CREATED_STORE_ID_STORE_STORE_ID": true, + "TAX_ORDER_AGGRED_CREATED_PERIOD_STORE_ID_CODE_PERCENT_ORDER_STS": true + } }, - "constraint": { - "PRIMARY": true, - "TAX_ORDER_AGGREGATED_UPDATED_STORE_ID_STORE_STORE_ID": true, - "TAX_ORDER_AGGRED_UPDATED_PERIOD_STORE_ID_CODE_PERCENT_ORDER_STS": true + "tax_order_aggregated_updated": { + "column": { + "id": true, + "period": true, + "store_id": true, + "code": true, + "order_status": true, + "percent": true, + "orders_count": true, + "tax_base_amount_sum": true + }, + "index": { + "TAX_ORDER_AGGREGATED_UPDATED_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "TAX_ORDER_AGGREGATED_UPDATED_STORE_ID_STORE_STORE_ID": true, + "TAX_ORDER_AGGRED_UPDATED_PERIOD_STORE_ID_CODE_PERCENT_ORDER_STS": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Theme/etc/db_schema_whitelist.json b/app/code/Magento/Theme/etc/db_schema_whitelist.json index 2d1c3f8b6554a..25b81c16bfcab 100644 --- a/app/code/Magento/Theme/etc/db_schema_whitelist.json +++ b/app/code/Magento/Theme/etc/db_schema_whitelist.json @@ -1,49 +1,49 @@ { - "theme": { - "column": { - "theme_id": true, - "parent_id": true, - "theme_path": true, - "theme_title": true, - "preview_image": true, - "is_featured": true, - "area": true, - "type": true, - "code": true + "theme": { + "column": { + "theme_id": true, + "parent_id": true, + "theme_path": true, + "theme_title": true, + "preview_image": true, + "is_featured": true, + "area": true, + "type": true, + "code": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "theme_file": { - "column": { - "theme_files_id": true, - "theme_id": true, - "file_path": true, - "file_type": true, - "content": true, - "sort_order": true, - "is_temporary": true - }, - "constraint": { - "PRIMARY": true, - "THEME_FILE_THEME_ID_THEME_THEME_ID": true - } - }, - "design_change": { - "column": { - "design_change_id": true, - "store_id": true, - "design": true, - "date_from": true, - "date_to": true - }, - "index": { - "DESIGN_CHANGE_STORE_ID": true + "theme_file": { + "column": { + "theme_files_id": true, + "theme_id": true, + "file_path": true, + "file_type": true, + "content": true, + "sort_order": true, + "is_temporary": true + }, + "constraint": { + "PRIMARY": true, + "THEME_FILE_THEME_ID_THEME_THEME_ID": true + } }, - "constraint": { - "PRIMARY": true, - "DESIGN_CHANGE_STORE_ID_STORE_STORE_ID": true + "design_change": { + "column": { + "design_change_id": true, + "store_id": true, + "design": true, + "date_from": true, + "date_to": true + }, + "index": { + "DESIGN_CHANGE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "DESIGN_CHANGE_STORE_ID_STORE_STORE_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Translation/etc/db_schema_whitelist.json b/app/code/Magento/Translation/etc/db_schema_whitelist.json index deee0d900e3ee..975b1af9bcbbc 100644 --- a/app/code/Magento/Translation/etc/db_schema_whitelist.json +++ b/app/code/Magento/Translation/etc/db_schema_whitelist.json @@ -1,17 +1,17 @@ { - "translation": { - "column": { - "key_id": true, - "string": true, - "store_id": true, - "translate": true, - "locale": true, - "crc_string": true - }, - "constraint": { - "PRIMARY": true, - "TRANSLATION_STORE_ID_STORE_STORE_ID": true, - "TRANSLATION_STORE_ID_LOCALE_CRC_STRING_STRING": true + "translation": { + "column": { + "key_id": true, + "string": true, + "store_id": true, + "translate": true, + "locale": true, + "crc_string": true + }, + "constraint": { + "PRIMARY": true, + "TRANSLATION_STORE_ID_STORE_STORE_ID": true, + "TRANSLATION_STORE_ID_LOCALE_CRC_STRING_STRING": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Ui/etc/db_schema_whitelist.json b/app/code/Magento/Ui/etc/db_schema_whitelist.json index 662d6234ab96d..16d441f6d3ada 100644 --- a/app/code/Magento/Ui/etc/db_schema_whitelist.json +++ b/app/code/Magento/Ui/etc/db_schema_whitelist.json @@ -1,22 +1,22 @@ { - "ui_bookmark": { - "column": { - "bookmark_id": true, - "user_id": true, - "namespace": true, - "identifier": true, - "current": true, - "title": true, - "config": true, - "created_at": true, - "updated_at": true - }, - "index": { - "UI_BOOKMARK_USER_ID_NAMESPACE_IDENTIFIER": true - }, - "constraint": { - "PRIMARY": true, - "UI_BOOKMARK_USER_ID_ADMIN_USER_USER_ID": true + "ui_bookmark": { + "column": { + "bookmark_id": true, + "user_id": true, + "namespace": true, + "identifier": true, + "current": true, + "title": true, + "config": true, + "created_at": true, + "updated_at": true + }, + "index": { + "UI_BOOKMARK_USER_ID_NAMESPACE_IDENTIFIER": true + }, + "constraint": { + "PRIMARY": true, + "UI_BOOKMARK_USER_ID_ADMIN_USER_USER_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/etc/db_schema_whitelist.json b/app/code/Magento/UrlRewrite/etc/db_schema_whitelist.json index 24db0162e0394..bdaed647587a6 100644 --- a/app/code/Magento/UrlRewrite/etc/db_schema_whitelist.json +++ b/app/code/Magento/UrlRewrite/etc/db_schema_whitelist.json @@ -1,24 +1,24 @@ { - "url_rewrite": { - "column": { - "url_rewrite_id": true, - "entity_type": true, - "entity_id": true, - "request_path": true, - "target_path": true, - "redirect_type": true, - "store_id": true, - "description": true, - "is_autogenerated": true, - "metadata": true - }, - "index": { - "URL_REWRITE_TARGET_PATH": true, - "URL_REWRITE_STORE_ID_ENTITY_ID": true - }, - "constraint": { - "PRIMARY": true, - "URL_REWRITE_REQUEST_PATH_STORE_ID": true + "url_rewrite": { + "column": { + "url_rewrite_id": true, + "entity_type": true, + "entity_id": true, + "request_path": true, + "target_path": true, + "redirect_type": true, + "store_id": true, + "description": true, + "is_autogenerated": true, + "metadata": true + }, + "index": { + "URL_REWRITE_TARGET_PATH": true, + "URL_REWRITE_STORE_ID_ENTITY_ID": true + }, + "constraint": { + "PRIMARY": true, + "URL_REWRITE_REQUEST_PATH_STORE_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/User/etc/db_schema_whitelist.json b/app/code/Magento/User/etc/db_schema_whitelist.json index 1ae7f78de0218..2af77c0d8455b 100644 --- a/app/code/Magento/User/etc/db_schema_whitelist.json +++ b/app/code/Magento/User/etc/db_schema_whitelist.json @@ -1,45 +1,45 @@ { - "admin_user": { - "column": { - "user_id": true, - "firstname": true, - "lastname": true, - "email": true, - "username": true, - "password": true, - "created": true, - "modified": true, - "logdate": true, - "lognum": true, - "reload_acl_flag": true, - "is_active": true, - "extra": true, - "rp_token": true, - "rp_token_created_at": true, - "interface_locale": true, - "failures_num": true, - "first_failure": true, - "lock_expires": true + "admin_user": { + "column": { + "user_id": true, + "firstname": true, + "lastname": true, + "email": true, + "username": true, + "password": true, + "created": true, + "modified": true, + "logdate": true, + "lognum": true, + "reload_acl_flag": true, + "is_active": true, + "extra": true, + "rp_token": true, + "rp_token_created_at": true, + "interface_locale": true, + "failures_num": true, + "first_failure": true, + "lock_expires": true + }, + "constraint": { + "PRIMARY": true, + "ADMIN_USER_USERNAME": true + } }, - "constraint": { - "PRIMARY": true, - "ADMIN_USER_USERNAME": true + "admin_passwords": { + "column": { + "password_id": true, + "user_id": true, + "password_hash": true, + "expires": true, + "last_updated": true + }, + "index": { + "ADMIN_PASSWORDS_USER_ID": true + }, + "constraint": { + "PRIMARY": true, + "ADMIN_PASSWORDS_USER_ID_ADMIN_USER_USER_ID": true + } } - }, - "admin_passwords": { - "column": { - "password_id": true, - "user_id": true, - "password_hash": true, - "expires": true, - "last_updated": true - }, - "index": { - "ADMIN_PASSWORDS_USER_ID": true - }, - "constraint": { - "PRIMARY": true, - "ADMIN_PASSWORDS_USER_ID_ADMIN_USER_USER_ID": true - } - } } \ No newline at end of file diff --git a/app/code/Magento/Variable/etc/db_schema_whitelist.json b/app/code/Magento/Variable/etc/db_schema_whitelist.json index f039ad3a87a37..b122e33eceb19 100644 --- a/app/code/Magento/Variable/etc/db_schema_whitelist.json +++ b/app/code/Magento/Variable/etc/db_schema_whitelist.json @@ -1,31 +1,31 @@ { - "variable": { - "column": { - "variable_id": true, - "code": true, - "name": true + "variable": { + "column": { + "variable_id": true, + "code": true, + "name": true + }, + "constraint": { + "PRIMARY": true, + "VARIABLE_CODE": true + } }, - "constraint": { - "PRIMARY": true, - "VARIABLE_CODE": true + "variable_value": { + "column": { + "value_id": true, + "variable_id": true, + "store_id": true, + "plain_value": true, + "html_value": true + }, + "index": { + "VARIABLE_VALUE_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "VARIABLE_VALUE_VARIABLE_ID_STORE_ID": true, + "VARIABLE_VALUE_STORE_ID_STORE_STORE_ID": true, + "VARIABLE_VALUE_VARIABLE_ID_VARIABLE_VARIABLE_ID": true + } } - }, - "variable_value": { - "column": { - "value_id": true, - "variable_id": true, - "store_id": true, - "plain_value": true, - "html_value": true - }, - "index": { - "VARIABLE_VALUE_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "VARIABLE_VALUE_VARIABLE_ID_STORE_ID": true, - "VARIABLE_VALUE_STORE_ID_STORE_STORE_ID": true, - "VARIABLE_VALUE_VARIABLE_ID_VARIABLE_VARIABLE_ID": true - } - } } \ No newline at end of file diff --git a/app/code/Magento/Vault/etc/db_schema_whitelist.json b/app/code/Magento/Vault/etc/db_schema_whitelist.json index 9b5e740fd16e8..e2275dc3532d7 100644 --- a/app/code/Magento/Vault/etc/db_schema_whitelist.json +++ b/app/code/Magento/Vault/etc/db_schema_whitelist.json @@ -1,36 +1,36 @@ { - "vault_payment_token": { - "column": { - "entity_id": true, - "customer_id": true, - "public_hash": true, - "payment_method_code": true, - "type": true, - "created_at": true, - "expires_at": true, - "gateway_token": true, - "details": true, - "is_active": true, - "is_visible": true + "vault_payment_token": { + "column": { + "entity_id": true, + "customer_id": true, + "public_hash": true, + "payment_method_code": true, + "type": true, + "created_at": true, + "expires_at": true, + "gateway_token": true, + "details": true, + "is_active": true, + "is_visible": true + }, + "constraint": { + "PRIMARY": true, + "VAULT_PAYMENT_TOKEN_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "VAULT_PAYMENT_TOKEN_PAYMENT_METHOD_CODE_CSTR_ID_GATEWAY_TOKEN": true, + "UNQ_54DCE14AEAEA03B587F9EF723EB10A10": true, + "VAULT_PAYMENT_TOKEN_PUBLIC_HASH": true, + "VAULT_PAYMENT_TOKEN_HASH_UNIQUE_INDEX_PUBLIC_HASH": true + } }, - "constraint": { - "PRIMARY": true, - "VAULT_PAYMENT_TOKEN_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "VAULT_PAYMENT_TOKEN_PAYMENT_METHOD_CODE_CSTR_ID_GATEWAY_TOKEN": true, - "UNQ_54DCE14AEAEA03B587F9EF723EB10A10": true, - "VAULT_PAYMENT_TOKEN_PUBLIC_HASH": true, - "VAULT_PAYMENT_TOKEN_HASH_UNIQUE_INDEX_PUBLIC_HASH": true + "vault_payment_token_order_payment_link": { + "column": { + "order_payment_id": true, + "payment_token_id": true + }, + "constraint": { + "PRIMARY": true, + "FK_CF37B9D854256534BE23C818F6291CA2": true, + "FK_4ED894655446D385894580BECA993862": true + } } - }, - "vault_payment_token_order_payment_link": { - "column": { - "order_payment_id": true, - "payment_token_id": true - }, - "constraint": { - "PRIMARY": true, - "FK_CF37B9D854256534BE23C818F6291CA2": true, - "FK_4ED894655446D385894580BECA993862": true - } - } -} +} \ No newline at end of file diff --git a/app/code/Magento/Weee/etc/db_schema_whitelist.json b/app/code/Magento/Weee/etc/db_schema_whitelist.json index cf7c72a87a7fa..eb18574877cfe 100644 --- a/app/code/Magento/Weee/etc/db_schema_whitelist.json +++ b/app/code/Magento/Weee/etc/db_schema_whitelist.json @@ -1,79 +1,79 @@ { - "weee_tax": { - "column": { - "value_id": true, - "website_id": true, - "entity_id": true, - "country": true, - "value": true, - "state": true, - "attribute_id": true + "weee_tax": { + "column": { + "value_id": true, + "website_id": true, + "entity_id": true, + "country": true, + "value": true, + "state": true, + "attribute_id": true + }, + "index": { + "WEEE_TAX_WEBSITE_ID": true, + "WEEE_TAX_ENTITY_ID": true, + "WEEE_TAX_COUNTRY": true, + "WEEE_TAX_ATTRIBUTE_ID": true + }, + "constraint": { + "PRIMARY": true, + "WEEE_TAX_COUNTRY_DIRECTORY_COUNTRY_COUNTRY_ID": true, + "WEEE_TAX_ENTITY_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, + "WEEE_TAX_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, + "WEEE_TAX_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, + "WEEE_TAX_ENTITY_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true + } }, - "index": { - "WEEE_TAX_WEBSITE_ID": true, - "WEEE_TAX_ENTITY_ID": true, - "WEEE_TAX_COUNTRY": true, - "WEEE_TAX_ATTRIBUTE_ID": true + "quote_item": { + "column": { + "weee_tax_applied": true, + "weee_tax_applied_amount": true, + "weee_tax_applied_row_amount": true, + "weee_tax_disposition": true, + "weee_tax_row_disposition": true, + "base_weee_tax_applied_amount": true, + "base_weee_tax_applied_row_amnt": true, + "base_weee_tax_disposition": true, + "base_weee_tax_row_disposition": true + } }, - "constraint": { - "PRIMARY": true, - "WEEE_TAX_COUNTRY_DIRECTORY_COUNTRY_COUNTRY_ID": true, - "WEEE_TAX_ENTITY_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, - "WEEE_TAX_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, - "WEEE_TAX_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true, - "WEEE_TAX_ENTITY_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true - } - }, - "quote_item": { - "column": { - "weee_tax_applied": true, - "weee_tax_applied_amount": true, - "weee_tax_applied_row_amount": true, - "weee_tax_disposition": true, - "weee_tax_row_disposition": true, - "base_weee_tax_applied_amount": true, - "base_weee_tax_applied_row_amnt": true, - "base_weee_tax_disposition": true, - "base_weee_tax_row_disposition": true - } - }, - "sales_order_item": { - "column": { - "weee_tax_applied": true, - "weee_tax_applied_amount": true, - "weee_tax_applied_row_amount": true, - "weee_tax_disposition": true, - "weee_tax_row_disposition": true, - "base_weee_tax_applied_amount": true, - "base_weee_tax_applied_row_amnt": true, - "base_weee_tax_disposition": true, - "base_weee_tax_row_disposition": true - } - }, - "sales_invoice_item": { - "column": { - "weee_tax_applied": true, - "weee_tax_applied_amount": true, - "weee_tax_applied_row_amount": true, - "weee_tax_disposition": true, - "weee_tax_row_disposition": true, - "base_weee_tax_applied_amount": true, - "base_weee_tax_applied_row_amnt": true, - "base_weee_tax_disposition": true, - "base_weee_tax_row_disposition": true - } - }, - "sales_creditmemo_item": { - "column": { - "weee_tax_applied": true, - "weee_tax_applied_amount": true, - "weee_tax_applied_row_amount": true, - "weee_tax_disposition": true, - "weee_tax_row_disposition": true, - "base_weee_tax_applied_amount": true, - "base_weee_tax_applied_row_amnt": true, - "base_weee_tax_disposition": true, - "base_weee_tax_row_disposition": true + "sales_order_item": { + "column": { + "weee_tax_applied": true, + "weee_tax_applied_amount": true, + "weee_tax_applied_row_amount": true, + "weee_tax_disposition": true, + "weee_tax_row_disposition": true, + "base_weee_tax_applied_amount": true, + "base_weee_tax_applied_row_amnt": true, + "base_weee_tax_disposition": true, + "base_weee_tax_row_disposition": true + } + }, + "sales_invoice_item": { + "column": { + "weee_tax_applied": true, + "weee_tax_applied_amount": true, + "weee_tax_applied_row_amount": true, + "weee_tax_disposition": true, + "weee_tax_row_disposition": true, + "base_weee_tax_applied_amount": true, + "base_weee_tax_applied_row_amnt": true, + "base_weee_tax_disposition": true, + "base_weee_tax_row_disposition": true + } + }, + "sales_creditmemo_item": { + "column": { + "weee_tax_applied": true, + "weee_tax_applied_amount": true, + "weee_tax_applied_row_amount": true, + "weee_tax_disposition": true, + "weee_tax_row_disposition": true, + "base_weee_tax_applied_amount": true, + "base_weee_tax_applied_row_amnt": true, + "base_weee_tax_disposition": true, + "base_weee_tax_row_disposition": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Widget/etc/db_schema_whitelist.json b/app/code/Magento/Widget/etc/db_schema_whitelist.json index 431ade7f52f04..a42646a355045 100644 --- a/app/code/Magento/Widget/etc/db_schema_whitelist.json +++ b/app/code/Magento/Widget/etc/db_schema_whitelist.json @@ -1,98 +1,98 @@ { - "widget": { - "column": { - "widget_id": true, - "widget_code": true, - "widget_type": true, - "parameters": true + "widget": { + "column": { + "widget_id": true, + "widget_code": true, + "widget_type": true, + "parameters": true + }, + "index": { + "WIDGET_WIDGET_CODE": true + }, + "constraint": { + "PRIMARY": true + } }, - "index": { - "WIDGET_WIDGET_CODE": true + "widget_instance": { + "column": { + "instance_id": true, + "instance_type": true, + "theme_id": true, + "title": true, + "store_ids": true, + "widget_parameters": true, + "sort_order": true + }, + "constraint": { + "PRIMARY": true, + "WIDGET_INSTANCE_THEME_ID_THEME_THEME_ID": true + } }, - "constraint": { - "PRIMARY": true - } - }, - "widget_instance": { - "column": { - "instance_id": true, - "instance_type": true, - "theme_id": true, - "title": true, - "store_ids": true, - "widget_parameters": true, - "sort_order": true - }, - "constraint": { - "PRIMARY": true, - "WIDGET_INSTANCE_THEME_ID_THEME_THEME_ID": true - } - }, - "widget_instance_page": { - "column": { - "page_id": true, - "instance_id": true, - "page_group": true, - "layout_handle": true, - "block_reference": true, - "page_for": true, - "entities": true, - "page_template": true - }, - "index": { - "WIDGET_INSTANCE_PAGE_INSTANCE_ID": true - }, - "constraint": { - "PRIMARY": true, - "WIDGET_INSTANCE_PAGE_INSTANCE_ID_WIDGET_INSTANCE_INSTANCE_ID": true - } - }, - "widget_instance_page_layout": { - "column": { - "page_id": true, - "layout_update_id": true + "widget_instance_page": { + "column": { + "page_id": true, + "instance_id": true, + "page_group": true, + "layout_handle": true, + "block_reference": true, + "page_for": true, + "entities": true, + "page_template": true + }, + "index": { + "WIDGET_INSTANCE_PAGE_INSTANCE_ID": true + }, + "constraint": { + "PRIMARY": true, + "WIDGET_INSTANCE_PAGE_INSTANCE_ID_WIDGET_INSTANCE_INSTANCE_ID": true + } }, - "index": { - "WIDGET_INSTANCE_PAGE_LAYOUT_PAGE_ID": true - }, - "constraint": { - "WIDGET_INSTANCE_PAGE_LAYOUT_PAGE_ID_WIDGET_INSTANCE_PAGE_PAGE_ID": true, - "WIDGET_INSTANCE_PAGE_LYT_LYT_UPDATE_ID_LYT_UPDATE_LYT_UPDATE_ID": true, - "WIDGET_INSTANCE_PAGE_LAYOUT_LAYOUT_UPDATE_ID_PAGE_ID": true - } - }, - "layout_update": { - "column": { - "layout_update_id": true, - "handle": true, - "xml": true, - "sort_order": true, - "updated_at": true - }, - "index": { - "LAYOUT_UPDATE_HANDLE": true - }, - "constraint": { - "PRIMARY": true - } - }, - "layout_link": { - "column": { - "layout_link_id": true, - "store_id": true, - "theme_id": true, - "layout_update_id": true, - "is_temporary": true + "widget_instance_page_layout": { + "column": { + "page_id": true, + "layout_update_id": true + }, + "index": { + "WIDGET_INSTANCE_PAGE_LAYOUT_PAGE_ID": true + }, + "constraint": { + "WIDGET_INSTANCE_PAGE_LAYOUT_PAGE_ID_WIDGET_INSTANCE_PAGE_PAGE_ID": true, + "WIDGET_INSTANCE_PAGE_LYT_LYT_UPDATE_ID_LYT_UPDATE_LYT_UPDATE_ID": true, + "WIDGET_INSTANCE_PAGE_LAYOUT_LAYOUT_UPDATE_ID_PAGE_ID": true + } }, - "index": { - "LAYOUT_LINK_LAYOUT_UPDATE_ID": true, - "LAYOUT_LINK_STORE_ID_THEME_ID_LAYOUT_UPDATE_ID_IS_TEMPORARY": true + "layout_update": { + "column": { + "layout_update_id": true, + "handle": true, + "xml": true, + "sort_order": true, + "updated_at": true + }, + "index": { + "LAYOUT_UPDATE_HANDLE": true + }, + "constraint": { + "PRIMARY": true + } }, - "constraint": { - "PRIMARY": true, - "LAYOUT_LINK_LAYOUT_UPDATE_ID_LAYOUT_UPDATE_LAYOUT_UPDATE_ID": true, - "LAYOUT_LINK_STORE_ID_STORE_STORE_ID": true, - "LAYOUT_LINK_THEME_ID_THEME_THEME_ID": true + "layout_link": { + "column": { + "layout_link_id": true, + "store_id": true, + "theme_id": true, + "layout_update_id": true, + "is_temporary": true + }, + "index": { + "LAYOUT_LINK_LAYOUT_UPDATE_ID": true, + "LAYOUT_LINK_STORE_ID_THEME_ID_LAYOUT_UPDATE_ID_IS_TEMPORARY": true + }, + "constraint": { + "PRIMARY": true, + "LAYOUT_LINK_LAYOUT_UPDATE_ID_LAYOUT_UPDATE_LAYOUT_UPDATE_ID": true, + "LAYOUT_LINK_STORE_ID_STORE_STORE_ID": true, + "LAYOUT_LINK_THEME_ID_THEME_THEME_ID": true + } } - } } \ No newline at end of file diff --git a/app/code/Magento/Wishlist/etc/db_schema_whitelist.json b/app/code/Magento/Wishlist/etc/db_schema_whitelist.json index 82791ad70958f..beaab64280a76 100644 --- a/app/code/Magento/Wishlist/etc/db_schema_whitelist.json +++ b/app/code/Magento/Wishlist/etc/db_schema_whitelist.json @@ -1,55 +1,55 @@ { - "wishlist": { - "column": { - "wishlist_id": true, - "customer_id": true, - "shared": true, - "sharing_code": true, - "updated_at": true + "wishlist": { + "column": { + "wishlist_id": true, + "customer_id": true, + "shared": true, + "sharing_code": true, + "updated_at": true + }, + "index": { + "WISHLIST_SHARED": true + }, + "constraint": { + "PRIMARY": true, + "WISHLIST_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, + "WISHLIST_CUSTOMER_ID": true + } }, - "index": { - "WISHLIST_SHARED": true + "wishlist_item": { + "column": { + "wishlist_item_id": true, + "wishlist_id": true, + "product_id": true, + "store_id": true, + "added_at": true, + "description": true, + "qty": true + }, + "index": { + "WISHLIST_ITEM_WISHLIST_ID": true, + "WISHLIST_ITEM_PRODUCT_ID": true, + "WISHLIST_ITEM_STORE_ID": true + }, + "constraint": { + "PRIMARY": true, + "WISHLIST_ITEM_WISHLIST_ID_WISHLIST_WISHLIST_ID": true, + "WISHLIST_ITEM_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, + "WISHLIST_ITEM_STORE_ID_STORE_STORE_ID": true, + "WISHLIST_ITEM_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true + } }, - "constraint": { - "PRIMARY": true, - "WISHLIST_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, - "WISHLIST_CUSTOMER_ID": true + "wishlist_item_option": { + "column": { + "option_id": true, + "wishlist_item_id": true, + "product_id": true, + "code": true, + "value": true + }, + "constraint": { + "PRIMARY": true, + "FK_A014B30B04B72DD0EAB3EECD779728D6": true + } } - }, - "wishlist_item": { - "column": { - "wishlist_item_id": true, - "wishlist_id": true, - "product_id": true, - "store_id": true, - "added_at": true, - "description": true, - "qty": true - }, - "index": { - "WISHLIST_ITEM_WISHLIST_ID": true, - "WISHLIST_ITEM_PRODUCT_ID": true, - "WISHLIST_ITEM_STORE_ID": true - }, - "constraint": { - "PRIMARY": true, - "WISHLIST_ITEM_WISHLIST_ID_WISHLIST_WISHLIST_ID": true, - "WISHLIST_ITEM_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, - "WISHLIST_ITEM_STORE_ID_STORE_STORE_ID": true, - "WISHLIST_ITEM_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true - } - }, - "wishlist_item_option": { - "column": { - "option_id": true, - "wishlist_item_id": true, - "product_id": true, - "code": true, - "value": true - }, - "constraint": { - "PRIMARY": true, - "FK_A014B30B04B72DD0EAB3EECD779728D6": true - } - } } \ No newline at end of file From 989c1a5d395340a83285936ccd6a35337d377d88 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Thu, 16 Aug 2018 16:33:05 -0400 Subject: [PATCH 1039/1171] MQE-1174: Deliver weekly regression enablement tests - Add annotation for base test to prevent it from failing PageBuilder --- .../Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml index 6a8de1ca5f0a0..8f62c15b8441c 100644 --- a/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml +++ b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml @@ -11,6 +11,9 @@ <!-- This test exists to serve as a base for extension for other tests --> <test name="NewProductsListWidgetTest"> + <annotations> + <group value="WYSIWYGDisabled"/> + </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> From 840755560f20acb9574e6741f1404b6ba2ad7405 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <paliarus@adobe.com> Date: Thu, 16 Aug 2018 15:33:22 -0500 Subject: [PATCH 1040/1171] MAGETWO-94207: Cart GET for customer in REST returns 400 when cart not created --- app/code/Magento/Quote/Model/Webapi/ParamOverriderCartId.php | 2 +- .../Framework/Webapi/Rest/Request/ParamOverriderInterface.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/Webapi/ParamOverriderCartId.php b/app/code/Magento/Quote/Model/Webapi/ParamOverriderCartId.php index a13a2dc590def..ae6c82629c437 100644 --- a/app/code/Magento/Quote/Model/Webapi/ParamOverriderCartId.php +++ b/app/code/Magento/Quote/Model/Webapi/ParamOverriderCartId.php @@ -56,7 +56,7 @@ public function getOverriddenValue() } } } catch (NoSuchEntityException $e) { - /* do nothing and just return null */ + throw new NoSuchEntityException(__('Current customer does not have an active cart.')); } return null; } diff --git a/lib/internal/Magento/Framework/Webapi/Rest/Request/ParamOverriderInterface.php b/lib/internal/Magento/Framework/Webapi/Rest/Request/ParamOverriderInterface.php index 21741a2c16c1d..828022353e4fa 100644 --- a/lib/internal/Magento/Framework/Webapi/Rest/Request/ParamOverriderInterface.php +++ b/lib/internal/Magento/Framework/Webapi/Rest/Request/ParamOverriderInterface.php @@ -34,6 +34,7 @@ interface ParamOverriderInterface * Returns the overridden value to use. * * @return string|int|null + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getOverriddenValue(); } From 8278e9be7f1a90d3cabdb124fa57402594d49d8d Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Thu, 16 Aug 2018 17:28:13 -0500 Subject: [PATCH 1041/1171] MAGETWO-91678: Minimum Order amount required in Admin orders --- .../Magento/Quote/Model/QuoteValidator.php | 7 +- .../AllowedCountryValidationRule.php | 25 ++- .../BillingAddressValidationRule.php | 24 ++- .../MinimumAmountValidationRule.php | 29 ++- .../PaymentMethodValidationRule.php | 24 ++- .../QuoteValidationComposite.php | 20 +- .../QuoteValidationRuleInterface.php | 15 +- .../ShippingAddressValidationRule.php | 23 ++- .../ShippingMethodValidationRule.php | 24 ++- .../Test/Unit/Model/QuoteValidatorTest.php | 178 ------------------ app/code/Magento/Quote/etc/di.xml | 10 +- .../Quote/Model/QuoteValidatorTest.php | 19 +- 12 files changed, 154 insertions(+), 244 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteValidator.php b/app/code/Magento/Quote/Model/QuoteValidator.php index 59e7334d30ff3..04d6d4ecba160 100644 --- a/app/code/Magento/Quote/Model/QuoteValidator.php +++ b/app/code/Magento/Quote/Model/QuoteValidator.php @@ -84,7 +84,12 @@ public function validateQuoteAmount(QuoteEntity $quote, $amount) */ public function validateBeforeSubmit(QuoteEntity $quote) { - foreach ($this->quoteValidationRule->validate($quote) as $messages) { + foreach ($this->quoteValidationRule->validate($quote) as $validationResult) { + if ($validationResult->isValid()) { + continue; + } + + $messages = $validationResult->getErrors(); $defaultMessage = array_shift($messages); if ($defaultMessage && !empty($messages)) { $defaultMessage .= ' %1'; diff --git a/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php index 72627936daaf4..34e9b910de8e9 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php @@ -8,6 +8,7 @@ namespace Magento\Quote\Model\ValidationRules; use Magento\Directory\Model\AllowedCountries; +use Magento\Framework\Validation\ValidationResultFactory; use Magento\Quote\Model\Quote; class AllowedCountryValidationRule implements QuoteValidationRuleInterface @@ -15,21 +16,31 @@ class AllowedCountryValidationRule implements QuoteValidationRuleInterface /** * @var string */ - private $defaultMessage; + private $generalMessage; /** * @var AllowedCountries */ private $allowedCountryReader; + /** + * @var ValidationResultFactory + */ + private $validationResultFactory; + /** * @param AllowedCountries $allowedCountryReader - * @param string $defaultMessage + * @param ValidationResultFactory $validationResultFactory + * @param string $generalMessage */ - public function __construct(AllowedCountries $allowedCountryReader, string $defaultMessage = '') - { - $this->defaultMessage = $defaultMessage; + public function __construct( + AllowedCountries $allowedCountryReader, + ValidationResultFactory $validationResultFactory, + string $generalMessage = '' + ) { $this->allowedCountryReader = $allowedCountryReader; + $this->validationResultFactory = $validationResultFactory; + $this->generalMessage = $generalMessage; } /** @@ -46,10 +57,10 @@ public function validate(Quote $quote): array $this->allowedCountryReader->getAllowedCountries() ); if (!$validationResult) { - $validationErrors = [$this->defaultMessage]; + $validationErrors = [$this->generalMessage]; } } - return $validationErrors ? [get_class($this) => $validationErrors] : []; + return [$this->validationResultFactory->create(['errors' => $validationErrors])]; } } diff --git a/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php index 0dd6d472e761d..a28bc23cc0f0f 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php @@ -7,6 +7,7 @@ namespace Magento\Quote\Model\ValidationRules; +use Magento\Framework\Validation\ValidationResultFactory; use Magento\Quote\Model\Quote; class BillingAddressValidationRule implements QuoteValidationRuleInterface @@ -14,14 +15,23 @@ class BillingAddressValidationRule implements QuoteValidationRuleInterface /** * @var string */ - private $defaultMessage; + private $generalMessage; /** - * @param string $defaultMessage + * @var ValidationResultFactory */ - public function __construct(string $defaultMessage = '') - { - $this->defaultMessage = $defaultMessage; + private $validationResultFactory; + + /** + * @param ValidationResultFactory $validationResultFactory + * @param string $generalMessage + */ + public function __construct( + ValidationResultFactory $validationResultFactory, + string $generalMessage = '' + ) { + $this->validationResultFactory = $validationResultFactory; + $this->generalMessage = $generalMessage; } /** @@ -32,12 +42,12 @@ public function validate(Quote $quote): array $validationErrors = []; $validationResult = $quote->getBillingAddress()->validate(); if ($validationResult !== true) { - $validationErrors = [$this->defaultMessage]; + $validationErrors = [$this->generalMessage]; } if (is_array($validationResult)) { $validationErrors = array_merge($validationErrors, $validationResult); } - return $validationErrors ? [get_class($this) => $validationErrors] : []; + return [$this->validationResultFactory->create(['errors' => $validationErrors])]; } } diff --git a/app/code/Magento/Quote/Model/ValidationRules/MinimumAmountValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/MinimumAmountValidationRule.php index 9230f370c7ca7..74f605e2e4c66 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/MinimumAmountValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/MinimumAmountValidationRule.php @@ -7,6 +7,7 @@ namespace Magento\Quote\Model\ValidationRules; +use Magento\Framework\Validation\ValidationResultFactory; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Validator\MinimumOrderAmount\ValidationMessage; @@ -15,21 +16,31 @@ class MinimumAmountValidationRule implements QuoteValidationRuleInterface /** * @var string */ - private $defaultMessage; + private $generalMessage; /** * @var ValidationMessage */ private $amountValidationMessage; + /** + * @var ValidationResultFactory + */ + private $validationResultFactory; + /** * @param ValidationMessage $amountValidationMessage - * @param string $defaultMessage + * @param ValidationResultFactory $validationResultFactory + * @param string $generalMessage */ - public function __construct(ValidationMessage $amountValidationMessage, string $defaultMessage = '') - { + public function __construct( + ValidationMessage $amountValidationMessage, + ValidationResultFactory $validationResultFactory, + string $generalMessage = '' + ) { $this->amountValidationMessage = $amountValidationMessage; - $this->defaultMessage = $defaultMessage; + $this->validationResultFactory = $validationResultFactory; + $this->generalMessage = $generalMessage; } /** @@ -41,12 +52,12 @@ public function validate(Quote $quote): array $validationErrors = []; $validationResult = $quote->validateMinimumAmount($quote->getIsMultiShipping()); if (!$validationResult) { - if (!$this->defaultMessage) { - $this->defaultMessage = $this->amountValidationMessage->getMessage(); + if (!$this->generalMessage) { + $this->generalMessage = $this->amountValidationMessage->getMessage(); } - $validationErrors = [$this->defaultMessage]; + $validationErrors = [$this->generalMessage]; } - return $validationErrors ? [get_class($this) => $validationErrors] : []; + return [$this->validationResultFactory->create(['errors' => $validationErrors])]; } } diff --git a/app/code/Magento/Quote/Model/ValidationRules/PaymentMethodValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/PaymentMethodValidationRule.php index 7bb35096a5878..ac9f24bf3567b 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/PaymentMethodValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/PaymentMethodValidationRule.php @@ -7,6 +7,7 @@ namespace Magento\Quote\Model\ValidationRules; +use Magento\Framework\Validation\ValidationResultFactory; use Magento\Quote\Model\Quote; class PaymentMethodValidationRule implements QuoteValidationRuleInterface @@ -14,14 +15,23 @@ class PaymentMethodValidationRule implements QuoteValidationRuleInterface /** * @var string */ - private $defaultMessage; + private $generalMessage; /** - * @param string $defaultMessage + * @var ValidationResultFactory */ - public function __construct(string $defaultMessage = '') - { - $this->defaultMessage = $defaultMessage; + private $validationResultFactory; + + /** + * @param ValidationResultFactory $validationResultFactory + * @param string $generalMessage + */ + public function __construct( + ValidationResultFactory $validationResultFactory, + string $generalMessage = '' + ) { + $this->validationResultFactory = $validationResultFactory; + $this->generalMessage = $generalMessage; } /** @@ -32,9 +42,9 @@ public function validate(Quote $quote): array $validationErrors = []; $validationResult = $quote->getPayment()->getMethod(); if (!$validationResult) { - $validationErrors = [$this->defaultMessage]; + $validationErrors = [$this->generalMessage]; } - return $validationErrors ? [get_class($this) => $validationErrors] : []; + return [$this->validationResultFactory->create(['errors' => $validationErrors])]; } } diff --git a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationComposite.php b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationComposite.php index cfce07048064a..e6e9bd4fc7917 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationComposite.php +++ b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationComposite.php @@ -16,8 +16,22 @@ class QuoteValidationComposite implements QuoteValidationRuleInterface */ private $validationRules = []; + /** + * @param QuoteValidationRuleInterface[] $validationRules + * @throws \InvalidArgumentException + */ public function __construct(array $validationRules) { + foreach ($validationRules as $validationRule) { + if (!($validationRule instanceof QuoteValidationRuleInterface)) { + throw new \InvalidArgumentException( + sprintf( + 'Instance of the ValidationRuleInterface is expected, got %s instead.', + get_class($validationRule) + ) + ); + } + } $this->validationRules = $validationRules; } @@ -30,7 +44,11 @@ public function validate(Quote $quote): array foreach ($this->validationRules as $validationRule) { $ruleValidationResult = $validationRule->validate($quote); - $aggregateResult += $ruleValidationResult; + foreach ($ruleValidationResult as $item) { + if (!$item->isValid()) { + array_push($aggregateResult, $item); + } + } } return $aggregateResult; diff --git a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php index 31da5b731aa4b..cb397f3483055 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php +++ b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php @@ -6,6 +6,7 @@ namespace Magento\Quote\Model\ValidationRules; +use Magento\Framework\Validation\ValidationResult; use Magento\Quote\Model\Quote; interface QuoteValidationRuleInterface @@ -14,19 +15,7 @@ interface QuoteValidationRuleInterface * Validate quote model. * * @param Quote $quote - * @return array - * [ - * 'ruleId_1' => [ - * 'Base error message', - * 'Additional error message #1', - * 'Additional error message #2', - * 'Additional error message #3', - * 'Additional error message #4', - * ], - * 'ruleId_2' => [ - * 'Base error message', - * ] - * ] + * @return ValidationResult[] */ public function validate(Quote $quote): array; } \ No newline at end of file diff --git a/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php index 71fc2503e9a32..87e69230ae36b 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php @@ -7,6 +7,7 @@ namespace Magento\Quote\Model\ValidationRules; +use Magento\Framework\Validation\ValidationResultFactory; use Magento\Quote\Model\Quote; class ShippingAddressValidationRule implements QuoteValidationRuleInterface @@ -14,14 +15,24 @@ class ShippingAddressValidationRule implements QuoteValidationRuleInterface /** * @var string */ - private $defaultMessage; + private $generalMessage; /** - * @param string $defaultMessage + * @var ValidationResultFactory */ - public function __construct(string $defaultMessage = '') + private $validationResultFactory; + + /** + * @param ValidationResultFactory $validationResultFactory + * @param string $generalMessage + */ + public function __construct( + ValidationResultFactory $validationResultFactory, + string $generalMessage = '' + ) { - $this->defaultMessage = $defaultMessage; + $this->validationResultFactory = $validationResultFactory; + $this->generalMessage = $generalMessage; } /** @@ -34,13 +45,13 @@ public function validate(Quote $quote): array if (!$quote->isVirtual()) { $validationResult = $quote->getShippingAddress()->validate(); if ($validationResult !== true) { - $validationErrors = [$this->defaultMessage]; + $validationErrors = [$this->generalMessage]; } if (is_array($validationResult)) { $validationErrors = array_merge($validationErrors, $validationResult); } } - return $validationErrors ? [get_class($this) => $validationErrors] : []; + return [$this->validationResultFactory->create(['errors' => $validationErrors])]; } } diff --git a/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php index 1f4f613dd5a12..93c775c465235 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php @@ -7,6 +7,7 @@ namespace Magento\Quote\Model\ValidationRules; +use Magento\Framework\Validation\ValidationResultFactory; use Magento\Quote\Model\Quote; class ShippingMethodValidationRule implements QuoteValidationRuleInterface @@ -14,14 +15,23 @@ class ShippingMethodValidationRule implements QuoteValidationRuleInterface /** * @var string */ - private $defaultMessage; + private $generalMessage; /** - * @param string $defaultMessage + * @var ValidationResultFactory */ - public function __construct(string $defaultMessage = '') - { - $this->defaultMessage = $defaultMessage; + private $validationResultFactory; + + /** + * @param ValidationResultFactory $validationResultFactory + * @param string $generalMessage + */ + public function __construct( + ValidationResultFactory $validationResultFactory, + string $generalMessage = '' + ) { + $this->validationResultFactory = $validationResultFactory; + $this->generalMessage = $generalMessage; } /** @@ -36,10 +46,10 @@ public function validate(Quote $quote): array $shippingRate = $quote->getShippingAddress()->getShippingRateByCode($shippingMethod); $validationResult = $shippingMethod && $shippingRate; if (!$validationResult) { - $validationErrors = [$this->defaultMessage]; + $validationErrors = [$this->generalMessage]; } } - return $validationErrors ? [get_class($this) => $validationErrors] : []; + return [$this->validationResultFactory->create(['errors' => $validationErrors])]; } } diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteValidatorTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteValidatorTest.php index 6865134a04870..d0a3c9fab5131 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteValidatorTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteValidatorTest.php @@ -88,182 +88,4 @@ public function testCheckQuoteAmountExistingError() $this->quoteValidator->validateQuoteAmount($this->quoteMock, QuoteValidator::MAXIMUM_AVAILABLE_NUMBER + 1) ); } - - public function testCheckQuoteAmountAmountLessThanAvailable() - { - $this->quoteMock->expects($this->once()) - ->method('getHasError') - ->will($this->returnValue(false)); - - $this->quoteMock->expects($this->never()) - ->method('setHasError'); - - $this->quoteMock->expects($this->never()) - ->method('addMessage'); - - $this->assertSame( - $this->quoteValidator, - $this->quoteValidator->validateQuoteAmount($this->quoteMock, QuoteValidator::MAXIMUM_AVAILABLE_NUMBER - 1) - ); - } - - public function testCheckQuoteAmountAmountGreaterThanAvailable() - { - $this->quoteMock->expects($this->once()) - ->method('getHasError') - ->will($this->returnValue(false)); - - $this->quoteMock->expects($this->once()) - ->method('setHasError') - ->with(true); - - $this->quoteMock->expects($this->once()) - ->method('addMessage') - ->with(__('This item price or quantity is not valid for checkout.')); - - $this->assertSame( - $this->quoteValidator, - $this->quoteValidator->validateQuoteAmount($this->quoteMock, QuoteValidator::MAXIMUM_AVAILABLE_NUMBER + 1) - ); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Please check the shipping address information. - */ - public function testValidateBeforeSubmitThrowsExceptionIfShippingAddressIsInvalid() - { - $shippingAddressMock = $this->createMock(\Magento\Quote\Model\Quote\Address::class); - $this->quoteMock->expects($this->any())->method('getShippingAddress')->willReturn($shippingAddressMock); - $this->quoteMock->expects($this->any())->method('isVirtual')->willReturn(false); - $shippingAddressMock->expects($this->any())->method('validate')->willReturn(['Invalid Shipping Address']); - - $this->quoteValidator->validateBeforeSubmit($this->quoteMock); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage The shipping method is missing. Select the shipping method and try again. - */ - public function testValidateBeforeSubmitThrowsExceptionIfShippingRateIsNotSelected() - { - $shippingMethod = 'checkmo'; - $shippingAddressMock = $this->getMockBuilder(Address::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->allowedCountryReader->method('getAllowedCountries') - ->willReturn(['US' => 'US']); - - $this->quoteMock->expects($this->any())->method('getShippingAddress')->willReturn($shippingAddressMock); - $this->quoteMock->expects($this->any())->method('isVirtual')->willReturn(false); - $shippingAddressMock->expects($this->any())->method('validate')->willReturn(true); - $shippingAddressMock->method('getCountryId') - ->willReturn('US'); - $shippingAddressMock->expects($this->any())->method('getShippingMethod')->willReturn($shippingMethod); - $shippingAddressMock->expects($this->once())->method('getShippingRateByCode')->with($shippingMethod); - - $this->quoteValidator->validateBeforeSubmit($this->quoteMock); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Please check the billing address information. - */ - public function testValidateBeforeSubmitThrowsExceptionIfBillingAddressIsNotValid() - { - $billingAddressMock = $this->createMock(\Magento\Quote\Model\Quote\Address::class); - $this->quoteMock->expects($this->any())->method('getBillingAddress')->willReturn($billingAddressMock); - $this->quoteMock->expects($this->any())->method('isVirtual')->willReturn(true); - $billingAddressMock->expects($this->any())->method('validate')->willReturn(['Invalid Billing Address']); - - $this->quoteValidator->validateBeforeSubmit($this->quoteMock); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Enter a valid payment method and try again. - */ - public function testValidateBeforeSubmitThrowsExceptionIfPaymentMethodIsNotSelected() - { - $paymentMock = $this->createMock(\Magento\Quote\Model\Quote\Payment::class); - $billingAddressMock = $this->createMock(\Magento\Quote\Model\Quote\Address::class); - $billingAddressMock->expects($this->any())->method('validate')->willReturn(true); - - $this->quoteMock->expects($this->any())->method('getBillingAddress')->willReturn($billingAddressMock); - $this->quoteMock->expects($this->any())->method('getPayment')->willReturn($paymentMock); - $this->quoteMock->expects($this->any())->method('isVirtual')->willReturn(true); - - $this->quoteValidator->validateBeforeSubmit($this->quoteMock); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Minimum Order Amount Exceeded. - */ - public function testValidateBeforeSubmitThrowsExceptionIfMinimumOrderAmount() - { - $paymentMock = $this->createMock(\Magento\Quote\Model\Quote\Payment::class); - $paymentMock->expects($this->once())->method('getMethod')->willReturn('checkmo'); - - $billingAddressMock = $this->createMock(\Magento\Quote\Model\Quote\Address::class); - $billingAddressMock->expects($this->any())->method('validate')->willReturn(true); - - $this->quoteMock->expects($this->any())->method('getBillingAddress')->willReturn($billingAddressMock); - $this->quoteMock->expects($this->any())->method('getPayment')->willReturn($paymentMock); - $this->quoteMock->expects($this->any())->method('isVirtual')->willReturn(true); - - $this->quoteMock->expects($this->any())->method('getIsMultiShipping')->willReturn(false); - $this->quoteMock->expects($this->any())->method('validateMinimumAmount')->willReturn(false); - - $this->orderAmountValidationMessage->expects($this->once())->method('getMessage') - ->willReturn(__("Minimum Order Amount Exceeded.")); - - $this->quoteValidator->validateBeforeSubmit($this->quoteMock); - } - - /** - * Test case when country id not present in allowed countries list. - * - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Some addresses can't be used due to the configurations for specific countries. - */ - public function testValidateBeforeSubmitThrowsExceptionIfCountrySpecificConfigurations() - { - $this->allowedCountryReader->method('getAllowedCountries') - ->willReturn(['EE' => 'EE']); - - $addressMock = $this->getMockBuilder(Address::class) - ->disableOriginalConstructor() - ->getMock(); - $addressMock->method('validate') - ->willReturn(true); - $addressMock->method('getCountryId') - ->willReturn('EU'); - - $paymentMock = $this->getMockBuilder(Payment::class) - ->setMethods(['getMethod']) - ->disableOriginalConstructor() - ->getMock(); - $paymentMock->method('getMethod') - ->willReturn(true); - - $billingAddressMock = $this->getMockBuilder(Address::class) - ->disableOriginalConstructor() - ->setMethods(['validate']) - ->getMock(); - $billingAddressMock->method('validate') - ->willReturn(true); - - $this->quoteMock->method('getShippingAddress') - ->willReturn($addressMock); - $this->quoteMock->method('isVirtual') - ->willReturn(false); - $this->quoteMock->method('getBillingAddress') - ->willReturn($billingAddressMock); - $this->quoteMock->method('getPayment') - ->willReturn($paymentMock); - - $this->quoteValidator->validateBeforeSubmit($this->quoteMock); - } } diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml index 3ad6c9c40e359..d9398ad538bc0 100644 --- a/app/code/Magento/Quote/etc/di.xml +++ b/app/code/Magento/Quote/etc/di.xml @@ -110,27 +110,27 @@ </type> <type name="Magento\Quote\Model\ValidationRules\AllowedCountryValidationRule"> <arguments> - <argument name="defaultMessage" xsi:type="string">Some addresses can't be used due to the configurations for specific countries.</argument> + <argument name="generalMessage" xsi:type="string" translatable="true">Some addresses can't be used due to the configurations for specific countries.</argument> </arguments> </type> <type name="Magento\Quote\Model\ValidationRules\ShippingAddressValidationRule"> <arguments> - <argument name="defaultMessage" xsi:type="string">Please check the shipping address information.</argument> + <argument name="generalMessage" xsi:type="string" translatable="true">Please check the shipping address information.</argument> </arguments> </type> <type name="Magento\Quote\Model\ValidationRules\ShippingMethodValidationRule"> <arguments> - <argument name="defaultMessage" xsi:type="string">The shipping method is missing. Select the shipping method and try again.</argument> + <argument name="generalMessage" xsi:type="string" translatable="true">The shipping method is missing. Select the shipping method and try again.</argument> </arguments> </type> <type name="Magento\Quote\Model\ValidationRules\BillingAddressValidationRule"> <arguments> - <argument name="defaultMessage" xsi:type="string">Please check the billing address information.</argument> + <argument name="generalMessage" xsi:type="string" translatable="true">Please check the billing address information.</argument> </arguments> </type> <type name="Magento\Quote\Model\ValidationRules\PaymentMethodValidationRule"> <arguments> - <argument name="defaultMessage" xsi:type="string">Enter a valid payment method and try again.</argument> + <argument name="generalMessage" xsi:type="string" translatable="true">Enter a valid payment method and try again.</argument> </arguments> </type> </config> diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php index 981cd106853ca..efd39da95d3aa 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php @@ -11,7 +11,7 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Class QuoteValidatorTest + * Class QuoteValidatorTest. * * @magentoDbIsolation enabled */ @@ -106,7 +106,8 @@ public function testValidateBeforeSubmitPaymentMethodInvalid() public function testValidateBeforeSubmitMinimumAmountInvalid() { $quote = $this->getQuote(); - + $quote->getShippingAddress() + ->setBaseSubtotal(0); $this->quoteValidator->validateBeforeSubmit($quote); } @@ -118,6 +119,18 @@ public function testValidateBeforeSubmitWithoutMinimumOrderAmount() $this->quoteValidator->validateBeforeSubmit($this->getQuote()); } + /** + * @magentoConfigFixture current_store sales/minimum_order/active 1 + * @magentoConfigFixture current_store sales/minimum_order/amount 100 + */ + public function testValidateBeforeSubmitWithMinimumOrderAmount() + { + $quote = $this->getQuote(); + $quote->getShippingAddress() + ->setBaseSubtotal(200); + $this->quoteValidator->validateBeforeSubmit($quote); + } + /** * @return Quote */ @@ -171,4 +184,4 @@ private function getQuote(): Quote return $quote; } -} \ No newline at end of file +} From fdc446ede5e776402fc9194c88ca3d85d620fb80 Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Fri, 17 Aug 2018 10:53:22 +0400 Subject: [PATCH 1042/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Updated automated test. Renamed Action Group. --- .../Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml | 2 +- .../Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml index 49edf529a188d..feabe2e28b94f 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml @@ -31,7 +31,7 @@ <see userInput="You saved the product." stepKey="seeSaveConfirmation"/> </actionGroup> - <actionGroup name="FindAndAddProductToCardActionGroup"> + <actionGroup name="FindAndAddProductToCardActGr"> <click selector="{{StorefrontAddProductToCartSection.addToCartBtn}}" stepKey="addToCart"/> <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" time="30" stepKey="waitForProductAdded"/> <click selector="{{StorefrontAddProductToCartSection.showCard}}" stepKey="clickToOpenCard"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml index b8abda3b37de6..3927ecf21698e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml @@ -36,7 +36,7 @@ delete_cookie('form_key');" stepKey="removeCookies" after="waitForPageLoad"/> <!-- "Add to Cart" any product--> - <actionGroup ref="FindAndAddProductToCardActionGroup" stepKey="addProductToCard"/> + <actionGroup ref="FindAndAddProductToCardActGr" stepKey="addProductToCard"/> <after> <!--Delete created product--> From c01370db8df0eb82f3ebe2902ce19caa2e6eb4df Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Fri, 17 Aug 2018 12:50:12 +0400 Subject: [PATCH 1043/1171] MAGETWO-91701: Newsletter subscription is not correctly updated when user is registered on 2 stores - Updated automated test. Using Search Filter for deleting created customer. --- .../VerifySubscribedNewsletterDisplayedActionGroup.xml | 9 ++++++--- .../VerifySubscribedNewsLetterDisplayedSection.xml | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml index 941bcbd2d357a..b76d3e3022248 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml @@ -143,9 +143,12 @@ <wait stepKey="WaitForCustomerViewOpened" time="2"/> <click stepKey="clickCustomerAllCustomerItem" selector="{{Dashboard.customerAllCustomer}}"/> <waitForPageLoad stepKey="WaitForCustomerPageIsLoaded"/> - <fillField stepKey="searchToKeyword" selector="{{AdminCustomerAccountInformationSection.searchToKeyword}}" userInput="{{CreateUserData.firstName}}"/> - <click stepKey="clickSearchButton" selector="{{AdminCustomerAccountInformationSection.searchButton}}"/> - <waitForElementVisible stepKey="waitForFiltering" selector="{{AdminCustomerAccountInformationSection.selectCustomer}}"/> + <conditionalClick selector="{{AdminCustomerAccountInformationSection.clearAll}}" dependentSelector="{{AdminCustomerAccountInformationSection.clearAll}}" visible="1" stepKey="clickClearAllIfThereIsAnyValue"/> + <click stepKey="clickFilterButton" selector="{{AdminCustomerAccountInformationSection.filterButton}}"/> + <waitForElementVisible selector="{{AdminCustomerAccountInformationSection.filterNameField}}" stepKey="waitForFilterDataLoaded"/> + <fillField stepKey="searchProductUsingNameField" selector="{{AdminCustomerAccountInformationSection.filterNameField}}" userInput="{{CreateUserData.firstName}}"/> + <click stepKey="clickFiltersApplyButton" selector="{{AdminCustomerAccountInformationSection.filtersApplyButton}}"/> + <waitForElementNotVisible selector="{{AdminCustomerAccountInformationSection.filterNameField}}" stepKey="waitForFilterBecomeNotVisible"/> <click selector="{{AdminCustomerAccountInformationSection.selectCustomer}}" stepKey="ClickOnCustomer"/> <click selector="{{AdminCustomerAccountInformationSection.actions}}" stepKey="ClickOnActions"/> <waitForElementVisible selector="{{AdminCustomerAccountInformationSection.delete}}" stepKey="waitForDeleteButtonAppeared"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml index 1af5c142e2cd7..96c3398058742 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml @@ -75,6 +75,9 @@ <element name="delete" type="button" selector="//div[@class='col-xs-2']//span[text()='Delete']"/> <element name="confirm" type="button" selector=".action-primary.action-accept"/> <element name="clearAll" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[contains(text(), 'Clear all')]"/> + <element name="filterButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-filters-action-wrap']/button"/> + <element name="filterNameField" type="input" selector="//*[@name='name']"/> + <element name="filtersApplyButton" type="button" selector="//*[contains(text(),'Apply Filters')]"/> </section> </sections> \ No newline at end of file From 3a942809cf3f73114b5236e4f78bf2adf71411ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20der=20Linden?= <daniel.van.der.linden@guapa.nl> Date: Fri, 17 Aug 2018 12:47:45 +0200 Subject: [PATCH 1044/1171] Fix #17579 Typo in Gallery.php --- .../Catalog/Block/Adminhtml/Product/Helper/Form/Gallery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery.php index 0557a215383d3..6cb6f0e526e9f 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery.php @@ -117,7 +117,7 @@ public function getContentHtml() $content->setId($this->getHtmlId() . '_content')->setElement($this); $content->setFormName($this->formName); $galleryJs = $content->getJsObjectName(); - $content->getUploader()->getConfig()->setMegiaGallery($galleryJs); + $content->getUploader()->getConfig()->setMediaGallery($galleryJs); return $content->toHtml(); } From 8f4a2920d25dd523059182b7325768634e1e9aff Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Fri, 17 Aug 2018 13:58:55 +0300 Subject: [PATCH 1045/1171] MAGETWO-91757: [2.3] Default merchant account ID is used on subsequent partial invoices --- .../Request/MerchantAccountDataBuilder.php | 64 ++++++++ .../Gateway/Request/PaymentDataBuilder.php | 21 +-- .../Request/PaymentDataBuilderTest.php | 41 +++-- .../Magento/Braintree/etc/adminhtml/di.xml | 2 + app/code/Magento/Braintree/etc/di.xml | 5 + .../Order/Payment/Transaction/Repository.php | 11 +- .../Payment/Transaction/RepositoryTest.php | 69 +++++--- .../Adminhtml/Invoice/CreateTest.php | 151 ++++++++++++++++++ .../Magento/Braintree/Fixtures/order.php | 57 +++++++ .../Braintree/Fixtures/order_rollback.php | 28 ++++ .../Braintree/Fixtures/partial_invoice.php | 47 ++++++ .../Fixtures/partial_invoice_rollback.php | 28 ++++ .../Magento/Braintree/Fixtures/payment.php | 29 ++++ .../Adminhtml/Order/AddCommentTest.php | 16 -- .../Adminhtml/Order/AddressSaveTest.php | 16 -- .../Adminhtml/Order/AddressTest.php | 16 -- .../Controller/Adminhtml/Order/CancelTest.php | 16 -- .../Controller/Adminhtml/Order/EmailTest.php | 16 -- .../Controller/Adminhtml/Order/HoldTest.php | 16 -- .../Adminhtml/Order/ReviewPaymentTest.php | 16 -- .../Controller/Adminhtml/Order/UnholdTest.php | 16 -- .../Controller/Adminhtml/Order/ViewTest.php | 16 -- .../Adminhtml/Transactions/FetchTest.php | 18 --- .../testsuite/Magento/Vault/_files/token.php | 2 +- 24 files changed, 490 insertions(+), 227 deletions(-) create mode 100644 app/code/Magento/Braintree/Gateway/Request/MerchantAccountDataBuilder.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Controller/Adminhtml/Invoice/CreateTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddCommentTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressSaveTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CancelTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/EmailTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/HoldTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ReviewPaymentTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/UnholdTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ViewTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Transactions/FetchTest.php diff --git a/app/code/Magento/Braintree/Gateway/Request/MerchantAccountDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/MerchantAccountDataBuilder.php new file mode 100644 index 0000000000000..6dc40e76322df --- /dev/null +++ b/app/code/Magento/Braintree/Gateway/Request/MerchantAccountDataBuilder.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Gateway\Request; + +use Magento\Braintree\Gateway\Config\Config; +use Magento\Braintree\Gateway\SubjectReader; +use Magento\Payment\Gateway\Request\BuilderInterface; + +/** + * Adds Merchant Account ID to the request if it was specified in the configuration. + */ +class MerchantAccountDataBuilder implements BuilderInterface +{ + /** + * The merchant account ID used to create a transaction. + * Currency is also determined by merchant account ID. + * If no merchant account ID is specified, Braintree will use your default merchant account. + */ + private static $merchantAccountId = 'merchantAccountId'; + + /** + * @var Config + */ + private $config; + + /** + * @var SubjectReader + */ + private $subjectReader; + + /** + * Constructor + * + * @param Config $config + * @param SubjectReader $subjectReader + */ + public function __construct(Config $config, SubjectReader $subjectReader) + { + $this->config = $config; + $this->subjectReader = $subjectReader; + } + + /** + * @inheritdoc + */ + public function build(array $buildSubject): array + { + $paymentDO = $this->subjectReader->readPayment($buildSubject); + $order = $paymentDO->getOrder(); + + $result = []; + $merchantAccountId = $this->config->getMerchantAccountId($order->getStoreId()); + if (!empty($merchantAccountId)) { + $result[self::$merchantAccountId] = $merchantAccountId; + } + + return $result; + } +} diff --git a/app/code/Magento/Braintree/Gateway/Request/PaymentDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/PaymentDataBuilder.php index 85a0c64451398..fe75ce86cca2f 100644 --- a/app/code/Magento/Braintree/Gateway/Request/PaymentDataBuilder.php +++ b/app/code/Magento/Braintree/Gateway/Request/PaymentDataBuilder.php @@ -6,8 +6,8 @@ namespace Magento\Braintree\Gateway\Request; use Magento\Braintree\Gateway\Config\Config; -use Magento\Braintree\Observer\DataAssignObserver; use Magento\Braintree\Gateway\SubjectReader; +use Magento\Braintree\Observer\DataAssignObserver; use Magento\Payment\Gateway\Request\BuilderInterface; use Magento\Payment\Helper\Formatter; @@ -36,9 +36,8 @@ class PaymentDataBuilder implements BuilderInterface const PAYMENT_METHOD_NONCE = 'paymentMethodNonce'; /** - * The merchant account ID used to create a transaction. - * Currency is also determined by merchant account ID. - * If no merchant account ID is specified, Braintree will use your default merchant account. + * @deprecated + * @see \Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder */ const MERCHANT_ACCOUNT_ID = 'merchantAccountId'; @@ -47,25 +46,18 @@ class PaymentDataBuilder implements BuilderInterface */ const ORDER_ID = 'orderId'; - /** - * @var Config - */ - private $config; - /** * @var SubjectReader */ private $subjectReader; /** - * Constructor - * * @param Config $config * @param SubjectReader $subjectReader + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct(Config $config, SubjectReader $subjectReader) { - $this->config = $config; $this->subjectReader = $subjectReader; } @@ -87,11 +79,6 @@ public function build(array $buildSubject) self::ORDER_ID => $order->getOrderIncrementId() ]; - $merchantAccountId = $this->config->getMerchantAccountId($order->getStoreId()); - if (!empty($merchantAccountId)) { - $result[self::MERCHANT_ACCOUNT_ID] = $merchantAccountId; - } - return $result; } } diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php index 76ab8b8b53b3f..5620e8ffa92b8 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php @@ -5,14 +5,14 @@ */ namespace Magento\Braintree\Test\Unit\Gateway\Request; -use Magento\Braintree\Gateway\Config\Config; -use Magento\Braintree\Gateway\SubjectReader; use Magento\Braintree\Gateway\Request\PaymentDataBuilder; +use Magento\Braintree\Gateway\SubjectReader; use Magento\Braintree\Observer\DataAssignObserver; use Magento\Payment\Gateway\Data\OrderAdapterInterface; use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; use Magento\Sales\Model\Order\Payment; use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Magento\Braintree\Gateway\Config\Config; /** * Tests \Magento\Braintree\Gateway\Request\PaymentDataBuilder. @@ -20,18 +20,12 @@ class PaymentDataBuilderTest extends \PHPUnit\Framework\TestCase { const PAYMENT_METHOD_NONCE = 'nonce'; - const MERCHANT_ACCOUNT_ID = '245345'; /** * @var PaymentDataBuilder */ private $builder; - /** - * @var Config|MockObject - */ - private $configMock; - /** * @var Payment|MockObject */ @@ -52,12 +46,12 @@ class PaymentDataBuilderTest extends \PHPUnit\Framework\TestCase */ private $orderMock; + /** + * @inheritdoc + */ protected function setUp() { $this->paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->configMock = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->getMock(); $this->paymentMock = $this->getMockBuilder(Payment::class) ->disableOriginalConstructor() ->getMock(); @@ -66,13 +60,19 @@ protected function setUp() ->getMock(); $this->orderMock = $this->createMock(OrderAdapterInterface::class); - $this->builder = new PaymentDataBuilder($this->configMock, $this->subjectReaderMock); + /** @var Config $config */ + $config = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->builder = new PaymentDataBuilder($config, $this->subjectReaderMock); } /** + * @return void * @expectedException \InvalidArgumentException */ - public function testBuildReadPaymentException() + public function testBuildReadPaymentException(): void { $buildSubject = []; @@ -85,9 +85,10 @@ public function testBuildReadPaymentException() } /** + * @return void * @expectedException \InvalidArgumentException */ - public function testBuildReadAmountException() + public function testBuildReadAmountException(): void { $buildSubject = [ 'payment' => $this->paymentDOMock, @@ -106,7 +107,10 @@ public function testBuildReadAmountException() $this->builder->build($buildSubject); } - public function testBuild() + /** + * @return void + */ + public function testBuild(): void { $additionalData = [ [ @@ -118,8 +122,7 @@ public function testBuild() $expectedResult = [ PaymentDataBuilder::AMOUNT => 10.00, PaymentDataBuilder::PAYMENT_METHOD_NONCE => self::PAYMENT_METHOD_NONCE, - PaymentDataBuilder::ORDER_ID => '000000101', - PaymentDataBuilder::MERCHANT_ACCOUNT_ID => self::MERCHANT_ACCOUNT_ID, + PaymentDataBuilder::ORDER_ID => '000000101' ]; $buildSubject = [ @@ -131,10 +134,6 @@ public function testBuild() ->method('getAdditionalInformation') ->willReturnMap($additionalData); - $this->configMock->expects(self::once()) - ->method('getMerchantAccountId') - ->willReturn(self::MERCHANT_ACCOUNT_ID); - $this->paymentDOMock->expects(self::once()) ->method('getPayment') ->willReturn($this->paymentMock); diff --git a/app/code/Magento/Braintree/etc/adminhtml/di.xml b/app/code/Magento/Braintree/etc/adminhtml/di.xml index 7a803f803ae89..9de1ad48d2261 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/di.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/di.xml @@ -29,6 +29,7 @@ <item name="vault" xsi:type="string">Magento\Braintree\Gateway\Request\VaultDataBuilder</item> <item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> @@ -41,6 +42,7 @@ <item name="address" xsi:type="string">Magento\Braintree\Gateway\Request\AddressDataBuilder</item> <item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> diff --git a/app/code/Magento/Braintree/etc/di.xml b/app/code/Magento/Braintree/etc/di.xml index 290fb5be58f34..67c90e6991e28 100644 --- a/app/code/Magento/Braintree/etc/di.xml +++ b/app/code/Magento/Braintree/etc/di.xml @@ -233,6 +233,7 @@ <item name="device_data" xsi:type="string">Magento\Braintree\Gateway\Request\KountPaymentDataBuilder</item> <item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> @@ -291,6 +292,7 @@ <item name="device_data" xsi:type="string">Magento\Braintree\Gateway\Request\KountPaymentDataBuilder</item> <item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> @@ -325,6 +327,7 @@ <item name="vault_capture" xsi:type="string">Magento\Braintree\Gateway\Request\VaultCaptureDataBuilder</item> <item name="settlement" xsi:type="string">Magento\Braintree\Gateway\Request\SettlementDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> @@ -347,6 +350,7 @@ <item name="device_data" xsi:type="string">Magento\Braintree\Gateway\Request\PayPal\DeviceDataBuilder</item> <item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> @@ -380,6 +384,7 @@ <item name="address" xsi:type="string">Magento\Braintree\Gateway\Request\AddressDataBuilder</item> <item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> diff --git a/app/code/Magento/Sales/Model/Order/Payment/Transaction/Repository.php b/app/code/Magento/Sales/Model/Order/Payment/Transaction/Repository.php index 3caae611d9551..a602fe54363ed 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Transaction/Repository.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Transaction/Repository.php @@ -13,14 +13,11 @@ use Magento\Framework\Data\Collection; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Sales\Api\Data\TransactionInterface; -use Magento\Sales\Api\OrderPaymentRepositoryInterface; -use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Api\Data\TransactionSearchResultInterfaceFactory as SearchResultFactory; use Magento\Sales\Api\TransactionRepositoryInterface; use Magento\Sales\Model\EntityStorage; use Magento\Sales\Model\EntityStorageFactory; -use Magento\Sales\Model\Order\Payment; use Magento\Sales\Model\ResourceModel\Metadata; -use Magento\Sales\Api\Data\TransactionSearchResultInterfaceFactory as SearchResultFactory; use Magento\Sales\Model\ResourceModel\Order\Payment\Transaction as TransactionResource; /** @@ -95,7 +92,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function get($id) { @@ -117,12 +114,10 @@ public function get($id) /** * @param int $transactionType * @param int $paymentId - * @param int $orderId * @return bool|\Magento\Framework\Model\AbstractModel|mixed * @throws \Magento\Framework\Exception\InputException - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getByTransactionType($transactionType, $paymentId, $orderId) + public function getByTransactionType($transactionType, $paymentId) { $identityFieldsForCache = [$transactionType, $paymentId]; $cacheStorage = 'txn_type'; diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Transaction/RepositoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Transaction/RepositoryTest.php index 95966b138097c..e9bda29900858 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Transaction/RepositoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Transaction/RepositoryTest.php @@ -122,14 +122,20 @@ protected function setUp() ); } - public function testCreate() + /** + * @return void + */ + public function testCreate(): void { $expected = "expect"; $this->metaData->expects($this->once())->method('getNewInstance')->willReturn($expected); $this->assertEquals($expected, $this->repository->create()); } - public function testSave() + /** + * @return void + */ + public function testSave(): void { $transactionId = 12; $transaction = $this->mockTransaction($transactionId); @@ -142,7 +148,10 @@ public function testSave() $this->assertSame($transaction, $this->repository->save($transaction)); } - public function testDelete() + /** + * @return void + */ + public function testDelete(): void { $transactionId = 12; $transaction = $this->mockTransaction($transactionId); @@ -152,7 +161,10 @@ public function testDelete() $this->assertTrue($this->repository->delete($transaction)); } - public function testGet() + /** + * @return void + */ + public function testGet(): void { $transactionId = 12; $transaction = $this->mockTransaction($transactionId); @@ -165,22 +177,24 @@ public function testGet() } /** + * @return void * @expectedException \Magento\Framework\Exception\InputException * @throws \Magento\Framework\Exception\InputException * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testGetException() + public function testGetException(): void { $transactionId = null; $this->repository->get($transactionId); } /** + * @return void * @expectedException \Magento\Framework\Exception\NoSuchEntityException * @throws \Magento\Framework\Exception\InputException * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testGetNoSuchEntity() + public function testGetNoSuchEntity(): void { $transactionId = null; $transactionIdFromArgument = 12; @@ -193,7 +207,10 @@ public function testGetNoSuchEntity() $this->assertSame($transaction, $this->repository->get(12)); } - public function testGetExistInStorage() + /** + * @return void + */ + public function testGetExistInStorage(): void { $transactionId = 12; $transaction = "transaction"; @@ -206,7 +223,10 @@ public function testGetExistInStorage() $this->assertSame($transaction, $this->repository->get($transactionId)); } - public function testGetList() + /** + * @return void + */ + public function testGetList(): void { $this->initListMock(); $this->collectionProcessor->expects($this->once()) @@ -215,7 +235,10 @@ public function testGetList() $this->assertSame($this->collection, $this->repository->getList($this->searchCriteria)); } - public function testGetByTransactionId() + /** + * @return void + */ + public function testGetByTransactionId(): void { $transactionId = "100-refund"; $paymentId = 1; @@ -241,7 +264,10 @@ public function testGetByTransactionId() $this->assertEquals($transaction, $this->repository->getByTransactionId($transactionId, $paymentId, $orderId)); } - public function testGetByTransactionIdNotFound() + /** + * @return void + */ + public function testGetByTransactionIdNotFound(): void { $transactionId = "100-refund"; $paymentId = 1; @@ -267,7 +293,10 @@ public function testGetByTransactionIdNotFound() ); } - public function testGetByTransactionIdFromStorage() + /** + * @return void + */ + public function testGetByTransactionIdFromStorage(): void { $transactionId = "100-refund"; $paymentId = 1; @@ -284,11 +313,13 @@ public function testGetByTransactionIdFromStorage() ); } - public function testGetByTransactionType() + /** + * @return void + */ + public function testGetByTransactionType(): void { $transactionType = Transaction::TYPE_AUTH; $paymentId = 1; - $orderId = 3; $cacheStorage = 'txn_type'; $identityFieldsForCache = [$transactionType, $paymentId]; $this->entityStorage->expects($this->once()) @@ -341,15 +372,17 @@ public function testGetByTransactionType() ->with($transaction, $identityFieldsForCache, $cacheStorage); $this->assertEquals( $transaction, - $this->repository->getByTransactionType($transactionType, $paymentId, $orderId) + $this->repository->getByTransactionType($transactionType, $paymentId) ); } - public function testGetByTransactionTypeFromCache() + /** + * @return void + */ + public function testGetByTransactionTypeFromCache(): void { $transactionType = Transaction::TYPE_AUTH; $paymentId = 1; - $orderId = 3; $cacheStorage = 'txn_type'; $transaction = "transaction"; $identityFieldsForCache = [$transactionType, $paymentId]; @@ -358,7 +391,7 @@ public function testGetByTransactionTypeFromCache() ->willReturn($transaction); $this->assertEquals( $transaction, - $this->repository->getByTransactionType($transactionType, $paymentId, $orderId) + $this->repository->getByTransactionType($transactionType, $paymentId) ); } @@ -379,7 +412,7 @@ protected function mockTransaction($transactionId, $withoutTransactionIdMatcher /** * @return void */ - protected function initListMock() + protected function initListMock(): void { $this->searchResultFactory->method('create')->willReturn($this->collection); $this->collection->expects($this->once())->method('addPaymentInformation')->with(['method']); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Controller/Adminhtml/Invoice/CreateTest.php b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Adminhtml/Invoice/CreateTest.php new file mode 100644 index 0000000000000..d6ea08a2f7ca3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Adminhtml/Invoice/CreateTest.php @@ -0,0 +1,151 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Controller\Adminhtml\Invoice; + +use Braintree\Result\Successful; +use Braintree\Transaction; +use Magento\Braintree\Model\Adapter\BraintreeAdapter; +use Magento\Braintree\Model\Adapter\BraintreeAdapterFactory; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Message\MessageInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * @magentoAppArea adminhtml + */ +class CreateTest extends AbstractBackendController +{ + /** + * @var BraintreeAdapter|MockObject + */ + private $adapter; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $adapterFactory = $this->getMockBuilder(BraintreeAdapterFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->adapter = $this->getMockBuilder(BraintreeAdapter::class) + ->disableOriginalConstructor() + ->getMock(); + $adapterFactory->method('create') + ->willReturn($this->adapter); + + $this->_objectManager->addSharedInstance($adapterFactory, BraintreeAdapterFactory::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->_objectManager->removeSharedInstance(BraintreeAdapterFactory::class); + parent::tearDown(); + } + + /** + * Checks a case when non default Merchant Account ID should be send to Braintree + * during creation second partial invoice. + * + * @return void + * @magentoConfigFixture default_store payment/braintree/merchant_account_id Magneto + * @magentoConfigFixture current_store payment/braintree/merchant_account_id USA_Merchant + * @magentoDataFixture Magento/Braintree/Fixtures/partial_invoice.php + */ + public function testCreatePartialInvoiceWithNonDefaultMerchantAccount(): void + { + $order = $this->getOrder('100000002'); + + $this->adapter->method('sale') + ->with(self::callback(function ($request) { + self::assertEquals('USA_Merchant', $request['merchantAccountId']); + return true; + })) + ->willReturn($this->getTransactionStub()); + + $uri = 'backend/sales/order_invoice/save/order_id/' . $order->getEntityId(); + $this->prepareRequest($uri); + $this->dispatch($uri); + + self::assertSessionMessages( + self::equalTo(['The invoice has been created.']), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Creates stub for Braintree capture Transaction. + * + * @return Successful + */ + private function getTransactionStub(): Successful + { + $transaction = $this->getMockBuilder(Transaction::class) + ->disableOriginalConstructor() + ->getMock(); + $transaction->status = 'submitted_for_settlement'; + $response = new Successful(); + $response->success = true; + $response->transaction = $transaction; + + return $response; + } + + /** + * Gets order by increment ID. + * + * @param string $incrementId + * @return OrderInterface + */ + private function getOrder(string $incrementId): OrderInterface + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('increment_id', $incrementId) + ->create(); + + /** @var OrderRepositoryInterface $repository */ + $repository = $this->_objectManager->get(OrderRepositoryInterface::class); + $items = $repository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } + + /** + * Prepares POST request for invoice creation. + * + * @param string $uri + * @return void + */ + private function prepareRequest(string $uri): void + { + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); + $request = $this->getRequest(); + $request->setMethod('POST'); + $request->setParam('form_key', $formKey->getFormKey()); + $request->setRequestUri($uri); + $request->setPostValue( + [ + 'invoice' => [ + 'capture_case' => 'online' + ] + ] + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order.php new file mode 100644 index 0000000000000..ceb90710ed5e7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Item as OrderItem; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +$addressData = include __DIR__ . '/../../Sales/_files/address_data.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; + +$billingAddress = $objectManager->create(Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); + +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null) + ->setAddressType('shipping'); + +/** @var OrderItem $orderItem */ +$orderItem = $objectManager->create(OrderItem::class); +$orderItem->setProductId($product->getId()) + ->setQtyOrdered(2) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType('simple'); + +require __DIR__ . '/payment.php'; + +$order = $objectManager->create(Order::class); +$order->setIncrementId('100000002') + ->setSubtotal($product->getPrice() * 2) + ->setBaseSubtotal($product->getPrice() * 2) + ->setCustomerEmail('admin@example.com') + ->setCustomerIsGuest(true) + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId( + $objectManager->get(StoreManagerInterface::class)->getStore() + ->getId() + ) + ->addItem($orderItem) + ->setPayment($payment); + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order_rollback.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order_rollback.php new file mode 100644 index 0000000000000..a2da0b639e98d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\TestFramework\ObjectManager; + +$objectManager = ObjectManager::getInstance(); + +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); +$searchCriteria = $searchCriteriaBuilder->addFilter('increment_id', '100000002') + ->create(); + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); +$items = $orderRepository->getList($searchCriteria) + ->getItems(); + +foreach ($items as $item) { + $orderRepository->delete($item); +} + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice.php new file mode 100644 index 0000000000000..22b954515f3b5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Sales\Api\InvoiceRepositoryInterface; +use Magento\Sales\Api\TransactionRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Service\InvoiceService; +use Magento\TestFramework\ObjectManager; + +/** @var Order $order */ + +require __DIR__ . '/order.php'; + +$objectManager = ObjectManager::getInstance(); + +/** @var InvoiceService $invoiceService */ +$invoiceService = $objectManager->get(InvoiceService::class); +$invoice = $invoiceService->prepareInvoice($order); +$invoice->setIncrementId('100000002'); +$invoice->register(); + +$items = $invoice->getAllItems(); +$item = array_pop($items); +$item->setQty(1); +$invoice->setTotalQty(1); + +$items = $order->getAllItems(); +/** @var \Magento\Sales\Api\Data\OrderItemInterface $item */ +$item = array_pop($items); +$item->setQtyInvoiced(1); +$invoice->collectTotals(); + +/** @var InvoiceRepositoryInterface $invoiceRepository */ +$invoiceRepository = $objectManager->get(InvoiceRepositoryInterface::class); +$invoice = $invoiceRepository->save($invoice); + +/** @var TransactionRepositoryInterface $transactionRepository */ +$transactionRepository = $objectManager->get(TransactionRepositoryInterface::class); +$transaction = $transactionRepository->create(); +$transaction->setTxnType('capture'); +$transaction->setPaymentId($order->getPayment()->getEntityId()); +$transaction->setOrderId($order->getEntityId()); +$transactionRepository->save($transaction); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice_rollback.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice_rollback.php new file mode 100644 index 0000000000000..1ed4438f87db2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Sales\Api\InvoiceRepositoryInterface; +use Magento\TestFramework\ObjectManager; + +$objectManager = ObjectManager::getInstance(); + +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); +$searchCriteria = $searchCriteriaBuilder->addFilter('increment_id', '%10000000%', 'like') + ->create(); + +/** @var InvoiceRepositoryInterface $invoiceRepository */ +$invoiceRepository = $objectManager->get(InvoiceRepositoryInterface::class); +$items = $invoiceRepository->getList($searchCriteria) + ->getItems(); + +foreach ($items as $item) { + $invoiceRepository->delete($item); +} + +require __DIR__ . '/order_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment.php new file mode 100644 index 0000000000000..a4285b963bffa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Braintree\Model\Ui\ConfigProvider; +use Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory; +use Magento\Sales\Model\Order\Payment; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +require __DIR__ . '/../../Vault/_files/token.php'; + +$token->setPaymentMethodCode(ConfigProvider::CODE); +/** @var OrderPaymentExtensionInterfaceFactory $paymentExtensionFactory */ +$paymentExtensionFactory = $objectManager->get(OrderPaymentExtensionInterfaceFactory::class); +$extensionAttributes = $paymentExtensionFactory->create(); +$extensionAttributes->setVaultPaymentToken($token); + +/** @var Payment $payment */ +$payment = $objectManager->create(Payment::class); +$payment->setMethod(ConfigProvider::CODE); +$payment->setExtensionAttributes($extensionAttributes); +$payment->setAuthorizationTransaction(true); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddCommentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddCommentTest.php deleted file mode 100644 index 020c9f51bb10f..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddCommentTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class AddCommentTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::comment'; - $this->uri = 'backend/sales/order/addcomment'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressSaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressSaveTest.php deleted file mode 100644 index 9cdd84a5971f3..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressSaveTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class AddressSaveTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::actions_edit'; - $this->uri = 'backend/sales/order/addresssave'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressTest.php deleted file mode 100644 index 73e46b513287f..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class AddressTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::actions_edit'; - $this->uri = 'backend/sales/order/address'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CancelTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CancelTest.php deleted file mode 100644 index c24191fe5e45e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CancelTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class CancelTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::cancel'; - $this->uri = 'backend/sales/order/cancel'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/EmailTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/EmailTest.php deleted file mode 100644 index 70fbb2ee9a5bd..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/EmailTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class EmailTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::email'; - $this->uri = 'backend/sales/order/email'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/HoldTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/HoldTest.php deleted file mode 100644 index 4c90939d75b2d..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/HoldTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class HoldTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::hold'; - $this->uri = 'backend/sales/order/hold'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ReviewPaymentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ReviewPaymentTest.php deleted file mode 100644 index 96d197628584a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ReviewPaymentTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class ReviewPaymentTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::review_payment'; - $this->uri = 'backend/sales/order/reviewpayment'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/UnholdTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/UnholdTest.php deleted file mode 100644 index 351801d8a0558..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/UnholdTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class UnholdTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::unhold'; - $this->uri = 'backend/sales/order/unhold'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ViewTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ViewTest.php deleted file mode 100644 index 0374edfaad9d9..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ViewTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class ViewTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::actions_view'; - $this->uri = 'backend/sales/order/view'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Transactions/FetchTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Transactions/FetchTest.php deleted file mode 100644 index 7b9481db7997a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Transactions/FetchTest.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Transactions; - -use Magento\TestFramework\TestCase\AbstractBackendController; - -class FetchTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::transactions_fetch'; - $this->uri = 'backend/sales/transactions/fetch'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Vault/_files/token.php b/dev/tests/integration/testsuite/Magento/Vault/_files/token.php index 396b2a5c20536..e1e0a31da4f5f 100644 --- a/dev/tests/integration/testsuite/Magento/Vault/_files/token.php +++ b/dev/tests/integration/testsuite/Magento/Vault/_files/token.php @@ -23,4 +23,4 @@ /** @var PaymentTokenRepository $tokenRepository */ $tokenRepository = $objectManager->create(PaymentTokenRepository::class); -$tokenRepository->save($token); +$token = $tokenRepository->save($token); From 5d91fa483637ace3e5ca332790d2faf19c543b61 Mon Sep 17 00:00:00 2001 From: Shcherbatykh Nikita <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 17 Aug 2018 14:06:35 +0300 Subject: [PATCH 1046/1171] MAGETWO-64315: [API] catalogProductAttributeRepository does not return "frontend_labels" value --- .../Model/Product/Attribute/Repository.php | 75 ++++++++++++------ .../Product/Attribute/RepositoryTest.php | 21 ++--- .../Entity/Attribute/AbstractAttribute.php | 22 ++++++ .../Model/ResourceModel/Entity/Attribute.php | 45 ++++++++++- .../Test/Unit/Model/Entity/AttributeTest.php | 35 ++++++++ .../Api/ProductAttributeRepositoryTest.php | 79 +++++++++++++++++-- 6 files changed, 231 insertions(+), 46 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php b/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php index 270a2f229678b..f6d3ca36c1e1e 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php @@ -119,16 +119,7 @@ public function save(\Magento\Catalog\Api\Data\ProductAttributeInterface $attrib $attribute->setIsUserDefined($existingModel->getIsUserDefined()); $attribute->setFrontendInput($existingModel->getFrontendInput()); - if (is_array($attribute->getFrontendLabels())) { - $defaultFrontendLabel = $attribute->getDefaultFrontendLabel(); - $frontendLabel[0] = !empty($defaultFrontendLabel) - ? $defaultFrontendLabel - : $existingModel->getDefaultFrontendLabel(); - foreach ($attribute->getFrontendLabels() as $item) { - $frontendLabel[$item->getStoreId()] = $item->getLabel(); - } - $attribute->setDefaultFrontendLabel($frontendLabel); - } + $this->updateDefaultFrontendLabel($attribute, $existingModel); } else { $attribute->setAttributeId(null); @@ -136,22 +127,10 @@ public function save(\Magento\Catalog\Api\Data\ProductAttributeInterface $attrib throw InputException::requiredField('frontend_label'); } - $frontendLabels = []; - if ($attribute->getDefaultFrontendLabel()) { - $frontendLabels[0] = $attribute->getDefaultFrontendLabel(); - } - if ($attribute->getFrontendLabels() && is_array($attribute->getFrontendLabels())) { - foreach ($attribute->getFrontendLabels() as $label) { - $frontendLabels[$label->getStoreId()] = $label->getLabel(); - } - if (!isset($frontendLabels[0]) || !$frontendLabels[0]) { - throw InputException::invalidFieldValue('frontend_label', null); - } + $frontendLabel = $this->updateDefaultFrontendLabel($attribute, null); - $attribute->setDefaultFrontendLabel($frontendLabels); - } $attribute->setAttributeCode( - $attribute->getAttributeCode() ?: $this->generateCode($frontendLabels[0]) + $attribute->getAttributeCode() ?: $this->generateCode($frontendLabel) ); $this->validateCode($attribute->getAttributeCode()); $this->validateFrontendInput($attribute->getFrontendInput()); @@ -275,4 +254,52 @@ protected function validateFrontendInput($frontendInput) throw InputException::invalidFieldValue('frontend_input', $frontendInput); } } + + /** + * This method sets default frontend value using given default frontend value or frontend value from admin store + * if default frontend value is not presented. + * If both default frontend label and admin store frontend label are not given it throws exception + * for attribute creation process or sets existing attribute value for attribute update action. + * + * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute + * @param \Magento\Catalog\Api\Data\ProductAttributeInterface|null $existingModel + * @return string|null + * @throws InputException + */ + private function updateDefaultFrontendLabel($attribute, $existingModel) + { + $frontendLabel = $attribute->getDefaultFrontendLabel(); + if (empty($frontendLabel)) { + $frontendLabel = $this->extractAdminStoreFrontendLabel($attribute); + if (empty($frontendLabel)) { + if ($existingModel) { + $frontendLabel = $existingModel->getDefaultFrontendLabel(); + } else { + throw InputException::invalidFieldValue('frontend_label', null); + } + } + $attribute->setDefaultFrontendLabel($frontendLabel); + } + return $frontendLabel; + } + + /** + * This method extracts frontend label from FrontendLabel object for admin store. + * + * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute + * @return string|null + */ + private function extractAdminStoreFrontendLabel($attribute) + { + $frontendLabel = []; + $frontendLabels = $attribute->getFrontendLabels(); + if (isset($frontendLabels[0]) + && $frontendLabels[0] instanceof \Magento\Eav\Api\Data\AttributeFrontendLabelInterface + ) { + foreach ($attribute->getFrontendLabels() as $label) { + $frontendLabel[$label->getStoreId()] = $label->getLabel(); + } + } + return $frontendLabel[0] ?? null; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php index 3cc6f94d58c29..e9820b07af1b8 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php @@ -10,7 +10,6 @@ use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Catalog\Model\Product\Attribute\Repository; use Magento\Catalog\Model\ResourceModel\Eav\Attribute; -use Magento\Eav\Api\Data\AttributeFrontendLabelInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -235,9 +234,9 @@ public function testSaveInputExceptionInvalidFieldValue() ); $attributeMock->expects($this->once())->method('getAttributeId')->willReturn(null); $attributeMock->expects($this->once())->method('setAttributeId')->with(null)->willReturnSelf(); - $labelMock = $this->createMock(\Magento\Eav\Api\Data\AttributeFrontendLabelInterface::class); - $attributeMock->expects($this->exactly(4))->method('getFrontendLabels')->willReturn([$labelMock]); - $attributeMock->expects($this->exactly(2))->method('getDefaultFrontendLabel')->willReturn('test'); + $labelMock = $this->createMock(\Magento\Eav\Model\Entity\Attribute\FrontendLabel::class); + $attributeMock->expects($this->any())->method('getFrontendLabels')->willReturn([$labelMock]); + $attributeMock->expects($this->any())->method('getDefaultFrontendLabel')->willReturn(null); $labelMock->expects($this->once())->method('getStoreId')->willReturn(0); $labelMock->expects($this->once())->method('getLabel')->willReturn(null); @@ -260,7 +259,7 @@ public function testSaveDoesNotSaveAttributeOptionsIfOptionsAreAbsentInPayload() ->method('get') ->with(ProductAttributeInterface::ENTITY_TYPE_CODE, $attributeCode) ->willReturn($existingModelMock); - + $existingModelMock->expects($this->once())->method('getDefaultFrontendLabel')->willReturn('default_label'); // Attribute code must not be changed after attribute creation $attributeMock->expects($this->once())->method('setAttributeCode')->with($attributeCode); $this->attributeResourceMock->expects($this->once())->method('save')->with($attributeMock); @@ -271,7 +270,7 @@ public function testSaveDoesNotSaveAttributeOptionsIfOptionsAreAbsentInPayload() public function testSaveSavesDefaultFrontendLabelIfItIsPresentInPayload() { - $labelMock = $this->createMock(AttributeFrontendLabelInterface::class); + $labelMock = $this->createMock(\Magento\Eav\Api\Data\AttributeFrontendLabelInterface::class); $labelMock->expects($this->any())->method('getStoreId')->willReturn(1); $labelMock->expects($this->any())->method('getLabel')->willReturn('Store Scope Label'); @@ -280,11 +279,12 @@ public function testSaveSavesDefaultFrontendLabelIfItIsPresentInPayload() $attributeMock = $this->createMock(Attribute::class); $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('getDefaultFrontendLabel')->willReturn(null); $attributeMock->expects($this->any())->method('getFrontendLabels')->willReturn([$labelMock]); $attributeMock->expects($this->any())->method('getOptions')->willReturn([]); $existingModelMock = $this->createMock(Attribute::class); + $existingModelMock->expects($this->any())->method('getDefaultFrontendLabel')->willReturn('Default Label'); $existingModelMock->expects($this->any())->method('getAttributeId')->willReturn($attributeId); $existingModelMock->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode); @@ -295,12 +295,7 @@ public function testSaveSavesDefaultFrontendLabelIfItIsPresentInPayload() $attributeMock->expects($this->once()) ->method('setDefaultFrontendLabel') - ->with( - [ - 0 => 'Default Label', - 1 => 'Store Scope Label' - ] - ); + ->with('Default Label'); $this->attributeResourceMock->expects($this->once())->method('save')->with($attributeMock); $this->model->save($attributeMock); diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php index f5c7d88919f3c..b8b6d2ae39d64 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php @@ -119,6 +119,11 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens */ protected $dataObjectHelper; + /** + * @var FrontendLabelFactory + */ + private $frontendLabelFactory; + /** * Serializer Instance. * @@ -162,6 +167,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param FrontendLabelFactory|null $frontendLabelFactory * @param \Magento\Eav\Api\Data\AttributeExtensionFactory|null $eavExtensionFactory * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @codeCoverageIgnore @@ -182,6 +188,7 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], + FrontendLabelFactory $frontendLabelFactory = null, \Magento\Eav\Api\Data\AttributeExtensionFactory $eavExtensionFactory = null ) { parent::__construct( @@ -203,6 +210,8 @@ public function __construct( $this->dataObjectHelper = $dataObjectHelper; $this->eavExtensionFactory = $eavExtensionFactory ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Eav\Api\Data\AttributeExtensionFactory::class); + $this->frontendLabelFactory = $frontendLabelFactory + ?: \Magento\Framework\App\ObjectManager::getInstance()->get(FrontendLabelFactory::class); } /** @@ -1234,6 +1243,19 @@ public function setDefaultFrontendLabel($defaultFrontendLabel) */ public function getFrontendLabels() { + if ($this->getData(self::FRONTEND_LABELS) == null) { + $attributeId = $this->getAttributeId(); + $storeLabels = $this->_getResource()->getStoreLabelsByAttributeId($attributeId); + + $resultFrontedLabels = []; + foreach ($storeLabels as $i => $label) { + $frontendLabel = $this->frontendLabelFactory->create(); + $frontendLabel->setStoreId($i); + $frontendLabel->setLabel($label); + $resultFrontedLabels[] = $frontendLabel; + } + $this->setData(self::FRONTEND_LABELS, $resultFrontedLabels); + } return $this->_getData(self::FRONTEND_LABELS); } diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php index 25858f6a3454d..88f58c6d0111c 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php +++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php @@ -171,10 +171,10 @@ protected function _beforeSave(AbstractModel $object) { $frontendLabel = $object->getFrontendLabel(); if (is_array($frontendLabel)) { - if (!isset($frontendLabel[0]) || $frontendLabel[0] === null || $frontendLabel[0] == '') { - throw new \Magento\Framework\Exception\LocalizedException(__('The storefront label is not defined.')); - } + $this->checkDefaultFrontendLabelExists($frontendLabel, $frontendLabel); $object->setFrontendLabel($frontendLabel[0])->setStoreLabels($frontendLabel); + } else { + $this->setStoreLabels($object, $frontendLabel); } /** @@ -742,4 +742,43 @@ public function __wakeup() $this->_storeManager = \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Store\Model\StoreManagerInterface::class); } + + /** + * This method extracts frontend labels into array and sets array values as storeLabels into an object. + * + * @param AbstractModel $object + * @param string|null $frontendLabel + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function setStoreLabels(AbstractModel $object, $frontendLabel) + { + $resultLabel = []; + $frontendLabels = $object->getFrontendLabels(); + if (isset($frontendLabels[0]) + && $frontendLabels[0] instanceof \Magento\Eav\Model\Entity\Attribute\FrontendLabel + ) { + foreach ($frontendLabels as $label) { + $resultLabel[$label->getStoreId()] = $label->getLabel(); + } + $this->checkDefaultFrontendLabelExists($frontendLabel, $resultLabel); + $object->setStoreLabels($resultLabel); + } + } + + /** + * This method checks whether value for default frontend label exists in attribute data. + * + * @param array|string|null $frontendLabel + * @param array $resultLabels + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function checkDefaultFrontendLabelExists($frontendLabel, $resultLabels) + { + $isAdminStoreLabel = (isset($resultLabels[0]) && !empty($resultLabels[0])); + if (empty($frontendLabel) && !$isAdminStoreLabel) { + throw new \Magento\Framework\Exception\LocalizedException(__('The storefront label is not defined.')); + } + } } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php index 911aecf8e7cfb..18c94381a5054 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php @@ -112,4 +112,39 @@ public function getSortWeightDataProvider() ] ]; } + + public function testGetFrontendLabels() + { + $attributeId = 1; + $storeLabels = ['test_attribute_store1']; + $frontendLabelFactory = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\FrontendLabelFactory::class) + ->setMethods(['create']) + ->getMock(); + $resource = $this->getMockBuilder(\Magento\Eav\Model\ResourceModel\Entity\Attribute::class) + ->setMethods(['getStoreLabelsByAttributeId']) + ->disableOriginalConstructor() + ->getMock(); + $arguments = [ + '_resource' => $resource, + 'frontendLabelFactory' => $frontendLabelFactory, + ]; + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->_model = $objectManager->getObject(\Magento\Eav\Model\Entity\Attribute::class, $arguments); + $this->_model->setAttributeId($attributeId); + + $resource->expects($this->once()) + ->method('getStoreLabelsByAttributeId') + ->with($attributeId) + ->willReturn($storeLabels); + $frontendLabel = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\FrontendLabel::class) + ->setMethods(['setStoreId', 'setLabel']) + ->disableOriginalConstructor() + ->getMock(); + $frontendLabelFactory->expects($this->once()) + ->method('create') + ->willReturn($frontendLabel); + $expectedFrontendLabel[] = $frontendLabel; + + $this->assertEquals($expectedFrontendLabel, $this->_model->getFrontendLabels()); + } } diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php index 5241e281b342d..b7715309b12df 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php @@ -101,7 +101,8 @@ public function testCreate() "is_filterable_in_search" => true, ]; - $this->assertEquals('front_lbl', $attribute['default_frontend_label']); + $this->assertEquals('default_label', $attribute['default_frontend_label']); + $this->assertEquals('front_lbl_store1', $attribute['frontend_labels'][0]['label']); foreach ($expectedData as $key => $value) { $this->assertEquals($value, $attribute[$key]); } @@ -146,9 +147,12 @@ public function testUpdate() 'attribute_code' => $attributeCode, 'entity_type_id' => 4, 'is_used_in_grid' => true, + //Update existing 'default_frontend_label' => 'default_label_new', 'frontend_labels' => [ - ['store_id' => 1, 'label' => 'front_lbl_new'], + //Update existing + ['store_id' => 0, 'label' => 'front_lbl_store0_new'], + ['store_id' => 1, 'label' => 'front_lbl_store1_new'], ], "options" => [ //Update existing @@ -190,11 +194,75 @@ public function testUpdate() $this->assertEquals(true, $result['is_used_in_grid']); $this->assertEquals($attributeCode, $result['attribute_code']); $this->assertEquals('default_label_new', $result['default_frontend_label']); + $this->assertEquals('front_lbl_store1_new', $result['frontend_labels'][0]['label']); //New option set as default $this->assertEquals($result['options'][3]['value'], $result['default_value']); $this->assertEquals("Default Blue Updated", $result['options'][1]['label']); } + /** + * @magentoApiDataFixture Magento/Catalog/Model/Product/Attribute/_files/create_attribute_service.php + */ + public function testUpdateWithNoDefaultLabelAndAdminStorelabel() + { + $attributeCode = uniqid('label_attr_code'); + $attribute = $this->createAttribute($attributeCode); + + $attributeData = [ + 'attribute' => [ + 'attribute_id' => $attribute['attribute_id'], + 'attribute_code' => $attributeCode, + 'entity_type_id' => 4, + 'is_used_in_grid' => true, + 'frontend_labels' => [ + //Update existing + ['store_id' => 0, 'label' => 'front_lbl_store0_new'], + ['store_id' => 1, 'label' => 'front_lbl_store1_new'], + ], + 'is_required' => false, + 'frontend_input' => 'select', + ], + ]; + $result = $this->updateAttribute($attributeCode, $attributeData); + + $this->assertEquals($attribute['attribute_id'], $result['attribute_id']); + $this->assertEquals(true, $result['is_used_in_grid']); + $this->assertEquals($attributeCode, $result['attribute_code']); + $this->assertEquals('front_lbl_store0_new', $result['default_frontend_label']); + $this->assertEquals('front_lbl_store1_new', $result['frontend_labels'][0]['label']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/Model/Product/Attribute/_files/create_attribute_service.php + */ + public function testUpdateWithNoDefaultLabelAndNoAdminStoreLabel() + { + $attributeCode = uniqid('label_attr_code'); + $attribute = $this->createAttribute($attributeCode); + + $attributeData = [ + 'attribute' => [ + 'attribute_id' => $attribute['attribute_id'], + 'attribute_code' => $attributeCode, + 'entity_type_id' => 4, + 'is_used_in_grid' => true, + 'frontend_labels' => [ + //Update existing + ['store_id' => 1, 'label' => 'front_lbl_store1_new'], + ], + 'is_required' => false, + 'frontend_input' => 'select', + ], + ]; + $result = $this->updateAttribute($attributeCode, $attributeData); + + $this->assertEquals($attribute['attribute_id'], $result['attribute_id']); + $this->assertEquals(true, $result['is_used_in_grid']); + $this->assertEquals($attributeCode, $result['attribute_code']); + $this->assertEquals('default_label', $result['default_frontend_label']); + $this->assertEquals('front_lbl_store1_new', $result['frontend_labels'][0]['label']); + } + /** * @magentoApiDataFixture Magento/Catalog/Model/Product/Attribute/_files/create_attribute_service.php */ @@ -286,11 +354,10 @@ protected function createAttribute($attributeCode) 'attribute' => [ 'attribute_code' => $attributeCode, 'entity_type_id' => '4', + "default_frontend_label" => 'default_label', 'frontend_labels' => [ - [ - 'store_id' => 0, - 'label' => 'front_lbl' - ], + ['store_id' => 0, 'label' => 'front_lbl_store0'], + ['store_id' => 1, 'label' => 'front_lbl_store1'], ], 'is_required' => true, "default_value" => "", From 3a1eab9872ac616b8e456953d6ecd948f1fc6333 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Fri, 17 Aug 2018 14:27:12 +0300 Subject: [PATCH 1047/1171] MAGETWO-93174: [2.3] Sitemaps generated by cron showing /pub/ in image urls --- .../Magento/Framework/Console/CliTest.php | 129 ++++++++++++++++++ .../Magento/Framework/Console/_files/env.php | 46 +++++++ .../Magento/Framework/Console/Cli.php | 25 ++++ 3 files changed, 200 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Console/CliTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Console/_files/env.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/Console/CliTest.php b/dev/tests/integration/testsuite/Magento/Framework/Console/CliTest.php new file mode 100644 index 0000000000000..7a439d84ce563 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Console/CliTest.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Console; + +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\DeploymentConfig\FileReader; +use Magento\Framework\App\DeploymentConfig\Writer; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Config\File\ConfigFilePool; +use Magento\Framework\Filesystem; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +class CliTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var ConfigFilePool + */ + private $configFilePool; + + /** + * @var FileReader + */ + private $reader; + + /** + * @var Writer + */ + private $writer; + + /** + * @var array + */ + private $envConfig; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->configFilePool = $this->objectManager->get(ConfigFilePool::class); + $this->filesystem = $this->objectManager->get(Filesystem::class); + $this->reader = $this->objectManager->get(FileReader::class); + $this->writer = $this->objectManager->get(Writer::class); + + $this->envConfig = $this->reader->load(ConfigFilePool::APP_ENV); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->filesystem->getDirectoryWrite(DirectoryList::CONFIG)->writeFile( + $this->configFilePool->getPath(ConfigFilePool::APP_ENV), + "<?php\n return array();\n" + ); + + $this->writer->saveConfig([ConfigFilePool::APP_ENV => $this->envConfig], true); + } + + /** + * Checks that settings from env.php config file are applied + * to created application instance. + * + * @param bool $isPub + * @param array $params + * @dataProvider documentRootIsPubProvider + */ + public function testDocumentRootIsPublic($isPub, $params) + { + $config = include __DIR__ . '/_files/env.php'; + $config['directories']['document_root_is_pub'] = $isPub; + $this->writer->saveConfig([ConfigFilePool::APP_ENV => $config], true); + + $cli = new Cli(); + $cliReflection = new \ReflectionClass($cli); + + $serviceManagerProperty = $cliReflection->getProperty('serviceManager'); + $serviceManagerProperty->setAccessible(true); + $serviceManager = $serviceManagerProperty->getValue($cli); + $deploymentConfig = $this->objectManager->get(DeploymentConfig::class); + $serviceManager->setAllowOverride(true); + $serviceManager->setService(DeploymentConfig::class, $deploymentConfig); + $serviceManagerProperty->setAccessible(false); + + $documentRootResolver = $cliReflection->getMethod('documentRootResolver'); + $documentRootResolver->setAccessible(true); + + self::assertEquals($params, $documentRootResolver->invoke($cli)); + } + + /** + * Provides document root setting and expecting + * properties for object manager creation. + * + * @return array + */ + public function documentRootIsPubProvider(): array + { + return [ + [true, [ + 'MAGE_DIRS' => [ + 'pub' => ['uri' => ''], + 'media' => ['uri' => 'media'], + 'static' => ['uri' => 'static'], + 'upload' => ['uri' => 'media/upload'] + ] + ]], + [false, []] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Console/_files/env.php b/dev/tests/integration/testsuite/Magento/Framework/Console/_files/env.php new file mode 100644 index 0000000000000..e314e7638c22c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Console/_files/env.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +return [ + 'backend' => [ + 'frontName' => 'admin', + ], + 'crypt' => [ + 'key' => 'some_key', + ], + 'session' => [ + 'save' => 'files', + ], + 'db' => [ + 'table_prefix' => '', + 'connection' => [], + ], + 'resource' => [], + 'x-frame-options' => 'SAMEORIGIN', + 'MAGE_MODE' => 'default', + 'cache_types' => [ + 'config' => 1, + 'layout' => 1, + 'block_html' => 1, + 'collections' => 1, + 'reflection' => 1, + 'db_ddl' => 1, + 'eav' => 1, + 'customer_notification' => 1, + 'config_integration' => 1, + 'config_integration_api' => 1, + 'full_page' => 1, + 'translate' => 1, + 'config_webservice' => 1, + ], + 'install' => [ + 'date' => 'Thu, 09 Feb 2017 14:28:00 +0000', + ], + 'directories' => [ + 'document_root_is_pub' => true + ] +]; diff --git a/lib/internal/Magento/Framework/Console/Cli.php b/lib/internal/Magento/Framework/Console/Cli.php index c90d8a9acaed6..dabb080cf7ede 100644 --- a/lib/internal/Magento/Framework/Console/Cli.php +++ b/lib/internal/Magento/Framework/Console/Cli.php @@ -19,6 +19,7 @@ use Magento\Setup\Console\CompilerPreparation; use Magento\Setup\Model\ObjectManagerProvider; use Symfony\Component\Console; +use Magento\Framework\Config\ConfigOptionsListConstants; /** * Magento 2 CLI Application. @@ -157,6 +158,7 @@ private function initObjectManager() { $params = (new ComplexParameter(self::INPUT_KEY_BOOTSTRAP))->mergeFromArgv($_SERVER, $_SERVER); $params[Bootstrap::PARAM_REQUIRE_MAINTENANCE] = null; + $params = $this->documentRootResolver($params); $requestParams = $this->serviceManager->get('magento-init-params'); $appBootstrapKey = Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS; @@ -242,4 +244,27 @@ protected function getVendorCommands($objectManager) return $commands; } + + /** + * Provides updated configuration in + * accordance to document root settings. + * + * @param array $config + * @return array + */ + private function documentRootResolver(array $config = []): array + { + $params = []; + $deploymentConfig = $this->serviceManager->get(DeploymentConfig::class); + if ((bool)$deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_DOCUMENT_ROOT_IS_PUB)) { + $params[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS] = [ + DirectoryList::PUB => [DirectoryList::URL_PATH => ''], + DirectoryList::MEDIA => [DirectoryList::URL_PATH => 'media'], + DirectoryList::STATIC_VIEW => [DirectoryList::URL_PATH => 'static'], + DirectoryList::UPLOAD => [DirectoryList::URL_PATH => 'media/upload'], + ]; + } + + return array_merge_recursive($config, $params); + } } From eb231143e3a428a81256713ae4a355a7393796bc Mon Sep 17 00:00:00 2001 From: Shcherbatykh Nikita <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 17 Aug 2018 14:57:34 +0300 Subject: [PATCH 1048/1171] MAGETWO-93765: [2.3] Elasticsearch for Chinese produces error --- app/code/Magento/Elasticsearch/etc/esconfig.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Elasticsearch/etc/esconfig.xml b/app/code/Magento/Elasticsearch/etc/esconfig.xml index 0a87b58fd3a18..49124c4b70392 100644 --- a/app/code/Magento/Elasticsearch/etc/esconfig.xml +++ b/app/code/Magento/Elasticsearch/etc/esconfig.xml @@ -16,7 +16,6 @@ <fr_FR>french</fr_FR> <nl_NL>dutch</nl_NL> <pt_BR>portuguese</pt_BR> - <zh_Hans_CN>cjk</zh_Hans_CN> </stemmer> <stopwords_file> <default>stopwords.csv</default> From bf757d46d1aea9df93f0cd22304603ef5b61402c Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 17 Aug 2018 08:59:53 -0500 Subject: [PATCH 1049/1171] MAGETWO-90531: WYSIWYG shows special character button in toolbar on product page - when test module is enabled - moving location to make path shorter --- .../Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml | 4 ++-- .../_files/Magento/TestModuleWysiwygConfig/etc/di.xml | 2 +- .../web/wysiwyg/{tiny_mce => }/tinymce4TestAdapter.js | 0 .../testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/view/adminhtml/web/wysiwyg/{tiny_mce => }/tinymce4TestAdapter.js (100%) diff --git a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml index cd5b0e4df7fe9..9303631a8aa61 100644 --- a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml +++ b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml @@ -12,7 +12,7 @@ <item name="testAdapter" xsi:type="string">Magento\TestModuleWysiwygConfig\Model\Config</item> </argument> <argument name="wysiwygConfigPostProcessor" xsi:type="array"> - <item name="Magento_TestModuleWysiwygConfig/wysiwyg/tiny_mce/tinymce4TestAdapter" xsi:type="string">Magento\TestModuleWysiwygConfig\Model\Config</item> + <item name="Magento_TestModuleWysiwygConfig/wysiwyg/tinymce4TestAdapter" xsi:type="string">Magento\TestModuleWysiwygConfig\Model\Config</item> </argument> <argument name="variablePluginConfigProvider" xsi:type="array"> <item name="testAdapter" xsi:type="string">Magento\Variable\Model\Variable\ConfigProvider</item> @@ -29,7 +29,7 @@ <arguments> <argument name="adapterOptions" xsi:type="array"> <item name="testAdapter" xsi:type="array"> - <item name="value" xsi:type="string">Magento_TestModuleWysiwygConfig/wysiwyg/tiny_mce/tinymce4TestAdapter</item> + <item name="value" xsi:type="string">Magento_TestModuleWysiwygConfig/wysiwyg/tinymce4TestAdapter</item> <item name="label" xsi:type="string" translatable="true">Test Adapter</item> </item> </argument> diff --git a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/di.xml b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/di.xml index db21145491d83..3be3d9dc3ec58 100644 --- a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/di.xml +++ b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/di.xml @@ -9,7 +9,7 @@ <type name="Magento\Ui\Block\Wysiwyg\ActiveEditor"> <arguments> <argument name="availableAdapterPaths" xsi:type="array"> - <item name="Magento_TestModuleWysiwygConfig/wysiwyg/tiny_mce/tinymce4TestAdapter" xsi:type="string"/> + <item name="Magento_TestModuleWysiwygConfig/wysiwyg/tinymce4TestAdapter" xsi:type="string"/> </argument> </arguments> </type> diff --git a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/view/adminhtml/web/wysiwyg/tiny_mce/tinymce4TestAdapter.js b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/view/adminhtml/web/wysiwyg/tinymce4TestAdapter.js similarity index 100% rename from dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/view/adminhtml/web/wysiwyg/tiny_mce/tinymce4TestAdapter.js rename to dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/view/adminhtml/web/wysiwyg/tinymce4TestAdapter.js diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php index 431c9383a8c74..983d5657a4cee 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php @@ -63,7 +63,7 @@ public function testGetConfigCssUrls() * * @return void * - * @magentoConfigFixture default/cms/wysiwyg/editor Magento_TestModuleWysiwygConfig/wysiwyg/tiny_mce/tinymce4TestAdapter + * @magentoConfigFixture default/cms/wysiwyg/editor Magento_TestModuleWysiwygConfig/wysiwyg/tinymce4TestAdapter */ public function testTestModuleEnabledModuleIsAbleToModifyConfig() { From 429edc86fca8f97626fc3742c50c2971a353870d Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Fri, 17 Aug 2018 18:05:53 +0400 Subject: [PATCH 1050/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Updated automated test. --- .../ActionGroup/StorefrontAddProductToCardActionGroup.xml | 4 ++-- .../Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml index feabe2e28b94f..4f6b7e31a9c5b 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml @@ -35,9 +35,9 @@ <click selector="{{StorefrontAddProductToCartSection.addToCartBtn}}" stepKey="addToCart"/> <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" time="30" stepKey="waitForProductAdded"/> <click selector="{{StorefrontAddProductToCartSection.showCard}}" stepKey="clickToOpenCard"/> - <waitForPageLoad stepKey="WaitForFormOpened" time="3"/> + <waitForPageLoad stepKey="WaitForFormOpened"/> <click selector="{{StorefrontAddProductToCartSection.proceed}}" stepKey="clickToProceedToCheckout"/> - <waitForPageLoad time="5" stepKey="waitForTheFormIsOpened"/> + <waitForPageLoad stepKey="waitForTheFormIsOpened"/> <see userInput="Shipping Address" stepKey="seeShippingAddress"/> </actionGroup> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml index 3927ecf21698e..1359893ec090e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml @@ -25,7 +25,7 @@ </before> <!--Navigate to a category page --> - <amOnPage url="/{{SimpleProductOne.name}}.html" stepKey="GoToProductPage"/> + <amOnPage url="/{{SimpleProductOne.urlKey}}.html" stepKey="GoToProductPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> From 1e56fe758f8b2d28e3dd1bbca654a431869794e1 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 17 Aug 2018 09:10:59 -0500 Subject: [PATCH 1051/1171] MAGETWO-92279: An incorrect result of declaration:generate:whitelist execution - simplifying code --- .../Console/Command/TablesWhitelistGenerateCommand.php | 4 +--- .../Console/Command/TablesWhitelistGenerateCommandTest.php | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Developer/Console/Command/TablesWhitelistGenerateCommand.php b/app/code/Magento/Developer/Console/Command/TablesWhitelistGenerateCommand.php index b8ef557735db0..d283e693b8de6 100644 --- a/app/code/Magento/Developer/Console/Command/TablesWhitelistGenerateCommand.php +++ b/app/code/Magento/Developer/Console/Command/TablesWhitelistGenerateCommand.php @@ -197,9 +197,7 @@ private function filterPrimaryTables(array $moduleDbSchema) $primaryDbSchema = $this->getPrimaryDbSchema(); if (isset($moduleDbSchema['table']) && isset($primaryDbSchema['table'])) { foreach ($primaryDbSchema['table'] as $tableNameKey => $tableContents) { - if (isset($moduleDbSchema['table'][$tableNameKey])) { - unset($moduleDbSchema['table'][$tableNameKey]); - } + unset($moduleDbSchema['table'][$tableNameKey]); } } return $moduleDbSchema; diff --git a/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php b/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php index 81ad56b793224..f06f3a8a93a37 100644 --- a/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php +++ b/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php @@ -67,9 +67,8 @@ public function setUp() */ public function testExecute(array $expectedWhitelistContent) { - $this->cliCommand->install(['Magento_TestSetupDeclarationModule1']); - $moduleName = 'Magento_TestSetupDeclarationModule1'; + $this->cliCommand->install([$moduleName]); $modulePath = $this->componentRegistrar->getPath('module', $moduleName); $whiteListFileName = $modulePath . DIRECTORY_SEPARATOR From 2d1a4d14b81b62509332a4176cc4e5d252468e35 Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Fri, 17 Aug 2018 18:31:33 +0400 Subject: [PATCH 1052/1171] MAGETWO-62891: New address is not marked as "Default Billing" - Update automated test --- ...ltBillingAndShippingAddressActionGroup.xml | 19 +++++++++++-------- ...OfDefaultBillingAndShippingAddressData.xml | 6 ++++++ ...efaultBillingAndShippingAddressSection.xml | 4 ++++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml index e126bd4b5f743..4e84128d8385a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml @@ -38,12 +38,12 @@ <actionGroup name="StorefrontCreateAccountActionGroup"> <click selector="{{StorefrontCreateAccountSection.createAccount}}" stepKey="ClickToCreateAccount"/> <waitForPageLoad stepKey="waitForAccountFormIsOpened"/> - <fillField selector="{{StorefrontCreateAccountSection.firstName}}" userInput="{{CreateUserData.firstName}}" stepKey="TypeFirstName"/> - <fillField selector="{{StorefrontCreateAccountSection.lastName}}" userInput="{{CreateUserData.lastName}}" stepKey="TypeLastName"/> - <fillField selector="{{StorefrontCreateAccountSection.email}}" userInput="{{CreateUserData.firstName}}@magento.com" stepKey="TypeEmail"/> - <fillField selector="{{StorefrontCreateAccountSection.password}}" userInput="{{CreateUserData.password}}" stepKey="TypePassword"/> + <fillField selector="{{StorefrontCreateAccountSection.firstName}}" userInput="{{CreateNewUserData.firstName}}" stepKey="TypeFirstName"/> + <fillField selector="{{StorefrontCreateAccountSection.lastName}}" userInput="{{CreateNewUserData.lastName}}" stepKey="TypeLastName"/> + <fillField selector="{{StorefrontCreateAccountSection.email}}" userInput="{{CreateNewUserData.firstName}}@magento.com" stepKey="TypeEmail"/> + <fillField selector="{{StorefrontCreateAccountSection.password}}" userInput="{{CreateNewUserData.password}}" stepKey="TypePassword"/> <waitForPageLoad stepKey="waitToConfirmPassword"/> - <fillField selector="{{StorefrontCreateAccountSection.confirmPass}}" userInput="{{CreateUserData.password}}" stepKey="confirmPassword"/> + <fillField selector="{{StorefrontCreateAccountSection.confirmPass}}" userInput="{{CreateNewUserData.password}}" stepKey="confirmPassword"/> <click selector="{{StorefrontCreateAccountSection.create}}" stepKey="ClickToSaveAccount"/> <waitForPageLoad stepKey="waitForAccountPageLoaded"/> </actionGroup> @@ -129,9 +129,12 @@ <wait stepKey="WaitForCustomerViewOpened" time="2"/> <click stepKey="clickCustomerAllCustomerItem" selector="{{DashboardSection.customerAllCustomer}}"/> <waitForPageLoad stepKey="WaitForCustomerPageIsLoaded"/> - <fillField stepKey="searchToKeyword" selector="{{AdminCustomerAccInformationSection.searchToKeyword}}" userInput="{{CreateUserData.firstName}}"/> - <click stepKey="clickSearchButton" selector="{{AdminCustomerAccInformationSection.searchButton}}"/> - <waitForElementVisible stepKey="waitForFiltering" selector="{{AdminCustomerAccInformationSection.selectCustomer}}"/> + <conditionalClick selector="{{AdminCustomerAccInformationSection.clearAll}}" dependentSelector="{{AdminCustomerAccInformationSection.clearAll}}" visible="1" stepKey="clickClearAllIfThereIsAnyValue"/> + <click stepKey="clickFilterButton" selector="{{AdminCustomerAccInformationSection.filterButton}}"/> + <waitForElementVisible selector="{{AdminCustomerAccInformationSection.filterNameField}}" stepKey="waitForFilterDataLoaded"/> + <fillField stepKey="searchProductUsingNameField" selector="{{AdminCustomerAccInformationSection.filterNameField}}" userInput="{{CreateNewUserData.firstName}}"/> + <click stepKey="clickFiltersApplyButton" selector="{{AdminCustomerAccInformationSection.filtersApplyButton}}"/> + <waitForElementNotVisible selector="{{AdminCustomerAccInformationSection.filterNameField}}" stepKey="waitForFilterBecomeNotVisible"/> <click selector="{{AdminCustomerAccInformationSection.selectCustomer}}" stepKey="ClickOnCustomer"/> <click selector="{{AdminCustomerAccInformationSection.actions}}" stepKey="ClickOnActions"/> <waitForElementVisible selector="{{AdminCustomerAccInformationSection.delete}}" stepKey="waitForDeleteButtonAppeared"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/IdentityOfDefaultBillingAndShippingAddressData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/IdentityOfDefaultBillingAndShippingAddressData.xml index a627eb3102bf6..0f5dc1ce4cfa9 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Data/IdentityOfDefaultBillingAndShippingAddressData.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Data/IdentityOfDefaultBillingAndShippingAddressData.xml @@ -23,4 +23,10 @@ <data key="telephone">222222222</data> </entity> + <entity name="CreateNewUserData" type="user"> + <data key="firstName" unique="suffix">John</data> + <data key="lastName">Smith</data> + <data key="password">Admin@123</data> + </entity> + </entities> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml index 8e8183a2be946..4b689e1b68441 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml @@ -89,6 +89,10 @@ <element name="actions" type="button" selector="//div[@class='admin__data-grid-header']//div[@class='col-xs-2']//span[text()='Actions']"/> <element name="delete" type="button" selector="//div[@class='col-xs-2']//span[text()='Delete']"/> <element name="confirm" type="button" selector=".action-primary.action-accept"/> + <element name="filterButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-filters-action-wrap']/button"/> + <element name="filterNameField" type="input" selector="//*[@name='name']"/> + <element name="filtersApplyButton" type="button" selector="//*[contains(text(),'Apply Filters')]"/> + <element name="clearAll" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[contains(text(), 'Clear all')]"/> </section> </sections> From e1130708c7a045784c5b90f6cf8839ce3cd69e4b Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Fri, 17 Aug 2018 17:50:58 +0300 Subject: [PATCH 1053/1171] MAGETWO-91493: MDC Framework Issues. Message passed as a service to TransportBuilder - Fix static and unit tests --- .../Unit/Model/Queue/TransportBuilderTest.php | 66 ++++++------------- .../Mail/Template/TransportBuilder.php | 4 +- .../Unit/Template/TransportBuilderTest.php | 3 + 3 files changed, 25 insertions(+), 48 deletions(-) diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/Queue/TransportBuilderTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/Queue/TransportBuilderTest.php index 86d40187de845..e8b141a24c9e8 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/Queue/TransportBuilderTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/Queue/TransportBuilderTest.php @@ -45,6 +45,11 @@ class TransportBuilderTest extends \PHPUnit\Framework\TestCase */ protected $mailTransportFactoryMock; + /** + * @var \Magento\Framework\Mail\MessageInterfaceFactory | \PHPUnit_Framework_MockObject_MockObject + */ + private $messageFactoryMock; + /** * @return void */ @@ -52,7 +57,10 @@ public function setUp() { $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->templateFactoryMock = $this->createMock(\Magento\Framework\Mail\Template\FactoryInterface::class); - $this->messageMock = $this->createMock(\Magento\Framework\Mail\Message::class); + $this->messageMock = $this->getMockBuilder(\Magento\Framework\Mail\MessageInterface::class) + ->disableOriginalConstructor() + ->setMethods(['setBodyHtml', 'setSubject']) + ->getMockForAbstractClass(); $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); $this->senderResolverMock = $this->createMock(\Magento\Framework\Mail\Template\SenderResolverInterface::class); $this->mailTransportFactoryMock = $this->getMockBuilder( @@ -60,6 +68,11 @@ public function setUp() )->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); + $this->messageFactoryMock = $this->getMockBuilder(\Magento\Framework\Mail\MessageInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMockForAbstractClass(); + $this->messageFactoryMock->expects($this->atLeastOnce())->method('create')->willReturn($this->messageMock); $this->builder = $objectManagerHelper->getObject( $this->builderClassName, [ @@ -67,7 +80,8 @@ public function setUp() 'message' => $this->messageMock, 'objectManager' => $this->objectManagerMock, 'senderResolver' => $this->senderResolverMock, - 'mailTransportFactory' => $this->mailTransportFactoryMock + 'mailTransportFactory' => $this->mailTransportFactoryMock, + 'messageFactory' => $this->messageFactoryMock ] ); } @@ -108,7 +122,7 @@ public function testGetTransport( $template->expects($this->once()) ->method('getProcessedTemplate') ->with($vars) - ->will($this->returnValue($bodyText)); + ->willReturn($bodyText); $template->expects($this->once()) ->method('setTemplateFilter') ->with($filter); @@ -123,46 +137,8 @@ public function testGetTransport( $this->returnValue($template) ); - $this->messageMock->expects( - $this->once() - )->method( - 'setSubject' - )->with( - $this->equalTo('Email Subject') - )->will( - $this->returnSelf() - ); - $this->messageMock->expects( - $this->once() - )->method( - 'setBodyHtml' - )->with( - $this->equalTo($bodyText) - )->will( - $this->returnSelf() - ); - - $transport = $this->createMock(\Magento\Framework\Mail\TransportInterface::class); - - $this->mailTransportFactoryMock->expects( - $this->at(0) - )->method( - 'create' - )->with( - $this->equalTo(['message' => $this->messageMock]) - )->will( - $this->returnValue($transport) - ); - - $this->objectManagerMock->expects( - $this->at(0) - )->method( - 'create' - )->with( - $this->equalTo(\Magento\Framework\Mail\Message::class) - )->will( - $this->returnValue($transport) - ); + $this->messageMock->expects($this->once())->method('setBodyHtml')->willReturnSelf(); + $this->messageMock->expects($this->once())->method('setSubject')->willReturnSelf(); $this->builder->setTemplateIdentifier( 'identifier' @@ -174,8 +150,6 @@ public function testGetTransport( $data ); - $result = $this->builder->getTransport(); - - $this->assertInstanceOf(\Magento\Framework\Mail\TransportInterface::class, $result); + $this->builder->getTransport(); } } diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index cd8cf94ab50ee..eac5d74c6e3dc 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -18,6 +18,7 @@ /** * @api + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class TransportBuilder { @@ -116,8 +117,7 @@ public function __construct( $this->objectManager = $objectManager; $this->_senderResolver = $senderResolver; $this->mailTransportFactory = $mailTransportFactory; - $this->messageFactory = $messageFactory ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(MessageInterfaceFactory::class); + $this->messageFactory = $messageFactory ?: $this->objectManager->get(MessageInterfaceFactory::class); $this->message = $this->messageFactory->create(); } diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index e1ebbb421468a..731db30bada06 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -10,6 +10,9 @@ use Magento\Framework\Mail\MessageInterface; use Magento\Framework\Mail\MessageInterfaceFactory; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class TransportBuilderTest extends \PHPUnit\Framework\TestCase { /** From c219dadf5e334e747172f4d50c8bc9c707150fd4 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 17 Aug 2018 10:36:55 -0500 Subject: [PATCH 1054/1171] MAGETWO-90531: WYSIWYG shows special character button in toolbar on product page - when test module is enabled - removing invalid xml --- .../_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml index 9303631a8aa61..dd2e003f1a544 100644 --- a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml +++ b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/adminhtml/di.xml @@ -10,8 +10,6 @@ <arguments> <argument name="wysiwygConfigPostProcessor" xsi:type="array"> <item name="testAdapter" xsi:type="string">Magento\TestModuleWysiwygConfig\Model\Config</item> - </argument> - <argument name="wysiwygConfigPostProcessor" xsi:type="array"> <item name="Magento_TestModuleWysiwygConfig/wysiwyg/tinymce4TestAdapter" xsi:type="string">Magento\TestModuleWysiwygConfig\Model\Config</item> </argument> <argument name="variablePluginConfigProvider" xsi:type="array"> From 6565136ff9d2886dfd85462e3ac17dadf10c4b57 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 17 Aug 2018 10:44:00 -0500 Subject: [PATCH 1055/1171] MAGETWO-92279: An incorrect result of declaration:generate:whitelist execution - add strict --- .../Console/Command/TablesWhitelistGenerateCommandTest.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php b/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php index f06f3a8a93a37..10eed4d5ea99e 100644 --- a/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php +++ b/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Developer\Console\Command; @@ -44,7 +45,7 @@ class TablesWhitelistGenerateCommandTest extends SetupTestCase /** * {@inheritdoc} */ - public function setUp() + public function setUp() : void { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $this->command = $this->objectManager->create( @@ -65,7 +66,7 @@ public function setUp() * @moduleName Magento_TestSetupDeclarationModule1 * @dataProvider contentsDataProvider */ - public function testExecute(array $expectedWhitelistContent) + public function testExecute(array $expectedWhitelistContent) : void { $moduleName = 'Magento_TestSetupDeclarationModule1'; $this->cliCommand->install([$moduleName]); @@ -94,7 +95,7 @@ public function testExecute(array $expectedWhitelistContent) * @return array * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function contentsDataProvider() + public function contentsDataProvider() : array { return [ [ From 965689dcdfda3b22f683f40972aeaeaa2e2024e0 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 17 Aug 2018 11:50:36 -0500 Subject: [PATCH 1056/1171] MAGETWO-91678: Minimum Order amount required in Admin orders --- .../Model/ValidationRules/AllowedCountryValidationRule.php | 5 ++++- .../Model/ValidationRules/BillingAddressValidationRule.php | 5 ++++- .../Model/ValidationRules/MinimumAmountValidationRule.php | 5 ++++- .../Model/ValidationRules/PaymentMethodValidationRule.php | 5 ++++- .../Model/ValidationRules/QuoteValidationComposite.php | 3 +++ .../Model/ValidationRules/QuoteValidationRuleInterface.php | 5 ++++- .../Model/ValidationRules/ShippingAddressValidationRule.php | 5 ++++- .../Model/ValidationRules/ShippingMethodValidationRule.php | 5 ++++- app/code/Magento/Quote/etc/di.xml | 2 +- app/code/Magento/Sales/Test/Mftf/Data/SalesConfigData.xml | 6 +++--- .../Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml | 2 +- .../testsuite/Magento/Quote/Model/QuoteValidatorTest.php | 1 + 12 files changed, 37 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php index 34e9b910de8e9..840e94e3ece1f 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php @@ -11,6 +11,9 @@ use Magento\Framework\Validation\ValidationResultFactory; use Magento\Quote\Model\Quote; +/** + * @inheritdoc + */ class AllowedCountryValidationRule implements QuoteValidationRuleInterface { /** @@ -57,7 +60,7 @@ public function validate(Quote $quote): array $this->allowedCountryReader->getAllowedCountries() ); if (!$validationResult) { - $validationErrors = [$this->generalMessage]; + $validationErrors = [__($this->generalMessage)]; } } diff --git a/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php index a28bc23cc0f0f..fbacbf1c8d30c 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php @@ -10,6 +10,9 @@ use Magento\Framework\Validation\ValidationResultFactory; use Magento\Quote\Model\Quote; +/** + * @inheritdoc + */ class BillingAddressValidationRule implements QuoteValidationRuleInterface { /** @@ -42,7 +45,7 @@ public function validate(Quote $quote): array $validationErrors = []; $validationResult = $quote->getBillingAddress()->validate(); if ($validationResult !== true) { - $validationErrors = [$this->generalMessage]; + $validationErrors = [__($this->generalMessage)]; } if (is_array($validationResult)) { $validationErrors = array_merge($validationErrors, $validationResult); diff --git a/app/code/Magento/Quote/Model/ValidationRules/MinimumAmountValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/MinimumAmountValidationRule.php index 74f605e2e4c66..34e953be43c74 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/MinimumAmountValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/MinimumAmountValidationRule.php @@ -11,6 +11,9 @@ use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Validator\MinimumOrderAmount\ValidationMessage; +/** + * @inheritdoc + */ class MinimumAmountValidationRule implements QuoteValidationRuleInterface { /** @@ -55,7 +58,7 @@ public function validate(Quote $quote): array if (!$this->generalMessage) { $this->generalMessage = $this->amountValidationMessage->getMessage(); } - $validationErrors = [$this->generalMessage]; + $validationErrors = [__($this->generalMessage)]; } return [$this->validationResultFactory->create(['errors' => $validationErrors])]; diff --git a/app/code/Magento/Quote/Model/ValidationRules/PaymentMethodValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/PaymentMethodValidationRule.php index ac9f24bf3567b..bf2b813541fb0 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/PaymentMethodValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/PaymentMethodValidationRule.php @@ -10,6 +10,9 @@ use Magento\Framework\Validation\ValidationResultFactory; use Magento\Quote\Model\Quote; +/** + * @inheritdoc + */ class PaymentMethodValidationRule implements QuoteValidationRuleInterface { /** @@ -42,7 +45,7 @@ public function validate(Quote $quote): array $validationErrors = []; $validationResult = $quote->getPayment()->getMethod(); if (!$validationResult) { - $validationErrors = [$this->generalMessage]; + $validationErrors = [__($this->generalMessage)]; } return [$this->validationResultFactory->create(['errors' => $validationErrors])]; diff --git a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationComposite.php b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationComposite.php index e6e9bd4fc7917..6a75be3acce89 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationComposite.php +++ b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationComposite.php @@ -9,6 +9,9 @@ use Magento\Quote\Model\Quote; +/** + * @inheritdoc + */ class QuoteValidationComposite implements QuoteValidationRuleInterface { /** diff --git a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php index cb397f3483055..da741d64241eb 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php +++ b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php @@ -9,10 +9,13 @@ use Magento\Framework\Validation\ValidationResult; use Magento\Quote\Model\Quote; +/** + * Provides validation of Quote model. + */ interface QuoteValidationRuleInterface { /** - * Validate quote model. + * Validate Quote model. * * @param Quote $quote * @return ValidationResult[] diff --git a/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php index 87e69230ae36b..11f3d51e0df5f 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php @@ -10,6 +10,9 @@ use Magento\Framework\Validation\ValidationResultFactory; use Magento\Quote\Model\Quote; +/** + * @inheritdoc + */ class ShippingAddressValidationRule implements QuoteValidationRuleInterface { /** @@ -45,7 +48,7 @@ public function validate(Quote $quote): array if (!$quote->isVirtual()) { $validationResult = $quote->getShippingAddress()->validate(); if ($validationResult !== true) { - $validationErrors = [$this->generalMessage]; + $validationErrors = [__($this->generalMessage)]; } if (is_array($validationResult)) { $validationErrors = array_merge($validationErrors, $validationResult); diff --git a/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php index 93c775c465235..6df7f663b0630 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php @@ -10,6 +10,9 @@ use Magento\Framework\Validation\ValidationResultFactory; use Magento\Quote\Model\Quote; +/** + * @inheritdoc + */ class ShippingMethodValidationRule implements QuoteValidationRuleInterface { /** @@ -46,7 +49,7 @@ public function validate(Quote $quote): array $shippingRate = $quote->getShippingAddress()->getShippingRateByCode($shippingMethod); $validationResult = $shippingMethod && $shippingRate; if (!$validationResult) { - $validationErrors = [$this->generalMessage]; + $validationErrors = [__($this->generalMessage)]; } } diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml index d9398ad538bc0..ecc6426855679 100644 --- a/app/code/Magento/Quote/etc/di.xml +++ b/app/code/Magento/Quote/etc/di.xml @@ -41,7 +41,7 @@ <preference for="Magento\Quote\Api\GuestCartTotalManagementInterface" type="Magento\Quote\Model\GuestCart\GuestCartTotalManagement" /> <preference for="Magento\Quote\Api\Data\EstimateAddressInterface" type="Magento\Quote\Model\EstimateAddress" /> <preference for="Magento\Quote\Api\Data\ProductOptionInterface" type="Magento\Quote\Model\Quote\ProductOption" /> - <preference for="Magento\Quote\Model\ValidationRules\QuoteValidationRuleInterface" type="Magento\Quote\Model\ValidationRules\QuoteValidationComposite"/> + <preference for="Magento\Quote\Model\ValidationRules\QuoteValidationRuleInterface" type="Magento\Quote\Model\ValidationRules\QuoteValidationComposite\Proxy"/> <type name="Magento\Webapi\Controller\Rest\ParamsOverrider"> <arguments> <argument name="paramOverriders" xsi:type="array"> diff --git a/app/code/Magento/Sales/Test/Mftf/Data/SalesConfigData.xml b/app/code/Magento/Sales/Test/Mftf/Data/SalesConfigData.xml index c9c6d19173689..3239e17403255 100644 --- a/app/code/Magento/Sales/Test/Mftf/Data/SalesConfigData.xml +++ b/app/code/Magento/Sales/Test/Mftf/Data/SalesConfigData.xml @@ -7,14 +7,14 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="EnabledMinimumOrderAmount" type="sales_minimum_order"> + <entity name="EnabledMinimumOrderAmount500" type="sales_minimum_order"> <requiredEntity type="active">EnableMinimumOrderCheck</requiredEntity> - <requiredEntity type="amount">MinimumOrderAmount</requiredEntity> + <requiredEntity type="amount">MinimumOrderAmount500</requiredEntity> </entity> <entity name="EnableMinimumOrderCheck" type="active"> <data key="value">1</data> </entity> - <entity name="MinimumOrderAmount" type="amount"> + <entity name="MinimumOrderAmount500" type="amount"> <data key="value">500</data> </entity> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml index 420c54eca63a0..fbbce2df4876c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml @@ -18,7 +18,7 @@ <group value="sales"/> </annotations> <before> - <createData entity="EnabledMinimumOrderAmount" stepKey="enableMinimumOrderAmount"/> + <createData entity="EnabledMinimumOrderAmount500" stepKey="enableMinimumOrderAmount"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php index efd39da95d3aa..922ee35a9df5e 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Quote\Model; From cff3d1571ed42811ae921b4c14123658f63244b3 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 17 Aug 2018 12:35:58 -0500 Subject: [PATCH 1057/1171] MAGETWO-92279: An incorrect result of declaration:generate:whitelist execution - remove redundant strict types --- .../Console/Command/TablesWhitelistGenerateCommandTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php b/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php index 10eed4d5ea99e..77f2d741fc6b5 100644 --- a/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php +++ b/dev/tests/setup-integration/testsuite/Magento/Developer/Console/Command/TablesWhitelistGenerateCommandTest.php @@ -45,7 +45,7 @@ class TablesWhitelistGenerateCommandTest extends SetupTestCase /** * {@inheritdoc} */ - public function setUp() : void + public function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $this->command = $this->objectManager->create( @@ -66,7 +66,7 @@ public function setUp() : void * @moduleName Magento_TestSetupDeclarationModule1 * @dataProvider contentsDataProvider */ - public function testExecute(array $expectedWhitelistContent) : void + public function testExecute(array $expectedWhitelistContent) { $moduleName = 'Magento_TestSetupDeclarationModule1'; $this->cliCommand->install([$moduleName]); @@ -95,7 +95,7 @@ public function testExecute(array $expectedWhitelistContent) : void * @return array * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function contentsDataProvider() : array + public function contentsDataProvider(): array { return [ [ From 92cd2933e4a6ee45a4ef6bcf6e1e655e27297d6a Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 17 Aug 2018 12:43:29 -0500 Subject: [PATCH 1058/1171] MAGETWO-91678: Minimum Order amount required in Admin orders --- .../Quote/Model/ValidationRules/QuoteValidationRuleInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php index da741d64241eb..5c578515bcfe0 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php +++ b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Quote\Model\ValidationRules; From 21c91c8353b2b3f690ad361fae32412068223e45 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 17 Aug 2018 12:46:16 -0500 Subject: [PATCH 1059/1171] MAGETWO-91678: Minimum Order amount required in Admin orders --- .../Model/ValidationRules/QuoteValidationRuleInterface.php | 2 +- .../Model/ValidationRules/ShippingAddressValidationRule.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php index 5c578515bcfe0..1a777e3f1a5fe 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php +++ b/app/code/Magento/Quote/Model/ValidationRules/QuoteValidationRuleInterface.php @@ -22,4 +22,4 @@ interface QuoteValidationRuleInterface * @return ValidationResult[] */ public function validate(Quote $quote): array; -} \ No newline at end of file +} diff --git a/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php index 11f3d51e0df5f..f5eebe241acc9 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php @@ -32,8 +32,7 @@ class ShippingAddressValidationRule implements QuoteValidationRuleInterface public function __construct( ValidationResultFactory $validationResultFactory, string $generalMessage = '' - ) - { + ) { $this->validationResultFactory = $validationResultFactory; $this->generalMessage = $generalMessage; } From 52d3188c7e14ddb81bdb870d6d22da28d004d24a Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Wed, 15 Aug 2018 17:03:25 +0200 Subject: [PATCH 1060/1171] Added unit test for newsletter problem model --- .../Test/Unit/Model/ProblemTest.php | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 app/code/Magento/Newsletter/Test/Unit/Model/ProblemTest.php diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/ProblemTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/ProblemTest.php new file mode 100644 index 0000000000000..85f104d17b17b --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Unit/Model/ProblemTest.php @@ -0,0 +1,200 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Test\Unit\Model; + +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Newsletter\Model\Problem as ProblemModel; +use Magento\Newsletter\Model\Queue; +use Magento\Newsletter\Model\ResourceModel\Problem; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; + +class ProblemTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var Registry|\PHPUnit_Framework_MockObject_MockObject + */ + private $registryMock; + + /** + * @var SubscriberFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $subscriberFactoryMock; + + /** + * @var Subscriber|\PHPUnit_Framework_MockObject_MockObject + */ + private $subscriberMock; + + /** + * @var AbstractResource|\PHPUnit_Framework_MockObject_MockObject + */ + private $abstractResourceMock; + + /** + * @var AbstractDb|\PHPUnit_Framework_MockObject_MockObject + */ + private $abstractDbMock; + + /** + * @var ObjectManager + */ + protected $objectManager; + + /** + * @var ProblemModel + */ + private $problemModel; + + protected function setUp() + { + $this->contextMock = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + $this->registryMock = $this->getMockBuilder(Registry::class) + ->disableOriginalConstructor() + ->getMock(); + $this->subscriberFactoryMock = $this->getMockBuilder(SubscriberFactory::class) + ->getMock(); + $this->subscriberMock = $this->getMockBuilder(Subscriber::class) + ->disableOriginalConstructor() + ->getMock(); + $this->abstractResourceMock = $this->getMockBuilder(Problem::class) + ->disableOriginalConstructor() + ->getMock(); + $this->abstractDbMock = $this->getMockBuilder(AbstractDb::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->abstractResourceMock->expects($this->any()) + ->method('getIdFieldName') + ->willReturn('id'); + + $this->objectManager = new ObjectManager($this); + + $this->problemModel = $this->objectManager->getObject( + ProblemModel::class, + [ + 'context' => $this->contextMock, + 'registry' => $this->registryMock, + 'subscriberFactory' => $this->subscriberFactoryMock, + 'resource' => $this->abstractResourceMock, + 'resourceCollection' => $this->abstractDbMock, + 'data' => [], + ] + ); + } + + public function testAddSubscriberData() + { + $subscriberId = 1; + $this->subscriberMock->expects($this->once()) + ->method('getId') + ->willReturn($subscriberId); + + $result = $this->problemModel->addSubscriberData($this->subscriberMock); + + self::assertEquals($result, $this->problemModel); + self::assertEquals($subscriberId, $this->problemModel->getSubscriberId()); + } + + public function testAddQueueData() + { + $queueId = 1; + $queueMock = $this->getMockBuilder(Queue::class) + ->disableOriginalConstructor() + ->getMock(); + $queueMock->expects($this->once()) + ->method('getId') + ->willReturn($queueId); + + $result = $this->problemModel->addQueueData($queueMock); + + self::assertEquals($result, $this->problemModel); + self::assertEquals($queueId, $this->problemModel->getQueueId()); + } + + public function testAddErrorData() + { + $exceptionMessage = 'Some message'; + $exceptionCode = 111; + $exception = new \Exception($exceptionMessage, $exceptionCode); + + $result = $this->problemModel->addErrorData($exception); + + self::assertEquals($result, $this->problemModel); + self::assertEquals($exceptionMessage, $this->problemModel->getProblemErrorText()); + self::assertEquals($exceptionCode, $this->problemModel->getProblemErrorCode()); + } + + public function testGetSubscriberWithNoSubscriberId() + { + self::assertNull($this->problemModel->getSubscriber()); + } + + public function testGetSubscriber() + { + $this->setSubscriber(); + self::assertEquals($this->subscriberMock, $this->problemModel->getSubscriber()); + } + + public function testUnsubscribeWithNoSubscriber() + { + $this->subscriberMock->expects($this->never()) + ->method('__call') + ->with($this->equalTo('setSubscriberStatus')); + + $result = $this->problemModel->unsubscribe(); + + self::assertEquals($this->problemModel, $result); + } + + public function testUnsubscribe() + { + $this->setSubscriber(); + $this->subscriberMock->expects($this->at(1)) + ->method('__call') + ->with($this->equalTo('setSubscriberStatus'), $this->equalTo([Subscriber::STATUS_UNSUBSCRIBED])) + ->willReturnSelf(); + $this->subscriberMock->expects($this->at(2)) + ->method('__call') + ->with($this->equalTo('setIsStatusChanged')) + ->willReturnSelf(); + $this->subscriberMock->expects($this->once()) + ->method('save'); + + $result = $this->problemModel->unsubscribe(); + + self::assertEquals($this->problemModel, $result); + } + + /** + * Sets subscriber to the Problem model + */ + private function setSubscriber() + { + $subscriberId = 1; + $this->problemModel->setSubscriberId($subscriberId); + $this->subscriberFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->subscriberMock); + $this->subscriberMock->expects($this->once()) + ->method('load') + ->with($subscriberId) + ->willReturnSelf(); + } +} From 65ad339d119619b04e37d92ff0d2f69579f9bb2d Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Wed, 15 Aug 2018 17:07:44 +0200 Subject: [PATCH 1061/1171] Minor naming adjustments --- .../Newsletter/Test/Unit/Model/ProblemTest.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/ProblemTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/ProblemTest.php index 85f104d17b17b..889fc11d71d7e 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/ProblemTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/ProblemTest.php @@ -9,12 +9,11 @@ use Magento\Framework\Data\Collection\AbstractDb; use Magento\Framework\Model\Context; -use Magento\Framework\Model\ResourceModel\AbstractResource; use Magento\Framework\Registry; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Newsletter\Model\Problem as ProblemModel; use Magento\Newsletter\Model\Queue; -use Magento\Newsletter\Model\ResourceModel\Problem; +use Magento\Newsletter\Model\ResourceModel\Problem as ProblemResource; use Magento\Newsletter\Model\Subscriber; use Magento\Newsletter\Model\SubscriberFactory; @@ -41,9 +40,9 @@ class ProblemTest extends \PHPUnit\Framework\TestCase private $subscriberMock; /** - * @var AbstractResource|\PHPUnit_Framework_MockObject_MockObject + * @var ProblemResource|\PHPUnit_Framework_MockObject_MockObject */ - private $abstractResourceMock; + private $resourceModelMock; /** * @var AbstractDb|\PHPUnit_Framework_MockObject_MockObject @@ -73,14 +72,14 @@ protected function setUp() $this->subscriberMock = $this->getMockBuilder(Subscriber::class) ->disableOriginalConstructor() ->getMock(); - $this->abstractResourceMock = $this->getMockBuilder(Problem::class) + $this->resourceModelMock = $this->getMockBuilder(ProblemResource::class) ->disableOriginalConstructor() ->getMock(); $this->abstractDbMock = $this->getMockBuilder(AbstractDb::class) ->disableOriginalConstructor() ->getMock(); - $this->abstractResourceMock->expects($this->any()) + $this->resourceModelMock->expects($this->any()) ->method('getIdFieldName') ->willReturn('id'); @@ -92,7 +91,7 @@ protected function setUp() 'context' => $this->contextMock, 'registry' => $this->registryMock, 'subscriberFactory' => $this->subscriberFactoryMock, - 'resource' => $this->abstractResourceMock, + 'resource' => $this->resourceModelMock, 'resourceCollection' => $this->abstractDbMock, 'data' => [], ] From 9a7fcaa54faf763d428bd629f6b22fe1dd25ec44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Fri, 17 Aug 2018 21:33:51 +0300 Subject: [PATCH 1062/1171] remove empty abs classes --- .../Magento/blank/web/css/source/_extends.less | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/app/design/frontend/Magento/blank/web/css/source/_extends.less b/app/design/frontend/Magento/blank/web/css/source/_extends.less index 6df78859a1a80..13da4a4d996cc 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_extends.less +++ b/app/design/frontend/Magento/blank/web/css/source/_extends.less @@ -803,20 +803,6 @@ } } -// -// Checkout order review price -// --------------------------------------------- - -.abs-checkout-cart-price { -} - -// -// Checkout order product name -// --------------------------------------------- - -.abs-checkout-product-name { -} - // // Checkout order review // --------------------------------------------- From f7e93677a2888d321e34a0f2b74501f6beebda50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Fri, 17 Aug 2018 21:37:18 +0300 Subject: [PATCH 1063/1171] removed empty abs class extend --- .../Magento_Multishipping/web/css/source/_module.less | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Multishipping/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Multishipping/web/css/source/_module.less index 66dbb30cb6712..96648f504af80 100644 --- a/app/design/frontend/Magento/blank/Magento_Multishipping/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Multishipping/web/css/source/_module.less @@ -113,14 +113,6 @@ } } } - - .cart-price { - &:extend(.abs-checkout-cart-price all); - } - - .product-item-name { - &:extend(.abs-checkout-product-name all); - } } &:not(.address) { From bac945cd6da6ca152e8cb49a4cee69dac752681b Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Sat, 18 Aug 2018 00:54:23 +0530 Subject: [PATCH 1064/1171] Fixed a couple of spelling mistakes --- .../Block/Adminhtml/Integration/Edit/Tab/Info.php | 2 +- lib/internal/Magento/Framework/App/DeploymentConfig.php | 2 +- setup/src/Magento/Setup/Console/Command/InstallCommand.php | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Integration/Block/Adminhtml/Integration/Edit/Tab/Info.php b/app/code/Magento/Integration/Block/Adminhtml/Integration/Edit/Tab/Info.php index cc08796d98e28..d8684e635b943 100644 --- a/app/code/Magento/Integration/Block/Adminhtml/Integration/Edit/Tab/Info.php +++ b/app/code/Magento/Integration/Block/Adminhtml/Integration/Edit/Tab/Info.php @@ -78,7 +78,7 @@ public function getTabTitle() } /** - * Returns status flag about this tab can be showen or not + * Returns status flag about this tab can be shown or not * * @return true */ diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig.php b/lib/internal/Magento/Framework/App/DeploymentConfig.php index f83f89ee4caeb..615c295675adc 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig.php @@ -117,7 +117,7 @@ public function resetData() } /** - * Check if data from deploy files is avaiable + * Check if data from deploy files is available * * @return bool * @since 100.1.3 diff --git a/setup/src/Magento/Setup/Console/Command/InstallCommand.php b/setup/src/Magento/Setup/Console/Command/InstallCommand.php index 65eb047a5c77e..d94beff15b396 100644 --- a/setup/src/Magento/Setup/Console/Command/InstallCommand.php +++ b/setup/src/Magento/Setup/Console/Command/InstallCommand.php @@ -50,7 +50,7 @@ class InstallCommand extends AbstractSetupCommand /** * List of comma-separated module names. That must be avoided during installation. * List of comma-separated module names. That must be avoided during installation. - * Avaiable magic param all. + * Available magic param all. */ const INPUT_KEY_DISABLE_MODULES = 'disable_modules'; @@ -166,14 +166,14 @@ protected function configure() null, InputOption::VALUE_OPTIONAL, 'List of comma-separated module names. That must be included during installation. ' - . 'Avaiable magic param "all".' + . 'Available magic param "all".' ), new InputOption( self::INPUT_KEY_DISABLE_MODULES, null, InputOption::VALUE_OPTIONAL, 'List of comma-separated module names. That must be avoided during installation. ' - . 'Avaiable magic param "all".' + . 'Available magic param "all".' ), new InputOption( self::CONVERT_OLD_SCRIPTS_KEY, From e0aa3e8d35a789ca383d508b9708ed4b836173d4 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <paliarus@adobe.com> Date: Fri, 17 Aug 2018 14:26:43 -0500 Subject: [PATCH 1065/1171] MAGETWO-94207: Cart GET for customer in REST returns 400 when cart not created --- .../Test/Unit/Model/Webapi/ParamOverriderCartIdTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Test/Unit/Model/Webapi/ParamOverriderCartIdTest.php b/app/code/Magento/Quote/Test/Unit/Model/Webapi/ParamOverriderCartIdTest.php index bc47a04a226e1..b533463d689a8 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Webapi/ParamOverriderCartIdTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Webapi/ParamOverriderCartIdTest.php @@ -67,6 +67,9 @@ public function testGetOverriddenValueIsCustomerAndCartExists() $this->assertSame($retValue, $this->model->getOverriddenValue()); } + /** + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + */ public function testGetOverriddenValueIsCustomerAndCartDoesNotExist() { $customerId = 1; @@ -83,7 +86,7 @@ public function testGetOverriddenValueIsCustomerAndCartDoesNotExist() ->with($customerId) ->will($this->throwException(new NoSuchEntityException())); - $this->assertNull($this->model->getOverriddenValue()); + $this->model->getOverriddenValue(); } public function testGetOverriddenValueIsCustomerAndCartIsNull() From 71104c498f3153159b3e762f8f139b1968f6b8a2 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Sat, 18 Aug 2018 01:16:56 +0530 Subject: [PATCH 1066/1171] Replace strval() function by using direct type casting to (string) --- .../Authorizenet/Model/Directpost/Request.php | 52 +++++++++---------- .../Backend/Currency/AbstractCurrency.php | 4 +- .../Paypal/Controller/Payflow/ReturnUrl.php | 2 +- app/code/Magento/Quote/Model/Quote.php | 2 +- .../Fixture/CatalogSearchQuery/QueryText.php | 2 +- .../Test/Fixture/UrlRewrite/TargetPath.php | 2 +- .../Framework/DB/Select/OrderRenderer.php | 4 +- .../Data/Form/Element/Checkboxes.php | 12 ++--- .../Magento/Framework/Filter/Translit.php | 2 +- .../Framework/Phrase/Renderer/Placeholder.php | 2 +- 10 files changed, 42 insertions(+), 42 deletions(-) diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Request.php b/app/code/Magento/Authorizenet/Model/Directpost/Request.php index d9a403e5c991e..fc78d836b6080 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost/Request.php +++ b/app/code/Magento/Authorizenet/Model/Directpost/Request.php @@ -112,50 +112,50 @@ public function setDataFromOrder( sprintf('%.2F', $order->getBaseShippingAmount()) ); - //need to use strval() because NULL values IE6-8 decodes as "null" in JSON in JavaScript, + //need to use (string) because NULL values IE6-8 decodes as "null" in JSON in JavaScript, //but we need "" for null values. $billing = $order->getBillingAddress(); if (!empty($billing)) { - $this->setXFirstName(strval($billing->getFirstname())) - ->setXLastName(strval($billing->getLastname())) - ->setXCompany(strval($billing->getCompany())) - ->setXAddress(strval($billing->getStreetLine(1))) - ->setXCity(strval($billing->getCity())) - ->setXState(strval($billing->getRegion())) - ->setXZip(strval($billing->getPostcode())) - ->setXCountry(strval($billing->getCountryId())) - ->setXPhone(strval($billing->getTelephone())) - ->setXFax(strval($billing->getFax())) - ->setXCustId(strval($billing->getCustomerId())) - ->setXCustomerIp(strval($order->getRemoteIp())) - ->setXCustomerTaxId(strval($billing->getTaxId())) - ->setXEmail(strval($order->getCustomerEmail())) - ->setXEmailCustomer(strval($paymentMethod->getConfigData('email_customer'))) - ->setXMerchantEmail(strval($paymentMethod->getConfigData('merchant_email'))); + $this->setXFirstName((string)$billing->getFirstname()) + ->setXLastName((string)$billing->getLastname()) + ->setXCompany((string)$billing->getCompany()) + ->setXAddress((string)$billing->getStreetLine(1)) + ->setXCity((string)$billing->getCity()) + ->setXState((string)$billing->getRegion()) + ->setXZip((string)$billing->getPostcode()) + ->setXCountry((string)$billing->getCountryId()) + ->setXPhone((string)$billing->getTelephone()) + ->setXFax((string)$billing->getFax()) + ->setXCustId((string)$billing->getCustomerId()) + ->setXCustomerIp((string)$order->getRemoteIp()) + ->setXCustomerTaxId((string)$billing->getTaxId()) + ->setXEmail((string)$order->getCustomerEmail()) + ->setXEmailCustomer((string)$paymentMethod->getConfigData('email_customer')) + ->setXMerchantEmail((string)$paymentMethod->getConfigData('merchant_email')); } $shipping = $order->getShippingAddress(); if (!empty($shipping)) { $this->setXShipToFirstName( - strval($shipping->getFirstname()) + (string)$shipping->getFirstname() )->setXShipToLastName( - strval($shipping->getLastname()) + (string)$shipping->getLastname() )->setXShipToCompany( - strval($shipping->getCompany()) + (string)$shipping->getCompany() )->setXShipToAddress( - strval($shipping->getStreetLine(1)) + (string)$shipping->getStreetLine(1) )->setXShipToCity( - strval($shipping->getCity()) + (string)$shipping->getCity() )->setXShipToState( - strval($shipping->getRegion()) + (string)$shipping->getRegion() )->setXShipToZip( - strval($shipping->getPostcode()) + (string)$shipping->getPostcode() )->setXShipToCountry( - strval($shipping->getCountryId()) + (string)$shipping->getCountryId() ); } - $this->setXPoNum(strval($payment->getPoNumber())); + $this->setXPoNum((string)$payment->getPoNumber()); return $this; } diff --git a/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php b/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php index b86b86ad3bb8c..4ae66bfd9692b 100644 --- a/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php +++ b/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php @@ -71,7 +71,7 @@ protected function _getCurrencyBase() $this->getScopeId() ); } - return strval($value); + return (string)$value; } /** @@ -88,7 +88,7 @@ protected function _getCurrencyDefault() $this->getScopeId() ); } - return strval($value); + return (string)$value; } /** diff --git a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php index a370eeb40eafd..73b4c9f6ee6ea 100644 --- a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php +++ b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php @@ -50,7 +50,7 @@ public function execute() $redirectBlock->setData('goto_success_page', true); } else { if ($this->checkPaymentMethod($order)) { - $gotoSection = $this->_cancelPayment(strval($this->getRequest()->getParam('RESPMSG'))); + $gotoSection = $this->_cancelPayment((string)$this->getRequest()->getParam('RESPMSG')); $redirectBlock->setData('goto_section', $gotoSection); $redirectBlock->setData('error_msg', __('Your payment has been declined. Please try again.')); } else { diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index b7a1b7d563ef6..5beb4527cf2a5 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -1608,7 +1608,7 @@ public function addProduct( * Error message */ if (is_string($cartCandidates) || $cartCandidates instanceof \Magento\Framework\Phrase) { - return strval($cartCandidates); + return (string)$cartCandidates; } /** diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery/QueryText.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery/QueryText.php index 400289eccda15..e2193b799c3be 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery/QueryText.php +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery/QueryText.php @@ -70,7 +70,7 @@ private function createProducts(FixtureFactory $fixtureFactory, $productsData) $products[] = $product; } elseif ($this->data === null) { - $this->data = strval($productData); + $this->data = (string)$productData; } } diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite/TargetPath.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite/TargetPath.php index cc2763c0e3cb9..7f0c29da03784 100644 --- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite/TargetPath.php +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Fixture/UrlRewrite/TargetPath.php @@ -44,7 +44,7 @@ public function __construct(FixtureFactory $fixtureFactory, array $params, $data $id = $this->entity->hasData('id') ? $this->entity->getId() : $this->entity->getPageId(); $this->data = preg_replace('`(%.*?%)`', $id, $data['entity']); } else { - $this->data = strval($data['entity']); + $this->data = (string)$data['entity']; } } diff --git a/lib/internal/Magento/Framework/DB/Select/OrderRenderer.php b/lib/internal/Magento/Framework/DB/Select/OrderRenderer.php index 36a075b9af8c9..c3d6e96c112e0 100644 --- a/lib/internal/Magento/Framework/DB/Select/OrderRenderer.php +++ b/lib/internal/Magento/Framework/DB/Select/OrderRenderer.php @@ -40,12 +40,12 @@ public function render(Select $select, $sql = '') $order = []; foreach ($select->getPart(Select::ORDER) as $term) { if (is_array($term)) { - if (is_numeric($term[0]) && strval(intval($term[0])) == $term[0]) { + if (is_numeric($term[0]) && (string)intval($term[0]) == $term[0]) { $order[] = (int)trim($term[0]) . ' ' . $term[1]; } else { $order[] = $this->quote->quoteIdentifier($term[0]) . ' ' . $term[1]; } - } elseif (is_numeric($term) && strval(intval($term)) == $term) { + } elseif (is_numeric($term) && (string)intval($term) == $term) { $order[] = (int)trim($term); } else { $order[] = $this->quote->quoteIdentifier($term); diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php b/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php index 3048be4d5dc1b..2a68432207b23 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php @@ -121,13 +121,13 @@ public function getChecked($value) return; } if (!is_array($checked)) { - $checked = [strval($checked)]; + $checked = [(string)$checked]; } else { foreach ($checked as $k => $v) { - $checked[$k] = strval($v); + $checked[$k] = (string)$v; } } - if (in_array(strval($value), $checked)) { + if (in_array((string)$value, $checked)) { return 'checked'; } return; @@ -141,13 +141,13 @@ public function getDisabled($value) { if ($disabled = $this->getData('disabled')) { if (!is_array($disabled)) { - $disabled = [strval($disabled)]; + $disabled = [(string)$disabled]; } else { foreach ($disabled as $k => $v) { - $disabled[$k] = strval($v); + $disabled[$k] = (string)$v; } } - if (in_array(strval($value), $disabled)) { + if (in_array((string)$value, $disabled)) { return 'disabled'; } } diff --git a/lib/internal/Magento/Framework/Filter/Translit.php b/lib/internal/Magento/Framework/Filter/Translit.php index 7a84a6e33af18..a04b0a0cfddda 100644 --- a/lib/internal/Magento/Framework/Filter/Translit.php +++ b/lib/internal/Magento/Framework/Filter/Translit.php @@ -409,7 +409,7 @@ public function __construct(\Magento\Framework\App\Config\ScopeConfigInterface $ $convertConfig = $config->getValue('url/convert', 'default'); if ($convertConfig) { foreach ($convertConfig as $configValue) { - $this->convertTable[strval($configValue['from'])] = strval($configValue['to']); + $this->convertTable[(string)$configValue['from']] = strval($configValue['to']); } } } diff --git a/lib/internal/Magento/Framework/Phrase/Renderer/Placeholder.php b/lib/internal/Magento/Framework/Phrase/Renderer/Placeholder.php index 4ba8c747fa12c..fd1b0c18ead17 100644 --- a/lib/internal/Magento/Framework/Phrase/Renderer/Placeholder.php +++ b/lib/internal/Magento/Framework/Phrase/Renderer/Placeholder.php @@ -40,6 +40,6 @@ public function render(array $source, array $arguments) */ private function keyToPlaceholder($key) { - return '%' . (is_int($key) ? strval($key + 1) : $key); + return '%' . (is_int($key) ? (string)($key + 1) : $key); } } From 26fd77357014bb9a4359ebdaa53e8ae5819d10b1 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sun, 19 Aug 2018 01:27:52 +0300 Subject: [PATCH 1067/1171] CMS: Add unit test for block model class --- .../Magento/Cms/Test/Unit/Model/BlockTest.php | 337 ++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 app/code/Magento/Cms/Test/Unit/Model/BlockTest.php diff --git a/app/code/Magento/Cms/Test/Unit/Model/BlockTest.php b/app/code/Magento/Cms/Test/Unit/Model/BlockTest.php new file mode 100644 index 0000000000000..448112b228a0d --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/Model/BlockTest.php @@ -0,0 +1,337 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Cms\Test\Unit\Model; + +use Magento\Cms\Model\Block; +use Magento\Cms\Model\ResourceModel\Block as BlockResource; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Model\Context; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +/** + * @covers \Magento\Cms\Model\Block + */ +class BlockTest extends \PHPUnit\Framework\TestCase +{ + /** + * Testable Object + * + * @var Block + */ + private $blockModel; + + /** + * Object Manager + * + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $eventManagerMock; + + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var BlockResource|\PHPUnit_Framework_MockObject_MockObject + */ + private $resourceMock; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->resourceMock = $this->createMock(BlockResource::class); + $this->eventManagerMock = $this->createMock(ManagerInterface::class); + $this->contextMock = $this->createMock(Context::class); + $this->contextMock->expects($this->any())->method('getEventDispatcher')->willReturn($this->eventManagerMock); + $this->objectManager = new ObjectManager($this); + $this->blockModel = $this->objectManager->getObject( + Block::class, + [ + 'context' => $this->contextMock, + 'resource' => $this->resourceMock, + ] + ); + } + + /** + * Test beforeSave method + * + * @return void + * + * @throws LocalizedException + */ + public function testBeforeSave() + { + $blockId = 7; + $this->blockModel->setData(Block::BLOCK_ID, $blockId); + $this->blockModel->setData(Block::CONTENT, 'test'); + $this->objectManager->setBackwardCompatibleProperty($this->blockModel, '_hasDataChanges', true); + $this->eventManagerMock->expects($this->atLeastOnce())->method('dispatch'); + $expected = $this->blockModel; + $actual = $this->blockModel->beforeSave(); + self::assertEquals($expected, $actual); + } + + /** + * Test beforeSave method + * + * @return void + * + * @throws LocalizedException + */ + public function testBeforeSaveWithException() + { + $blockId = 10; + $this->blockModel->setData(Block::BLOCK_ID, $blockId); + $this->blockModel->setData(Block::CONTENT, 'Test block_id="' . $blockId . '".'); + $this->objectManager->setBackwardCompatibleProperty($this->blockModel, '_hasDataChanges', false); + $this->eventManagerMock->expects($this->never())->method('dispatch'); + $this->expectException(LocalizedException::class); + $this->blockModel->beforeSave(); + } + + /** + * Test getIdentities method + * + * @return void + */ + public function testGetIdentities() + { + $result = $this->blockModel->getIdentities(); + self::assertInternalType('array', $result); + } + + /** + * Test getId method + * + * @return void + */ + public function testGetId() + { + $blockId = 12; + $this->blockModel->setData(Block::BLOCK_ID, $blockId); + $expected = $blockId; + $actual = $this->blockModel->getId(); + self::assertEquals($expected, $actual); + } + + /** + * Test getIdentifier method + * + * @return void + */ + public function testGetIdentifier() + { + $identifier = 'test01'; + $this->blockModel->setData(Block::IDENTIFIER, $identifier); + $expected = $identifier; + $actual = $this->blockModel->getIdentifier(); + self::assertEquals($expected, $actual); + } + + /** + * Test getTitle method + * + * @return void + */ + public function testGetTitle() + { + $title = 'test02'; + $this->blockModel->setData(Block::TITLE, $title); + $expected = $title; + $actual = $this->blockModel->getTitle(); + self::assertEquals($expected, $actual); + } + + /** + * Test getContent method + * + * @return void + */ + public function testGetContent() + { + $content = 'test03'; + $this->blockModel->setData(Block::CONTENT, $content); + $expected = $content; + $actual = $this->blockModel->getContent(); + self::assertEquals($expected, $actual); + } + + /** + * Test getCreationTime method + * + * @return void + */ + public function testGetCreationTime() + { + $creationTime = 'test04'; + $this->blockModel->setData(Block::CREATION_TIME, $creationTime); + $expected = $creationTime; + $actual = $this->blockModel->getCreationTime(); + self::assertEquals($expected, $actual); + } + + /** + * Test getUpdateTime method + * + * @return void + */ + public function testGetUpdateTime() + { + $updateTime = 'test05'; + $this->blockModel->setData(Block::UPDATE_TIME, $updateTime); + $expected = $updateTime; + $actual = $this->blockModel->getUpdateTime(); + self::assertEquals($expected, $actual); + } + + /** + * Test isActive method + * + * @return void + */ + public function testIsActive() + { + $isActive = true; + $this->blockModel->setData(Block::IS_ACTIVE, $isActive); + $result = $this->blockModel->isActive(); + self::assertTrue($result); + } + + /** + * Test setId method + * + * @return void + */ + public function testSetId() + { + $blockId = 15; + $this->blockModel->setId($blockId); + $expected = $blockId; + $actual = $this->blockModel->getData(Block::BLOCK_ID); + self::assertEquals($expected, $actual); + } + + /** + * Test setIdentifier method + * + * @return void + */ + public function testSetIdentifier() + { + $identifier = 'test06'; + $this->blockModel->setIdentifier($identifier); + $expected = $identifier; + $actual = $this->blockModel->getData(Block::IDENTIFIER); + self::assertEquals($expected, $actual); + } + + /** + * Test setTitle method + * + * @return void + */ + public function testSetTitle() + { + $title = 'test07'; + $this->blockModel->setTitle($title); + $expected = $title; + $actual = $this->blockModel->getData(Block::TITLE); + self::assertEquals($expected, $actual); + } + + /** + * Test setContent method + * + * @return void + */ + public function testSetContent() + { + $content = 'test08'; + $this->blockModel->setContent($content); + $expected = $content; + $actual = $this->blockModel->getData(Block::CONTENT); + self::assertEquals($expected, $actual); + } + + /** + * Test setCreationTime method + * + * @return void + */ + public function testSetCreationTime() + { + $creationTime = 'test09'; + $this->blockModel->setCreationTime($creationTime); + $expected = $creationTime; + $actual = $this->blockModel->getData(Block::CREATION_TIME); + self::assertEquals($expected, $actual); + } + + /** + * Test setUpdateTime method + * + * @return void + */ + public function testSetUpdateTime() + { + $updateTime = 'test10'; + $this->blockModel->setUpdateTime($updateTime); + $expected = $updateTime; + $actual = $this->blockModel->getData(Block::UPDATE_TIME); + self::assertEquals($expected, $actual); + } + + /** + * Test setIsActive method + * + * @return void + */ + public function testSetIsActive() + { + $this->blockModel->setIsActive(false); + $result = $this->blockModel->getData(Block::IS_ACTIVE); + self::assertFalse($result); + } + + /** + * Test getStores method + * + * @return void + */ + public function testGetStores() + { + $stores = [1, 4, 9]; + $this->blockModel->setData('stores', $stores); + $expected = $stores; + $actual = $this->blockModel->getStores(); + self::assertEquals($expected, $actual); + } + + /** + * Test getAvailableStatuses method + * + * @return void + */ + public function testGetAvailableStatuses() + { + $result = $this->blockModel->getAvailableStatuses(); + self::assertInternalType('array', $result); + } +} From aa465d5feba68ea657b61925376d628a1db27c5c Mon Sep 17 00:00:00 2001 From: Grayson <abken7642@gmail.com> Date: Wed, 15 Aug 2018 00:10:05 +0800 Subject: [PATCH 1068/1171] Fix Custom Attribute Group can not translation in catalog/product page --- .../Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7cd81419c0347..e425e1c732d75 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 @@ -287,7 +287,7 @@ public function modifyMeta(array $meta) if ($attributes) { $meta[$groupCode]['children'] = $this->getAttributesMeta($attributes, $groupCode); $meta[$groupCode]['arguments']['data']['config']['componentType'] = Fieldset::NAME; - $meta[$groupCode]['arguments']['data']['config']['label'] = __('%1', $group->getAttributeGroupName()); + $meta[$groupCode]['arguments']['data']['config']['label'] = __($group->getAttributeGroupName()); $meta[$groupCode]['arguments']['data']['config']['collapsible'] = true; $meta[$groupCode]['arguments']['data']['config']['dataScope'] = self::DATA_SCOPE_PRODUCT; $meta[$groupCode]['arguments']['data']['config']['sortOrder'] = From fe088901a8e2c32fc75baca5b32ec332a022651c Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sun, 19 Aug 2018 10:19:12 +0300 Subject: [PATCH 1069/1171] Make "time12h" validation rule to be compatible with minify js --- app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index e52791deaf2ad..1103f3e54a4cf 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -221,7 +221,7 @@ define([ ], 'time12h': [ function (value) { - return /^((0?[1-9]|1[012])(:[0-5]\d){0,2}(\ [AP]M))$/i.test(value); + return /^((0?[1-9]|1[012])(:[0-5]\d){0,2}(\s[AP]M))$/i.test(value); }, $.mage.__('Please enter a valid time, between 00:00 am and 12:00 pm') ], From 7bd7be3e0a1d0352abe3be738ff0ab32a3d9ce93 Mon Sep 17 00:00:00 2001 From: Mikalai Shostka <Mikalai_Shostka@epam.com> Date: Sun, 19 Aug 2018 16:58:05 +0300 Subject: [PATCH 1070/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Updated automated test. --- .../StorefrontAddProductToCardActionGroup.xml | 66 ---------------- .../Test/Mftf/Data/SimpleProductData.xml | 18 ----- .../StorefrontAddProductToCardSection.xml | 76 ------------------- .../AddingProductWithExpiredSessionTest.xml | 27 +++---- 4 files changed, 12 insertions(+), 175 deletions(-) delete mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Data/SimpleProductData.xml delete mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml deleted file mode 100644 index 4f6b7e31a9c5b..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddProductToCardActionGroup.xml +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> - - <!--Go To Products page--> - <actionGroup name="GoToProductPage"> - <click selector="{{GoToProductPageSection.catalog}}" stepKey="clickOnCatalog" /> - <waitForPageLoad stepKey="waitForPage"/> - <click selector="{{GoToProductPageSection.product}}" stepKey="clickToSelectProductsItem" /> - <waitForPageLoad stepKey="waitForPageProducts"/> - </actionGroup> - - <!--Create Simple product--> - <actionGroup name="AdminCreateSimpleProduct"> - <click selector="{{GoToProductPageSection.add}}" stepKey="clickToAddProduct"/> - <waitForPageLoad stepKey="WaitForProductPageIsLoaded"/> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProductOne.name}}" stepKey="setNameForProduct"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{SimpleProductOne.sku}}" stepKey="setSKUForProduct"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{SimpleProductOne.price}}" stepKey="setPriceForProduct"/> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{SimpleProductOne.quantity}}" stepKey="setQuantityForProduct"/> - <click selector="{{AdminProductFormSection.searchOptimization}}" stepKey="clickOnSearchEngineOptimization"/> - <fillField selector="{{AdminProductFormSection.urlKey}}" userInput="{{SimpleProductOne.urlKey}}" stepKey="setSearchUrlForProduct"/> - <click selector="{{AdminProductFormSection.saveButton}}" stepKey="clickSaveProduct"/> - <waitForPageLoad stepKey="WaitForProductSave"/> - <see userInput="You saved the product." stepKey="seeSaveConfirmation"/> - </actionGroup> - - <actionGroup name="FindAndAddProductToCardActGr"> - <click selector="{{StorefrontAddProductToCartSection.addToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" time="30" stepKey="waitForProductAdded"/> - <click selector="{{StorefrontAddProductToCartSection.showCard}}" stepKey="clickToOpenCard"/> - <waitForPageLoad stepKey="WaitForFormOpened"/> - <click selector="{{StorefrontAddProductToCartSection.proceed}}" stepKey="clickToProceedToCheckout"/> - <waitForPageLoad stepKey="waitForTheFormIsOpened"/> - <see userInput="Shipping Address" stepKey="seeShippingAddress"/> - </actionGroup> - - <actionGroup name="DeleteCreatedProductActionGroup"> - <conditionalClick selector="{{DeleteCreatedProduct.clearAll}}" dependentSelector="{{DeleteCreatedProduct.clearAll}}" visible="1" stepKey="clickClearAllIfThereIsAnyValue"/> - <click stepKey="clickFilterButton" selector="{{DeleteCreatedProduct.filterButton}}"/> - <waitForElementVisible selector="{{DeleteCreatedProduct.filterSKUField}}" stepKey="waitForFilterDataLoaded"/> - <fillField stepKey="searchProductUsingSKUField" selector="{{DeleteCreatedProduct.filterSKUField}}" userInput="{{SimpleProductOne.sku}}"/> - <click stepKey="clickFiltersApplyButton" selector="{{DeleteCreatedProduct.filtersApplyButton}}"/> - <waitForElementNotVisible selector="{{DeleteCreatedProduct.filterSKUField}}" stepKey="waitForFilterBecomeNotVisible"/> - <click selector="{{DeleteCreatedProduct.createdProductID}}" stepKey="selectCreatedProduct"/> - <wait stepKey="waitSelectCreatedProduct" time="2"/> - <waitForElementVisible selector="{{DeleteCreatedProduct.actionSelectBox}}" stepKey="waitToSelectActionVisible" time="50"/> - <click stepKey="clickToSelectAction" selector="{{DeleteCreatedProduct.actionSelectBox}}"/> - <waitForElementVisible selector="{{DeleteCreatedProduct.deleteButton}}" stepKey="waitForDeleteButtonAppeared" time="2"/> - <click selector="{{DeleteCreatedProduct.deleteButton}}" stepKey="clickToDeleteProduct"/> - <waitForElementVisible selector="{{DeleteCreatedProduct.okButton}}" stepKey="waitForOkButtonAppeared" time="2"/> - <click selector="{{DeleteCreatedProduct.okButton}}" stepKey="clickToConfirm"/> - <wait stepKey="waitForRecordIsDeleted" time="2"/> - <see userInput="A total of 1 record(s) have been deleted." stepKey="productDeletedSuccessfully"/> - <click stepKey="clickClearAllFilterButton" selector="{{DeleteCreatedProduct.clearAll}}"/> - <!-- We need this wait to make sure that Active filters is clear (waitForElementNotVisible tag doesn't wait until clearing filters)--> - <wait stepKey="waitToClearAllFilters" time="2"/> - </actionGroup> - -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/SimpleProductData.xml b/app/code/Magento/Customer/Test/Mftf/Data/SimpleProductData.xml deleted file mode 100644 index e9eacb6e37a11..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Data/SimpleProductData.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="SimpleProductOne" type="product"> - <data key="name" unique="suffix">testProduct</data> - <data key="sku" unique="suffix">testSku</data> - <data key="price">200</data> - <data key="quantity">100</data> - <data key="urlKey" unique="suffix">testProduct</data> - </entity> -</entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml deleted file mode 100644 index cc0cf8e07008e..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontAddProductToCardSection.xml +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="StorefrontAddProductToCartSection"> - <element name="addToCartBtn" type="button" selector="button.action.tocart.primary"/> - <element name="successMsg" type="button" selector="div.message-success"/> - <element name="showCard" type="button" selector=".action.showcart"/> - <element name="proceed" type="button" selector="#top-cart-btn-checkout"/> - </section> - - <section name="GoToProductPageSection"> - <!--Go to Catalog/Products--> - <element name="catalog" type="button" selector="#menu-magento-catalog-catalog"/> - <element name="product" type="button" selector="//span[contains(text(), 'Products')]"/> - <element name="add" type="button" selector="#add_new_product-button"/> - </section> - - <section name="AdminProductFormSection"> - <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> - <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> - <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> - <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> - <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> - <element name="productStatus" type="checkbox" selector="input[name='product[status]']"/> - <element name="enableProductLabel" type="checkbox" selector="input[name='product[status]']+label"/> - <element name="productStatusUseDefault" type="checkbox" selector="input[name='use_default[status]']"/> - <element name="productNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> - <element name="productPrice" type="input" selector=".admin__field[data-index=price] input"/> - <element name="productTaxClassUseDefault" type="checkbox" selector="input[name='use_default[tax_class_id]']"/> - <element name="advancedPricingLink" type="button" selector="button[data-index='advanced_pricing_button']"/> - <element name="categoriesDropdown" type="multiselect" selector="div[data-index='category_ids']"/> - <element name="productQuantity" type="input" selector=".admin__field[data-index=qty] input"/> - <element name="productStockStatus" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]']"/> - <element name="productWeight" type="input" selector=".admin__field[data-index=weight] input"/> - <element name="productWeightSelect" type="select" selector="select[name='product[product_has_weight]']"/> - <element name="contentTab" type="button" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Content']"/> - <element name="fieldError" type="text" selector="//input[@name='product[{{fieldName}}]']/following-sibling::label[@class='admin__field-error']" parameterized="true"/> - <element name="priceFieldError" type="text" selector="//input[@name='product[price]']/parent::div/parent::div/label[@class='admin__field-error']"/> - <element name="addAttributeBtn" type="button" selector="#addAttribute"/> - <element name="createNewAttributeBtn" type="button" selector="button[data-index='add_new_attribute_button']"/> - <element name="save" type="button" selector="#save"/> - <element name="attributeTab" type="button" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Attributes']"/> - <element name="attributeLabel" type="input" selector="//input[@name='frontend_label[0]']"/> - <element name="frontendInput" type="select" selector="select[name = 'frontend_input']"/> - <element name="productFormTab" type="button" selector="//strong[@class='admin__collapsible-title']/span[contains(text(), '{{tabName}}')]" parameterized="true"/> - <element name="productFormTabState" type="text" selector="//strong[@class='admin__collapsible-title']/span[contains(text(), '{{tabName}}')]/parent::*/parent::*[@data-state-collapsible='{{state}}']" parameterized="true"/> - <element name="visibility" type="select" selector="//select[@name='product[visibility]']"/> - <element name="visibilityUseDefault" type="checkbox" selector="//input[@name='use_default[visibility]']"/> - <element name="divByDataIndex" type="input" selector="div[data-index='{{var}}']" parameterized="true"/> - <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//span[text()='{{attributeLabel}}']" parameterized="true"/> - <element name="searchOptimization" type="button" selector="//*[contains(text(),'Search Engine Optimization')]"/> - <element name="urlKey" type="input" selector="//input[contains(@name,'url_key')]"/> - <element name="saveButton" type="button" selector="#save-button"/> - </section> - - <section name="DeleteCreatedProduct"> - <element name="searchToKeyword" type="input" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/input"/> - <element name="searchButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/button"/> - <element name="createdProductID" type="select" selector="//*[@class='data-grid-checkbox-cell-inner']/input"/> - <element name="actionSelectBox" type="button" selector="//*[@class='col-xs-2']//span[text()='Actions']"/> - <element name="deleteButton" type="button" selector="//div[@class='col-xs-2']//*[text()='Delete']"/> - <element name="okButton" type="button" selector=".action-primary.action-accept"/> - <element name="clearAll" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[contains(text(), 'Clear all')]"/> - <element name="filterButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-filters-action-wrap']/button"/> - <element name="filterSKUField" type="input" selector="//*[@name='sku']"/> - <element name="filtersApplyButton" type="button" selector="//*[contains(text(),'Apply Filters')]"/> - </section> - -</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml index 1359893ec090e..0e2ac68eb180b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml @@ -19,30 +19,27 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <actionGroup ref="GoToProductPage" stepKey="goToProductPage"/> - <actionGroup ref="AdminCreateSimpleProduct" stepKey="adminCreateSimpleProduct"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> </before> - <!--Navigate to a category page --> - <amOnPage url="/{{SimpleProductOne.urlKey}}.html" stepKey="GoToProductPage"/> - + <!--Navigate to a category page --> + <amOnPage url="$$createSimpleProduct.name$$.html" stepKey="goToProductPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> <!-- Remove PHPSESSID and form_key to replicate an expired session--> - <executeJS function="var delete_cookie = function(name) { - document.cookie = name + '=;expires=Thu, 01 Jan 1970 00:00:01 UTC; path=/;';}; - delete_cookie('PHPSESSID'); - delete_cookie('form_key');" stepKey="removeCookies" after="waitForPageLoad"/> + <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> + <resetCookie userInput="form_key" stepKey="resetCookieForCart2"/> <!-- "Add to Cart" any product--> - <actionGroup ref="FindAndAddProductToCardActGr" stepKey="addProductToCard"/> - + <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontProductPageSection.errorMsg}}" time="30" stepKey="assertErrorMessage"/> <after> <!--Delete created product--> - <amOnPage url="/admin" stepKey="GoToDashboard"/> - <actionGroup ref="GoToProductPage" stepKey="againGoToProductPage"/> - <actionGroup ref="DeleteCreatedProductActionGroup" stepKey="deleteCreatedProduct"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> </after> </test> From b27e02824049ac4151b1ec330a8494bdb98bcc3e Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Mon, 13 Aug 2018 17:53:21 +0530 Subject: [PATCH 1071/1171] Translated validation error messages --- app/code/Magento/CatalogSearch/i18n/en_US.csv | 1 + .../CatalogSearch/view/frontend/templates/advanced/form.phtml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogSearch/i18n/en_US.csv b/app/code/Magento/CatalogSearch/i18n/en_US.csv index 9121520774ccc..ba97dc9de1d31 100644 --- a/app/code/Magento/CatalogSearch/i18n/en_US.csv +++ b/app/code/Magento/CatalogSearch/i18n/en_US.csv @@ -37,3 +37,4 @@ name,name "Minimal Query Length","Minimal Query Length" "Maximum Query Length","Maximum Query Length" "Rebuild Catalog product fulltext search index","Rebuild Catalog product fulltext search index" +"Please enter a valid price range.","Please enter a valid price range." diff --git a/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/form.phtml b/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/form.phtml index 53a301022873b..95ea7fcef3a1a 100644 --- a/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/form.phtml +++ b/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/form.phtml @@ -147,8 +147,8 @@ require([ } }, messages: { - 'price[to]': {'greater-than-equals-to': 'Please enter a valid price range.'}, - 'price[from]': {'less-than-equals-to': 'Please enter a valid price range.'} + 'price[to]': {'greater-than-equals-to': '<?= /* @escapeNotVerified */ __('Please enter a valid price range.') ?>'}, + 'price[from]': {'less-than-equals-to': '<?= /* @escapeNotVerified */ __('Please enter a valid price range.') ?>'} } }); }); From 2f3d7b6046a90382eda51da6cdd20af5b1311a13 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Mon, 20 Aug 2018 13:48:55 +0300 Subject: [PATCH 1072/1171] MAGETWO-64315: [API] catalogProductAttributeRepository does not return "frontend_labels" value --- .../Product/Attribute/RepositoryTest.php | 24 +++++++++++++++++++ .../Entity/Attribute/AbstractAttribute.php | 6 ++--- .../Test/Unit/Model/Entity/AttributeTest.php | 10 ++++++++ .../Api/ProductAttributeRepositoryTest.php | 17 +++++++++++++ 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php index e9820b07af1b8..1b42b09e5dd32 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php @@ -71,6 +71,9 @@ class RepositoryTest extends \PHPUnit\Framework\TestCase */ private $optionManagementMock; + /** + * @inheritdoc + */ protected function setUp() { $this->attributeResourceMock = @@ -115,6 +118,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testGet() { $attributeCode = 'some attribute code'; @@ -127,6 +133,9 @@ public function testGet() $this->model->get($attributeCode); } + /** + * @return void + */ public function testGetList() { $searchCriteriaMock = $this->createMock(\Magento\Framework\Api\SearchCriteria::class); @@ -140,6 +149,9 @@ public function testGetList() $this->model->getList($searchCriteriaMock); } + /** + * @return void + */ public function testDelete() { $attributeMock = $this->createMock(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); @@ -148,6 +160,9 @@ public function testDelete() $this->assertEquals(true, $this->model->delete($attributeMock)); } + /** + * @return void + */ public function testDeleteById() { $attributeCode = 'some attribute code'; @@ -163,6 +178,9 @@ public function testDeleteById() $this->assertEquals(true, $this->model->deleteById($attributeCode)); } + /** + * @return void + */ public function testGetCustomAttributesMetadata() { $searchCriteriaMock = $this->createMock(\Magento\Framework\Api\SearchCriteria::class); @@ -243,6 +261,9 @@ public function testSaveInputExceptionInvalidFieldValue() $this->model->save($attributeMock); } + /** + * @return void + */ public function testSaveDoesNotSaveAttributeOptionsIfOptionsAreAbsentInPayload() { $attributeId = 1; @@ -268,6 +289,9 @@ public function testSaveDoesNotSaveAttributeOptionsIfOptionsAreAbsentInPayload() $this->model->save($attributeMock); } + /** + * @return void + */ public function testSaveSavesDefaultFrontendLabelIfItIsPresentInPayload() { $labelMock = $this->createMock(\Magento\Eav\Api\Data\AttributeFrontendLabelInterface::class); diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php index b8b6d2ae39d64..6601c05051378 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php @@ -167,8 +167,8 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data - * @param FrontendLabelFactory|null $frontendLabelFactory * @param \Magento\Eav\Api\Data\AttributeExtensionFactory|null $eavExtensionFactory + * @param FrontendLabelFactory|null $frontendLabelFactory * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @codeCoverageIgnore */ @@ -188,8 +188,8 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - FrontendLabelFactory $frontendLabelFactory = null, - \Magento\Eav\Api\Data\AttributeExtensionFactory $eavExtensionFactory = null + \Magento\Eav\Api\Data\AttributeExtensionFactory $eavExtensionFactory = null, + FrontendLabelFactory $frontendLabelFactory = null ) { parent::__construct( $context, diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php index 18c94381a5054..b15174960524c 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php @@ -13,11 +13,17 @@ class AttributeTest extends \PHPUnit\Framework\TestCase */ protected $_model; + /** + * @inheritdoc + */ protected function setUp() { $this->_model = $this->createPartialMock(\Magento\Eav\Model\Entity\Attribute::class, ['__wakeup']); } + /** + * @inheritdoc + */ protected function tearDown() { $this->_model = null; @@ -27,6 +33,7 @@ protected function tearDown() * @param string $givenFrontendInput * @param string $expectedBackendType * @dataProvider dataGetBackendTypeByInput + * @return void */ public function testGetBackendTypeByInput($givenFrontendInput, $expectedBackendType) { @@ -113,6 +120,9 @@ public function getSortWeightDataProvider() ]; } + /** + * return void + */ public function testGetFrontendLabels() { $attributeId = 1; diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php index b7715309b12df..386bd9fc9aeeb 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php @@ -23,6 +23,7 @@ class ProductAttributeRepositoryTest extends \Magento\TestFramework\TestCase\Web /** * @magentoApiDataFixture Magento/Catalog/_files/product_attribute.php + * @return void */ public function testGet() { @@ -35,6 +36,9 @@ public function testGet() $this->assertEquals($attributeCode, $attribute['attribute_code']); } + /** + * @return void + */ public function testGetList() { $searchCriteria = [ @@ -83,6 +87,7 @@ public function testGetList() /** * @magentoApiDataFixture Magento/Catalog/Model/Product/Attribute/_files/create_attribute_service.php + * @return void */ public function testCreate() { @@ -117,6 +122,7 @@ public function testCreate() /** * @magentoApiDataFixture Magento/Catalog/_files/product_attribute.php + * @return void */ public function testCreateWithExceptionIfAttributeAlreadyExists() { @@ -133,6 +139,7 @@ public function testCreateWithExceptionIfAttributeAlreadyExists() /** * @magentoApiDataFixture Magento/Catalog/Model/Product/Attribute/_files/create_attribute_service.php + * @return void */ public function testUpdate() { @@ -202,6 +209,7 @@ public function testUpdate() /** * @magentoApiDataFixture Magento/Catalog/Model/Product/Attribute/_files/create_attribute_service.php + * @return void */ public function testUpdateWithNoDefaultLabelAndAdminStorelabel() { @@ -234,6 +242,7 @@ public function testUpdateWithNoDefaultLabelAndAdminStorelabel() /** * @magentoApiDataFixture Magento/Catalog/Model/Product/Attribute/_files/create_attribute_service.php + * @return void */ public function testUpdateWithNoDefaultLabelAndNoAdminStoreLabel() { @@ -265,6 +274,7 @@ public function testUpdateWithNoDefaultLabelAndNoAdminStoreLabel() /** * @magentoApiDataFixture Magento/Catalog/Model/Product/Attribute/_files/create_attribute_service.php + * @return void */ public function testUpdateWithNewOption() { @@ -302,6 +312,7 @@ public function testUpdateWithNewOption() /** * @magentoApiDataFixture Magento/Catalog/_files/product_attribute.php + * @return void */ public function testDeleteById() { @@ -309,6 +320,9 @@ public function testDeleteById() $this->assertTrue($this->deleteAttribute($attributeCode)); } + /** + * @return void + */ public function testDeleteNoSuchEntityException() { $attributeCode = 'some_test_code'; @@ -490,6 +504,9 @@ protected function updateAttribute($attributeCode, $attributeData) return $this->_webApiCall($serviceInfo, $attributeData); } + /** + * @inheritdoc + */ protected function tearDown() { foreach ($this->createdAttributes as $attributeCode) { From 88aa0bd9b77856f2f10a507cb120fd4e9309fa96 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Mon, 20 Aug 2018 14:47:29 +0300 Subject: [PATCH 1073/1171] Message component fix: the message type is always error when parameters specified --- app/code/Magento/Ui/view/frontend/web/js/model/messages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/frontend/web/js/model/messages.js b/app/code/Magento/Ui/view/frontend/web/js/model/messages.js index b970b2236155f..fb9a20c054da2 100644 --- a/app/code/Magento/Ui/view/frontend/web/js/model/messages.js +++ b/app/code/Magento/Ui/view/frontend/web/js/model/messages.js @@ -55,7 +55,7 @@ define([ return messageObj.parameters.shift(); }); this.clear(); - this.errorMessages.push(message); + type.push(message); return true; }, From a9361b9b1c8df62246112263ee066c02a43734bd Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 20 Aug 2018 15:33:04 +0300 Subject: [PATCH 1074/1171] GraphQL-87: Fetch attribute values and labels for customAttributeMetadata --- .../Model/Resolver/AttributeOptions.php | 94 ++++++++++++------- .../DataProvider/AttributeOptions.php | 54 +++++++++++ 2 files changed, 114 insertions(+), 34 deletions(-) create mode 100644 app/code/Magento/EavGraphQl/Model/Resolver/DataProvider/AttributeOptions.php diff --git a/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php b/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php index 72f483b7445bb..6ccd610bead0d 100644 --- a/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php +++ b/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php @@ -7,8 +7,12 @@ namespace Magento\EavGraphQl\Model\Resolver; -use Magento\Eav\Api\AttributeOptionManagementInterface; +use Magento\EavGraphQl\Model\Resolver\DataProvider\AttributeOptions as AttributeOptionsDataProvider; +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\StateException; use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\Resolver\Value; use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -20,31 +24,29 @@ class AttributeOptions implements ResolverInterface { /** - * @var AttributeOptionManagementInterface + * @var AttributeOptionsDataProvider */ - protected $optionManager; + private $attributeOptionsDataProvider; /** - * @var ValueFactory + * @var AttributeOptions */ - protected $valueFactory; + private $valueFactory; /** - * AttributeOptions constructor. - * - * @param AttributeOptionManagementInterface $optionManager + * @param AttributeOptionsDataProvider $attributeOptionsDataProvider * @param ValueFactory $valueFactory */ public function __construct( - AttributeOptionManagementInterface $optionManager, + AttributeOptionsDataProvider $attributeOptionsDataProvider, ValueFactory $valueFactory ) { - $this->optionManager = $optionManager; + $this->attributeOptionsDataProvider = $attributeOptionsDataProvider; $this->valueFactory = $valueFactory; } /** - * {@inheritDoc} + * @inheritDoc */ public function resolve( Field $field, @@ -53,36 +55,60 @@ public function resolve( array $value = null, array $args = null ) : Value { - $options = []; - $entityType = !empty($value['entity_type']) ? $value['entity_type'] : ''; - $attributeCode = !empty($value['attribute_code']) ? $value['attribute_code'] : ''; + return $this->valueFactory->create(function () use ($value) { + $entityType = $this->getEntityType($value); + $attributeCode = $this->getAttributeCode($value); - try { - /** @var \Magento\Eav\Api\Data\AttributeOptionInterface[] $attributeOptions */ - $attributeOptions = $this->optionManager->getItems($entityType, $attributeCode); - } catch (\Exception $e) { - $attributeOptions = []; + $optionsData = $this->getAttributeOptionsData($entityType, $attributeCode); + return $optionsData; + }); + } + + /** + * @param array $value + * @return int + * @throws GraphQlInputException + */ + private function getEntityType(array $value): int + { + if (!isset($value['entity_type'])) { + throw new GraphQlInputException(__('"Entity type should be specified')); } - if (is_array($attributeOptions)) { - /** @var \Magento\Eav\Api\Data\AttributeOptionInterface $option */ - foreach ($attributeOptions as $option) { - if ($option->getValue() === '') { - continue; - } + return (int)$value['entity_type']; + } - $options[] = [ - 'label' => $option->getLabel(), - 'value' => $option->getValue() - ]; - } + /** + * @param array $value + * @return string + * @throws GraphQlInputException + */ + private function getAttributeCode(array $value): string + { + if (!isset($value['attribute_code'])) { + throw new GraphQlInputException(__('"Attribute code should be specified')); } - $result = function () use ($options) { - return $options; - }; + return $value['attribute_code']; + } - return $this->valueFactory->create($result); + /** + * @param int $entityType + * @param string $attributeCode + * @return array + * @throws GraphQlInputException + * @throws GraphQlNoSuchEntityException + */ + private function getAttributeOptionsData(int $entityType, string $attributeCode): array + { + try { + $optionsData = $this->attributeOptionsDataProvider->getData($entityType, $attributeCode); + } catch (InputException $e) { + throw new GraphQlInputException(__($e->getMessage()), $e); + } catch (StateException $e) { + throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e); + } + return $optionsData; } } diff --git a/app/code/Magento/EavGraphQl/Model/Resolver/DataProvider/AttributeOptions.php b/app/code/Magento/EavGraphQl/Model/Resolver/DataProvider/AttributeOptions.php new file mode 100644 index 0000000000000..900a31c1093ed --- /dev/null +++ b/app/code/Magento/EavGraphQl/Model/Resolver/DataProvider/AttributeOptions.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\EavGraphQl\Model\Resolver\DataProvider; + +use Magento\Eav\Api\AttributeOptionManagementInterface; + +/** + * Attribute Options data provider + */ +class AttributeOptions +{ + /** + * @var AttributeOptionManagementInterface + */ + private $optionManager; + + /** + * @param AttributeOptionManagementInterface $optionManager + */ + public function __construct( + AttributeOptionManagementInterface $optionManager + ) { + $this->optionManager = $optionManager; + } + + /** + * @param int $entityType + * @param string $attributeCode + * @return array + */ + public function getData(int $entityType, string $attributeCode): array + { + $options = $this->optionManager->getItems($entityType, $attributeCode); + + $optionsData = []; + foreach ($options as $option) { + // without empty option @see \Magento\Eav\Model\Entity\Attribute\Source\Table::getAllOptions + if ($option->getValue() === '') { + continue; + } + + $optionsData[] = [ + 'label' => $option->getLabel(), + 'value' => $option->getValue() + ]; + } + return $optionsData; + } +} From b77f96e83998e3b305390ef0ba0902b7a2d90211 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 20 Aug 2018 16:01:22 +0300 Subject: [PATCH 1075/1171] GraphQL-87: Fetch attribute values and labels for customAttributeMetadata --- app/code/Magento/EavGraphQl/composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/EavGraphQl/composer.json b/app/code/Magento/EavGraphQl/composer.json index 6da27ed27cf36..b878a0ec3aa68 100644 --- a/app/code/Magento/EavGraphQl/composer.json +++ b/app/code/Magento/EavGraphQl/composer.json @@ -4,7 +4,8 @@ "type": "magento2-module", "require": { "php": "~7.1.3||~7.2.0", - "magento/framework": "*" + "magento/framework": "*", + "magento/eav": "*" }, "suggest": { "magento/module-graph-ql": "*" From 5d204c7e1622385dc17c4b9941b77ad20098941c Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 20 Aug 2018 16:02:29 +0300 Subject: [PATCH 1076/1171] MAGETWO-93962: [2.3] Gift Message lost at Checkout after merging quotes --- .../Model/Plugin/MergeQuoteItems.php | 36 +++++ .../Observer/SalesEventQuoteMerge.php | 5 +- .../Magento/GiftMessage/etc/frontend/di.xml | 3 + app/code/Magento/Quote/Model/Quote.php | 61 ++++----- .../Quote/Model/Quote/Item/Processor.php | 28 +++- .../Magento/Quote/Model/QuoteTest.php | 126 +++++++++++++----- 6 files changed, 187 insertions(+), 72 deletions(-) create mode 100644 app/code/Magento/GiftMessage/Model/Plugin/MergeQuoteItems.php diff --git a/app/code/Magento/GiftMessage/Model/Plugin/MergeQuoteItems.php b/app/code/Magento/GiftMessage/Model/Plugin/MergeQuoteItems.php new file mode 100644 index 0000000000000..2c097cc9a6653 --- /dev/null +++ b/app/code/Magento/GiftMessage/Model/Plugin/MergeQuoteItems.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GiftMessage\Model\Plugin; + +use Magento\Quote\Model\Quote\Item; +use Magento\Quote\Model\Quote\Item\Processor; + +class MergeQuoteItems +{ + /** + * Resolves gift message to be + * applied to merged quote items. + * + * @param Processor $subject + * @param Item $result + * @param Item $source + * @return Item + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterMerge(Processor $subject, Item $result, Item $source): Item + { + $giftMessageId = $source->getGiftMessageId(); + + if ($giftMessageId) { + $result->setGiftMessageId($giftMessageId); + } + + return $result; + } +} diff --git a/app/code/Magento/GiftMessage/Observer/SalesEventQuoteMerge.php b/app/code/Magento/GiftMessage/Observer/SalesEventQuoteMerge.php index 0d2280d29fed4..044a0bf91c982 100644 --- a/app/code/Magento/GiftMessage/Observer/SalesEventQuoteMerge.php +++ b/app/code/Magento/GiftMessage/Observer/SalesEventQuoteMerge.php @@ -28,7 +28,10 @@ public function execute(\Magento\Framework\Event\Observer $observer) /** @var Quote $sourceQuote */ $sourceQuote = $observer->getData('source'); - $targetQuote->setGiftMessageId($sourceQuote->getGiftMessageId()); + $giftMessageId = $sourceQuote->getGiftMessageId(); + if ($giftMessageId) { + $targetQuote->setGiftMessageId($giftMessageId); + } return $this; } diff --git a/app/code/Magento/GiftMessage/etc/frontend/di.xml b/app/code/Magento/GiftMessage/etc/frontend/di.xml index 1566c51ee9df3..a4837e0180c0b 100644 --- a/app/code/Magento/GiftMessage/etc/frontend/di.xml +++ b/app/code/Magento/GiftMessage/etc/frontend/di.xml @@ -43,4 +43,7 @@ </argument> </arguments> </type> + <type name="Magento\Quote\Model\Quote\Item\Processor"> + <plugin name="mergeQuoteItems" type="Magento\GiftMessage\Model\Plugin\MergeQuoteItems"/> + </type> </config> diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index b7a1b7d563ef6..892fecdeceaf7 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -526,7 +526,7 @@ public function getCurrency() } /** - * {@inheritdoc} + * @inheritdoc */ public function setCurrency(\Magento\Quote\Api\Data\CurrencyInterface $currency = null) { @@ -534,7 +534,7 @@ public function setCurrency(\Magento\Quote\Api\Data\CurrencyInterface $currency } /** - * {@inheritdoc} + * @inheritdoc */ public function getItems() { @@ -542,7 +542,7 @@ public function getItems() } /** - * {@inheritdoc} + * @inheritdoc */ public function setItems(array $items = null) { @@ -550,7 +550,7 @@ public function setItems(array $items = null) } /** - * {@inheritdoc} + * @inheritdoc */ public function getCreatedAt() { @@ -558,7 +558,7 @@ public function getCreatedAt() } /** - * {@inheritdoc} + * @inheritdoc */ public function setCreatedAt($createdAt) { @@ -566,7 +566,7 @@ public function setCreatedAt($createdAt) } /** - * {@inheritdoc} + * @inheritdoc */ public function getUpdatedAt() { @@ -574,7 +574,7 @@ public function getUpdatedAt() } /** - * {@inheritdoc} + * @inheritdoc */ public function setUpdatedAt($updatedAt) { @@ -582,7 +582,7 @@ public function setUpdatedAt($updatedAt) } /** - * {@inheritdoc} + * @inheritdoc */ public function getConvertedAt() { @@ -590,7 +590,7 @@ public function getConvertedAt() } /** - * {@inheritdoc} + * @inheritdoc */ public function setConvertedAt($convertedAt) { @@ -598,7 +598,7 @@ public function setConvertedAt($convertedAt) } /** - * {@inheritdoc} + * @inheritdoc */ public function getIsActive() { @@ -606,7 +606,7 @@ public function getIsActive() } /** - * {@inheritdoc} + * @inheritdoc */ public function setIsActive($isActive) { @@ -614,7 +614,7 @@ public function setIsActive($isActive) } /** - * {@inheritdoc} + * @inheritdoc */ public function setIsVirtual($isVirtual) { @@ -622,7 +622,7 @@ public function setIsVirtual($isVirtual) } /** - * {@inheritdoc} + * @inheritdoc */ public function getItemsCount() { @@ -630,7 +630,7 @@ public function getItemsCount() } /** - * {@inheritdoc} + * @inheritdoc */ public function setItemsCount($itemsCount) { @@ -638,7 +638,7 @@ public function setItemsCount($itemsCount) } /** - * {@inheritdoc} + * @inheritdoc */ public function getItemsQty() { @@ -646,7 +646,7 @@ public function getItemsQty() } /** - * {@inheritdoc} + * @inheritdoc */ public function setItemsQty($itemsQty) { @@ -654,7 +654,7 @@ public function setItemsQty($itemsQty) } /** - * {@inheritdoc} + * @inheritdoc */ public function getOrigOrderId() { @@ -662,7 +662,7 @@ public function getOrigOrderId() } /** - * {@inheritdoc} + * @inheritdoc */ public function setOrigOrderId($origOrderId) { @@ -670,7 +670,7 @@ public function setOrigOrderId($origOrderId) } /** - * {@inheritdoc} + * @inheritdoc */ public function getReservedOrderId() { @@ -678,7 +678,7 @@ public function getReservedOrderId() } /** - * {@inheritdoc} + * @inheritdoc */ public function setReservedOrderId($reservedOrderId) { @@ -686,7 +686,7 @@ public function setReservedOrderId($reservedOrderId) } /** - * {@inheritdoc} + * @inheritdoc */ public function getCustomerIsGuest() { @@ -694,7 +694,7 @@ public function getCustomerIsGuest() } /** - * {@inheritdoc} + * @inheritdoc */ public function setCustomerIsGuest($customerIsGuest) { @@ -702,7 +702,7 @@ public function setCustomerIsGuest($customerIsGuest) } /** - * {@inheritdoc} + * @inheritdoc */ public function getCustomerNote() { @@ -710,7 +710,7 @@ public function getCustomerNote() } /** - * {@inheritdoc} + * @inheritdoc */ public function setCustomerNote($customerNote) { @@ -718,7 +718,7 @@ public function setCustomerNote($customerNote) } /** - * {@inheritdoc} + * @inheritdoc */ public function getCustomerNoteNotify() { @@ -726,7 +726,7 @@ public function getCustomerNoteNotify() } /** - * {@inheritdoc} + * @inheritdoc */ public function setCustomerNoteNotify($customerNoteNotify) { @@ -736,7 +736,7 @@ public function setCustomerNoteNotify($customerNoteNotify) //@codeCoverageIgnoreEnd /** - * {@inheritdoc} + * @inheritdoc */ public function getStoreId() { @@ -747,7 +747,7 @@ public function getStoreId() } /** - * {@inheritdoc} + * @inheritdoc */ public function setStoreId($storeId) { @@ -1078,7 +1078,7 @@ public function getCustomerGroupId() } /** - * {@inheritdoc} + * @inheritdoc */ public function getCustomerTaxClassId() { @@ -1097,7 +1097,7 @@ public function getCustomerTaxClassId() } /** - * {@inheritdoc} + * @inheritdoc */ public function setCustomerTaxClassId($customerTaxClassId) { @@ -2341,6 +2341,7 @@ public function merge(Quote $quote) foreach ($this->getAllItems() as $quoteItem) { if ($quoteItem->compare($item)) { $quoteItem->setQty($quoteItem->getQty() + $item->getQty()); + $this->itemProcessor->merge($item, $quoteItem); $found = true; break; } diff --git a/app/code/Magento/Quote/Model/Quote/Item/Processor.php b/app/code/Magento/Quote/Model/Quote/Item/Processor.php index f34591cfad143..2577008ecbae3 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/Processor.php +++ b/app/code/Magento/Quote/Model/Quote/Item/Processor.php @@ -5,7 +5,7 @@ */ namespace Magento\Quote\Model\Quote\Item; -use \Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product; use Magento\Quote\Model\Quote\ItemFactory; use Magento\Quote\Model\Quote\Item; use Magento\Store\Model\StoreManagerInterface; @@ -53,12 +53,12 @@ public function __construct( /** * Initialize quote item object * - * @param \Magento\Framework\DataObject $request + * @param DataObject $request * @param Product $product * - * @return \Magento\Quote\Model\Quote\Item + * @return Item */ - public function init(Product $product, $request) + public function init(Product $product, DataObject $request): Item { $item = $this->quoteItemFactory->create(); @@ -82,11 +82,11 @@ public function init(Product $product, $request) * Set qty and custom price for quote item * * @param Item $item - * @param \Magento\Framework\DataObject $request + * @param DataObject $request * @param Product $candidate * @return void */ - public function prepare(Item $item, DataObject $request, Product $candidate) + public function prepare(Item $item, DataObject $request, Product $candidate): void { /** * We specify qty after we know about parent (for stock) @@ -103,13 +103,27 @@ public function prepare(Item $item, DataObject $request, Product $candidate) } } + /** + * Merge two quote items. + * + * @param Item $source + * @param Item $target + * @return Item + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function merge(Item $source, Item $target): Item + { + return $target; + } + /** * Set store_id value to quote item * * @param Item $item * @return void */ - protected function setItemStoreId(Item $item) + protected function setItemStoreId(Item $item): void { if ($this->appState->getAreaCode() === \Magento\Backend\App\Area\FrontNameResolver::AREA_CODE) { $storeId = $this->storeManager->getStore($this->storeManager->getStore()->getId()) diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php index 5df185cad7ac3..6ea25c8f337df 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php @@ -3,10 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Quote\Model; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\ProductRepository; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Customer\Model\Data\Customer; use Magento\Framework\Exception\LocalizedException; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; @@ -54,7 +60,7 @@ public function testCollectTotalsWithVirtual(): void $quote->load('test01', 'reserved_order_id'); $productRepository = $this->objectManager->create( - \Magento\Catalog\Api\ProductRepositoryInterface::class + ProductRepositoryInterface::class ); $product = $productRepository->get('virtual-product', false, null, true); $quote->addProduct($product); @@ -89,7 +95,6 @@ public function testSetCustomerData(): void $this->assertEquals($expected, $this->convertToArray($customer)); $quote->setCustomer($customer); - // $customer = $quote->getCustomer(); $this->assertEquals($expected, $this->convertToArray($customer)); $this->assertEquals('qa@example.com', $quote->getCustomerEmail()); @@ -335,7 +340,7 @@ public function testAddProductUpdateItem(): void $productStockQty = 100; $productRepository = $this->objectManager->create( - \Magento\Catalog\Api\ProductRepositoryInterface::class + ProductRepositoryInterface::class ); $product = $productRepository->get('simple-1', false, null, true); @@ -368,14 +373,12 @@ public function testAddProductUpdateItem(): void * Customer with two addresses created. First address is default billing, second is default shipping. * * @param Quote $quote - * @return \Magento\Customer\Api\Data\CustomerInterface + * @return CustomerInterface */ - protected function _prepareQuoteForTestAssignCustomerWithAddressChange( - Quote $quote - ): \Magento\Customer\Api\Data\CustomerInterface { - /** @var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */ + protected function _prepareQuoteForTestAssignCustomerWithAddressChange(Quote $quote): CustomerInterface + { $customerRepository = $this->objectManager->create( - \Magento\Customer\Api\CustomerRepositoryInterface::class + CustomerRepositoryInterface::class ); $fixtureCustomerId = 1; /** @var \Magento\Customer\Model\Customer $customer */ @@ -421,25 +424,24 @@ protected function removeIdFromCustomerData(array $customerData): array protected function _getCustomerDataArray(): array { return [ - \Magento\Customer\Model\Data\Customer::CONFIRMATION => 'test', - \Magento\Customer\Model\Data\Customer::CREATED_AT => '2/3/2014', - \Magento\Customer\Model\Data\Customer::CREATED_IN => 'Default', - \Magento\Customer\Model\Data\Customer::DEFAULT_BILLING => 'test', - \Magento\Customer\Model\Data\Customer::DEFAULT_SHIPPING => 'test', - \Magento\Customer\Model\Data\Customer::DOB => '2014-02-03 00:00:00', - \Magento\Customer\Model\Data\Customer::EMAIL => 'qa@example.com', - \Magento\Customer\Model\Data\Customer::FIRSTNAME => 'Joe', - \Magento\Customer\Model\Data\Customer::GENDER => 0, - \Magento\Customer\Model\Data\Customer::GROUP_ID => - \Magento\Customer\Model\GroupManagement::NOT_LOGGED_IN_ID, - \Magento\Customer\Model\Data\Customer::ID => 1, - \Magento\Customer\Model\Data\Customer::LASTNAME => 'Dou', - \Magento\Customer\Model\Data\Customer::MIDDLENAME => 'Ivan', - \Magento\Customer\Model\Data\Customer::PREFIX => 'Dr.', - \Magento\Customer\Model\Data\Customer::STORE_ID => 1, - \Magento\Customer\Model\Data\Customer::SUFFIX => 'Jr.', - \Magento\Customer\Model\Data\Customer::TAXVAT => 1, - \Magento\Customer\Model\Data\Customer::WEBSITE_ID => 1 + Customer::CONFIRMATION => 'test', + Customer::CREATED_AT => '2/3/2014', + Customer::CREATED_IN => 'Default', + Customer::DEFAULT_BILLING => 'test', + Customer::DEFAULT_SHIPPING => 'test', + Customer::DOB => '2014-02-03 00:00:00', + Customer::EMAIL => 'qa@example.com', + Customer::FIRSTNAME => 'Joe', + Customer::GENDER => 0, + Customer::GROUP_ID => \Magento\Customer\Model\GroupManagement::NOT_LOGGED_IN_ID, + Customer::ID => 1, + Customer::LASTNAME => 'Dou', + Customer::MIDDLENAME => 'Ivan', + Customer::PREFIX => 'Dr.', + Customer::STORE_ID => 1, + Customer::SUFFIX => 'Jr.', + Customer::TAXVAT => 1, + Customer::WEBSITE_ID => 1 ]; } @@ -496,7 +498,7 @@ public function testGetItemById(): void $quoteItem = $this->objectManager->create(\Magento\Quote\Model\Quote\Item::class); - $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); $product = $productRepository->get('simple'); $quoteItem->setProduct($product); @@ -510,22 +512,78 @@ public function testGetItemById(): void /** * Tests of quotes merging. * + * @param int|null $guestItemGiftMessageId + * @param int|null $customerItemGiftMessageId + * @param int|null $guestOrderGiftMessageId + * @param int|null $customerOrderGiftMessageId + * @param int|null $expectedItemGiftMessageId + * @param int|null $expectedOrderGiftMessageId + * * @magentoDataFixture Magento/Sales/_files/quote.php + * @dataProvider giftMessageDataProvider + * @throws LocalizedException * @return void */ - public function testMerge(): void - { - $giftMessageId = 1; + public function testMerge( + $guestItemGiftMessageId, + $customerItemGiftMessageId, + $guestOrderGiftMessageId, + $customerOrderGiftMessageId, + $expectedItemGiftMessageId, + $expectedOrderGiftMessageId + ): void { + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $product = $productRepository->get('simple', false, null, true); /** @var Quote $quote */ $guestQuote = $this->getQuote('test01'); - $guestQuote->setGiftMessageId($giftMessageId); + $guestQuote->setGiftMessageId($guestOrderGiftMessageId); /** @var Quote $customerQuote */ $customerQuote = $this->objectManager->create(Quote::class); + $customerQuote->setReservedOrderId('test02') + ->setStoreId($guestQuote->getStoreId()) + ->addProduct($product); + $customerQuote->setGiftMessageId($customerOrderGiftMessageId); + + $guestItem = $guestQuote->getItemByProduct($product); + $guestItem->setGiftMessageId($guestItemGiftMessageId); + + $customerItem = $customerQuote->getItemByProduct($product); + $customerItem->setGiftMessageId($customerItemGiftMessageId); + $customerQuote->merge($guestQuote); + $mergedItemItem = $customerQuote->getItemByProduct($product); + + self::assertEquals($expectedOrderGiftMessageId, $customerQuote->getGiftMessageId()); + self::assertEquals($expectedItemGiftMessageId, $mergedItemItem->getGiftMessageId()); + } - self::assertEquals($giftMessageId, $customerQuote->getGiftMessageId()); + /** + * Provides order- and item-level gift message Id. + * + * @return array + */ + public function giftMessageDataProvider(): array + { + return [ + [ + 'guestItemId' => null, + 'customerItemId' => 1, + 'guestOrderId' => null, + 'customerOrderId' => 11, + 'expectedItemId' => 1, + 'expectedOrderId' => 11 + ], + [ + 'guestItemId' => 1, + 'customerItemId' => 2, + 'guestOrderId' => 11, + 'customerOrderId' => 22, + 'expectedItemId' => 1, + 'expectedOrderId' => 11 + ] + ]; } /** From a84e6d85fd0cb6f2b9c8541a2cefc5a2d087aef9 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 20 Aug 2018 16:30:24 +0300 Subject: [PATCH 1077/1171] GraphQL-87: Fetch attribute values and labels for customAttributeMetadata --- app/code/Magento/EavGraphQl/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/EavGraphQl/composer.json b/app/code/Magento/EavGraphQl/composer.json index b878a0ec3aa68..a2c2d025a3d9d 100644 --- a/app/code/Magento/EavGraphQl/composer.json +++ b/app/code/Magento/EavGraphQl/composer.json @@ -5,7 +5,7 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/eav": "*" + "magento/module-eav": "*" }, "suggest": { "magento/module-graph-ql": "*" From 7220e131f9ae28ad599197dbaf6a0443dc8e70ca Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 20 Aug 2018 16:31:20 +0300 Subject: [PATCH 1078/1171] Review: add missing unit test for Observer --- ...essProductAfterDeleteEventObserverTest.php | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 app/code/Magento/Review/Test/Unit/Observer/ProcessProductAfterDeleteEventObserverTest.php diff --git a/app/code/Magento/Review/Test/Unit/Observer/ProcessProductAfterDeleteEventObserverTest.php b/app/code/Magento/Review/Test/Unit/Observer/ProcessProductAfterDeleteEventObserverTest.php new file mode 100644 index 0000000000000..1a8490a921716 --- /dev/null +++ b/app/code/Magento/Review/Test/Unit/Observer/ProcessProductAfterDeleteEventObserverTest.php @@ -0,0 +1,121 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Review\Test\Unit\Observer; + +use Magento\Catalog\Model\Product; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Review\Model\ResourceModel\Rating; +use Magento\Review\Model\ResourceModel\Review; +use Magento\Review\Observer\ProcessProductAfterDeleteEventObserver; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject; + +/** + * Class ProcessProductAfterDeleteEventObserverTest + */ +class ProcessProductAfterDeleteEventObserverTest extends TestCase +{ + /** + * Testable Object + * + * @var ProcessProductAfterDeleteEventObserver + */ + private $observer; + + /** + * @var Review|PHPUnit_Framework_MockObject_MockObject + */ + private $_resourceReviewMock; + + /** + * @var Rating|PHPUnit_Framework_MockObject_MockObject + */ + private $_resourceRatingMock; + + /** + * Set up + */ + protected function setUp() + { + $this->_resourceReviewMock = $this->createMock(Review::class); + $this->_resourceRatingMock = $this->createMock(Rating::class); + + $this->observer = new ProcessProductAfterDeleteEventObserver( + $this->_resourceReviewMock, + $this->_resourceRatingMock + ); + } + + /** + * Test cleanup product reviews after product delete + * + * @return void + */ + public function testCleanupProductReviewsWithProduct() + { + $productId = 1; + $observerMock = $this->createMock(Observer::class); + $eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getProduct']) + ->getMock(); + + $productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getId']) + ->getMock(); + + $productMock->expects(self::exactly(3)) + ->method('getId') + ->willReturn($productId); + $eventMock->expects($this->once()) + ->method('getProduct') + ->willReturn($productMock); + $observerMock->expects($this->once()) + ->method('getEvent') + ->willReturn($eventMock); + $this->_resourceReviewMock->expects($this->once()) + ->method('deleteReviewsByProductId') + ->willReturnSelf(); + $this->_resourceRatingMock->expects($this->once()) + ->method('deleteAggregatedRatingsByProductId') + ->willReturnSelf(); + + $this->observer->execute($observerMock); + } + + /** + * Test with no event product + * + * @return void + */ + public function testCleanupProductReviewsWithoutProduct() + { + $observerMock = $this->createMock(Observer::class); + $eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getProduct']) + ->getMock(); + + $eventMock->expects($this->once()) + ->method('getProduct') + ->willReturn(null); + $observerMock->expects($this->once()) + ->method('getEvent') + ->willReturn($eventMock); + $this->_resourceReviewMock->expects($this->never()) + ->method('deleteReviewsByProductId') + ->willReturnSelf(); + $this->_resourceRatingMock->expects($this->never()) + ->method('deleteAggregatedRatingsByProductId') + ->willReturnSelf(); + + $this->observer->execute($observerMock); + } +} From 1ac3fb14d75cd1b509067b2dd1ac505726bbb650 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Mon, 20 Aug 2018 10:48:04 -0500 Subject: [PATCH 1079/1171] MAGETWO-90591: Add Selected Button shouldn't be enabled if there are no images available - update function test --- .../Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml index 74d8f9962540a..e9d17b5c70ddd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml @@ -39,6 +39,7 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoading2" /> <see selector="{{ProductDescriptionWYSIWYGToolbarSection.CancelBtn}}" userInput="Cancel" stepKey="seeCancelBtn1" /> <see selector="{{ProductDescriptionWYSIWYGToolbarSection.CreateFolder}}" userInput="Create Folder" stepKey="seeCreateFolderBtn1" /> + <dontSeeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertFile}}" stepKey="dontSeeAddSelectedBtn" /> <click selector="{{ProductDescriptionWYSIWYGToolbarSection.CreateFolder}}" stepKey="createFolder1"/> <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.FolderName}}" stepKey="waitForPopUp1" /> <fillField selector="{{ProductDescriptionWYSIWYGToolbarSection.FolderName}}" userInput="{{ImageFolder.name}}" stepKey="fillFolderName1" /> @@ -59,6 +60,7 @@ <click selector="{{ProductDescriptionWYSIWYGToolbarSection.confirmDelete}}" stepKey="confirmDelete1" /> <waitForElementNotVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.image(ImageUpload1.value)}}" stepKey="waitForImageDeleted1" /> <dontSeeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.image(ImageUpload1.value)}}" stepKey="dontSeeImage1" /> + <dontSeeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertFile}}" stepKey="dontSeeAddSelectedBtn1" /> <attachFile selector="{{ProductDescriptionWYSIWYGToolbarSection.BrowseUploadImage}}" userInput="{{ImageUpload1.value}}" stepKey="uploadImage2"/> <waitForLoadingMaskToDisappear stepKey="waitForLoading6" /> <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.image(ImageUpload1.value)}}" stepKey="waitForUploadImage2" /> @@ -77,6 +79,7 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoading8" /> <see selector="{{ProductShortDescriptionWYSIWYGToolbarSection.CancelBtn}}" userInput="Cancel" stepKey="seeCancelBtn2" /> <see selector="{{ProductShortDescriptionWYSIWYGToolbarSection.CreateFolder}}" userInput="Create Folder" stepKey="seeCreateFolderBtn2" /> + <dontSeeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertFile}}" stepKey="dontSeeAddSelectedBtn2" /> <attachFile selector="{{ProductShortDescriptionWYSIWYGToolbarSection.BrowseUploadImage}}" userInput="{{ImageUpload3.value}}" stepKey="uploadImage3"/> <waitForElementVisible selector="{{ProductShortDescriptionWYSIWYGToolbarSection.image(ImageUpload3.value)}}" stepKey="waitForUploadImage3" /> <waitForLoadingMaskToDisappear stepKey="waitForLoading9" /> @@ -86,6 +89,7 @@ <click selector="{{ProductShortDescriptionWYSIWYGToolbarSection.DeleteSelectedBtn}}" stepKey="clickDeleteSelected2" /> <waitForText userInput="OK" stepKey="waitForConfirm3" /> <click selector="{{ProductShortDescriptionWYSIWYGToolbarSection.confirmDelete}}" stepKey="confirmDelete2" /> + <dontSeeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertFile}}" stepKey="dontSeeAddSelectedBtn3" /> <attachFile selector="{{ProductShortDescriptionWYSIWYGToolbarSection.BrowseUploadImage}}" userInput="{{ImageUpload3.value}}" stepKey="uploadImage4"/> <waitForLoadingMaskToDisappear stepKey="waitForLoading10" /> <waitForElementVisible selector="{{ProductShortDescriptionWYSIWYGToolbarSection.image(ImageUpload3.value)}}" stepKey="waitForUploadImage4" /> From 0d7e4b7087370c136433be2df743d6af302d07a9 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Mon, 20 Aug 2018 14:27:04 -0400 Subject: [PATCH 1080/1171] MQE-1174: Deliver weekly regression enablement tests - Try MFTF 2.3.x-dev --- composer.json | 2 +- composer.lock | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index b7435e98695c3..50927a2ebfee9 100644 --- a/composer.json +++ b/composer.json @@ -81,7 +81,7 @@ "zendframework/zend-view": "~2.10.0" }, "require-dev": { - "magento/magento2-functional-testing-framework": "2.3.4", + "magento/magento2-functional-testing-framework": "2.3.x-dev", "friendsofphp/php-cs-fixer": "~2.12.0", "lusitanian/oauth": "~0.8.10", "pdepend/pdepend": "2.5.2", diff --git a/composer.lock b/composer.lock index d90750e2547ef..3a4a3c1aaf317 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "74013a4027763e05b29b127b4c03c752", + "content-hash": "bd3e37613fe27f3d28a70d353676d0a2", "packages": [ { "name": "braintree/braintree_php", @@ -6183,16 +6183,16 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.3.4", + "version": "2.3.x-dev", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "ac56e5a6520dd580658034ae53d3724985c2a901" + "reference": "2d4b061399b2327ae6f9f5e4ad6a380f62ab8e35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/ac56e5a6520dd580658034ae53d3724985c2a901", - "reference": "ac56e5a6520dd580658034ae53d3724985c2a901", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/2d4b061399b2327ae6f9f5e4ad6a380f62ab8e35", + "reference": "2d4b061399b2327ae6f9f5e4ad6a380f62ab8e35", "shasum": "" }, "require": { @@ -6250,7 +6250,7 @@ "magento", "testing" ], - "time": "2018-08-10T20:16:42+00:00" + "time": "2018-08-15T17:20:25+00:00" }, { "name": "moontoast/math", @@ -8873,6 +8873,7 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { + "magento/magento2-functional-testing-framework": 20, "phpmd/phpmd": 0 }, "prefer-stable": true, From 24af0e1740c59db8198cbb8648583e38b4388389 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Mon, 20 Aug 2018 13:56:45 -0500 Subject: [PATCH 1081/1171] MAGETWO-91678: Minimum Order amount required in Admin orders --- dev/tests/acceptance/.gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/tests/acceptance/.gitignore b/dev/tests/acceptance/.gitignore index de6a96d05e7e2..e3ef78df82b9f 100755 --- a/dev/tests/acceptance/.gitignore +++ b/dev/tests/acceptance/.gitignore @@ -1,7 +1,10 @@ .idea .env +.htaccess codeception.yml tests/_output/* tests/functional.suite.yml tests/functional/Magento/FunctionalTest/_generated vendor/* +mftf.log +/utils/ \ No newline at end of file From d6c31524d85b0c0248cde1814f98d56b3096a47d Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@adobe.com> Date: Mon, 20 Aug 2018 15:33:58 -0500 Subject: [PATCH 1082/1171] ENGCOM-2841: Fix external Sodium Encryption PR --- .../Setup/Patch/Data/SodiumChachaPatch.php | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php index 69bc47d2c6732..aae30026b2b77 100644 --- a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php +++ b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php @@ -1,5 +1,8 @@ <?php - +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ declare(strict_types=1); namespace Magento\EncryptionKey\Setup\Patch\Data; @@ -27,26 +30,26 @@ class SodiumChachaPatch implements DataPatchInterface private $encryptor; /** - * @var \Magento\Framework\Config\ScopeInterface + * @var \Magento\Framework\App\State */ - private $scope; + private $state; /** * @param \Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup * @param \Magento\Config\Model\Config\Structure\Proxy $structure * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor - * @param \Magento\Framework\Config\ScopeInterface $scope + * @param \Magento\Framework\App\State $state */ public function __construct( \Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup, \Magento\Config\Model\Config\Structure\Proxy $structure, \Magento\Framework\Encryption\EncryptorInterface $encryptor, - \Magento\Framework\Config\ScopeInterface $scope + \Magento\Framework\App\State $state ) { $this->moduleDataSetup = $moduleDataSetup; $this->structure = $structure; $this->encryptor = $encryptor; - $this->scope = $scope; + $this->state = $state; } /** @@ -79,17 +82,16 @@ public function getAliases() private function reEncryptSystemConfigurationValues() { - $currentScope = $this->scope->getCurrentScope(); - - $this->scope->setCurrentScope(\Magento\Framework\App\Area::AREA_ADMINHTML); - - $paths = $this->structure->getFieldPathsByAttribute( - 'backend_model', - \Magento\Config\Model\Config\Backend\Encrypted::class + $structure = $this->structure; + $paths = $this->state->emulateAreaCode( + \Magento\Framework\App\Area::AREA_ADMINHTML, + function () use ($structure) { + return $structure->getFieldPathsByAttribute( + 'backend_model', + \Magento\Config\Model\Config\Backend\Encrypted::class + ); + } ); - - $this->scope->setCurrentScope($currentScope); - // walk through found data and re-encrypt it if ($paths) { $table = $this->moduleDataSetup->getTable('core_config_data'); From 0cfa53a96f3eb0d82d7f7a8faca90c6800ab4850 Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@adobe.com> Date: Mon, 20 Aug 2018 16:42:24 -0500 Subject: [PATCH 1083/1171] ENGCOM-2841: Fix external Sodium Encryption PR --- .../Setup/Patch/Data/SodiumChachaPatchTest.php | 4 ++-- .../Magento/Sales/_files/payment_enc_cc.php | 4 ++-- .../Adapter/EncryptionAdapterInterface.php | 4 ++++ .../Framework/Encryption/Adapter/Mcrypt.php | 16 ++++++++++------ .../Encryption/Adapter/SodiumChachaIetf.php | 4 ++++ 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatchTest.php b/dev/tests/integration/testsuite/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatchTest.php index 96d511e9bc81e..3a47692bdb932 100644 --- a/dev/tests/integration/testsuite/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatchTest.php +++ b/dev/tests/integration/testsuite/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatchTest.php @@ -81,7 +81,7 @@ public function testChangeEncryptionKey() private function legacyEncrypt(string $data): string { - // @codingStandardIgnoreStart + // @codingStandardsIgnoreStart $handle = @mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, ''); $initVectorSize = @mcrypt_enc_get_iv_size($handle); $initVector = str_repeat("\0", $initVectorSize); @@ -91,7 +91,7 @@ private function legacyEncrypt(string $data): string @mcrypt_generic_deinit($handle); @mcrypt_module_close($handle); - // @codingStandardIgnoreEnd + // @codingStandardsIgnoreEnd return '0:' . Encryptor::CIPHER_RIJNDAEL_256 . ':' . base64_encode($encrpted); } diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/payment_enc_cc.php b/dev/tests/integration/testsuite/Magento/Sales/_files/payment_enc_cc.php index 35f1aaa6a2911..bfa643fcf5f99 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/payment_enc_cc.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/payment_enc_cc.php @@ -22,7 +22,7 @@ * Creates an encrypted card number with the current crypt key using * a legacy cipher. */ -// @codingStandardIgnoreStart +// @codingStandardsIgnoreStart $handle = @mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, ''); $initVectorSize = @mcrypt_enc_get_iv_size($handle); $initVector = str_repeat("\0", $initVectorSize); @@ -32,7 +32,7 @@ @mcrypt_generic_deinit($handle); @mcrypt_module_close($handle); -// @codingStandardIgnoreEnd +// @codingStandardsIgnoreEnd /** @var SearchCriteria $searchCriteria */ $searchCriteria = $objectManager->get(SearchCriteriaBuilder::class) diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/EncryptionAdapterInterface.php b/lib/internal/Magento/Framework/Encryption/Adapter/EncryptionAdapterInterface.php index 3dd661f197781..b9bbb089ae0cc 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/EncryptionAdapterInterface.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/EncryptionAdapterInterface.php @@ -1,4 +1,8 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ declare(strict_types=1); diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php index 61cebac0c6e4b..3660b28657b58 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/Mcrypt.php @@ -1,4 +1,8 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ declare(strict_types=1); @@ -47,17 +51,17 @@ public function __construct( ) { $this->cipher = $cipher; $this->mode = $mode; - // @codingStandardIgnoreLine + // @codingStandardsIgnoreLine $this->handle = @mcrypt_module_open($cipher, '', $mode, ''); try { - // @codingStandardIgnoreLine + // @codingStandardsIgnoreLine $maxKeySize = @mcrypt_enc_get_key_size($this->handle); if (strlen($key) > $maxKeySize) { throw new \Magento\Framework\Exception\LocalizedException( new \Magento\Framework\Phrase('Key must not exceed %1 bytes.', [$maxKeySize]) ); } - // @codingStandardIgnoreLine + // @codingStandardsIgnoreLine $initVectorSize = @mcrypt_enc_get_iv_size($this->handle); if (null === $initVector) { /* Set vector to zero bytes to not use it */ @@ -72,11 +76,11 @@ public function __construct( } $this->initVector = $initVector; } catch (\Exception $e) { - // @codingStandardIgnoreLine + // @codingStandardsIgnoreLine @mcrypt_module_close($this->handle); throw new \Magento\Framework\Exception\LocalizedException(new \Magento\Framework\Phrase($e->getMessage())); } - // @codingStandardIgnoreLine + // @codingStandardsIgnoreLine @mcrypt_generic_init($this->handle, $key, $initVector); } @@ -156,7 +160,7 @@ public function decrypt(string $data): string if (strlen($data) == 0) { return $data; } - // @codingStandardIgnoreLine + // @codingStandardsIgnoreLine $data = @mdecrypt_generic($this->handle, $data); /* * Returned string can in fact be longer than the unencrypted string due to the padding of the data diff --git a/lib/internal/Magento/Framework/Encryption/Adapter/SodiumChachaIetf.php b/lib/internal/Magento/Framework/Encryption/Adapter/SodiumChachaIetf.php index 36fc388498ba2..9f9facf98ff84 100644 --- a/lib/internal/Magento/Framework/Encryption/Adapter/SodiumChachaIetf.php +++ b/lib/internal/Magento/Framework/Encryption/Adapter/SodiumChachaIetf.php @@ -1,4 +1,8 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ declare(strict_types=1); From aa2e0fa03622db4ec34dce9d3eedbeb6f18ff120 Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@adobe.com> Date: Mon, 20 Aug 2018 16:45:10 -0500 Subject: [PATCH 1084/1171] ENGCOM-2841: Fix external Sodium Encryption PR --- .../Magento/Test/Legacy/_files/blacklist/obsolete_mage.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php index 0f7231e19aeed..801a62957b527 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php @@ -10,4 +10,5 @@ 'dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php', 'lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/CompiledTest.php', 'dev/tests/integration/testsuite/Magento/Indexer/Model/Config/_files/result.php', + 'lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php', ]; From d4591334974ea66586923d087528187dd1cb4ede Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@adobe.com> Date: Mon, 20 Aug 2018 17:04:40 -0500 Subject: [PATCH 1085/1171] ENGCOM-2841: Fix external Sodium Encryption PR --- .../Magento/Test/Legacy/_files/blacklist/obsolete_mage.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php index 801a62957b527..62816cd4e4f76 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php @@ -11,4 +11,5 @@ 'lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/CompiledTest.php', 'dev/tests/integration/testsuite/Magento/Indexer/Model/Config/_files/result.php', 'lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php', + 'lib/internal/Magento/Framework/Encryption/Test/Unit/CryptTest.php' ]; From 5b568abe49ff56f160fdc2c3572f5a2290aa47ca Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 21 Aug 2018 10:28:44 +0300 Subject: [PATCH 1086/1171] Review: fixing the properties' naming --- ...essProductAfterDeleteEventObserverTest.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Review/Test/Unit/Observer/ProcessProductAfterDeleteEventObserverTest.php b/app/code/Magento/Review/Test/Unit/Observer/ProcessProductAfterDeleteEventObserverTest.php index 1a8490a921716..9d3980a99f9a4 100644 --- a/app/code/Magento/Review/Test/Unit/Observer/ProcessProductAfterDeleteEventObserverTest.php +++ b/app/code/Magento/Review/Test/Unit/Observer/ProcessProductAfterDeleteEventObserverTest.php @@ -31,24 +31,24 @@ class ProcessProductAfterDeleteEventObserverTest extends TestCase /** * @var Review|PHPUnit_Framework_MockObject_MockObject */ - private $_resourceReviewMock; + private $resourceReviewMock; /** * @var Rating|PHPUnit_Framework_MockObject_MockObject */ - private $_resourceRatingMock; + private $resourceRatingMock; /** * Set up */ protected function setUp() { - $this->_resourceReviewMock = $this->createMock(Review::class); - $this->_resourceRatingMock = $this->createMock(Rating::class); + $this->resourceReviewMock = $this->createMock(Review::class); + $this->resourceRatingMock = $this->createMock(Rating::class); $this->observer = new ProcessProductAfterDeleteEventObserver( - $this->_resourceReviewMock, - $this->_resourceRatingMock + $this->resourceReviewMock, + $this->resourceRatingMock ); } @@ -80,10 +80,10 @@ public function testCleanupProductReviewsWithProduct() $observerMock->expects($this->once()) ->method('getEvent') ->willReturn($eventMock); - $this->_resourceReviewMock->expects($this->once()) + $this->resourceReviewMock->expects($this->once()) ->method('deleteReviewsByProductId') ->willReturnSelf(); - $this->_resourceRatingMock->expects($this->once()) + $this->resourceRatingMock->expects($this->once()) ->method('deleteAggregatedRatingsByProductId') ->willReturnSelf(); @@ -109,10 +109,10 @@ public function testCleanupProductReviewsWithoutProduct() $observerMock->expects($this->once()) ->method('getEvent') ->willReturn($eventMock); - $this->_resourceReviewMock->expects($this->never()) + $this->resourceReviewMock->expects($this->never()) ->method('deleteReviewsByProductId') ->willReturnSelf(); - $this->_resourceRatingMock->expects($this->never()) + $this->resourceRatingMock->expects($this->never()) ->method('deleteAggregatedRatingsByProductId') ->willReturnSelf(); From 704329e1e963fa6e880c9fd20e89e5888e876681 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Sun, 19 Aug 2018 10:33:38 +0300 Subject: [PATCH 1087/1171] Integration test for reviews delete observer --- ...essProductAfterDeleteEventObserverTest.php | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Review/Observer/ProcessProductAfterDeleteEventObserverTest.php diff --git a/dev/tests/integration/testsuite/Magento/Review/Observer/ProcessProductAfterDeleteEventObserverTest.php b/dev/tests/integration/testsuite/Magento/Review/Observer/ProcessProductAfterDeleteEventObserverTest.php new file mode 100644 index 0000000000000..eb50200898d4f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Review/Observer/ProcessProductAfterDeleteEventObserverTest.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Review\Controller; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Review\Model\ResourceModel\Review\Collection as ReviewCollection; +use Magento\Review\Model\ResourceModel\Review\CollectionFactory as ReviewCollectionFactory; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test checks that product review is removed when the corresponding product is removed + */ +class ProcessProductAfterDeleteEventObserverTest extends AbstractController +{ + /** + * @magentoDataFixture Magento/Review/_files/customer_review.php + */ + public function testReviewIsRemovedWhenProductDeleted() + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $objectManager->get(ProductRepositoryInterface::class); + $product = $productRepository->get('simple'); + + /** @var ReviewCollection $reviewsCollection */ + $reviewsCollection = $objectManager->get(ReviewCollectionFactory::class)->create(); + $reviewsCollection->addEntityFilter('product', $product->getId()); + + self::assertEquals(1, $reviewsCollection->count()); + + /* Remove product and ensure that the product review is removed as well */ + $productRepository->delete($product); + $reviewsCollection->clear(); + + self::assertEquals(0, $reviewsCollection->count()); + } +} From 81edc360f082d662f3b298e28d85d79bdd9844b4 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Tue, 21 Aug 2018 11:31:27 +0300 Subject: [PATCH 1088/1171] Translated menu titles --- app/code/Magento/AdminNotification/etc/adminhtml/menu.xml | 2 +- app/code/Magento/Braintree/etc/acl.xml | 2 +- app/code/Magento/Braintree/etc/adminhtml/menu.xml | 1 + app/code/Magento/Braintree/i18n/en_US.csv | 3 ++- app/code/Magento/Marketplace/etc/adminhtml/menu.xml | 3 +-- app/code/Magento/TaxImportExport/etc/acl.xml | 2 +- app/code/Magento/TaxImportExport/i18n/en_US.csv | 1 + app/code/Magento/UrlRewrite/etc/adminhtml/menu.xml | 4 +--- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/AdminNotification/etc/adminhtml/menu.xml b/app/code/Magento/AdminNotification/etc/adminhtml/menu.xml index fbed5c0960b73..04d700b9f90ce 100644 --- a/app/code/Magento/AdminNotification/etc/adminhtml/menu.xml +++ b/app/code/Magento/AdminNotification/etc/adminhtml/menu.xml @@ -7,6 +7,6 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd"> <menu> - <add id="Magento_AdminNotification::system_adminnotification" title="Notifications" translate="title" module="Magento_AdminNotification" sortOrder="10" parent="Magento_Backend::system_other_settings" action="adminhtml/notification" resource="Magento_AdminNotification::adminnotification"/> + <add id="Magento_AdminNotification::system_adminnotification" title="Notifications" translate="title" module="Magento_AdminNotification" sortOrder="10" parent="Magento_Backend::system_other_settings" action="adminhtml/notification" resource="Magento_AdminNotification::adminnotification"/> </menu> </config> diff --git a/app/code/Magento/Braintree/etc/acl.xml b/app/code/Magento/Braintree/etc/acl.xml index f50a55911d687..066ccc818d4e6 100644 --- a/app/code/Magento/Braintree/etc/acl.xml +++ b/app/code/Magento/Braintree/etc/acl.xml @@ -11,7 +11,7 @@ <resource id="Magento_Backend::admin"> <resource id="Magento_Reports::report"> <resource id="Magento_Reports::salesroot"> - <resource id="Magento_Braintree::settlement_report" title="Braintree Settlement" sortOrder="80" /> + <resource id="Magento_Braintree::settlement_report" title="Braintree Settlement" translate="title" sortOrder="80" /> </resource> </resource> <resource id="Magento_Sales::sales"> diff --git a/app/code/Magento/Braintree/etc/adminhtml/menu.xml b/app/code/Magento/Braintree/etc/adminhtml/menu.xml index 590d5b3dce008..ce4dd4844f3bc 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/menu.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/menu.xml @@ -10,6 +10,7 @@ <add id="Magento_Braintree::settlement_report" title="Braintree Settlement" + translate="title" module="Magento_Braintree" sortOrder="80" parent="Magento_Reports::report_salesroot" diff --git a/app/code/Magento/Braintree/i18n/en_US.csv b/app/code/Magento/Braintree/i18n/en_US.csv index 194ad14d49751..6bf677151ed0d 100644 --- a/app/code/Magento/Braintree/i18n/en_US.csv +++ b/app/code/Magento/Braintree/i18n/en_US.csv @@ -190,4 +190,5 @@ Currency,Currency "Partial settlements are not supported by this processor.","Partial settlements are not supported by this processor." "Transaction can not be voided if status of a PayPal partial settlement child transaction is settlement_pending.","Transaction can not be voided if status of a PayPal partial settlement child transaction is settlement_pending." "Too many concurrent attempts to refund this transaction. Try again later.","Too many concurrent attempts to refund this transaction. Try again later." -"Too many concurrent attempts to void this transaction. Try again later.","Too many concurrent attempts to void this transaction. Try again later." \ No newline at end of file +"Too many concurrent attempts to void this transaction. Try again later.","Too many concurrent attempts to void this transaction. Try again later." +"Braintree Settlement","Braintree Settlement" diff --git a/app/code/Magento/Marketplace/etc/adminhtml/menu.xml b/app/code/Magento/Marketplace/etc/adminhtml/menu.xml index 084ca708d5825..ae9e629f95364 100644 --- a/app/code/Magento/Marketplace/etc/adminhtml/menu.xml +++ b/app/code/Magento/Marketplace/etc/adminhtml/menu.xml @@ -7,7 +7,6 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd"> <menu> - <add id="Magento_Marketplace::partners" title="Find Partners & Extensions" module="Magento_Marketplace" sortOrder="80" - action="marketplace/index" resource="Magento_Marketplace::partners"/> + <add id="Magento_Marketplace::partners" title="Find Partners & Extensions" translate="title" module="Magento_Marketplace" sortOrder="80" action="marketplace/index" resource="Magento_Marketplace::partners"/> </menu> </config> diff --git a/app/code/Magento/TaxImportExport/etc/acl.xml b/app/code/Magento/TaxImportExport/etc/acl.xml index 35c811e8441eb..7a7ac16743225 100644 --- a/app/code/Magento/TaxImportExport/etc/acl.xml +++ b/app/code/Magento/TaxImportExport/etc/acl.xml @@ -11,7 +11,7 @@ <resource id="Magento_Backend::admin"> <resource id="Magento_Backend::system"> <resource id="Magento_Backend::convert"> - <resource id="Magento_TaxImportExport::import_export" title="Import/Export Tax Rates" sortOrder="30" /> + <resource id="Magento_TaxImportExport::import_export" title="Import/Export Tax Rates" translate="title" sortOrder="30" /> </resource> </resource> </resource> diff --git a/app/code/Magento/TaxImportExport/i18n/en_US.csv b/app/code/Magento/TaxImportExport/i18n/en_US.csv index 40db8846b54cb..95f94dcfd3b2c 100644 --- a/app/code/Magento/TaxImportExport/i18n/en_US.csv +++ b/app/code/Magento/TaxImportExport/i18n/en_US.csv @@ -17,3 +17,4 @@ Rate,Rate "Export Tax Rates","Export Tax Rates" CSV,CSV "Excel XML","Excel XML" +"Import/Export Tax Rates","Import/Export Tax Rates" diff --git a/app/code/Magento/UrlRewrite/etc/adminhtml/menu.xml b/app/code/Magento/UrlRewrite/etc/adminhtml/menu.xml index 615e17dbb7af8..66ae94dac1af1 100644 --- a/app/code/Magento/UrlRewrite/etc/adminhtml/menu.xml +++ b/app/code/Magento/UrlRewrite/etc/adminhtml/menu.xml @@ -7,8 +7,6 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd"> <menu> - <add id="Magento_UrlRewrite::urlrewrite" title="URL Rewrites" translate="title" module="Magento_UrlRewrite" - sortOrder="20" parent="Magento_Backend::marketing_seo" - action="adminhtml/url_rewrite/index" resource="Magento_UrlRewrite::urlrewrite"/> + <add id="Magento_UrlRewrite::urlrewrite" title="URL Rewrites" translate="title" module="Magento_UrlRewrite" sortOrder="20" parent="Magento_Backend::marketing_seo" action="adminhtml/url_rewrite/index" resource="Magento_UrlRewrite::urlrewrite"/> </menu> </config> From d7ab99d8ba8914ffc9e47e396cda238c825712ce Mon Sep 17 00:00:00 2001 From: Ivan Gerchak <ivang@ven.com> Date: Tue, 14 Aug 2018 14:01:18 +0300 Subject: [PATCH 1089/1171] Fix sending duplicate emails --- .../Magento/Sales/Model/EmailSenderHandler.php | 3 +++ app/code/Magento/Sales/etc/adminhtml/system.xml | 8 ++++++++ app/code/Magento/Sales/etc/config.xml | 1 + app/code/Magento/Sales/etc/di.xml | 16 ++++++++-------- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Sales/Model/EmailSenderHandler.php b/app/code/Magento/Sales/Model/EmailSenderHandler.php index fe8f1685fe525..7c7005bf0da75 100644 --- a/app/code/Magento/Sales/Model/EmailSenderHandler.php +++ b/app/code/Magento/Sales/Model/EmailSenderHandler.php @@ -90,6 +90,9 @@ public function sendEmails() if ($this->globalConfig->getValue('sales_email/general/async_sending')) { $this->entityCollection->addFieldToFilter('send_email', ['eq' => 1]); $this->entityCollection->addFieldToFilter('email_sent', ['null' => true]); + $this->entityCollection->setPageSize( + $this->globalConfig->getValue('sales_email/general/sending_limit') + ); /** @var \Magento\Store\Api\Data\StoreInterface[] $stores */ $stores = $this->getStores(clone $this->entityCollection); diff --git a/app/code/Magento/Sales/etc/adminhtml/system.xml b/app/code/Magento/Sales/etc/adminhtml/system.xml index 9d6d11d56c81f..7d06e0f7b74c4 100644 --- a/app/code/Magento/Sales/etc/adminhtml/system.xml +++ b/app/code/Magento/Sales/etc/adminhtml/system.xml @@ -132,6 +132,14 @@ <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model> <backend_model>Magento\Sales\Model\Config\Backend\Email\AsyncSending</backend_model> </field> + <field id="sending_limit" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <label>Limit per cron run</label> + <comment>Limit how many entities (orders/shipments/etc) will be processed during one cron run.</comment> + <validate>required-number validate-number validate-greater-than-zero</validate> + <depends> + <field id="async_sending">1</field> + </depends> + </field> </group> <group id="order" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Order</label> diff --git a/app/code/Magento/Sales/etc/config.xml b/app/code/Magento/Sales/etc/config.xml index da2408416133d..d4d10bfa6dcce 100644 --- a/app/code/Magento/Sales/etc/config.xml +++ b/app/code/Magento/Sales/etc/config.xml @@ -29,6 +29,7 @@ <sales_email> <general> <async_sending>0</async_sending> + <sending_limit>50</sending_limit> </general> <order> <enabled>1</enabled> diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index 0f69389c2969e..3c246b02393fe 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -354,43 +354,43 @@ <virtualType name="SalesOrderSendEmailsObserver" type="Magento\Sales\Observer\Virtual\SendEmails"> <arguments> - <argument name="emailSenderHandler" xsi:type="object">SalesOrderSendEmails</argument> + <argument name="emailSenderHandler" xsi:type="object" shared="false">SalesOrderSendEmails</argument> </arguments> </virtualType> <virtualType name="SalesOrderInvoiceSendEmailsObserver" type="Magento\Sales\Observer\Virtual\SendEmails"> <arguments> - <argument name="emailSenderHandler" xsi:type="object">SalesOrderInvoiceSendEmails</argument> + <argument name="emailSenderHandler" xsi:type="object" shared="false">SalesOrderInvoiceSendEmails</argument> </arguments> </virtualType> <virtualType name="SalesOrderShipmentSendEmailsObserver" type="Magento\Sales\Observer\Virtual\SendEmails"> <arguments> - <argument name="emailSenderHandler" xsi:type="object">SalesOrderShipmentSendEmails</argument> + <argument name="emailSenderHandler" xsi:type="object" shared="false">SalesOrderShipmentSendEmails</argument> </arguments> </virtualType> <virtualType name="SalesOrderCreditmemoSendEmailsObserver" type="Magento\Sales\Observer\Virtual\SendEmails"> <arguments> - <argument name="emailSenderHandler" xsi:type="object">SalesOrderCreditmemoSendEmails</argument> + <argument name="emailSenderHandler" xsi:type="object" shared="false">SalesOrderCreditmemoSendEmails</argument> </arguments> </virtualType> <virtualType name="SalesOrderSendEmailsCron" type="Magento\Sales\Cron\SendEmails"> <arguments> - <argument name="emailSenderHandler" xsi:type="object">SalesOrderSendEmails</argument> + <argument name="emailSenderHandler" xsi:type="object" shared="false">SalesOrderSendEmails</argument> </arguments> </virtualType> <virtualType name="SalesInvoiceSendEmailsCron" type="Magento\Sales\Cron\SendEmails"> <arguments> - <argument name="emailSenderHandler" xsi:type="object">SalesOrderInvoiceSendEmails</argument> + <argument name="emailSenderHandler" xsi:type="object" shared="false">SalesOrderInvoiceSendEmails</argument> </arguments> </virtualType> <virtualType name="SalesShipmentSendEmailsCron" type="Magento\Sales\Cron\SendEmails"> <arguments> - <argument name="emailSenderHandler" xsi:type="object">SalesOrderShipmentSendEmails</argument> + <argument name="emailSenderHandler" xsi:type="object" shared="false">SalesOrderShipmentSendEmails</argument> </arguments> </virtualType> <virtualType name="SalesCreditmemoSendEmailsCron" type="Magento\Sales\Cron\SendEmails"> <arguments> - <argument name="emailSenderHandler" xsi:type="object">SalesOrderCreditmemoSendEmails</argument> + <argument name="emailSenderHandler" xsi:type="object" shared="false">SalesOrderCreditmemoSendEmails</argument> </arguments> </virtualType> <type name="Magento\SalesSequence\Model\EntityPool"> From c43443b0e5e6493c8b59ee857c3c1c56ce993684 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Mon, 20 Aug 2018 21:34:55 +0300 Subject: [PATCH 1090/1171] Add unit test for Sales Rule model class --- .../Test/Unit/Model/CouponGeneratorTest.php | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 app/code/Magento/SalesRule/Test/Unit/Model/CouponGeneratorTest.php diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/CouponGeneratorTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/CouponGeneratorTest.php new file mode 100644 index 0000000000000..24ea8f2ab5efb --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Unit/Model/CouponGeneratorTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesRule\Test\Unit\Model; + +use Magento\SalesRule\Api\Data\CouponGenerationSpecInterface; +use Magento\SalesRule\Api\Data\CouponGenerationSpecInterfaceFactory; +use Magento\SalesRule\Model\CouponGenerator; +use Magento\SalesRule\Model\Service\CouponManagementService; + +/** + * @covers \Magento\SalesRule\Model\CouponGenerator + */ +class CouponGeneratorTest extends \PHPUnit\Framework\TestCase +{ + /** + * Testable Object + * + * @var CouponGenerator + */ + private $couponGenerator; + + /** + * @var CouponManagementService|\PHPUnit_Framework_MockObject_MockObject + */ + private $couponManagementServiceMock; + + /** + * @var CouponGenerationSpecInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $generationSpecFactoryMock; + + /** + * @var CouponGenerationSpecInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $generationSpecMock; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->generationSpecFactoryMock = $this->getMockBuilder(CouponGenerationSpecInterfaceFactory::class) + ->disableOriginalConstructor()->setMethods(['create'])->getMock(); + $this->couponManagementServiceMock = $this->createMock(CouponManagementService::class); + $this->generationSpecMock = $this->createMock(CouponGenerationSpecInterface::class); + $this->couponGenerator = new CouponGenerator( + $this->couponManagementServiceMock, + $this->generationSpecFactoryMock + ); + } + + /** + * Test beforeSave method + * + * @return void + */ + public function testBeforeSave() + { + $expected = ['test']; + $this->generationSpecFactoryMock->expects($this->once())->method('create') + ->willReturn($this->generationSpecMock); + $this->couponManagementServiceMock->expects($this->once())->method('generate') + ->with($this->generationSpecMock)->willReturn($expected); + $actual = $this->couponGenerator->generateCodes([]); + self::assertEquals($expected, $actual); + } +} From 05f8e78913cd9922708a35b830f10ad0f2cd713c Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 21 Aug 2018 12:54:31 +0300 Subject: [PATCH 1091/1171] [Forwardport] ISSUE-17715: Duplicate event in Delete operation transaction "entity_manager_delete_before". --- .../Magento/Framework/EntityManager/Operation/Delete.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/EntityManager/Operation/Delete.php b/lib/internal/Magento/Framework/EntityManager/Operation/Delete.php index aadc968370bfd..b9c0de5f4c52a 100644 --- a/lib/internal/Magento/Framework/EntityManager/Operation/Delete.php +++ b/lib/internal/Magento/Framework/EntityManager/Operation/Delete.php @@ -118,7 +118,7 @@ public function execute($entity, $arguments = []) $entity = $this->deleteMain->execute($entity, $arguments); $this->eventManager->dispatchEntityEvent($entityType, 'delete_after', ['entity' => $entity]); $this->eventManager->dispatch( - 'entity_manager_delete_before', + 'entity_manager_delete_after', [ 'entity_type' => $entityType, 'entity' => $entity From 3f0168cbf6589e850cbc4ca4b6ca3796a04330ab Mon Sep 17 00:00:00 2001 From: Ananth <ananth.iyer@aureatelabs.com> Date: Tue, 21 Aug 2018 13:13:47 +0300 Subject: [PATCH 1092/1171] Fixed review list ajax if product not exist redirect to 404 page --- .../Review/Controller/Product/ListAjax.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Review/Controller/Product/ListAjax.php b/app/code/Magento/Review/Controller/Product/ListAjax.php index 0180e5a7ee035..a309b5f0626a4 100644 --- a/app/code/Magento/Review/Controller/Product/ListAjax.php +++ b/app/code/Magento/Review/Controller/Product/ListAjax.php @@ -3,9 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Review\Controller\Product; -use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\View\Result\Layout; use Magento\Review\Controller\Product as ProductController; use Magento\Framework\Controller\ResultFactory; @@ -14,17 +17,16 @@ class ListAjax extends ProductController /** * Show list of product's reviews * - * @return \Magento\Framework\View\Result\Layout + * @return ResponseInterface|ResultInterface|Layout */ public function execute() { if (!$this->initProduct()) { - throw new LocalizedException(__("The product can't be initialized.")); - } else { - /** @var \Magento\Framework\View\Result\Layout $resultLayout */ - $resultLayout = $this->resultFactory->create(ResultFactory::TYPE_LAYOUT); + /** @var \Magento\Framework\Controller\Result\Forward $resultForward */ + $resultForward = $this->resultFactory->create(ResultFactory::TYPE_FORWARD); + return $resultForward->forward('noroute'); } - return $resultLayout; + return $this->resultFactory->create(ResultFactory::TYPE_LAYOUT); } } From 57f9fef74874097b2a7175421a8456dd618396f1 Mon Sep 17 00:00:00 2001 From: Jason Woods <devel@jasonwoods.me.uk> Date: Thu, 11 Jan 2018 14:48:19 +0200 Subject: [PATCH 1093/1171] Magento PayPal checkout fails if email sending fails / other payment method does not --- app/code/Magento/Paypal/Model/Express/Checkout.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Paypal/Model/Express/Checkout.php b/app/code/Magento/Paypal/Model/Express/Checkout.php index 9c9b4dc3e87a7..92fd72cf32ecd 100644 --- a/app/code/Magento/Paypal/Model/Express/Checkout.php +++ b/app/code/Magento/Paypal/Model/Express/Checkout.php @@ -809,8 +809,12 @@ public function place($token, $shippingMethodCode = null) case \Magento\Sales\Model\Order::STATE_PROCESSING: case \Magento\Sales\Model\Order::STATE_COMPLETE: case \Magento\Sales\Model\Order::STATE_PAYMENT_REVIEW: - if (!$order->getEmailSent()) { - $this->orderSender->send($order); + try { + if (!$order->getEmailSent()) { + $this->orderSender->send($order); + } + } catch (\Exception $e) { + $this->_logger->critical($e); } $this->_checkoutSession->start(); break; From d5c979c7f2a497ef2ce16c416ebb262a66048b8b Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Tue, 21 Aug 2018 13:45:42 +0300 Subject: [PATCH 1094/1171] MAGETWO-94032: Wrong totals shown in exported Coupon Report --- .../Block/Adminhtml/Grid/AbstractGrid.php | 2 + .../Adminhtml/Sales/Coupons/GridTest.php | 173 ++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Sales/Coupons/GridTest.php diff --git a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php index 158455db26455..be7dfa70efbb4 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php @@ -302,6 +302,7 @@ public function getCountTotals() ); $this->_addOrderStatusFilter($totalsCollection, $filterData); + $this->_addCustomFilter($totalsCollection, $filterData); if ($totalsCollection->load()->getSize() < 1 || !$filterData->getData('from')) { $this->setTotals(new \Magento\Framework\DataObject()); @@ -313,6 +314,7 @@ public function getCountTotals() } } } + return parent::getCountTotals(); } diff --git a/app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Sales/Coupons/GridTest.php b/app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Sales/Coupons/GridTest.php new file mode 100644 index 0000000000000..3c7ad9ee9e686 --- /dev/null +++ b/app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Sales/Coupons/GridTest.php @@ -0,0 +1,173 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Reports\Test\Unit\Block\Adminhtml\Sales\Coupons; + +/** + * Test for class \Magento\Reports\Block\Adminhtml\Sales\Coupons\Grid + */ +class GridTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Reports\Block\Adminhtml\Sales\Coupons\Grid + */ + private $model; + + /** + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeManagerMock; + + /** + * @var \Magento\Reports\Model\ResourceModel\Report\Collection\Factory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resourceFactoryMock; + + /** + * Set up mock objects for tested class + * + * @return void + */ + protected function setUp(): void + { + $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + ->getMock(); + $this->resourceFactoryMock = $this + ->getMockBuilder(\Magento\Reports\Model\ResourceModel\Report\Collection\Factory::class) + ->disableOriginalConstructor() + ->getMock(); + $aggregatedColumns = [1 => 'SUM(value)']; + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Reports\Block\Adminhtml\Sales\Coupons\Grid::class, + [ + '_storeManager' => $this->storeManagerMock, + '_aggregatedColumns' => $aggregatedColumns, + 'resourceFactory' => $this->resourceFactoryMock, + ] + ); + } + + /** + * @dataProvider getCountTotalsDataProvider + * + * @param string $reportType + * @param int $priceRuleType + * @param int $collectionSize + * @param bool $expectedCountTotals + * @return void + */ + public function testGetCountTotals( + string $reportType, + int $priceRuleType, + int $collectionSize, + bool $expectedCountTotals + ): void { + $filterData = new \Magento\Framework\DataObject(); + $filterData->setData('report_type', $reportType); + $filterData->setData('period_type', 'day'); + $filterData->setData('from', '2000-01-01'); + $filterData->setData('to', '2000-01-30'); + $filterData->setData('store_ids', '1'); + $filterData->setData('price_rule_type', $priceRuleType); + if ($priceRuleType) { + $filterData->setData('rules_list', ['0,1']); + } + $filterData->setData('order_statuses', 'statuses'); + $this->model->setFilterData($filterData); + + $resourceCollectionName = $this->model->getResourceCollectionName(); + $collectionMock = $this->buildBaseCollectionMock($filterData, $resourceCollectionName, $collectionSize); + + $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) + ->getMock(); + $this->storeManagerMock->method('getStores') + ->willReturn([1 => $store]); + $this->resourceFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($collectionMock); + + $this->assertEquals($expectedCountTotals, $this->model->getCountTotals()); + } + + /** + * @return array + */ + public function getCountTotalsDataProvider(): array + { + return [ + ['created_at_shipment', 0, 0, false], + ['created_at_shipment', 0, 1, true], + ['updated_at_order', 0, 1, true], + ['updated_at_order', 1, 1, true], + ]; + } + + /** + * @param \Magento\Framework\DataObject $filterData + * @param string $resourceCollectionName + * @param int $collectionSize + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function buildBaseCollectionMock( + \Magento\Framework\DataObject $filterData, + string $resourceCollectionName, + int $collectionSize + ): \PHPUnit_Framework_MockObject_MockObject { + $collectionMock = $this->getMockBuilder($resourceCollectionName) + ->disableOriginalConstructor() + ->getMock(); + $collectionMock->expects($this->once()) + ->method('setPeriod') + ->with($filterData->getData('period_type')) + ->willReturnSelf(); + $collectionMock->expects($this->once()) + ->method('setDateRange') + ->with($filterData->getData('from'), $filterData->getData('to')) + ->willReturnSelf(); + $collectionMock->expects($this->once()) + ->method('addStoreFilter') + ->with(\explode(',', $filterData->getData('store_ids'))) + ->willReturnSelf(); + $collectionMock->expects($this->once()) + ->method('setAggregatedColumns') + ->willReturnSelf(); + $collectionMock->expects($this->once()) + ->method('isTotals') + ->with(true) + ->willReturnSelf(); + $collectionMock->expects($this->once()) + ->method('addOrderStatusFilter') + ->with($filterData->getData('order_statuses')) + ->willReturnSelf(); + + if ($filterData->getData('price_rule_type')) { + $collectionMock->expects($this->once()) + ->method('addRuleFilter') + ->with(\explode(',', $filterData->getData('rules_list')[0])) + ->willReturnSelf(); + } + + $collectionMock->expects($this->once()) + ->method('load') + ->willReturnSelf(); + $collectionMock->expects($this->once()) + ->method('getSize') + ->willReturn($collectionSize); + if ($collectionSize) { + $itemMock = $this->getMockBuilder(\Magento\Reports\Model\Item::class) + ->disableOriginalConstructor() + ->getMock(); + $collectionMock->expects($this->once()) + ->method('getItems') + ->willReturn([$itemMock]); + } + + return $collectionMock; + } +} From 5a03f97fa113e20901a2d4d8d104cc83a3c2ab87 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Mon, 20 Aug 2018 13:50:57 +0300 Subject: [PATCH 1095/1171] Fixed incorrect namespace --- .../Observer/ProcessProductAfterDeleteEventObserverTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Review/Observer/ProcessProductAfterDeleteEventObserverTest.php b/dev/tests/integration/testsuite/Magento/Review/Observer/ProcessProductAfterDeleteEventObserverTest.php index eb50200898d4f..8c1fd8eca70f9 100644 --- a/dev/tests/integration/testsuite/Magento/Review/Observer/ProcessProductAfterDeleteEventObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/Review/Observer/ProcessProductAfterDeleteEventObserverTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\Review\Controller; +namespace Magento\Review\Observer; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Review\Model\ResourceModel\Review\Collection as ReviewCollection; From d6c7459b26ce2e4e24ef2bbf8547e2790c01324c Mon Sep 17 00:00:00 2001 From: Tommy Quissens <tommy.quissens@storefront.be> Date: Thu, 5 Apr 2018 12:31:02 +0300 Subject: [PATCH 1096/1171] made attributes nillable so we can reset parent settings --- lib/internal/Magento/Framework/Config/etc/view.xsd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/Config/etc/view.xsd b/lib/internal/Magento/Framework/Config/etc/view.xsd index 20b6a7d4fbfd2..eb0583a58bf29 100644 --- a/lib/internal/Magento/Framework/Config/etc/view.xsd +++ b/lib/internal/Magento/Framework/Config/etc/view.xsd @@ -49,12 +49,12 @@ <xs:element name="image" minOccurs="1" maxOccurs="unbounded"> <xs:complexType> <xs:sequence> - <xs:element name="width" type="xs:positiveInteger" minOccurs="0"/> - <xs:element name="height" type="xs:positiveInteger" minOccurs="0"/> - <xs:element name="constrain" type="xs:boolean" minOccurs="0"/> - <xs:element name="aspect_ratio" type="xs:boolean" minOccurs="0"/> - <xs:element name="frame" type="xs:boolean" minOccurs="0"/> - <xs:element name="transparency" type="xs:boolean" minOccurs="0"/> + <xs:element name="width" type="xs:positiveInteger" minOccurs="0" nillable="true"/> + <xs:element name="height" type="xs:positiveInteger" minOccurs="0" nillable="true"/> + <xs:element name="constrain" type="xs:boolean" minOccurs="0" nillable="true"/> + <xs:element name="aspect_ratio" type="xs:boolean" minOccurs="0" nillable="true"/> + <xs:element name="frame" type="xs:boolean" minOccurs="0" nillable="true"/> + <xs:element name="transparency" type="xs:boolean" minOccurs="0" nillable="true"/> <xs:element name="background" minOccurs="0"> <xs:simpleType> <xs:restriction base="xs:string"> From f2a1c08a401a338fa93f6f35aea34a9643ae811e Mon Sep 17 00:00:00 2001 From: Tommy Quissens <tommy.quissens@storefront.be> Date: Thu, 5 Apr 2018 13:45:28 +0300 Subject: [PATCH 1097/1171] parse nil values correctly --- app/code/Magento/Catalog/Model/ImageExtractor.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ImageExtractor.php b/app/code/Magento/Catalog/Model/ImageExtractor.php index d2c11a3762961..4d8c95f26a72e 100644 --- a/app/code/Magento/Catalog/Model/ImageExtractor.php +++ b/app/code/Magento/Catalog/Model/ImageExtractor.php @@ -35,7 +35,11 @@ public function process(\DOMElement $mediaNode, $mediaParentTag) if ($attributeTagName === 'background') { $nodeValue = $this->processImageBackground($attribute->nodeValue); } elseif ($attributeTagName === 'width' || $attributeTagName === 'height') { - $nodeValue = intval($attribute->nodeValue); + if ((bool)$attribute->getAttribute('xsi:nil') !== true) { + $nodeValue = intval($attribute->nodeValue); + } else { + $nodeValue = null; + } } else { $nodeValue = !in_array($attribute->nodeValue, ['false', '0']); } From 240e18df3e108c5bb244ce01945f8e9415675fb4 Mon Sep 17 00:00:00 2001 From: Tommy Quissens <tommy.quissens@storefront.be> Date: Tue, 19 Jun 2018 16:23:15 +0300 Subject: [PATCH 1098/1171] made more attributes nillable --- app/code/Magento/Catalog/Model/ImageExtractor.php | 14 +++++++------- lib/internal/Magento/Framework/Config/etc/view.xsd | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ImageExtractor.php b/app/code/Magento/Catalog/Model/ImageExtractor.php index 4d8c95f26a72e..1c20608670672 100644 --- a/app/code/Magento/Catalog/Model/ImageExtractor.php +++ b/app/code/Magento/Catalog/Model/ImageExtractor.php @@ -32,16 +32,16 @@ public function process(\DOMElement $mediaNode, $mediaParentTag) continue; } $attributeTagName = $attribute->tagName; - if ($attributeTagName === 'background') { - $nodeValue = $this->processImageBackground($attribute->nodeValue); - } elseif ($attributeTagName === 'width' || $attributeTagName === 'height') { - if ((bool)$attribute->getAttribute('xsi:nil') !== true) { - $nodeValue = intval($attribute->nodeValue); + if ((bool)$attribute->getAttribute('xsi:nil') !== true) { + if ($attributeTagName === 'background') { + $nodeValue = $this->processImageBackground($attribute->nodeValue); + } elseif ($attributeTagName === 'width' || $attributeTagName === 'height') { + $nodeValue = intval($attribute->nodeValue); } else { - $nodeValue = null; + $nodeValue = $attribute->nodeValue; } } else { - $nodeValue = !in_array($attribute->nodeValue, ['false', '0']); + $nodeValue = null; } $result[$mediaParentTag][$moduleNameImage][Image::MEDIA_TYPE_CONFIG_NODE][$imageId][$attribute->tagName] = $nodeValue; diff --git a/lib/internal/Magento/Framework/Config/etc/view.xsd b/lib/internal/Magento/Framework/Config/etc/view.xsd index eb0583a58bf29..b908862b02147 100644 --- a/lib/internal/Magento/Framework/Config/etc/view.xsd +++ b/lib/internal/Magento/Framework/Config/etc/view.xsd @@ -55,7 +55,7 @@ <xs:element name="aspect_ratio" type="xs:boolean" minOccurs="0" nillable="true"/> <xs:element name="frame" type="xs:boolean" minOccurs="0" nillable="true"/> <xs:element name="transparency" type="xs:boolean" minOccurs="0" nillable="true"/> - <xs:element name="background" minOccurs="0"> + <xs:element name="background" minOccurs="0" nillable="true"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:pattern value="\[(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\]"/> From 2e22671e97b06851b68dbf218834019c5b9fbf47 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Mon, 16 Jul 2018 09:37:54 +0300 Subject: [PATCH 1099/1171] [task] remove strval() --- .../Authorizenet/Model/Directpost/Request.php | 52 +++++++++---------- .../Backend/Currency/AbstractCurrency.php | 4 +- .../Paypal/Controller/Payflow/ReturnUrl.php | 2 +- app/code/Magento/Quote/Model/Quote.php | 2 +- .../Framework/DB/Select/OrderRenderer.php | 4 +- .../Data/Form/Element/Checkboxes.php | 12 ++--- .../Magento/Framework/Filter/Translit.php | 2 +- .../Framework/Phrase/Renderer/Placeholder.php | 2 +- 8 files changed, 40 insertions(+), 40 deletions(-) diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Request.php b/app/code/Magento/Authorizenet/Model/Directpost/Request.php index d9a403e5c991e..fc78d836b6080 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost/Request.php +++ b/app/code/Magento/Authorizenet/Model/Directpost/Request.php @@ -112,50 +112,50 @@ public function setDataFromOrder( sprintf('%.2F', $order->getBaseShippingAmount()) ); - //need to use strval() because NULL values IE6-8 decodes as "null" in JSON in JavaScript, + //need to use (string) because NULL values IE6-8 decodes as "null" in JSON in JavaScript, //but we need "" for null values. $billing = $order->getBillingAddress(); if (!empty($billing)) { - $this->setXFirstName(strval($billing->getFirstname())) - ->setXLastName(strval($billing->getLastname())) - ->setXCompany(strval($billing->getCompany())) - ->setXAddress(strval($billing->getStreetLine(1))) - ->setXCity(strval($billing->getCity())) - ->setXState(strval($billing->getRegion())) - ->setXZip(strval($billing->getPostcode())) - ->setXCountry(strval($billing->getCountryId())) - ->setXPhone(strval($billing->getTelephone())) - ->setXFax(strval($billing->getFax())) - ->setXCustId(strval($billing->getCustomerId())) - ->setXCustomerIp(strval($order->getRemoteIp())) - ->setXCustomerTaxId(strval($billing->getTaxId())) - ->setXEmail(strval($order->getCustomerEmail())) - ->setXEmailCustomer(strval($paymentMethod->getConfigData('email_customer'))) - ->setXMerchantEmail(strval($paymentMethod->getConfigData('merchant_email'))); + $this->setXFirstName((string)$billing->getFirstname()) + ->setXLastName((string)$billing->getLastname()) + ->setXCompany((string)$billing->getCompany()) + ->setXAddress((string)$billing->getStreetLine(1)) + ->setXCity((string)$billing->getCity()) + ->setXState((string)$billing->getRegion()) + ->setXZip((string)$billing->getPostcode()) + ->setXCountry((string)$billing->getCountryId()) + ->setXPhone((string)$billing->getTelephone()) + ->setXFax((string)$billing->getFax()) + ->setXCustId((string)$billing->getCustomerId()) + ->setXCustomerIp((string)$order->getRemoteIp()) + ->setXCustomerTaxId((string)$billing->getTaxId()) + ->setXEmail((string)$order->getCustomerEmail()) + ->setXEmailCustomer((string)$paymentMethod->getConfigData('email_customer')) + ->setXMerchantEmail((string)$paymentMethod->getConfigData('merchant_email')); } $shipping = $order->getShippingAddress(); if (!empty($shipping)) { $this->setXShipToFirstName( - strval($shipping->getFirstname()) + (string)$shipping->getFirstname() )->setXShipToLastName( - strval($shipping->getLastname()) + (string)$shipping->getLastname() )->setXShipToCompany( - strval($shipping->getCompany()) + (string)$shipping->getCompany() )->setXShipToAddress( - strval($shipping->getStreetLine(1)) + (string)$shipping->getStreetLine(1) )->setXShipToCity( - strval($shipping->getCity()) + (string)$shipping->getCity() )->setXShipToState( - strval($shipping->getRegion()) + (string)$shipping->getRegion() )->setXShipToZip( - strval($shipping->getPostcode()) + (string)$shipping->getPostcode() )->setXShipToCountry( - strval($shipping->getCountryId()) + (string)$shipping->getCountryId() ); } - $this->setXPoNum(strval($payment->getPoNumber())); + $this->setXPoNum((string)$payment->getPoNumber()); return $this; } diff --git a/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php b/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php index b86b86ad3bb8c..4ae66bfd9692b 100644 --- a/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php +++ b/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php @@ -71,7 +71,7 @@ protected function _getCurrencyBase() $this->getScopeId() ); } - return strval($value); + return (string)$value; } /** @@ -88,7 +88,7 @@ protected function _getCurrencyDefault() $this->getScopeId() ); } - return strval($value); + return (string)$value; } /** diff --git a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php index a370eeb40eafd..73b4c9f6ee6ea 100644 --- a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php +++ b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php @@ -50,7 +50,7 @@ public function execute() $redirectBlock->setData('goto_success_page', true); } else { if ($this->checkPaymentMethod($order)) { - $gotoSection = $this->_cancelPayment(strval($this->getRequest()->getParam('RESPMSG'))); + $gotoSection = $this->_cancelPayment((string)$this->getRequest()->getParam('RESPMSG')); $redirectBlock->setData('goto_section', $gotoSection); $redirectBlock->setData('error_msg', __('Your payment has been declined. Please try again.')); } else { diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index b7a1b7d563ef6..5beb4527cf2a5 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -1608,7 +1608,7 @@ public function addProduct( * Error message */ if (is_string($cartCandidates) || $cartCandidates instanceof \Magento\Framework\Phrase) { - return strval($cartCandidates); + return (string)$cartCandidates; } /** diff --git a/lib/internal/Magento/Framework/DB/Select/OrderRenderer.php b/lib/internal/Magento/Framework/DB/Select/OrderRenderer.php index 36a075b9af8c9..dfe9c8949c353 100644 --- a/lib/internal/Magento/Framework/DB/Select/OrderRenderer.php +++ b/lib/internal/Magento/Framework/DB/Select/OrderRenderer.php @@ -40,12 +40,12 @@ public function render(Select $select, $sql = '') $order = []; foreach ($select->getPart(Select::ORDER) as $term) { if (is_array($term)) { - if (is_numeric($term[0]) && strval(intval($term[0])) == $term[0]) { + if (is_numeric($term[0]) && (string)(int)$term[0] == $term[0]) { $order[] = (int)trim($term[0]) . ' ' . $term[1]; } else { $order[] = $this->quote->quoteIdentifier($term[0]) . ' ' . $term[1]; } - } elseif (is_numeric($term) && strval(intval($term)) == $term) { + } elseif (is_numeric($term) && (string)(int)$term == $term) { $order[] = (int)trim($term); } else { $order[] = $this->quote->quoteIdentifier($term); diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php b/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php index 3048be4d5dc1b..2a68432207b23 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php @@ -121,13 +121,13 @@ public function getChecked($value) return; } if (!is_array($checked)) { - $checked = [strval($checked)]; + $checked = [(string)$checked]; } else { foreach ($checked as $k => $v) { - $checked[$k] = strval($v); + $checked[$k] = (string)$v; } } - if (in_array(strval($value), $checked)) { + if (in_array((string)$value, $checked)) { return 'checked'; } return; @@ -141,13 +141,13 @@ public function getDisabled($value) { if ($disabled = $this->getData('disabled')) { if (!is_array($disabled)) { - $disabled = [strval($disabled)]; + $disabled = [(string)$disabled]; } else { foreach ($disabled as $k => $v) { - $disabled[$k] = strval($v); + $disabled[$k] = (string)$v; } } - if (in_array(strval($value), $disabled)) { + if (in_array((string)$value, $disabled)) { return 'disabled'; } } diff --git a/lib/internal/Magento/Framework/Filter/Translit.php b/lib/internal/Magento/Framework/Filter/Translit.php index 7a84a6e33af18..a6162aa7a7fff 100644 --- a/lib/internal/Magento/Framework/Filter/Translit.php +++ b/lib/internal/Magento/Framework/Filter/Translit.php @@ -409,7 +409,7 @@ public function __construct(\Magento\Framework\App\Config\ScopeConfigInterface $ $convertConfig = $config->getValue('url/convert', 'default'); if ($convertConfig) { foreach ($convertConfig as $configValue) { - $this->convertTable[strval($configValue['from'])] = strval($configValue['to']); + $this->convertTable[(string)$configValue['from']] = (string)$configValue['to']; } } } diff --git a/lib/internal/Magento/Framework/Phrase/Renderer/Placeholder.php b/lib/internal/Magento/Framework/Phrase/Renderer/Placeholder.php index 4ba8c747fa12c..fd1b0c18ead17 100644 --- a/lib/internal/Magento/Framework/Phrase/Renderer/Placeholder.php +++ b/lib/internal/Magento/Framework/Phrase/Renderer/Placeholder.php @@ -40,6 +40,6 @@ public function render(array $source, array $arguments) */ private function keyToPlaceholder($key) { - return '%' . (is_int($key) ? strval($key + 1) : $key); + return '%' . (is_int($key) ? (string)($key + 1) : $key); } } From b428aab33240e8b5e40b6d7a507f2df56bdc558e Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Mon, 16 Jul 2018 09:42:03 +0300 Subject: [PATCH 1100/1171] [task] add strval to obsolete methods --- .../testsuite/Magento/Test/Legacy/_files/obsolete_methods.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php index 8b811cc6b3fc0..ff8e7db0f4260 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php @@ -2528,6 +2528,7 @@ ['_isAttributeValueEmpty', 'Magento\Catalog\Model\ResourceModel\AbstractResource'], ['var_dump', ''], ['each', ''], + ['strval', ''], ['create_function', ''], ['configure', 'Magento\Framework\MessageQueue\BatchConsumer'], [ From ba86d5891cdfa3becc6e615724157608200a9c1b Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Tue, 21 Aug 2018 09:01:30 -0400 Subject: [PATCH 1101/1171] MQE-1174: Deliver weekly regression enablement tests - Skip MC-222 due to flakiness --- .../Mftf/Test/AdminBasicBundleProductAttributesTest.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml index eeb04aeadb555..56775617e793f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml @@ -6,8 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminBasicBundleProductAttributesTest"> <annotations> <features value="Bundle"/> @@ -16,6 +15,9 @@ <description value="Admin should be able to set/edit all the basic product attributes when creating/editing a bundle product"/> <severity value="CRITICAL"/> <testCaseId value="MC-222"/> + <skip> + <issueId value="MQE-1214"/> + </skip> <group value="Bundle"/> </annotations> <before> From 0a4aaaaae29794f019b0dbadf39dc8fd86e92c61 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Tue, 21 Aug 2018 12:02:16 -0500 Subject: [PATCH 1102/1171] MQE-1217: Deliver MFTF 2.3.5 - Version bump in composer.json/lock --- composer.json | 2 +- composer.lock | 157 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 118 insertions(+), 41 deletions(-) diff --git a/composer.json b/composer.json index b7435e98695c3..ce003f73621af 100644 --- a/composer.json +++ b/composer.json @@ -81,7 +81,7 @@ "zendframework/zend-view": "~2.10.0" }, "require-dev": { - "magento/magento2-functional-testing-framework": "2.3.4", + "magento/magento2-functional-testing-framework": "2.3.5", "friendsofphp/php-cs-fixer": "~2.12.0", "lusitanian/oauth": "~0.8.10", "pdepend/pdepend": "2.5.2", diff --git a/composer.lock b/composer.lock index d90750e2547ef..f20aa46b6d796 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "74013a4027763e05b29b127b4c03c752", + "content-hash": "ef3c5510832524507bfdfbb6ddd49f07", "packages": [ { "name": "braintree/braintree_php", @@ -257,16 +257,16 @@ }, { "name": "composer/composer", - "version": "1.7.1", + "version": "1.7.2", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "5d9311d4555787c8a57fea15f82471499aedf712" + "reference": "576aab9b5abb2ed11a1c52353a759363216a4ad2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/5d9311d4555787c8a57fea15f82471499aedf712", - "reference": "5d9311d4555787c8a57fea15f82471499aedf712", + "url": "https://api.github.com/repos/composer/composer/zipball/576aab9b5abb2ed11a1c52353a759363216a4ad2", + "reference": "576aab9b5abb2ed11a1c52353a759363216a4ad2", "shasum": "" }, "require": { @@ -333,7 +333,7 @@ "dependency", "package" ], - "time": "2018-08-07T07:39:23+00:00" + "time": "2018-08-16T14:57:12+00:00" }, { "name": "composer/semver", @@ -460,16 +460,16 @@ }, { "name": "composer/xdebug-handler", - "version": "1.1.0", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "c919dc6c62e221fc6406f861ea13433c0aa24f08" + "reference": "e1809da56ce1bd1b547a752936817341ac244d8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/c919dc6c62e221fc6406f861ea13433c0aa24f08", - "reference": "c919dc6c62e221fc6406f861ea13433c0aa24f08", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/e1809da56ce1bd1b547a752936817341ac244d8e", + "reference": "e1809da56ce1bd1b547a752936817341ac244d8e", "shasum": "" }, "require": { @@ -500,7 +500,7 @@ "Xdebug", "performance" ], - "time": "2018-04-11T15:42:36+00:00" + "time": "2018-08-16T10:54:23+00:00" }, { "name": "container-interop/container-interop", @@ -2400,16 +2400,16 @@ }, { "name": "zendframework/zend-code", - "version": "3.3.0", + "version": "3.3.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-code.git", - "reference": "6b1059db5b368db769e4392c6cb6cc139e56640d" + "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/6b1059db5b368db769e4392c6cb6cc139e56640d", - "reference": "6b1059db5b368db769e4392c6cb6cc139e56640d", + "url": "https://api.github.com/repos/zendframework/zend-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", + "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb", "shasum": "" }, "require": { @@ -2430,8 +2430,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev", - "dev-develop": "3.3-dev" + "dev-master": "3.3.x-dev", + "dev-develop": "3.4.x-dev" } }, "autoload": { @@ -2449,7 +2449,7 @@ "code", "zf2" ], - "time": "2017-10-20T15:21:32+00:00" + "time": "2018-08-13T20:36:59+00:00" }, { "name": "zendframework/zend-config", @@ -4725,16 +4725,16 @@ }, { "name": "consolidation/annotated-command", - "version": "2.8.4", + "version": "2.8.5", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "651541a0b68318a2a202bda558a676e5ad92223c" + "reference": "1e8ff512072422b850b44aa721b5b303e4a5ebb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/651541a0b68318a2a202bda558a676e5ad92223c", - "reference": "651541a0b68318a2a202bda558a676e5ad92223c", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/1e8ff512072422b850b44aa721b5b303e4a5ebb3", + "reference": "1e8ff512072422b850b44aa721b5b303e4a5ebb3", "shasum": "" }, "require": { @@ -4773,7 +4773,7 @@ } ], "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2018-05-25T18:04:25+00:00" + "time": "2018-08-18T23:51:49+00:00" }, { "name": "consolidation/config", @@ -4935,16 +4935,16 @@ }, { "name": "consolidation/robo", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f" + "reference": "31f2d2562c4e1dcde70f2659eefd59aa9c7f5b2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/ac563abfadf7cb7314b4e152f2b5033a6c255f6f", - "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/31f2d2562c4e1dcde70f2659eefd59aa9c7f5b2d", + "reference": "31f2d2562c4e1dcde70f2659eefd59aa9c7f5b2d", "shasum": "" }, "require": { @@ -4952,6 +4952,8 @@ "consolidation/config": "^1.0.10", "consolidation/log": "~1", "consolidation/output-formatters": "^3.1.13", + "consolidation/self-update": "^1", + "g1a/composer-test-scenarios": "^2", "grasmash/yaml-expander": "^1.3", "league/container": "^2.2", "php": ">=5.5.0", @@ -4968,7 +4970,6 @@ "codeception/aspect-mock": "^1|^2.1.1", "codeception/base": "^2.3.7", "codeception/verify": "^0.3.2", - "g1a/composer-test-scenarios": "^2", "goaop/framework": "~2.1.2", "goaop/parser-reflection": "^1.1.0", "natxet/cssmin": "3.0.4", @@ -5011,7 +5012,50 @@ } ], "description": "Modern task runner", - "time": "2018-05-27T01:42:53+00:00" + "time": "2018-08-17T18:44:18+00:00" + }, + { + "name": "consolidation/self-update", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/consolidation/self-update.git", + "reference": "adbb784e58cc0836d8522851f7e38ee7ade0d553" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/self-update/zipball/adbb784e58cc0836d8522851f7e38ee7ade0d553", + "reference": "adbb784e58cc0836d8522851f7e38ee7ade0d553", + "shasum": "" + }, + "require": { + "php": ">=5.5.0", + "symfony/console": "^2.8|^3|^4", + "symfony/filesystem": "^2.5|^3|^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "SelfUpdate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alexander Menk", + "email": "menk@mestrona.net" + } + ], + "description": "Provides a self:update command for Symfony Console applications.", + "time": "2018-08-17T04:50:59+00:00" }, { "name": "dflydev/dot-access-data", @@ -5464,21 +5508,21 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.12.2", + "version": "v2.12.3", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "dcc87d5414e9d0bd316fce81a5bedb9ce720b183" + "reference": "b23d49981cfc95497d03081aeb6df6575196a0d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/dcc87d5414e9d0bd316fce81a5bedb9ce720b183", - "reference": "dcc87d5414e9d0bd316fce81a5bedb9ce720b183", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/b23d49981cfc95497d03081aeb6df6575196a0d3", + "reference": "b23d49981cfc95497d03081aeb6df6575196a0d3", "shasum": "" }, "require": { "composer/semver": "^1.4", - "composer/xdebug-handler": "^1.0", + "composer/xdebug-handler": "^1.2", "doctrine/annotations": "^1.2", "ext-json": "*", "ext-tokenizer": "*", @@ -5551,7 +5595,7 @@ } ], "description": "A tool to automatically fix PHP code style", - "time": "2018-07-06T10:37:40+00:00" + "time": "2018-08-19T22:33:38+00:00" }, { "name": "fzaninotto/faker", @@ -5603,6 +5647,39 @@ ], "time": "2018-07-12T10:23:15+00:00" }, + { + "name": "g1a/composer-test-scenarios", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/g1a/composer-test-scenarios.git", + "reference": "a166fd15191aceab89f30c097e694b7cf3db4880" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/g1a/composer-test-scenarios/zipball/a166fd15191aceab89f30c097e694b7cf3db4880", + "reference": "a166fd15191aceab89f30c097e694b7cf3db4880", + "shasum": "" + }, + "bin": [ + "scripts/create-scenario", + "scripts/dependency-licenses", + "scripts/install-scenario" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Useful scripts for testing multiple sets of Composer dependencies.", + "time": "2018-08-08T23:37:23+00:00" + }, { "name": "grasmash/expander", "version": "1.0.0", @@ -6183,16 +6260,16 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.3.4", + "version": "2.3.5", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "ac56e5a6520dd580658034ae53d3724985c2a901" + "reference": "bb1518aab82464e25ff97874da939d13ba4b6fac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/ac56e5a6520dd580658034ae53d3724985c2a901", - "reference": "ac56e5a6520dd580658034ae53d3724985c2a901", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/bb1518aab82464e25ff97874da939d13ba4b6fac", + "reference": "bb1518aab82464e25ff97874da939d13ba4b6fac", "shasum": "" }, "require": { @@ -6250,7 +6327,7 @@ "magento", "testing" ], - "time": "2018-08-10T20:16:42+00:00" + "time": "2018-08-21T16:57:34+00:00" }, { "name": "moontoast/math", From f55919033d7ca5a6d36bfa183fb339ad65108650 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Tue, 21 Aug 2018 14:41:44 -0500 Subject: [PATCH 1103/1171] MQE-1217: Deliver MFTF 2.3.5 - Fix MFTF Tests --- .../ConfigurableProductAttributeNameDesignActionGroup.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml index eec1fd6273ee3..95533057608f2 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml @@ -69,26 +69,31 @@ <!--Add option 1 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption1"/> + <waitForPageLoad stepKey="waitForOption1"/> <fillField stepKey="fillInAdminFieldRed" selector="{{NewProduct.adminFieldRed}}" userInput="{{NewProductsData.adminFieldRed}}"/> <fillField stepKey="fillInDefaultStoreViewFieldRed" selector="{{NewProduct.defaultStoreViewFieldRed}}" userInput="{{NewProductsData.defaultStoreViewFieldRed}}"/> <!--Add option 2 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption2"/> + <waitForPageLoad stepKey="waitForOption2"/> <fillField stepKey="fillInAdminFieldBlue" selector="{{NewProduct.adminFieldBlue}}" userInput="{{NewProductsData.adminFieldBlue}}"/> <fillField stepKey="fillInDefaultStoreViewFieldBlue" selector="{{NewProduct.defaultStoreViewFieldBlue}}" userInput="{{NewProductsData.defaultStoreViewFieldBlue}}"/> <!--Add option 3 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption3"/> + <waitForPageLoad stepKey="waitForOption3"/> <fillField stepKey="fillInAdminFieldYellow" selector="{{NewProduct.adminFieldYellow}}" userInput="{{NewProductsData.adminFieldYellow}}"/> <fillField stepKey="fillInDefaultStoreViewFieldYellow" selector="{{NewProduct.defaultStoreViewFieldYellow}}" userInput="{{NewProductsData.defaultStoreViewFieldYellow}}"/> <!--Add option 4 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption4"/> + <waitForPageLoad stepKey="waitForOption4"/> <fillField stepKey="fillInAdminFieldGreen" selector="{{NewProduct.adminFieldGreen}}" userInput="{{NewProductsData.adminFieldGreen}}"/> <fillField stepKey="fillInDefaultStoreViewFieldGreen" selector="{{NewProduct.defaultStoreViewFieldGreen}}" userInput="{{NewProductsData.defaultStoreViewFieldGreen}}"/> <!--Add option 5 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption5"/> + <waitForPageLoad stepKey="waitForOption5"/> <fillField stepKey="fillInAdminFieldBlack" selector="{{NewProduct.adminFieldBlack}}" userInput="{{NewProductsData.adminFieldBlack}}"/> <fillField stepKey="fillInDefaultStoreViewFieldBlack" selector="{{NewProduct.defaultStoreViewFieldBlack}}" userInput="{{NewProductsData.defaultStoreViewFieldBlack}}"/> @@ -135,6 +140,7 @@ <!--Click on Stores item--> <click stepKey="clickOnStoresItem" selector="{{CatalogProductsSection.storesItem}}"/> + <waitForPageLoad stepKey="waitForNavigationPanel"/> <!--Click on Products item--> <waitForElementVisible selector="{{CatalogProductsSection.storesProductItem}}" stepKey="waitForCatalogLoad"/> From 5c8f128f864beebc79aaaf4628494d8aec83b93c Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Tue, 21 Aug 2018 15:04:54 -0500 Subject: [PATCH 1104/1171] MAGETWO-90974: HTML showing in minicart with custom option file upload Add <a> </a> as allowed tags for the escape html function. --- .../view/frontend/web/template/summary/item/details.html | 2 +- .../frontend/templates/order/items/renderer/default.phtml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html b/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html index 730ceadbd914c..dd59bd78416c6 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html @@ -35,7 +35,7 @@ <dd class="values" data-bind="html: full_view"></dd> <!-- /ko --> <!-- ko ifnot: ($data.full_view)--> - <dd class="values" data-bind="text: value"></dd> + <dd class="values" data-bind="html: value"></dd> <!-- /ko --> <!-- /ko --> </dl> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml index 227866b8e1c3d..19da4994c914e 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml @@ -20,9 +20,9 @@ $_item = $block->getItem(); <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dd> <?php if (isset($_formatedOptionValue['full_view'])): ?> - <?= $block->escapeHtml($_formatedOptionValue['full_view']) ?> + <?= $block->escapeHtml($_formatedOptionValue['full_view'], ['a']) ?> <?php else: ?> - <?=$block->escapeHtml($_formatedOptionValue['value']) ?> + <?=$block->escapeHtml($_formatedOptionValue['value'], ['a']) ?> <?php endif; ?> </dd> <?php else: ?> From 133311494ebc8bd60df6d1a1de4955aee843b860 Mon Sep 17 00:00:00 2001 From: NazarKlovanych <nazar.klovanich@transoftgroup.com> Date: Wed, 15 Aug 2018 10:17:03 +0300 Subject: [PATCH 1105/1171] Fix failed test EmailSenderHandlerTest --- .../Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php b/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php index 56ec763a31cfe..e26b6e52b8d17 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php @@ -118,7 +118,7 @@ public function testExecute($configValue, $collectionItems, $emailSendingResult) $path = 'sales_email/general/async_sending'; $this->globalConfig - ->expects($this->once()) + ->expects($this->at(0)) ->method('getValue') ->with($path) ->willReturn($configValue); From 4b5ae00f0677a32e5172b378f3bb83ed80e61978 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Wed, 22 Aug 2018 10:35:21 +0300 Subject: [PATCH 1106/1171] Replaced deprecated methods. --- .../Controller/Adminhtml/Export/GetFilter.php | 4 ++-- .../Backend/App/Action/Plugin/Authentication.php | 4 ++-- .../Backend/Controller/Adminhtml/Auth/Logout.php | 2 +- .../Controller/Adminhtml/Cache/CleanImages.php | 6 +++--- .../Backend/Controller/Adminhtml/Cache/CleanMedia.php | 7 ++++--- .../Controller/Adminhtml/Cache/CleanStaticFiles.php | 2 +- .../Backend/Controller/Adminhtml/Cache/FlushAll.php | 2 +- .../Controller/Adminhtml/Cache/FlushSystem.php | 2 +- .../Controller/Adminhtml/Cache/MassDisable.php | 6 +++--- .../Backend/Controller/Adminhtml/Cache/MassEnable.php | 6 +++--- .../Controller/Adminhtml/Cache/MassRefresh.php | 6 +++--- .../Adminhtml/Dashboard/RefreshStatistics.php | 4 ++-- .../Controller/Adminhtml/System/Account/Save.php | 10 +++++----- .../Controller/Adminhtml/System/Design/Delete.php | 6 +++--- .../Controller/Adminhtml/System/Design/Save.php | 4 ++-- .../Backend/Controller/Adminhtml/System/Store.php | 8 ++++---- .../Controller/Adminhtml/System/Store/DeleteGroup.php | 4 ++-- .../Adminhtml/System/Store/DeleteGroupPost.php | 10 +++++----- .../Controller/Adminhtml/System/Store/DeleteStore.php | 4 ++-- .../Adminhtml/System/Store/DeleteStorePost.php | 11 ++++++----- .../Adminhtml/System/Store/DeleteWebsite.php | 4 ++-- .../Adminhtml/System/Store/DeleteWebsitePost.php | 10 +++++----- .../Controller/Adminhtml/System/Store/EditStore.php | 4 ++-- .../Controller/Adminhtml/System/Store/Save.php | 10 +++++----- .../Controller/Adminhtml/Cache/CleanMediaTest.php | 4 ++-- .../Adminhtml/Cache/CleanStaticFilesTest.php | 2 +- .../Controller/Adminhtml/Cache/MassDisableTest.php | 6 +++--- .../Controller/Adminhtml/Cache/MassEnableTest.php | 6 +++--- .../Adminhtml/Dashboard/RefreshStatisticsTest.php | 2 +- .../Controller/Adminhtml/System/Account/SaveTest.php | 4 ++-- .../Backup/Controller/Adminhtml/Index/Create.php | 2 +- .../Backup/Controller/Adminhtml/Index/MassDelete.php | 4 ++-- .../CatalogRule/Observer/AddDirtyRulesNotice.php | 2 +- .../Test/Unit/Observer/AddDirtyRulesNoticeTest.php | 2 +- .../Backend/Controller/Adminhtml/CacheTest.php | 2 +- 35 files changed, 87 insertions(+), 85 deletions(-) diff --git a/app/code/Magento/AdvancedPricingImportExport/Controller/Adminhtml/Export/GetFilter.php b/app/code/Magento/AdvancedPricingImportExport/Controller/Adminhtml/Export/GetFilter.php index 02413a1899cd7..818bcda1da65f 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Controller/Adminhtml/Export/GetFilter.php +++ b/app/code/Magento/AdvancedPricingImportExport/Controller/Adminhtml/Export/GetFilter.php @@ -37,10 +37,10 @@ public function execute() ); return $resultLayout; } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } } else { - $this->messageManager->addError(__('Please correct the data sent.')); + $this->messageManager->addErrorMessage(__('Please correct the data sent.')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Backend/App/Action/Plugin/Authentication.php b/app/code/Magento/Backend/App/Action/Plugin/Authentication.php index 68506a521c1cf..4b25e9921e404 100644 --- a/app/code/Magento/Backend/App/Action/Plugin/Authentication.php +++ b/app/code/Magento/Backend/App/Action/Plugin/Authentication.php @@ -160,7 +160,7 @@ protected function _processNotLoggedInUser(\Magento\Framework\App\RequestInterfa } else { $this->_actionFlag->set('', \Magento\Framework\App\ActionInterface::FLAG_NO_DISPATCH, true); $this->_response->setRedirect($this->_url->getCurrentUrl()); - $this->messageManager->addError(__('Invalid Form Key. Please refresh the page.')); + $this->messageManager->addErrorMessage(__('Invalid Form Key. Please refresh the page.')); $isRedirectNeeded = true; } } @@ -205,7 +205,7 @@ protected function _performLogin(\Magento\Framework\App\RequestInterface $reques $this->_auth->login($username, $password); } catch (AuthenticationException $e) { if (!$request->getParam('messageSent')) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $request->setParam('messageSent', true); $outputValue = false; } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Auth/Logout.php b/app/code/Magento/Backend/Controller/Adminhtml/Auth/Logout.php index 41e32c929287a..e55c449a0e5bb 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Auth/Logout.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Auth/Logout.php @@ -16,7 +16,7 @@ class Logout extends \Magento\Backend\Controller\Adminhtml\Auth public function execute() { $this->_auth->logout(); - $this->messageManager->addSuccess(__('You have logged out.')); + $this->messageManager->addSuccessMessage(__('You have logged out.')); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanImages.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanImages.php index 7a926b1c09c3e..888ce11313b8a 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanImages.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanImages.php @@ -28,11 +28,11 @@ public function execute() try { $this->_objectManager->create(\Magento\Catalog\Model\Product\Image::class)->clearCache(); $this->_eventManager->dispatch('clean_catalog_images_cache_after'); - $this->messageManager->addSuccess(__('The image cache was cleaned.')); + $this->messageManager->addSuccessMessage(__('The image cache was cleaned.')); } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('An error occurred while clearing the image cache.')); + $this->messageManager->addExceptionMessage($e, __('An error occurred while clearing the image cache.')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanMedia.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanMedia.php index 72f23ab65cf8a..5df0a7779c4c1 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanMedia.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanMedia.php @@ -28,11 +28,12 @@ public function execute() try { $this->_objectManager->get(\Magento\Framework\View\Asset\MergeService::class)->cleanMergedJsCss(); $this->_eventManager->dispatch('clean_media_cache_after'); - $this->messageManager->addSuccess(__('The JavaScript/CSS cache has been cleaned.')); + $this->messageManager->addSuccessMessage(__('The JavaScript/CSS cache has been cleaned.')); } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('An error occurred while clearing the JavaScript/CSS cache.')); + $this->messageManager + ->addExceptionMessage($e, __('An error occurred while clearing the JavaScript/CSS cache.')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanStaticFiles.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanStaticFiles.php index 27ae2fc31e150..489eb5799a5e7 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanStaticFiles.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanStaticFiles.php @@ -26,7 +26,7 @@ public function execute() { $this->_objectManager->get(\Magento\Framework\App\State\CleanupFiles::class)->clearMaterializedViewFiles(); $this->_eventManager->dispatch('clean_static_files_cache_after'); - $this->messageManager->addSuccess(__('The static files cache has been cleaned.')); + $this->messageManager->addSuccessMessage(__('The static files cache has been cleaned.')); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushAll.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushAll.php index ca89ea58fa6f3..a2f18b4baf53d 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushAll.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushAll.php @@ -27,7 +27,7 @@ public function execute() foreach ($this->_cacheFrontendPool as $cacheFrontend) { $cacheFrontend->getBackend()->clean(); } - $this->messageManager->addSuccess(__("You flushed the cache storage.")); + $this->messageManager->addSuccessMessage(__("You flushed the cache storage.")); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('adminhtml/*'); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushSystem.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushSystem.php index f0fed159e0f22..90ed3432fa87b 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushSystem.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushSystem.php @@ -27,7 +27,7 @@ public function execute() $cacheFrontend->clean(); } $this->_eventManager->dispatch('adminhtml_cache_flush_system'); - $this->messageManager->addSuccess(__("The Magento cache storage has been flushed.")); + $this->messageManager->addSuccessMessage(__("The Magento cache storage has been flushed.")); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('adminhtml/*'); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassDisable.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassDisable.php index 2bfa937b06b77..03b88ca1d3f47 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassDisable.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassDisable.php @@ -67,12 +67,12 @@ private function disableCache() } if ($updatedTypes > 0) { $this->_cacheState->persist(); - $this->messageManager->addSuccess(__("%1 cache type(s) disabled.", $updatedTypes)); + $this->messageManager->addSuccessMessage(__("%1 cache type(s) disabled.", $updatedTypes)); } } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('An error occurred while disabling cache.')); + $this->messageManager->addExceptionMessage($e, __('An error occurred while disabling cache.')); } } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassEnable.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassEnable.php index 113e0f2d8961b..1b98a00d4bf35 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassEnable.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassEnable.php @@ -66,12 +66,12 @@ private function enableCache() } if ($updatedTypes > 0) { $this->_cacheState->persist(); - $this->messageManager->addSuccess(__("%1 cache type(s) enabled.", $updatedTypes)); + $this->messageManager->addSuccessMessage(__("%1 cache type(s) enabled.", $updatedTypes)); } } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('An error occurred while enabling cache.')); + $this->messageManager->addExceptionMessage($e, __('An error occurred while enabling cache.')); } } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassRefresh.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassRefresh.php index 3843b030afb3d..bde211debcf72 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassRefresh.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassRefresh.php @@ -37,12 +37,12 @@ public function execute() $updatedTypes++; } if ($updatedTypes > 0) { - $this->messageManager->addSuccess(__("%1 cache type(s) refreshed.", $updatedTypes)); + $this->messageManager->addSuccessMessage(__("%1 cache type(s) refreshed.", $updatedTypes)); } } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('An error occurred while refreshing cache.')); + $this->messageManager->addExceptionMessage($e, __('An error occurred while refreshing cache.')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/RefreshStatistics.php b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/RefreshStatistics.php index f831fa67f4bb0..c10d1a77997b7 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/RefreshStatistics.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/RefreshStatistics.php @@ -34,9 +34,9 @@ public function execute() foreach ($collectionsNames as $collectionName) { $this->_objectManager->create($collectionName)->aggregate(); } - $this->messageManager->addSuccess(__('We updated lifetime statistic.')); + $this->messageManager->addSuccessMessage(__('We updated lifetime statistic.')); } catch (\Exception $e) { - $this->messageManager->addError(__('We can\'t refresh lifetime statistics.')); + $this->messageManager->addErrorMessage(__('We can\'t refresh lifetime statistics.')); $this->logger->critical($e); } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Account/Save.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Account/Save.php index 1b10c151a9d21..d95b0541c2c76 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Account/Save.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Account/Save.php @@ -76,12 +76,12 @@ public function execute() $errors = $user->validate(); if ($errors !== true && !empty($errors)) { foreach ($errors as $error) { - $this->messageManager->addError($error); + $this->messageManager->addErrorMessage($error); } } else { $user->save(); $user->sendNotificationEmailsIfRequired(); - $this->messageManager->addSuccess(__('You saved the account.')); + $this->messageManager->addSuccessMessage(__('You saved the account.')); } } catch (UserLockedException $e) { $this->_auth->logout(); @@ -91,12 +91,12 @@ public function execute() } catch (ValidatorException $e) { $this->messageManager->addMessages($e->getMessages()); if ($e->getMessage()) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('An error occurred while saving account.')); + $this->messageManager->addErrorMessage(__('An error occurred while saving account.')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Delete.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Delete.php index 76402169f269e..21f28188cf874 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Delete.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Delete.php @@ -19,11 +19,11 @@ public function execute() try { $design->delete(); - $this->messageManager->addSuccess(__('You deleted the design change.')); + $this->messageManager->addSuccessMessage(__('You deleted the design change.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __("You can't delete the design change.")); + $this->messageManager->addExceptionMessage($e, __("You can't delete the design change.")); } } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php index 1f478604ced7d..0228b48f7f11e 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php @@ -50,9 +50,9 @@ public function execute() try { $design->save(); $this->_eventManager->dispatch('theme_save_after'); - $this->messageManager->addSuccess(__('You saved the design change.')); + $this->messageManager->addSuccessMessage(__('You saved the design change.')); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setDesignData($data); return $resultRedirect->setPath('adminhtml/*/', ['id' => $design->getId()]); } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store.php index 4fbae6abb423a..0beeb5168b6d1 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store.php @@ -103,12 +103,12 @@ protected function _backupDatabase() ->setType('db') ->setPath($filesystem->getDirectoryRead(DirectoryList::VAR_DIR)->getAbsolutePath('backups')); $backupDb->createBackup($backup); - $this->messageManager->addSuccess(__('The database was backed up.')); + $this->messageManager->addSuccessMessage(__('The database was backed up.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); return false; } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('We can\'t create a backup right now. Please try again later.') ); @@ -125,7 +125,7 @@ protected function _backupDatabase() */ protected function _addDeletionNotice($typeTitle) { - $this->messageManager->addNotice( + $this->messageManager->addNoticeMessage( __( 'Deleting a %1 will not delete the information associated with the %1 (e.g. categories, products, etc.)' . ', but the %1 will not be able to be restored. It is suggested that you create a database backup ' diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroup.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroup.php index 925ae4c69ee8e..4e323be709ae1 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroup.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroup.php @@ -15,13 +15,13 @@ public function execute() { $itemId = $this->getRequest()->getParam('item_id', null); if (!($model = $this->_objectManager->create(\Magento\Store\Model\Group::class)->load($itemId))) { - $this->messageManager->addError(__('Something went wrong. Please try again.')); + $this->messageManager->addErrorMessage(__('Something went wrong. Please try again.')); /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultRedirectFactory->create(); return $redirectResult->setPath('adminhtml/*/'); } if (!$model->isCanDelete()) { - $this->messageManager->addError(__('This store cannot be deleted.')); + $this->messageManager->addErrorMessage(__('This store cannot be deleted.')); /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultRedirectFactory->create(); return $redirectResult->setPath('adminhtml/*/editGroup', ['group_id' => $itemId]); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroupPost.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroupPost.php index b6fbd88c7669c..23364aac1f0ab 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroupPost.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroupPost.php @@ -21,11 +21,11 @@ public function execute() $redirectResult = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); if (!($model = $this->_objectManager->create(\Magento\Store\Model\Group::class)->load($itemId))) { - $this->messageManager->addError(__('Something went wrong. Please try again.')); + $this->messageManager->addErrorMessage(__('Something went wrong. Please try again.')); return $redirectResult->setPath('adminhtml/*/'); } if (!$model->isCanDelete()) { - $this->messageManager->addError(__('This store cannot be deleted.')); + $this->messageManager->addErrorMessage(__('This store cannot be deleted.')); return $redirectResult->setPath('adminhtml/*/editGroup', ['group_id' => $model->getId()]); } @@ -35,12 +35,12 @@ public function execute() try { $model->delete(); - $this->messageManager->addSuccess(__('You deleted the store.')); + $this->messageManager->addSuccessMessage(__('You deleted the store.')); return $redirectResult->setPath('adminhtml/*/'); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Unable to delete the store. Please try again later.')); + $this->messageManager->addExceptionMessage($e, __('Unable to delete the store. Please try again later.')); } return $redirectResult->setPath('adminhtml/*/editGroup', ['group_id' => $itemId]); } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStore.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStore.php index b31de6cacc5ff..c340b1ec53aa5 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStore.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStore.php @@ -15,13 +15,13 @@ public function execute() { $itemId = $this->getRequest()->getParam('item_id', null); if (!($model = $this->_objectManager->create(\Magento\Store\Model\Store::class)->load($itemId))) { - $this->messageManager->addError(__('Something went wrong. Please try again.')); + $this->messageManager->addErrorMessage(__('Something went wrong. Please try again.')); /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultRedirectFactory->create(); return $redirectResult->setPath('adminhtml/*/'); } if (!$model->isCanDelete()) { - $this->messageManager->addError(__('This store view cannot be deleted.')); + $this->messageManager->addErrorMessage(__('This store view cannot be deleted.')); /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultRedirectFactory->create(); return $redirectResult->setPath('adminhtml/*/editStore', ['store_id' => $itemId]); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStorePost.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStorePost.php index 13b104c5ec4c0..8146bfdd41e40 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStorePost.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStorePost.php @@ -22,11 +22,11 @@ public function execute() /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); if (!($model = $this->_objectManager->create(\Magento\Store\Model\Store::class)->load($itemId))) { - $this->messageManager->addError(__('Something went wrong. Please try again.')); + $this->messageManager->addErrorMessage(__('Something went wrong. Please try again.')); return $redirectResult->setPath('adminhtml/*/'); } if (!$model->isCanDelete()) { - $this->messageManager->addError(__('This store view cannot be deleted.')); + $this->messageManager->addErrorMessage(__('This store view cannot be deleted.')); return $redirectResult->setPath('adminhtml/*/editStore', ['store_id' => $model->getId()]); } @@ -37,12 +37,13 @@ public function execute() try { $model->delete(); - $this->messageManager->addSuccess(__('You deleted the store view.')); + $this->messageManager->addSuccessMessage(__('You deleted the store view.')); return $redirectResult->setPath('adminhtml/*/'); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Unable to delete the store view. Please try again later.')); + $this->messageManager + ->addExceptionMessage($e, __('Unable to delete the store view. Please try again later.')); } return $redirectResult->setPath('adminhtml/*/editStore', ['store_id' => $itemId]); } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsite.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsite.php index 1f2ec4b2ba4b1..d86f57daa396c 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsite.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsite.php @@ -15,13 +15,13 @@ public function execute() { $itemId = $this->getRequest()->getParam('item_id', null); if (!($model = $this->_objectManager->create(\Magento\Store\Model\Website::class)->load($itemId))) { - $this->messageManager->addError(__('Something went wrong. Please try again.')); + $this->messageManager->addErrorMessage(__('Something went wrong. Please try again.')); /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultRedirectFactory->create(); return $redirectResult->setPath('adminhtml/*/'); } if (!$model->isCanDelete()) { - $this->messageManager->addError(__('This website cannot be deleted.')); + $this->messageManager->addErrorMessage(__('This website cannot be deleted.')); /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultRedirectFactory->create(); return $redirectResult->setPath('adminhtml/*/editWebsite', ['website_id' => $itemId]); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsitePost.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsitePost.php index c2d24b8c41a8c..1fca5a896e050 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsitePost.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsitePost.php @@ -23,11 +23,11 @@ public function execute() $redirectResult = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); if (!$model) { - $this->messageManager->addError(__('Something went wrong. Please try again.')); + $this->messageManager->addErrorMessage(__('Something went wrong. Please try again.')); return $redirectResult->setPath('adminhtml/*/'); } if (!$model->isCanDelete()) { - $this->messageManager->addError(__('This website cannot be deleted.')); + $this->messageManager->addErrorMessage(__('This website cannot be deleted.')); return $redirectResult->setPath('adminhtml/*/editWebsite', ['website_id' => $model->getId()]); } @@ -37,12 +37,12 @@ public function execute() try { $model->delete(); - $this->messageManager->addSuccess(__('You deleted the website.')); + $this->messageManager->addSuccessMessage(__('You deleted the website.')); return $redirectResult->setPath('adminhtml/*/'); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Unable to delete the website. Please try again later.')); + $this->messageManager->addExceptionMessage($e, __('Unable to delete the website. Please try again later.')); } return $redirectResult->setPath('*/*/editWebsite', ['website_id' => $itemId]); } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/EditStore.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/EditStore.php index cbc068a480865..a8651984cfa63 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/EditStore.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/EditStore.php @@ -57,7 +57,7 @@ public function execute() if ($model->getId() || $this->_coreRegistry->registry('store_action') == 'add') { $this->_coreRegistry->register('store_data', $model); if ($this->_coreRegistry->registry('store_action') == 'edit' && $codeBase && !$model->isReadOnly()) { - $this->messageManager->addNotice($codeBase); + $this->messageManager->addNoticeMessage($codeBase); } $resultPage = $this->createPage(); if ($this->_coreRegistry->registry('store_action') == 'add') { @@ -71,7 +71,7 @@ public function execute() )); return $resultPage; } else { - $this->messageManager->addError($notExists); + $this->messageManager->addErrorMessage($notExists); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('adminhtml/*/'); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/Save.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/Save.php index 8ca783f887ec4..910511c2b275e 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/Save.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/Save.php @@ -32,7 +32,7 @@ private function processWebsiteSave($postData) } $websiteModel->save(); - $this->messageManager->addSuccess(__('You saved the website.')); + $this->messageManager->addSuccessMessage(__('You saved the website.')); return $postData; } @@ -68,7 +68,7 @@ private function processStoreSave($postData) ); } $storeModel->save(); - $this->messageManager->addSuccess(__('You saved the store view.')); + $this->messageManager->addSuccessMessage(__('You saved the store view.')); return $postData; } @@ -98,7 +98,7 @@ private function processGroupSave($postData) ); } $groupModel->save(); - $this->messageManager->addSuccess(__('You saved the store.')); + $this->messageManager->addSuccessMessage(__('You saved the store.')); return $postData; } @@ -134,10 +134,10 @@ public function execute() $redirectResult->setPath('adminhtml/*/'); return $redirectResult; } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $this->_getSession()->setPostData($postData); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('Something went wrong while saving. Please review the error log.') ); diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanMediaTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanMediaTest.php index b1911da024227..ac0f4a2f467c8 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanMediaTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanMediaTest.php @@ -38,7 +38,7 @@ public function testExecute() $messageManagerParams = $helper->getConstructArguments(\Magento\Framework\Message\Manager::class); $messageManagerParams['exceptionMessageFactory'] = $exceptionMessageFactory; $messageManager = $this->getMockBuilder(\Magento\Framework\Message\Manager::class) - ->setMethods(['addSuccess']) + ->setMethods(['addSuccessMessage']) ->setConstructorArgs($messageManagerParams) ->getMock(); @@ -86,7 +86,7 @@ public function testExecute() $mergeService->expects($this->once())->method('cleanMergedJsCss'); $messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('The JavaScript/CSS cache has been cleaned.'); $valueMap = [ diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanStaticFilesTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanStaticFilesTest.php index 40d9ca1aa8996..fc457cd9681e6 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanStaticFilesTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanStaticFilesTest.php @@ -76,7 +76,7 @@ public function testExecute() ->with('clean_static_files_cache_after'); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('The static files cache has been cleaned.'); $resultRedirect = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassDisableTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassDisableTest.php index 197b46acc61bc..a8b248c611e07 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassDisableTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassDisableTest.php @@ -156,7 +156,7 @@ public function testExecuteInvalidTypeCache() ->willReturn(['someCache']); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('These cache type(s) don\'t exist: someCache') ->willReturnSelf(); @@ -176,7 +176,7 @@ public function testExecuteWithException() ->willThrowException($exception); $this->messageManagerMock->expects($this->once()) - ->method('addException') + ->method('addExceptionMessage') ->with($exception, 'An error occurred while disabling cache.') ->willReturnSelf(); @@ -216,7 +216,7 @@ public function testExecuteSuccess() ->method('persist'); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('1 cache type(s) disabled.') ->willReturnSelf(); 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 index 9b3640193154a..6eac44a564f6d 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php @@ -156,7 +156,7 @@ public function testExecuteInvalidTypeCache() ->willReturn(['someCache']); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('These cache type(s) don\'t exist: someCache') ->willReturnSelf(); @@ -176,7 +176,7 @@ public function testExecuteWithException() ->willThrowException($exception); $this->messageManagerMock->expects($this->once()) - ->method('addException') + ->method('addExceptionMessage') ->with($exception, 'An error occurred while enabling cache.') ->willReturnSelf(); @@ -216,7 +216,7 @@ public function testExecuteSuccess() ->method('persist'); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('1 cache type(s) enabled.') ->willReturnSelf(); diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/RefreshStatisticsTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/RefreshStatisticsTest.php index e8dcc00345fc6..a985681919f0b 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/RefreshStatisticsTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/RefreshStatisticsTest.php @@ -107,7 +107,7 @@ public function testExecute() $this->resultRedirectFactory->expects($this->any())->method('create')->willReturn($this->resultRedirect); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('We updated lifetime statistic.')); $this->objectManager->expects($this->any()) diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/System/Account/SaveTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/System/Account/SaveTest.php index 844a821df1c20..a8490d6ba2e58 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/System/Account/SaveTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/System/Account/SaveTest.php @@ -71,7 +71,7 @@ protected function setUp() ->getMock(); $this->_messagesMock = $this->getMockBuilder(\Magento\Framework\Message\Manager::class) ->disableOriginalConstructor() - ->setMethods(['addSuccess']) + ->setMethods(['addSuccessMessage']) ->getMockForAbstractClass(); $this->_authSessionMock = $this->getMockBuilder(\Magento\Backend\Model\Auth\Session::class) @@ -221,7 +221,7 @@ public function testSaveAction() $this->_requestMock->setParams($requestParams); - $this->_messagesMock->expects($this->once())->method('addSuccess')->with($this->equalTo($testedMessage)); + $this->_messagesMock->expects($this->once())->method('addSuccessMessage')->with($this->equalTo($testedMessage)); $this->_controller->execute(); } diff --git a/app/code/Magento/Backup/Controller/Adminhtml/Index/Create.php b/app/code/Magento/Backup/Controller/Adminhtml/Index/Create.php index 27770182a6db6..53f45aff50cbc 100644 --- a/app/code/Magento/Backup/Controller/Adminhtml/Index/Create.php +++ b/app/code/Magento/Backup/Controller/Adminhtml/Index/Create.php @@ -82,7 +82,7 @@ public function execute() $backupManager->create(); - $this->messageManager->addSuccess($successMessage); + $this->messageManager->addSuccessMessage($successMessage); $response->setRedirectUrl($this->getUrl('*/*/index')); } catch (\Magento\Framework\Backup\Exception\NotEnoughFreeSpace $e) { diff --git a/app/code/Magento/Backup/Controller/Adminhtml/Index/MassDelete.php b/app/code/Magento/Backup/Controller/Adminhtml/Index/MassDelete.php index 04292d2759093..90657fc2490ba 100644 --- a/app/code/Magento/Backup/Controller/Adminhtml/Index/MassDelete.php +++ b/app/code/Magento/Backup/Controller/Adminhtml/Index/MassDelete.php @@ -49,13 +49,13 @@ public function execute() $resultData->setIsSuccess(true); if ($allBackupsDeleted) { - $this->messageManager->addSuccess(__('You deleted the selected backup(s).')); + $this->messageManager->addSuccessMessage(__('You deleted the selected backup(s).')); } else { throw new \Exception($deleteFailMessage); } } catch (\Exception $e) { $resultData->setIsSuccess(false); - $this->messageManager->addError($deleteFailMessage); + $this->messageManager->addErrorMessage($deleteFailMessage); } return $this->_redirect('backup/*/index'); diff --git a/app/code/Magento/CatalogRule/Observer/AddDirtyRulesNotice.php b/app/code/Magento/CatalogRule/Observer/AddDirtyRulesNotice.php index 08c3d97b216ed..749ac3cf51249 100644 --- a/app/code/Magento/CatalogRule/Observer/AddDirtyRulesNotice.php +++ b/app/code/Magento/CatalogRule/Observer/AddDirtyRulesNotice.php @@ -37,7 +37,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) $dirtyRules = $observer->getData('dirty_rules'); if (!empty($dirtyRules)) { if ($dirtyRules->getState()) { - $this->messageManager->addNotice($observer->getData('message')); + $this->messageManager->addNoticeMessage($observer->getData('message')); } } } diff --git a/app/code/Magento/CatalogRule/Test/Unit/Observer/AddDirtyRulesNoticeTest.php b/app/code/Magento/CatalogRule/Test/Unit/Observer/AddDirtyRulesNoticeTest.php index b052ccddbf6b4..25bae43a930bb 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Observer/AddDirtyRulesNoticeTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Observer/AddDirtyRulesNoticeTest.php @@ -49,7 +49,7 @@ public function testExecute() $eventObserverMock->expects($this->at(0))->method('getData')->with('dirty_rules')->willReturn($flagMock); $flagMock->expects($this->once())->method('getState')->willReturn(1); $eventObserverMock->expects($this->at(1))->method('getData')->with('message')->willReturn($message); - $this->messageManagerMock->expects($this->once())->method('addNotice')->with($message); + $this->messageManagerMock->expects($this->once())->method('addNoticeMessage')->with($message); $this->observer->execute($eventObserverMock); } } diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/CacheTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/CacheTest.php index 04930661efb43..6836161fd6ec2 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/CacheTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/CacheTest.php @@ -73,7 +73,7 @@ public function testMassActionsInvalidTypes($action) $this->getRequest()->setParams(['types' => ['invalid_type_1', 'invalid_type_2', 'config']]); $this->dispatch('backend/admin/cache/' . $action); $this->assertSessionMessages( - $this->contains("These cache type(s) don't exist: invalid_type_1, invalid_type_2"), + $this->contains("These cache type(s) don't exist: invalid_type_1, invalid_type_2"), \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); } From 007bad9ccaeb4130f000808fe19efb680a49a403 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Wed, 13 Jun 2018 14:08:07 +0300 Subject: [PATCH 1107/1171] Added translation for label/comment tags --- .../Magento/Backend/etc/adminhtml/system.xml | 28 +++++++++---------- .../Braintree/etc/adminhtml/system.xml | 22 +++++++-------- .../CatalogInventory/etc/adminhtml/system.xml | 4 +-- .../CatalogSearch/etc/adminhtml/system.xml | 2 +- .../Magento/Cookie/etc/adminhtml/system.xml | 2 +- .../Magento/Customer/etc/adminhtml/system.xml | 12 ++++---- .../Developer/etc/adminhtml/system.xml | 4 +-- app/code/Magento/Dhl/etc/adminhtml/system.xml | 2 +- .../GoogleOptimizer/etc/adminhtml/system.xml | 2 +- .../InstantPurchase/etc/adminhtml/system.xml | 2 +- .../Integration/etc/adminhtml/system.xml | 18 ++++++------ .../etc/adminhtml/system.xml | 2 +- .../MediaStorage/etc/adminhtml/system.xml | 2 +- .../OfflinePayments/etc/adminhtml/system.xml | 2 +- .../OfflineShipping/etc/adminhtml/system.xml | 2 +- .../PageCache/etc/adminhtml/system.xml | 2 +- .../Magento/Paypal/etc/adminhtml/system.xml | 20 ++++++------- .../Magento/Sales/etc/adminhtml/system.xml | 4 +-- .../Magento/Signifyd/etc/adminhtml/system.xml | 4 +-- app/code/Magento/Tax/etc/adminhtml/system.xml | 2 +- .../Translation/etc/adminhtml/system.xml | 2 +- .../WebapiSecurity/etc/adminhtml/system.xml | 2 +- 22 files changed, 71 insertions(+), 71 deletions(-) diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index be1b836d64802..cd32d5224ab6a 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -197,7 +197,7 @@ </group> <group id="image" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Image Processing Settings</label> - <field id="default_adapter" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="default_adapter" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Image Adapter</label> <source_model>Magento\Config\Model\Config\Source\Image\Adapter</source_model> <backend_model>Magento\Config\Model\Config\Backend\Image\Adapter</backend_model> @@ -314,11 +314,11 @@ <label>Disable Email Communications</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="host" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="host" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Host</label> <comment>For Windows server only.</comment> </field> - <field id="port" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="port" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Port (25)</label> <comment>For Windows server only.</comment> </field> @@ -435,7 +435,7 @@ <![CDATA[<strong style="color:red">Warning!</strong> When using Store Code in URLs, in some cases system may not work properly if URLs without Store Codes are specified in the third-party services (e.g. PayPal etc.).]]> </comment> </field> - <field id="redirect_to_base" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="redirect_to_base" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Auto-redirect to Base URL</label> <source_model>Magento\Config\Model\Config\Source\Web\Redirect</source_model> <comment>I.e. redirect from http://example.com/store/ to http://www.example.com/store/</comment> @@ -448,7 +448,7 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> </group> - <group id="unsecure" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <group id="unsecure" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Base URLs</label> <comment>Any of the fields allow fully qualified URLs that end with '/' (slash) e.g. http://example.com/magento/</comment> <field id="base_url" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -456,7 +456,7 @@ <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> <comment>Specify URL or {{base_url}} placeholder.</comment> </field> - <field id="base_link_url" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="base_link_url" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Base Link URL</label> <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> <comment>May start with {{unsecure_base_url}} placeholder.</comment> @@ -466,13 +466,13 @@ <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> <comment>May be empty or start with {{unsecure_base_url}} placeholder.</comment> </field> - <field id="base_media_url" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="base_media_url" translate="label comment" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Base URL for User Media Files</label> <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> <comment>May be empty or start with {{unsecure_base_url}} placeholder.</comment> </field> </group> - <group id="secure" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <group id="secure" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Base URLs (Secure)</label> <comment>Any of the fields allow fully qualified URLs that end with '/' (slash) e.g. https://example.com/magento/</comment> <field id="base_url" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -480,7 +480,7 @@ <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> <comment>Specify URL or {{base_url}}, or {{unsecure_base_url}} placeholder.</comment> </field> - <field id="base_link_url" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="base_link_url" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Secure Base Link URL</label> <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> <comment>May start with {{secure_base_url}} or {{unsecure_base_url}} placeholder.</comment> @@ -490,24 +490,24 @@ <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> <comment>May be empty or start with {{secure_base_url}}, or {{unsecure_base_url}} placeholder.</comment> </field> - <field id="base_media_url" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="base_media_url" translate="label comment" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Secure Base URL for User Media Files</label> <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> <comment>May be empty or start with {{secure_base_url}}, or {{unsecure_base_url}} placeholder.</comment> </field> - <field id="use_in_frontend" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="use_in_frontend" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Use Secure URLs on Storefront</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Secure</backend_model> <comment>Enter https protocol to use Secure URLs on Storefront.</comment> </field> - <field id="use_in_adminhtml" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_in_adminhtml" translate="label comment" type="select" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Use Secure URLs in Admin</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Secure</backend_model> <comment>Enter https protocol to use Secure URLs in Admin.</comment> </field> - <field id="enable_hsts" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="enable_hsts" translate="label comment" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Enable HTTP Strict Transport Security (HSTS)</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Secure</backend_model> @@ -517,7 +517,7 @@ <field id="use_in_adminhtml">1</field> </depends> </field> - <field id="enable_upgrade_insecure" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="enable_upgrade_insecure" translate="label comment" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Upgrade Insecure Requests</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Secure</backend_model> diff --git a/app/code/Magento/Braintree/etc/adminhtml/system.xml b/app/code/Magento/Braintree/etc/adminhtml/system.xml index c49402070f0fd..5215dbc00b7ef 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/system.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/system.xml @@ -84,18 +84,18 @@ <label>Vault Title</label> <config_path>payment/braintree_cc_vault/title</config_path> </field> - <field id="merchant_account_id" translate="label" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="merchant_account_id" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Merchant Account ID</label> <comment>If you don't specify the merchant account to use to process a transaction, Braintree will process it using your default merchant account.</comment> <config_path>payment/braintree/merchant_account_id</config_path> </field> - <field id="fraudprotection" translate="label" type="select" sortOrder="34" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="fraudprotection" translate="label comment" type="select" sortOrder="34" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Advanced Fraud Protection</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Be sure to Enable Advanced Fraud Protection in Your Braintree Account in Settings/Processing Section</comment> <config_path>payment/braintree/fraudprotection</config_path> </field> - <field id="kount_id" translate="label" sortOrder="35" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="kount_id" translate="label comment" sortOrder="35" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Kount Merchant ID</label> <comment><![CDATA[Used for direct fraud tool integration. Make sure you also contact <a href="mailto:accounts@braintreepayments.com">accounts@braintreepayments.com</a> to setup your Kount account.]]></comment> <depends> @@ -108,7 +108,7 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree/debug</config_path> </field> - <field id="useccv" translate="label" type="select" sortOrder="150" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="useccv" translate="label comment" type="select" sortOrder="150" showInDefault="1" showInWebsite="1" showInStore="0"> <label>CVV Verification</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Be sure to Enable AVS and/or CVV in Your Braintree Account in Settings/Processing Section.</comment> @@ -149,7 +149,7 @@ <group id="braintree_paypal" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40"> <label>PayPal through Braintree</label> <frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> - <field id="title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="title" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Title</label> <config_path>payment/braintree_paypal/title</config_path> <comment>It is recommended to set this value to "PayPal" per store views.</comment> @@ -187,7 +187,7 @@ <can_be_empty>1</can_be_empty> <config_path>payment/braintree_paypal/specificcountry</config_path> </field> - <field id="require_billing_address" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="require_billing_address" translate="label comment" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Require Customer's Billing Address</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/require_billing_address</config_path> @@ -203,7 +203,7 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/debug</config_path> </field> - <field id="display_on_shopping_cart" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="display_on_shopping_cart" translate="label comment" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Display on Shopping Cart</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/display_on_shopping_cart</config_path> @@ -239,14 +239,14 @@ <config_path>payment/braintree/verify_specific_countries</config_path> </field> </group> - <group id="braintree_dynamic_descriptor" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="50"> + <group id="braintree_dynamic_descriptor" translate="label comment" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="50"> <label>Dynamic Descriptors</label> <comment><![CDATA[Dynamic descriptors are sent on a per-transaction basis and define what will appear on your customers credit card statements for a specific purchase. The clearer the description of your product, the less likely customers will issue chargebacks due to confusion or non-recognition. Dynamic descriptors are not enabled on all accounts by default. If you receive a validation error of 92203 or if your dynamic descriptors are not displaying as expected, please <a href="mailto:support@getbraintree.com">Braintree Support</a>.]]></comment> <frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> - <field id="name" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="name" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Name</label> <config_path>payment/braintree/descriptor_name</config_path> <comment> @@ -254,14 +254,14 @@ and the product descriptor can be up to 18, 14, or 9 characters respectively (with an * in between for a total descriptor name of 22 characters). </comment> </field> - <field id="phone" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="phone" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Phone</label> <config_path>payment/braintree/descriptor_phone</config_path> <comment> The value in the phone number field of a customer's statement. Phone must be 10-14 characters and can only contain numbers, dashes, parentheses and periods. </comment> </field> - <field id="url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="url" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>URL</label> <config_path>payment/braintree/descriptor_url</config_path> <comment> diff --git a/app/code/Magento/CatalogInventory/etc/adminhtml/system.xml b/app/code/Magento/CatalogInventory/etc/adminhtml/system.xml index b9332575c96f7..08ed0a8f49470 100644 --- a/app/code/Magento/CatalogInventory/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogInventory/etc/adminhtml/system.xml @@ -41,13 +41,13 @@ <![CDATA[Please note that these settings apply to individual items in the cart, not to the entire cart.]]> </comment> <label>Product Stock Options</label> - <field id="manage_stock" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="manage_stock" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Manage Stock</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\CatalogInventory\Model\Config\Backend\Managestock</backend_model> <comment>Changing can take some time due to processing whole catalog.</comment> </field> - <field id="backorders" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="backorders" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Backorders</label> <source_model>Magento\CatalogInventory\Model\Source\Backorders</source_model> <backend_model>Magento\CatalogInventory\Model\Config\Backend\Backorders</backend_model> diff --git a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml index 39235511eaeec..b8f2863139e9b 100644 --- a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml @@ -27,7 +27,7 @@ <label>Maximum Query Length</label> <validate>validate-digits</validate> </field> - <field id="max_count_cacheable_search_terms" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="max_count_cacheable_search_terms" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Number of top search results to cache</label> <comment>Number of popular search terms to be cached for faster response. Use “0” to cache all results after a term is searched for the second time.</comment> <validate>validate-digits</validate> diff --git a/app/code/Magento/Cookie/etc/adminhtml/system.xml b/app/code/Magento/Cookie/etc/adminhtml/system.xml index 26c963ddba76d..9790410969055 100644 --- a/app/code/Magento/Cookie/etc/adminhtml/system.xml +++ b/app/code/Magento/Cookie/etc/adminhtml/system.xml @@ -22,7 +22,7 @@ <label>Cookie Domain</label> <backend_model>Magento\Cookie\Model\Config\Backend\Domain</backend_model> </field> - <field id="cookie_httponly" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="cookie_httponly" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Use HTTP Only</label> <comment> <![CDATA[<strong style="color:red">Warning</strong>: Do not set to "No". User security could be compromised.]]> diff --git a/app/code/Magento/Customer/etc/adminhtml/system.xml b/app/code/Magento/Customer/etc/adminhtml/system.xml index 31e968de14d99..86e5852d67aeb 100644 --- a/app/code/Magento/Customer/etc/adminhtml/system.xml +++ b/app/code/Magento/Customer/etc/adminhtml/system.xml @@ -26,7 +26,7 @@ </group> <group id="create_account" translate="label" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Create New Account Options</label> - <field id="auto_group_assign" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="auto_group_assign" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Enable Automatic Assignment to Customer Group</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -170,7 +170,7 @@ <comment>Use 0 to disable account locking.</comment> <frontend_class>required-entry validate-digits</frontend_class> </field> - <field id="lockout_threshold" translate="label" sortOrder="80" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="lockout_threshold" translate="label comment" sortOrder="80" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Lockout Time (minutes)</label> <comment>Account will be unlocked after provided time.</comment> <frontend_class>required-entry validate-digits</frontend_class> @@ -272,16 +272,16 @@ </group> <group id="address_templates" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Address Templates</label> - <field id="text" type="textarea" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="text" translate="label" type="textarea" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Text</label> </field> - <field id="oneline" type="textarea" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="oneline" translate="label" type="textarea" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Text One Line</label> </field> - <field id="html" type="textarea" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="html" translate="label" type="textarea" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>HTML</label> </field> - <field id="pdf" type="textarea" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="pdf" translate="label" type="textarea" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>PDF</label> </field> </group> diff --git a/app/code/Magento/Developer/etc/adminhtml/system.xml b/app/code/Magento/Developer/etc/adminhtml/system.xml index aae9913009837..4ebc45f1a2ca2 100644 --- a/app/code/Magento/Developer/etc/adminhtml/system.xml +++ b/app/code/Magento/Developer/etc/adminhtml/system.xml @@ -6,7 +6,7 @@ */--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <section id="dev" translate="label"> + <section id="dev"> <group id="front_end_development_workflow" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Frontend Development Workflow</label> <field id="type" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> @@ -25,7 +25,7 @@ <backend_model>Magento\Developer\Model\Config\Backend\AllowedIps</backend_model> </field> </group> - <group id="debug" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> + <group id="debug" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <field id="debug_logging" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Log to File</label> <comment>Not available in production mode.</comment> diff --git a/app/code/Magento/Dhl/etc/adminhtml/system.xml b/app/code/Magento/Dhl/etc/adminhtml/system.xml index c0f7e209ad61b..7694c6791f9f2 100644 --- a/app/code/Magento/Dhl/etc/adminhtml/system.xml +++ b/app/code/Magento/Dhl/etc/adminhtml/system.xml @@ -94,7 +94,7 @@ <field id="content_type">N</field> </depends> </field> - <field id="ready_time" type="text" sortOrder="180" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="ready_time" translate="label" type="text" sortOrder="180" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Ready time</label> <comment>Package ready time after order submission (in hours)</comment> </field> diff --git a/app/code/Magento/GoogleOptimizer/etc/adminhtml/system.xml b/app/code/Magento/GoogleOptimizer/etc/adminhtml/system.xml index f0c703b7693c7..3c91c30c5cfa6 100644 --- a/app/code/Magento/GoogleOptimizer/etc/adminhtml/system.xml +++ b/app/code/Magento/GoogleOptimizer/etc/adminhtml/system.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <section id="google" translate="label"> + <section id="google"> <group id="analytics"> <field id="experiments" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Enable Content Experiments</label> diff --git a/app/code/Magento/InstantPurchase/etc/adminhtml/system.xml b/app/code/Magento/InstantPurchase/etc/adminhtml/system.xml index 4b7a6029507b4..76785c023ed0b 100644 --- a/app/code/Magento/InstantPurchase/etc/adminhtml/system.xml +++ b/app/code/Magento/InstantPurchase/etc/adminhtml/system.xml @@ -10,7 +10,7 @@ <section id="sales"> <group id="instant_purchase" translate="label" type="text" sortOrder="200" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Instant Purchase</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="active" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Payment method with vault and instant purchase support should be enabled.</comment> diff --git a/app/code/Magento/Integration/etc/adminhtml/system.xml b/app/code/Magento/Integration/etc/adminhtml/system.xml index 97aec083e7abb..5abec8efbfdd6 100644 --- a/app/code/Magento/Integration/etc/adminhtml/system.xml +++ b/app/code/Magento/Integration/etc/adminhtml/system.xml @@ -13,48 +13,48 @@ <resource>Magento_Integration::config_oauth</resource> <group id="access_token_lifetime" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Access Token Expiration</label> - <field id="customer" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="customer" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Customer Token Lifetime (hours)</label> <comment>We will disable this feature if the value is empty.</comment> </field> - <field id="admin" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="admin" translate="label comment" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Admin Token Lifetime (hours)</label> <comment>We will disable this feature if the value is empty.</comment> </field> </group> <group id="cleanup" translate="label" type="text" sortOrder="300" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Cleanup Settings</label> - <field id="cleanup_probability" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="cleanup_probability" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Cleanup Probability</label> <comment>Integer. Launch cleanup in X OAuth requests. 0 (not recommended) - to disable cleanup</comment> </field> - <field id="expiration_period" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="expiration_period" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Expiration Period</label> <comment>Cleanup entries older than X minutes.</comment> </field> </group> <group id="consumer" translate="label" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Consumer Settings</label> - <field id="expiration_period" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="expiration_period" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Expiration Period</label> <comment>Consumer key/secret will expire if not used within X seconds after Oauth token exchange starts.</comment> </field> - <field id="post_maxredirects" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="post_maxredirects" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>OAuth consumer credentials HTTP Post maxredirects</label> <comment>Number of maximum redirects for OAuth consumer credentials Post request.</comment> </field> - <field id="post_timeout" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="post_timeout" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>OAuth consumer credentials HTTP Post timeout</label> <comment>Timeout for OAuth consumer credentials Post request within X seconds.</comment> </field> </group> <group id="authentication_lock" translate="label" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Authentication Locks</label> - <field id="max_failures_count" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="max_failures_count" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Maximum Login Failures to Lock Out Account</label> <comment>Maximum Number of authentication failures to lock out account.</comment> </field> - <field id="timeout" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="timeout" translate="label" type="text comment" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Lockout Time (seconds)</label> <comment>Period of time in seconds after which account will be unlocked.</comment> </field> diff --git a/app/code/Magento/LayeredNavigation/etc/adminhtml/system.xml b/app/code/Magento/LayeredNavigation/etc/adminhtml/system.xml index e9bf7933b94a9..de4637847456e 100644 --- a/app/code/Magento/LayeredNavigation/etc/adminhtml/system.xml +++ b/app/code/Magento/LayeredNavigation/etc/adminhtml/system.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <section id="catalog" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> + <section id="catalog" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="layered_navigation" translate="label" sortOrder="490" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Layered Navigation</label> <field id="display_product_count" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> diff --git a/app/code/Magento/MediaStorage/etc/adminhtml/system.xml b/app/code/Magento/MediaStorage/etc/adminhtml/system.xml index 09b6b23744053..d7244a5d4fd01 100644 --- a/app/code/Magento/MediaStorage/etc/adminhtml/system.xml +++ b/app/code/Magento/MediaStorage/etc/adminhtml/system.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <section id="system" translate="label" type="text" sortOrder="900" showInDefault="1" showInWebsite="1" showInStore="1"> + <section id="system" type="text" sortOrder="900" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="media_storage_configuration" translate="label" type="text" sortOrder="900" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Storage Configuration for Media</label> <field id="media_storage" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> diff --git a/app/code/Magento/OfflinePayments/etc/adminhtml/system.xml b/app/code/Magento/OfflinePayments/etc/adminhtml/system.xml index b47bd8f749040..89cc4d0986a00 100644 --- a/app/code/Magento/OfflinePayments/etc/adminhtml/system.xml +++ b/app/code/Magento/OfflinePayments/etc/adminhtml/system.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <section id="payment" translate="label" type="text" sortOrder="400" showInDefault="1" showInWebsite="1" showInStore="1"> + <section id="payment" type="text" sortOrder="400" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="checkmo" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Check / Money Order</label> <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> diff --git a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml index 306aac1769913..4db5f489aa4a2 100644 --- a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml +++ b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <section id="carriers" translate="label" type="text" sortOrder="320" showInDefault="1" showInWebsite="1" showInStore="1"> + <section id="carriers" type="text" sortOrder="320" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="flatrate" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Flat Rate</label> <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> diff --git a/app/code/Magento/PageCache/etc/adminhtml/system.xml b/app/code/Magento/PageCache/etc/adminhtml/system.xml index 1d6c0d890737b..5055b17956819 100644 --- a/app/code/Magento/PageCache/etc/adminhtml/system.xml +++ b/app/code/Magento/PageCache/etc/adminhtml/system.xml @@ -49,7 +49,7 @@ <field id="caching_application">1</field> </depends> </field> - <field id="export_button_version4" type="button" sortOrder="35" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="export_button_version4" translate="label" type="button" sortOrder="35" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Export Configuration</label> <frontend_model>Magento\PageCache\Block\System\Config\Form\Field\Export\Varnish4</frontend_model> <depends> diff --git a/app/code/Magento/Paypal/etc/adminhtml/system.xml b/app/code/Magento/Paypal/etc/adminhtml/system.xml index c1ff4c9b1c6ca..26ec9b3152e4b 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system.xml @@ -77,7 +77,7 @@ <group id="configuration_details"> <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-pro.html</comment> </group> - <group id="paypal_payflow_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> + <group id="paypal_payflow_required" showInDefault="1" showInWebsite="1" sortOrder="10"> <field id="enable_paypal_payflow"> <attribute type="shared">0</attribute> <config_path>payment/paypal_payment_pro/active</config_path> @@ -90,7 +90,7 @@ <label>Basic Settings - PayPal Payments Pro</label> </group> </group> - <group id="wps_express" extends="payment_all_paypal/express_checkout"> + <group id="wps_express" translate="label comment" extends="payment_all_paypal/express_checkout"> <label>Payments Standard</label> <comment>Accept credit card and PayPal payments securely.</comment> <attribute type="activity_path">payment/wps_express/active</attribute> @@ -110,7 +110,7 @@ <config_path>payment/wps_express_bml/active</config_path> </field> </group> - <group id="settings_ec"> + <group id="settings_ec" translate="label"> <label>Basic Settings - PayPal Website Payments Standard</label> </group> </group> @@ -124,7 +124,7 @@ <group id="payflow_link_us" extends="payment_all_paypal/payflow_link"/> </group> </section> - <section id="payment_gb" extends="payment" showInDefault="0" showInWebsite="0" showInStore="0"> + <section id="payment_gb" extends="payment" showInDefault="0" showInWebsite="0" showInStore="0"> <group id="paypal_alternative_payment_methods" sortOrder="5" showInDefault="0" showInWebsite="0" showInStore="0"> <group id="express_checkout_gb" translate="label comment" extends="payment_all_paypal/express_checkout" showInDefault="1" showInWebsite="1" showInStore="1"> <label>PayPal Express Checkout</label> @@ -158,7 +158,7 @@ </group> </group> <include path="Magento_Paypal::system/payments_pro_hosted_solution_with_express_checkout.xml"/> - <group id="wps_express" extends="payment_all_paypal/express_checkout" sortOrder="50"> + <group id="wps_express" translate="label comment" extends="payment_all_paypal/express_checkout" sortOrder="50"> <label>Website Payments Standard</label> <comment>Accept credit card and PayPal payments securely.</comment> <attribute type="activity_path">payment/wps_express/active</attribute> @@ -166,7 +166,7 @@ <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-standard.html</comment> </group> <group id="express_checkout_required"> - <group id="express_checkout_required_express_checkout"> + <group id="express_checkout_required_express_checkout" translate="label"> <label>Website Payments Standard</label> </group> <field id="enable_in_context_checkout" showInDefault="0" showInWebsite="0"/> @@ -178,7 +178,7 @@ <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> <group id="advertise_bml" showInDefault="0" showInWebsite="0"/> </group> - <group id="settings_ec"> + <group id="settings_ec" translate="label"> <label>Basic Settings - PayPal Website Payments Standard</label> </group> </group> @@ -227,7 +227,7 @@ <comment>Choose a secure bundled payment solution for your business.</comment> <help_url>https://www.paypal-marketing.com/emarketing/partner/na/merchantlineup/home.page#mainTab=checkoutlineup&subTab=newlineup</help_url> <attribute type="displayIn">other_paypal_payment_solutions</attribute> - <group id="wps_other" extends="payment_all_paypal/express_checkout" showInDefault="1" showInWebsite="1" showInStore="1"> + <group id="wps_other" translate="label comment" extends="payment_all_paypal/express_checkout" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Website Payments Standard</label> <fieldset_css>complex</fieldset_css> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> @@ -237,7 +237,7 @@ <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-standard.html</comment> </group> <group id="express_checkout_required"> - <group id="express_checkout_required_express_checkout"> + <group id="express_checkout_required_express_checkout" translate="label"> <label>Website Payments Standard</label> </group> <field id="enable_in_context_checkout" showInDefault="0" showInWebsite="0"/> @@ -271,7 +271,7 @@ <group id="paypal_group_all_in_one"> <group id="wps_other" sortOrder="20"/> </group> - <group id="paypal_payment_gateways" showInDefault="1" showInWebsite="1" showInStore="1"> + <group id="paypal_payment_gateways" translate="label" showInDefault="1" showInWebsite="1" showInStore="1"> <fieldset_css>complex paypal-other-section paypal-gateways-section</fieldset_css> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> <label><![CDATA[PayPal Payment Gateways <i>Process payments using your own internet merchant account.</i>]]></label> diff --git a/app/code/Magento/Sales/etc/adminhtml/system.xml b/app/code/Magento/Sales/etc/adminhtml/system.xml index 9d6d11d56c81f..ccfd9c2ac7bfd 100644 --- a/app/code/Magento/Sales/etc/adminhtml/system.xml +++ b/app/code/Magento/Sales/etc/adminhtml/system.xml @@ -393,7 +393,7 @@ </group> </section> <section id="rss"> - <group id="order" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> + <group id="order" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Order</label> <field id="status" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Customer Order Status Notification</label> @@ -402,7 +402,7 @@ </group> </section> <section id="dev"> - <group id="grid" type="text" sortOrder="131" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="grid" translate="label" type="text" sortOrder="131" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Grid Settings</label> <field id="async_indexing" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Asynchronous indexing</label> diff --git a/app/code/Magento/Signifyd/etc/adminhtml/system.xml b/app/code/Magento/Signifyd/etc/adminhtml/system.xml index 71f5916ca5325..2dd75d2d91e5b 100644 --- a/app/code/Magento/Signifyd/etc/adminhtml/system.xml +++ b/app/code/Magento/Signifyd/etc/adminhtml/system.xml @@ -11,7 +11,7 @@ <label>Fraud Protection</label> <tab>sales</tab> <resource>Magento_Sales::fraud_protection</resource> - <group id="signifyd" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="signifyd" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> <fieldset_css>signifyd-logo-header</fieldset_css> <group id="about" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="0"> <frontend_model>Magento\Signifyd\Block\Adminhtml\System\Config\Fieldset\Info</frontend_model> @@ -52,7 +52,7 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>fraud_protection/signifyd/debug</config_path> </field> - <field id="webhook_url" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="webhook_url" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Webhook URL</label> <comment><![CDATA[Your webhook URL will be used to <a href="https://app.signifyd.com/settings/notifications" target="_blank">configure</a> a guarantee completed webhook in Signifyd. Webhooks are used to sync Signifyd`s guarantee decisions back to Magento.]]></comment> <attribute type="handler_url">signifyd/webhooks/handler</attribute> diff --git a/app/code/Magento/Tax/etc/adminhtml/system.xml b/app/code/Magento/Tax/etc/adminhtml/system.xml index c03a8aa44bf7b..7fc1744b8e27e 100644 --- a/app/code/Magento/Tax/etc/adminhtml/system.xml +++ b/app/code/Magento/Tax/etc/adminhtml/system.xml @@ -62,7 +62,7 @@ <backend_model>Magento\Tax\Model\Config\Notification</backend_model> <comment>Warning: To apply the discount on prices including tax and apply the tax after discount, set Catalog Prices to “Including Tax”.</comment> </field> - <field id="apply_tax_on" translate="label comment" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="apply_tax_on" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Apply Tax On</label> <source_model>Magento\Tax\Model\Config\Source\Apply\On</source_model> </field> diff --git a/app/code/Magento/Translation/etc/adminhtml/system.xml b/app/code/Magento/Translation/etc/adminhtml/system.xml index 8c3cdc5c39916..dbce9f148b412 100644 --- a/app/code/Magento/Translation/etc/adminhtml/system.xml +++ b/app/code/Magento/Translation/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <system> <section id="dev"> <group id="js"> - <field id="translate_strategy" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="translate_strategy" translate="label" type="select comment" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Translation Strategy</label> <source_model>Magento\Translation\Model\Js\Config\Source\Strategy</source_model> <comment>Please put your store into maintenance mode and redeploy static files after changing strategy</comment> diff --git a/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml b/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml index c38ea402718e3..d6f40f5ac2023 100644 --- a/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml +++ b/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml @@ -8,7 +8,7 @@ <section id="webapi" type="text" sortOrder="102" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="webapisecurity" translate="label" type="text" sortOrder="250" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Web API Security</label> - <field id="allow_insecure" translate="label" type="select comment" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="allow_insecure" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Allow Anonymous Guest Access</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>This feature applies only to CMS, Catalog and Store APIs. Please consult your developers for details on potential security risks.</comment> From 1c982d31d8c812cb689b2651667ee0122255e774 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Wed, 13 Jun 2018 14:13:56 +0300 Subject: [PATCH 1108/1171] Added translation comment tag --- app/code/Magento/Dhl/etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Dhl/etc/adminhtml/system.xml b/app/code/Magento/Dhl/etc/adminhtml/system.xml index 7694c6791f9f2..91ed6c6568a70 100644 --- a/app/code/Magento/Dhl/etc/adminhtml/system.xml +++ b/app/code/Magento/Dhl/etc/adminhtml/system.xml @@ -94,7 +94,7 @@ <field id="content_type">N</field> </depends> </field> - <field id="ready_time" translate="label" type="text" sortOrder="180" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="ready_time" translate="label comment" type="text" sortOrder="180" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Ready time</label> <comment>Package ready time after order submission (in hours)</comment> </field> From ea5865af56495e913af430629c550a2521e06c2b Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 4 Jul 2018 09:36:33 +0300 Subject: [PATCH 1109/1171] Fixed \Magento\Backend\Test\TestCase\LoginAfterJSMinificationTest fail --- app/code/Magento/Translation/etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Translation/etc/adminhtml/system.xml b/app/code/Magento/Translation/etc/adminhtml/system.xml index dbce9f148b412..ab854f8a4db52 100644 --- a/app/code/Magento/Translation/etc/adminhtml/system.xml +++ b/app/code/Magento/Translation/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <system> <section id="dev"> <group id="js"> - <field id="translate_strategy" translate="label" type="select comment" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="translate_strategy" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Translation Strategy</label> <source_model>Magento\Translation\Model\Js\Config\Source\Strategy</source_model> <comment>Please put your store into maintenance mode and redeploy static files after changing strategy</comment> From 49027eddc2951241039e016b716b09d006ace20b Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Wed, 22 Aug 2018 15:57:39 +0400 Subject: [PATCH 1110/1171] MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added - Updated automated test. --- .../Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml index 0e2ac68eb180b..38a9cd0467151 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml @@ -35,7 +35,7 @@ <!-- "Add to Cart" any product--> <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontProductPageSection.errorMsg}}" time="30" stepKey="assertErrorMessage"/> + <see stepKey="assertErrorMessage" userInput="Your session has expired"/> <after> <!--Delete created product--> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> From 934fedc7234d5036b4a9672014486c0d0be8cc4a Mon Sep 17 00:00:00 2001 From: Jisse Reitsma <info@yireo.com> Date: Sat, 26 May 2018 19:41:17 +0300 Subject: [PATCH 1111/1171] Enhancements to module:status command --- .../Console/Command/ModuleStatusCommand.php | 107 ++++++++++++++++-- 1 file changed, 96 insertions(+), 11 deletions(-) diff --git a/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php b/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php index 85af8f3caeb1a..7afbaac803de9 100644 --- a/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php +++ b/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php @@ -3,11 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Console\Command; +use Magento\Framework\Module\FullModuleList; +use Magento\Framework\Module\ModuleList; use Magento\Setup\Model\ObjectManagerProvider; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; /** * Command for displaying status of modules @@ -38,7 +42,10 @@ public function __construct(ObjectManagerProvider $objectManagerProvider) protected function configure() { $this->setName('module:status') - ->setDescription('Displays status of modules'); + ->setDescription('Displays status of modules') + ->addArgument('module', InputArgument::OPTIONAL, 'Optional module name') + ->addOption('enabled', null, null, 'Print only enabled modules') + ->addOption('disabled', null, null, 'Print only disabled modules'); parent::configure(); } @@ -47,24 +54,102 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $moduleList = $this->objectManagerProvider->get()->create(\Magento\Framework\Module\ModuleList::class); + $moduleName = (string)$input->getArgument('module'); + if ($moduleName) { + return $this->showSpecificModule($moduleName, $output); + } + + $onlyEnabled = $input->getOption('enabled'); + if ($onlyEnabled) { + return $this->showEnabledModules($output); + } + + $onlyDisabled = $input->getOption('disabled'); + if ($onlyDisabled) { + return $this->showDisabledModules($output); + } + $output->writeln('<info>List of enabled modules:</info>'); - $enabledModules = $moduleList->getNames(); - if (count($enabledModules) === 0) { + $this->showEnabledModules($output); + $output->writeln(''); + + $output->writeln("<info>List of disabled modules:</info>"); + $this->showDisabledModules($output); + $output->writeln(''); + } + + /** + * @param string $moduleName + * @param OutputInterface $output + */ + private function showSpecificModule(string $moduleName, OutputInterface $output) + { + $allModules = $this->getAllModules(); + if (!in_array($moduleName, $allModules->getNames())) { + $output->writeln('<error>Module does not exist</error>'); + return; + } + + $enabledModules = $this->getEnabledModules(); + if (in_array($moduleName, $enabledModules->getNames())) { + $output->writeln('<info>Module is enabled</info>'); + return; + } + + $output->writeln('<info>Module is disabled</info>'); + } + + /** + * @param OutputInterface $output + */ + private function showEnabledModules(OutputInterface $output) + { + $enabledModules = $this->getEnabledModules(); + $enabledModuleNames = $enabledModules->getNames(); + if (count($enabledModuleNames) === 0) { $output->writeln('None'); } else { - $output->writeln(join("\n", $enabledModules)); + $output->writeln(join("\n", $enabledModuleNames)); } - $output->writeln(''); + } - $fullModuleList = $this->objectManagerProvider->get()->create(\Magento\Framework\Module\FullModuleList::class); - $output->writeln("<info>List of disabled modules:</info>"); - $disabledModules = array_diff($fullModuleList->getNames(), $enabledModules); - if (count($disabledModules) === 0) { + /** + * @param OutputInterface $output + */ + private function showDisabledModules(OutputInterface $output) + { + $disabledModuleNames = $this->getDisabledModuleNames(); + if (count($disabledModuleNames) === 0) { $output->writeln('None'); } else { - $output->writeln(join("\n", $disabledModules)); + $output->writeln(join("\n", $disabledModuleNames)); } return \Magento\Framework\Console\Cli::RETURN_SUCCESS; } + + /** + * @return FullModuleList + */ + private function getAllModules(): FullModuleList + { + return $this->objectManagerProvider->get()->create(FullModuleList::class); + } + + /** + * @return ModuleList + */ + private function getEnabledModules(): ModuleList + { + return $this->objectManagerProvider->get()->create(ModuleList::class); + } + + /** + * @return array + */ + private function getDisabledModuleNames(): array + { + $fullModuleList = $this->getAllModules(); + $enabledModules = $this->getEnabledModules(); + return array_diff($fullModuleList->getNames(), $enabledModules->getNames()); + } } From 0198f967d0ba130d28380d28817689fb08724630 Mon Sep 17 00:00:00 2001 From: Jisse Reitsma <info@yireo.com> Date: Sat, 26 May 2018 19:44:31 +0300 Subject: [PATCH 1112/1171] Add strict-modes --- setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php b/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php index 7afbaac803de9..c30d6d7d2a97f 100644 --- a/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php +++ b/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Setup\Console\Command; use Magento\Framework\Module\FullModuleList; From 3c74589177d57f29b3dc528a830c8c49c546b18b Mon Sep 17 00:00:00 2001 From: Jisse Reitsma <jisse@yireo.com> Date: Tue, 5 Jun 2018 20:44:01 +0300 Subject: [PATCH 1113/1171] Add RETURN_FAILURED return if things fail --- .../Console/Command/ModuleStatusCommand.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php b/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php index c30d6d7d2a97f..b3669ba7b5c8f 100644 --- a/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php +++ b/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php @@ -11,6 +11,7 @@ use Magento\Framework\Module\FullModuleList; use Magento\Framework\Module\ModuleList; use Magento\Setup\Model\ObjectManagerProvider; +use Magento\Framework\Console\Cli; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputArgument; @@ -89,13 +90,13 @@ private function showSpecificModule(string $moduleName, OutputInterface $output) $allModules = $this->getAllModules(); if (!in_array($moduleName, $allModules->getNames())) { $output->writeln('<error>Module does not exist</error>'); - return; + return Cli::RETURN_FAILURE; } $enabledModules = $this->getEnabledModules(); if (in_array($moduleName, $enabledModules->getNames())) { $output->writeln('<info>Module is enabled</info>'); - return; + return Cli::RETURN_FAILURE; } $output->writeln('<info>Module is disabled</info>'); @@ -110,9 +111,10 @@ private function showEnabledModules(OutputInterface $output) $enabledModuleNames = $enabledModules->getNames(); if (count($enabledModuleNames) === 0) { $output->writeln('None'); - } else { - $output->writeln(join("\n", $enabledModuleNames)); + return Cli::RETURN_FAILURE; } + + $output->writeln(join("\n", $enabledModuleNames)); } /** @@ -123,10 +125,10 @@ private function showDisabledModules(OutputInterface $output) $disabledModuleNames = $this->getDisabledModuleNames(); if (count($disabledModuleNames) === 0) { $output->writeln('None'); - } else { - $output->writeln(join("\n", $disabledModuleNames)); + return Cli::RETURN_FAILURE; } - return \Magento\Framework\Console\Cli::RETURN_SUCCESS; + + $output->writeln(join("\n", $disabledModuleNames)); } /** From 245d4efc4c0ccd0b0fe1de4c099634d01c742869 Mon Sep 17 00:00:00 2001 From: Jisse Reitsma <info@yireo.com> Date: Wed, 22 Aug 2018 15:37:50 +0300 Subject: [PATCH 1114/1171] Add RETURN_SUCCESS return --- .../src/Magento/Setup/Console/Command/ModuleStatusCommand.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php b/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php index b3669ba7b5c8f..65fc265a64ec8 100644 --- a/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php +++ b/setup/src/Magento/Setup/Console/Command/ModuleStatusCommand.php @@ -100,6 +100,7 @@ private function showSpecificModule(string $moduleName, OutputInterface $output) } $output->writeln('<info>Module is disabled</info>'); + return \Magento\Framework\Console\Cli::RETURN_SUCCESS; } /** @@ -115,6 +116,7 @@ private function showEnabledModules(OutputInterface $output) } $output->writeln(join("\n", $enabledModuleNames)); + return \Magento\Framework\Console\Cli::RETURN_SUCCESS; } /** @@ -129,6 +131,7 @@ private function showDisabledModules(OutputInterface $output) } $output->writeln(join("\n", $disabledModuleNames)); + return \Magento\Framework\Console\Cli::RETURN_SUCCESS; } /** From b92eefb6b13fc38982ac438c6aec6021dcd56810 Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Wed, 22 Aug 2018 15:53:42 +0300 Subject: [PATCH 1115/1171] Removed double occurrence of 'it' from sentences. --- app/code/Magento/Paypal/i18n/en_US.csv | 2 +- .../Tinymce3/view/base/web/tiny_mce/classes/ControlManager.js | 2 +- .../Tinymce3/view/base/web/tiny_mce/classes/html/Styles.js | 2 +- .../base/web/tiny_mce/plugins/autosave/editor_plugin_src.js | 4 ++-- .../Tinymce3/view/base/web/tiny_mce/tiny_mce_jquery_src.js | 2 +- .../Tinymce3/view/base/web/tiny_mce/tiny_mce_prototype_src.js | 2 +- .../Magento/Tinymce3/view/base/web/tiny_mce/tiny_mce_src.js | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Paypal/i18n/en_US.csv b/app/code/Magento/Paypal/i18n/en_US.csv index e47264878f16e..c3f3e38fa03b4 100644 --- a/app/code/Magento/Paypal/i18n/en_US.csv +++ b/app/code/Magento/Paypal/i18n/en_US.csv @@ -2,7 +2,7 @@ <a href=""https://financing.paypal.com/ppfinportal/content/whyUseFinancing"" target=""_blank"">Why Advertise Financing?</a><br/> <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. - Use PayPal’ s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. + Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% or more. <a href=""https://financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. "," diff --git a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/classes/ControlManager.js b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/classes/ControlManager.js index 51fa311525862..784042988c0ef 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/classes/ControlManager.js +++ b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/classes/ControlManager.js @@ -45,7 +45,7 @@ }, /** - * Returns a control by id or undefined it it wasn't found. + * Returns a control by id or undefined it wasn't found. * * @method get * @param {String} id Control instance name. diff --git a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/classes/html/Styles.js b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/classes/html/Styles.js index edf1e13a4e3bc..60c5565d72a99 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/classes/html/Styles.js +++ b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/classes/html/Styles.js @@ -79,7 +79,7 @@ tinymce.html.Styles = function(settings, schema) { function compress(prefix, suffix) { var top, right, bottom, left; - // Get values and check it it needs compressing + // Get values and check it needs compressing top = styles[prefix + '-top' + suffix]; if (!top) return; diff --git a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/autosave/editor_plugin_src.js b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/autosave/editor_plugin_src.js index e215f078c4b02..c147e4d8f87d5 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/autosave/editor_plugin_src.js +++ b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/autosave/editor_plugin_src.js @@ -20,14 +20,14 @@ * 1. localStorage - A new feature of HTML 5, localStorage can store megabytes of data per domain * on the client computer. Data stored in the localStorage area has no expiration date, so we must * manage expiring the data ourselves. localStorage is fully supported by IE8, and it is supposed - * to be working in Firefox 3 and Safari 3.2, but in reality is flaky in those browsers. As + * to be working in Firefox 3 and Safari 3.2, but in reality it is flaky in those browsers. As * HTML 5 gets wider support, the AutoSave plugin will use it automatically. In Windows Vista/7, * localStorage is stored in the following folder: * C:\Users\[username]\AppData\Local\Microsoft\Internet Explorer\DOMStore\[tempFolder] * * 2. sessionStorage - A new feature of HTML 5, sessionStorage works similarly to localStorage, * except it is designed to expire after a certain amount of time. Because the specification - * around expiration date/time is very loosely-described, it is preferrable to use locaStorage and + * around expiration date/time is very loosely-described, it is preferable to use locaStorage and * manage the expiration ourselves. sessionStorage has similar storage characteristics to * localStorage, although it seems to have better support by Firefox 3 at the moment. (That will * certainly change as Firefox continues getting better at HTML 5 adoption.) diff --git a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/tiny_mce_jquery_src.js b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/tiny_mce_jquery_src.js index 2daf1620a918e..b3d77f6ce3139 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/tiny_mce_jquery_src.js +++ b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/tiny_mce_jquery_src.js @@ -1731,7 +1731,7 @@ tinymce.html.Styles = function(settings, schema) { function compress(prefix, suffix) { var top, right, bottom, left; - // Get values and check it it needs compressing + // Get values and check it needs compressing top = styles[prefix + '-top' + suffix]; if (!top) return; diff --git a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/tiny_mce_prototype_src.js b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/tiny_mce_prototype_src.js index 44b3010b0adfd..daf2ad4e71bac 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/tiny_mce_prototype_src.js +++ b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/tiny_mce_prototype_src.js @@ -1483,7 +1483,7 @@ tinymce.html.Styles = function(settings, schema) { function compress(prefix, suffix) { var top, right, bottom, left; - // Get values and check it it needs compressing + // Get values and check it needs compressing top = styles[prefix + '-top' + suffix]; if (!top) return; diff --git a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/tiny_mce_src.js b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/tiny_mce_src.js index 46ba27e60f419..2634633d8eee5 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/tiny_mce_src.js +++ b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/tiny_mce_src.js @@ -1456,7 +1456,7 @@ tinymce.html.Styles = function(settings, schema) { function compress(prefix, suffix) { var top, right, bottom, left; - // Get values and check it it needs compressing + // Get values and check it needs compressing top = styles[prefix + '-top' + suffix]; if (!top) return; From 1589c7985b691c00e75c21a7ebf705391afd511e Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Tue, 19 Jun 2018 09:36:53 +0300 Subject: [PATCH 1116/1171] Correct spelling mistakes in Model and library files. --- lib/internal/Magento/Framework/Code/GeneratedFiles.php | 2 +- lib/web/modernizr/modernizr.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Code/GeneratedFiles.php b/lib/internal/Magento/Framework/Code/GeneratedFiles.php index f660b3d698293..bc44b361c57ea 100644 --- a/lib/internal/Magento/Framework/Code/GeneratedFiles.php +++ b/lib/internal/Magento/Framework/Code/GeneratedFiles.php @@ -180,7 +180,7 @@ private function disableAllCacheTypes() } /** - * Enables apppropriate cache types in app/etc/env.php based on the passed in $cacheTypes array + * Enables appropriate cache types in app/etc/env.php based on the passed in $cacheTypes array * TODO: to be removed in scope of MAGETWO-53476 * * @param string[] $cacheTypes diff --git a/lib/web/modernizr/modernizr.js b/lib/web/modernizr/modernizr.js index d7ddc86f63cae..8c826fa18c582 100644 --- a/lib/web/modernizr/modernizr.js +++ b/lib/web/modernizr/modernizr.js @@ -169,7 +169,7 @@ window.Modernizr = (function( window, document, undefined ) { // isEventSupported determines if a given element supports the given event // kangax.github.com/iseventsupported/ // - // The following results are known incorrects: + // The following results are known incorrect: // Modernizr.hasEvent("webkitTransitionEnd", elem) // false negative // Modernizr.hasEvent("textInput") // in Webkit. github.com/Modernizr/Modernizr/issues/333 // ... From fda50209019d744970f839b08cd48fdc675bb375 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Fri, 18 May 2018 08:40:45 +0300 Subject: [PATCH 1117/1171] Fix typo in DeploymentConfig class --- lib/internal/Magento/Framework/App/DeploymentConfig.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig.php b/lib/internal/Magento/Framework/App/DeploymentConfig.php index f83f89ee4caeb..615c295675adc 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig.php @@ -117,7 +117,7 @@ public function resetData() } /** - * Check if data from deploy files is avaiable + * Check if data from deploy files is available * * @return bool * @since 100.1.3 From 72dbca307d71bcd72b7878a0f85e014fe8fa4157 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 22 Aug 2018 09:26:02 -0500 Subject: [PATCH 1118/1171] MQE-1217: Deliver MFTF 2.3.5 - Fix MFTF tests --- .../Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml index 80351c9d97520..e329239ff46e4 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml @@ -23,7 +23,7 @@ <amOnPage url="admin/admin/system_config/edit/section/cms/" stepKey="navigateToConfigurationPage" /> <waitForPageLoad stepKey="wait3"/> <conditionalClick stepKey="expandWYSIWYGOptions" selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.CheckIfTabExpand}}" visible="true" /> - <waitForElementVisible selector="{{ContentManagementSection.EnableWYSIWYG}}" stepKey="waitForEnableWYSIWYGDropdown2" /> + <waitForElementVisible selector="{{ContentManagementSection.EnableWYSIWYG}}" stepKey="waitForEnableWYSIWYGDropdown2" time="30"/> <uncheckOption selector="{{ContentManagementSection.EnableSystemValue}}" stepKey="uncheckUseSystemValue"/> <selectOption selector="{{ContentManagementSection.EnableWYSIWYG}}" userInput="Disabled Completely" stepKey="selectOption2"/> <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions" /> From 67dd85820841cb01cbbbf05bdc8a973ca6ae0f21 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 22 Aug 2018 10:21:13 -0500 Subject: [PATCH 1119/1171] MAGETWO-94021: Problems with adding products in wish list - extending span to display as block to occupy the whole li element so users can block anywhere and the click event can be trigered properly, before it was too small and you had to click on the span text itself --- .../web/css/source/_module.less | 7 +++++++ .../web/css/source/_module.less | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less index 6baa2432ff035..145dc10263fb1 100644 --- a/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less @@ -35,12 +35,19 @@ .items { text-align: left; .item { + > span { + display: block; + padding: 5px 5px 5px 23px; + } &:last-child { &:hover { .lib-css(background, @dropdown-list-item__hover); } } } + li { + padding: 0; + } } .table-comparison &, diff --git a/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less index da284eab8f49e..6ab7a8e47a174 100644 --- a/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less @@ -40,6 +40,16 @@ .items { padding: 6px 0; text-align: left; + .item { + > span { + display: block; + padding: 5px 5px 5px 23px; + } + + } + li { + padding: 0; + } } > .action { From cba6c39197568d8255541b9871e53582d72f1b60 Mon Sep 17 00:00:00 2001 From: Sunil Patel <patelsunil42@gmail.com> Date: Wed, 22 Aug 2018 18:44:38 +0300 Subject: [PATCH 1120/1171] Misleading data-container in product list --- .../Magento/Catalog/view/frontend/templates/product/list.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml index f7799b30436be..e970ade6cee96 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml @@ -46,7 +46,7 @@ $_helper = $this->helper('Magento\Catalog\Helper\Output'); <?php /** @var $_product \Magento\Catalog\Model\Product */ ?> <?php foreach ($_productCollection as $_product): ?> <li class="item product product-item"> - <div class="product-item-info" data-container="product-grid"> + <div class="product-item-info" data-container="product-<?= /* @escapeNotVerified */ $viewMode ?>"> <?php $productImage = $block->getImage($_product, $imageDisplayArea); if ($pos != null) { From a3c361a25d2747971e728c231df1026e29e93b0d Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Thu, 23 Aug 2018 00:31:29 +0530 Subject: [PATCH 1121/1171] Fixed a couple of spelling mistakes --- .../Signifyd/Test/Unit/Controller/Webhooks/HandlerTest.php | 2 +- .../Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Signifyd/Test/Unit/Controller/Webhooks/HandlerTest.php b/app/code/Magento/Signifyd/Test/Unit/Controller/Webhooks/HandlerTest.php index 1a8cfdc703247..8b98be338b973 100644 --- a/app/code/Magento/Signifyd/Test/Unit/Controller/Webhooks/HandlerTest.php +++ b/app/code/Magento/Signifyd/Test/Unit/Controller/Webhooks/HandlerTest.php @@ -140,7 +140,7 @@ protected function setUp() } /** - * Successfull case + * Successful case */ public function testExecuteSuccessfully() { diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php index 0ca9f6846f77d..98fcdc626a971 100644 --- a/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php +++ b/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php @@ -315,7 +315,7 @@ public function testAsymmetricRollBackSuccess() } /** - * Test successfull nested transaction + * Test successful nested transaction */ public function testNestedTransactionCommitSuccess() { @@ -337,7 +337,7 @@ public function testNestedTransactionCommitSuccess() } /** - * Test successfull nested transaction + * Test successful nested transaction */ public function testNestedTransactionRollBackSuccess() { @@ -359,7 +359,7 @@ public function testNestedTransactionRollBackSuccess() } /** - * Test successfull nested transaction + * Test successful nested transaction */ public function testNestedTransactionLastRollBack() { From 98c41f20529d95139e87b973bd139317762421d9 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Tue, 21 Aug 2018 08:57:27 -0500 Subject: [PATCH 1122/1171] MAGETWO-94226: Catalog Products, Orders, All Customer page doesnt load (Continuous spinner) on cloud starter --- lib/web/mage/requirejs/resolver.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/web/mage/requirejs/resolver.js b/lib/web/mage/requirejs/resolver.js index 6927a3205d94d..6ff270f5c1a20 100644 --- a/lib/web/mage/requirejs/resolver.js +++ b/lib/web/mage/requirejs/resolver.js @@ -34,7 +34,11 @@ define([ * @return {Boolean} */ function isRegistered(module) { - return registry[module.id]; + if (registry.hasOwnProperty(module.id)) { + return registry[module.id].inited || registry[module.id].error; + } + + return false; } /** From 812e97ef45c300cbf8e2c0a1fcc9350798ab1500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Lavorel?= <aurelien@lavoweb.net> Date: Thu, 23 Aug 2018 11:23:21 +0200 Subject: [PATCH 1123/1171] [Forwardport] 16570 enhance performance on large catalog --- .../FillQuoteAddressIdInSalesOrderAddress.php | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php b/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php index 0ad2245a6287e..2716e860243bf 100644 --- a/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php +++ b/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php @@ -15,11 +15,12 @@ use Magento\Sales\Setup\SalesSetupFactory; use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Framework\Setup\Patch\PatchVersionInterface; +use Magento\Framework\Setup\ModuleDataSetupInterface; class FillQuoteAddressIdInSalesOrderAddress implements DataPatchInterface, PatchVersionInterface { /** - * @var \Magento\Framework\Setup\ModuleDataSetupInterface + * @var ModuleDataSetupInterface */ private $moduleDataSetup; @@ -55,10 +56,10 @@ class FillQuoteAddressIdInSalesOrderAddress implements DataPatchInterface, Patch /** * PatchInitial constructor. - * @param \Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup + * @param ModuleDataSetupInterface $moduleDataSetup */ public function __construct( - \Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup, + ModuleDataSetupInterface $moduleDataSetup, SalesSetupFactory $salesSetupFactory, State $state, Config $eavConfig, @@ -82,39 +83,41 @@ public function apply() { $this->state->emulateAreaCode( \Magento\Backend\App\Area\FrontNameResolver::AREA_CODE, - [$this, 'fillQuoteAddressIdInSalesOrderAddress'] + [$this, 'fillQuoteAddressIdInSalesOrderAddress'], + [$this->moduleDataSetup] ); $this->eavConfig->clear(); } /** * Fill quote_address_id in table sales_order_address if it is empty. + * + * @param ModuleDataSetupInterface $setup */ - public function fillQuoteAddressIdInSalesOrderAddress() + public function fillQuoteAddressIdInSalesOrderAddress(ModuleDataSetupInterface $setup) { - $addressCollection = $this->addressCollectionFactory->create(); - /** @var \Magento\Sales\Model\Order\Address $orderAddress */ - foreach ($addressCollection as $orderAddress) { - if (!$orderAddress->getData('quote_address_id')) { - $orderId = $orderAddress->getParentId(); - $addressType = $orderAddress->getAddressType(); - - /** @var \Magento\Sales\Model\Order $order */ - $order = $this->orderFactory->create()->load($orderId); - $quoteId = $order->getQuoteId(); - $quote = $this->quoteFactory->create()->load($quoteId); - - if ($addressType == \Magento\Sales\Model\Order\Address::TYPE_SHIPPING) { - $quoteAddressId = $quote->getShippingAddress()->getId(); - $orderAddress->setData('quote_address_id', $quoteAddressId); - } elseif ($addressType == \Magento\Sales\Model\Order\Address::TYPE_BILLING) { - $quoteAddressId = $quote->getBillingAddress()->getId(); - $orderAddress->setData('quote_address_id', $quoteAddressId); - } - - $orderAddress->save(); - } - } + $addressTable = $setup->getTable('sales_order_address'); + $updateOrderAddress = $setup->getConnection() + ->select() + ->joinInner( + ['sales_order' => $setup->getTable('sales_order')], + $addressTable . '.parent_id = sales_order.entity_id', + ['quote_address_id' => 'quote_address.address_id'] + ) + ->joinInner( + ['quote_address' => $setup->getTable('quote_address')], + 'sales_order.quote_id = quote_address.quote_id + AND ' . $addressTable . '.address_type = quote_address.address_type', + [] + ) + ->where( + $addressTable . '.quote_address_id IS NULL' + ); + $updateOrderAddress = $setup->getConnection()->updateFromSelect( + $updateOrderAddress, + $addressTable + ); + $setup->getConnection()->query($updateOrderAddress); } /** From c77a32396fa3c7943ecd58452ac41de0ec15e57b Mon Sep 17 00:00:00 2001 From: vprohorov <prohorov.vital@gmail.com> Date: Wed, 22 Aug 2018 19:45:22 +0300 Subject: [PATCH 1124/1171] MAGETWO-91701: Newsletter subscription is not correctly updated when user is registered on 2 stores - Changing customer account getSubscriptionObject behavior --- app/code/Magento/Customer/Block/Account/Dashboard/Info.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Block/Account/Dashboard/Info.php b/app/code/Magento/Customer/Block/Account/Dashboard/Info.php index ded7238edc755..87132c3afb8bc 100644 --- a/app/code/Magento/Customer/Block/Account/Dashboard/Info.php +++ b/app/code/Magento/Customer/Block/Account/Dashboard/Info.php @@ -102,7 +102,7 @@ public function getSubscriptionObject() $this->_subscription = $this->_createSubscriber(); $customer = $this->getCustomer(); if ($customer) { - $this->_subscription->loadByEmail($customer->getEmail()); + $this->_subscription->loadByCustomerId($customer->getId()); } } return $this->_subscription; From baa2a190ecd5bbc9a5f0ad16f05668793909e76d Mon Sep 17 00:00:00 2001 From: Mikalai Shostka <Mikalai_Shostka@epam.com> Date: Thu, 23 Aug 2018 13:03:39 +0300 Subject: [PATCH 1125/1171] MAGETWO-91701: Newsletter subscription is not correctly updated when user is registered on 2 stores - Update functional test --- ...bscribedNewsletterDisplayedActionGroup.xml | 162 ++---------------- ...erifySubscribedNewsletterDisplayedData.xml | 23 --- ...fySubscribedNewsLetterDisplayedSection.xml | 65 +------ ...erifySubscribedNewsletterDisplayedTest.xml | 67 ++++---- 4 files changed, 49 insertions(+), 268 deletions(-) delete mode 100644 app/code/Magento/Newsletter/Test/Mftf/Data/VerifySubscribedNewsletterDisplayedData.xml diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml index b76d3e3022248..92d17e9d71e2f 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml @@ -8,158 +8,24 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> - <!--Go To Stores/All Stores--> - <actionGroup name="GoToAllStores"> - <click stepKey="clickStoreItem" selector="{{Dashboard.storesItem}}"/> - <waitForElementVisible selector="{{Dashboard.storesAllStoresItem}}" stepKey="waitForAllStoresItemBecomeAvailable"/> - <click stepKey="clickAllStoreItem" selector="{{Dashboard.storesAllStoresItem}}"/> - <waitForPageLoad stepKey="waitForStoresPageLoaded"/> - </actionGroup> - - <!--Create new website--> - <actionGroup name="AdminCreateWebsiteActGroup"> - <!--Fill required fields--> - <click selector="{{AdminNewWebsiteSection.addWebSite}}" stepKey="clickOnCreateWebsiteButton"/> - <waitForPageLoad stepKey="waitFormToBeOpened"/> - <fillField selector="{{AdminNewWebsiteSection.name}}" userInput="{{AdminTestData.testData}}" stepKey="enterWebsiteName" /> - <fillField selector="{{AdminNewWebsiteSection.code}}" userInput="{{AdminTestData.testData}}" stepKey="enterWebsiteCode" /> - <click selector="{{AdminNewWebsiteActionsSection.saveWebsite}}" stepKey="clickSaveWebsite" /> - <waitForElementVisible selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="waitForStoreGridToReload"/> - <see userInput="You saved the website." stepKey="seeSavedMessage" /> - </actionGroup> - - <!--Create new store--> - <actionGroup name="AdminCreateNewStoreActGroup"> - <!--Fill required fields--> - <click selector="{{AdminNewStoreGroupSection.create}}" stepKey="clickOnCreateStore"/> - <waitForPageLoad stepKey="waitFormToBeOpened"/> - <selectOption selector="{{AdminNewStoreGroupSection.storeGrpWebsiteDropdown}}" userInput="{{AdminTestData.testData}}" stepKey="selectWebsite" /> - <fillField selector="{{AdminNewStoreGroupSection.storeGrpNameTextField}}" userInput="{{AdminTestData.testData}}" stepKey="enterStoreGroupName" /> - <fillField selector="{{AdminNewStoreGroupSection.storeGrpCodeTextField}}" userInput="{{AdminTestData.testData}}" stepKey="enterStoreGroupCode" /> - <selectOption selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" userInput="Default Category" stepKey="chooseRootCategory" /> - <click selector="{{AdminStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreGroup" /> - <waitForElementVisible selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="waitForStoreGridReload"/> - <see userInput="You saved the store." stepKey="seeSavedMessage" /> - </actionGroup> - - <!--Create store view--> - <actionGroup name="AdminCreateStoreViewActGroup"> - <!--Fill required fields--> - <click selector="{{AdminNewStoreSection.create}}" stepKey="clickOnCreateStoreView"/> - <waitForPageLoad stepKey="waitFormToBeOpened"/> - <selectOption selector="{{AdminNewStoreSection.storeGrpDropdown}}" userInput="{{AdminTestData.testData}}" stepKey="selectStore" /> - <fillField selector="{{AdminNewStoreSection.storeNameTextField}}" userInput="{{AdminTestData.testData}}" stepKey="enterStoreViewName" /> - <fillField selector="{{AdminNewStoreSection.storeCodeTextField}}" userInput="{{AdminTestData.testData}}" stepKey="enterStoreViewCode" /> - <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="Enabled" stepKey="setStatus" /> - <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView" /> - <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal" /> - <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning" /> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal" /> - <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReolad"/> - <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReload"/> - <see userInput="You saved the store view." stepKey="seeSavedMessage" /> - </actionGroup> - - <!--Go to Stores -> Configuration -> Web.--> - <actionGroup name="GoToStoresConfigurationWeb"> - <click stepKey="againClickStoreItem" selector="{{Dashboard.storesItem}}"/> - <waitForElementVisible selector="{{Dashboard.storesConfigurationItem}}" stepKey="waitForAllStoreItemExtends"/> - <click stepKey="clickConfigurationItem" selector="{{Dashboard.storesConfigurationItem}}"/> - <waitForPageLoad stepKey="waitForStoresConfigurationPageLoaded"/> - <click stepKey="clickWebItem" selector="{{AdminStoresConfigurationSection.webItem}}"/> - <waitForPageLoad stepKey="waitForStoresConfigurationWebPageLoaded"/> - </actionGroup> - - <!--Select Yes in Add Store Code to Urls field.--> - <actionGroup name="SelectYesInAddStoreCodeToUrlsField"> - <click stepKey="clickOpenUrlOptions" selector="{{AdminStoresConfigurationSection.openUrlOptions}}"/> - <click stepKey="clickUseSystemValueCheckbox" selector="{{AdminStoresConfigurationSection.useSystemValueCheckbox}}"/> - <selectOption selector="{{AdminStoresConfigurationSection.addStoreCodeToUrlsDropDown}}" userInput="Yes" stepKey="setAddStoreCodeToUrlsYes" /> - <click stepKey="clickSaveConfigButton" selector="{{AdminStoresConfigurationSection.saveConfigButton}}"/> - <waitForPageLoad stepKey="waitForSaveConfig"/> - <see stepKey="seeSavedConfigurationMessage" userInput="You saved the configuration."/> - </actionGroup> - <!--Create an Account. Check Sign Up for Newsletter checkbox --> - <actionGroup name="StorefrontCreateNewAccount"> - <fillField selector="{{StorefrontCustomerCreateFormSection.firstNameField}}" userInput="{{CreateUserData.firstName}}" stepKey="enterFirstName" /> - <fillField selector="{{StorefrontCustomerCreateFormSection.lastNameField}}" userInput="{{CreateUserData.lastName}}" stepKey="enterLastName" /> - <click selector="{{StorefrontCustomerCreateFormSection.signUpForNewsletter}}" stepKey="selectSignUpForNewsletterCheckbox"/> - <fillField selector="{{StorefrontCustomerCreateFormSection.emailField}}" userInput="{{CreateUserData.firstName}}@magento.com" stepKey="enterEmail" /> - <fillField selector="{{StorefrontCustomerCreateFormSection.passwordField}}" userInput="{{CreateUserData.password}}" stepKey="enterPassword" /> - <fillField selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}" userInput="{{CreateUserData.password}}" stepKey="confirmPassword" /> - <click stepKey="clickCreateAccountButton" selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}"/> - <waitForPageLoad stepKey="waitForMyAccountPageLoad"/> - </actionGroup> - - <!-- Sign out --> - <actionGroup name="StorefrontSignOut"> - <click stepKey="clickCustomerNameItem" selector="{{CustomerMyAccountPage.customerName}}"/> - <click stepKey="clickSignOutButton" selector="{{CustomerMyAccountPage.customerSignOut}}"/> - <waitForPageLoad stepKey="waitForSignOut"/> + <actionGroup name="StorefrontCreateNewAccountNewsletterChecked" extends="SignUpNewUserFromStorefrontActionGroup"> + <arguments> + <argument name="Customer"/> + </arguments> + <click selector="{{StorefrontCustomerCreateFormSection.signUpForNewsletter}}" stepKey="selectSignUpForNewsletterCheckbox" after="fillLastName"/> + <see stepKey="seeDescriptionNewsletter" userInput='You are subscribed to "General Subscription".' selector="{{CustomerMyAccountPage.DescriptionNewsletter}}" /> </actionGroup> <!--Create an Account. Uncheck Sign Up for Newsletter checkbox --> - <actionGroup name="StorefrontCreateNewAccountNewsletterUnchecked"> - <click stepKey="clickCreateNewAccountButton" selector="{{CustomerMyAccountPage.createNewAccount}}"/> - <waitForPageLoad stepKey="waitForCreateNewAccountPageLoaded"/> - <fillField selector="{{StorefrontCustomerCreateFormSection.firstNameField}}" userInput="{{CreateUserData.firstName}}" stepKey="enterFirstName" /> - <fillField selector="{{StorefrontCustomerCreateFormSection.lastNameField}}" userInput="{{CreateUserData.lastName}}" stepKey="enterLastName" /> - <fillField selector="{{StorefrontCustomerCreateFormSection.emailField}}" userInput="{{CreateUserData.firstName}}@magento.com" stepKey="enterEmail" /> - <fillField selector="{{StorefrontCustomerCreateFormSection.passwordField}}" userInput="{{CreateUserData.password}}" stepKey="enterPassword" /> - <fillField selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}" userInput="{{CreateUserData.password}}" stepKey="confirmPassword" /> - <click stepKey="clickCreateAccountButton" selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}"/> - <waitForPageLoad stepKey="waitForMyAccountPageLoad"/> - <see userInput="Thank you for registering with" stepKey="seeValidRegistrationMessage"/> - </actionGroup> - - <!--Delete created Website --> - <actionGroup name="AdminDeleteWebsiteActGroup"> - <fillField stepKey="fillSearchWebsiteField" selector="{{AdminStoresGridSection.websiteFilterTextField}}" userInput="{{AdminTestData.testData}}"/> - <click stepKey="clickSearchButton" selector="{{AdminStoresGridSection.searchButton}}"/> - <see stepKey="verifyThatCorrectWebsiteFound" selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" userInput="{{AdminTestData.testData}}"/> - <click stepKey="clickEditExistingStoreRow" selector="{{AdminStoresGridSection.websiteNameInFirstRow}}"/> - <waitForPageLoad stepKey="waitForStoreToLoad"/> - <click stepKey="clickDeleteWebsiteButtonOnEditWebsitePage" selector="{{AdminStoresMainActionsSection.deleteButton}}"/> - <selectOption stepKey="setCreateDbBackupToNo" selector="{{AdminStoresDeleteStoreGroupSection.createDbBackup}}" userInput="No"/> - <click stepKey="clickDeleteWebsiteButton" selector="{{AdminStoresDeleteStoreGroupSection.deleteStoreGroupButton}}"/> - <waitForElementVisible stepKey="waitForStoreGridToReload" selector="{{AdminStoresGridSection.websiteFilterTextField}}"/> - <see stepKey="seeSavedMessage" userInput="You deleted the website."/> - </actionGroup> - - <!--Set Default config --> - <actionGroup name="AdminSetDefaultConfig"> - <selectOption selector="{{AdminStoresConfigurationSection.addStoreCodeToUrlsDropDown}}" userInput="No" stepKey="setAddStoreCodeToUrlsNo" /> - <click stepKey="disableUseSystemValueCheckbox" selector="{{AdminStoresConfigurationSection.useSystemValueCheckbox}}"/> - <click stepKey="clickDefaultConfigSaveButton" selector="{{AdminStoresConfigurationSection.saveConfigButton}}"/> - <waitForPageLoad stepKey="waitForSaveConfig"/> - <see stepKey="saveDefaultConfig" userInput="You saved the configuration."/> - <click stepKey="clickOpenUrlOptions" selector="{{AdminStoresConfigurationSection.openUrlOptions}}"/> - </actionGroup> - - <!--Delete created Customer --> - <actionGroup name="AdminDeleteCreatedCustomer"> - <click stepKey="clickCustomerItem" selector="{{Dashboard.customer}}"/> - <wait stepKey="WaitForCustomerViewOpened" time="2"/> - <click stepKey="clickCustomerAllCustomerItem" selector="{{Dashboard.customerAllCustomer}}"/> - <waitForPageLoad stepKey="WaitForCustomerPageIsLoaded"/> - <conditionalClick selector="{{AdminCustomerAccountInformationSection.clearAll}}" dependentSelector="{{AdminCustomerAccountInformationSection.clearAll}}" visible="1" stepKey="clickClearAllIfThereIsAnyValue"/> - <click stepKey="clickFilterButton" selector="{{AdminCustomerAccountInformationSection.filterButton}}"/> - <waitForElementVisible selector="{{AdminCustomerAccountInformationSection.filterNameField}}" stepKey="waitForFilterDataLoaded"/> - <fillField stepKey="searchProductUsingNameField" selector="{{AdminCustomerAccountInformationSection.filterNameField}}" userInput="{{CreateUserData.firstName}}"/> - <click stepKey="clickFiltersApplyButton" selector="{{AdminCustomerAccountInformationSection.filtersApplyButton}}"/> - <waitForElementNotVisible selector="{{AdminCustomerAccountInformationSection.filterNameField}}" stepKey="waitForFilterBecomeNotVisible"/> - <click selector="{{AdminCustomerAccountInformationSection.selectCustomer}}" stepKey="ClickOnCustomer"/> - <click selector="{{AdminCustomerAccountInformationSection.actions}}" stepKey="ClickOnActions"/> - <waitForElementVisible selector="{{AdminCustomerAccountInformationSection.delete}}" stepKey="waitForDeleteButtonAppeared"/> - <click selector="{{AdminCustomerAccountInformationSection.delete}}" stepKey="ClickOnDelete"/> - <waitForElementVisible selector="{{AdminCustomerAccountInformationSection.confirm}}" stepKey="waitForConfirmButtonAppeared"/> - <click selector="{{AdminCustomerAccountInformationSection.confirm}}" stepKey="ClickToConfirm"/> - <waitForPageLoad stepKey="waitClickToConfirmButton"/> - <see stepKey="seeRecordsWereDeletedMessage" userInput="A total of 2 record(s) were deleted."/> - <click stepKey="clickClearAllFilterButton" selector="{{AdminCustomerAccountInformationSection.clearAll}}"/> - <!-- We need this wait to make sure that Active filters is clear (waitForElementNotVisible tag doesn't wait until clearing filters)--> - <wait stepKey="waitToClearAllFilters" time="2"/> + <actionGroup name="StorefrontCreateNewAccountNewsletterUnchecked" extends="SignUpNewUserFromStorefrontActionGroup"> + <arguments> + <argument name="Customer"/> + <argument name="Store"/> + </arguments> + <amOnPage stepKey="amOnStorefrontPage" url="{{Store.code}}"/> + <see stepKey="seeDescriptionNewsletter" userInput="You aren't subscribed to our newsletter." selector="{{CustomerMyAccountPage.DescriptionNewsletter}}" /> + <see stepKey="seeThankYouMessage" userInput="Thank you for registering with NewStore."/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Data/VerifySubscribedNewsletterDisplayedData.xml b/app/code/Magento/Newsletter/Test/Mftf/Data/VerifySubscribedNewsletterDisplayedData.xml deleted file mode 100644 index 4082626cef5a7..0000000000000 --- a/app/code/Magento/Newsletter/Test/Mftf/Data/VerifySubscribedNewsletterDisplayedData.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> - - <!-- For this Test, all data is the same. --> - <entity name="AdminTestData" type="data"> - <data key="testData" unique="suffix">store2</data> - </entity> - - <entity name="CreateUserData" type="user"> - <data key="firstName" unique="suffix">John</data> - <data key="lastName">Smith</data> - <data key="password">Admin@123</data> - </entity> - -</entities> \ No newline at end of file diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml index 96c3398058742..06f762900436e 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml @@ -9,75 +9,12 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="Dashboard"> - <element name="storesItem" type="button" selector="#menu-magento-backend-stores"/> - <element name="storesAllStoresItem" type="button" selector="//*[@data-ui-id='menu-magento-backend-system-store']/a/span"/> - <element name="storesConfigurationItem" type="button" selector="//*[@data-ui-id='menu-magento-config-system-config']/a/span"/> - <element name="customerAllCustomer" type="button" selector="//*[@data-ui-id='menu-magento-customer-customer-manage']"/> - <element name="customer" type="button" selector="#menu-magento-customer-customer"/> - </section> - - <section name="AdminNewWebsiteSection"> - <element name="stores" type="button" selector="#menu-magento-backend-stores"/> - <element name="allStores" type="button" selector="//span[contains(text(), 'All Stores')]"/> - <element name="addWebSite" type="button" selector="#add"/> - <element name="name" type="input" selector="#website_name"/> - <element name="code" type="input" selector="#website_code"/> - </section> - - <section name="AdminNewStoreGroupSection"> - <element name="create" type="button" selector="#add_group"/> - <element name="storeGrpWebsiteDropdown" type="select" selector="#group_website_id"/> - <element name="storeGrpNameTextField" type="input" selector="#group_name"/> - <element name="storeGrpCodeTextField" type="input" selector="#group_code"/> - <element name="storeRootCategoryDropdown" type="select" selector="#group_root_category_id"/> - </section> - - <section name="AdminNewStoreSection"> - <element name="create" type="button" selector="#add_store"/> - <element name="storeNameTextField" type="input" selector="#store_name"/> - <element name="storeCodeTextField" type="input" selector="#store_code"/> - <element name="statusDropdown" type="select" selector="#store_is_active"/> - <element name="storeGrpDropdown" type="select" selector="#store_group_id"/> - <element name="sortOrderTextField" type="input" selector="#store_sort_order"/> - <element name="acceptNewStoreViewCreation" type="button" selector=".action-primary.action-accept" /> - </section> - - <section name="AdminStoresConfigurationSection"> - <element name="openUrlOptions" type="button" selector="#web_url-head"/> - <element name="webItem" type="button" selector="//*[@id='system_config_tabs']/div[1]//span[contains(text(), 'Web')]"/> - <element name="useSystemValueCheckbox" type="checkbox" selector="#web_url_use_store_inherit"/> - <element name="addStoreCodeToUrlsDropDown" type="select" selector="#web_url_use_store"/> - <element name="saveConfigButton" type="button" selector="#save"/> - </section> - <section name="StorefrontCustomerCreateFormSection"> - <element name="firstNameField" type="input" selector="#firstname"/> - <element name="lastNameField" type="input" selector="#lastname"/> - <element name="emailField" type="input" selector="#email_address"/> - <element name="passwordField" type="input" selector="#password"/> - <element name="confirmPasswordField" type="input" selector="#password-confirmation"/> - <element name="createAccountButton" type="button" selector="button.action.submit.primary" timeout="30"/> <element name="signUpForNewsletter" type="checkbox" selector="//span[contains(text(), 'Sign Up for Newsletter')]"/> </section> <section name="CustomerMyAccountPage"> - <element name="createNewAccount" type="button" selector="//*[@class='page-header']//a[contains(text(),'Create an Account')]"/> - <element name="customerName" type="button" selector="//*[@class='customer-welcome']//span[@class='customer-name']/button[@data-action='customer-menu-toggle']"/> - <element name="customerSignOut" type="button" selector="//*[@class='customer-menu']//*[normalize-space()='Sign Out']"/> - </section> - - <section name="AdminCustomerAccountInformationSection"> - <element name="searchToKeyword" type="input" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/input"/> - <element name="searchButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/button"/> - <element name="selectCustomer" type="checkbox" selector="//*[@class='admin__data-grid-wrap' and @data-role='grid-wrapper']//*[@class='data-grid-multicheck-cell']/div/label"/> - <element name="actions" type="button" selector="//div[@class='admin__data-grid-header']//div[@class='col-xs-2']//span[text()='Actions']"/> - <element name="delete" type="button" selector="//div[@class='col-xs-2']//span[text()='Delete']"/> - <element name="confirm" type="button" selector=".action-primary.action-accept"/> - <element name="clearAll" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[contains(text(), 'Clear all')]"/> - <element name="filterButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-filters-action-wrap']/button"/> - <element name="filterNameField" type="input" selector="//*[@name='name']"/> - <element name="filtersApplyButton" type="button" selector="//*[contains(text(),'Apply Filters')]"/> + <element name="DescriptionNewsletter" type="text" selector=".box-newsletter p"/> </section> </sections> \ No newline at end of file diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml index ad223381deadc..faed8b1af952e 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml @@ -20,46 +20,47 @@ <before> <!--Log in to Magento as admin.--> - <actionGroup ref="LoginActionGroup" stepKey="login"/> - <!--Go to Stores.--> - <actionGroup ref="GoToAllStores" stepKey="goToAllStores"/> - <!--Create Website.--> - <actionGroup ref="AdminCreateWebsiteActGroup" stepKey="adminCreateWebsite"/> - <!--Create Store.--> - <actionGroup ref="AdminCreateNewStoreActGroup" stepKey="adminCreateNewStore"/> - <!--Create Store View.--> - <actionGroup ref="AdminCreateStoreViewActGroup" stepKey="adminCreateStoreView"/> - <!--Go to Stores -> Configuration -> Web.--> - <actionGroup ref="GoToStoresConfigurationWeb" stepKey="goToStoresConfigurationWeb"/> - <actionGroup ref="SelectYesInAddStoreCodeToUrlsField" stepKey="selectYesInAddStoreCodeToUrlsField"/> - </before> - - <!--Go to store front (default) and click Create an Account.--> - <amOnPage url="{{StorefrontCustomerCreatePage.url}}" stepKey="navigateToCustomers"/> - <actionGroup ref="StorefrontCreateNewAccount" stepKey="createNewAccount"/> - - <!--Sign Out--> - <actionGroup ref="StorefrontSignOut" stepKey="storefrontSignOut"/> + <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> + <argument name="newWebsiteName" value="Second"/> + <argument name="websiteCode" value="Base2"/> + </actionGroup> - <!--Change 'default' to 'url_name' in url--> - <amOnPage url="{{AdminTestData.testData}}" stepKey="goToCreatedWebPage"/> - <waitForPageLoad stepKey="waitForCreatedWebPageLoaded"/> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewStore"> + <argument name="website" value="Second"/> + <argument name="storeGroupName" value="NewStore"/> + <argument name="storeGroupCode" value="Base12"/> + </actionGroup> - <!--Create new Account with the same email address. (unchecked Sign Up for Newsletter checkbox)--> - <actionGroup ref="StorefrontCreateNewAccountNewsletterUnchecked" stepKey="createNewAccountNewsletterUnchecked"/> - <dontSee stepKey="verifySubscribedNewsletters" userInput="You aren't subscribed to our newsletter displayed"/> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createCustomStoreView"> + <argument name="StoreGroup" value="staticStoreGroup"/> + <argument name="customStore" value="staticStore"/> + </actionGroup> + <actionGroup ref="EnableWebUrlOptions" stepKey="addStoreCodeToUrls"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> <after> <!--Delete created data and set Default Configuration--> - <amOnPage url="admin" stepKey="goToAdminPage"/> - <waitForPageLoad stepKey="waitForAdminPageLoaded"/> - <actionGroup ref="AdminDeleteCreatedCustomer" stepKey="adminDeleteCustomer"/> - <actionGroup ref="GoToAllStores" stepKey="goToAllStores"/> - <actionGroup ref="AdminDeleteWebsiteActGroup" stepKey="adminDeleteWebsite"/> - <actionGroup ref="GoToStoresConfigurationWeb" stepKey="goToStoresConfigurationWeb"/> - <actionGroup ref="AdminSetDefaultConfig" stepKey="adminSetDefaultConfig"/> + <actionGroup ref="ResetWebUrlOptions" stepKey="resetUrlOption"/> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="Second"/> + </actionGroup> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> </after> + <!--Go to store front (default) and click Create an Account.--> + <actionGroup ref="StorefrontCreateNewAccountNewsletterChecked" stepKey="SignUpNewUser"> + <argument name="Customer" value="CustomerEntityOne"/> + </actionGroup> + <!--Sign Out--> + <amOnPage url="customer/account/logout/" stepKey="customerOnLogoutPage"/> + <waitForPageLoad stepKey="waitLogoutCustomer"/> + <!--Create new Account with the same email address. (unchecked Sign Up for Newsletter checkbox)--> + <actionGroup ref="StorefrontCreateNewAccountNewsletterUnchecked" stepKey="createNewAccountNewsletterUnchecked"> + <argument name="Customer" value="CustomerEntityOne"/> + <argument name="Store" value="staticStore"/> + </actionGroup> </test> </tests> From 54479d3e60489dfb1760e88b72208c3f4950f2ec Mon Sep 17 00:00:00 2001 From: IvanPletnyov <ivan.pletnyov@transoftgroup.com> Date: Thu, 23 Aug 2018 13:55:14 +0300 Subject: [PATCH 1126/1171] MSI-1542: Provide MSI support for Shipment Web API endpoint --- .../Test/Unit/Model/Order/ShipmentDocumentFactoryTest.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactoryTest.php index 6075bcfb615b2..f17d18c3be1da 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactoryTest.php @@ -140,9 +140,7 @@ public function testCreate() $packages = []; $items = [1 => 10]; - $this->extensionAttributeProcessorMock->expects($this->once()) - ->method('execute') - ->with($this->shipmentMock, null); + $this->extensionAttributeProcessorMock->expects($this->never())->method('execute'); $this->itemMock->expects($this->once())->method('getOrderItemId')->willReturn(1); $this->itemMock->expects($this->once())->method('getQty')->willReturn(10); $this->itemMock->expects($this->once()) From eecd47cdf8996dc1f37c5589a9b07bdfeffe0227 Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@magento.com> Date: Thu, 23 Aug 2018 16:40:55 +0300 Subject: [PATCH 1127/1171] MAGETWO-93753: [Forwardport] Implement sharding and parallelization for Price Indexer - part 2 --- .../Magento/Catalog/Model/ProductPriceWithDimensionTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php index c4b22b37d00e9..348d6e19b44e3 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php @@ -110,7 +110,7 @@ public function testGetMinPrice(): void $collection->load(); /** @var \Magento\Catalog\Model\Product $product */ $product = $collection->getFirstItem(); - $this->assertEquals(333, $product->getData('min_price')); + $this->assertEquals(323, $product->getData('min_price')); } /** From d95a7d14cdfa8d6e90dcdaf0bf43b9ffe545c6e2 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Thu, 23 Aug 2018 09:33:04 -0500 Subject: [PATCH 1128/1171] MQE-1174: Deliver weekly regression enablement tests - Use MFTF 2.3.5 --- composer.json | 2 +- composer.lock | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 50927a2ebfee9..ce003f73621af 100644 --- a/composer.json +++ b/composer.json @@ -81,7 +81,7 @@ "zendframework/zend-view": "~2.10.0" }, "require-dev": { - "magento/magento2-functional-testing-framework": "2.3.x-dev", + "magento/magento2-functional-testing-framework": "2.3.5", "friendsofphp/php-cs-fixer": "~2.12.0", "lusitanian/oauth": "~0.8.10", "pdepend/pdepend": "2.5.2", diff --git a/composer.lock b/composer.lock index 3a4a3c1aaf317..b4147f01a9cb5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "bd3e37613fe27f3d28a70d353676d0a2", + "content-hash": "ef3c5510832524507bfdfbb6ddd49f07", "packages": [ { "name": "braintree/braintree_php", @@ -6183,16 +6183,16 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.3.x-dev", + "version": "2.3.5", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "2d4b061399b2327ae6f9f5e4ad6a380f62ab8e35" + "reference": "bb1518aab82464e25ff97874da939d13ba4b6fac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/2d4b061399b2327ae6f9f5e4ad6a380f62ab8e35", - "reference": "2d4b061399b2327ae6f9f5e4ad6a380f62ab8e35", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/bb1518aab82464e25ff97874da939d13ba4b6fac", + "reference": "bb1518aab82464e25ff97874da939d13ba4b6fac", "shasum": "" }, "require": { @@ -6250,7 +6250,7 @@ "magento", "testing" ], - "time": "2018-08-15T17:20:25+00:00" + "time": "2018-08-21T16:57:34+00:00" }, { "name": "moontoast/math", @@ -8873,7 +8873,6 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "magento/magento2-functional-testing-framework": 20, "phpmd/phpmd": 0 }, "prefer-stable": true, From fd585bf983a6a154ea21db75c3a4ae0470b54d24 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Tue, 21 Aug 2018 14:41:44 -0500 Subject: [PATCH 1129/1171] MQE-1217: Deliver MFTF 2.3.5 - Fix MFTF Tests --- .../ConfigurableProductAttributeNameDesignActionGroup.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml index eec1fd6273ee3..95533057608f2 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml @@ -69,26 +69,31 @@ <!--Add option 1 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption1"/> + <waitForPageLoad stepKey="waitForOption1"/> <fillField stepKey="fillInAdminFieldRed" selector="{{NewProduct.adminFieldRed}}" userInput="{{NewProductsData.adminFieldRed}}"/> <fillField stepKey="fillInDefaultStoreViewFieldRed" selector="{{NewProduct.defaultStoreViewFieldRed}}" userInput="{{NewProductsData.defaultStoreViewFieldRed}}"/> <!--Add option 2 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption2"/> + <waitForPageLoad stepKey="waitForOption2"/> <fillField stepKey="fillInAdminFieldBlue" selector="{{NewProduct.adminFieldBlue}}" userInput="{{NewProductsData.adminFieldBlue}}"/> <fillField stepKey="fillInDefaultStoreViewFieldBlue" selector="{{NewProduct.defaultStoreViewFieldBlue}}" userInput="{{NewProductsData.defaultStoreViewFieldBlue}}"/> <!--Add option 3 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption3"/> + <waitForPageLoad stepKey="waitForOption3"/> <fillField stepKey="fillInAdminFieldYellow" selector="{{NewProduct.adminFieldYellow}}" userInput="{{NewProductsData.adminFieldYellow}}"/> <fillField stepKey="fillInDefaultStoreViewFieldYellow" selector="{{NewProduct.defaultStoreViewFieldYellow}}" userInput="{{NewProductsData.defaultStoreViewFieldYellow}}"/> <!--Add option 4 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption4"/> + <waitForPageLoad stepKey="waitForOption4"/> <fillField stepKey="fillInAdminFieldGreen" selector="{{NewProduct.adminFieldGreen}}" userInput="{{NewProductsData.adminFieldGreen}}"/> <fillField stepKey="fillInDefaultStoreViewFieldGreen" selector="{{NewProduct.defaultStoreViewFieldGreen}}" userInput="{{NewProductsData.defaultStoreViewFieldGreen}}"/> <!--Add option 5 to attribute--> <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption5"/> + <waitForPageLoad stepKey="waitForOption5"/> <fillField stepKey="fillInAdminFieldBlack" selector="{{NewProduct.adminFieldBlack}}" userInput="{{NewProductsData.adminFieldBlack}}"/> <fillField stepKey="fillInDefaultStoreViewFieldBlack" selector="{{NewProduct.defaultStoreViewFieldBlack}}" userInput="{{NewProductsData.defaultStoreViewFieldBlack}}"/> @@ -135,6 +140,7 @@ <!--Click on Stores item--> <click stepKey="clickOnStoresItem" selector="{{CatalogProductsSection.storesItem}}"/> + <waitForPageLoad stepKey="waitForNavigationPanel"/> <!--Click on Products item--> <waitForElementVisible selector="{{CatalogProductsSection.storesProductItem}}" stepKey="waitForCatalogLoad"/> From b2083266dfc0c75e688dae04c7046ff5ef12f598 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 22 Aug 2018 09:26:02 -0500 Subject: [PATCH 1130/1171] MQE-1217: Deliver MFTF 2.3.5 - Fix MFTF tests --- .../Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml index 80351c9d97520..e329239ff46e4 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml @@ -23,7 +23,7 @@ <amOnPage url="admin/admin/system_config/edit/section/cms/" stepKey="navigateToConfigurationPage" /> <waitForPageLoad stepKey="wait3"/> <conditionalClick stepKey="expandWYSIWYGOptions" selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.CheckIfTabExpand}}" visible="true" /> - <waitForElementVisible selector="{{ContentManagementSection.EnableWYSIWYG}}" stepKey="waitForEnableWYSIWYGDropdown2" /> + <waitForElementVisible selector="{{ContentManagementSection.EnableWYSIWYG}}" stepKey="waitForEnableWYSIWYGDropdown2" time="30"/> <uncheckOption selector="{{ContentManagementSection.EnableSystemValue}}" stepKey="uncheckUseSystemValue"/> <selectOption selector="{{ContentManagementSection.EnableWYSIWYG}}" userInput="Disabled Completely" stepKey="selectOption2"/> <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions" /> From a05c307f2a08b0941cc443232e4eebaf82f3adb5 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Thu, 23 Aug 2018 14:20:37 -0500 Subject: [PATCH 1131/1171] MQE-1174: Deliver weekly regression enablement tests - Undo changes to Cli.php --- .../functional/lib/Magento/Mtf/Util/Command/Cli.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php index e22f27b91ccc5..8fa22122cce89 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php @@ -59,14 +59,6 @@ public function execute($command, $options = []) private function prepareUrl($command, $options = []) { $command .= ' ' . implode(' ', $options); - // replacing index.php if it presents - $count = 1; - $trimmedAppFrontendUrl = str_replace( - 'index.php', - '', - rtrim($_ENV['app_frontend_url'], '/'), - $count - ); - return $trimmedAppFrontendUrl . self::URL . '?command=' . urlencode($command); + return $_ENV['app_frontend_url'] . self::URL . '?command=' . urlencode($command); } } From 591c13d0a9c4c74b9f6ccee9dd8ddd719439aaac Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Fri, 17 Aug 2018 17:46:32 +0300 Subject: [PATCH 1132/1171] MAGETWO-62891: New address is not marked as "Default Billing" - Update automated test --- ...ltBillingAndShippingAddressActionGroup.xml | 127 ------------------ ...OfDefaultBillingAndShippingAddressData.xml | 32 ----- ...efaultBillingAndShippingAddressSection.xml | 82 ----------- ...OfDefaultBillingAndShippingAddressTest.xml | 46 ++++--- 4 files changed, 28 insertions(+), 259 deletions(-) delete mode 100644 app/code/Magento/Checkout/Test/Mftf/Data/IdentityOfDefaultBillingAndShippingAddressData.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml index 4e84128d8385a..6e5f127eefc18 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml @@ -8,88 +8,6 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> - <!-- Go To Product Page --> - <actionGroup name="GoToProductPage"> - <click selector="{{GoToProductPageSection.catalog}}" stepKey="clickOnCatalog" /> - <waitForPageLoad stepKey="waitForPage"/> - <click selector="{{GoToProductPageSection.product}}" stepKey="clickToSelectProductsItem" /> - <waitForPageLoad stepKey="waitForPageProducts"/> - </actionGroup> - - <!-- Create Simple Product --> - <actionGroup name="CreateSimpleProduct"> - <arguments> - <argument name="product" defaultValue="SimpleProduct"/> - </arguments> - <click selector="{{GoToProductPageSection.add}}" stepKey="clickToAddProduct"/> - <waitForPageLoad stepKey="WaitForProductPageIsLoaded"/> - <fillField selector="{{AdminAddSimpleProductSection.productName}}" userInput="{{product.name}}" stepKey="setNameForProduct"/> - <fillField selector="{{AdminAddSimpleProductSection.productSku}}" userInput="{{product.sku}}" stepKey="setSKUForProduct"/> - <fillField selector="{{AdminAddSimpleProductSection.productPrice}}" userInput="{{product.price}}" stepKey="setPriceForProduct"/> - <fillField selector="{{AdminAddSimpleProductSection.productQuantity}}" userInput="{{product.quantity}}" stepKey="setQuantityForProduct"/> - <click selector="{{AdminAddSimpleProductSection.searchOptimization}}" stepKey="clickOnSearchEngineOptimization"/> - <fillField selector="{{AdminAddSimpleProductSection.urlKey}}" userInput="{{product.urlKey}}" stepKey="setSearchUrlForProduct"/> - <click selector="{{AdminAddSimpleProductSection.saveButton}}" stepKey="clickSaveProduct"/> - <waitForPageLoad stepKey="WaitForProductSave"/> - <see userInput="You saved the product." stepKey="seeSaveConfirmation"/> - </actionGroup> - - <!--Create Account --> - <actionGroup name="StorefrontCreateAccountActionGroup"> - <click selector="{{StorefrontCreateAccountSection.createAccount}}" stepKey="ClickToCreateAccount"/> - <waitForPageLoad stepKey="waitForAccountFormIsOpened"/> - <fillField selector="{{StorefrontCreateAccountSection.firstName}}" userInput="{{CreateNewUserData.firstName}}" stepKey="TypeFirstName"/> - <fillField selector="{{StorefrontCreateAccountSection.lastName}}" userInput="{{CreateNewUserData.lastName}}" stepKey="TypeLastName"/> - <fillField selector="{{StorefrontCreateAccountSection.email}}" userInput="{{CreateNewUserData.firstName}}@magento.com" stepKey="TypeEmail"/> - <fillField selector="{{StorefrontCreateAccountSection.password}}" userInput="{{CreateNewUserData.password}}" stepKey="TypePassword"/> - <waitForPageLoad stepKey="waitToConfirmPassword"/> - <fillField selector="{{StorefrontCreateAccountSection.confirmPass}}" userInput="{{CreateNewUserData.password}}" stepKey="confirmPassword"/> - <click selector="{{StorefrontCreateAccountSection.create}}" stepKey="ClickToSaveAccount"/> - <waitForPageLoad stepKey="waitForAccountPageLoaded"/> - </actionGroup> - - <!--Find and add product to cart--> - <actionGroup name="FindAndAddProductToCardActionGroup"> - <!--Navigate to a category page --> - <amOnPage url="/{{SimpleProduct.name}}.html" stepKey="goToCreatedProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoaded"/> - <click selector="{{StorefrontAddCreatedProductToCartSection.addToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontAddCreatedProductToCartSection.successMessage}}" time="30" stepKey="waitForProductAdded"/> - <click selector="{{StorefrontAddCreatedProductToCartSection.showCard}}" stepKey="clickToOpenCard"/> - <waitForPageLoad stepKey="WaitForFormOpened"/> - <click selector="{{StorefrontAddCreatedProductToCartSection.proceed}}" stepKey="clickToProceedToCheckout"/> - <waitForPageLoad stepKey="waitForTheFormIsOpened"/> - <see userInput="Shipping Address" stepKey="seeShippingAddress"/> - </actionGroup> - - <!--Fill shipment form--> - <actionGroup name="FillShipmentFormActionGroup"> - <fillField selector="{{ShipmentFormSection.street}}" userInput="{{Account.street}}" stepKey="SetCustomerStreetAddress"/> - <fillField selector="{{ShipmentFormSection.city}}" userInput="{{Account.city}}" stepKey="SetCustomerCity"/> - <fillField selector="{{ShipmentFormSection.postcode}}" userInput="{{Account.postcode}}" stepKey="SetCustomerZipCode"/> - <fillField selector="{{ShipmentFormSection.telephone}}" userInput="{{Account.telephone}}" stepKey="SetCustomerPhoneNumber"/> - <click selector="{{ShipmentFormSection.region}}" stepKey="clickToSetState"/> - <click selector="{{ShipmentFormSection.state}}" stepKey="clickToChooseState"/> - <click selector="{{ShipmentFormSection.stateAlabama}}" stepKey="chooseStateAlabama"/> - <click selector="{{ShipmentFormSection.next}}" stepKey="clickToSaveShippingInfo"/> - <waitForPageLoad stepKey="waitForReviewAndPaymentsPageIsLoaded"/> - </actionGroup> - - <!--Mark "My billing and shipping address are the same"--> - <actionGroup name="MarkMyBillingAndShippingAddressAreTheSame"> - <conditionalClick selector="{{ShipmentFormSection.billingShippingAddressTheSameCheckbox}}" dependentSelector="{{ShipmentFormSection.placeOrderButton}}" visible="0" stepKey="selectkMyBillingAndShippingAddressAreTheSameCheckbox"/> - <click stepKey="clickPlaceOrderButton" selector="{{ShipmentFormSection.placeOrderButton}}"/> - <waitForPageLoad stepKey="waitForCheckoutPageLoaded"/> - <see stepKey="seeSuccessfulMessage" userInput="Thank you for your purchase!"/> - </actionGroup> - - <!--Go To My Account Page--> - <actionGroup name="GoToMyAccountPage"> - <click stepKey="clickCustomerNameItem" selector="{{GoToMyAccountSection.customerName}}"/> - <click stepKey="clickMyAccountItem" selector="{{GoToMyAccountSection.myAccountItem}}"/> - <waitForPageLoad stepKey="waitForMyAccountPageLoaded"/> - </actionGroup> - <!--Assert That Shipping And Billing Address are the same--> <actionGroup name="AssertThatShippingAndBillingAddressTheSame"> <!--Get shipping and billing addresses--> @@ -101,49 +19,4 @@ <assertEquals stepKey="assert" actual="$billingAddr" expected="$shippingAddr"/> </actionGroup> - <!-- Delete Created Product --> - <actionGroup name="DeleteCreatedProduct"> - <conditionalClick selector="{{DeleteCreatedProductSection.clearAll}}" dependentSelector="{{DeleteCreatedProductSection.clearAll}}" visible="1" stepKey="clickClearAllIfThereIsAnyValue"/> - <click stepKey="clickFilterButton" selector="{{DeleteCreatedProductSection.filterButton}}"/> - <waitForElementVisible selector="{{DeleteCreatedProductSection.filterSKUField}}" stepKey="waitForFilterDataLoaded"/> - <fillField stepKey="searchProductUsingSKUField" selector="{{DeleteCreatedProductSection.filterSKUField}}" userInput="{{SimpleProduct.sku}}"/> - <click stepKey="clickFiltersApplyButton" selector="{{DeleteCreatedProductSection.filtersApplyButton}}"/> - <waitForElementNotVisible selector="{{DeleteCreatedProductSection.filterSKUField}}" stepKey="waitForFilterBecomeNotVisible"/> - <click selector="{{DeleteCreatedProductSection.createdProductID}}" stepKey="selectCreatedProduct"/> - <wait stepKey="waitSelectCreatedProduct" time="2"/> - <waitForElementVisible selector="{{DeleteCreatedProductSection.actionSelectBox}}" stepKey="waitToSelectActionVisible"/> - <click stepKey="clickToSelectAction" selector="{{DeleteCreatedProductSection.actionSelectBox}}"/> - <waitForElementVisible selector="{{DeleteCreatedProductSection.deleteButton}}" stepKey="waitForDeleteButtonAppeared"/> - <click selector="{{DeleteCreatedProductSection.deleteButton}}" stepKey="clickToDeleteProduct"/> - <waitForElementVisible selector="{{DeleteCreatedProductSection.okButton}}" stepKey="waitForOkButtonAppeared"/> - <click selector="{{DeleteCreatedProductSection.okButton}}" stepKey="clickToConfirm"/> - <wait stepKey="waitForRecordIsDeleted" time="2"/> - <see userInput="A total of 1 record(s) have been deleted." stepKey="productDeletedSuccessfully"/> - <click stepKey="clickClearAllFilterButton" selector="{{DeleteCreatedProductSection.clearAll}}"/> - <wait stepKey="waitToClearAllFilters" time="2"/> - </actionGroup> - - <!--Delete created Customer --> - <actionGroup name="DeleteCreatedCustomerActionGroup"> - <click stepKey="clickCustomerItem" selector="{{DashboardSection.customer}}"/> - <wait stepKey="WaitForCustomerViewOpened" time="2"/> - <click stepKey="clickCustomerAllCustomerItem" selector="{{DashboardSection.customerAllCustomer}}"/> - <waitForPageLoad stepKey="WaitForCustomerPageIsLoaded"/> - <conditionalClick selector="{{AdminCustomerAccInformationSection.clearAll}}" dependentSelector="{{AdminCustomerAccInformationSection.clearAll}}" visible="1" stepKey="clickClearAllIfThereIsAnyValue"/> - <click stepKey="clickFilterButton" selector="{{AdminCustomerAccInformationSection.filterButton}}"/> - <waitForElementVisible selector="{{AdminCustomerAccInformationSection.filterNameField}}" stepKey="waitForFilterDataLoaded"/> - <fillField stepKey="searchProductUsingNameField" selector="{{AdminCustomerAccInformationSection.filterNameField}}" userInput="{{CreateNewUserData.firstName}}"/> - <click stepKey="clickFiltersApplyButton" selector="{{AdminCustomerAccInformationSection.filtersApplyButton}}"/> - <waitForElementNotVisible selector="{{AdminCustomerAccInformationSection.filterNameField}}" stepKey="waitForFilterBecomeNotVisible"/> - <click selector="{{AdminCustomerAccInformationSection.selectCustomer}}" stepKey="ClickOnCustomer"/> - <click selector="{{AdminCustomerAccInformationSection.actions}}" stepKey="ClickOnActions"/> - <waitForElementVisible selector="{{AdminCustomerAccInformationSection.delete}}" stepKey="waitForDeleteButtonAppeared"/> - <click selector="{{AdminCustomerAccInformationSection.delete}}" stepKey="ClickOnDelete"/> - <waitForElementVisible selector="{{AdminCustomerAccInformationSection.confirm}}" stepKey="waitForConfirmButtonAppeared"/> - <click selector="{{AdminCustomerAccInformationSection.confirm}}" stepKey="ClickToConfirm"/> - <waitForPageLoad stepKey="waitClickToConfirmButton"/> - <click stepKey="clickClearAllFilterButton" selector="{{DeleteCreatedProductSection.clearAll}}"/> - <wait stepKey="waitToClearAllFilters" time="2"/> - </actionGroup> - </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/IdentityOfDefaultBillingAndShippingAddressData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/IdentityOfDefaultBillingAndShippingAddressData.xml deleted file mode 100644 index 0f5dc1ce4cfa9..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/Data/IdentityOfDefaultBillingAndShippingAddressData.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="SimpleProduct" type="product"> - <data key="name" unique="suffix">testProduct</data> - <data key="sku" unique="suffix">testSku</data> - <data key="price">210</data> - <data key="quantity">10</data> - <data key="urlKey" unique="suffix">testProduct</data> - </entity> - - <entity name="Account" type="product"> - <data key="street">BirminghamStreet</data> - <data key="city">Birmingham</data> - <data key="postcode">35005</data> - <data key="telephone">222222222</data> - </entity> - - <entity name="CreateNewUserData" type="user"> - <data key="firstName" unique="suffix">John</data> - <data key="lastName">Smith</data> - <data key="password">Admin@123</data> - </entity> - -</entities> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml index 4b689e1b68441..89b3a25b45e3c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml @@ -8,91 +8,9 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="GoToProductPageSection"> - <!--Go to Catalog/Products--> - <element name="catalog" type="button" selector="#menu-magento-catalog-catalog"/> - <element name="product" type="button" selector="//span[contains(text(), 'Products')]"/> - <element name="add" type="button" selector="#add_new_product-button"/> - </section> - - <section name="AdminAddSimpleProductSection"> - <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> - <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> - <element name="productPrice" type="input" selector=".admin__field[data-index=price] input"/> - <element name="productQuantity" type="input" selector=".admin__field[data-index=qty] input"/> - <element name="searchOptimization" type="button" selector="//*[contains(text(),'Search Engine Optimization')]"/> - <element name="urlKey" type="input" selector="//input[contains(@name,'url_key')]"/> - <element name="saveButton" type="button" selector="#save-button"/> - </section> - - <section name="StorefrontCreateAccountSection"> - <element name="createAccount" type="button" selector="//div[contains(@class, 'panel wrapper')]//a[text()='Create an Account']"/> - <element name="firstName" type="input" selector="#firstname"/> - <element name="lastName" type="input" selector="#lastname"/> - <element name="email" type="input" selector="#email_address"/> - <element name="password" type="input" selector="#password"/> - <element name="confirmPass" type="input" selector="#password-confirmation"/> - <element name="create" type="button" selector="//button[@type='submit' and @title='Create an Account']"/> - </section> - - <section name="StorefrontAddCreatedProductToCartSection"> - <element name="addToCartBtn" type="button" selector="button.action.tocart.primary"/> - <element name="successMsg" type="button" selector="div.message-success"/> - <element name="showCard" type="button" selector=".action.showcart"/> - <element name="proceed" type="button" selector="#top-cart-btn-checkout"/> - <element name="successMessage" type="button" selector="div.message-success"/> - </section> <section name="ShipmentFormSection"> - <element name="street" type="input" selector="//*[@name='street[0]']"/> - <element name="city" type="input" selector="//*[@name='city']"/> - <element name="postcode" type="input" selector="//*[@name='postcode']"/> - <element name="telephone" type="input" selector="//*[@name='telephone']"/> - <element name="region" type="input" selector="//*[@name='region_id']"/> - <element name="state" type="input" selector="//*[@name='country_id']"/> - <element name="stateAlabama" type="select" selector="//*[@name='region_id']/option[contains(text(),'Alabama')]"/> - <element name="next" type="input" selector="//span[contains(text(), 'Next')]"/> - - <element name="billingShippingAddressTheSameCheckbox" type="select" selector="#billing-address-same-as-shipping-checkmo"/> - <element name="placeOrderButton" type="button" selector="//span[contains(text(),'Place Order')]"/> <element name="shippingAddress" type="textarea" selector="//*[@class='box box-billing-address']//address"/> <element name="billingAddress" type="textarea" selector="//*[@class='box box-shipping-address']//address"/> </section> - - <section name="GoToMyAccountSection"> - <element name="customerName" type="input" selector="//*[@class='page-header']//*[@data-bind='text: customer().fullname']"/> - <element name="myAccountItem" type="input" selector="//*[@class='page-header']//*[contains(text(),'My Account')]"/> - </section> - - <section name="DeleteCreatedProductSection"> - <element name="searchToKeyword" type="input" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/input"/> - <element name="searchButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/button"/> - <element name="createdProductID" type="select" selector="//*[@class='data-grid-checkbox-cell-inner']/input"/> - <element name="actionSelectBox" type="button" selector="//*[@class='col-xs-2']//span[text()='Actions']"/> - <element name="deleteButton" type="button" selector="//div[@class='col-xs-2']//*[text()='Delete']"/> - <element name="okButton" type="button" selector=".action-primary.action-accept"/> - <element name="clearAll" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[contains(text(), 'Clear all')]"/> - <element name="filterButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-filters-action-wrap']/button"/> - <element name="filterSKUField" type="input" selector="//*[@name='sku']"/> - <element name="filtersApplyButton" type="button" selector="//*[contains(text(),'Apply Filters')]"/> - </section> - - <section name="DashboardSection"> - <element name="customerAllCustomer" type="button" selector="//*[@data-ui-id='menu-magento-customer-customer-manage']"/> - <element name="customer" type="button" selector="#menu-magento-customer-customer"/> - </section> - - <section name="AdminCustomerAccInformationSection"> - <element name="searchToKeyword" type="input" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/input"/> - <element name="searchButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-search-control-wrap']/button"/> - <element name="selectCustomer" type="checkbox" selector="//*[@class='admin__data-grid-wrap' and @data-role='grid-wrapper']//*[@class='data-grid-multicheck-cell']/div/label"/> - <element name="actions" type="button" selector="//div[@class='admin__data-grid-header']//div[@class='col-xs-2']//span[text()='Actions']"/> - <element name="delete" type="button" selector="//div[@class='col-xs-2']//span[text()='Delete']"/> - <element name="confirm" type="button" selector=".action-primary.action-accept"/> - <element name="filterButton" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[@class='data-grid-filters-action-wrap']/button"/> - <element name="filterNameField" type="input" selector="//*[@name='name']"/> - <element name="filtersApplyButton" type="button" selector="//*[contains(text(),'Apply Filters')]"/> - <element name="clearAll" type="button" selector="//*[@class='admin__data-grid-outer-wrap']/*[@class='admin__data-grid-header']//*[contains(text(), 'Clear all')]"/> - </section> - </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml index 48e4cc631e175..9664ec47420cc 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml @@ -20,43 +20,53 @@ </annotations> <before> - <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!--Create product--> - <actionGroup ref="GoToProductPage" stepKey="goToProductPage"/> - <actionGroup ref="CreateSimpleProduct" stepKey="createSimpleProduct"/> + <createData stepKey="category" entity="SimpleSubCategory"/> + <createData stepKey="product" entity="SimpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> </before> <!--Go to Storefront--> <amOnPage url="" stepKey="DoToStorefront"/> - <!--Create account--> - <actionGroup ref="StorefrontCreateAccountActionGroup" stepKey="storefrontCreateAccountActionGroup"/> + <!-- Fill out form for a new user with address --> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> + <argument name="Customer" value="Simple_US_Customer_NY"/> + </actionGroup> + + <!-- Add simple product to cart --> + <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart1"> + <argument name="product" value="$$product$$"/> + </actionGroup> - <!--Add product to cart--> - <actionGroup ref="FindAndAddProductToCardActionGroup" stepKey="FindAndAddProductToCard"/> + <!--Proceed to shipment--> + <amOnPage url="{{CheckoutPage.url}}/" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForShippingSection"/> <!--Fill shipment form--> - <actionGroup ref="FillShipmentFormActionGroup" stepKey="ShipmentFormActionGroup"/> + <actionGroup ref="LoggedInUserCheckoutFillingShippingSectionActionGroup" stepKey="checkoutFillingShippingSection" > + <argument name="customerVar" value="Simple_US_Customer_NY" /> + <argument name="customerAddressVar" value="US_Address_NY" /> + </actionGroup> <!--Fill cart data--> - <actionGroup ref="MarkMyBillingAndShippingAddressAreTheSame" stepKey="markMyBillingAndShippingAddressAreTheSame"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment" /> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="placeorder"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage" /> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> + </actionGroup> <!--Go To My Account--> - <actionGroup ref="GoToMyAccountPage" stepKey="goToMyAccountPage"/> + <amOnPage stepKey="goToMyAccountPage" url="/customer/account/"/> <!--Assert That Shipping And Billing Address are the same--> <actionGroup ref="AssertThatShippingAndBillingAddressTheSame" stepKey="assertThatShippingAndBillingAddressTheSame"/> <after> <!--Delete created Product--> - <amOnPage url="/admin" stepKey="GoToDashboard"/> - <actionGroup ref="GoToProductPage" stepKey="againGoToProductPage"/> - <actionGroup ref="DeleteCreatedProduct" stepKey="deleteCreatedProduct"/> - - <!--Delete created Customer--> - <actionGroup ref="DeleteCreatedCustomerActionGroup" stepKey="deleteCreatedCustomer"/> + <deleteData stepKey="deleteProduct" createDataKey="product"/> + <deleteData stepKey="deleteCategory" createDataKey="category"/> </after> </test> </tests> From 8b657196364dc5523e2652bdb23d311493bb8c4b Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Fri, 24 Aug 2018 10:10:50 -0500 Subject: [PATCH 1133/1171] MC-71: Admin should be able to apply the catalog rule by customer group --- .../Test/AdminCreateCatalogPriceRuleTest.xml | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml index e4aed558ccea0..befe0b0ce7f98 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml @@ -133,4 +133,58 @@ </actionGroup> </after> </test> + + <test name="AdminCreateCatalogPriceRuleForCustomerGroupTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Apply catalog price rule"/> + <title value="Admin should be able to apply the catalog rule by customer group"/> + <description value="Admin should be able to apply the catalog rule by customer group"/> + <severity value="MAJOR"/> + <testCaseId value="MC-71"/> + <group value="CatalogRule"/> + </annotations> + <before> + <!-- Create a simple product and a category--> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Delete the simple product and category --> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete the catalog rule --> + <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToRulePage"/> + <waitForPageLoad stepKey="waitForRulePage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule"> + <argument name="name" value="{{_defaultCatalogRule.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create a catalog rule for the NOT LOGGED IN customer group --> + <actionGroup ref="newCatalogPriceRuleByUI" stepKey="createNewPriceRule"/> + <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccess"/> + + <!-- As a NOT LOGGED IN user, go to the storefront category page and should see the discount --> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory1"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createProduct.name$$" stepKey="seeProduct1"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$110.70" stepKey="seeDiscountedPrice1"/> + + <!-- Create a user account --> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="createAnAccount"> + <argument name="Customer" value="CustomerEntityOne"/> + </actionGroup> + + <!-- As a logged in user, go to the storefront category page and should NOT see discount --> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory2"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createProduct.name$$" stepKey="seeProduct2"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$123.00" stepKey="seeDiscountedPrice2"/> + </test> </tests> From 14ab8ace12f0e95a476675548d0712d5ecaf9a26 Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Sat, 25 Aug 2018 23:56:53 +0530 Subject: [PATCH 1134/1171] Fix the special price expression --- .../Product/Indexer/Price/Query/BaseFinalPrice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php index 8428ff3688b28..0005ac8dea58a 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php @@ -190,7 +190,7 @@ public function getQuery(array $dimensions, string $productType, array $entityId $specialFromExpr = "{$specialFrom} IS NULL OR {$specialFromDate} <= {$currentDate}"; $specialToExpr = "{$specialTo} IS NULL OR {$specialToDate} >= {$currentDate}"; $specialPriceExpr = $connection->getCheckSql( - "{$specialPrice} IS NOT NULL AND {$specialFromExpr} AND {$specialToExpr}", + "{$specialPrice} IS NOT NULL AND ({$specialFromExpr}) AND ({$specialToExpr})", $specialPrice, $maxUnsignedBigint ); From 5ca1f9cd74c9a686d3ece49d56a456153849deeb Mon Sep 17 00:00:00 2001 From: denistrator <denistrator@yandex.ua> Date: Sun, 22 Jul 2018 14:16:37 +0300 Subject: [PATCH 1135/1171] Adjust page-main container height for sticky footer; fixes #15118 --- .../Magento_Theme/web/css/source/_module.less | 20 +++++++++---------- .../Magento_Theme/web/css/source/_module.less | 19 +++++++++--------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less index f1e1529d9820f..8df5556e97721 100644 --- a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less @@ -52,6 +52,16 @@ .lib-css(background-color, @page__background-color); } + .page-wrapper { + .lib-vendor-prefix-display(flex); + .lib-vendor-prefix-flex-direction(column); + min-height: 100vh; // Stretch content area for sticky footer + } + + .page-main { + .lib-vendor-prefix-flex-grow(1); + } + // // Header // --------------------------------------------- @@ -279,17 +289,7 @@ // _____________________________________________ .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { - - html, - body { - height: 100%; // Stretch screen area for sticky footer - } - .page-wrapper { - .lib-vendor-prefix-display(flex); - .lib-vendor-prefix-flex-direction(column); - min-height: 100%; // Stretch content area for sticky footer - > .breadcrumbs, > .top-container, > .widget { diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less index af6cfa8605f6d..f5c5ddeb5f105 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less @@ -67,6 +67,16 @@ .lib-css(background-color, @page__background-color); } + .page-wrapper { + .lib-vendor-prefix-display(flex); + .lib-vendor-prefix-flex-direction(column); + min-height: 100vh; // Stretch content area for sticky footer + } + + .page-main { + .lib-vendor-prefix-flex-grow(1); + } + // // Header // --------------------------------------------- @@ -411,12 +421,6 @@ } } } - .page-footer, - .copyright { - bottom: 0; - position: absolute; - width: 100%; - } } .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) { @@ -614,10 +618,7 @@ } .page-wrapper { - .lib-vendor-prefix-display(flex); - .lib-vendor-prefix-flex-direction(column); margin: 0; - min-height: 100%; // Stretch content area for sticky footer position: relative; transition: margin .3s ease-out 0s; From 75cfe1eb9ee5cd67514f4285ba5fa9d03a654a11 Mon Sep 17 00:00:00 2001 From: Iryna Stiahailo <irynastagaylo@gmail.com> Date: Wed, 13 Jun 2018 12:08:03 +0300 Subject: [PATCH 1136/1171] Fix "escapeNotVerified". --- .../templates/items/column/name.phtml | 24 ++++++++------- .../templates/items/column/qty.phtml | 30 +++++++++---------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml b/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml index 30037a918a10c..ab66cc0e42ad6 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml @@ -14,32 +14,34 @@ ?> <?php if ($_item = $block->getItem()): ?> - <div id="order_item_<?= /* @escapeNotVerified */ $_item->getId() ?>_title" + <div id="order_item_<?= $block->escapeHtml($_item->getId()) ?>_title" class="product-title"> <?= $block->escapeHtml($_item->getName()) ?> </div> - <div class="product-sku-block"> - <span><?= /* @escapeNotVerified */ __('SKU') ?>:</span> <?= implode('<br />', $this->helper('Magento\Catalog\Helper\Data')->splitSku($block->escapeHtml($block->getSku()))) ?> + <span><?= $block->escapeHtml(__('SKU'))?>:</span> <?= implode('<br />', $this->helper('Magento\Catalog\Helper\Data')->splitSku($block->escapeHtml($block->getSku()))) ?> </div> <?php if ($block->getOrderOptions()): ?> <dl class="item-options"> <?php foreach ($block->getOrderOptions() as $_option): ?> - <dt><?= /* @escapeNotVerified */ $_option['label'] ?>:</dt> + <dt><?= $block->escapeHtml($_option['label']) ?>:</dt> <dd> <?php if (isset($_option['custom_view']) && $_option['custom_view']): ?> - <?= /* @escapeNotVerified */ $block->getCustomizedOptionValue($_option) ?> + <?= $block->escapeHtml($block->getCustomizedOptionValue($_option)) ?> <?php else: ?> <?php $_option = $block->getFormattedOption($_option['value']); ?> - <?= $block->escapeHtml($_option['value']) ?><?php if (isset($_option['remainder']) && $_option['remainder']): ?><span id="<?= /* @escapeNotVerified */ $_dots = 'dots' . uniqid() ?>"> ...</span><span id="<?= /* @escapeNotVerified */ $_id = 'id' . uniqid() ?>"><?= /* @escapeNotVerified */ $_option['remainder'] ?></span> + <?php $dots = 'dots' . uniqid(); ?> + <?= $block->escapeHtml($_option['value']) ?><?php if (isset($_option['remainder']) && $_option['remainder']): ?> <span id="<?= /* @noEscape */ $dots; ?>"> ...</span> + <?php $id = 'id' . uniqid(); ?> + <span id="<?= /* @noEscape */ $id; ?>"><?= $block->escapeHtml($_option['remainder']) ?></span> <script> require(['prototype'], function() { - $('<?= /* @escapeNotVerified */ $_id ?>').hide(); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseover', function(){$('<?= /* @escapeNotVerified */ $_id ?>').show();}); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseover', function(){$('<?= /* @escapeNotVerified */ $_dots ?>').hide();}); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseout', function(){$('<?= /* @escapeNotVerified */ $_id ?>').hide();}); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseout', function(){$('<?= /* @escapeNotVerified */ $_dots ?>').show();}); + $('<?= /* @noEscape */ $id; ?>').hide(); + $('<?= /* @noEscape */ $id; ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $id; ?>').show();}); + $('<?= /* @noEscape */ $id; ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $dots; ?>').hide();}); + $('<?= /* @noEscape */ $id; ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $id; ?>').hide();}); + $('<?= /* @noEscape */ $id; ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $dots; ?>').show();}); }); </script> <?php endif; ?> diff --git a/app/code/Magento/Sales/view/adminhtml/templates/items/column/qty.phtml b/app/code/Magento/Sales/view/adminhtml/templates/items/column/qty.phtml index faa49dca3a8eb..9bf4856795197 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/items/column/qty.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/items/column/qty.phtml @@ -7,38 +7,38 @@ // @codingStandardsIgnoreFile ?> -<?php if ($_item = $block->getItem()): ?> +<?php if ($item = $block->getItem()): ?> <table class="qty-table"> <tr> - <th><?= /* @escapeNotVerified */ __('Ordered') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getQtyOrdered()*1 ?></td> + <th><?= $block->escapeHtml(__('Ordered')); ?></th> + <td><?= /* @noEscape */ $item->getQtyOrdered()*1 ?></td> </tr> - <?php if ((float) $_item->getQtyInvoiced()): ?> + <?php if ((float) $item->getQtyInvoiced()): ?> <tr> - <th><?= /* @escapeNotVerified */ __('Invoiced') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getQtyInvoiced()*1 ?></td> + <th><?= $block->escapeHtml(__('Invoiced')); ?></th> + <td><?= /* @noEscape */ $item->getQtyInvoiced()*1 ?></td> </tr> <?php endif; ?> - <?php if ((float) $_item->getQtyShipped()): ?> + <?php if ((float) $item->getQtyShipped()): ?> <tr> - <th><?= /* @escapeNotVerified */ __('Shipped') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getQtyShipped()*1 ?></td> + <th><?= $block->escapeHtml(__('Shipped')); ?></th> + <td><?= /* @noEscape */ $item->getQtyShipped()*1 ?></td> </tr> <?php endif; ?> - <?php if ((float) $_item->getQtyRefunded()): ?> + <?php if ((float) $item->getQtyRefunded()): ?> <tr> - <th><?= /* @escapeNotVerified */ __('Refunded') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getQtyRefunded()*1 ?></td> + <th><?= $block->escapeHtml(__('Refunded')); ?></th> + <td><?= /* @noEscape */ $item->getQtyRefunded()*1 ?></td> </tr> <?php endif; ?> - <?php if ((float) $_item->getQtyCanceled()): ?> + <?php if ((float) $item->getQtyCanceled()): ?> <tr> - <th><?= /* @escapeNotVerified */ __('Canceled') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getQtyCanceled()*1 ?></td> + <th><?= $block->escapeHtml(__('Canceled')); ?></th> + <td><?= /* @noEscape */ $item->getQtyCanceled()*1 ?></td> </tr> <?php endif; ?> From 230f43b490d6dc7976fc38cbb120e66edfbc62d3 Mon Sep 17 00:00:00 2001 From: Jignesh Baldha <iamjignesh.b@gmail.com> Date: Mon, 27 Aug 2018 15:47:03 +0530 Subject: [PATCH 1137/1171] Removed unnecessary characters from comments --- .../Model/Product/Type/Configurable/Attribute.php | 7 ++++--- .../CustomerImportExport/Model/Export/Customer.php | 14 ++++---------- app/code/Magento/Multishipping/Helper/Data.php | 11 +++++++---- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php index 617297e545b7d..7306942c3c49b 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php @@ -18,7 +18,7 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel implements \Magento\ConfigurableProduct\Api\Data\OptionInterface { - /**#@+ + /** * Constants for field names */ const KEY_ATTRIBUTE_ID = 'attribute_id'; @@ -27,9 +27,10 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme const KEY_IS_USE_DEFAULT = 'is_use_default'; const KEY_VALUES = 'values'; const KEY_PRODUCT_ID = 'product_id'; - /**#@-*/ - /**#@-*/ + /** + * @var MetadataPool|\Magento\Framework\EntityManager\MetadataPool + */ private $metadataPool; /** diff --git a/app/code/Magento/CustomerImportExport/Model/Export/Customer.php b/app/code/Magento/CustomerImportExport/Model/Export/Customer.php index 14f0ae324e0a4..26576e31a3118 100644 --- a/app/code/Magento/CustomerImportExport/Model/Export/Customer.php +++ b/app/code/Magento/CustomerImportExport/Model/Export/Customer.php @@ -15,7 +15,7 @@ */ class Customer extends \Magento\ImportExport\Model\Export\Entity\AbstractEav { - /**#@+ + /** * Permanent column names. * * Names that begins with underscore is not an attribute. This name convention is for @@ -27,23 +27,17 @@ class Customer extends \Magento\ImportExport\Model\Export\Entity\AbstractEav const COLUMN_STORE = '_store'; - /**#@-*/ - - /**#@+ + /** * Attribute collection name */ const ATTRIBUTE_COLLECTION_NAME = \Magento\Customer\Model\ResourceModel\Attribute\Collection::class; - /**#@-*/ - - /**#@+ + /** * XML path to page size parameter */ const XML_PATH_PAGE_SIZE = 'export/customer_page_size/customer'; - /**#@-*/ - - /**#@-*/ + /** protected $_attributeOverrides = [ 'created_at' => ['backend_type' => 'datetime'], 'reward_update_notification' => ['source_model' => \Magento\Eav\Model\Entity\Attribute\Source\Boolean::class], diff --git a/app/code/Magento/Multishipping/Helper/Data.php b/app/code/Magento/Multishipping/Helper/Data.php index c9b36e34cb304..c7f552ac9236a 100644 --- a/app/code/Magento/Multishipping/Helper/Data.php +++ b/app/code/Magento/Multishipping/Helper/Data.php @@ -11,16 +11,19 @@ */ class Data extends \Magento\Framework\App\Helper\AbstractHelper { - /**#@+ + /* * Xml paths for multishipping checkout + * **/ const XML_PATH_CHECKOUT_MULTIPLE_AVAILABLE = 'multishipping/options/checkout_multiple'; const XML_PATH_CHECKOUT_MULTIPLE_MAXIMUM_QUANTITY = 'multishipping/options/checkout_multiple_maximum_qty'; - /**#@-*/ - - /**#@-*/ + /** + * Checkout session + * + * @var \Magento\Checkout\Model\Session + */ protected $checkoutSession; /** From 7e19f48f09716901c1ba216ea6f87f901caf254a Mon Sep 17 00:00:00 2001 From: Jignesh Baldha <iamjignesh.b@gmail.com> Date: Mon, 27 Aug 2018 15:57:42 +0530 Subject: [PATCH 1138/1171] Removed unnecessary characters from comments #2 --- app/code/Magento/CustomerImportExport/Model/Export/Customer.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/CustomerImportExport/Model/Export/Customer.php b/app/code/Magento/CustomerImportExport/Model/Export/Customer.php index 26576e31a3118..9ea9e1e5bd93d 100644 --- a/app/code/Magento/CustomerImportExport/Model/Export/Customer.php +++ b/app/code/Magento/CustomerImportExport/Model/Export/Customer.php @@ -38,6 +38,8 @@ class Customer extends \Magento\ImportExport\Model\Export\Entity\AbstractEav const XML_PATH_PAGE_SIZE = 'export/customer_page_size/customer'; /** + * @var array + */ protected $_attributeOverrides = [ 'created_at' => ['backend_type' => 'datetime'], 'reward_update_notification' => ['source_model' => \Magento\Eav\Model\Entity\Attribute\Source\Boolean::class], From a8dabd366805d669d50465b7c9f20802b88ee8ce Mon Sep 17 00:00:00 2001 From: Timon de Groot <tdegroot96@gmail.com> Date: Mon, 27 Aug 2018 13:39:55 +0300 Subject: [PATCH 1139/1171] Fill visibility in AdminConfigurableProductCreateTest.xml --- .../Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml index 63ef6cb99f8c1..7c16630b17ebd 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml @@ -60,6 +60,7 @@ <fillField userInput="{{product.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> <fillField userInput="{{product.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="fillCategory"/> + <selectOption userInput="{{product.visibility}}" selector="{{AdminProductFormSection.visibility}}" stepKey="fillVisibility"/> <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> <fillField userInput="{{product.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> From 3c499dbb45fc86dcb25052f4bc01d78afe6b173d Mon Sep 17 00:00:00 2001 From: Keith Bentrup <kbentrup@magento.com> Date: Mon, 27 Aug 2018 14:50:37 +0300 Subject: [PATCH 1140/1171] declare var to fix scope error --- .../adminhtml/web/js/category-checkbox-tree.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/category-checkbox-tree.js b/app/code/Magento/Catalog/view/adminhtml/web/js/category-checkbox-tree.js index bc44128663cd0..2359d1499f834 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/category-checkbox-tree.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/category-checkbox-tree.js @@ -33,7 +33,6 @@ define([ data = {}, parameters = {}, root = {}, - len = 0, key = ''; /** @@ -160,15 +159,15 @@ define([ * @returns {void} */ categoryLoader.buildCategoryTree = function (parent, nodeConfig) { - var j = 0; + var i = 0; if (!nodeConfig) { return null; } if (parent && nodeConfig && nodeConfig.length) { - for (j = 0; j < nodeConfig.length; j++) { - categoryLoader.processCategoryTree(parent, nodeConfig, j); + for (i; i < nodeConfig.length; i++) { + categoryLoader.processCategoryTree(parent, nodeConfig, i); } } }; @@ -180,14 +179,15 @@ define([ * @returns {Object} */ categoryLoader.buildHashChildren = function (hash, node) { - var j = 0; + var i = 0, + len; if (node.childNodes.length > 0 || node.loaded === false && node.loading === false) { hash.children = []; - for (j = 0, len = node.childNodes.length; j < len; j++) { + for (i, len = node.childNodes.length; i < len; i++) { hash.children = hash.children ? hash.children : []; - hash.children.push(this.buildHash(node.childNodes[j])); + hash.children.push(this.buildHash(node.childNodes[i])); } } From 9cd5924ebb221c4a99f1399fbc4d363018c52978 Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@adobe.com> Date: Mon, 27 Aug 2018 10:12:45 -0500 Subject: [PATCH 1141/1171] MAGETWO-93979: Required Field Indicators (Asterisks) Are Gone From Magento UI - added functional test --- .../AdminCategoryBasicFieldSection.xml | 4 ++ .../Mftf/Section/AdminProductFormSection.xml | 2 + ...edFieldsHaveRequiredFieldIndicatorTest.xml | 48 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml index 3ed3763da19d6..f573e3b8d02e7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml @@ -16,9 +16,13 @@ <element name="enableCategoryLabel" type="text" selector="input[name='is_active']+label"/> <element name="enableUseDefault" type="checkbox" selector="input[name='use_default[is_active]']"/> <element name="CategoryNameInput" type="input" selector="input[name='name']"/> + <!--<element name="RequiredFieldIndicator" type="text" selector='._required[data-index="{{arg1}}"] > .admin__field-label span' parameterized="true"/>--> + <element name="RequiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('content');"/> + <element name="RequiredFieldIndicatorColor" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('color');"/> <element name="categoryNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> <element name="ContentTab" type="input" selector="input[name='name']"/> <element name="FieldError" type="text" selector=".admin__field-error[data-bind='attr: {for: {{field}}}, text: error']" parameterized="true"/> + <element name="panelFieldControl" type="input" selector='//aside//div[@data-index="{{arg1}}"]/descendant::*[@name="{{arg2}}"]' parameterized="true"/> </section> <section name="CategoryContentSection"> <element name="SelectFromGalleryBtn" type="button" selector="//label[text()='Select from Gallery']"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 9b11e2a874811..bf3bb14492ec6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -12,6 +12,8 @@ <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> + <element name="RequiredNameIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('content');"/> + <element name="RequiredSkuIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=sku]>.admin__field-label span'), ':after').getPropertyValue('content');"/> <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> <element name="enableProductAttributeLabel" type="text" selector="//span[text()='Enable Product']/parent::label"/> <element name="enableProductAttributeLabelWrapper" type="text" selector="//span[text()='Enable Product']/parent::label/parent::div"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml new file mode 100644 index 0000000000000..d295ad872589b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminRequiredFieldsHaveRequiredFieldIndicatorTest"> + <annotations> + <title value="Required fields should have the required asterisk indicator "/> + <description value="Verify that Required fields should have the required indicator icon next to the field name"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-93979"/> + <group value="Catalog"/> + </annotations> + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> + <waitForElementVisible selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="waitForAddSubCategoryVisible"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> + + <!-- Verify that the Category Name field has the required field name indicator --> + <!--<executeJS function="window.getComputedStyle(document.querySelector('{{AdminCategoryBasicFieldSection.RequiredFieldIndicator('name')}}'), ':after').getPropertyValue('color');" stepKey="getRequiredFieldIndicator"/>--> + <executeJS function="{{AdminCategoryBasicFieldSection.RequiredFieldIndicator}}" stepKey="getRequiredFieldIndicator"/> + <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="getRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator1"/> + + <executeJS function="{{AdminCategoryBasicFieldSection.RequiredFieldIndicatorColor}}" stepKey="getRequiredFieldIndicatorColor"/> + <assertEquals expected="rgb(226, 38, 38)" expectedType="string" actualType="variable" actual="getRequiredFieldIndicatorColor" message="pass" stepKey="assertRequiredFieldIndicator2"/> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPage"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="addProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="addSimpleProduct"/> + <!-- Verify that the Product Name and Sku fields have the required field name indicator --> + <executeJS function="{{AdminProductFormSection.RequiredNameIndicator}}" stepKey="productNameRequiredFieldIndicator"/> + <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="productNameRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator3"/> + + <executeJS function="{{AdminProductFormSection.RequiredSkuIndicator}}" stepKey="productSkuRequiredFieldIndicator"/> + <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="productSkuRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator4"/> + + + </test> +</tests> From 06e86f61d3fb54cf39cfee641a9e76f454c1fe93 Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@adobe.com> Date: Mon, 27 Aug 2018 10:57:19 -0500 Subject: [PATCH 1142/1171] MAGETWO-93979: Required Field Indicators (Asterisks) Are Gone From Magento UI - updated annotations --- ...nRequiredFieldsHaveRequiredFieldIndicatorTest.xml | 12 +++++++++--- .../Section/CmsNewPagePageBasicFieldsSection.xml | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml index d295ad872589b..240a5492355cf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml @@ -10,10 +10,11 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminRequiredFieldsHaveRequiredFieldIndicatorTest"> <annotations> + <stories value="Verify the presence of required field indicators across different pages in Magento Admin"/> <title value="Required fields should have the required asterisk indicator "/> <description value="Verify that Required fields should have the required indicator icon next to the field name"/> <severity value="MAJOR"/> - <testCaseId value="MAGETWO-93979"/> + <testCaseId value="MAGETWO-94330"/> <group value="Catalog"/> </annotations> <after> @@ -26,7 +27,6 @@ <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> <!-- Verify that the Category Name field has the required field name indicator --> - <!--<executeJS function="window.getComputedStyle(document.querySelector('{{AdminCategoryBasicFieldSection.RequiredFieldIndicator('name')}}'), ':after').getPropertyValue('color');" stepKey="getRequiredFieldIndicator"/>--> <executeJS function="{{AdminCategoryBasicFieldSection.RequiredFieldIndicator}}" stepKey="getRequiredFieldIndicator"/> <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="getRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator1"/> @@ -36,13 +36,19 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPage"/> <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="addProductDropdown"/> <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="addSimpleProduct"/> + <!-- Verify that the Product Name and Sku fields have the required field name indicator --> <executeJS function="{{AdminProductFormSection.RequiredNameIndicator}}" stepKey="productNameRequiredFieldIndicator"/> <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="productNameRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator3"/> - <executeJS function="{{AdminProductFormSection.RequiredSkuIndicator}}" stepKey="productSkuRequiredFieldIndicator"/> <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="productSkuRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator4"/> + <!-- Verify that the CMS page have the required field name indicator next to Page Title --> + <amOnPage url="{{CmsPagesPage.url}}" stepKey="amOnPagePagesGrid"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click selector="{{CmsPagesPageActionsSection.addNewPageButton}}" stepKey="clickAddNewPage"/> + <executeJS function="{{CmsNewPagePageBasicFieldsSection.RequiredFieldIndicator}}" stepKey="pageTitleRequiredFieldIndicator"/> + <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="pageTitleRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator5"/> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml index 468dbecb20e02..5570c06b1e2bf 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml @@ -10,6 +10,7 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="CmsNewPagePageBasicFieldsSection"> <element name="pageTitle" type="input" selector="input[name=title]"/> + <element name="RequiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=title]>.admin__field-label span'), ':after').getPropertyValue('content');"/> <element name="isActive" type="button" selector="//input[@name='is_active' and @value='{{var1}}']" parameterized="true"/> <element name="duplicatedURLKey" type="input" selector="//input[contains(@data-value,'{{var1}}')]" parameterized="true"/> </section> From c2bab17bc9d34984e815016dc53391669d5bdb89 Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@adobe.com> Date: Mon, 27 Aug 2018 13:22:35 -0500 Subject: [PATCH 1143/1171] MAGETWO-93979: Required Field Indicators (Asterisks) Are Gone From Magento UI - removed commented out line --- .../Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml index f573e3b8d02e7..e82159afcd99a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml @@ -16,7 +16,6 @@ <element name="enableCategoryLabel" type="text" selector="input[name='is_active']+label"/> <element name="enableUseDefault" type="checkbox" selector="input[name='use_default[is_active]']"/> <element name="CategoryNameInput" type="input" selector="input[name='name']"/> - <!--<element name="RequiredFieldIndicator" type="text" selector='._required[data-index="{{arg1}}"] > .admin__field-label span' parameterized="true"/>--> <element name="RequiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('content');"/> <element name="RequiredFieldIndicatorColor" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('color');"/> <element name="categoryNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> From 4fcaf1df2b0b31f16a68795f2a8f3ac059d96f76 Mon Sep 17 00:00:00 2001 From: Willian Keller <wkeller@ciandt.com> Date: Mon, 27 Aug 2018 16:04:08 -0300 Subject: [PATCH 1144/1171] Improve array semicolon and misspelled --- .../Catalog/Model/Indexer/Product/Category/Action/Rows.php | 4 ++-- app/code/Magento/Catalog/Model/ResourceModel/Category.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Category/Action/Rows.php b/app/code/Magento/Catalog/Model/Indexer/Product/Category/Action/Rows.php index 182f04de4ab0e..a218266c25034 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Category/Action/Rows.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Category/Action/Rows.php @@ -161,7 +161,7 @@ protected function removeEntries() $this->getIndexTable($store->getId()), ['product_id IN (?)' => $this->limitationByProducts] ); - }; + } } /** @@ -228,7 +228,7 @@ private function getCategoryIdsFromIndex(array $productIds) ->distinct() ) ); - }; + } $parentCategories = $categoryIds; foreach ($categoryIds as $categoryId) { $parentIds = explode('/', $this->getPathFromCategoryId($categoryId)); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php index fa68ae3f865ef..9de0e8a849046 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php @@ -490,7 +490,7 @@ public function getProductsPosition($category) } /** - * Get chlden categories count + * Get children categories count * * @param int $categoryId * @return int From d42605a2746cdac54038abf4b8e0b98f75b0c43a Mon Sep 17 00:00:00 2001 From: Devagouda Patil <depatil@Devagoudas-MacBook-Pro.local> Date: Mon, 27 Aug 2018 15:37:29 -0500 Subject: [PATCH 1145/1171] MAGETWO-93978: Footer Overlaps Storefront Page Content In Mobile View - Added functional test to cover bug fix --- .../Cms/Test/Mftf/Data/CmsPageData.xml | 6 +++ .../Mftf/Section/StorefrontCMSPageSection.xml | 4 ++ .../Test/StoreFrontMobileViewValidation.xml | 43 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml diff --git a/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml b/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml index 84561d6cd96ca..aeb8d12ae7744 100644 --- a/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml +++ b/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml @@ -80,4 +80,10 @@ <entity name="ImageFolder" type="uploadImage"> <data key="name" unique="suffix">Test</data> </entity> + <entity name="_longContentCmsPage" type="cms_page"> + <data key="title">Test CMS Page</data> + <data key="content_heading">Test Content Heading</data> + <data key="content">Sample long page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.</data> + <data key="identifier" unique="suffix">test-page-</data> + </entity> </entities> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml index d7c0a41464d21..9ab173e2c7252 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml @@ -12,5 +12,9 @@ <element name="mediaDescription" type="text" selector=".column.main>p>img"/> <element name="imageSource" type="text" selector="//img[contains(@src,'{{var1}}')]" parameterized="true"/> <element name="mainTitle" type="text" selector="#maincontent .page-title"/> + <element name="mainContent" type="text" selector="#maincontent"/> + </section> + <section name="StorefrontCMSPageFooterSection"> + <element name="footerSection" type="text" selector="footer.page-footer"/> </section> </sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml new file mode 100644 index 0000000000000..542ef668d5834 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StoreFrontMobileViewValidation"> + <annotations> + <features value="Cms"/> + <title value="Mobile view page footer should stick to the bottom of page on Store front"/> + <description value="Mobile view page footer should stick to the bottom of page on Store front"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-94333"/> + <group value="Cms12"/> + </annotations> + <before> + <createData entity="_longContentCmsPage" stepKey="createPreReqCMSPage"/> + </before> + <after> + <deleteData createDataKey="createPreReqCMSPage" stepKey="deletePreReqCMSPage"/> + </after> + <resizeWindow width="375" height="812" stepKey="resizeWindowToDesktop"/> + <amOnPage url="$$createPreReqCMSPage.identifier$$" stepKey="amOnPageTestPage"/> + <waitForPageLoad stepKey="waitForPageLoad6" /> + <!--check header/footer location on Storefront--> + <executeJS function="return document.querySelector('{{StorefrontCMSPageFooterSection.footerSection}}').getBoundingClientRect().top" stepKey="topOfFooter"/> + <assertGreaterThan stepKey="assertDefaultLoad"> + <actualResult type="variable">topOfFooter</actualResult> + <expectedResult type="string">812</expectedResult> + </assertGreaterThan> + <scrollTo selector="{{StorefrontCMSPageFooterSection.footerSection}}" stepKey="scrollToFooterSection"/> + <executeJS function="return document.querySelector('{{StorefrontCMSPageFooterSection.footerSection}}').getBoundingClientRect().top" stepKey="bottomOfFooter"/> + <executeJS function="return document.querySelector('{{StorefrontCMSPageSection.mainContent}}').getBoundingClientRect().bottom" stepKey="mainContent"/> + <assertGreaterThan stepKey="assertAfterScroll"> + <actualResult type="variable">bottomOfFooter</actualResult> + <expectedResult type="variable">mainContent</expectedResult> + </assertGreaterThan> + </test> +</tests> From 3b4deb378b8bda856a96a9b080bc7244eb76e031 Mon Sep 17 00:00:00 2001 From: Devagouda Patil <depatil@Devagoudas-MacBook-Pro.local> Date: Mon, 27 Aug 2018 15:41:03 -0500 Subject: [PATCH 1146/1171] MAGETWO-93978: Footer Overlaps Storefront Page Content In Mobile View - Updated Functional test group value --- .../Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml index 542ef668d5834..aa987b10cb170 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml @@ -15,7 +15,7 @@ <description value="Mobile view page footer should stick to the bottom of page on Store front"/> <severity value="MAJOR"/> <testCaseId value="MAGETWO-94333"/> - <group value="Cms12"/> + <group value="Cms"/> </annotations> <before> <createData entity="_longContentCmsPage" stepKey="createPreReqCMSPage"/> From c7aa331fe03b00a012f349538bd59d9e40850be1 Mon Sep 17 00:00:00 2001 From: Devagouda Patil <depatil@Devagoudas-MacBook-Pro.local> Date: Mon, 27 Aug 2018 16:46:06 -0500 Subject: [PATCH 1147/1171] MAGETWO-93978: Footer Overlaps Storefront Page Content In Mobile View - Incorporated review comments of functional test --- .../Mftf/Test/StoreFrontMobileViewValidation.xml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml index aa987b10cb170..2558f3f27971d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml @@ -15,6 +15,7 @@ <description value="Mobile view page footer should stick to the bottom of page on Store front"/> <severity value="MAJOR"/> <testCaseId value="MAGETWO-94333"/> + <useCaseId value="MAGETWO-93978"/> <group value="Cms"/> </annotations> <before> @@ -22,22 +23,24 @@ </before> <after> <deleteData createDataKey="createPreReqCMSPage" stepKey="deletePreReqCMSPage"/> + <resizeWindow width="1280" height="1024" stepKey="resizeWindowToDesktop"/> </after> - <resizeWindow width="375" height="812" stepKey="resizeWindowToDesktop"/> + <resizeWindow width="375" height="812" stepKey="resizeWindowToMobile"/> <amOnPage url="$$createPreReqCMSPage.identifier$$" stepKey="amOnPageTestPage"/> <waitForPageLoad stepKey="waitForPageLoad6" /> - <!--check header/footer location on Storefront--> + <!-- Verifying that Footer is not in visible area by default as the CMS page has lots of content which --> <executeJS function="return document.querySelector('{{StorefrontCMSPageFooterSection.footerSection}}').getBoundingClientRect().top" stepKey="topOfFooter"/> <assertGreaterThan stepKey="assertDefaultLoad"> <actualResult type="variable">topOfFooter</actualResult> <expectedResult type="string">812</expectedResult> </assertGreaterThan> + <!-- Verifying that even after scroll footer section is below the main content section --> <scrollTo selector="{{StorefrontCMSPageFooterSection.footerSection}}" stepKey="scrollToFooterSection"/> - <executeJS function="return document.querySelector('{{StorefrontCMSPageFooterSection.footerSection}}').getBoundingClientRect().top" stepKey="bottomOfFooter"/> - <executeJS function="return document.querySelector('{{StorefrontCMSPageSection.mainContent}}').getBoundingClientRect().bottom" stepKey="mainContent"/> + <executeJS function="return document.querySelector('{{StorefrontCMSPageFooterSection.footerSection}}').getBoundingClientRect().top" stepKey="topOfTheFooterAfterScroll"/> + <executeJS function="return document.querySelector('{{StorefrontCMSPageSection.mainContent}}').getBoundingClientRect().bottom" stepKey="bottomOfMainContent"/> <assertGreaterThan stepKey="assertAfterScroll"> - <actualResult type="variable">bottomOfFooter</actualResult> - <expectedResult type="variable">mainContent</expectedResult> + <actualResult type="variable">topOfTheFooterAfterScroll</actualResult> + <expectedResult type="variable">bottomOfMainContent</expectedResult> </assertGreaterThan> </test> </tests> From b112eb5336a4421c71410a81c3d21ad52e23f23f Mon Sep 17 00:00:00 2001 From: Devagouda Patil <depatil@Devagoudas-MacBook-Pro.local> Date: Mon, 27 Aug 2018 16:50:48 -0500 Subject: [PATCH 1148/1171] MAGETWO-93978: Footer Overlaps Storefront Page Content In Mobile View - Updated comment section of MFTF test --- .../Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml index 2558f3f27971d..dc7fec83c41dd 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml @@ -28,7 +28,7 @@ <resizeWindow width="375" height="812" stepKey="resizeWindowToMobile"/> <amOnPage url="$$createPreReqCMSPage.identifier$$" stepKey="amOnPageTestPage"/> <waitForPageLoad stepKey="waitForPageLoad6" /> - <!-- Verifying that Footer is not in visible area by default as the CMS page has lots of content which --> + <!-- Verifying that Footer is not in visible area by default as the CMS page has lots of content which will occupy entire visible area--> <executeJS function="return document.querySelector('{{StorefrontCMSPageFooterSection.footerSection}}').getBoundingClientRect().top" stepKey="topOfFooter"/> <assertGreaterThan stepKey="assertDefaultLoad"> <actualResult type="variable">topOfFooter</actualResult> From 2cba91293595947b4dec2e5f2eb19dcbe12f255f Mon Sep 17 00:00:00 2001 From: Devagouda Patil <depatil@ip-192-168-0-7.ec2.internal> Date: Mon, 27 Aug 2018 23:48:02 -0500 Subject: [PATCH 1149/1171] MAGETWO-93978: Footer Overlaps Storefront Page Content In Mobile View - Review comments incorporated for Function test --- app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml | 2 +- .../Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml | 4 +--- .../Test/Mftf/Test/StoreFrontMobileViewValidation.xml | 10 +++++----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml b/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml index aeb8d12ae7744..eef2f15d266aa 100644 --- a/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml +++ b/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml @@ -83,7 +83,7 @@ <entity name="_longContentCmsPage" type="cms_page"> <data key="title">Test CMS Page</data> <data key="content_heading">Test Content Heading</data> - <data key="content">Sample long page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada. Sample page content. Yada yada yada.</data> + <data key="content">1<br/>2<br/>3<br/>4<br/>5<br/>6<br/>7<br/>8<br/>9<br/>10<br/>11<br/>12<br/>13<br/>14<br/>15<br/>16<br/>17<br/>18<br/>19<br/>20<br/>line21<br/>22<br/>23<br/>24<br/>25<br/>26<br/>line27<br/>2<br/>3<br/>4<br/>5</data> <data key="identifier" unique="suffix">test-page-</data> </entity> </entities> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml index 9ab173e2c7252..99e3e9a24ee5a 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml @@ -13,8 +13,6 @@ <element name="imageSource" type="text" selector="//img[contains(@src,'{{var1}}')]" parameterized="true"/> <element name="mainTitle" type="text" selector="#maincontent .page-title"/> <element name="mainContent" type="text" selector="#maincontent"/> - </section> - <section name="StorefrontCMSPageFooterSection"> - <element name="footerSection" type="text" selector="footer.page-footer"/> + <element name="footerTop" type="text" selector="footer.page-footer"/> </section> </sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml index dc7fec83c41dd..d1c44383b4ac1 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml @@ -28,15 +28,15 @@ <resizeWindow width="375" height="812" stepKey="resizeWindowToMobile"/> <amOnPage url="$$createPreReqCMSPage.identifier$$" stepKey="amOnPageTestPage"/> <waitForPageLoad stepKey="waitForPageLoad6" /> - <!-- Verifying that Footer is not in visible area by default as the CMS page has lots of content which will occupy entire visible area--> - <executeJS function="return document.querySelector('{{StorefrontCMSPageFooterSection.footerSection}}').getBoundingClientRect().top" stepKey="topOfFooter"/> + <!--Verifying that Footer is not in visible area by default as the CMS page has lots of content which will occupy entire visible area--> + <executeJS function="return document.querySelector('{{StorefrontCMSPageSection.footerTop}}').getBoundingClientRect().top" stepKey="topOfFooter"/> <assertGreaterThan stepKey="assertDefaultLoad"> <actualResult type="variable">topOfFooter</actualResult> <expectedResult type="string">812</expectedResult> </assertGreaterThan> - <!-- Verifying that even after scroll footer section is below the main content section --> - <scrollTo selector="{{StorefrontCMSPageFooterSection.footerSection}}" stepKey="scrollToFooterSection"/> - <executeJS function="return document.querySelector('{{StorefrontCMSPageFooterSection.footerSection}}').getBoundingClientRect().top" stepKey="topOfTheFooterAfterScroll"/> + <!--Verifying that even after scroll footer section is below the main content section--> + <scrollTo selector="{{StorefrontCMSPageSection.footerTop}}" stepKey="scrollToFooterSection"/> + <executeJS function="return document.querySelector('{{StorefrontCMSPageSection.footerTop}}').getBoundingClientRect().top" stepKey="topOfTheFooterAfterScroll"/> <executeJS function="return document.querySelector('{{StorefrontCMSPageSection.mainContent}}').getBoundingClientRect().bottom" stepKey="bottomOfMainContent"/> <assertGreaterThan stepKey="assertAfterScroll"> <actualResult type="variable">topOfTheFooterAfterScroll</actualResult> From 57110a2d22a1f6f08ce20d4eb1ad104c2a2929ab Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 28 Aug 2018 10:47:45 -0500 Subject: [PATCH 1150/1171] MAGETWO-93305: Broken upgrade to 2.3 due to changed data type for minify_exclude - fix backward incompatibility --- .../Framework/View/Asset/Minification.php | 5 ++++- .../View/Test/Unit/Asset/MinificationTest.php | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Asset/Minification.php b/lib/internal/Magento/Framework/View/Asset/Minification.php index 33c82a1810db6..2742f5a2657b3 100644 --- a/lib/internal/Magento/Framework/View/Asset/Minification.php +++ b/lib/internal/Magento/Framework/View/Asset/Minification.php @@ -162,7 +162,10 @@ public function getExcludes($contentType) private function getMinificationExcludeValues($key) { $configValues = $this->scopeConfig->getValue($key, $this->scope) ?? []; - + //compatibility fix for type change from string to array + if (!is_array($configValues)) { + $configValues = [$configValues => $configValues]; + } return array_values($configValues); } } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php index 1cc2a3dd7e2b7..229c03b2f9b64 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php @@ -195,6 +195,8 @@ public function isMinifiedFilenameDataProvider() } /** + * Test dev/js/minify_exclude system value as array + * * @return void */ public function testGetExcludes() @@ -213,4 +215,23 @@ public function testGetExcludes() /** check cache: */ $this->assertEquals($expected, $this->minification->getExcludes('js')); } + + /** + * Test dev/js/minify_exclude system value backward compatibility when value was a string + * + * @return void + */ + public function testGetExcludesTinyMceAsString() + { + $this->scopeConfigMock + ->expects($this->once()) + ->method('getValue') + ->with('dev/js/minify_exclude') + ->willReturn('/tiny_mce/'); + + $expected = ['/tiny_mce/']; + $this->assertEquals($expected, $this->minification->getExcludes('js')); + /** check cache: */ + $this->assertEquals($expected, $this->minification->getExcludes('js')); + } } From c626a48109731aca9ebaaad2b9f00d3c77a7fef0 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Tue, 28 Aug 2018 10:48:28 -0500 Subject: [PATCH 1151/1171] MAGETWO-94226: Catalog Products, Orders, All Customer page doesnt load (Continuous spinner) on cloud starter --- lib/web/mage/requirejs/resolver.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/web/mage/requirejs/resolver.js b/lib/web/mage/requirejs/resolver.js index 6ff270f5c1a20..27779aca325de 100644 --- a/lib/web/mage/requirejs/resolver.js +++ b/lib/web/mage/requirejs/resolver.js @@ -34,11 +34,7 @@ define([ * @return {Boolean} */ function isRegistered(module) { - if (registry.hasOwnProperty(module.id)) { - return registry[module.id].inited || registry[module.id].error; - } - - return false; + return registry[module.id] && (registry[module.id].inited || registry[module.id].error); } /** From d8d4db98519de2982a0b9f1780c10d7b370a76d6 Mon Sep 17 00:00:00 2001 From: Igor Melnykov <melnykov@adobe.com> Date: Tue, 28 Aug 2018 16:45:35 -0500 Subject: [PATCH 1152/1171] MAGETWO-94290: Add conflict with gene/bluefoot to composer.json - add conflict --- composer.json | 3 ++ composer.lock | 115 ++++++++++++++++++++++++++------------------------ 2 files changed, 64 insertions(+), 54 deletions(-) diff --git a/composer.json b/composer.json index 0040fc46fb743..124c700459bdc 100644 --- a/composer.json +++ b/composer.json @@ -256,6 +256,9 @@ "tinymce/tinymce": "3.4.7", "magento/module-tinymce-3": "*" }, + "conflict": { + "gene/bluefoot": "*" + }, "extra": { "component_paths": { "trentrichardson/jquery-timepicker-addon": "lib/web/jquery/jquery-ui-timepicker-addon.js", diff --git a/composer.lock b/composer.lock index 014c8eca5570c..c97b95bc1695d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "da340950d3c725fcf2e01e73c48683d4", + "content-hash": "02069f162167ee438dcef6b3fb141707", "packages": [ { "name": "braintree/braintree_php", @@ -460,16 +460,16 @@ }, { "name": "composer/xdebug-handler", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "e1809da56ce1bd1b547a752936817341ac244d8e" + "reference": "e37cbd80da64afe314c72de8d2d2fec0e40d9373" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/e1809da56ce1bd1b547a752936817341ac244d8e", - "reference": "e1809da56ce1bd1b547a752936817341ac244d8e", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/e37cbd80da64afe314c72de8d2d2fec0e40d9373", + "reference": "e37cbd80da64afe314c72de8d2d2fec0e40d9373", "shasum": "" }, "require": { @@ -500,7 +500,7 @@ "Xdebug", "performance" ], - "time": "2018-08-16T10:54:23+00:00" + "time": "2018-08-23T12:00:19+00:00" }, { "name": "container-interop/container-interop", @@ -1836,7 +1836,7 @@ }, { "name": "symfony/console", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", @@ -1904,7 +1904,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -1967,16 +1967,16 @@ }, { "name": "symfony/filesystem", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "2e30335e0aafeaa86645555959572fe7cea22b43" + "reference": "c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/2e30335e0aafeaa86645555959572fe7cea22b43", - "reference": "2e30335e0aafeaa86645555959572fe7cea22b43", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e", + "reference": "c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e", "shasum": "" }, "require": { @@ -2013,11 +2013,11 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:24:31+00:00" + "time": "2018-08-18T16:52:46+00:00" }, { "name": "symfony/finder", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -2183,16 +2183,16 @@ }, { "name": "symfony/process", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "f01fc7a4493572f7f506c49dcb50ad01fb3a2f56" + "reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f01fc7a4493572f7f506c49dcb50ad01fb3a2f56", - "reference": "f01fc7a4493572f7f506c49dcb50ad01fb3a2f56", + "url": "https://api.github.com/repos/symfony/process/zipball/86cdb930a6a855b0ab35fb60c1504cb36184f843", + "reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843", "shasum": "" }, "require": { @@ -2228,7 +2228,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:24:31+00:00" + "time": "2018-08-03T11:13:38+00:00" }, { "name": "tedivm/jshrink", @@ -5098,16 +5098,16 @@ }, { "name": "consolidation/self-update", - "version": "1.0.0", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/consolidation/self-update.git", - "reference": "adbb784e58cc0836d8522851f7e38ee7ade0d553" + "reference": "de33822f907e0beb0ffad24cf4b1b4fae5ada318" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/self-update/zipball/adbb784e58cc0836d8522851f7e38ee7ade0d553", - "reference": "adbb784e58cc0836d8522851f7e38ee7ade0d553", + "url": "https://api.github.com/repos/consolidation/self-update/zipball/de33822f907e0beb0ffad24cf4b1b4fae5ada318", + "reference": "de33822f907e0beb0ffad24cf4b1b4fae5ada318", "shasum": "" }, "require": { @@ -5115,6 +5115,9 @@ "symfony/console": "^2.8|^3|^4", "symfony/filesystem": "^2.5|^3|^4" }, + "bin": [ + "scripts/release" + ], "type": "library", "extra": { "branch-alias": { @@ -5131,13 +5134,17 @@ "MIT" ], "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + }, { "name": "Alexander Menk", "email": "menk@mestrona.net" } ], "description": "Provides a self:update command for Symfony Console applications.", - "time": "2018-08-17T04:50:59+00:00" + "time": "2018-08-24T17:01:46+00:00" }, { "name": "dflydev/dot-access-data", @@ -7377,16 +7384,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.11", + "version": "6.5.12", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "7bab54cb366076023bbf457a2a0d513332cd40f2" + "reference": "24da433d7384824d65ea93fbb462e2f31bbb494e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7bab54cb366076023bbf457a2a0d513332cd40f2", - "reference": "7bab54cb366076023bbf457a2a0d513332cd40f2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/24da433d7384824d65ea93fbb462e2f31bbb494e", + "reference": "24da433d7384824d65ea93fbb462e2f31bbb494e", "shasum": "" }, "require": { @@ -7457,7 +7464,7 @@ "testing", "xunit" ], - "time": "2018-08-07T07:05:35+00:00" + "time": "2018-08-22T06:32:48+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -8219,7 +8226,7 @@ }, { "name": "symfony/browser-kit", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -8276,16 +8283,16 @@ }, { "name": "symfony/config", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "c868972ac26e4e19860ce11b300bb74145246ff9" + "reference": "76015a3cc372b14d00040ff58e18e29f69eba717" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/c868972ac26e4e19860ce11b300bb74145246ff9", - "reference": "c868972ac26e4e19860ce11b300bb74145246ff9", + "url": "https://api.github.com/repos/symfony/config/zipball/76015a3cc372b14d00040ff58e18e29f69eba717", + "reference": "76015a3cc372b14d00040ff58e18e29f69eba717", "shasum": "" }, "require": { @@ -8335,11 +8342,11 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:24:31+00:00" + "time": "2018-08-08T06:37:38+00:00" }, { "name": "symfony/css-selector", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -8392,16 +8399,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "f4f401fc2766eb8d766fc6043d9e6489b37a41e4" + "reference": "bae4983003c9d451e278504d7d9b9d7fc1846873" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f4f401fc2766eb8d766fc6043d9e6489b37a41e4", - "reference": "f4f401fc2766eb8d766fc6043d9e6489b37a41e4", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/bae4983003c9d451e278504d7d9b9d7fc1846873", + "reference": "bae4983003c9d451e278504d7d9b9d7fc1846873", "shasum": "" }, "require": { @@ -8459,11 +8466,11 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2018-08-01T08:24:03+00:00" + "time": "2018-08-08T11:48:58+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", @@ -8520,16 +8527,16 @@ }, { "name": "symfony/http-foundation", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "7d93e3547660ec7ee3dad1428ba42e8076a0e5f1" + "reference": "3a5c91e133b220bb882b3cd773ba91bf39989345" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7d93e3547660ec7ee3dad1428ba42e8076a0e5f1", - "reference": "7d93e3547660ec7ee3dad1428ba42e8076a0e5f1", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3a5c91e133b220bb882b3cd773ba91bf39989345", + "reference": "3a5c91e133b220bb882b3cd773ba91bf39989345", "shasum": "" }, "require": { @@ -8570,11 +8577,11 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-08-01T14:07:44+00:00" + "time": "2018-08-27T17:47:02+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", @@ -8742,7 +8749,7 @@ }, { "name": "symfony/stopwatch", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -8791,16 +8798,16 @@ }, { "name": "symfony/yaml", - "version": "v3.4.14", + "version": "v3.4.15", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "810af2d35fc72b6cf5c01116806d2b65ccaaf2e2" + "reference": "c2f4812ead9f847cb69e90917ca7502e6892d6b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/810af2d35fc72b6cf5c01116806d2b65ccaaf2e2", - "reference": "810af2d35fc72b6cf5c01116806d2b65ccaaf2e2", + "url": "https://api.github.com/repos/symfony/yaml/zipball/c2f4812ead9f847cb69e90917ca7502e6892d6b8", + "reference": "c2f4812ead9f847cb69e90917ca7502e6892d6b8", "shasum": "" }, "require": { @@ -8846,7 +8853,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:19:56+00:00" + "time": "2018-08-10T07:34:36+00:00" }, { "name": "theseer/fdomdocument", From d891eb7773cfdcd9a04107374455b31374517c47 Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Wed, 29 Aug 2018 11:23:41 -0500 Subject: [PATCH 1153/1171] MQE-1174: Deliver weekly regression enablement tests - Remove composer.lock file changes --- composer.lock | 322 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 244 insertions(+), 78 deletions(-) diff --git a/composer.lock b/composer.lock index b4147f01a9cb5..c82248aa153a5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ef3c5510832524507bfdfbb6ddd49f07", + "content-hash": "1be51b51fccf9e5f2f2c1942cc3d8e35", "packages": [ { "name": "braintree/braintree_php", @@ -257,16 +257,16 @@ }, { "name": "composer/composer", - "version": "1.7.1", + "version": "1.7.2", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "5d9311d4555787c8a57fea15f82471499aedf712" + "reference": "576aab9b5abb2ed11a1c52353a759363216a4ad2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/5d9311d4555787c8a57fea15f82471499aedf712", - "reference": "5d9311d4555787c8a57fea15f82471499aedf712", + "url": "https://api.github.com/repos/composer/composer/zipball/576aab9b5abb2ed11a1c52353a759363216a4ad2", + "reference": "576aab9b5abb2ed11a1c52353a759363216a4ad2", "shasum": "" }, "require": { @@ -333,7 +333,7 @@ "dependency", "package" ], - "time": "2018-08-07T07:39:23+00:00" + "time": "2018-08-16T14:57:12+00:00" }, { "name": "composer/semver", @@ -460,16 +460,16 @@ }, { "name": "composer/xdebug-handler", - "version": "1.1.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "c919dc6c62e221fc6406f861ea13433c0aa24f08" + "reference": "e37cbd80da64afe314c72de8d2d2fec0e40d9373" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/c919dc6c62e221fc6406f861ea13433c0aa24f08", - "reference": "c919dc6c62e221fc6406f861ea13433c0aa24f08", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/e37cbd80da64afe314c72de8d2d2fec0e40d9373", + "reference": "e37cbd80da64afe314c72de8d2d2fec0e40d9373", "shasum": "" }, "require": { @@ -500,7 +500,7 @@ "Xdebug", "performance" ], - "time": "2018-04-11T15:42:36+00:00" + "time": "2018-08-23T12:00:19+00:00" }, { "name": "container-interop/container-interop", @@ -1106,6 +1106,88 @@ ], "time": "2018-07-04T16:31:37+00:00" }, + { + "name": "paragonie/sodium_compat", + "version": "v1.6.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/sodium_compat.git", + "reference": "7d0549c3947eaea620f4e523f42ab236cf7fd304" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/7d0549c3947eaea620f4e523f42ab236cf7fd304", + "reference": "7d0549c3947eaea620f4e523f42ab236cf7fd304", + "shasum": "" + }, + "require": { + "paragonie/random_compat": ">=1", + "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7" + }, + "require-dev": { + "phpunit/phpunit": "^3|^4|^5" + }, + "suggest": { + "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", + "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." + }, + "type": "library", + "autoload": { + "files": [ + "autoload.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com" + }, + { + "name": "Frank Denis", + "email": "jedisct1@pureftpd.org" + } + ], + "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", + "keywords": [ + "Authentication", + "BLAKE2b", + "ChaCha20", + "ChaCha20-Poly1305", + "Chapoly", + "Curve25519", + "Ed25519", + "EdDSA", + "Edwards-curve Digital Signature Algorithm", + "Elliptic Curve Diffie-Hellman", + "Poly1305", + "Pure-PHP cryptography", + "RFC 7748", + "RFC 8032", + "Salpoly", + "Salsa20", + "X25519", + "XChaCha20-Poly1305", + "XSalsa20-Poly1305", + "Xchacha20", + "Xsalsa20", + "aead", + "cryptography", + "ecdh", + "elliptic curve", + "elliptic curve cryptography", + "encryption", + "libsodium", + "php", + "public-key cryptography", + "secret-key cryptography", + "side-channel resistant" + ], + "time": "2018-06-06T17:30:29+00:00" + }, { "name": "pelago/emogrifier", "version": "v2.0.0", @@ -1754,7 +1836,7 @@ }, { "name": "symfony/console", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", @@ -1822,7 +1904,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -1885,16 +1967,16 @@ }, { "name": "symfony/filesystem", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "2e30335e0aafeaa86645555959572fe7cea22b43" + "reference": "c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/2e30335e0aafeaa86645555959572fe7cea22b43", - "reference": "2e30335e0aafeaa86645555959572fe7cea22b43", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e", + "reference": "c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e", "shasum": "" }, "require": { @@ -1931,11 +2013,11 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:24:31+00:00" + "time": "2018-08-18T16:52:46+00:00" }, { "name": "symfony/finder", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -2101,16 +2183,16 @@ }, { "name": "symfony/process", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "f01fc7a4493572f7f506c49dcb50ad01fb3a2f56" + "reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f01fc7a4493572f7f506c49dcb50ad01fb3a2f56", - "reference": "f01fc7a4493572f7f506c49dcb50ad01fb3a2f56", + "url": "https://api.github.com/repos/symfony/process/zipball/86cdb930a6a855b0ab35fb60c1504cb36184f843", + "reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843", "shasum": "" }, "require": { @@ -2146,7 +2228,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:24:31+00:00" + "time": "2018-08-03T11:13:38+00:00" }, { "name": "tedivm/jshrink", @@ -2400,16 +2482,16 @@ }, { "name": "zendframework/zend-code", - "version": "3.3.0", + "version": "3.3.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-code.git", - "reference": "6b1059db5b368db769e4392c6cb6cc139e56640d" + "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/6b1059db5b368db769e4392c6cb6cc139e56640d", - "reference": "6b1059db5b368db769e4392c6cb6cc139e56640d", + "url": "https://api.github.com/repos/zendframework/zend-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", + "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb", "shasum": "" }, "require": { @@ -2430,8 +2512,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev", - "dev-develop": "3.3-dev" + "dev-master": "3.3.x-dev", + "dev-develop": "3.4.x-dev" } }, "autoload": { @@ -2449,7 +2531,7 @@ "code", "zf2" ], - "time": "2017-10-20T15:21:32+00:00" + "time": "2018-08-13T20:36:59+00:00" }, { "name": "zendframework/zend-config", @@ -4725,16 +4807,16 @@ }, { "name": "consolidation/annotated-command", - "version": "2.8.4", + "version": "2.8.5", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "651541a0b68318a2a202bda558a676e5ad92223c" + "reference": "1e8ff512072422b850b44aa721b5b303e4a5ebb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/651541a0b68318a2a202bda558a676e5ad92223c", - "reference": "651541a0b68318a2a202bda558a676e5ad92223c", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/1e8ff512072422b850b44aa721b5b303e4a5ebb3", + "reference": "1e8ff512072422b850b44aa721b5b303e4a5ebb3", "shasum": "" }, "require": { @@ -4773,7 +4855,7 @@ } ], "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2018-05-25T18:04:25+00:00" + "time": "2018-08-18T23:51:49+00:00" }, { "name": "consolidation/config", @@ -4935,16 +5017,16 @@ }, { "name": "consolidation/robo", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f" + "reference": "31f2d2562c4e1dcde70f2659eefd59aa9c7f5b2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/ac563abfadf7cb7314b4e152f2b5033a6c255f6f", - "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/31f2d2562c4e1dcde70f2659eefd59aa9c7f5b2d", + "reference": "31f2d2562c4e1dcde70f2659eefd59aa9c7f5b2d", "shasum": "" }, "require": { @@ -4952,6 +5034,8 @@ "consolidation/config": "^1.0.10", "consolidation/log": "~1", "consolidation/output-formatters": "^3.1.13", + "consolidation/self-update": "^1", + "g1a/composer-test-scenarios": "^2", "grasmash/yaml-expander": "^1.3", "league/container": "^2.2", "php": ">=5.5.0", @@ -4968,7 +5052,6 @@ "codeception/aspect-mock": "^1|^2.1.1", "codeception/base": "^2.3.7", "codeception/verify": "^0.3.2", - "g1a/composer-test-scenarios": "^2", "goaop/framework": "~2.1.2", "goaop/parser-reflection": "^1.1.0", "natxet/cssmin": "3.0.4", @@ -5011,7 +5094,57 @@ } ], "description": "Modern task runner", - "time": "2018-05-27T01:42:53+00:00" + "time": "2018-08-17T18:44:18+00:00" + }, + { + "name": "consolidation/self-update", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/consolidation/self-update.git", + "reference": "de33822f907e0beb0ffad24cf4b1b4fae5ada318" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/self-update/zipball/de33822f907e0beb0ffad24cf4b1b4fae5ada318", + "reference": "de33822f907e0beb0ffad24cf4b1b4fae5ada318", + "shasum": "" + }, + "require": { + "php": ">=5.5.0", + "symfony/console": "^2.8|^3|^4", + "symfony/filesystem": "^2.5|^3|^4" + }, + "bin": [ + "scripts/release" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "SelfUpdate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + }, + { + "name": "Alexander Menk", + "email": "menk@mestrona.net" + } + ], + "description": "Provides a self:update command for Symfony Console applications.", + "time": "2018-08-24T17:01:46+00:00" }, { "name": "dflydev/dot-access-data", @@ -5464,21 +5597,21 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.12.2", + "version": "v2.12.3", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "dcc87d5414e9d0bd316fce81a5bedb9ce720b183" + "reference": "b23d49981cfc95497d03081aeb6df6575196a0d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/dcc87d5414e9d0bd316fce81a5bedb9ce720b183", - "reference": "dcc87d5414e9d0bd316fce81a5bedb9ce720b183", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/b23d49981cfc95497d03081aeb6df6575196a0d3", + "reference": "b23d49981cfc95497d03081aeb6df6575196a0d3", "shasum": "" }, "require": { "composer/semver": "^1.4", - "composer/xdebug-handler": "^1.0", + "composer/xdebug-handler": "^1.2", "doctrine/annotations": "^1.2", "ext-json": "*", "ext-tokenizer": "*", @@ -5551,7 +5684,7 @@ } ], "description": "A tool to automatically fix PHP code style", - "time": "2018-07-06T10:37:40+00:00" + "time": "2018-08-19T22:33:38+00:00" }, { "name": "fzaninotto/faker", @@ -5603,6 +5736,39 @@ ], "time": "2018-07-12T10:23:15+00:00" }, + { + "name": "g1a/composer-test-scenarios", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/g1a/composer-test-scenarios.git", + "reference": "a166fd15191aceab89f30c097e694b7cf3db4880" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/g1a/composer-test-scenarios/zipball/a166fd15191aceab89f30c097e694b7cf3db4880", + "reference": "a166fd15191aceab89f30c097e694b7cf3db4880", + "shasum": "" + }, + "bin": [ + "scripts/create-scenario", + "scripts/dependency-licenses", + "scripts/install-scenario" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Useful scripts for testing multiple sets of Composer dependencies.", + "time": "2018-08-08T23:37:23+00:00" + }, { "name": "grasmash/expander", "version": "1.0.0", @@ -7218,16 +7384,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.11", + "version": "6.5.12", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "7bab54cb366076023bbf457a2a0d513332cd40f2" + "reference": "24da433d7384824d65ea93fbb462e2f31bbb494e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7bab54cb366076023bbf457a2a0d513332cd40f2", - "reference": "7bab54cb366076023bbf457a2a0d513332cd40f2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/24da433d7384824d65ea93fbb462e2f31bbb494e", + "reference": "24da433d7384824d65ea93fbb462e2f31bbb494e", "shasum": "" }, "require": { @@ -7298,7 +7464,7 @@ "testing", "xunit" ], - "time": "2018-08-07T07:05:35+00:00" + "time": "2018-08-22T06:32:48+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -8060,7 +8226,7 @@ }, { "name": "symfony/browser-kit", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -8117,16 +8283,16 @@ }, { "name": "symfony/config", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "c868972ac26e4e19860ce11b300bb74145246ff9" + "reference": "76015a3cc372b14d00040ff58e18e29f69eba717" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/c868972ac26e4e19860ce11b300bb74145246ff9", - "reference": "c868972ac26e4e19860ce11b300bb74145246ff9", + "url": "https://api.github.com/repos/symfony/config/zipball/76015a3cc372b14d00040ff58e18e29f69eba717", + "reference": "76015a3cc372b14d00040ff58e18e29f69eba717", "shasum": "" }, "require": { @@ -8176,11 +8342,11 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:24:31+00:00" + "time": "2018-08-08T06:37:38+00:00" }, { "name": "symfony/css-selector", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -8233,16 +8399,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "f4f401fc2766eb8d766fc6043d9e6489b37a41e4" + "reference": "bae4983003c9d451e278504d7d9b9d7fc1846873" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f4f401fc2766eb8d766fc6043d9e6489b37a41e4", - "reference": "f4f401fc2766eb8d766fc6043d9e6489b37a41e4", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/bae4983003c9d451e278504d7d9b9d7fc1846873", + "reference": "bae4983003c9d451e278504d7d9b9d7fc1846873", "shasum": "" }, "require": { @@ -8300,11 +8466,11 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2018-08-01T08:24:03+00:00" + "time": "2018-08-08T11:48:58+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", @@ -8361,16 +8527,16 @@ }, { "name": "symfony/http-foundation", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "7d93e3547660ec7ee3dad1428ba42e8076a0e5f1" + "reference": "3a5c91e133b220bb882b3cd773ba91bf39989345" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7d93e3547660ec7ee3dad1428ba42e8076a0e5f1", - "reference": "7d93e3547660ec7ee3dad1428ba42e8076a0e5f1", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3a5c91e133b220bb882b3cd773ba91bf39989345", + "reference": "3a5c91e133b220bb882b3cd773ba91bf39989345", "shasum": "" }, "require": { @@ -8411,11 +8577,11 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-08-01T14:07:44+00:00" + "time": "2018-08-27T17:47:02+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", @@ -8583,7 +8749,7 @@ }, { "name": "symfony/stopwatch", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -8632,16 +8798,16 @@ }, { "name": "symfony/yaml", - "version": "v3.4.14", + "version": "v3.4.15", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "810af2d35fc72b6cf5c01116806d2b65ccaaf2e2" + "reference": "c2f4812ead9f847cb69e90917ca7502e6892d6b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/810af2d35fc72b6cf5c01116806d2b65ccaaf2e2", - "reference": "810af2d35fc72b6cf5c01116806d2b65ccaaf2e2", + "url": "https://api.github.com/repos/symfony/yaml/zipball/c2f4812ead9f847cb69e90917ca7502e6892d6b8", + "reference": "c2f4812ead9f847cb69e90917ca7502e6892d6b8", "shasum": "" }, "require": { @@ -8687,7 +8853,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:19:56+00:00" + "time": "2018-08-10T07:34:36+00:00" }, { "name": "theseer/fdomdocument", From 592e9a1290e44a0a58789bb4ff6137d7ff4b4dcf Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Wed, 29 Aug 2018 11:24:13 -0500 Subject: [PATCH 1154/1171] MQE-1174: Deliver weekly regression enablement tests - Remove non-modular MC-182 test --- .../Mftf/Test/StorefrontSortByPriceTest.xml | 177 ------------------ 1 file changed, 177 deletions(-) delete mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontSortByPriceTest.xml diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontSortByPriceTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontSortByPriceTest.xml deleted file mode 100644 index 5e1cc854c0f5f..0000000000000 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontSortByPriceTest.xml +++ /dev/null @@ -1,177 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontSortByPriceTest"> - <annotations> - <features value="CatalogRule"/> - <stories value="Apply catalog price rule"/> - <title value="Customer should be able to sort by price with catalog rules applied to configurable product"/> - <description value="Customer should be able to sort by price with catalog rules applied to configurable product"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-182"/> - <group value="CatalogRule"/> - </annotations> - <before> - <!-- Create category and two simple products --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct5"> - <requiredEntity createDataKey="createCategory"/> - <field key="price">5</field> - </createData> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct10"> - <requiredEntity createDataKey="createCategory"/> - <field key="price">10</field> - </createData> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Enable SKU for use in promo rule conditions --> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - <waitForPageLoad stepKey="waitForProductAttributes"/> - <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid"/> - <fillField selector="{{AdminProductAttributeGridSection.GridFilterFrontEndLabel}}" userInput="SKU" stepKey="setAttributeLabel"/> - <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromGrid"/> - <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> - <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="goToStorefrontProperties"/> - <selectOption selector="{{StorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="Yes" stepKey="selectUseForPromoRuleCondition"/> - <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSaveAttribute"/> - - <!-- Create a configurable product --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> - <waitForPageLoad time="30" stepKey="waitForProductGrid"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> - <click selector="{{AdminProductGridActionSection.addConfigurableProduct}}" stepKey="clickOnAddConfigurableProduct"/> - <fillField userInput="{{_defaultProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> - <fillField userInput="{{_defaultProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> - <fillField userInput="{{_defaultProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> - <fillField userInput="{{_defaultProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> - <fillField userInput="{{_defaultProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="clickOnNewAttribute"/> - <waitForPageLoad stepKey="waitForIFrame"/> - <switchToIFrame selector="{{AdminNewAttributePanel.newAttributeIFrame}}" stepKey="switchToNewAttributeIFrame"/> - <fillField selector="{{AdminNewAttributePanel.defaultLabel}}" userInput="{{colorProductAttribute.default_label}}" stepKey="fillDefaultLabel"/> - <click selector="{{AdminNewAttributePanel.saveAttribute}}" stepKey="clickOnNewAttributePanel"/> - <waitForPageLoad stepKey="waitForSaveAttribute"/> - <switchToIFrame stepKey="switchOutOfIFrame"/> - <waitForPageLoad stepKey="waitForFilters"/> - <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickOnFilters"/> - <fillField userInput="{{colorProductAttribute.default_label}}" selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" stepKey="fillFilterAttributeCodeField"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> - <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue1"/> - <fillField userInput="{{colorProductAttribute1.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue2"/> - <fillField userInput="{{colorProductAttribute2.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue3"/> - <fillField userInput="{{colorProductAttribute3.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applyUniquePricesByAttributeToEachSku}}" stepKey="clickOnApplyUniquePricesByAttributeToEachSku"/> - <selectOption selector="{{AdminCreateProductConfigurationsPanel.selectAttribute}}" userInput="{{colorProductAttribute.default_label}}" stepKey="selectAttributes"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute1}}" userInput="15" stepKey="fillAttributePrice1"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute2}}" userInput="20" stepKey="fillAttributePrice2"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute3}}" userInput="25" stepKey="fillAttributePrice3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="1" stepKey="enterAttributeQuantity"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> - <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> - </before> - <after> - <!-- Delete category and two simple products --> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createSimpleProduct5" stepKey="deleteSimpleProduct5"/> - <deleteData createDataKey="createSimpleProduct10" stepKey="deleteSimpleProduct10"/> - - <!-- Delete the catalog price rule --> - <amOnPage stepKey="goToPriceRulePage" url="{{CatalogRulePage.url}}"/> - <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> - <argument name="name" value="{{_defaultCatalogRule.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - </after> - - <!-- 1. Check category page price sorting BEFORE catalog rule is created --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory1"/> - <selectOption selector="{{StorefrontCategoryTopToolbarSection.sortByDropdown}}" userInput="price" stepKey="sortByPrice1"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createSimpleProduct5.name$$" stepKey="seeOrder1"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$5.00" stepKey="seePrice1"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$$createSimpleProduct10.name$$" stepKey="seeOrder2"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$10.00" stepKey="seePrice2"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="{{_defaultProduct.name}}" stepKey="seeOrder3"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="$15.00" stepKey="seePrice3"/> - <click selector="{{StorefrontCategoryTopToolbarSection.sortDirectionAsc}}" stepKey="clickSortByArrow1"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="{{_defaultProduct.name}}" stepKey="seeOrder4"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$15.00" stepKey="seePrice4"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$$createSimpleProduct10.name$$" stepKey="seeOrder5"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$10.00" stepKey="seePrice5"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="$$createSimpleProduct5.name$$" stepKey="seeOrder6"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="$5.00" stepKey="seePrice6"/> - - <!-- 2. Create a new catalog rule that adjusts one of the configurable products down to $1 --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage"/> - <waitForPageLoad stepKey="waitForPriceRulePage"/> - <click selector="{{AdminGridMainControls.add}}" stepKey="addNewRule"/> - <waitForPageLoad stepKey="waitForIndividualRulePage"/> - <fillField selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{_defaultCatalogRule.name}}" stepKey="fillName"/> - <fillField selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{_defaultCatalogRule.description}}" stepKey="fillDescription"/> - <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" userInput="{{_defaultCatalogRule.website_ids[0]}}" stepKey="selectSite"/> - <click selector="{{AdminNewCatalogPriceRule.fromDateButton}}" stepKey="clickFromCalender"/> - <click selector="{{AdminNewCatalogPriceRule.todayDate}}" stepKey="clickFromToday"/> - <click selector="{{AdminNewCatalogPriceRule.toDateButton}}" stepKey="clickToCalender"/> - <click selector="{{AdminNewCatalogPriceRule.todayDate}}" stepKey="clickToToday"/> - <click selector="{{AdminNewCatalogPriceRule.conditionsTab}}" stepKey="openConditions"/> - <click selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" stepKey="clickNewRule"/> - <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionSelect('1')}}" userInput="SKU" stepKey="selectSKU"/> - <waitForPageLoad stepKey="waitForEllipsis"/> - <click selector="{{AdminNewCatalogPriceRuleConditions.targetEllipsis('1')}}" stepKey="clickEllipsis"/> - <waitForPageLoad stepKey="waitForInput"/> - <fillField selector="{{AdminNewCatalogPriceRuleConditions.targetInput('1', '1')}}" userInput="{{_defaultProduct.sku}}-{{colorProductAttribute3.name}}" stepKey="fillSku"/> - <click selector="{{AdminNewCatalogPriceRuleConditions.applyButton('1', '1')}}" stepKey="clickApply"/> - <click selector="{{AdminNewCatalogPriceRule.actionsTab}}" stepKey="openActionDropdown"/> - <selectOption selector="{{AdminNewCatalogPriceRuleActions.apply}}" userInput="{{_defaultCatalogRule.simple_action}}" stepKey="discountType"/> - <fillField selector="{{AdminNewCatalogPriceRuleActions.discountAmount}}" userInput="96" stepKey="fillDiscountValue"/> - <selectOption selector="{{AdminNewCatalogPriceRuleActions.disregardRules}}" userInput="Yes" stepKey="discardSubsequentRules"/> - <scrollToTopOfPage stepKey="scrollToTop"/> - <waitForPageLoad stepKey="waitForApplied"/> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> - - <!-- 3. Save the catalog rule, reindex, and flush cache--> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <!-- 4. Check category page price sorting AFTER catalog rule is created --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory2"/> - <selectOption selector="{{StorefrontCategoryTopToolbarSection.sortByDropdown}}" userInput="price" stepKey="sortByPrice2"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="{{_defaultProduct.name}}" stepKey="seeOrder7"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$1.00" stepKey="seePrice7"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$$createSimpleProduct5.name$$" stepKey="seeOrder8"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$5.00" stepKey="seePrice8"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="$$createSimpleProduct10.name$$" stepKey="seeOrder9"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="$10.00" stepKey="seePrice9"/> - <click selector="{{StorefrontCategoryTopToolbarSection.sortDirectionAsc}}" stepKey="clickSortByArrow2"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createSimpleProduct10.name$$" stepKey="seeOrder10"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$10.00" stepKey="seePrice10"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$$createSimpleProduct5.name$$" stepKey="seeOrder11"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('2')}}" userInput="$5.00" stepKey="seePrice11"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="{{_defaultProduct.name}}" stepKey="seeOrder12"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('3')}}" userInput="$1.00" stepKey="seePrice12"/> - </test> -</tests> From 238f63f4b8e4616c1f1512aabc8fb385b3c987ab Mon Sep 17 00:00:00 2001 From: Tom Reece <tomreece@gmail.com> Date: Wed, 29 Aug 2018 14:53:24 -0500 Subject: [PATCH 1155/1171] MQE-1176: Fix all deprecation warnings - Fix AddingProductWithExpiredSessionTest and ConfigurableProductAttributeNameDesignTest --- .../Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml | 3 ++- .../Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml index bb7792b9d375e..7fbff5eac2583 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml @@ -7,10 +7,11 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="ConfigurableProductAttributeNameDesignTest"> <annotations> <title value="Generation of configurable products with an attribute named 'design'"/> + <description value="Generation of configurable products with an attribute named 'design'"/> <features value="Product Customizable Option"/> <severity value="AVERAGE"/> <testCaseId value="MAGETWO-93307"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml index 38a9cd0467151..01f35439f23b8 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml @@ -6,11 +6,11 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AddingProductWithExpiredSessionTest"> <annotations> <title value="Adding a product to cart from category page with an expired session"/> + <description value="Adding a product to cart from category page with an expired session"/> <features value="Module/ Catalog"/> <severity value="MAJOR"/> <testCaseId value="MAGETWO-93289"/> From 30a822a9b6d0a704be340dbadf5b390205d1785b Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@adobe.com> Date: Wed, 29 Aug 2018 17:46:10 -0500 Subject: [PATCH 1156/1171] MAGETWO-94021: Problems with adding products in wish list - added functional test --- ...nfigurationCustomerWishlistActionGroup.xml | 31 +++++++++ .../StorefrontCustomerWishlistActionGroup.xml | 33 ++++++++++ .../Page/StorefrontCustomerWishlistPage.xml | 3 + .../Section/AdminCustomerWishlistSection.xml | 16 +++++ .../StorefrontCategoryProductSection.xml | 3 + ...shListWithMultipleWishlistsEnabledTest.xml | 64 +++++++++++++++++++ 6 files changed, 150 insertions(+) create mode 100644 app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigurationCustomerWishlistActionGroup.xml create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Section/AdminCustomerWishlistSection.xml create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Test/StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest.xml diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigurationCustomerWishlistActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigurationCustomerWishlistActionGroup.xml new file mode 100644 index 0000000000000..637775cfa4064 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigurationCustomerWishlistActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="EnableCustomerMultipleWishlistOption"> + <amOnPage url="{{AdminCustomerWishlistConfigurationPage.url}}" stepKey="goToWishlistConfigurationPage"/> + <waitForPageLoad stepKey="waitForSystemConfigPage"/> + <conditionalClick selector="{{WishlistGeneralSection.GeneralOptionsTab}}" dependentSelector="{{WishlistGeneralSection.CheckIfGeneralOptionsTabExpand}}" visible="true" stepKey="expandGeneralSectionTab"/> + <waitForElementVisible selector="{{WishlistGeneralSection.EnableMultipleWishList}}" stepKey="waitForDropdownToBeVisible"/> + <selectOption selector="{{WishlistGeneralSection.EnableMultipleWishList}}" userInput="Yes" stepKey="enableMultipleWishLists"/> + <click selector="{{WishlistGeneralSection.GeneralOptionsTab}}" stepKey="collapseGeneralOptionsTab"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfiguration"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeConfigSuccessMessage"/> + </actionGroup> + <actionGroup name="ResetCustomerMultipleWishlistOption"> + <amOnPage url="{{AdminCustomerWishlistConfigurationPage.url}}" stepKey="goToWishlistConfigurationPage"/> + <waitForPageLoad stepKey="waitForSystemConfigPage"/> + <conditionalClick selector="{{WishlistGeneralSection.GeneralOptionsTab}}" dependentSelector="{{WishlistGeneralSection.CheckIfGeneralOptionsTabExpand}}" visible="true" stepKey="expandGeneralSectionTab"/> + <waitForElementVisible selector="{{WishlistGeneralSection.EnableMultipleWishList}}" stepKey="waitForDropdownToBeVisible"/> + <selectOption selector="{{WishlistGeneralSection.EnableMultipleWishList}}" userInput="No" stepKey="enableMultipleWishLists"/> + <click selector="{{WishlistGeneralSection.GeneralOptionsTab}}" stepKey="collapseGeneralOptionsTab"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfiguration"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeConfigSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml index edd1af41964a2..5dae62050004d 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml @@ -20,6 +20,39 @@ <seeCurrentUrlMatches regex="~/wishlist_id/\d+/$~" stepKey="seeCurrentUrlMatches"/> </actionGroup> + <!-- Add Product to split wishlist from the category page and check message --> + <actionGroup name="StorefrontCustomerAddCategoryProductToWishlistActionGroup2"> + <arguments> + <argument name="productVar"/> + </arguments> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(productVar.name)}}" stepKey="addCategoryProductToWishlistMoveMouseOverProduct" /> + <!--<click selector="{{StorefrontCategoryProductSection.ProductAddToSplitWishlistByName(productVar.name)}}" stepKey="addCategoryProductToWishlistClickAddProductToWishlist"/>--> + <click selector="{{StorefrontCategoryProductSection.addToWishListArrow(productVar.name)}}" stepKey="clickAddToWishlist"/> + <click selector="{{StorefrontCategoryProductSection.chooseWishlist}}" stepKey="selectWishlist"/> + + <waitForElement selector="{{StorefrontCustomerWishlistSection.successMsg}}" time="30" stepKey="addCategoryProductToWishlistWaitForSuccessMessage"/> + <see selector="{{StorefrontCustomerWishlistSection.successMsg}}" userInput="{{productVar.name}} has been added to your Wish List." stepKey="addCategoryProductToWishlistSeeProductNameAddedToWishlist"/> + <seeCurrentUrlMatches regex="~/wishlist_id/\d+/$~" stepKey="seeCurrentUrlMatches"/> + </actionGroup> + + <actionGroup name="StorefrontCustomerAddCategoryProductToWishlistActionGroup3"> + <arguments> + <argument name="productVar"/> + </arguments> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(productVar.name)}}" stepKey="addCategoryProductToWishlistMoveMouseOverProduct" /> + <!--<click selector="{{StorefrontCategoryProductSection.ProductAddToSplitWishlistByName(productVar.name)}}" stepKey="addCategoryProductToWishlistClickAddProductToWishlist"/>--> + <click selector="{{StorefrontCategoryProductSection.addToWishListArrow(productVar.name)}}" stepKey="clickDropdownToAddToWishlist"/> + <!--<click selector="{{StorefrontCategoryProductSection.testWishlist(productVar.name)}}" stepKey="chooseWishlistSpan"/>--> + <!--<scrollTo selector="{{StorefrontCategoryProductSection.testWishlist(productVar.name)}}" x="-10" y="10" stepKey="selectWishlistwithOffset"/>--> + <!--<scrollTo selector="{{StorefrontCategoryProductSection.chooseWishlist}}" x="10" y="10" stepKey="selectWishlistwithOffset"/>--> + <!--<clickWithLeftButton selector="{{StorefrontCategoryProductSection.chooseWishlist}}" x="-90" y="0" stepKey="selectWishlistWithOffset"/>--> + <clickWithLeftButton selector="{{StorefrontCategoryProductSection.testWishlist(productVar.name)}}" x="90" y="0" stepKey="selectWishlistWithOffset"/> + + <waitForElement selector="{{StorefrontCustomerWishlistSection.successMsg}}" time="30" stepKey="addCategoryProductToWishlistWaitForSuccessMessage"/> + <see selector="{{StorefrontCustomerWishlistSection.successMsg}}" userInput="{{productVar.name}} has been added to your Wish List." stepKey="addCategoryProductToWishlistSeeProductNameAddedToWishlist"/> + <seeCurrentUrlMatches regex="~/wishlist_id/\d+/$~" stepKey="seeCurrentUrlMatches"/> + </actionGroup> + <!-- Add Product to wishlist from the product page and check message --> <actionGroup name="StorefrontCustomerAddProductToWishlistActionGroup"> <arguments> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml b/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml index cf2db7efab6c6..0e9354990e9ab 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml @@ -11,4 +11,7 @@ <page name="StorefrontCustomerWishlistPage" url="/wishlist/" area="storefront" module="Magento_Wishlist"> <section name="StorefrontCustomerWishlistSection" /> </page> + <page name="AdminCustomerWishlistConfigurationPage" url="admin/system_config/edit/section/wishlist/" area="admin" module="Magento_Wishlist"> + <section name="WishlistGeneralSection"/> + </page> </pages> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/AdminCustomerWishlistSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/AdminCustomerWishlistSection.xml new file mode 100644 index 0000000000000..6d71b5374fa05 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/AdminCustomerWishlistSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="WishlistGeneralSection"> + <element name="GeneralOptionsTab" type="button" selector="#wishlist_general-head"/> + <element name="CheckIfGeneralOptionsTabExpand" type="button" selector="#wishlist_general-head:not(.open)"/> + <element name="EnableMultipleWishList" type="select" selector="#wishlist_general_multiple_enabled"/> + </section> +</sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml index 20d72a0704699..67172c1481940 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -11,5 +11,8 @@ <section name="StorefrontCategoryProductSection"> <element name="ProductAddToWishlistByNumber" type="text" selector="//main//li[{{var1}}]//a[contains(@class, 'towishlist')]" parameterized="true"/> <element name="ProductAddToWishlistByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//a[contains(@class, 'towishlist')]" parameterized="true"/> + <element name="addToWishListArrow" type="button" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//button[@title='Add to:']" parameterized="true"/> + <element name="chooseWishlist" type="button" selector="span[title='Wish List']" timeout="30"/> + <element name="testWishlist" type="button" selector="(//main//li[.//a[contains(text(), '{{var1}}')]]//li[contains(@class,'item')]/span)[1]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest.xml new file mode 100644 index 0000000000000..3fe3f40778bd9 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Add products from the wishlist to the cart using the sidebar."/> + <description value="Products added to the cart from wishlist and a customer remains on the same page."/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-94021"/> + <group value="wishlist"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryFirst"/> + + <createData entity="SimpleProduct" stepKey="simpleProduct1"> + <requiredEntity createDataKey="categoryFirst"/> + </createData> + <!--<createData entity="SimpleProduct" stepKey="simpleProduct2">--> + <!--<requiredEntity createDataKey="categoryFirst"/>--> + <!--</createData>--> + + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <!--<deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/>--> + <deleteData createDataKey="categoryFirst" stepKey="deleteCategoryFirst"/> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + <actionGroup ref="ResetCustomerMultipleWishlistOption" stepKey="resetWishlistConfiguration"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="logout"/> + </after> + <!--Login to admin--> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdmin"/> + <comment userInput="Enable multiple wishlist" stepKey="enableMultipleWishlist"/> + <actionGroup ref="EnableCustomerMultipleWishlistOption" stepKey="enableCustomerWishlist"/> + <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache"/> + + <!--<amOnPage url="admin/admin/auth/logout/" stepKey="logout"/>--> + <!-- Sign in as customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!-- Add product from first category to the wishlist --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryFirst.name$$)}}" stepKey="navigateToCategoryFirstPage"/> + <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct1"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <!--<actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup2" stepKey="addSimpleProduct1ToWishlist">--> + <!--<argument name="productVar" value="$$simpleProduct1$$"/>--> + <!--</actionGroup>--> + + <!-- Add product from first category to the wishlist after shifting--> + <actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup3" stepKey="addSimpleProduct1ToWishlist"> + <argument name="productVar" value="$$simpleProduct1$$"/> + </actionGroup> + </test> +</tests> From 9ec8a6c235edf6e38b373a844a874a1cf03e4f30 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 30 Aug 2018 11:01:21 -0500 Subject: [PATCH 1157/1171] MAGETWO-93305: Broken upgrade to 2.3 due to changed data type for minify_exclude - adding multi value string support divided by \n --- .../Framework/View/Asset/Minification.php | 4 +-- .../View/Test/Unit/Asset/MinificationTest.php | 25 +++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Asset/Minification.php b/lib/internal/Magento/Framework/View/Asset/Minification.php index 2742f5a2657b3..8c6c75c7f6e95 100644 --- a/lib/internal/Magento/Framework/View/Asset/Minification.php +++ b/lib/internal/Magento/Framework/View/Asset/Minification.php @@ -162,9 +162,9 @@ public function getExcludes($contentType) private function getMinificationExcludeValues($key) { $configValues = $this->scopeConfig->getValue($key, $this->scope) ?? []; - //compatibility fix for type change from string to array + //compatibility fix for type change from new line separated string values to array if (!is_array($configValues)) { - $configValues = [$configValues => $configValues]; + $configValues = explode("\n", $configValues); } return array_values($configValues); } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php index 229c03b2f9b64..1758492a56384 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php @@ -219,19 +219,34 @@ public function testGetExcludes() /** * Test dev/js/minify_exclude system value backward compatibility when value was a string * + * @param string $value + * @param array $expectedValue * @return void + * + * @dataProvider getExcludesTinyMceAsStringDataProvider */ - public function testGetExcludesTinyMceAsString() + public function testGetExcludesTinyMceAsString(string $value, array $expectedValue) { $this->scopeConfigMock ->expects($this->once()) ->method('getValue') ->with('dev/js/minify_exclude') - ->willReturn('/tiny_mce/'); + ->willReturn($value); - $expected = ['/tiny_mce/']; - $this->assertEquals($expected, $this->minification->getExcludes('js')); + $this->assertEquals($expectedValue, $this->minification->getExcludes('js')); /** check cache: */ - $this->assertEquals($expected, $this->minification->getExcludes('js')); + $this->assertEquals($expectedValue, $this->minification->getExcludes('js')); + } + + + /** + * @return array + */ + public function getExcludesTinyMceAsStringDataProvider() + { + return [ + ["/tiny_mce/\n/tiny_mce2/", ['/tiny_mce/', '/tiny_mce2/']], + ['/tiny_mce/', ['/tiny_mce/']], + ]; } } From 491b3005225be356b2fcc4a028dcd8a2a44a5769 Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@adobe.com> Date: Thu, 30 Aug 2018 11:07:52 -0500 Subject: [PATCH 1158/1171] MAGETWO-94021: Problems with adding products in wish list - updated annotations and cleaned up the test --- .../StorefrontCustomerWishlistActionGroup.xml | 23 ++----------------- .../StorefrontCategoryProductSection.xml | 3 +-- ...shListWithMultipleWishlistsEnabledTest.xml | 19 ++++----------- 3 files changed, 7 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml index 5dae62050004d..d1c9aeab02b60 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml @@ -28,28 +28,9 @@ <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(productVar.name)}}" stepKey="addCategoryProductToWishlistMoveMouseOverProduct" /> <!--<click selector="{{StorefrontCategoryProductSection.ProductAddToSplitWishlistByName(productVar.name)}}" stepKey="addCategoryProductToWishlistClickAddProductToWishlist"/>--> <click selector="{{StorefrontCategoryProductSection.addToWishListArrow(productVar.name)}}" stepKey="clickAddToWishlist"/> - <click selector="{{StorefrontCategoryProductSection.chooseWishlist}}" stepKey="selectWishlist"/> - + <clickWithLeftButton selector="{{StorefrontCategoryProductSection.chooseWishlist(productVar.name)}}" x="96" y="5" stepKey="selectWishlistWithOffset"/> <waitForElement selector="{{StorefrontCustomerWishlistSection.successMsg}}" time="30" stepKey="addCategoryProductToWishlistWaitForSuccessMessage"/> - <see selector="{{StorefrontCustomerWishlistSection.successMsg}}" userInput="{{productVar.name}} has been added to your Wish List." stepKey="addCategoryProductToWishlistSeeProductNameAddedToWishlist"/> - <seeCurrentUrlMatches regex="~/wishlist_id/\d+/$~" stepKey="seeCurrentUrlMatches"/> - </actionGroup> - - <actionGroup name="StorefrontCustomerAddCategoryProductToWishlistActionGroup3"> - <arguments> - <argument name="productVar"/> - </arguments> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(productVar.name)}}" stepKey="addCategoryProductToWishlistMoveMouseOverProduct" /> - <!--<click selector="{{StorefrontCategoryProductSection.ProductAddToSplitWishlistByName(productVar.name)}}" stepKey="addCategoryProductToWishlistClickAddProductToWishlist"/>--> - <click selector="{{StorefrontCategoryProductSection.addToWishListArrow(productVar.name)}}" stepKey="clickDropdownToAddToWishlist"/> - <!--<click selector="{{StorefrontCategoryProductSection.testWishlist(productVar.name)}}" stepKey="chooseWishlistSpan"/>--> - <!--<scrollTo selector="{{StorefrontCategoryProductSection.testWishlist(productVar.name)}}" x="-10" y="10" stepKey="selectWishlistwithOffset"/>--> - <!--<scrollTo selector="{{StorefrontCategoryProductSection.chooseWishlist}}" x="10" y="10" stepKey="selectWishlistwithOffset"/>--> - <!--<clickWithLeftButton selector="{{StorefrontCategoryProductSection.chooseWishlist}}" x="-90" y="0" stepKey="selectWishlistWithOffset"/>--> - <clickWithLeftButton selector="{{StorefrontCategoryProductSection.testWishlist(productVar.name)}}" x="90" y="0" stepKey="selectWishlistWithOffset"/> - - <waitForElement selector="{{StorefrontCustomerWishlistSection.successMsg}}" time="30" stepKey="addCategoryProductToWishlistWaitForSuccessMessage"/> - <see selector="{{StorefrontCustomerWishlistSection.successMsg}}" userInput="{{productVar.name}} has been added to your Wish List." stepKey="addCategoryProductToWishlistSeeProductNameAddedToWishlist"/> + <see selector="{{StorefrontCustomerWishlistSection.successMsg}}" userInput="{{productVar.name}} has been added to your Wish List." stepKey="SeeProductNameAddedToWishlist"/> <seeCurrentUrlMatches regex="~/wishlist_id/\d+/$~" stepKey="seeCurrentUrlMatches"/> </actionGroup> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml index 67172c1481940..e57a9105ff466 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -12,7 +12,6 @@ <element name="ProductAddToWishlistByNumber" type="text" selector="//main//li[{{var1}}]//a[contains(@class, 'towishlist')]" parameterized="true"/> <element name="ProductAddToWishlistByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//a[contains(@class, 'towishlist')]" parameterized="true"/> <element name="addToWishListArrow" type="button" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//button[@title='Add to:']" parameterized="true"/> - <element name="chooseWishlist" type="button" selector="span[title='Wish List']" timeout="30"/> - <element name="testWishlist" type="button" selector="(//main//li[.//a[contains(text(), '{{var1}}')]]//li[contains(@class,'item')]/span)[1]" parameterized="true"/> + <element name="chooseWishlist" type="button" selector="(//main//li[.//a[contains(text(), '{{var1}}')]]//li[contains(@class,'item')]/span)[1]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest.xml index 3fe3f40778bd9..cd5a3a0536b8c 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest.xml @@ -10,10 +10,10 @@ <test name="StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest"> <annotations> <stories value="Wishlist"/> - <title value="Add products from the wishlist to the cart using the sidebar."/> - <description value="Products added to the cart from wishlist and a customer remains on the same page."/> + <title value="Add products to wishlist from Category page with multiple wishlist enabled"/> + <description value="Registered customer should be able to add products from category page to wishlist when multiple wishlist enabled"/> <severity value="MAJOR"/> - <testCaseId value="MAGETWO-94021"/> + <testCaseId value="MAGETWO-94422"/> <group value="wishlist"/> </annotations> <before> @@ -22,10 +22,6 @@ <createData entity="SimpleProduct" stepKey="simpleProduct1"> <requiredEntity createDataKey="categoryFirst"/> </createData> - <!--<createData entity="SimpleProduct" stepKey="simpleProduct2">--> - <!--<requiredEntity createDataKey="categoryFirst"/>--> - <!--</createData>--> - <createData entity="Simple_US_Customer" stepKey="customer"/> </before> <after> @@ -41,9 +37,6 @@ <comment userInput="Enable multiple wishlist" stepKey="enableMultipleWishlist"/> <actionGroup ref="EnableCustomerMultipleWishlistOption" stepKey="enableCustomerWishlist"/> <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache"/> - - <!--<amOnPage url="admin/admin/auth/logout/" stepKey="logout"/>--> - <!-- Sign in as customer --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> <argument name="Customer" value="$$customer$$"/> </actionGroup> @@ -52,12 +45,8 @@ <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct1"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> - <!--<actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup2" stepKey="addSimpleProduct1ToWishlist">--> - <!--<argument name="productVar" value="$$simpleProduct1$$"/>--> - <!--</actionGroup>--> - <!-- Add product from first category to the wishlist after shifting--> - <actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup3" stepKey="addSimpleProduct1ToWishlist"> + <actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup2" stepKey="addSimpleProduct1ToWishlist"> <argument name="productVar" value="$$simpleProduct1$$"/> </actionGroup> </test> From 0a99c85ff014c44e598b74e28eb8f46bd39894c3 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 30 Aug 2018 11:08:12 -0500 Subject: [PATCH 1159/1171] MAGETWO-93305: Broken upgrade to 2.3 due to changed data type for minify_exclude - adding trim --- .../Magento/Framework/View/Asset/Minification.php | 8 +++++++- .../Framework/View/Test/Unit/Asset/MinificationTest.php | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Asset/Minification.php b/lib/internal/Magento/Framework/View/Asset/Minification.php index 8c6c75c7f6e95..f7daf23595a8f 100644 --- a/lib/internal/Magento/Framework/View/Asset/Minification.php +++ b/lib/internal/Magento/Framework/View/Asset/Minification.php @@ -164,7 +164,13 @@ private function getMinificationExcludeValues($key) $configValues = $this->scopeConfig->getValue($key, $this->scope) ?? []; //compatibility fix for type change from new line separated string values to array if (!is_array($configValues)) { - $configValues = explode("\n", $configValues); + $configValuesFromString = []; + foreach (explode("\n", $configValues) as $exclude) { + if (trim($exclude) != '') { + $configValuesFromString[] = trim($exclude); + } + } + $configValues = $configValuesFromString; } return array_values($configValues); } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php index 1758492a56384..10d28d36ecb3f 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php @@ -245,8 +245,9 @@ public function testGetExcludesTinyMceAsString(string $value, array $expectedVal public function getExcludesTinyMceAsStringDataProvider() { return [ - ["/tiny_mce/\n/tiny_mce2/", ['/tiny_mce/', '/tiny_mce2/']], + ["/tiny_mce/ \n /tiny_mce2/", ['/tiny_mce/', '/tiny_mce2/']], ['/tiny_mce/', ['/tiny_mce/']], + [' /tiny_mce/', ['/tiny_mce/']], ]; } } From 169cab98b18ee1632472f272b579a16d79386d14 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 30 Aug 2018 11:36:09 -0500 Subject: [PATCH 1160/1171] MAGETWO-93305: Broken upgrade to 2.3 due to changed data type for minify_exclude - rename comment --- lib/internal/Magento/Framework/View/Asset/Minification.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Asset/Minification.php b/lib/internal/Magento/Framework/View/Asset/Minification.php index f7daf23595a8f..596add349dbfa 100644 --- a/lib/internal/Magento/Framework/View/Asset/Minification.php +++ b/lib/internal/Magento/Framework/View/Asset/Minification.php @@ -162,7 +162,7 @@ public function getExcludes($contentType) private function getMinificationExcludeValues($key) { $configValues = $this->scopeConfig->getValue($key, $this->scope) ?? []; - //compatibility fix for type change from new line separated string values to array + //value used to be a string separated by 'newline' separator so we need to convert it to array if (!is_array($configValues)) { $configValuesFromString = []; foreach (explode("\n", $configValues) as $exclude) { From 3a8c27f0afa615610076af5d1bd2497f2e5d6264 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Thu, 30 Aug 2018 16:13:34 -0500 Subject: [PATCH 1161/1171] MAGETWO-94269: PayPal advanced payments won't process payment due to an incorrect redirect after a silent post --- .../Paypal/Controller/Payflow/ReturnUrl.php | 1 + .../Unit/Controller/Payflow/ReturnUrlTest.php | 37 +++++++++++++------ .../Magento/Signifyd/Observer/PlaceOrder.php | 5 ++- .../Test/Unit/Observer/PlaceOrderTest.php | 18 +++++++++ app/code/Magento/Signifyd/etc/events.xml | 3 ++ 5 files changed, 52 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php index 5ff96a53b6d94..b55e7584cd745 100644 --- a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php +++ b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php @@ -68,6 +68,7 @@ public function execute() if ($order->getIncrementId()) { if ($this->checkOrderState($order)) { $redirectBlock->setData('goto_success_page', true); + $this->_eventManager->dispatch('checkout_success', ['order' => $order]); } else { if ($this->checkPaymentMethod($order)) { $gotoSection = $this->_cancelPayment((string)$this->getRequest()->getParam('RESPMSG')); diff --git a/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php b/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php index bd4da25cb84d0..50985b2359c51 100644 --- a/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Paypal\Test\Unit\Controller\Payflow; +use Magento\Framework\Event\ManagerInterface; use Magento\Sales\Api\PaymentFailuresInterface; use Magento\Checkout\Block\Onepage\Success; use Magento\Checkout\Model\Session; @@ -96,6 +97,11 @@ class ReturnUrlTest extends \PHPUnit\Framework\TestCase */ private $paymentFailures; + /** + * @var ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $eventManagerMock; + /** * @inheritdoc */ @@ -148,25 +154,31 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->context->expects($this->any())->method('getView')->willReturn($this->view); - $this->context->expects($this->any())->method('getRequest')->willReturn($this->request); - $this->paymentFailures = $this->getMockBuilder(PaymentFailuresInterface::class) ->disableOriginalConstructor() ->getMock(); + $this->eventManagerMock = $this->getMockBuilder(ManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->context->method('getView') ->willReturn($this->view); $this->context->method('getRequest') ->willReturn($this->request); - - $this->returnUrl = $this->objectManager->getObject(ReturnUrl::class, [ - 'context' => $this->context, - 'checkoutSession' => $this->checkoutSession, - 'orderFactory' => $this->orderFactory, - 'checkoutHelper' => $this->checkoutHelper, - 'paymentFailures' => $this->paymentFailures, - ]); + $this->context->method('getEventManager') + ->willReturn($this->eventManagerMock); + + $this->returnUrl = $this->objectManager->getObject( + ReturnUrl::class, + [ + 'context' => $this->context, + 'checkoutSession' => $this->checkoutSession, + 'orderFactory' => $this->orderFactory, + 'checkoutHelper' => $this->checkoutHelper, + 'paymentFailures' => $this->paymentFailures, + ] + ); } /** @@ -187,6 +199,9 @@ public function testExecuteAllowedOrderState($state) ->with('goto_success_page', true) ->willReturnSelf(); + $this->eventManagerMock->expects($this->once()) + ->method('dispatch'); + $result = $this->returnUrl->execute(); $this->assertNull($result); } diff --git a/app/code/Magento/Signifyd/Observer/PlaceOrder.php b/app/code/Magento/Signifyd/Observer/PlaceOrder.php index 8415bc006b8aa..9b74a6ccff860 100644 --- a/app/code/Magento/Signifyd/Observer/PlaceOrder.php +++ b/app/code/Magento/Signifyd/Observer/PlaceOrder.php @@ -10,6 +10,7 @@ use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Exception\AlreadyExistsException; use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order; use Magento\Signifyd\Api\CaseCreationServiceInterface; use Magento\Signifyd\Model\Config; use Psr\Log\LoggerInterface; @@ -80,7 +81,9 @@ public function execute(Observer $observer) private function createCaseForOrder($order) { $orderId = $order->getEntityId(); - if (null === $orderId || $order->getPayment()->getMethodInstance()->isOffline()) { + if (null === $orderId + || $order->getPayment()->getMethodInstance()->isOffline() + || $order->getState() == Order::STATE_PENDING_PAYMENT) { return; } diff --git a/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php b/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php index 4e7edddf7b948..d63831b1d4a8e 100644 --- a/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php +++ b/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php @@ -10,6 +10,7 @@ use Magento\Framework\Exception\AlreadyExistsException; use Magento\Payment\Model\MethodInterface; use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment; use Magento\Signifyd\Api\CaseCreationServiceInterface; use Magento\Signifyd\Model\Config; @@ -193,6 +194,23 @@ public function testExecute() $this->placeOrder->execute($this->observer); } + public function testExecuteWithOrderPendingPayment() + { + $orderId = 1; + $storeId = 2; + + $this->withActiveSignifydIntegration(true, $storeId); + $this->withOrderEntity($orderId, $storeId); + $this->orderEntity->method('getState') + ->willReturn(Order::STATE_PENDING_PAYMENT); + $this->withAvailablePaymentMethod(true); + + $this->creationService->expects(self::never()) + ->method('createForOrder'); + + $this->placeOrder->execute($this->observer); + } + /** * Specifies order entity mock execution. * diff --git a/app/code/Magento/Signifyd/etc/events.xml b/app/code/Magento/Signifyd/etc/events.xml index a89b56ddf13c6..cc4f620500494 100644 --- a/app/code/Magento/Signifyd/etc/events.xml +++ b/app/code/Magento/Signifyd/etc/events.xml @@ -12,4 +12,7 @@ <event name="paypal_express_place_order_success"> <observer name="signifyd_place_order_paypal_express_observer" instance="Magento\Signifyd\Observer\PlaceOrder"/> </event> + <event name="checkout_success"> + <observer name="signifyd_place_order_checkout_success_observer" instance="Magento\Signifyd\Observer\PlaceOrder" /> + </event> </config> From 7a7540e1a2abfd4d4d7e860c6012d28f3dba2817 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Fri, 31 Aug 2018 10:53:23 -0500 Subject: [PATCH 1162/1171] MQE-1174: Deliver weekly regression enablement tests - Use enable admin account sharing action group at start of MAGETWO-92925 test --- .../Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml index fbbce2df4876c..0b737ed459f3b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml @@ -18,6 +18,7 @@ <group value="sales"/> </annotations> <before> + <actionGroup ref="EnableAdminAccountSharingActionGroup" stepKey="enableAdminAccountSharing"/> <createData entity="EnabledMinimumOrderAmount500" stepKey="enableMinimumOrderAmount"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createProduct"> From 00e3b5ed9006e4269322490d41c22387d1895bad Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Fri, 31 Aug 2018 12:09:56 -0500 Subject: [PATCH 1163/1171] MAGETWO-94269: PayPal advanced payments won't process payment due to an incorrect redirect after a silent post - address code review comments --- .../Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php | 3 ++- app/code/Magento/Signifyd/Observer/PlaceOrder.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php b/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php index 50985b2359c51..dea28f1e7fc3b 100644 --- a/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php @@ -200,7 +200,8 @@ public function testExecuteAllowedOrderState($state) ->willReturnSelf(); $this->eventManagerMock->expects($this->once()) - ->method('dispatch'); + ->method('dispatch') + ->with('checkout_success', $this->arrayHasKey('order')); $result = $this->returnUrl->execute(); $this->assertNull($result); diff --git a/app/code/Magento/Signifyd/Observer/PlaceOrder.php b/app/code/Magento/Signifyd/Observer/PlaceOrder.php index 9b74a6ccff860..7c451a129cccd 100644 --- a/app/code/Magento/Signifyd/Observer/PlaceOrder.php +++ b/app/code/Magento/Signifyd/Observer/PlaceOrder.php @@ -83,7 +83,7 @@ private function createCaseForOrder($order) $orderId = $order->getEntityId(); if (null === $orderId || $order->getPayment()->getMethodInstance()->isOffline() - || $order->getState() == Order::STATE_PENDING_PAYMENT) { + || $order->getState() === Order::STATE_PENDING_PAYMENT) { return; } From 172e3b206de0dad4f24b2ccbe60667ae97362d15 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Fri, 31 Aug 2018 14:53:10 -0500 Subject: [PATCH 1164/1171] MAGETWO-94269: PayPal advanced payments won't process payment due to an incorrect redirect after a silent post - address code review comments --- app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php | 2 +- .../Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php | 2 +- app/code/Magento/Signifyd/etc/events.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php index b55e7584cd745..56a5da40edb85 100644 --- a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php +++ b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php @@ -68,7 +68,7 @@ public function execute() if ($order->getIncrementId()) { if ($this->checkOrderState($order)) { $redirectBlock->setData('goto_success_page', true); - $this->_eventManager->dispatch('checkout_success', ['order' => $order]); + $this->_eventManager->dispatch('paypal_checkout_success', ['order' => $order]); } else { if ($this->checkPaymentMethod($order)) { $gotoSection = $this->_cancelPayment((string)$this->getRequest()->getParam('RESPMSG')); diff --git a/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php b/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php index dea28f1e7fc3b..44775d7e381bb 100644 --- a/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php @@ -201,7 +201,7 @@ public function testExecuteAllowedOrderState($state) $this->eventManagerMock->expects($this->once()) ->method('dispatch') - ->with('checkout_success', $this->arrayHasKey('order')); + ->with('paypal_checkout_success', $this->arrayHasKey('order')); $result = $this->returnUrl->execute(); $this->assertNull($result); diff --git a/app/code/Magento/Signifyd/etc/events.xml b/app/code/Magento/Signifyd/etc/events.xml index cc4f620500494..d5ba6e5a99227 100644 --- a/app/code/Magento/Signifyd/etc/events.xml +++ b/app/code/Magento/Signifyd/etc/events.xml @@ -12,7 +12,7 @@ <event name="paypal_express_place_order_success"> <observer name="signifyd_place_order_paypal_express_observer" instance="Magento\Signifyd\Observer\PlaceOrder"/> </event> - <event name="checkout_success"> + <event name="paypal_checkout_success"> <observer name="signifyd_place_order_checkout_success_observer" instance="Magento\Signifyd\Observer\PlaceOrder" /> </event> </config> From 1e1629ec7cd3ea469bf2ba599fa9003c9a8182f6 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Fri, 31 Aug 2018 17:28:30 -0500 Subject: [PATCH 1165/1171] MAGETWO-90974: HTML showing in minicart with custom option file upload - Check for properly constructed link in functional test --- .../Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml | 4 ++++ .../Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml | 1 + .../Test/Mftf/Section/StorefrontCustomerOrderSection.xml | 1 + .../Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml | 1 + 4 files changed, 7 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml index 6e43753220d7c..6c13ef0e9ca6f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml @@ -95,6 +95,7 @@ <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionField.title}}" stepKey="seeProductOptionFieldInput1"/> <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionArea.title}}" stepKey="seeProductOptionAreaInput1"/> <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{productWithOptions.file}}" stepKey="seeProductOptionFileInput1"/> + <seeElement selector="{{CheckoutPaymentSection.ProductOptionLinkActiveByProductItemName($createProduct.name$, productWithOptions.file)}}" stepKey="seeProductOptionFileInputLink1"/> <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionValueDropdown1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionValueRadioButtons1.title}}" stepKey="seeProductOptionValueRadioButtons1Input1"/> <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionValueCheckbox.title}}" stepKey="seeProductOptionValueCheckboxInput1" /> @@ -128,6 +129,7 @@ <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionField.title}}" stepKey="seeAdminOrderProductOptionField" /> <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionArea.title}}" stepKey="seeAdminOrderProductOptionArea"/> <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{productWithOptions.file}}" stepKey="seeAdminOrderProductOptionFile"/> + <seeElement selector="{{AdminOrderItemsOrderedSection.productNameOptionsLink(productWithOptions.file)}}" stepKey="seeAdminOrderProductOptionFileLink"/> <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueDropdown1.title}}" stepKey="seeAdminOrderProductOptionValueDropdown1"/> <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueRadioButtons1.title}}" stepKey="seeAdminOrderProductOptionValueRadioButton1"/> <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueCheckbox.title}}" stepKey="seeAdminOrderProductOptionValueCheckbox" /> @@ -144,6 +146,7 @@ <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionField.title}}" stepKey="seeAdminOrderProductOptionField1" /> <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionArea.title}}" stepKey="seeAdminOrderProductOptionArea1"/> <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{productWithOptions.file}}" stepKey="seeAdminOrderProductOptionFile1"/> + <seeElement selector="{{AdminOrderItemsOrderedSection.productNameOptionsLink(productWithOptions.file)}}" stepKey="seeAdminOrderProductOptionFileLink1"/> <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueDropdown1.title}}" stepKey="seeAdminOrderProductOptionValueDropdown11"/> <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueRadioButtons1.title}}" stepKey="seeAdminOrderProductOptionValueRadioButton11"/> <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueCheckbox.title}}" stepKey="seeAdminOrderProductOptionValueCheckbox1" /> @@ -161,6 +164,7 @@ <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionField.title, ProductOptionField.title)}}" userInput="{{ProductOptionField.title}}" stepKey="seeStorefontOrderProductOptionField1" /> <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionArea.title, ProductOptionArea.title)}}" userInput="{{ProductOptionArea.title}}" stepKey="seeStorefontOrderProductOptionArea1"/> <see selector="{{StorefrontCustomerOrderSection.productCustomOptionsFile($createProduct.name$, ProductOptionFile.title, productWithOptions.file)}}" userInput="{{productWithOptions.file}}" stepKey="seeStorefontOrderProductOptionFile1"/> + <seeElement selector="{{StorefrontCustomerOrderSection.productCustomOptionsLink($createProduct.name$, ProductOptionFile.title, productWithOptions.file)}}" stepKey="seeStorefontOrderProductOptionFileLink1"/> <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionDropDown.title, ProductOptionValueDropdown1.title)}}" userInput="{{ProductOptionValueDropdown1.title}}" stepKey="seeStorefontOrderProductOptionValueDropdown11"/> <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionRadiobutton.title, ProductOptionValueRadioButtons1.title)}}" userInput="{{ProductOptionValueRadioButtons1.title}}" stepKey="seeStorefontOrderProductOptionValueRadioButtons11"/> <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionCheckbox.title, ProductOptionValueCheckbox.title)}}" userInput="{{ProductOptionValueCheckbox.title}}" stepKey="seeStorefontOrderProductOptionValueCheckbox1" /> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index a12852ea1ef25..349cc5889d2b5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -36,6 +36,7 @@ <element name="ProductItemByName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']" parameterized="true" /> <element name="ProductOptionsByProductItemName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']//ancestor::div[@class='product-item-details']//div[@class='product options']" parameterized="true" /> <element name="ProductOptionsActiveByProductItemName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']//ancestor::div[@class='product-item-details']//div[@class='product options active']" parameterized="true" /> + <element name="ProductOptionLinkActiveByProductItemName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']//ancestor::div[@class='product-item-details']//div[@class='product options active']//a[text() = '{{var2}}']" parameterized="true" /> <element name="shipToInformation" type="text" selector="//div[@class='ship-to']//div[@class='shipping-information-content']" /> <element name="shippingMethodInformation" type="text" selector="//div[@class='ship-via']//div[@class='shipping-information-content']" /> <element name="paymentMethodTitle" type="text" selector=".payment-method-title span" /> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderSection.xml index c39dfef5f74e7..5b913a5e5cea3 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderSection.xml @@ -11,5 +11,6 @@ <section name="StorefrontCustomerOrderSection"> <element name="productCustomOptions" type="text" selector="//strong[contains(@class, 'product-item-name') and normalize-space(.)='{{var1}}']/following-sibling::*[contains(@class, 'item-options')]/dt[normalize-space(.)='{{var2}}']/following-sibling::dd[normalize-space(.)='{{var3}}']" parameterized="true"/> <element name="productCustomOptionsFile" type="text" selector="//strong[contains(@class, 'product-item-name') and normalize-space(.)='{{var1}}']/following-sibling::*[contains(@class, 'item-options')]/dt[normalize-space(.)='{{var2}}']/following-sibling::dd[contains(.,'{{var3}}')]" parameterized="true"/> + <element name="productCustomOptionsLink" type="text" selector="//strong[contains(@class, 'product-item-name') and normalize-space(.)='{{var1}}']/following-sibling::*[contains(@class, 'item-options')]/dt[normalize-space(.)='{{var2}}']/following-sibling::dd//a[text() = '{{var3}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml index 9807d7364c7c6..314b16738a82d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml @@ -23,6 +23,7 @@ <element name="productNameColumn" type="text" selector=".edit-order-table .col-product .product-title"/> <element name="productNameOptions" type="text" selector=".edit-order-table .col-product .item-options"/> + <element name="productNameOptionsLink" type="text" selector="//table[contains(@class, 'edit-order-table')]//td[contains(@class, 'col-product')]//a[text() = '{{var1}}']" parameterized="true"/> <element name="productSkuColumn" type="text" selector=".edit-order-table .col-product .product-sku-block"/> <element name="statusColumn" type="text" selector=".edit-order-table .col-status"/> <element name="originalPriceColumn" type="text" selector=".edit-order-table .col-original-price .price"/> From 7e46ed2b570249a61b965148fe6e74c905a4f4c3 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 31 Aug 2018 20:35:31 -0500 Subject: [PATCH 1166/1171] MAGETWO-93305: Broken upgrade to 2.3 due to changed data type for minify_exclude - fix static --- .../Magento/Framework/View/Test/Unit/Asset/MinificationTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php index 10d28d36ecb3f..48fc8d5c775a4 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MinificationTest.php @@ -238,7 +238,6 @@ public function testGetExcludesTinyMceAsString(string $value, array $expectedVal $this->assertEquals($expectedValue, $this->minification->getExcludes('js')); } - /** * @return array */ From f67116375e836d320d6039d754822e6cde61eaea Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 31 Aug 2018 23:03:35 -0500 Subject: [PATCH 1167/1171] MAGETWO-94021: Problems with adding products in wish list - remove multiple wishlist test from CE --- ...nfigurationCustomerWishlistActionGroup.xml | 31 ----------- .../StorefrontCustomerWishlistActionGroup.xml | 14 ----- .../Page/StorefrontCustomerWishlistPage.xml | 3 -- .../Section/AdminCustomerWishlistSection.xml | 16 ------ .../StorefrontCategoryProductSection.xml | 2 - ...shListWithMultipleWishlistsEnabledTest.xml | 53 ------------------- 6 files changed, 119 deletions(-) delete mode 100644 app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigurationCustomerWishlistActionGroup.xml delete mode 100644 app/code/Magento/Wishlist/Test/Mftf/Section/AdminCustomerWishlistSection.xml delete mode 100644 app/code/Magento/Wishlist/Test/Mftf/Test/StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest.xml diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigurationCustomerWishlistActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigurationCustomerWishlistActionGroup.xml deleted file mode 100644 index 637775cfa4064..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigurationCustomerWishlistActionGroup.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="EnableCustomerMultipleWishlistOption"> - <amOnPage url="{{AdminCustomerWishlistConfigurationPage.url}}" stepKey="goToWishlistConfigurationPage"/> - <waitForPageLoad stepKey="waitForSystemConfigPage"/> - <conditionalClick selector="{{WishlistGeneralSection.GeneralOptionsTab}}" dependentSelector="{{WishlistGeneralSection.CheckIfGeneralOptionsTabExpand}}" visible="true" stepKey="expandGeneralSectionTab"/> - <waitForElementVisible selector="{{WishlistGeneralSection.EnableMultipleWishList}}" stepKey="waitForDropdownToBeVisible"/> - <selectOption selector="{{WishlistGeneralSection.EnableMultipleWishList}}" userInput="Yes" stepKey="enableMultipleWishLists"/> - <click selector="{{WishlistGeneralSection.GeneralOptionsTab}}" stepKey="collapseGeneralOptionsTab"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfiguration"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeConfigSuccessMessage"/> - </actionGroup> - <actionGroup name="ResetCustomerMultipleWishlistOption"> - <amOnPage url="{{AdminCustomerWishlistConfigurationPage.url}}" stepKey="goToWishlistConfigurationPage"/> - <waitForPageLoad stepKey="waitForSystemConfigPage"/> - <conditionalClick selector="{{WishlistGeneralSection.GeneralOptionsTab}}" dependentSelector="{{WishlistGeneralSection.CheckIfGeneralOptionsTabExpand}}" visible="true" stepKey="expandGeneralSectionTab"/> - <waitForElementVisible selector="{{WishlistGeneralSection.EnableMultipleWishList}}" stepKey="waitForDropdownToBeVisible"/> - <selectOption selector="{{WishlistGeneralSection.EnableMultipleWishList}}" userInput="No" stepKey="enableMultipleWishLists"/> - <click selector="{{WishlistGeneralSection.GeneralOptionsTab}}" stepKey="collapseGeneralOptionsTab"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfiguration"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeConfigSuccessMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml index d1c9aeab02b60..edd1af41964a2 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml @@ -20,20 +20,6 @@ <seeCurrentUrlMatches regex="~/wishlist_id/\d+/$~" stepKey="seeCurrentUrlMatches"/> </actionGroup> - <!-- Add Product to split wishlist from the category page and check message --> - <actionGroup name="StorefrontCustomerAddCategoryProductToWishlistActionGroup2"> - <arguments> - <argument name="productVar"/> - </arguments> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(productVar.name)}}" stepKey="addCategoryProductToWishlistMoveMouseOverProduct" /> - <!--<click selector="{{StorefrontCategoryProductSection.ProductAddToSplitWishlistByName(productVar.name)}}" stepKey="addCategoryProductToWishlistClickAddProductToWishlist"/>--> - <click selector="{{StorefrontCategoryProductSection.addToWishListArrow(productVar.name)}}" stepKey="clickAddToWishlist"/> - <clickWithLeftButton selector="{{StorefrontCategoryProductSection.chooseWishlist(productVar.name)}}" x="96" y="5" stepKey="selectWishlistWithOffset"/> - <waitForElement selector="{{StorefrontCustomerWishlistSection.successMsg}}" time="30" stepKey="addCategoryProductToWishlistWaitForSuccessMessage"/> - <see selector="{{StorefrontCustomerWishlistSection.successMsg}}" userInput="{{productVar.name}} has been added to your Wish List." stepKey="SeeProductNameAddedToWishlist"/> - <seeCurrentUrlMatches regex="~/wishlist_id/\d+/$~" stepKey="seeCurrentUrlMatches"/> - </actionGroup> - <!-- Add Product to wishlist from the product page and check message --> <actionGroup name="StorefrontCustomerAddProductToWishlistActionGroup"> <arguments> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml b/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml index 0e9354990e9ab..cf2db7efab6c6 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml @@ -11,7 +11,4 @@ <page name="StorefrontCustomerWishlistPage" url="/wishlist/" area="storefront" module="Magento_Wishlist"> <section name="StorefrontCustomerWishlistSection" /> </page> - <page name="AdminCustomerWishlistConfigurationPage" url="admin/system_config/edit/section/wishlist/" area="admin" module="Magento_Wishlist"> - <section name="WishlistGeneralSection"/> - </page> </pages> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/AdminCustomerWishlistSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/AdminCustomerWishlistSection.xml deleted file mode 100644 index 6d71b5374fa05..0000000000000 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/AdminCustomerWishlistSection.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="WishlistGeneralSection"> - <element name="GeneralOptionsTab" type="button" selector="#wishlist_general-head"/> - <element name="CheckIfGeneralOptionsTabExpand" type="button" selector="#wishlist_general-head:not(.open)"/> - <element name="EnableMultipleWishList" type="select" selector="#wishlist_general_multiple_enabled"/> - </section> -</sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml index e57a9105ff466..20d72a0704699 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -11,7 +11,5 @@ <section name="StorefrontCategoryProductSection"> <element name="ProductAddToWishlistByNumber" type="text" selector="//main//li[{{var1}}]//a[contains(@class, 'towishlist')]" parameterized="true"/> <element name="ProductAddToWishlistByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//a[contains(@class, 'towishlist')]" parameterized="true"/> - <element name="addToWishListArrow" type="button" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//button[@title='Add to:']" parameterized="true"/> - <element name="chooseWishlist" type="button" selector="(//main//li[.//a[contains(text(), '{{var1}}')]]//li[contains(@class,'item')]/span)[1]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest.xml deleted file mode 100644 index cd5a3a0536b8c..0000000000000 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest.xml +++ /dev/null @@ -1,53 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="StoreFrontAddProductsToWishListWithMultipleWishlistsEnabledTest"> - <annotations> - <stories value="Wishlist"/> - <title value="Add products to wishlist from Category page with multiple wishlist enabled"/> - <description value="Registered customer should be able to add products from category page to wishlist when multiple wishlist enabled"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-94422"/> - <group value="wishlist"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="categoryFirst"/> - - <createData entity="SimpleProduct" stepKey="simpleProduct1"> - <requiredEntity createDataKey="categoryFirst"/> - </createData> - <createData entity="Simple_US_Customer" stepKey="customer"/> - </before> - <after> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - <!--<deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/>--> - <deleteData createDataKey="categoryFirst" stepKey="deleteCategoryFirst"/> - <deleteData createDataKey="customer" stepKey="deleteCustomer"/> - <actionGroup ref="ResetCustomerMultipleWishlistOption" stepKey="resetWishlistConfiguration"/> - <amOnPage url="admin/admin/auth/logout/" stepKey="logout"/> - </after> - <!--Login to admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdmin"/> - <comment userInput="Enable multiple wishlist" stepKey="enableMultipleWishlist"/> - <actionGroup ref="EnableCustomerMultipleWishlistOption" stepKey="enableCustomerWishlist"/> - <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache"/> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> - <argument name="Customer" value="$$customer$$"/> - </actionGroup> - <!-- Add product from first category to the wishlist --> - <amOnPage url="{{StorefrontCategoryPage.url($$categoryFirst.name$$)}}" stepKey="navigateToCategoryFirstPage"/> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct1"> - <argument name="product" value="$$simpleProduct1$$"/> - </actionGroup> - <!-- Add product from first category to the wishlist after shifting--> - <actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup2" stepKey="addSimpleProduct1ToWishlist"> - <argument name="productVar" value="$$simpleProduct1$$"/> - </actionGroup> - </test> -</tests> From 6b0874c6f551d7a3d8efb3934159c183e849a247 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Mon, 3 Sep 2018 09:50:08 +0300 Subject: [PATCH 1168/1171] Skip unstable test due to: MAGETWO-94438 --- .../Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml index e9d17b5c70ddd..6fdb32fd4560e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml @@ -16,6 +16,9 @@ <description value="Admin should be able to add image to WYSIWYG Editor on Product Page"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-84375"/> + <skip> + <issueId value="MAGETWO-94438"/> + </skip> </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="login"/> From 7699538468d4dd77b0f84078f531562821a84bb5 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Tue, 4 Sep 2018 12:58:43 +0300 Subject: [PATCH 1169/1171] Reverted escape fix to avoid issue with file option --- .../Sales/view/adminhtml/templates/items/column/name.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml b/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml index ab66cc0e42ad6..a903c92ef33e2 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml @@ -28,7 +28,7 @@ <dt><?= $block->escapeHtml($_option['label']) ?>:</dt> <dd> <?php if (isset($_option['custom_view']) && $_option['custom_view']): ?> - <?= $block->escapeHtml($block->getCustomizedOptionValue($_option)) ?> + <?= /* @escapeNotVerified */ $block->getCustomizedOptionValue($_option) ?> <?php else: ?> <?php $_option = $block->getFormattedOption($_option['value']); ?> <?php $dots = 'dots' . uniqid(); ?> From 8f4150c654219c315ef73809dfda892193f4064d Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Tue, 4 Sep 2018 14:57:08 -0500 Subject: [PATCH 1170/1171] MQE-1174: Deliver weekly regression enablement tests --- .../Test/AdminRemoveDefaultImageDownloadableProductTest.xml | 3 +++ .../Test/AdminRemoveDefaultVideoDownloadableProductTest.xml | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml index 3ee6cef47738b..94809563de3ca 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-201"/> <group value="Downloadable"/> + <skip> + <issueId value="MAGETWO-94795"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml index 4a62a7a43bc60..83d859ee7421a 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminRemoveDefaultVideoDownloadableProductTest" extends="AdminRemoveDefaultVideoSimpleProductTest"> <annotations> <features value="Downloadable"/> @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-207"/> <group value="Downloadable"/> + <skip> + <issueId value="MAGETWO-94795"/> + </skip> </annotations> <!-- Create a downloadable product --> From 820e03d3cdf0f94a76f15ecc6ad00a04a7fd2756 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Wed, 5 Sep 2018 10:29:24 -0500 Subject: [PATCH 1171/1171] MQE-1187: Fix MFTF skipped tests - Skip NewProductsListWidgetDownloadableProductTest --- .../Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml index 4864d11c884bc..1a9ad271d62c7 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml @@ -18,6 +18,9 @@ <testCaseId value="MC-124"/> <group value="Downloadable"/> <group value="WYSIWYGDisabled"/> + <skip> + <issueId value="MQE-1187"/> + </skip> </annotations> <!-- A Cms page containing the New Products Widget gets created here via extends -->